Use other sync messages for conversations missing ACI

This commit is contained in:
Scott Nonnenberg 2025-02-07 00:40:25 +10:00 committed by GitHub
commit c279108c75
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 224 additions and 93 deletions

View file

@ -220,6 +220,8 @@ import {
import { markFailed } from '../../test-node/util/messageFailures';
import { cleanupMessages } from '../../util/cleanup';
import { MessageModel } from '../../models/messages';
import type { ConversationModel } from '../../models/conversations';
// State
export type DBConversationType = ReadonlyDeep<{
@ -3579,17 +3581,10 @@ function revokePendingMembershipsFromGroupV2(
}
async function syncMessageRequestResponse(
conversationData: ConversationType,
conversation: ConversationModel,
response: Proto.SyncMessage.MessageRequestResponse.Type,
{ shouldSave = true } = {}
): Promise<void> {
const conversation = window.ConversationController.get(conversationData.id);
if (!conversation) {
throw new Error(
`syncMessageRequestResponse: No conversation found for conversation ${conversationData.id}`
);
}
// In GroupsV2, this may modify the server. We only want to continue if those
// server updates were successful.
await conversation.applyMessageRequestResponse(response, { shouldSave });
@ -3603,11 +3598,18 @@ async function syncMessageRequestResponse(
return;
}
const threadAci = conversation.getAci();
if (!threadAci) {
log.warn(
'syncMessageRequestResponse: No ACI for target conversation, not sending'
);
return;
}
try {
await singleProtoJobQueue.add(
MessageSender.getMessageRequestResponseSync({
threadE164: conversation.get('e164'),
threadAci: conversation.getAci(),
threadAci,
groupId,
type: response,
})
@ -3651,7 +3653,13 @@ function reportSpam(
}
const conversation = getConversationForReportSpam(conversationOrGroup);
if (conversation == null) {
const conversationModel = window.ConversationController.get(
conversation?.id
);
if (!conversation || !conversationModel) {
log.error(
`reportSpam: Conversation for report spam not found ${conversation?.id}. Doing nothing.`
);
return;
}
@ -3664,7 +3672,10 @@ function reportSpam(
idForLogging,
task: async () => {
await Promise.all([
syncMessageRequestResponse(conversation, messageRequestEnum.SPAM),
syncMessageRequestResponse(
conversationModel,
messageRequestEnum.SPAM
),
addReportSpamJob({
conversation,
getMessageServerGuidsForSpam:
@ -3700,10 +3711,19 @@ function blockAndReportSpam(
const conversationForSpam =
getConversationForReportSpam(conversationOrGroup);
const conversationModel = window.ConversationController.get(
conversationForSpam?.id
);
if (!conversationForSpam || !conversationModel) {
log.error(
`reportSpam: Conversation for report spam not found ${conversationForSpam?.id}. Doing nothing.`
);
return;
}
const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type;
const idForLogging = getConversationIdForLogging(conversationOrGroup);
if (conversationModel.getAci()) {
drop(
longRunningTaskWrapper({
name: 'blockAndReportSpam',
@ -3711,7 +3731,7 @@ function blockAndReportSpam(
task: async () => {
await Promise.all([
syncMessageRequestResponse(
conversationOrGroup,
conversationModel,
messageRequestEnum.BLOCK_AND_SPAM
),
conversationForSpam != null &&
@ -3732,36 +3752,71 @@ function blockAndReportSpam(
},
})
);
} else {
try {
await singleProtoJobQueue.add(
MessageSender.getBlockSync(
window.textsecure.storage.blocked.getBlockedData()
)
);
} catch (error) {
log.error(
`blockConversation/${idForLogging}: Failed to queue block sync message`,
Errors.toLogFormat(error)
);
}
}
};
}
function acceptConversation(
conversationId: string
): ThunkAction<void, RootStateType, unknown, NoopActionType> {
return async (dispatch, getState) => {
const conversationSelector = getConversationSelector(getState());
const conversationOrGroup = conversationSelector(conversationId);
if (!conversationOrGroup) {
return async dispatch => {
const conversation = window.ConversationController.get(conversationId);
if (!conversation) {
throw new Error(
'acceptConversation: Expected a conversation to be found. Doing nothing'
);
}
const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type;
const idForLogging = getConversationIdForLogging(conversationOrGroup);
const idForLogging = getConversationIdForLogging(conversation.attributes);
if (conversation.getAci()) {
drop(
longRunningTaskWrapper({
name: 'acceptConversation',
idForLogging,
task: async () => {
await syncMessageRequestResponse(
conversationOrGroup,
conversation,
messageRequestEnum.ACCEPT
);
},
})
);
} else {
await conversation.applyMessageRequestResponse(
messageRequestEnum.ACCEPT,
{
shouldSave: true,
}
);
try {
await singleProtoJobQueue.add(
MessageSender.getBlockSync(
window.textsecure.storage.blocked.getBlockedData()
)
);
} catch (error) {
log.error(
`acceptConversation/${idForLogging}: Failed to queue sync message`,
Errors.toLogFormat(error)
);
}
}
dispatch({
type: 'NOOP',
@ -3794,10 +3849,8 @@ function removeConversation(conversationId: string): ShowToastActionType {
function blockConversation(
conversationId: string
): ThunkAction<void, RootStateType, unknown, NoopActionType> {
return (dispatch, getState) => {
const conversationSelector = getConversationSelector(getState());
const conversation = conversationSelector(conversationId);
return async dispatch => {
const conversation = window.ConversationController.get(conversationId);
if (!conversation) {
throw new Error(
'blockConversation: Expected a conversation to be found. Doing nothing'
@ -3805,8 +3858,9 @@ function blockConversation(
}
const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type;
const idForLogging = getConversationIdForLogging(conversation);
const idForLogging = getConversationIdForLogging(conversation.attributes);
if (conversation.getAci()) {
drop(
longRunningTaskWrapper({
name: 'blockConversation',
@ -3819,6 +3873,26 @@ function blockConversation(
},
})
);
} else {
// In GroupsV2, this may modify the server. We only want to continue if those
// server updates were successful.
await conversation.applyMessageRequestResponse(messageRequestEnum.BLOCK, {
shouldSave: true,
});
try {
await singleProtoJobQueue.add(
MessageSender.getBlockSync(
window.textsecure.storage.blocked.getBlockedData()
)
);
} catch (error) {
log.error(
`blockConversation/${idForLogging}: Failed to queue block sync message`,
Errors.toLogFormat(error)
);
}
}
dispatch({
type: 'NOOP',
@ -3830,9 +3904,8 @@ function blockConversation(
function deleteConversation(
conversationId: string
): ThunkAction<void, RootStateType, unknown, NoopActionType> {
return (dispatch, getState) => {
const conversationSelector = getConversationSelector(getState());
const conversation = conversationSelector(conversationId);
return async dispatch => {
const conversation = window.ConversationController.get(conversationId);
if (!conversation) {
throw new Error(
'deleteConversation: Expected a conversation to be found. Doing nothing'
@ -3840,8 +3913,9 @@ function deleteConversation(
}
const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type;
const idForLogging = getConversationIdForLogging(conversation);
const idForLogging = getConversationIdForLogging(conversation.attributes);
if (conversation.getAci()) {
drop(
longRunningTaskWrapper({
name: 'deleteConversation',
@ -3854,6 +3928,9 @@ function deleteConversation(
},
})
);
} else {
await conversation.destroyMessages({ source: 'local-delete' });
}
dispatch({
type: 'NOOP',

View file

@ -1796,6 +1796,40 @@ export default class MessageSender {
});
}
static getBlockSync(
options: Readonly<{
e164s: Array<string>;
acis: Array<string>;
groupIds: Array<Uint8Array>;
}>
): SingleProtoJobData {
const myAci = window.textsecure.storage.user.getCheckedAci();
const syncMessage = MessageSender.createSyncMessage();
const blocked = new Proto.SyncMessage.Blocked();
blocked.numbers = options.e164s;
blocked.acis = options.acis;
blocked.groupIds = options.groupIds;
syncMessage.blocked = blocked;
const contentMessage = new Proto.Content();
contentMessage.syncMessage = syncMessage;
const { ContentHint } = Proto.UnidentifiedSenderMessage.Message;
return {
contentHint: ContentHint.RESENDABLE,
serviceId: myAci,
isSyncMessage: true,
protoBase64: Bytes.toBase64(
Proto.Content.encode(contentMessage).finish()
),
type: 'blockSync',
urgent: false,
};
}
static getMessageRequestResponseSync(
options: Readonly<{
threadE164?: string;

View file

@ -3,9 +3,11 @@
import { without } from 'lodash';
import type { StorageInterface } from '../../types/Storage.d';
import type { ServiceIdString } from '../../types/ServiceId';
import * as log from '../../logging/log';
import * as Bytes from '../../Bytes';
import { isAciString } from '../../util/isAciString';
import type { StorageInterface } from '../../types/Storage.d';
import type { AciString, ServiceIdString } from '../../types/ServiceId';
export const BLOCKED_NUMBERS_ID = 'blocked';
export const BLOCKED_UUIDS_ID = 'blocked-uuids';
@ -99,4 +101,22 @@ export class Blocked {
log.info(`removing group(${groupId} from blocked list`);
await this.storage.put(BLOCKED_GROUPS_ID, without(groupIds, groupId));
}
public getBlockedData(): {
e164s: Array<string>;
acis: Array<AciString>;
groupIds: Array<Uint8Array>;
} {
const e164s = this.getBlockedNumbers();
const acis = this.getBlockedServiceIds().filter(item => isAciString(item));
const groupIds = this.getBlockedGroups().map(item =>
Bytes.fromBase64(item)
);
return {
e164s,
acis,
groupIds,
};
}
}

View file

@ -49,13 +49,13 @@ export const sendTypesEnum = z.enum([
'pniIdentitySyncRequest', // urgent because we need our PNI to be fully functional
// The actual sync messages, which we never send, just receive - non-urgent
'blockSync',
'configurationSync',
'contactSync',
'keySync',
'pniIdentitySync',
// Syncs, default non-urgent
'blockSync',
'deleteForMeSync',
'fetchLatestManifestSync',
'fetchLocalProfileSync',