@@ -26,7 +26,8 @@ def __init__(self, manager, recipient):
26
26
self .close_task = None
27
27
28
28
def __repr__ (self ):
29
- return f'Thread(recipient="{ self .recipient } ", channel={ self .channel .id } )'
29
+ return f'Thread(recipient="{ self .recipient } ", ' \
30
+ f'channel={ self .channel .id } )'
30
31
31
32
def wait_until_ready (self ):
32
33
"""Blocks execution until the thread is fully set up."""
@@ -42,28 +43,35 @@ def ready(self, flag):
42
43
self .ready_event .set ()
43
44
44
45
def _close_after (self , closer , silent , delete_channel , message ):
45
- return self .bot .loop .create_task (self ._close (closer , silent , delete_channel , message , True ))
46
+ return self .bot .loop .create_task (
47
+ self ._close (closer , silent , delete_channel , message , True ))
46
48
47
- async def close (self , * , closer , after = 0 , silent = False , delete_channel = True , message = None ):
48
- '''Close a thread now or after a set time in seconds'''
49
+ async def close (self , * , closer , after = 0 , silent = False ,
50
+ delete_channel = True , message = None ):
51
+ """Close a thread now or after a set time in seconds"""
49
52
50
53
if self .close_task is not None :
51
54
self .close_task .cancel ()
52
55
53
56
if after > 0 :
54
- self .close_task = self .bot .loop .call_later (after , self ._close_after , closer , silent , delete_channel , message )
57
+ self .close_task = self .bot .loop .call_later (
58
+ after , self ._close_after , closer ,
59
+ silent , delete_channel , message )
55
60
return
56
61
57
62
return await self ._close (closer , silent , delete_channel , message )
58
63
59
- async def _close (self , closer , silent = False , delete_channel = True , message = None , scheduled = False ):
64
+ async def _close (self , closer , silent = False , delete_channel = True ,
65
+ message = None , scheduled = False ):
60
66
del self .manager .cache [self .id ]
61
67
if str (self .id ) in self .bot .config .subscriptions :
62
68
del self .bot .config .subscriptions [str (self .id )]
63
69
64
70
# Logging
65
71
log_data = await self .bot .modmail_api .post_log (self .channel .id , {
66
- 'open' : False , 'closed_at' : str (datetime .datetime .utcnow ()), 'closer' : {
72
+ 'open' : False ,
73
+ 'closed_at' : str (datetime .datetime .utcnow ()),
74
+ 'closer' : {
67
75
'id' : str (closer .id ),
68
76
'name' : closer .name ,
69
77
'discriminator' : closer .discriminator ,
@@ -79,9 +87,10 @@ async def _close(self, closer, silent=False, delete_channel=True, message=None,
79
87
if self .bot .selfhosted :
80
88
log_url = f'{ self .bot .config .log_url } /logs/{ log_data ["key" ]} '
81
89
else :
82
- log_url = f"https://logs.modmail.tk/{ log_data ['user_id' ]} /{ log_data ['key' ]} "
90
+ log_url = f"https://logs.modmail.tk/" \
91
+ f"{ log_data ['user_id' ]} /{ log_data ['key' ]} "
83
92
84
- user = self .recipient .mention if self .recipient else f'` { self .id } `'
93
+ user = self .recipient .mention if self .recipient else str ( self .id )
85
94
86
95
if log_data ['messages' ]:
87
96
msg = str (log_data ['messages' ][0 ]['content' ])
@@ -105,9 +114,9 @@ async def _close(self, closer, silent=False, delete_channel=True, message=None,
105
114
106
115
# Thread closed message
107
116
108
- em = discord .Embed (title = 'Thread Closed' )
109
- em .description = message or f' { closer . mention } has closed this modmail thread.'
110
- em . color = discord . Color . red ()
117
+ em = discord .Embed (title = 'Thread Closed' , color = discord . Color . red () )
118
+ em .description = message or \
119
+ f' { closer . mention } has closed this modmail thread.'
111
120
112
121
if not silent and self .recipient is not None :
113
122
tasks .append (self .recipient .send (embed = em ))
@@ -117,15 +126,17 @@ async def _close(self, closer, silent=False, delete_channel=True, message=None,
117
126
118
127
await asyncio .gather (* tasks )
119
128
120
- async def _edit_thread_message (self , channel , message_id , message ):
129
+ @staticmethod
130
+ async def _edit_thread_message (channel , message_id , message ):
121
131
async for msg in channel .history ():
122
132
if not msg .embeds :
123
133
continue
124
134
embed = msg .embeds [0 ]
125
135
if embed and embed .author and embed .author .url :
126
136
if str (message_id ) == str (embed .author .url ).split ('/' )[- 1 ]:
127
137
if ' - (Edited)' not in embed .footer .text :
128
- embed .set_footer (text = embed .footer .text + ' - (Edited)' )
138
+ embed .set_footer (
139
+ text = embed .footer .text + ' - (Edited)' )
129
140
embed .description = message
130
141
await msg .edit (embed = embed )
131
142
break
@@ -140,31 +151,47 @@ async def reply(self, message):
140
151
if not message .content and not message .attachments :
141
152
raise commands .UserInputError
142
153
if all (not g .get_member (self .id ) for g in self .bot .guilds ):
143
- return await message .channel .send (embed = discord .Embed (color = discord .Color .red (), description = 'This user shares no servers with me and is thus unreachable.' ))
154
+ return await message .channel .send (
155
+ embed = discord .Embed (
156
+ color = discord .Color .red (),
157
+ description = 'This user shares no servers with '
158
+ 'me and is thus unreachable.' ))
144
159
145
160
tasks = [
146
- self .send (message , self .channel , from_mod = True ), # in thread channel
147
- self .send (message , self .recipient , from_mod = True ) # to user
161
+ # in thread channel
162
+ self .send (message , self .channel , from_mod = True ),
163
+ # to user
164
+ self .send (message , self .recipient , from_mod = True )
148
165
]
149
166
150
167
if self .close_task is not None and not self .close_task .cancelled ():
151
- self .close_task .cancel () # cancel closing if a thread message is sent.
152
- tasks .append (self .channel .send (embed = discord .Embed (color = discord .Color .red (), description = 'Scheduled close has been cancelled.' )))
168
+ # cancel closing if a thread message is sent.
169
+ self .close_task .cancel ()
170
+ tasks .append (self .channel .send (
171
+ embed = discord .Embed (color = discord .Color .red (),
172
+ description = 'Scheduled close has '
173
+ 'been cancelled.' )))
153
174
154
175
await asyncio .gather (* tasks )
155
176
156
- async def send (self , message , destination = None , from_mod = False , delete_message = True ):
177
+ async def send (self , message , destination = None , from_mod = False ):
157
178
if self .close_task is not None and not self .close_task .cancelled ():
158
- self .close_task .cancel () # cancel closing if a thread message is sent.
159
- await self .channel .send (embed = discord .Embed (color = discord .Color .red (), description = 'Scheduled close has been cancelled.' ))
179
+ # cancel closing if a thread message is sent.
180
+ self .close_task .cancel ()
181
+ await self .channel .send (embed = discord .Embed (
182
+ color = discord .Color .red (),
183
+ description = 'Scheduled close has been cancelled.' ))
184
+
160
185
if not self .ready :
161
186
await self .wait_until_ready ()
162
187
163
188
destination = destination or self .channel
164
189
if from_mod and not isinstance (destination , discord .User ):
165
- asyncio .create_task (self .bot .modmail_api .append_log (message ))
190
+ self .bot .loop .create_task (
191
+ self .bot .modmail_api .append_log (message ))
166
192
elif not from_mod :
167
- asyncio .create_task (self .bot .modmail_api .append_log (message , destination .id ))
193
+ self .bot .loop .create_task (
194
+ self .bot .modmail_api .append_log (message , destination .id ))
168
195
169
196
author = message .author
170
197
@@ -173,10 +200,16 @@ async def send(self, message, destination=None, from_mod=False, delete_message=T
173
200
timestamp = message .created_at
174
201
)
175
202
176
- em .set_author (name = str (author ), icon_url = author .avatar_url , url = message .jump_url ) # store message id in hidden url
203
+ # store message id in hidden url
204
+ em .set_author (name = str (author ),
205
+ icon_url = author .avatar_url ,
206
+ url = message .jump_url )
177
207
178
208
image_types = ['.png' , '.jpg' , '.gif' , '.jpeg' , '.webp' ]
179
- is_image_url = lambda u , _ : any (urlparse (u .lower ()).path .endswith (x ) for x in image_types )
209
+
210
+ def is_image_url (u , _ ):
211
+ return any (urlparse (u .lower ()).path .endswith (x )
212
+ for x in image_types )
180
213
181
214
delete_message = not bool (message .attachments )
182
215
@@ -185,7 +218,8 @@ async def send(self, message, destination=None, from_mod=False, delete_message=T
185
218
images = [x for x in attachments if is_image_url (* x )]
186
219
attachments = [x for x in attachments if not is_image_url (* x )]
187
220
188
- image_links = [(link , None ) for link in re .findall (r'(https?://[^\s]+)' , message .content )]
221
+ image_links = [(link , None ) for link in
222
+ re .findall (r'(https?://[^\s]+)' , message .content )]
189
223
image_links = [x for x in image_links if is_image_url (* x )]
190
224
images .extend (image_links )
191
225
@@ -196,19 +230,24 @@ async def send(self, message, destination=None, from_mod=False, delete_message=T
196
230
additional_count = 1
197
231
198
232
for att in images :
199
- if is_image_url (* att ) and not embedded_image and att [1 ] if prioritize_uploads else True :
233
+ if is_image_url (* att ) and not embedded_image and att [1 ] \
234
+ if prioritize_uploads else True :
200
235
em .set_image (url = att [0 ])
201
236
embedded_image = True
202
237
elif att [1 ] is not None :
203
238
link = f'[{ att [1 ]} ]({ att [0 ]} )'
204
- em .add_field (name = f'Additional Image upload ({ additional_count } )' , value = link , inline = False )
239
+ em .add_field (
240
+ name = f'Additional Image upload ({ additional_count } )' ,
241
+ value = link ,
242
+ inline = False
243
+ )
205
244
additional_count += 1
206
245
207
246
file_upload_count = 1
208
247
209
-
210
248
for att in attachments :
211
- em .add_field (name = f'File upload ({ file_upload_count } )' , value = f'[{ att [1 ]} ]({ att [0 ]} )' )
249
+ em .add_field (name = f'File upload ({ file_upload_count } )' ,
250
+ value = f'[{ att [1 ]} ]({ att [0 ]} )' )
212
251
file_upload_count += 1
213
252
214
253
if from_mod :
@@ -257,7 +296,8 @@ def __init__(self, bot):
257
296
258
297
async def populate_cache (self ):
259
298
for channel in self .bot .modmail_guild .text_channels :
260
- if not self .bot .using_multiple_server_setup and channel .category != self .bot .main_category :
299
+ if not self .bot .using_multiple_server_setup and \
300
+ channel .category != self .bot .main_category :
261
301
continue
262
302
await self .find (channel = channel )
263
303
@@ -274,16 +314,16 @@ async def find(self, *, recipient=None, channel=None):
274
314
"""Finds a thread from cache or from discord channel topics."""
275
315
if recipient is None and channel is not None :
276
316
return await self ._find_from_channel (channel )
317
+
318
+ thread = None
277
319
try :
278
320
thread = self .cache [recipient .id ]
279
321
except KeyError :
280
322
channel = discord .utils .get (
281
323
self .bot .modmail_guild .text_channels ,
282
324
topic = f'User ID: { recipient .id } '
283
325
)
284
- if not channel :
285
- thread = None
286
- else :
326
+ if channel :
287
327
self .cache [recipient .id ] = thread = Thread (self , recipient )
288
328
thread .channel = channel
289
329
thread .ready = True
@@ -294,19 +334,22 @@ async def _find_from_channel(self, channel):
294
334
"""
295
335
Tries to find a thread from a channel channel topic,
296
336
if channel topic doesnt exist for some reason, falls back to
297
- searching channel history for genesis embed and extracts user_id fron that.
337
+ searching channel history for genesis embed and
338
+ extracts user_id from that.
298
339
"""
299
340
user_id = None
300
341
301
342
if channel .topic and 'User ID: ' in channel .topic :
302
343
user_id = int (re .findall (r'\d+' , channel .topic )[0 ])
303
344
304
- # BUG: When discord fails to create channel topic. search through message history
345
+ # BUG: When discord fails to create channel topic.
346
+ # search through message history
305
347
elif channel .topic is None :
306
348
async for message in channel .history (limit = 50 ):
307
349
if message .embeds :
308
350
em = message .embeds [0 ]
309
- matches = re .findall (r'User ID: (\d+)' , str (em .footer .text ))
351
+ # TODO: use re.search instead
352
+ matches = re .findall (r'User ID: (\d+)' , em .footer .text )
310
353
if matches :
311
354
user_id = int (matches [0 ])
312
355
break
@@ -329,12 +372,15 @@ async def create(self, recipient, *, creator=None):
329
372
330
373
em = discord .Embed (
331
374
title = 'Thread created!' ,
332
- description = self .bot .config .get ('thread_creation_response' , 'The moderation team will get back to you as soon as possible!' ),
375
+ description = self .bot .config .get (
376
+ 'thread_creation_response' ,
377
+ 'The moderation team will get back to you as soon as possible!'
378
+ ),
333
379
color = discord .Color .green ()
334
380
)
335
381
336
382
if creator is None :
337
- asyncio .create_task (recipient .send (embed = em ))
383
+ self . bot . loop .create_task (recipient .send (embed = em ))
338
384
339
385
self .cache [recipient .id ] = thread = Thread (self , recipient )
340
386
@@ -346,16 +392,20 @@ async def create(self, recipient, *, creator=None):
346
392
thread .channel = channel
347
393
348
394
log_url , log_data = await asyncio .gather (
349
- self .bot .modmail_api .get_log_url (recipient , channel , creator or recipient ),
395
+ self .bot .modmail_api .get_log_url (recipient , channel ,
396
+ creator or recipient ),
350
397
self .bot .modmail_api .get_user_logs (recipient .id )
351
398
# self.get_dominant_color(recipient.avatar_url)
352
399
)
353
400
354
401
log_count = sum (1 for log in log_data if not log ['open' ])
355
- info_embed = self ._format_info_embed (recipient , creator , log_url , log_count , discord .Color .green ())
402
+ info_embed = self ._format_info_embed (recipient , creator ,
403
+ log_url , log_count ,
404
+ discord .Color .green ())
356
405
357
406
topic = f'User ID: { recipient .id } '
358
- mention = self .bot .config .get ('mention' , '@here' ) if not creator else None
407
+ mention = self .bot .config .get ('mention' , '@here' ) \
408
+ if not creator else None
359
409
360
410
_ , msg = await asyncio .gather (
361
411
channel .edit (topic = topic ),
@@ -369,7 +419,8 @@ async def create(self, recipient, *, creator=None):
369
419
return thread
370
420
371
421
async def find_or_create (self , recipient ):
372
- return await self .find (recipient = recipient ) or await self .create (recipient )
422
+ return await self .find (recipient = recipient ) or \
423
+ await self .create (recipient )
373
424
374
425
@staticmethod
375
426
def valid_image_url (url ):
@@ -398,7 +449,7 @@ async def get_dominant_color(self, url=None, quality=10):
398
449
async with self .bot .session .get (url ) as resp :
399
450
image = await resp .read ()
400
451
color = await self ._do_get_dc (image , quality )
401
- except Exception :
452
+ except :
402
453
traceback .print_exc ()
403
454
return discord .Color .blurple ()
404
455
else :
@@ -411,7 +462,8 @@ def _format_channel_name(self, author):
411
462
new_name = '' .join (l for l in name if l in allowed ) or 'null'
412
463
new_name += f'-{ author .discriminator } '
413
464
414
- while new_name in [c .name for c in self .bot .modmail_guild .text_channels ]:
465
+ while new_name in [c .name for c in
466
+ self .bot .modmail_guild .text_channels ]:
415
467
new_name += '-x' # two channels with same name
416
468
417
469
return new_name
@@ -422,21 +474,26 @@ def _format_info_embed(self, user, creator, log_url, log_count, dc):
422
474
member = self .bot .guild .get_member (user .id )
423
475
avi = user .avatar_url
424
476
time = datetime .datetime .utcnow ()
425
- desc = f'{ creator .mention } has created a thread with { user .mention } ' if creator else f'{ user .mention } has started a thread'
477
+ desc = f'{ creator .mention } has created a thread with { user .mention } ' \
478
+ if creator else f'{ user .mention } has started a thread'
426
479
key = log_url .split ('/' )[- 1 ]
427
480
desc = f'{ desc } [`{ key } `]({ log_url } )'
428
481
482
+ role_names = ''
429
483
if member :
430
484
seperate_server = self .bot .guild != self .bot .modmail_guild
431
485
roles = sorted (member .roles , key = lambda c : c .position )
432
486
if seperate_server :
433
- rolenames = ', ' .join (r .name for r in roles if r .name != "@everyone" )
487
+ role_names = ', ' .join (r .name for r in roles
488
+ if r .name != "@everyone" )
434
489
else :
435
- rolenames = ' ' .join (r .mention for r in roles if r .name != "@everyone" )
490
+ role_names = ' ' .join (r .mention for r in roles
491
+ if r .name != "@everyone" )
436
492
437
493
em = discord .Embed (colour = dc , description = desc , timestamp = time )
438
494
439
- days = lambda d : (' day ago.' if d == '1' else ' days ago.' )
495
+ def days (d ):
496
+ return ' day ago.' if d == '1' else ' days ago.'
440
497
441
498
created = str ((time - user .created_at ).days )
442
499
# em.add_field(name='Mention', value=user.mention)
@@ -453,9 +510,11 @@ def _format_info_embed(self, user, creator, log_url, log_count, dc):
453
510
em .add_field (name = 'Joined' , value = joined + days (joined ))
454
511
if member .nick :
455
512
em .add_field (name = 'Nickname' , value = member .nick , inline = True )
456
- if rolenames :
457
- em .add_field (name = 'Roles' , value = rolenames , inline = False )
513
+ if role_names :
514
+ em .add_field (name = 'Roles' , value = role_names , inline = False )
458
515
else :
459
- em .set_footer (text = footer + ' | Note: this member is not part of this server.' )
516
+ em .set_footer (
517
+ text = f'{ footer } | Note: this member'
518
+ f' is not part of this server.' )
460
519
461
520
return em
0 commit comments