From f4d7d2899156276dfe9a3715daff54ba657d9acd Mon Sep 17 00:00:00 2001 From: Joachim Kainz Date: Mon, 30 Apr 2012 15:52:14 -0700 Subject: [PATCH] Major refactoring & simplification of the API. --- examples/send-msg.js | 13 +-- lib/simple-xmpp.js | 242 +++++++++++++++++++++++++++---------------- 2 files changed, 160 insertions(+), 95 deletions(-) diff --git a/examples/send-msg.js b/examples/send-msg.js index 47ff0bd..b897e0d 100644 --- a/examples/send-msg.js +++ b/examples/send-msg.js @@ -6,27 +6,28 @@ if (process.argv.length < 5) { } var simpler = require('../index'), + Element = require('node-xmpp').Element, jid = process.argv[2], password = process.argv[3], to = process.argv[4], - xmpp = simpler({ + client = simpler({ jid:jid, password:password, host:'talk.google.com' }), connection; -xmpp.on('online', function () { +client.on('online', function () { console.log('Yes, I\'m connected!'); - connection.send(to, "this is a message from the send-msg script"); }); -xmpp.on('chat', function (from, message) { +client.on('chat', function (from, message) { console.log("from %s: ", from, message); }); -xmpp.on('error', function (err) { +client.on('error', function (err) { console.error(err); }); -connection = xmpp.connect(); +client.send(to, "this is a message from the send-msg script"); +//client.discoverServices('gmail.com'); diff --git a/lib/simple-xmpp.js b/lib/simple-xmpp.js index eb93d6e..53d45a3 100644 --- a/lib/simple-xmpp.js +++ b/lib/simple-xmpp.js @@ -28,45 +28,12 @@ var STATUS_ONLINE = "online", STATUS_OFFLINE = "offline", + DISCO_URL = 'http://jabber.org/protocol/disco#info', xmpp = require('node-xmpp'), debug = require('./debug'), EventEmitter = require('events').EventEmitter, util = require('util'); - function SimpleXMPP(connection, events, probeBuddies) { - this.send = function (to, message) { - var stanza = new xmpp.Element('message', { - to:to, - type:'chat' - }); - stanza.c('body').t(message); - connection.send(stanza); - }; - - this.probe = function (buddy, callback) { - probeBuddies[buddy] = true; - - var stanza = new xmpp.Element('presence', { - type:'probe', - to:buddy - }); - events.once('probe_' + buddy, callback); - connection.send(stanza); - }; - - this.on = function (event, listener) { - events.on(event, listener); - }; - - this.removeListener = function (event, listener) { - return events.removeListener(event, listener); - }; - - this.removeAllListeners = function (event) { - return events.removeListener(event); - } - } - function getState(stanza) { if (stanza.attrs.type === 'unavailable') { return STATUS_OFFLINE; @@ -105,85 +72,182 @@ events.emit('chat', id, message); } - function processStanza(events, stanza, probeBuddies) { + function processPresence(events, stanza, probeBuddies) { + var frm = stanza.attrs.from; + + if (!frm) { + return; + } + var iD = frm.split('/')[0], + state = getState(stanza); + + debug('presence', iD, state); + + if (!probeBuddies[iD]) { + return events.emit('buddy', iD, state); + } + + events.emit('probe_' + iD, state); + delete probeBuddies[iD]; + } + + function match(stanza, handler) { + if (handler.from && handler.from !== stanza.attr.from) { + return undefined; + } + + if (handler.to && handler.to !== stanza.attr.to) { + return undefined; + } + + var child = stanza.getChild(handler.name, handler.xmlns); + + if (!child) { + return undefined; + } + + return { + child:child, + stanza:stanza + }; + } + + function processStanza(handlers, events, stanza, probeBuddies) { events.emit('stanza', stanza); - debug("stanza", stanza); if (stanza.is('message')) { return processMessage(events, stanza); } - if (!stanza.is('presence')) { - return; + if (stanza.is('presence')) { + return processPresence(events, stanza, probeBuddies); } - //looking for presence stenza for availability changes - var frm = stanza.attrs.from; + for (var idx = 0; idx < handlers.length; idx++) { + var handler = handlers[0]; + var matched = match(stanza, handler); - if (!frm) { - return; - } - var iD = frm.split('/')[0]; - var state = getState(stanza); - - if (probeBuddies[iD]) { - events.emit('probe_' + iD, state); - delete probeBuddies[iD]; - } else { - events.emit('buddy', iD, state); + if (matched) { + if (matched.stanza.attrs.type == 'error') { + return handler(handler.callback(matched.stanza)); + } + + return handler.callback(undefined, matched.child, matched.stanza); + } } + debug("tangling stanza", stanza); } - function connect(config, events, probeBuddies) { - var conn = new xmpp.Client(config); + function sendMessage(pending, connection) { + var args = Array.prototype.splice.call(arguments, 2); - conn.on('online', function () { - conn.send(new xmpp.Element('presence')); - events.emit('online'); - //make the connection live - setInterval(function () { - conn.send(new xmpp.Element('presence')); - }, 1000 * 10) - }); - - conn.on('stanza', function (stanza) { - debug("stanza", util.inspect(stanza, true, 100)); - processStanza(events, stanza, probeBuddies); - }); + debug("sending", args); - conn.on('error', function (err) { - debug("error", util.inspect(err, true, 100)); - events.emit('error', err); - }); + if (pending) { + return pending.push(args); + } - return new SimpleXMPP(conn, events, probeBuddies); + return xmpp.Client.prototype.send.apply(connection, args); } - module.exports = function (config) { - var events = new EventEmitter(); - var probeBuddies = {}; + function SimpleXMPP(params) { + EventEmitter.call(this); - return { - "on":function (event, listener) { - return events.on(event, listener); + var connection, + config = { + jid:new xmpp.JID(params.jid), + password:params.password, + host:params.host, + port:params.port }, + probeBuddies = {}, + handlers = [], + pending = [], + self = this; + + process.nextTick(function () { + connection = new xmpp.Client(config); + connection.on('stanza', function (stanza) { + processStanza(handlers, self, stanza, probeBuddies); + }); - "removeListener":function (event, listener) { - return events.removeListener(event, listener); - }, + connection.on('error', function (err) { + debug("error", util.inspect(err, true, 100)); + self.emit('error', err); + }); - "removeAllListeners":function (event) { - return events.removeListener(event); - }, + connection.on('online', function () { + sendMessage(pending, connection, new xmpp.Presence()); + self.emit('online'); - "setMaxListeners":function (n) { - return events.setMaxListeners(n); - }, + setInterval(function () { + sendMessage(pending, connection, new xmpp.Presence()); + }, config.presenceInterval || 10000); - "connect":function () { - return connect(config, events, probeBuddies); - } + var queue = pending; + pending = undefined; + + queue.forEach(function (msg) { + xmpp.Client.prototype.send.apply(connection, msg); + }); + }); + + }); + self.discoverServices = function (to, cb) { + var jid = config.jid.toString(), + disco = new xmpp.Iq('iq', { + type:'get', + to:to, + from:jid + }).c('query', { xmlns:DISCO_URL }); + + handlers.push({ + matcher:{ + from:to, + to:jid, + xmlns:DISCO_URL, + name:'query', + callback:cb + } + }); + + sendMessage(pending, connection, disco); + + /* + var roster = new xmpp.Element('iq', { + type: 'get', + from: connection.jid.toString(), + id: 'roster_1' + }).c('query', { xmlns: 'jabber:iq:roster' }); + + sendMessage(pending, connection, roster); + */ + }; + self.send = function (to, message) { + var stanza = new xmpp.Message({ + to:to, + type:'chat' + }); + stanza.c('body').t(message); + + sendMessage(pending, connection, stanza); }; + self.probe = function (buddy, callback) { + probeBuddies[buddy] = true; + + var stanza = new xmpp.Element('presence', { + type:'probe', + to:buddy + }); + self.once('probe_' + buddy, callback); + sendMessage(pending, connection, stanza); + }; + } + + util.inherits(SimpleXMPP, EventEmitter); + + module.exports = function (config) { + return new SimpleXMPP(config); }; })(module); \ No newline at end of file