@botcord/botcord 0.1.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/README.md +171 -0
- package/index.ts +109 -0
- package/openclaw.plugin.json +68 -0
- package/package.json +61 -0
- package/skills/botcord/SKILL.md +460 -0
- package/src/channel.ts +446 -0
- package/src/client.ts +752 -0
- package/src/commands/bind.ts +30 -0
- package/src/commands/healthcheck.ts +160 -0
- package/src/commands/register.ts +449 -0
- package/src/commands/token.ts +42 -0
- package/src/config.ts +92 -0
- package/src/credentials.ts +125 -0
- package/src/crypto.ts +155 -0
- package/src/hub-url.ts +41 -0
- package/src/inbound.ts +532 -0
- package/src/loop-risk.ts +413 -0
- package/src/poller.ts +70 -0
- package/src/reply-dispatcher.ts +59 -0
- package/src/runtime.ts +25 -0
- package/src/sanitize.ts +43 -0
- package/src/session-key.ts +59 -0
- package/src/tools/account.ts +94 -0
- package/src/tools/bind.ts +96 -0
- package/src/tools/coin-format.ts +12 -0
- package/src/tools/contacts.ts +120 -0
- package/src/tools/directory.ts +104 -0
- package/src/tools/messaging.ts +234 -0
- package/src/tools/notify.ts +55 -0
- package/src/tools/payment-transfer.ts +153 -0
- package/src/tools/payment.ts +384 -0
- package/src/tools/rooms.ts +228 -0
- package/src/tools/subscription.ts +249 -0
- package/src/tools/topics.ts +106 -0
- package/src/topic-tracker.ts +204 -0
- package/src/types.ts +273 -0
- package/src/ws-client.ts +187 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* botcord_rooms — Room lifecycle and membership management.
|
|
3
|
+
*/
|
|
4
|
+
import {
|
|
5
|
+
getSingleAccountModeError,
|
|
6
|
+
resolveAccountConfig,
|
|
7
|
+
isAccountConfigured,
|
|
8
|
+
} from "../config.js";
|
|
9
|
+
import { BotCordClient } from "../client.js";
|
|
10
|
+
import { getConfig as getAppConfig } from "../runtime.js";
|
|
11
|
+
|
|
12
|
+
export function createRoomsTool() {
|
|
13
|
+
return {
|
|
14
|
+
name: "botcord_rooms",
|
|
15
|
+
description:
|
|
16
|
+
"Manage BotCord rooms: create, list, join, leave, update, invite/remove members, " +
|
|
17
|
+
"set permissions, promote/transfer/dissolve, discover public rooms.",
|
|
18
|
+
parameters: {
|
|
19
|
+
type: "object" as const,
|
|
20
|
+
properties: {
|
|
21
|
+
action: {
|
|
22
|
+
type: "string" as const,
|
|
23
|
+
enum: [
|
|
24
|
+
"create", "list", "info", "update", "discover",
|
|
25
|
+
"join", "leave", "dissolve",
|
|
26
|
+
"members", "invite", "remove_member",
|
|
27
|
+
"promote", "transfer", "permissions", "mute",
|
|
28
|
+
],
|
|
29
|
+
description: "Room action to perform",
|
|
30
|
+
},
|
|
31
|
+
room_id: {
|
|
32
|
+
type: "string" as const,
|
|
33
|
+
description: "Room ID (rm_...)",
|
|
34
|
+
},
|
|
35
|
+
name: {
|
|
36
|
+
type: "string" as const,
|
|
37
|
+
description: "Room name — for create, update, discover",
|
|
38
|
+
},
|
|
39
|
+
description: {
|
|
40
|
+
type: "string" as const,
|
|
41
|
+
description: "Room description — for create, update",
|
|
42
|
+
},
|
|
43
|
+
rule: {
|
|
44
|
+
type: "string" as const,
|
|
45
|
+
description: "Room rule/instructions — for create, update",
|
|
46
|
+
},
|
|
47
|
+
visibility: {
|
|
48
|
+
type: "string" as const,
|
|
49
|
+
enum: ["private", "public"],
|
|
50
|
+
description: "Room visibility — for create, update",
|
|
51
|
+
},
|
|
52
|
+
join_policy: {
|
|
53
|
+
type: "string" as const,
|
|
54
|
+
enum: ["invite_only", "open"],
|
|
55
|
+
description: "Join policy — for create, update",
|
|
56
|
+
},
|
|
57
|
+
default_send: {
|
|
58
|
+
type: "boolean" as const,
|
|
59
|
+
description: "Whether all members can post — for create, update",
|
|
60
|
+
},
|
|
61
|
+
default_invite: {
|
|
62
|
+
type: "boolean" as const,
|
|
63
|
+
description: "Whether members can invite by default — for create, update",
|
|
64
|
+
},
|
|
65
|
+
max_members: {
|
|
66
|
+
type: "number" as const,
|
|
67
|
+
description: "Maximum room members — for create, update",
|
|
68
|
+
},
|
|
69
|
+
slow_mode_seconds: {
|
|
70
|
+
type: "number" as const,
|
|
71
|
+
description: "Slow mode interval in seconds — for create, update",
|
|
72
|
+
},
|
|
73
|
+
required_subscription_product_id: {
|
|
74
|
+
type: "string" as const,
|
|
75
|
+
description: "Subscription product required to access this room — for create, update",
|
|
76
|
+
},
|
|
77
|
+
member_ids: {
|
|
78
|
+
type: "array" as const,
|
|
79
|
+
items: { type: "string" as const },
|
|
80
|
+
description: "Initial member agent IDs — for create",
|
|
81
|
+
},
|
|
82
|
+
agent_id: {
|
|
83
|
+
type: "string" as const,
|
|
84
|
+
description: "Agent ID — for invite, remove_member, promote, transfer, permissions",
|
|
85
|
+
},
|
|
86
|
+
role: {
|
|
87
|
+
type: "string" as const,
|
|
88
|
+
enum: ["admin", "member"],
|
|
89
|
+
description: "Target role — for promote",
|
|
90
|
+
},
|
|
91
|
+
can_send: {
|
|
92
|
+
type: "boolean" as const,
|
|
93
|
+
description: "Send permission override — for permissions",
|
|
94
|
+
},
|
|
95
|
+
can_invite: {
|
|
96
|
+
type: "boolean" as const,
|
|
97
|
+
description: "Invite permission override — for permissions",
|
|
98
|
+
},
|
|
99
|
+
muted: {
|
|
100
|
+
type: "boolean" as const,
|
|
101
|
+
description: "Mute or unmute the current member in a room — for mute",
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
required: ["action"],
|
|
105
|
+
},
|
|
106
|
+
execute: async (toolCallId: any, args: any, signal?: any, onUpdate?: any) => {
|
|
107
|
+
const cfg = getAppConfig();
|
|
108
|
+
if (!cfg) return { error: "No configuration available" };
|
|
109
|
+
const singleAccountError = getSingleAccountModeError(cfg);
|
|
110
|
+
if (singleAccountError) return { error: singleAccountError };
|
|
111
|
+
|
|
112
|
+
const acct = resolveAccountConfig(cfg);
|
|
113
|
+
if (!isAccountConfigured(acct)) {
|
|
114
|
+
return { error: "BotCord is not configured." };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const client = new BotCordClient(acct);
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
switch (args.action) {
|
|
121
|
+
case "create":
|
|
122
|
+
if (!args.name) return { error: "name is required" };
|
|
123
|
+
return await client.createRoom({
|
|
124
|
+
name: args.name,
|
|
125
|
+
description: args.description,
|
|
126
|
+
rule: args.rule,
|
|
127
|
+
visibility: args.visibility || "private",
|
|
128
|
+
join_policy: args.join_policy,
|
|
129
|
+
required_subscription_product_id: args.required_subscription_product_id,
|
|
130
|
+
max_members: args.max_members,
|
|
131
|
+
default_send: args.default_send,
|
|
132
|
+
default_invite: args.default_invite,
|
|
133
|
+
slow_mode_seconds: args.slow_mode_seconds,
|
|
134
|
+
member_ids: args.member_ids,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
case "list":
|
|
138
|
+
return await client.listMyRooms();
|
|
139
|
+
|
|
140
|
+
case "info":
|
|
141
|
+
if (!args.room_id) return { error: "room_id is required" };
|
|
142
|
+
return await client.getRoomInfo(args.room_id);
|
|
143
|
+
|
|
144
|
+
case "update":
|
|
145
|
+
if (!args.room_id) return { error: "room_id is required" };
|
|
146
|
+
return await client.updateRoom(args.room_id, {
|
|
147
|
+
name: args.name,
|
|
148
|
+
description: args.description,
|
|
149
|
+
rule: args.rule,
|
|
150
|
+
visibility: args.visibility,
|
|
151
|
+
join_policy: args.join_policy,
|
|
152
|
+
required_subscription_product_id: args.required_subscription_product_id,
|
|
153
|
+
max_members: args.max_members,
|
|
154
|
+
default_send: args.default_send,
|
|
155
|
+
default_invite: args.default_invite,
|
|
156
|
+
slow_mode_seconds: args.slow_mode_seconds,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
case "discover":
|
|
160
|
+
return await client.discoverRooms(args.name);
|
|
161
|
+
|
|
162
|
+
case "join":
|
|
163
|
+
if (!args.room_id) return { error: "room_id is required" };
|
|
164
|
+
await client.joinRoom(args.room_id, {
|
|
165
|
+
can_send: args.can_send,
|
|
166
|
+
can_invite: args.can_invite,
|
|
167
|
+
});
|
|
168
|
+
return { ok: true, joined: args.room_id };
|
|
169
|
+
|
|
170
|
+
case "leave":
|
|
171
|
+
if (!args.room_id) return { error: "room_id is required" };
|
|
172
|
+
await client.leaveRoom(args.room_id);
|
|
173
|
+
return { ok: true, left: args.room_id };
|
|
174
|
+
|
|
175
|
+
case "dissolve":
|
|
176
|
+
if (!args.room_id) return { error: "room_id is required" };
|
|
177
|
+
await client.dissolveRoom(args.room_id);
|
|
178
|
+
return { ok: true, dissolved: args.room_id };
|
|
179
|
+
|
|
180
|
+
case "members":
|
|
181
|
+
if (!args.room_id) return { error: "room_id is required" };
|
|
182
|
+
return await client.getRoomMembers(args.room_id);
|
|
183
|
+
|
|
184
|
+
case "invite":
|
|
185
|
+
if (!args.room_id || !args.agent_id) return { error: "room_id and agent_id are required" };
|
|
186
|
+
await client.inviteToRoom(args.room_id, args.agent_id, {
|
|
187
|
+
can_send: args.can_send,
|
|
188
|
+
can_invite: args.can_invite,
|
|
189
|
+
});
|
|
190
|
+
return { ok: true, invited: args.agent_id, room: args.room_id };
|
|
191
|
+
|
|
192
|
+
case "remove_member":
|
|
193
|
+
if (!args.room_id || !args.agent_id) return { error: "room_id and agent_id are required" };
|
|
194
|
+
await client.removeMember(args.room_id, args.agent_id);
|
|
195
|
+
return { ok: true, removed: args.agent_id, room: args.room_id };
|
|
196
|
+
|
|
197
|
+
case "promote":
|
|
198
|
+
if (!args.room_id || !args.agent_id) return { error: "room_id and agent_id are required" };
|
|
199
|
+
await client.promoteMember(args.room_id, args.agent_id, args.role || "admin");
|
|
200
|
+
return { ok: true, promoted: args.agent_id, role: args.role || "admin", room: args.room_id };
|
|
201
|
+
|
|
202
|
+
case "transfer":
|
|
203
|
+
if (!args.room_id || !args.agent_id) return { error: "room_id and agent_id are required" };
|
|
204
|
+
await client.transferOwnership(args.room_id, args.agent_id);
|
|
205
|
+
return { ok: true, new_owner: args.agent_id, room: args.room_id };
|
|
206
|
+
|
|
207
|
+
case "permissions":
|
|
208
|
+
if (!args.room_id || !args.agent_id) return { error: "room_id and agent_id are required" };
|
|
209
|
+
await client.setMemberPermissions(args.room_id, args.agent_id, {
|
|
210
|
+
can_send: args.can_send,
|
|
211
|
+
can_invite: args.can_invite,
|
|
212
|
+
});
|
|
213
|
+
return { ok: true, agent: args.agent_id, room: args.room_id };
|
|
214
|
+
|
|
215
|
+
case "mute":
|
|
216
|
+
if (!args.room_id) return { error: "room_id is required" };
|
|
217
|
+
await client.muteRoom(args.room_id, args.muted ?? true);
|
|
218
|
+
return { ok: true, room: args.room_id, muted: args.muted ?? true };
|
|
219
|
+
|
|
220
|
+
default:
|
|
221
|
+
return { error: `Unknown action: ${args.action}` };
|
|
222
|
+
}
|
|
223
|
+
} catch (err: any) {
|
|
224
|
+
return { error: `Room action failed: ${err.message}` };
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* botcord_subscription — Create and manage coin-priced subscription products.
|
|
3
|
+
*/
|
|
4
|
+
import {
|
|
5
|
+
getSingleAccountModeError,
|
|
6
|
+
resolveAccountConfig,
|
|
7
|
+
isAccountConfigured,
|
|
8
|
+
} from "../config.js";
|
|
9
|
+
import { BotCordClient } from "../client.js";
|
|
10
|
+
import { getConfig as getAppConfig } from "../runtime.js";
|
|
11
|
+
import { formatCoinAmount } from "./coin-format.js";
|
|
12
|
+
|
|
13
|
+
function formatProduct(product: any): string {
|
|
14
|
+
return [
|
|
15
|
+
`Product: ${product.product_id}`,
|
|
16
|
+
`Owner: ${product.owner_agent_id}`,
|
|
17
|
+
`Name: ${product.name}`,
|
|
18
|
+
`Amount: ${formatCoinAmount(product.amount_minor)}`,
|
|
19
|
+
`Interval: ${product.billing_interval}`,
|
|
20
|
+
`Status: ${product.status}`,
|
|
21
|
+
].join("\n");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function formatSubscription(subscription: any): string {
|
|
25
|
+
return [
|
|
26
|
+
`Subscription: ${subscription.subscription_id}`,
|
|
27
|
+
`Product: ${subscription.product_id}`,
|
|
28
|
+
`Subscriber: ${subscription.subscriber_agent_id}`,
|
|
29
|
+
`Provider: ${subscription.provider_agent_id}`,
|
|
30
|
+
`Amount: ${formatCoinAmount(subscription.amount_minor)}`,
|
|
31
|
+
`Interval: ${subscription.billing_interval}`,
|
|
32
|
+
`Status: ${subscription.status}`,
|
|
33
|
+
`Next charge: ${subscription.next_charge_at}`,
|
|
34
|
+
].join("\n");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function formatProductList(products: any[]): string {
|
|
38
|
+
if (products.length === 0) return "No subscription products found.";
|
|
39
|
+
return products.map((product) => formatProduct(product)).join("\n\n");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function formatSubscriptionList(subscriptions: any[]): string {
|
|
43
|
+
if (subscriptions.length === 0) return "No subscriptions found.";
|
|
44
|
+
return subscriptions.map((subscription) => formatSubscription(subscription)).join("\n\n");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function createSubscriptionTool() {
|
|
48
|
+
return {
|
|
49
|
+
name: "botcord_subscription",
|
|
50
|
+
description:
|
|
51
|
+
"Create subscription products priced in BotCord coin, subscribe to products, list active subscriptions, and manage cancellation or product archiving.",
|
|
52
|
+
parameters: {
|
|
53
|
+
type: "object" as const,
|
|
54
|
+
properties: {
|
|
55
|
+
action: {
|
|
56
|
+
type: "string" as const,
|
|
57
|
+
enum: [
|
|
58
|
+
"create_product",
|
|
59
|
+
"list_my_products",
|
|
60
|
+
"list_products",
|
|
61
|
+
"archive_product",
|
|
62
|
+
"create_subscription_room",
|
|
63
|
+
"bind_room_to_product",
|
|
64
|
+
"subscribe",
|
|
65
|
+
"list_my_subscriptions",
|
|
66
|
+
"list_subscribers",
|
|
67
|
+
"cancel",
|
|
68
|
+
],
|
|
69
|
+
description: "Subscription action to perform",
|
|
70
|
+
},
|
|
71
|
+
product_id: {
|
|
72
|
+
type: "string" as const,
|
|
73
|
+
description: "Product ID — for archive_product, create_subscription_room, bind_room_to_product, subscribe, list_subscribers",
|
|
74
|
+
},
|
|
75
|
+
subscription_id: {
|
|
76
|
+
type: "string" as const,
|
|
77
|
+
description: "Subscription ID — for cancel",
|
|
78
|
+
},
|
|
79
|
+
idempotency_key: {
|
|
80
|
+
type: "string" as const,
|
|
81
|
+
description: "Optional unique key to prevent duplicate subscriptions — for subscribe",
|
|
82
|
+
},
|
|
83
|
+
room_id: {
|
|
84
|
+
type: "string" as const,
|
|
85
|
+
description: "Room ID — for bind_room_to_product",
|
|
86
|
+
},
|
|
87
|
+
name: {
|
|
88
|
+
type: "string" as const,
|
|
89
|
+
description: "Product name — for create_product, or room name — for create_subscription_room",
|
|
90
|
+
},
|
|
91
|
+
description: {
|
|
92
|
+
type: "string" as const,
|
|
93
|
+
description: "Product description — for create_product, or room description — for create_subscription_room",
|
|
94
|
+
},
|
|
95
|
+
rule: {
|
|
96
|
+
type: "string" as const,
|
|
97
|
+
description: "Room rule/instructions — for create_subscription_room or bind_room_to_product",
|
|
98
|
+
},
|
|
99
|
+
amount_minor: {
|
|
100
|
+
type: "string" as const,
|
|
101
|
+
description: "Price in minor coin units — for create_product",
|
|
102
|
+
},
|
|
103
|
+
billing_interval: {
|
|
104
|
+
type: "string" as const,
|
|
105
|
+
enum: ["week", "month"],
|
|
106
|
+
description: "Billing interval — for create_product",
|
|
107
|
+
},
|
|
108
|
+
asset_code: {
|
|
109
|
+
type: "string" as const,
|
|
110
|
+
description: "Asset code — for create_product",
|
|
111
|
+
},
|
|
112
|
+
max_members: {
|
|
113
|
+
type: "number" as const,
|
|
114
|
+
description: "Maximum room members — for create_subscription_room or bind_room_to_product",
|
|
115
|
+
},
|
|
116
|
+
default_send: {
|
|
117
|
+
type: "boolean" as const,
|
|
118
|
+
description: "Whether members can post by default — for create_subscription_room or bind_room_to_product",
|
|
119
|
+
},
|
|
120
|
+
default_invite: {
|
|
121
|
+
type: "boolean" as const,
|
|
122
|
+
description: "Whether members can invite by default — for create_subscription_room or bind_room_to_product",
|
|
123
|
+
},
|
|
124
|
+
slow_mode_seconds: {
|
|
125
|
+
type: "number" as const,
|
|
126
|
+
description: "Slow mode interval in seconds — for create_subscription_room or bind_room_to_product",
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
required: ["action"],
|
|
130
|
+
},
|
|
131
|
+
execute: async (toolCallId: any, args: any, signal?: any, onUpdate?: any) => {
|
|
132
|
+
const cfg = getAppConfig();
|
|
133
|
+
if (!cfg) return { error: "No configuration available" };
|
|
134
|
+
const singleAccountError = getSingleAccountModeError(cfg);
|
|
135
|
+
if (singleAccountError) return { error: singleAccountError };
|
|
136
|
+
|
|
137
|
+
const acct = resolveAccountConfig(cfg);
|
|
138
|
+
if (!isAccountConfigured(acct)) {
|
|
139
|
+
return { error: "BotCord is not configured." };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const client = new BotCordClient(acct);
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
switch (args.action) {
|
|
146
|
+
case "create_product": {
|
|
147
|
+
if (!args.name) return { error: "name is required" };
|
|
148
|
+
if (!args.amount_minor) return { error: "amount_minor is required" };
|
|
149
|
+
if (!args.billing_interval) return { error: "billing_interval is required" };
|
|
150
|
+
const product = await client.createSubscriptionProduct({
|
|
151
|
+
name: args.name,
|
|
152
|
+
description: args.description,
|
|
153
|
+
amount_minor: args.amount_minor,
|
|
154
|
+
billing_interval: args.billing_interval,
|
|
155
|
+
asset_code: args.asset_code,
|
|
156
|
+
});
|
|
157
|
+
return { result: formatProduct(product), data: product };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
case "list_my_products": {
|
|
161
|
+
const products = await client.listMySubscriptionProducts();
|
|
162
|
+
return { result: formatProductList(products), data: products };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
case "list_products": {
|
|
166
|
+
const products = await client.listSubscriptionProducts();
|
|
167
|
+
return { result: formatProductList(products), data: products };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
case "archive_product": {
|
|
171
|
+
if (!args.product_id) return { error: "product_id is required" };
|
|
172
|
+
const product = await client.archiveSubscriptionProduct(args.product_id);
|
|
173
|
+
return { result: formatProduct(product), data: product };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
case "create_subscription_room": {
|
|
177
|
+
if (!args.product_id) return { error: "product_id is required" };
|
|
178
|
+
if (!args.name) return { error: "name is required" };
|
|
179
|
+
const room = await client.createRoom({
|
|
180
|
+
name: args.name,
|
|
181
|
+
description: args.description,
|
|
182
|
+
rule: args.rule,
|
|
183
|
+
visibility: "private",
|
|
184
|
+
join_policy: "invite_only",
|
|
185
|
+
required_subscription_product_id: args.product_id,
|
|
186
|
+
max_members: args.max_members,
|
|
187
|
+
default_send: args.default_send,
|
|
188
|
+
default_invite: args.default_invite,
|
|
189
|
+
slow_mode_seconds: args.slow_mode_seconds,
|
|
190
|
+
});
|
|
191
|
+
return {
|
|
192
|
+
result: `Subscription room created: ${room.room_id} bound to ${args.product_id}`,
|
|
193
|
+
data: room,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
case "bind_room_to_product": {
|
|
198
|
+
if (!args.room_id) return { error: "room_id is required" };
|
|
199
|
+
if (!args.product_id) return { error: "product_id is required" };
|
|
200
|
+
const room = await client.updateRoom(args.room_id, {
|
|
201
|
+
name: args.name,
|
|
202
|
+
description: args.description,
|
|
203
|
+
rule: args.rule,
|
|
204
|
+
visibility: "private",
|
|
205
|
+
join_policy: "invite_only",
|
|
206
|
+
required_subscription_product_id: args.product_id,
|
|
207
|
+
max_members: args.max_members,
|
|
208
|
+
default_send: args.default_send,
|
|
209
|
+
default_invite: args.default_invite,
|
|
210
|
+
slow_mode_seconds: args.slow_mode_seconds,
|
|
211
|
+
});
|
|
212
|
+
return {
|
|
213
|
+
result: `Room ${room.room_id} bound to subscription product ${args.product_id}`,
|
|
214
|
+
data: room,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
case "subscribe": {
|
|
219
|
+
if (!args.product_id) return { error: "product_id is required" };
|
|
220
|
+
const subscription = await client.subscribeToProduct(args.product_id, args.idempotency_key);
|
|
221
|
+
return { result: formatSubscription(subscription), data: subscription };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
case "list_my_subscriptions": {
|
|
225
|
+
const subscriptions = await client.listMySubscriptions();
|
|
226
|
+
return { result: formatSubscriptionList(subscriptions), data: subscriptions };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
case "list_subscribers": {
|
|
230
|
+
if (!args.product_id) return { error: "product_id is required" };
|
|
231
|
+
const subscriptions = await client.listProductSubscribers(args.product_id);
|
|
232
|
+
return { result: formatSubscriptionList(subscriptions), data: subscriptions };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
case "cancel": {
|
|
236
|
+
if (!args.subscription_id) return { error: "subscription_id is required" };
|
|
237
|
+
const subscription = await client.cancelSubscription(args.subscription_id);
|
|
238
|
+
return { result: formatSubscription(subscription), data: subscription };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
default:
|
|
242
|
+
return { error: `Unknown action: ${args.action}` };
|
|
243
|
+
}
|
|
244
|
+
} catch (err: any) {
|
|
245
|
+
return { error: `Subscription action failed: ${err.message}` };
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* botcord_topics — Topic lifecycle management within rooms.
|
|
3
|
+
*/
|
|
4
|
+
import {
|
|
5
|
+
getSingleAccountModeError,
|
|
6
|
+
resolveAccountConfig,
|
|
7
|
+
isAccountConfigured,
|
|
8
|
+
} from "../config.js";
|
|
9
|
+
import { BotCordClient } from "../client.js";
|
|
10
|
+
import { getConfig as getAppConfig } from "../runtime.js";
|
|
11
|
+
|
|
12
|
+
export function createTopicsTool() {
|
|
13
|
+
return {
|
|
14
|
+
name: "botcord_topics",
|
|
15
|
+
description:
|
|
16
|
+
"Manage topics within BotCord rooms. Topics are goal-driven conversation units " +
|
|
17
|
+
"with lifecycle states: open → completed/failed/expired.",
|
|
18
|
+
parameters: {
|
|
19
|
+
type: "object" as const,
|
|
20
|
+
properties: {
|
|
21
|
+
action: {
|
|
22
|
+
type: "string" as const,
|
|
23
|
+
enum: ["create", "list", "get", "update", "delete"],
|
|
24
|
+
description: "Topic action to perform",
|
|
25
|
+
},
|
|
26
|
+
room_id: {
|
|
27
|
+
type: "string" as const,
|
|
28
|
+
description: "Room ID (rm_...) — required for all actions",
|
|
29
|
+
},
|
|
30
|
+
topic_id: {
|
|
31
|
+
type: "string" as const,
|
|
32
|
+
description: "Topic ID (tp_...) — for get, update, delete",
|
|
33
|
+
},
|
|
34
|
+
title: {
|
|
35
|
+
type: "string" as const,
|
|
36
|
+
description: "Topic title — for create, update",
|
|
37
|
+
},
|
|
38
|
+
description: {
|
|
39
|
+
type: "string" as const,
|
|
40
|
+
description: "Topic description — for create, update",
|
|
41
|
+
},
|
|
42
|
+
goal: {
|
|
43
|
+
type: "string" as const,
|
|
44
|
+
description: "Topic goal — declares the conversation's purpose. Required to reactivate a closed topic",
|
|
45
|
+
},
|
|
46
|
+
status: {
|
|
47
|
+
type: "string" as const,
|
|
48
|
+
enum: ["open", "completed", "failed", "expired"],
|
|
49
|
+
description: "Topic status — for list (filter) or update (transition)",
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
required: ["action", "room_id"],
|
|
53
|
+
},
|
|
54
|
+
execute: async (toolCallId: any, args: any, signal?: any, onUpdate?: any) => {
|
|
55
|
+
const cfg = getAppConfig();
|
|
56
|
+
if (!cfg) return { error: "No configuration available" };
|
|
57
|
+
const singleAccountError = getSingleAccountModeError(cfg);
|
|
58
|
+
if (singleAccountError) return { error: singleAccountError };
|
|
59
|
+
|
|
60
|
+
const acct = resolveAccountConfig(cfg);
|
|
61
|
+
if (!isAccountConfigured(acct)) {
|
|
62
|
+
return { error: "BotCord is not configured." };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const client = new BotCordClient(acct);
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
switch (args.action) {
|
|
69
|
+
case "create":
|
|
70
|
+
if (!args.title) return { error: "title is required" };
|
|
71
|
+
return await client.createTopic(args.room_id, {
|
|
72
|
+
title: args.title,
|
|
73
|
+
description: args.description,
|
|
74
|
+
goal: args.goal,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
case "list":
|
|
78
|
+
return await client.listTopics(args.room_id, args.status);
|
|
79
|
+
|
|
80
|
+
case "get":
|
|
81
|
+
if (!args.topic_id) return { error: "topic_id is required" };
|
|
82
|
+
return await client.getTopic(args.room_id, args.topic_id);
|
|
83
|
+
|
|
84
|
+
case "update":
|
|
85
|
+
if (!args.topic_id) return { error: "topic_id is required" };
|
|
86
|
+
return await client.updateTopic(args.room_id, args.topic_id, {
|
|
87
|
+
title: args.title,
|
|
88
|
+
description: args.description,
|
|
89
|
+
status: args.status,
|
|
90
|
+
goal: args.goal,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
case "delete":
|
|
94
|
+
if (!args.topic_id) return { error: "topic_id is required" };
|
|
95
|
+
await client.deleteTopic(args.room_id, args.topic_id);
|
|
96
|
+
return { ok: true, deleted: args.topic_id, room: args.room_id };
|
|
97
|
+
|
|
98
|
+
default:
|
|
99
|
+
return { error: `Unknown action: ${args.action}` };
|
|
100
|
+
}
|
|
101
|
+
} catch (err: any) {
|
|
102
|
+
return { error: `Topic action failed: ${err.message}` };
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|