add "add word" dialog with handlers, windows, and states integration
This commit is contained in:
parent
40ed52777e
commit
6c34219a6f
@ -11,7 +11,8 @@ from ..database.base import Base
|
||||
from ..languages import LanguageEnum
|
||||
|
||||
|
||||
class LexicalCategoryEnum(str, enum.Enum):
|
||||
@enum.unique
|
||||
class LexicalCategoryEnum(enum.StrEnum):
|
||||
noun = "noun"
|
||||
verb = "verb"
|
||||
adjective = "adjective"
|
||||
|
||||
@ -2,7 +2,7 @@ import enum
|
||||
|
||||
|
||||
@enum.unique
|
||||
class LanguageEnum(str, enum.Enum):
|
||||
class LanguageEnum(enum.StrEnum):
|
||||
ru = "ru"
|
||||
en = "en"
|
||||
el = "el"
|
||||
|
||||
@ -16,6 +16,7 @@ from sqlalchemy.orm import Mapped, mapped_column
|
||||
from greek_lang.database.base import Base
|
||||
|
||||
|
||||
@enum.unique
|
||||
class ReviewState(enum.StrEnum):
|
||||
learning = "learning"
|
||||
review = "review"
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
from aiogram_dialog import Dialog
|
||||
|
||||
from .main_menu import windows as main_windows
|
||||
from .add_word import windows as add_word_windows
|
||||
|
||||
|
||||
dialog = Dialog(
|
||||
main_windows.main_window,
|
||||
add_word_windows.add_word_window,
|
||||
add_word_windows.add_word_result_window,
|
||||
)
|
||||
|
||||
0
src/greek_lang/tg_bot/dialogs/add_word/__init__.py
Normal file
0
src/greek_lang/tg_bot/dialogs/add_word/__init__.py
Normal file
65
src/greek_lang/tg_bot/dialogs/add_word/handlers.py
Normal file
65
src/greek_lang/tg_bot/dialogs/add_word/handlers.py
Normal file
@ -0,0 +1,65 @@
|
||||
from aiogram.types import Message, CallbackQuery, BufferedInputFile
|
||||
from aiogram_dialog import DialogManager
|
||||
from aiogram_dialog.widgets.input import MessageInput
|
||||
from aiogram_dialog.widgets.kbd import Button
|
||||
from greek_lang.languages import LanguageEnum
|
||||
from greek_lang.translator import translate
|
||||
from ..states import States
|
||||
|
||||
|
||||
async def add_word(
|
||||
message: Message,
|
||||
source: MessageInput | Button,
|
||||
manager: DialogManager,
|
||||
) -> None:
|
||||
if not message.text:
|
||||
return
|
||||
word = message.text.strip()
|
||||
if not word:
|
||||
return
|
||||
source_lang = LanguageEnum.ru
|
||||
target_lang = LanguageEnum.el
|
||||
glossary_word = await translate(word, source_lang, target_lang=target_lang)
|
||||
|
||||
# Try to send audio pronunciation back to the user
|
||||
try:
|
||||
audio_bytes = getattr(glossary_word, "audio_file", None)
|
||||
if audio_bytes:
|
||||
# aiogTTS produces MP3 data; send as audio
|
||||
caption = (
|
||||
f"<b>{glossary_word.term}</b> → <b>{glossary_word.translation}</b>"
|
||||
)
|
||||
input_file = BufferedInputFile(
|
||||
audio_bytes, filename=f"{glossary_word.term}.mp3"
|
||||
)
|
||||
await message.answer_audio(
|
||||
audio=input_file, caption=caption, parse_mode="HTML"
|
||||
)
|
||||
except Exception:
|
||||
# Silently ignore audio sending issues to not break the flow
|
||||
pass
|
||||
|
||||
# Store data for the result window
|
||||
manager.dialog_data.update(
|
||||
{
|
||||
"term": glossary_word.term,
|
||||
"translation": glossary_word.translation,
|
||||
"transcription": glossary_word.transcription or "",
|
||||
"lexical_category": getattr(glossary_word, "lexical_category", ""),
|
||||
"description": glossary_word.description or "",
|
||||
"example": glossary_word.example or "",
|
||||
"note": glossary_word.note or "",
|
||||
}
|
||||
)
|
||||
|
||||
# Switch to the result window state
|
||||
await manager.switch_to(States.add_word_result)
|
||||
|
||||
|
||||
async def on_add_another(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
manager: DialogManager,
|
||||
) -> None:
|
||||
await callback.answer()
|
||||
await manager.switch_to(States.add_word)
|
||||
73
src/greek_lang/tg_bot/dialogs/add_word/windows.py
Normal file
73
src/greek_lang/tg_bot/dialogs/add_word/windows.py
Normal file
@ -0,0 +1,73 @@
|
||||
from typing import Any
|
||||
|
||||
from aiogram.enums import ParseMode
|
||||
from aiogram_dialog import DialogManager, Window
|
||||
from aiogram_dialog.widgets.input import MessageInput
|
||||
from aiogram_dialog.widgets.kbd import Button, Row
|
||||
from aiogram_dialog.widgets.markup.reply_keyboard import ReplyKeyboardFactory
|
||||
from aiogram_dialog.widgets.text import Format, Const
|
||||
|
||||
from ..states import States
|
||||
from ..base_handlers import cancel_handler
|
||||
from . import handlers
|
||||
|
||||
|
||||
async def add_word_window_getter(
|
||||
dialog_manager: DialogManager, **kwargs: Any
|
||||
) -> dict[str, Any]:
|
||||
return {}
|
||||
|
||||
|
||||
add_word_window = Window(
|
||||
Const("<b>Введите слово</b>:"),
|
||||
MessageInput(func=handlers.add_word),
|
||||
Row(
|
||||
Button(
|
||||
Format("❌ Отмена"),
|
||||
on_click=cancel_handler, # type: ignore[arg-type]
|
||||
id="cancel_add_word",
|
||||
),
|
||||
),
|
||||
markup_factory=ReplyKeyboardFactory(
|
||||
input_field_placeholder=Format("Слово..."),
|
||||
resize_keyboard=True,
|
||||
one_time_keyboard=True,
|
||||
),
|
||||
state=States.add_word,
|
||||
parse_mode=ParseMode.HTML,
|
||||
getter=add_word_window_getter,
|
||||
)
|
||||
|
||||
|
||||
async def add_word_result_getter(
|
||||
dialog_manager: DialogManager, **kwargs: Any
|
||||
) -> dict[str, Any]:
|
||||
# Expose dialog_data fields directly for Format widgets
|
||||
return dialog_manager.dialog_data
|
||||
|
||||
|
||||
add_word_result_window = Window(
|
||||
Format(
|
||||
"✅ <b>Слово добавлено</b>!\n\n"
|
||||
"<b>{term}</b> → <b>{translation}</b>\n"
|
||||
"{transcription}\n"
|
||||
"<b>Часть речи</b>: {lexical_category!s}\n"
|
||||
"<b>Описание</b>: {description}\n"
|
||||
"<b>Пример</b>: {example}"
|
||||
),
|
||||
Row(
|
||||
Button(
|
||||
Const("➕ Добавить ещё"),
|
||||
id="add_another",
|
||||
on_click=handlers.on_add_another,
|
||||
),
|
||||
Button(
|
||||
Const("🏠 В меню"),
|
||||
on_click=cancel_handler, # type: ignore[arg-type]
|
||||
id="to_main_menu",
|
||||
),
|
||||
),
|
||||
state=States.add_word_result,
|
||||
parse_mode=ParseMode.HTML,
|
||||
getter=add_word_result_getter,
|
||||
)
|
||||
16
src/greek_lang/tg_bot/dialogs/base_handlers.py
Normal file
16
src/greek_lang/tg_bot/dialogs/base_handlers.py
Normal file
@ -0,0 +1,16 @@
|
||||
from aiogram_dialog import DialogManager, ShowMode
|
||||
from aiogram_dialog.api.internal import ReplyCallbackQuery
|
||||
from aiogram_dialog.widgets.kbd import Cancel
|
||||
|
||||
from .states import States
|
||||
|
||||
|
||||
async def cancel_handler(
|
||||
callback_query: ReplyCallbackQuery,
|
||||
button: Cancel,
|
||||
manager: DialogManager,
|
||||
) -> None:
|
||||
await manager.switch_to(
|
||||
States.main_menu,
|
||||
show_mode=ShowMode.DELETE_AND_SEND,
|
||||
)
|
||||
@ -0,0 +1,26 @@
|
||||
from aiogram.types import CallbackQuery
|
||||
from aiogram_dialog import DialogManager, ShowMode
|
||||
from aiogram_dialog.api.internal import ReplyCallbackQuery
|
||||
from aiogram_dialog.widgets.kbd import Cancel, Button
|
||||
|
||||
from ..states import States
|
||||
|
||||
|
||||
async def on_add_word(
|
||||
callback: CallbackQuery,
|
||||
button: Button,
|
||||
manager: DialogManager,
|
||||
) -> None:
|
||||
await callback.answer()
|
||||
await manager.switch_to(States.add_word)
|
||||
|
||||
|
||||
async def cancel_handler(
|
||||
callback_query: ReplyCallbackQuery,
|
||||
button: Cancel,
|
||||
manager: DialogManager,
|
||||
) -> None:
|
||||
await manager.switch_to(
|
||||
States.add_word,
|
||||
show_mode=ShowMode.DELETE_AND_SEND,
|
||||
)
|
||||
@ -2,9 +2,11 @@ from typing import Any
|
||||
|
||||
from aiogram.enums import ParseMode
|
||||
from aiogram_dialog import DialogManager, Window
|
||||
from aiogram_dialog.widgets.text import Format
|
||||
from aiogram_dialog.widgets.kbd import Row, Button
|
||||
from aiogram_dialog.widgets.text import Format, Const
|
||||
|
||||
from ..states import States
|
||||
from . import handlers
|
||||
|
||||
|
||||
async def main_getter(dialog_manager: DialogManager, **kwargs: Any) -> dict[str, Any]:
|
||||
@ -13,10 +15,17 @@ async def main_getter(dialog_manager: DialogManager, **kwargs: Any) -> dict[str,
|
||||
|
||||
main_window = Window(
|
||||
Format(
|
||||
"Выбери действие:",
|
||||
"<b>Выбери действие</b>:",
|
||||
when=lambda data, widget, dialog_manager: data["dialog_data"].get("action")
|
||||
is None,
|
||||
),
|
||||
Row(
|
||||
Button(
|
||||
Const("Добавить слово"),
|
||||
id="add_word",
|
||||
on_click=handlers.on_add_word,
|
||||
),
|
||||
),
|
||||
state=States.main_menu,
|
||||
getter=main_getter,
|
||||
parse_mode=ParseMode.HTML,
|
||||
|
||||
@ -3,3 +3,5 @@ from aiogram.fsm.state import State, StatesGroup
|
||||
|
||||
class States(StatesGroup):
|
||||
main_menu = State()
|
||||
add_word = State()
|
||||
add_word_result = State()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user