@converse/headless
Version:
Converse.js Headless build
152 lines (142 loc) • 5.48 kB
JavaScript
import log from '@converse/log';
import { Strophe } from 'strophe.js';
import _converse from './_converse.js';
import api from './api/index.js';
import converse from './api/public.js';
import { CHAT_STATES, MARKER_TYPES } from './constants.js';
const { u, stx, Stanza } = converse.env;
/**
* Reject an incoming message by replying with an error message of type "cancel".
* @param {Element} stanza
* @param {string} text
* @return void
*/
export function rejectMessage(stanza, text) {
api.send(
stx`<message to="${stanza.getAttribute('from')}"
type="error"
id="${stanza.getAttribute('id')}"
xmlns="jabber:client">
<error type="cancel">
<not-allowed xmlns="${Strophe.NS.STANZAS}"/>
<text xmlns="${Strophe.NS.STANZAS}">${text}</text>
</error>
</message>`,
);
log.warn(`Rejecting message stanza with the following reason: ${text}`);
log.warn(stanza);
}
/**
* Send out a XEP-0333 chat marker.
* @fires sendMarker
* @param {string} to_jid
* @param {string} id - The id of the message being marked
* @param {import("./types").MessageMarkerType} type - The marker type
* @param {string} [msg_type]
*/
export async function sendMarker(to_jid, id, type, msg_type) {
if (!MARKER_TYPES.includes(type)) {
log.error(`Invalid marker type: ${type}`);
return;
}
const bare_jid = Strophe.getBareJidFromJid(to_jid);
const chatbox = _converse.state.chatboxes?.get(bare_jid);
/**
* Hook which lets plugins handle sending a chat marker (such as OMEMO
* which sends an encrypted version). If any hook listener returns
* `{ handled: true }` the cleartext stanza is suppressed.
*
* Context: the {@link ChatBox} / {@link MUC} model for `to_jid`, or
* `undefined` if not found.
*
* Hook data: `{ to_jid, id, type, msg_type, handled: false }`
*
* @example
* api.listen.on('sendMarker', async (chatbox, data) => {
* if (!chatbox?.get('omemo_active')) return data;
* await sendEncryptedMarker(chatbox, data);
* return { ...data, handled: true };
* });
*/
const { handled } = await api.hook('sendMarker', chatbox, { to_jid, id, type, msg_type, handled: false });
if (handled) return;
const stanza = stx`
<message from="${api.connection.get().jid}"
id="${u.getUniqueId()}"
to="${to_jid}"
type="${msg_type ? msg_type : 'chat'}"
xmlns="jabber:client">
<${Stanza.unsafeXML(type)} xmlns="${Strophe.NS.MARKERS}" id="${id}"/>
</message>`;
api.send(stanza);
}
/**
* @param {string} to_jid
* @param {string} id
* @return void
*/
export function sendReceiptStanza(to_jid, id) {
const receipt_stanza = stx`
<message from="${api.connection.get().jid}"
id="${u.getUniqueId()}"
to="${to_jid}"
type="chat"
xmlns="jabber:client">
<received xmlns="${Strophe.NS.RECEIPTS}" id="${id}"/>
<store xmlns="${Strophe.NS.HINTS}"/>
</message>`;
api.send(receipt_stanza);
}
/**
* Sends a message with the given XEP-0085 chat state.
* @param {string} jid
* @param {import("./types").ChatStateType} chat_state
*/
export function sendChatState(jid, chat_state) {
if (!jid) {
log.error(`sendChatState called with no JID`);
return;
} else if (!CHAT_STATES.includes(chat_state)) {
log.error(`Invalid chat state: ${chat_state}`);
return;
}
if (api.settings.get('send_chat_state_notifications') && chat_state) {
const allowed = api.settings.get('send_chat_state_notifications');
if (Array.isArray(allowed) && !allowed.includes(chat_state)) {
return;
}
// When OMEMO is active the OMEMO plugin sends an encrypted SCE version;
// suppress the cleartext stanza so only one notification goes out.
if (_converse.state.chatboxes?.get(jid)?.get('omemo_active')) return;
api.send(
stx`<message id="${u.getUniqueId()}" to="${jid}" type="chat" xmlns="jabber:client">
<${Stanza.unsafeXML(chat_state)} xmlns="${Strophe.NS.CHATSTATES}"/>
<no-store xmlns="${Strophe.NS.HINTS}"/>
<no-permanent-store xmlns="${Strophe.NS.HINTS}"/>
</message>`,
);
}
}
/**
* Sends a message stanza to retract a message in this chat
* @param {string} jid
* @param {import('../shared/message').default} message - The message which we're retracting.
* @param {string} retraction_id - Unique ID for the retraction message
*/
export function sendRetractionMessage(jid, message, retraction_id) {
const origin_id = message.get('origin_id');
if (!origin_id) {
throw new Error("Can't retract message without a XEP-0359 Origin ID");
}
const stanza = stx`
<message id="${retraction_id}"
to="${jid}"
type="chat"
xmlns="jabber:client">
<retract id="${origin_id}" xmlns="${Strophe.NS.RETRACT}"/>
<body>/me retracted a message</body>
<store xmlns="${Strophe.NS.HINTS}"/>
<fallback xmlns="${Strophe.NS.FALLBACK}" for="${Strophe.NS.RETRACT}" />
</message>`;
return api.connection.get().send(stanza);
}