@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 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']}")
@@ -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()