botforje 0.0.1
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/LICENSE +21 -0
- package/README.md +381 -0
- package/dist/action/action.js +44 -0
- package/dist/action/catalog.js +40 -0
- package/dist/action/cooldown.js +60 -0
- package/dist/action/executor.js +23 -0
- package/dist/action/template.js +13 -0
- package/dist/action/types.js +2 -0
- package/dist/action/webhook.js +36 -0
- package/dist/actions/action.js +26 -0
- package/dist/actions/cooldown.js +66 -0
- package/dist/actions/request.js +40 -0
- package/dist/api/routes/auth.js +56 -0
- package/dist/api/routes/bots.js +51 -0
- package/dist/api/routes/config.js +24 -0
- package/dist/api/routes/health.js +15 -0
- package/dist/api/routes/index.js +14 -0
- package/dist/api/routes/messages.js +69 -0
- package/dist/api/routes/sessions.js +97 -0
- package/dist/api/routes/status.js +38 -0
- package/dist/api/server.js +153 -0
- package/dist/auth/service.js +102 -0
- package/dist/boot/fleet.js +207 -0
- package/dist/bot/bot.js +30 -0
- package/dist/bot/fleet.js +211 -0
- package/dist/bot/fuzzy.js +20 -0
- package/dist/bot/mapper.js +47 -0
- package/dist/bot/types.js +2 -0
- package/dist/bot/validation.js +38 -0
- package/dist/bot.js +31 -0
- package/dist/cli/create-bot.js +84 -0
- package/dist/cli.js +138 -0
- package/dist/commands/auth.js +200 -0
- package/dist/commands/create-bot.js +64 -0
- package/dist/commands/guide.js +563 -0
- package/dist/commands/lock.js +73 -0
- package/dist/commands/status.js +145 -0
- package/dist/commands/unlock.js +88 -0
- package/dist/commands/validate.js +19 -0
- package/dist/config/mapper.js +152 -0
- package/dist/config/schema.js +2 -0
- package/dist/config/validation.js +705 -0
- package/dist/config/watcher.js +145 -0
- package/dist/config/yaml.js +197 -0
- package/dist/fleet.js +247 -0
- package/dist/flow/executor.js +286 -0
- package/dist/flow/flow.js +2 -0
- package/dist/flow/mapper.js +72 -0
- package/dist/flow/state.js +115 -0
- package/dist/flow/types.js +2 -0
- package/dist/graph/executor.js +294 -0
- package/dist/graph/graph.js +2 -0
- package/dist/graph/state.js +118 -0
- package/dist/helpers/data.js +42 -0
- package/dist/helpers/fuzzy.js +30 -0
- package/dist/helpers/logger.js +89 -0
- package/dist/helpers/validation.js +38 -0
- package/dist/index.js +92 -0
- package/dist/messages/contracts.js +2 -0
- package/dist/messages/inbox.js +58 -0
- package/dist/messages/outbox.js +136 -0
- package/dist/services/auto-response.js +62 -0
- package/dist/services/cooldown.js +60 -0
- package/dist/services/fleet.js +207 -0
- package/dist/services/flow-executor.js +195 -0
- package/dist/services/flow-state.js +118 -0
- package/dist/services/inbox.js +50 -0
- package/dist/services/message-handler.js +78 -0
- package/dist/services/message-queue.js +138 -0
- package/dist/services/outbox.js +138 -0
- package/dist/services/session.js +118 -0
- package/dist/services/webhook.js +87 -0
- package/dist/utils/logger.js +89 -0
- package/dist/utils/webhook.js +36 -0
- package/dist/validation/validate.js +592 -0
- package/dist/whatsapp/client.js +293 -0
- package/dist/whatsapp/session.js +158 -0
- package/dist/whatsapp/types.js +109 -0
- package/dist/whatsapp/whatsapp.js +109 -0
- package/package.json +97 -0
- package/scripts/postinstall.js +45 -0
- package/scripts/preuninstall.js +48 -0
- package/scripts/setup-systemd.js +91 -0
- package/service/botforje.service.template +25 -0
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runGuide = runGuide;
|
|
4
|
+
function runGuide() {
|
|
5
|
+
const guide = `# Botforje — AI Agent Configuration Guide
|
|
6
|
+
|
|
7
|
+
Framework for running config-driven WhatsApp bots. All behavior is defined in YAML — no code required.
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
|
|
11
|
+
Three core concepts: **Actions** (what to do), **Graphs** (how to respond), **Bots** (who responds).
|
|
12
|
+
|
|
13
|
+
\`\`\`
|
|
14
|
+
Bot
|
|
15
|
+
└─ Graph (one per bot)
|
|
16
|
+
└─ Nodes
|
|
17
|
+
└─ Edges (transitions based on fuzzy-matched user input)
|
|
18
|
+
└─ Actions
|
|
19
|
+
\`\`\`
|
|
20
|
+
|
|
21
|
+
A bot owns exactly one graph. A graph represents the complete conversation for that bot. Nodes belong to their graph and are not reusable across graphs. Actions remain globally reusable.
|
|
22
|
+
|
|
23
|
+
\`\`\`
|
|
24
|
+
WhatsApp message
|
|
25
|
+
-> InboxService (filters: ignored senders, groups, self-messages)
|
|
26
|
+
-> GraphExecutor.handleMessage()
|
|
27
|
+
-> active session? -> match edge from current/visited nodes -> transition -> executeAction()
|
|
28
|
+
-> new sender? -> create session at root, execute root action, then re-resolve the original message
|
|
29
|
+
against the root edges (and any visited nodes)
|
|
30
|
+
-> executeAction()
|
|
31
|
+
-> resolve template variables ({{sender}}, {{message}}, etc.)
|
|
32
|
+
-> check cooldown
|
|
33
|
+
-> enqueue reply -> OutboxService -> WhatsAppChannel.send()
|
|
34
|
+
-> fire request -> sendRequestRequest()
|
|
35
|
+
\`\`\`
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Modular Configuration (Recommended)
|
|
40
|
+
|
|
41
|
+
Place config files in a directory structure. File names become IDs.
|
|
42
|
+
|
|
43
|
+
\`\`\`
|
|
44
|
+
~/.config/botforje/
|
|
45
|
+
config.yml # global settings only
|
|
46
|
+
actions/ # each file = one action, filename = action ID
|
|
47
|
+
greet.yml
|
|
48
|
+
menu.yml
|
|
49
|
+
escalate.yml
|
|
50
|
+
graphs/ # each file = one graph, filename = graph ID
|
|
51
|
+
support.yml
|
|
52
|
+
ping-pong.yml
|
|
53
|
+
bots/ # each file = one bot, filename = bot ID
|
|
54
|
+
support-bot.yml
|
|
55
|
+
sales-bot.yml
|
|
56
|
+
\`\`\`
|
|
57
|
+
|
|
58
|
+
The loader merges directory files with any inline definitions in \`config.yml\`. Inline definitions take precedence.
|
|
59
|
+
|
|
60
|
+
### config.yml (global settings only, actions/graphs/bots are separate files)
|
|
61
|
+
|
|
62
|
+
\`\`\`yaml
|
|
63
|
+
chromium_path: "/usr/bin/chromium"
|
|
64
|
+
port: 3000
|
|
65
|
+
address: "127.0.0.1"
|
|
66
|
+
log_level: "info"
|
|
67
|
+
default_timeout: 300
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Schema Reference
|
|
73
|
+
|
|
74
|
+
### Global Settings
|
|
75
|
+
|
|
76
|
+
| Field | Type | Default | Description |
|
|
77
|
+
|---|---|---|---|---|
|
|
78
|
+
| chromium_path | string | - | Path to Chromium/Chrome |
|
|
79
|
+
| port | number | 3000 | REST API port (if set, API runs on this port) |
|
|
80
|
+
| key | string | - | REST API key (optional — if set, all /api/* requests require \`Authorization: Bearer <key>\`) |
|
|
81
|
+
| address | string | "127.0.0.1" | Bind address (127.0.0.1 = localhost only, 0.0.0.0 = all interfaces) |
|
|
82
|
+
| log_level | string | "info" | info, debug, warn, error |
|
|
83
|
+
| default_timeout | number | 300 | Default graph session TTL (seconds) |
|
|
84
|
+
|
|
85
|
+
### Actions
|
|
86
|
+
|
|
87
|
+
Each action has an ID (the key). Stored in \`actions/<id>.yml\`. An action is a **pipeline of ordered steps** with optional **guards** for rate limiting.
|
|
88
|
+
|
|
89
|
+
\`\`\`yaml
|
|
90
|
+
# Simple message step
|
|
91
|
+
steps:
|
|
92
|
+
- message:
|
|
93
|
+
body: "Hello!"
|
|
94
|
+
|
|
95
|
+
# Pipeline: message + request
|
|
96
|
+
steps:
|
|
97
|
+
- message:
|
|
98
|
+
body: "Processing..."
|
|
99
|
+
- request:
|
|
100
|
+
name: my-request
|
|
101
|
+
url: "https://api.example.com/hook"
|
|
102
|
+
method: POST
|
|
103
|
+
headers:
|
|
104
|
+
Authorization: "Bearer token"
|
|
105
|
+
timeout: 10000
|
|
106
|
+
retry: 3
|
|
107
|
+
|
|
108
|
+
# Cooldown guard — per-sender rate limiting
|
|
109
|
+
guards:
|
|
110
|
+
cooldown:
|
|
111
|
+
duration: 120
|
|
112
|
+
on_blocked:
|
|
113
|
+
- message:
|
|
114
|
+
body: "Please wait before requesting again."
|
|
115
|
+
steps:
|
|
116
|
+
- message:
|
|
117
|
+
body: "Processing request..."
|
|
118
|
+
|
|
119
|
+
# Request-only (no message)
|
|
120
|
+
steps:
|
|
121
|
+
- request:
|
|
122
|
+
url: "https://crm.example.com/leads"
|
|
123
|
+
method: POST
|
|
124
|
+
\`\`\`
|
|
125
|
+
|
|
126
|
+
**Step types:**
|
|
127
|
+
|
|
128
|
+
| Step | Field | Type | Required | Description |
|
|
129
|
+
|---|---|---|---|---|
|
|
130
|
+
| message | body | string | yes | Message body (supports templates) |
|
|
131
|
+
| message | to | string | no | Recipient override (default = sender) |
|
|
132
|
+
| request | url | string | yes | Target URL |
|
|
133
|
+
| request | method | string | "POST" | GET, POST, PUT, PATCH |
|
|
134
|
+
| request | headers | object | {} | HTTP headers |
|
|
135
|
+
| request | timeout | number | 5000 | Request timeout (ms) |
|
|
136
|
+
| request | retry | number | 3 | Retry count |
|
|
137
|
+
| location | latitude | number | yes | -90 to 90 |
|
|
138
|
+
| location | longitude | number | yes | -180 to 180 |
|
|
139
|
+
| location | name | string | no | Location name |
|
|
140
|
+
| location | address | string | no | Location address |
|
|
141
|
+
|
|
142
|
+
**Guards:**
|
|
143
|
+
|
|
144
|
+
| Guard | Field | Type | Required | Description |
|
|
145
|
+
|---|---|---|---|---|
|
|
146
|
+
| cooldown | duration | number | yes | Cooldown seconds per sender |
|
|
147
|
+
| cooldown | on_blocked | array | no | Pipeline to run when blocked |
|
|
148
|
+
|
|
149
|
+
An action must define \`steps\` (at least one) or a \`cooldown\` guard with \`on_blocked\`.
|
|
150
|
+
|
|
151
|
+
### Graphs
|
|
152
|
+
|
|
153
|
+
Each graph has an ID (the key). Stored in \`graphs/<id>.yml\`.
|
|
154
|
+
|
|
155
|
+
\`\`\`yaml
|
|
156
|
+
root: menu
|
|
157
|
+
timeout: 300
|
|
158
|
+
fallback: invalid
|
|
159
|
+
nodes:
|
|
160
|
+
menu:
|
|
161
|
+
action: menu
|
|
162
|
+
edges:
|
|
163
|
+
- match: "1, hour, schedule, hours"
|
|
164
|
+
goto: hours
|
|
165
|
+
- match: "2, catalog, products"
|
|
166
|
+
goto: catalog
|
|
167
|
+
- match: "0, exit, bye"
|
|
168
|
+
goto: end
|
|
169
|
+
- goto: invalid
|
|
170
|
+
hours:
|
|
171
|
+
action: hours
|
|
172
|
+
edges:
|
|
173
|
+
- match: "menu, back"
|
|
174
|
+
goto: menu
|
|
175
|
+
- goto: invalid
|
|
176
|
+
end:
|
|
177
|
+
action: farewell
|
|
178
|
+
edges: []
|
|
179
|
+
\`\`\`
|
|
180
|
+
|
|
181
|
+
| Field | Type | Required | Description |
|
|
182
|
+
|---|---|---|---|
|
|
183
|
+
| root | string | yes | Starting node ID |
|
|
184
|
+
| timeout | number | global default | Session TTL (seconds) |
|
|
185
|
+
| fallback | string | - | Node for unmatched input |
|
|
186
|
+
|
|
187
|
+
Graphs have **no entry triggers**. When a sender has no active session, the bot automatically creates a session at \`root\`, executes the root action, and then re-applies the original message against the root's edges.
|
|
188
|
+
|
|
189
|
+
**Nodes:**
|
|
190
|
+
|
|
191
|
+
| Field | Type | Required | Description |
|
|
192
|
+
|---|---|---|---|
|
|
193
|
+
| action | string | yes | Action ID to execute |
|
|
194
|
+
| edges | array | - | Edge conditions for user input |
|
|
195
|
+
|
|
196
|
+
**Edges:**
|
|
197
|
+
|
|
198
|
+
| Field | Type | Description |
|
|
199
|
+
|---|---|---|
|
|
200
|
+
| match | string or string[] | Comma-separated fuzzy-matched phrases |
|
|
201
|
+
| fuzzy_threshold | number | Fuse.js threshold (0.3=strict, 0.6=moderate default, 0.9=loose) |
|
|
202
|
+
| goto | string | **Target node ID** |
|
|
203
|
+
|
|
204
|
+
- **Default edge**: omit \`match\` to catch unmatched input.
|
|
205
|
+
- **No edges (\`edges: []\`)**: session stays alive; the user can still navigate via edges from any previously visited node. Sessions only end via timeout.
|
|
206
|
+
|
|
207
|
+
### Bots
|
|
208
|
+
|
|
209
|
+
Each bot has an ID (the key). Stored in \`bots/<id>.yml\`.
|
|
210
|
+
|
|
211
|
+
\`\`\`yaml
|
|
212
|
+
graph: support
|
|
213
|
+
settings:
|
|
214
|
+
queue_delay: 1500
|
|
215
|
+
ignore_groups: true
|
|
216
|
+
ignored_senders:
|
|
217
|
+
- "status@broadcast"
|
|
218
|
+
\`\`\`
|
|
219
|
+
|
|
220
|
+
| Field | Type | Default | Description |
|
|
221
|
+
|---|---|---|---|
|
|
222
|
+
| graph | string | - | Graph ID this bot owns |
|
|
223
|
+
| settings | object | - | Bot behavior |
|
|
224
|
+
| settings.queue_delay | number | 1000 | ms between outgoing messages |
|
|
225
|
+
| settings.ignore_groups | boolean | true | Ignore group messages |
|
|
226
|
+
| settings.ignored_senders | string[] | [] | Senders to ignore |
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
A bot references exactly one graph. There is no priority or list of graphs per bot.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Template Variables
|
|
234
|
+
|
|
235
|
+
Action replies support variable interpolation:
|
|
236
|
+
|
|
237
|
+
| Variable | Description |
|
|
238
|
+
|---|---|
|
|
239
|
+
| \`{{senderPhone}}\` | Sender phone number |
|
|
240
|
+
| \`{{senderName}}\` | Sender pushname (if available) |
|
|
241
|
+
| \`{{message}}\` | Raw message body |
|
|
242
|
+
| \`{{bot.id}}\` | Bot ID |
|
|
243
|
+
| \`{{variables.remaining}}\` | Cooldown remaining seconds |
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## !include Directive
|
|
248
|
+
|
|
249
|
+
Explicit file inclusion from within \`config.yml\`:
|
|
250
|
+
|
|
251
|
+
\`\`\`yaml
|
|
252
|
+
actions: !include actions/main.yml
|
|
253
|
+
graphs: !include graphs/main.yml
|
|
254
|
+
|
|
255
|
+
bots:
|
|
256
|
+
support: !include bots/support.yml
|
|
257
|
+
sales: !include bots/sales.yml
|
|
258
|
+
\`\`\`
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Best Practices
|
|
263
|
+
|
|
264
|
+
1. **Use directory-based config** — one file per action/graph/bot instead of one giant \`config.yml\`. File name = ID.
|
|
265
|
+
2. **One graph per bot** — keep the whole conversation for a single bot inside one graph.
|
|
266
|
+
3. **Cooldown on human-escalation actions** — prevent spam-triggering manual agent handoffs.
|
|
267
|
+
4. **Use edges: [] for end-of-conversation nodes** — sessions will still expire on timeout, but a user can still reach previously visited nodes.
|
|
268
|
+
5. **Fallback node** — always configure a \`fallback\` to handle unexpected user input.
|
|
269
|
+
6. **Fuzzy thresholds** — \`0.3\` for commands (strict), \`0.6\` default for conversation, \`0.9\` for loose matching.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Complete Modular Example
|
|
274
|
+
|
|
275
|
+
\`~/.config/botforje/config.yml\`:
|
|
276
|
+
|
|
277
|
+
\`\`\`yaml
|
|
278
|
+
chromium_path: "/usr/bin/chromium"
|
|
279
|
+
log_level: "info"
|
|
280
|
+
\`\`\`
|
|
281
|
+
|
|
282
|
+
\`~/.config/botforje/actions/greet.yml\`:
|
|
283
|
+
|
|
284
|
+
\`\`\`yaml
|
|
285
|
+
steps:
|
|
286
|
+
- message:
|
|
287
|
+
body: "Hev {{senderName}}! How can I help you today?"
|
|
288
|
+
\`\`\`
|
|
289
|
+
|
|
290
|
+
\`~/.config/botforje/actions/menu.yml\`:
|
|
291
|
+
|
|
292
|
+
\`\`\`yaml
|
|
293
|
+
steps:
|
|
294
|
+
- message:
|
|
295
|
+
body: "Main menu:\\n1. Hours\\n2. Contact\\n0. Exit"
|
|
296
|
+
\`\`\`
|
|
297
|
+
|
|
298
|
+
\`~/.config/botforje/actions/hours.yml\`:
|
|
299
|
+
|
|
300
|
+
\`\`\`yaml
|
|
301
|
+
steps:
|
|
302
|
+
- message:
|
|
303
|
+
body: "Mon-Fri 9am-6pm"
|
|
304
|
+
\`\`\`
|
|
305
|
+
|
|
306
|
+
\`~/.config/botforje/actions/farewell.yml\`:
|
|
307
|
+
|
|
308
|
+
\`\`\`yaml
|
|
309
|
+
steps:
|
|
310
|
+
- message:
|
|
311
|
+
body: "Thanks, have a great day!"
|
|
312
|
+
\`\`\`
|
|
313
|
+
|
|
314
|
+
\`~/.config/botforje/actions/invalid.yml\`:
|
|
315
|
+
|
|
316
|
+
\`\`\`yaml
|
|
317
|
+
steps:
|
|
318
|
+
- message:
|
|
319
|
+
body: "Invalid option. Choose a number from the menu."
|
|
320
|
+
\`\`\`
|
|
321
|
+
|
|
322
|
+
\`~/.config/botforje/graphs/support.yml\`:
|
|
323
|
+
|
|
324
|
+
\`\`\`yaml
|
|
325
|
+
root: greet
|
|
326
|
+
timeout: 300
|
|
327
|
+
fallback: invalid
|
|
328
|
+
nodes:
|
|
329
|
+
greet:
|
|
330
|
+
action: greet
|
|
331
|
+
edges:
|
|
332
|
+
- match: "menu, help, continue"
|
|
333
|
+
goto: main_menu
|
|
334
|
+
- goto: invalid
|
|
335
|
+
main_menu:
|
|
336
|
+
action: menu
|
|
337
|
+
edges:
|
|
338
|
+
- match: "1, hours, schedule"
|
|
339
|
+
goto: hours
|
|
340
|
+
- match: "0, exit, bye"
|
|
341
|
+
goto: farewell
|
|
342
|
+
- goto: invalid
|
|
343
|
+
hours:
|
|
344
|
+
action: hours
|
|
345
|
+
edges:
|
|
346
|
+
- match: "menu, back"
|
|
347
|
+
goto: main_menu
|
|
348
|
+
- match: "0, exit"
|
|
349
|
+
goto: farewell
|
|
350
|
+
- goto: invalid
|
|
351
|
+
invalid:
|
|
352
|
+
action: invalid
|
|
353
|
+
edges:
|
|
354
|
+
- goto: main_menu
|
|
355
|
+
farewell:
|
|
356
|
+
action: farewell
|
|
357
|
+
edges: []
|
|
358
|
+
\`\`\`
|
|
359
|
+
|
|
360
|
+
\`~/.config/botforje/bots/support.yml\`:
|
|
361
|
+
|
|
362
|
+
\`\`\`yaml
|
|
363
|
+
graph: support
|
|
364
|
+
settings:
|
|
365
|
+
queue_delay: 1000
|
|
366
|
+
ignore_groups: true
|
|
367
|
+
ignored_senders:
|
|
368
|
+
- "status@broadcast"
|
|
369
|
+
\`\`\`
|
|
370
|
+
|
|
371
|
+
---
|
|
372
|
+
|
|
373
|
+
## REST API Reference
|
|
374
|
+
|
|
375
|
+
When \`port\` is set in config, the daemon starts an HTTP API bound to the configured \`address\` (default \`127.0.0.1\`).
|
|
376
|
+
|
|
377
|
+
### Authentication
|
|
378
|
+
|
|
379
|
+
If \`key\` is configured in \`config.yml\`, every \`/api/*\` request (except \`/api/health\`) must include:
|
|
380
|
+
|
|
381
|
+
\`\`\`
|
|
382
|
+
Authorization: Bearer <key>
|
|
383
|
+
\`\`\`
|
|
384
|
+
|
|
385
|
+
If \`key\` is not set, the API is open (suitable for localhost-only or reverse-proxy-controlled setups).
|
|
386
|
+
|
|
387
|
+
### Endpoints
|
|
388
|
+
|
|
389
|
+
#### GET /api/health
|
|
390
|
+
|
|
391
|
+
Liveness probe. Always responds without authentication.
|
|
392
|
+
|
|
393
|
+
\`\`\`json
|
|
394
|
+
{ "status": "ok", "timestamp": "2025-01-01T00:00:00.000Z", "service": "Botforje API" }
|
|
395
|
+
\`\`\`
|
|
396
|
+
|
|
397
|
+
#### GET /api/status
|
|
398
|
+
|
|
399
|
+
Detailed status of all bots with session state.
|
|
400
|
+
|
|
401
|
+
\`\`\`bash
|
|
402
|
+
curl http://localhost:3000/api/status
|
|
403
|
+
\`\`\`
|
|
404
|
+
|
|
405
|
+
\`\`\`json
|
|
406
|
+
{
|
|
407
|
+
"running": true,
|
|
408
|
+
"bots": [
|
|
409
|
+
{
|
|
410
|
+
"id": "support-bot",
|
|
411
|
+
"graph": "faq-support",
|
|
412
|
+
"phone": "521234567890@c.us",
|
|
413
|
+
"session": { "state": "connected", "lastQR": false, "error": null }
|
|
414
|
+
}
|
|
415
|
+
],
|
|
416
|
+
"total": 1
|
|
417
|
+
}
|
|
418
|
+
\`\`\`
|
|
419
|
+
|
|
420
|
+
#### GET /api/bots
|
|
421
|
+
|
|
422
|
+
List all bots with their configuration.
|
|
423
|
+
|
|
424
|
+
\`\`\`bash
|
|
425
|
+
curl http://localhost:3000/api/bots
|
|
426
|
+
\`\`\`
|
|
427
|
+
|
|
428
|
+
\`\`\`json
|
|
429
|
+
{ "bots": [{ "id": "support-bot", "phone": "...", "graph": "faq-support", "settings": {} }], "total": 1 }
|
|
430
|
+
\`\`\`
|
|
431
|
+
|
|
432
|
+
#### GET /api/bots/:botId
|
|
433
|
+
|
|
434
|
+
Single bot info.
|
|
435
|
+
|
|
436
|
+
\`\`\`bash
|
|
437
|
+
curl http://localhost:3000/api/bots/support-bot
|
|
438
|
+
\`\`\`
|
|
439
|
+
|
|
440
|
+
#### POST /api/messages/send
|
|
441
|
+
|
|
442
|
+
Enqueue an outgoing WhatsApp message.
|
|
443
|
+
|
|
444
|
+
\`\`\`bash
|
|
445
|
+
curl -X POST http://localhost:3000/api/messages/send \\
|
|
446
|
+
-H 'Content-Type: application/json' \\
|
|
447
|
+
-d '{"botId": "support-bot", "to": "521234567890@c.us", "content": "Hello from the API!", "metadata": {"source": "webhook"}}'
|
|
448
|
+
\`\`\`
|
|
449
|
+
|
|
450
|
+
\`\`\`json
|
|
451
|
+
{ "success": true, "messageId": "uuid", "botId": "support-bot", "queued": true }
|
|
452
|
+
\`\`\`
|
|
453
|
+
|
|
454
|
+
| Field | Type | Required | Description |
|
|
455
|
+
|---|---|---|---|
|
|
456
|
+
| botId | string | yes | Bot ID to send from |
|
|
457
|
+
| to | string | yes | Recipient WhatsApp ID (number@c.us) |
|
|
458
|
+
| content | string | yes | Message body (supports templates) |
|
|
459
|
+
| metadata | object | no | Arbitrary metadata attached to the message |
|
|
460
|
+
|
|
461
|
+
#### GET /api/messages/queue
|
|
462
|
+
|
|
463
|
+
All message queues status.
|
|
464
|
+
|
|
465
|
+
\`\`\`bash
|
|
466
|
+
curl http://localhost:3000/api/messages/queue
|
|
467
|
+
\`\`\`
|
|
468
|
+
|
|
469
|
+
#### GET /api/messages/queue/:botId
|
|
470
|
+
|
|
471
|
+
Per-bot queue status.
|
|
472
|
+
|
|
473
|
+
\`\`\`bash
|
|
474
|
+
curl http://localhost:3000/api/messages/queue/support-bot
|
|
475
|
+
\`\`\`
|
|
476
|
+
|
|
477
|
+
#### GET /api/sessions
|
|
478
|
+
|
|
479
|
+
List all registered WhatsApp sessions.
|
|
480
|
+
|
|
481
|
+
\`\`\`bash
|
|
482
|
+
curl http://localhost:3000/api/sessions
|
|
483
|
+
\`\`\`
|
|
484
|
+
|
|
485
|
+
#### POST /api/sessions/:id
|
|
486
|
+
|
|
487
|
+
Register/initiate a WhatsApp session. If the session already exists and is pending, returns current state. If already connected, returns 409.
|
|
488
|
+
|
|
489
|
+
\`\`\`bash
|
|
490
|
+
curl -X POST http://localhost:3000/api/sessions/support-bot
|
|
491
|
+
\`\`\`
|
|
492
|
+
|
|
493
|
+
\`\`\`json
|
|
494
|
+
{ "success": true, "id": "support-bot", "session": { "state": "pending" } }
|
|
495
|
+
\`\`\`
|
|
496
|
+
|
|
497
|
+
#### GET /api/sessions/:id
|
|
498
|
+
|
|
499
|
+
Get session info by bot ID.
|
|
500
|
+
|
|
501
|
+
\`\`\`bash
|
|
502
|
+
curl http://localhost:3000/api/sessions/support-bot
|
|
503
|
+
\`\`\`
|
|
504
|
+
|
|
505
|
+
#### DELETE /api/sessions/:id
|
|
506
|
+
|
|
507
|
+
Remove/disconnect a WhatsApp session.
|
|
508
|
+
|
|
509
|
+
\`\`\`bash
|
|
510
|
+
curl -X DELETE http://localhost:3000/api/sessions/support-bot
|
|
511
|
+
\`\`\`
|
|
512
|
+
|
|
513
|
+
#### GET /api/sessions/:id/events
|
|
514
|
+
|
|
515
|
+
Server-Sent Events (SSE) stream for session lifecycle. Useful for QR authentication programmatically.
|
|
516
|
+
|
|
517
|
+
\`\`\`bash
|
|
518
|
+
curl -N http://localhost:3000/api/sessions/support-bot/events
|
|
519
|
+
\`\`\`
|
|
520
|
+
|
|
521
|
+
Events:
|
|
522
|
+
|
|
523
|
+
| event type | data | description |
|
|
524
|
+
|---|---|---|
|
|
525
|
+
| \`qr\` | \`{ "qr": "base64..." }\` | QR code for authentication |
|
|
526
|
+
| \`ready\` | \`{ "phone": "521234567890@c.us" }\` | Session connected |
|
|
527
|
+
| \`auth_failure\` | \`{ "error": "..." }\` | Authentication failed |
|
|
528
|
+
| \`disconnected\` | \`{}\` | Session disconnected |
|
|
529
|
+
|
|
530
|
+
#### POST /api/config/reload
|
|
531
|
+
|
|
532
|
+
Trigger a hot reload of the configuration from disk.
|
|
533
|
+
|
|
534
|
+
\`\`\`bash
|
|
535
|
+
curl -X POST http://localhost:3000/api/config/reload
|
|
536
|
+
\`\`\`
|
|
537
|
+
|
|
538
|
+
#### GET /api/config/status
|
|
539
|
+
|
|
540
|
+
Check if the config file watcher is active.
|
|
541
|
+
|
|
542
|
+
\`\`\`bash
|
|
543
|
+
curl http://localhost:3000/api/config/status
|
|
544
|
+
\`\`\`
|
|
545
|
+
|
|
546
|
+
\`\`\`json
|
|
547
|
+
{ "watching": true }
|
|
548
|
+
\`\`\`
|
|
549
|
+
|
|
550
|
+
---
|
|
551
|
+
|
|
552
|
+
## Quick Start CLI Commands
|
|
553
|
+
|
|
554
|
+
\`\`\`
|
|
555
|
+
botforje daemon # Start the bot daemon
|
|
556
|
+
botforje status # Show bot session status
|
|
557
|
+
botforje auth <botId> # Authenticate a bot
|
|
558
|
+
botforje setup # Setup systemd service
|
|
559
|
+
botforje guide # Show this guide
|
|
560
|
+
\`\`\`
|
|
561
|
+
`;
|
|
562
|
+
console.log(guide);
|
|
563
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runLock = runLock;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const service_1 = require("../auth/service");
|
|
40
|
+
const yaml_1 = require("../config/yaml");
|
|
41
|
+
const data_1 = require("../helpers/data");
|
|
42
|
+
async function runLock(key, configPath) {
|
|
43
|
+
try {
|
|
44
|
+
if (configPath)
|
|
45
|
+
(0, yaml_1.setConfigPath)(configPath);
|
|
46
|
+
await (0, yaml_1.loadConfig)(configPath);
|
|
47
|
+
const dataDir = (0, data_1.getDataDir)();
|
|
48
|
+
fs.mkdirSync(dataDir, { recursive: true });
|
|
49
|
+
const dbPath = path.join(dataDir, 'botforje.db');
|
|
50
|
+
const auth = new service_1.AuthService(dbPath);
|
|
51
|
+
if (auth.isLocked()) {
|
|
52
|
+
console.error('Botforje is already locked. Run `botforje unlock` first to reset the key.');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
if (key && key.length < 32) {
|
|
56
|
+
console.error('Key must be at least 32 characters long.');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
const secret = auth.lock(key);
|
|
60
|
+
console.log('\n' + '='.repeat(60));
|
|
61
|
+
console.log('BOTFORJE AUTH KEY');
|
|
62
|
+
console.log('='.repeat(60));
|
|
63
|
+
console.log(`\n${secret}\n`);
|
|
64
|
+
console.log('-'.repeat(60));
|
|
65
|
+
console.log('IMPORTANT: Save this key somewhere safe. It CANNOT be recovered.');
|
|
66
|
+
console.log('='.repeat(60) + '\n');
|
|
67
|
+
process.exit(0);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.error(`\nError: ${error.message || error}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}
|