ReminderBot/bot/handlers/snooze.py
leo 9ac7e88ac6 feat: replace fixed 10-min snooze with user-defined snooze duration
- Add snooze.py handler: prompts user for snooze minutes and schedules
  a one-shot job with a cancel button (cancel_snooze_<id>)
- Refactor callback.py: support compound action prefix parsing for
  cancel_snooze_*, switch snooze action to prompt-based flow
- Add snooze_action_keyboard() in keyboards.py; update snooze button label
- Register handle_snooze_input in group=1 to avoid ConversationHandler conflict
- Filter completed once-type reminders from get_user_reminders()
- Fix DateTrigger: compare and localize once_time in UTC consistently

Co-Authored-By: claude-sonnet-4-6 <noreply@anthropic.com>
2026-03-05 15:19:02 +08:00

73 lines
2.5 KiB
Python

from datetime import datetime, timedelta, timezone
import pytz
from telegram import Update
from telegram.ext import ContextTypes
from bot.models.database import Session
from bot.models.reminder import Reminder
from bot.utils.keyboards import snooze_action_keyboard
async def handle_snooze_input(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""Handle user input for snooze minutes."""
if "snooze_reminder_id" not in context.user_data:
return
reminder_id = context.user_data.pop("snooze_reminder_id")
chat_id = context.user_data.pop("snooze_chat_id", None)
try:
minutes = int(update.message.text.strip())
if minutes <= 0 or minutes > 1440:
await update.message.reply_text("请输入 1-1440 之间的分钟数。")
return
except ValueError:
await update.message.reply_text("请输入有效的数字。")
return
session = Session()
try:
reminder = session.get(Reminder, reminder_id)
if reminder is None:
await update.message.reply_text("提醒不存在或已被删除。")
return
snooze_time_utc = datetime.now(timezone.utc) + timedelta(minutes=minutes)
# For once-type reminders, update the once_time and reschedule
if reminder.reminder_type == "once":
reminder.once_time = snooze_time_utc
reminder.is_active = True
session.commit()
# Reschedule using APScheduler
from bot.scheduler.job_manager import add_reminder_job
add_reminder_job(reminder_id)
else:
# For recurring reminders, use temporary job_queue
from bot.scheduler.executor import execute_reminder
_rid = reminder_id
async def snooze_callback(ctx):
await execute_reminder(_rid, ctx.bot)
context.job_queue.run_once(
snooze_callback,
when=snooze_time_utc,
name=f"snooze_{reminder_id}",
)
tz = pytz.timezone("Asia/Shanghai")
display_time = snooze_time_utc.astimezone(tz).strftime("%Y-%m-%d %H:%M")
# Only show cancel button for recurring reminders (once-type uses DB now)
keyboard = None if reminder.reminder_type == "once" else snooze_action_keyboard(reminder_id)
await update.message.reply_text(
f"⏰ 已延期 {minutes} 分钟\n将在 {display_time} 再次提醒:{reminder.title}",
reply_markup=keyboard,
)
finally:
Session.remove()