Skip to content

Commit d558c22

Browse files
committed
Stability improvements using asyncio Events
1 parent c3d1c75 commit d558c22

File tree

2 files changed

+62
-60
lines changed

2 files changed

+62
-60
lines changed

bot.py

Lines changed: 60 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import re
4141
import io
4242

43+
from cachetools import TTLCache
4344
from colorama import init, Fore, Back, Style
4445

4546
init()
@@ -60,6 +61,8 @@ class Modmail(commands.Bot):
6061
def __init__(self):
6162
super().__init__(command_prefix=self.get_pre)
6263
self.start_time = datetime.datetime.utcnow()
64+
self.channel_cache = TTLCache(100, 30)
65+
self.channel_ready_events = {}
6366
self.loop.create_task(self.data_loop())
6467
self._add_commands()
6568

@@ -138,6 +141,7 @@ async def on_connect(self):
138141
print(Fore.YELLOW + 'Connected to gateway.')
139142

140143
self.session = aiohttp.ClientSession()
144+
self.modmail_api = ModmailApiClient(self)
141145
status = os.getenv('STATUS') or self.config.get('STATUS')
142146
if status:
143147
await self.change_presence(activity=discord.Game(status))
@@ -395,8 +399,7 @@ async def github(self, ctx):
395399
if ctx.invoked_subcommand:
396400
return
397401

398-
client = ModmailApiClient(self)
399-
data = await client.get_user_info()
402+
data = await self.modmail_api.get_user_info()
400403

401404
prefix = self.config.get('PREFIX', 'm.')
402405

@@ -416,30 +419,27 @@ async def github(self, ctx):
416419
@trigger_typing
417420
async def update(self, ctx):
418421
'''Updates the bot, this only works with heroku users.'''
419-
420-
client = ModmailApiClient(self)
421-
422-
metadata = await client.get_metadata()
422+
metadata = await self.modmail_api.get_metadata()
423423

424424
em = discord.Embed(
425425
title='Already up to date',
426426
description=f'The latest version is [`{__version__}`](https://github.com/kyb3r/modmail/blob/master/bot.py#L25)',
427427
color=discord.Color.green()
428428
)
429429

430-
if not client.token:
430+
if not self.modmail_api.token:
431431
em.title = 'Unauthorised'
432432
em.description = f"You haven't logged in with github yet. Please get your api token from https://dashboard.modmail.tk"
433433
em.color = discord.Color.red()
434434
return await ctx.send(embed=em)
435435

436436
if metadata['latest_version'] == __version__:
437-
data = await client.get_user_info()
437+
data = await self.modmail_api.get_user_info()
438438
if not data['error']:
439439
user = data['user']
440440
em.set_author(name=user['username'], icon_url=user['avatar_url'], url=user['url'])
441441
else:
442-
data = await client.update_repository()
442+
data = await self.modmail_api.update_repository()
443443

444444
commit_data = data['data']
445445
user = data['user']
@@ -549,6 +549,12 @@ async def _close(self, ctx):
549549
return await ctx.send('This is not a modmail thread.')
550550

551551
user_id = user_id or int(ctx.channel.topic.split(': ')[1])
552+
553+
try:
554+
del self.channel_cache[user_id]
555+
except KeyError:
556+
pass
557+
552558
user = self.get_user(user_id)
553559
em = discord.Embed(title='Thread Closed')
554560
em.description = f'{ctx.author.mention} has closed this modmail thread.'
@@ -565,8 +571,7 @@ async def _close(self, ctx):
565571
categ = discord.utils.get(ctx.guild.categories, name='Mod Mail')
566572
log_channel = categ.channels[1]
567573

568-
client = ModmailApiClient(self)
569-
log_data = await client.post_log(ctx.channel.id, {
574+
log_data = await self.modmail_api.post_log(ctx.channel.id, {
570575
'open': False, 'closed_at': str(datetime.datetime.utcnow()), 'closer': {
571576
'id': str(ctx.author.id),
572577
'name': ctx.author.name,
@@ -578,12 +583,18 @@ async def _close(self, ctx):
578583

579584
log_url = f"https://logs.modmail.tk/{log_data['user_id']}/{log_data['key']}"
580585

581-
desc = f"{ctx.author.mention} closed a thread with {user.mention}"
586+
desc = f"[`{log_data['key']}`]({log_url}) {ctx.author.mention} closed a thread with {user.mention} - `{user} ({user.id})`"
582587
em = discord.Embed(description=desc, color=em.color)
583588
em.set_author(name='Thread closed', url=log_url)
584-
em.add_field(name='Log URL', value=f"[`{log_data['key']}`]({log_url})")
585589
await log_channel.send(embed=em)
586590

591+
@commands.command()
592+
async def nsfw(self, ctx):
593+
if ctx.channel.category and ctx.channel.category.name == 'Mod Mail':
594+
await ctx.edit(nsfw=True)
595+
await ctx.send('Done')
596+
597+
587598
@commands.command()
588599
@trigger_typing
589600
@commands.has_permissions(administrator=True)
@@ -609,7 +620,8 @@ def format_info(self, user, description, log_url, log_count=None):
609620
avi = user.avatar_url
610621
time = datetime.datetime.utcnow()
611622
desc = description or f'{user.mention} has started a thread.'
612-
desc += f'\nLog URL: {log_url}'
623+
key = log_url.split('/')[-1]
624+
desc = f'{desc} [`{key}`]({log_url})'
613625
color = discord.Color.blurple()
614626

615627
if member:
@@ -629,12 +641,11 @@ def format_info(self, user, description, log_url, log_count=None):
629641
em.add_field(name='Registered', value=created + days(created))
630642
footer = 'User ID: '+str(user.id)
631643
em.set_footer(text=footer)
632-
em.set_thumbnail(url=avi)
633644
em.set_author(name=str(user), icon_url=avi)
634645

635646
if member:
636647
if log_count:
637-
em.add_field(name='Past logs', value=f'{log_count}. Use `logs` to view them')
648+
em.add_field(name='Past logs', value=f'{log_count}')
638649
joined = str((time - member.joined_at).days)
639650
em.add_field(name='Joined', value=joined + days(joined))
640651
em.add_field(name='Member No.',value=str(member_number),inline = True)
@@ -647,12 +658,11 @@ def format_info(self, user, description, log_url, log_count=None):
647658
return em
648659

649660
async def send_mail(self, message, channel, from_mod, delete_message=True):
650-
client = ModmailApiClient(self)
651661
if from_mod and not isinstance(channel, discord.User):
652662
# ensures logs wont dupe
653-
self.loop.create_task(client.append_log(message))
663+
self.loop.create_task(self.modmail_api.append_log(message))
654664
elif not from_mod:
655-
self.loop.create_task(client.append_log(message, channel.id))
665+
self.loop.create_task(self.modmail_api.append_log(message, channel.id))
656666

657667
author = message.author
658668
em = discord.Embed()
@@ -744,6 +754,9 @@ async def process_modmail(self, message):
744754
await message.author.send(embed=self.blocked_em)
745755
else:
746756
channel = await self.find_or_create_thread(message.author)
757+
event = self.channel_ready_events.get(channel.id)
758+
if event is not None:
759+
await event.wait()
747760
await self.send_mail(message, channel, from_mod=False)
748761

749762

@@ -753,7 +766,6 @@ async def find_or_create_thread(self, user, *, creator=None, reopen=False):
753766
topic = f'User ID: {user.id}'
754767
channel = discord.utils.get(guild.text_channels, topic=topic)
755768
categ = discord.utils.get(guild.categories, name='Mod Mail')
756-
archives = discord.utils.get(guild.categories, name='Mod Mail Archives')
757769

758770
em = discord.Embed(title='Thanks for the message!')
759771
em.description = 'The moderation team will get back to you as soon as possible!'
@@ -773,36 +785,34 @@ async def find_or_create_thread(self, user, *, creator=None, reopen=False):
773785

774786
mention = (self.config.get('MENTION') or '@here') if not creator else None
775787

776-
client = ModmailApiClient(self)
777-
778788
if channel is not None:
779-
if channel.category is archives: # thread appears to be closed
780-
if creator: await user.send(embed=em)
781-
await channel.edit(category=categ)
782-
log_url, logs = await asyncio.gather(
783-
client.get_log_url(user, channel, creator or user),
784-
client.get_user_logs(user.id)
785-
)
786-
log_count = len(logs)
787-
info_description = info_description or f'{user.mention} has reopened this thread.'
788-
await channel.send(mention, embed=self.format_info(user, info_description, log_url, log_count))
789-
else:
789+
return channel
790+
791+
try:
792+
channel = self.channel_cache[user.id]
793+
return channel
794+
except KeyError:
790795
await user.send(embed=em)
791796
channel = await guild.create_text_channel(
792797
name=self.format_name(user, guild.text_channels),
793798
category=categ
794799
)
800+
self.channel_cache[user.id] = channel
801+
self.channel_ready_events[channel.id] = channel_ready = asyncio.Event()
795802
log_url, logs = await asyncio.gather( # concurrently make requests
796-
client.get_log_url(user, channel, creator or user),
797-
client.get_user_logs(user.id)
798-
)
803+
self.modmail_api.get_log_url(user, channel, creator or user),
804+
self.modmail_api.get_user_logs(user.id)
805+
)
799806
log_count = len(logs)
800807
await channel.edit(topic=topic)
801808
await channel.send(mention, embed=self.format_info(user, info_description, log_url, log_count))
802-
809+
channel_ready.set()
810+
803811
return channel
804812

805813
async def find_user_id_from_channel(self, channel):
814+
815+
user_id = None
806816
async for message in channel.history():
807817
if message.embeds:
808818
em = message.embeds[0]
@@ -821,54 +831,45 @@ async def logs(self, ctx, *, member: discord.Member=None):
821831
user_id = user_id or int(ctx.channel.topic.split(': ')[1])
822832

823833
user = member or self.get_user(user_id)
824-
825834

826-
client = ModmailApiClient(self)
827-
logs = await client.get_user_logs(user.id)
835+
logs = await self.modmail_api.get_user_logs(user.id)
828836

829837
if not logs:
830838
return await ctx.send('This user has no previous logs.')
831839

832-
embeds = []
833-
834840
em = discord.Embed(color=discord.Color.green())
835841
em.set_author(name='Previous Logs', icon_url=user.avatar_url)
836842

837-
embeds.append(em)
843+
embeds = [em]
838844

839-
current_day = datetime.datetime.fromisoformat(logs[0]['created_at']).strftime('%d %b %Y')
845+
current_day = datetime.datetime.fromisoformat(logs[0]['created_at']).strftime(r'%d %b %Y')
840846

841847
fmt = ''
842848

843849
for index, entry in enumerate(logs):
850+
if len(embeds[-1].fields) == 3:
851+
em = discord.Embed(color=discord.Color.green())
852+
em.set_author(name='Previous Logs', icon_url=user.avatar_url)
853+
embeds.append(em)
844854

845855
date = datetime.datetime.fromisoformat(entry['created_at'])
846-
new_day = date.strftime('%d %b %Y')
856+
new_day = date.strftime(r'%d %b %Y')
847857

848858
key = entry['key']
849859
user_id = entry['user_id']
850860
log_url = f"https://logs.modmail.tk/{user_id}/{key}"
851861

852-
if not entry['open']:
862+
if not entry['open']: # only list closed threads
853863
fmt += f"[`{key}`]({log_url})\n"
854864

855-
if current_day != new_day or index == len(logs) - 1:
856-
em.add_field(name=current_day, value=fmt)
857-
current_day = new_day
858-
fmt = ''
859-
860-
if len(em.fields) == 3:
861-
embeds.append(em)
862-
em = discord.Embed(color=discord.Color.green())
863-
em.set_author(name='Previous Logs', icon_url=user.avatar_url)
865+
if current_day != new_day or index == len(logs) - 1:
866+
embeds[-1].add_field(name=current_day, value=fmt)
867+
current_day = new_day
868+
fmt = ''
864869

865-
for em in embeds:
866-
print(em.to_dict())
867870

868871
session = PaginatorSession(ctx, *embeds)
869872
await session.run()
870-
871-
872873

873874
@commands.command()
874875
@trigger_typing

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
git+https://github.com/Rapptz/discord.py@rewrite
2-
colorama
2+
colorama
3+
cachetools

0 commit comments

Comments
 (0)