/
makk
/
Stone_parser
Обзор
Документация
Войти
/
makk
/
Stone_parser
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
main.py
250 строк
10 KB
makk
stone_monitor
28 май 2026, 09:45
Верифицирован
28 май 2026, 09:45
06d9882
Код
Авторство
О чём код?
""" Stone Monitor v1.1 — бот-парсер упоминаний девелопера Stone. Ежедневный отчёт в Telegram: сбор → дедупликация → AI-анализ → отчёт. """ import logging import sys import threading import telebot from telebot.types import InlineKeyboardMarkup, InlineKeyboardButton from config import ( TG_BOT_TOKEN, TG_CHAT_ID, TG_PARSE_MODE, TG_DISABLE_WEB_PREVIEW, ALL_CHANNELS, ) from collector import collect_telegram_posts, filter_mentions, get_last_stats from dedup import dedup_items, get_cache_stats from analyzer import analyze_mentions from report import format_report, format_empty_report, format_channels_report, split_message # === ЛОГГЕР === logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", stream=sys.stdout, ) logger = logging.getLogger("stone_monitor") # === TELEGRAM БОТ === bot = telebot.TeleBot(TG_BOT_TOKEN) # === БЛОКИРОВКА ПОВТОРНОГО ЗАПУСКА === _report_running = False _report_lock = threading.Lock() def get_menu_markup(): markup = InlineKeyboardMarkup(row_width=1) markup.add( InlineKeyboardButton("🔍 Запустить проверку", callback_data="run_check"), ) markup.add( InlineKeyboardButton("📊 Статус", callback_data="cmd_status"), ) markup.add( InlineKeyboardButton("📡 Каналы", callback_data="cmd_channels"), ) return markup def execute_report(chat_id: int = None, message_id: int = None): """Полный цикл: сбор → дедупликация → AI → отчёт.""" global _report_running if chat_id is None: chat_id = TG_CHAT_ID # Блокировка повторного запуска with _report_lock: if _report_running: bot.send_message(chat_id, "⏳ Отчёт уже выполняется, дождитесь завершения...") logger.warning("Report already running, skipping duplicate call") return _report_running = True logger.info("=== Starting Stone Monitor report ===") def status(text: str): """Обновляет статусное сообщение.""" if message_id: try: bot.edit_message_text(text, chat_id=chat_id, message_id=message_id, parse_mode=TG_PARSE_MODE) except Exception: pass try: # 1. Сбор status("📡 Собираю данные из ТГ...") all_items, collect_stats = collect_telegram_posts() logger.info(f"Collected: {len(all_items)} items, stats: {collect_stats}") total_24h = collect_stats.get("total_24h", 0) status(f"📡 Собрано <b>{total_24h}</b> постов за сутки") if not all_items: report = format_empty_report(collect_stats) bot.send_message(chat_id, report, parse_mode=TG_PARSE_MODE, disable_web_page_preview=TG_DISABLE_WEB_PREVIEW) return # 2. Пред-фильтр: только сообщения со словом "stone" mentions = filter_mentions(all_items) status(f"🔍 Найдено <b>{len(mentions)}</b> упоминаний 'Stone'") if not mentions: report = format_empty_report(collect_stats) bot.send_message(chat_id, report, parse_mode=TG_PARSE_MODE, disable_web_page_preview=TG_DISABLE_WEB_PREVIEW) logger.info("No mentions found") return # 3. Дедупликация new_mentions = dedup_items(mentions) status(f"🗂 После дедупликации: <b>{len(new_mentions)}</b> новых") logger.info(f"After dedup: {len(new_mentions)} new") if not new_mentions: report = format_empty_report(collect_stats) bot.send_message(chat_id, report, parse_mode=TG_PARSE_MODE, disable_web_page_preview=TG_DISABLE_WEB_PREVIEW) logger.info("All mentions already seen") return # 4. AI-анализ (disambiguation + summarisation) status(f"🧠 AI анализирует <b>{len(new_mentions)}</b> сообщений...") analyzed = analyze_mentions(new_mentions) logger.info(f"After AI analysis: {len(analyzed)} Stone mentions") # 5. Отчёт report = format_report(analyzed, collect_stats) parts = split_message(report) for i, part in enumerate(parts): if i == 0: bot.send_message(chat_id, part, parse_mode=TG_PARSE_MODE, disable_web_page_preview=TG_DISABLE_WEB_PREVIEW) else: bot.send_message(chat_id, part, parse_mode=TG_PARSE_MODE, disable_web_page_preview=TG_DISABLE_WEB_PREVIEW) logger.info(f"Report sent: {len(parts)} messages") except Exception as e: logger.error(f"Report execution error: {e}", exc_info=True) bot.send_message(chat_id, f"❌ Ошибка: {str(e)}") finally: with _report_lock: _report_running = False # === ОБРАБОТЧИКИ TELEGRAM === @bot.message_handler(commands=["start", "help"]) def handle_help(message): if message.chat.id != TG_CHAT_ID: bot.send_message(message.chat.id, "⛔ Доступ запрещён.") return help_text = ( "<b>🏗 Stone Monitor v1.1</b>\n\n" "<b>Команды:</b>\n" "/check — запустить проверку упоминаний\n" "/channels — статистика по каналам\n" "/status — статистика кэша\n" "/help — эта справка\n\n" "<b>Мониторинг:</b>\n" f"• {len(ALL_CHANNELS)} каналов\n" "• Категории: Топ рынка, СМИ, Конкуренты, Консультанты, Архбюро, Дополнительно\n" "• AI-фильтр: отделяет Stone-девелопера от stone-камня\n" "• Запуск только вручную (/check или кнопка)" ) bot.send_message(message.chat.id, help_text, parse_mode=TG_PARSE_MODE, disable_web_page_preview=TG_DISABLE_WEB_PREVIEW) @bot.message_handler(commands=["check"]) def handle_check(message): if message.chat.id != TG_CHAT_ID: bot.send_message(message.chat.id, "⛔ Доступ запрещён.") return logger.info(f"Manual /check from {message.chat.id}") msg = bot.send_message(message.chat.id, "🔍 Запускаю проверку упоминаний Stone...", parse_mode=TG_PARSE_MODE) execute_report(message.chat.id, message_id=msg.message_id) @bot.message_handler(commands=["channels"]) def handle_channels(message): if message.chat.id != TG_CHAT_ID: bot.send_message(message.chat.id, "⛔ Доступ запрещён.") return logger.info(f"Manual /channels from {message.chat.id}") stats = get_last_stats() if not stats or stats.get("total_24h", 0) == 0: bot.send_message(message.chat.id, "📡 Нет данных. Запустите проверку через /check", parse_mode=TG_PARSE_MODE) return report = format_channels_report(stats) bot.send_message(message.chat.id, report, parse_mode=TG_PARSE_MODE) @bot.message_handler(commands=["status"]) def handle_status(message): if message.chat.id != TG_CHAT_ID: bot.send_message(message.chat.id, "⛔ Доступ запрещён.") return stats = get_cache_stats() text = ( "<b>📊 Статус Stone Monitor</b>\n" f"💾 Кэш сообщений: {stats['seen_messages']}\n" f"⏳ TTL: {stats['ttl_days']} дней" ) bot.send_message(message.chat.id, text, parse_mode=TG_PARSE_MODE) @bot.message_handler(func=lambda message: True) def handle_any_message(message): if message.chat.id == TG_CHAT_ID: bot.send_message( message.chat.id, "<b>🏗 Stone Monitor v1.1</b>\n" "Выберите действие:", reply_markup=get_menu_markup(), parse_mode=TG_PARSE_MODE, ) @bot.callback_query_handler(func=lambda call: call.data == "run_check") def callback_check(call): bot.answer_callback_query(call.id, "Запускаю проверку...") msg = bot.send_message(call.message.chat.id, "🔍 Запускаю проверку упоминаний Stone...", parse_mode=TG_PARSE_MODE) execute_report(call.message.chat.id, message_id=msg.message_id) @bot.callback_query_handler(func=lambda call: call.data == "cmd_status") def callback_status(call): handle_status(call.message) bot.answer_callback_query(call.id) @bot.callback_query_handler(func=lambda call: call.data == "cmd_channels") def callback_channels(call): bot.answer_callback_query(call.id, "Загружаю статистику...") stats = get_last_stats() if not stats or stats.get("total_24h", 0) == 0: bot.send_message(call.message.chat.id, "📡 Нет данных. Запустите проверку через /check", parse_mode=TG_PARSE_MODE) return report = format_channels_report(stats) bot.send_message(call.message.chat.id, report, parse_mode=TG_PARSE_MODE) # === ТОЧКА ВХОДА === if __name__ == "__main__": logger.info("=" * 50) logger.info("Stone Monitor v1.1 — запуск") logger.info(f"Chat ID: {TG_CHAT_ID}") logger.info("Режим: только ручной запуск (/check)") logger.info("=" * 50) logger.info("Starting polling...") bot.infinity_polling()