abs-zalo-bot 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +48 -0
- package/LICENSE +21 -0
- package/README.md +435 -0
- package/SECURITY.md +22 -0
- package/config/bots.example.json +33 -0
- package/config.toml +48 -0
- package/mcp/run.sh +7 -0
- package/mcp/server.js +255 -0
- package/package.json +76 -0
- package/scripts/battle-smoke.js +118 -0
- package/scripts/corpus-stats.js +23 -0
- package/scripts/probe-history.js +76 -0
- package/scripts/public-gate.js +216 -0
- package/scripts/setup.js +233 -0
- package/src/abs_telemetry.js +60 -0
- package/src/ask.js +7 -0
- package/src/backfill.js +364 -0
- package/src/bot_registry.js +180 -0
- package/src/brand.js +31 -0
- package/src/cli.js +268 -0
- package/src/commands.js +265 -0
- package/src/config.js +223 -0
- package/src/digest.js +330 -0
- package/src/discovery.js +185 -0
- package/src/hermes_client.js +302 -0
- package/src/inbound_router.js +295 -0
- package/src/keepalive.js +375 -0
- package/src/oa_adapter.js +284 -0
- package/src/oa_auto_reply.js +75 -0
- package/src/oa_policy.js +32 -0
- package/src/oa_webhook.js +95 -0
- package/src/onboarding.js +102 -0
- package/src/ops_report.js +135 -0
- package/src/policy.js +320 -0
- package/src/privacy.js +38 -0
- package/src/schema.js +163 -0
- package/src/server.js +604 -0
- package/src/store.js +972 -0
- package/src/zalo_runtime.js +480 -0
package/.env.example
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Local only. Never commit real values. Never paste secrets into chat.
|
|
2
|
+
PORT=3871
|
|
3
|
+
HOST=127.0.0.1
|
|
4
|
+
DATA_DIR=./data
|
|
5
|
+
DEFAULT_ACCOUNT_ID=default
|
|
6
|
+
DASHBOARD_TOKEN=change-me
|
|
7
|
+
OWNER_USER_IDS=
|
|
8
|
+
# Optional Hermes analysis webhook (internal only)
|
|
9
|
+
HERMES_WEBHOOK_URL=
|
|
10
|
+
HERMES_WEBHOOK_TOKEN=
|
|
11
|
+
# Hermes Agent brain (OpenAI-compatible API server on :8642)
|
|
12
|
+
HERMES_API_BASE=http://127.0.0.1:8642/v1
|
|
13
|
+
HERMES_API_SERVER_KEY=
|
|
14
|
+
HERMES_API_MODEL=hermes-agent
|
|
15
|
+
# Digest cron minutes; 0 = disabled auto digest
|
|
16
|
+
DIGEST_INTERVAL_MINUTES=0
|
|
17
|
+
# Global kill switch
|
|
18
|
+
BRIDGE_PAUSED=false
|
|
19
|
+
# 24/7 listener + auto-reconnect (no deep-scroll)
|
|
20
|
+
KEEPALIVE_AUTO_CONNECT=true
|
|
21
|
+
KEEPALIVE_TICK_MS=20000
|
|
22
|
+
KEEPALIVE_RECONNECT_BASE_MS=5000
|
|
23
|
+
KEEPALIVE_RECONNECT_MAX_MS=300000
|
|
24
|
+
KEEPALIVE_ALERT_COOLDOWN_MS=900000
|
|
25
|
+
# Operator labels
|
|
26
|
+
ZALO_PHONE_LABEL=
|
|
27
|
+
DESTINATION_GROUP_NAME=
|
|
28
|
+
LISTEN_ALL_GROUPS=false
|
|
29
|
+
# QR onboarding: used only to print safe browser/tunnel instructions.
|
|
30
|
+
# These are not credentials and do not expose the QR image itself.
|
|
31
|
+
QR_LOCAL_PORT=13871
|
|
32
|
+
DASHBOARD_SSH_USER=user
|
|
33
|
+
DASHBOARD_SSH_HOST=your-vps-host
|
|
34
|
+
# Optional: authenticated HTTPS reverse-proxy origin, e.g. https://bridge.example.test
|
|
35
|
+
DASHBOARD_PUBLIC_URL=
|
|
36
|
+
|
|
37
|
+
# ── Brain endpoint cho Zalo OA auto-reply (tuỳ chọn) ───────────────────────────
|
|
38
|
+
# Trỏ tới agent của CHÍNH BẠN: Hermes, n8n, hay bất kỳ API tương thích nào.
|
|
39
|
+
# Bỏ trống = tắt auto-reply. Không có giá trị mặc định để lead không chảy ra ngoài.
|
|
40
|
+
ABS_BRAIN_CHAT_URL=
|
|
41
|
+
ABS_BRAIN_TOKEN=
|
|
42
|
+
# Tên header, chỉ đổi khi backend của bạn yêu cầu khác
|
|
43
|
+
ABS_BRAIN_TOKEN_HEADER=x-abs-token
|
|
44
|
+
ABS_BRAIN_ID_HEADER=x-abs-external-id
|
|
45
|
+
|
|
46
|
+
# ── Telemetry (tuỳ chọn) ──────────────────────────────────────────────────────
|
|
47
|
+
# Đường dẫn tới ledger script của bạn. Bỏ trống = tắt telemetry.
|
|
48
|
+
ABS_LEDGER_PATH=
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Zalo Bridge contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
# ABS Zalo Bot
|
|
2
|
+
|
|
3
|
+
**Install once · run from a dashboard button or one command · agents read the repo and know how to work in it**
|
|
4
|
+
|
|
5
|
+
A local/VPS Zalo bridge for Hermes and coding agents:
|
|
6
|
+
|
|
7
|
+
- **Zalo Personal QR**: listens to allowlisted sources, stores data locally, and posts digests to one destination. This is the *unofficial* path — internal/demo use with a dedicated account only.
|
|
8
|
+
- **Zalo OA**: the official adapter, kept separate from Personal QR, and the right choice for a customer-facing bot.
|
|
9
|
+
- **Policy Guard**: fail-closed by default. Nothing is sent until it is configured.
|
|
10
|
+
- **Dashboard**: a non-coder can connect the QR, pick sources, pick a destination, and flip the kill switch.
|
|
11
|
+
- **MCP**: agents can read status, groups, users and corpus, and ask for the destination through a safe contract.
|
|
12
|
+
|
|
13
|
+
> Just want it running for the owner? Read the **[Non-coder quickstart](docs/quickstart-non-coder.md)**.
|
|
14
|
+
>
|
|
15
|
+
> Handing this repo to Claude Code, Codex, Hermes or any other agent? The agent must read **[AGENTS.md](AGENTS.md)** and the **[Agent handoff](docs/agent-handoff.md)** before running any command.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Tóm tắt tiếng Việt
|
|
20
|
+
|
|
21
|
+
Repo này là cầu nối Zalo cho agent. Hai đường tách biệt: **Personal QR** (không chính thức, dùng account riêng, chỉ nội bộ/demo) và **Zalo OA** (chính thức, dùng cho khách hàng). Mặc định **fail-closed** — chưa cấu hình thì không gửi gì cả.
|
|
22
|
+
|
|
23
|
+
Cài nhanh: `bash setup.sh` rồi `npm start`, mở `http://127.0.0.1:3871`. Bắt đầu ở chế độ `listen_only`, kiểm tra dữ liệu đúng rồi mới mở thêm.
|
|
24
|
+
|
|
25
|
+
Ba điều tuyệt đối không làm: không dán secret/OTP/PIN vào chat cho agent, không dùng account Zalo cá nhân chính để automation, không mở port 3871 thẳng ra Internet.
|
|
26
|
+
|
|
27
|
+
Tài liệu còn lại viết bằng tiếng Anh để mọi agent đọc được. Câu lệnh và tên biến giữ nguyên, đọc lướt vẫn theo được.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Documentation map
|
|
32
|
+
|
|
33
|
+
Start here, in this order:
|
|
34
|
+
|
|
35
|
+
| File | What it is |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `README.md` | This file — install, run, and what the project does |
|
|
38
|
+
| `docs/quickstart-non-coder.md` | Fastest path if you do not write code |
|
|
39
|
+
| `AGENTS.md` | Safety boundary every AI agent must follow |
|
|
40
|
+
| `docs/install.md` · `docs/configuration.md` | Setup and environment variables |
|
|
41
|
+
| `docs/personal-qr.md` · `docs/official-oa.md` | The two Zalo adapters |
|
|
42
|
+
| `docs/operations.md` · `docs/troubleshooting.md` | Running it day to day |
|
|
43
|
+
| `SECURITY.md` · `CONTRIBUTING.md` | Reporting issues and sending changes |
|
|
44
|
+
| `docs/internal/` | Design, spec, plan and verification notes kept for maintainers |
|
|
45
|
+
|
|
46
|
+
## Works with any AI coding tool
|
|
47
|
+
|
|
48
|
+
This repo follows the `AGENTS.md` convention that Codex, Cursor, Zed and other agents already read.
|
|
49
|
+
Each tool has its own entrypoint file; they all point at the same rule set:
|
|
50
|
+
|
|
51
|
+
| Tool | Entrypoint | How to start |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| Claude Code (Sonnet/Opus) | `CLAUDE.md` | open repo, run `claude` |
|
|
54
|
+
| Codex CLI (GPT) | `CODEX.md` + `AGENTS.md` | open repo, run `codex` |
|
|
55
|
+
| Antigravity / Gemini CLI | `GEMINI.md` | open repo, run the agent |
|
|
56
|
+
| Hermes Agent | `HERMES.md` + `SKILL.md` | `cp -r . ~/.hermes/skills/abs-zalo-bot` or attach via MCP |
|
|
57
|
+
| Cursor | `.cursorrules` | auto-loaded on open |
|
|
58
|
+
| GitHub Copilot | `.github/copilot-instructions.md` | auto-loaded in VS Code |
|
|
59
|
+
| Any other agent | `AGENTS.md` | drop the file into chat |
|
|
60
|
+
|
|
61
|
+
Same bootstrap for every tool:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
node --version # requires >= 22.5
|
|
65
|
+
npm ci
|
|
66
|
+
npm run doctor
|
|
67
|
+
npm test
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 1. Pick the right path before installing
|
|
71
|
+
|
|
72
|
+
| What you need | Pick | Notes |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| Listen to internal groups and digest them into one ops group | Personal QR | Unofficial. Use a dedicated account. No spam, no broadcast |
|
|
75
|
+
| Talk to customers: FAQ, leads, booking, official notices | Zalo OA | The official path. Needs OA credentials and an HTTPS webhook |
|
|
76
|
+
| Not sure yet | Start with Personal QR in `listen_only` | No automatic sending. Inspect the data first |
|
|
77
|
+
|
|
78
|
+
**Do not use your main personal Zalo account for automation.** Never auto-enter OTP/PIN, never bypass login, never broadcast.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 2. Quick install — no coding required
|
|
83
|
+
|
|
84
|
+
### Requirements
|
|
85
|
+
|
|
86
|
+
- A Linux or macOS machine.
|
|
87
|
+
- Node.js **22.5 or newer**.
|
|
88
|
+
- A dedicated Zalo account if you use Personal QR.
|
|
89
|
+
- No API key is needed to run Personal QR in local listen/digest mode.
|
|
90
|
+
|
|
91
|
+
### Are a VPS, a model, and Telegram required?
|
|
92
|
+
|
|
93
|
+
- **VPS:** not required to try it or run locally. Only needed for 24/7 operation. For an MVP, start around 1 vCPU / 1 GB RAM / 10 GB disk, and put HTTPS plus auth in front of it if it is reachable from outside. That is a starting point for operations, not a load guarantee.
|
|
94
|
+
- **Model/API key:** not required for `listen_only` or the local fallback. You only need a Hermes API compatible with OpenAI's `/v1/chat/completions` if you want an LLM to analyse or rewrite digests. This repo does not install a model and does not issue keys.
|
|
95
|
+
- **Telegram:** not required for the core bridge. The QR is shown in the dashboard on the local machine or VPS; this version does not forward the QR image over Telegram.
|
|
96
|
+
- **Live QR:** there are real routes — `POST /api/accounts/:id/connect`, the `QRCodeGenerated` callback, `GET /api/accounts/:id/qr` — and the dashboard renders it. Scanning and confirming on the phone stays a manual action by the account owner.
|
|
97
|
+
|
|
98
|
+
If you do not know whether Node.js is installed, hand the agent exactly this:
|
|
99
|
+
|
|
100
|
+
> "Check whether this machine has Node.js 22.5+. If not, walk me through installing it. Do not enter any secret, OTP or PIN on my behalf."
|
|
101
|
+
|
|
102
|
+
### One install command
|
|
103
|
+
|
|
104
|
+
From the repo directory:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
bash setup.sh
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Setup will:
|
|
111
|
+
|
|
112
|
+
1. check Node.js;
|
|
113
|
+
2. create a local `.env` if missing;
|
|
114
|
+
3. create `config/bots.json` from the example if missing;
|
|
115
|
+
4. create the data directory with local-only permissions;
|
|
116
|
+
5. run `npm ci`;
|
|
117
|
+
6. run tests, config validation, secret scan, syntax check and self-check;
|
|
118
|
+
7. print the next step.
|
|
119
|
+
|
|
120
|
+
Setup does **not** log into Zalo, scan a QR, send messages, enter OTP/PIN, or touch real credentials.
|
|
121
|
+
|
|
122
|
+
For agents or CI that must run unattended:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
bash setup.sh --non-interactive
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
To set up and immediately run in the foreground:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
bash setup.sh --start
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Run the dashboard
|
|
135
|
+
|
|
136
|
+
If you did not use `--start`:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
npm start
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Then open:
|
|
143
|
+
|
|
144
|
+
```text
|
|
145
|
+
http://127.0.0.1:3871
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The dashboard binds to localhost by default. On a remote machine or VPS, **do not expose the port directly to the Internet** — use an HTTPS reverse proxy with its own token/auth.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 3. First Personal QR connection
|
|
153
|
+
|
|
154
|
+
1. Run `npm start`.
|
|
155
|
+
2. Open the dashboard.
|
|
156
|
+
3. Click **Connect QR** — the dashboard calls the live route and renders the QR image.
|
|
157
|
+
4. Scan it from your phone using a **dedicated account**.
|
|
158
|
+
5. Wait for status `connected`.
|
|
159
|
+
6. Click **Scan group IDs** if the group list needs refreshing.
|
|
160
|
+
7. Pick one group as the **destination group**.
|
|
161
|
+
8. Add sources under **Allowlist source**.
|
|
162
|
+
9. Start in `listen_only`.
|
|
163
|
+
10. Only after the stored data looks right, move one source to `digest_only`.
|
|
164
|
+
|
|
165
|
+
What the modes mean:
|
|
166
|
+
|
|
167
|
+
- `off`: source disabled.
|
|
168
|
+
- `listen_only`: store only. No digest, no send.
|
|
169
|
+
- `digest_only`: included in digests on request or on schedule.
|
|
170
|
+
- `alert_only`: only high-priority candidates are considered for alerts.
|
|
171
|
+
- `mention_only`: still blocked from replying while `READ_ONLY_SOURCE=true`.
|
|
172
|
+
- `reply_enabled`: does not override Policy Guard on its own. Use only with a reviewed policy.
|
|
173
|
+
|
|
174
|
+
**Safe defaults:** sources are not replied to, DMs are not replied to, mentions are not replied to, and outbound goes only to the configured destination.
|
|
175
|
+
|
|
176
|
+
On a headless VPS, reach the dashboard over an SSH tunnel or an authenticated HTTPS reverse proxy. Do not open port `3871` to the Internet, and never put a QR or session into logs or chat.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## 4. Verify the install
|
|
181
|
+
|
|
182
|
+
None of these send a real message:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
npm run doctor
|
|
186
|
+
npm run status
|
|
187
|
+
npm test
|
|
188
|
+
npm run validate-config
|
|
189
|
+
npm run secret-scan
|
|
190
|
+
npm run syntax-check
|
|
191
|
+
npm run self-check
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
What to look for:
|
|
195
|
+
|
|
196
|
+
- `npm test`: everything passes.
|
|
197
|
+
- `npm run doctor`: no public/runtime config errors.
|
|
198
|
+
- `status`: never prints cookies, IMEI, refresh tokens or a raw session.
|
|
199
|
+
- `self-check`: an empty destination/allowlist is a normal fail-closed state.
|
|
200
|
+
|
|
201
|
+
Daemon smoke test:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
npm run smoke
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
`npm run smoke` only checks health, MCP and safety. It **does not send a real message** unless `BATTLE_SEND=true` is set.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## 5. Official OA
|
|
212
|
+
|
|
213
|
+
Personal QR and OA are two different paths. Never mix a Personal session with OA OAuth.
|
|
214
|
+
|
|
215
|
+
### Local preparation
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
cp config/bots.example.json config/bots.json
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Open `config/bots.json`, keep the bot on `draft_first`, and enable it only once valid credentials exist. That file is git-ignored and must not be committed.
|
|
222
|
+
|
|
223
|
+
Put credentials in `.env` or a local secret manager — **never in chat, issues, the README, or an agent prompt**. Only the variable names declared under `credential` are used:
|
|
224
|
+
|
|
225
|
+
- `ZALO_OA_APP_ID_DEMO_OA`
|
|
226
|
+
- `ZALO_OA_APP_SECRET_DEMO_OA`
|
|
227
|
+
- `ZALO_OA_REFRESH_TOKEN_DEMO_OA`
|
|
228
|
+
- `ZALO_OA_WEBHOOK_SECRET`
|
|
229
|
+
|
|
230
|
+
Real values never live in the public repo.
|
|
231
|
+
|
|
232
|
+
### Webhook
|
|
233
|
+
|
|
234
|
+
Adapter route:
|
|
235
|
+
|
|
236
|
+
```text
|
|
237
|
+
POST /webhooks/zalo/oa/:bot_id
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The handler validates ingress, normalises text events, and acknowledges quickly. It **does not call an AI and does not send a reply**. Any outbound must go through an external workflow or agent with its own approval and policy.
|
|
241
|
+
|
|
242
|
+
Production needs:
|
|
243
|
+
|
|
244
|
+
- an HTTPS reverse proxy;
|
|
245
|
+
- a signature check or an authenticated edge;
|
|
246
|
+
- a body size limit;
|
|
247
|
+
- logs limited to redacted event IDs/receipts;
|
|
248
|
+
- `draft_first` before enabling any send path.
|
|
249
|
+
|
|
250
|
+
Details: [docs/official-oa.md](docs/official-oa.md).
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## 6. Repo layout for agents and CLIs
|
|
255
|
+
|
|
256
|
+
```text
|
|
257
|
+
AGENTS.md non-negotiable rules for every agent
|
|
258
|
+
CLAUDE.md entrypoint for Claude Code
|
|
259
|
+
CODEX.md entrypoint/checklist for Codex CLI
|
|
260
|
+
README.md home page and quickstart
|
|
261
|
+
CONTRIBUTING.md contribution rules
|
|
262
|
+
SECURITY.md how to report a vulnerability
|
|
263
|
+
docs/internal/00-DESIGN.md architecture
|
|
264
|
+
docs/internal/01-SPEC.md I/O contract
|
|
265
|
+
docs/internal/02-PLAN.md build order
|
|
266
|
+
docs/internal/03-HARNESS.md offline test harness
|
|
267
|
+
docs/internal/04-VERIFY.md verification gate
|
|
268
|
+
docs/internal/05-CONTEXT.md runtime constraints
|
|
269
|
+
|
|
270
|
+
setup.sh / install.sh one-command install
|
|
271
|
+
scripts/setup.js setup + doctor + dashboard info
|
|
272
|
+
scripts/public-gate.js validate-config/secret-scan/syntax-check
|
|
273
|
+
|
|
274
|
+
src/ deterministic runtime
|
|
275
|
+
policy.js inbound/outbound guard
|
|
276
|
+
store.js local SQLite
|
|
277
|
+
zalo_runtime.js personal QR listener
|
|
278
|
+
bot_registry.js registry + credential references
|
|
279
|
+
oa_adapter.js official OA OAuth/send boundary
|
|
280
|
+
oa_webhook.js official OA ingress boundary
|
|
281
|
+
server.js dashboard/API/webhook routes
|
|
282
|
+
|
|
283
|
+
config/bots.example.json public example registry
|
|
284
|
+
config/bots.json local registry, git-ignored
|
|
285
|
+
.env.example example environment variables
|
|
286
|
+
.env local, git-ignored
|
|
287
|
+
|
|
288
|
+
data/ SQLite/session/QR, git-ignored
|
|
289
|
+
public/ dashboard static files
|
|
290
|
+
mcp/ MCP stdio facade
|
|
291
|
+
test/ offline tests
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## 7. Rules for agents, Claude Code and Codex
|
|
297
|
+
|
|
298
|
+
Agents work in this order:
|
|
299
|
+
|
|
300
|
+
```text
|
|
301
|
+
1. read AGENTS.md
|
|
302
|
+
2. read docs/agent-handoff.md
|
|
303
|
+
3. run npm run doctor
|
|
304
|
+
4. open only the relevant files — do not load the whole repo
|
|
305
|
+
5. make small changes and test immediately
|
|
306
|
+
6. report real evidence: command, exit code, file, test
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
An agent must **never**:
|
|
310
|
+
|
|
311
|
+
- ask the owner to paste an API key, refresh token, cookie, IMEI, session, OTP or PIN into chat;
|
|
312
|
+
- enter an OTP/PIN or confirm a third-party login on the owner's behalf;
|
|
313
|
+
- enable public send, broadcast, deploy, systemd or credential changes without approval;
|
|
314
|
+
- treat "code written" as "code ran" — every done claim needs runtime evidence;
|
|
315
|
+
- claim a GitHub publish without real auth and a real receipt;
|
|
316
|
+
- use an LLM to decide deterministic gates such as empty, duplicate, quota, policy or signature.
|
|
317
|
+
|
|
318
|
+
Full instructions: [docs/agent-handoff.md](docs/agent-handoff.md).
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## 8. Quick troubleshooting
|
|
323
|
+
|
|
324
|
+
### `Node.js 22.5+ is required`
|
|
325
|
+
|
|
326
|
+
Install a current Node.js LTS, open a new terminal, then:
|
|
327
|
+
|
|
328
|
+
```bash
|
|
329
|
+
node --version
|
|
330
|
+
bash setup.sh
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### The dashboard will not open
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
npm run doctor
|
|
337
|
+
npm start
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
If port 3871 is taken, use another one:
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
PORT=3872 npm start
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
Then open `http://127.0.0.1:3872`.
|
|
347
|
+
|
|
348
|
+
### The QR does not appear
|
|
349
|
+
|
|
350
|
+
- Check that `npm start` is still running.
|
|
351
|
+
- Click **Connect QR** again.
|
|
352
|
+
- Do not give a PIN/OTP to an agent — do it yourself on the phone/app.
|
|
353
|
+
- Use a dedicated account, not your main personal one.
|
|
354
|
+
|
|
355
|
+
### `not_connected` on digest/send
|
|
356
|
+
|
|
357
|
+
That is the safety block doing its job. Connect the QR, check the destination, then retry. Do not delete the session unless you intend to log in again.
|
|
358
|
+
|
|
359
|
+
### `destination_unset`
|
|
360
|
+
|
|
361
|
+
Open the dashboard and pick a destination group. Never set a destination from a guessed ID.
|
|
362
|
+
|
|
363
|
+
### `npm test` fails
|
|
364
|
+
|
|
365
|
+
Do not skip tests. Hand the agent this:
|
|
366
|
+
|
|
367
|
+
> "Run npm test, read the first failing test, find the cause in the related file/test, make the smallest fix, and run it again. Do not touch secrets or runtime data."
|
|
368
|
+
|
|
369
|
+
### Stop all outbound immediately
|
|
370
|
+
|
|
371
|
+
Click **Kill switch ON** in the dashboard, or:
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
curl -X POST http://127.0.0.1:3871/api/kill-switch \
|
|
375
|
+
-H 'content-type: application/json' \
|
|
376
|
+
-d '{"paused":true}'
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## 9. Stopping, backups and data
|
|
382
|
+
|
|
383
|
+
Stop a foreground run with `Ctrl+C`.
|
|
384
|
+
|
|
385
|
+
Local data lives in `data/` and is never committed. The Personal session is stored with restricted permissions. Never hand the database, session or QR to an agent or a third party without redacting it first.
|
|
386
|
+
|
|
387
|
+
Before a large config change:
|
|
388
|
+
|
|
389
|
+
```bash
|
|
390
|
+
cp .env .env.backup.local
|
|
391
|
+
cp config/bots.json config/bots.backup.local.json
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
Those local backups must not be committed either.
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## 10. Production checklist
|
|
399
|
+
|
|
400
|
+
An open dashboard is not production. You need all of:
|
|
401
|
+
|
|
402
|
+
- clear account/OA ownership;
|
|
403
|
+
- a reviewed allowlist and destination;
|
|
404
|
+
- green tests;
|
|
405
|
+
- secrets in a secret manager or env, never in Git;
|
|
406
|
+
- HTTPS + auth on any public endpoint;
|
|
407
|
+
- a kill switch you have actually tested;
|
|
408
|
+
- logs free of PII and credentials;
|
|
409
|
+
- systemd/Docker enabled only after approval;
|
|
410
|
+
- a live smoke test with real evidence;
|
|
411
|
+
- a tested rollback: pause, disconnect, disable bot.
|
|
412
|
+
|
|
413
|
+
The systemd file in this repo is a template. It does not enable itself.
|
|
414
|
+
|
|
415
|
+
---
|
|
416
|
+
|
|
417
|
+
## Next documents
|
|
418
|
+
|
|
419
|
+
- [Non-coder quickstart](docs/quickstart-non-coder.md)
|
|
420
|
+
- [Install guide](docs/install.md)
|
|
421
|
+
- [Agent handoff](docs/agent-handoff.md)
|
|
422
|
+
- [Personal QR](docs/personal-qr.md)
|
|
423
|
+
- [Official OA](docs/official-oa.md)
|
|
424
|
+
- [Commands](docs/commands.md)
|
|
425
|
+
- [Configuration](docs/configuration.md)
|
|
426
|
+
- [Operations](docs/operations.md)
|
|
427
|
+
- [Troubleshooting](docs/troubleshooting.md)
|
|
428
|
+
- [Release checklist](docs/release-checklist.md)
|
|
429
|
+
- [MCP notes](MCP.md)
|
|
430
|
+
|
|
431
|
+
## License
|
|
432
|
+
|
|
433
|
+
MIT. See [LICENSE](LICENSE).
|
|
434
|
+
|
|
435
|
+
**There is no "100% safe".** Official OA is the path to prefer for customers; Personal QR is unofficial, carries platform risk, and belongs only inside a boundary you have reviewed.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
Security fixes target the latest release on the default branch. Older snapshots may be unsupported.
|
|
6
|
+
|
|
7
|
+
## Reporting a vulnerability
|
|
8
|
+
|
|
9
|
+
Please do not open a public issue for an undisclosed vulnerability.
|
|
10
|
+
|
|
11
|
+
Use GitHub Security Advisories for this repository, or contact the repository maintainers through the private security channel configured by the hosting organization. Include:
|
|
12
|
+
|
|
13
|
+
- a short description and impact;
|
|
14
|
+
- affected version or commit;
|
|
15
|
+
- minimal reproduction steps that do not contain credentials or personal data;
|
|
16
|
+
- a suggested mitigation, if known.
|
|
17
|
+
|
|
18
|
+
Do not attach `.env` files, QR images, session files, cookies, IMEI values, OAuth tokens, database files, or raw Zalo exports. Redact all user IDs, group IDs, phone numbers, and message text before sharing evidence.
|
|
19
|
+
|
|
20
|
+
## Security boundaries
|
|
21
|
+
|
|
22
|
+
The personal QR adapter uses an unofficial protocol and can be affected by platform changes or account restrictions. It is not a ban-proof or production-safe substitute for the official OA path. Review the policy, rate limits, destination, and account isolation before enabling outbound behavior.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"bots": [
|
|
4
|
+
{
|
|
5
|
+
"bot_id": "demo-oa",
|
|
6
|
+
"tenant_id": "demo",
|
|
7
|
+
"name": "Demo Zalo OA",
|
|
8
|
+
"adapter": "zalo_oa",
|
|
9
|
+
"enabled": true,
|
|
10
|
+
"credential": {
|
|
11
|
+
"app_id_env": "ZALO_OA_APP_ID_DEMO_OA",
|
|
12
|
+
"app_secret_env": "ZALO_OA_APP_SECRET_DEMO_OA",
|
|
13
|
+
"refresh_token_env": "ZALO_OA_REFRESH_TOKEN_DEMO_OA"
|
|
14
|
+
},
|
|
15
|
+
"policy": {
|
|
16
|
+
"mode": "draft_first",
|
|
17
|
+
"allow_user_ids": []
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"bot_id": "demo-personal",
|
|
22
|
+
"tenant_id": "demo",
|
|
23
|
+
"name": "Demo Personal QR",
|
|
24
|
+
"adapter": "zalo_personal_qr",
|
|
25
|
+
"account_id": "demo-personal",
|
|
26
|
+
"enabled": false,
|
|
27
|
+
"policy": {
|
|
28
|
+
"mode": "draft_first",
|
|
29
|
+
"allow_user_ids": []
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
package/config.toml
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# ABS-Zalo-Bot demo-safe local configuration.
|
|
2
|
+
# Real destination/group/user IDs belong in runtime data or a private config.
|
|
3
|
+
|
|
4
|
+
default_account_id = "default"
|
|
5
|
+
phone_label = ""
|
|
6
|
+
retention_days = 30
|
|
7
|
+
|
|
8
|
+
# Personal adapter: full capture, absolute silence. No inbound message may
|
|
9
|
+
# invoke a model, skill, MCP workflow, command, digest, alert, or send.
|
|
10
|
+
read_only_source = true
|
|
11
|
+
listen_self = false
|
|
12
|
+
listen_all_groups = true
|
|
13
|
+
listen_dms = true
|
|
14
|
+
listener_only = true
|
|
15
|
+
auto_alert = false
|
|
16
|
+
auto_reply_default = false
|
|
17
|
+
|
|
18
|
+
dashboard_bind = "127.0.0.1"
|
|
19
|
+
dashboard_port = 3871
|
|
20
|
+
|
|
21
|
+
# Leave destination empty until the operator selects one in the private dashboard.
|
|
22
|
+
[destination]
|
|
23
|
+
account_id = "default"
|
|
24
|
+
group_id = ""
|
|
25
|
+
group_name = ""
|
|
26
|
+
|
|
27
|
+
[rate_limit]
|
|
28
|
+
messages_per_hour = 20
|
|
29
|
+
messages_per_day = 120
|
|
30
|
+
ingest_per_hour = 2000
|
|
31
|
+
destination_per_hour = 30
|
|
32
|
+
alert_cooldown_minutes = 30
|
|
33
|
+
|
|
34
|
+
[roles]
|
|
35
|
+
owner = []
|
|
36
|
+
admin = []
|
|
37
|
+
operator = []
|
|
38
|
+
viewer = []
|
|
39
|
+
|
|
40
|
+
[hermes]
|
|
41
|
+
# Optional Hermes Agent/OpenAI-compatible endpoint. Keep keys in environment only.
|
|
42
|
+
api_base = "http://127.0.0.1:8642/v1"
|
|
43
|
+
model = "hermes-agent"
|
|
44
|
+
webhook_url = ""
|
|
45
|
+
timeout_ms = 45000
|
|
46
|
+
auto_analyze = false
|
|
47
|
+
|
|
48
|
+
# Keepalive is env-driven. Use KEEPALIVE_AUTO_CONNECT=false until interactive QR setup is approved.
|
package/mcp/run.sh
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# stdio MCP entrypoint for Hermes / Claude / Cursor
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
5
|
+
export ZALO_BRIDGE_URL="${ZALO_BRIDGE_URL:-http://127.0.0.1:3871}"
|
|
6
|
+
# Never print to stdout except MCP JSON-RPC (handled by node server)
|
|
7
|
+
exec node "$ROOT/mcp/server.js"
|