Skip to content

Commit 78562b0

Browse files
committed
Replace fuzzywuzzy with rapidfuzz, readd autocomplete
1 parent eeeb3a2 commit 78562b0

File tree

5 files changed

+49
-19
lines changed

5 files changed

+49
-19
lines changed

modules/games.py

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from dateutil import parser
1616
from discord import app_commands
1717
from discord.ext import commands, tasks
18-
from fuzzywuzzy import fuzz
18+
import rapidfuzz
1919

2020
import tools # type: ignore
2121

@@ -136,17 +136,14 @@ async def sync_db(self) -> Tuple[int, str]:
136136
return count
137137

138138
def search(self, query: str) -> Optional[dict]:
139-
match = {'deku_id': None, 'score': None, 'name': None}
139+
match = {'deku_id': None, 'score': 0, 'name': None}
140140
for game in self.aggregatePipeline:
141+
# If the game we are comparing is really short (<=3 chars), do not allow a match if our search is longer.
142+
# This prevents things like 'a' being the best match for 'realMyst' and not 'realMyst: Masterpiece Edition'
143+
if len(game['name']) <= 5 and len(query) > 5:
144+
continue
141145

142-
methods = [fuzz.ratio, fuzz.partial_ratio, fuzz.token_sort_ratio, fuzz.token_set_ratio]
143-
rem_punc = re.compile('[^0-9a-zA-Z ]+')
144-
145-
# Remove punctuation and casing for name and query
146-
scores = [
147-
method(rem_punc.sub('', game['name'].lower()), rem_punc.sub('', query.lower())) for method in methods
148-
]
149-
score = sum(scores) / len(methods)
146+
score = rapidfuzz.fuzz.WRatio(query.lower(), game['name'], processor=rapidfuzz.utils.default_process)
150147

151148
if not match['score'] or (score > match['score']):
152149
match = {'deku_id': game['deku_id'], 'score': score, 'name': game['name']}
@@ -177,6 +174,19 @@ def get_name(self, deku_id: str):
177174
document = self.db.find_one({'deku_id': deku_id}, projection={'name': 1})
178175
return document['name'] if document else None
179176

177+
async def _games_search_autocomplete(self, interaction: discord.Interaction, current: str):
178+
if current:
179+
game = self.search(current)
180+
181+
else:
182+
# Current textbox is empty
183+
return []
184+
185+
if game:
186+
return [app_commands.Choice(name=game['name'], value=game['deku_id'])]
187+
else:
188+
return []
189+
180190
@app_commands.guilds(discord.Object(id=config.nintendoswitch))
181191
class GamesCommand(app_commands.Group):
182192
pass
@@ -186,13 +196,23 @@ class GamesCommand(app_commands.Group):
186196
@games_group.command(name='search')
187197
@app_commands.describe(query='The term you want to search for a game')
188198
@app_commands.checks.cooldown(2, 60, key=lambda i: (i.guild_id, i.user.id))
199+
@app_commands.autocomplete(query=_games_search_autocomplete)
189200
async def _games_search(self, interaction: discord.Interaction, query: str):
190201
'''Search for Nintendo Switch games'''
191202
await interaction.response.defer()
192-
result = self.search(query)
193203

194-
if result and result['deku_id']:
195-
game = self.db.find_one({'deku_id': result['deku_id']})
204+
user_deku_id = self.db.find_one({'deku_id': query.strip()})
205+
game = None
206+
207+
if user_deku_id:
208+
game = user_deku_id # User clicked an autocomplete, giving us the exact deku_id
209+
result = {'deku_id': user_deku_id['deku_id'], 'score': 100.0, 'name': user_deku_id['name']}
210+
211+
else:
212+
result = self.search(query)
213+
214+
if result and result['deku_id']:
215+
game = self.db.find_one({'deku_id': result['deku_id']})
196216

197217
if game:
198218
embed = discord.Embed(

modules/social.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import yaml
2323
from discord import app_commands
2424
from discord.ext import commands
25-
from fuzzywuzzy import process
25+
from rapidfuzz import process
2626
from PIL import Image, ImageDraw, ImageFont
2727

2828
import tools # type: ignore
@@ -963,6 +963,9 @@ async def _profile_timezone(self, interaction: discord.Interaction, timezone: st
963963
f'{config.redTick} The timezone you provided is invalid. It should be in the format similar to `America/New_York`. If you aren\'t sure how to find it or what yours is, you can visit [this helpful website](https://www.timezoneconverter.com/cgi-bin/findzone.tzc)'
964964
)
965965

966+
async def _profile_games_autocomplete(self, interaction: discord.Interaction, current: str):
967+
return await self.Games._games_search_autocomplete(interaction, current)
968+
966969
@social_group.command(name='games', description='Pick up-to 5 of your fav Nintendo Switch games to show them off')
967970
@app_commands.describe(
968971
game1='You need to pick at least one game. Search by name',
@@ -971,6 +974,13 @@ async def _profile_timezone(self, interaction: discord.Interaction, timezone: st
971974
game4='Optionally pick a 4th game to show on your profile as well. Search by name',
972975
game5='Optionally pick a 5th game to show on your profile as well. Search by name',
973976
)
977+
@app_commands.autocomplete(
978+
game1=_profile_games_autocomplete,
979+
game2=_profile_games_autocomplete,
980+
game3=_profile_games_autocomplete,
981+
game4=_profile_games_autocomplete,
982+
game5=_profile_games_autocomplete,
983+
)
974984
async def _profile_games(
975985
self,
976986
interaction: discord.Interaction,
@@ -1584,7 +1594,7 @@ async def _migratefavgames(self, ctx):
15841594
for name in names:
15851595
search = self.Games.search(name)
15861596

1587-
if search['score'] > score:
1597+
if search and (search['score'] > score):
15881598
score = search['score']
15891599
deku_id = search['deku_id']
15901600
new_name = search['name']
@@ -1604,7 +1614,7 @@ async def _migratefavgames(self, ctx):
16041614
# update the progress message
16051615
interval = (count // 100) + 1
16061616
if ((j + 1) % interval) == 0:
1607-
percent_complete = (j + 1) / count
1617+
percent_complete = ((j + 1) / count) * 100
16081618
await message.edit(content=f'Migrating... {j+1}/{count} ({percent_complete:.0f}%)')
16091619

16101620
await message.edit(content=f'Migrating... {count}/{count} (100%)')

modules/utility.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import pymongo
1515
from discord import Webhook, WebhookType, app_commands
1616
from discord.ext import commands, tasks
17-
from fuzzywuzzy import process
17+
from rapidfuzz import process
1818

1919
import tools
2020

private

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ discord.py==2.4.0
33
pymongo[srv]<=3.12.3
44
jishaku
55
pylint
6-
fuzzywuzzy[speedup]
6+
rapidfuzz
77
pillow<=9.5.0
88
pytz
99
codepoints

0 commit comments

Comments
 (0)