From 5d88b8899ee51fc294482beb2c54e68c785c0858 Mon Sep 17 00:00:00 2001 From: Kenik Date: Wed, 2 Sep 2026 21:49:28 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9D=D0=B0=D0=BF=D0=BE=D0=BC=D0=B8=D0=BD?= =?UTF-8?q?=D0=B0=D0=BB=D0=BA=D0=B8:=20=D1=84=D0=BE=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D1=8B=D0=B9=20=D0=BF=D0=BB=D0=B0=D0=BD=D0=B8=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D1=89=D0=B8=D0=BA=20=D0=BE=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BA?= =?UTF-8?q?=D0=B8=20=D0=B2=20=D1=81=D1=80=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5 --- bot.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/bot.py b/bot.py index c591740..4dbfb64 100644 --- a/bot.py +++ b/bot.py @@ -1956,8 +1956,40 @@ class VKBotApplication: time.sleep(0.4) # троттлинг против лимитов VK self.send_message(admin_id, f"✅ Рассылка завершена. Доставлено получателям: {sent} из {len(recipients)}.") + def _check_due_reminders(self): + """Проверяет и рассылает напоминания, чьё время наступило. + Вызывается фоновым потоком `_reminders_loop`, но выделена отдельно, + чтобы её можно было дёргать напрямую из тестов без запуска потока.""" + now = datetime.now() + now_str = now.strftime("%Y-%m-%d %H:%M:%S") + for r in db.get_due_reminders(now_str): + targets = self._all_admin_ids() if r["recipients"] == "all" else r["recipients"] + body = f"⏰ Напоминание:\n{r['text']}" + for uid in targets: + try: + self.send_message(uid, body) + except Exception as e: + logger.error(f"Не удалось отправить напоминание {r['id']} пользователю {uid}: {e}") + if r["recurrence"] == "once": + db.deactivate_reminder(r["id"]) + else: + anchor = datetime.strptime(r["remind_at"], "%Y-%m-%d %H:%M:%S") + next_dt = compute_next_occurrence(anchor, r["recurrence"], now) + db.update_reminder_next_at(r["id"], next_dt.strftime("%Y-%m-%d %H:%M:%S")) + + def _reminders_loop(self): + """Фоновый цикл: раз в REMINDER_CHECK_INTERVAL_SEC секунд проверяет и шлёт напоминания. + Обрыв на одной итерации (например, сбой отправки) не должен останавливать цикл.""" + while True: + try: + self._check_due_reminders() + except Exception as e: + logger.error(f"Ошибка обработки напоминаний: {e}") + time.sleep(REMINDER_CHECK_INTERVAL_SEC) + def run(self): """Запуск бесконечного цикла прослушивания событий VK LongPoll.""" + threading.Thread(target=self._reminders_loop, daemon=True).start() if not self.longpoll: logger.warning("Бот запущен в режиме ЭМУЛЯЦИИ (без валидных токенов VK API).") print("Симуляция работы бота в консоли. Напишите 'выход' для завершения.")