An asyncio-based IRC bot that runs the classic Duck Hunt mini-game in IRC channels.
This repository is an enhanced fork maintained by CodeVaultX. It keeps the core gameplay while adding multi-network support, improved authentication, a friendlier help system, and several reliability fixes for real-world IRC networks.
Compared to lord3nd3r/duckhunt, this version adds and changes the following:
- Connect to multiple IRC networks at once from a single process.
- Configure servers in a top-level
connectionsarray inconfig.json. - Each server gets its own database file:
duckhunt_<server>.json(e.g.duckhunt_irc.rizon.net.json). - Per-server options:
server,port,nick,channels,ssl,sasl,nickserv, reconnect settings, etc. enabled: falseon a connection entry disables that network without deleting its config.- Optional
labelfield for readable log messages (e.g."Rizon","QuakeNet"). - Legacy single
connectionobject is still supported for one-server setups.
- The configured prefix (default
!viacommands.prefix) works as before. - Additionally, every command also accepts a dot prefix (
.), regardless of config.- Example: if
commands.prefixis!, both!bangand.bangwork. - Same for
!help/.help,!shop/.shop, admin commands, etc.
- Example: if
The upstream bot documents !duckhelp for a full command list via PM. This fork adds:
| Where | Command | What it does |
|---|---|---|
| Channel | !help or .help |
Short public command summary + “PRIVMSG me help” |
| PM | help (no prefix) |
Short command list |
| PM | help full (no prefix) |
Detailed command list |
| Channel / PM | !help full / .help full |
Same as help full |
| Channel / PM | !duckhelp / .duckhelp |
Shortcut for full help (PM) |
Help replies are sent via PRIVMSG, not NOTICE.
Per-server authentication is supported inside each connections entry:
- SASL — standard PLAIN auth when the network supports it.
- NickServ IDENTIFY — for networks like Rizon, DALnet, etc.
- On the registered nick:
IDENTIFY <password> - On a guest nick / different account:
IDENTIFY <nick> <password>
- On the registered nick:
- QuakeNet Q auth —
command: "auth"sendsAUTH <username> <password>toQ@CServe.quakenet.org.
SASL and NickServ can be configured per connection, overriding global defaults.
duck_spawning.timeout— global duck fly-away timeout (seconds) is now respected; per-type overrides induck_types.*.timeoutstill work.- Unicode channel names — channels like
#Fenerbahçeare handled correctly (no broken replies from ASCII-only sanitization). - Per-server enable/disable — run only the networks you need without editing channel lists each time.
- Smoke tests —
tests/test_smoke.pycovers multi-server config, NickServ hoisting, and QuakeNet auth. - Windows helpers (optional) —
start_hidden.vbs/stop_duckhunt.vbsfor background startup (use a Startup shortcut, not a copied VBS).
- Multi-channel support per IRC server
- Per-channel player stats + global leaderboard
- Duck types: Normal, Golden, Fast, Ninja, Flock
- Shop, inventory, items, temporary effects
- Leveling, XP, daily bonus streaks
- Achievements
- JSON persistence with auto-save
- Admin tools (rearm, disarm, ignore, ducklaunch, join/part)
- Rate limiting, reconnect, auto-rejoin, lag watchdog
- Python 3.9+ (stdlib only — no pip dependencies)
- An IRC network and channel(s) where the bot is allowed
git clone https://github.com/CodeVaultX/duckhunt.git
cd duckhuntcp config.json.example config.json# Create config.json (see Configuration section below)
python duckhunt.pyRun tests:
python -m unittest discover -s tests -p "test_*.py"config.json is gitignored — never commit real passwords.
{
"connection": {
"server": "irc.example.net",
"port": 6697,
"nick": "DuckHunt",
"channels": ["#duckhunt"],
"ssl": true,
"ssl_verify": true
},
"commands": { "prefix": "!" },
"admins": ["YourNick"],
"duck_spawning": {
"spawn_min": 600,
"spawn_max": 1200,
"timeout": 300
}
}{
"connections": [
{
"label": "Rizon",
"enabled": true,
"server": "irc.rizon.net",
"port": 6697,
"nick": "DuckHunt",
"channels": ["#games", "#duckhunt"],
"ssl": true,
"ssl_verify": true,
"nickserv": {
"enabled": true,
"service": "NickServ",
"password": "YOUR_NICKSERV_PASSWORD",
"identify_delay": 1.0
}
},
{
"label": "QuakeNet",
"enabled": false,
"server": "irc.quakenet.org",
"port": 6667,
"nick": "DuckHunt",
"channels": ["#duckhunt"],
"ssl": false,
"nickserv": {
"enabled": true,
"service": "Q@CServe.quakenet.org",
"command": "auth",
"username": "DuckHunt",
"password": "YOUR_Q_PASSWORD"
}
}
],
"commands": { "prefix": "!" },
"admins": [
"YourNick",
{ "nick": "TrustedAdmin", "hostmask": "*!user@trusted.host" }
],
"duck_spawning": {
"spawn_min": 600,
"spawn_max": 1200,
"timeout": 300,
"flock_chance": 0.08
}
}| Key | Description |
|---|---|
connections[] |
List of IRC servers (preferred) |
connection |
Legacy single-server object |
enabled |
Per connection; false skips that server |
label |
Optional name shown in logs |
commands.prefix |
Primary trigger (default !). Dot . always works too |
admins |
Nick string and/or { "nick", "hostmask" } objects |
sasl |
Global or per-connection SASL (enabled, username, password) |
nickserv |
Per-connection services auth (see above) |
duck_spawning.spawn_min / spawn_max |
Random spawn interval in seconds |
duck_spawning.timeout |
Seconds before an unshot duck flies away (global default) |
duck_types.*.timeout |
Per duck-type timeout override |
connection.ssl_verify |
Keep true in production; false only for self-signed certs |
Admin security: plain-string admins ("colby") authenticate by nick only. Use hostmask objects for production.
Throughout this section:
!= your configured prefix (commands.prefix, default!).= alternate prefix (always accepted)- Examples use
!; replace with.anytime (e.g..bang,.help)
| Command | Description |
|---|---|
!bang or .bang |
Shoot at the active duck |
!bef / !befriend or .bef / .befriend |
Try to befriend the duck |
!reload or .reload |
Reload or unjam your weapon |
!daily or .daily |
Claim daily XP bonus (24h cooldown, streaks) |
!duckstats [player] or .duckstats [player] |
Hunting stats for this channel |
!topduck or .topduck |
Channel XP leaderboard |
!globaltop or .globaltop |
Top players across all channels on this server |
!shop or .shop |
Shop menu (sent via NOTICE; short ack in channel) |
!shop buy <id> [target] or .shop buy <id> [target] |
Buy an item (optionally for another player) |
!use <item_id> [target] or .use <item_id> [target] |
Use an inventory item |
!give <item_id> <player> or .give <item_id> <player> |
Give an inventory item to someone |
!inv or .inv |
Quick inventory view |
!effects or .effects |
Active buffs and timers |
!profile or .profile |
Detailed stat card (PM) |
!achievements or .achievements |
Earned badges (PM) |
!help / !duckhelp or .help / .duckhelp |
Help (see Help commands above) |
!rearm or .rearm |
Channel |
!rearm <player> or .rearm <player> |
Channel |
# In channel:
!help
.help
# In private message to the bot:
help
help full
# Also works with prefix:
!help full
.duckhelp
Admins must match admins in config (hostmask recommended).
| Command | Where | Description |
|---|---|---|
!rearm all |
Channel | Rearm all confiscated players in channel |
!rearm all |
PM | Rearm all players on this server |
!rearm <#channel> <player> |
PM | Rearm a specific player in a channel |
!disarm <player> |
Channel / PM | Confiscate a player's gun |
!ignore <player> |
Channel / PM | Ignore a player's commands |
!unignore <player> |
Channel / PM | Stop ignoring a player |
!ducklaunch [duck_type] |
Channel | Force-spawn duck in current channel |
!ducklaunch <#channel> [duck_type] |
PM | Force-spawn in another channel |
!join <#channel> |
Channel / PM | Bot joins a channel |
!part <#channel> |
Channel / PM | Bot leaves a channel |
!reload |
PM only | Restart the bot process (applies code/config changes) |
Note: In channels, !reload reloads your gun. In PM, !reload (admin only) restarts the entire bot process (all enabled servers in multi-server mode).
normal, golden, fast, ninja, flock
| ID | Name | Typical cost | Effect |
|---|---|---|---|
| 1 | Single Bullet | 5 XP | +1 bullet in current magazine |
| 2 | Magazine | 15 XP | +1 spare magazine |
| 4 | Gun Brush | 20 XP | −10% jam chance |
| 5 | Bread | 50 XP | 2× duck spawn rate for 20 minutes |
| 7 | Buy Gun Back | 40 XP | Recover confiscated gun |
| 13 | Scope | 60 XP | +20% accuracy for next 5 shots |
| 14 | Body Armor | 100 XP | Blocks next XP-loss event |
Use !shop for live prices and descriptions.
- A duck spawns randomly in the channel (interval set by
duck_spawning.spawn_min/spawn_max). - Players type
!bangto shoot or!befto befriend. - Earn XP, level up, buy shop items, unlock achievements.
- Unshot ducks fly away after
duck_spawning.timeoutseconds (unless overridden per duck type).
XP, level, streaks, ducks shot/befriended, accuracy, inventory, active effects, achievements, shop spending — per channel, per IRC server database.
| File | Purpose |
|---|---|
duckhunt_<server>.json |
Player data per IRC server (multi-server mode) |
duckhunt.json |
Used when only a legacy single connection is configured |
levels.json |
Level definitions |
shop.json |
Shop catalog |
messages.json |
Bot message templates |
Writes are atomic with retry logic. Relative DB paths resolve to the project root, not the shell CWD.
duckhunt/
├── duckhunt.py # Entry point (spawns one bot per enabled connection)
├── config.json # Your config (gitignored — create locally)
├── duckhunt_*.json # Per-server databases (gitignored)
├── levels.json
├── shop.json
├── messages.json
├── start_hidden.vbs # Optional Windows background launcher
├── stop_duckhunt.vbs # Optional Windows stop script
├── tests/
│ └── test_smoke.py
└── src/
├── duckhuntbot.py # IRC bot, commands, auth
├── game.py # Duck spawning & combat
├── db.py # JSON persistence
├── shop.py # Shop & inventory
├── levels.py # Leveling
├── sasl.py # SASL
├── error_handling.py
├── logging_utils.py
└── utils.py
- All enabled bots run concurrently via
asyncio.gather. - Same channel name on different networks has separate stats (different DB files).
- Disabling a server: set
"enabled": falseon thatconnectionsentry and restart. - PM admin
!reloadrestarts the whole process (every enabled server). - Do not run the same nick on two machines against the same network — IRC will kick one connection.
| Feature | lord3nd3r/duckhunt | This fork |
|---|---|---|
| IRC networks | Single connection |
connections[] multi-server |
| Command prefix | Configured prefix only | Configured prefix + . always |
| Help | !duckhelp → full list in PM |
!help / .help in channel + PM help / help full |
| Nick/password auth | SASL (global) | SASL + per-server NickServ / QuakeNet Q |
| Per-server on/off | — | enabled: true/false |
| Database | duckhunt.json |
duckhunt_<server>.json per network |
| Duck timeout config | Per-type only in practice | duck_spawning.timeout global fallback |
| Unicode channels | — | Full Unicode channel names supported |
- Keep
config.jsonout of git (already in.gitignore). - Prefer admin entries with
hostmask, not nick-only strings. - Keep
connection.ssl_verify: trueunless you fully trust the network path. - Rotate passwords if they were ever shared in logs or chat.
Follow the license of the upstream repository. This fork is based on that project.
- Fork maintained by CodeVaultX
- Original repository — lord3nd3r/duckhunt
- Originally written by Computertech
- Maintained upstream by End3r
Happy Duck Hunting!