@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
|
@@ -1,144 +1,189 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isJidGroup,
|
|
3
|
-
areJidsSameUser,
|
|
4
|
-
getDevice,
|
|
5
|
-
downloadMediaMessage,
|
|
6
|
-
getContentType,
|
|
7
|
-
isLidUser,
|
|
8
|
-
jidNormalizedUser
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import {
|
|
12
|
-
getTimeString,
|
|
13
|
-
isString,
|
|
14
|
-
toNumber,
|
|
15
|
-
toBase64,
|
|
16
|
-
getPN
|
|
17
|
-
} from '#utils.js';
|
|
18
|
-
|
|
19
|
-
const extractMessage = (message) => {
|
|
20
|
-
|
|
21
|
-
let
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
1
|
+
import {
|
|
2
|
+
isJidGroup,
|
|
3
|
+
areJidsSameUser,
|
|
4
|
+
getDevice,
|
|
5
|
+
downloadMediaMessage,
|
|
6
|
+
getContentType,
|
|
7
|
+
isLidUser,
|
|
8
|
+
jidNormalizedUser,
|
|
9
|
+
} from 'baileys';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
getTimeString,
|
|
13
|
+
isString,
|
|
14
|
+
toNumber,
|
|
15
|
+
toBase64,
|
|
16
|
+
getPN,
|
|
17
|
+
} from '#utils.js';
|
|
18
|
+
|
|
19
|
+
const extractMessage = (message) => {
|
|
20
|
+
if (!message) return [undefined, undefined];
|
|
21
|
+
let type = getContentType(message);
|
|
22
|
+
let data = message?.[type];
|
|
23
|
+
|
|
24
|
+
// cases like viewonce or document messages etc
|
|
25
|
+
if (data?.message) {
|
|
26
|
+
const innerType = getContentType(data.message);
|
|
27
|
+
if (innerType) {
|
|
28
|
+
type = innerType;
|
|
29
|
+
data = data.message[type];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return [type, data];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const extractContent = (message = {}) => ({
|
|
36
|
+
body:
|
|
37
|
+
message.text ??
|
|
38
|
+
message.caption ??
|
|
39
|
+
(isString(message) ? message : undefined),
|
|
40
|
+
mentions: message.contextInfo?.mentionedJid,
|
|
41
|
+
groupMentions: message.contextInfo?.groupMentions,
|
|
42
|
+
mimetype: message.mimetype,
|
|
43
|
+
fileName: message.fileName,
|
|
44
|
+
pageCount: message.pageCount,
|
|
45
|
+
fileLength: toNumber(message.fileLength),
|
|
46
|
+
hash: toBase64(message.fileSha256),
|
|
47
|
+
isViewOnce: message.viewOnce,
|
|
48
|
+
url: message.matchedText,
|
|
49
|
+
description: message.description,
|
|
50
|
+
thumbnail: message.jpegThumbnail,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const load = async (x) => downloadMediaMessage(x, 'buffer', {});
|
|
54
|
+
|
|
55
|
+
export const formatMessage = (sock, raw) => {
|
|
56
|
+
const userJid = sock.user?.lid || sock.user?.id || '';
|
|
57
|
+
const myId = userJid ? jidNormalizedUser(userJid) : '';
|
|
58
|
+
const [type, messageData] = extractMessage(raw.message);
|
|
59
|
+
|
|
60
|
+
const {
|
|
61
|
+
pushName: name,
|
|
62
|
+
messageTimestamp,
|
|
63
|
+
participant: p1,
|
|
64
|
+
broadcast,
|
|
65
|
+
key,
|
|
66
|
+
key: {
|
|
67
|
+
remoteJid: roomId,
|
|
68
|
+
id,
|
|
69
|
+
fromMe,
|
|
70
|
+
participant: p2,
|
|
71
|
+
} = {},
|
|
72
|
+
} = raw;
|
|
73
|
+
|
|
74
|
+
const jid = fromMe ? myId : p2 || p1 || roomId;
|
|
75
|
+
|
|
76
|
+
const {
|
|
77
|
+
quotedMessage,
|
|
78
|
+
participant: quotedSender,
|
|
79
|
+
stanzaId: quotedId,
|
|
80
|
+
isForwarded,
|
|
81
|
+
forwardingScore,
|
|
82
|
+
} = messageData?.contextInfo || {};
|
|
83
|
+
const [quotedType, quotedData] = quotedMessage ? extractMessage(quotedMessage) : [undefined, undefined];
|
|
84
|
+
|
|
85
|
+
const quotedKey = quotedData
|
|
86
|
+
? {
|
|
87
|
+
id: quotedId,
|
|
88
|
+
participant: quotedSender,
|
|
89
|
+
remoteJid: roomId,
|
|
90
|
+
fromMe: areJidsSameUser(quotedSender, myId),
|
|
91
|
+
}
|
|
92
|
+
: undefined;
|
|
93
|
+
|
|
94
|
+
let cachedTimeString;
|
|
95
|
+
let cachedQuoted;
|
|
96
|
+
|
|
97
|
+
const msg = {
|
|
98
|
+
type,
|
|
99
|
+
name,
|
|
100
|
+
id,
|
|
101
|
+
broadcast,
|
|
102
|
+
isForwarded,
|
|
103
|
+
forwardingScore,
|
|
104
|
+
fromMe,
|
|
105
|
+
jid,
|
|
106
|
+
roomId,
|
|
107
|
+
timestamp: toNumber(messageTimestamp),
|
|
108
|
+
isLid: isLidUser(jid),
|
|
109
|
+
device: getDevice(id),
|
|
110
|
+
isGroup: isJidGroup(roomId),
|
|
111
|
+
...extractContent(messageData),
|
|
112
|
+
key,
|
|
113
|
+
raw,
|
|
114
|
+
contextInfo: {
|
|
115
|
+
stanzaId: id,
|
|
116
|
+
participant: jid,
|
|
117
|
+
remoteJid: roomId,
|
|
118
|
+
},
|
|
119
|
+
load() {
|
|
120
|
+
return load(this.raw);
|
|
121
|
+
},
|
|
122
|
+
senderIs(targetId) {
|
|
123
|
+
return areJidsSameUser(this.jid, targetId);
|
|
124
|
+
},
|
|
125
|
+
pn() {
|
|
126
|
+
return getPN(sock, this.jid);
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
Object.defineProperty(msg, 'timeString', {
|
|
131
|
+
enumerable: true,
|
|
132
|
+
configurable: true,
|
|
133
|
+
get() {
|
|
134
|
+
if (cachedTimeString === undefined) {
|
|
135
|
+
cachedTimeString = getTimeString(messageTimestamp);
|
|
136
|
+
}
|
|
137
|
+
return cachedTimeString;
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
Object.defineProperty(msg, 'quoted', {
|
|
142
|
+
enumerable: true,
|
|
143
|
+
configurable: true,
|
|
144
|
+
get() {
|
|
145
|
+
if (!quotedData) return undefined;
|
|
146
|
+
if (cachedQuoted === undefined) {
|
|
147
|
+
let cachedQuotedTimeString;
|
|
148
|
+
cachedQuoted = {
|
|
149
|
+
type: quotedType,
|
|
150
|
+
jid: quotedSender,
|
|
151
|
+
id: quotedId,
|
|
152
|
+
...extractContent(quotedData),
|
|
153
|
+
key: quotedKey,
|
|
154
|
+
contextInfo: {
|
|
155
|
+
stanzaId: quotedId,
|
|
156
|
+
participant: quotedSender,
|
|
157
|
+
remoteJid: roomId,
|
|
158
|
+
},
|
|
159
|
+
raw: {
|
|
160
|
+
key: quotedKey,
|
|
161
|
+
message: quotedMessage,
|
|
162
|
+
},
|
|
163
|
+
load() {
|
|
164
|
+
return load(this.raw);
|
|
165
|
+
},
|
|
166
|
+
senderIs(targetId) {
|
|
167
|
+
return areJidsSameUser(this.jid, targetId);
|
|
168
|
+
},
|
|
169
|
+
pn() {
|
|
170
|
+
return getPN(sock, this.jid);
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
Object.defineProperty(cachedQuoted, 'timeString', {
|
|
174
|
+
enumerable: true,
|
|
175
|
+
configurable: true,
|
|
176
|
+
get() {
|
|
177
|
+
if (cachedQuotedTimeString === undefined) {
|
|
178
|
+
cachedQuotedTimeString = getTimeString(messageTimestamp);
|
|
179
|
+
}
|
|
180
|
+
return cachedQuotedTimeString;
|
|
181
|
+
},
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
return cachedQuoted;
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
return msg;
|
|
189
|
+
};
|
package/src/index.js
CHANGED
package/src/internals/config.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { cosmiconfig } from 'cosmiconfig';
|
|
2
|
-
import {
|
|
2
|
+
import { createDefu } from 'defu';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
const mergeConfig = createDefu((obj, key, value) => {
|
|
6
|
+
if (Array.isArray(value)) {
|
|
7
|
+
obj[key] = value;
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
3
11
|
|
|
4
12
|
const loadDefaultConfig = async () => {
|
|
5
13
|
try {
|
|
@@ -19,7 +27,11 @@ const loadDefaultConfig = async () => {
|
|
|
19
27
|
const loadUserConfig = async () => {
|
|
20
28
|
try {
|
|
21
29
|
const explorer = cosmiconfig('kittenwa');
|
|
22
|
-
const
|
|
30
|
+
const searchFrom = process.argv[1] ? path.dirname(path.resolve(process.argv[1])) : process.cwd();
|
|
31
|
+
let result = await explorer.search(searchFrom);
|
|
32
|
+
if (!result && searchFrom !== process.cwd()) {
|
|
33
|
+
result = await explorer.search(process.cwd());
|
|
34
|
+
}
|
|
23
35
|
return result?.config ?? {};
|
|
24
36
|
} catch (err) {
|
|
25
37
|
throw new Error(`[USER_CONFIG] Error loading user config: ${err.message}`, { cause: err });
|
|
@@ -32,7 +44,7 @@ export const loadConfig = async () => {
|
|
|
32
44
|
loadDefaultConfig()
|
|
33
45
|
]);
|
|
34
46
|
|
|
35
|
-
return
|
|
47
|
+
return mergeConfig(userConfig, defaultConfig);
|
|
36
48
|
};
|
|
37
49
|
|
|
38
50
|
let cachedConfig = null;
|
package/src/internals/index.js
CHANGED
|
@@ -1,54 +1,79 @@
|
|
|
1
|
-
import { open } from 'lmdb';
|
|
2
|
-
import { logger } from './logger.js';
|
|
3
|
-
import { getConfig } from './config.js';
|
|
4
|
-
|
|
5
|
-
const config = await getConfig();
|
|
6
|
-
|
|
7
|
-
class LMDBDatabaseManager {
|
|
8
|
-
#db = null;
|
|
9
|
-
#config = config.db;
|
|
10
|
-
#isClosing = false;
|
|
11
|
-
|
|
12
|
-
constructor(config) {
|
|
13
|
-
this.#config = { ...this.#config, ...config };
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
get db() {
|
|
17
|
-
if (this.#isClosing) {
|
|
18
|
-
throw new Error('[LMDBManager] Database is closing');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (!this.#db) {
|
|
22
|
-
this.#db = open(this.#config);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return this.#db;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
get isOpen() {
|
|
29
|
-
return this.#db !== null && !this.#isClosing;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
get config() {
|
|
33
|
-
return Object.freeze({ ...this.#config });
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
this.#db
|
|
49
|
-
this.#
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
1
|
+
import { open } from 'lmdb';
|
|
2
|
+
import { logger } from './logger.js';
|
|
3
|
+
import { getConfig } from './config.js';
|
|
4
|
+
|
|
5
|
+
const config = await getConfig();
|
|
6
|
+
|
|
7
|
+
class LMDBDatabaseManager {
|
|
8
|
+
#db = null;
|
|
9
|
+
#config = config.db;
|
|
10
|
+
#isClosing = false;
|
|
11
|
+
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.#config = { ...this.#config, ...config };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get db() {
|
|
17
|
+
if (this.#isClosing) {
|
|
18
|
+
throw new Error('[LMDBManager] Database is closing');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!this.#db) {
|
|
22
|
+
this.#db = open(this.#config);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return this.#db;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get isOpen() {
|
|
29
|
+
return this.#db !== null && !this.#isClosing;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get config() {
|
|
33
|
+
return Object.freeze({ ...this.#config });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async sync() {
|
|
37
|
+
if (this.#db && !this.#isClosing) {
|
|
38
|
+
await this.#db.flushed;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async close() {
|
|
43
|
+
if (!this.#db || this.#isClosing) return;
|
|
44
|
+
|
|
45
|
+
this.#isClosing = true;
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
await this.#db.flushed;
|
|
49
|
+
await this.#db.close();
|
|
50
|
+
} catch (err) {
|
|
51
|
+
logger.error(err, '[LMDBManager] Error closing database');
|
|
52
|
+
throw err;
|
|
53
|
+
} finally {
|
|
54
|
+
this.#db = null;
|
|
55
|
+
this.#isClosing = false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const LMDBManager = new LMDBDatabaseManager();
|
|
61
|
+
|
|
62
|
+
// Graceful shutdown hooks to guarantee asynchronous writes are flushed
|
|
63
|
+
const handleExit = async () => {
|
|
64
|
+
try {
|
|
65
|
+
await LMDBManager.close();
|
|
66
|
+
} catch {
|
|
67
|
+
/* noop */
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
process.once('beforeExit', handleExit);
|
|
72
|
+
process.once('SIGINT', async () => {
|
|
73
|
+
await handleExit();
|
|
74
|
+
process.exit(0);
|
|
75
|
+
});
|
|
76
|
+
process.once('SIGTERM', async () => {
|
|
77
|
+
await handleExit();
|
|
78
|
+
process.exit(0);
|
|
79
|
+
});
|
package/src/plugins/config.js
CHANGED
|
@@ -14,7 +14,7 @@ export const {
|
|
|
14
14
|
}
|
|
15
15
|
} = config.plugins;
|
|
16
16
|
|
|
17
|
-
export const PLUGIN_DIR = path.
|
|
17
|
+
export const PLUGIN_DIR = path.resolve(process.cwd(), dir);
|
|
18
18
|
|
|
19
19
|
export const EVENTS = Object.freeze(new Set([
|
|
20
20
|
'messaging-history.set', 'chats.upsert', 'chats.update', 'chats.delete',
|