Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ Converse implements a wide range of XMPP Extensions (XEPs), making it one of the
| [XEP-0184](https://xmpp.org/extensions/xep-0184.html) | Message Receipt | |
| [XEP-0198](https://xmpp.org/extensions/xep-0198.html) | Stream Management | |
| [XEP-0199](https://xmpp.org/extensions/xep-0199.html) | XMPP Ping | |
| [XEP-0202](https://xmpp.org/extensions/xep-0202.html) | Entity Time | |
| [XEP-0203](https://xmpp.org/extensions/xep-0203.html) | Delayed Delivery | |
| [XEP-0206](https://xmpp.org/extensions/xep-0206.html) | XMPP Over BOSH | |
| [XEP-0245](https://xmpp.org/extensions/xep-0245.html) | The /me Command | |
Expand Down
7 changes: 7 additions & 0 deletions conversejs.doap
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,13 @@
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0199.html"/>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0202.html"/>
<xmpp:status>partial</xmpp:status>
<xmpp:note>1:1 chats only, no MUC occupant support</xmpp:note>
</xmpp:SupportedXep>
</implements>
<implements>
<xmpp:SupportedXep>
<xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0203.html"/>
Expand Down
61 changes: 61 additions & 0 deletions docs/source/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2180,6 +2180,23 @@ If true, you'll see yourself in your list of contacts (aka the "roster") and
can open a chat with yourself.


send_entity_time
----------------

* Default: ``true``

If enabled, Converse will respond to entity time queries from other users
(per `XEP-0202 <https://xmpp.org/extensions/xep-0202.html>`_), sharing your
local timezone information.

Set to ``false`` if you prefer not to share your timezone with others for
privacy reasons. When disabled, Converse will respond with a
``service-unavailable`` error to time queries.

Note: This setting controls *outgoing* timezone information. To disable
*incoming* time warnings, use ``show_entity_time``.


show_send_button
----------------

Expand All @@ -2189,6 +2206,50 @@ Adds a button to the chat which can be clicked or tapped in order to send the
message.


show_entity_time
----------------

* Default: ``true``

If enabled, Converse will query contacts for their local time (per `XEP-0202 <https://xmpp.org/extensions/xep-0202.html>`_)
and show a warning bar in private chats when the contact is in "off-hours"
(e.g., nighttime in their timezone).

This is useful for distributed teams to avoid messaging colleagues at inappropriate times.

Related settings: ``entity_time_warning_start``, ``entity_time_warning_end``,
``entity_time_min_diff_hours``.


entity_time_warning_start
-------------------------

* Default: ``22``

The hour (0-23) at which the "off-hours" warning period starts.
Default is 22 (10 PM).


entity_time_warning_end
-----------------------

* Default: ``7``

The hour (0-23) at which the "off-hours" warning period ends.
Default is 7 (7 AM).


entity_time_min_diff_hours
--------------------------

* Default: ``0``

Minimum timezone difference (in hours) before showing the warning.

- ``0``: Show warning for any different timezone (default)
- ``3``: Only show warning if contact is 3+ hours ahead/behind


show_tab_notifications
----------------------

Expand Down
1 change: 1 addition & 0 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ module.exports = function(config) {
{ pattern: "src/plugins/rootview/tests/*.js", type: 'module' },
{ pattern: "src/plugins/rosterview/tests/*.js", type: 'module' },
{ pattern: "src/plugins/rosterview/tests/requesting_contacts.js", type: 'module' },
{ pattern: "src/plugins/time-views/tests/*.js", type: 'module' },
{ pattern: "src/shared/modals/tests/*.js", type: 'module' },
{ pattern: "src/utils/tests/*.js", type: 'module' },
],
Expand Down
1 change: 1 addition & 0 deletions src/headless/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export { MUCMessage, MUCMessages, MUC, MUCOccupant, MUCOccupants } from './plugi

import './plugins/ping/index.js'; // XEP-0199 XMPP Ping
import './plugins/pubsub/index.js'; // XEP-0060 Pubsub
import './plugins/time/index.js'; // XEP-0202 Entity Time

// RFC-6121 Contacts Roster
export { RosterContact, RosterContacts, RosterFilter, Presence, Presences } from './plugins/roster/index.js';
Expand Down
1 change: 1 addition & 0 deletions src/headless/karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ module.exports = function(config) {
{ pattern: "plugins/roster/tests/*.js", type: 'module' },
{ pattern: "plugins/smacks/tests/*.js", type: 'module' },
{ pattern: "plugins/status/tests/*.js", type: 'module' },
{ pattern: "plugins/time/tests/*.js", type: 'module' },
],
client: {
jasmine: {
Expand Down
57 changes: 57 additions & 0 deletions src/headless/plugins/time/api.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import api from '../../shared/api/index.js';
import converse from '../../shared/api/public.js';
import log from '@converse/log';

const { Strophe, stx, u } = converse.env;

export default {
/**
* The "time" namespace groups methods for XEP-0202 Entity Time
* @namespace api.time
* @memberOf api
*/
time: {
/**
* Gets the entity time from a JID per XEP-0202
* @method api.time.get
* @param {string} jid - The JID to query for time
* @param {number} [timeout=10000] - Timeout in milliseconds
* @returns {Promise<{utc: Date, tzo: string}|null>} The entity's time info or null on error
*/
async get(jid, timeout = 10000) {
if (!api.connection.authenticated()) {
log.debug('Not querying time when not authenticated');
return null;
}

const iq = stx`
<iq type="get" to="${jid}" id="${u.getUniqueId('time')}" xmlns="jabber:client">
<time xmlns="${Strophe.NS.TIME}"/>
</iq>`;

const result = await api.sendIQ(iq, timeout, false);

if (result === null) {
log.warn(`Timeout while getting time from ${jid}`);
return null;
} else if (u.isErrorStanza(result)) {
log.debug(`Error getting time from ${jid} (entity may not support XEP-0202)`);
return null;
}

const time_el = result.querySelector('time');
const utc_str = time_el?.querySelector('utc')?.textContent;
const tzo = time_el?.querySelector('tzo')?.textContent;

if (!utc_str || !tzo) {
log.error('Invalid time response - missing utc or tzo');
return null;
}

return {
utc: new Date(utc_str),
tzo: tzo
};
}
}
};
29 changes: 29 additions & 0 deletions src/headless/plugins/time/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* @description
* Converse.js plugin which adds support for XEP-0202 Entity Time.
* @see https://xmpp.org/extensions/xep-0202.html
* @copyright 2026, the Converse.js contributors
* @license Mozilla Public License (MPLv2)
*/
import api from '../../shared/api/index.js';
import converse from '../../shared/api/public.js';
import time_api from './api.js';
import { registerTimeHandler } from './utils.js';

converse.plugins.add('converse-time', {

initialize() {
api.settings.extend({
'send_entity_time': true, // Whether to respond to time requests
'show_entity_time': true,
'entity_time_warning_start': 22,
'entity_time_warning_end': 7,
'entity_time_min_diff_hours': 0, // Minimum timezone difference to show warning (0 = any different timezone)
});

Object.assign(api, time_api);

api.listen.on('connected', registerTimeHandler);
api.listen.on('reconnected', registerTimeHandler);
}
});
Loading
Loading