Bläddra i källkod

Adding JoinAgeCog for searching for recent users

master
Rocketsoup 2 år sedan
förälder
incheckning
76ee107ad6
3 ändrade filer med 152 tillägg och 0 borttagningar
  1. 2
    0
      bot.py
  2. 3
    0
      config.py.sample
  3. 147
    0
      rocketbot/cogs/joinagecog.py

+ 2
- 0
bot.py Visa fil

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

+ 3
- 0
config.py.sample Visa fil

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

+ 147
- 0
rocketbot/cogs/joinagecog.py Visa fil

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))

Laddar…
Avbryt
Spara