init
This commit is contained in:
64
assistant_bot/reminders.py
Normal file
64
assistant_bot/reminders.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import re
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from .models import ReminderParseResult
|
||||
from .time_utils import utc_now
|
||||
|
||||
|
||||
def unit_to_timedelta(amount: int, unit: str) -> timedelta | None:
|
||||
normalized = unit.lower().strip(".")
|
||||
if normalized in {"s", "sec", "secs", "second", "seconds", "с", "сек", "секунд"}:
|
||||
return timedelta(seconds=amount)
|
||||
if normalized in {"m", "min", "mins", "minute", "minutes", "м"} or normalized.startswith("мин"):
|
||||
return timedelta(minutes=amount)
|
||||
if normalized in {"h", "hr", "hrs", "hour", "hours", "ч"} or normalized.startswith("час"):
|
||||
return timedelta(hours=amount)
|
||||
if normalized in {"d", "day", "days", "д"} or normalized.startswith(("дн", "ден")):
|
||||
return timedelta(days=amount)
|
||||
return None
|
||||
|
||||
|
||||
def parse_reminder(raw_text: str, tz: ZoneInfo) -> ReminderParseResult | None:
|
||||
text = raw_text.strip()
|
||||
if not text:
|
||||
return None
|
||||
|
||||
relative_match = re.match(r"^(?:через\s+)?(\d+)\s*([a-zа-я.]+)\s+(.+)$", text, re.IGNORECASE)
|
||||
if relative_match:
|
||||
amount = int(relative_match.group(1))
|
||||
delta = unit_to_timedelta(amount, relative_match.group(2))
|
||||
reminder_text = relative_match.group(3).strip()
|
||||
if delta and reminder_text:
|
||||
return ReminderParseResult(utc_now() + delta, reminder_text)
|
||||
|
||||
now_local = datetime.now(tz)
|
||||
absolute_patterns = (
|
||||
(r"^(\d{4}-\d{2}-\d{2})\s+(\d{1,2}:\d{2})\s+(.+)$", "%Y-%m-%d %H:%M"),
|
||||
(r"^(\d{1,2}\.\d{1,2}\.\d{4})\s+(\d{1,2}:\d{2})\s+(.+)$", "%d.%m.%Y %H:%M"),
|
||||
)
|
||||
|
||||
for pattern, date_format in absolute_patterns:
|
||||
match = re.match(pattern, text)
|
||||
if not match:
|
||||
continue
|
||||
|
||||
naive = datetime.strptime(f"{match.group(1)} {match.group(2)}", date_format)
|
||||
remind_at = naive.replace(tzinfo=tz)
|
||||
reminder_text = match.group(3).strip()
|
||||
if reminder_text:
|
||||
return ReminderParseResult(remind_at.astimezone(timezone.utc), reminder_text)
|
||||
|
||||
time_match = re.match(r"^(\d{1,2}:\d{2})\s+(.+)$", text)
|
||||
if time_match:
|
||||
hour, minute = (int(part) for part in time_match.group(1).split(":", 1))
|
||||
if 0 <= hour <= 23 and 0 <= minute <= 59:
|
||||
remind_at = now_local.replace(hour=hour, minute=minute, second=0, microsecond=0)
|
||||
if remind_at <= now_local:
|
||||
remind_at += timedelta(days=1)
|
||||
reminder_text = time_match.group(2).strip()
|
||||
if reminder_text:
|
||||
return ReminderParseResult(remind_at.astimezone(timezone.utc), reminder_text)
|
||||
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user