async def create_timer(bot: commands.Bot, expires_at: datetime.datetime, event_type: str, *args, **kwargs) -> Timer: now = datetime.datetime.utcnow() timer = Timer.temporary(now, expires_at, event_type, *args, **kwargs) delta = (expires_at - now).total_seconds() if delta <= 60: bot.loop.create_task(timer.call(bot)) return timer record = await Timers.insert(returning=Timers.id, expires_at=expires_at, event_type=event_type, data=dict(args=args, kwargs=kwargs)) # Set the timer's ID timer.id = record[0] # Only set the data check if the timer can be waited for if delta <= discord.utils.MAX_ASYNCIO_SECONDS: bot._active_timer.set() # Check if the timer is earlier than the currently set timer if bot._current_timer is not None and expires_at < bot._current_timer.expires_at: bot._timer_task.cancel() bot._timer_task = bot.loop.create_task(dispatch_timers(bot)) return timer
async def dispatch_timers(bot: commands.Bot): # Wait until bot is ready await bot.wait_until_ready() try: while not bot.is_closed(): # fetch the next timer from the database timer = bot._current_timer = await _wait_for_active(bot, days=40) now = datetime.datetime.utcnow() # if timer has not yet expired if timer.expires_at >= now: await discord.utils.sleep_until(timer.expires_at) await timer.call(bot) except asyncio.CancelledError: pass except (OSError, discord.ConnectionClosed, asyncpg.PostgresConnectionError): bot._timer_task.cancel() bot._timer_task = bot.loop.create_task(dispatch_timers(bot)) except Exception as exc: print('Unhandled exception in internal timer task', file=sys.stderr) traceback.print_exception(type(exc), exc, exc.__traceback__, file=sys.stderr)
async def delete_timer(bot: commands.Bot, record: asyncpg.Record, *, connection: asyncpg.Connection): await Timers.delete(id=record['id']) # if the current timer is being deleted skip it if bot._current_timer and bot._current_timer.id == record['id']: bot._timer_task.cancel() bot._timer_task = bot.loop.create_task(dispatch_timers(bot))