UNPKG

modernirc

Version:

IRC library for creating modern IRC bots

239 lines (235 loc) 7.79 kB
import process from 'node:process' /** * Initializes all the required events for the automatic behaviour of the bot * @param bot {object} Bot object * @param options {object} Bot options */ export function initEvents(bot, options) { if (options.uplink.autoPong) { bot.on('ping', ({ params }) => { bot.send('pong', params) }) } bot.on('clientinfo', () => { bot.send('clientinfo', [ 'ACTION', 'CLIENTINFO', 'FINGER', 'PING', 'SOURCE', 'TIME', 'USERINFO', 'VERSION', ]) }) bot.on('finger', () => { bot.send('finger', [ 'ModernIRCBot', `v${process.env.npm_package_version}`, ]) }) bot.on('source', () => { bot.send('source', [ 'https://codeberg.org/ChakSoft/modernirc.git', ]) }) bot.on('time', () => { bot.send('time', [ new Date().toISOString(), ]) }) bot.on('version', () => { bot.send('version', [ 'ModernIRCBot', `v${process.env.npm_package_version}`, ]) }) bot.on('userinfo', () => { bot.send('userinfo', [ bot.identity.username, `(${bot.identity.realname})`, ]) }) bot.on('error', (err) => { options.logger.error(err.message ?? err.params.join(' ')) }) bot.on('end', () => { options.logger.info('connection terminated') }) bot.on('message', async (message) => { const eventName = `on${message.command[0].toUpperCase()}${message.command.substring(1).toLowerCase()}` for (let i = 0; i < bot.loadedModules.length; i++) { const { runner } = bot.loadedModules[i] if (typeof runner[eventName] === 'function') { await runner[eventName](message) } } }) bot.on('privmsg', async (message) => { for (let i = 0; i < bot.loadedModules.length; i++) { const { runner, moduleOptions } = bot.loadedModules[i] if (typeof moduleOptions.commandPrefix === 'string' && message.message.startsWith(moduleOptions.commandPrefix)) { if (typeof runner.onModuleCommand === 'function') { const moduleCommand = message.message.split(' ')[0].substring(1) // we remove the command prefix const moduleArgs = message.message.split(' ').slice(1) await runner.onModuleCommand(moduleCommand, moduleArgs, message) } } } }) bot.on('serverconfig', ({ params }) => { for (let i = 0; i < params.length; i++) { const [name, value] = params[i].split('=') bot.serverConfig[name] = value } // We treat particular things about the prefixes to ensure the bot is understanding the symbols // and the modes available on this server // // The resulting object is something like { 'q': '~', '~': 'q', 'a': '&', '&': 'a' ... } // to ensure the bot is able to convert from one another. // // Technically, modes are : // - q: owner // - a: admin // - o: operator // - h: half-operator // - v: voiced if (typeof bot.serverConfig.PREFIX === 'string') { const prefixMatch = bot.serverConfig.PREFIX?.match(/\((?<modes>\w+)\)(?<symbols>.*)/) if (!prefixMatch?.groups) { options.logger.warning('server prefix malformed: modes will not be auto-detected') return } const { modes, symbols } = prefixMatch.groups bot.serverConfig.PREFIX = modes.split('').reduce((agg, cur, currentIndex) => { return { ...agg, [cur]: symbols[currentIndex], [symbols[currentIndex]]: cur, } }, {}) bot.serverConfig.PREFIX.modes = modes.split('') bot.serverConfig.PREFIX.symbols = symbols.split('') } if (typeof bot.serverConfig.CHANTYPES === 'string') { bot.serverConfig.CHANTYPES = bot.serverConfig.CHANTYPES.split('') } }) bot.on('notice', (notice) => { if (options.identity.password && notice.message.includes('IDENTIFY') && notice.prefix.nickname.toLowerCase() === 'nickserv') { // we're detecting if nickserv sent a message to invite the bot to identify itself bot.message('NickServ', `IDENTIFY ${options.identity.password}`) } }) bot.on('privmsg', (privmsg) => { if (options.identity.password && privmsg.params[0] === options.identity.nickname && privmsg.message.includes('IDENTIFY') && privmsg.prefix.nickname.toLowerCase() === 'nickserv') { bot.message('NickServ', `IDENTIFY ${options.identity.password}`) } else if (privmsg.message.startsWith('\x01ACTION')) { // the message is a CTCP action from an user bot.emit('action', { ...privmsg, message: privmsg.message.replace('\x01ACTION ', ''), }) } }) bot.on('endofmotd', () => { // at the end of message of the day, we should be able to join the specified channels bot.send('join', [options.autojoin.map(({ name }) => name).join(','), options.autojoin.map(({ key }) => key).join(',')]) }) bot.on('join', (message) => { if (message.self) { // this join message is the answer to the bot joining the channel // just after this message will come the names list of the users in the channel bot.joinedChannels[message.params[0]] = { name: message.params[0], users: [], } } else { bot.joinedChannels[message.params[0]].users.push({ nickname: message.prefix.nickname, mode: '', }) } }) bot.on('part', (part) => { const chan = bot.joinedChannels[part.params[0]] if (!chan) { return } const userIndex = chan.users.findIndex((user) => user.nickname === part.prefix.nickname) if (userIndex === -1) { return } chan.users.splice(userIndex, 1) }) bot.on('kick', (kick) => { const chan = bot.joinedChannels[kick.params[0]] if (!chan) { return } const userIndex = chan.users.findIndex((user) => user.nickname === kick.params[1]) if (userIndex === -1) { return } chan.users.splice(userIndex, 1) }) bot.on('namreply', (nr) => { const chan = bot.joinedChannels[nr.params[2]] if (!chan) { return } const users = nr.message.split(' ') for (let i = 0; i < users.length; i++) { const user = users[i] if (bot.serverConfig.PREFIX?.symbols.includes(user[0])) { chan.users.push({ nickname: user.substring(1), mode: bot.serverConfig.PREFIX[user[0]], }) } else { chan.users.push({ nickname: user, mode: '', }) } } }) bot.on('mode', (mode) => { const [channelName, modepattern, target, ...args] = mode.params const chan = bot.joinedChannels[channelName] if (!chan) { return } const modificators = { added: [], removed: [] } let currentModificator = null for (let i = 0; i < modepattern.length; i++) { if (modepattern[i] === '+') { currentModificator = 'added' } else if (modepattern[i] === '-') { currentModificator = 'removed' } else if (currentModificator !== null) { modificators[currentModificator].push(modepattern[i]) } } if (target) { const user = chan.users.find(({ nickname }) => nickname === target) if (!user) { return } for (let i = 0; i< modificators.added.length; i++) { user.mode += modificators.added[i] } for (let i = 0; i < modificators.removed.length; i++) { user.mode = user.mode.replace(modificators.removed[i], '') } } }) bot.on('quit', (quit) => { Object.keys(bot.joinedChannels).forEach((chan) => { const userIndex = bot.joinedChannels[chan].users.findIndex((({ nickname }) => nickname === quit.prefix.nickname)) if (userIndex !== -1) { bot.joinedChannels[chan].users.splice(userIndex, 1) } }) }) }