Przeglądaj źródła

Adding Kickstarter cog, most of support for both email and username based attribution

pull/35/head
Rocketsoup 2 tygodni temu
rodzic
commit
f721914df7

+ 1
- 1
requirements.txt Wyświetl plik

@@ -1,2 +1,2 @@
1
-discord.py == 2.6.4
1
+discord.py == 2.7.1
2 2
 yt-dlp == 2026.07.04

+ 3
- 0
rocketbot/bot.py Wyświetl plik

@@ -99,6 +99,7 @@ async def start_bot():
99 99
 	from rocketbot.cogs.generalcog import GeneralCog
100 100
 	from rocketbot.cogs.helpcog import HelpCog
101 101
 	from rocketbot.cogs.joinraidcog import JoinRaidCog
102
+	from rocketbot.cogs.kickstartercog import KickstarterCog
102 103
 	from rocketbot.cogs.logcog import LoggingCog
103 104
 	from rocketbot.cogs.patterncog import PatternCog
104 105
 	from rocketbot.cogs.urlspamcog import URLSpamCog
@@ -134,6 +135,8 @@ async def start_bot():
134 135
 		await rocketbot.add_cog(UsernamePatternCog(rocketbot))
135 136
 	if VideoPreviewCog.supports_intents(rocketbot.intents):
136 137
 		await rocketbot.add_cog(VideoPreviewCog(rocketbot))
138
+	if KickstarterCog.supports_intents(rocketbot.intents):
139
+		await rocketbot.add_cog(KickstarterCog(rocketbot))
137 140
 
138 141
 	await rocketbot.start(CONFIG['client_token'], reconnect=True)
139 142
 	print('\nBot aborted')

+ 6
- 1
rocketbot/cogs/basecog.py Wyświetl plik

@@ -67,6 +67,7 @@ class BaseCog(Cog):
67 67
 	async def cog_app_command_error(self, interaction: Interaction, error: AppCommandError) -> None:
68 68
 		if isinstance(error, CommandInvokeError):
69 69
 			error = error.original
70
+		bot_log(interaction.guild, type(self), f"Error in {interaction.command.qualified_name if interaction.command else 'unknown command'}")
70 71
 		dump_stacktrace(error)
71 72
 		message = f"\nException: {error.__class__.__name__}, "\
72 73
 				  f"Command: {interaction.command.qualified_name if interaction.command else None}, "\
@@ -75,7 +76,11 @@ class BaseCog(Cog):
75 76
 		try:
76 77
 			await interaction.response.send_message(f"An error occurred: {message}", ephemeral=True)
77 78
 		except discord.InteractionResponded:
78
-			await interaction.followup.send(f"An error occurred: {message}", ephemeral=True)
79
+			try:
80
+				await interaction.followup.send(f"An error occurred: {message}", ephemeral=True)
81
+			except BaseException:
82
+				interaction.channel.send
83
+				bot_log(interaction.guild, None, message)
79 84
 
80 85
 	@property
81 86
 	def basecogs(self) -> list['BaseCog']:

+ 1339
- 0
rocketbot/cogs/kickstartercog.py
Plik diff jest za duży
Wyświetl plik


+ 10
- 0
rocketbot/utils.py Wyświetl plik

@@ -135,6 +135,8 @@ __ID_REGEX: re.Pattern = re.compile('^[0-9]{17,20}$')
135 135
 __MENTION_REGEX: re.Pattern = re.compile('^<@[!&]([0-9]{17,20})>$')
136 136
 __USER_MENTION_REGEX: re.Pattern = re.compile('^<@!([0-9]{17,20})>$')
137 137
 __ROLE_MENTION_REGEX: re.Pattern = re.compile('^<@&([0-9]{17,20})>$')
138
+__EMAIL_REGEX: re.Pattern = re.compile(r'^(?:(?:[^<>()\[\]\\.,;:\s@"]+(?:\.[^<>()\[\]\\.,;:\s@"]+)*)|(?:".+"))@(?:(?:\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(?:(?:[a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$')
139
+__USERNAME_REGEX: re.Pattern = re.compile(r'[a-z0-9\._]{2,32}')
138 140
 
139 141
 def is_user_id(val: str) -> bool:
140 142
 	"""Tests if a string is in user/role ID format."""
@@ -152,6 +154,14 @@ def is_user_mention(val: str) -> bool:
152 154
 	"""Tests if a string is a user mention."""
153 155
 	return __USER_MENTION_REGEX.match(val) is not None
154 156
 
157
+def is_email_address(val: str) -> bool:
158
+	"""Tests if a string is a well-formed email address."""
159
+	return __EMAIL_REGEX.match(val) is not None
160
+
161
+def is_discord_username(val: str) -> bool:
162
+	"""Tests if a string is a properly formatted Discord username."""
163
+	return __USERNAME_REGEX.match(val.lower())
164
+
155 165
 def user_id_from_mention(mention: str) -> str:
156 166
 	"""Extracts the user ID from a mention. Raises a ValueError if malformed."""
157 167
 	m = __USER_MENTION_REGEX.match(mention)

+ 29
- 0
sql/kickstarter-create.sql Wyświetl plik

@@ -0,0 +1,29 @@
1
+CREATE TABLE IF NOT EXISTS kickstarter_emails (
2
+	pk INTEGER PRIMARY KEY AUTOINCREMENT,
3
+	guild_id INTEGER NOT NULL,  -- Discord Guild.id
4
+	email_hash TEXT NOT NULL,  -- base64(sha256(email.lower.trimmed))
5
+	imported_at INTEGER NOT NULL,  -- unix timestamp
6
+	UNIQUE(guild_id, email_hash) ON CONFLICT FAIL
7
+);
8
+
9
+CREATE TABLE IF NOT EXISTS kickstarter_discord_users (
10
+	pk INTEGER PRIMARY KEY AUTOINCREMENT,
11
+	guild_id INTEGER NOT NULL,  -- Discord Guild.id
12
+	discord_username TEXT NOT NULL,  -- Discord Member.name
13
+	discord_member_id INTEGER DEFAULT NULL,  -- resolved member id
14
+	lookup_status INTEGER NOT NULL DEFAULT 0,  -- enum defined in code
15
+	imported_at INTEGER NOT NULL,  -- unix timestamp
16
+	UNIQUE(guild_id, discord_username COLLATE NOCASE) ON CONFLICT FAIL
17
+);
18
+CREATE INDEX IF NOT EXISTS idx_kickstarter_discord_users_discord_member_id
19
+	ON kickstarter_discord_users (discord_member_id);
20
+
21
+CREATE TABLE IF NOT EXISTS member_links (
22
+	pk INTEGER PRIMARY KEY AUTOINCREMENT,
23
+	guild_id INTEGER NOT NULL,  -- Discord Guild.id
24
+	member_id INTEGER NOT NULL,  -- Discord Member.id
25
+	email_hash TEXT NOT NULL,  -- base64(sha256(email.lower.trimmed))
26
+	complete INTEGER NOT NULL DEFAULT 0,  -- link found, role assigned successfully
27
+	UNIQUE(guild_id, member_id) ON CONFLICT FAIL,
28
+	UNIQUE(guild_id, email_hash) ON CONFLICT FAIL
29
+);

Ładowanie…
Anuluj
Zapisz