From 0e248950220804ff5cdc353a6eaf7d179e32495f Mon Sep 17 00:00:00 2001 From: calebmckay Date: Thu, 17 Oct 2019 09:58:06 -0400 Subject: [PATCH 1/6] Update 'ircStatusNotices' config option to specify a single channel to display joins/quits --- lib/bot.js | 84 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 14 deletions(-) diff --git a/lib/bot.js b/lib/bot.js index 141b4b5d..acff073e 100644 --- a/lib/bot.js +++ b/lib/bot.js @@ -34,7 +34,12 @@ class Bot { this.commandCharacters = options.commandCharacters || []; this.ircNickColor = options.ircNickColor !== false; // default to true this.channels = _.values(options.channelMapping); - this.ircStatusNotices = options.ircStatusNotices; + if (typeof(options.ircStatusNotices) == "string") + { + this.ircStatusNotices = options.ircStatusNotices + } else { + this.ircStatusNotices = options.ircStatusNotices !== false; + } // Can be true for in their respective channels, or a custom Discord channel to announce in. this.announceSelfJoin = options.announceSelfJoin; this.webhookOptions = options.webhooks; @@ -159,35 +164,61 @@ class Bot { }); this.ircClient.on('nick', (oldNick, newNick, channels) => { - if (!this.ircStatusNotices) return; + if (this.ircStatusNotices === false) return; channels.forEach((channelName) => { const channel = channelName.toLowerCase(); if (this.channelUsers[channel]) { if (this.channelUsers[channel].has(oldNick)) { this.channelUsers[channel].delete(oldNick); this.channelUsers[channel].add(newNick); - this.sendExactToDiscord(channel, `*${oldNick}* is now known as ${newNick}`); + if(this.ircStatusNotices === true) { + this.sendExactToDiscord(channel, `*${oldNick}* is now known as ${newNick}`); + } } } else { logger.warn(`No channelUsers found for ${channel} when ${oldNick} changed.`); + return; } }); + if(this.ircStatusNotices !== true && this.ircStatusNotices !== false) { + this.sendExactToDiscordChannel(this.ircStatusNotices, `*${oldNick}* is now known as ${newNick}`); + } }); this.ircClient.on('join', (channelName, nick) => { logger.debug('Received join:', channelName, nick); - if (!this.ircStatusNotices) return; + if (this.ircStatusNotices === false) return; if (nick === this.ircClient.nick && !this.announceSelfJoin) return; - const channel = channelName.toLowerCase(); - // self-join is announced before names (which includes own nick) - // so don't add nick to channelUsers - if (nick !== this.ircClient.nick) this.channelUsers[channel].add(nick); - this.sendExactToDiscord(channel, `*${nick}* has joined the channel`); + if (this.ircStatusNotices === true) { + const channel = channelName.toLowerCase(); + // self-join is announced before names (which includes own nick) + // so don't add nick to channelUsers + if (nick !== this.ircClient.nick) this.channelUsers[channel].add(nick); + this.sendExactToDiscord(channel, `*${nick}* has joined the channel`); + } else { + const ircChannel = channelName.toLowerCase(); + const discordChannel = this.ircStatusNotices; + // Only send the message once per user. Do this by checking channelUsers + // and sending if user is being added for the first time. + if (nick !== this.ircClient.nick) { + var firstAdd = true; + for (var channel in this.channelUsers) { + if (this.channelUsers[channel].has(nick)) { + firstAdd = false; + break; + } + } + this.channelUsers[ircChannel].add(nick); + if (firstAdd) { + this.sendExactToDiscordChannel(discordChannel, `*${nick}* has joined IRC`); + } + } + } }); this.ircClient.on('part', (channelName, nick, reason) => { logger.debug('Received part:', channelName, nick, reason); - if (!this.ircStatusNotices) return; + if (this.ircStatusNotices === false) return; const channel = channelName.toLowerCase(); // remove list of users when no longer in channel (as it will become out of date) if (nick === this.ircClient.nick) { @@ -200,12 +231,14 @@ class Bot { } else { logger.warn(`No channelUsers found for ${channel} when ${nick} parted.`); } - this.sendExactToDiscord(channel, `*${nick}* has left the channel (${reason})`); + if(this.ircStatusNotices) { + this.sendExactToDiscord(channel, `*${nick}* has left the channel (${reason})`); + } }); this.ircClient.on('quit', (nick, reason, channels) => { logger.debug('Received quit:', nick, channels); - if (!this.ircStatusNotices || nick === this.ircClient.nick) return; + if (this.ircStatusNotices === false || nick === this.ircClient.nick) return; channels.forEach((channelName) => { const channel = channelName.toLowerCase(); if (!this.channelUsers[channel]) { @@ -213,8 +246,13 @@ class Bot { return; } if (!this.channelUsers[channel].delete(nick)) return; - this.sendExactToDiscord(channel, `*${nick}* has quit (${reason})`); + if (this.ircStatusNotices === true) { + this.sendExactToDiscord(channel, `*${nick}* has quit (${reason})`); + } }); + if(this.ircStatusNotices !== true && this.ircStatusNotices !== false) { + this.sendExactToDiscordChannel(this.ircStatusNotices, `*${nick}* has quit (${reason})`); + } }); this.ircClient.on('names', (channelName, nicks) => { @@ -553,7 +591,7 @@ class Bot { discordChannel.send(withAuthor); } - /* Sends a message to Discord exactly as it appears */ + /* Sends a message to Discord exactly as it appears in the passed IRC channel name*/ sendExactToDiscord(channel, text) { const discordChannel = this.findDiscordChannel(channel); if (!discordChannel) return; @@ -561,6 +599,24 @@ class Bot { logger.debug('Sending special message to Discord', text, channel, '->', `#${discordChannel.name}`); discordChannel.send(text); } + + /* Sends a message to Discord exactly as it appears in the passed Discord channel name*/ + sendExactToDiscordChannel(discordChannelName, text) { + const discordChannel = discordChannelName.startsWith('#') ? this.discord.channels + .filter(c => c.type === 'text') + .find('name', discordChannelName.slice(1)) : this.discord.channels.get(discordChannelName); + + if (!discordChannel) { + logger.info( + 'Tried to send a message to a Discord channel the bot isn\'t in: ', + discordChannelName + ); + return null; + } + + logger.debug('Sending special message to Discord', text, discordChannelName, '->', `#${discordChannel.name}`); + discordChannel.send(text); + } } export default Bot; From c6a2c151e43595e1d78a16dd61c1a28adb356098 Mon Sep 17 00:00:00 2001 From: calebmckay Date: Thu, 17 Oct 2019 14:24:51 -0400 Subject: [PATCH 2/6] Send part messages in indicated channel with joins/quits. Fix eslint problems. --- lib/bot.js | 103 +++++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 51 deletions(-) diff --git a/lib/bot.js b/lib/bot.js index acff073e..c0a06acb 100644 --- a/lib/bot.js +++ b/lib/bot.js @@ -1,3 +1,4 @@ +/* eslint-disable linebreak-style */ import _ from 'lodash'; import irc from 'irc-upd'; import logger from 'winston'; @@ -34,12 +35,12 @@ class Bot { this.commandCharacters = options.commandCharacters || []; this.ircNickColor = options.ircNickColor !== false; // default to true this.channels = _.values(options.channelMapping); - if (typeof(options.ircStatusNotices) == "string") - { - this.ircStatusNotices = options.ircStatusNotices + if (typeof (options.ircStatusNotices) === 'string') { + this.ircStatusNotices = options.ircStatusNotices; // custom channel to announce join/quit } else { + // default to true (announce in respective channels) this.ircStatusNotices = options.ircStatusNotices !== false; - } // Can be true for in their respective channels, or a custom Discord channel to announce in. + } this.announceSelfJoin = options.announceSelfJoin; this.webhookOptions = options.webhooks; @@ -171,17 +172,16 @@ class Bot { if (this.channelUsers[channel].has(oldNick)) { this.channelUsers[channel].delete(oldNick); this.channelUsers[channel].add(newNick); - if(this.ircStatusNotices === true) { - this.sendExactToDiscord(channel, `*${oldNick}* is now known as ${newNick}`); + if (this.ircStatusNotices === true) { + this.sendExactToDiscord(channel, `*${oldNick}* is now known as ${newNick}`); } } } else { logger.warn(`No channelUsers found for ${channel} when ${oldNick} changed.`); - return; } }); - if(this.ircStatusNotices !== true && this.ircStatusNotices !== false) { - this.sendExactToDiscordChannel(this.ircStatusNotices, `*${oldNick}* is now known as ${newNick}`); + if (typeof (this.ircStatusNotices) === 'string') { + this.sendExactToDiscordChannel(this.ircStatusNotices, `*${oldNick}* is now known as ${newNick}`); } }); @@ -190,29 +190,28 @@ class Bot { if (this.ircStatusNotices === false) return; if (nick === this.ircClient.nick && !this.announceSelfJoin) return; if (this.ircStatusNotices === true) { - const channel = channelName.toLowerCase(); - // self-join is announced before names (which includes own nick) - // so don't add nick to channelUsers - if (nick !== this.ircClient.nick) this.channelUsers[channel].add(nick); - this.sendExactToDiscord(channel, `*${nick}* has joined the channel`); + const channel = channelName.toLowerCase(); + // self-join is announced before names (which includes own nick) + // so don't add nick to channelUsers + if (nick !== this.ircClient.nick) this.channelUsers[channel].add(nick); + this.sendExactToDiscord(channel, `*${nick}* has joined the channel`); } else { - const ircChannel = channelName.toLowerCase(); - const discordChannel = this.ircStatusNotices; - // Only send the message once per user. Do this by checking channelUsers - // and sending if user is being added for the first time. - if (nick !== this.ircClient.nick) { - var firstAdd = true; - for (var channel in this.channelUsers) { - if (this.channelUsers[channel].has(nick)) { - firstAdd = false; - break; - } + const ircChannel = channelName.toLowerCase(); + const discordChannel = this.ircStatusNotices; + // Only send the message once per user. Do this by checking channelUsers + // and sending if user is being added for the first time. + if (nick !== this.ircClient.nick) { + let firstAdd = true; + this.channelUsers.forEach((channel) => { + if (this.channelUsers[channel].has(nick)) { + firstAdd = false; } - this.channelUsers[ircChannel].add(nick); - if (firstAdd) { - this.sendExactToDiscordChannel(discordChannel, `*${nick}* has joined IRC`); - } - } + }); + this.channelUsers[ircChannel].add(nick); + if (firstAdd) { + this.sendExactToDiscordChannel(discordChannel, `*${nick}* has joined IRC`); + } + } } }); @@ -231,8 +230,10 @@ class Bot { } else { logger.warn(`No channelUsers found for ${channel} when ${nick} parted.`); } - if(this.ircStatusNotices) { - this.sendExactToDiscord(channel, `*${nick}* has left the channel (${reason})`); + if (typeof (this.ircStatusNotices) === 'string') { + this.sendExactToDiscord(this.ircStatusNotices, `*${nick}* has left ${channel} (${reason})`); + } else if (this.ircStatusNotices === true) { + this.sendExactToDiscord(channel, `*${nick}* has left the channel (${reason})`); } }); @@ -250,8 +251,8 @@ class Bot { this.sendExactToDiscord(channel, `*${nick}* has quit (${reason})`); } }); - if(this.ircStatusNotices !== true && this.ircStatusNotices !== false) { - this.sendExactToDiscordChannel(this.ircStatusNotices, `*${nick}* has quit (${reason})`); + if (typeof (this.ircStatusNotices) === 'string') { + this.sendExactToDiscordChannel(this.ircStatusNotices, `*${nick}* has quit (${reason})`); } }); @@ -591,7 +592,7 @@ class Bot { discordChannel.send(withAuthor); } - /* Sends a message to Discord exactly as it appears in the passed IRC channel name*/ + /* Sends a message to Discord exactly as it appears in the passed IRC channel name */ sendExactToDiscord(channel, text) { const discordChannel = this.findDiscordChannel(channel); if (!discordChannel) return; @@ -600,23 +601,23 @@ class Bot { discordChannel.send(text); } - /* Sends a message to Discord exactly as it appears in the passed Discord channel name*/ + /* Sends a message to Discord exactly as it appears in the passed Discord channel name */ sendExactToDiscordChannel(discordChannelName, text) { - const discordChannel = discordChannelName.startsWith('#') ? this.discord.channels - .filter(c => c.type === 'text') - .find('name', discordChannelName.slice(1)) : this.discord.channels.get(discordChannelName); - - if (!discordChannel) { - logger.info( - 'Tried to send a message to a Discord channel the bot isn\'t in: ', - discordChannelName - ); - return null; - } - - logger.debug('Sending special message to Discord', text, discordChannelName, '->', `#${discordChannel.name}`); - discordChannel.send(text); - } + const discordChannel = discordChannelName.startsWith('#') ? this.discord.channels + .filter(c => c.type === 'text') + .find('name', discordChannelName.slice(1)) : this.discord.channels.get(discordChannelName); + + if (!discordChannel) { + logger.info( + 'Tried to send a message to a Discord channel the bot isn\'t in: ', + discordChannelName + ); + return; + } + + logger.debug('Sending special message to Discord', text, discordChannelName, '->', `#${discordChannel.name}`); + discordChannel.send(text); + } } export default Bot; From 66be7554377b979d4fbab5b388491911b67b004f Mon Sep 17 00:00:00 2001 From: calebmckay Date: Fri, 18 Oct 2019 16:19:06 -0400 Subject: [PATCH 3/6] Add tests for sending join/part/quit/nick events to single Discord channel. Update README with new configuration option. --- README.md | 5 +- lib/bot.js | 31 ++++++------ test/bot-events.test.js | 104 +++++++++++++++++++++++++++++++++++----- test/bot.test.js | 6 +-- 4 files changed, 114 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 78befe59..49462564 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,10 @@ First you need to create a Discord bot user, which you can do by following the i // Makes the bot hide the username prefix for messages that start // with one of these characters (commands): "commandCharacters": ["!", "."], - "ircStatusNotices": true, // Enables notifications in Discord when people join/part in the relevant IRC channel + // Enables notifications in Discord when people join/part in the relevant IRC channel + // Passing a channel name will cause all joins/parts to appear in that channel. For example: + // "ircStatusNotices": "#joins-and-leaves" + "ircStatusNotices": true, "ignoreUsers": { "irc": ["irc_nick1", "irc_nick2"], // Ignore specified IRC nicks and do not send their messages to Discord. "discord": ["discord_nick1", "discord_nick2"] // Ignore specified Discord nicks and do not send their messages to IRC. diff --git a/lib/bot.js b/lib/bot.js index c0a06acb..2b4f5e94 100644 --- a/lib/bot.js +++ b/lib/bot.js @@ -1,4 +1,3 @@ -/* eslint-disable linebreak-style */ import _ from 'lodash'; import irc from 'irc-upd'; import logger from 'winston'; @@ -173,7 +172,7 @@ class Bot { this.channelUsers[channel].delete(oldNick); this.channelUsers[channel].add(newNick); if (this.ircStatusNotices === true) { - this.sendExactToDiscord(channel, `*${oldNick}* is now known as ${newNick}`); + this.sendExactToDiscordByIrcChannel(channel, `*${oldNick}* is now known as ${newNick}`); } } } else { @@ -181,7 +180,7 @@ class Bot { } }); if (typeof (this.ircStatusNotices) === 'string') { - this.sendExactToDiscordChannel(this.ircStatusNotices, `*${oldNick}* is now known as ${newNick}`); + this.sendExactToDiscordByDiscordChannel(this.ircStatusNotices, `*${oldNick}* is now known as ${newNick}`); } }); @@ -194,7 +193,7 @@ class Bot { // self-join is announced before names (which includes own nick) // so don't add nick to channelUsers if (nick !== this.ircClient.nick) this.channelUsers[channel].add(nick); - this.sendExactToDiscord(channel, `*${nick}* has joined the channel`); + this.sendExactToDiscordByIrcChannel(channel, `*${nick}* has joined the channel`); } else { const ircChannel = channelName.toLowerCase(); const discordChannel = this.ircStatusNotices; @@ -202,14 +201,14 @@ class Bot { // and sending if user is being added for the first time. if (nick !== this.ircClient.nick) { let firstAdd = true; - this.channelUsers.forEach((channel) => { + Object.keys(this.channelUsers).forEach((channel) => { if (this.channelUsers[channel].has(nick)) { firstAdd = false; } }); this.channelUsers[ircChannel].add(nick); if (firstAdd) { - this.sendExactToDiscordChannel(discordChannel, `*${nick}* has joined IRC`); + this.sendExactToDiscordByDiscordChannel(discordChannel, `*${nick}* has joined IRC`); } } } @@ -231,9 +230,9 @@ class Bot { logger.warn(`No channelUsers found for ${channel} when ${nick} parted.`); } if (typeof (this.ircStatusNotices) === 'string') { - this.sendExactToDiscord(this.ircStatusNotices, `*${nick}* has left ${channel} (${reason})`); + this.sendExactToDiscordByDiscordChannel(this.ircStatusNotices, `*${nick}* has left ${channel} (${reason})`); } else if (this.ircStatusNotices === true) { - this.sendExactToDiscord(channel, `*${nick}* has left the channel (${reason})`); + this.sendExactToDiscordByIrcChannel(channel, `*${nick}* has left the channel (${reason})`); } }); @@ -248,11 +247,11 @@ class Bot { } if (!this.channelUsers[channel].delete(nick)) return; if (this.ircStatusNotices === true) { - this.sendExactToDiscord(channel, `*${nick}* has quit (${reason})`); + this.sendExactToDiscordByIrcChannel(channel, `*${nick}* has quit (${reason})`); } }); if (typeof (this.ircStatusNotices) === 'string') { - this.sendExactToDiscordChannel(this.ircStatusNotices, `*${nick}* has quit (${reason})`); + this.sendExactToDiscordByDiscordChannel(this.ircStatusNotices, `*${nick}* has quit (${reason})`); } }); @@ -593,7 +592,7 @@ class Bot { } /* Sends a message to Discord exactly as it appears in the passed IRC channel name */ - sendExactToDiscord(channel, text) { + sendExactToDiscordByIrcChannel(channel, text) { const discordChannel = this.findDiscordChannel(channel); if (!discordChannel) return; @@ -602,20 +601,20 @@ class Bot { } /* Sends a message to Discord exactly as it appears in the passed Discord channel name */ - sendExactToDiscordChannel(discordChannelName, text) { - const discordChannel = discordChannelName.startsWith('#') ? this.discord.channels + sendExactToDiscordByDiscordChannel(channel, text) { + const discordChannel = channel.startsWith('#') ? this.discord.channels .filter(c => c.type === 'text') - .find('name', discordChannelName.slice(1)) : this.discord.channels.get(discordChannelName); + .find('name', channel.slice(1)) : this.discord.channels.get(channel); if (!discordChannel) { logger.info( 'Tried to send a message to a Discord channel the bot isn\'t in: ', - discordChannelName + channel ); return; } - logger.debug('Sending special message to Discord', text, discordChannelName, '->', `#${discordChannel.name}`); + logger.debug('Sending special message to Discord', text, channel, '->', `#${discordChannel.name}`); discordChannel.send(text); } } diff --git a/test/bot-events.test.js b/test/bot-events.test.js index 6ff389e2..a1a77d71 100644 --- a/test/bot-events.test.js +++ b/test/bot-events.test.js @@ -24,7 +24,8 @@ describe('Bot Events', function () { const bot = new Bot(useConfig); bot.sendToIRC = sandbox.stub(); bot.sendToDiscord = sandbox.stub(); - bot.sendExactToDiscord = sandbox.stub(); + bot.sendExactToDiscordByIrcChannel = sandbox.stub(); + bot.sendExactToDiscordByDiscordChannel = sandbox.stub(); return bot; }; @@ -118,7 +119,7 @@ describe('Bot Events', function () { const oldnick = 'user1'; const newnick = 'user2'; this.bot.ircClient.emit('nick', oldnick, newnick, [channel]); - this.bot.sendExactToDiscord.should.not.have.been.called; + this.bot.sendExactToDiscordByIrcChannel.should.not.have.been.called; }); it('should send name change event to discord', function () { @@ -138,7 +139,33 @@ describe('Bot Events', function () { const formattedText = `*${oldNick}* is now known as ${newNick}`; const channelNicksAfter = new Set([bot.nickname, newNick]); bot.ircClient.emit('nick', oldNick, newNick, [channel1, channel2, channel3]); - bot.sendExactToDiscord.should.have.been.calledWithExactly(channel1, formattedText); + bot.sendExactToDiscordByIrcChannel.should.have.been.calledWithExactly(channel1, formattedText); + bot.channelUsers.should.deep.equal({ '#channel1': channelNicksAfter, '#channel2': staticChannel }); + }); + + it('should send name change event to specified discord channel', function () { + const channel1 = '#channel1'; + const channel2 = '#channel2'; + const channel3 = '#channel3'; + const oldNick = 'user1'; + const newNick = 'user2'; + const user3 = 'user3'; + const notifyChannel = '#joins-and-leaves'; + const bot = createBot({ ...config, ircStatusNotices: notifyChannel }); + const staticChannel = new Set([bot.nickname, user3]); + bot.connect(); + bot.ircClient.emit('names', channel1, { [bot.nickname]: '', [oldNick]: '' }); + bot.ircClient.emit('names', channel2, { [bot.nickname]: '', [user3]: '' }); + const channelNicksPre = new Set([bot.nickname, oldNick]); + bot.channelUsers.should.deep.equal({ '#channel1': channelNicksPre, '#channel2': staticChannel }); + const formattedText = `*${oldNick}* is now known as ${newNick}`; + const channelNicksAfter = new Set([bot.nickname, newNick]); + bot.ircClient.emit('nick', oldNick, newNick, [channel1, channel2, channel3]); + bot.sendExactToDiscordByDiscordChannel.should.have.been.calledOnce; + bot.sendExactToDiscordByDiscordChannel.should.have.been.calledWithExactly( + notifyChannel, + formattedText + ); bot.channelUsers.should.deep.equal({ '#channel1': channelNicksAfter, '#channel2': staticChannel }); }); @@ -184,7 +211,22 @@ describe('Bot Events', function () { const nick = 'user'; const text = `*${nick}* has joined the channel`; bot.ircClient.emit('join', channel, nick); - bot.sendExactToDiscord.should.have.been.calledWithExactly(channel, text); + bot.sendExactToDiscordByIrcChannel.should.have.been.calledWithExactly(channel, text); + const channelNicks = new Set([bot.nickname, nick]); + bot.channelUsers.should.deep.equal({ '#channel': channelNicks }); + }); + + it('should send join messages to specified discord channel when config enabled', function () { + const notifyChannel = '#joins-and-leaves'; + const bot = createBot({ ...config, ircStatusNotices: notifyChannel }); + bot.connect(); + const channel = '#channel'; + bot.ircClient.emit('names', channel, { [bot.nickname]: '' }); + const nick = 'user'; + const text = `*${nick}* has joined IRC`; + bot.ircClient.emit('join', channel, nick); + bot.sendExactToDiscordByDiscordChannel.should.have.been.calledOnce; + bot.sendExactToDiscordByDiscordChannel.should.have.been.calledWithExactly(notifyChannel, text); const channelNicks = new Set([bot.nickname, nick]); bot.channelUsers.should.deep.equal({ '#channel': channelNicks }); }); @@ -196,7 +238,7 @@ describe('Bot Events', function () { bot.ircClient.emit('names', channel, { [bot.nickname]: '' }); const nick = bot.nickname; bot.ircClient.emit('join', channel, nick); - bot.sendExactToDiscord.should.not.have.been.called; + bot.sendExactToDiscordByIrcChannel.should.not.have.been.called; const channelNicks = new Set([bot.nickname]); bot.channelUsers.should.deep.equal({ '#channel': channelNicks }); }); @@ -210,7 +252,7 @@ describe('Bot Events', function () { const nick = this.bot.nickname; const text = `*${nick}* has joined the channel`; bot.ircClient.emit('join', channel, nick); - bot.sendExactToDiscord.should.have.been.calledWithExactly(channel, text); + bot.sendExactToDiscordByIrcChannel.should.have.been.calledWithExactly(channel, text); }); it('should send part messages to discord when config enabled', function () { @@ -224,7 +266,26 @@ describe('Bot Events', function () { const reason = 'Leaving'; const text = `*${nick}* has left the channel (${reason})`; bot.ircClient.emit('part', channel, nick, reason); - bot.sendExactToDiscord.should.have.been.calledWithExactly(channel, text); + bot.sendExactToDiscordByIrcChannel.should.have.been.calledWithExactly(channel, text); + // it should remove the nickname from the channelUsers list + const channelNicks = new Set([bot.nickname]); + bot.channelUsers.should.deep.equal({ '#channel': channelNicks }); + }); + + it('should send part messages to specified discord channel when config enabled', function () { + const notifyChannel = '#joins-and-leaves'; + const bot = createBot({ ...config, ircStatusNotices: notifyChannel }); + bot.connect(); + const channel = '#channel'; + const nick = 'user'; + bot.ircClient.emit('names', channel, { [bot.nickname]: '', [nick]: '' }); + const originalNicks = new Set([bot.nickname, nick]); + bot.channelUsers.should.deep.equal({ '#channel': originalNicks }); + const reason = 'Leaving'; + const text = `*${nick}* has left ${channel} (${reason})`; + bot.ircClient.emit('part', channel, nick, reason); + bot.sendExactToDiscordByDiscordChannel.should.have.been.calledOnce; + bot.sendExactToDiscordByDiscordChannel.should.have.been.calledWithExactly(notifyChannel, text); // it should remove the nickname from the channelUsers list const channelNicks = new Set([bot.nickname]); bot.channelUsers.should.deep.equal({ '#channel': channelNicks }); @@ -239,7 +300,7 @@ describe('Bot Events', function () { bot.channelUsers.should.deep.equal({ '#channel': originalNicks }); const reason = 'Leaving'; bot.ircClient.emit('part', channel, bot.nickname, reason); - bot.sendExactToDiscord.should.not.have.been.called; + bot.sendExactToDiscordByIrcChannel.should.not.have.been.called; // it should remove the nickname from the channelUsers list bot.channelUsers.should.deep.equal({}); }); @@ -258,9 +319,28 @@ describe('Bot Events', function () { const text = `*${nick}* has quit (${reason})`; // send quit message for all channels on server, as the node-irc library does bot.ircClient.emit('quit', nick, reason, [channel1, channel2, channel3]); - bot.sendExactToDiscord.should.have.been.calledTwice; - bot.sendExactToDiscord.getCall(0).args.should.deep.equal([channel1, text]); - bot.sendExactToDiscord.getCall(1).args.should.deep.equal([channel3, text]); + bot.sendExactToDiscordByIrcChannel.should.have.been.calledTwice; + bot.sendExactToDiscordByIrcChannel.getCall(0).args.should.deep.equal([channel1, text]); + bot.sendExactToDiscordByIrcChannel.getCall(1).args.should.deep.equal([channel3, text]); + }); + + it('should send quit messages to a specified discord channel when config enabled', function () { + const notifyChannel = '#joins-and-leaves'; + const bot = createBot({ ...config, ircStatusNotices: notifyChannel }); + bot.connect(); + const channel1 = '#channel1'; + const channel2 = '#channel2'; + const channel3 = '#channel3'; + const nick = 'user'; + bot.ircClient.emit('names', channel1, { [bot.nickname]: '', [nick]: '' }); + bot.ircClient.emit('names', channel2, { [bot.nickname]: '' }); + bot.ircClient.emit('names', channel3, { [bot.nickname]: '', [nick]: '' }); + const reason = 'Quit: Leaving'; + const text = `*${nick}* has quit (${reason})`; + // send quit message for all channels on server, as the node-irc library does + bot.ircClient.emit('quit', nick, reason, [channel1, channel2, channel3]); + bot.sendExactToDiscordByDiscordChannel.should.have.been.calledOnce; + bot.sendExactToDiscordByDiscordChannel.should.have.been.calledWithExactly(notifyChannel, text); }); it('should not crash with join/part/quit messages and weird channel casing', function () { @@ -291,7 +371,7 @@ describe('Bot Events', function () { bot.ircClient.emit('part', channel, nick, reason); bot.ircClient.emit('join', channel, nick); bot.ircClient.emit('quit', nick, reason, [channel]); - bot.sendExactToDiscord.should.not.have.been.called; + bot.sendExactToDiscordByIrcChannel.should.not.have.been.called; }); it('should warn if it receives a part/quit before a names event', function () { diff --git a/test/bot.test.js b/test/bot.test.js index cea05fb2..eda15176 100644 --- a/test/bot.test.js +++ b/test/bot.test.js @@ -126,7 +126,7 @@ describe('Bot', function () { it( 'should not send special messages to discord if the channel isn\'t in the channel mapping', function () { - this.bot.sendExactToDiscord('#no-irc', 'message'); + this.bot.sendExactToDiscordByIrcChannel('#no-irc', 'message'); this.sendStub.should.not.have.been.called; } ); @@ -134,7 +134,7 @@ describe('Bot', function () { it( 'should not send special messages to discord if it isn\'t in the channel', function () { - this.bot.sendExactToDiscord('#otherirc', 'message'); + this.bot.sendExactToDiscordByIrcChannel('#otherirc', 'message'); this.sendStub.should.not.have.been.called; } ); @@ -142,7 +142,7 @@ describe('Bot', function () { it( 'should send special messages to discord', function () { - this.bot.sendExactToDiscord('#irc', 'message'); + this.bot.sendExactToDiscordByIrcChannel('#irc', 'message'); this.sendStub.should.have.been.calledWith('message'); this.debugSpy.should.have.been.calledWith('Sending special message to Discord', 'message', '#irc', '->', '#discord'); } From 569f82a9e0008a91e907bdf6ebdb58cff095cc5c Mon Sep 17 00:00:00 2001 From: calebmckay Date: Fri, 18 Oct 2019 17:04:08 -0400 Subject: [PATCH 4/6] Add additional tests for multiple joins and new sendExactToDiscord function --- lib/bot.js | 6 +++--- test/bot-events.test.js | 16 ++++++++++++++++ test/bot.test.js | 19 ++++++++++++++++++- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/lib/bot.js b/lib/bot.js index 2b4f5e94..8ab57907 100644 --- a/lib/bot.js +++ b/lib/bot.js @@ -602,9 +602,9 @@ class Bot { /* Sends a message to Discord exactly as it appears in the passed Discord channel name */ sendExactToDiscordByDiscordChannel(channel, text) { - const discordChannel = channel.startsWith('#') ? this.discord.channels + const discordChannel = this.discord.channels .filter(c => c.type === 'text') - .find('name', channel.slice(1)) : this.discord.channels.get(channel); + .find('name', channel.slice(1)); if (!discordChannel) { logger.info( @@ -614,7 +614,7 @@ class Bot { return; } - logger.debug('Sending special message to Discord', text, channel, '->', `#${discordChannel.name}`); + logger.debug('Sending special message to Discord', text, `#${discordChannel.name}`); discordChannel.send(text); } } diff --git a/test/bot-events.test.js b/test/bot-events.test.js index a1a77d71..fbcca1d4 100644 --- a/test/bot-events.test.js +++ b/test/bot-events.test.js @@ -231,6 +231,22 @@ describe('Bot Events', function () { bot.channelUsers.should.deep.equal({ '#channel': channelNicks }); }); + it('should send single join message to specified discord channel when config enabled and multiple channels are joined', function () { + const notifyChannel = '#joins-and-leaves'; + const bot = createBot({ ...config, ircStatusNotices: notifyChannel }); + bot.connect(); + const channel1 = '#channel1'; + const channel2 = '#channel2'; + bot.ircClient.emit('names', channel1, { [bot.nickname]: '' }); + bot.ircClient.emit('names', channel2, { [bot.nickname]: '' }); + const nick = 'user'; + const text = `*${nick}* has joined IRC`; + bot.ircClient.emit('join', channel1, nick); + bot.ircClient.emit('join', channel2, nick); + bot.sendExactToDiscordByDiscordChannel.should.have.been.calledOnce; + bot.sendExactToDiscordByDiscordChannel.should.have.been.calledWithExactly(notifyChannel, text); + }); + it('should not announce itself joining by default', function () { const bot = createBot({ ...config, ircStatusNotices: true }); bot.connect(); diff --git a/test/bot.test.js b/test/bot.test.js index eda15176..28b1cfb0 100644 --- a/test/bot.test.js +++ b/test/bot.test.js @@ -124,7 +124,7 @@ describe('Bot', function () { }); it( - 'should not send special messages to discord if the channel isn\'t in the channel mapping', + 'should not send special messages to discord if the channel isn\'t in the IRC channel mapping', function () { this.bot.sendExactToDiscordByIrcChannel('#no-irc', 'message'); this.sendStub.should.not.have.been.called; @@ -148,6 +148,23 @@ describe('Bot', function () { } ); + it( + 'should not send special messages to discord by Discord channel if it isn\'t in the channel', + function () { + this.bot.sendExactToDiscordByIrcChannel('#notinchannel', 'message'); + this.sendStub.should.not.have.been.called; + } + ); + + it( + 'should send special messages to discord by Discord channel', + function () { + this.bot.sendExactToDiscordByDiscordChannel('#discord', 'message'); + this.sendStub.should.have.been.calledWith('message'); + this.debugSpy.should.have.been.calledWith('Sending special message to Discord', 'message', '#discord'); + } + ); + it('should not color irc messages if the option is disabled', function () { const text = 'testmessage'; const newConfig = { ...config, ircNickColor: false }; From dc039c08effb41bede397f261c9525dcb5e578a3 Mon Sep 17 00:00:00 2001 From: calebmckay Date: Fri, 18 Oct 2019 17:48:30 -0400 Subject: [PATCH 5/6] Change default ircStatusNotices to false. --- lib/bot.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/bot.js b/lib/bot.js index 8ab57907..a6556a1e 100644 --- a/lib/bot.js +++ b/lib/bot.js @@ -37,8 +37,8 @@ class Bot { if (typeof (options.ircStatusNotices) === 'string') { this.ircStatusNotices = options.ircStatusNotices; // custom channel to announce join/quit } else { - // default to true (announce in respective channels) - this.ircStatusNotices = options.ircStatusNotices !== false; + // default to false (don't announce) + this.ircStatusNotices = options.ircStatusNotices || false; } this.announceSelfJoin = options.announceSelfJoin; this.webhookOptions = options.webhooks; From 49bc041407fc3b32a46381a0034d0375acfdbc48 Mon Sep 17 00:00:00 2001 From: calebmckay Date: Fri, 18 Oct 2019 18:08:38 -0400 Subject: [PATCH 6/6] Fix typo in unit test --- test/bot.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/bot.test.js b/test/bot.test.js index 28b1cfb0..6838a4d0 100644 --- a/test/bot.test.js +++ b/test/bot.test.js @@ -151,8 +151,10 @@ describe('Bot', function () { it( 'should not send special messages to discord by Discord channel if it isn\'t in the channel', function () { - this.bot.sendExactToDiscordByIrcChannel('#notinchannel', 'message'); + const channel = '#notinchannel'; + this.bot.sendExactToDiscordByDiscordChannel(channel, 'message'); this.sendStub.should.not.have.been.called; + this.infoSpy.should.have.been.calledWith('Tried to send a message to a Discord channel the bot isn\'t in: ', channel); } );