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
  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. 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] === ':>') {
  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. };