megabot/bot_template.js
megaproxy a3ed25f8dd Initial commit: Enhanced IRC bot with duck hunt game
- Added hunt/feed duck mechanics (80% hunt, 20% feed)
- Implemented persistent scoring system
- Added channel control commands (\!stopducks/\!startducks)
- Enhanced duck hunt with wrong action penalties
- Organized bot structure with botmain.js as main file
- Added comprehensive documentation (README.md)
- Included 17 plugins with various games and utilities

🦆 Duck Hunt Features:
- Hunt ducks with \!shoot/\!bang (80% of spawns)
- Feed ducks with \!feed (20% of spawns)
- Persistent scores saved to JSON
- Channel-specific controls for #bakedbeans
- Reaction time tracking and special achievements

🎮 Other Games:
- Casino games (slots, coinflip, hi-lo, scratch cards)
- Multiplayer games (pigs, zombie dice, quiplash)
- Text generation (babble, conspiracy, drunk historian)
- Interactive features (story writing, emojify, combos)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-17 19:03:45 +00:00

296 lines
No EOL
8.7 KiB
JavaScript

// bot.js - Main IRC Bot with Plugin System
const net = require('net');
const fs = require('fs');
const path = require('path');
class IRCBot {
constructor(config) {
this.config = {
server: config.server || 'irc.libera.chat',
port: config.port || 6667,
nick: config.nick || 'MyBot',
channels: config.channels || ['#test'],
commandPrefix: config.commandPrefix || '!',
pluginsDir: config.pluginsDir || './plugins'
};
this.socket = null;
this.plugins = new Map();
this.commands = new Map();
this.connected = false;
this.loadPlugins();
}
connect() {
console.log(`Connecting to ${this.config.server}:${this.config.port}`);
this.socket = net.createConnection(this.config.port, this.config.server);
this.socket.on('connect', () => {
console.log('Connected to IRC server');
this.send(`NICK ${this.config.nick}`);
this.send(`USER ${this.config.nick} 0 * :${this.config.nick}`);
});
this.socket.on('data', (data) => {
const lines = data.toString().split('\r\n');
lines.forEach(line => {
if (line.trim()) {
this.handleMessage(line);
}
});
});
this.socket.on('error', (err) => {
console.error('Socket error:', err);
});
this.socket.on('close', () => {
console.log('Connection closed');
this.connected = false;
// Reconnect after 5 seconds
setTimeout(() => this.connect(), 5000);
});
}
send(message) {
if (this.socket && this.socket.writable) {
console.log('→', message);
this.socket.write(message + '\r\n');
}
}
handleMessage(line) {
console.log('←', line);
// Handle PING
if (line.startsWith('PING')) {
this.send('PONG ' + line.substring(5));
return;
}
// Handle successful connection
if (line.includes('001')) {
this.connected = true;
console.log('Successfully connected and registered');
// Join channels
this.config.channels.forEach(channel => {
this.send(`JOIN ${channel}`);
});
return;
}
// Parse IRC message
const parsed = this.parseMessage(line);
if (parsed) {
this.handleParsedMessage(parsed);
}
}
parseMessage(line) {
// Basic IRC message parsing
const match = line.match(/^:([^!]+)!([^@]+)@([^\s]+)\s+(\w+)\s+([^:]+):(.*)$/);
if (match) {
return {
nick: match[1],
user: match[2],
host: match[3],
command: match[4],
target: match[5].trim(),
message: match[6]
};
}
return null;
}
handleParsedMessage(msg) {
// Handle PRIVMSG (channel messages and private messages)
if (msg.command === 'PRIVMSG') {
const isChannel = msg.target.startsWith('#');
const replyTo = isChannel ? msg.target : msg.nick;
// Check if it's a command
if (msg.message.startsWith(this.config.commandPrefix)) {
const commandLine = msg.message.substring(this.config.commandPrefix.length);
const [commandName, ...args] = commandLine.split(' ');
this.executeCommand(commandName, {
nick: msg.nick,
user: msg.user,
host: msg.host,
channel: isChannel ? msg.target : null,
replyTo: replyTo,
args: args,
fullMessage: msg.message
});
}
// Emit message event for plugins
this.emit('message', {
nick: msg.nick,
user: msg.user,
host: msg.host,
target: msg.target,
message: msg.message,
isChannel: isChannel,
replyTo: replyTo
});
}
}
executeCommand(commandName, context) {
const command = this.commands.get(commandName);
if (command) {
try {
command.execute(context, this);
} catch (error) {
console.error(`Error executing command ${commandName}:`, error);
this.say(context.replyTo, `Error executing command: ${error.message}`);
}
}
}
loadPlugins() {
if (!fs.existsSync(this.config.pluginsDir)) {
fs.mkdirSync(this.config.pluginsDir, { recursive: true });
console.log(`Created plugins directory: ${this.config.pluginsDir}`);
return;
}
const pluginFiles = fs.readdirSync(this.config.pluginsDir)
.filter(file => file.endsWith('.js'));
pluginFiles.forEach(file => {
this.loadPlugin(file);
});
console.log(`Loaded ${pluginFiles.length} plugins`);
}
loadPlugin(filename) {
const filePath = path.join(this.config.pluginsDir, filename);
try {
// Clear require cache to allow reloading
delete require.cache[require.resolve(filePath)];
const plugin = require(filePath);
if (typeof plugin.init === 'function') {
plugin.init(this);
}
// Register commands
if (plugin.commands) {
plugin.commands.forEach(command => {
this.commands.set(command.name, command);
console.log(`Registered command: ${command.name}`);
});
}
this.plugins.set(filename, plugin);
console.log(`Loaded plugin: ${filename}`);
} catch (error) {
console.error(`Error loading plugin ${filename}:`, error);
}
}
reloadPlugin(filename) {
const plugin = this.plugins.get(filename);
if (plugin) {
// Unregister old commands
if (plugin.commands) {
plugin.commands.forEach(command => {
this.commands.delete(command.name);
});
}
// Call cleanup if available
if (typeof plugin.cleanup === 'function') {
plugin.cleanup(this);
}
this.plugins.delete(filename);
}
// Load the plugin again
this.loadPlugin(filename);
}
reloadAllPlugins() {
// Clear all plugins and commands
this.plugins.forEach((plugin, filename) => {
if (typeof plugin.cleanup === 'function') {
plugin.cleanup(this);
}
});
this.plugins.clear();
this.commands.clear();
// Reload all plugins
this.loadPlugins();
}
// Helper methods for plugins
say(target, message) {
this.send(`PRIVMSG ${target} :${message}`);
}
action(target, message) {
this.send(`PRIVMSG ${target} :\x01ACTION ${message}\x01`);
}
notice(target, message) {
this.send(`NOTICE ${target} :${message}`);
}
join(channel) {
this.send(`JOIN ${channel}`);
}
part(channel, reason = '') {
this.send(`PART ${channel} :${reason}`);
}
// Simple event system for plugins
emit(event, data) {
this.plugins.forEach(plugin => {
if (typeof plugin[`on${event.charAt(0).toUpperCase() + event.slice(1)}`] === 'function') {
try {
plugin[`on${event.charAt(0).toUpperCase() + event.slice(1)}`](data, this);
} catch (error) {
console.error(`Error in plugin event handler:`, error);
}
}
});
}
}
// Configuration
const config = {
server: 'irc.libera.chat',
port: 6667,
nick: 'MyBot',
channels: ['#test'],
commandPrefix: '!',
pluginsDir: './plugins'
};
// Create and start bot
const bot = new IRCBot(config);
bot.connect();
// Handle graceful shutdown
process.on('SIGINT', () => {
console.log('\nShutting down bot...');
if (bot.socket) {
bot.send('QUIT :Bot shutting down');
bot.socket.end();
}
process.exit(0);
});
module.exports = IRCBot;