Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
};
}
}
};
31 changes: 31 additions & 0 deletions src/headless/plugins/time/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* @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';

const { Strophe } = converse.env;
Comment thread Fixed

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