@shoru/kitten 0.0.1-beta

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.
@@ -0,0 +1,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
+ let type = getContentType(message);
21
+ let data = message?.[type];
22
+
23
+ // cases like viewonce or document messages etc
24
+ if (data?.message) {
25
+ const innerType = getContentType(data.message);
26
+ if (innerType) {
27
+ type = innerType;
28
+ data = data.message[type];
29
+ }
30
+ }
31
+ return [type, data];
32
+ };
33
+
34
+ const extractContent = (message = {}) => ({
35
+ body: message.text ?? message.caption ??
36
+ (isString(message) ? message : undefined),
37
+ mentions: message.contextInfo?.mentionedJid,
38
+ groupMentions: message.contextInfo?.groupMentions,
39
+ mimetype: message.mimetype,
40
+ fileName: message.fileName,
41
+ pageCount: message.pageCount,
42
+ fileLength: toNumber(message.fileLength),
43
+ hash: toBase64(message.fileSha256),
44
+ isViewOnce: message.viewOnce,
45
+ url: message.matchedText,
46
+ description: message.description,
47
+ thumbnail: message.jpegThumbnail,
48
+ });
49
+
50
+ const load = async (x) => downloadMediaMessage(x, 'buffer', {});
51
+
52
+ export const formatMessage = (sock, raw) => {
53
+ const myId = jidNormalizedUser(sock.user.lid);
54
+ const [type, messageData] = extractMessage(raw.message);
55
+
56
+ const {
57
+ pushName: name,
58
+ messageTimestamp,
59
+ participant: p1,
60
+ broadcast,
61
+ key,
62
+ key: {
63
+ remoteJid: roomId,
64
+ id, fromMe,
65
+ participant: p2,
66
+ }
67
+ } = raw;
68
+
69
+ const jid = fromMe ? myId : p2 || p1 || roomId;
70
+
71
+ const {
72
+ quotedMessage,
73
+ participant: quotedSender,
74
+ stanzaId: quotedId,
75
+ isForwarded,
76
+ forwardingScore,
77
+ } = messageData?.contextInfo || {};
78
+ const [quotedType, quotedData] = extractMessage(quotedMessage);
79
+
80
+ const quotedKey = {
81
+ id: quotedId,
82
+ participant: quotedSender,
83
+ remoteJid: roomId,
84
+ fromMe: areJidsSameUser(quotedSender, myId)
85
+ };
86
+
87
+ return {
88
+ type,
89
+ name,
90
+ id,
91
+ broadcast,
92
+ isForwarded,
93
+ forwardingScore,
94
+ fromMe,
95
+ jid,
96
+ roomId,
97
+ timestamp: toNumber(messageTimestamp),
98
+ isLid: isLidUser(jid),
99
+ device: getDevice(id),
100
+ isGroup: isJidGroup(roomId),
101
+ timeString: getTimeString(messageTimestamp),
102
+ ...extractContent(messageData),
103
+ key, raw,
104
+ contextInfo: {
105
+ stanzaId: id,
106
+ participant: jid,
107
+ remoteJid: roomId
108
+ },
109
+ load() {
110
+ return load(this.raw)
111
+ },
112
+ senderIs(id) {
113
+ return areJidsSameUser(this.jid, id)
114
+ },
115
+ pn() {
116
+ return getPN(sock, this.jid)
117
+ },
118
+ quoted: quotedData ? {
119
+ type: quotedType,
120
+ jid: quotedSender,
121
+ id: quotedId,
122
+ ...extractContent(quotedData),
123
+ key: quotedKey,
124
+ contextInfo: {
125
+ stanzaId: quotedId,
126
+ participant: quotedSender,
127
+ remoteJid: roomId
128
+ },
129
+ raw: {
130
+ key: quotedKey,
131
+ message: quotedMessage
132
+ },
133
+ load() {
134
+ return load(this.raw)
135
+ },
136
+ senderIs(id) {
137
+ return areJidsSameUser(this.jid, id)
138
+ },
139
+ pn() {
140
+ return getPN(sock, this.jid)
141
+ }
142
+ }: undefined,
143
+ }
144
+ }
@@ -0,0 +1,11 @@
1
+ import { formatMessage } from './format-message.js';
2
+
3
+ export const formatter = async (wa, event, eventName) => {
4
+ eventName ??= 'massages.upsert';
5
+
6
+ switch (eventName) {
7
+ case 'massages.upsert':
8
+ return formatMessage(wa, event);
9
+ default: return event;
10
+ }
11
+ }
package/src/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from "#auth.js";
2
+ export * from "#client.js";
3
+ export * from "#internals.js";
4
+ export * from "#utils.js";
@@ -0,0 +1,40 @@
1
+ import { cosmiconfig } from 'cosmiconfig';
2
+ import { defu } from 'defu';
3
+
4
+ const loadDefaultConfig = async () => {
5
+ try {
6
+ const module = await import('../config/default.js');
7
+ const defaultConfig = module?.default ?? module;
8
+
9
+ if (typeof defaultConfig !== 'object' || defaultConfig === null || Array.isArray(defaultConfig)) {
10
+ throw new Error(`[INTERNAL_CONFIG] default config must export an object`);
11
+ }
12
+
13
+ return defaultConfig;
14
+ } catch (err) {
15
+ throw new Error(`[INTERNAL_CONFIG] Error loading default config: ${err.message}`, { cause: err });
16
+ }
17
+ };
18
+
19
+ const loadUserConfig = async () => {
20
+ try {
21
+ const explorer = cosmiconfig('kittenwa');
22
+ const result = await explorer.search();
23
+ return result?.config ?? {};
24
+ } catch (err) {
25
+ throw new Error(`[USER_CONFIG] Error loading user config: ${err.message}`, { cause: err });
26
+ }
27
+ };
28
+
29
+ export const loadConfig = async () => {
30
+ const [userConfig, defaultConfig] = await Promise.all([
31
+ loadUserConfig(),
32
+ loadDefaultConfig()
33
+ ]);
34
+
35
+ return defu(userConfig, defaultConfig);
36
+ };
37
+
38
+ const configObject = await loadConfig()
39
+
40
+ export const config = Object.freeze(configObject);
@@ -0,0 +1,5 @@
1
+ export * from './logger.js';
2
+ export * from './lmdb-manager.js';
3
+ export * from './config.js';
4
+ export * from './spinner.js';
5
+ export * from './plugin-manager.js'
@@ -0,0 +1,52 @@
1
+ import { open } from 'lmdb';
2
+ import { logger } from './logger.js';
3
+ import { config } from './config.js';
4
+
5
+ class LMDBDatabaseManager {
6
+ #db = null;
7
+ #config = config.db;
8
+ #isClosing = false;
9
+
10
+ constructor(config) {
11
+ this.#config = { ...this.#config, ...config };
12
+ }
13
+
14
+ get db() {
15
+ if (this.#isClosing) {
16
+ throw new Error('[LMDBManager] Database is closing');
17
+ }
18
+
19
+ if (!this.#db) {
20
+ this.#db = open(this.#config);
21
+ }
22
+
23
+ return this.#db;
24
+ }
25
+
26
+ get isOpen() {
27
+ return this.#db !== null && !this.#isClosing;
28
+ }
29
+
30
+ get config() {
31
+ return Object.freeze({ ...this.#config });
32
+ }
33
+
34
+ async close() {
35
+ if (!this.#db || this.#isClosing) return;
36
+
37
+ this.#isClosing = true;
38
+
39
+ try {
40
+ await this.#db.flushed;
41
+ await this.#db.close();
42
+ } catch (err) {
43
+ logger.error(err, '[LMDBManager] Error closing database');
44
+ throw err;
45
+ } finally {
46
+ this.#db = null;
47
+ this.#isClosing = false;
48
+ }
49
+ }
50
+ }
51
+
52
+ export const LMDBManager = new LMDBDatabaseManager();
@@ -0,0 +1,15 @@
1
+ import pino from "pino";
2
+
3
+ const logger = pino({
4
+ transport: {
5
+ target: "pino-pretty",
6
+ options: {
7
+ colorize: true,
8
+ ignore: "pid,hostname",
9
+ }
10
+ }
11
+ });
12
+
13
+ logger.prompt = console.log.bind(console);
14
+
15
+ export { logger, pino };