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
|
from ..languages import LanguageEnum
|
||||||
|
|
||||||
|
|
||||||
class LexicalCategoryEnum(str, enum.Enum):
|
@enum.unique
|
||||||
|
class LexicalCategoryEnum(enum.StrEnum):
|
||||||
noun = "noun"
|
noun = "noun"
|
||||||
verb = "verb"
|
verb = "verb"
|
||||||
adjective = "adjective"
|
adjective = "adjective"
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import enum
|
|||||||
|
|
||||||
|
|
||||||
@enum.unique
|
@enum.unique
|
||||||
class LanguageEnum(str, enum.Enum):
|
class LanguageEnum(enum.StrEnum):
|
||||||
ru = "ru"
|
ru = "ru"
|
||||||
en = "en"
|
en = "en"
|
||||||
el = "el"
|
el = "el"
|
||||||
|
|||||||
@ -16,6 +16,7 @@ from sqlalchemy.orm import Mapped, mapped_column
|
|||||||
from greek_lang.database.base import Base
|
from greek_lang.database.base import Base
|
||||||
|
|
||||||
|
|
||||||
|
@enum.unique
|
||||||
class ReviewState(enum.StrEnum):
|
class ReviewState(enum.StrEnum):
|
||||||
learning = "learning"
|
learning = "learning"
|
||||||
review = "review"
|
review = "review"
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
from aiogram_dialog import Dialog
|
from aiogram_dialog import Dialog
|
||||||
|
|
||||||
from .main_menu import windows as main_windows
|
from .main_menu import windows as main_windows
|
||||||
|
from .add_word import windows as add_word_windows
|
||||||
|
|
||||||
|
|
||||||
dialog = Dialog(
|
dialog = Dialog(
|
||||||
main_windows.main_window,
|
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.enums import ParseMode
|
||||||
from aiogram_dialog import DialogManager, Window
|
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 ..states import States
|
||||||
|
from . import handlers
|
||||||
|
|
||||||
|
|
||||||
async def main_getter(dialog_manager: DialogManager, **kwargs: Any) -> dict[str, Any]:
|
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(
|
main_window = Window(
|
||||||
Format(
|
Format(
|
||||||
"Выбери действие:",
|
"<b>Выбери действие</b>:",
|
||||||
when=lambda data, widget, dialog_manager: data["dialog_data"].get("action")
|
when=lambda data, widget, dialog_manager: data["dialog_data"].get("action")
|
||||||
is None,
|
is None,
|
||||||
),
|
),
|
||||||
|
Row(
|
||||||
|
Button(
|
||||||
|
Const("Добавить слово"),
|
||||||
|
id="add_word",
|
||||||
|
on_click=handlers.on_add_word,
|
||||||
|
),
|
||||||
|
),
|
||||||
state=States.main_menu,
|
state=States.main_menu,
|
||||||
getter=main_getter,
|
getter=main_getter,
|
||||||
parse_mode=ParseMode.HTML,
|
parse_mode=ParseMode.HTML,
|
||||||
|
|||||||
@ -3,3 +3,5 @@ from aiogram.fsm.state import State, StatesGroup
|
|||||||
|
|
||||||
class States(StatesGroup):
|
class States(StatesGroup):
|
||||||
main_menu = State()
|
main_menu = State()
|
||||||
|
add_word = State()
|
||||||
|
add_word_result = State()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user