diff --git a/modules/bot.py b/modules/bot.py new file mode 100644 index 0000000..578fe88 --- /dev/null +++ b/modules/bot.py @@ -0,0 +1,116 @@ +import logging + +from aiogram import Bot +from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup +from aiogram.client.default import DefaultBotProperties + +from config import Config +from typing import Dict, Any, List + +logger = logging.getLogger(__name__) + + +class NotificationBot: + def __init__(self, config: Config): + self.config = config + + try: + self.bot: Bot = Bot( + token=config.bot_token, + default=DefaultBotProperties( + parse_mode="HTML", + disable_notification=True, + link_preview_is_disabled=True, + ), + ) + + except: + raise + + async def _notify(self, message: str, reply_to: int, link: str = None): + try: + keyboard = None + if link: + keyboard = InlineKeyboardMarkup( + inline_keyboard=[[InlineKeyboardButton(text=f"Перейти")]] + ) + + if not reply_to: + sent_message = await self.bot.send_message( + chat_id=self.config.admin_id, text=message, reply_markup=keyboard + ) + else: + sent_message = await self.bot.send_message( + chat_id=self.config.admin_id, + text=message, + reply_to_message_id=reply_to, + reply_markup=keyboard, + ) + + return sent_message.message_id + + except Exception as e: + logger.error(f"Ошибка отправки уведомления: {e}") + + async def new_post(self, post: Dict[str, Any], links: List[str]): + try: + thread = post.get("thread") + + thread_title = thread.get("thread_title") + permalink = post.get("links").get("permalink") + thread_link = f'{thread_title}' + + poster_username = post.get("poster_username") + poster_user_id = post.get("poster_user_id") + poster_link = f"https://lolz.team/members/{poster_user_id}" + user_link = f'{poster_username}' + + links_msg = f"
\n" + for link in links: + links_msg += f"{link}\n" + + links_msg += "
" + + message = ( + f"🚩 Новый ответ в теме {thread_link}:\n" + f"▪️ Пользователь: {user_link}\n" + f"▪️ Ссылки: {links_msg}" + ) + + post_message_id = await self._notify(message=message, link=permalink) + return post_message_id + + except: + return None + + async def success(self, link: str, post_message_id: int): + try: + message = ( + f"🌟 Звезда отправлена:\n" + f"▪️ Количество: {self.config.stars_count} ⭐️\n" + f"▪️ Ссылка на сообщение: {link}" + ) + + success_message_id = await self._notify( + message=message, reply_to=post_message_id, link=link + ) + return success_message_id + + except: + return None + + async def failure(self, post: Dict[str, Any], reason: str, post_message_id: int): + try: + permalink = post.get("links").get("permalink") + message = ( + f"❌ Не удалось отправить звезду:\n" + f"▪️ Причина: {reason}" + ) + + success_message_id = await self._notify( + message=message, reply_to=post_message_id, link=permalink + ) + return success_message_id + + except: + return None diff --git a/modules/telegram.py b/modules/telegram.py index c5d3baf..ea585b5 100644 --- a/modules/telegram.py +++ b/modules/telegram.py @@ -11,6 +11,7 @@ from typing import List, Optional, Dict, Any from config import Config from modules.lolz import Lolz +from modules.bot import NotificationBot from modules.misc import ProcessedPostsManager logger = logging.getLogger(__name__) @@ -57,6 +58,10 @@ class TelegramStarsBot: self.client: Optional[Client] = None + self.notification_bot = None + if config.bot_token and config.admin_id and config.notify_admin: + self.notification_bot = NotificationBot(config) + async def parse_existing_posts(self): logger.info("Начинаю парсинг всех существующих постов в теме...") all_posts = await self.lolz_api.get_all_thread_posts( @@ -137,8 +142,11 @@ class TelegramStarsBot: return False async def _process_single_post(self, post: Dict[str, Any]): + post_message_id = None + post_id = post.get("post_id") poster_user_id = post.get("poster_user_id") + poster_username = post.get("poster_username", "Неизвестный пользователь") if not post_id or self.processed_manager.is_processed(post_id): if post_id: @@ -147,7 +155,9 @@ class TelegramStarsBot: logger.warning("Пост без post_id, пропускаю.") return - logger.info(f"Найден новый пост для обработки: ID {post_id}") + logger.info( + f"Найден новый пост для обработки: ID {post_id} (от {poster_username})" + ) if self.config.skip_commented: if await self.lolz_api.has_comments(post=post): @@ -155,21 +165,47 @@ class TelegramStarsBot: f"Пост {post_id} уже имеет комментарии. Пропускаю обработку." ) self.processed_manager.mark_processed(post_id) + + if self.notification_bot: + self.notification_bot.failure( + post=post, + reason=f"пост уже имеет комментарии", + post_message_id=post_message_id, + ) + return post_content = post.get("post_body_html") or post.get("post_body") if not post_content: logger.warning(f"У поста {post_id} отсутствует содержимое. Пропускаем.") self.processed_manager.mark_processed(post_id) + + if self.notification_bot: + self.notification_bot.failure( + post=post, + reason=f"у поста отсутствует содержимое", + post_message_id=post_message_id, + ) + return links = TelegramLinkExtractor.extract(post_content) if not links: logger.info(f"В посте {post_id} не найдено ссылок Telegram.") self.processed_manager.mark_processed(post_id) + + if self.notification_bot: + self.notification_bot.failure( + post=post, + reason=f"в посте не найдено ссылок Telegram", + post_message_id=post_message_id, + ) + return logger.info(f"В посте {post_id} найдено ссылок: {len(links)}.") + if self.notification_bot: + post_message_id = self.notification_bot.new_post(post=post, links=links) successful_reactions = 0 for link in links: @@ -194,15 +230,36 @@ class TelegramStarsBot: reply_message = f"[userids={poster_user_id};align=left]{random.choice(reply_options)}[/userids]" await self.lolz_api.create_comment(post_id, reply_message) + + if self.notification_bot: + self.notification_bot.success( + link=parsed_link, post_message_id=post_message_id + ) + else: logger.warning( f"Пост {post_id}: poster_user_id отсутствует, комментарий не оставлен." ) + + if self.notification_bot: + self.notification_bot.failure( + post=post, + reason=f"poster_user_id отсутствует", + post_message_id=post_message_id, + ) + elif successful_reactions == 0: logger.warning( f"Пост {post_id}: ни одна звезда не была отправлена успешно ({len(links)} ссылок)." ) + if self.notification_bot: + self.notification_bot.failure( + post=post, + reason=f"ни одна звезда не была отправлена успешно", + post_message_id=post_message_id, + ) + logger.info(f"Пост {post_id} полностью обработан.") self.processed_manager.mark_processed(post_id) @@ -277,16 +334,8 @@ class TelegramStarsBot: logger.info("Пожалуйста, перезапустите скрипт для начала работы.") return - logger.info("=" * 40) - logger.info( - f"Комментарии на форуме: {'ВКЛЮЧЕНЫ' if self.config.enable_reply else 'ВЫКЛЮЧЕНЫ'}" - ) - logger.info( - f"Пропуск постов с комментариями: {'ВКЛЮЧЕН' if self.config.skip_commented else 'ВЫКЛЮЧЕН'}" - ) logger.info(f"Проверка начинается со страницы: {self.start_page}") logger.info("Бот в работе. Для остановки нажмите Ctrl+C.") - logger.info("=" * 40) await self._main_loop() finally: diff --git a/requirements.txt b/requirements.txt index 4481601..fec5bbb 100644 Binary files a/requirements.txt and b/requirements.txt differ