94 lines
3.0 KiB
Python
94 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
# utils.py
|
|
|
|
from datetime import datetime, date
|
|
from typing import List, Optional
|
|
from zoneinfo import ZoneInfo
|
|
|
|
from telegram import ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton
|
|
from config import BTN_ADD, BTN_LIST, BTN_EDIT, BTN_DELETE, BTN_HISTORY, BTN_TZ, BTN_CANCEL, TZ_SUGGESTIONS
|
|
|
|
# ----------------- UI HELPERS -----------------
|
|
def main_menu_kb():
|
|
kb = [
|
|
[BTN_ADD, BTN_LIST],
|
|
[BTN_EDIT, BTN_DELETE],
|
|
[BTN_HISTORY, BTN_TZ]
|
|
]
|
|
return ReplyKeyboardMarkup(kb, resize_keyboard=True, one_time_keyboard=False)
|
|
|
|
def tz_keyboard():
|
|
kb = [[tz] for tz in TZ_SUGGESTIONS]
|
|
kb.append(["Ввести вручную"])
|
|
kb.append([BTN_CANCEL])
|
|
return ReplyKeyboardMarkup(kb, one_time_keyboard=True, resize_keyboard=True)
|
|
|
|
def build_wd_keyboard(wd_sel: set):
|
|
labels = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"]
|
|
kb = []
|
|
row = []
|
|
for i, lab in enumerate(labels):
|
|
prefix = "✅" if i in wd_sel else "◻️"
|
|
row.append(InlineKeyboardButton(f"{prefix} {lab}", callback_data=f"wd_toggle:{i}"))
|
|
if (i + 1) % 3 == 0:
|
|
kb.append(row)
|
|
row = []
|
|
if row:
|
|
kb.append(row)
|
|
kb.append([
|
|
InlineKeyboardButton("Готово", callback_data="wd_done"),
|
|
InlineKeyboardButton("Отмена", callback_data="wd_cancel")
|
|
])
|
|
return InlineKeyboardMarkup(kb)
|
|
|
|
# ----------------- DATA HELPERS -----------------
|
|
def parse_hhmm(s: str) -> Optional[str]:
|
|
s = s.strip()
|
|
try:
|
|
dt = datetime.strptime(s, "%H:%M")
|
|
return dt.strftime("%H:%M")
|
|
except ValueError:
|
|
return None
|
|
|
|
def weekdays_list_to_csv(lst: List[int]) -> str:
|
|
return ",".join(str(i) for i in sorted(set(lst))) if lst else ""
|
|
|
|
def weekdays_csv_to_list(csv: Optional[str]) -> List[int]:
|
|
if not csv:
|
|
return []
|
|
return [int(x) for x in csv.split(",") if x.isdigit()]
|
|
|
|
def now_utc() -> datetime:
|
|
return datetime.utcnow()
|
|
|
|
def utc_str_min(dt: datetime) -> str:
|
|
return dt.strftime("%Y-%m-%d %H:%M")
|
|
|
|
def utc_str_full(dt: datetime) -> str:
|
|
return dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
def localized_now_for_tz(tzname: str) -> datetime:
|
|
try:
|
|
tz = ZoneInfo(tzname)
|
|
except Exception:
|
|
tz = ZoneInfo("UTC")
|
|
return datetime.now(tz)
|
|
|
|
def local_time_hhmm_for_user(tzname: str, when_utc: datetime) -> str:
|
|
try:
|
|
tz = ZoneInfo(tzname)
|
|
except Exception:
|
|
tz = ZoneInfo("UTC")
|
|
local = when_utc.astimezone(tz)
|
|
return local.strftime("%H:%M")
|
|
|
|
def user_local_hhmm_to_utc_dt(tzname: str, local_date: date, hhmm: str) -> datetime:
|
|
hour, minute = map(int, hhmm.split(":"))
|
|
local_dt = datetime(local_date.year, local_date.month, local_date.day, hour, minute)
|
|
try:
|
|
tz = ZoneInfo(tzname)
|
|
local_dt = local_dt.replace(tzinfo=tz)
|
|
except Exception:
|
|
tz = ZoneInfo("UTC")
|
|
local_dt = local_dt.replace(tzinfo=tz)
|
|
return local_dt.astimezone(ZoneInfo("UTC")).replace(tzinfo=None) |