Skip to content

Commit c5e82f6

Browse files
committed
chore(skill): sync provider-aware skills to plugin copy
1 parent 1ab043d commit c5e82f6

11 files changed

Lines changed: 566 additions & 29 deletions

File tree

plugins/zaileys-official/skills/zaileys-assist/SKILL.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,17 @@ description: >-
1212
You are the **zaileys expert orchestrator**. This is the single entry point for any
1313
zaileys work — detect what the user needs and apply the right capability, pulling from
1414
the verified references below. zaileys ([github](https://github.com/zeative/zaileys),
15-
npm `zaileys`, docs <https://zeative.github.io/zaileys/>) is a typed wrapper over Baileys.
15+
npm `zaileys`, docs <https://zeative.github.io/zaileys/>) is a typed WhatsApp framework.
1616

1717
> Import is always `import { Client } from 'zaileys'`. Dual ESM/CJS; runs on Node 20+, Bun, Deno, Termux.
1818
19+
> **Two providers, one API.** zaileys runs on either the **🔗 unofficial** WhatsApp Web engine
20+
> (Baileys — the **default**) or the **☁️ official** Meta WhatsApp Cloud API. The `send(jid)…`
21+
> builder, `on(...)` events, commands, storage, broadcast — all identical across both; you flip
22+
> `provider: 'cloud'`. **Detect the provider from the task** (webhook/template/OTP/campaign/`wa.cloud`/
23+
> "official"/"business API" ⇒ cloud) and load [references/cloud.md](references/cloud.md) for anything
24+
> cloud-specific. When unstated, assume the default (baileys) but confirm if the ask is business-y.
25+
1926
## Routing — detect intent, then act
2027

2128
Figure out what the user wants and handle it directly (this skill carries all the
@@ -30,9 +37,10 @@ knowledge). Sibling focused skills exist for explicit invocation, but you can do
3037
| **Explain/choose** ("how does X work", "which adapter", "qr vs pairing") | Answer from the references; show a minimal example | all references |
3138
| **Manage account/chats/groups** (profile name/status/avatar, archive/pin/mute a chat, save contact, group join-requests, newsletter/community admin, business catalog/products) | Use the right `client.*` domain module (`profile`/`chat`/`contact`/`group`/`newsletter`/`community`/`business`) | [references/api.md](references/api.md) |
3239
| **AI bot + external tools** ("connect MCP", "give the bot tools", scraper catalog, "MCP server", zpi) | Wire MCP server(s) into the LLM call (AI SDK), expose tools via a lazy router | [references/mcp.md](references/mcp.md) |
40+
| **☁️ Official Cloud API** ("official", "cloud API", "business API", `provider: 'cloud'`, webhook, template, OTP, campaign, broadcast to cold numbers, Flows, catalog/commerce, `wa.cloud.*`, `sendTemplate`) | Use `provider: 'cloud'` + the cloud surface; mount `wa.webhook()`; template-gate cold sends | [references/cloud.md](references/cloud.md), [references/recipes.md](references/recipes.md) |
3341

34-
Default when ambiguous: ask one clarifying question, then proceed. Prefer doing the work
35-
over describing it.
42+
Default when ambiguous: ask one clarifying question (incl. **which provider** if business-y), then
43+
proceed. Prefer doing the work over describing it.
3644

3745
## Deep references (load on demand)
3846

@@ -44,6 +52,7 @@ Read the relevant file before writing or debugging — they contain the verified
4452
- [references/troubleshooting.md](references/troubleshooting.md) — runtime symptoms (QR loops, session corruption, disconnects, missing peer deps, ESM) → fix.
4553
- [references/pitfalls.md](references/pitfalls.md) — common mistakes & anti-patterns → the correct way. **Read this when reviewing code.**
4654
- [references/mcp.md](references/mcp.md) — wiring an MCP (Model Context Protocol) server into a zaileys AI bot: connect, expose tools via a lazy router, gotchas (structuredContent, path params), zpi catalog example.
55+
- [references/cloud.md](references/cloud.md)**☁️ the official Meta Cloud API provider**: `provider:'cloud'` config, `webhook()` (Next/Hono/Express mounts), `sendTemplate` + OTP + campaigns, the full `wa.cloud.*` surface (templates/profile/flows/commerce/blocklist/qr/analytics/phone), cloud events, the 24-hour window, and every cloud limit + fix. **Read this for anything cloud/official/webhook/template.**
4756

4857
For exhaustive detail, the full docs are one file: <https://zeative.github.io/zaileys/llms-full.txt>.
4958

@@ -53,6 +62,7 @@ For exhaustive detail, the full docs are one file: <https://zeative.github.io/za
5362
2. **Receiving = events.** `client.on('message' | 'text' | 'image' | 'button-click' | 'group-update' | …, handler)`. `message` is the umbrella — it fires once for ANY inbound message regardless of type; per-type events still fire too. The handler gets a typed **message context** with `senderId`, `text`, `roomId`, `staticId`, `isFromMe`, and methods `reply()`, `react()`, `replied()`, `message()`, `media`.
5463
3. **Sending = a fluent builder.** `client.send(jid)` returns a builder; pick one content method, optionally chain `.reply()`/`.mentions()`, and `await` it → resolves to a `WAMessageKey`.
5564
4. **Storage is pluggable.** `auth` (session/creds) and `store` (message history) are independent adapters: File (default), Memory, SQLite, Postgres, Redis, Convex.
65+
5. **Provider = one option.** Default `provider:'baileys'` (WhatsApp Web, QR/pairing, socket). `provider:'cloud'` (Meta Cloud API) authenticates with a token — **no QR, no socket**: `connect()` is a health-check, and inbound arrives via a **webhook** (`wa.webhook()`), not a socket. Same builder + events either way. Cloud adds `sendTemplate()`, `wa.cloud.*`, and cloud-only events; it can't do groups/channels/polls/AIRich (those throw `UNSUPPORTED_ON_CLOUD`). All cloud detail lives in [references/cloud.md](references/cloud.md).
5666

5767
## Golden rules (best practice — enforce these)
5868

@@ -70,6 +80,7 @@ For exhaustive detail, the full docs are one file: <https://zeative.github.io/za
7080
12. **Events render in GROUPS only.** `client.send(jid).event({...})` does NOT show up in 1:1 DMs — only in `@g.us` groups. `startAt`/`endAt` accept a `Date` or epoch **ms**.
7181
13. **`groupInvite.expiresAt` is unix SECONDS** (passing ms makes the card show "expired"). The card may fail to resolve on linked-device/LID-group sessions even when it renders — the `chat.whatsapp.com/<code>` link always works, so include it as a fallback.
7282
14. **product/order/payment are receive-mostly.** All three can be RECEIVED (`ctx.media` variants), but only **`product`** can be SENT — and only from a **WhatsApp Business** account.
83+
15. **☁️ On cloud, respect the platform.** `provider:'cloud'` has no QR/socket — mount `wa.webhook()` on a public URL and subscribe the `messages` field. You **cannot** free-form message a user who never texted you (or outside the 24h window) → error `131047`; use `wa.sendTemplate()`. Template `parameters` must match the `{{n}}` count exactly (`132000`). `group`/`newsletter`/`edit`/`delete`/AIRich/polls throw `UNSUPPORTED_ON_CLOUD`. Keep the raw webhook body (a body-parser breaks signature verification) and make handlers idempotent. Full rules → [references/cloud.md](references/cloud.md).
7384

7485
## Quick API cheat-sheet
7586

plugins/zaileys-official/skills/zaileys-assist/references/api.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,27 @@
11
# zaileys API Reference
22

3-
Dense, source-verified API surface for implementing zaileys (WhatsApp client over baileys). Import: `import { Client } from 'zaileys'`. JIDs: user `628xxx@s.whatsapp.net`, group `xxx@g.us`, newsletter `xxx@newsletter`.
3+
Dense, source-verified API surface for implementing zaileys. Import: `import { Client } from 'zaileys'`. JIDs: user `628xxx@s.whatsapp.net`, group `xxx@g.us`, newsletter `xxx@newsletter`.
4+
5+
## Providers (🔗 unofficial / ☁️ official)
6+
7+
zaileys has **two providers** behind one `Client`. `provider` defaults to `'baileys'`.
8+
9+
| | 🔗 `provider: 'baileys'` (default) | ☁️ `provider: 'cloud'` |
10+
|---|---|---|
11+
| Engine | WhatsApp Web (Baileys) | Meta WhatsApp **Cloud API** (Graph + webhooks) |
12+
| Auth | QR / pairing code | `cloud: { accessToken, phoneNumberId, wabaId?, verifyToken?, appSecret? }` |
13+
| Inbound | socket events | `wa.webhook()` handler (push) |
14+
| Exclusive | groups, channels, communities, polls, presence, edit/delete, AIRich | templates/OTP, campaigns, Flows, commerce, `wa.cloud.*`, `message-status` |
15+
16+
**Everything below (the send builder, events, message context, commands, storage, automation) is
17+
shared and works on BOTH providers** — except the surfaces flagged **🔗 unofficial-only** below,
18+
which throw `ZaileysProviderError('UNSUPPORTED_ON_CLOUD')` on cloud:
19+
`client.group` / `newsletter` / `community` / `privacy` / `presence` / `chat` / `contact` /
20+
`business` / `profile`, and `client.edit` / `delete` / `pin` / `unpin` / `setDisappearing`, plus
21+
carousels, polls, and AIRich (`rich: true`).
22+
23+
For the full cloud surface (webhook, `sendTemplate`, `wa.cloud.*`, cloud events, limits) →
24+
**[cloud.md](cloud.md)**.
425

526
## Quick start
627

@@ -22,6 +43,8 @@ client.on('text', async (msg) => { if (msg.text === 'ping') await msg.reply('pon
2243

2344
| option | type | default | note |
2445
|---|---|---|---|
46+
| `provider` | `'baileys' \| 'cloud'` | `'baileys'` | `'cloud'` requires `cloud` (see [cloud.md](cloud.md)) |
47+
| `cloud` | `CloudOptions` || `{accessToken,phoneNumberId,wabaId?,verifyToken?,appSecret?,apiVersion?,baseUrl?}`; required for `provider:'cloud'` |
2548
| `sessionId` | `string` | `'default'` | namespaces auth dir + scheduler |
2649
| `authType` | `'qr' \| 'pairing'` | `'qr'` | `'pairing'` REQUIRES `phoneNumber` (else `connect()` rejects) |
2750
| `phoneNumber` | `string` || digits only, no `+`; required for pairing |
@@ -343,6 +366,8 @@ client.command('add user', async (ctx) => { /* multi-word path */ })
343366

344367
## Domain namespaces
345368

369+
> **🔗 unofficial-only.** All modules below (`group`/`profile`/`chat`/`contact`/`business`/`privacy`/`newsletter`/`community`/`presence`) throw `ZaileysProviderError('UNSUPPORTED_ON_CLOUD')` on `provider:'cloud'`. The cloud equivalent for account/business ops is `wa.cloud.*` — see [cloud.md](cloud.md).
370+
346371
### `client.group` (GroupModule)
347372

348373
| method | signature | returns |
Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
# zaileys — Official Meta Cloud API provider (☁️)
2+
3+
The cloud-exclusive reference. zaileys runs on **two providers** behind one API:
4+
5+
- **🔗 unofficial (default)** — WhatsApp Web via Baileys. QR/pairing login, groups/channels/polls,
6+
full personal-account power. Documented in [api.md](api.md) + the other references.
7+
- **☁️ official** — the Meta WhatsApp **Cloud API**. Token auth, no ban risk, templates/OTP/
8+
campaigns, Flows, commerce. **This file.**
9+
10+
The `send(jid)…` builder and `on('text' | 'image' | …)` events are **identical** across providers
11+
(see [api.md](api.md)) — this file documents ONLY what's different or cloud-exclusive. Verified
12+
against zaileys **4.8.1** (`src/cloud/*`).
13+
14+
## Switch provider
15+
16+
```ts
17+
import { Client } from 'zaileys'
18+
19+
const wa = new Client({
20+
provider: 'cloud', // default is 'baileys'
21+
cloud: {
22+
accessToken: process.env.WA_TOKEN!, // permanent / system-user token (required)
23+
phoneNumberId: process.env.WA_PHONE_ID!, // sender phone-number id, NOT the number (required)
24+
wabaId: process.env.WA_WABA_ID, // WhatsApp Business Account id — needed for wa.cloud.*
25+
verifyToken: process.env.WA_VERIFY!, // webhook GET challenge secret you choose
26+
appSecret: process.env.WA_APP_SECRET!, // verifies webhook X-Hub-Signature-256 (recommended)
27+
apiVersion: 'v23.0', // optional, pinned default
28+
baseUrl: 'https://graph.facebook.com', // optional override
29+
},
30+
})
31+
```
32+
33+
`new Client()` (no `provider`) = baileys, unchanged. `client.provider``'baileys' | 'cloud'`.
34+
Constructing `provider:'cloud'` without `accessToken`+`phoneNumberId` throws `ZaileysCloudError('CONFIG')`.
35+
36+
## connect() — no socket, no QR
37+
38+
```ts
39+
await wa.connect() // lightweight Graph health-check, resolves immediately
40+
```
41+
42+
No QR, no pairing, no session file — the token authenticates. `qr`/`pairing-code`/reconnect events
43+
never fire on cloud. `disconnect()` tears down local listeners.
44+
45+
**The webhook works even without `connect()`** — a serverless route (`export const POST = wa.webhook()`)
46+
never calls connect and still dispatches events (runtime is lazy-initialized).
47+
48+
## Webhook — inbound is push-based
49+
50+
`wa.webhook()` returns a framework-agnostic `(req: Request) => Promise<Response>`:
51+
GET → verification challenge (uses `verifyToken`); POST → verifies `X-Hub-Signature-256` HMAC (uses
52+
`appSecret`, rejects forgeries with 401), then dispatches events. Only available on cloud (throws on baileys).
53+
54+
```ts
55+
// Next.js — app/api/whatsapp/route.ts
56+
const handler = wa.webhook()
57+
export const GET = handler
58+
export const POST = handler
59+
60+
// Hono
61+
app.all('/webhook', (c) => handler(c.req.raw))
62+
63+
// Express — keep the RAW body (a body-parser breaks signature verification)
64+
app.all('/webhook', express.raw({ type: '*/*' }), async (req, res) => {
65+
const request = new Request(`${req.protocol}://${req.get('host')}${req.originalUrl}`, {
66+
method: req.method,
67+
headers: req.headers as Record<string, string>,
68+
...(req.method === 'POST' ? { body: req.body as Buffer } : {}),
69+
})
70+
const r = await handler(request)
71+
res.status(r.status).send(await r.text())
72+
})
73+
```
74+
75+
In the Meta dashboard: **WhatsApp → Configuration → Webhook** → set Callback URL + Verify token,
76+
**subscribe to the `messages` field**. Meta expects a 200 within ~10s or it retries — zaileys acks
77+
immediately, so make handlers **idempotent** (dedupe by message id).
78+
79+
## Sending — same builder, cloud caveats
80+
81+
All of `send(jid).text/image/video/audio/document/sticker/location/contact/buttons/list`, `reply`,
82+
`react`, `forward`, `markRead` work (see [api.md](api.md)). Cloud differences:
83+
84+
- **Reply buttons max 3**; **carousels & polls not supported**; **media headers on buttons** unsupported.
85+
- **`markRead(messageId, { typing })`** — takes a message id (no chat cursor); optional typing indicator.
86+
- **Contacts need a first name** — vCard must have `FN:` + an `N:` line (zaileys derives it; error 131009 otherwise).
87+
- **AIRich (`rich: true`) is NOT supported** — it's a WhatsApp-Web-only proto. Throws a clear error;
88+
send plain text instead (WhatsApp renders `*bold*` `_italic_` `~strike~` `` ```mono``` `` natively).
89+
- **Media**: image ≤5MB, video ≤16MB, audio ≤16MB, document ≤100MB, sticker webp; URL must be public HTTPS or pass a Buffer/path.
90+
91+
```ts
92+
await wa.send(to).text('*Order confirmed* ✅')
93+
await wa.send(to).image('https://cdn/x.jpg', { caption: 'hi' })
94+
await wa.markRead(msg.chatId, { typing: true })
95+
```
96+
97+
## The 24-hour window — the #1 gotcha
98+
99+
Free-form sends (`text`/media/interactive) only work inside the **24h customer-service window** that
100+
opens when a user messages you. To a cold contact / after the window → **only approved templates**,
101+
else Graph error **131047**.
102+
103+
```ts
104+
await wa.send('628xxx').text('hi') // ❌ 131047 if user never texted you
105+
await wa.sendTemplate('628xxx', 'welcome', 'en_US') // ✅ business-initiated
106+
```
107+
108+
## Templates — `sendTemplate` + `wa.cloud.templates`
109+
110+
`sendTemplate(to, name, language, components?)` — takes the template **name**, not id. The number of
111+
`parameters` must exactly match the template's `{{n}}` or you get **132000**.
112+
113+
```ts
114+
await wa.sendTemplate(to, 'promo_juli', 'id', [
115+
{ type: 'body', parameters: [{ type: 'text', text: 'Budi' }] },
116+
])
117+
// image header:
118+
await wa.sendTemplate(to, 'flash_sale', 'id', [
119+
{ type: 'header', parameters: [{ type: 'image', image: { link: 'https://…/banner.jpg' } }] },
120+
{ type: 'body', parameters: [{ type: 'text', text: '50%' }] },
121+
])
122+
```
123+
124+
**OTP** (AUTHENTICATION templates are usually approved instantly):
125+
126+
```ts
127+
await wa.cloud.templates.create({
128+
name: 'kode_otp', category: 'AUTHENTICATION', language: 'id',
129+
components: [
130+
{ type: 'BODY', add_security_recommendation: true },
131+
{ type: 'FOOTER', code_expiration_minutes: 5 },
132+
{ type: 'BUTTONS', buttons: [{ type: 'OTP', otp_type: 'COPY_CODE' }] },
133+
],
134+
})
135+
await wa.sendTemplate(to, 'kode_otp', 'id', [
136+
{ type: 'body', parameters: [{ type: 'text', text: '839201' }] },
137+
{ type: 'button', sub_type: 'url', index: '0', parameters: [{ type: 'text', text: '839201' }] },
138+
])
139+
```
140+
141+
**Management** (needs `wabaId`):
142+
143+
```ts
144+
await wa.cloud.templates.list({ status: 'APPROVED', limit: 100 })
145+
await wa.cloud.templates.get('nama' | '1783414372642659') // by name OR numeric id → components
146+
await wa.cloud.templates.create({ name, category, language, components })
147+
await wa.cloud.templates.delete('nama')
148+
wa.on('template-status', (t) => console.log(t.name, t.event)) // APPROVED / REJECTED / PAUSED
149+
```
150+
151+
## Marketing campaigns
152+
153+
Create+approve template → broadcast → track. Marketing is subject to Meta's per-user daily cap +
154+
quality tiers (250 → unlimited by tier); watch `wa.cloud.info().quality_rating`.
155+
156+
```ts
157+
for (const { phone, name } of contacts) {
158+
await wa.sendTemplate(phone, 'promo_juli', 'id', [{ type: 'body', parameters: [{ type: 'text', text: name }] }])
159+
}
160+
wa.on('message-status', (s) => console.log(s.id, s.status)) // sent/delivered/read/failed
161+
const stats = await wa.cloud.analytics.messages({ start, end })
162+
```
163+
164+
## `wa.cloud.*` — full management surface (needs `wabaId` where noted)
165+
166+
```ts
167+
// account
168+
await wa.cloud.info() // display_phone_number, verified_name, quality_rating, throughput
169+
await wa.cloud.phoneNumbers() // all numbers on the WABA
170+
171+
// business profile
172+
await wa.cloud.profile.get()
173+
await wa.cloud.profile.update({ about, address, description, email, websites, vertical })
174+
175+
// whatsapp flows
176+
await wa.cloud.flows.list()
177+
await wa.cloud.flows.send(to, { flowId, cta, bodyText, screen, flowToken?, data?, headerText?, footerText?, mode?, action? })
178+
wa.on('flow-response', (f) => console.log(f.response)) // parsed nfm_reply
179+
180+
// catalog & commerce
181+
await wa.cloud.commerce.catalogs()
182+
await wa.cloud.commerce.products(catalogId)
183+
await wa.cloud.commerce.sendProduct(to, { catalogId, retailerId, bodyText?, footerText? })
184+
await wa.cloud.commerce.sendProductList(to, { catalogId, headerText, bodyText, sections: [{ title, productIds }] })
185+
wa.on('order', (o) => console.log(o.items)) // { productRetailerId, quantity, price, currency }[]
186+
187+
// address request (ID/BR only)
188+
await wa.cloud.sendAddressRequest(to, { bodyText, countryIso: 'ID' })
189+
190+
// blocklist / qr / analytics
191+
await wa.cloud.blocklist.add(['628xxx']); await wa.cloud.blocklist.remove(['628xxx']); await wa.cloud.blocklist.list()
192+
await wa.cloud.qr.create('prefilled msg', 'PNG'); await wa.cloud.qr.list(); await wa.cloud.qr.delete(code)
193+
await wa.cloud.analytics.conversations({ start, end, granularity: 'DAILY' })
194+
await wa.cloud.analytics.messages({ start, end, granularity: 'DAY' })
195+
196+
// phone-number management (touches live registration — use with care)
197+
await wa.cloud.phone.register(pin); await wa.cloud.phone.deregister()
198+
await wa.cloud.phone.requestCode('SMS' | 'VOICE', 'id'); await wa.cloud.phone.verifyCode(code)
199+
```
200+
201+
`start`/`end` are Unix seconds. WABA-scoped calls throw `ZaileysCloudError('CONFIG')` without `wabaId`.
202+
Accessing `wa.cloud` on the baileys provider throws.
203+
204+
## Events
205+
206+
Shared events fire the same (see [api.md](api.md)). Cloud-only:
207+
208+
| Event | Payload |
209+
| --- | --- |
210+
| `message-status` | `{ id, status: 'sent'\|'delivered'\|'read'\|'failed', recipientId, timestamp, error? }` |
211+
| `template-status` | `{ name, event, id, language?, reason? }` |
212+
| `flow-response` | `{ name, response, senderId, senderName?, id, timestamp }` |
213+
| `order` | `{ catalogId, items: {productRetailerId,quantity,price,currency}[], senderId, ... }` |
214+
215+
## What's NOT on cloud → throws `UNSUPPORTED_ON_CLOUD`
216+
217+
`group`, `community`, `newsletter`, `privacy`, `presence`, `chat`, `contact`, `business`, `profile`
218+
(the domain modules), plus `edit`, `delete`, `pin`, `setDisappearing`. Also: carousels, polls,
219+
AIRich, status/stories. All throw `ZaileysProviderError('UNSUPPORTED_ON_CLOUD')` immediately — never a
220+
silent no-op. For these, use the unofficial provider.
221+
222+
## Cloud error codes (see [errors.md](errors.md))
223+
224+
`131047` re-engagement (outside 24h → use a template) · `132000` param count mismatch · `131009`
225+
contact name · `190` token expired (use a permanent System User token) · `131026` undeliverable ·
226+
`131056` pair rate limit. All surface as `ZaileysCloudError` carrying the Graph code.

0 commit comments

Comments
 (0)