@raevon/n8n-nodes-whatsapp 2.0.2 → 2.0.4
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.
|
@@ -12,5 +12,4 @@ export interface WhatsAppCredentials {
|
|
|
12
12
|
export declare function getServerUrl(cfg: WhatsAppCredentials): string;
|
|
13
13
|
export declare function ensureServerRunning(cfg: WhatsAppCredentials): Promise<void>;
|
|
14
14
|
export declare function disconnect(): Promise<void>;
|
|
15
|
-
export declare function parseIncomingMessage(msg: any): Record<string, any> | null;
|
|
16
15
|
export declare function getWhatsAppCredentials(credentials: Record<string, any>): Promise<WhatsAppCredentials>;
|
|
@@ -1,4 +1,37 @@
|
|
|
1
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 () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -6,19 +39,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
39
|
exports.getServerUrl = getServerUrl;
|
|
7
40
|
exports.ensureServerRunning = ensureServerRunning;
|
|
8
41
|
exports.disconnect = disconnect;
|
|
9
|
-
exports.parseIncomingMessage = parseIncomingMessage;
|
|
10
42
|
exports.getWhatsAppCredentials = getWhatsAppCredentials;
|
|
11
|
-
const node_child_process_1 = require("node:child_process");
|
|
12
43
|
const node_path_1 = __importDefault(require("node:path"));
|
|
13
44
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
14
45
|
const node_net_1 = __importDefault(require("node:net"));
|
|
46
|
+
const node_http_1 = __importDefault(require("node:http"));
|
|
47
|
+
const node_url_1 = require("node:url");
|
|
48
|
+
// Dynamic imports for Baileys (loaded only when server starts)
|
|
49
|
+
let baileysModule = null;
|
|
15
50
|
function expandHome(p) {
|
|
16
51
|
if (!p.startsWith('~'))
|
|
17
52
|
return p;
|
|
18
53
|
return node_path_1.default.join(process.env.HOME || process.env.USERPROFILE || '', p.slice(1));
|
|
19
54
|
}
|
|
20
55
|
function getPort(cfg) {
|
|
21
|
-
// Derive port from session path to avoid conflicts between credentials
|
|
22
56
|
const hash = cfg.sessionPath.split('').reduce((a, b) => ((a << 5) - a + b.charCodeAt(0)) | 0, 0);
|
|
23
57
|
return 3456 + (Math.abs(hash) % 1000);
|
|
24
58
|
}
|
|
@@ -33,126 +67,288 @@ async function isPortOpen(port) {
|
|
|
33
67
|
server.listen(port, '127.0.0.1');
|
|
34
68
|
});
|
|
35
69
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
async function
|
|
70
|
+
// --- In-memory server state (singleton per process) ---
|
|
71
|
+
const servers = new Map();
|
|
72
|
+
async function startServer(cfg) {
|
|
39
73
|
const port = getPort(cfg);
|
|
40
|
-
//
|
|
41
|
-
if (
|
|
42
|
-
serverPort = port;
|
|
74
|
+
// Already running?
|
|
75
|
+
if (servers.has(port))
|
|
43
76
|
return;
|
|
77
|
+
// Load Baileys dynamically
|
|
78
|
+
if (!baileysModule) {
|
|
79
|
+
baileysModule = await Promise.resolve().then(() => __importStar(require('@whiskeysockets/baileys')));
|
|
44
80
|
}
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
DAILY_SEND_LIMIT: String(cfg.dailySendLimit),
|
|
61
|
-
CHECK_RECIPIENT: String(cfg.checkRecipientExists),
|
|
81
|
+
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
82
|
+
const randBetween = (min, max) => max > min ? min + Math.floor(Math.random() * (max - min + 1)) : min;
|
|
83
|
+
const noop = () => { };
|
|
84
|
+
const silentLogger = { level: 'silent', info: noop, warn: noop, error: noop, debug: noop, fatal: noop, trace: noop, child: () => silentLogger };
|
|
85
|
+
const state = {
|
|
86
|
+
server: null,
|
|
87
|
+
status: 'stopped',
|
|
88
|
+
qr: null,
|
|
89
|
+
socket: null,
|
|
90
|
+
messageBuffer: [],
|
|
91
|
+
queue: null,
|
|
92
|
+
nextSendAt: 0,
|
|
93
|
+
sentInBurst: 0,
|
|
94
|
+
sentTodayCount: 0,
|
|
95
|
+
sentTodayDate: '',
|
|
62
96
|
};
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
97
|
+
async function connect() {
|
|
98
|
+
if (state.socket && state.status === 'connected')
|
|
99
|
+
return;
|
|
100
|
+
const resolvedPath = expandHome(cfg.sessionPath);
|
|
101
|
+
if (!node_fs_1.default.existsSync(resolvedPath))
|
|
102
|
+
node_fs_1.default.mkdirSync(resolvedPath, { recursive: true });
|
|
103
|
+
const { useMultiFileAuthState, fetchLatestBaileysVersion, makeWASocket, makeCacheableSignalKeyStore, Browsers, DisconnectReason } = baileysModule;
|
|
104
|
+
const { state: authState, saveCreds } = await useMultiFileAuthState(resolvedPath);
|
|
105
|
+
const { version } = await fetchLatestBaileysVersion();
|
|
106
|
+
state.status = 'connecting';
|
|
107
|
+
state.qr = null;
|
|
108
|
+
state.socket = makeWASocket({
|
|
109
|
+
version,
|
|
110
|
+
browser: Browsers.ubuntu('n8n WhatsApp Node'),
|
|
111
|
+
auth: { creds: authState.creds, keys: makeCacheableSignalKeyStore(authState.keys, silentLogger) },
|
|
112
|
+
markOnlineOnConnect: false,
|
|
113
|
+
syncFullHistory: false,
|
|
114
|
+
shouldSyncHistoryMessage: () => false,
|
|
115
|
+
generateHighQualityLinkPreview: false,
|
|
116
|
+
logger: silentLogger,
|
|
117
|
+
});
|
|
118
|
+
state.socket.ev.on('creds.update', () => saveCreds().catch(() => { }));
|
|
119
|
+
state.socket.ev.on('connection.update', (update) => {
|
|
120
|
+
var _a, _b;
|
|
121
|
+
const { connection, lastDisconnect, qr: newQr } = update;
|
|
122
|
+
if (newQr) {
|
|
123
|
+
state.qr = newQr;
|
|
124
|
+
state.status = 'qr_ready';
|
|
125
|
+
}
|
|
126
|
+
if (connection === 'open') {
|
|
127
|
+
state.status = 'connected';
|
|
128
|
+
state.qr = null;
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (connection === 'close') {
|
|
132
|
+
const code = (_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;
|
|
133
|
+
if (code === DisconnectReason.loggedOut) {
|
|
134
|
+
state.status = 'logged_out';
|
|
135
|
+
state.socket = null;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
state.status = 'disconnected';
|
|
139
|
+
state.socket = null;
|
|
140
|
+
setTimeout(() => connect().catch(() => { }), 5000);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
state.socket.ev.on('messages.upsert', (upsert) => {
|
|
145
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
146
|
+
const { messages, type } = upsert;
|
|
147
|
+
if (type !== 'notify')
|
|
148
|
+
return;
|
|
149
|
+
for (const msg of messages) {
|
|
150
|
+
if (msg.key.fromMe)
|
|
151
|
+
continue;
|
|
152
|
+
let content = null;
|
|
153
|
+
const msgType = Object.keys(msg.message || {})[0] || 'unknown';
|
|
154
|
+
if ((_a = msg.message) === null || _a === void 0 ? void 0 : _a.conversation)
|
|
155
|
+
content = msg.message.conversation;
|
|
156
|
+
else if ((_c = (_b = msg.message) === null || _b === void 0 ? void 0 : _b.extendedTextMessage) === null || _c === void 0 ? void 0 : _c.text)
|
|
157
|
+
content = msg.message.extendedTextMessage.text;
|
|
158
|
+
else if ((_e = (_d = msg.message) === null || _d === void 0 ? void 0 : _d.imageMessage) === null || _e === void 0 ? void 0 : _e.caption)
|
|
159
|
+
content = msg.message.imageMessage.caption;
|
|
160
|
+
else if ((_g = (_f = msg.message) === null || _f === void 0 ? void 0 : _f.videoMessage) === null || _g === void 0 ? void 0 : _g.caption)
|
|
161
|
+
content = msg.message.videoMessage.caption;
|
|
162
|
+
else if ((_j = (_h = msg.message) === null || _h === void 0 ? void 0 : _h.documentMessage) === null || _j === void 0 ? void 0 : _j.fileName)
|
|
163
|
+
content = msg.message.documentMessage.fileName;
|
|
164
|
+
else if ((_k = msg.message) === null || _k === void 0 ? void 0 : _k.audioMessage)
|
|
165
|
+
content = '[Audio]';
|
|
166
|
+
else
|
|
167
|
+
content = `[${msgType}]`;
|
|
168
|
+
const ts = msg.messageTimestamp != null ? Number(msg.messageTimestamp) : Date.now() / 1000;
|
|
169
|
+
state.messageBuffer.push({ messageId: msg.key.id, chatJid: msg.key.remoteJid, sender: (_l = msg.key.participant) !== null && _l !== void 0 ? _l : msg.key.remoteJid, content, timestamp: new Date(ts * 1000).toISOString(), isFromMe: false, messageType: msgType });
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
if (!state.queue) {
|
|
173
|
+
const PQueue = (await Promise.resolve().then(() => __importStar(require('p-queue')))).default;
|
|
174
|
+
state.queue = new PQueue({ concurrency: 1 });
|
|
175
|
+
}
|
|
67
176
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
177
|
+
function normalizeRecipient(to) {
|
|
178
|
+
if (to.endsWith('@s.whatsapp.net') || to.endsWith('@g.us'))
|
|
179
|
+
return to;
|
|
180
|
+
const digits = to.replace(/\D/g, '');
|
|
181
|
+
if (!/^[1-9][0-9]{7,14}$/.test(digits))
|
|
182
|
+
throw new Error(`Invalid phone number: ${to}`);
|
|
183
|
+
return `${digits}@s.whatsapp.net`;
|
|
72
184
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
185
|
+
async function sendMessage(to, content) {
|
|
186
|
+
var _a;
|
|
187
|
+
if (!state.socket || state.status !== 'connected')
|
|
188
|
+
throw new Error('WhatsApp not connected');
|
|
189
|
+
if (cfg.dailySendLimit > 0) {
|
|
190
|
+
const today = new Date().toISOString().slice(0, 10) + 'T00:00:00.000Z';
|
|
191
|
+
if (state.sentTodayDate !== today) {
|
|
192
|
+
state.sentTodayDate = today;
|
|
193
|
+
state.sentTodayCount = 0;
|
|
81
194
|
}
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
|
|
195
|
+
if (state.sentTodayCount >= cfg.dailySendLimit)
|
|
196
|
+
throw new Error(`Daily limit (${cfg.dailySendLimit}) reached`);
|
|
197
|
+
}
|
|
198
|
+
const jid = normalizeRecipient(to);
|
|
199
|
+
if (cfg.checkRecipientExists && !jid.endsWith('@g.us')) {
|
|
200
|
+
const results = await state.socket.onWhatsApp(jid);
|
|
201
|
+
if (!((_a = results === null || results === void 0 ? void 0 : results[0]) === null || _a === void 0 ? void 0 : _a.exists))
|
|
202
|
+
throw new Error(`Recipient ${to} not on WhatsApp`);
|
|
203
|
+
}
|
|
204
|
+
const task = state.queue.add(async () => {
|
|
205
|
+
var _a;
|
|
206
|
+
const wait = state.nextSendAt - Date.now();
|
|
207
|
+
if (wait > 0)
|
|
208
|
+
await sleep(wait);
|
|
209
|
+
if (cfg.typingSimulation) {
|
|
210
|
+
try {
|
|
211
|
+
await state.socket.sendPresenceUpdate('composing', jid);
|
|
212
|
+
const len = (content.text || content.caption || '').length;
|
|
213
|
+
await sleep(Math.min(randBetween(900, 1800) + len * 25, 4000));
|
|
214
|
+
await state.socket.sendPresenceUpdate('paused', jid);
|
|
215
|
+
}
|
|
216
|
+
catch { }
|
|
85
217
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
218
|
+
const response = await state.socket.sendMessage(jid, content);
|
|
219
|
+
const gap = randBetween(cfg.messageDelayMinMs, cfg.messageDelayMaxMs);
|
|
220
|
+
let burst = 0;
|
|
221
|
+
if (cfg.burstSize && ++state.sentInBurst >= cfg.burstSize) {
|
|
222
|
+
state.sentInBurst = 0;
|
|
223
|
+
burst = randBetween(cfg.burstPauseMinMs, cfg.burstPauseMaxMs);
|
|
224
|
+
}
|
|
225
|
+
state.nextSendAt = Date.now() + gap + burst;
|
|
226
|
+
if (cfg.dailySendLimit > 0)
|
|
227
|
+
state.sentTodayCount++;
|
|
228
|
+
if (!((_a = response === null || response === void 0 ? void 0 : response.key) === null || _a === void 0 ? void 0 : _a.id))
|
|
229
|
+
throw new Error('No message ID returned');
|
|
230
|
+
return { messageId: response.key.id, status: 'sent', recipient: jid };
|
|
231
|
+
});
|
|
232
|
+
let timer;
|
|
233
|
+
const winner = await Promise.race([
|
|
234
|
+
task.then((r) => ({ result: r }), (e) => ({ error: e })),
|
|
235
|
+
new Promise(r => { timer = setTimeout(() => r(null), 15000); }),
|
|
236
|
+
]);
|
|
237
|
+
clearTimeout(timer);
|
|
238
|
+
if (!winner) {
|
|
239
|
+
task.then(() => { }, () => { });
|
|
240
|
+
return { messageId: 'queued', status: 'queued', recipient: jid };
|
|
93
241
|
}
|
|
94
|
-
|
|
95
|
-
|
|
242
|
+
if (winner.error)
|
|
243
|
+
throw winner.error;
|
|
244
|
+
return winner.result;
|
|
96
245
|
}
|
|
97
|
-
|
|
98
|
-
async
|
|
99
|
-
|
|
100
|
-
|
|
246
|
+
// HTTP server
|
|
247
|
+
const httpServer = node_http_1.default.createServer(async (req, res) => {
|
|
248
|
+
const url = new node_url_1.URL(req.url || '/', `http://localhost:${port}`);
|
|
249
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
250
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
251
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
252
|
+
if (req.method === 'OPTIONS') {
|
|
253
|
+
res.writeHead(204);
|
|
254
|
+
res.end();
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
101
257
|
try {
|
|
102
|
-
|
|
258
|
+
if (url.pathname === '/health') {
|
|
259
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
260
|
+
res.end(JSON.stringify({ ok: true, status: state.status, connected: state.status === 'connected' }));
|
|
261
|
+
}
|
|
262
|
+
else if (url.pathname === '/status') {
|
|
263
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
264
|
+
res.end(JSON.stringify({ status: state.status, connected: state.status === 'connected', qrAvailable: state.status === 'qr_ready', sentToday: state.sentTodayCount, dailyLimit: cfg.dailySendLimit }));
|
|
265
|
+
}
|
|
266
|
+
else if (url.pathname === '/connect') {
|
|
267
|
+
await connect();
|
|
268
|
+
const result = await new Promise(r => {
|
|
269
|
+
const check = setInterval(() => {
|
|
270
|
+
if (state.status === 'connected') {
|
|
271
|
+
clearInterval(check);
|
|
272
|
+
r({ connected: true, message: 'Connected' });
|
|
273
|
+
}
|
|
274
|
+
if (state.qr) {
|
|
275
|
+
clearInterval(check);
|
|
276
|
+
Promise.resolve().then(() => __importStar(require('qrcode'))).then(QRCode => QRCode.toDataURL(state.qr, { width: 300, margin: 2 }).then(qrUrl => r({ connected: false, qrUrl, message: 'Scan QR' })));
|
|
277
|
+
}
|
|
278
|
+
}, 500);
|
|
279
|
+
setTimeout(() => { clearInterval(check); r({ connected: false, status: state.status, message: 'Timeout' }); }, 30000);
|
|
280
|
+
});
|
|
281
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
282
|
+
res.end(JSON.stringify(result));
|
|
283
|
+
}
|
|
284
|
+
else if (url.pathname === '/qr' && state.qr) {
|
|
285
|
+
const QRCode = await Promise.resolve().then(() => __importStar(require('qrcode')));
|
|
286
|
+
const qrUrl = await QRCode.toDataURL(state.qr, { width: 400, margin: 2 });
|
|
287
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
288
|
+
res.end(`<!DOCTYPE html><html><body style="display:flex;justify-content:center;align-items:center;height:100vh;background:#111"><img src="${qrUrl}"/></body></html>`);
|
|
289
|
+
}
|
|
290
|
+
else if (url.pathname === '/send' && req.method === 'POST') {
|
|
291
|
+
let body = '';
|
|
292
|
+
req.on('data', chunk => body += chunk);
|
|
293
|
+
req.on('end', async () => {
|
|
294
|
+
try {
|
|
295
|
+
const { to, ...content } = JSON.parse(body);
|
|
296
|
+
if (!to)
|
|
297
|
+
throw new Error('Missing "to"');
|
|
298
|
+
const result = await sendMessage(to, content);
|
|
299
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
300
|
+
res.end(JSON.stringify(result));
|
|
301
|
+
}
|
|
302
|
+
catch (err) {
|
|
303
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
304
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
else if (url.pathname === '/messages') {
|
|
309
|
+
const msgs = state.messageBuffer.splice(0, state.messageBuffer.length);
|
|
310
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
311
|
+
res.end(JSON.stringify({ messages: msgs, count: msgs.length }));
|
|
312
|
+
}
|
|
313
|
+
else if (url.pathname === '/signout') {
|
|
314
|
+
if (state.socket) {
|
|
315
|
+
try {
|
|
316
|
+
state.socket.end(new Error('Sign out'));
|
|
317
|
+
}
|
|
318
|
+
catch { }
|
|
319
|
+
state.socket = null;
|
|
320
|
+
}
|
|
321
|
+
state.status = 'stopped';
|
|
322
|
+
const p = expandHome(cfg.sessionPath);
|
|
323
|
+
if (node_fs_1.default.existsSync(p)) {
|
|
324
|
+
node_fs_1.default.readdirSync(p).forEach(f => node_fs_1.default.unlinkSync(node_path_1.default.join(p, f)));
|
|
325
|
+
node_fs_1.default.rmdirSync(p);
|
|
326
|
+
}
|
|
327
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
328
|
+
res.end(JSON.stringify({ success: true, message: 'Signed out' }));
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
res.writeHead(404);
|
|
332
|
+
res.end('Not found');
|
|
333
|
+
}
|
|
103
334
|
}
|
|
104
|
-
catch {
|
|
105
|
-
|
|
335
|
+
catch (err) {
|
|
336
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
337
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
await new Promise(r => httpServer.listen(port, () => r()));
|
|
341
|
+
state.server = httpServer;
|
|
342
|
+
servers.set(port, state);
|
|
343
|
+
console.log(`[WhatsApp] Server started on port ${port}`);
|
|
106
344
|
}
|
|
107
|
-
function
|
|
108
|
-
|
|
109
|
-
if (!
|
|
110
|
-
|
|
111
|
-
let content = null;
|
|
112
|
-
const messageType = Object.keys(msg.message)[0];
|
|
113
|
-
if (msg.message.conversation) {
|
|
114
|
-
content = msg.message.conversation;
|
|
115
|
-
}
|
|
116
|
-
else if ((_a = msg.message.extendedTextMessage) === null || _a === void 0 ? void 0 : _a.text) {
|
|
117
|
-
content = msg.message.extendedTextMessage.text;
|
|
118
|
-
}
|
|
119
|
-
else if ((_b = msg.message.imageMessage) === null || _b === void 0 ? void 0 : _b.caption) {
|
|
120
|
-
content = msg.message.imageMessage.caption;
|
|
121
|
-
}
|
|
122
|
-
else if ((_c = msg.message.videoMessage) === null || _c === void 0 ? void 0 : _c.caption) {
|
|
123
|
-
content = msg.message.videoMessage.caption;
|
|
124
|
-
}
|
|
125
|
-
else if ((_d = msg.message.documentMessage) === null || _d === void 0 ? void 0 : _d.fileName) {
|
|
126
|
-
content = msg.message.documentMessage.fileName;
|
|
127
|
-
}
|
|
128
|
-
else if (msg.message.audioMessage) {
|
|
129
|
-
content = '[Audio]';
|
|
130
|
-
}
|
|
131
|
-
else if (msg.message.stickerMessage) {
|
|
132
|
-
content = '[Sticker]';
|
|
133
|
-
}
|
|
134
|
-
else if (msg.message.locationMessage) {
|
|
135
|
-
content = `[Location] ${msg.message.locationMessage.address || ''}`;
|
|
136
|
-
}
|
|
137
|
-
else if ((_e = msg.message.contactMessage) === null || _e === void 0 ? void 0 : _e.displayName) {
|
|
138
|
-
content = `[Contact] ${msg.message.contactMessage.displayName}`;
|
|
139
|
-
}
|
|
140
|
-
else if ((_f = msg.message.pollCreationMessage) === null || _f === void 0 ? void 0 : _f.name) {
|
|
141
|
-
content = `[Poll] ${msg.message.pollCreationMessage.name}`;
|
|
345
|
+
async function ensureServerRunning(cfg) {
|
|
346
|
+
const port = getPort(cfg);
|
|
347
|
+
if (!servers.has(port)) {
|
|
348
|
+
await startServer(cfg);
|
|
142
349
|
}
|
|
143
|
-
const timestampSeconds = msg.messageTimestamp != null
|
|
144
|
-
? Number(msg.messageTimestamp)
|
|
145
|
-
: Date.now() / 1000;
|
|
146
|
-
return {
|
|
147
|
-
messageId: msg.key.id,
|
|
148
|
-
chatJid: msg.key.remoteJid,
|
|
149
|
-
sender: (_g = msg.key.participant) !== null && _g !== void 0 ? _g : msg.key.remoteJid,
|
|
150
|
-
content: content || `[${messageType}]`,
|
|
151
|
-
timestamp: new Date(timestampSeconds * 1000).toISOString(),
|
|
152
|
-
isFromMe: (_h = msg.key.fromMe) !== null && _h !== void 0 ? _h : false,
|
|
153
|
-
messageType,
|
|
154
|
-
};
|
|
155
350
|
}
|
|
351
|
+
async function disconnect() { }
|
|
156
352
|
async function getWhatsAppCredentials(credentials) {
|
|
157
353
|
var _a, _b;
|
|
158
354
|
return {
|
|
@@ -52,18 +52,32 @@ class WhatsAppConnect {
|
|
|
52
52
|
for (let i = 0; i < items.length; i++) {
|
|
53
53
|
try {
|
|
54
54
|
if (operation === 'connect') {
|
|
55
|
-
// Start the
|
|
56
|
-
|
|
55
|
+
// Start the WhatsApp server
|
|
56
|
+
try {
|
|
57
|
+
await (0, WhatsAppApiHelper_1.ensureServerRunning)(cfg);
|
|
58
|
+
}
|
|
59
|
+
catch (serverErr) {
|
|
60
|
+
returnData.push({
|
|
61
|
+
json: { connected: false, error: `Server start failed: ${serverErr.message}`, sessionPath: cfg.sessionPath },
|
|
62
|
+
pairedItem: { item: i },
|
|
63
|
+
});
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
57
66
|
// Connect to the server
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
...result,
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
try {
|
|
68
|
+
const response = await fetch(`${(0, WhatsAppApiHelper_1.getServerUrl)(cfg)}/connect`, { method: 'POST' });
|
|
69
|
+
const result = await response.json();
|
|
70
|
+
returnData.push({
|
|
71
|
+
json: { ...result, sessionPath: cfg.sessionPath },
|
|
72
|
+
pairedItem: { item: i },
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
catch (fetchErr) {
|
|
76
|
+
returnData.push({
|
|
77
|
+
json: { connected: false, error: `Fetch failed: ${fetchErr.message}. Server URL: ${(0, WhatsAppApiHelper_1.getServerUrl)(cfg)}`, sessionPath: cfg.sessionPath },
|
|
78
|
+
pairedItem: { item: i },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
67
81
|
}
|
|
68
82
|
else if (operation === 'status') {
|
|
69
83
|
try {
|
package/package.json
CHANGED