使用Node处理XMPP在线状态

4
我正在使用node-xmpp模块连接XMPP服务器并加入群聊。目前已经成功连接到服务器,设置了出席状态,加入了房间并读取消息。但我也想接收房间的用户列表。
根据XMPP协议,在客户端进入房间时需要发送出席元素(http://xmpp.org/extensions/xep-0045.html#enter-pres)。但是,我该如何在node中解析它呢?
我的代码目前看起来像这样:
var xmpp = require('node-xmpp');

// Create the XMPP Client
var cl = new xmpp.Client({
    jid: jid,
    password: password,
    reconnect: true
});

// Do things when online
cl.on('online', function() {
  util.log("We're online!");

  // Set client's presence
  cl.send(new xmpp.Element('presence', { type: 'available' }).c('show').t('chat'));
  cl.send(new xmpp.Element('presence', { to: room_jid+'/'+room_nick }).c('x', { xmlns: 'http://jabber.org/protocol/muc' }).c('history', {seconds: 1}));

  // Send keepalive
  setInterval(function() {
    cl.send(' ');
  }, 30000);




  cl.on('stanza', function(stanza) {
      // always log error stanzas
      if (stanza.attrs.type == 'error') {
        util.log('[error] ' + stanza);
        return;
      }

      // ignore everything that isn't a room message
      if (!stanza.is('message') || !stanza.attrs.type == 'chat') {
        return;
      }

      var body = stanza.getChild('body');
      // message without body is probably a topic change
      if (!body) {
        return;
      }

    // Extract username
    var from, room, _ref;
    _ref = stanza.attrs.from.split('/'), room = _ref[0], from = _ref[1];
     var message = body.getText();

     // Log topics and messages to the console
     if(!from) {
        util.log('Topic: ' + message);
     } else {
        util.log('[' + from + ']: ' + message);
     }
    });
});

我已经尝试使用以下方式触发出席状态:

if(stanza.is('presence')) {}

我尝试在cl.on('stanza')部分内实现,但没有成功。

1个回答

4

更新: 我现在正在描述一种新的方法,不需要客户端发送请求。

背景: 当客户端加入一个群聊时,服务器会返回包含已连接用户信息的存在数据包。

cl.on('stanza', function(stanza) {
    // always log error stanzas
    if (stanza.attrs.type == 'error') {
        util.log('[error] ' + stanza);
        return;
    }

    if(stanza.is('presence')){
        // We are only interested in stanzas with <x> in the payload or it will throw some errors
        if(stanza.getChild('x') !== undefined) {
            // Deciding what to do based on the xmlns attribute
            var _presXmlns = stanza.getChild('x').attrs.xmlns;

            switch(_presXmlns) {
                // If someone is joining or leaving
                case 'http://jabber.org/protocol/muc#user':
                    // Get the role of joiner/leaver
                    _presRole = stanza.getChild('x').getChild('item').attrs.role;
                    // Get the JID of joiner/leaver
                    _presJID  = stanza.getChild('x').getChild('item').attrs.jid;
                    // Get the nick of joiner/leaver
                    _presNick = stanza.attrs.from.split('/')[1];


                    // If it's not none, this user must be joining or changing his nick
                    if(_presRole !== 'none') {

                        // We are now handling the data of joinging / nick changing users. I recommend to use an in-memory store like 'dirty' [https://github.com/felixge/node-dirty] to store information of the users currentliy in the group chat.


                    } else {

                        // We are now handling the data of leaving users

                    }
                break;
            }

            return;
        }

        return;
    }

旧方法

之前我描述了一种查询群聊中当前用户的方法。通过维护一个存储所有用户流量(加入、离开、昵称更改)的存储器,这不再需要。但是,您仍然可以使用它来确保数据的一致性,比如存在问题,如出席信息未正确传递给客户端。这就是为什么下面仍然会描述它的原因:

要请求连接到房间的用户列表,您需要执行以下操作:

首先向服务器发送请求并请求用户列表:

cl.send(new xmpp.Element('iq', {from: jid, to: room_jid, type: 'get' }).c('query', { xmlns: 'http://jabber.org/protocol/disco#items' }));

然后监听iq-stanza,解析它们并用数据填充数组:
// Catching the requested user list
if(stanza.is('iq')){
    // Fetching usernames from return data (data structure: http://xmpp.org/extensions/xep-0045.html#example-12)
    var _items = stanza.getChild('query').getChildren('item');
    var users = new Array();
    for(var i = 0; i<_items.length; i++) {
        // We are building an object here to add more data later
        users[i] = new Object();
        users[i]['name'] = _items[i].attrs.name;
    }
    console.log(util.inspect(users, {depth: null, colors: true}));
    return;
}

这将为您提供用户列表。要请求唯一的JID,您必须探测每个用户。为了使列表保持最新,当用户离开时应该将其删除并添加+探测当他们加入时。

我不确定你问题的实际答案是什么,但你在这里描述的似乎很糟糕。所有房间内的人都会收到用户进出房间的消息。你为什么需要发送disco查询? - MattJ
1
@MattJ 你说得对。我找到了一种方法,可以利用服务器发送的存在数据来监测脚本进入房间以及用户加入/离开的情况。我会在有时间的时候更新我的帖子。但是使用 iq 查询的方法仍然很有用,可以确保客户端维护正确的用户列表,例如当存在消息未被传递时。 - user1372346

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接