Experimental Discord bot written in Python
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

patterncog.py 3.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. from discord import Guild, Message
  2. from discord.ext import commands
  3. from datetime import timedelta
  4. from cogs.basecog import BaseCog, BotMessage
  5. from storage import Storage
  6. class Criterion:
  7. def __init__(self, type, **kwargs):
  8. self.type = type
  9. if type == 'contains':
  10. text = kwargs['text']
  11. self.text = text
  12. self.test = lambda m : text.lower() in m.content.lower()
  13. elif type == 'joinage':
  14. min = kwargs['min']
  15. self.min = min
  16. self.test = lambda m : m.created_at - m.author.joined_at < min
  17. else:
  18. raise RuntimeError(f'Unknown criterion type "{type}"')
  19. def matches(self, message: Message) -> bool:
  20. return self.test(message)
  21. @classmethod
  22. def decode(cls, val: dict):
  23. type = val['type']
  24. if type == 'contains':
  25. return Criterion(type, text=val['text'])
  26. elif type == 'joinage':
  27. return Criterion(type, min=timedelta(seconds=val['min']))
  28. class Pattern:
  29. def __init__(self, criteria: list, action: str, must_match_all: bool = True):
  30. self.criteria = criteria
  31. self.action = action
  32. self.must_match_all = must_match_all
  33. def matches(self, message: Message) -> bool:
  34. for criterion in self.criteria:
  35. crit_matches = criterion.matches(message)
  36. if crit_matches and not self.must_match_all:
  37. return True
  38. if not crit_matches and self.must_match_all:
  39. return False
  40. return self.must_match_all
  41. @classmethod
  42. def decode(cls, val: dict):
  43. match_all = val.get('must_match_all')
  44. action = val.get('action')
  45. encoded_criteria = val.get('criteria')
  46. criteria = []
  47. for ec in encoded_criteria:
  48. criteria.append(Criterion.decode(ec))
  49. return Pattern(criteria, action, match_all if isinstance(match_all, bool) else True)
  50. class PatternCog(BaseCog):
  51. def __init__(self, bot):
  52. super().__init__(bot)
  53. def __patterns(self, guild: Guild) -> list:
  54. patterns = Storage.get_state_value(guild, 'pattern_patterns')
  55. if patterns is None:
  56. patterns_encoded = Storage.get_config_value(guild, 'pattern_patterns')
  57. if patterns_encoded:
  58. patterns = []
  59. for pe in patterns_encoded:
  60. patterns.append(Pattern.decode(pe))
  61. Storage.set_state_value(guild, 'pattern_patterns', patterns)
  62. return patterns
  63. @commands.Cog.listener()
  64. async def on_message(self, message: Message) -> None:
  65. if message.guild is None or message.content is None or message.channel is None:
  66. return
  67. if message.author.id == self.bot.user.id:
  68. # Ignore self
  69. return
  70. if message.author.permissions_in(message.channel).ban_members:
  71. # Ignore mods
  72. return
  73. patterns = self.__patterns(message.guild)
  74. for pattern in patterns:
  75. if pattern.matches(message):
  76. msg = None
  77. if pattern.action == 'delete':
  78. await message.delete()
  79. msg = f'Message from {message.author.mention} matched ' + \
  80. 'banned pattern. Deleted.'
  81. elif pattern.action == 'kick':
  82. await message.delete()
  83. await message.author.kick(reason='Rocketbot: Message matched banned pattern')
  84. msg = f'Message from {message.author.mention} matched ' + \
  85. 'banned pattern. Message deleted and user kicked.'
  86. elif pattern.action == 'ban':
  87. await message.delete()
  88. await message.author.ban(reason='Rocketbot: Message matched banned pattern')
  89. msg = f'Message from {message.author.mention} matched ' + \
  90. 'banned pattern. Message deleted and user banned.'
  91. if msg:
  92. m = BotMessage(message.guild,
  93. text = msg,
  94. type = BotMessage.TYPE_MOD_WARNING)
  95. m.quote = message.content
  96. await self.post_message(m)
  97. break