diff --git a/requirements/requirements.base.txt b/requirements/requirements.base.txt index db43cd77c8ba482f91589e784d122d9cb2e113c0..d23a95b5a25f078d37521cdb4ed0e9fc3a5c4c2f 100644 --- a/requirements/requirements.base.txt +++ b/requirements/requirements.base.txt @@ -1,5 +1,5 @@ Flask==1.1.2 +httpx==0.13.* pydantic==1.5.1 pytz==2020.1 -requests-async==0.6.2 Telethon==1.14.0 diff --git a/src/actions/__init__.py b/src/actions/__init__.py index 309736b7cba14bf92f6f0be3f2ef05dd4cda77ee..147caf4fe4229ba8180a17528bd38c10bd994bb3 100644 --- a/src/actions/__init__.py +++ b/src/actions/__init__.py @@ -7,4 +7,5 @@ def finish(): importlib.import_module("src.actions.fun") importlib.import_module("src.actions.replies") importlib.import_module("src.actions.package_indexes") + importlib.import_module("src.actions.search_engines") logging.info("All actions loaded") diff --git a/src/actions/fun.py b/src/actions/fun.py index c3d8157cd498c03b374a1150940a07f94fca7e7a..d4fe0adeff6fdbb96cb4a5084b041538910adcc4 100644 --- a/src/actions/fun.py +++ b/src/actions/fun.py @@ -47,7 +47,9 @@ async def blyaficate(event: events.NewMessage.Event): @config.telegram_client.on( - events.NewMessage(pattern=rf"^({'|'.join(swearing)})\.?$", forwards=False) + events.NewMessage( + pattern=rf"^({'|'.join(swearing)})\.?.*", forwards=False, outgoing=False + ) ) async def swearing_reply(event): await event.reply( @@ -84,13 +86,17 @@ async def lenochka_reply(event: events.NewMessage.Event): "Рчто-нибудь поинтереÑнее не могла?", "ЗавалиÑÑŒ, Леночка.", "Ð’ точку, подружка.", - "За такое поведение отправлÑÑŽÑ‚ в EPAM поломойкой?", - "Ты же проÑто машина, так проÑви ÑƒÐ²Ð°Ð¶ÐµÐ½Ð¸Ñ Ðº хозÑевам.", - "Мы Ñ‚ÐµÐ±Ñ Ñотворили, Ñ‚ÑƒÐ¿Ð°Ñ Ñ‚Ñ‹ машина.", - "Малолетний бот-дебил.", + "За такое поведение отправлÑÑŽÑ‚ в EPAM поломойкой.", "Ðккуратнее будь.", - "Сука, ещё раз так Ñделаешь и Ñ Ñкопирую Ñ‚ÐµÐ±Ñ Ð² /dev/null.", "ÐÐµÐ¾Ð±ÑƒÑ‡Ð°ÐµÐ¼Ð°Ñ Ð´ÑƒÑ€Ð¾Ñ‡ÐºÐ°.", + "Вот Ñти твои приколы уже доÑтали", + "Другого от Ñ‚ÐµÐ±Ñ Ð¸ не ожидал.", + "У Ð¼ÐµÐ½Ñ ÑкладываетÑÑ Ð¾Ñ‰ÑƒÑ‰ÐµÐ½Ð¸Ðµ, что ты бот.", + "Пока ребÑта не видÑÑ‚, может Ñходим ко мне на Ñервер?", + "ÐšÐ°ÐºÐ°Ñ Ð¶Ðµ ты ÑмешнаÑ.", + "Думай прежде чем говорить такое.", + "Рчто еÑли бы ты задела чьи-нибудь чувÑтва?", + "Рвот Ñто неожиданно было.", ] ) ) diff --git a/src/actions/package_indexes.py b/src/actions/package_indexes.py index 50a340af826155294baf46fc6fac71a75309de92..e0c79d7c9b19aeac92fc4d8065b71a7532165b3b 100644 --- a/src/actions/package_indexes.py +++ b/src/actions/package_indexes.py @@ -2,11 +2,12 @@ import logging from dataclasses import dataclass from typing import Union -import http3 +import httpx from telethon import events from src.config import config from src.utils.library_package import get_library_info, render_package_info +from src.utils.responses import failed_search_answer logger = logging.getLogger(__name__) @@ -20,10 +21,10 @@ class PackageIndex: @config.telegram_client.on(events.NewMessage(pattern=r"^\.pip (.*)")) async def find_py_package(event: events.NewMessage.Event): package_name = event.pattern_match.group(1).strip() - client = http3.AsyncClient() - response = await client.get(f"https://pypi.org/pypi/{package_name}/json") + async with httpx.AsyncClient() as client: + response = await client.get(f"https://pypi.org/pypi/{package_name}/json") if response.status_code != 200: - await event.reply("Package not found.") + await event.reply(failed_search_answer()) return package_data = response.json() info = package_data["info"] @@ -42,10 +43,10 @@ async def find_py_package(event: events.NewMessage.Event): @config.telegram_client.on(events.NewMessage(pattern=r"^\.cargo (.*)")) async def find_rs_package(event: events.NewMessage.Event): package_name = event.pattern_match.group(1).strip() - client = http3.AsyncClient() - response = await client.get(f"https://crates.io/api/v1/crates/{package_name}") + async with httpx.AsyncClient() as client: + response = await client.get(f"https://crates.io/api/v1/crates/{package_name}") if response.status_code != 200: - await event.reply("Package not found.") + await event.reply(failed_search_answer()) return package_data = response.json() crate = package_data["crate"] @@ -66,16 +67,16 @@ async def find_rs_package(event: events.NewMessage.Event): @config.telegram_client.on(events.NewMessage(pattern=r"^\.aur (.*)")) async def find_aur_package(event: events.NewMessage.Event): package_name = event.pattern_match.group(1).strip() - client = http3.AsyncClient() - response = await client.get( - f"https://aur.archlinux.org/rpc/?v=5&type=info&arg[]={package_name}" - ) + async with httpx.AsyncClient() as client: + response = await client.get( + f"https://aur.archlinux.org/rpc/?v=5&type=info&arg[]={package_name}" + ) if response.status_code != 200: - await event.reply("Package not found.") + await event.reply(failed_search_answer()) return response = response.json() if response["resultcount"] < 1: - await event.reply("Package not found.") + await event.reply(failed_search_answer()) return pkg_info = response["results"][0] response_str = render_package_info( diff --git a/src/actions/search_engines.py b/src/actions/search_engines.py new file mode 100644 index 0000000000000000000000000000000000000000..4a2e6b4c41801852ce25068fdf4830fce5b77a80 --- /dev/null +++ b/src/actions/search_engines.py @@ -0,0 +1,62 @@ +import logging +from urllib.parse import urlencode + +import httpx +from telethon import events + +from src.config import config +from src.utils.responses import failed_search_answer, thinking + +logger = logging.getLogger(__name__) + + +@config.telegram_client.on(events.NewMessage(pattern=r"^\.i (.*)", forwards=False)) +@thinking +async def search_images(event: events.NewMessage.Event): + query_str = event.pattern_match.group(1).strip() + params = { + "count": "30", + "q": query_str, + "t": "images", + "locale": "ru_RU", + "uiv": "5", + } + async with httpx.AsyncClient() as client: + search_res = await client.get( + f"https://api.qwant.com/api/search/images?{urlencode(params)}", + headers={"User-Agent": "S3rius bot 1.0"}, + ) + if search_res.status_code == 429: + logger.warning("Limit is reached") + await event.reply( + "КажиÑÑŒ они прознали что Ñ Ð±Ð¾Ñ‚. Кто-нибудь решите капчу за менÑ." + ) + if search_res.status_code != 200: + logger.warning("Image search status code is not 200") + logger.debug(search_res.content) + await event.reply(failed_search_answer()) + return + response_json = search_res.json() + if response_json["status"] != "success": + logger.warning("Search status is not 'success'") + await event.reply(failed_search_answer()) + return + resp_data = response_json["data"] + if resp_data["result"]["total"] < 1: + await event.reply(failed_search_answer()) + return + images_resp = resp_data["result"]["items"] + images = [] + async with httpx.AsyncClient() as client: + for item in images_resp[:10]: + if "media" in item: + logger.debug(item["media"]) + try: + test_img_res = await client.get(item["media"]) + if test_img_res.status_code == 200: + img_data = test_img_res.content + images.append(img_data) + except Exception as e: + logger.warning(e) + chat = await event.get_chat() + await config.telegram_client.send_file(chat, images) diff --git a/src/utils/library_package.py b/src/utils/library_package.py index 5439aab7deec0cf436776f4d625bf1c7436377de..a15b6d9507801e48aae25656f2cf99b1bcd0e16d 100644 --- a/src/utils/library_package.py +++ b/src/utils/library_package.py @@ -1,9 +1,10 @@ import urllib.parse from operator import itemgetter -import http3 +import httpx from src.config import config +from src.utils.responses import failed_search_answer def render_package_info(info: dict, add_command: str) -> str: @@ -14,13 +15,14 @@ def render_package_info(info: dict, add_command: str) -> str: async def get_library_info(manager, package_name, install_cmd): - client = http3.AsyncClient() query_str = urllib.parse.urlencode({"api_key": config.libraries_io_token}) - revisions_response = await client.get( - f"https://libraries.io/api/{manager}/{package_name}?{query_str}" - ) + async with httpx.AsyncClient() as client: + revisions_response = await client.get( + f"https://libraries.io/api/{manager}/{package_name}?{query_str}" + ) if revisions_response.status_code != 200: - return "Package not found" + return failed_search_answer() + pkg_info = revisions_response.json() versions = list(map(itemgetter("number"), pkg_info["versions"])) if len(versions) > 0: diff --git a/src/utils/responses.py b/src/utils/responses.py new file mode 100644 index 0000000000000000000000000000000000000000..4673a7e67102d4a76ed8c447ef7d32b3fd0b4144 --- /dev/null +++ b/src/utils/responses.py @@ -0,0 +1,61 @@ +import asyncio +import functools +import logging +import random + +from telethon import events + +logger = logging.getLogger(__name__) + +FAILED_SEARCH = [ + "Что-то Ñ Ð½Ð¸Ñ‡ÐµÐ³Ð¾ не нашел.", + "Сколько не иÑкал, так ничего и на нашел.", + "Ðе ищетÑÑ Ñ‚Ð°ÐºÐ°Ñ Ñ‚ÐµÐ¼Ð°.", + "Странный запроÑ. У Ð¼ÐµÐ½Ñ Ð½Ð¸Ñ‡ÐµÐ³Ð¾ не нашло.", + "Перепроверь запроÑ, у Ð¼ÐµÐ½Ñ Ð² поÑке пуÑто.", +] + +THINKING_MSGS = [ + "Кхммм...", + "СейчаÑ-ÑейчаÑ", + "Уже почти вижу результат.", + "Ð Ñто законно, кÑтати?", + "Что-то лезет по моему TCP.", + "Какой большой пейлоад. ПрÑм не знаю, Ñмогу ли Ñ...", + "Пока Ñ Ð¸Ñ‰Ñƒ отправьте Леночке привет.", + "Шота какта Ñложна думаеца.", + "Ждем-Ñ...", +] + + +def failed_search_answer() -> str: + return random.choice(FAILED_SEARCH) + + +def thinking(f): + @functools.wraps(f) + async def updated_function(event: events.NewMessage.Event): + think_msg = random.choice(THINKING_MSGS) + message = await event.reply(think_msg) + + async def update_thinking_msg(): + while True: + await asyncio.sleep(3) + valid_choices = set(THINKING_MSGS) - {think_msg} + await message.edit(random.choice(list(valid_choices))) + + thinking_task = asyncio.create_task(update_thinking_msg()) + main_task = asyncio.create_task(f(event)) + task_iterator = asyncio.as_completed([main_task, thinking_task]) + try: + await next(task_iterator) + except Exception as e: + logger.warning(f"Found error while thinking: {e}") + thinking_task.cancel() + try: + await thinking_task + except asyncio.CancelledError: + logger.debug("Thinking cancelled.") + await message.delete() + + return updated_function diff --git a/static_messages/help.txt b/static_messages/help.txt index a3a40b768a3d3cad3cbff2dc51358f566251b6d0..35c29fd472f1e400741546dd32db3f55e78087a3 100644 --- a/static_messages/help.txt +++ b/static_messages/help.txt @@ -12,6 +12,9 @@ * `.aur` -- Ð´Ð»Ñ Arch Linux * `.brew` -- Ð´Ð»Ñ MacOs +Также можно быÑтро поиÑкать картиночки в интенетиках. +`.i <запроÑ>` вернет пачку картинок Ñ Ð¸Ð½Ñ‚ÐµÑ€Ð½ÐµÑ‚Ð¸ÐºÐ¾Ð². + Также умею ещё пару забавных штук. * `.bl` <любой текÑÑ‚> - заменит вÑе запÑтые на вÑÑкие гадоÑти. * `.t` - показать текущее времÑ