40
40
import re
41
41
import io
42
42
43
+ from cachetools import TTLCache
43
44
from colorama import init , Fore , Back , Style
44
45
45
46
init ()
@@ -60,6 +61,8 @@ class Modmail(commands.Bot):
60
61
def __init__ (self ):
61
62
super ().__init__ (command_prefix = self .get_pre )
62
63
self .start_time = datetime .datetime .utcnow ()
64
+ self .channel_cache = TTLCache (100 , 30 )
65
+ self .channel_ready_events = {}
63
66
self .loop .create_task (self .data_loop ())
64
67
self ._add_commands ()
65
68
@@ -138,6 +141,7 @@ async def on_connect(self):
138
141
print (Fore .YELLOW + 'Connected to gateway.' )
139
142
140
143
self .session = aiohttp .ClientSession ()
144
+ self .modmail_api = ModmailApiClient (self )
141
145
status = os .getenv ('STATUS' ) or self .config .get ('STATUS' )
142
146
if status :
143
147
await self .change_presence (activity = discord .Game (status ))
@@ -395,8 +399,7 @@ async def github(self, ctx):
395
399
if ctx .invoked_subcommand :
396
400
return
397
401
398
- client = ModmailApiClient (self )
399
- data = await client .get_user_info ()
402
+ data = await self .modmail_api .get_user_info ()
400
403
401
404
prefix = self .config .get ('PREFIX' , 'm.' )
402
405
@@ -416,30 +419,27 @@ async def github(self, ctx):
416
419
@trigger_typing
417
420
async def update (self , ctx ):
418
421
'''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 ()
423
423
424
424
em = discord .Embed (
425
425
title = 'Already up to date' ,
426
426
description = f'The latest version is [`{ __version__ } `](https://github.com/kyb3r/modmail/blob/master/bot.py#L25)' ,
427
427
color = discord .Color .green ()
428
428
)
429
429
430
- if not client .token :
430
+ if not self . modmail_api .token :
431
431
em .title = 'Unauthorised'
432
432
em .description = f"You haven't logged in with github yet. Please get your api token from https://dashboard.modmail.tk"
433
433
em .color = discord .Color .red ()
434
434
return await ctx .send (embed = em )
435
435
436
436
if metadata ['latest_version' ] == __version__ :
437
- data = await client .get_user_info ()
437
+ data = await self . modmail_api .get_user_info ()
438
438
if not data ['error' ]:
439
439
user = data ['user' ]
440
440
em .set_author (name = user ['username' ], icon_url = user ['avatar_url' ], url = user ['url' ])
441
441
else :
442
- data = await client .update_repository ()
442
+ data = await self . modmail_api .update_repository ()
443
443
444
444
commit_data = data ['data' ]
445
445
user = data ['user' ]
@@ -549,6 +549,12 @@ async def _close(self, ctx):
549
549
return await ctx .send ('This is not a modmail thread.' )
550
550
551
551
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
+
552
558
user = self .get_user (user_id )
553
559
em = discord .Embed (title = 'Thread Closed' )
554
560
em .description = f'{ ctx .author .mention } has closed this modmail thread.'
@@ -565,8 +571,7 @@ async def _close(self, ctx):
565
571
categ = discord .utils .get (ctx .guild .categories , name = 'Mod Mail' )
566
572
log_channel = categ .channels [1 ]
567
573
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 , {
570
575
'open' : False , 'closed_at' : str (datetime .datetime .utcnow ()), 'closer' : {
571
576
'id' : str (ctx .author .id ),
572
577
'name' : ctx .author .name ,
@@ -578,12 +583,18 @@ async def _close(self, ctx):
578
583
579
584
log_url = f"https://logs.modmail.tk/{ log_data ['user_id' ]} /{ log_data ['key' ]} "
580
585
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 } )` "
582
587
em = discord .Embed (description = desc , color = em .color )
583
588
em .set_author (name = 'Thread closed' , url = log_url )
584
- em .add_field (name = 'Log URL' , value = f"[`{ log_data ['key' ]} `]({ log_url } )" )
585
589
await log_channel .send (embed = em )
586
590
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
+
587
598
@commands .command ()
588
599
@trigger_typing
589
600
@commands .has_permissions (administrator = True )
@@ -609,7 +620,8 @@ def format_info(self, user, description, log_url, log_count=None):
609
620
avi = user .avatar_url
610
621
time = datetime .datetime .utcnow ()
611
622
desc = description or f'{ user .mention } has started a thread.'
612
- desc += f'\n Log URL: { log_url } '
623
+ key = log_url .split ('/' )[- 1 ]
624
+ desc = f'{ desc } [`{ key } `]({ log_url } )'
613
625
color = discord .Color .blurple ()
614
626
615
627
if member :
@@ -629,12 +641,11 @@ def format_info(self, user, description, log_url, log_count=None):
629
641
em .add_field (name = 'Registered' , value = created + days (created ))
630
642
footer = 'User ID: ' + str (user .id )
631
643
em .set_footer (text = footer )
632
- em .set_thumbnail (url = avi )
633
644
em .set_author (name = str (user ), icon_url = avi )
634
645
635
646
if member :
636
647
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 } ' )
638
649
joined = str ((time - member .joined_at ).days )
639
650
em .add_field (name = 'Joined' , value = joined + days (joined ))
640
651
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):
647
658
return em
648
659
649
660
async def send_mail (self , message , channel , from_mod , delete_message = True ):
650
- client = ModmailApiClient (self )
651
661
if from_mod and not isinstance (channel , discord .User ):
652
662
# ensures logs wont dupe
653
- self .loop .create_task (client .append_log (message ))
663
+ self .loop .create_task (self . modmail_api .append_log (message ))
654
664
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 ))
656
666
657
667
author = message .author
658
668
em = discord .Embed ()
@@ -744,6 +754,9 @@ async def process_modmail(self, message):
744
754
await message .author .send (embed = self .blocked_em )
745
755
else :
746
756
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 ()
747
760
await self .send_mail (message , channel , from_mod = False )
748
761
749
762
@@ -753,7 +766,6 @@ async def find_or_create_thread(self, user, *, creator=None, reopen=False):
753
766
topic = f'User ID: { user .id } '
754
767
channel = discord .utils .get (guild .text_channels , topic = topic )
755
768
categ = discord .utils .get (guild .categories , name = 'Mod Mail' )
756
- archives = discord .utils .get (guild .categories , name = 'Mod Mail Archives' )
757
769
758
770
em = discord .Embed (title = 'Thanks for the message!' )
759
771
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):
773
785
774
786
mention = (self .config .get ('MENTION' ) or '@here' ) if not creator else None
775
787
776
- client = ModmailApiClient (self )
777
-
778
788
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 :
790
795
await user .send (embed = em )
791
796
channel = await guild .create_text_channel (
792
797
name = self .format_name (user , guild .text_channels ),
793
798
category = categ
794
799
)
800
+ self .channel_cache [user .id ] = channel
801
+ self .channel_ready_events [channel .id ] = channel_ready = asyncio .Event ()
795
802
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
+ )
799
806
log_count = len (logs )
800
807
await channel .edit (topic = topic )
801
808
await channel .send (mention , embed = self .format_info (user , info_description , log_url , log_count ))
802
-
809
+ channel_ready .set ()
810
+
803
811
return channel
804
812
805
813
async def find_user_id_from_channel (self , channel ):
814
+
815
+ user_id = None
806
816
async for message in channel .history ():
807
817
if message .embeds :
808
818
em = message .embeds [0 ]
@@ -821,54 +831,45 @@ async def logs(self, ctx, *, member: discord.Member=None):
821
831
user_id = user_id or int (ctx .channel .topic .split (': ' )[1 ])
822
832
823
833
user = member or self .get_user (user_id )
824
-
825
834
826
- client = ModmailApiClient (self )
827
- logs = await client .get_user_logs (user .id )
835
+ logs = await self .modmail_api .get_user_logs (user .id )
828
836
829
837
if not logs :
830
838
return await ctx .send ('This user has no previous logs.' )
831
839
832
- embeds = []
833
-
834
840
em = discord .Embed (color = discord .Color .green ())
835
841
em .set_author (name = 'Previous Logs' , icon_url = user .avatar_url )
836
842
837
- embeds . append ( em )
843
+ embeds = [ em ]
838
844
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' )
840
846
841
847
fmt = ''
842
848
843
849
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 )
844
854
845
855
date = datetime .datetime .fromisoformat (entry ['created_at' ])
846
- new_day = date .strftime ('%d %b %Y' )
856
+ new_day = date .strftime (r '%d %b %Y' )
847
857
848
858
key = entry ['key' ]
849
859
user_id = entry ['user_id' ]
850
860
log_url = f"https://logs.modmail.tk/{ user_id } /{ key } "
851
861
852
- if not entry ['open' ]:
862
+ if not entry ['open' ]: # only list closed threads
853
863
fmt += f"[`{ key } `]({ log_url } )\n "
854
864
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 = ''
864
869
865
- for em in embeds :
866
- print (em .to_dict ())
867
870
868
871
session = PaginatorSession (ctx , * embeds )
869
872
await session .run ()
870
-
871
-
872
873
873
874
@commands .command ()
874
875
@trigger_typing
0 commit comments