From c2505cefd1b3db8db24e60f447cb40e071ebe9b0 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Thu, 22 Mar 2018 21:32:04 -0600 Subject: [PATCH 01/44] add preliminary snake 'get' command --- bot/cogs/snakes.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index c9ed8042..adccbf2b 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -1,7 +1,12 @@ # coding=utf-8 +import os import logging from typing import Any, Dict +import aiohttp +import json +import random +import discord from discord.ext.commands import AutoShardedBot, Context, command log = logging.getLogger(__name__) @@ -29,6 +34,29 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: :return: A dict containing information on a snake """ + async def get_snek_qwant_json(self, snake_name): + """ + Gets the json from Unsplash for a given snake query + :param snake_name: name of the snake + :return: the full JSON from the search API + """ + url = "https://api.unsplash.com/search/photos?client_id" \ + "={}&query={}".format(os.environ.get("UNSPLASH_CLIENT_ID"), snake_name) + async with aiohttp.ClientSession() as session: + async with session.get(url) as response: + response = await response.read() + return json.loads(response.decode("utf-8")) + + async def get_snek_image(self, name): + """ + Gets the URL of a snake image + :param name: name of snake + :return: image url + """ + json_response = await self.get_snek_qwant_json(name) + rand = random.randint(0, 9) # prevents returning the same image every time + return json_response['results'][rand]['urls']['small'] + @command() async def get(self, ctx: Context, name: str = None): """ @@ -40,6 +68,11 @@ async def get(self, ctx: Context, name: str = None): :param ctx: Context object passed from discord.py :param name: Optional, the name of the snake to get information for - omit for a random snake """ + url = await self.get_snek_image("python") # TODO: accept user input for the snake type + await ctx.channel.send( + content=ctx.message.author.mention + " Here's your snek!", + embed=discord.Embed().set_image(url=url) + ) # Any additional commands can be placed here. Be creative, but keep it to a reasonable amount! From 8c25449b9fd41bd01692b3c18c919f54b633992c Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Thu, 22 Mar 2018 21:43:45 -0600 Subject: [PATCH 02/44] allow user input for snake names --- bot/cogs/snakes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index adccbf2b..93c5f811 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -58,7 +58,7 @@ async def get_snek_image(self, name): return json_response['results'][rand]['urls']['small'] @command() - async def get(self, ctx: Context, name: str = None): + async def get(self, ctx: Context, name: str = "python"): """ Go online and fetch information about a snake @@ -68,7 +68,7 @@ async def get(self, ctx: Context, name: str = None): :param ctx: Context object passed from discord.py :param name: Optional, the name of the snake to get information for - omit for a random snake """ - url = await self.get_snek_image("python") # TODO: accept user input for the snake type + url = await self.get_snek_image(name) # not limited to snakes - user can search anything they like await ctx.channel.send( content=ctx.message.author.mention + " Here's your snek!", embed=discord.Embed().set_image(url=url) From e3f0c70c8053e957bafbdfc7fba1e18d636b08db Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Thu, 22 Mar 2018 22:43:28 -0600 Subject: [PATCH 03/44] change imports order, index format string --- bot/cogs/snakes.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 93c5f811..a4f814b5 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -1,11 +1,12 @@ # coding=utf-8 -import os +import json import logging +import os +import random from typing import Any, Dict import aiohttp -import json -import random + import discord from discord.ext.commands import AutoShardedBot, Context, command @@ -41,7 +42,7 @@ async def get_snek_qwant_json(self, snake_name): :return: the full JSON from the search API """ url = "https://api.unsplash.com/search/photos?client_id" \ - "={}&query={}".format(os.environ.get("UNSPLASH_CLIENT_ID"), snake_name) + "={0}&query={1}".format(os.environ.get("UNSPLASH_CLIENT_ID"), snake_name) async with aiohttp.ClientSession() as session: async with session.get(url) as response: response = await response.read() From 611501f0dd810533285d62ab750652e87e49373c Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Fri, 23 Mar 2018 15:53:46 -0600 Subject: [PATCH 04/44] add interim fancy message formatting, handle special python case --- bot/cogs/snakes.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index a4f814b5..18df8cf5 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -59,7 +59,7 @@ async def get_snek_image(self, name): return json_response['results'][rand]['urls']['small'] @command() - async def get(self, ctx: Context, name: str = "python"): + async def get(self, ctx: Context, name: str = None): """ Go online and fetch information about a snake @@ -69,10 +69,27 @@ async def get(self, ctx: Context, name: str = "python"): :param ctx: Context object passed from discord.py :param name: Optional, the name of the snake to get information for - omit for a random snake """ - url = await self.get_snek_image(name) # not limited to snakes - user can search anything they like + embed = discord.Embed(color=0x3E885B) + if name == "python": + # handle Python special case + embed.add_field( + name="Python (programming language)", + value="*Guido van Rossum*\n\n" + "This language is neither dangerous nor venomous and be found in software globally", + inline=False + ) + embed.set_image(url=await self.get_snek_image("python programming language")) + else: + url = await self.get_snek_image(name) # not limited to snakes - user can search anything they like + embed.add_field( + name="common name", + value="*sci name*\n\nThis snake is (**not**) venomous and can be found in (...)", + inline=False + ) + embed.set_image(url=url) await ctx.channel.send( content=ctx.message.author.mention + " Here's your snek!", - embed=discord.Embed().set_image(url=url) + embed=embed ) # Any additional commands can be placed here. Be creative, but keep it to a reasonable amount! From 85bb282278845abfeb3acf544b88a995443c3c8e Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Fri, 23 Mar 2018 15:58:12 -0600 Subject: [PATCH 05/44] add snake to query string for images --- bot/cogs/snakes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 18df8cf5..867ff79f 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -42,7 +42,7 @@ async def get_snek_qwant_json(self, snake_name): :return: the full JSON from the search API """ url = "https://api.unsplash.com/search/photos?client_id" \ - "={0}&query={1}".format(os.environ.get("UNSPLASH_CLIENT_ID"), snake_name) + "={0}&query={1}+snake".format(os.environ.get("UNSPLASH_CLIENT_ID"), snake_name) async with aiohttp.ClientSession() as session: async with session.get(url) as response: response = await response.read() From 243b51f25f7477aa7dc450e4c6eb90202a08fd51 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Fri, 23 Mar 2018 20:10:34 -0600 Subject: [PATCH 06/44] add list of snake facts, sources files --- bot/cogs/resources/facts.json | 36 +++++++++++++++++++++++++++++++++++ sources.md | 1 + 2 files changed, 37 insertions(+) create mode 100644 bot/cogs/resources/facts.json create mode 100644 sources.md diff --git a/bot/cogs/resources/facts.json b/bot/cogs/resources/facts.json new file mode 100644 index 00000000..cd1c28ba --- /dev/null +++ b/bot/cogs/resources/facts.json @@ -0,0 +1,36 @@ +{ + "facts": [ + "Snakes are carnivores (meat eaters).", + "Snakes don’t have eyelids.", + "Snakes can’t bite food so have to swallow it whole.", + "Snakes have flexible jaws which allow them to eat prey bigger than their head!", + "Snakes are found on every continent of the world except Antarctica.", + "Snakes have internal ears but not external ones.", + "Snakes used in snake charming performances respond to movement, not sound.", + "There are around 3000 different species of snake.", + "Snakes have a unique anatomy which allows them to swallow and digest large prey.", + "Snakes are covered in scales.", + "Snakeskin is smooth and dry.", + "Snakes shed their skin a number of times a year in a process that usually lasts a few days.", + "Snakes smell with their tongue.", + "Pythons kill their prey by tightly wrapping around it and suffocating it in a process called constriction.", + "Some sea snakes can breathe partially through their skin, allowing for longer dives underwater.", + "Some animals, such as the Mongoose, are immune to snake venom.", + "While snakes do not have external ears or eardrums, their skin, muscles, and bones carry sound vibrations to their inner ears.", + "Rattlesnake rattles are made of rings of keratin, which is the same material as human hair and fingernails. A rattler will add a new ring each time it sheds its skin.", + "Some snakes have over 200 teeth. The teeth aren’t used for chewing but they point backward to prevent prey from escaping the snake’s throat.", + "There are five recognized species of flying snakes. Growing up to 4 feet, some types can glide up to 330 feet through the air.", + "Snakes do not lap up water like mammals do. Instead, they dunk their snouts underwater and use their throats to pump water into their stomachs.", + "A snake’s fangs usually last about 6–10 weeks. When a fang wears out, a new one grows in its place.", + "Snakes typically need to eat only 6–30 meals each year to be healthy.", + "If the temperature reaches below 50° Fahrenheit, a snake’s body does not work properly.", + "The Gaboon viper has the longest fangs of any snake, reaching about 2 inches (5 cm) long.", + "The longest snake ever recorded is the reticulated python. It can reach over 33 feet long, which is big enough to swallow a pig, a deer, or even a person.", + "Some venomous snakes have died after biting and poisoning themselves by mistake.", + "While a snake cannot hear the music of a snake charmer, the snake responds to the vibrations of the charmer’s tapping foot or to the movement of the flute.", + "Most snakes are not harmful to humans and they help balance the ecosystem by keeping the population of rats, mice, and birds under control.", + "Most snakes have an elongated right lung, many have a smaller left lung, and a few even have a third lung. They do not have a sense of taste, and most of their organs are organized linearly.", + "Snakes kill over 40,000 people a year—though, with unreported incidents, the total may be over 100,000. About half of these deaths are in India.", + "Some members of the U.S. Army Special Forces are taught to kill and eat snakes during their survival training, which has earned them the nickname “Snake Eaters.”" + ] +} \ No newline at end of file diff --git a/sources.md b/sources.md new file mode 100644 index 00000000..e929117d --- /dev/null +++ b/sources.md @@ -0,0 +1 @@ +Snake Information From b4e6468b40894620bd9d609c3fe0d073c1e9304b Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Fri, 23 Mar 2018 20:40:39 -0600 Subject: [PATCH 07/44] update sources --- sources.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/sources.md b/sources.md index e929117d..bdd82e05 100644 --- a/sources.md +++ b/sources.md @@ -1 +1,11 @@ -Snake Information +# Sources / Credits + +## Snake DB Information +Data about various snakes, stored in our database +* [Snake Type](http://www.snaketype.com) +* uh another one + +## Snake Fun Facts +Random facts about snakes +* [Science Kids](http://www.sciencekids.co.nz/sciencefacts/animals/snake.html) +* [Fact Retriever](https://www.factretriever.com/snake-facts) From cea4cf37ee10f8e4d915dbd7cdefade0676beeb8 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Fri, 23 Mar 2018 20:41:01 -0600 Subject: [PATCH 08/44] add snake fact command --- bot/cogs/snakes.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 867ff79f..c08bc192 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -58,7 +58,7 @@ async def get_snek_image(self, name): rand = random.randint(0, 9) # prevents returning the same image every time return json_response['results'][rand]['urls']['small'] - @command() + @command(aliases=["g"]) async def get(self, ctx: Context, name: str = None): """ Go online and fetch information about a snake @@ -94,6 +94,29 @@ async def get(self, ctx: Context, name: str = None): # Any additional commands can be placed here. Be creative, but keep it to a reasonable amount! + @command(aliases=["f"]) + async def fact(self, ctx: Context): + """ + Gets a random fact about snakes + :param ctx: Context object passed from discord.py + """ + em = discord.Embed(color=0x399600) + em.add_field( + name="Snake Fact", + value=self.get_snek_fact(), + inline=False + ) + await ctx.channel.send( + content=ctx.message.author.mention, + embed=em + ) + + def get_snek_fact(self): + with open('bot/cogs/resources/facts.json', 'r', encoding="utf8") as f: + data = json.load(f) + choice = random.randint(0, len(data['facts'])-1) + return data['facts'][choice] + def setup(bot): bot.add_cog(Snakes(bot)) From 3a372522a23b4aac5330ccd9dd71917841140a90 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Fri, 23 Mar 2018 20:56:18 -0600 Subject: [PATCH 09/44] clean up code - type hinting, spelling, URL formatting, etc. (thanks @1mn) --- bot/cogs/snakes.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index c08bc192..7e734109 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -35,20 +35,22 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: :return: A dict containing information on a snake """ - async def get_snek_qwant_json(self, snake_name): + async def get_snek_qwant_json(self, snake_name: str) -> str: """ Gets the json from Unsplash for a given snake query :param snake_name: name of the snake :return: the full JSON from the search API """ - url = "https://api.unsplash.com/search/photos?client_id" \ - "={0}&query={1}+snake".format(os.environ.get("UNSPLASH_CLIENT_ID"), snake_name) + client_id = os.environ.get("UNSPLASH_CLIENT_ID") + url = ( + "https://api.unsplash.com/search/photos?client_id={client_id}&query={snake_name}+snake" + ) async with aiohttp.ClientSession() as session: async with session.get(url) as response: response = await response.read() return json.loads(response.decode("utf-8")) - async def get_snek_image(self, name): + async def get_snek_image(self, name: str) -> str: """ Gets the URL of a snake image :param name: name of snake @@ -75,7 +77,7 @@ async def get(self, ctx: Context, name: str = None): embed.add_field( name="Python (programming language)", value="*Guido van Rossum*\n\n" - "This language is neither dangerous nor venomous and be found in software globally", + "This language is neither dangerous nor venomous and can be found in software globally", inline=False ) embed.set_image(url=await self.get_snek_image("python programming language")) @@ -111,7 +113,7 @@ async def fact(self, ctx: Context): embed=em ) - def get_snek_fact(self): + def get_snek_fact(self) -> str: with open('bot/cogs/resources/facts.json', 'r', encoding="utf8") as f: data = json.load(f) choice = random.randint(0, len(data['facts'])-1) From 6602d02dc7f03bc4895848187d8698114fa60949 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Fri, 23 Mar 2018 21:01:36 -0600 Subject: [PATCH 10/44] fix broken URL --- bot/cogs/snakes.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 7e734109..a439dab5 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -43,7 +43,8 @@ async def get_snek_qwant_json(self, snake_name: str) -> str: """ client_id = os.environ.get("UNSPLASH_CLIENT_ID") url = ( - "https://api.unsplash.com/search/photos?client_id={client_id}&query={snake_name}+snake" + "https://api.unsplash.com/search/photos?client_id" + f"={client_id}&query={snake_name}+snake" ) async with aiohttp.ClientSession() as session: async with session.get(url) as response: From 62401e5e0c6368ddccb64b504c96800a9a196714 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Fri, 23 Mar 2018 21:07:34 -0600 Subject: [PATCH 11/44] resolve PEP E226 --- bot/cogs/snakes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index a439dab5..a29505fa 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -117,7 +117,7 @@ async def fact(self, ctx: Context): def get_snek_fact(self) -> str: with open('bot/cogs/resources/facts.json', 'r', encoding="utf8") as f: data = json.load(f) - choice = random.randint(0, len(data['facts'])-1) + choice = random.randint(0, len(data['facts']) - 1) return data['facts'][choice] From 7f63f4f40a509b884bc43424d34640a60ce2991b Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Fri, 23 Mar 2018 21:13:10 -0600 Subject: [PATCH 12/44] implement more of @1mn's suggestions --- bot/cogs/snakes.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index a29505fa..c5ab1338 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -73,12 +73,14 @@ async def get(self, ctx: Context, name: str = None): :param name: Optional, the name of the snake to get information for - omit for a random snake """ embed = discord.Embed(color=0x3E885B) - if name == "python": + if name and name.lower() == "python": # handle Python special case embed.add_field( name="Python (programming language)", - value="*Guido van Rossum*\n\n" - "This language is neither dangerous nor venomous and can be found in software globally", + value=( + "*Guido van Rossum*\n\n" + "This language is neither dangerous nor venomous and can be found in software globally" + ), inline=False ) embed.set_image(url=await self.get_snek_image("python programming language")) From 013705135e9cb333073be74a4a628c8f8f5ed356 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Fri, 23 Mar 2018 21:37:02 -0600 Subject: [PATCH 13/44] use the snake emoji --- bot/cogs/snakes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index c5ab1338..ece42675 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -93,7 +93,7 @@ async def get(self, ctx: Context, name: str = None): ) embed.set_image(url=url) await ctx.channel.send( - content=ctx.message.author.mention + " Here's your snek!", + content=ctx.message.author.mention + " :snake: !", embed=embed ) From 1e710db0ea0889060a423c7b6ca981265366350c Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Fri, 23 Mar 2018 23:20:00 -0600 Subject: [PATCH 14/44] integrate with the API - thanks @prithajnath - remove message from response, just return the embed --- bot/cogs/snakes.py | 83 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 22 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index ece42675..804065aa 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -21,20 +21,6 @@ class Snakes: def __init__(self, bot: AutoShardedBot): self.bot = bot - async def get_snek(self, name: str = None) -> Dict[str, Any]: - """ - Go online and fetch information about a snake - - The information includes the name of the snake, a picture of the snake, and various other pieces of info. - What information you get for the snake is up to you. Be creative! - - If "python" is given as the snake name, you should return information about the programming language, but with - all the information you'd provide for a real snake. Try to have some fun with this! - - :param name: Optional, the name of the snake to get information for - omit for a random snake - :return: A dict containing information on a snake - """ - async def get_snek_qwant_json(self, snake_name: str) -> str: """ Gets the json from Unsplash for a given snake query @@ -61,6 +47,42 @@ async def get_snek_image(self, name: str) -> str: rand = random.randint(0, 9) # prevents returning the same image every time return json_response['results'][rand]['urls']['small'] + async def get_snek(self, name: str = None) -> Dict[str, Any]: + """ + Go online and fetch information about a snake + + The information includes the name of the snake, a picture of the snake, and various other pieces of info. + What information you get for the snake is up to you. Be creative! + + If "python" is given as the snake name, you should return information about the programming language, but with + all the information you'd provide for a real snake. Try to have some fun with this! + + :param name: Optional, the name of the snake to get information for - omit for a random snake + :return: A dict containing information on a snake + """ + all_snakes_url = 'https://protected-reef-75100.herokuapp.com/get_all_snakes?format=json' + search_url = '' # INCOMPLETE - NEED SEARCHABLE ENDPOINT + if not name: + # get a random snake... + async with aiohttp.ClientSession() as session: + async with session.get(all_snakes_url) as response: + response = await response.read() + data = json.loads(response.decode("utf-8")) + rand = random.randint(0, len(data) - 1) + snake_info = data[rand] + else: + # todo: get snake info from API + # todo: make api endpoint to search for snakes? + async with aiohttp.ClientSession() as session: + async with session.get(search_url) as response: + # search snake endpoint something... + # response = await response.read() + # data = json.loads(response.decode("utf-8")) + # rand = random.randint(0, len(data) - 1) + pass + snake_info['image_url'] = await self.get_snek_image(snake_info['common_name']) + return snake_info + @command(aliases=["g"]) async def get(self, ctx: Context, name: str = None): """ @@ -85,15 +107,32 @@ async def get(self, ctx: Context, name: str = None): ) embed.set_image(url=await self.get_snek_image("python programming language")) else: - url = await self.get_snek_image(name) # not limited to snakes - user can search anything they like - embed.add_field( - name="common name", - value="*sci name*\n\nThis snake is (**not**) venomous and can be found in (...)", - inline=False - ) - embed.set_image(url=url) + snek_info = await self.get_snek(name) + if snek_info['is_venomous']: + # if the snake is venomous -- use the fancy check icon + embed.add_field( + name=snek_info['common_name'], + value=( + f":microscope: *{snek_info['scientific_name']}*\n\n" + f":white_check_mark: venomous\n\n" + f":globe_with_meridians: Found in {snek_info['locations']}" + ), + inline=False + ) + else: + # if the snake is not venomous -- use the fancy not allowed icon + embed.add_field( + name=snek_info['common_name'], + value=( + f":microscope: *{snek_info['scientific_name']}*\n\n" + f":no_entry_sign: NOT venomous\n\n" + f":globe_with_meridians: Found in {snek_info['locations']}" + ), + inline=False + ) + embed.set_image(url=snek_info['image_url']) await ctx.channel.send( - content=ctx.message.author.mention + " :snake: !", + # content=ctx.message.author.mention + " :snake: !", embed=embed ) From 3656c01c1f3503f0dc5d0de945c7be274718b37c Mon Sep 17 00:00:00 2001 From: prithajnath Date: Sat, 24 Mar 2018 14:52:57 -0400 Subject: [PATCH 15/44] Add search endpoint and auth headers --- bot/cogs/snakes.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 804065aa..210d5b08 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -61,11 +61,13 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: :return: A dict containing information on a snake """ all_snakes_url = 'https://protected-reef-75100.herokuapp.com/get_all_snakes?format=json' - search_url = '' # INCOMPLETE - NEED SEARCHABLE ENDPOINT + search_url = 'https://protected-reef-75100.herokuapp.com/search' + token = os.getenv('ACCESS_TOKEN') + headers = {'Authorization':f'Token {token}'} if not name: # get a random snake... async with aiohttp.ClientSession() as session: - async with session.get(all_snakes_url) as response: + async with session.get(all_snakes_url,headers=headers) as response: response = await response.read() data = json.loads(response.decode("utf-8")) rand = random.randint(0, len(data) - 1) @@ -73,13 +75,15 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: else: # todo: get snake info from API # todo: make api endpoint to search for snakes? + params = {'snake':name} async with aiohttp.ClientSession() as session: - async with session.get(search_url) as response: + async with session.get(search_url, headers=headers, params=params) as response: # search snake endpoint something... - # response = await response.read() - # data = json.loads(response.decode("utf-8")) - # rand = random.randint(0, len(data) - 1) - pass + response = await response.read() + data = json.loads(response.decode("utf-8")) + rand = random.randint(0, len(data) - 1) + snake_info = data[rand] + snake_info['image_url'] = await self.get_snek_image(snake_info['common_name']) return snake_info From bf2f9ea514671ea6520df011fd1b919482c7e295 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sat, 24 Mar 2018 18:35:08 -0600 Subject: [PATCH 16/44] remove repetitive embed code, fix linting errors (hopefully!) --- bot/cogs/snakes.py | 55 ++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 210d5b08..20f4dc42 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -45,7 +45,7 @@ async def get_snek_image(self, name: str) -> str: """ json_response = await self.get_snek_qwant_json(name) rand = random.randint(0, 9) # prevents returning the same image every time - return json_response['results'][rand]['urls']['small'] + return str(json_response['results'][rand]['urls']['small']) async def get_snek(self, name: str = None) -> Dict[str, Any]: """ @@ -60,30 +60,30 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: :param name: Optional, the name of the snake to get information for - omit for a random snake :return: A dict containing information on a snake """ - all_snakes_url = 'https://protected-reef-75100.herokuapp.com/get_all_snakes?format=json' - search_url = 'https://protected-reef-75100.herokuapp.com/search' + base_url = "https://protected-reef-75100.herokuapp.com/{}" + all_snakes_url = base_url.format('get_all_snakes?format=json') + search_url = base_url.format('search') token = os.getenv('ACCESS_TOKEN') - headers = {'Authorization':f'Token {token}'} + headers = {'Authorization': f'Token {token}'} if not name: # get a random snake... async with aiohttp.ClientSession() as session: - async with session.get(all_snakes_url,headers=headers) as response: + async with session.get(all_snakes_url, headers=headers) as response: response = await response.read() + print(response) data = json.loads(response.decode("utf-8")) rand = random.randint(0, len(data) - 1) snake_info = data[rand] else: - # todo: get snake info from API - # todo: make api endpoint to search for snakes? - params = {'snake':name} + params = {'snake': name} async with aiohttp.ClientSession() as session: async with session.get(search_url, headers=headers, params=params) as response: # search snake endpoint something... - response = await response.read() - data = json.loads(response.decode("utf-8")) - rand = random.randint(0, len(data) - 1) - snake_info = data[rand] - + response = await response.read() + data = json.loads(response.decode("utf-8")) + rand = random.randint(0, len(data) - 1) + snake_info = data[rand] + snake_info['image_url'] = await self.get_snek_image(snake_info['common_name']) return snake_info @@ -114,26 +114,19 @@ async def get(self, ctx: Context, name: str = None): snek_info = await self.get_snek(name) if snek_info['is_venomous']: # if the snake is venomous -- use the fancy check icon - embed.add_field( - name=snek_info['common_name'], - value=( - f":microscope: *{snek_info['scientific_name']}*\n\n" - f":white_check_mark: venomous\n\n" - f":globe_with_meridians: Found in {snek_info['locations']}" - ), - inline=False - ) + venom_info = f":white_check_mark: venomous\n\n" else: # if the snake is not venomous -- use the fancy not allowed icon - embed.add_field( - name=snek_info['common_name'], - value=( - f":microscope: *{snek_info['scientific_name']}*\n\n" - f":no_entry_sign: NOT venomous\n\n" - f":globe_with_meridians: Found in {snek_info['locations']}" - ), - inline=False - ) + venom_info = f":no_entry_sign: NOT venomous\n\n" + embed.add_field( + name=snek_info['common_name'], + value=( + f":microscope: *{snek_info['scientific_name']}*\n\n" + f"{venom_info}" + f":globe_with_meridians: Found in {snek_info['locations']}" + ), + inline=False + ) embed.set_image(url=snek_info['image_url']) await ctx.channel.send( # content=ctx.message.author.mention + " :snake: !", From d8a26bad3c0af9c160b3f8d05e4f08d42dd20f50 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sat, 24 Mar 2018 18:39:20 -0600 Subject: [PATCH 17/44] resolve flake8 - P103 --- bot/cogs/snakes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 20f4dc42..1e7f1e3b 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -60,7 +60,7 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: :param name: Optional, the name of the snake to get information for - omit for a random snake :return: A dict containing information on a snake """ - base_url = "https://protected-reef-75100.herokuapp.com/{}" + base_url = "https://protected-reef-75100.herokuapp.com/{0}" all_snakes_url = base_url.format('get_all_snakes?format=json') search_url = base_url.format('search') token = os.getenv('ACCESS_TOKEN') From 9c8a9f4d24ebe137d766d0ba4c7aeeddbfbd277d Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sat, 24 Mar 2018 21:09:44 -0600 Subject: [PATCH 18/44] resolve linting issues --- bot/cogs/snakes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 1e7f1e3b..5b7a260e 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -60,9 +60,9 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: :param name: Optional, the name of the snake to get information for - omit for a random snake :return: A dict containing information on a snake """ - base_url = "https://protected-reef-75100.herokuapp.com/{0}" - all_snakes_url = base_url.format('get_all_snakes?format=json') - search_url = base_url.format('search') + base_url = "https://protected-reef-75100.herokuapp.com/" + all_snakes_url = base_url + 'get_all_snakes?format=json' + search_url = base_url + 'search' token = os.getenv('ACCESS_TOKEN') headers = {'Authorization': f'Token {token}'} if not name: From 697f557ffae3d452e97f1d0a9967bac63370c9b7 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sat, 24 Mar 2018 21:16:06 -0600 Subject: [PATCH 19/44] implement random api endpoint --- bot/cogs/snakes.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 5b7a260e..e3ed67df 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -62,18 +62,16 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: """ base_url = "https://protected-reef-75100.herokuapp.com/" all_snakes_url = base_url + 'get_all_snakes?format=json' + random_url = 'https://protected-reef-75100.herokuapp.com/random_snake' search_url = base_url + 'search' token = os.getenv('ACCESS_TOKEN') headers = {'Authorization': f'Token {token}'} if not name: # get a random snake... async with aiohttp.ClientSession() as session: - async with session.get(all_snakes_url, headers=headers) as response: + async with session.get(random_url, headers=headers) as response: response = await response.read() - print(response) - data = json.loads(response.decode("utf-8")) - rand = random.randint(0, len(data) - 1) - snake_info = data[rand] + snake_info = json.loads(response.decode("utf-8")) else: params = {'snake': name} async with aiohttp.ClientSession() as session: From 0951f92b6f4a6c547f96a3e524e2a19b171b365a Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sat, 24 Mar 2018 21:25:53 -0600 Subject: [PATCH 20/44] implement titlecase for common and scientific names, remove unused all_snakes_url var --- Pipfile | 8 ++ Pipfile.lock | 246 +++++++++++++++++++++++---------------------- bot/cogs/snakes.py | 6 +- 3 files changed, 138 insertions(+), 122 deletions(-) diff --git a/Pipfile b/Pipfile index 096fb9b3..c0ed7ad5 100644 --- a/Pipfile +++ b/Pipfile @@ -1,15 +1,21 @@ [[source]] + url = "https://pypi.python.org/simple" verify_ssl = true name = "pypi" + [packages] + "72eb2aa" = {file = "https://github.com/Rapptz/discord.py/archive/rewrite.zip"} aiodns = "*" aiohttp = "<2.3.0,>=2.0.0" websockets = ">=4.0,<5.0" +titlecase = "*" + [dev-packages] + "flake8" = "*" "flake8-bugbear" = "*" "flake8-bandit" = "*" @@ -20,5 +26,7 @@ websockets = ">=4.0,<5.0" safety = "*" dodgy = "*" + [requires] + python_version = "3.6" diff --git a/Pipfile.lock b/Pipfile.lock index 4e5214bb..683fe3e6 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,20 @@ { "_meta": { "hash": { - "sha256": "d797e580ddcddc99bf058109ab0306ad584c2902752a3d4076ba713fdc580fb7" + "sha256": "2cca231e2bd4b5fdaa06fd33b68143231524f2855847ebf4cc497462e135fdf2" + }, + "host-environment-markers": { + "implementation_name": "cpython", + "implementation_version": "3.6.1", + "os_name": "nt", + "platform_machine": "AMD64", + "platform_python_implementation": "CPython", + "platform_release": "10", + "platform_system": "Windows", + "platform_version": "10.0.16299", + "python_full_version": "3.6.1", + "python_version": "3.6", + "sys_platform": "win32" }, "pipfile-spec": 6, "requires": { @@ -24,139 +37,143 @@ "sha256:99d0652f2c02f73bfa646bf44af82705260a523014576647d7959e664830b26b", "sha256:d8677adc679ce8d0ef706c14d9c3d2f27a0e0cc11d59730cdbaf218ad52dd9ea" ], - "index": "pypi", "version": "==1.1.1" }, "aiohttp": { "hashes": [ - "sha256:129d83dd067760cec3cfd4456b5c6d7ac29f2c639d856884568fd539bed5a51f", - "sha256:33c62afd115c456b0cf1e890fe6753055effe0f31a28321efd4f787378d6f4ab", - "sha256:666756e1d4cf161ed1486b82f65fdd386ac07dd20fb10f025abf4be54be12746", "sha256:9705ded5a0faa25c8f14c6afb7044002d66c9120ed7eadb4aa9ca4aad32bd00c", - "sha256:af5bfdd164256118a0a306b3f7046e63207d1f8cba73a67dcc0bd858dcfcd3bc", + "sha256:de8ef106e130b94ca143fdfc6f27cda1d8ba439462542377738af4d99d9f5dd2", + "sha256:f0e2ac69cb709367400008cebccd5d48161dd146096a009a632a132babe5714c", + "sha256:33c62afd115c456b0cf1e890fe6753055effe0f31a28321efd4f787378d6f4ab", + "sha256:dcc7e4dcec6b0012537b9f8a0726f8b111188894ab0f924b680d40b13d3298a0", "sha256:b80f44b99fa3c9b4530fcfa324a99b84843043c35b084e0b653566049974435d", + "sha256:eb6f1405b607fff7e44168e3ceb5d3c8a8c5a2d3effe0a27f843b16ec047a6d7", + "sha256:666756e1d4cf161ed1486b82f65fdd386ac07dd20fb10f025abf4be54be12746", + "sha256:d611ebd1ef48498210b65486306e065fde031040a1f3c455ca1b6baa7bf32ad3", "sha256:c67e105ec74b85c8cb666b6877569dee6f55b9548f982983b9bee80b3d47e6f3", + "sha256:129d83dd067760cec3cfd4456b5c6d7ac29f2c639d856884568fd539bed5a51f", "sha256:d15c6658de5b7783c2538407278fa062b079a46d5f814a133ae0f09bbb2cfbc4", - "sha256:d611ebd1ef48498210b65486306e065fde031040a1f3c455ca1b6baa7bf32ad3", - "sha256:dcc7e4dcec6b0012537b9f8a0726f8b111188894ab0f924b680d40b13d3298a0", - "sha256:de8ef106e130b94ca143fdfc6f27cda1d8ba439462542377738af4d99d9f5dd2", - "sha256:eb6f1405b607fff7e44168e3ceb5d3c8a8c5a2d3effe0a27f843b16ec047a6d7", - "sha256:f0e2ac69cb709367400008cebccd5d48161dd146096a009a632a132babe5714c" + "sha256:af5bfdd164256118a0a306b3f7046e63207d1f8cba73a67dcc0bd858dcfcd3bc" ], - "index": "pypi", "version": "==2.2.5" }, "async-timeout": { "hashes": [ - "sha256:00cff4d2dce744607335cba84e9929c3165632da2d27970dbc55802a0c7873d0", - "sha256:9093db5b8ddbe4b8f6885d1a6e0ad84ae3155464cbf6877c387605244c285f3c" + "sha256:9093db5b8ddbe4b8f6885d1a6e0ad84ae3155464cbf6877c387605244c285f3c", + "sha256:00cff4d2dce744607335cba84e9929c3165632da2d27970dbc55802a0c7873d0" ], "version": "==2.0.1" }, "chardet": { "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691", + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae" ], "version": "==3.0.4" }, "idna": { "hashes": [ - "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f", - "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4" + "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4", + "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f" ], "version": "==2.6" }, "multidict": { "hashes": [ - "sha256:0462372fc74e4c061335118a4a5992b9a618d6c584b028ef03cf3e9b88a960e2", - "sha256:068e91060e3e211441b1a31f5e65de88fc346490e1fae583c35a75a5295c8ef7", "sha256:0fd4d255adcbab3341d64a2fff5acce23409e57bb94e626485dea3db70ddc35e", - "sha256:16c78b10e897a512aa34ab1969982e42246e53077ae903c1b334926e1ea832d1", + "sha256:93f1af99bbe75c854370460a60823d6726f9af2196818a64346000d02e074ed7", + "sha256:65546242d0c481c0daf0ef20c1be81c075fb763c5f4346f18f748b422fc40f32", + "sha256:0462372fc74e4c061335118a4a5992b9a618d6c584b028ef03cf3e9b88a960e2", + "sha256:63663541d395ffe4d51a3c021467d0a7b46c965b63fa1646cb46e2e2f1f36415", + "sha256:84a1cb5320f1494cd444ca3bd09ddba2e0af0cb210f9263bcf17357ab22671a1", "sha256:241c11614f64535e213ea143efa8b7e598793256601fc795e77075bdfa54f5d6", + "sha256:ea8a18ea02bf84981ec93faded773a866554666f13955c92139127892c4bb45c", + "sha256:b46ec31bb7729eaa678a3bb1c999460902df1e295fcc093b9aa5f2c7e68d5803", + "sha256:608f7eef60e6558418d7da6551dd3d07ccc1290ecc85755d781bd8100322ea5b", + "sha256:068e91060e3e211441b1a31f5e65de88fc346490e1fae583c35a75a5295c8ef7", "sha256:288e8f94fb6f586e7386c1f22c979ce3ec866ab23371fa8fef1dd526cd4dfde1", - "sha256:3508bea4974ee30fabcf7c8852fca7d9d54d496eaa068bee8311e0ac4df4ade3", "sha256:503ae54582601b0ff647731fee5efcdff5db1f4da0350febb31b628236a5f0b5", - "sha256:50de6f3786ba868ffb7d78d4bcacf0928321f9892366b2f4a0426bba644e3f25", - "sha256:608f7eef60e6558418d7da6551dd3d07ccc1290ecc85755d781bd8100322ea5b", - "sha256:63663541d395ffe4d51a3c021467d0a7b46c965b63fa1646cb46e2e2f1f36415", - "sha256:65546242d0c481c0daf0ef20c1be81c075fb763c5f4346f18f748b422fc40f32", "sha256:6d5f6f26f9025756035c473167b39c5a72e4e519a2286c9399d21f6682e4e5bc", - "sha256:84a1cb5320f1494cd444ca3bd09ddba2e0af0cb210f9263bcf17357ab22671a1", - "sha256:93f1af99bbe75c854370460a60823d6726f9af2196818a64346000d02e074ed7", - "sha256:b46ec31bb7729eaa678a3bb1c999460902df1e295fcc093b9aa5f2c7e68d5803", - "sha256:cd172509bfc9144395204dd2c0eb305ae5e89f8ad1714ffd7d793607c53c3244", - "sha256:d99819e9e15e1295a31a757360cab65bc96162870f90c29432564bd8e8999aca", - "sha256:e04b5bf8581718cf84c1c60bda40221d926ceb06f942ebabfc3baf467a1e34be", "sha256:e13265feabb1fa26f9cd49cbafd9b5de70ad768093ddb092af477c9823f44f0e", - "sha256:ea8a18ea02bf84981ec93faded773a866554666f13955c92139127892c4bb45c", + "sha256:50de6f3786ba868ffb7d78d4bcacf0928321f9892366b2f4a0426bba644e3f25", + "sha256:16c78b10e897a512aa34ab1969982e42246e53077ae903c1b334926e1ea832d1", + "sha256:e04b5bf8581718cf84c1c60bda40221d926ceb06f942ebabfc3baf467a1e34be", + "sha256:d99819e9e15e1295a31a757360cab65bc96162870f90c29432564bd8e8999aca", + "sha256:cd172509bfc9144395204dd2c0eb305ae5e89f8ad1714ffd7d793607c53c3244", + "sha256:3508bea4974ee30fabcf7c8852fca7d9d54d496eaa068bee8311e0ac4df4ade3", "sha256:fb4412490324705dcd2172baa8a3ea58ae23c5f982476805cad58ae929fe2a52" ], "version": "==4.1.0" }, "pycares": { "hashes": [ - "sha256:0e81c971236bb0767354f1456e67ab6ae305f248565ce77cd413a311f9572bf5", - "sha256:11c0ff3ccdb5a838cbd59a4e59df35d31355a80a61393bca786ca3b44569ba10", - "sha256:170d62bd300999227e64da4fa85459728cc96e62e44780bbc86a915fdae01f78", - "sha256:36f4c03df57c41a87eb3d642201684eb5a8bc194f4bafaa9f60ee6dc0aef8e40", - "sha256:371ce688776da984c4105c8ca760cc60944b9b49ccf8335c71dc7669335e6173", - "sha256:3a2234516f7db495083d8bba0ccdaabae587e62cfcd1b8154d5d0b09d3a48dfc", "sha256:3f288586592c697109b2b06e3988b7e17d9765887b5fc367010ee8500cbddc86", - "sha256:40134cee03c8bbfbc644d4c0bc81796e12dd012a5257fb146c5a5417812ee5f7", - "sha256:722f5d2c5f78d47b13b0112f6daff43ce4e08e8152319524d14f1f917cc5125e", - "sha256:7b18fab0ed534a898552df91bc804bd62bb3a2646c11e054baca14d23663e1d6", "sha256:8a39d03bd99ea191f86b990ef67ecce878d6bf6518c5cde9173fb34fb36beb5e", + "sha256:d8637bcc2f901aa61ec1d754abc862f9f145cb0346a0249360df4c159377018e", + "sha256:b95b339c11d824f0bb789d31b91c8534916fcbdce248cccce216fa2630bb8a90", "sha256:8ea263de8bf1a30b0d87150b4aa0e3203cf93bc1723ea3e7408a7d25e1299217", + "sha256:e72fa163f37ae3b09f143cc6690a36f012d13e905d142e1beed4ec0e593ff657", + "sha256:40134cee03c8bbfbc644d4c0bc81796e12dd012a5257fb146c5a5417812ee5f7", + "sha256:e2446577eeea79d2179c9469d9d4ce3ab8a07d7985465c3cb91e7d74abc329b6", + "sha256:11c0ff3ccdb5a838cbd59a4e59df35d31355a80a61393bca786ca3b44569ba10", + "sha256:7b18fab0ed534a898552df91bc804bd62bb3a2646c11e054baca14d23663e1d6", + "sha256:722f5d2c5f78d47b13b0112f6daff43ce4e08e8152319524d14f1f917cc5125e", + "sha256:bbfd9aba1e172cd2ab7b7142d49b28cf44d6451c4a66a870aff1dc3cb84849c7", + "sha256:f50be4dd53f009cfb4b98c3c6b240e18ff9b17e3f1c320bd594bb83eddabfcb2", "sha256:943e2dc67ff45ab4c81d628c959837d01561d7e185080ab7a276b8ca67573fb5", + "sha256:3a2234516f7db495083d8bba0ccdaabae587e62cfcd1b8154d5d0b09d3a48dfc", "sha256:9d56a54c93e64b30c0d31f394d9890f175edec029cd846221728f99263cdee82", - "sha256:b95b339c11d824f0bb789d31b91c8534916fcbdce248cccce216fa2630bb8a90", - "sha256:bbfd9aba1e172cd2ab7b7142d49b28cf44d6451c4a66a870aff1dc3cb84849c7", - "sha256:d8637bcc2f901aa61ec1d754abc862f9f145cb0346a0249360df4c159377018e", - "sha256:e2446577eeea79d2179c9469d9d4ce3ab8a07d7985465c3cb91e7d74abc329b6", - "sha256:e72fa163f37ae3b09f143cc6690a36f012d13e905d142e1beed4ec0e593ff657", + "sha256:371ce688776da984c4105c8ca760cc60944b9b49ccf8335c71dc7669335e6173", + "sha256:0e81c971236bb0767354f1456e67ab6ae305f248565ce77cd413a311f9572bf5", "sha256:f32b7c63094749fbc0c1106c9a785666ec8afd49ecfe7002a30bb7c42e62b47c", - "sha256:f50be4dd53f009cfb4b98c3c6b240e18ff9b17e3f1c320bd594bb83eddabfcb2" + "sha256:170d62bd300999227e64da4fa85459728cc96e62e44780bbc86a915fdae01f78", + "sha256:36f4c03df57c41a87eb3d642201684eb5a8bc194f4bafaa9f60ee6dc0aef8e40" ], "version": "==2.3.0" }, + "titlecase": { + "hashes": [ + "sha256:95d643a0c08097c02933aced707adfe1c275c335019e8e514dea782a465c5b84", + "sha256:84de7a97fb702c400e5ba11c6b30849944b39db12e20fbf4515a23c7538a0611" + ], + "version": "==0.12.0" + }, "websockets": { "hashes": [ + "sha256:f5192da704535a7cbf76d6e99c1ec4af7e8d1288252bf5a2385d414509ded0cf", "sha256:0c31bc832d529dc7583d324eb6c836a4f362032a1902723c112cf57883488d8c", - "sha256:1f3e5a52cab6daa3d432c7b0de0a14109be39d2bfaad033ee5de4a3d3e11dcdf", - "sha256:341824d8c9ad53fc43cca3fa9407f294125fa258592f7676640396501448e57e", - "sha256:367ff945bc0950ad9634591e2afe50bf2222bc4fad1088a386c4bb700888026e", - "sha256:3859ca16c229ddb0fa21c5090e4efcb037c08ce69b0c1dfed6122c3f98cd0c22", - "sha256:3d425ae081fb4ba1eef9ecf30472ffd79f8e868297ccc7a47993c96dbf2a819c", - "sha256:64896a6b3368c959b8096b655e46f03dfa65b96745249f374bd6a35705cc3489", - "sha256:6df87698022aef2596bffdfecc96d656db59c8d719708c8a471daa815ee61656", - "sha256:80188abdadd23edaaea05ce761dc9a2e1df31a74a0533967f0dcd9560c85add0", - "sha256:d1a0572b6edb22c9208e3e5381064e09d287d2a915f90233fef994ee7a14a935", - "sha256:da4d4fbe059b0453e726d6d993760065d69b823a27efc3040402a6fcfe6a1ed9", "sha256:da7610a017f5343fdf765f4e0eb6fd0dfd08264ca1565212b110836d9367fc9c", - "sha256:ebdd4f18fe7e3bea9bd3bf446b0f4117739478caa2c76e4f0fb72cc45b03cbd7", - "sha256:f5192da704535a7cbf76d6e99c1ec4af7e8d1288252bf5a2385d414509ded0cf", "sha256:fd81af8cf3e69f9a97f3a6c0623a0527de0f922c2df725f00cd7646d478af632", - "sha256:fecf51c13195c416c22422353b306dddb9c752e4b80b21e0fa1fccbe38246677" + "sha256:3d425ae081fb4ba1eef9ecf30472ffd79f8e868297ccc7a47993c96dbf2a819c", + "sha256:ebdd4f18fe7e3bea9bd3bf446b0f4117739478caa2c76e4f0fb72cc45b03cbd7", + "sha256:3859ca16c229ddb0fa21c5090e4efcb037c08ce69b0c1dfed6122c3f98cd0c22", + "sha256:d1a0572b6edb22c9208e3e5381064e09d287d2a915f90233fef994ee7a14a935", + "sha256:80188abdadd23edaaea05ce761dc9a2e1df31a74a0533967f0dcd9560c85add0", + "sha256:fecf51c13195c416c22422353b306dddb9c752e4b80b21e0fa1fccbe38246677", + "sha256:367ff945bc0950ad9634591e2afe50bf2222bc4fad1088a386c4bb700888026e", + "sha256:6df87698022aef2596bffdfecc96d656db59c8d719708c8a471daa815ee61656", + "sha256:341824d8c9ad53fc43cca3fa9407f294125fa258592f7676640396501448e57e", + "sha256:64896a6b3368c959b8096b655e46f03dfa65b96745249f374bd6a35705cc3489", + "sha256:1f3e5a52cab6daa3d432c7b0de0a14109be39d2bfaad033ee5de4a3d3e11dcdf", + "sha256:da4d4fbe059b0453e726d6d993760065d69b823a27efc3040402a6fcfe6a1ed9" ], - "index": "pypi", "version": "==4.0.1" }, "yarl": { "hashes": [ - "sha256:045dbba18c9142278113d5dc62622978a6f718ba662392d406141c59b540c514", + "sha256:d9ca55a5a297408f08e5401c23ad22bd9f580dab899212f0d5dc1830f0909404", "sha256:17e57a495efea42bcfca08b49e16c6d89e003acd54c99c903ea1cb3de0ba1248", - "sha256:213e8f54b4a942532d6ac32314c69a147d3b82fa1725ca05061b7c1a19a1d9b1", "sha256:3353fae45d93cc3e7e41bfcb1b633acc37db821d368e660b03068dbfcf68f8c8", "sha256:51a084ff8756811101f8b5031a14d1c2dd26c666976e1b18579c6b1c8761a102", - "sha256:5580f22ac1298261cd24e8e584180d83e2cca9a6167113466d2d16cb2aa1f7b1", - "sha256:64727a2593fdba5d6ef69e94eba793a196deeda7152c7bd3a64edda6b1f95f6e", - "sha256:6e75753065c310befab71c5077a59b7cb638d2146b1cfbb1c3b8f08b51362714", + "sha256:045dbba18c9142278113d5dc62622978a6f718ba662392d406141c59b540c514", + "sha256:213e8f54b4a942532d6ac32314c69a147d3b82fa1725ca05061b7c1a19a1d9b1", "sha256:7236eba4911a5556b497235828e7a4bc5d90957efa63b7c4b3e744d2d2cf1b94", - "sha256:a69dd7e262cdb265ac7d5e929d55f2f3d07baaadd158c8f19caebf8dde08dfe8", - "sha256:d9ca55a5a297408f08e5401c23ad22bd9f580dab899212f0d5dc1830f0909404", + "sha256:e9a6a319c4bbfb57618f207e86a7c519ab0f637be3d2366e4cdac271577834b8", + "sha256:6e75753065c310befab71c5077a59b7cb638d2146b1cfbb1c3b8f08b51362714", + "sha256:64727a2593fdba5d6ef69e94eba793a196deeda7152c7bd3a64edda6b1f95f6e", + "sha256:5580f22ac1298261cd24e8e584180d83e2cca9a6167113466d2d16cb2aa1f7b1", "sha256:e072edbd1c5628c0b8f97d00cf6c9fcd6a4ee2b5ded10d463fcb6eaa066cf40c", - "sha256:e9a6a319c4bbfb57618f207e86a7c519ab0f637be3d2366e4cdac271577834b8" + "sha256:a69dd7e262cdb265ac7d5e929d55f2f3d07baaadd158c8f19caebf8dde08dfe8" ], "version": "==1.1.1" } @@ -164,15 +181,15 @@ "develop": { "attrs": { "hashes": [ - "sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9", - "sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450" + "sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450", + "sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9" ], "version": "==17.4.0" }, "bandit": { "hashes": [ - "sha256:cb977045497f83ec3a02616973ab845c829cdab8144ce2e757fe031104a9abd4", - "sha256:de4cc19d6ba32d6f542c6a1ddadb4404571347d83ef1ed1e7afb7d0b38e0c25b" + "sha256:de4cc19d6ba32d6f542c6a1ddadb4404571347d83ef1ed1e7afb7d0b38e0c25b", + "sha256:cb977045497f83ec3a02616973ab845c829cdab8144ce2e757fe031104a9abd4" ], "version": "==1.4.0" }, @@ -185,8 +202,8 @@ }, "chardet": { "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691", + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae" ], "version": "==3.0.4" }, @@ -201,30 +218,27 @@ "hashes": [ "sha256:65e13cf878d7aff129f1461c13cb5fd1bb6dfe66bb5327e09379c3877763280c" ], - "index": "pypi", "version": "==0.1.9" }, "dparse": { "hashes": [ - "sha256:7c9f9175d8fd83aed6d31a16c1a3ba4c38189120f1df416b46029d940b4ef582", - "sha256:e4b479dd4d6078ba5f087b28447a50eee0caed57f135b0f5a5e5d5024390f41d" + "sha256:e4b479dd4d6078ba5f087b28447a50eee0caed57f135b0f5a5e5d5024390f41d", + "sha256:7c9f9175d8fd83aed6d31a16c1a3ba4c38189120f1df416b46029d940b4ef582" ], "version": "==0.2.1" }, "flake8": { "hashes": [ - "sha256:7253265f7abd8b313e3892944044a365e3f4ac3fcdcfb4298f55ee9ddf188ba0", - "sha256:c7841163e2b576d435799169b78703ad6ac1bbb0f199994fc05f700b2a90ea37" + "sha256:c7841163e2b576d435799169b78703ad6ac1bbb0f199994fc05f700b2a90ea37", + "sha256:7253265f7abd8b313e3892944044a365e3f4ac3fcdcfb4298f55ee9ddf188ba0" ], - "index": "pypi", "version": "==3.5.0" }, "flake8-bandit": { "hashes": [ - "sha256:24572dc2181c967bec1e7767c1a1faa4921f3d5a4cf9f84a78e2a555ca6c442a", - "sha256:345f5e61cd5eaf354156d7b45173fd14f6e31d35391d884c3222597a363c61ca" + "sha256:345f5e61cd5eaf354156d7b45173fd14f6e31d35391d884c3222597a363c61ca", + "sha256:24572dc2181c967bec1e7767c1a1faa4921f3d5a4cf9f84a78e2a555ca6c442a" ], - "index": "pypi", "version": "==1.0.1" }, "flake8-bugbear": { @@ -232,7 +246,6 @@ "sha256:541746f0f3b2f1a8d7278e1d2d218df298996b60b02677708560db7c7e620e3b", "sha256:5f14a99d458e29cb92be9079c970030e0dd398b2decb179d76d39a5266ea1578" ], - "index": "pypi", "version": "==18.2.0" }, "flake8-import-order": { @@ -240,7 +253,6 @@ "sha256:40d2a39ed91e080f3285f4c16256b252d7c31070e7f11b7854415bb9f924ea81", "sha256:68d430781a9ef15c85a0121500cf8462f1a4bc7672acb2a32bfdbcab044ae0b7" ], - "index": "pypi", "version": "==0.17.1" }, "flake8-polyfill": { @@ -255,42 +267,39 @@ "sha256:68ea72a1a5b75e7018cae44d14f32473c798cf73d75cbaed86c6a9a907b770b2", "sha256:774d56103d9242ed968897455ef49b7d6de272000cfa83de5814273a868832f1" ], - "index": "pypi", "version": "==0.2.3" }, "flake8-tidy-imports": { "hashes": [ - "sha256:5fc28c82bba16abb4f1154dc59a90487f5491fbdb27e658cbee241e8fddc1b91", - "sha256:c05c9f7dadb5748a04b6fa1c47cb6ae5a8170f03cfb1dca8b37aec58c1ee6d15" + "sha256:c05c9f7dadb5748a04b6fa1c47cb6ae5a8170f03cfb1dca8b37aec58c1ee6d15", + "sha256:5fc28c82bba16abb4f1154dc59a90487f5491fbdb27e658cbee241e8fddc1b91" ], - "index": "pypi", "version": "==1.1.0" }, "flake8-todo": { "hashes": [ "sha256:6e4c5491ff838c06fe5a771b0e95ee15fc005ca57196011011280fc834a85915" ], - "index": "pypi", "version": "==0.7" }, "gitdb2": { "hashes": [ - "sha256:b60e29d4533e5e25bb50b7678bbc187c8f6bcff1344b4f293b2ba55c85795f09", - "sha256:cf9a4b68e8c4da8d42e48728c944ff7af2d8c9db303ac1ab32eac37aa4194b0e" + "sha256:cf9a4b68e8c4da8d42e48728c944ff7af2d8c9db303ac1ab32eac37aa4194b0e", + "sha256:b60e29d4533e5e25bb50b7678bbc187c8f6bcff1344b4f293b2ba55c85795f09" ], "version": "==2.0.3" }, "gitpython": { "hashes": [ - "sha256:ad61bc25deadb535b047684d06f3654c001d9415e1971e51c9c20f5b510076e9", - "sha256:b8367c432de995dc330b5b146c5bfdc0926b8496e100fda6692134e00c0dcdc5" + "sha256:05069e26177c650b3cb945dd543a7ef7ca449f8db5b73038b465105673c1ef61", + "sha256:c47cc31af6e88979c57a33962cbc30a7c25508d74a1b3a19ec5aa7ed64b03129" ], - "version": "==2.1.8" + "version": "==2.1.9" }, "idna": { "hashes": [ - "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f", - "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4" + "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4", + "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f" ], "version": "==2.6" }, @@ -310,15 +319,15 @@ }, "pbr": { "hashes": [ - "sha256:05f61c71aaefc02d8e37c0a3eeb9815ff526ea28b3b76324769e6158d7f95be1", - "sha256:60c25b7dfd054ef9bb0ae327af949dd4676aa09ac3a9471cdc871d8a9213f9ac" + "sha256:60c25b7dfd054ef9bb0ae327af949dd4676aa09ac3a9471cdc871d8a9213f9ac", + "sha256:05f61c71aaefc02d8e37c0a3eeb9815ff526ea28b3b76324769e6158d7f95be1" ], "version": "==3.1.1" }, "pycodestyle": { "hashes": [ - "sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766", - "sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9" + "sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9", + "sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766" ], "version": "==2.3.1" }, @@ -331,32 +340,32 @@ }, "pyparsing": { "hashes": [ + "sha256:fee43f17a9c4087e7ed1605bd6df994c6173c1e977d7ade7b651292fab2bd010", "sha256:0832bcf47acd283788593e7a0f542407bd9550a55a8a8435214a1960e04bcb04", - "sha256:281683241b25fe9b80ec9d66017485f6deff1af5cde372469134b56ca8447a07", - "sha256:8f1e18d3fd36c6795bb7e02a39fd05c611ffc2596c1e0d995d34d67630426c18", "sha256:9e8143a3e15c13713506886badd96ca4b579a87fbdf49e550dbfc057d6cb218e", + "sha256:281683241b25fe9b80ec9d66017485f6deff1af5cde372469134b56ca8447a07", "sha256:b8b3117ed9bdf45e14dcc89345ce638ec7e0e29b2b579fa1ecf32ce45ebac8a5", - "sha256:e4d45427c6e20a59bf4f88c639dcc03ce30d193112047f94012102f235853a58", - "sha256:fee43f17a9c4087e7ed1605bd6df994c6173c1e977d7ade7b651292fab2bd010" + "sha256:8f1e18d3fd36c6795bb7e02a39fd05c611ffc2596c1e0d995d34d67630426c18", + "sha256:e4d45427c6e20a59bf4f88c639dcc03ce30d193112047f94012102f235853a58" ], "version": "==2.2.0" }, "pyyaml": { "hashes": [ - "sha256:0c507b7f74b3d2dd4d1322ec8a94794927305ab4cebbe89cc47fe5e81541e6e8", - "sha256:16b20e970597e051997d90dc2cddc713a2876c47e3d92d59ee198700c5427736", "sha256:3262c96a1ca437e7e4763e2843746588a965426550f3797a79fca9c6199c431f", - "sha256:326420cbb492172dec84b0f65c80942de6cedb5233c413dd824483989c000608", - "sha256:4474f8ea030b5127225b8894d626bb66c01cda098d47a2b0d3429b6700af9fd8", + "sha256:16b20e970597e051997d90dc2cddc713a2876c47e3d92d59ee198700c5427736", + "sha256:e863072cdf4c72eebf179342c94e6989c67185842d9997960b3e69290b2fa269", + "sha256:bc6bced57f826ca7cb5125a10b23fd0f2fff3b7c4701d64c439a300ce665fff8", + "sha256:c01b880ec30b5a6e6aa67b09a2fe3fb30473008c85cd6a67359a1b15ed6d83a4", + "sha256:827dc04b8fa7d07c44de11fabbc888e627fa8293b695e0f99cb544fdfa1bf0d1", "sha256:592766c6303207a20efc445587778322d7f73b161bd994f227adaa341ba212ab", - "sha256:5ac82e411044fb129bae5cfbeb3ba626acb2af31a8d17d175004b70862a741a7", "sha256:5f84523c076ad14ff5e6c037fe1c89a7f73a3e04cf0377cb4d017014976433f3", - "sha256:827dc04b8fa7d07c44de11fabbc888e627fa8293b695e0f99cb544fdfa1bf0d1", + "sha256:0c507b7f74b3d2dd4d1322ec8a94794927305ab4cebbe89cc47fe5e81541e6e8", "sha256:b4c423ab23291d3945ac61346feeb9a0dc4184999ede5e7c43e1ffb975130ae6", - "sha256:bc6bced57f826ca7cb5125a10b23fd0f2fff3b7c4701d64c439a300ce665fff8", - "sha256:c01b880ec30b5a6e6aa67b09a2fe3fb30473008c85cd6a67359a1b15ed6d83a4", "sha256:ca233c64c6e40eaa6c66ef97058cdc80e8d0157a443655baa1b2966e812807ca", - "sha256:e863072cdf4c72eebf179342c94e6989c67185842d9997960b3e69290b2fa269" + "sha256:4474f8ea030b5127225b8894d626bb66c01cda098d47a2b0d3429b6700af9fd8", + "sha256:326420cbb492172dec84b0f65c80942de6cedb5233c413dd824483989c000608", + "sha256:5ac82e411044fb129bae5cfbeb3ba626acb2af31a8d17d175004b70862a741a7" ], "version": "==3.12" }, @@ -372,13 +381,12 @@ "sha256:9fb74211a0a0ab09541fe894293d66a558b6138a9fe8ebabc8cf56670e8a009c", "sha256:ff0c4b76ad791d33825e36c41671ea45330d438921e5395903c0e87e576a377a" ], - "index": "pypi", "version": "==1.7.0" }, "six": { "hashes": [ - "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", - "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" + "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb", + "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9" ], "version": "==1.11.0" }, diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index e3ed67df..90e46207 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -9,6 +9,7 @@ import discord from discord.ext.commands import AutoShardedBot, Context, command +from titlecase import titlecase log = logging.getLogger(__name__) @@ -61,7 +62,6 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: :return: A dict containing information on a snake """ base_url = "https://protected-reef-75100.herokuapp.com/" - all_snakes_url = base_url + 'get_all_snakes?format=json' random_url = 'https://protected-reef-75100.herokuapp.com/random_snake' search_url = base_url + 'search' token = os.getenv('ACCESS_TOKEN') @@ -117,9 +117,9 @@ async def get(self, ctx: Context, name: str = None): # if the snake is not venomous -- use the fancy not allowed icon venom_info = f":no_entry_sign: NOT venomous\n\n" embed.add_field( - name=snek_info['common_name'], + name=titlecase(snek_info['common_name']), value=( - f":microscope: *{snek_info['scientific_name']}*\n\n" + f":microscope: *{titlecase(snek_info['scientific_name'])}*\n\n" f"{venom_info}" f":globe_with_meridians: Found in {snek_info['locations']}" ), From 544bed53ea5e601d9af7e78459093cf93009ecd1 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sat, 24 Mar 2018 22:04:20 -0600 Subject: [PATCH 21/44] return number of matches in searches, add handling of searches with 0 results --- bot/cogs/snakes.py | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 90e46207..5c790853 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -66,12 +66,14 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: search_url = base_url + 'search' token = os.getenv('ACCESS_TOKEN') headers = {'Authorization': f'Token {token}'} + snake_info = {} if not name: # get a random snake... async with aiohttp.ClientSession() as session: async with session.get(random_url, headers=headers) as response: response = await response.read() snake_info = json.loads(response.decode("utf-8")) + snake_info['matches_count'] = 1 else: params = {'snake': name} async with aiohttp.ClientSession() as session: @@ -79,10 +81,21 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: # search snake endpoint something... response = await response.read() data = json.loads(response.decode("utf-8")) - rand = random.randint(0, len(data) - 1) - snake_info = data[rand] + try: + rand = random.randint(0, len(data) - 1) + except ValueError: + # handle the scenario with an empty range of (0,0) + rand = 0 + try: + snake_info = data[rand] + except IndexError: + # handles if there is no match in the database + snake_info['pass'] = False + return snake_info + snake_info['matches_count'] = len(data) snake_info['image_url'] = await self.get_snek_image(snake_info['common_name']) + snake_info['pass'] = True # successful data retrieval return snake_info @command(aliases=["g"]) @@ -110,26 +123,39 @@ async def get(self, ctx: Context, name: str = None): embed.set_image(url=await self.get_snek_image("python programming language")) else: snek_info = await self.get_snek(name) + if not snek_info['pass']: + embed = discord.Embed(color=0xDD3D5A) + embed.add_field( + name="We Couldn't Find That :snake:", + value=f':x: "{name}" didn\'t match anything in our database :frowning:', + inline=False + ) + await ctx.channel.send(embed=embed) + return # break out of rest of method if snek_info['is_venomous']: # if the snake is venomous -- use the fancy check icon venom_info = f":white_check_mark: venomous\n\n" else: # if the snake is not venomous -- use the fancy not allowed icon venom_info = f":no_entry_sign: NOT venomous\n\n" + additional_info = '' # required to prevent referencing before assignment + if snek_info['matches_count'] and snek_info['matches_count'] > 1: + additional_info = f"\n\n" \ + f"This search matched {snek_info['matches_count']} snakes. " \ + f"Try creating a more specific query for information about a particular snake. " \ + f"(This is a random selection from the {snek_info['matches_count']}.)" embed.add_field( name=titlecase(snek_info['common_name']), value=( f":microscope: *{titlecase(snek_info['scientific_name'])}*\n\n" f"{venom_info}" f":globe_with_meridians: Found in {snek_info['locations']}" + f"{additional_info}" ), inline=False ) embed.set_image(url=snek_info['image_url']) - await ctx.channel.send( - # content=ctx.message.author.mention + " :snake: !", - embed=embed - ) + await ctx.channel.send(embed=embed) # Any additional commands can be placed here. Be creative, but keep it to a reasonable amount! From a2255e757b9493b51128f2a75c809c518c17885a Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sat, 24 Mar 2018 23:00:03 -0600 Subject: [PATCH 22/44] add qwant api in favor of unsplash, including handling for small numbers of results --- bot/cogs/snakes.py | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 5c790853..2e2c065c 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -3,6 +3,7 @@ import logging import os import random +import urllib from typing import Any, Dict import aiohttp @@ -28,13 +29,14 @@ async def get_snek_qwant_json(self, snake_name: str) -> str: :param snake_name: name of the snake :return: the full JSON from the search API """ - client_id = os.environ.get("UNSPLASH_CLIENT_ID") - url = ( - "https://api.unsplash.com/search/photos?client_id" - f"={client_id}&query={snake_name}+snake" - ) + head = { + 'user-agent': ('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) ' + 'AppleWebKit/537.36 (KHTML, like Gecko) ' + 'Chrome/45.0.2454.101 Safari/537.36'), + } + url = f'https://api.qwant.com/api/search/images?count=5&offset=1&q={urllib.parse.quote(snake_name)}+snake' async with aiohttp.ClientSession() as session: - async with session.get(url) as response: + async with session.get(url, headers=head) as response: response = await response.read() return json.loads(response.decode("utf-8")) @@ -45,8 +47,21 @@ async def get_snek_image(self, name: str) -> str: :return: image url """ json_response = await self.get_snek_qwant_json(name) - rand = random.randint(0, 9) # prevents returning the same image every time - return str(json_response['results'][rand]['urls']['small']) + result_count = len(json_response["data"]["result"]["items"]) + print(result_count) + if 3 > result_count > 1: + rand = random.randint(0, result_count - 1) + print(rand) + if result_count == 1: + rand = 0 + else: + rand = random.randint(0, 3) # prevents returning the same image every time + try: + choice = str(json_response["data"]["result"]["items"][rand]["media"]) + except IndexError: + # if no(t enough) images are returned... + return 'https://dksignmt.com/wp-content/uploads/2015/01/404-Im%C3%A1gen-14.png' # 404 image + return choice async def get_snek(self, name: str = None) -> Dict[str, Any]: """ @@ -95,7 +110,7 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: snake_info['matches_count'] = len(data) snake_info['image_url'] = await self.get_snek_image(snake_info['common_name']) - snake_info['pass'] = True # successful data retrieval + snake_info['pass'] = True # successful data retrieval return snake_info @command(aliases=["g"]) @@ -131,14 +146,14 @@ async def get(self, ctx: Context, name: str = None): inline=False ) await ctx.channel.send(embed=embed) - return # break out of rest of method + return # break out of rest of method if snek_info['is_venomous']: # if the snake is venomous -- use the fancy check icon venom_info = f":white_check_mark: venomous\n\n" else: # if the snake is not venomous -- use the fancy not allowed icon venom_info = f":no_entry_sign: NOT venomous\n\n" - additional_info = '' # required to prevent referencing before assignment + additional_info = '' # required to prevent referencing before assignment if snek_info['matches_count'] and snek_info['matches_count'] > 1: additional_info = f"\n\n" \ f"This search matched {snek_info['matches_count']} snakes. " \ From 9b2f8d06194655a877d3eb01fb072675a1f6b186 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sat, 24 Mar 2018 23:01:41 -0600 Subject: [PATCH 23/44] rm prints --- bot/cogs/snakes.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 2e2c065c..f485e584 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -48,10 +48,8 @@ async def get_snek_image(self, name: str) -> str: """ json_response = await self.get_snek_qwant_json(name) result_count = len(json_response["data"]["result"]["items"]) - print(result_count) if 3 > result_count > 1: rand = random.randint(0, result_count - 1) - print(rand) if result_count == 1: rand = 0 else: From 48f25840f505ad92f18c954268dfaaf4d1bb3e03 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sat, 24 Mar 2018 23:14:49 -0600 Subject: [PATCH 24/44] add newline between imports (I201) for linting --- bot/cogs/snakes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index f485e584..f13f2139 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -10,6 +10,7 @@ import discord from discord.ext.commands import AutoShardedBot, Context, command + from titlecase import titlecase log = logging.getLogger(__name__) From e5901b61ec951920f4318caeedfa6a5d10613883 Mon Sep 17 00:00:00 2001 From: Prithaj Nath Date: Sun, 25 Mar 2018 08:02:59 -0400 Subject: [PATCH 25/44] Add APIs used --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 76c385a4..71b165f8 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,11 @@ This is the repository for all code relating to our first code jam, in March 201 **This code jam runs from the 23rd of March to the 25th of March, measured using the UTC timezone.** Make sure you open your pull request by then. Once the deadline is up, stop pushing commits - we will not accept any submissions made after this date. +## APIs used +* [Qwant](https://www.qwant.com/) (for snake pics) +* [Our custom Snake API](https://github.com/prithajnath/snake) (scraped a page with bs4 and saved it in a db) + + ## How To Participate First things first - set up your repository. Read [this guide on our site](https://pythondiscord.com/info/jams) for information on how to set yourself up for a code jam. From 5770c796be05b28a8116aaf3fa7eecb136e8f8be Mon Sep 17 00:00:00 2001 From: prithajnath Date: Sun, 25 Mar 2018 11:14:52 -0400 Subject: [PATCH 26/44] Add some GIFs for facts --- bot/cogs/resources/facts.json | 87 +++++++++++++++++++++-------------- bot/cogs/snakes.py | 17 +++++-- 2 files changed, 65 insertions(+), 39 deletions(-) diff --git a/bot/cogs/resources/facts.json b/bot/cogs/resources/facts.json index cd1c28ba..6f7c27a8 100644 --- a/bot/cogs/resources/facts.json +++ b/bot/cogs/resources/facts.json @@ -1,36 +1,53 @@ { - "facts": [ - "Snakes are carnivores (meat eaters).", - "Snakes don’t have eyelids.", - "Snakes can’t bite food so have to swallow it whole.", - "Snakes have flexible jaws which allow them to eat prey bigger than their head!", - "Snakes are found on every continent of the world except Antarctica.", - "Snakes have internal ears but not external ones.", - "Snakes used in snake charming performances respond to movement, not sound.", - "There are around 3000 different species of snake.", - "Snakes have a unique anatomy which allows them to swallow and digest large prey.", - "Snakes are covered in scales.", - "Snakeskin is smooth and dry.", - "Snakes shed their skin a number of times a year in a process that usually lasts a few days.", - "Snakes smell with their tongue.", - "Pythons kill their prey by tightly wrapping around it and suffocating it in a process called constriction.", - "Some sea snakes can breathe partially through their skin, allowing for longer dives underwater.", - "Some animals, such as the Mongoose, are immune to snake venom.", - "While snakes do not have external ears or eardrums, their skin, muscles, and bones carry sound vibrations to their inner ears.", - "Rattlesnake rattles are made of rings of keratin, which is the same material as human hair and fingernails. A rattler will add a new ring each time it sheds its skin.", - "Some snakes have over 200 teeth. The teeth aren’t used for chewing but they point backward to prevent prey from escaping the snake’s throat.", - "There are five recognized species of flying snakes. Growing up to 4 feet, some types can glide up to 330 feet through the air.", - "Snakes do not lap up water like mammals do. Instead, they dunk their snouts underwater and use their throats to pump water into their stomachs.", - "A snake’s fangs usually last about 6–10 weeks. When a fang wears out, a new one grows in its place.", - "Snakes typically need to eat only 6–30 meals each year to be healthy.", - "If the temperature reaches below 50° Fahrenheit, a snake’s body does not work properly.", - "The Gaboon viper has the longest fangs of any snake, reaching about 2 inches (5 cm) long.", - "The longest snake ever recorded is the reticulated python. It can reach over 33 feet long, which is big enough to swallow a pig, a deer, or even a person.", - "Some venomous snakes have died after biting and poisoning themselves by mistake.", - "While a snake cannot hear the music of a snake charmer, the snake responds to the vibrations of the charmer’s tapping foot or to the movement of the flute.", - "Most snakes are not harmful to humans and they help balance the ecosystem by keeping the population of rats, mice, and birds under control.", - "Most snakes have an elongated right lung, many have a smaller left lung, and a few even have a third lung. They do not have a sense of taste, and most of their organs are organized linearly.", - "Snakes kill over 40,000 people a year—though, with unreported incidents, the total may be over 100,000. About half of these deaths are in India.", - "Some members of the U.S. Army Special Forces are taught to kill and eat snakes during their survival training, which has earned them the nickname “Snake Eaters.”" - ] -} \ No newline at end of file + "facts": { + "Snakes are carnivores (meat eaters).":"dangerous", + "Snakes don’t have eyelids.":"interesting", + "Snakes can’t bite food so have to swallow it whole.":"dangerous", + "Snakes have flexible jaws which allow them to eat prey bigger than their head!":"dangerous", + "Snakes are found on every continent of the world except Antarctica.":"interesting", + "Snakes have internal ears but not external ones.":"interesting", + "Snakes used in snake charming performances respond to movement, not sound.":"interesting", + "There are around 3000 different species of snake.":"interesting", + "Snakes have a unique anatomy which allows them to swallow and digest large prey.":"dangerous", + "Snakes are covered in scales.":"interesting", + "Snakeskin is smooth and dry.":"interesting", + "Snakes shed their skin a number of times a year in a process that usually lasts a few days.":"interesting", + "Snakes smell with their tongue.":"interesting", + "Pythons kill their prey by tightly wrapping around it and suffocating it in a process called constriction.":"dangerous", + "Some sea snakes can breathe partially through their skin, allowing for longer dives underwater.":"interesting", + "Some animals, such as the Mongoose, are immune to snake venom.":"interesting", + "While snakes do not have external ears or eardrums, their skin, muscles, and bones carry sound vibrations to their inner ears.":"interesting", + "Rattlesnake rattles are made of rings of keratin, which is the same material as human hair and fingernails. A rattler will add a new ring each time it sheds its skin.":"interesting", + "Some snakes have over 200 teeth. The teeth aren’t used for chewing but they point backward to prevent prey from escaping the snake’s throat.":"interesting", + "There are five recognized species of flying snakes. Growing up to 4 feet, some types can glide up to 330 feet through the air.":"interesting", + "Snakes do not lap up water like mammals do. Instead, they dunk their snouts underwater and use their throats to pump water into their stomachs.":"interesting", + "A snake’s fangs usually last about 6–10 weeks. When a fang wears out, a new one grows in its place.":"interesting", + "Snakes typically need to eat only 6–30 meals each year to be healthy.":"interesting", + "If the temperature reaches below 50° Fahrenheit, a snake’s body does not work properly.":"interesting", + "The Gaboon viper has the longest fangs of any snake, reaching about 2 inches (5 cm) long.":"dangerous", + "The longest snake ever recorded is the reticulated python. It can reach over 33 feet long, which is big enough to swallow a pig, a deer, or even a person.":"dangerous", + "Some venomous snakes have died after biting and poisoning themselves by mistake.":"self-harm", + "While a snake cannot hear the music of a snake charmer, the snake responds to the vibrations of the charmer’s tapping foot or to the movement of the flute.":"interesting", + "Most snakes are not harmful to humans and they help balance the ecosystem by keeping the population of rats, mice, and birds under control.":"interesting", + "Most snakes have an elongated right lung, many have a smaller left lung, and a few even have a third lung. They do not have a sense of taste, and most of their organs are organized linearly.":"interesting", + "Snakes kill over 40,000 people a year—though, with unreported incidents, the total may be over 100,000. About half of these deaths are in India.":"dangerous", + "Some members of the U.S. Army Special Forces are taught to kill and eat snakes during their survival training, which has earned them the nickname “Snake Eaters.”":"dangerous" + }, + "gifs":{ + "dangerous":[ + "https://media.giphy.com/media/2yP1jNgjNAkvu/giphy.gif", + "https://media.giphy.com/media/OGufyodtN4NUs/giphy.gif", + "https://media.giphy.com/media/tP2fZCu4K9Yac/giphy.gif", + "https://media.giphy.com/media/ldC6st7Bz3lZK/giphy.gif" + ], + "self-harm":[ + "https://media.giphy.com/media/A6r0BbR2ob1GE/giphy.gif", + "https://media.giphy.com/media/dFEd78w0sJNwA/giphy.gif" + ], + "interesting":[ + "https://media.giphy.com/media/10lKFp5Y03YITK/giphy.gif", + "https://media.giphy.com/media/ZPtGZIbpA1Z0Q/giphy.gif", + "https://media.giphy.com/media/hfV9dWY07x960/giphy.gif" + ] + } +} diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index f13f2139..518821b0 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -179,12 +179,19 @@ async def fact(self, ctx: Context): Gets a random fact about snakes :param ctx: Context object passed from discord.py """ + message_suffix = { + "dangerous":["Yikes!!","Oh my!"], + "interesting":["Pretty cool!","Whoah!!","Pretty sick!!"], + "self-harm":["Ouch!!"] + } + _fact = self.get_snek_fact() em = discord.Embed(color=0x399600) em.add_field( - name="Snake Fact", - value=self.get_snek_fact(), + name=f"{_fact['cat']} snake fact", + value=f"{_fact['message']}. {random.choice(message_suffix[_fact['cat']])}", inline=False ) + em.set_image(url=_fact['gif']) await ctx.channel.send( content=ctx.message.author.mention, embed=em @@ -193,8 +200,10 @@ async def fact(self, ctx: Context): def get_snek_fact(self) -> str: with open('bot/cogs/resources/facts.json', 'r', encoding="utf8") as f: data = json.load(f) - choice = random.randint(0, len(data['facts']) - 1) - return data['facts'][choice] + random_fact = random.choice(list(data['facts'].keys())) + gif_cat = data['facts'][random_fact] + gif_url = random.choice(data['gifs'][gif_cat]) + return {'message':random_fact, 'gif':gif_url, 'cat':gif_cat} def setup(bot): From 259c5a60adcba4738f221a53d5b68f443d39ecbd Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sun, 25 Mar 2018 10:59:38 -0600 Subject: [PATCH 27/44] linting updates --- bot/cogs/snakes.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 518821b0..6841bd48 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -180,9 +180,9 @@ async def fact(self, ctx: Context): :param ctx: Context object passed from discord.py """ message_suffix = { - "dangerous":["Yikes!!","Oh my!"], - "interesting":["Pretty cool!","Whoah!!","Pretty sick!!"], - "self-harm":["Ouch!!"] + "dangerous": ["Yikes!!", "Oh my!"], + "interesting": ["Pretty cool!", "Whoah!!", "Pretty sick!!"], + "self-harm": ["Ouch!!"] } _fact = self.get_snek_fact() em = discord.Embed(color=0x399600) @@ -197,13 +197,13 @@ async def fact(self, ctx: Context): embed=em ) - def get_snek_fact(self) -> str: + def get_snek_fact(self) -> Dict[str, any]: with open('bot/cogs/resources/facts.json', 'r', encoding="utf8") as f: data = json.load(f) random_fact = random.choice(list(data['facts'].keys())) gif_cat = data['facts'][random_fact] gif_url = random.choice(data['gifs'][gif_cat]) - return {'message':random_fact, 'gif':gif_url, 'cat':gif_cat} + return {'message': random_fact, 'gif': gif_url, 'cat': gif_cat} def setup(bot): From 2d45b2f992ce228a99df0de8d406ff2460c6a8ff Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sun, 25 Mar 2018 11:02:57 -0600 Subject: [PATCH 28/44] add list of zen python sayings --- bot/cogs/resources/zen.json | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 bot/cogs/resources/zen.json diff --git a/bot/cogs/resources/zen.json b/bot/cogs/resources/zen.json new file mode 100644 index 00000000..ad2398c1 --- /dev/null +++ b/bot/cogs/resources/zen.json @@ -0,0 +1,23 @@ +{ + "zen" : [ + "Beautiful is better than ugly.", + "Explicit is better than implicit.", + "Simple is better than complex.", + "Complex is better than complicated.", + "Flat is better than nested.", + "Sparse is better than dense.", + "Readability counts.", + "Special cases aren't special enough to break the rules.", + "Although practicality beats purity.", + "Errors should never pass silently.", + "Unless explicitly silenced.", + "In the face of ambiguity, refuse the temptation to guess.", + "There should be one-- and preferably only one --obvious way to do it.", + "Although that way may not be obvious at first unless you're Dutch.", + "Now is better than never.", + "Although never is often better than *right* now.", + "If the implementation is hard to explain, it's a bad idea.", + "If the implementation is easy to explain, it may be a good idea.", + "Namespaces are one honking great idea -- let's do more of those!" + ] +} \ No newline at end of file From 67762641a3e64a7e7eb19e5470f81b1dc2e27f47 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sun, 25 Mar 2018 11:12:26 -0600 Subject: [PATCH 29/44] use response.json() for qwant access --- bot/cogs/snakes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 6841bd48..5b54c936 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -38,8 +38,8 @@ async def get_snek_qwant_json(self, snake_name: str) -> str: url = f'https://api.qwant.com/api/search/images?count=5&offset=1&q={urllib.parse.quote(snake_name)}+snake' async with aiohttp.ClientSession() as session: async with session.get(url, headers=head) as response: - response = await response.read() - return json.loads(response.decode("utf-8")) + response = await response.json() + return response async def get_snek_image(self, name: str) -> str: """ From 03f9b618bdffd5be198d6c5b12efff58159ed6c0 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sun, 25 Mar 2018 11:29:42 -0600 Subject: [PATCH 30/44] handle snakes without a location, reduce variance in photos to three photos per snake --- bot/cogs/snakes.py | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 5b54c936..92e4e307 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -49,12 +49,10 @@ async def get_snek_image(self, name: str) -> str: """ json_response = await self.get_snek_qwant_json(name) result_count = len(json_response["data"]["result"]["items"]) - if 3 > result_count > 1: - rand = random.randint(0, result_count - 1) if result_count == 1: rand = 0 else: - rand = random.randint(0, 3) # prevents returning the same image every time + rand = random.randint(0, 2) # prevents returning the same image every time try: choice = str(json_response["data"]["result"]["items"][rand]["media"]) except IndexError: @@ -146,12 +144,19 @@ async def get(self, ctx: Context, name: str = None): ) await ctx.channel.send(embed=embed) return # break out of rest of method + if snek_info['is_venomous']: # if the snake is venomous -- use the fancy check icon - venom_info = f":white_check_mark: venomous\n\n" + venom_info = f":white_check_mark: venomous" else: # if the snake is not venomous -- use the fancy not allowed icon - venom_info = f":no_entry_sign: NOT venomous\n\n" + venom_info = f":no_entry_sign: NOT venomous" + + if not snek_info['locations']: + # if no location field + location_info = '' + else: + location_info = f"\n\n:globe_with_meridians: Found in {snek_info['locations']}" additional_info = '' # required to prevent referencing before assignment if snek_info['matches_count'] and snek_info['matches_count'] > 1: additional_info = f"\n\n" \ @@ -163,7 +168,7 @@ async def get(self, ctx: Context, name: str = None): value=( f":microscope: *{titlecase(snek_info['scientific_name'])}*\n\n" f"{venom_info}" - f":globe_with_meridians: Found in {snek_info['locations']}" + f"{location_info}" f"{additional_info}" ), inline=False @@ -187,8 +192,8 @@ async def fact(self, ctx: Context): _fact = self.get_snek_fact() em = discord.Embed(color=0x399600) em.add_field( - name=f"{_fact['cat']} snake fact", - value=f"{_fact['message']}. {random.choice(message_suffix[_fact['cat']])}", + name=titlecase(f"{_fact['cat']} snake fact"), + value=f"{_fact['message']} {random.choice(message_suffix[_fact['cat']])}", inline=False ) em.set_image(url=_fact['gif']) From a27582a8d2c6d394721f13689beeecb6f1674261 Mon Sep 17 00:00:00 2001 From: prithajnath Date: Sun, 25 Mar 2018 13:44:49 -0400 Subject: [PATCH 31/44] Add cat input for GIF --- bot/cogs/snakes.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 518821b0..1607cdf9 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -174,7 +174,7 @@ async def get(self, ctx: Context, name: str = None): # Any additional commands can be placed here. Be creative, but keep it to a reasonable amount! @command(aliases=["f"]) - async def fact(self, ctx: Context): + async def fact(self, ctx: Context, cat: str = None): """ Gets a random fact about snakes :param ctx: Context object passed from discord.py @@ -184,7 +184,7 @@ async def fact(self, ctx: Context): "interesting":["Pretty cool!","Whoah!!","Pretty sick!!"], "self-harm":["Ouch!!"] } - _fact = self.get_snek_fact() + _fact = self.get_snek_fact(cat) em = discord.Embed(color=0x399600) em.add_field( name=f"{_fact['cat']} snake fact", @@ -197,10 +197,14 @@ async def fact(self, ctx: Context): embed=em ) - def get_snek_fact(self) -> str: + def get_snek_fact(self, cat = None) -> str: with open('bot/cogs/resources/facts.json', 'r', encoding="utf8") as f: data = json.load(f) - random_fact = random.choice(list(data['facts'].keys())) + if cat: + facts_with_cat = list(filter(lambda x:data['facts'][x]==cat.lower(),list(data['facts'].keys()))) + random_fact = random.choice(facts_with_cat) + else: + random_fact = random.choice(list(data['facts'].keys())) gif_cat = data['facts'][random_fact] gif_url = random.choice(data['gifs'][gif_cat]) return {'message':random_fact, 'gif':gif_url, 'cat':gif_cat} From e2d556b836fc6253db3c8ee67ecbde14f80099e6 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sun, 25 Mar 2018 12:02:56 -0600 Subject: [PATCH 32/44] start video command - needs formatting work for embed --- bot/cogs/snakes.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 92e4e307..e41cd1d0 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -210,6 +210,50 @@ def get_snek_fact(self) -> Dict[str, any]: gif_url = random.choice(data['gifs'][gif_cat]) return {'message': random_fact, 'gif': gif_url, 'cat': gif_cat} + async def get_video_json(self, search: str) -> str: + """ + Gets the json from Unsplash for a given snake query + :param search: optional param for a user to search a specific video type + :return: the full JSON from the search API + """ + # (YouTube Data API v3) + youtube_key = os.getenv('YOUTUBE_DATA_KEY') # generated: https://console.developers.google.com/apis/credentials + if search: + query = search + ' snake' + else: + query = 'snake' + url = f'https://www.googleapis.com/youtube/v3/search' \ + f'?part=snippet&q={urllib.parse.quote(query)}&type=video&key={youtube_key}' + async with aiohttp.ClientSession() as session: + async with session.get(url) as response: + response = await response.json() + return response['items'] + + @command(aliases=["v"]) + async def video(self, ctx: Context, name: str = None): + """ + Gets a video about snakes + :param name: + :param ctx: Context object passed from discord.py + :return: + """ + data = await self.get_video_json(name) + num = random.randint(0, 5) # 5 videos are returned from the api + youtube_base_url = 'https://www.youtube.com/watch?v=' + embed = discord.Embed(color=0x399600) + embed.add_field( + name='Snake YouTube Video', + value=ctx.message.author.mention, + # value=youtube_base_url + data[num]['id']['videoId'], + inline=False + ) + # embed.set_image(url=_fact['gif']) + await ctx.channel.send( + content=youtube_base_url + data[num]['id']['videoId'], + # content=ctx.message.author.mention, + embed=embed + ) + def setup(bot): bot.add_cog(Snakes(bot)) From 003ace9f3f0228edb1c92fd436dec113f8b21997 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sun, 25 Mar 2018 12:43:19 -0600 Subject: [PATCH 33/44] remove embed (Discord is finnicky), fix command format --- bot/cogs/snakes.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index e41cd1d0..b7e6e664 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -240,18 +240,9 @@ async def video(self, ctx: Context, name: str = None): data = await self.get_video_json(name) num = random.randint(0, 5) # 5 videos are returned from the api youtube_base_url = 'https://www.youtube.com/watch?v=' - embed = discord.Embed(color=0x399600) - embed.add_field( - name='Snake YouTube Video', - value=ctx.message.author.mention, - # value=youtube_base_url + data[num]['id']['videoId'], - inline=False - ) - # embed.set_image(url=_fact['gif']) await ctx.channel.send( - content=youtube_base_url + data[num]['id']['videoId'], - # content=ctx.message.author.mention, - embed=embed + content=f"{ctx.message.author.mention} Here's a Snake Video!" + f"\n{youtube_base_url}{data[num]['id']['videoId']}" ) From 2366c437e27308433018eb1b4effebdad43750cb Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sun, 25 Mar 2018 12:45:21 -0600 Subject: [PATCH 34/44] update docstring for get --- bot/cogs/snakes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 92e4e307..c9193507 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -113,7 +113,7 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: @command(aliases=["g"]) async def get(self, ctx: Context, name: str = None): """ - Go online and fetch information about a snake + Gets information and an image about a snake This should make use of your `get_snek` method, using it to get information about a snake. This information should be sent back to Discord in an embed. From ea317983b92e3cf0b224573929422ed6f713c40c Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sun, 25 Mar 2018 12:47:03 -0600 Subject: [PATCH 35/44] update video docstring --- bot/cogs/snakes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index b7e6e664..32eef6ad 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -232,8 +232,8 @@ async def get_video_json(self, search: str) -> str: @command(aliases=["v"]) async def video(self, ctx: Context, name: str = None): """ - Gets a video about snakes - :param name: + Gets a YouTube video about snakes + :param name: Optional, a name of a snake. Used to search for videos with that name :param ctx: Context object passed from discord.py :return: """ From a9f8863ad59b74d33fa7f22340e1c19c77aadb94 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sun, 25 Mar 2018 12:49:52 -0600 Subject: [PATCH 36/44] fix get_video_json docstring --- bot/cogs/snakes.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 32eef6ad..3f774e0d 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -212,11 +212,10 @@ def get_snek_fact(self) -> Dict[str, any]: async def get_video_json(self, search: str) -> str: """ - Gets the json from Unsplash for a given snake query - :param search: optional param for a user to search a specific video type - :return: the full JSON from the search API + Gets the json from the YouTube search API (YouTube Data API v3), with an optional search query + :param search: optional param for a user to search a specific type/name of snake videos + :return: the full JSON from the search API, as a string """ - # (YouTube Data API v3) youtube_key = os.getenv('YOUTUBE_DATA_KEY') # generated: https://console.developers.google.com/apis/credentials if search: query = search + ' snake' From 01c701b7229cadd14ab7f06dcf35e81c6a3a7016 Mon Sep 17 00:00:00 2001 From: prithajnath Date: Sun, 25 Mar 2018 16:14:09 -0400 Subject: [PATCH 37/44] Add Zen of Python --- bot/cogs/resources/zen.json | 20 +++++++++++++++----- bot/cogs/snakes.py | 27 +++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/bot/cogs/resources/zen.json b/bot/cogs/resources/zen.json index ad2398c1..f573fe08 100644 --- a/bot/cogs/resources/zen.json +++ b/bot/cogs/resources/zen.json @@ -7,10 +7,8 @@ "Flat is better than nested.", "Sparse is better than dense.", "Readability counts.", - "Special cases aren't special enough to break the rules.", - "Although practicality beats purity.", - "Errors should never pass silently.", - "Unless explicitly silenced.", + "Special cases aren't special enough to break the rules. Although practicality beats purity.", + "Errors should never pass silently. Unless explicitly silenced.", "In the face of ambiguity, refuse the temptation to guess.", "There should be one-- and preferably only one --obvious way to do it.", "Although that way may not be obvious at first unless you're Dutch.", @@ -19,5 +17,17 @@ "If the implementation is hard to explain, it's a bad idea.", "If the implementation is easy to explain, it may be a good idea.", "Namespaces are one honking great idea -- let's do more of those!" + ], + + "gif":[ + "https://media.giphy.com/media/jCodV34MoczjW/giphy.gif", + "https://media.giphy.com/media/ZLF9Loju0q3MA/giphy.gif", + "https://media.giphy.com/media/v4NCChJtoH076/giphy.gif", + "https://media.giphy.com/media/3o6Ztfi66HECEU0Z4k/giphy.gif", + "https://media.giphy.com/media/HNSv7wrNUK5Ko/giphy.gif", + "https://media.giphy.com/media/3oFzmdjqH15YebLQ52/giphy.gif", + "https://media.giphy.com/media/2PIjvGv9DKtJ6/giphy.gif", + "https://media.giphy.com/media/26DN0U3SqKDG2fTFe/giphy.gif", + "https://media.giphy.com/media/3o6EhXODzc79cnZfRC/giphy.gif" ] -} \ No newline at end of file +} diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 92e4e307..4b210b4f 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -178,6 +178,33 @@ async def get(self, ctx: Context, name: str = None): # Any additional commands can be placed here. Be creative, but keep it to a reasonable amount! + @command(aliases=["t"]) + async def this(self, ctx: Context): + """ + Gets a random fact about snakes + :param ctx: Context object passed from discord.py + """ + + em = discord.Embed(color=0x399600) + with open('bot/cogs/resources/zen.json', 'r', encoding="utf8") as f: + data = json.load(f) + zen_quote = random.choice(data['zen']) + a = zen_quote.split() + b = ["..."*random.randint(1,3)+"..*hi"+"s"*random.randint(3,7)+"*"+"..."*random.randint(1,3) for _ in range(len(a))] + message = ''.join(list(map(lambda x:x[0]+x[1],list(zip(a,b))))) + + em.add_field( + name="The Zen of Python says...", + value=message, + inline=False + ) + + em.set_image(url=random.choice(data['gif'])) + await ctx.channel.send( + content=ctx.message.author.mention, + embed=em + ) + @command(aliases=["f"]) async def fact(self, ctx: Context): """ From f1b7a2cf759ac7b23ee146becacde164e4cd5392 Mon Sep 17 00:00:00 2001 From: prithajnath Date: Sun, 25 Mar 2018 16:38:26 -0400 Subject: [PATCH 38/44] Add alias for this --- bot/cogs/snakes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 4b210b4f..602a3975 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -178,7 +178,7 @@ async def get(self, ctx: Context, name: str = None): # Any additional commands can be placed here. Be creative, but keep it to a reasonable amount! - @command(aliases=["t"]) + @command(aliases=["t","zen"]) async def this(self, ctx: Context): """ Gets a random fact about snakes From ff361b8ad649d6028a424bbdec4a5c25f66bec73 Mon Sep 17 00:00:00 2001 From: prithajnath Date: Sun, 25 Mar 2018 16:40:31 -0400 Subject: [PATCH 39/44] Add descp for zen --- bot/cogs/snakes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 602a3975..396b8de7 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -181,7 +181,7 @@ async def get(self, ctx: Context, name: str = None): @command(aliases=["t","zen"]) async def this(self, ctx: Context): """ - Gets a random fact about snakes + Gets a random qoute from the Zen of Python. Inspired by the Python this module :param ctx: Context object passed from discord.py """ From 3279319f47c1bd7079db4fcb52539993c2b68d2d Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sun, 25 Mar 2018 16:03:26 -0600 Subject: [PATCH 40/44] lint, fix spelling --- bot/cogs/snakes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index d37664c4..c2615e1d 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -178,10 +178,10 @@ async def get(self, ctx: Context, name: str = None): # Any additional commands can be placed here. Be creative, but keep it to a reasonable amount! - @command(aliases=["t","zen"]) + @command(aliases=["t", "zen"]) async def this(self, ctx: Context): """ - Gets a random qoute from the Zen of Python. Inspired by the Python this module + Gets a random quote from the Zen of Python. Inspired by the Python this module :param ctx: Context object passed from discord.py """ From e0d2bf20b974e565cfa3cded7fb9446a28d37123 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sun, 25 Mar 2018 16:04:43 -0600 Subject: [PATCH 41/44] lint, fix spelling --- bot/cogs/snakes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 396b8de7..8b3940ea 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -178,10 +178,10 @@ async def get(self, ctx: Context, name: str = None): # Any additional commands can be placed here. Be creative, but keep it to a reasonable amount! - @command(aliases=["t","zen"]) + @command(aliases=["t", "zen"]) async def this(self, ctx: Context): """ - Gets a random qoute from the Zen of Python. Inspired by the Python this module + Gets a random quote from the Zen of Python. Inspired by the Python this module :param ctx: Context object passed from discord.py """ From 375f4bc8620b0830015342774ecbe9110774dbc7 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sun, 25 Mar 2018 16:16:00 -0600 Subject: [PATCH 42/44] fix arguments, lint, and format for get_snek_fact --- bot/cogs/snakes.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index d1736174..3e3b1683 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -110,7 +110,7 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: snake_info['pass'] = True # successful data retrieval return snake_info - @command(aliases=["g"]) + @command(aliases=["g", "snake.get", "snake.g"]) async def get(self, ctx: Context, name: str = None): """ Gets information and an image about a snake @@ -209,6 +209,7 @@ async def this(self, ctx: Context): async def fact(self, ctx: Context, cat: str = None): """ Gets a random fact about snakes + :param cat: the category for the given fact :param ctx: Context object passed from discord.py """ message_suffix = { @@ -229,11 +230,14 @@ async def fact(self, ctx: Context, cat: str = None): embed=em ) - def get_snek_fact(self) -> Dict[str, any]: + def get_snek_fact(self, cat: str) -> Dict[str, any]: with open('bot/cogs/resources/facts.json', 'r', encoding="utf8") as f: data = json.load(f) if cat: - facts_with_cat = list(filter(lambda x:data['facts'][x]==cat.lower(),list(data['facts'].keys()))) + facts_with_cat = list(filter( + lambda x: data['facts'][x] == cat.lower(), + list(data['facts'].keys()) + )) random_fact = random.choice(facts_with_cat) else: random_fact = random.choice(list(data['facts'].keys())) From 71a0f4482d6d76ed792eead3e303c1b6408f25ec Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Sun, 25 Mar 2018 16:23:38 -0600 Subject: [PATCH 43/44] linting --- bot/cogs/snakes.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 3e3b1683..521c628c 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -190,8 +190,13 @@ async def this(self, ctx: Context): data = json.load(f) zen_quote = random.choice(data['zen']) a = zen_quote.split() - b = ["..."*random.randint(1,3)+"..*hi"+"s"*random.randint(3,7)+"*"+"..."*random.randint(1,3) for _ in range(len(a))] - message = ''.join(list(map(lambda x:x[0]+x[1],list(zip(a,b))))) + b = [ + "..." * random.randint(1, 3) + "..*hi" + "s" * random.randint(3, 7) + + "*" + "..." * random.randint(1, 3) for _ in range(len(a)) + ] + message = ''.join(list(map( + lambda x: x[0] + x[1], list(zip(a, b))) + )) em.add_field( name="The Zen of Python says...", From 99f00f28a1879e368fb641d181de9e0ea6d1d550 Mon Sep 17 00:00:00 2001 From: Andrew Schwartz Date: Mon, 26 Mar 2018 17:02:46 -0600 Subject: [PATCH 44/44] add information about credentials --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 71b165f8..7ab38d46 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,20 @@ This is the repository for all code relating to our first code jam, in March 201 **This code jam runs from the 23rd of March to the 25th of March, measured using the UTC timezone.** Make sure you open your pull request by then. Once the deadline is up, stop pushing commits - we will not accept any submissions made after this date. +## Credentials +In order to run this bot on your own, you will need a couple credentials, stored as environment variables. Since we are using `pipenv`, we are storing these in the `.env` file in the root directory. +Your .env should look like this: +``` +BOT_TOKEN=bottoken +ACCESS_TOKEN=accesstoken +YOUTUBE_DATA_KEY=youtubekey +``` +The `BOT_TOKEN` is the Discord token. Find how to obtain this [here](https://github.com/discord-python/code-jam-1/blob/master/doc/bot-setup.md). + +The `ACCESS_TOKEN` is a token to access our data API. Get in touch with @prithajnath to receive a token. + +The `YOUTUBE_DATA_KEY` is an API key from Google. Specifically, this uses the [YouTube Data API v3](https://console.developers.google.com/apis/credentials). Generate a key here, which will be associated with your Google account. + ## APIs used * [Qwant](https://www.qwant.com/) (for snake pics) * [Our custom Snake API](https://github.com/prithajnath/snake) (scraped a page with bs4 and saved it in a db)