Source code for quackamollie.core.bot.quackamollie_bot

# -*- coding: utf-8 -*-
""" Module to define commands, handle additional configuration and start the bot """
__all__ = ["get_commands_list", "start_quackamollie_bot"]
__author__ = "QuacktorAI"
__copyright__ = "Copyright 2024, Forge of Absurd Ducks"
__credits__ = ["QuacktorAI"]

import logging

from aiogram import Bot, Dispatcher
from aiogram.types import BotCommand
from click import BadParameter
from sqlalchemy.ext.asyncio import async_sessionmaker, AsyncEngine, AsyncSession
from typing import List

from quackamollie.core.bot.bot_info import QuackamollieBotData
from quackamollie.core.bot.callback.info_callback import info_router
from quackamollie.core.bot.callback.user_settings_callback import user_settings_router
from quackamollie.core.bot.callback.chat_settings_callback import chat_settings_router
from quackamollie.core.bot.callback.app_settings_callback import app_settings_router
from quackamollie.core.bot.command.history import history_router
from quackamollie.core.bot.command.reset import reset_router
from quackamollie.core.bot.command.settings_bot_command import settings_router
from quackamollie.core.bot.command.start_bot_command import start_router
from quackamollie.core.bot.command.message_by_id import message_by_id_router
from quackamollie.core.bot.handler.messages import message_router
from quackamollie.core.bot.middleware.user_filter import get_user_filter_outer_middleware_router
from quackamollie.core.cli.settings import QuackamollieSettings
from quackamollie.core.database.query.startup_query import (ensure_app_roles_registered_in_db,
                                                            ensure_bot_user_registered_in_db,
                                                            ensure_model_manager_types_registered_in_db)
from quackamollie.core.model_manager_registry.model_manager_registry import QuackamollieModelManagerRegistry

log = logging.getLogger(__name__)


[docs] def get_commands_list() -> List[BotCommand]: return [ BotCommand(command="start", description="Start a new chat with Quackamollie"), BotCommand(command="settings", description="Access and change Quackamollie settings"), BotCommand(command="reset", description="Reset Chat"), BotCommand(command="history", description="Look through messages visible to the model"), BotCommand(command="message_by_id", description="Get the content of a message of the current chat from its ID"), # TODO: Improving settings will lead to using finite state machines, implement `/cancel` command to delete state # types.BotCommand(command="cancel", description="Cancel an active action"), ]
[docs] async def start_quackamollie_bot(settings: QuackamollieSettings, bot: Bot, dispatcher: Dispatcher, commands: List[BotCommand], engine: AsyncEngine, async_session: async_sessionmaker[AsyncSession]): """ Start the Quackamollie Telegram bot and close the database engine after polling ends :param settings: Quackamollie settings to pass between commands of quackamollie :type settings: QuackamollieSettings :param bot: The Telegram bot already initialized with token :type bot: Bot :param dispatcher: The aiogram dispatcher for messages and Telegram commands :type dispatcher: Dispatcher :param commands: The list of the Bot commands :type commands: List[BotCommand] :param engine: Database engine to dispose when the bot is stopped :type engine: AsyncEngine :param async_session: Database asynchronous session maker :type async_session: async_sessionmaker[AsyncSession] """ # Ensure given default model name matches a model in the default model manager default_model_manager = settings.default_model_manager default_model = settings.default_model if default_model_manager is not None and default_model is not None: model_managers = QuackamollieModelManagerRegistry().model_managers model_list = await model_managers[default_model_manager].get_model_list() if default_model not in model_list: raise BadParameter(f"No model found with name '{default_model}' among models of the" f" '{default_model_manager}' model manager. Available models" f" are {', '.join(model_list)}.", param_hint="'--default-model'") # Add routers to the dispatcher dispatcher.include_router(get_user_filter_outer_middleware_router()) dispatcher.include_router(start_router) dispatcher.include_router(settings_router) dispatcher.include_router(reset_router) dispatcher.include_router(history_router) dispatcher.include_router(message_by_id_router) dispatcher.include_router(info_router) dispatcher.include_router(user_settings_router) dispatcher.include_router(chat_settings_router) dispatcher.include_router(app_settings_router) dispatcher.include_router(message_router) log.debug("Routers added") await bot.set_my_commands(commands) log.debug("Commands set") quackamollie_bot_data = QuackamollieBotData() await quackamollie_bot_data.load_bot_info(bot) log.debug("QuackamollieBotData set") await ensure_app_roles_registered_in_db(async_session) log.debug("Database initialized with application roles") await ensure_bot_user_registered_in_db(async_session) log.debug("Database initialized with bot user") await ensure_model_manager_types_registered_in_db(async_session) log.debug("Database initialized with model managers types") log.debug("Start polling") await dispatcher.start_polling(bot, skip_update=True) # Close and clean-up pooled connections for database engine log.debug("Closing database engine") await engine.dispose()