mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-05 18:58:53 -05:00
d.py 2.3 / pomelo changes (#6130)
Co-authored-by: Michael Oliveira <34169552+Flame442@users.noreply.github.com>
This commit is contained in:
parent
3abf4cac05
commit
10e09d6abc
@ -215,11 +215,11 @@ modset deletenames
|
||||
|
||||
**Description**
|
||||
|
||||
Delete all stored usernames and nicknames.
|
||||
Delete all stored usernames, global display names, and server nicknames.
|
||||
|
||||
**Arguments**
|
||||
|
||||
- ``<confirmation>``: Whether to delete all stored usernames and nicknames. |bool-input|
|
||||
- ``<confirmation>``: Whether to delete all stored usernames, global display names, and server nicknames. |bool-input|
|
||||
|
||||
.. _mod-command-modset-deleterepeats:
|
||||
|
||||
@ -469,7 +469,7 @@ modset tracknicknames
|
||||
|
||||
**Description**
|
||||
|
||||
Toggle whether nickname changes should be tracked.
|
||||
Toggle whether server nickname changes should be tracked.
|
||||
|
||||
This setting will be overridden if trackallnames is disabled.
|
||||
|
||||
@ -527,7 +527,7 @@ names
|
||||
|
||||
**Description**
|
||||
|
||||
Show previous names and nicknames of a member.
|
||||
Show previous usernames, global display names, and server nicknames of a member.
|
||||
|
||||
**Arguments**
|
||||
|
||||
@ -549,14 +549,14 @@ rename
|
||||
|
||||
**Description**
|
||||
|
||||
Change a member's nickname.
|
||||
Change a member's server nickname.
|
||||
|
||||
Leaving the nickname empty will remove it.
|
||||
Leaving the nickname argument empty will remove it.
|
||||
|
||||
**Arguments**
|
||||
|
||||
* ``<member>``: |member-input|
|
||||
* ``[nickname]``: The new nickname for the member.
|
||||
* ``[nickname]``: The new server nickname for the member.
|
||||
|
||||
.. _mod-command-slowmode:
|
||||
|
||||
@ -684,9 +684,9 @@ userinfo
|
||||
Show information about a user.
|
||||
|
||||
This includes fields for status, discord join date, server
|
||||
join date, voice state and previous names/nicknames.
|
||||
join date, voice state and previous usernames/global display names/nicknames.
|
||||
|
||||
If the user has no roles, previous names or previous nicknames,
|
||||
If the user has no roles, previous usernames, global display names, or server nicknames,
|
||||
these fields will be omitted.
|
||||
|
||||
**Arguments**
|
||||
|
||||
@ -272,7 +272,9 @@ class Admin(commands.Cog):
|
||||
`[p]editrole colour Test #ff9900`
|
||||
"""
|
||||
author = ctx.author
|
||||
reason = "{}({}) changed the colour of role '{}'".format(author.name, author.id, role.name)
|
||||
reason = _("{author} ({author.id}) changed the colour of role '{role.name}'").format(
|
||||
author=author, role=role.name
|
||||
)
|
||||
|
||||
if not self.pass_user_hierarchy_check(ctx, role):
|
||||
await ctx.send(_(ROLE_USER_HIERARCHY_ISSUE).format(role=role))
|
||||
@ -303,9 +305,9 @@ class Admin(commands.Cog):
|
||||
"""
|
||||
author = ctx.message.author
|
||||
old_name = role.name
|
||||
reason = "{}({}) changed the name of role '{}' to '{}'".format(
|
||||
author.name, author.id, old_name, name
|
||||
)
|
||||
reason = _(
|
||||
"{author} ({author.id}) changed the name of role '{old_name}' to '{name}'"
|
||||
).format(author=author, old_name=old_name, name=name)
|
||||
|
||||
if not self.pass_user_hierarchy_check(ctx, role):
|
||||
await ctx.send(_(ROLE_USER_HIERARCHY_ISSUE).format(role=role))
|
||||
|
||||
@ -113,7 +113,9 @@ async def global_unique_user_finder(
|
||||
return user
|
||||
|
||||
maybe_matches = []
|
||||
async for user in AsyncIter(bot.users).filter(lambda u: u.name == arg or f"{u}" == arg):
|
||||
async for user in AsyncIter(bot.users).filter(
|
||||
lambda u: u.name == arg or u.global_name == arg or f"{u}" == arg
|
||||
):
|
||||
maybe_matches.append(user)
|
||||
|
||||
if guild is not None:
|
||||
|
||||
@ -105,32 +105,30 @@ class MiscellaneousCommands(MixinMeta, metaclass=CompositeMetaClass):
|
||||
queue_tracks = player.queue
|
||||
requesters = {"total": 0, "users": {}}
|
||||
|
||||
async def _usercount(req_username):
|
||||
if req_username in requesters["users"]:
|
||||
requesters["users"][req_username]["songcount"] += 1
|
||||
async def _usercount(req_user_handle):
|
||||
if req_user_handle in requesters["users"]:
|
||||
requesters["users"][req_user_handle]["songcount"] += 1
|
||||
requesters["total"] += 1
|
||||
else:
|
||||
requesters["users"][req_username] = {}
|
||||
requesters["users"][req_username]["songcount"] = 1
|
||||
requesters["users"][req_user_handle] = {}
|
||||
requesters["users"][req_user_handle]["songcount"] = 1
|
||||
requesters["total"] += 1
|
||||
|
||||
async for track in AsyncIter(queue_tracks):
|
||||
req_username = "{}#{}".format(track.requester.name, track.requester.discriminator)
|
||||
await _usercount(req_username)
|
||||
req_user_handle = str(track.requester)
|
||||
await _usercount(req_user_handle)
|
||||
|
||||
try:
|
||||
req_username = "{}#{}".format(
|
||||
player.current.requester.name, player.current.requester.discriminator
|
||||
)
|
||||
await _usercount(req_username)
|
||||
req_user_handle = str(player.current.requester)
|
||||
await _usercount(req_user_handle)
|
||||
except AttributeError:
|
||||
return await self.send_embed_msg(ctx, title=_("There's nothing in the queue."))
|
||||
|
||||
async for req_username in AsyncIter(requesters["users"]):
|
||||
percentage = float(requesters["users"][req_username]["songcount"]) / float(
|
||||
async for req_user_handle in AsyncIter(requesters["users"]):
|
||||
percentage = float(requesters["users"][req_user_handle]["songcount"]) / float(
|
||||
requesters["total"]
|
||||
)
|
||||
requesters["users"][req_username]["percent"] = round(percentage * 100, 1)
|
||||
requesters["users"][req_user_handle]["percent"] = round(percentage * 100, 1)
|
||||
|
||||
top_queue_users = heapq.nlargest(
|
||||
20,
|
||||
|
||||
@ -61,7 +61,7 @@ HUMANIZED_PERM = {
|
||||
"manage_nicknames": _("Manage Nicknames"),
|
||||
"manage_roles": _("Manage Roles"),
|
||||
"manage_webhooks": _("Manage Webhooks"),
|
||||
"manage_emojis": _("Manage Emojis"),
|
||||
"manage_expressions": _("Manage Expressions"),
|
||||
"use_application_commands": _("Use Application Commands"),
|
||||
"request_to_speak": _("Request to Speak"),
|
||||
"manage_events": _("Manage Events"),
|
||||
@ -72,6 +72,10 @@ HUMANIZED_PERM = {
|
||||
"send_messages_in_threads": _("Send Messages in Threads"),
|
||||
"start_embedded_activities": _("Start Activities"),
|
||||
"moderate_members": _("Moderate Member"),
|
||||
"use_soundboard": _("Use Soundboard"),
|
||||
"create_expressions": _("Create Expressions"),
|
||||
"use_external_sounds": _("Use External Sounds"),
|
||||
"send_voice_messages": _("Send Voice Messages"),
|
||||
}
|
||||
|
||||
DANGEROUS_COMMANDS = {
|
||||
|
||||
@ -228,8 +228,8 @@ class Cleanup(commands.Cog):
|
||||
)
|
||||
to_delete.append(ctx.message)
|
||||
|
||||
reason = "{}({}) deleted {} messages containing '{}' in channel #{}.".format(
|
||||
author.name,
|
||||
reason = "{} ({}) deleted {} messages containing '{}' in channel #{}.".format(
|
||||
author,
|
||||
author.id,
|
||||
humanize_number(len(to_delete), override_locale="en_us"),
|
||||
text,
|
||||
@ -295,10 +295,10 @@ class Cleanup(commands.Cog):
|
||||
to_delete.append(ctx.message)
|
||||
|
||||
reason = (
|
||||
"{}({}) deleted {} messages"
|
||||
" made by {}({}) in channel #{}."
|
||||
"{} ({}) deleted {} messages"
|
||||
" made by {} ({}) in channel #{}."
|
||||
"".format(
|
||||
author.name,
|
||||
author,
|
||||
author.id,
|
||||
humanize_number(len(to_delete), override_locale="en_US"),
|
||||
member or "???",
|
||||
@ -353,8 +353,8 @@ class Cleanup(commands.Cog):
|
||||
channel=channel, number=None, after=after, delete_pinned=delete_pinned
|
||||
)
|
||||
|
||||
reason = "{}({}) deleted {} messages in channel #{}.".format(
|
||||
author.name,
|
||||
reason = "{} ({}) deleted {} messages in channel #{}.".format(
|
||||
author,
|
||||
author.id,
|
||||
humanize_number(len(to_delete), override_locale="en_US"),
|
||||
channel.name,
|
||||
@ -409,8 +409,8 @@ class Cleanup(commands.Cog):
|
||||
)
|
||||
to_delete.append(ctx.message)
|
||||
|
||||
reason = "{}({}) deleted {} messages in channel #{}.".format(
|
||||
author.name,
|
||||
reason = "{} ({}) deleted {} messages in channel #{}.".format(
|
||||
author,
|
||||
author.id,
|
||||
humanize_number(len(to_delete), override_locale="en_US"),
|
||||
channel.name,
|
||||
@ -462,8 +462,8 @@ class Cleanup(commands.Cog):
|
||||
channel=channel, before=mtwo, after=mone, delete_pinned=delete_pinned
|
||||
)
|
||||
to_delete.append(ctx.message)
|
||||
reason = "{}({}) deleted {} messages in channel #{}.".format(
|
||||
author.name,
|
||||
reason = "{} ({}) deleted {} messages in channel #{}.".format(
|
||||
author,
|
||||
author.id,
|
||||
humanize_number(len(to_delete), override_locale="en_US"),
|
||||
channel.name,
|
||||
@ -504,8 +504,8 @@ class Cleanup(commands.Cog):
|
||||
)
|
||||
to_delete.append(ctx.message)
|
||||
|
||||
reason = "{}({}) deleted {} messages in channel #{}.".format(
|
||||
author.name, author.id, len(to_delete), channel.name
|
||||
reason = "{} ({}) deleted {} messages in channel #{}.".format(
|
||||
author, author.id, len(to_delete), channel.name
|
||||
)
|
||||
log.info(reason)
|
||||
|
||||
@ -585,10 +585,10 @@ class Cleanup(commands.Cog):
|
||||
to_delete.append(ctx.message)
|
||||
|
||||
reason = (
|
||||
"{}({}) deleted {}"
|
||||
"{} ({}) deleted {}"
|
||||
" command messages in channel #{}."
|
||||
"".format(
|
||||
author.name,
|
||||
author,
|
||||
author.id,
|
||||
humanize_number(len(to_delete), override_locale="en_US"),
|
||||
channel.name,
|
||||
@ -670,15 +670,11 @@ class Cleanup(commands.Cog):
|
||||
else:
|
||||
channel_name = str(channel)
|
||||
|
||||
reason = (
|
||||
"{}({}) deleted {} messages "
|
||||
"sent by the bot in {}."
|
||||
"".format(
|
||||
author.name,
|
||||
author.id,
|
||||
humanize_number(len(to_delete), override_locale="en_US"),
|
||||
channel_name,
|
||||
)
|
||||
reason = "{} ({}) deleted {} messages sent by the bot in {}.".format(
|
||||
author,
|
||||
author.id,
|
||||
humanize_number(len(to_delete), override_locale="en_US"),
|
||||
channel_name,
|
||||
)
|
||||
log.info(reason)
|
||||
|
||||
|
||||
@ -434,7 +434,9 @@ class Economy(commands.Cog):
|
||||
if show_global and await bank.is_global():
|
||||
# show_global is only applicable if bank is global
|
||||
bank_sorted = await bank.get_leaderboard(positions=top, guild=None)
|
||||
base_embed.set_author(name=ctx.bot.user.name, icon_url=ctx.bot.user.display_avatar)
|
||||
base_embed.set_author(
|
||||
name=ctx.bot.user.display_name, icon_url=ctx.bot.user.display_avatar
|
||||
)
|
||||
else:
|
||||
bank_sorted = await bank.get_leaderboard(positions=top, guild=guild)
|
||||
if guild:
|
||||
|
||||
@ -355,7 +355,7 @@ class General(commands.Cog):
|
||||
joined_on = _(
|
||||
"{bot_name} joined this server on {bot_join}. That's over {since_join} days ago!"
|
||||
).format(
|
||||
bot_name=ctx.bot.user.name,
|
||||
bot_name=ctx.bot.user.display_name,
|
||||
bot_join=guild.me.joined_at.strftime("%d %b %Y %H:%M:%S"),
|
||||
since_join=humanize_number((ctx.message.created_at - guild.me.joined_at).days),
|
||||
)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import logging
|
||||
from datetime import timezone
|
||||
from collections import defaultdict, deque
|
||||
from typing import List, Optional
|
||||
|
||||
import discord
|
||||
from redbot.core import i18n, modlog, commands
|
||||
@ -158,6 +159,17 @@ class Events(MixinMeta):
|
||||
if not deleted:
|
||||
await self.check_mention_spam(message)
|
||||
|
||||
@staticmethod
|
||||
def _update_past_names(name: str, name_list: List[Optional[str]]) -> None:
|
||||
while None in name_list: # clean out null entries from a bug
|
||||
name_list.remove(None)
|
||||
if name in name_list:
|
||||
# Ensure order is maintained without duplicates occurring
|
||||
name_list.remove(name)
|
||||
name_list.append(name)
|
||||
while len(name_list) > 20:
|
||||
name_list.pop(0)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_user_update(self, before: discord.User, after: discord.User):
|
||||
if before.name != after.name:
|
||||
@ -165,14 +177,13 @@ class Events(MixinMeta):
|
||||
if not track_all_names:
|
||||
return
|
||||
async with self.config.user(before).past_names() as name_list:
|
||||
while None in name_list: # clean out null entries from a bug
|
||||
name_list.remove(None)
|
||||
if before.name in name_list:
|
||||
# Ensure order is maintained without duplicates occurring
|
||||
name_list.remove(before.name)
|
||||
name_list.append(before.name)
|
||||
while len(name_list) > 20:
|
||||
name_list.pop(0)
|
||||
self._update_past_names(before.name, name_list)
|
||||
if before.display_name != after.display_name:
|
||||
track_all_names = await self.config.track_all_names()
|
||||
if not track_all_names:
|
||||
return
|
||||
async with self.config.user(before).past_display_names() as name_list:
|
||||
self._update_past_names(before.display_name, name_list)
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_member_update(self, before: discord.Member, after: discord.Member):
|
||||
@ -185,10 +196,4 @@ class Events(MixinMeta):
|
||||
if (not track_all_names) or (not track_nicknames):
|
||||
return
|
||||
async with self.config.member(before).past_nicks() as nick_list:
|
||||
while None in nick_list: # clean out null entries from a bug
|
||||
nick_list.remove(None)
|
||||
if before.nick in nick_list:
|
||||
nick_list.remove(before.nick)
|
||||
nick_list.append(before.nick)
|
||||
while len(nick_list) > 20:
|
||||
nick_list.pop(0)
|
||||
self._update_past_names(before.nick, nick_list)
|
||||
|
||||
@ -177,21 +177,23 @@ class KickBanMixin(MixinMeta):
|
||||
|
||||
if removed_temp:
|
||||
log.info(
|
||||
"{}({}) upgraded the tempban for {} to a permaban.".format(
|
||||
author.name, author.id, user.id
|
||||
)
|
||||
"%s (%s) upgraded the tempban for %s to a permaban.", author, author.id, user.id
|
||||
)
|
||||
success_message = _(
|
||||
"User with ID {user_id} was upgraded from a temporary to a permanent ban."
|
||||
).format(user_id=user.id)
|
||||
else:
|
||||
username = user.name if hasattr(user, "name") else "Unknown"
|
||||
user_handle = str(user) if isinstance(user, discord.abc.User) else "Unknown"
|
||||
try:
|
||||
await guild.ban(user, reason=audit_reason, delete_message_seconds=days * 86400)
|
||||
log.info(
|
||||
"{}({}) {}ned {}({}), deleting {} days worth of messages.".format(
|
||||
author.name, author.id, ban_type, username, user.id, str(days)
|
||||
)
|
||||
"%s (%s) %sned %s (%s), deleting %s days worth of messages.",
|
||||
author,
|
||||
author.id,
|
||||
ban_type,
|
||||
user_handle,
|
||||
user.id,
|
||||
days,
|
||||
)
|
||||
success_message = _("Done. That felt good.")
|
||||
except discord.Forbidden:
|
||||
@ -200,9 +202,12 @@ class KickBanMixin(MixinMeta):
|
||||
return False, _("User with ID {user_id} not found").format(user_id=user.id)
|
||||
except Exception:
|
||||
log.exception(
|
||||
"{}({}) attempted to {} {}({}), but an error occurred.".format(
|
||||
author.name, author.id, ban_type, username, user.id
|
||||
)
|
||||
"%s (%s) attempted to %s %s (%s), but an error occurred.",
|
||||
author,
|
||||
author.id,
|
||||
ban_type,
|
||||
user_handle,
|
||||
user.id,
|
||||
)
|
||||
return False, _("An unexpected error occurred.")
|
||||
|
||||
@ -333,14 +338,16 @@ class KickBanMixin(MixinMeta):
|
||||
await member.send(embed=em)
|
||||
try:
|
||||
await guild.kick(member, reason=audit_reason)
|
||||
log.info("{}({}) kicked {}({})".format(author.name, author.id, member.name, member.id))
|
||||
log.info("%s (%s) kicked %s (%s)", author, author.id, member, member.id)
|
||||
except discord.errors.Forbidden:
|
||||
await ctx.send(_("I'm not allowed to do that."))
|
||||
except Exception:
|
||||
log.exception(
|
||||
"{}({}) attempted to kick {}({}), but an error occurred.".format(
|
||||
author.name, author.id, member.name, member.id
|
||||
)
|
||||
"%s (%s) attempted to kick %s (%s), but an error occurred.",
|
||||
author,
|
||||
author.id,
|
||||
member,
|
||||
member.id,
|
||||
)
|
||||
else:
|
||||
await modlog.create_case(
|
||||
@ -531,9 +538,10 @@ class KickBanMixin(MixinMeta):
|
||||
tempbans.remove(user_id)
|
||||
upgrades.append(str(user_id))
|
||||
log.info(
|
||||
"{}({}) upgraded the tempban for {} to a permaban.".format(
|
||||
author.name, author.id, user_id
|
||||
)
|
||||
"%s (%s) upgraded the tempban for %s to a permaban.",
|
||||
author,
|
||||
author.id,
|
||||
user_id,
|
||||
)
|
||||
banned.append(user_id)
|
||||
else:
|
||||
@ -541,7 +549,7 @@ class KickBanMixin(MixinMeta):
|
||||
await guild.ban(
|
||||
user, reason=audit_reason, delete_message_seconds=days * 86400
|
||||
)
|
||||
log.info("{}({}) hackbanned {}".format(author.name, author.id, user_id))
|
||||
log.info("%s (%s) hackbanned %s", author, author.id, user_id)
|
||||
except discord.NotFound:
|
||||
errors[user_id] = _("User with ID {user_id} not found").format(
|
||||
user_id=user_id
|
||||
@ -716,24 +724,32 @@ class KickBanMixin(MixinMeta):
|
||||
return
|
||||
except discord.HTTPException:
|
||||
log.exception(
|
||||
"{}({}) attempted to softban {}({}), but an error occurred trying to ban them.".format(
|
||||
author.name, author.id, member.name, member.id
|
||||
)
|
||||
"%s (%s) attempted to softban %s (%s), but an error occurred trying to ban them.",
|
||||
author,
|
||||
author.id,
|
||||
member,
|
||||
member.id,
|
||||
)
|
||||
return
|
||||
try:
|
||||
await guild.unban(member)
|
||||
except discord.HTTPException:
|
||||
log.exception(
|
||||
"{}({}) attempted to softban {}({}), but an error occurred trying to unban them.".format(
|
||||
author.name, author.id, member.name, member.id
|
||||
)
|
||||
"%s (%s) attempted to softban %s (%s),"
|
||||
" but an error occurred trying to unban them.",
|
||||
author,
|
||||
author.id,
|
||||
member,
|
||||
member.id,
|
||||
)
|
||||
return
|
||||
else:
|
||||
log.info(
|
||||
"{}({}) softbanned {}({}), deleting 1 day worth "
|
||||
"of messages.".format(author.name, author.id, member.name, member.id)
|
||||
"%s (%s) softbanned %s (%s), deleting 1 day worth of messages.",
|
||||
author,
|
||||
author.id,
|
||||
member,
|
||||
member.id,
|
||||
)
|
||||
await modlog.create_case(
|
||||
self.bot,
|
||||
|
||||
@ -66,7 +66,7 @@ class Mod(
|
||||
|
||||
default_member_settings = {"past_nicks": [], "perms_cache": {}, "banned_until": False}
|
||||
|
||||
default_user_settings = {"past_names": []}
|
||||
default_user_settings = {"past_names": [], "past_display_names": []}
|
||||
|
||||
def __init__(self, bot: Red):
|
||||
super().__init__()
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import datetime
|
||||
from typing import cast
|
||||
from typing import List, Tuple, cast
|
||||
|
||||
import discord
|
||||
from redbot.core import commands, i18n
|
||||
from redbot.core.utils.chat_formatting import bold, pagify
|
||||
from redbot.core.utils.common_filters import (
|
||||
filter_invites,
|
||||
filter_various_mentions,
|
||||
@ -20,23 +21,23 @@ class ModInfo(MixinMeta):
|
||||
Commands regarding names, userinfo, etc.
|
||||
"""
|
||||
|
||||
async def get_names_and_nicks(self, user):
|
||||
names = await self.config.user(user).past_names()
|
||||
nicks = await self.config.member(user).past_nicks()
|
||||
if names:
|
||||
names = [escape_spoilers_and_mass_mentions(name) for name in names if name]
|
||||
if nicks:
|
||||
nicks = [escape_spoilers_and_mass_mentions(nick) for nick in nicks if nick]
|
||||
return names, nicks
|
||||
async def get_names(self, member: discord.Member) -> Tuple[List[str], List[str], List[str]]:
|
||||
user_data = await self.config.user(member).all()
|
||||
usernames, display_names = user_data["past_names"], user_data["past_display_names"]
|
||||
nicks = await self.config.member(member).past_nicks()
|
||||
usernames = list(map(escape_spoilers_and_mass_mentions, filter(None, usernames)))
|
||||
display_names = list(map(escape_spoilers_and_mass_mentions, filter(None, display_names)))
|
||||
nicks = list(map(escape_spoilers_and_mass_mentions, filter(None, nicks)))
|
||||
return usernames, display_names, nicks
|
||||
|
||||
@commands.command()
|
||||
@commands.guild_only()
|
||||
@commands.bot_has_permissions(manage_nicknames=True)
|
||||
@commands.admin_or_permissions(manage_nicknames=True)
|
||||
async def rename(self, ctx: commands.Context, member: discord.Member, *, nickname: str = ""):
|
||||
"""Change a member's nickname.
|
||||
"""Change a member's server nickname.
|
||||
|
||||
Leaving the nickname empty will remove it.
|
||||
Leaving the nickname argument empty will remove it.
|
||||
"""
|
||||
nickname = nickname.strip()
|
||||
me = cast(discord.Member, ctx.me)
|
||||
@ -175,9 +176,9 @@ class ModInfo(MixinMeta):
|
||||
"""Show information about a member.
|
||||
|
||||
This includes fields for status, discord join date, server
|
||||
join date, voice state and previous names/nicknames.
|
||||
join date, voice state and previous usernames/global display names/nicknames.
|
||||
|
||||
If the member has no roles, previous names or previous nicknames,
|
||||
If the member has no roles, previous usernames, global display names, or server nicknames,
|
||||
these fields will be omitted.
|
||||
"""
|
||||
author = ctx.author
|
||||
@ -191,7 +192,7 @@ class ModInfo(MixinMeta):
|
||||
is_special = member.id == 96130341705637888 and guild.id == 133049272517001216
|
||||
|
||||
roles = member.roles[-1:0:-1]
|
||||
names, nicks = await self.get_names_and_nicks(member)
|
||||
usernames, display_names, nicks = await self.get_names(member)
|
||||
|
||||
if is_special:
|
||||
joined_at = special_date
|
||||
@ -273,22 +274,17 @@ class ModInfo(MixinMeta):
|
||||
data.add_field(
|
||||
name=_("Roles") if len(roles) > 1 else _("Role"), value=role_str, inline=False
|
||||
)
|
||||
if names:
|
||||
# May need sanitizing later, but mentions do not ping in embeds currently
|
||||
val = filter_invites(", ".join(names))
|
||||
data.add_field(
|
||||
name=_("Previous Names") if len(names) > 1 else _("Previous Name"),
|
||||
value=val,
|
||||
inline=False,
|
||||
)
|
||||
if nicks:
|
||||
# May need sanitizing later, but mentions do not ping in embeds currently
|
||||
val = filter_invites(", ".join(nicks))
|
||||
data.add_field(
|
||||
name=_("Previous Nicknames") if len(nicks) > 1 else _("Previous Nickname"),
|
||||
value=val,
|
||||
inline=False,
|
||||
)
|
||||
for single_form, plural_form, names in (
|
||||
(_("Previous Username"), _("Previous Usernames"), usernames),
|
||||
(_("Previous Global Display Name"), _("Previous Global Display Names"), display_names),
|
||||
(_("Previous Server Nickname"), _("Previous Server Nicknames"), nicks),
|
||||
):
|
||||
if names:
|
||||
data.add_field(
|
||||
name=plural_form if len(names) > 1 else single_form,
|
||||
value=filter_invites(", ".join(names)),
|
||||
inline=False,
|
||||
)
|
||||
if voice_state and voice_state.channel:
|
||||
data.add_field(
|
||||
name=_("Current voice channel"),
|
||||
@ -309,21 +305,20 @@ class ModInfo(MixinMeta):
|
||||
|
||||
@commands.command()
|
||||
async def names(self, ctx: commands.Context, *, member: discord.Member):
|
||||
"""Show previous names and nicknames of a member."""
|
||||
names, nicks = await self.get_names_and_nicks(member)
|
||||
msg = ""
|
||||
if names:
|
||||
msg += _("**Past 20 names**:")
|
||||
msg += "\n"
|
||||
msg += ", ".join(names)
|
||||
if nicks:
|
||||
if msg:
|
||||
msg += "\n\n"
|
||||
msg += _("**Past 20 nicknames**:")
|
||||
msg += "\n"
|
||||
msg += ", ".join(nicks)
|
||||
if msg:
|
||||
msg = filter_various_mentions(msg)
|
||||
await ctx.send(msg)
|
||||
"""Show previous usernames, global display names, and server nicknames of a member."""
|
||||
usernames, display_names, nicks = await self.get_names(member)
|
||||
parts = []
|
||||
for header, names in (
|
||||
(_("Past 20 usernames:"), usernames),
|
||||
(_("Past 20 global display names:"), display_names),
|
||||
(_("Past 20 server nicknames:"), nicks),
|
||||
):
|
||||
if names:
|
||||
parts.append(bold(header) + ", ".join(names))
|
||||
if parts:
|
||||
# each name can have 32 characters, we store 3*20 names which totals to
|
||||
# 60*32=1920 characters which is quite close to the message length limit
|
||||
for msg in pagify(filter_various_mentions("\n\n".join(parts))):
|
||||
await ctx.send(msg)
|
||||
else:
|
||||
await ctx.send(_("That member doesn't have any recorded name or nickname change."))
|
||||
|
||||
@ -418,7 +418,7 @@ class ModSettings(MixinMeta):
|
||||
@commands.guild_only()
|
||||
async def tracknicknames(self, ctx: commands.Context, enabled: bool = None):
|
||||
"""
|
||||
Toggle whether nickname changes should be tracked.
|
||||
Toggle whether server nickname changes should be tracked.
|
||||
|
||||
This setting will be overridden if trackallnames is disabled.
|
||||
"""
|
||||
@ -470,11 +470,11 @@ class ModSettings(MixinMeta):
|
||||
@commands.max_concurrency(1, commands.BucketType.default)
|
||||
@commands.is_owner()
|
||||
async def deletenames(self, ctx: commands.Context, confirmation: bool = False) -> None:
|
||||
"""Delete all stored usernames and nicknames.
|
||||
"""Delete all stored usernames, global display names, and server nicknames.
|
||||
|
||||
Examples:
|
||||
- `[p]modset deletenames` - Did not confirm. Shows the help message.
|
||||
- `[p]modset deletenames yes` - Deletes all stored usernames and nicknames.
|
||||
- `[p]modset deletenames yes` - Deletes all stored usernames, global display names, and server nicknames.
|
||||
|
||||
**Arguments**
|
||||
|
||||
@ -483,8 +483,8 @@ class ModSettings(MixinMeta):
|
||||
if not confirmation:
|
||||
await ctx.send(
|
||||
_(
|
||||
"This will delete all stored usernames and nicknames the bot has stored."
|
||||
"\nIf you're sure, type {command}"
|
||||
"This will delete all stored usernames, global display names,"
|
||||
" and server nicknames the bot has stored.\nIf you're sure, type {command}"
|
||||
).format(command=inline(f"{ctx.clean_prefix}modset deletenames yes"))
|
||||
)
|
||||
return
|
||||
@ -511,16 +511,23 @@ class ModSettings(MixinMeta):
|
||||
async for guild_id in AsyncIter(guilds_to_remove, steps=100):
|
||||
del mod_member_data[guild_id]
|
||||
|
||||
# Username data
|
||||
# Username and global display name data
|
||||
async with self.config._get_base_group(self.config.USER).all() as mod_user_data:
|
||||
users_to_remove = []
|
||||
async for user_id, user_data in AsyncIter(mod_user_data.items(), steps=100):
|
||||
if "past_names" in user_data:
|
||||
del user_data["past_names"]
|
||||
if "past_display_names" in user_data:
|
||||
del user_data["past_display_names"]
|
||||
if not user_data:
|
||||
users_to_remove.append(user_id)
|
||||
|
||||
async for user_id in AsyncIter(users_to_remove, steps=100):
|
||||
del mod_user_data[user_id]
|
||||
|
||||
await ctx.send(_("Usernames and nicknames have been deleted from Mod config."))
|
||||
await ctx.send(
|
||||
_(
|
||||
"Usernames, global display names, and server nicknames"
|
||||
" have been deleted from Mod config."
|
||||
)
|
||||
)
|
||||
|
||||
@ -323,7 +323,7 @@ class TriviaSession:
|
||||
if payout <= 0:
|
||||
return
|
||||
for winner in winners:
|
||||
LOG.debug("Paying trivia winner: %d credits --> %s", payout, winner.name)
|
||||
LOG.debug("Paying trivia winner: %d credits --> %s", payout, winner)
|
||||
try:
|
||||
await bank.deposit_credits(winner, payout)
|
||||
except errors.BalanceTooHigh as e:
|
||||
|
||||
@ -183,13 +183,13 @@ def init_events(bot, cli_flags):
|
||||
if guilds:
|
||||
rich_console.print(
|
||||
Columns(
|
||||
[Panel(table_general_info, title=str(bot.user.name)), Panel(table_counts)],
|
||||
[Panel(table_general_info, title=bot.user.display_name), Panel(table_counts)],
|
||||
equal=True,
|
||||
align="center",
|
||||
)
|
||||
)
|
||||
else:
|
||||
rich_console.print(Columns([Panel(table_general_info, title=str(bot.user.name))]))
|
||||
rich_console.print(Columns([Panel(table_general_info, title=bot.user.display_name)]))
|
||||
|
||||
rich_console.print(
|
||||
"Loaded {} cogs with {} commands".format(len(bot.cogs), len(bot.commands))
|
||||
|
||||
@ -1794,6 +1794,8 @@ class Red(
|
||||
Checks if the user, message, context, or role should be considered immune from automated
|
||||
moderation actions.
|
||||
|
||||
Bot users are considered immune.
|
||||
|
||||
This will return ``False`` in direct messages.
|
||||
|
||||
Parameters
|
||||
@ -1812,22 +1814,22 @@ class Red(
|
||||
return False
|
||||
|
||||
if isinstance(to_check, discord.Role):
|
||||
ids_to_check = [to_check.id]
|
||||
ids_to_check = {to_check.id}
|
||||
else:
|
||||
author = getattr(to_check, "author", to_check)
|
||||
if author.bot:
|
||||
return True
|
||||
ids_to_check = set()
|
||||
try:
|
||||
ids_to_check = [r.id for r in author.roles]
|
||||
ids_to_check = {r.id for r in author.roles}
|
||||
except AttributeError:
|
||||
# webhook messages are a user not member,
|
||||
# cheaper than isinstance
|
||||
if author.bot and author.discriminator == "0000":
|
||||
return True # webhooks require significant permissions to enable.
|
||||
else:
|
||||
ids_to_check.append(author.id)
|
||||
# cheaper than isinstance(author, discord.User)
|
||||
pass
|
||||
ids_to_check.add(author.id)
|
||||
|
||||
immune_ids = await self._config.guild(guild).autoimmune_ids()
|
||||
|
||||
return any(i in immune_ids for i in ids_to_check)
|
||||
return not ids_to_check.isdisjoint(immune_ids)
|
||||
|
||||
@staticmethod
|
||||
async def send_filtered(
|
||||
|
||||
@ -2568,7 +2568,9 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
"This will delete all bank accounts for {scope}.\nIf you're sure, type "
|
||||
"`{prefix}bankset reset yes`"
|
||||
).format(
|
||||
scope=self.bot.user.name if await bank.is_global() else _("this server"),
|
||||
scope=self.bot.user.display_name
|
||||
if await bank.is_global()
|
||||
else _("this server"),
|
||||
prefix=ctx.clean_prefix,
|
||||
)
|
||||
)
|
||||
@ -2576,7 +2578,9 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
await bank.wipe_bank(guild=ctx.guild)
|
||||
await ctx.send(
|
||||
_("All bank accounts for {scope} have been deleted.").format(
|
||||
scope=self.bot.user.name if await bank.is_global() else _("this server")
|
||||
scope=self.bot.user.display_name
|
||||
if await bank.is_global()
|
||||
else _("this server")
|
||||
)
|
||||
)
|
||||
|
||||
@ -3855,7 +3859,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
|
||||
"Global regional format: {regional_format}\n"
|
||||
"Default embed colour: {colour}"
|
||||
).format(
|
||||
bot_name=ctx.bot.user.name,
|
||||
bot_name=ctx.bot.user.display_name,
|
||||
prefixes=prefix_string,
|
||||
guild_settings=guild_settings,
|
||||
locale=locale,
|
||||
|
||||
@ -306,8 +306,8 @@ class Case:
|
||||
(note: it might not exist regardless of whether this attribute is `None`)
|
||||
or if it has never been created.
|
||||
last_known_username: Optional[str]
|
||||
The last known username of the user.
|
||||
`None` if the username of the user was never saved
|
||||
The last known user handle (``username`` / ``username#1234``) of the user.
|
||||
`None` if the handle of the user was never saved
|
||||
or if their data had to be anonymized.
|
||||
"""
|
||||
|
||||
@ -393,9 +393,9 @@ class Case:
|
||||
else:
|
||||
setattr(self, item, value)
|
||||
|
||||
# update last known username
|
||||
# update last known user handle
|
||||
if not isinstance(self.user, int):
|
||||
self.last_known_username = f"{self.user.name}#{self.user.discriminator}"
|
||||
self.last_known_username = str(self.user)
|
||||
|
||||
if isinstance(self.channel, discord.Thread):
|
||||
self.parent_channel_id = self.channel.parent_id
|
||||
@ -503,7 +503,15 @@ class Case:
|
||||
# can't use _() inside f-string expressions, see bpo-36310 and red#3818
|
||||
translated = _("Unknown or Deleted User")
|
||||
user = f"[{translated}] ({self.user})"
|
||||
# Handle pomelo usernames stored before we updated our implementation
|
||||
elif self.last_known_username.endswith("#0"):
|
||||
user = f"{self.last_known_username[:-2]} ({self.user})"
|
||||
# New usernames can't contain `#` and old usernames couldn't either.
|
||||
elif len(self.last_known_username) <= 5 or self.last_known_username[-5] != "#":
|
||||
user = f"{self.last_known_username} ({self.user})"
|
||||
# Last known user handle is a legacy username with a discriminator
|
||||
else:
|
||||
# isolate the name so that the direction of the discriminator and ID aren't changed
|
||||
# See usage explanation here: https://www.unicode.org/reports/tr9/#Formatting
|
||||
name = self.last_known_username[:-5]
|
||||
discriminator = self.last_known_username[-4:]
|
||||
@ -511,8 +519,10 @@ class Case:
|
||||
f"\N{FIRST STRONG ISOLATE}{name}"
|
||||
f"\N{POP DIRECTIONAL ISOLATE}#{discriminator} ({self.user})"
|
||||
)
|
||||
elif self.user.discriminator == "0":
|
||||
user = f"{self.user} ({self.user.id})"
|
||||
else:
|
||||
# isolate the name so that the direction of the discriminator and ID do not get changed
|
||||
# isolate the name so that the direction of the discriminator and ID aren't changed
|
||||
# See usage explanation here: https://www.unicode.org/reports/tr9/#Formatting
|
||||
user = escape_spoilers(
|
||||
filter_invites(
|
||||
@ -1019,7 +1029,7 @@ async def create_case(
|
||||
channel: Optional[Union[discord.abc.GuildChannel, discord.Thread]]
|
||||
The channel the action was taken in
|
||||
last_known_username: Optional[str]
|
||||
The last known username of the user
|
||||
The last known user handle (``username`` / ``username#1234``) of the user
|
||||
Note: This is ignored if a Member or User object is provided
|
||||
in the user field
|
||||
|
||||
|
||||
@ -162,7 +162,7 @@ class Tunnel(metaclass=TunnelMeta):
|
||||
|
||||
"""
|
||||
files = []
|
||||
max_size = 8 * 1000 * 1000
|
||||
max_size = 26214400
|
||||
if m.attachments and sum(a.size for a in m.attachments) <= max_size:
|
||||
for a in m.attachments:
|
||||
if images_only and a.height is None:
|
||||
|
||||
@ -28,7 +28,7 @@ click==8.1.3
|
||||
# via -r base.in
|
||||
contextlib2==21.6.0
|
||||
# via schema
|
||||
discord-py==2.2.3
|
||||
discord-py==2.3.0
|
||||
# via
|
||||
# -r base.in
|
||||
# red-lavalink
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user