Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added bot commands regarding team info #3

Merged
merged 16 commits into from
Oct 23, 2022
2 changes: 2 additions & 0 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ jobs:
runs-on: ubuntu-20.04
env:
DB_URL: postgresql://user:[email protected]:5432/db
BOT_TOKEN: BOT_TOKEN
BOT_NAME: BOT_NAME

services:

Expand Down
21 changes: 20 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,21 @@
import asyncio

from requestor.bot import dp, config, register_handlers, bot, BotCommands
from requestor.services import make_db_service
from requestor.log import setup_logging, app_logger

async def main():
db_service = make_db_service(config)
register_handlers(dp, db_service, config)
setup_logging(config)

await bot.set_my_commands(commands=BotCommands.get_bot_commands())
await db_service.setup()
try:
await dp.start_polling()
finally:
await db_service.cleanup()


if __name__ == "__main__":
pass
asyncio.run(main())
323 changes: 220 additions & 103 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ orjson = "^3.5.3"
alembic = "^1.6.5"
aiohttp = "^3.7.4"
asyncpg = "^0.23.0"
aiogram = "^2.22.2"
uvloop = "^0.17.0"

[tool.poetry.dev-dependencies]
black = "22.3.0"
Expand Down
11 changes: 11 additions & 0 deletions requestor/bot/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from .commands import BotCommands
from .create_bot import bot, config, dp
from .handlers import register_handlers

__all__ = (
"dp",
"bot",
"config",
"register_handlers",
"BotCommands",
)
42 changes: 42 additions & 0 deletions requestor/bot/bot_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import typing as tp

from aiogram import types

from requestor.models import TeamInfo


# TODO: somehow try generalize this func to reduce duplicate code
def parse_msg_with_team_info(
message: types.Message,
) -> tp.Tuple[tp.Optional[str], tp.Optional[TeamInfo]]:
args = message.get_args().split()
n_args = len(args)
if n_args == 4:
token, title, api_base_url, api_key = args
elif n_args == 3:
token, title, api_base_url = args
api_key = None

try:
return token, TeamInfo(
title=title, chat_id=message.chat.id, api_base_url=api_base_url, api_key=api_key
)
except NameError:
return None, None


def parse_msg_with_model_info(
message: types.Message,
) -> tp.Tuple[tp.Optional[str], tp.Optional[str]]:
args = message.get_args().split(maxsplit=1)
n_args = len(args)
if n_args == 2:
name, description = args
elif n_args == 1:
name = args[0]
description = None

try:
return name, description
except NameError:
return None, None
121 changes: 121 additions & 0 deletions requestor/bot/commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import typing as tp
from dataclasses import dataclass
from enum import Enum

from aiogram.types import BotCommand
from aiogram.utils.markdown import text


@dataclass
class CommandDescription:
command_name: str
short_description: str
long_description: tp.Optional[str] = None


commands_description = (
(
"start",
"Начало работы с ботом",
),
(
"help",
"Список доступных команд",
),
(
"register_team",
"Регистрация команды",
text(
"С помощью этой команды можно зарегистрировать свою команду",
"Принимает на вход аргументы через пробел:",
"token - токен, который генерируется индивидуально для каждой команды.",
"title - название команды, без пробелов.",
"api_base_url - хост, по которому будет находиться API команды.",
"api_key - опционально, токен для запрашивания API.",
"Пример использования:",
"/register_team 123 MyTeamName http://myapi.ru/api/v1 MyApiKey",
sep="\n",
),
),
(
"update_team",
"Обновление информации команды",
text(
"С помощью этой команды можно обновить хост или токен API.",
"Для этого используются соответствующие аргументы через пробел.",
"api_base_url - хост, по которому будет находиться API команды.",
"api_key - токен для запрашивания API.",
"Пример использования для обновления хоста:",
"/update_team api_base_url http://myapi.ru/api/v2",
sep="\n",
),
),
(
"show_current_team",
feldlime marked this conversation as resolved.
Show resolved Hide resolved
"Вывод информацию по текущей команде",
feldlime marked this conversation as resolved.
Show resolved Hide resolved
"Выводит название команды, хоста и API токена",
feldlime marked this conversation as resolved.
Show resolved Hide resolved
),
(
"add_model",
"Добавление новой модели",
text(
"С помощью этой команды можно добавить модели для проверки.",
feldlime marked this conversation as resolved.
Show resolved Hide resolved
"Для этого используются следующие аргументы:",
"name - хост, по которому будет находиться API команды.",
feldlime marked this conversation as resolved.
Show resolved Hide resolved
"description - опционально, более подробное описание модели",
"Пример использования для обновления хоста:",
"/add_model lightfm_64",
feldlime marked this conversation as resolved.
Show resolved Hide resolved
("Далее модели будут запрашиваться по адресу: " "{api_base_url}/{name}/{user_id}"),
feldlime marked this conversation as resolved.
Show resolved Hide resolved
(
"То есть адрес для запроса выглядит, например, так: "
"http://myapi.ru/api/v1/lightfm_64/123"
feldlime marked this conversation as resolved.
Show resolved Hide resolved
),
sep="\n",
),
),
(
"show_models",
"Вывод информации по добавленным моделям",
text(
"С помощью этой команды можно вывести следующую информацию:",
"Название, описание (если присутствует) и дату добавления модели."
"Если было добавлено более 10 моделей, то выведутся последние 10 по дате добавления",
sep="\n",
),
),
# TODO: create request command
("request", "Запрос рекомендаций по модели", "Какое-то описание"),
)

cmd2cls_desc = {args[0]: CommandDescription(*args) for args in commands_description}


# it can be initialized via Enum("BotCommands", cmd2cls_desc)
# but IDE doesn't provide you with helper annotations
# and you can not add class methods without "hacks"
# TODO: think of simple way instantiate a frozen class
# with typehinting from IDE
class BotCommands(Enum):
start: CommandDescription = cmd2cls_desc["start"]
help: CommandDescription = cmd2cls_desc["help"]
register_team: CommandDescription = cmd2cls_desc["register_team"]
update_team: CommandDescription = cmd2cls_desc["update_team"]
show_current_team: CommandDescription = cmd2cls_desc["show_current_team"]
add_model: CommandDescription = cmd2cls_desc["add_model"]
show_models: CommandDescription = cmd2cls_desc["show_models"]

@classmethod
def get_bot_commands(cls) -> tp.List[BotCommand]:
return [
BotCommand(command=command.name, description=command.value.short_description)
for command in BotCommands
]

@classmethod
def get_description_for_available_commands(cls) -> str:
descriptions = []
for command in BotCommands:
if command not in (BotCommands.start, BotCommands.help):
descriptions.append(f"/{command.name}\n{command.value.long_description}")

return "\n\n".join(descriptions)
14 changes: 14 additions & 0 deletions requestor/bot/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import typing as tp

AVAILABLE_FOR_UPDATE: tp.Final = {
"api_base_url",
"api_key",
}

INCORRECT_DATA_IN_MSG: tp.Final = (
"Пожалуйста, введите данные в корректном формате. Используйте команду /help для справки."
)

TEAM_NOT_FOUND_MSG: tp.Final = (
"Команда от вашего чата не найдена. Скорее всего, вы еще не регистрировались."
)
10 changes: 10 additions & 0 deletions requestor/bot/create_bot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from aiogram import Bot
from aiogram.dispatcher import Dispatcher

from requestor.settings import get_config

config = get_config()

bot = Bot(token=config.telegram_config.bot_token)

dp = Dispatcher(bot)
Loading