diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c7dee5..d9a5a00 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,10 @@ jobs: python -m pip install --upgrade pip pip install -r requirements.txt pytest pytest-asyncio + - name: Compile sources + run: | + python -m compileall -q src main.py + - name: Run tests env: BOT_TOKEN: dummy-token diff --git a/src/app/bot.py b/src/app/bot.py index 64b1fb2..ca22ee4 100644 --- a/src/app/bot.py +++ b/src/app/bot.py @@ -10,6 +10,7 @@ create_room, room_admin, wishes, + custom_invation, invitation, debug, join_room, @@ -34,6 +35,7 @@ async def run_bot(token: str) -> None: wishes.router, room_admin.router, create_room.router, + custom_invation.router, invitation.router, debug.router, join_room.router, diff --git a/src/handlers/custom_invation.py b/src/handlers/custom_invation.py new file mode 100644 index 0000000..51b9331 --- /dev/null +++ b/src/handlers/custom_invation.py @@ -0,0 +1,131 @@ +from aiogram import F, Router +from aiogram.fsm.context import FSMContext +from aiogram.types import Message, CallbackQuery + +from src.db import db +from src.keyboards import common_kb, invitation_kb, room_admin_kb +from src.states.states import Gen, CallbackFactory +from src.texts import messages +from src.texts.callback_actions import CallbackAction +from src.handlers.common import get_room_name + + +router = Router(name=__name__) + + +@router.callback_query( + CallbackFactory.filter(F.action == CallbackAction.CUSTOM_INVITATION) +) +async def custom_invitation( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): + isMemberOrAdmin = await db.check_room_and_member( + call.from_user.id, callback_data.room_iden + ) + room_name = await get_room_name(callback_data.room_iden) + + if isMemberOrAdmin == "MEMBER NOT EXISTS": + await call.message.edit_text( + messages.not_a_member(room_name), + reply_markup=await common_kb.ok_kb("None", asAdmin=False), + ) + return + + elif isMemberOrAdmin == "ROOM NOT EXISTS": + await call.message.edit_text( + messages.room_not_exists(room_name), + reply_markup=await common_kb.ok_kb("None", asAdmin=False), + ) + return + + await state.set_state(Gen.set_custom_invitation) + await state.set_data({"room_iden": callback_data.room_iden}) + + await call.message.answer( + messages.prompt_custom_invitation(room_name), + reply_markup=await common_kb.cancel_kb("None", asAdmin=True), + ) + + +@router.message(Gen.set_custom_invitation) +async def set_custom_invitation(msg: Message, state: FSMContext): + data = await state.get_data() + room_iden = data.get("room_iden") + + if msg.text == "🚫Отмена": + await state.clear() + if room_iden: + room_name = await get_room_name(room_iden) + await msg.answer( + messages.room_admin_title(room_name), + reply_markup=await room_admin_kb.room_admin_kb(room_iden), + ) + else: + await msg.answer(messages.menu(), reply_markup=common_kb.choice_kb) + return + + if msg.media_group_id: + await msg.answer( + messages.media_group_not_supported(), + reply_markup=await common_kb.cancel_kb("None", asAdmin=True), + ) + return + + if not room_iden: + await state.clear() + await msg.answer(messages.menu(), reply_markup=common_kb.choice_kb) + return + + access = await db.check_room_and_member(msg.from_user.id, room_iden) + room_name = await get_room_name(room_iden) + if access == "ROOM NOT EXISTS": + await state.clear() + await msg.answer( + messages.room_not_exists(room_name), + reply_markup=await common_kb.ok_kb("None", asAdmin=False), + ) + return + + if access == "MEMBER NOT EXISTS": + await state.clear() + await msg.answer( + messages.not_a_member(room_name), + reply_markup=await common_kb.ok_kb("None", asAdmin=False), + ) + return + + raw_text = msg.text or msg.caption or "" + if not raw_text and not msg.photo: + await msg.answer( + messages.invitation_empty(), + reply_markup=await common_kb.cancel_kb("None", asAdmin=True), + ) + return + + invitation_text = raw_text.replace("\\", "/").replace("'", "`").replace('"', "`") + manual_join = f"Если приглашение не сработало попробуйте присоединиться в ручном режиме {room_name}" + + if invitation_text.strip(): + full_text = f"{invitation_text}\n\n{manual_join}" + else: + full_text = manual_join + + limit = 1024 if msg.photo else 4096 + if len(full_text) > limit: + await msg.answer( + messages.invitation_too_long(), + reply_markup=await common_kb.cancel_kb("None", asAdmin=True), + ) + return + + info = await msg.bot.get_me() + kb = await invitation_kb.join_to_room_kb(room_iden, info.username) + + await state.clear() + if msg.photo: + await msg.answer_photo( + photo=msg.photo[-1].file_id, caption=full_text, reply_markup=kb + ) + return + + await msg.answer(full_text, reply_markup=kb) diff --git a/src/keyboards/room_admin_kb.py b/src/keyboards/room_admin_kb.py index a93d085..e5e643e 100644 --- a/src/keyboards/room_admin_kb.py +++ b/src/keyboards/room_admin_kb.py @@ -63,6 +63,16 @@ async def room_admin_kb(room_iden): ).pack(), ) ], + [ + InlineKeyboardButton( + text="📝Кастомное приглашение", + callback_data=states.CallbackFactory( + action=CallbackAction.CUSTOM_INVITATION, + room_iden=room_iden, + asAdmin=True, + ).pack(), + ) + ], [ InlineKeyboardButton( text="◀️Вернуться в меню", diff --git a/src/states/states.py b/src/states/states.py index c9709df..f354071 100644 --- a/src/states/states.py +++ b/src/states/states.py @@ -29,5 +29,6 @@ class Gen(StatesGroup): room_name_to_join = State() approval_delete = State() set_wishes = State() + set_custom_invitation = State() set_room_price = State() set_room_time = State() diff --git a/src/texts/callback_actions.py b/src/texts/callback_actions.py index 8e6bfe6..f3248cb 100644 --- a/src/texts/callback_actions.py +++ b/src/texts/callback_actions.py @@ -20,6 +20,7 @@ class CallbackAction: SEE_WISHES = "see_wishes" CREATE_INVITATION = "create_invitation" + CUSTOM_INVITATION = "custom_invitation" START_EVENT = "start_event" DELETE_ROOM = "delete_room" diff --git a/src/texts/messages.py b/src/texts/messages.py index 091edb1..fe2aa43 100644 --- a/src/texts/messages.py +++ b/src/texts/messages.py @@ -144,11 +144,26 @@ def event_started_notify(room_name: str) -> str: def invitation_text(room_name: str) -> str: return ( - f"✉️Приглашение принять участвие в Тайном санта в комнате {room_name}\n" + f"✉️Приглашение принять участвие в Тайном санта в комнате {room_name}\n" "Если приглашение не сработало попробуйте присоединиться в ручном режиме" ) +def prompt_custom_invitation(room_name: str) -> str: + return ( + f"Отправьте текст приглашения для комнаты {room_name}\n" + "Можно добавить одно фото без альбома" + ) + + +def invitation_too_long() -> str: + return "Слишком длинное приглашение. Укоротите текст и отправьте новое" + + +def invitation_empty() -> str: + return "Приглашение не распознано. Отправьте текст или фото с подписью" + + def room_not_exists_retry() -> str: return "Такой комнаты не существует\nПопробуйте ещё раз:"