Răsfoiți Sursa

Implement sending presences according to XEP-0319: Last User Interaction in Presence

Christoph Scholz 6 ani în urmă
părinte
comite
12b7687a89
5 a modificat fișierele cu 74 adăugiri și 9 ștergeri
  1. 1 0
      CHANGES.md
  2. 33 2
      dist/converse.js
  3. 10 0
      docs/source/configuration.rst
  4. 5 5
      spec/presence.js
  5. 25 2
      src/headless/converse-core.js

+ 1 - 0
CHANGES.md

@@ -13,6 +13,7 @@
 - #1306 added option `notification_delay`
 - #1306 added option `notification_delay`
 - #1305 added value 'all' for 'show_desktop_notifications' to notifiy even if converse.js is open
 - #1305 added value 'all' for 'show_desktop_notifications' to notifiy even if converse.js is open
 - #1312 Error `unrecognized expression` in Safari
 - #1312 Error `unrecognized expression` in Safari
+- #1319 Implement sending of presences according to XEP-0319: Last User Interaction in Presence
 
 
 ## 4.0.4 (2018-10-29)
 ## 4.0.4 (2018-10-29)
 
 

+ 33 - 2
dist/converse.js

@@ -71780,6 +71780,7 @@ strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('DELAY', 'urn:xm
 strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('FORWARD', 'urn:xmpp:forward:0');
 strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('FORWARD', 'urn:xmpp:forward:0');
 strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('HINTS', 'urn:xmpp:hints');
 strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('HINTS', 'urn:xmpp:hints');
 strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload:0');
 strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload:0');
+strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('IDLE', 'urn:xmpp:idle:1');
 strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('MAM', 'urn:xmpp:mam:2');
 strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('MAM', 'urn:xmpp:mam:2');
 strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('NICK', 'http://jabber.org/protocol/nick');
 strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('NICK', 'http://jabber.org/protocol/nick');
 strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('OMEMO', "eu.siacs.conversations.axolotl");
 strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].addNamespace('OMEMO', "eu.siacs.conversations.axolotl");
@@ -71931,6 +71932,8 @@ _converse.default_settings = {
   expose_rid_and_sid: false,
   expose_rid_and_sid: false,
   geouri_regex: /https:\/\/www.openstreetmap.org\/.*#map=[0-9]+\/([\-0-9.]+)\/([\-0-9.]+)\S*/g,
   geouri_regex: /https:\/\/www.openstreetmap.org\/.*#map=[0-9]+\/([\-0-9.]+)\/([\-0-9.]+)\S*/g,
   geouri_replacement: 'https://www.openstreetmap.org/?mlat=$1&mlon=$2#map=18/$1/$2',
   geouri_replacement: 'https://www.openstreetmap.org/?mlat=$1&mlon=$2#map=18/$1/$2',
+  idle_presence_timeout: 300,
+  // Seconds after which an idle presence is sent
   jid: undefined,
   jid: undefined,
   keepalive: true,
   keepalive: true,
   locales_url: 'locale/{{{locale}}}/LC_MESSAGES/converse.json',
   locales_url: 'locale/{{{locale}}}/LC_MESSAGES/converse.json',
@@ -72166,6 +72169,12 @@ _converse.initialize = function (settings, callback) {
       _converse.sendCSI(_converse.ACTIVE);
       _converse.sendCSI(_converse.ACTIVE);
     }
     }
 
 
+    if (_converse.idle) {
+      _converse.idle = false;
+
+      _converse.xmppstatus.sendPresence();
+    }
+
     if (_converse.auto_changed_status === true) {
     if (_converse.auto_changed_status === true) {
       _converse.auto_changed_status = false; // XXX: we should really remember the original state here, and
       _converse.auto_changed_status = false; // XXX: we should really remember the original state here, and
       // then set it back to that...
       // then set it back to that...
@@ -72192,6 +72201,12 @@ _converse.initialize = function (settings, callback) {
       _converse.sendCSI(_converse.INACTIVE);
       _converse.sendCSI(_converse.INACTIVE);
     }
     }
 
 
+    if (_converse.idle_presence_timeout > 0 && _converse.idle_seconds > _converse.idle_presence_timeout && !_converse.idle) {
+      _converse.idle = true;
+
+      _converse.xmppstatus.sendPresence();
+    }
+
     if (_converse.auto_away > 0 && _converse.idle_seconds > _converse.auto_away && stat !== 'away' && stat !== 'xa' && stat !== 'dnd') {
     if (_converse.auto_away > 0 && _converse.idle_seconds > _converse.auto_away && stat !== 'away' && stat !== 'xa' && stat !== 'dnd') {
       _converse.auto_changed_status = true;
       _converse.auto_changed_status = true;
 
 
@@ -72207,7 +72222,7 @@ _converse.initialize = function (settings, callback) {
     /* Set an interval of one second and register a handler for it.
     /* Set an interval of one second and register a handler for it.
      * Required for the auto_away, auto_xa and csi_waiting_time features.
      * Required for the auto_away, auto_xa and csi_waiting_time features.
      */
      */
-    if (_converse.auto_away < 1 && _converse.auto_xa < 1 && _converse.csi_waiting_time < 1) {
+    if (_converse.auto_away < 1 && _converse.auto_xa < 1 && _converse.csi_waiting_time < 1 && _converse.idle_presence_timeout < 1) {
       // Waiting time of less then one second means features aren't used.
       // Waiting time of less then one second means features aren't used.
       return;
       return;
     }
     }
@@ -72707,7 +72722,17 @@ _converse.initialize = function (settings, callback) {
         presence.c('status').t(status_message).up();
         presence.c('status').t(status_message).up();
       }
       }
 
 
-      presence.c('priority').t(_lodash_noconflict__WEBPACK_IMPORTED_MODULE_4___default.a.isNaN(Number(_converse.priority)) ? 0 : _converse.priority);
+      presence.c('priority').t(_lodash_noconflict__WEBPACK_IMPORTED_MODULE_4___default.a.isNaN(Number(_converse.priority)) ? 0 : _converse.priority).up();
+
+      if (_converse.idle) {
+        const idle_since = new Date();
+        idle_since.setSeconds(idle_since.getSeconds() - _converse.idle_seconds);
+        presence.c('idle', {
+          xmlns: strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].NS.IDLE,
+          since: idle_since.toISOString()
+        });
+      }
+
       return presence;
       return presence;
     },
     },
 
 
@@ -73015,6 +73040,12 @@ _converse.initialize = function (settings, callback) {
     if (!Backbone.history.started) {
     if (!Backbone.history.started) {
       Backbone.history.start();
       Backbone.history.start();
     }
     }
+
+    if (_converse.idle_presence_timeout > 0) {
+      _converse.on('addClientFeatures', () => {
+        _converse.api.disco.own.features.add(strophe_js__WEBPACK_IMPORTED_MODULE_0__["Strophe"].NS.IDLE);
+      });
+    }
   }
   }
 
 
   if (!_lodash_noconflict__WEBPACK_IMPORTED_MODULE_4___default.a.isUndefined(_converse.connection) && _converse.connection.service === 'jasmine tests') {
   if (!_lodash_noconflict__WEBPACK_IMPORTED_MODULE_4___default.a.isUndefined(_converse.connection) && _converse.connection.service === 'jasmine tests') {

+ 10 - 0
docs/source/configuration.rst

@@ -757,6 +757,16 @@ then Converse will fall back to trying to determine the browser's language
 and fetching those translations, or if that fails the default English texts
 and fetching those translations, or if that fails the default English texts
 will be used.
 will be used.
 
 
+idle_presence_timeout
+---------------------
+
+* Default:  ``300``
+
+The amount of seconds after which the user is considered to be idle
+and an idle presence according to XEP-0319 is sent.
+
+If the given value is negative or ``0``, this feature is disabled.
+
 jid
 jid
 ---
 ---
 
 

+ 5 - 5
spec/presence.js

@@ -47,7 +47,7 @@
                 `<presence xmlns="jabber:client">`+
                 `<presence xmlns="jabber:client">`+
                     `<status>Hello world</status>`+
                     `<status>Hello world</status>`+
                     `<priority>0</priority>`+
                     `<priority>0</priority>`+
-                    `<c hash="sha-1" node="https://conversejs.org" ver="K7kn/M6VtmdMyo61pgn/jkZlax8=" xmlns="http://jabber.org/protocol/caps"/>`+
+                    `<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
                 `</presence>`
                 `</presence>`
             );
             );
             _converse.priority = 2;
             _converse.priority = 2;
@@ -57,7 +57,7 @@
                     `<show>away</show>`+
                     `<show>away</show>`+
                     `<status>Going jogging</status>`+
                     `<status>Going jogging</status>`+
                     `<priority>2</priority>`+
                     `<priority>2</priority>`+
-                    `<c hash="sha-1" node="https://conversejs.org" ver="K7kn/M6VtmdMyo61pgn/jkZlax8=" xmlns="http://jabber.org/protocol/caps"/>`+
+                    `<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
                 `</presence>`
                 `</presence>`
             );
             );
 
 
@@ -68,7 +68,7 @@
                     `<show>dnd</show>`+
                     `<show>dnd</show>`+
                     `<status>Doing taxes</status>`+
                     `<status>Doing taxes</status>`+
                     `<priority>0</priority>`+
                     `<priority>0</priority>`+
-                    `<c hash="sha-1" node="https://conversejs.org" ver="K7kn/M6VtmdMyo61pgn/jkZlax8=" xmlns="http://jabber.org/protocol/caps"/>`+
+                    `<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
                 `</presence>`
                 `</presence>`
             );
             );
         }));
         }));
@@ -97,7 +97,7 @@
                     .toBe(`<presence xmlns="jabber:client">`+
                     .toBe(`<presence xmlns="jabber:client">`+
                           `<status>My custom status</status>`+
                           `<status>My custom status</status>`+
                           `<priority>0</priority>`+
                           `<priority>0</priority>`+
-                          `<c hash="sha-1" node="https://conversejs.org" ver="K7kn/M6VtmdMyo61pgn/jkZlax8=" xmlns="http://jabber.org/protocol/caps"/>`+
+                          `<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
                           `</presence>`)
                           `</presence>`)
 
 
                 return test_utils.waitUntil(() => modal.el.getAttribute('aria-hidden') === "true");
                 return test_utils.waitUntil(() => modal.el.getAttribute('aria-hidden') === "true");
@@ -109,7 +109,7 @@
                 modal.el.querySelector('[type="submit"]').click();
                 modal.el.querySelector('[type="submit"]').click();
                 expect(_converse.connection.send.calls.mostRecent().args[0].toLocaleString())
                 expect(_converse.connection.send.calls.mostRecent().args[0].toLocaleString())
                     .toBe(`<presence xmlns="jabber:client"><show>dnd</show><status>My custom status</status><priority>0</priority>`+
                     .toBe(`<presence xmlns="jabber:client"><show>dnd</show><status>My custom status</status><priority>0</priority>`+
-                          `<c hash="sha-1" node="https://conversejs.org" ver="K7kn/M6VtmdMyo61pgn/jkZlax8=" xmlns="http://jabber.org/protocol/caps"/>`+
+                          `<c hash="sha-1" node="https://conversejs.org" ver="Hxbsr5fazs62i+O0GxIXf2OEDNs=" xmlns="http://jabber.org/protocol/caps"/>`+
                           `</presence>`)
                           `</presence>`)
                 done();
                 done();
             });
             });

+ 25 - 2
src/headless/converse-core.js

@@ -32,6 +32,7 @@ Strophe.addNamespace('DELAY', 'urn:xmpp:delay');
 Strophe.addNamespace('FORWARD', 'urn:xmpp:forward:0');
 Strophe.addNamespace('FORWARD', 'urn:xmpp:forward:0');
 Strophe.addNamespace('HINTS', 'urn:xmpp:hints');
 Strophe.addNamespace('HINTS', 'urn:xmpp:hints');
 Strophe.addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload:0');
 Strophe.addNamespace('HTTPUPLOAD', 'urn:xmpp:http:upload:0');
+Strophe.addNamespace('IDLE', 'urn:xmpp:idle:1');
 Strophe.addNamespace('MAM', 'urn:xmpp:mam:2');
 Strophe.addNamespace('MAM', 'urn:xmpp:mam:2');
 Strophe.addNamespace('NICK', 'http://jabber.org/protocol/nick');
 Strophe.addNamespace('NICK', 'http://jabber.org/protocol/nick');
 Strophe.addNamespace('OMEMO', "eu.siacs.conversations.axolotl");
 Strophe.addNamespace('OMEMO', "eu.siacs.conversations.axolotl");
@@ -197,6 +198,7 @@ _converse.default_settings = {
     expose_rid_and_sid: false,
     expose_rid_and_sid: false,
     geouri_regex: /https:\/\/www.openstreetmap.org\/.*#map=[0-9]+\/([\-0-9.]+)\/([\-0-9.]+)\S*/g,
     geouri_regex: /https:\/\/www.openstreetmap.org\/.*#map=[0-9]+\/([\-0-9.]+)\/([\-0-9.]+)\S*/g,
     geouri_replacement: 'https://www.openstreetmap.org/?mlat=$1&mlon=$2#map=18/$1/$2',
     geouri_replacement: 'https://www.openstreetmap.org/?mlat=$1&mlon=$2#map=18/$1/$2',
+    idle_presence_timeout: 300, // Seconds after which an idle presence is sent
     jid: undefined,
     jid: undefined,
     keepalive: true,
     keepalive: true,
     locales_url: 'locale/{{{locale}}}/LC_MESSAGES/converse.json',
     locales_url: 'locale/{{{locale}}}/LC_MESSAGES/converse.json',
@@ -416,6 +418,10 @@ _converse.initialize = function (settings, callback) {
         if (_converse.inactive) {
         if (_converse.inactive) {
             _converse.sendCSI(_converse.ACTIVE);
             _converse.sendCSI(_converse.ACTIVE);
         }
         }
+        if (_converse.idle) {
+            _converse.idle = false;
+            _converse.xmppstatus.sendPresence();
+        }
         if (_converse.auto_changed_status === true) {
         if (_converse.auto_changed_status === true) {
             _converse.auto_changed_status = false;
             _converse.auto_changed_status = false;
             // XXX: we should really remember the original state here, and
             // XXX: we should really remember the original state here, and
@@ -440,6 +446,12 @@ _converse.initialize = function (settings, callback) {
                 !_converse.inactive) {
                 !_converse.inactive) {
             _converse.sendCSI(_converse.INACTIVE);
             _converse.sendCSI(_converse.INACTIVE);
         }
         }
+        if (_converse.idle_presence_timeout > 0 &&
+                _converse.idle_seconds > _converse.idle_presence_timeout &&
+                !_converse.idle) {
+            _converse.idle = true;
+            _converse.xmppstatus.sendPresence();
+        }
         if (_converse.auto_away > 0 &&
         if (_converse.auto_away > 0 &&
                 _converse.idle_seconds > _converse.auto_away &&
                 _converse.idle_seconds > _converse.auto_away &&
                 stat !== 'away' && stat !== 'xa' && stat !== 'dnd') {
                 stat !== 'away' && stat !== 'xa' && stat !== 'dnd') {
@@ -457,7 +469,7 @@ _converse.initialize = function (settings, callback) {
         /* Set an interval of one second and register a handler for it.
         /* Set an interval of one second and register a handler for it.
          * Required for the auto_away, auto_xa and csi_waiting_time features.
          * Required for the auto_away, auto_xa and csi_waiting_time features.
          */
          */
-        if (_converse.auto_away < 1 && _converse.auto_xa < 1 && _converse.csi_waiting_time < 1) {
+        if (_converse.auto_away < 1 && _converse.auto_xa < 1 && _converse.csi_waiting_time < 1 && _converse.idle_presence_timeout < 1) {
             // Waiting time of less then one second means features aren't used.
             // Waiting time of less then one second means features aren't used.
             return;
             return;
         }
         }
@@ -888,7 +900,12 @@ _converse.initialize = function (settings, callback) {
             }
             }
             presence.c('priority').t(
             presence.c('priority').t(
                 _.isNaN(Number(_converse.priority)) ? 0 : _converse.priority
                 _.isNaN(Number(_converse.priority)) ? 0 : _converse.priority
-            );
+            ).up();
+            if (_converse.idle) {
+                const idle_since = new Date();
+                idle_since.setSeconds(idle_since.getSeconds() - _converse.idle_seconds);
+                presence.c('idle', {xmlns: Strophe.NS.IDLE, since: idle_since.toISOString()});
+            }
             return presence;
             return presence;
         },
         },
 
 
@@ -1187,6 +1204,12 @@ _converse.initialize = function (settings, callback) {
         if (!Backbone.history.started) {
         if (!Backbone.history.started) {
             Backbone.history.start();
             Backbone.history.start();
         }
         }
+
+        if (_converse.idle_presence_timeout > 0) {
+            _converse.on('addClientFeatures', () => {
+                _converse.api.disco.own.features.add(Strophe.NS.IDLE);
+            });
+        }
     }
     }
 
 
     if (!_.isUndefined(_converse.connection) &&
     if (!_.isUndefined(_converse.connection) &&