@vielhuber/wahelper 1.3.6 → 1.3.7

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/README.MD CHANGED
@@ -37,6 +37,9 @@ npx wahelper \
37
37
  --device "xxxxxxxxxxxx" \
38
38
  ...
39
39
 
40
+ --action "fetch_messages" \
41
+ --limit 42
42
+
40
43
  --action "send_user" \
41
44
  --number "xxxxxxxxxxxx" \
42
45
  --message "This is a test! 🚀"
@@ -48,7 +51,7 @@ npx wahelper \
48
51
  --action "send_user" \
49
52
  --number "xxxxxxxxxxxx" \
50
53
  --message "This is a test! 🚀" \
51
- --attachments="/full/path/to/file.pdf,/full/path/to/image.png"
54
+ --attachments "/full/path/to/file.pdf,/full/path/to/image.png"
52
55
 
53
56
  sqlite3 \
54
57
  -header \
@@ -62,45 +65,41 @@ sqlite3 \
62
65
 
63
66
  ```php
64
67
  require_once __DIR__ . '/vendor/autoload.php';
65
- use vielhuber\wahelper\WhatsApp;
68
+ use vielhuber\wahelper\wahelper;
66
69
 
67
- // fetch messages
68
- WhatsApp::run([
69
- 'device' => 'xxxxxxxxxxxx',
70
- 'action' => 'fetch_messages'
71
- ]);
70
+ $wahelper = new wahelper();
72
71
 
73
- // send messages
74
- WhatsApp::run([
75
- 'device' => 'xxxxxxxxxxxx',
76
- 'action' => 'send_user',
77
- 'number' => 'xxxxxxxxxxxx',
78
- 'message' => 'This is a test! 🚀'
79
- ]);
80
- WhatsApp::run([
81
- 'device' => 'xxxxxxxxxxxx',
82
- 'action' => 'send_group',
83
- 'name' => 'Group name',
84
- 'message' => 'This is a test! 🚀'
72
+ // fetch messages
73
+ $wahelper->fetchMessages(
74
+ device: 'xxxxxxxxxxxx',
75
+ limit: 42
76
+ );
77
+
78
+ // send message to user
79
+ $wahelper->sendUser(
80
+ device: 'xxxxxxxxxxxx',
81
+ number: 'xxxxxxxxxxxx',
82
+ message => 'This is a test! 🚀',
83
+ attachments: ['/full/path/to/file.pdf', '/full/path/to/image.png']
85
84
  ]);
86
85
 
87
- // send attachments
88
- WhatsApp::run([
89
- 'device' => 'xxxxxxxxxxxx',
90
- 'action' => 'send_user', // or 'send_group'
91
- /* ... */
92
- 'attachments' => ['/full/path/to/file.pdf', '/full/path/to/image.png']
86
+ // send message to group
87
+ $wahelper->sendGroup(
88
+ device: 'xxxxxxxxxxxx',
89
+ name: 'Group name',
90
+ message: 'This is a test! 🚀',
91
+ attachments: ['/full/path/to/file.pdf', '/full/path/to/image.png']
93
92
  ]);
94
93
  ```
95
94
 
96
95
  ### mcp
97
96
 
98
- ```
97
+ ```json
99
98
  {
100
99
  "mcpServers": {
101
100
  "whatsapp": {
102
101
  "command": "/root/.nvm/versions/node/v22.20.0/bin/node",
103
- "args": ["/var/www/project/node_modules/@vielhuber/wahelper/WhatsApp.js", "--mcp"]
102
+ "args": ["/var/www/project/node_modules/@vielhuber/wahelper/mcp.js"]
104
103
  }
105
104
  }
106
105
  }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@vielhuber/wahelper",
3
- "version": "1.3.6",
3
+ "version": "1.3.7",
4
4
  "description": "Lightweight whatsapp integration layer.",
5
- "main": "WhatsApp.js",
5
+ "main": "wahelper.js",
6
6
  "files": [
7
- "WhatsApp.js"
7
+ "wahelper.js"
8
8
  ],
9
9
  "bin": {
10
- "wahelper": "WhatsApp.js"
10
+ "wahelper": "wahelper.js"
11
11
  },
12
12
  "publishConfig": {
13
13
  "access": "public"
@@ -20,11 +20,10 @@
20
20
  "author": "David Vielhuber <david@vielhuber.de>",
21
21
  "license": "MIT",
22
22
  "dependencies": {
23
- "@modelcontextprotocol/sdk": "^1.20.2",
24
- "baileys": "^7.0.0-rc.6"
23
+ "baileys": "^7.0.0-rc.9"
25
24
  },
26
25
  "devDependencies": {
27
26
  "@prettier/plugin-php": "^0.24.0",
28
- "prettier": "^3.6.2"
27
+ "prettier": "^3.7.4"
29
28
  }
30
29
  }
@@ -1,20 +1,11 @@
1
- #!/usr/bin/env node
2
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
- import { z } from 'zod';
5
- import makeWASocket, {
6
- useMultiFileAuthState,
7
- DisconnectReason,
8
- downloadMediaMessage,
9
- processSyncAction
10
- } from 'baileys';
1
+ import makeWASocket, { useMultiFileAuthState, DisconnectReason, downloadMediaMessage } from 'baileys';
11
2
  import P from 'pino';
12
3
  import { fileURLToPath } from 'url';
13
4
  import { dirname } from 'path';
14
5
  import fs from 'fs';
15
6
  import { DatabaseSync } from 'node:sqlite';
16
7
 
17
- export default class WhatsApp {
8
+ export default class wahelper {
18
9
  constructor() {
19
10
  this.args = this.parseArgs();
20
11
  this.dirname = this.getDirname();
@@ -28,7 +19,6 @@ export default class WhatsApp {
28
19
  this.dbIsOpen = false;
29
20
  this.shutdown = false;
30
21
  this.isFirstRun = false;
31
- this.isMcp = this.args.mcp === true;
32
22
  this.inactivityTimeMaxOrig = 10;
33
23
  this.inactivityTimeMax = this.inactivityTimeMaxOrig;
34
24
  this.inactivityTimeCur = 0;
@@ -61,64 +51,55 @@ export default class WhatsApp {
61
51
  this.setLock('init', true);
62
52
  this.write({ success: false, message: 'loading_state', data: null }, false);
63
53
 
64
- if (this.isMcp === false) {
65
- this.log('cli start');
66
- this.log(this.args);
67
- if (
68
- this.args.device === undefined ||
69
- (this.args.action === 'send_user' &&
70
- (this.args.number === undefined || this.args.message === undefined)) ||
71
- (this.args.action === 'send_group' &&
72
- (this.args.name === undefined || this.args.message === undefined)) ||
73
- !['fetch_messages', 'send_user', 'send_group'].includes(this.args.action)
74
- ) {
75
- console.error('input missing or unknown action!');
76
- this.log('⛔input missing or unknown action!');
77
- this.write(
78
- {
79
- success: false,
80
- message: 'error',
81
- public_message: 'input missing or unknown action!',
82
- data: null
83
- },
84
- true
54
+ this.log('cli start');
55
+ this.log(this.args);
56
+ if (
57
+ this.args.device === undefined ||
58
+ (this.args.action === 'send_user' && (this.args.number === undefined || this.args.message === undefined)) ||
59
+ (this.args.action === 'send_group' && (this.args.name === undefined || this.args.message === undefined)) ||
60
+ (this.args.action === 'fetch_messages' &&
61
+ this.args.limit !== undefined &&
62
+ typeof this.args.limit !== 'number') ||
63
+ !['fetch_messages', 'send_user', 'send_group'].includes(this.args.action)
64
+ ) {
65
+ console.error('input missing or unknown action!');
66
+ this.log('⛔input missing or unknown action!');
67
+ this.write(
68
+ {
69
+ success: false,
70
+ message: 'error',
71
+ public_message: 'input missing or unknown action!',
72
+ data: null
73
+ },
74
+ true
75
+ );
76
+ this.removeLocks();
77
+ } else {
78
+ this.initDatabase();
79
+ this.initInactivityTimer();
80
+ this.initExitHooks();
81
+ if (this.args.reset === true) {
82
+ this.resetFolder();
83
+ }
84
+ let response = null;
85
+ if (this.args.action === 'fetch_messages') {
86
+ response = await this.authAndRun(() => this.fetchMessages(this.args.limit));
87
+ }
88
+ if (this.args.action === 'send_user') {
89
+ response = await this.authAndRun(() =>
90
+ this.sendMessageToUser(this.args.number, this.args.message, this.args.attachments)
85
91
  );
86
- this.removeLocks();
87
- } else {
88
- this.initDatabase();
89
- this.initInactivityTimer();
90
- this.initExitHooks();
91
- if (this.args.reset === true) {
92
- this.resetFolder();
93
- }
94
- let response = null;
95
- if (this.args.action === 'fetch_messages') {
96
- response = await this.authAndRun(() => this.fetchMessages());
97
- }
98
- if (this.args.action === 'send_user') {
99
- response = await this.authAndRun(() =>
100
- this.sendMessageToUser(this.args.number, this.args.message, this.args.attachments)
101
- );
102
- }
103
- if (this.args.action === 'send_group') {
104
- response = await this.authAndRun(() =>
105
- this.sendMessageToGroup(this.args.name, this.args.message, this.args.attachments)
106
- );
107
- }
108
- console.log(response);
109
- await this.endSession();
110
92
  }
111
- this.log('cli stop');
112
- //process.exit();
113
- }
114
-
115
- if (this.isMcp === true) {
116
- this.log('mcp start');
117
- this.registerMcp();
118
- let transport = new StdioServerTransport();
119
- await server.connect(transport);
120
- this.log('mcp stop');
93
+ if (this.args.action === 'send_group') {
94
+ response = await this.authAndRun(() =>
95
+ this.sendMessageToGroup(this.args.name, this.args.message, this.args.attachments)
96
+ );
97
+ }
98
+ console.log(response);
99
+ await this.endSession();
121
100
  }
101
+ this.log('cli stop');
102
+ //process.exit();
122
103
  }
123
104
 
124
105
  async endSession() {
@@ -194,45 +175,35 @@ export default class WhatsApp {
194
175
  // increase inactivity timer on pairing
195
176
  this.restartInactivityTimer();
196
177
  this.setInactivityTimeMax(60);
197
- if (!this.isMcp) {
198
- //let code = await QRCode.toString(qr, { type: 'utf8' });
199
- //console.log(code);
200
- let code = await this.sock.requestPairingCode(this.formatNumber(this.args.device));
201
- // format code XXXXXXX => XXXX-XXXX
202
- code = code.match(/.{1,4}/g).join('-');
203
- console.log('Bitte verknüpfe das neue Gerät und gib diesen Code ein:');
204
- console.log(code);
205
- this.write({ success: false, message: 'pairing_code_required', data: code }, true);
206
- }
207
- if (this.isMcp) {
208
- resolve({
209
- content: [{ type: 'text', text: 'QR Code muss gescannt werden.' }]
210
- });
211
- return;
212
- }
178
+ //let code = await QRCode.toString(qr, { type: 'utf8' });
179
+ //console.log(code);
180
+ let code = await this.sock.requestPairingCode(this.formatNumber(this.args.device));
181
+ // format code XXXXXXX => XXXX-XXXX
182
+ code = code.match(/.{1,4}/g).join('-');
183
+ console.log('Bitte verknüpfe das neue Gerät und gib diesen Code ein:');
184
+ console.log(code);
185
+ this.write({ success: false, message: 'pairing_code_required', data: code }, true);
213
186
  } else {
214
187
  if (connection === 'close') {
215
- if (this.isMcp === false) {
216
- // reconnect after pairing (needed!)
217
- if (statusCode === DisconnectReason.restartRequired) {
218
- // again: reset inactivity timer after pairing
219
- this.restartInactivityTimer();
220
- this.setInactivityTimeMax(this.inactivityTimeMaxOrig);
221
- this.log('⚠️close: reconnect');
222
- resolve(await this.authAndRun(fn));
223
- return;
224
- } else if (statusCode === 401) {
225
- this.log('⚠️close: 2');
226
- if (this.resetFolder() === true) {
227
- console.log('reset authentication. try again!');
228
- }
229
- resolve(await this.authAndRun(fn));
230
- return;
231
- } else {
232
- this.log('⚠️close: 3');
233
- resolve();
234
- return;
188
+ // reconnect after pairing (needed!)
189
+ if (statusCode === DisconnectReason.restartRequired) {
190
+ // again: reset inactivity timer after pairing
191
+ this.restartInactivityTimer();
192
+ this.setInactivityTimeMax(this.inactivityTimeMaxOrig);
193
+ this.log('⚠️close: reconnect');
194
+ resolve(await this.authAndRun(fn));
195
+ return;
196
+ } else if (statusCode === 401) {
197
+ this.log('⚠️close: 2');
198
+ if (this.resetFolder() === true) {
199
+ console.log('reset authentication. try again!');
235
200
  }
201
+ resolve(await this.authAndRun(fn));
202
+ return;
203
+ } else {
204
+ this.log('⚠️close: 3');
205
+ resolve();
206
+ return;
236
207
  }
237
208
  }
238
209
 
@@ -354,7 +325,7 @@ export default class WhatsApp {
354
325
  return;
355
326
  }
356
327
 
357
- async fetchMessages() {
328
+ async fetchMessages(limit = null) {
358
329
  // wait for inactivity
359
330
  await this.awaitInactivityTimer();
360
331
 
@@ -365,6 +336,7 @@ export default class WhatsApp {
365
336
  SELECT id, \`from\`, \`to\`, content, media_filename, timestamp
366
337
  FROM messages
367
338
  ORDER BY timestamp DESC
339
+ ${limit !== null ? 'LIMIT ' + limit : ''}
368
340
  `
369
341
  )
370
342
  .all();
@@ -493,60 +465,6 @@ export default class WhatsApp {
493
465
  };
494
466
  }
495
467
 
496
- registerMcp() {
497
- let server = new McpServer({
498
- name: 'whatsapp-mcp',
499
- version: '1.0.0'
500
- });
501
-
502
- server.registerTool(
503
- 'fetch_messages',
504
- {
505
- title: 'Fetch messages',
506
- description: 'Fetch all messages',
507
- inputSchema: {},
508
- outputSchema: { result: z.string().describe('Result of fetching messages') }
509
- },
510
- async ({}) => {
511
- let response = await this.authAndRun(() => fetchMessages());
512
- return response;
513
- }
514
- );
515
-
516
- server.registerTool(
517
- 'send_group',
518
- {
519
- title: 'Send message to group',
520
- description: 'Send message to group',
521
- inputSchema: { group: z.string().describe('Group name'), text: z.string().describe('Message text') },
522
- outputSchema: { result: z.string().describe('Result of sending message') }
523
- },
524
- async ({ group, text }) => {
525
- this.log([group, text]);
526
- let response = await this.authAndRun(() => sendMessageToGroup(group, text));
527
- return response;
528
- }
529
- );
530
-
531
- server.registerTool(
532
- 'send_message',
533
- {
534
- title: 'Send message to person',
535
- description: 'Send message to person',
536
- inputSchema: {
537
- number: z.string().describe('Person number'),
538
- text: z.string().describe('Message text')
539
- },
540
- outputSchema: { result: z.string().describe('Result of sending message') }
541
- },
542
- async ({ number, text }) => {
543
- this.log([number, text]);
544
- let response = await this.authAndRun(() => sendMessageToPerson(number, text));
545
- return response;
546
- }
547
- );
548
- }
549
-
550
468
  formatNumber(number) {
551
469
  // replace leading zero with 49
552
470
  number = number.replace(/^0+/, '49');
@@ -587,6 +505,10 @@ export default class WhatsApp {
587
505
  value = value.split(',');
588
506
  }
589
507
 
508
+ if (key === 'limit') {
509
+ value = parseInt(value);
510
+ }
511
+
590
512
  args[key] = value;
591
513
  }
592
514
  }
@@ -831,5 +753,5 @@ export default class WhatsApp {
831
753
  }
832
754
  }
833
755
 
834
- let wa = new WhatsApp();
756
+ let wa = new wahelper();
835
757
  wa.init();