Добавлены основные функции MVP: тематические подборки, импорт слов, диалоговая практика, напоминания и тест уровня
Новые команды:
- /words [тема] - AI-генерация тематических подборок слов (10 слов по теме с учётом уровня)
- /import - извлечение до 15 ключевых слов из текста (книги, статьи, песни)
- /practice - диалоговая практика с AI в 6 сценариях (ресторан, магазин, путешествие, работа, врач, общение)
- /reminder - настройка ежедневных напоминаний по расписанию
- /level_test - тест из 7 вопросов для определения уровня английского (A1-C2)
Основные изменения:
- AI сервис: добавлены методы generate_thematic_words, extract_words_from_text, start_conversation, continue_conversation, generate_level_test
- Диалоговая практика: исправление ошибок в реальном времени, подсказки, перевод реплик
- Напоминания: APScheduler для ежедневной отправки напоминаний в выбранное время
- Тест уровня: автоматическое определение уровня при регистрации, можно пропустить
- База данных: добавлены поля reminders_enabled, last_reminder_sent
- Vocabulary service: метод get_word_by_original для проверки дубликатов
- Зависимости: apscheduler==3.10.4
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 15:46:02 +03:00
|
|
|
|
from aiogram import Router, F
|
|
|
|
|
|
from aiogram.filters import Command
|
|
|
|
|
|
from aiogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, CallbackQuery
|
|
|
|
|
|
from aiogram.fsm.context import FSMContext
|
|
|
|
|
|
from aiogram.fsm.state import State, StatesGroup
|
|
|
|
|
|
|
|
|
|
|
|
from database.db import async_session_maker
|
|
|
|
|
|
from database.models import WordSource
|
|
|
|
|
|
from services.user_service import UserService
|
|
|
|
|
|
from services.vocabulary_service import VocabularyService
|
|
|
|
|
|
from services.ai_service import ai_service
|
|
|
|
|
|
|
|
|
|
|
|
router = Router()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WordsStates(StatesGroup):
|
|
|
|
|
|
"""Состояния для работы с тематическими подборками"""
|
|
|
|
|
|
viewing_words = State()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.message(Command("words"))
|
|
|
|
|
|
async def cmd_words(message: Message, state: FSMContext):
|
|
|
|
|
|
"""Обработчик команды /words [тема]"""
|
|
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
|
user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
|
|
|
|
|
|
|
|
|
|
|
|
if not user:
|
|
|
|
|
|
await message.answer("Сначала запусти бота командой /start")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# Извлекаем тему из команды
|
|
|
|
|
|
command_parts = message.text.split(maxsplit=1)
|
|
|
|
|
|
|
|
|
|
|
|
if len(command_parts) < 2:
|
|
|
|
|
|
await message.answer(
|
|
|
|
|
|
"📚 <b>Тематические подборки слов</b>\n\n"
|
|
|
|
|
|
"Используй: <code>/words [тема]</code>\n\n"
|
|
|
|
|
|
"Примеры:\n"
|
|
|
|
|
|
"• <code>/words travel</code> - путешествия\n"
|
|
|
|
|
|
"• <code>/words food</code> - еда\n"
|
|
|
|
|
|
"• <code>/words work</code> - работа\n"
|
|
|
|
|
|
"• <code>/words nature</code> - природа\n"
|
|
|
|
|
|
"• <code>/words technology</code> - технологии\n\n"
|
|
|
|
|
|
"Я сгенерирую 10 слов по теме, подходящих для твоего уровня!"
|
|
|
|
|
|
)
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
theme = command_parts[1].strip()
|
|
|
|
|
|
|
|
|
|
|
|
# Показываем индикатор генерации
|
|
|
|
|
|
generating_msg = await message.answer(f"🔄 Генерирую подборку слов по теме '{theme}'...")
|
|
|
|
|
|
|
|
|
|
|
|
# Генерируем слова через AI
|
|
|
|
|
|
words = await ai_service.generate_thematic_words(
|
|
|
|
|
|
theme=theme,
|
|
|
|
|
|
level=user.level.value,
|
|
|
|
|
|
count=10
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
await generating_msg.delete()
|
|
|
|
|
|
|
|
|
|
|
|
if not words:
|
|
|
|
|
|
await message.answer(
|
|
|
|
|
|
"❌ Не удалось сгенерировать подборку. Попробуй другую тему или повтори позже."
|
|
|
|
|
|
)
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# Сохраняем данные в состоянии
|
|
|
|
|
|
await state.update_data(
|
|
|
|
|
|
theme=theme,
|
|
|
|
|
|
words=words,
|
2025-12-04 16:44:16 +03:00
|
|
|
|
user_id=user.id,
|
|
|
|
|
|
level=user.level.name
|
Добавлены основные функции MVP: тематические подборки, импорт слов, диалоговая практика, напоминания и тест уровня
Новые команды:
- /words [тема] - AI-генерация тематических подборок слов (10 слов по теме с учётом уровня)
- /import - извлечение до 15 ключевых слов из текста (книги, статьи, песни)
- /practice - диалоговая практика с AI в 6 сценариях (ресторан, магазин, путешествие, работа, врач, общение)
- /reminder - настройка ежедневных напоминаний по расписанию
- /level_test - тест из 7 вопросов для определения уровня английского (A1-C2)
Основные изменения:
- AI сервис: добавлены методы generate_thematic_words, extract_words_from_text, start_conversation, continue_conversation, generate_level_test
- Диалоговая практика: исправление ошибок в реальном времени, подсказки, перевод реплик
- Напоминания: APScheduler для ежедневной отправки напоминаний в выбранное время
- Тест уровня: автоматическое определение уровня при регистрации, можно пропустить
- База данных: добавлены поля reminders_enabled, last_reminder_sent
- Vocabulary service: метод get_word_by_original для проверки дубликатов
- Зависимости: apscheduler==3.10.4
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 15:46:02 +03:00
|
|
|
|
)
|
|
|
|
|
|
await state.set_state(WordsStates.viewing_words)
|
|
|
|
|
|
|
|
|
|
|
|
# Показываем подборку
|
|
|
|
|
|
await show_words_list(message, words, theme)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def show_words_list(message: Message, words: list, theme: str):
|
|
|
|
|
|
"""Показать список слов с кнопками для добавления"""
|
|
|
|
|
|
|
|
|
|
|
|
text = f"📚 <b>Подборка слов: {theme}</b>\n\n"
|
|
|
|
|
|
|
|
|
|
|
|
for idx, word_data in enumerate(words, 1):
|
|
|
|
|
|
text += (
|
|
|
|
|
|
f"{idx}. <b>{word_data['word']}</b> "
|
|
|
|
|
|
f"[{word_data.get('transcription', '')}]\n"
|
|
|
|
|
|
f" {word_data['translation']}\n"
|
|
|
|
|
|
f" <i>{word_data.get('example', '')}</i>\n\n"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
text += "Выбери слова, которые хочешь добавить в словарь:"
|
|
|
|
|
|
|
|
|
|
|
|
# Создаем кнопки для каждого слова (по 2 в ряд)
|
|
|
|
|
|
keyboard = []
|
|
|
|
|
|
for idx, word_data in enumerate(words):
|
|
|
|
|
|
button = InlineKeyboardButton(
|
|
|
|
|
|
text=f"➕ {word_data['word']}",
|
|
|
|
|
|
callback_data=f"add_word_{idx}"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# Добавляем по 2 кнопки в ряд
|
|
|
|
|
|
if len(keyboard) == 0 or len(keyboard[-1]) == 2:
|
|
|
|
|
|
keyboard.append([button])
|
|
|
|
|
|
else:
|
|
|
|
|
|
keyboard[-1].append(button)
|
|
|
|
|
|
|
|
|
|
|
|
# Кнопка "Добавить все"
|
|
|
|
|
|
keyboard.append([
|
|
|
|
|
|
InlineKeyboardButton(text="✅ Добавить все", callback_data="add_all_words")
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
# Кнопка "Закрыть"
|
|
|
|
|
|
keyboard.append([
|
|
|
|
|
|
InlineKeyboardButton(text="❌ Закрыть", callback_data="close_words")
|
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
|
|
reply_markup = InlineKeyboardMarkup(inline_keyboard=keyboard)
|
|
|
|
|
|
await message.answer(text, reply_markup=reply_markup)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.callback_query(F.data.startswith("add_word_"), WordsStates.viewing_words)
|
|
|
|
|
|
async def add_single_word(callback: CallbackQuery, state: FSMContext):
|
|
|
|
|
|
"""Добавить одно слово из подборки"""
|
|
|
|
|
|
word_index = int(callback.data.split("_")[2])
|
|
|
|
|
|
|
|
|
|
|
|
data = await state.get_data()
|
|
|
|
|
|
words = data.get('words', [])
|
|
|
|
|
|
user_id = data.get('user_id')
|
|
|
|
|
|
|
|
|
|
|
|
if word_index >= len(words):
|
|
|
|
|
|
await callback.answer("❌ Ошибка: слово не найдено")
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
word_data = words[word_index]
|
|
|
|
|
|
|
|
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
|
# Проверяем, нет ли уже такого слова
|
|
|
|
|
|
existing = await VocabularyService.get_word_by_original(
|
|
|
|
|
|
session, user_id, word_data['word']
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if existing:
|
|
|
|
|
|
await callback.answer(f"Слово '{word_data['word']}' уже в словаре", show_alert=True)
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# Добавляем слово
|
|
|
|
|
|
await VocabularyService.add_word(
|
|
|
|
|
|
session=session,
|
|
|
|
|
|
user_id=user_id,
|
|
|
|
|
|
word_original=word_data['word'],
|
|
|
|
|
|
word_translation=word_data['translation'],
|
|
|
|
|
|
transcription=word_data.get('transcription'),
|
|
|
|
|
|
examples=[{"en": word_data.get('example', ''), "ru": ""}] if word_data.get('example') else [],
|
|
|
|
|
|
source=WordSource.SUGGESTED,
|
|
|
|
|
|
category=data.get('theme', 'general'),
|
2025-12-04 16:44:16 +03:00
|
|
|
|
difficulty_level=data.get('level')
|
Добавлены основные функции MVP: тематические подборки, импорт слов, диалоговая практика, напоминания и тест уровня
Новые команды:
- /words [тема] - AI-генерация тематических подборок слов (10 слов по теме с учётом уровня)
- /import - извлечение до 15 ключевых слов из текста (книги, статьи, песни)
- /practice - диалоговая практика с AI в 6 сценариях (ресторан, магазин, путешествие, работа, врач, общение)
- /reminder - настройка ежедневных напоминаний по расписанию
- /level_test - тест из 7 вопросов для определения уровня английского (A1-C2)
Основные изменения:
- AI сервис: добавлены методы generate_thematic_words, extract_words_from_text, start_conversation, continue_conversation, generate_level_test
- Диалоговая практика: исправление ошибок в реальном времени, подсказки, перевод реплик
- Напоминания: APScheduler для ежедневной отправки напоминаний в выбранное время
- Тест уровня: автоматическое определение уровня при регистрации, можно пропустить
- База данных: добавлены поля reminders_enabled, last_reminder_sent
- Vocabulary service: метод get_word_by_original для проверки дубликатов
- Зависимости: apscheduler==3.10.4
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 15:46:02 +03:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
await callback.answer(f"✅ Слово '{word_data['word']}' добавлено в словарь")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.callback_query(F.data == "add_all_words", WordsStates.viewing_words)
|
|
|
|
|
|
async def add_all_words(callback: CallbackQuery, state: FSMContext):
|
|
|
|
|
|
"""Добавить все слова из подборки"""
|
|
|
|
|
|
data = await state.get_data()
|
|
|
|
|
|
words = data.get('words', [])
|
|
|
|
|
|
user_id = data.get('user_id')
|
|
|
|
|
|
theme = data.get('theme', 'general')
|
|
|
|
|
|
|
|
|
|
|
|
added_count = 0
|
|
|
|
|
|
skipped_count = 0
|
|
|
|
|
|
|
|
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
|
for word_data in words:
|
|
|
|
|
|
# Проверяем, нет ли уже такого слова
|
|
|
|
|
|
existing = await VocabularyService.get_word_by_original(
|
|
|
|
|
|
session, user_id, word_data['word']
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if existing:
|
|
|
|
|
|
skipped_count += 1
|
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
# Добавляем слово
|
|
|
|
|
|
await VocabularyService.add_word(
|
|
|
|
|
|
session=session,
|
|
|
|
|
|
user_id=user_id,
|
|
|
|
|
|
word_original=word_data['word'],
|
|
|
|
|
|
word_translation=word_data['translation'],
|
|
|
|
|
|
transcription=word_data.get('transcription'),
|
|
|
|
|
|
examples=[{"en": word_data.get('example', ''), "ru": ""}] if word_data.get('example') else [],
|
|
|
|
|
|
source=WordSource.SUGGESTED,
|
|
|
|
|
|
category=theme,
|
2025-12-04 16:44:16 +03:00
|
|
|
|
difficulty_level=data.get('level')
|
Добавлены основные функции MVP: тематические подборки, импорт слов, диалоговая практика, напоминания и тест уровня
Новые команды:
- /words [тема] - AI-генерация тематических подборок слов (10 слов по теме с учётом уровня)
- /import - извлечение до 15 ключевых слов из текста (книги, статьи, песни)
- /practice - диалоговая практика с AI в 6 сценариях (ресторан, магазин, путешествие, работа, врач, общение)
- /reminder - настройка ежедневных напоминаний по расписанию
- /level_test - тест из 7 вопросов для определения уровня английского (A1-C2)
Основные изменения:
- AI сервис: добавлены методы generate_thematic_words, extract_words_from_text, start_conversation, continue_conversation, generate_level_test
- Диалоговая практика: исправление ошибок в реальном времени, подсказки, перевод реплик
- Напоминания: APScheduler для ежедневной отправки напоминаний в выбранное время
- Тест уровня: автоматическое определение уровня при регистрации, можно пропустить
- База данных: добавлены поля reminders_enabled, last_reminder_sent
- Vocabulary service: метод get_word_by_original для проверки дубликатов
- Зависимости: apscheduler==3.10.4
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 15:46:02 +03:00
|
|
|
|
)
|
|
|
|
|
|
added_count += 1
|
|
|
|
|
|
|
|
|
|
|
|
result_text = f"✅ Добавлено слов: <b>{added_count}</b>"
|
|
|
|
|
|
if skipped_count > 0:
|
|
|
|
|
|
result_text += f"\n⚠️ Пропущено (уже в словаре): {skipped_count}"
|
|
|
|
|
|
|
|
|
|
|
|
await callback.message.edit_reply_markup(reply_markup=None)
|
|
|
|
|
|
await callback.message.answer(result_text)
|
|
|
|
|
|
await state.clear()
|
|
|
|
|
|
await callback.answer()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.callback_query(F.data == "close_words", WordsStates.viewing_words)
|
|
|
|
|
|
async def close_words(callback: CallbackQuery, state: FSMContext):
|
|
|
|
|
|
"""Закрыть подборку слов"""
|
|
|
|
|
|
await callback.message.delete()
|
|
|
|
|
|
await state.clear()
|
|
|
|
|
|
await callback.answer()
|