From 62da9d1c28be5f40c485668e5c314b6427996731 Mon Sep 17 00:00:00 2001 From: ComputerTech312 Date: Sat, 13 Sep 2025 15:02:38 +0100 Subject: [PATCH] Major improvements: Remove rate limiting and improve message readability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✅ Completely removed all rate limiting functionality ✅ Made messages less compact and more readable ✅ Removed duck detector and auto shotgun from shop Changes: - Removed rate limiting: check_rate_limit(), is_rate_limited(), command_cooldowns - Improved message formats with natural language - Hit messages: 'Duck shot in X.Xs!' instead of 'X.Xs' - Miss messages: 'You missed the duck!' instead of 'MISS' - Reload messages: 'You reloaded your gun!' instead of 'RELOADED' - Stats display: Full words instead of abbreviations - Shop cleanup: Removed items #14 (auto shotgun) and #22 (duck detector) - Better spacing and punctuation throughout - Maintained efficiency while improving readability --- .../simple_duckhunt.cpython-312.pyc | Bin 142904 -> 141195 bytes duckhunt/duckhunt.log | 2 + duckhunt/simple_duckhunt.py | 79 +++--------------- 3 files changed, 15 insertions(+), 66 deletions(-) diff --git a/duckhunt/__pycache__/simple_duckhunt.cpython-312.pyc b/duckhunt/__pycache__/simple_duckhunt.cpython-312.pyc index 8680ae62c786bdd98b21f44c25d3d561f9bc995e..5ed5d1246ef9fc4cc7f08ecb3f2c4874850b0aff 100644 GIT binary patch delta 24596 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 delta 26422 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 O!i&}OrS@bl_WuC>_R#47 diff --git a/duckhunt/duckhunt.log b/duckhunt/duckhunt.log index 55bb940..b9276b5 100644 --- a/duckhunt/duckhunt.log +++ b/duckhunt/duckhunt.log @@ -555,3 +555,5 @@ 2025-09-13 14:45:32,893 [INFO ] DuckHuntBot - load_database:402: Loaded 19 players from duckhunt.json 2025-09-13 14:47:29,566 [INFO ] DuckHuntBot - setup_logger:79: Enhanced logging system initialized with file rotation 2025-09-13 14:47:29,568 [INFO ] DuckHuntBot - load_database:403: Loaded 21 players from duckhunt.json +2025-09-13 15:01:52,979 [INFO ] DuckHuntBot - setup_logger:79: Enhanced logging system initialized with file rotation +2025-09-13 15:01:52,981 [INFO ] DuckHuntBot - load_database:402: Loaded 21 players from duckhunt.json diff --git a/duckhunt/simple_duckhunt.py b/duckhunt/simple_duckhunt.py index abd6cc3..3e06b59 100644 --- a/duckhunt/simple_duckhunt.py +++ b/duckhunt/simple_duckhunt.py @@ -148,7 +148,6 @@ class SimpleIRCBot: self.db_file = "duckhunt.json" self.admins = [admin.lower() for admin in self.config.get('admins', ['colby'])] # Load from config only, case insensitive self.ignored_nicks = set() # Nicks to ignore commands from - self.command_cooldowns = {} # Rate limiting for commands self.duck_timeout_min = self.config.get('duck_timeout_min', 45) # Minimum duck timeout self.duck_timeout_max = self.config.get('duck_timeout_max', 75) # Maximum duck timeout self.duck_spawn_min = self.config.get('duck_spawn_min', 1800) # Minimum duck spawn time (30 min) @@ -391,7 +390,7 @@ class SimpleIRCBot: player[item_key] = player.get(item_key, 0) + 1 self.send_message(channel, f"{nick} > {self.colors['cyan']}You found {found_item} in the bushes!{self.colors['reset']}") - self.save_player(f"{nick}!user@host") # Save player data + # Player data will be saved by the calling function def load_database(self): """Load player data from JSON file""" @@ -515,8 +514,8 @@ class SimpleIRCBot: self.save_database() # Send notification to channel rearmed_list = ', '.join(rearmed_players) - self.send_message(channel, f"🔫 {self.colors['green']}Auto-rearm:{self.colors['reset']} {rearmed_list} got their guns back! (Thanks to {shooter_nick}'s duck shot)") - self.logger.info(f"Auto-rearmed {len(rearmed_players)} players after {shooter_nick} shot duck in {channel}") + self.send_message(channel, f"🔫 {self.colors['green']}Auto-rearm:{self.colors['reset']} {rearmed_list} got their guns back! (Thanks to {shooter_nick}'s duck shot)") + self.logger.info(f"Auto-rearmed {len(rearmed_players)} players after {shooter_nick} shot duck in {channel}") async def update_channel_records(self, channel, hunter, shot_time, duck_type): """Update channel records and duck difficulty after a successful shot""" @@ -623,34 +622,6 @@ class SimpleIRCBot: self.send_raw(f'PRIVMSG {target} :{msg}') # Remove drain() for faster responses - let TCP handle buffering - def check_rate_limit(self, nick: str, channel: str) -> bool: - """Check if user is within rate limits""" - try: - current_time = time.time() - key = f"{nick}:{channel}" - - # Rate limit: 5 commands per 30 seconds per user per channel - if key not in self.command_cooldowns: - self.command_cooldowns[key] = [] - - # Remove old entries - self.command_cooldowns[key] = [ - timestamp for timestamp in self.command_cooldowns[key] - if current_time - timestamp < 30 - ] - - # Check if under limit - if len(self.command_cooldowns[key]) >= 5: - return False - - # Add current command - self.command_cooldowns[key].append(current_time) - return True - - except Exception as e: - self.logger.error(f"Rate limit check failed: {e}") - return True # Allow command if rate limiting fails - def get_player(self, user): """Get player data by nickname only (case insensitive)""" if '!' not in user: @@ -768,18 +739,6 @@ class SimpleIRCBot: if hasattr(signal, 'SIGHUP'): signal.signal(signal.SIGHUP, lambda s, f: signal_handler(s)) - def is_rate_limited(self, user, command, cooldown=2.0): - """Check if user is rate limited for a command""" - now = time.time() - key = f"{user}:{command}" - - if key in self.command_cooldowns: - if now - self.command_cooldowns[key] < cooldown: - return True - - self.command_cooldowns[key] = now - return False - async def handle_command(self, user, channel, message): """Enhanced command handler with logging, validation, and graceful degradation""" if not user: @@ -811,11 +770,6 @@ class SimpleIRCBot: if nick_lower in self.ignored_nicks: self.logger.debug(f"Ignoring command from ignored user: {nick}") return - - # Rate limiting check - if not self.check_rate_limit(nick_lower, channel): - self.logger.info(f"Rate limit exceeded for {nick} in {channel}") - return # Determine if this is a private message to the bot is_private = channel == self.config['nick'] @@ -877,10 +831,6 @@ class SimpleIRCBot: # Regular game commands (channel only) # Inline common commands for speed if cmd == '!bang': - # Rate limit shooting to prevent spam - if self.is_rate_limited(user, 'bang', 1.0): - return - player = self.get_player(user) if not player: return @@ -999,9 +949,9 @@ class SimpleIRCBot: if is_golden: golden_count = player.get('golden_ducks', 0) - hit_msg = f"{nick} > {self.colors['yellow']}{shot_sound} ★GOLDEN★{self.colors['reset']} {shot_time:.3f}s | Ducks:{player['caught']} ({self.colors['yellow']}{golden_count}g{self.colors['reset']}) | L{level} | +{xp_earned}xp{explosive_text}{lucky_text}" + hit_msg = f"{nick} > {self.colors['yellow']}{shot_sound} ★ GOLDEN DUCK ★{self.colors['reset']} shot in {shot_time:.3f}s! | Ducks: {player['caught']} ({self.colors['yellow']}{golden_count} golden{self.colors['reset']}) | Level {level} | +{xp_earned} xp{explosive_text}{lucky_text}" else: - hit_msg = f"{nick} > {self.colors['green']}{shot_sound}{self.colors['reset']} {shot_time:.3f}s | Ducks:{player['caught']} | L{level} | +{xp_earned}xp{explosive_text}{lucky_text}" + hit_msg = f"{nick} > {self.colors['green']}{shot_sound}{self.colors['reset']} Duck shot in {shot_time:.3f}s! | Ducks: {player['caught']} | Level {level} | +{xp_earned} xp{explosive_text}{lucky_text}" self.send_message(channel, hit_msg) @@ -1045,7 +995,7 @@ class SimpleIRCBot: await self.scare_duck_on_miss(channel, target_duck) miss_sound = "•click•" if player.get('silencer', 0) > 0 else "*CLICK*" - await self.send_user_message(nick, channel, f"{nick} > {miss_sound} MISS | {miss_penalty}xp{ricochet_msg}") + await self.send_user_message(nick, channel, f"{nick} > {miss_sound} You missed the duck! | {miss_penalty} xp{ricochet_msg}") else: # No duck present - wild fire! @@ -1084,7 +1034,7 @@ class SimpleIRCBot: if player.get('silencer', 0) > 0: wild_sound = "•" + wild_sound[1:-1] + "•" - await self.send_user_message(nick, channel, f"{nick} > {wild_sound} WILD SHOT! | {miss_penalty+wild_penalty}xp | GUN CONFISCATED{friendly_fire_msg}") + await self.send_user_message(nick, channel, f"{nick} > {wild_sound} You shot at nothing! What were you aiming at? | {miss_penalty+wild_penalty} xp | GUN CONFISCATED{friendly_fire_msg}") # Save after each shot self.save_player(user) @@ -1147,7 +1097,7 @@ class SimpleIRCBot: remaining_ducks = len([d for d in channel_ducks if d.get('alive')]) duck_count_text = f" | {remaining_ducks} ducks remain" if remaining_ducks > 0 else "" - self.send_message(channel, f"{nick} > \\_o< BEFRIENDED {bef_time:.3f}s | Friends:{player['befriended']} | +{xp_earned}xp{lucky_text}{duck_count_text}") + self.send_message(channel, f"{nick} > \\_o< You befriended the duck in {bef_time:.3f}s! | Friends: {player['befriended']} ducks | +{xp_earned} xp{lucky_text}{duck_count_text}") # Update karma for successful befriend if self.get_config('karma.enabled', True): @@ -1204,7 +1154,7 @@ class SimpleIRCBot: if player.get('jammed', False): player['jammed'] = False unjam_sound = "•click click•" if player.get('silencer', 0) > 0 else "*click click*" - self.send_message(channel, f"{nick} > {unjam_sound} UNJAMMED | {player['ammo']}/{player['max_ammo']} | {player['chargers']}/{player['max_chargers']}") + self.send_message(channel, f"{nick} > {unjam_sound} You unjammed your gun! | Ammo: {player['ammo']}/{player['max_ammo']} | Chargers: {player['chargers']}/{player['max_chargers']}") self.save_player(user) return @@ -1223,13 +1173,13 @@ class SimpleIRCBot: player['chargers'] -= 1 player['ammo'] = player['max_ammo'] reload_sound = "•click•" if player.get('silencer', 0) > 0 else "*click*" - self.send_message(channel, f"{nick} > {reload_sound} RELOADED | {player['ammo']}/{player['max_ammo']} | {player['chargers']}/{player['max_chargers']}") + self.send_message(channel, f"{nick} > {reload_sound} You reloaded your gun! | Ammo: {player['ammo']}/{player['max_ammo']} | Chargers: {player['chargers']}/{player['max_chargers']}") else: # Gun jams during reload player['jammed'] = True player['jammed_count'] = player.get('jammed_count', 0) + 1 jam_sound = "•CLACK• •click click•" if player.get('silencer', 0) > 0 else "*CLACK* *click click*" - self.send_message(channel, f"{nick} > {jam_sound} RELOAD JAMMED! Use !reload to unjam.") + self.send_message(channel, f"{nick} > {jam_sound} Your gun jammed while reloading! Use !reload again to unjam it.") # Save to database after reload self.save_player(user) @@ -1508,7 +1458,7 @@ class SimpleIRCBot: if player.get('explosive_ammo', False): compact_gun_status += "[EXP]" - stats_line2 = f"{nick} > {weapon_name.title()}{compact_gun_status} | {player['ammo']}/{player['max_ammo']}a | {player['chargers']}/{player['max_chargers']}c | Acc:{player['accuracy']}%({effective_accuracy:.0f}%) | Rel:{self.calculate_gun_reliability(player)}%" + stats_line2 = f"{nick} > {weapon_name.title()}{compact_gun_status} | Ammo: {player['ammo']}/{player['max_ammo']} | Chargers: {player['chargers']}/{player['max_chargers']} | Accuracy: {player['accuracy']}% (effective: {effective_accuracy:.0f}%) | Reliability: {self.calculate_gun_reliability(player)}%" # Optional advanced stats line (if requested) best_time = player.get('best_time', 999.9) @@ -1793,8 +1743,7 @@ class SimpleIRCBot: 'weapons': [ {'id': '11', 'name': 'Shotgun', 'cost': 100}, {'id': '12', 'name': 'Assault rifle', 'cost': 200}, - {'id': '13', 'name': 'Sniper rifle', 'cost': 350}, - {'id': '14', 'name': 'Auto shotgun', 'cost': 500} + {'id': '13', 'name': 'Sniper rifle', 'cost': 350} ], 'upgrades': [ {'id': '6', 'name': 'Grease', 'cost': 8}, @@ -1810,7 +1759,6 @@ class SimpleIRCBot: {'id': '17', 'name': 'Sabotage', 'cost': 14}, {'id': '20', 'name': 'Decoy', 'cost': 80}, {'id': '21', 'name': 'Bread', 'cost': 50}, - {'id': '22', 'name': 'Duck detector', 'cost': 50}, {'id': '23', 'name': 'Mechanical duck', 'cost': 50} ], 'insurance': [ @@ -2602,7 +2550,6 @@ class SimpleIRCBot: # Clear in-memory data self.players.clear() self.ducks.clear() - self.command_cooldowns.clear() self.logger.info("Cleanup completed successfully")