mirror of
https://github.com/neoarz/Syntrel.git
synced 2025-12-25 03:40:11 +01:00
feat(timeout): new command :)
This commit is contained in:
@@ -33,7 +33,7 @@ def crash_command():
|
||||
view.add_item(discord.ui.Button(
|
||||
label="Edit Command",
|
||||
style=discord.ButtonStyle.secondary,
|
||||
url="https://github.com/neoarz/Syntrel/blob/main/cogs/melonx/crash.py",
|
||||
url="https://github.com/neoarz/Syntrel/blob/main/cogs/melonx/gamecrash.py",
|
||||
emoji="<:githubicon:1417717356846776340>"
|
||||
))
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import discord
|
||||
from discord import app_commands
|
||||
from discord.ext import commands
|
||||
from discord.ext.commands import Context
|
||||
|
||||
@@ -9,6 +10,7 @@ from .warnings import warnings_command
|
||||
from .archive import archive_command
|
||||
from .hackban import hackban_command
|
||||
from .nick import nick_command
|
||||
from .timeout import timeout_command
|
||||
|
||||
class Moderation(commands.GroupCog, name="moderation"):
|
||||
def __init__(self, bot) -> None:
|
||||
@@ -22,7 +24,7 @@ class Moderation(commands.GroupCog, name="moderation"):
|
||||
description="Use `.moderation <subcommand>` or `/moderation <subcommand>`.",
|
||||
color=0x7289DA
|
||||
)
|
||||
embed.add_field(name="Available", value="ban, kick, purge, warnings, archive, hackban, nick", inline=False)
|
||||
embed.add_field(name="Available", value="ban, kick, purge, warnings, archive, hackban, nick, timeout", inline=False)
|
||||
await context.send(embed=embed)
|
||||
|
||||
async def _invoke_hybrid(self, context: Context, name: str, **kwargs):
|
||||
@@ -42,6 +44,26 @@ class Moderation(commands.GroupCog, name="moderation"):
|
||||
content = context.message.content.strip().lower()
|
||||
return content.startswith(f"{prefix}{group} ")
|
||||
|
||||
async def timeout_duration_autocomplete(
|
||||
self,
|
||||
interaction: discord.Interaction,
|
||||
current: str,
|
||||
) -> list[app_commands.Choice[str]]:
|
||||
options = [
|
||||
("60 secs", "60s"),
|
||||
("5 mins", "5m"),
|
||||
("10 mins", "10m"),
|
||||
("1 hour", "1h"),
|
||||
("1 day", "1d"),
|
||||
("1 week", "1w"),
|
||||
]
|
||||
q = (current or "").lower()
|
||||
results: list[app_commands.Choice[str]] = []
|
||||
for name, value in options:
|
||||
if q in name.lower() or q in value.lower():
|
||||
results.append(app_commands.Choice(name=name, value=value))
|
||||
return results[:25]
|
||||
|
||||
@moderation_group.command(name="ban")
|
||||
async def moderation_group_ban(self, context: Context, user: discord.User, *, reason: str = "Not specified", delete_messages: str = "none"):
|
||||
await self._invoke_hybrid(context, "ban", user=user, reason=reason, delete_messages=delete_messages)
|
||||
@@ -70,6 +92,10 @@ class Moderation(commands.GroupCog, name="moderation"):
|
||||
async def moderation_group_nick(self, context: Context, user: discord.User, *, nickname: str = None):
|
||||
await self._invoke_hybrid(context, "nick", user=user, nickname=nickname)
|
||||
|
||||
@moderation_group.command(name="timeout")
|
||||
async def moderation_group_timeout(self, context: Context, user: discord.User, duration: str, *, reason: str = "Not specified"):
|
||||
await self._invoke_hybrid(context, "timeout", user=user, duration=duration, reason=reason)
|
||||
|
||||
@commands.check(_require_group_prefix)
|
||||
@commands.hybrid_command(
|
||||
name="ban",
|
||||
@@ -126,6 +152,20 @@ class Moderation(commands.GroupCog, name="moderation"):
|
||||
async def nick(self, context, user: discord.User, *, nickname: str = None):
|
||||
return await nick_command()(self, context, user=user, nickname=nickname)
|
||||
|
||||
@commands.check(_require_group_prefix)
|
||||
@commands.hybrid_command(
|
||||
name="timeout",
|
||||
description="Timeout a user for a specified duration."
|
||||
)
|
||||
@app_commands.describe(
|
||||
user="The user that should be timed out.",
|
||||
duration="Duration",
|
||||
reason="The reason why the user should be timed out.",
|
||||
)
|
||||
@app_commands.autocomplete(duration=timeout_duration_autocomplete)
|
||||
async def timeout(self, context, user: discord.User, duration: str, *, reason: str = "Not specified"):
|
||||
return await timeout_command()(self, context, user=user, duration=duration, reason=reason)
|
||||
|
||||
async def setup(bot) -> None:
|
||||
cog = Moderation(bot)
|
||||
await bot.add_cog(cog)
|
||||
@@ -137,3 +177,4 @@ async def setup(bot) -> None:
|
||||
bot.logger.info("Loaded extension 'moderation.archive'")
|
||||
bot.logger.info("Loaded extension 'moderation.hackban'")
|
||||
bot.logger.info("Loaded extension 'moderation.nick'")
|
||||
bot.logger.info("Loaded extension 'moderation.timeout'")
|
||||
|
||||
176
cogs/moderation/timeout.py
Normal file
176
cogs/moderation/timeout.py
Normal file
@@ -0,0 +1,176 @@
|
||||
import discord
|
||||
from discord import app_commands
|
||||
from discord.ext import commands
|
||||
from datetime import timedelta
|
||||
|
||||
|
||||
def timeout_command():
|
||||
DURATION_CHOICES = [
|
||||
app_commands.Choice(name="60 secs", value="60s"),
|
||||
app_commands.Choice(name="5 mins", value="5m"),
|
||||
app_commands.Choice(name="10 mins", value="10m"),
|
||||
app_commands.Choice(name="1 hour", value="1h"),
|
||||
app_commands.Choice(name="1 day", value="1d"),
|
||||
app_commands.Choice(name="1 week", value="1w"),
|
||||
]
|
||||
@commands.hybrid_command(
|
||||
name="timeout",
|
||||
description="Timeout a user for a specified duration.",
|
||||
)
|
||||
@app_commands.describe(
|
||||
user="The user that should be timed out.",
|
||||
duration="Duration",
|
||||
reason="The reason why the user should be timed out.",
|
||||
)
|
||||
@app_commands.choices(duration=DURATION_CHOICES)
|
||||
async def timeout(
|
||||
self, context, user: discord.User, duration: str, *, reason: str = "Not specified"
|
||||
):
|
||||
try:
|
||||
member = context.guild.get_member(user.id)
|
||||
if not member:
|
||||
try:
|
||||
member = await context.guild.fetch_member(user.id)
|
||||
except discord.NotFound:
|
||||
embed = discord.Embed(
|
||||
title="Error!",
|
||||
description="This user is not in the server.",
|
||||
color=0xE02B2B,
|
||||
).set_author(name="Moderation", icon_url="https://yes.nighty.works/raw/CPKHQd.png")
|
||||
await context.send(embed=embed, ephemeral=True)
|
||||
return
|
||||
|
||||
if not context.author.guild_permissions.moderate_members and context.author != context.guild.owner:
|
||||
embed = discord.Embed(
|
||||
title="Missing Permissions!",
|
||||
description="You don't have the `Timeout Members` permission to use this command.",
|
||||
color=0xE02B2B,
|
||||
).set_author(name="Moderation", icon_url="https://yes.nighty.works/raw/CPKHQd.png")
|
||||
await context.send(embed=embed, ephemeral=True)
|
||||
return
|
||||
|
||||
if member and member.top_role >= context.guild.me.top_role:
|
||||
embed = discord.Embed(
|
||||
title="Cannot Timeout User",
|
||||
description="This user has a higher or equal role to me. Make sure my role is above theirs.",
|
||||
color=0xE02B2B,
|
||||
).set_author(name="Moderation", icon_url="https://yes.nighty.works/raw/CPKHQd.png")
|
||||
await context.send(embed=embed, ephemeral=True)
|
||||
return
|
||||
|
||||
if member and context.author != context.guild.owner:
|
||||
if member.top_role >= context.author.top_role:
|
||||
embed = discord.Embed(
|
||||
title="Cannot Timeout User",
|
||||
description="You cannot timeout this user as they have a higher or equal role to you.",
|
||||
color=0xE02B2B,
|
||||
).set_author(name="Moderation", icon_url="https://yes.nighty.works/raw/CPKHQd.png")
|
||||
await context.send(embed=embed, ephemeral=True)
|
||||
return
|
||||
|
||||
seconds = _parse_duration_to_seconds(duration)
|
||||
if seconds is None or seconds <= 0:
|
||||
embed = discord.Embed(
|
||||
title="Invalid Duration",
|
||||
description="Choose one of: 60 secs, 5 mins, 10 mins, 1 hour, 1 day, 1 week.",
|
||||
color=0xE02B2B,
|
||||
).set_author(name="Moderation", icon_url="https://yes.nighty.works/raw/CPKHQd.png")
|
||||
await context.send(embed=embed, ephemeral=True)
|
||||
return
|
||||
|
||||
max_seconds = 28 * 24 * 60 * 60
|
||||
seconds = min(seconds, max_seconds)
|
||||
|
||||
try:
|
||||
timeout_delta = timedelta(seconds=seconds)
|
||||
await member.timeout(timeout_delta, reason=reason)
|
||||
|
||||
embed = discord.Embed(
|
||||
title="Timeout",
|
||||
description=f"**{user}** was timed out by **{context.author}**!",
|
||||
color=0x7289DA,
|
||||
).set_author(name="Moderation", icon_url="https://yes.nighty.works/raw/CPKHQd.png")
|
||||
embed.add_field(name="Reason:", value=reason)
|
||||
embed.add_field(name="Duration:", value=_format_duration(duration), inline=False)
|
||||
await context.send(embed=embed)
|
||||
|
||||
except discord.Forbidden:
|
||||
embed = discord.Embed(
|
||||
title="Error!",
|
||||
description="I don't have permission to timeout this user. Make sure my role is above theirs.",
|
||||
color=0xE02B2B,
|
||||
).set_author(name="Moderation", icon_url="https://yes.nighty.works/raw/CPKHQd.png")
|
||||
await context.send(embed=embed, ephemeral=True)
|
||||
except discord.HTTPException as e:
|
||||
embed = discord.Embed(
|
||||
title="Error!",
|
||||
description=f"Discord API error: {str(e)}",
|
||||
color=0xE02B2B,
|
||||
).set_author(name="Moderation", icon_url="https://yes.nighty.works/raw/CPKHQd.png")
|
||||
await context.send(embed=embed, ephemeral=True)
|
||||
except Exception as e:
|
||||
embed = discord.Embed(
|
||||
title="Debug Error!",
|
||||
description=f"Error type: {type(e).__name__}\nError message: {str(e)}",
|
||||
color=0xE02B2B,
|
||||
).set_author(name="Moderation", icon_url="https://yes.nighty.works/raw/CPKHQd.png")
|
||||
await context.send(embed=embed, ephemeral=True)
|
||||
|
||||
except Exception:
|
||||
embed = discord.Embed(
|
||||
title="Error!",
|
||||
description="An unexpected error occurred.",
|
||||
color=0xE02B2B,
|
||||
).set_author(name="Moderation", icon_url="https://yes.nighty.works/raw/CPKHQd.png")
|
||||
await context.send(embed=embed, ephemeral=True)
|
||||
|
||||
@timeout.autocomplete("duration")
|
||||
async def duration_autocomplete(interaction: discord.Interaction, current: str):
|
||||
query = (current or "").lower()
|
||||
filtered = [c for c in DURATION_CHOICES if query in c.name.lower() or query in c.value.lower()]
|
||||
return filtered[:25]
|
||||
|
||||
return timeout
|
||||
|
||||
|
||||
def _parse_duration_to_seconds(duration: str):
|
||||
try:
|
||||
s = duration.strip().lower()
|
||||
if s.endswith("s"):
|
||||
return int(s[:-1])
|
||||
if s.endswith("m"):
|
||||
return int(s[:-1]) * 60
|
||||
if s.endswith("h"):
|
||||
return int(s[:-1]) * 60 * 60
|
||||
if s.endswith("d"):
|
||||
return int(s[:-1]) * 60 * 60 * 24
|
||||
if s.endswith("w"):
|
||||
return int(s[:-1]) * 60 * 60 * 24 * 7
|
||||
return int(s)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _format_duration(duration: str) -> str:
|
||||
mapping = {
|
||||
"s": "seconds",
|
||||
"m": "minutes",
|
||||
"h": "hours",
|
||||
"d": "days",
|
||||
"w": "weeks",
|
||||
}
|
||||
s = duration.strip().lower()
|
||||
for suffix, word in mapping.items():
|
||||
if s.endswith(suffix):
|
||||
try:
|
||||
value = int(s[:-1])
|
||||
return f"{value} {word}"
|
||||
except Exception:
|
||||
return duration
|
||||
try:
|
||||
value = int(s)
|
||||
return f"{value} seconds"
|
||||
except Exception:
|
||||
return duration
|
||||
|
||||
|
||||
Reference in New Issue
Block a user