Skip to content

Commit 38afc67

Browse files
Generate new Discord invite-link fallback if missing CUSTOM_DISCORD_INVITE_URL (#504)
1 parent e3075f4 commit 38afc67

File tree

5 files changed

+88
-40
lines changed

5 files changed

+88
-40
lines changed

.env.example

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ MEMBERSHIP_PERKS_URL=[Replace with your group\'s membership-perks URL]
3232

3333
# The invite link URL to allow users to join your community group's Discord server
3434
# Must be a valid URL
35-
DISCORD_INVITE_URL=[Replace with your group\'s Discord server invite link]
35+
CUSTOM_DISCORD_INVITE_URL=[Replace with your group\'s Discord server invite link]
3636

3737

3838
# The minimum level that logs must meet in order to be logged to the console output stream
@@ -54,7 +54,7 @@ MEMBERS_LIST_URL_SESSION_COOKIE=[Replace with your .ASPXAUTH cookie]
5454

5555

5656
# The probability that the more rare ping command response will be sent instead of the normal one
57-
# Must be a float between & including 1 & 0
57+
# Must be a float between & including 0 to 1
5858
PING_COMMAND_EASTER_EGG_PROBABILITY=0.01
5959

6060

cogs/invite_link.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,27 @@ class InviteLinkCommandCog(TeXBotBaseCog):
2323
)
2424
async def invite_link(self, ctx: "TeXBotApplicationContext") -> None: # type: ignore[misc]
2525
"""Definition & callback response of the "invite-link" command."""
26+
discord_invite_url: str | None = settings["CUSTOM_DISCORD_INVITE_URL"]
27+
28+
if not discord_invite_url:
29+
invite_destination_channel: discord.TextChannel | None = discord.utils.get(
30+
ctx.bot.main_guild.text_channels, name="welcome"
31+
)
32+
33+
if invite_destination_channel is None:
34+
invite_destination_channel = await ctx.bot.rules_channel
35+
36+
discord_invite_url = (
37+
await invite_destination_channel.create_invite(
38+
reason=f'{ctx.user} used TeX Bot slash-command: "/invite-link"',
39+
max_age=21600,
40+
)
41+
).url
42+
2643
await ctx.respond(
2744
(
2845
f"Invite your friends to the {self.bot.group_short_name} Discord server: "
29-
f"{settings['DISCORD_INVITE_URL']}"
46+
f"{discord_invite_url}"
3047
),
3148
ephemeral=False,
3249
)

cogs/make_member.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ async def make_member(self, ctx: "TeXBotApplicationContext", group_member_id: st
226226
# NOTE: The "Member" role must be added to the user **before** the "Guest" role to ensure that the welcome message does not include the suggestion to purchase membership
227227
await interaction_member.add_roles(
228228
member_role,
229-
reason='TeX Bot slash-command: "/makemember"',
229+
reason=f'{ctx.user} used TeX Bot slash-command: "/makemember"',
230230
)
231231

232232
try:
@@ -258,7 +258,7 @@ async def make_member(self, ctx: "TeXBotApplicationContext", group_member_id: st
258258
if guest_role not in interaction_member.roles:
259259
await interaction_member.add_roles(
260260
guest_role,
261-
reason='TeX Bot slash-command: "/makemember"',
261+
reason=f'{ctx.user} used TeX Bot slash-command: "/makemember"',
262262
)
263263
applicant_role: discord.Role | None
264264
try:
@@ -269,7 +269,7 @@ async def make_member(self, ctx: "TeXBotApplicationContext", group_member_id: st
269269
if applicant_role and applicant_role in interaction_member.roles:
270270
await interaction_member.remove_roles(
271271
applicant_role,
272-
reason='TeX Bot slash-command: "/makemember"',
272+
reason=f'{ctx.user} used TeX Bot slash-command: "/makemember"',
273273
)
274274

275275

config.py

Lines changed: 63 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -233,11 +233,14 @@ def _setup_discord_guild_id(cls) -> None:
233233
def _setup_group_full_name(cls) -> None:
234234
raw_group_full_name: str | None = os.getenv("GROUP_NAME")
235235

236-
GROUP_FULL_NAME_IS_VALID: Final[bool] = bool(
237-
not raw_group_full_name
238-
or re.fullmatch(r"\A[A-Za-z0-9 '&!?:,.#%\"-]+\Z", raw_group_full_name),
239-
)
240-
if not GROUP_FULL_NAME_IS_VALID:
236+
if raw_group_full_name is not None:
237+
raw_group_full_name = raw_group_full_name.strip()
238+
239+
if not raw_group_full_name:
240+
cls._settings["_GROUP_FULL_NAME"] = None
241+
return
242+
243+
if not re.fullmatch(r"\A[A-Za-z0-9 '&!?:,.#%\"-]+\Z", raw_group_full_name):
241244
INVALID_GROUP_FULL_NAME: Final[str] = (
242245
"GROUP_NAME must not contain any invalid characters."
243246
)
@@ -249,11 +252,14 @@ def _setup_group_full_name(cls) -> None:
249252
def _setup_group_short_name(cls) -> None:
250253
raw_group_short_name: str | None = os.getenv("GROUP_SHORT_NAME")
251254

252-
GROUP_SHORT_NAME_IS_VALID: Final[bool] = bool(
253-
not raw_group_short_name
254-
or re.fullmatch(r"\A[A-Za-z0-9'&!?:,.#%\"-]+\Z", raw_group_short_name),
255-
)
256-
if not GROUP_SHORT_NAME_IS_VALID:
255+
if raw_group_short_name is not None:
256+
raw_group_short_name = raw_group_short_name.strip()
257+
258+
if not raw_group_short_name:
259+
cls._settings["_GROUP_SHORT_NAME"] = None
260+
return
261+
262+
if not re.fullmatch(r"\A[A-Za-z0-9'&!?:,.#%\"-]+\Z", raw_group_short_name):
257263
INVALID_GROUP_SHORT_NAME: Final[str] = (
258264
"GROUP_SHORT_NAME must not contain any invalid characters."
259265
)
@@ -265,10 +271,14 @@ def _setup_group_short_name(cls) -> None:
265271
def _setup_purchase_membership_url(cls) -> None:
266272
raw_purchase_membership_url: str | None = os.getenv("PURCHASE_MEMBERSHIP_URL")
267273

268-
PURCHASE_MEMBERSHIP_URL_IS_VALID: Final[bool] = bool(
269-
not raw_purchase_membership_url or validators.url(raw_purchase_membership_url),
270-
)
271-
if not PURCHASE_MEMBERSHIP_URL_IS_VALID:
274+
if raw_purchase_membership_url is not None:
275+
raw_purchase_membership_url = raw_purchase_membership_url.strip()
276+
277+
if not raw_purchase_membership_url:
278+
cls._settings["PURCHASE_MEMBERSHIP_URL"] = None
279+
return
280+
281+
if not validators.url(raw_purchase_membership_url):
272282
INVALID_PURCHASE_MEMBERSHIP_URL_MESSAGE: Final[str] = (
273283
"PURCHASE_MEMBERSHIP_URL must be a valid URL."
274284
)
@@ -280,10 +290,14 @@ def _setup_purchase_membership_url(cls) -> None:
280290
def _setup_membership_perks_url(cls) -> None:
281291
raw_membership_perks_url: str | None = os.getenv("MEMBERSHIP_PERKS_URL")
282292

283-
MEMBERSHIP_PERKS_URL_IS_VALID: Final[bool] = bool(
284-
not raw_membership_perks_url or validators.url(raw_membership_perks_url),
285-
)
286-
if not MEMBERSHIP_PERKS_URL_IS_VALID:
293+
if raw_membership_perks_url is not None:
294+
raw_membership_perks_url = raw_membership_perks_url.strip()
295+
296+
if not raw_membership_perks_url:
297+
cls._settings["MEMBERSHIP_PERKS_URL"] = None
298+
return
299+
300+
if not validators.url(raw_membership_perks_url):
287301
INVALID_MEMBERSHIP_PERKS_URL_MESSAGE: Final[str] = (
288302
"MEMBERSHIP_PERKS_URL must be a valid URL."
289303
)
@@ -292,30 +306,47 @@ def _setup_membership_perks_url(cls) -> None:
292306
cls._settings["MEMBERSHIP_PERKS_URL"] = raw_membership_perks_url
293307

294308
@classmethod
295-
def _setup_discord_invite_url(cls) -> None:
296-
raw_discord_invite_url: str | None = os.getenv("DISCORD_INVITE_URL")
309+
def _setup_custom_discord_invite_url(cls) -> None:
310+
raw_custom_discord_invite_url: str | None = os.getenv("CUSTOM_DISCORD_INVITE_URL")
297311

298-
DISCORD_INVITE_URL_IS_VALID: Final[bool] = bool(
299-
not raw_discord_invite_url or validators.url(raw_discord_invite_url),
300-
)
301-
if not DISCORD_INVITE_URL_IS_VALID:
302-
INVALID_DISCORD_INVITE_URL_MESSAGE: Final[str] = (
303-
"DISCORD_INVITE_URL must be a valid URL."
312+
if raw_custom_discord_invite_url is not None:
313+
raw_custom_discord_invite_url = raw_custom_discord_invite_url.strip()
314+
315+
if not raw_custom_discord_invite_url:
316+
cls._settings["CUSTOM_DISCORD_INVITE_URL"] = None
317+
return
318+
319+
if not validators.url(raw_custom_discord_invite_url):
320+
INVALID_CUSTOM_DISCORD_INVITE_URL_MESSAGE: Final[str] = (
321+
"CUSTOM_DISCORD_INVITE_URL must be a valid URL."
304322
)
305-
raise ImproperlyConfiguredError(INVALID_DISCORD_INVITE_URL_MESSAGE)
323+
raise ImproperlyConfiguredError(INVALID_CUSTOM_DISCORD_INVITE_URL_MESSAGE)
306324

307-
cls._settings["DISCORD_INVITE_URL"] = raw_discord_invite_url
325+
cls._settings["CUSTOM_DISCORD_INVITE_URL"] = raw_custom_discord_invite_url
308326

309327
@classmethod
310328
def _setup_ping_command_easter_egg_probability(cls) -> None:
329+
raw_ping_command_easter_egg_probability_string: str | None = os.getenv(
330+
"PING_COMMAND_EASTER_EGG_PROBABILITY"
331+
)
332+
333+
if raw_ping_command_easter_egg_probability_string is not None:
334+
raw_ping_command_easter_egg_probability_string = (
335+
raw_ping_command_easter_egg_probability_string.strip()
336+
)
337+
338+
if not raw_ping_command_easter_egg_probability_string:
339+
cls._settings["PING_COMMAND_EASTER_EGG_PROBABILITY"] = 1
340+
return
341+
311342
INVALID_PING_COMMAND_EASTER_EGG_PROBABILITY_MESSAGE: Final[str] = (
312-
"PING_COMMAND_EASTER_EGG_PROBABILITY must be a float between & including 1 & 0."
343+
"PING_COMMAND_EASTER_EGG_PROBABILITY must be a float between & including 0 to 1."
313344
)
314345

315346
e: ValueError
316347
try:
317348
raw_ping_command_easter_egg_probability: float = 100 * float(
318-
os.getenv("PING_COMMAND_EASTER_EGG_PROBABILITY", "0.01"),
349+
raw_ping_command_easter_egg_probability_string
319350
)
320351
except ValueError as e:
321352
raise (
@@ -324,7 +355,7 @@ def _setup_ping_command_easter_egg_probability(cls) -> None:
324355

325356
if not 0 <= raw_ping_command_easter_egg_probability <= 100:
326357
raise ImproperlyConfiguredError(
327-
INVALID_PING_COMMAND_EASTER_EGG_PROBABILITY_MESSAGE,
358+
INVALID_PING_COMMAND_EASTER_EGG_PROBABILITY_MESSAGE
328359
)
329360

330361
cls._settings["PING_COMMAND_EASTER_EGG_PROBABILITY"] = (
@@ -753,7 +784,7 @@ def _setup_env_variables(cls) -> None:
753784
cls._setup_members_list_auth_session_cookie()
754785
cls._setup_membership_perks_url()
755786
cls._setup_purchase_membership_url()
756-
cls._setup_discord_invite_url()
787+
cls._setup_custom_discord_invite_url()
757788
cls._setup_send_introduction_reminders()
758789
cls._setup_send_introduction_reminders_delay()
759790
cls._setup_send_introduction_reminders_interval()

utils/tex_bot.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ async def archivist_role(self) -> discord.Role:
196196
"""
197197
Shortcut accessor to the archivist role.
198198
199-
The archivist role is the one that allows members to see channels & categories
199+
The archivist role is the one that allows members to see channels and categories
200200
that are no longer in use, which are hidden from all other members.
201201
202202
Raises `ArchivistRoleDoesNotExist` if the role does not exist.
@@ -268,7 +268,7 @@ async def rules_channel(self) -> discord.TextChannel:
268268
"""
269269
Shortcut accessor to the rules text channel.
270270
271-
The rules text channel is the one that contains the welcome message & rules.
271+
The rules text channel is the one that contains the welcome message and rules.
272272
273273
Raises `RulesChannelDoesNotExist` if the channel does not exist.
274274
"""

0 commit comments

Comments
 (0)