fluxy-bot 0.13.2 → 0.13.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.
package/package.json
CHANGED
|
@@ -7,7 +7,6 @@ import { log } from '../../shared/logger.js';
|
|
|
7
7
|
import { initRoleResolver } from './role-resolver.js';
|
|
8
8
|
import { ChannelRouter, type ChannelRouterOpts } from './router.js';
|
|
9
9
|
import { ChatChannel } from './chat-channel.js';
|
|
10
|
-
import { WhatsAppChannel } from './whatsapp-channel.js';
|
|
11
10
|
|
|
12
11
|
export { ChannelRouter } from './router.js';
|
|
13
12
|
export type { Channel, ChannelType, SenderRole, SenderIdentity, IncomingMessage, OutgoingMessage } from './types.js';
|
|
@@ -30,17 +29,20 @@ export async function initializeChannels(
|
|
|
30
29
|
router.registerChannel(chatChannel);
|
|
31
30
|
await chatChannel.initialize(router);
|
|
32
31
|
|
|
33
|
-
// WhatsApp channel —
|
|
32
|
+
// WhatsApp channel — dynamic import so supervisor starts even without baileys installed
|
|
34
33
|
if (config.channels?.whatsapp?.enabled) {
|
|
35
34
|
try {
|
|
35
|
+
const { WhatsAppChannel } = await import('./whatsapp-channel.js');
|
|
36
36
|
const waChannel = new WhatsAppChannel((qr) => {
|
|
37
|
-
// Broadcast QR to chat UI so owner can scan from dashboard
|
|
38
37
|
opts.broadcastFluxy('whatsapp:qr', { qr });
|
|
39
38
|
});
|
|
40
39
|
router.registerChannel(waChannel);
|
|
41
40
|
await waChannel.initialize(router);
|
|
42
41
|
} catch (err: any) {
|
|
43
42
|
log.warn(`[channels] WhatsApp initialization failed: ${err.message}`);
|
|
43
|
+
if (err.code === 'ERR_MODULE_NOT_FOUND') {
|
|
44
|
+
log.warn('[channels] Install baileys: npm install @whiskeysockets/baileys');
|
|
45
|
+
}
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
|
|
@@ -2,14 +2,12 @@
|
|
|
2
2
|
* WhatsApp Channel — Baileys adapter for WhatsApp Web.
|
|
3
3
|
* Receives messages, routes through ChannelRouter, sends responses back.
|
|
4
4
|
* Session persisted in ~/.fluxy/whatsapp-session/.
|
|
5
|
+
*
|
|
6
|
+
* Baileys is imported dynamically so the supervisor starts even if
|
|
7
|
+
* @whiskeysockets/baileys is not installed. Install it with:
|
|
8
|
+
* npm install @whiskeysockets/baileys
|
|
5
9
|
*/
|
|
6
10
|
|
|
7
|
-
import makeWASocket, {
|
|
8
|
-
useMultiFileAuthState,
|
|
9
|
-
DisconnectReason,
|
|
10
|
-
type WASocket,
|
|
11
|
-
} from '@whiskeysockets/baileys';
|
|
12
|
-
import { Boom } from '@hapi/boom';
|
|
13
11
|
import path from 'path';
|
|
14
12
|
import { DATA_DIR } from '../../shared/paths.js';
|
|
15
13
|
import { log } from '../../shared/logger.js';
|
|
@@ -19,14 +17,11 @@ const SESSION_DIR = path.join(DATA_DIR, 'whatsapp-session');
|
|
|
19
17
|
|
|
20
18
|
export class WhatsAppChannel implements Channel {
|
|
21
19
|
readonly type = 'whatsapp' as const;
|
|
22
|
-
private sock:
|
|
20
|
+
private sock: any = null;
|
|
23
21
|
private router: ChannelRouter | null = null;
|
|
24
22
|
private onQR?: (qr: string) => void;
|
|
25
23
|
private reconnecting = false;
|
|
26
24
|
|
|
27
|
-
/**
|
|
28
|
-
* @param onQR - Optional callback to surface QR code to the chat UI
|
|
29
|
-
*/
|
|
30
25
|
constructor(onQR?: (qr: string) => void) {
|
|
31
26
|
this.onQR = onQR;
|
|
32
27
|
}
|
|
@@ -37,6 +32,11 @@ export class WhatsAppChannel implements Channel {
|
|
|
37
32
|
}
|
|
38
33
|
|
|
39
34
|
private async connect(): Promise<void> {
|
|
35
|
+
// Dynamic import — fails gracefully if baileys is not installed
|
|
36
|
+
const baileys = await import('@whiskeysockets/baileys');
|
|
37
|
+
const makeWASocket = baileys.default;
|
|
38
|
+
const { useMultiFileAuthState, DisconnectReason } = baileys;
|
|
39
|
+
|
|
40
40
|
const { state, saveCreds } = await useMultiFileAuthState(SESSION_DIR);
|
|
41
41
|
|
|
42
42
|
this.sock = makeWASocket({
|
|
@@ -46,16 +46,15 @@ export class WhatsAppChannel implements Channel {
|
|
|
46
46
|
|
|
47
47
|
this.sock.ev.on('creds.update', saveCreds);
|
|
48
48
|
|
|
49
|
-
this.sock.ev.on('connection.update', (update) => {
|
|
49
|
+
this.sock.ev.on('connection.update', (update: any) => {
|
|
50
50
|
const { connection, lastDisconnect, qr } = update;
|
|
51
51
|
|
|
52
|
-
// Surface QR to chat UI for scanning
|
|
53
52
|
if (qr && this.onQR) {
|
|
54
53
|
this.onQR(qr);
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
if (connection === 'close') {
|
|
58
|
-
const statusCode = (lastDisconnect?.error as
|
|
57
|
+
const statusCode = (lastDisconnect?.error as any)?.output?.statusCode;
|
|
59
58
|
const shouldReconnect = statusCode !== DisconnectReason.loggedOut;
|
|
60
59
|
|
|
61
60
|
log.warn(`[whatsapp] Connection closed: ${statusCode}. Reconnect: ${shouldReconnect}`);
|
|
@@ -78,19 +77,16 @@ export class WhatsAppChannel implements Channel {
|
|
|
78
77
|
}
|
|
79
78
|
});
|
|
80
79
|
|
|
81
|
-
this.sock.ev.on('messages.upsert', ({ messages, type }) => {
|
|
80
|
+
this.sock.ev.on('messages.upsert', ({ messages, type }: any) => {
|
|
82
81
|
if (type !== 'notify') return;
|
|
83
82
|
|
|
84
83
|
for (const msg of messages) {
|
|
85
|
-
// Skip messages sent by us
|
|
86
84
|
if (msg.key.fromMe) continue;
|
|
87
|
-
// Skip status broadcasts
|
|
88
85
|
if (msg.key.remoteJid === 'status@broadcast') continue;
|
|
89
86
|
|
|
90
87
|
const remoteJid = msg.key.remoteJid!;
|
|
91
88
|
const senderId = remoteJid.replace('@s.whatsapp.net', '').replace('@g.us', '');
|
|
92
89
|
|
|
93
|
-
// Extract text content
|
|
94
90
|
const content =
|
|
95
91
|
msg.message?.conversation ||
|
|
96
92
|
msg.message?.extendedTextMessage?.text ||
|
|
@@ -104,13 +100,12 @@ export class WhatsAppChannel implements Channel {
|
|
|
104
100
|
|
|
105
101
|
log.info(`[whatsapp] Message from ${displayName} (${senderId}): ${content.slice(0, 80)}`);
|
|
106
102
|
|
|
107
|
-
// Route through the channel router
|
|
108
103
|
this.router!.handleIncoming({
|
|
109
104
|
sender: {
|
|
110
105
|
channelType: 'whatsapp',
|
|
111
106
|
senderId,
|
|
112
107
|
displayName,
|
|
113
|
-
role: 'customer',
|
|
108
|
+
role: 'customer',
|
|
114
109
|
conversationKey,
|
|
115
110
|
},
|
|
116
111
|
content,
|