| """ |
| Task Scheduler — real asyncio-based delayed execution |
| """ |
| import asyncio, json, datetime, traceback |
| from typing import Callable |
|
|
| _pending: list = [] |
|
|
|
|
| async def schedule_task(delay_seconds: float, desc: str, coro_fn, *args, **kwargs): |
| """Schedule coro_fn(*args, **kwargs) to run after delay_seconds.""" |
| async def runner(): |
| await asyncio.sleep(delay_seconds) |
| try: |
| await coro_fn(*args, **kwargs) |
| except Exception as e: |
| print(f"[SCHEDULER] Task '{desc}' failed: {e}\n{traceback.format_exc()}") |
| task = asyncio.create_task(runner()) |
| _pending.append({"desc": desc, "at": datetime.datetime.now().timestamp() + delay_seconds, "task": task}) |
| print(f"[SCHEDULER] Scheduled '{desc}' in {delay_seconds:.0f}s") |
| return task |
|
|
|
|
| def parse_delay(text: str) -> float | None: |
| """Parse '5 minutes', '30 seconds', '1 hour', '2 mins' → seconds.""" |
| import re |
| text = text.lower().strip() |
| patterns = [ |
| (r'(\d+(?:\.\d+)?)\s*(?:hours?|hrs?|h)\b', 3600), |
| (r'(\d+(?:\.\d+)?)\s*(?:minutes?|mins?|m)\b', 60), |
| (r'(\d+(?:\.\d+)?)\s*(?:seconds?|secs?|s)\b', 1), |
| ] |
| total = 0 |
| found = False |
| for pat, mult in patterns: |
| for m in re.finditer(pat, text): |
| total += float(m.group(1)) * mult |
| found = True |
| return total if found else None |