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Попробуйте ещё раз:"