Advertisement
Guest User

Untitled

a guest
May 4th, 2017
105
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.59 KB | None | 0 0
  1. (function () {
  2. 'use strict';
  3.  
  4. var irc = require('irc'),
  5. fs = require('fs'),
  6. _ = require('underscore'),
  7. async = require('async'),
  8. path = require('path'),
  9. moduleMan = require("./node-module-manager"),
  10. changeCase = require('change-case'),
  11. SnailEscape = require('snailescape.js');
  12.  
  13. var heapdump = null;
  14.  
  15. var reEscape = function(s) {
  16. return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
  17. };
  18.  
  19. var bot = {};
  20.  
  21. bot.util = {}; //Util functions
  22.  
  23. bot.init = function(cb) {
  24. if(bot.config.heapdump) {
  25. console.log("Enabling heap dumps");
  26. heapdump = require('heapdump');
  27. process.on('SIGINT', function() {
  28. console.log("Please stand by...");
  29. bot.dump();
  30. process.exit();
  31. });
  32. }
  33. bot.configfolder = bot.config.configfolder;
  34. bot.tmpfolder = bot.config.tmpfolder;
  35. bot.datafolder = bot.config.datafolder;
  36. _.each(['configfolder', 'tmpfolder', 'datafolder'],function(i) {
  37. if(!fs.existsSync("./"+bot[i])) {
  38. fs.mkdirSync("./"+bot[i]);
  39. }
  40. });
  41. cb(null);
  42. };
  43.  
  44. bot.initModuleManager = function(cb) {
  45. moduleMan.init(bot, cb);
  46. };
  47.  
  48. var supportedConfigTypes = [
  49. {
  50. exts: [".json"],
  51. test: null, //TODO
  52. parse: function(data, loc, cb) {
  53. try {
  54. cb(null, JSON.parse(data));
  55. } catch(ex) {
  56. cb(ex);
  57. }
  58. },
  59. },
  60. {
  61. exts: ['.js'],
  62. test: null,
  63. parse: function(data, loc, cb) {
  64. try {
  65. cb(null, require(loc));
  66. } catch(ex) {
  67. cb(ex);
  68. }
  69. }
  70. }
  71. ];
  72.  
  73. bot.getConfig = function(name, cb) {
  74. var fullPath = path.join(bot.config.configfolder, name);
  75. var done = false;
  76. fs.readFile(fullPath,{encoding: 'utf8'}, function(err, res) {
  77. if(err) return cb(err);
  78.  
  79. var ext = path.extname(fullPath);
  80. supportedConfigTypes.forEach(function(type) {
  81. if(_.any(type.exts, function(e) { return e === ext; })) {
  82. type.parse(res, fullPath, cb);
  83. done = true;
  84. return false;
  85. }
  86. });
  87. if(!done) return cb(null, res);
  88. });
  89. };
  90.  
  91. bot.getDataFolder = function(namespace) {
  92. return path.join(bot.datafolder, namespace);
  93. };
  94.  
  95.  
  96. bot.callModuleFn = function(fname, args) {
  97. return moduleMan.callModuleFn(fname, args);
  98. };
  99.  
  100.  
  101. bot.callCommandFn = function(command, args) {
  102. return moduleMan.callCommandFn(command, args);
  103. };
  104.  
  105.  
  106. bot.loadConfig = function(cb) { //sync
  107. var conf;
  108. var default_config = JSON.parse(fs.readFileSync("./config.example.json"));
  109. try {
  110. conf = JSON.parse(fs.readFileSync('./config.json'));
  111. } catch(ex) {
  112. console.log("Error reading config file: ", e);
  113. conf = default_config;
  114. }
  115.  
  116. _.each(default_config, function(value, key) {
  117. var envKey = changeCase.constantCase(key);
  118. // Load environment variable approximations of each config key and let them override
  119. if(typeof process.env[envKey] !== 'undefined') {
  120. try {
  121. conf[key] = JSON.parse(process.env[envKey]);
  122. } catch(ex) {
  123. console.log("Could not load key: " + envKey + " because it was not valid json");
  124. }
  125. }
  126. });
  127.  
  128. var def_keys = Object.keys(default_config);
  129. _.each(default_config, function(value, key) {
  130. if(typeof conf[key] === 'undefined') {
  131. console.log("Setting: ", key, " to ", value);
  132. conf[key] = value;
  133. }
  134. });
  135. return conf;
  136. };
  137.  
  138.  
  139. bot.initClient = function(cb) {
  140. var conf = bot.config;
  141. bot.client = new irc.Client(conf.server, conf.nick, {
  142. userName: conf.userName,
  143. password: conf.password,
  144. realName: conf.realName,
  145. port: conf.port,
  146. debug: conf.debug,
  147. showErrors: conf.showErrors,
  148. autoRejoin: true,
  149. autoConnect: false,
  150. channels: [],
  151. secure: conf.ssl,
  152. selfSigned: true,
  153. floodProtection: false,
  154. channelPrefixes: conf.channelPrefixes,
  155. messageSplit: conf.messageSplit
  156. });
  157.  
  158. var quoteSplit = new SnailEscape();
  159.  
  160. bot.client.on('error', function(err) { console.log(err);});
  161.  
  162.  
  163. bot.client.on('join', function(channel, nick, raw) {
  164. bot.callModuleFn('join', [channel, nick, raw]);
  165. });
  166. bot.client.on('part', function(channel, nick, raw) {
  167. bot.callModuleFn('part', [channel, nick, raw]);
  168. });
  169. bot.client.on('quit', function(nick,reason,channels,raw) {
  170. bot.callModuleFn('quit', [nick, reason, channels, raw]);
  171. });
  172.  
  173.  
  174. bot.client.on('notice', function(from, to, text, raw) {
  175. var primaryFrom = (to == bot.client.nick) ? from : to;
  176.  
  177. bot.callModuleFn('notice', [text, from, to, bot.getNoticeReply(primaryFrom), raw]);
  178. if(to == bot.client.nick) {
  179. bot.callModuleFn('pmnotice', [text, from, bot.getNoticeReply(primaryFrom), raw]);
  180. } else {
  181. bot.callModuleFn('channotice', [text, to, from, bot.getNoticeReply(primaryFrom), raw]);
  182. }
  183.  
  184. });
  185.  
  186. bot.client.on('message', function(from, to, text, raw) {
  187. var primaryFrom = (to == bot.client.nick) ? from : to;
  188. bot.callModuleFn('message', [text, from, to, bot.getReply(primaryFrom), raw]);
  189.  
  190. bot.callModuleFn('msg', [text, from, bot.getReply(primaryFrom), raw]);
  191.  
  192. if(to == bot.client.nick) {
  193. bot.callModuleFn('pm', [text, from, bot.getReply(from), raw]);
  194. } else {
  195. bot.callModuleFn('chanmsg', [text, to, from, bot.getReply(to), raw]);
  196. }
  197. if(text.substring(0, bot.config.commandPrefix.length) == bot.config.commandPrefix) {
  198. var re = new RegExp('^' + reEscape(bot.config.commandPrefix) + '(\\S*)\\s*(.*)$', 'g');
  199. var rem = re.exec(text);
  200. var command = rem[1];
  201. var remainder = rem.length == 3 ? rem[2] : "";
  202. var respTo = (bot.client.nick == to) ? from : to;
  203.  
  204. var parts = quoteSplit.parse(remainder).parts || remainder.split(" ");
  205. bot.callModuleFn("any_command", [remainder, parts, bot.getReply(respTo), command, from, to, text, raw]);
  206. bot.callCommandFn(command, [remainder, parts, bot.getReply(respTo), command, from, to, text, raw]);
  207. }
  208. });
  209.  
  210. bot.client.on('ctcp', function(from, to, text, type, raw) {
  211. if(from == bot.config.owner && to == bot.client.nick && text == "RELOAD") {
  212. moduleMan.reloadModules();
  213. } else if(from == bot.config.owner && to == bot.client.nick && text == "LOAD") {
  214. moduleMan.loadModules();
  215. } else {
  216. moduleMan.callModuleFn('ctcp', [text, type, from, to, raw]);
  217. }
  218. });
  219.  
  220. bot.client.on('action', function(from, to, text, type, raw) {
  221. var primaryFrom = (to == bot.client.nick) ? from : to;
  222. moduleMan.callModuleFn('action', [text, from, to, bot.getActionReply(primaryFrom), raw]);
  223. if(to == bot.client.nick) {
  224. moduleMan.callModuleFn('pmaction', [text, from, bot.getActionReply(primaryFrom), raw]);
  225. } else {
  226. moduleMan.callModuleFn('chanaction', [text, to, from, bot.getActionReply(primaryFrom), raw]);
  227. }
  228. });
  229.  
  230. // This is emitted by the client right before it tries to say something.
  231. // Note, this will not work if we send notices or certain other events,
  232. // but that won't happen in practice yet
  233. bot.client.on('selfMessage', function(to, text) {
  234. // Hack! This ensures that even though node-irc calls this as part of the same function path, the events for pmsay/chansay happen a tick later.
  235. // To understand why this matters, see issue
  236. // https://github.com/euank/EuIrcBot/issues/131.
  237. //
  238. // This also makes sense if we envision the call stack for a message if we
  239. // have mod1, which does a reply on 'foo', and mod2, which records all
  240. // 'pmsay' and all 'msg' events.
  241. //
  242. // callModuleFn(msg, foo) // triggers mod1 and mod2
  243. // -> mod1.onmsg(foo)
  244. // -> reply(bar)
  245. // -> bot.client.say(bar)
  246. // -> bot.client.emit(selfMessage)
  247. // -> mod2.onpmsay(bar) // mod2.onpmsay is triggered and records "bar"
  248. // -> ...
  249. // -> mod2.onmsg(foo) // mod2.onmsg records "foo"
  250. //
  251. //
  252. // The problem with the above is that mod2 has a wrong state of the world
  253. // in that it recorded "bar" as being said before receiving the message
  254. // "foo". This is obviously wrong.
  255. // The easy and sorta hacky fix for this is simply ensuring that all
  256. // 'onpmsay' events are triggered after the current eventloop finishes
  257. // processing, since that ensures that if the 'onmsg' handler of bar
  258. // records the message in the same tick it was called, it will retain a
  259. // correct ordering.
  260. process.nextTick(function() {
  261. if(bot.isChannel(to)) {
  262. bot.callModuleFn('chansay', [bot.client.nick, to, text]);
  263. } else {
  264. bot.callModuleFn('pmsay', [bot.client.nick, to, text]);
  265. }
  266. });
  267. });
  268.  
  269. cb();
  270. };
  271.  
  272. bot.sayTo = function(target, args) {
  273. // Todo, make this use stringifyArgs.
  274. var tosay = [];
  275. for(var i=1;i<arguments.length;i++) {
  276. tosay.push(arguments[i]);
  277. }
  278. bot.client.say(target, tosay.join(' '));
  279. };
  280. /* say("one", "two") => "one two" */
  281. bot.say = function(args) {
  282. var tosay = [];
  283. for(var i=0;i<arguments.length;i++) {
  284. tosay.push(arguments[i]);
  285. }
  286. bot.client.say(bot.config.mainChannel, tosay.join(' '));
  287. };
  288.  
  289. bot.joinChannels = function(cb) {
  290. if(!cb) cb = function(err) { if(err) console.log(err); };
  291.  
  292. var channels = Array.isArray(bot.conf.channels) ? bot.conf.channels : bot.conf.channels.split(',');
  293. async.map(channels, function(item, joined) {
  294. bot.client.join(item, function(){joined();});
  295. }, cb);
  296. };
  297.  
  298. bot.stringifyArgs = function(args) {
  299. var strParts = [];
  300. for(var i=0;i<arguments.length;i++) {
  301. if(typeof arguments[i] === 'string') {
  302. strParts.push(arguments[i]);
  303. } else if(Array.isArray(arguments[i])) {
  304. strParts.push(bot.stringifyArgs.apply(this, arguments[i]));
  305. } else if(arguments[i] === undefined || arguments[i] === null) {
  306. strParts.push('');
  307. } else{
  308. strParts.push(arguments[i].toString());
  309. }
  310. }
  311. return strParts.join(' ');
  312. };
  313.  
  314. bot.isChannel = function(name) {
  315. return _.some(_.map(bot.config.channelPrefixes.split(''), function(el) { return name[0] == el; }));
  316. };
  317.  
  318.  
  319. bot.getReply = function(chan) {
  320. return function(args) {
  321. var repStr = bot.stringifyArgs.apply(this, arguments);
  322. bot.client.say(chan, repStr);
  323. };
  324. };
  325.  
  326. bot.getNoticeReply = function(to) {
  327. return function(args) {
  328. var repStr = bot.stringifyArgs.apply(this, arguments);
  329.  
  330. if(bot.isChannel(chan)) {
  331. bot.callModuleFn('channotice', [bot.client.nick, to, repStr]);
  332. } else {
  333. bot.callModuleFn('pmnotice', [bot.client.nick, to, repStr]);
  334. }
  335. bot.client.notice(to, repStr);
  336. };
  337. };
  338. bot.getActionReply = function(to) {
  339. return function(args) {
  340. var repStr = bot.stringifyArgs.apply(this, arguments);
  341.  
  342. if(bot.isChannel(chan)) {
  343. bot.callModuleFn('channotice', [bot.client.nick, to, repStr]);
  344. } else {
  345. bot.callModuleFn('pmnotice', [bot.client.nick, to, repStr]);
  346. }
  347. bot.client.action(to, repStr);
  348. };
  349. };
  350.  
  351. bot.createPathIfNeeded = function(fullPath, cb) {
  352. var dirname = path.dirname(fullPath);
  353. fs.mkdir(dirname, function(err) {
  354. if(err && err.code != 'EEXIST') {
  355. // Directory doesn't already exist and couldn't be made
  356. cb(err);
  357. } else {
  358. // Made or already exists.
  359. cb(null);
  360. }
  361. });
  362. };
  363.  
  364. bot.fsStoreData = function(namespace, filePath, data, flag, cb) {
  365. // Flags is an optional argument
  366. if(typeof flag == 'function') {
  367. cb = flag;
  368. flag = 'w';
  369. }
  370.  
  371. var basePath = bot.getDataFolder(namespace);
  372. var finalPath = path.join(basePath, filePath);
  373.  
  374. bot.createPathIfNeeded(finalPath, function(err, res) {
  375. if(err) return cb(err);
  376. fs.writeFile(finalPath, data, {flag: flag}, cb);
  377. });
  378. };
  379.  
  380. bot.fsGetData = function(namespace, filePath, cb) {
  381. var basePath = bot.getDataFolder(namespace);
  382. var finalPath = path.join(basePath, filePath);
  383.  
  384. fs.readFile(finalPath, cb);
  385. };
  386.  
  387. bot.fsListData = function(namespace, listPath, cb) {
  388. var basePath = bot.getDataFolder(namespace);
  389. var finalPath = path.join(basePath, listPath);
  390.  
  391. fs.readdir(finalPath, cb);
  392. };
  393.  
  394. bot.dump = function() {
  395. if(heapdump) {
  396. heapdump.writeSnapshot(function(err, filename) {
  397. console.log('Heapdump written to', filename);
  398. });
  399. }
  400. }
  401.  
  402. async.series([
  403. function(cb) {
  404. bot.conf = bot.config = bot.loadConfig();
  405. cb(null);
  406. },
  407. bot.initClient,
  408. bot.init,
  409. function(cb){
  410. bot.client.connect(function(){cb(null);});
  411. },
  412. function(cb){
  413. console.log("Connected!");
  414. cb(null);
  415. },
  416. bot.initModuleManager,
  417. moduleMan.loadModules,
  418. bot.joinChannels,
  419. function(cb) {
  420. bot.dump();
  421. },
  422. ], function(err, results) {
  423. if(err) {
  424. bot.dump();
  425. console.trace("Error in init");
  426. console.log(err);
  427. }
  428. });
  429.  
  430. }());
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement