diff --git a/.gitignore b/.gitignore index e59dfb0..941ff36 100644 --- a/.gitignore +++ b/.gitignore @@ -109,22 +109,4 @@ venv.bak/ .mypy_cache/ /src/config/Config.json -/src/config/PrivateConfig.json - -# IntelliJ stuff -.idea/ -*.iml - -# Sublime stuff -# Cache files for Sublime Text -*.tmlanguage.cache -*.tmPreferences.cache -*.stTheme.cache - -# Workspace files are user-specific -*.sublime-workspace - -# Project files should be checked into the repository, unless a significant -# proportion of contributors will probably not be using Sublime Text -# *.sublime-project - +/src/config/PrivateConfig.json \ No newline at end of file diff --git a/README.md b/README.md index 1e0e30f..eea3036 100644 --- a/README.md +++ b/README.md @@ -7,36 +7,57 @@ http://discord.gg/GWdhBSp This bot extends the rewrite version of discord.py. A couple of variables have been added to give you easy access to a couple of objects listed here. -> self.ownerlist +> self.bot.ownerlist self.ownerlist can be used to retrieve a `list` of user ID's. (`int`). Those ID's belong to contributors. -> self.defaultprefix +> self.bot.defaultprefix self.defaultprefix can be used to retrieve a `str` object of the default prefix. -> self.version +> self.bot.version self.version can be used to retrieve a `float` which represent the version number of the bot. -> self.display_name +> self.bot.display_name self.display_name returns a `str` which represent the display_name of the bot. -> self.mainenance +> self.bot.mainenance self.maintenance is equal to `True` or `False`. If you would like to exclude code in the master branch, use this. Make sure this one is installed. example: ```py -if self.mainenance: +if self.bot.mainenance: print('I am in the development branch') -if not self.mainenance: +if not self.bot.mainenance: print('I am in the master branch) ``` With other words. self.mainenance returns False in production and True in developer modus. +> self.bot.embed_color + +self.embed_color can be used to use the default color of out embed theme. +``` +discord.Embed(title='Foo', description='bar', color=self.bot.embed_color) +``` + +## Docker environment +This bot is heavly based on docker. This means it will run in a container. Other words. The code will run in a jail. Dont be afraid for bugs that cause harm. or commands that could potential restarts the server. Its safe. + +There are a couple of things to know about docker within this project. +1. Please read the docs of docker first before editing the docker files +2. If you need a pip package, place the name into requirements.txt, docker handles the rest. +3. Everything in project folder is the workfolder of the docker container +4. Initialize cogs by adding them into cogs.txt. one line is one cogfile + ## Initialize a cog -Cogs can be placed in `./src/cogs`. Overall the `src` folder is the place to put code in. -Make sure to update the `requirements.txt` and it is important to add the name of your cog file into the `cogs.txt` list. Otherwise it may turn out that your cog wont load. +Put your cog in `src/cogs` and edit the `cogs.txt` file. Add the filename of your cog into `cogs.txt`. No absolute path, just the name. ## Update source code There is a git command available provided by Dusty. `S!git pull` should pull the latest commits into the docker container. Make sure afterwards to reload the cog. -If you are stuck in any way shape or form you can always contact anyone who works on this project. Dont forget to check `S!help`. \ No newline at end of file +If you are stuck in any way shape or form you can always contact anyone who works on this project. Dont forget to check `S!help`. + +## Project links: + - http://discord.gg/GWdhBSp + - http://chillout.ueuo.com + - http://trello.com/b/x02goBbW/sebis-bot-tutorial-roadmap + diff --git a/avatar.png b/avatar.png new file mode 100644 index 0000000..25d0115 Binary files /dev/null and b/avatar.png differ diff --git a/cogs.txt b/cogs.txt index 5069854..9529ab0 100644 --- a/cogs.txt +++ b/cogs.txt @@ -1,3 +1,5 @@ example -upload -git \ No newline at end of file +contributors +code +git +fun \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index fac7406..0927ad0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ yarl<1.2 numpy==1.14.0 +uvloop diff --git a/run.py b/run.py index 3342e7d..dffb7a8 100644 --- a/run.py +++ b/run.py @@ -2,6 +2,7 @@ # -*- coding: utf8 -*- # Import packages +import asyncio import discord from discord.ext import commands import json @@ -11,6 +12,18 @@ import random # Import custom files from src.config.config import LoadConfig +# If uvloop is installed, change to that eventloop policy as it +# is more efficient +try: + import uvloop + asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) + del uvloop +except BaseException as ex: + print(f'Could not load uvloop. {type(ex).__name__}: {ex};', + 'reverting to default impl.') +else: + print(f'Using uvloop for asyncio event loop policy.') + # Bot Class class SebiMachine(commands.Bot, LoadConfig): @@ -19,7 +32,7 @@ class SebiMachine(commands.Bot, LoadConfig): # Initialize and attach config / settings LoadConfig.__init__(self) commands.Bot.__init__(self, command_prefix=self.defaultprefix) - self.embed_color = discord.Color(0x00FFFF) + # Load plugins # Add your cog file name in this list @@ -67,4 +80,4 @@ if __name__ == '__main__': with open('src/config/PrivateConfig.json') as fp: PrivateConfig = json.load(fp) fp.close() - client.run(PrivateConfig["bot-key"]) \ No newline at end of file + client.run(PrivateConfig["bot-key"]) diff --git a/src/cogs/code.py b/src/cogs/code.py new file mode 100644 index 0000000..69bea4d --- /dev/null +++ b/src/cogs/code.py @@ -0,0 +1,126 @@ +from discord.ext import commands +import traceback +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: + msg = 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: + msg = 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: + msg = 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: + msg = await ctx.send(f'```py\n{i}\n```') + else: + value = stdout.getvalue() + if ret is None: + if value: + try: + msg = 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: + msg = 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: + msg = 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: + msg = await ctx.send(f'```py\n{i}\n```') + + +def setup(bot): + bot.add_cog(REPL(bot)) \ No newline at end of file diff --git a/src/cogs/upload.py b/src/cogs/contributors.py similarity index 79% rename from src/cogs/upload.py rename to src/cogs/contributors.py index fa8b66e..f546128 100644 --- a/src/cogs/upload.py +++ b/src/cogs/contributors.py @@ -1,87 +1,92 @@ -#!/usr/bin/python -# -*- coding: -*- - -from discord.ext import commands -import os -import traceback - -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.""" - - if ctx.author.id not in self.bot.ownerlist: - return await ctx.send('Only my creator 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.""" - if ctx.author.id not in self.bot.ownerlist: - return await ctx.send('Only my creator 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.""" - if ctx.author.id not in self.bot.ownerlist: - return await ctx.send('Only my creator 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.""" - if ctx.author.id not in self.bot.ownerlist: - return await ctx.send('Only my creator 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 unload `{extension}` -> `{e}`') - else: - await ctx.send(f'Loaded `{extension}`.') - - @commands.command() - async def err(self, ctx): - """triggers error to test traceback""" - await ctx.send(a) - -def setup(bot): +#!/usr/bin/python +# -*- coding: -*- + +from discord.ext import commands +import os +import traceback + +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 unload `{extension}` -> `{e}`') + else: + await ctx.send(f'Loaded `{extension}`.') + + @commands.command() + async def err(self, ctx): + """triggers error to test traceback""" + await ctx.send(a) + + + +def setup(bot): bot.add_cog(Upload(bot)) \ No newline at end of file diff --git a/src/cogs/example.py b/src/cogs/example.py index b8e88c8..de1c1da 100644 --- a/src/cogs/example.py +++ b/src/cogs/example.py @@ -1,20 +1,24 @@ -#!/usr/bin/python -# -*- coding: -*- - -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""" - await ctx.send('Pong') - -def setup(bot): - bot.add_cog(CogName(bot)) \ No newline at end of file +#!/usr/bin/python +# -*- coding: -*- + +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}') + + +def setup(bot): + bot.add_cog(CogName(bot)) diff --git a/src/cogs/fun.py b/src/cogs/fun.py new file mode 100644 index 0000000..d066435 --- /dev/null +++ b/src/cogs/fun.py @@ -0,0 +1,44 @@ +#!/usr/bin/python +# -*- coding: -*- + +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/src/cogs/git.py index 7c83f98..b1072a3 100644 --- a/src/cogs/git.py +++ b/src/cogs/git.py @@ -1,90 +1,90 @@ -""" -=== - -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. - -""" - - -import discord -from discord.ext import commands -import logging -from ..shared_libs.utils import paginate, run_command -import asyncio - -git_log = logging.getLogger('git') - - -class Git: - def __init__(self, bot): - self.bot = bot - - @commands.group(case_insensitive=True) - async def git(self, ctx): - """Run help git for more info""" - pass - - @git.command() - async def pull(self, ctx): - await ctx.trigger_typing() - if ctx.author.id not in self.bot.ownerlist: - return await ctx.send('Only my creator can use me like this :blush:', delete_after=10) - em = discord.Embed(style='rich', - title=f'Git Pull', - color=self.bot.embed_color) - em.set_thumbnail(url=f'{ctx.guild.me.avatar_url}') - - result = await asyncio.wait_for(self.bot.loop.create_task( - run_command('git fetch --all')), 120) + '\n' - result += await asyncio.wait_for(self.bot.loop.create_task( - run_command('git reset --hard origin/$(git rev-parse ' - '--symbolic-full-name --abbrev-ref HEAD)')), - 120) + '\n\n' - result += await asyncio.wait_for(self.bot.loop.create_task( - run_command('git show --stat | sed "s/.*@.*[.].*/ /g"')), 10) - - results = paginate(result, maxlen=1014) - for page in results[:5]: - em.add_field(name='\uFFF0', value=f'{page}') - await ctx.send(embed=em) - - @git.command() - async def status(self, ctx): - await ctx.trigger_typing() - if ctx.author.id not in self.bot.ownerlist: - return await ctx.send('Only my creator can use me like this :blush:', delete_after=10) - em = discord.Embed(style='rich', - title=f'Git Pull', - color=self.bot.embed_color) - em.set_thumbnail(url=f'{ctx.guild.me.avatar_url}') - result = await asyncio.wait_for(self.bot.loop.create_task( - run_command('git status')), 10) - results = paginate(result, maxlen=1014) - for page in results[:5]: - em.add_field(name='\uFFF0', value=f'{page}') - await ctx.send(embed=em) - - -def setup(bot): - bot.add_cog(Git(bot)) +""" +=== + +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. + +""" + + +import discord +from discord.ext import commands +import logging +from ..shared_libs.utils import paginate, run_command +import asyncio + +git_log = logging.getLogger('git') + + +class Git: + def __init__(self, bot): + self.bot = bot + + @commands.group(case_insensitive=True) + async def git(self, ctx): + """Run help git for more info""" + pass + + @git.command() + async def pull(self, ctx): + 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) + em = discord.Embed(style='rich', + title=f'Git Pull', + color=self.bot.embed_color) + em.set_thumbnail(url=f'{ctx.guild.me.avatar_url}') + + result = await asyncio.wait_for(self.bot.loop.create_task( + run_command('git fetch --all')), 120) + '\n' + result += await asyncio.wait_for(self.bot.loop.create_task( + run_command('git reset --hard origin/$(git rev-parse ' + '--symbolic-full-name --abbrev-ref HEAD)')), + 120) + '\n\n' + result += await asyncio.wait_for(self.bot.loop.create_task( + run_command('git show --stat | sed "s/.*@.*[.].*/ /g"')), 10) + + results = paginate(result, maxlen=1014) + for page in results[:5]: + em.add_field(name='\uFFF0', value=f'{page}') + await ctx.send(embed=em) + + @git.command() + async def status(self, ctx): + 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) + em = discord.Embed(style='rich', + title=f'Git Status', + color=self.bot.embed_color) + em.set_thumbnail(url=f'{ctx.guild.me.avatar_url}') + result = await asyncio.wait_for(self.bot.loop.create_task( + run_command('git status')), 10) + results = paginate(result, maxlen=1014) + for page in results[:5]: + em.add_field(name='\uFFF0', value=f'{page}') + await ctx.send(embed=em) + + +def setup(bot): + bot.add_cog(Git(bot)) diff --git a/src/config/Config.json b/src/config/Config.json new file mode 100644 index 0000000..62c06ae --- /dev/null +++ b/src/config/Config.json @@ -0,0 +1,7 @@ +{ + "version": 0.1, + "display_name" : "[S!] Sebi-Machine", + "maintenance": "True", + "ownerlist": [], + "prefix": "S!" +} \ No newline at end of file diff --git a/src/config/PrivateConfig.json b/src/config/PrivateConfig.json new file mode 100644 index 0000000..9fa108b --- /dev/null +++ b/src/config/PrivateConfig.json @@ -0,0 +1,3 @@ +{ + "bot-key": "" +} \ No newline at end of file diff --git a/src/config/config.py b/src/config/config.py index f32d0f6..b6f5acf 100644 --- a/src/config/config.py +++ b/src/config/config.py @@ -2,7 +2,7 @@ # -*- coding: -*- import json - +import discord class LoadConfig: """ @@ -19,6 +19,7 @@ class LoadConfig: self.version = self.config["version"] self.display_name = self.config["display_name"] self.maintenance = self.config["maintenance"] + self.embed_color = discord.Color(0x00FFFF) if self.maintenance == 'False': self.maintenance = False else: diff --git a/src/shared_libs/aliases.json b/src/shared_libs/aliases.json new file mode 100644 index 0000000..2890f82 --- /dev/null +++ b/src/shared_libs/aliases.json @@ -0,0 +1,6 @@ +{ + "sar": "sar", + "selfrole": "sar", + "selfroles": "sar" + +} \ No newline at end of file diff --git a/src/shared_libs/utils.py b/src/shared_libs/utils.py index 47a8ff4..9ba5998 100644 --- a/src/shared_libs/utils.py +++ b/src/shared_libs/utils.py @@ -1,113 +1,113 @@ -""" -=== - -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 -import numpy as np - - -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 +""" +=== + +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 +import numpy as np + + +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() \ No newline at end of file