1515from dateutil import parser
1616from discord import app_commands
1717from discord .ext import commands , tasks
18- from fuzzywuzzy import fuzz
18+ import rapidfuzz
1919
2020import 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 (
0 commit comments