diff --git a/dockerfile b/dockerfile index 87c38ca..310dc72 100644 --- a/dockerfile +++ b/dockerfile @@ -16,4 +16,4 @@ RUN python3.6 -m pip install --upgrade pip && \ python3.6 -m pip install -r requirements.txt && \ python3.6 -m pip install -U git+https://github.com/Rapptz/discord.py@rewrite#egg=discord.py[voice] -cmd ["python3.6","-m","src"] +cmd ["python3.6","-m","sebimachine"] diff --git a/sebi_machine_launcher.sh b/sebi_machine_launcher.sh index ceb3395..a297954 100644 --- a/sebi_machine_launcher.sh +++ b/sebi_machine_launcher.sh @@ -9,7 +9,7 @@ trap "echo 'Received interrupt. Exiting.'; exit 0" SIGINT # Also loads the venv if it is present. [ -d .venv/bin ] && source .venv/bin/activate && echo "Entered venv." || echo "No venv detected." -until python -m src; do +until python -m sebimachine; do # Added colouring to ensure the date of shutdown and the exit code stands # out from the other clutter in the traceback that might have been output. echo -e "\e[0;31m[$(date --utc)]\e[0m Sebi-Machine shutdown with error \e[0;31m$?\e[0m. Restarting..." >&2 diff --git a/src/__init__.py b/sebimachine/__init__.py similarity index 91% rename from src/__init__.py rename to sebimachine/__init__.py index 70933e4..c53fc8d 100644 --- a/src/__init__.py +++ b/sebimachine/__init__.py @@ -4,7 +4,7 @@ Sebi-Machine. """ -__author__ = "Annihilator708" +__author__ = "Dusty.P" __contributors__ = (__author__, "Neko404NotFound", "Dusty.P", "davfsa", "YashKandalkar") __license__ = "MIT" diff --git a/src/__main__.py b/sebimachine/__main__.py similarity index 97% rename from src/__main__.py rename to sebimachine/__main__.py index fc39246..4300b3b 100644 --- a/src/__main__.py +++ b/sebimachine/__main__.py @@ -8,19 +8,19 @@ Something meaningful here, eventually. import asyncio import json import logging -import random -import traceback import os +import random import sys +import traceback +from typing import Dict import discord from discord.ext import commands -from src.config.config import LoadConfig -from src.shared_libs.loggable import Loggable -from src.shared_libs.ioutils import in_here -from src.shared_libs import database -from typing import Dict +from .config.config import LoadConfig +from .shared_libs import database +from .shared_libs.ioutils import in_here +from .shared_libs.loggable import Loggable # Init logging to output on INFO level to stderr. diff --git a/src/avatar.png b/sebimachine/avatar.png similarity index 100% rename from src/avatar.png rename to sebimachine/avatar.png diff --git a/src/cogs/__init__.py b/sebimachine/cogs/__init__.py similarity index 100% rename from src/cogs/__init__.py rename to sebimachine/cogs/__init__.py diff --git a/src/cogs/basic_commands.py b/sebimachine/cogs/basic_commands.py similarity index 100% rename from src/cogs/basic_commands.py rename to sebimachine/cogs/basic_commands.py index 91b265e..74eec70 100644 --- a/src/cogs/basic_commands.py +++ b/sebimachine/cogs/basic_commands.py @@ -1,9 +1,9 @@ #!/usr/bin/python # -*- coding: utf-8 -*- +import asyncio from discord.ext import commands import discord -import asyncio class BasicCommands: diff --git a/src/cogs/bot_management.py b/sebimachine/cogs/bot_management.py similarity index 100% rename from src/cogs/bot_management.py rename to sebimachine/cogs/bot_management.py diff --git a/src/cogs/code.py b/sebimachine/cogs/code.py similarity index 97% rename from src/cogs/code.py rename to sebimachine/cogs/code.py index 1608eef..6f70c2e 100644 --- a/src/cogs/code.py +++ b/sebimachine/cogs/code.py @@ -1,248 +1,248 @@ -from discord.ext import commands -import traceback -import discord -import inspect -import textwrap -from contextlib import redirect_stdout -import io - - -class REPL: - """Python in Discords""" - - def __init__(self, bot): - self.bot = bot - self._last_result = None - self.sessions = set() - - def cleanup_code(self, content): - """ - Automatically removes code blocks from the code. - """ - # remove ```py\n``` - if content.startswith("```") and content.endswith("```"): - return "\n".join(content.split("\n")[1:-1]) - - # remove `foo` - return content.strip("` \n") - - def get_syntax_error(self, e): - if e.text is None: - return "{0.__class__.__name__}: {0}".format(e) - return "{0.text}{1:>{0.offset}}\n{2}: {0}".format(e, "^", type(e).__name__) - - @commands.command(name="exec") - async def _eval(self, ctx, *, body: str = None): - """ - Execute python code in discord chat. - Only the owner of this bot can use this command. - - Alias: - - exec - Usage: - - exec < python code > - Example: - - exec print(546132) - """ - if ctx.author.id not in self.bot.ownerlist: - return await ctx.send( - "Only my contributors can use me like this :blush:", delete_after=10 - ) - - if body is None: - return await ctx.send( - "Please, use\n" - f'`{self.bot.config["prefix"]}exec`\n\n' - "\n`\\`\\`\\`py\n[python code]\n\\`\\`\\`\n" - "to get the most out of the command" - ) - - env = { - "bot": self.bot, - "ctx": ctx, - "channel": ctx.message.channel, - "author": ctx.message.author, - "server": ctx.message.guild, - "message": ctx.message, - "_": self._last_result, - } - - env.update(globals()) - - body = self.cleanup_code(body) - stdout = io.StringIO() - - to_compile = "async def func():\n%s" % textwrap.indent(body, " ") - - try: - exec(to_compile, env) - except SyntaxError as e: - try: - await ctx.send(f"```py\n{self.get_syntax_error(e)}\n```") - - except Exception as e: - error = [ - self.get_syntax_error(e)[i : i + 2000] - for i in range(0, len(self.get_syntax_error(e)), 2000) - ] - for i in error: - await ctx.send(f"```py\n{i}\n```") - - func = env["func"] - try: - with redirect_stdout(stdout): - ret = await func() - except Exception as e: - value = stdout.getvalue() - try: - await ctx.send(f"```py\n{value}{traceback.format_exc()}\n```") - - except Exception as e: - error = [value[i : i + 2000] for i in range(0, len(value), 2000)] - for i in error: - await ctx.send(f"```py\n{i}\n```") - - tracebackerror = [ - traceback.format_exc()[i : i + 2000] - for i in range(0, len(traceback.format_exc()), 2000) - ] - for i in tracebackerror: - await ctx.send(f"```py\n{i}\n```") - else: - value = stdout.getvalue() - if ret is None: - if value: - try: - await ctx.send(f"```py\n{value}\n```") - except Exception as e: - code = [value[i : i + 1980] for i in range(0, len(value), 1980)] - for i in code: - await ctx.send(f"```py\n{i}\n```") - else: - self._last_result = ret - try: - code = [value[i : i + 1980] for i in range(0, len(value), 1980)] - for i in code: - await ctx.send(f"```py\n{i}\n```") - except Exception as e: - code = [value[i : i + 1980] for i in range(0, len(value), 1980)] - for i in code: - await ctx.send(f"```py\n{i}\n```") - modifyd_ret = [ret[i : i + 1980] for i in range(0, len(ret), 1980)] - for i in modifyd_ret: - await ctx.send(f"```py\n{i}\n```") - - @commands.command(hidden=True) - async def repl(self, ctx): - """ - Start a interactive python shell in chat. - Only the owner of this bot can use this command. - - Usage: - - repl < python code > - Example: - - repl print(205554) - """ - if ctx.author.id not in self.bot.ownerlist: - return await ctx.send( - "Only my contributors can use me like this :blush:", delete_after=10 - ) - - msg = ctx.message - - variables = { - "ctx": ctx, - "bot": self.bot, - "message": msg, - "server": msg.guild, - "channel": msg.channel, - "author": msg.author, - "_": None, - } - - if msg.channel.id in self.sessions: - msg = await ctx.send( - "Already running a REPL session in this channel. Exit it with `quit`." - ) - - self.sessions.add(msg.channel.id) - - await ctx.send("Enter code to execute or evaluate. `exit()` or `quit` to exit.") - - while True: - response = await self.bot.wait_for( - "message", - check=lambda m: m.content.startswith("`") - and m.author == ctx.author - and m.channel == ctx.channel, - ) - - cleaned = self.cleanup_code(response.content) - - if cleaned in ("quit", "exit", "exit()"): - msg = await ctx.send("Exiting.") - self.sessions.remove(msg.channel.id) - return - - executor = exec - if cleaned.count("\n") == 0: - # single statement, potentially 'eval' - try: - code = compile(cleaned, "", "eval") - except SyntaxError: - pass - else: - executor = eval - - if executor is exec: - try: - code = compile(cleaned, "", "exec") - except SyntaxError as e: - try: - await ctx.send(f"```Python\n{self.get_syntax_error(e)}\n```") - except Exception as e: - error = [ - self.get_syntax_error(e)[i : i + 2000] - for i in range(0, len(self.get_syntax_error(e)), 2000) - ] - for i in error: - await ctx.send(f"```Python\n{i}\n```") - - variables["message"] = response - fmt = None - stdout = io.StringIO() - try: - with redirect_stdout(stdout): - result = executor(code, variables) - if inspect.isawaitable(result): - result = await result - - except Exception as e: - value = stdout.getvalue() - await ctx.send(f"```Python\n{value}{traceback.format_exc()}\n```") - continue - else: - value = stdout.getvalue() - if result is not None: - fmt = "{}{}".format(value, result) - variables["_"] = result - elif value: - fmt = value - - try: - if fmt is not None: - if len(fmt) > 1980: - code = [fmt[i : i + 1980] for i in range(0, len(fmt), 1980)] - for i in code: - await ctx.send(f"```py\n{i}\n```") - else: - await ctx.send(fmt) - - except discord.Forbidden: - pass - except discord.HTTPException as e: - await ctx.send(f"Unexpected error: `{e}`") - - -def setup(bot): - bot.add_cog(REPL(bot)) +from discord.ext import commands +import traceback +import discord +import inspect +import textwrap +from contextlib import redirect_stdout +import io + + +class REPL: + """Python in Discords""" + + def __init__(self, bot): + self.bot = bot + self._last_result = None + self.sessions = set() + + def cleanup_code(self, content): + """ + Automatically removes code blocks from the code. + """ + # remove ```py\n``` + if content.startswith("```") and content.endswith("```"): + return "\n".join(content.split("\n")[1:-1]) + + # remove `foo` + return content.strip("` \n") + + def get_syntax_error(self, e): + if e.text is None: + return "{0.__class__.__name__}: {0}".format(e) + return "{0.text}{1:>{0.offset}}\n{2}: {0}".format(e, "^", type(e).__name__) + + @commands.command(name="exec") + async def _eval(self, ctx, *, body: str = None): + """ + Execute python code in discord chat. + Only the owner of this bot can use this command. + + Alias: + - exec + Usage: + - exec < python code > + Example: + - exec print(546132) + """ + if ctx.author.id not in self.bot.ownerlist: + return await ctx.send( + "Only my contributors can use me like this :blush:", delete_after=10 + ) + + if body is None: + return await ctx.send( + "Please, use\n" + f'`{self.bot.config["prefix"]}exec`\n\n' + "\n`\\`\\`\\`py\n[python code]\n\\`\\`\\`\n" + "to get the most out of the command" + ) + + env = { + "bot": self.bot, + "ctx": ctx, + "channel": ctx.message.channel, + "author": ctx.message.author, + "server": ctx.message.guild, + "message": ctx.message, + "_": self._last_result, + } + + env.update(globals()) + + body = self.cleanup_code(body) + stdout = io.StringIO() + + to_compile = "async def func():\n%s" % textwrap.indent(body, " ") + + try: + exec(to_compile, env) + except SyntaxError as e: + try: + await ctx.send(f"```py\n{self.get_syntax_error(e)}\n```") + + except Exception as e: + error = [ + self.get_syntax_error(e)[i : i + 2000] + for i in range(0, len(self.get_syntax_error(e)), 2000) + ] + for i in error: + await ctx.send(f"```py\n{i}\n```") + + func = env["func"] + try: + with redirect_stdout(stdout): + ret = await func() + except Exception as e: + value = stdout.getvalue() + try: + await ctx.send(f"```py\n{value}{traceback.format_exc()}\n```") + + except Exception as e: + error = [value[i : i + 2000] for i in range(0, len(value), 2000)] + for i in error: + await ctx.send(f"```py\n{i}\n```") + + tracebackerror = [ + traceback.format_exc()[i : i + 2000] + for i in range(0, len(traceback.format_exc()), 2000) + ] + for i in tracebackerror: + await ctx.send(f"```py\n{i}\n```") + else: + value = stdout.getvalue() + if ret is None: + if value: + try: + await ctx.send(f"```py\n{value}\n```") + except Exception as e: + code = [value[i : i + 1980] for i in range(0, len(value), 1980)] + for i in code: + await ctx.send(f"```py\n{i}\n```") + else: + self._last_result = ret + try: + code = [value[i : i + 1980] for i in range(0, len(value), 1980)] + for i in code: + await ctx.send(f"```py\n{i}\n```") + except Exception as e: + code = [value[i : i + 1980] for i in range(0, len(value), 1980)] + for i in code: + await ctx.send(f"```py\n{i}\n```") + modifyd_ret = [ret[i : i + 1980] for i in range(0, len(ret), 1980)] + for i in modifyd_ret: + await ctx.send(f"```py\n{i}\n```") + + @commands.command(hidden=True) + async def repl(self, ctx): + """ + Start a interactive python shell in chat. + Only the owner of this bot can use this command. + + Usage: + - repl < python code > + Example: + - repl print(205554) + """ + if ctx.author.id not in self.bot.ownerlist: + return await ctx.send( + "Only my contributors can use me like this :blush:", delete_after=10 + ) + + msg = ctx.message + + variables = { + "ctx": ctx, + "bot": self.bot, + "message": msg, + "server": msg.guild, + "channel": msg.channel, + "author": msg.author, + "_": None, + } + + if msg.channel.id in self.sessions: + msg = await ctx.send( + "Already running a REPL session in this channel. Exit it with `quit`." + ) + + self.sessions.add(msg.channel.id) + + await ctx.send("Enter code to execute or evaluate. `exit()` or `quit` to exit.") + + while True: + response = await self.bot.wait_for( + "message", + check=lambda m: m.content.startswith("`") + and m.author == ctx.author + and m.channel == ctx.channel, + ) + + cleaned = self.cleanup_code(response.content) + + if cleaned in ("quit", "exit", "exit()"): + msg = await ctx.send("Exiting.") + self.sessions.remove(msg.channel.id) + return + + executor = exec + if cleaned.count("\n") == 0: + # single statement, potentially 'eval' + try: + code = compile(cleaned, "", "eval") + except SyntaxError: + pass + else: + executor = eval + + if executor is exec: + try: + code = compile(cleaned, "", "exec") + except SyntaxError as e: + try: + await ctx.send(f"```Python\n{self.get_syntax_error(e)}\n```") + except Exception as e: + error = [ + self.get_syntax_error(e)[i : i + 2000] + for i in range(0, len(self.get_syntax_error(e)), 2000) + ] + for i in error: + await ctx.send(f"```Python\n{i}\n```") + + variables["message"] = response + fmt = None + stdout = io.StringIO() + try: + with redirect_stdout(stdout): + result = executor(code, variables) + if inspect.isawaitable(result): + result = await result + + except Exception as e: + value = stdout.getvalue() + await ctx.send(f"```Python\n{value}{traceback.format_exc()}\n```") + continue + else: + value = stdout.getvalue() + if result is not None: + fmt = "{}{}".format(value, result) + variables["_"] = result + elif value: + fmt = value + + try: + if fmt is not None: + if len(fmt) > 1980: + code = [fmt[i : i + 1980] for i in range(0, len(fmt), 1980)] + for i in code: + await ctx.send(f"```py\n{i}\n```") + else: + await ctx.send(fmt) + + except discord.Forbidden: + pass + except discord.HTTPException as e: + await ctx.send(f"Unexpected error: `{e}`") + + +def setup(bot): + bot.add_cog(REPL(bot)) diff --git a/src/cogs/contributors.py b/sebimachine/cogs/contributors.py similarity index 97% rename from src/cogs/contributors.py rename to sebimachine/cogs/contributors.py index f242eb4..ee567bf 100644 --- a/src/cogs/contributors.py +++ b/sebimachine/cogs/contributors.py @@ -1,146 +1,146 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -from discord.ext import commands -import discord -import traceback -import aiofiles -import os - - -class Upload: - """ - CogName should be the name of the cog - """ - - def __init__(self, bot): - self.bot = bot - print("upload loaded") - - @commands.command() - async def reload(self, ctx, *, extension: str): - """Reload an extension.""" - await ctx.trigger_typing() - if ctx.author.id not in self.bot.ownerlist: - return await ctx.send( - "Only my contributors can use me like this :blush:", delete_after=10 - ) - - extension = extension.lower() - try: - self.bot.unload_extension("src.cogs.{}".format(extension)) - self.bot.load_extension("src.cogs.{}".format(extension)) - except Exception as e: - traceback.print_exc() - await ctx.send(f"Could not reload `{extension}` -> `{e}`") - else: - await ctx.send(f"Reloaded `{extension}`.") - - @commands.command() - async def reloadall(self, ctx): - """Reload all extensions.""" - await ctx.trigger_typing() - if ctx.author.id not in self.bot.ownerlist: - return await ctx.send( - "Only my contributors can use me like this :blush:", delete_after=10 - ) - - try: - for extension in self.bot.extensions: - self.bot.unload_extension(extension) - self.bot.load_extension(extension) - await ctx.send(f"Reload success! :thumbsup:\n") - except Exception as e: - await ctx.send(f"Could not reload `{extension}` -> `{e}`.\n") - - @commands.command() - async def unload(self, ctx, *, extension: str): - """Unload an extension.""" - await ctx.trigger_typing() - if ctx.author.id not in self.bot.ownerlist: - return await ctx.send( - "Only my contributors can use me like this :blush:", delete_after=10 - ) - - extension = extension.lower() - try: - self.bot.unload_extension("src.cogs.{}".format(extension)) - - except Exception as e: - traceback.print_exc() - if ctx.message.author.id not in self.bot.owner_list: - await ctx.send(f"Could not unload `{extension}` -> `{e}`") - - else: - await ctx.send(f"Unloaded `{extension}`.") - - @commands.command() - async def load(self, ctx, *, extension: str): - """Load an extension.""" - await ctx.trigger_typing() - if ctx.author.id not in self.bot.ownerlist: - return await ctx.send( - "Only my contributors can use me like this :blush:", delete_after=10 - ) - - extension = extension.lower() - try: - self.bot.load_extension("src.cogs.{}".format(extension)) - except Exception as e: - traceback.print_exc() - await ctx.send(f"Could not load `{extension}` -> `{e}`") - else: - await ctx.send(f"Loaded `{extension}`.") - - @commands.command() - async def permunload(self, ctx, extension=None): - """Disables permanently a cog.""" - await ctx.trigger_typing() - if ctx.author.id not in self.bot.ownerlist: - return await ctx.send( - "Only my contributors can use me like this :blush:", delete_after=10 - ) - - if cog is None: - return await ctx.send( - "Please provide a extension. Do `help permunload` for more info" - ) - - extension = extension.lower() - - async with aiofiles.open("extension.txt") as fp: - lines = fp.readlines() - - removed = False - async with aiofiles.open("extension.txt", "w") as fp: - for i in lines: - if i.replace("\n", "") != extension: - fp.write(i) - else: - removed = True - break - - if removed is True: - try: - self.bot.unload_extension(extension) - except: - pass - return await ctx.send("Extension removed successfully") - - await ctx.send("Extension not found") - - @commands.command(hidden=True) - async def reboot(self, ctx): - if ctx.author.id not in self.bot.ownerlist: - return await ctx.send( - "Only my contributors can use me like this :blush:", delete_after=10 - ) - await ctx.send("Sebi-Machine is restarting.") - with open(f"src/config/reboot", "w") as f: - f.write(f"1\n{ctx.channel.id}") - # noinspection PyProtectedMember - os._exit(1) - - -def setup(bot): - bot.add_cog(Upload(bot)) +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from discord.ext import commands +import discord +import traceback +import aiofiles +import os + + +class Upload: + """ + CogName should be the name of the cog + """ + + def __init__(self, bot): + self.bot = bot + print("upload loaded") + + @commands.command() + async def reload(self, ctx, *, extension: str): + """Reload an extension.""" + await ctx.trigger_typing() + if ctx.author.id not in self.bot.ownerlist: + return await ctx.send( + "Only my contributors can use me like this :blush:", delete_after=10 + ) + + extension = extension.lower() + try: + self.bot.unload_extension("src.cogs.{}".format(extension)) + self.bot.load_extension("src.cogs.{}".format(extension)) + except Exception as e: + traceback.print_exc() + await ctx.send(f"Could not reload `{extension}` -> `{e}`") + else: + await ctx.send(f"Reloaded `{extension}`.") + + @commands.command() + async def reloadall(self, ctx): + """Reload all extensions.""" + await ctx.trigger_typing() + if ctx.author.id not in self.bot.ownerlist: + return await ctx.send( + "Only my contributors can use me like this :blush:", delete_after=10 + ) + + try: + for extension in self.bot.extensions: + self.bot.unload_extension(extension) + self.bot.load_extension(extension) + await ctx.send(f"Reload success! :thumbsup:\n") + except Exception as e: + await ctx.send(f"Could not reload `{extension}` -> `{e}`.\n") + + @commands.command() + async def unload(self, ctx, *, extension: str): + """Unload an extension.""" + await ctx.trigger_typing() + if ctx.author.id not in self.bot.ownerlist: + return await ctx.send( + "Only my contributors can use me like this :blush:", delete_after=10 + ) + + extension = extension.lower() + try: + self.bot.unload_extension("src.cogs.{}".format(extension)) + + except Exception as e: + traceback.print_exc() + if ctx.message.author.id not in self.bot.owner_list: + await ctx.send(f"Could not unload `{extension}` -> `{e}`") + + else: + await ctx.send(f"Unloaded `{extension}`.") + + @commands.command() + async def load(self, ctx, *, extension: str): + """Load an extension.""" + await ctx.trigger_typing() + if ctx.author.id not in self.bot.ownerlist: + return await ctx.send( + "Only my contributors can use me like this :blush:", delete_after=10 + ) + + extension = extension.lower() + try: + self.bot.load_extension("src.cogs.{}".format(extension)) + except Exception as e: + traceback.print_exc() + await ctx.send(f"Could not load `{extension}` -> `{e}`") + else: + await ctx.send(f"Loaded `{extension}`.") + + @commands.command() + async def permunload(self, ctx, extension=None): + """Disables permanently a cog.""" + await ctx.trigger_typing() + if ctx.author.id not in self.bot.ownerlist: + return await ctx.send( + "Only my contributors can use me like this :blush:", delete_after=10 + ) + + if cog is None: + return await ctx.send( + "Please provide a extension. Do `help permunload` for more info" + ) + + extension = extension.lower() + + async with aiofiles.open("extension.txt") as fp: + lines = fp.readlines() + + removed = False + async with aiofiles.open("extension.txt", "w") as fp: + for i in lines: + if i.replace("\n", "") != extension: + fp.write(i) + else: + removed = True + break + + if removed is True: + try: + self.bot.unload_extension(extension) + except: + pass + return await ctx.send("Extension removed successfully") + + await ctx.send("Extension not found") + + @commands.command(hidden=True) + async def reboot(self, ctx): + if ctx.author.id not in self.bot.ownerlist: + return await ctx.send( + "Only my contributors can use me like this :blush:", delete_after=10 + ) + await ctx.send("Sebi-Machine is restarting.") + with open(f"src/config/reboot", "w") as f: + f.write(f"1\n{ctx.channel.id}") + # noinspection PyProtectedMember + os._exit(1) + + +def setup(bot): + bot.add_cog(Upload(bot)) diff --git a/src/cogs/example.py b/sebimachine/cogs/example.py similarity index 95% rename from src/cogs/example.py rename to sebimachine/cogs/example.py index 4840a3d..fbc859b 100644 --- a/src/cogs/example.py +++ b/sebimachine/cogs/example.py @@ -1,26 +1,26 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -from discord.ext import commands -import discord - - -class CogName: - """ - CogName should be the name of the cog - """ - - def __init__(self, bot): - self.bot = bot - - @commands.command() - async def ping(self, ctx): - """Say pong""" - now = ctx.message.created_at - msg = await ctx.send("Pong") - sub = msg.created_at - now - await msg.edit(content=f"🏓Pong, **{sub.total_seconds() * 1000}ms**") - - -def setup(bot): - bot.add_cog(CogName(bot)) +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from discord.ext import commands +import discord + + +class CogName: + """ + CogName should be the name of the cog + """ + + def __init__(self, bot): + self.bot = bot + + @commands.command() + async def ping(self, ctx): + """Say pong""" + now = ctx.message.created_at + msg = await ctx.send("Pong") + sub = msg.created_at - now + await msg.edit(content=f"🏓Pong, **{sub.total_seconds() * 1000}ms**") + + +def setup(bot): + bot.add_cog(CogName(bot)) diff --git a/src/cogs/fun.py b/sebimachine/cogs/fun.py similarity index 95% rename from src/cogs/fun.py rename to sebimachine/cogs/fun.py index 7410940..396c231 100644 --- a/src/cogs/fun.py +++ b/sebimachine/cogs/fun.py @@ -1,47 +1,47 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -from discord.ext import commands -import discord -import random -import aiohttp - - -class Fun: - """ - CogName should be the name of the cog - """ - - def __init__(self, bot): - self.bot = bot - - @commands.command() - async def sebisauce(self, ctx): - """ - Get a image related to Sebi. - Sebi is a random guy with perfect code related jokes. - - Usage: - - sebisauce - """ - await ctx.trigger_typing() - url = "http://ikbengeslaagd.com/API/sebisauce.json" - async with aiohttp.ClientSession() as session: - async with session.get(url) as response: - source = await response.json(encoding="utf8") - - total_sebi = 0 - for key in dict.keys(source): - total_sebi += 1 - - im = random.randint(0, int(total_sebi) - 1) - - await ctx.send( - embed=discord.Embed( - title="\t", description="\t", color=self.bot.embed_color - ).set_image(url=source[str(im)]) - ) - - -def setup(bot): - bot.add_cog(Fun(bot)) +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from discord.ext import commands +import discord +import random +import aiohttp + + +class Fun: + """ + CogName should be the name of the cog + """ + + def __init__(self, bot): + self.bot = bot + + @commands.command() + async def sebisauce(self, ctx): + """ + Get a image related to Sebi. + Sebi is a random guy with perfect code related jokes. + + Usage: + - sebisauce + """ + await ctx.trigger_typing() + url = "http://ikbengeslaagd.com/API/sebisauce.json" + async with aiohttp.ClientSession() as session: + async with session.get(url) as response: + source = await response.json(encoding="utf8") + + total_sebi = 0 + for key in dict.keys(source): + total_sebi += 1 + + im = random.randint(0, int(total_sebi) - 1) + + await ctx.send( + embed=discord.Embed( + title="\t", description="\t", color=self.bot.embed_color + ).set_image(url=source[str(im)]) + ) + + +def setup(bot): + bot.add_cog(Fun(bot)) diff --git a/src/cogs/git.py b/sebimachine/cogs/git.py similarity index 92% rename from src/cogs/git.py rename to sebimachine/cogs/git.py index 39f8a0a..6bd3a62 100644 --- a/src/cogs/git.py +++ b/sebimachine/cogs/git.py @@ -29,8 +29,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import discord from discord.ext import commands -from src.shared_libs.utils import paginate, run_command -from src.shared_libs.loggable import Loggable +from sebimachine.shared_libs.utils import paginate, run_command +from sebimachine.shared_libs.loggable import Loggable + +from sebimachine import __url__ import asyncio @@ -41,7 +43,8 @@ class Git(Loggable): @commands.group(case_insensitive=True, invoke_without_command=True) async def git(self, ctx): """Run help git for more info""" - await ctx.send("https://github.com/dustinpianalto/Sebi-Machine/") + # await ctx.send("https://github.com/dustinpianalto/Sebi-Machine/") + await ctx.send(__url__ or "No URL specified in __init__.py") @commands.command(case_insensitive=True, brief="Gets the Trello link.") async def trello(self, ctx): diff --git a/src/cogs/moderation.py b/sebimachine/cogs/moderation.py similarity index 100% rename from src/cogs/moderation.py rename to sebimachine/cogs/moderation.py diff --git a/src/cogs/music.py b/sebimachine/cogs/music.py similarity index 99% rename from src/cogs/music.py rename to sebimachine/cogs/music.py index 5a0aab3..8815347 100644 --- a/src/cogs/music.py +++ b/sebimachine/cogs/music.py @@ -15,7 +15,7 @@ from .utils import noblock YT_DL_OPTS = { - "format": "mp3[abr>0]/bestaudio/best", + "format": "ogg[abr>0]/bestaudio/best", "ignoreerrors": True, "default_search": "auto", "source_address": "0.0.0.0", diff --git a/src/cogs/sar.js b/sebimachine/cogs/sar.js similarity index 100% rename from src/cogs/sar.js rename to sebimachine/cogs/sar.js diff --git a/src/cogs/tag.py b/sebimachine/cogs/tag.py similarity index 100% rename from src/cogs/tag.py rename to sebimachine/cogs/tag.py diff --git a/src/cogs/utils/__init__.py b/sebimachine/cogs/utils/__init__.py similarity index 100% rename from src/cogs/utils/__init__.py rename to sebimachine/cogs/utils/__init__.py diff --git a/src/cogs/utils/noblock.py b/sebimachine/cogs/utils/noblock.py similarity index 100% rename from src/cogs/utils/noblock.py rename to sebimachine/cogs/utils/noblock.py diff --git a/src/config/__init__.py b/sebimachine/config/__init__.py similarity index 100% rename from src/config/__init__.py rename to sebimachine/config/__init__.py diff --git a/src/config/config.py b/sebimachine/config/config.py similarity index 100% rename from src/config/config.py rename to sebimachine/config/config.py diff --git a/src/extensions.txt b/sebimachine/extensions.txt similarity index 100% rename from src/extensions.txt rename to sebimachine/extensions.txt diff --git a/src/run.js b/sebimachine/run.js similarity index 100% rename from src/run.js rename to sebimachine/run.js diff --git a/src/shared_libs/__init__.py b/sebimachine/shared_libs/__init__.py similarity index 100% rename from src/shared_libs/__init__.py rename to sebimachine/shared_libs/__init__.py diff --git a/src/shared_libs/aliases.json b/sebimachine/shared_libs/aliases.json similarity index 93% rename from src/shared_libs/aliases.json rename to sebimachine/shared_libs/aliases.json index 2890f82..99e41c5 100644 --- a/src/shared_libs/aliases.json +++ b/sebimachine/shared_libs/aliases.json @@ -1,6 +1,6 @@ -{ - "sar": "sar", - "selfrole": "sar", - "selfroles": "sar" - +{ + "sar": "sar", + "selfrole": "sar", + "selfroles": "sar" + } \ No newline at end of file diff --git a/src/shared_libs/database.py b/sebimachine/shared_libs/database.py similarity index 99% rename from src/shared_libs/database.py rename to sebimachine/shared_libs/database.py index c97170c..181425d 100644 --- a/src/shared_libs/database.py +++ b/sebimachine/shared_libs/database.py @@ -1,6 +1,7 @@ -import asyncpg import asyncio +import asyncpg + class DatabaseConnection: def __init__( diff --git a/src/shared_libs/ioutils.py b/sebimachine/shared_libs/ioutils.py similarity index 100% rename from src/shared_libs/ioutils.py rename to sebimachine/shared_libs/ioutils.py diff --git a/src/shared_libs/loggable.py b/sebimachine/shared_libs/loggable.py similarity index 100% rename from src/shared_libs/loggable.py rename to sebimachine/shared_libs/loggable.py diff --git a/src/shared_libs/paginator.py b/sebimachine/shared_libs/paginator.py similarity index 99% rename from src/shared_libs/paginator.py rename to sebimachine/shared_libs/paginator.py index a5ddf60..0c16b47 100644 --- a/src/shared_libs/paginator.py +++ b/sebimachine/shared_libs/paginator.py @@ -31,9 +31,10 @@ Utility for creating Paginated responses import asyncio -import discord import typing +import discord + class Paginator: def __init__( diff --git a/src/shared_libs/tags.json b/sebimachine/shared_libs/tags.json similarity index 100% rename from src/shared_libs/tags.json rename to sebimachine/shared_libs/tags.json diff --git a/src/shared_libs/utils.py b/sebimachine/shared_libs/utils.py similarity index 96% rename from src/shared_libs/utils.py rename to sebimachine/shared_libs/utils.py index dfb32ea..48bea65 100644 --- a/src/shared_libs/utils.py +++ b/sebimachine/shared_libs/utils.py @@ -1,115 +1,115 @@ -""" -=== - -MIT License - -Copyright (c) 2018 Dusty.P https://github.com/dustinpianalto - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -""" - - -from io import StringIO -import sys -import asyncio -import discord -from discord.ext.commands.formatter import Paginator - - -class Capturing(list): - def __enter__(self): - self._stdout = sys.stdout - sys.stdout = self._stringio = StringIO() - return self - - def __exit__(self, *args): - self.extend(self._stringio.getvalue().splitlines()) - del self._stringio # free up some memory - sys.stdout = self._stdout - - -def to_list_of_str(items, out: list = list(), level=1, recurse=0): - def rec_loop(item, key, out, level): - quote = '"' - if type(item) == list: - out.append(f'{" "*level}{quote+key+quote+": " if key else ""}[') - new_level = level + 1 - out = to_list_of_str(item, out, new_level, 1) - out.append(f'{" "*level}]') - elif type(item) == dict: - out.append(f'{" "*level}{quote+key+quote+": " if key else ""}{{') - new_level = level + 1 - out = to_list_of_str(item, out, new_level, 1) - out.append(f'{" "*level}}}') - else: - out.append( - f'{" "*level}{quote+key+quote+": " if key else ""}{repr(item)},' - ) - - if type(items) == list: - if not recurse: - out = list() - out.append("[") - for item in items: - rec_loop(item, None, out, level) - if not recurse: - out.append("]") - elif type(items) == dict: - if not recurse: - out = list() - out.append("{") - for key in items: - rec_loop(items[key], key, out, level) - if not recurse: - out.append("}") - - return out - - -def paginate(text, maxlen=1990): - paginator = Paginator(prefix="```py", max_size=maxlen + 10) - if type(text) == list: - data = to_list_of_str(text) - elif type(text) == dict: - data = to_list_of_str(text) - else: - data = str(text).split("\n") - for line in data: - if len(line) > maxlen: - n = maxlen - for l in [line[i : i + n] for i in range(0, len(line), n)]: - paginator.add_line(l) - else: - paginator.add_line(line) - return paginator.pages - - -async def run_command(args): - # Create subprocess - process = await asyncio.create_subprocess_shell( - args, - # stdout must a pipe to be accessible as process.stdout - stdout=asyncio.subprocess.PIPE, - ) - # Wait for the subprocess to finish - stdout, stderr = await process.communicate() - # Return stdout - return stdout.decode().strip() +""" +=== + +MIT License + +Copyright (c) 2018 Dusty.P https://github.com/dustinpianalto + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +""" + + +from io import StringIO +import sys +import asyncio +import discord +from discord.ext.commands.formatter import Paginator + + +class Capturing(list): + def __enter__(self): + self._stdout = sys.stdout + sys.stdout = self._stringio = StringIO() + return self + + def __exit__(self, *args): + self.extend(self._stringio.getvalue().splitlines()) + del self._stringio # free up some memory + sys.stdout = self._stdout + + +def to_list_of_str(items, out: list = list(), level=1, recurse=0): + def rec_loop(item, key, out, level): + quote = '"' + if type(item) == list: + out.append(f'{" "*level}{quote+key+quote+": " if key else ""}[') + new_level = level + 1 + out = to_list_of_str(item, out, new_level, 1) + out.append(f'{" "*level}]') + elif type(item) == dict: + out.append(f'{" "*level}{quote+key+quote+": " if key else ""}{{') + new_level = level + 1 + out = to_list_of_str(item, out, new_level, 1) + out.append(f'{" "*level}}}') + else: + out.append( + f'{" "*level}{quote+key+quote+": " if key else ""}{repr(item)},' + ) + + if type(items) == list: + if not recurse: + out = list() + out.append("[") + for item in items: + rec_loop(item, None, out, level) + if not recurse: + out.append("]") + elif type(items) == dict: + if not recurse: + out = list() + out.append("{") + for key in items: + rec_loop(items[key], key, out, level) + if not recurse: + out.append("}") + + return out + + +def paginate(text, maxlen=1990): + paginator = Paginator(prefix="```py", max_size=maxlen + 10) + if type(text) == list: + data = to_list_of_str(text) + elif type(text) == dict: + data = to_list_of_str(text) + else: + data = str(text).split("\n") + for line in data: + if len(line) > maxlen: + n = maxlen + for l in [line[i : i + n] for i in range(0, len(line), n)]: + paginator.add_line(l) + else: + paginator.add_line(line) + return paginator.pages + + +async def run_command(args): + # Create subprocess + process = await asyncio.create_subprocess_shell( + args, + # stdout must a pipe to be accessible as process.stdout + stdout=asyncio.subprocess.PIPE, + ) + # Wait for the subprocess to finish + stdout, stderr = await process.communicate() + # Return stdout + return stdout.decode().strip() diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..42d2602 --- /dev/null +++ b/setup.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3.6 +# -*- coding: utf-8 -*- +""" +Setup. +""" +import re +from setuptools import setup +import traceback + + +package_name = 'sebimachine' + + +req_line_test = lambda l: l and l[0] != '#' + + +with open('README.md') as fp: + readme = fp.read() + + +with open('requirements.txt') as fp: + requirements = {*filter(req_line_test, map(str.lstrip, fp.read().split('\n')))} + + +with open(f'{package_name}/__init__.py') as fp: + attrs = {} + print('Attributes:') + for k, v in re.findall(r'^__(\w+)__\s?=\s?"([^"]*)"', fp.read(), re.M): + k = 'name' if k == 'title' else k + attrs[k] = v + print(k, v) + + +# Use pip on invoke to install requirements. Ensures we can essentially just run this +# script without setuptools arguments. TODO: fix. +try: + import pip + pip.main(['install', *install_requires]) +except (ModuleNotFoundError, ImportError): + print('Failed to import pip. Install git dependencies manually.') + traceback.print_exc() + + +setup( + long_description=readme, + **attrs +)