"""JARVIS reminders.""" import asyncio import logging from datetime import datetime, timedelta, timezone from typing import Optional from jarvis_core.db import q from jarvis_core.db.models import Reminder from naff import Client from naff.models.discord.channel import GuildText from naff.models.discord.embed import Embed from naff.models.discord.user import User from jarvis_tasks.prometheus.stats import reminder_count from jarvis_tasks.util import build_embed, runat queue = [] logger = logging.getLogger(__name__) async def _remind( user: User, reminder: Reminder, embed: Embed, channel: Optional[GuildText] = None, ) -> None: delete = True reminded = False try: await user.send(embed=embed) reminded = True logger.debug(f"Reminder {reminder.id} sent to user") except Exception: logger.debug("Failed to DM user, falling back to channel") if channel: member = await channel.guild.fetch_member(user.id) if not member: logger.debug("User no longer in origin guild") else: if channel and not reminder.private: await channel.send(f"{member.mention}", embed=embed) reminded = True logger.debug(f"Reminder {reminder.id} sent to origin channel") elif channel: await channel.send( f"{member.mention}, you had a private reminder set for now," " but I couldn't send it to you.\n" f"Use `/reminder fetch {str(reminder.id)}` to view" ) reminded = True logger.debug( f"Reminder {reminder.id} private, sent notification to origin channel" ) reminder.active = False await reminder.commit() delete = False else: logger.warning(f"Reminder {reminder.id} failed, no way to contact user.") if delete: await reminder.delete() if reminded: count = reminder_count.labels(guild_id=channel.guild.id, guild_name=channel.guild.name) count.inc() queue.remove(reminder.id) async def remind(bot: Client) -> None: """ Run reminders in the background. Args: bot: Client instance """ logger.debug("Starting Task-remind") while True: max_ts = datetime.now(tz=timezone.utc) + timedelta(seconds=5) reminders = Reminder.find(q(id__nin=queue, remind_at__lte=max_ts, active=True)) async for reminder in reminders: if reminder.id in queue: logger.debug(f"Reminder {reminder.id} was found despite filter") continue user = await bot.fetch_user(reminder.user) if not user: logger.warning(f"Failed to get user with ID {reminder.user}") await reminder.delete() continue embed = build_embed( title="You have a reminder!", description=reminder.message, fields=[] ) embed.set_author( name=user.username + "#" + user.discriminator, icon_url=user.avatar.url ) embed.set_thumbnail(url=user.avatar.url) channel = await bot.fetch_channel(reminder.channel) coro = _remind(user, reminder, embed, channel) asyncio.create_task(runat(reminder.remind_at, coro, logger)) queue.append(reminder.id) # Check every 5 seconds await asyncio.sleep(5)