feat: add suppport in groupv2 of disappear v2 messages

pull/2963/head
Audric Ackermann 1 year ago
parent fda6ca315c
commit f6bffc8bb6

@ -846,7 +846,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
* @param providedDisappearingMode
* @param providedExpireTimer
* @param providedSource the pubkey of the user who made the change
* @param receivedAt the timestamp of when the change was received
* @param sentAt the timestamp of when the change was sent (when receiving it)
* @param fromSync if the change was made from a sync message
* @param shouldCommitConvo if the conversation change should be committed to the DB
* @param shouldCommitMessage if the timer update message change should be committed to the DB
@ -857,7 +857,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
providedDisappearingMode,
providedExpireTimer,
providedSource,
receivedAt, // is set if it comes from outside
sentAt, // is set if it comes from outside
fromSync, // if the update comes from sync message ONLY
fromConfigMessage, // if the update comes from a libsession config message ONLY
fromCurrentDevice,
@ -867,16 +867,14 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
providedDisappearingMode?: DisappearingMessageConversationModeType;
providedExpireTimer?: number;
providedSource?: string;
receivedAt?: number; // is set if it comes from outside
sentAt?: number; // is set if it comes from outside
fromSync: boolean;
fromCurrentDevice: boolean;
fromConfigMessage: boolean;
shouldCommitConvo?: boolean;
existingMessage?: MessageModel;
}): Promise<boolean> {
const isRemoteChange = Boolean(
(receivedAt || fromSync || fromConfigMessage) && !fromCurrentDevice
);
const isRemoteChange = Boolean((sentAt || fromSync || fromConfigMessage) && !fromCurrentDevice);
// we don't add an update message when this comes from a config message, as we already have the SyncedMessage itself with the right timestamp to display
const shouldAddExpireUpdateMessage = !fromConfigMessage;
@ -895,7 +893,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
// When we add a disappearing messages notification to the conversation, we want it
// to be above the message that initiated that change, hence the subtraction.
const createAtNetworkTimestamp = (receivedAt || GetNetworkTime.now()) - 1;
const createAtNetworkTimestamp = (sentAt || GetNetworkTime.now()) - 1;
// NOTE when we turn the disappearing setting to off, we don't want it to expire with the previous expiration anymore
const isV2DisappearReleased = ReleasedFeatures.isDisappearMessageV2FeatureReleasedCached();
@ -953,7 +951,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
};
if (!message) {
if (!receivedAt) {
if (!sentAt) {
// outgoing message
message = await this.addSingleOutgoingMessage({
...commonAttributes,
@ -993,7 +991,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
// if change was made remotely, don't send it to the contact/group
if (isRemoteChange) {
window.log.debug(
`[updateExpireTimer] remote change, not sending message again. receivedAt: ${receivedAt} fromSync: ${fromSync} fromCurrentDevice: ${fromCurrentDevice} for ${ed25519Str(
`[updateExpireTimer] remote change, not sending message again. sentAt: ${sentAt} fromSync: ${fromSync} fromCurrentDevice: ${fromCurrentDevice} for ${ed25519Str(
this.id
)}`
);
@ -1029,7 +1027,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
// We would have returned if that message sending part was not needed
//
const expireUpdate = {
identifier: message.id,
identifier: message.id as string,
createAtNetworkTimestamp,
expirationType,
expireTimer,
@ -1054,11 +1052,33 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
await getMessageQueue().sendToPubKey(pubkey, expirationTimerMessage, SnodeNamespaces.Default);
return true;
}
window?.log?.warn('TODO: Expiration update for closed groups are to be updated');
if (this.isClosedGroup()) {
if (this.isAdmin(UserUtils.getOurPubKeyStrFromCache())) {
// NOTE: we agreed that outgoing ExpirationTimerUpdate **for groups** are not expiring,
if (this.isClosedGroupV2()) {
const group = await UserGroupsWrapperActions.getGroup(this.id);
if (!group || !group.secretKey) {
throw new Error(
'trying to change timer for a group we do not have the secretKey is not possible'
);
}
const v2groupMessage = new GroupUpdateInfoChangeMessage({
typeOfChange: SignalService.GroupUpdateInfoChangeMessage.Type.DISAPPEARING_MESSAGES,
...expireUpdate,
groupPk: this.id,
identifier: message.get('id'),
sodium: await getSodiumRenderer(),
secretKey: group.secretKey,
updatedExpirationSeconds: expireUpdate.expireTimer,
});
await getMessageQueue().sendToGroupV2({
message: v2groupMessage,
});
return true;
}
// NOTE: we agreed that outgoing ExpirationTimerUpdate **for legacy groups** are not expiring,
// but they still need the content to be right(as this is what we use for the change itself)
const expireUpdateForGroup = {

@ -252,9 +252,8 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
let description = this.getDescription();
if (description) {
// regex with a 'g' to ignore part groups
const regex = new RegExp(`@${PubKey.regexForPubkeys}`, 'g');
const pubkeysInDesc = description.match(regex);
(pubkeysInDesc || []).forEach((pubkeyWithAt: string) => {
const regexWithAt = new RegExp(`@${PubKey.regexForPubkeys}`, 'g');
(description.match(regexWithAt) || []).forEach((pubkeyWithAt: string) => {
const pubkey = pubkeyWithAt.slice(1);
const isUS = isUsAnySogsFromCache(pubkey);
const displayName = ConvoHub.use().getContactProfileNameOrShortenedPubKey(pubkey);
@ -264,6 +263,12 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
description = description?.replace(pubkeyWithAt, `@${displayName}`);
}
});
const regexWithoutAt = new RegExp(`${PubKey.regexForPubkeys}`, 'g');
(description.match(regexWithoutAt) || []).forEach((pubkeyWithoutAt: string) => {
description = description?.replace(pubkeyWithoutAt, `${PubKey.shorten(pubkeyWithoutAt)}`);
});
return description;
}
if ((this.get('attachments') || []).length > 0) {

@ -323,7 +323,7 @@ export async function handleNewClosedGroup(
: 'legacy',
providedExpireTimer: expireTimer,
providedSource: sender,
receivedAt: GetNetworkTime.now(),
sentAt: GetNetworkTime.now(),
fromSync: false,
fromCurrentDevice: false,
fromConfigMessage: false,

@ -264,7 +264,7 @@ async function handleUserProfileUpdate(result: IncomingUserResult): Promise<void
: 'off',
providedExpireTimer: wrapperNoteToSelfExpirySeconds,
providedSource: ourConvo.id,
receivedAt: result.latestEnvelopeTimestamp,
sentAt: result.latestEnvelopeTimestamp,
fromSync: true,
shouldCommitConvo: false,
fromCurrentDevice: false,
@ -409,7 +409,7 @@ async function handleContactsUpdate(result: IncomingUserResult) {
providedDisappearingMode: wrapperConvo.expirationMode,
providedExpireTimer: wrapperConvo.expirationTimerSeconds,
providedSource: wrapperConvo.id,
receivedAt: result.latestEnvelopeTimestamp, // this is most likely incorrect, but that's all we have
sentAt: result.latestEnvelopeTimestamp, // this is most likely incorrect, but that's all we have
fromSync: true,
fromCurrentDevice: false,
shouldCommitConvo: false,
@ -639,7 +639,7 @@ async function handleLegacyGroupUpdate(latestEnvelopeTimestamp: number) {
: 'off',
providedExpireTimer: fromWrapper.disappearingTimerSeconds,
providedSource: legacyGroupConvo.id,
receivedAt: latestEnvelopeTimestamp,
sentAt: latestEnvelopeTimestamp,
fromSync: true,
shouldCommitConvo: false,
fromCurrentDevice: false,

@ -220,23 +220,18 @@ async function handleGroupInfoChangeMessage({
break;
}
case SignalService.GroupUpdateInfoChangeMessage.Type.DISAPPEARING_MESSAGES: {
const newTimerSeconds = change.updatedExpiration;
if (
change.updatedExpiration &&
isNumber(change.updatedExpiration) &&
isFinite(change.updatedExpiration) &&
change.updatedExpiration >= 0
newTimerSeconds &&
isNumber(newTimerSeconds) &&
isFinite(newTimerSeconds) &&
newTimerSeconds >= 0
) {
await ClosedGroup.addUpdateMessage({
convo,
diff: { type: 'name', newName: change.updatedName },
sender: author,
sentAt: signatureTimestamp,
expireUpdate: null,
});
await convo.updateExpireTimer({
providedExpireTimer: change.updatedExpiration,
providedExpireTimer: newTimerSeconds,
providedSource: author,
receivedAt: signatureTimestamp,
providedDisappearingMode: newTimerSeconds > 0 ? 'deleteAfterSend' : 'off',
sentAt: signatureTimestamp,
fromCurrentDevice: false,
fromSync: false,
fromConfigMessage: false,

@ -465,7 +465,7 @@ export async function handleMessageJob(
providedExpireTimer: expireTimerUpdate,
providedSource: source,
fromSync: source === UserUtils.getOurPubKeyStrFromCache(),
receivedAt: messageModel.get('received_at'),
sentAt: messageModel.get('received_at'),
existingMessage: messageModel,
shouldCommitConvo: false,
fromCurrentDevice: false,

@ -5,15 +5,15 @@ import pRetry from 'p-retry';
// eslint-disable-next-line import/no-named-default
import { default as insecureNodeFetch } from 'node-fetch';
import { OnionPaths } from '.';
import { Data, Snode } from '../../data/data';
import { updateOnionPaths } from '../../state/ducks/onion';
import { APPLICATION_JSON } from '../../types/MIME';
import { ERROR_CODE_NO_CONNECT } from '../apis/snode_api/SNodeAPI';
import { Onions, snodeHttpsAgent } from '../apis/snode_api/onions';
import * as SnodePool from '../apis/snode_api/snodePool';
import { UserUtils } from '../utils';
import { Onions, snodeHttpsAgent } from '../apis/snode_api/onions';
import { allowOnlyOneAtATime } from '../utils/Promise';
import { updateOnionPaths } from '../../state/ducks/onion';
import { ERROR_CODE_NO_CONNECT } from '../apis/snode_api/SNodeAPI';
import { OnionPaths } from '.';
import { APPLICATION_JSON } from '../../types/MIME';
const desiredGuardCount = 3;
const minimumGuardCount = 2;

@ -145,58 +145,88 @@ async function sendSingleMessage({
}
const subRequests: Array<RawSnodeSubRequests> = [];
if (
SnodeNamespace.isUserConfigNamespace(encryptedAndWrapped.namespace) &&
PubKey.is05Pubkey(destination)
) {
subRequests.push(
new StoreUserConfigSubRequest({
encryptedData: encryptedAndWrapped.encryptedAndWrappedData,
namespace: encryptedAndWrapped.namespace,
ttlMs: overridenTtl,
})
);
} else if (
encryptedAndWrapped.namespace === SnodeNamespaces.Default &&
PubKey.is05Pubkey(destination)
) {
subRequests.push(
new StoreUserMessageSubRequest({
encryptedData: encryptedAndWrapped.encryptedAndWrappedData,
dbMessageIdentifier: encryptedAndWrapped.identifier || null,
ttlMs: overridenTtl,
destination,
})
);
} else if (
SnodeNamespace.isGroupConfigNamespace(encryptedAndWrapped.namespace) &&
PubKey.is03Pubkey(destination)
) {
subRequests.push(
new StoreGroupConfigOrMessageSubRequest({
encryptedData: encryptedAndWrapped.encryptedAndWrappedData,
namespace: encryptedAndWrapped.namespace,
ttlMs: overridenTtl,
groupPk: destination,
dbMessageIdentifier: encryptedAndWrapped.identifier || null,
})
if (PubKey.is05Pubkey(destination)) {
if (encryptedAndWrapped.namespace === SnodeNamespaces.Default) {
subRequests.push(
new StoreUserMessageSubRequest({
encryptedData: encryptedAndWrapped.encryptedAndWrappedData,
dbMessageIdentifier: encryptedAndWrapped.identifier || null,
ttlMs: overridenTtl,
destination,
})
);
} else if (SnodeNamespace.isUserConfigNamespace(encryptedAndWrapped.namespace)) {
subRequests.push(
new StoreUserConfigSubRequest({
encryptedData: encryptedAndWrapped.encryptedAndWrappedData,
namespace: encryptedAndWrapped.namespace,
ttlMs: overridenTtl,
})
);
} else if (encryptedAndWrapped.namespace === SnodeNamespaces.LegacyClosedGroup) {
subRequests.push(
new StoreUserMessageSubRequest({
encryptedData: encryptedAndWrapped.encryptedAndWrappedData,
dbMessageIdentifier: encryptedAndWrapped.identifier || null,
ttlMs: overridenTtl,
destination,
})
);
} else {
window.log.error(
`unhandled sendSingleMessage case with details: ${ed25519Str(destination)},namespace: ${
encryptedAndWrapped.namespace
}`
);
throw new Error(
`unhandled sendSingleMessage case for 05 ${ed25519Str(destination)} and namespace ${
encryptedAndWrapped.namespace
}`
);
}
} else if (PubKey.is03Pubkey(destination)) {
if (SnodeNamespace.isGroupConfigNamespace(encryptedAndWrapped.namespace)) {
subRequests.push(
new StoreGroupConfigOrMessageSubRequest({
encryptedData: encryptedAndWrapped.encryptedAndWrappedData,
namespace: encryptedAndWrapped.namespace,
ttlMs: overridenTtl,
groupPk: destination,
dbMessageIdentifier: encryptedAndWrapped.identifier || null,
})
);
} else if (encryptedAndWrapped.namespace === SnodeNamespaces.ClosedGroupMessages) {
subRequests.push(
new StoreGroupConfigOrMessageSubRequest({
encryptedData: encryptedAndWrapped.encryptedAndWrappedData,
namespace: encryptedAndWrapped.namespace,
ttlMs: overridenTtl,
groupPk: destination,
dbMessageIdentifier: encryptedAndWrapped.identifier || null,
})
);
} else {
window.log.error(
`unhandled sendSingleMessage case with details: ${ed25519Str(destination)},namespace: ${
encryptedAndWrapped.namespace
}`
);
throw new Error(
`unhandled sendSingleMessage case for 03 ${ed25519Str(destination)} and namespace ${
encryptedAndWrapped.namespace
}`
);
}
} else {
window.log.error(
`unhandled sendSingleMessage case with details: ${ed25519Str(destination)},namespace: ${
encryptedAndWrapped.namespace
}`
);
} else if (
encryptedAndWrapped.namespace === SnodeNamespaces.ClosedGroupMessages &&
PubKey.is03Pubkey(destination)
) {
subRequests.push(
new StoreGroupConfigOrMessageSubRequest({
encryptedData: encryptedAndWrapped.encryptedAndWrappedData,
namespace: encryptedAndWrapped.namespace,
ttlMs: overridenTtl,
groupPk: destination,
dbMessageIdentifier: encryptedAndWrapped.identifier || null,
})
throw new Error(
`unhandled sendSingleMessage case unsupported destination ${ed25519Str(destination)}`
);
} else {
window.log.error('unhandled sendSingleMessage case');
throw new Error('unhandled sendSingleMessage case');
}
const targetNode = await getNodeFromSwarmOrThrow(destination);

@ -609,7 +609,7 @@ describe('DisappearingMessage', () => {
providedDisappearingMode: 'deleteAfterSend',
providedExpireTimer: 600,
providedSource: testPubkey,
receivedAt: GetNetworkTime.now(),
sentAt: GetNetworkTime.now(),
fromSync: true,
shouldCommitConvo: false,
existingMessage: undefined,

Loading…
Cancel
Save