Implement competitive item snatching system
- Add dropped items tracking with timestamps per channel - Items drop to ground (10% chance on duck kills) for any player to grab - Add 60-second timeout for unclaimed items - Background cleanup task removes expired items automatically - First-come-first-served basis for item collection - Eggdrop-style messaging for drops and successful snatches
This commit is contained in:
BIN
__pycache__/duckhunt.cpython-312.pyc
Normal file
BIN
__pycache__/duckhunt.cpython-312.pyc
Normal file
Binary file not shown.
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"server": "irc.rizon.net",
|
||||
"port": 6697,
|
||||
"nick": "DuckHunt",
|
||||
"channels": ["#computertech"],
|
||||
"nick": "DickHunt",
|
||||
"channels": ["#ct"],
|
||||
"ssl": true,
|
||||
"sasl": {
|
||||
"enabled": true,
|
||||
"enabled": false,
|
||||
"username": "duckhunt",
|
||||
"password": "duckhunt//789//"
|
||||
},
|
||||
@@ -22,7 +22,7 @@
|
||||
"caught": 12,
|
||||
"ammo": 5,
|
||||
"max_ammo": 10,
|
||||
"chargers": 0,
|
||||
"chargers": 2,
|
||||
"max_chargers": 2,
|
||||
"xp": 100,
|
||||
"accuracy": 65,
|
||||
@@ -31,22 +31,37 @@
|
||||
"gun_level": 1,
|
||||
"luck": 0,
|
||||
"gun_type": "pistol",
|
||||
"gun_confiscated": false,
|
||||
"gun_confiscated": true,
|
||||
"jammed": false,
|
||||
"jammed_count": 1,
|
||||
"total_ammo_used": 12,
|
||||
"total_ammo_used": 18,
|
||||
"shot_at": 9,
|
||||
"reflex_shots": 6,
|
||||
"total_reflex_time": 47.87793278694153,
|
||||
"best_time": 2.6269078254699707,
|
||||
"karma": 1,
|
||||
"wild_shots": 4,
|
||||
"wild_shots": 10,
|
||||
"befriended": 6,
|
||||
"missed": 1,
|
||||
"inventory": {
|
||||
"15": 1
|
||||
},
|
||||
"sand": 1
|
||||
"sand": 1,
|
||||
"shots": 9,
|
||||
"max_shots": 10,
|
||||
"reload_time": 5.0,
|
||||
"ducks_shot": 12,
|
||||
"ducks_befriended": 6,
|
||||
"accuracy_bonus": 0,
|
||||
"xp_bonus": 0,
|
||||
"charm_bonus": 0,
|
||||
"exp": 100,
|
||||
"money": 119,
|
||||
"last_hunt": 0,
|
||||
"last_reload": 0,
|
||||
"level": 1,
|
||||
"ignored_users": [],
|
||||
"confiscated_count": 2
|
||||
},
|
||||
"colby_": {
|
||||
"xp": 0,
|
||||
@@ -1014,7 +1029,77 @@
|
||||
"bread": 0,
|
||||
"duck_detector": 0,
|
||||
"mechanical": 0
|
||||
},
|
||||
"py-ctcp": {
|
||||
"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": {
|
||||
"output_mode": "PUBLIC",
|
||||
"notices": false,
|
||||
"private_messages": false
|
||||
},
|
||||
"inventory": {},
|
||||
"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,
|
||||
"shots": 6,
|
||||
"max_shots": 6,
|
||||
"reload_time": 5.0,
|
||||
"ducks_shot": 0,
|
||||
"ducks_befriended": 0,
|
||||
"accuracy_bonus": 0,
|
||||
"xp_bonus": 0,
|
||||
"charm_bonus": 0,
|
||||
"exp": 0,
|
||||
"money": 100,
|
||||
"last_hunt": 0,
|
||||
"last_reload": 0,
|
||||
"level": 1,
|
||||
"ignored_users": [],
|
||||
"confiscated_count": 0
|
||||
}
|
||||
},
|
||||
"last_save": "1757771132.8947892"
|
||||
"last_save": "1758314578.1957033"
|
||||
}
|
||||
198
duckhunt.log
Normal file
198
duckhunt.log
Normal file
@@ -0,0 +1,198 @@
|
||||
2025-09-19 21:04:25,978 [INFO ] DuckHuntBot - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:04:25,978 [INFO ] SASL - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:04:25,978 [INFO ] DuckHuntBot - main:26: 🦆 Starting DuckHunt Bot...
|
||||
2025-09-19 21:04:25,980 [INFO ] DuckHuntBot.DB - load_database:40: Loaded 21 players from duckhunt.json
|
||||
2025-09-19 21:04:32,607 [INFO ] DuckHuntBot - signal_handler:188: Received signal 2, shutting down...
|
||||
2025-09-19 21:04:33,747 [INFO ] DuckHuntBot - signal_handler:188: Received signal 2, shutting down...
|
||||
2025-09-19 21:05:02,022 [INFO ] DuckHuntBot - connect:92: Connected to irc.rizon.net:6697
|
||||
2025-09-19 21:05:02,086 [ERROR ] DuckHuntBot - run:898: Bot error: [SSL: APPLICATION_DATA_AFTER_CLOSE_NOTIFY] application data after close notify (_ssl.c:2685)
|
||||
2025-09-19 21:06:16,216 [INFO ] DuckHuntBot - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:06:16,217 [INFO ] SASL - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:06:16,221 [INFO ] DuckHuntBot.DB - load_database:40: Loaded 21 players from duckhunt.json
|
||||
2025-09-19 21:06:17,222 [INFO ] DuckHuntBot - signal_handler:188: Received signal 2, shutting down...
|
||||
2025-09-19 21:06:17,223 [INFO ] DuckHuntBot - run:908: Shutting down bot...
|
||||
2025-09-19 21:08:34,809 [INFO ] DuckHuntBot - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:08:34,810 [INFO ] SASL - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:08:34,817 [INFO ] DuckHuntBot.DB - load_database:40: Loaded 21 players from duckhunt.json
|
||||
2025-09-19 21:08:34,977 [INFO ] DuckHuntBot - connect:92: Connected to irc.libera.chat:6667
|
||||
2025-09-19 21:08:35,507 [INFO ] DuckHuntBot - handle_message:205: Successfully registered with IRC server
|
||||
2025-09-19 21:08:35,815 [INFO ] DuckHuntBot - signal_handler:188: Received signal 2, shutting down...
|
||||
2025-09-19 21:08:35,816 [INFO ] DuckHuntBot - message_loop:960: Message loop cancelled
|
||||
2025-09-19 21:08:35,817 [INFO ] DuckHuntBot - message_loop:966: Message loop ended
|
||||
2025-09-19 21:08:35,818 [INFO ] DuckHuntBot.Game - duck_timeout_checker:295: Duck timeout checker cancelled
|
||||
2025-09-19 21:08:35,819 [INFO ] DuckHuntBot - run:899: Main loop cancelled
|
||||
2025-09-19 21:08:35,821 [INFO ] DuckHuntBot - run:909: Shutting down bot...
|
||||
2025-09-19 21:08:35,822 [INFO ] DuckHuntBot.Game - spawn_ducks:250: Duck spawning loop cancelled
|
||||
2025-09-19 21:08:35,830 [INFO ] DuckHuntBot - run:925: Database saved
|
||||
2025-09-19 21:08:35,831 [INFO ] DuckHuntBot - run:935: IRC connection closed
|
||||
2025-09-19 21:08:35,831 [INFO ] DuckHuntBot - run:939: Bot shutdown complete
|
||||
2025-09-19 21:08:44,430 [INFO ] DuckHuntBot - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:08:44,431 [INFO ] SASL - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:08:44,431 [INFO ] DuckHuntBot - main:26: 🦆 Starting DuckHunt Bot...
|
||||
2025-09-19 21:08:44,432 [INFO ] DuckHuntBot.DB - load_database:40: Loaded 21 players from duckhunt.json
|
||||
2025-09-19 21:08:44,740 [INFO ] DuckHuntBot - connect:92: Connected to irc.rizon.net:6697
|
||||
2025-09-19 21:08:49,547 [INFO ] DuckHuntBot - signal_handler:188: Received signal 2, shutting down...
|
||||
2025-09-19 21:08:49,558 [INFO ] DuckHuntBot - run:899: Main loop cancelled
|
||||
2025-09-19 21:08:49,559 [INFO ] DuckHuntBot - run:909: Shutting down bot...
|
||||
2025-09-19 21:08:49,559 [INFO ] DuckHuntBot - message_loop:960: Message loop cancelled
|
||||
2025-09-19 21:08:49,560 [INFO ] DuckHuntBot - message_loop:966: Message loop ended
|
||||
2025-09-19 21:08:49,560 [INFO ] DuckHuntBot.Game - spawn_ducks:250: Duck spawning loop cancelled
|
||||
2025-09-19 21:08:49,560 [INFO ] DuckHuntBot.Game - duck_timeout_checker:295: Duck timeout checker cancelled
|
||||
2025-09-19 21:08:49,563 [INFO ] DuckHuntBot - run:925: Database saved
|
||||
2025-09-19 21:08:49,655 [ERROR ] DuckHuntBot - run:937: Error closing connection: [SSL: APPLICATION_DATA_AFTER_CLOSE_NOTIFY] application data after close notify (_ssl.c:2685)
|
||||
2025-09-19 21:08:49,655 [INFO ] DuckHuntBot - run:939: Bot shutdown complete
|
||||
2025-09-19 21:09:05,620 [INFO ] DuckHuntBot - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:09:05,621 [INFO ] SASL - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:09:05,621 [INFO ] DuckHuntBot - main:26: 🦆 Starting DuckHunt Bot...
|
||||
2025-09-19 21:09:05,622 [INFO ] DuckHuntBot.DB - load_database:40: Loaded 21 players from duckhunt.json
|
||||
2025-09-19 21:09:05,871 [INFO ] DuckHuntBot - connect:92: Connected to irc.rizon.net:6697
|
||||
2025-09-19 21:09:06,058 [INFO ] DuckHuntBot - handle_message:205: Successfully registered with IRC server
|
||||
2025-09-19 21:09:09,738 [ERROR ] DuckHuntBot - handle_command:287: Error in command handler: 'shots'
|
||||
2025-09-19 21:09:17,085 [INFO ] DuckHuntBot.Game - spawn_duck_now:203: Spawned normal duck in #ct
|
||||
2025-09-19 21:09:17,085 [DEBUG ] DuckHuntBot.Game - send_duck_alerts:217: Duck alerts for normal duck in #ct
|
||||
2025-09-19 21:09:22,905 [INFO ] DuckHuntBot.Game - spawn_duck_now:203: Spawned rare duck in #ct
|
||||
2025-09-19 21:09:22,906 [DEBUG ] DuckHuntBot.Game - send_duck_alerts:217: Duck alerts for rare duck in #ct
|
||||
2025-09-19 21:09:24,398 [INFO ] DuckHuntBot.Game - spawn_duck_now:203: Spawned rare duck in #ct
|
||||
2025-09-19 21:09:24,398 [DEBUG ] DuckHuntBot.Game - send_duck_alerts:217: Duck alerts for rare duck in #ct
|
||||
2025-09-19 21:09:25,076 [DEBUG ] DuckHuntBot.Game - spawn_duck_now:161: Max ducks already in #ct
|
||||
2025-09-19 21:10:05,877 [DEBUG ] DuckHuntBot.Game - duck_timeout_checker:290: Duck timed out in #ct
|
||||
2025-09-19 21:10:35,879 [DEBUG ] DuckHuntBot.Game - duck_timeout_checker:290: Duck timed out in #ct
|
||||
2025-09-19 21:10:35,880 [DEBUG ] DuckHuntBot.Game - duck_timeout_checker:290: Duck timed out in #ct
|
||||
2025-09-19 21:10:53,732 [INFO ] DuckHuntBot - signal_handler:188: Received signal 2, shutting down...
|
||||
2025-09-19 21:10:53,764 [INFO ] DuckHuntBot - run:899: Main loop cancelled
|
||||
2025-09-19 21:10:53,768 [INFO ] DuckHuntBot - run:909: Shutting down bot...
|
||||
2025-09-19 21:10:53,768 [INFO ] DuckHuntBot - message_loop:960: Message loop cancelled
|
||||
2025-09-19 21:10:53,770 [INFO ] DuckHuntBot - message_loop:966: Message loop ended
|
||||
2025-09-19 21:10:53,771 [INFO ] DuckHuntBot.Game - spawn_ducks:250: Duck spawning loop cancelled
|
||||
2025-09-19 21:10:53,775 [INFO ] DuckHuntBot.Game - duck_timeout_checker:295: Duck timeout checker cancelled
|
||||
2025-09-19 21:10:53,789 [INFO ] DuckHuntBot - run:925: Database saved
|
||||
2025-09-19 21:10:53,853 [ERROR ] DuckHuntBot - run:937: Error closing connection: [SSL: APPLICATION_DATA_AFTER_CLOSE_NOTIFY] application data after close notify (_ssl.c:2685)
|
||||
2025-09-19 21:10:53,854 [INFO ] DuckHuntBot - run:939: Bot shutdown complete
|
||||
2025-09-19 21:12:51,877 [INFO ] DuckHuntBot - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:12:51,879 [INFO ] SASL - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:12:51,881 [INFO ] DuckHuntBot - main:26: 🦆 Starting DuckHunt Bot...
|
||||
2025-09-19 21:12:51,887 [INFO ] DuckHuntBot.DB - load_database:40: Loaded 22 players from duckhunt.json
|
||||
2025-09-19 21:12:52,184 [INFO ] DuckHuntBot - connect:92: Connected to irc.rizon.net:6697
|
||||
2025-09-19 21:12:52,366 [INFO ] DuckHuntBot - handle_message:205: Successfully registered with IRC server
|
||||
2025-09-19 21:13:21,643 [INFO ] DuckHuntBot - signal_handler:188: Received signal 15, shutting down...
|
||||
2025-09-19 21:13:21,681 [INFO ] DuckHuntBot - run:899: Main loop cancelled
|
||||
2025-09-19 21:13:21,682 [INFO ] DuckHuntBot - run:909: Shutting down bot...
|
||||
2025-09-19 21:13:21,684 [INFO ] DuckHuntBot - message_loop:960: Message loop cancelled
|
||||
2025-09-19 21:13:21,685 [INFO ] DuckHuntBot - message_loop:966: Message loop ended
|
||||
2025-09-19 21:13:21,686 [INFO ] DuckHuntBot.Game - spawn_ducks:250: Duck spawning loop cancelled
|
||||
2025-09-19 21:13:21,686 [INFO ] DuckHuntBot.Game - duck_timeout_checker:295: Duck timeout checker cancelled
|
||||
2025-09-19 21:13:21,690 [INFO ] DuckHuntBot - run:925: Database saved
|
||||
2025-09-19 21:13:21,752 [ERROR ] DuckHuntBot - run:937: Error closing connection: [SSL: APPLICATION_DATA_AFTER_CLOSE_NOTIFY] application data after close notify (_ssl.c:2685)
|
||||
2025-09-19 21:13:21,753 [INFO ] DuckHuntBot - run:939: Bot shutdown complete
|
||||
2025-09-19 21:13:27,996 [INFO ] DuckHuntBot - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:13:27,997 [INFO ] SASL - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:13:27,997 [INFO ] DuckHuntBot - main:26: 🦆 Starting DuckHunt Bot...
|
||||
2025-09-19 21:13:27,999 [INFO ] DuckHuntBot.DB - load_database:40: Loaded 22 players from duckhunt.json
|
||||
2025-09-19 21:13:28,246 [INFO ] DuckHuntBot - connect:92: Connected to irc.rizon.net:6697
|
||||
2025-09-19 21:13:28,721 [INFO ] DuckHuntBot - handle_message:205: Successfully registered with IRC server
|
||||
2025-09-19 21:18:41,831 [INFO ] DuckHuntBot - signal_handler:188: Received signal 2, shutting down...
|
||||
2025-09-19 21:18:41,878 [INFO ] DuckHuntBot - run:899: Main loop cancelled
|
||||
2025-09-19 21:18:41,879 [INFO ] DuckHuntBot - run:909: Shutting down bot...
|
||||
2025-09-19 21:18:41,879 [INFO ] DuckHuntBot - message_loop:960: Message loop cancelled
|
||||
2025-09-19 21:18:41,880 [INFO ] DuckHuntBot - message_loop:966: Message loop ended
|
||||
2025-09-19 21:18:41,881 [INFO ] DuckHuntBot.Game - spawn_ducks:250: Duck spawning loop cancelled
|
||||
2025-09-19 21:18:41,881 [INFO ] DuckHuntBot.Game - duck_timeout_checker:295: Duck timeout checker cancelled
|
||||
2025-09-19 21:18:41,902 [INFO ] DuckHuntBot - run:925: Database saved
|
||||
2025-09-19 21:18:41,987 [ERROR ] DuckHuntBot - run:937: Error closing connection: [SSL: APPLICATION_DATA_AFTER_CLOSE_NOTIFY] application data after close notify (_ssl.c:2685)
|
||||
2025-09-19 21:18:41,988 [INFO ] DuckHuntBot - run:939: Bot shutdown complete
|
||||
2025-09-19 21:19:02,499 [INFO ] DuckHuntBot - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:19:02,500 [INFO ] SASL - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:19:02,501 [INFO ] DuckHuntBot - main:26: 🦆 Starting DuckHunt Bot...
|
||||
2025-09-19 21:19:02,504 [INFO ] DuckHuntBot.DB - load_database:40: Loaded 22 players from duckhunt.json
|
||||
2025-09-19 21:19:02,778 [INFO ] DuckHuntBot - connect:92: Connected to irc.rizon.net:6697
|
||||
2025-09-19 21:19:03,277 [INFO ] DuckHuntBot - handle_message:205: Successfully registered with IRC server
|
||||
2025-09-19 21:19:12,233 [INFO ] DuckHuntBot - signal_handler:188: Received signal 15, shutting down...
|
||||
2025-09-19 21:19:12,323 [INFO ] DuckHuntBot - run:1012: Main loop cancelled
|
||||
2025-09-19 21:19:12,324 [INFO ] DuckHuntBot - run:1022: Shutting down bot...
|
||||
2025-09-19 21:19:12,324 [INFO ] DuckHuntBot - message_loop:1073: Message loop cancelled
|
||||
2025-09-19 21:19:12,324 [INFO ] DuckHuntBot - message_loop:1079: Message loop ended
|
||||
2025-09-19 21:19:12,324 [INFO ] DuckHuntBot.Game - duck_timeout_checker:306: Duck timeout checker cancelled
|
||||
2025-09-19 21:19:12,324 [INFO ] DuckHuntBot.Game - spawn_ducks:261: Duck spawning loop cancelled
|
||||
2025-09-19 21:19:12,327 [INFO ] DuckHuntBot - run:1038: Database saved
|
||||
2025-09-19 21:19:12,383 [ERROR ] DuckHuntBot - run:1050: Error closing connection: [SSL: APPLICATION_DATA_AFTER_CLOSE_NOTIFY] application data after close notify (_ssl.c:2685)
|
||||
2025-09-19 21:19:12,383 [INFO ] DuckHuntBot - run:1052: Bot shutdown complete
|
||||
2025-09-19 21:19:43,902 [INFO ] DuckHuntBot - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:19:43,903 [INFO ] SASL - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:19:43,903 [INFO ] DuckHuntBot - main:26: 🦆 Starting DuckHunt Bot...
|
||||
2025-09-19 21:19:43,904 [INFO ] DuckHuntBot.DB - load_database:40: Loaded 22 players from duckhunt.json
|
||||
2025-09-19 21:19:44,156 [INFO ] DuckHuntBot - connect:92: Connected to irc.rizon.net:6697
|
||||
2025-09-19 21:19:44,322 [INFO ] DuckHuntBot - handle_message:205: Successfully registered with IRC server
|
||||
2025-09-19 21:19:56,638 [INFO ] DuckHuntBot - signal_handler:188: Received signal 2, shutting down...
|
||||
2025-09-19 21:19:56,731 [INFO ] DuckHuntBot - run:1012: Main loop cancelled
|
||||
2025-09-19 21:19:56,731 [INFO ] DuckHuntBot - run:1022: Shutting down bot...
|
||||
2025-09-19 21:19:56,732 [INFO ] DuckHuntBot - message_loop:1073: Message loop cancelled
|
||||
2025-09-19 21:19:56,732 [INFO ] DuckHuntBot - message_loop:1079: Message loop ended
|
||||
2025-09-19 21:19:56,733 [INFO ] DuckHuntBot.Game - spawn_ducks:261: Duck spawning loop cancelled
|
||||
2025-09-19 21:19:56,733 [INFO ] DuckHuntBot.Game - duck_timeout_checker:306: Duck timeout checker cancelled
|
||||
2025-09-19 21:19:56,738 [INFO ] DuckHuntBot - run:1038: Database saved
|
||||
2025-09-19 21:19:56,794 [ERROR ] DuckHuntBot - run:1050: Error closing connection: [SSL: APPLICATION_DATA_AFTER_CLOSE_NOTIFY] application data after close notify (_ssl.c:2685)
|
||||
2025-09-19 21:19:56,795 [INFO ] DuckHuntBot - run:1052: Bot shutdown complete
|
||||
2025-09-19 21:21:50,220 [INFO ] DuckHuntBot - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:21:50,221 [INFO ] SASL - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:21:50,221 [INFO ] DuckHuntBot - main:26: 🦆 Starting DuckHunt Bot...
|
||||
2025-09-19 21:21:50,224 [INFO ] DuckHuntBot.DB - load_database:40: Loaded 22 players from duckhunt.json
|
||||
2025-09-19 21:21:50,526 [INFO ] DuckHuntBot - connect:92: Connected to irc.rizon.net:6697
|
||||
2025-09-19 21:21:52,130 [INFO ] DuckHuntBot - handle_message:205: Successfully registered with IRC server
|
||||
2025-09-19 21:26:33,004 [INFO ] DuckHuntBot - signal_handler:188: Received signal 2, shutting down...
|
||||
2025-09-19 21:26:33,069 [INFO ] DuckHuntBot - run:1012: Main loop cancelled
|
||||
2025-09-19 21:26:33,070 [INFO ] DuckHuntBot - run:1022: Shutting down bot...
|
||||
2025-09-19 21:26:33,070 [INFO ] DuckHuntBot - message_loop:1073: Message loop cancelled
|
||||
2025-09-19 21:26:33,071 [INFO ] DuckHuntBot - message_loop:1079: Message loop ended
|
||||
2025-09-19 21:26:33,074 [INFO ] DuckHuntBot.Game - spawn_ducks:261: Duck spawning loop cancelled
|
||||
2025-09-19 21:26:33,075 [INFO ] DuckHuntBot.Game - duck_timeout_checker:306: Duck timeout checker cancelled
|
||||
2025-09-19 21:26:33,097 [INFO ] DuckHuntBot - run:1038: Database saved
|
||||
2025-09-19 21:26:33,184 [ERROR ] DuckHuntBot - run:1050: Error closing connection: [SSL: APPLICATION_DATA_AFTER_CLOSE_NOTIFY] application data after close notify (_ssl.c:2685)
|
||||
2025-09-19 21:26:33,185 [INFO ] DuckHuntBot - run:1052: Bot shutdown complete
|
||||
2025-09-19 21:27:12,029 [INFO ] DuckHuntBot - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:27:12,030 [INFO ] SASL - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:27:12,030 [INFO ] DuckHuntBot - main:26: 🦆 Starting DuckHunt Bot...
|
||||
2025-09-19 21:27:12,033 [INFO ] DuckHuntBot.DB - load_database:40: Loaded 22 players from duckhunt.json
|
||||
2025-09-19 21:27:12,302 [INFO ] DuckHuntBot - connect:92: Connected to irc.rizon.net:6697
|
||||
2025-09-19 21:27:12,685 [INFO ] DuckHuntBot - handle_message:205: Successfully registered with IRC server
|
||||
2025-09-19 21:27:21,684 [INFO ] DuckHuntBot - signal_handler:188: Received signal 15, shutting down...
|
||||
2025-09-19 21:27:21,734 [INFO ] DuckHuntBot - run:1000: Main loop cancelled
|
||||
2025-09-19 21:27:21,734 [INFO ] DuckHuntBot - run:1010: Shutting down bot...
|
||||
2025-09-19 21:27:21,735 [INFO ] DuckHuntBot.Game - duck_timeout_checker:307: Duck timeout checker cancelled
|
||||
2025-09-19 21:27:21,735 [INFO ] DuckHuntBot - message_loop:1061: Message loop cancelled
|
||||
2025-09-19 21:27:21,735 [INFO ] DuckHuntBot - message_loop:1067: Message loop ended
|
||||
2025-09-19 21:27:21,735 [INFO ] DuckHuntBot.Game - spawn_ducks:261: Duck spawning loop cancelled
|
||||
2025-09-19 21:27:21,743 [INFO ] DuckHuntBot - run:1026: Database saved
|
||||
2025-09-19 21:27:21,799 [ERROR ] DuckHuntBot - run:1038: Error closing connection: [SSL: APPLICATION_DATA_AFTER_CLOSE_NOTIFY] application data after close notify (_ssl.c:2685)
|
||||
2025-09-19 21:27:21,800 [INFO ] DuckHuntBot - run:1040: Bot shutdown complete
|
||||
2025-09-19 21:27:36,083 [INFO ] DuckHuntBot - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:27:36,084 [INFO ] SASL - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:27:36,084 [INFO ] DuckHuntBot - main:26: 🦆 Starting DuckHunt Bot...
|
||||
2025-09-19 21:27:36,086 [INFO ] DuckHuntBot.DB - load_database:40: Loaded 22 players from duckhunt.json
|
||||
2025-09-19 21:27:36,338 [INFO ] DuckHuntBot - connect:92: Connected to irc.rizon.net:6697
|
||||
2025-09-19 21:27:36,504 [INFO ] DuckHuntBot - handle_message:205: Successfully registered with IRC server
|
||||
2025-09-19 21:30:40,213 [INFO ] DuckHuntBot - signal_handler:188: Received signal 2, shutting down...
|
||||
2025-09-19 21:30:40,256 [INFO ] DuckHuntBot - run:1000: Main loop cancelled
|
||||
2025-09-19 21:30:40,257 [INFO ] DuckHuntBot - run:1010: Shutting down bot...
|
||||
2025-09-19 21:30:40,257 [INFO ] DuckHuntBot - message_loop:1061: Message loop cancelled
|
||||
2025-09-19 21:30:40,257 [INFO ] DuckHuntBot - message_loop:1067: Message loop ended
|
||||
2025-09-19 21:30:40,257 [INFO ] DuckHuntBot.Game - spawn_ducks:261: Duck spawning loop cancelled
|
||||
2025-09-19 21:30:40,257 [INFO ] DuckHuntBot.Game - duck_timeout_checker:307: Duck timeout checker cancelled
|
||||
2025-09-19 21:30:40,262 [INFO ] DuckHuntBot - run:1026: Database saved
|
||||
2025-09-19 21:30:40,318 [ERROR ] DuckHuntBot - run:1038: Error closing connection: [SSL: APPLICATION_DATA_AFTER_CLOSE_NOTIFY] application data after close notify (_ssl.c:2685)
|
||||
2025-09-19 21:30:40,319 [INFO ] DuckHuntBot - run:1040: Bot shutdown complete
|
||||
2025-09-19 21:42:07,003 [INFO ] DuckHuntBot - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:42:07,005 [INFO ] SASL - setup_logger:65: Enhanced logging system initialized with file rotation
|
||||
2025-09-19 21:42:07,005 [INFO ] DuckHuntBot - main:26: 🦆 Starting DuckHunt Bot...
|
||||
2025-09-19 21:42:07,009 [INFO ] DuckHuntBot.DB - load_database:40: Loaded 22 players from duckhunt.json
|
||||
2025-09-19 21:42:07,297 [INFO ] DuckHuntBot - connect:95: Connected to irc.rizon.net:6697
|
||||
2025-09-19 21:42:07,739 [INFO ] DuckHuntBot - handle_message:208: Successfully registered with IRC server
|
||||
2025-09-19 21:42:58,096 [INFO ] DuckHuntBot - signal_handler:191: Received signal 2, shutting down...
|
||||
2025-09-19 21:42:58,194 [INFO ] DuckHuntBot.Game - spawn_ducks:261: Duck spawning loop cancelled
|
||||
2025-09-19 21:42:58,194 [INFO ] DuckHuntBot - run:1128: Main loop cancelled
|
||||
2025-09-19 21:42:58,194 [INFO ] DuckHuntBot - run:1138: Shutting down bot...
|
||||
2025-09-19 21:42:58,195 [INFO ] DuckHuntBot.Game - duck_timeout_checker:307: Duck timeout checker cancelled
|
||||
2025-09-19 21:42:58,195 [INFO ] DuckHuntBot - message_loop:1189: Message loop cancelled
|
||||
2025-09-19 21:42:58,195 [INFO ] DuckHuntBot - message_loop:1195: Message loop ended
|
||||
2025-09-19 21:42:58,198 [INFO ] DuckHuntBot - run:1154: Database saved
|
||||
2025-09-19 21:42:58,261 [ERROR ] DuckHuntBot - run:1166: Error closing connection: [SSL: APPLICATION_DATA_AFTER_CLOSE_NOTIFY] application data after close notify (_ssl.c:2685)
|
||||
2025-09-19 21:42:58,262 [INFO ] DuckHuntBot - run:1168: Bot shutdown complete
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Main entry point for DuckHunt Bot
|
||||
DuckHunt IRC Bot - Main Entry Point
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
@@ -11,14 +11,18 @@ import os
|
||||
# Add src directory to path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
|
||||
|
||||
from src.duckhuntbot import IRCBot
|
||||
from src.duckhuntbot import DuckHuntBot
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point for DuckHunt Bot"""
|
||||
try:
|
||||
# Load configuration
|
||||
with open('config.json') as f:
|
||||
config = json.load(f)
|
||||
|
||||
bot = IRCBot(config)
|
||||
# Create and run bot
|
||||
bot = DuckHuntBot(config)
|
||||
bot.logger.info("🦆 Starting DuckHunt Bot...")
|
||||
|
||||
# Run the bot
|
||||
@@ -33,5 +37,6 @@ def main():
|
||||
print(f"❌ Error: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,272 +0,0 @@
|
||||
# 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 }
|
||||
```
|
||||
@@ -1,172 +0,0 @@
|
||||
# 🦆 DuckHunt IRC Bot
|
||||
|
||||
A feature-rich IRC game bot where players hunt ducks, upgrade weapons, trade items, and compete on leaderboards!
|
||||
|
||||
## 🚀 Features
|
||||
|
||||
### 🎯 Core Game Mechanics
|
||||
- **Different Duck Types**: Common, Rare, Golden, and Armored ducks with varying rewards
|
||||
- **Weapon System**: Multiple weapon types (Basic Gun, Shotgun, Rifle) with durability
|
||||
- **Ammunition Types**: Standard, Rubber Bullets, Explosive Rounds
|
||||
- **Weapon Attachments**: Laser Sight, Extended Magazine, Bipod
|
||||
- **Accuracy & Reliability**: Skill-based hit/miss and reload failure mechanics
|
||||
|
||||
### 🏦 Economy System
|
||||
- **Shop**: Buy/sell weapons, attachments, and upgrades
|
||||
- **Banking**: Deposit coins for interest, take loans
|
||||
- **Trading**: Trade coins and items with other players
|
||||
- **Insurance**: Protect your equipment from damage
|
||||
- **Hunting Licenses**: Unlock premium features and bonuses
|
||||
|
||||
### 👤 Player Progression
|
||||
- **Hunter Levels**: Gain XP and level up for better abilities
|
||||
- **Account System**: Register accounts with password authentication
|
||||
- **Multiple Auth Methods**: Nick-based, hostmask, or registered account
|
||||
- **Persistent Stats**: All progress saved to SQLite database
|
||||
|
||||
### 🏆 Social Features
|
||||
- **Leaderboards**: Compete for top rankings
|
||||
- **Duck Alerts**: Get notified when rare ducks spawn
|
||||
- **Sabotage**: Interfere with other players (for a cost!)
|
||||
- **Comprehensive Help**: Detailed command reference
|
||||
|
||||
## 📋 Requirements
|
||||
|
||||
- Python 3.7+
|
||||
- asyncio support
|
||||
- SQLite3 (included with Python)
|
||||
|
||||
## 🛠️ Installation
|
||||
|
||||
1. Clone or download the bot files
|
||||
2. Edit `config.json` with your IRC server details:
|
||||
```json
|
||||
{
|
||||
"server": "irc.libera.chat",
|
||||
"port": 6697,
|
||||
"nick": "DuckHuntBot",
|
||||
"channels": ["#yourchannel"],
|
||||
"ssl": true,
|
||||
"sasl": false,
|
||||
"password": "",
|
||||
"duck_spawn_min": 60,
|
||||
"duck_spawn_max": 300
|
||||
}
|
||||
```
|
||||
|
||||
3. Test the bot:
|
||||
```bash
|
||||
python test_bot.py
|
||||
```
|
||||
|
||||
4. Run the bot:
|
||||
```bash
|
||||
python duckhunt.py
|
||||
```
|
||||
|
||||
## 🎮 Commands
|
||||
|
||||
### 🎯 Hunting
|
||||
- `!bang` - Shoot at a duck (accuracy-based hit/miss)
|
||||
- `!reload` - Reload weapon (can fail based on reliability)
|
||||
- `!catch` - Catch a duck with your hands
|
||||
- `!bef` - Befriend a duck instead of shooting
|
||||
|
||||
### 🛒 Economy
|
||||
- `!shop` - View available items
|
||||
- `!buy <number>` - Purchase items
|
||||
- `!sell <number>` - Sell equipment
|
||||
- `!bank` - Banking services
|
||||
- `!trade <player> <item> <amount>` - Trade with others
|
||||
|
||||
### 📊 Stats & Info
|
||||
- `!stats` - Detailed combat statistics
|
||||
- `!duckstats` - Personal hunting record
|
||||
- `!leaderboard` - Top players ranking
|
||||
- `!license` - Hunting license management
|
||||
|
||||
### ⚙️ Settings
|
||||
- `!alerts` - Toggle duck spawn notifications
|
||||
- `!help` - Complete command reference
|
||||
|
||||
### 🔐 Account System
|
||||
- `/msg BotNick register <username> <password>` - Register account
|
||||
- `/msg BotNick identify <username> <password>` - Login to account
|
||||
|
||||
### 🎮 Advanced
|
||||
- `!sabotage <player>` - Sabotage another hunter's weapon
|
||||
|
||||
## 🗂️ File Structure
|
||||
|
||||
```
|
||||
duckhunt/
|
||||
├── src/
|
||||
│ ├── duckhuntbot.py # Main IRC bot logic
|
||||
│ ├── game.py # Game mechanics and commands
|
||||
│ ├── db.py # SQLite database handling
|
||||
│ ├── auth.py # Authentication system
|
||||
│ ├── items.py # Duck types, weapons, attachments
|
||||
│ ├── logging_utils.py # Colored logging setup
|
||||
│ └── utils.py # IRC message parsing
|
||||
├── config.json # Bot configuration
|
||||
├── duckhunt.py # Main entry point
|
||||
├── test_bot.py # Test script
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## 🎯 Game Balance
|
||||
|
||||
### Duck Types & Rewards
|
||||
- **Common Duck** 🦆: 1 coin, 10 XP (70% spawn rate)
|
||||
- **Rare Duck** 🦆✨: 3 coins, 25 XP (20% spawn rate)
|
||||
- **Golden Duck** 🥇🦆: 10 coins, 50 XP (8% spawn rate)
|
||||
- **Armored Duck** 🛡️🦆: 15 coins, 75 XP (2% spawn rate, 3 health)
|
||||
|
||||
### Weapon Stats
|
||||
- **Basic Gun**: 0% accuracy bonus, 100 durability, 1 attachment slot
|
||||
- **Shotgun**: -10% accuracy, 80 durability, 2 slots, spread shot
|
||||
- **Rifle**: +20% accuracy, 120 durability, 3 slots
|
||||
|
||||
### Progression
|
||||
- Players start with 100 coins and basic stats
|
||||
- Level up by gaining XP from successful hunts
|
||||
- Unlock better equipment and abilities as you progress
|
||||
|
||||
## 🔧 Configuration
|
||||
|
||||
Edit `config.json` to customize:
|
||||
- IRC server and channels
|
||||
- Duck spawn timing (min/max seconds)
|
||||
- SSL and SASL authentication
|
||||
- Bot nickname
|
||||
|
||||
## 🛡️ Security
|
||||
|
||||
- Passwords are hashed with PBKDF2
|
||||
- Account data stored separately from temporary nick data
|
||||
- Multiple authentication methods supported
|
||||
- Database uses prepared statements to prevent injection
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
1. **Bot won't connect**: Check server/port in config.json
|
||||
2. **Database errors**: Ensure write permissions in bot directory
|
||||
3. **Commands not working**: Verify bot has joined the channel
|
||||
4. **Test failures**: Run `python test_bot.py` to diagnose issues
|
||||
|
||||
## 🎖️ Contributing
|
||||
|
||||
Feel free to add new features:
|
||||
- More duck types and weapons
|
||||
- Additional mini-games
|
||||
- Seasonal events
|
||||
- Guild/team systems
|
||||
- Advanced trading mechanics
|
||||
|
||||
## 📄 License
|
||||
|
||||
This bot is provided as-is for educational and entertainment purposes.
|
||||
|
||||
---
|
||||
|
||||
🦆 **Happy Hunting!** 🦆
|
||||
Binary file not shown.
@@ -1,216 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
#!/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())
|
||||
Binary file not shown.
@@ -1,559 +0,0 @@
|
||||
[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
|
||||
2025-09-13 13:06:23,192 [INFO ] DuckHuntBot - setup_logger:78: Enhanced logging system initialized with file rotation
|
||||
2025-09-13 13:06:23,212 [INFO ] DuckHuntBot - load_database:401: Loaded 19 players from duckhunt.json
|
||||
2025-09-13 13:07:06,713 [INFO ] DuckHuntBot - setup_logger:79: Enhanced logging system initialized with file rotation
|
||||
2025-09-13 13:07:06,715 [INFO ] DuckHuntBot - load_database:402: Loaded 19 players from duckhunt.json
|
||||
2025-09-13 13:07:20,053 [INFO ] DuckHuntBot - setup_logger:79: Enhanced logging system initialized with file rotation
|
||||
2025-09-13 13:07:20,054 [INFO ] DuckHuntBot - load_database:402: Loaded 19 players from duckhunt.json
|
||||
2025-09-13 13:17:14,822 [INFO ] DuckHuntBot - setup_logger:79: Enhanced logging system initialized with file rotation
|
||||
2025-09-13 13:17:14,824 [INFO ] DuckHuntBot - load_database:402: Loaded 19 players from duckhunt.json
|
||||
2025-09-13 13:22:07,954 [INFO ] DuckHuntBot - setup_logger:79: Enhanced logging system initialized with file rotation
|
||||
2025-09-13 13:22:07,956 [INFO ] DuckHuntBot - load_database:402: Loaded 19 players from duckhunt.json
|
||||
2025-09-13 14:45:32,890 [INFO ] DuckHuntBot - setup_logger:79: Enhanced logging system initialized with file rotation
|
||||
2025-09-13 14:45:32,893 [INFO ] DuckHuntBot - load_database:402: Loaded 19 players from duckhunt.json
|
||||
2025-09-13 14:47:29,566 [INFO ] DuckHuntBot - setup_logger:79: Enhanced logging system initialized with file rotation
|
||||
2025-09-13 14:47:29,568 [INFO ] DuckHuntBot - load_database:403: Loaded 21 players from duckhunt.json
|
||||
2025-09-13 15:01:52,979 [INFO ] DuckHuntBot - setup_logger:79: Enhanced logging system initialized with file rotation
|
||||
2025-09-13 15:01:52,981 [INFO ] DuckHuntBot - load_database:402: Loaded 21 players from duckhunt.json
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,108 +0,0 @@
|
||||
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}")
|
||||
@@ -1,97 +0,0 @@
|
||||
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))
|
||||
@@ -1,277 +0,0 @@
|
||||
#!/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
|
||||
@@ -1,566 +0,0 @@
|
||||
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)
|
||||
@@ -1,124 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
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
|
||||
@@ -1,11 +0,0 @@
|
||||
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
|
||||
@@ -1,167 +0,0 @@
|
||||
#!/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)
|
||||
BIN
src/__pycache__/db.cpython-312.pyc
Normal file
BIN
src/__pycache__/db.cpython-312.pyc
Normal file
Binary file not shown.
BIN
src/__pycache__/duckhuntbot.cpython-312.pyc
Normal file
BIN
src/__pycache__/duckhuntbot.cpython-312.pyc
Normal file
Binary file not shown.
BIN
src/__pycache__/game.cpython-312.pyc
Normal file
BIN
src/__pycache__/game.cpython-312.pyc
Normal file
Binary file not shown.
BIN
src/__pycache__/logging_utils.cpython-312.pyc
Normal file
BIN
src/__pycache__/logging_utils.cpython-312.pyc
Normal file
Binary file not shown.
Binary file not shown.
BIN
src/__pycache__/utils.cpython-312.pyc
Normal file
BIN
src/__pycache__/utils.cpython-312.pyc
Normal file
Binary file not shown.
189
src/db.py
Normal file
189
src/db.py
Normal file
@@ -0,0 +1,189 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Database functionality for DuckHunt Bot
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
|
||||
class DuckDB:
|
||||
"""Database management for DuckHunt Bot"""
|
||||
|
||||
def __init__(self, db_file="duckhunt.json"):
|
||||
self.db_file = db_file
|
||||
self.players = {}
|
||||
self._save_pending = False
|
||||
self.logger = logging.getLogger('DuckHuntBot.DB')
|
||||
|
||||
def get_config(self, path, default=None):
|
||||
"""Helper method to get config values (needs to be set by bot)"""
|
||||
# This will be set by the main bot class
|
||||
if hasattr(self, '_config_getter'):
|
||||
return self._config_getter(path, default)
|
||||
return default
|
||||
|
||||
def set_config_getter(self, config_getter):
|
||||
"""Set the config getter function from the main bot"""
|
||||
self._config_getter = config_getter
|
||||
|
||||
def load_database(self):
|
||||
"""Load player data from JSON file"""
|
||||
if os.path.exists(self.db_file):
|
||||
try:
|
||||
with open(self.db_file, 'r') as f:
|
||||
data = json.load(f)
|
||||
self.players = data.get('players', {})
|
||||
self.logger.info(f"Loaded {len(self.players)} players from {self.db_file}")
|
||||
except (json.JSONDecodeError, IOError) as e:
|
||||
self.logger.error(f"Error loading database: {e}")
|
||||
self.players = {}
|
||||
else:
|
||||
self.players = {}
|
||||
self.logger.info(f"Created new database: {self.db_file}")
|
||||
|
||||
def save_database(self):
|
||||
"""Save all player data to JSON file with error handling"""
|
||||
try:
|
||||
# Atomic write to prevent corruption
|
||||
temp_file = f"{self.db_file}.tmp"
|
||||
data = {
|
||||
'players': self.players,
|
||||
'last_save': str(time.time())
|
||||
}
|
||||
with open(temp_file, 'w') as f:
|
||||
json.dump(data, f, indent=2)
|
||||
|
||||
# Atomic rename to replace old file
|
||||
os.replace(temp_file, self.db_file)
|
||||
|
||||
except IOError as e:
|
||||
self.logger.error(f"Error saving database: {e}")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Unexpected database save error: {e}")
|
||||
|
||||
def _get_starting_accuracy(self):
|
||||
"""Get starting accuracy with optional randomization"""
|
||||
base_accuracy = self.get_config('new_players.starting_accuracy', 65) or 65
|
||||
if self.get_config('new_players.random_stats.enabled', False):
|
||||
import random
|
||||
variance = self.get_config('new_players.random_stats.accuracy_variance', 10) or 10
|
||||
return max(10, min(95, base_accuracy + random.randint(-variance, variance)))
|
||||
return base_accuracy
|
||||
|
||||
def _get_starting_reliability(self):
|
||||
"""Get starting reliability with optional randomization"""
|
||||
base_reliability = self.get_config('new_players.starting_reliability', 70) or 70
|
||||
if self.get_config('new_players.random_stats.enabled', False):
|
||||
import random
|
||||
variance = self.get_config('new_players.random_stats.reliability_variance', 10) or 10
|
||||
return max(10, min(95, base_reliability + random.randint(-variance, variance)))
|
||||
return base_reliability
|
||||
|
||||
def get_player(self, user):
|
||||
"""Get player data, creating if doesn't exist"""
|
||||
if '!' not in user:
|
||||
nick = user.lower()
|
||||
else:
|
||||
nick = user.split('!')[0].lower()
|
||||
|
||||
if nick in self.players:
|
||||
player = self.players[nick]
|
||||
# Ensure backward compatibility by adding missing fields
|
||||
self._ensure_player_fields(player)
|
||||
return player
|
||||
else:
|
||||
return self.create_player(nick)
|
||||
|
||||
def create_player(self, nick):
|
||||
"""Create a new player with default stats"""
|
||||
player = {
|
||||
'shots': 6,
|
||||
'max_shots': 6,
|
||||
'chargers': 2,
|
||||
'max_chargers': 2,
|
||||
'reload_time': 5.0,
|
||||
'ducks_shot': 0,
|
||||
'ducks_befriended': 0,
|
||||
'accuracy_bonus': 0,
|
||||
'xp_bonus': 0,
|
||||
'charm_bonus': 0,
|
||||
'exp': 0,
|
||||
'money': 100,
|
||||
'last_hunt': 0,
|
||||
'last_reload': 0,
|
||||
'level': 1,
|
||||
'inventory': {},
|
||||
'ignored_users': [],
|
||||
# Gun mechanics (eggdrop style)
|
||||
'jammed': False,
|
||||
'jammed_count': 0,
|
||||
'total_ammo_used': 0,
|
||||
'shot_at': 0,
|
||||
'wild_shots': 0,
|
||||
'accuracy': self._get_starting_accuracy(),
|
||||
'reliability': self._get_starting_reliability(),
|
||||
'gun_confiscated': False,
|
||||
'confiscated_count': 0
|
||||
}
|
||||
|
||||
self.players[nick] = player
|
||||
self.logger.info(f"Created new player: {nick}")
|
||||
return player
|
||||
|
||||
def _ensure_player_fields(self, player):
|
||||
"""Ensure player has all required fields for backward compatibility"""
|
||||
required_fields = {
|
||||
'shots': player.get('ammo', 6), # Map old 'ammo' to 'shots'
|
||||
'max_shots': player.get('max_ammo', 6), # Map old 'max_ammo' to 'max_shots'
|
||||
'chargers': player.get('chargers', 2), # Map old 'chargers' (magazines)
|
||||
'max_chargers': player.get('max_chargers', 2), # Map old 'max_chargers'
|
||||
'reload_time': 5.0,
|
||||
'ducks_shot': player.get('caught', 0), # Map old 'caught' to 'ducks_shot'
|
||||
'ducks_befriended': player.get('befriended', 0), # Use existing befriended count
|
||||
'accuracy_bonus': 0,
|
||||
'xp_bonus': 0,
|
||||
'charm_bonus': 0,
|
||||
'exp': player.get('xp', 0), # Map old 'xp' to 'exp'
|
||||
'money': player.get('coins', 100), # Map old 'coins' to 'money'
|
||||
'last_hunt': 0,
|
||||
'last_reload': 0,
|
||||
'level': 1,
|
||||
'inventory': {},
|
||||
'ignored_users': [],
|
||||
# Gun mechanics (eggdrop style)
|
||||
'jammed': False,
|
||||
'jammed_count': player.get('jammed_count', 0),
|
||||
'total_ammo_used': player.get('total_ammo_used', 0),
|
||||
'shot_at': player.get('shot_at', 0),
|
||||
'wild_shots': player.get('wild_shots', 0),
|
||||
'accuracy': player.get('accuracy', 65),
|
||||
'reliability': player.get('reliability', 70),
|
||||
'gun_confiscated': player.get('gun_confiscated', False),
|
||||
'confiscated_count': player.get('confiscated_count', 0)
|
||||
}
|
||||
|
||||
for field, default_value in required_fields.items():
|
||||
if field not in player:
|
||||
player[field] = default_value
|
||||
|
||||
def save_player(self, user):
|
||||
"""Save player data - batch saves for performance"""
|
||||
if not self._save_pending:
|
||||
self._save_pending = True
|
||||
# Schedule delayed save to batch multiple writes
|
||||
asyncio.create_task(self._delayed_save())
|
||||
|
||||
async def _delayed_save(self):
|
||||
"""Batch save to reduce disk I/O"""
|
||||
await asyncio.sleep(0.5) # Small delay to batch saves
|
||||
try:
|
||||
self.save_database()
|
||||
self.logger.debug("Database batch save completed")
|
||||
except Exception as e:
|
||||
self.logger.error(f"Database batch save failed: {e}")
|
||||
finally:
|
||||
self._save_pending = False
|
||||
1195
src/duckhuntbot.py
Normal file
1195
src/duckhuntbot.py
Normal file
File diff suppressed because it is too large
Load Diff
326
src/game.py
Normal file
326
src/game.py
Normal file
@@ -0,0 +1,326 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Game mechanics for DuckHunt Bot
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import random
|
||||
import time
|
||||
import uuid
|
||||
import logging
|
||||
from typing import Dict, Any, Optional, List
|
||||
|
||||
|
||||
class DuckGame:
|
||||
"""Game mechanics and duck management"""
|
||||
|
||||
def __init__(self, bot, db):
|
||||
self.bot = bot
|
||||
self.db = db
|
||||
self.ducks = {} # Format: {channel: [{'alive': True, 'spawn_time': time, 'id': uuid}, ...]}
|
||||
self.logger = logging.getLogger('DuckHuntBot.Game')
|
||||
|
||||
# Colors for IRC messages
|
||||
self.colors = {
|
||||
'red': '\x0304',
|
||||
'green': '\x0303',
|
||||
'yellow': '\x0308',
|
||||
'blue': '\x0302',
|
||||
'cyan': '\x0311',
|
||||
'magenta': '\x0306',
|
||||
'white': '\x0300',
|
||||
'bold': '\x02',
|
||||
'reset': '\x03',
|
||||
'underline': '\x1f'
|
||||
}
|
||||
|
||||
def get_config(self, path, default=None):
|
||||
"""Helper method to get config values"""
|
||||
return self.bot.get_config(path, default)
|
||||
|
||||
def get_player_level(self, xp):
|
||||
"""Calculate player level from XP"""
|
||||
if xp < 0:
|
||||
return 0
|
||||
return int((xp ** 0.5) / 2) + 1
|
||||
|
||||
def get_xp_for_next_level(self, xp):
|
||||
"""Calculate XP needed for next level"""
|
||||
level = self.get_player_level(xp)
|
||||
return ((level * 2) ** 2) - xp
|
||||
|
||||
def calculate_penalty_by_level(self, base_penalty, xp):
|
||||
"""Reduce penalties for higher level players"""
|
||||
level = self.get_player_level(xp)
|
||||
return max(1, base_penalty - (level - 1))
|
||||
|
||||
def update_karma(self, player, event):
|
||||
"""Update player karma based on events"""
|
||||
if not self.get_config('karma.enabled', True):
|
||||
return
|
||||
|
||||
karma_changes = {
|
||||
'hit': self.get_config('karma.hit_bonus', 2),
|
||||
'golden_hit': self.get_config('karma.golden_hit_bonus', 5),
|
||||
'teamkill': -self.get_config('karma.teamkill_penalty', 10),
|
||||
'wild_shot': -self.get_config('karma.wild_shot_penalty', 3),
|
||||
'miss': -self.get_config('karma.miss_penalty', 1),
|
||||
'befriend_success': self.get_config('karma.befriend_success_bonus', 2),
|
||||
'befriend_fail': -self.get_config('karma.befriend_fail_penalty', 1)
|
||||
}
|
||||
|
||||
if event in karma_changes:
|
||||
player['karma'] = player.get('karma', 0) + karma_changes[event]
|
||||
|
||||
def is_sleep_time(self):
|
||||
"""Check if ducks should not spawn due to sleep hours"""
|
||||
sleep_hours = self.get_config('sleep_hours', [])
|
||||
if not sleep_hours or len(sleep_hours) != 2:
|
||||
return False
|
||||
|
||||
import datetime
|
||||
current_hour = datetime.datetime.now().hour
|
||||
start_hour, end_hour = sleep_hours
|
||||
|
||||
if start_hour <= end_hour:
|
||||
return start_hour <= current_hour <= end_hour
|
||||
else: # Crosses midnight
|
||||
return current_hour >= start_hour or current_hour <= end_hour
|
||||
|
||||
def calculate_gun_reliability(self, player):
|
||||
"""Calculate gun reliability with modifiers"""
|
||||
base_reliability = player.get('reliability', 70)
|
||||
# Add weapon modifiers, items, etc.
|
||||
return min(100, max(0, base_reliability))
|
||||
|
||||
def gun_jams(self, player):
|
||||
"""Check if gun jams (eggdrop style)"""
|
||||
# Base jamming probability is inverse of reliability
|
||||
reliability = player.get('reliability', 70)
|
||||
jam_chance = max(1, 101 - reliability) # Higher reliability = lower jam chance
|
||||
|
||||
# Additional factors that increase jam chance
|
||||
if player.get('total_ammo_used', 0) > 100:
|
||||
jam_chance += 2 # Gun gets more prone to jamming with use
|
||||
|
||||
if player.get('jammed_count', 0) > 5:
|
||||
jam_chance += 1 # Previously jammed guns are more prone to jamming
|
||||
|
||||
# Roll for jam (1-100, jam if roll <= jam_chance)
|
||||
return random.randint(1, 100) <= jam_chance
|
||||
|
||||
async def scare_other_ducks(self, channel, shot_duck_id):
|
||||
"""Scare other ducks when one is shot"""
|
||||
if channel not in self.ducks:
|
||||
return
|
||||
|
||||
for duck in self.ducks[channel][:]: # Copy list to avoid modification during iteration
|
||||
if duck['id'] != shot_duck_id and duck['alive']:
|
||||
# 30% chance to scare away other ducks
|
||||
if random.random() < 0.3:
|
||||
duck['alive'] = False
|
||||
self.ducks[channel].remove(duck)
|
||||
|
||||
async def scare_duck_on_miss(self, channel, target_duck):
|
||||
"""Scare duck when someone misses"""
|
||||
if target_duck and random.random() < 0.15: # 15% chance
|
||||
target_duck['alive'] = False
|
||||
if channel in self.ducks and target_duck in self.ducks[channel]:
|
||||
self.ducks[channel].remove(target_duck)
|
||||
|
||||
async def find_bushes_items(self, nick, channel, player):
|
||||
"""Find random items in bushes"""
|
||||
if not self.get_config('items.enabled', True):
|
||||
return
|
||||
|
||||
if random.random() < 0.1: # 10% chance
|
||||
items = [
|
||||
("a mirror", "mirror", "You can now deflect shots!"),
|
||||
("some sand", "sand", "Throw this to blind opponents!"),
|
||||
("a rusty bullet", None, "It's too rusty to use..."),
|
||||
("some bread crumbs", "bread", "Feed ducks to make them friendly!"),
|
||||
]
|
||||
|
||||
found_item, item_key, message = random.choice(items)
|
||||
|
||||
if item_key and item_key in player:
|
||||
player[item_key] = player.get(item_key, 0) + 1
|
||||
elif item_key in player:
|
||||
player[item_key] = player.get(item_key, 0) + 1
|
||||
|
||||
await self.bot.send_user_message(nick, channel,
|
||||
f"You found {found_item} in the bushes! {message}")
|
||||
|
||||
def get_duck_spawn_message(self):
|
||||
"""Get random duck spawn message (eggdrop style)"""
|
||||
messages = [
|
||||
"-.,¸¸.-·°'`'°·-.,¸¸.-·°'`'°· \\_O< QUACK",
|
||||
"-.,¸¸.-·°'`'°·-.,¸¸.-·°'`'°· \\_o< QUACK!",
|
||||
"-.,¸¸.-·°'`'°·-.,¸¸.-·°'`'°· \\_O< QUAAACK!",
|
||||
"-.,¸¸.-·°'`'°·-.,¸¸.-·°'`'°· \\_ö< Quack?",
|
||||
"-.,¸¸.-·°'`'°·-.,¸¸.-·°'`'°· \\_O< *QUACK*"
|
||||
]
|
||||
return random.choice(messages)
|
||||
|
||||
async def spawn_duck_now(self, channel, force_golden=False):
|
||||
"""Spawn a duck immediately in the specified channel"""
|
||||
if channel not in self.ducks:
|
||||
self.ducks[channel] = []
|
||||
|
||||
max_ducks = self.get_config('max_ducks_per_channel', 3)
|
||||
if len([d for d in self.ducks[channel] if d['alive']]) >= max_ducks:
|
||||
self.logger.debug(f"Max ducks already in {channel}")
|
||||
return
|
||||
|
||||
# Determine duck type
|
||||
if force_golden:
|
||||
duck_type = "golden"
|
||||
else:
|
||||
rand = random.random()
|
||||
if rand < 0.02:
|
||||
duck_type = "armored"
|
||||
elif rand < 0.10:
|
||||
duck_type = "golden"
|
||||
elif rand < 0.30:
|
||||
duck_type = "rare"
|
||||
elif rand < 0.40:
|
||||
duck_type = "fast"
|
||||
else:
|
||||
duck_type = "normal"
|
||||
|
||||
# Get duck configuration
|
||||
duck_config = self.get_config(f'duck_types.{duck_type}', {})
|
||||
if not duck_config.get('enabled', True):
|
||||
duck_type = "normal"
|
||||
duck_config = self.get_config('duck_types.normal', {})
|
||||
|
||||
# Create duck
|
||||
duck = {
|
||||
'id': str(uuid.uuid4())[:8],
|
||||
'type': duck_type,
|
||||
'alive': True,
|
||||
'spawn_time': time.time(),
|
||||
'health': duck_config.get('health', 1),
|
||||
'max_health': duck_config.get('health', 1)
|
||||
}
|
||||
|
||||
self.ducks[channel].append(duck)
|
||||
|
||||
# Send spawn message
|
||||
messages = duck_config.get('messages', [self.get_duck_spawn_message()])
|
||||
spawn_message = random.choice(messages)
|
||||
|
||||
self.bot.send_message(channel, spawn_message)
|
||||
self.logger.info(f"Spawned {duck_type} duck in {channel}")
|
||||
|
||||
# Alert users who have alerts enabled
|
||||
await self.send_duck_alerts(channel, duck_type)
|
||||
|
||||
return duck
|
||||
|
||||
async def send_duck_alerts(self, channel, duck_type):
|
||||
"""Send alerts to users who have them enabled"""
|
||||
if not self.get_config('social.duck_alerts_enabled', True):
|
||||
return
|
||||
|
||||
# Implementation would iterate through players with alerts enabled
|
||||
# For now, just log
|
||||
self.logger.debug(f"Duck alerts for {duck_type} duck in {channel}")
|
||||
|
||||
async def spawn_ducks(self):
|
||||
"""Main duck spawning loop"""
|
||||
while not self.bot.shutdown_requested:
|
||||
try:
|
||||
if self.is_sleep_time():
|
||||
await asyncio.sleep(300) # Check every 5 minutes during sleep
|
||||
continue
|
||||
|
||||
for channel in self.bot.channels_joined:
|
||||
if self.bot.shutdown_requested:
|
||||
break
|
||||
|
||||
if channel not in self.ducks:
|
||||
self.ducks[channel] = []
|
||||
|
||||
# Clean up dead ducks
|
||||
self.ducks[channel] = [d for d in self.ducks[channel] if d['alive']]
|
||||
|
||||
max_ducks = self.get_config('max_ducks_per_channel', 3)
|
||||
alive_ducks = len([d for d in self.ducks[channel] if d['alive']])
|
||||
|
||||
if alive_ducks < max_ducks:
|
||||
min_spawn_time = self.get_config('duck_spawn_min', 1800)
|
||||
max_spawn_time = self.get_config('duck_spawn_max', 5400)
|
||||
|
||||
if random.random() < 0.1: # 10% chance each check
|
||||
await self.spawn_duck_now(channel)
|
||||
|
||||
await asyncio.sleep(random.randint(60, 300)) # Check every 1-5 minutes
|
||||
|
||||
except asyncio.CancelledError:
|
||||
self.logger.info("Duck spawning loop cancelled")
|
||||
break
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error in duck spawning: {e}")
|
||||
await asyncio.sleep(60)
|
||||
|
||||
async def duck_timeout_checker(self):
|
||||
"""Check for ducks that should timeout"""
|
||||
while not self.bot.shutdown_requested:
|
||||
try:
|
||||
current_time = time.time()
|
||||
|
||||
for channel in list(self.ducks.keys()):
|
||||
if self.bot.shutdown_requested:
|
||||
break
|
||||
|
||||
if channel not in self.ducks:
|
||||
continue
|
||||
|
||||
for duck in self.ducks[channel][:]: # Copy to avoid modification
|
||||
if not duck['alive']:
|
||||
continue
|
||||
|
||||
age = current_time - duck['spawn_time']
|
||||
min_timeout = self.get_config('duck_timeout_min', 45)
|
||||
max_timeout = self.get_config('duck_timeout_max', 75)
|
||||
|
||||
timeout = random.randint(min_timeout, max_timeout)
|
||||
|
||||
if age > timeout:
|
||||
duck['alive'] = False
|
||||
self.ducks[channel].remove(duck)
|
||||
|
||||
# Send timeout message (eggdrop style)
|
||||
timeout_messages = [
|
||||
"-.,¸¸.-·°'`'°·-.,¸¸.-·°'`'°· \\_o> The duck flew away!",
|
||||
"-.,¸¸.-·°'`'°·-.,¸¸.-·°'`'°· \\_O> *FLAP FLAP FLAP*",
|
||||
"-.,¸¸.-·°'`'°·-.,¸¸.-·°'`'°· \\_o> The duck got tired of waiting and left!",
|
||||
"-.,¸¸.-·°'`'°·-.,¸¸.-·°'`'°· \\_O> *KWAK* The duck escaped!"
|
||||
]
|
||||
self.bot.send_message(channel, random.choice(timeout_messages))
|
||||
self.logger.debug(f"Duck timed out in {channel}")
|
||||
|
||||
await asyncio.sleep(10) # Check every 10 seconds
|
||||
|
||||
except asyncio.CancelledError:
|
||||
self.logger.info("Duck timeout checker cancelled")
|
||||
break
|
||||
except Exception as e:
|
||||
self.logger.error(f"Error in duck timeout checker: {e}")
|
||||
await asyncio.sleep(30)
|
||||
|
||||
def get_alive_ducks(self, channel):
|
||||
"""Get list of alive ducks in channel"""
|
||||
if channel not in self.ducks:
|
||||
return []
|
||||
return [d for d in self.ducks[channel] if d['alive']]
|
||||
|
||||
def get_duck_by_id(self, channel, duck_id):
|
||||
"""Get duck by ID"""
|
||||
if channel not in self.ducks:
|
||||
return None
|
||||
for duck in self.ducks[channel]:
|
||||
if duck['id'] == duck_id and duck['alive']:
|
||||
return duck
|
||||
return None
|
||||
69
src/logging_utils.py
Normal file
69
src/logging_utils.py
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Logging utilities for DuckHunt Bot
|
||||
"""
|
||||
|
||||
import logging
|
||||
import logging.handlers
|
||||
|
||||
|
||||
class DetailedColorFormatter(logging.Formatter):
|
||||
"""Console formatter with color support"""
|
||||
COLORS = {
|
||||
'DEBUG': '\033[94m', # Blue
|
||||
'INFO': '\033[92m', # Green
|
||||
'WARNING': '\033[93m', # Yellow
|
||||
'ERROR': '\033[91m', # Red
|
||||
'CRITICAL': '\033[95m', # Magenta
|
||||
'ENDC': '\033[0m' # End color
|
||||
}
|
||||
|
||||
def format(self, record):
|
||||
color = self.COLORS.get(record.levelname, '')
|
||||
endc = self.COLORS['ENDC']
|
||||
msg = super().format(record)
|
||||
return f"{color}{msg}{endc}"
|
||||
|
||||
|
||||
class DetailedFileFormatter(logging.Formatter):
|
||||
"""File formatter with extra context but no colors"""
|
||||
def format(self, record):
|
||||
return super().format(record)
|
||||
|
||||
|
||||
def setup_logger(name="DuckHuntBot"):
|
||||
"""Setup logger with console and file handlers"""
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
# Clear any existing handlers
|
||||
logger.handlers.clear()
|
||||
|
||||
# Console handler with colors
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setLevel(logging.INFO)
|
||||
console_formatter = DetailedColorFormatter(
|
||||
'%(asctime)s [%(levelname)s] %(name)s: %(message)s'
|
||||
)
|
||||
console_handler.setFormatter(console_formatter)
|
||||
logger.addHandler(console_handler)
|
||||
|
||||
# File handler with rotation for detailed logs
|
||||
try:
|
||||
file_handler = logging.handlers.RotatingFileHandler(
|
||||
'duckhunt.log',
|
||||
maxBytes=10*1024*1024, # 10MB per file
|
||||
backupCount=5 # Keep 5 backup files
|
||||
)
|
||||
file_handler.setLevel(logging.DEBUG)
|
||||
file_formatter = DetailedFileFormatter(
|
||||
'%(asctime)s [%(levelname)-8s] %(name)s - %(funcName)s:%(lineno)d: %(message)s'
|
||||
)
|
||||
file_handler.setFormatter(file_formatter)
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
logger.info("Enhanced logging system initialized with file rotation")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to setup file logging: {e}")
|
||||
|
||||
return logger
|
||||
63
src/utils.py
Normal file
63
src/utils.py
Normal file
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Utility functions for DuckHunt Bot
|
||||
"""
|
||||
|
||||
import re
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class InputValidator:
|
||||
"""Input validation utilities"""
|
||||
|
||||
@staticmethod
|
||||
def validate_nickname(nick: str) -> bool:
|
||||
"""Validate IRC nickname format"""
|
||||
if not nick or len(nick) > 30:
|
||||
return False
|
||||
# RFC 2812 nickname pattern
|
||||
pattern = r'^[a-zA-Z\[\]\\`_^{|}][a-zA-Z0-9\[\]\\`_^{|}\-]*$'
|
||||
return bool(re.match(pattern, nick))
|
||||
|
||||
@staticmethod
|
||||
def validate_channel(channel: str) -> bool:
|
||||
"""Validate IRC channel format"""
|
||||
if not channel or len(channel) > 50:
|
||||
return False
|
||||
return channel.startswith('#') and ' ' not in channel
|
||||
|
||||
@staticmethod
|
||||
def validate_numeric_input(value: str, min_val: Optional[int] = None, max_val: Optional[int] = None) -> Optional[int]:
|
||||
"""Safely parse and validate numeric input"""
|
||||
try:
|
||||
num = int(value)
|
||||
if min_val is not None and num < min_val:
|
||||
return None
|
||||
if max_val is not None and num > max_val:
|
||||
return None
|
||||
return num
|
||||
except (ValueError, TypeError):
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def sanitize_message(message: str) -> str:
|
||||
"""Sanitize user input message"""
|
||||
if not message:
|
||||
return ""
|
||||
# Remove control characters and limit length
|
||||
sanitized = ''.join(char for char in message if ord(char) >= 32 or char in '\t\n')
|
||||
return sanitized[:500] # Limit message length
|
||||
|
||||
|
||||
def parse_message(line):
|
||||
"""Parse IRC message format"""
|
||||
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
|
||||
Reference in New Issue
Block a user