From 3456162402c1dfc2c5b3e742ad6ca761094e5191 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Thu, 27 May 2021 14:35:30 +1000 Subject: [PATCH] Moved opengroupv1 test to opengroupv2 tests --- build/afterPackHook.js | 9 ++ js/background.js | 14 --- package.json | 20 +++-- .../conversation/InviteContactsDialog.tsx | 51 ++++------- .../conversation/ModeratorsAddDialog.tsx | 18 ++-- .../conversation/ModeratorsRemoveDialog.tsx | 39 +++----- ts/interactions/conversation.ts | 44 ++------- ts/interactions/message.ts | 78 ++++------------ ts/models/conversation.ts | 32 +------ ts/models/message.ts | 38 ++++---- ts/opengroup/opengroupV2/ApiUtil.ts | 18 ++-- ts/opengroup/opengroupV2/OpenGroupAPIV2.ts | 40 +++++---- ts/opengroup/utils/OpenGroupUtils.ts | 22 ----- ts/session/group/index.ts | 4 +- .../messages/outgoing/OpenGroupMessage.ts | 39 -------- ts/session/messages/outgoing/index.ts | 3 +- ts/session/sending/MessageQueue.ts | 33 +------ ts/session/sending/MessageSender.ts | 39 +------- ts/session/sending/MessageSentHandler.ts | 16 ++-- ts/session/utils/Attachments.ts | 25 ++---- ts/session/utils/syncUtils.ts | 7 +- .../unit/messages/OpenGroupMessage_test.ts | 89 ------------------- .../session/unit/sending/MessageQueue_test.ts | 39 ++++---- .../unit/sending/MessageSender_test.ts | 70 ++++++++++----- ts/test/test-utils/utils/message.ts | 29 +++--- 25 files changed, 229 insertions(+), 587 deletions(-) delete mode 100644 ts/session/messages/outgoing/OpenGroupMessage.ts delete mode 100644 ts/test/session/unit/messages/OpenGroupMessage_test.ts diff --git a/build/afterPackHook.js b/build/afterPackHook.js index 37d19d316..b8a01ae67 100644 --- a/build/afterPackHook.js +++ b/build/afterPackHook.js @@ -11,7 +11,16 @@ module.exports = async function(context) { return; } const isAppImage = context.targets.find(target => target.name === 'appImage'); + console.log( + 'targets', + context.targets.map(target => target.name) + ); + + console.log('AppImage', isAppImage.options); + if (!isAppImage) { + console.log('afterPack hook not triggered', context); + return; } // eslint-disable-next-line no-console diff --git a/js/background.js b/js/background.js index 00c0bb2b4..f27fe2204 100644 --- a/js/background.js +++ b/js/background.js @@ -100,19 +100,6 @@ window.log.info('Storage fetch'); storage.fetch(); - let specialConvInited = false; - const initSpecialConversations = async () => { - if (specialConvInited) { - return; - } - const publicConversations = await window.Signal.Data.getAllOpenGroupV1Conversations(); - publicConversations.forEach(conversation => { - // weird but create the object and does everything we need - conversation.getPublicSendData(); - }); - specialConvInited = true; - }; - const initAPIs = () => { if (window.initialisedAPI) { return; @@ -743,7 +730,6 @@ window.NewReceiver.queueAllCached(); initAPIs(); - await initSpecialConversations(); messageReceiver = new textsecure.MessageReceiver(); // those handleMessageEvent calls are only used by opengroupv1 messageReceiver.addEventListener('message', window.DataMessageReceiver.handleMessageEvent); diff --git a/package.json b/package.json index 3c1b86e11..6a7bdd1a8 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "session-desktop", "productName": "Session", "description": "Private messaging from your desktop", - "version": "1.6.4", + "version": "1.6.5", "license": "GPL-3.0", "author": { "name": "Loki Project", @@ -14,7 +14,9 @@ }, "main": "main.js", "scripts": { - "postinstall": "electron-builder install-app-deps && rimraf node_modules/dtrace-provider && husky install", + "prepare": "patch-package", + "postinstall": "yarn custom-react-mentions-build && electron-builder install-app-deps && rimraf node_modules/dtrace-provider && husky install", + "custom-react-mentions-build": "cd node_modules/react-mentions && yarn install --node_modules=../node_modules && yarn build && rimraf node_modules/react node_modules/react-dom && cd ..", "start": "cross-env NODE_APP_INSTANCE=$MULTI electron .", "start-prod": "cross-env NODE_ENV=production NODE_APP_INSTANCE=devprod$MULTI electron .", "start-swarm-test": "cross-env NODE_ENV=swarm-testing NODE_APP_INSTANCE=$MULTI electron .", @@ -33,7 +35,6 @@ "test-electron": "yarn grunt test", "test-integration": "ELECTRON_DISABLE_SANDBOX=1 mocha --exit --full-trace --timeout 10000 ts/test/session/integration/integration_itest.js", "test-node": "mocha --recursive --exit --timeout 10000 test/app test/modules \"./ts/test/**/*_test.js\" ", - "test-audric": "mocha --recursive --exit --timeout 500 test/app test/modules \"./ts/test/session/unit/onion/*.js\" ", "eslint-full": "eslint . --cache", "lint-full": "yarn format-full && yarn lint-files-full", "lint-files-full": "yarn eslint-full && yarn tslint", @@ -41,7 +42,7 @@ "format-full": "prettier --list-different --write \"*.{css,js,json,scss,ts,tsx}\" \"./**/*.{css,js,json,scss,ts,tsx}\"", "transpile": "tsc --incremental", "transpile:watch": "tsc -w", - "clean-transpile": "rimraf ts/**/*.js ts/*.js ts/*.js.map ts/**/*.js.map && rimraf tsconfig.tsbuildinfo;", + "clean-transpile": "rimraf ts/**/*.js && rimraf ts/*.js && rimraf tsconfig.tsbuildinfo;", "ready": "yarn clean-transpile; yarn grunt && yarn lint-full && yarn test" }, "dependencies": { @@ -76,7 +77,7 @@ "jquery": "3.3.1", "jsbn": "1.1.0", "libsodium-wrappers": "^0.7.8", - "linkify-it": "3.0.2", + "linkify-it": "2.0.3", "lodash": "4.17.11", "long": "^4.0.0", "mic-recorder-to-mp3": "^2.2.2", @@ -97,7 +98,7 @@ "react-emoji-render": "^1.2.4", "react-h5-audio-player": "^3.2.0", "react-intersection-observer": "^8.30.3", - "react-mentions": "^4.2.0", + "react-mentions": "https://github.com/Bilb/react-mentions", "react-portal": "^4.2.0", "react-qr-svg": "^2.2.1", "react-redux": "7.2.1", @@ -125,7 +126,7 @@ "@types/blueimp-load-image": "^2.23.8", "@types/buffer-crc32": "^0.2.0", "@types/bytebuffer": "^5.0.41", - "@types/chai": "^4.2.18", + "@types/chai": "4.1.2", "@types/chai-as-promised": "^7.1.2", "@types/classnames": "2.2.3", "@types/color": "^3.0.0", @@ -148,7 +149,7 @@ "@types/rc-slider": "^8.6.5", "@types/react": "16.8.5", "@types/react-dom": "16.8.2", - "@types/react-mentions": "^4.1.1", + "@types/react-mentions": "^3.3.1", "@types/react-mic": "^12.4.1", "@types/react-portal": "^4.0.2", "@types/react-redux": "7.1.9", @@ -163,7 +164,7 @@ "arraybuffer-loader": "1.0.3", "asar": "0.14.0", "bower": "1.8.2", - "chai": "4.3.4", + "chai": "4.1.2", "chai-as-promised": "^7.1.1", "chai-bytes": "^0.1.2", "css-loader": "^3.6.0", @@ -194,6 +195,7 @@ "node-gyp": "3.8.0", "node-sass-import-once": "1.2.0", "nyc": "11.4.1", + "patch-package": "^6.2.2", "postinstall-prepare": "^1.0.1", "prettier": "1.19.0", "qs": "6.5.1", diff --git a/ts/components/conversation/InviteContactsDialog.tsx b/ts/components/conversation/InviteContactsDialog.tsx index 287e34520..b1dabdab1 100644 --- a/ts/components/conversation/InviteContactsDialog.tsx +++ b/ts/components/conversation/InviteContactsDialog.tsx @@ -100,41 +100,22 @@ class InviteContactsDialogInner extends React.Component { private async submitForOpenGroup(pubkeys: Array) { const { convo } = this.props; - if (convo.isOpenGroupV1()) { - const v1 = convo.toOpenGroupV1(); - const groupInvitation = { - serverAddress: v1.server, - serverName: convo.getName(), - channelId: 1, // always 1 - }; - pubkeys.forEach(async pubkeyStr => { - const privateConvo = await ConversationController.getInstance().getOrCreateAndWait( - pubkeyStr, - ConversationTypeEnum.PRIVATE - ); - - if (privateConvo) { - void privateConvo.sendMessage('', null, null, null, groupInvitation); - } - }); - } else if (convo.isOpenGroupV2()) { - const v2 = convo.toOpenGroupV2(); - const completeUrl = await getCompleteUrlForV2ConvoId(convo.id); - const groupInvitation = { - serverAddress: completeUrl, - serverName: convo.getName(), - }; - pubkeys.forEach(async pubkeyStr => { - const privateConvo = await ConversationController.getInstance().getOrCreateAndWait( - pubkeyStr, - ConversationTypeEnum.PRIVATE - ); - - if (privateConvo) { - void privateConvo.sendMessage('', null, null, null, groupInvitation); - } - }); - } + + const completeUrl = await getCompleteUrlForV2ConvoId(convo.id); + const groupInvitation = { + serverAddress: completeUrl, + serverName: convo.getName(), + }; + pubkeys.forEach(async pubkeyStr => { + const privateConvo = await ConversationController.getInstance().getOrCreateAndWait( + pubkeyStr, + ConversationTypeEnum.PRIVATE + ); + + if (privateConvo) { + void privateConvo.sendMessage('', null, null, null, groupInvitation); + } + }); } private async submitForClosedGroup(pubkeys: Array) { diff --git a/ts/components/conversation/ModeratorsAddDialog.tsx b/ts/components/conversation/ModeratorsAddDialog.tsx index f83a8cafa..0bd57696f 100644 --- a/ts/components/conversation/ModeratorsAddDialog.tsx +++ b/ts/components/conversation/ModeratorsAddDialog.tsx @@ -36,11 +36,7 @@ export class AddModeratorsDialog extends React.Component { }; } - public async componentDidMount() { - if (this.props.convo.isOpenGroupV1()) { - this.channelAPI = await this.props.convo.getPublicSendData(); - } - + public componentDidMount() { this.setState({ firstLoading: false }); } @@ -60,13 +56,11 @@ export class AddModeratorsDialog extends React.Component { addingInProgress: true, }); let isAdded: any; - if (this.props.convo.isOpenGroupV1()) { - isAdded = await this.channelAPI.serverAPI.addModerator([pubkey.key]); - } else { - // this is a v2 opengroup - const roomInfos = this.props.convo.toOpenGroupV2(); - isAdded = await ApiV2.addModerator(pubkey, roomInfos); - } + + // this is a v2 opengroup + const roomInfos = this.props.convo.toOpenGroupV2(); + isAdded = await ApiV2.addModerator(pubkey, roomInfos); + if (!isAdded) { window?.log?.warn('failed to add moderators:', isAdded); diff --git a/ts/components/conversation/ModeratorsRemoveDialog.tsx b/ts/components/conversation/ModeratorsRemoveDialog.tsx index c0f9ba6bb..85369fe47 100644 --- a/ts/components/conversation/ModeratorsRemoveDialog.tsx +++ b/ts/components/conversation/ModeratorsRemoveDialog.tsx @@ -40,11 +40,7 @@ export class RemoveModeratorsDialog extends React.Component { }; } - public async componentDidMount() { - if (this.props.convo.isOpenGroupV1()) { - this.channelAPI = await this.props.convo.getPublicSendData(); - } - + public componentDidMount() { void this.refreshModList(); } @@ -137,14 +133,10 @@ export class RemoveModeratorsDialog extends React.Component { }); } - private async refreshModList() { + private refreshModList() { let modPubKeys: Array = []; - if (this.props.convo.isOpenGroupV1()) { - // get current list of moderators - modPubKeys = (await this.channelAPI.getModerators()) as Array; - } else if (this.props.convo.isOpenGroupV2()) { - modPubKeys = this.props.convo.getGroupAdmins() || []; - } + modPubKeys = this.props.convo.getGroupAdmins() || []; + const convos = ConversationController.getInstance().getConversations(); const moderatorsConvos = modPubKeys .map( @@ -197,19 +189,16 @@ export class RemoveModeratorsDialog extends React.Component { removingInProgress: true, }); let res; - if (this.props.convo.isOpenGroupV1()) { - res = await this.channelAPI.serverAPI.removeModerators(removedMods); - } else if (this.props.convo.isOpenGroupV2()) { - const roomInfos = this.props.convo.toOpenGroupV2(); - const modsToRemove = _.compact(removedMods.map(m => PubKey.from(m))); - res = await Promise.all( - modsToRemove.map(async m => { - return ApiV2.removeModerator(m, roomInfos); - }) - ); - // all moderators are removed means all promise resolved with bool= true - res = res.every(r => !!r); - } + const roomInfos = this.props.convo.toOpenGroupV2(); + const modsToRemove = _.compact(removedMods.map(m => PubKey.from(m))); + res = await Promise.all( + modsToRemove.map(async m => { + return ApiV2.removeModerator(m, roomInfos); + }) + ); + // all moderators are removed means all promise resolved with bool= true + res = res.every(r => !!r); + if (!res) { window?.log?.warn('failed to remove moderators:', res); diff --git a/ts/interactions/conversation.ts b/ts/interactions/conversation.ts index bf1e2b410..7ccf7ae4d 100644 --- a/ts/interactions/conversation.ts +++ b/ts/interactions/conversation.ts @@ -53,6 +53,11 @@ export async function copyPublicKey(convoId: string) { ToastUtils.pushCopiedToClipBoard(); } +/** + * + * @param messages the list of MessageModel to delete + * @param convo the conversation to delete from (only v2 opengroups are supported) + */ export async function deleteOpenGroupMessages( messages: Array, convo: ConversationModel @@ -99,42 +104,7 @@ export async function deleteOpenGroupMessages( ); return []; } - } else if (convo.isOpenGroupV1()) { - const channelAPI = await convo.getPublicSendData(); - - if (!channelAPI) { - throw new Error('Unable to get public channel API'); - } - - const invalidMessages = messages.filter(m => !m.attributes.serverId); - const pendingMessages = messages.filter(m => m.attributes.serverId); - - let deletedServerIds = []; - let ignoredServerIds = []; - - if (pendingMessages.length > 0) { - const result = await channelAPI.deleteMessages( - pendingMessages.map(m => m.attributes.serverId) - ); - deletedServerIds = result.deletedIds; - ignoredServerIds = result.ignoredIds; - } - - const toDeleteLocallyServerIds = _.union(deletedServerIds, ignoredServerIds); - let toDeleteLocally = messages.filter(m => - toDeleteLocallyServerIds.includes(m.attributes.serverId) - ); - toDeleteLocally = _.union(toDeleteLocally, invalidMessages); - - await Promise.all( - toDeleteLocally.map(async m => { - await convo.removeMessage(m.id); - }) - ); - - await convo.updateLastMessage(); - - return toDeleteLocally; + } else { + throw new Error('Opengroupv1 are not supported anymore'); } - return []; } diff --git a/ts/interactions/message.ts b/ts/interactions/message.ts index 8d2a38620..bc95e0eda 100644 --- a/ts/interactions/message.ts +++ b/ts/interactions/message.ts @@ -114,36 +114,17 @@ export async function removeSenderFromModerator(sender: string, convoId: string) try { const pubKeyToRemove = PubKey.cast(sender); const convo = ConversationController.getInstance().getOrThrow(convoId); - if (convo.isOpenGroupV1()) { - const channelAPI = await convo.getPublicSendData(); - if (!channelAPI) { - throw new Error('No channelAPI'); - } - const res = await channelAPI.serverAPI.removeModerators([pubKeyToRemove.key]); - if (!res) { - window?.log?.warn('failed to remove moderators:', res); - - ToastUtils.pushErrorHappenedWhileRemovingModerator(); - } else { - // refresh the moderator list. Will trigger a refresh - const modPubKeys = await channelAPI.getModerators(); - await convo.updateGroupAdmins(modPubKeys); - window?.log?.info(`${pubKeyToRemove.key} removed from moderators...`); - ToastUtils.pushUserRemovedFromModerators(); - } - } else if (convo.isOpenGroupV2()) { - // FXIME audric removeModerator not working serverside - const roomInfo = convo.toOpenGroupV2(); - const res = await ApiV2.removeModerator(pubKeyToRemove, roomInfo); - if (!res) { - window?.log?.warn('failed to remove moderator:', res); + // FXIME audric removeModerator not working serverside + const roomInfo = convo.toOpenGroupV2(); + const res = await ApiV2.removeModerator(pubKeyToRemove, roomInfo); + if (!res) { + window?.log?.warn('failed to remove moderator:', res); - ToastUtils.pushErrorHappenedWhileRemovingModerator(); - } else { - window?.log?.info(`${pubKeyToRemove.key} removed from moderators...`); - ToastUtils.pushUserRemovedFromModerators(); - } + ToastUtils.pushErrorHappenedWhileRemovingModerator(); + } else { + window?.log?.info(`${pubKeyToRemove.key} removed from moderators...`); + ToastUtils.pushUserRemovedFromModerators(); } } catch (e) { window?.log?.error('Got error while removing moderator:', e); @@ -153,41 +134,18 @@ export async function removeSenderFromModerator(sender: string, convoId: string) export async function addSenderAsModerator(sender: string, convoId: string) { try { const pubKeyToRemove = PubKey.cast(sender); - const convo = ConversationController.getInstance().getOrThrow(convoId); - if (convo.isOpenGroupV1()) { - const channelAPI = await convo.getPublicSendData(); - if (!channelAPI) { - throw new Error('No channelAPI'); - } - if (!channelAPI.serverAPI) { - throw new Error('No serverAPI'); - } - const res = await channelAPI.serverAPI.addModerator([pubKeyToRemove.key]); - if (!res) { - window?.log?.warn('failed to add moderators:', res); - ToastUtils.pushUserNeedsToHaveJoined(); - } else { - window?.log?.info(`${pubKeyToRemove.key} added as moderator...`); - // refresh the moderator list. Will trigger a refresh - const modPubKeys = await channelAPI.getModerators(); - await convo.updateGroupAdmins(modPubKeys); + // FXIME audric addModerator not working serverside + const roomInfo = convo.toOpenGroupV2(); + const res = await ApiV2.addModerator(pubKeyToRemove, roomInfo); + if (!res) { + window?.log?.warn('failed to add moderator:', res); - ToastUtils.pushUserAddedToModerators(); - } - } else if (convo.isOpenGroupV2()) { - // FXIME audric addModerator not working serverside - const roomInfo = convo.toOpenGroupV2(); - const res = await ApiV2.addModerator(pubKeyToRemove, roomInfo); - if (!res) { - window?.log?.warn('failed to add moderator:', res); - - ToastUtils.pushUserNeedsToHaveJoined(); - } else { - window?.log?.info(`${pubKeyToRemove.key} removed from moderators...`); - ToastUtils.pushUserAddedToModerators(); - } + ToastUtils.pushUserNeedsToHaveJoined(); + } else { + window?.log?.info(`${pubKeyToRemove.key} removed from moderators...`); + ToastUtils.pushUserAddedToModerators(); } } catch (e) { window?.log?.error('Got error while adding moderator:', e); diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index f98259e7d..5171d999f 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -27,7 +27,6 @@ import { ConversationType as ReduxConversationType, LastMessageStatusType, } from '../state/ducks/conversations'; -import { OpenGroupMessage } from '../session/messages/outgoing'; import { ExpirationTimerUpdateMessage } from '../session/messages/outgoing/controlMessage/ExpirationTimerUpdateMessage'; import { TypingMessage } from '../session/messages/outgoing/controlMessage/TypingMessage'; import { @@ -203,9 +202,6 @@ export class ConversationModel extends Backbone.Model { public isOpenGroupV2(): boolean { return OpenGroupUtils.isOpenGroupV2(this.id); } - public isOpenGroupV1(): boolean { - return OpenGroupUtils.isOpenGroupV1(this.id); - } public isClosedGroup() { return this.get('type') === ConversationTypeEnum.GROUP && !this.isPublic(); } @@ -587,17 +583,6 @@ export class ConversationModel extends Backbone.Model { return getOpenGroupV2FromConversationId(this.id); } - public toOpenGroupV1(): OpenGroup { - if (!this.isOpenGroupV1()) { - throw new Error('tried to run toOpenGroup for not public group v1'); - } - - return new OpenGroup({ - server: this.get('server'), - channel: this.get('channelId'), - conversationId: this.id, - }); - } public async sendMessageJob(message: MessageModel, expireTimer: number | undefined) { try { const uploads = await message.uploadData(); @@ -611,22 +596,7 @@ export class ConversationModel extends Backbone.Model { } if (this.isPublic() && !this.isOpenGroupV2()) { - const openGroup = this.toOpenGroupV1(); - - const openGroupParams = { - body: uploads.body, - timestamp: sentAt, - group: openGroup, - attachments: uploads.attachments, - preview: uploads.preview, - quote: uploads.quote, - identifier: id, - }; - const openGroupMessage = new OpenGroupMessage(openGroupParams); - // we need the return await so that errors are caught in the catch {} - await getMessageQueue().sendToOpenGroup(openGroupMessage); - - return; + throw new Error('Only opengroupv2 are supported now'); } // an OpenGroupV2 message is just a visible message const chatMessageParams: VisibleMessageParams = { diff --git a/ts/models/message.ts b/ts/models/message.ts index 30639d171..9485abbc2 100644 --- a/ts/models/message.ts +++ b/ts/models/message.ts @@ -6,7 +6,7 @@ import { SignalService } from '../../ts/protobuf'; import { getMessageQueue, Utils } from '../../ts/session'; import { ConversationController } from '../../ts/session/conversations'; import { MessageController } from '../../ts/session/messages'; -import { DataMessage, OpenGroupMessage } from '../../ts/session/messages/outgoing'; +import { DataMessage } from '../../ts/session/messages/outgoing'; import { ClosedGroupVisibleMessage } from '../session/messages/outgoing/visibleMessage/ClosedGroupVisibleMessage'; import { PubKey } from '../../ts/session/types'; import { UserUtils } from '../../ts/session/utils'; @@ -31,6 +31,9 @@ import { uploadQuoteThumbnailsV2, } from '../session/utils/AttachmentsV2'; import { acceptOpenGroupInvitation } from '../interactions/message'; +import { OpenGroupMessageV2 } from '../opengroup/opengroupV2/OpenGroupMessageV2'; +import { OpenGroupVisibleMessage } from '../session/messages/outgoing/visibleMessage/OpenGroupVisibleMessage'; +import { getV2OpenGroupRoom } from '../data/opengroups'; export class MessageModel extends Backbone.Model { public propsForTimerNotification: any; public propsForGroupNotification: any; @@ -771,17 +774,9 @@ export class MessageModel extends Backbone.Model { } else { // NOTE: we want to go for the v1 if this is an OpenGroupV1 or not an open group at all // because there is a fallback invoked on uploadV1() for attachments for not open groups attachments - - const openGroupV1 = conversation?.isOpenGroupV1() ? conversation?.toOpenGroupV1() : undefined; - attachmentPromise = AttachmentFsV2Utils.uploadAttachmentsToFsV2( - filenameOverridenAttachments, - openGroupV1 - ); - linkPreviewPromise = AttachmentFsV2Utils.uploadLinkPreviewsToFsV2( - previewWithData, - openGroupV1 - ); - quotePromise = AttachmentFsV2Utils.uploadQuoteThumbnailsToFsV2(quoteWithData, openGroupV1); + attachmentPromise = AttachmentFsV2Utils.uploadAttachmentsToFsV2(filenameOverridenAttachments); + linkPreviewPromise = AttachmentFsV2Utils.uploadLinkPreviewsToFsV2(previewWithData); + quotePromise = AttachmentFsV2Utils.uploadQuoteThumbnailsToFsV2(quoteWithData); } const [attachments, preview, quote] = await Promise.all([ @@ -817,21 +812,24 @@ export class MessageModel extends Backbone.Model { } if (conversation.isPublic()) { - const openGroup = { - server: conversation.get('server'), - channel: conversation.get('channelId'), - conversationId: conversation.id, - }; + if (!conversation.isOpenGroupV2()) { + throw new Error('Only opengroupv2 are supported now'); + } const uploaded = await this.uploadData(); const openGroupParams = { identifier: this.id, timestamp: Date.now(), - group: openGroup, + lokiProfile: UserUtils.getOurProfile(), ...uploaded, }; - const openGroupMessage = new OpenGroupMessage(openGroupParams); - return getMessageQueue().sendToOpenGroup(openGroupMessage); + const roomInfos = await getV2OpenGroupRoom(conversation.id); + if (!roomInfos) { + throw new Error('Could not find roomInfos for this conversation'); + } + + const openGroupMessage = new OpenGroupVisibleMessage(openGroupParams); + return getMessageQueue().sendToOpenGroupV2(openGroupMessage, roomInfos); } const { body, attachments, preview, quote } = await this.uploadData(); diff --git a/ts/opengroup/opengroupV2/ApiUtil.ts b/ts/opengroup/opengroupV2/ApiUtil.ts index 59fa1bc05..5e858e347 100644 --- a/ts/opengroup/opengroupV2/ApiUtil.ts +++ b/ts/opengroup/opengroupV2/ApiUtil.ts @@ -49,27 +49,27 @@ export const parseMessages = async ( const messages = await Promise.all( rawMessages.map(async r => { try { - const opengroupMessage = OpenGroupMessageV2.fromJson(r); + const opengroupv2Message = OpenGroupMessageV2.fromJson(r); if ( - !opengroupMessage?.serverId || - !opengroupMessage.sentTimestamp || - !opengroupMessage.base64EncodedData || - !opengroupMessage.base64EncodedSignature + !opengroupv2Message?.serverId || + !opengroupv2Message.sentTimestamp || + !opengroupv2Message.base64EncodedData || + !opengroupv2Message.base64EncodedSignature ) { window?.log?.warn('invalid open group message received'); return null; } // Validate the message signature - const senderPubKey = PubKey.cast(opengroupMessage.sender).withoutPrefix(); - const signature = fromBase64ToArrayBuffer(opengroupMessage.base64EncodedSignature); - const messageData = fromBase64ToArrayBuffer(opengroupMessage.base64EncodedData); + const senderPubKey = PubKey.cast(opengroupv2Message.sender).withoutPrefix(); + const signature = fromBase64ToArrayBuffer(opengroupv2Message.base64EncodedSignature); + const messageData = fromBase64ToArrayBuffer(opengroupv2Message.base64EncodedData); // throws if signature failed await window.libsignal.Curve.async.verifySignature( fromHex(senderPubKey), messageData, signature ); - return opengroupMessage; + return opengroupv2Message; } catch (e) { window?.log?.error('An error happened while fetching getMessages output:', e); return null; diff --git a/ts/opengroup/opengroupV2/OpenGroupAPIV2.ts b/ts/opengroup/opengroupV2/OpenGroupAPIV2.ts index 6303ae261..d62b54179 100644 --- a/ts/opengroup/opengroupV2/OpenGroupAPIV2.ts +++ b/ts/opengroup/opengroupV2/OpenGroupAPIV2.ts @@ -20,6 +20,9 @@ import { isOpenGroupV2Request } from '../../fileserver/FileServerApiV2'; import { getAuthToken } from './ApiAuth'; import pRetry from 'p-retry'; +// used to be overwritten by testing +export const getMinTimeout = () => 1000; + /** * This function returns a base url to this room * This is basically used for building url after posting an attachment @@ -171,7 +174,7 @@ export async function openGroupV2GetRoomInfo({ isAuthRequired: false, endpoint: `rooms/${roomId}`, }; - const result = (await sendApiV2Request(request)) as any; + const result = (await exports.sendApiV2Request(request)) as any; if (result?.result?.room) { const { id, name, image_id: imageId } = result?.result?.room; @@ -194,10 +197,9 @@ export async function openGroupV2GetRoomInfo({ /** * Send the specified message to the specified room. * If an error happens, this function throws it - * + * Exported only for testing */ - -const postMessageRetryable = async ( +export const postMessageRetryable = async ( message: OpenGroupMessageV2, room: OpenGroupRequestCommonType ) => { @@ -213,7 +215,7 @@ const postMessageRetryable = async ( endpoint: 'messages', }; - const result = await sendApiV2Request(request); + const result = await exports.sendApiV2Request(request); const statusCode = parseStatusCodeFromOnionRequest(result); @@ -234,12 +236,12 @@ export const postMessage = async ( ) => { const result = await pRetry( async () => { - return postMessageRetryable(message, room); + return exports.postMessageRetryable(message, room); }, { retries: 3, // each path can fail 3 times before being dropped, we have 3 paths at most factor: 2, - minTimeout: 1000, + minTimeout: exports.getMinTimeout(), maxTimeout: 4000, onFailedAttempt: e => { window?.log?.warn( @@ -265,7 +267,7 @@ export const banUser = async ( queryParams, endpoint: 'block_list', }; - const banResult = await sendApiV2Request(request); + const banResult = await exports.sendApiV2Request(request); const isOk = parseStatusCodeFromOnionRequest(banResult) === 200; return isOk; }; @@ -281,7 +283,7 @@ export const unbanUser = async ( isAuthRequired: true, endpoint: `block_list/${userToBan.key}`, }; - const unbanResult = await sendApiV2Request(request); + const unbanResult = await exports.sendApiV2Request(request); const isOk = parseStatusCodeFromOnionRequest(unbanResult) === 200; return isOk; }; @@ -298,7 +300,7 @@ export const deleteMessageByServerIds = async ( endpoint: 'delete_messages', queryParams: { ids: idsToRemove }, }; - const messageDeletedResult = await sendApiV2Request(request); + const messageDeletedResult = await exports.sendApiV2Request(request); const isOk = parseStatusCodeFromOnionRequest(messageDeletedResult) === 200; return isOk; }; @@ -313,7 +315,7 @@ export const getAllRoomInfos = async (roomInfos: OpenGroupV2Room) => { endpoint: 'rooms', serverPublicKey: roomInfos.serverPublicKey, }; - const result = await sendApiV2Request(request); + const result = await exports.sendApiV2Request(request); const statusCode = parseStatusCodeFromOnionRequest(result); if (statusCode !== 200) { @@ -334,7 +336,7 @@ export const getMemberCount = async ( isAuthRequired: true, endpoint: 'member_count', }; - const result = await sendApiV2Request(request); + const result = await exports.sendApiV2Request(request); if (parseStatusCodeFromOnionRequest(result) !== 200) { window?.log?.warn('getMemberCount failed invalid status code'); return; @@ -368,7 +370,7 @@ export const downloadFileOpenGroupV2 = async ( endpoint: `files/${fileId}`, }; - const result = await sendApiV2Request(request); + const result = await exports.sendApiV2Request(request); const statusCode = parseStatusCodeFromOnionRequest(result); if (statusCode !== 200) { return null; @@ -395,7 +397,7 @@ export const downloadFileOpenGroupV2ByUrl = async ( endpoint: pathName, }; - const result = await sendApiV2Request(request); + const result = await exports.sendApiV2Request(request); const statusCode = parseStatusCodeFromOnionRequest(result); if (statusCode !== 200) { return null; @@ -427,7 +429,7 @@ export const downloadPreviewOpenGroupV2 = async ( serverPublicKey: roomInfos.serverPublicKey, }; - const result = await sendApiV2Request(request); + const result = await exports.sendApiV2Request(request); const statusCode = parseStatusCodeFromOnionRequest(result); if (statusCode !== 200) { return null; @@ -466,7 +468,7 @@ export const uploadFileOpenGroupV2 = async ( queryParams, }; - const result = await sendApiV2Request(request); + const result = await exports.sendApiV2Request(request); const statusCode = parseStatusCodeFromOnionRequest(result); if (statusCode !== 200) { return null; @@ -506,7 +508,7 @@ export const uploadImageForRoomOpenGroupV2 = async ( queryParams, }; - const result = await sendApiV2Request(request); + const result = await exports.sendApiV2Request(request); const statusCode = parseStatusCodeFromOnionRequest(result); if (statusCode !== 200) { return null; @@ -531,7 +533,7 @@ export const addModerator = async ( queryParams: { public_key: userToAddAsMods.key, room_id: roomInfos.roomId }, endpoint: 'moderators', }; - const addModResult = await sendApiV2Request(request); + const addModResult = await exports.sendApiV2Request(request); const isOk = parseStatusCodeFromOnionRequest(addModResult) === 200; return isOk; }; @@ -547,7 +549,7 @@ export const removeModerator = async ( isAuthRequired: true, endpoint: `moderators/${userToAddAsMods.key}`, }; - const removeModResult = await sendApiV2Request(request); + const removeModResult = await exports.sendApiV2Request(request); const isOk = parseStatusCodeFromOnionRequest(removeModResult) === 200; return isOk; }; diff --git a/ts/opengroup/utils/OpenGroupUtils.ts b/ts/opengroup/utils/OpenGroupUtils.ts index 0bd669bae..4ba999ee3 100644 --- a/ts/opengroup/utils/OpenGroupUtils.ts +++ b/ts/opengroup/utils/OpenGroupUtils.ts @@ -44,13 +44,6 @@ export const openGroupPrefix = 'publicChat:'; */ export const openGroupPrefixRegex = new RegExp(`^${openGroupPrefix}`); -/** - * An open group v1 conversation id can only have the char '1' as roomId - */ -export const openGroupV1ConversationIdRegex = new RegExp( - `${openGroupPrefix}1@${protocolRegex.source}${hostnameRegex.source}` -); - export const openGroupV2ConversationIdRegex = new RegExp( `${openGroupPrefix}${roomIdV2Regex}@${protocolRegex.source}${hostnameRegex.source}${portRegex}` ); @@ -190,16 +183,6 @@ export function getOpenGroupV2FromConversationId( throw new Error('Not a v2 open group convo id'); } -/** - * Check if this conversation id corresponds to an OpenGroupV1 conversation. - * No access to database are made. Only regex matches - * @param conversationId the convo id to evaluate - * @returns true if this conversation id matches the Opengroupv1 conversation id regex - */ -export function isOpenGroupV1(conversationId: string) { - return openGroupV1ConversationIdRegex.test(conversationId); -} - /** * Check if this conversation id corresponds to an OpenGroupV2 conversation. * No access to database are made. Only regex matches @@ -207,10 +190,5 @@ export function isOpenGroupV1(conversationId: string) { * @returns true if this conversation id matches the Opengroupv2 conversation id regex */ export function isOpenGroupV2(conversationId: string) { - if (openGroupV1ConversationIdRegex.test(conversationId)) { - // this is an open group v1 - return false; - } - return openGroupV2ConversationIdRegex.test(conversationId); } diff --git a/ts/session/group/index.ts b/ts/session/group/index.ts index ccf416375..485b2b8cf 100644 --- a/ts/session/group/index.ts +++ b/ts/session/group/index.ts @@ -93,8 +93,8 @@ export async function initiateGroupUpdate( ); if (convo.isPublic()) { - if (convo.isOpenGroupV1()) { - await updateOpenGroupV1(convo, groupName, avatar); + if (!convo.isOpenGroupV2()) { + throw new Error('Only opengroupv2 are supported'); } else { await updateOpenGroupV2(convo, groupName, avatar); } diff --git a/ts/session/messages/outgoing/OpenGroupMessage.ts b/ts/session/messages/outgoing/OpenGroupMessage.ts deleted file mode 100644 index 355e9c3b4..000000000 --- a/ts/session/messages/outgoing/OpenGroupMessage.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Message, MessageParams } from './Message'; -import { OpenGroup } from '../../../opengroup/opengroupV1/OpenGroup'; -import { AttachmentPointer, Preview, Quote } from './visibleMessage/VisibleMessage'; - -interface OpenGroupMessageParams extends MessageParams { - group: OpenGroup; - attachments?: Array; - preview?: Array; - body?: string; - quote?: Quote; -} - -/** - * This class is only used for OpenGroup v1 (deprecated) - */ -export class OpenGroupMessage extends Message { - public readonly group: OpenGroup; - public readonly body?: string; - public readonly attachments: Array; - public readonly quote?: Quote; - public readonly preview?: Array; - - constructor({ - timestamp, - group, - attachments, - body, - quote, - identifier, - preview, - }: OpenGroupMessageParams) { - super({ timestamp, identifier }); - this.group = group; - this.body = body; - this.attachments = attachments ?? []; - this.quote = quote; - this.preview = preview ?? []; - } -} diff --git a/ts/session/messages/outgoing/index.ts b/ts/session/messages/outgoing/index.ts index 4ebd05fbd..96e276147 100644 --- a/ts/session/messages/outgoing/index.ts +++ b/ts/session/messages/outgoing/index.ts @@ -1,7 +1,6 @@ import { Message } from './Message'; -import { OpenGroupMessage } from './OpenGroupMessage'; export * from './ContentMessage'; export * from './DataMessage'; -export { Message, OpenGroupMessage }; +export { Message }; diff --git a/ts/session/sending/MessageQueue.ts b/ts/session/sending/MessageQueue.ts index c3ee15993..749499932 100644 --- a/ts/session/sending/MessageQueue.ts +++ b/ts/session/sending/MessageQueue.ts @@ -8,7 +8,7 @@ import { ClosedGroupNameChangeMessage } from '../messages/outgoing/controlMessag import { ClosedGroupMemberLeftMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupMemberLeftMessage'; import { MessageSentHandler } from './MessageSentHandler'; -import { ContentMessage, OpenGroupMessage } from '../messages/outgoing'; +import { ContentMessage } from '../messages/outgoing'; import { ExpirationTimerUpdateMessage } from '../messages/outgoing/controlMessage/ExpirationTimerUpdateMessage'; import { ClosedGroupAddedMembersMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupAddedMembersMessage'; import { ClosedGroupEncryptionPairMessage } from '../messages/outgoing/controlMessage/group/ClosedGroupEncryptionPairMessage'; @@ -53,35 +53,6 @@ export class MessageQueue { await this.process(user, message, sentCb); } - /** - * DEPRECATED This function is synced. It will wait for the message to be delivered to the open - * group to return. - * So there is no need for a sendCb callback - * - */ - public async sendToOpenGroup(message: OpenGroupMessage) { - // Open groups - if (!(message instanceof OpenGroupMessage)) { - throw new Error('sendToOpenGroup can only be used with OpenGroupMessage'); - } - // No queue needed for Open Groups; send directly - const error = new Error('Failed to send message to open group.'); - - // This is absolutely yucky ... we need to make it not use Promise - try { - const result = await MessageSender.sendToOpenGroup(message); - // sendToOpenGroup returns -1 if failed or an id if succeeded - if (result.serverId < 0) { - void MessageSentHandler.handleMessageSentFailure(message, error); - } else { - void MessageSentHandler.handlePublicMessageSentSuccess(message, result); - } - } catch (e) { - window?.log?.warn(`Failed to send message to open group: ${message.group.server}`, e); - void MessageSentHandler.handleMessageSentFailure(message, error); - } - } - /** * This function is synced. It will wait for the message to be delivered to the open * group to return. @@ -97,7 +68,7 @@ export class MessageQueue { try { const { sentTimestamp, serverId } = await MessageSender.sendToOpenGroupV2(message, roomInfos); - if (!serverId) { + if (!serverId || serverId === -1) { throw new Error(`Invalid serverId returned by server: ${serverId}`); } void MessageSentHandler.handlePublicMessageSentSuccess(message, { diff --git a/ts/session/sending/MessageSender.ts b/ts/session/sending/MessageSender.ts index caf483d94..6dd77bdaf 100644 --- a/ts/session/sending/MessageSender.ts +++ b/ts/session/sending/MessageSender.ts @@ -1,7 +1,6 @@ // REMOVE COMMENT AFTER: This can just export pure functions as it doesn't need state import { RawMessage } from '../types/RawMessage'; -import { OpenGroupMessage } from '../messages/outgoing'; import { SignalService } from '../../protobuf'; import { MessageEncrypter } from '../crypto'; import pRetry from 'p-retry'; @@ -92,44 +91,8 @@ function wrapEnvelope(envelope: SignalService.Envelope): Uint8Array { } // ================ Open Group ================ - -/** - * Deprecated Send a message to an open group v2. - * @param message The open group message. - */ -export async function sendToOpenGroup( - message: OpenGroupMessage -): Promise<{ serverId: number; serverTimestamp: number }> { - /* - Note: Retrying wasn't added to this but it can be added in the future if needed. - The only problem is that `channelAPI.sendMessage` returns true/false and doesn't throw any error so we can never be sure why sending failed. - This should be fixed and we shouldn't rely on returning true/false, rather return nothing (success) or throw an error (failure) - */ - const { group, quote, attachments, preview, body, timestamp } = message; - const channelAPI = await window.lokiPublicChatAPI.findOrCreateChannel( - group.server, - group.channel, - group.conversationId - ); - - if (!channelAPI) { - return { serverId: -1, serverTimestamp: -1 }; - } - - // Returns -1 on fail or an id > 0 on success - return channelAPI.sendMessage( - { - quote, - attachments: attachments || [], - preview: preview || [], - body, - }, - timestamp - ); -} - /** - * Deprecated Send a message to an open group v2. + * Send a message to an open group v2. * @param message The open group message. */ export async function sendToOpenGroupV2( diff --git a/ts/session/sending/MessageSentHandler.ts b/ts/session/sending/MessageSentHandler.ts index ac583611b..f5ed8c538 100644 --- a/ts/session/sending/MessageSentHandler.ts +++ b/ts/session/sending/MessageSentHandler.ts @@ -2,7 +2,6 @@ import _ from 'lodash'; import { getMessageById } from '../../data/data'; import { SignalService } from '../../protobuf'; import { MessageController } from '../messages'; -import { OpenGroupMessage } from '../messages/outgoing'; import { OpenGroupVisibleMessage } from '../messages/outgoing/visibleMessage/OpenGroupVisibleMessage'; import { EncryptionType, RawMessage } from '../types'; import { UserUtils } from '../utils'; @@ -10,7 +9,7 @@ import { UserUtils } from '../utils'; // tslint:disable-next-line no-unnecessary-class export class MessageSentHandler { public static async handlePublicMessageSentSuccess( - sentMessage: OpenGroupMessage | OpenGroupVisibleMessage, + sentMessage: OpenGroupVisibleMessage, result: { serverId: number; serverTimestamp: number } ) { const { serverId, serverTimestamp } = result; @@ -44,7 +43,7 @@ export class MessageSentHandler { sentMessage: RawMessage, wrappedEnvelope?: Uint8Array ) { - // The wrappedEnvelope will be set only if the message is not one of OpenGroupMessage type. + // The wrappedEnvelope will be set only if the message is not one of OpenGroupV2Message type. const fetchedMessage = await MessageSentHandler.fetchHandleMessageSentData(sentMessage); if (!fetchedMessage) { return; @@ -131,7 +130,7 @@ export class MessageSentHandler { } public static async handleMessageSentFailure( - sentMessage: RawMessage | OpenGroupMessage | OpenGroupVisibleMessage, + sentMessage: RawMessage | OpenGroupVisibleMessage, error: any ) { const fetchedMessage = await MessageSentHandler.fetchHandleMessageSentData(sentMessage); @@ -143,10 +142,7 @@ export class MessageSentHandler { await fetchedMessage.saveErrors(error); } - if ( - !(sentMessage instanceof OpenGroupMessage) && - !(sentMessage instanceof OpenGroupVisibleMessage) - ) { + if (!(sentMessage instanceof OpenGroupVisibleMessage)) { const isOurDevice = UserUtils.isUsFromCache(sentMessage.device); // if this message was for ourself, and it was not already synced, // it means that we failed to sync it. @@ -179,9 +175,7 @@ export class MessageSentHandler { * In this case, this function will look for it in the database and return it. * If the message is found on the db, it will also register it to the MessageController so our subsequent calls are quicker. */ - private static async fetchHandleMessageSentData( - m: RawMessage | OpenGroupMessage | OpenGroupVisibleMessage - ) { + private static async fetchHandleMessageSentData(m: RawMessage | OpenGroupVisibleMessage) { // if a message was sent and this message was sent after the last app restart, // this message is still in memory in the MessageController const msg = MessageController.getInstance().get(m.identifier); diff --git a/ts/session/utils/Attachments.ts b/ts/session/utils/Attachments.ts index ed73d4c19..acbaf27a0 100644 --- a/ts/session/utils/Attachments.ts +++ b/ts/session/utils/Attachments.ts @@ -7,13 +7,11 @@ import { Quote, QuotedAttachment, } from '../messages/outgoing/visibleMessage/VisibleMessage'; -import { OpenGroup } from '../../opengroup/opengroupV1/OpenGroup'; import { FSv2 } from '../../fileserver'; import { addAttachmentPadding } from '../crypto/BufferPadding'; interface UploadParams { attachment: Attachment; - openGroup?: OpenGroup; isAvatar?: boolean; isRaw?: boolean; shouldPad?: boolean; @@ -43,7 +41,7 @@ export class AttachmentFsV2Utils { private constructor() {} public static async uploadToFsV2(params: UploadParams): Promise { - const { attachment, openGroup, isRaw = false, shouldPad = false } = params; + const { attachment, isRaw = false, shouldPad = false } = params; if (typeof attachment !== 'object' || attachment == null) { throw new Error('Invalid attachment passed.'); } @@ -53,10 +51,6 @@ export class AttachmentFsV2Utils { `\`attachment.data\` must be an \`ArrayBuffer\`; got: ${typeof attachment.data}` ); } - // this can only be an opengroupv1 - if (openGroup) { - throw new Error('opengroupv1 attachments are not supported anymore'); - } const pointer: AttachmentPointer = { contentType: attachment.contentType || undefined, size: attachment.size, @@ -67,8 +61,7 @@ export class AttachmentFsV2Utils { let attachmentData: ArrayBuffer; - // We don't pad attachments for opengroup as they are unencrypted - if (isRaw || openGroup) { + if (isRaw) { attachmentData = attachment.data; } else { pointer.key = new Uint8Array(crypto.randomBytes(64)); @@ -103,13 +96,11 @@ export class AttachmentFsV2Utils { } public static async uploadAttachmentsToFsV2( - attachments: Array, - openGroup?: OpenGroup + attachments: Array ): Promise> { const promises = (attachments || []).map(async attachment => this.uploadToFsV2({ attachment, - openGroup, shouldPad: true, }) ); @@ -118,8 +109,7 @@ export class AttachmentFsV2Utils { } public static async uploadLinkPreviewsToFsV2( - previews: Array, - openGroup?: OpenGroup + previews: Array ): Promise> { const promises = (previews || []).map(async item => { // some links does not have an image associated, and it makes the whole message fail to send @@ -130,17 +120,13 @@ export class AttachmentFsV2Utils { ...item, image: await this.uploadToFsV2({ attachment: item.image, - openGroup, }), }; }); return Promise.all(promises); } - public static async uploadQuoteThumbnailsToFsV2( - quote?: RawQuote, - openGroup?: OpenGroup - ): Promise { + public static async uploadQuoteThumbnailsToFsV2(quote?: RawQuote): Promise { if (!quote) { return undefined; } @@ -150,7 +136,6 @@ export class AttachmentFsV2Utils { if (attachment.thumbnail) { thumbnail = await this.uploadToFsV2({ attachment: attachment.thumbnail, - openGroup, }); } return { diff --git a/ts/session/utils/syncUtils.ts b/ts/session/utils/syncUtils.ts index 567177f39..3936eb984 100644 --- a/ts/session/utils/syncUtils.ts +++ b/ts/session/utils/syncUtils.ts @@ -178,11 +178,6 @@ export const getCurrentConfigurationMessage = async (convos: Array convo.id === ourPubKey); - // Filter open groups v1 - const openGroupsV1Ids = convos - .filter(c => !!c.get('active_at') && c.isOpenGroupV1() && !c.get('left')) - .map(c => c.id.substring((c.id as string).lastIndexOf('@') + 1)) as Array; - const opengroupV2CompleteUrls = await getActiveOpenGroupV2CompleteUrls(convos); const onlyValidClosedGroup = await getValidClosedGroups(convos); const validContacts = getValidContacts(convos); @@ -196,7 +191,7 @@ export const getCurrentConfigurationMessage = async (convos: Array { - const group = new OpenGroup({ - server: 'chat.example.server', - channel: 1, - conversationId: '0', - }); - - it('can create empty message with just a timestamp and group', () => { - const message = new OpenGroupMessage({ - timestamp: Date.now(), - group, - }); - expect(message?.timestamp).to.be.approximately(Date.now(), 10); - expect(message?.group).to.deep.equal(group); - expect(message?.body).to.be.equal(undefined, 'body should be undefined'); - expect(message?.quote).to.be.equal(undefined, 'quote should be undefined'); - expect(message?.attachments).to.have.lengthOf(0); - expect(message?.preview).to.have.lengthOf(0); - }); - - it('can create message with a body', () => { - const message = new OpenGroupMessage({ - timestamp: Date.now(), - group, - body: 'body', - }); - expect(message).to.have.deep.property('body', 'body'); - }); - - it('can create message with a quote', () => { - const attachment = { - contentType: MIME.IMAGE_JPEG, - fileName: 'fileName', - isVoiceMessage: false, - }; - const quote = { - id: 0, - author: 'me', - text: 'hi', - attachments: [attachment], - }; - const message = new OpenGroupMessage({ - timestamp: Date.now(), - group, - quote, - }); - expect(message?.quote).to.deep.equal(quote); - }); - - it('can create message with an attachment', () => { - const attachment: AttachmentPointer = { - id: 0, - contentType: 'type', - key: new Uint8Array(1), - size: 10, - thumbnail: new Uint8Array(2), - digest: new Uint8Array(3), - fileName: 'filename', - flags: 0, - width: 10, - height: 20, - caption: 'caption', - url: 'url', - }; - const message = new OpenGroupMessage({ - timestamp: Date.now(), - group, - attachments: [attachment], - }); - expect(message?.attachments).to.have.lengthOf(1); - expect(message?.attachments[0]).to.deep.equal(attachment); - }); - - it('has an identifier', () => { - const message = new OpenGroupMessage({ - timestamp: Date.now(), - group, - }); - expect(message.identifier).to.not.equal(null, 'identifier cannot be null'); - expect(message.identifier).to.not.equal(undefined, 'identifier cannot be undefined'); - }); -}); diff --git a/ts/test/session/unit/sending/MessageQueue_test.ts b/ts/test/session/unit/sending/MessageQueue_test.ts index 49bb2e2c8..e140b7e6c 100644 --- a/ts/test/session/unit/sending/MessageQueue_test.ts +++ b/ts/test/session/unit/sending/MessageQueue_test.ts @@ -8,7 +8,7 @@ import { describe } from 'mocha'; import { GroupUtils, PromiseUtils, UserUtils } from '../../../../session/utils'; import { TestUtils } from '../../../../test/test-utils'; import { MessageQueue } from '../../../../session/sending/MessageQueue'; -import { ContentMessage, OpenGroupMessage } from '../../../../session/messages/outgoing'; +import { ContentMessage } from '../../../../session/messages/outgoing'; import { PubKey, RawMessage } from '../../../../session/types'; import { MessageSender } from '../../../../session/sending'; import { PendingMessageCacheStub } from '../../../test-utils/stubs'; @@ -202,31 +202,31 @@ describe('MessageQueue', () => { ); }); - describe('open groups', () => { - let sendToOpenGroupStub: sinon.SinonStub< - [OpenGroupMessage], - Promise<{ serverId: number; serverTimestamp: number }> - >; + describe('open groupsv2', () => { + let sendToOpenGroupV2Stub: sinon.SinonStub; beforeEach(() => { - sendToOpenGroupStub = sandbox - .stub(MessageSender, 'sendToOpenGroup') - .resolves({ serverId: -1, serverTimestamp: -1 }); + sendToOpenGroupV2Stub = sandbox + .stub(MessageSender, 'sendToOpenGroupV2') + .resolves(TestUtils.generateOpenGroupMessageV2()); }); it('can send to open group', async () => { - const message = TestUtils.generateOpenGroupMessage(); - await messageQueueStub.sendToOpenGroup(message); - expect(sendToOpenGroupStub.callCount).to.equal(1); + const message = TestUtils.generateOpenGroupVisibleMessage(); + const roomInfos = TestUtils.generateOpenGroupV2RoomInfos(); + + await messageQueueStub.sendToOpenGroupV2(message, roomInfos); + expect(sendToOpenGroupV2Stub.callCount).to.equal(1); }); it('should emit a success event when send was successful', async () => { - sendToOpenGroupStub.resolves({ + sendToOpenGroupV2Stub.resolves({ serverId: 5125, - serverTimestamp: 5126, + sentTimestamp: 5126, }); - const message = TestUtils.generateOpenGroupMessage(); - await messageQueueStub.sendToOpenGroup(message); + const message = TestUtils.generateOpenGroupVisibleMessage(); + const roomInfos = TestUtils.generateOpenGroupV2RoomInfos(); + await messageQueueStub.sendToOpenGroupV2(message, roomInfos); expect(messageSentPublicHandlerSuccessStub.callCount).to.equal(1); expect(messageSentPublicHandlerSuccessStub.lastCall.args[0].identifier).to.equal( message.identifier @@ -238,10 +238,11 @@ describe('MessageQueue', () => { }); it('should emit a fail event if something went wrong', async () => { - sendToOpenGroupStub.resolves({ serverId: -1, serverTimestamp: -1 }); - const message = TestUtils.generateOpenGroupMessage(); + sendToOpenGroupV2Stub.resolves({ serverId: -1, serverTimestamp: -1 }); + const message = TestUtils.generateOpenGroupVisibleMessage(); + const roomInfos = TestUtils.generateOpenGroupV2RoomInfos(); - await messageQueueStub.sendToOpenGroup(message); + await messageQueueStub.sendToOpenGroupV2(message, roomInfos); expect(messageSentHandlerFailedStub.callCount).to.equal(1); expect(messageSentHandlerFailedStub.lastCall.args[0].identifier).to.equal( message.identifier diff --git a/ts/test/session/unit/sending/MessageSender_test.ts b/ts/test/session/unit/sending/MessageSender_test.ts index fb2925f8d..c156c1e07 100644 --- a/ts/test/session/unit/sending/MessageSender_test.ts +++ b/ts/test/session/unit/sending/MessageSender_test.ts @@ -6,10 +6,10 @@ import { LokiMessageApi, MessageSender } from '../../../../session/sending'; import { TestUtils } from '../../../test-utils'; import { MessageEncrypter } from '../../../../session/crypto'; import { SignalService } from '../../../../protobuf'; -import { OpenGroupMessage } from '../../../../session/messages/outgoing'; import { EncryptionType } from '../../../../session/types/EncryptionType'; import { PubKey } from '../../../../session/types'; import { UserUtils } from '../../../../session/utils'; +import { ApiV2 } from '../../../../opengroup/opengroupV2'; describe('MessageSender', () => { const sandbox = sinon.createSandbox(); @@ -185,34 +185,56 @@ describe('MessageSender', () => { }); }); - describe('sendToOpenGroup', () => { - it('should send the message to the correct server and channel', async () => { - // We can do this because LokiPublicChatFactoryAPI has a module export in it - const stub = sandbox.stub().resolves({ - sendMessage: sandbox.stub(), - }); + describe('sendToOpenGroupV2', () => { + const sandbox2 = sinon.createSandbox(); + let postMessageRetryableStub: sinon.SinonStub; + beforeEach(() => { + sandbox + .stub(UserUtils, 'getOurPubKeyStrFromCache') + .resolves(TestUtils.generateFakePubKey().key); - TestUtils.stubWindow('lokiPublicChatAPI', { - findOrCreateChannel: stub, - }); + postMessageRetryableStub = sandbox + .stub(ApiV2, 'postMessageRetryable') + .resolves(TestUtils.generateOpenGroupMessageV2()); + }); - const group = { - server: 'server', - channel: 1, - conversationId: '0', - }; + afterEach(() => { + sandbox2.restore(); + }); - const message = new OpenGroupMessage({ - timestamp: Date.now(), - group, - }); + it('should call postMessageRetryableStub', async () => { + const message = TestUtils.generateOpenGroupVisibleMessage(); + const roomInfos = TestUtils.generateOpenGroupV2RoomInfos(); + + await MessageSender.sendToOpenGroupV2(message, roomInfos); + expect(postMessageRetryableStub.callCount).to.eq(1); + }); - await MessageSender.sendToOpenGroup(message); + it('should retry postMessageRetryableStub ', async () => { + const message = TestUtils.generateOpenGroupVisibleMessage(); + const roomInfos = TestUtils.generateOpenGroupV2RoomInfos(); + + postMessageRetryableStub.throws('whate'); + sandbox2.stub(ApiV2, 'getMinTimeout').returns(2); + + postMessageRetryableStub.onThirdCall().resolves(); + await MessageSender.sendToOpenGroupV2(message, roomInfos); + expect(postMessageRetryableStub.callCount).to.eq(3); + }); - const [server, channel, conversationId] = stub.getCall(0).args; - expect(server).to.equal(group.server); - expect(channel).to.equal(group.channel); - expect(conversationId).to.equal(group.conversationId); + it('should not retry more than 3 postMessageRetryableStub ', async () => { + const message = TestUtils.generateOpenGroupVisibleMessage(); + const roomInfos = TestUtils.generateOpenGroupV2RoomInfos(); + sandbox2.stub(ApiV2, 'getMinTimeout').returns(2); + postMessageRetryableStub.throws('fake error'); + postMessageRetryableStub.onCall(4).resolves(); + try { + await MessageSender.sendToOpenGroupV2(message, roomInfos); + throw new Error('Error expected'); + } catch (e) { + expect(e.name).to.eq('fake error'); + } + expect(postMessageRetryableStub.calledThrice); }); }); }); diff --git a/ts/test/test-utils/utils/message.ts b/ts/test/test-utils/utils/message.ts index 87b8f0d55..4478f8195 100644 --- a/ts/test/test-utils/utils/message.ts +++ b/ts/test/test-utils/utils/message.ts @@ -2,10 +2,12 @@ import { v4 as uuid } from 'uuid'; import { generateFakePubKey, generateFakePubKeys } from './pubkey'; import { ClosedGroupVisibleMessage } from '../../../session/messages/outgoing/visibleMessage/ClosedGroupVisibleMessage'; import { ConversationAttributes, ConversationTypeEnum } from '../../../models/conversation'; -import { OpenGroupMessage } from '../../../session/messages/outgoing'; import { VisibleMessage } from '../../../session/messages/outgoing/visibleMessage/VisibleMessage'; -import { OpenGroup } from '../../../opengroup/opengroupV1/OpenGroup'; import { openGroupPrefixRegex } from '../../../opengroup/utils/OpenGroupUtils'; +import { OpenGroupMessageV2 } from '../../../opengroup/opengroupV2/OpenGroupMessageV2'; +import { TestUtils } from '..'; +import { OpenGroupRequestCommonType } from '../../../opengroup/opengroupV2/ApiUtil'; +import { OpenGroupVisibleMessage } from '../../../session/messages/outgoing/visibleMessage/OpenGroupVisibleMessage'; export function generateVisibleMessage(identifier?: string): VisibleMessage { return new VisibleMessage({ @@ -20,23 +22,24 @@ export function generateVisibleMessage(identifier?: string): VisibleMessage { }); } -export function generateOpenGroupMessage(): OpenGroupMessage { - const group = new OpenGroup({ - server: 'chat.example.server', - channel: 0, - conversationId: '0', +export function generateOpenGroupMessageV2(): OpenGroupMessageV2 { + return new OpenGroupMessageV2({ + sentTimestamp: Date.now(), + sender: TestUtils.generateFakePubKey().key, + base64EncodedData: 'whatever', }); +} - return new OpenGroupMessage({ +export function generateOpenGroupVisibleMessage(): OpenGroupVisibleMessage { + return new OpenGroupVisibleMessage({ timestamp: Date.now(), - group, - attachments: undefined, - preview: undefined, - body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit', - quote: undefined, }); } +export function generateOpenGroupV2RoomInfos(): OpenGroupRequestCommonType { + return { roomId: 'main', serverUrl: 'http://116.203.70.33' }; +} + export function generateClosedGroupMessage(groupId?: string): ClosedGroupVisibleMessage { return new ClosedGroupVisibleMessage({ identifier: uuid(),