@@ -140,27 +140,26 @@ async def _mark_highscore_message(
140140 new_count : int ,
141141 previous_high_score : int ,
142142 ) -> None :
143- """Add ✅+🏆 to the message and keep only one active highscore marker per guild."""
143+ """Add ✅+🏆 to the message.
144+
145+ Note: Reactions, once added by the bot, should never be removed.
146+ """
144147 if not message .guild or not isinstance (message .channel , discord .TextChannel ):
145148 return
146149
147150 guild_id = message .guild .id
148151 channel = message .channel
149152
150- previous_marker = await self ._get_active_highscore_message_id (guild_id )
151- if previous_marker and previous_marker != message .id :
152- await self ._remove_bot_reactions (channel , previous_marker )
153-
154- # Ensure reactions exist (✅ may already be there)
155- try :
156- await message .add_reaction ("✅" )
157- except Exception :
158- pass
153+ # Only add the trophy here.
154+ # The ✅ reaction is added for all valid counts in the main handler;
155+ # adding it again here causes extra API calls and rate limits.
159156 try :
160157 await message .add_reaction ("🏆" )
161158 except Exception :
162159 pass
163160
161+ # Track the latest highscore/tie message ID for bookkeeping.
162+ # (We no longer remove reactions from older messages.)
164163 await self ._set_active_highscore_message_id (guild_id , message .id )
165164
166165 # Record history only if it is a NEW record
@@ -178,24 +177,48 @@ async def _mark_highscore_message(
178177 async def _clear_highscore_marker_if_any (self , guild_id : int , channel : discord .TextChannel ) -> None :
179178 marker_id = await self ._get_active_highscore_message_id (guild_id )
180179 if marker_id :
181- await self ._remove_bot_reactions (channel , marker_id )
182180 await self ._set_active_highscore_message_id (guild_id , None )
183181
184182 @app_commands .command (name = "setcountingchannel" , description = "Set the channel for the counting game" )
185183 @app_commands .checks .has_permissions (administrator = True )
186184 async def setcountingchannel (self , interaction : discord .Interaction , channel : discord .TextChannel ):
187- async with aiosqlite .connect (DB_PATH , timeout = 30.0 ) as db :
188- await db .execute ("""
189- INSERT INTO counting_config (guild_id, channel_id)
190- VALUES (?, ?)
191- ON CONFLICT(guild_id) DO UPDATE SET channel_id = excluded.channel_id
192- """ , (interaction .guild_id , channel .id ))
193- await db .commit ()
194-
185+ # Slash command interactions must be acknowledged quickly.
186+ # DB operations can take >3s (locks, slow disks), so defer immediately.
187+ if interaction .response .is_done ():
188+ # Extremely defensive; normally false here.
189+ pass
190+ else :
191+ await interaction .response .defer (ephemeral = True )
192+
193+ if interaction .guild_id is None :
194+ await interaction .followup .send ("This command can only be used in a server." , ephemeral = True )
195+ return
196+
197+ retries = 3
198+ while retries > 0 :
199+ try :
200+ async with aiosqlite .connect (DB_PATH , timeout = 30.0 ) as db :
201+ await db .execute (
202+ """
203+ INSERT INTO counting_config (guild_id, channel_id)
204+ VALUES (?, ?)
205+ ON CONFLICT(guild_id) DO UPDATE SET channel_id = excluded.channel_id
206+ """ ,
207+ (interaction .guild_id , channel .id ),
208+ )
209+ await db .commit ()
210+ break
211+ except aiosqlite .OperationalError as e :
212+ if "locked" in str (e ).lower ():
213+ retries -= 1
214+ await asyncio .sleep (0.5 )
215+ continue
216+ raise
217+
195218 # Update cache
196219 self .counting_channels [interaction .guild_id ] = channel .id
197-
198- await interaction .response . send_message (f"Counting channel set to { channel .mention } " , ephemeral = True )
220+
221+ await interaction .followup . send (f"Counting channel set to { channel .mention } " , ephemeral = True )
199222
200223 def safe_eval (self , expr ):
201224 operators = {
0 commit comments