Skip to content

Commit d359949

Browse files
committed
fix: Adjustment in the recording of temporary files and periodic cleaning
1 parent 30cd8a0 commit d359949

File tree

11 files changed

+350
-58
lines changed

11 files changed

+350
-58
lines changed

Docker/.env

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,17 @@ LOG_COLOR=true
1212
DEL_INSTANCE=5
1313

1414
# Temporary data storage
15-
STORE_CLEANING_INTERVAL=7200 # seconds ===2h
16-
STORE_MESSAGE=true
15+
STORE_MESSAGES=true
16+
STORE_MESSAGE_UP=true
1717
STORE_CONTACTS=false
1818
STORE_CHATS=false
1919

20+
CLEAN_STORE_CLEANING_INTERVAL=7200 # seconds ===2h
21+
CLEAN_STORE_MESSAGES=true
22+
CLEAN_STORE_MESSAGE_UP=true
23+
CLEAN_STORE_CONTACTS=false
24+
CLEAN_STORE_CHATS=false
25+
2026
# Permanent data storage
2127
DATABASE_ENABLED=false
2228
DATABASE_CONNECTION_URI='<uri>'

Dockerfile

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,17 @@ ENV LOG_COLOR=true
2121

2222
ENV DEL_INSTANCE=$DEL_INSTANCE
2323

24-
ENV STORE_CLEANING_INTERVAL=$STORE_CLEANING_INTERVAL
25-
ENV STORE_MESSAGE=$STORE_MESSAGE
24+
ENV STORE_MESSAGES=$STORE_MESSAGE
25+
ENV STORE_MESSAGE_UP=$STORE_MESSAGE_UP
2626
ENV STORE_CONTACTS=$STORE_CONTACTS
2727
ENV STORE_CHATS=$STORE_CHATS
2828

29+
ENV CLEAN_STORE_CLEANING_INTERVAL=$CLEAN_STORE_CLEANING_INTERVAL
30+
ENV CLEAN_STORE_MESSAGES=$CLEAN_STORE_MESSAGE
31+
ENV CLEAN_STORE_MESSAGE_UP=$CLEAN_STORE_MESSAGE_UP
32+
ENV CLEAN_STORE_CONTACTS=$CLEAN_STORE_CONTACTS
33+
ENV CLEAN_STORE_CHATS=$CLEAN_STORE_CHATS
34+
2935
ENV DATABASE_ENABLED=$DATABASE_ENABLED
3036
ENV DATABASE_CONNECTION_URI=$DATABASE_CONNECTION_URI
3137
ENV DATABASE_CONNECTION_DB_PREFIX_NAME=$DATABASE_CONNECTION_DB_PREFIX_NAME

docker-compose.yaml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ services:
1212
- 8080:8080
1313
volumes:
1414
- evolution_instances:/evolution/instances
15+
- evolution_store:/evolution/store
1516
depends_on:
1617
- mongodb
1718
- redis
@@ -21,10 +22,15 @@ services:
2122
# If you don't even want an expiration, enter the value false
2223
- DEL_INSTANCE=5 # or false
2324
# Temporary data storage
24-
- STORE_CLEANING_INTERVAL=7200 # seconds === 2h
25-
- STORE_MESSAGE=true
25+
- STORE_MESSAGES=true
26+
- STORE_MESSAGE_UP=true
2627
- STORE_CONTACTS=true
2728
- STORE_CHATS=true
29+
- CLEAN_STORE_CLEANING_INTERVAL=7200 # seconds === 2h
30+
- CLEAN_STORE_MESSAGES=true
31+
- CLEAN_STORE_MESSAGE_UP=true
32+
- CLEAN_STORE_CONTACTS=true
33+
- CLEAN_STORE_CHATS=true
2834
# Permanent data storage
2935
- DATABASE_ENABLED=true
3036
- DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true
@@ -131,6 +137,7 @@ services:
131137

132138
volumes:
133139
evolution_instances:
140+
evolution_store:
134141
evolution_mongodb_data:
135142
evolution_mongodb_configdb:
136143
evolution_redis:

src/config/env.config.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,16 @@ export type SaveData = {
2929
};
3030

3131
export type StoreConf = {
32+
MESSAGES: boolean;
33+
MESSAGE_UP: boolean;
34+
CONTACTS: boolean;
35+
CHATS: boolean;
36+
};
37+
38+
export type CleanStoreConf = {
3239
CLEANING_INTERVAL: number;
3340
MESSAGES: boolean;
41+
MESSAGE_UP: boolean;
3442
CONTACTS: boolean;
3543
CHATS: boolean;
3644
};
@@ -107,6 +115,7 @@ export interface Env {
107115
CORS: Cors;
108116
SSL_CONF: SslConf;
109117
STORE: StoreConf;
118+
CLEAN_STORE: CleanStoreConf;
110119
DATABASE: Database;
111120
REDIS: Redis;
112121
LOG: Log;
@@ -160,13 +169,20 @@ export class ConfigService {
160169
FULLCHAIN: process.env?.SSL_CONF_FULLCHAIN,
161170
},
162171
STORE: {
163-
CLEANING_INTERVAL: Number.isInteger(process.env?.STORE_CLEANING_TERMINAL)
164-
? Number.parseInt(process.env.STORE_CLEANING_TERMINAL)
165-
: undefined,
166-
MESSAGES: process.env?.STORE_MESSAGE === 'true',
172+
MESSAGES: process.env?.STORE_MESSAGES === 'true',
173+
MESSAGE_UP: process.env?.STORE_MESSAGE_UP === 'true',
167174
CONTACTS: process.env?.STORE_CONTACTS === 'true',
168175
CHATS: process.env?.STORE_CHATS === 'true',
169176
},
177+
CLEAN_STORE: {
178+
CLEANING_INTERVAL: Number.isInteger(process.env?.CLEAN_STORE_CLEANING_TERMINAL)
179+
? Number.parseInt(process.env.CLEAN_STORE_CLEANING_TERMINAL)
180+
: undefined,
181+
MESSAGES: process.env?.CLEAN_STORE_MESSAGES === 'true',
182+
MESSAGE_UP: process.env?.CLEAN_STORE_MESSAGE_UP === 'true',
183+
CONTACTS: process.env?.CLEAN_STORE_CONTACTS === 'true',
184+
CHATS: process.env?.CLEAN_STORE_CHATS === 'true',
185+
},
170186
DATABASE: {
171187
CONNECTION: {
172188
URI: process.env.DATABASE_CONNECTION_URI,

src/dev-env.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,15 @@ DEL_INSTANCE: false # or false
4545

4646
# Temporary data storage
4747
STORE:
48-
CLEANING_INTERVAL: 7200 # seconds === 2h
49-
MESSAGE: true
48+
MESSAGES: true
49+
MESSAGE_UP: true
50+
CONTACTS: true
51+
CHATS: true
52+
53+
CLEAN_STORE:
54+
CLEANING_INTERVAL: 7200 # 7200 seconds === 2h
55+
MESSAGES: true
56+
MESSAGE_UP: true
5057
CONTACTS: true
5158
CHATS: true
5259

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
// Built around ShellTear's POC at #2215#issuecomment-1292885678 on @adiwajshing/baileys
2+
// Copyright ~ purpshell
3+
4+
import crypto from 'node:crypto';
5+
6+
const enc = new TextEncoder();
7+
/**
8+
* Decrypt PollUpdate messages
9+
*/
10+
export class PollUpdateDecrypt {
11+
/**
12+
* Compare the SHA-256 hashes of the poll options from the update to find the original choices
13+
* @param options Options from the poll creation message
14+
* @param pollOptionHash hash from `this.decrypt()`
15+
* @returns the original option, can be empty when none are currently selected
16+
*/
17+
static async compare(options: string[], pollOptionHashes: string[]): Promise<string[]> {
18+
const selectedOptions = [];
19+
for (const option of options) {
20+
const hash = Buffer.from(
21+
await crypto.webcrypto.subtle.digest('SHA-256', new TextEncoder().encode(option)),
22+
)
23+
.toString('hex')
24+
.toUpperCase();
25+
for (const pollOptionHash of pollOptionHashes) {
26+
if (pollOptionHash === hash) {
27+
selectedOptions.push(option);
28+
}
29+
}
30+
}
31+
return selectedOptions;
32+
}
33+
34+
/**
35+
* decrypt a poll message update
36+
* @param encPayload from the update
37+
* @param encIv from the update
38+
* @param encKey from the original poll
39+
* @param pollMsgSender sender jid of the pollCreation message
40+
* @param pollMsgId id of the pollCreation message
41+
* @param voteMsgSender sender of the pollUpdate message
42+
* @returns The option or empty array if something went wrong OR everything was unticked
43+
*/
44+
static async decrypt(
45+
encKey: Uint8Array,
46+
encPayload: Uint8Array,
47+
encIv: Uint8Array,
48+
pollMsgSender: string,
49+
pollMsgId: string,
50+
voteMsgSender: string,
51+
): Promise<string[]> {
52+
const stanzaId = enc.encode(pollMsgId);
53+
const parentMsgOriginalSender = enc.encode(pollMsgSender);
54+
const modificationSender = enc.encode(voteMsgSender);
55+
const modificationType = enc.encode('Poll Vote');
56+
const pad = new Uint8Array([1]);
57+
58+
const signMe = new Uint8Array([
59+
...stanzaId,
60+
...parentMsgOriginalSender,
61+
...modificationSender,
62+
...modificationType,
63+
pad,
64+
] as any);
65+
66+
const createSignKey = async (n: Uint8Array = new Uint8Array(32)) => {
67+
return await crypto.webcrypto.subtle.importKey(
68+
'raw',
69+
n,
70+
{ name: 'HMAC', hash: 'SHA-256' },
71+
false,
72+
['sign'],
73+
);
74+
};
75+
76+
const sign = async (
77+
n: crypto.webcrypto.BufferSource,
78+
key: crypto.webcrypto.CryptoKey,
79+
) => {
80+
return await crypto.webcrypto.subtle.sign(
81+
{ name: 'HMAC', hash: 'SHA-256' },
82+
key,
83+
n,
84+
);
85+
};
86+
87+
let key = await createSignKey();
88+
89+
const temp = await sign(encKey, key);
90+
91+
key = await createSignKey(new Uint8Array(temp));
92+
93+
const decryptionKey = new Uint8Array(await sign(signMe, key));
94+
95+
const additionalData = enc.encode(`${pollMsgId}\u0000${voteMsgSender}`);
96+
97+
const decryptedMessage = await this._decryptMessage(
98+
encPayload,
99+
encIv,
100+
additionalData,
101+
decryptionKey,
102+
);
103+
104+
const pollOptionHash = this._decodeMessage(decryptedMessage);
105+
106+
// '0A20' in hex represents unicode " " and "\n" thus declaring the end of one option
107+
// we want multiple hashes to make it easier to iterate and understand for your use cases
108+
return pollOptionHash.split('0A20') || [];
109+
}
110+
111+
/**
112+
* Internal method to decrypt the message after gathering all information
113+
* @deprecated Use `this.decrypt()` instead, only use this if you know what you are doing
114+
* @param encPayload
115+
* @param encIv
116+
* @param additionalData
117+
* @param decryptionKey
118+
* @returns
119+
*/
120+
static async _decryptMessage(
121+
encPayload: Uint8Array,
122+
encIv: Uint8Array,
123+
additionalData: Uint8Array,
124+
decryptionKey: Uint8Array,
125+
) {
126+
const tagSize_multiplier = 16;
127+
const encoded = encPayload;
128+
const key = await crypto.webcrypto.subtle.importKey(
129+
'raw',
130+
decryptionKey,
131+
'AES-GCM',
132+
false,
133+
['encrypt', 'decrypt'],
134+
);
135+
const decrypted = await crypto.webcrypto.subtle.decrypt(
136+
{
137+
name: 'AES-GCM',
138+
iv: encIv,
139+
additionalData: additionalData,
140+
tagLength: 8 * tagSize_multiplier,
141+
},
142+
key,
143+
encoded,
144+
);
145+
return new Uint8Array(decrypted).slice(2); // remove 2 bytes (OA20)(space+newline)
146+
}
147+
148+
/**
149+
* Decode the message from `this._decryptMessage()`
150+
* @param decryptedMessage the message from `this._decrpytMessage()`
151+
* @returns
152+
*/
153+
static _decodeMessage(decryptedMessage: Uint8Array) {
154+
const n = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70];
155+
const outarr: number[] = [];
156+
157+
for (let i = 0; i < decryptedMessage.length; i++) {
158+
const val = decryptedMessage[i];
159+
outarr.push(n[val >> 4], n[15 & val]);
160+
}
161+
162+
return String.fromCharCode(...outarr);
163+
}
164+
}

src/whatsapp/repository/chat.repository.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { join } from 'path';
2-
import { ConfigService } from '../../config/env.config';
2+
import { ConfigService, StoreConf } from '../../config/env.config';
33
import { IInsert, Repository } from '../abstract/abstract.repository';
44
import { opendirSync, readFileSync, rmSync } from 'fs';
55
import { ChatRaw, IChatModel } from '../models';
@@ -27,15 +27,21 @@ export class ChatRepository extends Repository {
2727
return { insertCount: insert.length };
2828
}
2929

30-
data.forEach((chat) => {
31-
this.writeStore<ChatRaw>({
32-
path: join(this.storePath, 'chats', chat.owner),
33-
fileName: chat.id,
34-
data: chat,
30+
const store = this.configService.get<StoreConf>('STORE');
31+
32+
if (store.CHATS) {
33+
data.forEach((chat) => {
34+
this.writeStore<ChatRaw>({
35+
path: join(this.storePath, 'chats', chat.owner),
36+
fileName: chat.id,
37+
data: chat,
38+
});
3539
});
36-
});
3740

38-
return { insertCount: data.length };
41+
return { insertCount: data.length };
42+
}
43+
44+
return { insertCount: 0 };
3945
} catch (error) {
4046
return error;
4147
} finally {

src/whatsapp/repository/contact.repository.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { opendirSync, readFileSync } from 'fs';
22
import { join } from 'path';
3-
import { ConfigService } from '../../config/env.config';
3+
import { ConfigService, StoreConf } from '../../config/env.config';
44
import { ContactRaw, IContactModel } from '../models';
55
import { IInsert, Repository } from '../abstract/abstract.repository';
66

@@ -27,15 +27,21 @@ export class ContactRepository extends Repository {
2727
return { insertCount: insert.length };
2828
}
2929

30-
data.forEach((contact) => {
31-
this.writeStore({
32-
path: join(this.storePath, 'contacts', contact.owner),
33-
fileName: contact.id,
34-
data: contact,
30+
const store = this.configService.get<StoreConf>('STORE');
31+
32+
if (store.CONTACTS) {
33+
data.forEach((contact) => {
34+
this.writeStore({
35+
path: join(this.storePath, 'contacts', contact.owner),
36+
fileName: contact.id,
37+
data: contact,
38+
});
3539
});
36-
});
3740

38-
return { insertCount: data.length };
41+
return { insertCount: data.length };
42+
}
43+
44+
return { insertCount: 0 };
3945
} catch (error) {
4046
return error;
4147
} finally {

src/whatsapp/repository/message.repository.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ConfigService } from '../../config/env.config';
1+
import { ConfigService, StoreConf } from '../../config/env.config';
22
import { join } from 'path';
33
import { IMessageModel, MessageRaw } from '../models';
44
import { IInsert, Repository } from '../abstract/abstract.repository';
@@ -47,7 +47,9 @@ export class MessageRepository extends Repository {
4747
return { insertCount: insert.length };
4848
}
4949

50-
if (saveDb) {
50+
const store = this.configService.get<StoreConf>('STORE');
51+
52+
if (store.MESSAGES) {
5153
data.forEach((msg) =>
5254
this.writeStore<MessageRaw>({
5355
path: join(this.storePath, 'messages', msg.owner),

0 commit comments

Comments
 (0)