Новый тип вложений - Attachment.CALL с активными звонками
This commit is contained in:
160
app/providers/DialogProvider/useDeattachedSender.ts
Normal file
160
app/providers/DialogProvider/useDeattachedSender.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
import { generateRandomKey } from "@/app/utils/utils";
|
||||
import { Attachment, AttachmentType, PacketMessage } from "../ProtocolProvider/protocol/packets/packet.message";
|
||||
import { useGroups } from "./useGroups";
|
||||
import { chacha20Encrypt, encodeWithPassword, encrypt, generateMd5 } from "@/app/workers/crypto/crypto";
|
||||
import { usePrivatePlain } from "../AccountProvider/usePrivatePlain";
|
||||
import { useConsoleLogger } from "@/app/hooks/useConsoleLogger";
|
||||
import { AttachmentMeta, DeliveredMessageState, emitDialogMessage, Message } from "./DialogProvider";
|
||||
import { useDatabase } from "../DatabaseProvider/useDatabase";
|
||||
import { useFileStorage } from "@/app/hooks/useFileStorage";
|
||||
import { usePublicKey } from "../AccountProvider/usePublicKey";
|
||||
import { ProtocolState } from "../ProtocolProvider/ProtocolProvider";
|
||||
import { useDialogsList } from "../DialogListProvider/useDialogsList";
|
||||
import { useProtocolState } from "../ProtocolProvider/useProtocolState";
|
||||
import { usePrivateKeyHash } from "../AccountProvider/usePrivateKeyHash";
|
||||
import { useSender } from "../ProtocolProvider/useSender";
|
||||
import { usePrepareAttachment } from "../AttachmentProvider/usePrepareAttachment";
|
||||
|
||||
/**
|
||||
* Используется для отправки сообщений не внутри DialogProvider, а например в CallProvider,
|
||||
* когда нам нужно отправить сообщение от своего имени что мы совершли звонок (Attachment.CALL)
|
||||
*/
|
||||
export function useDeattachedSender() {
|
||||
const {hasGroup, getGroupKey} = useGroups();
|
||||
const privatePlain = usePrivatePlain();
|
||||
const {warn} = useConsoleLogger('useDeattachedSender');
|
||||
const {runQuery} = useDatabase();
|
||||
const {writeFile} = useFileStorage();
|
||||
const publicKey = usePublicKey();
|
||||
const {updateDialog} = useDialogsList();
|
||||
const [protocolState] = useProtocolState();
|
||||
const privateKey = usePrivateKeyHash();
|
||||
const send = useSender();
|
||||
const {prepareAttachmentsToSend} = usePrepareAttachment();
|
||||
|
||||
/**
|
||||
* Отправка сообщения в диалог
|
||||
* @param dialog ID диалога, может быть как публичным ключом собеседника, так и ID группового диалога
|
||||
* @param message Сообщение
|
||||
* @param attachemnts Вложения
|
||||
*/
|
||||
const sendMessage = async (dialog: string, message: string, attachemnts : Attachment[], serverSent: boolean = false) => {
|
||||
const messageId = generateRandomKey(16);
|
||||
|
||||
|
||||
let cahchaEncrypted = {ciphertext: "", key: "", nonce: ""} as any;
|
||||
let key = Buffer.from("");
|
||||
let encryptedKey = "";
|
||||
let plainMessage = "";
|
||||
let content = "";
|
||||
|
||||
if(!hasGroup(dialog)){
|
||||
cahchaEncrypted = (await chacha20Encrypt(message.trim()) as any);
|
||||
key = Buffer.concat([
|
||||
Buffer.from(cahchaEncrypted.key, "hex"),
|
||||
Buffer.from(cahchaEncrypted.nonce, "hex")]);
|
||||
encryptedKey = await encrypt(key.toString('binary'), dialog);
|
||||
plainMessage = await encodeWithPassword(privatePlain, message.trim());
|
||||
content = cahchaEncrypted.ciphertext;
|
||||
}else{
|
||||
/**
|
||||
* Это группа, там шифрование устроено иначе
|
||||
* для групп используется один общий ключ, который
|
||||
* есть только у участников группы, сам ключ при этом никак
|
||||
* не отправляется по сети (ведь ID у группы общий и у каждого
|
||||
* и так есть этот ключ)
|
||||
*/
|
||||
const groupKey = await getGroupKey(dialog);
|
||||
if(!groupKey){
|
||||
warn("Group key not found for dialog " + dialog);
|
||||
return;
|
||||
}
|
||||
content = await encodeWithPassword(groupKey, message.trim());
|
||||
plainMessage = await encodeWithPassword(privatePlain, message.trim());
|
||||
encryptedKey = ""; // В группах не нужен зашифрованный ключ
|
||||
key = Buffer.from(groupKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Нужно зашифровать ключ еще и нашим ключом,
|
||||
* чтобы в последствии мы могли расшифровать этот ключ у своих
|
||||
* же сообщений (смотреть problem_sync.md)
|
||||
*/
|
||||
const aesChachaKey = await encodeWithPassword(privatePlain, key.toString('binary'));
|
||||
|
||||
emitDialogMessage({
|
||||
dialogId: dialog,
|
||||
message: {
|
||||
from_public_key: publicKey,
|
||||
to_public_key: dialog,
|
||||
content: content,
|
||||
timestamp: Date.now(),
|
||||
readed: publicKey == dialog ? 1 : 0,
|
||||
chacha_key: "",
|
||||
from_me: 1,
|
||||
plain_message: message,
|
||||
delivered: serverSent ? (publicKey == dialog ? DeliveredMessageState.DELIVERED : DeliveredMessageState.WAITING) : DeliveredMessageState.DELIVERED,
|
||||
message_id: messageId,
|
||||
attachments: attachemnts
|
||||
} as Message
|
||||
})
|
||||
|
||||
|
||||
let attachmentsMeta : AttachmentMeta[] = [];
|
||||
for(let i = 0; i < attachemnts.length; i++) {
|
||||
const attachment = attachemnts[i];
|
||||
attachmentsMeta.push({
|
||||
id: attachment.id,
|
||||
type: attachment.type,
|
||||
preview: attachment.preview
|
||||
});
|
||||
if(attachment.type == AttachmentType.FILE){
|
||||
/**
|
||||
* Обычно вложения дублируются на диск. Так происходит со всем.
|
||||
* Кроме файлов. Если дублировать файл весом в 2гб на диск отправка будет
|
||||
* занимать очень много времени.
|
||||
* К тому же, это приведет к созданию ненужной копии у отправителя
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
writeFile(`m/${await generateMd5(attachment.id + publicKey)}`, Buffer.from(await encodeWithPassword(privatePlain, attachment.blob)).toString('binary'));
|
||||
}
|
||||
|
||||
await runQuery(`
|
||||
INSERT INTO messages
|
||||
(from_public_key, to_public_key, content, timestamp, read, chacha_key, from_me, plain_message, account, message_id, delivered, attachments) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`, [publicKey, dialog, content, Date.now(), publicKey == dialog ? 1 : 0, encryptedKey, 1, plainMessage, publicKey, messageId, publicKey == dialog ? DeliveredMessageState.DELIVERED : (
|
||||
(serverSent ? (protocolState != ProtocolState.CONNECTED ? DeliveredMessageState.ERROR : DeliveredMessageState.WAITING) : DeliveredMessageState.DELIVERED)
|
||||
), JSON.stringify(attachmentsMeta)]);
|
||||
updateDialog(dialog);
|
||||
if(publicKey == ""
|
||||
|| dialog == ""
|
||||
|| publicKey == dialog) {
|
||||
return;
|
||||
}
|
||||
let preparedToNetworkSendAttachements : Attachment[] = await prepareAttachmentsToSend(messageId, dialog, key.toString('utf-8'), attachemnts);
|
||||
if(attachemnts.length <= 0 && message.trim() == ""){
|
||||
runQuery("UPDATE messages SET delivered = ? WHERE message_id = ?", [DeliveredMessageState.ERROR, messageId]);
|
||||
updateDialog(dialog);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!serverSent){
|
||||
return;
|
||||
}
|
||||
|
||||
const packet = new PacketMessage();
|
||||
packet.setFromPublicKey(publicKey);
|
||||
packet.setToPublicKey(dialog);
|
||||
packet.setContent(content);
|
||||
packet.setChachaKey(encryptedKey);
|
||||
packet.setPrivateKey(privateKey);
|
||||
packet.setMessageId(messageId);
|
||||
packet.setTimestamp(Date.now());
|
||||
packet.setAttachments(preparedToNetworkSendAttachements);
|
||||
packet.setAesChachaKey(aesChachaKey);
|
||||
send(packet);
|
||||
}
|
||||
|
||||
return {sendMessage};
|
||||
}
|
||||
Reference in New Issue
Block a user