@jsxc/jsxc
Version:
Real-time XMPP chat application with video calls, file transfer and encrypted communication
163 lines (125 loc) • 4.54 kB
text/typescript
import JID from '../JID';
import Message from '../Message';
import { AbstractPlugin, IMetaData } from '../plugin/AbstractPlugin';
import PluginAPI from '../plugin/PluginAPI';
import * as Namespace from '../connection/xmpp/namespace';
import { $msg } from '../vendor/Strophe';
import { IContact } from '@src/Contact.interface';
import Translation from '@util/Translation';
const MIN_VERSION = '4.0.0';
const MAX_VERSION = '99.0.0';
let PRESERVE_HANDLER = true;
export default class ReceiptPlugin extends AbstractPlugin {
public static getId(): string {
return 'receipts';
}
public static getName(): string {
return 'Message Delivery Receipts';
}
public static getMetaData(): IMetaData {
return {
description: Translation.t('setting-receipts-enable'),
xeps: [
{
id: 'XEP-0184',
name: 'Message Delivery Receipts',
version: '1.2',
},
],
};
}
constructor(pluginAPI: PluginAPI) {
super(MIN_VERSION, MAX_VERSION, pluginAPI);
Namespace.register('RECEIPTS', 'urn:xmpp:receipts');
pluginAPI.addFeature(Namespace.get('RECEIPTS'));
pluginAPI.addPreSendMessageStanzaProcessor(this.preSendMessageStanzaProcessor);
let connection = pluginAPI.getConnection();
connection.registerHandler(this.onMessage, null, 'message');
connection.registerHandler(this.onReceiptRequest, null, 'message', 'chat');
}
private preSendMessageStanzaProcessor = (message: Message, xmlStanza: Strophe.Builder): Promise<any> => {
if (message.getType() !== Message.MSGTYPE.CHAT) {
return Promise.resolve([message, xmlStanza]);
}
if (message.getPeer().isBare) {
addRequest();
return Promise.resolve([message, xmlStanza]);
}
let discoRepository = this.pluginAPI.getDiscoInfoRepository();
return discoRepository.hasFeature(message.getPeer(), [Namespace.get('RECEIPTS')]).then(hasFeature => {
if (hasFeature) {
addRequest();
}
return [message, xmlStanza];
});
function addRequest() {
// Add request according to XEP-0184
xmlStanza
.c('request', {
xmlns: Namespace.get('RECEIPTS'),
})
.up();
}
};
private onMessage = stanza => {
let stanzaElement = $(stanza);
let from = stanzaElement.attr('from');
let contact = from ? this.pluginAPI.getContact(new JID(from)) : undefined;
let receivedElement = stanzaElement.find('received[xmlns="urn:xmpp:receipts"]');
if (contact && receivedElement.length === 1) {
this.onReceipt(contact, receivedElement);
}
return PRESERVE_HANDLER;
};
private onReceipt = (contact: IContact, receivedElement: JQuery<Element>, tries = 0) => {
let receivedAttrId = receivedElement.attr('id');
if (!receivedAttrId) {
return;
}
let message = contact.getTranscript().findMessageByAttrId(receivedAttrId);
if (message) {
message.received();
} else if (tries < 5) {
setTimeout(() => {
this.onReceipt(contact, receivedElement, ++tries);
}, tries * 200);
}
};
private onReceiptRequest = (stanza: string) => {
let messageElement = $(stanza);
let isReceiptRequest = messageElement.find("request[xmlns='urn:xmpp:receipts']").length > 0;
if (!isReceiptRequest) {
return PRESERVE_HANDLER;
}
//@REVIEW is ^=urn:xmpp:forward: not enough?
let isForwarded = messageElement.find('forwarded[xmlns="urn:xmpp:forward:0"]').length > 0;
if (isForwarded) {
return PRESERVE_HANDLER;
}
let messageId = messageElement.attr('id');
if (!messageId) {
return PRESERVE_HANDLER;
}
let from = messageElement.attr('from');
if (!from) {
return PRESERVE_HANDLER;
}
let contact = this.pluginAPI.getContact(new JID(from));
if (!contact) {
return PRESERVE_HANDLER;
}
let subscription = contact.getSubscription();
if (subscription === 'both' || subscription === 'from') {
// Send received according to XEP-0184
this.pluginAPI.send(
$msg({
to: from,
}).c('received', {
xmlns: 'urn:xmpp:receipts',
id: messageId,
})
);
}
return PRESERVE_HANDLER;
};
}