"use strict"; import {GeoLoc} from "./GeoLoc"; import {stringify} from "querystring"; import {xu} from "../XMPPUtils"; import {Conversation} from "./Conversation"; import {isDefined} from "../Utils.js"; import {DataStoreType} from "../../config/config.js"; export {}; /** * @class * @name Message * @public * @description * This class is used to represent a message in a conversation
* A message is exchanged when discussing in One-to-One or in a Bubble. */ class Message { /** * The Type of message. * @public * @enum {{ key: number, value: string }} * @readonly */ public static Type: any = { /** A chat message */ CHAT: { key: 0, value: "Chat" }, /** A file message */ FILE: { key: 1, value: "File" }, /** A file message */ FS: { key: 2, value: "FileSharing" }, /** A WebRTC message */ WEBRTC: { key: 4, value: "WebRTC CAll" }, /** A Recording message */ RECORDING: { key: 5, value: "Recording" }, /** A Form message */ FORM: { key: 6, value: "FORM" } }; /** * The Status of the Receipt. * @public * @enum {number} * @readonly */ public static ReceiptStatus = { /** No receipt received yet */ NONE: 0, /** No receipt received after a while (The server doesn't answer) */ ERROR: 1, /** Receipt in progress */ IN_PROGRESS: 2, /** The server has confirmed the reception of the message */ SENT: 3, /** The message has been received but not read */ UNREAD: 4, /** The message has been read */ READ: 5 }; /** * @private */ public static ReceiptStatusText = ["close", "info", "calllog", "check", "done", "read"]; /** * The Side of Message's from * @public * @enum {string} * @readonly */ public static Side = { /** Message is from a recipient */ LEFT: "L", /** Message is from me */ RIGHT: "R", /** Specific admin message */ ADMIN: "ADMIN" }; public serverAckTimer: any; private index: any; public id: string; public type: any; public date: Date; public from: any; public side: string; //public data: string; public status: string; public receiptStatus: number; public fileId: string; public fileName: string; public isMarkdown: boolean; public subject: string; public geoloc: GeoLoc; public voiceMessage: any; public alternativeContent: any; public attention: any; public mentions: any; public urgency: string; public urgencyAck: boolean = false; public urgencyHandler: any = null; //public translatedText: string = null; // private rxSubject: Subject; //public isMerged: boolean; public historyIndex: string = null; //public showCorrectedMessages: boolean; //public replaceMsgs: any[]; public fileErrorMsg: string = null; // Message Attachment Part public attachedMsgId: string = null; public attachIndex: number; public attachNumber: number; public fromJid: any; public resource: any; public toJid: any; public content: any; public lang: any; public cc: any; public cctype: any; public isEvent: any; public event: any; public oob: { url: string, mime: string, filename: string, filesize: string }; public fromBubbleJid: any; public fromBubbleUserJid: any; public answeredMsgId: string; public answeredMsg: Message; public answeredMsgDate: string; public answeredMsgStamp: string; fileTransfer: any; public eventJid: string; public originalMessageReplaced: Message; public confOwnerId: string; public confOwnerDisplayName: string; public confOwnerJid: string; public conversation: Conversation; public isForwarded : boolean; public forwardedMsg : any; public replacedByMessage: Message; public deleted : boolean; public modified : boolean; public rainbowCpaas : any; public datastoretypeOfMsg : DataStoreType; public shortFileDescriptor: any; constructor(serverAckTimer: any, index: any, id: string, type: any, date: Date, from: any, side: string, // data: string, status: string, receiptStatus: number, // fileId: string, // fileName: string, isMarkdown: boolean, subject: string, geoloc: GeoLoc, voiceMessage: any, alternativeContent: any, attention: any, mentions: any, urgency: string, urgencyAck: boolean = false, urgencyHandler: any = null, /* translatedText: string = null, */ // isMerged: boolean, historyIndex: string = null, //showCorrectedMessages: boolean, //replaceMsgs: any[], // fileErrorMsg: string = null, attachedMsgId: string = null, attachIndex: number, attachNumber: number, // fromJid: any, resource: any, toJid: any, content: any, lang: any, cc: any, cctype: any, isEvent: any, event: any, oob: { url: string, mime: string, filename: string, filesize: string }, fromBubbleJid: any, fromBubbleUserJid: any, answeredMsg: Message, answeredMsgId: string, answeredMsgDate: string, answeredMsgStamp: string, // fileTransfer: any, eventJid: string, originalMessageReplaced: Message, confOwnerId: string, confOwnerDisplayName: string, confOwnerJid: string, isForwarded:boolean, forwardedMsg: any, deleted:boolean = false, modified : boolean = false, rainbowCpaas: any = null, datastoretypeOfMsg: DataStoreType = DataStoreType.StoreTwinSide ) { /** * @private * @readonly */ this.serverAckTimer = null; /** * @private * @readonly */ this.index = index; /** * @public * @readonly * @property {string} id The ID of the Message * @instance */ this.id = id; // Answer message. /** * @public * @property {string} answeredMsg The answered message of the message answered * @readonly */ this.answeredMsg = answeredMsg; /** * @public * @property {string} answeredMsgId The Id of the message answered * @readonly */ this.answeredMsgId = answeredMsgId; /** * @public * @property {string} answeredMsgDate The Date of the message answered * @readonly */ this.answeredMsgDate = answeredMsgDate; /** * @public * @property {string} answeredMsgStamp The Stamp of the message answered * @readonly */ this.answeredMsgStamp = answeredMsgStamp; /** * @public * @readonly * @property {string} fromJid The JID (without the resource) of the user who sent this Message. Can be the identity of a user or a user inside a Bubble * @instance */ this.fromJid = from && from.jid_im; this.from = from; /** * @public * @property {Side} side The message originator Message.Side.RIGHT is the bot and Message.Side.LT is other side. * @instance * @readonly */ this.side = side; /** * @public * @readonly * @property {string} resource The resource of the user who sent this message * @instance */ this.resource = ""; /** * @public * @property {Date} date The creation date of the message * @instance * @readonly */ this.date = date; /** * @public * @readonly * @property {string} toJid The JID of the recipient of this message * @instance */ this.toJid = ""; /** * @public * @readonly * @property {string} type The type of the message. Can be `chat` or `groupchat` * @instance */ this.type = type; /** * @public * @readonly * @property {string} content The content of this message (text) * @instance */ this.content = content; /** * @private * @instance * @readonly */ this.status = status; /** * @public * @property {ReceiptStatus} receiptStatus The state of the receipt * @instance * @readonly */ this.receiptStatus = Message.ReceiptStatus.NONE; /** * @public * @readonly * @property {string} lang The language of the content for this Message (if specified) * @instance */ this.lang = ""; /** * @public * @property {string} fileId An attached file Id (if exists) * @instance * @readonly */ //this.fileId = fileId; /** * @public * @readonly * @property {Boolean} cc True if the message is a carbon-copy (duplicated message due to several resources used) * @instance */ this.cc = false; /** * @public * @readonly * @property {string} cctype The Carbon-copy type. Can be `sent` or `received` * @instance */ this.cctype = "sent"; /** * @public * @readonly * @property {Boolean} isEvent True if the message is an event (a specific admin message in Bubble - should not be considered as text message) * @instance */ this.isEvent = isDefined(isEvent)?isEvent:false; /** * @public * @readonly * @property {string} event Contains the name of the event (only filled if isEvent=true) * @instance */ this.event = event?event:""; /** * @public * @readonly * @property {Object[]} alternativeContent The list of alternative contents * @property {String} alternativeContent.message The alternative message content * @property {String} alternativeContent.type The alternative message content-type * @instance */ this.alternativeContent = alternativeContent; /** * @public * @property {boolean} isMarkdown If the message is a markdown type message * @readonly */ this.isMarkdown = isMarkdown; /** * @public * @readonly * @property {string} subject The subject of the message (if provided) * @instance */ this.subject = subject; /** * @public * @readonly * @property {Object} oob The description of an attached file to the message (if provided) * @property {String} oob.url The file URL * @property {String} oob.mime The file mime-type * @property {String} oob.filename The file name * @property {Number} oob.filesize The file size * @instance */ this.oob = oob?oob:null; /** * @public * @readonly * @property {string} fromBubbleJid The JID of the bubble that received the message. (Only for `groupchat` message) * @instance */ this.fromBubbleJid = ""; /** * @public * @readonly * @property {string} fromBubbleUserJid The JID of the user who send the message without the JID of the Bubble. (Only for `groupchat` message) * @instance */ this.fromBubbleUserJid = null; /** * @public * @property {geoloc} geoloc * @readonly */ this.geoloc = geoloc; /** * @public * @property {object} attention Boolean to indicate if the current logged user is mentionned in the message. * @readonly * @instance */ this.attention = attention; /** * @public * @property {Array} mentions Array of contacts mentionned in the message. * @readonly * @instance */ this.mentions = mentions; /** * @public * @property {any} voiceMessage * @readonly */ this.voiceMessage = voiceMessage; /** * @public * @property {string} urgency the urgency of message ('std', 'low', 'middle', 'high') * @readonly */ this.urgency = urgency; /** * @public * @property {boolean} urgencyAck give the information that an urgent message has been updated. * @readonly */ this.urgencyAck = urgencyAck; /** * @private * @readonly */ this.urgencyHandler = urgencyHandler; /** * @public * @property {string} translatedText the translation of the message. * @readonly */ //this.translatedText = translatedText; /** * used to know if merged cell when it's webrtc or SFU message or ADMIN message * @private * @readonly */ // this.isMerged = isMerged; /** * @public * @property {Array} historyIndex the historyIndex of the message. * @readonly */ this.historyIndex = historyIndex; /** * @public * @property {boolean} showCorrectedMessages the showCorrectedMessages of the message. * @readonly */ //this.showCorrectedMessages = showCorrectedMessages; /** * @public * @property {string} replaceMsgs the replaceMsgs of the message. * @readonly */ //this.replaceMsgs = replaceMsgs; // Message Attachment Part /** * @public * @property {string} attachedMsgId the attachedMsgId of the message. * @readonly */ this.attachedMsgId = attachedMsgId; /** * @public * @property {string} attachIndex the attachIndex of the message. * @readonly */ this.attachIndex = attachIndex; /** * @public * @property {string} attachNumber the attachNumber of the message. * @readonly */ this.attachNumber = attachNumber; /** * @public * @property {string} eventJid the eventJid of the message. * @readonly */ this.eventJid = eventJid; /** * @public * @property {Message} originalMessageReplaced the originalMessageReplaced of the message. * @readonly */ this.originalMessageReplaced = originalMessageReplaced; /** * @public * @property {string} confOwnerId the confOwnerId of the message. * @readonly */ this.confOwnerId = confOwnerId; /** * @public * @property {string} confOwnerDisplayName the confOwnerDisplayName of the message. * @readonly */ this.confOwnerDisplayName = confOwnerDisplayName; /** * @public * @property {string} confOwnerJid the confOwnerJid of the message. * @readonly */ this.confOwnerJid = confOwnerJid; /** * @public * @readonly * @property {Conversation} conversation The Conversation the message belongs to (if provided) */ this.conversation = undefined; /** * @public * @property {boolean} isForwarded the message has been forwarded. * @readonly */ this.isForwarded = isForwarded; /** * @public * @property {any} forwardedMsg original message that has been forwarded. * @readonly */ this.forwardedMsg = forwardedMsg; /** * @public * @property {Message} replacedBuMessage original message has been replaced by the spotted Message.. * @readonly */ this.replacedByMessage = null; /** * @public * @property {boolean} deleted the message has been deleted. * @readonly */ this.deleted = deleted; /** * @public * @property {boolean} modified the message has been modified. * @readonly */ this.modified = modified; /** * @public * @property {Object} rainbowCpaas the message CPaaS API hidden data. * @readonly */ this.rainbowCpaas = rainbowCpaas; this.datastoretypeOfMsg = datastoretypeOfMsg; } /** * @private * @method * @instance */ static create(serverAckTimer: any, index: any, id: string, type: any, date: Date, from: any, side: string, /* data: string ,*/ status: string, receiptStatus: number, /* fileId: string, */ /* fileName: string, */ isMarkdown: boolean, subject: string, geoloc: GeoLoc, voiceMessage: any, alternativeContent: any, attention: any, mentions : any, urgency: string, urgencyAck: boolean = false, urgencyHandler: any = null,/* translatedText: string = null, */ /* isMerged: boolean, */ historyIndex: string = null, /*showCorrectedMessages: boolean,*//* replaceMsgs: any[],*/ /* fileErrorMsg: string = null, */ attachedMsgId: string = null, attachIndex: number, attachNumber: number, /* fromJid: any, */resource: any, toJid: any, content: any, lang: any, cc: any, cctype: any, isEvent: any, event: any, oob: { url: string, mime: string, filename: string, filesize: string }, fromBubbleJid: any, fromBubbleUserJid: any, answeredMsg: Message, answeredMsgId: string, answeredMsgDate: string, answeredMsgStamp: string, /* fileTransfer: any,*/ eventJid: string, originalMessageReplaced: Message, confOwnerId: string, confOwnerDisplayName: string, confOwnerJid: string, isForwarded: boolean, forwardedMsg : any, deleted : boolean = false, modified : boolean = false, rainbowCpaas: any = null, datastoretypeOfMsg : DataStoreType = DataStoreType.StoreTwinSide) { // convert emojione from unicode to short //let message = $filter("emojiUnicodeToShort")(data); //const message = data; // return new Message(id, Message.Type.CHAT, date, from, side, message, status, null, isMarkdown, subject); // constructor(id: string, type: any, date: any, from: any, side: string, data:string , status: string, answeredMsg: Message, answeredMsgId: string, answeredMsgDate: string, answeredMsgStamp: string, fileId?, isMarkdown? : boolean , subject?, attention1 = false, additionalContent: any = null, fileName: string = null, geoloc: GeoLoc = null, alternativeContent: any = null) { return Message.MessageFactory()({ serverAckTimer, index, id, type, date, from, side, /* data: string ,*/ status, receiptStatus, /* fileId: string, */ /* fileName: string, */ isMarkdown, subject, geoloc, voiceMessage, alternativeContent, attention, mentions, urgency, urgencyAck, urgencyHandler, // translatedText, //isMerged, historyIndex, //showCorrectedMessages, //replaceMsgs, /* fileErrorMsg: string = null, */ attachedMsgId, attachIndex, attachNumber, /* fromJid: any, */ resource, toJid, content, lang, cc, cctype, isEvent, event, oob, fromBubbleJid, fromBubbleUserJid, answeredMsg, answeredMsgId, answeredMsgDate, answeredMsgStamp, /* fileTransfer: any,*/ eventJid, originalMessageReplaced, confOwnerId, confOwnerDisplayName, confOwnerJid, isForwarded, forwardedMsg, deleted, modified, rainbowCpaas, datastoretypeOfMsg }); } /** * @private * @method * @instance */ static createFileSharingMessage(id, date, from, side, data, status, fileId) { // convert emojione from unicode to short let message = data; //return new Message(id, Message.Type.FS, date, from, side, message, status, fileId); return Message.MessageFactory()({id, type: Message.Type.FS, date, from, side, data: message, status, fileId}); } /** * @private * @method * @instance */ static createWebRTCMessage(id, date, from, side, data, status) { //return new Message(id, Message.Type.WEBRTC, date, from, side, data, status); return Message.MessageFactory()({id, type: Message.Type.WEBRTC, date, from, side, data, status}); } /** * @private * @method * @instance */ static createFTMessage(id, date, from, side, data, status, fileTransfer) { //let message = new Message(id, Message.Type.FT, date, from, side, data, status); let message = Message.MessageFactory()({id, type: Message.Type.FT, date, from, side, data, status}); message.fileTransfer = fileTransfer; return message; } /** * @private * @method * @instance */ static createBubbleAdminMessage(id, date, from, type, body, subject) { let event = type; let isEvent = isDefined(event)?true:false; let side = Message.Side.ADMIN; //let message = Message.create(id, date, from, side, data, false); let message = Message.MessageFactory()({id, date, from, side, event, status: false, content:body, subject, isEvent}); return message; } /** * @private * @method * @instance */ static createRecordingAdminMessage(id, date, from, type, cmd) { let data = type + "Recording"; if (cmd) { data = data + cmd; } let side = Message.Side.ADMIN; //let message = new Message(id, Message.Type.RECORDING, date, from, side, data, false); let message = Message.MessageFactory()({ id, type: Message.Type.RECORDING, date, from, side, data, status: false }); return message; } /** * Method extract fileId part of URL * * @private * @param {string} url * @returns {string} * * @memberof Conversation */ static extractFileIdFromUrl(url) { let parts = url.split("/"); let fileDescriptorId = parts.pop() || parts.pop(); return fileDescriptorId; } updateMessage(data) { let that = this; if (data) { let messageproperties = Object.getOwnPropertyNames(that); //console.log("updateBubble update Bubble with : ", data["id"]); Object.getOwnPropertyNames(data).forEach( (val, idx, array) => { //console.log(val + " -> " + data[val]); if (messageproperties.find((el) => { return val == el; })) { //console.log("WARNING : One property of the parameter of MessageFactory method is not present in the Message class : ", val, " -> ", data[val]); that[val] = data[val]; } else { // dev-code-console // //console.log("WARNING : One property of the parameter of MessageFactory method is not present in the Message class can not update Message with : ", val, " -> ", data[val]); console.log("WARNING : One property of the parameter of Message::updateMessage method is not present in the Message class can not update Message with : ", val); // end-dev-code-console // } }); } return this; } /** * @function * @public * @name MessageFactory * @description * This class is used to create a message from data object */ public static MessageFactory() { //constructor(id, type, date, from, side, data, status, fileId?, isMarkdown?, subject?) { return (data: any): Message => { let geoloc = data.geoloc ? GeoLoc.create(data.geoloc.datum, data.geoloc.latitude, data.geoloc.longitude, data.geoloc.altitude) : null; let message = new Message( null, null, data.id, data.type, data.date, data.from, data.side, data.status, Message.ReceiptStatus.NONE, data.isMarkdown, data.subject, geoloc, data.voiceMessage, data.alternativeContent, data.attention, data.mentions, data.urgency, data.urgencyAck, data.urgencyHandler, //data.translatedText, //data.isMerged, data.historyIndex, //data.showCorrectedMessages, //data.replaceMsgs, data.attachedMsgId, data.attachIndex, data.attachNumber, // fromJid: any, data.resource, data.toJid, data.content, data.lang, data.cc, data.cctype, data.isEvent, data.event, data.oob, data.fromBubbleJid, data.fromBubbleUserJid, data.answeredMsg, data.answeredMsgId, data.answeredMsgDate, data.answeredMsgStamp, // fileTransfer: any, data.eventJid, data.originalMessageReplaced, data.confOwnerId, data.confOwnerDisplayName, data.confOwnerJid, data.isForwarded, data.forwardedMsg, data.deleted, data.modified, data.rainbowCpaas, data.datastoretypeOfMsg ); if (data) { let messageproperties = Object.getOwnPropertyNames(message); Object.getOwnPropertyNames(data).forEach( (val, idx, array) => { //console.log(val + " -> " + data[val]); if (!messageproperties.find((el) => { return val == el; })) { //console.log("WARNING : One property of the parameter of MessageFactory method is not present in the Bubble class : ", val, " -> ", data[val]); // from become fromJid and data become content if (val != "from" && val != "data") { // dev-code-console // console.log("WARNING : One property of the parameter of MessageFactory method is not present in the Message class : ", val); // end-dev-code-console // } } }); // */ /* const propertyNames = Object.getOwnPropertyNames(data); for (let idx = 0; idx < propertyNames.length; idx++) { const val = propertyNames[idx]; const propertyExists = messageproperties.find((el) => val === el); if (!propertyExists) { if (val !== "from" && val !== "data") { console.log("WARNING : One property of the parameter of MessageFactory method is not present in the Message class : ", val); } } } // */ } return message; }; } } module.exports.Message = Message; export {Message};