refactor(cogs): prepare the env for help command

This commit is contained in:
Romain J 2020-01-04 02:46:12 +01:00
parent c71c976111
commit fdf220cdfa
27 changed files with 452 additions and 261 deletions

7
.gitignore vendored
View file

@ -4,16 +4,15 @@ __pycache__/
.env .env
configs/config.cfg configs/config.cfg
configs/prefixes.cfg configs/prefixes.cfg
configs/clusters.cfg configs/fallbacks.cfg
.DS_Store .DS_Store
private.py private.py
#jetbrains #jetbrains
.idea/ .idea/
#other # other
logs/tuxbot.log *.logs
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/

36
bot.py
View file

@ -10,35 +10,35 @@ import discord
import git import git
from discord.ext import commands from discord.ext import commands
from cogs.utils.config import Config from utils import Config
from cogs.utils.database import Database from utils import Database
from cogs.utils.lang import Texts from utils import Texts
from cogs.utils.version import Version from utils import Version
description = """ description = """
Je suis TuxBot, le bot qui vit de l'OpenSource ! ;) Je suis TuxBot, le bot qui vit de l'OpenSource ! ;)
""" """
build = git.Repo(search_parent_directories=True).head.object.hexsha build = git.Repo(search_parent_directories=True).head.object.hexsha
version = (2, 1, 0)
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
l_extensions: List[str] = [ l_extensions: List[str] = [
'cogs.admin', 'cogs.Admin',
'cogs.basics', 'cogs.Help',
'cogs.fallback_manager', 'cogs.Logs',
'cogs.logs', 'cogs.Monitoring',
'cogs.poll', 'cogs.Polls',
'cogs.user', 'cogs.Useful',
'cogs.utility', 'cogs.User',
'jishaku', 'jishaku',
] ]
async def _prefix_callable(bot, message: discord.message) -> list: async def _prefix_callable(bot, message: discord.message) -> list:
extras = [bot.cluster.get('Name')] extras = [bot.cluster.get('Name') + '.']
if message.guild is not None: if message.guild is not None:
extras = []
if str(message.guild.id) in bot.prefixes: if str(message.guild.id) in bot.prefixes:
extras.extend( extras.extend(
bot.prefixes.get(str(message.guild.id), "prefixes").split( bot.prefixes.get(str(message.guild.id), "prefixes").split(
@ -70,10 +70,11 @@ class TuxBot(commands.AutoShardedBot):
self.config = Config('./configs/config.cfg') self.config = Config('./configs/config.cfg')
self.prefixes = Config('./configs/prefixes.cfg') self.prefixes = Config('./configs/prefixes.cfg')
self.blacklist = Config('./configs/blacklist.cfg') self.blacklist = Config('./configs/blacklist.cfg')
self.clusters = Config('./configs/clusters.cfg') self.fallbacks = Config('./configs/fallbacks.cfg')
self.cluster = self.clusters.find('True', key='This', first=True) self.cluster = self.fallbacks.find('True', key='This', first=True)
self.version = Version(10, 1, 0, pre_release='a5', build=build) self.version = Version(*version, pre_release='a5', build=build)
self.owner = int
for extension in l_extensions: for extension in l_extensions:
try: try:
@ -149,6 +150,9 @@ class TuxBot(commands.AutoShardedBot):
print('-' * 60) print('-' * 60)
await self.change_presence(**presence) await self.change_presence(**presence)
self.owner = await self.fetch_user(
int(self.config.get('permissions', 'Owners').split(', ')[0])
)
@staticmethod @staticmethod
async def on_resumed(): async def on_resumed():

View file

@ -1,17 +1,16 @@
import asyncio
import datetime import datetime
import logging import logging
from typing import Union from typing import Union
import asyncio
import discord import discord
import humanize import humanize
from discord.ext import commands from discord.ext import commands
from bot import TuxBot from bot import TuxBot
from .utils.lang import Texts from utils import Texts
from .utils.extra import commandExtra, groupExtra from utils import WarnModel, LangModel
from .utils.models import WarnModel, LangModel from utils import commandExtra, groupExtra
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -20,6 +19,8 @@ class Admin(commands.Cog):
def __init__(self, bot: TuxBot): def __init__(self, bot: TuxBot):
self.bot = bot self.bot = bot
self.icon = ":shield:"
self.big_icon = "https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/twitter/233/shield_1f6e1.png"
async def cog_check(self, ctx: commands.Context) -> bool: async def cog_check(self, ctx: commands.Context) -> bool:
permissions: discord.Permissions = ctx.channel.permissions_for( permissions: discord.Permissions = ctx.channel.permissions_for(

220
cogs/Help.py Normal file
View file

@ -0,0 +1,220 @@
# Created by romain at 04/01/2020
import logging
import discord
from discord.ext import commands
from bot import TuxBot
from utils import FieldPages
from utils import commandsPlus
log = logging.getLogger(__name__)
class HelpCommand(commands.HelpCommand):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.ignore_cogs = ["Monitoring", "Help", "Logs"]
self.owner_cogs = []
def get_command_signature(self, command):
return f"[{command.cog.qualified_name.upper()}] > {command.qualified_name}"
def common_command_formatting(self, emb, command):
emb.title = self.get_command_signature(command)
if command.cog_name != "Jishaku":
emb.set_thumbnail(url=command.cog.big_icon)
try:
emb.description = f"{command.cog.qualified_name.lower()}_help " \
f"{command.parent}_{command.name}_description"
except:
emb.description = f"{command.cog.qualified_name.lower()}_help " \
f"{command.name}_description"
usage = "help.command_help.usage"
try:
if command.parent:
try:
usg = f"{command.cog.qualified_name.lower()}_help " \
f"{command.parent}_{command.name}_usage"
except:
usg = f"{command.cog.qualified_name.lower()}_help " \
f"{command.name}_usage"
else:
usg = f"{command.cog.qualified_name.lower()}_help " \
f"{command.name}_usage"
emb.add_field(name=usage,
value=f"{self.context.prefix}{command.qualified_name} " + usg)
except KeyError:
emb.add_field(name=usage,
value=f"{self.context.prefix}{command.qualified_name}")
aliases = "`" + '`, `'.join(command.aliases) + "`"
if aliases == "``":
aliases = "help " \
"help.command_help.no_aliases"
emb.add_field(name="help "
"help.command_help.aliases",
value=aliases)
return emb
async def command_callback(self, ctx, *, command=None):
await self.prepare_help_command(ctx, command)
if command is None:
mapping = self.get_bot_mapping()
return await self.send_bot_help(mapping)
cog = ctx.bot.get_cog(command.title())
if cog is not None:
return await self.send_cog_help(cog)
maybe_coro = discord.utils.maybe_coroutine
keys = command.split(' ')
cmd = ctx.bot.all_commands.get(keys[0])
if cmd is None:
string = await maybe_coro(self.command_not_found,
self.remove_mentions(keys[0]))
return await self.send_error_message(string)
for key in keys[1:]:
try:
found = cmd.all_commands.get(key)
except AttributeError:
string = await maybe_coro(self.subcommand_not_found, cmd,
self.remove_mentions(key))
return await self.send_error_message(string)
else:
if found is None:
string = await maybe_coro(self.subcommand_not_found,
cmd,
self.remove_mentions(key))
return await self.send_error_message(string)
cmd = found
if isinstance(cmd, commands.Group):
return await self.send_group_help(cmd)
else:
return await self.send_command_help(cmd)
async def send_bot_help(self, mapping):
owner = self.context.bot.owner
emb = discord.Embed(color=discord.colour.Color.blue())
emb.description = "help " \
"help.main_page.description".format(owner)
emb.set_author(icon_url=self.context.author.avatar_url,
name=self.context.author)
cogs = ""
for extension in self.context.bot.cogs.values():
if self.context.author != owner and extension.qualified_name.upper() in self.owner_cogs:
continue
if self.context.author == owner and extension.qualified_name in self.ignore_cogs:
continue
if extension.qualified_name == "Jishaku":
continue
cogs += f"{extension.icon} **{extension.qualified_name}**\n"
emb.add_field(name="help "
"help.main_page.field_title.categories",
value=cogs)
await self.context.send(embed=emb)
async def send_command_help(self, command):
if command.cog_name in self.ignore_cogs:
return await self.send_error_message(
self.command_not_found(command.name))
if isinstance(command, commandsPlus):
if command.name == "jishaku":
pass
formatted = self.common_command_formatting(
discord.Embed(color=discord.colour.Color.blue()), command)
await self.context.send(embed=formatted)
async def send_group_help(self, group):
if group.cog_name in self.ignore_cogs:
return await self.send_error_message(
self.command_not_found(group.name))
formatted = self.common_command_formatting(
discord.Embed(color=discord.colour.Color.blue()), group)
sub_cmd_list = ""
for group_command in group.commands:
try:
sub_cmd_list += f"`╚╡` **{group_command.name}** - " \
f"{group.cog.qualified_name.lower()}_help " \
f"{group_command.parent}_{group_command.name}_brief\n"
except Exception:
sub_cmd_list += f"`╚╡` **{group_command.name}** - " \
f"{group.cog.qualified_name.lower()}_help" \
f"{group_command.name}_brief\n"
subcommands = "help.command_help.subcommands"
formatted.add_field(name=subcommands, value=sub_cmd_list,
inline=False)
await self.context.send(embed=formatted)
async def send_cog_help(self, cog):
if (
cog.qualified_name.upper() in self.owner_cogs
and not await self.context.bot.is_owner(self.context.author)
) or cog.qualified_name.upper() in self.ignore_cogs:
return
if cog.qualified_name == "Jishaku":
return
if cog.qualified_name in self.ignore_cogs:
return
pages = {}
for cmd in cog.get_commands():
if not await self.context.bot.is_owner(
self.context.author) and (
cmd.hidden or cmd.category == "Hidden"):
continue
if cmd.category not in pages:
pages[cmd.category] = "```asciidoc\n"
cmd_brief = f"{cog.qualified_name.lower()}_help " \
f"{cmd.name}_brief"
pages[
cmd.category] += f"{cmd.name}{' ' * int(17 - len(cmd.name))}:: {cmd_brief}\n"
if isinstance(cmd, commands.Group):
for group_command in cmd.commands:
try:
cmd_brief = f"{cog.qualified_name.lower()}_help " \
f"{group_command.parent}_{group_command.name}_brief"
except Exception:
cmd_brief = f"{cog.qualified_name.lower()}_help " \
f"{group_command.name}_brief"
pages[
cmd.category] += f"{group_command.name}{' ' * int(15 - len(group_command.name))}:: {cmd_brief}\n"
for e in pages:
pages[e] += "```"
formatted = []
for name, cont in pages.items():
formatted.append((name, cont))
footer_text = "help " \
"help.category_page.footer_info".format(self.context.prefix)
pages = FieldPages(self.context,
embed_color=discord.colour.Color.blue(),
entries=formatted,
title=cog.qualified_name.upper(),
thumbnail=cog.big_icon,
footertext=footer_text,
per_page=1)
await pages.paginate()
def command_not_found(self, string):
return 'No command called "{}" found.'.format(string)
class Help(commands.Cog):
def __init__(self, bot: TuxBot):
bot.help_command = HelpCommand()
def setup(bot: TuxBot):
bot.add_cog(Help(bot))

View file

@ -19,8 +19,8 @@ import psutil
from discord.ext import commands, tasks from discord.ext import commands, tasks
from bot import TuxBot from bot import TuxBot
from .utils import Texts from utils import Texts
from .utils.extra import commandExtra from utils import commandExtra
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

View file

@ -1,6 +1,6 @@
from datetime import datetime
import logging import logging
import urllib.request import urllib.request
from datetime import datetime
import discord import discord
from aiohttp import web from aiohttp import web
@ -30,11 +30,11 @@ class Monitoring(commands.Cog):
@tasks.loop(seconds=10.0) @tasks.loop(seconds=10.0)
async def ping_clusters(self): async def ping_clusters(self):
for cluster in self.bot.clusters: for cluster in self.bot.fallbacks:
if cluster == 'DEFAULT': if cluster == 'DEFAULT':
pass pass
else: else:
cluster = self.bot.clusters[cluster] cluster = self.bot.fallbacks[cluster]
if not cluster.get('This', False): if not cluster.get('This', False):
host = cluster.get('Host') host = cluster.get('Host')
port = cluster.get('Port') port = cluster.get('Port')

View file

@ -7,10 +7,9 @@ from discord.ext import commands
from yarl import URL from yarl import URL
from bot import TuxBot from bot import TuxBot
from .utils.lang import Texts from utils import PollModel, ResponsesModel
from .utils.models import PollModel, ResponsesModel from utils import Texts, emotes as utils_emotes
from .utils.extra import groupExtra from utils import groupExtra
from .utils import emotes as utils_emotes
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -19,6 +18,8 @@ class Polls(commands.Cog):
def __init__(self, bot: TuxBot): def __init__(self, bot: TuxBot):
self.bot = bot self.bot = bot
self.icon = ":bar_chart:"
self.big_icon = "https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/twitter/233/bar-chart_1f4ca.png:"
def get_poll(self, pld) -> Union[bool, PollModel]: def get_poll(self, pld) -> Union[bool, PollModel]:
if pld.user_id != self.bot.user.id: if pld.user_id != self.bot.user.id:
@ -76,7 +77,8 @@ class Polls(commands.Cog):
await self.update_poll(poll.id) await self.update_poll(poll.id)
@commands.Cog.listener() @commands.Cog.listener()
async def on_raw_reaction_remove(self, pld: discord.RawReactionActionEvent): async def on_raw_reaction_remove(self,
pld: discord.RawReactionActionEvent):
poll = self.get_poll(pld) poll = self.get_poll(pld)
if poll: if poll:
@ -156,8 +158,8 @@ class Polls(commands.Cog):
content = json.loads(poll.content) \ content = json.loads(poll.content) \
if isinstance(poll.content, str) \ if isinstance(poll.content, str) \
else poll.content else poll.content
raw_responses = self.bot.database.session\ raw_responses = self.bot.database.session \
.query(ResponsesModel)\ .query(ResponsesModel) \
.filter(ResponsesModel.poll_id == poll_id) .filter(ResponsesModel.poll_id == poll_id)
responses = {} responses = {}

View file

@ -1,26 +1,34 @@
# Created by romain at 04/01/2020
import logging import logging
import os import os
import pathlib import pathlib
import platform import platform
import re
import socket
import time import time
from socket import AF_INET6
import aiohttp
import discord import discord
import humanize import humanize
import psutil import psutil
from discord.ext import commands from discord.ext import commands
from tcp_latency import measure_latency
from bot import TuxBot from bot import TuxBot
from .utils.lang import Texts from utils import Texts
from .utils.extra import commandExtra from utils import commandExtra
from tcp_latency import measure_latency
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class Basics(commands.Cog): class Useful(commands.Cog):
def __init__(self, bot: TuxBot): def __init__(self, bot: TuxBot):
self.bot = bot self.bot = bot
self.icon = ":toolbox:"
self.big_icon = "https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/twitter/233/toolbox_1f9f0.png"
@staticmethod @staticmethod
def _latest_commits(): def _latest_commits():
@ -30,6 +38,142 @@ class Basics(commands.Cog):
########################################################################### ###########################################################################
@commandExtra(name='iplocalise', category='utility',
description=Texts('commands').get('utility._iplocalise'))
async def _iplocalise(self, ctx: commands.Context, addr, ip_type=''):
addr = re.sub(r'http(s?)://', '', addr)
addr = addr[:-1] if addr.endswith('/') else addr
await ctx.trigger_typing()
try:
if ip_type in ('v6', 'ipv6'):
try:
ip = socket.getaddrinfo(addr, None, AF_INET6)[1][4][0]
except socket.gaierror:
return await ctx.send(
Texts('utility', ctx).get('ipv6 not available'))
else:
ip = socket.gethostbyname(addr)
async with self.bot.session.get(f"http://ip-api.com/json/{ip}") \
as s:
response: dict = await s.json()
if response.get('status') == 'success':
e = discord.Embed(
title=f"{Texts('utility', ctx).get('Information for')}"
f" ``{addr}`` *`({response.get('query')})`*",
color=0x5858d7
)
e.add_field(
name=Texts('utility', ctx).get('Belongs to :'),
value=response.get('org', 'N/A'),
inline=False
)
e.add_field(
name=Texts('utility', ctx).get('Is located at :'),
value=response.get('city', 'N/A'),
inline=True
)
e.add_field(
name="Region :",
value=f"{response.get('regionName', 'N/A')} "
f"({response.get('country', 'N/A')})",
inline=True
)
e.set_thumbnail(
url=f"https://www.countryflags.io/"
f"{response.get('countryCode')}/flat/64.png")
await ctx.send(embed=e)
else:
await ctx.send(
content=f"{Texts('utility', ctx).get('info not available')}"
f"``{response.get('query')}``")
except Exception:
await ctx.send(
f"{Texts('utility', ctx).get('Cannot connect to host')} {addr}"
)
###########################################################################
@commandExtra(name='getheaders', category='utility',
description=Texts('commands').get('utility._getheaders'))
async def _getheaders(self, ctx: commands.Context, addr: str):
if (addr.startswith('http') or addr.startswith('ftp')) is not True:
addr = f"http://{addr}"
await ctx.trigger_typing()
try:
async with self.bot.session.get(addr) as s:
e = discord.Embed(
title=f"{Texts('utility', ctx).get('Headers of')} {addr}",
color=0xd75858
)
e.add_field(name="Status", value=s.status, inline=True)
e.set_thumbnail(url=f"https://http.cat/{s.status}")
headers = dict(s.headers.items())
headers.pop('Set-Cookie', headers)
for key, value in headers.items():
e.add_field(name=key, value=value, inline=True)
await ctx.send(embed=e)
except aiohttp.client_exceptions.ClientError:
await ctx.send(
f"{Texts('utility', ctx).get('Cannot connect to host')} {addr}"
)
###########################################################################
@commandExtra(name='git', aliases=['sources', 'source', 'github'],
category='utility',
description=Texts('commands').get('utility._git'))
async def _git(self, ctx):
e = discord.Embed(
title=Texts('utility', ctx).get('git repo'),
description=Texts('utility', ctx).get('git text'),
colour=0xE9D460
)
e.set_author(
name='Gnous',
icon_url="https://cdn.gnous.eu/logo1.png"
)
await ctx.send(embed=e)
###########################################################################
@commandExtra(name='quote', category='utility',
description=Texts('commands').get('utility._quote'))
async def _quote(self, ctx, message_id: discord.Message):
e = discord.Embed(
colour=message_id.author.colour,
description=message_id.clean_content,
timestamp=message_id.created_at
)
e.set_author(
name=message_id.author.display_name,
icon_url=message_id.author.avatar_url_as(format="jpg")
)
if len(message_id.attachments) >= 1:
e.set_image(url=message_id.attachments[0].url)
e.add_field(name="**Original**",
value=f"[Go!]({message_id.jump_url})")
e.set_footer(text="#" + message_id.channel.name)
await ctx.send(embed=e)
###########################################################################
@commandExtra(name='ping', category='basics', @commandExtra(name='ping', category='basics',
description=Texts('commands').get('basics._ping')) description=Texts('commands').get('basics._ping'))
async def _ping(self, ctx: commands.Context): async def _ping(self, ctx: commands.Context):
@ -178,4 +322,4 @@ class Basics(commands.Cog):
def setup(bot: TuxBot): def setup(bot: TuxBot):
bot.add_cog(Basics(bot)) bot.add_cog(Useful(bot))

View file

@ -3,9 +3,9 @@ import logging
from discord.ext import commands from discord.ext import commands
from bot import TuxBot from bot import TuxBot
from .utils.extra import groupExtra from utils import AliasesModel
from .utils.lang import Texts from utils import Texts
from .utils.models import AliasesModel from utils import groupExtra
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -14,6 +14,8 @@ class User(commands.Cog):
def __init__(self, bot: TuxBot): def __init__(self, bot: TuxBot):
self.bot = bot self.bot = bot
self.icon = ":bust_in_silhouette:"
self.big_icon = "https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/120/twitter/233/bust-in-silhouette_1f464.png"
########################################################################### ###########################################################################

View file

@ -1,160 +0,0 @@
import logging
import re
import aiohttp
import discord
from discord.ext import commands
from bot import TuxBot
import socket
from socket import AF_INET6
from .utils.lang import Texts
from .utils.extra import commandExtra
log = logging.getLogger(__name__)
class Utility(commands.Cog):
def __init__(self, bot: TuxBot):
self.bot = bot
###########################################################################
@commandExtra(name='iplocalise', category='utility',
description=Texts('commands').get('utility._iplocalise'))
async def _iplocalise(self, ctx: commands.Context, addr, ip_type=''):
addr = re.sub(r'http(s?)://', '', addr)
addr = addr[:-1] if addr.endswith('/') else addr
await ctx.trigger_typing()
try:
if ip_type in ('v6', 'ipv6'):
try:
ip = socket.getaddrinfo(addr, None, AF_INET6)[1][4][0]
except socket.gaierror:
return await ctx.send(
Texts('utility', ctx).get('ipv6 not available'))
else:
ip = socket.gethostbyname(addr)
async with self.bot.session.get(f"http://ip-api.com/json/{ip}") \
as s:
response: dict = await s.json()
if response.get('status') == 'success':
e = discord.Embed(
title=f"{Texts('utility', ctx).get('Information for')}"
f" ``{addr}`` *`({response.get('query')})`*",
color=0x5858d7
)
e.add_field(
name=Texts('utility', ctx).get('Belongs to :'),
value=response.get('org', 'N/A'),
inline=False
)
e.add_field(
name=Texts('utility', ctx).get('Is located at :'),
value=response.get('city', 'N/A'),
inline=True
)
e.add_field(
name="Region :",
value=f"{response.get('regionName', 'N/A')} "
f"({response.get('country', 'N/A')})",
inline=True
)
e.set_thumbnail(
url=f"https://www.countryflags.io/"
f"{response.get('countryCode')}/flat/64.png")
await ctx.send(embed=e)
else:
await ctx.send(
content=f"{Texts('utility', ctx).get('info not available')}"
f"``{response.get('query')}``")
except Exception:
await ctx.send(
f"{Texts('utility', ctx).get('Cannot connect to host')} {addr}"
)
###########################################################################
@commandExtra(name='getheaders', category='utility',
description=Texts('commands').get('utility._getheaders'))
async def _getheaders(self, ctx: commands.Context, addr: str):
if (addr.startswith('http') or addr.startswith('ftp')) is not True:
addr = f"http://{addr}"
await ctx.trigger_typing()
try:
async with self.bot.session.get(addr) as s:
e = discord.Embed(
title=f"{Texts('utility', ctx).get('Headers of')} {addr}",
color=0xd75858
)
e.add_field(name="Status", value=s.status, inline=True)
e.set_thumbnail(url=f"https://http.cat/{s.status}")
headers = dict(s.headers.items())
headers.pop('Set-Cookie', headers)
for key, value in headers.items():
e.add_field(name=key, value=value, inline=True)
await ctx.send(embed=e)
except aiohttp.client_exceptions.ClientError:
await ctx.send(
f"{Texts('utility', ctx).get('Cannot connect to host')} {addr}"
)
###########################################################################
@commandExtra(name='git', aliases=['sources', 'source', 'github'],
category='utility',
description=Texts('commands').get('utility._git'))
async def _git(self, ctx):
e = discord.Embed(
title=Texts('utility', ctx).get('git repo'),
description=Texts('utility', ctx).get('git text'),
colour=0xE9D460
)
e.set_author(
name='Gnous',
icon_url="https://cdn.gnous.eu/logo1.png"
)
await ctx.send(embed=e)
###########################################################################
@commandExtra(name='quote', category='utility',
description=Texts('commands').get('utility._quote'))
async def _quote(self, ctx, message_id: discord.Message):
e = discord.Embed(
colour=message_id.author.colour,
description=message_id.clean_content,
timestamp=message_id.created_at
)
e.set_author(
name=message_id.author.display_name,
icon_url=message_id.author.avatar_url_as(format="jpg")
)
if len(message_id.attachments) >= 1:
e.set_image(url=message_id.attachments[0].url)
e.add_field(name="**Original**",
value=f"[Go!]({message_id.jump_url})")
e.set_footer(text="#" + message_id.channel.name)
await ctx.send(embed=e)
def setup(bot: TuxBot):
bot.add_cog(Utility(bot))

View file

@ -1,3 +0,0 @@
from .config import *
from .lang import *
from .version import *

View file

@ -1,5 +1,5 @@
[280805240977227776] [280805240977227776]
prefixes = rm-srv01. prefixes = b1.
[303633056944881686] [303633056944881686]
prefixes = b1. prefixes = b1.
@ -14,4 +14,5 @@ prefixes = ba.
prefixes = test. prefixes = test.
[528679953399676938] [528679953399676938]
prefixes = test. prefixes = test.

View file

@ -1,6 +1,5 @@
from cogs.utils.models import * from utils import Config
from cogs.utils.config import Config from utils import Database
from cogs.utils.database import Database
database = Database(Config("./configs/config.cfg")) database = Database(Config("./configs/config.cfg"))

9
utils/__init__.py Executable file
View file

@ -0,0 +1,9 @@
from .database import Database
from .models import *
from .config import *
from .lang import *
from .version import *
from .extra import *
from .paginator import *

View file

@ -1,6 +1,6 @@
import gettext import gettext
from .config import Config from .config import Config
from cogs.utils.database import Database from utils import Database
from .models.lang import LangModel from .models.lang import LangModel
from discord.ext import commands from discord.ext import commands

View file

@ -2,20 +2,15 @@ import asyncio
import discord import discord
from discord.ext.commands import Paginator as CommandPaginator from discord.ext.commands import Paginator as CommandPaginator
class CannotPaginate(Exception): class CannotPaginate(Exception):
pass pass
class Pages: class Pages:
"""Implements a paginator that queries the user for the """Implements a paginator that queries the user for the
pagination interface. pagination interface.
Pages are 1-index based, not 0-index based. Pages are 1-index based, not 0-index based.
If the user does not reply within 2 minutes then the pagination If the user does not reply within 2 minutes then the pagination
interface exits automatically. interface exits automatically.
Parameters Parameters
------------ ------------
ctx: Context ctx: Context
@ -26,7 +21,6 @@ class Pages:
How many entries show up per page. How many entries show up per page.
show_entry_count: bool show_entry_count: bool
Whether to show an entry count in the footer. Whether to show an entry count in the footer.
Attributes Attributes
----------- -----------
embed: discord.Embed embed: discord.Embed
@ -36,7 +30,6 @@ class Pages:
permissions: discord.Permissions permissions: discord.Permissions
Our permissions for the channel. Our permissions for the channel.
""" """
def __init__(self, ctx, *, entries, per_page=12, show_entry_count=True): def __init__(self, ctx, *, entries, per_page=12, show_entry_count=True):
self.bot = ctx.bot self.bot = ctx.bot
self.entries = entries self.entries = entries
@ -52,13 +45,11 @@ class Pages:
self.paginating = len(entries) > per_page self.paginating = len(entries) > per_page
self.show_entry_count = show_entry_count self.show_entry_count = show_entry_count
self.reaction_emojis = [ self.reaction_emojis = [
('\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', ('\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', self.first_page),
self.first_page),
('\N{BLACK LEFT-POINTING TRIANGLE}', self.previous_page), ('\N{BLACK LEFT-POINTING TRIANGLE}', self.previous_page),
('\N{BLACK RIGHT-POINTING TRIANGLE}', self.next_page), ('\N{BLACK RIGHT-POINTING TRIANGLE}', self.next_page),
('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', ('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', self.last_page),
self.last_page), ('\N{INPUT SYMBOL FOR NUMBERS}', self.numbered_page ),
('\N{INPUT SYMBOL FOR NUMBERS}', self.numbered_page),
('\N{BLACK SQUARE FOR STOP}', self.stop_pages), ('\N{BLACK SQUARE FOR STOP}', self.stop_pages),
('\N{INFORMATION SOURCE}', self.show_help), ('\N{INFORMATION SOURCE}', self.show_help),
] ]
@ -77,12 +68,10 @@ class Pages:
if self.paginating: if self.paginating:
# verify we can actually use the pagination session # verify we can actually use the pagination session
if not self.permissions.add_reactions: if not self.permissions.add_reactions:
raise CannotPaginate( raise CannotPaginate('Bot does not have add reactions permission.')
'Bot does not have add reactions permission.')
if not self.permissions.read_message_history: if not self.permissions.read_message_history:
raise CannotPaginate( raise CannotPaginate('Bot does not have Read Message History permission.')
'Bot does not have Read Message History permission.')
def get_page(self, page): def get_page(self, page):
base = (page - 1) * self.per_page base = (page - 1) * self.per_page
@ -97,8 +86,7 @@ class Pages:
def prepare_embed(self, entries, page, *, first=False): def prepare_embed(self, entries, page, *, first=False):
p = [] p = []
for index, entry in enumerate(entries, for index, entry in enumerate(entries, 1 + ((page - 1) * self.per_page)):
1 + ((page - 1) * self.per_page)):
p.append(f'{index}. {entry}') p.append(f'{index}. {entry}')
if self.maximum_pages > 1: if self.maximum_pages > 1:
@ -111,8 +99,7 @@ class Pages:
if self.paginating and first: if self.paginating and first:
p.append('') p.append('')
p.append( p.append('Confused? React with \N{INFORMATION SOURCE} for more info.')
'Confused? React with \N{INFORMATION SOURCE} for more info.')
self.embed.description = '\n'.join(p) self.embed.description = '\n'.join(p)
@ -166,8 +153,7 @@ class Pages:
async def numbered_page(self): async def numbered_page(self):
"""lets you type a page number to go to""" """lets you type a page number to go to"""
to_delete = [] to_delete = []
to_delete.append( to_delete.append(await self.channel.send('What page do you want to go to?'))
await self.channel.send('What page do you want to go to?'))
def message_check(m): def message_check(m):
return m.author == self.author and \ return m.author == self.author and \
@ -175,8 +161,7 @@ class Pages:
m.content.isdigit() m.content.isdigit()
try: try:
msg = await self.bot.wait_for('message', check=message_check, msg = await self.bot.wait_for('message', check=message_check, timeout=30.0)
timeout=30.0)
except asyncio.TimeoutError: except asyncio.TimeoutError:
to_delete.append(await self.channel.send('Took too long.')) to_delete.append(await self.channel.send('Took too long.'))
await asyncio.sleep(5) await asyncio.sleep(5)
@ -186,8 +171,7 @@ class Pages:
if page != 0 and page <= self.maximum_pages: if page != 0 and page <= self.maximum_pages:
await self.show_page(page) await self.show_page(page)
else: else:
to_delete.append(await self.channel.send( to_delete.append(await self.channel.send(f'Invalid page given. ({page}/{self.maximum_pages})'))
f'Invalid page given. ({page}/{self.maximum_pages})'))
await asyncio.sleep(5) await asyncio.sleep(5)
try: try:
@ -198,9 +182,8 @@ class Pages:
async def show_help(self): async def show_help(self):
"""shows this message""" """shows this message"""
messages = ['Welcome to the interactive paginator!\n'] messages = ['Welcome to the interactive paginator!\n']
messages.append( messages.append('This interactively allows you to see pages of text by navigating with ' \
'This interactively allows you to see pages of text by navigating with ' \ 'reactions. They are as follows:\n')
'reactions. They are as follows:\n')
for (emoji, func) in self.reaction_emojis: for (emoji, func) in self.reaction_emojis:
messages.append(f'{emoji} {func.__doc__}') messages.append(f'{emoji} {func.__doc__}')
@ -208,8 +191,7 @@ class Pages:
embed = self.embed.copy() embed = self.embed.copy()
embed.clear_fields() embed.clear_fields()
embed.description = '\n'.join(messages) embed.description = '\n'.join(messages)
embed.set_footer( embed.set_footer(text=f'We were on page {self.current_page} before this message.')
text=f'We were on page {self.current_page} before this message.')
await self.message.edit(content=None, embed=embed) await self.message.edit(content=None, embed=embed)
async def go_back_to_current_page(): async def go_back_to_current_page():
@ -248,9 +230,7 @@ class Pages:
while self.paginating: while self.paginating:
try: try:
payload = await self.bot.wait_for('raw_reaction_add', payload = await self.bot.wait_for('raw_reaction_add', check=self.react_check, timeout=120.0)
check=self.react_check,
timeout=120.0)
except asyncio.TimeoutError: except asyncio.TimeoutError:
self.paginating = False self.paginating = False
try: try:
@ -261,15 +241,12 @@ class Pages:
break break
try: try:
await self.message.remove_reaction(payload.emoji, await self.message.remove_reaction(payload.emoji, discord.Object(id=payload.user_id))
discord.Object(
id=payload.user_id))
except: except:
pass # can't remove it so don't bother doing so pass # can't remove it so don't bother doing so
await self.match() await self.match()
class FieldPages(Pages): class FieldPages(Pages):
"""Similar to Pages except entries should be a list of """Similar to Pages except entries should be a list of
tuples having (key, value) to show as embed fields instead. tuples having (key, value) to show as embed fields instead.
@ -290,19 +267,15 @@ class FieldPages(Pages):
self.embed.set_footer(text=text) self.embed.set_footer(text=text)
class TextPages(Pages): class TextPages(Pages):
"""Uses a commands.Paginator internally to paginate some text.""" """Uses a commands.Paginator internally to paginate some text."""
def __init__(self, ctx, text, *, prefix='```', suffix='```', def __init__(self, ctx, text, *, prefix='```', suffix='```', max_size=2000):
max_size=2000): paginator = CommandPaginator(prefix=prefix, suffix=suffix, max_size=max_size - 200)
paginator = CommandPaginator(prefix=prefix, suffix=suffix,
max_size=max_size - 200)
for line in text.split('\n'): for line in text.split('\n'):
paginator.add_line(line) paginator.add_line(line)
super().__init__(ctx, entries=paginator.pages, per_page=1, super().__init__(ctx, entries=paginator.pages, per_page=1, show_entry_count=False)
show_entry_count=False)
def get_page(self, page): def get_page(self, page):
return self.entries[page - 1] return self.entries[page - 1]
@ -313,4 +286,4 @@ class TextPages(Pages):
def get_content(self, entry, page, *, first=False): def get_content(self, entry, page, *, first=False):
if self.maximum_pages > 1: if self.maximum_pages > 1:
return f'{entry}\nPage {page}/{self.maximum_pages}' return f'{entry}\nPage {page}/{self.maximum_pages}'
return entry return entry