[Docs] Mod Cog Guide (#4886)

* Initial commit

* Add some args

* bool prolog

* index

* Add more arguments

* more arguments, style fixes

* improve naming of arguments

* Fix up backlog

* Run black

* extra corrections from backlog

* Update tempban arg names

* backlog

* change prolog type

* Change kick argument prolog to member instead of user

* Update argument names

* missed a colon

* Update prolog to be friendly with non-positional args

* Edit through the decorator instead

* Add back docstring spacing

* black

* usage in decorator for name commands

* Fix command signature

* fixup docstring spacing

* style fixes

* Add spacing inside unban docstring

* Rename args instead of using usage

* black - simple

* Add labeler glob

* unify style

* rename variables instead of usage

* Update docstrings correspondingly

* run black

* update description in usage

* ehh this isn't necessary...

* fix up tags

* fix grammar and accuracy issues

* fix docstring too
This commit is contained in:
Kreusada 2021-05-18 22:10:30 +01:00 committed by GitHub
parent 5d905a93ac
commit 410b2419dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 857 additions and 82 deletions

3
.github/labeler.yml vendored
View File

@ -121,7 +121,10 @@
- schema/* - schema/*
- tools/* - tools/*
"Category: Mod Cog": "Category: Mod Cog":
# Source
- redbot/cogs/mod/* - redbot/cogs/mod/*
# Docs
- docs/cog_guides/mod.rst
"Category: Modlog API": "Category: Modlog API":
# Source # Source
- redbot/core/generic_casetypes.py - redbot/core/generic_casetypes.py

763
docs/cog_guides/mod.rst Normal file
View File

@ -0,0 +1,763 @@
.. _mod:
===
Mod
===
This is the cog guide for the mod cog. You will
find detailed docs about usage and commands.
``[p]`` is considered as your prefix.
.. note:: To use this cog, load it by typing this::
[p]load mod
.. _mod-usage:
-----
Usage
-----
A range of highly customizable moderation tools used to protect your
guild from users who cannot follow the rules.
.. _mod-commands:
--------
Commands
--------
.. _mod-command-ban:
^^^
ban
^^^
.. note:: |admin-lock|
**Syntax**
.. code-block:: none
[p]ban <user> [days] [reason]
**Description**
Ban a user from this server and optionally delete days of messages.
``days`` is the amount of days of messages to cleanup on ban.
**Arguments**
* ``<user>``: The user to ban. |user-input|
* ``[days]``: The amount of days of messages to cleanup on ban. This parameter defaults to the defaultdays setting, or no days if this has not yet been configured.
* ``[reason]``: The reason why the user was banned (optional).
**Example Usage**
* ``[p]ban 428675506947227648 7 Continued to spam after told to stop.``
This will ban Twentysix and it will delete 7 days worth of messages.
* ``[p]ban @Twentysix 7 Continued to spam after told to stop.``
This will ban Twentysix and it will delete 7 days worth of messages.
A user ID should be provided if the user is not a member of this server.
If days is not a number, it's treated as the first word of the reason.
Minimum 0 days, maximum 7. If not specified, the defaultdays setting will be used instead.
.. _mod-command-kick:
^^^^
kick
^^^^
.. note:: |admin-lock|
**Syntax**
.. code-block:: none
[p]kick <member> [reason]
**Description**
Kick a user.
**Arguments**
* ``<member>``: The member to kick. |member-input|
* ``[reason]``: The reason why the user was kicked (optional).
**Example Usage**
* ``[p]kick 428675506947227648 wanted to be kicked.``
This will kick Twentysix from the server.
* ``[p]kick @Twentysix wanted to be kicked.``
This will kick Twentysix from the server.
If a reason is specified, it will be the reason that shows up
in the audit log.
.. _mod-command-massban:
^^^^^^^
massban
^^^^^^^
.. note:: |admin-lock|
**Syntax**
.. code-block:: none
[p]massban <user_ids...> [days] [reason]
.. tip:: Alias: ``hackban``
**Description**
Mass bans user(s) from the server.
**Arguments**
* ``<user_ids...>``: The users to ban. This must be a list of user IDs separated by spaces.
* ``[days]``: The amount of days of messages to cleanup on massban.
* ``[reason]``: The reason why these users were banned.
**Example Usage**
* ``[p]massban 345628097929936898 57287406247743488 7 they broke all rules.``
This will ban all the added userids and delete 7 days worth of their messages.
.. _mod-command-modset:
^^^^^^
modset
^^^^^^
.. note:: |guildowner-lock|
**Syntax**
.. code-block:: none
[p]modset
**Description**
Manage server administration settings.
.. _mod-command-modset-defaultdays:
""""""""""""""""""
modset defaultdays
""""""""""""""""""
**Syntax**
.. code-block:: none
[p]modset defaultdays [days=0]
**Description**
Set the default number of days worth of messages to be deleted when a user is banned.
The number of days must be between 0 and 7.
**Arguments**
* ``[days=0]``: The default number of days of messages to be deleted when a user is banned.
.. note:: This value must be between 0 and 7.
.. _mod-command-modset-defaultduration:
""""""""""""""""""""""
modset defaultduration
""""""""""""""""""""""
**Syntax**
.. code-block:: none
[p]modset defaultduration <duration>
**Description**
Set the default time to be used when a user is tempbanned.
Accepts: seconds, minutes, hours, days, weeks
**Arguments**
* ``<duration>``: The default duration for when a user is temporarily banned. Accepts seconds, minutes, hours, days or weeks.
**Example Usage**
* ``[p]modset defaultduration 7d12h10m``
* ``[p]modset defaultduration 7 days 12 hours 10 minutes``
.. _mod-command-modset-deletenames:
""""""""""""""""""
modset deletenames
""""""""""""""""""
.. note:: |owner-lock|
**Syntax**
.. code-block:: none
[p]modset deletenames [confirmation=False]
**Description**
Delete all stored usernames and nicknames.
**Arguments**
- ``<confirmation>``: Whether to delete all stored usernames and nicknames. |bool-input|
.. _mod-command-modset-deleterepeats:
""""""""""""""""""""
modset deleterepeats
""""""""""""""""""""
**Syntax**
.. code-block:: none
[p]modset deleterepeats [repeats]
**Description**
Enable auto-deletion of repeated messages.
**Arguments**
* ``[repeats]``: The number of repeated messages needed before further messages are deleted.
.. note:: Must be between 2 and 20. Set to -1 to disable this feature.
.. _mod-command-modset-dm:
"""""""""
modset dm
"""""""""
**Syntax**
.. code-block:: none
[p]modset dm [enabled]
**Description**
Toggle whether a message should be sent to a user when they are kicked/banned.
If this option is enabled, the bot will attempt to DM the user with the guild name
and reason as to why they were kicked/banned.
**Arguments**
* ``[enabled]``: Whether a message should be sent to a user when they are kicked/banned. |bool-input|
.. _mod-command-modset-hierarchy:
""""""""""""""""
modset hierarchy
""""""""""""""""
**Syntax**
.. code-block:: none
[p]modset hierarchy
**Description**
Toggle role hierarchy check for mods and admins.
..warning:: Disabling this setting will allow mods to take actions on users above them in the role hierarchy!
This is enabled by default.
.. _mod-command-modset-mentionspam:
""""""""""""""""""
modset mentionspam
""""""""""""""""""
**Syntax**
.. code-block:: none
[p]modset mentionspam
**Description**
Manage the automoderation settings for mentionspam.
.. _mod-command-modset-mentionspam-ban:
""""""""""""""""""""""
modset mentionspam ban
""""""""""""""""""""""
**Syntax**
.. code-block:: none
[p]modset mentionspam ban <max_mentions>
**Description**
Set the autoban conditions for mention spam.
Users will be banned if they send any message which contains more than
``<max_mentions>`` mentions.
**Arguments**
* ``<max_mentions>``: Must be 0 or greater. Set to 0 to disable this feature.
.. _mod-command-modset-mentionspam-kick:
"""""""""""""""""""""""
modset mentionspam kick
"""""""""""""""""""""""
**Syntax**
.. code-block:: none
[p]modset mentionspam kick <max_mentions>
**Description**
Set the autokick conditions for mention spam.
Users will be kicked if they send any message which contains more than
``<max_mentions>`` mentions.
**Arguments**
* ``<max_mentions>``: Must be 0 or greater. Set to 0 to disable this feature.
.. _mod-command-modset-mentionspam-strict:
"""""""""""""""""""""""""
modset mentionspam strict
"""""""""""""""""""""""""
**Syntax**
.. code-block:: none
[p]modset mentionspam strict [enabled]
**Description**
Setting to account for duplicate mentions.
If enabled all mentions will count including duplicated mentions.
If disabled only unique mentions will count.
Use this command without any parameter to see the current setting.
**Arguments**
* ``[enabled]``: Whether all mentions will count, including duplicated mentions. |bool-input|
.. _mod-command-modset-mentionspam-warn:
"""""""""""""""""""""""
modset mentionspam warn
"""""""""""""""""""""""
**Syntax**
.. code-block:: none
[p]modset mentionspam warn <max_mentions>
**Description**
Sets the autowarn conditions for mention spam.
Users will be warned if they send any messages which contain more than
``<max_mentions>`` mentions.
**Arguments**
* ``<max_mentions>``: Must be 0 or greater. Set to 0 to disable this feature.
.. _mod-command-modset-reinvite:
"""""""""""""""
modset reinvite
"""""""""""""""
**Syntax**
.. code-block:: none
[p]modset reinvite
**Description**
Toggle whether an invite will be sent to a user when unbanned.
If this is True, the bot will attempt to create and send a single-use invite
to the newly-unbanned user.
.. _mod-command-modset-showsettings:
"""""""""""""""""""
modset showsettings
"""""""""""""""""""
**Syntax**
.. code-block:: none
[p]modset showsettings
**Description**
Show the current server administration settings.
.. _mod-command-modset-trackallnames:
""""""""""""""""""""
modset trackallnames
""""""""""""""""""""
.. note:: |owner-lock|
**Syntax**
.. code-block:: none
[p]modset trackallnames [enabled]
**Description**
Toggle whether all name changes should be tracked.
Toggling this off also overrides the tracknicknames setting.
**Arguments**
* ``[enabled]``: Whether all name changes should be tracked. |bool-input|
.. _mod-command-modset-tracknicknames:
"""""""""""""""""""""
modset tracknicknames
"""""""""""""""""""""
**Syntax**
.. code-block:: none
[p]modset tracknicknames [enabled]
**Description**
Toggle whether nickname changes should be tracked.
This setting will be overridden if trackallnames is disabled.
**Arguments**
* ``[enabled]``: Whether all nickname changes should be tracked. |bool-input|
.. _mod-command-movedeletedelay:
^^^^^^^^^^^^^^^
movedeletedelay
^^^^^^^^^^^^^^^
.. note:: |owner-lock|
**Syntax**
.. code-block:: none
[p]movedeletedelay
**Description**
Move deletedelay settings to core
.. _mod-command-moveignoredchannels:
^^^^^^^^^^^^^^^^^^^
moveignoredchannels
^^^^^^^^^^^^^^^^^^^
.. note:: |owner-lock|
**Syntax**
.. code-block:: none
[p]moveignoredchannels
**Description**
Move ignored channels and servers to core
.. _mod-command-names:
^^^^^
names
^^^^^
**Syntax**
.. code-block:: none
[p]names <member>
**Description**
Show previous names and nicknames of a member.
**Arguments**
* ``<member>``: |member-input|
.. _mod-command-rename:
^^^^^^
rename
^^^^^^
.. note:: |admin-lock|
**Syntax**
.. code-block:: none
[p]rename <member> [nickname]
**Description**
Change a member's nickname.
Leaving the nickname empty will remove it.
**Arguments**
* ``<member>``: |member-input|
* ``[nickname]``: The new nickname for the member.
.. _mod-command-slowmode:
^^^^^^^^
slowmode
^^^^^^^^
.. note:: |admin-lock|
**Syntax**
.. code-block:: none
[p]slowmode [interval=0:00:00]
**Description**
Changes channel's slowmode setting.
Interval can be anything from 0 seconds to 6 hours.
Use without parameters to disable.
**Arguments**
* ``[interval=0:00:00]``: The time for the channel's slowmode settings.
.. note::
Interval can be anything from 0 seconds to 6 hours.
Use without parameters to disable.
.. _mod-command-softban:
^^^^^^^
softban
^^^^^^^
.. note:: |admin-lock|
**Syntax**
.. code-block:: none
[p]softban <member> [reason]
**Description**
Kick a member and delete 1 day's worth of their messages.
**Arguments**
* ``<member>``: The member to softban. |member-input-quotes|
* ``[reason]``: Reason for the kick (optional).
.. _mod-command-tempban:
^^^^^^^
tempban
^^^^^^^
.. note:: |admin-lock|
**Syntax**
.. code-block:: none
[p]tempban <member> [duration] [days] [reason]
**Description**
Temporarily ban a user from this server.
**Arguments**
* ``<member>``: The member to temporarily ban. |member-input-quotes|
* ``[duration]``: The amount of time the user should be banned for.
* ``[days]``: The amount of days of messages to cleanup on tempban.
* ``[reason]``: The reason for the tempban (optional).
**Example Usage**
* ``[p]tempban @Twentysix Because I say so``
This will ban Twentysix for the default amount of time set by an administrator.
* ``[p]tempban @Twentysix 15m You need a timeout``
This will ban Twentysix for 15 minutes.
* ``[p]tempban 428675506947227648 1d2h15m 5 Evil person``
This will ban the user for 1 day 2 hours 15 minutes and will delete the last 5 days of their messages.
.. _mod-command-unban:
^^^^^
unban
^^^^^
.. note:: |admin-lock|
**Syntax**
.. code-block:: none
[p]unban <user_id> [reason]
**Description**
Unban a user from this server.
**Arguments**
* ``<user_id>``: |user-input|
* ``[reason]``: The reason for the unban (optional).
.. _mod-command-userinfo:
^^^^^^^^
userinfo
^^^^^^^^
**Syntax**
.. code-block:: none
[p]userinfo [member]
**Description**
Show information about a user.
This includes fields for status, discord join date, server
join date, voice state and previous names/nicknames.
If the user has no roles, previous names or previous nicknames,
these fields will be omitted.
**Arguments**
* ``[member]``: |member-input|
.. _mod-command-voiceban:
^^^^^^^^
voiceban
^^^^^^^^
.. note:: |admin-lock|
**Syntax**
.. code-block:: none
[p]voiceban <member> [reason]
**Description**
Ban a user from speaking and listening in the server's voice channels.
**Arguments**
* ``<member>``: The member to ban from voice. |member-input|
* ``[reason]``: The reason for the voiceban (optional).
.. _mod-command-voicekick:
^^^^^^^^^
voicekick
^^^^^^^^^
.. note:: |mod-lock|
**Syntax**
.. code-block:: none
[p]voicekick <member> [reason]
**Description**
Kick a member from a voice channel.
**Arguments**
* ``<member>``: |member-input|
* ``[reason]``: The reason for the voicekick (optional).
.. _mod-command-voiceunban:
^^^^^^^^^^
voiceunban
^^^^^^^^^^
.. note:: |admin-lock|
**Syntax**
.. code-block:: none
[p]voiceunban <member> [reason]
**Description**
Unban a user from speaking and listening in the server's voice channels.
**Arguments**
* ``<member>``: The member to unban from voice. |member-input-quotes|
* ``[reason]``: The reason for the voiceunban (optional).

View File

@ -45,6 +45,7 @@ Welcome to Red - Discord Bot's documentation!
cog_guides/filter cog_guides/filter
cog_guides/general cog_guides/general
cog_guides/image cog_guides/image
cog_guides/mod
cog_guides/mutes cog_guides/mutes
cog_guides/reports cog_guides/reports
cog_guides/streams cog_guides/streams

View File

@ -27,16 +27,16 @@
.. |role-input-quotes| replace:: Please give **the exact role name or ID**, or it won't be detected. .. |role-input-quotes| replace:: Please give **the exact role name or ID**, or it won't be detected.
If the role name has spaces, provide it enclosed in quotes like this: ``"my role with spaces"`` If the role name has spaces, provide it enclosed in quotes like this: ``"my role with spaces"``
.. |member-input| replace:: You can either mention the member, provide its ID, its exact name with .. |member-input| replace:: You can either mention the member, provide their ID, their exact name with
the tag or not, or its nickname. the tag or not, or their nickname.
.. |member-input-quotes| replace:: You can either mention the member, provide its ID, its exact .. |member-input-quotes| replace:: You can either mention the member, provide their ID, their exact
name with the tag or not, or its nickname enclosed in quotes if there are spaces. name with the tag or not, or their nickname enclosed in quotes if there are spaces.
.. |user-input| replace:: You can either provide the member's ID or its exact name with the tag or .. |user-input| replace:: You can either provide the member's ID or their exact name with the tag or
not. not.
.. |user-input-quotes| replace:: You can either provide the member's ID or its exact name with the .. |user-input-quotes| replace:: You can either provide the member's ID or their exact name with the
tag or not, enclosed in quotes if there are spaces. tag or not, enclosed in quotes if there are spaces.
.. |channel-input| replace:: You can either mention the channel, provide its exact name or its ID. .. |channel-input| replace:: You can either mention the channel, provide its exact name or its ID.
@ -48,3 +48,7 @@
.. |color-input| replace:: You can either provide the hexadecimal code of the color, or one of the .. |color-input| replace:: You can either provide the hexadecimal code of the color, or one of the
colors listed here: :class:`discord.Color`. colors listed here: :class:`discord.Color`.
.. These are the comments for parameter types such as `bool`.
.. |bool-input| replace:: You should provide either 'true' or 'false'.

View File

@ -290,7 +290,7 @@ class KickBanMixin(MixinMeta):
@commands.guild_only() @commands.guild_only()
@commands.bot_has_permissions(kick_members=True) @commands.bot_has_permissions(kick_members=True)
@checks.admin_or_permissions(kick_members=True) @checks.admin_or_permissions(kick_members=True)
async def kick(self, ctx: commands.Context, user: discord.Member, *, reason: str = None): async def kick(self, ctx: commands.Context, member: discord.Member, *, reason: str = None):
""" """
Kick a user. Kick a user.
@ -306,14 +306,14 @@ class KickBanMixin(MixinMeta):
author = ctx.author author = ctx.author
guild = ctx.guild guild = ctx.guild
if author == user: if author == member:
await ctx.send( await ctx.send(
_("I cannot let you do that. Self-harm is bad {emoji}").format( _("I cannot let you do that. Self-harm is bad {emoji}").format(
emoji="\N{PENSIVE FACE}" emoji="\N{PENSIVE FACE}"
) )
) )
return return
elif not await is_allowed_by_hierarchy(self.bot, self.config, guild, author, user): elif not await is_allowed_by_hierarchy(self.bot, self.config, guild, author, member):
await ctx.send( await ctx.send(
_( _(
"I cannot let you do that. You are " "I cannot let you do that. You are "
@ -322,7 +322,7 @@ class KickBanMixin(MixinMeta):
) )
) )
return return
elif ctx.guild.me.top_role <= user.top_role or user == ctx.guild.owner: elif ctx.guild.me.top_role <= member.top_role or member == ctx.guild.owner:
await ctx.send(_("I cannot do that due to Discord hierarchy rules.")) await ctx.send(_("I cannot do that due to Discord hierarchy rules."))
return return
audit_reason = get_audit_reason(author, reason, shorten=True) audit_reason = get_audit_reason(author, reason, shorten=True)
@ -331,23 +331,23 @@ class KickBanMixin(MixinMeta):
with contextlib.suppress(discord.HTTPException): with contextlib.suppress(discord.HTTPException):
em = discord.Embed( em = discord.Embed(
title=bold(_("You have been kicked from {guild}.").format(guild=guild)), title=bold(_("You have been kicked from {guild}.").format(guild=guild)),
color=await self.bot.get_embed_color(user), color=await self.bot.get_embed_color(member),
) )
em.add_field( em.add_field(
name=_("**Reason**"), name=_("**Reason**"),
value=reason if reason is not None else _("No reason was given."), value=reason if reason is not None else _("No reason was given."),
inline=False, inline=False,
) )
await user.send(embed=em) await member.send(embed=em)
try: try:
await guild.kick(user, reason=audit_reason) await guild.kick(member, reason=audit_reason)
log.info("{}({}) kicked {}({})".format(author.name, author.id, user.name, user.id)) log.info("{}({}) kicked {}({})".format(author.name, author.id, member.name, member.id))
except discord.errors.Forbidden: except discord.errors.Forbidden:
await ctx.send(_("I'm not allowed to do that.")) await ctx.send(_("I'm not allowed to do that."))
except Exception: except Exception:
log.exception( log.exception(
"{}({}) attempted to kick {}({}), but an error occurred.".format( "{}({}) attempted to kick {}({}), but an error occurred.".format(
author.name, author.id, user.name, user.id author.name, author.id, member.name, member.id
) )
) )
else: else:
@ -356,7 +356,7 @@ class KickBanMixin(MixinMeta):
guild, guild,
ctx.message.created_at.replace(tzinfo=timezone.utc), ctx.message.created_at.replace(tzinfo=timezone.utc),
"kick", "kick",
user, member,
author, author,
reason, reason,
until=None, until=None,
@ -420,7 +420,7 @@ class KickBanMixin(MixinMeta):
Example: Example:
- `[p]massban 345628097929936898 57287406247743488 7 they broke all rules.` - `[p]massban 345628097929936898 57287406247743488 7 they broke all rules.`
This will ban all the added userids and delete 7 days of worth messages. This will ban all the added userids and delete 7 days worth of their messages.
User IDs need to be provided in order to ban User IDs need to be provided in order to ban
using this command. using this command.
@ -580,7 +580,7 @@ class KickBanMixin(MixinMeta):
async def tempban( async def tempban(
self, self,
ctx: commands.Context, ctx: commands.Context,
user: discord.Member, member: discord.Member,
duration: Optional[commands.TimedeltaConverter] = None, duration: Optional[commands.TimedeltaConverter] = None,
days: Optional[int] = None, days: Optional[int] = None,
*, *,
@ -602,12 +602,12 @@ class KickBanMixin(MixinMeta):
guild = ctx.guild guild = ctx.guild
author = ctx.author author = ctx.author
if author == user: if author == member:
await ctx.send( await ctx.send(
_("I cannot let you do that. Self-harm is bad {}").format("\N{PENSIVE FACE}") _("I cannot let you do that. Self-harm is bad {}").format("\N{PENSIVE FACE}")
) )
return return
elif not await is_allowed_by_hierarchy(self.bot, self.config, guild, author, user): elif not await is_allowed_by_hierarchy(self.bot, self.config, guild, author, member):
await ctx.send( await ctx.send(
_( _(
"I cannot let you do that. You are " "I cannot let you do that. You are "
@ -616,7 +616,7 @@ class KickBanMixin(MixinMeta):
) )
) )
return return
elif guild.me.top_role <= user.top_role or user == guild.owner: elif guild.me.top_role <= member.top_role or member == guild.owner:
await ctx.send(_("I cannot do that due to Discord hierarchy rules.")) await ctx.send(_("I cannot do that due to Discord hierarchy rules."))
return return
@ -634,9 +634,9 @@ class KickBanMixin(MixinMeta):
if invite is None: if invite is None:
invite = "" invite = ""
await self.config.member(user).banned_until.set(unban_time.timestamp()) await self.config.member(member).banned_until.set(unban_time.timestamp())
async with self.config.guild(guild).current_tempbans() as current_tempbans: async with self.config.guild(guild).current_tempbans() as current_tempbans:
current_tempbans.append(user.id) current_tempbans.append(member.id)
with contextlib.suppress(discord.HTTPException): with contextlib.suppress(discord.HTTPException):
# We don't want blocked DMs preventing us from banning # We don't want blocked DMs preventing us from banning
@ -647,12 +647,12 @@ class KickBanMixin(MixinMeta):
msg += _(" Here is an invite for when your ban expires: {invite_link}").format( msg += _(" Here is an invite for when your ban expires: {invite_link}").format(
invite_link=invite invite_link=invite
) )
await user.send(msg) await member.send(msg)
audit_reason = get_audit_reason(author, reason, shorten=True) audit_reason = get_audit_reason(author, reason, shorten=True)
try: try:
await guild.ban(user, reason=audit_reason, delete_message_days=days) await guild.ban(member, reason=audit_reason, delete_message_days=days)
except discord.Forbidden: except discord.Forbidden:
await ctx.send(_("I can't do that for some reason.")) await ctx.send(_("I can't do that for some reason."))
except discord.HTTPException: except discord.HTTPException:
@ -663,7 +663,7 @@ class KickBanMixin(MixinMeta):
guild, guild,
ctx.message.created_at.replace(tzinfo=timezone.utc), ctx.message.created_at.replace(tzinfo=timezone.utc),
"tempban", "tempban",
user, member,
author, author,
reason, reason,
unban_time, unban_time,
@ -674,19 +674,19 @@ class KickBanMixin(MixinMeta):
@commands.guild_only() @commands.guild_only()
@commands.bot_has_permissions(ban_members=True) @commands.bot_has_permissions(ban_members=True)
@checks.admin_or_permissions(ban_members=True) @checks.admin_or_permissions(ban_members=True)
async def softban(self, ctx: commands.Context, user: discord.Member, *, reason: str = None): async def softban(self, ctx: commands.Context, member: discord.Member, *, reason: str = None):
"""Kick a user and delete 1 day's worth of their messages.""" """Kick a user and delete 1 day's worth of their messages."""
guild = ctx.guild guild = ctx.guild
author = ctx.author author = ctx.author
if author == user: if author == member:
await ctx.send( await ctx.send(
_("I cannot let you do that. Self-harm is bad {emoji}").format( _("I cannot let you do that. Self-harm is bad {emoji}").format(
emoji="\N{PENSIVE FACE}" emoji="\N{PENSIVE FACE}"
) )
) )
return return
elif not await is_allowed_by_hierarchy(self.bot, self.config, guild, author, user): elif not await is_allowed_by_hierarchy(self.bot, self.config, guild, author, member):
await ctx.send( await ctx.send(
_( _(
"I cannot let you do that. You are " "I cannot let you do that. You are "
@ -703,7 +703,7 @@ class KickBanMixin(MixinMeta):
invite = "" invite = ""
try: # We don't want blocked DMs preventing us from banning try: # We don't want blocked DMs preventing us from banning
msg = await user.send( msg = await member.send(
_( _(
"You have been banned and " "You have been banned and "
"then unbanned as a quick way to delete your messages.\n" "then unbanned as a quick way to delete your messages.\n"
@ -713,7 +713,7 @@ class KickBanMixin(MixinMeta):
except discord.HTTPException: except discord.HTTPException:
msg = None msg = None
try: try:
await guild.ban(user, reason=audit_reason, delete_message_days=1) await guild.ban(member, reason=audit_reason, delete_message_days=1)
except discord.errors.Forbidden: except discord.errors.Forbidden:
await ctx.send(_("My role is not high enough to softban that user.")) await ctx.send(_("My role is not high enough to softban that user."))
if msg is not None: if msg is not None:
@ -722,30 +722,30 @@ class KickBanMixin(MixinMeta):
except discord.HTTPException: except discord.HTTPException:
log.exception( log.exception(
"{}({}) attempted to softban {}({}), but an error occurred trying to ban them.".format( "{}({}) attempted to softban {}({}), but an error occurred trying to ban them.".format(
author.name, author.id, user.name, user.id author.name, author.id, member.name, member.id
) )
) )
return return
try: try:
await guild.unban(user) await guild.unban(member)
except discord.HTTPException: except discord.HTTPException:
log.exception( log.exception(
"{}({}) attempted to softban {}({}), but an error occurred trying to unban them.".format( "{}({}) attempted to softban {}({}), but an error occurred trying to unban them.".format(
author.name, author.id, user.name, user.id author.name, author.id, member.name, member.id
) )
) )
return return
else: else:
log.info( log.info(
"{}({}) softbanned {}({}), deleting 1 day worth " "{}({}) softbanned {}({}), deleting 1 day worth "
"of messages.".format(author.name, author.id, user.name, user.id) "of messages.".format(author.name, author.id, member.name, member.id)
) )
await modlog.create_case( await modlog.create_case(
self.bot, self.bot,
guild, guild,
ctx.message.created_at.replace(tzinfo=timezone.utc), ctx.message.created_at.replace(tzinfo=timezone.utc),
"softban", "softban",
user, member,
author, author,
reason, reason,
until=None, until=None,
@ -802,9 +802,11 @@ class KickBanMixin(MixinMeta):
@commands.command() @commands.command()
@commands.guild_only() @commands.guild_only()
@checks.admin_or_permissions(mute_members=True, deafen_members=True) @checks.admin_or_permissions(mute_members=True, deafen_members=True)
async def voiceunban(self, ctx: commands.Context, user: discord.Member, *, reason: str = None): async def voiceunban(
self, ctx: commands.Context, member: discord.Member, *, reason: str = None
):
"""Unban a user from speaking and listening in the server's voice channels.""" """Unban a user from speaking and listening in the server's voice channels."""
user_voice_state = user.voice user_voice_state = member.voice
if ( if (
await self._voice_perm_check( await self._voice_perm_check(
ctx, user_voice_state, deafen_members=True, mute_members=True ctx, user_voice_state, deafen_members=True, mute_members=True
@ -816,11 +818,11 @@ class KickBanMixin(MixinMeta):
needs_undeafen = True if user_voice_state.deaf else False needs_undeafen = True if user_voice_state.deaf else False
audit_reason = get_audit_reason(ctx.author, reason, shorten=True) audit_reason = get_audit_reason(ctx.author, reason, shorten=True)
if needs_unmute and needs_undeafen: if needs_unmute and needs_undeafen:
await user.edit(mute=False, deafen=False, reason=audit_reason) await member.edit(mute=False, deafen=False, reason=audit_reason)
elif needs_unmute: elif needs_unmute:
await user.edit(mute=False, reason=audit_reason) await member.edit(mute=False, reason=audit_reason)
elif needs_undeafen: elif needs_undeafen:
await user.edit(deafen=False, reason=audit_reason) await member.edit(deafen=False, reason=audit_reason)
else: else:
await ctx.send(_("That user isn't muted or deafened by the server.")) await ctx.send(_("That user isn't muted or deafened by the server."))
return return
@ -832,7 +834,7 @@ class KickBanMixin(MixinMeta):
guild, guild,
ctx.message.created_at.replace(tzinfo=timezone.utc), ctx.message.created_at.replace(tzinfo=timezone.utc),
"voiceunban", "voiceunban",
user, member,
author, author,
reason, reason,
until=None, until=None,
@ -843,9 +845,9 @@ class KickBanMixin(MixinMeta):
@commands.command() @commands.command()
@commands.guild_only() @commands.guild_only()
@checks.admin_or_permissions(mute_members=True, deafen_members=True) @checks.admin_or_permissions(mute_members=True, deafen_members=True)
async def voiceban(self, ctx: commands.Context, user: discord.Member, *, reason: str = None): async def voiceban(self, ctx: commands.Context, member: discord.Member, *, reason: str = None):
"""Ban a user from speaking and listening in the server's voice channels.""" """Ban a user from speaking and listening in the server's voice channels."""
user_voice_state: discord.VoiceState = user.voice user_voice_state: discord.VoiceState = member.voice
if ( if (
await self._voice_perm_check( await self._voice_perm_check(
ctx, user_voice_state, deafen_members=True, mute_members=True ctx, user_voice_state, deafen_members=True, mute_members=True
@ -859,11 +861,11 @@ class KickBanMixin(MixinMeta):
author = ctx.author author = ctx.author
guild = ctx.guild guild = ctx.guild
if needs_mute and needs_deafen: if needs_mute and needs_deafen:
await user.edit(mute=True, deafen=True, reason=audit_reason) await member.edit(mute=True, deafen=True, reason=audit_reason)
elif needs_mute: elif needs_mute:
await user.edit(mute=True, reason=audit_reason) await member.edit(mute=True, reason=audit_reason)
elif needs_deafen: elif needs_deafen:
await user.edit(deafen=True, reason=audit_reason) await member.edit(deafen=True, reason=audit_reason)
else: else:
await ctx.send(_("That user is already muted and deafened server-wide.")) await ctx.send(_("That user is already muted and deafened server-wide."))
return return
@ -873,7 +875,7 @@ class KickBanMixin(MixinMeta):
guild, guild,
ctx.message.created_at.replace(tzinfo=timezone.utc), ctx.message.created_at.replace(tzinfo=timezone.utc),
"voiceban", "voiceban",
user, member,
author, author,
reason, reason,
until=None, until=None,

View File

@ -32,8 +32,8 @@ class ModInfo(MixinMeta):
@commands.guild_only() @commands.guild_only()
@commands.bot_has_permissions(manage_nicknames=True) @commands.bot_has_permissions(manage_nicknames=True)
@checks.admin_or_permissions(manage_nicknames=True) @checks.admin_or_permissions(manage_nicknames=True)
async def rename(self, ctx: commands.Context, user: discord.Member, *, nickname: str = ""): async def rename(self, ctx: commands.Context, member: discord.Member, *, nickname: str = ""):
"""Change a user's nickname. """Change a member's nickname.
Leaving the nickname empty will remove it. Leaving the nickname empty will remove it.
""" """
@ -46,8 +46,8 @@ class ModInfo(MixinMeta):
return return
if not ( if not (
(me.guild_permissions.manage_nicknames or me.guild_permissions.administrator) (me.guild_permissions.manage_nicknames or me.guild_permissions.administrator)
and me.top_role > user.top_role and me.top_role > member.top_role
and user != ctx.guild.owner and member != ctx.guild.owner
): ):
await ctx.send( await ctx.send(
_( _(
@ -57,7 +57,7 @@ class ModInfo(MixinMeta):
) )
else: else:
try: try:
await user.edit(reason=get_audit_reason(ctx.author, None), nick=nickname) await member.edit(reason=get_audit_reason(ctx.author, None), nick=nickname)
except discord.Forbidden: except discord.Forbidden:
# Just in case we missed something in the permissions check above # Just in case we missed something in the permissions check above
await ctx.send(_("I do not have permission to rename that member.")) await ctx.send(_("I do not have permission to rename that member."))
@ -160,58 +160,60 @@ class ModInfo(MixinMeta):
@commands.command() @commands.command()
@commands.guild_only() @commands.guild_only()
@commands.bot_has_permissions(embed_links=True) @commands.bot_has_permissions(embed_links=True)
async def userinfo(self, ctx, *, user: discord.Member = None): async def userinfo(self, ctx, *, member: discord.Member = None):
"""Show information about a user. """Show information about a member.
This includes fields for status, discord join date, server This includes fields for status, discord join date, server
join date, voice state and previous names/nicknames. join date, voice state and previous names/nicknames.
If the user has no roles, previous names or previous nicknames, If the member has no roles, previous names or previous nicknames,
these fields will be omitted. these fields will be omitted.
""" """
author = ctx.author author = ctx.author
guild = ctx.guild guild = ctx.guild
if not user: if not member:
user = author member = author
# A special case for a special someone :^) # A special case for a special someone :^)
special_date = datetime(2016, 1, 10, 6, 8, 4, 443000) special_date = datetime(2016, 1, 10, 6, 8, 4, 443000)
is_special = user.id == 96130341705637888 and guild.id == 133049272517001216 is_special = member.id == 96130341705637888 and guild.id == 133049272517001216
roles = user.roles[-1:0:-1] roles = member.roles[-1:0:-1]
names, nicks = await self.get_names_and_nicks(user) names, nicks = await self.get_names_and_nicks(member)
joined_at = user.joined_at if not is_special else special_date joined_at = member.joined_at if not is_special else special_date
since_created = (ctx.message.created_at - user.created_at).days since_created = (ctx.message.created_at - member.created_at).days
if joined_at is not None: if joined_at is not None:
since_joined = (ctx.message.created_at - joined_at).days since_joined = (ctx.message.created_at - joined_at).days
user_joined = joined_at.strftime("%d %b %Y %H:%M") user_joined = joined_at.strftime("%d %b %Y %H:%M")
else: else:
since_joined = "?" since_joined = "?"
user_joined = _("Unknown") user_joined = _("Unknown")
user_created = user.created_at.strftime("%d %b %Y %H:%M") user_created = member.created_at.strftime("%d %b %Y %H:%M")
voice_state = user.voice voice_state = member.voice
member_number = ( member_number = (
sorted(guild.members, key=lambda m: m.joined_at or ctx.message.created_at).index(user) sorted(guild.members, key=lambda m: m.joined_at or ctx.message.created_at).index(
member
)
+ 1 + 1
) )
created_on = _("{}\n({} days ago)").format(user_created, since_created) created_on = _("{}\n({} days ago)").format(user_created, since_created)
joined_on = _("{}\n({} days ago)").format(user_joined, since_joined) joined_on = _("{}\n({} days ago)").format(user_joined, since_joined)
if any(a.type is discord.ActivityType.streaming for a in user.activities): if any(a.type is discord.ActivityType.streaming for a in member.activities):
statusemoji = "\N{LARGE PURPLE CIRCLE}" statusemoji = "\N{LARGE PURPLE CIRCLE}"
elif user.status.name == "online": elif member.status.name == "online":
statusemoji = "\N{LARGE GREEN CIRCLE}" statusemoji = "\N{LARGE GREEN CIRCLE}"
elif user.status.name == "offline": elif member.status.name == "offline":
statusemoji = "\N{MEDIUM WHITE CIRCLE}\N{VARIATION SELECTOR-16}" statusemoji = "\N{MEDIUM WHITE CIRCLE}\N{VARIATION SELECTOR-16}"
elif user.status.name == "dnd": elif member.status.name == "dnd":
statusemoji = "\N{LARGE RED CIRCLE}" statusemoji = "\N{LARGE RED CIRCLE}"
elif user.status.name == "idle": elif member.status.name == "idle":
statusemoji = "\N{LARGE ORANGE CIRCLE}" statusemoji = "\N{LARGE ORANGE CIRCLE}"
activity = _("Chilling in {} status").format(user.status) activity = _("Chilling in {} status").format(member.status)
status_string = self.get_status_string(user) status_string = self.get_status_string(member)
if roles: if roles:
@ -249,7 +251,7 @@ class ModInfo(MixinMeta):
else: else:
role_str = None role_str = None
data = discord.Embed(description=status_string or activity, colour=user.colour) data = discord.Embed(description=status_string or activity, colour=member.colour)
data.add_field(name=_("Joined Discord on"), value=created_on) data.add_field(name=_("Joined Discord on"), value=created_on)
data.add_field(name=_("Joined this server on"), value=joined_on) data.add_field(name=_("Joined this server on"), value=joined_on)
@ -279,22 +281,22 @@ class ModInfo(MixinMeta):
value="{0.mention} ID: {0.id}".format(voice_state.channel), value="{0.mention} ID: {0.id}".format(voice_state.channel),
inline=False, inline=False,
) )
data.set_footer(text=_("Member #{} | User ID: {}").format(member_number, user.id)) data.set_footer(text=_("Member #{} | User ID: {}").format(member_number, member.id))
name = str(user) name = str(member)
name = " ~ ".join((name, user.nick)) if user.nick else name name = " ~ ".join((name, member.nick)) if member.nick else name
name = filter_invites(name) name = filter_invites(name)
avatar = user.avatar_url_as(static_format="png") avatar = member.avatar_url_as(static_format="png")
data.set_author(name=f"{statusemoji} {name}", url=avatar) data.set_author(name=f"{statusemoji} {name}", url=avatar)
data.set_thumbnail(url=avatar) data.set_thumbnail(url=avatar)
await ctx.send(embed=data) await ctx.send(embed=data)
@commands.command() @commands.command()
async def names(self, ctx: commands.Context, *, user: discord.Member): async def names(self, ctx: commands.Context, *, member: discord.Member):
"""Show previous names and nicknames of a user.""" """Show previous names and nicknames of a member."""
names, nicks = await self.get_names_and_nicks(user) names, nicks = await self.get_names_and_nicks(member)
msg = "" msg = ""
if names: if names:
msg += _("**Past 20 names**:") msg += _("**Past 20 names**:")
@ -310,4 +312,4 @@ class ModInfo(MixinMeta):
msg = filter_various_mentions(msg) msg = filter_various_mentions(msg)
await ctx.send(msg) await ctx.send(msg)
else: else:
await ctx.send(_("That user doesn't have any recorded name or nickname change.")) await ctx.send(_("That member doesn't have any recorded name or nickname change."))