Промежуточный этап синхронизации
This commit is contained in:
@@ -24,6 +24,7 @@ import { useGroups } from "./useGroups";
|
||||
import { useDialogState } from "../DialogStateProvider.tsx/useDialogState";
|
||||
import { useUserInformation } from "../InformationProvider/useUserInformation";
|
||||
import { useMentions } from "../DialogStateProvider.tsx/useMentions";
|
||||
import { runTaskInQueue } from "./dialogQueue";
|
||||
|
||||
/**
|
||||
* При вызове будет запущен "фоновый" обработчик
|
||||
@@ -41,14 +42,14 @@ export function useDialogFiber() {
|
||||
const notify = useNotification();
|
||||
const focused = useWindowFocus();
|
||||
const { getDialogCache, addOrUpdateDialogCache } = useDialogsCache();
|
||||
const {info, error} = useConsoleLogger('useDialogFiber');
|
||||
const { info, error } = useConsoleLogger('useDialogFiber');
|
||||
const [viewState] = useViewPanelsState();
|
||||
const {writeFile} = useFileStorage();
|
||||
const {updateDialog} = useDialogsList();
|
||||
const {hasGroup, getGroupKey, normalize} = useGroups();
|
||||
const {muted} = useDialogState();
|
||||
const { writeFile } = useFileStorage();
|
||||
const { updateDialog } = useDialogsList();
|
||||
const { hasGroup, getGroupKey, normalize } = useGroups();
|
||||
const { muted } = useDialogState();
|
||||
const [userInfo] = useUserInformation(publicKey);
|
||||
const {pushMention} = useMentions();
|
||||
const { pushMention } = useMentions();
|
||||
|
||||
/**
|
||||
* Лог
|
||||
@@ -64,486 +65,501 @@ export function useDialogFiber() {
|
||||
* Метод нужен для синхронизации своих сообщений
|
||||
*/
|
||||
usePacket(0x06, async (packet: PacketMessage) => {
|
||||
const fromPublicKey = packet.getFromPublicKey();
|
||||
const toPublicKey = packet.getToPublicKey();
|
||||
const aesChachaKey = packet.getAesChachaKey();
|
||||
const content = packet.getContent();
|
||||
const timestamp = packet.getTimestamp();
|
||||
const messageId = packet.getMessageId();
|
||||
|
||||
|
||||
if(fromPublicKey != publicKey){
|
||||
/**
|
||||
* Игнорируем если это не сообщение от нас
|
||||
*/
|
||||
return;
|
||||
}
|
||||
const chachaDecryptedKey = Buffer.from(await decodeWithPassword(privatePlain, aesChachaKey), "binary");
|
||||
const key = chachaDecryptedKey.slice(0, 32);
|
||||
const nonce = chachaDecryptedKey.slice(32);
|
||||
const decryptedContent = await chacha20Decrypt(content, nonce.toString('hex'), key.toString('hex'));
|
||||
runTaskInQueue(async () => {
|
||||
const fromPublicKey = packet.getFromPublicKey();
|
||||
const toPublicKey = packet.getToPublicKey();
|
||||
const aesChachaKey = packet.getAesChachaKey();
|
||||
const content = packet.getContent();
|
||||
const timestamp = packet.getTimestamp();
|
||||
const messageId = packet.getMessageId();
|
||||
|
||||
let attachmentsMeta: any[] = [];
|
||||
let messageAttachments: Attachment[] = [];
|
||||
for (let i = 0; i < packet.getAttachments().length; i++) {
|
||||
const attachment = packet.getAttachments()[i];
|
||||
log("Attachment received id " + attachment.id + " type " + attachment.type);
|
||||
|
||||
let nextLength = messageAttachments.push({
|
||||
...attachment,
|
||||
blob: ""
|
||||
});
|
||||
|
||||
if(attachment.type == AttachmentType.MESSAGES){
|
||||
if (fromPublicKey != publicKey) {
|
||||
/**
|
||||
* Этот тип вложения приходит сразу в blob и не нуждается
|
||||
* в последующем скачивании
|
||||
* Игнорируем если это не сообщение от нас
|
||||
*/
|
||||
const decryptedBlob = await decodeWithPassword(chachaDecryptedKey.toString('utf-8'), attachment.blob);
|
||||
writeFile(`m/${await generateMd5(attachment.id + publicKey)}`,
|
||||
Buffer.from(await encodeWithPassword(privatePlain, decryptedBlob)).toString('binary'));
|
||||
messageAttachments[nextLength - 1].blob = decryptedBlob;
|
||||
return;
|
||||
}
|
||||
const chachaDecryptedKey = Buffer.from(await decodeWithPassword(privatePlain, aesChachaKey), "binary");
|
||||
const key = chachaDecryptedKey.slice(0, 32);
|
||||
const nonce = chachaDecryptedKey.slice(32);
|
||||
const decryptedContent = await chacha20Decrypt(content, nonce.toString('hex'), key.toString('hex'));
|
||||
|
||||
let attachmentsMeta: any[] = [];
|
||||
let messageAttachments: Attachment[] = [];
|
||||
for (let i = 0; i < packet.getAttachments().length; i++) {
|
||||
const attachment = packet.getAttachments()[i];
|
||||
log("Attachment received id " + attachment.id + " type " + attachment.type);
|
||||
|
||||
let nextLength = messageAttachments.push({
|
||||
...attachment,
|
||||
blob: ""
|
||||
});
|
||||
|
||||
if (attachment.type == AttachmentType.MESSAGES) {
|
||||
/**
|
||||
* Этот тип вложения приходит сразу в blob и не нуждается
|
||||
* в последующем скачивании
|
||||
*/
|
||||
const decryptedBlob = await decodeWithPassword(chachaDecryptedKey.toString('utf-8'), attachment.blob);
|
||||
writeFile(`m/${await generateMd5(attachment.id + publicKey)}`,
|
||||
Buffer.from(await encodeWithPassword(privatePlain, decryptedBlob)).toString('binary'));
|
||||
messageAttachments[nextLength - 1].blob = decryptedBlob;
|
||||
}
|
||||
|
||||
attachmentsMeta.push({
|
||||
id: attachment.id,
|
||||
type: attachment.type,
|
||||
preview: attachment.preview
|
||||
});
|
||||
}
|
||||
|
||||
attachmentsMeta.push({
|
||||
id: attachment.id,
|
||||
type: attachment.type,
|
||||
preview: attachment.preview
|
||||
});
|
||||
}
|
||||
const newMessage: Message = {
|
||||
from_public_key: fromPublicKey,
|
||||
to_public_key: toPublicKey,
|
||||
content: content,
|
||||
timestamp: timestamp,
|
||||
readed: 1, //сообщение прочитано
|
||||
chacha_key: chachaDecryptedKey.toString('utf-8'),
|
||||
from_me: 1, //сообщение от нас
|
||||
plain_message: (decryptedContent as string),
|
||||
delivered: DeliveredMessageState.DELIVERED,
|
||||
message_id: messageId,
|
||||
attachments: messageAttachments
|
||||
};
|
||||
|
||||
const newMessage: Message = {
|
||||
from_public_key: fromPublicKey,
|
||||
to_public_key: toPublicKey,
|
||||
content: content,
|
||||
timestamp: timestamp,
|
||||
readed: 1, //сообщение прочитано
|
||||
chacha_key: chachaDecryptedKey.toString('utf-8'),
|
||||
from_me: 1, //сообщение от нас
|
||||
plain_message: (decryptedContent as string),
|
||||
delivered: DeliveredMessageState.DELIVERED,
|
||||
message_id: messageId,
|
||||
attachments: messageAttachments
|
||||
};
|
||||
|
||||
await runQuery(`
|
||||
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`, [fromPublicKey,
|
||||
toPublicKey,
|
||||
content,
|
||||
timestamp,
|
||||
0, //по умолчанию не прочитаны
|
||||
'',
|
||||
1, //Свои же сообщения всегда от нас
|
||||
await encodeWithPassword(privatePlain, decryptedContent),
|
||||
publicKey,
|
||||
messageId,
|
||||
DeliveredMessageState.DELIVERED,
|
||||
JSON.stringify(attachmentsMeta)]);
|
||||
`, [fromPublicKey,
|
||||
toPublicKey,
|
||||
content,
|
||||
timestamp,
|
||||
0, //по умолчанию не прочитаны
|
||||
'',
|
||||
1, //Свои же сообщения всегда от нас
|
||||
await encodeWithPassword(privatePlain, decryptedContent),
|
||||
publicKey,
|
||||
messageId,
|
||||
DeliveredMessageState.DELIVERED,
|
||||
JSON.stringify(attachmentsMeta)]);
|
||||
|
||||
updateDialog(toPublicKey);
|
||||
updateDialog(toPublicKey);
|
||||
|
||||
let dialogCache = getDialogCache(toPublicKey);
|
||||
if (currentDialogPublicKeyView !== toPublicKey && dialogCache.length > 0) {
|
||||
addOrUpdateDialogCache(toPublicKey, [...dialogCache, newMessage].slice(-MESSAGE_MAX_LOADED));
|
||||
}
|
||||
let dialogCache = getDialogCache(toPublicKey);
|
||||
if (currentDialogPublicKeyView !== toPublicKey && dialogCache.length > 0) {
|
||||
addOrUpdateDialogCache(toPublicKey, [...dialogCache, newMessage].slice(-MESSAGE_MAX_LOADED));
|
||||
}
|
||||
});
|
||||
}, [privatePlain, currentDialogPublicKeyView]);
|
||||
|
||||
/**
|
||||
* Обработчик сообщений для группы
|
||||
*/
|
||||
usePacket(0x06, async (packet: PacketMessage) => {
|
||||
const fromPublicKey = packet.getFromPublicKey();
|
||||
const toPublicKey = packet.getToPublicKey();
|
||||
const content = packet.getContent();
|
||||
const timestamp = packet.getTimestamp();
|
||||
const messageId = packet.getMessageId();
|
||||
if(!hasGroup(toPublicKey)){
|
||||
/**
|
||||
* Если это личное сообщение, то игнорируем его здесь
|
||||
* для него есть отдельный слушатель usePacket (снизу)
|
||||
*/
|
||||
return;
|
||||
}
|
||||
if(fromPublicKey == publicKey){
|
||||
/**
|
||||
* Игнорируем свои же сообщения,
|
||||
* такое получается при пакете синхронизации
|
||||
*/
|
||||
return;
|
||||
}
|
||||
const groupKey = await getGroupKey(toPublicKey);
|
||||
if(!groupKey){
|
||||
log("Group key not found for group " + toPublicKey);
|
||||
error("Message dropped because group key not found for group " + toPublicKey);
|
||||
return;
|
||||
}
|
||||
info("New group message packet received from " + fromPublicKey);
|
||||
|
||||
let decryptedContent = '';
|
||||
|
||||
try{
|
||||
decryptedContent = await decodeWithPassword(groupKey, content);
|
||||
}catch(e) {
|
||||
decryptedContent = '';
|
||||
}
|
||||
|
||||
let attachmentsMeta: any[] = [];
|
||||
let messageAttachments: Attachment[] = [];
|
||||
for (let i = 0; i < packet.getAttachments().length; i++) {
|
||||
const attachment = packet.getAttachments()[i];
|
||||
log("Attachment received id " + attachment.id + " type " + attachment.type);
|
||||
|
||||
let nextLength = messageAttachments.push({
|
||||
...attachment,
|
||||
blob: ""
|
||||
});
|
||||
|
||||
if(attachment.type == AttachmentType.MESSAGES){
|
||||
runTaskInQueue(async () => {
|
||||
const fromPublicKey = packet.getFromPublicKey();
|
||||
const toPublicKey = packet.getToPublicKey();
|
||||
const content = packet.getContent();
|
||||
const timestamp = packet.getTimestamp();
|
||||
const messageId = packet.getMessageId();
|
||||
if (!hasGroup(toPublicKey)) {
|
||||
/**
|
||||
* Этот тип вложения приходит сразу в blob и не нуждается
|
||||
* в последующем скачивании
|
||||
* Если это личное сообщение, то игнорируем его здесь
|
||||
* для него есть отдельный слушатель usePacket (снизу)
|
||||
*/
|
||||
const decryptedBlob = await decodeWithPassword(groupKey, attachment.blob);
|
||||
writeFile(`m/${await generateMd5(attachment.id + publicKey)}`,
|
||||
Buffer.from(await encodeWithPassword(privatePlain, decryptedBlob)).toString('binary'));
|
||||
messageAttachments[nextLength - 1].blob = decryptedBlob;
|
||||
return;
|
||||
}
|
||||
if (fromPublicKey == publicKey) {
|
||||
/**
|
||||
* Игнорируем свои же сообщения,
|
||||
* такое получается при пакете синхронизации
|
||||
*/
|
||||
return;
|
||||
}
|
||||
const groupKey = await getGroupKey(toPublicKey);
|
||||
if (!groupKey) {
|
||||
log("Group key not found for group " + toPublicKey);
|
||||
error("Message dropped because group key not found for group " + toPublicKey);
|
||||
return;
|
||||
}
|
||||
info("New group message packet received from " + fromPublicKey);
|
||||
|
||||
let decryptedContent = '';
|
||||
|
||||
try {
|
||||
decryptedContent = await decodeWithPassword(groupKey, content);
|
||||
} catch (e) {
|
||||
decryptedContent = '';
|
||||
}
|
||||
|
||||
attachmentsMeta.push({
|
||||
id: attachment.id,
|
||||
type: attachment.type,
|
||||
preview: attachment.preview
|
||||
});
|
||||
}
|
||||
let attachmentsMeta: any[] = [];
|
||||
let messageAttachments: Attachment[] = [];
|
||||
for (let i = 0; i < packet.getAttachments().length; i++) {
|
||||
const attachment = packet.getAttachments()[i];
|
||||
log("Attachment received id " + attachment.id + " type " + attachment.type);
|
||||
|
||||
const newMessage: Message = {
|
||||
from_public_key: fromPublicKey,
|
||||
to_public_key: toPublicKey,
|
||||
content: content,
|
||||
timestamp: timestamp,
|
||||
readed: idle ? 0 : 1,
|
||||
chacha_key: groupKey,
|
||||
from_me: fromPublicKey == publicKey ? 1 : 0,
|
||||
plain_message: decryptedContent,
|
||||
delivered: DeliveredMessageState.DELIVERED,
|
||||
message_id: messageId,
|
||||
attachments: messageAttachments
|
||||
};
|
||||
let nextLength = messageAttachments.push({
|
||||
...attachment,
|
||||
blob: ""
|
||||
});
|
||||
|
||||
await runQuery(`
|
||||
if (attachment.type == AttachmentType.MESSAGES) {
|
||||
/**
|
||||
* Этот тип вложения приходит сразу в blob и не нуждается
|
||||
* в последующем скачивании
|
||||
*/
|
||||
const decryptedBlob = await decodeWithPassword(groupKey, attachment.blob);
|
||||
writeFile(`m/${await generateMd5(attachment.id + publicKey)}`,
|
||||
Buffer.from(await encodeWithPassword(privatePlain, decryptedBlob)).toString('binary'));
|
||||
messageAttachments[nextLength - 1].blob = decryptedBlob;
|
||||
}
|
||||
|
||||
attachmentsMeta.push({
|
||||
id: attachment.id,
|
||||
type: attachment.type,
|
||||
preview: attachment.preview
|
||||
});
|
||||
}
|
||||
|
||||
const newMessage: Message = {
|
||||
from_public_key: fromPublicKey,
|
||||
to_public_key: toPublicKey,
|
||||
content: content,
|
||||
timestamp: timestamp,
|
||||
readed: idle ? 0 : 1,
|
||||
chacha_key: groupKey,
|
||||
from_me: fromPublicKey == publicKey ? 1 : 0,
|
||||
plain_message: decryptedContent,
|
||||
delivered: DeliveredMessageState.DELIVERED,
|
||||
message_id: messageId,
|
||||
attachments: messageAttachments
|
||||
};
|
||||
|
||||
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`, [fromPublicKey,
|
||||
toPublicKey,
|
||||
content,
|
||||
timestamp,
|
||||
/**если текущий открытый диалог == беседе (которая приходит в toPublicKey) */
|
||||
(currentDialogPublicKeyView == toPublicKey && !idle && viewState != ViewPanelsState.DIALOGS_PANEL_ONLY) ? 1 : 0,
|
||||
'',
|
||||
0,
|
||||
await encodeWithPassword(privatePlain, decryptedContent),
|
||||
publicKey,
|
||||
messageId,
|
||||
DeliveredMessageState.DELIVERED,
|
||||
JSON.stringify(attachmentsMeta)]);
|
||||
`, [fromPublicKey,
|
||||
toPublicKey,
|
||||
content,
|
||||
timestamp,
|
||||
/**если текущий открытый диалог == беседе (которая приходит в toPublicKey) */
|
||||
(currentDialogPublicKeyView == toPublicKey && !idle && viewState != ViewPanelsState.DIALOGS_PANEL_ONLY) ? 1 : 0,
|
||||
'',
|
||||
0,
|
||||
await encodeWithPassword(privatePlain, decryptedContent),
|
||||
publicKey,
|
||||
messageId,
|
||||
DeliveredMessageState.DELIVERED,
|
||||
JSON.stringify(attachmentsMeta)]);
|
||||
|
||||
/**
|
||||
* Так как у нас в toPublicKey приходит ID группы,
|
||||
* то обновляем диалог по этому ID, а не по fromPublicKey
|
||||
* как это сделано в личных сообщениях
|
||||
*/
|
||||
updateDialog(toPublicKey);
|
||||
|
||||
if (((normalize(currentDialogPublicKeyView) !== normalize(toPublicKey) || viewState == ViewPanelsState.DIALOGS_PANEL_ONLY) &&
|
||||
(timestamp + TIME_TO_INACTIVE_FOR_MESSAGES_UNREAD) > (Date.now() / 1000)) || !focused) {
|
||||
/**
|
||||
* Условие со временем нужно для того,
|
||||
* чтобы когда приходит пачка сообщений с сервера в момент того как
|
||||
* пользователь был неактивен, не слать уведомления по всем этим сообщениям
|
||||
* Так как у нас в toPublicKey приходит ID группы,
|
||||
* то обновляем диалог по этому ID, а не по fromPublicKey
|
||||
* как это сделано в личных сообщениях
|
||||
*/
|
||||
let mentionFlag = false;
|
||||
if((newMessage.from_public_key != publicKey) && (decryptedContent.includes(`@${userInfo.username}`) || decryptedContent.includes(`@all`))){
|
||||
/**
|
||||
* Если в сообщении есть упоминание текущего пользователя или @all,
|
||||
* при этом сообщение отправляли не мы,
|
||||
* то добавляем упоминание в состояние диалога.
|
||||
*
|
||||
* TODO: сделать чтобы all работал только для админов группы
|
||||
*/
|
||||
mentionFlag = true;
|
||||
}
|
||||
updateDialog(toPublicKey);
|
||||
|
||||
if(!muted.includes(toPublicKey) || mentionFlag){
|
||||
if (((normalize(currentDialogPublicKeyView) !== normalize(toPublicKey) || viewState == ViewPanelsState.DIALOGS_PANEL_ONLY) &&
|
||||
(timestamp + TIME_TO_INACTIVE_FOR_MESSAGES_UNREAD) > (Date.now() / 1000)) || !focused) {
|
||||
/**
|
||||
* Если группа не в мутие или есть упоминание - отправляем уведомление
|
||||
* Условие со временем нужно для того,
|
||||
* чтобы когда приходит пачка сообщений с сервера в момент того как
|
||||
* пользователь был неактивен, не слать уведомления по всем этим сообщениям
|
||||
*/
|
||||
notify("New message", "You have a new message");
|
||||
let mentionFlag = false;
|
||||
if ((newMessage.from_public_key != publicKey) && (decryptedContent.includes(`@${userInfo.username}`) || decryptedContent.includes(`@all`))) {
|
||||
/**
|
||||
* Если в сообщении есть упоминание текущего пользователя или @all,
|
||||
* при этом сообщение отправляли не мы,
|
||||
* то добавляем упоминание в состояние диалога.
|
||||
*
|
||||
* TODO: сделать чтобы all работал только для админов группы
|
||||
*/
|
||||
mentionFlag = true;
|
||||
}
|
||||
|
||||
if (!muted.includes(toPublicKey) || mentionFlag) {
|
||||
/**
|
||||
* Если группа не в мутие или есть упоминание - отправляем уведомление
|
||||
*/
|
||||
notify("New message", "You have a new message");
|
||||
}
|
||||
if (mentionFlag) {
|
||||
/**
|
||||
* Если в сообщении есть упоминание текущего пользователя или @all,
|
||||
* то добавляем упоминание в состояние диалога
|
||||
*
|
||||
* TODO: сделать чтобы all работал только для админов группы
|
||||
*/
|
||||
pushMention({
|
||||
dialog_id: toPublicKey,
|
||||
message_id: messageId
|
||||
});
|
||||
}
|
||||
}
|
||||
if(mentionFlag){
|
||||
/**
|
||||
* Если в сообщении есть упоминание текущего пользователя или @all,
|
||||
* то добавляем упоминание в состояние диалога
|
||||
*
|
||||
* TODO: сделать чтобы all работал только для админов группы
|
||||
*/
|
||||
pushMention({
|
||||
dialog_id: toPublicKey,
|
||||
message_id: messageId
|
||||
});
|
||||
}
|
||||
}
|
||||
let dialogCache = getDialogCache(toPublicKey);
|
||||
if (currentDialogPublicKeyView !== toPublicKey && dialogCache.length > 0) {
|
||||
addOrUpdateDialogCache(toPublicKey, [...dialogCache, newMessage].slice(-MESSAGE_MAX_LOADED));
|
||||
}
|
||||
let dialogCache = getDialogCache(toPublicKey);
|
||||
if (currentDialogPublicKeyView !== toPublicKey && dialogCache.length > 0) {
|
||||
addOrUpdateDialogCache(toPublicKey, [...dialogCache, newMessage].slice(-MESSAGE_MAX_LOADED));
|
||||
}
|
||||
});
|
||||
}, [blocked, muted, updateDialog, focused, currentDialogPublicKeyView, viewState, idle]);
|
||||
/**
|
||||
* Обработчик личных сообщений
|
||||
*/
|
||||
usePacket(0x06, async (packet: PacketMessage) => {
|
||||
const fromPublicKey = packet.getFromPublicKey();
|
||||
if(fromPublicKey == publicKey){
|
||||
/**
|
||||
* Игнорируем свои же сообщения,
|
||||
* такое получается при пакете синхронизации
|
||||
*/
|
||||
return;
|
||||
}
|
||||
const toPublicKey = packet.getToPublicKey();
|
||||
const content = packet.getContent();
|
||||
const chachaKey = packet.getChachaKey();
|
||||
const timestamp = packet.getTimestamp();
|
||||
const messageId = packet.getMessageId();
|
||||
if(hasGroup(toPublicKey)){
|
||||
/**
|
||||
* Если это групповое сообщение, то игнорируем его здесь
|
||||
* для него есть отдельный слушатель usePacket
|
||||
*/
|
||||
return;
|
||||
}
|
||||
info("New message packet received from " + fromPublicKey);
|
||||
if (blocked.includes(fromPublicKey)) {
|
||||
/**
|
||||
* Если пользователь заблокирован и это не групповое сообщение,
|
||||
* то игнорируем сообщение
|
||||
*/
|
||||
log("Message from blocked user, ignore " + fromPublicKey);
|
||||
return;
|
||||
}
|
||||
|
||||
if (privatePlain == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
const chachaDecryptedKey = Buffer.from(await decrypt(chachaKey, privatePlain), "binary");
|
||||
const key = chachaDecryptedKey.slice(0, 32);
|
||||
const nonce = chachaDecryptedKey.slice(32);
|
||||
const decryptedContent = await chacha20Decrypt(content, nonce.toString('hex'), key.toString('hex'));
|
||||
|
||||
let attachmentsMeta: any[] = [];
|
||||
let messageAttachments: Attachment[] = [];
|
||||
for (let i = 0; i < packet.getAttachments().length; i++) {
|
||||
const attachment = packet.getAttachments()[i];
|
||||
log("Attachment received id " + attachment.id + " type " + attachment.type);
|
||||
|
||||
let nextLength = messageAttachments.push({
|
||||
...attachment,
|
||||
blob: ""
|
||||
});
|
||||
|
||||
if(attachment.type == AttachmentType.MESSAGES){
|
||||
runTaskInQueue(async () => {
|
||||
const fromPublicKey = packet.getFromPublicKey();
|
||||
if (fromPublicKey == publicKey) {
|
||||
/**
|
||||
* Этот тип вложения приходит сразу в blob и не нуждается
|
||||
* в последующем скачивании
|
||||
* Игнорируем свои же сообщения,
|
||||
* такое получается при пакете синхронизации
|
||||
*/
|
||||
const decryptedBlob = await decodeWithPassword(chachaDecryptedKey.toString('utf-8'), attachment.blob);
|
||||
writeFile(`m/${await generateMd5(attachment.id + publicKey)}`,
|
||||
Buffer.from(await encodeWithPassword(privatePlain, decryptedBlob)).toString('binary'));
|
||||
messageAttachments[nextLength - 1].blob = decryptedBlob;
|
||||
return;
|
||||
}
|
||||
const toPublicKey = packet.getToPublicKey();
|
||||
const content = packet.getContent();
|
||||
const chachaKey = packet.getChachaKey();
|
||||
const timestamp = packet.getTimestamp();
|
||||
const messageId = packet.getMessageId();
|
||||
if (hasGroup(toPublicKey)) {
|
||||
/**
|
||||
* Если это групповое сообщение, то игнорируем его здесь
|
||||
* для него есть отдельный слушатель usePacket
|
||||
*/
|
||||
return;
|
||||
}
|
||||
info("New message packet received from " + fromPublicKey);
|
||||
if (blocked.includes(fromPublicKey)) {
|
||||
/**
|
||||
* Если пользователь заблокирован и это не групповое сообщение,
|
||||
* то игнорируем сообщение
|
||||
*/
|
||||
log("Message from blocked user, ignore " + fromPublicKey);
|
||||
return;
|
||||
}
|
||||
|
||||
attachmentsMeta.push({
|
||||
id: attachment.id,
|
||||
type: attachment.type,
|
||||
preview: attachment.preview
|
||||
});
|
||||
}
|
||||
if (privatePlain == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
const newMessage: Message = {
|
||||
from_public_key: fromPublicKey,
|
||||
to_public_key: toPublicKey,
|
||||
content: content,
|
||||
timestamp: timestamp,
|
||||
readed: idle ? 0 : 1,
|
||||
chacha_key: chachaDecryptedKey.toString('utf-8'),
|
||||
from_me: fromPublicKey == publicKey ? 1 : 0,
|
||||
plain_message: (decryptedContent as string),
|
||||
delivered: DeliveredMessageState.DELIVERED,
|
||||
message_id: messageId,
|
||||
attachments: messageAttachments
|
||||
};
|
||||
const chachaDecryptedKey = Buffer.from(await decrypt(chachaKey, privatePlain), "binary");
|
||||
const key = chachaDecryptedKey.slice(0, 32);
|
||||
const nonce = chachaDecryptedKey.slice(32);
|
||||
const decryptedContent = await chacha20Decrypt(content, nonce.toString('hex'), key.toString('hex'));
|
||||
|
||||
let attachmentsMeta: any[] = [];
|
||||
let messageAttachments: Attachment[] = [];
|
||||
for (let i = 0; i < packet.getAttachments().length; i++) {
|
||||
const attachment = packet.getAttachments()[i];
|
||||
log("Attachment received id " + attachment.id + " type " + attachment.type);
|
||||
|
||||
let nextLength = messageAttachments.push({
|
||||
...attachment,
|
||||
blob: ""
|
||||
});
|
||||
|
||||
if (attachment.type == AttachmentType.MESSAGES) {
|
||||
/**
|
||||
* Этот тип вложения приходит сразу в blob и не нуждается
|
||||
* в последующем скачивании
|
||||
*/
|
||||
const decryptedBlob = await decodeWithPassword(chachaDecryptedKey.toString('utf-8'), attachment.blob);
|
||||
writeFile(`m/${await generateMd5(attachment.id + publicKey)}`,
|
||||
Buffer.from(await encodeWithPassword(privatePlain, decryptedBlob)).toString('binary'));
|
||||
messageAttachments[nextLength - 1].blob = decryptedBlob;
|
||||
}
|
||||
|
||||
attachmentsMeta.push({
|
||||
id: attachment.id,
|
||||
type: attachment.type,
|
||||
preview: attachment.preview
|
||||
});
|
||||
}
|
||||
|
||||
const newMessage: Message = {
|
||||
from_public_key: fromPublicKey,
|
||||
to_public_key: toPublicKey,
|
||||
content: content,
|
||||
timestamp: timestamp,
|
||||
readed: idle ? 0 : 1,
|
||||
chacha_key: chachaDecryptedKey.toString('utf-8'),
|
||||
from_me: fromPublicKey == publicKey ? 1 : 0,
|
||||
plain_message: (decryptedContent as string),
|
||||
delivered: DeliveredMessageState.DELIVERED,
|
||||
message_id: messageId,
|
||||
attachments: messageAttachments
|
||||
};
|
||||
|
||||
|
||||
await runQuery(`
|
||||
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`, [fromPublicKey,
|
||||
toPublicKey,
|
||||
content,
|
||||
timestamp,
|
||||
(currentDialogPublicKeyView == fromPublicKey && !idle && viewState != ViewPanelsState.DIALOGS_PANEL_ONLY) ? 1 : 0,
|
||||
chachaKey,
|
||||
0,
|
||||
await encodeWithPassword(privatePlain, decryptedContent),
|
||||
publicKey,
|
||||
messageId,
|
||||
DeliveredMessageState.DELIVERED,
|
||||
JSON.stringify(attachmentsMeta)]);
|
||||
`, [fromPublicKey,
|
||||
toPublicKey,
|
||||
content,
|
||||
timestamp,
|
||||
(currentDialogPublicKeyView == fromPublicKey && !idle && viewState != ViewPanelsState.DIALOGS_PANEL_ONLY) ? 1 : 0,
|
||||
chachaKey,
|
||||
0,
|
||||
await encodeWithPassword(privatePlain, decryptedContent),
|
||||
publicKey,
|
||||
messageId,
|
||||
DeliveredMessageState.DELIVERED,
|
||||
JSON.stringify(attachmentsMeta)]);
|
||||
|
||||
log("New message received from " + fromPublicKey);
|
||||
log("New message received from " + fromPublicKey);
|
||||
|
||||
updateDialog(fromPublicKey);
|
||||
if (((currentDialogPublicKeyView !== fromPublicKey || viewState == ViewPanelsState.DIALOGS_PANEL_ONLY) &&
|
||||
(timestamp + TIME_TO_INACTIVE_FOR_MESSAGES_UNREAD) > (Date.now() / 1000)) || !focused) {
|
||||
/**
|
||||
* Условие со временем нужно для того,
|
||||
* чтобы когда приходит пачка сообщений с сервера в момент того как
|
||||
* пользователь был неактивен, не слать уведомления по всем этим сообщениям
|
||||
*/
|
||||
if(!muted.includes(fromPublicKey)){
|
||||
updateDialog(fromPublicKey);
|
||||
if (((currentDialogPublicKeyView !== fromPublicKey || viewState == ViewPanelsState.DIALOGS_PANEL_ONLY) &&
|
||||
(timestamp + TIME_TO_INACTIVE_FOR_MESSAGES_UNREAD) > (Date.now() / 1000)) || !focused) {
|
||||
/**
|
||||
* Если пользователь в муте - не отправляем уведомление
|
||||
* Условие со временем нужно для того,
|
||||
* чтобы когда приходит пачка сообщений с сервера в момент того как
|
||||
* пользователь был неактивен, не слать уведомления по всем этим сообщениям
|
||||
*/
|
||||
notify("New message", "You have a new message");
|
||||
if (!muted.includes(fromPublicKey)) {
|
||||
/**
|
||||
* Если пользователь в муте - не отправляем уведомление
|
||||
*/
|
||||
notify("New message", "You have a new message");
|
||||
}
|
||||
}
|
||||
}
|
||||
let dialogCache = getDialogCache(fromPublicKey);
|
||||
if (currentDialogPublicKeyView !== fromPublicKey && dialogCache.length > 0) {
|
||||
addOrUpdateDialogCache(fromPublicKey, [...dialogCache, newMessage].slice(-MESSAGE_MAX_LOADED));
|
||||
}
|
||||
let dialogCache = getDialogCache(fromPublicKey);
|
||||
if (currentDialogPublicKeyView !== fromPublicKey && dialogCache.length > 0) {
|
||||
addOrUpdateDialogCache(fromPublicKey, [...dialogCache, newMessage].slice(-MESSAGE_MAX_LOADED));
|
||||
}
|
||||
});
|
||||
}, [blocked, muted, updateDialog, focused, currentDialogPublicKeyView, viewState, idle]);
|
||||
|
||||
/**
|
||||
* Обработчик синхронизации прочтения личных сообщений
|
||||
*/
|
||||
usePacket(0x07, async (packet: PacketRead) => {
|
||||
if(hasGroup(packet.getToPublicKey())){
|
||||
/**
|
||||
* Если это относится к группам, то игнорируем здесь,
|
||||
* для этого есть отдельный слушатель usePacket ниже
|
||||
*/
|
||||
return;
|
||||
}
|
||||
const fromPublicKey = packet.getFromPublicKey();
|
||||
const toPublicKey = packet.getToPublicKey();
|
||||
if(fromPublicKey != publicKey){
|
||||
/**
|
||||
* Игнорируем если это не синхронизация нашего прочтения
|
||||
*/
|
||||
return;
|
||||
}
|
||||
console.info("PACKED_READ_SYNC");
|
||||
await runQuery(`UPDATE messages SET read = 1 WHERE from_public_key = ? AND to_public_key = ? AND account = ?`,
|
||||
[toPublicKey, fromPublicKey, publicKey]);
|
||||
|
||||
console.info("updating with params ", [fromPublicKey, toPublicKey, publicKey]);
|
||||
updateDialog(toPublicKey);
|
||||
log("Read sync packet from other device");
|
||||
addOrUpdateDialogCache(fromPublicKey, getDialogCache(fromPublicKey).map((message) => {
|
||||
if (message.from_public_key == toPublicKey && !message.readed) {
|
||||
console.info("Marking message as read in cache for dialog with " + fromPublicKey);
|
||||
console.info({fromPublicKey, toPublicKey});
|
||||
return {
|
||||
...message,
|
||||
readed: 1
|
||||
}
|
||||
runTaskInQueue(async () => {
|
||||
if (hasGroup(packet.getToPublicKey())) {
|
||||
/**
|
||||
* Если это относится к группам, то игнорируем здесь,
|
||||
* для этого есть отдельный слушатель usePacket ниже
|
||||
*/
|
||||
return;
|
||||
}
|
||||
return message;
|
||||
}));
|
||||
const fromPublicKey = packet.getFromPublicKey();
|
||||
const toPublicKey = packet.getToPublicKey();
|
||||
if (fromPublicKey != publicKey) {
|
||||
/**
|
||||
* Игнорируем если это не синхронизация нашего прочтения
|
||||
*/
|
||||
return;
|
||||
}
|
||||
console.info("PACKED_READ_SYNC");
|
||||
await runQuery(`UPDATE messages SET read = 1 WHERE from_public_key = ? AND to_public_key = ? AND account = ?`,
|
||||
[toPublicKey, fromPublicKey, publicKey]);
|
||||
|
||||
console.info("updating with params ", [fromPublicKey, toPublicKey, publicKey]);
|
||||
updateDialog(toPublicKey);
|
||||
log("Read sync packet from other device");
|
||||
addOrUpdateDialogCache(fromPublicKey, getDialogCache(fromPublicKey).map((message) => {
|
||||
if (message.from_public_key == toPublicKey && !message.readed) {
|
||||
console.info("Marking message as read in cache for dialog with " + fromPublicKey);
|
||||
console.info({ fromPublicKey, toPublicKey });
|
||||
return {
|
||||
...message,
|
||||
readed: 1
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}));
|
||||
});
|
||||
}, [updateDialog, publicKey]);
|
||||
|
||||
/**
|
||||
* Обработчик прочтения личных сообщений
|
||||
*/
|
||||
usePacket(0x07, async (packet: PacketRead) => {
|
||||
if(hasGroup(packet.getToPublicKey())){
|
||||
/**
|
||||
* Если это относится к группам, то игнорируем здесь,
|
||||
* для этого есть отдельный слушатель usePacket ниже
|
||||
*/
|
||||
return;
|
||||
}
|
||||
const fromPublicKey = packet.getFromPublicKey();
|
||||
const toPublicKey = packet.getToPublicKey();
|
||||
if(fromPublicKey == publicKey){
|
||||
/**
|
||||
* Игнорируем если это наше прочтение
|
||||
* которое получается при синхронизации
|
||||
*/
|
||||
return;
|
||||
}
|
||||
console.info("PACKED_READ_IM");
|
||||
await runQuery(`UPDATE messages SET read = 1 WHERE from_public_key = ? AND to_public_key = ? AND account = ?`, [toPublicKey, fromPublicKey, publicKey]);
|
||||
updateDialog(fromPublicKey);
|
||||
log("Read packet received from " + fromPublicKey + " for " + toPublicKey);
|
||||
addOrUpdateDialogCache(fromPublicKey, getDialogCache(fromPublicKey).map((message) => {
|
||||
if (message.from_public_key == toPublicKey && !message.readed) {
|
||||
console.info("Marking message as read in cache for dialog with " + fromPublicKey);
|
||||
console.info({fromPublicKey, toPublicKey});
|
||||
return {
|
||||
...message,
|
||||
readed: 1
|
||||
}
|
||||
runTaskInQueue(async () => {
|
||||
if (hasGroup(packet.getToPublicKey())) {
|
||||
/**
|
||||
* Если это относится к группам, то игнорируем здесь,
|
||||
* для этого есть отдельный слушатель usePacket ниже
|
||||
*/
|
||||
return;
|
||||
}
|
||||
return message;
|
||||
}));
|
||||
const fromPublicKey = packet.getFromPublicKey();
|
||||
const toPublicKey = packet.getToPublicKey();
|
||||
if (fromPublicKey == publicKey) {
|
||||
/**
|
||||
* Игнорируем если это наше прочтение
|
||||
* которое получается при синхронизации
|
||||
*/
|
||||
return;
|
||||
}
|
||||
console.info("PACKED_READ_IM");
|
||||
await runQuery(`UPDATE messages SET read = 1 WHERE from_public_key = ? AND to_public_key = ? AND account = ?`, [toPublicKey, fromPublicKey, publicKey]);
|
||||
console.info("read im with params ", [fromPublicKey, toPublicKey, publicKey]);
|
||||
updateDialog(fromPublicKey);
|
||||
log("Read packet received from " + fromPublicKey + " for " + toPublicKey);
|
||||
addOrUpdateDialogCache(fromPublicKey, getDialogCache(fromPublicKey).map((message) => {
|
||||
if (message.from_public_key == toPublicKey && !message.readed) {
|
||||
console.info("Marking message as read in cache for dialog with " + fromPublicKey);
|
||||
console.info({ fromPublicKey, toPublicKey });
|
||||
return {
|
||||
...message,
|
||||
readed: 1
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}));
|
||||
});
|
||||
}, [updateDialog, publicKey]);
|
||||
/**
|
||||
* Обработчик прочтения групповых сообщений
|
||||
*/
|
||||
usePacket(0x07, async (packet: PacketRead) => {
|
||||
if(!hasGroup(packet.getToPublicKey())){
|
||||
/**
|
||||
* Если это не относится к группам, то игнорируем здесь,
|
||||
* для этого есть отдельный слушатель usePacket выше
|
||||
*/
|
||||
return;
|
||||
}
|
||||
const fromPublicKey = packet.getFromPublicKey();
|
||||
const toPublicKey = packet.getToPublicKey();
|
||||
await runQuery(`UPDATE messages SET read = 1 WHERE to_public_key = ? AND from_public_key = ? AND account = ?`, [toPublicKey, publicKey, publicKey]);
|
||||
updateDialog(toPublicKey);
|
||||
addOrUpdateDialogCache(toPublicKey, getDialogCache(toPublicKey).map((message) => {
|
||||
if (!message.readed) {
|
||||
console.info("Marking message as read in cache for dialog with " + fromPublicKey);
|
||||
console.info({fromPublicKey, toPublicKey});
|
||||
return {
|
||||
...message,
|
||||
readed: 1
|
||||
}
|
||||
runTaskInQueue(async () => {
|
||||
if (!hasGroup(packet.getToPublicKey())) {
|
||||
/**
|
||||
* Если это не относится к группам, то игнорируем здесь,
|
||||
* для этого есть отдельный слушатель usePacket выше
|
||||
*/
|
||||
return;
|
||||
}
|
||||
return message;
|
||||
}));
|
||||
}, [updateDialog]);
|
||||
const fromPublicKey = packet.getFromPublicKey();
|
||||
const toPublicKey = packet.getToPublicKey();
|
||||
await runQuery(`UPDATE messages SET read = 1 WHERE to_public_key = ? AND from_public_key = ? AND account = ?`, [toPublicKey, publicKey, publicKey]);
|
||||
updateDialog(toPublicKey);
|
||||
addOrUpdateDialogCache(toPublicKey, getDialogCache(toPublicKey).map((message) => {
|
||||
if (!message.readed) {
|
||||
console.info("Marking message as read in cache for dialog with " + fromPublicKey);
|
||||
console.info({ fromPublicKey, toPublicKey });
|
||||
return {
|
||||
...message,
|
||||
readed: 1
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}));
|
||||
});
|
||||
}, [updateDialog]);
|
||||
/**
|
||||
* Обработчик доставки сообщений
|
||||
*/
|
||||
usePacket(0x08, async (packet: PacketDelivery) => {
|
||||
const messageId = packet.getMessageId();
|
||||
await runQuery(`UPDATE messages SET delivered = ?, timestamp = ? WHERE message_id = ? AND account = ?`, [DeliveredMessageState.DELIVERED, Date.now(), messageId, publicKey]);
|
||||
updateDialog(packet.getToPublicKey());
|
||||
log("Delivery packet received msg id " + messageId);
|
||||
addOrUpdateDialogCache(packet.getToPublicKey(), getDialogCache(packet.getToPublicKey()).map((message) => {
|
||||
if (message.message_id == messageId) {
|
||||
return {
|
||||
...message,
|
||||
delivered: DeliveredMessageState.DELIVERED,
|
||||
timestamp: Date.now()
|
||||
runTaskInQueue(async () => {
|
||||
const messageId = packet.getMessageId();
|
||||
await runQuery(`UPDATE messages SET delivered = ?, timestamp = ? WHERE message_id = ? AND account = ?`, [DeliveredMessageState.DELIVERED, Date.now(), messageId, publicKey]);
|
||||
updateDialog(packet.getToPublicKey());
|
||||
log("Delivery packet received msg id " + messageId);
|
||||
addOrUpdateDialogCache(packet.getToPublicKey(), getDialogCache(packet.getToPublicKey()).map((message) => {
|
||||
if (message.message_id == messageId) {
|
||||
return {
|
||||
...message,
|
||||
delivered: DeliveredMessageState.DELIVERED,
|
||||
timestamp: Date.now()
|
||||
}
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}));
|
||||
return message;
|
||||
}));
|
||||
});
|
||||
}, [updateDialog]);
|
||||
}
|
||||
Reference in New Issue
Block a user