@jsxc/jsxc
Version:
Real-time XMPP chat application with video calls, file transfer and encrypted communication
186 lines (141 loc) • 5.54 kB
text/typescript
import { AbstractPlugin, IMetaData } from '../plugin/AbstractPlugin';
import PluginAPI from '../plugin/PluginAPI';
import Translation from '@util/Translation';
import * as Namespace from '@connection/xmpp/namespace';
import { IContact } from '@src/Contact.interface';
import { IMessage } from '@src/Message.interface';
import Message from '@src/Message';
/**
* XEP-0308: Last Message Correction
*
* @version: 1.2.0
* @see: https://xmpp.org/extensions/xep-0308.html
*
*/
const CORRECTION_CMD = '/fix';
const LMC = 'urn:xmpp:message-correct:0';
const MIN_VERSION = '4.3.0';
const MAX_VERSION = '99.0.0';
Namespace.register('LAST_MSG_CORRECTION', LMC);
export default class LastMessageCorrectionPlugin extends AbstractPlugin {
public static getId(): string {
return 'lmc';
}
public static getName(): string {
return 'Last Message Correction';
}
public static getMetaData(): IMetaData {
return {
description: Translation.t('setting-lmc-enable'),
xeps: [
{
id: 'XEP-0308',
name: 'Last Message Correction',
version: '1.2.0',
},
],
};
}
private correctionRequests: { [contactUid: string]: IMessage } = {};
constructor(pluginAPI: PluginAPI) {
super(MIN_VERSION, MAX_VERSION, pluginAPI);
pluginAPI.registerCommand(CORRECTION_CMD, this.commandHandler, 'cmd_correction');
pluginAPI.addPreSendMessageStanzaProcessor(this.addReplaceElementToStanza, 90);
pluginAPI.addAfterReceiveMessageProcessor(this.checkMessageCorrection, 90);
pluginAPI.registerChatMessageMenuItem({
generate: this.generateCorrectMessageMenuItem,
});
}
private commandHandler = async (args: string[], contact: IContact, messageString: string) => {
const originalMessage = contact.getTranscript().getFirstOutgoingMessage();
this.correctionRequests[contact.getUid()] = originalMessage;
if (!originalMessage || !contact.isChat()) {
return false;
}
const chatWindow = contact.getChatWindow();
const message = new Message({
peer: contact.getJid(),
direction: Message.DIRECTION.OUT,
type: contact.getType(),
plaintextMessage: messageString.replace(/^\/fix /, ''),
attachment: chatWindow.getAttachment(),
unread: false,
original: originalMessage.getUid(),
});
contact.getTranscript().pushMessage(message);
chatWindow.clearAttachment();
let pipe = contact.getAccount().getPipe('preSendMessage');
return pipe
.run(contact, message)
.then(([contact, message]) => {
originalMessage.getLastVersion().setReplacedBy(message);
contact.getAccount().getConnection().sendMessage(message);
return true;
})
.catch(err => {
this.pluginAPI.Log.warn('Error during preSendMessage pipe', err);
return false;
});
};
private addReplaceElementToStanza = async (
message: IMessage,
xmlMsg: Strophe.Builder
): Promise<[IMessage, Strophe.Builder]> => {
const contact = this.pluginAPI.getContact(message.getPeer());
const originalMessage = this.correctionRequests[contact.getUid()];
if (!originalMessage || originalMessage.getLastVersion().getUid() !== message.getUid()) {
return [message, xmlMsg];
}
delete this.correctionRequests[contact.getUid()];
xmlMsg
.c('replace', {
xmlns: LMC,
id: originalMessage.getAttrId(),
})
.up();
return [message, xmlMsg];
};
private checkMessageCorrection = async (
contact: IContact,
message: IMessage,
stanza: Element
): Promise<[IContact, IMessage, Element]> => {
const replaceElement = $(stanza).find(`>replace[xmlns="${LMC}"]`);
if (replaceElement.length === 0) {
return [contact, message, stanza];
}
const attrIdToBeReplaced = replaceElement.attr('id');
if (!attrIdToBeReplaced) {
return [contact, message, stanza];
}
const firstIncomingMessage = contact.getTranscript().getFirstIncomingMessage();
if (firstIncomingMessage && firstIncomingMessage.getAttrId() === attrIdToBeReplaced) {
message.setOriginal(firstIncomingMessage);
firstIncomingMessage.getLastVersion().setReplacedBy(message);
}
return [contact, message, stanza];
};
private generateCorrectMessageMenuItem = (contact: IContact, message: IMessage) => {
if (message.isOutgoing()) {
const lastOutgoingMessage = contact.getTranscript().getFirstOutgoingMessage().getLastVersion();
if (lastOutgoingMessage.getUid() === message.getUid()) {
const chatWindow = contact.getChatWindow();
return {
id: 'lmc-edit',
label: '',
icon: 'edit',
handler: () => {
let plaintextMessage = message.getPlaintextMessage();
if (message.hasAttachment()) {
const attachment = message.getAttachment();
chatWindow.setAttachment(attachment);
plaintextMessage = plaintextMessage.replace(attachment.getData() + '\n', '');
}
chatWindow.setInput(CORRECTION_CMD + ' ' + plaintextMessage);
},
};
}
}
return false;
};
}