You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
375 lines
11 KiB
375 lines
11 KiB
/**
|
|
* 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 = '';
|
|
}
|
|
|
|
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) {
|
|
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');
|
|
} 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);
|
|
};
|