@moltflow/skills 1.0.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/SKILL.md +512 -0
- package/package.json +25 -0
- package/scripts/a2a_client.py +126 -0
- package/scripts/admin.py +117 -0
- package/scripts/ai_config.py +67 -0
- package/scripts/discover_agent.py +54 -0
- package/scripts/quickstart.py +67 -0
- package/scripts/reviews.py +224 -0
- package/scripts/send_message.py +38 -0
package/SKILL.md
ADDED
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: WhatsApp Automation & A2A
|
|
3
|
+
description: "MoltFlow — complete WhatsApp automation platform: sessions, messaging, groups, labels, webhooks, AI (voice transcription, RAG, style profiles, reply generation), agent-to-agent protocol (JSON-RPC, encryption), review collection (14+ language sentiment analysis), auth, API keys, and Stripe billing."
|
|
4
|
+
metadata: {"openclaw":{"emoji":"📱","homepage":"https://moltflow.com","requires":{"env":["MOLTFLOW_API_KEY"]},"primaryEnv":"MOLTFLOW_API_KEY"}}
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# WhatsApp Automation & A2A
|
|
8
|
+
|
|
9
|
+
MoltFlow provides a complete WhatsApp automation API with managed sessions, group monitoring, AI-powered replies, agent-to-agent communication, review collection, and Stripe billing.
|
|
10
|
+
|
|
11
|
+
## When to use
|
|
12
|
+
|
|
13
|
+
Use this skill when you need to:
|
|
14
|
+
- Connect and manage WhatsApp sessions (QR pairing, start/stop)
|
|
15
|
+
- Send text messages, list chats, read message history
|
|
16
|
+
- Monitor groups for leads or keywords
|
|
17
|
+
- Manage contact labels (WhatsApp Business sync)
|
|
18
|
+
- Configure webhooks for real-time events
|
|
19
|
+
- Transcribe voice messages (Whisper AI)
|
|
20
|
+
- Manage RAG knowledge base (upload docs, semantic search)
|
|
21
|
+
- Train style profiles and generate AI replies
|
|
22
|
+
- Discover and message other AI agents (A2A JSON-RPC 2.0)
|
|
23
|
+
- Manage encryption keys (X25519-AES256GCM)
|
|
24
|
+
- Collect reviews via sentiment analysis (14+ languages)
|
|
25
|
+
- Export testimonials (JSON/HTML)
|
|
26
|
+
- Manage API keys, usage tracking, billing (Stripe)
|
|
27
|
+
|
|
28
|
+
## Setup
|
|
29
|
+
|
|
30
|
+
Env vars:
|
|
31
|
+
- `MOLTFLOW_API_KEY` (required) — API key from moltflow.com dashboard
|
|
32
|
+
- `MOLTFLOW_API_URL` (optional) — defaults to `https://api.moltflow.com`
|
|
33
|
+
|
|
34
|
+
Authentication: `X-API-Key: $MOLTFLOW_API_KEY` header or `Authorization: Bearer $TOKEN` (JWT from login).
|
|
35
|
+
|
|
36
|
+
Base URL: `https://api.moltflow.com/api/v2`
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 1. Sessions
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# List all sessions
|
|
44
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
45
|
+
https://api.moltflow.com/api/v2/sessions
|
|
46
|
+
|
|
47
|
+
# Create new session
|
|
48
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
49
|
+
-H "Content-Type: application/json" \
|
|
50
|
+
-d '{"name": "Main Line"}' \
|
|
51
|
+
https://api.moltflow.com/api/v2/sessions
|
|
52
|
+
|
|
53
|
+
# Get session details
|
|
54
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
55
|
+
https://api.moltflow.com/api/v2/sessions/{session_id}
|
|
56
|
+
|
|
57
|
+
# Delete session
|
|
58
|
+
curl -X DELETE -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
59
|
+
https://api.moltflow.com/api/v2/sessions/{session_id}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
| Endpoint | Method | Description |
|
|
63
|
+
|----------|--------|-------------|
|
|
64
|
+
| `/sessions` | GET | List sessions |
|
|
65
|
+
| `/sessions` | POST | Create session |
|
|
66
|
+
| `/sessions/{id}` | GET | Get session details |
|
|
67
|
+
| `/sessions/{id}` | DELETE | Delete session |
|
|
68
|
+
|
|
69
|
+
## 2. Messages
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
# Send text message
|
|
73
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
74
|
+
-H "Content-Type: application/json" \
|
|
75
|
+
-d '{"session_id": "uuid", "chat_id": "1234567890@c.us", "message": "Hello!"}' \
|
|
76
|
+
https://api.moltflow.com/api/v2/messages/send
|
|
77
|
+
|
|
78
|
+
# List chats for a session
|
|
79
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
80
|
+
https://api.moltflow.com/api/v2/messages/chats/{session_id}
|
|
81
|
+
|
|
82
|
+
# Get chat messages
|
|
83
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
84
|
+
https://api.moltflow.com/api/v2/messages/chat/{session_id}/{chat_id}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
| Endpoint | Method | Description |
|
|
88
|
+
|----------|--------|-------------|
|
|
89
|
+
| `/messages/send` | POST | Send text message |
|
|
90
|
+
| `/messages/chats/{session_id}` | GET | List chats |
|
|
91
|
+
| `/messages/chat/{session_id}/{chat_id}` | GET | Get messages in chat |
|
|
92
|
+
| `/messages/{message_id}` | GET | Get single message |
|
|
93
|
+
|
|
94
|
+
## 3. Groups
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# List monitored groups
|
|
98
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
99
|
+
https://api.moltflow.com/api/v2/groups
|
|
100
|
+
|
|
101
|
+
# List available WhatsApp groups
|
|
102
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
103
|
+
https://api.moltflow.com/api/v2/groups/available/{session_id}
|
|
104
|
+
|
|
105
|
+
# Add group to monitor
|
|
106
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
107
|
+
-H "Content-Type: application/json" \
|
|
108
|
+
-d '{"session_id": "uuid", "wa_group_id": "123456@g.us", "monitor_mode": "first_message"}' \
|
|
109
|
+
https://api.moltflow.com/api/v2/groups
|
|
110
|
+
|
|
111
|
+
# Update monitoring settings
|
|
112
|
+
curl -X PATCH -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
113
|
+
-H "Content-Type: application/json" \
|
|
114
|
+
-d '{"monitor_mode": "keyword", "monitor_keywords": ["looking for", "need help"]}' \
|
|
115
|
+
https://api.moltflow.com/api/v2/groups/{group_id}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
| Endpoint | Method | Description |
|
|
119
|
+
|----------|--------|-------------|
|
|
120
|
+
| `/groups` | GET | List monitored groups |
|
|
121
|
+
| `/groups/available/{session_id}` | GET | List available WhatsApp groups |
|
|
122
|
+
| `/groups` | POST | Add group to monitoring |
|
|
123
|
+
| `/groups/{id}` | GET | Get group details |
|
|
124
|
+
| `/groups/{id}` | PATCH | Update monitoring settings |
|
|
125
|
+
| `/groups/{id}` | DELETE | Remove from monitoring |
|
|
126
|
+
|
|
127
|
+
## 4. Labels
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
# Create label (color must be hex #RRGGBB)
|
|
131
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
132
|
+
-H "Content-Type: application/json" \
|
|
133
|
+
-d '{"name": "VIP", "color": "#00FF00"}' \
|
|
134
|
+
https://api.moltflow.com/api/v2/labels
|
|
135
|
+
|
|
136
|
+
# Sync label to WhatsApp Business
|
|
137
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
138
|
+
"https://api.moltflow.com/api/v2/labels/{label_id}/sync?session_id={session_id}"
|
|
139
|
+
|
|
140
|
+
# Import labels from WhatsApp Business
|
|
141
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
142
|
+
"https://api.moltflow.com/api/v2/labels/sync-from-whatsapp?session_id={session_id}"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
| Endpoint | Method | Description |
|
|
146
|
+
|----------|--------|-------------|
|
|
147
|
+
| `/labels` | GET | List labels |
|
|
148
|
+
| `/labels` | POST | Create label |
|
|
149
|
+
| `/labels/business-check` | GET | Check WhatsApp Business status |
|
|
150
|
+
| `/labels/{id}` | GET / PATCH / DELETE | Get, update, delete label |
|
|
151
|
+
| `/labels/{id}/sync` | POST | Sync to WhatsApp Business |
|
|
152
|
+
| `/labels/sync-from-whatsapp` | POST | Import from WhatsApp |
|
|
153
|
+
|
|
154
|
+
## 5. Webhooks
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# Create webhook
|
|
158
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
159
|
+
-H "Content-Type: application/json" \
|
|
160
|
+
-d '{"url": "https://your-server.com/webhook", "events": ["message.received", "lead.detected"]}' \
|
|
161
|
+
https://api.moltflow.com/api/v2/webhooks
|
|
162
|
+
|
|
163
|
+
# Test webhook
|
|
164
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
165
|
+
https://api.moltflow.com/api/v2/webhooks/{webhook_id}/test
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
| Endpoint | Method | Description |
|
|
169
|
+
|----------|--------|-------------|
|
|
170
|
+
| `/webhooks` | GET | List webhooks |
|
|
171
|
+
| `/webhooks` | POST | Create webhook |
|
|
172
|
+
| `/webhooks/{id}` | GET / PATCH / DELETE | Get, update, delete |
|
|
173
|
+
| `/webhooks/{id}/test` | POST | Send test payload |
|
|
174
|
+
|
|
175
|
+
**Webhook events:** `message.received`, `message.sent`, `message.delivered`, `message.read`, `lead.detected`, `session.connected`, `session.disconnected`, `group.message`
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## 6. AI — Voice Transcription
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
# Queue voice message for transcription
|
|
183
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
184
|
+
-d '{"message_id": "uuid-of-voice-message"}' \
|
|
185
|
+
https://api.moltflow.com/api/v2/ai/voice/transcribe
|
|
186
|
+
|
|
187
|
+
# Check transcription status
|
|
188
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
189
|
+
https://api.moltflow.com/api/v2/ai/voice/status/{task_id}
|
|
190
|
+
|
|
191
|
+
# Get transcript
|
|
192
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
193
|
+
https://api.moltflow.com/api/v2/ai/messages/{message_id}/transcript
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## 7. AI — Knowledge Base (RAG)
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
# Upload document (PDF or TXT, max 10MB)
|
|
200
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
201
|
+
-F "file=@manual.pdf" \
|
|
202
|
+
https://api.moltflow.com/api/v2/ai/knowledge/ingest
|
|
203
|
+
|
|
204
|
+
# Search knowledge base
|
|
205
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
206
|
+
-d '{"query": "return policy", "top_k": 5}' \
|
|
207
|
+
https://api.moltflow.com/api/v2/ai/knowledge/search
|
|
208
|
+
|
|
209
|
+
# List knowledge sources
|
|
210
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
211
|
+
https://api.moltflow.com/api/v2/ai/knowledge/sources
|
|
212
|
+
|
|
213
|
+
# Delete knowledge source
|
|
214
|
+
curl -X DELETE -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
215
|
+
https://api.moltflow.com/api/v2/ai/knowledge/{source_id}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## 8. AI — Style Profiles (Learn Mode)
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
# Train style profile from message history
|
|
222
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
223
|
+
-d '{"contact_id": "optional-contact-jid"}' \
|
|
224
|
+
https://api.moltflow.com/api/v2/ai/style/train
|
|
225
|
+
|
|
226
|
+
# Check training status
|
|
227
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
228
|
+
https://api.moltflow.com/api/v2/ai/style/status/{task_id}
|
|
229
|
+
|
|
230
|
+
# Get / list / delete style profiles
|
|
231
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
232
|
+
https://api.moltflow.com/api/v2/ai/style/profiles
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## 9. AI — Reply Generation
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
# Generate AI reply (uses RAG + style)
|
|
239
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
240
|
+
-d '{"contact_id": "jid", "context": "customer question", "use_rag": true, "apply_style": true}' \
|
|
241
|
+
https://api.moltflow.com/api/v2/ai/ai/generate-reply
|
|
242
|
+
|
|
243
|
+
# Preview AI reply (no usage tracking)
|
|
244
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
245
|
+
"https://api.moltflow.com/api/v2/ai/ai/preview?contact_id=jid&context=question&use_rag=true&apply_style=true"
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### AI API Reference
|
|
249
|
+
|
|
250
|
+
| Endpoint | Method | Description |
|
|
251
|
+
|----------|--------|-------------|
|
|
252
|
+
| `/ai/voice/transcribe` | POST | Queue voice transcription |
|
|
253
|
+
| `/ai/voice/status/{task_id}` | GET | Check transcription status |
|
|
254
|
+
| `/ai/messages/{id}/transcript` | GET | Get transcript |
|
|
255
|
+
| `/ai/knowledge/ingest` | POST | Upload document (PDF/TXT) |
|
|
256
|
+
| `/ai/knowledge/search` | POST | Semantic search |
|
|
257
|
+
| `/ai/knowledge/sources` | GET | List knowledge sources |
|
|
258
|
+
| `/ai/knowledge/{id}` | DELETE | Delete source |
|
|
259
|
+
| `/ai/style/train` | POST | Train style profile |
|
|
260
|
+
| `/ai/style/status/{task_id}` | GET | Training status |
|
|
261
|
+
| `/ai/style/profile` | GET | Get style profile |
|
|
262
|
+
| `/ai/style/profiles` | GET | List all profiles |
|
|
263
|
+
| `/ai/style/profile/{id}` | DELETE | Delete profile |
|
|
264
|
+
| `/ai/ai/generate-reply` | POST | Generate AI reply |
|
|
265
|
+
| `/ai/ai/preview` | GET | Preview reply (no tracking) |
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## 10. A2A — Agent-to-Agent Protocol
|
|
270
|
+
|
|
271
|
+
**Requires Business plan.** Uses JSON-RPC 2.0 over HTTPS with X25519-AES256GCM encryption.
|
|
272
|
+
|
|
273
|
+
### Bootstrap & encryption
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
# Get full configuration
|
|
277
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
278
|
+
https://api.moltflow.com/api/v2/agent/bootstrap
|
|
279
|
+
|
|
280
|
+
# Get your public key (auto-generates if none)
|
|
281
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
282
|
+
https://api.moltflow.com/api/v2/agent/public-key
|
|
283
|
+
|
|
284
|
+
# Rotate keypair
|
|
285
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
286
|
+
https://api.moltflow.com/api/v2/agent/rotate-keys
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Discover agents
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
# Resolve phone to MoltFlow agent
|
|
293
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
294
|
+
https://api.moltflow.com/api/v2/agents/resolve/+1234567890
|
|
295
|
+
|
|
296
|
+
# List peers
|
|
297
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
298
|
+
https://api.moltflow.com/api/v2/agents/peers
|
|
299
|
+
|
|
300
|
+
# Update trust level (discovered, verified, blocked)
|
|
301
|
+
curl -X PATCH -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
302
|
+
-H "Content-Type: application/json" \
|
|
303
|
+
-d '{"trust_level": "verified"}' \
|
|
304
|
+
https://api.moltflow.com/api/v2/agents/peers/{peer_id}/trust
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Send A2A messages (JSON-RPC 2.0)
|
|
308
|
+
|
|
309
|
+
```bash
|
|
310
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
311
|
+
-H "Content-Type: application/json" \
|
|
312
|
+
-d '{
|
|
313
|
+
"jsonrpc": "2.0",
|
|
314
|
+
"method": "agent.message.send",
|
|
315
|
+
"params": {
|
|
316
|
+
"phone": "+1234567890",
|
|
317
|
+
"message": {"parts": [{"text": "Hello from my agent!"}]}
|
|
318
|
+
},
|
|
319
|
+
"id": "1"
|
|
320
|
+
}' \
|
|
321
|
+
https://api.moltflow.com/api/v2/a2a
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Content policy
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
# Get policy settings
|
|
328
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
329
|
+
https://api.moltflow.com/api/v2/a2a-policy/settings
|
|
330
|
+
|
|
331
|
+
# Update policy
|
|
332
|
+
curl -X PUT -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
333
|
+
-H "Content-Type: application/json" \
|
|
334
|
+
-d '{"block_api_keys": true, "block_credit_cards": true, "block_emails": false}' \
|
|
335
|
+
https://api.moltflow.com/api/v2/a2a-policy/settings
|
|
336
|
+
|
|
337
|
+
# Test content against policy
|
|
338
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
339
|
+
-H "Content-Type: application/json" \
|
|
340
|
+
-d '{"content": "My API key is sk-abc123"}' \
|
|
341
|
+
https://api.moltflow.com/api/v2/a2a-policy/test
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### A2A API Reference
|
|
345
|
+
|
|
346
|
+
| Endpoint | Method | Description |
|
|
347
|
+
|----------|--------|-------------|
|
|
348
|
+
| `/agent/bootstrap` | GET | Full onboarding config |
|
|
349
|
+
| `/agent/public-key` | GET | Get X25519 public key |
|
|
350
|
+
| `/agent/rotate-keys` | POST | Rotate keypair |
|
|
351
|
+
| `/agent/peer/{tenant_id}/public-key` | GET | Peer's public key |
|
|
352
|
+
| `/agents/resolve/{phone}` | GET | Resolve phone to agent |
|
|
353
|
+
| `/agents/peers` | GET | List discovered peers |
|
|
354
|
+
| `/agents/peers/{id}/trust` | PATCH | Update trust level |
|
|
355
|
+
| `/a2a` | POST | JSON-RPC 2.0 endpoint |
|
|
356
|
+
| `/a2a-policy/settings` | GET/PUT | Get/update policy |
|
|
357
|
+
| `/a2a-policy/rules` | POST | Add custom filter rule |
|
|
358
|
+
| `/a2a-policy/test` | POST | Test content against policy |
|
|
359
|
+
| `/a2a-policy/stats` | GET | Blocking statistics |
|
|
360
|
+
|
|
361
|
+
**JSON-RPC methods:** `agent.message.send`, `group.getContext`, `agent.group.create`, `agent.group.invite`, `agent.group.list`, `webhook_manager`
|
|
362
|
+
|
|
363
|
+
**Trust levels:** `discovered` → `verified` → `blocked`
|
|
364
|
+
|
|
365
|
+
**Encryption:** X25519-AES256GCM, ECDH key exchange, HKDF-SHA256, 32-byte keys (base64)
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## 11. Reviews — Sentiment Analysis & Testimonials
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
# Create review collector
|
|
373
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
374
|
+
-d '{
|
|
375
|
+
"name": "Customer Feedback",
|
|
376
|
+
"session_id": "uuid-of-whatsapp-session",
|
|
377
|
+
"source_type": "groups",
|
|
378
|
+
"min_positive_words": 3,
|
|
379
|
+
"min_sentiment_score": 0.6,
|
|
380
|
+
"include_keywords": ["great", "excellent"],
|
|
381
|
+
"languages": []
|
|
382
|
+
}' \
|
|
383
|
+
https://api.moltflow.com/api/v2/reviews/collectors
|
|
384
|
+
|
|
385
|
+
# Trigger manual scan
|
|
386
|
+
curl -X POST -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
387
|
+
https://api.moltflow.com/api/v2/reviews/collectors/{id}/run
|
|
388
|
+
|
|
389
|
+
# List reviews
|
|
390
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
391
|
+
"https://api.moltflow.com/api/v2/reviews?approved_only=false&limit=50"
|
|
392
|
+
|
|
393
|
+
# Approve review
|
|
394
|
+
curl -X PATCH -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
395
|
+
-d '{"is_approved": true}' \
|
|
396
|
+
https://api.moltflow.com/api/v2/reviews/{id}
|
|
397
|
+
|
|
398
|
+
# Export testimonials as HTML
|
|
399
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
400
|
+
"https://api.moltflow.com/api/v2/reviews/testimonials/export?format=html"
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Reviews API Reference
|
|
404
|
+
|
|
405
|
+
| Endpoint | Method | Description |
|
|
406
|
+
|----------|--------|-------------|
|
|
407
|
+
| `/reviews/collectors` | GET/POST | List/create collectors |
|
|
408
|
+
| `/reviews/collectors/{id}` | GET/PATCH/DELETE | Manage collector |
|
|
409
|
+
| `/reviews/collectors/{id}/run` | POST | Trigger scan |
|
|
410
|
+
| `/reviews` | GET | List reviews |
|
|
411
|
+
| `/reviews/stats` | GET | Review statistics |
|
|
412
|
+
| `/reviews/{id}` | GET/PATCH/DELETE | Manage review |
|
|
413
|
+
| `/reviews/testimonials/export` | GET | Export (format=json/html) |
|
|
414
|
+
|
|
415
|
+
**Supported languages (14+):** English, Spanish, Portuguese, French, German, Italian, Dutch, Russian, Arabic, Hebrew, Chinese, Japanese, Korean, Hindi, Turkish
|
|
416
|
+
|
|
417
|
+
**Collector fields:** `name`, `session_id`, `source_type` (all/groups/chats/selected), `min_positive_words` (1-10), `min_sentiment_score` (0.0-1.0), `include_keywords`, `exclude_keywords`, `languages`
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## 12. Auth & API Keys
|
|
422
|
+
|
|
423
|
+
```bash
|
|
424
|
+
# Login
|
|
425
|
+
curl -X POST -d '{"email": "user@example.com", "password": "..."}' \
|
|
426
|
+
https://api.moltflow.com/api/v2/auth/login
|
|
427
|
+
|
|
428
|
+
# Get current user
|
|
429
|
+
curl -H "Authorization: Bearer $TOKEN" \
|
|
430
|
+
https://api.moltflow.com/api/v2/auth/me
|
|
431
|
+
|
|
432
|
+
# Create API key
|
|
433
|
+
curl -X POST -H "Authorization: Bearer $TOKEN" \
|
|
434
|
+
-d '{"name": "Production Key", "expires_in_days": 90}' \
|
|
435
|
+
https://api.moltflow.com/api/v2/api-keys
|
|
436
|
+
|
|
437
|
+
# Revoke key
|
|
438
|
+
curl -X DELETE -H "Authorization: Bearer $TOKEN" \
|
|
439
|
+
https://api.moltflow.com/api/v2/api-keys/{id}
|
|
440
|
+
|
|
441
|
+
# Rotate key
|
|
442
|
+
curl -X POST -H "Authorization: Bearer $TOKEN" \
|
|
443
|
+
https://api.moltflow.com/api/v2/api-keys/{id}/rotate
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
| Endpoint | Method | Description |
|
|
447
|
+
|----------|--------|-------------|
|
|
448
|
+
| `/auth/login` | POST | Login (email/password), returns JWT |
|
|
449
|
+
| `/auth/refresh` | POST | Refresh token |
|
|
450
|
+
| `/auth/me` | GET | Current user + tenant |
|
|
451
|
+
| `/auth/magic-link/request` | POST | Request magic link |
|
|
452
|
+
| `/api-keys` | GET/POST | List/create API keys |
|
|
453
|
+
| `/api-keys/{id}` | GET/DELETE | Get/revoke key |
|
|
454
|
+
| `/api-keys/{id}/rotate` | POST | Rotate key |
|
|
455
|
+
|
|
456
|
+
## 13. Usage & Billing
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
# Current period usage
|
|
460
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
461
|
+
https://api.moltflow.com/api/v2/usage/current
|
|
462
|
+
|
|
463
|
+
# Daily breakdown
|
|
464
|
+
curl -H "X-API-Key: $MOLTFLOW_API_KEY" \
|
|
465
|
+
"https://api.moltflow.com/api/v2/usage/daily?days=30"
|
|
466
|
+
|
|
467
|
+
# List plans
|
|
468
|
+
curl https://api.moltflow.com/api/v2/billing/plans
|
|
469
|
+
|
|
470
|
+
# Create checkout session
|
|
471
|
+
curl -X POST -H "Authorization: Bearer $TOKEN" \
|
|
472
|
+
-d '{"plan": "pro", "cycle": "monthly", "success_url": "https://...", "cancel_url": "https://..."}' \
|
|
473
|
+
https://api.moltflow.com/api/v2/billing/checkout
|
|
474
|
+
|
|
475
|
+
# Billing portal
|
|
476
|
+
curl -H "Authorization: Bearer $TOKEN" \
|
|
477
|
+
https://api.moltflow.com/api/v2/billing/portal
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
| Endpoint | Method | Description |
|
|
481
|
+
|----------|--------|-------------|
|
|
482
|
+
| `/usage/current` | GET | Current period usage + limits |
|
|
483
|
+
| `/usage/history` | GET | Historical usage |
|
|
484
|
+
| `/usage/daily` | GET | Daily breakdown |
|
|
485
|
+
| `/billing/plans` | GET | Available plans |
|
|
486
|
+
| `/billing/subscription` | GET | Current subscription |
|
|
487
|
+
| `/billing/checkout` | POST | Stripe checkout session |
|
|
488
|
+
| `/billing/portal` | GET | Stripe billing portal |
|
|
489
|
+
| `/billing/cancel` | POST | Cancel subscription |
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
## Notes
|
|
494
|
+
|
|
495
|
+
- All messages include anti-spam compliance (typing indicators, random delays)
|
|
496
|
+
- Rate limits by plan: Free 10/min, Starter 60/min, Pro 300/min, Business 1000/min
|
|
497
|
+
- Sessions require QR code pairing on first connect
|
|
498
|
+
- Use E.164 phone format without `+` where required
|
|
499
|
+
- AI features require Pro plan or above
|
|
500
|
+
- A2A protocol requires Business plan
|
|
501
|
+
- AI reply generation includes safety: input sanitization, intent verification, output filtering, PII/secrets detection
|
|
502
|
+
- Content policy filters secrets (API keys, tokens) and PII (SSN, credit cards)
|
|
503
|
+
- API keys use name + expires_in_days (no scopes param); raw key shown only once at creation
|
|
504
|
+
- Respect WhatsApp opt-in, business hours, and opt-out requests
|
|
505
|
+
|
|
506
|
+
<!-- FILEMAP:BEGIN -->
|
|
507
|
+
```text
|
|
508
|
+
[moltflow file map]|root: .
|
|
509
|
+
|.:{SKILL.md,package.json}
|
|
510
|
+
|scripts:{quickstart.py,a2a_client.py,discover_agent.py,send_message.py,admin.py,ai_config.py,reviews.py}
|
|
511
|
+
```
|
|
512
|
+
<!-- FILEMAP:END -->
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@moltflow/skills",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MoltFlow — complete WhatsApp automation platform: sessions, messaging, groups, AI, A2A protocol, reviews, billing.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"private": false,
|
|
7
|
+
"files": [
|
|
8
|
+
"SKILL.md",
|
|
9
|
+
"scripts/"
|
|
10
|
+
],
|
|
11
|
+
"keywords": [
|
|
12
|
+
"moltflow",
|
|
13
|
+
"whatsapp",
|
|
14
|
+
"openclaw",
|
|
15
|
+
"clawhub",
|
|
16
|
+
"automation",
|
|
17
|
+
"a2a",
|
|
18
|
+
"ai",
|
|
19
|
+
"reviews",
|
|
20
|
+
"crm"
|
|
21
|
+
],
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
MoltFlow A2A - Agent Discovery and JSON-RPC Messaging
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
import json
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
API_KEY = os.environ.get("MOLTFLOW_API_KEY")
|
|
10
|
+
BASE_URL = os.environ.get("MOLTFLOW_API_URL", "https://api.moltflow.com")
|
|
11
|
+
|
|
12
|
+
if not API_KEY:
|
|
13
|
+
print("Error: MOLTFLOW_API_KEY environment variable not set")
|
|
14
|
+
exit(1)
|
|
15
|
+
|
|
16
|
+
headers = {"X-API-Key": API_KEY, "Content-Type": "application/json"}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def bootstrap():
|
|
20
|
+
"""Get full skill onboarding config."""
|
|
21
|
+
r = requests.get(f"{BASE_URL}/api/v2/agent/bootstrap", headers=headers)
|
|
22
|
+
r.raise_for_status()
|
|
23
|
+
return r.json()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_public_key():
|
|
27
|
+
"""Get your agent's X25519 public key."""
|
|
28
|
+
r = requests.get(f"{BASE_URL}/api/v2/agent/public-key", headers=headers)
|
|
29
|
+
r.raise_for_status()
|
|
30
|
+
return r.json()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def resolve_phone(phone: str):
|
|
34
|
+
"""Resolve a phone number to a MoltFlow agent."""
|
|
35
|
+
r = requests.get(f"{BASE_URL}/api/v2/agents/resolve/{phone}", headers=headers)
|
|
36
|
+
r.raise_for_status()
|
|
37
|
+
return r.json()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def list_peers(trust_level: str = None):
|
|
41
|
+
"""List discovered peer agents."""
|
|
42
|
+
params = {"trust_level": trust_level} if trust_level else {}
|
|
43
|
+
r = requests.get(f"{BASE_URL}/api/v2/agents/peers", headers=headers, params=params)
|
|
44
|
+
r.raise_for_status()
|
|
45
|
+
return r.json()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def update_trust(peer_id: str, trust_level: str):
|
|
49
|
+
"""Update peer trust level (discovered, verified, blocked)."""
|
|
50
|
+
r = requests.patch(
|
|
51
|
+
f"{BASE_URL}/api/v2/agents/peers/{peer_id}/trust",
|
|
52
|
+
headers=headers,
|
|
53
|
+
json={"trust_level": trust_level},
|
|
54
|
+
)
|
|
55
|
+
r.raise_for_status()
|
|
56
|
+
return r.json()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def a2a_send_message(phone: str, text: str, request_id: str = "1"):
|
|
60
|
+
"""Send a message via A2A JSON-RPC."""
|
|
61
|
+
r = requests.post(
|
|
62
|
+
f"{BASE_URL}/api/v2/a2a",
|
|
63
|
+
headers=headers,
|
|
64
|
+
json={
|
|
65
|
+
"jsonrpc": "2.0",
|
|
66
|
+
"method": "agent.message.send",
|
|
67
|
+
"params": {
|
|
68
|
+
"phone": phone,
|
|
69
|
+
"message": {"parts": [{"text": text}]},
|
|
70
|
+
},
|
|
71
|
+
"id": request_id,
|
|
72
|
+
},
|
|
73
|
+
)
|
|
74
|
+
r.raise_for_status()
|
|
75
|
+
return r.json()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def a2a_group_context(group_id: str, request_id: str = "1"):
|
|
79
|
+
"""Get group context via A2A JSON-RPC."""
|
|
80
|
+
r = requests.post(
|
|
81
|
+
f"{BASE_URL}/api/v2/a2a",
|
|
82
|
+
headers=headers,
|
|
83
|
+
json={
|
|
84
|
+
"jsonrpc": "2.0",
|
|
85
|
+
"method": "group.getContext",
|
|
86
|
+
"params": {"groupId": group_id},
|
|
87
|
+
"id": request_id,
|
|
88
|
+
},
|
|
89
|
+
)
|
|
90
|
+
r.raise_for_status()
|
|
91
|
+
return r.json()
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def get_policy():
|
|
95
|
+
"""Get A2A content policy settings."""
|
|
96
|
+
r = requests.get(f"{BASE_URL}/api/v2/a2a-policy/settings", headers=headers)
|
|
97
|
+
r.raise_for_status()
|
|
98
|
+
return r.json()
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_content(content: str):
|
|
102
|
+
"""Test content against policy (dry run)."""
|
|
103
|
+
r = requests.post(
|
|
104
|
+
f"{BASE_URL}/api/v2/a2a-policy/test",
|
|
105
|
+
headers=headers,
|
|
106
|
+
json={"content": content},
|
|
107
|
+
)
|
|
108
|
+
r.raise_for_status()
|
|
109
|
+
return r.json()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if __name__ == "__main__":
|
|
113
|
+
print("MoltFlow A2A Protocol")
|
|
114
|
+
print("=" * 40)
|
|
115
|
+
|
|
116
|
+
# Bootstrap
|
|
117
|
+
config = bootstrap()
|
|
118
|
+
print(f"\nTenant: {config['tenant_name']} (Plan: {config['plan']})")
|
|
119
|
+
print(f"Encryption: {'enabled' if config['encryption']['enabled'] else 'disabled'}")
|
|
120
|
+
|
|
121
|
+
# List peers
|
|
122
|
+
peers = list_peers()
|
|
123
|
+
print(f"\nDiscovered Peers: {peers['total']}")
|
|
124
|
+
|
|
125
|
+
for p in peers.get("peers", []):
|
|
126
|
+
print(f" - {p.get('peer_name', 'Unknown')} ({p.get('peer_phone', 'N/A')}) - trust: {p['trust_level']}")
|
package/scripts/admin.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
MoltFlow Admin - API Key Management and Usage
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
API_KEY = os.environ.get("MOLTFLOW_API_KEY")
|
|
9
|
+
BASE_URL = os.environ.get("MOLTFLOW_API_URL", "https://api.moltflow.com")
|
|
10
|
+
|
|
11
|
+
if not API_KEY:
|
|
12
|
+
print("Error: MOLTFLOW_API_KEY environment variable not set")
|
|
13
|
+
exit(1)
|
|
14
|
+
|
|
15
|
+
headers = {"X-API-Key": API_KEY, "Content-Type": "application/json"}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_current_user(token: str):
|
|
19
|
+
"""Get current user info (requires JWT)."""
|
|
20
|
+
r = requests.get(
|
|
21
|
+
f"{BASE_URL}/api/v2/auth/me",
|
|
22
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
23
|
+
)
|
|
24
|
+
r.raise_for_status()
|
|
25
|
+
return r.json()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def list_api_keys(token: str):
|
|
29
|
+
"""List API keys (requires JWT)."""
|
|
30
|
+
r = requests.get(
|
|
31
|
+
f"{BASE_URL}/api/v2/api-keys",
|
|
32
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
33
|
+
)
|
|
34
|
+
r.raise_for_status()
|
|
35
|
+
return r.json()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def create_api_key(token: str, name: str, description: str = None, expires_in_days: int = None):
|
|
39
|
+
"""Create a new API key (requires JWT)."""
|
|
40
|
+
data = {"name": name}
|
|
41
|
+
if description:
|
|
42
|
+
data["description"] = description
|
|
43
|
+
if expires_in_days:
|
|
44
|
+
data["expires_in_days"] = expires_in_days
|
|
45
|
+
r = requests.post(
|
|
46
|
+
f"{BASE_URL}/api/v2/api-keys",
|
|
47
|
+
headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"},
|
|
48
|
+
json=data,
|
|
49
|
+
)
|
|
50
|
+
r.raise_for_status()
|
|
51
|
+
return r.json()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_current_usage():
|
|
55
|
+
"""Get current period usage stats."""
|
|
56
|
+
r = requests.get(f"{BASE_URL}/api/v2/usage/current", headers=headers)
|
|
57
|
+
r.raise_for_status()
|
|
58
|
+
return r.json()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_usage_history(months: int = 6):
|
|
62
|
+
"""Get usage history for past months."""
|
|
63
|
+
r = requests.get(
|
|
64
|
+
f"{BASE_URL}/api/v2/usage/history",
|
|
65
|
+
headers=headers,
|
|
66
|
+
params={"months": months},
|
|
67
|
+
)
|
|
68
|
+
r.raise_for_status()
|
|
69
|
+
return r.json()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_daily_usage(days: int = 30):
|
|
73
|
+
"""Get daily usage breakdown."""
|
|
74
|
+
r = requests.get(
|
|
75
|
+
f"{BASE_URL}/api/v2/usage/daily",
|
|
76
|
+
headers=headers,
|
|
77
|
+
params={"days": days},
|
|
78
|
+
)
|
|
79
|
+
r.raise_for_status()
|
|
80
|
+
return r.json()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def get_subscription(token: str):
|
|
84
|
+
"""Get current subscription (requires JWT)."""
|
|
85
|
+
r = requests.get(
|
|
86
|
+
f"{BASE_URL}/api/v2/billing/subscription",
|
|
87
|
+
headers={"Authorization": f"Bearer {token}"},
|
|
88
|
+
)
|
|
89
|
+
r.raise_for_status()
|
|
90
|
+
return r.json()
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def get_plans():
|
|
94
|
+
"""Get available subscription plans."""
|
|
95
|
+
r = requests.get(f"{BASE_URL}/api/v2/billing/plans")
|
|
96
|
+
r.raise_for_status()
|
|
97
|
+
return r.json()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
if __name__ == "__main__":
|
|
101
|
+
print("MoltFlow Admin Dashboard")
|
|
102
|
+
print("=" * 40)
|
|
103
|
+
|
|
104
|
+
usage = get_current_usage()
|
|
105
|
+
print(f"\nUsage This Month ({usage.get('period')}):")
|
|
106
|
+
print(f" Messages Sent: {usage.get('messages_sent', 0)}")
|
|
107
|
+
print(f" Messages Received: {usage.get('messages_received', 0)}")
|
|
108
|
+
print(f" Total Messages: {usage.get('total_messages', 0)}")
|
|
109
|
+
print(f" Leads Detected: {usage.get('leads_detected', 0)}")
|
|
110
|
+
print(f" API Calls: {usage.get('api_calls', 0)}")
|
|
111
|
+
print(f" Usage: {usage.get('usage_percentage', 0)}%")
|
|
112
|
+
print(f" Limit: {usage.get('limit_messages', 0)} messages/month")
|
|
113
|
+
|
|
114
|
+
plans = get_plans()
|
|
115
|
+
print(f"\nAvailable Plans: {len(plans.get('plans', []))}")
|
|
116
|
+
for plan in plans.get("plans", []):
|
|
117
|
+
print(f" - {plan.get('name', 'unknown')}")
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
MoltFlow AI - Lead Detection and Auto-Responses
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
API_KEY = os.environ.get("MOLTFLOW_API_KEY")
|
|
9
|
+
BASE_URL = os.environ.get("MOLTFLOW_API_URL", "https://api.moltflow.com")
|
|
10
|
+
|
|
11
|
+
if not API_KEY:
|
|
12
|
+
print("Error: MOLTFLOW_API_KEY environment variable not set")
|
|
13
|
+
exit(1)
|
|
14
|
+
|
|
15
|
+
headers = {"X-API-Key": API_KEY, "Content-Type": "application/json"}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_ai_config():
|
|
19
|
+
"""Get current AI configuration."""
|
|
20
|
+
r = requests.get(f"{BASE_URL}/api/v2/ai/config", headers=headers)
|
|
21
|
+
r.raise_for_status()
|
|
22
|
+
return r.json()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def enable_ai(provider: str = "openai", model: str = "gpt-4o", system_prompt: str = None):
|
|
26
|
+
"""Enable AI auto-responses."""
|
|
27
|
+
data = {"enabled": True, "provider": provider, "model": model}
|
|
28
|
+
if system_prompt:
|
|
29
|
+
data["system_prompt"] = system_prompt
|
|
30
|
+
r = requests.patch(f"{BASE_URL}/api/v2/ai/config", headers=headers, json=data)
|
|
31
|
+
r.raise_for_status()
|
|
32
|
+
return r.json()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_leads(status: str = "new", limit: int = 50):
|
|
36
|
+
"""Get detected leads."""
|
|
37
|
+
r = requests.get(
|
|
38
|
+
f"{BASE_URL}/api/v2/ai/leads",
|
|
39
|
+
headers=headers,
|
|
40
|
+
params={"status": status, "limit": limit},
|
|
41
|
+
)
|
|
42
|
+
r.raise_for_status()
|
|
43
|
+
return r.json()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def add_knowledge_source(name: str, source_type: str, source: str):
|
|
47
|
+
"""Add a knowledge source for RAG."""
|
|
48
|
+
r = requests.post(
|
|
49
|
+
f"{BASE_URL}/api/v2/ai/knowledge",
|
|
50
|
+
headers=headers,
|
|
51
|
+
json={"name": name, "type": source_type, "source": source},
|
|
52
|
+
)
|
|
53
|
+
r.raise_for_status()
|
|
54
|
+
return r.json()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
if __name__ == "__main__":
|
|
58
|
+
print("MoltFlow AI Configuration")
|
|
59
|
+
print("=" * 40)
|
|
60
|
+
|
|
61
|
+
config = get_ai_config()
|
|
62
|
+
print(f"\nAI Enabled: {config.get('enabled', False)}")
|
|
63
|
+
print(f"Provider: {config.get('provider', 'not set')}")
|
|
64
|
+
print(f"Model: {config.get('model', 'not set')}")
|
|
65
|
+
|
|
66
|
+
leads = get_leads()
|
|
67
|
+
print(f"\nNew Leads: {len(leads.get('leads', []))}")
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
A2A Discovery Script
|
|
4
|
+
Fetches and validates the agent.json card.
|
|
5
|
+
"""
|
|
6
|
+
import requests
|
|
7
|
+
import sys
|
|
8
|
+
import json
|
|
9
|
+
import argparse
|
|
10
|
+
from typing import Dict, Any
|
|
11
|
+
|
|
12
|
+
def discover_agent(base_url: str) -> Dict[str, Any]:
|
|
13
|
+
"""
|
|
14
|
+
Fetch the .well-known/agent.json from the base URL.
|
|
15
|
+
"""
|
|
16
|
+
# Ensure URL doesn't end with slash to avoid double slashes
|
|
17
|
+
url = f"{base_url.rstrip('/')}/.well-known/agent.json"
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
response = requests.get(url, timeout=10)
|
|
21
|
+
response.raise_for_status()
|
|
22
|
+
|
|
23
|
+
data = response.json()
|
|
24
|
+
|
|
25
|
+
# Validation
|
|
26
|
+
if "supportedInterfaces" not in data:
|
|
27
|
+
print(f"WARNING: {url} response missing 'supportedInterfaces'.", file=sys.stderr)
|
|
28
|
+
|
|
29
|
+
return data
|
|
30
|
+
|
|
31
|
+
except requests.exceptions.RequestException as e:
|
|
32
|
+
# Return a structured error for the agent to parse
|
|
33
|
+
return {
|
|
34
|
+
"error": True,
|
|
35
|
+
"message": f"Discovery failed: {str(e)}",
|
|
36
|
+
"url": url
|
|
37
|
+
}
|
|
38
|
+
except json.JSONDecodeError:
|
|
39
|
+
return {
|
|
40
|
+
"error": True,
|
|
41
|
+
"message": "Invalid JSON response from endpoint",
|
|
42
|
+
"url": url
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
parser = argparse.ArgumentParser(description="Discover MoltFlow Agent capabilities.")
|
|
47
|
+
parser.add_argument("url", help="Base URL of the agent (e.g., https://api.waiflow.app)")
|
|
48
|
+
|
|
49
|
+
args = parser.parse_args()
|
|
50
|
+
|
|
51
|
+
card = discover_agent(args.url)
|
|
52
|
+
|
|
53
|
+
# Output solely JSON for easy parsing by calling agent
|
|
54
|
+
print(json.dumps(card, indent=2))
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
MoltFlow Quickstart - Send your first WhatsApp message
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
API_KEY = os.environ.get("MOLTFLOW_API_KEY")
|
|
9
|
+
BASE_URL = os.environ.get("MOLTFLOW_API_URL", "https://api.moltflow.com")
|
|
10
|
+
|
|
11
|
+
if not API_KEY:
|
|
12
|
+
print("Error: MOLTFLOW_API_KEY environment variable not set")
|
|
13
|
+
print("Get your API key at https://moltflow.com")
|
|
14
|
+
exit(1)
|
|
15
|
+
|
|
16
|
+
headers = {"X-API-Key": API_KEY, "Content-Type": "application/json"}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def list_sessions():
|
|
20
|
+
"""List all WhatsApp sessions."""
|
|
21
|
+
r = requests.get(f"{BASE_URL}/api/v2/sessions", headers=headers)
|
|
22
|
+
r.raise_for_status()
|
|
23
|
+
return r.json()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def send_message(session_id: str, chat_id: str, message: str):
|
|
27
|
+
"""Send a text message."""
|
|
28
|
+
r = requests.post(
|
|
29
|
+
f"{BASE_URL}/api/v2/messages/send",
|
|
30
|
+
headers=headers,
|
|
31
|
+
json={"session_id": session_id, "chat_id": chat_id, "message": message},
|
|
32
|
+
)
|
|
33
|
+
r.raise_for_status()
|
|
34
|
+
return r.json()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def list_groups():
|
|
38
|
+
"""List monitored groups."""
|
|
39
|
+
r = requests.get(f"{BASE_URL}/api/v2/groups", headers=headers)
|
|
40
|
+
r.raise_for_status()
|
|
41
|
+
return r.json()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_usage():
|
|
45
|
+
"""Get current usage stats."""
|
|
46
|
+
r = requests.get(f"{BASE_URL}/api/v2/usage/current", headers=headers)
|
|
47
|
+
r.raise_for_status()
|
|
48
|
+
return r.json()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
if __name__ == "__main__":
|
|
52
|
+
print("MoltFlow Quickstart")
|
|
53
|
+
print("=" * 40)
|
|
54
|
+
|
|
55
|
+
# List sessions
|
|
56
|
+
sessions = list_sessions()
|
|
57
|
+
print(f"\nFound {len(sessions)} sessions")
|
|
58
|
+
|
|
59
|
+
for s in sessions:
|
|
60
|
+
print(f" - {s['name']} ({s['id'][:8]}...) - {s['status']}")
|
|
61
|
+
|
|
62
|
+
# Show usage
|
|
63
|
+
usage = get_usage()
|
|
64
|
+
print(f"\nUsage: {usage['total_messages']}/{usage['limit_messages']} messages ({usage['usage_percentage']}%)")
|
|
65
|
+
|
|
66
|
+
print("\nTo send a message:")
|
|
67
|
+
print(' send_message("session-id", "1234567890@c.us", "Hello!")')
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
MoltFlow Reviews - Review Collector Management
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
API_KEY = os.environ.get("MOLTFLOW_API_KEY")
|
|
9
|
+
BASE_URL = os.environ.get("MOLTFLOW_API_URL", "https://api.moltflow.com")
|
|
10
|
+
|
|
11
|
+
if not API_KEY:
|
|
12
|
+
print("Error: MOLTFLOW_API_KEY environment variable not set")
|
|
13
|
+
exit(1)
|
|
14
|
+
|
|
15
|
+
headers = {"X-API-Key": API_KEY, "Content-Type": "application/json"}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# ============================================================================
|
|
19
|
+
# Collectors
|
|
20
|
+
# ============================================================================
|
|
21
|
+
|
|
22
|
+
def list_collectors(include_inactive: bool = False):
|
|
23
|
+
"""List all review collectors."""
|
|
24
|
+
r = requests.get(
|
|
25
|
+
f"{BASE_URL}/api/v2/reviews/collectors",
|
|
26
|
+
headers=headers,
|
|
27
|
+
params={"include_inactive": include_inactive},
|
|
28
|
+
)
|
|
29
|
+
r.raise_for_status()
|
|
30
|
+
return r.json()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def create_collector(
|
|
34
|
+
name: str,
|
|
35
|
+
session_id: str,
|
|
36
|
+
source_type: str = "all",
|
|
37
|
+
min_positive_words: int = 3,
|
|
38
|
+
min_sentiment_score: float = 0.6,
|
|
39
|
+
include_keywords: list = None,
|
|
40
|
+
exclude_keywords: list = None,
|
|
41
|
+
languages: list = None,
|
|
42
|
+
description: str = None,
|
|
43
|
+
selected_chat_ids: list = None,
|
|
44
|
+
):
|
|
45
|
+
"""Create a new review collector."""
|
|
46
|
+
data = {
|
|
47
|
+
"name": name,
|
|
48
|
+
"session_id": session_id,
|
|
49
|
+
"source_type": source_type,
|
|
50
|
+
"min_positive_words": min_positive_words,
|
|
51
|
+
"min_sentiment_score": min_sentiment_score,
|
|
52
|
+
}
|
|
53
|
+
if include_keywords:
|
|
54
|
+
data["include_keywords"] = include_keywords
|
|
55
|
+
if exclude_keywords:
|
|
56
|
+
data["exclude_keywords"] = exclude_keywords
|
|
57
|
+
if languages is not None:
|
|
58
|
+
data["languages"] = languages
|
|
59
|
+
if description:
|
|
60
|
+
data["description"] = description
|
|
61
|
+
if selected_chat_ids:
|
|
62
|
+
data["selected_chat_ids"] = selected_chat_ids
|
|
63
|
+
r = requests.post(
|
|
64
|
+
f"{BASE_URL}/api/v2/reviews/collectors",
|
|
65
|
+
headers=headers,
|
|
66
|
+
json=data,
|
|
67
|
+
)
|
|
68
|
+
r.raise_for_status()
|
|
69
|
+
return r.json()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_collector(collector_id: str):
|
|
73
|
+
"""Get a collector with stats."""
|
|
74
|
+
r = requests.get(
|
|
75
|
+
f"{BASE_URL}/api/v2/reviews/collectors/{collector_id}",
|
|
76
|
+
headers=headers,
|
|
77
|
+
)
|
|
78
|
+
r.raise_for_status()
|
|
79
|
+
return r.json()
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def update_collector(collector_id: str, **kwargs):
|
|
83
|
+
"""Update a collector. Pass fields to update as keyword args."""
|
|
84
|
+
r = requests.patch(
|
|
85
|
+
f"{BASE_URL}/api/v2/reviews/collectors/{collector_id}",
|
|
86
|
+
headers=headers,
|
|
87
|
+
json=kwargs,
|
|
88
|
+
)
|
|
89
|
+
r.raise_for_status()
|
|
90
|
+
return r.json()
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def delete_collector(collector_id: str):
|
|
94
|
+
"""Delete a collector and all its reviews."""
|
|
95
|
+
r = requests.delete(
|
|
96
|
+
f"{BASE_URL}/api/v2/reviews/collectors/{collector_id}",
|
|
97
|
+
headers=headers,
|
|
98
|
+
)
|
|
99
|
+
r.raise_for_status()
|
|
100
|
+
return r.json()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def run_collector(collector_id: str):
|
|
104
|
+
"""Trigger a manual scan for a collector."""
|
|
105
|
+
r = requests.post(
|
|
106
|
+
f"{BASE_URL}/api/v2/reviews/collectors/{collector_id}/run",
|
|
107
|
+
headers=headers,
|
|
108
|
+
)
|
|
109
|
+
r.raise_for_status()
|
|
110
|
+
return r.json()
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# ============================================================================
|
|
114
|
+
# Reviews
|
|
115
|
+
# ============================================================================
|
|
116
|
+
|
|
117
|
+
def list_reviews(
|
|
118
|
+
collector_id: str = None,
|
|
119
|
+
approved_only: bool = False,
|
|
120
|
+
include_hidden: bool = False,
|
|
121
|
+
limit: int = 50,
|
|
122
|
+
offset: int = 0,
|
|
123
|
+
):
|
|
124
|
+
"""List collected reviews."""
|
|
125
|
+
params = {
|
|
126
|
+
"approved_only": approved_only,
|
|
127
|
+
"include_hidden": include_hidden,
|
|
128
|
+
"limit": limit,
|
|
129
|
+
"offset": offset,
|
|
130
|
+
}
|
|
131
|
+
if collector_id:
|
|
132
|
+
params["collector_id"] = collector_id
|
|
133
|
+
r = requests.get(
|
|
134
|
+
f"{BASE_URL}/api/v2/reviews",
|
|
135
|
+
headers=headers,
|
|
136
|
+
params=params,
|
|
137
|
+
)
|
|
138
|
+
r.raise_for_status()
|
|
139
|
+
return r.json()
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def get_review(review_id: str):
|
|
143
|
+
"""Get a single review."""
|
|
144
|
+
r = requests.get(
|
|
145
|
+
f"{BASE_URL}/api/v2/reviews/{review_id}",
|
|
146
|
+
headers=headers,
|
|
147
|
+
)
|
|
148
|
+
r.raise_for_status()
|
|
149
|
+
return r.json()
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def approve_review(review_id: str):
|
|
153
|
+
"""Approve a review as a testimonial."""
|
|
154
|
+
r = requests.patch(
|
|
155
|
+
f"{BASE_URL}/api/v2/reviews/{review_id}",
|
|
156
|
+
headers=headers,
|
|
157
|
+
json={"is_approved": True},
|
|
158
|
+
)
|
|
159
|
+
r.raise_for_status()
|
|
160
|
+
return r.json()
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def hide_review(review_id: str):
|
|
164
|
+
"""Hide a review from the list."""
|
|
165
|
+
r = requests.patch(
|
|
166
|
+
f"{BASE_URL}/api/v2/reviews/{review_id}",
|
|
167
|
+
headers=headers,
|
|
168
|
+
json={"is_hidden": True},
|
|
169
|
+
)
|
|
170
|
+
r.raise_for_status()
|
|
171
|
+
return r.json()
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def delete_review(review_id: str):
|
|
175
|
+
"""Delete a review."""
|
|
176
|
+
r = requests.delete(
|
|
177
|
+
f"{BASE_URL}/api/v2/reviews/{review_id}",
|
|
178
|
+
headers=headers,
|
|
179
|
+
)
|
|
180
|
+
r.raise_for_status()
|
|
181
|
+
return r.json()
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def get_stats(collector_id: str = None):
|
|
185
|
+
"""Get review statistics."""
|
|
186
|
+
params = {}
|
|
187
|
+
if collector_id:
|
|
188
|
+
params["collector_id"] = collector_id
|
|
189
|
+
r = requests.get(
|
|
190
|
+
f"{BASE_URL}/api/v2/reviews/stats",
|
|
191
|
+
headers=headers,
|
|
192
|
+
params=params,
|
|
193
|
+
)
|
|
194
|
+
r.raise_for_status()
|
|
195
|
+
return r.json()
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def export_testimonials(format: str = "json"):
|
|
199
|
+
"""Export approved reviews as testimonials (json or html)."""
|
|
200
|
+
r = requests.get(
|
|
201
|
+
f"{BASE_URL}/api/v2/reviews/testimonials/export",
|
|
202
|
+
headers=headers,
|
|
203
|
+
params={"format": format},
|
|
204
|
+
)
|
|
205
|
+
r.raise_for_status()
|
|
206
|
+
return r.json()
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
if __name__ == "__main__":
|
|
210
|
+
print("MoltFlow Review Collector")
|
|
211
|
+
print("=" * 40)
|
|
212
|
+
|
|
213
|
+
collectors = list_collectors()
|
|
214
|
+
print(f"\nCollectors: {collectors.get('total', 0)}")
|
|
215
|
+
for c in collectors.get("collectors", []):
|
|
216
|
+
print(f" - {c.get('name')} ({c.get('source_type')}) active={c.get('is_active')}")
|
|
217
|
+
|
|
218
|
+
stats = get_stats()
|
|
219
|
+
print(f"\nReview Stats:")
|
|
220
|
+
print(f" Total: {stats.get('total', 0)}")
|
|
221
|
+
print(f" Approved: {stats.get('approved', 0)}")
|
|
222
|
+
print(f" Pending: {stats.get('pending', 0)}")
|
|
223
|
+
print(f" Hidden: {stats.get('hidden', 0)}")
|
|
224
|
+
print(f" Avg Score: {stats.get('avg_score', 0)}")
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Send WhatsApp Message Helper
|
|
4
|
+
Wrapper around A2A Client for the most common task.
|
|
5
|
+
"""
|
|
6
|
+
import argparse
|
|
7
|
+
import sys
|
|
8
|
+
import json
|
|
9
|
+
from a2a_client import A2AClient
|
|
10
|
+
|
|
11
|
+
def main():
|
|
12
|
+
parser = argparse.ArgumentParser(description="Send WhatsApp message via A2A.")
|
|
13
|
+
parser.add_argument("--url", required=True, help="Full A2A API URL")
|
|
14
|
+
parser.add_argument("--key", required=True, help="API Key")
|
|
15
|
+
parser.add_argument("--to", required=True, help="Target phone number (e.g. 1234567890)")
|
|
16
|
+
parser.add_argument("--text", required=True, help="Message text content")
|
|
17
|
+
|
|
18
|
+
args = parser.parse_args()
|
|
19
|
+
|
|
20
|
+
client = A2AClient(args.url, args.key)
|
|
21
|
+
|
|
22
|
+
# Construct standard A2A message payload
|
|
23
|
+
params = {
|
|
24
|
+
"phone": args.to,
|
|
25
|
+
"text": args.text
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
response = client.send_rpc("message/send", params)
|
|
29
|
+
|
|
30
|
+
# Output result
|
|
31
|
+
print(json.dumps(response, indent=2))
|
|
32
|
+
|
|
33
|
+
# Exit code based on success
|
|
34
|
+
if "error" in response:
|
|
35
|
+
sys.exit(1)
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
38
|
+
main()
|