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.

375 lines
11 KiB

12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 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 : 'irc.twitch.tv', //__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. debugger;
  142. __self.monitor_queue();
  143. };
  144. IRC.prototype.realtime = function (data){
  145. var __self = this;
  146. // only log irc data if streaming
  147. if (__self.streaming) {
  148. // check if file exists, if it does append
  149. file.exists(__self.log, function (exists) {
  150. if (exists) {
  151. file.appendFile(__self.log, data + '\r\n', 'utf-8', function (err) {
  152. if (err) {
  153. throw err;
  154. }
  155. });
  156. }
  157. });
  158. }
  159. };
  160. IRC.prototype.connect = function () {
  161. var __self = this;
  162. // create new socket
  163. __self.socket = new net.Socket();
  164. __self.socket.setEncoding(__self.config.encoding);
  165. __self.socket.setNoDelay();
  166. __self.socket.connect(__self.config.port, __self.config.addr);
  167. // connect to twitch irc via socket
  168. __self.socket.on('connect', function () {
  169. __self.emit('data', 'RECV - Established connection to Twitch IRC, registering user...');
  170. __self.raw('PASS ' + __self.config.pass, true);
  171. __self.raw('NICK ' + __self.config.name);
  172. __self.raw('USER ' + __self.config.nick + ' ' + __self.config.nick + '.com ' + __self.config.nick + ' :' + __self.config.name);
  173. });
  174. // handle incoming socket data
  175. __self.socket.on('data', function (data) {
  176. var prepack, lines, line, word;
  177. prepack = data.replace('\r\n', '\n');
  178. __self.buffer += prepack;
  179. lines = __self.buffer.split('\n');
  180. __self.buffer = '';
  181. if (lines[lines.length - 1] !== '') {
  182. __self.buffer = lines[lines.length - 1];
  183. }
  184. lines = lines.splice(0, lines.length - 1);
  185. for (var i = 0; i < lines.length; i++) {
  186. line = lines[i].replace('\r','');
  187. word = line.replace('\r', '').split(' ');
  188. __self.emit('data', 'RECV - ' + line);
  189. __self.join(word);
  190. __self.pingpong(word);
  191. __self.moderators(word);
  192. if (word[3] !== undefined) {
  193. __self.emit('command', word);
  194. }
  195. }
  196. });
  197. __self.socket.on('error', function(){
  198. __self.reconnect();
  199. });
  200. };
  201. // reconnect to socket
  202. IRC.prototype.reconnect = function () {
  203. var __self = this;
  204. // send quit to server, destroy socket connection,
  205. // clear socket variable and then reconnect
  206. __self.socket.end('QUIT\r\n');
  207. __self.socket.destroy();
  208. __self.socket = null;
  209. __self.connect();
  210. };
  211. // join channel
  212. IRC.prototype.join = function (data) {
  213. var __self = this;
  214. if (data[3] === ':>' || data[3] === ':End') {
  215. __self.raw('JOIN ' + __self.config.channel);
  216. }
  217. };
  218. // ping / pong
  219. IRC.prototype.pingpong = function (data) {
  220. var __self = this;
  221. if (data[0] === 'PING') {
  222. __self.raw('PONG ' + data[1]);
  223. }
  224. };
  225. // store / remove mods
  226. IRC.prototype.moderators = function (data) {
  227. var __self = this;
  228. if (data[1] === 'MODE') {
  229. if (data[4] !== undefined) {
  230. var user = data[4].charAt(0).toUpperCase() + data[4].slice(1);
  231. switch (data[3]) {
  232. case '+o':
  233. if (__self.mods.indexOf(user) < 0) {
  234. __self.mods.push(user);
  235. }
  236. break;
  237. case '-o':
  238. if (__self.mods.indexOf(user) >= 0) {
  239. __self.mods.splice(__self.mods.indexOf(user), 1);
  240. }
  241. break;
  242. }
  243. }
  244. }
  245. };
  246. // output to socket / console
  247. IRC.prototype.raw = function (data, hide) {
  248. var __self = this;
  249. __self.socket.write(data + '\r\n', __self.config.encoding, function (){
  250. if (!hide) {
  251. // monitor commands sent by the bot
  252. // and push them to command action
  253. var parse = data.split(' ');
  254. if (parse[0] === 'PRIVMSG') {
  255. parse = __self.options.name + ' ' + data;
  256. __self.emit('command', parse.split(' '));
  257. __self.emit('data', 'SENT - ' + __self.options.name + ' ' + data);
  258. } else {
  259. // output response
  260. __self.emit('data', 'SENT - ' + data);
  261. }
  262. }
  263. });
  264. };
  265. // who sent message
  266. IRC.prototype.caller = function (data) {
  267. var caller = data.split('!');
  268. return caller[0].charAt(1).toUpperCase() + caller[0].slice(2);
  269. };
  270. // send message to twitch chat
  271. IRC.prototype.msg = function (msg, options) {
  272. var __self = this, opts = options || {caller:null, auth:0};
  273. switch (opts.auth) {
  274. case 0:
  275. __self.raw('PRIVMSG ' + __self.config.channel + ' :' + msg);
  276. break;
  277. case 1:
  278. if (__self.mods.indexOf(opts.caller) >= 0) {
  279. __self.raw('PRIVMSG ' + __self.config.channel + ' :' + msg);
  280. }
  281. break;
  282. }
  283. };
  284. // message queue
  285. IRC.prototype.queue = function(msg) {
  286. var __self = this;
  287. __self.queue_messages.push(msg);
  288. };
  289. IRC.prototype.monitor_queue = function() {
  290. var __self = this, prepend_text = ['>', '+'];
  291. // handle messages in queue
  292. function handle_queue() {
  293. if (__self.queue_messages.length > 0) {
  294. var message = __self.queue_messages[0].message,
  295. options = __self.queue_messages[0].options,
  296. timer = __self.queue_messages[0].timer || __self.queue_timer;
  297. // change message if it's the same as the previous message
  298. if (message === __self.previous_message) {
  299. for (var i = 0; i < prepend_text.length; i++) {
  300. if (prepend_text[i] !== message.charAt(0)) {
  301. message = prepend_text[i] + message.slice(1);
  302. __self.previous_message = message;
  303. break;
  304. }
  305. }
  306. } else {
  307. __self.previous_message = __self.queue_messages[0].message;
  308. }
  309. // remove message from queue
  310. __self.queue_messages.splice(0, 1);
  311. // output message to chat
  312. setTimeout(function() {
  313. if (options === null) {
  314. __self.msg(message);
  315. } else {
  316. __self.msg(message, options);
  317. }
  318. // recheck the queue
  319. setTimeout(handle_queue, 500);
  320. }, timer);
  321. } else {
  322. setTimeout(handle_queue, 500);
  323. }
  324. }
  325. handle_queue();
  326. };
  327. module.exports = function (options) {
  328. return new IRC(options);
  329. };