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:
272
duckhunt/CONFIG_GUIDE.md
Normal file
272
duckhunt/CONFIG_GUIDE.md
Normal 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
172
duckhunt/README.md
Normal 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!** 🦆
|
||||
BIN
duckhunt/__pycache__/simple_duckhunt.cpython-312.pyc
Normal file
BIN
duckhunt/__pycache__/simple_duckhunt.cpython-312.pyc
Normal file
Binary file not shown.
205
duckhunt/config.json
Normal file
205
duckhunt/config.json
Normal 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
216
duckhunt/config_backup.json
Normal 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
122
duckhunt/demo_sasl.py
Normal 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
BIN
duckhunt/duckhunt.db
Normal file
Binary file not shown.
960
duckhunt/duckhunt.json
Normal file
960
duckhunt/duckhunt.json
Normal 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
543
duckhunt/duckhunt.log
Normal 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
37
duckhunt/duckhunt.py
Normal 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
2243
duckhunt/simple_duckhunt.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
duckhunt/src/__pycache__/auth.cpython-312.pyc
Normal file
BIN
duckhunt/src/__pycache__/auth.cpython-312.pyc
Normal file
Binary file not shown.
BIN
duckhunt/src/__pycache__/db.cpython-312.pyc
Normal file
BIN
duckhunt/src/__pycache__/db.cpython-312.pyc
Normal file
Binary file not shown.
BIN
duckhunt/src/__pycache__/duckhuntbot.cpython-312.pyc
Normal file
BIN
duckhunt/src/__pycache__/duckhuntbot.cpython-312.pyc
Normal file
Binary file not shown.
BIN
duckhunt/src/__pycache__/game.cpython-312.pyc
Normal file
BIN
duckhunt/src/__pycache__/game.cpython-312.pyc
Normal file
Binary file not shown.
BIN
duckhunt/src/__pycache__/items.cpython-312.pyc
Normal file
BIN
duckhunt/src/__pycache__/items.cpython-312.pyc
Normal file
Binary file not shown.
BIN
duckhunt/src/__pycache__/logging_utils.cpython-312.pyc
Normal file
BIN
duckhunt/src/__pycache__/logging_utils.cpython-312.pyc
Normal file
Binary file not shown.
BIN
duckhunt/src/__pycache__/sasl.cpython-312.pyc
Normal file
BIN
duckhunt/src/__pycache__/sasl.cpython-312.pyc
Normal file
Binary file not shown.
BIN
duckhunt/src/__pycache__/utils.cpython-312.pyc
Normal file
BIN
duckhunt/src/__pycache__/utils.cpython-312.pyc
Normal file
Binary file not shown.
108
duckhunt/src/auth.py
Normal file
108
duckhunt/src/auth.py
Normal 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
97
duckhunt/src/db.py
Normal 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
277
duckhunt/src/duckhuntbot.py
Normal 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
566
duckhunt/src/game.py
Normal 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
124
duckhunt/src/items.py
Normal 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
|
||||
}
|
||||
28
duckhunt/src/logging_utils.py
Normal file
28
duckhunt/src/logging_utils.py
Normal 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
198
duckhunt/src/sasl.py
Normal 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
11
duckhunt/src/utils.py
Normal 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
167
duckhunt/test_bot.py
Normal 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)
|
||||
Reference in New Issue
Block a user