83 lines
2.7 KiB
Python
83 lines
2.7 KiB
Python
|
|
"""Telegram-бот: текстовые и голосовые сообщения → LLM → ответ."""
|
|||
|
|
import asyncio
|
|||
|
|
import logging
|
|||
|
|
import tempfile
|
|||
|
|
from pathlib import Path
|
|||
|
|
|
|||
|
|
from aiogram import Bot, Dispatcher, F, Router
|
|||
|
|
from aiogram.types import Message
|
|||
|
|
|
|||
|
|
import config
|
|||
|
|
from config import get_gitea_username
|
|||
|
|
from llm_handler import process_message
|
|||
|
|
from transcribe import transcribe_audio
|
|||
|
|
|
|||
|
|
logging.basicConfig(level=logging.INFO)
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
config.validate()
|
|||
|
|
|
|||
|
|
bot = Bot(token=config.TELEGRAM_BOT_TOKEN)
|
|||
|
|
dp = Dispatcher()
|
|||
|
|
router = Router()
|
|||
|
|
dp.include_router(router)
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.message(F.text)
|
|||
|
|
async def on_text(message: Message) -> None:
|
|||
|
|
text = (message.text or "").strip()
|
|||
|
|
if not text:
|
|||
|
|
return
|
|||
|
|
gitea_username = get_gitea_username(
|
|||
|
|
message.from_user.username if message.from_user else None,
|
|||
|
|
message.from_user.id if message.from_user else None,
|
|||
|
|
)
|
|||
|
|
try:
|
|||
|
|
reply = await asyncio.wait_for(process_message(text, gitea_username), timeout=60.0)
|
|||
|
|
await message.answer(reply or "Готово.")
|
|||
|
|
except asyncio.TimeoutError:
|
|||
|
|
await message.answer("Таймаут. Попробуй короче или позже.")
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.exception("Handler error")
|
|||
|
|
await message.answer(f"Ошибка: {e!s}")
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.message(F.voice)
|
|||
|
|
async def on_voice(message: Message) -> None:
|
|||
|
|
if not message.voice:
|
|||
|
|
return
|
|||
|
|
gitea_username = get_gitea_username(
|
|||
|
|
message.from_user.username if message.from_user else None,
|
|||
|
|
message.from_user.id if message.from_user else None,
|
|||
|
|
)
|
|||
|
|
tmp = None
|
|||
|
|
try:
|
|||
|
|
file = await message.bot.get_file(message.voice.file_id)
|
|||
|
|
tmp = tempfile.NamedTemporaryFile(suffix=".ogg", delete=False)
|
|||
|
|
tmp.close()
|
|||
|
|
await message.bot.download_file(file.file_path, tmp.name)
|
|||
|
|
text = await asyncio.to_thread(transcribe_audio, tmp.name)
|
|||
|
|
Path(tmp.name).unlink(missing_ok=True)
|
|||
|
|
tmp = None
|
|||
|
|
if not (text or "").strip():
|
|||
|
|
await message.answer("Не удалось распознать речь.")
|
|||
|
|
return
|
|||
|
|
reply = await asyncio.wait_for(process_message(text.strip(), gitea_username), timeout=60.0)
|
|||
|
|
await message.answer(reply or "Готово.")
|
|||
|
|
except asyncio.TimeoutError:
|
|||
|
|
await message.answer("Таймаут. Попробуй короче или позже.")
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.exception("Voice handler error")
|
|||
|
|
await message.answer(f"Ошибка: {e!s}")
|
|||
|
|
finally:
|
|||
|
|
if tmp is not None and getattr(tmp, "name", None):
|
|||
|
|
Path(tmp.name).unlink(missing_ok=True)
|
|||
|
|
|
|||
|
|
|
|||
|
|
async def main() -> None:
|
|||
|
|
await dp.start_polling(bot)
|
|||
|
|
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
asyncio.run(main())
|