Напоминалки: гейт часового пояса, конвертация времени, состояние паузы
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -24,7 +24,8 @@ except ImportError:
|
||||
vk_api = None
|
||||
|
||||
from database import DatabaseManager
|
||||
from reminders import parse_reminder_datetime, compute_next_occurrence
|
||||
from reminders import parse_reminder_datetime, compute_next_occurrence, \
|
||||
server_utc_offset_hours, convert_time
|
||||
from datetime import datetime
|
||||
import config
|
||||
import mailer
|
||||
@@ -486,17 +487,60 @@ class VKBotApplication:
|
||||
name = self._vk_name(uid)
|
||||
return f"{name} (vk.com/id{uid})" if name else f"vk.com/id{uid}"
|
||||
|
||||
def _parse_tz_offset(self, raw: str) -> Optional[int]:
|
||||
"""Парсит смещение от UTC вида '+3', '-5', '0'. None, если не число
|
||||
или вне диапазона реальных часовых поясов (-12..+14)."""
|
||||
raw = raw.strip()
|
||||
if not re.match(r"^[+-]?\d{1,2}$", raw):
|
||||
return None
|
||||
val = int(raw)
|
||||
return val if -12 <= val <= 14 else None
|
||||
|
||||
def _require_reminder_tz(self, user_id: int) -> bool:
|
||||
"""Гейт часового пояса: если у админа он ещё не задан — переводит его
|
||||
в мастер ввода пояса и возвращает False (вызывающий должен просто
|
||||
return). Если пояс уже задан — возвращает True, можно продолжать."""
|
||||
if db.get_admin_tz_offset(user_id) is not None:
|
||||
return True
|
||||
db.update_user_state(user_id, "ADMIN_REMINDER_TZ_SETUP")
|
||||
self.send_message(
|
||||
user_id,
|
||||
"🌐 Прежде чем пользоваться напоминалками, укажите свой часовой пояс "
|
||||
"относительно UTC.\nНапример: +3 (Москва), +5 (Екатеринбург), 0 (UTC).",
|
||||
keyboard=get_cancel_admin_keyboard()
|
||||
)
|
||||
return False
|
||||
|
||||
def _handle_set_timezone(self, user_id: int, text: str):
|
||||
"""Команда 'часовой пояс <±N>' — сменить уже заданный пояс в любой момент."""
|
||||
raw = text[len("часовой пояс"):].strip()
|
||||
offset = self._parse_tz_offset(raw)
|
||||
if offset is None:
|
||||
self.send_message(user_id, "⚠️ Формат: часовой пояс +3 (число от -12 до +14).")
|
||||
return
|
||||
db.set_admin_tz_offset(user_id, offset)
|
||||
self.send_message(user_id, f"✅ Часовой пояс сохранён: UTC{offset:+d}.")
|
||||
|
||||
def _send_reminders_admin(self, user_id: int):
|
||||
"""Раздел «Напоминалки»: список активных + инструкции."""
|
||||
"""Раздел «Напоминалки»: список активных + инструкции.
|
||||
Вызывается только после прохождения гейта часового пояса
|
||||
(см. _require_reminder_tz) — тут он гарантированно уже задан."""
|
||||
rows = db.get_active_reminders()
|
||||
viewer_offset = db.get_admin_tz_offset(user_id)
|
||||
if viewer_offset is None:
|
||||
viewer_offset = 0
|
||||
server_offset = server_utc_offset_hours()
|
||||
listing = ""
|
||||
for r in rows:
|
||||
dt = datetime.strptime(r["next_at"], "%Y-%m-%d %H:%M:%S")
|
||||
server_dt = datetime.strptime(r["next_at"], "%Y-%m-%d %H:%M:%S")
|
||||
viewer_dt = convert_time(server_dt, server_offset, viewer_offset)
|
||||
who = "всем админам" if r["recipients"] == "all" else \
|
||||
", ".join(self._admin_display(uid) for uid in r["recipients"])
|
||||
period = REMINDER_PERIOD_LABELS.get(r["recurrence"], r["recurrence"])
|
||||
short = (r["text"][:50] + "…") if len(r["text"]) > 50 else r["text"]
|
||||
listing += (f"🔹 [{r['id']}] {dt.strftime('%d.%m.%Y %H:%M')} ({period}) → {who}\n"
|
||||
pause_mark = "⏸ (на паузе) " if r["paused"] else ""
|
||||
listing += (f"🔹 [{r['id']}] {pause_mark}{viewer_dt.strftime('%d.%m.%Y %H:%M')} "
|
||||
f"(UTC{viewer_offset:+d}, {period}) → {who}\n"
|
||||
f" «{short}»\n")
|
||||
if not listing:
|
||||
listing = "Активных напоминаний нет.\n"
|
||||
@@ -504,7 +548,9 @@ class VKBotApplication:
|
||||
user_id,
|
||||
"⏰ Напоминалки\n\n"
|
||||
f"{listing}\n"
|
||||
"Управляйте кнопками ниже: ➕ добавить • 🗑 удалить",
|
||||
"ℹ️ Чтобы время в списке отображалось верно у всех, каждому нужно один раз "
|
||||
"зайти сюда и указать свой часовой пояс.\n\n"
|
||||
"Управляйте кнопками ниже: ➕ добавить • 🗑 удалить • ⏸/▶ пауза",
|
||||
keyboard=get_reminders_admin_keyboard()
|
||||
)
|
||||
|
||||
@@ -650,9 +696,12 @@ class VKBotApplication:
|
||||
self._send_links_admin(user_id)
|
||||
return
|
||||
elif text_lower in ["⏰ напоминалки", "напоминалки"]:
|
||||
self._send_reminders_admin(user_id)
|
||||
if self._require_reminder_tz(user_id):
|
||||
self._send_reminders_admin(user_id)
|
||||
return
|
||||
elif text_lower == "➕ добавить":
|
||||
if not self._require_reminder_tz(user_id):
|
||||
return
|
||||
db.update_user_state(user_id, "ADMIN_REMINDER_DATE")
|
||||
self.send_message(
|
||||
user_id,
|
||||
@@ -665,6 +714,8 @@ class VKBotApplication:
|
||||
self._handle_reminder_delete(user_id, text_clean)
|
||||
return
|
||||
elif text_lower in ["🗑 удалить напоминание", "удалить напоминание"]:
|
||||
if not self._require_reminder_tz(user_id):
|
||||
return
|
||||
rows = db.get_active_reminders()
|
||||
if not rows:
|
||||
self.send_message(user_id, "Активных напоминаний нет.")
|
||||
@@ -675,6 +726,25 @@ class VKBotApplication:
|
||||
self.send_message(user_id, "🗑 Какое напоминание удалить? Выберите:",
|
||||
keyboard=get_pick_keyboard(items))
|
||||
return
|
||||
elif text_lower in ["⏸/▶ пауза", "пауза"]:
|
||||
if not self._require_reminder_tz(user_id):
|
||||
return
|
||||
rows = db.get_active_reminders()
|
||||
if not rows:
|
||||
self.send_message(user_id, "Активных напоминаний нет.")
|
||||
self._send_reminders_admin(user_id)
|
||||
return
|
||||
items = [
|
||||
(r["id"], ("▶ Возобновить: " if r["paused"] else "⏸ На паузу: ") + r["text"])
|
||||
for r in rows
|
||||
]
|
||||
db.update_user_state(user_id, "ADMIN_REMINDER_PAUSE_PICK")
|
||||
self.send_message(user_id, "⏸/▶ Какое напоминание переключить? Выберите:",
|
||||
keyboard=get_pick_keyboard(items))
|
||||
return
|
||||
elif text_lower.startswith("часовой пояс"):
|
||||
self._handle_set_timezone(user_id, text_clean)
|
||||
return
|
||||
elif text_lower in ["уведомления: вкл 🔔", "уведомления: выкл 🔕", "уведомления", "уведомления 🔔", "уведомления 🔕"]:
|
||||
self._toggle_admin_notifications(user_id)
|
||||
return
|
||||
@@ -1709,6 +1779,18 @@ class VKBotApplication:
|
||||
return
|
||||
|
||||
# ---------- Напоминалки ----------
|
||||
if state == "ADMIN_REMINDER_TZ_SETUP":
|
||||
offset = self._parse_tz_offset(text)
|
||||
if offset is None:
|
||||
self.send_message(user_id, "⚠️ Формат: число от -12 до +14, например +3 или -5.",
|
||||
keyboard=get_cancel_admin_keyboard())
|
||||
return
|
||||
db.set_admin_tz_offset(user_id, offset)
|
||||
db.update_user_state(user_id, "FREE_CHAT")
|
||||
self.send_message(user_id, f"✅ Часовой пояс сохранён: UTC{offset:+d}.")
|
||||
self._send_reminders_admin(user_id)
|
||||
return
|
||||
|
||||
if state == "ADMIN_REMINDER_DEL_PICK":
|
||||
rid = pick_id(text)
|
||||
if not rid.isdigit() or not db.delete_reminder(int(rid)):
|
||||
@@ -1719,17 +1801,38 @@ class VKBotApplication:
|
||||
self._send_reminders_admin(user_id)
|
||||
return
|
||||
|
||||
if state == "ADMIN_REMINDER_PAUSE_PICK":
|
||||
rid = pick_id(text)
|
||||
rows_by_id = {r["id"]: r for r in db.get_active_reminders()}
|
||||
if not rid.isdigit() or int(rid) not in rows_by_id:
|
||||
self.send_message(user_id, "⚠️ Выберите напоминание кнопкой ниже.")
|
||||
return
|
||||
new_paused = not rows_by_id[int(rid)]["paused"]
|
||||
db.set_reminder_paused(int(rid), new_paused)
|
||||
db.update_user_state(user_id, "FREE_CHAT")
|
||||
self.send_message(
|
||||
user_id,
|
||||
f"✅ Напоминание №{rid} " +
|
||||
("поставлено на паузу." if new_paused else "снято с паузы.")
|
||||
)
|
||||
self._send_reminders_admin(user_id)
|
||||
return
|
||||
|
||||
if state == "ADMIN_REMINDER_DATE":
|
||||
dt = parse_reminder_datetime(text)
|
||||
if not dt:
|
||||
self.send_message(user_id, "⚠️ Формат: ДД.ММ.ГГГГ ЧЧ:ММ (например, 15.03.2027 09:00)",
|
||||
keyboard=get_cancel_admin_keyboard())
|
||||
return
|
||||
if dt <= datetime.now():
|
||||
admin_offset = db.get_admin_tz_offset(user_id)
|
||||
if admin_offset is None:
|
||||
admin_offset = 0
|
||||
server_dt = convert_time(dt, admin_offset, server_utc_offset_hours())
|
||||
if server_dt <= datetime.now():
|
||||
self.send_message(user_id, "⚠️ Дата и время должны быть в будущем.",
|
||||
keyboard=get_cancel_admin_keyboard())
|
||||
return
|
||||
self._admin_ctx[user_id] = {"remind_at": dt.strftime("%Y-%m-%d %H:%M:%S")}
|
||||
self._admin_ctx[user_id] = {"remind_at": server_dt.strftime("%Y-%m-%d %H:%M:%S")}
|
||||
db.update_user_state(user_id, "ADMIN_REMINDER_TEXT")
|
||||
self.send_message(user_id, "✏️ Введите текст напоминания:", keyboard=get_cancel_admin_keyboard())
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user