Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,25 @@ is Python — is exactly what the thin Node bridge pays for.
> ⚠️ **zca-js is UNOFFICIAL.** Use a secondary Zalo account. Zalo may
> rate-limit or lock accounts that automate. You accept that risk.

## Companion X/Twitter Context

For social monitoring workflows, pair this bridge with
[Hermes Tweet](https://github.com/Xquik-dev/hermes-tweet), the native Hermes
Agent plugin for Xquik. Install and enable its current `0.1.8` release:

```bash
hermes plugins install Xquik-dev/hermes-tweet --enable
```

Use `tweet_explore` to discover a catalog-listed route, then use `tweet_read`
for public read-only X/Twitter context. Keep `tweet_action` disabled unless the
operator approves the exact private or write-like operation. Hermes can pass a
resulting summary or follow-up prompt to this bridge for delivery to an allowed
Zalo user or group.

Xquik is an independent third-party service. Not affiliated with X Corp.
"Twitter" and "X" are trademarks of X Corp.

## Requirements

Before installing, make sure you have:
Expand Down
15 changes: 14 additions & 1 deletion bin/cli.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,22 @@ function runNode(script, args = []) {
async function status() {
const port = process.env.ZALO_PLUGIN_PORT || "8787";
const host = process.env.ZALO_PLUGIN_HOST || "127.0.0.1";
const token = process.env.ZALO_PLUGIN_TOKEN || "";
const headers = token ? { "x-bridge-token": token } : {};
console.log(`Data directory: ${dataDir()}`);
try {
const res = await fetch(`http://${host}:${port}/health`, { signal: AbortSignal.timeout(3000) });
const res = await fetch(`http://${host}:${port}/health`, {
headers,
signal: AbortSignal.timeout(3000),
});
if (res.status === 401) {
console.log("Bridge: authentication failed.");
console.log(" Set ZALO_PLUGIN_TOKEN to the bridge token, then retry.");
return;
}
if (!res.ok) {
throw new Error(`health returned HTTP ${res.status}`);
}
const j = await res.json();
console.log(`Bridge: RUNNING on http://${host}:${port}`);
console.log(` loggedIn=${j.loggedIn} sessionDead=${j.sessionDead} ownId=${j.ownId || "-"}`);
Expand Down
7 changes: 5 additions & 2 deletions hermes-plugin/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,8 +324,11 @@ async def connect(self) -> bool:
# Probe bridge health and login state.
try:
async with self._session.get(
f"{self.bridge_url}/health", timeout=aiohttp.ClientTimeout(total=10)
f"{self.bridge_url}/health",
headers=self._headers(),
timeout=aiohttp.ClientTimeout(total=10),
) as resp:
resp.raise_for_status()
data = await resp.json()
except Exception as e:
logger.error("Zalo: cannot reach bridge at %s — %s", self.bridge_url, e)
Expand Down Expand Up @@ -1370,7 +1373,7 @@ def interactive_setup() -> None:
else:
print_warning("⚠ Cấu hình Zalo đã lưu, NHƯNG bridge chưa sẵn sàng — bot sẽ chưa hoạt động.")
print_info(" Bridge (Node service) phải ĐANG CHẠY và đã đăng nhập thì bot mới chạy được.")
print_info(f" • Kiểm tra: curl {bridge}/health")
print_info(f" • Kiểm tra: {cli} status")
print_info(f" • Bật service: {cli} setup --service-only (đã login thì không cần QR)")
print_info(f" • Đăng nhập QR: {cli} login (nếu bị đăng xuất)")
print_info(" Xong rồi chạy: hermes gateway")
Expand Down
2 changes: 1 addition & 1 deletion install.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ function nextSteps() {
✓ Zalo plugin is set up.

Bridge URL: http://127.0.0.1:${port}
Health: curl http://127.0.0.1:${port}/health
Health: hermes-zalo-plugin status

Next, register Zalo in Hermes:
1) hermes gateway setup → choose "Zalo" (🇻🇳)
Expand Down
7 changes: 6 additions & 1 deletion login.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@ const QR_PATH = qrPath();
async function bridgeAlreadyLoggedIn() {
const port = process.env.ZALO_PLUGIN_PORT || "8787";
const host = process.env.ZALO_PLUGIN_HOST || "127.0.0.1";
const token = process.env.ZALO_PLUGIN_TOKEN || "";
const headers = token ? { "x-bridge-token": token } : {};
try {
const res = await fetch(`http://${host}:${port}/health`, { signal: AbortSignal.timeout(3000) });
const res = await fetch(`http://${host}:${port}/health`, {
headers,
signal: AbortSignal.timeout(3000),
});
if (!res.ok) return false;
const j = await res.json();
return !!j.loggedIn && !j.sessionDead;
Expand Down
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ app.get("/policy", (req, res) => {
});

app.get("/health", (req, res) => {
if (!checkAuth(req, res)) return;
res.json({
ok: true,
loggedIn: client.loggedIn,
Expand Down