Security fixes, UI improvements, and game balance updates

- Fixed critical security vulnerabilities in shop targeting system
- Fixed admin authentication bypass issues
- Fixed auto-rearm feature config path (duck_spawning.rearm_on_duck_shot)
- Updated duck spawn timing to 20-60 minutes for better game balance
- Enhanced inventory display formatting with proper spacing
- Added comprehensive admin security documentation
This commit is contained in:
2025-09-26 19:06:26 +01:00
parent 5484548c30
commit f3a9c5b611
21 changed files with 1162 additions and 256 deletions

46
ADMIN_SECURITY.md Normal file
View File

@@ -0,0 +1,46 @@
# Enhanced Admin Configuration
For better security, update your `config.json` to use hostmask-based admin authentication:
## Current (Less Secure) - Nick Only:
```json
{
"admins": [
"peorth",
"computertech",
"colby"
]
}
```
## Recommended (More Secure) - Hostmask Based:
```json
{
"admins": [
{
"nick": "peorth",
"hostmask": "peorth!*@trusted.domain.com"
},
{
"nick": "computertech",
"hostmask": "computertech!*@*.isp.net"
},
{
"nick": "colby",
"hostmask": "colby!user@192.168.*.*"
}
]
}
```
## Migration Notes:
- The bot supports both formats for backward compatibility
- Nick-only authentication generates security warnings in logs
- Hostmask patterns use shell-style wildcards (* and ?)
- Consider using registered nick services for additional security
## Security Benefits:
- Prevents nick spoofing attacks
- Allows IP/hostname restrictions
- Provides audit logging of admin access
- Maintains backward compatibility during migration

127
CONFIG.md
View File

@@ -1,127 +0,0 @@
# DuckHunt Bot Configuration Guide
This document explains all configuration options in `config.json`.
## 📡 IRC Connection Settings (`connection`)
| Setting | Description | Example |
|---------|-------------|---------|
| `server` | IRC server hostname | `"irc.rizon.net"` |
| `port` | IRC server port | `6697` (SSL) or `6667` (non-SSL) |
| `nick` | Bot's nickname on IRC | `"DuckHunt"` |
| `channels` | List of channels to auto-join | `["#channel1", "#channel2"]` |
| `ssl` | Use SSL/TLS encryption | `true` (recommended) |
| `password` | Server password (I-Line auth) | Change if server requires auth |
| `max_retries` | Connection retry attempts | `3` |
| `retry_delay` | Seconds between retries | `5` |
| `timeout` | Connection timeout in seconds | `30` |
## 🔐 SASL Authentication (`sasl`)
SASL is used for NickServ authentication on connect.
| Setting | Description | Example |
|---------|-------------|---------|
| `enabled` | Enable SASL authentication | `false` |
| `username` | Registered nickname | `"your_registered_nick"` |
| `password` | NickServ password | `"your_nickserv_password"` |
**Note:** Change `password` from the default if enabling SASL!
## 👑 Bot Administration (`admins`)
Array of IRC nicknames with admin privileges.
```json
"admins": ["peorth", "computertech", "colby"]
```
## 🦆 Duck Spawning (`duck_spawning`)
| Setting | Description | Default |
|---------|-------------|---------|
| `spawn_min` | Minimum seconds between spawns | `10` |
| `spawn_max` | Maximum seconds between spawns | `30` |
| `timeout` | Global fallback timeout | `60` |
| `rearm_on_duck_shot` | Auto-rearm guns when duck shot | `true` |
## 🎯 Duck Types (`duck_types`)
### Normal Ducks (`normal`)
- `xp`: XP reward (default: `10`)
- `timeout`: Seconds before flying away (default: `60`)
### Golden Ducks (`golden`)
- `chance`: Spawn probability (default: `0.15` = 15%)
- `min_hp`: Minimum hit points (default: `3`)
- `max_hp`: Maximum hit points (default: `5`)
- `xp`: XP reward (default: `15`)
- `timeout`: Seconds before flying away (default: `60`)
### Fast Ducks (`fast`)
- `chance`: Spawn probability (default: `0.25` = 25%)
- `timeout`: Seconds before flying away (default: `20`)
- `xp`: XP reward (default: `12`)
**Note:** Chances are decimal percentages (0.15 = 15%, 0.25 = 25%)
## 👤 New Player Defaults (`player_defaults`)
Starting values for new players:
| Setting | Description | Default |
|---------|-------------|---------|
| `accuracy` | Starting accuracy percentage (0-100) | `75` |
| `magazines` | Starting number of magazines | `3` |
| `bullets_per_magazine` | Bullets per magazine | `6` |
| `jam_chance` | Gun jam percentage (0-100) | `15` |
| `xp` | Starting XP (also currency) | `0` |
## 🎮 Game Mechanics (`gameplay`)
| Setting | Description | Default |
|---------|-------------|---------|
| `befriend_success_rate` | Base befriend chance (%) | `75` |
| `befriend_xp` | XP from befriending | `5` |
| `accuracy_gain_on_hit` | Accuracy boost per hit | `1` |
| `accuracy_loss_on_miss` | Accuracy loss per miss | `2` |
| `min_accuracy` | Minimum accuracy limit | `10` |
| `max_accuracy` | Maximum accuracy limit | `100` |
| `min_befriend_success_rate` | Min befriend rate | `5` |
| `max_befriend_success_rate` | Max befriend rate | `95` |
## 🔧 Feature Toggles (`features`)
| Setting | Description | Default |
|---------|-------------|---------|
| `shop_enabled` | Enable shop system | `true` |
| `inventory_enabled` | Enable inventory system | `true` |
| `auto_rearm_enabled` | Enable auto gun rearming | `true` |
## ⚖️ System Limits (`limits`)
| Setting | Description | Default |
|---------|-------------|---------|
| `max_inventory_items` | Max items per player | `20` |
| `max_temp_effects` | Max temporary effects | `20` |
---
## 🔧 Configuration Access
The bot uses dot notation to access nested settings:
```python
# Examples:
server = bot.get_config('connection.server')
normal_xp = bot.get_config('duck_types.normal.xp')
player_accuracy = bot.get_config('player_defaults.accuracy')
```
## 📝 Tips
1. **Percentages:** Most percentage values use 0-100 scale, but spawn chances use 0.0-1.0 decimals
2. **Authentication:** Set real passwords when using server auth or SASL
3. **Balance:** Adjust XP rewards and duck spawn rates to balance gameplay
4. **Testing:** Change one setting at a time to test effects
5. **Backup:** Keep a backup of working config before major changes

View File

@@ -2,7 +2,7 @@
"connection": {
"server": "irc.rizon.net",
"port": 6697,
"nick": "DuckHunt",
"nick": "DickHunt",
"channels": [
"#ct"
],
@@ -13,7 +13,7 @@
"timeout": 30
},
"sasl": {
"enabled": true,
"enabled": false,
"username": "duckhunt",
"password": "duckhunt//789//"
},
@@ -24,8 +24,8 @@
"colby"
],
"duck_spawning": {
"spawn_min": 10,
"spawn_max": 30,
"spawn_min": 1200,
"spawn_max": 3600,
"timeout": 60,
"rearm_on_duck_shot": true
},
@@ -62,7 +62,8 @@
"min_accuracy": 10,
"max_accuracy": 100,
"min_befriend_success_rate": 5,
"max_befriend_success_rate": 95
"max_befriend_success_rate": 95,
"wet_clothes_duration": 300
},
"features": {
"shop_enabled": true,

View File

@@ -1,19 +1,23 @@
{
"players": {
"computertech": {
"nick": "ComputerTech",
"xp": 60,
"ducks_shot": 5,
"nick": "computertech",
"xp": 67,
"ducks_shot": 6,
"ducks_befriended": 2,
"accuracy": 62,
"shots_fired": 2,
"shots_missed": 1,
"accuracy": 63,
"gun_confiscated": false,
"current_ammo": 5,
"current_ammo": 6,
"magazines": 3,
"bullets_per_magazine": 6,
"jam_chance": 5,
"inventory": {},
"inventory": {
"1": 1
},
"temporary_effects": []
}
},
"last_save": "1758825127.4272072"
"last_save": "1758909688.9995604"
}

View File

@@ -651,3 +651,78 @@
2025-09-24 18:56:53,434 [INFO ] DuckHuntBot.Shop - load_items:30: Loaded 5 shop items from /home/colby/duckhunt/shop.json
2025-09-24 18:56:53,436 [INFO ] DuckHuntBot.Levels - load_levels:28: Loaded 8 levels from /home/colby/duckhunt/levels.json
2025-09-24 18:56:53,439 [INFO ] DuckHuntBot.DB - load_database:47: Loaded 1 players from duckhunt.json
2025-09-25 18:05:24,467 [INFO ] DuckHuntBot - setup_logger:61: Enhanced logging system initialized with file rotation
2025-09-25 18:05:24,469 [INFO ] DuckHuntBot.DB - load_database:48: Loaded 1 players from duckhunt.json
2025-09-25 18:05:24,470 [INFO ] SASL - setup_logger:61: Enhanced logging system initialized with file rotation
2025-09-25 18:05:24,471 [INFO ] DuckHuntBot.Shop - load_items:30: Loaded 5 shop items from /home/colby/duckhunt/shop.json
2025-09-25 18:05:24,478 [INFO ] DuckHuntBot.Levels - load_levels:28: Loaded 8 levels from /home/colby/duckhunt/levels.json
2025-09-25 18:05:24,479 [INFO ] DuckHuntBot - main:28: 🦆 Starting DuckHunt Bot...
2025-09-25 18:05:24,568 [INFO ] DuckHuntBot - connect:115: Attempting to connect to irc.rizon.net:6697 (attempt 1/3)
2025-09-25 18:05:25,012 [INFO ] DuckHuntBot - connect:126: ✅ Successfully connected to irc.rizon.net:6697
2025-09-25 18:05:25,013 [INFO ] SASL - start_negotiation:45: SASL authentication enabled
2025-09-25 18:05:25,013 [INFO ] DuckHuntBot - run:774: 🦆 Bot is now running! Press Ctrl+C to stop.
2025-09-25 18:05:27,048 [INFO ] SASL - handle_cap_response:59: Server capabilities: ['away-notify', 'chghost', 'invite-notify', 'multi-prefix', 'sasl', 'userhost-in-names']
2025-09-25 18:05:27,048 [INFO ] SASL - handle_cap_response:61: SASL capability available
2025-09-25 18:05:27,098 [INFO ] SASL - handle_cap_response:72: SASL capability acknowledged
2025-09-25 18:05:27,098 [INFO ] SASL - handle_cap_response:74: Authenticating via SASL as duckhunt
2025-09-25 18:05:27,099 [INFO ] SASL - handle_sasl:94: Sending AUTHENTICATE PLAIN
2025-09-25 18:05:27,507 [INFO ] SASL - handle_authenticate_response:103: Server ready for SASL authentication
2025-09-25 18:05:27,508 [DEBUG ] SASL - handle_authenticate_response:106: Auth string length: 33 chars
2025-09-25 18:05:27,508 [DEBUG ] SASL - handle_authenticate_response:107: Auth components: user='duckhunt', pass='duc...'
2025-09-25 18:05:27,508 [DEBUG ] SASL - handle_authenticate_response:110: Base64 encoded length: 44 chars
2025-09-25 18:05:27,508 [DEBUG ] SASL - handle_authenticate_response:111: Sending: AUTHENTICATE ZHVja2h1bnQAZHVja2h1...
2025-09-25 18:05:27,918 [INFO ] SASL - handle_sasl_result:123: SASL authentication successful!
2025-09-25 18:05:28,019 [INFO ] DuckHuntBot - handle_message:232: Successfully registered with IRC server
2025-09-25 18:05:47,029 [INFO ] DuckHuntBot.Game - spawn_duck:149: Fast duck (hidden) spawned in #ct
2025-09-25 18:05:51,297 [DEBUG ] DuckHuntBot.DB - save_database:134: Database saved successfully with 1 players
2025-09-25 18:06:06,048 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:06:13,814 [DEBUG ] DuckHuntBot.DB - save_database:134: Database saved successfully with 1 players
2025-09-25 18:06:24,064 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:07:26,130 [INFO ] DuckHuntBot.Game - spawn_duck:149: Fast duck (hidden) spawned in #ct
2025-09-25 18:07:56,160 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 3 HP
2025-09-25 18:09:17,234 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:10:24,298 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:11:42,412 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 4 HP
2025-09-25 18:12:55,485 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:14:06,578 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 3 HP
2025-09-25 18:15:14,722 [INFO ] DuckHuntBot.Game - spawn_duck:149: Fast duck (hidden) spawned in #ct
2025-09-25 18:15:54,792 [INFO ] DuckHuntBot.Game - spawn_duck:149: Fast duck (hidden) spawned in #ct
2025-09-25 18:16:15,812 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:17:22,878 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:18:31,953 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:19:49,029 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:20:57,099 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:22:04,175 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:23:19,252 [INFO ] DuckHuntBot.Game - spawn_duck:149: Fast duck (hidden) spawned in #ct
2025-09-25 18:23:46,278 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:24:57,354 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 3 HP
2025-09-25 18:26:17,435 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:27:24,505 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:28:33,580 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:29:37,647 [INFO ] DuckHuntBot.Game - spawn_duck:149: Fast duck (hidden) spawned in #ct
2025-09-25 18:30:17,686 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:31:35,762 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:33:01,847 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:34:13,914 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:35:27,982 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:36:39,046 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:37:41,102 [INFO ] DuckHuntBot.Game - spawn_duck:149: Fast duck (hidden) spawned in #ct
2025-09-25 18:38:18,132 [INFO ] DuckHuntBot.Game - spawn_duck:149: Fast duck (hidden) spawned in #ct
2025-09-25 18:38:44,166 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:40:12,271 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:41:23,349 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 3 HP
2025-09-25 18:42:28,415 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:43:52,494 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 4 HP
2025-09-25 18:45:03,560 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 3 HP
2025-09-25 18:46:04,620 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:47:20,702 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:48:29,761 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:49:35,827 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:50:48,894 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:51:51,944 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:52:58,006 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:53:59,056 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 4 HP
2025-09-25 18:55:08,121 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:56:27,201 [INFO ] DuckHuntBot.Game - spawn_duck:161: Normal duck spawned in #ct
2025-09-25 18:57:44,273 [INFO ] DuckHuntBot.Game - spawn_duck:137: Golden duck (hidden) spawned in #ct with 3 HP

412
logs/duckhunt.log Normal file
View File

@@ -0,0 +1,412 @@
19:30:08.709 📘 INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log
19:30:08.709 📘 INFO DuckHuntBot Debug mode: ON
19:30:08.710 📘 INFO DuckHuntBot Log everything: YES
19:30:08.710 📘 INFO DuckHuntBot Unified format: YES
19:30:08.710 📘 INFO DuckHuntBot Console level: INFO
19:30:08.710 📘 INFO DuckHuntBot File level: DEBUG
19:30:08.711 📘 INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log
19:30:08.711 📘 INFO DuckHuntBot 🤖 Initializing DuckHunt Bot components...
19:30:08.712 📘 INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json
19:30:08.713 📘 INFO SASL Unified logging system initialized: all logs in duckhunt.log
19:30:08.713 📘 INFO SASL Debug mode: ON
19:30:08.714 📘 INFO SASL Log everything: YES
19:30:08.714 📘 INFO SASL Unified format: YES
19:30:08.714 📘 INFO SASL Console level: INFO
19:30:08.715 📘 INFO SASL File level: DEBUG
19:30:08.715 📘 INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log
19:30:08.715 📘 INFO DuckHuntBot 👑 Configured 3 admin(s): peorth, computertech, colby
19:30:08.716 📘 INFO DuckHuntBot.Shop Loaded 5 shop items from /home/colby/duckhunt/shop.json
19:30:08.718 📘 INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json
19:30:08.718 📘 INFO DuckHuntBot 🦆 Starting DuckHunt Bot...
19:30:08.807 📘 INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3)
19:30:09.028 📘 INFO DuckHuntBot ✅ Successfully connected to irc.rizon.net:6697
19:30:09.028 📘 INFO DuckHuntBot 🔐 Sending server password
19:30:09.028 📘 INFO SASL SASL authentication enabled
19:30:09.029 📘 INFO DuckHuntBot 🦆 Bot is now running! Press Ctrl+C to stop.
19:30:09.416 📘 INFO SASL Server capabilities: ['away-notify', 'chghost', 'invite-notify', 'multi-prefix', 'sasl', 'userhost-in-names']
19:30:09.417 📘 INFO SASL SASL capability available
19:30:09.476 📘 INFO SASL SASL capability acknowledged
19:30:09.477 📘 INFO SASL Authenticating via SASL as duckhunt
19:30:09.477 📘 INFO SASL Sending AUTHENTICATE PLAIN
19:30:09.887 📘 INFO SASL Server ready for SASL authentication
19:30:09.888 🔍 DEBUG SASL Auth string length: 33 chars [handle_authenticate_response:106]
19:30:09.888 🔍 DEBUG SASL Auth components: user='duckhunt', pass='duc...' [handle_authenticate_response:107]
19:30:09.888 🔍 DEBUG SASL Base64 encoded length: 44 chars [handle_authenticate_response:110]
19:30:09.889 🔍 DEBUG SASL Sending: AUTHENTICATE ZHVja2h1bnQAZHVja2h1... [handle_authenticate_response:111]
19:30:10.304 📘 INFO SASL SASL authentication successful!
19:30:10.430 📘 INFO DuckHuntBot Successfully registered with IRC server
19:30:34.049 📘 INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct
19:31:07.073 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
19:31:24.462 📘 INFO DuckHuntBot 🛑 Received SIGINT (Ctrl+C), shutting down immediately...
19:31:24.463 📘 INFO DuckHuntBot 🔄 Cancelled 5 running tasks
19:31:24.544 📘 INFO DuckHuntBot Message loop cancelled
19:31:24.544 📘 INFO DuckHuntBot Message loop ended
19:31:24.545 📘 INFO DuckHuntBot.Game Duck spawning loop cancelled
19:31:24.545 📘 INFO DuckHuntBot.Game Duck timeout loop cancelled
19:31:24.546 📘 INFO DuckHuntBot 🛑 Main loop cancelled
19:31:24.547 📘 INFO DuckHuntBot.Game Game loops cancelled
19:31:24.570 🔍 DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:134]
19:31:24.571 📘 INFO DuckHuntBot 💾 Database saved
19:31:24.773 📘 INFO DuckHuntBot 🔌 IRC connection closed
19:31:24.774 📘 INFO DuckHuntBot ✅ Bot shutdown complete
19:31:25.531 📘 INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log
19:31:25.531 📘 INFO DuckHuntBot Debug mode: ON
19:31:25.531 📘 INFO DuckHuntBot Log everything: YES
19:31:25.531 📘 INFO DuckHuntBot Unified format: YES
19:31:25.532 📘 INFO DuckHuntBot Console level: INFO
19:31:25.532 📘 INFO DuckHuntBot File level: DEBUG
19:31:25.532 📘 INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log
19:31:25.532 📘 INFO DuckHuntBot 🤖 Initializing DuckHunt Bot components...
19:31:25.533 📘 INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json
19:31:25.533 📘 INFO SASL Unified logging system initialized: all logs in duckhunt.log
19:31:25.534 📘 INFO SASL Debug mode: ON
19:31:25.534 📘 INFO SASL Log everything: YES
19:31:25.534 📘 INFO SASL Unified format: YES
19:31:25.534 📘 INFO SASL Console level: INFO
19:31:25.534 📘 INFO SASL File level: DEBUG
19:31:25.535 📘 INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log
19:31:25.535 📘 INFO DuckHuntBot 👑 Configured 3 admin(s): peorth, computertech, colby
19:31:25.536 📘 INFO DuckHuntBot.Shop Loaded 5 shop items from /home/colby/duckhunt/shop.json
19:31:25.537 📘 INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json
19:31:25.537 📘 INFO DuckHuntBot 🦆 Starting DuckHunt Bot...
19:31:25.621 📘 INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3)
19:31:25.815 📘 INFO DuckHuntBot ✅ Successfully connected to irc.rizon.net:6697
19:31:25.815 📘 INFO DuckHuntBot 🔐 Sending server password
19:31:25.816 📘 INFO SASL SASL authentication enabled
19:31:25.816 📘 INFO DuckHuntBot 🦆 Bot is now running! Press Ctrl+C to stop.
19:31:27.051 📘 INFO SASL Server capabilities: ['away-notify', 'chghost', 'invite-notify', 'multi-prefix', 'sasl', 'userhost-in-names']
19:31:27.051 📘 INFO SASL SASL capability available
19:31:27.100 📘 INFO SASL SASL capability acknowledged
19:31:27.103 📘 INFO SASL Authenticating via SASL as duckhunt
19:31:27.103 📘 INFO SASL Sending AUTHENTICATE PLAIN
19:31:27.512 📘 INFO SASL Server ready for SASL authentication
19:31:27.513 🔍 DEBUG SASL Auth string length: 33 chars [handle_authenticate_response:106]
19:31:27.513 🔍 DEBUG SASL Auth components: user='duckhunt', pass='duc...' [handle_authenticate_response:107]
19:31:27.513 🔍 DEBUG SASL Base64 encoded length: 44 chars [handle_authenticate_response:110]
19:31:27.513 🔍 DEBUG SASL Sending: AUTHENTICATE ZHVja2h1bnQAZHVja2h1... [handle_authenticate_response:111]
19:31:27.922 📘 INFO SASL SASL authentication successful!
19:31:29.097 📘 INFO DuckHuntBot Successfully registered with IRC server
19:31:46.833 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
19:32:07.421 📘 INFO DuckHuntBot 🛑 Received SIGINT (Ctrl+C), shutting down immediately...
19:32:07.422 📘 INFO DuckHuntBot 🔄 Cancelled 5 running tasks
19:32:07.424 📘 INFO DuckHuntBot Message loop cancelled
19:32:07.424 📘 INFO DuckHuntBot Message loop ended
19:32:07.425 📘 INFO DuckHuntBot.Game Duck spawning loop cancelled
19:32:07.425 📘 INFO DuckHuntBot.Game Duck timeout loop cancelled
19:32:07.425 📘 INFO DuckHuntBot 🛑 Main loop cancelled
19:32:07.426 📘 INFO DuckHuntBot.Game Game loops cancelled
19:32:07.439 🔍 DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:134]
19:32:07.439 📘 INFO DuckHuntBot 💾 Database saved
19:32:07.647 📘 INFO DuckHuntBot 🔌 IRC connection closed
19:32:07.650 📘 INFO DuckHuntBot ✅ Bot shutdown complete
17:11:18.874 📘 INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log
17:11:18.874 📘 INFO DuckHuntBot Debug mode: ON
17:11:18.874 📘 INFO DuckHuntBot Log everything: YES
17:11:18.875 📘 INFO DuckHuntBot Unified format: YES
17:11:18.875 📘 INFO DuckHuntBot Console level: INFO
17:11:18.875 📘 INFO DuckHuntBot File level: DEBUG
17:11:18.875 📘 INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log
17:11:18.876 📘 INFO DuckHuntBot 🤖 Initializing DuckHunt Bot components...
17:11:18.883 📘 INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json
17:11:18.884 📘 INFO SASL Unified logging system initialized: all logs in duckhunt.log
17:11:18.884 📘 INFO SASL Debug mode: ON
17:11:18.884 📘 INFO SASL Log everything: YES
17:11:18.885 📘 INFO SASL Unified format: YES
17:11:18.885 📘 INFO SASL Console level: INFO
17:11:18.885 📘 INFO SASL File level: DEBUG
17:11:18.885 📘 INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log
17:11:18.885 📘 INFO DuckHuntBot 👑 Configured 3 admin(s): peorth, computertech, colby
17:11:18.886 📘 INFO DuckHuntBot.Shop Loaded 6 shop items from /home/colby/duckhunt/shop.json
17:11:18.886 📘 INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json
17:11:18.886 📘 INFO DuckHuntBot 🦆 Starting DuckHunt Bot...
17:11:18.941 📘 INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3)
17:11:19.228 📘 INFO DuckHuntBot ✅ Successfully connected to irc.rizon.net:6697
17:11:19.228 📘 INFO DuckHuntBot 🔐 Sending server password
17:11:19.228 📘 INFO DuckHuntBot 🦆 Bot is now running! Press Ctrl+C to stop.
17:11:21.111 📘 INFO DuckHuntBot Successfully registered with IRC server
17:11:38.245 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
17:12:50.310 📘 INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct
17:13:11.327 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
17:14:31.393 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
17:15:37.451 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
17:16:52.528 📘 INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 5 HP
17:17:56.586 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
17:18:59.645 📘 INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct
17:19:06.831 🔍 DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:136]
17:19:14.373 🔍 DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:136]
17:19:16.665 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
17:20:39.736 📘 INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 3 HP
17:21:48.800 📘 INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 5 HP
17:22:49.858 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
17:24:01.922 📘 INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 4 HP
17:25:10.997 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
17:26:32.071 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
17:27:35.135 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
17:28:57.215 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
17:30:15.284 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
17:31:30.355 📘 INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct
17:32:06.393 📘 INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 5 HP
17:33:13.455 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
17:34:22.518 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
17:35:24.570 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
17:36:37.639 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
17:37:54.733 📘 INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct
17:38:16.763 📘 INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 5 HP
17:39:18.620 📘 INFO DuckHuntBot 🛑 Received SIGINT (Ctrl+C), shutting down immediately...
17:39:20.268 📘 INFO DuckHuntBot 🔄 Cancelled 5 running tasks
17:39:20.271 📘 INFO DuckHuntBot 🛑 Main loop cancelled
17:39:20.272 📘 INFO DuckHuntBot.Game Duck spawning loop cancelled
17:39:20.279 📘 INFO DuckHuntBot.Game Duck timeout loop cancelled
17:39:20.279 📘 INFO DuckHuntBot Message loop cancelled
17:39:20.280 📘 INFO DuckHuntBot Message loop ended
17:39:20.280 📘 INFO DuckHuntBot.Game Game loops cancelled
17:39:20.321 🔍 DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:136]
17:39:20.321 📘 INFO DuckHuntBot 💾 Database saved
17:39:20.567 📘 INFO DuckHuntBot 🔌 IRC connection closed
17:39:20.568 📘 INFO DuckHuntBot ✅ Bot shutdown complete
18:37:16.947 📘 INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log
18:37:16.948 📘 INFO DuckHuntBot Debug mode: ON
18:37:16.949 📘 INFO DuckHuntBot Log everything: YES
18:37:16.949 📘 INFO DuckHuntBot Unified format: YES
18:37:16.949 📘 INFO DuckHuntBot Console level: INFO
18:37:16.949 📘 INFO DuckHuntBot File level: DEBUG
18:37:16.950 📘 INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log
18:37:16.950 📘 INFO DuckHuntBot 🤖 Initializing DuckHunt Bot components...
18:37:16.958 📘 INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json
18:37:16.959 📘 INFO SASL Unified logging system initialized: all logs in duckhunt.log
18:37:16.960 📘 INFO SASL Debug mode: ON
18:37:16.960 📘 INFO SASL Log everything: YES
18:37:16.960 📘 INFO SASL Unified format: YES
18:37:16.960 📘 INFO SASL Console level: INFO
18:37:16.960 📘 INFO SASL File level: DEBUG
18:37:16.960 📘 INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log
18:37:16.961 📘 INFO DuckHuntBot 👑 Configured 3 admin(s): peorth, computertech, colby
18:37:16.962 📘 INFO DuckHuntBot.Shop Loaded 9 shop items from /home/colby/duckhunt/shop.json
18:37:16.962 📘 INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json
18:37:16.963 📘 INFO DuckHuntBot 🦆 Starting DuckHunt Bot...
18:37:17.032 📘 INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3)
18:37:17.428 📘 INFO DuckHuntBot ✅ Successfully connected to irc.rizon.net:6697
18:37:17.428 📘 INFO DuckHuntBot 🔐 Sending server password
18:37:17.429 📘 INFO DuckHuntBot 🦆 Bot is now running! Press Ctrl+C to stop.
18:37:19.105 📘 INFO DuckHuntBot Successfully registered with IRC server
18:37:32.441 📘 INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 4 HP
18:38:34.495 📘 INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 4 HP
18:39:37.167 🔍 DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:142]
18:39:43.570 📘 INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 3 HP
18:40:51.627 📘 INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct
18:41:14.648 📘 INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 4 HP
18:42:21.722 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
18:43:25.380 📘 INFO DuckHuntBot 🛑 Received SIGINT (Ctrl+C), shutting down immediately...
18:43:25.389 📘 INFO DuckHuntBot 🔄 Cancelled 5 running tasks
18:43:25.411 📘 INFO DuckHuntBot.Game Duck spawning loop cancelled
18:43:25.411 📘 INFO DuckHuntBot.Game Duck timeout loop cancelled
18:43:25.412 📘 INFO DuckHuntBot 🛑 Main loop cancelled
18:43:25.416 📘 INFO DuckHuntBot Message loop cancelled
18:43:25.416 📘 INFO DuckHuntBot Message loop ended
18:43:25.416 📘 INFO DuckHuntBot.Game Game loops cancelled
18:43:25.426 🔍 DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:142]
18:43:25.427 📘 INFO DuckHuntBot 💾 Database saved
18:43:25.639 📘 INFO DuckHuntBot 🔌 IRC connection closed
18:43:25.639 📘 INFO DuckHuntBot ✅ Bot shutdown complete
18:43:26.132 📘 INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log
18:43:26.132 📘 INFO DuckHuntBot Debug mode: ON
18:43:26.133 📘 INFO DuckHuntBot Log everything: YES
18:43:26.133 📘 INFO DuckHuntBot Unified format: YES
18:43:26.133 📘 INFO DuckHuntBot Console level: INFO
18:43:26.133 📘 INFO DuckHuntBot File level: DEBUG
18:43:26.133 📘 INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log
18:43:26.133 📘 INFO DuckHuntBot 🤖 Initializing DuckHunt Bot components...
18:43:26.134 📘 INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json
18:43:26.135 📘 INFO SASL Unified logging system initialized: all logs in duckhunt.log
18:43:26.135 📘 INFO SASL Debug mode: ON
18:43:26.136 📘 INFO SASL Log everything: YES
18:43:26.136 📘 INFO SASL Unified format: YES
18:43:26.136 📘 INFO SASL Console level: INFO
18:43:26.136 📘 INFO SASL File level: DEBUG
18:43:26.136 📘 INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log
18:43:26.137 📘 INFO DuckHuntBot 👑 Configured 3 admin(s): peorth, computertech, colby
18:43:26.137 📘 INFO DuckHuntBot.Shop Loaded 9 shop items from /home/colby/duckhunt/shop.json
18:43:26.138 📘 INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json
18:43:26.138 📘 INFO DuckHuntBot 🦆 Starting DuckHunt Bot...
18:43:26.215 📘 INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3)
18:43:26.392 📘 INFO DuckHuntBot ✅ Successfully connected to irc.rizon.net:6697
18:43:26.393 📘 INFO DuckHuntBot 🔐 Sending server password
18:43:26.393 📘 INFO DuckHuntBot 🦆 Bot is now running! Press Ctrl+C to stop.
18:43:30.050 📘 INFO DuckHuntBot Successfully registered with IRC server
18:43:53.434 📘 INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct
18:44:23.468 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
18:45:10.514 📘 INFO DuckHuntBot 🛑 Received SIGINT (Ctrl+C), shutting down immediately...
18:45:10.515 📘 INFO DuckHuntBot 🔄 Cancelled 5 running tasks
18:45:10.519 📘 INFO DuckHuntBot Message loop cancelled
18:45:10.519 📘 INFO DuckHuntBot Message loop ended
18:45:10.519 📘 INFO DuckHuntBot 🛑 Main loop cancelled
18:45:10.520 📘 INFO DuckHuntBot.Game Duck spawning loop cancelled
18:45:10.520 📘 INFO DuckHuntBot.Game Duck timeout loop cancelled
18:45:10.521 📘 INFO DuckHuntBot.Game Game loops cancelled
18:45:10.550 🔍 DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:142]
18:45:10.550 📘 INFO DuckHuntBot 💾 Database saved
18:45:10.755 📘 INFO DuckHuntBot 🔌 IRC connection closed
18:45:10.755 📘 INFO DuckHuntBot ✅ Bot shutdown complete
18:45:11.133 📘 INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log
18:45:11.133 📘 INFO DuckHuntBot Debug mode: ON
18:45:11.133 📘 INFO DuckHuntBot Log everything: YES
18:45:11.133 📘 INFO DuckHuntBot Unified format: YES
18:45:11.133 📘 INFO DuckHuntBot Console level: INFO
18:45:11.133 📘 INFO DuckHuntBot File level: DEBUG
18:45:11.134 📘 INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log
18:45:11.134 📘 INFO DuckHuntBot 🤖 Initializing DuckHunt Bot components...
18:45:11.134 📘 INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json
18:45:11.135 📘 INFO SASL Unified logging system initialized: all logs in duckhunt.log
18:45:11.136 📘 INFO SASL Debug mode: ON
18:45:11.136 📘 INFO SASL Log everything: YES
18:45:11.136 📘 INFO SASL Unified format: YES
18:45:11.136 📘 INFO SASL Console level: INFO
18:45:11.136 📘 INFO SASL File level: DEBUG
18:45:11.137 📘 INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log
18:45:11.137 📘 INFO DuckHuntBot 👑 Configured 3 admin(s): peorth, computertech, colby
18:45:11.137 📘 INFO DuckHuntBot.Shop Loaded 9 shop items from /home/colby/duckhunt/shop.json
18:45:11.138 📘 INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json
18:45:11.138 📘 INFO DuckHuntBot 🦆 Starting DuckHunt Bot...
18:45:11.187 📘 INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3)
18:45:11.355 📘 INFO DuckHuntBot ✅ Successfully connected to irc.rizon.net:6697
18:45:11.355 📘 INFO DuckHuntBot 🔐 Sending server password
18:45:11.356 📘 INFO DuckHuntBot 🦆 Bot is now running! Press Ctrl+C to stop.
18:45:13.291 📘 INFO DuckHuntBot Successfully registered with IRC server
18:45:34.379 📘 INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct
18:46:10.413 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
18:47:06.032 📘 INFO DuckHuntBot 🛑 Received SIGINT (Ctrl+C), shutting down immediately...
18:47:06.041 📘 INFO DuckHuntBot 🔄 Cancelled 5 running tasks
18:47:06.082 📘 INFO DuckHuntBot Message loop cancelled
18:47:06.082 📘 INFO DuckHuntBot Message loop ended
18:47:06.082 📘 INFO DuckHuntBot 🛑 Main loop cancelled
18:47:06.083 📘 INFO DuckHuntBot.Game Duck spawning loop cancelled
18:47:06.084 📘 INFO DuckHuntBot.Game Duck timeout loop cancelled
18:47:06.084 📘 INFO DuckHuntBot.Game Game loops cancelled
18:47:06.108 🔍 DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:142]
18:47:06.109 📘 INFO DuckHuntBot 💾 Database saved
18:47:06.324 📘 INFO DuckHuntBot 🔌 IRC connection closed
18:47:06.325 📘 INFO DuckHuntBot ✅ Bot shutdown complete
18:47:06.755 📘 INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log
18:47:06.755 📘 INFO DuckHuntBot Debug mode: ON
18:47:06.756 📘 INFO DuckHuntBot Log everything: YES
18:47:06.756 📘 INFO DuckHuntBot Unified format: YES
18:47:06.756 📘 INFO DuckHuntBot Console level: INFO
18:47:06.756 📘 INFO DuckHuntBot File level: DEBUG
18:47:06.756 📘 INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log
18:47:06.757 📘 INFO DuckHuntBot 🤖 Initializing DuckHunt Bot components...
18:47:06.757 📘 INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json
18:47:06.764 📘 INFO SASL Unified logging system initialized: all logs in duckhunt.log
18:47:06.765 📘 INFO SASL Debug mode: ON
18:47:06.765 📘 INFO SASL Log everything: YES
18:47:06.765 📘 INFO SASL Unified format: YES
18:47:06.765 📘 INFO SASL Console level: INFO
18:47:06.765 📘 INFO SASL File level: DEBUG
18:47:06.765 📘 INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log
18:47:06.766 📘 INFO DuckHuntBot 👑 Configured 3 admin(s): peorth, computertech, colby
18:47:06.766 📘 INFO DuckHuntBot.Shop Loaded 9 shop items from /home/colby/duckhunt/shop.json
18:47:06.766 📘 INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json
18:47:06.766 📘 INFO DuckHuntBot 🦆 Starting DuckHunt Bot...
18:47:06.813 📘 INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3)
18:47:06.991 📘 INFO DuckHuntBot ✅ Successfully connected to irc.rizon.net:6697
18:47:06.991 📘 INFO DuckHuntBot 🔐 Sending server password
18:47:06.992 📘 INFO DuckHuntBot 🦆 Bot is now running! Press Ctrl+C to stop.
18:47:09.170 📘 INFO DuckHuntBot Successfully registered with IRC server
18:47:23.007 📘 INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct
18:48:02.049 📘 INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct
18:48:31.072 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
18:49:49.143 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
18:50:38.197 ⚠️ WARNING DuckHuntBot Admin access granted via nick-only authentication: ComputerTech!ComputerTe@ComputerTech.Network
18:51:05.220 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
18:52:09.272 📘 INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 3 HP
18:53:29.365 📘 INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 3 HP
18:54:43.468 📘 INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct
18:55:15.496 📘 INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 4 HP
18:56:27.559 📘 INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 4 HP
18:56:48.773 📘 INFO DuckHuntBot 🛑 Received SIGINT (Ctrl+C), shutting down immediately...
18:56:48.777 📘 INFO DuckHuntBot 🔄 Cancelled 5 running tasks
18:56:48.816 📘 INFO DuckHuntBot Message loop cancelled
18:56:48.816 📘 INFO DuckHuntBot Message loop ended
18:56:48.817 📘 INFO DuckHuntBot 🛑 Main loop cancelled
18:56:48.817 📘 INFO DuckHuntBot.Game Duck spawning loop cancelled
18:56:48.817 📘 INFO DuckHuntBot.Game Duck timeout loop cancelled
18:56:48.818 📘 INFO DuckHuntBot.Game Game loops cancelled
18:56:48.857 🔍 DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:142]
18:56:48.857 📘 INFO DuckHuntBot 💾 Database saved
18:56:49.325 📘 INFO DuckHuntBot 🔌 IRC connection closed
18:56:49.326 📘 INFO DuckHuntBot ✅ Bot shutdown complete
18:56:49.936 📘 INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log
18:56:49.937 📘 INFO DuckHuntBot Debug mode: ON
18:56:49.938 📘 INFO DuckHuntBot Log everything: YES
18:56:49.938 📘 INFO DuckHuntBot Unified format: YES
18:56:49.939 📘 INFO DuckHuntBot Console level: INFO
18:56:49.939 📘 INFO DuckHuntBot File level: DEBUG
18:56:49.939 📘 INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log
18:56:49.940 📘 INFO DuckHuntBot 🤖 Initializing DuckHunt Bot components...
18:56:49.940 📘 INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json
18:56:49.941 📘 INFO SASL Unified logging system initialized: all logs in duckhunt.log
18:56:49.941 📘 INFO SASL Debug mode: ON
18:56:49.942 📘 INFO SASL Log everything: YES
18:56:49.942 📘 INFO SASL Unified format: YES
18:56:49.942 📘 INFO SASL Console level: INFO
18:56:49.943 📘 INFO SASL File level: DEBUG
18:56:49.943 📘 INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log
18:56:49.943 📘 INFO DuckHuntBot 👑 Configured 3 admin(s): peorth, computertech, colby
18:56:49.944 📘 INFO DuckHuntBot.Shop Loaded 9 shop items from /home/colby/duckhunt/shop.json
18:56:49.945 📘 INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json
18:56:49.946 📘 INFO DuckHuntBot 🦆 Starting DuckHunt Bot...
18:56:50.005 📘 INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3)
18:56:50.174 📘 INFO DuckHuntBot ✅ Successfully connected to irc.rizon.net:6697
18:56:50.174 📘 INFO DuckHuntBot 🔐 Sending server password
18:56:50.175 📘 INFO DuckHuntBot 🦆 Bot is now running! Press Ctrl+C to stop.
18:56:52.108 📘 INFO DuckHuntBot Successfully registered with IRC server
18:57:01.185 📘 INFO DuckHuntBot.Game Golden duck (hidden) spawned in #ct with 4 HP
18:57:11.327 ⚠️ WARNING DuckHuntBot Admin access granted via nick-only authentication: ComputerTech!ComputerTe@ComputerTech.Network
18:58:06.241 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
18:58:38.133 ⚠️ WARNING DuckHuntBot Admin access granted via nick-only authentication: ComputerTech!ComputerTe@ComputerTech.Network
18:59:10.630 ⚠️ WARNING DuckHuntBot Admin access granted via nick-only authentication: ComputerTech!ComputerTe@ComputerTech.Network
18:59:21.310 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
19:00:26.365 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
19:01:20.851 📘 INFO DuckHuntBot 🛑 Received SIGINT (Ctrl+C), shutting down immediately...
19:01:20.860 📘 INFO DuckHuntBot 🔄 Cancelled 5 running tasks
19:01:20.861 📘 INFO DuckHuntBot Message loop cancelled
19:01:20.861 📘 INFO DuckHuntBot Message loop ended
19:01:20.862 📘 INFO DuckHuntBot 🛑 Main loop cancelled
19:01:20.862 📘 INFO DuckHuntBot.Game Duck spawning loop cancelled
19:01:20.862 📘 INFO DuckHuntBot.Game Duck timeout loop cancelled
19:01:20.863 📘 INFO DuckHuntBot.Game Game loops cancelled
19:01:20.888 🔍 DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:142]
19:01:20.888 📘 INFO DuckHuntBot 💾 Database saved
19:01:21.097 📘 INFO DuckHuntBot 🔌 IRC connection closed
19:01:21.097 📘 INFO DuckHuntBot ✅ Bot shutdown complete
19:01:22.172 📘 INFO DuckHuntBot Unified logging system initialized: all logs in duckhunt.log
19:01:22.172 📘 INFO DuckHuntBot Debug mode: ON
19:01:22.173 📘 INFO DuckHuntBot Log everything: YES
19:01:22.173 📘 INFO DuckHuntBot Unified format: YES
19:01:22.173 📘 INFO DuckHuntBot Console level: INFO
19:01:22.173 📘 INFO DuckHuntBot File level: DEBUG
19:01:22.173 📘 INFO DuckHuntBot Main log: /home/colby/duckhunt/logs/duckhunt.log
19:01:22.174 📘 INFO DuckHuntBot 🤖 Initializing DuckHunt Bot components...
19:01:22.174 📘 INFO DuckHuntBot.DB Loaded 1 players from duckhunt.json
19:01:22.181 📘 INFO SASL Unified logging system initialized: all logs in duckhunt.log
19:01:22.181 📘 INFO SASL Debug mode: ON
19:01:22.181 📘 INFO SASL Log everything: YES
19:01:22.182 📘 INFO SASL Unified format: YES
19:01:22.182 📘 INFO SASL Console level: INFO
19:01:22.182 📘 INFO SASL File level: DEBUG
19:01:22.182 📘 INFO SASL Main log: /home/colby/duckhunt/logs/duckhunt.log
19:01:22.183 📘 INFO DuckHuntBot 👑 Configured 3 admin(s): peorth, computertech, colby
19:01:22.184 📘 INFO DuckHuntBot.Shop Loaded 9 shop items from /home/colby/duckhunt/shop.json
19:01:22.185 📘 INFO DuckHuntBot.Levels Loaded 8 levels from /home/colby/duckhunt/levels.json
19:01:22.185 📘 INFO DuckHuntBot 🦆 Starting DuckHunt Bot...
19:01:22.231 📘 INFO DuckHuntBot Attempting to connect to irc.rizon.net:6697 (attempt 1/3)
19:01:22.424 📘 INFO DuckHuntBot ✅ Successfully connected to irc.rizon.net:6697
19:01:22.424 📘 INFO DuckHuntBot 🔐 Sending server password
19:01:22.425 📘 INFO DuckHuntBot 🦆 Bot is now running! Press Ctrl+C to stop.
19:01:24.093 📘 INFO DuckHuntBot Successfully registered with IRC server
19:01:28.998 ⚠️ WARNING DuckHuntBot Admin access granted via nick-only authentication: ComputerTech!ComputerTe@ComputerTech.Network
19:01:29.016 🔍 DEBUG DuckHuntBot.DB Database saved successfully with 1 players [save_database:142]
19:01:48.445 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
19:02:49.507 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
19:04:05.573 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct
19:05:15.648 📘 INFO DuckHuntBot.Game Fast duck (hidden) spawned in #ct
19:05:39.670 📘 INFO DuckHuntBot.Game Normal duck spawned in #ct

View File

@@ -12,10 +12,12 @@
"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_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} > You are not armed.",
"bang_not_armed": "{nick} > Your gun has been confiscated. Buy it back from the shop.",
"bef_success": "{nick} > *befriend* You befriended the duck! [+{xp_gained} xp] [Ducks befriended: {ducks_befriended}]",
"bef_failed": "{nick} > *gentle approach* The duck doesn't trust you and flies away...",
"bef_no_duck": "{nick} > *gentle approach* There is no duck to befriend in the area...",
@@ -47,6 +49,19 @@
"shop_buy_invalid_id": "{nick} > Invalid item ID. Use !shop to see available items.",
"shop_buy_usage": "Usage: !shop buy <item_id>",
"use_attract_ducks": "{nick} > You scattered bread around the pond! Ducks will spawn {spawn_multiplier}x faster for {duration} minutes.",
"use_insurance": "{nick} > You activated Hunter's Insurance! Protected from friendly fire penalties for {duration} hours.",
"use_buy_gun_back": "{nick} > Your gun has been returned with {ammo_restored} bullets and {magazines_restored} magazines.",
"use_buy_gun_back_not_needed": "{nick} > Your gun is not confiscated.",
"bang_wet_clothes": "{nick} > *SPLASH* Your clothes are soaked! You can't shoot until you dry off or buy new clothes.",
"use_splash_water": "{nick} > *SPLASH* You soaked {target_nick} with water! They can't shoot for {duration} minutes.",
"use_dry_clothes": "{nick} > You changed into dry clothes! Ready to hunt again.",
"use_dry_clothes_not_needed": "{nick} > You weren't wet - no need for new clothes.",
"gift_ammo": "{nick} > Gave {amount} bullet(s) to {target_nick}! What a generous hunter.",
"gift_magazine": "{nick} > Gave 1 magazine to {target_nick}! Sharing the ammo love.",
"gift_gun_brush": "{nick} > Gave a gun brush to {target_nick} - keeping their weapon clean!",
"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!",
"colours": {
"white": "\u00030",

View File

@@ -19,8 +19,7 @@
"price": 10,
"description": "Throw sand in target's gun - increases jam chance by 15%",
"type": "sabotage_jam",
"amount": 15,
"target_required": true
"amount": 15
},
"4": {
"name": "Gun Brush",
@@ -36,6 +35,32 @@
"type": "attract_ducks",
"duration": 1200,
"spawn_multiplier": 2.0
},
"6": {
"name": "Hunter's Insurance",
"price": 75,
"description": "Protects against friendly fire penalties for 24 hours - no XP loss or gun confiscation",
"type": "insurance",
"duration": 86400,
"protection": "friendly_fire"
},
"7": {
"name": "Buy Gun Back",
"price": 40,
"description": "Get your confiscated gun back with the same ammo it had when taken",
"type": "buy_gun_back"
},
"8": {
"name": "Bucket of Water",
"price": 25,
"description": "Splash another hunter with water - soaks their clothes and prevents shooting until they dry or buy new clothes",
"type": "splash_water"
},
"9": {
"name": "Dry Clothes",
"price": 30,
"description": "Change into dry clothes - allows shooting again after being soaked",
"type": "dry_clothes"
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -68,6 +68,8 @@ class DuckDB:
sanitized['xp'] = max(0, int(player_data.get('xp', 0))) # Non-negative XP
sanitized['ducks_shot'] = max(0, int(player_data.get('ducks_shot', 0)))
sanitized['ducks_befriended'] = max(0, int(player_data.get('ducks_befriended', 0)))
sanitized['shots_fired'] = max(0, int(player_data.get('shots_fired', 0)))
sanitized['shots_missed'] = max(0, int(player_data.get('shots_missed', 0)))
default_accuracy = self.bot.get_config('default_accuracy', 75) if self.bot else 75
max_accuracy = self.bot.get_config('max_accuracy', 100) if self.bot else 100
sanitized['accuracy'] = max(0, min(max_accuracy, int(player_data.get('accuracy', default_accuracy)))) # 0-max_accuracy range
@@ -76,6 +78,12 @@ class DuckDB:
# Ammo system with validation
sanitized['current_ammo'] = max(0, min(50, int(player_data.get('current_ammo', 6))))
sanitized['magazines'] = max(0, min(20, int(player_data.get('magazines', 3))))
# Confiscated ammo (optional fields)
if 'confiscated_ammo' in player_data:
sanitized['confiscated_ammo'] = max(0, min(50, int(player_data.get('confiscated_ammo', 0))))
if 'confiscated_magazines' in player_data:
sanitized['confiscated_magazines'] = max(0, min(20, int(player_data.get('confiscated_magazines', 0))))
sanitized['bullets_per_magazine'] = max(1, min(50, int(player_data.get('bullets_per_magazine', 6))))
sanitized['jam_chance'] = max(0, min(100, int(player_data.get('jam_chance', 5))))
@@ -237,6 +245,8 @@ class DuckDB:
'xp': xp,
'ducks_shot': 0,
'ducks_befriended': 0,
'shots_fired': 0, # Total shots fired
'shots_missed': 0, # Total shots that missed
'current_ammo': bullets_per_mag, # Bullets in current magazine
'magazines': magazines, # Total magazines (including current)
'bullets_per_magazine': bullets_per_mag, # Bullets per magazine

View File

@@ -58,11 +58,44 @@ class DuckHuntBot:
return value
def is_admin(self, user):
"""Check if user is admin by nick only"""
"""Check if user is admin with enhanced security checks"""
if '!' not in user:
return False
nick = user.split('!')[0].lower()
return nick in self.admins
# Check admin configuration - support both nick-only (legacy) and hostmask patterns
admin_config = self.get_config('admins', [])
# Ensure admin_config is a list
if not isinstance(admin_config, list):
admin_config = []
for admin_entry in admin_config:
if isinstance(admin_entry, str):
# Simple nick-based check (less secure but compatible)
if admin_entry.lower() == nick:
self.logger.warning(f"Admin access granted via nick-only authentication: {user}")
return True
elif isinstance(admin_entry, dict):
# Enhanced hostmask-based authentication
if admin_entry.get('nick', '').lower() == nick:
# Check hostmask pattern if provided
required_pattern = admin_entry.get('hostmask')
if required_pattern:
import fnmatch
if fnmatch.fnmatch(user.lower(), required_pattern.lower()):
self.logger.info(f"Admin access granted via hostmask: {user}")
return True
else:
self.logger.warning(f"Admin nick match but hostmask mismatch: {user} vs {required_pattern}")
return False
else:
# Nick-only fallback
self.logger.warning(f"Admin access granted via nick-only (no hostmask configured): {user}")
return True
return False
def _handle_single_target_admin_command(self, args, usage_message_key, action_func, success_message_key, nick, channel):
"""Helper for admin commands that target a single player"""
@@ -307,6 +340,11 @@ class DuckHuntBot:
self.logger.error(f"Error getting player data for {nick}: {e}")
player = {}
# Track activity for channel membership validation
if channel.startswith('#'): # Only track for channel messages
player['last_activity_channel'] = channel
player['last_activity_time'] = time.time()
# Check if player is ignored (unless it's an admin)
try:
if player.get('ignored', False) and not self.is_admin(user):
@@ -360,6 +398,77 @@ class DuckHuntBot:
except Exception as send_error:
self.logger.error(f"Error sending error message: {send_error}")
def validate_target_player(self, target_nick, channel):
"""
Validate that a target player is a valid hunter
Returns (is_valid, player_data, error_message)
TODO: Implement proper channel membership tracking to ensure
the target is actually present in the channel
"""
if not target_nick:
return False, None, "No target specified"
# Normalize the nickname
target_nick = target_nick.lower().strip()
# Check if target_nick is empty after normalization
if not target_nick:
return False, None, "Invalid target nickname"
# Check if player exists in database
player = self.db.get_player(target_nick)
if not player:
return False, None, f"Player '{target_nick}' not found. They need to participate in the game first."
# Check if player has any game activity (basic validation they're a hunter)
has_activity = (
player.get('xp', 0) > 0 or
player.get('shots_fired', 0) > 0 or
'current_ammo' in player or
'magazines' in player
)
if not has_activity:
return False, None, f"Player '{target_nick}' has no hunting activity. They may not be an active hunter."
# Check if player is currently in the channel (for channel messages only)
if channel.startswith('#'):
is_in_channel = self.is_user_in_channel_sync(target_nick, channel)
if not is_in_channel:
return False, None, f"Player '{target_nick}' is not currently in {channel}."
return True, player, None
def is_user_in_channel_sync(self, nick, channel):
"""
Check if a user is likely in the channel based on recent activity (synchronous version)
This is a practical approach that doesn't require complex IRC response parsing.
We assume if someone has been active recently, they're still in the channel.
"""
try:
player = self.db.get_player(nick)
if not player:
return False
# Check if they've been active in this channel recently
last_activity_channel = player.get('last_activity_channel')
last_activity_time = player.get('last_activity_time', 0)
current_time = time.time()
# If they were active in this channel within the last 30 minutes, assume they're still here
if (last_activity_channel == channel and
current_time - last_activity_time < 1800): # 30 minutes
return True
# If no recent activity in this channel, they might not be here
return False
except Exception as e:
self.logger.error(f"Error checking channel membership for {nick} in {channel}: {e}")
return True # Default to allowing the command if we can't check
async def handle_bang(self, nick, channel, player):
"""Handle !bang command"""
result = self.game.shoot_duck(nick, channel, player)
@@ -409,11 +518,12 @@ class DuckHuntBot:
"""Handle buying an item from the shop"""
target_player = None
# Get target player if specified
# Get target player if specified and validate they're in channel
if target_nick:
target_player = self.db.get_player(target_nick)
if not target_player:
message = f"{nick} > Target player '{target_nick}' not found"
# Use the same validation as other commands
is_valid, target_player, error_msg = self.validate_target_player(target_nick, channel)
if not is_valid:
message = f"{nick} > {error_msg}"
self.send_message(channel, message)
return
@@ -460,11 +570,15 @@ class DuckHuntBot:
# Apply color formatting
bold = self.messages.messages.get('colours', {}).get('bold', '')
reset = self.messages.messages.get('colours', {}).get('reset', '')
green = self.messages.messages.get('colours', {}).get('green', '')
blue = self.messages.messages.get('colours', {}).get('blue', '')
yellow = self.messages.messages.get('colours', {}).get('yellow', '')
red = self.messages.messages.get('colours', {}).get('red', '')
# Get player level info
level_info = self.levels.get_player_level_info(player)
level = level_info['level']
level_name = level_info['level_data']['name']
level_name = level_info['name']
# Build stats message
xp = player.get('xp', 0)
@@ -472,23 +586,40 @@ class DuckHuntBot:
ducks_befriended = player.get('ducks_befriended', 0)
accuracy = player.get('accuracy', self.get_config('player_defaults.accuracy', 75))
# Calculate additional stats
total_ducks_encountered = ducks_shot + ducks_befriended
shots_missed = player.get('shots_missed', 0)
total_shots = ducks_shot + shots_missed
hit_rate = round((ducks_shot / total_shots * 100) if total_shots > 0 else 0, 1)
# Get level progression info
xp_needed = level_info.get('needed_for_next', 0)
next_level_name = level_info.get('next_level_name', 'Max Level')
if xp_needed > 0:
xp_progress = f" (Need {xp_needed} XP for {next_level_name})"
else:
xp_progress = " (Max level reached!)"
# Ammo info
current_ammo = player.get('current_ammo', 0)
magazines = player.get('magazines', 0)
bullets_per_mag = player.get('bullets_per_magazine', 6)
jam_chance = player.get('jam_chance', 0)
# Gun status
gun_status = "🔫 Armed" if not player.get('gun_confiscated', False) else "Confiscated"
gun_status = "Armed" if not player.get('gun_confiscated', False) else "Confiscated"
stats_lines = [
f"📊 {bold}Duck Hunt Stats for {nick}{reset}",
f"🏆 Level {level}: {level_name}",
f"⭐ XP: {xp}",
f"🦆 Ducks Shot: {ducks_shot}",
f"💚 Ducks Befriended: {ducks_befriended}",
f"🎯 Accuracy: {accuracy}%",
f"🔫 Status: {gun_status}",
f"💀 Ammo: {current_ammo}/{bullets_per_mag} | Magazines: {magazines}"
# Build compact stats message with subtle colors
stats_parts = [
f"Lv{level} {level_name}",
f"{green}{xp}XP{reset}{xp_progress}",
f"{ducks_shot} shot",
f"{ducks_befriended} befriended",
f"{accuracy}% accuracy",
f"{hit_rate}% hit rate",
f"{green if gun_status == 'Armed' else red}{gun_status}{reset}",
f"{current_ammo}/{bullets_per_mag}|{magazines} mags",
f"{jam_chance}% jam chance"
]
# Add inventory if player has items
@@ -500,11 +631,18 @@ class DuckHuntBot:
if item:
items.append(f"{item['name']} x{quantity}")
if items:
stats_lines.append(f"🎒 Inventory: {', '.join(items)}")
stats_parts.append(f"Items: {', '.join(items)}")
# Send each line
for line in stats_lines:
self.send_message(channel, line)
# Add temporary effects if any
temp_effects = player.get('temporary_effects', [])
if temp_effects:
active_effects = [effect.get('name', 'Unknown Effect') for effect in temp_effects if isinstance(effect, dict)]
if active_effects:
stats_parts.append(f"Effects:{','.join(active_effects)}")
# Send as one compact message
stats_message = f"{bold}{nick}{reset}: {' | '.join(stats_parts)}"
self.send_message(channel, stats_message)
async def handle_topduck(self, nick, channel):
"""Handle !topduck command - show leaderboards"""
@@ -574,9 +712,9 @@ class DuckHuntBot:
# Get target player if specified
if target_nick:
target_player = self.db.get_player(target_nick)
if not target_player:
message = f"{nick} > Target player '{target_nick}' not found"
is_valid, target_player, error_msg = self.validate_target_player(target_nick, channel)
if not is_valid:
message = f"{nick} > {error_msg}"
self.send_message(channel, message)
return
@@ -597,8 +735,58 @@ class DuckHuntBot:
spawn_multiplier=effect.get('spawn_multiplier', 2.0),
duration=effect.get('duration', 10)
)
elif effect_type == 'insurance':
# Use specific message for insurance
message = self.messages.get('use_insurance',
nick=nick,
duration=effect.get('duration', 24)
)
elif effect_type == 'buy_gun_back':
# Use specific message for buying gun back
if effect.get('restored', False):
message = self.messages.get('use_buy_gun_back', nick=nick,
ammo_restored=effect.get('ammo_restored', 0),
magazines_restored=effect.get('magazines_restored', 0))
else:
message = self.messages.get('use_buy_gun_back_not_needed', nick=nick)
elif effect_type == 'splash_water':
# Use specific message for water splash
message = self.messages.get('use_splash_water',
nick=nick,
target_nick=target_nick,
duration=effect.get('duration', 5))
elif effect_type == 'dry_clothes':
# Use specific message for dry clothes
if effect.get('was_wet', False):
message = self.messages.get('use_dry_clothes', nick=nick)
else:
message = self.messages.get('use_dry_clothes_not_needed', nick=nick)
elif result.get("target_affected"):
message = f"{nick} > Used {result['item_name']} on {target_nick}!"
# Check if it's a gift (beneficial effect to target)
if effect.get('is_gift', False):
# Use specific gift messages based on item type
if effect_type == 'ammo':
message = self.messages.get('gift_ammo',
nick=nick, target_nick=target_nick, amount=effect.get('amount', 1))
elif effect_type == 'magazine':
message = self.messages.get('gift_magazine',
nick=nick, target_nick=target_nick)
elif effect_type == 'clean_gun':
message = self.messages.get('gift_gun_brush',
nick=nick, target_nick=target_nick)
elif effect_type == 'insurance':
message = self.messages.get('gift_insurance',
nick=nick, target_nick=target_nick)
elif effect_type == 'dry_clothes':
message = self.messages.get('gift_dry_clothes',
nick=nick, target_nick=target_nick)
elif effect_type == 'buy_gun_back':
message = self.messages.get('gift_buy_gun_back',
nick=nick, target_nick=target_nick)
else:
message = f"{nick} > Gave {result['item_name']} to {target_nick}!"
else:
message = f"{nick} > Used {result['item_name']} on {target_nick}!"
else:
message = f"{nick} > Used {result['item_name']}!"
@@ -618,20 +806,48 @@ class DuckHuntBot:
is_private_msg = not channel.startswith('#')
if args:
target = args[0].lower()
player = self.db.get_player(target)
if player is None:
player = {}
player['gun_confiscated'] = False
target_nick = args[0]
# Update magazines based on player level
# Validate target player (only for channel messages, skip validation if targeting self)
player = None
if not is_private_msg:
# If targeting self, skip validation since the user is obviously in the channel
if target_nick.lower() == nick.lower():
target_nick = target_nick.lower()
player = self.db.get_player(target_nick)
if player is None:
player = self.db.create_player(target_nick)
self.db.players[target_nick] = player
else:
is_valid, player, error_msg = self.validate_target_player(target_nick, channel)
if not is_valid:
message = f"{nick} > {error_msg}"
self.send_message(channel, message)
return
# Ensure player is properly stored in database
target_nick = target_nick.lower()
if target_nick not in self.db.players:
self.db.players[target_nick] = player
else:
# For private messages, allow targeting any nick (admin override)
target_nick = target_nick.lower()
player = self.db.get_player(target_nick)
if player is None:
# Create new player data for the target
player = self.db.create_player(target_nick)
self.db.players[target_nick] = player
# At this point player is guaranteed to be not None
if player is not None:
player['gun_confiscated'] = False # Update magazines based on player level
self.levels.update_player_magazines(player)
player['current_ammo'] = player.get('bullets_per_magazine', 6)
# Player data is already modified in place and will be saved by save_database()
if is_private_msg:
message = f"{nick} > Rearmed {target}"
message = f"{nick} > Rearmed {target_nick}"
else:
message = self.messages.get('admin_rearm_player', target=target, admin=nick)
message = self.messages.get('admin_rearm_player', target=target_nick, admin=nick)
self.send_message(channel, message)
else:
if is_private_msg:
@@ -665,16 +881,46 @@ class DuckHuntBot:
self.send_message(channel, message)
return
target = args[0].lower()
player = self.db.get_player(target)
if player is None:
player = {}
player['gun_confiscated'] = True
target_nick = args[0]
# Validate target player (only for channel messages, skip validation if targeting self)
player = None
if not is_private_msg:
# If targeting self, skip validation since the user is obviously in the channel
if target_nick.lower() == nick.lower():
target_nick = target_nick.lower()
player = self.db.get_player(target_nick)
if player is None:
player = self.db.create_player(target_nick)
self.db.players[target_nick] = player
else:
is_valid, player, error_msg = self.validate_target_player(target_nick, channel)
if not is_valid:
message = f"{nick} > {error_msg}"
self.send_message(channel, message)
return
# Ensure player is properly stored in database
target_nick = target_nick.lower()
if target_nick not in self.db.players:
self.db.players[target_nick] = player
else:
# For private messages, allow targeting any nick (admin override)
target_nick = target_nick.lower()
player = self.db.get_player(target_nick)
if player is None:
# Create new player data for the target
player = self.db.create_player(target_nick)
self.db.players[target_nick] = player
# At this point player is guaranteed to be not None
if player is not None:
player['gun_confiscated'] = True
# Player data is already modified in place and will be saved by save_database()
if is_private_msg:
message = f"{nick} > Disarmed {target}"
message = f"{nick} > Disarmed {target_nick}"
else:
message = self.messages.get('admin_disarm', target=target, admin=nick)
message = self.messages.get('admin_disarm', target=target_nick, admin=nick)
self.send_message(channel, message)
self.db.save_database()

View File

@@ -175,6 +175,14 @@ class DuckGame:
'message_args': {'nick': nick}
}
# Check if clothes are wet
if self._is_player_wet(player):
return {
'success': False,
'message_key': 'bang_wet_clothes',
'message_args': {'nick': nick}
}
# Check ammo
if player.get('current_ammo', 0) <= 0:
return {
@@ -197,8 +205,14 @@ class DuckGame:
# Check for duck
if channel not in self.ducks or not self.ducks[channel]:
# Wild shot - gun confiscated
player['current_ammo'] = player.get('current_ammo', 1) - 1
# Wild shot - gun confiscated for unsafe shooting
player['shots_fired'] = player.get('shots_fired', 0) + 1 # Track wild shots too
player['shots_missed'] = player.get('shots_missed', 0) + 1 # Wild shots count as misses
# Use ammo for the shot, then store remaining ammo before confiscation
remaining_ammo = player.get('current_ammo', 1) - 1
player['confiscated_ammo'] = remaining_ammo
player['confiscated_magazines'] = player.get('magazines', 0)
player['current_ammo'] = 0 # No ammo while confiscated
player['gun_confiscated'] = True
self.db.save_database()
return {
@@ -209,6 +223,7 @@ class DuckGame:
# Shoot at duck
player['current_ammo'] = player.get('current_ammo', 1) - 1
player['shots_fired'] = player.get('shots_fired', 0) + 1 # Track total shots fired
# Calculate hit chance using level-modified accuracy
modified_accuracy = self.bot.levels.get_modified_accuracy(player)
hit_chance = modified_accuracy / 100.0
@@ -268,7 +283,7 @@ class DuckGame:
self.bot.levels.update_player_magazines(player)
# If config option enabled, rearm all disarmed players when duck is shot
if self.bot.get_config('rearm_on_duck_shot', False):
if self.bot.get_config('duck_spawning.rearm_on_duck_shot', False):
self._rearm_all_disarmed_players()
self.db.save_database()
@@ -284,9 +299,67 @@ class DuckGame:
}
else:
# Miss! Duck stays in the channel
accuracy_loss = self.bot.get_config('accuracy_loss_on_miss', 2)
min_accuracy = self.bot.get_config('min_accuracy', 10)
player['accuracy'] = max(player.get('accuracy', self.bot.get_config('default_accuracy', 75)) - accuracy_loss, min_accuracy)
player['shots_missed'] = player.get('shots_missed', 0) + 1 # Track missed shots
accuracy_loss = self.bot.get_config('gameplay.accuracy_loss_on_miss', 2)
min_accuracy = self.bot.get_config('gameplay.min_accuracy', 10)
player['accuracy'] = max(player.get('accuracy', self.bot.get_config('player_defaults.accuracy', 75)) - accuracy_loss, min_accuracy)
# Check for friendly fire (chance to hit another hunter)
friendly_fire_chance = 0.15 # 15% chance of hitting another hunter on miss
if random.random() < friendly_fire_chance:
# Get other armed players in the same channel
armed_players = []
for other_nick, other_player in self.db.players.items():
if (other_nick.lower() != nick.lower() and
not other_player.get('gun_confiscated', False) and
other_player.get('current_ammo', 0) > 0):
armed_players.append((other_nick, other_player))
if armed_players:
# Hit a random armed hunter
victim_nick, victim_player = random.choice(armed_players)
# Check if shooter has insurance protection
has_insurance = self._check_insurance_protection(player, 'friendly_fire')
if has_insurance:
# Protected by insurance - no penalties
self.db.save_database()
return {
'success': True,
'hit': False,
'friendly_fire': True,
'victim': victim_nick,
'message_key': 'bang_friendly_fire_insured',
'message_args': {
'nick': nick,
'victim': victim_nick
}
}
else:
# Apply friendly fire penalties - gun confiscated for unsafe shooting
xp_loss = min(player.get('xp', 0) // 4, 25) # Lose 25% XP or max 25 XP
player['xp'] = max(0, player.get('xp', 0) - xp_loss)
# Store current ammo state before confiscation (no shot fired yet in friendly fire)
player['confiscated_ammo'] = player.get('current_ammo', 0)
player['confiscated_magazines'] = player.get('magazines', 0)
player['current_ammo'] = 0 # No ammo while confiscated
player['gun_confiscated'] = True
self.db.save_database()
return {
'success': True,
'hit': False,
'friendly_fire': True,
'victim': victim_nick,
'message_key': 'bang_friendly_fire_penalty',
'message_args': {
'nick': nick,
'victim': victim_nick,
'xp_lost': xp_loss
}
}
self.db.save_database()
return {
'success': True,
@@ -443,6 +516,35 @@ class DuckGame:
self.logger.error(f"Error getting spawn multiplier: {e}")
return 1.0
def _is_player_wet(self, player):
"""Check if player has wet clothes that prevent shooting"""
import time
current_time = time.time()
effects = player.get('temporary_effects', [])
for effect in effects:
if (effect.get('type') == 'wet_clothes' and
effect.get('expires_at', 0) > current_time):
return True
return False
def _check_insurance_protection(self, player, protection_type):
"""Check if player has active insurance protection"""
import time
current_time = time.time()
try:
effects = player.get('temporary_effects', [])
for effect in effects:
if (effect.get('type') == 'insurance' and
effect.get('protection') == protection_type and
effect.get('expires_at', 0) > current_time):
return True
return False
except Exception as e:
self.logger.error(f"Error checking insurance protection: {e}")
return False
def _clean_expired_effects(self):
"""Remove expired temporary effects from all players"""
import time

View File

@@ -105,10 +105,42 @@ class ShopManager:
player['xp'] = player_xp - item['price']
if store_in_inventory:
# Add to inventory
# Add to inventory with bounds checking
inventory = player.get('inventory', {})
item_id_str = str(item_id)
current_count = inventory.get(item_id_str, 0)
# Load inventory limits from config
config_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'config.json')
max_per_item = 99 # Default limit per item type
max_total_items = 20 # Default total items limit
try:
with open(config_path, 'r') as f:
config = json.load(f)
max_total_items = config.get('gameplay', {}).get('max_inventory_items', 20)
max_per_item = config.get('gameplay', {}).get('max_per_item_type', 99)
except:
pass # Use defaults
# Check individual item limit
if current_count >= max_per_item:
return {
"success": False,
"error": "item_limit_reached",
"message": f"Cannot hold more than {max_per_item} {item['name']}s",
"item_name": item['name']
}
# Check total inventory size limit
total_items = sum(inventory.values())
if total_items >= max_total_items:
return {
"success": False,
"error": "inventory_full",
"message": f"Inventory full! (max {max_total_items} items)",
"item_name": item['name']
}
inventory[item_id_str] = current_count + 1
player['inventory'] = inventory
@@ -190,11 +222,11 @@ class ShopManager:
elif item_type == 'luck':
# Store luck bonus (would be used in duck spawning logic)
current_luck = player.get('luck_bonus', 0)
new_luck = current_luck + amount
new_luck = min(max(current_luck + amount, -50), 100) # Bounded between -50 and +100
player['luck_bonus'] = new_luck
return {
"type": "luck",
"added": amount,
"added": new_luck - current_luck,
"new_total": new_luck
}
@@ -316,7 +348,7 @@ class ShopManager:
elif item_type == 'clean_gun':
# Clean gun to reduce jamming chance (positive amount reduces jam chance)
current_jam = player.get('jam_chance', 5) # Default 5% jam chance
new_jam = max(current_jam + amount, 0) # amount is negative for cleaning
new_jam = min(max(current_jam + amount, 0), 100) # Bounded between 0% and 100%
player['jam_chance'] = new_jam
return {
@@ -346,10 +378,109 @@ class ShopManager:
"duration": duration // 60 # return duration in minutes
}
elif item_type == 'insurance':
# Add insurance protection against friendly fire
if 'temporary_effects' not in player:
player['temporary_effects'] = []
duration = item.get('duration', 86400) # 24 hours default
protection_type = item.get('protection', 'friendly_fire')
effect = {
'type': 'insurance',
'protection': protection_type,
'expires_at': time.time() + duration,
'name': 'Hunter\'s Insurance'
}
player['temporary_effects'].append(effect)
return {
"type": "insurance",
"protection": protection_type,
"duration": duration // 3600 # return duration in hours
}
elif item_type == 'buy_gun_back':
# Restore confiscated gun with original ammo
was_confiscated = player.get('gun_confiscated', False)
if was_confiscated:
player['gun_confiscated'] = False
# Restore original ammo and magazines from when gun was confiscated
restored_ammo = player.get('confiscated_ammo', 0)
restored_magazines = player.get('confiscated_magazines', 1)
player['current_ammo'] = restored_ammo
player['magazines'] = restored_magazines
# Clean up the stored values
player.pop('confiscated_ammo', None)
player.pop('confiscated_magazines', None)
return {
"type": "buy_gun_back",
"restored": True,
"ammo_restored": restored_ammo
}
else:
return {
"type": "buy_gun_back",
"restored": False,
"message": "Your gun is not confiscated"
}
elif item_type == 'dry_clothes':
# Remove wet clothes effect
# Remove any wet clothes effects
if 'temporary_effects' in player:
original_count = len(player['temporary_effects'])
player['temporary_effects'] = [
effect for effect in player['temporary_effects']
if effect.get('type') != 'wet_clothes'
]
new_count = len(player['temporary_effects'])
was_wet = original_count > new_count
else:
was_wet = False
return {
"type": "dry_clothes",
"was_wet": was_wet,
"message": "You changed into dry clothes!" if was_wet else "You weren't wet!"
}
else:
self.logger.warning(f"Unknown item type: {item_type}")
return {"type": "unknown", "message": f"Unknown effect type: {item_type}"}
def _apply_splash_water_effect(self, target_player: Dict[str, Any], item: Dict[str, Any]) -> Dict[str, Any]:
"""Apply splash water effect to target player"""
# Load config directly without import issues
config_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'config.json')
try:
with open(config_path, 'r') as f:
config = json.load(f)
wet_duration = config.get('gameplay', {}).get('wet_clothes_duration', 300) # 5 minutes default
except:
wet_duration = 300 # Default 5 minutes
if 'temporary_effects' not in target_player:
target_player['temporary_effects'] = []
# Add wet clothes effect
wet_effect = {
'type': 'wet_clothes',
'expires_at': time.time() + wet_duration
}
target_player['temporary_effects'].append(wet_effect)
return {
"type": "splash_water",
"target_soaked": True,
"duration": wet_duration // 60 # return duration in minutes
}
def use_inventory_item(self, player: Dict[str, Any], item_id: int, target_player: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
"""
Use an item from player's inventory
@@ -370,12 +501,22 @@ class ShopManager:
"item_name": item['name']
}
# Check if item requires a target
if item.get('target_required', False) and not target_player:
# Special restrictions: Some items require targets, bread cannot have targets
if item['type'] == 'attract_ducks' and target_player:
return {
"success": False,
"error": "bread_no_target",
"message": "Bread affects everyone in the channel - you cannot target a specific player",
"item_name": item['name']
}
# Items that must have targets when used (but can be stored in inventory)
target_required_items = ['sabotage_jam', 'splash_water']
if item['type'] in target_required_items and not target_player:
return {
"success": False,
"error": "target_required",
"message": f"{item['name']} requires a target player",
"message": f"{item['name']} requires a target player to use",
"item_name": item['name']
}
@@ -385,19 +526,31 @@ class ShopManager:
del inventory[item_id_str]
player['inventory'] = inventory
# Apply effect
if item.get('target_required', False) and target_player:
effect_result = self._apply_item_effect(target_player, item)
# Determine who gets the effect
if target_player:
# Special handling for harmful effects
if item['type'] == 'splash_water':
effect_result = self._apply_splash_water_effect(target_player, item)
target_affected = True
elif item['type'] == 'sabotage_jam':
effect_result = self._apply_item_effect(target_player, item)
target_affected = True
else:
# Beneficial items - give to target (gifting)
effect_result = self._apply_item_effect(target_player, item)
target_affected = True
# Mark as gift in the result
effect_result['is_gift'] = True
return {
"success": True,
"item_name": item['name'],
"effect": effect_result,
"target_affected": True,
"target_affected": target_affected,
"remaining_in_inventory": inventory.get(item_id_str, 0)
}
else:
# Apply effect to user
# Apply effect to user (no target specified)
effect_result = self._apply_item_effect(player, item)
return {

View File

@@ -1,56 +0,0 @@
#!/usr/bin/env python3
import json
import os
def test_config():
"""Test the config structure and values"""
config_path = 'config.json'
if not os.path.exists(config_path):
print("❌ Config file not found")
return
with open(config_path, 'r') as f:
config = json.load(f)
print("🧪 Testing configuration structure...")
# Test connection settings
connection = config.get('connection', {})
print(f"🌐 Server: {connection.get('server')}")
print(f"🔌 Port: {connection.get('port')}")
print(f"🤖 Nick: {connection.get('nick')}")
print(f"🔒 SSL: {connection.get('ssl')}")
# Check if password is set (not default)
password = connection.get('password')
password_set = password and password != "your_iline_password_here"
print(f"🔑 Password configured: {'Yes' if password_set else 'No (default placeholder)'}")
# Test SASL settings
sasl = config.get('sasl', {})
print(f"🔐 SASL enabled: {sasl.get('enabled')}")
print(f"👤 SASL username: {sasl.get('username')}")
# Check if SASL password is set (not default)
sasl_password = sasl.get('password')
sasl_password_set = sasl_password and sasl_password != "duckhunt//789//"
print(f"🗝️ SASL password configured: {'Yes' if sasl_password_set else 'No (default placeholder)'}")
# Test channels
channels = connection.get('channels', [])
print(f"📺 Channels to join: {channels}")
print("\n✅ Configuration structure looks good!")
if not password_set:
print("⚠️ Warning: Server password is still set to placeholder value")
print(" Update 'connection.password' if your server requires authentication")
if sasl.get('enabled') and not sasl_password_set:
print("⚠️ Warning: SASL is enabled but password is still placeholder")
print(" Update 'sasl.password' with your actual NickServ password")
if __name__ == "__main__":
test_config()