UNPKG

24.2 kBJavaScriptView Raw
1// https://github.com/marado/TalkerNode
2
3"use strict";
4var net = require('net');
5var crypto = require('crypto');
6var valid = require('password-strength');
7var chalk = require('chalk');
8
9var sockets = [];
10var port = process.env.PORT || 8888;
11var version = require('./package.json').version;
12
13// Instantiates the users database
14const low = require('lowdb');
15const FileSync = require('lowdb/adapters/FileSync')
16var loadeddb = 0; // number of databases loaded so far // TODO: this probably is no longer needed with lowdb
17const usersadapter = new FileSync('user.db');
18const usersdb = low(usersadapter);
19loadeddb++;
20
21// Instantiates the talker settings database
22var ranks;
23var commands = {};
24const talkeradapter = new FileSync('talker.db');
25const talkerdb = low(talkeradapter);
26ranks = talkerdb.get('ranks').value();
27
28if (typeof ranks === 'undefined') {
29 ranks = {list:[
30 "Jailed",
31 "Newcomer",
32 "Newbie",
33 "Juvie",
34 "Learner",
35 "Adult",
36 "Wiseman",
37 "Hero",
38 "Mage",
39 "Imortal",
40 "God"
41 ], entrylevel: 10};
42 talkerdb.set("ranks", ranks).write();
43}
44loadeddb++;
45
46// Instantiates the universe
47var nodiverse = require('nodiverse');
48var universe;
49var talkername = "Moosville"; // TODO: make this configurable
50const universeadapter = new FileSync('universe.db');
51const universedb = low(universeadapter);
52universe = universedb.get("universe").value();
53if (typeof universe === 'undefined') {
54 universe = nodiverse(); // new universe
55 universe.create([0,0,0],0);
56 var limbo = universe.get([0,0,0]);
57 limbo.name = "Limbo"; // at the beginning there was just the limbo
58 universe.update(limbo);
59 universe.entrypoint=[0,0,0]; // where everyone was meant to be
60 universedb.set("universe", universe).write();
61} else {
62 // assign the correct prototype to universe
63 // this is somewhat ugly, since we're falling back from
64 // Object.?etPrototypeOf to __proto__ in order not to depend on
65 // nodejs 0.12
66 // TODO: now that we already depend on a nodejs >= 0.12, this ugly hack
67 // can be cleaned up
68 var setProtoOf = function(obj, proto) { obj.__proto__ = proto; };
69 var mixinProperties = function(obj, proto) {
70 for (var prop in proto) { obj[prop] = proto[prop]; }
71 };
72 var setPrototypeOf = Object.setPrototypeOf ||
73 {__proto__:[]} instanceof Array ? setProtoOf : mixinProperties;
74 var getPrototypeOf = Object.getPrototypeOf ||
75 function(obj) { return obj.__proto__; };
76 setPrototypeOf(universe, getPrototypeOf(nodiverse()));
77}
78if (typeof universe.name !== 'undefined') talkername = universe.name;
79loadeddb++;
80
81/*
82 * Cleans the input of carriage return, newline and control characters
83 */
84function cleanInput(data) {
85 var newString = data.toString().replace("[\u0000-\u001a]", "").replace("[\u001c-\u001f]", "").replace(/(\r\n|\n|\r)/gm,"").replace(/\u001b\[./gm,"");
86 while (newString.charAt(0) === " ") newString=newString.substring(1);
87 return newString;
88}
89
90/*
91 * Turns client's echo on or off. echo off is usually wanted on passwords
92 */
93function echo(bool) {
94 var bytes = Array(3);
95 bytes[0] = 0xFF;
96 bytes[1] = bool ? 0xFC : 0xFB; // 0xFF 0xFC 0x01 for off, 0xFF 0xFB 0x01 for on
97 bytes[2] = 0x01;
98 return new Buffer(bytes);
99}
100
101/*
102 * Method executed when data is received from a socket
103 */
104function receiveData(socket, data) {
105
106 var cleanData = cleanInput(data);
107
108 if(cleanData.length == 0)
109 return;
110
111 // Useful when in debug mode, you don't want this otherwise... it wouldn't be nice for your users' privacy, would it?
112 // console.log("Moo [" + cleanData + "]");
113
114 // TODO: We're just filtering out IAC commands. We should be dealing with them instead...
115 // See: https://github.com/marado/TalkerNode/issues/34
116 // IAC commands
117 var IAC = [
118 1 , // Telnet IAC - Echo
119 3 , // Telnet IAC - Suppress Go Ahead
120 5 , // Telnet IAC - Status
121 6 , // Telnet IAC - Timing Mark
122 24, // Telnet IAC - Terminal Type
123 31, // Telnet IAC - Window Size
124 33, // Telnet IAC - Remote Flow Control
125 34, // Telnet IAC - Linemode
126 36, // Telnet IAC - Environment Variables
127 65533 // reply to echo off
128 ];
129 if (IAC.indexOf(cleanData.charCodeAt(0)) !== -1) {
130 // This is IAC, not an user input
131 // console.log("Moo: IAC: [" + cleanData.charCodeAt(0) +"]");
132 return;
133 }
134
135 socket.write(echo(true));
136 if(socket.username == undefined) {
137 if (cleanData.toLowerCase() === "quit") return socket.end('Goodbye!\r\n');
138 if (cleanData.toLowerCase() === "who") { socket.db={rank:0}; doCommand(socket, ".who"); return socket.write(chalk.cyan("Give me a name: ")); }
139 if (cleanData.toLowerCase() === "version") { socket.db={rank:0}; doCommand(socket, ".version"); return socket.write(chalk.cyan("Give me a name: ")); }
140 var reservedNames=["who","quit","version"];
141 if (reservedNames.indexOf(cleanData.toLowerCase()) > -1) {
142 socket.write("\r\nThat username is reserved, you cannot have it.\r\n" + chalk.cyan("Give me a name: "));
143 }
144 else if ((cleanData.match(/^[a-zA-Z]+$/) !== null) && (1 < cleanData.length) && (cleanData.length < 17)) {
145 socket.username = cleanData.toLowerCase().charAt(0).toUpperCase() + cleanData.toLowerCase().slice(1); // Capitalized name
146 socket.loggedin = false;
147 socket.db = usersdb.get(socket.username).value();
148 if (typeof socket.db === 'undefined') {
149 socket.write(chalk.cyan("\r\nNew user, welcome! Please choose a password: "));
150 socket.write(echo(false));
151 socket.registering=true;
152 } else {
153 socket.write(chalk.cyan("\r\nGive me your password: "));
154 socket.write(echo(false));
155 socket.registering=false;
156 }
157 return;
158 } else {
159 socket.write(chalk.red(
160 "\r\nInvalid username: it can only contain letters, have at least two characters and be no longer than 16 characters.\r\n") +
161 chalk.cyan("Give me a name: "
162 ));
163 }
164 return;
165 } else if (socket.loggedin == false) {
166 // this is the password
167 if (socket.registering) {
168 if (typeof socket.password === 'undefined') {
169 if ((cleanData.toLowerCase() === socket.username.toLowerCase()) || !valid(cleanData).valid) {
170 socket.write(chalk.red("\r\nThat password is not valid"));
171 if (valid(cleanData).hint !== null) socket.write(" (" + valid(cleanData).hint + ")");
172 socket.write(chalk.red(". ") + "Let's try again...\r\n" + chalk.cyan("Give me a name: "));
173 delete socket.registering;
174 delete socket.username;
175 delete socket.loggedin;
176 delete socket.db;
177 return;
178 }
179 socket.password = crypto.createHash('sha512').update(cleanData).digest('hex');
180 socket.write(echo(false));
181 socket.write(chalk.cyan("\r\nConfirm the chosen password: "));
182 return;
183 } else {
184 if (socket.password === crypto.createHash('sha512').update(cleanData).digest('hex')) {
185 // password confirmed
186 socket.db={
187 "password":crypto.createHash('sha512').update(cleanData).digest('hex'),
188 "rank":ranks.entrylevel,
189 "where":universe.entrypoint,
190 "registerTime":Date.now(),
191 };
192 if (ranks.list.length - 1 == ranks.entrylevel) {
193 if (ranks.list.length == 1) {
194 ranks.entrylevel = 0;
195 } else {
196 ranks.entrylevel = 1;
197 }
198 talkerdb.set("ranks", ranks).write();
199 }
200 usersdb.set(socket.username, socket.db).write();
201 delete socket.password;
202 delete socket.registering;
203 } else {
204 // wrong confirmation password
205 delete socket.password;
206 delete socket.registering;
207 delete socket.username;
208 delete socket.db;
209 socket.write(chalk.red("\r\nPasswords don't match!") + "Let's start from the beggining... " + chalk.cyan("Tell me your name: "));
210 return;
211 }
212 }
213 } else if (socket.db.password !== crypto.createHash('sha512').update(cleanData).digest('hex')) {
214 delete socket.username;
215 delete socket.db;
216 socket.write(chalk.red("\r\nWrong password! ") + "Let's start from the beggining... " + chalk.cyan("Tell me your name: "));
217 return;
218 }
219
220 // entering the talker...
221 if (universe.get(socket.db.where) === null) { // there's no where, or that place doesn't exist anymore
222 socket.db.where = universe.entrypoint;
223 // save changes into the database
224 var tmp = usersdb.get(socket.username).value();
225 tmp.where = socket.db.where;
226 usersdb.set(socket.username, tmp).write();
227 }
228 if (command_utility().allButMe(socket,function(me,to){if(to.username.toLowerCase()===me.username.toLowerCase()){return true;}})) {
229 var old = command_utility().allButMe(socket,function(me,to){if(to.username.toLowerCase()===me.username.toLowerCase()){to.end('Session is being taken over...\n');}});
230 socket.write('Taking over session...\n');
231 } else {
232 socket.lastLogin = socket.db.loginTime;
233 socket.db.loginTime = Date.now();
234 if (typeof(socket.db.loginCount) === "undefined") {
235 socket.db.loginCount = 1;
236 } else {
237 socket.db.loginCount++;
238 }
239 socket.activityTime = Date.now();
240 command_utility().allButMe(socket,function(me,to){to.write("[Entering is: "+ me.username + " (" + universe.get(me.db.where).name + " " + me.db.where + ") ]\r\n");});
241 }
242 socket.write("\r\n+----------------------------------------------------------------------------+\r\n");
243 socket.write(" Welcome to " + chalk.bold(talkername) + ", " + chalk.green(socket.username) + "!\r\n");
244 if (typeof(socket.lastLogin) !== "undefined") {
245 socket.write(" Your last login was at " + chalk.magenta(new Date(socket.lastLogin).toString()) + ".\r\n");
246 }
247 socket.write(" Your rank is " + chalk.bold(ranks.list[socket.db.rank]) + ".\r\n");
248 socket.write("+----------------------------------------------------------------------------+\r\n");
249 socket.loggedin = true;
250 doCommand(socket, ".look");
251 return;
252 } else if (typeof socket.interactive !== 'undefined') {
253 switch (socket.interactive.type) {
254 case "password":
255 if (socket.interactive.state === "old") {
256 // let's confirm the password
257 if (socket.db.password !== crypto.createHash('sha512').update(cleanData).digest('hex')) {
258 socket.write("\r\n:: " + chalk.red("Wrong password!\r\n"));
259 delete socket.interactive;
260 } else {
261 // password is correct
262 socket.write("\r\n:: " + chalk.green("Tell me the new password: "));
263 socket.write(echo(false));
264 socket.interactive.state = "new";
265 }
266 } else {
267 // let's set cleanData as the new password
268 if ((cleanData.toLowerCase() === socket.username.toLowerCase()) || !valid(cleanData).valid) {
269 socket.write(chalk.red("\r\nThat password is not valid"));
270 if (valid(cleanData).hint !== null) socket.write(" (" + valid(cleanData).hint + ")");
271 socket.write(chalk.red(". Password not changed.\r\n"));
272 delete socket.interactive;
273 return;
274 }
275 socket.db.password = crypto.createHash('sha512').update(cleanData).digest('hex');
276 usersdb.set(socket.username, socket.db).write();
277 socket.write("\r\n:: " + chalk.cyan("Password changed, now don't you forget your new password!\r\n"));
278 delete socket.interactive;
279 }
280 break;
281 case "suicide":
282 if (socket.interactive.state === "confirmation") {
283 if (cleanData === "yes, I am sure") {
284 // they really want to .suicide, let's validate they are who they're supposed to be...
285 socket.write(chalk.bold("\r\n:: Alright then... just confirm you're who're you supposed to be, tell us your password: "));
286 socket.write(echo(false));
287 socket.interactive.state = "pass";
288 } else {
289 socket.write(chalk.bold("\r\n:: Ooof, we're glad you don't want to leave us!\r\n"));
290 delete socket.interactive;
291 }
292 } else {
293 // let's confirm the password
294 if (socket.db.password !== crypto.createHash('sha512').update(cleanData).digest('hex')) {
295 socket.write("\r\n:: " + chalk.red("Wrong password!\r\n"));
296 delete socket.interactive;
297 } else {
298 // password is correct
299 socket.write(chalk.gray("\r\n:: Deleting all your data... We're sad to see you go!\r\n"));
300 command_utility().allButMe(socket,function(me,to){to.write("[Leaving is: "+ me.username + " ]\r\n");});
301 delete socket.interactive;
302 var quitter = socket.username;
303 socket.end(":'(\r\n");
304 // actually delete the user
305 var users = usersdb.getState();
306 delete users[quitter];
307 usersdb.setState(users).write();
308 }
309 }
310 break;
311 default:
312 socket.write("\r\n:: Something really weird just happened... let's try to recover from it...\r\n");
313 delete socket.interactive;
314 break;
315 }
316 return;
317 }
318
319 // if we have a command...
320 if (cleanData === ".") {
321 if ((typeof socket.lastcommand) !== 'undefined')
322 doCommand(socket, socket.lastcommand);
323 } else if (cleanData.charAt(0) === ".") {
324 doCommand(socket, cleanData);
325 } else {
326 doCommand(socket, ".say " + cleanData);
327 }
328
329}
330
331/*
332 * Load all commands from the command subdirectory
333 */
334function loadCommands() {
335 // Loop through all files, trying to load them
336 var normalizedPath = require("path").join(__dirname, "commands");
337 require("fs").readdirSync(normalizedPath).forEach(function(file) {
338 if (file.substr(file.length-3, 3) === ".js") {
339 delete require.cache[require.resolve('./commands/' + file)]
340 var cmd_load = require('./commands/' + file);
341 var cmd = cmd_load.command;
342
343 // Only load the command if it's set to Autoload
344 if(cmd.autoload) {
345 console.log("Loading Command: Command '" + cmd.name + "' loaded (from '" + file + "')");
346 cmd.loaded_date = new Date();
347 commands[cmd.name] = cmd;
348 } else {
349 console.log("Loading Command: Skipping " + cmd.name + " (from '" + file + "'). Autoload = false");
350 }
351 } else {
352 console.log("Skipping " + file + ": file extension is not 'js'");
353 }
354 });
355}
356
357/*
358 * Method that returns the command rank.
359 * If a custom one exists, use it. Else, use the hardcoded one.
360 * In any case, validate if there's a rank as higher as the one of the command.
361 * If not, assign it to the highest rank available.
362 * If the command doesn't exist at all, return null.
363 */
364function getCmdRank(command) {
365 var r;
366 var cmdRanks = talkerdb.get("commands").value();
367 if (typeof (cmdRanks) !== 'undefined' && typeof (cmdRanks[command]) !== 'undefined') {
368 r = cmdRanks[command];
369 } else if (commands[command]) {
370 r = commands[command].min_rank;
371 } else {
372 return null;
373 }
374 if (r >= ranks.list.length) r = ranks.list.length - 1;
375 return r;
376}
377
378/*
379 * Method that changes the command rank.
380 */
381function setCmdRank(command, rank) {
382 var cmdRanks = talkerdb.get("commands").value();
383 if (typeof(cmdRanks) === 'undefined') cmdRanks = {};
384 if (commands[command]) {
385 var old = commands[command].min_rank;
386 if (typeof (cmdRanks[command]) !== 'undefined') {
387 old = cmdRanks[command];
388 }
389 if (rank != old) {
390 cmdRanks[command] = rank;
391 talkerdb.set("commands", cmdRanks).write();
392 }
393 }
394}
395
396/*
397 * Method to find a command
398 */
399function findCommand(socket, command) {
400 var c = command;
401 var userRank = socket.db.rank;
402 if(commands[c] && userRank >= getCmdRank(c)) {
403 return [commands[c]];
404 } else {
405 // when we have more than one possible command, we
406 // choose the most heavier from the ones with lower
407 // getCmdRank
408 var results = [];
409 var weigth = 0;
410 var rank = ranks.list.length - 1;
411 for (var cmd in commands) {
412 if(cmd.substr(0, c.length) == c && userRank >= getCmdRank(cmd)) {
413 var cweigth = 0;
414 if (typeof commands[cmd].weigth !== 'undefined')
415 cweigth = commands[cmd].weigth;
416 if (getCmdRank(cmd) < rank) {
417 rank = getCmdRank(cmd);
418 weigth = cweigth;
419 results = [commands[cmd]];
420 } else if (getCmdRank(cmd) === rank) {
421 if (cweigth > weigth) {
422 weigth = commands[cmd].weigth;
423 results = [commands[cmd]];
424 } else if (cweigth === weigth) {
425 results.push(commands[cmd]);
426 }
427 }
428 }
429 }
430 return results;
431 }
432}
433
434/*
435 * Method to execute commands.
436 */
437function doCommand(socket, command) {
438 socket.activityTime = Date.now();
439 try {
440 var c = command.split(' ')[0].toLowerCase().substring(1);
441 var userRank = socket.db.rank;
442 var cArr = findCommand(socket, c);
443 if (cArr.length === 1) {
444 socket.lastcommand = command;
445 return cArr[0].execute(socket, command.split(' ').slice(1).join(" "), command_utility())
446 }
447 if (cArr.length > 1) {
448 var possibilities = "";
449 for (var p = 0; p < cArr.length - 1; p++) {
450 possibilities += cArr[p].name + ", ";
451 }
452 possibilities += cArr[cArr.length - 1].name;
453 return socket.write("Found " + cArr.length + " possible commands (" + possibilities + "). Please be more specific.\r\n");
454 } else {
455 return socket.write("There's no such thing as a " + c + " command.\r\n");
456 }
457 }
458 catch(err) {
459 socket.write("Error executing command '" + c + "': " + err + "\r\n");
460 console.error("Error executing command '" + c + "': " + err + "\r\n");
461 }
462}
463
464/*
465 * Method executed when a socket ends
466 */
467function closeSocket(socket) {
468 var i = sockets.indexOf(socket);
469 if (i != -1) {
470 // write total time on socket db
471 sockets[i].db = usersdb.get(sockets[i].username).value();
472 if (typeof sockets[i].db !== 'undefined') {
473 if (typeof sockets[i].db.totalTime === 'undefined') {
474 sockets[i].db.totalTime = (Date.now() - sockets[i].db.loginTime);
475 } else {
476 sockets[i].db.totalTime += (Date.now() - sockets[i].db.loginTime);
477 }
478 usersdb.set(sockets[i].username, sockets[i].db).write();
479 }
480 sockets.splice(i, 1);
481 }
482}
483
484/*
485 * Callback method executed when a new TCP socket is opened.
486 */
487function newSocket(socket) {
488 socket.setKeepAlive(true);
489 sockets.push(socket);
490 socket.write(chalk.green('Welcome to the ') + chalk.bold.white(talkername) + chalk.green("!") + chalk.cyan('\r\n\r\nGive me a name: '));
491 socket.on('data', function(data) {
492 receiveData(socket, data);
493 })
494 socket.on('end', function() {
495 closeSocket(socket);
496 })
497}
498
499
500/*
501 * COMMAND UTILITY - Should probably be moved to own module
502 * Object passed to commands. Gives them access to specific server properties, methods
503 */
504
505//
506function command_utility() {
507 var ret = {
508 version: version,
509 talkername: talkername,
510 sockets: sockets,
511 commands: commands,
512 ranks: ranks,
513 echo: echo,
514 getCmdRank: getCmdRank,
515 setCmdRank: setCmdRank,
516 findCommand: findCommand,
517
518 /*
519 * Execute function to all connected users *but* the triggering one.
520 * It stops at the first connected user to which the function returns true, returning true.
521 */
522 allButMe: function allButMe(socket,fn) {
523 for(var i = 0; i<sockets.length; i++) {
524 if (sockets[i] !== socket) {
525 if ((typeof sockets[i].loggedin != 'undefined') && sockets[i].loggedin){
526 if(fn(socket,sockets[i])) return true;
527 }
528 }
529 }
530 },
531
532 // same as allButMe, but only for those in the same room as me
533 allHereButMe: function allHereButMe(socket,fn) {
534 for(var i = 0 ; i < sockets.length; i++) {
535 if (sockets[i] !== socket) {
536 if ((typeof sockets[i].loggedin != 'undefined') && sockets[i].loggedin &&
537 (sockets[i].db.where[0] == socket.db.where[0]) &&
538 (sockets[i].db.where[1] == socket.db.where[1]) &&
539 (sockets[i].db.where[2] == socket.db.where[2])
540 ){
541 if(fn(socket,sockets[i])) return true;
542 }
543 }
544 }
545 },
546
547 // returns socket for the user, or false if he doesn't exist
548 getOnlineUser: function getOnlineUser(name) {
549 for (var i = 0; i < sockets.length; i++) {
550 if (name.toLowerCase() === sockets[i].username.toLowerCase() && sockets[i].loggedin) return sockets[i];
551 }
552 return false;
553 },
554
555 // returns array of sockets of the 'approximate' online users
556 // While 'getOnlineUser' is the correct function to use if you want
557 // to know if 'username' is online or not, sometimes users want to
558 // refer to another user in an 'human' way, abbreviating.
559 // Eg.: .wizlist tries to find if each wiz is online or not. Since
560 // the username is fully and correctly known, 'getOnlineUser' should be
561 // used. On the other hand, .tell gets an username as an argument. On
562 // that case, an user can type '.tell mr hello', meaning '.tell MrMe
563 // hello'. On that case, 'getAproxOnlineUser' should be used.
564 getAproxOnlineUser: function getOnlineUser(name) {
565 if (this.getOnlineUser(name) !== false) return [this.getOnlineUser(name)];
566 var possibilities = [];
567 for (var i = 0; i < sockets.length; i++) {
568 if (name.toLowerCase() === sockets[i].username.toLowerCase().substr(0,name.length) && sockets[i].loggedin && (name.length < sockets[i].username.length))
569 possibilities.push(sockets[i]);
570 }
571 return possibilities;
572 },
573
574 // returns the user object, in all its db glory
575 // TODO: Let's give just a subset of data from the user, OK? I mean, we
576 // don't want any command to have access to other users' passwords, do
577 // we?
578 getUser: function getUser(name) {
579 name = name.toLowerCase().charAt(0).toUpperCase() + name.toLowerCase().slice(1);
580 return usersdb.get(name).value();
581 },
582
583 // returns the username of an "aproximate" user
584 // read 'getAproxOnlineUser' to understand the difference between
585 // 'getOnlineUser' and it, same happens here between 'getUser' and
586 // 'getAproxUser'.
587 getAproxUser: function getAproxUser(name) {
588 if (this.getUser(name) !== undefined) return [name];
589 var possibilities = [];
590 for (var key in usersdb.getState()) {
591 if (name.toLowerCase() === key.toLowerCase().substr(0,name.length) && (name.length < key.length)) {
592 possibilities.push(key);
593 }
594 }
595 if (possibilities.length === 0) return [];
596 return possibilities;
597 },
598
599 // updates a user in the database
600 // TODO: argh, we surely don't want this! harden it!
601 updateUser: function updateUser(username, userObj) {
602 username = username.toLowerCase().charAt(0).toUpperCase() + username.toLowerCase().slice(1);
603 usersdb.set(username,userObj).write();
604 },
605
606 // get users list, only insensitive information
607 getUsersList: function getUsersList() {
608 var list = [];
609 for (var key in usersdb.getState()) {
610 // retrieving username, rank and loginTime. If needed, we can always add stuff later
611 var val = usersdb.get(key).value();
612 list.push({username:key, rank:val.rank, loginTime:val.loginTime});
613 }
614 return list;
615 },
616
617 // gives a full view of the universe; TODO: we surely don't want this
618 // TODO: in the meantime, we don't need to define a function for this!
619 getUniverse: function getUniverse() {return universe; },
620 // update universe's database
621 saveUniverse: function setUniverse() {
622 return universedb.set("universe", universe).write();
623 },
624
625 // updates the ranks object, both in memory and on the database
626 updateRanks: function updateRanks(updated) {
627 ranks = updated;
628 return talkerdb.set("ranks", ranks).write();
629 },
630
631 // reloads Talker Name
632 reloadTalkerName: function reloadTalkerName() {
633 talkername = universe.name;
634 },
635
636 };
637 return ret;
638};
639
640/*
641 * PROMPT UTILITY - adds a command prompt to the server.
642 * Implemented commands:
643 * rc: Reload commands. Useful for development/debug. Allows
644 * you to reload the commands on the fly!
645 */
646function setPrompt() {
647 var readline = require('readline');
648 var rl = readline.createInterface({
649 input: process.stdin,
650 output: process.stdout,
651 prompt: 'OHAI> '
652 });
653
654 rl.prompt();
655
656 rl.on('line', function(line) {
657 switch(line.trim()) {
658 case 'rc':
659 loadCommands();
660 break;
661 default:
662 console.log("Available commands: ");
663 console.log(" rc: Reload commands. Useful for development/debug. Allows");
664 console.log(" you to reload the commands on the fly!");
665 break;
666 }
667 rl.prompt();
668 }).on('close', function() {
669 console.log('Bye!');
670 process.exit(0);
671 });
672}
673
674
675/*
676 * AND FINALLY... THE ACTUAL main()!
677 */
678
679function main() {
680 if (loadeddb !== 3) {
681 console.log("Waiting for databases to load: " + loadeddb + "/3");
682 setTimeout(main, 100);
683 } else {
684 // Create a new server and provide a callback for when a connection occurs
685 var server = net.createServer(newSocket);
686
687 // Listen on defined port
688 server.listen(port);
689 console.log(talkername + " initialized on port "+ port);
690 loadCommands();
691 setPrompt();
692 }
693}
694
695main();