d.py 2.3 / pomelo changes (#6130)

Co-authored-by: Michael Oliveira <34169552+Flame442@users.noreply.github.com>
This commit is contained in:
Jakub Kuczys
2023-06-14 04:56:50 +02:00
committed by GitHub
parent 3abf4cac05
commit 10e09d6abc
20 changed files with 216 additions and 173 deletions

View File

@@ -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)

View File

@@ -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,

View File

@@ -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__()

View File

@@ -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."))

View File

@@ -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."
)
)