Skip to content

Commit d659d3f

Browse files
committed
refactor: handles in dialogs
1 parent c2d6d7a commit d659d3f

File tree

15 files changed

+762
-674
lines changed

15 files changed

+762
-674
lines changed

src/bot/app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from aiogram.filters import ExceptionTypeFilter
66
from aiogram.fsm.context import FSMContext
77
from aiogram.fsm.storage.memory import MemoryStorage
8-
from aiogram.fsm.storage.redis import DefaultKeyBuilder, RedisStorage
8+
from aiogram.fsm.storage.redis import DefaultKeyBuilder, RedisStorage # type: ignore
99
from aiogram.types import ErrorEvent
1010
from aiogram_dialog import DialogManager, setup_dialogs
1111
from aiogram_dialog.api.exceptions import UnknownIntent, UnknownState
Lines changed: 54 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,174 +1,83 @@
1-
from aiogram.types import BufferedInputFile, Message
1+
from aiogram.types import Message
22
from aiogram_dialog import DialogManager, ShowMode
33
from aiogram_dialog.widgets.input import MessageInput
44

55
from src.bot.dto import *
6+
from src.bot.extended_dialog_manager import extend
7+
from src.bot.user_errors import *
68
from src.bot.utils import *
79
from src.db.repositories import meetings_repo
810

911
from .getters import *
12+
from .logic import *
1013
from .states import *
1114

12-
MAX_ATTENDANCE_FILE_SIZE = 5_242_880 # 5 MiB
1315

14-
15-
async def get_attendance_file_close(message: Message, _: MessageInput, dialog_manager: DialogManager):
16-
bot: Bot = dialog_manager.middleware_data["bot"]
17-
state = get_state(dialog_manager)
18-
await clear_messages(dialog_manager)
16+
async def get_attendance_file_close(message: Message, _: MessageInput, manager: DialogManager):
17+
manager = extend(manager)
18+
await clear_messages(manager)
1919
await message.delete()
20-
21-
meeting = dto_to_meeting(await state.get_value("meeting"))
22-
if not meeting:
23-
raise ValueError("No meeting")
24-
25-
if not message.document:
26-
to_delete = await message.answer("You've sent no file ‼️")
27-
await track_message(to_delete, dialog_manager)
28-
return await dialog_manager.switch_to(AttendanceStates.close, show_mode=ShowMode.DELETE_AND_SEND)
29-
30-
file = await bot.get_file(message.document.file_id)
31-
32-
if not file.file_size:
33-
raise ValueError("No file.file_size")
34-
if not file.file_path:
35-
raise ValueError("No file.file_path")
36-
37-
if file.file_size > MAX_ATTENDANCE_FILE_SIZE:
38-
to_delete = await message.answer("File you've sent is over 5MiB in size, I won't accept that ‼️")
39-
await track_message(to_delete, dialog_manager)
40-
return await dialog_manager.switch_to(AttendanceStates.close, show_mode=ShowMode.DELETE_AND_SEND)
41-
42-
bytes = await bot.download_file(file.file_path)
43-
if not bytes:
44-
raise ValueError("No bytes")
45-
46-
content: str = bytes.read().decode("utf-8")
47-
4820
try:
49-
attendance = parse_attendance(content)
50-
meeting.close(attendance)
51-
await meetings_repo.save(meeting)
21+
contents = await get_document_contents(message, manager)
22+
attendance = parse_attendance(contents)
23+
async with manager.state.sync_meeting() as meeting:
24+
meeting.close(attendance)
25+
await meetings_repo.save(meeting)
26+
except NoDocumentError:
27+
return await manager.answer_and_retry("You've sent no file ‼️")
28+
except FileTooBigError:
29+
return await manager.answer_and_retry("File you've sent is over 5MiB in size, I won't accept that ‼️")
5230
except Exception as e:
53-
to_delete = await message.answer(f"There is an error with your file: {e}")
54-
await track_message(to_delete, dialog_manager)
55-
return await dialog_manager.switch_to(AttendanceStates.close, show_mode=ShowMode.DELETE_AND_SEND)
31+
return await manager.answer_and_retry(f"There is an error with your file: {e}")
32+
await manager.done(show_mode=ShowMode.DELETE_AND_SEND)
5633

57-
await state.update_data({"meeting": meeting_to_dto(meeting)})
58-
await dialog_manager.done(show_mode=ShowMode.DELETE_AND_SEND)
5934

60-
61-
async def get_attendance_file_resend(message: Message, _: MessageInput, dialog_manager: DialogManager):
62-
bot: Bot = dialog_manager.middleware_data["bot"]
63-
state = get_state(dialog_manager)
64-
await clear_messages(dialog_manager)
35+
async def get_attendance_file_resend(message: Message, _: MessageInput, manager: DialogManager):
36+
manager = extend(manager)
37+
await clear_messages(manager)
6538
await message.delete()
66-
67-
meeting = dto_to_meeting(await state.get_value("meeting"))
68-
if not meeting:
69-
raise ValueError("No meeting")
70-
71-
if not message.document:
72-
to_delete = await message.answer("You've sent no file ‼️")
73-
await track_message(to_delete, dialog_manager)
74-
return await dialog_manager.switch_to(AttendanceStates.resend, show_mode=ShowMode.DELETE_AND_SEND)
75-
76-
file = await bot.get_file(message.document.file_id)
77-
78-
if not file.file_size:
79-
raise ValueError("No file.file_size")
80-
if not file.file_path:
81-
raise ValueError("No file.file_path")
82-
83-
if file.file_size > MAX_ATTENDANCE_FILE_SIZE:
84-
to_delete = await message.answer("File you've sent is over 5MiB in size, I won't accept that ‼️")
85-
await track_message(to_delete, dialog_manager)
86-
return await dialog_manager.switch_to(AttendanceStates.resend, show_mode=ShowMode.DELETE_AND_SEND)
87-
88-
bytes = await bot.download_file(file.file_path)
89-
if not bytes:
90-
raise ValueError("No bytes")
91-
92-
content: str = bytes.read().decode("utf-8")
93-
9439
try:
95-
attendance = parse_attendance(content)
96-
meeting._attendance = attendance
97-
await meetings_repo.save(meeting)
40+
contents = await get_document_contents(message, manager)
41+
attendance = parse_attendance(contents)
42+
async with manager.state.sync_meeting() as meeting:
43+
meeting._attendance = attendance
44+
await meetings_repo.save(meeting)
45+
except NoDocumentError:
46+
return await manager.answer_and_retry("You've sent no file ‼️")
47+
except FileTooBigError:
48+
return await manager.answer_and_retry("File you've sent is over 5MiB in size, I won't accept that ‼️")
9849
except Exception as e:
99-
to_delete = await message.answer(f"There is an error with your file: {e}")
100-
await track_message(to_delete, dialog_manager)
101-
return await dialog_manager.switch_to(AttendanceStates.resend, show_mode=ShowMode.DELETE_AND_SEND)
50+
return await manager.answer_and_retry(f"There is an error with your file: {e}")
51+
await manager.switch_to(state=AttendanceStates.init, show_mode=ShowMode.DELETE_AND_SEND)
10252

103-
await state.update_data({"meeting": meeting_to_dto(meeting)})
104-
await dialog_manager.switch_to(state=AttendanceStates.init, show_mode=ShowMode.DELETE_AND_SEND)
105-
106-
107-
async def on_download_attendance(query: CallbackQuery, button: Button, dialog_manager: DialogManager):
108-
state = get_state(dialog_manager)
109-
110-
if not query.message:
111-
raise ValueError("No query.message")
112-
113-
meeting = dto_to_meeting(await state.get_value("meeting"))
114-
if not meeting:
115-
raise ValueError("No meeting")
116-
117-
if not meeting.attendance:
118-
return await query.answer("This meeting has no attendance somehow", show_alert=True)
119-
120-
file_content = "\n".join([email.value for email in meeting.attendance])
121-
file_content += "\n"
122-
123-
safe_title = re.sub(r"[^\w\s-]", "", meeting.title).strip().replace(" ", "_")
124-
filename = f"attendance_{safe_title}.txt"
125-
126-
file_bytes = file_content.encode("utf-8")
12753

54+
async def on_download_attendance(query: CallbackQuery, button: Button, manager: DialogManager):
55+
manager = extend(manager)
12856
try:
129-
input_file = BufferedInputFile(file_bytes, filename=filename)
57+
input_file = await get_attendance_file_to_download(manager)
13058
await query.answer("Sending attendance file...")
131-
await query.message.answer_document(document=input_file)
132-
await dialog_manager.switch_to(
133-
state=AttendanceStates.init, show_mode=ShowMode.DELETE_AND_SEND
134-
) # to make window appear after the document
59+
await manager.bot.send_document(manager.chat.id, document=input_file)
60+
except NoMeetingAttendance:
61+
return await query.answer("This meeting has no attendance somehow", show_alert=True)
13562
except Exception as e:
136-
await query.answer(f"Error: {e}", show_alert=True)
63+
return await query.answer(f"Error: {e}", show_alert=True)
64+
await manager.switch_to_current(ShowMode.DELETE_AND_SEND) # to make window appear after the document
13765

13866

139-
async def get_email_to_add(message: Message, _: MessageInput, dialog_manager: DialogManager):
140-
state = get_state(dialog_manager)
141-
await clear_messages(dialog_manager)
67+
async def get_email_to_add(message: Message, _: MessageInput, manager: DialogManager):
68+
manager = extend(manager)
69+
await clear_messages(manager)
14270
await message.delete()
143-
144-
if not message.text:
145-
to_delete = await message.answer("️There's no text in your message, enter email of a person to add")
146-
await track_message(to_delete, dialog_manager)
147-
return await dialog_manager.switch_to(AttendanceStates.add_email, show_mode=ShowMode.DELETE_AND_SEND)
148-
149-
meeting = dto_to_meeting(await state.get_value("meeting"))
150-
if not meeting:
151-
raise ValueError("No meeting")
152-
153-
if not meeting.attendance:
154-
to_delete = await message.answer("Somehow there is no attendance for this meeting, resend the file maybe")
155-
await track_message(to_delete, dialog_manager)
156-
return await dialog_manager.switch_to(AttendanceStates.add_email, show_mode=ShowMode.DELETE_AND_SEND)
157-
15871
try:
159-
email = Email(message.text)
72+
email = await extract_email(message)
73+
except NoMessageText:
74+
return await manager.answer_and_retry("There's no text in your message, enter email of a person to add")
75+
try:
76+
await add_email_to_attendance(email, manager)
77+
except NoMeetingAttendance:
78+
return await manager.answer_and_retry("Somehow there is no attendance for this meeting, resend the file maybe")
79+
except EmailAlreadyPresent:
80+
return await manager.answer_and_retry(f'"{email.value}" is already present')
16081
except Exception as e:
161-
to_delete = await message.answer(f"️Error: {e}")
162-
await track_message(to_delete, dialog_manager)
163-
return await dialog_manager.switch_to(AttendanceStates.add_email, show_mode=ShowMode.DELETE_AND_SEND)
164-
165-
if email in meeting.attendance:
166-
to_delete = await message.answer(f'"{email.value}" is already present')
167-
await track_message(to_delete, dialog_manager)
168-
return await dialog_manager.switch_to(AttendanceStates.add_email, show_mode=ShowMode.DELETE_AND_SEND)
169-
170-
meeting.attendance.append(email)
171-
await state.update_data({"meeting": meeting_to_dto(meeting)})
172-
await meetings_repo.save(meeting)
173-
174-
await dialog_manager.switch_to(AttendanceStates.init, show_mode=ShowMode.DELETE_AND_SEND)
82+
return await manager.answer_and_retry(f"️Error: {e}")
83+
await manager.switch_to(AttendanceStates.init, show_mode=ShowMode.DELETE_AND_SEND)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from aiogram.types import BufferedInputFile, InputFile
2+
from aiogram_dialog import DialogManager
3+
4+
from src.bot.extended_dialog_manager import extend
5+
from src.bot.scheduling import *
6+
from src.bot.user_errors import *
7+
from src.bot.utils import *
8+
from src.db.repositories import meetings_repo
9+
10+
MAX_ATTENDANCE_FILE_SIZE = 5_242_880 # 5 MiB
11+
12+
13+
async def get_document_contents(message: Message, manager: DialogManager) -> str:
14+
manager = extend(manager)
15+
if not message.document:
16+
raise NoDocumentError()
17+
file = await manager.bot.get_file(message.document.file_id)
18+
if not file.file_size:
19+
raise ValueError("No file.file_size")
20+
if not file.file_path:
21+
raise ValueError("No file.file_path")
22+
if file.file_size > MAX_ATTENDANCE_FILE_SIZE:
23+
raise FileTooBigError()
24+
bytes = await manager.bot.download_file(file.file_path)
25+
if not bytes:
26+
raise ValueError("No bytes")
27+
return bytes.read().decode("utf-8")
28+
29+
30+
async def get_attendance_file_to_download(manager: DialogManager) -> InputFile:
31+
manager = extend(manager)
32+
meeting = await manager.state.get_meeting()
33+
if not meeting.attendance:
34+
raise NoMeetingAttendance()
35+
file_content = "\n".join([email.value for email in meeting.attendance])
36+
file_content += "\n"
37+
safe_title = re.sub(r"[^\w\s-]", "", meeting.title).strip().replace(" ", "_")
38+
filename = f"attendance_{safe_title}.txt"
39+
file_bytes = file_content.encode("utf-8")
40+
return BufferedInputFile(file_bytes, filename=filename)
41+
42+
43+
async def extract_email(message: Message) -> Email:
44+
if not message.text:
45+
raise NoMessageText()
46+
return Email(message.text)
47+
48+
49+
async def add_email_to_attendance(email: Email, manager: DialogManager):
50+
manager = extend(manager)
51+
async with manager.state.sync_meeting() as meeting:
52+
if not meeting.attendance:
53+
raise NoMeetingAttendance()
54+
if email in meeting.attendance:
55+
raise EmailAlreadyPresent()
56+
meeting.attendance.append(email)
57+
await meetings_repo.save(meeting)

0 commit comments

Comments
 (0)