Implement duck item drop system and remove all emojis

Duck Item Drop System:
- Added configurable drop rates per duck type (15%/25%/50%)
- Created weighted drop tables for different items
- Normal ducks: basic items (bullets, magazines, gun brush, sand)
- Fast ducks: useful items including bucket of water
- Golden ducks: rare items (bread, insurance, gun buyback, dry clothes)
- Items automatically added to player inventory
- Added drop notification messages for each duck type
- Integrated seamlessly with existing combat mechanics

Emoji Removal:
- Removed all emojis from source code files
- Updated logging system to use clean text prefixes
- Replaced trophy/medal emojis with #1/#2/#3 rankings
- Updated README.md to remove all emojis
- Professional clean appearance throughout codebase
This commit is contained in:
2025-09-26 19:59:34 +01:00
parent 5ed2f0fce6
commit 7aded2ed83
13 changed files with 266 additions and 70 deletions

View File

@@ -1,16 +1,16 @@
# 🦆 DuckHunt IRC Bot
# DuckHunt IRC Bot
A feature-rich IRC bot that brings the classic duck hunting game to your IRC channels. Players can shoot, befriend, and collect various types of ducks while managing their equipment and competing for high scores.
## ✨ Features
- 🦆 **Multiple Duck Types**: Normal, Golden (high HP), and Fast (quick timeout) ducks
- **Multiple Duck Types**: Normal, Golden (high HP), and Fast (quick timeout) ducks
- 🎯 **Accuracy System**: Dynamic accuracy that improves with hits and degrades with misses
- 🔫 **Weapon Management**: Magazines, bullets, and gun jamming mechanics
- **Weapon Management**: Magazines, bullets, and gun jamming mechanics
- 🛒 **Shop System**: Buy equipment and items with XP (currency)
- 🎒 **Inventory System**: Collect and use various items (bread, grease, sights, etc.)
- 👥 **Player Statistics**: Track shots, hits, misses, and best times
- 🔧 **Fully Configurable**: Every game parameter can be customized via config
- **Fully Configurable**: Every game parameter can be customized via config
- 🔐 **Authentication**: Support for both server passwords and SASL/NickServ auth
- 📊 **Admin Commands**: Comprehensive bot management and player administration
@@ -100,7 +100,7 @@ The bot uses a nested JSON configuration system. Key settings include:
- `!setstat <player> <stat> <value>` - Modify player stats
- `!reload_config` - Reload configuration without restart
## 🦆 Duck Types
## Duck Types
| Type | Spawn Rate | HP | Timeout | XP Reward |
|------|------------|----|---------|-----------|
@@ -139,7 +139,7 @@ duckhunt/
└── duckhunt.json # Player database
```
## 🔧 Development
## Development
### Adding New Features
@@ -198,4 +198,4 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
---
**Happy Duck Hunting!** 🦆🔫
**Happy Duck Hunting!**

View File

@@ -32,21 +32,47 @@
"duck_types": {
"normal": {
"xp": 10,
"timeout": 60
"timeout": 60,
"drop_chance": 0.15
},
"golden": {
"chance": 0.15,
"min_hp": 3,
"max_hp": 5,
"xp": 15,
"timeout": 60
"timeout": 60,
"drop_chance": 0.50
},
"fast": {
"chance": 0.25,
"timeout": 20,
"xp": 12
"xp": 12,
"drop_chance": 0.25
}
},
"item_drops": {
"normal_duck_drops": [
{"item_id": 1, "weight": 40},
{"item_id": 2, "weight": 25},
{"item_id": 4, "weight": 20},
{"item_id": 3, "weight": 15}
],
"fast_duck_drops": [
{"item_id": 1, "weight": 30},
{"item_id": 2, "weight": 25},
{"item_id": 4, "weight": 20},
{"item_id": 8, "weight": 15},
{"item_id": 3, "weight": 10}
],
"golden_duck_drops": [
{"item_id": 5, "weight": 25},
{"item_id": 6, "weight": 20},
{"item_id": 7, "weight": 20},
{"item_id": 2, "weight": 15},
{"item_id": 9, "weight": 10},
{"item_id": 1, "weight": 10}
]
},
"player_defaults": {
"accuracy": 75,
"magazines": 3,

View File

@@ -35,5 +35,5 @@
"temporary_effects": []
}
},
"last_save": "1758910428.1765292"
"last_save": "1758912506.1474519"
}

View File

@@ -25,7 +25,7 @@ def main():
config = json.load(f)
bot = DuckHuntBot(config)
bot.logger.info("🦆 Starting DuckHunt Bot...")
bot.logger.info("Starting DuckHunt Bot...")
# Run the bot
asyncio.run(bot.run())

View File

@@ -512,3 +512,76 @@
19:13:48.194 📘 INFO DuckHuntBot 💾 Database saved
19:13:48.395 📘 INFO DuckHuntBot 🔌 IRC connection closed
19:13:48.396 📘 INFO DuckHuntBot ✅ Bot shutdown complete
19:34:31.269 📘 INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log
19:34:31.269 📘 INFO DuckHuntBot Debug mode: ON
19:34:31.269 📘 INFO DuckHuntBot Log everything: YES
19:34:31.270 📘 INFO DuckHuntBot Unified format: YES
19:34:31.270 📘 INFO DuckHuntBot Console level: INFO
19:34:31.270 📘 INFO DuckHuntBot File level: DEBUG
19:34:31.270 📘 INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log
19:34:31.270 📘 INFO DuckHuntBot 🤖 Initializing DuckHunt Bot components...
19:34:31.271 📘 INFO DuckHuntBot.DB Loaded 2 players from duckhunt.json
19:34:31.272 📘 INFO SASL Unified logging system initialized: all logs in duckhunt.log
19:34:31.272 📘 INFO SASL Debug mode: ON
19:34:31.272 📘 INFO SASL Log everything: YES
19:34:31.272 📘 INFO SASL Unified format: YES
19:34:31.272 📘 INFO SASL Console level: INFO
19:34:31.272 📘 INFO SASL File level: DEBUG
19:34:31.273 📘 INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log
19:34:31.273 📘 INFO DuckHuntBot 👑 Configured 3 admin(s): peorth, computertech, colby
19:34:31.277 📘 INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json
19:34:31.277 📘 INFO DuckHuntBot.Shop Loaded 9 shop items from /home/colby/duckhunt/shop.json
19:34:31.278 📘 INFO DuckHuntBot 🦆 Starting DuckHunt Bot...
19:34:31.359 📘 INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3)
19:34:31.587 📘 INFO DuckHuntBot ✅ Successfully connected to irc.rizon.net:6697
19:34:31.588 📘 INFO DuckHuntBot 🔐 Sending server password
19:34:31.589 📘 INFO DuckHuntBot 🦆 Bot is now running! Press Ctrl+C to stop.
19:34:33.131 📘 INFO DuckHuntBot Successfully registered with IRC server
19:45:52.686 📘 INFO DuckHuntBot 🛑 Received SIGINT (Ctrl+C), shutting down immediately...
19:45:52.735 📘 INFO DuckHuntBot 🔄 Cancelled 5 running tasks
19:45:52.741 📘 INFO DuckHuntBot Message loop cancelled
19:45:52.743 📘 INFO DuckHuntBot Message loop ended
19:45:52.747 📘 INFO DuckHuntBot 🛑 Main loop cancelled
19:45:52.752 📘 INFO DuckHuntBot.Game Duck spawning loop cancelled
19:45:52.755 📘 INFO DuckHuntBot.Game Duck timeout loop cancelled
19:45:52.761 📘 INFO DuckHuntBot.Game Game loops cancelled
19:45:52.793 🔍 DEBUG DuckHuntBot.DB Database saved successfully with 2 players [save_database:142]
19:45:52.795 📘 INFO DuckHuntBot 💾 Database saved
19:45:53.041 📘 INFO DuckHuntBot 🔌 IRC connection closed
19:45:53.043 📘 INFO DuckHuntBot ✅ Bot shutdown complete
19:48:23.828 📘 INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log
19:48:23.828 📘 INFO DuckHuntBot Debug mode: ON
19:48:23.829 📘 INFO DuckHuntBot Log everything: YES
19:48:23.829 📘 INFO DuckHuntBot Unified format: YES
19:48:23.829 📘 INFO DuckHuntBot Console level: INFO
19:48:23.829 📘 INFO DuckHuntBot File level: DEBUG
19:48:23.830 📘 INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log
19:48:23.830 📘 INFO DuckHuntBot 🤖 Initializing DuckHunt Bot components...
19:48:23.830 📘 INFO DuckHuntBot.DB Loaded 2 players from duckhunt.json
19:48:23.832 📘 INFO SASL Unified logging system initialized: all logs in duckhunt.log
19:48:23.832 📘 INFO SASL Debug mode: ON
19:48:23.832 📘 INFO SASL Log everything: YES
19:48:23.832 📘 INFO SASL Unified format: YES
19:48:23.832 📘 INFO SASL Console level: INFO
19:48:23.833 📘 INFO SASL File level: DEBUG
19:48:23.833 📘 INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log
19:48:23.833 📘 INFO DuckHuntBot Configured 3 admin(s): peorth, computertech, colby
19:48:23.841 📘 INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json
19:48:23.845 📘 INFO DuckHuntBot.Shop Loaded 9 shop items from /home/colby/duckhunt/shop.json
19:48:23.845 📘 INFO DuckHuntBot Starting DuckHunt Bot...
19:48:23.946 📘 INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3)
19:48:24.268 📘 INFO DuckHuntBot ✅ Successfully connected to irc.rizon.net:6697
19:48:24.268 📘 INFO DuckHuntBot 🔐 Sending server password
19:48:24.269 📘 INFO DuckHuntBot Bot is now running! Press Ctrl+C to stop.
19:48:26.087 📘 INFO DuckHuntBot 🛑 Received SIGINT (Ctrl+C), shutting down immediately...
19:48:26.087 📘 INFO DuckHuntBot 🔄 Cancelled 5 running tasks
19:48:26.144 📘 INFO DuckHuntBot.Game Duck timeout loop cancelled
19:48:26.145 📘 INFO DuckHuntBot Message loop cancelled
19:48:26.145 📘 INFO DuckHuntBot Message loop ended
19:48:26.145 📘 INFO DuckHuntBot 🛑 Main loop cancelled
19:48:26.146 📘 INFO DuckHuntBot.Game Duck spawning loop cancelled
19:48:26.147 📘 INFO DuckHuntBot.Game Game loops cancelled
19:48:26.214 🔍 DEBUG DuckHuntBot.DB Database saved successfully with 2 players [save_database:142]
19:48:26.215 📘 INFO DuckHuntBot 💾 Database saved
19:48:26.422 📘 INFO DuckHuntBot 🔌 IRC connection closed
19:48:26.422 📘 INFO DuckHuntBot ✅ Bot shutdown complete

View File

@@ -7,14 +7,14 @@
"duck_flies_away": "The duck flies away. ·°'`'°-.,¸¸.·°'`",
"fast_duck_flies_away": "The fast duck quickly flies away! ·°'`'°-.,¸¸.·°'`",
"golden_duck_flies_away": "The {gold}golden duck{reset} flies away majestically. ·°'`'°-.,¸¸.·°'`",
"bang_hit": "{nick} > *BANG* You shot the duck! [+{xp_gained} xp] [Total ducks: {ducks_shot}]",
"bang_hit_golden": "{nick} > *BANG* You shot a GOLDEN DUCK! [{hp_remaining} HP remaining] [+{xp_gained} xp]",
"bang_hit_golden_killed": "{nick} > *BANG* You killed the GOLDEN DUCK! [+{xp_gained} xp] [Total ducks: {ducks_shot}]",
"bang_hit_fast": "{nick} > *BANG* You shot a FAST DUCK! [+{xp_gained} xp] [Total ducks: {ducks_shot}]",
"bang_miss": "{nick} > *BANG* You missed the duck!",
"bang_friendly_fire_penalty": "{nick} > *BANG* You missed and hit {victim}! [GUN CONFISCATED] [LOST {xp_lost} XP]",
"bang_friendly_fire_insured": "{nick} > *BANG* You missed and hit {victim}! [INSURANCE PROTECTED - No penalties]",
"bang_no_duck": "{nick} > *BANG* What did you shoot at? There is no duck in the area... [GUN CONFISCATED]",
"bang_hit": "{nick} > {red}*BANG*{reset} You shot the duck! [+{xp_gained} xp] [Total ducks: {ducks_shot}]",
"bang_hit_golden": "{nick} > {red}*BANG*{reset} You shot a {gold}GOLDEN DUCK!{reset} [{hp_remaining} HP remaining] [+{xp_gained} xp]",
"bang_hit_golden_killed": "{nick} > {red}*BANG*{reset} You killed the GOLDEN DUCK! [+{xp_gained} xp] [Total ducks: {ducks_shot}]",
"bang_hit_fast": "{nick} > {red}*BANG*{reset} You shot a FAST DUCK! [+{xp_gained} xp] [Total ducks: {ducks_shot}]",
"bang_miss": "{nick} > {red}*BANG*{reset} You missed the duck!",
"bang_friendly_fire_penalty": "{nick} > {red}*BANG*{reset} You missed and hit {victim}! {red}[GUN CONFISCATED]{reset} [LOST {xp_lost} XP]",
"bang_friendly_fire_insured": "{nick} > *BANG* You missed and hit {victim}! {green}[INSURANCE PROTECTED - No penalties]{reset}",
"bang_no_duck": "{nick} > *BANG* What did you shoot at? There is no duck in the area... {red}[GUN CONFISCATED]{reset}",
"bang_no_ammo": "{nick} > *click* You're out of ammo! Use !reload",
"bang_gun_jammed": "{nick} > *click* Your gun jammed! [AMMO WASTED]",
"bang_not_armed": "{nick} > Your gun has been confiscated. Buy it back from the shop.",
@@ -62,6 +62,9 @@
"gift_insurance": "{nick} > Gave Hunter's Insurance to {target_nick} - protecting them from friendly fire!",
"gift_dry_clothes": "{nick} > Gave dry clothes to {target_nick} - now they can stay dry!",
"gift_buy_gun_back": "{nick} > Gave a gun license to {target_nick} - helping them get their gun back!",
"duck_drop_normal": "{nick} > The duck dropped a {green}{item_name}{reset}! [Added to inventory]",
"duck_drop_fast": "{nick} > The {cyan}fast duck{reset} dropped a {green}{item_name}{reset}! [Added to inventory]",
"duck_drop_golden": "{nick} > The {gold}golden duck{reset} dropped a {green}{item_name}{reset}! [Added to inventory]",
"colours": {
"white": "\u00030",

Binary file not shown.

View File

@@ -36,7 +36,7 @@ class DuckHuntBot:
admins_list = self.get_config('admins', ['colby']) or ['colby']
self.admins = [admin.lower() for admin in admins_list]
self.logger.info(f"👑 Configured {len(self.admins)} admin(s): {', '.join(self.admins)}")
self.logger.info(f"Configured {len(self.admins)} admin(s): {', '.join(self.admins)}")
# Initialize level manager first
levels_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'levels.json')
@@ -125,7 +125,7 @@ class DuckHuntBot:
tasks = [t for t in asyncio.all_tasks(loop) if not t.done()]
for task in tasks:
task.cancel()
self.logger.info(f"🔄 Cancelled {len(tasks)} running tasks")
self.logger.info(f"Cancelled {len(tasks)} running tasks")
except Exception as e:
self.logger.error(f"Error cancelling tasks: {e}")
@@ -159,7 +159,7 @@ class DuckHuntBot:
timeout=self.get_config('connection.timeout', 30) or 30.0 # Connection timeout from config
)
self.logger.info(f"Successfully connected to {server}:{port}")
self.logger.info(f"Successfully connected to {server}:{port}")
return
except asyncio.TimeoutError:
@@ -475,6 +475,28 @@ class DuckHuntBot:
message = self.messages.get(result['message_key'], **result['message_args'])
self.send_message(channel, message)
# Check if an item was dropped
if result.get('success') and result.get('dropped_item'):
dropped_item = result['dropped_item']
duck_type = dropped_item['duck_type']
item_name = dropped_item['item_name']
# Send drop notification message
drop_message_key = f'duck_drop_{duck_type}'
drop_message = self.messages.get(drop_message_key,
nick=nick,
item_name=item_name
)
self.send_message(channel, drop_message)
# Send drop notification message
drop_message_key = f'duck_drop_{duck_type}'
drop_message = self.messages.get(drop_message_key,
nick=nick,
item_name=dropped_item['item_name']
)
self.send_message(channel, drop_message)
async def handle_bef(self, nick, channel, player):
"""Handle !bef (befriend) command"""
result = self.game.befriend_duck(nick, channel, player)
@@ -676,23 +698,23 @@ class DuckHuntBot:
if top_xp:
xp_rankings = []
for i, (player_nick, xp) in enumerate(top_xp, 1):
medal = "🥇" if i == 1 else "🥈" if i == 2 else "🥉"
medal = "#1" if i == 1 else "#2" if i == 2 else "#3"
xp_rankings.append(f"{medal} {player_nick}:{xp}XP")
xp_line = f"🏆 {bold}Top XP{reset} " + " | ".join(xp_rankings)
xp_line = f"Top XP: {bold}{reset} " + " | ".join(xp_rankings)
self.send_message(channel, xp_line)
else:
self.send_message(channel, "🏆 No XP data available yet!")
self.send_message(channel, "No XP data available yet!")
# Format ducks shot leaderboard as single line
if top_ducks:
duck_rankings = []
for i, (player_nick, ducks) in enumerate(top_ducks, 1):
medal = "🥇" if i == 1 else "🥈" if i == 2 else "🥉"
medal = "#1" if i == 1 else "#2" if i == 2 else "#3"
duck_rankings.append(f"{medal} {player_nick}:{ducks}")
duck_line = f"🦆 {bold}Top Hunters{reset} " + " | ".join(duck_rankings)
duck_line = f"Top Hunters: {bold}{reset} " + " | ".join(duck_rankings)
self.send_message(channel, duck_line)
else:
self.send_message(channel, "🦆 No duck hunting data available yet!")
self.send_message(channel, "No duck hunting data available yet!")
except Exception as e:
self.logger.error(f"Error in handle_topduck: {e}")
@@ -1167,7 +1189,7 @@ class DuckHuntBot:
game_task = asyncio.create_task(self.game.start_game_loops())
message_task = asyncio.create_task(self.message_loop())
self.logger.info("🦆 Bot is now running! Press Ctrl+C to stop.")
self.logger.info("Bot is now running! Press Ctrl+C to stop.")
# Wait for shutdown signal or task completion with frequent checks
while not self.shutdown_requested:
done, _pending = await asyncio.wait(
@@ -1181,12 +1203,12 @@ class DuckHuntBot:
break
break
self.logger.info("🔄 Shutdown initiated, cleaning up...")
self.logger.info("Shutdown initiated, cleaning up...")
except asyncio.CancelledError:
self.logger.info("🛑 Main loop cancelled")
except Exception as e:
self.logger.error(f"Bot error: {e}")
self.logger.error(f"Bot error: {e}")
finally:
# Fast cleanup - cancel tasks immediately with short timeout
tasks_to_cancel = [task for task in [game_task, message_task] if task and not task.done()]
@@ -1201,19 +1223,19 @@ class DuckHuntBot:
timeout=1.0
)
except asyncio.TimeoutError:
self.logger.warning("⚠️ Task cancellation timed out")
self.logger.warning("Task cancellation timed out")
# Quick database save
try:
self.db.save_database()
self.logger.info("💾 Database saved")
self.logger.info("Database saved")
except Exception as e:
self.logger.error(f"Error saving database: {e}")
self.logger.error(f"Error saving database: {e}")
# Fast connection close
await self._close_connection()
self.logger.info("Bot shutdown complete")
self.logger.info("Bot shutdown complete")
async def _close_connection(self):
"""Close IRC connection with comprehensive error handling"""
@@ -1235,14 +1257,14 @@ class DuckHuntBot:
self.writer.close()
await asyncio.wait_for(self.writer.wait_closed(), timeout=2.0)
except asyncio.TimeoutError:
self.logger.warning("⚠️ Connection close timed out - forcing close")
self.logger.warning("Connection close timed out - forcing close")
except Exception as e:
self.logger.debug(f"Error during connection close: {e}")
self.logger.info("🔌 IRC connection closed")
except Exception as e:
self.logger.error(f"Critical error closing connection: {e}")
self.logger.error(f"Critical error closing connection: {e}")
finally:
# Ensure writer is cleared regardless of errors
self.writer = None

View File

@@ -286,8 +286,13 @@ class DuckGame:
if self.bot.get_config('duck_spawning.rearm_on_duck_shot', False):
self._rearm_all_disarmed_players()
# Check for item drops
dropped_item = self._check_item_drop(player, duck_type)
self.db.save_database()
return {
# Include drop info in the return
result = {
'success': True,
'hit': True,
'message_key': message_key,
@@ -297,6 +302,12 @@ class DuckGame:
'ducks_shot': player['ducks_shot']
}
}
# Add drop info if an item was dropped
if dropped_item:
result['dropped_item'] = dropped_item
return result
else:
# Miss! Duck stays in the channel
player['shots_missed'] = player.get('shots_missed', 0) + 1 # Track missed shots
@@ -565,3 +576,64 @@ class DuckGame:
except Exception as e:
self.logger.error(f"Error cleaning expired effects: {e}")
def _check_item_drop(self, player, duck_type):
"""
Check if the duck drops an item and add it to player's inventory
Returns the dropped item info or None
"""
import random
try:
# Get drop chance for this duck type
drop_chance = self.bot.get_config(f'duck_types.{duck_type}.drop_chance', 0.0)
# Roll for drop
if random.random() > drop_chance:
return None # No drop
# Get drop table for this duck type
drop_table_key = f'{duck_type}_duck_drops'
drop_table = self.bot.get_config(f'item_drops.{drop_table_key}', [])
if not drop_table:
self.logger.warning(f"No drop table found for {duck_type} duck")
return None
# Weighted random selection
total_weight = sum(item.get('weight', 1) for item in drop_table)
if total_weight <= 0:
return None
random_weight = random.randint(1, total_weight)
current_weight = 0
for drop_item in drop_table:
current_weight += drop_item.get('weight', 1)
if random_weight <= current_weight:
item_id = drop_item.get('item_id')
if item_id:
# Add item to player's inventory
inventory = player.get('inventory', {})
item_key = str(item_id)
inventory[item_key] = inventory.get(item_key, 0) + 1
player['inventory'] = inventory
# Get item info from shop
item_info = self.bot.shop.get_item(item_id)
item_name = item_info.get('name', f'Item {item_id}') if item_info else f'Item {item_id}'
self.logger.info(f"Duck dropped {item_name} for player {player.get('nick', 'Unknown')}")
return {
'item_id': item_id,
'item_name': item_name,
'duck_type': duck_type
}
break
return None
except Exception as e:
self.logger.error(f"Error in _check_item_drop: {e}")
return None

View File

@@ -33,9 +33,9 @@ def load_config():
class EnhancedColourFormatter(logging.Formatter):
"""Enhanced console formatter with colors, emojis, and better formatting"""
"""Enhanced colour formatter for different log levels"""
# ANSI color codes with styles
# ANSI color codes
COLORS = {
'DEBUG': '\033[36m', # Cyan
'INFO': '\033[32m', # Green
@@ -48,13 +48,13 @@ class EnhancedColourFormatter(logging.Formatter):
'UNDERLINE': '\033[4m', # Underline
}
# Emojis for different log levels
# Log level prefixes
EMOJIS = {
'DEBUG': '🔍',
'INFO': '📘',
'WARNING': '⚠️',
'ERROR': '',
'CRITICAL': '💥',
'DEBUG': 'DEBUG',
'INFO': 'INFO',
'WARNING': 'WARNING',
'ERROR': 'ERROR',
'CRITICAL': 'CRITICAL',
}
# Component colors
@@ -75,8 +75,8 @@ class EnhancedColourFormatter(logging.Formatter):
bold = self.COLORS['BOLD']
dim = self.COLORS['DIM']
# Get emoji
emoji = self.EMOJIS.get(record.levelname, '📝')
# Get level prefix
emoji = self.EMOJIS.get(record.levelname, 'LOG')
# Format timestamp
timestamp = datetime.fromtimestamp(record.created).strftime('%H:%M:%S.%f')[:-3]
@@ -109,18 +109,18 @@ class EnhancedColourFormatter(logging.Formatter):
class EnhancedFileFormatter(logging.Formatter):
"""Enhanced file formatter matching console format (no colors)"""
# Emojis for different log levels (same as console)
# Log level prefixes (same as console)
EMOJIS = {
'DEBUG': '🔍',
'INFO': '📘',
'WARNING': '⚠️',
'ERROR': '',
'CRITICAL': '💥',
'DEBUG': 'DEBUG',
'INFO': 'INFO',
'WARNING': 'WARNING',
'ERROR': 'ERROR',
'CRITICAL': 'CRITICAL',
}
def format(self, record):
# Get emoji (same as console)
emoji = self.EMOJIS.get(record.levelname, '📝')
# Get level prefix (same as console)
emoji = self.EMOJIS.get(record.levelname, 'LOG')
# Format timestamp (same as console - just time, not date)
timestamp = datetime.fromtimestamp(record.created).strftime('%H:%M:%S.%f')[:-3]
@@ -169,13 +169,13 @@ class UnifiedFormatter(logging.Formatter):
'DIM': '\033[2m', # Dim
}
# Emojis for different log levels
# Log level prefixes
EMOJIS = {
'DEBUG': '🔍',
'INFO': '📘',
'WARNING': '⚠️',
'ERROR': '',
'CRITICAL': '💥',
'DEBUG': 'DEBUG',
'INFO': 'INFO',
'WARNING': 'WARNING',
'ERROR': 'ERROR',
'CRITICAL': 'CRITICAL',
}
# Component colors
@@ -193,8 +193,8 @@ class UnifiedFormatter(logging.Formatter):
self.use_colors = use_colors
def format(self, record):
# Get emoji
emoji = self.EMOJIS.get(record.levelname, '📝')
# Get level prefix
emoji = self.EMOJIS.get(record.levelname, 'LOG')
# Format timestamp (same for both)
timestamp = datetime.fromtimestamp(record.created).strftime('%H:%M:%S.%f')[:-3]
@@ -350,7 +350,7 @@ def setup_logger(name="DuckHuntBot", console_level=None, file_level=None):
simple_handler.setFormatter(simple_formatter)
logger.addHandler(simple_handler)
logger.error(f"❌ Failed to setup enhanced file logging: {e}")
logger.info("📝 Using fallback file logging")
logger.info("Using fallback file logging")
except Exception as fallback_error:
logger.error(f"💥 Complete logging setup failure: {fallback_error}")

View File

@@ -36,7 +36,7 @@ class MessageManager:
"duck_spawn": [
"・゜゜・。。・゜゜\\_o< QUACK! A duck has appeared! Type !bang to shoot it!",
"・゜゜・。。・゜゜\\_o< *flap flap* A wild duck landed! Use !bang to hunt it!",
"🦆 A duck swoops into view! Quick, type !bang before it escapes!",
"A duck swoops into view! Quick, type !bang before it escapes!",
"・゜゜・。。・゜゜\\_o< Quack quack! Fresh duck spotted! !bang to bag it!",
"*rustling* A duck waddles out from the bushes! Fire with !bang!",
"・゜゜・。。・゜゜\\_o< Splash! A duck surfaces! Shoot it with !bang!"