zaileys 0.29.7-beta → 0.29.9-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.
package/package.json CHANGED
@@ -1,7 +1,23 @@
1
1
  {
2
2
  "name": "zaileys",
3
- "version": "0.29.07-beta",
3
+ "version": "0.29.9-beta",
4
4
  "description": "Zaileys - Simplify Typescript/Javascript WhatsApp NodeJS API",
5
+ "main": "dist/zaileys.cjs.js",
6
+ "module": "dist/zaileys.esm.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/zaileys.esm.js",
11
+ "require": "./dist/zaileys.cjs.js"
12
+ }
13
+ },
14
+ "scripts": {
15
+ "build": "rollup --bundleConfigAsCjs -c",
16
+ "watch": "rollup --bundleConfigAsCjs -c -w",
17
+ "dev": "concurrently \"npm:dev:test\"",
18
+ "dev:test": "npx tsx watch test/example.ts",
19
+ "clean": "rimraf dist"
20
+ },
5
21
  "keywords": [
6
22
  "zaileys",
7
23
  "whatsapp",
@@ -29,39 +45,36 @@
29
45
  "email": "zaadevofc@gmail.com",
30
46
  "url": "https://github.com/zeative"
31
47
  },
32
- "license": "MIT",
33
- "main": "dist/index.js",
34
- "module": "dist/index.mjs",
35
- "types": "dist/index.d.ts",
36
48
  "engines": {
37
49
  "node": ">=18"
38
50
  },
39
- "scripts": {
40
- "build": "tsup",
41
- "dev": "npx ts-node-dev test/example",
42
- "test": "npx ts-node test/example",
43
- "docs": "typedoc",
44
- "prod": "tsup && typedoc && npm publish --tag latest"
51
+ "license": "MIT",
52
+ "devDependencies": {
53
+ "@rollup/plugin-commonjs": "^24.0.0",
54
+ "@rollup/plugin-json": "^6.1.0",
55
+ "@rollup/plugin-node-resolve": "^15.0.0",
56
+ "@rollup/plugin-typescript": "^12.0.0",
57
+ "@types/better-sqlite3": "^7.6.13",
58
+ "@types/pg": "^8.15.1",
59
+ "concurrently": "^8.0.0",
60
+ "rimraf": "^5.0.0",
61
+ "rollup": "^3.0.0",
62
+ "rollup-plugin-dts": "^5.0.0",
63
+ "rollup-plugin-esbuild": "^6.2.1",
64
+ "rollup-plugin-strip": "^1.2.2",
65
+ "rollup-plugin-terser": "^7.0.0",
66
+ "tsx": "^4.19.4",
67
+ "typescript": "^5.0.0",
68
+ "zod": "^3.24.4"
45
69
  },
46
70
  "dependencies": {
47
- "@whiskeysockets/baileys": "^6.7.12",
48
- "awesome-phonenumber": "^7.2.0",
49
- "chalk": "^5.4.1",
50
- "consola": "^3.4.0",
51
- "figlet": "^1.8.0",
52
- "jimp": "0.22.12",
71
+ "baileys": "^6.7.16",
72
+ "better-sqlite3": "^11.10.0",
73
+ "kysely": "^0.28.2",
74
+ "libsignal": "^2.0.1",
75
+ "mysql2": "^3.14.1",
53
76
  "node-cache": "^5.1.2",
54
- "ora": "^8.2.0",
55
- "pino": "^9.6.0",
56
- "qrcode-terminal": "^0.12.0"
57
- },
58
- "devDependencies": {
59
- "@types/node": "^16.0.0",
60
- "ts-node": "^10.8.1",
61
- "ts-node-dev": "^2.0.0",
62
- "tsup": "^8.3.6",
63
- "typedoc": "^0.24.7",
64
- "typedoc-material-theme": "^1.3.0",
65
- "typescript": "^4.6.4"
77
+ "pg": "^8.15.6",
78
+ "pino": "^9.6.0"
66
79
  }
67
80
  }
@@ -0,0 +1,38 @@
1
+ import commonjs from '@rollup/plugin-commonjs';
2
+ import { nodeResolve } from '@rollup/plugin-node-resolve';
3
+ import typescript from '@rollup/plugin-typescript';
4
+ import { dts } from 'rollup-plugin-dts';
5
+ import { minify } from 'rollup-plugin-esbuild';
6
+ import strip from 'rollup-plugin-strip';
7
+ import { terser } from 'rollup-plugin-terser';
8
+
9
+ /** @type {import('rollup').RollupOptions[]} */
10
+ export default [
11
+ {
12
+ input: 'src/index.ts',
13
+ external: Object.keys(require('./package.json').dependencies || {}),
14
+ treeshake: {
15
+ moduleSideEffects: false,
16
+ propertyReadSideEffects: false
17
+ },
18
+ output: [
19
+ { file: 'dist/zaileys.cjs.js', format: 'cjs', sourcemap: false },
20
+ { file: 'dist/zaileys.esm.js', format: 'esm', sourcemap: false }
21
+ ],
22
+ plugins: [
23
+ nodeResolve({
24
+ extensions: ['.js', '.ts']
25
+ }), ,
26
+ commonjs(),
27
+ minify(),
28
+ typescript(),
29
+ strip({ functions: ['assert.*'] }),
30
+ terser({ format: { comments: false } })
31
+ ]
32
+ },
33
+ {
34
+ input: 'src/index.ts',
35
+ output: { file: 'dist/index.d.ts', format: 'es' },
36
+ plugins: [dts(), typescript()]
37
+ }
38
+ ];
@@ -0,0 +1,73 @@
1
+ import makeWASocket, { Browsers, makeCacheableSignalKeyStore } from "baileys";
2
+ import { Kysely, sql } from "kysely";
3
+ import NodeCache from "node-cache";
4
+ import pino from "pino";
5
+ import { z } from "zod";
6
+ import { AuthAdapterHandler, ConnectDB, StoreAdapterHandler } from "../database/handler";
7
+ import { DB } from "../database/schema";
8
+ import { ClientClassesType } from "../types/classes/client";
9
+ import { EventCallbackType, EventEnumType } from "../types/classes/event";
10
+ import Event from "./Event";
11
+
12
+ class Client {
13
+ options: z.infer<typeof ClientClassesType> | undefined;
14
+ private chatId = "zaileys-chats";
15
+
16
+ private logger = pino({ level: "silent", enabled: false });
17
+ private event: Event;
18
+ private db: Kysely<DB> | undefined;
19
+ private socket: ReturnType<typeof makeWASocket> | undefined;
20
+ private groupCache = new NodeCache({ stdTTL: 5 * 60, useClones: false });
21
+
22
+ constructor(private props: z.input<typeof ClientClassesType>) {
23
+ this.initialize();
24
+ this.event = new Event(this);
25
+ }
26
+
27
+ async initialize() {
28
+ this.options = await ClientClassesType.parseAsync(this.props);
29
+ this.db = ConnectDB(this.options!.database.type, this.options!.database.connection.url);
30
+
31
+ const { state, saveCreds, removeCreds } = await AuthAdapterHandler(this.db, this.chatId);
32
+ const store = await StoreAdapterHandler(this.db, this.chatId);
33
+
34
+ this.socket = makeWASocket({
35
+ logger: this.logger,
36
+ markOnlineOnConnect: this.options.autoOnline,
37
+ syncFullHistory: false,
38
+ defaultQueryTimeoutMs: undefined,
39
+ msgRetryCounterCache: new NodeCache(),
40
+ cachedGroupMetadata: async (jid) => this.groupCache.get(jid),
41
+ printQRInTerminal: this.options.authType == "qr",
42
+ browser: Browsers.ubuntu(this.options.authType == "qr" ? "Zaileys Library" : "Firefox"),
43
+ auth: {
44
+ creds: state.creds,
45
+ keys: makeCacheableSignalKeyStore(state.keys, this.logger),
46
+ },
47
+ });
48
+
49
+ if (this.options.authType == "pairing" && this.options.phoneNumber && !this.socket?.authState.creds.registered) {
50
+ setTimeout(async () => {
51
+ try {
52
+ if (this.options?.authType == "pairing") {
53
+ const code = await this.socket?.requestPairingCode(this.options.phoneNumber.toString());
54
+ console.log("🚀 ~ Client.ts:53 ~ Client ~ setTimeout ~ code:", code);
55
+ }
56
+ } catch {
57
+ console.log("Connection failed");
58
+ process.exit(1);
59
+ }
60
+ }, 5000);
61
+ }
62
+
63
+ this.socket?.ev.on("creds.update", saveCreds);
64
+ store.bind(this.socket?.ev);
65
+ this.event.setup(this.socket);
66
+ }
67
+
68
+ on<T extends EventEnumType>(event: T, handler: EventCallbackType[T]) {
69
+ this.event.on(event, handler);
70
+ }
71
+ }
72
+
73
+ export default Client;
@@ -0,0 +1,59 @@
1
+ import { z } from "zod";
2
+ import { DisconnectReason, WASocket } from "baileys";
3
+ import { EventCallbackType, EventEnumType } from "../types/classes/event";
4
+ import Client from "./Client";
5
+
6
+ class Event {
7
+ private events: Map<EventEnumType, Function[]> = new Map();
8
+
9
+ constructor(private client: Client) {}
10
+
11
+ setup(sock: WASocket) {
12
+ sock.ev.on("connection.update", async (update) => {
13
+ const { connection, lastDisconnect, qr } = update;
14
+ this.emit("connection", { status: connection || "connecting" });
15
+
16
+ if (this.client.options?.authType == "qr" && qr) {
17
+ console.log("Scan qrcode with your whatsapp: ");
18
+ }
19
+
20
+ if (connection === "close") {
21
+ const code = (lastDisconnect?.error as any)?.output?.statusCode;
22
+ const isReconnect = code !== DisconnectReason.loggedOut;
23
+ console.log(lastDisconnect?.error?.message);
24
+
25
+ if (code == 401 || code == 405 || code == 500) {
26
+ // await this.client.logout();
27
+ // await this.client.initialize();
28
+ return;
29
+ }
30
+
31
+ if (isReconnect) await this.client.initialize();
32
+ } else if (connection === "open") {
33
+ this.emit("connection", { status: "open" });
34
+ }
35
+ });
36
+
37
+ sock.ev.on("messages.upsert", ({ messages }) => {
38
+ this.emit("messages", messages as never);
39
+ });
40
+
41
+ sock.ev.on("call", (call) => {
42
+ this.emit("call", call as never);
43
+ });
44
+ }
45
+
46
+ on<T extends EventEnumType>(event: T, handler: EventCallbackType[T]) {
47
+ if (!this.events.has(event)) {
48
+ this.events.set(event, []);
49
+ }
50
+ this.events.get(event)!.push(handler);
51
+ }
52
+
53
+ private emit<T extends EventEnumType>(event: T, data: Parameters<EventCallbackType[T]>[0]) {
54
+ const handlers = this.events.get(event) || [];
55
+ handlers.forEach((handler) => handler(data));
56
+ }
57
+ }
58
+
59
+ export default Event;
@@ -0,0 +1,3 @@
1
+ export default class Parser {
2
+ constructor(parameters) {}
3
+ }
@@ -0,0 +1,272 @@
1
+ import Database from "better-sqlite3";
2
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
3
+ import { Kysely, MysqlDialect, PostgresDialect, SqliteDialect } from "kysely";
4
+ import mysql from "mysql2";
5
+ import path from "path";
6
+ import { Pool } from "pg";
7
+ import { URL } from "url";
8
+ import { z } from "zod";
9
+ import { BufferJSON, fromObject, initAuthCreds } from "../helpers/adapter";
10
+ import { AuthAdapterHandlerType, AuthenticationCreds, SignalDataTypeMap } from "../types/adapter/general";
11
+ import { AdapterDatabaseType, ClientClassesType } from "../types/classes/client";
12
+ import type { DB } from "./schema";
13
+ import { BaileysEventEmitter } from "baileys";
14
+
15
+ export const ConnectDB = (type: z.infer<typeof AdapterDatabaseType>["type"], url: string): Kysely<DB> => {
16
+ if (type === "sqlite") {
17
+ const filepath = url || "./db/zaileys.db";
18
+ const resolvedPath = path.resolve(filepath);
19
+
20
+ mkdirSync(path.dirname(resolvedPath), { recursive: true });
21
+ writeFileSync(resolvedPath, "", { flag: "a" });
22
+
23
+ return new Kysely<DB>({
24
+ dialect: new SqliteDialect({
25
+ database: new Database(resolvedPath),
26
+ }),
27
+ });
28
+ }
29
+
30
+ const conn = new URL(url);
31
+ const protocol = conn.protocol.replace(":", "");
32
+
33
+ if (type === "mysql") {
34
+ return new Kysely<DB>({
35
+ dialect: new MysqlDialect({
36
+ pool: mysql.createPool({
37
+ host: conn.hostname,
38
+ user: conn.username,
39
+ password: conn.password,
40
+ database: conn.pathname.replace("/", ""),
41
+ port: parseInt(conn.port || "3306", 10),
42
+ }),
43
+ }),
44
+ });
45
+ }
46
+
47
+ if (type === "postgresql") {
48
+ return new Kysely<DB>({
49
+ dialect: new PostgresDialect({
50
+ pool: new Pool({
51
+ host: conn.hostname,
52
+ user: conn.username,
53
+ password: conn.password,
54
+ database: conn.pathname.replace("/", ""),
55
+ port: parseInt(conn.port || "5432", 10),
56
+ }),
57
+ }),
58
+ });
59
+ }
60
+
61
+ throw new Error(`Unsupported database protocol: ${protocol}`);
62
+ };
63
+
64
+ export const MigrateDB = async (db: Kysely<DB>) => {
65
+ await db.schema
66
+ .createTable("auth")
67
+ .ifNotExists()
68
+ .addColumn("session", "varchar(50)", (col) => col.notNull())
69
+ .addColumn("id", "varchar(80)", (col) => col.notNull())
70
+ .addColumn("value", "text", (col) => col.defaultTo(null))
71
+ .addUniqueConstraint("auth_session_id_unique", ["session", "id"])
72
+ .execute();
73
+
74
+ await db.schema
75
+ .createTable("chats")
76
+ .ifNotExists()
77
+ .addColumn("session", "varchar(50)", (col) => col.notNull())
78
+ .addColumn("id", "varchar(80)", (col) => col.notNull())
79
+ .addColumn("value", "text", (col) => col.defaultTo(null))
80
+ .addUniqueConstraint("chats_session_id_unique", ["session", "id"])
81
+ .execute();
82
+
83
+ await db.schema
84
+ .createTable("contacts")
85
+ .ifNotExists()
86
+ .addColumn("session", "varchar(50)", (col) => col.notNull())
87
+ .addColumn("id", "varchar(80)", (col) => col.notNull())
88
+ .addColumn("value", "text", (col) => col.defaultTo(null))
89
+ .addUniqueConstraint("contacts_session_id_unique", ["session", "id"])
90
+ .execute();
91
+
92
+ await db.schema
93
+ .createTable("messages")
94
+ .ifNotExists()
95
+ .addColumn("session", "varchar(50)", (col) => col.notNull())
96
+ .addColumn("id", "varchar(80)", (col) => col.notNull())
97
+ .addColumn("value", "text", (col) => col.defaultTo(null))
98
+ .addUniqueConstraint("messages_session_id_unique", ["session", "id"])
99
+ .execute();
100
+
101
+ await db.schema.createIndex("auth_session_idx").ifNotExists().on("auth").column("session").execute();
102
+ await db.schema.createIndex("auth_id_idx").ifNotExists().on("auth").column("id").execute();
103
+ await db.schema.createIndex("chats_session_idx").ifNotExists().on("chats").column("session").execute();
104
+ await db.schema.createIndex("chats_id_idx").ifNotExists().on("chats").column("id").execute();
105
+ await db.schema.createIndex("contacts_session_idx").ifNotExists().on("contacts").column("session").execute();
106
+ await db.schema.createIndex("contacts_id_idx").ifNotExists().on("contacts").column("id").execute();
107
+ await db.schema.createIndex("messages_session_idx").ifNotExists().on("messages").column("session").execute();
108
+ await db.schema.createIndex("messages_id_idx").ifNotExists().on("messages").column("id").execute();
109
+ };
110
+
111
+ export const AuthAdapterHandler = async (db: Kysely<DB>, session: string): AuthAdapterHandlerType => {
112
+ const TABLE_NAME = "auth";
113
+ const RETRY_DELAY = 200;
114
+ const MAX_RETRIES = 10;
115
+
116
+ await MigrateDB(db);
117
+
118
+ const retry = async <T>(fn: () => Promise<T>): Promise<T> => {
119
+ for (let x = 0; x < MAX_RETRIES; x++) {
120
+ try {
121
+ return await fn();
122
+ } catch (e) {
123
+ await new Promise((r) => setTimeout(r, RETRY_DELAY));
124
+ }
125
+ }
126
+ throw new Error("Max retries reached");
127
+ };
128
+
129
+ const readData = async (id: string) => {
130
+ const row = await retry(() => db.selectFrom(TABLE_NAME).select(["value"]).where("id", "=", id).where("session", "=", session).executeTakeFirst());
131
+
132
+ if (!row?.value) return null;
133
+
134
+ const credsStr = typeof row.value === "object" ? JSON.stringify(row.value) : row.value;
135
+ return JSON.parse(credsStr, BufferJSON.reviver);
136
+ };
137
+
138
+ const writeData = async (id: string, value: object) => {
139
+ const valueFixed = JSON.stringify(value, BufferJSON.replacer);
140
+
141
+ await retry(() =>
142
+ db
143
+ .insertInto(TABLE_NAME)
144
+ .values({
145
+ session: session,
146
+ id,
147
+ value: valueFixed,
148
+ })
149
+ .onConflict((oc) => oc.columns(["session", "id"]).doUpdateSet({ value: valueFixed }))
150
+ .execute()
151
+ );
152
+ };
153
+
154
+ const removeData = async (id: string) => {
155
+ await retry(() => db.deleteFrom(TABLE_NAME).where("id", "=", id).where("session", "=", session).execute());
156
+ };
157
+
158
+ const clearAll = async () => {
159
+ await retry(() => db.deleteFrom(TABLE_NAME).where("session", "=", session).where("id", "!=", "creds").execute());
160
+ };
161
+
162
+ const removeAll = async () => {
163
+ await retry(() => db.deleteFrom(TABLE_NAME).where("session", "=", session).execute());
164
+ };
165
+
166
+ const creds: AuthenticationCreds = (await readData("creds")) || initAuthCreds();
167
+
168
+ return {
169
+ state: {
170
+ creds: creds,
171
+ keys: {
172
+ get: async (type, ids) => {
173
+ const data: { [id: string]: SignalDataTypeMap[typeof type] } = {};
174
+ for (const id of ids) {
175
+ let value = await readData(`${type}-${id}`);
176
+ if (type === "app-state-sync-key" && value) {
177
+ value = fromObject(value);
178
+ }
179
+ data[id] = value;
180
+ }
181
+ return data;
182
+ },
183
+ set: async (data) => {
184
+ for (const category in data) {
185
+ for (const id in data[category as never] as any) {
186
+ const value = data[category as never][id];
187
+ const name = `${category}-${id}`;
188
+ if (value) {
189
+ await writeData(name, value);
190
+ } else {
191
+ await removeData(name);
192
+ }
193
+ }
194
+ }
195
+ },
196
+ },
197
+ },
198
+ saveCreds: async () => {
199
+ await writeData("creds", creds);
200
+ },
201
+ clear: async () => {
202
+ await clearAll();
203
+ },
204
+ removeCreds: async () => {
205
+ await removeAll();
206
+ },
207
+ };
208
+ };
209
+
210
+ export const StoreAdapterHandler = async (db: Kysely<DB>, session: string) => {
211
+ return {
212
+ bind: (event: BaileysEventEmitter) => {
213
+ event.on("messaging-history.set", async (update) => {
214
+ const { chats, contacts, messages } = update;
215
+
216
+ for (const chat of chats) {
217
+ await db
218
+ .insertInto("chats")
219
+ .values({ session, id: chat.id!, value: JSON.stringify(chat) })
220
+ .onConflict((oc) => oc.columns(["session", "id"]).doUpdateSet({ value: JSON.stringify(chat) }))
221
+ .execute();
222
+ }
223
+
224
+ for (const contact of contacts) {
225
+ await db
226
+ .insertInto("contacts")
227
+ .values({ session, id: contact.id!, value: JSON.stringify(contact) })
228
+ .onConflict((oc) => oc.columns(["session", "id"]).doUpdateSet({ value: JSON.stringify(contact) }))
229
+ .execute();
230
+ }
231
+
232
+ for (const message of messages) {
233
+ await db
234
+ .insertInto("messages")
235
+ .values({ session, id: message.key.id!, value: JSON.stringify(message) })
236
+ .onConflict((oc) => oc.columns(["session", "id"]).doUpdateSet({ value: JSON.stringify(message) }))
237
+ .execute();
238
+ }
239
+ });
240
+
241
+ event.on("messages.upsert", async ({ messages }) => {
242
+ for (const message of messages) {
243
+ await db
244
+ .insertInto("messages")
245
+ .values({ session, id: message.key.id!, value: JSON.stringify(message) })
246
+ .onConflict((oc) => oc.columns(["session", "id"]).doUpdateSet({ value: JSON.stringify(message) }))
247
+ .execute();
248
+ }
249
+ });
250
+
251
+ event.on("chats.upsert", async (chats) => {
252
+ for (const chat of chats) {
253
+ await db
254
+ .insertInto("chats")
255
+ .values({ session, id: chat.id!, value: JSON.stringify(chat) })
256
+ .onConflict((oc) => oc.columns(["session", "id"]).doUpdateSet({ value: JSON.stringify(chat) }))
257
+ .execute();
258
+ }
259
+ });
260
+
261
+ event.on("contacts.upsert", async (contacts) => {
262
+ for (const contact of contacts) {
263
+ await db
264
+ .insertInto("contacts")
265
+ .values({ session, id: contact.id!, value: JSON.stringify(contact) })
266
+ .onConflict((oc) => oc.columns(["session", "id"]).doUpdateSet({ value: JSON.stringify(contact) }))
267
+ .execute();
268
+ }
269
+ });
270
+ },
271
+ };
272
+ };
@@ -0,0 +1,37 @@
1
+ import { z } from "zod";
2
+
3
+ export const AuthSchema = z.object({
4
+ session: z.string(),
5
+ id: z.string(),
6
+ value: z.string().nullable(),
7
+ });
8
+
9
+ export const ChatSchema = z.object({
10
+ session: z.string(),
11
+ id: z.string(),
12
+ value: z.string().nullable(),
13
+ });
14
+
15
+ export const ContactSchema = z.object({
16
+ session: z.string(),
17
+ id: z.string(),
18
+ value: z.string().nullable(),
19
+ });
20
+
21
+ export const MessageSchema = z.object({
22
+ session: z.string(),
23
+ id: z.string(),
24
+ value: z.string().nullable(),
25
+ });
26
+
27
+ export type AuthTable = z.infer<typeof AuthSchema>;
28
+ export type ChatTable = z.infer<typeof ChatSchema>;
29
+ export type ContactTable = z.infer<typeof ContactSchema>;
30
+ export type MessageTable = z.infer<typeof MessageSchema>;
31
+
32
+ export type DB = {
33
+ auth: AuthTable;
34
+ chats: ChatTable;
35
+ contacts: ContactTable;
36
+ messages: MessageTable;
37
+ };