* Upgrade discord.js to 12.2.0 * Use cache accessors in Discord client * Replace sendMessage with send * Use avatarURL method * Update test stubs for discord.js 12 * Disconnect clients at end of tests * Fix disabling webhook mentions when bot lacks permissions
370 lines
15 KiB
JavaScript
370 lines
15 KiB
JavaScript
/* eslint-disable no-unused-expressions, prefer-arrow-callback */
|
|
import chai from 'chai';
|
|
import sinonChai from 'sinon-chai';
|
|
import sinon from 'sinon';
|
|
import irc from 'irc-upd';
|
|
import discord from 'discord.js';
|
|
import Bot from '../lib/bot';
|
|
import logger from '../lib/logger';
|
|
import createDiscordStub from './stubs/discord-stub';
|
|
import createWebhookStub from './stubs/webhook-stub';
|
|
import ClientStub from './stubs/irc-client-stub';
|
|
import config from './fixtures/single-test-config.json';
|
|
|
|
chai.should();
|
|
chai.use(sinonChai);
|
|
|
|
describe('Bot Events', function () {
|
|
const sandbox = sinon.createSandbox({
|
|
useFakeTimers: false,
|
|
useFakeServer: false
|
|
});
|
|
|
|
const createBot = (optConfig = null) => {
|
|
const useConfig = optConfig || config;
|
|
const bot = new Bot(useConfig);
|
|
bot.sendToIRC = sandbox.stub();
|
|
bot.sendToDiscord = sandbox.stub();
|
|
bot.sendExactToDiscord = sandbox.stub();
|
|
return bot;
|
|
};
|
|
|
|
beforeEach(function () {
|
|
this.infoSpy = sandbox.stub(logger, 'info');
|
|
this.debugSpy = sandbox.stub(logger, 'debug');
|
|
this.warnSpy = sandbox.stub(logger, 'warn');
|
|
this.errorSpy = sandbox.stub(logger, 'error');
|
|
this.sendStub = sandbox.stub();
|
|
irc.Client = ClientStub;
|
|
discord.Client = createDiscordStub(this.sendStub);
|
|
discord.WebhookClient = createWebhookStub(this.sendStub);
|
|
ClientStub.prototype.send = sandbox.stub();
|
|
ClientStub.prototype.join = sandbox.stub();
|
|
this.bot = createBot();
|
|
this.bot.connect();
|
|
});
|
|
|
|
afterEach(function () {
|
|
this.bot.disconnect();
|
|
sandbox.restore();
|
|
});
|
|
|
|
it('should log on discord ready event', function () {
|
|
this.bot.discord.emit('ready');
|
|
this.infoSpy.should.have.been.calledWithExactly('Connected to Discord');
|
|
});
|
|
|
|
it('should log on irc registered event', function () {
|
|
const message = 'registered';
|
|
this.bot.ircClient.emit('registered', message);
|
|
this.infoSpy.should.have.been.calledWithExactly('Connected to IRC');
|
|
this.debugSpy.should.have.been.calledWithExactly('Registered event: ', message);
|
|
});
|
|
|
|
it('should try to send autoSendCommands on registered IRC event', function () {
|
|
this.bot.ircClient.emit('registered');
|
|
ClientStub.prototype.send.should.have.been.calledTwice;
|
|
ClientStub.prototype.send.getCall(0)
|
|
.args.should.deep.equal(config.autoSendCommands[0]);
|
|
ClientStub.prototype.send.getCall(1)
|
|
.args.should.deep.equal(config.autoSendCommands[1]);
|
|
});
|
|
|
|
it('should error log on error events', function () {
|
|
const discordError = new Error('discord');
|
|
const ircError = new Error('irc');
|
|
this.bot.discord.emit('error', discordError);
|
|
this.bot.ircClient.emit('error', ircError);
|
|
this.errorSpy.getCall(0).args[0].should.equal('Received error event from Discord');
|
|
this.errorSpy.getCall(0).args[1].should.equal(discordError);
|
|
this.errorSpy.getCall(1).args[0].should.equal('Received error event from IRC');
|
|
this.errorSpy.getCall(1).args[1].should.equal(ircError);
|
|
});
|
|
|
|
it('should warn log on warn events from discord', function () {
|
|
const discordError = new Error('discord');
|
|
this.bot.discord.emit('warn', discordError);
|
|
const [message, error] = this.warnSpy.firstCall.args;
|
|
message.should.equal('Received warn event from Discord');
|
|
error.should.equal(discordError);
|
|
});
|
|
|
|
it('should send messages to irc if correct', function () {
|
|
const message = {
|
|
type: 'message'
|
|
};
|
|
|
|
this.bot.discord.emit('message', message);
|
|
this.bot.sendToIRC.should.have.been.calledWithExactly(message);
|
|
});
|
|
|
|
it('should send messages to discord', function () {
|
|
const channel = '#channel';
|
|
const author = 'user';
|
|
const text = 'hi';
|
|
this.bot.ircClient.emit('message', author, channel, text);
|
|
this.bot.sendToDiscord.should.have.been.calledWithExactly(author, channel, text);
|
|
});
|
|
|
|
it('should send notices to discord', function () {
|
|
const channel = '#channel';
|
|
const author = 'user';
|
|
const text = 'hi';
|
|
const formattedText = `*${text}*`;
|
|
this.bot.ircClient.emit('notice', author, channel, text);
|
|
this.bot.sendToDiscord.should.have.been.calledWithExactly(author, channel, formattedText);
|
|
});
|
|
|
|
it('should not send name change event to discord', function () {
|
|
const channel = '#channel';
|
|
const oldnick = 'user1';
|
|
const newnick = 'user2';
|
|
this.bot.ircClient.emit('nick', oldnick, newnick, [channel]);
|
|
this.bot.sendExactToDiscord.should.not.have.been.called;
|
|
});
|
|
|
|
it('should send name change event to discord', function () {
|
|
const channel1 = '#channel1';
|
|
const channel2 = '#channel2';
|
|
const channel3 = '#channel3';
|
|
const oldNick = 'user1';
|
|
const newNick = 'user2';
|
|
const user3 = 'user3';
|
|
const bot = createBot({ ...config, ircStatusNotices: true });
|
|
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.sendExactToDiscord.should.have.been.calledWithExactly(channel1, formattedText);
|
|
bot.channelUsers.should.deep.equal({ '#channel1': channelNicksAfter, '#channel2': staticChannel });
|
|
});
|
|
|
|
it('should send actions to discord', function () {
|
|
const channel = '#channel';
|
|
const author = 'user';
|
|
const text = 'hi';
|
|
const formattedText = '_hi_';
|
|
const message = {};
|
|
this.bot.ircClient.emit('action', author, channel, text, message);
|
|
this.bot.sendToDiscord.should.have.been.calledWithExactly(author, channel, formattedText);
|
|
});
|
|
|
|
it('should keep track of users through names event when irc status notices enabled', function () {
|
|
const bot = createBot({ ...config, ircStatusNotices: true });
|
|
bot.connect();
|
|
bot.channelUsers.should.be.an('object');
|
|
const channel = '#channel';
|
|
// nick => '' means the user is not a special user
|
|
const nicks = {
|
|
[bot.nickname]: '', user: '', user2: '@', user3: '+'
|
|
};
|
|
bot.ircClient.emit('names', channel, nicks);
|
|
const channelNicks = new Set([bot.nickname, 'user', 'user2', 'user3']);
|
|
bot.channelUsers.should.deep.equal({ '#channel': channelNicks });
|
|
});
|
|
|
|
it('should lowercase the channelUsers mapping', function () {
|
|
const bot = createBot({ ...config, ircStatusNotices: true });
|
|
bot.connect();
|
|
const channel = '#channelName';
|
|
const nicks = { [bot.nickname]: '' };
|
|
bot.ircClient.emit('names', channel, nicks);
|
|
const channelNicks = new Set([bot.nickname]);
|
|
bot.channelUsers.should.deep.equal({ '#channelname': channelNicks });
|
|
});
|
|
|
|
it('should send join messages to discord when config enabled', function () {
|
|
const bot = createBot({ ...config, ircStatusNotices: true });
|
|
bot.connect();
|
|
const channel = '#channel';
|
|
bot.ircClient.emit('names', channel, { [bot.nickname]: '' });
|
|
const nick = 'user';
|
|
const text = `*${nick}* has joined the channel`;
|
|
bot.ircClient.emit('join', channel, nick);
|
|
bot.sendExactToDiscord.should.have.been.calledWithExactly(channel, text);
|
|
const channelNicks = new Set([bot.nickname, nick]);
|
|
bot.channelUsers.should.deep.equal({ '#channel': channelNicks });
|
|
});
|
|
|
|
it('should not announce itself joining by default', function () {
|
|
const bot = createBot({ ...config, ircStatusNotices: true });
|
|
bot.connect();
|
|
const channel = '#channel';
|
|
bot.ircClient.emit('names', channel, { [bot.nickname]: '' });
|
|
const nick = bot.nickname;
|
|
bot.ircClient.emit('join', channel, nick);
|
|
bot.sendExactToDiscord.should.not.have.been.called;
|
|
const channelNicks = new Set([bot.nickname]);
|
|
bot.channelUsers.should.deep.equal({ '#channel': channelNicks });
|
|
});
|
|
|
|
it('should announce the bot itself when config enabled', function () {
|
|
// self-join is announced before names (which includes own nick)
|
|
// hence don't trigger a names and don't expect anything of bot.channelUsers
|
|
const bot = createBot({ ...config, ircStatusNotices: true, announceSelfJoin: true });
|
|
bot.connect();
|
|
const channel = '#channel';
|
|
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);
|
|
});
|
|
|
|
it('should send part messages to discord when config enabled', function () {
|
|
const bot = createBot({ ...config, ircStatusNotices: true });
|
|
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 the channel (${reason})`;
|
|
bot.ircClient.emit('part', channel, nick, reason);
|
|
bot.sendExactToDiscord.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 not announce itself leaving a channel', function () {
|
|
const bot = createBot({ ...config, ircStatusNotices: true });
|
|
bot.connect();
|
|
const channel = '#channel';
|
|
bot.ircClient.emit('names', channel, { [bot.nickname]: '', user: '' });
|
|
const originalNicks = new Set([bot.nickname, 'user']);
|
|
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;
|
|
// it should remove the nickname from the channelUsers list
|
|
bot.channelUsers.should.deep.equal({});
|
|
});
|
|
|
|
it('should only send quit messages to discord for channels the user is tracked in', function () {
|
|
const bot = createBot({ ...config, ircStatusNotices: true });
|
|
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.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]);
|
|
});
|
|
|
|
it('should not crash with join/part/quit messages and weird channel casing', function () {
|
|
const bot = createBot({ ...config, ircStatusNotices: true });
|
|
bot.connect();
|
|
|
|
function wrap() {
|
|
const nick = 'user';
|
|
const reason = 'Leaving';
|
|
bot.ircClient.emit('names', '#Channel', { [bot.nickname]: '' });
|
|
bot.ircClient.emit('join', '#cHannel', nick);
|
|
bot.ircClient.emit('part', '#chAnnel', nick, reason);
|
|
bot.ircClient.emit('join', '#chaNnel', nick);
|
|
bot.ircClient.emit('quit', nick, reason, ['#chanNel']);
|
|
}
|
|
(wrap).should.not.throw();
|
|
});
|
|
|
|
it('should be possible to disable join/part/quit messages', function () {
|
|
const bot = createBot({ ...config, ircStatusNotices: false });
|
|
bot.connect();
|
|
const channel = '#channel';
|
|
const nick = 'user';
|
|
const reason = 'Leaving';
|
|
|
|
bot.ircClient.emit('names', channel, { [bot.nickname]: '' });
|
|
bot.ircClient.emit('join', channel, nick);
|
|
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;
|
|
});
|
|
|
|
it('should warn if it receives a part/quit before a names event', function () {
|
|
const bot = createBot({ ...config, ircStatusNotices: true });
|
|
bot.connect();
|
|
const channel = '#channel';
|
|
const reason = 'Leaving';
|
|
|
|
bot.ircClient.emit('part', channel, 'user1', reason);
|
|
bot.ircClient.emit('quit', 'user2', reason, [channel]);
|
|
this.warnSpy.should.have.been.calledTwice;
|
|
this.warnSpy.getCall(0).args.should.deep.equal([`No channelUsers found for ${channel} when user1 parted.`]);
|
|
this.warnSpy.getCall(1).args.should.deep.equal([`No channelUsers found for ${channel} when user2 quit, ignoring.`]);
|
|
});
|
|
|
|
it('should not crash if it uses a different name from config', function () {
|
|
// this can happen when a user with the same name is already connected
|
|
const bot = createBot({ ...config, nickname: 'testbot' });
|
|
bot.connect();
|
|
const newName = 'testbot1';
|
|
bot.ircClient.nick = newName;
|
|
function wrap() {
|
|
bot.ircClient.emit('join', '#channel', newName);
|
|
}
|
|
(wrap).should.not.throw;
|
|
});
|
|
|
|
it('should not listen to discord debug messages in production', function () {
|
|
logger.level = 'info';
|
|
const bot = createBot();
|
|
bot.connect();
|
|
const listeners = bot.discord.listeners('debug');
|
|
listeners.length.should.equal(0);
|
|
});
|
|
|
|
it('should listen to discord debug messages in development', function () {
|
|
logger.level = 'debug';
|
|
const bot = createBot();
|
|
bot.connect();
|
|
const listeners = bot.discord.listeners('debug');
|
|
listeners.length.should.equal(1);
|
|
});
|
|
|
|
it('should join channels when invited', function () {
|
|
const channel = '#irc';
|
|
const author = 'user';
|
|
this.bot.ircClient.emit('invite', channel, author);
|
|
const firstCall = this.debugSpy.getCall(1);
|
|
firstCall.args[0].should.equal('Received invite:');
|
|
firstCall.args[1].should.equal(channel);
|
|
firstCall.args[2].should.equal(author);
|
|
|
|
ClientStub.prototype.join.should.have.been.calledWith(channel);
|
|
const secondCall = this.debugSpy.getCall(2);
|
|
secondCall.args[0].should.equal('Joining channel:');
|
|
secondCall.args[1].should.equal(channel);
|
|
});
|
|
|
|
it('should not join channels that aren\'t in the channel mapping', function () {
|
|
const channel = '#wrong';
|
|
const author = 'user';
|
|
this.bot.ircClient.emit('invite', channel, author);
|
|
const firstCall = this.debugSpy.getCall(1);
|
|
firstCall.args[0].should.equal('Received invite:');
|
|
firstCall.args[1].should.equal(channel);
|
|
firstCall.args[2].should.equal(author);
|
|
|
|
ClientStub.prototype.join.should.not.have.been.called;
|
|
const secondCall = this.debugSpy.getCall(2);
|
|
secondCall.args[0].should.equal('Channel not found in config, not joining:');
|
|
secondCall.args[1].should.equal(channel);
|
|
});
|
|
});
|