Files
trueconf_bot/main.py
T

218 lines
9.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import sys
import asyncio
import logging
import os
import psutil
import time
from datetime import datetime
# --- НАСТРОЙКА ЛОГИРОВАНИЯ ---
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s - %(message)s",
handlers=[
logging.FileHandler("bot_log.txt", encoding="utf-8"),
logging.StreamHandler(sys.stdout)
]
)
logging.getLogger('trueconf.client.chatbot').propagate = False
logger = logging.getLogger(__name__)
# Добавляем путь к проекту в sys.path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from trueconf import Bot, Dispatcher, Router, Message
from config.config import *
from utils.states import set_state, clear_state, get_state
from utils import states
# 🎯 ИСПРАВЛЕНО: Импортируем оба варианта меню (короткий и приветственный)
from utils.menu import MENU_TEXT, WELCOME_TEXT, MENU_MAP
# =========================================================
# 🔄 МОДУЛЬ АВТОМАТИЧЕСКОГО КОНТРОЛЯ СЕССИЙ И ТАЙМАУТОВ
# =========================================================
USER_LAST_ACTIVITY = {}
USER_MENU_SHOWN = {}
DEFAULT_TIMEOUT = 1800
_orig_get_state = states.get_state
def patched_get_state(user_id):
current_time = time.time()
last_activity = USER_LAST_ACTIVITY.get(user_id)
session_timeout = getattr(states.config, 'SESSION_TIMEOUT', DEFAULT_TIMEOUT)
if last_activity and (current_time - last_activity > session_timeout):
states.clear_state(user_id)
USER_MENU_SHOWN[user_id] = False
logger.info(f"⏳ Сессия пользователя {user_id} сброшена по таймауту бездействия ({session_timeout} сек).")
USER_LAST_ACTIVITY[user_id] = current_time
return _orig_get_state(user_id)
states.get_state = patched_get_state
get_state = patched_get_state
# =========================================================
# 🔌 КОРПОРАТИВНЫЕ ШАБЛОНЫ ТЕКСТОВ КЛЕВЕР
# =========================================================
from utils.texts import UNKNOWN_MAIN_CMD_TEXT
from utils.stats_logger import log_menu_stats
# =========================================================
# ФУНКЦИЯ МОНИТОРИНГА ОПЕРАТИВНОЙ ПАМЯТИ
# =========================================================
def log_ram_usage(stage_name: str):
process = psutil.Process(os.getpid())
ram_mb = process.memory_info().rss / 1024 / 1024
logger.info(f"📊 [RAM MONITOR] {stage_name}: {ram_mb:.2f} MB")
log_ram_usage("Старт скрипта (базовый вес)")
from photo_bot.handlers import router as photo_router, PHOTO_MAIN_MENU_TEXT
log_ram_usage("После импорта photo_bot")
from service_desk.handlers import router as sd_router, SD_MAIN_MENU_TEXT
log_ram_usage("После импорта service_desk")
from transcription_bot.handlers import router as transcription_router, TRANSCRIPTION_MAIN_MENU_TEXT
log_ram_usage("После импорта transcription_router (Виспер)")
from search_bot.handlers import router as search_router, SEARCH_MAIN_MENU_TEXT
log_ram_usage("После импорта search_router")
from instruct.handlers import router as instruct_router
log_ram_usage("После импорта instruct_router")
from lk.handlers import router as lk_router
log_ram_usage("После импорта lk_router (Личный кабинет)")
# =========================================================
# ГЛАВНЫЙ РОУТЕР
# =========================================================
main_router = Router()
@main_router.message()
async def main_menu(msg: Message):
if ENABLE_WHITELIST and msg.from_user.id not in ALLOWED_USERS:
logger.info(f"Доступ закрыт (Whitelist ON): Игнорирование пользователя {msg.from_user.id}")
return
user_id = msg.from_user.id
if getattr(msg, "handled", False):
return
if get_state(user_id) is not None:
return
is_attachment = hasattr(msg.type, "name") and msg.type.name == "ATTACHMENT"
if is_attachment:
await msg.answer("⚠️ <b>Сначала выберите режим работы из меню.</b>", parse_mode="html")
await msg.answer(WELCOME_TEXT, parse_mode="html")
USER_MENU_SHOWN[user_id] = True
return
menu_shown = USER_MENU_SHOWN.get(user_id, False)
if msg.text:
cmd = msg.text.strip().lower()
# Обработка "0" — возврат в главное меню
if cmd in ("0", "меню"):
await msg.answer(MENU_TEXT, parse_mode="html")
return
action = MENU_MAP.get(cmd)
if action == "SD":
USER_MENU_SHOWN[user_id] = True
set_state(user_id, "SD_MODE")
log_menu_stats(user_id, "Service Desk", "Вход")
await msg.answer(SD_MAIN_MENU_TEXT, parse_mode="html")
return
elif action == "PHOTO":
USER_MENU_SHOWN[user_id] = True
from photo_bot.handlers import check_moderation_status
from utils.texts import photo_moderation_pending
if not PHOTO_TEST_MODE:
status = await check_moderation_status(user_id)
if status.get("status") == "ok" and status.get("moderation_status") == "pending":
photo_id = status.get("photo_id", "???")
log_menu_stats(user_id, "Photo Bot", "Вход заблокирован — фото на модерации")
await msg.answer(
photo_moderation_pending(photo_id),
parse_mode="html"
)
return
set_state(user_id, "PHOTO_MODE")
log_menu_stats(user_id, "Photo Bot (TEST)" if PHOTO_TEST_MODE else "Photo Bot", "Вход")
from utils.texts import PHOTO_TEST_ENTRY_TEXT
await msg.answer(
PHOTO_TEST_ENTRY_TEXT if PHOTO_TEST_MODE else PHOTO_MAIN_MENU_TEXT,
parse_mode="html"
)
return
elif action == "TRANSCRIPTION":
USER_MENU_SHOWN[user_id] = True
set_state(user_id, "TRANSCRIPTION_MODE")
log_menu_stats(user_id, "Speech-to-Text", "Вход")
await msg.answer(TRANSCRIPTION_MAIN_MENU_TEXT, parse_mode="html")
return
elif action == "SEARCH":
USER_MENU_SHOWN[user_id] = True
set_state(user_id, "SEARCH_MODE")
log_menu_stats(user_id, "Search Bot", "Вход")
await msg.answer(SEARCH_MAIN_MENU_TEXT, parse_mode="html")
return
elif action == "LK":
USER_MENU_SHOWN[user_id] = True
set_state(user_id, "LK_MODE")
log_menu_stats(user_id, "Личный кабинет", "Вход")
from lk.handlers import send_dynamic_lk_main_menu
await send_dynamic_lk_main_menu(msg, user_id)
return
elif action == "INSTRUCT":
USER_MENU_SHOWN[user_id] = True
set_state(user_id, "INSTRUCT_MODE")
log_menu_stats(user_id, "Инструкции", "Вход")
await msg.answer(
"📋 <b>Выберите инструкцию:</b>\n\n"
"1⃣ — 🖥 Trueconf\n"
"2⃣ — 📧 Почта\n\n"
"<i>0⃣ — В главное меню</i>",
parse_mode="html"
)
return
else:
if not menu_shown:
USER_MENU_SHOWN[user_id] = True
await msg.answer(WELCOME_TEXT, parse_mode="html")
return
await msg.answer(UNKNOWN_MAIN_CMD_TEXT, parse_mode="html")
return
USER_MENU_SHOWN[user_id] = True
await msg.answer(WELCOME_TEXT, parse_mode="html")
async def main():
logger.info("Инициализация Dispatcher и подключение роутеров...")
dp = Dispatcher()
dp.include_router(photo_router)
dp.include_router(sd_router)
dp.include_router(transcription_router)
dp.include_router(search_router)
dp.include_router(instruct_router)
dp.include_router(lk_router)
dp.include_router(main_router)
bot = Bot.from_credentials(server=TC_SERVER, username=TC_LOGIN, password=TC_PASSWORD, dispatcher=dp, verify_ssl=False)
states.tc_bot = bot
logger.info("Бот запущен и готов к работе!")
target_chat = globals().get('ADMIN_CHAT_ID', "533a9784afdfe80c8d80b971a732cd2520a71dc6")
async def send_startup_notification(bot_instance, chat_id):
await asyncio.sleep(10)
try:
await bot_instance.send_message(chat_id, "✅ <b>Я - online!</b> Запуск прошел успешно.", parse_mode="html")
logger.info(f"Стартовое сообщение успешно отправлено в чат {chat_id}")
except Exception as e:
logger.error(f"Не удалось отправить стартовое сообщение администратору: {e}")
asyncio.create_task(send_startup_notification(bot, target_chat))
logger.info("Подключение к серверу TrueConf...")
await bot.run()
if __name__ == "__main__":
try:
main_router.message()
asyncio.run(main())
except KeyboardInterrupt:
logger.info("Бот остановлен вручную.")