|
|
/** * api: * IRC([required options]) * required options - {name, pass, channel} * * IRC.connect() * connects to the twitch irc server * * IRC.on('command', callback) * allows custom commands * * IRC.on('data', callback) * event when data is recieved / sent * * IRC.msg(message, [options]) * options - {caller, auth[0/1]} * * IRC.caller(data[0]) * parse out user name from socket data, * mainly for plugin use when working with commands * * example: * require: * var irc = require('./lib/core/irc.js')({ * name : 'TwitchBot', * pass : 'twitch!twitch!twitch!', * channel : '#awesomebroadcaster' * }); * * connect: * irc.connect(); * * custom commands: * irc.on('command' function (data) { * if (data[3] == ':!command') { * // do something
* } * }); * * irc data: * irc.on('data', function (data) { * //do something with data
* }); * * irc logging: * irc.on('data', function (data) { * irc.realtime(data); * } * * send a message to chat: * irc.msg('Hi chat!'); * irc.msg('Hi chat!', {caller: 'SupremoRTD', auth: 1}); * * get user name: * irc.caller(data[0]); */
var net = require('net'), events = require('events'), file = require('fs'), https = require('https'), utils = require('./utils.js');
//-------- Construct ---------
function IRC(options) { var __self = this;
__self.options = options || {pass: 'irc_bot', name: 'irc_bot', channel: 'irc_bot'};
__self.config = { // twitch bot info
pass : __self.options.pass, name : __self.options.name, nick : 'irc_bot', broadcaster : __self.options.channel.slice(1).charAt(0).toUpperCase() + __self.options.channel.slice(2).toLowerCase(), // twitch server
addr : 'irc.twitch.tv', //__self.options.name.toLowerCase() + '.jtvirc.com',
port : 6667, channel : __self.options.channel.toLowerCase(), encoding : 'ascii' };
__self.mods = []; __self.buffer = [];
// message queue
__self.queue_timer = 2000; __self.queue_messages = []; __self.previous_message = '';
// irc logging
__self.check_streaming = 4;//minutes
__self.streaming = false; __self.new_file = false; __self.log = '';
// chat logging
__self.first_check = true; __self.store_chat = __self.options.chatlog || false; __self.chat_log = ''; }
IRC.prototype = new events.EventEmitter();
//-------- Methods --------
IRC.prototype.start = function () { var __self = this;
// check stream status
function stream_status() { var time = utils.make_interval(__self.check_streaming); if (time === 0 || __self.first_check) { // Important to log chat right away, so lets skip the first 4 minute wait
if (__self.first_check) __self.first_check = false; https.get('https://api.twitch.tv/kraken/streams/' + __self.config.channel.slice(1), function (response) { var body = '';
// put together response
response.on('data', function (chunk) { body += chunk; });
// log file creation
response.on('end', function () { var json = null; try { json = JSON.parse(body); __self.streaming = json.stream !== null; } catch (err) { __self.streaming = false; } if (__self.streaming && !__self.new_file) { // prevent another file from being created while streaming
__self.new_file = true;
// set stream time for file
var date = new Date(), hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours(), min = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes(), sec = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds(), streaming_time = '';
// create start time string
streaming_time += (date.getMonth().toString() + 1) + date.getDate().toString() + date.getFullYear().toString(); streaming_time += hours.toString() + min.toString() + sec.toString();
// create new log file
__self.log = './../logs/' + __self.config.channel.slice(1) + '_' + streaming_time.toString() + '.txt'; file.open(__self.log, 'w');
// create chat log
if (__self.store_chat) { __self.chat_log = './../logs/chat/' + __self.config.channel.slice(1) + '_' + streaming_time.toString() + '.txt'; file.open(__self.chat_log, 'w'); } } else if (!__self.streaming) { __self.new_file = false; __self.log = ''; } setTimeout(stream_status, 1000); }); }); } else { setTimeout(stream_status, time); } }
stream_status(); __self.connect(); debugger; __self.monitor_queue(); };
IRC.prototype.realtime = function (data){ var __self = this;
// only log irc data if streaming
if (__self.streaming) { // check if file exists, if it does append
file.exists(__self.log, function (exists) { if (exists) { file.appendFile(__self.log, data + '\r\n', 'utf-8', function (err) { if (err) { throw err; } }); } }); } };
IRC.prototype.connect = function () { var __self = this;
// create new socket
__self.socket = new net.Socket(); __self.socket.setEncoding(__self.config.encoding); __self.socket.setNoDelay(); __self.socket.connect(__self.config.port, __self.config.addr);
// connect to twitch irc via socket
__self.socket.on('connect', function () { __self.emit('data', 'RECV - Established connection to Twitch IRC, registering user...'); __self.raw('PASS ' + __self.config.pass, true); __self.raw('NICK ' + __self.config.name); __self.raw('USER ' + __self.config.nick + ' ' + __self.config.nick + '.com ' + __self.config.nick + ' :' + __self.config.name); });
// handle incoming socket data
__self.socket.on('data', function (data) { var prepack, lines, line, word; prepack = data.replace('\r\n', '\n'); __self.buffer += prepack; lines = __self.buffer.split('\n'); __self.buffer = '';
if (lines[lines.length - 1] !== '') { __self.buffer = lines[lines.length - 1]; }
lines = lines.splice(0, lines.length - 1); for (var i = 0; i < lines.length; i++) { line = lines[i].replace('\r',''); word = line.replace('\r', '').split(' '); __self.emit('data', 'RECV - ' + line); __self.join(word); __self.pingpong(word); __self.moderators(word); if (word[3] !== undefined) { __self.emit('command', word); } } });
__self.socket.on('error', function(){ __self.reconnect(); }); };
// reconnect to socket
IRC.prototype.reconnect = function () { var __self = this;
// send quit to server, destroy socket connection,
// clear socket variable and then reconnect
__self.socket.end('QUIT\r\n'); __self.socket.destroy(); __self.socket = null; __self.connect(); };
// join channel
IRC.prototype.join = function (data) { var __self = this;
if (data[3] === ':>' || data[3] === ':End') { __self.raw('JOIN ' + __self.config.channel); } };
// ping / pong
IRC.prototype.pingpong = function (data) { var __self = this;
if (data[0] === 'PING') { __self.raw('PONG ' + data[1]); } };
// store / remove mods
IRC.prototype.moderators = function (data) { var __self = this;
if (data[1] === 'MODE') { if (data[4] !== undefined) { var user = data[4].charAt(0).toUpperCase() + data[4].slice(1); switch (data[3]) { case '+o': if (__self.mods.indexOf(user) < 0) { __self.mods.push(user); } break; case '-o': if (__self.mods.indexOf(user) >= 0) { __self.mods.splice(__self.mods.indexOf(user), 1); } break; } } } };
// output to socket / console
IRC.prototype.raw = function (data, hide) { var __self = this;
__self.socket.write(data + '\r\n', __self.config.encoding, function (){ if (!hide) { // monitor commands sent by the bot
// and push them to command action
var parse = data.split(' '); if (parse[0] === 'PRIVMSG') { parse = __self.options.name + ' ' + data; __self.emit('command', parse.split(' ')); __self.emit('data', 'SENT - ' + __self.options.name + ' ' + data); } else { // output response
__self.emit('data', 'SENT - ' + data); } } }); };
// who sent message
IRC.prototype.caller = function (data) { var caller = data.split('!');
return caller[0].charAt(1).toUpperCase() + caller[0].slice(2); };
// send message to twitch chat
IRC.prototype.msg = function (msg, options) { var __self = this, opts = options || {caller:null, auth:0};
switch (opts.auth) { case 0: __self.raw('PRIVMSG ' + __self.config.channel + ' :' + msg); break; case 1: if (__self.mods.indexOf(opts.caller) >= 0) { __self.raw('PRIVMSG ' + __self.config.channel + ' :' + msg); } break; } };
// message queue
IRC.prototype.queue = function(msg) { var __self = this; __self.queue_messages.push(msg); };
IRC.prototype.monitor_queue = function() { var __self = this, prepend_text = ['>', '+'];
// handle messages in queue
function handle_queue() { if (__self.queue_messages.length > 0) { var message = __self.queue_messages[0].message, options = __self.queue_messages[0].options, timer = __self.queue_messages[0].timer || __self.queue_timer;
// change message if it's the same as the previous message
if (message === __self.previous_message) { for (var i = 0; i < prepend_text.length; i++) { if (prepend_text[i] !== message.charAt(0)) { message = prepend_text[i] + message.slice(1); __self.previous_message = message; break; } } } else { __self.previous_message = __self.queue_messages[0].message; }
// remove message from queue
__self.queue_messages.splice(0, 1);
// output message to chat
setTimeout(function() { if (options === null) { __self.msg(message); } else { __self.msg(message, options); } // recheck the queue
setTimeout(handle_queue, 500); }, timer); } else { setTimeout(handle_queue, 500); } } handle_queue(); };
module.exports = function (options) { return new IRC(options); };
|