Kaynağa Gözat

Adding username pattern cog

master
Rocketsoup 3 yıl önce
ebeveyn
işleme
a85124b51c
3 değiştirilmiş dosya ile 188 ekleme ve 0 silme
  1. 2
    0
      bot.py
  2. 1
    0
      config.py.sample
  3. 185
    0
      rocketbot/cogs/usernamecog.py

+ 2
- 0
bot.py Dosyayı Görüntüle

16
 from rocketbot.cogs.joinraidcog import JoinRaidCog
16
 from rocketbot.cogs.joinraidcog import JoinRaidCog
17
 from rocketbot.cogs.patterncog import PatternCog
17
 from rocketbot.cogs.patterncog import PatternCog
18
 from rocketbot.cogs.urlspamcog import URLSpamCog
18
 from rocketbot.cogs.urlspamcog import URLSpamCog
19
+from rocketbot.cogs.usernamecog import UsernamePatternCog
19
 
20
 
20
 CURRENT_CONFIG_VERSION = 3
21
 CURRENT_CONFIG_VERSION = 3
21
 if (CONFIG.get('__config_version') or 0) < CURRENT_CONFIG_VERSION:
22
 if (CONFIG.get('__config_version') or 0) < CURRENT_CONFIG_VERSION:
65
 bot.add_cog(JoinRaidCog(bot))
66
 bot.add_cog(JoinRaidCog(bot))
66
 bot.add_cog(PatternCog(bot))
67
 bot.add_cog(PatternCog(bot))
67
 bot.add_cog(URLSpamCog(bot))
68
 bot.add_cog(URLSpamCog(bot))
69
+bot.add_cog(UsernamePatternCog(bot))
68
 
70
 
69
 bot.run(CONFIG['client_token'], bot=True, reconnect=True)
71
 bot.run(CONFIG['client_token'], bot=True, reconnect=True)
70
 print('\nBot aborted')
72
 print('\nBot aborted')

+ 1
- 0
config.py.sample Dosyayı Görüntüle

10
 	'failure_emoji': '❌',
10
 	'failure_emoji': '❌',
11
 	'warning_emoji': '⚠️',
11
 	'warning_emoji': '⚠️',
12
 	'info_emoji': 'ℹ️',
12
 	'info_emoji': 'ℹ️',
13
+	'ignore_emoji': '👍',
13
 	'config_path': 'config/',
14
 	'config_path': 'config/',
14
 	'cog_defaults': {
15
 	'cog_defaults': {
15
 		'CrossPostCog': {
16
 		'CrossPostCog': {

+ 185
- 0
rocketbot/cogs/usernamecog.py Dosyayı Görüntüle

1
+"""
2
+Cog for detecting username patterns.
3
+"""
4
+from discord import Guild, Member
5
+from discord.ext import commands
6
+
7
+from config import CONFIG
8
+from rocketbot.cogs.basecog import BaseCog, BotMessage, BotMessageReaction, CogSetting
9
+from rocketbot.storage import Storage
10
+
11
+class UsernamePatternContext:
12
+	"""
13
+	BotMessage context for a flagged username
14
+	"""
15
+	def __init__(self, member: Member) -> None:
16
+		self.member: Member = member
17
+		self.kicked_by: Member = None
18
+		self.banned_by: Member = None
19
+		self.ignored_by: Member = None
20
+
21
+	def reactions(self) -> list[BotMessageReaction]:
22
+		"""
23
+		Generates updated BotMessageReactions based on context state.
24
+		"""
25
+		r: list[BotMessageReaction] = []
26
+		if self.ignored_by:
27
+			r.append(BotMessageReaction(CONFIG['ignore_emoji'], False, f'Ignored by {self.ignored_by.name}'))
28
+		elif self.banned_by:
29
+			r.append(BotMessageReaction(CONFIG['ban_emoji'], False, f'Banned by {self.banned_by.name}'))
30
+		elif self.kicked_by:
31
+			r.append(BotMessageReaction(CONFIG['kick_emoji'], False, f'Kicked by {self.kicked_by.name}'))
32
+			r.append(BotMessageReaction(CONFIG['ban_emoji'], True, 'Ban user'))
33
+		else:
34
+			r.append(BotMessageReaction(CONFIG['kick_emoji'], True, 'Kick user'))
35
+			r.append(BotMessageReaction(CONFIG['ban_emoji'], True, 'Ban user'))
36
+			r.append(BotMessageReaction(CONFIG['ignore_emoji'], True, 'Ignore warning'))
37
+		return r
38
+
39
+class UsernamePatternCog(BaseCog, name='Username Pattern'):
40
+	"""
41
+	Detects usernames that match certain flagged patterns. Posts a mod warning
42
+	message on a match.
43
+	"""
44
+
45
+	SETTING_ENABLED = CogSetting('enabled', bool,
46
+			brief='username pattern detection',
47
+			description='Whether new users are checked for common patterns.')
48
+
49
+	SETTING_PATTERNS = CogSetting('patterns', None)
50
+
51
+	def __init__(self, bot):
52
+		super().__init__(bot)
53
+		self.add_setting(UsernamePatternCog.SETTING_ENABLED)
54
+
55
+	def __get_patterns(self, guild: Guild) -> list[str]:
56
+		"""
57
+		Returns an array of username patterns.
58
+		"""
59
+		patterns: list[str] = self.get_guild_setting(guild, self.SETTING_PATTERNS)
60
+		if patterns is None:
61
+			patterns = []
62
+			Storage.set_config_value(guild, 'UsernamePatternCog.patterns', patterns)
63
+		return patterns
64
+
65
+	@classmethod
66
+	def __save_patterns(cls,
67
+			guild: Guild,
68
+			patterns: list[str]) -> None:
69
+		"""
70
+		Saves username pattern array.
71
+		"""
72
+		cls.set_guild_setting(guild, cls.SETTING_PATTERNS, patterns)
73
+
74
+	@commands.group(
75
+		brief='Manages username pattern detection'
76
+	)
77
+	@commands.has_permissions(ban_members=True)
78
+	@commands.guild_only()
79
+	async def username(self, context: commands.Context):
80
+		'Username pattern command group'
81
+		if context.invoked_subcommand is None:
82
+			await context.send_help()
83
+
84
+	@username.command(
85
+		brief='Adds a username pattern',
86
+		description='Adds a username pattern.',
87
+		usage='<pattern>'
88
+	)
89
+	async def add(self, context: commands.Context, pattern: str) -> None:
90
+		'Command handler'
91
+		norm_pattern = pattern.lower()
92
+		patterns: list[str] = self.__get_patterns(context.guild)
93
+		if norm_pattern in patterns:
94
+			await context.reply(f'Pattern `{norm_pattern}` already added.', mention_author=False)
95
+			return
96
+		patterns.append(norm_pattern)
97
+		self.__save_patterns(context.guild, patterns)
98
+		await context.reply(f'Pattern `{norm_pattern}` added.', mention_author=False)
99
+
100
+	@username.command(
101
+		brief='Removes a username pattern',
102
+		description='Removes an existing username pattern',
103
+		usage='<pattern>'
104
+	)
105
+	async def remove(self, context: commands.Context, pattern: str) -> None:
106
+		'Command handler'
107
+		norm_pattern = pattern.lower()
108
+		guild: Guild = context.guild
109
+		patterns: list[str] = self.__get_patterns(guild)
110
+		len_before = len(patterns)
111
+		patterns = list(filter(lambda p: p != norm_pattern, patterns))
112
+		if len(patterns) == len_before:
113
+			await context.reply(f'Pattern `{norm_pattern}` not found.', mention_author=False)
114
+			return
115
+		self.__save_patterns(guild, patterns)
116
+		await context.reply(f'Pattern `{norm_pattern}` removed.', mention_author=False)
117
+
118
+	@username.command(
119
+		brief='Lists username patterns'
120
+	)
121
+	async def list(self, context: commands.Context) -> None:
122
+		'Command handler'
123
+		guild: Guild = context.guild
124
+		patterns: list[str] = self.__get_patterns(guild)
125
+		if len(patterns) == 0:
126
+			await context.reply('No patterns defined', mention_author=False)
127
+		else:
128
+			msg = 'Patterns:\n\n> `' + '`\n> `'.join(patterns) + '`'
129
+			await context.reply(msg, mention_author=False)
130
+
131
+	@commands.Cog.listener()
132
+	async def on_member_join(self, member: Member) -> None:
133
+		'Event handler'
134
+		for pattern in self.__get_patterns(member.guild):
135
+			if self.matches(pattern, member.name):
136
+				await self.handle_match(member, pattern)
137
+			elif self.matches(pattern, member.display_name):
138
+				await self.handle_match(member, pattern)
139
+
140
+	def matches(self, pattern: str, subject: str) -> bool:
141
+		'Checks if a username matches a given pattern'
142
+		if pattern is None:
143
+			return False
144
+		if subject is None:
145
+			return False
146
+		return pattern.lower() in subject.lower()
147
+
148
+	async def handle_match(self, member: Member, pattern: str) -> None:
149
+		"""
150
+		Handles a username match.
151
+		"""
152
+		# TODO: Prevent double handling?
153
+		self.log(member.guild, f'User {member.id} {member.display_name} matches pattern "{pattern}"')
154
+		context = UsernamePatternContext(member)
155
+		bm = BotMessage(
156
+			member.guild,
157
+			f'User {member.mention} ({str(member.id)}, {member.display_name}) has ' +
158
+			f'username matching pattern `{pattern}`.',
159
+			BotMessage.TYPE_MOD_WARNING,
160
+			context)
161
+		await bm.set_reactions(context.reactions())
162
+		await self.post_message(bm)
163
+
164
+	async def on_mod_react(self,
165
+			bot_message: BotMessage,
166
+			reaction: BotMessageReaction,
167
+			reacted_by: Member) -> None:
168
+		context: UsernamePatternContext = bot_message.context
169
+		if reaction.emoji == CONFIG['kick_emoji']:
170
+			await context.member.kick(
171
+				reason=f'Rocketbot: Flagged username pattern. Kicked by {reacted_by.name}.')
172
+			context.kicked_by = reacted_by
173
+			self.log(context.member.guild, f'User {context.member.name} kicked by {reacted_by.name}')
174
+			await bot_message.set_reactions(context.reactions())
175
+		elif reaction.emoji == CONFIG['ban_emoji']:
176
+			await context.member.ban(
177
+				reason=f'Rocketbot: Flagged username pattern. Banned by {reacted_by.name}.',
178
+				delete_message_days=0)
179
+			context.banned_by = reacted_by
180
+			self.log(context.member.guild, f'User {context.member.name} banned by {reacted_by.name}')
181
+			await bot_message.set_reactions(context.reactions())
182
+		elif reaction.emoji == CONFIG['ignore_emoji']:
183
+			context.ignored_by = reacted_by
184
+			self.log(context.member.guild, f'Warning ignored by {reacted_by.name}')
185
+			await bot_message.set_reactions(context.reactions())

Loading…
İptal
Kaydet