clawbee 2.1.0 ā 2.2.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/README.md +65 -45
- package/bin/clawbee.js +341 -107
- package/lib/ai/provider.js +275 -112
- package/lib/index.js +24 -2
- package/lib/integrations/whatsapp.js +247 -0
- package/package.json +11 -4
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClawBee WhatsApp Integration
|
|
3
|
+
* Real working WhatsApp integration using whatsapp-web.js
|
|
4
|
+
* Connects via QR code scanning
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const qrcode = require('qrcode-terminal');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('os').homedir();
|
|
10
|
+
|
|
11
|
+
// Session directory for persistent login
|
|
12
|
+
const SESSION_DIR = `${path}/.local/share/clawbee/integrations/whatsapp`;
|
|
13
|
+
|
|
14
|
+
class WhatsAppBot {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.client = null;
|
|
17
|
+
this.isReady = false;
|
|
18
|
+
this.messageHandler = null;
|
|
19
|
+
this.qrHandler = null;
|
|
20
|
+
this.statusHandler = null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Initialize the WhatsApp client
|
|
25
|
+
*/
|
|
26
|
+
async initialize() {
|
|
27
|
+
// Dynamic import for whatsapp-web.js (ES module compatibility)
|
|
28
|
+
const { Client, LocalAuth } = await import('whatsapp-web.js');
|
|
29
|
+
|
|
30
|
+
// Ensure session directory exists
|
|
31
|
+
if (!fs.existsSync(SESSION_DIR)) {
|
|
32
|
+
fs.mkdirSync(SESSION_DIR, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
this.client = new Client({
|
|
36
|
+
authStrategy: new LocalAuth({
|
|
37
|
+
dataPath: SESSION_DIR
|
|
38
|
+
}),
|
|
39
|
+
puppeteer: {
|
|
40
|
+
headless: true,
|
|
41
|
+
args: [
|
|
42
|
+
'--no-sandbox',
|
|
43
|
+
'--disable-setuid-sandbox',
|
|
44
|
+
'--disable-dev-shm-usage',
|
|
45
|
+
'--disable-accelerated-2d-canvas',
|
|
46
|
+
'--no-first-run',
|
|
47
|
+
'--no-zygote',
|
|
48
|
+
'--disable-gpu'
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Start the WhatsApp bot
|
|
58
|
+
* @param {Function} messageHandler - Function to handle incoming messages
|
|
59
|
+
* @param {Object} options - Additional options
|
|
60
|
+
*/
|
|
61
|
+
async start(messageHandler, options = {}) {
|
|
62
|
+
if (!this.client) {
|
|
63
|
+
await this.initialize();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.messageHandler = messageHandler;
|
|
67
|
+
this.qrHandler = options.onQR || null;
|
|
68
|
+
this.statusHandler = options.onStatus || null;
|
|
69
|
+
|
|
70
|
+
// QR Code event - display for scanning
|
|
71
|
+
this.client.on('qr', (qr) => {
|
|
72
|
+
console.log('\nš Scan this QR code with WhatsApp:\n');
|
|
73
|
+
qrcode.generate(qr, { small: true });
|
|
74
|
+
console.log('\nOpen WhatsApp > Settings > Linked Devices > Link a Device\n');
|
|
75
|
+
|
|
76
|
+
if (this.qrHandler) {
|
|
77
|
+
this.qrHandler(qr);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Ready event - successfully connected
|
|
82
|
+
this.client.on('ready', () => {
|
|
83
|
+
this.isReady = true;
|
|
84
|
+
console.log('š WhatsApp client is ready!');
|
|
85
|
+
|
|
86
|
+
if (this.statusHandler) {
|
|
87
|
+
this.statusHandler('ready', 'WhatsApp connected successfully');
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Authentication success
|
|
92
|
+
this.client.on('authenticated', () => {
|
|
93
|
+
console.log('š WhatsApp authenticated');
|
|
94
|
+
|
|
95
|
+
if (this.statusHandler) {
|
|
96
|
+
this.statusHandler('authenticated', 'Authentication successful');
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Authentication failure
|
|
101
|
+
this.client.on('auth_failure', (msg) => {
|
|
102
|
+
console.error('š WhatsApp authentication failed:', msg);
|
|
103
|
+
|
|
104
|
+
if (this.statusHandler) {
|
|
105
|
+
this.statusHandler('auth_failure', msg);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Disconnected event
|
|
110
|
+
this.client.on('disconnected', (reason) => {
|
|
111
|
+
this.isReady = false;
|
|
112
|
+
console.log('š WhatsApp disconnected:', reason);
|
|
113
|
+
|
|
114
|
+
if (this.statusHandler) {
|
|
115
|
+
this.statusHandler('disconnected', reason);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Message received event
|
|
120
|
+
this.client.on('message', async (message) => {
|
|
121
|
+
// Skip status updates and own messages
|
|
122
|
+
if (message.isStatus || message.fromMe) return;
|
|
123
|
+
|
|
124
|
+
const chat = await message.getChat();
|
|
125
|
+
const contact = await message.getContact();
|
|
126
|
+
|
|
127
|
+
const context = {
|
|
128
|
+
platform: 'whatsapp',
|
|
129
|
+
chatId: message.from,
|
|
130
|
+
chatName: chat.name || contact.pushname || contact.number,
|
|
131
|
+
username: contact.pushname || contact.name || contact.number,
|
|
132
|
+
isGroup: chat.isGroup,
|
|
133
|
+
messageId: message.id._serialized,
|
|
134
|
+
timestamp: message.timestamp,
|
|
135
|
+
hasMedia: message.hasMedia
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Only process text messages for now
|
|
139
|
+
if (message.body && this.messageHandler) {
|
|
140
|
+
try {
|
|
141
|
+
// Show typing indicator
|
|
142
|
+
await chat.sendStateTyping();
|
|
143
|
+
|
|
144
|
+
// Get AI response
|
|
145
|
+
const response = await this.messageHandler(message.body, context);
|
|
146
|
+
|
|
147
|
+
// Send response
|
|
148
|
+
await message.reply(response);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error('WhatsApp message handling error:', error);
|
|
151
|
+
await message.reply(`ā Error: ${error.message}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Initialize client
|
|
157
|
+
console.log('š Initializing WhatsApp connection...');
|
|
158
|
+
await this.client.initialize();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Send a message to a specific chat
|
|
163
|
+
* @param {string} chatId - WhatsApp chat ID (number@c.us for contacts, number-id@g.us for groups)
|
|
164
|
+
* @param {string} content - Message content
|
|
165
|
+
*/
|
|
166
|
+
async sendMessage(chatId, content) {
|
|
167
|
+
if (!this.isReady) {
|
|
168
|
+
throw new Error('WhatsApp client is not ready');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return this.client.sendMessage(chatId, content);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Get all chats
|
|
176
|
+
*/
|
|
177
|
+
async getChats() {
|
|
178
|
+
if (!this.isReady) {
|
|
179
|
+
throw new Error('WhatsApp client is not ready');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return this.client.getChats();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Get contact info
|
|
187
|
+
* @param {string} contactId - Contact ID
|
|
188
|
+
*/
|
|
189
|
+
async getContact(contactId) {
|
|
190
|
+
if (!this.isReady) {
|
|
191
|
+
throw new Error('WhatsApp client is not ready');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return this.client.getContactById(contactId);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get current connection state
|
|
199
|
+
*/
|
|
200
|
+
getState() {
|
|
201
|
+
return {
|
|
202
|
+
isReady: this.isReady,
|
|
203
|
+
state: this.client?.info?.wid ? 'connected' : 'disconnected',
|
|
204
|
+
phoneNumber: this.client?.info?.wid?.user || null,
|
|
205
|
+
platform: this.client?.info?.platform || null
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Logout and clear session
|
|
211
|
+
*/
|
|
212
|
+
async logout() {
|
|
213
|
+
if (this.client) {
|
|
214
|
+
await this.client.logout();
|
|
215
|
+
this.isReady = false;
|
|
216
|
+
|
|
217
|
+
// Clear session files
|
|
218
|
+
if (fs.existsSync(SESSION_DIR)) {
|
|
219
|
+
fs.rmSync(SESSION_DIR, { recursive: true, force: true });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Destroy client connection
|
|
226
|
+
*/
|
|
227
|
+
async stop() {
|
|
228
|
+
if (this.client) {
|
|
229
|
+
await this.client.destroy();
|
|
230
|
+
this.client = null;
|
|
231
|
+
this.isReady = false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Format phone number to WhatsApp ID
|
|
238
|
+
* @param {string} phoneNumber - Phone number with country code
|
|
239
|
+
* @returns {string} WhatsApp chat ID
|
|
240
|
+
*/
|
|
241
|
+
function formatWhatsAppId(phoneNumber) {
|
|
242
|
+
// Remove any non-digit characters
|
|
243
|
+
const cleaned = phoneNumber.replace(/\D/g, '');
|
|
244
|
+
return `${cleaned}@c.us`;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
module.exports = { WhatsAppBot, formatWhatsAppId };
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clawbee",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "ClawBee - Your Personal AI, Endless Possibilities. AI assistant that runs locally and connects to any chat app.",
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "ClawBee - Your Personal AI, Endless Possibilities. AI assistant that runs locally and connects to any chat app. Supports Emergent Universal Key for GPT-5, Claude 4, and Gemini 3.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"clawbee": "./bin/clawbee.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node bin/clawbee.js start",
|
|
11
|
+
"chat": "node bin/clawbee.js chat",
|
|
11
12
|
"test": "node bin/clawbee.js status",
|
|
12
13
|
"postinstall": "echo 'š ClawBee installed! Run: clawbee onboard'"
|
|
13
14
|
},
|
|
@@ -30,12 +31,16 @@
|
|
|
30
31
|
"personal-assistant",
|
|
31
32
|
"openai",
|
|
32
33
|
"gpt",
|
|
34
|
+
"gpt-5",
|
|
33
35
|
"claude",
|
|
36
|
+
"claude-4",
|
|
34
37
|
"anthropic",
|
|
35
38
|
"gemini",
|
|
36
39
|
"ollama",
|
|
37
40
|
"bot",
|
|
38
|
-
"local-ai"
|
|
41
|
+
"local-ai",
|
|
42
|
+
"emergent",
|
|
43
|
+
"universal-key"
|
|
39
44
|
],
|
|
40
45
|
"author": "ClawBee Team <hello@clawbee.pro>",
|
|
41
46
|
"license": "MIT",
|
|
@@ -57,11 +62,13 @@
|
|
|
57
62
|
"dotenv": "^16.3.1",
|
|
58
63
|
"inquirer": "^8.2.6",
|
|
59
64
|
"ora": "^5.4.1",
|
|
65
|
+
"qrcode-terminal": "^0.12.0",
|
|
60
66
|
"yaml": "^2.3.4"
|
|
61
67
|
},
|
|
62
68
|
"optionalDependencies": {
|
|
63
69
|
"discord.js": "^14.14.1",
|
|
64
70
|
"@slack/bolt": "^3.17.0",
|
|
65
|
-
"puppeteer": "^21.6.0"
|
|
71
|
+
"puppeteer": "^21.6.0",
|
|
72
|
+
"whatsapp-web.js": "^1.34.6"
|
|
66
73
|
}
|
|
67
74
|
}
|