diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | bot/exts/info/site.py | 21 | ||||
-rw-r--r-- | bot/exts/moderation/infraction/superstarify.py | 5 | ||||
-rw-r--r-- | bot/exts/moderation/verification.py | 47 |
4 files changed, 60 insertions, 14 deletions
diff --git a/.gitignore b/.gitignore index fb3156ab1..2074887ad 100644 --- a/.gitignore +++ b/.gitignore @@ -110,6 +110,7 @@ ENV/ # Logfiles log.* +*.log.* # Custom user configuration config.yml diff --git a/bot/exts/info/site.py b/bot/exts/info/site.py index 2d3a3d9f3..fb5b99086 100644 --- a/bot/exts/info/site.py +++ b/bot/exts/info/site.py @@ -1,7 +1,7 @@ import logging from discord import Colour, Embed -from discord.ext.commands import Cog, Context, group +from discord.ext.commands import Cog, Context, Greedy, group from bot.bot import Bot from bot.constants import URLs @@ -105,10 +105,9 @@ class Site(Cog): await ctx.send(embed=embed) @site_group.command(name="rules", aliases=("r", "rule"), root_aliases=("rules", "rule")) - async def site_rules(self, ctx: Context, *rules: int) -> None: + async def site_rules(self, ctx: Context, rules: Greedy[int]) -> None: """Provides a link to all rules or, if specified, displays specific rule(s).""" - rules_embed = Embed(title='Rules', color=Colour.blurple()) - rules_embed.url = f"{PAGES_URL}/rules" + rules_embed = Embed(title='Rules', color=Colour.blurple(), url=f'{PAGES_URL}/rules') if not rules: # Rules were not submitted. Return the default description. @@ -122,15 +121,13 @@ class Site(Cog): return full_rules = await self.bot.api_client.get('rules', params={'link_format': 'md'}) - invalid_indices = tuple( - pick - for pick in rules - if pick < 1 or pick > len(full_rules) - ) - if invalid_indices: - indices = ', '.join(map(str, invalid_indices)) - await ctx.send(f":x: Invalid rule indices: {indices}") + # Remove duplicates and sort the rule indices + rules = sorted(set(rules)) + invalid = ', '.join(str(index) for index in rules if index < 1 or index > len(full_rules)) + + if invalid: + await ctx.send(f":x: Invalid rule indices: {invalid}") return for rule in rules: diff --git a/bot/exts/moderation/infraction/superstarify.py b/bot/exts/moderation/infraction/superstarify.py index eec63f5b3..adfe42fcd 100644 --- a/bot/exts/moderation/infraction/superstarify.py +++ b/bot/exts/moderation/infraction/superstarify.py @@ -135,7 +135,8 @@ class Superstarify(InfractionScheduler, Cog): return # Post the infraction to the API - reason = reason or f"old nick: {member.display_name}" + old_nick = member.display_name + reason = reason or f"old nick: {old_nick}" infraction = await _utils.post_infraction(ctx, member, "superstar", reason, duration, active=True) id_ = infraction["id"] @@ -148,7 +149,7 @@ class Superstarify(InfractionScheduler, Cog): await member.edit(nick=forced_nick, reason=reason) self.schedule_expiration(infraction) - old_nick = escape_markdown(member.display_name) + old_nick = escape_markdown(old_nick) forced_nick = escape_markdown(forced_nick) # Send a DM to the user to notify them of their new infraction. diff --git a/bot/exts/moderation/verification.py b/bot/exts/moderation/verification.py index 206556483..c3ad8687e 100644 --- a/bot/exts/moderation/verification.py +++ b/bot/exts/moderation/verification.py @@ -53,6 +53,23 @@ If you'd like to unsubscribe from the announcement notifications, simply send `! <#{constants.Channels.bot_commands}>. """ +ALTERNATE_VERIFIED_MESSAGE = f""" +Thanks for accepting our rules! + +You can find a copy of our rules for reference at <https://pythondiscord.com/pages/rules>. + +Additionally, if you'd like to receive notifications for the announcements \ +we post in <#{constants.Channels.announcements}> +from time to time, you can send `!subscribe` to <#{constants.Channels.bot_commands}> at any time \ +to assign yourself the **Announcements** role. We'll mention this role every time we make an announcement. + +If you'd like to unsubscribe from the announcement notifications, simply send `!unsubscribe` to \ +<#{constants.Channels.bot_commands}>. + +To introduce you to our community, we've made the following video: +https://youtu.be/ZH26PuX3re0 +""" + # Sent via DMs to users kicked for failing to verify KICKED_MESSAGE = f""" Hi! You have been automatically kicked from Python Discord as you have failed to accept our rules \ @@ -156,6 +173,9 @@ class Verification(Cog): # ] task_cache = RedisCache() + # Create a cache for storing recipients of the alternate welcome DM. + member_gating_cache = RedisCache() + def __init__(self, bot: Bot) -> None: """Start internal tasks.""" self.bot = bot @@ -519,6 +539,16 @@ class Verification(Cog): if member.guild.id != constants.Guild.id: return # Only listen for PyDis events + raw_member = await self.bot.http.get_member(member.guild.id, member.id) + + # If the user has the is_pending flag set, they will be using the alternate + # gate and will not need a welcome DM with verification instructions. + # We will send them an alternate DM once they verify with the welcome + # video. + if raw_member.get("is_pending"): + await self.member_gating_cache.set(member.id, True) + return + log.trace(f"Sending on join message to new member: {member.id}") try: await safe_dm(member.send(ON_JOIN_MESSAGE)) @@ -526,6 +556,23 @@ class Verification(Cog): log.exception("DM dispatch failed on unexpected error code") @Cog.listener() + async def on_member_update(self, before: discord.Member, after: discord.Member) -> None: + """Check if we need to send a verification DM to a gated user.""" + before_roles = [role.id for role in before.roles] + after_roles = [role.id for role in after.roles] + + if constants.Roles.verified not in before_roles and constants.Roles.verified in after_roles: + if await self.member_gating_cache.pop(after.id): + try: + # If the member has not received a DM from our !accept command + # and has gone through the alternate gating system we should send + # our alternate welcome DM which includes info such as our welcome + # video. + await safe_dm(after.send(ALTERNATE_VERIFIED_MESSAGE)) + except discord.HTTPException: + log.exception("DM dispatch failed on unexpected error code") + + @Cog.listener() async def on_message(self, message: discord.Message) -> None: """Check new message event for messages to the checkpoint channel & process.""" if message.channel.id != constants.Channels.verification: |