From 90194263023e6b8608d72e8fe8f17321cb756978 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 11 Jan 2018 10:08:23 -0600 Subject: [PATCH 01/16] Added a horse command/use API for snake command --- cogs/images.py | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/cogs/images.py b/cogs/images.py index 0b7e2b2..d95d7d1 100644 --- a/cogs/images.py +++ b/cogs/images.py @@ -55,7 +55,7 @@ class Images: f = discord.File(image, filename=filename) await ctx.send(file=f) - @commands.command() + @commands.command(aliases=['snake']) @utils.custom_perms(send_messages=True) @utils.check_restricted() async def snek(self, ctx): @@ -63,10 +63,41 @@ class Images: EXAMPLE: !snek RESULT: A beautiful picture of a snek o3o""" - # Find a random image based on how many we currently have - f = random.SystemRandom().choice(glob.glob('images/snek*')) - with open(f, 'rb') as f: - await ctx.send(file=discord.File(f)) + result = await utils.requrest("http://hrsendl.com/snake") + if result is None: + await ctx.send("I couldn't connect! Sorry no snakes right now ;w;") + return + filename = result.get('image', None) + if filename is None: + await ctx.send("I couldn't connect! Sorry no snakes right now ;w;") + return + + image = await utils.download_image(filename) + filename = re.search('.*/snakes/(.*)', filename).group(1) + f = discord.File(image, filename=filename) + await ctx.send(file=f) + + @commands.command() + @utils.custom_perms(send_messages=True) + @utils.check_restricted() + async def horse(self, ctx): + """Use this to print a random horse image. + + EXAMPLE: !horse + RESULT: A beautiful picture of a horse o3o""" + result = await utils.requrest("http://hrsendl.com/horse") + if result is None: + await ctx.send("I couldn't connect! Sorry no horses right now ;w;") + return + filename = result.get('image', None) + if filename is None: + await ctx.send("I couldn't connect! Sorry no horses right now ;w;") + return + + image = await utils.download_image(filename) + filename = re.search('.*/horses/(.*)', filename).group(1) + f = discord.File(image, filename=filename) + await ctx.send(file=f) @commands.command() @commands.guild_only() From 5fe0e65cb3d3fcad24455a3dab89015e5d52c557 Mon Sep 17 00:00:00 2001 From: Dan Date: Thu, 11 Jan 2018 10:43:38 -0600 Subject: [PATCH 02/16] Correct typo --- cogs/images.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cogs/images.py b/cogs/images.py index d95d7d1..5168945 100644 --- a/cogs/images.py +++ b/cogs/images.py @@ -63,7 +63,7 @@ class Images: EXAMPLE: !snek RESULT: A beautiful picture of a snek o3o""" - result = await utils.requrest("http://hrsendl.com/snake") + result = await utils.request("http://hrsendl.com/snake") if result is None: await ctx.send("I couldn't connect! Sorry no snakes right now ;w;") return @@ -85,7 +85,7 @@ class Images: EXAMPLE: !horse RESULT: A beautiful picture of a horse o3o""" - result = await utils.requrest("http://hrsendl.com/horse") + result = await utils.request("http://hrsendl.com/horse") if result is None: await ctx.send("I couldn't connect! Sorry no horses right now ;w;") return From 11b63663608e00a06a2d4b8af6f640df04c3c505 Mon Sep 17 00:00:00 2001 From: Phxntxm Date: Thu, 11 Jan 2018 10:56:11 -0600 Subject: [PATCH 03/16] Add a bash command --- cogs/owner.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cogs/owner.py b/cogs/owner.py index 212466c..f061906 100644 --- a/cogs/owner.py +++ b/cogs/owner.py @@ -11,6 +11,7 @@ import inspect import pendulum import textwrap import traceback +import subprocess from contextlib import redirect_stdout import io @@ -248,6 +249,17 @@ class Owner: except discord.HTTPException: await ctx.send("Content too large for me to print!") + @commands.command() + @commands.check(utils.is_owner) + async def bash(self, ctx, *, cmd: str): + """Runs a bash command""" + proc = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) + output, error = proc.communicate() + if output: + await ctx.send("STDOUT:\n```\n{}```".format(output.decode("utf-8", "ignore"))) + if error: + await ctx.send("STDERR:\n```\n{}```".format(error.decode("utf-8", "ignore"))) + @commands.command() @commands.check(utils.is_owner) async def shutdown(self, ctx): From 0d3ec6f46f76e02e6d67d3fc568622872b7b036a Mon Sep 17 00:00:00 2001 From: Phxntxm Date: Thu, 11 Jan 2018 11:07:42 -0600 Subject: [PATCH 04/16] Use a shell in the bash command --- cogs/owner.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cogs/owner.py b/cogs/owner.py index f061906..e561604 100644 --- a/cogs/owner.py +++ b/cogs/owner.py @@ -253,12 +253,12 @@ class Owner: @commands.check(utils.is_owner) async def bash(self, ctx, *, cmd: str): """Runs a bash command""" - proc = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE) + proc = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) output, error = proc.communicate() if output: - await ctx.send("STDOUT:\n```\n{}```".format(output.decode("utf-8", "ignore"))) + await ctx.send("STDOUT:\n```\n{}```".format(output.decode("utf-8", "ignore").strip())) if error: - await ctx.send("STDERR:\n```\n{}```".format(error.decode("utf-8", "ignore"))) + await ctx.send("STDERR:\n```\n{}```".format(error.decode("utf-8", "ignore").strip())) @commands.command() @commands.check(utils.is_owner) From 936040a6c88f279dae16d4a91e7ad43d890432e8 Mon Sep 17 00:00:00 2001 From: Phxntxm Date: Thu, 11 Jan 2018 11:32:41 -0600 Subject: [PATCH 05/16] Use check_output to stop blocking --- cogs/owner.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/cogs/owner.py b/cogs/owner.py index e561604..5a89603 100644 --- a/cogs/owner.py +++ b/cogs/owner.py @@ -253,12 +253,9 @@ class Owner: @commands.check(utils.is_owner) async def bash(self, ctx, *, cmd: str): """Runs a bash command""" - proc = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) - output, error = proc.communicate() + output = subprocess.check_output("{}; exit 0".format(cmd), stderr=subprocess.STDOUT, shell=True) if output: - await ctx.send("STDOUT:\n```\n{}```".format(output.decode("utf-8", "ignore").strip())) - if error: - await ctx.send("STDERR:\n```\n{}```".format(error.decode("utf-8", "ignore").strip())) + await ctx.send("\n```\n{}```".format(output.decode("utf-8", "ignore").strip())) @commands.command() @commands.check(utils.is_owner) From 37270d6b6c5e8ade394a205d08b5d079bbae547d Mon Sep 17 00:00:00 2001 From: Phxntxm Date: Fri, 12 Jan 2018 15:41:12 -0600 Subject: [PATCH 06/16] Add a check if no output is found --- cogs/owner.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cogs/owner.py b/cogs/owner.py index 5a89603..e284b2a 100644 --- a/cogs/owner.py +++ b/cogs/owner.py @@ -255,7 +255,9 @@ class Owner: """Runs a bash command""" output = subprocess.check_output("{}; exit 0".format(cmd), stderr=subprocess.STDOUT, shell=True) if output: - await ctx.send("\n```\n{}```".format(output.decode("utf-8", "ignore").strip())) + await ctx.send("```\n{}\n```".format(output.decode("utf-8", "ignore").strip())) + else: + await ctx.send("No output for `{}`".format(cmd)) @commands.command() @commands.check(utils.is_owner) From 6858a22a11fc7f29db9eefe48a2e59ac07de71b4 Mon Sep 17 00:00:00 2001 From: Phxntxm Date: Fri, 12 Jan 2018 15:41:33 -0600 Subject: [PATCH 07/16] Add a tutorial command --- cogs/tutorial.py | 101 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 cogs/tutorial.py diff --git a/cogs/tutorial.py b/cogs/tutorial.py new file mode 100644 index 0000000..e1672b9 --- /dev/null +++ b/cogs/tutorial.py @@ -0,0 +1,101 @@ +from discord.ext import commands + +from . import utils + +class Tutorial: + + def __init__(self, bot): + self.bot = bot + + @commands.command() + @commands.check(utils.is_owner) + # @utils.custom_perms(send_messages=True) + async def tutorial(self, ctx, *, cmd_or_cog = None): + # The message we'll use to send + output = "" + + # The list of commands we need to run through + commands = [] + if cmd_or_cog: + cmd = self.bot.get_command(cmd_or_cog.lower()) + # This should be a cog + if cmd is None: + cog = self.bot.get_cog(cmd_or_cog.title()) + if cog is None: + await ctx.send("Could not find a command or a cog for {}".format(cmd_or_cog)) + return + + commands = [c for c in utils.get_all_commands(self.bot) if c.cog_name == cmd_or_cog.title()] + # Specific command + else: + commands = [cmd] + # Use all commands + else: + commands = list(utils.get_all_commands(self.bot)) + + # Loop through all the commands that we want to use + for command in commands: + embed = self.generate_embed(command) + await ctx.author.send(embed) + return + + async def generate_embed(self, command): + # Create the embed object + opts = { + "title": "Here is the tutorial for the command {}:\n\n".format(command.qualified_name), + "colour": discord.Colour.green() + } + embed = discord.Embed(**opts) + + if command.help is not None: + # Split into examples, results, and the description itself based on the string + description, _, rest = command.help.partition('EXAMPLE:') + example, _, rest = rest.partition('RESULT:') + result, _, gif = rest.partition("GIF:") + else: + example = None + result = None + gif = None + + # Add a field for the aliases + embed.add_field( + name="Aliases", + value="\n".join(["\t{}".format(alias) for alias in command.aliases]), + inline=False + ) + # Add any paramaters needed + if command.clean_params: + required_params = [x for x in command.clean_params if "=" not in x] + optional_params = [x for x in command.clean_params if "=" in x] + name = "Paramaters" + value = "" + # Add the required params + if required_params: + value += "Requried:\n{}\n\n".format("\n".join(required_params)) + # Add the optional params + if optional_params: + value += "Optional:\n{}".format("\n".join(optional_params)) + embed.add_field(name=name, value=value, inline=False) + # Set the description of the embed to the description + if description: + embed.description = description + # Add these two in one embed + if example and result: + embed.add_field( + name="Example", + value="{}\n{}".format(example, result), + inline=False + ) + try: + custom_perms = [func for func in command.checks if "custom_perms" in func.__qualname__][0] + perms = ",".join(attribute for attribute, setting in custom_perms.perms.items() if setting) + embed.set_footer(text="Permissions required: {}".format(perms)) + except IndexError: + pass + + return embed + + + +def setup(bot): + bot.add_cog(Tutorial(bot)) \ No newline at end of file From 8877f6f2be71781208684d4685d5d9440b80d376 Mon Sep 17 00:00:00 2001 From: Phxntxm Date: Fri, 12 Jan 2018 15:43:09 -0600 Subject: [PATCH 08/16] No need for method to be async --- cogs/tutorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/tutorial.py b/cogs/tutorial.py index e1672b9..f0e19ce 100644 --- a/cogs/tutorial.py +++ b/cogs/tutorial.py @@ -39,7 +39,7 @@ class Tutorial: await ctx.author.send(embed) return - async def generate_embed(self, command): + def generate_embed(self, command): # Create the embed object opts = { "title": "Here is the tutorial for the command {}:\n\n".format(command.qualified_name), From 58539d85fadb0b7b3eb5e5ac95afa0edf9597ad8 Mon Sep 17 00:00:00 2001 From: Phxntxm Date: Fri, 12 Jan 2018 15:45:44 -0600 Subject: [PATCH 09/16] Import Discord --- cogs/tutorial.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cogs/tutorial.py b/cogs/tutorial.py index f0e19ce..4aaa0a3 100644 --- a/cogs/tutorial.py +++ b/cogs/tutorial.py @@ -2,6 +2,8 @@ from discord.ext import commands from . import utils +import discord + class Tutorial: def __init__(self, bot): From f942852813f813c9c67d6a71290806bcb8a8e522 Mon Sep 17 00:00:00 2001 From: Phxntxm Date: Fri, 12 Jan 2018 15:48:19 -0600 Subject: [PATCH 10/16] Send as an actual embed object --- cogs/tutorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/tutorial.py b/cogs/tutorial.py index 4aaa0a3..75d3e05 100644 --- a/cogs/tutorial.py +++ b/cogs/tutorial.py @@ -38,7 +38,7 @@ class Tutorial: # Loop through all the commands that we want to use for command in commands: embed = self.generate_embed(command) - await ctx.author.send(embed) + await ctx.author.send(embed=embed) return def generate_embed(self, command): From 536853b00e96d2975f38dedc0f21435581c60ff8 Mon Sep 17 00:00:00 2001 From: Phxntxm Date: Fri, 12 Jan 2018 15:50:06 -0600 Subject: [PATCH 11/16] Ensure there are aliases before adding them --- cogs/tutorial.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cogs/tutorial.py b/cogs/tutorial.py index 75d3e05..39d2c71 100644 --- a/cogs/tutorial.py +++ b/cogs/tutorial.py @@ -60,11 +60,12 @@ class Tutorial: gif = None # Add a field for the aliases - embed.add_field( - name="Aliases", - value="\n".join(["\t{}".format(alias) for alias in command.aliases]), - inline=False - ) + if command.aliases: + embed.add_field( + name="Aliases", + value="\n".join(["\t{}".format(alias) for alias in command.aliases]), + inline=False + ) # Add any paramaters needed if command.clean_params: required_params = [x for x in command.clean_params if "=" not in x] From 122461b6e0f86b710fc703f8e20f2e07f0aa64ba Mon Sep 17 00:00:00 2001 From: Phxntxm Date: Fri, 12 Jan 2018 16:14:23 -0600 Subject: [PATCH 12/16] Corect logic for showing parameters --- cogs/tutorial.py | 44 ++++++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/cogs/tutorial.py b/cogs/tutorial.py index 39d2c71..c4c44f8 100644 --- a/cogs/tutorial.py +++ b/cogs/tutorial.py @@ -38,13 +38,14 @@ class Tutorial: # Loop through all the commands that we want to use for command in commands: embed = self.generate_embed(command) - await ctx.author.send(embed=embed) + # await ctx.author.send(embed=embed) + await ctx.send(embed=embed) return def generate_embed(self, command): # Create the embed object opts = { - "title": "Here is the tutorial for the command {}:\n\n".format(command.qualified_name), + "title": "`{}` command tutorial:\n\n".format(command.qualified_name), "colour": discord.Colour.green() } embed = discord.Embed(**opts) @@ -68,17 +69,36 @@ class Tutorial: ) # Add any paramaters needed if command.clean_params: - required_params = [x for x in command.clean_params if "=" not in x] - optional_params = [x for x in command.clean_params if "=" in x] + params = [] + for key, value in command.clean_params.items(): + # Get the parameter type, as well as the default value if it exists + param_type, has_default, default_value = value.partition("=") + try: + # We want everything after the : + param_type = param_type.split(":")[1] + # Now we want to split based on . (for possible deep level types IE discord.member.Member) then get the last value + param_type = param_type.split(".") + param_type = param_type[len(param_type) - 1] + # This could mean something like *param was provided as the parameter + except IndexError: + param_type = "str" + + # Start the string that we'll use as the param's info + string = "{} (Type: {}".format(key, param_type) + + if default_value: + string += ", Default: {}".format(default_value) + + # This is the = from the partition, if it exists, then there's a default...hence the name + if has_default: + string += "optional" + else: + string += "required" + + # Now push our string to the list of params + params.append(string) name = "Paramaters" - value = "" - # Add the required params - if required_params: - value += "Requried:\n{}\n\n".format("\n".join(required_params)) - # Add the optional params - if optional_params: - value += "Optional:\n{}".format("\n".join(optional_params)) - embed.add_field(name=name, value=value, inline=False) + embed.add_field(name=name, value="\n".join(params), inline=False) # Set the description of the embed to the description if description: embed.description = description From 86f9984998cb5b8d223d2c60768e4e29f33271c3 Mon Sep 17 00:00:00 2001 From: Phxntxm Date: Fri, 12 Jan 2018 16:15:21 -0600 Subject: [PATCH 13/16] Convert to string --- cogs/tutorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/tutorial.py b/cogs/tutorial.py index c4c44f8..dd672ba 100644 --- a/cogs/tutorial.py +++ b/cogs/tutorial.py @@ -72,7 +72,7 @@ class Tutorial: params = [] for key, value in command.clean_params.items(): # Get the parameter type, as well as the default value if it exists - param_type, has_default, default_value = value.partition("=") + param_type, has_default, default_value = str(value).partition("=") try: # We want everything after the : param_type = param_type.split(":")[1] From f36c340f110a821ecc78c6d0122e215736445592 Mon Sep 17 00:00:00 2001 From: Phxntxm Date: Fri, 12 Jan 2018 16:16:53 -0600 Subject: [PATCH 14/16] Correct spacing issue --- cogs/tutorial.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cogs/tutorial.py b/cogs/tutorial.py index dd672ba..fab8bf5 100644 --- a/cogs/tutorial.py +++ b/cogs/tutorial.py @@ -91,9 +91,9 @@ class Tutorial: # This is the = from the partition, if it exists, then there's a default...hence the name if has_default: - string += "optional" + string += ", optional)" else: - string += "required" + string += ", required)" # Now push our string to the list of params params.append(string) From d66bee2e382d16e1e332aba111e37b2ac53f41d5 Mon Sep 17 00:00:00 2001 From: Phxntxm Date: Fri, 12 Jan 2018 16:18:03 -0600 Subject: [PATCH 15/16] Correct spacing issue --- cogs/tutorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/tutorial.py b/cogs/tutorial.py index fab8bf5..171ae7e 100644 --- a/cogs/tutorial.py +++ b/cogs/tutorial.py @@ -106,7 +106,7 @@ class Tutorial: if example and result: embed.add_field( name="Example", - value="{}\n{}".format(example, result), + value="{}\n{}".format(example.strip(), result.strip()), inline=False ) try: From d15397dad95a24780df08c297b4ceb7e93e8ae65 Mon Sep 17 00:00:00 2001 From: MahxieNoodle Date: Thu, 22 Mar 2018 10:21:12 -0600 Subject: [PATCH 16/16] New cat api endpoint Updated cat api url. --- cogs/images.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cogs/images.py b/cogs/images.py index 5168945..73c32d8 100644 --- a/cogs/images.py +++ b/cogs/images.py @@ -21,7 +21,7 @@ class Images: EXAMPLE: !cat RESULT: A beautiful picture of a cat o3o""" - result = await utils.request('http://random.cat/meow') + result = await utils.request('http://aws.random.cat/meow') if result is None: await ctx.send("I couldn't connect! Sorry no cats right now ;w;") return