Codync
免费开源的 1:1 Grok Bot 与 Muse 替代方案。可在 iPhone、Mac 或 Linux 桌面上将 Claude 代码、Codex、Cursor 以及 40 多个编码代理作为持久机器人使用。支持群组聊天、线程、应用。
AI 翻译
尚未收集仓库信息。
尚未检测到更新。GitHub 发布版本后会显示在这里。
WhatsApp Business Platform for Rust: typed Cloud API client, webhooks, Embedded Signup, templates, OTP, adapters, Typst documents
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗WhatsApp Business Platform for Rust — a typed client for Meta's Cloud API and Business Management API, webhooks, pluggable storage/transport/sink adapters, and Typst-rendered documents.
Built for three jobs:
What is implemented, and what is not, is in
docs/coverage.md; the design is
docs/architecture.md. The API reference is the
rustdoc: cargo doc -p meta-whatsapp-rs --all-features --open.
meta-whatsapp-rs is not on crates.io (see Naming); depend on it from git,
pinned to a commit:
[dependencies]
meta-whatsapp-rs = { git = "https://github.com/vaam-apps/meta-whatsapp-rs", rev = "<commit>", features = ["axum", "postgres"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
axum and sqlx types are part of the API (Router, PgPool), so use the
versions meta-whatsapp-rs was built with, re-exported: meta_whatsapp_rs::webhooks::axum
(feature axum) and meta_whatsapp_rs::adapters::store::postgres::sqlx (feature
postgres), as the examples do. If you need axum features meta-whatsapp-rs does not
turn on, add axum = "0.8" with them yourself: Cargo builds one axum 0.8
for both, so the types still match.
use meta_whatsapp_rs::prelude::*; brings in the client, ids, Recipient, the
message and template builders, the webhook pieces, the storage and sink
ports, and the inbox. The snippets below are excerpts of the runnable
programs in crates/meta-whatsapp-rs/examples/ (a test
keeps them in sync); there, env("X") is std::env::var("X") with the
variable's name in the error. Each example's header lists the variables it
reads and the command that runs it.
From send_message.rs. A
free-form message only reaches a customer who wrote to you in the last 24
hours; a template reaches anyone who opted in. Errors are branched on
ErrorKind, never on their text.
let client = meta_whatsapp_rs::client(env("WA_TOKEN")?)?;
let messages = client.messages(PhoneNumberId::new(env("WA_PHONE_NUMBER_ID")?)?);
let to = Recipient::phone(env("WA_TO")?); // E.164, with `+`
let text = OutboundMessage::text(to.clone(), "Your order has shipped.");
match messages.send(&text).await {
Ok(sent) => println!("text accepted as {:?}", sent.message_id()),
Err(e) if e.kind() == ErrorKind::CustomerServiceWindowClosed => println!("window closed"),
Err(e) => return Err(e.into()),
}
let hello = TemplateMessage::new(
env_or("WA_TEMPLATE", "hello_world"),
env_or("WA_TEMPLATE_LANGUAGE", "en_US"),
);
let mut template = OutboundMessage::template(to, hello);
if let Ok(id) = std::env::var("WA_MESSAGING_ACCOUNT_ID") {
template = template.messaging_account_id(WabaId::new(id)?); // which account pays
}
let sent = messages.send(&template).await?;
invoice_document.rs renders
an invoice PDF with Typst, uploads it and sends it as a document.
From embedded_signup.rs, a
server with the Facebook JavaScript SDK page. It is a Tech Provider
server (each merchant adds a payment method); a Solution Partner, whose
credit line pays, onboards with onboard_with_approval instead: see
Solution Partner mode.
When a merchant starts, bind the attempt to them and give the page the
FB.login options:
let state = signup.sessions.start(&tenant, ATTEMPT_TTL).await?;
let launch_options = LaunchOptions::new(signup.config_id.as_str()).to_json()?;
When the page posts back the code, the WA_EMBEDDED_SIGNUP event and the
number's two-step verification PIN (the merchant's own, typed into the page),
redeem the state for the merchant your own authentication says is calling —
never a tenant named by the page or the URL — then onboard: exchange the
code, check the WABA and number with Meta, store the token encrypted,
subscribe the app, register the number.
// Exactly once, and only for the merchant who started the attempt.
if !signup.sessions.redeem(&state, &tenant).await? {
return Err(ApiError::StaleAttempt);
}
let onboarded = signup.onboarding.onboard(&request, &signup.vault).await;
From cms_inbox.rs. Webhook events
are verified, deduplicated, recorded per conversation and published for
the live view:
let (live, _) = broadcast::channel(256);
let sink = FanoutSink::new()
.with(InboxSink::new(conversations.clone()))
.with(BroadcastSink::from_sender(live.clone()));
let handler = WebhookHandler::builder(
SignatureVerifier::new(vec![app_secret])?, // X-Hub-Signature-256
verify_token,
Arc::new(sink),
)
.dedup(DedupGuard::new(kv)) // Meta retries for 7 days: record each event once
.build();
handler.check()?; // a blank verify token stops the boot, not Meta's verification
let webhook = meta_whatsapp_rs::webhooks::router(Arc::new(handler)); // GET verify, POST deliver
Every /inbox route first asks who is calling (a bearer token in the
example, your session in your CMS) and whether that merchant owns the
number; only then is the merchant's token taken from the vault. Replies go
out as that merchant, and only inside the 24-hour window:
// Your own table says which numbers this tenant owns; ask it before
// touching the vault, whose tokens belong to every merchant.
if !state.tenants.owns(tenant, &number) {
return Err(ApiError::Forbidden);
}
// The business token Embedded Signup stored for this number's WABA.
let Some(merchant) = state.vault.get_by_phone_number(&number).await? else {
return Err(ApiError::NotConnected);
};
let key = inbox.key(body.contact);
// Refused locally, before any request, outside the 24-hour window.
let sent = inbox.reply(&key, Text::new(body.text).into()).await?;
Both servers listen on 127.0.0.1 unless WA_BIND says otherwise, and
refuse to start without WA_TENANTS, the stand-in for your authentication
(bearer token → tenant, and tenant → phone numbers for the inbox). Replace
it with your own sessions and tenant table; keep the checks where they are.
From otp_login.rs. Codes are stored
as keyed hashes and bound to the sending number and the required
OtpConfig::namespace (the tenant the codes are for, so tenants sharing a
number never share codes), issuing is rate-limited per number, and verify
attempts are counted atomically:
let otp = OtpService::new(
meta_whatsapp_rs::client(env("WA_TOKEN")?)?,
PhoneNumberId::new(env("WA_PHONE_NUMBER_ID")?)?,
OtpTemplate::new(env("WA_OTP_TEMPLATE")?, env_or("WA_OTP_LANGUAGE", "en_US")),
Arc::new(MemoryKvStore::new()), // Postgres or Redis with several instances
Arc::new(SystemClock),
OtpPepper::new(env("WA_OTP_PEPPER")?)?, // >= 32 bytes, not stored with the codes
OtpConfig::new(env("WA_OTP_NAMESPACE")?), // the tenant: required, never a default
)?;
let user = Recipient::phone(env("WA_TO")?); // strict E.164, with `+`
let issued = otp.issue(&user, PURPOSE).await; // Sent, CoolingDown or RateLimited
let verified = otp.verify(&user, PURPOSE, code.trim()).await?; // counts as an attempt
A number that answers /commands, button taps and free text: the bot
feature's Bot is an EventSink behind the same webhook handler, with
prefixes and aliases, private/group/owner-only guards, a banned list,
per-user cooldowns, middleware, one compiled-in plugin per feature, a
generated /help, Meta's command menu, and Markdown replies converted to
WhatsApp formatting. Its Broadcast sends one message to many
recipients, each person once, paced per number under Meta's throughput,
with progress, a cancel and a report per recipient. Guide: docs/guides/bots.md.
| Example | What | Command |
|---|---|---|
send_message | a text, then a template | cargo run -p meta-whatsapp-rs --example send_message |
invoice_document | Typst invoice → upload → document message | cargo run -p meta-whatsapp-rs --example invoice_document --features typst |
embedded_signup | onboarding server and launch page | WA_TENANTS=… WA_APP_ID=… WA_APP_SECRET=… WA_ES_CONFIG_ID=… cargo run -p meta-whatsapp-rs --example embedded_signup --features axum |
cms_inbox | webhook endpoint, inbox, SSE, replies | WA_TENANTS=… WA_APP_SECRET=… WA_VERIFY_TOKEN=… cargo run -p meta-whatsapp-rs --example cms_inbox --features axum |
otp_login | issue and verify a code | cargo run -p meta-whatsapp-rs --example otp_login |
embedded_signup and cms_inbox refuse to start without WA_TENANTS
(their headers show a one-line setup) and listen on 127.0.0.1 unless
WA_BIND says otherwise. Add postgres to the features and set DATABASE_URL to
run them on Postgres; with the same DATABASE_URL, WA_VAULT_KEY and
WA_TENANTS, the merchant you connect in the first is the one you chat as
in the second (list the connected number under that tenant's
phone_number_ids).
Apps in other stacks (a Medusa store, a CMS backend) use meta-whatsapp-rs
through meta-whatsapp-server, an HTTP service built on the library and
deployed next to them: one deployment per Meta app, many tenants, keys per
tenant or per platform, the /v1 REST API described by a committed OpenAPI
document (crates/meta-whatsapp-server/openapi/v1.json).
Milestone M1 is here: tenants, keys, the admin API, the platform's own
WABAs, numbers and business profiles, vault key rotation, sending
messages, media and templates (with idempotency keys and rate limits),
Meta's webhooks into the inbox and an event outbox polled with
GET /v1/events, health, metrics. The inbox routes, live events,
webhooks to your backend, Embedded Signup, OTP, the Docker image and the
TypeScript client come in the next milestones
(docs/design/server.md, section 9).
cargo build --release -p meta-whatsapp-server
export DATABASE_URL=postgres://… WA_APP_SECRET=… WA_VERIFY_TOKEN=…
export WA_VAULT_KEY="$(openssl rand -base64 32)" WA_OTP_PEPPER="$(openssl rand -hex 32)" # keep both
./target/release/meta-whatsapp-server admin create-admin-key # printed once
./target/release/meta-whatsapp-server serve # 127.0.0.1:8080 (Meta), :8081 (API)
It refuses to start on an unsafe setting (a blank secret, no vault key,
memory storage in production); meta-whatsapp-server doctor lists every
such setting at once and checks the database, the app's webhook
subscription and the tokens' reach, read-only. Run, configure, create tenants and keys,
make a first call: docs/guides/server.md. For the
coding agents of those apps: the meta-whatsapp-rs-server skill.
| Feature | Default | Adds |
|---|---|---|
reqwest | yes | adapters::http::ReqwestTransport (rustls, HTTP/2) and the meta_whatsapp_rs::client() / meta_whatsapp_rs::client_builder() shortcuts |
memory | yes | MemoryKvStore, MemoryConversationStore: tests, development, one instance |
sinks | yes | channel, broadcast, fan-out, filter, fn and tracing sinks |
postgres | PostgresKvStore, PostgresConversationStore, embedded migrations (sqlx) | |
sqlite | SqliteKvStore, SqliteConversationStore, embedded migrations (sqlx, SQLite compiled in): one process on one machine | |
redis | RedisKvStore, RedisConversationStore, and RedisTls for rediss:// (aws-lc-rs, never the process default) | |
axum | webhooks::router (webhook endpoint) and webhooks::sse (live inbox stream) | |
typst | meta_whatsapp_rs::typst: invoice, receipt and voucher templates → PDF/PNG | |
flows-endpoint | WhatsApp Flows data-endpoint crypto (aws-lc-rs) | |
ffmpeg | adapters::media::FfmpegConverter: media conversion (a recording into a voice message's Ogg Opus, an image into a sticker's WebP or under the image limit) by an ffmpeg you install; nothing is linked | |
bot | meta_whatsapp_rs::bot: a bot framework (commands, guards, cooldowns, middleware, plugins, Markdown replies, paced broadcasts) | |
testing | core::testing::ScriptedTransport for your own tests (enable in [dev-dependencies]) | |
full | all of the above |
| Crate | Role |
|---|---|
meta-whatsapp-rs | Facade: depend on this. Re-exports the others (meta_whatsapp_rs::client, webhooks, adapters, core, typst, bot), a prelude, the CMS inbox, and the client() shortcut. Feature flags pick adapters. |
meta-whatsapp-core | Error tree, ids, secrets, ports (HttpTransport, KvStore, ConversationStore, EventSink, Clock). |
meta-whatsapp-client | Graph API client, one module per endpoint family; OTP service, Embedded Signup onboarding and token vault. |
meta-whatsapp-webhooks | Signature/verify-token checks, typed payloads, normalized events, dedup, axum router + SSE. |
meta-whatsapp-adapters | reqwest transport; memory, Postgres, Redis stores; channel/broadcast/fan-out sinks. |
meta-whatsapp-typst | Typst → PDF/PNG (invoices, receipts, vouchers) for document and image messages. |
meta-whatsapp-bot | Bot framework over webhooks: commands, guards, cooldowns, middleware, compile-time plugins, Markdown → WhatsApp formatting, paced broadcasts. |
meta-whatsapp-server | The HTTP service (a binary, not a dependency): tenants, keys, the /v1 API over the facade. See Not writing Rust? Run the service. |
meta-whatsapp-server-core | The service's framework-free core (not a dependency either): its domain, authorization order, error model as data, and the storage ports its memory and Postgres backends implement. No axum, sqlx or utoipa. |
meta-whatsapp-server-http, -api-axum, -store-memory, -store-postgres | The crates the service's binary composes by its Cargo features: the HTTP layer (listeners, authentication, Meta's webhook, media, operations), the /v1 API and its OpenAPI document, and the memory and Postgres backends. |
just # list recipes
just ci # the gate CI runs: lint, check, test, skills-check, skills-ts, doc, features, deny, test-live
just test # unit and in-process tests (live adapter tests skip)
just test-live # adapter, inbox, bot retention and service tests against real Postgres, Redis and MongoDB
just meta-docs # mirror Meta's docs locally (gitignored) for grep
Open the repo in the dev container (.devcontainer/) for a sandboxed
Claude Code environment: pinned toolchain, default-deny egress firewall,
Postgres, Redis and MongoDB sidecars.
Agents: see AGENTS.md. Claude Code project skills and agents
live in .claude/.
Coding agents in the repositories that use meta-whatsapp-rs (the store, the CMS) get
consumer skills from skills/: 28 small, task-shaped
skills (meta-whatsapp-rs is the map; meta-whatsapp-rs-send-messages, meta-whatsapp-rs-webhook-endpoint,
meta-whatsapp-rs-otp-login, meta-whatsapp-rs-cms-inbox, …). Install all of them with
npx skills add vaam-apps/meta-whatsapp-rs, or a subset with
npx skills add vaam-apps/meta-whatsapp-rs -s meta-whatsapp-rs -s meta-whatsapp-rs-cms-inbox. Each is stamped
with the meta-whatsapp-rs commit it was verified against, and just ci keeps them
true: their Rust blocks are excerpts of example files it compiles and
tests, and every Rust name they use must exist. A public API change
updates them in the same PR.
The project was called wa-rs until 2026-09-25, a name taken on crates.io
by an unrelated project; the owner renamed it (the CHANGELOG's "Renamed"
section maps every old name to its new one). The meta-whatsapp-* crate
names were free on crates.io that day, but nothing is published: the
workspace stays publish = false until a release is decided.
| Read | For |
|---|---|
| docs/guides/ | integrator guides: Meta setup, Embedded Signup, webhooks, CMS inbox, marketing, OTP login, documents, bots, production, the HTTP service |
| docs/coverage.md | what is implemented, per Meta feature |
| docs/parity.md | capability by capability against Zaileys and Meta's Cloud API, with the status of each |
| docs/categories.md | what is implemented, per Meta platform category |
| docs/roadmap.md | the plan to parity, in pull-request-sized items, and what waits for the owner |
| docs/architecture.md | the design spec: ports, error tree, security rules |
| OPEN_QUESTIONS.md | product decisions, all but one decided on 2026-09-26, and what the code does today (read before production) |
| CONTRIBUTING.md | setup, the just ci gate, review pipeline, docs/skills parity |
| docs/dev-environment.md | the Claude Code dev container and its firewall |
| CHANGELOG.md |
MIT
| what changed |
| skills/ | agent skills for code that uses meta-whatsapp-rs |
| docs/public/llms.txt | an index of these docs and skills for language models (the llms.txt convention), served at the docs site's root |
just docs-mcp | these docs and skills served to a coding agent over MCP (stdio): search them, read a page or one section |
cargo doc --open -p meta-whatsapp-rs --all-features | the API reference |