- 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>
773 lines
No EOL
30 KiB
JavaScript
773 lines
No EOL
30 KiB
JavaScript
// plugins/babble.js - Fresh Markov Chain Text Generator plugin with user-specific generation
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
|
|
module.exports = {
|
|
init(bot) {
|
|
console.log('🎯 Babble plugin starting...');
|
|
this.bot = bot;
|
|
|
|
// Try to load sqlite3
|
|
try {
|
|
this.sqlite3 = require('sqlite3').verbose();
|
|
console.log('✅ SQLite3 loaded successfully');
|
|
} catch (error) {
|
|
console.error('❌ SQLite3 failed to load:', error.message);
|
|
this.sqlite3 = null;
|
|
return;
|
|
}
|
|
|
|
// Database setup
|
|
this.dbPath = path.join(__dirname, 'babble.db');
|
|
console.log(`📁 Database will be created at: ${this.dbPath}`);
|
|
|
|
// Initialize database
|
|
this.initDB();
|
|
|
|
// Simple settings
|
|
this.settings = {
|
|
minLength: 5,
|
|
excludeBots: ['cancerbot', 'services'],
|
|
excludeCommands: true
|
|
};
|
|
|
|
console.log('✅ Babble plugin initialized');
|
|
},
|
|
|
|
cleanup(bot) {
|
|
console.log('🎯 Babble plugin cleaning up');
|
|
if (this.db) {
|
|
this.db.close();
|
|
}
|
|
},
|
|
|
|
initDB() {
|
|
try {
|
|
this.db = new this.sqlite3.Database(this.dbPath, (err) => {
|
|
if (err) {
|
|
console.error('❌ Database creation failed:', err.message);
|
|
this.db = null;
|
|
return;
|
|
}
|
|
|
|
console.log('✅ Database created/opened successfully');
|
|
this.createTables();
|
|
});
|
|
} catch (error) {
|
|
console.error('❌ Database init error:', error.message);
|
|
this.db = null;
|
|
}
|
|
},
|
|
|
|
createTables() {
|
|
if (!this.db) return;
|
|
|
|
// Simple messages table
|
|
this.db.run(`
|
|
CREATE TABLE IF NOT EXISTS messages (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
nick TEXT,
|
|
channel TEXT,
|
|
message TEXT,
|
|
created DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
)
|
|
`, (err) => {
|
|
if (err) {
|
|
console.error('❌ Messages table creation failed:', err.message);
|
|
} else {
|
|
console.log('✅ Messages table ready');
|
|
}
|
|
});
|
|
|
|
// Check if word_pairs table exists and if it has nick column
|
|
this.db.get("PRAGMA table_info(word_pairs)", (err, result) => {
|
|
if (err) {
|
|
console.log('🔧 Creating new word_pairs table...');
|
|
this.createWordPairsTable();
|
|
} else {
|
|
// Table exists, check if it has nick column
|
|
this.db.all("PRAGMA table_info(word_pairs)", (err, columns) => {
|
|
if (err) {
|
|
console.error('❌ Error checking table structure:', err.message);
|
|
return;
|
|
}
|
|
|
|
const hasNickColumn = columns.some(col => col.name === 'nick');
|
|
|
|
if (!hasNickColumn) {
|
|
console.log('🔧 Migrating word_pairs table to add nick column...');
|
|
this.migrateWordPairsTable();
|
|
} else {
|
|
console.log('✅ Word pairs table ready with nick column');
|
|
console.log('🎯 Database fully initialized');
|
|
}
|
|
});
|
|
}
|
|
});
|
|
},
|
|
|
|
createWordPairsTable() {
|
|
this.db.run(`
|
|
CREATE TABLE word_pairs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
word1 TEXT,
|
|
word2 TEXT,
|
|
count INTEGER DEFAULT 1,
|
|
nick TEXT,
|
|
UNIQUE(word1, word2, nick)
|
|
)
|
|
`, (err) => {
|
|
if (err) {
|
|
console.error('❌ Word pairs table creation failed:', err.message);
|
|
} else {
|
|
console.log('✅ Word pairs table created with nick column');
|
|
console.log('🎯 Database fully initialized');
|
|
}
|
|
});
|
|
},
|
|
|
|
migrateWordPairsTable() {
|
|
// Rename old table
|
|
this.db.run("ALTER TABLE word_pairs RENAME TO word_pairs_old", (err) => {
|
|
if (err) {
|
|
console.error('❌ Failed to rename old table:', err.message);
|
|
return;
|
|
}
|
|
|
|
// Create new table with nick column
|
|
this.db.run(`
|
|
CREATE TABLE word_pairs (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
word1 TEXT,
|
|
word2 TEXT,
|
|
count INTEGER DEFAULT 1,
|
|
nick TEXT,
|
|
UNIQUE(word1, word2, nick)
|
|
)
|
|
`, (err) => {
|
|
if (err) {
|
|
console.error('❌ Failed to create new table:', err.message);
|
|
return;
|
|
}
|
|
|
|
// Copy old data (nick will be NULL for old entries)
|
|
this.db.run(`
|
|
INSERT INTO word_pairs (word1, word2, count, nick)
|
|
SELECT word1, word2, count, NULL FROM word_pairs_old
|
|
`, (err) => {
|
|
if (err) {
|
|
console.error('❌ Failed to migrate data:', err.message);
|
|
return;
|
|
}
|
|
|
|
// Drop old table
|
|
this.db.run("DROP TABLE word_pairs_old", (err) => {
|
|
if (err) {
|
|
console.error('❌ Failed to drop old table:', err.message);
|
|
} else {
|
|
console.log('✅ Database migration completed successfully');
|
|
console.log('🎯 Database fully initialized');
|
|
}
|
|
});
|
|
});
|
|
});
|
|
});
|
|
},
|
|
|
|
shouldLog(nick, message) {
|
|
// Skip bots
|
|
if (this.settings.excludeBots.includes(nick.toLowerCase())) {
|
|
return false;
|
|
}
|
|
|
|
// Skip commands
|
|
if (this.settings.excludeCommands && message.startsWith('!')) {
|
|
return false;
|
|
}
|
|
|
|
// Skip short messages
|
|
if (message.length < this.settings.minLength) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
cleanMessage(message) {
|
|
// Remove IRC colors and formatting
|
|
message = message.replace(/[\x02\x1D\x1F\x16\x0F]|\x03\d{0,2}(,\d{0,2})?/g, '');
|
|
|
|
// Basic cleanup
|
|
message = message.toLowerCase().trim();
|
|
message = message.replace(/[^\w\s]/g, ' '); // Remove punctuation
|
|
message = message.replace(/\s+/g, ' '); // Multiple spaces to single
|
|
|
|
return message;
|
|
},
|
|
|
|
storeMessage(nick, channel, message) {
|
|
if (!this.db) {
|
|
console.log('❌ No database available for storing message');
|
|
return;
|
|
}
|
|
|
|
if (!this.shouldLog(nick, message)) {
|
|
console.log(`⏭️ Skipping message from ${nick}: ${message.substring(0, 20)}...`);
|
|
return;
|
|
}
|
|
|
|
const cleaned = this.cleanMessage(message);
|
|
if (cleaned.length < this.settings.minLength) {
|
|
console.log('⏭️ Message too short after cleaning');
|
|
return;
|
|
}
|
|
|
|
console.log(`📝 Storing message from ${nick}: "${cleaned}"`);
|
|
|
|
// Store the message
|
|
this.db.run(
|
|
'INSERT INTO messages (nick, channel, message) VALUES (?, ?, ?)',
|
|
[nick, channel, cleaned],
|
|
(err) => {
|
|
if (err) {
|
|
console.error('❌ Failed to store message:', err.message);
|
|
} else {
|
|
console.log('✅ Message stored successfully');
|
|
this.buildPairs(cleaned, nick);
|
|
}
|
|
}
|
|
);
|
|
},
|
|
|
|
buildPairs(message, nick) {
|
|
if (!this.db) return;
|
|
|
|
const words = message.split(' ').filter(w => w.length > 0);
|
|
if (words.length < 2) return;
|
|
|
|
// Add START and END markers
|
|
const allWords = ['<START>', ...words, '<END>'];
|
|
|
|
// Create word pairs
|
|
for (let i = 0; i < allWords.length - 1; i++) {
|
|
const word1 = allWords[i];
|
|
const word2 = allWords[i + 1];
|
|
|
|
this.db.run(`
|
|
INSERT INTO word_pairs (word1, word2, count, nick) VALUES (?, ?, 1, ?)
|
|
ON CONFLICT(word1, word2, nick) DO UPDATE SET count = count + 1
|
|
`, [word1, word2, nick], (err) => {
|
|
if (err) {
|
|
console.error('❌ Failed to store word pair:', err.message);
|
|
} else {
|
|
console.log(`🔗 Stored pair: "${word1}" → "${word2}" (${nick})`);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
generateText(callback, targetNick = null, creative = false) {
|
|
if (!this.db) {
|
|
callback('No database available');
|
|
return;
|
|
}
|
|
|
|
const mode = creative ? 'creative' : 'normal';
|
|
console.log(`🤖 Starting ${mode} text generation${targetNick ? ` for ${targetNick}` : ''}...`);
|
|
|
|
// First check if target user exists and has enough data
|
|
if (targetNick) {
|
|
this.db.get(
|
|
'SELECT COUNT(*) as count FROM word_pairs WHERE nick = ?',
|
|
[targetNick],
|
|
(err, row) => {
|
|
if (err || !row || row.count < 10) {
|
|
callback(`Not enough data for user "${targetNick}" (need at least 10 word pairs)`);
|
|
return;
|
|
}
|
|
this.performGeneration(callback, targetNick, creative);
|
|
}
|
|
);
|
|
} else {
|
|
this.performGeneration(callback, null, creative);
|
|
}
|
|
},
|
|
|
|
performGeneration(callback, targetNick, creative = false) {
|
|
const self = this; // Capture 'this' reference
|
|
// Strategy: Start from a random word
|
|
this.getRandomStartWord((err, startWord) => {
|
|
if (err) {
|
|
callback(err);
|
|
return;
|
|
}
|
|
|
|
let currentWord = startWord;
|
|
let result = [];
|
|
let attempts = 0;
|
|
const maxAttempts = creative ? 80 : 50; // More attempts for creative mode
|
|
const maxLength = creative ?
|
|
Math.floor(Math.random() * 15) + 8 : // Creative: 8-22 words
|
|
Math.floor(Math.random() * 10) + 5; // Normal: 5-14 words
|
|
|
|
console.log(`🎲 Starting with word: "${currentWord}" (${creative ? 'CREATIVE' : 'normal'} mode)`);
|
|
|
|
// Add the starting word if it's not a marker
|
|
if (currentWord !== '<START>' && currentWord !== '<END>') {
|
|
result.push(currentWord);
|
|
}
|
|
|
|
const getNext = () => {
|
|
if (attempts++ > maxAttempts || result.length >= maxLength) {
|
|
if (result.length >= 3) {
|
|
const prefix = targetNick ? `[${targetNick}] ` : '';
|
|
const emoji = creative ? '🎨 ' : '🤖 ';
|
|
callback(null, prefix + emoji + result.join(' '));
|
|
} else {
|
|
callback('Generation failed - too short');
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Build query based on whether we're targeting a specific user
|
|
let query = 'SELECT word2, count FROM word_pairs WHERE word1 = ?';
|
|
let params = [currentWord];
|
|
|
|
if (targetNick) {
|
|
query += ' AND nick = ?';
|
|
params.push(targetNick);
|
|
}
|
|
|
|
query += ' ORDER BY RANDOM() LIMIT 15'; // More options for creative mode
|
|
|
|
this.db.all(query, params, (err, rows) => {
|
|
if (err) {
|
|
console.error('❌ Database error during generation:', err.message);
|
|
callback('Database error: ' + err.message);
|
|
return;
|
|
}
|
|
|
|
if (rows.length === 0) {
|
|
// If stuck, try jumping to a random word
|
|
console.log(`🔄 No next words for "${currentWord}", trying random jump`);
|
|
|
|
// In creative mode, try mixing users more often
|
|
const mixUsers = creative && Math.random() < 0.4;
|
|
const jumpTargetNick = mixUsers ? null : targetNick;
|
|
|
|
this.getRandomWord((err, randomWord) => {
|
|
if (err || !randomWord || randomWord === '<START>' || randomWord === '<END>') {
|
|
if (result.length >= 3) {
|
|
const prefix = targetNick ? `[${targetNick}] ` : '';
|
|
const emoji = creative ? '🎨 ' : '🤖 ';
|
|
callback(null, prefix + emoji + result.join(' '));
|
|
} else {
|
|
callback('Generation incomplete');
|
|
}
|
|
return;
|
|
}
|
|
|
|
result.push(randomWord);
|
|
currentWord = randomWord;
|
|
console.log(`🎲 Random jump to: "${randomWord}"${mixUsers ? ' (mixed user)' : ''}`);
|
|
getNext();
|
|
}, jumpTargetNick);
|
|
return;
|
|
}
|
|
|
|
// Different selection strategies for creative vs normal mode
|
|
let nextWord;
|
|
|
|
if (creative) {
|
|
// Creative mode: Much more chaos and randomness
|
|
const randomChoice = Math.random();
|
|
|
|
if (randomChoice < 0.6) {
|
|
// 60% chance: Pick rare/uncommon words for weirdness
|
|
const uncommon = rows.filter(r => r.count <= 2);
|
|
if (uncommon.length > 0) {
|
|
nextWord = uncommon[Math.floor(Math.random() * uncommon.length)].word2;
|
|
console.log(`🎨 Weird choice: "${nextWord}"`);
|
|
} else {
|
|
// If no uncommon words, pick any random one
|
|
nextWord = rows[Math.floor(Math.random() * rows.length)].word2;
|
|
console.log(`🎨 Random fallback: "${nextWord}"`);
|
|
}
|
|
} else if (randomChoice < 0.8) {
|
|
// 20% chance: Pick completely random word (ignore context)
|
|
self.getRandomWord((err, randomWord) => {
|
|
if (!err && randomWord && randomWord !== '<START>' && randomWord !== '<END>') {
|
|
result.push(randomWord);
|
|
currentWord = randomWord;
|
|
console.log(`🎨 Context break: "${randomWord}"`);
|
|
getNext();
|
|
return;
|
|
}
|
|
// Fallback to normal selection
|
|
nextWord = rows[Math.floor(Math.random() * rows.length)].word2;
|
|
console.log(`🎨 Fallback random: "${nextWord}"`);
|
|
processNextWord();
|
|
}, null); // null = any user for maximum chaos
|
|
return;
|
|
} else {
|
|
// 20% chance: Normal weighted selection for some coherence
|
|
const totalWeight = rows.reduce((sum, row) => sum + row.count, 0);
|
|
let random = Math.random() * totalWeight;
|
|
|
|
nextWord = rows[0].word2;
|
|
for (const row of rows) {
|
|
random -= row.count;
|
|
if (random <= 0) {
|
|
nextWord = row.word2;
|
|
break;
|
|
}
|
|
}
|
|
console.log(`🎨 Weighted choice: "${nextWord}"`);
|
|
}
|
|
} else {
|
|
// Normal mode: Original logic
|
|
if (Math.random() < 0.3) {
|
|
// 30% chance: Pick a less common word for creativity
|
|
const lessCommon = rows.filter(r => r.count <= 2);
|
|
if (lessCommon.length > 0) {
|
|
nextWord = lessCommon[Math.floor(Math.random() * lessCommon.length)].word2;
|
|
console.log(`🎨 Creative choice: "${nextWord}"`);
|
|
} else {
|
|
nextWord = rows[Math.floor(Math.random() * Math.min(5, rows.length))].word2;
|
|
}
|
|
} else {
|
|
// 70% chance: Normal weighted selection
|
|
const totalWeight = rows.reduce((sum, row) => sum + row.count, 0);
|
|
let random = Math.random() * totalWeight;
|
|
|
|
nextWord = rows[0].word2;
|
|
for (const row of rows) {
|
|
random -= row.count;
|
|
if (random <= 0) {
|
|
nextWord = row.word2;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
processNextWord();
|
|
|
|
function processNextWord() {
|
|
console.log(`🔗 Selected next word: "${nextWord}"`);
|
|
|
|
// Check for end
|
|
if (nextWord === '<END>') {
|
|
if (result.length >= 3) {
|
|
const prefix = targetNick ? `[${targetNick}] ` : '';
|
|
const emoji = creative ? '🎨 ' : '🤖 ';
|
|
callback(null, prefix + emoji + result.join(' '));
|
|
} else {
|
|
// Too short, try to continue with random word
|
|
self.getRandomWord((err, randomWord) => {
|
|
if (!err && randomWord && randomWord !== '<START>' && randomWord !== '<END>') {
|
|
result.push(randomWord);
|
|
currentWord = randomWord;
|
|
getNext();
|
|
} else {
|
|
const prefix = targetNick ? `[${targetNick}] ` : '';
|
|
const emoji = creative ? '🎨 ' : '🤖 ';
|
|
callback(null, prefix + emoji + result.join(' '));
|
|
}
|
|
}, targetNick);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Add word and continue
|
|
if (nextWord !== '<START>') {
|
|
result.push(nextWord);
|
|
}
|
|
|
|
currentWord = nextWord;
|
|
getNext();
|
|
}
|
|
});
|
|
};
|
|
|
|
getNext();
|
|
}, targetNick);
|
|
},
|
|
|
|
getRandomStartWord(callback, targetNick = null) {
|
|
if (!this.db) {
|
|
callback('No database available');
|
|
return;
|
|
}
|
|
|
|
// 50% chance to start with <START>, 50% chance to start with random word
|
|
if (Math.random() < 0.5) {
|
|
callback(null, '<START>');
|
|
return;
|
|
}
|
|
|
|
// Build query for random word that appears as word1
|
|
let query = 'SELECT word1 FROM word_pairs WHERE word1 != "<START>" AND word1 != "<END>"';
|
|
let params = [];
|
|
|
|
if (targetNick) {
|
|
query += ' AND nick = ?';
|
|
params.push(targetNick);
|
|
}
|
|
|
|
query += ' ORDER BY RANDOM() LIMIT 1';
|
|
|
|
this.db.get(query, params, (err, row) => {
|
|
if (err || !row) {
|
|
callback(null, '<START>'); // Fallback
|
|
} else {
|
|
callback(null, row.word1);
|
|
}
|
|
});
|
|
},
|
|
|
|
getRandomWord(callback, targetNick = null) {
|
|
if (!this.db) {
|
|
callback('No database available');
|
|
return;
|
|
}
|
|
|
|
let query = 'SELECT word2 FROM word_pairs WHERE word2 != "<START>" AND word2 != "<END>"';
|
|
let params = [];
|
|
|
|
if (targetNick) {
|
|
query += ' AND nick = ?';
|
|
params.push(targetNick);
|
|
}
|
|
|
|
query += ' ORDER BY RANDOM() LIMIT 1';
|
|
|
|
this.db.get(query, params, (err, row) => {
|
|
if (err || !row) {
|
|
callback('No random word found');
|
|
} else {
|
|
callback(null, row.word2);
|
|
}
|
|
});
|
|
},
|
|
|
|
getStats(callback) {
|
|
if (!this.db) {
|
|
callback('No database available');
|
|
return;
|
|
}
|
|
|
|
this.db.get('SELECT COUNT(*) as messages FROM messages', (err, msgRow) => {
|
|
if (err) {
|
|
callback('Database error: ' + err.message);
|
|
return;
|
|
}
|
|
|
|
this.db.get('SELECT COUNT(*) as pairs FROM word_pairs', (err, pairRow) => {
|
|
if (err) {
|
|
callback('Database error: ' + err.message);
|
|
return;
|
|
}
|
|
|
|
this.db.get('SELECT COUNT(DISTINCT nick) as users FROM messages', (err, userRow) => {
|
|
if (err) {
|
|
callback('Database error: ' + err.message);
|
|
return;
|
|
}
|
|
|
|
callback(null, {
|
|
messages: msgRow.messages,
|
|
pairs: pairRow.pairs,
|
|
users: userRow.users
|
|
});
|
|
});
|
|
});
|
|
});
|
|
},
|
|
|
|
// This is the key - message event handler
|
|
onMessage(data, bot) {
|
|
console.log(`📨 Babble received message from ${data.nick}: "${data.message}"`);
|
|
|
|
if (data.isChannel) {
|
|
this.storeMessage(data.nick, data.target, data.message);
|
|
}
|
|
},
|
|
|
|
commands: [
|
|
{
|
|
name: 'babble',
|
|
description: 'Generate random text from learned messages, optionally like a specific user',
|
|
execute: function(context, bot) {
|
|
const plugin = module.exports;
|
|
const target = context.replyTo;
|
|
const from = context.nick;
|
|
const args = context.args;
|
|
|
|
// Check if user specified a target nick
|
|
const targetNick = args.length > 0 ? args[0] : null;
|
|
|
|
plugin.generateText((err, text) => {
|
|
if (err) {
|
|
bot.say(target, `${from}: ${err}`);
|
|
} else {
|
|
bot.say(target, text);
|
|
}
|
|
}, targetNick, false);
|
|
}
|
|
},
|
|
|
|
{
|
|
name: 'babblecreative',
|
|
description: 'Generate weird, funny, and strange text with maximum chaos',
|
|
execute: function(context, bot) {
|
|
const plugin = module.exports;
|
|
const target = context.replyTo;
|
|
const from = context.nick;
|
|
const args = context.args;
|
|
|
|
// Check if user specified a target nick
|
|
const targetNick = args.length > 0 ? args[0] : null;
|
|
|
|
plugin.generateText((err, text) => {
|
|
if (err) {
|
|
bot.say(target, `${from}: ${err}`);
|
|
} else {
|
|
bot.say(target, text);
|
|
}
|
|
}, targetNick, true); // true = creative mode
|
|
}
|
|
},
|
|
|
|
{
|
|
name: 'babblestats',
|
|
description: 'Show babble learning statistics',
|
|
execute: function(context, bot) {
|
|
const plugin = module.exports;
|
|
const target = context.replyTo;
|
|
|
|
plugin.getStats((err, stats) => {
|
|
if (err) {
|
|
bot.say(target, `Error: ${err}`);
|
|
} else {
|
|
bot.say(target, `🧠 Learned: ${stats.messages} messages, ${stats.pairs} word pairs, ${stats.users} users`);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
{
|
|
name: 'babbleusers',
|
|
description: 'Show users available for targeted babbling',
|
|
execute: function(context, bot) {
|
|
const plugin = module.exports;
|
|
const target = context.replyTo;
|
|
|
|
if (!plugin.db) {
|
|
bot.say(target, 'No database available');
|
|
return;
|
|
}
|
|
|
|
plugin.db.all(
|
|
'SELECT nick, COUNT(*) as word_count FROM word_pairs WHERE nick IS NOT NULL GROUP BY nick ORDER BY word_count DESC LIMIT 10',
|
|
(err, rows) => {
|
|
if (err) {
|
|
bot.say(target, 'Database error: ' + err.message);
|
|
return;
|
|
}
|
|
|
|
if (rows.length === 0) {
|
|
bot.say(target, '🤖 No users found in babble database yet.');
|
|
return;
|
|
}
|
|
|
|
const userList = rows.map(row => `${row.nick}(${row.word_count})`).join(', ');
|
|
bot.say(target, `🎭 Available users: ${userList}`);
|
|
bot.say(target, `💡 Usage: !babble <nick> or !babblecreative <nick>`);
|
|
}
|
|
);
|
|
}
|
|
},
|
|
|
|
{
|
|
name: 'babbleadd',
|
|
description: 'Manually add a message to learning database',
|
|
execute: function(context, bot) {
|
|
const plugin = module.exports;
|
|
const target = context.replyTo;
|
|
const from = context.nick;
|
|
const args = context.args;
|
|
|
|
if (args.length === 0) {
|
|
bot.say(target, `Usage: !babbleadd <message to learn>`);
|
|
return;
|
|
}
|
|
|
|
const message = args.join(' ');
|
|
plugin.storeMessage(from, target, message);
|
|
bot.say(target, `${from}: Added message to learning database`);
|
|
}
|
|
},
|
|
|
|
{
|
|
name: 'babbletest',
|
|
description: 'Add test data to verify babble is working',
|
|
execute: function(context, bot) {
|
|
const plugin = module.exports;
|
|
const target = context.replyTo;
|
|
const from = context.nick;
|
|
|
|
const testMessages = [
|
|
'hello world how are you doing today',
|
|
'i love programming with javascript and node',
|
|
'the weather is really nice outside today',
|
|
'coding is fun but sometimes challenging',
|
|
'artificial intelligence is fascinating technology'
|
|
];
|
|
|
|
testMessages.forEach(msg => {
|
|
plugin.storeMessage('testuser', target, msg);
|
|
});
|
|
|
|
bot.say(target, `${from}: Added ${testMessages.length} test messages. Try !babble in a few seconds.`);
|
|
}
|
|
},
|
|
|
|
{
|
|
name: 'babblereset',
|
|
description: 'Reset all babble data (admin only)',
|
|
execute: function(context, bot) {
|
|
const plugin = module.exports;
|
|
const target = context.replyTo;
|
|
const from = context.nick;
|
|
|
|
const adminNicks = ['admin', 'owner', 'cancerbot', 'megasconed'];
|
|
|
|
if (!adminNicks.includes(from)) {
|
|
bot.say(target, `${from}: Access denied - admin only command`);
|
|
return;
|
|
}
|
|
|
|
if (!plugin.db) {
|
|
bot.say(target, `${from}: No database available`);
|
|
return;
|
|
}
|
|
|
|
plugin.db.run('DELETE FROM messages', (err) => {
|
|
if (err) {
|
|
bot.say(target, `${from}: Error clearing messages: ${err.message}`);
|
|
return;
|
|
}
|
|
|
|
plugin.db.run('DELETE FROM word_pairs', (err) => {
|
|
if (err) {
|
|
bot.say(target, `${from}: Error clearing word pairs: ${err.message}`);
|
|
} else {
|
|
bot.say(target, `${from}: All babble data cleared`);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
]
|
|
}; |