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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fluxy-bot",
3
- "version": "0.13.2",
3
+ "version": "0.13.4",
4
4
  "releaseNotes": [
5
5
  "1. react router implemented",
6
6
  "2. new workspace design",
@@ -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 — registered if enabled in config
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: WASocket | null = null;
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 Boom)?.output?.statusCode;
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', // Router will resolve the actual role
108
+ role: 'customer',
114
109
  conversationKey,
115
110
  },
116
111
  content,