feat(shard): ✨ Add duration and reason arguments to p.ban.

This commit is contained in:
2025-03-18 13:24:22 -03:00
parent 57459699ea
commit 41b8bb8825
6 changed files with 62 additions and 12 deletions
+4 -2
View File
@@ -287,6 +287,7 @@ class Payphone(commands.AutoShardedBot):
report: models.Report | None, report: models.Report | None,
expires_at: datetime | None, expires_at: datetime | None,
created_by_id: int | None, created_by_id: int | None,
reason: str | None = None,
) -> models.Ban: ) -> models.Ban:
"""Bans a user. """Bans a user.
@@ -295,7 +296,8 @@ class Payphone(commands.AutoShardedBot):
report (Report | None): The report that caused the ban. report (Report | None): The report that caused the ban.
expires_at (datetime | None): When the ban expires. expires_at (datetime | None): When the ban expires.
created_by_id (int | None): The ID of the user who created the ban. created_by_id (int | None): The ID of the user who created the ban.
reason (str | None): The reason for the ban.
Returns: Returns:
(Ban): The ban. (Ban): The ban.
""" """
@@ -305,6 +307,7 @@ class Payphone(commands.AutoShardedBot):
report=report, report=report,
expires_at=expires_at, expires_at=expires_at,
created_by_id=created_by_id, created_by_id=created_by_id,
reason=reason,
) )
await ban.save() await ban.save()
@@ -456,7 +459,6 @@ def run():
sentry_sdk.init( sentry_sdk.init(
constants.SENTRY_DSN, constants.SENTRY_DSN,
environment="development" if constants.IS_DEBUG else "production", environment="development" if constants.IS_DEBUG else "production",
debug=constants.IS_DEBUG,
server_name=f"Shard #{constants.SHARD_ID + 1}", server_name=f"Shard #{constants.SHARD_ID + 1}",
attach_stacktrace=True, attach_stacktrace=True,
send_default_pii=True, send_default_pii=True,
+36 -8
View File
@@ -51,12 +51,19 @@ class Admin(base.Cog):
@commands.command(aliases=["b"]) @commands.command(aliases=["b"])
@checks.is_moderator() @checks.is_moderator()
async def ban( async def ban(
self, ctx: commands.Context, user: discord.User | int | None = None self,
ctx: commands.Context,
user: discord.User | int | None = None,
duration: str | None = None,
*,
reason: str | None = None,
) -> None: ) -> None:
"""Bans the user from the bot. """Bans the user from the bot.
Options: Options:
- `[user]`: The user to ban or their ID. If none is provided, the author will be banned. - `[user]`: The user to ban or their ID. If none is provided, the author will be banned.
- `[duration]`: The duration of the ban (e.g 5d, 4h, 1M). If none is provided, the ban will be permanent.
- `[reason]`: The reason for the ban.
Permissions: Permissions:
- Bot Moderator - Bot Moderator
@@ -64,7 +71,21 @@ class Admin(base.Cog):
user_id = user if isinstance(user, int) else user.id if user else ctx.author.id user_id = user if isinstance(user, int) else user.id if user else ctx.author.id
await self.bot.ban(user_id, None, None, ctx.author.id) if duration:
try:
duration = formatting.parse_timedelta(duration)
except ValueError:
reason = f"{duration} {reason}" if reason else duration
duration = None
await self.bot.ban(
user_id,
None,
datetime.now(timezone.utc) + duration if duration else None,
ctx.author.id,
reason,
)
await ctx.send(f"<@!{user_id}> was banned from Payphone.") await ctx.send(f"<@!{user_id}> was banned from Payphone.")
@@ -149,17 +170,19 @@ class Admin(base.Cog):
@commands.command() @commands.command()
@checks.is_moderator() @checks.is_moderator()
async def bans(self, ctx: commands.Context, user: discord.User | int) -> None: async def bans(
self, ctx: commands.Context, user: discord.User | int | None = None
) -> None:
"""Shows all bans of a specific user. """Shows all bans of a specific user.
Options: Options:
- `<user>`: The user to show the bans of. - `[user]`: The user to show the bans of.
Permissions: Permissions:
- Bot Moderator - Bot Moderator
""" """
user_id = user if isinstance(user, int) else user.id user_id = user if isinstance(user, int) else user.id if user else ctx.author.id
bans = ( bans = (
await models.Ban.filter(user_id=user_id) await models.Ban.filter(user_id=user_id)
@@ -175,7 +198,7 @@ class Admin(base.Cog):
[ [
( (
discord.Embed( discord.Embed(
color=constants.PRIMARY_COLOR, color=constants.MODERATOR_COLOR,
title=f"Ban #{ban.id}", title=f"Ban #{ban.id}",
description=formatting.trim(ban.report.message_content, 500), description=formatting.trim(ban.report.message_content, 500),
author=discord.EmbedAuthor( author=discord.EmbedAuthor(
@@ -218,9 +241,14 @@ class Admin(base.Cog):
) )
if ban.report if ban.report
else discord.Embed( else discord.Embed(
color=constants.PRIMARY_COLOR, color=constants.MODERATOR_COLOR,
title=f"Ban #{ban.id}", title=f"Ban #{ban.id}",
description=f"The user was banned manually by <@!{ban.created_by.id}> <t:{int(ban.created_at.timestamp())}:R>. It expires {f"<t:{int(ban.expires_at.timestamp())}:R>" if ban.expires_at else "never"}.", description=f"The user was banned manually by <@!{ban.created_by.id}> <t:{int(ban.created_at.timestamp())}:R>. {(f"It expires <t:{int(ban.expires_at.timestamp())}:R>" if not ban.is_expired else f"It expired <t:{int(ban.expires_at.timestamp())}:R>") if ban.expires_at else "It doesn't expire"}.",
fields=[
discord.EmbedField(
name="Reason", value=ban.reason or "No reason provided."
)
],
footer=discord.EmbedFooter( footer=discord.EmbedFooter(
text=f"Banned by {(await self.bot.get_or_fetch_user(ban.created_by.id)).display_name}" text=f"Banned by {(await self.bot.get_or_fetch_user(ban.created_by.id)).display_name}"
+ ( + (
+1 -1
View File
@@ -201,7 +201,7 @@ def parse_timedelta(text: str) -> timedelta:
) )
matches = pattern.match(text.strip()) matches = pattern.match(text.strip())
if not matches: if matches.span() == (0, 0):
raise ValueError("Invalid time string format") raise ValueError("Invalid time string format")
years, months, weeks, days, hours, minutes, seconds = matches.groups() years, months, weeks, days, hours, minutes, seconds = matches.groups()
@@ -0,0 +1,11 @@
from tortoise import BaseDBAsyncClient
async def upgrade(db: BaseDBAsyncClient) -> str:
return """
ALTER TABLE "ban" ADD "reason" TEXT;"""
async def downgrade(db: BaseDBAsyncClient) -> str:
return """
ALTER TABLE "ban" DROP COLUMN "reason";"""
+9
View File
@@ -146,11 +146,20 @@ class Ban(models.Model):
user_id = fields.BigIntField() user_id = fields.BigIntField()
report = fields.ForeignKeyField("models.Report", null=True) report = fields.ForeignKeyField("models.Report", null=True)
reason = fields.TextField(null=True, default=None)
created_at = fields.DatetimeField(auto_now_add=True) created_at = fields.DatetimeField(auto_now_add=True)
expires_at = fields.DatetimeField(null=True) expires_at = fields.DatetimeField(null=True)
created_by = fields.ForeignKeyField("models.User", related_name="created_bans") created_by = fields.ForeignKeyField("models.User", related_name="created_bans")
expired_by = fields.ForeignKeyField("models.User", related_name="expired_bans", null=True) expired_by = fields.ForeignKeyField("models.User", related_name="expired_bans", null=True)
@property
def is_expired(self) -> bool:
"""
Whether the ban is expired.
"""
return self.expires_at is not None and self.expires_at < datetime.now(timezone.utc)
class Meta: class Meta:
""" """
Meta information. Meta information.
Generated
+1 -1
View File
@@ -413,7 +413,7 @@ wheels = [
[[package]] [[package]]
name = "payphone" name = "payphone"
version = "0.58.0" version = "0.59.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "aerich" }, { name = "aerich" },