Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // http://2354213892/nitestryker <-------
- /*
- # Copyright (C) 2025 Nitestryker
- #
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, version 3 of the License.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- */
- const irc = require('irc');
- const sqlite3 = require('sqlite3').verbose();
- const Sentiment = require('sentiment');
- const natural = require('natural');
- const axios = require('axios');
- const tokenizer = new natural.WordTokenizer();
- const classifier = new natural.BayesClassifier();
- const urlRegex = /(https?:\/\/[^\s]+)/g;
- async function fetchDefinition(term) {
- try {
- // Common name corrections
- const nameCorrections = {
- 'george washington carter': 'George Washington Carver',
- // Add more common misspellings here
- };
- // Check if we have a correction for this term
- const correctedTerm = nameCorrections[term.toLowerCase()] || term;
- // Log the term being searched
- console.log(`Searching Wikipedia for term: "${correctedTerm}"`);
- // Check if term contains programming-related keywords
- const isProgramming = /^(javascript|python|java|html|css|api|function|class|database|framework)$/i.test(correctedTerm);
- if (isProgramming) {
- // Try MDN for technical terms
- const mdnResponse = await axios.get(`https://developer.mozilla.org/api/rest_v1/search?q=${encodeURIComponent(term)}&limit=1`);
- if (mdnResponse.data.documents && mdnResponse.data.documents.length > 0) {
- return mdnResponse.data.documents[0].summary;
- }
- } else {
- // Use Wikipedia for general knowledge
- const searchTerm = term.replace(/^(who|what|where|when|why|how) (is|are|was|were) /i, '').trim();
- try {
- const wikiResponse = await axios.get(`https://en.wikipedia.org/api/rest_v1/page/summary/${encodeURIComponent(searchTerm)}`);
- if (wikiResponse.data.extract && wikiResponse.data.extract.length > 10) {
- // Process the response to be conversational and complete
- let summary = wikiResponse.data.extract;
- // Calculate a more reasonable typing delay (2-5 seconds)
- const typingTimeMs = Math.random() * 3000 + 2000;
- await new Promise(resolve => setTimeout(resolve, typingTimeMs));
- if (summary.length > 250) {
- summary = summary
- .split(/[.!?]+/)
- .filter(s => s.trim().length > 0)
- .slice(0, 2)
- .map(s => s.trim())
- .join('. ');
- }
- if (!summary.endsWith('.')) {
- summary += '.';
- }
- // Verify the response is meaningful
- if (summary.includes('may refer to') || summary.length < 20) {
- return null;
- }
- return summary.charAt(0).toUpperCase() + summary.slice(1);
- }
- return null;
- } catch (error) {
- console.error('Wikipedia API error:', error);
- return null;
- }
- }
- } catch (error) {
- console.error('Definition lookup error:', error);
- return null;
- }
- }
- async function fetchUrlTitle(url) {
- try {
- const response = await axios.get(url);
- const cheerio = require('cheerio');
- const $ = cheerio.load(response.data);
- return $('title').text().trim();
- } catch (error) {
- console.error('Error fetching URL title:', error);
- return null;
- }
- }
- async function searchStackOverflow(query) {
- try {
- const response = await axios.get('https://api.stackexchange.com/2.3/search/advanced', {
- params: {
- order: 'desc',
- sort: 'votes',
- q: query,
- site: 'stackoverflow',
- pagesize: 1,
- answers: 1,
- accepted: true,
- filter: '!9Z(-wwYGT'
- }
- });
- if (response.data.items && response.data.items.length > 0) {
- const post = response.data.items[0];
- const words = query.split(' ').length;
- const typingTimeMs = (words / 30) * 60 * 1000; // 30 WPM typing speed
- await new Promise(resolve => setTimeout(resolve, Math.min(Math.max(typingTimeMs, 2000), 10000)));
- return {
- link: post.link,
- title: post.title,
- answer_count: post.answer_count,
- score: post.score
- };
- }
- return null;
- } catch (error) {
- console.error('Stack Overflow API error:', error);
- return null;
- }
- }
- // Helper function for keyword identification
- function identifyKeywords(message) {
- return message.toLowerCase()
- .split(/\s+/)
- .filter(word => word.length > 3)
- .slice(0, 5);
- }
- // Train the classifier with more specific patterns
- classifier.addDocument('what is', 'question');
- classifier.addDocument('how do i', 'question');
- classifier.addDocument('can you help', 'question');
- classifier.addDocument('could you explain', 'question');
- classifier.addDocument('who knows', 'question');
- classifier.addDocument('why does', 'question');
- classifier.addDocument('where can i', 'question');
- classifier.addDocument('is there', 'question');
- classifier.addDocument('does anyone', 'question');
- classifier.addDocument('how can', 'question');
- classifier.addDocument('what are', 'question');
- classifier.addDocument('how do you', 'question');
- classifier.addDocument('lol', 'joke');
- classifier.addDocument('haha', 'joke');
- classifier.addDocument('rofl', 'joke');
- classifier.addDocument('π', 'joke');
- classifier.addDocument('π€£', 'joke');
- classifier.addDocument('hello', 'greeting');
- classifier.addDocument('hi', 'greeting');
- classifier.addDocument('hey', 'greeting');
- classifier.addDocument('morning', 'greeting');
- classifier.addDocument('bye', 'farewell');
- classifier.addDocument('goodbye', 'farewell');
- classifier.addDocument('cya', 'farewell');
- classifier.addDocument('later', 'farewell');
- classifier.addDocument('i just', 'update');
- classifier.addDocument('i have', 'update');
- classifier.addDocument('check this', 'update');
- classifier.addDocument('look at', 'update');
- classifier.train();
- // Initialize SQLite database
- const db = new sqlite3.Database('irc_logs.db');
- const sentiment = new Sentiment();
- // Initialize maps and sets
- const learningData = new Map();
- const relatedQuestions = new Map();
- const activeUsers = new Set();
- const conversationMemory = new Map(); // Store recent messages for each user
- const MEMORY_WINDOW = 5; // Number of messages to remember per user
- const userCooldowns = new Map(); // Track user cooldowns
- const COOLDOWN_TIME = 30000; // 30 seconds cooldown
- function isUserInCooldown(username) {
- const lastInteraction = userCooldowns.get(username);
- if (!lastInteraction) return false;
- return (Date.now() - lastInteraction) < COOLDOWN_TIME;
- }
- function setUserCooldown(username) {
- userCooldowns.set(username, Date.now());
- }
- const Fuse = require('fuse.js');
- function handleLearning(message, from, to) {
- if (message.startsWith('!q ')) {
- const query = message.slice(3);
- db.all('SELECT id, question, answer, related_to FROM learning', [], (err, rows) => {
- if (err) return console.error(err);
- const fuse = new Fuse(rows, {
- keys: ['question', 'answer'],
- threshold: 0.4,
- includeScore: true
- });
- const results = fuse.search(query);
- if (results.length > 0) {
- const bestMatch = results[0].item;
- client.say(to, `Found: ${bestMatch.question} -> ${bestMatch.answer}`);
- // Find related questions using fuzzy search
- const relatedResults = fuse.search(bestMatch.question)
- .filter(r => r.item.id !== bestMatch.id)
- .slice(0, 3);
- if (relatedResults.length > 0) {
- client.say(to, `Related questions:`);
- relatedResults.forEach(r => {
- client.say(to, `- ${r.item.question} -> ${r.item.answer}`);
- });
- }
- } else {
- client.say(to, `No matches found for "${query}"`);
- }
- });
- } else if (message.startsWith('!a ')) {
- const [, question, answer] = message.match(/!a (.+?) -> (.+)/) || [];
- if (question && answer) {
- learningData.set(question, answer);
- db.get('SELECT id FROM learning ORDER BY created_at DESC LIMIT 1', [], (err, row) => {
- if (err) return console.error(err);
- const relatedTo = row ? row.id : null;
- db.run('INSERT INTO learning (question, answer, related_to, user) VALUES (?, ?, ?, ?)',
- [question, answer, relatedTo, from],
- function(err) {
- if (err) return console.error(err);
- client.say(to, 'Answer stored! ' + (relatedTo ? '(Linked to previous question)' : ''));
- }
- );
- });
- }
- } else if (message === '!resetlearning' && from.toLowerCase().includes('admin')) {
- learningData.clear();
- relatedQuestions.clear();
- db.run('DELETE FROM learning');
- client.say(to, 'Learning data reset!');
- }
- }
- // Create necessary tables
- db.serialize(() => {
- db.run(`CREATE TABLE IF NOT EXISTS messages (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
- user TEXT,
- channel TEXT,
- message TEXT,
- sentiment_score INTEGER
- )`);
- db.run(`CREATE TABLE IF NOT EXISTS users (
- username TEXT PRIMARY KEY,
- channel TEXT,
- last_seen DATETIME DEFAULT CURRENT_TIMESTAMP
- )`);
- db.run(`CREATE TABLE IF NOT EXISTS learning (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- question TEXT,
- answer TEXT,
- related_to INTEGER,
- user TEXT,
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
- FOREIGN KEY(related_to) REFERENCES learning(id)
- )`);
- db.run(`CREATE TABLE IF NOT EXISTS user_knowledge (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- username TEXT,
- question TEXT,
- answer TEXT,
- last_asked DATETIME DEFAULT CURRENT_TIMESTAMP,
- times_asked INTEGER DEFAULT 1,
- UNIQUE(username, question)
- )`);
- });
- // Create IRC client
- const client = new irc.Client('irc.afternet.org', 'nodebot', {
- channels: ['#Programming'],
- port: 6667,
- debug: true,
- secure: false,
- autoRejoin: true,
- autoConnect: true,
- retryDelay: 10000,
- retryCount: 3
- });
- // Event Listeners
- client.addListener('connect', () => {
- console.log('Connected to IRC server');
- });
- client.addListener('message', async (from, to, message) => {
- // Update conversation memory
- if (!conversationMemory.has(from)) {
- conversationMemory.set(from, []);
- }
- const userMemory = conversationMemory.get(from);
- userMemory.push({ message, timestamp: Date.now() });
- if (userMemory.length > MEMORY_WINDOW) {
- userMemory.shift();
- }
- const sentimentResult = sentiment.analyze(message);
- const keywords = identifyKeywords(message);
- const messageType = classifier.classify(message.toLowerCase());
- const tokens = tokenizer.tokenize(message);
- // Get conversation context
- const recentContext = userMemory
- .map(m => m.message)
- .join(' ');
- db.run(
- 'INSERT INTO messages (user, channel, message, sentiment_score) VALUES (?, ?, ?, ?)',
- [from, to, message, sentimentResult.score]
- );
- db.run(
- 'INSERT OR REPLACE INTO users (username, channel, last_seen) VALUES (?, ?, CURRENT_TIMESTAMP)',
- [from, to]
- );
- handleLearning(message, from, to);
- // Handle URLs in messages
- const urls = message.match(urlRegex);
- if (urls) {
- for (const url of urls) {
- const title = await fetchUrlTitle(url);
- if (title) {
- client.say(to, `π Title: ${title}`);
- }
- }
- }
- // Detect question type and category
- // Programming keywords must appear with technical context
- const programmingKeywords = /\b(how|why|what|when)\b.{0,30}\b(javascript|python|java|function|api|sql|react|node|express|html|css|php)\b|\b(code|error|debug|compile|runtime|syntax)\b.{0,30}\b(javascript|python|java|function|loop|array|class)\b|\b(programming|coding|development)\b.{0,30}\b(problem|issue|error|bug)\b/i;
- const generalKnowledgeKeywords = /(who|what|where|when) (is|are|was|were) (the|a|an)|history|president|country|capital|population|invented|discovered|born|died|leader|founded|king|jr|dr|martin|luther|historical|figure|civil|rights/i;
- // A question is programming-related only if it contains programming keywords in proper context
- const isProgrammingQuestion = programmingKeywords.test(message.toLowerCase()) &&
- message.split(' ').length > 3 && // Ensure it's a substantial question
- !generalKnowledgeKeywords.test(message); // Ensure it's not a general knowledge question
- // Debug logging for question detection
- console.log('\n=== Question Analysis ===');
- console.log(`Message from ${from}: "${message}"`);
- console.log(`Contains programming keywords: ${programmingKeywords.test(message.toLowerCase())}`);
- console.log(`Contains general knowledge keywords: ${generalKnowledgeKeywords.test(message.toLowerCase())}`);
- console.log(`Is programming question: ${isProgrammingQuestion}`);
- // First check if message ends with question mark
- const hasQuestionMark = message.trim().endsWith('?');
- // Enhanced question detection patterns for additional context
- const questionPatterns = [
- /\b(who|what|where|when|why|which|whose|how)\b/i,
- /\bcan\s+(you|i|we|they)\b/i,
- /\bcould\s+(you|i|we|they)\b/i,
- /\bwould\s+(you|i|we|they)\b/i,
- /\bshould\s+(you|i|we|they)\b/i,
- /^(is|are|do|does|did|was|were|will|would|should|has|have|had)\b/i
- ];
- const hasQuestionPattern = questionPatterns.some(pattern => pattern.test(message));
- const isQuestion = hasQuestionMark || (hasQuestionPattern && message.length > 10);
- // Handle programming questions
- console.log(`Is question pattern match: ${isQuestion}`);
- if (isProgrammingQuestion && isQuestion) {
- console.log('\n=== Processing Programming Question ===');
- if (isUserInCooldown(from)) {
- console.log(`${from} is in cooldown period`);
- return;
- }
- console.log('User not in cooldown, proceeding with Stack Overflow search');
- try {
- const searchQuery = message
- .replace(/^(what|how|why|can|could|where|when|which|whose|is|are|do|does|did|was|were|will|would|should|has|have|had)\s+/i, '')
- .replace(/[?.,!]/g, '')
- .trim();
- setUserCooldown(from);
- console.log(`Searching Stack Overflow for programming question: ${searchQuery}`);
- const result = await searchStackOverflow(searchQuery);
- if (result && result.score > 5) {
- const responses = [
- `${from}, check this out: ${result.link}`,
- `${from}, here you go: ${result.link}`,
- `${from}, this might help: ${result.link}`,
- `${from}, found something similar: ${result.link}`
- ];
- const response = responses[Math.floor(Math.random() * responses.length)];
- client.say(to, response);
- }
- return;
- } catch (error) {
- console.error('Error searching Stack Overflow:', error);
- return;
- }
- }
- // Check user knowledge history
- async function checkUserKnowledge(username, question) {
- return new Promise((resolve, reject) => {
- db.get(
- 'SELECT * FROM user_knowledge WHERE username = ? AND question LIKE ? ORDER BY last_asked DESC LIMIT 1',
- [username, `%${question}%`],
- (err, row) => {
- if (err) reject(err);
- else resolve(row);
- }
- );
- });
- }
- async function updateUserKnowledge(username, question, answer) {
- db.run(
- `INSERT INTO user_knowledge (username, question, answer)
- VALUES (?, ?, ?)
- ON CONFLICT(username, question)
- DO UPDATE SET times_asked = times_asked + 1, last_asked = CURRENT_TIMESTAMP`,
- [username, question, answer]
- );
- }
- // Handle general knowledge questions and "I don't know" statements
- const definitionMatch = message.match(/\b(what|who) (is|are) ([^?]+)\??/i) ||
- message.match(/\bwhere (is|are) ([^?]+)\??/i) ||
- message.match(/\bwhen (is|was|will) ([^?]+)\??/i) ||
- message.match(/\bwhy (is|are|does) ([^?]+)\??/i) ||
- message.match(/\bwhich ([^?]+)\??/i) ||
- message.match(/\bhow (does|do|can|could) ([^?]+)\??/i) ||
- message.match(/I don['']?t know (what|who|where|when|why|which|how) ([^?.]+) (is|are|was|were|will|would)/i);
- if (definitionMatch && !isUserInCooldown(from)) {
- console.log('\n=== Processing General Knowledge Question ===');
- console.log(`Definition match found: ${JSON.stringify(definitionMatch)}`);
- setUserCooldown(from);
- const term = definitionMatch[3]?.trim() || definitionMatch[2]?.trim();
- console.log(`Searching term: "${term}"`);
- // Check if user has asked this before
- const previousKnowledge = await checkUserKnowledge(from, term);
- if (previousKnowledge) {
- const timeAgo = Math.floor((Date.now() - new Date(previousKnowledge.last_asked)) / (1000 * 60 * 60 * 24));
- const responses = [
- `${from}, you asked about this ${timeAgo} days ago! Need a refresher?`,
- `${from}, we discussed this before! Want me to explain again?`,
- `${from}, I remember you asking about this! Here's a reminder: ${previousKnowledge.answer}`
- ];
- client.say(to, responses[Math.floor(Math.random() * responses.length)]);
- return;
- }
- console.log(`Fetching definition for term: "${term}"`);
- const definition = await fetchDefinition(term);
- if (definition && definition !== null) {
- await updateUserKnowledge(from, term, definition);
- client.say(to, `${from}, ${definition}`);
- } else {
- console.log(`No definition found for term: "${term}"`);
- }
- return;
- }
- // Prevent responding to our own messages or messages that look like they contain our previous messages
- if (from === 'BGood' || message.includes('<BGood>')) {
- return;
- }
- // Add realistic typing delay and only respond sometimes
- if (Math.random() < 0.7) { // 70% chance to respond
- // Calculate typing time based on message length (30 WPM)
- const words = message.split(' ').length;
- const typingTimeMs = (words / 30) * 60 * 1000; // Convert WPM to milliseconds
- // Add typing delay (minimum 2 seconds, maximum 10 seconds)
- await new Promise(resolve => setTimeout(resolve, Math.min(Math.max(typingTimeMs, 2000), 10000)));
- const responses = {
- greeting: [
- `Hi ${from}! π`,
- `Hello ${from}!`,
- `Hey there ${from}!`
- ],
- farewell: [
- `Goodbye ${from}!`,
- `See you later ${from}!`,
- `Take care ${from}!`
- ],
- joke: [
- `π Good one ${from}!`,
- `That's funny ${from}!`,
- `You're hilarious ${from}! π`
- ]
- };
- const getRandomResponse = (type) => {
- const options = responses[type];
- return options ? options[Math.floor(Math.random() * options.length)] : null;
- };
- switch(messageType) {
- case 'greeting':
- client.say(to, getRandomResponse('greeting'));
- break;
- case 'farewell':
- client.say(to, getRandomResponse('farewell'));
- break;
- case 'question':
- // Questions are now handled earlier in the code
- break;
- case 'joke':
- if (sentimentResult.score >= 0) {
- client.say(to, jokeResponses[Math.floor(Math.random() * jokeResponses.length)]);
- }
- break;
- case 'update':
- if (tokens.length > 5) {
- client.say(to, `Thanks for sharing that update, ${from}!`);
- }
- break;
- default:
- if (sentimentResult.score < -3) {
- client.say(to, `Hey ${from}, let's keep things positive! π`);
- } else if (sentimentResult.score > 3) {
- client.say(to, `Great energy, ${from}! π`);
- }
- }
- }
- });
- client.addListener('join', (channel, nick) => {
- console.log(`${nick} joined ${channel}`);
- activeUsers.add(nick);
- db.run('INSERT OR REPLACE INTO users (username, channel) VALUES (?, ?)', [nick, channel]);
- });
- client.addListener('part', (channel, nick) => {
- console.log(`${nick} left ${channel}`);
- activeUsers.delete(nick);
- db.run('UPDATE users SET last_seen = CURRENT_TIMESTAMP WHERE username = ?', [nick]);
- });
- client.addListener('quit', (nick, reason, channels) => {
- console.log(`${nick} quit (${reason})`);
- activeUsers.delete(nick);
- db.run('UPDATE users SET last_seen = CURRENT_TIMESTAMP WHERE username = ?', [nick]);
- });
- client.addListener('error', (message) => {
- console.error('IRC Error:', message);
- });
- process.on('SIGINT', () => {
- db.close();
- process.exit();
- });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement