-
-
Notifications
You must be signed in to change notification settings - Fork 16
Team 17 #8
base: master
Are you sure you want to change the base?
Team 17 #8
Changes from 30 commits
b312786
31fe5d8
f92cf5e
d04759c
91cfb2d
6dd7aef
c865044
e830c95
80273f6
36ace30
9816692
2d743c8
b77b7fe
49f5251
f915d90
a552543
3234d57
3f56b6c
dfd3175
0ff0f42
ddc6e4e
9edf8c9
97e0e9d
adafa48
7033463
cca57c4
3f91775
b712a24
5850ddb
256d4e1
0854cb1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,24 @@ | ||
| # coding=utf-8 | ||
| import logging | ||
| from difflib import get_close_matches | ||
| from random import choice | ||
| from typing import Any, Dict | ||
|
|
||
| from aiohttp import ClientSession | ||
|
|
||
| from bs4 import BeautifulSoup | ||
|
|
||
| import discord | ||
| from discord.ext.commands import AutoShardedBot, Context, command | ||
|
|
||
| from bot.selectors import ( | ||
| ALT_IMG_SELECTOR, | ||
| DID_YOU_KNOW_SELECTOR, | ||
| SCIENTIFIC_NAME_SELECTOR, | ||
| SNAKE_IMG_SELECTOR, | ||
| SNEK_MAP_SELECTOR | ||
| ) | ||
|
|
||
| log = logging.getLogger(__name__) | ||
|
|
||
|
|
||
|
|
@@ -15,6 +30,62 @@ class Snakes: | |
| def __init__(self, bot: AutoShardedBot): | ||
| self.bot = bot | ||
|
|
||
| PYTHON_INFO = { | ||
| 'name': 'Python', | ||
| 'scientific-name': 'Pseudo anguis', | ||
| 'image-url': 'https://www.python.org/static/community_logos/python-logo-master-v3-TM.png', | ||
| 'url': 'https://en.wikipedia.org/wiki/Python_(programming_language)', | ||
| 'map-url': 'https://ih0.redbubble.net/image.80621508.8934/flat,800x800,075,t.u1.jpg', | ||
| 'description': 'Python is an interpreted high-level programming language ' | ||
| 'for general-purpose programming. ' | ||
| 'Created by Guido van Rossum and first released in 1991, ' | ||
| 'Python has a design philosophy that emphasizes code readability, ' | ||
| 'notably using significant whitespace.' | ||
| } | ||
|
|
||
| async def on_ready(self): | ||
| self.session = ClientSession(loop=self.bot.loop) | ||
| self.info_url = 'https://snake-facts.weebly.com/' | ||
| log.info('Session created.') | ||
|
|
||
| with open('./snakes.txt', encoding='utf-8') as f: | ||
| self.sneks = f.read().split('\n') | ||
| for i, snek in enumerate(self.sneks): | ||
| self.sneks[i] = snek.replace('\u200b', '').replace('\ufeff', '') | ||
| log.info('Snakes loaded.') | ||
|
|
||
| def no_sneks_found(self, name): | ||
| '''Helper function if the snake was not found in the directory.''' | ||
| em = discord.Embed( | ||
| title='No snake found.', | ||
| color=discord.Color.green() | ||
| ) | ||
|
|
||
| snakes = get_close_matches(name, self.sneks) | ||
|
|
||
| if snakes: | ||
| em.description = 'Did you mean...\n' | ||
| em.description += '\n'.join(f'`{x}`' for x in snakes) | ||
| else: | ||
| snakes = 'https://github.com/SharpBit/code-jam-1/blob/master/snakes.txt' | ||
| em.description = f'No close matches found. Click [here]({snakes}) for the list of available snakes.' | ||
|
|
||
| return em | ||
|
|
||
| def format_info(self, data, color=discord.Color.green()): | ||
| '''Formats the info with the given data.''' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not required, but some documentation for the parameters would be nice, maybe some type annotations at the least to say what |
||
| em = discord.Embed( | ||
| title=f"{data['name']} ({data['scientific-name']})", | ||
| description=data['description'], | ||
| color=color, | ||
| url=data['url'] | ||
| ) | ||
|
|
||
| em.set_image(url=data['image-url']) | ||
| em.set_thumbnail(url=data['map-url']) | ||
|
|
||
| return em | ||
|
|
||
| async def get_snek(self, name: str = None) -> Dict[str, Any]: | ||
| """ | ||
| Go online and fetch information about a snake | ||
|
|
@@ -28,9 +99,72 @@ 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 | ||
| """ | ||
| if name: | ||
| if name not in self.sneks: | ||
| return self.no_sneks_found(name) | ||
| else: | ||
| name = choice(self.sneks) | ||
|
|
||
| snake = name.lower().replace(' ', '-').replace("'", '') | ||
| url = f'{self.info_url}{snake}.html' | ||
|
|
||
| async with self.session.get(url) as resp: | ||
| info = await resp.read() | ||
| soup = BeautifulSoup(info, 'lxml') | ||
|
|
||
| for x in range(1, 7): | ||
| try: | ||
| img = soup.select(SNAKE_IMG_SELECTOR.format(x))[0]['src'] | ||
| break | ||
| except IndexError: | ||
| continue | ||
| try: | ||
| img = img[1:] | ||
| except UnboundLocalError: | ||
| img = soup.select(ALT_IMG_SELECTOR)[0]['src'][1:] | ||
|
|
||
| names = soup.find('td', class_='wsite-multicol-col') | ||
| sci_name = soup.select(SCIENTIFIC_NAME_SELECTOR)[0].text.strip() | ||
| description_tag = soup.find(attrs={'property': {'og:description'}}) | ||
|
|
||
| for x in range(1, 7): | ||
| try: | ||
| location_map = soup.select(SNEK_MAP_SELECTOR.format(x))[0]['src'] | ||
| break | ||
| except IndexError: | ||
| continue | ||
|
|
||
| info = { | ||
| 'name': names.h1.string, | ||
| 'scientific-name': sci_name, | ||
| 'image-url': f'{self.info_url}{img}', | ||
| 'map-url': f'{self.info_url}{location_map[1:]}', | ||
| 'description': description_tag['content'], | ||
| 'url': url | ||
| } | ||
|
|
||
| return info | ||
|
|
||
| async def get_snek_fact(self): | ||
| '''Helper function to get a snake fact.''' | ||
| page = choice(self.sneks).replace(' ', '-').replace("'", '') | ||
| url = f'{self.info_url}{page}.html' | ||
|
|
||
| async with self.session.get(url) as resp: | ||
| response = await resp.read() | ||
| soup = BeautifulSoup(response, 'lxml') | ||
| fact = soup.select(DID_YOU_KNOW_SELECTOR)[0].text | ||
|
|
||
| em = discord.Embed( | ||
| title='Did you know?', | ||
| description=fact[13:], | ||
| color=discord.Color.green() | ||
| ) | ||
|
|
||
| return em | ||
|
|
||
| @command() | ||
| async def get(self, ctx: Context, name: str = None): | ||
| @command(aliases=['snakes.get', 'snakes.get()', 'get()']) | ||
| async def get(self, ctx: Context, *, name: str = None): | ||
| """ | ||
| Go online and fetch information about a snake | ||
|
|
||
|
|
@@ -40,8 +174,26 @@ 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 | ||
| """ | ||
| # Sends info about the programming language | ||
| if name: | ||
| if name.lower() == 'python': | ||
| em = self.format_info(self.PYTHON_INFO, discord.Color.blurple()) | ||
| return await ctx.send(embed=em) | ||
| data = await self.get_snek(name) | ||
| # if the snake is not found | ||
| if isinstance(data, discord.Embed): | ||
| return await ctx.send(embed=data) | ||
| # format the given data | ||
| em = self.format_info(data) | ||
| await ctx.send(embed=em) | ||
|
|
||
| # Any additional commands can be placed here. Be creative, but keep it to a reasonable amount! | ||
| @command(aliases=['getsnekfact', 'snekfact()', 'get_snek_fact()']) | ||
| async def snekfact(self, ctx: Context): | ||
| ''' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, |
||
| Gets a randomsnek fact from the "Did you know?" cards | ||
| that the website has on the right hand side. | ||
| ''' | ||
| await ctx.send(embed=await self.get_snek_fact()) | ||
|
|
||
|
|
||
| def setup(bot): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| ALT_IMG_SELECTOR = ( | ||
| '#wsite-content > div:nth-of-type(2) > div > div > table > ' | ||
| 'tbody > tr > td:nth-of-type(1) > div:nth-of-type(5) > div > a > img' | ||
| ) | ||
|
|
||
| DID_YOU_KNOW_SELECTOR = ( | ||
| '#wsite-content > div:nth-of-type(2) > div > div > table > ' | ||
| 'tbody > tr > td:nth-of-type(2) > div:nth-of-type(1)' | ||
| ) | ||
|
|
||
| SNAKE_IMG_SELECTOR = ( | ||
| '#wsite-content > div:nth-of-type(2) > div > div > table > ' | ||
| 'tbody > tr > td:nth-of-type(1) > div:nth-of-type(4) > div > div > ' | ||
| 'table > tbody > tr > td:nth-of-type({0}) > div > div > a > img' | ||
| ) | ||
|
|
||
| SCIENTIFIC_NAME_SELECTOR = ( | ||
| "#wsite-content > div:nth-of-type(1) > div > div > " | ||
| "table > tbody > tr > td:nth-of-type(1) > div:nth-of-type(2)" | ||
| ) | ||
|
|
||
| SNEK_MAP_SELECTOR = ( | ||
| "#wsite-content > div:nth-of-type(2) > div > div > table > " | ||
| "tbody > tr > td:nth-of-type(2) > div:nth-of-type({0}) > div > a > img" | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,7 +16,7 @@ | |
| ">>> ", ">> ", "> ", | ||
| ">>>", ">>", ">" | ||
| ), # Order matters (and so do commas) | ||
| activity=Game(name="Help: bot.help()"), | ||
| activity=Game(name="with snekky sneks"), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably a bit user-unfriendly, but it's fine in this case. :P
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. still mentionable /shrug |
||
| help_attrs={"aliases": ["help()"]}, | ||
| formatter=Formatter() | ||
| ) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This probably shouldn't be a class variable, but a module variable declared above class declaration.