import datetime import logging from typing import Union import asyncio import discord import humanize from discord.ext import commands from bot import TuxBot from .utils.lang import Texts log = logging.getLogger(__name__) class Admin(commands.Cog): def __init__(self, bot: TuxBot): self.bot = bot async def cog_check(self, ctx: commands.Context) -> bool: permissions: discord.Permissions = ctx.channel.permissions_for( ctx.author) has_permission = permissions.administrator is_owner = await self.bot.is_owner(ctx.author) return has_permission or is_owner @staticmethod async def kick_ban_message(ctx: commands.Context, **kwargs) -> discord.Embed: member: discord.Member = kwargs.get('member') reason = kwargs.get( 'reason', Texts('admin').get("Please enter a reason") ) if kwargs.get('type') == 'ban': title = '**Ban** ' + str(len(await ctx.guild.bans())) color = discord.Color.dark_red() else: title = '**Kick**' color = discord.Color.red() e = discord.Embed( title=title, description=reason, timestamp=datetime.datetime.utcnow(), color=color ) e.set_author( name=f'{member.name}#{member.discriminator} ({member.id})', icon_url=member.avatar_url_as(format='jpg') ) e.set_footer( text=f'{ctx.author.name}#{ctx.author.discriminator}', icon_url=ctx.author.avatar_url_as(format='png') ) return e """---------------------------------------------------------------------""" @commands.group(name='say', invoke_without_command=True) async def _say(self, ctx: commands.Context, *, content: str): try: await ctx.message.delete() except discord.errors.Forbidden: pass await ctx.send(content) @_say.command(name='edit') async def _say_edit(self, ctx: commands.Context, message_id: int, *, content: str): try: await ctx.message.delete() except discord.errors.Forbidden: pass try: message: discord.Message = await ctx.channel.fetch_message( message_id) await message.edit(content=content) except (discord.errors.NotFound, discord.errors.Forbidden): await ctx.send(Texts('utils').get("Unable to find the message"), delete_after=5) @_say.command(name='to') async def _say_to(self, ctx: commands.Context, channel: Union[discord.TextChannel, discord.User], *, content): try: await ctx.message.delete() except discord.errors.Forbidden: pass await channel.send(content) """---------------------------------------------------------------------""" @commands.command(name='ban') async def _ban(self, ctx: commands.Context, user: discord.Member, *, reason=""): try: member: discord.Member = await ctx.guild.fetch_member(user.id) try: await member.ban(reason=reason) e: discord.Embed = await self.kick_ban_message( ctx, member=member, type='ban', reason=reason ) await ctx.send(embed=e) except discord.Forbidden: await ctx.send(Texts('admin').get("Unable to ban this user"), delete_after=5) except discord.errors.NotFound: await ctx.send(Texts('utils').get("Unable to find the user..."), delete_after=5) """---------------------------------------------------------------------""" @commands.command(name='kick') async def _kick(self, ctx: commands.Context, user: discord.Member, *, reason=""): try: member: discord.Member = await ctx.guild.fetch_member(user.id) try: await member.kick(reason=reason) e: discord.Embed = await self.kick_ban_message( ctx, member=member, type='kick', reason=reason ) await ctx.send(embed=e) except discord.Forbidden: await ctx.send(Texts('admin').get("Unable to kick this user"), delete_after=5) except discord.errors.NotFound: await ctx.send(Texts('utils').get("Unable to find the user..."), delete_after=5) """---------------------------------------------------------------------""" @commands.command(name='clear') async def _clear(self, ctx: commands.Context, count: int): try: await ctx.message.delete() await ctx.channel.purge(limit=count) except discord.errors.Forbidden: pass """---------------------------------------------------------------------""" @commands.group(name='react') async def _react(self, ctx: commands.Context): if ctx.invoked_subcommand is None: return @_react.command(name='add') async def _react_add(self, ctx: commands.Context, message_id: int, *, emojis: str): emojis: list = emojis.split(' ') try: message: discord.Message = await ctx.channel.fetch_message( message_id) for emoji in emojis: await message.add_reaction(emoji) except discord.errors.NotFound: await ctx.send(Texts('utils').get("Unable to find the message"), delete_after=5) @_react.command(name='clear') async def _react_remove(self, ctx: commands.Context, message_id: int): try: message: discord.Message = await ctx.channel.fetch_message( message_id) await message.clear_reactions() except discord.errors.NotFound: await ctx.send(Texts('utils').get("Unable to find the message"), delete_after=5) """---------------------------------------------------------------------""" @commands.group(name='delete', invoke_without_command=True) async def _delete(self, ctx: commands.Context, message_id: int): try: await ctx.message.delete() except discord.errors.Forbidden: pass try: message: discord.Message = await ctx.channel.fetch_message( message_id) await message.delete() except (discord.errors.NotFound, discord.errors.Forbidden): await ctx.send(Texts('utils').get("Unable to find the message"), delete_after=5) @_delete.command(name='from', aliases=['to', 'in']) async def _delete_from(self, ctx: commands.Context, channel: discord.TextChannel, message_id: int): try: await ctx.message.delete() except discord.errors.Forbidden: pass try: message: discord.Message = await channel.fetch_message( message_id) await message.delete() except (discord.errors.NotFound, discord.errors.Forbidden): await ctx.send(Texts('utils').get("Unable to find the message"), delete_after=5) """---------------------------------------------------------------------""" async def get_warn(self, ctx: commands.Context, member: discord.Member = False): query = """ SELECT * FROM warns WHERE created_at >= $1 AND server_id = $2 """ query += """AND user_id = $3""" if member else "" query += """ORDER BY created_at DESC""" week_ago = datetime.datetime.now() - datetime.timedelta(weeks=6) async with self.bot.db.acquire() as con: await ctx.trigger_typing() args = [week_ago, ctx.guild.id] if member: args.append(member.id) warns = await con.fetch(query, *args) warns_list = '' for warn in warns: row_id = warn.get('id') user_id = warn.get('user_id') user = await self.bot.fetch_user(user_id) reason = warn.get('reason') ago = humanize.naturaldelta( datetime.datetime.now() - warn.get('created_at') ) warns_list += f"[{row_id}] **{user}**: `{reason}` " \ f"*({ago} ago)*\n" return warns_list, warns @commands.group(name='warn', aliases=['warns']) async def _warn(self, ctx: commands.Context): if ctx.invoked_subcommand is None: warns_list, warns = await self.get_warn(ctx) e = discord.Embed( title=f"{len(warns)} {Texts('admin').get('last warns')}: ", description=warns_list ) await ctx.send(embed=e) async def add_warn(self, ctx: commands.Context, member: discord.Member, reason): query = """ INSERT INTO warns (server_id, user_id, reason, created_at) VALUES ($1, $2, $3, $4) """ now = datetime.datetime.now() await self.bot.db.execute(query, ctx.guild.id, member.id, reason, now) @_warn.command(name='add', aliases=['new']) async def _warn_new(self, ctx: commands.Context, member: discord.Member, *, reason="N/A"): member = await ctx.guild.fetch_member(member.id) if not member: return await ctx.send( Texts('utils').get("Unable to find the user...") ) query = """ SELECT user_id, reason, created_at FROM warns WHERE created_at >= $1 AND server_id = $2 and user_id = $3 """ week_ago = datetime.datetime.now() - datetime.timedelta(weeks=6) def check(payload: discord.RawReactionActionEvent): if payload.message_id != choice.id \ or payload.user_id != ctx.author.id: return False return payload.emoji.name in ('1⃣', '2⃣', '3⃣') async with self.bot.db.acquire() as con: await ctx.trigger_typing() warns = await con.fetch(query, week_ago, ctx.guild.id, member.id) if len(warns) >= 2: e = discord.Embed( title=Texts('admin').get('More than 2 warns'), description=f"{member.mention} " + Texts('admin').get('has more than 2 warns') ) e.add_field( name='__Actions__', value=':one: kick\n' ':two: ban\n' ':three: ' + Texts('admin').get('ignore') ) choice = await ctx.send(embed=e) for reaction in ('1⃣', '2⃣', '3⃣'): await choice.add_reaction(reaction) try: payload = await self.bot.wait_for( 'raw_reaction_add', check=check, timeout=50.0 ) except asyncio.TimeoutError: return await ctx.send( Texts('admin').get('Took too long. Aborting.') ) finally: await choice.delete() if payload.emoji.name == '1⃣': from jishaku.models import copy_context_with alt_ctx = await copy_context_with( ctx, content=f"{ctx.prefix}" f"kick " f"{member} " f"{Texts('admin').get('More than 2 warns')}" ) return await alt_ctx.command.invoke(alt_ctx) elif payload.emoji.name == '2⃣': from jishaku.models import copy_context_with alt_ctx = await copy_context_with( ctx, content=f"{ctx.prefix}" f"ban " f"{member} " f"{Texts('admin').get('More than 2 warns')}" ) return await alt_ctx.command.invoke(alt_ctx) await self.add_warn(ctx, member, reason) await ctx.send( content=f"{member.mention} **{Texts('admin').get('got a warn')}**" f"\n**{Texts('admin').get('Reason')}:** `{reason}`" if reason != 'N/A' else '' ) @_warn.command(name='remove', aliases=['revoke']) async def _warn_remove(self, ctx: commands.Context, warn_id: int): query = """ DELETE FROM warns WHERE id = $1 """ async with self.bot.db.acquire() as con: await ctx.trigger_typing() await con.fetch(query, warn_id) await ctx.send(f"{Texts('admin').get('Warn with id')} `{warn_id}`" f" {Texts('admin').get('successfully removed')}") @_warn.command(name='show', aliases=['list']) async def _warn_show(self, ctx: commands.Context, member: discord.Member): warns_list, warns = await self.get_warn(ctx, member) e = discord.Embed( title=f"{len(warns)} {Texts('admin').get('last warns')}: ", description=warns_list ) await ctx.send(embed=e) @_warn.command(name='edit', aliases=['change']) async def _warn_edit(self, ctx: commands.Context, warn_id: int, *, reason): query = """ UPDATE warns SET reason = $2 WHERE id = $1 """ async with self.bot.db.acquire() as con: await ctx.trigger_typing() await con.fetch(query, warn_id, reason) await ctx.send(f"{Texts('admin').get('Warn with id')} `{warn_id}`" f" {Texts('admin').get('successfully edited')}") def setup(bot: TuxBot): bot.add_cog(Admin(bot))