diff --git a/src/api/methods.py b/src/api/methods.py index 6b9999b..9d5fe68 100644 --- a/src/api/methods.py +++ b/src/api/methods.py @@ -27,9 +27,7 @@ async def _post(self, path: str, user_id: int = None, **data: any) -> any: data["user_id"] = user_id async with aiohttp.ClientSession() as session: r: aiohttp.ClientResponse - async with session.post( - self.url + path, json=data, headers={"X-Token": self.secret} - ) as r: + async with session.post(self.url + path, json=data, headers={"X-Token": self.secret}) as r: if r.status != 200: if r.status in (400, 422): json = await r.json() @@ -47,14 +45,10 @@ async def create_room(self, name: str, user_id: int) -> int: return await self._post("/bot/room/create", user_id, room={"name": name}) async def invite_person(self, alias: str, user_id: int) -> int: - return await self._post( - "/bot/invitation/create", user_id, addressee={"alias": alias} - ) + return await self._post("/bot/invitation/create", user_id, addressee={"alias": alias}) async def accept_invitation(self, id_: int, user_id: int) -> int: - return await self._post( - "/bot/invitation/accept", user_id, invitation={"id": id_} - ) + return await self._post("/bot/invitation/accept", user_id, invitation={"id": id_}) async def create_order(self, users: list[int], user_id: int) -> int: return await self._post("/bot/order/create", user_id, order={"users": users}) @@ -66,38 +60,25 @@ async def modify_task(self, body: ModifyTaskBody, user_id: int) -> bool: return await self._post("/bot/task/modify", user_id, task=body.model_dump()) async def get_daily_info(self, user_id: int) -> DailyInfoResponse: - return DailyInfoResponse.model_validate( - await self._post("/bot/room/daily_info", user_id) - ) + return DailyInfoResponse.model_validate(await self._post("/bot/room/daily_info", user_id)) - async def get_incoming_invitations( - self, alias: str, user_id: int - ) -> list[IncomingInvitationInfo]: + async def get_incoming_invitations(self, alias: str, user_id: int) -> list[IncomingInvitationInfo]: return [ IncomingInvitationInfo.model_validate(obj) - for obj in await self._post("/bot/invitation/inbox", user_id, alias=alias)[ - "invitations" - ] + for obj in await self._post("/bot/invitation/inbox", user_id, alias=alias)["invitations"] ] async def get_room_info(self, user_id: int) -> RoomInfoResponse: - return RoomInfoResponse.model_validate( - await self._post("/bot/room/info", user_id) - ) + return RoomInfoResponse.model_validate(await self._post("/bot/room/info", user_id)) async def leave_room(self, user_id: int) -> bool: return await self._post("/bot/room/leave", user_id) async def get_tasks(self, user_id: int) -> list[Task]: - return [ - Task.model_validate(obj) - for obj in await self._post("/bot/task/list", user_id)["tasks"] - ] + return [Task.model_validate(obj) for obj in await self._post("/bot/task/list", user_id)["tasks"]] async def get_task_info(self, id_: int, user_id: int) -> TaskInfoResponse: - return TaskInfoResponse.model_validate( - await self._post("/bot/task/info", user_id, task={"id": id_}) - ) + return TaskInfoResponse.model_validate(await self._post("/bot/task/info", user_id, task={"id": id_})) async def get_sent_invitations(self, user_id: int) -> list[SentInvitationInfo]: return [ @@ -106,14 +87,10 @@ async def get_sent_invitations(self, user_id: int) -> list[SentInvitationInfo]: ] async def delete_invitation(self, id_: int, user_id: int) -> bool: - return await self._post( - "/bot/invitation/delete", user_id, invitation={"id": id_} - ) + return await self._post("/bot/invitation/delete", user_id, invitation={"id": id_}) async def reject_invitation(self, id_: int, user_id: int) -> bool: - return await self._post( - "/bot/invitation/reject", user_id, invitation={"id": id_} - ) + return await self._post("/bot/invitation/reject", user_id, invitation={"id": id_}) async def get_order_info(self, id_: int, user_id: int) -> list[int]: return await self._post("/bot/order/info", user_id, order={"id": id_})["users"] diff --git a/src/bot/dialogs/__init__.py b/src/bot/dialogs/__init__.py new file mode 100644 index 0000000..b21396a --- /dev/null +++ b/src/bot/dialogs/__init__.py @@ -0,0 +1,3 @@ +from src.bot.dialogs.roomless import roomless_dialog + +__all__ = ["roomless_dialog"] diff --git a/src/bot/dialogs/communication.py b/src/bot/dialogs/communication.py new file mode 100644 index 0000000..3e665d8 --- /dev/null +++ b/src/bot/dialogs/communication.py @@ -0,0 +1,16 @@ +from dataclasses import dataclass + + +@dataclass +class CreateRoomDialogResult: + name: str | None + ok: bool = True + + +@dataclass +class RoomDialogStartData: + id: int + name: str + + +__all__ = ["CreateRoomDialogResult", "RoomDialogStartData"] diff --git a/src/bot/dialogs/create_room.py b/src/bot/dialogs/create_room.py new file mode 100644 index 0000000..17fd5b9 --- /dev/null +++ b/src/bot/dialogs/create_room.py @@ -0,0 +1,25 @@ +from aiogram.types import Message +from aiogram_dialog import Dialog, Window, DialogManager, ShowMode +from aiogram_dialog.widgets.input import TextInput +from aiogram_dialog.widgets.kbd import Cancel +from aiogram_dialog.widgets.text import Const + +from src.bot.dialogs.communication import CreateRoomDialogResult +from src.bot.dialogs.states import CreateRoomSG + + +NAME_INPUT_ID = "name_input" + + +async def text_handler(message: Message, widget, dialog_manager: DialogManager, _): + await dialog_manager.done(CreateRoomDialogResult(name=message.text), show_mode=ShowMode.SEND) + + +create_room_dialog = Dialog( + Window( + Const("Enter a room's name"), + Cancel(result=CreateRoomDialogResult(name=None, ok=False)), + TextInput(NAME_INPUT_ID, on_success=text_handler), + state=CreateRoomSG.enter_name, + ) +) diff --git a/src/bot/dialogs/room.py b/src/bot/dialogs/room.py new file mode 100644 index 0000000..6f1ada7 --- /dev/null +++ b/src/bot/dialogs/room.py @@ -0,0 +1,27 @@ +from aiogram_dialog import Dialog, Window, DialogManager +from aiogram_dialog.widgets.text import Format + +from src.bot.dialogs.communication import RoomDialogStartData +from src.bot.dialogs.states import RoomSG + + +async def getter(dialog_manager: DialogManager): + room_info: RoomDialogStartData = dialog_manager.dialog_data["room_info"] + return { + "room_id": room_info.id, + "room_name": room_info.name, + } + + +async def on_start(start_data: RoomDialogStartData, dialog_manager: DialogManager): + dialog_manager.dialog_data["room_info"] = start_data + + +room_dialog = Dialog( + Window( + Format("Your room: {room_name}\nID: {room_id}"), + getter=getter, + state=RoomSG.main, + ), + on_start=on_start, +) diff --git a/src/bot/dialogs/roomless.py b/src/bot/dialogs/roomless.py new file mode 100644 index 0000000..fdba982 --- /dev/null +++ b/src/bot/dialogs/roomless.py @@ -0,0 +1,47 @@ +from aiogram.types import CallbackQuery +from aiogram_dialog import Window, Dialog, DialogManager, Data, ShowMode, StartMode +from aiogram_dialog.widgets.text import Const +from aiogram_dialog.widgets.kbd import Row, Button + +from src.api import client +from src.bot.dialogs.communication import CreateRoomDialogResult, RoomDialogStartData +from src.bot.dialogs.states import RoomlessSG, CreateRoomSG, RoomSG + +WELCOME_MESSAGE = """This is a welcome message + +You can: +- Accept an invitation to a room +- Create a new room""" + +INVITATIONS_BUTTON_ID = "invitations_button" +CREATE_ROOM_BUTTON_ID = "create_room_button" + + +async def process_result(start_data: Data, result: CreateRoomDialogResult, dialog_manager: DialogManager): + if result.ok: + room_id = await client.create_room(result.name, dialog_manager.event.from_user.id) + # noinspection PyTypeChecker + await dialog_manager.start( + RoomSG.main, data=RoomDialogStartData(room_id, result.name), mode=StartMode.RESET_STACK + ) + + +async def start_creating_room(event: CallbackQuery, button: Button, dialog_manager: DialogManager): + await dialog_manager.start(CreateRoomSG.enter_name, show_mode=ShowMode.SEND) + + +roomless_dialog = Dialog( + Window( + Const(WELCOME_MESSAGE), + Row( + Button(Const("Invitations"), INVITATIONS_BUTTON_ID), + Button( + Const("Create"), + CREATE_ROOM_BUTTON_ID, + on_click=start_creating_room, + ), + ), + state=RoomlessSG.welcome, + ), + on_process_result=process_result, +) diff --git a/src/bot/dialogs/states.py b/src/bot/dialogs/states.py new file mode 100644 index 0000000..6a378b4 --- /dev/null +++ b/src/bot/dialogs/states.py @@ -0,0 +1,14 @@ +from aiogram.fsm.state import StatesGroup, State + + +class CreateRoomSG(StatesGroup): + enter_name = State() + + +class RoomlessSG(StatesGroup): + welcome = State() + invitations = State() + + +class RoomSG(StatesGroup): + main = State() diff --git a/src/bot/main.py b/src/bot/main.py index d716653..df42e44 100644 --- a/src/bot/main.py +++ b/src/bot/main.py @@ -4,18 +4,24 @@ import dotenv from aiogram import Bot, Dispatcher +from aiogram.filters import CommandStart from aiogram_dialog import setup_dialogs - -dotenv.load_dotenv(".env") - -bot = Bot(token=os.getenv("BOT_TOKEN")) -dp = Dispatcher() +from src.bot.dialogs.create_room import create_room_dialog +from src.bot.dialogs import roomless_dialog +from src.bot.dialogs.room import room_dialog +from src.bot.start_message import start_message_handler async def main(): + dotenv.load_dotenv(".env") logging.basicConfig(level=logging.INFO) + bot = Bot(token=os.getenv("BOT_TOKEN")) + dp = Dispatcher() + dp.message.register(start_message_handler, CommandStart()) + dp.include_routers(roomless_dialog, create_room_dialog, room_dialog) + setup_dialogs(dp) await dp.start_polling(bot) diff --git a/src/bot/start_message.py b/src/bot/start_message.py new file mode 100644 index 0000000..0e4f2a8 --- /dev/null +++ b/src/bot/start_message.py @@ -0,0 +1,25 @@ +from aiogram.types import Message +from aiogram_dialog import DialogManager, StartMode + +from src.api import client +from src.bot.dialogs.communication import RoomDialogStartData +from src.bot.dialogs.states import RoomlessSG, RoomSG + + +async def start_message_handler(message: Message, dialog_manager: DialogManager): + user_id = message.from_user.id + try: + await client.create_user(user_id) + except RuntimeError: + pass + + try: + room_info = await client.get_room_info(user_id) + # noinspection PyTypeChecker + await dialog_manager.start( + RoomSG.main, + data=RoomDialogStartData(room_info.id, room_info.name), + mode=StartMode.RESET_STACK, + ) + except RuntimeError: + await dialog_manager.start(RoomlessSG.welcome, mode=StartMode.RESET_STACK)