feat(schedule): Add DM notifications.

This commit is contained in:
2023-09-30 21:38:28 +03:00
parent 1f92957393
commit 4828e7bf8b
2 changed files with 99 additions and 2 deletions

View File

@@ -1,3 +1,4 @@
from random import random
from typing import Optional from typing import Optional
import datetime as dt import datetime as dt
import asyncio import asyncio
@@ -335,12 +336,12 @@ class ScheduledSession:
self.opened = True self.opened = True
@log_wrap(action='Notify') @log_wrap(action='Notify')
async def _notify(self, wait=60): async def _notify(self, ping_wait=10, dm_wait=60):
""" """
Ghost ping members who have not yet attended. Ghost ping members who have not yet attended.
""" """
try: try:
await asyncio.sleep(wait) await asyncio.sleep(ping_wait)
except asyncio.CancelledError: except asyncio.CancelledError:
return return
missing = [mid for mid, m in self.members.items() if m.total_clock == 0 and m.clock_start is None] missing = [mid for mid, m in self.members.items() if m.total_clock == 0 and m.clock_start is None]
@@ -349,6 +350,90 @@ class ScheduledSession:
message = await self.send(ping) message = await self.send(ping)
if message is not None: if message is not None:
asyncio.create_task(message.delete()) asyncio.create_task(message.delete())
try:
# Random dither to spread out sharded notifications
dither = 30 * random()
await asyncio.sleep(dm_wait - ping_wait + dither)
except asyncio.CancelledError:
return
if not self.guild:
# In case we somehow left the guild in the meantime
return
missing = [mid for mid, m in self.members.items() if m.total_clock == 0 and m.clock_start is None]
for mid in missing:
member = self.guild.get_member(mid)
if member:
args = await self._notify_dm(member)
try:
await member.send(**args.send_args)
except discord.HTTPException:
# Discord really doesn't like failed DM requests
# So take a moment of silence
await asyncio.sleep(1)
async def _notify_dm(self, member: discord.Member) -> MessageArgs:
t = self.bot.translator.t
# Join line depends on guild setup
channels = self.channels_setting.value
room = self.room_channel
if room:
if room.type is discord.enums.ChannelType.category:
join_line = t(_p(
'session|notify|dm|join_line:room_category',
"Please attend your session by joining a voice channel under **{room}**!"
)).format(room=room.name)
else:
join_line = t(_p(
'session|notify|dm|join_line:room_voice',
"Please attend your session by joining {room}"
)).format(room=room.mention)
elif not channels:
join_line = t(_p(
'session|notify|dm|join_line:all_channels',
"Please attend your session by joining a tracked voice channel!"
))
else:
# Expand channels into a list of valid voice channels
voice_channels = set()
for channel in channels:
if channel.type is discord.enums.ChannelType.category:
voice_channels.update(channel.voice_channels)
elif channel.type is discord.enums.ChannelType.voice:
voice_channels.add(channel)
# Now filter by connectivity and tracked
voice_tracker = self.bot.get_cog('VoiceTrackerCog')
valid = []
for channel in voice_channels:
if voice_tracker.is_untracked(channel):
continue
if not channel.permissions_for(member).connect:
continue
valid.append(channel)
join_line = t(_p(
'session|notify|dm|join_line:channels',
"Please attend your session by joining one of the following:"
))
join_line = '\n'.join(join_line, *(channel.mention for channel in valid[:20]))
if len(valid) > 20:
join_line += '\n...'
embed = discord.Embed(
colour=discord.Colour.orange(),
title=t(_p(
'session|notify|dm|title',
"Your Scheduled Session has started!"
)),
description=t(_p(
'session|notify|dm|description',
"Your scheduled session in {dest} has now begun!"
)).format(
dest=self.lobby_channel.mention if self.lobby_channel else f"**{self.guild.name}**"
) + '\n' + join_line
)
return MessageArgs(embed=embed)
def notify(self): def notify(self):
""" """

View File

@@ -79,6 +79,18 @@ class VoiceTrackerCog(LionCog):
""" """
return VoiceSession.get(self.bot, guildid, userid, **kwargs) return VoiceSession.get(self.bot, guildid, userid, **kwargs)
def is_untracked(self, channel) -> bool:
if not channel.guild:
raise ValueError("Untracked check invalid for private channels.")
untracked = self.untracked_channels.get(channel.guild.id, ())
if channel.id in untracked:
untracked = True
elif channel.category_id and channel.category_id in untracked:
untracked = True
else:
untracked = False
return untracked
@LionCog.listener('on_ready') @LionCog.listener('on_ready')
@log_wrap(action='Init Voice Sessions') @log_wrap(action='Init Voice Sessions')
async def initialise(self): async def initialise(self):