8
8
from discord .ext .commands import UserInputError , CommandError
9
9
10
10
from core .models import Bot , ThreadManagerABC , ThreadABC
11
- from core .utils import is_image_url , days , match_user_id , truncate
11
+ from core .utils import is_image_url , days , match_user_id , truncate , ignore
12
12
13
13
14
14
class Thread (ThreadABC ):
15
15
"""Represents a discord Modmail thread"""
16
16
17
17
def __init__ (self , manager : 'ThreadManager' ,
18
- recipient : typing .Union [discord .Member , discord .User ,
19
- int ],
18
+ recipient : typing .Union [discord .Member , discord .User , int ],
20
19
channel : typing .Union [discord .DMChannel ,
21
- discord .TextChannel ]):
20
+ discord .TextChannel ] = None ):
22
21
self .manager = manager
23
22
self .bot = manager .bot
24
23
if isinstance (recipient , int ):
@@ -72,6 +71,75 @@ def ready(self, flag):
72
71
else :
73
72
self ._ready_event .clear ()
74
73
74
+ async def setup (self , * , creator = None , category = None ):
75
+ """Create the thread channel and other io related initialisation tasks"""
76
+
77
+ recipient = self .recipient
78
+
79
+ # in case it creates a channel outside of category
80
+ overwrites = {
81
+ self .bot .modmail_guild .default_role :
82
+ discord .PermissionOverwrite (read_messages = False )
83
+ }
84
+
85
+ category = category or self .bot .main_category
86
+
87
+ if category is not None :
88
+ overwrites = None
89
+
90
+ channel = await self .bot .modmail_guild .create_text_channel (
91
+ name = self .manager ._format_channel_name (recipient ),
92
+ category = category ,
93
+ overwrites = overwrites ,
94
+ reason = 'Creating a thread channel'
95
+ )
96
+
97
+ self ._channel = channel
98
+
99
+ log_url , log_data = await asyncio .gather (
100
+ self .bot .api .create_log_entry (recipient , channel ,
101
+ creator or recipient ),
102
+ self .bot .api .get_user_logs (recipient .id )
103
+ )
104
+
105
+ log_count = sum (1 for log in log_data if not log ['open' ])
106
+ info_embed = self .manager ._format_info_embed (recipient , log_url ,
107
+ log_count ,
108
+ discord .Color .green ())
109
+
110
+ topic = f'User ID: { recipient .id } '
111
+ if creator :
112
+ mention = None
113
+ else :
114
+ mention = self .bot .config .get ('mention' , '@here' )
115
+
116
+ _ , msg = await asyncio .gather (
117
+ channel .edit (topic = topic ),
118
+ channel .send (mention , embed = info_embed )
119
+ )
120
+
121
+ self .ready = True
122
+
123
+ # Once thread is ready, tell the recipient.
124
+ thread_creation_response = self .bot .config .get (
125
+ 'thread_creation_response' ,
126
+ 'The moderation team will get back to you as soon as possible!'
127
+ )
128
+
129
+ embed = discord .Embed (
130
+ color = self .bot .mod_color ,
131
+ description = thread_creation_response ,
132
+ timestamp = datetime .utcnow (),
133
+ )
134
+ embed .set_footer (text = 'Your message has been sent' ,
135
+ icon_url = self .bot .guild .icon_url )
136
+ embed .set_author (name = 'Thread Created' )
137
+
138
+ if creator is None :
139
+ self .bot .loop .create_task (recipient .send (embed = embed ))
140
+
141
+ await msg .pin ()
142
+
75
143
def _close_after (self , closer , silent , delete_channel , message ):
76
144
return self .bot .loop .create_task (
77
145
self ._close (closer , silent , delete_channel , message , True )
@@ -144,7 +212,7 @@ async def _close(self, closer, silent=False, delete_channel=True,
144
212
sneak_peak = 'No content'
145
213
146
214
desc = f"[`{ log_data ['key' ]} `]({ log_url } ): "
147
- desc += truncate (sneak_peak , max = 75 - 13 )
215
+ desc += truncate (sneak_peak , max = 75 - 13 )
148
216
else :
149
217
desc = "Could not resolve log url."
150
218
@@ -231,33 +299,45 @@ async def reply(self, message, anonymous=False):
231
299
if not message .content and not message .attachments :
232
300
raise UserInputError
233
301
if all (not g .get_member (self .id ) for g in self .bot .guilds ):
234
- await message .channel .send (
302
+ return await message .channel .send (
235
303
embed = discord .Embed (
236
304
color = discord .Color .red (),
237
- description = 'This user shares no servers with '
238
- 'me and is thus unreachable.'
305
+ description = 'Your message could not be delivered since'
306
+ 'the recipient shares no servers with the bot'
307
+ ))
308
+
309
+ tasks = []
310
+
311
+ try :
312
+ await self .send (message ,
313
+ destination = self .recipient ,
314
+ from_mod = True ,
315
+ anonymous = anonymous )
316
+ except Exception as e :
317
+ print (e )
318
+ tasks .append (message .channel .send (
319
+ embed = discord .Embed (
320
+ color = discord .Color .red (),
321
+ description = 'Your message could not be delivered because '
322
+ 'the recipient is only accepting direct '
323
+ 'messages from friends, or the bot was '
324
+ 'blocked by the recipient.'
239
325
)
326
+ ))
327
+ else :
328
+ # Send the same thing in the thread channel.
329
+ tasks .append (
330
+ self .send (message ,
331
+ destination = self .channel ,
332
+ from_mod = True ,
333
+ anonymous = anonymous )
240
334
)
241
- return
242
335
243
- tasks = [
244
- # in thread channel
245
- self .send (message ,
246
- destination = self .channel ,
247
- from_mod = True ,
248
- anonymous = anonymous ),
249
- # to user
250
- self .send (message ,
251
- destination = self .recipient ,
252
- from_mod = True ,
253
- anonymous = anonymous )
254
- ]
255
-
256
- await self .bot .api .append_log (
257
- message ,
258
- self .channel .id ,
259
- type_ = 'anonymous' if anonymous else 'thread_message'
260
- )
336
+ tasks .append (
337
+ self .bot .api .append_log (message ,
338
+ self .channel .id ,
339
+ type_ = 'anonymous' if anonymous else 'thread_message'
340
+ ))
261
341
262
342
if self .close_task is not None :
263
343
# cancel closing if a thread message is sent.
@@ -277,14 +357,18 @@ async def send(self, message, destination=None,
277
357
from_mod = False , note = False , anonymous = False ):
278
358
if self .close_task is not None :
279
359
# cancel closing if a thread message is sent.
280
- await self .cancel_closure ()
281
- await self .channel .send (embed = discord .Embed (
282
- color = discord .Color .red (),
283
- description = 'Scheduled close has been cancelled.'
284
- ))
360
+ tasks = asyncio .gather (
361
+ self .cancel_closure (),
362
+ self .channel .send (embed = discord .Embed (
363
+ color = discord .Color .red (),
364
+ description = 'Scheduled close has been cancelled.'
365
+ )))
366
+ self .bot .loop .create_task (tasks )
285
367
286
368
if not from_mod and not note :
287
- await self .bot .api .append_log (message , self .channel .id )
369
+ self .bot .loop .create_task (
370
+ self .bot .api .append_log (message , self .channel .id )
371
+ )
288
372
289
373
if not self .ready :
290
374
await self .wait_until_ready ()
@@ -410,17 +494,13 @@ async def send(self, message, destination=None,
410
494
mentions = None
411
495
412
496
await destination .send (mentions , embed = embed )
413
-
414
497
if additional_images :
415
- self .ready = False
416
- await asyncio .gather (* additional_images )
417
498
self .ready = True
499
+ await asyncio .gather (* additional_images )
500
+ self .ready = False
418
501
419
502
if delete_message :
420
- try :
421
- await message .delete ()
422
- except discord .HTTPException :
423
- pass
503
+ self .bot .loop .create_task (ignore (message .delete ()))
424
504
425
505
def get_notifications (self ):
426
506
config = self .bot .config
@@ -519,75 +599,21 @@ async def _find_from_channel(self, channel):
519
599
520
600
return thread
521
601
522
- async def create (self , recipient , * , creator = None , category = None ):
602
+ def create (self , recipient , * , creator = None , category = None ):
523
603
"""Creates a Modmail thread"""
524
-
525
- thread_creation_response = self .bot .config .get (
526
- 'thread_creation_response' ,
527
- 'The moderation team will get back to you as soon as possible!'
528
- )
529
-
530
- embed = discord .Embed (
531
- color = self .bot .mod_color ,
532
- description = thread_creation_response ,
533
- timestamp = datetime .utcnow (),
534
- )
535
- embed .set_footer (text = 'Your message has been sent' ,
536
- icon_url = self .bot .guild .icon_url )
537
- embed .set_author (name = 'Thread Created' )
538
-
539
- if creator is None :
540
- self .bot .loop .create_task (recipient .send (embed = embed ))
541
-
542
- # in case it creates a channel outside of category
543
- overwrites = {
544
- self .bot .modmail_guild .default_role :
545
- discord .PermissionOverwrite (read_messages = False )
546
- }
547
-
548
- category = category or self .bot .main_category
549
-
550
- if category is not None :
551
- overwrites = None
552
-
553
- channel = await self .bot .modmail_guild .create_text_channel (
554
- name = self ._format_channel_name (recipient ),
555
- category = category ,
556
- overwrites = overwrites ,
557
- reason = 'Creating a thread channel'
558
- )
559
-
560
- thread = Thread (self , recipient , channel )
604
+ # create thread immediately so messages can be processed
605
+ thread = Thread (self , recipient )
561
606
self .cache [recipient .id ] = thread
562
607
563
- log_url , log_data = await asyncio .gather (
564
- self .bot .api .create_log_entry (recipient , channel ,
565
- creator or recipient ),
566
- self .bot .api .get_user_logs (recipient .id )
567
- )
568
-
569
- log_count = sum (1 for log in log_data if not log ['open' ])
570
- info_embed = self ._format_info_embed (recipient , log_url , log_count ,
571
- discord .Color .green ())
572
-
573
- topic = f'User ID: { recipient .id } '
574
- if creator :
575
- mention = None
576
- else :
577
- mention = self .bot .config .get ('mention' , '@here' )
578
-
579
- _ , msg = await asyncio .gather (
580
- channel .edit (topic = topic ),
581
- channel .send (mention , embed = info_embed )
582
- )
583
-
584
- thread .ready = True
585
- await msg .pin ()
608
+ # Schedule thread setup for later
609
+ self .bot .loop .create_task (thread .setup (
610
+ creator = creator ,
611
+ category = category
612
+ ))
586
613
return thread
587
614
588
615
async def find_or_create (self , recipient ):
589
- return await self .find (recipient = recipient ) or \
590
- await self .create (recipient )
616
+ return await self .find (recipient = recipient ) or self .create (recipient )
591
617
592
618
def _format_channel_name (self , author ):
593
619
"""Sanitises a username for use with text channel names"""
0 commit comments