#!/usr/bin/env python3 # handlers.py import logging from datetime import datetime, timedelta from telegram import Update, ReplyKeyboardRemove from telegram.ext import ContextTypes, ConversationHandler, CallbackQueryHandler, MessageHandler, filters from config import * from app.utils import * from database import get_db_connection logger = logging.getLogger(__name__) # ----------------- BASIC HANDLERS ----------------- async def help_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): """Обработчик команды /help""" help_text = """ 🤖 *Помощь по боту-напоминалке* *Основные команды:* /start - Начать работу с ботом /help - Показать это сообщение *Как пользоваться:* 1. Сначала настройте часовой пояс 2. Добавьте лекарства через меню 3. Бот будет присылать напоминания 4. Нажимайте "✅ Выпил(-а)" в напоминании *Меню:* ➕ Добавить лекарство - добавить новое лекарство 📋 Мои лекарства - список всех лекарств ✏️ Редактировать - изменить параметры лекарства 🗑 Удалить - удалить лекарство 📅 История (7 дней) - история приёмов 🌍 Установить часовой пояс - сменить часовой пояс ❌ Отмена - отменить текущее действие """ await update.message.reply_text(help_text, parse_mode='Markdown', reply_markup=main_menu_kb()) return S_MAIN_MENU async def unknown_command(update: Update, context: ContextTypes.DEFAULT_TYPE): """Обработчик неизвестных команд""" await update.message.reply_text( "Неизвестная команда. Используйте /help для списка команд.", reply_markup=main_menu_kb() ) return S_MAIN_MENU async def cancel_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): """Обработчик кнопки Отмена""" await update.message.reply_text("Отмена.", reply_markup=main_menu_kb()) return S_MAIN_MENU # ----------------- LIST PILLS ----------------- async def list_pills(update: Update, context: ContextTypes.DEFAULT_TYPE): user_id = update.effective_user.id conn = get_db_connection() cursor = conn.cursor() cursor.execute( '''SELECT id, name, dose, interval_days, weekdays, course_days, start_date FROM pills WHERE user_id=?''', (user_id,) ) rows = cursor.fetchall() if not rows: await update.message.reply_text( "У вас нет добавленных лекарств.", reply_markup=main_menu_kb() ) conn.close() return S_MAIN_MENU lines = [] for r in rows: pid = r["id"] name = r["name"] dose = r["dose"] interval_days = r["interval_days"] weekdays = r["weekdays"] course_days = r["course_days"] start_date = r["start_date"] cursor.execute( 'SELECT time FROM pill_times WHERE pill_id=?', (pid,) ) times = [t["time"] for t in cursor.fetchall()] freq = "" if weekdays: day_names = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"] selected = [day_names[i] for i in weekdays_csv_to_list(weekdays)] freq = f"по дням ({', '.join(selected)})" else: if interval_days > 1: freq = f"каждые {interval_days} дн." else: freq = "каждый день" course_info = f"курс {course_days} дн." if course_days > 0 else "бессрочно" lines.append( f"ID {pid}: {name} — {dose}\n" f"⏰ Время: {', '.join(times)}\n" f"📅 {freq} | {course_info} (с {start_date})\n" ) conn.close() await update.message.reply_text( "\n".join(lines), reply_markup=main_menu_kb() ) return S_MAIN_MENU # ----------------- HISTORY ----------------- async def history_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): user_id = update.effective_user.id conn = get_db_connection() cursor = conn.cursor() try: # Проверяем, есть ли вообще история за последние 7 дней seven_days_ago = (datetime.utcnow() - timedelta(days=7)).date().strftime("%Y-%m-%d") cursor.execute( '''SELECT COUNT(*) as cnt FROM history WHERE user_id=? AND date(ts) >= ?''', (user_id, seven_days_ago) ) has_history = cursor.fetchone()["cnt"] > 0 if not has_history: await update.message.reply_text( "📭 История приёмов за последние 7 дней пуста.\n\n" "Как только вы отметите приём лекарства (кнопка '✅ Выпил(-а)'), " "здесь появится история.", reply_markup=main_menu_kb() ) conn.close() return S_MAIN_MENU # Получаем все лекарства пользователя cursor.execute('SELECT id, name FROM pills WHERE user_id=?', (user_id,)) pills = cursor.fetchall() now = datetime.utcnow().date() lines = [] for pill in pills: pid = pill["id"] name = pill["name"] lines.append(f"💊 {name}:") for d in range(6, -1, -1): day = now - timedelta(days=d) day_str = day.strftime("%Y-%m-%d") cursor.execute('SELECT COUNT(*) as cnt FROM history WHERE pill_id=? AND user_id=? AND date(ts)=?', (pid, user_id, day_str)) cnt = cursor.fetchone()["cnt"] mark = "✅" if cnt > 0 else "❌" lines.append(f"{day.strftime('%d.%m')}: {mark}") lines.append("") response = "\n".join(lines) await update.message.reply_text(response, reply_markup=main_menu_kb()) except Exception as e: logger.error(f"Error in history_handler: {e}") await update.message.reply_text( "❌ Ошибка при получении истории.", reply_markup=main_menu_kb() ) finally: conn.close() return S_MAIN_MENU # ----------------- CALLBACK: TAKEN ----------------- async def callback_taken(update: Update, context: ContextTypes.DEFAULT_TYPE): query = update.callback_query await query.answer() if not query.data or not query.data.startswith("take:"): await query.edit_message_text("Неизвестная команда.") return try: ptid = int(query.data.split(":", 1)[1]) except ValueError: await query.edit_message_text("Ошибка в данных.") return conn = get_db_connection() cursor = conn.cursor() try: cursor.execute( '''SELECT pt.pill_id, p.user_id, p.name FROM pill_times pt JOIN pills p ON p.id = pt.pill_id WHERE pt.id=?''', (ptid,) ) row = cursor.fetchone() if not row: await query.edit_message_text("Запись не найдена.") conn.close() return pid = row["pill_id"] user_id = row["user_id"] pill_name = row["name"] now_utc = datetime.utcnow() now_min_str = utc_str_min(now_utc) now_full_str = utc_str_full(now_utc) cursor.execute( 'UPDATE pill_times SET last_confirmed=? WHERE id=?', (now_min_str, ptid) ) cursor.execute( '''INSERT INTO history (pill_id, user_id, ts, taken) VALUES (?, ?, ?, 1)''', (pid, user_id, now_full_str) ) conn.commit() await query.edit_message_reply_markup(reply_markup=None) await query.message.reply_text( f"✅ Приём '{pill_name}' зарегистрирован!\n" "Спасибо, что заботишься о своём здоровье 💖", reply_markup=main_menu_kb() ) except Exception as e: logger.error(f"Error in callback_taken: {e}") await query.edit_message_text("Произошла ошибка.") finally: conn.close()