2019-09-12 15:43:57 +02:00
|
|
|
import datetime
|
2019-09-22 04:18:28 +02:00
|
|
|
import logging
|
2019-09-13 11:20:17 +02:00
|
|
|
from typing import Union
|
2019-09-12 15:43:57 +02:00
|
|
|
|
2019-09-22 04:18:28 +02:00
|
|
|
import asyncio
|
2019-09-12 15:43:57 +02:00
|
|
|
import discord
|
2019-09-19 01:48:52 +02:00
|
|
|
import humanize
|
2019-09-21 00:11:29 +02:00
|
|
|
from discord.ext import commands
|
2019-09-12 15:43:57 +02:00
|
|
|
|
2019-09-11 15:59:37 +02:00
|
|
|
from bot import TuxBot
|
2019-09-12 15:43:57 +02:00
|
|
|
from .utils.lang import Texts
|
2018-12-03 01:26:23 +01:00
|
|
|
|
2019-09-22 04:18:28 +02:00
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
2019-05-30 00:59:20 +02:00
|
|
|
|
|
|
|
class Admin(commands.Cog):
|
|
|
|
|
2019-09-11 15:59:37 +02:00
|
|
|
def __init__(self, bot: TuxBot):
|
2019-05-30 00:59:20 +02:00
|
|
|
self.bot = bot
|
|
|
|
|
2019-09-19 01:48:52 +02:00
|
|
|
async def cog_check(self, ctx: commands.Context) -> bool:
|
2019-09-12 17:21:00 +02:00
|
|
|
permissions: discord.Permissions = ctx.channel.permissions_for(
|
|
|
|
ctx.author)
|
2019-09-12 15:43:57 +02:00
|
|
|
|
|
|
|
has_permission = permissions.administrator
|
|
|
|
is_owner = await self.bot.is_owner(ctx.author)
|
|
|
|
|
|
|
|
return has_permission or is_owner
|
|
|
|
|
|
|
|
@staticmethod
|
2019-09-12 17:21:00 +02:00
|
|
|
async def kick_ban_message(ctx: commands.Context,
|
|
|
|
**kwargs) -> discord.Embed:
|
2019-09-12 15:43:57 +02:00
|
|
|
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
|
2019-05-30 00:59:20 +02:00
|
|
|
|
|
|
|
"""---------------------------------------------------------------------"""
|
|
|
|
|
2019-09-13 11:20:17 +02:00
|
|
|
@commands.group(name='say', invoke_without_command=True)
|
|
|
|
async def _say(self, ctx: commands.Context, *, content: str):
|
2019-09-12 15:43:57 +02:00
|
|
|
try:
|
|
|
|
await ctx.message.delete()
|
|
|
|
except discord.errors.Forbidden:
|
2019-09-13 11:20:17 +02:00
|
|
|
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):
|
2019-09-13 11:33:47 +02:00
|
|
|
await ctx.send(Texts('utils').get("Unable to find the message"),
|
|
|
|
delete_after=5)
|
2019-09-13 11:20:17 +02:00
|
|
|
|
|
|
|
@_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)
|
2019-09-12 15:43:57 +02:00
|
|
|
|
|
|
|
"""---------------------------------------------------------------------"""
|
|
|
|
|
2019-09-12 17:21:00 +02:00
|
|
|
@commands.command(name='ban')
|
2019-09-22 04:18:28 +02:00
|
|
|
async def _ban(self, ctx: commands.Context, user: discord.Member, *,
|
2019-09-12 15:43:57 +02:00
|
|
|
reason=""):
|
2019-09-13 11:20:17 +02:00
|
|
|
try:
|
|
|
|
member: discord.Member = await ctx.guild.fetch_member(user.id)
|
2019-09-12 15:43:57 +02:00
|
|
|
|
|
|
|
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:
|
2019-09-13 11:33:47 +02:00
|
|
|
await ctx.send(Texts('admin').get("Unable to ban this user"),
|
|
|
|
delete_after=5)
|
2019-09-13 11:20:17 +02:00
|
|
|
except discord.errors.NotFound:
|
2019-09-13 11:33:47 +02:00
|
|
|
await ctx.send(Texts('utils').get("Unable to find the user..."),
|
|
|
|
delete_after=5)
|
2019-09-12 15:43:57 +02:00
|
|
|
|
|
|
|
"""---------------------------------------------------------------------"""
|
|
|
|
|
2019-09-12 17:21:00 +02:00
|
|
|
@commands.command(name='kick')
|
2019-09-22 04:18:28 +02:00
|
|
|
async def _kick(self, ctx: commands.Context, user: discord.Member, *,
|
2019-09-12 15:43:57 +02:00
|
|
|
reason=""):
|
2019-09-13 11:20:17 +02:00
|
|
|
try:
|
|
|
|
member: discord.Member = await ctx.guild.fetch_member(user.id)
|
2019-09-12 15:43:57 +02:00
|
|
|
|
|
|
|
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:
|
2019-09-13 11:33:47 +02:00
|
|
|
await ctx.send(Texts('admin').get("Unable to kick this user"),
|
|
|
|
delete_after=5)
|
2019-09-13 11:20:17 +02:00
|
|
|
except discord.errors.NotFound:
|
2019-09-13 11:33:47 +02:00
|
|
|
await ctx.send(Texts('utils').get("Unable to find the user..."),
|
|
|
|
delete_after=5)
|
2019-09-12 15:43:57 +02:00
|
|
|
|
|
|
|
"""---------------------------------------------------------------------"""
|
2019-05-30 00:59:20 +02:00
|
|
|
|
2019-09-12 17:21:00 +02:00
|
|
|
@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(' ')
|
|
|
|
|
2019-09-13 11:20:17 +02:00
|
|
|
try:
|
|
|
|
message: discord.Message = await ctx.channel.fetch_message(
|
|
|
|
message_id)
|
|
|
|
|
|
|
|
for emoji in emojis:
|
|
|
|
await message.add_reaction(emoji)
|
|
|
|
except discord.errors.NotFound:
|
2019-09-13 11:33:47 +02:00
|
|
|
await ctx.send(Texts('utils').get("Unable to find the message"),
|
|
|
|
delete_after=5)
|
2019-09-12 17:21:00 +02:00
|
|
|
|
|
|
|
@_react.command(name='clear')
|
|
|
|
async def _react_remove(self, ctx: commands.Context, message_id: int):
|
2019-09-13 11:20:17 +02:00
|
|
|
try:
|
|
|
|
message: discord.Message = await ctx.channel.fetch_message(
|
|
|
|
message_id)
|
|
|
|
await message.clear_reactions()
|
|
|
|
except discord.errors.NotFound:
|
2019-09-13 11:33:47 +02:00
|
|
|
await ctx.send(Texts('utils').get("Unable to find the message"),
|
|
|
|
delete_after=5)
|
2019-09-12 17:21:00 +02:00
|
|
|
|
|
|
|
"""---------------------------------------------------------------------"""
|
|
|
|
|
2019-09-13 11:33:47 +02:00
|
|
|
@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)
|
|
|
|
|
2019-09-19 01:48:52 +02:00
|
|
|
"""---------------------------------------------------------------------"""
|
|
|
|
|
2019-09-22 04:18:28 +02:00
|
|
|
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
|
|
|
|
|
2019-09-19 01:48:52 +02:00
|
|
|
@commands.group(name='warn', aliases=['warns'])
|
|
|
|
async def _warn(self, ctx: commands.Context):
|
|
|
|
if ctx.invoked_subcommand is None:
|
2019-09-22 04:18:28 +02:00
|
|
|
warns_list, warns = await self.get_warn(ctx)
|
|
|
|
e = discord.Embed(
|
|
|
|
title=f"{len(warns)} {Texts('admin').get('last warns')}: ",
|
|
|
|
description=warns_list
|
|
|
|
)
|
2019-09-19 01:48:52 +02:00
|
|
|
|
2019-09-22 04:18:28 +02:00
|
|
|
await ctx.send(embed=e)
|
2019-09-19 01:48:52 +02:00
|
|
|
|
2019-09-22 04:18:28 +02:00
|
|
|
async def add_warn(self, ctx: commands.Context, member: discord.Member,
|
|
|
|
reason):
|
2019-09-19 01:48:52 +02:00
|
|
|
|
2019-09-22 04:18:28 +02:00
|
|
|
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)
|
2019-09-19 01:48:52 +02:00
|
|
|
|
|
|
|
@_warn.command(name='add', aliases=['new'])
|
|
|
|
async def _warn_new(self, ctx: commands.Context, member: discord.Member,
|
2019-09-22 04:18:28 +02:00
|
|
|
*, 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
|
2019-09-19 01:48:52 +02:00
|
|
|
"""
|
2019-09-22 04:18:28 +02:00
|
|
|
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
|
2019-09-19 01:48:52 +02:00
|
|
|
"""
|
|
|
|
|
2019-09-22 04:18:28 +02:00
|
|
|
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')}")
|
|
|
|
|
2017-06-11 20:04:03 +02:00
|
|
|
|
2019-09-11 15:59:37 +02:00
|
|
|
def setup(bot: TuxBot):
|
2019-05-30 00:59:20 +02:00
|
|
|
bot.add_cog(Admin(bot))
|