mirror of
https://github.com/artiemis/artemis.git
synced 2026-02-14 08:31:55 +00:00
194 lines
6.1 KiB
Python
194 lines
6.1 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING, Optional
|
|
|
|
import discord
|
|
import pendulum
|
|
from discord.ext import commands
|
|
|
|
from utils.common import ArtemisError, parse_short_time
|
|
|
|
if TYPE_CHECKING:
|
|
from bot import Artemis
|
|
|
|
|
|
class ShortTime(commands.Converter):
|
|
async def convert(self, ctx: commands.Context, argument: str) -> pendulum.DateTime:
|
|
return parse_short_time(argument)
|
|
|
|
|
|
class Mod(commands.Cog):
|
|
def __init__(self, bot: Artemis):
|
|
self.bot: Artemis = bot
|
|
|
|
def cog_check(self, ctx: commands.Context):
|
|
if ctx.guild:
|
|
return True
|
|
raise commands.CheckFailure("This command cannot be used in private messages.")
|
|
|
|
@commands.command()
|
|
@commands.has_permissions(kick_members=True)
|
|
async def kick(self, ctx: commands.Context, member: discord.Member, *, reason: Optional[str]):
|
|
"""Kicks a member with an optional reason."""
|
|
if not reason:
|
|
reason = f"Action done by {ctx.author} ({ctx.author.id})"
|
|
else:
|
|
reason = f"{ctx.author} ({ctx.author.id}): {reason}"
|
|
await ctx.guild.kick(member, reason=reason)
|
|
await ctx.reply(f"Successfully kicked {member}.")
|
|
|
|
@commands.command()
|
|
@commands.has_permissions(ban_members=True)
|
|
async def ban(self, ctx: commands.Context, member: discord.Member, *, reason: Optional[str]):
|
|
"""Bans a member with an optional reason."""
|
|
if not reason:
|
|
reason = f"Action done by {ctx.author} ({ctx.author.id})"
|
|
else:
|
|
reason = f"{ctx.author} ({ctx.author.id}): {reason}"
|
|
await ctx.guild.ban(member, reason=reason)
|
|
await ctx.reply(f"Successfully banned {member}.")
|
|
|
|
@commands.command()
|
|
@commands.has_permissions(moderate_members=True)
|
|
async def mute(
|
|
self,
|
|
ctx: commands.Context,
|
|
member: discord.Member,
|
|
time: ShortTime,
|
|
*,
|
|
reason: Optional[str],
|
|
):
|
|
"""
|
|
Mutes a member for a specific time with an optional reason.
|
|
|
|
Usage examples:
|
|
`{prefix}mute 555412947883524098 2h`
|
|
`{prefix}mute Artemis 12h`
|
|
`{prefix}mute Artemis 2d12h for being nosy`
|
|
"""
|
|
max_timeout = pendulum.now("UTC").add(days=28)
|
|
if time > max_timeout:
|
|
raise ArtemisError("Mute time cannot exceed 28 days.")
|
|
|
|
if not reason:
|
|
reason = f"Action done by {ctx.author} ({ctx.author.id})"
|
|
else:
|
|
reason = f"{ctx.author} ({ctx.author.id}): {reason}"
|
|
|
|
await member.timeout(time, reason=reason)
|
|
return await ctx.reply(
|
|
f"Successfully muted {member} until {discord.utils.format_dt(time)}."
|
|
)
|
|
|
|
@commands.command()
|
|
@commands.has_permissions(moderate_members=True)
|
|
async def unmute(self, ctx: commands.Context, member: discord.Member):
|
|
"""
|
|
Unmutes a member.
|
|
"""
|
|
if not member.timed_out_until:
|
|
return await ctx.reply("This member is not muted.")
|
|
|
|
await member.timeout(None, reason=f"Action done by {ctx.author} ({ctx.author.id})")
|
|
return await ctx.reply(f"Successfully unmuted {member}.")
|
|
|
|
async def move_impl(
|
|
self,
|
|
ctx: commands.Context,
|
|
channel: discord.TextChannel,
|
|
*,
|
|
no_messages: int = None,
|
|
from_message_id: int = None,
|
|
to_message_id: int = None,
|
|
reason: str = None,
|
|
):
|
|
await ctx.typing()
|
|
|
|
webhooks = await channel.webhooks()
|
|
webhook = discord.utils.get(webhooks, name="Artemis")
|
|
|
|
if not webhook:
|
|
webhook = await channel.create_webhook(
|
|
name="Artemis", reason="Creating general-purpose webhook (called from $move)."
|
|
)
|
|
|
|
if no_messages:
|
|
messages = [msg async for msg in ctx.history(limit=no_messages + 1)]
|
|
messages = messages[::-1][:-1]
|
|
else:
|
|
messages = [
|
|
msg
|
|
async for msg in ctx.history(
|
|
after=discord.Object(from_message_id - 1),
|
|
before=discord.Object(to_message_id + 1),
|
|
)
|
|
]
|
|
|
|
for message in messages:
|
|
embeds = []
|
|
files = [await attachment.to_file() for attachment in message.attachments]
|
|
|
|
if not message.content and message.author.bot:
|
|
embeds = [embed for embed in message.embeds if embed.type == "rich"]
|
|
|
|
if not message.content and not files and not embeds:
|
|
continue
|
|
|
|
await webhook.send(
|
|
content=message.content,
|
|
username=message.author.display_name,
|
|
avatar_url=message.author.avatar.url,
|
|
files=files,
|
|
embeds=embeds,
|
|
)
|
|
await message.delete()
|
|
|
|
await ctx.message.delete()
|
|
|
|
if reason:
|
|
await channel.send(
|
|
f"Moved **{len(messages)}** messages from {ctx.channel.mention} for: `{reason}`",
|
|
delete_after=30,
|
|
)
|
|
else:
|
|
await channel.send(
|
|
f"Moved **{len(messages)}** messages from {ctx.channel.mention}.", delete_after=30
|
|
)
|
|
|
|
@commands.command()
|
|
@commands.has_permissions(manage_messages=True)
|
|
async def move(
|
|
self,
|
|
ctx: commands.Context,
|
|
channel: discord.TextChannel,
|
|
no_messages: int,
|
|
*,
|
|
reason: Optional[str],
|
|
):
|
|
"""Move `no_messages` to `channel` with an optional `reason`."""
|
|
await self.move_impl(ctx, channel, no_messages=no_messages, reason=reason)
|
|
|
|
@commands.command()
|
|
@commands.has_permissions(manage_messages=True)
|
|
async def move2(
|
|
self,
|
|
ctx: commands.Context,
|
|
channel: discord.TextChannel,
|
|
from_message_id: int,
|
|
to_message_id: int,
|
|
*,
|
|
reason: Optional[str],
|
|
):
|
|
"""Move `from_message_id`-`to_message_id` messages to `channel` with an optional `reason`."""
|
|
await self.move_impl(
|
|
ctx,
|
|
channel,
|
|
from_message_id=from_message_id,
|
|
to_message_id=to_message_id,
|
|
reason=reason,
|
|
)
|
|
|
|
|
|
async def setup(bot: Artemis):
|
|
await bot.add_cog(Mod(bot))
|