Rocketsoup 1 год назад
Родитель
Сommit
dae32e26b6
1 измененных файлов: 228 добавлений и 0 удалений
  1. 228
    0
      rocketbot/cogs/logcog.py

+ 228
- 0
rocketbot/cogs/logcog.py Просмотреть файл

@@ -0,0 +1,228 @@
1
+"""
2
+Cog for detecting large numbers of guild joins in a short period of time.
3
+"""
4
+import weakref
5
+from collections.abc import Sequence
6
+from datetime import datetime, timedelta
7
+from discord import Emoji, Guild, GuildChannel, GuildSticker, Invite, Member, Message, Role, Thread, User
8
+from discord.ext import commands
9
+from typing import List, Union
10
+
11
+from config import CONFIG
12
+from rocketbot.cogs.basecog import BaseCog, BotMessage, BotMessageReaction, CogSetting
13
+from rocketbot.collections import AgeBoundList
14
+from rocketbot.storage import Storage
15
+
16
+class LogCog(BaseCog, name='Logging'):
17
+	"""
18
+	Cog for logging notable events to a designated logging channel.
19
+	"""
20
+	SETTING_ENABLED = CogSetting('enabled', bool,
21
+			brief='logging',
22
+			description='Whether this cog is enabled for a guild.')
23
+	SETTING_EDITS_ENABLED = CogSetting('edits_enabled', bool,
24
+			brief='post edits',
25
+			description='Whether to log when users edit their posts.')
26
+	SETTING_JOINS_ENABLED = CogSetting('joins_enabled', bool,
27
+			brief='joins',
28
+			description='Whether to log when new users join the server.')
29
+	SETTING_LEAVES_ENABLED = CogSetting('leaves_enabled', bool,
30
+			brief='leaves',
31
+			description='Whether to log when users leave the server.')
32
+
33
+	def __init__(self, bot):
34
+		super().__init__(bot)
35
+		self.add_setting(LogCog.SETTING_ENABLED)
36
+		self.add_setting(LogCog.SETTING_EDITS_ENABLED)
37
+		self.add_setting(LogCog.SETTING_JOINS_ENABLED)
38
+		self.add_setting(LogCog.SETTING_LEAVES_ENABLED)
39
+
40
+	@commands.group(
41
+		brief='Manages event logging',
42
+	)
43
+	@commands.has_permissions(ban_members=True)
44
+	@commands.guild_only()
45
+	async def log(self, context: commands.Context):
46
+		'Logging command group'
47
+		if context.invoked_subcommand is None:
48
+			await context.send_help()
49
+
50
+	# Events - Channels
51
+
52
+	@commands.Cog.listener()
53
+	async def on_guild_channel_delete(self, channel: GuildChannel) -> None:
54
+		pass
55
+
56
+	@commands.Cog.listener()
57
+	async def on_guild_channel_create(self, channel: GuildChannel) -> None:
58
+		pass
59
+
60
+	@commands.Cog.listener()
61
+	async def on_guild_channel_update(self, before: GuildChannel, after: GuildChannel) -> None:
62
+		pass
63
+
64
+	# Events - Guilds
65
+
66
+	@commands.Cog.listener()
67
+	async def on_guild_available(self, guild: Guild) -> None:
68
+		pass
69
+
70
+	@commands.Cog.listener()
71
+	async def on_guild_unavailable(self, guild: Guild) -> None:
72
+		pass
73
+
74
+	@commands.Cog.listener()
75
+	async def on_guild_update(self, before: Guild, after: Guild) -> None:
76
+		pass
77
+
78
+	@commands.Cog.listener()
79
+	async def on_guild_emojis_update(self, guild: Guild, before: Sequence[Emoji], after: Sequence[Emoji]) -> None:
80
+		pass
81
+
82
+	@commands.Cog.listener()
83
+	async def on_guild_stickers_update(self, guild: Guild, before: Sequence[GuildSticker], after: Sequence[GuildSticker]) -> None:
84
+		pass
85
+
86
+	@commands.Cog.listener()
87
+	async def on_invite_create(self, invite: Invite) -> None:
88
+		pass
89
+
90
+	@commands.Cog.listener()
91
+	async def on_invite_delete(self, invite: Invite) -> None:
92
+		pass
93
+
94
+	# Events - Members
95
+
96
+	@commands.Cog.listener()
97
+	async def on_member_join(self, member: Member) -> None:
98
+		pass
99
+
100
+	@commands.Cog.listener()
101
+	async def on_member_remove(self, member: Member) -> None:
102
+		pass
103
+
104
+	@commands.Cog.listener()
105
+	async def on_member_update(self, before: Member, after: Member) -> None:
106
+		pass
107
+
108
+	@commands.Cog.listener()
109
+	async def on_user_update(self, before: User, after: User) -> None:
110
+		pass
111
+
112
+	@commands.Cog.listener()
113
+	async def on_member_ban(self, user: Union[User, Member]) -> None:
114
+		pass
115
+
116
+	@commands.Cog.listener()
117
+	async def on_member_unban(self, guild: Guild, user: Union[User, Member]) -> None:
118
+		pass
119
+
120
+	# Events - Messages
121
+
122
+	@commands.Cog.listener()
123
+	async def on_message_edit(self, before: Message, after: Message) -> None:
124
+		pass
125
+
126
+	@commands.Cog.listener()
127
+	async def on_message_delete(self, message: Message) -> None:
128
+		pass
129
+
130
+	@commands.Cog.listener()
131
+	async def on_bulk_message_delete(self, messages: List[Message]) -> None:
132
+		pass
133
+
134
+	# Events - Roles
135
+
136
+	@commands.Cog.listener()
137
+	async def on_guild_role_create(self, role: Role) -> None:
138
+		pass
139
+
140
+	@commands.Cog.listener()
141
+	async def on_guild_role_delete(self, role: Role) -> None:
142
+		pass
143
+
144
+	@commands.Cog.listener()
145
+	async def on_guild_role_update(self, before: Role, after: Role) -> None:
146
+		pass
147
+
148
+	# Events - Threads
149
+
150
+	@commands.Cog.listener()
151
+	async def on_thread_create(self, thread: Thread) -> None:
152
+		pass
153
+
154
+	@commands.Cog.listener()
155
+	async def on_thread_update(self, before: Thread, after: Thread) -> None:
156
+		pass
157
+
158
+	@commands.Cog.listener()
159
+	async def on_thread_delete(self, thread: Thread) -> None:
160
+		pass
161
+
162
+
163
+	def remove_me():
164
+		pass
165
+
166
+
167
+	@commands.Cog.listener()
168
+	async def on_member_join(self, member: Member) -> None:
169
+		'Event handler'
170
+		guild: Guild = member.guild
171
+		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
172
+			return
173
+		min_count = self.get_guild_setting(guild, self.SETTING_JOIN_COUNT)
174
+		seconds = self.get_guild_setting(guild, self.SETTING_JOIN_TIME)
175
+		timespan: timedelta = timedelta(seconds=seconds)
176
+
177
+		last_raid: JoinRaidContext = Storage.get_state_value(guild, self.STATE_KEY_LAST_RAID)
178
+		recent_joins: AgeBoundList = Storage.get_state_value(guild, self.STATE_KEY_RECENT_JOINS)
179
+		if recent_joins is None:
180
+			recent_joins = AgeBoundList(timespan, lambda i, member : member.joined_at)
181
+			Storage.set_state_value(guild, self.STATE_KEY_RECENT_JOINS, recent_joins)
182
+		if last_raid:
183
+			if member.joined_at - last_raid.last_join_time() > timespan:
184
+				# Last raid is over
185
+				Storage.set_state_value(guild, self.STATE_KEY_LAST_RAID, None)
186
+				recent_joins.append(member)
187
+				return
188
+			# Add join to existing raid
189
+			last_raid.join_members.append(member)
190
+			self.record_warning(member)
191
+			if len(last_raid.banned_members) > 0:
192
+				self.log(guild, f'Banning as part of last join raid: {member.name}')
193
+				await member.ban(
194
+					reason='Rocketbot: Part of join raid.',
195
+					delete_message_days=0)
196
+				last_raid.banned_members.add(member)
197
+			elif len(last_raid.kicked_members) > 0:
198
+				self.log(guild, f'Kicking as part of last join raid: {member.name}')
199
+				await member.kick(
200
+					reason='Rocketbot: Part of join raid.')
201
+				last_raid.kicked_members.add(member)
202
+			await self.__update_warning_message(last_raid)
203
+		else:
204
+			# Add join to the general, non-raid recent join list
205
+			recent_joins.append(member)
206
+			if len(recent_joins) >= min_count:
207
+				self.log(guild, '\u0007Join raid detected')
208
+				last_raid = JoinRaidContext(recent_joins)
209
+				Storage.set_state_value(guild, self.STATE_KEY_LAST_RAID, last_raid)
210
+				recent_joins.clear()
211
+				msg = BotMessage(guild,
212
+						text='',
213
+						type=BotMessage.TYPE_MOD_WARNING,
214
+						context=last_raid)
215
+				self.record_warnings(recent_joins)
216
+				last_raid.warning_message_ref = weakref.ref(msg)
217
+				await self.__update_warning_message(last_raid)
218
+				await self.post_message(msg)
219
+
220
+	async def on_setting_updated(self, guild: Guild, setting: CogSetting) -> None:
221
+		if setting is self.SETTING_JOIN_TIME:
222
+			seconds = self.get_guild_setting(guild, self.SETTING_JOIN_TIME)
223
+			timespan: timedelta = timedelta(seconds=seconds)
224
+			recent_joins: AgeBoundList = Storage.get_state_value(guild,
225
+				self.STATE_KEY_RECENT_JOINS)
226
+			if recent_joins:
227
+				recent_joins.max_age = timespan
228
+				recent_joins.purge_old_elements()

Загрузка…
Отмена
Сохранить