[NEW] Quotes on E2EE Messages
This commit is contained in:
parent
4de7c83e80
commit
eb9f973e47
|
@ -1,3 +1,5 @@
|
||||||
|
import { Clause } from '@nozbe/watermelondb/QueryDescription';
|
||||||
|
|
||||||
import database from '..';
|
import database from '..';
|
||||||
import { TAppDatabase } from '../interfaces';
|
import { TAppDatabase } from '../interfaces';
|
||||||
import { MESSAGES_TABLE } from '../model/Message';
|
import { MESSAGES_TABLE } from '../model/Message';
|
||||||
|
@ -17,3 +19,14 @@ export const getMessageById = async (messageId: string | null) => {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getMessageByQuery = async (...query: Clause[]) => {
|
||||||
|
const db = database.active;
|
||||||
|
const messageCollection = getCollection(db);
|
||||||
|
try {
|
||||||
|
const result = await messageCollection.query(...query).fetch();
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { store } from '../../store/auxStore';
|
||||||
|
import { IAttachment, IMessage } from '../../../definitions';
|
||||||
|
import { getAvatarURL } from '../../methods/helpers';
|
||||||
|
|
||||||
|
export function createQuoteAttachment(message: IMessage, messageLink: string): IAttachment {
|
||||||
|
const { server, version: serverVersion } = store.getState().server;
|
||||||
|
const externalProviderUrl = (store.getState().settings?.Accounts_AvatarExternalProviderUrl as string) || '';
|
||||||
|
|
||||||
|
return {
|
||||||
|
text: message.msg,
|
||||||
|
...('translations' in message && { translations: message?.translations }),
|
||||||
|
message_link: messageLink,
|
||||||
|
author_name: message.alias || message.u.username,
|
||||||
|
author_icon: getAvatarURL({
|
||||||
|
avatar: message.u?.username && `/avatar/${message.u?.username}`,
|
||||||
|
type: message.t,
|
||||||
|
userId: message.u?._id,
|
||||||
|
server,
|
||||||
|
serverVersion,
|
||||||
|
externalProviderUrl
|
||||||
|
}),
|
||||||
|
attachments: message.attachments || [],
|
||||||
|
ts: message.ts
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { getMessageUrlRegex } from './getMessageUrlRegex';
|
||||||
|
|
||||||
|
describe('Should regex', () => {
|
||||||
|
test('a common quote separated by space', () => {
|
||||||
|
const quote = '[ ](https://open.rocket.chat/group/room?msg=rid) test';
|
||||||
|
expect(quote.match(getMessageUrlRegex())).toStrictEqual(['https://open.rocket.chat/group/room?msg=rid']);
|
||||||
|
});
|
||||||
|
test('a quote separated by break line', () => {
|
||||||
|
const quote = '[ ](https://open.rocket.chat/group/room?msg=rid)\ntest';
|
||||||
|
expect(quote.match(getMessageUrlRegex())).toStrictEqual(['https://open.rocket.chat/group/room?msg=rid']);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,2 @@
|
||||||
|
export const getMessageUrlRegex = (): RegExp =>
|
||||||
|
/([A-Za-z]{3,9}):\/\/([-;:&=\+\$,\w]+@{1})?([-A-Za-z0-9\.]+)+:?(\d+)?((\/[-\+=!:~%\/\.@\,\w]*)?\??([-\+=&!:;%@\/\.\,\w]+)?(?:#([^\s\)]+))?)?/g;
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { IMessage } from '../../../definitions';
|
||||||
|
|
||||||
|
export const mapMessageFromApi = ({ attachments, tlm, ts, _updatedAt, ...message }: IMessage) => ({
|
||||||
|
...message,
|
||||||
|
ts: new Date(ts),
|
||||||
|
...(tlm && { tlm: new Date(tlm) }),
|
||||||
|
_updatedAt: new Date(_updatedAt),
|
||||||
|
// FIXME: webRtcCallEndTs doesn't exist in our interface IMessage, but exists on @rocket.chat/core-typings
|
||||||
|
// @ts-ignore
|
||||||
|
...(message?.webRtcCallEndTs && { webRtcCallEndTs: new Date(message.webRtcCallEndTs) }),
|
||||||
|
...(attachments && {
|
||||||
|
attachments: attachments.map(({ ts, ...attachment }) => ({
|
||||||
|
...(ts && { ts: new Date(ts) }),
|
||||||
|
...(attachment as any)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
});
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { TMessageModel } from '../../../definitions';
|
||||||
|
import { parseModelMessageToMessage } from './parseModelMessageToMessage';
|
||||||
|
|
||||||
|
export const mapMessageFromDB = (messageModel: TMessageModel) => {
|
||||||
|
const parsedMessage = parseModelMessageToMessage(messageModel);
|
||||||
|
return {
|
||||||
|
...parsedMessage,
|
||||||
|
ts: new Date(parsedMessage.ts),
|
||||||
|
...(parsedMessage.tlm && { tlm: new Date(parsedMessage.tlm) }),
|
||||||
|
_updatedAt: new Date(parsedMessage._updatedAt),
|
||||||
|
// FIXME: webRtcCallEndTs doesn't exist in our interface IMessage, but exists on @rocket.chat/core-typings
|
||||||
|
// @ts-ignore
|
||||||
|
...(parsedMessage?.webRtcCallEndTs && { webRtcCallEndTs: new Date(parsedMessage.webRtcCallEndTs) }),
|
||||||
|
...(parsedMessage.attachments && {
|
||||||
|
attachments: parsedMessage.attachments.map(({ ts, ...attachment }) => ({
|
||||||
|
...(ts && { ts: new Date(ts) }),
|
||||||
|
...(attachment as any)
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
};
|
||||||
|
};
|
|
@ -0,0 +1,42 @@
|
||||||
|
import { IMessage, TMessageModel } from '../../../definitions';
|
||||||
|
|
||||||
|
export const parseModelMessageToMessage = (messageModel: TMessageModel): IMessage =>
|
||||||
|
({
|
||||||
|
msg: messageModel.msg,
|
||||||
|
t: messageModel.t,
|
||||||
|
ts: messageModel.ts,
|
||||||
|
u: messageModel.u,
|
||||||
|
subscription: messageModel.subscription,
|
||||||
|
alias: messageModel.alias,
|
||||||
|
parseUrls: messageModel.parseUrls,
|
||||||
|
groupable: messageModel.groupable,
|
||||||
|
avatar: messageModel.avatar,
|
||||||
|
emoji: messageModel.emoji,
|
||||||
|
attachments: messageModel.attachments,
|
||||||
|
urls: messageModel.urls,
|
||||||
|
_updatedAt: messageModel._updatedAt,
|
||||||
|
status: messageModel.status,
|
||||||
|
pinned: messageModel.pinned,
|
||||||
|
starred: messageModel.starred,
|
||||||
|
editedBy: messageModel.editedBy,
|
||||||
|
reactions: messageModel.reactions,
|
||||||
|
role: messageModel.role,
|
||||||
|
drid: messageModel.drid,
|
||||||
|
dcount: messageModel.dcount,
|
||||||
|
dlm: messageModel.dlm,
|
||||||
|
tmid: messageModel.tmid,
|
||||||
|
tcount: messageModel.tcount,
|
||||||
|
tlm: messageModel.tlm,
|
||||||
|
replies: messageModel.replies,
|
||||||
|
mentions: messageModel.mentions,
|
||||||
|
channels: messageModel.channels,
|
||||||
|
unread: messageModel.unread,
|
||||||
|
autoTranslate: messageModel.autoTranslate,
|
||||||
|
translations: messageModel.translations,
|
||||||
|
tmsg: messageModel.tmsg,
|
||||||
|
blocks: messageModel.blocks,
|
||||||
|
e2e: messageModel.e2e,
|
||||||
|
tshow: messageModel.tshow,
|
||||||
|
md: messageModel.md,
|
||||||
|
comment: messageModel.comment
|
||||||
|
} as IMessage);
|
|
@ -2,7 +2,10 @@ import EJSON from 'ejson';
|
||||||
import { Base64 } from 'js-base64';
|
import { Base64 } from 'js-base64';
|
||||||
import SimpleCrypto from 'react-native-simple-crypto';
|
import SimpleCrypto from 'react-native-simple-crypto';
|
||||||
import ByteBuffer from 'bytebuffer';
|
import ByteBuffer from 'bytebuffer';
|
||||||
|
import parse from 'url-parse';
|
||||||
|
import { Q } from '@nozbe/watermelondb';
|
||||||
|
|
||||||
|
import getSingleMessage from '../methods/getSingleMessage';
|
||||||
import { IMessage, IUser } from '../../definitions';
|
import { IMessage, IUser } from '../../definitions';
|
||||||
import Deferred from './helpers/deferred';
|
import Deferred from './helpers/deferred';
|
||||||
import { debounce } from '../methods/helpers';
|
import { debounce } from '../methods/helpers';
|
||||||
|
@ -21,6 +24,11 @@ import {
|
||||||
import { Encryption } from './index';
|
import { Encryption } from './index';
|
||||||
import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../constants';
|
import { E2E_MESSAGE_TYPE, E2E_STATUS } from '../constants';
|
||||||
import { Services } from '../services';
|
import { Services } from '../services';
|
||||||
|
import { getMessageUrlRegex } from './helpers/getMessageUrlRegex';
|
||||||
|
import { mapMessageFromApi } from './helpers/mapMessageFromApi';
|
||||||
|
import { mapMessageFromDB } from './helpers/mapMessageFromDB';
|
||||||
|
import { createQuoteAttachment } from './helpers/createQuoteAttachment';
|
||||||
|
import { getMessageByQuery } from '../database/services/Message';
|
||||||
|
|
||||||
export default class EncryptionRoom {
|
export default class EncryptionRoom {
|
||||||
ready: boolean;
|
ready: boolean;
|
||||||
|
@ -268,12 +276,15 @@ export default class EncryptionRoom {
|
||||||
tmsg = await this.decryptText(tmsg);
|
tmsg = await this.decryptText(tmsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
const decryptedMessage: IMessage = {
|
||||||
...message,
|
...message,
|
||||||
tmsg,
|
tmsg,
|
||||||
msg,
|
msg,
|
||||||
e2e: E2E_STATUS.DONE
|
e2e: 'done'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const decryptedMessageWithQuote = await this.parseQuoteAttachment(decryptedMessage);
|
||||||
|
return decryptedMessageWithQuote;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
|
@ -281,4 +292,35 @@ export default class EncryptionRoom {
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async parseQuoteAttachment(message: IMessage) {
|
||||||
|
const urls = message.msg?.match(getMessageUrlRegex()) || [];
|
||||||
|
await Promise.all(
|
||||||
|
urls.map(async (url: string) => {
|
||||||
|
const parsedUrl = parse(url, true);
|
||||||
|
const messageId = parsedUrl.query?.msg;
|
||||||
|
if (!messageId || Array.isArray(messageId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const messageFromDB = await getMessageByQuery(Q.and(Q.where('id', messageId), Q.where('e2e', E2E_STATUS.DONE)));
|
||||||
|
if (messageFromDB?.length) {
|
||||||
|
const decryptedQuoteMessage = mapMessageFromDB(messageFromDB[0]);
|
||||||
|
message.attachments = message.attachments || [];
|
||||||
|
const quoteAttachment = createQuoteAttachment(decryptedQuoteMessage, url);
|
||||||
|
return message.attachments.push(quoteAttachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
const quotedMessageObject = await getSingleMessage(messageId);
|
||||||
|
if (!quotedMessageObject) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const decryptedQuoteMessage = await this.decrypt(mapMessageFromApi(quotedMessageObject));
|
||||||
|
message.attachments = message.attachments || [];
|
||||||
|
const quoteAttachment = createQuoteAttachment(decryptedQuoteMessage, url);
|
||||||
|
return message.attachments.push(quoteAttachment);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue