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.

367 lines
11 KiB

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