friends-irc
Version:
Connects IRC and Friends channels by sending messages back and forth.
162 lines (133 loc) • 4.55 kB
JavaScript
var _ = require('lodash')
var irc = require('irc')
var logger = require('winston')
var subleveldown = require('subleveldown')
var createSwarm = require('./swarm')
var errors = require('./errors')
var validateChannelMapping = require('./validators').validateChannelMapping
var emojis = require('./emoji')
var REQUIRED_FIELDS = ['server', 'nickname', 'channelMapping']
/**
* An IRC bot, works as a middleman for all communication
* @param {object} options - server, nickname, channelMapping, outgoingToken, incomingURL
*/
function Bot (db, options) {
REQUIRED_FIELDS.forEach(function (field) {
if (!options[field]) {
throw new errors.ConfigurationError('Missing configuration field ' + field)
}
})
validateChannelMapping(options.channelMapping)
this.db = db
this.server = options.server
this.nickname = options.nickname
this.ircOptions = options.ircOptions
this.channels = _.values(options.channelMapping)
this.channelMapping = {}
// Remove channel passwords from the mapping
_.forOwn(options.channelMapping, function (ircChan, friendsChan) {
this.channelMapping[friendsChan] = ircChan.split(' ')[0]
}, this)
this.invertedMapping = _.invert(this.channelMapping)
this.autoSendCommands = options.autoSendCommands || []
}
Bot.prototype.connect = function () {
logger.debug('Connecting to IRC and Friends')
this.swarm = createSwarm(subleveldown(this.db, 'swarm'))
Object.keys(this.channelMapping).forEach(this.swarm.addChannel)
var ircOptions = _.assign({
userName: this.nickname,
realName: this.nickname,
channels: this.channels,
floodProtection: true,
floodProtectionDelay: 500
}, this.ircOptions)
this.ircClient = new irc.Client(this.server, this.nickname, ircOptions)
this.attachListeners()
}
Bot.prototype.attachListeners = function () {
this.ircClient.on('registered', function (message) {
logger.debug('Registered event: ', message)
var connectedAt = Date.now()
this.autoSendCommands.forEach(function (element) {
this.ircClient.send.apply(this.ircClient, element)
}, this)
this.swarm.process(function (message, cb) {
if (message.timestamp > connectedAt) {
logger.debug('Swarm process', message)
this.sendToIRC(message)
}
cb()
}.bind(this))
}.bind(this))
this.ircClient.on('error', function (error) {
logger.error('Received error event from IRC', error)
})
this.swarm.on('error', function (error) {
logger.error('Received error event from Swarm', error)
})
this.ircClient.on('message', this.sendToSwarm.bind(this))
this.ircClient.on('invite', function (channel, from) {
logger.debug('Received invite:', channel, from)
if (!this.invertedMapping[channel]) {
logger.debug('Channel not found in config, not joining:', channel)
} else {
this.ircClient.join(channel)
logger.debug('Joining channel:', channel)
}
}.bind(this))
}
Bot.prototype.parseText = function (text) {
return text
.replace(/\n|\r\n|\r/g, ' ')
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/\:(\w+)\:/g, function (match, emoji) {
if (emoji in emojis) {
return emojis[emoji]
}
return match
})
}
Bot.prototype.checkDuplicate = function (message) {
if (!this.lastSent) return false
for (var key in this.lastSent) {
if (this.lastSent.hasOwnProperty(key)) {
if (message[key] !== this.lastSent[key]) {
return false
}
}
}
return true
}
Bot.prototype.sendToIRC = function (message) {
if (this.checkDuplicate(message)) return
var ircChannel = this.channelMapping[message.channel]
logger.debug('Trying to send to IRC', ircChannel, message)
if (ircChannel) {
var text = '<' + message.username + '> ' + this.parseText(message.text)
logger.debug('Sending message to IRC', ircChannel, text)
this.ircClient.say(ircChannel, text)
}
}
Bot.prototype.sendToSwarm = function (author, channel, text) {
var swarmChannel = this.invertedMapping[channel]
if (swarmChannel) {
if (!swarmChannel) {
logger.info('Tried to send a message to a channel the bot isn\'t in: ',
swarmChannel)
return
}
var message = {
text: text,
username: 'Anonymous ' + author,
channel: swarmChannel,
timestamp: Date.now()
}
logger.debug('Sending message to Swarm', text, channel, '->', swarmChannel, '- by', author)
this.swarm.send(message)
this.lastSent = message
}
}
module.exports = Bot