whazaa 0.1.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/LICENSE +21 -0
- package/README.md +204 -0
- package/dist/auth.d.ts +31 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +53 -0
- package/dist/auth.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +144 -0
- package/dist/index.js.map +1 -0
- package/dist/whatsapp.d.ts +54 -0
- package/dist/whatsapp.d.ts.map +1 -0
- package/dist/whatsapp.js +300 -0
- package/dist/whatsapp.js.map +1 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Matthias Nott
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Whazaa
|
|
2
|
+
|
|
3
|
+
WhatsApp MCP server for Claude Code — bidirectional self-chat messaging.
|
|
4
|
+
|
|
5
|
+
You message yourself on WhatsApp, Claude receives it. Claude responds, you see it on WhatsApp. Your phone becomes a parallel terminal.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Bidirectional messaging** — send from Claude, receive from your phone
|
|
12
|
+
- **Zero configuration** — auto-detects your phone number after first scan
|
|
13
|
+
- **First-run QR pairing** — scan once, connects automatically thereafter
|
|
14
|
+
- **Markdown support** — `**bold**`, `*italic*`, `` `code` `` converted to WhatsApp format
|
|
15
|
+
- **Deduplication** — outgoing messages never echo back as incoming
|
|
16
|
+
- **Exponential backoff** — reconnects automatically (1s to 60s)
|
|
17
|
+
- **MCP-safe** — all output except JSON-RPC goes to stderr
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
### Option 1: Run directly with npx / bunx
|
|
24
|
+
|
|
25
|
+
No installation required:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx whazaa
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
or with Bun:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
bunx whazaa
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The first run prints a QR code to your terminal. Scan it with WhatsApp (Settings -> Linked Devices -> Link a Device). Credentials are saved to `~/.whazaa/auth/` and all subsequent runs connect automatically.
|
|
38
|
+
|
|
39
|
+
### Option 2: Install globally
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install -g whazaa
|
|
43
|
+
whazaa
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Option 3: Build from source
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
git clone https://github.com/mnott/Whazaa.git
|
|
50
|
+
cd whazaa
|
|
51
|
+
npm install
|
|
52
|
+
npm run build
|
|
53
|
+
node dist/index.js
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Claude Code MCP Configuration
|
|
59
|
+
|
|
60
|
+
Add Whazaa to `~/.claude/.mcp.json` (or your project's `.mcp.json`):
|
|
61
|
+
|
|
62
|
+
### Using npx (always latest version)
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"mcpServers": {
|
|
67
|
+
"whazaa": {
|
|
68
|
+
"command": "npx",
|
|
69
|
+
"args": ["whazaa"],
|
|
70
|
+
"description": "Whazaa — WhatsApp self-chat MCP server for Claude Code"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Using bunx
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"mcpServers": {
|
|
81
|
+
"whazaa": {
|
|
82
|
+
"command": "bunx",
|
|
83
|
+
"args": ["whazaa"],
|
|
84
|
+
"description": "Whazaa — WhatsApp self-chat MCP server for Claude Code"
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Using a local build
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"mcpServers": {
|
|
95
|
+
"whazaa": {
|
|
96
|
+
"command": "node",
|
|
97
|
+
"args": ["/path/to/whazaa/dist/index.js"],
|
|
98
|
+
"description": "Whazaa — WhatsApp self-chat MCP server for Claude Code"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
After updating the MCP config, restart Claude Code. On first run, Whazaa prints a QR code to the Claude Code logs (check Settings -> Developer -> MCP Logs or run it manually from a terminal first to complete pairing).
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Available Tools
|
|
109
|
+
|
|
110
|
+
| Tool | Description |
|
|
111
|
+
|------|-------------|
|
|
112
|
+
| `whatsapp_status` | Report connection state and phone number |
|
|
113
|
+
| `whatsapp_send` | Send a message to your own WhatsApp self-chat |
|
|
114
|
+
| `whatsapp_receive` | Drain queued incoming messages from your phone |
|
|
115
|
+
| `whatsapp_login` | Trigger a new QR pairing flow |
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## How It Works
|
|
120
|
+
|
|
121
|
+
Whazaa uses the [Baileys](https://github.com/WhiskeySockets/Baileys) library to maintain a persistent WebSocket connection to WhatsApp's servers using the same multi-device protocol as WhatsApp Web. It exposes four MCP tools over stdin/stdout and routes all Baileys output to stderr to keep the JSON-RPC stream clean. Incoming messages from your phone are queued in memory and returned when `whatsapp_receive` is called.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Environment Variables
|
|
126
|
+
|
|
127
|
+
| Variable | Default | Description |
|
|
128
|
+
|----------|---------|-------------|
|
|
129
|
+
| `WHAZAA_AUTH_DIR` | `~/.whazaa/auth/` | Directory for WhatsApp session credentials |
|
|
130
|
+
|
|
131
|
+
### Example: custom auth directory
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"mcpServers": {
|
|
136
|
+
"whazaa": {
|
|
137
|
+
"command": "node",
|
|
138
|
+
"args": ["/path/to/whazaa/dist/index.js"],
|
|
139
|
+
"env": {
|
|
140
|
+
"WHAZAA_AUTH_DIR": "/custom/path/whatsapp-creds"
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## First-Time Pairing
|
|
150
|
+
|
|
151
|
+
1. Run Whazaa from a terminal so you can see the QR code:
|
|
152
|
+
```bash
|
|
153
|
+
npx whazaa
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
2. A QR code prints on stderr. Open WhatsApp on your phone:
|
|
157
|
+
- iOS: Settings -> Linked Devices -> Link a Device
|
|
158
|
+
- Android: Menu (three dots) -> Linked Devices -> Link a Device
|
|
159
|
+
|
|
160
|
+
3. Scan the QR code. Whazaa logs `Connected. Phone: +XXXXXXXXXXX`.
|
|
161
|
+
|
|
162
|
+
4. Add the MCP config to Claude Code. Credentials are now saved; subsequent connections are automatic.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Troubleshooting
|
|
167
|
+
|
|
168
|
+
**QR code not appearing**
|
|
169
|
+
|
|
170
|
+
Run Whazaa directly from a terminal (`npx whazaa`) rather than from within Claude Code's MCP runner, which may not show stderr. Once paired, restart Claude Code.
|
|
171
|
+
|
|
172
|
+
**"Logged out (401)" error**
|
|
173
|
+
|
|
174
|
+
Your session was invalidated (e.g. you unlinked the device in WhatsApp). Use the `whatsapp_login` tool or delete `~/.whazaa/auth/` and restart Whazaa to re-pair.
|
|
175
|
+
|
|
176
|
+
**Messages not received**
|
|
177
|
+
|
|
178
|
+
Call `whatsapp_receive` to drain the queue. Only messages sent to your own number (the self-chat / "Saved Messages" chat) are captured.
|
|
179
|
+
|
|
180
|
+
**Connection keeps dropping**
|
|
181
|
+
|
|
182
|
+
Whazaa uses exponential backoff to reconnect automatically. Check your network connection. If the problem persists, use `whatsapp_login` to re-establish the session.
|
|
183
|
+
|
|
184
|
+
**Multiple WhatsApp accounts**
|
|
185
|
+
|
|
186
|
+
Set `WHAZAA_AUTH_DIR` to a different directory for each account and run separate instances.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Security Notes
|
|
191
|
+
|
|
192
|
+
- Session credentials are stored locally in `~/.whazaa/auth/`. Treat them like passwords.
|
|
193
|
+
- Whazaa only reads and sends messages in your self-chat. It does not have access to other conversations.
|
|
194
|
+
- No data is sent to any third-party service. The connection is directly to WhatsApp's servers via Baileys.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
MIT — see [LICENSE](LICENSE)
|
|
201
|
+
|
|
202
|
+
## Author
|
|
203
|
+
|
|
204
|
+
Matthias Nott — [github.com/mnott](https://github.com/mnott)
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auth.ts — Auth directory resolution and QR code display helper
|
|
3
|
+
*
|
|
4
|
+
* Whazaa stores session credentials in ~/.whazaa/auth/ by default.
|
|
5
|
+
* Override with WHAZAA_AUTH_DIR environment variable.
|
|
6
|
+
*
|
|
7
|
+
* QR codes are printed to stderr so they never pollute MCP's JSON-RPC
|
|
8
|
+
* stream on stdout.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the auth directory, with the following priority:
|
|
12
|
+
* 1. WHAZAA_AUTH_DIR environment variable
|
|
13
|
+
* 2. ~/.whazaa/auth/ (default)
|
|
14
|
+
*
|
|
15
|
+
* Creates the directory if it does not exist.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveAuthDir(): string;
|
|
18
|
+
/**
|
|
19
|
+
* Return true if an existing auth session appears to be present.
|
|
20
|
+
* Baileys writes creds.json when credentials are saved.
|
|
21
|
+
*/
|
|
22
|
+
export declare function hasExistingSession(authDir: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Print a QR code to stderr.
|
|
25
|
+
*
|
|
26
|
+
* Baileys provides the raw QR string in its 'connection.update' event.
|
|
27
|
+
* We render it here so it appears in the user's terminal without
|
|
28
|
+
* contaminating the MCP JSON-RPC stream on stdout.
|
|
29
|
+
*/
|
|
30
|
+
export declare function printQR(qrString: string): void;
|
|
31
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH;;;;;;GAMG;AACH,wBAAgB,cAAc,IAAI,MAAM,CASvC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAG3D;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAY9C"}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auth.ts — Auth directory resolution and QR code display helper
|
|
3
|
+
*
|
|
4
|
+
* Whazaa stores session credentials in ~/.whazaa/auth/ by default.
|
|
5
|
+
* Override with WHAZAA_AUTH_DIR environment variable.
|
|
6
|
+
*
|
|
7
|
+
* QR codes are printed to stderr so they never pollute MCP's JSON-RPC
|
|
8
|
+
* stream on stdout.
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
11
|
+
import { homedir } from "node:os";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import qrcode from "qrcode-terminal";
|
|
14
|
+
/**
|
|
15
|
+
* Resolve the auth directory, with the following priority:
|
|
16
|
+
* 1. WHAZAA_AUTH_DIR environment variable
|
|
17
|
+
* 2. ~/.whazaa/auth/ (default)
|
|
18
|
+
*
|
|
19
|
+
* Creates the directory if it does not exist.
|
|
20
|
+
*/
|
|
21
|
+
export function resolveAuthDir() {
|
|
22
|
+
const dir = process.env.WHAZAA_AUTH_DIR ?? join(homedir(), ".whazaa", "auth");
|
|
23
|
+
if (!existsSync(dir)) {
|
|
24
|
+
mkdirSync(dir, { recursive: true });
|
|
25
|
+
process.stderr.write(`[whazaa] Created auth directory: ${dir}\n`);
|
|
26
|
+
}
|
|
27
|
+
return dir;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Return true if an existing auth session appears to be present.
|
|
31
|
+
* Baileys writes creds.json when credentials are saved.
|
|
32
|
+
*/
|
|
33
|
+
export function hasExistingSession(authDir) {
|
|
34
|
+
const credsPath = join(authDir, "creds.json");
|
|
35
|
+
return existsSync(credsPath);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Print a QR code to stderr.
|
|
39
|
+
*
|
|
40
|
+
* Baileys provides the raw QR string in its 'connection.update' event.
|
|
41
|
+
* We render it here so it appears in the user's terminal without
|
|
42
|
+
* contaminating the MCP JSON-RPC stream on stdout.
|
|
43
|
+
*/
|
|
44
|
+
export function printQR(qrString) {
|
|
45
|
+
process.stderr.write("\n[whazaa] Scan the QR code below with WhatsApp:\n");
|
|
46
|
+
process.stderr.write("[whazaa] Open WhatsApp -> Linked Devices -> Link a Device\n\n");
|
|
47
|
+
// qrcode-terminal writes to stdout by default; we capture and re-emit to stderr
|
|
48
|
+
qrcode.generate(qrString, { small: true }, (rendered) => {
|
|
49
|
+
process.stderr.write(rendered + "\n");
|
|
50
|
+
});
|
|
51
|
+
process.stderr.write("[whazaa] Waiting for scan...\n\n");
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=auth.js.map
|
package/dist/auth.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,iBAAiB,CAAC;AAErC;;;;;;GAMG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;IAE9E,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oCAAoC,GAAG,IAAI,CAAC,CAAC;IACpE,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC9C,OAAO,UAAU,CAAC,SAAS,CAAC,CAAC;AAC/B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CAAC,QAAgB;IACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAC;IAC3E,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,+DAA+D,CAChE,CAAC;IAEF,gFAAgF;IAChF,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,QAAgB,EAAE,EAAE;QAC9D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;AAC3D,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* index.ts — Whazaa MCP server entry point
|
|
4
|
+
*
|
|
5
|
+
* Exposes four tools over the Model Context Protocol (stdio transport):
|
|
6
|
+
*
|
|
7
|
+
* whatsapp_status — Report connection state and phone number
|
|
8
|
+
* whatsapp_send — Send a message to your own WhatsApp number
|
|
9
|
+
* whatsapp_receive — Drain queued incoming messages from your phone
|
|
10
|
+
* whatsapp_login — Trigger a new QR pairing flow
|
|
11
|
+
*
|
|
12
|
+
* CRITICAL: stdout is the MCP JSON-RPC transport.
|
|
13
|
+
* - NEVER write non-JSON to stdout.
|
|
14
|
+
* - All debug output, QR codes, and logs go to stderr.
|
|
15
|
+
*/
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;GAaG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* index.ts — Whazaa MCP server entry point
|
|
4
|
+
*
|
|
5
|
+
* Exposes four tools over the Model Context Protocol (stdio transport):
|
|
6
|
+
*
|
|
7
|
+
* whatsapp_status — Report connection state and phone number
|
|
8
|
+
* whatsapp_send — Send a message to your own WhatsApp number
|
|
9
|
+
* whatsapp_receive — Drain queued incoming messages from your phone
|
|
10
|
+
* whatsapp_login — Trigger a new QR pairing flow
|
|
11
|
+
*
|
|
12
|
+
* CRITICAL: stdout is the MCP JSON-RPC transport.
|
|
13
|
+
* - NEVER write non-JSON to stdout.
|
|
14
|
+
* - All debug output, QR codes, and logs go to stderr.
|
|
15
|
+
*/
|
|
16
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
17
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
18
|
+
import { z } from "zod";
|
|
19
|
+
import { initialize, getStatus, sendMessage, drainMessages, triggerLogin, } from "./whatsapp.js";
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// MCP server setup
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
const server = new McpServer({
|
|
24
|
+
name: "whazaa",
|
|
25
|
+
version: "0.1.0",
|
|
26
|
+
});
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Tool: whatsapp_status
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
server.tool("whatsapp_status", "Check the Whazaa connection state and the WhatsApp phone number it is logged in as.", {}, async () => {
|
|
31
|
+
const s = getStatus();
|
|
32
|
+
let text;
|
|
33
|
+
if (s.awaitingQR) {
|
|
34
|
+
text =
|
|
35
|
+
"Awaiting QR scan. Check the terminal where Whazaa is running and scan the QR code with WhatsApp.";
|
|
36
|
+
}
|
|
37
|
+
else if (s.connected && s.phoneNumber) {
|
|
38
|
+
text = `Connected. Phone: +${s.phoneNumber}`;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
text =
|
|
42
|
+
"Disconnected. Whazaa is attempting to reconnect in the background.";
|
|
43
|
+
}
|
|
44
|
+
return { content: [{ type: "text", text }] };
|
|
45
|
+
});
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Tool: whatsapp_send
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
server.tool("whatsapp_send", [
|
|
50
|
+
"Send a message to yourself via WhatsApp self-chat.",
|
|
51
|
+
"Supports basic Markdown: **bold**, *italic*, `code`.",
|
|
52
|
+
"The message appears in your own WhatsApp chat with yourself.",
|
|
53
|
+
].join(" "), {
|
|
54
|
+
message: z
|
|
55
|
+
.string()
|
|
56
|
+
.min(1)
|
|
57
|
+
.describe("The message text to send to your WhatsApp self-chat"),
|
|
58
|
+
}, async ({ message }) => {
|
|
59
|
+
try {
|
|
60
|
+
await sendMessage(message);
|
|
61
|
+
const preview = message.length > 80 ? `${message.slice(0, 80)}...` : message;
|
|
62
|
+
return {
|
|
63
|
+
content: [{ type: "text", text: `Sent: ${preview}` }],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
68
|
+
return {
|
|
69
|
+
content: [{ type: "text", text: `Error: ${errMsg}` }],
|
|
70
|
+
isError: true,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Tool: whatsapp_receive
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
server.tool("whatsapp_receive", [
|
|
78
|
+
"Return all queued incoming WhatsApp messages received since the last call, then clear the queue.",
|
|
79
|
+
"Messages are from your own WhatsApp number (self-chat) — i.e. messages you type on your phone.",
|
|
80
|
+
"Returns 'No new messages.' if the queue is empty.",
|
|
81
|
+
].join(" "), {}, async () => {
|
|
82
|
+
const messages = drainMessages();
|
|
83
|
+
if (messages.length === 0) {
|
|
84
|
+
return { content: [{ type: "text", text: "No new messages." }] };
|
|
85
|
+
}
|
|
86
|
+
const formatted = messages
|
|
87
|
+
.map((m) => {
|
|
88
|
+
const ts = new Date(m.timestamp).toISOString();
|
|
89
|
+
return `[${ts}] ${m.body}`;
|
|
90
|
+
})
|
|
91
|
+
.join("\n");
|
|
92
|
+
return { content: [{ type: "text", text: formatted }] };
|
|
93
|
+
});
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Tool: whatsapp_login
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
server.tool("whatsapp_login", [
|
|
98
|
+
"Trigger a new WhatsApp QR pairing flow.",
|
|
99
|
+
"Use this when the connection is lost and automatic reconnection fails,",
|
|
100
|
+
"or when you need to link a different phone number.",
|
|
101
|
+
"A QR code will be printed to the Whazaa server's stderr — check the terminal where it is running.",
|
|
102
|
+
].join(" "), {}, async () => {
|
|
103
|
+
try {
|
|
104
|
+
// Non-blocking: triggerLogin initiates the reconnect but QR display
|
|
105
|
+
// happens asynchronously via the Baileys event handler.
|
|
106
|
+
triggerLogin().catch((err) => {
|
|
107
|
+
process.stderr.write(`[whazaa] Login trigger error: ${err}\n`);
|
|
108
|
+
});
|
|
109
|
+
return {
|
|
110
|
+
content: [
|
|
111
|
+
{
|
|
112
|
+
type: "text",
|
|
113
|
+
text: "QR pairing initiated. Check the terminal where Whazaa is running and scan the QR code with WhatsApp (Linked Devices -> Link a Device).",
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
120
|
+
return {
|
|
121
|
+
content: [{ type: "text", text: `Error: ${errMsg}` }],
|
|
122
|
+
isError: true,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
// Main
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
async function main() {
|
|
130
|
+
// Start WhatsApp connection in the background.
|
|
131
|
+
// initialize() resolves once connected OR once a QR code has been emitted,
|
|
132
|
+
// so the MCP server is immediately available for tool calls in both cases.
|
|
133
|
+
initialize().catch((err) => {
|
|
134
|
+
process.stderr.write(`[whazaa] Initialization error: ${err}\n`);
|
|
135
|
+
});
|
|
136
|
+
// Start the MCP server over stdio
|
|
137
|
+
const transport = new StdioServerTransport();
|
|
138
|
+
await server.connect(transport);
|
|
139
|
+
}
|
|
140
|
+
main().catch((err) => {
|
|
141
|
+
process.stderr.write(`[whazaa] Fatal error: ${err}\n`);
|
|
142
|
+
process.exit(1);
|
|
143
|
+
});
|
|
144
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* whatsapp.ts — WhatsApp connection manager
|
|
3
|
+
*
|
|
4
|
+
* Manages the Baileys WebSocket connection with:
|
|
5
|
+
* - Automatic self-JID detection (no hardcoded phone number)
|
|
6
|
+
* - QR pairing flow on first run, silent reconnect thereafter
|
|
7
|
+
* - Exponential backoff reconnection (1s -> 60s max)
|
|
8
|
+
* - Deduplication of sent messages to prevent self-echo
|
|
9
|
+
* - Markdown -> WhatsApp format conversion
|
|
10
|
+
* - All Baileys output silenced to avoid MCP stdio pollution
|
|
11
|
+
*/
|
|
12
|
+
export interface QueuedMessage {
|
|
13
|
+
body: string;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
}
|
|
16
|
+
export interface WhatsAppStatus {
|
|
17
|
+
connected: boolean;
|
|
18
|
+
/** E.164 phone number without leading +, e.g. "1234567890" */
|
|
19
|
+
phoneNumber: string | null;
|
|
20
|
+
/** Full WhatsApp JID, e.g. "1234567890@s.whatsapp.net" */
|
|
21
|
+
selfJid: string | null;
|
|
22
|
+
/** Linked Identity JID, e.g. "123456789012@lid" — used for self-chat */
|
|
23
|
+
selfLid: string | null;
|
|
24
|
+
/** Whether a QR scan is currently required */
|
|
25
|
+
awaitingQR: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Initialize the WhatsApp connection.
|
|
29
|
+
* Resolves once the connection is open OR a QR code has been emitted
|
|
30
|
+
* (so the MCP server can start handling tool calls immediately).
|
|
31
|
+
*/
|
|
32
|
+
export declare function initialize(): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Trigger a new QR pairing flow.
|
|
35
|
+
* Closes the current socket (if any) and reconnects with fresh state
|
|
36
|
+
* so Baileys emits a new QR code.
|
|
37
|
+
*/
|
|
38
|
+
export declare function triggerLogin(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Return the current connection status snapshot.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getStatus(): WhatsAppStatus;
|
|
43
|
+
/**
|
|
44
|
+
* Send a message to the authenticated user's own WhatsApp number.
|
|
45
|
+
* Converts Markdown to WhatsApp formatting before sending.
|
|
46
|
+
*
|
|
47
|
+
* @throws If not connected or socket is unavailable.
|
|
48
|
+
*/
|
|
49
|
+
export declare function sendMessage(message: string): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Drain and return all queued incoming messages, then clear the queue.
|
|
52
|
+
*/
|
|
53
|
+
export declare function drainMessages(): QueuedMessage[];
|
|
54
|
+
//# sourceMappingURL=whatsapp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whatsapp.d.ts","sourceRoot":"","sources":["../src/whatsapp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAaH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,0DAA0D;IAC1D,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,wEAAwE;IACxE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,8CAA8C;IAC9C,UAAU,EAAE,OAAO,CAAC;CACrB;AAmOD;;;;GAIG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAQhD;AAED;;;;GAIG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CA4BlD;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,cAAc,CAE1C;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6BhE;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,aAAa,EAAE,CAI/C"}
|
package/dist/whatsapp.js
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* whatsapp.ts — WhatsApp connection manager
|
|
3
|
+
*
|
|
4
|
+
* Manages the Baileys WebSocket connection with:
|
|
5
|
+
* - Automatic self-JID detection (no hardcoded phone number)
|
|
6
|
+
* - QR pairing flow on first run, silent reconnect thereafter
|
|
7
|
+
* - Exponential backoff reconnection (1s -> 60s max)
|
|
8
|
+
* - Deduplication of sent messages to prevent self-echo
|
|
9
|
+
* - Markdown -> WhatsApp format conversion
|
|
10
|
+
* - All Baileys output silenced to avoid MCP stdio pollution
|
|
11
|
+
*/
|
|
12
|
+
import makeWASocket, { DisconnectReason, fetchLatestBaileysVersion, makeCacheableSignalKeyStore, useMultiFileAuthState, } from "@whiskeysockets/baileys";
|
|
13
|
+
import pino from "pino";
|
|
14
|
+
import { resolveAuthDir, printQR } from "./auth.js";
|
|
15
|
+
// --- Module state ------------------------------------------------------------
|
|
16
|
+
/** The active Baileys socket (null when disconnected) */
|
|
17
|
+
let sock = null;
|
|
18
|
+
/** Incoming messages from the phone waiting to be drained */
|
|
19
|
+
const messageQueue = [];
|
|
20
|
+
/** IDs of messages sent by this process — used to suppress self-echo */
|
|
21
|
+
const sentMessageIds = new Set();
|
|
22
|
+
/** Current connection state exposed to MCP tools */
|
|
23
|
+
let status = {
|
|
24
|
+
connected: false,
|
|
25
|
+
phoneNumber: null,
|
|
26
|
+
selfJid: null,
|
|
27
|
+
selfLid: null,
|
|
28
|
+
awaitingQR: false,
|
|
29
|
+
};
|
|
30
|
+
/** Reconnect scheduling */
|
|
31
|
+
let reconnectTimer = null;
|
|
32
|
+
let reconnectAttempts = 0;
|
|
33
|
+
const MAX_RECONNECT_DELAY_MS = 60_000;
|
|
34
|
+
/** Set to true on loggedOut — stops all reconnect attempts */
|
|
35
|
+
let permanentlyLoggedOut = false;
|
|
36
|
+
/** Resolve when the first connection attempt completes (either open or qr shown) */
|
|
37
|
+
let initResolve = null;
|
|
38
|
+
// --- Internal helpers --------------------------------------------------------
|
|
39
|
+
/**
|
|
40
|
+
* Silenced Pino logger — CRITICAL: Baileys must not write to stdout.
|
|
41
|
+
* Stdout is the MCP JSON-RPC transport; any non-JSON output breaks the protocol.
|
|
42
|
+
*/
|
|
43
|
+
const logger = pino({ level: "silent" });
|
|
44
|
+
/**
|
|
45
|
+
* Convert common Markdown syntax to WhatsApp formatting codes.
|
|
46
|
+
*
|
|
47
|
+
* **bold** -> *bold* (WhatsApp bold)
|
|
48
|
+
* *italic* -> _italic_ (WhatsApp italic)
|
|
49
|
+
* `code` -> ```code``` (WhatsApp monospace)
|
|
50
|
+
*/
|
|
51
|
+
function markdownToWhatsApp(text) {
|
|
52
|
+
return text
|
|
53
|
+
.replace(/\*\*(.+?)\*\*/gs, "*$1*")
|
|
54
|
+
.replace(/(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)/gs, "_$1_")
|
|
55
|
+
.replace(/`([^`]+)`/g, "```$1```");
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Schedule a reconnection attempt with exponential backoff.
|
|
59
|
+
* Does nothing if permanently logged out or a timer is already pending.
|
|
60
|
+
*/
|
|
61
|
+
function scheduleReconnect() {
|
|
62
|
+
if (permanentlyLoggedOut)
|
|
63
|
+
return;
|
|
64
|
+
if (reconnectTimer)
|
|
65
|
+
return; // already scheduled
|
|
66
|
+
reconnectAttempts++;
|
|
67
|
+
const delay = Math.min(1_000 * Math.pow(2, reconnectAttempts - 1), MAX_RECONNECT_DELAY_MS);
|
|
68
|
+
process.stderr.write(`[whazaa] Reconnecting in ${delay / 1_000}s (attempt ${reconnectAttempts})...\n`);
|
|
69
|
+
reconnectTimer = setTimeout(() => {
|
|
70
|
+
reconnectTimer = null;
|
|
71
|
+
connect().catch((err) => {
|
|
72
|
+
process.stderr.write(`[whazaa] Reconnect error: ${err}\n`);
|
|
73
|
+
});
|
|
74
|
+
}, delay);
|
|
75
|
+
}
|
|
76
|
+
// --- Core connection ---------------------------------------------------------
|
|
77
|
+
/**
|
|
78
|
+
* Open (or re-open) the Baileys WebSocket connection.
|
|
79
|
+
* On first run with no saved credentials, Baileys emits a QR code which we
|
|
80
|
+
* display on stderr. After the user scans it, credentials are saved and all
|
|
81
|
+
* subsequent runs connect automatically without showing a QR.
|
|
82
|
+
*/
|
|
83
|
+
async function connect() {
|
|
84
|
+
const authDir = resolveAuthDir();
|
|
85
|
+
const { state: authState, saveCreds } = await useMultiFileAuthState(authDir);
|
|
86
|
+
const { version } = await fetchLatestBaileysVersion();
|
|
87
|
+
sock = makeWASocket({
|
|
88
|
+
auth: {
|
|
89
|
+
creds: authState.creds,
|
|
90
|
+
keys: makeCacheableSignalKeyStore(authState.keys, logger),
|
|
91
|
+
},
|
|
92
|
+
version,
|
|
93
|
+
// Browser string shown in WhatsApp's linked devices list
|
|
94
|
+
browser: ["Whazaa", "cli", "0.1.0"],
|
|
95
|
+
printQRInTerminal: false, // We handle QR display ourselves on stderr
|
|
96
|
+
syncFullHistory: false,
|
|
97
|
+
markOnlineOnConnect: false,
|
|
98
|
+
logger,
|
|
99
|
+
});
|
|
100
|
+
// Persist credentials whenever they are updated
|
|
101
|
+
sock.ev.on("creds.update", saveCreds);
|
|
102
|
+
// Handle connection lifecycle
|
|
103
|
+
sock.ev.on("connection.update", ({ connection, lastDisconnect, qr }) => {
|
|
104
|
+
// Display QR code when pairing is needed
|
|
105
|
+
if (qr) {
|
|
106
|
+
status.awaitingQR = true;
|
|
107
|
+
printQR(qr);
|
|
108
|
+
// Resolve init promise so MCP server starts up even while awaiting scan
|
|
109
|
+
if (initResolve) {
|
|
110
|
+
initResolve();
|
|
111
|
+
initResolve = null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (connection === "open") {
|
|
115
|
+
status.awaitingQR = false;
|
|
116
|
+
status.connected = true;
|
|
117
|
+
reconnectAttempts = 0;
|
|
118
|
+
// Derive phone number and LID from the authenticated user
|
|
119
|
+
const jid = sock?.user?.id ?? null;
|
|
120
|
+
if (jid) {
|
|
121
|
+
// JID format: "1234567890:12@s.whatsapp.net" or "1234567890@s.whatsapp.net"
|
|
122
|
+
const number = jid.split(":")[0].split("@")[0];
|
|
123
|
+
status.phoneNumber = number;
|
|
124
|
+
status.selfJid = `${number}@s.whatsapp.net`;
|
|
125
|
+
}
|
|
126
|
+
// Capture LID (Linked Identity) — self-chat uses this format
|
|
127
|
+
const lid = sock?.user?.lid;
|
|
128
|
+
if (lid) {
|
|
129
|
+
status.selfLid = lid;
|
|
130
|
+
}
|
|
131
|
+
process.stderr.write(`[whazaa] Connected. Phone: +${status.phoneNumber ?? "unknown"}\n`);
|
|
132
|
+
// Resolve init promise on successful connection
|
|
133
|
+
if (initResolve) {
|
|
134
|
+
initResolve();
|
|
135
|
+
initResolve = null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (connection === "close") {
|
|
139
|
+
status.connected = false;
|
|
140
|
+
const statusCode = lastDisconnect?.error?.output
|
|
141
|
+
?.statusCode;
|
|
142
|
+
if (statusCode === DisconnectReason.loggedOut) {
|
|
143
|
+
// Auth is invalid (401). Stop reconnecting — user must re-pair.
|
|
144
|
+
permanentlyLoggedOut = true;
|
|
145
|
+
process.stderr.write("[whazaa] Logged out (401). Run whatsapp_login to re-pair.\n");
|
|
146
|
+
if (initResolve) {
|
|
147
|
+
initResolve();
|
|
148
|
+
initResolve = null;
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
process.stderr.write("[whazaa] Connection closed. Will reconnect...\n");
|
|
153
|
+
scheduleReconnect();
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
// Handle incoming messages — only self-chat messages are queued
|
|
157
|
+
sock.ev.on("messages.upsert", ({ type, messages }) => {
|
|
158
|
+
// Accept both "notify" (real-time) and "append" (history sync / self-sent)
|
|
159
|
+
// Only skip "set" which is bulk history sync on first connect
|
|
160
|
+
for (const msg of messages) {
|
|
161
|
+
const remoteJid = msg.key?.remoteJid;
|
|
162
|
+
const body = msg.message?.conversation ??
|
|
163
|
+
msg.message?.extendedTextMessage?.text ??
|
|
164
|
+
null;
|
|
165
|
+
// Filter to self-chat only: match selfJid, selfLid, or phone number prefix
|
|
166
|
+
// Strip device suffix (e.g. ":6" or ":12") for comparison
|
|
167
|
+
const stripDevice = (jid) => jid.replace(/:\d+@/, "@");
|
|
168
|
+
const selfNumber = status.phoneNumber;
|
|
169
|
+
const selfLid = status.selfLid ? stripDevice(status.selfLid) : null;
|
|
170
|
+
const remoteJidNorm = remoteJid ? stripDevice(remoteJid) : null;
|
|
171
|
+
const isSelfChat = (status.selfJid && remoteJidNorm === stripDevice(status.selfJid)) ||
|
|
172
|
+
(selfLid && remoteJidNorm === selfLid) ||
|
|
173
|
+
(selfNumber && remoteJid?.startsWith(selfNumber));
|
|
174
|
+
if (!isSelfChat) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
// Deduplicate: skip messages sent by this process
|
|
178
|
+
const msgId = msg.key?.id;
|
|
179
|
+
if (msgId && sentMessageIds.has(msgId)) {
|
|
180
|
+
sentMessageIds.delete(msgId);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (body) {
|
|
184
|
+
messageQueue.push({
|
|
185
|
+
body,
|
|
186
|
+
timestamp: Number(msg.messageTimestamp) * 1_000,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
// --- Public API --------------------------------------------------------------
|
|
193
|
+
/**
|
|
194
|
+
* Initialize the WhatsApp connection.
|
|
195
|
+
* Resolves once the connection is open OR a QR code has been emitted
|
|
196
|
+
* (so the MCP server can start handling tool calls immediately).
|
|
197
|
+
*/
|
|
198
|
+
export async function initialize() {
|
|
199
|
+
await new Promise((resolve) => {
|
|
200
|
+
initResolve = resolve;
|
|
201
|
+
connect().catch((err) => {
|
|
202
|
+
process.stderr.write(`[whazaa] Init error: ${err}\n`);
|
|
203
|
+
resolve(); // Don't block MCP startup on connection failure
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Trigger a new QR pairing flow.
|
|
209
|
+
* Closes the current socket (if any) and reconnects with fresh state
|
|
210
|
+
* so Baileys emits a new QR code.
|
|
211
|
+
*/
|
|
212
|
+
export async function triggerLogin() {
|
|
213
|
+
permanentlyLoggedOut = false;
|
|
214
|
+
reconnectAttempts = 0;
|
|
215
|
+
if (reconnectTimer) {
|
|
216
|
+
clearTimeout(reconnectTimer);
|
|
217
|
+
reconnectTimer = null;
|
|
218
|
+
}
|
|
219
|
+
if (sock) {
|
|
220
|
+
try {
|
|
221
|
+
sock.end(undefined);
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
// Ignore errors on close
|
|
225
|
+
}
|
|
226
|
+
sock = null;
|
|
227
|
+
}
|
|
228
|
+
status = {
|
|
229
|
+
connected: false,
|
|
230
|
+
phoneNumber: null,
|
|
231
|
+
selfJid: null,
|
|
232
|
+
selfLid: null,
|
|
233
|
+
awaitingQR: false,
|
|
234
|
+
};
|
|
235
|
+
// Reconnect — Baileys will emit a QR if no creds are present
|
|
236
|
+
await connect();
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Return the current connection status snapshot.
|
|
240
|
+
*/
|
|
241
|
+
export function getStatus() {
|
|
242
|
+
return { ...status };
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Send a message to the authenticated user's own WhatsApp number.
|
|
246
|
+
* Converts Markdown to WhatsApp formatting before sending.
|
|
247
|
+
*
|
|
248
|
+
* @throws If not connected or socket is unavailable.
|
|
249
|
+
*/
|
|
250
|
+
export async function sendMessage(message) {
|
|
251
|
+
if (!sock) {
|
|
252
|
+
throw new Error("WhatsApp socket not initialized. Is the connection open?");
|
|
253
|
+
}
|
|
254
|
+
if (!status.connected) {
|
|
255
|
+
throw new Error("WhatsApp is not connected. Check status with whatsapp_status.");
|
|
256
|
+
}
|
|
257
|
+
if (!status.selfJid) {
|
|
258
|
+
throw new Error("Self JID not yet known. Wait for connection to fully open.");
|
|
259
|
+
}
|
|
260
|
+
const text = markdownToWhatsApp(message);
|
|
261
|
+
const result = await sock.sendMessage(status.selfJid, { text });
|
|
262
|
+
// Register the sent message ID for deduplication
|
|
263
|
+
if (result?.key?.id) {
|
|
264
|
+
const id = result.key.id;
|
|
265
|
+
sentMessageIds.add(id);
|
|
266
|
+
// Auto-expire after 30 seconds to prevent unbounded growth
|
|
267
|
+
setTimeout(() => {
|
|
268
|
+
sentMessageIds.delete(id);
|
|
269
|
+
}, 30_000);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Drain and return all queued incoming messages, then clear the queue.
|
|
274
|
+
*/
|
|
275
|
+
export function drainMessages() {
|
|
276
|
+
const snapshot = [...messageQueue];
|
|
277
|
+
messageQueue.length = 0;
|
|
278
|
+
return snapshot;
|
|
279
|
+
}
|
|
280
|
+
// --- Graceful shutdown -------------------------------------------------------
|
|
281
|
+
function shutdown(signal) {
|
|
282
|
+
process.stderr.write(`[whazaa] Received ${signal}. Shutting down...\n`);
|
|
283
|
+
if (reconnectTimer) {
|
|
284
|
+
clearTimeout(reconnectTimer);
|
|
285
|
+
reconnectTimer = null;
|
|
286
|
+
}
|
|
287
|
+
if (sock) {
|
|
288
|
+
try {
|
|
289
|
+
sock.end(undefined);
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
// Ignore
|
|
293
|
+
}
|
|
294
|
+
sock = null;
|
|
295
|
+
}
|
|
296
|
+
process.exit(0);
|
|
297
|
+
}
|
|
298
|
+
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
299
|
+
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
300
|
+
//# sourceMappingURL=whatsapp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whatsapp.js","sourceRoot":"","sources":["../src/whatsapp.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "whazaa",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "WhatsApp MCP server for Claude Code — talk to Claude from your phone via WhatsApp self-chat",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"whazaa": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"dev": "tsc --watch",
|
|
18
|
+
"start": "node dist/index.js",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"mcp",
|
|
23
|
+
"whatsapp",
|
|
24
|
+
"claude",
|
|
25
|
+
"claude-code",
|
|
26
|
+
"mcp-server",
|
|
27
|
+
"baileys",
|
|
28
|
+
"chatbot",
|
|
29
|
+
"ai"
|
|
30
|
+
],
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"author": "Matthias Nott",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/mnott/Whazaa"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
42
|
+
"@whiskeysockets/baileys": "^7.0.0-rc.9",
|
|
43
|
+
"pino": "^9.0.0",
|
|
44
|
+
"qrcode-terminal": "^0.12.0",
|
|
45
|
+
"zod": "^3.23.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^22.0.0",
|
|
49
|
+
"@types/qrcode-terminal": "^0.12.2",
|
|
50
|
+
"typescript": "^5.0.0"
|
|
51
|
+
}
|
|
52
|
+
}
|