baileys-redis-auth 1.0.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/.eslintrc ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "env": {
3
+ "node": true,
4
+ "browser": true,
5
+ "es6": true
6
+ },
7
+ "extends": ["eslint:recommended", "plugin:prettier/recommended"],
8
+ "globals": {
9
+ "Atomics": "readonly",
10
+ "SharedArrayBuffer": "readonly"
11
+ },
12
+ "parserOptions": {
13
+ "ecmaVersion": 2018,
14
+ "sourceType": "module"
15
+ },
16
+ "rules": {
17
+ "prettier/prettier": [
18
+ "error",
19
+ {
20
+ "singleQuote": true,
21
+ "trailingComa": "es5",
22
+ "bracketSpacing": false,
23
+ "printWidth": 100,
24
+ "tabWidth": 2,
25
+ "semi": true
26
+ }
27
+ ],
28
+ "eqeqeq": ["error", "always"]
29
+ }
30
+ }
package/.prettierrc ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "printWidth": 100,
3
+ "singleQuote": true,
4
+ "trailingComma": "es5",
5
+ "tabWidth": 2,
6
+ "semi": true,
7
+ "bracketSpacing": false
8
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "editor.formatOnSave": true,
3
+ "[javascript]": {
4
+ "editor.codeActionsOnSave": {
5
+ "source.fixAll.eslint": false
6
+ }
7
+ },
8
+ "files.autoSave": "afterDelay",
9
+ "workbench.editor.highlightModifiedTabs": true,
10
+ "editor.cursorStyle": "block",
11
+ "editor.cursorBlinking": "smooth",
12
+ "files.trimFinalNewlines": true,
13
+ "codemetrics.nodeconfiguration.JsxElement": 0,
14
+ "codemetrics.nodeconfiguration.JsxSelfClosingElement": 0,
15
+ "editor.fontFamily": "Fira Code",
16
+ "editor.fontLigatures": true,
17
+ "editor.codeActionsOnSave": {
18
+ "source.fixAll.eslint": true
19
+ }
20
+ }
package/README.md ADDED
@@ -0,0 +1 @@
1
+ # baileys-redis-auth
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "baileys-redis-auth",
3
+ "version": "1.0.0",
4
+ "description": "Redis Auth for Baileys",
5
+ "author": "heriyanto binduni <hbinduni@gmail.com>",
6
+ "license": "MIT",
7
+ "main": "lib/index.ts",
8
+ "types": "lib/index.d.ts",
9
+ "homepage": "https://github.com/WhiskeySockets/Baileys",
10
+ "repository": {
11
+ "url": "git@github.com:WhiskeySockets/Baileys.git"
12
+ },
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "example": "node --inspect -r ts-node/register src/Example/example.ts --no-store --no-reply"
16
+ },
17
+ "keywords": [
18
+ "baileys",
19
+ "redis",
20
+ "auth"
21
+ ],
22
+ "devDependencies": {
23
+ "@adiwajshing/keyed-db": "^0.2.4",
24
+ "@hapi/boom": "^10.0.1",
25
+ "@types/node": "^20.2.5",
26
+ "eslint": "^8.41.0",
27
+ "eslint-config-prettier": "^8.8.0",
28
+ "eslint-plugin-prettier": "^4.2.1",
29
+ "node-cache": "^5.1.2",
30
+ "pino": "^8.14.1",
31
+ "pino-pretty": "^10.0.0",
32
+ "ts-node": "^10.9.1",
33
+ "typescript": "^5.0.4"
34
+ },
35
+ "dependencies": {
36
+ "@whiskeysockets/baileys": "^6.1.0",
37
+ "ioredis": "^5.3.2"
38
+ }
39
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,195 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const node_cache_1 = __importDefault(require("node-cache"));
30
+ const baileys_1 = __importStar(require("@whiskeysockets/baileys"));
31
+ const logger_pino_1 = require("./logger-pino");
32
+ const index_1 = require("../index");
33
+ logger_pino_1.logger.level = 'silent';
34
+ const useStore = !process.argv.includes('--no-store');
35
+ const doReplies = !process.argv.includes('--no-reply');
36
+ // external map to store retry counts of messages when decryption/encryption fails
37
+ // keep this out of the socket itself, so as to prevent a message decryption/encryption loop across socket restarts
38
+ const msgRetryCounterCache = new node_cache_1.default();
39
+ // the store maintains the data of the WA connection in memory
40
+ // can be written out to a file & read from it
41
+ const store = useStore ? (0, baileys_1.makeInMemoryStore)({ logger: logger_pino_1.logger }) : undefined;
42
+ store === null || store === void 0 ? void 0 : store.readFromFile('./baileys_store_multi.json');
43
+ // save every 10s
44
+ setInterval(() => {
45
+ store === null || store === void 0 ? void 0 : store.writeToFile('./baileys_store_multi.json');
46
+ }, 10000);
47
+ // start a connection
48
+ const startSock = async () => {
49
+ const redisOptions = {
50
+ host: 'localhost',
51
+ port: 6379,
52
+ password: 'd334911fd345f1170b5bfcc8e75ee72df0f114eb',
53
+ };
54
+ const { state, saveCreds } = await (0, index_1.useRedisAuthState)(redisOptions, 'DB1');
55
+ // fetch latest version of WA Web
56
+ const { version, isLatest } = await (0, baileys_1.fetchLatestBaileysVersion)();
57
+ console.log(`using WA v${version.join('.')}, isLatest: ${isLatest}`);
58
+ const sock = (0, baileys_1.default)({
59
+ version,
60
+ logger: logger_pino_1.logger,
61
+ printQRInTerminal: true,
62
+ auth: {
63
+ creds: state.creds,
64
+ /** caching makes the store faster to send/recv messages */
65
+ keys: (0, baileys_1.makeCacheableSignalKeyStore)(state.keys, logger_pino_1.logger),
66
+ },
67
+ msgRetryCounterCache,
68
+ generateHighQualityLinkPreview: true,
69
+ // ignore all broadcast messages -- to receive the same
70
+ // comment the line below out
71
+ // shouldIgnoreJid: jid => isJidBroadcast(jid),
72
+ // implement to handle retries & poll updates
73
+ getMessage,
74
+ });
75
+ store === null || store === void 0 ? void 0 : store.bind(sock.ev);
76
+ const sendMessageWTyping = async (msg, jid) => {
77
+ await sock.presenceSubscribe(jid);
78
+ await (0, baileys_1.delay)(500);
79
+ await sock.sendPresenceUpdate('composing', jid);
80
+ await (0, baileys_1.delay)(2000);
81
+ await sock.sendPresenceUpdate('paused', jid);
82
+ await sock.sendMessage(jid, msg);
83
+ };
84
+ // the process function lets you process all events that just occurred
85
+ // efficiently in a batch
86
+ sock.ev.process(
87
+ // events is a map for event name => event data
88
+ async (events) => {
89
+ var _a, _b, _c;
90
+ // something about the connection changed
91
+ // maybe it closed, or we received all offline message or connection opened
92
+ if (events['connection.update']) {
93
+ const update = events['connection.update'];
94
+ const { connection, lastDisconnect } = update;
95
+ if (connection === 'close') {
96
+ // reconnect if not logged out
97
+ if (((_b = (_a = lastDisconnect === null || lastDisconnect === void 0 ? void 0 : lastDisconnect.error) === null || _a === void 0 ? void 0 : _a.output) === null || _b === void 0 ? void 0 : _b.statusCode) !== baileys_1.DisconnectReason.loggedOut) {
98
+ startSock();
99
+ }
100
+ else {
101
+ console.log('Connection closed. You are logged out.');
102
+ }
103
+ }
104
+ console.log('connection update', update);
105
+ }
106
+ // credentials updated -- save them
107
+ if (events['creds.update']) {
108
+ await saveCreds();
109
+ }
110
+ if (events['labels.association']) {
111
+ console.log(events['labels.association']);
112
+ }
113
+ if (events['labels.edit']) {
114
+ console.log(events['labels.edit']);
115
+ }
116
+ if (events.call) {
117
+ console.log('recv call event', events.call);
118
+ }
119
+ // history received
120
+ if (events['messaging-history.set']) {
121
+ const { chats, contacts, messages, isLatest } = events['messaging-history.set'];
122
+ console.log(`recv ${chats.length} chats, ${contacts.length} contacts, ${messages.length} msgs (is latest: ${isLatest})`);
123
+ }
124
+ // received a new message
125
+ if (events['messages.upsert']) {
126
+ const upsert = events['messages.upsert'];
127
+ // console.log('recv messages ', JSON.stringify(upsert, undefined, 2));
128
+ if (upsert.type === 'notify') {
129
+ for (const msg of upsert.messages) {
130
+ if (!msg.key.fromMe) {
131
+ if (doReplies) {
132
+ console.log('replying to', msg.key.remoteJid);
133
+ await sock.readMessages([msg.key]);
134
+ await sendMessageWTyping({ text: 'Hello there!' }, msg.key.remoteJid);
135
+ }
136
+ if (((_c = msg.message) === null || _c === void 0 ? void 0 : _c.conversation) === 'ping') {
137
+ await sock.readMessages([msg.key]);
138
+ await sendMessageWTyping({ text: 'Ping!' }, msg.key.remoteJid);
139
+ }
140
+ }
141
+ }
142
+ }
143
+ }
144
+ // messages updated like status delivered, message deleted etc.
145
+ if (events['messages.update']) {
146
+ // console.log(JSON.stringify(events['messages.update'], undefined, 2));
147
+ for (const { key, update } of events['messages.update']) {
148
+ if (update.pollUpdates) {
149
+ const pollCreation = await getMessage(key);
150
+ if (pollCreation) {
151
+ console.log('got poll update, aggregation: ', (0, baileys_1.getAggregateVotesInPollMessage)({
152
+ message: pollCreation,
153
+ pollUpdates: update.pollUpdates,
154
+ }));
155
+ }
156
+ }
157
+ }
158
+ }
159
+ if (events['message-receipt.update']) {
160
+ // console.log(events['message-receipt.update']);
161
+ }
162
+ if (events['messages.reaction']) {
163
+ // console.log(events['messages.reaction']);
164
+ }
165
+ if (events['presence.update']) {
166
+ // console.log(events['presence.update']);
167
+ }
168
+ if (events['chats.update']) {
169
+ // console.log(events['chats.update']);
170
+ }
171
+ if (events['contacts.update']) {
172
+ for (const contact of events['contacts.update']) {
173
+ if (typeof contact.imgUrl !== 'undefined') {
174
+ const newUrl = contact.imgUrl === null
175
+ ? null
176
+ : await sock.profilePictureUrl(contact.id).catch(() => null);
177
+ console.log(`contact ${contact.id} has a new profile pic: ${newUrl}`);
178
+ }
179
+ }
180
+ }
181
+ if (events['chats.delete']) {
182
+ console.log('chats deleted ', events['chats.delete']);
183
+ }
184
+ });
185
+ return sock;
186
+ async function getMessage(key) {
187
+ if (store) {
188
+ const msg = await store.loadMessage(key.remoteJid, key.id);
189
+ return (msg === null || msg === void 0 ? void 0 : msg.message) || undefined;
190
+ }
191
+ // only if store is present
192
+ return baileys_1.proto.Message.fromObject({});
193
+ }
194
+ };
195
+ startSock();
@@ -0,0 +1,17 @@
1
+ export declare const logger: import("pino").Logger<{
2
+ level: string;
3
+ formatters: {
4
+ bindings: (bindings: any) => {
5
+ pid: any;
6
+ host: any;
7
+ app: string;
8
+ v: string;
9
+ node_version: string;
10
+ };
11
+ level: (label: any, number: any) => {
12
+ level: any;
13
+ label: any;
14
+ };
15
+ };
16
+ timestamp: () => string;
17
+ }>;
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.logger = void 0;
7
+ const pino_1 = __importDefault(require("pino"));
8
+ const package_json_1 = __importDefault(require("../../package.json"));
9
+ const pretty = {
10
+ level: 'info',
11
+ target: 'pino-pretty',
12
+ options: {
13
+ colorize: true,
14
+ translateTime: 'SYS:isoDateTime',
15
+ ignore: 'pid,hostname',
16
+ },
17
+ };
18
+ const transports = pino_1.default.transport({
19
+ targets: [pretty],
20
+ });
21
+ const options = {
22
+ level: process.env.PINO_LOG_LEVEL || 'info',
23
+ formatters: {
24
+ bindings: (bindings) => {
25
+ return {
26
+ pid: bindings.pid,
27
+ host: bindings.hostname,
28
+ app: package_json_1.default.name,
29
+ v: package_json_1.default.version,
30
+ node_version: process.version,
31
+ };
32
+ },
33
+ level: (label, number) => {
34
+ return { level: number, label: label.toUpperCase() };
35
+ },
36
+ },
37
+ timestamp: pino_1.default.stdTimeFunctions.isoTime,
38
+ };
39
+ exports.logger = (0, pino_1.default)(options, transports);
@@ -0,0 +1,9 @@
1
+ import { RedisOptions } from 'ioredis';
2
+ import { AuthenticationState } from '@whiskeysockets/baileys';
3
+ /**
4
+ * Stores the full authentication state in Redis.
5
+ * */
6
+ export declare const useRedisAuthState: (redisOptions: RedisOptions, prefix?: string) => Promise<{
7
+ state: AuthenticationState;
8
+ saveCreds: () => Promise<void>;
9
+ }>;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.useRedisAuthState = void 0;
7
+ const ioredis_1 = __importDefault(require("ioredis"));
8
+ const baileys_1 = require("@whiskeysockets/baileys");
9
+ /**
10
+ * Stores the full authentication state in Redis.
11
+ * */
12
+ const useRedisAuthState = async (redisOptions, prefix = 'DB1') => {
13
+ const redis = new ioredis_1.default(redisOptions);
14
+ const writeData = (data, key) => {
15
+ return redis.set(`${prefix}:${key}`, JSON.stringify(data, baileys_1.BufferJSON.replacer));
16
+ };
17
+ const readData = async (key) => {
18
+ try {
19
+ const data = await redis.get(`${prefix}:${key}`);
20
+ return data ? JSON.parse(data, baileys_1.BufferJSON.reviver) : null;
21
+ }
22
+ catch (error) {
23
+ return null;
24
+ }
25
+ };
26
+ const creds = (await readData('creds')) || (0, baileys_1.initAuthCreds)();
27
+ return {
28
+ state: {
29
+ creds,
30
+ keys: {
31
+ get: async (type, ids) => {
32
+ const data = {};
33
+ await Promise.all(ids.map(async (id) => {
34
+ let value = await readData(`${type}-${id}`);
35
+ if (type === `${prefix}:app-state-sync-key` && value) {
36
+ value = baileys_1.proto.Message.AppStateSyncKeyData.fromObject(value);
37
+ }
38
+ data[id] = value;
39
+ }));
40
+ return data;
41
+ },
42
+ set: async (data) => {
43
+ const pipeline = redis.pipeline();
44
+ for (const category in data) {
45
+ for (const id in data[category]) {
46
+ const value = data[category][id];
47
+ const key = `${prefix}:${category}-${id}`;
48
+ if (value) {
49
+ pipeline.set(key, JSON.stringify(value, baileys_1.BufferJSON.replacer));
50
+ }
51
+ else {
52
+ pipeline.del(key);
53
+ }
54
+ }
55
+ }
56
+ await pipeline.exec();
57
+ },
58
+ },
59
+ },
60
+ saveCreds: async () => {
61
+ await writeData(creds, 'creds');
62
+ },
63
+ };
64
+ };
65
+ exports.useRedisAuthState = useRedisAuthState;
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "baileys-redis-auth",
3
+ "version": "1.0.0",
4
+ "description": "Redis Auth for Baileys",
5
+ "author": "heriyanto binduni <hbinduni@gmail.com>",
6
+ "license": "MIT",
7
+ "main": "lib/index.ts",
8
+ "types": "lib/index.d.ts",
9
+ "homepage": "https://github.com/hbinduni/baileys-redis-auth",
10
+ "repository": {
11
+ "url": "https://github.com/hbinduni/baileys-redis-auth.git"
12
+ },
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "example": "node --inspect -r ts-node/register src/Example/example.ts --no-store --no-reply"
16
+ },
17
+ "keywords": [
18
+ "baileys",
19
+ "redis",
20
+ "auth"
21
+ ],
22
+ "devDependencies": {
23
+ "@adiwajshing/keyed-db": "^0.2.4",
24
+ "@hapi/boom": "^10.0.1",
25
+ "@types/node": "^20.2.5",
26
+ "eslint": "^8.41.0",
27
+ "eslint-config-prettier": "^8.8.0",
28
+ "eslint-plugin-prettier": "^4.2.1",
29
+ "node-cache": "^5.1.2",
30
+ "pino": "^8.14.1",
31
+ "pino-pretty": "^10.0.0",
32
+ "ts-node": "^10.9.1",
33
+ "typescript": "^5.0.4"
34
+ },
35
+ "dependencies": {
36
+ "@whiskeysockets/baileys": "^6.1.0",
37
+ "ioredis": "^5.3.2"
38
+ }
39
+ }
@@ -0,0 +1,220 @@
1
+ import {Boom} from '@hapi/boom';
2
+ import NodeCache from 'node-cache';
3
+ import makeWASocket, {
4
+ AnyMessageContent,
5
+ delay,
6
+ DisconnectReason,
7
+ fetchLatestBaileysVersion,
8
+ getAggregateVotesInPollMessage,
9
+ makeCacheableSignalKeyStore,
10
+ makeInMemoryStore,
11
+ proto,
12
+ useMultiFileAuthState,
13
+ WAMessageContent,
14
+ WAMessageKey,
15
+ } from '@whiskeysockets/baileys';
16
+ import {logger} from './logger-pino';
17
+ import {useRedisAuthState} from '../index';
18
+
19
+ logger.level = 'silent';
20
+
21
+ const useStore = !process.argv.includes('--no-store');
22
+ const doReplies = !process.argv.includes('--no-reply');
23
+
24
+ // external map to store retry counts of messages when decryption/encryption fails
25
+ // keep this out of the socket itself, so as to prevent a message decryption/encryption loop across socket restarts
26
+ const msgRetryCounterCache = new NodeCache();
27
+
28
+ // the store maintains the data of the WA connection in memory
29
+ // can be written out to a file & read from it
30
+ const store = useStore ? makeInMemoryStore({logger}) : undefined;
31
+ store?.readFromFile('./baileys_store_multi.json');
32
+ // save every 10s
33
+ setInterval(() => {
34
+ store?.writeToFile('./baileys_store_multi.json');
35
+ }, 10_000);
36
+
37
+ // start a connection
38
+ const startSock = async () => {
39
+ const redisOptions = {
40
+ host: 'localhost',
41
+ port: 6379,
42
+ password: 'd334911fd345f1170b5bfcc8e75ee72df0f114eb',
43
+ };
44
+
45
+ const {state, saveCreds} = await useRedisAuthState(redisOptions, 'DB1');
46
+
47
+ // fetch latest version of WA Web
48
+ const {version, isLatest} = await fetchLatestBaileysVersion();
49
+ console.log(`using WA v${version.join('.')}, isLatest: ${isLatest}`);
50
+
51
+ const sock = makeWASocket({
52
+ version,
53
+ logger,
54
+ printQRInTerminal: true,
55
+ auth: {
56
+ creds: state.creds,
57
+ /** caching makes the store faster to send/recv messages */
58
+ keys: makeCacheableSignalKeyStore(state.keys, logger),
59
+ },
60
+ msgRetryCounterCache,
61
+ generateHighQualityLinkPreview: true,
62
+ // ignore all broadcast messages -- to receive the same
63
+ // comment the line below out
64
+ // shouldIgnoreJid: jid => isJidBroadcast(jid),
65
+ // implement to handle retries & poll updates
66
+ getMessage,
67
+ });
68
+
69
+ store?.bind(sock.ev);
70
+
71
+ const sendMessageWTyping = async (msg: AnyMessageContent, jid: string) => {
72
+ await sock.presenceSubscribe(jid);
73
+ await delay(500);
74
+
75
+ await sock.sendPresenceUpdate('composing', jid);
76
+ await delay(2000);
77
+
78
+ await sock.sendPresenceUpdate('paused', jid);
79
+
80
+ await sock.sendMessage(jid, msg);
81
+ };
82
+
83
+ // the process function lets you process all events that just occurred
84
+ // efficiently in a batch
85
+ sock.ev.process(
86
+ // events is a map for event name => event data
87
+ async (events) => {
88
+ // something about the connection changed
89
+ // maybe it closed, or we received all offline message or connection opened
90
+ if (events['connection.update']) {
91
+ const update = events['connection.update'];
92
+ const {connection, lastDisconnect} = update;
93
+ if (connection === 'close') {
94
+ // reconnect if not logged out
95
+ if ((lastDisconnect?.error as Boom)?.output?.statusCode !== DisconnectReason.loggedOut) {
96
+ startSock();
97
+ } else {
98
+ console.log('Connection closed. You are logged out.');
99
+ }
100
+ }
101
+
102
+ console.log('connection update', update);
103
+ }
104
+
105
+ // credentials updated -- save them
106
+ if (events['creds.update']) {
107
+ await saveCreds();
108
+ }
109
+
110
+ if (events['labels.association']) {
111
+ console.log(events['labels.association']);
112
+ }
113
+
114
+ if (events['labels.edit']) {
115
+ console.log(events['labels.edit']);
116
+ }
117
+
118
+ if (events.call) {
119
+ console.log('recv call event', events.call);
120
+ }
121
+
122
+ // history received
123
+ if (events['messaging-history.set']) {
124
+ const {chats, contacts, messages, isLatest} = events['messaging-history.set'];
125
+ console.log(
126
+ `recv ${chats.length} chats, ${contacts.length} contacts, ${messages.length} msgs (is latest: ${isLatest})`
127
+ );
128
+ }
129
+
130
+ // received a new message
131
+ if (events['messages.upsert']) {
132
+ const upsert = events['messages.upsert'];
133
+ // console.log('recv messages ', JSON.stringify(upsert, undefined, 2));
134
+
135
+ if (upsert.type === 'notify') {
136
+ for (const msg of upsert.messages) {
137
+ if (!msg.key.fromMe) {
138
+ if (doReplies) {
139
+ console.log('replying to', msg.key.remoteJid);
140
+ await sock!.readMessages([msg.key]);
141
+ await sendMessageWTyping({text: 'Hello there!'}, msg.key.remoteJid!);
142
+ }
143
+
144
+ if (msg.message?.conversation === 'ping') {
145
+ await sock!.readMessages([msg.key]);
146
+ await sendMessageWTyping({text: 'Ping!'}, msg.key.remoteJid!);
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+
153
+ // messages updated like status delivered, message deleted etc.
154
+ if (events['messages.update']) {
155
+ // console.log(JSON.stringify(events['messages.update'], undefined, 2));
156
+
157
+ for (const {key, update} of events['messages.update']) {
158
+ if (update.pollUpdates) {
159
+ const pollCreation = await getMessage(key);
160
+ if (pollCreation) {
161
+ console.log(
162
+ 'got poll update, aggregation: ',
163
+ getAggregateVotesInPollMessage({
164
+ message: pollCreation,
165
+ pollUpdates: update.pollUpdates,
166
+ })
167
+ );
168
+ }
169
+ }
170
+ }
171
+ }
172
+
173
+ if (events['message-receipt.update']) {
174
+ // console.log(events['message-receipt.update']);
175
+ }
176
+
177
+ if (events['messages.reaction']) {
178
+ // console.log(events['messages.reaction']);
179
+ }
180
+
181
+ if (events['presence.update']) {
182
+ // console.log(events['presence.update']);
183
+ }
184
+
185
+ if (events['chats.update']) {
186
+ // console.log(events['chats.update']);
187
+ }
188
+
189
+ if (events['contacts.update']) {
190
+ for (const contact of events['contacts.update']) {
191
+ if (typeof contact.imgUrl !== 'undefined') {
192
+ const newUrl =
193
+ contact.imgUrl === null
194
+ ? null
195
+ : await sock!.profilePictureUrl(contact.id!).catch(() => null);
196
+ console.log(`contact ${contact.id} has a new profile pic: ${newUrl}`);
197
+ }
198
+ }
199
+ }
200
+
201
+ if (events['chats.delete']) {
202
+ console.log('chats deleted ', events['chats.delete']);
203
+ }
204
+ }
205
+ );
206
+
207
+ return sock;
208
+
209
+ async function getMessage(key: WAMessageKey): Promise<WAMessageContent | undefined> {
210
+ if (store) {
211
+ const msg = await store.loadMessage(key.remoteJid!, key.id!);
212
+ return msg?.message || undefined;
213
+ }
214
+
215
+ // only if store is present
216
+ return proto.Message.fromObject({});
217
+ }
218
+ };
219
+
220
+ startSock();
@@ -0,0 +1,38 @@
1
+ import pino from 'pino';
2
+
3
+ import Pack from '../../package.json';
4
+
5
+ const pretty = {
6
+ level: 'info',
7
+ target: 'pino-pretty',
8
+ options: {
9
+ colorize: true,
10
+ translateTime: 'SYS:isoDateTime',
11
+ ignore: 'pid,hostname',
12
+ },
13
+ };
14
+
15
+ const transports = pino.transport({
16
+ targets: [pretty],
17
+ });
18
+
19
+ const options = {
20
+ level: process.env.PINO_LOG_LEVEL || 'info',
21
+ formatters: {
22
+ bindings: (bindings) => {
23
+ return {
24
+ pid: bindings.pid,
25
+ host: bindings.hostname,
26
+ app: Pack.name,
27
+ v: Pack.version,
28
+ node_version: process.version,
29
+ };
30
+ },
31
+ level: (label, number) => {
32
+ return {level: number, label: label.toUpperCase()};
33
+ },
34
+ },
35
+ timestamp: pino.stdTimeFunctions.isoTime,
36
+ };
37
+
38
+ export const logger = pino(options, transports);
package/src/index.ts ADDED
@@ -0,0 +1,76 @@
1
+ import Redis, {RedisOptions} from 'ioredis';
2
+ import {
3
+ AuthenticationCreds,
4
+ AuthenticationState,
5
+ SignalDataTypeMap,
6
+ initAuthCreds,
7
+ BufferJSON,
8
+ proto,
9
+ } from '@whiskeysockets/baileys';
10
+
11
+ /**
12
+ * Stores the full authentication state in Redis.
13
+ * */
14
+ export const useRedisAuthState = async (
15
+ redisOptions: RedisOptions,
16
+ prefix: string = 'DB1'
17
+ ): Promise<{state: AuthenticationState; saveCreds: () => Promise<void>}> => {
18
+ const redis = new Redis(redisOptions);
19
+
20
+ const writeData = (data: any, key: string) => {
21
+ return redis.set(`${prefix}:${key}`, JSON.stringify(data, BufferJSON.replacer));
22
+ };
23
+
24
+ const readData = async (key: string) => {
25
+ try {
26
+ const data = await redis.get(`${prefix}:${key}`);
27
+ return data ? JSON.parse(data, BufferJSON.reviver) : null;
28
+ } catch (error) {
29
+ return null;
30
+ }
31
+ };
32
+
33
+ const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds();
34
+
35
+ return {
36
+ state: {
37
+ creds,
38
+ keys: {
39
+ get: async (type, ids) => {
40
+ const data: {[_: string]: SignalDataTypeMap[typeof type]} = {};
41
+ await Promise.all(
42
+ ids.map(async (id) => {
43
+ let value = await readData(`${type}-${id}`);
44
+ if (type === `${prefix}:app-state-sync-key` && value) {
45
+ value = proto.Message.AppStateSyncKeyData.fromObject(value);
46
+ }
47
+
48
+ data[id] = value;
49
+ })
50
+ );
51
+
52
+ return data;
53
+ },
54
+ set: async (data) => {
55
+ const pipeline = redis.pipeline();
56
+ for (const category in data) {
57
+ for (const id in data[category]) {
58
+ const value = data[category][id];
59
+ const key = `${prefix}:${category}-${id}`;
60
+ if (value) {
61
+ pipeline.set(key, JSON.stringify(value, BufferJSON.replacer));
62
+ } else {
63
+ pipeline.del(key);
64
+ }
65
+ }
66
+ }
67
+
68
+ await pipeline.exec();
69
+ },
70
+ },
71
+ },
72
+ saveCreds: async () => {
73
+ await writeData(creds, 'creds');
74
+ },
75
+ };
76
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2018",
4
+ "module": "commonjs",
5
+ "experimentalDecorators": true,
6
+ "allowJs": false,
7
+ "checkJs": false,
8
+ "outDir": "lib",
9
+ "strict": false,
10
+ "strictNullChecks": true,
11
+ "skipLibCheck": true,
12
+ "noImplicitThis": true,
13
+ "esModuleInterop": true,
14
+ "resolveJsonModule": true,
15
+ "forceConsistentCasingInFileNames": true,
16
+ "declaration": true,
17
+ "lib": ["es2020", "esnext.array", "DOM"]
18
+ },
19
+ "include": ["./src/**/*.ts"],
20
+ "exclude": ["node_modules", "src/__test__/*"]
21
+ }