From fdf220cdfa6341433e66f9a27ea7cc39377ad93e Mon Sep 17 00:00:00 2001 From: Romain J Date: Sat, 4 Jan 2020 02:46:12 +0100 Subject: [PATCH] refactor(cogs): prepare the env for help command --- .gitignore | 7 +- bot.py | 36 +-- cogs/{admin.py => Admin.py} | 11 +- cogs/Help.py | 220 ++++++++++++++++++ cogs/{logs.py => Logs.py} | 4 +- cogs/{fallback_manager.py => Monitoring.py} | 6 +- cogs/{poll.py => Polls.py} | 16 +- cogs/{basics.py => Useful.py} | 154 +++++++++++- cogs/{user.py => User.py} | 8 +- cogs/utility.py | 160 ------------- cogs/utils/__init__.py | 3 - ...ters.cfg.example => fallbacks.cfg.example} | 0 configs/prefixes.cfg | 5 +- migrate.py | 5 +- utils/__init__.py | 9 + {cogs/utils => utils}/config.py | 0 {cogs/utils => utils}/database.py | 0 {cogs/utils => utils}/emotes.py | 0 {cogs/utils => utils}/extra.py | 0 {cogs/utils => utils}/lang.py | 2 +- {cogs/utils => utils}/models/__init__.py | 0 {cogs/utils => utils}/models/alias.py | 0 {cogs/utils => utils}/models/lang.py | 0 {cogs/utils => utils}/models/poll.py | 0 {cogs/utils => utils}/models/warn.py | 0 {cogs/utils => utils}/paginator.py | 67 ++---- {cogs/utils => utils}/version.py | 0 27 files changed, 452 insertions(+), 261 deletions(-) rename cogs/{admin.py => Admin.py} (98%) create mode 100644 cogs/Help.py rename cogs/{logs.py => Logs.py} (99%) rename cogs/{fallback_manager.py => Monitoring.py} (97%) rename cogs/{poll.py => Polls.py} (93%) rename cogs/{basics.py => Useful.py} (52%) rename cogs/{user.py => User.py} (88%) delete mode 100644 cogs/utility.py delete mode 100755 cogs/utils/__init__.py rename configs/{clusters.cfg.example => fallbacks.cfg.example} (100%) create mode 100755 utils/__init__.py rename {cogs/utils => utils}/config.py (100%) rename {cogs/utils => utils}/database.py (100%) rename {cogs/utils => utils}/emotes.py (100%) rename {cogs/utils => utils}/extra.py (100%) rename {cogs/utils => utils}/lang.py (96%) rename {cogs/utils => utils}/models/__init__.py (100%) rename {cogs/utils => utils}/models/alias.py (100%) rename {cogs/utils => utils}/models/lang.py (100%) rename {cogs/utils => utils}/models/poll.py (100%) rename {cogs/utils => utils}/models/warn.py (100%) rename {cogs/utils => utils}/paginator.py (84%) rename {cogs/utils => utils}/version.py (100%) diff --git a/.gitignore b/.gitignore index f916017..a2f5f70 100644 --- a/.gitignore +++ b/.gitignore @@ -4,16 +4,15 @@ __pycache__/ .env configs/config.cfg configs/prefixes.cfg -configs/clusters.cfg +configs/fallbacks.cfg .DS_Store private.py #jetbrains .idea/ -#other -logs/tuxbot.log - +# other +*.logs # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/bot.py b/bot.py index c88398f..10e78f9 100755 --- a/bot.py +++ b/bot.py @@ -10,35 +10,35 @@ import discord import git from discord.ext import commands -from cogs.utils.config import Config -from cogs.utils.database import Database -from cogs.utils.lang import Texts -from cogs.utils.version import Version +from utils import Config +from utils import Database +from utils import Texts +from utils import Version description = """ Je suis TuxBot, le bot qui vit de l'OpenSource ! ;) """ build = git.Repo(search_parent_directories=True).head.object.hexsha +version = (2, 1, 0) log = logging.getLogger(__name__) l_extensions: List[str] = [ - 'cogs.admin', - 'cogs.basics', - 'cogs.fallback_manager', - 'cogs.logs', - 'cogs.poll', - 'cogs.user', - 'cogs.utility', + 'cogs.Admin', + 'cogs.Help', + 'cogs.Logs', + 'cogs.Monitoring', + 'cogs.Polls', + 'cogs.Useful', + 'cogs.User', 'jishaku', ] 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: - extras = [] if str(message.guild.id) in bot.prefixes: extras.extend( bot.prefixes.get(str(message.guild.id), "prefixes").split( @@ -70,10 +70,11 @@ class TuxBot(commands.AutoShardedBot): self.config = Config('./configs/config.cfg') self.prefixes = Config('./configs/prefixes.cfg') self.blacklist = Config('./configs/blacklist.cfg') - self.clusters = Config('./configs/clusters.cfg') - self.cluster = self.clusters.find('True', key='This', first=True) + self.fallbacks = Config('./configs/fallbacks.cfg') + 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: try: @@ -149,6 +150,9 @@ class TuxBot(commands.AutoShardedBot): print('-' * 60) await self.change_presence(**presence) + self.owner = await self.fetch_user( + int(self.config.get('permissions', 'Owners').split(', ')[0]) + ) @staticmethod async def on_resumed(): diff --git a/cogs/admin.py b/cogs/Admin.py similarity index 98% rename from cogs/admin.py rename to cogs/Admin.py index 3ffcaab..9be576b 100644 --- a/cogs/admin.py +++ b/cogs/Admin.py @@ -1,17 +1,16 @@ +import asyncio 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 -from .utils.extra import commandExtra, groupExtra -from .utils.models import WarnModel, LangModel +from utils import Texts +from utils import WarnModel, LangModel +from utils import commandExtra, groupExtra log = logging.getLogger(__name__) @@ -20,6 +19,8 @@ class Admin(commands.Cog): def __init__(self, bot: TuxBot): 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: permissions: discord.Permissions = ctx.channel.permissions_for( diff --git a/cogs/Help.py b/cogs/Help.py new file mode 100644 index 0000000..1fd2794 --- /dev/null +++ b/cogs/Help.py @@ -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)) diff --git a/cogs/logs.py b/cogs/Logs.py similarity index 99% rename from cogs/logs.py rename to cogs/Logs.py index 27b45fc..b8f50a9 100644 --- a/cogs/logs.py +++ b/cogs/Logs.py @@ -19,8 +19,8 @@ import psutil from discord.ext import commands, tasks from bot import TuxBot -from .utils import Texts -from .utils.extra import commandExtra +from utils import Texts +from utils import commandExtra log = logging.getLogger(__name__) diff --git a/cogs/fallback_manager.py b/cogs/Monitoring.py similarity index 97% rename from cogs/fallback_manager.py rename to cogs/Monitoring.py index 8e96438..5827299 100644 --- a/cogs/fallback_manager.py +++ b/cogs/Monitoring.py @@ -1,6 +1,6 @@ -from datetime import datetime import logging import urllib.request +from datetime import datetime import discord from aiohttp import web @@ -30,11 +30,11 @@ class Monitoring(commands.Cog): @tasks.loop(seconds=10.0) async def ping_clusters(self): - for cluster in self.bot.clusters: + for cluster in self.bot.fallbacks: if cluster == 'DEFAULT': pass else: - cluster = self.bot.clusters[cluster] + cluster = self.bot.fallbacks[cluster] if not cluster.get('This', False): host = cluster.get('Host') port = cluster.get('Port') diff --git a/cogs/poll.py b/cogs/Polls.py similarity index 93% rename from cogs/poll.py rename to cogs/Polls.py index 8132ed5..b02e36e 100644 --- a/cogs/poll.py +++ b/cogs/Polls.py @@ -7,10 +7,9 @@ from discord.ext import commands from yarl import URL from bot import TuxBot -from .utils.lang import Texts -from .utils.models import PollModel, ResponsesModel -from .utils.extra import groupExtra -from .utils import emotes as utils_emotes +from utils import PollModel, ResponsesModel +from utils import Texts, emotes as utils_emotes +from utils import groupExtra log = logging.getLogger(__name__) @@ -19,6 +18,8 @@ class Polls(commands.Cog): def __init__(self, bot: TuxBot): 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]: if pld.user_id != self.bot.user.id: @@ -76,7 +77,8 @@ class Polls(commands.Cog): await self.update_poll(poll.id) @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) if poll: @@ -156,8 +158,8 @@ class Polls(commands.Cog): content = json.loads(poll.content) \ if isinstance(poll.content, str) \ else poll.content - raw_responses = self.bot.database.session\ - .query(ResponsesModel)\ + raw_responses = self.bot.database.session \ + .query(ResponsesModel) \ .filter(ResponsesModel.poll_id == poll_id) responses = {} diff --git a/cogs/basics.py b/cogs/Useful.py similarity index 52% rename from cogs/basics.py rename to cogs/Useful.py index ffd1556..f11433c 100644 --- a/cogs/basics.py +++ b/cogs/Useful.py @@ -1,26 +1,34 @@ +# Created by romain at 04/01/2020 + import logging import os import pathlib import platform +import re +import socket import time +from socket import AF_INET6 +import aiohttp import discord import humanize import psutil from discord.ext import commands +from tcp_latency import measure_latency from bot import TuxBot -from .utils.lang import Texts -from .utils.extra import commandExtra -from tcp_latency import measure_latency +from utils import Texts +from utils import commandExtra log = logging.getLogger(__name__) -class Basics(commands.Cog): +class Useful(commands.Cog): def __init__(self, bot: TuxBot): 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 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', description=Texts('commands').get('basics._ping')) async def _ping(self, ctx: commands.Context): @@ -178,4 +322,4 @@ class Basics(commands.Cog): def setup(bot: TuxBot): - bot.add_cog(Basics(bot)) + bot.add_cog(Useful(bot)) diff --git a/cogs/user.py b/cogs/User.py similarity index 88% rename from cogs/user.py rename to cogs/User.py index 02f7036..215052b 100644 --- a/cogs/user.py +++ b/cogs/User.py @@ -3,9 +3,9 @@ import logging from discord.ext import commands from bot import TuxBot -from .utils.extra import groupExtra -from .utils.lang import Texts -from .utils.models import AliasesModel +from utils import AliasesModel +from utils import Texts +from utils import groupExtra log = logging.getLogger(__name__) @@ -14,6 +14,8 @@ class User(commands.Cog): def __init__(self, bot: TuxBot): 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" ########################################################################### diff --git a/cogs/utility.py b/cogs/utility.py deleted file mode 100644 index 46e1649..0000000 --- a/cogs/utility.py +++ /dev/null @@ -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)) diff --git a/cogs/utils/__init__.py b/cogs/utils/__init__.py deleted file mode 100755 index 85cb31b..0000000 --- a/cogs/utils/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .config import * -from .lang import * -from .version import * diff --git a/configs/clusters.cfg.example b/configs/fallbacks.cfg.example similarity index 100% rename from configs/clusters.cfg.example rename to configs/fallbacks.cfg.example diff --git a/configs/prefixes.cfg b/configs/prefixes.cfg index fa8ccdc..1924607 100644 --- a/configs/prefixes.cfg +++ b/configs/prefixes.cfg @@ -1,5 +1,5 @@ [280805240977227776] -prefixes = rm-srv01. +prefixes = b1. [303633056944881686] prefixes = b1. @@ -14,4 +14,5 @@ prefixes = ba. prefixes = test. [528679953399676938] -prefixes = test. \ No newline at end of file +prefixes = test. + diff --git a/migrate.py b/migrate.py index 31c2ef6..cec7eba 100644 --- a/migrate.py +++ b/migrate.py @@ -1,6 +1,5 @@ -from cogs.utils.models import * -from cogs.utils.config import Config -from cogs.utils.database import Database +from utils import Config +from utils import Database database = Database(Config("./configs/config.cfg")) diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100755 index 0000000..9335559 --- /dev/null +++ b/utils/__init__.py @@ -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 * diff --git a/cogs/utils/config.py b/utils/config.py similarity index 100% rename from cogs/utils/config.py rename to utils/config.py diff --git a/cogs/utils/database.py b/utils/database.py similarity index 100% rename from cogs/utils/database.py rename to utils/database.py diff --git a/cogs/utils/emotes.py b/utils/emotes.py similarity index 100% rename from cogs/utils/emotes.py rename to utils/emotes.py diff --git a/cogs/utils/extra.py b/utils/extra.py similarity index 100% rename from cogs/utils/extra.py rename to utils/extra.py diff --git a/cogs/utils/lang.py b/utils/lang.py similarity index 96% rename from cogs/utils/lang.py rename to utils/lang.py index 73bb86b..1a21062 100644 --- a/cogs/utils/lang.py +++ b/utils/lang.py @@ -1,6 +1,6 @@ import gettext from .config import Config -from cogs.utils.database import Database +from utils import Database from .models.lang import LangModel from discord.ext import commands diff --git a/cogs/utils/models/__init__.py b/utils/models/__init__.py similarity index 100% rename from cogs/utils/models/__init__.py rename to utils/models/__init__.py diff --git a/cogs/utils/models/alias.py b/utils/models/alias.py similarity index 100% rename from cogs/utils/models/alias.py rename to utils/models/alias.py diff --git a/cogs/utils/models/lang.py b/utils/models/lang.py similarity index 100% rename from cogs/utils/models/lang.py rename to utils/models/lang.py diff --git a/cogs/utils/models/poll.py b/utils/models/poll.py similarity index 100% rename from cogs/utils/models/poll.py rename to utils/models/poll.py diff --git a/cogs/utils/models/warn.py b/utils/models/warn.py similarity index 100% rename from cogs/utils/models/warn.py rename to utils/models/warn.py diff --git a/cogs/utils/paginator.py b/utils/paginator.py similarity index 84% rename from cogs/utils/paginator.py rename to utils/paginator.py index 07c92de..a1d065c 100644 --- a/cogs/utils/paginator.py +++ b/utils/paginator.py @@ -2,20 +2,15 @@ import asyncio import discord from discord.ext.commands import Paginator as CommandPaginator - class CannotPaginate(Exception): pass - class Pages: """Implements a paginator that queries the user for the pagination interface. - Pages are 1-index based, not 0-index based. - If the user does not reply within 2 minutes then the pagination interface exits automatically. - Parameters ------------ ctx: Context @@ -26,7 +21,6 @@ class Pages: How many entries show up per page. show_entry_count: bool Whether to show an entry count in the footer. - Attributes ----------- embed: discord.Embed @@ -36,7 +30,6 @@ class Pages: permissions: discord.Permissions Our permissions for the channel. """ - def __init__(self, ctx, *, entries, per_page=12, show_entry_count=True): self.bot = ctx.bot self.entries = entries @@ -52,13 +45,11 @@ class Pages: self.paginating = len(entries) > per_page self.show_entry_count = show_entry_count self.reaction_emojis = [ - ('\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', - self.first_page), + ('\N{BLACK LEFT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', self.first_page), ('\N{BLACK LEFT-POINTING TRIANGLE}', self.previous_page), ('\N{BLACK RIGHT-POINTING TRIANGLE}', self.next_page), - ('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', - self.last_page), - ('\N{INPUT SYMBOL FOR NUMBERS}', self.numbered_page), + ('\N{BLACK RIGHT-POINTING DOUBLE TRIANGLE WITH VERTICAL BAR}', self.last_page), + ('\N{INPUT SYMBOL FOR NUMBERS}', self.numbered_page ), ('\N{BLACK SQUARE FOR STOP}', self.stop_pages), ('\N{INFORMATION SOURCE}', self.show_help), ] @@ -77,12 +68,10 @@ class Pages: if self.paginating: # verify we can actually use the pagination session if not self.permissions.add_reactions: - raise CannotPaginate( - 'Bot does not have add reactions permission.') + raise CannotPaginate('Bot does not have add reactions permission.') if not self.permissions.read_message_history: - raise CannotPaginate( - 'Bot does not have Read Message History permission.') + raise CannotPaginate('Bot does not have Read Message History permission.') def get_page(self, page): base = (page - 1) * self.per_page @@ -97,8 +86,7 @@ class Pages: def prepare_embed(self, entries, page, *, first=False): p = [] - for index, entry in enumerate(entries, - 1 + ((page - 1) * self.per_page)): + for index, entry in enumerate(entries, 1 + ((page - 1) * self.per_page)): p.append(f'{index}. {entry}') if self.maximum_pages > 1: @@ -111,8 +99,7 @@ class Pages: if self.paginating and first: p.append('') - p.append( - 'Confused? React with \N{INFORMATION SOURCE} for more info.') + p.append('Confused? React with \N{INFORMATION SOURCE} for more info.') self.embed.description = '\n'.join(p) @@ -166,8 +153,7 @@ class Pages: async def numbered_page(self): """lets you type a page number to go to""" to_delete = [] - to_delete.append( - await self.channel.send('What page do you want to go to?')) + to_delete.append(await self.channel.send('What page do you want to go to?')) def message_check(m): return m.author == self.author and \ @@ -175,8 +161,7 @@ class Pages: m.content.isdigit() try: - msg = await self.bot.wait_for('message', check=message_check, - timeout=30.0) + msg = await self.bot.wait_for('message', check=message_check, timeout=30.0) except asyncio.TimeoutError: to_delete.append(await self.channel.send('Took too long.')) await asyncio.sleep(5) @@ -186,8 +171,7 @@ class Pages: if page != 0 and page <= self.maximum_pages: await self.show_page(page) else: - to_delete.append(await self.channel.send( - f'Invalid page given. ({page}/{self.maximum_pages})')) + to_delete.append(await self.channel.send(f'Invalid page given. ({page}/{self.maximum_pages})')) await asyncio.sleep(5) try: @@ -198,9 +182,8 @@ class Pages: async def show_help(self): """shows this message""" messages = ['Welcome to the interactive paginator!\n'] - messages.append( - 'This interactively allows you to see pages of text by navigating with ' \ - 'reactions. They are as follows:\n') + messages.append('This interactively allows you to see pages of text by navigating with ' \ + 'reactions. They are as follows:\n') for (emoji, func) in self.reaction_emojis: messages.append(f'{emoji} {func.__doc__}') @@ -208,8 +191,7 @@ class Pages: embed = self.embed.copy() embed.clear_fields() embed.description = '\n'.join(messages) - embed.set_footer( - text=f'We were on page {self.current_page} before this message.') + embed.set_footer(text=f'We were on page {self.current_page} before this message.') await self.message.edit(content=None, embed=embed) async def go_back_to_current_page(): @@ -248,9 +230,7 @@ class Pages: while self.paginating: try: - payload = await self.bot.wait_for('raw_reaction_add', - check=self.react_check, - timeout=120.0) + payload = await self.bot.wait_for('raw_reaction_add', check=self.react_check, timeout=120.0) except asyncio.TimeoutError: self.paginating = False try: @@ -261,15 +241,12 @@ class Pages: break try: - await self.message.remove_reaction(payload.emoji, - discord.Object( - id=payload.user_id)) + await self.message.remove_reaction(payload.emoji, discord.Object(id=payload.user_id)) 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() - class FieldPages(Pages): """Similar to Pages except entries should be a list of tuples having (key, value) to show as embed fields instead. @@ -290,19 +267,15 @@ class FieldPages(Pages): self.embed.set_footer(text=text) - class TextPages(Pages): """Uses a commands.Paginator internally to paginate some text.""" - def __init__(self, ctx, text, *, prefix='```', suffix='```', - max_size=2000): - paginator = CommandPaginator(prefix=prefix, suffix=suffix, - max_size=max_size - 200) + def __init__(self, ctx, text, *, prefix='```', suffix='```', max_size=2000): + paginator = CommandPaginator(prefix=prefix, suffix=suffix, max_size=max_size - 200) for line in text.split('\n'): paginator.add_line(line) - super().__init__(ctx, entries=paginator.pages, per_page=1, - show_entry_count=False) + super().__init__(ctx, entries=paginator.pages, per_page=1, show_entry_count=False) def get_page(self, page): return self.entries[page - 1] @@ -313,4 +286,4 @@ class TextPages(Pages): def get_content(self, entry, page, *, first=False): if self.maximum_pages > 1: return f'{entry}\nPage {page}/{self.maximum_pages}' - return entry + return entry \ No newline at end of file diff --git a/cogs/utils/version.py b/utils/version.py similarity index 100% rename from cogs/utils/version.py rename to utils/version.py