init commit

This commit is contained in:
2025-12-18 05:55:32 +09:00
commit a6817e487e
72 changed files with 13847 additions and 0 deletions

139
app/handlers/schedule.py Normal file
View File

@@ -0,0 +1,139 @@
"""
Обработчик команд для управления расписанием рассылок
"""
import logging
from telegram import Update
from telegram.ext import ContextTypes
from app.scheduler import broadcast_scheduler, schedule_broadcast, cancel_broadcast, list_broadcasts
from app.database.repository import MessageRepository
from app.database import AsyncSessionLocal
logger = logging.getLogger(__name__)
async def schedule_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Команда для управления расписанием"""
if not update.message:
return
user_id = update.message.from_user.id
# Только администратор может управлять расписанием
# (это нужно добавить в конфигурацию)
try:
# /schedule list - показать все расписания
if context.args and context.args[0] == 'list':
schedules = await list_broadcasts()
if not schedules:
await update.message.reply_text("📋 Нет активных расписаний")
return
text = "📅 Активные расписания:\n\n"
for idx, sched in enumerate(schedules, 1):
text += f"{idx}. {sched['name']}\n"
text += f" ID: `{sched['id']}`\n"
text += f" Расписание: {sched['trigger']}\n"
text += f" Следующее выполнение: {sched['next_run_time']}\n\n"
await update.message.reply_text(text, parse_mode='Markdown')
# /schedule add message_id group_id cron_expr
elif context.args and context.args[0] == 'add':
if len(context.args) < 4:
await update.message.reply_text(
"❌ Использование: /schedule add <message_id> <group_id> <cron_expr>\n\n"
"Пример: /schedule add 1 10 '0 9 * * *'\n\n"
"Cron формат: minute hour day month day_of_week\n"
"0 9 * * * - ежедневно в 9:00 UTC"
)
return
try:
message_id = int(context.args[1])
group_id = int(context.args[2])
cron_expr = ' '.join(context.args[3:])
# Проверить, что сообщение существует
async with AsyncSessionLocal() as session:
message_repo = MessageRepository(session)
message = await message_repo.get_by_id(message_id)
if not message:
await update.message.reply_text(f"❌ Сообщение с ID {message_id} не найдено")
return
job_id = await schedule_broadcast(
message_id=message_id,
group_ids=[group_id],
cron_expr=cron_expr
)
await update.message.reply_text(
f"✅ Расписание создано!\n\n"
f"ID: `{job_id}`\n"
f"Сообщение: {message_id}\n"
f"Группа: {group_id}\n"
f"Расписание: {cron_expr}"
)
except ValueError as e:
await update.message.reply_text(f"❌ Ошибка: {e}")
except Exception as e:
logger.error(f"Ошибка при создании расписания: {e}")
await update.message.reply_text(f"❌ Ошибка: {e}")
# /schedule remove job_id
elif context.args and context.args[0] == 'remove':
if len(context.args) < 2:
await update.message.reply_text(
"❌ Использование: /schedule remove <job_id>"
)
return
job_id = context.args[1]
success = await cancel_broadcast(job_id)
if success:
await update.message.reply_text(f"✅ Расписание удалено: {job_id}")
else:
await update.message.reply_text(f"❌ Расписание не найдено: {job_id}")
else:
await update.message.reply_text(
"📅 Управление расписанием\n\n"
"Команды:\n"
"/schedule list - показать все расписания\n"
"/schedule add <msg_id> <group_id> <cron> - добавить расписание\n"
"/schedule remove <job_id> - удалить расписание\n\n"
"Примеры cron:\n"
"0 9 * * * - ежедневно в 9:00 UTC\n"
"0 9 * * MON - по понедельникам в 9:00\n"
"*/30 * * * * - каждые 30 минут"
)
except Exception as e:
logger.error(f"Ошибка в команде schedule: {e}")
await update.message.reply_text(f"❌ Ошибка: {e}")
async def initialize_scheduler(context: ContextTypes.DEFAULT_TYPE):
"""Инициализировать планировщик при запуске бота"""
try:
await broadcast_scheduler.initialize()
broadcast_scheduler.start()
await broadcast_scheduler.add_maintenance_schedules()
logger.info("✅ Планировщик инициализирован и запущен")
except Exception as e:
logger.error(f"❌ Ошибка при инициализации планировщика: {e}")
async def shutdown_scheduler(context: ContextTypes.DEFAULT_TYPE):
"""Завершить планировщик при остановке бота"""
try:
await broadcast_scheduler.shutdown()
logger.info("✅ Планировщик завершен")
except Exception as e:
logger.error(f"❌ Ошибка при завершении планировщика: {e}")