Добавлены основные функции 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>
This commit is contained in:
172
bot/handlers/reminder.py
Normal file
172
bot/handlers/reminder.py
Normal file
@@ -0,0 +1,172 @@
|
||||
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 services.user_service import UserService
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
class ReminderStates(StatesGroup):
|
||||
"""Состояния для настройки напоминаний"""
|
||||
waiting_for_time = State()
|
||||
|
||||
|
||||
@router.message(Command("reminder"))
|
||||
async def cmd_reminder(message: Message):
|
||||
"""Обработчик команды /reminder"""
|
||||
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
|
||||
|
||||
# Формируем текст
|
||||
status = "✅ Включены" if user.reminders_enabled else "❌ Выключены"
|
||||
time_text = user.daily_task_time if user.daily_task_time else "Не установлено"
|
||||
|
||||
text = (
|
||||
f"⏰ <b>Напоминания</b>\n\n"
|
||||
f"Статус: {status}\n"
|
||||
f"Время: {time_text} UTC\n\n"
|
||||
f"Напоминания помогут не забывать о ежедневной практике.\n"
|
||||
f"Бот будет присылать сообщение в выбранное время каждый день."
|
||||
)
|
||||
|
||||
# Создаем кнопки
|
||||
keyboard = []
|
||||
|
||||
if user.reminders_enabled:
|
||||
keyboard.append([
|
||||
InlineKeyboardButton(text="❌ Выключить", callback_data="reminder_disable")
|
||||
])
|
||||
else:
|
||||
keyboard.append([
|
||||
InlineKeyboardButton(text="✅ Включить", callback_data="reminder_enable")
|
||||
])
|
||||
|
||||
keyboard.append([
|
||||
InlineKeyboardButton(text="⏰ Изменить время", callback_data="reminder_set_time")
|
||||
])
|
||||
|
||||
reply_markup = InlineKeyboardMarkup(inline_keyboard=keyboard)
|
||||
await message.answer(text, reply_markup=reply_markup)
|
||||
|
||||
|
||||
@router.callback_query(F.data == "reminder_enable")
|
||||
async def enable_reminders(callback: CallbackQuery):
|
||||
"""Включить напоминания"""
|
||||
async with async_session_maker() as session:
|
||||
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
|
||||
|
||||
if not user.daily_task_time:
|
||||
await callback.answer(
|
||||
"Сначала установи время напоминаний!",
|
||||
show_alert=True
|
||||
)
|
||||
return
|
||||
|
||||
user.reminders_enabled = True
|
||||
await session.commit()
|
||||
|
||||
await callback.answer("✅ Напоминания включены!")
|
||||
await callback.message.edit_text(
|
||||
f"✅ <b>Напоминания включены!</b>\n\n"
|
||||
f"Время: {user.daily_task_time} UTC\n\n"
|
||||
f"Ты будешь получать ежедневные напоминания о практике."
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data == "reminder_disable")
|
||||
async def disable_reminders(callback: CallbackQuery):
|
||||
"""Выключить напоминания"""
|
||||
async with async_session_maker() as session:
|
||||
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
|
||||
|
||||
user.reminders_enabled = False
|
||||
await session.commit()
|
||||
|
||||
await callback.answer("❌ Напоминания выключены")
|
||||
await callback.message.edit_text(
|
||||
"❌ <b>Напоминания выключены</b>\n\n"
|
||||
"Используй /reminder чтобы включить их снова."
|
||||
)
|
||||
|
||||
|
||||
@router.callback_query(F.data == "reminder_set_time")
|
||||
async def set_reminder_time_prompt(callback: CallbackQuery, state: FSMContext):
|
||||
"""Запросить время для напоминаний"""
|
||||
await state.set_state(ReminderStates.waiting_for_time)
|
||||
|
||||
await callback.message.edit_text(
|
||||
"⏰ <b>Установка времени напоминаний</b>\n\n"
|
||||
"Отправь время в формате <b>HH:MM</b> (UTC)\n\n"
|
||||
"Примеры:\n"
|
||||
"• <code>09:00</code> - 9 утра по UTC\n"
|
||||
"• <code>18:30</code> - 18:30 по UTC\n"
|
||||
"• <code>20:00</code> - 8 вечера по UTC\n\n"
|
||||
"💡 UTC = МСК - 3 часа\n"
|
||||
"(если хочешь 12:00 по МСК, введи 09:00)\n\n"
|
||||
"Отправь /cancel для отмены"
|
||||
)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@router.message(Command("cancel"), ReminderStates.waiting_for_time)
|
||||
async def cancel_set_time(message: Message, state: FSMContext):
|
||||
"""Отменить установку времени"""
|
||||
await state.clear()
|
||||
await message.answer("❌ Установка времени отменена")
|
||||
|
||||
|
||||
@router.message(ReminderStates.waiting_for_time)
|
||||
async def process_reminder_time(message: Message, state: FSMContext):
|
||||
"""Обработать введённое время"""
|
||||
time_str = message.text.strip()
|
||||
|
||||
# Валидация формата HH:MM
|
||||
try:
|
||||
parts = time_str.split(':')
|
||||
if len(parts) != 2:
|
||||
raise ValueError()
|
||||
|
||||
hour, minute = int(parts[0]), int(parts[1])
|
||||
|
||||
if not (0 <= hour <= 23 and 0 <= minute <= 59):
|
||||
raise ValueError()
|
||||
|
||||
# Формат OK
|
||||
formatted_time = f"{hour:02d}:{minute:02d}"
|
||||
|
||||
except:
|
||||
await message.answer(
|
||||
"❌ Неверный формат времени!\n\n"
|
||||
"Используй формат <b>HH:MM</b> (например, 09:00 или 18:30)\n"
|
||||
"Или отправь /cancel для отмены"
|
||||
)
|
||||
return
|
||||
|
||||
# Сохраняем время
|
||||
async with async_session_maker() as session:
|
||||
user = await UserService.get_user_by_telegram_id(session, message.from_user.id)
|
||||
|
||||
user.daily_task_time = formatted_time
|
||||
|
||||
# Автоматически включаем напоминания
|
||||
user.reminders_enabled = True
|
||||
|
||||
await session.commit()
|
||||
|
||||
await state.clear()
|
||||
|
||||
await message.answer(
|
||||
f"✅ <b>Время установлено!</b>\n\n"
|
||||
f"Напоминания: <b>{formatted_time} UTC</b>\n"
|
||||
f"Статус: <b>Включены</b>\n\n"
|
||||
f"Ты будешь получать ежедневные напоминания о практике.\n"
|
||||
f"Используй /reminder для изменения настроек."
|
||||
)
|
||||
Reference in New Issue
Block a user