From 98a14935f65c11c0f46a01ce9b404128af34d407 Mon Sep 17 00:00:00 2001 From: kitiketov Date: Mon, 24 Nov 2025 20:39:17 +0500 Subject: [PATCH 1/3] =?UTF-8?q?black=20=D0=BF=D0=BE=D1=87=D0=B8=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D1=81=D0=BF=D0=B8=D1=81=D0=BA=D0=B0=20=D1=83=D1=87=D0=B0=D1=81?= =?UTF-8?q?=D1=82=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2,=20=D0=BF=D0=BE=D1=87?= =?UTF-8?q?=D0=B8=D0=BD=D0=B5=D0=BD=D0=B0=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA?= =?UTF-8?q?=D0=B0=20=D0=BF=D1=80=D0=B8=20=D0=BE=D1=82=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BA=D0=B5=20=D1=84=D0=BE=D1=82=D0=BA=D0=B8=20=D0=B2=20?= =?UTF-8?q?=D0=B6=D0=B5=D0=BB=D0=B0=D0=BD=D0=B8=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/bot.py | 9 +- src/db/db.py | 143 +++++++++++++++++++++++++------- src/handlers/common.py | 21 ++++- src/handlers/create_room.py | 11 ++- src/handlers/debug.py | 10 +-- src/handlers/invitation.py | 15 ++-- src/handlers/join_room.py | 14 +++- src/handlers/legacy_route.py | 77 ++++++++++++----- src/handlers/room_admin.py | 54 ++++++++---- src/handlers/wishes.py | 96 ++++++++++++++------- src/keyboards/invitation_kb.py | 2 +- src/keyboards/room_admin_kb.py | 32 +++++-- src/keyboards/room_member_kb.py | 20 +++-- src/settings/settings.py | 1 - src/states/states.py | 2 + src/texts/messages.py | 2 + src/texts/text.py | 5 +- src/utilities/utils.py | 16 ++-- test/test_db.py | 56 ++++++++++--- 19 files changed, 434 insertions(+), 152 deletions(-) diff --git a/src/app/bot.py b/src/app/bot.py index feff9ce..afe5bda 100644 --- a/src/app/bot.py +++ b/src/app/bot.py @@ -11,15 +11,16 @@ wishes, invitation, debug, - join_room + join_room, ) async def run_bot(token: str) -> None: await db.start_db() - bot = Bot(token, - default=DefaultBotProperties(parse_mode="HTML"), - ) + bot = Bot( + token, + default=DefaultBotProperties(parse_mode="HTML"), + ) dp = Dispatcher(storage=MemoryStorage()) dp.include_routers( diff --git a/src/db/db.py b/src/db/db.py index 7677447..1387702 100644 --- a/src/db/db.py +++ b/src/db/db.py @@ -10,9 +10,11 @@ async def start_db(): cur.execute( - "CREATE TABLE IF NOT EXISTS rooms(room_iden TEXT PRIMARY KEY,status BOOLEAN DEFAULT FALSE,admin INTEGER)") + "CREATE TABLE IF NOT EXISTS rooms(room_iden TEXT PRIMARY KEY,status BOOLEAN DEFAULT FALSE,admin INTEGER)" + ) cur.execute( - "CREATE TABLE IF NOT EXISTS users(tg_id INTEGER PRIMARY KEY,first_name TEXT,last_name TEXT,username TEXT)") + "CREATE TABLE IF NOT EXISTS users(tg_id INTEGER PRIMARY KEY,first_name TEXT,last_name TEXT,username TEXT)" + ) cur.execute( """ CREATE TABLE IF NOT EXISTS user_rooms( @@ -28,23 +30,73 @@ async def start_db(): async def create_room(room_name, user_id): - disvalid = ["_saint", "_mem", "\\", ".", "/", ",", ":", ";", "'", "\"", " ", "*", "+", "-", "#", "@", "$", "%", "^", - "!", "~", "`", "&", "|", "<", ">", "[", "]", "(", ")", "{", "}"] - if all([a not in room_name for a in disvalid]) and room_name[0] not in "0123456789" and len(room_name) <= 30: + disvalid = [ + "_saint", + "_mem", + "\\", + ".", + "/", + ",", + ":", + ";", + "'", + '"', + " ", + "*", + "+", + "-", + "#", + "@", + "$", + "%", + "^", + "!", + "~", + "`", + "&", + "|", + "<", + ">", + "[", + "]", + "(", + ")", + "{", + "}", + ] + if ( + all([a not in room_name for a in disvalid]) + and room_name[0] not in "0123456789" + and len(room_name) <= 30 + ): while True: room_id = f"{random.randint(1, 9999):04}" room_iden = f"{room_name}{room_id}" - _room_iden = cur.execute("SELECT 1 FROM rooms WHERE room_iden = ?", (room_iden,)).fetchone() + _room_iden = cur.execute( + "SELECT 1 FROM rooms WHERE room_iden = ?", (room_iden,) + ).fetchone() if not _room_iden: break member_table_name = f"{room_iden}_mem" saint_table_name = f"{room_iden}_saint" - cur.execute("INSERT INTO rooms (room_iden,admin) VALUES (?, ?)", (room_iden, user_id)) - cur.execute(f"CREATE TABLE {member_table_name} (user_id INTEGER PRIMARY KEY, wishes TEXT, photo_id TEXT)") - cur.execute(f"CREATE TABLE {saint_table_name} (saint_user_id INTEGER PRIMARY KEY,reciver_user_id INTEGER)") + cur.execute( + "INSERT INTO rooms (room_iden,admin) VALUES (?, ?)", (room_iden, user_id) + ) + cur.execute( + f"CREATE TABLE {member_table_name} (user_id INTEGER PRIMARY KEY, wishes TEXT, photo_id TEXT)" + ) + cur.execute( + f"CREATE TABLE {saint_table_name} (saint_user_id INTEGER PRIMARY KEY,reciver_user_id INTEGER)" + ) - cur.execute("INSERT OR IGNORE INTO user_rooms (tg_id, room_iden) VALUES (?, ?)", (user_id, room_iden)) - cur.execute("UPDATE user_rooms SET is_admin = TRUE WHERE tg_id = ? AND room_iden = ?", (user_id, room_iden)) + cur.execute( + "INSERT OR IGNORE INTO user_rooms (tg_id, room_iden) VALUES (?, ?)", + (user_id, room_iden), + ) + cur.execute( + "UPDATE user_rooms SET is_admin = TRUE WHERE tg_id = ? AND room_iden = ?", + (user_id, room_iden), + ) db.commit() return room_id @@ -74,15 +126,21 @@ async def connect2room(raw_data, user_id): room_name, room_id, *_ = raw_data.split(":") room_iden = f"{room_name}{room_id}" table_name = f"{room_iden}_mem" - _room = cur.execute("SELECT * FROM rooms WHERE room_iden = ?", (room_iden,)).fetchone() + _room = cur.execute( + "SELECT * FROM rooms WHERE room_iden = ?", (room_iden,) + ).fetchone() if not _room: return "room_error" if _room[1] == True: return "joined late" - _user = cur.execute(f"SELECT * FROM {table_name} WHERE user_id = ?", (user_id,)).fetchone() + _user = cur.execute( + f"SELECT * FROM {table_name} WHERE user_id = ?", (user_id,) + ).fetchone() if _user: return "user_error" - cur.execute(f"INSERT INTO {table_name} (user_id,wishes) VALUES (?, '-')", (user_id,)) + cur.execute( + f"INSERT INTO {table_name} (user_id,wishes) VALUES (?, '-')", (user_id,) + ) _room = cur.execute( "SELECT * FROM user_rooms WHERE tg_id = ? AND room_iden = ?", @@ -104,17 +162,23 @@ async def connect2room(raw_data, user_id): async def get_members_list(room_iden): - admin_raw = cur.execute("SELECT * FROM rooms WHERE room_iden = ?", (room_iden,)).fetchone() + admin_raw = cur.execute( + "SELECT * FROM rooms WHERE room_iden = ?", (room_iden,) + ).fetchone() *_, admin_id = admin_raw isAdminMember = False member_id_list = cur.execute(f"SELECT * FROM {room_iden}_mem ").fetchall() - admin = member = cur.execute("SELECT * FROM users WHERE tg_id = ?", (admin_id,)).fetchone() + admin = member = cur.execute( + "SELECT * FROM users WHERE tg_id = ?", (admin_id,) + ).fetchone() member_list = [] for member_id in member_id_list: if member_id[0] != admin_id: - member = cur.execute("SELECT * FROM users WHERE tg_id = ?", (member_id[0],)).fetchone() + member = cur.execute( + "SELECT * FROM users WHERE tg_id = ?", (member_id[0],) + ).fetchone() member_list.append(member) else: isAdminMember = True @@ -154,9 +218,15 @@ async def delete_room(room_iden, admin_id): saint_table_name = f"{room_iden}_saint" users_id = cur.execute(f"SELECT user_id FROM {member_table_name}").fetchall() - cur.execute("DELETE FROM user_rooms WHERE tg_id = ? AND room_iden = ?", (admin_id, room_iden)) + cur.execute( + "DELETE FROM user_rooms WHERE tg_id = ? AND room_iden = ?", + (admin_id, room_iden), + ) for user_id in users_id: - cur.execute("DELETE FROM user_rooms WHERE tg_id = ? AND room_iden = ?", (user_id[0], room_iden)) + cur.execute( + "DELETE FROM user_rooms WHERE tg_id = ? AND room_iden = ?", + (user_id[0], room_iden), + ) cur.execute(f"DROP TABLE IF EXISTS {member_table_name}") cur.execute(f"DROP TABLE IF EXISTS {saint_table_name}") @@ -181,14 +251,18 @@ async def start_event(room_iden): async def who_gives(room_iden, user_id): table_name = f"{room_iden}_saint" - pair = cur.execute(f"SELECT * FROM {table_name} WHERE saint_user_id = ?", (user_id,)).fetchone() + pair = cur.execute( + f"SELECT * FROM {table_name} WHERE saint_user_id = ?", (user_id,) + ).fetchone() if not pair: - return 'JOINED LATE' + return "JOINED LATE" return pair[1] async def isStarted(room_iden): - _, status, _ = cur.execute("SELECT * FROM rooms WHERE room_iden = ?", (room_iden,)).fetchone() + _, status, _ = cur.execute( + "SELECT * FROM rooms WHERE room_iden = ?", (room_iden,) + ).fetchone() return status @@ -197,11 +271,15 @@ async def get_user(user_id): async def check_room_and_member(user_id, room_iden): - _room = cur.execute("SELECT * FROM rooms WHERE room_iden = ?", (room_iden,)).fetchone() + _room = cur.execute( + "SELECT * FROM rooms WHERE room_iden = ?", (room_iden,) + ).fetchone() if not _room: return "ROOM NOT EXISTS" table_name = f"{room_iden}_mem" - _user = cur.execute(f"SELECT * FROM {table_name} WHERE user_id = ?", (user_id,)).fetchone() + _user = cur.execute( + f"SELECT * FROM {table_name} WHERE user_id = ?", (user_id,) + ).fetchone() if not _user and _room[2] == user_id: return "IS ADMIN" if not _user: @@ -218,11 +296,15 @@ async def count_user_room(user_id): async def get_wishes_and_photo(room_iden, user_id): - _room = cur.execute("SELECT * FROM rooms WHERE room_iden = ?", (room_iden,)).fetchone() + _room = cur.execute( + "SELECT * FROM rooms WHERE room_iden = ?", (room_iden,) + ).fetchone() if not _room: return "ROOM NOT EXISTS", None, None table_name = f"{room_iden}_mem" - _user = cur.execute(f"SELECT * FROM {table_name} WHERE user_id = ?", (user_id,)).fetchone() + _user = cur.execute( + f"SELECT * FROM {table_name} WHERE user_id = ?", (user_id,) + ).fetchone() if not _user: return "MEMBER NOT EXISTS", None, None _, wishes, photo_id = _user @@ -238,11 +320,16 @@ async def edit_wishes(wishes, user_id, room_iden, photo_id=""): return "ROOM NOT EXISTS" table_name = f"{room_iden}_mem" - _user = cur.execute(f"SELECT * FROM {table_name} WHERE user_id = ?", (user_id,)).fetchone() + _user = cur.execute( + f"SELECT * FROM {table_name} WHERE user_id = ?", (user_id,) + ).fetchone() if not _user: return "MEMBER NOT EXISTS" - cur.execute(f"UPDATE {table_name} SET wishes = ?, photo_id = ? WHERE user_id = ?", (wishes, photo_id, user_id)) + cur.execute( + f"UPDATE {table_name} SET wishes = ?, photo_id = ? WHERE user_id = ?", + (wishes, photo_id, user_id), + ) db.commit() return True diff --git a/src/handlers/common.py b/src/handlers/common.py index 1037270..d7c8057 100644 --- a/src/handlers/common.py +++ b/src/handlers/common.py @@ -1,5 +1,5 @@ from aiogram import F, Router -from aiogram.types import Message, CallbackQuery +from aiogram.types import Message, CallbackQuery, ReactionTypeEmoji from aiogram.filters import Command from aiogram.fsm.context import FSMContext @@ -11,14 +11,31 @@ from src.texts.callback_actions import CallbackAction +async def set_reaction(message: Message) -> None: + """ + Устанавливает реакцию 👍 на сообщение пользователя. + + Args: + message (Message): Объект сообщения от пользователя. + """ + await message.bot.set_message_reaction( + chat_id=message.chat.id, + message_id=message.message_id, + reaction=[ReactionTypeEmoji(emoji="👍")], + ) + + async def get_room_name(room_iden): return f"{room_iden[:-4]}:{room_iden[-4:]}" router = Router(name=__name__) + @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.CANCEL)) -async def cancel(call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext): +async def cancel( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): await db.update_user(call.from_user) if callback_data.room_iden == "None": await state.clear() diff --git a/src/handlers/create_room.py b/src/handlers/create_room.py index 5fb3552..21ed13e 100644 --- a/src/handlers/create_room.py +++ b/src/handlers/create_room.py @@ -3,6 +3,7 @@ from aiogram.types import Message, CallbackQuery from src.db import db +from src.handlers.common import set_reaction from src.keyboards import common_kb, room_admin_kb from src.states.states import Gen, CallbackFactory from src.texts import messages @@ -12,7 +13,9 @@ @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.CREATE_ROOM)) -async def start_create_room(call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext): +async def start_create_room( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): await db.update_user(call.from_user) room_count = await db.count_user_room(call.from_user.id) @@ -25,7 +28,10 @@ async def start_create_room(call: CallbackQuery, callback_data: CallbackFactory, await db.add_user(call.from_user) await state.set_state(Gen.room_name_to_create) - await call.message.answer(messages.prompt_create_room_name(), reply_markup=await common_kb.cancel_kb("None", False)) + await call.message.answer( + messages.prompt_create_room_name(), + reply_markup=await common_kb.cancel_kb("None", False), + ) @router.message(Gen.room_name_to_create) @@ -48,6 +54,7 @@ async def create_room(msg: Message, state: FSMContext): await state.clear() kb = await room_admin_kb.room_admin_kb(f"{name}{id}") + await set_reaction(msg) await msg.answer( messages.room_created(name, id), reply_markup=kb, diff --git a/src/handlers/debug.py b/src/handlers/debug.py index f3a6495..6a6ae11 100644 --- a/src/handlers/debug.py +++ b/src/handlers/debug.py @@ -26,14 +26,14 @@ async def get_id(msg: Message): if msg.forward_from_chat: chat_id = msg.forward_from_chat.id - await msg.answer( - f"Твой id: {forwarder_id}\n" - f"ID канала/чата: {chat_id}" + await msg.answer(f"Твой id: {forwarder_id}\n" f"ID канала/чата: {chat_id}") + await msg.bot.send_message( + chat_id=chat_id, + text="Привет!", ) - await msg.bot.send_message(chat_id=chat_id, text='Привет!',) return await msg.answer( "Не могу получить ID оригинального отправителя — " "скорее всего, у него включена приватность пересылок." - ) \ No newline at end of file + ) diff --git a/src/handlers/invitation.py b/src/handlers/invitation.py index 375d8fc..df53adc 100644 --- a/src/handlers/invitation.py +++ b/src/handlers/invitation.py @@ -16,10 +16,16 @@ async def get_room_name(room_iden): router = Router(name=__name__) -@router.callback_query(CallbackFactory.filter(F.action == CallbackAction.CREATE_INVITATION)) -async def create_invitation(call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext): +@router.callback_query( + CallbackFactory.filter(F.action == CallbackAction.CREATE_INVITATION) +) +async def create_invitation( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): await db.update_user(call.from_user) - isMemberOrAdmin = await db.check_room_and_member(call.from_user.id, callback_data.room_iden) + 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": @@ -36,7 +42,6 @@ async def create_invitation(call: CallbackQuery, callback_data: CallbackFactory, ) return info = await call.bot.get_me() - kb = await invitation_kb.join_to_room_kb(callback_data.room_iden,info.username) + kb = await invitation_kb.join_to_room_kb(callback_data.room_iden, info.username) await call.message.answer(messages.invitation_text(room_name), reply_markup=kb) - diff --git a/src/handlers/join_room.py b/src/handlers/join_room.py index ce8642a..cfcc1e7 100644 --- a/src/handlers/join_room.py +++ b/src/handlers/join_room.py @@ -17,7 +17,9 @@ async def get_room_name(room_iden): @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.JOIN_ROOM)) -async def start_join_room(call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext): +async def start_join_room( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): await db.add_user(call.from_user) await state.set_state(Gen.room_name_to_join) await call.message.answer( @@ -38,11 +40,17 @@ async def join_room(msg: Message, state: FSMContext): room_status = await db.connect2room(name, msg.from_user.id) if room_status == "room_error": - await msg.answer(messages.room_not_exists_retry(), reply_markup=await common_kb.cancel_kb("None", False)) + await msg.answer( + messages.room_not_exists_retry(), + reply_markup=await common_kb.cancel_kb("None", False), + ) return elif room_status == "user_error": - await msg.answer(messages.user_already_in_room(), reply_markup=await common_kb.cancel_kb("None", False)) + await msg.answer( + messages.user_already_in_room(), + reply_markup=await common_kb.cancel_kb("None", False), + ) await state.clear() return diff --git a/src/handlers/legacy_route.py b/src/handlers/legacy_route.py index deaffe2..72b080b 100644 --- a/src/handlers/legacy_route.py +++ b/src/handlers/legacy_route.py @@ -35,7 +35,10 @@ async def start_handler(msg: Message): room_status = await db.connect2room(name, msg.from_user.id) if room_status == "room_error": - await msg.answer(messages.room_not_exists(), reply_markup=await common_kb.cancel_kb("None", False)) + await msg.answer( + messages.room_not_exists(), + reply_markup=await common_kb.cancel_kb("None", False), + ) return if room_status == "user_error": @@ -47,22 +50,28 @@ async def start_handler(msg: Message): if room_status == "joined late": await msg.answer( - messages.game_already_started(), reply_markup=await common_kb.cancel_kb("None", False) + messages.game_already_started(), + reply_markup=await common_kb.cancel_kb("None", False), ) return kb = await room_member_kb.room_member_kb(f"{''.join(name.split(':'))}") - await msg.answer(messages.join_success(msg.from_user.first_name, name), reply_markup=kb) + await msg.answer( + messages.join_success(msg.from_user.first_name, name), reply_markup=kb + ) @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.REFRESH_LIST)) @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.MEMBERS_LIST)) async def get_member_list(call: CallbackQuery, callback_data: CallbackFactory): await db.update_user(call.from_user) - isMemberOrAdmin = await db.check_room_and_member(call.from_user.id, callback_data.room_iden) + print(call.from_user.id, callback_data.room_iden) + 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" or (callback_data.asAdmin == False and isMemberOrAdmin == "IS ADMIN"): + 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), @@ -79,30 +88,43 @@ async def get_member_list(call: CallbackQuery, callback_data: CallbackFactory): if callback_data.action == CallbackAction.REFRESH_LIST: await call.bot.delete_message(call.from_user.id, call.message.message_id) - member_list, admin, isAdminMember = await db.get_members_list(callback_data.room_iden) + member_list, admin, isAdminMember = await db.get_members_list( + callback_data.room_iden + ) if isAdminMember: member_list.append(admin) ans = await text.create_member_list(member_list, admin, callback_data.room_iden) await call.message.answer( - ans, reply_markup=await room_admin_kb.refresh_list_kb(callback_data.room_iden, callback_data.asAdmin) + ans, + reply_markup=await room_admin_kb.refresh_list_kb( + callback_data.room_iden, callback_data.asAdmin + ), ) @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.LEAVE_ROOM)) -async def cancel(call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext): +async def cancel( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): await db.update_user(call.from_user) await db.leave_room(callback_data.room_iden, call.from_user.id) await call.message.edit_text(messages.left_room(), reply_markup=common_kb.choice_kb) @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.LIST_OF_ROOMS)) -async def get_list_of_rooms(call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext): +async def get_list_of_rooms( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): await db.update_user(call.from_user) - await call.message.edit_text(messages.choose_option(), reply_markup=rooms_kb.my_rooms_kb) + await call.message.edit_text( + messages.choose_option(), reply_markup=rooms_kb.my_rooms_kb + ) @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.MY_ROOMS)) -async def get_my_admin_rooms(call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext): +async def get_my_admin_rooms( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): await db.update_user(call.from_user) rooms = await db.get_my_rooms(call.from_user.id, callback_data.asAdmin) @@ -111,9 +133,13 @@ async def get_my_admin_rooms(call: CallbackQuery, callback_data: CallbackFactory @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.SHOW_ROOM)) -async def show_room(call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext): +async def show_room( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): await db.update_user(call.from_user) - isMemberOrAdmin = await db.check_room_and_member(call.from_user.id, callback_data.room_iden) + 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 == "ROOM NOT EXISTS": @@ -130,7 +156,9 @@ async def show_room(call: CallbackQuery, callback_data: CallbackFactory, state: ) return - if isMemberOrAdmin == "MEMBER NOT EXISTS" or (callback_data.asAdmin == False and isMemberOrAdmin == "IS ADMIN"): + if isMemberOrAdmin == "MEMBER NOT EXISTS" or ( + callback_data.asAdmin == False and isMemberOrAdmin == "IS ADMIN" + ): await call.message.edit_text( messages.not_a_member(room_name), reply_markup=await common_kb.ok_kb("None", asAdmin=False), @@ -144,10 +172,14 @@ async def show_room(call: CallbackQuery, callback_data: CallbackFactory, state: @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.WHO_GIVES)) -async def who_gives(call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext): +async def who_gives( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): await db.update_user(call.from_user) - isMemberOrAdmin = await db.check_room_and_member(call.from_user.id, callback_data.room_iden) - room_name = f'{callback_data.room_iden[:-4]}:{callback_data.room_iden[-4:]}' + isMemberOrAdmin = await db.check_room_and_member( + call.from_user.id, callback_data.room_iden + ) + room_name = f"{callback_data.room_iden[:-4]}:{callback_data.room_iden[-4:]}" async def safe_edit(text: str, markup): try: @@ -158,7 +190,10 @@ async def safe_edit(text: str, markup): raise if isMemberOrAdmin == "ROOM NOT EXISTS": - await safe_edit(messages.room_not_exists(room_name), await common_kb.ok_kb("None", asAdmin=False)) + await safe_edit( + messages.room_not_exists(room_name), + await common_kb.ok_kb("None", asAdmin=False), + ) return status = await db.isStarted(callback_data.room_iden) @@ -170,7 +205,7 @@ async def safe_edit(text: str, markup): return member_id = await db.who_gives(callback_data.room_iden, call.from_user.id) - if member_id == 'JOINED LATE': + if member_id == "JOINED LATE": await safe_edit( messages.event_started_before_join(room_name), await room_member_kb.room_member_kb(callback_data.room_iden), @@ -182,5 +217,7 @@ async def safe_edit(text: str, markup): user_info = await text.create_user_info(member) await call.message.answer( messages.gift_target(user_info), - reply_markup=await room_member_kb.wishes_kb2(callback_data.room_iden, asAdmin=False), + reply_markup=await room_member_kb.wishes_kb2( + callback_data.room_iden, asAdmin=False + ), ) diff --git a/src/handlers/room_admin.py b/src/handlers/room_admin.py index cafcb35..69f141b 100644 --- a/src/handlers/room_admin.py +++ b/src/handlers/room_admin.py @@ -11,6 +11,7 @@ from src.texts.callback_actions import CallbackAction from src.utilities import utils + async def get_room_name(room_iden): return f"{room_iden[:-4]}:{room_iden[-4:]}" @@ -18,11 +19,14 @@ async def get_room_name(room_iden): router = Router(name=__name__) - @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.DELETE_ROOM)) -async def delete_room(call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext): +async def delete_room( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): await db.update_user(call.from_user) - isMemberOrAdmin = await db.check_room_and_member(call.from_user.id, callback_data.room_iden) + 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 == "ROOM NOT EXISTS": @@ -33,13 +37,21 @@ async def delete_room(call: CallbackQuery, callback_data: CallbackFactory, state return kb = await room_admin_kb.confirm_kb(callback_data.room_iden, callback_data.asAdmin) - await call.message.answer(messages.room_leave_confirmation(room_name), reply_markup=kb) + await call.message.answer( + messages.room_leave_confirmation(room_name), reply_markup=kb + ) -@router.callback_query(CallbackFactory.filter(F.action == CallbackAction.CONFIRM_DELETE)) -async def delete_room(call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext): +@router.callback_query( + CallbackFactory.filter(F.action == CallbackAction.CONFIRM_DELETE) +) +async def delete_room( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): await db.update_user(call.from_user) - isMemberOrAdmin = await db.check_room_and_member(call.from_user.id, callback_data.room_iden) + 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 == "ROOM NOT EXISTS": @@ -50,11 +62,15 @@ async def delete_room(call: CallbackQuery, callback_data: CallbackFactory, state return await db.delete_room(callback_data.room_iden, call.from_user.id) - await call.message.edit_text(messages.room_deleted(room_name), reply_markup=common_kb.choice_kb) + await call.message.edit_text( + messages.room_deleted(room_name), reply_markup=common_kb.choice_kb + ) @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.REMOVE_MEMBER)) -async def remove_member(call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext): +async def remove_member( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): await db.update_user(call.from_user) members, *_ = await db.get_members_list(callback_data.room_iden) @@ -62,10 +78,16 @@ async def remove_member(call: CallbackQuery, callback_data: CallbackFactory, sta await call.message.answer(messages.choose_option(), reply_markup=kb) -@router.callback_query(RemoveCallbackFactory.filter(F.action == CallbackAction.REMOVE_MEMBER)) -async def removing_member(call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext): +@router.callback_query( + RemoveCallbackFactory.filter(F.action == CallbackAction.REMOVE_MEMBER) +) +async def removing_member( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): await db.update_user(call.from_user) - isMemberOrAdmin = await db.check_room_and_member(callback_data.user_id, callback_data.room_iden) + isMemberOrAdmin = await db.check_room_and_member( + callback_data.user_id, callback_data.room_iden + ) room_name = await get_room_name(callback_data.room_iden) if isMemberOrAdmin == "MEMBER NOT EXISTS": @@ -90,9 +112,13 @@ async def removing_member(call: CallbackQuery, callback_data: CallbackFactory, s @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.START_EVENT)) -async def start_event(call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext): +async def start_event( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): await db.update_user(call.from_user) - isMemberOrAdmin = await db.check_room_and_member(call.from_user.id, callback_data.room_iden) + 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 == "ROOM NOT EXISTS": diff --git a/src/handlers/wishes.py b/src/handlers/wishes.py index 204a493..ae1384e 100644 --- a/src/handlers/wishes.py +++ b/src/handlers/wishes.py @@ -2,7 +2,7 @@ from aiogram.fsm.context import FSMContext from aiogram.types import Message, CallbackQuery -from src.config import settings,logger +from src.config import settings, logger from src.db import db from src.keyboards import common_kb, room_member_kb from src.states.states import Gen, CallbackFactory @@ -18,9 +18,13 @@ async def get_room_name(room_iden): @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.MY_WISHES)) -async def my_wishes(call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext): +async def my_wishes( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): await db.update_user(call.from_user) - isMemberOrAdmin = await db.check_room_and_member(call.from_user.id, callback_data.room_iden) + isMemberOrAdmin = await db.check_room_and_member( + call.from_user.id, callback_data.room_iden + ) if isMemberOrAdmin == "ROOM NOT EXISTS": room_name = await get_room_name(callback_data.room_iden) @@ -30,21 +34,27 @@ async def my_wishes(call: CallbackQuery, callback_data: CallbackFactory, state: ) return - status, wishes, photo_id = await db.get_wishes_and_photo(callback_data.room_iden, call.from_user.id) + status, wishes, photo_id = await db.get_wishes_and_photo( + callback_data.room_iden, call.from_user.id + ) wishes_info = await text.create_wishes_info(wishes) kb = await room_member_kb.wishes_kb(callback_data.room_iden, asAdmin=False) if photo_id: - await call.message.answer_photo(photo=photo_id, caption=wishes_info, reply_markup=kb) + await call.message.answer_photo( + photo=photo_id, caption=wishes_info, reply_markup=kb + ) return - await call.message.answer( - wishes_info, reply_markup=kb - ) + await call.message.answer(wishes_info, reply_markup=kb) @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.EDIT_WISHES)) -async def my_wishes(call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext): +async def my_wishes( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): await db.update_user(call.from_user) - isMemberOrAdmin = await db.check_room_and_member(call.from_user.id, callback_data.room_iden) + isMemberOrAdmin = await db.check_room_and_member( + call.from_user.id, callback_data.room_iden + ) if isMemberOrAdmin == "ROOM NOT EXISTS": room_name = await get_room_name(callback_data.room_iden) @@ -61,7 +71,9 @@ async def my_wishes(call: CallbackQuery, callback_data: CallbackFactory, state: ) return - status, wishes, photo_id = await db.get_wishes_and_photo(callback_data.room_iden, call.from_user.id) + status, wishes, photo_id = await db.get_wishes_and_photo( + callback_data.room_iden, call.from_user.id + ) if status in ("ROOM NOT EXISTS", "MEMBER NOT EXISTS"): await call.message.edit_text( @@ -70,7 +82,7 @@ async def my_wishes(call: CallbackQuery, callback_data: CallbackFactory, state: ) return - await state.set_data({'room_iden': callback_data.room_iden}) + await state.set_data({"room_iden": callback_data.room_iden}) await state.set_state(Gen.set_wishes) kb = await common_kb.cancel_kb("None", asAdmin=False) @@ -99,48 +111,62 @@ async def edit_wishes_room(msg: Message, state: FSMContext): return if msg.media_group_id: - await msg.answer(messages.media_group_not_supported(), reply_markup=await common_kb.cancel_kb("None", False)) + await msg.answer( + messages.media_group_not_supported(), + reply_markup=await common_kb.cancel_kb("None", False), + ) return - edit_wishes = ( - wishes_raw - .replace("\\", "/") - .replace("'", "`") - .replace('"', "`") - ) + edit_wishes = wishes_raw.replace("\\", "/").replace("'", "`").replace('"', "`") if msg.photo: if settings.chat_id: try: - await msg.bot.send_photo(chat_id=settings.chat_id, photo=msg.photo[-1].file_id) + await msg.bot.send_photo( + chat_id=settings.chat_id, photo=msg.photo[-1].file_id + ) except Exception as e: logger.error(f"Error sending photo to chat {settings.chat_id}: {e}") pass - room_status = await db.edit_wishes(edit_wishes, msg.from_user.id, room_iden, msg.photo[-1].file_id) + room_status = await db.edit_wishes( + edit_wishes, msg.from_user.id, room_iden, msg.photo[-1].file_id + ) else: room_status = await db.edit_wishes(edit_wishes, msg.from_user.id, room_iden) if room_status == "ROOM NOT EXISTS": - await msg.answer(messages.room_not_exists(), reply_markup=await common_kb.ok_kb("None", False)) + await msg.answer( + messages.room_not_exists(), + reply_markup=await common_kb.ok_kb("None", False), + ) return if room_status == "MEMBER NOT EXISTS": - await msg.answer(messages.wish_not_member(), reply_markup=await common_kb.ok_kb("None", False)) + await msg.answer( + messages.wish_not_member(), + reply_markup=await common_kb.ok_kb("None", False), + ) return wishes_info = messages.wish_updated(edit_wishes) await state.clear() + kb = await room_member_kb.wishes_kb(room_iden, asAdmin=False) if msg.photo: - await msg.answer_photo(photo=msg.photo[-1].file_id, caption=wishes_info, - reply_markup=await common_kb.ok_kb("None", asAdmin=False)) + await msg.answer_photo( + photo=msg.photo[-1].file_id, caption=wishes_info, reply_markup=kb + ) return await msg.answer( wishes_info, - reply_markup=await room_member_kb.wishes_kb(room_iden, asAdmin=False), + reply_markup=kb, ) @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.SEE_WISHES)) -async def see_wishes(call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext): +async def see_wishes( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): await db.update_user(call.from_user) - isMemberOrAdmin = await db.check_room_and_member(call.from_user.id, callback_data.room_iden) + isMemberOrAdmin = await db.check_room_and_member( + call.from_user.id, callback_data.room_iden + ) if isMemberOrAdmin == "ROOM NOT EXISTS": room_name = await get_room_name(callback_data.room_iden) @@ -151,11 +177,17 @@ async def see_wishes(call: CallbackQuery, callback_data: CallbackFactory, state: return member_id = await db.who_gives(callback_data.room_iden, call.from_user.id) - status, wishes, photo_id = await db.get_wishes_and_photo(callback_data.room_iden, member_id) + status, wishes, photo_id = await db.get_wishes_and_photo( + callback_data.room_iden, member_id + ) wishes_info = await text.take_wishes_info(wishes) + + kb = await common_kb.ok_kb("None", asAdmin=False) + if photo_id: - await call.message.answer_photo(photo=photo_id, caption=wishes_info, - reply_markup=await common_kb.ok_kb("None", asAdmin=False)) + await call.message.answer_photo( + photo=photo_id, caption=wishes_info, reply_markup=kb + ) return - await call.message.answer(wishes_info, reply_markup=await common_kb.ok_kb("None", False)) + await call.message.answer(wishes_info, reply_markup=kb) diff --git a/src/keyboards/invitation_kb.py b/src/keyboards/invitation_kb.py index 384ab73..313020b 100644 --- a/src/keyboards/invitation_kb.py +++ b/src/keyboards/invitation_kb.py @@ -3,7 +3,7 @@ from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup -async def join_to_room_kb(room_iden: str,bot_name: str): +async def join_to_room_kb(room_iden: str, bot_name: str): room_iden64 = base64.urlsafe_b64encode(room_iden.encode()).decode().replace("=", "") join_kb = [ [ diff --git a/src/keyboards/room_admin_kb.py b/src/keyboards/room_admin_kb.py index 8080e0d..c5bf9f2 100644 --- a/src/keyboards/room_admin_kb.py +++ b/src/keyboards/room_admin_kb.py @@ -19,7 +19,9 @@ async def room_admin_kb(room_iden): InlineKeyboardButton( text="📄Список участников", callback_data=states.CallbackFactory( - action=CallbackAction.MEMBERS_LIST, room_iden=room_iden, asAdmin=True + action=CallbackAction.MEMBERS_LIST, + room_iden=room_iden, + asAdmin=True, ).pack(), ) ], @@ -27,7 +29,9 @@ async def room_admin_kb(room_iden): InlineKeyboardButton( text="✏️Изменить настройки комнаты(в разработке)", callback_data=states.CallbackFactory( - action=CallbackAction.EDIT_ROOM_SETTINGS, room_iden=room_iden, asAdmin=True + action=CallbackAction.EDIT_ROOM_SETTINGS, + room_iden=room_iden, + asAdmin=True, ).pack(), ) ], @@ -43,7 +47,9 @@ async def room_admin_kb(room_iden): InlineKeyboardButton( text="📛Удалить участника", callback_data=states.CallbackFactory( - action=CallbackAction.REMOVE_MEMBER, room_iden=room_iden, asAdmin=True + action=CallbackAction.REMOVE_MEMBER, + room_iden=room_iden, + asAdmin=True, ).pack(), ) ], @@ -51,7 +57,9 @@ async def room_admin_kb(room_iden): InlineKeyboardButton( text="✉️Создать приглашение", callback_data=states.CallbackFactory( - action=CallbackAction.CREATE_INVITATION, room_iden=room_iden, asAdmin=True + action=CallbackAction.CREATE_INVITATION, + room_iden=room_iden, + asAdmin=True, ).pack(), ) ], @@ -59,7 +67,9 @@ async def room_admin_kb(room_iden): InlineKeyboardButton( text="◀️Вернуться в меню", callback_data=states.CallbackFactory( - action=CallbackAction.BACK_TO_MENU, room_iden=room_iden, asAdmin=True + action=CallbackAction.BACK_TO_MENU, + room_iden=room_iden, + asAdmin=True, ).pack(), ) ], @@ -81,7 +91,9 @@ async def confirm_kb(room_iden, asAdmin): InlineKeyboardButton( text="✅Да", callback_data=states.CallbackFactory( - action=CallbackAction.CONFIRM_DELETE, room_iden=room_iden, asAdmin=asAdmin + action=CallbackAction.CONFIRM_DELETE, + room_iden=room_iden, + asAdmin=asAdmin, ).pack(), ), InlineKeyboardButton( @@ -101,7 +113,9 @@ async def member_kb(members, room_iden): builder.button( text=f"{member[1]} {member[2]}", callback_data=states.RemoveCallbackFactory( - action=CallbackAction.REMOVE_MEMBER, room_iden=room_iden, user_id=member[0] + action=CallbackAction.REMOVE_MEMBER, + room_iden=room_iden, + user_id=member[0], ).pack(), ) builder.adjust(1) @@ -121,7 +135,9 @@ async def refresh_list_kb(room_iden, asAdmin): InlineKeyboardButton( text="🔄Обновить", callback_data=states.CallbackFactory( - action=CallbackAction.REFRESH_LIST, room_iden=room_iden, asAdmin=False + action=CallbackAction.REFRESH_LIST, + room_iden=room_iden, + asAdmin=False, ).pack(), ) ], diff --git a/src/keyboards/room_member_kb.py b/src/keyboards/room_member_kb.py index 4d15335..02bb80e 100644 --- a/src/keyboards/room_member_kb.py +++ b/src/keyboards/room_member_kb.py @@ -26,7 +26,9 @@ async def room_member_kb(room_iden): InlineKeyboardButton( text="📄Список участников", callback_data=states.CallbackFactory( - action=CallbackAction.MEMBERS_LIST, room_iden=room_iden, asAdmin=False + action=CallbackAction.MEMBERS_LIST, + room_iden=room_iden, + asAdmin=False, ).pack(), ) ], @@ -42,7 +44,9 @@ async def room_member_kb(room_iden): InlineKeyboardButton( text="✉️Создать приглашение", callback_data=states.CallbackFactory( - action=CallbackAction.CREATE_INVITATION, room_iden=room_iden, asAdmin=False + action=CallbackAction.CREATE_INVITATION, + room_iden=room_iden, + asAdmin=False, ).pack(), ) ], @@ -50,7 +54,9 @@ async def room_member_kb(room_iden): InlineKeyboardButton( text="◀️Вернуться в меню", callback_data=states.CallbackFactory( - action=CallbackAction.BACK_TO_MENU, room_iden=room_iden, asAdmin=False + action=CallbackAction.BACK_TO_MENU, + room_iden=room_iden, + asAdmin=False, ).pack(), ) ], @@ -78,7 +84,9 @@ async def wishes_kb(room_iden, asAdmin): InlineKeyboardButton( text="✏️Изменить желание", callback_data=states.CallbackFactory( - action=CallbackAction.EDIT_WISHES, room_iden=room_iden, asAdmin=asAdmin + action=CallbackAction.EDIT_WISHES, + room_iden=room_iden, + asAdmin=asAdmin, ).pack(), ), ], @@ -98,7 +106,9 @@ async def wishes_kb2(room_iden, asAdmin): InlineKeyboardButton( text="👀Посмотреть желание", callback_data=states.CallbackFactory( - action=CallbackAction.SEE_WISHES, room_iden=room_iden, asAdmin=asAdmin + action=CallbackAction.SEE_WISHES, + room_iden=room_iden, + asAdmin=asAdmin, ).pack(), ), ] diff --git a/src/settings/settings.py b/src/settings/settings.py index 699f138..5423932 100644 --- a/src/settings/settings.py +++ b/src/settings/settings.py @@ -19,7 +19,6 @@ def _normalize_chat_id(cls, v): except Exception: return None - @property def api_base(self) -> AnyUrl: """Возвращает адрес API в зависимости от окружения.""" diff --git a/src/states/states.py b/src/states/states.py index d3814a5..57d4a3c 100644 --- a/src/states/states.py +++ b/src/states/states.py @@ -18,10 +18,12 @@ class CancelCallbackFactory(CallbackData, prefix="canceldata"): action: str clearStates: bool + class EditCallbackFactory(CallbackData, prefix="editdata"): action: str editMessage: str + class Gen(StatesGroup): room_name_to_create = State() room_name_to_join = State() diff --git a/src/texts/messages.py b/src/texts/messages.py index 9671e4a..091edb1 100644 --- a/src/texts/messages.py +++ b/src/texts/messages.py @@ -89,6 +89,7 @@ def room_created(room_name: str, room_id: str) -> str: def prompt_wish() -> str: return "Напишите ваше пожелание" + def prompt_wish_with_current(current: str) -> str: return ( "Ваше текущее желание:\n" @@ -96,6 +97,7 @@ def prompt_wish_with_current(current: str) -> str: "Отправьте новое (можно нажать на текст выше, чтобы скопировать)." ) + def wish_not_member() -> str: return "Вы не участник комнаты" diff --git a/src/texts/text.py b/src/texts/text.py index 47ad677..ca1ab26 100644 --- a/src/texts/text.py +++ b/src/texts/text.py @@ -2,10 +2,11 @@ async def create_user_info(user): + print(user) text = f"{user[1]} " - if user[2] != "None": + if user[2] is not None: text += f"{user[2]} " - if user[3] != "None": + if user[3] is not None: text += f"@{user[3]}\n" else: text += f"{user[1]}\n" diff --git a/src/utilities/utils.py b/src/utilities/utils.py index 8d2b69e..675a117 100644 --- a/src/utilities/utils.py +++ b/src/utilities/utils.py @@ -2,29 +2,31 @@ import random + def randomize_members(members): pairs = {} members.sort() reciver_list = members.copy() - + for member in members: while True: reciver = random.choice(reciver_list) - if len(reciver_list)==2 and members[-1] in reciver_list: + if len(reciver_list) == 2 and members[-1] in reciver_list: reciver = members[-1] - if reciver!=member: + if reciver != member: reciver_list.pop(reciver_list.index(reciver)) break pairs[member] = reciver return pairs -if __name__ =="__main__": + +if __name__ == "__main__": for i in range(1000): - members =[1,2,3,4,5,6,7,8,9,0] + members = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0] pp = randomize_members(members) for p in pp: - if p==pp[p] or len(members)!=len(list(pp.values())): + if p == pp[p] or len(members) != len(list(pp.values())): print("error") exit(0) - print('успешно') + print("успешно") diff --git a/test/test_db.py b/test/test_db.py index 8113ee9..505d1aa 100644 --- a/test/test_db.py +++ b/test/test_db.py @@ -9,7 +9,13 @@ class DummyUser: - def __init__(self, user_id: int, first_name: str = "Test", last_name: str = "User", username: str = "tester"): + def __init__( + self, + user_id: int, + first_name: str = "Test", + last_name: str = "User", + username: str = "tester", + ): self.id = user_id self.first_name = first_name self.last_name = last_name @@ -82,7 +88,10 @@ def test_get_my_rooms_returns_admin_and_member_lists(setup_db, stub_random): admin_rooms = run(db.get_my_rooms(admin.id, asAdmin=True)) member_rooms = run(db.get_my_rooms(member.id, asAdmin=False)) - assert set(admin_rooms) == {_room_name("alpha", room_a_id), _room_name("beta", room_b_id)} + assert set(admin_rooms) == { + _room_name("alpha", room_a_id), + _room_name("beta", room_b_id), + } assert member_rooms == [_room_name("alpha", room_a_id)] @@ -143,8 +152,18 @@ def test_delete_room_removes_all_traces(setup_db, stub_random): run(db.delete_room(room_iden, admin.id)) - assert db.cur.execute("SELECT * FROM rooms WHERE room_iden = ?", (room_iden,)).fetchone() is None - assert db.cur.execute("SELECT * FROM user_rooms WHERE room_iden = ?", (room_iden,)).fetchone() is None + assert ( + db.cur.execute( + "SELECT * FROM rooms WHERE room_iden = ?", (room_iden,) + ).fetchone() + is None + ) + assert ( + db.cur.execute( + "SELECT * FROM user_rooms WHERE room_iden = ?", (room_iden,) + ).fetchone() + is None + ) tables = db.cur.execute( "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE ?", @@ -166,16 +185,27 @@ def test_leave_room_removes_member_record(setup_db, stub_random): run(db.leave_room(room_iden, member.id)) - assert db.cur.execute(f"SELECT * FROM {room_iden}_mem WHERE user_id = ?", (member.id,)).fetchone() is None - assert db.cur.execute( - "SELECT * FROM user_rooms WHERE tg_id = ? AND room_iden = ?", - (member.id, room_iden), - ).fetchone() is None + assert ( + db.cur.execute( + f"SELECT * FROM {room_iden}_mem WHERE user_id = ?", (member.id,) + ).fetchone() + is None + ) + assert ( + db.cur.execute( + "SELECT * FROM user_rooms WHERE tg_id = ? AND room_iden = ?", + (member.id, room_iden), + ).fetchone() + is None + ) # Admin entry must remain untouched. - assert db.cur.execute( - "SELECT * FROM user_rooms WHERE tg_id = ? AND room_iden = ?", - (admin.id, room_iden), - ).fetchone() is not None + assert ( + db.cur.execute( + "SELECT * FROM user_rooms WHERE tg_id = ? AND room_iden = ?", + (admin.id, room_iden), + ).fetchone() + is not None + ) def test_randomize_members_assigns_unique_pairs(): From 2c00133007bde437274e556d7eb5172a3636ca23 Mon Sep 17 00:00:00 2001 From: kitiketov Date: Mon, 24 Nov 2025 21:23:23 +0500 Subject: [PATCH 2/3] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=BD=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B9=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=BA=D0=BE=D0=BC=D0=BD=D0=B0=D1=82=D1=8B=20(=D1=81?= =?UTF-8?q?=D0=BF=D1=83=D1=81=D1=82=D1=8F=202=20=D0=B3=D0=BE=D0=B4=D0=B0?= =?UTF-8?q?=20=D1=80=D0=B0=D0=B7=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=BA=D0=B8?= =?UTF-8?q?))?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/bot.py | 2 + src/db/db.py | 94 ++++++- src/handlers/settings.py | 417 ++++++++++++++++++++++++++++++++ src/keyboards/room_admin_kb.py | 2 +- src/keyboards/room_member_kb.py | 10 + src/keyboards/settings_kb.py | 121 +++++++++ src/states/states.py | 2 + src/texts/callback_actions.py | 7 + src/utilities/validators.py | 48 ++++ 9 files changed, 698 insertions(+), 5 deletions(-) create mode 100644 src/handlers/settings.py create mode 100644 src/keyboards/settings_kb.py create mode 100644 src/utilities/validators.py diff --git a/src/app/bot.py b/src/app/bot.py index afe5bda..3650c02 100644 --- a/src/app/bot.py +++ b/src/app/bot.py @@ -12,6 +12,7 @@ invitation, debug, join_room, + settings as room_settings, ) @@ -32,6 +33,7 @@ async def run_bot(token: str) -> None: invitation.router, debug.router, join_room.router, + room_settings.router, ) await bot.delete_webhook(drop_pending_updates=True) diff --git a/src/db/db.py b/src/db/db.py index 1387702..1f0fd39 100644 --- a/src/db/db.py +++ b/src/db/db.py @@ -7,10 +7,21 @@ db = sq.connect(DB_PATH) cur = db.cursor() +ROOM_DEFAULT_PRICE = "не установлен" +ROOM_DEFAULT_EVENT_TIME = "не установлено" +ROOM_DEFAULT_EXCHANGE_TYPE = "централизованый" + async def start_db(): cur.execute( - "CREATE TABLE IF NOT EXISTS rooms(room_iden TEXT PRIMARY KEY,status BOOLEAN DEFAULT FALSE,admin INTEGER)" + "CREATE TABLE IF NOT EXISTS rooms(" + "room_iden TEXT PRIMARY KEY," + "status BOOLEAN DEFAULT FALSE," + "admin INTEGER," + "gift_price_range TEXT DEFAULT 'не установлен'," + "event_time TEXT DEFAULT 'не установлено'," + "exchange_type TEXT DEFAULT 'централизованый'" + ")" ) cur.execute( "CREATE TABLE IF NOT EXISTS users(tg_id INTEGER PRIMARY KEY,first_name TEXT,last_name TEXT,username TEXT)" @@ -26,6 +37,7 @@ async def start_db(): ) """ ) + await migrate_rooms_table() db.commit() @@ -165,7 +177,7 @@ async def get_members_list(room_iden): admin_raw = cur.execute( "SELECT * FROM rooms WHERE room_iden = ?", (room_iden,) ).fetchone() - *_, admin_id = admin_raw + admin_id = admin_raw[2] isAdminMember = False member_id_list = cur.execute(f"SELECT * FROM {room_iden}_mem ").fetchall() @@ -260,10 +272,10 @@ async def who_gives(room_iden, user_id): async def isStarted(room_iden): - _, status, _ = cur.execute( + room_raw = cur.execute( "SELECT * FROM rooms WHERE room_iden = ?", (room_iden,) ).fetchone() - return status + return room_raw[1] async def get_user(user_id): @@ -287,6 +299,15 @@ async def check_room_and_member(user_id, room_iden): return True +async def get_room_admin(room_iden): + admin_raw = cur.execute( + "SELECT admin FROM rooms WHERE room_iden = ?", (room_iden,) + ).fetchone() + if not admin_raw: + return None + return admin_raw[0] + + async def count_user_room(user_id): count = cur.execute( "SELECT COUNT(*) FROM user_rooms WHERE tg_id = ?", @@ -335,5 +356,70 @@ async def edit_wishes(wishes, user_id, room_iden, photo_id=""): return True +async def migrate_rooms_table(): + columns = [ + column[1] for column in cur.execute("PRAGMA table_info(rooms)").fetchall() + ] + migrations = [ + ( + "gift_price_range", + f"ALTER TABLE rooms ADD COLUMN gift_price_range TEXT DEFAULT '{ROOM_DEFAULT_PRICE}'", + ), + ( + "event_time", + f"ALTER TABLE rooms ADD COLUMN event_time TEXT DEFAULT '{ROOM_DEFAULT_EVENT_TIME}'", + ), + ( + "exchange_type", + f"ALTER TABLE rooms ADD COLUMN exchange_type TEXT DEFAULT '{ROOM_DEFAULT_EXCHANGE_TYPE}'", + ), + ] + + for column, query in migrations: + if column not in columns: + cur.execute(query) + + db.commit() + + +async def get_room_settings(room_iden): + _room = cur.execute( + "SELECT gift_price_range, event_time, exchange_type FROM rooms WHERE room_iden = ?", + (room_iden,), + ).fetchone() + if not _room: + return "ROOM NOT EXISTS", None, None, None + price, event_time, exchange_type = _room + return True, price, event_time, exchange_type + + +async def update_room_settings(room_iden, price=None, event_time=None, exchange_type=None): + _room = cur.execute( + "SELECT 1 FROM rooms WHERE room_iden = ?", + (room_iden,), + ).fetchone() + if not _room: + return "ROOM NOT EXISTS" + + if price is not None: + cur.execute( + "UPDATE rooms SET gift_price_range = ? WHERE room_iden = ?", + (price, room_iden), + ) + if event_time is not None: + cur.execute( + "UPDATE rooms SET event_time = ? WHERE room_iden = ?", + (event_time, room_iden), + ) + if exchange_type is not None: + cur.execute( + "UPDATE rooms SET exchange_type = ? WHERE room_iden = ?", + (exchange_type, room_iden), + ) + + db.commit() + return True + + if __name__ == "__main__": asyncio.run(start_db()) diff --git a/src/handlers/settings.py b/src/handlers/settings.py new file mode 100644 index 0000000..f28d3cd --- /dev/null +++ b/src/handlers/settings.py @@ -0,0 +1,417 @@ +from aiogram import F, Router +from aiogram.fsm.context import FSMContext +from aiogram.types import CallbackQuery, Message + +from src.db import db +from src.keyboards import common_kb, settings_kb +from src.states.states import CallbackFactory, Gen +from src.texts import messages, settings_texts +from src.texts.callback_actions import CallbackAction +from src.utilities import validators + +router = Router(name=__name__) + + +async def get_room_name(room_iden): + return f"{room_iden[:-4]}:{room_iden[-4:]}" + + +async def check_room_access(user_id, room_iden): + room_name = await get_room_name(room_iden) + status = await db.check_room_and_member(user_id, room_iden) + if status == "ROOM NOT EXISTS": + return "ROOM NOT EXISTS", room_name + + admin_id = await db.get_room_admin(room_iden) + if admin_id is None: + return "ROOM NOT EXISTS", room_name + if admin_id != user_id: + return "NOT ADMIN", room_name + + return True, room_name + + +@router.callback_query(CallbackFactory.filter(F.action == CallbackAction.EDIT_ROOM_SETTINGS)) +async def show_room_settings( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): + await db.update_user(call.from_user) + access, room_name = await check_room_access( + call.from_user.id, callback_data.room_iden + ) + + if access == "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 + + if access == "NOT ADMIN": + await call.message.edit_text( + messages.not_a_member(room_name), + reply_markup=await common_kb.ok_kb("None", asAdmin=False), + ) + return + + _, price, event_time, exchange_type = await db.get_room_settings( + callback_data.room_iden + ) + info = settings_texts.room_settings_info( + room_name, price, event_time, exchange_type + ) + kb = await settings_kb.settings_view_kb( + callback_data.room_iden, callback_data.asAdmin + ) + await call.message.answer(info, reply_markup=kb) + + +@router.callback_query( + CallbackFactory.filter(F.action == CallbackAction.SHOW_ROOM_SETTINGS) +) +async def show_room_settings_member( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): + await db.update_user(call.from_user) + status = 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 status == "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 + + if status == "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 + + _, price, event_time, exchange_type = await db.get_room_settings( + callback_data.room_iden + ) + info = settings_texts.room_settings_info( + room_name, price, event_time, exchange_type + ) + kb = await settings_kb.settings_view_kb( + callback_data.room_iden, callback_data.asAdmin + ) + await call.message.answer(info, reply_markup=kb) + + +@router.callback_query( + CallbackFactory.filter(F.action == CallbackAction.OPEN_ROOM_SETTINGS_EDIT) +) +async def open_settings_edit( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): + await db.update_user(call.from_user) + access, room_name = await check_room_access( + call.from_user.id, callback_data.room_iden + ) + + if access == "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 + + if access == "NOT ADMIN": + await call.message.edit_text( + messages.not_a_member(room_name), + reply_markup=await common_kb.ok_kb("None", asAdmin=False), + ) + return + + _, price, event_time, exchange_type = await db.get_room_settings( + callback_data.room_iden + ) + info = settings_texts.room_settings_info( + room_name, price, event_time, exchange_type + ) + kb = await settings_kb.settings_edit_kb( + callback_data.room_iden, callback_data.asAdmin + ) + await call.message.edit_text(info, reply_markup=kb) + + +@router.callback_query(CallbackFactory.filter(F.action == CallbackAction.EDIT_ROOM_PRICE)) +async def edit_room_price( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): + await db.update_user(call.from_user) + access, room_name = await check_room_access( + call.from_user.id, callback_data.room_iden + ) + + if access == "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 + + if access == "NOT ADMIN": + await call.message.edit_text( + messages.not_a_member(room_name), + reply_markup=await common_kb.ok_kb("None", asAdmin=False), + ) + return + + await state.set_state(Gen.set_room_price) + await state.set_data({"room_iden": callback_data.room_iden}) + + _, price, *_ = await db.get_room_settings(callback_data.room_iden) + await call.message.answer( + settings_texts.prompt_price(price), + reply_markup=await common_kb.cancel_kb("None", asAdmin=True), + ) + + +@router.callback_query(CallbackFactory.filter(F.action == CallbackAction.EDIT_ROOM_TIME)) +async def edit_room_time( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): + await db.update_user(call.from_user) + access, room_name = await check_room_access( + call.from_user.id, callback_data.room_iden + ) + + if access == "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 + + if access == "NOT ADMIN": + await call.message.edit_text( + messages.not_a_member(room_name), + reply_markup=await common_kb.ok_kb("None", asAdmin=False), + ) + return + + await state.set_state(Gen.set_room_time) + await state.set_data({"room_iden": callback_data.room_iden}) + + _, _, event_time, _ = await db.get_room_settings(callback_data.room_iden) + await call.message.answer( + settings_texts.prompt_event_time(event_time), + reply_markup=await common_kb.cancel_kb("None", asAdmin=True), + ) + + +@router.callback_query(CallbackFactory.filter(F.action == CallbackAction.EDIT_ROOM_TYPE)) +async def edit_room_type( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): + await db.update_user(call.from_user) + access, room_name = await check_room_access( + call.from_user.id, callback_data.room_iden + ) + + if access == "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 + + if access == "NOT ADMIN": + await call.message.edit_text( + messages.not_a_member(room_name), + reply_markup=await common_kb.ok_kb("None", asAdmin=False), + ) + return + + _, _, _, exchange_type = await db.get_room_settings(callback_data.room_iden) + await call.message.edit_text( + settings_texts.choose_exchange_type(exchange_type), + reply_markup=await settings_kb.settings_type_kb( + callback_data.room_iden, callback_data.asAdmin + ), + ) + + +@router.callback_query( + CallbackFactory.filter( + F.action.in_( + [CallbackAction.SET_ROOM_TYPE_CENTRAL, CallbackAction.SET_ROOM_TYPE_THROW] + ) + ) +) +async def set_room_type( + call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext +): + await db.update_user(call.from_user) + access, room_name = await check_room_access( + call.from_user.id, callback_data.room_iden + ) + + if access == "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 + + if access == "NOT ADMIN": + await call.message.edit_text( + messages.not_a_member(room_name), + reply_markup=await common_kb.ok_kb("None", asAdmin=False), + ) + return + + new_type = ( + "централизованый" + if callback_data.action == CallbackAction.SET_ROOM_TYPE_CENTRAL + else "подброс подарка" + ) + status = await db.update_room_settings( + callback_data.room_iden, exchange_type=new_type + ) + if status == "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 + + _, price, event_time, exchange_type = await db.get_room_settings( + callback_data.room_iden + ) + info = settings_texts.settings_updated( + room_name, price, event_time, exchange_type + ) + kb = await settings_kb.settings_view_kb( + callback_data.room_iden, callback_data.asAdmin + ) + await call.message.edit_text(info, reply_markup=kb) + + +@router.message(Gen.set_room_price) +async def set_room_price(msg: Message, state: FSMContext): + await db.update_user(msg.from_user) + data = await state.get_data() + room_iden = data.get("room_iden") + + if msg.text == "🚫Отмена": + await state.clear() + if room_iden: + _, price, event_time, exchange_type = await db.get_room_settings(room_iden) + room_name = await get_room_name(room_iden) + await msg.answer( + settings_texts.room_settings_info( + room_name, price, event_time, exchange_type + ), + reply_markup=await settings_kb.settings_view_kb(room_iden, True), + ) + else: + await msg.answer(messages.menu(), reply_markup=common_kb.choice_kb) + return + + if not room_iden: + await state.clear() + await msg.answer(messages.menu(), reply_markup=common_kb.choice_kb) + return + + price = validators.normalize_price_range(msg.text) + if not price: + await msg.answer( + settings_texts.invalid_price(), + reply_markup=await common_kb.cancel_kb("None", asAdmin=True), + ) + return + + access, room_name = await check_room_access(msg.from_user.id, 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 == "NOT ADMIN": + await state.clear() + await msg.answer( + messages.not_a_member(room_name), + reply_markup=await common_kb.ok_kb("None", asAdmin=False), + ) + return + + await db.update_room_settings(room_iden, price=price) + _, new_price, event_time, exchange_type = await db.get_room_settings(room_iden) + await state.clear() + await msg.answer( + settings_texts.settings_updated( + room_name, new_price, event_time, exchange_type + ), + reply_markup=await settings_kb.settings_view_kb(room_iden, True), + ) + + +@router.message(Gen.set_room_time) +async def set_room_time(msg: Message, state: FSMContext): + await db.update_user(msg.from_user) + data = await state.get_data() + room_iden = data.get("room_iden") + + if msg.text == "🚫Отмена": + await state.clear() + if room_iden: + _, price, event_time, exchange_type = await db.get_room_settings(room_iden) + room_name = await get_room_name(room_iden) + await msg.answer( + settings_texts.room_settings_info( + room_name, price, event_time, exchange_type + ), + reply_markup=await settings_kb.settings_view_kb(room_iden, True), + ) + else: + await msg.answer(messages.menu(), reply_markup=common_kb.choice_kb) + return + + if not room_iden: + await state.clear() + await msg.answer(messages.menu(), reply_markup=common_kb.choice_kb) + return + + event_time = validators.normalize_event_time(msg.text) + if not event_time: + await msg.answer( + settings_texts.invalid_event_time(), + reply_markup=await common_kb.cancel_kb("None", asAdmin=True), + ) + return + + access, room_name = await check_room_access(msg.from_user.id, 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 == "NOT ADMIN": + await state.clear() + await msg.answer( + messages.not_a_member(room_name), + reply_markup=await common_kb.ok_kb("None", asAdmin=False), + ) + return + + await db.update_room_settings(room_iden, event_time=event_time) + _, price, new_event_time, exchange_type = await db.get_room_settings(room_iden) + await state.clear() + await msg.answer( + settings_texts.settings_updated( + room_name, price, new_event_time, exchange_type + ), + reply_markup=await settings_kb.settings_view_kb(room_iden, True), + ) diff --git a/src/keyboards/room_admin_kb.py b/src/keyboards/room_admin_kb.py index c5bf9f2..a93d085 100644 --- a/src/keyboards/room_admin_kb.py +++ b/src/keyboards/room_admin_kb.py @@ -27,7 +27,7 @@ async def room_admin_kb(room_iden): ], [ InlineKeyboardButton( - text="✏️Изменить настройки комнаты(в разработке)", + text="✏️Изменить настройки комнаты", callback_data=states.CallbackFactory( action=CallbackAction.EDIT_ROOM_SETTINGS, room_iden=room_iden, diff --git a/src/keyboards/room_member_kb.py b/src/keyboards/room_member_kb.py index 02bb80e..0bee52e 100644 --- a/src/keyboards/room_member_kb.py +++ b/src/keyboards/room_member_kb.py @@ -32,6 +32,16 @@ async def room_member_kb(room_iden): ).pack(), ) ], + [ + InlineKeyboardButton( + text="⚙️Настройки комнаты", + callback_data=states.CallbackFactory( + action=CallbackAction.SHOW_ROOM_SETTINGS, + room_iden=room_iden, + asAdmin=False, + ).pack(), + ) + ], [ InlineKeyboardButton( text="🚪Покинуть комнату", diff --git a/src/keyboards/settings_kb.py b/src/keyboards/settings_kb.py new file mode 100644 index 0000000..57aed9d --- /dev/null +++ b/src/keyboards/settings_kb.py @@ -0,0 +1,121 @@ +from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup + +from src.states import states +from src.texts.callback_actions import CallbackAction + + +async def settings_view_kb(room_iden, asAdmin=True): + row = [ + InlineKeyboardButton( + text="✅Окей", + callback_data=states.CallbackFactory( + action=CallbackAction.CANCEL, room_iden=room_iden, asAdmin=asAdmin + ).pack(), + ) + ] + if asAdmin: + row.append( + InlineKeyboardButton( + text="✏️Редактировать", + callback_data=states.CallbackFactory( + action=CallbackAction.OPEN_ROOM_SETTINGS_EDIT, + room_iden=room_iden, + asAdmin=asAdmin, + ).pack(), + ) + ) + return InlineKeyboardMarkup(inline_keyboard=[row]) + + +async def settings_edit_kb(room_iden, asAdmin=True): + kb = [ + [ + InlineKeyboardButton( + text="💰Диапазон стоимости", + callback_data=states.CallbackFactory( + action=CallbackAction.EDIT_ROOM_PRICE, + room_iden=room_iden, + asAdmin=asAdmin, + ).pack(), + ) + ], + [ + InlineKeyboardButton( + text="🗓Время проведения", + callback_data=states.CallbackFactory( + action=CallbackAction.EDIT_ROOM_TIME, + room_iden=room_iden, + asAdmin=asAdmin, + ).pack(), + ) + ], + [ + InlineKeyboardButton( + text="🎁Тип обмена", + callback_data=states.CallbackFactory( + action=CallbackAction.EDIT_ROOM_TYPE, + room_iden=room_iden, + asAdmin=asAdmin, + ).pack(), + ) + ], + [ + InlineKeyboardButton( + text="◀️Назад", + callback_data=states.CallbackFactory( + action=CallbackAction.EDIT_ROOM_SETTINGS, + room_iden=room_iden, + asAdmin=asAdmin, + ).pack(), + ), + InlineKeyboardButton( + text="🚫Отмена", + callback_data=states.CallbackFactory( + action=CallbackAction.CANCEL, room_iden=room_iden, asAdmin=asAdmin + ).pack(), + ), + ], + ] + return InlineKeyboardMarkup(inline_keyboard=kb) + + +async def settings_type_kb(room_iden, asAdmin=True): + kb = [ + [ + InlineKeyboardButton( + text="🎄Централизованый", + callback_data=states.CallbackFactory( + action=CallbackAction.SET_ROOM_TYPE_CENTRAL, + room_iden=room_iden, + asAdmin=asAdmin, + ).pack(), + ) + ], + [ + InlineKeyboardButton( + text="🎁Подброс подарка", + callback_data=states.CallbackFactory( + action=CallbackAction.SET_ROOM_TYPE_THROW, + room_iden=room_iden, + asAdmin=asAdmin, + ).pack(), + ) + ], + [ + InlineKeyboardButton( + text="◀️Назад", + callback_data=states.CallbackFactory( + action=CallbackAction.OPEN_ROOM_SETTINGS_EDIT, + room_iden=room_iden, + asAdmin=asAdmin, + ).pack(), + ), + InlineKeyboardButton( + text="🚫Отмена", + callback_data=states.CallbackFactory( + action=CallbackAction.CANCEL, room_iden=room_iden, asAdmin=asAdmin + ).pack(), + ), + ], + ] + return InlineKeyboardMarkup(inline_keyboard=kb) diff --git a/src/states/states.py b/src/states/states.py index 57d4a3c..c9709df 100644 --- a/src/states/states.py +++ b/src/states/states.py @@ -29,3 +29,5 @@ class Gen(StatesGroup): room_name_to_join = State() approval_delete = State() set_wishes = State() + set_room_price = State() + set_room_time = State() diff --git a/src/texts/callback_actions.py b/src/texts/callback_actions.py index 36a5e27..8e6bfe6 100644 --- a/src/texts/callback_actions.py +++ b/src/texts/callback_actions.py @@ -26,3 +26,10 @@ class CallbackAction: CONFIRM_DELETE = "confirm_delete" REMOVE_MEMBER = "remove_member" EDIT_ROOM_SETTINGS = "edit_room_settings" + OPEN_ROOM_SETTINGS_EDIT = "open_room_settings_edit" + EDIT_ROOM_PRICE = "edit_room_price" + EDIT_ROOM_TIME = "edit_room_time" + EDIT_ROOM_TYPE = "edit_room_type" + SET_ROOM_TYPE_CENTRAL = "set_room_type_central" + SET_ROOM_TYPE_THROW = "set_room_type_throw" + SHOW_ROOM_SETTINGS = "show_room_settings" diff --git a/src/utilities/validators.py b/src/utilities/validators.py new file mode 100644 index 0000000..b5bda11 --- /dev/null +++ b/src/utilities/validators.py @@ -0,0 +1,48 @@ +def normalize_price_range(raw): + if raw is None: + return None + + lowered = raw.lower().replace(" ", "") + if lowered in ("неустановлен", "неустановлено"): + return "не установлен" + + value = raw.replace(" ", "") + if value.count("-") != 1: + return None + + start, end = value.split("-", 1) + if not start.isdigit() or not end.isdigit(): + return None + + start_val = int(start) + end_val = int(end) + if start_val <= 0 or end_val <= 0 or start_val >= end_val: + return None + + return f"{start_val}-{end_val}" + + +def normalize_event_time(raw): + if raw is None: + return None + + lowered = raw.lower().replace(" ", "") + if lowered in ("неустановлено", "неустановлен"): + return "не установлено" + + value = raw.replace(" ", "") + if value.count(":") != 1: + return None + + day, month = value.split(":", 1) + if not day.isdigit() or not month.isdigit(): + return None + + day_val = int(day) + month_val = int(month) + if month_val < 1 or month_val > 12: + return None + if day_val < 1 or day_val > 31: + return None + + return f"{day_val:02}:{month_val:02}" From 679c6dbf34dd1d05c39973ae010151bdf3898e7c Mon Sep 17 00:00:00 2001 From: kitiketov Date: Mon, 24 Nov 2025 21:33:45 +0500 Subject: [PATCH 3/3] =?UTF-8?q?=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=20=D0=BE?= =?UTF-8?q?=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B4?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D0=BE=20=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5=D0=BB=D0=B5=20=D0=B2?= =?UTF-8?q?=20midlware=20=D0=BA=D0=B0=D0=BA=20=D1=82=D0=B0=D0=BC=20=D0=B5?= =?UTF-8?q?=D0=B3=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/bot.py | 3 +++ src/handlers/common.py | 2 -- src/handlers/create_room.py | 2 -- src/handlers/invitation.py | 1 - src/handlers/join_room.py | 1 - src/handlers/legacy_route.py | 7 ------ src/handlers/room_admin.py | 5 ---- src/handlers/settings.py | 9 ------- src/handlers/wishes.py | 4 --- src/middlewares/__init__.py | 3 +++ src/middlewares/update_user.py | 11 +++++++++ src/texts/settings_texts.py | 45 ++++++++++++++++++++++++++++++++++ src/texts/text.py | 1 - 13 files changed, 62 insertions(+), 32 deletions(-) create mode 100644 src/middlewares/__init__.py create mode 100644 src/middlewares/update_user.py create mode 100644 src/texts/settings_texts.py diff --git a/src/app/bot.py b/src/app/bot.py index 3650c02..02449db 100644 --- a/src/app/bot.py +++ b/src/app/bot.py @@ -3,6 +3,7 @@ from aiogram.fsm.storage.memory import MemoryStorage from src.db import db +from src.middlewares import UpdateUserMiddleware from src.handlers import ( legacy_route, common, @@ -23,6 +24,8 @@ async def run_bot(token: str) -> None: default=DefaultBotProperties(parse_mode="HTML"), ) dp = Dispatcher(storage=MemoryStorage()) + dp.message.middleware(UpdateUserMiddleware()) + dp.callback_query.middleware(UpdateUserMiddleware()) dp.include_routers( legacy_route.router, diff --git a/src/handlers/common.py b/src/handlers/common.py index d7c8057..a4b630d 100644 --- a/src/handlers/common.py +++ b/src/handlers/common.py @@ -36,7 +36,6 @@ async def get_room_name(room_iden): async def cancel( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) if callback_data.room_iden == "None": await state.clear() await call.message.delete() @@ -45,5 +44,4 @@ async def cancel( @router.message(F.text == "◀️Вернуться в меню") @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.BACK_TO_MENU)) async def menu(call: CallbackQuery, callback_data: CallbackFactory): - await db.update_user(call.from_user) await call.message.edit_text(messages.menu(), reply_markup=common_kb.choice_kb) diff --git a/src/handlers/create_room.py b/src/handlers/create_room.py index 21ed13e..71b9296 100644 --- a/src/handlers/create_room.py +++ b/src/handlers/create_room.py @@ -16,7 +16,6 @@ async def start_create_room( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) room_count = await db.count_user_room(call.from_user.id) if room_count > 5: @@ -36,7 +35,6 @@ async def start_create_room( @router.message(Gen.room_name_to_create) async def create_room(msg: Message, state: FSMContext): - await db.update_user(msg.from_user) name = msg.text if msg.text == "🚫Отмена": diff --git a/src/handlers/invitation.py b/src/handlers/invitation.py index df53adc..b5c3d47 100644 --- a/src/handlers/invitation.py +++ b/src/handlers/invitation.py @@ -22,7 +22,6 @@ async def get_room_name(room_iden): async def create_invitation( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) isMemberOrAdmin = await db.check_room_and_member( call.from_user.id, callback_data.room_iden ) diff --git a/src/handlers/join_room.py b/src/handlers/join_room.py index cfcc1e7..6866048 100644 --- a/src/handlers/join_room.py +++ b/src/handlers/join_room.py @@ -30,7 +30,6 @@ async def start_join_room( @router.message(Gen.room_name_to_join) async def join_room(msg: Message, state: FSMContext): - await db.update_user(msg.from_user) name = msg.text if msg.text == "🚫Отмена": diff --git a/src/handlers/legacy_route.py b/src/handlers/legacy_route.py index 72b080b..8dcf209 100644 --- a/src/handlers/legacy_route.py +++ b/src/handlers/legacy_route.py @@ -64,8 +64,6 @@ async def start_handler(msg: Message): @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.REFRESH_LIST)) @router.callback_query(CallbackFactory.filter(F.action == CallbackAction.MEMBERS_LIST)) async def get_member_list(call: CallbackQuery, callback_data: CallbackFactory): - await db.update_user(call.from_user) - print(call.from_user.id, callback_data.room_iden) isMemberOrAdmin = await db.check_room_and_member( call.from_user.id, callback_data.room_iden ) @@ -106,7 +104,6 @@ async def get_member_list(call: CallbackQuery, callback_data: CallbackFactory): async def cancel( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) await db.leave_room(callback_data.room_iden, call.from_user.id) await call.message.edit_text(messages.left_room(), reply_markup=common_kb.choice_kb) @@ -115,7 +112,6 @@ async def cancel( async def get_list_of_rooms( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) await call.message.edit_text( messages.choose_option(), reply_markup=rooms_kb.my_rooms_kb ) @@ -125,7 +121,6 @@ async def get_list_of_rooms( async def get_my_admin_rooms( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) rooms = await db.get_my_rooms(call.from_user.id, callback_data.asAdmin) kb = await rooms_kb.rooms_kb(rooms, callback_data.asAdmin) @@ -136,7 +131,6 @@ async def get_my_admin_rooms( async def show_room( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) isMemberOrAdmin = await db.check_room_and_member( call.from_user.id, callback_data.room_iden ) @@ -175,7 +169,6 @@ async def show_room( async def who_gives( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) isMemberOrAdmin = await db.check_room_and_member( call.from_user.id, callback_data.room_iden ) diff --git a/src/handlers/room_admin.py b/src/handlers/room_admin.py index 69f141b..700b3ad 100644 --- a/src/handlers/room_admin.py +++ b/src/handlers/room_admin.py @@ -23,7 +23,6 @@ async def get_room_name(room_iden): async def delete_room( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) isMemberOrAdmin = await db.check_room_and_member( call.from_user.id, callback_data.room_iden ) @@ -48,7 +47,6 @@ async def delete_room( async def delete_room( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) isMemberOrAdmin = await db.check_room_and_member( call.from_user.id, callback_data.room_iden ) @@ -71,7 +69,6 @@ async def delete_room( async def remove_member( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) members, *_ = await db.get_members_list(callback_data.room_iden) kb = await room_admin_kb.member_kb(members, callback_data.room_iden) @@ -84,7 +81,6 @@ async def remove_member( async def removing_member( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) isMemberOrAdmin = await db.check_room_and_member( callback_data.user_id, callback_data.room_iden ) @@ -115,7 +111,6 @@ async def removing_member( async def start_event( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) isMemberOrAdmin = await db.check_room_and_member( call.from_user.id, callback_data.room_iden ) diff --git a/src/handlers/settings.py b/src/handlers/settings.py index f28d3cd..aefb7fd 100644 --- a/src/handlers/settings.py +++ b/src/handlers/settings.py @@ -35,7 +35,6 @@ async def check_room_access(user_id, room_iden): async def show_room_settings( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) access, room_name = await check_room_access( call.from_user.id, callback_data.room_iden ) @@ -72,7 +71,6 @@ async def show_room_settings( async def show_room_settings_member( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) status = await db.check_room_and_member( call.from_user.id, callback_data.room_iden ) @@ -110,7 +108,6 @@ async def show_room_settings_member( async def open_settings_edit( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) access, room_name = await check_room_access( call.from_user.id, callback_data.room_iden ) @@ -145,7 +142,6 @@ async def open_settings_edit( async def edit_room_price( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) access, room_name = await check_room_access( call.from_user.id, callback_data.room_iden ) @@ -178,7 +174,6 @@ async def edit_room_price( async def edit_room_time( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) access, room_name = await check_room_access( call.from_user.id, callback_data.room_iden ) @@ -211,7 +206,6 @@ async def edit_room_time( async def edit_room_type( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) access, room_name = await check_room_access( call.from_user.id, callback_data.room_iden ) @@ -249,7 +243,6 @@ async def edit_room_type( async def set_room_type( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) access, room_name = await check_room_access( call.from_user.id, callback_data.room_iden ) @@ -297,7 +290,6 @@ async def set_room_type( @router.message(Gen.set_room_price) async def set_room_price(msg: Message, state: FSMContext): - await db.update_user(msg.from_user) data = await state.get_data() room_iden = data.get("room_iden") @@ -358,7 +350,6 @@ async def set_room_price(msg: Message, state: FSMContext): @router.message(Gen.set_room_time) async def set_room_time(msg: Message, state: FSMContext): - await db.update_user(msg.from_user) data = await state.get_data() room_iden = data.get("room_iden") diff --git a/src/handlers/wishes.py b/src/handlers/wishes.py index ae1384e..fce9906 100644 --- a/src/handlers/wishes.py +++ b/src/handlers/wishes.py @@ -21,7 +21,6 @@ async def get_room_name(room_iden): async def my_wishes( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) isMemberOrAdmin = await db.check_room_and_member( call.from_user.id, callback_data.room_iden ) @@ -51,7 +50,6 @@ async def my_wishes( async def my_wishes( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) isMemberOrAdmin = await db.check_room_and_member( call.from_user.id, callback_data.room_iden ) @@ -99,7 +97,6 @@ async def my_wishes( @router.message(Gen.set_wishes) async def edit_wishes_room(msg: Message, state: FSMContext): - await db.update_user(msg.from_user) wishes_raw = msg.text or msg.caption or "" data = await state.get_data() @@ -163,7 +160,6 @@ async def edit_wishes_room(msg: Message, state: FSMContext): async def see_wishes( call: CallbackQuery, callback_data: CallbackFactory, state: FSMContext ): - await db.update_user(call.from_user) isMemberOrAdmin = await db.check_room_and_member( call.from_user.id, callback_data.room_iden ) diff --git a/src/middlewares/__init__.py b/src/middlewares/__init__.py new file mode 100644 index 0000000..bb03ab7 --- /dev/null +++ b/src/middlewares/__init__.py @@ -0,0 +1,3 @@ +from .update_user import UpdateUserMiddleware + +__all__ = ["UpdateUserMiddleware"] diff --git a/src/middlewares/update_user.py b/src/middlewares/update_user.py new file mode 100644 index 0000000..e816808 --- /dev/null +++ b/src/middlewares/update_user.py @@ -0,0 +1,11 @@ +from aiogram import BaseMiddleware + +from src.db import db + + +class UpdateUserMiddleware(BaseMiddleware): + async def __call__(self, handler, event, data): + user = getattr(event, "from_user", None) + if user: + await db.update_user(user) + return await handler(event, data) diff --git a/src/texts/settings_texts.py b/src/texts/settings_texts.py new file mode 100644 index 0000000..9c3a08e --- /dev/null +++ b/src/texts/settings_texts.py @@ -0,0 +1,45 @@ +def room_settings_info(room_name, price, event_time, exchange_type): + return ( + f"Настройки комнаты {room_name}\n\n" + f"💰Диапазон стоимости подарка: {price}\n" + f"🗓Ориентировочное время проведения: {event_time}\n" + f"🎁Тип обмена: {exchange_type}" + ) + + +def prompt_price(price): + return ( + f"Текущий диапазон: {price}\n" + "Напишите новый диапазон стоимости через дефис (например 500-1500)." + ) + + +def invalid_price(): + return "Некорректный диапазон. Используйте формат 500-1500 (минимум меньше максимума)." + + +def prompt_event_time(event_time): + return ( + f"Текущее время проведения: {event_time}\n" + "Укажите дату в формате ДД:ММ (например 24:12)." + ) + + +def invalid_event_time(): + return "Некорректная дата. Используйте формат ДД:ММ и существующие значения." + + +def choose_exchange_type(current_type): + return ( + f"Текущий тип обмена: {current_type}\n" + "Выберите новый вариант:" + ) + + +def settings_updated(room_name, price, event_time, exchange_type): + return ( + f"Настройки комнаты {room_name} обновлены\n\n" + f"💰Диапазон стоимости подарка: {price}\n" + f"🗓Ориентировочное время проведения: {event_time}\n" + f"🎁Тип обмена: {exchange_type}" + ) diff --git a/src/texts/text.py b/src/texts/text.py index ca1ab26..4e1a843 100644 --- a/src/texts/text.py +++ b/src/texts/text.py @@ -2,7 +2,6 @@ async def create_user_info(user): - print(user) text = f"{user[1]} " if user[2] is not None: text += f"{user[2]} "