Skip to content
This repository was archived by the owner on Mar 14, 2021. It is now read-only.

Commit 5a015eb

Browse files
committed
Implement disambiguation
1 parent 8cd8e43 commit 5a015eb

1 file changed

Lines changed: 78 additions & 13 deletions

File tree

bot/cogs/snakes.py

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)