@shoru/kitten 0.0.5 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/internal-test/index.js +4 -0
- package/internal-test/kittenwa.config.js +33 -0
- package/internal-test/plugins/exp.js +7 -0
- package/package.json +10 -10
- package/src/auth/init-session.js +18 -5
- package/src/auth/lmdb-auth-state.js +270 -176
- package/src/client/auth-handler.js +97 -0
- package/src/client/client.js +552 -603
- package/src/client/constants.js +41 -0
- package/src/client/errors.js +47 -0
- package/src/client/event-bus.js +69 -0
- package/src/client/index.js +9 -2
- package/src/client/registry.js +63 -0
- package/src/client/socket-proxy.js +57 -0
- package/src/client/sync-manager.js +79 -0
- package/src/config/default.js +39 -39
- package/src/formatter/format-message.js +189 -144
- package/src/index.js +2 -1
- package/src/internals/config.js +15 -3
- package/src/internals/index.js +1 -2
- package/src/internals/lmdb-manager.js +79 -54
- package/src/plugins/config.js +1 -1
- package/src/plugins/handlers.js +121 -84
- package/src/plugins/loader.js +19 -13
- package/src/plugins/matcher.js +77 -58
- package/src/plugins/registry.js +184 -94
- package/src/plugins/watcher.js +13 -11
- package/src/utils/time-string.js +31 -19
- package/src/utils/type-conversions.js +11 -5
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { fileURLToPath } from 'url';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
5
|
+
const __dirname = path.dirname(__filename);
|
|
6
|
+
|
|
7
|
+
const pluginsDir = path.resolve(__dirname, 'plugins');
|
|
8
|
+
|
|
9
|
+
const db = {
|
|
10
|
+
path: path.resolve(__dirname, '../db'),
|
|
11
|
+
compression: true,
|
|
12
|
+
mapSize: 2 * 1024 * 1024 * 1024, // 2 GB
|
|
13
|
+
maxReaders: 126,
|
|
14
|
+
noSync: false,
|
|
15
|
+
noMetaSync: false,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const plugins = {
|
|
19
|
+
dir: pluginsDir,
|
|
20
|
+
prefixes: ['.', '\\', '!'],
|
|
21
|
+
defaultEvent: 'messages.upsert',
|
|
22
|
+
hmr: {
|
|
23
|
+
enable: true,
|
|
24
|
+
debounce: 200,
|
|
25
|
+
debug: true
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default {
|
|
30
|
+
db,
|
|
31
|
+
plugins,
|
|
32
|
+
timeZone: 'Africa/Casablanca'
|
|
33
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shoru/kitten",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0
|
|
4
|
+
"version": "0.1.0",
|
|
5
5
|
"description": "A powerful Node.js framework to simplify making WhatsApp Bots",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"repository": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"start": "node --no-warnings src/index.js",
|
|
12
|
-
"test": "
|
|
12
|
+
"test": "node --no-warnings internal-test/index.js"
|
|
13
13
|
},
|
|
14
14
|
"imports": {
|
|
15
15
|
"#utils.js": "./src/utils/index.js",
|
|
@@ -26,16 +26,16 @@
|
|
|
26
26
|
"license": "MIT",
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@hapi/boom": "^10.0.1",
|
|
29
|
-
"@inquirer/prompts": "^8.
|
|
30
|
-
"@shoru/spindle": "^1.0.
|
|
29
|
+
"@inquirer/prompts": "^8.7.0",
|
|
30
|
+
"@shoru/spindle": "^1.0.7",
|
|
31
31
|
"async-mutex": "^0.5.0",
|
|
32
|
-
"baileys": "
|
|
33
|
-
"chalk": "^
|
|
32
|
+
"baileys": "github:WhiskeySockets/Baileys",
|
|
33
|
+
"chalk": "^6.0.0",
|
|
34
34
|
"chokidar": "^5.0.0",
|
|
35
|
-
"cosmiconfig": "^
|
|
36
|
-
"defu": "^6.1.
|
|
37
|
-
"lmdb": "^3.
|
|
38
|
-
"pino": "^10.1
|
|
35
|
+
"cosmiconfig": "^10.0.0",
|
|
36
|
+
"defu": "^6.1.7",
|
|
37
|
+
"lmdb": "^3.5.6",
|
|
38
|
+
"pino": "^10.3.1",
|
|
39
39
|
"pino-pretty": "^13.1.3",
|
|
40
40
|
"qrcode-terminal": "^0.12.0"
|
|
41
41
|
}
|
package/src/auth/init-session.js
CHANGED
|
@@ -1,19 +1,32 @@
|
|
|
1
|
-
import { makeWASocket } from "baileys";
|
|
1
|
+
import { makeWASocket, fetchLatestWaWebVersion } from "baileys";
|
|
2
2
|
import { useLMDBAuthState } from "./lmdb-auth-state.js";
|
|
3
3
|
import { getConfig } from "#internals.js";
|
|
4
4
|
|
|
5
5
|
const config = await getConfig();
|
|
6
|
+
let cachedWaVersion = null;
|
|
7
|
+
|
|
8
|
+
const getWaVersion = async () => {
|
|
9
|
+
if (!cachedWaVersion) {
|
|
10
|
+
try {
|
|
11
|
+
const { version } = await fetchLatestWaWebVersion();
|
|
12
|
+
cachedWaVersion = version;
|
|
13
|
+
} catch {/* noop */}
|
|
14
|
+
}
|
|
15
|
+
return cachedWaVersion;
|
|
16
|
+
};
|
|
6
17
|
|
|
7
18
|
export const initSession = async ({ socketConfig, id } = {}) => {
|
|
8
19
|
try {
|
|
9
20
|
const { state, saveCreds, session } = await useLMDBAuthState(id);
|
|
10
|
-
|
|
11
|
-
|
|
21
|
+
const version = (await getWaVersion()) || config.socket?.version;
|
|
22
|
+
|
|
23
|
+
const sock = makeWASocket({
|
|
24
|
+
version: version || undefined,
|
|
12
25
|
auth: state,
|
|
13
|
-
...config.socket,
|
|
26
|
+
...config.socket,
|
|
14
27
|
...socketConfig
|
|
15
28
|
});
|
|
16
|
-
|
|
29
|
+
|
|
17
30
|
sock.ev.on("creds.update", saveCreds);
|
|
18
31
|
|
|
19
32
|
return { sock, session };
|
|
@@ -1,177 +1,271 @@
|
|
|
1
|
-
import { proto, initAuthCreds } from "baileys";
|
|
2
|
-
import { LMDBManager, logger } from "#internals.js";
|
|
3
|
-
import { serialize, deserialize } from "#utils.js";
|
|
4
|
-
|
|
5
|
-
const KEY_PREFIX = "baileys";
|
|
6
|
-
const COUNTER_KEY = `${KEY_PREFIX}:__meta__:counter`;
|
|
7
|
-
const SESSION_PREFIX = `${KEY_PREFIX}:__sessions__:`;
|
|
8
|
-
|
|
9
|
-
const keyBuilder = (sessionId) => {
|
|
10
|
-
const prefix = `${KEY_PREFIX}:${sessionId}:`;
|
|
11
|
-
return {
|
|
12
|
-
sessionId,
|
|
13
|
-
sessionPrefix: prefix,
|
|
14
|
-
creds: `${prefix}creds`,
|
|
15
|
-
forKey: (type, id) => `${prefix}${type}:${id}`,
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const genID = async (db) => {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
1
|
+
import { proto, initAuthCreds } from "baileys";
|
|
2
|
+
import { LMDBManager, logger } from "#internals.js";
|
|
3
|
+
import { serialize, deserialize } from "#utils.js";
|
|
4
|
+
|
|
5
|
+
const KEY_PREFIX = "baileys";
|
|
6
|
+
const COUNTER_KEY = `${KEY_PREFIX}:__meta__:counter`;
|
|
7
|
+
const SESSION_PREFIX = `${KEY_PREFIX}:__sessions__:`;
|
|
8
|
+
|
|
9
|
+
const keyBuilder = (sessionId) => {
|
|
10
|
+
const prefix = `${KEY_PREFIX}:${sessionId}:`;
|
|
11
|
+
return {
|
|
12
|
+
sessionId,
|
|
13
|
+
sessionPrefix: prefix,
|
|
14
|
+
creds: `${prefix}creds`,
|
|
15
|
+
forKey: (type, id) => `${prefix}${type}:${id}`,
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const genID = async (db) => {
|
|
20
|
+
const current = db.get(COUNTER_KEY);
|
|
21
|
+
let id;
|
|
22
|
+
if (current == null) {
|
|
23
|
+
const existing = listSessions();
|
|
24
|
+
id = existing.length > 0 ? Math.max(...existing) + 1 : 0;
|
|
25
|
+
} else {
|
|
26
|
+
id = current + 1;
|
|
27
|
+
}
|
|
28
|
+
await db.put(COUNTER_KEY, id);
|
|
29
|
+
return id;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const getSessionId = async (db, input) => {
|
|
33
|
+
if (input == null) {
|
|
34
|
+
const existing = listSessions();
|
|
35
|
+
if (existing.length > 0) {
|
|
36
|
+
return existing[0];
|
|
37
|
+
}
|
|
38
|
+
return genID(db);
|
|
39
|
+
}
|
|
40
|
+
if (Number.isInteger(input) && input >= 0) return input;
|
|
41
|
+
throw new TypeError(
|
|
42
|
+
'Invalid sessionId: expected null/undefined or non-negative integer'
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export async function useLMDBAuthState(inputSessionId) {
|
|
47
|
+
const { db } = LMDBManager;
|
|
48
|
+
const sessionId = await getSessionId(db, inputSessionId);
|
|
49
|
+
const keys = keyBuilder(sessionId);
|
|
50
|
+
|
|
51
|
+
const existingCreds = db.get(keys.creds);
|
|
52
|
+
let creds = null;
|
|
53
|
+
let isNew = false;
|
|
54
|
+
|
|
55
|
+
if (existingCreds != null) {
|
|
56
|
+
try {
|
|
57
|
+
creds = deserialize(existingCreds);
|
|
58
|
+
} catch {
|
|
59
|
+
logger.warn(`[LMDBAuthState] Corrupted credentials for session ${sessionId}, purging session`);
|
|
60
|
+
creds = null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (creds && (creds.registered || creds.me)) {
|
|
64
|
+
await db.put(`${SESSION_PREFIX}${sessionId}`, true);
|
|
65
|
+
} else {
|
|
66
|
+
// Credentials not registered or corrupted: clean up old session keys and start fresh
|
|
67
|
+
for (const { key } of db.getRange({
|
|
68
|
+
start: keys.sessionPrefix,
|
|
69
|
+
end: `${keys.sessionPrefix}\xFF`,
|
|
70
|
+
})) {
|
|
71
|
+
await db.remove(key);
|
|
72
|
+
}
|
|
73
|
+
await db.remove(`${SESSION_PREFIX}${sessionId}`);
|
|
74
|
+
creds = initAuthCreds();
|
|
75
|
+
await db.put(keys.creds, serialize(creds));
|
|
76
|
+
isNew = true;
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
creds = initAuthCreds();
|
|
80
|
+
await db.put(keys.creds, serialize(creds));
|
|
81
|
+
isNew = true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const writeCreds = async (credsData) => {
|
|
85
|
+
await db.put(keys.creds, serialize(credsData));
|
|
86
|
+
if (credsData?.registered || credsData?.me) {
|
|
87
|
+
await db.put(`${SESSION_PREFIX}${sessionId}`, true);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const getKeys = (type, ids) => {
|
|
92
|
+
if (!ids.length) return {};
|
|
93
|
+
|
|
94
|
+
const result = {};
|
|
95
|
+
for (const id of ids) {
|
|
96
|
+
const dbKey = keys.forKey(type, id);
|
|
97
|
+
const rawValue = db.get(dbKey);
|
|
98
|
+
|
|
99
|
+
if (rawValue != null) {
|
|
100
|
+
try {
|
|
101
|
+
let parsed = deserialize(rawValue);
|
|
102
|
+
if (type === "app-state-sync-key" && parsed) {
|
|
103
|
+
parsed = proto.Message.AppStateSyncKeyData.fromObject(parsed);
|
|
104
|
+
}
|
|
105
|
+
result[id] = parsed;
|
|
106
|
+
} catch (err) {
|
|
107
|
+
logger.error(
|
|
108
|
+
err,
|
|
109
|
+
`[LMDBAuthState] Deserialize error: ${type}:${id}`
|
|
110
|
+
);
|
|
111
|
+
result[id] = null;
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
result[id] = null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const setKeys = async (data) => {
|
|
121
|
+
const writes = [];
|
|
122
|
+
|
|
123
|
+
for (const [category, categoryData] of Object.entries(data)) {
|
|
124
|
+
if (!categoryData) continue;
|
|
125
|
+
for (const [id, value] of Object.entries(categoryData)) {
|
|
126
|
+
const key = keys.forKey(category, id);
|
|
127
|
+
if (value != null) {
|
|
128
|
+
writes.push(db.put(key, serialize(value)));
|
|
129
|
+
} else {
|
|
130
|
+
writes.push(db.remove(key));
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (writes.length > 0) {
|
|
136
|
+
await Promise.all(writes);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const clearKeys = async () => {
|
|
141
|
+
let count = 0;
|
|
142
|
+
const writes = [];
|
|
143
|
+
|
|
144
|
+
for (const { key } of db.getRange({
|
|
145
|
+
start: keys.sessionPrefix,
|
|
146
|
+
end: `${keys.sessionPrefix}\xFF`,
|
|
147
|
+
})) {
|
|
148
|
+
if (key !== keys.creds) {
|
|
149
|
+
writes.push(db.remove(key));
|
|
150
|
+
count++;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (writes.length > 0) {
|
|
155
|
+
await Promise.all(writes);
|
|
156
|
+
}
|
|
157
|
+
logger.debug(`[LMDBAuthState] Cleared ${count} keys`);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const deleteSession = async () => {
|
|
161
|
+
const writes = [];
|
|
162
|
+
|
|
163
|
+
for (const { key } of db.getRange({
|
|
164
|
+
start: keys.sessionPrefix,
|
|
165
|
+
end: `${keys.sessionPrefix}\xFF`,
|
|
166
|
+
})) {
|
|
167
|
+
writes.push(db.remove(key));
|
|
168
|
+
}
|
|
169
|
+
writes.push(db.remove(`${SESSION_PREFIX}${sessionId}`));
|
|
170
|
+
|
|
171
|
+
await Promise.all(writes);
|
|
172
|
+
logger.debug(`[LMDBAuthState] Deleted session ${sessionId}`);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
state: {
|
|
177
|
+
creds,
|
|
178
|
+
keys: {
|
|
179
|
+
get: getKeys,
|
|
180
|
+
set: setKeys,
|
|
181
|
+
clear: clearKeys,
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
saveCreds: () => writeCreds(creds),
|
|
185
|
+
session: {
|
|
186
|
+
delete: deleteSession,
|
|
187
|
+
clear: clearKeys,
|
|
188
|
+
id: sessionId,
|
|
189
|
+
isNew,
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function listSessions() {
|
|
195
|
+
const { db } = LMDBManager;
|
|
196
|
+
if (!db) return [];
|
|
197
|
+
|
|
198
|
+
const sessions = new Set();
|
|
199
|
+
|
|
200
|
+
for (const { key } of db.getRange({
|
|
201
|
+
start: SESSION_PREFIX,
|
|
202
|
+
end: `${SESSION_PREFIX}\xFF`,
|
|
203
|
+
})) {
|
|
204
|
+
const id = parseInt(key.slice(SESSION_PREFIX.length), 10);
|
|
205
|
+
if (!isNaN(id)) {
|
|
206
|
+
const rawCreds = db.get(`${KEY_PREFIX}:${id}:creds`);
|
|
207
|
+
if (rawCreds != null) {
|
|
208
|
+
try {
|
|
209
|
+
const creds = deserialize(rawCreds);
|
|
210
|
+
if (creds?.registered || creds?.me) {
|
|
211
|
+
sessions.add(id);
|
|
212
|
+
} else {
|
|
213
|
+
db.remove(key);
|
|
214
|
+
}
|
|
215
|
+
} catch {
|
|
216
|
+
db.remove(key);
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
db.remove(key);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Also check for existing credentials as fallback
|
|
225
|
+
for (const { key, value } of db.getRange({
|
|
226
|
+
start: `${KEY_PREFIX}:`,
|
|
227
|
+
end: `${KEY_PREFIX}:\xFF`,
|
|
228
|
+
})) {
|
|
229
|
+
if (key.endsWith(':creds')) {
|
|
230
|
+
const parts = key.split(':');
|
|
231
|
+
if (parts.length === 3 && parts[0] === KEY_PREFIX && parts[2] === 'creds') {
|
|
232
|
+
const id = parseInt(parts[1], 10);
|
|
233
|
+
if (!isNaN(id) && !sessions.has(id)) {
|
|
234
|
+
try {
|
|
235
|
+
const creds = deserialize(value);
|
|
236
|
+
if (creds?.registered || creds?.me) {
|
|
237
|
+
sessions.add(id);
|
|
238
|
+
db.put(`${SESSION_PREFIX}${id}`, true);
|
|
239
|
+
}
|
|
240
|
+
} catch {
|
|
241
|
+
/* ignore corrupted creds */
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return [...sessions].sort((a, b) => a - b);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export function sessionExists(sessionId) {
|
|
252
|
+
if (!Number.isInteger(sessionId) || sessionId < 0) return false;
|
|
253
|
+
const { db } = LMDBManager;
|
|
254
|
+
if (!db) return false;
|
|
255
|
+
|
|
256
|
+
const rawCreds = db.get(`${KEY_PREFIX}:${sessionId}:creds`);
|
|
257
|
+
if (rawCreds != null) {
|
|
258
|
+
try {
|
|
259
|
+
const creds = deserialize(rawCreds);
|
|
260
|
+
if (creds?.registered || creds?.me) {
|
|
261
|
+
db.put(`${SESSION_PREFIX}${sessionId}`, true);
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
} catch {
|
|
265
|
+
db.remove(`${SESSION_PREFIX}${sessionId}`);
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
db.remove(`${SESSION_PREFIX}${sessionId}`);
|
|
270
|
+
return false;
|
|
177
271
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import qrcode from 'qrcode-terminal';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { ConnectionError } from './errors.js';
|
|
4
|
+
import { ClientRegistry } from './registry.js';
|
|
5
|
+
import { getConnectionConfig } from './getConnectionConfig.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Formats a pairing code into readable 4-character groups with styling.
|
|
9
|
+
*
|
|
10
|
+
* @param {string} code - The raw pairing code string
|
|
11
|
+
* @returns {string} The styled formatted pairing code
|
|
12
|
+
*/
|
|
13
|
+
export function formatPairingCode(code) {
|
|
14
|
+
const formatted = code.match(/.{1,4}/g)?.join(' ') ?? code;
|
|
15
|
+
return `\n${chalk.green('> Your OTP Code: ')}${chalk.bold(formatted)}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Handles authentication for a client instance when a QR code or pairing challenge is received.
|
|
20
|
+
*
|
|
21
|
+
* @param {object} params
|
|
22
|
+
* @param {string} params.qr - The QR code string
|
|
23
|
+
* @param {any} params.rawSock - The raw WASocket instance
|
|
24
|
+
* @param {string} params.flag - Identifier string for logging
|
|
25
|
+
* @param {boolean} params.isSync - Whether the client is a background sync instance
|
|
26
|
+
* @param {boolean} params.isSilent - Whether logging/prompts are suppressed
|
|
27
|
+
* @param {Function|null} params.onPairing - Optional custom pairing callback
|
|
28
|
+
* @param {object} params.logger - Logger instance
|
|
29
|
+
* @param {object|null} params.authConfig - Cached auth config
|
|
30
|
+
* @param {(config: object|null) => void} params.setAuthConfig - Setter for auth config
|
|
31
|
+
* @param {boolean} params.pairingCodeRequested - Whether pairing code has already been requested
|
|
32
|
+
* @param {(requested: boolean) => void} params.setPairingCodeRequested - Setter for pairingCodeRequested
|
|
33
|
+
* @param {(err: Error) => void} params.onSyncAuthAbort - Callback when sync connection requires auth
|
|
34
|
+
*/
|
|
35
|
+
export async function handleClientAuth({
|
|
36
|
+
qr,
|
|
37
|
+
rawSock,
|
|
38
|
+
flag,
|
|
39
|
+
isSync,
|
|
40
|
+
isSilent,
|
|
41
|
+
onPairing,
|
|
42
|
+
logger,
|
|
43
|
+
authConfig,
|
|
44
|
+
setAuthConfig,
|
|
45
|
+
pairingCodeRequested,
|
|
46
|
+
setPairingCodeRequested,
|
|
47
|
+
onSyncAuthAbort,
|
|
48
|
+
}) {
|
|
49
|
+
if (isSync) {
|
|
50
|
+
const err = new ConnectionError('Authentication required for sync connection', {
|
|
51
|
+
recoverable: false,
|
|
52
|
+
});
|
|
53
|
+
logger.debug(`[${flag}] Sync connection requires auth, aborting`);
|
|
54
|
+
onSyncAuthAbort(err);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (ClientRegistry.isConfiguring) return;
|
|
59
|
+
|
|
60
|
+
if (typeof onPairing === 'function') {
|
|
61
|
+
const requestPairingCode = rawSock?.requestPairingCode?.bind(rawSock);
|
|
62
|
+
await onPairing({ qr, requestPairingCode });
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (isSilent) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let currentAuthConfig = authConfig;
|
|
71
|
+
if (!currentAuthConfig) {
|
|
72
|
+
ClientRegistry.isConfiguring = true;
|
|
73
|
+
try {
|
|
74
|
+
currentAuthConfig = await getConnectionConfig();
|
|
75
|
+
setAuthConfig(currentAuthConfig);
|
|
76
|
+
} finally {
|
|
77
|
+
ClientRegistry.isConfiguring = false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (currentAuthConfig.type === 'pn') {
|
|
82
|
+
if (!pairingCodeRequested && rawSock) {
|
|
83
|
+
try {
|
|
84
|
+
setPairingCodeRequested(true);
|
|
85
|
+
const code = await rawSock.requestPairingCode(currentAuthConfig.pn);
|
|
86
|
+
logger.prompt(formatPairingCode(code));
|
|
87
|
+
} catch (err) {
|
|
88
|
+
setPairingCodeRequested(false);
|
|
89
|
+
logger.error(err, `[${flag}] Failed to request pairing code`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
qrcode.generate(qr, { small: true });
|
|
94
|
+
process.stdout.write('\n');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|