Update file bot.py

This commit is contained in:
Markov Andrey
2026-06-30 15:55:06 +00:00
parent 3b1c4e2f56
commit eea2bbb62d

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Точка входа для запуска бота. Точка входа для запуска XMPP-бота.
С расширенной диагностикой событий. С расширенной диагностикой событий и использованием Pydantic-конфига (AppConfig).
""" """
import argparse import argparse
@@ -12,14 +12,19 @@ import logging
import signal import signal
from pathlib import Path from pathlib import Path
from core.utils.config_loader import BotConfig # Импорт нового загрузчика и модели конфига
from core.utils.logger import setup_logging from rag.utils.config_loader import load_config
from core.xmpp.client import Bot from rag.config_models import AppConfig
from rag.utils.logger import setup_logging
# Импорт класса Bot (который теперь принимает AppConfig)
from bots.xmpp.client import Bot
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
async def shutdown(sig, bot, stop_event): async def shutdown(sig, bot, stop_event):
"""Graceful shutdown с обработкой сигналов."""
logger.info(f"Получен сигнал {sig.name}, начинаю graceful shutdown...") logger.info(f"Получен сигнал {sig.name}, начинаю graceful shutdown...")
stop_event.set() stop_event.set()
try: try:
@@ -27,40 +32,64 @@ async def shutdown(sig, bot, stop_event):
await bot.disconnect() await bot.disconnect()
except Exception as e: except Exception as e:
logger.error(f"Ошибка отключения: {e}") logger.error(f"Ошибка отключения: {e}")
# Отменяем все задачи, кроме текущей
tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()] tasks = [t for t in asyncio.all_tasks() if t is not asyncio.current_task()]
for task in tasks: for task in tasks:
task.cancel() task.cancel()
await asyncio.gather(*tasks, return_exceptions=True) await asyncio.gather(*tasks, return_exceptions=True)
# Закрываем HTTP-сессию RAG-клиента
try:
await bot.rag_client.close()
except Exception:
pass
logger.info("Бот завершён") logger.info("Бот завершён")
async def main_async(profile_dir: str): async def main_async(profile_dir: str):
"""
Асинхронная инициализация и запуск бота.
"""
# 1. Загружаем конфигурацию через Pydantic (AppConfig)
try: try:
config = BotConfig(profile_dir) config: AppConfig = load_config(profile_dir)
except Exception as e: except Exception as e:
logger.error(f"Ошибка конфига: {e}") logger.error(f"Ошибка загрузки конфигурации: {e}")
sys.exit(1) sys.exit(1)
setup_logging(config.name, config.log_file, log_level=logging.DEBUG) # 2. Настраиваем логирование в режиме DEBUG для диагностики
# (можно переключить через --log-level, но пока жёстко)
setup_logging(config.name, config.log_file_path, log_level=logging.DEBUG)
logger.info(f"Запуск бота {config.name} с профилем {profile_dir}") logger.info(f"Запуск бота {config.name} с профилем {profile_dir}")
# 3. Создаём экземпляр бота
bot = Bot(config) bot = Bot(config)
# Отключаем SRV и устанавливаем IP # ---- ДЛЯ ОТЛАДКИ (как в старом коде) ----
# Отключаем SRV-записи, чтобы явно указать сервер и порт.
# Если в конфиге есть xmpp_server и xmpp_port, используем их,
# иначе оставляем как есть (slixmpp попытается определить автоматически).
bot.use_srv = False bot.use_srv = False
bot.xmpp_server = "192.168.1.150" # Если в конфиге явно не заданы, оставляем значения по умолчанию,
bot.xmpp_port = 5222 # но для отладки можно жёстко прописать IP.
# Попробуем SSL, если не работает — отключим # В production лучше брать из конфига.
# bot.xmpp_use_ssl = False # оставляем как есть if config.xmpp_server:
bot.xmpp_server = config.xmpp_server
else:
# fallback для тестовой среды
bot.xmpp_server = "192.168.1.150"
if config.xmpp_port:
bot.xmpp_port = config.xmpp_port
else:
bot.xmpp_port = 5222
# ---- ДОБАВЛЯЕМ ВСЕ ОБРАБОТЧИКИ ---- # ---- ДОБАВЛЯЕМ ВСЕ ОБРАБОТЧИКИ СОБЫТИЙ (как в старом коде) ----
def on_session_start(event): def on_session_start(event):
logger.info("✅ СЕССИЯ УСПЕШНО УСТАНОВЛЕНА! Бот готов.") logger.info("✅ СЕССИЯ УСПЕШНО УСТАНОВЛЕНА! Бот готов.")
bot.send_presence(pshow='chat', pstatus=config.status_message) bot.send_presence(pshow='chat', pstatus=config.status_message)
def on_failed_auth(event): def on_failed_auth(event):
logger.error("❌ ОШИБКА АВТОРИЗАЦИИ! Проверьте пароль и JID.") logger.error("❌ ОШИБКА АВТОРИЗАЦИИ! Проверьте пароль и JID.")
# Принудительно завершаем бота, чтобы не висеть # Принудительно завершаем бота
asyncio.create_task(shutdown(None, bot, stop_event)) asyncio.create_task(shutdown(None, bot, stop_event))
def on_disconnected(event): def on_disconnected(event):
@@ -94,30 +123,45 @@ async def main_async(profile_dir: str):
bot.add_event_handler("roster_update", on_roster_update) bot.add_event_handler("roster_update", on_roster_update)
bot.add_event_handler("presence", on_presence) bot.add_event_handler("presence", on_presence)
# ---- ОБРАБОТКА СИГНАЛОВ ----
stop_event = asyncio.Event() stop_event = asyncio.Event()
loop = asyncio.get_running_loop() loop = asyncio.get_running_loop()
for sig in (signal.SIGINT, signal.SIGTERM): for sig in (signal.SIGINT, signal.SIGTERM):
loop.add_signal_handler( loop.add_signal_handler(
sig, lambda s=sig: asyncio.create_task(shutdown(s, bot, stop_event)) sig, lambda s=sig: asyncio.create_task(shutdown(s, bot, stop_event))
) )
# ---- ПОДКЛЮЧЕНИЕ ----
logger.info(f"Попытка подключения к {bot.xmpp_server}:{bot.xmpp_port}...") logger.info(f"Попытка подключения к {bot.xmpp_server}:{bot.xmpp_port}...")
bot.connect() bot.connect()
logger.info("connect() вызван, ожидаем событий...") logger.info("connect() вызван, ожидаем событий...")
# Ждём сигнала остановки
await stop_event.wait() await stop_event.wait()
def main(): def main():
parser = argparse.ArgumentParser() """Синхронная точка входа."""
parser.add_argument('--profile-dir', required=True) parser = argparse.ArgumentParser(description="Запуск XMPP-бота")
parser.add_argument(
'--profile-dir',
required=True,
help="Путь к директории профиля бота (например, /usr/local/etc/bots/metabot)"
)
parser.add_argument(
"--log-level",
default="debug",
choices=["debug", "info", "warning", "error", "critical"],
help="Уровень логирования (по умолчанию debug)"
)
args = parser.parse_args() args = parser.parse_args()
profile_dir = Path(args.profile_dir).resolve() profile_dir = Path(args.profile_dir).resolve()
if not profile_dir.exists(): if not profile_dir.exists():
print(f"Ошибка: директория {profile_dir} не существует") print(f"Ошибка: директория {profile_dir} не существует")
sys.exit(1) sys.exit(1)
# Создаём событийный цикл
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop) asyncio.set_event_loop(loop)