Created
February 12, 2012 11:42
-
-
Save KamilaBorowska/1808128 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# IRC client implementation for YIBot 2.0. Technically, it's main part of | |
# program, but YIBot now supports not only IRC :). | |
{Server} = require '../server' | |
# Class IRC itself | |
class exports.IRC extends Server | |
constructor: (@name, @config) -> | |
unless @config.Host? | |
throw new Error "Server #{@name} doesn't have specified server!" | |
# Usual IRC port | |
@config.Port ?= 6667 | |
# Constructors are SUPER. Well, actually it activates original | |
# constructor which makes various initialization. | |
super | |
# Sends raw command to the server. Don't use it unless you can confirm | |
# using @config.Type that you're talking with "IRC". | |
raw: (msg) => | |
@log msg, 'write' | |
@client.write "#{msg}\r\n" | |
connect: => | |
net = require('net') | |
@client = net.connect(@config.Port, @config.Host) | |
# This code is likely to fail. If this happens, catch exception and stop | |
# trying to parse the message (but avoid useless crashes which may | |
# happen sadly). | |
@client.on 'data', (data) => | |
try | |
@onData data | |
catch exception | |
@log exception.message, 'error' | |
@client.on 'end', (data) => | |
# Make reconnection | |
@connect() | |
@client.setEncoding('utf8') | |
@raw "NICK #{@config.Nick}" | |
@raw "USER #{@config.User} place holder :#{@config.Realname}" | |
@nick = @config.Nick | |
join: (channel) => | |
# Channels are case insensitive. This is attempt to fix this. | |
channel = channel.toLowerCase() | |
if @channels[channel]? | |
throw new Error 'Bot has tried to join channel which already exists.' | |
@channels[channel] = [@nick] | |
@raw "JOIN #{channel}" | |
true | |
part: (channel) => | |
# Channels are case insensitive. This is attempt to fix this. | |
channel = channel.toLowerCase() | |
if not @channels[channel]? | |
throw new Error "#{channel} already is left." | |
@raw "PART #{channel}" | |
delete @channels[channel] | |
true | |
nick: (nick) -> | |
[@oldnick, @nick] = [@nick, nick] | |
@raw "NICK #{nick}" | |
true | |
onData: (data) => | |
# Sometimes servers split messages using \r. Node expects \n instead. | |
# This call should fix it. | |
data = data.replace(/^\s+|\s+$/g, '').split("\r") | |
if (data.length > 1) | |
for line in data | |
@onData line | |
return | |
data = data[0] | |
# If data is empty, it's probably a bug. Ignore it. | |
return if data is '' | |
@log data | |
# Some servers seem to insert ":" at beginning for some reason. This | |
# function will fix those cases. | |
data = data.replace(/^:/, '') | |
data = data.split(' ') | |
for value, i in data | |
break if /^:/.test(value) | |
# Complex call which sets data to the array of values before ":" has | |
# showed in query and joined with spaces string after ":" character. | |
data = [data[0...i]..., data[i..].join(' ').replace(/^:/, '')] | |
if (data[0] is 'PING') | |
@raw "PONG :#{data[1]}" | |
else | |
@message = {} | |
user = /(.*)!((.*)@(.*))/.exec(data[0]) | |
if user? | |
# Set variables for regular expressions parts | |
[ | |
@message.fullhost | |
@message.nick | |
@message.host | |
@message.user | |
@message.address | |
] = user | |
# Check if user is owner | |
@isOwner = @config.Owner.test(@message.host) | |
@message.type = data[1].toLowerCase() | |
switch @message.type | |
# When '001' is received you're free to join any channel | |
when '001' | |
for channel in @config.Channels | |
@join channel | |
# Nickname in use or unknown nick | |
when '432', '433' | |
@nick = @oldnick | |
# List of users in this channel | |
when '353' | |
channel = data[4].toLowerCase() | |
nicks = data[5].split(' ') | |
for nick in nicks | |
nick = nick.replace(/^[^A-}]+/, '') | |
# Ignore my bot name | |
continue if nick is @nick | |
# Remove modes at beginning. Those characters aren't allowed in | |
# IRC nicknames anyway, so there is no danger in removing those. | |
@channels[channel].push(nick) | |
when 'part' | |
@message.channel = data[2].toLowerCase() | |
@channels[@message.channel].remove(@message.nick) | |
when 'join' | |
break if @message.nick is @nick | |
@message.channel = data[2].toLowerCase() | |
@channels[@message.channel].push(@message.nick) | |
when 'quit' | |
for channel of @channels | |
@channels[channel].remove(@message.nick) | |
when 'privmsg' | |
@message.channel = data[2].toLowerCase() | |
@message.text = data[3] | |
if @message.channel[0] is '#' | |
@message.type = 'message' | |
else | |
@message.type = 'private' | |
@parseMessage() | |
when 'invite' | |
@message.channel = data[3].toLowerCase() | |
if @config.ReactOnInvite | |
@join @message.channel | |
if typeof @config.ReactOnInvite is 'string' | |
message = @config.ReactOnInvite.replace('%s', @message.nick) | |
@send message, @message.channel | |
@loadPlugins() | |
# In case of setTimeout attack (not really) | |
@message = {} | |
respond: (msg) => | |
if @message.channel? | |
@send msg, @message.channel | |
else | |
throw new Error 'You cannot respond to this message.' | |
# This is rather tricky version of send, needed because of IRC specifics. | |
send: (message, channel) => | |
if message instanceof Array | |
for msg in message | |
@send msg, channel | |
else | |
# Make nice message split. | |
message = message.split(/\r?\n|\r/) | |
for msg, i in message | |
line = msg.match(/.{1,400}(\s|$)|.{400}|.+$/g) | |
for text in line | |
continue if text is '' | |
@raw "PRIVMSG #{channel} :#{text}" | |
# This true will return "true" which in plugin will stop execution. | |
true | |
# In case of IRC, it's alias, but it's not always the case. | |
pm: (message, channel) => @send message, channel |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment