Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions examples/google_search_example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Google Search Bot Feature Examples

This document demonstrates how the Google search functionality works in the VK bot.

## Usage Examples

### Basic Search Commands

```
search python list comprehension
google javascript async await
найди react hooks tutorial
поиск how to use git
```

### Command Structure

- **Trigger words**: `search`, `google`, `найди`, `поиск`
- **Minimum words**: 3 words required in the query
- **Maximum results**: Up to 3 links returned
- **Whitelisted sites only**: Results filtered to trusted programming sites

### Response Format

When you send: `search python list comprehension`

The bot responds with:
```
Результаты поиска для 'python list comprehension':

1. https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions
2. https://stackoverflow.com/questions/34835951/what-does-list-comprehension-mean-how-does-it-work-and-how-can-i-use-it
3. https://medium.com/@python_guide/python-list-comprehensions-explained-765fb6ca5c8a
```

### Error Cases

**Too few words:**
```
User: search python
Bot: Пожалуйста, используйте не менее 3 слов для поиска.
```

**No whitelisted results found:**
```
User: search obscure topic nobody talks about
Bot: К сожалению, не найдено ссылок с проверенных сайтов для запроса 'obscure topic nobody talks about'.
```

**Network error:**
```
User: search python programming
Bot: Произошла ошибка при поиске. Попробуйте позже.
```

## Whitelisted Sites

The bot only returns results from these trusted programming sites:

- stackoverflow.com
- github.com
- docs.python.org
- developer.mozilla.org
- w3schools.com
- medium.com
- dev.to
- geeksforgeeks.org
- tutorialspoint.com
- programiz.com

## Technical Implementation

### Configuration

The feature is configured in `config.py`:

```python
GOOGLE_SEARCH_WHITELISTED_SITES = [
'stackoverflow.com',
'github.com',
# ... other trusted sites
]
GOOGLE_SEARCH_MIN_WORDS = 3
GOOGLE_SEARCH_MAX_RESULTS = 3
GOOGLE_SEARCH_TIMEOUT = 10 # seconds
```

### Pattern Matching

The command is recognized using a regex pattern in `patterns.py`:

```python
GOOGLE_SEARCH = recompile(
r'\A\s*(search|найди|поиск|google)\s+(?P<query>[\S][\S\s]*?)\??\s*\Z', IGNORECASE)
```

### Search Process

1. **Query Validation**: Check minimum word count
2. **Google Search**: Make HTTP request to Google with proper headers
3. **Link Extraction**: Parse HTML to find result URLs
4. **Whitelist Filtering**: Keep only links from trusted domains
5. **Relevance Scoring**: Prioritize results with more matching query words
6. **Response Formatting**: Send formatted results to user

### Security Features

- **Request Limiting**: Built-in timeout protection
- **Domain Filtering**: Only whitelisted domains returned
- **Query Sanitization**: URLs properly encoded
- **Error Handling**: Graceful fallback for network issues
3 changes: 2 additions & 1 deletion python/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ def __init__(
(patterns.WHAT_IS, self.commands.what_is),
(patterns.WHAT_MEAN, self.commands.what_is),
(patterns.APPLY_KARMA, self.commands.apply_karma),
(patterns.GITHUB_COPILOT, self.commands.github_copilot)
(patterns.GITHUB_COPILOT, self.commands.github_copilot),
(patterns.GOOGLE_SEARCH, self.commands.google_search)
)

def message_new(
Expand Down
17 changes: 17 additions & 0 deletions python/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,5 +153,22 @@
GITHUB_COPILOT_RUN_COMMAND = 'bash -c "./copilot.sh {input_file} {output_file}"'
GITHUB_COPILOT_TIMEOUT = 120 # seconds

# Google search configuration
GOOGLE_SEARCH_WHITELISTED_SITES = [
'stackoverflow.com',
'github.com',
'docs.python.org',
'developer.mozilla.org',
'w3schools.com',
'medium.com',
'dev.to',
'geeksforgeeks.org',
'tutorialspoint.com',
'programiz.com'
]
GOOGLE_SEARCH_MIN_WORDS = 3
GOOGLE_SEARCH_MAX_RESULTS = 3
GOOGLE_SEARCH_TIMEOUT = 10 # seconds

DEFAULT_PROGRAMMING_LANGUAGES_PATTERN_STRING = "|".join(DEFAULT_PROGRAMMING_LANGUAGES)
GITHUB_COPILOT_LANGUAGES_PATTERN_STRING = "|".join([i for i in GITHUB_COPILOT_LANGUAGES.keys()])
97 changes: 96 additions & 1 deletion python/modules/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
import os

from regex import Pattern, Match, split, match, search, IGNORECASE, sub
from requests import post
from requests import post, get
from social_ethosa import BetterUser
from saya import Vk
import wikipedia
import urllib.parse

from .commands_builder import CommandsBuilder
from .data_service import BetterBotBaseDataService
Expand Down Expand Up @@ -379,6 +380,100 @@ def github_copilot(self) -> NoReturn:
f'Пожалуйста, подождите {round(config.GITHUB_COPILOT_TIMEOUT - (now - self.now))} секунд', self.peer_id
)

def google_search(self) -> NoReturn:
"""Search Google for answers and return whitelisted links"""
query = self.matched.group('query').strip()

# Check minimum word count
words = query.split()
if len(words) < config.GOOGLE_SEARCH_MIN_WORDS:
self.vk_instance.send_msg(
f'Пожалуйста, используйте не менее {config.GOOGLE_SEARCH_MIN_WORDS} слов для поиска.',
self.peer_id
)
return

try:
# Build Google search URL
search_url = f"https://www.google.com/search?q={urllib.parse.quote(query)}&num=20"

# Set headers to mimic a browser
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
}

# Make the search request
response = get(search_url, headers=headers, timeout=config.GOOGLE_SEARCH_TIMEOUT)
response.raise_for_status()

# Extract links from search results using multiple patterns
import re

# Try multiple patterns to extract URLs from Google results
url_patterns = [
r'href="(/url\?q=[^"]+)"', # Standard Google redirect
r'href="(https?://[^"]*)"', # Direct URLs
r'data-href="([^"]+)"', # Alternative href attribute
]

all_links = []
for pattern in url_patterns:
matches = re.findall(pattern, response.text)
all_links.extend(matches)

filtered_links = []
seen_domains = set()

for link in all_links:
if len(filtered_links) >= config.GOOGLE_SEARCH_MAX_RESULTS:
break

# Clean up URL
actual_url = link
if '/url?q=' in link:
# Extract from Google redirect
try:
actual_url = link.split('/url?q=')[1].split('&')[0]
actual_url = urllib.parse.unquote(actual_url)
except:
continue
elif link.startswith('/'):
# Skip relative URLs
continue

# Validate URL format
if not actual_url.startswith(('http://', 'https://')):
continue

# Check if URL is from whitelisted domain
for whitelisted_site in config.GOOGLE_SEARCH_WHITELISTED_SITES:
if whitelisted_site in actual_url and whitelisted_site not in seen_domains:
# Count matching words in URL for relevance
word_matches = sum(1 for word in words if word.lower() in actual_url.lower())
if word_matches >= 0: # Allow URLs even without exact word matches
filtered_links.append(actual_url)
seen_domains.add(whitelisted_site)
break

# Send results
if filtered_links:
result_message = f"Результаты поиска для '{query}':\n\n"
for i, link in enumerate(filtered_links, 1):
result_message += f"{i}. {link}\n"
self.vk_instance.send_msg(result_message, self.peer_id)
else:
self.vk_instance.send_msg(
f"К сожалению, не найдено ссылок с проверенных сайтов для запроса '{query}'.",
self.peer_id
)

except Exception as e:
print(f"Google search error: {e}")
self.vk_instance.send_msg(
"Произошла ошибка при поиске. Попробуйте позже.",
self.peer_id
)

def match_command(
self,
pattern: Pattern
Expand Down
4 changes: 4 additions & 0 deletions python/patterns.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,7 @@
GITHUB_COPILOT = recompile(
r'\A\s*(code|код)\s+(?P<lang>(' + COPILOT_LANGUAGES +
r'))(?P<text>[\S\s]+)\Z', IGNORECASE)

# Google search pattern
GOOGLE_SEARCH = recompile(
r'\A\s*(search|найди|поиск|google)\s+(?P<query>[\S][\S\s]*?)\??\s*\Z', IGNORECASE)
Loading
Loading