122 lines
3.8 KiB
Python
122 lines
3.8 KiB
Python
import asyncio
|
||
import logging
|
||
import os
|
||
from logging.handlers import RotatingFileHandler
|
||
from aiogram import Bot, Dispatcher, types
|
||
from aiogram.filters import Command
|
||
from aiogram.types import BotCommand
|
||
|
||
from config import BOT_TOKEN, ALLOWED_CHAT_ID
|
||
from prompts import SYSTEM_PROMPT
|
||
from llm import ask_llm
|
||
from chat_history import load_history, add_message
|
||
from scheduler import BytikScheduler
|
||
|
||
LOG_DIR = "logs"
|
||
os.makedirs(LOG_DIR, exist_ok=True)
|
||
|
||
logging.basicConfig(
|
||
level=logging.INFO,
|
||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||
handlers=[
|
||
RotatingFileHandler(
|
||
os.path.join(LOG_DIR, "bytik.log"),
|
||
maxBytes=5*1024*1024,
|
||
backupCount=3,
|
||
encoding="utf-8",
|
||
),
|
||
logging.StreamHandler(),
|
||
],
|
||
)
|
||
logger = logging.getLogger(__name__)
|
||
|
||
bot = Bot(token=BOT_TOKEN)
|
||
dp = Dispatcher()
|
||
scheduler = None
|
||
|
||
def is_allowed_chat(chat_id: int) -> bool:
|
||
return chat_id == ALLOWED_CHAT_ID
|
||
|
||
@dp.message(Command("start"))
|
||
async def cmd_start(message: types.Message):
|
||
if not is_allowed_chat(message.chat.id):
|
||
logger.warning(f"Попытка /start из неразрешённого чата: {message.chat.id}")
|
||
return
|
||
await message.answer("Привет, Егор! 👋 Я Байтик, твой робот-помощник! Спрашивай меня о чём угодно! 🤖")
|
||
|
||
@dp.message(Command("clear"))
|
||
async def cmd_clear(message: types.Message):
|
||
if not is_allowed_chat(message.chat.id):
|
||
return
|
||
from chat_history import clear_history
|
||
clear_history(message.from_user.id)
|
||
await message.answer("История наших разговоров очищена! 🧹")
|
||
|
||
@dp.message()
|
||
async def handle_message(message: types.Message):
|
||
if not message.text:
|
||
return
|
||
|
||
if not is_allowed_chat(message.chat.id):
|
||
logger.debug(f"Игнорирую сообщение из чата {message.chat.id}")
|
||
return
|
||
|
||
user_message = message.text.strip()
|
||
logger.info(f"Сообщение от user {message.from_user.id}: {user_message[:100]}")
|
||
|
||
typing_msg = await message.answer("Думаю... 🤔")
|
||
|
||
history = load_history(message.from_user.id)
|
||
messages_for_llm = [{"role": "system", "content": SYSTEM_PROMPT}] + history
|
||
messages_for_llm.append({"role": "user", "content": user_message})
|
||
|
||
add_message(message.from_user.id, "user", user_message)
|
||
|
||
answer = await ask_llm(messages_for_llm)
|
||
|
||
add_message(message.from_user.id, "assistant", answer)
|
||
|
||
try:
|
||
await typing_msg.edit_text(answer)
|
||
except Exception:
|
||
await message.answer(answer)
|
||
|
||
@dp.my_chat_member()
|
||
async def handle_bot_status_change(update: types.ChatMemberUpdated):
|
||
chat_id = update.chat.id
|
||
new_status = update.new_chat_member.status
|
||
|
||
if new_status == "member" and chat_id != ALLOWED_CHAT_ID:
|
||
logger.warning(f"Бот добавлен в неразрешённый чат {chat_id}. Покидаю.")
|
||
await bot.leave_chat(chat_id)
|
||
elif new_status == "left":
|
||
logger.info(f"Бот удалён из чата {chat_id}")
|
||
|
||
async def set_commands():
|
||
commands = [
|
||
BotCommand(command="start", description="Начать общение"),
|
||
BotCommand(command="clear", description="Очистить историю диалога"),
|
||
]
|
||
await bot.set_my_commands(commands)
|
||
|
||
async def main():
|
||
global scheduler
|
||
await set_commands()
|
||
|
||
scheduler = BytikScheduler(bot)
|
||
scheduler.start()
|
||
|
||
logger.info(f"Бот запущен. Whitelist chat_id: {ALLOWED_CHAT_ID}")
|
||
await dp.start_polling(bot)
|
||
|
||
async def on_shutdown():
|
||
if scheduler:
|
||
scheduler.shutdown()
|
||
await bot.session.close()
|
||
|
||
if __name__ == "__main__":
|
||
try:
|
||
asyncio.run(main())
|
||
except (KeyboardInterrupt, SystemExit):
|
||
logger.info("Бот остановлен")
|