@@ -395,6 +395,10 @@ def _sub_join(*strs: str) -> str:
395
395
async def get_or_create_webhook (
396
396
channel : discord .TextChannel | discord .ForumChannel , name : str = "Ghostty Moderator"
397
397
) -> discord .Webhook :
398
+ """
399
+ NOTE: if the intent of this webhook is for moving messages, do not change
400
+ the default value of `name`.
401
+ """
398
402
webhooks = await channel .webhooks ()
399
403
for webhook in webhooks :
400
404
if webhook .name == name :
@@ -510,6 +514,43 @@ def format_or_file(
510
514
return full_message , None
511
515
512
516
517
+ async def get_moved_message (message : discord .Message ) -> discord .WebhookMessage | None :
518
+ """
519
+ Returns None if it could not be acquired, and discord.utils.MISSING if the
520
+ provided message is not a moved message.
521
+ """
522
+ if message .webhook_id is None or isinstance (
523
+ message .channel ,
524
+ # These types can't even have a webhook.
525
+ discord .DMChannel | discord .GroupChannel | discord .PartialMessageable ,
526
+ ):
527
+ return discord .utils .MISSING
528
+
529
+ if isinstance (message .channel , discord .Thread ):
530
+ thread = message .channel
531
+ if (channel := message .channel .parent ) is None :
532
+ return None
533
+ else :
534
+ channel = message .channel
535
+ thread = discord .utils .MISSING
536
+
537
+ for webhook in await channel .webhooks ():
538
+ if webhook .id == message .webhook_id :
539
+ break
540
+ else :
541
+ return discord .utils .MISSING
542
+ if webhook .name != "Ghostty Moderator" :
543
+ # More heuristics to determine if a webhook message is a moved message.
544
+ return discord .utils .MISSING
545
+
546
+ try :
547
+ return await webhook .fetch_message (message .id , thread = thread )
548
+ except discord .Forbidden :
549
+ return None
550
+ except discord .NotFound :
551
+ return discord .utils .MISSING
552
+
553
+
513
554
def _find_snowflake (
514
555
content : str , type_ : str , * , substring_start : int = 0
515
556
) -> tuple [int | None , int | None ]:
0 commit comments