diff options
| author | 2020-07-15 13:26:35 +0200 | |
|---|---|---|
| committer | 2020-07-15 13:26:35 +0200 | |
| commit | 9f90a350885da8fd1b0bd4990eb3b1bd89c43a54 (patch) | |
| tree | 4c8fcac852531940fc6c7333419fa52abe55fef6 | |
| parent | Merge pull request #1041 from python-discord/dm_relay (diff) | |
| parent | Merge branch 'master' into dm_relay (diff) | |
Merge pull request #1054 from python-discord/dm_relay
DM relay - minor fixes
| -rw-r--r-- | bot/cogs/dm_relay.py | 30 | ||||
| -rw-r--r-- | bot/converters.py | 19 |
2 files changed, 43 insertions, 6 deletions
diff --git a/bot/cogs/dm_relay.py b/bot/cogs/dm_relay.py index f62d6105e..0dc15d4b1 100644 --- a/bot/cogs/dm_relay.py +++ b/bot/cogs/dm_relay.py @@ -1,4 +1,5 @@ import logging +from typing import Optional import discord from discord import Color @@ -7,6 +8,8 @@ from discord.ext.commands import Cog from bot import constants from bot.bot import Bot +from bot.converters import UserMentionOrID +from bot.utils import RedisCache from bot.utils.checks import in_whitelist_check, with_role_check from bot.utils.messages import send_attachments from bot.utils.webhooks import send_webhook @@ -17,6 +20,9 @@ log = logging.getLogger(__name__) class DMRelay(Cog): """Relay direct messages to and from the bot.""" + # RedisCache[str, t.Union[discord.User.id, discord.Member.id]] + dm_cache = RedisCache() + def __init__(self, bot: Bot): self.bot = bot self.webhook_id = constants.Webhooks.dm_log @@ -24,11 +30,11 @@ class DMRelay(Cog): self.bot.loop.create_task(self.fetch_webhook()) @commands.command(aliases=("reply",)) - async def send_dm(self, ctx: commands.Context, member: discord.Member, *, message: str) -> None: + async def send_dm(self, ctx: commands.Context, member: Optional[UserMentionOrID], *, message: str) -> None: """ Allows you to send a DM to a user from the bot. - A `member` must be provided. + If `member` is not provided, it will send to the last user who DM'd the bot. This feature should be used extremely sparingly. Use ModMail if you need to have a serious conversation with a user. This is just for responding to extraordinary DMs, having a little @@ -36,14 +42,24 @@ class DMRelay(Cog): NOTE: This feature will be removed if it is overused. """ - try: - await member.send(message) - await ctx.message.add_reaction("✅") + if not member: + user_id = await self.dm_cache.get("last_user") + member = ctx.guild.get_member(user_id) if user_id else None + + # If we still don't have a Member at this point, give up + if not member: + log.debug("This bot has never gotten a DM, or the RedisCache has been cleared.") + await ctx.message.add_reaction("❌") return + try: + await member.send(message) except discord.errors.Forbidden: log.debug("User has disabled DMs.") await ctx.message.add_reaction("❌") + else: + await ctx.message.add_reaction("✅") + self.bot.stats.incr("dm_relay.dm_sent") async def fetch_webhook(self) -> None: """Fetches the webhook object, so we can post to it.""" @@ -65,9 +81,11 @@ class DMRelay(Cog): await send_webhook( webhook=self.webhook, content=message.clean_content, - username=message.author.display_name, + username=f"{message.author.display_name} ({message.author.id})", avatar_url=message.author.avatar_url ) + await self.dm_cache.set("last_user", message.author.id) + self.bot.stats.incr("dm_relay.dm_received") # Handle any attachments if message.attachments: diff --git a/bot/converters.py b/bot/converters.py index 898822165..4a0633951 100644 --- a/bot/converters.py +++ b/bot/converters.py @@ -330,6 +330,25 @@ def proxy_user(user_id: str) -> discord.Object: return user +class UserMentionOrID(UserConverter): + """ + Converts to a `discord.User`, but only if a mention or userID is provided. + + Unlike the default `UserConverter`, it does allow conversion from name, or name#descrim. + + This is useful in cases where that lookup strategy would lead to ambiguity. + """ + + async def convert(self, ctx: Context, argument: str) -> discord.User: + """Convert the `arg` to a `discord.User`.""" + match = self._get_id_match(argument) or re.match(r'<@!?([0-9]+)>$', argument) + + if match is not None: + return await super().convert(ctx, argument) + else: + raise BadArgument(f"`{argument}` is not a User mention or a User ID.") + + class FetchedUser(UserConverter): """ Converts to a `discord.User` or, if it fails, a `discord.Object`. |