1
- __version__ = "3.3.1-dev1 "
1
+ __version__ = "3.3.1-dev2 "
2
2
3
3
import asyncio
4
4
import logging
@@ -529,122 +529,171 @@ async def retrieve_emoji(self) -> typing.Tuple[str, str]:
529
529
530
530
return sent_emoji , blocked_emoji
531
531
532
- async def _process_blocked (self , message : discord .Message ) -> typing .Tuple [bool , str ]:
533
- sent_emoji , blocked_emoji = await self .retrieve_emoji ()
534
-
535
- if str (message .author .id ) in self .blocked_whitelisted_users :
536
- if str (message .author .id ) in self .blocked_users :
537
- self .blocked_users .pop (str (message .author .id ))
538
- await self .config .update ()
539
-
540
- return False , sent_emoji
541
-
542
- now = datetime .utcnow ()
543
-
532
+ def check_account_age (self , author : discord .Member ) -> bool :
544
533
account_age = self .config .get ("account_age" )
545
- guild_age = self .config .get ("guild_age" )
546
-
547
- if account_age is None :
548
- account_age = isodate .Duration ()
549
- if guild_age is None :
550
- guild_age = isodate .Duration ()
551
-
552
- reason = self .blocked_users .get (str (message .author .id )) or ""
553
- min_guild_age = min_account_age = now
534
+ now = datetime .utcnow ()
554
535
555
536
try :
556
- min_account_age = message . author .created_at + account_age
537
+ min_account_age = author .created_at + account_age
557
538
except ValueError :
558
539
logger .warning ("Error with 'account_age'." , exc_info = True )
559
- self .config .remove ("account_age" )
560
-
561
- try :
562
- joined_at = getattr (message .author , "joined_at" , None )
563
- if joined_at is not None :
564
- min_guild_age = joined_at + guild_age
565
- except ValueError :
566
- logger .warning ("Error with 'guild_age'." , exc_info = True )
567
- self .config .remove ("guild_age" )
540
+ min_account_age = author .created_at + self .config .remove ("account_age" )
568
541
569
542
if min_account_age > now :
570
543
# User account has not reached the required time
571
- reaction = blocked_emoji
572
- changed = False
573
544
delta = human_timedelta (min_account_age )
574
- logger .debug ("Blocked due to account age, user %s." , message . author .name )
545
+ logger .debug ("Blocked due to account age, user %s." , author .name )
575
546
576
- if str (message . author .id ) not in self .blocked_users :
547
+ if str (author .id ) not in self .blocked_users :
577
548
new_reason = f"System Message: New Account. Required to wait for { delta } ."
578
- self .blocked_users [str (message .author .id )] = new_reason
579
- changed = True
549
+ self .blocked_users [str (author .id )] = new_reason
580
550
581
- if reason .startswith ("System Message: New Account." ) or changed :
582
- await message .channel .send (
583
- embed = discord .Embed (
584
- title = "Message not sent!" ,
585
- description = f"Your must wait for { delta } before you can contact me." ,
586
- color = self .error_color ,
587
- )
588
- )
551
+ return False
552
+ return True
553
+
554
+ def check_guild_age (self , author : discord .Member ) -> bool :
555
+ guild_age = self .config .get ("guild_age" )
556
+ now = datetime .utcnow ()
557
+
558
+ if not hasattr (author , "joined_at" ):
559
+ logger .warning ("Not in guild, cannot verify guild_age, %s." , author .name )
560
+ return True
589
561
590
- elif min_guild_age > now :
562
+ try :
563
+ min_guild_age = author .joined_at + guild_age
564
+ except ValueError :
565
+ logger .warning ("Error with 'guild_age'." , exc_info = True )
566
+ min_guild_age = author .joined_at + self .config .remove ("guild_age" )
567
+
568
+ if min_guild_age > now :
591
569
# User has not stayed in the guild for long enough
592
- reaction = blocked_emoji
593
- changed = False
594
570
delta = human_timedelta (min_guild_age )
595
- logger .debug ("Blocked due to guild age, user %s." , message . author .name )
571
+ logger .debug ("Blocked due to guild age, user %s." , author .name )
596
572
597
- if str (message . author .id ) not in self .blocked_users :
573
+ if str (author .id ) not in self .blocked_users :
598
574
new_reason = f"System Message: Recently Joined. Required to wait for { delta } ."
599
- self .blocked_users [str (message .author .id )] = new_reason
600
- changed = True
575
+ self .blocked_users [str (author .id )] = new_reason
601
576
602
- if reason .startswith ("System Message: Recently Joined." ) or changed :
603
- await message .channel .send (
604
- embed = discord .Embed (
605
- title = "Message not sent!" ,
606
- description = f"Your must wait for { delta } before you can contact me." ,
607
- color = self .error_color ,
608
- )
577
+ return False
578
+ return True
579
+
580
+ def check_manual_blocked (self , author : discord .Member ) -> bool :
581
+ if str (author .id ) not in self .blocked_users :
582
+ return True
583
+
584
+ blocked_reason = self .blocked_users .get (str (author .id )) or ""
585
+ now = datetime .utcnow ()
586
+
587
+ if blocked_reason .startswith ("System Message:" ):
588
+ # Met the limits already, otherwise it would've been caught by the previous checks
589
+ logger .debug ("No longer internally blocked, user %s." , author .name )
590
+ self .blocked_users .pop (str (author .id ))
591
+ return True
592
+ # etc "blah blah blah... until 2019-10-14T21:12:45.559948."
593
+ end_time = re .search (r"until ([^`]+?)\.$" , blocked_reason )
594
+ if end_time is None :
595
+ # backwards compat
596
+ end_time = re .search (r"%([^%]+?)%" , blocked_reason )
597
+ if end_time is not None :
598
+ logger .warning (
599
+ r"Deprecated time message for user %s, block and unblock again to update." ,
600
+ author .name ,
609
601
)
610
602
611
- elif str (message .author .id ) in self .blocked_users :
612
- if reason .startswith ("System Message: New Account." ) or reason .startswith (
613
- "System Message: Recently Joined."
614
- ):
615
- # Met the age limit already, otherwise it would've been caught by the previous if's
616
- reaction = sent_emoji
617
- logger .debug ("No longer internally blocked, user %s." , message .author .name )
618
- self .blocked_users .pop (str (message .author .id ))
619
- else :
620
- reaction = blocked_emoji
621
- # etc "blah blah blah... until 2019-10-14T21:12:45.559948."
622
- end_time = re .search (r"until ([^`]+?)\.$" , reason )
623
- if end_time is None :
624
- # backwards compat
625
- end_time = re .search (r"%([^%]+?)%" , reason )
626
- if end_time is not None :
627
- logger .warning (
628
- r"Deprecated time message for user %s, block and unblock again to update." ,
629
- message .author ,
603
+ if end_time is not None :
604
+ after = (datetime .fromisoformat (end_time .group (1 )) - now ).total_seconds ()
605
+ if after <= 0 :
606
+ # No longer blocked
607
+ self .blocked_users .pop (str (author .id ))
608
+ logger .debug ("No longer blocked, user %s." , author .name )
609
+ return True
610
+ logger .debug ("User blocked, user %s." , author .name )
611
+ return False
612
+
613
+ async def _process_blocked (self , message ):
614
+ sent_emoji , blocked_emoji = await self .retrieve_emoji ()
615
+ if await self .is_blocked (message .author , channel = message .channel , send_message = True ):
616
+ await self .add_reaction (message , blocked_emoji )
617
+ return True
618
+ return False
619
+
620
+ async def is_blocked (
621
+ self ,
622
+ author : discord .User ,
623
+ * ,
624
+ channel : discord .TextChannel = None ,
625
+ send_message : bool = False ,
626
+ ) -> typing .Tuple [bool , str ]:
627
+
628
+ member = self .guild .get_member (author .id )
629
+ if member is None :
630
+ logger .debug ("User not in guild, %s." , author .id )
631
+ else :
632
+ author = member
633
+
634
+ if str (author .id ) in self .blocked_whitelisted_users :
635
+ if str (author .id ) in self .blocked_users :
636
+ self .blocked_users .pop (str (author .id ))
637
+ await self .config .update ()
638
+ return False
639
+
640
+ blocked_reason = self .blocked_users .get (str (author .id )) or ""
641
+
642
+ if (
643
+ not self .check_account_age (author )
644
+ or not self .check_guild_age (author )
645
+ ):
646
+ new_reason = self .blocked_users .get (str (author .id ))
647
+ if new_reason != blocked_reason :
648
+ if send_message :
649
+ await channel .send (
650
+ embed = discord .Embed (
651
+ title = "Message not sent!" ,
652
+ description = new_reason ,
653
+ color = self .error_color ,
630
654
)
655
+ )
656
+ return True
631
657
632
- if end_time is not None :
633
- after = (datetime .fromisoformat (end_time .group (1 )) - now ).total_seconds ()
634
- if after <= 0 :
635
- # No longer blocked
636
- reaction = sent_emoji
637
- self .blocked_users .pop (str (message .author .id ))
638
- logger .debug ("No longer blocked, user %s." , message .author .name )
639
- else :
640
- logger .debug ("User blocked, user %s." , message .author .name )
641
- else :
642
- logger .debug ("User blocked, user %s." , message .author .name )
643
- else :
644
- reaction = sent_emoji
658
+ if not self .check_manual_blocked (author ):
659
+ return True
645
660
646
661
await self .config .update ()
647
- return str (message .author .id ) in self .blocked_users , reaction
662
+ return False
663
+
664
+ async def get_thread_cooldown (self , author : discord .Member ):
665
+ thread_cooldown = self .config .get ("thread_cooldown" )
666
+ now = datetime .utcnow ()
667
+
668
+ if thread_cooldown == isodate .Duration ():
669
+ return
670
+
671
+ last_log = await self .api .get_latest_user_logs (author .id )
672
+
673
+ if last_log is None :
674
+ logger .debug ("Last thread wasn't found, %s." , author .name )
675
+ return
676
+
677
+ last_log_closed_at = last_log .get ("closed_at" )
678
+
679
+ if not last_log_closed_at :
680
+ logger .debug ("Last thread was not closed, %s." , author .name )
681
+ return
682
+
683
+ try :
684
+ cooldown = datetime .fromisoformat (last_log_closed_at ) + thread_cooldown
685
+ except ValueError :
686
+ logger .warning ("Error with 'thread_cooldown'." , exc_info = True )
687
+ cooldown = datetime .fromisoformat (last_log_closed_at ) + self .config .remove (
688
+ "thread_cooldown"
689
+ )
690
+
691
+ if cooldown > now :
692
+ # User messaged before thread cooldown ended
693
+ delta = human_timedelta (cooldown )
694
+ logger .debug ("Blocked due to thread cooldown, user %s." , author .name )
695
+ return delta
696
+ return
648
697
649
698
@staticmethod
650
699
async def add_reaction (msg , reaction ):
@@ -656,11 +705,24 @@ async def add_reaction(msg, reaction):
656
705
657
706
async def process_dm_modmail (self , message : discord .Message ) -> None :
658
707
"""Processes messages sent to the bot."""
659
- blocked , reaction = await self ._process_blocked (message )
708
+ blocked = await self ._process_blocked (message )
660
709
if blocked :
661
- return await self .add_reaction (message , reaction )
710
+ return
711
+ sent_emoji , blocked_emoji = await self .retrieve_emoji ()
712
+
662
713
thread = await self .threads .find (recipient = message .author )
663
714
if thread is None :
715
+ delta = await self .get_thread_cooldown (message .author )
716
+ if delta :
717
+ await message .channel .send (
718
+ embed = discord .Embed (
719
+ title = "Message not sent!" ,
720
+ description = f"You must wait for { delta } before you can contact me again." ,
721
+ color = self .error_color ,
722
+ )
723
+ )
724
+ return
725
+
664
726
if self .config ["dm_disabled" ] >= 1 :
665
727
embed = discord .Embed (
666
728
title = self .config ["disabled_new_thread_title" ],
@@ -673,9 +735,9 @@ async def process_dm_modmail(self, message: discord.Message) -> None:
673
735
logger .info (
674
736
"A new thread was blocked from %s due to disabled Modmail." , message .author
675
737
)
676
- _ , blocked_emoji = await self .retrieve_emoji ()
677
738
await self .add_reaction (message , blocked_emoji )
678
739
return await message .channel .send (embed = embed )
740
+
679
741
thread = self .threads .create (message .author )
680
742
else :
681
743
if self .config ["dm_disabled" ] == 2 :
@@ -691,12 +753,16 @@ async def process_dm_modmail(self, message: discord.Message) -> None:
691
753
logger .info (
692
754
"A message was blocked from %s due to disabled Modmail." , message .author
693
755
)
694
- _ , blocked_emoji = await self .retrieve_emoji ()
695
756
await self .add_reaction (message , blocked_emoji )
696
757
return await message .channel .send (embed = embed )
697
758
698
- await self .add_reaction (message , reaction )
699
- await thread .send (message )
759
+ try :
760
+ await thread .send (message )
761
+ except Exception :
762
+ logger .error ("Failed to send message:" , exc_info = True )
763
+ await self .add_reaction (message , blocked_emoji )
764
+ else :
765
+ await self .add_reaction (message , sent_emoji )
700
766
701
767
async def get_contexts (self , message , * , cls = commands .Context ):
702
768
"""
@@ -849,9 +915,6 @@ async def on_typing(self, channel, user, _):
849
915
if user .bot :
850
916
return
851
917
852
- async def _void (* _args , ** _kwargs ):
853
- pass
854
-
855
918
if isinstance (channel , discord .DMChannel ):
856
919
if not self .config .get ("user_typing" ):
857
920
return
@@ -866,13 +929,7 @@ async def _void(*_args, **_kwargs):
866
929
867
930
thread = await self .threads .find (channel = channel )
868
931
if thread is not None and thread .recipient :
869
- if (
870
- await self ._process_blocked (
871
- SimpleNamespace (
872
- author = thread .recipient , channel = SimpleNamespace (send = _void )
873
- )
874
- )
875
- )[0 ]:
932
+ if await self .is_blocked (thread .recipient ):
876
933
return
877
934
await thread .recipient .trigger_typing ()
878
935
0 commit comments