mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-06 11:18:54 -05:00
Allow [p]ban to hackban and rename [p]hackban to [p]massban (#4422)
* ban revamp * black & converters fix * discord.object * Update redbot/cogs/admin/admin.py Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com> * remove discord.user converter * black * . * Update redbot/cogs/mod/kickban.py Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com> * Update redbot/cogs/mod/kickban.py Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com> * Update redbot/cogs/mod/kickban.py Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com> * Update redbot/cogs/mod/kickban.py Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com> * incomplete * massban support * black * Use 2-tuple to separate result and the message in `ban_user()` This also fixes the issue with `True` being equal to `1` which caused a problem with previously returned types * Whoops... * trailing whitespace... * I missed this one * Update kickban.py * Update kickban.py Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com>
This commit is contained in:
parent
47c4edf335
commit
dc817aeeac
@ -2,7 +2,7 @@ import asyncio
|
|||||||
import contextlib
|
import contextlib
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Optional, Union
|
from typing import Optional, Tuple, Union
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from redbot.core import commands, i18n, checks, modlog
|
from redbot.core import commands, i18n, checks, modlog
|
||||||
@ -62,67 +62,109 @@ class KickBanMixin(MixinMeta):
|
|||||||
|
|
||||||
async def ban_user(
|
async def ban_user(
|
||||||
self,
|
self,
|
||||||
user: discord.Member,
|
user: Union[discord.Member, discord.User, discord.Object],
|
||||||
ctx: commands.Context,
|
ctx: commands.Context,
|
||||||
days: int = 0,
|
days: int = 0,
|
||||||
reason: str = None,
|
reason: str = None,
|
||||||
create_modlog_case=False,
|
create_modlog_case=False,
|
||||||
) -> Union[str, bool]:
|
) -> Tuple[bool, str]:
|
||||||
author = ctx.author
|
author = ctx.author
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
|
|
||||||
if author == user:
|
removed_temp = False
|
||||||
return _("I cannot let you do that. Self-harm is bad {}").format("\N{PENSIVE FACE}")
|
|
||||||
elif not await is_allowed_by_hierarchy(self.bot, self.config, guild, author, user):
|
|
||||||
return _(
|
|
||||||
"I cannot let you do that. You are "
|
|
||||||
"not higher than the user in the role "
|
|
||||||
"hierarchy."
|
|
||||||
)
|
|
||||||
elif guild.me.top_role <= user.top_role or user == guild.owner:
|
|
||||||
return _("I cannot do that due to Discord hierarchy rules.")
|
|
||||||
elif not (0 <= days <= 7):
|
|
||||||
return _("Invalid days. Must be between 0 and 7.")
|
|
||||||
|
|
||||||
toggle = await self.config.guild(guild).dm_on_kickban()
|
if not (0 <= days <= 7):
|
||||||
if toggle:
|
return False, _("Invalid days. Must be between 0 and 7.")
|
||||||
with contextlib.suppress(discord.HTTPException):
|
|
||||||
em = discord.Embed(
|
if isinstance(user, discord.Member):
|
||||||
title=bold(_("You have been banned from {guild}.").format(guild=guild))
|
if author == user:
|
||||||
|
return (
|
||||||
|
False,
|
||||||
|
_("I cannot let you do that. Self-harm is bad {}").format("\N{PENSIVE FACE}"),
|
||||||
)
|
)
|
||||||
em.add_field(
|
elif not await is_allowed_by_hierarchy(self.bot, self.config, guild, author, user):
|
||||||
name=_("**Reason**"),
|
return (
|
||||||
value=reason if reason is not None else _("No reason was given."),
|
False,
|
||||||
inline=False,
|
_(
|
||||||
|
"I cannot let you do that. You are "
|
||||||
|
"not higher than the user in the role "
|
||||||
|
"hierarchy."
|
||||||
|
),
|
||||||
)
|
)
|
||||||
await user.send(embed=em)
|
elif guild.me.top_role <= user.top_role or user == guild.owner:
|
||||||
|
return False, _("I cannot do that due to Discord hierarchy rules.")
|
||||||
|
|
||||||
|
toggle = await self.config.guild(guild).dm_on_kickban()
|
||||||
|
if toggle:
|
||||||
|
with contextlib.suppress(discord.HTTPException):
|
||||||
|
em = discord.Embed(
|
||||||
|
title=bold(_("You have been banned from {guild}.").format(guild=guild))
|
||||||
|
)
|
||||||
|
em.add_field(
|
||||||
|
name=_("**Reason**"),
|
||||||
|
value=reason if reason is not None else _("No reason was given."),
|
||||||
|
inline=False,
|
||||||
|
)
|
||||||
|
await user.send(embed=em)
|
||||||
|
|
||||||
|
ban_type = "ban"
|
||||||
|
else:
|
||||||
|
tempbans = await self.config.guild(guild).current_tempbans()
|
||||||
|
|
||||||
|
ban_list = [ban.user.id for ban in await guild.bans()]
|
||||||
|
if user.id in ban_list:
|
||||||
|
if user.id in tempbans:
|
||||||
|
async with self.config.guild(guild).current_tempbans() as tempbans:
|
||||||
|
tempbans.remove(user.id)
|
||||||
|
removed_temp = True
|
||||||
|
else:
|
||||||
|
return (
|
||||||
|
False,
|
||||||
|
_("User with ID {user_id} is already banned.").format(user_id=user.id),
|
||||||
|
)
|
||||||
|
|
||||||
|
ban_type = "hackban"
|
||||||
|
|
||||||
audit_reason = get_audit_reason(author, reason)
|
audit_reason = get_audit_reason(author, reason)
|
||||||
|
|
||||||
queue_entry = (guild.id, user.id)
|
queue_entry = (guild.id, user.id)
|
||||||
try:
|
if removed_temp:
|
||||||
await guild.ban(user, reason=audit_reason, delete_message_days=days)
|
|
||||||
log.info(
|
log.info(
|
||||||
"{}({}) banned {}({}), deleting {} days worth of messages.".format(
|
"{}({}) upgraded the tempban for {} to a permaban.".format(
|
||||||
author.name, author.id, user.name, user.id, str(days)
|
author.name, author.id, user.id
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except discord.Forbidden:
|
success_message = _(
|
||||||
return _("I'm not allowed to do that.")
|
"User with ID {user_id} was upgraded from a temporary to a permanent ban."
|
||||||
except Exception as e:
|
).format(user_id=user.id)
|
||||||
log.exception(
|
else:
|
||||||
"{}({}) attempted to kick {}({}), but an error occurred.".format(
|
username = user.name if hasattr(user, "name") else "Unknown"
|
||||||
author.name, author.id, user.name, user.id
|
try:
|
||||||
|
await guild.ban(user, reason=audit_reason, delete_message_days=days)
|
||||||
|
log.info(
|
||||||
|
"{}({}) {}ned {}({}), deleting {} days worth of messages.".format(
|
||||||
|
author.name, author.id, ban_type, username, user.id, str(days)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
success_message = _("Done. That felt good.")
|
||||||
return _("An unexpected error occurred.")
|
except discord.Forbidden:
|
||||||
|
return False, _("I'm not allowed to do that.")
|
||||||
|
except discord.NotFound:
|
||||||
|
return False, _("User with ID {user_id} not found").format(user_id=user.id)
|
||||||
|
except Exception as e:
|
||||||
|
log.exception(
|
||||||
|
"{}({}) attempted to {} {}({}), but an error occurred.".format(
|
||||||
|
author.name, author.id, ban_type, username, user.id
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return False, _("An unexpected error occurred.")
|
||||||
|
|
||||||
if create_modlog_case:
|
if create_modlog_case:
|
||||||
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),
|
||||||
"ban",
|
ban_type,
|
||||||
user,
|
user,
|
||||||
author,
|
author,
|
||||||
reason,
|
reason,
|
||||||
@ -130,7 +172,7 @@ class KickBanMixin(MixinMeta):
|
|||||||
channel=None,
|
channel=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
return True
|
return True, success_message
|
||||||
|
|
||||||
async def check_tempban_expirations(self):
|
async def check_tempban_expirations(self):
|
||||||
while self == self.bot.get_cog("Mod"):
|
while self == self.bot.get_cog("Mod"):
|
||||||
@ -247,13 +289,15 @@ class KickBanMixin(MixinMeta):
|
|||||||
async def ban(
|
async def ban(
|
||||||
self,
|
self,
|
||||||
ctx: commands.Context,
|
ctx: commands.Context,
|
||||||
user: discord.Member,
|
user: Union[discord.Member, RawUserIds],
|
||||||
days: Optional[int] = None,
|
days: Optional[int] = None,
|
||||||
*,
|
*,
|
||||||
reason: str = None,
|
reason: str = None,
|
||||||
):
|
):
|
||||||
"""Ban a user from this server and optionally delete days of messages.
|
"""Ban a user from this server and optionally delete days 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.
|
If days is not a number, it's treated as the first word of the reason.
|
||||||
|
|
||||||
Minimum 0 days, maximum 7. If not specified, defaultdays setting will be used instead."""
|
Minimum 0 days, maximum 7. If not specified, defaultdays setting will be used instead."""
|
||||||
@ -261,21 +305,20 @@ class KickBanMixin(MixinMeta):
|
|||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
if days is None:
|
if days is None:
|
||||||
days = await self.config.guild(guild).default_days()
|
days = await self.config.guild(guild).default_days()
|
||||||
|
if isinstance(user, int):
|
||||||
|
user = self.bot.get_user(user) or discord.Object(id=user)
|
||||||
|
|
||||||
result = await self.ban_user(
|
success_, message = await self.ban_user(
|
||||||
user=user, ctx=ctx, days=days, reason=reason, create_modlog_case=True
|
user=user, ctx=ctx, days=days, reason=reason, create_modlog_case=True
|
||||||
)
|
)
|
||||||
|
|
||||||
if result is True:
|
await ctx.send(message)
|
||||||
await ctx.send(_("Done. It was about time."))
|
|
||||||
elif isinstance(result, str):
|
|
||||||
await ctx.send(result)
|
|
||||||
|
|
||||||
@commands.command()
|
@commands.command(aliases=["hackban"])
|
||||||
@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 hackban(
|
async def massban(
|
||||||
self,
|
self,
|
||||||
ctx: commands.Context,
|
ctx: commands.Context,
|
||||||
user_ids: commands.Greedy[RawUserIds],
|
user_ids: commands.Greedy[RawUserIds],
|
||||||
@ -283,7 +326,7 @@ class KickBanMixin(MixinMeta):
|
|||||||
*,
|
*,
|
||||||
reason: str = None,
|
reason: str = None,
|
||||||
):
|
):
|
||||||
"""Preemptively bans user(s) from the server.
|
"""Mass bans user(s) from the server.
|
||||||
|
|
||||||
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."""
|
||||||
@ -339,7 +382,7 @@ class KickBanMixin(MixinMeta):
|
|||||||
# We need to check if a user is tempbanned here because otherwise they won't be processed later on.
|
# We need to check if a user is tempbanned here because otherwise they won't be processed later on.
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
errors[user_id] = _("User {user_id} is already banned.").format(
|
errors[user_id] = _("User with ID {user_id} is already banned.").format(
|
||||||
user_id=user_id
|
user_id=user_id
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -358,14 +401,14 @@ class KickBanMixin(MixinMeta):
|
|||||||
else:
|
else:
|
||||||
# Instead of replicating all that handling... gets attr from decorator
|
# Instead of replicating all that handling... gets attr from decorator
|
||||||
try:
|
try:
|
||||||
result = await self.ban_user(
|
success, reason = await self.ban_user(
|
||||||
user=user, ctx=ctx, days=days, reason=reason, create_modlog_case=True
|
user=user, ctx=ctx, days=days, reason=reason, create_modlog_case=True
|
||||||
)
|
)
|
||||||
if result is True:
|
if success:
|
||||||
banned.append(user_id)
|
banned.append(user_id)
|
||||||
else:
|
else:
|
||||||
errors[user_id] = _("Failed to ban user {user_id}: {reason}").format(
|
errors[user_id] = _("Failed to ban user {user_id}: {reason}").format(
|
||||||
user_id=user_id, reason=result
|
user_id=user_id, reason=reason
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
errors[user_id] = _("Failed to ban user {user_id}: {reason}").format(
|
errors[user_id] = _("Failed to ban user {user_id}: {reason}").format(
|
||||||
@ -397,13 +440,13 @@ class KickBanMixin(MixinMeta):
|
|||||||
await guild.ban(user, reason=audit_reason, delete_message_days=days)
|
await guild.ban(user, reason=audit_reason, delete_message_days=days)
|
||||||
log.info("{}({}) hackbanned {}".format(author.name, author.id, user_id))
|
log.info("{}({}) hackbanned {}".format(author.name, author.id, user_id))
|
||||||
except discord.NotFound:
|
except discord.NotFound:
|
||||||
errors[user_id] = _("User {user_id} does not exist.").format(
|
errors[user_id] = _("User with ID {user_id} not found").format(
|
||||||
user_id=user_id
|
user_id=user_id
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
except discord.Forbidden:
|
except discord.Forbidden:
|
||||||
errors[user_id] = _(
|
errors[user_id] = _(
|
||||||
"Could not ban {user_id}: missing permissions."
|
"Could not ban user with ID {user_id}: missing permissions."
|
||||||
).format(user_id=user_id)
|
).format(user_id=user_id)
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user