add reminder schedule
This commit is contained in:
+71
-1
@@ -1,6 +1,8 @@
|
||||
import asyncio
|
||||
import logging
|
||||
import random
|
||||
from datetime import datetime, timedelta
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from aiogram import Bot
|
||||
|
||||
@@ -15,14 +17,82 @@ QUESTIONS = (
|
||||
)
|
||||
|
||||
|
||||
class ReminderState:
|
||||
def __init__(self) -> None:
|
||||
self.last_manual_entry_at: datetime | None = None
|
||||
self.changed = asyncio.Event()
|
||||
|
||||
def mark_manual_entry(self, timezone: ZoneInfo) -> None:
|
||||
self.last_manual_entry_at = datetime.now(timezone)
|
||||
self.changed.set()
|
||||
|
||||
|
||||
def _is_allowed_time(now: datetime, start_hour: int, end_hour: int) -> bool:
|
||||
return start_hour <= now.hour < end_hour
|
||||
|
||||
|
||||
def _next_window_start(now: datetime, start_hour: int) -> datetime:
|
||||
if now.hour < start_hour:
|
||||
return now.replace(hour=start_hour, minute=0, second=0, microsecond=0)
|
||||
tomorrow = now + timedelta(days=1)
|
||||
return tomorrow.replace(hour=start_hour, minute=0, second=0, microsecond=0)
|
||||
|
||||
|
||||
def _move_to_allowed_time(now: datetime, start_hour: int, end_hour: int) -> datetime:
|
||||
if _is_allowed_time(now, start_hour, end_hour):
|
||||
return now
|
||||
return _next_window_start(now, start_hour)
|
||||
|
||||
|
||||
def _next_reminder_at(
|
||||
now: datetime,
|
||||
state: ReminderState,
|
||||
min_seconds: int,
|
||||
max_seconds: int,
|
||||
start_hour: int,
|
||||
end_hour: int,
|
||||
) -> datetime:
|
||||
if not _is_allowed_time(now, start_hour, end_hour):
|
||||
candidate = _move_to_allowed_time(now, start_hour, end_hour)
|
||||
else:
|
||||
candidate = now + timedelta(seconds=random.randint(min_seconds, max_seconds))
|
||||
|
||||
if state.last_manual_entry_at:
|
||||
candidate = max(candidate, state.last_manual_entry_at + timedelta(hours=1))
|
||||
|
||||
return _move_to_allowed_time(candidate, start_hour, end_hour)
|
||||
|
||||
|
||||
async def send_reminders(
|
||||
bot: Bot,
|
||||
user_ids: set[int],
|
||||
min_seconds: int,
|
||||
max_seconds: int,
|
||||
start_hour: int,
|
||||
end_hour: int,
|
||||
timezone: ZoneInfo,
|
||||
state: ReminderState,
|
||||
) -> None:
|
||||
while True:
|
||||
await asyncio.sleep(random.randint(min_seconds, max_seconds))
|
||||
now = datetime.now(timezone)
|
||||
next_reminder_at = _next_reminder_at(
|
||||
now=now,
|
||||
state=state,
|
||||
min_seconds=min_seconds,
|
||||
max_seconds=max_seconds,
|
||||
start_hour=start_hour,
|
||||
end_hour=end_hour,
|
||||
)
|
||||
sleep_seconds = max(0, (next_reminder_at - now).total_seconds())
|
||||
|
||||
try:
|
||||
await asyncio.wait_for(state.changed.wait(), timeout=sleep_seconds)
|
||||
except TimeoutError:
|
||||
pass
|
||||
else:
|
||||
state.changed.clear()
|
||||
continue
|
||||
|
||||
question = random.choice(QUESTIONS)
|
||||
|
||||
for user_id in user_ids:
|
||||
|
||||
Reference in New Issue
Block a user