|
# Hubot dependencies |
|
{Robot, Adapter, TextMessage, EnterMessage, LeaveMessage, Response} = require 'hubot' |
|
|
|
# Irc library |
|
Irc = require 'irc' |
|
Log = require 'log' |
|
|
|
logger = new Log process.env.HUBOT_LOG_LEVEL or 'info' |
|
|
|
class IrcBot extends Adapter |
|
_determineMessageType: (message) -> |
|
logger.debug("Parsing message") |
|
logger.debug("Message Type: #{message[0].constructor}") |
|
if (message[0].constructor == String) or (message[0].constructor == Array) |
|
logger.debug("Setting type to plain") |
|
return "plain" |
|
if (message[0] instanceof Object) |
|
logger.debug("Setting type to object") |
|
return "object" |
|
|
|
send: (envelope, strings...) -> |
|
logger.error("Envelope: #{JSON.stringify(envelope)}") |
|
logger.debug("Message: #{strings}") |
|
mType = @_determineMessageType(strings) |
|
target = @_getTargetFromEnvelope envelope |
|
unless target |
|
logger.error("Not sure who to send to. Envelope: #{JSON.stringify(envelope)}") |
|
_http = @robot.http(process.env.HUBOT_SLACK_WEBHOOK_URL) |
|
if mType == "plain" |
|
logger.debug("Responding with plain text") |
|
if envelope.user? and envelope.user.reply_to? |
|
for str in strings |
|
logger.debug("Sending via irc") |
|
@bot.say target, str |
|
else |
|
for str in strings |
|
logger.debug("A string appears: #{str}") |
|
if str.constructor == Array |
|
str = str.join("\n") |
|
data = |
|
text: str |
|
channel: target |
|
|
|
logger.debug("Data: #{JSON.stringify(data)}") |
|
|
|
_http.post(JSON.stringify(data)) (err, res, body) -> |
|
if err |
|
logger.error "(#{res.statusCode}): Couldn't send message to Slack: ", body |
|
else |
|
logger.debug("Posted to slack webhook: #{JSON.stringify(res)} - #{body}") |
|
logger.info("Posted via webhook") |
|
|
|
if mType == "object" |
|
logger.debug("Responding with an object") |
|
logger.debug("Object is #{JSON.stringify(strings)}") |
|
for o in strings |
|
if envelope.user.room? or envelope.room? |
|
logger.debug("Sending object via webhook to #{process.env.HUBOT_SLACK_WEBHOOK_URL}") |
|
logger.debug("current element is: #{JSON.stringify(o)}") |
|
o.channel = target |
|
_http.post(JSON.stringify(o)) (err, res, body) -> |
|
if err |
|
logger.error("#{res.statusCode}: Cloudn't send message to Slack: ", body) |
|
else |
|
logger.info("Posted o via webhook") |
|
|
|
else |
|
logger.info("Sending object via irc to #{target}") |
|
logger.debug("Checking if there are fallback attachments") |
|
logger.debug("o is #{JSON.stringify(o)}") |
|
if o.fallback |
|
logger.info("Found an attachments payload") |
|
@bot.say target, o.fallback |
|
else |
|
logger.info("Didn't find attachment fallback. Sending JSON dump") |
|
@bot.say target, JSON.stringify(o) |
|
|
|
topic: (envelope, strings...) -> |
|
data = strings.join " / " |
|
channel = envelope.room |
|
@bot.send 'TOPIC', channel, data |
|
|
|
emote: (envelope, strings...) -> |
|
# Use @notice if SEND_NOTICE_MODE is set |
|
return @notice envelope, strings if process.env.HUBOT_IRC_SEND_NOTICE_MODE? |
|
|
|
target = @_getTargetFromEnvelope envelope |
|
|
|
unless target |
|
return logger.error "ERROR: Not sure who to send to. envelope=", envelope |
|
|
|
for str in strings |
|
@bot.action target, str |
|
|
|
notice: (envelope, strings...) -> |
|
target = @_getTargetFromEnvelope envelope |
|
|
|
unless target |
|
return logger.warn "Notice: no target found", envelope |
|
|
|
# Flatten out strings from send |
|
flattened = [] |
|
for str in strings |
|
if Array.isArray str |
|
flattened = flattened.concat str |
|
else |
|
flattened.push str |
|
|
|
for str in flattened |
|
if not str? |
|
continue |
|
|
|
@bot.notice target, str |
|
|
|
reply: (envelope, strings...) -> |
|
self = @ |
|
@send envelope, strings |
|
|
|
join: (channel) -> |
|
self = @ |
|
@bot.join channel, () -> |
|
logger.info('joined %s', channel) |
|
|
|
selfUser = self.getUserFromName self.robot.name |
|
self.receive new EnterMessage(selfUser) |
|
|
|
part: (channel) -> |
|
self = @ |
|
@bot.part channel, () -> |
|
logger.info('left %s', channel) |
|
|
|
selfUser = self.getUserFromName self.robot.name |
|
self.receive new LeaveMessage(selfUser) |
|
|
|
getUserFromName: (name) -> |
|
return @robot.brain.userForName(name) if @robot.brain?.userForName? |
|
|
|
# Deprecated in 3.0.0 |
|
return @userForName name |
|
|
|
getUserFromId: (id) -> |
|
# TODO: Add logic to convert object if name matches |
|
return @robot.brain.userForId(id) if @robot.brain?.userForId? |
|
|
|
# Deprecated in 3.0.0 |
|
return @userForId id |
|
|
|
createUser: (channel, from) -> |
|
user = @getUserFromId from |
|
user.name = from |
|
|
|
if channel.match(/^[&#]/) |
|
user.room = channel |
|
else |
|
user.room = null |
|
user |
|
|
|
kick: (channel, client, message) -> |
|
@bot.emit 'raw', |
|
command: 'KICK' |
|
nick: process.env.HUBOT_IRC_NICK |
|
args: [ channel, client, message ] |
|
|
|
command: (command, strings...) -> |
|
@bot.send command, strings... |
|
|
|
checkCanStart: -> |
|
if not process.env.HUBOT_IRC_NICK and not @robot.name |
|
throw new Error("HUBOT_IRC_NICK is not defined; try: export HUBOT_IRC_NICK='mybot'") |
|
else if not process.env.HUBOT_IRC_ROOMS |
|
throw new Error("HUBOT_IRC_ROOMS is not defined; try: export HUBOT_IRC_ROOMS='#myroom'") |
|
else if not process.env.HUBOT_IRC_SERVER |
|
throw new Error("HUBOT_IRC_SERVER is not defined: try: export HUBOT_IRC_SERVER='irc.myserver.com'") |
|
|
|
unfloodProtection: (unflood) -> |
|
unflood == 'true' or !isNaN(parseInt(unflood)) |
|
|
|
unfloodProtectionDelay: (unflood) -> |
|
unfloodProtection = @unfloodProtection(unflood) |
|
unfloodValue = parseInt(unflood) or 1000 |
|
|
|
if unfloodProtection |
|
unfloodValue |
|
else |
|
0 |
|
|
|
run: -> |
|
self = @ |
|
|
|
do @checkCanStart |
|
|
|
options = |
|
nick: process.env.HUBOT_IRC_NICK or @robot.name |
|
realName: process.env.HUBOT_IRC_REALNAME |
|
port: process.env.HUBOT_IRC_PORT |
|
rooms: process.env.HUBOT_IRC_ROOMS.split(",") |
|
ignoreUsers: process.env.HUBOT_IRC_IGNORE_USERS?.split(",") or [] |
|
server: process.env.HUBOT_IRC_SERVER |
|
password: process.env.HUBOT_IRC_PASSWORD |
|
nickpass: process.env.HUBOT_IRC_NICKSERV_PASSWORD |
|
nickusername: process.env.HUBOT_IRC_NICKSERV_USERNAME |
|
connectCommand: process.env.HUBOT_IRC_CONNECT_COMMAND |
|
fakessl: process.env.HUBOT_IRC_SERVER_FAKE_SSL? |
|
certExpired: process.env.HUBOT_IRC_SERVER_CERT_EXPIRED? |
|
unflood: process.env.HUBOT_IRC_UNFLOOD |
|
debug: process.env.HUBOT_IRC_DEBUG? |
|
usessl: process.env.HUBOT_IRC_USESSL? |
|
userName: process.env.HUBOT_IRC_USERNAME |
|
|
|
client_options = |
|
userName: options.userName |
|
realName: options.realName |
|
password: options.password |
|
debug: options.debug |
|
port: options.port |
|
stripColors: true |
|
secure: options.usessl |
|
selfSigned: options.fakessl |
|
certExpired: options.certExpired |
|
floodProtection: @unfloodProtection(options.unflood), |
|
floodProtectionDelay: @unfloodProtectionDelay(options.unflood), |
|
|
|
client_options['channels'] = options.rooms unless options.nickpass |
|
|
|
@robot.name = options.nick |
|
bot = new Irc.Client options.server, options.nick, client_options |
|
|
|
next_id = 1 |
|
user_id = {} |
|
|
|
if options.nickpass? |
|
identify_args = "" |
|
|
|
if options.nickusername? |
|
identify_args += "#{options.nickusername} " |
|
|
|
identify_args += "#{options.nickpass}" |
|
|
|
bot.addListener 'notice', (from, to, text) -> |
|
if from is 'NickServ' and text.toLowerCase().indexOf('identify') isnt -1 |
|
bot.say 'NickServ', "identify #{identify_args}" |
|
else if options.nickpass and from is 'NickServ' and |
|
(text.indexOf('Password accepted') isnt -1 or |
|
text.indexOf('identified') isnt -1) |
|
for room in options.rooms |
|
@join room |
|
|
|
if options.connectCommand? |
|
bot.addListener 'registered', (message) -> |
|
# The 'registered' event is fired when you are connected to the server |
|
strings = options.connectCommand.split " " |
|
self.command strings.shift(), strings... |
|
|
|
bot.addListener 'names', (channel, nicks) -> |
|
for nick of nicks |
|
self.createUser channel, nick |
|
|
|
bot.addListener 'notice', (from, to, message) -> |
|
if from in options.ignoreUsers |
|
logger.info('Ignoring user: %s', from) |
|
# we'll ignore this message if it's from someone we want to ignore |
|
return |
|
|
|
logger.info "NOTICE from #{from} to #{to}: #{message}" |
|
|
|
user = self.createUser to, from |
|
self.receive new TextMessage(user, message) |
|
|
|
bot.addListener 'message', (from, to, message) -> |
|
if options.nick.toLowerCase() == to.toLowerCase() |
|
# this is a private message, let the 'pm' listener handle it |
|
return |
|
|
|
if from in options.ignoreUsers |
|
logger.info('Ignoring user: %s', from) |
|
# we'll ignore this message if it's from someone we want to ignore |
|
return |
|
|
|
#logger.debug "From #{from} to #{to}: #{message}" |
|
|
|
user = self.createUser to, from |
|
#if user.room |
|
# logger.info "#{to} <#{from}> #{message}" |
|
#else |
|
# unless message.indexOf(to) == 0 |
|
# message = "#{to}: #{message}" |
|
# logger.debug "msg <#{from}> #{message}" |
|
|
|
self.receive new TextMessage(user, message) |
|
|
|
bot.addListener 'action', (from, to, message) -> |
|
#logger.debug " * From #{from} to #{to}: #{message}" |
|
|
|
if from in options.ignoreUsers |
|
logger.info('Ignoring user: %s', from) |
|
# we'll ignore this message if it's from someone we want to ignore |
|
return |
|
|
|
user = self.createUser to, from |
|
#if user.room |
|
# logger.debug "#{to} * #{from} #{message}" |
|
#else |
|
# logger.debug "msg <#{from}> #{message}" |
|
|
|
self.receive new TextMessage(user, message) |
|
|
|
bot.addListener 'error', (message) -> |
|
logger.error('ERROR: %s: %s', message.command, message.args.join(' ')) |
|
|
|
bot.addListener 'pm', (nick, message) -> |
|
logger.info('Got private message from %s: %s', nick, message) |
|
|
|
if process.env.HUBOT_IRC_PRIVATE |
|
return |
|
|
|
if nick in options.ignoreUsers |
|
logger.info('Ignoring user: %s', nick) |
|
# we'll ignore this message if it's from someone we want to ignore |
|
return |
|
|
|
nameLength = options.nick.length |
|
if message.slice(0, nameLength).toLowerCase() != options.nick.toLowerCase() |
|
message = "#{options.nick} #{message}" |
|
|
|
self.receive new TextMessage({reply_to: nick, name: nick}, message) |
|
|
|
bot.addListener 'join', (channel, who) -> |
|
logger.info('%s has joined %s', who, channel) |
|
user = self.createUser channel, who |
|
user.room = channel |
|
self.receive new EnterMessage(user) |
|
|
|
bot.addListener 'part', (channel, who, reason) -> |
|
logger.info('%s has left %s: %s', who, channel, reason) |
|
user = self.createUser '', who |
|
user.room = channel |
|
self.receive new LeaveMessage(user) |
|
|
|
bot.addListener 'kick', (channel, who, _by, reason) -> |
|
logger.info('%s was kicked from %s by %s: %s', who, channel, _by, reason) |
|
|
|
bot.addListener 'invite', (channel, from) -> |
|
logger.info('%s invited you to join %s', from, channel) |
|
|
|
if from in options.ignoreUsers |
|
logger.info('Ignoring user: %s', from) |
|
# we'll ignore this message if it's from someone we want to ignore |
|
return |
|
|
|
if not process.env.HUBOT_IRC_PRIVATE or process.env.HUBOT_IRC_IGNOREINVITE |
|
bot.join channel |
|
|
|
@bot = bot |
|
|
|
self.emit "connected" |
|
|
|
_getTargetFromEnvelope: (envelope) -> |
|
user = null |
|
room = null |
|
target = null |
|
|
|
# as of hubot 2.4.2, the first param to send() is an object with 'user' |
|
# and 'room' data inside. detect the old style here. |
|
if envelope.reply_to? |
|
user = envelope |
|
else |
|
# expand envelope |
|
user = envelope.user |
|
room = envelope.room |
|
|
|
if user |
|
# most common case - we're replying to a user in a room |
|
if user.room |
|
target = user.room |
|
# reply directly |
|
else if user.name |
|
target = user.name |
|
# replying to pm |
|
else if user.reply_to |
|
target = user.reply_to |
|
# allows user to be an id string |
|
else if user.search?(/@/) != -1 |
|
target = user |
|
else if room |
|
# this will happen if someone uses robot.messageRoom(jid, ...) |
|
target = room |
|
|
|
target |
|
|
|
exports.use = (robot) -> |
|
new IrcBot robot |