Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 15
Team 21#12
base:master
Are you sure you want to change the base?
Uh oh!
There was an error while loading. Please reload this page.
Team 21 #12
Changes from all commits
cd2958aed789160a8ecf2194b420b41ebe6e10dfd95f2435633df4d738dadc29dd9c2f8e492716be2ba0a88e00e83a1019128efbf01529d4280bfa9316b55c20c555af9cf749525adc0f844893a143ae35e7ed88676f1ecf2e5d6992bc40a9373d7969ab790e579d2abfdc89c98b8ba7f741a2ba52208a3e7f499cc8624a11be7f153973e4ce51e6c812e4c2cf32fdb7e5a33cFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
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.
Uh oh!
There was an error while loading. Please reload this page.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,21 +1,47 @@ | ||
| # coding=utf-8 | ||
| import asyncio | ||
| import logging | ||
| import random | ||
| import re | ||
| import textwrap | ||
| from typing import Any, Dict | ||
| from discord.ext.commands import AutoShardedBot, Context, command | ||
| import aiohttp | ||
| import async_timeout | ||
| import discord | ||
| from discord.ext.commands import AutoShardedBot, Context, command, bot_has_permissions | ||
| from bot.converters import Snake | ||
| from bot.decorators import locked | ||
| from bot.utils import disambiguate | ||
| log = logging.getLogger(__name__) | ||
| URL = "https://en.wikipedia.org/w/api.php?" | ||
| class Snakes: | ||
| """ | ||
| Snake-related commands | ||
| """ | ||
| # I really hope this works | ||
| wiki_sects = re.compile(r'(?:=+ (.*?) =+)(.*?\n\n)', flags=re.DOTALL) | ||
| wiki_brief = re.compile(r'(.*?)(=+ (.*?) =+)', flags=re.DOTALL) | ||
| valid = ('gif', 'png', 'jpeg', 'jpg', 'webp') | ||
| def __init__(self, bot: AutoShardedBot): | ||
| self.bot = bot | ||
| async def get_snek(self, name: str = None) -> Dict[str, Any]: | ||
| async def fetch(self, session, url, params=None): | ||
| if params is None: | ||
| params = {} | ||
| async with async_timeout.timeout(10): | ||
| async with session.get(url, params=params) as response: | ||
| return await response.json() | ||
| async def get_snek(self, name: str) -> Dict[str, Any]: | ||
| """ | ||
| Go online and fetch information about a snake | ||
| @@ -25,23 +51,191 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: | ||
| 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 | ||
| :param name: The name of the snake to get information for - omit for a random snake | ||
| :return: A dict containing information on a snake | ||
| """ | ||
| snake_info = {} | ||
| @command() | ||
| async def get(self, ctx: Context, name: str = None): | ||
| """ | ||
| Go online and fetch information about a snake | ||
| async with aiohttp.ClientSession() as session: | ||
| params = { | ||
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. These don't have to be defined each time it's called, maybe class property? | ||
| 'format': 'json', | ||
| 'action': 'query', | ||
| 'list': 'search', | ||
| 'srsearch': name, | ||
| 'utf8': '', | ||
| 'srlimit': '1', | ||
| } | ||
| json = await self.fetch(session, URL, params=params) | ||
| # wikipedia does have a error page | ||
| try: | ||
| pageid = json["query"]["search"][0]["pageid"] | ||
| except KeyError: | ||
| # Wikipedia error page ID(?) | ||
| pageid = 41118 | ||
| params = { | ||
| 'format': 'json', | ||
| 'action': 'query', | ||
| 'prop': 'extracts|images|info', | ||
| 'exlimit': 'max', | ||
| 'explaintext': '', | ||
| 'inprop': 'url', | ||
| 'pageids': pageid | ||
| } | ||
| 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. | ||
| json = await self.fetch(session, URL, params=params) | ||
| # constructing dict - handle exceptions later | ||
| try: | ||
| snake_info["title"] = json["query"]["pages"][f"{pageid}"]["title"] | ||
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. Use loops! That would clean up this hunk of code | ||
| snake_info["extract"] = json["query"]["pages"][f"{pageid}"]["extract"] | ||
| snake_info["images"] = json["query"]["pages"][f"{pageid}"]["images"] | ||
| snake_info["fullurl"] = json["query"]["pages"][f"{pageid}"]["fullurl"] | ||
| snake_info["pageid"] = json["query"]["pages"][f"{pageid}"]["pageid"] | ||
| except KeyError: | ||
| snake_info["error"] = True | ||
| if snake_info["images"]: | ||
| i_url = 'https://commons.wikimedia.org/wiki/Special:FilePath/' | ||
| image_list = [] | ||
| map_list = [] | ||
| thumb_list = [] | ||
| # Wikipedia has arbitrary images that are not snakes | ||
| banned = [ | ||
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. Once again, hard coded lists should be moved to somewhere such as a class variable | ||
| 'Commons-logo.svg', | ||
| 'Red%20Pencil%20Icon.png', | ||
| 'distribution', | ||
| 'The%20Death%20of%20Cleopatra%20arthur.jpg', | ||
| 'Head%20of%20holotype', | ||
| 'locator', | ||
| 'Woma.png', | ||
| '-map.', | ||
| '.svg', | ||
| 'ange.', | ||
| 'Adder%20(PSF).png' | ||
| ] | ||
| for image in snake_info["images"]: | ||
| # images come in the format of `File:filename.extension` | ||
| file, sep, filename = image["title"].partition(':') | ||
| filename = filename.replace(" ", "%20") # Wikipedia returns good data! | ||
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. Use the built-in urllib for this! | ||
| if not filename.startswith('Map'): | ||
| if any(ban in filename for ban in banned): | ||
| log.info("the image is banned") | ||
| else: | ||
| image_list.append(f"{i_url}{filename}") | ||
| thumb_list.append(f"{i_url}{filename}?width=100") | ||
| else: | ||
| map_list.append(f"{i_url}{filename}") | ||
| snake_info["image_list"] = image_list | ||
| snake_info["map_list"] = map_list | ||
| snake_info["thumb_list"] = thumb_list | ||
| return snake_info | ||
| @command(name="snakes.get()", aliases=["snakes.get"]) | ||
| @bot_has_permissions(manage_messages=True) | ||
| @locked() | ||
| async def get(self, ctx: Context, name: Snake = None): | ||
| """ | ||
| Fetches information about a snake from Wikipedia. | ||
| :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 | ||
| """ | ||
| if name is None: | ||
| name = Snake.random() | ||
| data = await self.get_snek(name) | ||
| if data.get('error'): | ||
| return await ctx.send('Could not fetch data from Wikipedia.') | ||
| match = self.wiki_brief.match(data['extract']) | ||
| embed = discord.Embed( | ||
| title=data['title'], | ||
| description=match.group(1) if match else None, | ||
| url=data['fullurl'], | ||
| colour=0x59982F | ||
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. What colour is this? Is it not included in the 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. No it's just a random green I found online | ||
| ) | ||
| fields = self.wiki_sects.findall(data['extract']) | ||
| excluded = ('see also', 'further reading', 'subspecies') | ||
| for title, body in fields: | ||
| if title.lower() in excluded: | ||
| continue | ||
| if not body.strip(): | ||
| continue | ||
| # Only takes the first sentence | ||
| title, dot, _ = title.partition('.') | ||
| # There's probably a better way to do this | ||
| value = textwrap.shorten(body.strip(), width=200) | ||
| embed.add_field(name=title + dot, value=value + '\n\u200b', inline=False) | ||
| embed.set_footer(text='Powered by Wikipedia') | ||
| emoji = 'https://emojipedia-us.s3.amazonaws.com/thumbs/60/google/3/snake_1f40d.png' | ||
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. Yak! Hard coded value inside the function again. Same issue as the list and dicts. | ||
| image = next((url for url in data['image_list'] if url.endswith(self.valid)), emoji) | ||
| embed.set_thumbnail(url=image) | ||
| await ctx.send(embed=embed) | ||
| @command(hidden=True) | ||
| async def zen(self, ctx): | ||
| """ | ||
| >>> import this | ||
| Long time Pythoneer Tim Peters succinctly channels the BDFL's guiding principles | ||
| for Python's design into 20 aphorisms, only 19 of which have been written down. | ||
| You must be connected to a voice channel in order to use this command. | ||
| """ | ||
| channel = ctx.author.voice.channel | ||
| if channel is None: | ||
| return | ||
| state = ctx.guild.voice_client | ||
| if state is not None: | ||
| # Already playing | ||
| return | ||
| voice = await channel.connect() | ||
| source = discord.FFmpegPCMAudio('zen.mp3') | ||
| voice.play(source, after=lambda *args: asyncio.run_coroutine_threadsafe( | ||
| voice.disconnect(), loop=ctx.bot.loop | ||
| )) | ||
| @command(name="snakes.guess()", aliases=["snakes.guess", "identify"]) | ||
| @locked() | ||
| async def guess(self, ctx): | ||
| """ | ||
| Snake identifying game! | ||
| """ | ||
| image = None | ||
| while image is None: | ||
| snakes = [Snake.random() for _ in range(5)] | ||
| answer = random.choice(snakes) | ||
| data = await self.get_snek(answer) | ||
| image = next((url for url in data['image_list'] if url.endswith(self.valid)), None) | ||
| embed = discord.Embed( | ||
| title='Which of the following is the snake in the image?', | ||
| colour=random.randint(1, 0xFFFFFF) | ||
| ) | ||
| embed.set_image(url=image) | ||
| guess = await disambiguate(ctx, snakes, timeout=60, embed=embed) | ||
| # Any additional commands can be placed here. Be creative, but keep it to a reasonable amount! | ||
| if guess == answer: | ||
| return await ctx.send('You guessed correctly!') | ||
| await ctx.send(f'You guessed wrong. The correct answer was {answer}.') | ||
| def setup(bot): | ||
Uh oh!
There was an error while loading. Please reload this page.
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.
Why remove what it returns? That's great documenting!