aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--bot/exts/info/site.py21
-rw-r--r--bot/exts/moderation/infraction/superstarify.py5
-rw-r--r--bot/exts/moderation/verification.py47
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: