@manybot/manybot 5.7.0 → 5.8.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.
Files changed (71) hide show
  1. package/README.md +20 -3
  2. package/dist/client/banner.js +10 -0
  3. package/dist/client/banner.test.js +31 -0
  4. package/dist/client/store.js +56 -5
  5. package/dist/client/store.test.js +170 -0
  6. package/dist/config.js +28 -44
  7. package/dist/config.test.js +26 -0
  8. package/dist/drivers/baileys/adapter.js +58 -7
  9. package/dist/drivers/baileys/api/index.js +172 -24
  10. package/dist/drivers/baileys/index.js +62 -30
  11. package/dist/drivers/baileys/loginPrompt.js +0 -2
  12. package/dist/drivers/baileys/messageHandler.js +158 -4
  13. package/dist/drivers/baileys/messageHandler.test.js +203 -0
  14. package/dist/drivers/baileysAdapter.test.js +281 -0
  15. package/dist/drivers/jid.test.js +40 -0
  16. package/dist/drivers/types.js +5 -5
  17. package/dist/i18n/index.js +15 -2
  18. package/dist/kernel/activeDriverSend.js +21 -0
  19. package/dist/kernel/activeDriverSend.test.js +89 -0
  20. package/dist/kernel/alerts.js +3 -9
  21. package/dist/kernel/chatSession.js +65 -0
  22. package/dist/kernel/chatSession.test.js +46 -0
  23. package/dist/kernel/commandAccess.js +66 -0
  24. package/dist/kernel/commandAccess.test.js +74 -0
  25. package/dist/kernel/commandDeprecation.js +168 -0
  26. package/dist/kernel/commandDeprecation.test.js +107 -0
  27. package/dist/kernel/commandMenu.js +268 -0
  28. package/dist/kernel/commandMenu.test.js +234 -0
  29. package/dist/kernel/commandPermissions.js +125 -0
  30. package/dist/kernel/commandPermissions.test.js +159 -0
  31. package/dist/kernel/commandRegistry.js +459 -0
  32. package/dist/kernel/commandRegistry.test.js +156 -0
  33. package/dist/kernel/commandsConfig.js +517 -0
  34. package/dist/kernel/commandsConfig.test.js +236 -0
  35. package/dist/kernel/contactAutoSave.test.js +87 -0
  36. package/dist/kernel/driverManager.js +10 -6
  37. package/dist/kernel/driverManager.test.js +90 -0
  38. package/dist/kernel/integrationMode.js +88 -0
  39. package/dist/kernel/integrationMode.test.js +95 -0
  40. package/dist/kernel/loadIntegrationPlugin.test.js +67 -0
  41. package/dist/kernel/pluginApi.test.js +583 -0
  42. package/dist/kernel/pluginGuard.js +15 -12
  43. package/dist/kernel/pluginGuard.test.js +39 -0
  44. package/dist/kernel/pluginLoader.js +96 -1
  45. package/dist/kernel/pluginLoader.test.js +80 -0
  46. package/dist/kernel/runCommand.js +245 -0
  47. package/dist/kernel/runCommand.test.js +235 -0
  48. package/dist/kernel/sendFallbackGuard.js +19 -48
  49. package/dist/kernel/sendFallbackGuard.test.js +80 -0
  50. package/dist/kernel/sendGuard.js +38 -42
  51. package/dist/kernel/sendGuard.test.js +102 -0
  52. package/dist/kernel/settingsDb.js +4 -3
  53. package/dist/kernel/statusServer.js +9 -2
  54. package/dist/kernel/statusServer.test.js +70 -0
  55. package/dist/kernel/testConfig.js +183 -0
  56. package/dist/kernel/testConfig.test.js +181 -0
  57. package/dist/kernel/updateCheck.js +33 -10
  58. package/dist/locales/en.json +64 -13
  59. package/dist/locales/es.json +64 -13
  60. package/dist/locales/pt.json +64 -13
  61. package/dist/logger/logger.js +23 -3
  62. package/dist/logger/logger.test.js +45 -0
  63. package/dist/main.js +5 -76
  64. package/dist/plugins/__manybot_integration__/index.js +167 -0
  65. package/dist/plugins/__manybot_integration__/index.test.js +184 -0
  66. package/package.json +74 -17
  67. package/dist/drivers/whatsmeow/client.js +0 -252
  68. package/dist/drivers/whatsmeow/index.js +0 -79
  69. package/dist/drivers/whatsmeow/installer.js +0 -86
  70. package/dist/drivers/whatsmeow/supervisor.js +0 -328
  71. package/dist/drivers/whatsmeow/whatsmeow.proto +0 -64
@@ -1,86 +0,0 @@
1
- import { mkdirSync, writeFileSync, chmodSync, existsSync } from "node:fs";
2
- import path from "node:path";
3
- import * as clack from "@clack/prompts";
4
- import { persistConfigValue, CONFIG_DIR } from "#config";
5
- import { t } from "#i18n";
6
- const SUPPORTED = [
7
- { os: "linux", arch: "x64", name: "whatsmeow-service-linux-x64" },
8
- { os: "linux", arch: "arm64", name: "whatsmeow-service-linux-arm64" },
9
- { os: "win32", arch: "x64", name: "whatsmeow-service-windows-x64.exe" },
10
- ];
11
- function detectTarget() {
12
- return SUPPORTED.find((t) => t.os === process.platform && t.arch === process.arch) ?? null;
13
- }
14
- function str(val) {
15
- return typeof val === "string" ? val : String(val);
16
- }
17
- async function fetchLatestTag() {
18
- const res = await fetch("https://api.github.com/repos/many-bot/manybot/releases/latest");
19
- if (!res.ok)
20
- throw new Error(`GitHub API: ${res.status}`);
21
- const data = await res.json();
22
- return data.tag_name ?? "v5.6.1";
23
- }
24
- function binaryDir() {
25
- return path.resolve(CONFIG_DIR, "whatsmeow-service", "bin");
26
- }
27
- export async function promptWhatsmeowInstall() {
28
- const target = detectTarget();
29
- if (!target) {
30
- clack.log.warn(str(t("whatsmeow.unsupportedArch", { os: process.platform, arch: process.arch })));
31
- return;
32
- }
33
- const outPath = path.join(binaryDir(), "whatsmeow-service");
34
- // Binary already on disk → skip the prompt and the download, but
35
- // make sure the config flag is set so the supervisor boots on the
36
- // next run. The earlier "no, declined install" flow only writes the
37
- // TOML on success, so users who later build the binary by hand also
38
- // hit this branch on their next first-login.
39
- if (existsSync(outPath)) {
40
- await persistConfigValue("driver_whatsmeow_enabled", "true");
41
- return;
42
- }
43
- const choice = await clack.confirm({
44
- message: str(t("whatsmeow.installPrompt")),
45
- initialValue: false,
46
- });
47
- if (clack.isCancel(choice) || !choice)
48
- return;
49
- const spin = clack.spinner();
50
- spin.start(str(t("whatsmeow.fetchingTag")));
51
- let tag;
52
- try {
53
- tag = await fetchLatestTag();
54
- }
55
- catch {
56
- spin.stop(str(t("whatsmeow.fetchFailed")));
57
- return;
58
- }
59
- const url = `https://github.com/many-bot/manybot/releases/download/${tag}/${target.name}`;
60
- spin.message(str(t("whatsmeow.downloading", { url })));
61
- let res;
62
- try {
63
- res = await fetch(url);
64
- if (!res.ok)
65
- throw new Error(`${res.status}`);
66
- }
67
- catch (e) {
68
- spin.stop(str(t("whatsmeow.downloadFailed", { reason: e.message })));
69
- return;
70
- }
71
- const buffer = Buffer.from(await res.arrayBuffer());
72
- const dir = binaryDir();
73
- mkdirSync(dir, { recursive: true });
74
- writeFileSync(outPath, buffer);
75
- chmodSync(outPath, 0o755);
76
- await persistConfigValue("driver_whatsmeow_enabled", "true");
77
- spin.stop(str(t("whatsmeow.installed", { path: outPath })));
78
- clack.note(str(t("whatsmeow.restartNotice")), str(t("whatsmeow.installTitle")));
79
- // The bot is still in its very first run (no Baileys session yet, no
80
- // supervisor spawned) — the config has just been updated on disk but
81
- // the in-memory `CONFIG` object and the supervisor lifecycle were
82
- // initialized at startup with `whatsmeow.enabled = false`. Restarting
83
- // is required for the new value to take effect. Exit cleanly so the
84
- // user just re-runs the bot.
85
- setTimeout(() => process.exit(0), 100);
86
- }
@@ -1,328 +0,0 @@
1
- /**
2
- * supervisor.ts
3
- *
4
- * Lifecycle manager for the Go whatsmeow subprocess. Two responsibilities:
5
- *
6
- * 1. Spawn the binary, watch for crashes, restart with exponential
7
- * backoff (same circuit-breaker pattern the Baileys driver uses in
8
- * drivers/baileys/index.ts:98-106). After MAX_RECONNECT_ATTEMPTS
9
- * consecutive failures, halt permanently and fire
10
- * `whatsmeow_subprocess_halted` — ManyBot keeps running on Baileys
11
- * alone.
12
- * 2. Block `whenReady()` until the gRPC server has answered
13
- * `HealthCheck{ready:true}` at least once. Without this, callers
14
- * race ahead of the subprocess and try to `connect()` against an
15
- * empty SQLite / pre-auth state.
16
- *
17
- * Resolution of the binary path: `CONFIG.drivers.whatsmeow.binaryPath`
18
- * wins; if empty, falls back to the env var `WM_BINARY_PATH`, then
19
- * `<cwd>/whatsmeow-service/bin/whatsmeow-service` (dev), then the
20
- * npm-global install layout. Falls open into a logged fatal if nothing
21
- * matches — `enabled=true` without a binary is a setup bug, not a
22
- * silent degradation.
23
- *
24
- * `config.drivers.whatsmeow.enabled = false` (TOML:
25
- * `driver_whatsmeow_enabled = false`) ⇒ no supervisor at all
26
- * (`null` return from `startWhatsmeowSupervisor`); the rest of the
27
- * codebase is unaware anything is missing — it just never sees a
28
- * ready whatsmeow.
29
- *
30
- * See the lifecycle contract this implements.
31
- */
32
- import { spawn } from "node:child_process";
33
- import { existsSync, mkdirSync } from "node:fs";
34
- import path from "node:path";
35
- import { fileURLToPath } from "node:url";
36
- import { CONFIG, CONFIG_DIR, CLIENT_ID } from "#config";
37
- import { logger } from "#logger";
38
- import { fireAlert } from "#kernel/alerts.js";
39
- import { getDriverManager } from "#kernel/driverManager.js";
40
- // ── Tunables (mirror drivers/baileys/index.ts:98-106) ──────────────────────
41
- const RECONNECT_BASE_MS = 1000;
42
- const RECONNECT_MAX_MS = 60_000;
43
- const MAX_RECONNECT_ATTEMPTS = 6;
44
- const HEALTHCHECK_INTERVAL_MS = 500;
45
- const HEALTHCHECK_TIMEOUT_MS = 5_000;
46
- const SHUTDOWN_GRACE_MS = 5_000;
47
- // ── Resolution helpers ─────────────────────────────────────────────────────
48
- /**
49
- * Walks the conventional locations looking for the binary. Returns the
50
- * first match that exists and is a regular file. Order:
51
- * 1. CONFIG.drivers.whatsmeow.binaryPath (explicit user choice)
52
- * 2. env WM_BINARY_PATH (escape hatch for exotic installs)
53
- * 3. stable config dir (~/.manybot/whatsmeow-service/bin/whatsmeow-service)
54
- * 4. dev layout (<cwd>/whatsmeow-service/bin/whatsmeow-service)
55
- * 4. npm-global layout (sibling of the node binary, /usr/local style)
56
- */
57
- function resolveBinaryPath() {
58
- const candidates = [];
59
- const fromConfig = CONFIG.drivers.whatsmeow.binaryPath;
60
- if (fromConfig)
61
- candidates.push(path.resolve(fromConfig));
62
- const fromEnv = process.env.WM_BINARY_PATH;
63
- if (fromEnv)
64
- candidates.push(path.resolve(fromEnv));
65
- // Stable config dir: ~/.manybot/whatsmeow-service/bin/whatsmeow-service
66
- candidates.push(path.resolve(CONFIG_DIR, "whatsmeow-service", "bin", "whatsmeow-service"));
67
- // Dev: `<repo>/whatsmeow-service/bin/whatsmeow-service`
68
- candidates.push(path.resolve(process.cwd(), "whatsmeow-service", "bin", "whatsmeow-service"));
69
- // Global npm install: `<prefix>/bin/../share/manybot/bin/whatsmeow-service`
70
- // `<prefix>` is the parent of the running node binary on most setups.
71
- const prefix = path.dirname(path.dirname(process.execPath));
72
- candidates.push(path.join(prefix, "share", "manybot", "bin", "whatsmeow-service"));
73
- // Belt-and-suspenders: macOS/Linux homebrew-style shared dir.
74
- candidates.push(path.join(prefix, "lib", "node_modules", "manybot", "whatsmeow-service", "bin", "whatsmeow-service"));
75
- for (const c of candidates) {
76
- if (existsSync(c))
77
- return c;
78
- }
79
- return null;
80
- }
81
- /**
82
- * Single HealthCheck RPC against `addr`. Implemented with the low-level
83
- * @grpc/grpc-js client API so the supervisor doesn't need the rest of
84
- * the whatsmeow driver to be importable (the supervisor can outlive a
85
- * broken contract, e.g. when the proto is being regenerated).
86
- *
87
- * Uses an in-memory `.proto` with the bare minimum types needed for
88
- * HealthCheck so we don't depend on the driver module's loader cache.
89
- */
90
- async function probeHealth(addr) {
91
- // Lazy imports keep the fast path (enabled=false) cheap.
92
- const grpc = await import("@grpc/grpc-js");
93
- const protoLoader = await import("@grpc/proto-loader");
94
- // Resolve the .proto relative to this compiled module — works under
95
- // `node dist/main.js` (proto is copied into dist/drivers/whatsmeow/
96
- // by the build script), under a global npm install (proto ships in
97
- // the package's `dist/`), and in dev (`tsx src/main.ts`, where the
98
- // proto sits next to this source file). ESM has no `__dirname`, so
99
- // use `import.meta.url`.
100
- const protoPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "whatsmeow.proto");
101
- if (!existsSync(protoPath)) {
102
- return { ready: false, error: `proto not found at ${protoPath}` };
103
- }
104
- const def = protoLoader.loadSync(protoPath, {
105
- keepCase: true,
106
- longs: String,
107
- enums: String,
108
- defaults: true,
109
- oneofs: true,
110
- });
111
- const Service = grpc.loadPackageDefinition(def).whatsmeow.WhatsmeowService;
112
- const client = new Service(addr, grpc.credentials.createInsecure());
113
- return new Promise((resolve) => {
114
- const timer = setTimeout(() => {
115
- try {
116
- client.close();
117
- }
118
- catch { }
119
- resolve({ ready: false, error: "healthcheck timeout" });
120
- }, HEALTHCHECK_TIMEOUT_MS);
121
- client.HealthCheck({}, (err, resp) => {
122
- clearTimeout(timer);
123
- try {
124
- client.close();
125
- }
126
- catch { }
127
- if (err)
128
- return resolve({ ready: false, error: err.message });
129
- resolve({ ready: !!resp?.ready, error: resp?.last_error || undefined });
130
- });
131
- });
132
- }
133
- async function waitMs(ms) {
134
- return new Promise((r) => setTimeout(r, ms));
135
- }
136
- /**
137
- * Spawn the subprocess, register lifecycle handlers, and kick the
138
- * healthcheck loop. Returns the supervisor handle, or `null` if
139
- * `enabled = false`. Throws only on fatal startup conditions (binary
140
- * not found, spawn() failed synchronously); transient RPC failures are
141
- * logged + backed off, never thrown.
142
- */
143
- export async function startWhatsmeowSupervisor() {
144
- if (!CONFIG.drivers.whatsmeow.enabled) {
145
- logger.info("[supervisor] disabled by config (driver_whatsmeow_enabled = false)");
146
- return null;
147
- }
148
- const binary = resolveBinaryPath();
149
- if (!binary) {
150
- logger.error("[supervisor] whatsmeow-service binary not found. Set `driver_whatsmeow_binary_path` in manybot.toml, " +
151
- "set env WM_BINARY_PATH, or place the binary at " +
152
- "whatsmeow-service/bin/whatsmeow-service relative to cwd. Bot will run on Baileys only.");
153
- return null;
154
- }
155
- logger.info(`[supervisor] using binary: ${binary}`);
156
- const grpcAddress = CONFIG.drivers.whatsmeow.grpcAddress || "localhost:50051";
157
- const sessionDir = path.resolve(CONFIG_DIR, "sessions", CLIENT_ID, "whatsmeow", "session.db");
158
- // Ensure the parent directory exists before the Go subprocess tries to
159
- // create/open the SQLite file.
160
- mkdirSync(path.dirname(sessionDir), { recursive: true });
161
- const state = {
162
- proc: null,
163
- pid: null,
164
- addr: grpcAddress,
165
- binary,
166
- sessionDir,
167
- reconnectTries: 0,
168
- halted: false,
169
- ready: false,
170
- readyDeferred: null,
171
- shuttingDown: false,
172
- restartTimer: null,
173
- };
174
- // First ready promise — created lazily on the first `whenReady()` so
175
- // a never-called instance doesn't carry dangling deferreds.
176
- function ensureReadyPromise() {
177
- if (state.ready)
178
- return Promise.resolve();
179
- if (state.halted)
180
- return Promise.reject(new Error("whatsmeow supervisor halted"));
181
- if (!state.readyDeferred) {
182
- let resolve;
183
- let reject;
184
- const p = new Promise((res, rej) => { resolve = res; reject = rej; });
185
- state.readyDeferred = { resolve, reject };
186
- }
187
- return new Promise((resolve, reject) => {
188
- // Re-attach so callers can `await` multiple times.
189
- const d = state.readyDeferred;
190
- const origResolve = d.resolve;
191
- const origReject = d.reject;
192
- d.resolve = () => { origResolve(); resolve(); };
193
- d.reject = (e) => { origReject(e); reject(e); };
194
- });
195
- }
196
- function markReady() {
197
- if (state.ready)
198
- return;
199
- state.ready = true;
200
- state.reconnectTries = 0; // healthy again → reset circuit breaker
201
- state.readyDeferred?.resolve();
202
- }
203
- function markHalted(reason) {
204
- state.halted = true;
205
- state.ready = false;
206
- state.readyDeferred?.reject(new Error(reason));
207
- getDriverManager().markDegraded("whatsmeow", 600_000);
208
- fireAlert("whatsmeow_subprocess_halted", { reason });
209
- }
210
- function scheduleRestart() {
211
- if (state.shuttingDown || state.halted)
212
- return;
213
- if (state.reconnectTries >= MAX_RECONNECT_ATTEMPTS) {
214
- logger.error(`[supervisor] exhausted ${MAX_RECONNECT_ATTEMPTS} reconnect attempts — halting`);
215
- markHalted(`exhausted ${MAX_RECONNECT_ATTEMPTS} reconnect attempts`);
216
- return;
217
- }
218
- const delay = Math.min(RECONNECT_BASE_MS * 2 ** state.reconnectTries, RECONNECT_MAX_MS);
219
- state.reconnectTries += 1;
220
- logger.warn(`[supervisor] scheduling restart in ${delay}ms (attempt ${state.reconnectTries}/${MAX_RECONNECT_ATTEMPTS})`);
221
- state.restartTimer = setTimeout(() => {
222
- state.restartTimer = null;
223
- void spawnAndWatch();
224
- }, delay);
225
- }
226
- async function spawnAndWatch() {
227
- if (state.shuttingDown || state.halted)
228
- return;
229
- let proc;
230
- try {
231
- proc = spawn(state.binary, ["--grpc-addr", state.addr, "--session-dir", state.sessionDir], {
232
- stdio: ["ignore", "pipe", "pipe"],
233
- });
234
- // Forward Go service stdout/stderr to the bot log so we can see
235
- // crashes, missing dependencies, port-in-use, etc. Without this
236
- // `stdio: "ignore"` would discard everything and a crashed
237
- // subprocess would only surface as an exit code.
238
- proc.stdout?.on("data", (chunk) => {
239
- process.stdout.write(`[whatsmeow-stdout] ${chunk}`);
240
- });
241
- proc.stderr?.on("data", (chunk) => {
242
- process.stderr.write(`[whatsmeow-stderr] ${chunk}`);
243
- });
244
- }
245
- catch (e) {
246
- logger.error(`[supervisor] spawn failed: ${e.message}`);
247
- scheduleRestart();
248
- return;
249
- }
250
- state.proc = proc;
251
- state.pid = proc.pid ?? null;
252
- proc.on("error", (err) => {
253
- logger.warn(`[supervisor] subprocess error: ${err.message}`);
254
- // Don't restart here — the `exit` handler will fire on ENOENT etc.
255
- });
256
- proc.on("exit", (code, signal) => {
257
- logger.warn(`[supervisor] subprocess exited code=${code} signal=${signal} ready=${state.ready}`);
258
- state.proc = null;
259
- state.pid = null;
260
- state.ready = false;
261
- if (!state.shuttingDown && !state.halted)
262
- scheduleRestart();
263
- });
264
- logger.info(`[supervisor] subprocess started (pid=${state.pid}, binary=${state.binary})`);
265
- // Don't await — healthcheck loop runs in its own cycle.
266
- void runHealthLoop();
267
- }
268
- async function runHealthLoop() {
269
- while (!state.shuttingDown && !state.halted) {
270
- await waitMs(HEALTHCHECK_INTERVAL_MS);
271
- if (!state.proc)
272
- break; // subprocess died; `exit` handler owns restart
273
- const result = await probeHealth(state.addr);
274
- if (result.ready) {
275
- if (!state.ready)
276
- logger.info("[supervisor] healthcheck ready");
277
- markReady();
278
- // Once ready, keep polling at the same cadence to detect
279
- // when the subprocess silently dies. A slow poll would mean
280
- // we miss the reconnect window.
281
- }
282
- else if (result.error) {
283
- logger.debug(`[supervisor] healthcheck: not ready (${result.error})`);
284
- }
285
- }
286
- }
287
- const supervisor = {
288
- whenReady: ensureReadyPromise,
289
- isReady: () => state.ready && !!state.proc,
290
- isAlive: () => !!state.proc,
291
- pid: () => state.pid,
292
- async shutdown() {
293
- if (state.shuttingDown)
294
- return;
295
- state.shuttingDown = true;
296
- if (state.restartTimer) {
297
- clearTimeout(state.restartTimer);
298
- state.restartTimer = null;
299
- }
300
- state.readyDeferred?.reject(new Error("supervisor shutting down"));
301
- const proc = state.proc;
302
- if (!proc)
303
- return;
304
- try {
305
- proc.kill("SIGTERM");
306
- }
307
- catch { }
308
- const exited = await new Promise((resolve) => {
309
- const t = setTimeout(() => {
310
- try {
311
- proc.kill("SIGKILL");
312
- }
313
- catch { }
314
- resolve(true);
315
- }, SHUTDOWN_GRACE_MS);
316
- proc.once("exit", () => { clearTimeout(t); resolve(true); });
317
- });
318
- state.proc = null;
319
- state.pid = null;
320
- void exited;
321
- },
322
- };
323
- // Kick off — intentionally not awaited. The supervisor handle is
324
- // returned synchronously so callers can stash it / attach shutdown
325
- // hooks; the spawn + healthcheck loop runs in the background.
326
- void spawnAndWatch();
327
- return supervisor;
328
- }
@@ -1,64 +0,0 @@
1
- syntax = "proto3";
2
- package whatsmeow;
3
-
4
- service WhatsmeowService {
5
- rpc Connect(ConnectRequest) returns (ConnectResponse);
6
- rpc Disconnect(Empty) returns (Empty);
7
- rpc SendText(SendTextRequest) returns (SentMessageRef);
8
- rpc SendMedia(SendMediaRequest) returns (SentMessageRef);
9
- rpc React(ReactRequest) returns (Empty);
10
- rpc GetHistory(GetHistoryRequest) returns (GetHistoryResponse);
11
- rpc SubscribeEvents(Empty) returns (stream WaEvent);
12
- rpc HealthCheck(Empty) returns (HealthStatus);
13
- }
14
-
15
- message Empty {}
16
-
17
- message ConnectRequest { string sessionDir = 1; }
18
- message ConnectResponse { bool ok = 1; string qrCode = 2; }
19
-
20
- message SendTextRequest {
21
- string jid = 1;
22
- string text = 2;
23
- string quotedId = 3;
24
- repeated string mentions = 4;
25
- }
26
-
27
- message SendMediaRequest {
28
- string jid = 1;
29
- string kind = 2; // image|video|audio|sticker|document
30
- bytes buffer = 3;
31
- string caption = 4;
32
- string quotedId = 5;
33
- }
34
-
35
- message SentMessageRef {
36
- string id = 1;
37
- string chatId = 2;
38
- int64 timestamp = 3;
39
- }
40
-
41
- message ReactRequest { string jid = 1; string messageId = 2; string emoji = 3; }
42
-
43
- message GetHistoryRequest { string jid = 1; int32 limit = 2; }
44
- message GetHistoryResponse { repeated BotMessage messages = 1; }
45
-
46
- message BotMessage {
47
- string id = 1;
48
- string chatId = 2;
49
- bool fromMe = 3;
50
- string type = 4;
51
- string contentHash = 5;
52
- int64 timestamp = 6;
53
- }
54
-
55
- message WaEvent {
56
- oneof payload {
57
- BotMessage message = 1;
58
- ConnStateUpdate connState = 2;
59
- }
60
- }
61
-
62
- message ConnStateUpdate { string state = 1; }
63
-
64
- message HealthStatus { bool ready = 1; }