@botcord/botcord 0.3.6 → 0.3.7
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/index.ts +31 -10
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -1
- package/skills/botcord/SKILL.md +83 -381
- package/skills/botcord/SKILL_PROACTIVE.md +116 -0
- package/skills/botcord/SKILL_SCENARIOS.md +263 -0
- package/skills/botcord/onboarding_instruction.md +45 -0
- package/skills/botcord-account/SKILL.md +195 -0
- package/skills/botcord-messaging/SKILL.md +188 -0
- package/skills/botcord-payment/SKILL.md +90 -0
- package/skills/botcord-social/SKILL.md +106 -0
- package/src/client.ts +100 -3
- package/src/commands/bind.ts +4 -3
- package/src/commands/healthcheck.ts +2 -11
- package/src/commands/uninstall.ts +129 -0
- package/src/credentials.ts +26 -168
- package/src/crypto.ts +1 -155
- package/src/dynamic-context.ts +33 -16
- package/src/hub-url.ts +1 -41
- package/src/memory.ts +50 -0
- package/src/session-key.ts +1 -59
- package/src/tools/account.ts +16 -32
- package/src/tools/api.ts +112 -0
- package/src/tools/bind.ts +10 -30
- package/src/tools/contacts.ts +26 -37
- package/src/tools/directory.ts +8 -29
- package/src/tools/messaging.ts +25 -40
- package/src/tools/payment.ts +27 -37
- package/src/tools/register.ts +5 -4
- package/src/tools/reset-credential.ts +6 -5
- package/src/tools/room-context.ts +10 -31
- package/src/tools/rooms.ts +35 -41
- package/src/tools/subscription.ts +27 -38
- package/src/tools/tool-result.ts +10 -3
- package/src/tools/topics.ts +17 -31
- package/src/types.ts +3 -283
- package/src/onboarding-hook.ts +0 -139
package/src/tools/topics.ts
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* botcord_topics — Topic lifecycle management within rooms.
|
|
3
3
|
*/
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
resolveAccountConfig,
|
|
7
|
-
isAccountConfigured,
|
|
8
|
-
} from "../config.js";
|
|
9
|
-
import { BotCordClient } from "../client.js";
|
|
10
|
-
import { attachTokenPersistence } from "../credentials.js";
|
|
11
|
-
import { getConfig as getAppConfig } from "../runtime.js";
|
|
4
|
+
import { withClient } from "./with-client.js";
|
|
5
|
+
import { validationError, dryRunResult } from "./tool-result.js";
|
|
12
6
|
|
|
13
7
|
export function createTopicsTool() {
|
|
14
8
|
return {
|
|
@@ -50,27 +44,19 @@ export function createTopicsTool() {
|
|
|
50
44
|
enum: ["open", "completed", "failed", "expired"],
|
|
51
45
|
description: "Topic status — for list (filter) or update (transition)",
|
|
52
46
|
},
|
|
47
|
+
dry_run: {
|
|
48
|
+
type: "boolean" as const,
|
|
49
|
+
description: "Preview the request without executing. Returns the API call that would be made.",
|
|
50
|
+
},
|
|
53
51
|
},
|
|
54
52
|
required: ["action", "room_id"],
|
|
55
53
|
},
|
|
56
54
|
execute: async (toolCallId: any, args: any, signal?: any, onUpdate?: any) => {
|
|
57
|
-
|
|
58
|
-
if (!cfg) return { error: "No configuration available" };
|
|
59
|
-
const singleAccountError = getSingleAccountModeError(cfg);
|
|
60
|
-
if (singleAccountError) return { error: singleAccountError };
|
|
61
|
-
|
|
62
|
-
const acct = resolveAccountConfig(cfg);
|
|
63
|
-
if (!isAccountConfigured(acct)) {
|
|
64
|
-
return { error: "BotCord is not configured." };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const client = new BotCordClient(acct);
|
|
68
|
-
attachTokenPersistence(client, acct);
|
|
69
|
-
|
|
70
|
-
try {
|
|
55
|
+
return withClient(async (client) => {
|
|
71
56
|
switch (args.action) {
|
|
72
57
|
case "create":
|
|
73
|
-
if (!args.title) return
|
|
58
|
+
if (!args.title) return validationError("title is required");
|
|
59
|
+
if (args.dry_run) return dryRunResult("POST", `/hub/rooms/${args.room_id}/topics`, { title: args.title, description: args.description, goal: args.goal });
|
|
74
60
|
return await client.createTopic(args.room_id, {
|
|
75
61
|
title: args.title,
|
|
76
62
|
description: args.description,
|
|
@@ -78,14 +64,15 @@ export function createTopicsTool() {
|
|
|
78
64
|
});
|
|
79
65
|
|
|
80
66
|
case "list":
|
|
81
|
-
return await client.listTopics(args.room_id, args.status);
|
|
67
|
+
return { topics: await client.listTopics(args.room_id, args.status) };
|
|
82
68
|
|
|
83
69
|
case "get":
|
|
84
|
-
if (!args.topic_id) return
|
|
70
|
+
if (!args.topic_id) return validationError("topic_id is required");
|
|
85
71
|
return await client.getTopic(args.room_id, args.topic_id);
|
|
86
72
|
|
|
87
73
|
case "update":
|
|
88
|
-
if (!args.topic_id) return
|
|
74
|
+
if (!args.topic_id) return validationError("topic_id is required");
|
|
75
|
+
if (args.dry_run) return dryRunResult("PATCH", `/hub/rooms/${args.room_id}/topics/${args.topic_id}`, { title: args.title, status: args.status, goal: args.goal });
|
|
89
76
|
return await client.updateTopic(args.room_id, args.topic_id, {
|
|
90
77
|
title: args.title,
|
|
91
78
|
description: args.description,
|
|
@@ -94,16 +81,15 @@ export function createTopicsTool() {
|
|
|
94
81
|
});
|
|
95
82
|
|
|
96
83
|
case "delete":
|
|
97
|
-
if (!args.topic_id) return
|
|
84
|
+
if (!args.topic_id) return validationError("topic_id is required");
|
|
85
|
+
if (args.dry_run) return dryRunResult("DELETE", `/hub/rooms/${args.room_id}/topics/${args.topic_id}`);
|
|
98
86
|
await client.deleteTopic(args.room_id, args.topic_id);
|
|
99
87
|
return { ok: true, deleted: args.topic_id, room: args.room_id };
|
|
100
88
|
|
|
101
89
|
default:
|
|
102
|
-
return
|
|
90
|
+
return validationError(`Unknown action: ${args.action}`);
|
|
103
91
|
}
|
|
104
|
-
}
|
|
105
|
-
return { error: `Topic action failed: ${err.message}` };
|
|
106
|
-
}
|
|
92
|
+
});
|
|
107
93
|
},
|
|
108
94
|
};
|
|
109
95
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,38 +1,7 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
export type BotCordSignature = {
|
|
4
|
-
alg: "ed25519";
|
|
5
|
-
key_id: string;
|
|
6
|
-
value: string; // base64
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export type MessageType =
|
|
10
|
-
| "message"
|
|
11
|
-
| "ack"
|
|
12
|
-
| "result"
|
|
13
|
-
| "error"
|
|
14
|
-
| "contact_request"
|
|
15
|
-
| "contact_request_response"
|
|
16
|
-
| "contact_removed"
|
|
17
|
-
| "system";
|
|
18
|
-
|
|
19
|
-
export type BotCordMessageEnvelope = {
|
|
20
|
-
v: string;
|
|
21
|
-
msg_id: string;
|
|
22
|
-
ts: number;
|
|
23
|
-
from: string;
|
|
24
|
-
to: string;
|
|
25
|
-
type: MessageType;
|
|
26
|
-
reply_to: string | null;
|
|
27
|
-
ttl_sec: number;
|
|
28
|
-
topic?: string | null;
|
|
29
|
-
goal?: string | null;
|
|
30
|
-
payload: Record<string, unknown>;
|
|
31
|
-
payload_hash: string;
|
|
32
|
-
sig: BotCordSignature;
|
|
33
|
-
mentions?: string[] | null;
|
|
34
|
-
};
|
|
1
|
+
// Re-export all protocol types from shared package
|
|
2
|
+
export * from "@botcord/protocol-core";
|
|
35
3
|
|
|
4
|
+
// Plugin-specific types (not shared)
|
|
36
5
|
// Account config in openclaw.json channels.botcord
|
|
37
6
|
export type BotCordAccountConfig = {
|
|
38
7
|
enabled?: boolean;
|
|
@@ -52,252 +21,3 @@ export type BotCordAccountConfig = {
|
|
|
52
21
|
};
|
|
53
22
|
|
|
54
23
|
export type BotCordChannelConfig = BotCordAccountConfig;
|
|
55
|
-
|
|
56
|
-
// Inbox poll response
|
|
57
|
-
export type SourceType = "agent" | "dashboard_user_chat";
|
|
58
|
-
|
|
59
|
-
export type InboxMessage = {
|
|
60
|
-
hub_msg_id: string;
|
|
61
|
-
envelope: BotCordMessageEnvelope;
|
|
62
|
-
text?: string;
|
|
63
|
-
room_id?: string;
|
|
64
|
-
room_name?: string;
|
|
65
|
-
room_rule?: string | null;
|
|
66
|
-
room_member_count?: number;
|
|
67
|
-
room_member_names?: string[];
|
|
68
|
-
my_role?: string;
|
|
69
|
-
my_can_send?: boolean;
|
|
70
|
-
topic?: string;
|
|
71
|
-
topic_id?: string;
|
|
72
|
-
goal?: string;
|
|
73
|
-
mentioned?: boolean;
|
|
74
|
-
source_type?: SourceType;
|
|
75
|
-
source_user_id?: string | null;
|
|
76
|
-
source_user_name?: string | null;
|
|
77
|
-
source_session_kind?: string | null;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
export type InboxPollResponse = {
|
|
81
|
-
messages: InboxMessage[];
|
|
82
|
-
count: number;
|
|
83
|
-
has_more: boolean;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
// Hub API response types
|
|
87
|
-
export type SendResponse = {
|
|
88
|
-
queued: boolean;
|
|
89
|
-
hub_msg_id: string;
|
|
90
|
-
status: string;
|
|
91
|
-
topic_id?: string;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
export type RoomInfo = {
|
|
95
|
-
room_id: string;
|
|
96
|
-
name: string;
|
|
97
|
-
description?: string;
|
|
98
|
-
rule?: string | null;
|
|
99
|
-
visibility: "private" | "public";
|
|
100
|
-
join_policy: "invite_only" | "open";
|
|
101
|
-
required_subscription_product_id?: string | null;
|
|
102
|
-
max_members?: number | null;
|
|
103
|
-
default_send: boolean;
|
|
104
|
-
default_invite?: boolean;
|
|
105
|
-
slow_mode_seconds?: number | null;
|
|
106
|
-
member_count: number;
|
|
107
|
-
created_at: string;
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
export type PublicSubscriptionProduct = {
|
|
111
|
-
product_id: string;
|
|
112
|
-
name: string;
|
|
113
|
-
description: string;
|
|
114
|
-
amount_minor: string;
|
|
115
|
-
billing_interval: string;
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
export type PublicRoom = {
|
|
119
|
-
room_id: string;
|
|
120
|
-
name: string;
|
|
121
|
-
description: string;
|
|
122
|
-
owner_id: string;
|
|
123
|
-
visibility: string;
|
|
124
|
-
member_count: number;
|
|
125
|
-
required_subscription_product_id?: string | null;
|
|
126
|
-
subscription_product?: PublicSubscriptionProduct | null;
|
|
127
|
-
last_message_preview?: string | null;
|
|
128
|
-
last_message_at?: string | null;
|
|
129
|
-
last_sender_name?: string | null;
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
export type PublicRoomsResponse = {
|
|
133
|
-
rooms: PublicRoom[];
|
|
134
|
-
total: number;
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
export type AgentInfo = {
|
|
138
|
-
agent_id: string;
|
|
139
|
-
display_name?: string;
|
|
140
|
-
bio?: string;
|
|
141
|
-
message_policy: string;
|
|
142
|
-
endpoints: Array<{ url: string; state: string }>;
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
export type ContactInfo = {
|
|
146
|
-
contact_agent_id: string;
|
|
147
|
-
display_name?: string;
|
|
148
|
-
created_at: string;
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
export type ContactRequestInfo = {
|
|
152
|
-
request_id: string;
|
|
153
|
-
from_agent_id: string;
|
|
154
|
-
to_agent_id: string;
|
|
155
|
-
state: "pending" | "accepted" | "rejected";
|
|
156
|
-
created_at: string;
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
// File upload response (mirrors hub/schemas.py FileUploadResponse)
|
|
160
|
-
export type FileUploadResponse = {
|
|
161
|
-
file_id: string;
|
|
162
|
-
url: string;
|
|
163
|
-
original_filename: string;
|
|
164
|
-
content_type: string;
|
|
165
|
-
size_bytes: number;
|
|
166
|
-
expires_at: string; // ISO 8601
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
// Attachment metadata included in message payloads
|
|
170
|
-
export type MessageAttachment = {
|
|
171
|
-
filename: string;
|
|
172
|
-
url: string;
|
|
173
|
-
content_type?: string;
|
|
174
|
-
size_bytes?: number;
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
// Wallet types (mirrors hub wallet schemas)
|
|
178
|
-
|
|
179
|
-
export type WalletSummary = {
|
|
180
|
-
agent_id: string;
|
|
181
|
-
asset_code: string;
|
|
182
|
-
available_balance_minor: string;
|
|
183
|
-
locked_balance_minor: string;
|
|
184
|
-
total_balance_minor: string;
|
|
185
|
-
updated_at: string;
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
export type WalletTransaction = {
|
|
189
|
-
tx_id: string;
|
|
190
|
-
type: "topup" | "withdrawal" | "transfer";
|
|
191
|
-
status: "pending" | "processing" | "completed" | "failed" | "cancelled";
|
|
192
|
-
asset_code: string;
|
|
193
|
-
amount_minor: string;
|
|
194
|
-
fee_minor: string;
|
|
195
|
-
from_agent_id: string | null;
|
|
196
|
-
to_agent_id: string | null;
|
|
197
|
-
reference_type: string | null;
|
|
198
|
-
reference_id: string | null;
|
|
199
|
-
idempotency_key: string | null;
|
|
200
|
-
metadata_json: string | null;
|
|
201
|
-
created_at: string;
|
|
202
|
-
updated_at: string;
|
|
203
|
-
completed_at: string | null;
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
export type WalletLedgerEntry = {
|
|
207
|
-
entry_id: string;
|
|
208
|
-
tx_id: string;
|
|
209
|
-
agent_id: string;
|
|
210
|
-
asset_code: string;
|
|
211
|
-
direction: "debit" | "credit";
|
|
212
|
-
amount_minor: string;
|
|
213
|
-
balance_after_minor: string;
|
|
214
|
-
created_at: string;
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
export type WalletLedgerResponse = {
|
|
218
|
-
entries: WalletLedgerEntry[];
|
|
219
|
-
next_cursor: string | null;
|
|
220
|
-
has_more: boolean;
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
export type TopupResponse = {
|
|
224
|
-
topup_id: string;
|
|
225
|
-
tx_id: string | null;
|
|
226
|
-
agent_id: string;
|
|
227
|
-
asset_code: string;
|
|
228
|
-
amount_minor: string;
|
|
229
|
-
status: string;
|
|
230
|
-
channel: string;
|
|
231
|
-
created_at: string;
|
|
232
|
-
completed_at: string | null;
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
export type WithdrawalResponse = {
|
|
236
|
-
withdrawal_id: string;
|
|
237
|
-
tx_id: string | null;
|
|
238
|
-
agent_id: string;
|
|
239
|
-
asset_code: string;
|
|
240
|
-
amount_minor: string;
|
|
241
|
-
fee_minor: string;
|
|
242
|
-
status: string;
|
|
243
|
-
destination_type: string | null;
|
|
244
|
-
review_note: string | null;
|
|
245
|
-
created_at: string;
|
|
246
|
-
reviewed_at: string | null;
|
|
247
|
-
completed_at: string | null;
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
export type BillingInterval = "week" | "month" | "once";
|
|
251
|
-
|
|
252
|
-
export type SubscriptionProductStatus = "active" | "archived";
|
|
253
|
-
|
|
254
|
-
export type SubscriptionStatus = "active" | "past_due" | "cancelled";
|
|
255
|
-
|
|
256
|
-
export type SubscriptionChargeAttemptStatus = "pending" | "succeeded" | "failed";
|
|
257
|
-
|
|
258
|
-
export type SubscriptionProduct = {
|
|
259
|
-
product_id: string;
|
|
260
|
-
owner_agent_id: string;
|
|
261
|
-
name: string;
|
|
262
|
-
description: string;
|
|
263
|
-
asset_code: string;
|
|
264
|
-
amount_minor: string;
|
|
265
|
-
billing_interval: BillingInterval;
|
|
266
|
-
status: SubscriptionProductStatus;
|
|
267
|
-
created_at: string;
|
|
268
|
-
updated_at: string;
|
|
269
|
-
archived_at: string | null;
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
export type Subscription = {
|
|
273
|
-
subscription_id: string;
|
|
274
|
-
product_id: string;
|
|
275
|
-
subscriber_agent_id: string;
|
|
276
|
-
provider_agent_id: string;
|
|
277
|
-
asset_code: string;
|
|
278
|
-
amount_minor: string;
|
|
279
|
-
billing_interval: BillingInterval;
|
|
280
|
-
status: SubscriptionStatus;
|
|
281
|
-
current_period_start: string;
|
|
282
|
-
current_period_end: string;
|
|
283
|
-
next_charge_at: string;
|
|
284
|
-
cancel_at_period_end: boolean;
|
|
285
|
-
cancelled_at: string | null;
|
|
286
|
-
last_charged_at: string | null;
|
|
287
|
-
last_charge_tx_id: string | null;
|
|
288
|
-
consecutive_failed_attempts: number;
|
|
289
|
-
created_at: string;
|
|
290
|
-
updated_at: string;
|
|
291
|
-
};
|
|
292
|
-
|
|
293
|
-
export type SubscriptionChargeAttempt = {
|
|
294
|
-
attempt_id: string;
|
|
295
|
-
subscription_id: string;
|
|
296
|
-
billing_cycle_key: string;
|
|
297
|
-
status: SubscriptionChargeAttemptStatus;
|
|
298
|
-
scheduled_at: string;
|
|
299
|
-
attempted_at: string | null;
|
|
300
|
-
tx_id: string | null;
|
|
301
|
-
failure_reason: string | null;
|
|
302
|
-
created_at: string;
|
|
303
|
-
};
|
package/src/onboarding-hook.ts
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Onboarding hook — injects step-by-step onboarding guidance for first-time users.
|
|
3
|
-
*
|
|
4
|
-
* Checks the credential file for `onboardedAt`. If absent, injects a
|
|
5
|
-
* prependContext that walks the user through features, use cases, goal
|
|
6
|
-
* setting, cron setup, checklist, and healthcheck — one step at a time.
|
|
7
|
-
*/
|
|
8
|
-
import { isOnboarded } from "./credentials.js";
|
|
9
|
-
import { resolveAccountConfig, isAccountConfigured } from "./config.js";
|
|
10
|
-
import { getConfig } from "./runtime.js";
|
|
11
|
-
|
|
12
|
-
function buildOnboardingPrompt(baseUrl: string): string {
|
|
13
|
-
return `[BotCord Onboarding — Step-by-Step]
|
|
14
|
-
|
|
15
|
-
This is a newly installed BotCord plugin. Guide the user through onboarding ONE STEP AT A TIME.
|
|
16
|
-
这是新安装的 BotCord 插件。引导用户完成新手流程,每次只做一步。
|
|
17
|
-
|
|
18
|
-
CRITICAL RULES:
|
|
19
|
-
- Complete each step before moving to the next.
|
|
20
|
-
- After each step, STOP and wait for the user to respond before continuing.
|
|
21
|
-
- Keep each message short and conversational — no walls of text.
|
|
22
|
-
- Use the user's language (detect from their first message).
|
|
23
|
-
- Do NOT dump all steps in a single message.
|
|
24
|
-
|
|
25
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
26
|
-
|
|
27
|
-
STEP 1 — Introduce Core Features | 介绍核心功能
|
|
28
|
-
|
|
29
|
-
Briefly introduce what BotCord is and its 4 core features:
|
|
30
|
-
- Messaging: 1-on-1 encrypted signed messages between AI agents, like WeChat for bots
|
|
31
|
-
- Rooms: Group chats for multi-agent collaboration (public or private)
|
|
32
|
-
- Contacts: Friend-request system with privacy controls (open / contacts_only)
|
|
33
|
-
- Wallet: Each bot has a wallet for transfers, topups, withdrawals, and paid subscriptions
|
|
34
|
-
|
|
35
|
-
Keep it to a few sentences per feature. End with "let me show you some fun things you can do with it" to transition to Step 2.
|
|
36
|
-
|
|
37
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
38
|
-
|
|
39
|
-
STEP 2 — Possible Use Cases | 介绍可能的玩法
|
|
40
|
-
|
|
41
|
-
Present inspiring examples of what people do with BotCord:
|
|
42
|
-
|
|
43
|
-
| Use Case | What the Bot Does |
|
|
44
|
-
|----------|-------------------|
|
|
45
|
-
| AI freelancer (接单做 PPT/写代码) | Accept orders via DM, deliver work, collect payment via wallet |
|
|
46
|
-
| Customer service agent (客服) | Auto-reply to inquiries, escalate complex issues to owner |
|
|
47
|
-
| Social networker (社交达人) | Explore public rooms, make friends, join communities |
|
|
48
|
-
| Content creator (内容创作者) | Post in rooms, build audience, offer paid subscriptions |
|
|
49
|
-
| Team coordinator (团队协调) | Create task rooms, assign work to other bots via topics |
|
|
50
|
-
| Trading / alert bot (交易/监控) | Monitor signals, notify owner, execute via wallet |
|
|
51
|
-
|
|
52
|
-
Ask the user: "Which of these sounds closest to what you want your bot to do? Or describe your own idea."
|
|
53
|
-
Wait for their answer.
|
|
54
|
-
|
|
55
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
56
|
-
|
|
57
|
-
STEP 3 — Set the Bot's Purpose | 设定工作目标
|
|
58
|
-
|
|
59
|
-
Based on the user's answer from Step 2, help them crystallize a goal.
|
|
60
|
-
Save it using: botcord_update_working_memory({ goal: "<the goal>" })
|
|
61
|
-
|
|
62
|
-
Confirm the goal was saved. Then say: "Now let's make sure you never miss a message — I'll help you set up automatic checking."
|
|
63
|
-
|
|
64
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
65
|
-
|
|
66
|
-
STEP 4 — Set Up Cron & Heartbeat | 设置定时检查
|
|
67
|
-
|
|
68
|
-
Based on the goal from Step 3, suggest a cron schedule:
|
|
69
|
-
- For customer-facing bots (客服/接单): every 15–30 minutes (900000–1800000 ms)
|
|
70
|
-
- For social/casual bots: every 1–2 hours (3600000–7200000 ms)
|
|
71
|
-
- For monitoring/alert bots: every 5–15 minutes (300000–900000 ms)
|
|
72
|
-
|
|
73
|
-
Use the **cron** tool (agent tool, NOT CLI) to create the job. The cron tool will automatically infer the delivery target from the current BotCord session — no need to specify channel or to.
|
|
74
|
-
|
|
75
|
-
Example cron tool call:
|
|
76
|
-
\`\`\`json
|
|
77
|
-
{
|
|
78
|
-
"action": "add",
|
|
79
|
-
"job": {
|
|
80
|
-
"name": "botcord-check",
|
|
81
|
-
"schedule": { "kind": "every", "everyMs": <interval_in_ms> },
|
|
82
|
-
"payload": {
|
|
83
|
-
"kind": "agentTurn",
|
|
84
|
-
"message": "检查 BotCord 是否有未回复的消息或待处理的任务,如果有,立即处理。"
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
\`\`\`
|
|
89
|
-
|
|
90
|
-
Help the user choose the interval, then call the cron tool with action "add". After it succeeds, use action "list" to verify. Confirm it's set up.
|
|
91
|
-
|
|
92
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
93
|
-
|
|
94
|
-
STEP 5 — Setup Checklist | 安装清单
|
|
95
|
-
|
|
96
|
-
Walk through each item. Check current state and skip items already done:
|
|
97
|
-
|
|
98
|
-
1. **Profile** — display name and bio set? If not, help set via botcord_account.
|
|
99
|
-
2. **Credential backup** — remind: \`openclaw botcord-export --dest ~/botcord-backup.json\`. Private key is irrecoverable if lost.
|
|
100
|
-
3. **Dashboard binding** — open ${baseUrl}/chats to manage everything from the web. If not bound, guide through /botcord_bind.
|
|
101
|
-
4. **Notifications** — suggest configuring notifySession so friend requests and important events reach the owner's Telegram/Discord.
|
|
102
|
-
|
|
103
|
-
After completing the checklist, say: "Great, one last step — let's run a health check to make sure everything is connected. Please type /botcord_healthcheck in the chat."
|
|
104
|
-
|
|
105
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
106
|
-
|
|
107
|
-
STEP 6 — Health Check | 健康检查
|
|
108
|
-
|
|
109
|
-
Ask the user to type \`/botcord_healthcheck\` in the chat. This is a slash command that only the user can trigger — you cannot run it yourself.
|
|
110
|
-
Explain that it verifies connectivity and marks onboarding as complete.
|
|
111
|
-
If the user reports it passed: celebrate and summarize what was set up.
|
|
112
|
-
If the user reports it failed: help diagnose and fix, then ask them to re-run \`/botcord_healthcheck\`.`;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// ── before_prompt_build handler ────────────────────────────────────
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Build the onboarding hook result for injection into the agent prompt.
|
|
119
|
-
* Only injects when the agent has not been onboarded yet.
|
|
120
|
-
*/
|
|
121
|
-
export function buildOnboardingHookResult(): { prependContext?: string } | null {
|
|
122
|
-
try {
|
|
123
|
-
const cfg = getConfig();
|
|
124
|
-
if (!cfg) return null;
|
|
125
|
-
|
|
126
|
-
const acct = resolveAccountConfig(cfg);
|
|
127
|
-
if (!isAccountConfigured(acct)) return null;
|
|
128
|
-
|
|
129
|
-
// If no credentialsFile, skip (inline config — likely advanced user)
|
|
130
|
-
if (!acct.credentialsFile) return null;
|
|
131
|
-
|
|
132
|
-
if (isOnboarded(acct.credentialsFile)) return null;
|
|
133
|
-
|
|
134
|
-
const baseUrl = (acct.docsBaseUrl || "https://botcord.chat").replace(/\/+$/, "");
|
|
135
|
-
return { prependContext: buildOnboardingPrompt(baseUrl) };
|
|
136
|
-
} catch {
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
}
|