Also create MEP messages received from a MAM archive

This commit is contained in:
JC Brand 2021-09-07 15:39:55 +02:00
parent f40a8f69ae
commit 8f34065fc0
5 changed files with 61 additions and 77 deletions

View File

@ -28,7 +28,9 @@ async function handleErrorMessage (stanza) {
return;
}
const chatbox = await api.chatboxes.get(from_jid);
chatbox?.handleErrorMessageStanza(stanza);
if (chatbox.get('type') === _converse.PRIVATE_CHAT_TYPE) {
chatbox?.handleErrorMessageStanza(stanza);
}
}
export function autoJoinChats () {

View File

@ -434,6 +434,7 @@ const ChatRoomMixin = {
if (!(await this.shouldShowErrorMessage(attrs))) {
return;
}
const message = this.getMessageReferencedByError(attrs);
if (message) {
const new_attrs = {
@ -535,19 +536,22 @@ const ChatRoomMixin = {
* @param { XMLElement } stanza
*/
async handleMessageStanza (stanza) {
if (stanza.getAttribute('type') !== 'groupchat') {
this.handleForwardedMentions(stanza);
return;
} else if (isArchived(stanza)) {
// MAM messages are handled in converse-mam.
// We shouldn't get MAM messages here because
// they shouldn't have a `type` attribute.
return log.warn(`Received a MAM message with type "groupchat"`);
const type = stanza.getAttribute('type');
if (type === 'error') {
return this.handleErrorMessageStanza(stanza);
}
if (type === 'groupchat') {
if (isArchived(stanza)) {
// MAM messages are handled in converse-mam.
// We shouldn't get MAM messages here because
// they shouldn't have a `type` attribute.
return log.warn(`Received a MAM message with type "groupchat"`);
}
this.createInfoMessages(stanza);
this.fetchFeaturesIfConfigurationChanged(stanza);
} else if (!type) {
return this.handleForwardedMentions(stanza);
}
this.createInfoMessages(stanza);
this.fetchFeaturesIfConfigurationChanged(stanza);
/**
* @typedef { Object } MUCMessageData
* An object containing the original groupchat message stanza,
@ -2098,11 +2102,43 @@ const ChatRoomMixin = {
return false;
},
/**
* Given { @link MessageAttributes } look for XEP-0316 Room Notifications and create info
* messages for them.
* @param { XMLElement } stanza
*/
handleMEPNotification (attrs) {
if (attrs.from !== this.get('jid') || !attrs.activities) {
return false;
}
attrs.activities?.forEach(activity_attrs => {
const mdata = Object.assign({ 'msgid': attrs.msgid }, activity_attrs);
this.createMessage(mdata)
});
return !!attrs.activities.length
},
/**
* Returns an already cached message (if it exists) based on the
* passed in attributes map.
* @method _converse.ChatRoom#getDuplicateMessage
* @param { object } attrs - Attributes representing a received
* message, as returned by { @link parseMUCMessage }
* @returns {Promise<_converse.Message>}
*/
getDuplicateMessage (attrs) {
if (attrs.activities?.length) {
return this.messages.findWhere({'type': 'info', 'msgid': attrs.msgid});
} else {
return _converse.ChatBox.prototype.getDuplicateMessage.call(this, attrs);
}
},
/**
* Handler for all MUC messages sent to this groupchat. This method
* shouldn't be called directly, instead {@link _converse.ChatRoom#queueMessage}
* should be called.
* @private
* @method _converse.ChatRoom#onMessage
* @param { MessageAttributes } attrs - A promise which resolves to the message attributes.
*/
@ -2114,13 +2150,15 @@ const ChatRoomMixin = {
}
const message = this.getDuplicateMessage(attrs);
if (message) {
return this.updateMessage(message, attrs);
(message.get('type') === 'groupchat') && this.updateMessage(message, attrs);
return;
} else if (attrs.is_valid_receipt_request || attrs.is_marker || this.ignorableCSN(attrs)) {
return;
}
if (
this.handleMetadataFastening(attrs) ||
this.handleMEPNotification(attrs) ||
(await this.handleRetraction(attrs)) ||
(await this.handleModeration(attrs)) ||
(await this.handleSubjectChange(attrs))
@ -2128,6 +2166,7 @@ const ChatRoomMixin = {
attrs.nick && this.removeNotification(attrs.nick, ['composing', 'paused']);
return;
}
this.setEditable(attrs, attrs.time);
if (attrs['chat_state']) {

View File

@ -33,7 +33,7 @@ const { NS } = Strophe;
export function getMEPActivities (stanza) {
const items_el = sizzle(`items[node="${Strophe.NS.CONFINFO}"]`, stanza).pop();
if (!items_el) {
return [];
return null;
}
const from = stanza.getAttribute('from');
const msgid = stanza.getAttribute('id');
@ -127,6 +127,7 @@ export async function parseMUCMessage (stanza, chatbox, _converse) {
* @typedef { Object } MUCMessageAttributes
* The object which {@link parseMUCMessage} returns
* @property { ('me'|'them') } sender - Whether the message was sent by the current user or someone else
* @property { Array<Object> } activities - A list of objects representing XEP-0316 MEP notification data
* @property { Array<Object> } references - A list of objects representing XEP-0372 references
* @property { Boolean } editable - Is this message editable via XEP-0308?
* @property { Boolean } is_archived - Is this message from a XEP-0313 MAM archive?
@ -181,6 +182,7 @@ export async function parseMUCMessage (stanza, chatbox, _converse) {
{
from,
nick,
'activities': getMEPActivities(stanza),
'body': stanza.querySelector('body')?.textContent?.trim(),
'chat_state': getChatState(stanza),
'from_muc': Strophe.getBareJidFromJid(from),

View File

@ -2,7 +2,6 @@ import isObject from 'lodash-es/isObject';
import log from "@converse/headless/log.js";
import { ROLES } from '@converse/headless/plugins/muc/index.js';
import { _converse, api, converse } from '@converse/headless/core.js';
import { getMEPActivities } from '@converse/headless/plugins/muc/parsers.js';
import { safeSave } from '@converse/headless/utils/core.js';
const { Strophe, sizzle, u } = converse.env;
@ -180,57 +179,6 @@ export async function autoJoinRooms () {
}
/**
* Given a stanza, look for XEP-0316 Room Notifications and create info
* messages for them.
* @param { XMLElement } stanza
*/
async function handleMEPNotification (stanza) {
const from = stanza.getAttribute('from');
if (u.isSameBareJID(from, _converse.bare_jid)) {
return;
}
const room = await api.rooms.get(from);
if (!room) {
log.warn(`Received a MEP message for a non-existent room: ${from}`);
return;
}
const msgid = stanza.getAttribute('id');
if (room.messages.findWhere({ msgid })) {
// We already handled this stanza before
return;
}
getMEPActivities(stanza, room).forEach(attrs => {
room.createMessage(attrs);
api.trigger('message', { stanza, attrs, 'chatbox': room });
});
}
function checkIfMEP (message) {
try {
if (sizzle(`event[xmlns="${Strophe.NS.PUBSUB}#event"]`, message).length) {
handleMEPNotification(message);
}
} catch (e) {
log.error(e.message);
}
return true;
}
export function registerPEPPushHandler () {
// Add a handler for devices pushed from other connected clients
_converse.connection.addHandler(checkIfMEP, null, 'message', 'headline');
// XXX: This is a hack. Prosody's MUC MAM doesn't allow for quering
// non-groupchat messages. So even though they might be archived, they
// don't get returned on query. To bypass this, some MEP messages are sent
// with type="groupchat".
// https://hg.prosody.im/prosody-modules/rev/da9469e68dee
_converse.connection.addHandler(checkIfMEP, null, 'message', 'groupchat');
}
export function onAddClientFeatures () {
if (api.settings.get('allow_muc')) {
api.disco.own.features.add(Strophe.NS.MUC);

View File

@ -576,7 +576,6 @@ describe("Message Retractions", function () {
occupant.save('role', 'member');
const retraction_stanza = await sendAndThenRetractMessage(_converse, view);
await u.waitUntil(() => view.querySelectorAll('.chat-msg--retracted').length === 1, 1000);
console.log('XXX: First message retracted by author');
const msg_obj = view.model.messages.last();
expect(msg_obj.get('retracted')).toBeTruthy();
@ -598,19 +597,15 @@ describe("Message Retractions", function () {
const reflection = u.toStanza(`
<message type="groupchat" id="${retraction_stanza.getAttribute('id')}" from="${muc_jid}" to="${muc_jid}/romeo">
<apply-to id="${stanza_id}" xmlns="urn:xmpp:fasten:0">
<moderated by='${_converse.bare_jid}' xmlns='urn:xmpp:message-moderate:0'>
<retract xmlns='urn:xmpp:message-retract:0' />
</moderated>
<retract xmlns='urn:xmpp:message-retract:0' />
</apply-to>
</message>`);
spyOn(view.model, 'handleRetraction').and.callThrough();
_converse.connection._dataRecv(mock.createRequest(reflection));
await u.waitUntil(() => view.model.handleRetraction.calls.count() === 1, 1000);
console.log('XXX: Handle retraction was called on reflection');
await u.waitUntil(() => view.model.messages.length === 1, 1000);
console.log('XXX: We have one message');
await u.waitUntil(() => view.model.messages.length === 2, 1000);
expect(view.model.messages.last().get('retracted')).toBeTruthy();
expect(view.model.messages.last().get('is_ephemeral')).toBe(false);
expect(view.model.messages.last().get('editable')).toBe(false);
@ -648,9 +643,7 @@ describe("Message Retractions", function () {
<forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
<apply-to id="${stanza_id}" xmlns="urn:xmpp:fasten:0">
<moderated by='${_converse.bare_jid}' xmlns='urn:xmpp:message-moderate:0'>
<retract xmlns='urn:xmpp:message-retract:0' />
</moderated>
</apply-to>
</message>`);