A Twitch.tv viewer reward and games system.
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.

374 lines
11 KiB

11 years ago
11 years ago
11 years ago
  1. /**
  2. * api:
  3. * IRC([required options])
  4. * required options - {name, pass, channel}
  5. *
  6. * IRC.connect()
  7. * connects to the twitch irc server
  8. *
  9. * IRC.on('command', callback)
  10. * allows custom commands
  11. *
  12. * IRC.on('data', callback)
  13. * event when data is recieved / sent
  14. *
  15. * IRC.msg(message, [options])
  16. * options - {caller, auth[0/1]}
  17. *
  18. * IRC.caller(data[0])
  19. * parse out user name from socket data,
  20. * mainly for plugin use when working with commands
  21. *
  22. * example:
  23. * require:
  24. * var irc = require('./lib/core/irc.js')({
  25. * name : 'TwitchBot',
  26. * pass : 'twitch!twitch!twitch!',
  27. * channel : '#awesomebroadcaster'
  28. * });
  29. *
  30. * connect:
  31. * irc.connect();
  32. *
  33. * custom commands:
  34. * irc.on('command' function (data) {
  35. * if (data[3] == ':!command') {
  36. * // do something
  37. * }
  38. * });
  39. *
  40. * irc data:
  41. * irc.on('data', function (data) {
  42. * //do something with data
  43. * });
  44. *
  45. * irc logging:
  46. * irc.on('data', function (data) {
  47. * irc.realtime(data);
  48. * }
  49. *
  50. * send a message to chat:
  51. * irc.msg('Hi chat!');
  52. * irc.msg('Hi chat!', {caller: 'SupremoRTD', auth: 1});
  53. *
  54. * get user name:
  55. * irc.caller(data[0]);
  56. */
  57. var net = require('net'),
  58. events = require('events'),
  59. file = require('fs'),
  60. https = require('https'),
  61. utils = require('./utils.js');
  62. //-------- Construct ---------
  63. function IRC(options) {
  64. var __self = this;
  65. __self.options = options || {pass: 'irc_bot', name: 'irc_bot', channel: 'irc_bot'};
  66. __self.config = {
  67. // twitch bot info
  68. pass : __self.options.pass,
  69. name : __self.options.name,
  70. nick : 'irc_bot',
  71. broadcaster : __self.options.channel.slice(1).charAt(0).toUpperCase() + __self.options.channel.slice(2).toLowerCase(),
  72. // twitch server
  73. addr : '199.9.250.229', //__self.options.name.toLowerCase() + '.jtvirc.com',
  74. port : 6667,
  75. channel : __self.options.channel.toLowerCase(),
  76. encoding : 'ascii'
  77. };
  78. __self.mods = [];
  79. __self.buffer = [];
  80. // message queue
  81. __self.queue_timer = 2000;
  82. __self.queue_messages = [];
  83. __self.previous_message = '';
  84. // irc logging
  85. __self.check_streaming = 4;//minutes
  86. __self.streaming = false;
  87. __self.new_file = false;
  88. __self.log = '';
  89. }
  90. IRC.prototype = new events.EventEmitter();
  91. //-------- Methods --------
  92. IRC.prototype.start = function () {
  93. var __self = this;
  94. // check stream status
  95. function stream_status() {
  96. var time = utils.make_interval(__self.check_streaming);
  97. if (time === 0) {
  98. https.get('https://api.twitch.tv/kraken/streams/' + __self.config.channel.slice(1), function (response) {
  99. var body = '';
  100. // put together response
  101. response.on('data', function (chunk) {
  102. body += chunk;
  103. });
  104. // log file creation
  105. response.on('end', function () {
  106. var json = null;
  107. try {
  108. json = JSON.parse(body);
  109. __self.streaming = json.stream !== null;
  110. } catch (err) {
  111. __self.streaming = false;
  112. }
  113. if (__self.streaming && !__self.new_file) {
  114. // prevent another file from being created while streaming
  115. __self.new_file = true;
  116. // set stream time for file
  117. var date = new Date(),
  118. hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours(),
  119. min = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes(),
  120. sec = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds(),
  121. streaming_time = '';
  122. // create start time string
  123. streaming_time += (date.getMonth().toString() + 1) + date.getDate().toString() + date.getFullYear().toString();
  124. streaming_time += hours.toString() + min.toString() + sec.toString();
  125. // create new log file
  126. __self.log = './../logs/' + __self.config.channel.slice(1) + '_' + streaming_time.toString() + '.txt';
  127. file.open(__self.log, 'w');
  128. } else if (!__self.streaming) {
  129. __self.new_file = false;
  130. __self.log = '';
  131. }
  132. setTimeout(stream_status, 1000);
  133. });
  134. });
  135. } else {
  136. setTimeout(stream_status, time);
  137. }
  138. }
  139. stream_status();
  140. __self.connect();
  141. __self.monitor_queue();
  142. };
  143. IRC.prototype.realtime = function (data){
  144. var __self = this;
  145. // only log irc data if streaming
  146. if (__self.streaming) {
  147. // check if file exists, if it does append
  148. file.exists(__self.log, function (exists) {
  149. if (exists) {
  150. file.appendFile(__self.log, data + '\r\n', 'utf-8', function (err) {
  151. if (err) {
  152. throw err;
  153. }
  154. });
  155. }
  156. });
  157. }
  158. };
  159. IRC.prototype.connect = function () {
  160. var __self = this;
  161. // create new socket
  162. __self.socket = new net.Socket();
  163. __self.socket.setEncoding(__self.config.encoding);
  164. __self.socket.setNoDelay();
  165. __self.socket.connect(__self.config.port, __self.config.addr);
  166. // connect to twitch irc via socket
  167. __self.socket.on('connect', function () {
  168. __self.emit('data', 'RECV - Established connection to Twitch IRC, registering user...');
  169. __self.raw('PASS ' + __self.config.pass, true);
  170. __self.raw('NICK ' + __self.config.name);
  171. __self.raw('USER ' + __self.config.nick + ' ' + __self.config.nick + '.com ' + __self.config.nick + ' :' + __self.config.name);
  172. });
  173. // handle incoming socket data
  174. __self.socket.on('data', function (data) {
  175. var prepack, lines, line, word;
  176. prepack = data.replace('\r\n', '\n');
  177. __self.buffer += prepack;
  178. lines = __self.buffer.split('\n');
  179. __self.buffer = '';
  180. if (lines[lines.length - 1] !== '') {
  181. __self.buffer = lines[lines.length - 1];
  182. }
  183. lines = lines.splice(0, lines.length - 1);
  184. for (var i = 0; i < lines.length; i++) {
  185. line = lines[i].replace('\r','');
  186. word = line.replace('\r', '').split(' ');
  187. __self.emit('data', 'RECV - ' + line);
  188. __self.join(word);
  189. __self.pingpong(word);
  190. __self.moderators(word);
  191. if (word[3] !== undefined) {
  192. __self.emit('command', word);
  193. }
  194. }
  195. });
  196. __self.socket.on('error', function(){
  197. __self.reconnect();
  198. });
  199. };
  200. // reconnect to socket
  201. IRC.prototype.reconnect = function () {
  202. var __self = this;
  203. // send quit to server, destroy socket connection,
  204. // clear socket variable and then reconnect
  205. __self.socket.end('QUIT\r\n');
  206. __self.socket.destroy();
  207. __self.socket = null;
  208. __self.connect();
  209. };
  210. // join channel
  211. IRC.prototype.join = function (data) {
  212. var __self = this;
  213. if (data[3] === ':End') {
  214. __self.raw('JOIN ' + __self.config.channel);
  215. }
  216. };
  217. // ping / pong
  218. IRC.prototype.pingpong = function (data) {
  219. var __self = this;
  220. if (data[0] === 'PING') {
  221. __self.raw('PONG ' + data[1]);
  222. }
  223. };
  224. // store / remove mods
  225. IRC.prototype.moderators = function (data) {
  226. var __self = this;
  227. if (data[1] === 'MODE') {
  228. if (data[4] !== undefined) {
  229. var user = data[4].charAt(0).toUpperCase() + data[4].slice(1);
  230. switch (data[3]) {
  231. case '+o':
  232. if (__self.mods.indexOf(user) < 0) {
  233. __self.mods.push(user);
  234. }
  235. break;
  236. case '-o':
  237. if (__self.mods.indexOf(user) >= 0) {
  238. __self.mods.splice(__self.mods.indexOf(user), 1);
  239. }
  240. break;
  241. }
  242. }
  243. }
  244. };
  245. // output to socket / console
  246. IRC.prototype.raw = function (data, hide) {
  247. var __self = this;
  248. __self.socket.write(data + '\r\n', __self.config.encoding, function (){
  249. if (!hide) {
  250. // monitor commands sent by the bot
  251. // and push them to command action
  252. var parse = data.split(' ');
  253. if (parse[0] === 'PRIVMSG') {
  254. parse = __self.options.name + ' ' + data;
  255. __self.emit('command', parse.split(' '));
  256. __self.emit('data', 'SENT - ' + __self.options.name + ' ' + data);
  257. } else {
  258. // output response
  259. __self.emit('data', 'SENT - ' + data);
  260. }
  261. }
  262. });
  263. };
  264. // who sent message
  265. IRC.prototype.caller = function (data) {
  266. var caller = data.split('!');
  267. return caller[0].charAt(1).toUpperCase() + caller[0].slice(2);
  268. };
  269. // send message to twitch chat
  270. IRC.prototype.msg = function (msg, options) {
  271. var __self = this, opts = options || {caller:null, auth:0};
  272. switch (opts.auth) {
  273. case 0:
  274. __self.raw('PRIVMSG ' + __self.config.channel + ' :' + msg);
  275. break;
  276. case 1:
  277. if (__self.mods.indexOf(opts.caller) >= 0) {
  278. __self.raw('PRIVMSG ' + __self.config.channel + ' :' + msg);
  279. }
  280. break;
  281. }
  282. };
  283. // message queue
  284. IRC.prototype.queue = function(msg) {
  285. var __self = this;
  286. __self.queue_messages.push(msg);
  287. };
  288. IRC.prototype.monitor_queue = function() {
  289. var __self = this, prepend_text = ['>', '+'];
  290. // handle messages in queue
  291. function handle_queue() {
  292. if (__self.queue_messages.length > 0) {
  293. var message = __self.queue_messages[0].message,
  294. options = __self.queue_messages[0].options,
  295. timer = __self.queue_messages[0].timer || __self.queue_timer;
  296. // change message if it's the same as the previous message
  297. if (message === __self.previous_message) {
  298. for (var i = 0; i < prepend_text.length; i++) {
  299. if (prepend_text[i] !== message.charAt(0)) {
  300. message = prepend_text[i] + message.slice(1);
  301. __self.previous_message = message;
  302. break;
  303. }
  304. }
  305. } else {
  306. __self.previous_message = __self.queue_messages[0].message;
  307. }
  308. // remove message from queue
  309. __self.queue_messages.splice(0, 1);
  310. // output message to chat
  311. setTimeout(function() {
  312. if (options === null) {
  313. __self.msg(message);
  314. } else {
  315. __self.msg(message, options);
  316. }
  317. // recheck the queue
  318. setTimeout(handle_queue, 500);
  319. }, timer);
  320. } else {
  321. setTimeout(handle_queue, 500);
  322. }
  323. }
  324. handle_queue();
  325. };
  326. module.exports = function (options) {
  327. return new IRC(options);
  328. };