瀏覽代碼

Adding JoinAgeCog for searching for recent users

master
Rocketsoup 2 年之前
父節點
當前提交
76ee107ad6
共有 3 個文件被更改,包括 152 次插入0 次删除
  1. 2
    0
      bot.py
  2. 3
    0
      config.py.sample
  3. 147
    0
      rocketbot/cogs/joinagecog.py

+ 2
- 0
bot.py 查看文件

@@ -13,6 +13,7 @@ from config import CONFIG
13 13
 from rocketbot.cogs.configcog import ConfigCog
14 14
 from rocketbot.cogs.crosspostcog import CrossPostCog
15 15
 from rocketbot.cogs.generalcog import GeneralCog
16
+from rocketbot.cogs.joinagecog import JoinAgeCog
16 17
 from rocketbot.cogs.joinraidcog import JoinRaidCog
17 18
 from rocketbot.cogs.patterncog import PatternCog
18 19
 from rocketbot.cogs.urlspamcog import URLSpamCog
@@ -63,6 +64,7 @@ bot.add_cog(ConfigCog(bot))
63 64
 
64 65
 # Optional
65 66
 bot.add_cog(CrossPostCog(bot))
67
+bot.add_cog(JoinAgeCog(bot))
66 68
 bot.add_cog(JoinRaidCog(bot))
67 69
 bot.add_cog(PatternCog(bot))
68 70
 bot.add_cog(URLSpamCog(bot))

+ 3
- 0
config.py.sample 查看文件

@@ -20,6 +20,9 @@ CONFIG = {
20 20
 			'minlength': 1,
21 21
 			'timespan': 60,
22 22
 		},
23
+		'JoinAgeCog': {
24
+			'jointime': 3600,
25
+		},
23 26
 		'JoinRaidCog': {
24 27
 			'enabled': False,
25 28
 			'warning_count': 5,

+ 147
- 0
rocketbot/cogs/joinagecog.py 查看文件

@@ -0,0 +1,147 @@
1
+import weakref
2
+
3
+from datetime import datetime, timedelta
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.collections import AgeBoundList
10
+from rocketbot.storage import Storage
11
+from rocketbot.utils import timedelta_from_str
12
+
13
+class JoinAgeQueryContext:
14
+	"""
15
+	Data about a join age query
16
+	"""
17
+	def __init__(self, join_members: list, timespan: str):
18
+		self.join_members = list(join_members)
19
+		self.timespan = timespan
20
+		self.kicked_members = set()
21
+		self.banned_members = set()
22
+		self.results_message_ref = None
23
+
24
+class JoinAgeCog(BaseCog, name='Join Age'):
25
+	"""
26
+	Cog for finding users by when they joined.
27
+	"""
28
+	SETTING_ENABLED = CogSetting('enabled', bool,
29
+		brief='join age',
30
+		description='Whether this cog is enabled for a guild.')
31
+	SETTING_JOIN_TIME = CogSetting('jointime', float,
32
+		brief='maximum length of time to track new joins',
33
+		description='The number of seconds of join history to maintain.',
34
+		usage='<seconds:float>',
35
+		min_value=1.0)
36
+
37
+	STATE_KEY_RECENT_JOINS = "JoinAgeCog.recent_joins"
38
+
39
+	def __init__(self, bot):
40
+		super().__init__(bot)
41
+		self.add_setting(JoinAgeCog.SETTING_ENABLED)
42
+		self.add_setting(JoinAgeCog.SETTING_JOIN_TIME)
43
+
44
+	@commands.group(
45
+		brief='Tracks recently joined users with options to mass kick or ban',
46
+	)
47
+	@commands.has_permissions(ban_members=True)
48
+	@commands.guild_only()
49
+	async def joinage(self, context: commands.Context):
50
+		'Join age tracking'
51
+		if context.invoked_subcommand is None:
52
+			await context.send_help()
53
+
54
+	@joinage.command(
55
+		brief='Queries for users who joined in the past span of time',
56
+		description='Searches for users who joined the server recently. ' + \
57
+			'Can use time spans like 30s, 5m, 1h, 7d, etc.',
58
+		usage='<time_period>'
59
+	)
60
+	async def search(self, context: commands.Context, timespan: str):
61
+		'Command handler'
62
+		guild: Guild = context.guild
63
+		recent_joins: AgeBoundList = Storage.get_state_value(guild, self.STATE_KEY_RECENT_JOINS)
64
+		if recent_joins is None:
65
+			max_age: timedelta = timedelta(seconds=self.get_guild_setting(guild, self.SETTING_JOIN_TIME))
66
+			recent_joins = AgeBoundList(max_age, lambda i, member : member.joined_at)
67
+			Storage.set_state_value(guild, self.STATE_KEY_RECENT_JOINS, recent_joins)
68
+		results: list = []
69
+		ts: timedelta = timedelta_from_str(timespan)
70
+		cutoff: datetime = datetime.utcnow() - ts
71
+		for member in recent_joins:
72
+			if member.joined_at > cutoff:
73
+				results.append(member)
74
+		ctx = JoinAgeQueryContext(results, timespan)
75
+		msg = BotMessage(guild,
76
+						text='',
77
+						type=BotMessage.TYPE_INFO,
78
+						context=ctx)
79
+		ctx.results_message_ref = weakref.ref(msg)
80
+		await self.__update_results_message(ctx)
81
+		await self.post_message(msg)
82
+
83
+	async def on_mod_react(self,
84
+			bot_message: BotMessage,
85
+			reaction: BotMessageReaction,
86
+			reacted_by: Member) -> None:
87
+		guild: Guild = bot_message.guild
88
+		ctx: JoinAgeQueryContext = bot_message.context
89
+		if reaction.emoji == CONFIG['kick_emoji']:
90
+			to_kick = set(ctx.join_members) - ctx.kicked_members
91
+			for member in to_kick:
92
+				await member.kick(
93
+					reason=f'Rocketbot: Mass kick based on join age, by {reacted_by.name}.')
94
+			ctx.kicked_members |= to_kick
95
+			await self.__update_results_message(ctx)
96
+			self.log(guild, f'Users kicked by {reacted_by.name}.')
97
+		elif reaction.emoji == CONFIG['ban_emoji']:
98
+			to_ban = set(ctx.join_members) - ctx.banned_members
99
+			for member in to_ban:
100
+				await member.ban(
101
+					reason=f'Rocketbot: Mass ban based on join age, by {reacted_by.name}.',
102
+					delete_message_days=0)
103
+			ctx.banned_members |= to_ban
104
+			await self.__update_results_message(ctx)
105
+			self.log(guild, f'Users banned by {reacted_by.name}')
106
+
107
+	@commands.Cog.listener()
108
+	async def on_member_join(self, member: Member) -> None:
109
+		'Event handler'
110
+		guild: Guild = member.guild
111
+		if not self.get_guild_setting(guild, self.SETTING_ENABLED):
112
+			return
113
+		recent_joins: AgeBoundList = Storage.get_state_value(guild, self.STATE_KEY_RECENT_JOINS)
114
+		if recent_joins is None:
115
+			max_age: timedelta = timedelta(seconds=self.get_guild_setting(guild, self.SETTING_JOIN_TIME))
116
+			recent_joins = AgeBoundList(max_age, lambda i, member : member.joined_at)
117
+			Storage.set_state_value(guild, self.STATE_KEY_RECENT_JOINS, recent_joins)
118
+		recent_joins.append(member)
119
+
120
+	async def __update_results_message(self, context: JoinAgeQueryContext) -> None:
121
+		if context.results_message_ref is None:
122
+			return
123
+		bot_message = context.results_message_ref()
124
+		if bot_message is None:
125
+			return
126
+		text = f'The following members joined in the last {context.timespan}\n\n'
127
+		if len(context.join_members) > 0:
128
+			for member in context.join_members:
129
+				text += '\n• '
130
+				if member in context.banned_members:
131
+					text += f'~~{member.mention} ({member.id})~~ - banned'
132
+				elif member in context.kicked_members:
133
+					text += f'~~{member.mention} ({member.id})~~ - kicked'
134
+				else:
135
+					text += f'{member.mention} ({member.id})'
136
+		else:
137
+			text += 'No members found. If the bot was recently restarted ' + \
138
+				'or JoinAgeCog just enabled, only new joins will be tracked.'
139
+		await bot_message.set_text(text)
140
+		if len(context.join_members) > 0:
141
+			member_count = len(context.join_members)
142
+			kick_count = len(context.kicked_members)
143
+			ban_count = len(context.banned_members)
144
+			await bot_message.set_reactions(BotMessageReaction.standard_set(
145
+				did_kick=kick_count >= member_count,
146
+				did_ban=ban_count >= member_count,
147
+				user_count=member_count))

Loading…
取消
儲存