@@ -36,6 +36,13 @@ def __init__(self, bot: commands.AutoShardedBot):
3636 self .bot = bot
3737 self .session = aiohttp .ClientSession (loop = bot .loop ) # the provided session says no host is reachable
3838 self .h2md = html2text .HTML2Text ()
39+ self .disamb_query = API + (
40+ 'query'
41+ '&titles={}'
42+ '&prop=categories'
43+ '&cllimit=max'
44+ f"&clcategories={ '|' .join (hardcoded .categories )} "
45+ )
3946 self .base_query = API + (
4047 'parse'
4148 '&page={}'
@@ -46,12 +53,61 @@ def __init__(self, bot: commands.AutoShardedBot):
4653 '&titles={}'
4754 '&prop=pageimages|categories'
4855 '&pithumbsize=300'
49- f"&cllimit=max&clcategories={ '|' .join (hardcoded .categories )} "
56+ '&cllimit=max'
57+ f"&clcategories={ '|' .join (hardcoded .categories )} "
58+ '|Category:Disambiguation_pages|Category:All_disambiguation_pages'
5059 )
5160
61+ async def disambiguate (self , ctx : Context , content : str ) -> str :
62+ """
63+ Ask the user to choose between snakes if the name they requested is ambiguous.
64+ If only one snake is present in a disambig page, redirect to it without asking.
65+
66+ :param ctx: Needed to send the user a dialogue to choose a snake from.
67+ :param page: The disambiguation page in question.
68+ :return:
69+ """
70+ def check (rxn , usr ):
71+ if usr .id != ctx .message .author .id or rxn .message .id != msg .id :
72+ return False
73+ try :
74+ return int (rxn .emoji [0 ]) <= len (filt )
75+ except ValueError :
76+ return False
77+ soup = bs4 .BeautifulSoup (content )
78+ potentials = [
79+ tag .get ('title' ) for tag in soup .select ('li a' )
80+ if tag .parent .parent .parent .get ('id' ) != 'toc'
81+ and tag .find_previous (id = 'See_also' ) is None
82+ ]
83+ async with self .session .get (self .disamb_query .format ('|' .join (potentials ))) as resp :
84+ batch = await resp .json ()
85+ filt = [i ['title' ] for i in batch ['query' ]['pages' ].values () if 'categories' in i ][:9 ]
86+ if len (filt ) > 1 :
87+ em = discord .Embed (title = 'Disambiguation' )
88+ em .description = "Oh no, I can't tell which snake you wanted! Help me out by picking one of these:\n "
89+ em .description += '' .join (f'\n { idx } . { title } ' for idx , title in enumerate (filt ))
90+ msg = await ctx .send (embed = em )
91+ for i in range (len (filt )):
92+ await msg .add_reaction (f'{ i } \u20E3 ' )
93+ rxn , usr = await self .bot .wait_for ('reaction_add' , timeout = 15.0 , check = check )
94+ pg = filt [int (rxn .emoji [0 ])]
95+ else :
96+ pg = filt [0 ]
97+
98+ async with self .session .get (self .base_query .format (name )) as pg_resp , \
99+ self .session .get (self .info_query .format (name )) as if_resp : # noqa: E127
100+ data = await pg_resp .json ()
101+ info = await if_resp .json ()
102+
103+ return data , info
104+
52105 async def get_rand_name (self , category : str = None ) -> str :
53106 """
54107 Follow wikipedia's Special:RandomInCategory to grab the name of a random snake.
108+
109+ :param category: Optional, the name of the category to search for a random page in. Omit for random category.
110+ :return: A random snek's name
55111 """
56112 if category is None :
57113 category = random .choice (hardcoded .categories )
@@ -63,18 +119,17 @@ async def get_rand_name(self, category: str = None) -> str:
63119 await asyncio .sleep (1 ) # hmm
64120 return name
65121
66- async def get_snek (self , name : str = None ) -> Dict [str , Any ]:
122+ async def get_snek (self , ctx : Context , name : str = None ) -> Dict [str , Any ]:
67123 """
68- Go online and fetch information about a snake
69-
70- The information includes the name of the snake, a picture of the snake, and various other pieces of info.
71- What information you get for the snake is up to you. Be creative!
72-
73- If "python" is given as the snake name, you should return information about the programming language, but with
74- all the information you'd provide for a real snake. Try to have some fun with this!
124+ Go online and fetch information about a snake.
75125
126+ The information includes the name of the snake, a picture of the snake if applicable, and some tidbits.
127+
128+ If "python" is given as the snake name, information about the programming language is provided instead.
129+
130+ :param ctx: Only required for disambiguating to send the user a reaction-based dialogue
76131 :param name: Optional, the name of the snake to get information for - omit for a random snake
77- :return: A dict containing information on a snake
132+ :return: A dict containing information about the requested snake
78133 """
79134 if name is None :
80135 name = await self .get_rand_name ()
@@ -83,11 +138,21 @@ async def get_snek(self, name: str = None) -> Dict[str, Any]:
83138 self .session .get (self .info_query .format (name )) as if_resp : # noqa: E127
84139 data = await pg_resp .json ()
85140 info = await if_resp .json ()
86-
87141 pg_id = str (data ['parse' ]['pageid' ])
88142 pg_info = info ['query' ]['pages' ][pg_id ]
89- if 'categories' not in pg_info and pg_id != '23862' : # page ID of /wiki/Python_(programming_language)
143+
144+ if 'categories' not in pg_info and pg_id != '23862' : # 23862 == page ID of /wiki/Python_(programming_language)
90145 raise BadSnake ("This doesn't appear to be a snake!" )
146+
147+ cats = pg_info .get ('categories' , [])
148+ # i[9:] strips out 'Category:'
149+ if any (i ['title' ][9 :] in ('Disambiguation pages' , 'All disambiguation pages' ) for i in cats ):
150+ try :
151+ data , info = await self .disambiguate (ctx , data ['parse' ]['text' ]['*' ])
152+ except BadSnake :
153+ raise
154+ pg_info = info ['query' ]['pages' ][data ['parse' ]['pageid' ]]
155+
91156 soup = bs4 .BeautifulSoup (data ['parse' ]['text' ]['*' ])
92157 tidbits = []
93158 for section in data ['parse' ]['sections' ]:
@@ -122,7 +187,7 @@ async def get(self, ctx: Context, name: str.lower = None):
122187 if name == 'python' :
123188 name = 'Python_(programming_language)'
124189 try :
125- snek = await self .get_snek (name )
190+ snek = await self .get_snek (ctx , name )
126191 except BadSnake as e :
127192 return await ctx .send (f'`{ e } `' )
128193 image , page , title = snek ['🐍' ]
0 commit comments