From 480da64050d7c1702e700586421e34bc284dd392 Mon Sep 17 00:00:00 2001 From: momothereal Date: Wed, 18 Jul 2018 16:50:53 -0400 Subject: Add default infraction URLs to config, cleanup moderation cog --- config-default.yml | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'config-default.yml') diff --git a/config-default.yml b/config-default.yml index 5b87f8d78..8f0c32af9 100644 --- a/config-default.yml +++ b/config-default.yml @@ -85,5 +85,9 @@ urls: site_tags_api: 'https://api.pythondiscord.com/bot/tags' site_user_api: 'https://api.pythondiscord.com/bot/users' site_user_complete_api: 'https://api.pythondiscord.com/bot/users/complete' + site_infractions: 'https://api.pythondiscord.com/bot/infractions' + site_infractions_user: 'https://api.pythondiscord.com/bot/infractions/user/{user_id}' + site_infractions_type: 'https://api.pythondiscord.com/bot/infractions/type/{infraction_type}' + site_infractions_by_id: 'https://api.pythondiscord.com/bot/infractions/id/{infraction_id}' status: !ENV 'STATUS_URL' paste_service: 'https://paste.pydis.com/{key}' -- cgit v1.2.3 From ef1fc0e923ed0d9e730bcda20c4f5adfd3b46241 Mon Sep 17 00:00:00 2001 From: momothereal Date: Mon, 23 Jul 2018 22:20:40 -0400 Subject: Unmute and unban commands --- bot/cogs/moderation.py | 91 ++++++++++++++++++++++++++++++++++++++++++++++++-- bot/constants.py | 3 +- config-default.yml | 1 + 3 files changed, 91 insertions(+), 4 deletions(-) (limited to 'config-default.yml') diff --git a/bot/cogs/moderation.py b/bot/cogs/moderation.py index f2ab64850..8829c595d 100644 --- a/bot/cogs/moderation.py +++ b/bot/cogs/moderation.py @@ -248,14 +248,91 @@ class Moderation: await ctx.send(result_message) + @with_role(Roles.admin, Roles.owner, Roles.moderator) + @command(name="moderation.unmute") + async def unmute(self, ctx, user: Member): + """ + Deactivates the active mute infraction for a user. + :param user: Accepts user mention, ID, etc. + """ + try: + # check the current active infraction + response = await self.bot.http_session.get( + URLs.site_infractions_user_type_current.format( + user_id=user.id, + infraction_type="mute" + ), + headers=self.headers + ) + response_object = await response.json() + if "error_code" in response_object: + # something went wrong + await ctx.send(f":x: There was an error removing the infraction: {response_object['error_message']}") + return + + infraction_object = response_object["infraction"] + if infraction_object is None: + # no active infraction + await ctx.send(f":x: There is no active mute infraction for user {user.mention}.") + return + + await self._deactivate_infraction(infraction_object) + if infraction_object["expires_at"] is not None: + self.cancel_expiration(infraction_object["id"]) + + await ctx.send(f":ok_hand: Un-muted {user.mention}.") + except Exception: + log.exception("There was an error removing an infraction.") + await ctx.send(":x: There was an error removing the infraction.") + return + + @with_role(Roles.admin, Roles.owner, Roles.moderator) + @command(name="moderation.unban") + async def unban(self, ctx, user: User): + """ + Deactivates the active ban infraction for a user. + :param user: Accepts user mention, ID, etc. + """ + try: + # check the current active infraction + response = await self.bot.http_session.get( + URLs.site_infractions_user_type_current.format( + user_id=user.id, + infraction_type="ban" + ), + headers=self.headers + ) + response_object = await response.json() + if "error_code" in response_object: + # something went wrong + await ctx.send(f":x: There was an error removing the infraction: {response_object['error_message']}") + return + + infraction_object = response_object["infraction"] + if infraction_object is None: + # no active infraction + await ctx.send(f":x: There is no active ban infraction for user {user.mention}.") + return + + await self._deactivate_infraction(infraction_object, user=user) + if infraction_object["expires_at"] is not None: + self.cancel_expiration(infraction_object["id"]) + + await ctx.send(f":ok_hand: Un-banned {user.mention}.") + except Exception: + log.exception("There was an error removing an infraction.") + await ctx.send(":x: There was an error removing the infraction.") + return + def schedule_expiration(self, loop: asyncio.AbstractEventLoop, infraction_object: dict): infraction_id = infraction_object["id"] if infraction_id in self.expiration_tasks: return task: asyncio.Task = asyncio.ensure_future(self._scheduled_expiration(infraction_object), loop=loop) + # Silently ignore exceptions in a callback (handles the CancelledError nonsense) - task.add_done_callback(lambda future: future.exception()) + task.add_done_callback(_silent_exception) self.expiration_tasks[infraction_id] = task @@ -285,7 +362,7 @@ class Moderation: self.cancel_expiration(infraction_object["id"]) - async def _deactivate_infraction(self, infraction_object): + async def _deactivate_infraction(self, infraction_object, user: User = None): guild: Guild = self.bot.get_guild(constants.Guild.id) user_id = infraction_object["user"]["user_id"] infraction_type = infraction_object["type"] @@ -295,7 +372,8 @@ class Moderation: if member: await member.edit(mute=False) elif infraction_type == "ban": - user: User = self.bot.get_user(user_id) + if user is None: + user: User = self.bot.get_user(user_id) await guild.unban(user) await self.bot.http_session.patch( @@ -315,6 +393,13 @@ def parse_rfc1123(time_str): return datetime.datetime.strptime(time_str, RFC1123_FORMAT).replace(tzinfo=datetime.timezone.utc) +def _silent_exception(future): + try: + future.exception() + except Exception: + pass + + def setup(bot): bot.add_cog(Moderation(bot)) # Here we'll need to call a command I haven't made yet diff --git a/bot/constants.py b/bot/constants.py index c435fbad9..915373add 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -265,7 +265,8 @@ class URLs(metaclass=YAMLGetter): site_infractions: str site_infractions_user: str site_infractions_type: str - site_infractions_by_id: str + site_infractions_user_type_current: str + site_infractions_user_type: str status: str paste_service: str diff --git a/config-default.yml b/config-default.yml index 8f0c32af9..15cdd948c 100644 --- a/config-default.yml +++ b/config-default.yml @@ -89,5 +89,6 @@ urls: site_infractions_user: 'https://api.pythondiscord.com/bot/infractions/user/{user_id}' site_infractions_type: 'https://api.pythondiscord.com/bot/infractions/type/{infraction_type}' site_infractions_by_id: 'https://api.pythondiscord.com/bot/infractions/id/{infraction_id}' + site_infractions_user_type_current: 'https://api.pythondiscord.com/bot/infractions/user/{user_id}/{infraction_type}/current' status: !ENV 'STATUS_URL' paste_service: 'https://paste.pydis.com/{key}' -- cgit v1.2.3 From 79879317fd639eb795160c8e190186886f775e68 Mon Sep 17 00:00:00 2001 From: momothereal Date: Tue, 24 Jul 2018 01:03:36 -0400 Subject: Use 'Muted' role, show actor in infraction list --- bot/cogs/moderation.py | 50 +++++++++++++++++++++++++++++--------------------- bot/constants.py | 1 + config-default.yml | 1 + 3 files changed, 31 insertions(+), 21 deletions(-) (limited to 'config-default.yml') diff --git a/bot/cogs/moderation.py b/bot/cogs/moderation.py index 6eebd4f8d..c4ae52401 100644 --- a/bot/cogs/moderation.py +++ b/bot/cogs/moderation.py @@ -3,7 +3,7 @@ import datetime import logging from typing import Dict -from discord import Colour, Embed, Guild, Member, User +from discord import Colour, Embed, Guild, Member, Object, User from discord.ext.commands import Bot, Context, command from bot import constants @@ -24,6 +24,7 @@ class Moderation: self.bot = bot self.headers = {"X-API-KEY": Keys.site_api} self.expiration_tasks: Dict[str, asyncio.Task] = {} + self._muted_role = Object(constants.Roles.muted) async def on_ready(self): # Schedule expiration for previous infractions @@ -148,7 +149,8 @@ class Moderation: await ctx.send(f":x: There was an error adding the infraction: {response_object['error_message']}") return - await user.edit(reason=reason, mute=True) + # add the mute role + await user.add_roles(self._muted_role, reason=reason) if reason is None: result_message = f":ok_hand: permanently muted {user.mention}." @@ -191,7 +193,7 @@ class Moderation: await ctx.send(f":x: There was an error adding the infraction: {response_object['error_message']}") return - await user.edit(reason=reason, mute=True) + await user.add_roles(self._muted_role, reason=reason) infraction_object = response_object["infraction"] infraction_expiration = infraction_object["expires_at"] @@ -322,7 +324,7 @@ class Moderation: await ctx.send(f":x: There is no active ban infraction for user {user.mention}.") return - await self._deactivate_infraction(infraction_object, user=user) + await self._deactivate_infraction(infraction_object) if infraction_object["expires_at"] is not None: self.cancel_expiration(infraction_object["id"]) @@ -365,7 +367,7 @@ class Moderation: ) await LinePaginator.paginate( - lines=(infraction_to_string(infraction_object) for infraction_object in infraction_list), + lines=(self.infraction_to_string(infraction_object) for infraction_object in infraction_list), ctx=ctx, embed=embed, empty=True, @@ -418,18 +420,20 @@ class Moderation: self.cancel_expiration(infraction_object["id"]) - async def _deactivate_infraction(self, infraction_object, user: User = None): + async def _deactivate_infraction(self, infraction_object): guild: Guild = self.bot.get_guild(constants.Guild.id) - user_id = infraction_object["user"]["user_id"] + user_id = int(infraction_object["user"]["user_id"]) infraction_type = infraction_object["type"] if infraction_type == "mute": member: Member = guild.get_member(user_id) if member: - await member.edit(mute=False) + # remove the mute role + await member.remove_roles(self._muted_role) + else: + log.warning(f"Failed to un-mute user: {user_id} (not found)") elif infraction_type == "ban": - if user is None: - user: User = self.bot.get_user(user_id) + user: User = self.bot.get_user(user_id) await guild.unban(user) await self.bot.http_session.patch( @@ -441,6 +445,21 @@ class Moderation: } ) + def infraction_to_string(self, infraction_object): + actor_id = int(infraction_object["actor"]["user_id"]) + guild: Guild = self.bot.get_guild(constants.Guild.id) + actor = guild.get_member(actor_id) + + return "\n".join(( + "===============", + "Type: **{0}**".format(infraction_object["type"]), + "Reason: {0}".format(infraction_object["reason"] or "*None*"), + "Created: {0}".format(infraction_object["inserted_at"]), + "Expires: {0}".format(infraction_object["expires_at"] or "*Permanent*"), + "Actor: {0}".format(actor.mention if actor else actor_id), + "===============", + )) + RFC1123_FORMAT = "%a, %d %b %Y %H:%M:%S GMT" @@ -456,17 +475,6 @@ def _silent_exception(future): pass -def infraction_to_string(infraction_object): - return "\n".join(( - "===============", - "Type: **{0}**".format(infraction_object["type"]), - "Reason: {0}".format(infraction_object["reason"] or "*None*"), - "Created: {0}".format(infraction_object["inserted_at"]), - "Expires: {0}".format(infraction_object["expires_at"] or "*Permanent*"), - "===============", - )) - - def setup(bot): bot.add_cog(Moderation(bot)) # Here we'll need to call a command I haven't made yet diff --git a/bot/constants.py b/bot/constants.py index 915373add..163066fc0 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -209,6 +209,7 @@ class Roles(metaclass=YAMLGetter): moderator: int owner: int verified: int + muted: int class Guild(metaclass=YAMLGetter): diff --git a/config-default.yml b/config-default.yml index 15cdd948c..b7efb0010 100644 --- a/config-default.yml +++ b/config-default.yml @@ -44,6 +44,7 @@ guild: moderator: 267629731250176001 owner: 267627879762755584 verified: 352427296948486144 + muted: 0 keys: -- cgit v1.2.3