From 595d16af86ab74a93f5bbd11e490d2368cc2467f Mon Sep 17 00:00:00 2001 From: Phillip Date: Sat, 29 Apr 2023 05:47:30 +0300 Subject: [PATCH] switch receiving chat message to base64 and added qortal:// protocol to be recognized as links in the chat --- qortal-ui-crypto/api.js | 5 +- qortal-ui-crypto/api/deps/Base64.js | 24 +++ .../transactions/chat/decryptChatMessage.js | 31 ++++ .../plugins/core/components/ChatPage.js | 56 +++--- .../plugins/core/components/ChatScroller.js | 174 ++++++++++++++++-- .../plugins/core/qdn/browser/browser.src.js | 9 +- .../plugins/utils/replace-messages-edited.js | 8 +- 7 files changed, 253 insertions(+), 54 deletions(-) create mode 100644 qortal-ui-crypto/api/deps/Base64.js diff --git a/qortal-ui-crypto/api.js b/qortal-ui-crypto/api.js index cc81dc9e..543d875d 100644 --- a/qortal-ui-crypto/api.js +++ b/qortal-ui-crypto/api.js @@ -1,16 +1,19 @@ import { Sha256 } from 'asmcrypto.js' import Base58 from './api/deps/Base58' +import Base64 from './api/deps/Base64' import { base58PublicKeyToAddress } from './api/wallet/base58PublicKeyToAddress' import { validateAddress } from './api/wallet/validateAddress' -import { decryptChatMessage } from './api/transactions/chat/decryptChatMessage' +import { decryptChatMessage, decryptChatMessageBase64 } from './api/transactions/chat/decryptChatMessage' import _ from 'lodash' window.Sha256 = Sha256 window.Base58 = Base58 +window.Base64 = Base64 window._ = _ window.base58PublicKeyToAddress = base58PublicKeyToAddress window.validateAddress = validateAddress window.decryptChatMessage = decryptChatMessage +window.decryptChatMessageBase64 = decryptChatMessageBase64 export { initApi, store } from './api_deps.js' export * from './api/deps/deps.js' diff --git a/qortal-ui-crypto/api/deps/Base64.js b/qortal-ui-crypto/api/deps/Base64.js new file mode 100644 index 00000000..82bc5847 --- /dev/null +++ b/qortal-ui-crypto/api/deps/Base64.js @@ -0,0 +1,24 @@ + +const Base64 = {}; + + + +Base64.decode = function (string) { + + const binaryString = atob(string); + const binaryLength = binaryString.length; + const bytes = new Uint8Array(binaryLength); + + for (let i = 0; i < binaryLength; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + + const decoder = new TextDecoder(); + const decodedString = decoder.decode(bytes); + return decodedString; +}; + + + + +export default Base64; \ No newline at end of file diff --git a/qortal-ui-crypto/api/transactions/chat/decryptChatMessage.js b/qortal-ui-crypto/api/transactions/chat/decryptChatMessage.js index 038db155..e6f8bcd1 100644 --- a/qortal-ui-crypto/api/transactions/chat/decryptChatMessage.js +++ b/qortal-ui-crypto/api/transactions/chat/decryptChatMessage.js @@ -24,3 +24,34 @@ export const decryptChatMessage = (encryptedMessage, privateKey, recipientPublic _decryptedMessage === false ? decryptedMessage : decryptedMessage = new TextDecoder('utf-8').decode(_decryptedMessage) return decryptedMessage } + +export const decryptChatMessageBase64 = (encryptedMessage, privateKey, recipientPublicKey, lastReference) => { + let _encryptedMessage = atob(encryptedMessage); + const binaryLength = _encryptedMessage.length; + const bytes = new Uint8Array(binaryLength); + + for (let i = 0; i < binaryLength; i++) { + bytes[i] = _encryptedMessage.charCodeAt(i); + } + + const _base58RecipientPublicKey = recipientPublicKey instanceof Uint8Array ? Base58.encode(recipientPublicKey) : recipientPublicKey + const _recipientPublicKey = Base58.decode(_base58RecipientPublicKey) + + const _lastReference = lastReference instanceof Uint8Array ? lastReference : Base58.decode(lastReference) + + const convertedPrivateKey = ed2curve.convertSecretKey(privateKey) + const convertedPublicKey = ed2curve.convertPublicKey(_recipientPublicKey) + const sharedSecret = new Uint8Array(32); + nacl.lowlevel.crypto_scalarmult(sharedSecret, convertedPrivateKey, convertedPublicKey) + + const _chatEncryptionSeed = new Sha256().process(sharedSecret).finish().result + const _decryptedMessage = nacl.secretbox.open(bytes, _lastReference.slice(0, 24), _chatEncryptionSeed) + + + if (_decryptedMessage === false) { + return _decryptedMessage + } + return new TextDecoder('utf-8').decode(_decryptedMessage) + + +} \ No newline at end of file diff --git a/qortal-ui-plugins/plugins/core/components/ChatPage.js b/qortal-ui-plugins/plugins/core/components/ChatPage.js index 3e405f4f..b9da91f2 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatPage.js +++ b/qortal-ui-plugins/plugins/core/components/ChatPage.js @@ -2514,7 +2514,7 @@ class ChatPage extends LitElement { if (this.isReceipient) { const getInitialMessages = await parentEpml.request('apiCall', { type: 'api', - url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=${limit}&reverse=true&before=${before}&after=${after}&haschatreference=false`, + url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=${limit}&reverse=true&before=${before}&after=${after}&haschatreference=false&encoding=BASE64`, }); const decodeMsgs = getInitialMessages.map((eachMessage) => { @@ -2548,7 +2548,7 @@ class ChatPage extends LitElement { } else { const getInitialMessages = await parentEpml.request('apiCall', { type: 'api', - url: `/chat/messages?txGroupId=${Number(this._chatId)}&limit=${limit}&reverse=true&before=${before}&after=${after}&haschatreference=false`, + url: `/chat/messages?txGroupId=${Number(this._chatId)}&limit=${limit}&reverse=true&before=${before}&after=${after}&haschatreference=false&encoding=BASE64`, }); @@ -2589,7 +2589,7 @@ class ChatPage extends LitElement { if (this.isReceipient) { const getInitialMessages = await parentEpml.request('apiCall', { type: 'api', - url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=20&reverse=true&before=${scrollElement.messageObj.timestamp}&haschatreference=false`, + url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=20&reverse=true&before=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64`, }); const decodeMsgs = getInitialMessages.map((eachMessage) => { @@ -2622,7 +2622,7 @@ class ChatPage extends LitElement { } else { const getInitialMessages = await parentEpml.request('apiCall', { type: 'api', - url: `/chat/messages?txGroupId=${Number(this._chatId)}&limit=20&reverse=true&before=${scrollElement.messageObj.timestamp}&haschatreference=false`, + url: `/chat/messages?txGroupId=${Number(this._chatId)}&limit=20&reverse=true&before=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64`, }); @@ -2660,7 +2660,7 @@ class ChatPage extends LitElement { if (this.isReceipient) { const getInitialMessages = await parentEpml.request('apiCall', { type: 'api', - url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=20&reverse=true&afer=${scrollElement.messageObj.timestamp}&haschatreference=false`, + url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${this._chatId}&limit=20&reverse=true&afer=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64`, }); const decodeMsgs = getInitialMessages.map((eachMessage) => { @@ -2693,7 +2693,7 @@ class ChatPage extends LitElement { } else { const getInitialMessages = await parentEpml.request('apiCall', { type: 'api', - url: `/chat/messages?txGroupId=${Number(this._chatId)}&limit=20&reverse=true&after=${scrollElement.messageObj.timestamp}&haschatreference=false`, + url: `/chat/messages?txGroupId=${Number(this._chatId)}&limit=20&reverse=true&after=${scrollElement.messageObj.timestamp}&haschatreference=false&encoding=BASE64`, }); @@ -2739,8 +2739,7 @@ class ChatPage extends LitElement { let _eachMessage = this.decodeMessage(eachMessage) return _eachMessage } - }) - + }) if (isInitial) { this.chatEditorPlaceholder = await this.renderPlaceholder(); const replacedMessages = await replaceMessagesEdited({ @@ -2759,6 +2758,7 @@ class ChatPage extends LitElement { // TODO: Determine number of initial messages by screen height... this.messagesRendered = this._messages; this.isLoadingMessages = false; + setTimeout(() => this.downElementObserver(), 500); } else { const replacedMessages = await replaceMessagesEdited({ @@ -2885,11 +2885,10 @@ class ChatPage extends LitElement { if (isReceipientVar === true) { // direct chat if (encodedMessageObj.isEncrypted === true && _publicKeyVar.hasPubKey === true && encodedMessageObj.data) { - let decodedMessage = window.parent.decryptChatMessage(encodedMessageObj.data, window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey, _publicKeyVar.key, encodedMessageObj.reference); + let decodedMessage = window.parent.decryptChatMessageBase64(encodedMessageObj.data, window.parent.reduxStore.getState().app.selectedAddress.keyPair.privateKey, _publicKeyVar.key, encodedMessageObj.reference); decodedMessageObj = { ...encodedMessageObj, decodedMessage }; } else if (encodedMessageObj.isEncrypted === false && encodedMessageObj.data) { - let bytesArray = window.parent.Base58.decode(encodedMessageObj.data); - let decodedMessage = new TextDecoder('utf-8').decode(bytesArray); + let decodedMessage = window.parent.Base64.decode(encodedMessageObj.data); decodedMessageObj = { ...encodedMessageObj, decodedMessage }; } else { decodedMessageObj = { ...encodedMessageObj, decodedMessage: "Cannot Decrypt Message!" }; @@ -2897,8 +2896,7 @@ class ChatPage extends LitElement { } else { // group chat - let bytesArray = window.parent.Base58.decode(encodedMessageObj.data); - let decodedMessage = new TextDecoder('utf-8').decode(bytesArray); + let decodedMessage = window.parent.Base64.decode(encodedMessageObj.data); decodedMessageObj = { ...encodedMessageObj, decodedMessage }; } @@ -2919,11 +2917,11 @@ class ChatPage extends LitElement { if (window.parent.location.protocol === "https:") { - directSocketLink = `wss://${nodeUrl}/websockets/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}`; + directSocketLink = `wss://${nodeUrl}/websockets/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&encoding=BASE64`; } else { // Fallback to http - directSocketLink = `ws://${nodeUrl}/websockets/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}`; + directSocketLink = `ws://${nodeUrl}/websockets/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&encoding=BASE64`; } this.webSocket = new WebSocket(directSocketLink); @@ -2942,13 +2940,13 @@ class ChatPage extends LitElement { const lastMessage = cachedData[cachedData.length - 1] const newMessages = await parentEpml.request('apiCall', { type: 'api', - url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&limit=20&reverse=true&after=${lastMessage.timestamp}&haschatreference=false`, + url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&limit=20&reverse=true&after=${lastMessage.timestamp}&haschatreference=false&encoding=BASE64`, }); getInitialMessages = [...cachedData, ...newMessages].slice(-20) } else { getInitialMessages = await parentEpml.request('apiCall', { type: 'api', - url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&limit=20&reverse=true&haschatreference=false`, + url: `/chat/messages?involving=${window.parent.reduxStore.getState().app.selectedAddress.address}&involving=${cid}&limit=20&reverse=true&haschatreference=false&encoding=BASE64`, }); @@ -2959,9 +2957,14 @@ class ChatPage extends LitElement { initial = initial + 1 } else { + try { if(e.data){ this.processMessages(JSON.parse(e.data), false) } + } catch (error) { + + } + } } @@ -3009,11 +3012,11 @@ class ChatPage extends LitElement { if (window.parent.location.protocol === "https:") { - groupSocketLink = `wss://${nodeUrl}/websockets/chat/messages?txGroupId=${groupId}`; + groupSocketLink = `wss://${nodeUrl}/websockets/chat/messages?txGroupId=${groupId}&encoding=BASE64`; } else { // Fallback to http - groupSocketLink = `ws://${nodeUrl}/websockets/chat/messages?txGroupId=${groupId}`; + groupSocketLink = `ws://${nodeUrl}/websockets/chat/messages?txGroupId=${groupId}&encoding=BASE64`; } this.webSocket = new WebSocket(groupSocketLink); @@ -3037,17 +3040,15 @@ class ChatPage extends LitElement { const newMessages = await parentEpml.request('apiCall', { type: 'api', - url: `/chat/messages?txGroupId=${groupId}&limit=20&reverse=true&after=${lastMessage.timestamp}&haschatreference=false`, + url: `/chat/messages?txGroupId=${groupId}&limit=20&reverse=true&after=${lastMessage.timestamp}&haschatreference=false&encoding=BASE64`, }); - getInitialMessages = [...cachedData, ...newMessages].slice(-20) } else { getInitialMessages = await parentEpml.request('apiCall', { type: 'api', - url: `/chat/messages?txGroupId=${groupId}&limit=20&reverse=true&haschatreference=false`, + url: `/chat/messages?txGroupId=${groupId}&limit=20&reverse=true&haschatreference=false&encoding=BASE64`, }); - } @@ -3055,9 +3056,14 @@ class ChatPage extends LitElement { initial = initial + 1 } else { - if(e.data){ - this.processMessages(JSON.parse(e.data), false) + try { + if (e.data) { + this.processMessages(JSON.parse(e.data), false) + } + } catch (error) { + } + } } diff --git a/qortal-ui-plugins/plugins/core/components/ChatScroller.js b/qortal-ui-plugins/plugins/core/components/ChatScroller.js index 382a8114..598ea10c 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatScroller.js +++ b/qortal-ui-plugins/plugins/core/components/ChatScroller.js @@ -28,6 +28,112 @@ import Highlight from '@tiptap/extension-highlight' const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) let toggledMessage = {} + +const getApiKey = () => { + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; + let apiKey = myNode.apiKey; + return apiKey; +} + +const extractComponents = async (url) => { + if (!url.startsWith("qortal://")) { + return null; + } + + url = url.replace(/^(qortal\:\/\/)/, ""); + if (url.includes("/")) { + let parts = url.split("/"); + const service = parts[0].toUpperCase(); + parts.shift(); + const name = parts[0]; + parts.shift(); + let identifier; + + if (parts.length > 0) { + identifier = parts[0]; // Do not shift yet + // Check if a resource exists with this service, name and identifier combination + let responseObj = await parentEpml.request('apiCall', { + url: `/arbitrary/resource/status/${service}/${name}/${identifier}?apiKey=${getApiKey()}` + }) + + if (responseObj.totalChunkCount > 0) { + // Identifier exists, so don't include it in the path + parts.shift(); + } + else { + identifier = null; + } + } + + const path = parts.join("/"); + + const components = {}; + components["service"] = service; + components["name"] = name; + components["identifier"] = identifier; + components["path"] = path; + return components; + } + + return null; +} + +function processText(input) { + const linkRegex = /(qortal:\/\/\S+)/g; + + function processNode(node) { + if (node.nodeType === Node.TEXT_NODE) { + const parts = node.textContent.split(linkRegex); + + if (parts.length > 1) { + const fragment = document.createDocumentFragment(); + + parts.forEach((part) => { + if (part.startsWith('qortal://')) { + const link = document.createElement('span'); + // Store the URL in a data attribute + link.setAttribute('data-url', part); + link.textContent = part; + link.style.color = 'var(--nav-text-color)'; + link.style.textDecoration = 'underline'; + link.style.cursor = 'pointer' + + link.addEventListener('click', async (e) => { + e.preventDefault(); + try { + const res = await extractComponents(part) + if (!res) return + const { service, name, identifier, path } = res + window.location = `../../qdn/browser/index.html?service=${service}&name=${name}&identifier=${identifier}&path=${path}` + } catch (error) { + console.log({ error }) + } + + }); + + fragment.appendChild(link); + } else { + const textNode = document.createTextNode(part); + fragment.appendChild(textNode); + } + }); + + node.replaceWith(fragment); + } + } else { + for (const childNode of Array.from(node.childNodes)) { + processNode(childNode); + } + } + } + + const wrapper = document.createElement('div'); + wrapper.innerHTML = input; + + processNode(wrapper); + + return wrapper +} class ChatScroller extends LitElement { static get properties() { return { @@ -61,7 +167,9 @@ class ChatScroller extends LitElement { } } - static styles = [chatStyles] + static get styles() { + return [chatStyles]; + } constructor() { super() @@ -103,7 +211,10 @@ class ChatScroller extends LitElement { const isSameGroup = Math.abs(timestamp - message.timestamp) < 600000 && sender === message.sender && !repliedToData; if (isSameGroup) { - messageArray[messageArray.length - 1].messages = [...(messageArray[messageArray.length - 1]?.messages || []), message]; + messageArray[messageArray.length - 1].messages = [ + ...(messageArray[messageArray.length - 1].messages || []), + message + ]; } else { messageArray.push({ messages: [message], @@ -324,7 +435,10 @@ class MessageTemplate extends LitElement { this.viewImage = false } - static styles = [chatStyles] + static get styles() { + return [chatStyles]; + } + // Open & Close Private Message Chat Modal showPrivateMessageModal() { @@ -369,7 +483,7 @@ class MessageTemplate extends LitElement { } } firstUpdated(){ - const autoSeeChatList = window.parent.reduxStore.getState().app?.autoLoadImageChats + const autoSeeChatList = window.parent.reduxStore.getState().app.autoLoadImageChats if(autoSeeChatList.includes(this.chatId) || this.listSeenMessages.includes(this.messageObj.signature)){ this.viewImage = true } @@ -386,6 +500,7 @@ class MessageTemplate extends LitElement { const hidemsg = this.hideMessages; let message = ""; let messageVersion2 = "" + let messageVersion2WithLink = null let reactions = []; let repliedToData = null; let image = null; @@ -405,6 +520,8 @@ class MessageTemplate extends LitElement { Highlight // other extensions … ]) + messageVersion2WithLink = processText(messageVersion2) + } message = parsedMessageObj.messageText; repliedToData = this.messageObj.repliedToData; @@ -424,7 +541,6 @@ class MessageTemplate extends LitElement { gif = parsedMessageObj.gifs[0]; } } catch (error) { - console.error(error); message = this.messageObj.decodedMessage; } let avatarImg = ''; @@ -550,8 +666,28 @@ class MessageTemplate extends LitElement { } } - const escapedMessage = this.escapeHTML(message) - const replacedMessage = escapedMessage.replace(new RegExp('\r?\n','g'), '
'); + let repliedToMessageText = '' + if (repliedToData && repliedToData.decodedMessage && repliedToData.decodedMessage.messageText) { + try { + repliedToMessageText = generateHTML(repliedToData.decodedMessage.messageText, [ + StarterKit, + Underline, + Highlight + // other extensions … + ]) + } catch (error) { + + } + } + let replacedMessage = '' + if (message && +version < 2) { + const escapedMessage = this.escapeHTML(message) + if (escapedMessage) { + replacedMessage = escapedMessage.replace(new RegExp('\r?\n', 'g'), '
'); + } + + } + return hideit ? html`
  • ` : html`
  • - ${repliedToData.senderName ?? cropAddress(repliedToData.sender)} + ${repliedToData.senderName ? cropAddress(repliedToData.sender) : ''}

    - ${version.toString() === '1' ? html` + ${version && version.toString() === '1' ? html` ${repliedToData.decodedMessage.messageText} ` : ''} - ${+version > 1 ? html` - ${unsafeHTML(generateHTML(repliedToData.decodedMessage.messageText, [ - StarterKit, - Underline, - Highlight - // other extensions … - ]))} + ${+version > 1 && repliedToMessageText ? html` + ${unsafeHTML(repliedToMessageText)} ` : ''}

    @@ -764,13 +895,14 @@ class MessageTemplate extends LitElement { id="messageContent" class="message" style=${(image && replacedMessage !== "") &&"margin-top: 15px;"}> - ${+version > 1 ? html` + ${+version > 1 ? messageVersion2WithLink ? html`${messageVersion2WithLink}` : html` ${unsafeHTML(messageVersion2)} ` : ''} - ${version.toString() === '1' ? html` + + ${version && version.toString() === '1' ? html` ${unsafeHTML(this.emojiPicker.parse(replacedMessage))} ` : ''} - ${version.toString() === '0' ? html` + ${version && version.toString() === '0' ? html` ${unsafeHTML(this.emojiPicker.parse(replacedMessage))} ` : ''}
    {}; } - static styles = [chatStyles] + static get styles() { + return [chatStyles]; + } // Copy address to clipboard async copyToClipboard(text) { diff --git a/qortal-ui-plugins/plugins/core/qdn/browser/browser.src.js b/qortal-ui-plugins/plugins/core/qdn/browser/browser.src.js index 455cc1ac..003b2a76 100644 --- a/qortal-ui-plugins/plugins/core/qdn/browser/browser.src.js +++ b/qortal-ui-plugins/plugins/core/qdn/browser/browser.src.js @@ -146,15 +146,15 @@ class WebBrowser extends LitElement { // Build initial display URL let displayUrl = 'qortal://' + this.service + '/' + this.name; if ( - this.identifier != null && - data.identifier != '' && + this.identifier && this.identifier != 'null' && this.identifier != 'default' ) + { displayUrl = displayUrl.concat('/' + this.identifier); + } if (this.path != null && this.path != '/') displayUrl = displayUrl.concat(this.path); this.displayUrl = displayUrl; - const getFollowedNames = async () => { let followedNames = await parentEpml.request('apiCall', { url: `/lists/followedNames?apiKey=${this.getApiKey()}`, @@ -193,8 +193,9 @@ class WebBrowser extends LitElement { } else { // Normal mode + this.url = `${nodeUrl}/render/${this.service}/${this.name}${this.path != null ? this.path : '' - }?theme=${this.theme}&identifier=${this.identifier != null ? this.identifier : '' + }?theme=${this.theme}&identifier=${(this.identifier != null && this.identifier != 'null') ? this.identifier : '' }` } } diff --git a/qortal-ui-plugins/plugins/utils/replace-messages-edited.js b/qortal-ui-plugins/plugins/utils/replace-messages-edited.js index a7e459a7..ad815fe8 100644 --- a/qortal-ui-plugins/plugins/utils/replace-messages-edited.js +++ b/qortal-ui-plugins/plugins/utils/replace-messages-edited.js @@ -14,7 +14,7 @@ export const replaceMessagesEdited = async ({ } const response = await parentEpml.request("apiCall", { type: "api", - url: `/chat/messages?chatreference=${msg.signature}&reverse=true${msgQuery}&limit=1&sender=${msg.sender}`, + url: `/chat/messages?chatreference=${msg.signature}&reverse=true${msgQuery}&limit=1&sender=${msg.sender}&encoding=BASE64`, }) if (response && Array.isArray(response) && response.length !== 0) { @@ -54,13 +54,13 @@ export const replaceMessagesEdited = async ({ if(+parsedMessageObj.version > 2){ originalReply = await parentEpml.request("apiCall", { type: "api", - url: `/chat/message/${parsedMessageObj.repliedTo}`, + url: `/chat/message/${parsedMessageObj.repliedTo}?encoding=BASE64`, }) } if(+parsedMessageObj.version < 3){ originalReply = await parentEpml.request("apiCall", { type: "api", - url: `/chat/messages?reference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}`, + url: `/chat/messages?reference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}&encoding=BASE64`, }) } @@ -71,7 +71,7 @@ export const replaceMessagesEdited = async ({ const response = await parentEpml.request("apiCall", { type: "api", - url: `/chat/messages?chatreference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}&limit=1&sender=${originalReplyMessage.sender}`, + url: `/chat/messages?chatreference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}&limit=1&sender=${originalReplyMessage.sender}&encoding=BASE64`, }) if (