chomot 1.3.4 → 1.3.7
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/dist/factories/db.factory/db.factory.test.js +5 -8
- package/dist/factories/index.d.ts +0 -2
- package/dist/factories/index.js +0 -2
- package/dist/pipes/crypto.pipe/crypto.test.js +2 -4
- package/dist/pipes/crypto.pipe/genkey.js +11 -8
- package/dist/pipes/crypto.pipe/operations.d.ts +1 -1
- package/dist/pipes/crypto.pipe/operations.js +36 -29
- package/dist/pipes/crypto.pipe/sodium.d.ts +1 -1
- package/dist/pipes/crypto.pipe/sodium.js +1 -1
- package/dist/transit/index.d.ts +0 -1
- package/dist/transit/index.js +0 -1
- package/dist/types/http.types.d.ts +1 -0
- package/dist/types/http.types.js +1 -0
- package/dist/types/index.d.ts +0 -2
- package/dist/types/index.js +0 -2
- package/package.json +14 -21
- package/dist/factories/ioredis.factory/index.d.ts +0 -2
- package/dist/factories/ioredis.factory/index.js +0 -2
- package/dist/factories/whatsapp.factory/client.d.ts +0 -7
- package/dist/factories/whatsapp.factory/client.js +0 -44
- package/dist/factories/whatsapp.factory/index.d.ts +0 -1
- package/dist/factories/whatsapp.factory/index.js +0 -1
- package/dist/types/ioredis.types.d.ts +0 -1
- package/dist/types/ioredis.types.js +0 -1
- package/dist/types/whatsapp.types.d.ts +0 -9
- package/dist/types/whatsapp.types.js +0 -1
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import { describe, it } from "vitest";
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
2
|
import { createCursor, decodeCursor } from ".";
|
|
3
3
|
describe("Cursor", () => {
|
|
4
4
|
it("can create cursor", async () => {
|
|
5
5
|
const timestamp = new Date().toISOString();
|
|
6
|
-
const
|
|
7
|
-
const cursor = createCursor([
|
|
6
|
+
const plainCursor = [
|
|
8
7
|
{ column: "created_at", mark: timestamp, dir: "desc" },
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
console.log(cursor);
|
|
8
|
+
];
|
|
9
|
+
const cursor = createCursor(plainCursor);
|
|
12
10
|
const decodedCursor = decodeCursor(cursor);
|
|
13
|
-
|
|
14
|
-
console.log(decodedCursor);
|
|
11
|
+
expect(decodedCursor).toEqual(expect.arrayContaining([expect.objectContaining(plainCursor[0])]));
|
|
15
12
|
});
|
|
16
13
|
});
|
|
@@ -2,7 +2,5 @@ export * from "./app.factory";
|
|
|
2
2
|
export * from "./auth.factory";
|
|
3
3
|
export * from "./db.factory";
|
|
4
4
|
export * from "./faker.factory";
|
|
5
|
-
export * from "./ioredis.factory";
|
|
6
5
|
export * from "./response.factory";
|
|
7
6
|
export * from "./route.factory";
|
|
8
|
-
export * from "./whatsapp.factory";
|
package/dist/factories/index.js
CHANGED
|
@@ -2,7 +2,5 @@ export * from "./app.factory";
|
|
|
2
2
|
export * from "./auth.factory";
|
|
3
3
|
export * from "./db.factory";
|
|
4
4
|
export * from "./faker.factory";
|
|
5
|
-
export * from "./ioredis.factory";
|
|
6
5
|
export * from "./response.factory";
|
|
7
6
|
export * from "./route.factory";
|
|
8
|
-
export * from "./whatsapp.factory";
|
|
@@ -2,16 +2,14 @@ import { describe, expect, it } from "vitest";
|
|
|
2
2
|
import { decrypt, encrypt } from ".";
|
|
3
3
|
describe("encryption", () => {
|
|
4
4
|
it("can encrypt symetrically", async () => {
|
|
5
|
-
const plain = "secret";
|
|
5
|
+
const plain = "the-secret";
|
|
6
6
|
const encryptedText = await encrypt(plain);
|
|
7
|
-
const lateDecryptedText = await decrypt("3acbNCPcjvV6tRQPcikdBUI46HyYO0Mk__mVwd3I8USuNKPMutJig5MhmBfomw");
|
|
8
7
|
const decryptedText = await decrypt(encryptedText);
|
|
9
8
|
expect(plain).not.toEqual(encryptedText);
|
|
10
9
|
expect(decryptedText).toEqual(plain);
|
|
11
|
-
expect(lateDecryptedText).toEqual(plain);
|
|
12
10
|
});
|
|
13
11
|
it("can encrypt asymetrically", async () => {
|
|
14
|
-
const plain = "secret";
|
|
12
|
+
const plain = "the-secret";
|
|
15
13
|
const encryptedText = await encrypt(plain, "Asymetric");
|
|
16
14
|
const decryptedText = await decrypt(encryptedText, "Asymetric");
|
|
17
15
|
expect(plain).not.toEqual(encryptedText);
|
|
@@ -22,11 +22,11 @@ export async function getPublicKey() {
|
|
|
22
22
|
}
|
|
23
23
|
return appCryptography.publicKey;
|
|
24
24
|
}
|
|
25
|
-
async function
|
|
25
|
+
async function _getSalt() {
|
|
26
26
|
if (!appCryptography.salt) {
|
|
27
27
|
const sodium = await getSodium();
|
|
28
28
|
const publicKey = await getPublicKey();
|
|
29
|
-
appCryptography.salt = sodium.crypto_generichash(
|
|
29
|
+
appCryptography.salt = sodium.crypto_generichash(64, publicKey, null);
|
|
30
30
|
}
|
|
31
31
|
return appCryptography.salt;
|
|
32
32
|
}
|
|
@@ -34,9 +34,8 @@ export async function getSymetricKey() {
|
|
|
34
34
|
if (!appCryptography.symetricKey) {
|
|
35
35
|
const sodium = await getSodium();
|
|
36
36
|
const privateKey = await getPrivateKey();
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
privateKey, salt, sodium.crypto_pwhash_OPSLIMIT_MODERATE, sodium.crypto_pwhash_MEMLIMIT_MODERATE, sodium.crypto_pwhash_ALG_DEFAULT);
|
|
37
|
+
appCryptography.symetricKey = sodium.crypto_generichash(sodium.crypto_secretbox_KEYBYTES, // 32 byte output
|
|
38
|
+
privateKey, null);
|
|
40
39
|
}
|
|
41
40
|
return appCryptography.symetricKey;
|
|
42
41
|
}
|
|
@@ -51,10 +50,14 @@ export default async function run() {
|
|
|
51
50
|
const publicKeyPath = join(certDir, "public.crypto");
|
|
52
51
|
writeFileSync(privateKeyPath, privateKey);
|
|
53
52
|
writeFileSync(publicKeyPath, publicKey);
|
|
54
|
-
console.log("✅ Key pair berhasil dibuat di folder /
|
|
53
|
+
console.log("✅ Key pair berhasil dibuat di folder /crypto:");
|
|
55
54
|
console.log(`🔐 ${privateKeyPath}`);
|
|
56
55
|
console.log(`🔓 ${publicKeyPath}`);
|
|
57
56
|
}
|
|
58
|
-
if (import.meta.
|
|
59
|
-
|
|
57
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
58
|
+
const args = process.argv.slice(2);
|
|
59
|
+
const hasFlag = args.includes("--genkey");
|
|
60
|
+
if (hasFlag) {
|
|
61
|
+
run();
|
|
62
|
+
}
|
|
60
63
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export type EncryptionType = "Symetric" | "Asymetric";
|
|
2
|
-
export declare function decrypt(
|
|
2
|
+
export declare function decrypt(cipherBase64: string, type?: EncryptionType): Promise<string>;
|
|
3
3
|
export declare function encrypt(plain: string, type?: EncryptionType): Promise<string>;
|
|
@@ -1,41 +1,48 @@
|
|
|
1
1
|
import { getPrivateKey, getPublicKey, getSymetricKey } from "./genkey";
|
|
2
2
|
import { getSodium } from "./sodium";
|
|
3
|
-
export async function decrypt(
|
|
3
|
+
export async function decrypt(cipherBase64, type = "Symetric") {
|
|
4
4
|
const sodium = await getSodium();
|
|
5
|
-
const publicKey = await getPublicKey();
|
|
6
|
-
const privateKey = await getPrivateKey();
|
|
7
5
|
if (type === "Asymetric") {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
throw new Error("Key not initialized, call deriveKeyFromPassword() first");
|
|
14
|
-
const combined = sodium.from_base64(encrypted);
|
|
15
|
-
const nonce = combined.slice(0, sodium.crypto_secretbox_NONCEBYTES);
|
|
16
|
-
const ciphertext = combined.slice(sodium.crypto_secretbox_NONCEBYTES);
|
|
17
|
-
const decrypted = sodium.crypto_secretbox_open_easy(ciphertext, nonce, symetricKey);
|
|
18
|
-
if (!decrypted)
|
|
19
|
-
throw new Error("Decryption failed");
|
|
20
|
-
return sodium.to_string(decrypted);
|
|
6
|
+
const publicKey = await getPublicKey();
|
|
7
|
+
const privateKey = await getPrivateKey();
|
|
8
|
+
const cipher = sodium.from_base64(cipherBase64, sodium.base64_variants.ORIGINAL);
|
|
9
|
+
const plain = sodium.crypto_box_seal_open(cipher, publicKey, privateKey);
|
|
10
|
+
return sodium.to_string(plain);
|
|
21
11
|
}
|
|
12
|
+
// === SYMMETRIC ===
|
|
13
|
+
const key = await getSymetricKey();
|
|
14
|
+
if (!key)
|
|
15
|
+
throw new Error("Key not initialized");
|
|
16
|
+
const combined = sodium.from_base64(cipherBase64);
|
|
17
|
+
const nonceBytes = sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES;
|
|
18
|
+
const nonce = combined.slice(0, nonceBytes);
|
|
19
|
+
const cipher = combined.slice(nonceBytes);
|
|
20
|
+
const plain = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(null, // nsec
|
|
21
|
+
cipher, null, // AAD
|
|
22
|
+
nonce, key);
|
|
23
|
+
return sodium.to_string(plain);
|
|
22
24
|
}
|
|
23
25
|
export async function encrypt(plain, type = "Symetric") {
|
|
24
26
|
const sodium = await getSodium();
|
|
25
|
-
const publicKey = await getPublicKey();
|
|
26
27
|
if (type === "Asymetric") {
|
|
27
|
-
//
|
|
28
|
-
|
|
28
|
+
const publicKey = await getPublicKey(); // Uint8Array
|
|
29
|
+
const cipher = sodium.crypto_box_seal(sodium.from_string(plain), publicKey);
|
|
30
|
+
return sodium.to_base64(cipher, sodium.base64_variants.ORIGINAL);
|
|
29
31
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const combined = new Uint8Array(nonce.length + ciphertext.length);
|
|
37
|
-
combined.set(nonce);
|
|
38
|
-
combined.set(ciphertext, nonce.length);
|
|
39
|
-
return sodium.to_base64(combined);
|
|
32
|
+
// === SYMMETRIC ===
|
|
33
|
+
const key = await getSymetricKey(); // Uint8Array (32 bytes)
|
|
34
|
+
if (!key)
|
|
35
|
+
throw new Error("Key not initialized, call deriveKeyFromPassword() first");
|
|
36
|
+
if (key.length !== sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) {
|
|
37
|
+
throw new Error("Invalid symmetric key length");
|
|
40
38
|
}
|
|
39
|
+
const nonce = sodium.randombytes_buf(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES);
|
|
40
|
+
const cipher = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(sodium.from_string(plain), null, // additional data (AAD)
|
|
41
|
+
null, // nsec (unused)
|
|
42
|
+
nonce, key);
|
|
43
|
+
// nonce + cipher (same pattern as sebelum)
|
|
44
|
+
const combined = new Uint8Array(nonce.length + cipher.length);
|
|
45
|
+
combined.set(nonce);
|
|
46
|
+
combined.set(cipher, nonce.length);
|
|
47
|
+
return sodium.to_base64(combined);
|
|
41
48
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import _sodium from "libsodium-wrappers
|
|
1
|
+
import _sodium from "libsodium-wrappers";
|
|
2
2
|
export declare function getSodium(): Promise<typeof _sodium>;
|
package/dist/transit/index.d.ts
CHANGED
package/dist/transit/index.js
CHANGED
package/dist/types/http.types.js
CHANGED
package/dist/types/index.d.ts
CHANGED
package/dist/types/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chomot",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.7",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -12,34 +12,27 @@
|
|
|
12
12
|
"author": "smwgn1331",
|
|
13
13
|
"license": "ISC",
|
|
14
14
|
"devDependencies": {
|
|
15
|
-
"@biomejs/biome": "^2.
|
|
16
|
-
"@types/
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"tsx": "^4.20.6",
|
|
15
|
+
"@biomejs/biome": "^2.3.14",
|
|
16
|
+
"@types/node": "^25.2.0",
|
|
17
|
+
"rimraf": "^6.1.2",
|
|
18
|
+
"tsx": "^4.21.0",
|
|
20
19
|
"typescript": "^5.9.3",
|
|
21
|
-
"vitest": "^
|
|
22
|
-
},
|
|
23
|
-
"peerDependencies": {
|
|
24
|
-
"@whiskeysockets/baileys": "7.0.0-rc.6"
|
|
20
|
+
"vitest": "^4.0.18"
|
|
25
21
|
},
|
|
26
22
|
"dependencies": {
|
|
27
|
-
"@faker-js/faker": "^10.
|
|
23
|
+
"@faker-js/faker": "^10.2.0",
|
|
28
24
|
"@hapi/boom": "^10.0.1",
|
|
29
|
-
"@hono/node-server": "^1.19.
|
|
30
|
-
"@hono/zod-openapi": "^1.1
|
|
31
|
-
"@scalar/hono-api-reference": "^0.9.
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"hono": "^4.9.12",
|
|
36
|
-
"hono-rate-limiter": "^0.4.2",
|
|
37
|
-
"ioredis": "^5.8.2",
|
|
38
|
-
"libsodium-wrappers-sumo": "^0.7.15"
|
|
25
|
+
"@hono/node-server": "^1.19.9",
|
|
26
|
+
"@hono/zod-openapi": "^1.2.1",
|
|
27
|
+
"@scalar/hono-api-reference": "^0.9.40",
|
|
28
|
+
"hono": "^4.11.7",
|
|
29
|
+
"hono-rate-limiter": "^0.5.3",
|
|
30
|
+
"libsodium-wrappers": "^0.8.2"
|
|
39
31
|
},
|
|
40
32
|
"scripts": {
|
|
41
33
|
"clean": "rimraf dist",
|
|
42
34
|
"build": "npm run clean && tsc",
|
|
35
|
+
"key:generate": "bun ./src/pipes/crypto.pipe/genkey.ts --genkey",
|
|
43
36
|
"linter": "biome check ./src",
|
|
44
37
|
"linter:fix": "biome check ./src --write",
|
|
45
38
|
"linter:ufix": "biome check ./src --write --unsafe",
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { type WAMessage, type WASocket } from "@whiskeysockets/baileys";
|
|
2
|
-
import type { WhatsAppHandlers } from "../../types/whatsapp.types";
|
|
3
|
-
export declare function createWhatsappSession(accountID: string, handlers?: WhatsAppHandlers, opt?: {
|
|
4
|
-
markOnlineOnConnect: boolean;
|
|
5
|
-
syncFullHistory: boolean;
|
|
6
|
-
}): Promise<WASocket>;
|
|
7
|
-
export declare function saveWhatsappMedia(msg: WAMessage, folder: string, filename: string): Promise<string>;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import makeWASocket, { DisconnectReason, downloadMediaMessage, useMultiFileAuthState, } from "@whiskeysockets/baileys";
|
|
4
|
-
export async function createWhatsappSession(accountID, handlers = {}, opt = { syncFullHistory: false, markOnlineOnConnect: false }) {
|
|
5
|
-
const { state, saveCreds } = await useMultiFileAuthState(`.whatsapp_auth_${accountID}`);
|
|
6
|
-
const sock = makeWASocket({
|
|
7
|
-
auth: state,
|
|
8
|
-
syncFullHistory: opt.syncFullHistory,
|
|
9
|
-
markOnlineOnConnect: opt.markOnlineOnConnect
|
|
10
|
-
});
|
|
11
|
-
sock.ev.on("connection.update", (update) => {
|
|
12
|
-
const { connection, lastDisconnect } = update;
|
|
13
|
-
handlers.onConnectionUpdate?.(update, sock);
|
|
14
|
-
if (connection === "close") {
|
|
15
|
-
const shouldReconnect = lastDisconnect?.error?.output?.statusCode !==
|
|
16
|
-
DisconnectReason.loggedOut;
|
|
17
|
-
if (shouldReconnect) {
|
|
18
|
-
createWhatsappSession(accountID, handlers);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
else if (connection === "open") {
|
|
22
|
-
console.log(`[${accountID}] Connection opened`);
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
sock.ev.on("messages.upsert", async (event) => {
|
|
26
|
-
for (const m of event.messages) {
|
|
27
|
-
await handlers.onMessage?.(m, sock);
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
sock.ev.on("creds.update", saveCreds);
|
|
31
|
-
sock.ev.on("chats.update", (args) => {
|
|
32
|
-
handlers.onChatsUpdate?.(args, sock);
|
|
33
|
-
});
|
|
34
|
-
return sock;
|
|
35
|
-
}
|
|
36
|
-
export async function saveWhatsappMedia(msg, folder, filename) {
|
|
37
|
-
const buffer = await downloadMediaMessage(msg, "buffer", {});
|
|
38
|
-
const dir = path.join(process.cwd(), "media", folder);
|
|
39
|
-
if (!fs.existsSync(dir))
|
|
40
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
41
|
-
const filePath = path.join(dir, filename);
|
|
42
|
-
fs.writeFileSync(filePath, buffer);
|
|
43
|
-
return filePath;
|
|
44
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./client";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./client";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type { Redis } from "ioredis";
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { Chat, ConnectionState, WAMessage, WASocket } from "@whiskeysockets/baileys";
|
|
2
|
-
export { WASocket } from "@whiskeysockets/baileys";
|
|
3
|
-
export type WhatsAppSession = Map<string, WASocket>;
|
|
4
|
-
export type MessageType = "text" | "image" | "video" | "audio" | "document" | "sticker" | "location" | "contact" | "reaction" | null;
|
|
5
|
-
export interface WhatsAppHandlers {
|
|
6
|
-
onConnectionUpdate?: (update: Partial<ConnectionState>, sock: WASocket) => void;
|
|
7
|
-
onMessage?: (message: WAMessage, sock: WASocket) => Promise<void> | void;
|
|
8
|
-
onChatsUpdate?: (chats: Chat[] | Partial<Chat>[], sock: WASocket) => void;
|
|
9
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|