From 66897bcea7a4915ab3af8d1b80141fdba6256bdf Mon Sep 17 00:00:00 2001 From: Eli Date: Thu, 22 Mar 2018 22:48:35 -0700 Subject: [PATCH 01/11] Basic info fetching --- bot/cogs/snakes.py | 55 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 8 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index c9ed8042..0aa09808 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -1,10 +1,18 @@ # coding=utf-8 import logging -from typing import Any, Dict +import re -from discord.ext.commands import AutoShardedBot, Context, command +import aiohttp +import bs4 +import discord +import html2text +from discord.ext import commands log = logging.getLogger(__name__) +API = 'http://en.wikipedia.org/w/api.php?format=json&redirects=1&action=' + +rSENTENCE = re.compile(r'^.+?\.') +rBRACK = re.compile(r'[[(].+?[\])]') class Snakes: @@ -12,10 +20,16 @@ class Snakes: Snake-related commands """ - def __init__(self, bot: AutoShardedBot): + def __init__(self, bot): self.bot = bot + self.aexec = bot.loop.run_in_executor + self.session = aiohttp.ClientSession(loop=bot.loop) + self.h2md = html2text.HTML2Text() # TODO: use + self.base_query = API + 'parse&prop=text&page={}' + self.secs_query = API + 'parse&prop=sections&page={}' + self.img_query = API + 'query&titles={}&prop=pageimages&pithumbsize=300' - async def get_snek(self, name: str = None) -> Dict[str, Any]: + async def get_snek(self, name=None): """ Go online and fetch information about a snake @@ -28,9 +42,32 @@ 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 """ + # TODO: Random will be done by fetching from Special:RandomInCategory/Venomous_snakes, or something + async with self.session.get(self.base_query.format(name)) as pg_resp, \ + self.session.get(self.secs_query.format(name)) as sc_resp, \ + self.session.get(self.img_query.format(name)) as img_resp: + data = await pg_resp.json() + secs = await sc_resp.json() + img = await img_resp.json() + tidbits = [] + soup = bs4.BeautifulSoup(data['parse']['text']['*']) + for section in secs['parse']['sections']: + for tag in await self.aexec(None, soup.find(id=section['anchor']).find_all_next): # FIXME: inefficient...? + if tag.name == 'p': + try: + tidbits.append(rBRACK.sub('', rSENTENCE.match(tag.text)[0])) + except TypeError: + pass + break + try: + pgid = str(data['parse']['pageid']) + imglink = img['query']['pages'][pgid]['thumbnail']['source'] + except KeyError: + imglink = '' + return {'image': imglink, 'tidbits': tidbits} - @command() - async def get(self, ctx: Context, name: str = None): + @commands.command() + async def get(self, ctx, name: str.title = None): """ Go online and fetch information about a snake @@ -40,8 +77,10 @@ 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 """ - - # Any additional commands can be placed here. Be creative, but keep it to a reasonable amount! + d = await self.get_snek(name) + embed = discord.Embed(description='\n\n • '.join(d['tidbits'])) + embed.set_thumbnail(url=d['image']) + await ctx.send(embed=embed) def setup(bot): From 0fcaccd26fa6f6ab6b1c6c0fc77a7702fa6a55b1 Mon Sep 17 00:00:00 2001 From: Eli Date: Thu, 22 Mar 2018 22:48:48 -0700 Subject: [PATCH 02/11] Update Pipfile --- Pipfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Pipfile b/Pipfile index d33997f7..e5fc9f6e 100644 --- a/Pipfile +++ b/Pipfile @@ -5,7 +5,10 @@ name = "pypi" [packages] "72eb2aa" = {file = "https://github.com/Rapptz/discord.py/archive/rewrite.zip"} +aiohttp = "*" aiodns = "*" +bs4 = "*" +html2text = "*" [dev-packages] "flake8" = "*" From 72c5e2783e66ac893e8f4e43398059c410513792 Mon Sep 17 00:00:00 2001 From: Eli Date: Fri, 23 Mar 2018 22:13:52 -0700 Subject: [PATCH 03/11] Re-add typehints, fix bs4 inefficiency and dupes --- Pipfile.lock | 300 --------------------------------------------- bot/cogs/snakes.py | 38 +++--- 2 files changed, 19 insertions(+), 319 deletions(-) delete mode 100644 Pipfile.lock diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index ecadf33b..00000000 --- a/Pipfile.lock +++ /dev/null @@ -1,300 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "f0781ccdf990671b894d7c309cb9394e50f6129e06a50be42a8b9fb4621787c7" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.6" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.python.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "72eb2aa": { - "file": "https://github.com/Rapptz/discord.py/archive/rewrite.zip" - }, - "aiodns": { - "hashes": [ - "sha256:99d0652f2c02f73bfa646bf44af82705260a523014576647d7959e664830b26b", - "sha256:d8677adc679ce8d0ef706c14d9c3d2f27a0e0cc11d59730cdbaf218ad52dd9ea" - ], - "index": "pypi", - "version": "==1.1.1" - }, - "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:8ea263de8bf1a30b0d87150b4aa0e3203cf93bc1723ea3e7408a7d25e1299217", - "sha256:943e2dc67ff45ab4c81d628c959837d01561d7e185080ab7a276b8ca67573fb5", - "sha256:9d56a54c93e64b30c0d31f394d9890f175edec029cd846221728f99263cdee82", - "sha256:b95b339c11d824f0bb789d31b91c8534916fcbdce248cccce216fa2630bb8a90", - "sha256:bbfd9aba1e172cd2ab7b7142d49b28cf44d6451c4a66a870aff1dc3cb84849c7", - "sha256:d8637bcc2f901aa61ec1d754abc862f9f145cb0346a0249360df4c159377018e", - "sha256:e2446577eeea79d2179c9469d9d4ce3ab8a07d7985465c3cb91e7d74abc329b6", - "sha256:e72fa163f37ae3b09f143cc6690a36f012d13e905d142e1beed4ec0e593ff657", - "sha256:f32b7c63094749fbc0c1106c9a785666ec8afd49ecfe7002a30bb7c42e62b47c", - "sha256:f50be4dd53f009cfb4b98c3c6b240e18ff9b17e3f1c320bd594bb83eddabfcb2" - ], - "version": "==2.3.0" - } - }, - "develop": { - "attrs": { - "hashes": [ - "sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9", - "sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450" - ], - "version": "==17.4.0" - }, - "bandit": { - "hashes": [ - "sha256:cb977045497f83ec3a02616973ab845c829cdab8144ce2e757fe031104a9abd4", - "sha256:de4cc19d6ba32d6f542c6a1ddadb4404571347d83ef1ed1e7afb7d0b38e0c25b" - ], - "version": "==1.4.0" - }, - "certifi": { - "hashes": [ - "sha256:14131608ad2fd56836d33a71ee60fa1c82bc9d2c8d98b7bdbc631fe1b3cd1296", - "sha256:edbc3f203427eef571f79a7692bb160a2b0f7ccaa31953e99bd17e307cf63f7d" - ], - "version": "==2018.1.18" - }, - "chardet": { - "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" - ], - "version": "==3.0.4" - }, - "click": { - "hashes": [ - "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d", - "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b" - ], - "version": "==6.7" - }, - "dodgy": { - "hashes": [ - "sha256:65e13cf878d7aff129f1461c13cb5fd1bb6dfe66bb5327e09379c3877763280c" - ], - "index": "pypi", - "version": "==0.1.9" - }, - "dparse": { - "hashes": [ - "sha256:7c9f9175d8fd83aed6d31a16c1a3ba4c38189120f1df416b46029d940b4ef582", - "sha256:e4b479dd4d6078ba5f087b28447a50eee0caed57f135b0f5a5e5d5024390f41d" - ], - "version": "==0.2.1" - }, - "flake8": { - "hashes": [ - "sha256:7253265f7abd8b313e3892944044a365e3f4ac3fcdcfb4298f55ee9ddf188ba0", - "sha256:c7841163e2b576d435799169b78703ad6ac1bbb0f199994fc05f700b2a90ea37" - ], - "index": "pypi", - "version": "==3.5.0" - }, - "flake8-bandit": { - "hashes": [ - "sha256:24572dc2181c967bec1e7767c1a1faa4921f3d5a4cf9f84a78e2a555ca6c442a", - "sha256:345f5e61cd5eaf354156d7b45173fd14f6e31d35391d884c3222597a363c61ca" - ], - "index": "pypi", - "version": "==1.0.1" - }, - "flake8-bugbear": { - "hashes": [ - "sha256:541746f0f3b2f1a8d7278e1d2d218df298996b60b02677708560db7c7e620e3b", - "sha256:5f14a99d458e29cb92be9079c970030e0dd398b2decb179d76d39a5266ea1578" - ], - "index": "pypi", - "version": "==18.2.0" - }, - "flake8-import-order": { - "hashes": [ - "sha256:40d2a39ed91e080f3285f4c16256b252d7c31070e7f11b7854415bb9f924ea81", - "sha256:68d430781a9ef15c85a0121500cf8462f1a4bc7672acb2a32bfdbcab044ae0b7" - ], - "index": "pypi", - "version": "==0.17.1" - }, - "flake8-polyfill": { - "hashes": [ - "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9", - "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda" - ], - "version": "==1.0.2" - }, - "flake8-string-format": { - "hashes": [ - "sha256:68ea72a1a5b75e7018cae44d14f32473c798cf73d75cbaed86c6a9a907b770b2", - "sha256:774d56103d9242ed968897455ef49b7d6de272000cfa83de5814273a868832f1" - ], - "index": "pypi", - "version": "==0.2.3" - }, - "flake8-tidy-imports": { - "hashes": [ - "sha256:5fc28c82bba16abb4f1154dc59a90487f5491fbdb27e658cbee241e8fddc1b91", - "sha256:c05c9f7dadb5748a04b6fa1c47cb6ae5a8170f03cfb1dca8b37aec58c1ee6d15" - ], - "index": "pypi", - "version": "==1.1.0" - }, - "flake8-todo": { - "hashes": [ - "sha256:6e4c5491ff838c06fe5a771b0e95ee15fc005ca57196011011280fc834a85915" - ], - "index": "pypi", - "version": "==0.7" - }, - "gitdb2": { - "hashes": [ - "sha256:b60e29d4533e5e25bb50b7678bbc187c8f6bcff1344b4f293b2ba55c85795f09", - "sha256:cf9a4b68e8c4da8d42e48728c944ff7af2d8c9db303ac1ab32eac37aa4194b0e" - ], - "version": "==2.0.3" - }, - "gitpython": { - "hashes": [ - "sha256:ad61bc25deadb535b047684d06f3654c001d9415e1971e51c9c20f5b510076e9", - "sha256:b8367c432de995dc330b5b146c5bfdc0926b8496e100fda6692134e00c0dcdc5" - ], - "version": "==2.1.8" - }, - "idna": { - "hashes": [ - "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f", - "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4" - ], - "version": "==2.6" - }, - "mccabe": { - "hashes": [ - "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", - "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" - ], - "version": "==0.6.1" - }, - "packaging": { - "hashes": [ - "sha256:e9215d2d2535d3ae866c3d6efc77d5b24a0192cce0ff20e42896cc0664f889c0", - "sha256:f019b770dd64e585a99714f1fd5e01c7a8f11b45635aa953fd41c689a657375b" - ], - "version": "==17.1" - }, - "pbr": { - "hashes": [ - "sha256:05f61c71aaefc02d8e37c0a3eeb9815ff526ea28b3b76324769e6158d7f95be1", - "sha256:60c25b7dfd054ef9bb0ae327af949dd4676aa09ac3a9471cdc871d8a9213f9ac" - ], - "version": "==3.1.1" - }, - "pycodestyle": { - "hashes": [ - "sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766", - "sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9" - ], - "version": "==2.3.1" - }, - "pyflakes": { - "hashes": [ - "sha256:08bd6a50edf8cffa9fa09a463063c425ecaaf10d1eb0335a7e8b1401aef89e6f", - "sha256:8d616a382f243dbf19b54743f280b80198be0bca3a5396f1d2e1fca6223e8805" - ], - "version": "==1.6.0" - }, - "pyparsing": { - "hashes": [ - "sha256:0832bcf47acd283788593e7a0f542407bd9550a55a8a8435214a1960e04bcb04", - "sha256:281683241b25fe9b80ec9d66017485f6deff1af5cde372469134b56ca8447a07", - "sha256:8f1e18d3fd36c6795bb7e02a39fd05c611ffc2596c1e0d995d34d67630426c18", - "sha256:9e8143a3e15c13713506886badd96ca4b579a87fbdf49e550dbfc057d6cb218e", - "sha256:b8b3117ed9bdf45e14dcc89345ce638ec7e0e29b2b579fa1ecf32ce45ebac8a5", - "sha256:e4d45427c6e20a59bf4f88c639dcc03ce30d193112047f94012102f235853a58", - "sha256:fee43f17a9c4087e7ed1605bd6df994c6173c1e977d7ade7b651292fab2bd010" - ], - "version": "==2.2.0" - }, - "pyyaml": { - "hashes": [ - "sha256:0c507b7f74b3d2dd4d1322ec8a94794927305ab4cebbe89cc47fe5e81541e6e8", - "sha256:16b20e970597e051997d90dc2cddc713a2876c47e3d92d59ee198700c5427736", - "sha256:3262c96a1ca437e7e4763e2843746588a965426550f3797a79fca9c6199c431f", - "sha256:326420cbb492172dec84b0f65c80942de6cedb5233c413dd824483989c000608", - "sha256:4474f8ea030b5127225b8894d626bb66c01cda098d47a2b0d3429b6700af9fd8", - "sha256:592766c6303207a20efc445587778322d7f73b161bd994f227adaa341ba212ab", - "sha256:5ac82e411044fb129bae5cfbeb3ba626acb2af31a8d17d175004b70862a741a7", - "sha256:5f84523c076ad14ff5e6c037fe1c89a7f73a3e04cf0377cb4d017014976433f3", - "sha256:827dc04b8fa7d07c44de11fabbc888e627fa8293b695e0f99cb544fdfa1bf0d1", - "sha256:b4c423ab23291d3945ac61346feeb9a0dc4184999ede5e7c43e1ffb975130ae6", - "sha256:bc6bced57f826ca7cb5125a10b23fd0f2fff3b7c4701d64c439a300ce665fff8", - "sha256:c01b880ec30b5a6e6aa67b09a2fe3fb30473008c85cd6a67359a1b15ed6d83a4", - "sha256:ca233c64c6e40eaa6c66ef97058cdc80e8d0157a443655baa1b2966e812807ca", - "sha256:e863072cdf4c72eebf179342c94e6989c67185842d9997960b3e69290b2fa269" - ], - "version": "==3.12" - }, - "requests": { - "hashes": [ - "sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b", - "sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e" - ], - "version": "==2.18.4" - }, - "safety": { - "hashes": [ - "sha256:9fb74211a0a0ab09541fe894293d66a558b6138a9fe8ebabc8cf56670e8a009c", - "sha256:ff0c4b76ad791d33825e36c41671ea45330d438921e5395903c0e87e576a377a" - ], - "index": "pypi", - "version": "==1.7.0" - }, - "six": { - "hashes": [ - "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", - "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" - ], - "version": "==1.11.0" - }, - "smmap2": { - "hashes": [ - "sha256:b78ee0f1f5772d69ff50b1cbdb01b8c6647a8354f02f23b488cf4b2cfc923956", - "sha256:c7530db63f15f09f8251094b22091298e82bf6c699a6b8344aaaef3f2e1276c3" - ], - "version": "==2.0.3" - }, - "stevedore": { - "hashes": [ - "sha256:e3d96b2c4e882ec0c1ff95eaebf7b575a779fd0ccb4c741b9832bed410d58b3d", - "sha256:f1c7518e7b160336040fee272174f1f7b29a46febb3632502a8f2055f973d60b" - ], - "version": "==1.28.0" - }, - "urllib3": { - "hashes": [ - "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b", - "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f" - ], - "version": "==1.22" - } - } -} diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 0aa09808..89088a34 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -1,16 +1,18 @@ # coding=utf-8 import logging import re +from typing import Any, Dict, List import aiohttp import bs4 import discord import html2text from discord.ext import commands +from discord.ext.commands import Context log = logging.getLogger(__name__) -API = 'http://en.wikipedia.org/w/api.php?format=json&redirects=1&action=' +API = 'http://en.wikipedia.org/w/api.php?format=json&redirects=1&action=' rSENTENCE = re.compile(r'^.+?\.') rBRACK = re.compile(r'[[(].+?[\])]') @@ -20,16 +22,15 @@ class Snakes: Snake-related commands """ - def __init__(self, bot): + def __init__(self, bot: commands.AutoShardedBot): self.bot = bot - self.aexec = bot.loop.run_in_executor - self.session = aiohttp.ClientSession(loop=bot.loop) + self.session = aiohttp.ClientSession(loop=bot.loop) # the provided session says no host is reachable self.h2md = html2text.HTML2Text() # TODO: use self.base_query = API + 'parse&prop=text&page={}' self.secs_query = API + 'parse&prop=sections&page={}' self.img_query = API + 'query&titles={}&prop=pageimages&pithumbsize=300' - async def get_snek(self, name=None): + async def get_snek(self, name: str = None) -> Dict[str, Any]: """ Go online and fetch information about a snake @@ -45,29 +46,27 @@ async def get_snek(self, name=None): # TODO: Random will be done by fetching from Special:RandomInCategory/Venomous_snakes, or something async with self.session.get(self.base_query.format(name)) as pg_resp, \ self.session.get(self.secs_query.format(name)) as sc_resp, \ - self.session.get(self.img_query.format(name)) as img_resp: + self.session.get(self.img_query.format(name)) as img_resp: # noqa data = await pg_resp.json() secs = await sc_resp.json() img = await img_resp.json() - tidbits = [] soup = bs4.BeautifulSoup(data['parse']['text']['*']) + tidbits = [] for section in secs['parse']['sections']: - for tag in await self.aexec(None, soup.find(id=section['anchor']).find_all_next): # FIXME: inefficient...? - if tag.name == 'p': - try: - tidbits.append(rBRACK.sub('', rSENTENCE.match(tag.text)[0])) - except TypeError: - pass - break + tag = soup.find(id=section['anchor']).find_next('p') + try: + tidbits.append(rBRACK.sub('', rSENTENCE.match(tag.text).group())) + except AttributeError: + pass try: - pgid = str(data['parse']['pageid']) - imglink = img['query']['pages'][pgid]['thumbnail']['source'] + pg_id = str(data['parse']['pageid']) + imglink = img['query']['pages'][pg_id]['thumbnail']['source'] except KeyError: - imglink = '' + imglink = None return {'image': imglink, 'tidbits': tidbits} @commands.command() - async def get(self, ctx, name: str.title = None): + async def get(self, ctx: Context, name: str = None): """ Go online and fetch information about a snake @@ -79,7 +78,8 @@ async def get(self, ctx, name: str.title = None): """ d = await self.get_snek(name) embed = discord.Embed(description='\n\n • '.join(d['tidbits'])) - embed.set_thumbnail(url=d['image']) + if d['image'] is not None: + embed.set_thumbnail(url=d['image']) await ctx.send(embed=embed) From 60ea12a6ee9c78537f68d018d4bd3c5d59c30218 Mon Sep 17 00:00:00 2001 From: Eli Date: Fri, 23 Mar 2018 22:27:33 -0700 Subject: [PATCH 04/11] Impl proper markdown --- bot/cogs/snakes.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 89088a34..9a55abd2 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -53,11 +53,13 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: soup = bs4.BeautifulSoup(data['parse']['text']['*']) tidbits = [] for section in secs['parse']['sections']: - tag = soup.find(id=section['anchor']).find_next('p') + tag = rBRACK.sub('', str(soup.find(id=section['anchor']).find_next('p'))) try: - tidbits.append(rBRACK.sub('', rSENTENCE.match(tag.text).group())) + tidbits.append(self.h2md.handle(rSENTENCE.match(tag).group()).replace('\n', ' ')) except AttributeError: pass + if sum(map(len, tidbits)) > 1500: + break try: pg_id = str(data['parse']['pageid']) imglink = img['query']['pages'][pg_id]['thumbnail']['source'] From c56db82c3308d3d8be42c327758140ef5659a417 Mon Sep 17 00:00:00 2001 From: Eli Date: Fri, 23 Mar 2018 23:41:46 -0700 Subject: [PATCH 05/11] Input validation --- bot/cogs/snakes.py | 66 ++++++++++++++++++++++++++++++---------------- bot/hardcodings.py | 42 +++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 22 deletions(-) create mode 100644 bot/hardcodings.py diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 9a55abd2..80d91abd 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -1,7 +1,7 @@ # coding=utf-8 import logging import re -from typing import Any, Dict, List +from typing import Any, Dict import aiohttp import bs4 @@ -10,11 +10,14 @@ from discord.ext import commands from discord.ext.commands import Context +from ..hardcodings import categories + log = logging.getLogger(__name__) -API = 'http://en.wikipedia.org/w/api.php?format=json&redirects=1&action=' +API = 'https://en.wikipedia.org/w/api.php?format=json&redirects=1&action=' rSENTENCE = re.compile(r'^.+?\.') rBRACK = re.compile(r'[[(].+?[\])]') +rMDLINK = re.compile(r'(\[.*?\])\((\S+?)\s".*?"\)') class Snakes: @@ -25,10 +28,19 @@ class Snakes: def __init__(self, bot: commands.AutoShardedBot): self.bot = bot self.session = aiohttp.ClientSession(loop=bot.loop) # the provided session says no host is reachable - self.h2md = html2text.HTML2Text() # TODO: use - self.base_query = API + 'parse&prop=text&page={}' - self.secs_query = API + 'parse&prop=sections&page={}' - self.img_query = API + 'query&titles={}&prop=pageimages&pithumbsize=300' + self.h2md = html2text.HTML2Text() + self.base_query = API + ( + 'parse' + '&page={}' + '&prop=text|sections' + ) + self.info_query = API + ( + 'query' + '&titles={}' + '&prop=pageimages|categories' + '&pithumbsize=300' + f'&cllimit=max&clcategories={categories}' + ) async def get_snek(self, name: str = None) -> Dict[str, Any]: """ @@ -45,27 +57,33 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: """ # TODO: Random will be done by fetching from Special:RandomInCategory/Venomous_snakes, or something async with self.session.get(self.base_query.format(name)) as pg_resp, \ - self.session.get(self.secs_query.format(name)) as sc_resp, \ - self.session.get(self.img_query.format(name)) as img_resp: # noqa + self.session.get(self.info_query.format(name)) as if_resp: # noqa data = await pg_resp.json() - secs = await sc_resp.json() - img = await img_resp.json() + info = await if_resp.json() + + pg_id = str(data['parse']['pageid']) + pg_info = info['query']['pages'][pg_id] + if 'categories' not in pg_info: + raise ValueError("This doesn't appear to be a snake!") soup = bs4.BeautifulSoup(data['parse']['text']['*']) tidbits = [] - for section in secs['parse']['sections']: + for section in data['parse']['sections']: + if sum(map(len, tidbits)) > 1500: + break tag = rBRACK.sub('', str(soup.find(id=section['anchor']).find_next('p'))) try: - tidbits.append(self.h2md.handle(rSENTENCE.match(tag).group()).replace('\n', ' ')) + tidbit = self.h2md.handle(rSENTENCE.match(tag).group()).replace('\n', ' ') except AttributeError: pass - if sum(map(len, tidbits)) > 1500: - break + else: + tidbits.append(rMDLINK.sub(lambda m: f'{m[1]}(https://en.wikipedia.org{m[2]})', tidbit)) try: - pg_id = str(data['parse']['pageid']) - imglink = img['query']['pages'][pg_id]['thumbnail']['source'] + img_url = pg_info['thumbnail']['source'] except KeyError: - imglink = None - return {'image': imglink, 'tidbits': tidbits} + img_url = None + title = data['parse']['title'] + pg_url = f'https://en.wikipedia.org/wiki/{title.replace(" ", "_")}' + return {'info': (img_url, pg_url, title), 'tidbits': tidbits} @commands.command() async def get(self, ctx: Context, name: str = None): @@ -78,10 +96,14 @@ 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 """ - d = await self.get_snek(name) - embed = discord.Embed(description='\n\n • '.join(d['tidbits'])) - if d['image'] is not None: - embed.set_thumbnail(url=d['image']) + try: + snek = await self.get_snek(name) + except ValueError as e: + return await ctx.send(f'`{e}`') + image, page, title = snek['info'] + embed = discord.Embed(title=title, url=page, description='\n\n • '.join(snek['tidbits'])) + if image is not None: + embed.set_thumbnail(url=image) await ctx.send(embed=embed) diff --git a/bot/hardcodings.py b/bot/hardcodings.py new file mode 100644 index 00000000..e2231156 --- /dev/null +++ b/bot/hardcodings.py @@ -0,0 +1,42 @@ +#encoding: utf-8 + +categories = '|'.join( + f'Category:{s}' for s in [ + 'Acrochordidae', + 'Alethinophidia', + 'Aniliidae', + 'Anomalepidae', + 'Anomochilidae', + 'Boidae', + 'Bolyeriidae', + 'Colubrids', + 'Colubrid_stubs', + 'Crotalinae', + 'Crotalis', + 'Cylindrophiidae', + 'Elapidae', + 'Gerrhopilidae', + 'Homalopsidae', + 'Lamprophiidae', + 'Leptotyphlopidae', + 'Loxocemidae', + 'Mambas', + 'Pareidae', + 'Pythonidae', + 'Snakes', + 'Snake_families', + 'Snake_genera', + 'Snake_stubs', + 'Thamnophis', + 'Tropidophiidae', + 'Typhlopidae', + 'Uropeltidae', + 'Venomous_snakes', + 'Viperidae', + 'Viperinae', + 'Xenodermidae', + 'Xenopeltidae', + 'Xenophidiidae', + 'Xenotyphlopidae', + ] +) From a8d59f7dd3a5181bc123e75ef668b194c950da45 Mon Sep 17 00:00:00 2001 From: Eli Date: Sat, 24 Mar 2018 12:39:15 -0700 Subject: [PATCH 06/11] Impl random snek fetching --- bot/cogs/snakes.py | 39 ++++++++++++++++++++++------ bot/{hardcodings.py => hardcoded.py} | 8 +++--- 2 files changed, 35 insertions(+), 12 deletions(-) rename bot/{hardcodings.py => hardcoded.py} (92%) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 80d91abd..b5e96810 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -1,5 +1,7 @@ # coding=utf-8 +import asyncio import logging +import random import re from typing import Any, Dict @@ -10,16 +12,21 @@ from discord.ext import commands from discord.ext.commands import Context -from ..hardcodings import categories +from .. import hardcoded log = logging.getLogger(__name__) -API = 'https://en.wikipedia.org/w/api.php?format=json&redirects=1&action=' +WKPD = 'https://en.wikipedia.org' +API = WKPD + '/w/api.php?format=json&redirects=1&action=' rSENTENCE = re.compile(r'^.+?\.') rBRACK = re.compile(r'[[(].+?[\])]') rMDLINK = re.compile(r'(\[.*?\])\((\S+?)\s".*?"\)') +class BadSnake(ValueError): + pass + + class Snakes: """ Snake-related commands @@ -39,9 +46,23 @@ def __init__(self, bot: commands.AutoShardedBot): '&titles={}' '&prop=pageimages|categories' '&pithumbsize=300' - f'&cllimit=max&clcategories={categories}' + f"&cllimit=max&clcategories={'|'.join(hardcoded.categories)}" ) + async def get_rand_snek(self, category: str = None): + """ + Follow wikipedia's Special:RandomInCategory to grab the name of a random snake. + """ + if category is None: + category = random.choice(hardcoded.categories) + while True: + async with self.session.get(f"{WKPD}/wiki/Special:RandomInCategory/{category}") as resp: + *_, name = resp.url.path.split('/') + if 'Category:' not in name: # Sometimes is a subcategory instead of an article + break + await asyncio.sleep(1) # hmm + return name + async def get_snek(self, name: str = None) -> Dict[str, Any]: """ Go online and fetch information about a snake @@ -55,7 +76,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 """ - # TODO: Random will be done by fetching from Special:RandomInCategory/Venomous_snakes, or something + if name is None: + name = await self.get_rand_snek() + async with self.session.get(self.base_query.format(name)) as pg_resp, \ self.session.get(self.info_query.format(name)) as if_resp: # noqa data = await pg_resp.json() @@ -64,7 +87,7 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: pg_id = str(data['parse']['pageid']) pg_info = info['query']['pages'][pg_id] if 'categories' not in pg_info: - raise ValueError("This doesn't appear to be a snake!") + raise BadSnake("This doesn't appear to be a snake!") soup = bs4.BeautifulSoup(data['parse']['text']['*']) tidbits = [] for section in data['parse']['sections']: @@ -76,13 +99,13 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: except AttributeError: pass else: - tidbits.append(rMDLINK.sub(lambda m: f'{m[1]}(https://en.wikipedia.org{m[2]})', tidbit)) + tidbits.append(rMDLINK.sub(lambda m: f'{m[1]}({WKPD}{m[2]})', tidbit)) try: img_url = pg_info['thumbnail']['source'] except KeyError: img_url = None title = data['parse']['title'] - pg_url = f'https://en.wikipedia.org/wiki/{title.replace(" ", "_")}' + pg_url = f"{WKPD}/wiki/{title.replace(' ', '_')}" return {'info': (img_url, pg_url, title), 'tidbits': tidbits} @commands.command() @@ -98,7 +121,7 @@ async def get(self, ctx: Context, name: str = None): """ try: snek = await self.get_snek(name) - except ValueError as e: + except BadSnake as e: return await ctx.send(f'`{e}`') image, page, title = snek['info'] embed = discord.Embed(title=title, url=page, description='\n\n • '.join(snek['tidbits'])) diff --git a/bot/hardcodings.py b/bot/hardcoded.py similarity index 92% rename from bot/hardcodings.py rename to bot/hardcoded.py index e2231156..b38f1262 100644 --- a/bot/hardcodings.py +++ b/bot/hardcoded.py @@ -1,7 +1,7 @@ #encoding: utf-8 -categories = '|'.join( - f'Category:{s}' for s in [ +categories = [ + f'Category:{s}' for s in ( 'Acrochordidae', 'Alethinophidia', 'Aniliidae', @@ -38,5 +38,5 @@ 'Xenopeltidae', 'Xenophidiidae', 'Xenotyphlopidae', - ] -) + ) +] From 3203bc0cd889b1096af2e5b14d44374d6fe76f85 Mon Sep 17 00:00:00 2001 From: Eli Date: Sat, 24 Mar 2018 12:39:27 -0700 Subject: [PATCH 07/11] Implement snake emoji --- 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 b5e96810..9c164655 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -106,7 +106,7 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: img_url = None title = data['parse']['title'] pg_url = f"{WKPD}/wiki/{title.replace(' ', '_')}" - return {'info': (img_url, pg_url, title), 'tidbits': tidbits} + return {'🐍': (img_url, pg_url, title), 'tidbits': tidbits} @commands.command() async def get(self, ctx: Context, name: str = None): @@ -123,7 +123,7 @@ async def get(self, ctx: Context, name: str = None): snek = await self.get_snek(name) except BadSnake as e: return await ctx.send(f'`{e}`') - image, page, title = snek['info'] + image, page, title = snek['🐍'] embed = discord.Embed(title=title, url=page, description='\n\n • '.join(snek['tidbits'])) if image is not None: embed.set_thumbnail(url=image) From d3661d0d5cb5e73bc3cb51f7fd85e1d84cac322b Mon Sep 17 00:00:00 2001 From: Eli Date: Sun, 25 Mar 2018 14:33:36 -0700 Subject: [PATCH 08/11] Properly update Pipfile & restore pipenv lock --- Pipfile | 14 +- Pipfile.lock | 484 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 495 insertions(+), 3 deletions(-) create mode 100644 Pipfile.lock diff --git a/Pipfile b/Pipfile index e5fc9f6e..26e71409 100644 --- a/Pipfile +++ b/Pipfile @@ -1,16 +1,22 @@ [[source]] + url = "https://pypi.python.org/simple" verify_ssl = true name = "pypi" + [packages] + "72eb2aa" = {file = "https://github.com/Rapptz/discord.py/archive/rewrite.zip"} -aiohttp = "*" aiodns = "*" -bs4 = "*" -html2text = "*" +aiohttp = "<2.3.0,>=2.0.0" +websockets = ">=4.0,<5.0" +"html2text" = "*" +"bs4" = "*" + [dev-packages] + "flake8" = "*" "flake8-bugbear" = "*" "flake8-bandit" = "*" @@ -21,5 +27,7 @@ html2text = "*" safety = "*" dodgy = "*" + [requires] + python_version = "3.6" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 00000000..532787e9 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,484 @@ +{ + "_meta": { + "hash": { + "sha256": "f533ef0cdbbca35169bb4f36c3c6425dcde5cafbd7d3a08646e1897d59050754" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.6" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.python.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "72eb2aa": { + "file": "https://github.com/Rapptz/discord.py/archive/rewrite.zip" + }, + "aiodns": { + "hashes": [ + "sha256:99d0652f2c02f73bfa646bf44af82705260a523014576647d7959e664830b26b", + "sha256:d8677adc679ce8d0ef706c14d9c3d2f27a0e0cc11d59730cdbaf218ad52dd9ea" + ], + "index": "pypi", + "version": "==1.1.1" + }, + "aiohttp": { + "hashes": [ + "sha256:129d83dd067760cec3cfd4456b5c6d7ac29f2c639d856884568fd539bed5a51f", + "sha256:33c62afd115c456b0cf1e890fe6753055effe0f31a28321efd4f787378d6f4ab", + "sha256:666756e1d4cf161ed1486b82f65fdd386ac07dd20fb10f025abf4be54be12746", + "sha256:9705ded5a0faa25c8f14c6afb7044002d66c9120ed7eadb4aa9ca4aad32bd00c", + "sha256:af5bfdd164256118a0a306b3f7046e63207d1f8cba73a67dcc0bd858dcfcd3bc", + "sha256:b80f44b99fa3c9b4530fcfa324a99b84843043c35b084e0b653566049974435d", + "sha256:c67e105ec74b85c8cb666b6877569dee6f55b9548f982983b9bee80b3d47e6f3", + "sha256:d15c6658de5b7783c2538407278fa062b079a46d5f814a133ae0f09bbb2cfbc4", + "sha256:d611ebd1ef48498210b65486306e065fde031040a1f3c455ca1b6baa7bf32ad3", + "sha256:dcc7e4dcec6b0012537b9f8a0726f8b111188894ab0f924b680d40b13d3298a0", + "sha256:de8ef106e130b94ca143fdfc6f27cda1d8ba439462542377738af4d99d9f5dd2", + "sha256:eb6f1405b607fff7e44168e3ceb5d3c8a8c5a2d3effe0a27f843b16ec047a6d7", + "sha256:f0e2ac69cb709367400008cebccd5d48161dd146096a009a632a132babe5714c" + ], + "index": "pypi", + "version": "==2.2.5" + }, + "async-timeout": { + "hashes": [ + "sha256:00cff4d2dce744607335cba84e9929c3165632da2d27970dbc55802a0c7873d0", + "sha256:9093db5b8ddbe4b8f6885d1a6e0ad84ae3155464cbf6877c387605244c285f3c" + ], + "version": "==2.0.1" + }, + "asyncio": { + "hashes": [ + "sha256:83360ff8bc97980e4ff25c964c7bd3923d333d177aa4f7fb736b019f26c7cb41", + "sha256:b62c9157d36187eca799c378e572c969f0da87cd5fc42ca372d92cdb06e7e1de", + "sha256:c46a87b48213d7464f22d9a497b9eef8c1928b68320a2fa94240f969f6fec08c", + "sha256:c4d18b22701821de07bd6aea8b53d21449ec0ec5680645e5317062ea21817d2d" + ], + "markers": "python_version == '3.3'", + "version": "==3.4.3" + }, + "attrs": { + "hashes": [ + "sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9", + "sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450" + ], + "version": "==17.4.0" + }, + "beautifulsoup4": { + "hashes": [ + "sha256:11a9a27b7d3bddc6d86f59fb76afb70e921a25ac2d6cc55b40d072bd68435a76", + "sha256:7015e76bf32f1f574636c4288399a6de66ce08fb7b2457f628a8d70c0fbabb11", + "sha256:808b6ac932dccb0a4126558f7dfdcf41710dd44a4ef497a0bb59a77f9f078e89" + ], + "version": "==4.6.0" + }, + "bs4": { + "hashes": [ + "sha256:36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a" + ], + "index": "pypi", + "version": "==0.0.1" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "html2text": { + "hashes": [ + "sha256:490db40fe5b2cd79c461cf56be4d39eb8ca68191ae41ba3ba79f6cb05b7dd662", + "sha256:627514fb30e7566b37be6900df26c2c78a030cc9e6211bda604d8181233bcdd4" + ], + "index": "pypi", + "version": "==2018.1.9" + }, + "idna": { + "hashes": [ + "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f", + "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4" + ], + "version": "==2.6" + }, + "idna-ssl": { + "hashes": [ + "sha256:1293f030bc608e9aa9cdee72aa93c1521bbb9c7698068c61c9ada6772162b979" + ], + "version": "==1.0.1" + }, + "multidict": { + "hashes": [ + "sha256:0462372fc74e4c061335118a4a5992b9a618d6c584b028ef03cf3e9b88a960e2", + "sha256:068e91060e3e211441b1a31f5e65de88fc346490e1fae583c35a75a5295c8ef7", + "sha256:0fd4d255adcbab3341d64a2fff5acce23409e57bb94e626485dea3db70ddc35e", + "sha256:16c78b10e897a512aa34ab1969982e42246e53077ae903c1b334926e1ea832d1", + "sha256:241c11614f64535e213ea143efa8b7e598793256601fc795e77075bdfa54f5d6", + "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: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:8ea263de8bf1a30b0d87150b4aa0e3203cf93bc1723ea3e7408a7d25e1299217", + "sha256:943e2dc67ff45ab4c81d628c959837d01561d7e185080ab7a276b8ca67573fb5", + "sha256:9d56a54c93e64b30c0d31f394d9890f175edec029cd846221728f99263cdee82", + "sha256:b95b339c11d824f0bb789d31b91c8534916fcbdce248cccce216fa2630bb8a90", + "sha256:bbfd9aba1e172cd2ab7b7142d49b28cf44d6451c4a66a870aff1dc3cb84849c7", + "sha256:d8637bcc2f901aa61ec1d754abc862f9f145cb0346a0249360df4c159377018e", + "sha256:e2446577eeea79d2179c9469d9d4ce3ab8a07d7985465c3cb91e7d74abc329b6", + "sha256:e72fa163f37ae3b09f143cc6690a36f012d13e905d142e1beed4ec0e593ff657", + "sha256:f32b7c63094749fbc0c1106c9a785666ec8afd49ecfe7002a30bb7c42e62b47c", + "sha256:f50be4dd53f009cfb4b98c3c6b240e18ff9b17e3f1c320bd594bb83eddabfcb2" + ], + "version": "==2.3.0" + }, + "six": { + "hashes": [ + "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", + "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" + ], + "version": "==1.11.0" + }, + "trollius": { + "hashes": [ + "sha256:b35b9a9079c3c06a04cedf27dd833982b5d58401722da63d2c7c6384063a6924" + ], + "markers": "python_version <= '3.2'", + "version": "==2.2" + }, + "websockets": { + "hashes": [ + "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" + ], + "index": "pypi", + "version": "==4.0.1" + }, + "yarl": { + "hashes": [ + "sha256:045dbba18c9142278113d5dc62622978a6f718ba662392d406141c59b540c514", + "sha256:17e57a495efea42bcfca08b49e16c6d89e003acd54c99c903ea1cb3de0ba1248", + "sha256:213e8f54b4a942532d6ac32314c69a147d3b82fa1725ca05061b7c1a19a1d9b1", + "sha256:3353fae45d93cc3e7e41bfcb1b633acc37db821d368e660b03068dbfcf68f8c8", + "sha256:51a084ff8756811101f8b5031a14d1c2dd26c666976e1b18579c6b1c8761a102", + "sha256:5580f22ac1298261cd24e8e584180d83e2cca9a6167113466d2d16cb2aa1f7b1", + "sha256:64727a2593fdba5d6ef69e94eba793a196deeda7152c7bd3a64edda6b1f95f6e", + "sha256:6e75753065c310befab71c5077a59b7cb638d2146b1cfbb1c3b8f08b51362714", + "sha256:7236eba4911a5556b497235828e7a4bc5d90957efa63b7c4b3e744d2d2cf1b94", + "sha256:a69dd7e262cdb265ac7d5e929d55f2f3d07baaadd158c8f19caebf8dde08dfe8", + "sha256:d9ca55a5a297408f08e5401c23ad22bd9f580dab899212f0d5dc1830f0909404", + "sha256:e072edbd1c5628c0b8f97d00cf6c9fcd6a4ee2b5ded10d463fcb6eaa066cf40c", + "sha256:e9a6a319c4bbfb57618f207e86a7c519ab0f637be3d2366e4cdac271577834b8" + ], + "version": "==1.1.1" + } + }, + "develop": { + "attrs": { + "hashes": [ + "sha256:1c7960ccfd6a005cd9f7ba884e6316b5e430a3f1a6c37c5f87d8b43f83b54ec9", + "sha256:a17a9573a6f475c99b551c0e0a812707ddda1ec9653bed04c13841404ed6f450" + ], + "version": "==17.4.0" + }, + "bandit": { + "hashes": [ + "sha256:cb977045497f83ec3a02616973ab845c829cdab8144ce2e757fe031104a9abd4", + "sha256:de4cc19d6ba32d6f542c6a1ddadb4404571347d83ef1ed1e7afb7d0b38e0c25b" + ], + "version": "==1.4.0" + }, + "certifi": { + "hashes": [ + "sha256:14131608ad2fd56836d33a71ee60fa1c82bc9d2c8d98b7bdbc631fe1b3cd1296", + "sha256:edbc3f203427eef571f79a7692bb160a2b0f7ccaa31953e99bd17e307cf63f7d" + ], + "version": "==2018.1.18" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "click": { + "hashes": [ + "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d", + "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b" + ], + "version": "==6.7" + }, + "configparser": { + "hashes": [ + "sha256:5308b47021bc2340965c371f0f058cc6971a04502638d4244225c49d80db273a" + ], + "markers": "python_version < '3.2'", + "version": "==3.5.0" + }, + "dodgy": { + "hashes": [ + "sha256:65e13cf878d7aff129f1461c13cb5fd1bb6dfe66bb5327e09379c3877763280c" + ], + "index": "pypi", + "version": "==0.1.9" + }, + "dparse": { + "hashes": [ + "sha256:7c9f9175d8fd83aed6d31a16c1a3ba4c38189120f1df416b46029d940b4ef582", + "sha256:e4b479dd4d6078ba5f087b28447a50eee0caed57f135b0f5a5e5d5024390f41d" + ], + "version": "==0.2.1" + }, + "enum34": { + "hashes": [ + "sha256:2d81cbbe0e73112bdfe6ef8576f2238f2ba27dd0d55752a776c41d38b7da2850", + "sha256:644837f692e5f550741432dd3f223bbb9852018674981b1664e5dc339387588a", + "sha256:6bd0f6ad48ec2aa117d3d141940d484deccda84d4fcd884f5c3d93c23ecd8c79", + "sha256:8ad8c4783bf61ded74527bffb48ed9b54166685e4230386a9ed9b1279e2df5b1" + ], + "markers": "python_version < '3.4'", + "version": "==1.1.6" + }, + "flake8": { + "hashes": [ + "sha256:7253265f7abd8b313e3892944044a365e3f4ac3fcdcfb4298f55ee9ddf188ba0", + "sha256:c7841163e2b576d435799169b78703ad6ac1bbb0f199994fc05f700b2a90ea37" + ], + "index": "pypi", + "version": "==3.5.0" + }, + "flake8-bandit": { + "hashes": [ + "sha256:24572dc2181c967bec1e7767c1a1faa4921f3d5a4cf9f84a78e2a555ca6c442a", + "sha256:345f5e61cd5eaf354156d7b45173fd14f6e31d35391d884c3222597a363c61ca" + ], + "index": "pypi", + "version": "==1.0.1" + }, + "flake8-bugbear": { + "hashes": [ + "sha256:541746f0f3b2f1a8d7278e1d2d218df298996b60b02677708560db7c7e620e3b", + "sha256:5f14a99d458e29cb92be9079c970030e0dd398b2decb179d76d39a5266ea1578" + ], + "index": "pypi", + "version": "==18.2.0" + }, + "flake8-import-order": { + "hashes": [ + "sha256:40d2a39ed91e080f3285f4c16256b252d7c31070e7f11b7854415bb9f924ea81", + "sha256:68d430781a9ef15c85a0121500cf8462f1a4bc7672acb2a32bfdbcab044ae0b7" + ], + "index": "pypi", + "version": "==0.17.1" + }, + "flake8-polyfill": { + "hashes": [ + "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9", + "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda" + ], + "version": "==1.0.2" + }, + "flake8-string-format": { + "hashes": [ + "sha256:68ea72a1a5b75e7018cae44d14f32473c798cf73d75cbaed86c6a9a907b770b2", + "sha256:774d56103d9242ed968897455ef49b7d6de272000cfa83de5814273a868832f1" + ], + "index": "pypi", + "version": "==0.2.3" + }, + "flake8-tidy-imports": { + "hashes": [ + "sha256:5fc28c82bba16abb4f1154dc59a90487f5491fbdb27e658cbee241e8fddc1b91", + "sha256:c05c9f7dadb5748a04b6fa1c47cb6ae5a8170f03cfb1dca8b37aec58c1ee6d15" + ], + "index": "pypi", + "version": "==1.1.0" + }, + "flake8-todo": { + "hashes": [ + "sha256:6e4c5491ff838c06fe5a771b0e95ee15fc005ca57196011011280fc834a85915" + ], + "index": "pypi", + "version": "==0.7" + }, + "gitdb2": { + "hashes": [ + "sha256:b60e29d4533e5e25bb50b7678bbc187c8f6bcff1344b4f293b2ba55c85795f09", + "sha256:cf9a4b68e8c4da8d42e48728c944ff7af2d8c9db303ac1ab32eac37aa4194b0e" + ], + "version": "==2.0.3" + }, + "gitpython": { + "hashes": [ + "sha256:05069e26177c650b3cb945dd543a7ef7ca449f8db5b73038b465105673c1ef61", + "sha256:c47cc31af6e88979c57a33962cbc30a7c25508d74a1b3a19ec5aa7ed64b03129" + ], + "version": "==2.1.9" + }, + "idna": { + "hashes": [ + "sha256:2c6a5de3089009e3da7c5dde64a141dbc8551d5b7f6cf4ed7c2568d0cc520a8f", + "sha256:8c7309c718f94b3a625cb648ace320157ad16ff131ae0af362c9f21b80ef6ec4" + ], + "version": "==2.6" + }, + "mccabe": { + "hashes": [ + "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", + "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" + ], + "version": "==0.6.1" + }, + "packaging": { + "hashes": [ + "sha256:e9215d2d2535d3ae866c3d6efc77d5b24a0192cce0ff20e42896cc0664f889c0", + "sha256:f019b770dd64e585a99714f1fd5e01c7a8f11b45635aa953fd41c689a657375b" + ], + "version": "==17.1" + }, + "pbr": { + "hashes": [ + "sha256:05f61c71aaefc02d8e37c0a3eeb9815ff526ea28b3b76324769e6158d7f95be1", + "sha256:60c25b7dfd054ef9bb0ae327af949dd4676aa09ac3a9471cdc871d8a9213f9ac" + ], + "version": "==3.1.1" + }, + "pycodestyle": { + "hashes": [ + "sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766", + "sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9" + ], + "version": "==2.3.1" + }, + "pyflakes": { + "hashes": [ + "sha256:08bd6a50edf8cffa9fa09a463063c425ecaaf10d1eb0335a7e8b1401aef89e6f", + "sha256:8d616a382f243dbf19b54743f280b80198be0bca3a5396f1d2e1fca6223e8805" + ], + "version": "==1.6.0" + }, + "pyparsing": { + "hashes": [ + "sha256:0832bcf47acd283788593e7a0f542407bd9550a55a8a8435214a1960e04bcb04", + "sha256:281683241b25fe9b80ec9d66017485f6deff1af5cde372469134b56ca8447a07", + "sha256:8f1e18d3fd36c6795bb7e02a39fd05c611ffc2596c1e0d995d34d67630426c18", + "sha256:9e8143a3e15c13713506886badd96ca4b579a87fbdf49e550dbfc057d6cb218e", + "sha256:b8b3117ed9bdf45e14dcc89345ce638ec7e0e29b2b579fa1ecf32ce45ebac8a5", + "sha256:e4d45427c6e20a59bf4f88c639dcc03ce30d193112047f94012102f235853a58", + "sha256:fee43f17a9c4087e7ed1605bd6df994c6173c1e977d7ade7b651292fab2bd010" + ], + "version": "==2.2.0" + }, + "pyyaml": { + "hashes": [ + "sha256:0c507b7f74b3d2dd4d1322ec8a94794927305ab4cebbe89cc47fe5e81541e6e8", + "sha256:16b20e970597e051997d90dc2cddc713a2876c47e3d92d59ee198700c5427736", + "sha256:3262c96a1ca437e7e4763e2843746588a965426550f3797a79fca9c6199c431f", + "sha256:326420cbb492172dec84b0f65c80942de6cedb5233c413dd824483989c000608", + "sha256:4474f8ea030b5127225b8894d626bb66c01cda098d47a2b0d3429b6700af9fd8", + "sha256:592766c6303207a20efc445587778322d7f73b161bd994f227adaa341ba212ab", + "sha256:5ac82e411044fb129bae5cfbeb3ba626acb2af31a8d17d175004b70862a741a7", + "sha256:5f84523c076ad14ff5e6c037fe1c89a7f73a3e04cf0377cb4d017014976433f3", + "sha256:827dc04b8fa7d07c44de11fabbc888e627fa8293b695e0f99cb544fdfa1bf0d1", + "sha256:b4c423ab23291d3945ac61346feeb9a0dc4184999ede5e7c43e1ffb975130ae6", + "sha256:bc6bced57f826ca7cb5125a10b23fd0f2fff3b7c4701d64c439a300ce665fff8", + "sha256:c01b880ec30b5a6e6aa67b09a2fe3fb30473008c85cd6a67359a1b15ed6d83a4", + "sha256:ca233c64c6e40eaa6c66ef97058cdc80e8d0157a443655baa1b2966e812807ca", + "sha256:e863072cdf4c72eebf179342c94e6989c67185842d9997960b3e69290b2fa269" + ], + "version": "==3.12" + }, + "requests": { + "hashes": [ + "sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f594193a40a38b", + "sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132edaa6dda237e" + ], + "version": "==2.18.4" + }, + "safety": { + "hashes": [ + "sha256:9fb74211a0a0ab09541fe894293d66a558b6138a9fe8ebabc8cf56670e8a009c", + "sha256:ff0c4b76ad791d33825e36c41671ea45330d438921e5395903c0e87e576a377a" + ], + "index": "pypi", + "version": "==1.7.0" + }, + "six": { + "hashes": [ + "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", + "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" + ], + "version": "==1.11.0" + }, + "smmap2": { + "hashes": [ + "sha256:b78ee0f1f5772d69ff50b1cbdb01b8c6647a8354f02f23b488cf4b2cfc923956", + "sha256:c7530db63f15f09f8251094b22091298e82bf6c699a6b8344aaaef3f2e1276c3" + ], + "version": "==2.0.3" + }, + "stevedore": { + "hashes": [ + "sha256:e3d96b2c4e882ec0c1ff95eaebf7b575a779fd0ccb4c741b9832bed410d58b3d", + "sha256:f1c7518e7b160336040fee272174f1f7b29a46febb3632502a8f2055f973d60b" + ], + "version": "==1.28.0" + }, + "urllib3": { + "hashes": [ + "sha256:06330f386d6e4b195fbfc736b297f58c5a892e4440e54d294d7004e3a9bbea1b", + "sha256:cc44da8e1145637334317feebd728bd869a35285b93cbb4cca2577da7e62db4f" + ], + "version": "==1.22" + } + } +} From 3bd802360c6681bfb77429681bb077954f61b909 Mon Sep 17 00:00:00 2001 From: Eli Date: Sun, 25 Mar 2018 14:33:51 -0700 Subject: [PATCH 09/11] Change some names --- bot/cogs/snakes.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index 9c164655..d5acf323 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -40,7 +40,7 @@ def __init__(self, bot: commands.AutoShardedBot): 'parse' '&page={}' '&prop=text|sections' - ) + ) self.info_query = API + ( 'query' '&titles={}' @@ -49,7 +49,7 @@ def __init__(self, bot: commands.AutoShardedBot): f"&cllimit=max&clcategories={'|'.join(hardcoded.categories)}" ) - async def get_rand_snek(self, category: str = None): + async def get_rand_name(self, category: str = None) -> str: """ Follow wikipedia's Special:RandomInCategory to grab the name of a random snake. """ @@ -77,10 +77,10 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: :return: A dict containing information on a snake """ if name is None: - name = await self.get_rand_snek() + name = await self.get_rand_name() async with self.session.get(self.base_query.format(name)) as pg_resp, \ - self.session.get(self.info_query.format(name)) as if_resp: # noqa + self.session.get(self.info_query.format(name)) as if_resp: # noqa: E127 data = await pg_resp.json() info = await if_resp.json() From 8cd8e436ec5ce30b528ad6219ed340b517411501 Mon Sep 17 00:00:00 2001 From: Eli Date: Sun, 25 Mar 2018 15:40:25 -0700 Subject: [PATCH 10/11] Impl python special case --- bot/cogs/snakes.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index d5acf323..a0757157 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -18,9 +18,9 @@ WKPD = 'https://en.wikipedia.org' API = WKPD + '/w/api.php?format=json&redirects=1&action=' -rSENTENCE = re.compile(r'^.+?\.') +rSENTENCE = re.compile(r'^.+?\. ') rBRACK = re.compile(r'[[(].+?[\])]') -rMDLINK = re.compile(r'(\[.*?\])\((\S+?)\s".*?"\)') +rMDLINK = re.compile(r'(\[.*?\])\((.+?)\s".*?"\)') class BadSnake(ValueError): @@ -86,7 +86,7 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: pg_id = str(data['parse']['pageid']) pg_info = info['query']['pages'][pg_id] - if 'categories' not in pg_info: + if 'categories' not in pg_info and pg_id != '23862': # page ID of /wiki/Python_(programming_language) raise BadSnake("This doesn't appear to be a snake!") soup = bs4.BeautifulSoup(data['parse']['text']['*']) tidbits = [] @@ -99,7 +99,7 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: except AttributeError: pass else: - tidbits.append(rMDLINK.sub(lambda m: f'{m[1]}({WKPD}{m[2]})', tidbit)) + tidbits.append(rMDLINK.sub(lambda m: f'{m[1]}({WKPD}{m[2].replace(" ", "")})', tidbit)) try: img_url = pg_info['thumbnail']['source'] except KeyError: @@ -109,7 +109,7 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: return {'🐍': (img_url, pg_url, title), 'tidbits': tidbits} @commands.command() - async def get(self, ctx: Context, name: str = None): + async def get(self, ctx: Context, name: str.lower = None): """ Go online and fetch information about a snake @@ -119,6 +119,8 @@ 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 """ + if name == 'python': + name = 'Python_(programming_language)' try: snek = await self.get_snek(name) except BadSnake as e: From e043abbaa26f6ac6642460c987365369203c861b Mon Sep 17 00:00:00 2001 From: Eli Date: Sun, 25 Mar 2018 17:01:37 -0700 Subject: [PATCH 11/11] Implement disambiguation --- bot/cogs/snakes.py | 91 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 13 deletions(-) diff --git a/bot/cogs/snakes.py b/bot/cogs/snakes.py index a0757157..1558cd9d 100644 --- a/bot/cogs/snakes.py +++ b/bot/cogs/snakes.py @@ -36,6 +36,13 @@ def __init__(self, bot: commands.AutoShardedBot): self.bot = bot self.session = aiohttp.ClientSession(loop=bot.loop) # the provided session says no host is reachable self.h2md = html2text.HTML2Text() + self.disamb_query = API + ( + 'query' + '&titles={}' + '&prop=categories' + '&cllimit=max' + f"&clcategories={'|'.join(hardcoded.categories)}" + ) self.base_query = API + ( 'parse' '&page={}' @@ -46,12 +53,61 @@ def __init__(self, bot: commands.AutoShardedBot): '&titles={}' '&prop=pageimages|categories' '&pithumbsize=300' - f"&cllimit=max&clcategories={'|'.join(hardcoded.categories)}" + '&cllimit=max' + f"&clcategories={'|'.join(hardcoded.categories)}" + '|Category:Disambiguation_pages|Category:All_disambiguation_pages' ) + async def disambiguate(self, ctx: Context, content: str) -> str: + """ + Ask the user to choose between snakes if the name they requested is ambiguous. + If only one snake is present in a disambig page, redirect to it without asking. + + :param ctx: Needed to send the user a dialogue to choose a snake from. + :param page: The disambiguation page in question. + :return: + """ + def check(rxn, usr): + if usr.id != ctx.message.author.id or rxn.message.id != msg.id: + return False + try: + return int(rxn.emoji[0]) <= len(filt) + except ValueError: + return False + soup = bs4.BeautifulSoup(content) + potentials = [ + tag.get('title') for tag in soup.select('li a') + if tag.parent.parent.parent.get('id') != 'toc' + and tag.find_previous(id='See_also') is None + ] + async with self.session.get(self.disamb_query.format('|'.join(potentials))) as resp: + batch = await resp.json() + filt = [i['title'] for i in batch['query']['pages'].values() if 'categories' in i][:9] + if len(filt) > 1: + em = discord.Embed(title='Disambiguation') + em.description = "Oh no, I can't tell which snake you wanted! Help me out by picking one of these:\n" + em.description += ''.join(f'\n{idx}. {title}' for idx, title in enumerate(filt)) + msg = await ctx.send(embed=em) + for i in range(len(filt)): + await msg.add_reaction(f'{i}\u20E3') + rxn, usr = await self.bot.wait_for('reaction_add', timeout=15.0, check=check) + name = filt[int(rxn.emoji[0])] + else: + name = filt[0] + + async with self.session.get(self.base_query.format(name)) as pg_resp, \ + self.session.get(self.info_query.format(name)) as if_resp: # noqa: E127 + data = await pg_resp.json() + info = await if_resp.json() + + return data, info + async def get_rand_name(self, category: str = None) -> str: """ Follow wikipedia's Special:RandomInCategory to grab the name of a random snake. + + :param category: Optional, the name of the category to search for a random page in. Omit for random category. + :return: A random snek's name """ if category is None: category = random.choice(hardcoded.categories) @@ -63,18 +119,17 @@ async def get_rand_name(self, category: str = None) -> str: await asyncio.sleep(1) # hmm return name - async def get_snek(self, name: str = None) -> Dict[str, Any]: + async def get_snek(self, ctx: Context, 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! + Go online and fetch information about a snake. + The information includes the name of the snake, a picture of the snake if applicable, and some tidbits. + + If "python" is given as the snake name, information about the programming language is provided instead. + + :param ctx: Only required for disambiguating to send the user a reaction-based dialogue :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 + :return: A dict containing information about the requested snake """ if name is None: name = await self.get_rand_name() @@ -83,11 +138,21 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]: self.session.get(self.info_query.format(name)) as if_resp: # noqa: E127 data = await pg_resp.json() info = await if_resp.json() - pg_id = str(data['parse']['pageid']) pg_info = info['query']['pages'][pg_id] - if 'categories' not in pg_info and pg_id != '23862': # page ID of /wiki/Python_(programming_language) + + if 'categories' not in pg_info and pg_id != '23862': # 23862 == page ID of /wiki/Python_(programming_language) raise BadSnake("This doesn't appear to be a snake!") + + cats = pg_info.get('categories', []) + # i[9:] strips out 'Category:' + if any(i['title'][9:] in ('Disambiguation pages', 'All disambiguation pages') for i in cats): + try: + data, info = await self.disambiguate(ctx, data['parse']['text']['*']) + except BadSnake: + raise + pg_info = info['query']['pages'][str(data['parse']['pageid'])] + soup = bs4.BeautifulSoup(data['parse']['text']['*']) tidbits = [] for section in data['parse']['sections']: @@ -122,7 +187,7 @@ async def get(self, ctx: Context, name: str.lower = None): if name == 'python': name = 'Python_(programming_language)' try: - snek = await self.get_snek(name) + snek = await self.get_snek(ctx, name) except BadSnake as e: return await ctx.send(f'`{e}`') image, page, title = snek['🐍']