# scheduler.py import datetime import pytz from disnake.ext import tasks MOSCOW_TZ = pytz.timezone("Europe/Moscow") # 0 = Monday ... 6 = Sunday (datetime.weekday) SCHEDULED_EVENTS = [ # 1) ECHO TTvT — создавать в пятницу 07:00, начало в пятницу 19:30 { "name": "ECHO TTvT", "day": 4, # Friday "create_hour": 7, "create_minute": 0, "event_hour": 19, "event_minute": 30, "key": "echottvt", }, # 2) AS VDV PSG — создавать в пятницу 22:00, начало в субботу 16:30 # ВАЖНО: create_day = Friday (4), event_day = Saturday (5) { "name": "AS VDV PSG", "day": 4, # Friday for create "create_hour": 22, "create_minute": 0, # событие в субботу 16:30 "event_day": 5, # Saturday "event_hour": 16, "event_minute": 30, "key": "asvdvpsg", }, # 3) ECHO TvT — создавать в субботу 08:00, начало в субботу 19:30 { "name": "ECHO TvT", "day": 4, # Friday for create "create_hour": 22, "create_minute": 0, # событие в субботу 19:30 "event_day": 5, # Saturday "event_hour": 19, "event_minute": 30, "key": "echotvt_saturday", } ] def get_next_datetime(now: datetime.datetime, target_day: int, hour: int, minute: int) -> datetime.datetime: """ Возвращает ближайшую дату/время (timezone-aware, MSK) для target_day/hour/minute, начиная от 'now'. """ days_ahead = (target_day - now.weekday()) % 7 target_date = (now + datetime.timedelta(days=days_ahead)).date() return datetime.datetime( target_date.year, target_date.month, target_date.day, hour, minute, tzinfo=MOSCOW_TZ ) # Этот объект будет заполнен из main.py event_loop_context = {} # Включается из main.py (командой или on_ready) AUTO_EVENTS_ENABLED = False @tasks.loop(minutes=1) async def scheduled_event_loop(): if not AUTO_EVENTS_ENABLED: return # ⛔ автосоздание запрещено командой/состоянием now = datetime.datetime.now(MOSCOW_TZ) create_poll = event_loop_context["create_poll"] event_tasks = event_loop_context["event_tasks"] generate_event_id = event_loop_context["generate_event_id"] for event in SCHEDULED_EVENTS: # День и время создания create_day = event["day"] create_time = get_next_datetime(now, create_day, event["create_hour"], event["create_minute"]) # День и время старта (если не указан event_day — считаем что тот же день) start_day = event.get("event_day", event["day"]) start_time = get_next_datetime(now, start_day, event["event_hour"], event["event_minute"]) # ✅ Догоняющее окно: # если уже пора создать голосование (create_time наступило), # но событие ещё не началось if create_time <= now < start_time: event_id = generate_event_id(event["name"], start_time) # 🔒 Защита от дублей: если есть в event_tasks — значит уже создано if event_id in event_tasks: # Можно оставить принт — удобно для диагностики print(f"⏩ Уже существует: {event_id}") continue info = { "name": event["name"], "start_time": start_time, } print(f"🆕 Автосоздание: {info['name']} в {start_time}") await create_poll(info, message_id=event["key"]) def init_scheduler(create_poll_func, event_tasks_dict, generate_event_id_func): event_loop_context["create_poll"] = create_poll_func event_loop_context["event_tasks"] = event_tasks_dict event_loop_context["generate_event_id"] = generate_event_id_func