UNPKG

node-teamspeak

Version:

TeamSpeak (R)-3-ServerQuery-Client for node.js

190 lines (171 loc) 5.62 kB
/* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * <timklge@wh2.tu-dresden.de> wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return - Tim Kluge * ---------------------------------------------------------------------------- */ var net = require("net"), LineInputStream = require("line-input-stream"), events = require("events"), util = require("util"); function TeamSpeakClient(host, port){ events.EventEmitter.call(this); var self = this, socket = net.connect(port || 10011, host || 'localhost'), reader = null, status = -2, queue = [], executing = null; function tsescape(s){ var r = String(s); r = r.replace(/\\/g, "\\\\"); // Backslash r = r.replace(/\//g, "\\/"); // Slash r = r.replace(/\|/g, "\\p"); // Pipe r = r.replace(/\n/g, "\\n"); // Newline r = r.replace(/\r/g, "\\r"); // Carriage Return r = r.replace(/\t/g, "\\t"); // Tab r = r.replace(/\v/g, "\\v"); // Vertical Tab r = r.replace(/\f/g, "\\f"); // Formfeed r = r.replace(/ /g, "\\s"); // Whitespace return r; } function tsunescape(s){ var r = String(s); r = r.replace(/\\s/g, " "); // Whitespace r = r.replace(/\\p/g, "|"); // Pipe r = r.replace(/\\n/g, "\n"); // Newline r = r.replace(/\\f/g, "\f"); // Formfeed r = r.replace(/\\r/g, "\r"); // Carriage Return r = r.replace(/\\t/g, "\t"); // Tab r = r.replace(/\\v/g, "\v"); // Vertical Tab r = r.replace(/\\\//g, "\/"); // Slash r = r.replace(/\\\\/g, "\\"); // Backslash return r; } function checkQueue(){ if(!executing && queue.length >= 1){ executing = queue.shift(); socket.write(executing.text + "\n"); } } function parseResponse(s){ var response = []; var records = s.split("|"); response = records.map(function(k){ var args = k.split(" "); var thisrec = {}; args.forEach(function(v){ if(v.indexOf("=") > -1){ var key = tsunescape(v.substr(0, v.indexOf("="))); var value = tsunescape(v.substr(v.indexOf("=")+1)); if(parseInt(value, 10) == value) value = parseInt(value, 10); thisrec[key] = value; }else thisrec[v] = ""; }); return thisrec; }); if(response.length === 0){ response = null; } else if(response.length === 1){ response = response.shift(); } return response; } // Return pending commands that are going to be sent to the server. // Note that they have been parsed - Access getPending()[0].text to get // the full text representation of the command. TeamSpeakClient.prototype.getPending = function(){ return queue.slice(0); }; // Clear the queue of pending commands so that any command that is currently queued won't be executed. // The old queue is returned. TeamSpeakClient.prototype.clearPending = function(){ var q = queue; queue = []; return q; }; // Send a command to the server TeamSpeakClient.prototype.send = function(){ var args = Array.prototype.slice.call(arguments); var options = [], params = {}; var callback = undefined; var cmd = args.shift(); args.forEach(function(v){ if(util.isArray(v)){ options = v; } else if(typeof v === "function"){ callback = v; } else { params = v; } }); var tosend = tsescape(cmd); options.forEach(function(v){ tosend += " -" + tsescape(v); }); for(var k in params){ var v = params[k]; if(util.isArray(v)){ // Multiple values for the same key - concatenate all var doptions = v.map(function(val){ return tsescape(k) + "=" + tsescape(val); }); tosend += " " + doptions.join("|"); } else { tosend += " " + tsescape(k.toString()) + "=" + tsescape(v.toString()); } } queue.push({cmd: cmd, options: options, parameters: params, text: tosend, cb: callback}); if(status === 0) checkQueue(); }; // SetTimeout function TeamSpeakClient.prototype.setTimeout = function(time) { socket.setTimeout(time || 60000, function() { socket.destroy(); self.emit("timeout"); }); }; socket.on("error", function(err){ self.emit("error", err); }); socket.on("close", function(){ self.emit("close", queue); }); socket.on("connect", function(){ reader = LineInputStream(socket); reader.on("line", function(line){ var s = line.trim(); // Ignore two first lines sent by server ("TS3" and information message) if(status < 0){ status++; if(status === 0) checkQueue(); return; } // Server answers with: // [- One line containing the answer ] // - "error id=XX msg=YY". ID is zero if command was executed successfully. var response = undefined; if(s.indexOf("error") === 0){ response = parseResponse(s.substr("error ".length).trim()); executing.error = response; if(executing.error.id === 0) delete executing.error; if(executing.cb) executing.cb.call(executing, executing.error, executing.response, executing.rawResponse); executing = null; checkQueue(); } else if(s.indexOf("notify") === 0){ s = s.substr("notify".length); response = parseResponse(s); self.emit(s.substr(0, s.indexOf(" ")), response); } else if(executing) { response = parseResponse(s); executing.rawResponse = s; executing.response = response; } }); self.emit("connect"); }); } util.inherits(TeamSpeakClient, events.EventEmitter); module.exports = TeamSpeakClient;