add sending of message for opengroupv2`
This commit is contained in:
parent
35d66d8865
commit
f7e163c142
|
@ -39,6 +39,10 @@ import { ReadReceiptMessage } from '../session/messages/outgoing/controlMessage/
|
|||
import { OpenGroup } from '../opengroup/opengroupV1/OpenGroup';
|
||||
import { OpenGroupUtils } from '../opengroup/utils';
|
||||
import { ConversationInteraction } from '../interactions';
|
||||
import { getV2OpenGroupRoom } from '../data/opengroups';
|
||||
import { OpenGroupVisibleMessage } from '../session/messages/outgoing/visibleMessage/OpenGroupVisibleMessage';
|
||||
import { OpenGroupRequestCommonType } from '../opengroup/opengroupV2/ApiUtil';
|
||||
import { getOpenGroupV2FromConversationId } from '../opengroup/utils/OpenGroupUtils';
|
||||
|
||||
export enum ConversationType {
|
||||
GROUP = 'group',
|
||||
|
@ -573,9 +577,16 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
};
|
||||
}
|
||||
|
||||
public toOpenGroup() {
|
||||
if (!this.isPublic()) {
|
||||
throw new Error('tried to run toOpenGroup for not public group');
|
||||
public toOpenGroupV2(): OpenGroupRequestCommonType {
|
||||
if (!this.isOpenGroupV2()) {
|
||||
throw new Error('tried to run toOpenGroup for not public group v2');
|
||||
}
|
||||
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({
|
||||
|
@ -596,8 +607,8 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
throw new Error('sendMessageJob() sent_at must be set.');
|
||||
}
|
||||
|
||||
if (this.isPublic()) {
|
||||
const openGroup = this.toOpenGroup();
|
||||
if (this.isPublic() && !this.isOpenGroupV2()) {
|
||||
const openGroup = this.toOpenGroupV1();
|
||||
|
||||
const openGroupParams = {
|
||||
body: uploads.body,
|
||||
|
@ -611,8 +622,10 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
const openGroupMessage = new OpenGroupMessage(openGroupParams);
|
||||
// we need the return await so that errors are caught in the catch {}
|
||||
await getMessageQueue().sendToOpenGroup(openGroupMessage);
|
||||
|
||||
return;
|
||||
}
|
||||
// an OpenGroupV2 message is just a visible message
|
||||
const chatMessageParams: VisibleMessageParams = {
|
||||
body: uploads.body,
|
||||
identifier: id,
|
||||
|
@ -624,6 +637,18 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
lokiProfile: UserUtils.getOurProfile(),
|
||||
};
|
||||
|
||||
if (this.isOpenGroupV2()) {
|
||||
const chatMessageOpenGroupV2 = new OpenGroupVisibleMessage(chatMessageParams);
|
||||
const roomInfos = this.toOpenGroupV2();
|
||||
if (!roomInfos) {
|
||||
throw new Error('Could not find this room in db');
|
||||
}
|
||||
|
||||
// we need the return await so that errors are caught in the catch {}
|
||||
await getMessageQueue().sendToOpenGroupV2(chatMessageOpenGroupV2, roomInfos);
|
||||
return;
|
||||
}
|
||||
|
||||
const destinationPubkey = new PubKey(destination);
|
||||
if (this.isPrivate()) {
|
||||
if (this.isMe()) {
|
||||
|
|
|
@ -27,6 +27,11 @@ import { isOpenGroupV2 } from '../opengroup/utils/OpenGroupUtils';
|
|||
import { banUser } from '../opengroup/opengroupV2/OpenGroupAPIV2';
|
||||
import { getV2OpenGroupRoom } from '../data/opengroups';
|
||||
import { MessageInteraction } from '../interactions';
|
||||
import {
|
||||
uploadAttachmentsV2,
|
||||
uploadLinkPreviewsV2,
|
||||
uploadQuoteThumbnailsV2,
|
||||
} from '../session/utils/AttachmentsV2';
|
||||
export class MessageModel extends Backbone.Model<MessageAttributes> {
|
||||
public propsForTimerNotification: any;
|
||||
public propsForGroupNotification: any;
|
||||
|
@ -736,14 +741,35 @@ export class MessageModel extends Backbone.Model<MessageAttributes> {
|
|||
const previewWithData = await window.Signal.Migrations.loadPreviewData(this.get('preview'));
|
||||
|
||||
const conversation = this.getConversation();
|
||||
const openGroup =
|
||||
(conversation && conversation.isPublic() && conversation.toOpenGroup()) || undefined;
|
||||
|
||||
let attachmentPromise;
|
||||
let linkPreviewPromise;
|
||||
let quotePromise;
|
||||
const { AttachmentUtils } = Utils;
|
||||
|
||||
// we want to go for the v1, if this is an OpenGroupV1 or not an open group at all
|
||||
if (conversation?.isOpenGroupV2()) {
|
||||
const openGroupV2 = conversation.toOpenGroupV2();
|
||||
attachmentPromise = uploadAttachmentsV2(filenameOverridenAttachments, openGroupV2);
|
||||
linkPreviewPromise = uploadLinkPreviewsV2(previewWithData, openGroupV2);
|
||||
quotePromise = uploadQuoteThumbnailsV2(openGroupV2, quoteWithData);
|
||||
} 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?.toOpenGroupV1();
|
||||
attachmentPromise = AttachmentUtils.uploadAttachmentsV1(
|
||||
filenameOverridenAttachments,
|
||||
openGroupV1
|
||||
);
|
||||
linkPreviewPromise = AttachmentUtils.uploadLinkPreviewsV1(previewWithData, openGroupV1);
|
||||
quotePromise = AttachmentUtils.uploadQuoteThumbnailsV1(quoteWithData, openGroupV1);
|
||||
}
|
||||
|
||||
const [attachments, preview, quote] = await Promise.all([
|
||||
AttachmentUtils.uploadAttachments(filenameOverridenAttachments, openGroup),
|
||||
AttachmentUtils.uploadLinkPreviews(previewWithData, openGroup),
|
||||
AttachmentUtils.uploadQuoteThumbnails(quoteWithData, openGroup),
|
||||
attachmentPromise,
|
||||
linkPreviewPromise,
|
||||
quotePromise,
|
||||
]);
|
||||
|
||||
return {
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
import _ from 'underscore';
|
||||
import { getV2OpenGroupRoomByRoomId } from '../../data/opengroups';
|
||||
import { getSodium } from '../../session/crypto';
|
||||
import { PubKey } from '../../session/types';
|
||||
import {
|
||||
fromBase64ToArray,
|
||||
fromBase64ToArrayBuffer,
|
||||
fromHex,
|
||||
fromHexToArray,
|
||||
} from '../../session/utils/String';
|
||||
import { fromBase64ToArrayBuffer, fromHex } from '../../session/utils/String';
|
||||
import { OpenGroupMessageV2 } from './OpenGroupMessageV2';
|
||||
|
||||
export const defaultServer = 'https://sessionopengroup.com';
|
||||
|
@ -80,6 +73,7 @@ export const setCachedModerators = (
|
|||
cachedModerators.set(serverUrl, new Map());
|
||||
allRoomsMods = cachedModerators.get(serverUrl);
|
||||
}
|
||||
// tslint:disable: no-non-null-assertion
|
||||
if (!allRoomsMods!.get(roomId)) {
|
||||
allRoomsMods!.set(roomId, new Set());
|
||||
}
|
||||
|
|
|
@ -335,40 +335,38 @@ export const getMessages = async ({
|
|||
return validMessages;
|
||||
};
|
||||
|
||||
/**
|
||||
* Send the specified message to the specified room.
|
||||
* If an error happens, this function throws it
|
||||
*
|
||||
*/
|
||||
export const postMessage = async (
|
||||
message: OpenGroupMessageV2,
|
||||
room: OpenGroupRequestCommonType
|
||||
) => {
|
||||
try {
|
||||
const signedMessage = await message.sign();
|
||||
const json = signedMessage.toJson();
|
||||
const signedMessage = await message.sign();
|
||||
const json = signedMessage.toJson();
|
||||
|
||||
const request: OpenGroupV2Request = {
|
||||
method: 'POST',
|
||||
room: room.roomId,
|
||||
server: room.serverUrl,
|
||||
queryParams: json,
|
||||
isAuthRequired: true,
|
||||
endpoint: 'messages',
|
||||
};
|
||||
const result = await sendOpenGroupV2Request(request);
|
||||
const statusCode = parseStatusCodeFromOnionRequest(result);
|
||||
const request: OpenGroupV2Request = {
|
||||
method: 'POST',
|
||||
room: room.roomId,
|
||||
server: room.serverUrl,
|
||||
queryParams: json,
|
||||
isAuthRequired: true,
|
||||
endpoint: 'messages',
|
||||
};
|
||||
const result = await sendOpenGroupV2Request(request);
|
||||
const statusCode = parseStatusCodeFromOnionRequest(result);
|
||||
|
||||
if (statusCode !== 200) {
|
||||
window.log.warn(`Could not postMessage, status code: ${statusCode}`);
|
||||
return null;
|
||||
}
|
||||
const rawMessage = (result as any)?.result?.message;
|
||||
if (!rawMessage) {
|
||||
window.log.warn('postMessage parsing failed');
|
||||
return null;
|
||||
}
|
||||
// this will throw if the json is not valid
|
||||
return OpenGroupMessageV2.fromJson(rawMessage);
|
||||
} catch (e) {
|
||||
window.log.error('Failed to post message to open group v2', e);
|
||||
return null;
|
||||
if (statusCode !== 200) {
|
||||
throw new Error(`Could not postMessage, status code: ${statusCode}`);
|
||||
}
|
||||
const rawMessage = (result as any)?.result?.message;
|
||||
if (!rawMessage) {
|
||||
throw new Error('postMessage parsing failed');
|
||||
}
|
||||
// this will throw if the json is not valid
|
||||
return OpenGroupMessageV2.fromJson(rawMessage);
|
||||
};
|
||||
|
||||
/** Those functions are related to moderators management */
|
||||
|
|
|
@ -145,7 +145,6 @@ async function sendOpenGroupV2RequestCompactPoll(
|
|||
const roomPollValidResults = results.filter(ret => ret.statusCode === 200);
|
||||
|
||||
if (roomWithTokensToRefresh) {
|
||||
console.warn('roomWithTokensToRefresh', roomWithTokensToRefresh);
|
||||
await Promise.all(
|
||||
roomWithTokensToRefresh.map(async roomId => {
|
||||
const roomDetails = await getV2OpenGroupRoomByRoomId({
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { default as insecureNodeFetch } from 'node-fetch';
|
||||
import { OpenGroupV2Room } from '../../data/opengroups';
|
||||
import { sendViaOnion } from '../../session/onions/onionSend';
|
||||
import { OpenGroupRequestCommonType } from '../opengroupV2/ApiUtil';
|
||||
|
||||
const protocolRegex = new RegExp('(https?://)?');
|
||||
|
||||
|
@ -163,6 +164,24 @@ export function getOpenGroupV2ConversationId(serverUrl: string, roomId: string)
|
|||
return `${openGroupPrefix}${roomId}@${serverUrl}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* No sql access. Just plain string logic
|
||||
*/
|
||||
export function getOpenGroupV2FromConversationId(
|
||||
conversationId: string
|
||||
): OpenGroupRequestCommonType {
|
||||
if (isOpenGroupV2(conversationId)) {
|
||||
const atIndex = conversationId.indexOf('@');
|
||||
const roomId = conversationId.slice(openGroupPrefix.length, atIndex);
|
||||
const serverUrl = conversationId.slice(atIndex + 1);
|
||||
return {
|
||||
serverUrl,
|
||||
roomId,
|
||||
};
|
||||
}
|
||||
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
|
||||
|
|
|
@ -10,6 +10,9 @@ interface OpenGroupMessageParams extends MessageParams {
|
|||
quote?: Quote;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is only used for OpenGroup v1 (deprecated)
|
||||
*/
|
||||
export class OpenGroupMessage extends Message {
|
||||
public readonly group: OpenGroup;
|
||||
public readonly body?: string;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
import { VisibleMessage } from './VisibleMessage';
|
||||
|
||||
export class OpenGroupVisibleMessage extends VisibleMessage {}
|
|
@ -18,6 +18,9 @@ import { ClosedGroupRemovedMembersMessage } from '../messages/outgoing/controlMe
|
|||
import { ClosedGroupVisibleMessage } from '../messages/outgoing/visibleMessage/ClosedGroupVisibleMessage';
|
||||
import { SyncMessageType } from '../utils/syncUtils';
|
||||
|
||||
import { OpenGroupRequestCommonType } from '../../opengroup/opengroupV2/ApiUtil';
|
||||
import { OpenGroupVisibleMessage } from '../messages/outgoing/visibleMessage/OpenGroupVisibleMessage';
|
||||
|
||||
type ClosedGroupMessageType =
|
||||
| ClosedGroupVisibleMessage
|
||||
| ClosedGroupAddedMembersMessage
|
||||
|
@ -51,7 +54,7 @@ export class MessageQueue {
|
|||
}
|
||||
|
||||
/**
|
||||
* This function is synced. It will wait for the message to be delivered to the open
|
||||
* 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
|
||||
*
|
||||
|
@ -79,6 +82,34 @@ export class MessageQueue {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 sendToOpenGroupV2(
|
||||
message: OpenGroupVisibleMessage,
|
||||
roomInfos: OpenGroupRequestCommonType
|
||||
) {
|
||||
// No queue needed for Open Groups v2; send directly
|
||||
const error = new Error('Failed to send message to open group.');
|
||||
|
||||
try {
|
||||
const { sentTimestamp, serverId } = await MessageSender.sendToOpenGroupV2(message, roomInfos);
|
||||
if (!serverId) {
|
||||
throw new Error(`Invalid serverId returned by server: ${serverId}`);
|
||||
}
|
||||
void MessageSentHandler.handlePublicMessageSentSuccess(message, {
|
||||
serverId: serverId,
|
||||
serverTimestamp: sentTimestamp,
|
||||
});
|
||||
} catch (e) {
|
||||
window?.log?.warn(`Failed to send message to open group: ${roomInfos}`, e);
|
||||
void MessageSentHandler.handleMessageSentFailure(message, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param sentCb currently only called for medium groups sent message
|
||||
|
|
|
@ -7,6 +7,13 @@ import { MessageEncrypter } from '../crypto';
|
|||
import pRetry from 'p-retry';
|
||||
import { PubKey } from '../types';
|
||||
import { UserUtils } from '../utils';
|
||||
import { VisibleMessage } from '../messages/outgoing/visibleMessage/VisibleMessage';
|
||||
import { OpenGroupRequestCommonType } from '../../opengroup/opengroupV2/ApiUtil';
|
||||
import { postMessage } from '../../opengroup/opengroupV2/OpenGroupAPIV2';
|
||||
import { OpenGroupMessageV2 } from '../../opengroup/opengroupV2/OpenGroupMessageV2';
|
||||
import { padPlainTextBuffer } from '../crypto/MessageEncrypter';
|
||||
import { fromUInt8ArrayToBase64 } from '../utils/String';
|
||||
import { OpenGroupVisibleMessage } from '../messages/outgoing/visibleMessage/OpenGroupVisibleMessage';
|
||||
|
||||
// ================ Regular ================
|
||||
|
||||
|
@ -99,7 +106,7 @@ function wrapEnvelope(envelope: SignalService.Envelope): Uint8Array {
|
|||
// ================ Open Group ================
|
||||
|
||||
/**
|
||||
* Send a message to an open group.
|
||||
* Deprecated Send a message to an open group v2.
|
||||
* @param message The open group message.
|
||||
*/
|
||||
export async function sendToOpenGroup(
|
||||
|
@ -132,3 +139,24 @@ export async function sendToOpenGroup(
|
|||
timestamp
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated Send a message to an open group v2.
|
||||
* @param message The open group message.
|
||||
*/
|
||||
export async function sendToOpenGroupV2(
|
||||
rawMessage: OpenGroupVisibleMessage,
|
||||
roomInfos: OpenGroupRequestCommonType
|
||||
): Promise<OpenGroupMessageV2> {
|
||||
const paddedBody = padPlainTextBuffer(rawMessage.plainTextBuffer());
|
||||
const v2Message = new OpenGroupMessageV2({
|
||||
sentTimestamp: Date.now(),
|
||||
sender: UserUtils.getOurPubKeyStrFromCache(),
|
||||
base64EncodedData: fromUInt8ArrayToBase64(paddedBody),
|
||||
// the signature is added in the postMessage())
|
||||
});
|
||||
|
||||
// postMessage throws
|
||||
const sentMessage = await postMessage(v2Message, roomInfos);
|
||||
return sentMessage;
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import _ from 'lodash';
|
||||
import { getMessageById } from '../../data/data';
|
||||
import { SignalService } from '../../protobuf';
|
||||
import { ConversationController } from '../conversations';
|
||||
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';
|
||||
|
||||
// tslint:disable-next-line no-unnecessary-class
|
||||
export class MessageSentHandler {
|
||||
public static async handlePublicMessageSentSuccess(
|
||||
sentMessage: OpenGroupMessage,
|
||||
sentMessage: OpenGroupMessage | OpenGroupVisibleMessage,
|
||||
result: { serverId: number; serverTimestamp: number }
|
||||
) {
|
||||
const { serverId, serverTimestamp } = result;
|
||||
|
@ -131,7 +131,7 @@ export class MessageSentHandler {
|
|||
}
|
||||
|
||||
public static async handleMessageSentFailure(
|
||||
sentMessage: RawMessage | OpenGroupMessage,
|
||||
sentMessage: RawMessage | OpenGroupMessage | OpenGroupVisibleMessage,
|
||||
error: any
|
||||
) {
|
||||
const fetchedMessage = await MessageSentHandler.fetchHandleMessageSentData(sentMessage);
|
||||
|
@ -143,7 +143,10 @@ export class MessageSentHandler {
|
|||
await fetchedMessage.saveErrors(error);
|
||||
}
|
||||
|
||||
if (!(sentMessage instanceof OpenGroupMessage)) {
|
||||
if (
|
||||
!(sentMessage instanceof OpenGroupMessage) &&
|
||||
!(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.
|
||||
|
@ -176,7 +179,9 @@ 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) {
|
||||
private static async fetchHandleMessageSentData(
|
||||
m: RawMessage | OpenGroupMessage | 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);
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import { EncryptionType } from './EncryptionType';
|
||||
|
||||
// TODO: Should we store failure count on raw messages??
|
||||
// Might be better to have a seperate interface which takes in a raw message aswell as a failure count
|
||||
export type RawMessage = {
|
||||
identifier: string;
|
||||
plainTextBuffer: Uint8Array;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import * as crypto from 'crypto';
|
||||
import { Attachment } from '../../types/Attachment';
|
||||
|
||||
import { LokiAppDotNetServerInterface } from '../../../js/modules/loki_app_dot_net_api';
|
||||
import {
|
||||
AttachmentPointer,
|
||||
Preview,
|
||||
|
@ -43,11 +42,7 @@ export class AttachmentUtils {
|
|||
|
||||
private constructor() {}
|
||||
|
||||
public static getDefaultServer(): LokiAppDotNetServerInterface {
|
||||
return window.tokenlessFileServerAdnAPI;
|
||||
}
|
||||
|
||||
public static async upload(params: UploadParams): Promise<AttachmentPointer> {
|
||||
public static async uploadV1(params: UploadParams): Promise<AttachmentPointer> {
|
||||
const { attachment, openGroup, isAvatar = false, isRaw = false, shouldPad = false } = params;
|
||||
if (typeof attachment !== 'object' || attachment == null) {
|
||||
throw new Error('Invalid attachment passed.');
|
||||
|
@ -59,7 +54,7 @@ export class AttachmentUtils {
|
|||
);
|
||||
}
|
||||
|
||||
let server = this.getDefaultServer();
|
||||
let server = window.tokenlessFileServerAdnAPI;
|
||||
if (openGroup) {
|
||||
const openGroupServer = await window.lokiPublicChatAPI.findOrCreateServer(openGroup.server);
|
||||
if (!openGroupServer) {
|
||||
|
@ -68,7 +63,7 @@ export class AttachmentUtils {
|
|||
server = openGroupServer;
|
||||
}
|
||||
const pointer: AttachmentPointer = {
|
||||
contentType: attachment.contentType ? attachment.contentType : undefined,
|
||||
contentType: attachment.contentType || undefined,
|
||||
size: attachment.size,
|
||||
fileName: attachment.fileName,
|
||||
flags: attachment.flags,
|
||||
|
@ -80,7 +75,7 @@ export class AttachmentUtils {
|
|||
if (isRaw || openGroup) {
|
||||
attachmentData = attachment.data;
|
||||
} else {
|
||||
server = this.getDefaultServer();
|
||||
server = window.tokenlessFileServerAdnAPI;
|
||||
pointer.key = new Uint8Array(crypto.randomBytes(64));
|
||||
const iv = new Uint8Array(crypto.randomBytes(16));
|
||||
|
||||
|
@ -107,7 +102,7 @@ export class AttachmentUtils {
|
|||
return pointer;
|
||||
}
|
||||
|
||||
public static async uploadAvatar(
|
||||
public static async uploadAvatarV1(
|
||||
attachment?: Attachment
|
||||
): Promise<AttachmentPointer | undefined> {
|
||||
if (!attachment) {
|
||||
|
@ -116,19 +111,19 @@ export class AttachmentUtils {
|
|||
|
||||
// isRaw is true since the data is already encrypted
|
||||
// and doesn't need to be encrypted again
|
||||
return this.upload({
|
||||
return this.uploadV1({
|
||||
attachment,
|
||||
isAvatar: true,
|
||||
isRaw: true,
|
||||
});
|
||||
}
|
||||
|
||||
public static async uploadAttachments(
|
||||
public static async uploadAttachmentsV1(
|
||||
attachments: Array<Attachment>,
|
||||
openGroup?: OpenGroup
|
||||
): Promise<Array<AttachmentPointer>> {
|
||||
const promises = (attachments || []).map(async attachment =>
|
||||
this.upload({
|
||||
this.uploadV1({
|
||||
attachment,
|
||||
openGroup,
|
||||
shouldPad: true,
|
||||
|
@ -138,7 +133,7 @@ export class AttachmentUtils {
|
|||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
public static async uploadLinkPreviews(
|
||||
public static async uploadLinkPreviewsV1(
|
||||
previews: Array<RawPreview>,
|
||||
openGroup?: OpenGroup
|
||||
): Promise<Array<Preview>> {
|
||||
|
@ -149,7 +144,7 @@ export class AttachmentUtils {
|
|||
}
|
||||
return {
|
||||
...item,
|
||||
image: await this.upload({
|
||||
image: await this.uploadV1({
|
||||
attachment: item.image,
|
||||
openGroup,
|
||||
}),
|
||||
|
@ -158,7 +153,7 @@ export class AttachmentUtils {
|
|||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
public static async uploadQuoteThumbnails(
|
||||
public static async uploadQuoteThumbnailsV1(
|
||||
quote?: RawQuote,
|
||||
openGroup?: OpenGroup
|
||||
): Promise<Quote | undefined> {
|
||||
|
@ -169,7 +164,7 @@ export class AttachmentUtils {
|
|||
const promises = (quote.attachments ?? []).map(async attachment => {
|
||||
let thumbnail: AttachmentPointer | undefined;
|
||||
if (attachment.thumbnail) {
|
||||
thumbnail = await this.upload({
|
||||
thumbnail = await this.uploadV1({
|
||||
attachment: attachment.thumbnail,
|
||||
openGroup,
|
||||
});
|
||||
|
@ -206,7 +201,7 @@ export class AttachmentUtils {
|
|||
return true;
|
||||
}
|
||||
|
||||
private static addAttachmentPadding(data: ArrayBuffer): ArrayBuffer {
|
||||
public static addAttachmentPadding(data: ArrayBuffer): ArrayBuffer {
|
||||
const originalUInt = new Uint8Array(data);
|
||||
|
||||
const paddedSize = Math.max(
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
import * as crypto from 'crypto';
|
||||
import { Attachment } from '../../types/Attachment';
|
||||
|
||||
import { OpenGroupRequestCommonType } from '../../opengroup/opengroupV2/ApiUtil';
|
||||
import {
|
||||
AttachmentPointer,
|
||||
Preview,
|
||||
Quote,
|
||||
QuotedAttachment,
|
||||
} from '../messages/outgoing/visibleMessage/VisibleMessage';
|
||||
import { AttachmentUtils } from './Attachments';
|
||||
import { uploadFileOpenGroupV2 } from '../../opengroup/opengroupV2/OpenGroupAPIV2';
|
||||
|
||||
interface UploadParamsV2 {
|
||||
attachment: Attachment;
|
||||
openGroup: OpenGroupRequestCommonType;
|
||||
}
|
||||
|
||||
interface RawPreview {
|
||||
url?: string;
|
||||
title?: string;
|
||||
image: Attachment;
|
||||
}
|
||||
|
||||
interface RawQuoteAttachment {
|
||||
contentType?: string;
|
||||
fileName?: string;
|
||||
thumbnail?: Attachment;
|
||||
}
|
||||
|
||||
interface RawQuote {
|
||||
id?: number;
|
||||
author?: string;
|
||||
text?: string;
|
||||
attachments?: Array<RawQuoteAttachment>;
|
||||
}
|
||||
|
||||
const PADDING_BYTE = 0;
|
||||
|
||||
export async function uploadV2(params: UploadParamsV2): Promise<AttachmentPointer> {
|
||||
const { attachment, openGroup } = params;
|
||||
if (typeof attachment !== 'object' || attachment == null) {
|
||||
throw new Error('Invalid attachment passed.');
|
||||
}
|
||||
|
||||
if (!(attachment.data instanceof ArrayBuffer)) {
|
||||
throw new TypeError(
|
||||
`attachment.data must be an ArrayBuffer but got: ${typeof attachment.data}`
|
||||
);
|
||||
}
|
||||
|
||||
const pointer: AttachmentPointer = {
|
||||
contentType: attachment.contentType || undefined,
|
||||
size: attachment.size,
|
||||
fileName: attachment.fileName,
|
||||
flags: attachment.flags,
|
||||
caption: attachment.caption,
|
||||
};
|
||||
|
||||
const paddedAttachment: ArrayBuffer =
|
||||
(window.lokiFeatureFlags.padOutgoingAttachments &&
|
||||
AttachmentUtils.addAttachmentPadding(attachment.data)) ||
|
||||
attachment.data;
|
||||
|
||||
const fileId = await uploadFileOpenGroupV2(new Uint8Array(paddedAttachment), openGroup);
|
||||
|
||||
pointer.id = fileId || undefined;
|
||||
console.warn('should we set the URL too here for that v2?');
|
||||
|
||||
return pointer;
|
||||
}
|
||||
|
||||
export async function uploadAttachmentsV2(
|
||||
attachments: Array<Attachment>,
|
||||
openGroup: OpenGroupRequestCommonType
|
||||
): Promise<Array<AttachmentPointer>> {
|
||||
const promises = (attachments || []).map(async attachment =>
|
||||
exports.uploadV2({
|
||||
attachment,
|
||||
openGroup,
|
||||
})
|
||||
);
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
export async function uploadLinkPreviewsV2(
|
||||
previews: Array<RawPreview>,
|
||||
openGroup: OpenGroupRequestCommonType
|
||||
): Promise<Array<Preview>> {
|
||||
const promises = (previews || []).map(async item => {
|
||||
// some links does not have an image associated, and it makes the whole message fail to send
|
||||
if (!item.image) {
|
||||
return item;
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
image: await exports.uploadV2({
|
||||
attachment: item.image,
|
||||
openGroup,
|
||||
}),
|
||||
};
|
||||
});
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
export async function uploadQuoteThumbnailsV2(
|
||||
openGroup: OpenGroupRequestCommonType,
|
||||
quote?: RawQuote
|
||||
): Promise<Quote | undefined> {
|
||||
if (!quote) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const promises = (quote.attachments ?? []).map(async attachment => {
|
||||
let thumbnail: AttachmentPointer | undefined;
|
||||
if (attachment.thumbnail) {
|
||||
thumbnail = await exports.uploadV2({
|
||||
attachment: attachment.thumbnail,
|
||||
openGroup,
|
||||
});
|
||||
}
|
||||
return {
|
||||
...attachment,
|
||||
thumbnail,
|
||||
} as QuotedAttachment;
|
||||
});
|
||||
|
||||
const attachments = await Promise.all(promises);
|
||||
|
||||
return {
|
||||
...quote,
|
||||
attachments,
|
||||
};
|
||||
}
|
|
@ -32,7 +32,6 @@ function getEncryptionTypeFromMessageType(message: ContentMessage): EncryptionTy
|
|||
export async function toRawMessage(device: PubKey, message: ContentMessage): Promise<RawMessage> {
|
||||
const timestamp = message.timestamp;
|
||||
const ttl = message.ttl();
|
||||
// window?.log?.debug('toRawMessage proto:', message.contentProto());
|
||||
const plainTextBuffer = message.plainTextBuffer();
|
||||
|
||||
const encryption = getEncryptionTypeFromMessageType(message);
|
||||
|
|
|
@ -8,6 +8,7 @@ import * as MenuUtils from '../../components/session/menu/Menu';
|
|||
import * as ToastUtils from './Toast';
|
||||
import * as UserUtils from './User';
|
||||
import * as SyncUtils from './syncUtils';
|
||||
import * as AttachmentsV2Utils from './AttachmentsV2';
|
||||
|
||||
export * from './Attachments';
|
||||
export * from './TypedEmitter';
|
||||
|
@ -24,4 +25,5 @@ export {
|
|||
ToastUtils,
|
||||
UserUtils,
|
||||
SyncUtils,
|
||||
AttachmentsV2Utils,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue