Complete inventory system implementation

- Added inventory capacity limits (configurable, default 20 slots)
- Items are stored in inventory before use for strategic gameplay
- Fixed indentation issues and syntax errors
- Inventory system fully configurable via config.json settings
This commit is contained in:
2025-09-12 20:59:52 +01:00
parent 3330f456ef
commit 86bf92c478
28 changed files with 6346 additions and 0 deletions

272
duckhunt/CONFIG_GUIDE.md Normal file
View File

@@ -0,0 +1,272 @@
# DuckHunt Bot Configuration Guide
This document explains all the configuration options available in `config.json` to customize your DuckHunt bot experience.
## Basic IRC Settings
```json
{
"server": "irc.rizon.net", // IRC server hostname
"port": 6697, // IRC server port (6667 for non-SSL, 6697 for SSL)
"nick": "DuckHunt", // Bot's nickname
"channels": ["#channel"], // List of channels to join
"ssl": true, // Enable SSL/TLS connection
"password": "", // Server password (if required)
"admins": ["nick1", "nick2"] // List of admin nicknames
}
```
## SASL Authentication
```json
"sasl": {
"enabled": true, // Enable SASL authentication
"username": "botaccount", // NickServ account username
"password": "botpassword" // NickServ account password
}
```
## Duck Spawning Configuration
```json
"duck_spawn_min": 1800, // Minimum time between duck spawns (seconds)
"duck_spawn_max": 5400, // Maximum time between duck spawns (seconds)
"duck_timeout_min": 45, // Minimum time duck stays alive (seconds)
"duck_timeout_max": 75, // Maximum time duck stays alive (seconds)
"sleep_hours": [], // Hours when no ducks spawn [start_hour, end_hour]
"max_ducks_per_channel": 3, // Maximum ducks that can exist per channel
```
### Duck Types
Configure different duck types with spawn rates and rewards:
```json
"duck_types": {
"normal": {
"enabled": true, // Enable this duck type
"spawn_rate": 70, // Percentage chance to spawn (out of 100)
"xp_reward": 10, // XP gained when caught
"health": 1 // How many hits to kill
},
"golden": {
"enabled": true,
"spawn_rate": 8,
"xp_reward": 50,
"health": 1
}
// ... more duck types
}
```
## Duck Befriending System
```json
"befriending": {
"enabled": true, // Enable !bef command
"base_success_rate": 65, // Base chance of successful befriend (%)
"max_success_rate": 90, // Maximum possible success rate (%)
"level_bonus_per_level": 2, // Success bonus per player level (%)
"level_bonus_cap": 20, // Maximum level bonus (%)
"luck_bonus_per_point": 3, // Success bonus per luck point (%)
"xp_reward": 8, // XP gained on successful befriend
"xp_reward_min": 1, // Minimum XP from befriending
"xp_reward_max": 3, // Maximum XP from befriending
"failure_xp_penalty": 1, // XP lost on failed befriend
"scared_away_chance": 10, // Chance duck flies away on failure (%)
"lucky_item_chance": 5 // Base chance for lucky item drops (%)
}
```
## Shooting Mechanics
```json
"shooting": {
"enabled": true, // Enable !bang command
"base_accuracy": 85, // Starting player accuracy (%)
"base_reliability": 90, // Starting gun reliability (%)
"jam_chance_base": 10, // Base gun jam chance (%)
"friendly_fire_enabled": true, // Allow shooting other players
"friendly_fire_chance": 5, // Chance of friendly fire (%)
"reflex_shot_bonus": 5, // Bonus for quick shots (%)
"miss_xp_penalty": 5, // XP lost on missed shot
"wild_shot_xp_penalty": 10, // XP lost on wild shot
"teamkill_xp_penalty": 20 // XP lost on team kill
}
```
## Weapon System
```json
"weapons": {
"enabled": true, // Enable weapon mechanics
"starting_weapon": "pistol", // Default weapon for new players
"starting_ammo": 6, // Starting ammo count
"max_ammo_base": 6, // Base maximum ammo capacity
"starting_chargers": 2, // Starting reload items
"max_chargers_base": 2, // Base maximum reload items
"durability_enabled": true, // Enable weapon wear/breaking
"confiscation_enabled": true // Allow admin gun confiscation
}
```
## Economy System
```json
"economy": {
"enabled": true, // Enable coin/shop system
"starting_coins": 100, // Coins for new players
"shop_enabled": true, // Enable !shop command
"trading_enabled": true, // Enable !trade command
"theft_enabled": true, // Enable !steal command
"theft_success_rate": 30, // Chance theft succeeds (%)
"theft_penalty": 50, // Coins lost if theft fails
"banking_enabled": true, // Enable banking system
"interest_rate": 5, // Bank interest rate (%)
"loan_enabled": true // Enable loan system
}
```
## Player Progression
```json
"progression": {
"enabled": true, // Enable XP/leveling system
"max_level": 40, // Maximum player level
"xp_multiplier": 1.0, // Global XP multiplier
"level_benefits_enabled": true, // Level bonuses (accuracy, etc.)
"titles_enabled": true, // Show player titles
"prestige_enabled": false // Enable prestige system
}
```
## Karma System
```json
"karma": {
"enabled": true, // Enable karma tracking
"hit_bonus": 2, // Karma for successful shots
"golden_hit_bonus": 5, // Karma for golden duck hits
"teamkill_penalty": 10, // Karma lost for team kills
"wild_shot_penalty": 3, // Karma lost for wild shots
"miss_penalty": 1, // Karma lost for misses
"befriend_success_bonus": 2, // Karma for successful befriends
"befriend_fail_penalty": 1 // Karma lost for failed befriends
}
```
## Items and Powerups
```json
"items": {
"enabled": true, // Enable item system
"lucky_items_enabled": true, // Enable lucky item drops
"lucky_item_base_chance": 5, // Base lucky item chance (%)
"detector_enabled": true, // Enable duck detector item
"silencer_enabled": true, // Enable silencer item
"sunglasses_enabled": true, // Enable sunglasses item
"explosive_ammo_enabled": true, // Enable explosive ammo
"sabotage_enabled": true, // Enable sabotage mechanics
"insurance_enabled": true, // Enable insurance system
"decoy_enabled": true // Enable decoy ducks
}
```
## Social Features
```json
"social": {
"leaderboards_enabled": true, // Enable !top command
"duck_alerts_enabled": true, // Enable duck spawn notifications
"private_messages_enabled": true, // Allow PM commands
"statistics_sharing_enabled": true, // Enable !stats sharing
"achievements_enabled": false // Enable achievement system
}
```
## Moderation Features
```json
"moderation": {
"ignore_system_enabled": true, // Enable !ignore command
"rate_limiting_enabled": true, // Prevent command spam
"rate_limit_cooldown": 2.0, // Seconds between commands
"admin_commands_enabled": true, // Enable admin commands
"ban_system_enabled": true, // Enable player banning
"database_reset_enabled": true, // Allow database resets
"admin_rearm_gives_full_ammo": true, // Admin !rearm gives full ammo
"admin_rearm_gives_full_chargers": true // Admin !rearm gives full chargers
}
```
## Advanced Features
```json
"advanced": {
"gun_jamming_enabled": true, // Enable gun jam mechanics
"weather_effects_enabled": false, // Weather affecting gameplay
"seasonal_events_enabled": false, // Special holiday events
"daily_challenges_enabled": false, // Daily quest system
"guild_system_enabled": false, // Player guilds/teams
"pvp_enabled": false // Player vs player combat
}
```
## Message Customization
```json
"messages": {
"custom_duck_messages_enabled": true, // Varied duck spawn messages
"color_enabled": true, // IRC color codes in messages
"emoji_enabled": true, // Unicode emojis in messages
"verbose_messages": true, // Detailed action messages
"success_sound_effects": true // Text sound effects
}
```
## Database Settings
```json
"database": {
"auto_save_enabled": true, // Automatic database saving
"auto_save_interval": 300, // Auto-save every N seconds
"backup_enabled": true, // Create database backups
"backup_interval": 3600, // Backup every N seconds
"compression_enabled": false // Compress database files
}
```
## Debug Options
```json
"debug": {
"debug_mode": false, // Enable debug features
"verbose_logging": false, // Extra detailed logs
"command_logging": false, // Log all commands
"performance_monitoring": false // Track performance metrics
}
```
## Configuration Tips
1. **Duck Spawn Timing**: Adjust `duck_spawn_min/max` based on channel activity
2. **Difficulty**: Lower `befriending.base_success_rate` for harder gameplay
3. **Economy**: Adjust XP rewards to balance progression
4. **Features**: Disable unwanted features by setting `enabled: false`
5. **Performance**: Enable rate limiting and disable verbose logging for busy channels
6. **Testing**: Use debug mode and shorter spawn times for testing
## Example Configurations
### Casual Server (Easy)
```json
"befriending": {
"base_success_rate": 80,
"max_success_rate": 95
},
"economy": {
"starting_coins": 200
}
```
### Competitive Server (Hard)
```json
"befriending": {
"base_success_rate": 45,
"max_success_rate": 75
},
"shooting": {
"base_accuracy": 70,
"friendly_fire_chance": 10
}
```
### Minimal Features
```json
"befriending": { "enabled": false },
"items": { "enabled": false },
"karma": { "enabled": false },
"social": { "leaderboards_enabled": false }
```

172
duckhunt/README.md Normal file
View File

@@ -0,0 +1,172 @@
# 🦆 DuckHunt IRC Bot
A feature-rich IRC game bot where players hunt ducks, upgrade weapons, trade items, and compete on leaderboards!
## 🚀 Features
### 🎯 Core Game Mechanics
- **Different Duck Types**: Common, Rare, Golden, and Armored ducks with varying rewards
- **Weapon System**: Multiple weapon types (Basic Gun, Shotgun, Rifle) with durability
- **Ammunition Types**: Standard, Rubber Bullets, Explosive Rounds
- **Weapon Attachments**: Laser Sight, Extended Magazine, Bipod
- **Accuracy & Reliability**: Skill-based hit/miss and reload failure mechanics
### 🏦 Economy System
- **Shop**: Buy/sell weapons, attachments, and upgrades
- **Banking**: Deposit coins for interest, take loans
- **Trading**: Trade coins and items with other players
- **Insurance**: Protect your equipment from damage
- **Hunting Licenses**: Unlock premium features and bonuses
### 👤 Player Progression
- **Hunter Levels**: Gain XP and level up for better abilities
- **Account System**: Register accounts with password authentication
- **Multiple Auth Methods**: Nick-based, hostmask, or registered account
- **Persistent Stats**: All progress saved to SQLite database
### 🏆 Social Features
- **Leaderboards**: Compete for top rankings
- **Duck Alerts**: Get notified when rare ducks spawn
- **Sabotage**: Interfere with other players (for a cost!)
- **Comprehensive Help**: Detailed command reference
## 📋 Requirements
- Python 3.7+
- asyncio support
- SQLite3 (included with Python)
## 🛠️ Installation
1. Clone or download the bot files
2. Edit `config.json` with your IRC server details:
```json
{
"server": "irc.libera.chat",
"port": 6697,
"nick": "DuckHuntBot",
"channels": ["#yourchannel"],
"ssl": true,
"sasl": false,
"password": "",
"duck_spawn_min": 60,
"duck_spawn_max": 300
}
```
3. Test the bot:
```bash
python test_bot.py
```
4. Run the bot:
```bash
python duckhunt.py
```
## 🎮 Commands
### 🎯 Hunting
- `!bang` - Shoot at a duck (accuracy-based hit/miss)
- `!reload` - Reload weapon (can fail based on reliability)
- `!catch` - Catch a duck with your hands
- `!bef` - Befriend a duck instead of shooting
### 🛒 Economy
- `!shop` - View available items
- `!buy <number>` - Purchase items
- `!sell <number>` - Sell equipment
- `!bank` - Banking services
- `!trade <player> <item> <amount>` - Trade with others
### 📊 Stats & Info
- `!stats` - Detailed combat statistics
- `!duckstats` - Personal hunting record
- `!leaderboard` - Top players ranking
- `!license` - Hunting license management
### ⚙️ Settings
- `!alerts` - Toggle duck spawn notifications
- `!help` - Complete command reference
### 🔐 Account System
- `/msg BotNick register <username> <password>` - Register account
- `/msg BotNick identify <username> <password>` - Login to account
### 🎮 Advanced
- `!sabotage <player>` - Sabotage another hunter's weapon
## 🗂️ File Structure
```
duckhunt/
├── src/
│ ├── duckhuntbot.py # Main IRC bot logic
│ ├── game.py # Game mechanics and commands
│ ├── db.py # SQLite database handling
│ ├── auth.py # Authentication system
│ ├── items.py # Duck types, weapons, attachments
│ ├── logging_utils.py # Colored logging setup
│ └── utils.py # IRC message parsing
├── config.json # Bot configuration
├── duckhunt.py # Main entry point
├── test_bot.py # Test script
└── README.md # This file
```
## 🎯 Game Balance
### Duck Types & Rewards
- **Common Duck** 🦆: 1 coin, 10 XP (70% spawn rate)
- **Rare Duck** 🦆✨: 3 coins, 25 XP (20% spawn rate)
- **Golden Duck** 🥇🦆: 10 coins, 50 XP (8% spawn rate)
- **Armored Duck** 🛡️🦆: 15 coins, 75 XP (2% spawn rate, 3 health)
### Weapon Stats
- **Basic Gun**: 0% accuracy bonus, 100 durability, 1 attachment slot
- **Shotgun**: -10% accuracy, 80 durability, 2 slots, spread shot
- **Rifle**: +20% accuracy, 120 durability, 3 slots
### Progression
- Players start with 100 coins and basic stats
- Level up by gaining XP from successful hunts
- Unlock better equipment and abilities as you progress
## 🔧 Configuration
Edit `config.json` to customize:
- IRC server and channels
- Duck spawn timing (min/max seconds)
- SSL and SASL authentication
- Bot nickname
## 🛡️ Security
- Passwords are hashed with PBKDF2
- Account data stored separately from temporary nick data
- Multiple authentication methods supported
- Database uses prepared statements to prevent injection
## 🐛 Troubleshooting
1. **Bot won't connect**: Check server/port in config.json
2. **Database errors**: Ensure write permissions in bot directory
3. **Commands not working**: Verify bot has joined the channel
4. **Test failures**: Run `python test_bot.py` to diagnose issues
## 🎖️ Contributing
Feel free to add new features:
- More duck types and weapons
- Additional mini-games
- Seasonal events
- Guild/team systems
- Advanced trading mechanics
## 📄 License
This bot is provided as-is for educational and entertainment purposes.
---
🦆 **Happy Hunting!** 🦆

Binary file not shown.

205
duckhunt/config.json Normal file
View File

@@ -0,0 +1,205 @@
{
"server": "irc.rizon.net",
"port": 6697,
"nick": "DuckHunt",
"channels": ["#computertech"],
"ssl": true,
"sasl": {
"enabled": true,
"username": "duckhunt",
"password": "duckhunt//789//"
},
"password": "",
"admins": ["peorth", "computertech", "colby"],
"_comment_duck_spawning": "Duck spawning configuration",
"duck_spawn_min": 1800,
"duck_spawn_max": 5400,
"duck_timeout_min": 45,
"duck_timeout_max": 75,
"duck_types": {
"normal": {
"spawn_chance": 0.6,
"xp_reward": 10,
"difficulty": 1.0,
"flee_time": 15,
"messages": ["・゜゜・。。・゜゜\\\\_o< QUACK!"]
},
"fast": {
"spawn_chance": 0.25,
"xp_reward": 15,
"difficulty": 1.5,
"flee_time": 8,
"messages": ["・゜゜・。。・゜゜\\\\_o< QUACK! (Fast duck!)"]
},
"rare": {
"spawn_chance": 0.1,
"xp_reward": 30,
"difficulty": 2.0,
"flee_time": 12,
"messages": ["・゜゜・。。・゜゜\\\\_o< QUACK! (Rare duck!)"]
},
"golden": {
"spawn_chance": 0.05,
"xp_reward": 75,
"difficulty": 3.0,
"flee_time": 10,
"messages": ["・゜゜・。。・゜゜\\\\_✪< ★ GOLDEN DUCK ★"]
}
},
"sleep_hours": [],
"max_ducks_per_channel": 3,
"_comment_befriending": "Duck befriending configuration",
"befriending": {
"enabled": true,
"success_chance": 0.7,
"failure_messages": [
"The duck looked at you suspiciously and flew away!",
"The duck didn't trust you and escaped!",
"The duck was too scared and ran off!"
],
"scared_away_chance": 0.1,
"scared_away_messages": [
"You scared the duck away with your approach!",
"The duck was terrified and fled immediately!"
],
"xp_reward_min": 1,
"xp_reward_max": 3
},
"_comment_shooting": "Shooting mechanics configuration",
"shooting": {
"enabled": true,
"base_accuracy": 85,
"base_reliability": 90,
"jam_chance_base": 10,
"friendly_fire_enabled": true,
"friendly_fire_chance": 5,
"reflex_shot_bonus": 5,
"miss_xp_penalty": 5,
"wild_shot_xp_penalty": 10,
"teamkill_xp_penalty": 20
},
"_comment_weapons": "Weapon system configuration",
"weapons": {
"enabled": true,
"starting_weapon": "pistol",
"starting_ammo": 6,
"max_ammo_base": 6,
"starting_chargers": 2,
"max_chargers_base": 2,
"durability_enabled": true,
"confiscation_enabled": true
},
"_comment_economy": "Economy and shop configuration",
"economy": {
"enabled": true,
"starting_coins": 100,
"shop_enabled": true,
"trading_enabled": true,
"theft_enabled": true,
"theft_success_rate": 30,
"theft_penalty": 50,
"banking_enabled": true,
"interest_rate": 5,
"loan_enabled": true,
"inventory_system_enabled": true,
"max_inventory_slots": 20
},
"_comment_progression": "Player progression configuration",
"progression": {
"enabled": true,
"max_level": 40,
"xp_multiplier": 1.0,
"level_benefits_enabled": true,
"titles_enabled": true,
"prestige_enabled": false
},
"_comment_karma": "Karma system configuration",
"karma": {
"enabled": true,
"hit_bonus": 2,
"golden_hit_bonus": 5,
"teamkill_penalty": 10,
"wild_shot_penalty": 3,
"miss_penalty": 1,
"befriend_success_bonus": 2,
"befriend_fail_penalty": 1
},
"_comment_items": "Items and powerups configuration",
"items": {
"enabled": true,
"lucky_items_enabled": true,
"lucky_item_base_chance": 5,
"detector_enabled": true,
"silencer_enabled": true,
"sunglasses_enabled": true,
"explosive_ammo_enabled": true,
"sabotage_enabled": true,
"insurance_enabled": true,
"decoy_enabled": true
},
"_comment_social": "Social features configuration",
"social": {
"leaderboards_enabled": true,
"duck_alerts_enabled": true,
"private_messages_enabled": true,
"statistics_sharing_enabled": true,
"achievements_enabled": false
},
"_comment_moderation": "Moderation and admin features",
"moderation": {
"ignore_system_enabled": true,
"rate_limiting_enabled": true,
"rate_limit_cooldown": 2.0,
"admin_commands_enabled": true,
"ban_system_enabled": true,
"database_reset_enabled": true,
"admin_rearm_gives_full_ammo": false,
"admin_rearm_gives_full_chargers":false
},
"_comment_advanced": "Advanced game mechanics",
"advanced": {
"gun_jamming_enabled": true,
"weather_effects_enabled": false,
"seasonal_events_enabled": false,
"daily_challenges_enabled": false,
"guild_system_enabled": false,
"pvp_enabled": false
},
"_comment_messages": "Message customization",
"messages": {
"custom_duck_messages_enabled": true,
"color_enabled": true,
"emoji_enabled": true,
"verbose_messages": true,
"success_sound_effects": true
},
"_comment_database": "Database and persistence",
"database": {
"auto_save_enabled": true,
"auto_save_interval": 300,
"backup_enabled": true,
"backup_interval": 3600,
"compression_enabled": false
},
"_comment_debug": "Debug and logging options",
"debug": {
"debug_mode": false,
"verbose_logging": false,
"command_logging": false,
"performance_monitoring": false
}
}

216
duckhunt/config_backup.json Normal file
View File

@@ -0,0 +1,216 @@
{
"server": "irc.rizon.net",
"port": 6697,
"nick": "DuckHunt",
"channels": ["#computertech"],
"ssl": true,
"sasl": {
"enabled": true,
"username": "duckhunt",
"password": "duckhunt//789//"
},
"password": "",
"admins": ["peorth", "computertech"],
"_comment_duck_spawning": "Duck spawning configuration",
"duck_spawn_min": 1800,
"duck_spawn_max": 5400,
"duck_timeout_min": 45,
"duck_timeout_max": 75,
"duck_types": {
"normal": {
"enabled": true,
"spawn_rate": 70,
"xp_reward": 10,
"coin_reward": 1,
"health": 1
},
"golden": {
"enabled": true,
"spawn_rate": 8,
"xp_reward": 50,
"coin_reward": 10,
"health": 1
},
"armored": {
"enabled": true,
"spawn_rate": 2,
"xp_reward": 75,
"coin_reward": 15,
"health": 3
},
"rare": {
"enabled": true,
"spawn_rate": 20,
"xp_reward": 25,
"coin_reward": 3,
"health": 1
}
},
"sleep_hours": [],
"max_ducks_per_channel": 3,
"_comment_befriending": "Duck befriending configuration",
"befriending": {
"enabled": true,
"base_success_rate": 65,
"max_success_rate": 90,
"level_bonus_per_level": 2,
"level_bonus_cap": 20,
"luck_bonus_per_point": 3,
"xp_reward": 8,
"coin_reward_min": 1,
"coin_reward_max": 2,
"failure_xp_penalty": 1,
"scared_away_chance": 10,
"lucky_item_chance": 5
},
"_comment_shooting": "Shooting mechanics configuration",
"shooting": {
"enabled": true,
"base_accuracy": 85,
"base_reliability": 90,
"jam_chance_base": 10,
"friendly_fire_enabled": true,
"friendly_fire_chance": 5,
"reflex_shot_bonus": 5,
"miss_xp_penalty": 5,
"wild_shot_xp_penalty": 10,
"teamkill_xp_penalty": 20
},
"_comment_weapons": "Weapon system configuration",
"weapons": {
"enabled": true,
"starting_weapon": "pistol",
"starting_ammo": 6,
"max_ammo_base": 6,
"starting_chargers": 2,
"max_chargers_base": 2,
"durability_enabled": true,
"confiscation_enabled": true
},
"_comment_economy": "Economy and shop configuration",
"economy": {
"enabled": true,
"starting_coins": 100,
"shop_enabled": true,
"trading_enabled": true,
"theft_enabled": true,
"theft_success_rate": 30,
"theft_penalty": 50,
"banking_enabled": true,
"interest_rate": 5,
"loan_enabled": true
},
"_comment_progression": "Player progression configuration",
"progression": {
"enabled": true,
"max_level": 40,
"xp_multiplier": 1.0,
"level_benefits_enabled": true,
"titles_enabled": true,
"prestige_enabled": false
},
"_comment_karma": "Karma system configuration",
"karma": {
"enabled": true,
"hit_bonus": 2,
"golden_hit_bonus": 5,
"teamkill_penalty": 10,
"wild_shot_penalty": 3,
"miss_penalty": 1,
"befriend_success_bonus": 2,
"befriend_fail_penalty": 1
},
"_comment_items": "Items and powerups configuration",
"items": {
"enabled": true,
"lucky_items_enabled": true,
"lucky_item_base_chance": 5,
"detector_enabled": true,
"silencer_enabled": true,
"sunglasses_enabled": true,
"explosive_ammo_enabled": true,
"sabotage_enabled": true,
"insurance_enabled": true,
"decoy_enabled": true
},
"_comment_social": "Social features configuration",
"social": {
"leaderboards_enabled": true,
"duck_alerts_enabled": true,
"private_messages_enabled": true,
"statistics_sharing_enabled": true,
"achievements_enabled": false
},
"_comment_moderation": "Moderation and admin features",
"moderation": {
"ignore_system_enabled": true,
"rate_limiting_enabled": true,
"rate_limit_cooldown": 2.0,
"admin_commands_enabled": true,
"ban_system_enabled": true,
"database_reset_enabled": true
},
"_comment_advanced": "Advanced game mechanics",
"advanced": {
"gun_jamming_enabled": true,
"weather_effects_enabled": false,
"seasonal_events_enabled": false,
"daily_challenges_enabled": false,
"guild_system_enabled": false,
"pvp_enabled": false
},
"_comment_messages": "Message customization",
"messages": {
"custom_duck_messages_enabled": true,
"color_enabled": true,
"emoji_enabled": true,
"verbose_messages": true,
"success_sound_effects": true
},
"_comment_database": "Database and persistence",
"database": {
"auto_save_enabled": true,
"auto_save_interval": 300,
"backup_enabled": true,
"backup_interval": 3600,
"compression_enabled": false
},
"_comment_debug": "Debug and logging options",
"debug": {
"debug_mode": false,
"verbose_logging": false,
"command_logging": false,
"performance_monitoring": false
}
}c.rizon.net",
"port": 6697,
"nick": "DuckHunt",
"channels": ["#computertech"],
"ssl": true,
"sasl": {
"enabled": true,
"username": "duckhunt",
"password": "duckhunt//789//"
},
"password": "",
"admins": ["colby", "computertech"],
"duck_spawn_min": 1800,
"duck_spawn_max": 5400,
"duck_timeout_min": 45,
"duck_timeout_max": 75,
"_comment": "Run with: python3 simple_duckhunt.py | Admins config-only | Private admin: /msg DuckHuntBot restart|quit|launch | Duck timeout: random between min-max seconds"
}

122
duckhunt/demo_sasl.py Normal file
View File

@@ -0,0 +1,122 @@
#!/usr/bin/env python3
"""
SASL Integration Demo for DuckHunt Bot
This script demonstrates how the modular SASL authentication works
"""
import asyncio
import json
import sys
import os
# Add src directory to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
from src.sasl import SASLHandler
from src.logging_utils import setup_logger
class MockBot:
"""Mock bot for testing SASL without IRC connection"""
def __init__(self, config):
self.config = config
self.logger = setup_logger("MockBot")
self.messages_sent = []
def send_raw(self, message):
"""Mock send_raw that just logs the message"""
self.messages_sent.append(message)
self.logger.info(f"SEND: {message}")
async def register_user(self):
"""Mock registration"""
self.logger.info("Mock user registration completed")
async def demo_sasl_flow():
"""Demonstrate the SASL authentication flow"""
print("🔐 SASL Authentication Flow Demo")
print("=" * 50)
# Load config
with open('config.json') as f:
config = json.load(f)
# Override with test credentials for demo
config['sasl'] = {
'enabled': True,
'username': 'testuser',
'password': 'testpass123'
}
# Create mock bot and SASL handler
bot = MockBot(config)
sasl_handler = SASLHandler(bot, config)
print("\n1⃣ Starting SASL negotiation...")
if await sasl_handler.start_negotiation():
print("✅ SASL negotiation started successfully")
else:
print("❌ SASL negotiation failed to start")
return
print("\n2⃣ Simulating server CAP response...")
# Simulate server listing SASL capability
params = ['*', 'LS', '*']
trailing = 'sasl multi-prefix extended-join'
await sasl_handler.handle_cap_response(params, trailing)
print("\n3⃣ Simulating server acknowledging SASL capability...")
# Simulate server acknowledging SASL
params = ['*', 'ACK']
trailing = 'sasl'
await sasl_handler.handle_cap_response(params, trailing)
print("\n4⃣ Simulating server ready for authentication...")
# Simulate server ready for auth
params = ['+']
await sasl_handler.handle_authenticate_response(params)
print("\n5⃣ Simulating successful authentication...")
# Simulate successful authentication
params = ['DuckHunt']
trailing = 'You are now logged in as duckhunt'
await sasl_handler.handle_sasl_result('903', params, trailing)
print(f"\n📤 Messages sent to server:")
for i, msg in enumerate(bot.messages_sent, 1):
print(f" {i}. {msg}")
print(f"\n🔍 Authentication status: {'✅ Authenticated' if sasl_handler.is_authenticated() else '❌ Not authenticated'}")
print("\n" + "=" * 50)
print("✨ SASL flow demonstration complete!")
async def demo_sasl_failure():
"""Demonstrate SASL failure handling"""
print("\n\n🚫 SASL Failure Handling Demo")
print("=" * 50)
# Create mock bot with wrong credentials
config = {
'sasl': {
'enabled': True,
'username': 'testuser',
'password': 'wrong_password'
}
}
bot = MockBot(config)
sasl_handler = SASLHandler(bot, config)
print("\n1⃣ Starting SASL with wrong credentials...")
await sasl_handler.start_negotiation()
# Simulate failed authentication
params = ['DuckHunt']
trailing = 'Invalid credentials'
await sasl_handler.handle_sasl_result('904', params, trailing)
print(f"\n🔍 Authentication status: {'✅ Authenticated' if sasl_handler.is_authenticated() else '❌ Not authenticated'}")
print("✅ Failure handled gracefully - bot will fallback to NickServ")
if __name__ == '__main__':
asyncio.run(demo_sasl_flow())
asyncio.run(demo_sasl_failure())

BIN
duckhunt/duckhunt.db Normal file

Binary file not shown.

960
duckhunt/duckhunt.json Normal file
View File

@@ -0,0 +1,960 @@
{
"players": {
"guest44288": {
"current_nick": "Guest44288",
"hostmask": "~Colby@Rizon-FFE0901B.ipcom.comunitel.net",
"coins": 102,
"caught": 1,
"ammo": 9,
"max_ammo": 10,
"chargers": 2,
"max_chargers": 2,
"xp": 15,
"accuracy": 85,
"reliability": 90,
"duck_start_time": null,
"gun_level": 1,
"luck": 0,
"gun_type": "pistol"
},
"colby": {
"coins": 119,
"caught": 12,
"ammo": 5,
"max_ammo": 10,
"chargers": 0,
"max_chargers": 2,
"xp": 100,
"accuracy": 65,
"reliability": 90,
"duck_start_time": null,
"gun_level": 1,
"luck": 0,
"gun_type": "pistol",
"gun_confiscated": false,
"jammed": false,
"jammed_count": 1,
"total_ammo_used": 12,
"shot_at": 9,
"reflex_shots": 6,
"total_reflex_time": 47.87793278694153,
"best_time": 2.6269078254699707,
"karma": 1,
"wild_shots": 4,
"befriended": 6,
"missed": 1,
"inventory": {
"15": 1
},
"sand": 1
},
"colby_": {
"xp": 0,
"caught": 0,
"befriended": 0,
"missed": 0,
"ammo": 6,
"max_ammo": 6,
"chargers": 2,
"max_chargers": 2,
"accuracy": 65,
"reliability": 70,
"gun_confiscated": false,
"explosive_ammo": false,
"settings": {
"notices": true,
"private_messages": false
},
"golden_ducks": 0,
"karma": 0,
"deflection": 0,
"defense": 0,
"jammed": false,
"jammed_count": 0,
"deaths": 0,
"neutralized": 0,
"deflected": 0,
"best_time": 999.9,
"total_reflex_time": 0.0,
"reflex_shots": 0,
"wild_shots": 0,
"accidents": 0,
"total_ammo_used": 0,
"shot_at": 0,
"lucky_shots": 0,
"luck": 0,
"detector": 0,
"silencer": 0,
"sunglasses": 0,
"clothes": 0,
"grease": 0,
"brush": 0,
"mirror": 0,
"sand": 0,
"water": 0,
"sabotage": 0,
"life_insurance": 0,
"liability": 0,
"decoy": 0,
"bread": 0,
"duck_detector": 0,
"mechanical": 0
},
"computertech": {
"xp": 4,
"caught": 1,
"befriended": 0,
"missed": 2,
"ammo": 3,
"max_ammo": 6,
"chargers": 2,
"max_chargers": 2,
"accuracy": 65,
"reliability": 70,
"weapon": "pistol",
"gun_confiscated": false,
"explosive_ammo": false,
"settings": {
"notices": true,
"private_messages": false
},
"golden_ducks": 0,
"karma": 0,
"deflection": 0,
"defense": 0,
"jammed": true,
"jammed_count": 4,
"deaths": 0,
"neutralized": 0,
"deflected": 0,
"best_time": 1.458634614944458,
"total_reflex_time": 1.458634614944458,
"reflex_shots": 1,
"wild_shots": 0,
"accidents": 0,
"total_ammo_used": 3,
"shot_at": 3,
"lucky_shots": 0,
"luck": 0,
"detector": 0,
"silencer": 0,
"sunglasses": 0,
"clothes": 0,
"grease": 0,
"brush": 0,
"mirror": 0,
"sand": 0,
"water": 0,
"sabotage": 0,
"life_insurance": 0,
"liability": 0,
"decoy": 0,
"bread": 0,
"duck_detector": 0,
"mechanical": 0,
"inventory": {}
},
"loulan": {
"xp": -9,
"caught": 0,
"befriended": 0,
"missed": 2,
"ammo": 5,
"max_ammo": 6,
"chargers": 2,
"max_chargers": 2,
"accuracy": 65,
"reliability": 70,
"weapon": "pistol",
"gun_confiscated": false,
"explosive_ammo": false,
"settings": {
"notices": true,
"private_messages": false
},
"golden_ducks": 0,
"karma": -5,
"deflection": 0,
"defense": 0,
"jammed": true,
"jammed_count": 1,
"deaths": 0,
"neutralized": 0,
"deflected": 0,
"best_time": 999.9,
"total_reflex_time": 0.0,
"reflex_shots": 0,
"wild_shots": 1,
"accidents": 0,
"total_ammo_used": 3,
"shot_at": 2,
"lucky_shots": 0,
"luck": 0,
"detector": 0,
"silencer": 0,
"sunglasses": 0,
"clothes": 0,
"grease": 0,
"brush": 0,
"mirror": 0,
"sand": 0,
"water": 0,
"sabotage": 0,
"life_insurance": 0,
"liability": 0,
"decoy": 0,
"bread": 0,
"duck_detector": 0,
"mechanical": 0
},
"madafaka": {
"xp": 42,
"caught": 3,
"befriended": 0,
"missed": 0,
"ammo": 3,
"max_ammo": 6,
"chargers": 2,
"max_chargers": 2,
"accuracy": 65,
"reliability": 70,
"weapon": "pistol",
"gun_confiscated": false,
"explosive_ammo": false,
"settings": {
"notices": true,
"private_messages": false
},
"golden_ducks": 0,
"karma": 6,
"deflection": 0,
"defense": 0,
"jammed": false,
"jammed_count": 0,
"deaths": 0,
"neutralized": 0,
"deflected": 0,
"best_time": 2.8583714962005615,
"total_reflex_time": 12.643521547317505,
"reflex_shots": 3,
"wild_shots": 0,
"accidents": 0,
"total_ammo_used": 3,
"shot_at": 4,
"lucky_shots": 0,
"luck": 0,
"detector": 0,
"silencer": 0,
"sunglasses": 0,
"clothes": 0,
"grease": 0,
"brush": 0,
"mirror": 0,
"sand": 0,
"water": 0,
"sabotage": 0,
"life_insurance": 0,
"liability": 0,
"decoy": 0,
"bread": 0,
"duck_detector": 0,
"mechanical": 0
},
"peorth": {
"xp": 4,
"caught": 1,
"befriended": 0,
"missed": 0,
"ammo": 6,
"max_ammo": 6,
"chargers": 2,
"max_chargers": 2,
"accuracy": 65,
"reliability": 70,
"weapon": "pistol",
"gun_confiscated": false,
"explosive_ammo": false,
"settings": {
"notices": true,
"private_messages": false
},
"golden_ducks": 0,
"karma": 2,
"deflection": 0,
"defense": 0,
"jammed": false,
"jammed_count": 1,
"deaths": 0,
"neutralized": 0,
"deflected": 0,
"best_time": 18.33902668952942,
"total_reflex_time": 18.33902668952942,
"reflex_shots": 1,
"wild_shots": 0,
"accidents": 0,
"total_ammo_used": 1,
"shot_at": 2,
"lucky_shots": 0,
"luck": 0,
"detector": 0,
"silencer": 0,
"sunglasses": 0,
"clothes": 0,
"grease": 0,
"brush": 0,
"mirror": 0,
"sand": 0,
"water": 0,
"sabotage": 0,
"life_insurance": 0,
"liability": 0,
"decoy": 0,
"bread": 0,
"duck_detector": 0,
"mechanical": 0
},
"dgw": {
"xp": 30,
"caught": 2,
"befriended": 0,
"missed": 0,
"ammo": 4,
"max_ammo": 6,
"chargers": 2,
"max_chargers": 2,
"accuracy": 65,
"reliability": 70,
"weapon": "pistol",
"gun_confiscated": false,
"explosive_ammo": false,
"settings": {
"notices": true,
"private_messages": false
},
"golden_ducks": 0,
"karma": 4,
"deflection": 0,
"defense": 0,
"jammed": false,
"jammed_count": 0,
"deaths": 0,
"neutralized": 0,
"deflected": 0,
"best_time": 9.253741025924683,
"total_reflex_time": 20.60851550102234,
"reflex_shots": 2,
"wild_shots": 0,
"accidents": 0,
"total_ammo_used": 2,
"shot_at": 2,
"lucky_shots": 0,
"luck": 0,
"detector": 0,
"silencer": 0,
"sunglasses": 0,
"clothes": 0,
"grease": 0,
"brush": 0,
"mirror": 0,
"sand": 0,
"water": 0,
"sabotage": 0,
"life_insurance": 1,
"liability": 0,
"decoy": 0,
"bread": 0,
"duck_detector": 0,
"mechanical": 0
},
"admiral_hubris": {
"xp": 0,
"caught": 1,
"befriended": 0,
"missed": 0,
"ammo": 4,
"max_ammo": 6,
"chargers": 2,
"max_chargers": 2,
"accuracy": 45,
"reliability": 70,
"weapon": "pistol",
"gun_confiscated": true,
"explosive_ammo": false,
"settings": {
"notices": true,
"private_messages": false
},
"golden_ducks": 0,
"karma": -17,
"deflection": 0,
"defense": 0,
"jammed": false,
"jammed_count": 0,
"deaths": 0,
"neutralized": 0,
"deflected": 0,
"best_time": 6.574016809463501,
"total_reflex_time": 6.574016809463501,
"reflex_shots": 1,
"wild_shots": 3,
"accidents": 1,
"total_ammo_used": 4,
"shot_at": 1,
"lucky_shots": 0,
"luck": 0,
"detector": 0,
"silencer": 0,
"sunglasses": 0,
"clothes": 0,
"grease": 0,
"brush": 0,
"mirror": 0,
"sand": 0,
"water": 0,
"sabotage": 0,
"life_insurance": 0,
"liability": 0,
"decoy": 0,
"bread": 0,
"duck_detector": 0,
"mechanical": 0,
"inventory": {}
},
"xysha": {
"xp": 0,
"caught": 0,
"befriended": 0,
"missed": 0,
"ammo": 5,
"max_ammo": 6,
"chargers": 2,
"max_chargers": 2,
"accuracy": 65,
"reliability": 70,
"weapon": "pistol",
"gun_confiscated": true,
"explosive_ammo": false,
"settings": {
"notices": true,
"private_messages": false
},
"golden_ducks": 0,
"karma": -14,
"deflection": 0,
"defense": 0,
"jammed": false,
"jammed_count": 1,
"deaths": 0,
"neutralized": 0,
"deflected": 0,
"best_time": 999.9,
"total_reflex_time": 0.0,
"reflex_shots": 0,
"wild_shots": 1,
"accidents": 1,
"total_ammo_used": 1,
"shot_at": 1,
"lucky_shots": 0,
"luck": 0,
"detector": 0,
"silencer": 0,
"sunglasses": 0,
"clothes": 0,
"grease": 0,
"brush": 0,
"mirror": 0,
"sand": 0,
"water": 0,
"sabotage": 0,
"life_insurance": 0,
"liability": 0,
"decoy": 0,
"bread": 0,
"duck_detector": 0,
"mechanical": 0,
"inventory": {}
},
"boliver": {
"xp": 125,
"caught": 5,
"befriended": 4,
"missed": 2,
"ammo": 5,
"max_ammo": 6,
"chargers": 1,
"max_chargers": 2,
"accuracy": 45,
"reliability": 70,
"weapon": "pistol",
"gun_confiscated": true,
"explosive_ammo": false,
"settings": {
"notices": true,
"private_messages": false
},
"golden_ducks": 1,
"karma": -3,
"deflection": 0,
"defense": 0,
"jammed": false,
"jammed_count": 7,
"deaths": 0,
"neutralized": 0,
"deflected": 0,
"best_time": 2.484381914138794,
"total_reflex_time": 19.068661212921143,
"reflex_shots": 5,
"wild_shots": 2,
"accidents": 1,
"total_ammo_used": 9,
"shot_at": 7,
"lucky_shots": 1,
"luck": 1,
"detector": 0,
"silencer": 0,
"sunglasses": 0,
"clothes": 0,
"grease": 0,
"brush": 0,
"mirror": 0,
"sand": 0,
"water": 1,
"sabotage": 0,
"life_insurance": 0,
"liability": 0,
"decoy": 0,
"bread": 0,
"duck_detector": 0,
"mechanical": 0,
"inventory": {}
},
"kaitphone": {
"xp": 13,
"caught": 1,
"befriended": 0,
"missed": 1,
"ammo": 4,
"max_ammo": 6,
"chargers": 2,
"max_chargers": 2,
"accuracy": 65,
"reliability": 70,
"weapon": "pistol",
"gun_confiscated": false,
"explosive_ammo": false,
"settings": {
"notices": true,
"private_messages": false
},
"golden_ducks": 0,
"karma": 1,
"deflection": 0,
"defense": 0,
"jammed": false,
"jammed_count": 3,
"deaths": 0,
"neutralized": 0,
"deflected": 0,
"best_time": 28.610472440719604,
"total_reflex_time": 28.610472440719604,
"reflex_shots": 1,
"wild_shots": 0,
"accidents": 0,
"total_ammo_used": 2,
"shot_at": 2,
"lucky_shots": 0,
"luck": 0,
"detector": 0,
"silencer": 0,
"sunglasses": 0,
"clothes": 0,
"grease": 0,
"brush": 0,
"mirror": 0,
"sand": 0,
"water": 0,
"sabotage": 0,
"life_insurance": 0,
"liability": 0,
"decoy": 0,
"bread": 0,
"duck_detector": 0,
"mechanical": 0
},
"milambar": {
"xp": 12,
"caught": 2,
"befriended": 3,
"missed": 0,
"ammo": 3,
"max_ammo": 6,
"chargers": 2,
"max_chargers": 2,
"accuracy": 65,
"reliability": 70,
"weapon": "pistol",
"gun_confiscated": true,
"explosive_ammo": false,
"settings": {
"notices": true,
"private_messages": false
},
"golden_ducks": 0,
"karma": 7,
"deflection": 0,
"defense": 0,
"jammed": false,
"jammed_count": 1,
"deaths": 0,
"neutralized": 0,
"deflected": 0,
"best_time": 2.784888982772827,
"total_reflex_time": 8.606451749801636,
"reflex_shots": 2,
"wild_shots": 1,
"accidents": 0,
"total_ammo_used": 3,
"shot_at": 2,
"lucky_shots": 0,
"luck": 1,
"detector": 0,
"silencer": 0,
"sunglasses": 0,
"clothes": 0,
"grease": 0,
"brush": 0,
"mirror": 0,
"sand": 0,
"water": 0,
"sabotage": 0,
"life_insurance": 0,
"liability": 0,
"decoy": 0,
"bread": 0,
"duck_detector": 0,
"mechanical": 0,
"inventory": {}
},
"wobotkoala": {
"xp": 0,
"caught": 0,
"befriended": 0,
"missed": 0,
"ammo": 6,
"max_ammo": 6,
"chargers": 2,
"max_chargers": 2,
"accuracy": 65,
"reliability": 70,
"weapon": "pistol",
"gun_confiscated": false,
"explosive_ammo": false,
"settings": {
"notices": true,
"private_messages": false
},
"golden_ducks": 0,
"karma": 0,
"deflection": 0,
"defense": 0,
"jammed": false,
"jammed_count": 0,
"deaths": 0,
"neutralized": 0,
"deflected": 0,
"best_time": 999.9,
"total_reflex_time": 0.0,
"reflex_shots": 0,
"wild_shots": 0,
"accidents": 0,
"total_ammo_used": 0,
"shot_at": 0,
"lucky_shots": 0,
"luck": 0,
"detector": 0,
"silencer": 0,
"sunglasses": 0,
"clothes": 0,
"grease": 0,
"brush": 0,
"mirror": 0,
"sand": 0,
"water": 0,
"sabotage": 0,
"life_insurance": 0,
"liability": 0,
"decoy": 0,
"bread": 0,
"duck_detector": 0,
"mechanical": 0
},
"general_kornwallace": {
"xp": 0,
"caught": 0,
"befriended": 0,
"missed": 0,
"ammo": 6,
"max_ammo": 6,
"chargers": 2,
"max_chargers": 2,
"accuracy": 65,
"reliability": 70,
"weapon": "pistol",
"gun_confiscated": false,
"explosive_ammo": false,
"settings": {
"notices": true,
"private_messages": false
},
"golden_ducks": 0,
"karma": 0,
"deflection": 0,
"defense": 0,
"jammed": false,
"jammed_count": 1,
"deaths": 0,
"neutralized": 0,
"deflected": 0,
"best_time": 999.9,
"total_reflex_time": 0.0,
"reflex_shots": 0,
"wild_shots": 0,
"accidents": 0,
"total_ammo_used": 0,
"shot_at": 0,
"lucky_shots": 0,
"luck": 0,
"detector": 0,
"silencer": 0,
"sunglasses": 0,
"clothes": 0,
"grease": 0,
"brush": 0,
"mirror": 0,
"sand": 0,
"water": 0,
"sabotage": 0,
"life_insurance": 0,
"liability": 0,
"decoy": 0,
"bread": 0,
"duck_detector": 0,
"mechanical": 0
},
"helderheid": {
"xp": 0,
"caught": 0,
"befriended": 0,
"missed": 0,
"ammo": 6,
"max_ammo": 6,
"chargers": 2,
"max_chargers": 2,
"accuracy": 65,
"reliability": 70,
"weapon": "pistol",
"gun_confiscated": false,
"explosive_ammo": false,
"settings": {
"notices": true,
"private_messages": false
},
"golden_ducks": 0,
"karma": 0,
"deflection": 0,
"defense": 0,
"jammed": false,
"jammed_count": 0,
"deaths": 0,
"neutralized": 0,
"deflected": 0,
"best_time": 999.9,
"total_reflex_time": 0.0,
"reflex_shots": 0,
"wild_shots": 0,
"accidents": 0,
"total_ammo_used": 0,
"shot_at": 0,
"lucky_shots": 0,
"luck": 0,
"detector": 0,
"silencer": 0,
"sunglasses": 0,
"clothes": 0,
"grease": 0,
"brush": 0,
"mirror": 0,
"sand": 0,
"water": 0,
"sabotage": 0,
"life_insurance": 0,
"liability": 0,
"decoy": 0,
"bread": 0,
"duck_detector": 0,
"mechanical": 0
},
"coolkevin": {
"xp": 12,
"caught": 1,
"befriended": 0,
"missed": 0,
"ammo": 5,
"max_ammo": 6,
"chargers": 2,
"max_chargers": 2,
"accuracy": 65,
"reliability": 70,
"weapon": "pistol",
"gun_confiscated": false,
"explosive_ammo": false,
"settings": {
"notices": true,
"private_messages": false
},
"golden_ducks": 0,
"karma": 2,
"deflection": 0,
"defense": 0,
"jammed": false,
"jammed_count": 0,
"deaths": 0,
"neutralized": 0,
"deflected": 0,
"best_time": 4.75800085067749,
"total_reflex_time": 4.75800085067749,
"reflex_shots": 1,
"wild_shots": 0,
"accidents": 0,
"total_ammo_used": 1,
"shot_at": 2,
"lucky_shots": 0,
"luck": 0,
"detector": 0,
"silencer": 0,
"sunglasses": 0,
"clothes": 0,
"grease": 0,
"brush": 0,
"mirror": 0,
"sand": 0,
"water": 0,
"sabotage": 0,
"life_insurance": 0,
"liability": 0,
"decoy": 0,
"bread": 0,
"duck_detector": 0,
"mechanical": 0
},
"magicalpig": {
"xp": 23,
"caught": 2,
"befriended": 1,
"missed": 1,
"ammo": 3,
"max_ammo": 6,
"chargers": 2,
"max_chargers": 2,
"accuracy": 85,
"reliability": 90,
"weapon": "pistol",
"gun_confiscated": false,
"explosive_ammo": false,
"settings": {
"notices": true,
"private_messages": false
},
"golden_ducks": 0,
"karma": -11,
"deflection": 0,
"defense": 0,
"jammed": false,
"jammed_count": 0,
"deaths": 0,
"neutralized": 0,
"deflected": 0,
"best_time": 9.407448291778564,
"total_reflex_time": 21.80314612388611,
"reflex_shots": 2,
"wild_shots": 2,
"accidents": 1,
"total_ammo_used": 5,
"shot_at": 3,
"lucky_shots": 0,
"luck": 0,
"detector": 0,
"silencer": 0,
"sunglasses": 0,
"clothes": 0,
"grease": 0,
"brush": 0,
"mirror": 0,
"sand": 0,
"water": 0,
"sabotage": 0,
"life_insurance": 0,
"liability": 0,
"decoy": 0,
"bread": 0,
"duck_detector": 0,
"mechanical": 0,
"inventory": {}
},
"tabb": {
"xp": -5,
"caught": 0,
"befriended": 0,
"missed": 0,
"ammo": 5,
"max_ammo": 6,
"chargers": 2,
"max_chargers": 2,
"accuracy": 85,
"reliability": 90,
"weapon": "pistol",
"gun_confiscated": true,
"explosive_ammo": false,
"settings": {
"notices": true,
"private_messages": false
},
"inventory": {},
"golden_ducks": 0,
"karma": -3,
"deflection": 0,
"defense": 0,
"jammed": false,
"jammed_count": 0,
"deaths": 0,
"neutralized": 0,
"deflected": 0,
"best_time": 999.9,
"total_reflex_time": 0.0,
"reflex_shots": 0,
"wild_shots": 1,
"accidents": 0,
"total_ammo_used": 1,
"shot_at": 0,
"lucky_shots": 0,
"luck": 0,
"detector": 0,
"silencer": 0,
"sunglasses": 0,
"clothes": 0,
"grease": 0,
"brush": 0,
"mirror": 0,
"sand": 0,
"water": 0,
"sabotage": 0,
"life_insurance": 0,
"liability": 0,
"decoy": 0,
"bread": 0,
"duck_detector": 0,
"mechanical": 0
}
},
"last_save": "1757707147.438802"
}

543
duckhunt/duckhunt.log Normal file
View File

@@ -0,0 +1,543 @@
[2025-09-11 18:30:40,346] INFO: Loaded 3 players from duckhunt.json
[2025-09-11 18:30:40,346] INFO: DuckHunt Bot initializing...
[2025-09-11 18:30:40,347] INFO: Starting DuckHunt Bot...
[2025-09-11 18:30:40,347] INFO: Loaded 3 players from duckhunt.json
[2025-09-11 18:30:40,420] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-11 18:30:40,579] INFO: Connected successfully!
[2025-09-11 18:30:40,579] INFO: Registering as DuckHuntBot
[2025-09-11 18:30:41,067] INFO: Successfully registered!
[2025-09-11 18:30:41,067] INFO: Joining #colby
[2025-09-11 18:30:41,118] INFO: Successfully joined #colby
[2025-09-11 18:30:41,582] INFO: Starting duck spawning...
[2025-09-11 18:30:46,583] INFO: Admin spawned normal duck 965d7945 in #colby
[2025-09-11 18:30:46,583] INFO: Waiting 56m 37s for next duck
[2025-09-11 18:31:46,591] INFO: Duck 965d7945 timed out in #colby
[2025-09-11 18:38:33,894] INFO: Received SIGINT, initiating graceful shutdown...
[2025-09-11 18:38:34,097] INFO: Shutdown requested, stopping all tasks...
[2025-09-11 18:38:34,097] INFO: Starting cleanup process...
[2025-09-11 18:38:35,211] INFO: IRC connection closed
[2025-09-11 18:38:35,225] INFO: Final database save completed - 3 players saved
[2025-09-11 18:38:35,226] INFO: Cleanup completed successfully
[2025-09-11 18:38:35,234] INFO: DuckHunt Bot shutdown complete
[2025-09-11 18:38:53,536] INFO: Loaded 3 players from duckhunt.json
[2025-09-11 18:38:53,536] INFO: DuckHunt Bot initializing...
[2025-09-11 18:38:53,537] INFO: Starting DuckHunt Bot...
[2025-09-11 18:38:53,537] INFO: Loaded 3 players from duckhunt.json
[2025-09-11 18:38:53,607] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-11 18:38:53,785] INFO: Connected successfully!
[2025-09-11 18:38:53,785] INFO: SASL authentication enabled
[2025-09-11 18:38:54,162] INFO: SASL capability available
[2025-09-11 18:38:54,221] INFO: SASL capability acknowledged
[2025-09-11 18:38:54,221] INFO: Authenticating via SASL as duckhunt
[2025-09-11 18:38:54,645] INFO: Server ready for SASL authentication
[2025-09-11 18:38:54,645] ERROR: SASL authentication failed!
[2025-09-11 18:38:54,645] INFO: Registering as DuckHunt
[2025-09-11 18:39:27,102] WARNING: Connection closed by server
[2025-09-11 18:39:27,103] WARNING: A main task completed unexpectedly
[2025-09-11 18:39:27,103] INFO: Starting cleanup process...
[2025-09-11 18:39:27,105] INFO: Final database save completed - 3 players saved
[2025-09-11 18:39:27,105] INFO: Cleanup completed successfully
[2025-09-11 18:39:27,106] INFO: DuckHunt Bot shutdown complete
[2025-09-11 18:41:03,279] INFO: Loaded 3 players from duckhunt.json
[2025-09-11 18:41:03,279] INFO: DuckHunt Bot initializing...
[2025-09-11 18:41:03,280] INFO: Starting DuckHunt Bot...
[2025-09-11 18:41:03,280] INFO: Loaded 3 players from duckhunt.json
[2025-09-11 18:41:03,354] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-11 18:41:03,516] INFO: Connected successfully!
[2025-09-11 18:41:03,517] INFO: SASL authentication enabled
[2025-09-11 18:41:03,611] INFO: SASL capability available
[2025-09-11 18:41:03,660] INFO: SASL capability acknowledged
[2025-09-11 18:41:03,660] INFO: Authenticating via SASL as duckhunt
[2025-09-11 18:41:04,075] INFO: Server ready for SASL authentication
[2025-09-11 18:41:04,076] ERROR: SASL authentication failed! (904 - Invalid credentials or account not found)
[2025-09-11 18:41:04,076] ERROR: Attempted username: duckhunt
[2025-09-11 18:41:04,076] INFO: Registering as DuckHunt
[2025-09-11 18:41:36,030] WARNING: Connection closed by server
[2025-09-11 18:41:36,031] WARNING: A main task completed unexpectedly
[2025-09-11 18:41:36,031] INFO: Starting cleanup process...
[2025-09-11 18:41:36,032] INFO: Final database save completed - 3 players saved
[2025-09-11 18:41:36,032] INFO: Cleanup completed successfully
[2025-09-11 18:41:36,033] INFO: DuckHunt Bot shutdown complete
[2025-09-11 17:52:42,777] INFO: Loaded 3 players from duckhunt.json
[2025-09-11 17:52:42,778] INFO: DuckHunt Bot initializing...
[2025-09-11 17:52:42,778] INFO: Starting DuckHunt Bot...
[2025-09-11 17:52:42,778] INFO: Loaded 3 players from duckhunt.json
[2025-09-11 17:52:42,800] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-11 17:52:42,914] INFO: Connected successfully!
[2025-09-11 17:52:42,914] INFO: SASL authentication enabled
[2025-09-11 17:52:42,926] INFO: SASL capability available
[2025-09-11 17:52:42,932] INFO: SASL capability acknowledged
[2025-09-11 17:52:42,932] INFO: Authenticating via SASL as duckhunt
[2025-09-11 17:52:43,305] INFO: Server ready for SASL authentication
[2025-09-11 17:52:43,305] ERROR: SASL authentication failed! (904 - Invalid credentials or account not found)
[2025-09-11 17:52:43,305] INFO: Falling back to NickServ identification...
[2025-09-11 17:52:43,306] ERROR: Attempted username: duckhunt
[2025-09-11 17:52:43,306] INFO: Registering as DuckHunt
[2025-09-11 17:52:43,357] ERROR: SASL authentication aborted! (906)
[2025-09-11 17:52:43,357] INFO: Falling back to NickServ identification...
[2025-09-11 17:52:43,358] INFO: Registering as DuckHunt
[2025-09-11 17:52:43,358] INFO: Successfully registered!
[2025-09-11 17:52:43,358] INFO: Attempting NickServ identification for duckhunt
[2025-09-11 17:52:44,359] INFO: NickServ identification commands sent
[2025-09-11 17:52:44,360] INFO: Joining #computertech
[2025-09-11 17:52:44,366] INFO: Successfully joined #computertech
[2025-09-11 17:52:44,917] INFO: Starting duck spawning...
[2025-09-11 17:52:49,920] INFO: Admin spawned normal duck 2afda3aa in #computertech
[2025-09-11 17:52:49,920] INFO: Waiting 47m 46s for next duck
[2025-09-11 17:53:11,783] INFO: Admin spawned normal duck abbfac62 in #computertech
[2025-09-11 17:54:10,655] INFO: Received SIGINT, initiating graceful shutdown...
[2025-09-11 17:54:11,044] INFO: Duck spawning stopped due to shutdown request
[2025-09-11 17:54:11,045] INFO: Shutdown requested, stopping all tasks...
[2025-09-11 17:54:11,045] INFO: Starting cleanup process...
[2025-09-11 17:54:12,149] INFO: IRC connection closed
[2025-09-11 17:54:12,154] INFO: Final database save completed - 6 players saved
[2025-09-11 17:54:12,154] INFO: Cleanup completed successfully
[2025-09-11 17:54:12,156] INFO: DuckHunt Bot shutdown complete
[2025-09-11 17:54:28,757] INFO: Loaded 6 players from duckhunt.json
[2025-09-11 17:54:28,757] INFO: DuckHunt Bot initializing...
[2025-09-11 17:54:28,757] INFO: Starting DuckHunt Bot...
[2025-09-11 17:54:28,757] INFO: Loaded 6 players from duckhunt.json
[2025-09-11 17:54:28,780] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-11 17:54:28,894] INFO: Connected successfully!
[2025-09-11 17:54:28,894] INFO: SASL authentication enabled
[2025-09-11 17:54:28,906] INFO: SASL capability available
[2025-09-11 17:54:28,913] INFO: SASL capability acknowledged
[2025-09-11 17:54:28,913] INFO: Authenticating via SASL as duckhunt
[2025-09-11 17:54:29,288] INFO: Server ready for SASL authentication
[2025-09-11 17:54:29,289] ERROR: SASL authentication failed! (904 - Invalid credentials or account not found)
[2025-09-11 17:54:29,289] INFO: Falling back to NickServ identification...
[2025-09-11 17:54:29,289] ERROR: Attempted username: duckhunt
[2025-09-11 17:54:29,289] INFO: Registering as DuckHunt
[2025-09-11 17:54:29,302] ERROR: SASL authentication aborted! (906)
[2025-09-11 17:54:29,303] INFO: Falling back to NickServ identification...
[2025-09-11 17:54:29,303] INFO: Registering as DuckHunt
[2025-09-11 17:54:29,303] INFO: Successfully registered!
[2025-09-11 17:54:29,303] INFO: Attempting NickServ identification for duckhunt
[2025-09-11 17:54:30,304] INFO: NickServ identification commands sent
[2025-09-11 17:54:30,305] INFO: Joining #computertech
[2025-09-11 17:54:30,311] INFO: Successfully joined #computertech
[2025-09-11 17:54:30,898] INFO: Starting duck spawning...
[2025-09-11 17:54:35,900] INFO: Admin spawned normal duck ff2612cf in #computertech
[2025-09-11 17:54:35,901] INFO: Waiting 41m 7s for next duck
[2025-09-11 17:55:55,911] INFO: Duck ff2612cf timed out in #computertech
[2025-09-11 18:10:31,079] INFO: Admin spawned normal duck b7398aed in #computertech
[2025-09-11 18:35:46,651] INFO: Admin spawned normal duck ae21e5f4 in #computertech
[2025-09-11 18:35:46,651] INFO: Waiting 30m 29s for next duck
[2025-09-11 19:06:18,504] INFO: Admin spawned normal duck 79032e28 in #computertech
[2025-09-11 19:06:18,505] INFO: Waiting 37m 53s for next duck
[2025-09-11 19:44:15,288] INFO: Admin spawned normal duck 02fe65b6 in #computertech
[2025-09-11 19:44:15,288] INFO: Waiting 39m 22s for next duck
[2025-09-11 20:23:41,185] INFO: Admin spawned normal duck 829273ae in #computertech
[2025-09-11 20:23:41,186] INFO: Waiting 47m 31s for next duck
[2025-09-11 20:24:57,093] INFO: Duck 829273ae timed out in #computertech
[2025-09-11 21:11:16,779] INFO: Admin spawned normal duck 8298e88d in #computertech
[2025-09-11 21:11:16,779] INFO: Waiting 75m 48s for next duck
[2025-09-11 22:27:12,035] INFO: Admin spawned normal duck ef5755f3 in #computertech
[2025-09-11 22:27:12,035] INFO: Waiting 55m 57s for next duck
[2025-09-11 23:23:14,339] INFO: Admin spawned normal duck 9725ad5b in #computertech
[2025-09-11 23:23:14,339] INFO: Waiting 57m 47s for next duck
[2025-09-12 00:21:06,885] INFO: Admin spawned normal duck 62b88d87 in #computertech
[2025-09-12 00:21:06,885] INFO: Waiting 73m 47s for next duck
[2025-09-12 01:35:00,951] INFO: Admin spawned normal duck 3f7dc294 in #computertech
[2025-09-12 01:35:00,951] INFO: Waiting 84m 10s for next duck
[2025-09-12 01:36:09,586] INFO: Duck 3f7dc294 timed out in #computertech
[2025-09-12 02:59:19,153] INFO: Admin spawned normal duck 45e3ab57 in #computertech
[2025-09-12 02:59:19,154] INFO: Waiting 60m 35s for next duck
[2025-09-12 02:59:25,289] WARNING: A main task completed unexpectedly
[2025-09-12 02:59:25,289] INFO: Starting cleanup process...
[2025-09-12 02:59:26,394] INFO: IRC connection closed
[2025-09-12 02:59:26,400] INFO: Final database save completed - 16 players saved
[2025-09-12 02:59:26,400] INFO: Cleanup completed successfully
[2025-09-12 02:59:26,401] INFO: DuckHunt Bot shutdown complete
[2025-09-12 13:45:28,730] INFO: Loaded 16 players from duckhunt.json
[2025-09-12 13:45:28,730] INFO: DuckHunt Bot initializing...
[2025-09-12 13:45:28,730] INFO: Starting DuckHunt Bot...
[2025-09-12 13:45:28,731] INFO: Loaded 16 players from duckhunt.json
[2025-09-12 13:45:28,753] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-12 13:45:28,865] INFO: Connected successfully!
[2025-09-12 13:45:28,865] INFO: SASL authentication enabled
[2025-09-12 13:45:30,015] INFO: SASL capability available
[2025-09-12 13:45:30,022] INFO: SASL capability acknowledged
[2025-09-12 13:45:30,022] INFO: Authenticating via SASL as duckhunt
[2025-09-12 13:45:30,389] INFO: Server ready for SASL authentication
[2025-09-12 13:45:30,389] ERROR: SASL authentication failed! (904 - Invalid credentials or account not found)
[2025-09-12 13:45:30,389] INFO: Falling back to NickServ identification...
[2025-09-12 13:45:30,389] ERROR: Attempted username: duckhunt
[2025-09-12 13:45:30,389] INFO: Registering as DuckHunt
[2025-09-12 13:45:30,402] ERROR: SASL authentication aborted! (906)
[2025-09-12 13:45:30,402] INFO: Falling back to NickServ identification...
[2025-09-12 13:45:30,403] INFO: Registering as DuckHunt
[2025-09-12 13:45:30,403] INFO: Successfully registered!
[2025-09-12 13:45:30,403] INFO: Attempting NickServ identification for duckhunt
[2025-09-12 13:45:31,406] INFO: NickServ identification commands sent
[2025-09-12 13:45:31,406] INFO: Joining #computertech
[2025-09-12 13:45:31,413] INFO: Successfully joined #computertech
[2025-09-12 13:45:31,870] INFO: Starting duck spawning...
[2025-09-12 13:45:36,872] INFO: Admin spawned normal duck 8e703ce0 in #computertech
[2025-09-12 13:45:36,872] INFO: Waiting 64m 21s for next duck
[2025-09-12 13:45:46,370] WARNING: A main task completed unexpectedly
[2025-09-12 13:45:46,370] INFO: Starting cleanup process...
[2025-09-12 13:45:47,474] INFO: IRC connection closed
[2025-09-12 13:45:47,479] INFO: Final database save completed - 16 players saved
[2025-09-12 13:45:47,479] INFO: Cleanup completed successfully
[2025-09-12 13:45:47,480] INFO: DuckHunt Bot shutdown complete
[2025-09-12 15:02:57,578] INFO: Loaded 16 players from duckhunt.json
[2025-09-12 15:02:57,578] INFO: DuckHunt Bot initializing...
[2025-09-12 15:02:57,578] INFO: Starting DuckHunt Bot...
[2025-09-12 15:02:57,578] INFO: Loaded 16 players from duckhunt.json
[2025-09-12 15:02:57,601] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-12 15:02:57,725] INFO: Connected successfully!
[2025-09-12 15:02:57,726] INFO: SASL authentication enabled
[2025-09-12 15:02:57,738] INFO: SASL capability available
[2025-09-12 15:02:57,745] INFO: SASL capability acknowledged
[2025-09-12 15:02:57,745] INFO: Authenticating via SASL as duckhunt
[2025-09-12 15:02:58,113] INFO: Server ready for SASL authentication
[2025-09-12 15:02:58,113] ERROR: SASL authentication failed! (904 - Invalid credentials or account not found)
[2025-09-12 15:02:58,113] INFO: Falling back to NickServ identification...
[2025-09-12 15:02:58,113] ERROR: Attempted username: duckhunt
[2025-09-12 15:02:58,114] INFO: Registering as DuckHunt
[2025-09-12 15:02:58,172] ERROR: SASL authentication aborted! (906)
[2025-09-12 15:02:58,173] INFO: Falling back to NickServ identification...
[2025-09-12 15:02:58,173] INFO: Registering as DuckHunt
[2025-09-12 15:02:58,173] INFO: Successfully registered!
[2025-09-12 15:02:58,174] INFO: Attempting NickServ identification for duckhunt
[2025-09-12 15:02:59,176] INFO: NickServ identification commands sent
[2025-09-12 15:02:59,176] INFO: Joining #computertech
[2025-09-12 15:02:59,183] INFO: Successfully joined #computertech
[2025-09-12 15:02:59,728] INFO: Starting duck spawning...
[2025-09-12 15:03:04,730] INFO: Admin spawned normal duck 3e922056 in #computertech
[2025-09-12 15:03:04,731] INFO: Waiting 51m 22s for next duck
[2025-09-12 15:03:12,954] WARNING: A main task completed unexpectedly
[2025-09-12 15:03:12,954] INFO: Starting cleanup process...
[2025-09-12 15:03:14,059] INFO: IRC connection closed
[2025-09-12 15:03:14,062] INFO: Final database save completed - 16 players saved
[2025-09-12 15:03:14,063] INFO: Cleanup completed successfully
[2025-09-12 15:03:14,063] INFO: DuckHunt Bot shutdown complete
[2025-09-12 18:34:46,067] INFO: Loaded 16 players from duckhunt.json
[2025-09-12 18:34:46,067] INFO: DuckHunt Bot initializing...
[2025-09-12 18:34:46,067] INFO: Starting DuckHunt Bot...
[2025-09-12 18:34:46,067] INFO: Loaded 16 players from duckhunt.json
[2025-09-12 18:34:46,089] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-12 18:34:46,191] INFO: Connected successfully!
[2025-09-12 18:34:46,191] INFO: SASL authentication enabled
[2025-09-12 18:34:47,098] INFO: SASL capability available
[2025-09-12 18:34:47,105] INFO: SASL capability acknowledged
[2025-09-12 18:34:47,105] INFO: Authenticating via SASL as duckhunt
[2025-09-12 18:34:47,472] INFO: Server ready for SASL authentication
[2025-09-12 18:34:47,472] ERROR: SASL authentication failed! (904 - Invalid credentials or account not found)
[2025-09-12 18:34:47,473] INFO: Falling back to NickServ identification...
[2025-09-12 18:34:47,473] ERROR: Attempted username: duckhunt
[2025-09-12 18:34:47,474] INFO: Registering as DuckHunt
[2025-09-12 18:35:13,492] INFO: Received SIGINT, initiating graceful shutdown...
[2025-09-12 18:35:13,528] INFO: Shutdown requested, stopping all tasks...
[2025-09-12 18:35:13,528] INFO: Starting cleanup process...
[2025-09-12 18:35:14,531] INFO: IRC connection closed
[2025-09-12 18:35:14,532] INFO: Final database save completed - 16 players saved
[2025-09-12 18:35:14,532] INFO: Cleanup completed successfully
[2025-09-12 18:35:14,532] INFO: DuckHunt Bot shutdown complete
[2025-09-12 18:35:15,124] INFO: Loaded 16 players from duckhunt.json
[2025-09-12 18:35:15,124] INFO: DuckHunt Bot initializing...
[2025-09-12 18:35:15,124] INFO: Starting DuckHunt Bot...
[2025-09-12 18:35:15,125] INFO: Loaded 16 players from duckhunt.json
[2025-09-12 18:35:15,147] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-12 18:35:15,277] INFO: Connected successfully!
[2025-09-12 18:35:15,277] INFO: SASL authentication enabled
[2025-09-12 18:35:15,289] INFO: SASL capability available
[2025-09-12 18:35:15,295] INFO: SASL capability acknowledged
[2025-09-12 18:35:15,295] INFO: Authenticating via SASL as duckhunt
[2025-09-12 18:35:15,662] INFO: Server ready for SASL authentication
[2025-09-12 18:35:15,663] ERROR: SASL authentication failed! (904 - Invalid credentials or account not found)
[2025-09-12 18:35:15,663] INFO: Falling back to NickServ identification...
[2025-09-12 18:35:15,663] ERROR: Attempted username: duckhunt
[2025-09-12 18:35:15,663] INFO: Registering as DuckHunt
[2025-09-12 18:35:15,676] ERROR: SASL authentication aborted! (906)
[2025-09-12 18:35:15,677] INFO: Falling back to NickServ identification...
[2025-09-12 18:35:15,677] INFO: Registering as DuckHunt
[2025-09-12 18:35:15,677] INFO: Successfully registered!
[2025-09-12 18:35:15,677] INFO: Attempting NickServ identification for duckhunt
[2025-09-12 18:35:16,679] INFO: NickServ identification commands sent
[2025-09-12 18:35:16,679] INFO: Joining #computertech
[2025-09-12 18:35:16,686] INFO: Successfully joined #computertech
[2025-09-12 18:35:17,281] INFO: Starting duck spawning...
[2025-09-12 18:35:22,283] INFO: Admin spawned normal duck 76766050 in #computertech
[2025-09-12 18:35:22,283] INFO: Waiting 41m 31s for next duck
[2025-09-12 18:35:37,207] INFO: Received SIGINT, initiating graceful shutdown...
[2025-09-12 18:35:37,308] INFO: Duck spawning stopped due to shutdown request
[2025-09-12 18:35:37,308] INFO: Shutdown requested, stopping all tasks...
[2025-09-12 18:35:37,309] INFO: Starting cleanup process...
[2025-09-12 18:35:38,414] INFO: IRC connection closed
[2025-09-12 18:35:38,420] INFO: Final database save completed - 17 players saved
[2025-09-12 18:35:38,420] INFO: Cleanup completed successfully
[2025-09-12 18:35:38,420] INFO: DuckHunt Bot shutdown complete
[2025-09-12 18:37:36,458] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 18:37:36,458] INFO: DuckHunt Bot initializing...
[2025-09-12 18:37:36,458] INFO: Starting DuckHunt Bot...
[2025-09-12 18:37:36,458] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 18:37:36,481] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-12 18:37:36,589] INFO: Connected successfully!
[2025-09-12 18:37:36,589] INFO: SASL authentication enabled
[2025-09-12 18:37:36,601] INFO: SASL capability available
[2025-09-12 18:37:36,608] INFO: SASL capability acknowledged
[2025-09-12 18:37:36,608] INFO: Authenticating via SASL as duckhunt
[2025-09-12 18:37:36,975] INFO: Server ready for SASL authentication
[2025-09-12 18:37:36,976] ERROR: SASL authentication failed! (904 - Invalid credentials or account not found)
[2025-09-12 18:37:36,976] INFO: Falling back to NickServ identification...
[2025-09-12 18:37:36,976] ERROR: Attempted username: duckhunt
[2025-09-12 18:37:36,976] INFO: Registering as DuckHunt
[2025-09-12 18:37:36,990] ERROR: SASL authentication aborted! (906)
[2025-09-12 18:37:36,990] INFO: Falling back to NickServ identification...
[2025-09-12 18:37:36,990] INFO: Registering as DuckHunt
[2025-09-12 18:37:36,991] INFO: Successfully registered!
[2025-09-12 18:37:36,991] INFO: Attempting NickServ identification for duckhunt
[2025-09-12 18:37:37,770] INFO: Received SIGINT, initiating graceful shutdown...
[2025-09-12 18:37:37,993] INFO: NickServ identification commands sent
[2025-09-12 18:37:37,994] INFO: Joining #computertech
[2025-09-12 18:37:37,994] INFO: Shutdown requested, stopping all tasks...
[2025-09-12 18:37:37,994] INFO: Starting cleanup process...
[2025-09-12 18:37:38,999] INFO: IRC connection closed
[2025-09-12 18:37:39,004] INFO: Final database save completed - 17 players saved
[2025-09-12 18:37:39,004] INFO: Cleanup completed successfully
[2025-09-12 18:37:39,005] INFO: DuckHunt Bot shutdown complete
[2025-09-12 19:48:20,215] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 19:50:01,514] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 19:50:01,514] INFO: DuckHunt Bot initializing...
[2025-09-12 19:50:01,515] INFO: Starting DuckHunt Bot...
[2025-09-12 19:50:01,516] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 19:50:01,587] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-12 19:50:01,776] INFO: Connected successfully!
[2025-09-12 19:50:02,946] INFO: Registering as DuckHunt
[2025-09-12 19:50:03,077] INFO: Successfully registered! (SASL authenticated)
[2025-09-12 19:50:03,078] INFO: Joining #colby
[2025-09-12 19:50:03,146] INFO: Successfully joined #colby
[2025-09-12 19:50:03,780] INFO: Starting duck spawning...
[2025-09-12 19:50:08,782] INFO: Admin spawned normal duck 89470db7 in #colby
[2025-09-12 19:50:08,783] INFO: Waiting 72m 24s for next duck
[2025-09-12 19:50:35,497] INFO: Received SIGINT, initiating graceful shutdown...
[2025-09-12 19:50:35,810] INFO: Duck spawning stopped due to shutdown request
[2025-09-12 19:50:35,810] INFO: Shutdown requested, stopping all tasks...
[2025-09-12 19:50:35,811] INFO: Starting cleanup process...
[2025-09-12 19:50:36,916] INFO: IRC connection closed
[2025-09-12 19:50:36,922] INFO: Final database save completed - 17 players saved
[2025-09-12 19:50:36,922] INFO: Cleanup completed successfully
[2025-09-12 19:50:36,925] INFO: DuckHunt Bot shutdown complete
[2025-09-12 19:50:38,060] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 19:50:38,060] INFO: DuckHunt Bot initializing...
[2025-09-12 19:50:38,061] INFO: Starting DuckHunt Bot...
[2025-09-12 19:50:38,062] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 19:50:38,126] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-12 19:50:38,317] INFO: Connected successfully!
[2025-09-12 19:50:39,335] INFO: Registering as DuckHunt
[2025-09-12 19:50:39,456] INFO: Successfully registered! (SASL authenticated)
[2025-09-12 19:50:39,456] INFO: Joining #colby
[2025-09-12 19:50:39,519] INFO: Successfully joined #colby
[2025-09-12 19:50:40,323] INFO: Starting duck spawning...
[2025-09-12 19:50:45,326] INFO: Admin spawned normal duck 1399472e in #colby
[2025-09-12 19:50:45,326] INFO: Waiting 89m 7s for next duck
[2025-09-12 19:50:58,428] INFO: Admin spawned normal duck 7278ccc5 in #colby
[2025-09-12 19:51:03,718] INFO: Admin spawned normal duck b79d4c60 in #colby
[2025-09-12 19:51:04,973] INFO: Admin spawned normal duck f16535b2 in #colby
[2025-09-12 19:51:06,296] INFO: Admin spawned normal duck 287c11e5 in #colby
[2025-09-12 19:51:07,607] INFO: Admin spawned normal duck 87d9f58d in #colby
[2025-09-12 19:55:27,299] INFO: Received SIGINT, initiating graceful shutdown...
[2025-09-12 19:55:27,760] INFO: Duck spawning stopped due to shutdown request
[2025-09-12 19:55:27,764] INFO: Shutdown requested, stopping all tasks...
[2025-09-12 19:55:27,766] INFO: Starting cleanup process...
[2025-09-12 19:55:28,894] INFO: IRC connection closed
[2025-09-12 19:55:28,907] INFO: Final database save completed - 17 players saved
[2025-09-12 19:55:28,908] INFO: Cleanup completed successfully
[2025-09-12 19:55:28,925] INFO: DuckHunt Bot shutdown complete
[2025-09-12 20:09:21,565] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 20:09:21,568] INFO: DuckHunt Bot initializing...
[2025-09-12 20:09:21,568] INFO: Starting DuckHunt Bot...
[2025-09-12 20:09:21,569] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 20:09:21,648] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-12 20:09:21,817] INFO: Connected successfully!
[2025-09-12 20:09:22,784] INFO: Registering as DuckHunt
[2025-09-12 20:09:22,880] INFO: Successfully registered! (SASL authenticated)
[2025-09-12 20:09:22,881] INFO: Joining #computertech
[2025-09-12 20:09:22,937] INFO: Successfully joined #computertech
[2025-09-12 20:09:23,822] INFO: Starting duck spawning...
[2025-09-12 20:09:28,824] INFO: Admin spawned normal duck 3d18761a in #computertech
[2025-09-12 20:09:28,825] INFO: Waiting 43m 4s for next duck
[2025-09-12 20:10:31,986] INFO: Received SIGINT, initiating graceful shutdown...
[2025-09-12 20:10:32,701] INFO: Shutdown requested, stopping all tasks...
[2025-09-12 20:10:32,702] INFO: Starting cleanup process...
[2025-09-12 20:10:33,810] INFO: IRC connection closed
[2025-09-12 20:10:33,819] INFO: Final database save completed - 17 players saved
[2025-09-12 20:10:33,819] INFO: Cleanup completed successfully
[2025-09-12 20:10:33,823] INFO: DuckHunt Bot shutdown complete
[2025-09-12 20:10:34,241] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 20:10:34,242] INFO: DuckHunt Bot initializing...
[2025-09-12 20:10:34,243] INFO: Starting DuckHunt Bot...
[2025-09-12 20:10:34,244] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 20:10:34,365] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-12 20:10:34,541] INFO: Connected successfully!
[2025-09-12 20:10:36,475] INFO: Registering as DuckHunt
[2025-09-12 20:10:36,594] INFO: Successfully registered! (SASL authenticated)
[2025-09-12 20:10:36,597] INFO: Joining #computertech
[2025-09-12 20:10:36,665] INFO: Successfully joined #computertech
[2025-09-12 20:10:37,545] INFO: Starting duck spawning...
[2025-09-12 20:10:42,548] INFO: Admin spawned normal duck 11290de8 in #computertech
[2025-09-12 20:10:42,549] INFO: Waiting 74m 3s for next duck
[2025-09-12 20:10:53,126] WARNING: A main task completed unexpectedly
[2025-09-12 20:10:53,127] INFO: Starting cleanup process...
[2025-09-12 20:10:54,233] INFO: IRC connection closed
[2025-09-12 20:10:54,244] INFO: Final database save completed - 17 players saved
[2025-09-12 20:10:54,245] INFO: Cleanup completed successfully
[2025-09-12 20:10:54,248] INFO: DuckHunt Bot shutdown complete
[2025-09-12 20:11:53,511] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 20:11:53,512] INFO: DuckHunt Bot initializing...
[2025-09-12 20:11:53,512] INFO: Starting DuckHunt Bot...
[2025-09-12 20:11:53,513] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 20:11:53,599] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-12 20:11:53,797] INFO: Connected successfully!
[2025-09-12 20:11:54,764] INFO: Registering as DuckHunt
[2025-09-12 20:11:54,865] INFO: Successfully registered! (SASL authenticated)
[2025-09-12 20:11:54,865] INFO: Joining #computertech
[2025-09-12 20:11:54,914] INFO: Successfully joined #computertech
[2025-09-12 20:11:55,800] INFO: Starting duck spawning...
[2025-09-12 20:12:00,802] INFO: Admin spawned normal duck cefbb956 in #computertech
[2025-09-12 20:12:00,803] INFO: Waiting 32m 48s for next duck
[2025-09-12 20:12:01,603] INFO: Admin spawned normal duck e19381c2 in #computertech
[2025-09-12 20:12:08,171] INFO: Admin spawned normal duck 642daf60 in #computertech
[2025-09-12 20:12:08,705] INFO: Admin spawned normal duck deb6cc88 in #computertech
[2025-09-12 20:12:32,287] INFO: Admin spawned golden duck f5c388c1 in #computertech
[2025-09-12 20:17:29,215] INFO: Received SIGINT, initiating graceful shutdown...
[2025-09-12 20:17:29,253] INFO: Duck spawning stopped due to shutdown request
[2025-09-12 20:17:29,254] INFO: Shutdown requested, stopping all tasks...
[2025-09-12 20:17:29,254] INFO: Starting cleanup process...
[2025-09-12 20:17:30,361] INFO: IRC connection closed
[2025-09-12 20:17:30,368] INFO: Final database save completed - 17 players saved
[2025-09-12 20:17:30,369] INFO: Cleanup completed successfully
[2025-09-12 20:17:30,372] INFO: DuckHunt Bot shutdown complete
[2025-09-12 20:17:31,507] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 20:17:31,508] INFO: DuckHunt Bot initializing...
[2025-09-12 20:17:31,509] INFO: Starting DuckHunt Bot...
[2025-09-12 20:17:31,511] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 20:17:31,600] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-12 20:17:31,788] INFO: Connected successfully!
[2025-09-12 20:17:32,919] INFO: Registering as DuckHunt
[2025-09-12 20:17:33,047] INFO: Successfully registered! (SASL authenticated)
[2025-09-12 20:17:33,048] INFO: Joining #computertech
[2025-09-12 20:17:33,116] INFO: Successfully joined #computertech
[2025-09-12 20:17:33,792] INFO: Starting duck spawning...
[2025-09-12 20:17:38,793] INFO: Admin spawned normal duck fdc26682 in #computertech
[2025-09-12 20:17:38,794] INFO: Waiting 77m 31s for next duck
[2025-09-12 20:18:12,540] INFO: Received SIGINT, initiating graceful shutdown...
[2025-09-12 20:18:12,858] INFO: Duck spawning stopped due to shutdown request
[2025-09-12 20:18:12,859] INFO: Shutdown requested, stopping all tasks...
[2025-09-12 20:18:12,859] INFO: Starting cleanup process...
[2025-09-12 20:18:13,964] INFO: IRC connection closed
[2025-09-12 20:18:13,970] INFO: Final database save completed - 17 players saved
[2025-09-12 20:18:13,971] INFO: Cleanup completed successfully
[2025-09-12 20:18:13,975] INFO: DuckHunt Bot shutdown complete
[2025-09-12 20:18:14,525] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 20:18:14,527] INFO: DuckHunt Bot initializing...
[2025-09-12 20:18:14,528] INFO: Starting DuckHunt Bot...
[2025-09-12 20:18:14,531] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 20:18:14,636] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-12 20:18:14,805] INFO: Connected successfully!
[2025-09-12 20:18:15,768] INFO: Registering as DuckHunt
[2025-09-12 20:18:15,871] INFO: Successfully registered! (SASL authenticated)
[2025-09-12 20:18:15,873] INFO: Joining #computertech
[2025-09-12 20:18:15,929] INFO: Successfully joined #computertech
[2025-09-12 20:18:16,123] INFO: Received SIGINT, initiating graceful shutdown...
[2025-09-12 20:18:16,129] INFO: Received SIGINT, initiating graceful shutdown...
[2025-09-12 20:18:16,147] INFO: Shutdown requested, stopping all tasks...
[2025-09-12 20:18:16,148] INFO: Starting cleanup process...
[2025-09-12 20:18:17,115] INFO: Received SIGTERM, initiating graceful shutdown...
[2025-09-12 20:18:17,268] INFO: IRC connection closed
[2025-09-12 20:18:17,276] INFO: Final database save completed - 17 players saved
[2025-09-12 20:18:17,276] INFO: Cleanup completed successfully
[2025-09-12 20:18:17,290] INFO: DuckHunt Bot shutdown complete
[2025-09-12 20:18:23,251] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 20:18:23,252] INFO: DuckHunt Bot initializing...
[2025-09-12 20:18:23,253] INFO: Starting DuckHunt Bot...
[2025-09-12 20:18:23,255] INFO: Loaded 17 players from duckhunt.json
[2025-09-12 20:18:23,349] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-12 20:18:23,549] INFO: Connected successfully!
[2025-09-12 20:18:24,557] INFO: Registering as DuckHunt
[2025-09-12 20:18:24,676] INFO: Successfully registered! (SASL authenticated)
[2025-09-12 20:18:24,677] INFO: Joining #computertech
[2025-09-12 20:18:24,736] INFO: Successfully joined #computertech
[2025-09-12 20:18:25,554] INFO: Starting duck spawning...
[2025-09-12 20:18:30,556] INFO: Admin spawned normal duck 083638bd in #computertech
[2025-09-12 20:18:30,557] INFO: Waiting 38m 8s for next duck
[2025-09-12 20:18:43,399] INFO: Admin spawned normal duck 777c5080 in #computertech
[2025-09-12 20:18:43,910] INFO: Admin spawned normal duck 0e36368a in #computertech
[2025-09-12 20:18:44,307] INFO: Admin spawned normal duck 0dbf0209 in #computertech
[2025-09-12 20:18:44,693] INFO: Admin spawned normal duck 224f9870 in #computertech
[2025-09-12 20:18:45,055] INFO: Admin spawned normal duck 96c7a18d in #computertech
[2025-09-12 20:18:45,659] INFO: Admin spawned normal duck f881fa3a in #computertech
[2025-09-12 20:29:51,955] INFO: Received SIGINT, initiating graceful shutdown...
[2025-09-12 20:29:52,036] INFO: Shutdown requested, stopping all tasks...
[2025-09-12 20:29:52,036] INFO: Starting cleanup process...
[2025-09-12 20:29:53,159] INFO: IRC connection closed
[2025-09-12 20:29:53,169] INFO: Final database save completed - 18 players saved
[2025-09-12 20:29:53,170] INFO: Cleanup completed successfully
[2025-09-12 20:29:53,181] INFO: DuckHunt Bot shutdown complete
[2025-09-12 20:29:54,600] INFO: Loaded 18 players from duckhunt.json
[2025-09-12 20:29:54,601] INFO: DuckHunt Bot initializing...
[2025-09-12 20:29:54,601] INFO: Starting DuckHunt Bot...
[2025-09-12 20:29:54,604] INFO: Loaded 18 players from duckhunt.json
[2025-09-12 20:29:54,699] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-12 20:29:54,874] INFO: Connected successfully!
[2025-09-12 20:29:55,842] INFO: Registering as DuckHunt
[2025-09-12 20:29:55,941] INFO: Successfully registered! (SASL authenticated)
[2025-09-12 20:29:55,941] INFO: Joining #computertech
[2025-09-12 20:29:55,991] INFO: Successfully joined #computertech
[2025-09-12 20:29:56,877] INFO: Starting duck spawning...
[2025-09-12 20:30:01,878] INFO: Admin spawned golden duck f7373d01 in #computertech
[2025-09-12 20:30:01,878] INFO: Waiting 49m 50s for next duck
[2025-09-12 20:31:39,076] INFO: Received SIGINT, initiating graceful shutdown...
[2025-09-12 20:31:39,717] INFO: Shutdown requested, stopping all tasks...
[2025-09-12 20:31:39,718] INFO: Starting cleanup process...
[2025-09-12 20:31:40,827] INFO: IRC connection closed
[2025-09-12 20:31:40,834] INFO: Final database save completed - 19 players saved
[2025-09-12 20:31:40,835] INFO: Cleanup completed successfully
[2025-09-12 20:31:40,838] INFO: DuckHunt Bot shutdown complete
[2025-09-12 20:31:42,386] INFO: Loaded 19 players from duckhunt.json
[2025-09-12 20:31:42,387] INFO: DuckHunt Bot initializing...
[2025-09-12 20:31:42,388] INFO: Starting DuckHunt Bot...
[2025-09-12 20:31:42,389] INFO: Loaded 19 players from duckhunt.json
[2025-09-12 20:31:42,504] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-12 20:31:42,737] INFO: Connected successfully!
[2025-09-12 20:31:43,914] INFO: Registering as DuckHunt
[2025-09-12 20:31:44,034] INFO: Successfully registered! (SASL authenticated)
[2025-09-12 20:31:44,034] INFO: Joining #computertech
[2025-09-12 20:31:44,097] INFO: Successfully joined #computertech
[2025-09-12 20:31:44,744] INFO: Starting duck spawning...
[2025-09-12 20:31:49,747] INFO: Admin spawned normal duck 30bc917d in #computertech
[2025-09-12 20:31:49,748] INFO: Waiting 44m 50s for next duck
[2025-09-12 20:45:42,441] INFO: Received SIGINT, initiating graceful shutdown...
[2025-09-12 20:45:42,988] INFO: Duck spawning stopped due to shutdown request
[2025-09-12 20:45:42,993] INFO: Shutdown requested, stopping all tasks...
[2025-09-12 20:45:43,000] INFO: Starting cleanup process...
[2025-09-12 20:45:44,197] INFO: IRC connection closed
[2025-09-12 20:45:44,234] INFO: Final database save completed - 19 players saved
[2025-09-12 20:45:44,235] INFO: Cleanup completed successfully
[2025-09-12 20:45:44,316] INFO: DuckHunt Bot shutdown complete
[2025-09-12 20:57:05,952] INFO: Loaded 19 players from duckhunt.json
[2025-09-12 20:57:05,954] INFO: DuckHunt Bot initializing...
[2025-09-12 20:57:05,955] INFO: Starting DuckHunt Bot...
[2025-09-12 20:57:05,955] INFO: Loaded 19 players from duckhunt.json
[2025-09-12 20:57:06,019] INFO: Connecting to irc.rizon.net:6697 (SSL: True)
[2025-09-12 20:57:06,197] INFO: Connected successfully!
[2025-09-12 20:57:07,157] INFO: Registering as DuckHunt
[2025-09-12 20:57:07,256] INFO: Successfully registered! (SASL authenticated)
[2025-09-12 20:57:07,256] INFO: Joining #computertech
[2025-09-12 20:57:07,307] INFO: Successfully joined #computertech
[2025-09-12 20:57:08,199] INFO: Starting duck spawning...
[2025-09-12 20:57:13,200] INFO: Admin spawned normal duck 77291a83 in #computertech
[2025-09-12 20:57:13,200] INFO: Waiting 49m 42s for next duck
[2025-09-12 20:57:25,869] INFO: Admin spawned normal duck 4a2ff363 in #computertech
[2025-09-12 20:57:26,569] INFO: Admin spawned normal duck f2ae2d52 in #computertech
[2025-09-12 20:57:39,277] INFO: Admin spawned golden duck 3ef1281f in #computertech
[2025-09-12 20:59:05,695] INFO: Received SIGINT, initiating graceful shutdown...
[2025-09-12 20:59:06,333] INFO: Duck spawning stopped due to shutdown request
[2025-09-12 20:59:06,334] INFO: Shutdown requested, stopping all tasks...
[2025-09-12 20:59:06,334] INFO: Starting cleanup process...
[2025-09-12 20:59:07,437] INFO: IRC connection closed
[2025-09-12 20:59:07,442] INFO: Final database save completed - 19 players saved
[2025-09-12 20:59:07,443] INFO: Cleanup completed successfully
[2025-09-12 20:59:07,445] INFO: DuckHunt Bot shutdown complete

37
duckhunt/duckhunt.py Normal file
View File

@@ -0,0 +1,37 @@
#!/usr/bin/env python3
"""
Main entry point for DuckHunt Bot
"""
import asyncio
import json
import sys
import os
# Add src directory to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
from src.duckhuntbot import IRCBot
def main():
try:
with open('config.json') as f:
config = json.load(f)
bot = IRCBot(config)
bot.logger.info("🦆 Starting DuckHunt Bot...")
# Run the bot
asyncio.run(bot.run())
except KeyboardInterrupt:
print("\n🛑 Bot stopped by user")
except FileNotFoundError:
print("❌ config.json not found!")
sys.exit(1)
except Exception as e:
print(f"❌ Error: {e}")
sys.exit(1)
if __name__ == '__main__':
main()

2243
duckhunt/simple_duckhunt.py Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

108
duckhunt/src/auth.py Normal file
View File

@@ -0,0 +1,108 @@
import hashlib
import secrets
import asyncio
from typing import Optional
from src.db import DuckDB
class AuthSystem:
def __init__(self, db):
self.db = db
self.bot = None # Will be set by the bot
self.authenticated_users = {} # nick -> account_name
self.pending_registrations = {} # nick -> temp_data
def hash_password(self, password: str, salt: Optional[str] = None) -> tuple:
if salt is None:
salt = secrets.token_hex(16)
hashed = hashlib.pbkdf2_hmac('sha256', password.encode(), salt.encode(), 100000)
return hashed.hex(), salt
def verify_password(self, password: str, hashed: str, salt: str) -> bool:
test_hash, _ = self.hash_password(password, salt)
return test_hash == hashed
def register_account(self, username: str, password: str, nick: str, hostmask: str) -> bool:
# Check if account exists
existing = self.db.load_account(username)
if existing:
return False
hashed_pw, salt = self.hash_password(password)
account_data = {
'username': username,
'password_hash': hashed_pw,
'salt': salt,
'primary_nick': nick,
'hostmask': hostmask,
'created_at': None, # Set by DB
'auth_method': 'password' # 'password', 'nickserv', 'hostmask'
}
self.db.save_account(username, account_data)
return True
def authenticate(self, username: str, password: str, nick: str) -> bool:
account = self.db.load_account(username)
if not account:
return False
if self.verify_password(password, account['password_hash'], account['salt']):
self.authenticated_users[nick] = username
return True
return False
def get_account_for_nick(self, nick: str) -> str:
return self.authenticated_users.get(nick, "")
def is_authenticated(self, nick: str) -> bool:
return nick in self.authenticated_users
def logout(self, nick: str):
if nick in self.authenticated_users:
del self.authenticated_users[nick]
def set_bot(self, bot):
"""Set the bot instance for sending messages"""
self.bot = bot
async def attempt_nickserv_auth(self):
"""Attempt NickServ identification as fallback"""
if not self.bot:
return
sasl_config = self.bot.config.get('sasl', {})
username = sasl_config.get('username', '')
password = sasl_config.get('password', '')
if username and password:
self.bot.logger.info(f"Attempting NickServ identification for {username}")
# Try both common NickServ commands
self.bot.send_raw(f'PRIVMSG NickServ :IDENTIFY {username} {password}')
# Some networks use just the password if nick matches
await asyncio.sleep(1)
self.bot.send_raw(f'PRIVMSG NickServ :IDENTIFY {password}')
self.bot.logger.info("NickServ identification commands sent")
else:
self.bot.logger.debug("No SASL credentials available for NickServ fallback")
async def handle_nickserv_response(self, message):
"""Handle responses from NickServ"""
if not self.bot:
return
message_lower = message.lower()
if any(phrase in message_lower for phrase in [
'you are now identified', 'password accepted', 'you are already identified',
'authentication successful', 'you have been identified'
]):
self.bot.logger.info("NickServ identification successful!")
elif any(phrase in message_lower for phrase in [
'invalid password', 'incorrect password', 'access denied',
'authentication failed', 'not registered', 'nickname is not registered'
]):
self.bot.logger.error(f"NickServ identification failed: {message}")
else:
self.bot.logger.debug(f"NickServ message: {message}")

97
duckhunt/src/db.py Normal file
View File

@@ -0,0 +1,97 @@
import sqlite3
import json
import datetime
class DuckDB:
def __init__(self, db_path='duckhunt.db'):
self.conn = sqlite3.connect(db_path)
self.create_tables()
def create_tables(self):
with self.conn:
# Player data table
self.conn.execute('''CREATE TABLE IF NOT EXISTS players (
nick TEXT PRIMARY KEY,
data TEXT
)''')
# Account system table
self.conn.execute('''CREATE TABLE IF NOT EXISTS accounts (
username TEXT PRIMARY KEY,
data TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)''')
# Leaderboards table
self.conn.execute('''CREATE TABLE IF NOT EXISTS leaderboard (
account TEXT,
stat_type TEXT,
value INTEGER,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (account, stat_type)
)''')
# Trading table
self.conn.execute('''CREATE TABLE IF NOT EXISTS trades (
id INTEGER PRIMARY KEY AUTOINCREMENT,
from_account TEXT,
to_account TEXT,
trade_data TEXT,
status TEXT DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)''')
def save_player(self, nick, data):
with self.conn:
self.conn.execute('''INSERT OR REPLACE INTO players (nick, data) VALUES (?, ?)''',
(nick, json.dumps(data)))
def load_player(self, nick):
cur = self.conn.cursor()
cur.execute('SELECT data FROM players WHERE nick=?', (nick,))
row = cur.fetchone()
return json.loads(row[0]) if row else None
def get_all_players(self):
cur = self.conn.cursor()
cur.execute('SELECT nick, data FROM players')
return {nick: json.loads(data) for nick, data in cur.fetchall()}
def save_account(self, username, data):
with self.conn:
self.conn.execute('''INSERT OR REPLACE INTO accounts (username, data) VALUES (?, ?)''',
(username, json.dumps(data)))
def load_account(self, username):
cur = self.conn.cursor()
cur.execute('SELECT data FROM accounts WHERE username=?', (username,))
row = cur.fetchone()
return json.loads(row[0]) if row else None
def update_leaderboard(self, account, stat_type, value):
with self.conn:
self.conn.execute('''INSERT OR REPLACE INTO leaderboard (account, stat_type, value) VALUES (?, ?, ?)''',
(account, stat_type, value))
def get_leaderboard(self, stat_type, limit=10):
cur = self.conn.cursor()
cur.execute('SELECT account, value FROM leaderboard WHERE stat_type=? ORDER BY value DESC LIMIT ?',
(stat_type, limit))
return cur.fetchall()
def save_trade(self, from_account, to_account, trade_data):
with self.conn:
cur = self.conn.cursor()
cur.execute('''INSERT INTO trades (from_account, to_account, trade_data) VALUES (?, ?, ?)''',
(from_account, to_account, json.dumps(trade_data)))
return cur.lastrowid
def get_pending_trades(self, account):
cur = self.conn.cursor()
cur.execute('''SELECT id, from_account, trade_data FROM trades
WHERE to_account=? AND status='pending' ''', (account,))
return [(trade_id, from_acc, json.loads(data)) for trade_id, from_acc, data in cur.fetchall()]
def complete_trade(self, trade_id):
with self.conn:
self.conn.execute('UPDATE trades SET status=? WHERE id=?', ('completed', trade_id))

277
duckhunt/src/duckhuntbot.py Normal file
View File

@@ -0,0 +1,277 @@
#!/usr/bin/env python3
"""
Main DuckHunt IRC Bot using modular architecture
"""
import asyncio
import ssl
import json
import random
import logging
import sys
import os
import time
import uuid
import signal
from typing import Optional
from .logging_utils import setup_logger
from .utils import parse_message
from .db import DuckDB
from .game import DuckGame
from .auth import AuthSystem
from . import sasl
class IRCBot:
def __init__(self, config):
self.config = config
self.logger = setup_logger("DuckHuntBot")
self.reader: Optional[asyncio.StreamReader] = None
self.writer: Optional[asyncio.StreamWriter] = None
self.registered = False
self.channels_joined = set()
self.shutdown_requested = False
self.running_tasks = set()
# Initialize subsystems
self.db = DuckDB()
self.game = DuckGame(self, self.db)
self.auth = AuthSystem(self.db)
self.auth.set_bot(self) # Set bot reference for auth system
self.sasl_handler = sasl.SASLHandler(self, config)
# IRC connection state
self.nick = config['nick']
self.channels = config['channels']
def send_raw(self, msg):
"""Send raw IRC message"""
if self.writer and not self.writer.is_closing():
try:
self.writer.write(f"{msg}\r\n".encode('utf-8'))
except Exception as e:
self.logger.error(f"Error sending message: {e}")
def send_message(self, target, msg):
"""Send PRIVMSG to target"""
self.send_raw(f'PRIVMSG {target} :{msg}')
async def connect(self):
"""Connect to IRC server with SASL support"""
server = self.config['server']
port = self.config['port']
ssl_context = ssl.create_default_context() if self.config.get('ssl', True) else None
self.logger.info(f"Connecting to {server}:{port} (SSL: {ssl_context is not None})")
try:
self.reader, self.writer = await asyncio.open_connection(
server, port, ssl=ssl_context
)
self.logger.info("Connected successfully!")
# Start SASL negotiation if enabled
if await self.sasl_handler.start_negotiation():
return True
else:
# Standard registration without SASL
await self.register_user()
return True
except Exception as e:
self.logger.error(f"Connection failed: {e}")
return False
async def register_user(self):
"""Register with IRC server"""
self.logger.info(f"Registering as {self.nick}")
self.send_raw(f'NICK {self.nick}')
self.send_raw(f'USER {self.nick} 0 * :DuckHunt Bot')
# Send password if configured (for servers that require it)
if self.config.get('password'):
self.send_raw(f'PASS {self.config["password"]}')
async def handle_irc_message(self, line):
"""Handle individual IRC message"""
try:
prefix, command, params, trailing = parse_message(line)
# Handle SASL-related messages
if command in ['CAP', 'AUTHENTICATE', '903', '904', '905', '906', '907', '908']:
handled = await self.sasl_handler.handle_sasl_result(command, params, trailing)
if command == 'CAP':
handled = await self.sasl_handler.handle_cap_response(params, trailing)
elif command == 'AUTHENTICATE':
handled = await self.sasl_handler.handle_authenticate_response(params)
# If SASL handler didn't handle it, continue with normal processing
if handled:
return
# Handle standard IRC messages
if command == '001': # Welcome
self.registered = True
auth_status = " (SASL authenticated)" if self.sasl_handler.is_authenticated() else ""
self.logger.info(f"Successfully registered!{auth_status}")
# If SASL failed, try NickServ identification
if not self.sasl_handler.is_authenticated():
await self.auth.attempt_nickserv_auth()
# Join channels
for chan in self.channels:
self.logger.info(f"Joining {chan}")
self.send_raw(f'JOIN {chan}')
elif command == 'JOIN' and prefix and prefix.startswith(self.nick):
channel = trailing or (params[0] if params else '')
if channel:
self.channels_joined.add(channel)
self.logger.info(f"Successfully joined {channel}")
elif command == 'PRIVMSG' and trailing:
target = params[0] if params else ''
sender = prefix.split('!')[0] if prefix else ''
# Handle NickServ responses
if sender.lower() == 'nickserv':
await self.auth.handle_nickserv_response(trailing)
elif trailing == 'VERSION':
self.send_raw(f'NOTICE {sender} :VERSION DuckHunt Bot v2.0')
else:
# Handle game commands
await self.game.handle_command(prefix, target, trailing)
elif command == 'PING':
# Respond to PING
self.send_raw(f'PONG :{trailing}')
except Exception as e:
self.logger.error(f"Error handling IRC message '{line}': {e}")
async def listen(self):
"""Main IRC message listening loop"""
buffer = ""
while not self.shutdown_requested:
try:
if not self.reader:
break
data = await self.reader.read(4096)
if not data:
self.logger.warning("Connection closed by server")
break
buffer += data.decode('utf-8', errors='ignore')
while '\n' in buffer:
line, buffer = buffer.split('\n', 1)
line = line.rstrip('\r')
if line:
await self.handle_irc_message(line)
except asyncio.CancelledError:
break
except Exception as e:
self.logger.error(f"Error in listen loop: {e}")
await asyncio.sleep(1)
def setup_signal_handlers(self):
"""Setup signal handlers for graceful shutdown"""
def signal_handler(signum, frame):
self.logger.info(f"Received signal {signum}, initiating graceful shutdown...")
self.shutdown_requested = True
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
async def cleanup(self):
"""Cleanup resources and save data"""
self.logger.info("Starting cleanup process...")
try:
# Cancel all running tasks
for task in self.running_tasks.copy():
if not task.done():
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
# Send goodbye message
if self.writer and not self.writer.is_closing():
for channel in self.channels_joined:
self.send_message(channel, "🦆 DuckHunt Bot shutting down. Thanks for playing! 🦆")
await asyncio.sleep(0.1)
self.send_raw('QUIT :DuckHunt Bot shutting down gracefully')
await asyncio.sleep(1.0)
self.writer.close()
await self.writer.wait_closed()
self.logger.info("IRC connection closed")
# Save database (no specific save_all method)
# Players are saved individually through the game engine
self.logger.info("Final database save completed")
self.logger.info("Cleanup completed successfully")
except Exception as e:
self.logger.error(f"Error during cleanup: {e}")
async def run(self):
"""Main bot entry point"""
try:
self.setup_signal_handlers()
self.logger.info("Starting DuckHunt Bot...")
# Load database (no async initialization needed)
# Database is initialized in constructor
# Connect to IRC
if not await self.connect():
return False
# Create main tasks
listen_task = asyncio.create_task(self.listen(), name="listen")
game_task = asyncio.create_task(self.game.spawn_ducks_loop(), name="duck_spawner")
self.running_tasks.add(listen_task)
self.running_tasks.add(game_task)
# Wait for completion
done, pending = await asyncio.wait(
[listen_task, game_task],
return_when=asyncio.FIRST_COMPLETED
)
if self.shutdown_requested:
self.logger.info("Shutdown requested, stopping all tasks...")
else:
self.logger.warning("A main task completed unexpectedly")
# Cancel remaining tasks
for task in pending:
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
except KeyboardInterrupt:
self.logger.info("Keyboard interrupt received")
self.shutdown_requested = True
except Exception as e:
self.logger.error(f"Fatal error in main loop: {e}")
import traceback
traceback.print_exc()
finally:
await self.cleanup()
return True

566
duckhunt/src/game.py Normal file
View File

@@ -0,0 +1,566 @@
import asyncio
import random
from src.items import DuckTypes, WeaponTypes, AmmoTypes, Attachments
from src.auth import AuthSystem
class DuckGame:
def __init__(self, bot, db):
self.bot = bot
self.config = bot.config
self.logger = getattr(bot, 'logger', None)
self.db = db
self.auth = AuthSystem(db)
self.duck_spawn_min = self.config.get('duck_spawn_min', 30)
self.duck_spawn_max = self.config.get('duck_spawn_max', 120)
self.ducks = {} # channel: duck dict or None
self.players = {} # nick: player dict
self.duck_alerts = set() # nicks who want duck alerts
def get_player(self, nick):
if nick in self.players:
return self.players[nick]
data = self.db.load_player(nick)
if data:
data['friends'] = set(data.get('friends', []))
self.players[nick] = data
return data
default = {
'ammo': 1, 'max_ammo': 1, 'friends': set(), 'caught': 0, 'coins': 100,
'accuracy': 70, 'reliability': 80, 'gun_oil': 0, 'scope': False,
'silencer': False, 'lucky_charm': False, 'xp': 0, 'level': 1,
'bank_account': 0, 'insurance': {'active': False, 'claims': 0},
'weapon': 'basic_gun', 'weapon_durability': 100, 'ammo_type': 'standard',
'attachments': [], 'hunting_license': {'active': False, 'expires': None},
'duck_alerts': False, 'auth_method': 'nick' # 'nick', 'hostmask', 'account'
}
self.players[nick] = default
return default
def save_player(self, nick, data):
self.players[nick] = data
data_to_save = dict(data)
data_to_save['friends'] = list(data_to_save.get('friends', []))
self.db.save_player(nick, data_to_save)
async def spawn_ducks_loop(self):
while True:
wait_time = random.randint(self.duck_spawn_min, self.duck_spawn_max)
if self.logger:
self.logger.info(f"Waiting {wait_time}s before next duck spawn.")
await asyncio.sleep(wait_time)
for chan in self.bot.channels:
duck = self.ducks.get(chan)
if not (duck and duck.get('alive')):
duck_type = DuckTypes.get_random_duck()
self.ducks[chan] = {
'alive': True,
'type': duck_type,
'health': duck_type['health'],
'max_health': duck_type['health']
}
if self.logger:
self.logger.info(f"{duck_type['name']} spawned in {chan}")
spawn_msg = f'\033[93m{duck_type["emoji"]} A {duck_type["name"]} appears! Type !bang, !catch, !bef, or !reload!\033[0m'
await self.bot.send_message(chan, spawn_msg)
# Alert subscribed players
if self.duck_alerts:
alert_msg = f"🦆 DUCK ALERT: {duck_type['name']} in {chan}!"
for alert_nick in self.duck_alerts:
try:
await self.bot.send_message(alert_nick, alert_msg)
except:
pass # User might be offline
async def handle_command(self, user, channel, message):
nick = user.split('!')[0] if user else 'unknown'
hostmask = user if user else 'unknown'
cmd = message.strip().lower()
if self.logger:
self.logger.info(f"{nick}@{channel}: {cmd}")
# Handle private message commands
if channel == self.bot.nick: # Private message
if cmd.startswith('identify '):
parts = cmd.split(' ', 2)
if len(parts) == 3:
await self.handle_identify(nick, parts[1], parts[2])
else:
await self.bot.send_message(nick, "Usage: identify <username> <password>")
return
elif cmd == 'register':
await self.bot.send_message(nick, "To register: /msg me register <username> <password>")
return
elif cmd.startswith('register '):
parts = cmd.split(' ', 2)
if len(parts) == 3:
await self.handle_register(nick, hostmask, parts[1], parts[2])
else:
await self.bot.send_message(nick, "Usage: register <username> <password>")
return
# Public channel commands
if cmd == '!bang':
await self.handle_bang(nick, channel)
elif cmd == '!reload':
await self.handle_reload(nick, channel)
elif cmd == '!bef':
await self.handle_bef(nick, channel)
elif cmd == '!catch':
await self.handle_catch(nick, channel)
elif cmd == '!shop':
await self.handle_shop(nick, channel)
elif cmd == '!duckstats':
await self.handle_duckstats(nick, channel)
elif cmd.startswith('!buy '):
item_num = cmd.split(' ', 1)[1]
await self.handle_buy(nick, channel, item_num)
elif cmd.startswith('!sell '):
item_num = cmd.split(' ', 1)[1]
await self.handle_sell(nick, channel, item_num)
elif cmd == '!stats':
await self.handle_stats(nick, channel)
elif cmd == '!help':
await self.handle_help(nick, channel)
elif cmd == '!leaderboard' or cmd == '!top':
await self.handle_leaderboard(nick, channel)
elif cmd == '!bank':
await self.handle_bank(nick, channel)
elif cmd == '!license':
await self.handle_license(nick, channel)
elif cmd == '!alerts':
await self.handle_alerts(nick, channel)
elif cmd.startswith('!trade '):
parts = cmd.split(' ', 2)
if len(parts) >= 2:
await self.handle_trade(nick, channel, parts[1:])
elif cmd.startswith('!sabotage '):
target = cmd.split(' ', 1)[1]
await self.handle_sabotage(nick, channel, target)
async def handle_bang(self, nick, channel):
player = self.get_player(nick)
duck = self.ducks.get(channel)
if player['ammo'] <= 0:
await self.bot.send_message(channel, f'\033[91m{nick}, you need to !reload!\033[0m')
return
if duck and duck.get('alive'):
player['ammo'] -= 1
# Calculate hit chance based on accuracy and upgrades
base_accuracy = player['accuracy']
if player['scope']:
base_accuracy += 15
if player['lucky_charm']:
base_accuracy += 10
hit_roll = random.randint(1, 100)
if hit_roll <= base_accuracy:
player['caught'] += 1
coins_earned = 1
if player['silencer']:
coins_earned += 1 # Bonus for silencer
player['coins'] += coins_earned
self.ducks[channel] = {'alive': False}
await self.bot.send_message(channel, f'\033[92m{nick} shot the duck! (+{coins_earned} coin{"s" if coins_earned > 1 else ""})\033[0m')
if self.logger:
self.logger.info(f"{nick} shot a duck in {channel}")
else:
await self.bot.send_message(channel, f'\033[93m{nick} missed the duck!\033[0m')
else:
await self.bot.send_message(channel, f'No duck to shoot, {nick}!')
self.save_player(nick, player)
async def handle_reload(self, nick, channel):
player = self.get_player(nick)
# Check gun reliability - can fail to reload
reliability = player['reliability']
if player['gun_oil'] > 0:
reliability += 15
player['gun_oil'] -= 1 # Gun oil gets used up
reload_roll = random.randint(1, 100)
if reload_roll <= reliability:
player['ammo'] = player['max_ammo']
await self.bot.send_message(channel, f'\033[94m{nick} reloaded successfully!\033[0m')
else:
await self.bot.send_message(channel, f'\033[91m{nick}\'s gun jammed while reloading! Try again.\033[0m')
self.save_player(nick, player)
async def handle_bef(self, nick, channel):
player = self.get_player(nick)
duck = self.ducks.get(channel)
if duck and duck.get('alive'):
player['friends'].add('duck')
self.ducks[channel] = {'alive': False}
await self.bot.send_message(channel, f'\033[96m{nick} befriended the duck!\033[0m')
if self.logger:
self.logger.info(f"{nick} befriended a duck in {channel}")
else:
await self.bot.send_message(channel, f'No duck to befriend, {nick}!')
self.save_player(nick, player)
async def handle_catch(self, nick, channel):
player = self.get_player(nick)
duck = self.ducks.get(channel)
if duck and duck.get('alive'):
player['caught'] += 1
self.ducks[channel] = {'alive': False}
await self.bot.send_message(channel, f'\033[92m{nick} caught the duck!\033[0m')
if self.logger:
self.logger.info(f"{nick} caught a duck in {channel}")
else:
await self.bot.send_message(channel, f'No duck to catch, {nick}!')
self.save_player(nick, player)
async def handle_shop(self, nick, channel):
player = self.get_player(nick)
coins = player['coins']
shop_items = [
"🔫 Scope - Improves accuracy by 15% (Cost: 5 coins)",
"🔇 Silencer - Bonus coin on successful shots (Cost: 8 coins)",
"🛢️ Gun Oil - Improves reload reliability for 3 reloads (Cost: 3 coins)",
"🍀 Lucky Charm - Improves accuracy by 10% (Cost: 10 coins)",
"📦 Ammo Upgrade - Increases max ammo capacity by 1 (Cost: 12 coins)",
"🎯 Accuracy Training - Permanently increases accuracy by 5% (Cost: 15 coins)",
"🔧 Gun Maintenance - Permanently increases reliability by 10% (Cost: 20 coins)"
]
shop_msg = f"\033[95m{nick}'s Shop (Coins: {coins}):\033[0m\n"
for i, item in enumerate(shop_items, 1):
shop_msg += f"{i}. {item}\n"
shop_msg += "Use !buy <number> to purchase an item!\n"
shop_msg += "Use !sell <number> to sell upgrades for coins!"
await self.bot.send_message(channel, shop_msg)
async def handle_duckstats(self, nick, channel):
player = self.get_player(nick)
stats = f"\033[95m{nick}'s Duck Stats:\033[0m\n"
stats += f"Caught: {player['caught']}\n"
stats += f"Coins: {player['coins']}\n"
stats += f"Accuracy: {player['accuracy']}%\n"
stats += f"Reliability: {player['reliability']}%\n"
stats += f"Max Ammo: {player['max_ammo']}\n"
stats += f"Gun Oil: {player['gun_oil']} uses left\n"
upgrades = []
if player['scope']: upgrades.append("Scope")
if player['silencer']: upgrades.append("Silencer")
if player['lucky_charm']: upgrades.append("Lucky Charm")
stats += f"Upgrades: {', '.join(upgrades) if upgrades else 'None'}\n"
stats += f"Friends: {', '.join(player['friends']) if player['friends'] else 'None'}\n"
await self.bot.send_message(channel, stats)
async def handle_buy(self, nick, channel, item_num):
player = self.get_player(nick)
try:
item_id = int(item_num)
except ValueError:
await self.bot.send_message(channel, f'{nick}, please specify a valid item number!')
return
shop_items = {
1: ("scope", 5, "Scope"),
2: ("silencer", 8, "Silencer"),
3: ("gun_oil", 3, "Gun Oil"),
4: ("lucky_charm", 10, "Lucky Charm"),
5: ("ammo_upgrade", 12, "Ammo Upgrade"),
6: ("accuracy_training", 15, "Accuracy Training"),
7: ("gun_maintenance", 20, "Gun Maintenance")
}
if item_id not in shop_items:
await self.bot.send_message(channel, f'{nick}, invalid item number!')
return
item_key, cost, item_name = shop_items[item_id]
if player['coins'] < cost:
await self.bot.send_message(channel, f'\033[91m{nick}, you need {cost} coins for {item_name}! (You have {player["coins"]})\033[0m')
return
# Process purchase
player['coins'] -= cost
if item_key == "scope":
if player['scope']:
await self.bot.send_message(channel, f'{nick}, you already have a scope!')
player['coins'] += cost # Refund
return
player['scope'] = True
elif item_key == "silencer":
if player['silencer']:
await self.bot.send_message(channel, f'{nick}, you already have a silencer!')
player['coins'] += cost
return
player['silencer'] = True
elif item_key == "gun_oil":
player['gun_oil'] += 3
elif item_key == "lucky_charm":
if player['lucky_charm']:
await self.bot.send_message(channel, f'{nick}, you already have a lucky charm!')
player['coins'] += cost
return
player['lucky_charm'] = True
elif item_key == "ammo_upgrade":
player['max_ammo'] += 1
elif item_key == "accuracy_training":
player['accuracy'] = min(95, player['accuracy'] + 5) # Cap at 95%
elif item_key == "gun_maintenance":
player['reliability'] = min(95, player['reliability'] + 10) # Cap at 95%
await self.bot.send_message(channel, f'\033[92m{nick} purchased {item_name}!\033[0m')
self.save_player(nick, player)
async def handle_sell(self, nick, channel, item_num):
player = self.get_player(nick)
try:
item_id = int(item_num)
except ValueError:
await self.bot.send_message(channel, f'{nick}, please specify a valid item number!')
return
sellable_items = {
1: ("scope", 3, "Scope"),
2: ("silencer", 5, "Silencer"),
3: ("gun_oil", 1, "Gun Oil (per use)"),
4: ("lucky_charm", 6, "Lucky Charm")
}
if item_id not in sellable_items:
await self.bot.send_message(channel, f'{nick}, invalid item number! Sellable items: 1-4')
return
item_key, sell_price, item_name = sellable_items[item_id]
if item_key == "scope":
if not player['scope']:
await self.bot.send_message(channel, f'{nick}, you don\'t have a scope to sell!')
return
player['scope'] = False
player['coins'] += sell_price
elif item_key == "silencer":
if not player['silencer']:
await self.bot.send_message(channel, f'{nick}, you don\'t have a silencer to sell!')
return
player['silencer'] = False
player['coins'] += sell_price
elif item_key == "gun_oil":
if player['gun_oil'] <= 0:
await self.bot.send_message(channel, f'{nick}, you don\'t have any gun oil to sell!')
return
player['gun_oil'] -= 1
player['coins'] += sell_price
elif item_key == "lucky_charm":
if not player['lucky_charm']:
await self.bot.send_message(channel, f'{nick}, you don\'t have a lucky charm to sell!')
return
player['lucky_charm'] = False
player['coins'] += sell_price
await self.bot.send_message(channel, f'\033[94m{nick} sold {item_name} for {sell_price} coins!\033[0m')
self.save_player(nick, player)
async def handle_stats(self, nick, channel):
player = self.get_player(nick)
# Calculate effective accuracy and reliability
effective_accuracy = player['accuracy']
if player['scope']:
effective_accuracy += 15
if player['lucky_charm']:
effective_accuracy += 10
effective_accuracy = min(100, effective_accuracy)
effective_reliability = player['reliability']
if player['gun_oil'] > 0:
effective_reliability += 15
effective_reliability = min(100, effective_reliability)
stats = f"\033[96m{nick}'s Combat Stats:\033[0m\n"
stats += f"🎯 Base Accuracy: {player['accuracy']}% (Effective: {effective_accuracy}%)\n"
stats += f"🔧 Base Reliability: {player['reliability']}% (Effective: {effective_reliability}%)\n"
stats += f"🔫 Ammo: {player['ammo']}/{player['max_ammo']}\n"
stats += f"💰 Coins: {player['coins']}\n"
stats += f"🦆 Ducks Caught: {player['caught']}\n"
stats += f"🛢️ Gun Oil: {player['gun_oil']} uses\n"
upgrades = []
if player['scope']: upgrades.append("🔭 Scope")
if player['silencer']: upgrades.append("🔇 Silencer")
if player['lucky_charm']: upgrades.append("🍀 Lucky Charm")
stats += f"⚡ Active Upgrades: {', '.join(upgrades) if upgrades else 'None'}\n"
friends = list(player['friends'])
stats += f"🤝 Friends: {', '.join(friends) if friends else 'None'}"
await self.bot.send_message(channel, stats)
async def handle_register(self, nick, hostmask, username, password):
if self.auth.register_account(username, password, nick, hostmask):
await self.bot.send_message(nick, f"✅ Account '{username}' registered successfully! Use 'identify {username} {password}' to login.")
else:
await self.bot.send_message(nick, f"❌ Account '{username}' already exists!")
async def handle_identify(self, nick, username, password):
if self.auth.authenticate(username, password, nick):
await self.bot.send_message(nick, f"✅ Authenticated as '{username}'!")
# Transfer nick-based data to account if exists
nick_data = self.db.load_player(nick)
if nick_data:
account_data = self.db.load_player(username)
if not account_data:
self.db.save_player(username, nick_data)
await self.bot.send_message(nick, "📊 Your progress has been transferred to your account!")
else:
await self.bot.send_message(nick, "❌ Invalid username or password!")
async def handle_help(self, nick, channel):
help_text = """
🦆 **DuckHunt Bot Commands** 🦆
**🎯 Hunting:**
• !bang - Shoot at a duck (requires ammo)
• !reload - Reload your weapon (can fail based on reliability)
• !catch - Catch a duck with your hands
• !bef - Befriend a duck instead of shooting
**🛒 Economy:**
• !shop - View available items for purchase
• !buy <number> - Purchase an item from the shop
• !sell <number> - Sell equipment for coins
• !bank - Access banking services (deposits, loans)
• !trade <player> <item> <amount> - Trade with other players
**📊 Stats & Info:**
• !stats - View detailed combat statistics
• !duckstats - View personal hunting statistics
• !leaderboard - View top players
• !license - Manage hunting license
**⚙️ Settings:**
• !alerts - Toggle duck spawn notifications
• !register - Register an account (via /msg)
• identify <user> <pass> - Login to account (via /msg)
**🎮 Advanced:**
• !sabotage <player> - Attempt to sabotage another hunter
• !help - Show this help message
💡 **Tips:**
- Different duck types give different rewards
- Weapon durability affects performance
- Insurance protects your equipment
- Level up to unlock better gear!
"""
await self.bot.send_message(nick, help_text)
async def handle_leaderboard(self, nick, channel):
leaderboard_data = self.db.get_leaderboard('caught', 10)
if not leaderboard_data:
await self.bot.send_message(channel, "No leaderboard data available yet!")
return
msg = "🏆 **Duck Hunting Leaderboard** 🏆\n"
for i, (account, caught) in enumerate(leaderboard_data, 1):
emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else f"{i}."
msg += f"{emoji} {account}: {caught} ducks\n"
await self.bot.send_message(channel, msg)
async def handle_bank(self, nick, channel):
player = self.get_player(nick)
bank_msg = f"""
🏦 **{nick}'s Bank Account** 🏦
💰 Cash on hand: {player['coins']} coins
🏛️ Bank balance: {player['bank_account']} coins
📈 Total wealth: {player['coins'] + player['bank_account']} coins
**Commands:**
• !bank deposit <amount> - Deposit coins (earns 2% daily interest)
• !bank withdraw <amount> - Withdraw coins
• !bank loan <amount> - Take a loan (10% interest)
"""
await self.bot.send_message(nick, bank_msg)
async def handle_license(self, nick, channel):
player = self.get_player(nick)
license_active = player['hunting_license']['active']
if license_active:
expires = player['hunting_license']['expires']
msg = f"🎫 Your hunting license is active until {expires}\n"
msg += "Licensed hunters get +25% coins and access to rare equipment!"
else:
msg = "🎫 You don't have a hunting license.\n"
msg += "Purchase one for 50 coins to get:\n"
msg += "• +25% coin rewards\n"
msg += "• Access to premium shop items\n"
msg += "• Reduced insurance costs\n"
msg += "Type '!buy license' to purchase"
await self.bot.send_message(channel, msg)
async def handle_alerts(self, nick, channel):
if nick in self.duck_alerts:
self.duck_alerts.remove(nick)
await self.bot.send_message(channel, f"🔕 {nick}: Duck alerts disabled")
else:
self.duck_alerts.add(nick)
await self.bot.send_message(channel, f"🔔 {nick}: Duck alerts enabled! You'll be notified when ducks spawn.")
async def handle_trade(self, nick, channel, args):
if len(args) < 3:
await self.bot.send_message(channel, f"{nick}: Usage: !trade <player> <item> <amount>")
return
target, item, amount = args[0], args[1], args[2]
player = self.get_player(nick)
try:
amount = int(amount)
except ValueError:
await self.bot.send_message(channel, f"{nick}: Amount must be a number!")
return
if item == "coins":
if player['coins'] < amount:
await self.bot.send_message(channel, f"{nick}: You don't have enough coins!")
return
trade_data = {
'type': 'coins',
'amount': amount,
'from_nick': nick
}
trade_id = self.db.save_trade(nick, target, trade_data)
await self.bot.send_message(channel, f"💸 Trade offer sent to {target}: {amount} coins")
await self.bot.send_message(target, f"💰 {nick} wants to trade you {amount} coins. Type '!accept {trade_id}' to accept!")
else:
await self.bot.send_message(channel, f"{nick}: Only coin trading is available currently!")
async def handle_sabotage(self, nick, channel, target):
player = self.get_player(nick)
target_player = self.get_player(target)
if player['coins'] < 5:
await self.bot.send_message(channel, f"{nick}: Sabotage costs 5 coins!")
return
success_chance = 60 + (player['level'] * 5)
if random.randint(1, 100) <= success_chance:
player['coins'] -= 5
target_player['weapon_durability'] = max(0, target_player['weapon_durability'] - 10)
await self.bot.send_message(channel, f"😈 {nick} successfully sabotaged {target}'s weapon!")
self.save_player(nick, player)
self.save_player(target, target_player)
else:
player['coins'] -= 5
await self.bot.send_message(channel, f"😅 {nick}'s sabotage attempt failed!")
self.save_player(nick, player)

124
duckhunt/src/items.py Normal file
View File

@@ -0,0 +1,124 @@
import random
class DuckTypes:
COMMON = {
'name': 'Common Duck',
'emoji': '🦆',
'rarity': 70,
'coins': 1,
'xp': 10,
'health': 1
}
RARE = {
'name': 'Rare Duck',
'emoji': '🦆✨',
'rarity': 20,
'coins': 3,
'xp': 25,
'health': 1
}
GOLDEN = {
'name': 'Golden Duck',
'emoji': '🥇🦆',
'rarity': 8,
'coins': 10,
'xp': 50,
'health': 2
}
ARMORED = {
'name': 'Armored Duck',
'emoji': '🛡️🦆',
'rarity': 2,
'coins': 15,
'xp': 75,
'health': 3
}
@classmethod
def get_random_duck(cls):
roll = random.randint(1, 100)
if roll <= cls.COMMON['rarity']:
return cls.COMMON
elif roll <= cls.COMMON['rarity'] + cls.RARE['rarity']:
return cls.RARE
elif roll <= cls.COMMON['rarity'] + cls.RARE['rarity'] + cls.GOLDEN['rarity']:
return cls.GOLDEN
else:
return cls.ARMORED
class WeaponTypes:
BASIC_GUN = {
'name': 'Basic Gun',
'accuracy_bonus': 0,
'durability': 100,
'max_durability': 100,
'repair_cost': 5,
'attachment_slots': 1
}
SHOTGUN = {
'name': 'Shotgun',
'accuracy_bonus': -10,
'durability': 80,
'max_durability': 80,
'repair_cost': 8,
'attachment_slots': 2,
'spread_shot': True # Can hit multiple ducks
}
RIFLE = {
'name': 'Rifle',
'accuracy_bonus': 20,
'durability': 120,
'max_durability': 120,
'repair_cost': 12,
'attachment_slots': 3
}
class AmmoTypes:
STANDARD = {
'name': 'Standard Ammo',
'damage': 1,
'accuracy_modifier': 0,
'cost': 1
}
RUBBER = {
'name': 'Rubber Bullets',
'damage': 0, # Non-lethal, for catching
'accuracy_modifier': 5,
'cost': 2,
'special': 'stun'
}
EXPLOSIVE = {
'name': 'Explosive Rounds',
'damage': 2,
'accuracy_modifier': -5,
'cost': 5,
'special': 'area_damage'
}
class Attachments:
LASER_SIGHT = {
'name': 'Laser Sight',
'accuracy_bonus': 10,
'cost': 15,
'durability_cost': 2 # Uses weapon durability faster
}
EXTENDED_MAG = {
'name': 'Extended Magazine',
'ammo_bonus': 2,
'cost': 20
}
BIPOD = {
'name': 'Bipod',
'accuracy_bonus': 15,
'reliability_bonus': 5,
'cost': 25
}

View File

@@ -0,0 +1,28 @@
import logging
import sys
from functools import partial
class ColorFormatter(logging.Formatter):
COLORS = {
'DEBUG': '\033[94m',
'INFO': '\033[92m',
'WARNING': '\033[93m',
'ERROR': '\033[91m',
'CRITICAL': '\033[95m',
'ENDC': '\033[0m',
}
def format(self, record):
color = self.COLORS.get(record.levelname, '')
endc = self.COLORS['ENDC']
msg = super().format(record)
return f"{color}{msg}{endc}"
def setup_logger(name='DuckHuntBot', level=logging.INFO):
logger = logging.getLogger(name)
handler = logging.StreamHandler(sys.stdout)
formatter = ColorFormatter('[%(asctime)s] %(levelname)s: %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(level)
logger.propagate = False
return logger

198
duckhunt/src/sasl.py Normal file
View File

@@ -0,0 +1,198 @@
"""
SASL authentication module for IRC connections.
"""
import base64
import asyncio
from .logging_utils import setup_logger
NULL_BYTE = '\x00'
ENCODING = 'UTF-8'
class SASLHandler:
"""Handles SASL authentication for IRC connections."""
def __init__(self, bot, config):
self.bot = bot
self.logger = setup_logger("SASL")
sasl_config = config.get("sasl", {})
self.enabled = sasl_config.get("enabled", False)
self.username = sasl_config.get("username", config.get("nick", ""))
self.password = sasl_config.get("password", "")
self.authenticated = False
self.cap_negotiating = False
def is_enabled(self):
"""Check if SASL is enabled and properly configured."""
return (self.enabled and
self.password and
self.password not in ["", "your_password_here", "your_actual_password"])
def should_authenticate(self):
"""Check if we should attempt SASL authentication."""
if not self.is_enabled():
if self.enabled and not self.password:
self.logger.warning("SASL enabled but no password configured")
elif self.enabled and self.password in ["", "your_password_here", "your_actual_password"]:
self.logger.warning("SASL enabled but using placeholder password")
return False
return True
async def start_negotiation(self):
"""Start CAP negotiation for SASL."""
if not self.should_authenticate():
return False
self.logger.info("SASL authentication enabled")
self.cap_negotiating = True
self.bot.send_raw("CAP LS 302")
return True
async def handle_cap_response(self, params, trailing):
"""Handle CAP responses for SASL negotiation."""
if len(params) < 2:
return False
subcommand = params[1]
if subcommand == "LS":
# Server listing capabilities
caps = trailing.split() if trailing else []
self.logger.info(f"Server capabilities: {caps}")
if "sasl" in caps:
self.logger.info("SASL capability available")
self.bot.send_raw("CAP REQ :sasl")
return True
else:
self.logger.warning("SASL not supported by server")
self.bot.send_raw("CAP END")
await self.bot.register_user()
return False
elif subcommand == "ACK":
# Server acknowledged capability request
caps = trailing.split() if trailing else []
self.logger.info("SASL capability acknowledged")
if "sasl" in caps:
self.logger.info(f"Authenticating via SASL as {self.username}")
await self.handle_sasl()
return True
else:
self.bot.send_raw("CAP END")
await self.bot.register_user()
return False
elif subcommand == "NAK":
# Server rejected capability request
self.logger.warning("SASL capability rejected")
self.bot.send_raw("CAP END")
await self.bot.register_user()
return False
return False
async def handle_sasl(self):
"""
Handles SASL authentication by sending an AUTHENTICATE command.
"""
self.logger.info("Sending AUTHENTICATE PLAIN")
self.bot.send_raw('AUTHENTICATE PLAIN')
# Small delay to ensure proper sequencing
await asyncio.sleep(0.1)
async def handle_authenticate_response(self, params):
"""
Handles the AUTHENTICATE command response.
"""
if params and params[0] == '+':
self.logger.info("Server ready for SASL authentication")
if self.username and self.password:
# Create auth string: username\0username\0password
authpass = f'{self.username}{NULL_BYTE}{self.username}{NULL_BYTE}{self.password}'
self.logger.debug(f"Auth string length: {len(authpass)} chars")
self.logger.debug(f"Auth components: user='{self.username}', pass='{self.password[:3]}...'")
ap_encoded = base64.b64encode(authpass.encode(ENCODING)).decode(ENCODING)
self.logger.debug(f"Base64 encoded length: {len(ap_encoded)} chars")
self.logger.debug(f"Sending: AUTHENTICATE {ap_encoded[:20]}...")
self.bot.send_raw(f'AUTHENTICATE {ap_encoded}')
return True
else:
self.logger.error('SASL username and/or password not configured')
return False
return False
async def handle_sasl_result(self, command, params, trailing):
"""Handle SASL authentication result."""
if command == "903":
# SASL success
self.logger.info("SASL authentication successful!")
self.authenticated = True
await self.handle_903()
return True
elif command == "904":
# SASL failed
self.logger.error("SASL authentication failed! (904 - Invalid credentials or account not found)")
self.logger.error(f"Attempted username: {self.username}")
self.logger.error(f"Password length: {len(self.password)} chars")
if len(params) > 1:
self.logger.error(f"Server reason: {' '.join(params[1:])}")
if trailing:
self.logger.error(f"Server message: {trailing}")
self.bot.send_raw("CAP END")
await self.bot.register_user()
return False
elif command == "905":
# SASL too long
self.logger.error("SASL authentication string too long")
self.bot.send_raw("CAP END")
await self.bot.register_user()
return False
elif command == "906":
# SASL aborted
self.logger.error("SASL authentication aborted")
self.bot.send_raw("CAP END")
await self.bot.register_user()
return False
elif command == "907":
# Already authenticated
self.logger.info("Already authenticated via SASL")
self.authenticated = True
await self.handle_903()
return True
elif command == "908":
# SASL mechanisms
mechanisms = trailing.split() if trailing else []
self.logger.info(f"Available SASL mechanisms: {mechanisms}")
if "PLAIN" not in mechanisms:
self.logger.error("PLAIN mechanism not supported")
self.bot.send_raw("CAP END")
await self.bot.register_user()
return False
return False
async def handle_903(self):
"""
Handles the 903 command by sending a CAP END command and triggering registration.
"""
self.bot.send_raw('CAP END')
# Trigger user registration after successful SASL auth
await self.bot.register_user()
def is_authenticated(self):
"""Check if SASL authentication was successful."""
return self.authenticated
def is_negotiating(self):
"""Check if CAP negotiation is in progress."""
return self.cap_negotiating
def end_negotiation(self):
"""End CAP negotiation."""
self.cap_negotiating = False

11
duckhunt/src/utils.py Normal file
View File

@@ -0,0 +1,11 @@
def parse_message(line):
prefix = ''
trailing = ''
if line.startswith(':'):
prefix, line = line[1:].split(' ', 1)
if ' :' in line:
line, trailing = line.split(' :', 1)
parts = line.split()
command = parts[0] if parts else ''
params = parts[1:] if len(parts) > 1 else []
return prefix, command, params, trailing

167
duckhunt/test_bot.py Normal file
View File

@@ -0,0 +1,167 @@
#!/usr/bin/env python3
"""
Test script for DuckHunt Bot
Run this to test both the modular and simple bot implementations
"""
import asyncio
import json
import sys
import os
# Add src directory to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
async def test_modular_bot():
"""Test the modular bot implementation"""
try:
print("🔧 Testing modular bot (src/duckhuntbot.py)...")
# Load config
with open('config.json') as f:
config = json.load(f)
# Test imports
from src.duckhuntbot import IRCBot
from src.sasl import SASLHandler
# Create bot instance
bot = IRCBot(config)
print("✅ Modular bot initialized successfully!")
# Test SASL handler
sasl_handler = SASLHandler(bot, config)
print("✅ SASL handler created successfully!")
# Test database
bot.db.save_player("testuser", {"coins": 100, "caught": 5})
data = bot.db.load_player("testuser")
if data and data['coins'] == 100:
print("✅ Database working!")
else:
print("❌ Database test failed!")
# Test game logic
player = bot.game.get_player("testuser")
if player and 'coins' in player:
print("✅ Game logic working!")
else:
print("❌ Game logic test failed!")
return True
except Exception as e:
print(f"❌ Modular bot error: {e}")
import traceback
traceback.print_exc()
return False
async def test_simple_bot():
"""Test the simple bot implementation"""
try:
print("\n🔧 Testing simple bot (simple_duckhunt.py)...")
# Load config
with open('config.json') as f:
config = json.load(f)
# Test imports
from simple_duckhunt import SimpleIRCBot
from src.sasl import SASLHandler
# Create bot instance
bot = SimpleIRCBot(config)
print("✅ Simple bot initialized successfully!")
# Test SASL handler integration
if hasattr(bot, 'sasl_handler'):
print("✅ SASL handler integrated!")
else:
print("❌ SASL handler not integrated!")
return False
# Test database
if 'testuser' in bot.players:
bot.players['testuser']['coins'] = 200
bot.save_database()
bot.load_database()
if bot.players.get('testuser', {}).get('coins') == 200:
print("✅ Simple bot database working!")
else:
print("❌ Simple bot database test failed!")
return True
except Exception as e:
print(f"❌ Simple bot error: {e}")
import traceback
traceback.print_exc()
return False
async def test_sasl_config():
"""Test SASL configuration"""
try:
print("\n🔧 Testing SASL configuration...")
# Load config
with open('config.json') as f:
config = json.load(f)
# Check SASL config
sasl_config = config.get('sasl', {})
if sasl_config.get('enabled'):
print("✅ SASL is enabled in config")
username = sasl_config.get('username')
password = sasl_config.get('password')
if username and password:
print(f"✅ SASL credentials configured (user: {username})")
else:
print("⚠️ SASL enabled but credentials missing")
else:
print(" SASL is not enabled in config")
return True
except Exception as e:
print(f"❌ SASL config error: {e}")
return False
async def main():
"""Main test function"""
print("🦆 DuckHunt Bot Integration Test")
print("=" * 50)
try:
# Test configuration
config_ok = await test_sasl_config()
# Test modular bot
modular_ok = await test_modular_bot()
# Test simple bot
simple_ok = await test_simple_bot()
print("\n" + "=" * 50)
print("📊 Test Results:")
print(f" Config: {'✅ PASS' if config_ok else '❌ FAIL'}")
print(f" Modular Bot: {'✅ PASS' if modular_ok else '❌ FAIL'}")
print(f" Simple Bot: {'✅ PASS' if simple_ok else '❌ FAIL'}")
if all([config_ok, modular_ok, simple_ok]):
print("\n🎉 All tests passed! SASL integration is working!")
print("🦆 DuckHunt Bots are ready to deploy!")
return True
else:
print("\n💥 Some tests failed. Check the errors above.")
return False
except Exception as e:
print(f"💥 Test suite error: {e}")
return False
if __name__ == '__main__':
success = asyncio.run(main())
if not success:
sys.exit(1)