diff options
Diffstat (limited to 'src/plugins/LastMessageCorrectionPlugin.ts')
-rw-r--r-- | src/plugins/LastMessageCorrectionPlugin.ts | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/src/plugins/LastMessageCorrectionPlugin.ts b/src/plugins/LastMessageCorrectionPlugin.ts new file mode 100644 index 00000000..c3db4c46 --- /dev/null +++ b/src/plugins/LastMessageCorrectionPlugin.ts @@ -0,0 +1,185 @@ +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; + }; +} |