Advertisement
nitestryker

Advanced Node JS IRC Bot

Feb 23rd, 2025 (edited)
256
0
18 hours
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // http://2354213892/nitestryker <-------
  2. /*
  3. # Copyright (C) 2025  Nitestryker
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, version 3 of the License.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. */
  15. const irc = require('irc');
  16. const sqlite3 = require('sqlite3').verbose();
  17. const Sentiment = require('sentiment');
  18. const natural = require('natural');
  19. const axios = require('axios');
  20. const tokenizer = new natural.WordTokenizer();
  21. const classifier = new natural.BayesClassifier();
  22. const urlRegex = /(https?:\/\/[^\s]+)/g;
  23.  
  24. async function fetchDefinition(term) {
  25.     try {
  26.         // Common name corrections
  27.         const nameCorrections = {
  28.             'george washington carter': 'George Washington Carver',
  29.             // Add more common misspellings here
  30.         };
  31.  
  32.         // Check if we have a correction for this term
  33.         const correctedTerm = nameCorrections[term.toLowerCase()] || term;
  34.        
  35.         // Log the term being searched
  36.         console.log(`Searching Wikipedia for term: "${correctedTerm}"`);
  37.        
  38.         // Check if term contains programming-related keywords
  39.         const isProgramming = /^(javascript|python|java|html|css|api|function|class|database|framework)$/i.test(correctedTerm);
  40.        
  41.         if (isProgramming) {
  42.             // Try MDN for technical terms
  43.             const mdnResponse = await axios.get(`https://developer.mozilla.org/api/rest_v1/search?q=${encodeURIComponent(term)}&limit=1`);
  44.             if (mdnResponse.data.documents && mdnResponse.data.documents.length > 0) {
  45.                 return mdnResponse.data.documents[0].summary;
  46.             }
  47.         } else {
  48.             // Use Wikipedia for general knowledge
  49.             const searchTerm = term.replace(/^(who|what|where|when|why|how) (is|are|was|were) /i, '').trim();
  50.             try {
  51.                 const wikiResponse = await axios.get(`https://en.wikipedia.org/api/rest_v1/page/summary/${encodeURIComponent(searchTerm)}`);
  52.                
  53.                 if (wikiResponse.data.extract && wikiResponse.data.extract.length > 10) {
  54.                     // Process the response to be conversational and complete
  55.                     let summary = wikiResponse.data.extract;
  56.                    
  57.                     // Calculate a more reasonable typing delay (2-5 seconds)
  58.                     const typingTimeMs = Math.random() * 3000 + 2000;
  59.                     await new Promise(resolve => setTimeout(resolve, typingTimeMs));
  60.                     if (summary.length > 250) {
  61.                         summary = summary
  62.                             .split(/[.!?]+/)
  63.                             .filter(s => s.trim().length > 0)
  64.                             .slice(0, 2)
  65.                             .map(s => s.trim())
  66.                             .join('. ');
  67.                     }
  68.                    
  69.                     if (!summary.endsWith('.')) {
  70.                         summary += '.';
  71.                     }
  72.                    
  73.                     // Verify the response is meaningful
  74.                     if (summary.includes('may refer to') || summary.length < 20) {
  75.                         return null;
  76.                     }
  77.                    
  78.                     return summary.charAt(0).toUpperCase() + summary.slice(1);
  79.                 }
  80.                 return null;
  81.             } catch (error) {
  82.                 console.error('Wikipedia API error:', error);
  83.                 return null;
  84.             }
  85.         }
  86.     } catch (error) {
  87.         console.error('Definition lookup error:', error);
  88.         return null;
  89.     }
  90. }
  91.  
  92. async function fetchUrlTitle(url) {
  93.     try {
  94.         const response = await axios.get(url);
  95.         const cheerio = require('cheerio');
  96.         const $ = cheerio.load(response.data);
  97.         return $('title').text().trim();
  98.     } catch (error) {
  99.         console.error('Error fetching URL title:', error);
  100.         return null;
  101.     }
  102. }
  103.  
  104. async function searchStackOverflow(query) {
  105.     try {
  106.         const response = await axios.get('https://api.stackexchange.com/2.3/search/advanced', {
  107.             params: {
  108.                 order: 'desc',
  109.                 sort: 'votes',
  110.                 q: query,
  111.                 site: 'stackoverflow',
  112.                 pagesize: 1,
  113.                 answers: 1,
  114.                 accepted: true,
  115.                 filter: '!9Z(-wwYGT'
  116.             }
  117.         });
  118.  
  119.         if (response.data.items && response.data.items.length > 0) {
  120.             const post = response.data.items[0];
  121.             const words = query.split(' ').length;
  122.             const typingTimeMs = (words / 30) * 60 * 1000; // 30 WPM typing speed
  123.             await new Promise(resolve => setTimeout(resolve, Math.min(Math.max(typingTimeMs, 2000), 10000)));
  124.            
  125.             return {
  126.                 link: post.link,
  127.                 title: post.title,
  128.                 answer_count: post.answer_count,
  129.                 score: post.score
  130.             };
  131.         }
  132.         return null;
  133.     } catch (error) {
  134.         console.error('Stack Overflow API error:', error);
  135.         return null;
  136.     }
  137. }
  138.  
  139. // Helper function for keyword identification
  140. function identifyKeywords(message) {
  141.     return message.toLowerCase()
  142.         .split(/\s+/)
  143.         .filter(word => word.length > 3)
  144.         .slice(0, 5);
  145. }
  146.  
  147. // Train the classifier with more specific patterns
  148. classifier.addDocument('what is', 'question');
  149. classifier.addDocument('how do i', 'question');
  150. classifier.addDocument('can you help', 'question');
  151. classifier.addDocument('could you explain', 'question');
  152. classifier.addDocument('who knows', 'question');
  153. classifier.addDocument('why does', 'question');
  154. classifier.addDocument('where can i', 'question');
  155. classifier.addDocument('is there', 'question');
  156. classifier.addDocument('does anyone', 'question');
  157. classifier.addDocument('how can', 'question');
  158. classifier.addDocument('what are', 'question');
  159. classifier.addDocument('how do you', 'question');
  160.  
  161. classifier.addDocument('lol', 'joke');
  162. classifier.addDocument('haha', 'joke');
  163. classifier.addDocument('rofl', 'joke');
  164. classifier.addDocument('πŸ˜‚', 'joke');
  165. classifier.addDocument('🀣', 'joke');
  166.  
  167. classifier.addDocument('hello', 'greeting');
  168. classifier.addDocument('hi', 'greeting');
  169. classifier.addDocument('hey', 'greeting');
  170. classifier.addDocument('morning', 'greeting');
  171.  
  172. classifier.addDocument('bye', 'farewell');
  173. classifier.addDocument('goodbye', 'farewell');
  174. classifier.addDocument('cya', 'farewell');
  175. classifier.addDocument('later', 'farewell');
  176.  
  177. classifier.addDocument('i just', 'update');
  178. classifier.addDocument('i have', 'update');
  179. classifier.addDocument('check this', 'update');
  180. classifier.addDocument('look at', 'update');
  181.  
  182. classifier.train();
  183.  
  184. // Initialize SQLite database
  185. const db = new sqlite3.Database('irc_logs.db');
  186. const sentiment = new Sentiment();
  187.  
  188. // Initialize maps and sets
  189. const learningData = new Map();
  190. const relatedQuestions = new Map();
  191. const activeUsers = new Set();
  192. const conversationMemory = new Map(); // Store recent messages for each user
  193. const MEMORY_WINDOW = 5; // Number of messages to remember per user
  194. const userCooldowns = new Map(); // Track user cooldowns
  195. const COOLDOWN_TIME = 30000; // 30 seconds cooldown
  196.  
  197. function isUserInCooldown(username) {
  198.     const lastInteraction = userCooldowns.get(username);
  199.     if (!lastInteraction) return false;
  200.     return (Date.now() - lastInteraction) < COOLDOWN_TIME;
  201. }
  202.  
  203. function setUserCooldown(username) {
  204.     userCooldowns.set(username, Date.now());
  205. }
  206.  
  207. const Fuse = require('fuse.js');
  208.  
  209. function handleLearning(message, from, to) {
  210.     if (message.startsWith('!q ')) {
  211.         const query = message.slice(3);
  212.         db.all('SELECT id, question, answer, related_to FROM learning', [], (err, rows) => {
  213.             if (err) return console.error(err);
  214.  
  215.             const fuse = new Fuse(rows, {
  216.                 keys: ['question', 'answer'],
  217.                 threshold: 0.4,
  218.                 includeScore: true
  219.             });
  220.  
  221.             const results = fuse.search(query);
  222.             if (results.length > 0) {
  223.                 const bestMatch = results[0].item;
  224.                 client.say(to, `Found: ${bestMatch.question} -> ${bestMatch.answer}`);
  225.  
  226.                 // Find related questions using fuzzy search
  227.                 const relatedResults = fuse.search(bestMatch.question)
  228.                     .filter(r => r.item.id !== bestMatch.id)
  229.                     .slice(0, 3);
  230.  
  231.                 if (relatedResults.length > 0) {
  232.                     client.say(to, `Related questions:`);
  233.                     relatedResults.forEach(r => {
  234.                         client.say(to, `- ${r.item.question} -> ${r.item.answer}`);
  235.                     });
  236.                 }
  237.             } else {
  238.                 client.say(to, `No matches found for "${query}"`);
  239.             }
  240.         });
  241.     } else if (message.startsWith('!a ')) {
  242.         const [, question, answer] = message.match(/!a (.+?) -> (.+)/) || [];
  243.         if (question && answer) {
  244.             learningData.set(question, answer);
  245.             db.get('SELECT id FROM learning ORDER BY created_at DESC LIMIT 1', [], (err, row) => {
  246.                 if (err) return console.error(err);
  247.                 const relatedTo = row ? row.id : null;
  248.                 db.run('INSERT INTO learning (question, answer, related_to, user) VALUES (?, ?, ?, ?)',
  249.                     [question, answer, relatedTo, from],
  250.                     function(err) {
  251.                         if (err) return console.error(err);
  252.                         client.say(to, 'Answer stored! ' + (relatedTo ? '(Linked to previous question)' : ''));
  253.                     }
  254.                 );
  255.             });
  256.         }
  257.     } else if (message === '!resetlearning' && from.toLowerCase().includes('admin')) {
  258.         learningData.clear();
  259.         relatedQuestions.clear();
  260.         db.run('DELETE FROM learning');
  261.         client.say(to, 'Learning data reset!');
  262.     }
  263. }
  264.  
  265. // Create necessary tables
  266. db.serialize(() => {
  267.     db.run(`CREATE TABLE IF NOT EXISTS messages (
  268.         id INTEGER PRIMARY KEY AUTOINCREMENT,
  269.         timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
  270.         user TEXT,
  271.         channel TEXT,
  272.         message TEXT,
  273.         sentiment_score INTEGER
  274.     )`);
  275.  
  276.     db.run(`CREATE TABLE IF NOT EXISTS users (
  277.         username TEXT PRIMARY KEY,
  278.         channel TEXT,
  279.         last_seen DATETIME DEFAULT CURRENT_TIMESTAMP
  280.     )`);
  281.  
  282.     db.run(`CREATE TABLE IF NOT EXISTS learning (
  283.         id INTEGER PRIMARY KEY AUTOINCREMENT,
  284.         question TEXT,
  285.         answer TEXT,
  286.         related_to INTEGER,
  287.         user TEXT,
  288.         created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  289.         FOREIGN KEY(related_to) REFERENCES learning(id)
  290.     )`);
  291.  
  292.     db.run(`CREATE TABLE IF NOT EXISTS user_knowledge (
  293.         id INTEGER PRIMARY KEY AUTOINCREMENT,
  294.         username TEXT,
  295.         question TEXT,
  296.         answer TEXT,
  297.         last_asked DATETIME DEFAULT CURRENT_TIMESTAMP,
  298.         times_asked INTEGER DEFAULT 1,
  299.         UNIQUE(username, question)
  300.     )`);
  301. });
  302.  
  303. // Create IRC client
  304. const client = new irc.Client('irc.afternet.org', 'nodebot', {
  305.     channels: ['#Programming'],
  306.     port: 6667,
  307.     debug: true,
  308.     secure: false,
  309.     autoRejoin: true,
  310.     autoConnect: true,
  311.     retryDelay: 10000,
  312.     retryCount: 3
  313. });
  314.  
  315. // Event Listeners
  316. client.addListener('connect', () => {
  317.     console.log('Connected to IRC server');
  318. });
  319.  
  320. client.addListener('message', async (from, to, message) => {
  321.     // Update conversation memory
  322.     if (!conversationMemory.has(from)) {
  323.         conversationMemory.set(from, []);
  324.     }
  325.     const userMemory = conversationMemory.get(from);
  326.     userMemory.push({ message, timestamp: Date.now() });
  327.     if (userMemory.length > MEMORY_WINDOW) {
  328.         userMemory.shift();
  329.     }
  330.  
  331.     const sentimentResult = sentiment.analyze(message);
  332.     const keywords = identifyKeywords(message);
  333.     const messageType = classifier.classify(message.toLowerCase());
  334.     const tokens = tokenizer.tokenize(message);
  335.    
  336.     // Get conversation context
  337.     const recentContext = userMemory
  338.         .map(m => m.message)
  339.         .join(' ');
  340.  
  341.     db.run(
  342.         'INSERT INTO messages (user, channel, message, sentiment_score) VALUES (?, ?, ?, ?)',
  343.         [from, to, message, sentimentResult.score]
  344.     );
  345.  
  346.     db.run(
  347.         'INSERT OR REPLACE INTO users (username, channel, last_seen) VALUES (?, ?, CURRENT_TIMESTAMP)',
  348.         [from, to]
  349.     );
  350.  
  351.  
  352.  
  353.     handleLearning(message, from, to);
  354.  
  355.     // Handle URLs in messages
  356.     const urls = message.match(urlRegex);
  357.     if (urls) {
  358.         for (const url of urls) {
  359.             const title = await fetchUrlTitle(url);
  360.             if (title) {
  361.                 client.say(to, `πŸ“Ž Title: ${title}`);
  362.             }
  363.         }
  364.     }
  365.  
  366.     // Detect question type and category
  367.     // Programming keywords must appear with technical context
  368.     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;
  369.  
  370.     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;
  371.  
  372.     // A question is programming-related only if it contains programming keywords in proper context
  373.     const isProgrammingQuestion = programmingKeywords.test(message.toLowerCase()) &&
  374.         message.split(' ').length > 3 && // Ensure it's a substantial question
  375.         !generalKnowledgeKeywords.test(message); // Ensure it's not a general knowledge question
  376.  
  377.     // Debug logging for question detection
  378.     console.log('\n=== Question Analysis ===');
  379.     console.log(`Message from ${from}: "${message}"`);
  380.     console.log(`Contains programming keywords: ${programmingKeywords.test(message.toLowerCase())}`);
  381.     console.log(`Contains general knowledge keywords: ${generalKnowledgeKeywords.test(message.toLowerCase())}`);
  382.     console.log(`Is programming question: ${isProgrammingQuestion}`);
  383.  
  384.     // First check if message ends with question mark
  385.     const hasQuestionMark = message.trim().endsWith('?');
  386.    
  387.     // Enhanced question detection patterns for additional context
  388.     const questionPatterns = [
  389.         /\b(who|what|where|when|why|which|whose|how)\b/i,
  390.         /\bcan\s+(you|i|we|they)\b/i,
  391.         /\bcould\s+(you|i|we|they)\b/i,
  392.         /\bwould\s+(you|i|we|they)\b/i,
  393.         /\bshould\s+(you|i|we|they)\b/i,
  394.         /^(is|are|do|does|did|was|were|will|would|should|has|have|had)\b/i
  395.     ];
  396.  
  397.     const hasQuestionPattern = questionPatterns.some(pattern => pattern.test(message));
  398.     const isQuestion = hasQuestionMark || (hasQuestionPattern && message.length > 10);
  399.  
  400.     // Handle programming questions
  401.     console.log(`Is question pattern match: ${isQuestion}`);
  402.    
  403.     if (isProgrammingQuestion && isQuestion) {
  404.         console.log('\n=== Processing Programming Question ===');
  405.         if (isUserInCooldown(from)) {
  406.             console.log(`${from} is in cooldown period`);
  407.             return;
  408.         }
  409.         console.log('User not in cooldown, proceeding with Stack Overflow search');
  410.  
  411.         try {
  412.             const searchQuery = message
  413.                 .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, '')
  414.                 .replace(/[?.,!]/g, '')
  415.                 .trim();
  416.  
  417.             setUserCooldown(from);
  418.                
  419.             console.log(`Searching Stack Overflow for programming question: ${searchQuery}`);
  420.             const result = await searchStackOverflow(searchQuery);
  421.             if (result && result.score > 5) {
  422.                 const responses = [
  423.                     `${from}, check this out: ${result.link}`,
  424.                     `${from}, here you go: ${result.link}`,
  425.                     `${from}, this might help: ${result.link}`,
  426.                     `${from}, found something similar: ${result.link}`
  427.                 ];
  428.                 const response = responses[Math.floor(Math.random() * responses.length)];
  429.                 client.say(to, response);
  430.             }
  431.             return;
  432.         } catch (error) {
  433.             console.error('Error searching Stack Overflow:', error);
  434.             return;
  435.         }
  436.     }
  437.  
  438.     // Check user knowledge history
  439.     async function checkUserKnowledge(username, question) {
  440.         return new Promise((resolve, reject) => {
  441.             db.get(
  442.                 'SELECT * FROM user_knowledge WHERE username = ? AND question LIKE ? ORDER BY last_asked DESC LIMIT 1',
  443.                 [username, `%${question}%`],
  444.                 (err, row) => {
  445.                     if (err) reject(err);
  446.                     else resolve(row);
  447.                 }
  448.             );
  449.         });
  450.     }
  451.  
  452.     async function updateUserKnowledge(username, question, answer) {
  453.         db.run(
  454.             `INSERT INTO user_knowledge (username, question, answer)
  455.              VALUES (?, ?, ?)
  456.              ON CONFLICT(username, question)
  457.              DO UPDATE SET times_asked = times_asked + 1, last_asked = CURRENT_TIMESTAMP`,
  458.             [username, question, answer]
  459.         );
  460.     }
  461.  
  462.     // Handle general knowledge questions and "I don't know" statements
  463.     const definitionMatch = message.match(/\b(what|who) (is|are) ([^?]+)\??/i) ||
  464.                           message.match(/\bwhere (is|are) ([^?]+)\??/i) ||
  465.                           message.match(/\bwhen (is|was|will) ([^?]+)\??/i) ||
  466.                           message.match(/\bwhy (is|are|does) ([^?]+)\??/i) ||
  467.                           message.match(/\bwhich ([^?]+)\??/i) ||
  468.                           message.match(/\bhow (does|do|can|could) ([^?]+)\??/i) ||
  469.                           message.match(/I don['']?t know (what|who|where|when|why|which|how) ([^?.]+) (is|are|was|were|will|would)/i);
  470.     if (definitionMatch && !isUserInCooldown(from)) {
  471.         console.log('\n=== Processing General Knowledge Question ===');
  472.         console.log(`Definition match found: ${JSON.stringify(definitionMatch)}`);
  473.         setUserCooldown(from);
  474.         const term = definitionMatch[3]?.trim() || definitionMatch[2]?.trim();
  475.         console.log(`Searching term: "${term}"`);
  476.  
  477.         // Check if user has asked this before
  478.         const previousKnowledge = await checkUserKnowledge(from, term);
  479.         if (previousKnowledge) {
  480.             const timeAgo = Math.floor((Date.now() - new Date(previousKnowledge.last_asked)) / (1000 * 60 * 60 * 24));
  481.             const responses = [
  482.                 `${from}, you asked about this ${timeAgo} days ago! Need a refresher?`,
  483.                 `${from}, we discussed this before! Want me to explain again?`,
  484.                 `${from}, I remember you asking about this! Here's a reminder: ${previousKnowledge.answer}`
  485.            ];
  486.            client.say(to, responses[Math.floor(Math.random() * responses.length)]);
  487.            return;
  488.        }
  489.  
  490.        console.log(`Fetching definition for term: "${term}"`);
  491.        const definition = await fetchDefinition(term);
  492.        if (definition && definition !== null) {
  493.            await updateUserKnowledge(from, term, definition);
  494.            client.say(to, `${from}, ${definition}`);
  495.        } else {
  496.            console.log(`No definition found for term: "${term}"`);
  497.        }
  498.        return;
  499.    }
  500.  
  501.    // Prevent responding to our own messages or messages that look like they contain our previous messages
  502.    if (from === 'BGood' || message.includes('<BGood>')) {
  503.        return;
  504.    }
  505.  
  506.    // Add realistic typing delay and only respond sometimes
  507.    if (Math.random() < 0.7) { // 70% chance to respond
  508.        // Calculate typing time based on message length (30 WPM)
  509.        const words = message.split(' ').length;
  510.        const typingTimeMs = (words / 30) * 60 * 1000; // Convert WPM to milliseconds
  511.        
  512.        // Add typing delay (minimum 2 seconds, maximum 10 seconds)
  513.        await new Promise(resolve => setTimeout(resolve, Math.min(Math.max(typingTimeMs, 2000), 10000)));
  514.        
  515.        const responses = {
  516.            greeting: [
  517.                `Hi ${from}! πŸ‘‹`,
  518.                `Hello ${from}!`,
  519.                `Hey there ${from}!`
  520.            ],
  521.            farewell: [
  522.                `Goodbye ${from}!`,
  523.                `See you later ${from}!`,
  524.                `Take care ${from}!`
  525.            ],
  526.            joke: [
  527.                `πŸ˜„ Good one ${from}!`,
  528.                `That's funny ${from}!`,
  529.                 `You're hilarious ${from}! πŸ˜†`
  530.            ]
  531.        };
  532.  
  533.        const getRandomResponse = (type) => {
  534.            const options = responses[type];
  535.            return options ? options[Math.floor(Math.random() * options.length)] : null;
  536.        };
  537.  
  538.        switch(messageType) {
  539.            case 'greeting':
  540.                client.say(to, getRandomResponse('greeting'));
  541.                break;
  542.            case 'farewell':
  543.                client.say(to, getRandomResponse('farewell'));
  544.                break;
  545.            case 'question':
  546.                // Questions are now handled earlier in the code
  547.                break;
  548.            case 'joke':
  549.                if (sentimentResult.score >= 0) {
  550.                    client.say(to, jokeResponses[Math.floor(Math.random() * jokeResponses.length)]);
  551.                }
  552.                break;
  553.            case 'update':
  554.                if (tokens.length > 5) {
  555.                    client.say(to, `Thanks for sharing that update, ${from}!`);
  556.                }
  557.                break;
  558.            default:
  559.                if (sentimentResult.score < -3) {
  560.                    client.say(to, `Hey ${from}, let's keep things positive! 😊`);
  561.                 } else if (sentimentResult.score > 3) {
  562.                     client.say(to, `Great energy, ${from}! πŸŽ‰`);
  563.                 }
  564.         }
  565.     }
  566. });
  567.  
  568. client.addListener('join', (channel, nick) => {
  569.     console.log(`${nick} joined ${channel}`);
  570.     activeUsers.add(nick);
  571.     db.run('INSERT OR REPLACE INTO users (username, channel) VALUES (?, ?)', [nick, channel]);
  572. });
  573.  
  574. client.addListener('part', (channel, nick) => {
  575.     console.log(`${nick} left ${channel}`);
  576.     activeUsers.delete(nick);
  577.     db.run('UPDATE users SET last_seen = CURRENT_TIMESTAMP WHERE username = ?', [nick]);
  578. });
  579.  
  580. client.addListener('quit', (nick, reason, channels) => {
  581.     console.log(`${nick} quit (${reason})`);
  582.     activeUsers.delete(nick);
  583.     db.run('UPDATE users SET last_seen = CURRENT_TIMESTAMP WHERE username = ?', [nick]);
  584. });
  585.  
  586. client.addListener('error', (message) => {
  587.     console.error('IRC Error:', message);
  588. });
  589.  
  590. process.on('SIGINT', () => {
  591.     db.close();
  592.     process.exit();
  593. });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement