koishi-plugin-dynamic-bot 0.0.0-dev → 0.0.3

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/lib/index.js ADDED
@@ -0,0 +1,1942 @@
1
+ // src/index.ts
2
+ import { Schema } from "koishi";
3
+
4
+ // src/dbk/gateway.ts
5
+ import { create as create4 } from "@bufbuild/protobuf";
6
+
7
+ // src/gen/dbk/v1/common_pb.ts
8
+ import { enumDesc, fileDesc, messageDesc } from "@bufbuild/protobuf/codegenv2";
9
+ var file_dbk_v1_common = /* @__PURE__ */ fileDesc("ChNkYmsvdjEvY29tbW9uLnByb3RvEgZkYmsudjEiSAoGVGFyZ2V0EiAKBGtpbmQYASABKA4yEi5kYmsudjEuVGFyZ2V0S2luZBIKCgJpZBgCIAEoCRIQCghndWlsZF9pZBgDIAEoCSI7CghScGNFcnJvchIfCgRjb2RlGAEgASgOMhEuZGJrLnYxLkVycm9yQ29kZRIOCgZkZXRhaWwYAiABKAkqhwEKClRhcmdldEtpbmQSGwoXVEFSR0VUX0tJTkRfVU5TUEVDSUZJRUQQABIUChBUQVJHRVRfS0lORF9VU0VSEAESFQoRVEFSR0VUX0tJTkRfR1JPVVAQAhIXChNUQVJHRVRfS0lORF9DSEFOTkVMEAMSFgoSVEFSR0VUX0tJTkRfVEhSRUFEEAQqdAoJQm90U3RhdHVzEhoKFkJPVF9TVEFUVVNfVU5TUEVDSUZJRUQQABIUChBCT1RfU1RBVFVTX1JFQURZEAESGgoWQk9UX1NUQVRVU19VTkFWQUlMQUJMRRACEhkKFUJPVF9TVEFUVVNfQ09OTkVDVElORxADKocBCgpTZW5kU3RhdHVzEhsKF1NFTkRfU1RBVFVTX1VOU1BFQ0lGSUVEEAASEgoOU0VORF9TVEFUVVNfT0sQARIXChNTRU5EX1NUQVRVU19QQVJUSUFMEAISFwoTU0VORF9TVEFUVVNfVU5LTk9XThADEhYKElNFTkRfU1RBVFVTX0ZBSUxFRBAEKqwBCglFcnJvckNvZGUSGgoWRVJST1JfQ09ERV9VTlNQRUNJRklFRBAAEhsKF0VSUk9SX0NPREVfVU5BVVRIT1JJWkVEEAESFwoTRVJST1JfQ09ERV9QUk9UT0NPTBACEhgKFEVSUk9SX0NPREVfTk9UX0ZPVU5EEAMSGgoWRVJST1JfQ09ERV9VTlNVUFBPUlRFRBAEEhcKE0VSUk9SX0NPREVfSU5URVJOQUwQBSqFAQoNQm90Q2hhbmdlVHlwZRIfChtCT1RfQ0hBTkdFX1RZUEVfVU5TUEVDSUZJRUQQABIZChVCT1RfQ0hBTkdFX1RZUEVfQURERUQQARIbChdCT1RfQ0hBTkdFX1RZUEVfVVBEQVRFRBACEhsKF0JPVF9DSEFOR0VfVFlQRV9SRU1PVkVEEANiBnByb3RvMw");
10
+ var TargetSchema = /* @__PURE__ */ messageDesc(file_dbk_v1_common, 0);
11
+ var RpcErrorSchema = /* @__PURE__ */ messageDesc(file_dbk_v1_common, 1);
12
+ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
13
+ ErrorCode2[ErrorCode2["UNSPECIFIED"] = 0] = "UNSPECIFIED";
14
+ ErrorCode2[ErrorCode2["UNAUTHORIZED"] = 1] = "UNAUTHORIZED";
15
+ ErrorCode2[ErrorCode2["PROTOCOL"] = 2] = "PROTOCOL";
16
+ ErrorCode2[ErrorCode2["NOT_FOUND"] = 3] = "NOT_FOUND";
17
+ ErrorCode2[ErrorCode2["UNSUPPORTED"] = 4] = "UNSUPPORTED";
18
+ ErrorCode2[ErrorCode2["INTERNAL"] = 5] = "INTERNAL";
19
+ return ErrorCode2;
20
+ })(ErrorCode || {});
21
+
22
+ // src/gen/dbk/v1/rpc_pb.ts
23
+ import { fileDesc as fileDesc2, messageDesc as messageDesc2 } from "@bufbuild/protobuf/codegenv2";
24
+ var file_dbk_v1_rpc = /* @__PURE__ */ fileDesc2("ChBkYmsvdjEvcnBjLnByb3RvEgZkYmsudjEiTAoMSGVsbG9SZXF1ZXN0Eg0KBXRva2VuGAEgASgJEhMKC2FwcF92ZXJzaW9uGAIgASgJEhgKEHByb3RvY29sX3ZlcnNpb24YAyABKAkiXQoNSGVsbG9SZXNwb25zZRIYChBwcm90b2NvbF92ZXJzaW9uGAEgASgJEhcKD2dhdGV3YXlfdmVyc2lvbhgCIAEoCRIZCgRib3RzGAMgAygLMgsuZGJrLnYxLkJvdCIRCg9MaXN0Qm90c1JlcXVlc3QiLQoQTGlzdEJvdHNSZXNwb25zZRIZCgRib3RzGAEgAygLMgsuZGJrLnYxLkJvdCKMAQoDQm90Eg8KB2JvdF9rZXkYASABKAkSEAoIcGxhdGZvcm0YAiABKAkSDwoHc2VsZl9pZBgDIAEoCRIMCgRuYW1lGAQgASgJEg4KBmF2YXRhchgFIAEoCRIhCgZzdGF0dXMYBiABKA4yES5kYmsudjEuQm90U3RhdHVzEhAKCGZlYXR1cmVzGAcgAygJIlAKD0JvdENoYW5nZWRFdmVudBIjCgR0eXBlGAEgASgOMhUuZGJrLnYxLkJvdENoYW5nZVR5cGUSGAoDYm90GAIgASgLMgsuZGJrLnYxLkJvdCJHChJMaXN0VGFyZ2V0c1JlcXVlc3QSDwoHYm90X2tleRgBIAEoCRIgCgRraW5kGAIgASgOMhIuZGJrLnYxLlRhcmdldEtpbmQiTgoTTGlzdFRhcmdldHNSZXNwb25zZRIjCgd0YXJnZXRzGAEgAygLMhIuZGJrLnYxLlRhcmdldEluZm8SEgoKaW5jb21wbGV0ZRgCIAEoCCJDChBHZXRUYXJnZXRSZXF1ZXN0Eg8KB2JvdF9rZXkYASABKAkSHgoGdGFyZ2V0GAIgASgLMg4uZGJrLnYxLlRhcmdldCJLChFHZXRUYXJnZXRSZXNwb25zZRIiCgZ0YXJnZXQYASABKAsyEi5kYmsudjEuVGFyZ2V0SW5mbxISCgp1bnJlc29sdmVkGAIgASgIIoIBCgpUYXJnZXRJbmZvEh4KBnRhcmdldBgBIAEoCzIOLmRiay52MS5UYXJnZXQSDAoEbmFtZRgCIAEoCRIQCghndWlsZF9pZBgDIAEoCRISCgpndWlsZF9uYW1lGAQgASgJEhAKCGJvdF9rZXlzGAUgAygJEg4KBmF2YXRhchgGIAEoCSJ7CgpTZW5kUGFyYW1zEg8KB2JvdF9rZXkYASABKAkSHgoGdGFyZ2V0GAIgASgLMg4uZGJrLnYxLlRhcmdldBIbChNyZXBseV90b19tZXNzYWdlX2lkGAMgASgJEh8KBXVuaXRzGAQgAygLMhAuZGJrLnYxLlNlbmRVbml0ImAKCFNlbmRVbml0EiQKBm5vcm1hbBgBIAEoCzISLmRiay52MS5Ob3JtYWxVbml0SAASJgoHZm9yd2FyZBgCIAEoCzITLmRiay52MS5Gb3J3YXJkVW5pdEgAQgYKBGJvZHkiLwoKTm9ybWFsVW5pdBIhCghzZWdtZW50cxgBIAMoCzIPLmRiay52MS5TZWdtZW50IjEKC0ZvcndhcmRVbml0EiIKBW5vZGVzGAEgAygLMhMuZGJrLnYxLkZvcndhcmROb2RlIn0KC0ZvcndhcmROb2RlEhEKCXNlbmRlcl9pZBgBIAEoCRITCgtzZW5kZXJfbmFtZRgCIAEoCRIVCg1zZW5kZXJfYXZhdGFyGAMgASgJEgwKBHRpbWUYBCABKAMSIQoIc2VnbWVudHMYBSADKAsyDy5kYmsudjEuU2VnbWVudCL/AgoHU2VnbWVudBIjCgR0ZXh0GAEgASgLMhMuZGJrLnYxLlRleHRTZWdtZW50SAASJQoFaW1hZ2UYAiABKAsyFC5kYmsudjEuSW1hZ2VTZWdtZW50SAASJQoFdmlkZW8YAyABKAsyFC5kYmsudjEuVmlkZW9TZWdtZW50SAASJQoFYXVkaW8YBCABKAsyFC5kYmsudjEuQXVkaW9TZWdtZW50SAASKQoHbWVudGlvbhgFIAEoCzIWLmRiay52MS5NZW50aW9uU2VnbWVudEgAEjAKC21lbnRpb25fYWxsGAYgASgLMhkuZGJrLnYxLk1lbnRpb25BbGxTZWdtZW50SAASJQoFcmVwbHkYByABKAsyFC5kYmsudjEuUmVwbHlTZWdtZW50SAASIwoEbGluaxgIIAEoCzITLmRiay52MS5MaW5rU2VnbWVudEgAEikKB3Vua25vd24YCSABKAsyFi5kYmsudjEuVW5rbm93blNlZ21lbnRIAEIGCgRib2R5IhsKC1RleHRTZWdtZW50EgwKBHRleHQYASABKAkiNgoMSW1hZ2VTZWdtZW50EgsKA3VyaRgBIAEoCRIMCgRmaWxlGAIgASgJEgsKA2FsdBgDIAEoCSI2CgxWaWRlb1NlZ21lbnQSCwoDdXJpGAEgASgJEgwKBGZpbGUYAiABKAkSCwoDYWx0GAMgASgJIjYKDEF1ZGlvU2VnbWVudBILCgN1cmkYASABKAkSDAoEZmlsZRgCIAEoCRILCgNhbHQYAyABKAkiHAoOTWVudGlvblNlZ21lbnQSCgoCaWQYASABKAkiEwoRTWVudGlvbkFsbFNlZ21lbnQiIgoMUmVwbHlTZWdtZW50EhIKCm1lc3NhZ2VfaWQYASABKAkiKQoLTGlua1NlZ21lbnQSCwoDdXJsGAEgASgJEg0KBXRpdGxlGAIgASgJIisKDlVua25vd25TZWdtZW50EgwKBHR5cGUYASABKAkSCwoDcmF3GAIgASgJInoKClNlbmRSZXN1bHQSIgoGc3RhdHVzGAEgASgOMhIuZGJrLnYxLlNlbmRTdGF0dXMSDgoGcmVhc29uGAIgASgJEhEKCXJldHJ5YWJsZRgDIAEoCBIlCghyZWNlaXB0cxgEIAMoCzITLmRiay52MS5TZW5kUmVjZWlwdCI1CgtTZW5kUmVjZWlwdBISCgptZXNzYWdlX2lkGAEgASgJEhIKCnJlY2FsbGFibGUYAiABKAgiUwoMUmVjYWxsUGFyYW1zEg8KB2JvdF9rZXkYASABKAkSHgoGdGFyZ2V0GAIgASgLMg4uZGJrLnYxLlRhcmdldBISCgptZXNzYWdlX2lkGAMgASgJIlUKDFJlY2FsbFJlc3VsdBIiCgZzdGF0dXMYASABKA4yEi5kYmsudjEuU2VuZFN0YXR1cxIOCgZyZWFzb24YAiABKAkSEQoJcmV0cnlhYmxlGAMgASgIIvsBCg9JbmNvbWluZ01lc3NhZ2USDwoHYm90X2tleRgBIAEoCRIQCghwbGF0Zm9ybRgCIAEoCRIeCgZ0YXJnZXQYAyABKAsyDi5kYmsudjEuVGFyZ2V0EhEKCXNlbmRlcl9pZBgEIAEoCRISCgptZXNzYWdlX2lkGAUgASgJEhEKCXRpbWVzdGFtcBgGIAEoAxIMCgR0ZXh0GAcgASgJEhsKE3JlcGx5X3RvX21lc3NhZ2VfaWQYCCABKAkSEAoIbWVudGlvbnMYCSADKAkSIQoIc2VnbWVudHMYCiADKAsyDy5kYmsudjEuU2VnbWVudBILCgNyYXcYCyABKAliBnByb3RvMw", [file_dbk_v1_common]);
25
+ var HelloRequestSchema = /* @__PURE__ */ messageDesc2(file_dbk_v1_rpc, 0);
26
+ var HelloResponseSchema = /* @__PURE__ */ messageDesc2(file_dbk_v1_rpc, 1);
27
+ var ListBotsRequestSchema = /* @__PURE__ */ messageDesc2(file_dbk_v1_rpc, 2);
28
+ var ListBotsResponseSchema = /* @__PURE__ */ messageDesc2(file_dbk_v1_rpc, 3);
29
+ var BotSchema = /* @__PURE__ */ messageDesc2(file_dbk_v1_rpc, 4);
30
+ var BotChangedEventSchema = /* @__PURE__ */ messageDesc2(file_dbk_v1_rpc, 5);
31
+ var ListTargetsRequestSchema = /* @__PURE__ */ messageDesc2(file_dbk_v1_rpc, 6);
32
+ var ListTargetsResponseSchema = /* @__PURE__ */ messageDesc2(file_dbk_v1_rpc, 7);
33
+ var GetTargetRequestSchema = /* @__PURE__ */ messageDesc2(file_dbk_v1_rpc, 8);
34
+ var GetTargetResponseSchema = /* @__PURE__ */ messageDesc2(file_dbk_v1_rpc, 9);
35
+ var TargetInfoSchema = /* @__PURE__ */ messageDesc2(file_dbk_v1_rpc, 10);
36
+ var SendParamsSchema = /* @__PURE__ */ messageDesc2(file_dbk_v1_rpc, 11);
37
+ var SendResultSchema = /* @__PURE__ */ messageDesc2(file_dbk_v1_rpc, 26);
38
+ var SendReceiptSchema = /* @__PURE__ */ messageDesc2(file_dbk_v1_rpc, 27);
39
+ var RecallParamsSchema = /* @__PURE__ */ messageDesc2(file_dbk_v1_rpc, 28);
40
+ var RecallResultSchema = /* @__PURE__ */ messageDesc2(file_dbk_v1_rpc, 29);
41
+ var IncomingMessageSchema = /* @__PURE__ */ messageDesc2(file_dbk_v1_rpc, 30);
42
+
43
+ // src/dbk/bots.ts
44
+ import { create } from "@bufbuild/protobuf";
45
+ var KOISHI_STATUS_ONLINE = 1;
46
+ var KOISHI_STATUS_CONNECT = 2;
47
+ var KOISHI_STATUS_RECONNECT = 4;
48
+ var SATORI_MESSAGE_DELETE = "message.delete";
49
+ var SATORI_GUILD_LIST = "guild.list";
50
+ var SATORI_CHANNEL_LIST = "channel.list";
51
+ var SATORI_GUILD_GET = "guild.get";
52
+ var SATORI_CHANNEL_GET = "channel.get";
53
+ var SATORI_FRIEND_LIST = "friend.list";
54
+ var SATORI_USER_GET = "user.get";
55
+ var SATORI_USER_CHANNEL_CREATE = "user.channel.create";
56
+ var BotFeature = {
57
+ MESSAGE_RECALL: "message.recall",
58
+ MESSAGE_FORWARD: "message.forward",
59
+ MENTION_ALL: "mention.all",
60
+ TARGETS_LIST: "targets.list",
61
+ TARGET_USER: "target.user",
62
+ TARGET_GROUP: "target.group",
63
+ TARGET_CHANNEL: "target.channel",
64
+ TARGET_THREAD: "target.thread"
65
+ };
66
+ var TARGET_KIND_FEATURES = [
67
+ [1 /* USER */, BotFeature.TARGET_USER],
68
+ [2 /* GROUP */, BotFeature.TARGET_GROUP],
69
+ [3 /* CHANNEL */, BotFeature.TARGET_CHANNEL],
70
+ [4 /* THREAD */, BotFeature.TARGET_THREAD]
71
+ ];
72
+ var THREAD_METHODS = [
73
+ "getThreadList",
74
+ "listActiveThreads",
75
+ "listPublicArchivedThreads",
76
+ "listPrivateArchivedThreads",
77
+ "startThread",
78
+ "startThreadFromMessage",
79
+ "startThreadWithoutMessage",
80
+ "createThread"
81
+ ];
82
+ var MERGED_FORWARD_METHODS = [
83
+ "send_group_forward_msg",
84
+ "send_private_forward_msg",
85
+ "send_forward_msg",
86
+ "upload_forward_msg",
87
+ "sendGroupForwardMsg",
88
+ "sendPrivateForwardMsg",
89
+ "sendForwardMsg",
90
+ "uploadForwardMsg",
91
+ "sendGroupForwardMessage",
92
+ "sendPrivateForwardMessage",
93
+ "sendForwardMessage",
94
+ "sendMergeForward",
95
+ "send_merge_forward"
96
+ ];
97
+ function listBots(ctx) {
98
+ return ctx.bots.filter((bot) => !bot.hidden).map(toDbkBot);
99
+ }
100
+ function toDbkBot(bot) {
101
+ const platform = bot.platform ?? "";
102
+ const selfId = bot.selfId ?? "";
103
+ return create(BotSchema, {
104
+ botKey: platform && selfId ? `${platform}:${selfId}` : "",
105
+ platform,
106
+ selfId,
107
+ name: bot.user?.name || bot.user?.nick || "",
108
+ avatar: bot.user?.avatar || "",
109
+ status: toDbkBotStatus(bot.status),
110
+ features: botFeatures(bot)
111
+ });
112
+ }
113
+ function toDbkBotStatus(status) {
114
+ switch (status) {
115
+ case KOISHI_STATUS_ONLINE:
116
+ return 1 /* READY */;
117
+ case KOISHI_STATUS_CONNECT:
118
+ case KOISHI_STATUS_RECONNECT:
119
+ return 3 /* CONNECTING */;
120
+ default:
121
+ return 2 /* UNAVAILABLE */;
122
+ }
123
+ }
124
+ function botFeatures(bot) {
125
+ const features = [];
126
+ if (canRecall(bot)) features.push(BotFeature.MESSAGE_RECALL);
127
+ if (canMergedForward(bot)) features.push(BotFeature.MESSAGE_FORWARD);
128
+ if (canMentionAll(bot)) features.push(BotFeature.MENTION_ALL);
129
+ if (canListTargets(bot)) features.push(BotFeature.TARGETS_LIST);
130
+ for (const kind of botTargetKinds(bot)) {
131
+ const feature = targetKindFeature(kind);
132
+ if (feature) features.push(feature);
133
+ }
134
+ return features;
135
+ }
136
+ function botTargetKinds(bot) {
137
+ const kinds = [];
138
+ const nested = hasNestedChannels(bot);
139
+ const guilds = hasGuildSurface(bot);
140
+ if (canDirectMessage(bot)) kinds.push(1 /* USER */);
141
+ if (nested) {
142
+ kinds.push(3 /* CHANNEL */);
143
+ kinds.push(4 /* THREAD */);
144
+ } else if (guilds) {
145
+ kinds.push(2 /* GROUP */);
146
+ kinds.push(3 /* CHANNEL */);
147
+ }
148
+ if (hasThreadSurface(bot) && !kinds.includes(4 /* THREAD */)) {
149
+ kinds.push(4 /* THREAD */);
150
+ }
151
+ return kinds;
152
+ }
153
+ function targetKindFeature(kind) {
154
+ return TARGET_KIND_FEATURES.find(([value]) => value === kind)?.[1];
155
+ }
156
+ function hasNestedChannels(bot) {
157
+ return hasBotMethod(bot, "getChannelList") || satoriFeatures(bot).includes(SATORI_CHANNEL_LIST);
158
+ }
159
+ function hasGuildSurface(bot) {
160
+ return hasBotMethod(bot, "getGuildList") || hasBotMethod(bot, "getGuild") || satoriFeatures(bot).includes(SATORI_GUILD_LIST) || satoriFeatures(bot).includes(SATORI_GUILD_GET);
161
+ }
162
+ function canListDirectUsers(bot) {
163
+ return hasBotMethod(bot, "getFriendList") || hasBotMethod(bot, "getUserList") || satoriFeatures(bot).includes(SATORI_FRIEND_LIST);
164
+ }
165
+ function canDirectMessage(bot) {
166
+ return canListDirectUsers(bot) || hasBotMethod(bot, "getUser") || hasBotMethod(bot, "createDirectChannel") || satoriFeatures(bot).includes(SATORI_USER_GET) || satoriFeatures(bot).includes(SATORI_USER_CHANNEL_CREATE) || looksDirectMessageOnly(bot);
167
+ }
168
+ function hasThreadSurface(bot) {
169
+ if (THREAD_METHODS.some((name2) => hasBotMethod(bot, name2) || hasInternalMethod(bot, name2))) {
170
+ return true;
171
+ }
172
+ return satoriFeatures(bot).some((feature) => feature.includes("thread"));
173
+ }
174
+ function canRecall(bot) {
175
+ return hasBotMethod(bot, "deleteMessage") || satoriFeatures(bot).includes(SATORI_MESSAGE_DELETE);
176
+ }
177
+ function canListTargets(bot) {
178
+ return hasBotMethod(bot, "getGuildList") || hasBotMethod(bot, "getChannelList") || hasBotMethod(bot, "getGuild") || hasBotMethod(bot, "getChannel") || satoriFeatures(bot).some((feature) => feature === SATORI_GUILD_LIST || feature === SATORI_CHANNEL_LIST || feature === SATORI_GUILD_GET || feature === SATORI_CHANNEL_GET);
179
+ }
180
+ function canMentionAll(bot) {
181
+ if (encoderHandlesMentionAll(bot)) return true;
182
+ if (hasGuildOrChannelSurface(bot)) return true;
183
+ return !looksDirectMessageOnly(bot);
184
+ }
185
+ function canMergedForward(bot) {
186
+ return MERGED_FORWARD_METHODS.some((name2) => hasBotMethod(bot, name2) || hasInternalMethod(bot, name2));
187
+ }
188
+ function hasGuildOrChannelSurface(bot) {
189
+ return canListTargets(bot) || hasBotMethod(bot, "getGuildMember") || hasBotMethod(bot, "getGuildMemberList");
190
+ }
191
+ function looksDirectMessageOnly(bot) {
192
+ if (hasGuildOrChannelSurface(bot)) return false;
193
+ const Encoder = messageEncoder(bot);
194
+ if (Encoder && !encoderHandlesMentionAll(bot)) {
195
+ return true;
196
+ }
197
+ return false;
198
+ }
199
+ function encoderHandlesMentionAll(bot) {
200
+ const visit = messageEncoder(bot)?.prototype?.visit;
201
+ if (typeof visit !== "function") return false;
202
+ try {
203
+ const src = Function.prototype.toString.call(visit);
204
+ return /type\s*===\s*['"]all['"]/.test(src) || /['"]all['"]\s*===\s*(?:attrs\.)?type/.test(src) || /id\s*===\s*['"]all['"]/.test(src) || /['"]all['"]\s*===\s*(?:attrs\.)?id/.test(src) || /@everyone/.test(src) || /atall/i.test(src);
205
+ } catch {
206
+ return false;
207
+ }
208
+ }
209
+ function messageEncoder(bot) {
210
+ return bot.constructor.MessageEncoder;
211
+ }
212
+ function satoriFeatures(bot) {
213
+ const features = bot.features;
214
+ if (!Array.isArray(features)) return [];
215
+ return features.filter((item) => typeof item === "string");
216
+ }
217
+ function hasInternalMethod(bot, name2) {
218
+ return hasBotMethod(bot.internal, name2);
219
+ }
220
+ function hasBotMethod(target, name2) {
221
+ if (!target) return false;
222
+ const value = target[name2];
223
+ return typeof value === "function" && !looksUnimplemented(value);
224
+ }
225
+ function looksUnimplemented(fn) {
226
+ try {
227
+ const src = Function.prototype.toString.call(fn);
228
+ return /not implemented|NotImplementedError/i.test(src);
229
+ } catch {
230
+ return false;
231
+ }
232
+ }
233
+
234
+ // src/gen/dbk/v1/frame_pb.ts
235
+ import { enumDesc as enumDesc2, fileDesc as fileDesc3, messageDesc as messageDesc3 } from "@bufbuild/protobuf/codegenv2";
236
+ var file_dbk_v1_frame = /* @__PURE__ */ fileDesc3("ChJkYmsvdjEvZnJhbWUucHJvdG8SBmRiay52MSJ/CgVGcmFtZRIbCgJvcBgBIAEoDjIPLmRiay52MS5GcmFtZU9wEgoKAmlkGAIgASgJEgsKA3NlcRgDIAEoBBIOCgZtZXRob2QYBCABKAkSDwoHcGF5bG9hZBgFIAEoDBIfCgVlcnJvchgGIAEoCzIQLmRiay52MS5ScGNFcnJvciqVAQoHRnJhbWVPcBIYChRGUkFNRV9PUF9VTlNQRUNJRklFRBAAEhEKDUZSQU1FX09QX0NBTEwQARIPCgtGUkFNRV9PUF9PSxACEhIKDkZSQU1FX09QX0VSUk9SEAMSEgoORlJBTUVfT1BfRVZFTlQQBBIRCg1GUkFNRV9PUF9QSU5HEAUSEQoNRlJBTUVfT1BfUE9ORxAGYgZwcm90bzM", [file_dbk_v1_common]);
237
+ var FrameSchema = /* @__PURE__ */ messageDesc3(file_dbk_v1_frame, 0);
238
+ var FrameOp = /* @__PURE__ */ ((FrameOp2) => {
239
+ FrameOp2[FrameOp2["UNSPECIFIED"] = 0] = "UNSPECIFIED";
240
+ FrameOp2[FrameOp2["CALL"] = 1] = "CALL";
241
+ FrameOp2[FrameOp2["OK"] = 2] = "OK";
242
+ FrameOp2[FrameOp2["ERROR"] = 3] = "ERROR";
243
+ FrameOp2[FrameOp2["EVENT"] = 4] = "EVENT";
244
+ FrameOp2[FrameOp2["PING"] = 5] = "PING";
245
+ FrameOp2[FrameOp2["PONG"] = 6] = "PONG";
246
+ return FrameOp2;
247
+ })(FrameOp || {});
248
+
249
+ // src/dbk/version.ts
250
+ import { execFileSync } from "node:child_process";
251
+ import { existsSync, readFileSync } from "node:fs";
252
+ import { dirname, join } from "node:path";
253
+ import { fileURLToPath } from "node:url";
254
+ var FALLBACK = "0.0.0-dev";
255
+ var PACKAGE_NAME = "koishi-plugin-dynamic-bot";
256
+ function hereDir() {
257
+ try {
258
+ return dirname(fileURLToPath(import.meta.url));
259
+ } catch {
260
+ return process.cwd();
261
+ }
262
+ }
263
+ function readName(dir) {
264
+ try {
265
+ const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
266
+ return pkg.name;
267
+ } catch {
268
+ return void 0;
269
+ }
270
+ }
271
+ function findPackageDir(start) {
272
+ let dir = start;
273
+ for (let i = 0; i < 8; i++) {
274
+ if (existsSync(join(dir, "package.json")) && readName(dir) === PACKAGE_NAME) {
275
+ return dir;
276
+ }
277
+ const parent = join(dir, "..");
278
+ if (parent === dir) break;
279
+ dir = parent;
280
+ }
281
+ return void 0;
282
+ }
283
+ function packageDir() {
284
+ const found = findPackageDir(hereDir());
285
+ if (found) return found;
286
+ const fromCwd = join(process.cwd(), "koishi");
287
+ if (existsSync(join(fromCwd, "package.json"))) return fromCwd;
288
+ return process.cwd();
289
+ }
290
+ function isProductRepo(root) {
291
+ return existsSync(join(root, "pnpm-workspace.yaml")) && existsSync(join(root, "jvm", "build.gradle.kts")) && existsSync(join(root, "koishi", "package.json"));
292
+ }
293
+ function gitDescribe(cwd) {
294
+ try {
295
+ const described = execFileSync(
296
+ "git",
297
+ ["describe", "--tags", "--always", "--abbrev=7", "--dirty"],
298
+ { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }
299
+ ).trim();
300
+ return described || void 0;
301
+ } catch {
302
+ return void 0;
303
+ }
304
+ }
305
+ function packageVersion(dir) {
306
+ try {
307
+ const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
308
+ return pkg.version?.trim() || FALLBACK;
309
+ } catch {
310
+ return FALLBACK;
311
+ }
312
+ }
313
+ function resolveGatewayVersion() {
314
+ const pkg = packageDir();
315
+ const repoRoot = join(pkg, "..");
316
+ if (isProductRepo(repoRoot)) {
317
+ return gitDescribe(repoRoot) ?? packageVersion(pkg);
318
+ }
319
+ return packageVersion(pkg);
320
+ }
321
+ var GATEWAY_VERSION = resolveGatewayVersion();
322
+
323
+ // src/dbk/protocol.ts
324
+ var PROTOCOL_VERSION = "1";
325
+ var PING_INTERVAL_MS = 1e4;
326
+ var PONG_TIMEOUT_MS = 2e4;
327
+ var DbkMethod = {
328
+ SESSION_HELLO: "session.hello",
329
+ BOTS_LIST: "bots.list",
330
+ TARGETS_LIST: "targets.list",
331
+ TARGETS_GET: "targets.get",
332
+ MESSAGE_SEND: "message.send",
333
+ MESSAGE_RECALL: "message.recall"
334
+ };
335
+ var DbkEvent = {
336
+ BOT_CHANGED: "bot.changed",
337
+ MESSAGE_CREATED: "message.created"
338
+ };
339
+ var DbkRpcCodecs = {
340
+ [DbkMethod.SESSION_HELLO]: { request: HelloRequestSchema, response: HelloResponseSchema },
341
+ [DbkMethod.BOTS_LIST]: { request: ListBotsRequestSchema, response: ListBotsResponseSchema },
342
+ [DbkMethod.TARGETS_LIST]: { request: ListTargetsRequestSchema, response: ListTargetsResponseSchema },
343
+ [DbkMethod.TARGETS_GET]: { request: GetTargetRequestSchema, response: GetTargetResponseSchema },
344
+ [DbkMethod.MESSAGE_SEND]: { request: SendParamsSchema, response: SendResultSchema },
345
+ [DbkMethod.MESSAGE_RECALL]: { request: RecallParamsSchema, response: RecallResultSchema }
346
+ };
347
+ var DbkEventCodecs = {
348
+ [DbkEvent.BOT_CHANGED]: BotChangedEventSchema,
349
+ [DbkEvent.MESSAGE_CREATED]: IncomingMessageSchema
350
+ };
351
+
352
+ // src/dbk/session.ts
353
+ import { create as create3 } from "@bufbuild/protobuf";
354
+
355
+ // src/dbk/codec.ts
356
+ import { create as create2, fromBinary, fromJsonString, toBinary, toJsonString } from "@bufbuild/protobuf";
357
+
358
+ // src/dbk/error.ts
359
+ var DbkRpcError = class extends Error {
360
+ code;
361
+ constructor(code, detail) {
362
+ super(detail);
363
+ this.name = "DbkRpcError";
364
+ this.code = code;
365
+ }
366
+ };
367
+
368
+ // src/dbk/codec.ts
369
+ function encodeFrame(frame, encoding) {
370
+ if (encoding === "json") {
371
+ return toJsonString(FrameSchema, frame);
372
+ }
373
+ return toBinary(FrameSchema, frame);
374
+ }
375
+ function decodeFrame(data, encoding) {
376
+ if (encoding === "json") {
377
+ const text = typeof data === "string" ? data : new TextDecoder().decode(data);
378
+ return fromJsonString(FrameSchema, text);
379
+ }
380
+ if (typeof data === "string") {
381
+ throw new DbkRpcError(2 /* PROTOCOL */, "DBK only accepts protobuf binary frames");
382
+ }
383
+ return fromBinary(FrameSchema, data);
384
+ }
385
+ function encodePayload(schema, message) {
386
+ return toBinary(schema, message);
387
+ }
388
+ function decodePayload(schema, bytes) {
389
+ return fromBinary(schema, bytes);
390
+ }
391
+ function createFrame(init) {
392
+ return create2(FrameSchema, init);
393
+ }
394
+ function toBytes(data) {
395
+ if (data instanceof Uint8Array) return data;
396
+ if (data instanceof ArrayBuffer) return new Uint8Array(data);
397
+ if (ArrayBuffer.isView(data)) {
398
+ return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
399
+ }
400
+ if (Array.isArray(data)) {
401
+ const chunks = data.map((part) => toBytes(part));
402
+ const total = chunks.reduce((sum, chunk) => sum + chunk.byteLength, 0);
403
+ const out = new Uint8Array(total);
404
+ let offset = 0;
405
+ for (const chunk of chunks) {
406
+ out.set(chunk, offset);
407
+ offset += chunk.byteLength;
408
+ }
409
+ return out;
410
+ }
411
+ throw new DbkRpcError(2 /* PROTOCOL */, "DBK frame is not binary");
412
+ }
413
+
414
+ // src/dbk/session.ts
415
+ var DbkGatewaySession = class {
416
+ constructor(options) {
417
+ this.options = options;
418
+ this.now = options.now ?? Date.now;
419
+ this.lastInboundAt = this.now();
420
+ }
421
+ closed = false;
422
+ handshook = false;
423
+ seq = 0n;
424
+ sendQueue = Promise.resolve();
425
+ lastInboundAt;
426
+ heartbeat;
427
+ now;
428
+ get isHandshook() {
429
+ return this.handshook && !this.closed;
430
+ }
431
+ onFrame(frame) {
432
+ if (this.closed) return;
433
+ this.lastInboundAt = this.now();
434
+ void this.handle(frame);
435
+ }
436
+ rejectText() {
437
+ this.fail("DBK only accepts protobuf binary frames");
438
+ }
439
+ rejectDecode(error) {
440
+ this.options.logger.warn("DBK frame decode failed: %s", error);
441
+ this.fail("DBK frame could not be decoded");
442
+ }
443
+ emit(method, event) {
444
+ if (!this.isHandshook) return;
445
+ this.seq += 1n;
446
+ this.enqueueFrame(
447
+ createFrame({
448
+ op: 4 /* EVENT */,
449
+ seq: this.seq,
450
+ method,
451
+ payload: encodePayload(DbkEventCodecs[method], event)
452
+ })
453
+ );
454
+ }
455
+ close(reason = "closed") {
456
+ if (this.closed) return;
457
+ this.closed = true;
458
+ this.handshook = false;
459
+ if (this.heartbeat) {
460
+ clearInterval(this.heartbeat);
461
+ this.heartbeat = void 0;
462
+ }
463
+ this.options.logger.debug("DBK session closed: %s", reason);
464
+ }
465
+ async handle(frame) {
466
+ switch (frame.op) {
467
+ case 5 /* PING */:
468
+ if (!this.handshook) return;
469
+ this.enqueueFrame(createFrame({ op: 6 /* PONG */, id: frame.id }));
470
+ return;
471
+ case 6 /* PONG */:
472
+ return;
473
+ case 1 /* CALL */:
474
+ await this.handleCall(frame);
475
+ return;
476
+ case 4 /* EVENT */:
477
+ case 2 /* OK */:
478
+ case 3 /* ERROR */:
479
+ this.options.logger.debug("gateway ignored peer %s: method=%s", FrameOp[frame.op], frame.method);
480
+ return;
481
+ default:
482
+ this.options.logger.warn("ignored DBK frame with unspecified op");
483
+ }
484
+ }
485
+ async handleCall(frame) {
486
+ if (!frame.id) {
487
+ this.replyError("", 2 /* PROTOCOL */, "CALL is missing id");
488
+ return;
489
+ }
490
+ if (!this.handshook && frame.method !== DbkMethod.SESSION_HELLO) {
491
+ this.options.logger.debug("drop CALL before handshake: method=%s", frame.method);
492
+ this.replyError(frame.id, 2 /* PROTOCOL */, "DBK handshake is not complete");
493
+ return;
494
+ }
495
+ if (!(frame.method in DbkRpcCodecs)) {
496
+ this.replyError(frame.id, 4 /* UNSUPPORTED */, `unknown method: ${frame.method}`);
497
+ return;
498
+ }
499
+ try {
500
+ const payload = await this.dispatchCall(frame.method, frame.payload);
501
+ this.enqueueFrame(
502
+ createFrame({
503
+ op: 2 /* OK */,
504
+ id: frame.id,
505
+ method: frame.method,
506
+ payload
507
+ })
508
+ );
509
+ } catch (error) {
510
+ const rpcError = error instanceof DbkRpcError ? error : new DbkRpcError(5 /* INTERNAL */, error instanceof Error ? error.message : String(error));
511
+ this.options.logger.warn("DBK RPC failed: method=%s code=%s %s", frame.method, ErrorCode[rpcError.code], rpcError.message);
512
+ this.replyError(frame.id, rpcError.code, rpcError.message);
513
+ if (frame.method === DbkMethod.SESSION_HELLO && rpcError.code === 1 /* UNAUTHORIZED */) {
514
+ this.options.onUnauthorized?.();
515
+ await this.sendQueue;
516
+ this.fail(rpcError.message);
517
+ }
518
+ }
519
+ }
520
+ dispatchCall(method, payload) {
521
+ switch (method) {
522
+ case DbkMethod.SESSION_HELLO:
523
+ return this.runRpc(method, payload, (request) => this.hello(request));
524
+ case DbkMethod.BOTS_LIST:
525
+ case DbkMethod.TARGETS_LIST:
526
+ case DbkMethod.TARGETS_GET:
527
+ case DbkMethod.MESSAGE_SEND:
528
+ case DbkMethod.MESSAGE_RECALL:
529
+ return this.runRpc(method, payload, this.options.handlers[method]);
530
+ default:
531
+ return Promise.reject(new DbkRpcError(4 /* UNSUPPORTED */, `unknown method: ${method}`));
532
+ }
533
+ }
534
+ async runRpc(method, payload, handler) {
535
+ const codec = DbkRpcCodecs[method];
536
+ const request = decodePayload(codec.request, payload);
537
+ const response = await handler(request);
538
+ return encodePayload(codec.response, response);
539
+ }
540
+ async hello(request) {
541
+ if (request.token !== this.options.accessToken) {
542
+ throw new DbkRpcError(1 /* UNAUTHORIZED */, "invalid access token");
543
+ }
544
+ const remote = request.protocolVersion.trim();
545
+ if (remote && remote !== PROTOCOL_VERSION) {
546
+ throw new DbkRpcError(
547
+ 2 /* PROTOCOL */,
548
+ `protocol version mismatch: local=${PROTOCOL_VERSION} remote=${remote}`
549
+ );
550
+ }
551
+ const response = await this.options.handlers[DbkMethod.SESSION_HELLO](request);
552
+ this.handshook = true;
553
+ this.startHeartbeat();
554
+ this.options.logger.info(
555
+ "DBK handshake complete: bots=%d app=%s",
556
+ response.bots.length,
557
+ request.appVersion || "-"
558
+ );
559
+ return response;
560
+ }
561
+ replyError(id, code, detail) {
562
+ this.enqueueFrame(
563
+ createFrame({
564
+ op: 3 /* ERROR */,
565
+ id,
566
+ error: create3(RpcErrorSchema, { code, detail })
567
+ })
568
+ );
569
+ }
570
+ startHeartbeat() {
571
+ if (this.heartbeat) clearInterval(this.heartbeat);
572
+ this.lastInboundAt = this.now();
573
+ this.heartbeat = setInterval(() => {
574
+ if (this.closed) return;
575
+ if (this.now() - this.lastInboundAt >= PONG_TIMEOUT_MS) {
576
+ this.fail("DBK heartbeat timed out");
577
+ return;
578
+ }
579
+ this.enqueueFrame(createFrame({ op: 5 /* PING */ }));
580
+ }, PING_INTERVAL_MS);
581
+ }
582
+ enqueueFrame(frame) {
583
+ this.sendQueue = this.sendQueue.then(() => {
584
+ if (this.closed) return;
585
+ this.options.send(encodeFrame(frame, this.options.encoding));
586
+ }).catch((error) => {
587
+ this.options.logger.warn("DBK send failed: %s", error);
588
+ this.fail("DBK send failed");
589
+ });
590
+ }
591
+ fail(reason) {
592
+ if (this.closed) return;
593
+ this.options.onDead(reason);
594
+ this.close(reason);
595
+ }
596
+ };
597
+
598
+ // src/dbk/socket.ts
599
+ var WS_OPEN = 1;
600
+ function sendSocket(socket, data) {
601
+ if (socket.readyState !== WS_OPEN) {
602
+ throw new Error("WebSocket is not open");
603
+ }
604
+ socket.send(data);
605
+ }
606
+ function attachSocket(socket, session, encoding) {
607
+ if (socket.binaryType !== void 0) {
608
+ socket.binaryType = "arraybuffer";
609
+ }
610
+ const onMessage = (data, isBinary) => {
611
+ const binary = isBinary ?? typeof data !== "string";
612
+ if (encoding === "binary" && !binary) {
613
+ session.rejectText();
614
+ return;
615
+ }
616
+ try {
617
+ const raw = typeof data === "string" ? data : toBytes(data);
618
+ session.onFrame(decodeFrame(raw, encoding));
619
+ } catch (error) {
620
+ session.rejectDecode(error);
621
+ }
622
+ };
623
+ const onClose = () => session.close("ws close");
624
+ if (typeof socket.on === "function") {
625
+ socket.on("message", (data, isBinary) => onMessage(data, isBinary));
626
+ socket.on("close", onClose);
627
+ return;
628
+ }
629
+ socket.addEventListener?.("message", (event) => onMessage(event.data));
630
+ socket.addEventListener?.("close", onClose);
631
+ }
632
+
633
+ // src/dbk/gateway.ts
634
+ var RECONNECT_BACKOFF_MS = [
635
+ 5e3,
636
+ 1e4,
637
+ 3e4,
638
+ 6e4,
639
+ 5 * 6e4,
640
+ 10 * 6e4,
641
+ 30 * 6e4,
642
+ 60 * 6e4
643
+ ];
644
+ var DbkGateway = class {
645
+ constructor(ctx, config, handlers) {
646
+ this.ctx = ctx;
647
+ this.config = config;
648
+ this.handlers = handlers;
649
+ }
650
+ session;
651
+ socket;
652
+ closing = false;
653
+ reconnectAttempts = 0;
654
+ reconnectTimer;
655
+ reconnectSuspended = false;
656
+ get isHandshook() {
657
+ return this.session?.isHandshook ?? false;
658
+ }
659
+ emit(method, event) {
660
+ this.session?.emit(method, event);
661
+ }
662
+ startForward() {
663
+ const path = normalizePath(this.config.path);
664
+ this.ctx.logger.info("DBK forward WebSocket mounted on Koishi HTTP at %s", path);
665
+ this.ctx.server.ws(path, (socket) => {
666
+ this.attach("replaced", socket);
667
+ });
668
+ }
669
+ startReverse() {
670
+ this.connectReverse();
671
+ }
672
+ stop() {
673
+ this.closing = true;
674
+ this.reconnectTimer?.();
675
+ this.reconnectTimer = void 0;
676
+ this.session?.close("plugin stop");
677
+ this.session = void 0;
678
+ this.socket?.close(1e3, "plugin stop");
679
+ this.socket = void 0;
680
+ }
681
+ connectReverse() {
682
+ if (this.closing) return;
683
+ const url = `ws://${this.config.host}:${this.config.port}`;
684
+ this.ctx.logger.info("DBK reverse WebSocket connecting to %s", url);
685
+ const socket = this.ctx.http.ws(url);
686
+ const onOpen = () => {
687
+ this.reconnectAttempts = 0;
688
+ this.attach("ws close", socket);
689
+ };
690
+ if (typeof socket.on === "function") {
691
+ socket.on("open", onOpen);
692
+ socket.on("error", (error) => this.ctx.logger.warn("DBK reverse WebSocket error: %s", error));
693
+ socket.on("close", () => this.onReverseClosed());
694
+ } else {
695
+ socket.addEventListener?.("open", onOpen);
696
+ socket.addEventListener?.("close", () => this.onReverseClosed());
697
+ }
698
+ }
699
+ attach(replaceReason, socket) {
700
+ if (this.socket && this.socket !== socket) {
701
+ this.ctx.logger.warn("replacing existing DBK connection");
702
+ this.session?.close(replaceReason);
703
+ this.socket.close(1e3, replaceReason);
704
+ }
705
+ this.socket = socket;
706
+ const session = new DbkGatewaySession({
707
+ encoding: this.config.encoding,
708
+ accessToken: this.config.accessToken,
709
+ handlers: this.handlers,
710
+ logger: this.ctx.logger,
711
+ send: (data) => sendSocket(socket, data),
712
+ onDead: (reason) => {
713
+ socket.close(1e3, reason.slice(0, 123));
714
+ },
715
+ onUnauthorized: () => {
716
+ this.reconnectSuspended = true;
717
+ this.ctx.logger.warn("DBK unauthorized, automatic reconnect paused");
718
+ }
719
+ });
720
+ this.session = session;
721
+ attachSocket(socket, session, this.config.encoding);
722
+ }
723
+ onReverseClosed() {
724
+ this.session?.close("ws close");
725
+ if (this.socket) this.socket = void 0;
726
+ this.session = void 0;
727
+ this.scheduleReconnect();
728
+ }
729
+ scheduleReconnect() {
730
+ if (this.closing || !this.config.reconnect || this.reconnectSuspended) return;
731
+ this.reconnectTimer?.();
732
+ const attempt = this.reconnectAttempts + 1;
733
+ this.reconnectAttempts = attempt;
734
+ const delay = RECONNECT_BACKOFF_MS[Math.min(attempt, RECONNECT_BACKOFF_MS.length) - 1];
735
+ this.ctx.logger.warn("DBK reverse disconnected, reconnecting in %dms (attempt=%d)", delay, attempt);
736
+ this.reconnectTimer = this.ctx.setTimeout(() => {
737
+ this.reconnectTimer = void 0;
738
+ this.connectReverse();
739
+ }, delay);
740
+ }
741
+ };
742
+ function watchBots(ctx, gateway) {
743
+ const emit = (type, bot) => {
744
+ if (bot.hidden && type !== 3 /* REMOVED */) return;
745
+ gateway.emit(DbkEvent.BOT_CHANGED, create4(BotChangedEventSchema, {
746
+ type,
747
+ bot: toDbkBot(bot)
748
+ }));
749
+ };
750
+ ctx.on("bot-added", (bot) => emit(1 /* ADDED */, bot));
751
+ ctx.on("bot-removed", (bot) => emit(3 /* REMOVED */, bot));
752
+ ctx.on("bot-status-updated", (bot) => emit(2 /* UPDATED */, bot));
753
+ }
754
+ function normalizePath(path) {
755
+ const trimmed = path.trim() || "/dbk";
756
+ return trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
757
+ }
758
+
759
+ // src/dbk/handlers.ts
760
+ import { create as create9 } from "@bufbuild/protobuf";
761
+
762
+ // src/dbk/get-target.ts
763
+ import { create as create5 } from "@bufbuild/protobuf";
764
+
765
+ // src/dbk/avatar.ts
766
+ var AVATAR_KEYS = [
767
+ "avatar",
768
+ "avatarUrl",
769
+ "avatar_url",
770
+ "icon",
771
+ "iconUrl",
772
+ "icon_url",
773
+ "photoUrl",
774
+ "photo_url"
775
+ ];
776
+ function pickAvatar(...sources) {
777
+ for (const source of sources) {
778
+ const value = avatarFrom(source);
779
+ if (value) return value;
780
+ }
781
+ return "";
782
+ }
783
+ function avatarFrom(source) {
784
+ if (typeof source === "string") return sanitize(source);
785
+ if (!source || typeof source !== "object") return "";
786
+ const rec = source;
787
+ const nested = rec.user && typeof rec.user === "object" ? rec.user : void 0;
788
+ for (const obj of [nested, rec]) {
789
+ if (!obj) continue;
790
+ for (const key of AVATAR_KEYS) {
791
+ const value = sanitize(obj[key]);
792
+ if (value) return value;
793
+ }
794
+ }
795
+ return photoUri(rec.photo);
796
+ }
797
+ function photoUri(photo) {
798
+ if (typeof photo === "string") return sanitize(photo);
799
+ if (!photo || typeof photo !== "object") return "";
800
+ const rec = photo;
801
+ for (const key of ["url", "big", "small", "src"]) {
802
+ const value = sanitize(rec[key]);
803
+ if (value) return value;
804
+ }
805
+ return "";
806
+ }
807
+ function sanitize(value) {
808
+ if (typeof value !== "string") return "";
809
+ const trimmed = value.trim();
810
+ if (!trimmed) return "";
811
+ if (!/^(https?:|data:|file:|\/\/)/i.test(trimmed) && !trimmed.includes("/")) {
812
+ return "";
813
+ }
814
+ return trimmed;
815
+ }
816
+
817
+ // src/dbk/get-target.ts
818
+ var CHANNEL_TEXT = 0;
819
+ var CHANNEL_DIRECT = 1;
820
+ var CHANNEL_CATEGORY = 2;
821
+ var CHANNEL_VOICE = 3;
822
+ var DISCORD_THREAD_TYPES = /* @__PURE__ */ new Set([10, 11, 12]);
823
+ async function getTarget(ctx, request) {
824
+ const botKey = request.botKey.trim();
825
+ const target = request.target;
826
+ if (botKey) {
827
+ const bot = findBot(ctx, botKey);
828
+ if (!bot) {
829
+ throw new DbkRpcError(3 /* NOT_FOUND */, `bot not found: ${botKey}`);
830
+ }
831
+ const info = await resolveOnBot(bot, target);
832
+ return info ? resolved(info) : unresolved(target);
833
+ }
834
+ for (const bot of listSearchableBots(ctx)) {
835
+ const info = await resolveOnBot(bot, target);
836
+ if (info) return resolved(info);
837
+ }
838
+ return unresolved(target);
839
+ }
840
+ function findBot(ctx, botKey) {
841
+ return ctx.bots.find((bot) => !bot.hidden && botKeyOf(bot) === botKey);
842
+ }
843
+ function listSearchableBots(ctx) {
844
+ return ctx.bots.filter((bot) => !bot.hidden && (bot.platform ?? "").toLowerCase() !== "qq");
845
+ }
846
+ function botKeyOf(bot) {
847
+ const platform = bot.platform ?? "";
848
+ const selfId = bot.selfId ?? "";
849
+ return platform && selfId ? `${platform}:${selfId}` : "";
850
+ }
851
+ async function resolveOnBot(bot, target) {
852
+ const id = target?.id.trim() ?? "";
853
+ if (!id) return void 0;
854
+ const guildId = target?.guildId.trim() ?? "";
855
+ const kind = target?.kind ?? 0 /* UNSPECIFIED */;
856
+ try {
857
+ switch (kind) {
858
+ case 1 /* USER */:
859
+ return await resolveUser(bot, id, guildId);
860
+ case 2 /* GROUP */:
861
+ return await resolveGroup(bot, id, guildId);
862
+ case 3 /* CHANNEL */:
863
+ return await resolveChannelKind(bot, id, guildId, 3 /* CHANNEL */);
864
+ case 4 /* THREAD */:
865
+ return await resolveChannelKind(bot, id, guildId, 4 /* THREAD */);
866
+ default:
867
+ return await resolveChannelKind(bot, id, guildId, 0 /* UNSPECIFIED */) ?? await resolveGroup(bot, id, guildId) ?? await resolveUser(bot, id, guildId);
868
+ }
869
+ } catch {
870
+ return void 0;
871
+ }
872
+ }
873
+ async function resolveUser(bot, id, guildId) {
874
+ const user = await callBot(bot, "getUser", () => bot.getUser(id, guildId || void 0));
875
+ if (user) return toUserInfo(bot, user, guildId);
876
+ const friend = await callFriend(bot, id);
877
+ if (friend) return toUserInfo(bot, friend, guildId);
878
+ const channel = await callBot(bot, "getChannel", () => bot.getChannel(id, guildId || void 0));
879
+ if (channel && channelKind(bot, channel, 1 /* USER */) === 1 /* USER */) {
880
+ return toChannelInfo(bot, channel, 1 /* USER */, guildId);
881
+ }
882
+ return void 0;
883
+ }
884
+ async function resolveGroup(bot, id, guildId) {
885
+ if (!hasNestedChannels(bot)) {
886
+ const guild = await callBot(bot, "getGuild", () => bot.getGuild(id));
887
+ if (guild) {
888
+ const kind = kindFromGuild(bot, guild);
889
+ if (kind) return toGuildInfo(bot, guild, kind);
890
+ }
891
+ }
892
+ return resolveChannelKind(bot, id, guildId, 2 /* GROUP */);
893
+ }
894
+ async function resolveChannelKind(bot, id, guildId, requested) {
895
+ const channel = await callBot(bot, "getChannel", () => bot.getChannel(id, guildId || void 0));
896
+ if (!channel) return void 0;
897
+ const kind = channelKind(bot, channel, requested);
898
+ if (!kind) return void 0;
899
+ return toChannelInfo(bot, channel, kind, guildId);
900
+ }
901
+ function channelKind(bot, channel, requested) {
902
+ const nested = hasNestedChannels(bot);
903
+ const type = channel.type ?? CHANNEL_TEXT;
904
+ if (type === CHANNEL_DIRECT) return 1 /* USER */;
905
+ if (type === CHANNEL_CATEGORY || type === CHANNEL_VOICE) return void 0;
906
+ if (DISCORD_THREAD_TYPES.has(type) || requested === 4 /* THREAD */) {
907
+ if (requested === 1 /* USER */) return void 0;
908
+ if (requested === 2 /* GROUP */ && nested) return void 0;
909
+ return 4 /* THREAD */;
910
+ }
911
+ if (requested === 1 /* USER */) return void 0;
912
+ if (!nested) {
913
+ if (requested === 3 /* CHANNEL */) return 3 /* CHANNEL */;
914
+ return 2 /* GROUP */;
915
+ }
916
+ if (requested === 2 /* GROUP */) return void 0;
917
+ return 3 /* CHANNEL */;
918
+ }
919
+ function kindFromGuild(bot, guild) {
920
+ const chatType = guild.type;
921
+ if (chatType === "private") return 1 /* USER */;
922
+ if (chatType === "channel") return 3 /* CHANNEL */;
923
+ if (chatType === "group" || chatType === "supergroup") return 2 /* GROUP */;
924
+ if (hasNestedChannels(bot)) return void 0;
925
+ return 2 /* GROUP */;
926
+ }
927
+ async function toChannelInfo(bot, channel, kind, requestGuildId) {
928
+ const id = stringifyId(channel.id);
929
+ const guildId = requestGuildId || stringifyId(channel.guildId);
930
+ const guild = guildId ? await lookupGuild(bot, guildId) : void 0;
931
+ return create5(TargetInfoSchema, {
932
+ target: create5(TargetSchema, { kind, id, guildId }),
933
+ name: channel.name?.trim() || id,
934
+ guildId: stringifyId(guild?.id) || guildId,
935
+ guildName: guildDisplayName(guild),
936
+ avatar: pickAvatar(channel, guild),
937
+ botKeys: [botKeyOf(bot)]
938
+ });
939
+ }
940
+ function toGuildInfo(bot, guild, kind) {
941
+ const id = stringifyId(guild.id) || stringifyId(guild.guildId);
942
+ const guildId = kind === 1 /* USER */ ? "" : id;
943
+ return create5(TargetInfoSchema, {
944
+ target: create5(TargetSchema, { kind, id, guildId }),
945
+ name: guildDisplayName(guild) || id,
946
+ guildId,
947
+ guildName: kind === 1 /* USER */ ? "" : guildDisplayName(guild),
948
+ avatar: pickAvatar(guild),
949
+ botKeys: [botKeyOf(bot)]
950
+ });
951
+ }
952
+ function toUserInfo(bot, user, guildId) {
953
+ const id = stringifyId(user.id);
954
+ return create5(TargetInfoSchema, {
955
+ target: create5(TargetSchema, { kind: 1 /* USER */, id, guildId }),
956
+ name: user.nick?.trim() || user.name?.trim() || user.username?.trim() || id,
957
+ guildId,
958
+ guildName: "",
959
+ avatar: pickAvatar(user),
960
+ botKeys: [botKeyOf(bot)]
961
+ });
962
+ }
963
+ async function lookupGuild(bot, guildId) {
964
+ return callBot(bot, "getGuild", () => bot.getGuild(guildId));
965
+ }
966
+ function guildDisplayName(guild) {
967
+ if (!guild) return "";
968
+ return guild.name?.trim() || guild.guildName?.trim() || guild.title?.trim() || guild.first_name?.trim() || "";
969
+ }
970
+ function stringifyId(value) {
971
+ if (value === void 0 || value === null) return "";
972
+ return String(value).trim();
973
+ }
974
+ function resolved(target) {
975
+ return create5(GetTargetResponseSchema, { target, unresolved: false });
976
+ }
977
+ function unresolved(requestTarget) {
978
+ return create5(GetTargetResponseSchema, {
979
+ unresolved: true,
980
+ target: requestTarget ? create5(TargetInfoSchema, {
981
+ target: requestTarget
982
+ }) : void 0
983
+ });
984
+ }
985
+ async function callBot(bot, method, fn) {
986
+ if (typeof bot[method] !== "function") return void 0;
987
+ try {
988
+ return await fn();
989
+ } catch {
990
+ return void 0;
991
+ }
992
+ }
993
+ async function callFriend(bot, id) {
994
+ const getFriend = bot.getFriend;
995
+ if (typeof getFriend !== "function") return void 0;
996
+ try {
997
+ return await getFriend.call(bot, id);
998
+ } catch {
999
+ return void 0;
1000
+ }
1001
+ }
1002
+
1003
+ // src/dbk/list-targets.ts
1004
+ import { create as create6 } from "@bufbuild/protobuf";
1005
+ var CHANNEL_TEXT2 = 0;
1006
+ var CHANNEL_CATEGORY2 = 2;
1007
+ var CHANNEL_DIRECT_OR_VOICE = /* @__PURE__ */ new Set([1, 3]);
1008
+ var DISCORD_THREAD_TYPES2 = /* @__PURE__ */ new Set([10, 11, 12]);
1009
+ var MAX_LIST_PAGES = 64;
1010
+ var MAX_LIST_ITEMS = 1e4;
1011
+ async function listTargets(ctx, request) {
1012
+ const bots = resolveBots(ctx, request.botKey);
1013
+ const kindFilter = request.kind ?? 0 /* UNSPECIFIED */;
1014
+ const merged = /* @__PURE__ */ new Map();
1015
+ let incomplete = false;
1016
+ for (const bot of bots) {
1017
+ if (isQq(bot)) continue;
1018
+ const listed = await listTargetsForBot(ctx, bot, kindFilter);
1019
+ incomplete = incomplete || listed.incomplete;
1020
+ for (const target of listed.targets) {
1021
+ mergeTarget(merged, target);
1022
+ }
1023
+ }
1024
+ return create6(ListTargetsResponseSchema, {
1025
+ targets: [...merged.values()].map((target) => ({
1026
+ target: { kind: target.kind, id: target.id, guildId: target.guildId },
1027
+ name: target.name,
1028
+ guildId: target.guildId,
1029
+ guildName: target.guildName,
1030
+ avatar: target.avatar,
1031
+ botKeys: target.botKeys
1032
+ })),
1033
+ incomplete
1034
+ });
1035
+ }
1036
+ function resolveBots(ctx, botKey) {
1037
+ const key = botKey?.trim() ?? "";
1038
+ const visible = ctx.bots.filter((bot) => !bot.hidden);
1039
+ if (!key) return visible;
1040
+ const found = findBot2(ctx, key);
1041
+ if (!found) {
1042
+ throw new DbkRpcError(3 /* NOT_FOUND */, `bot not found: ${key}`);
1043
+ }
1044
+ return [found];
1045
+ }
1046
+ function findBot2(ctx, botKey) {
1047
+ return ctx.bots.find((bot) => !bot.hidden && botKeyOf2(bot) === botKey);
1048
+ }
1049
+ function botKeyOf2(bot) {
1050
+ const platform = bot.platform ?? "";
1051
+ const selfId = bot.selfId ?? "";
1052
+ return platform && selfId ? `${platform}:${selfId}` : "";
1053
+ }
1054
+ function isQq(bot) {
1055
+ return (bot.platform ?? "").toLowerCase() === "qq";
1056
+ }
1057
+ function wantsKind(filter, kind) {
1058
+ return filter === 0 /* UNSPECIFIED */ || filter === kind;
1059
+ }
1060
+ async function listTargetsForBot(ctx, bot, kindFilter) {
1061
+ const botKey = botKeyOf2(bot);
1062
+ const targets = [];
1063
+ const nested = hasNestedChannels(bot);
1064
+ const supported = botTargetKinds(bot);
1065
+ if (kindFilter !== 0 /* UNSPECIFIED */ && supported.length > 0 && !supported.includes(kindFilter)) {
1066
+ return { targets, incomplete: false };
1067
+ }
1068
+ let incomplete = hasGuildSurface(bot) && !nested;
1069
+ const wantGuilds = wantsKind(kindFilter, 2 /* GROUP */) || wantsKind(kindFilter, 3 /* CHANNEL */) || wantsKind(kindFilter, 4 /* THREAD */);
1070
+ const wantUsers = wantsKind(kindFilter, 1 /* USER */);
1071
+ if (wantGuilds) {
1072
+ const guilds = await paginate((next) => callBotList(bot, "getGuildList", next));
1073
+ if (guilds.unsupported || guilds.failed || guilds.truncated) incomplete = true;
1074
+ if (guilds.failed && !guilds.unsupported) {
1075
+ ctx.logger?.debug("targets.list: %s getGuildList failed", botKey);
1076
+ }
1077
+ let channelMode = nested ? "unknown" : "flat";
1078
+ for (const guild of guilds.items) {
1079
+ const guildId = String(guild.id ?? "").trim();
1080
+ if (!guildId) continue;
1081
+ const guildName = String(guild.name ?? "").trim();
1082
+ if (channelMode === "flat") {
1083
+ pushTarget(targets, kindFilter, {
1084
+ kind: kindFromListedGuild(guild),
1085
+ id: guildId,
1086
+ guildId: "",
1087
+ name: guildName || guildId,
1088
+ guildName: "",
1089
+ avatar: pickAvatar(guild),
1090
+ botKeys: [botKey]
1091
+ });
1092
+ continue;
1093
+ }
1094
+ const channels = await paginate((next) => callBotList(bot, "getChannelList", next, guildId));
1095
+ if (channels.truncated) incomplete = true;
1096
+ if (channels.unsupported) {
1097
+ channelMode = "flat";
1098
+ pushTarget(targets, kindFilter, {
1099
+ kind: kindFromListedGuild(guild),
1100
+ id: guildId,
1101
+ guildId: "",
1102
+ name: guildName || guildId,
1103
+ guildName: "",
1104
+ avatar: pickAvatar(guild),
1105
+ botKeys: [botKey]
1106
+ });
1107
+ continue;
1108
+ }
1109
+ if (channels.failed) {
1110
+ incomplete = true;
1111
+ ctx.logger?.debug("targets.list: %s getChannelList failed guild=%s", botKey, guildId);
1112
+ continue;
1113
+ }
1114
+ channelMode = "nested";
1115
+ const classified = classifyGuildChannels(channels.items, guildId, guildName, pickAvatar(guild), botKey);
1116
+ for (const target of classified) {
1117
+ pushTarget(targets, kindFilter, target);
1118
+ }
1119
+ }
1120
+ }
1121
+ if (wantUsers) {
1122
+ if (canListDirectUsers(bot)) {
1123
+ const friends = await listDirectUsers(bot);
1124
+ if (friends.truncated) incomplete = true;
1125
+ if (friends.failed && !friends.unsupported) {
1126
+ incomplete = true;
1127
+ ctx.logger?.debug("targets.list: %s friend/user list failed", botKey);
1128
+ }
1129
+ for (const user of friends.items) {
1130
+ pushTarget(targets, kindFilter, {
1131
+ kind: 1 /* USER */,
1132
+ id: user.id,
1133
+ guildId: "",
1134
+ name: user.name,
1135
+ guildName: "",
1136
+ avatar: user.avatar,
1137
+ botKeys: [botKey]
1138
+ });
1139
+ }
1140
+ } else if (supported.includes(1 /* USER */) || supported.length === 0) {
1141
+ incomplete = true;
1142
+ }
1143
+ }
1144
+ return { targets, incomplete };
1145
+ }
1146
+ function classifyGuildChannels(channels, guildId, guildName, guildAvatar, botKey) {
1147
+ const byId = /* @__PURE__ */ new Map();
1148
+ for (const channel of channels) {
1149
+ const id = String(channel.id ?? "").trim();
1150
+ if (id) byId.set(id, channel);
1151
+ }
1152
+ const out = [];
1153
+ for (const channel of channels) {
1154
+ const id = String(channel.id ?? "").trim();
1155
+ if (!id) continue;
1156
+ const kind = classifyChannel(channel, byId);
1157
+ if (kind == null) continue;
1158
+ const name2 = String(channel.name ?? "").trim() || id;
1159
+ out.push({
1160
+ kind,
1161
+ id,
1162
+ guildId,
1163
+ name: name2,
1164
+ guildName,
1165
+ avatar: pickAvatar(channel) || guildAvatar,
1166
+ botKeys: [botKey]
1167
+ });
1168
+ }
1169
+ return out;
1170
+ }
1171
+ function classifyChannel(channel, byId) {
1172
+ const type = channel.type;
1173
+ if (type === CHANNEL_CATEGORY2 || type != null && CHANNEL_DIRECT_OR_VOICE.has(type)) {
1174
+ return null;
1175
+ }
1176
+ if (type != null && DISCORD_THREAD_TYPES2.has(type)) {
1177
+ return 4 /* THREAD */;
1178
+ }
1179
+ if (type != null && type !== CHANNEL_TEXT2) {
1180
+ return null;
1181
+ }
1182
+ const parentId = String(channel.parentId ?? channel.parent_id ?? "").trim();
1183
+ if (parentId) {
1184
+ const parent = byId.get(parentId);
1185
+ if (parent && isTextLikeChannel(parent)) {
1186
+ return 4 /* THREAD */;
1187
+ }
1188
+ }
1189
+ return 3 /* CHANNEL */;
1190
+ }
1191
+ function kindFromListedGuild(guild) {
1192
+ const chatType = guild.type;
1193
+ if (chatType === "private") return 1 /* USER */;
1194
+ if (chatType === "channel") return 3 /* CHANNEL */;
1195
+ if (chatType === "group" || chatType === "supergroup") return 2 /* GROUP */;
1196
+ return 2 /* GROUP */;
1197
+ }
1198
+ function isTextLikeChannel(channel) {
1199
+ const type = channel.type;
1200
+ if (type === CHANNEL_CATEGORY2 || type != null && CHANNEL_DIRECT_OR_VOICE.has(type)) {
1201
+ return false;
1202
+ }
1203
+ return type == null || type === CHANNEL_TEXT2 || DISCORD_THREAD_TYPES2.has(type);
1204
+ }
1205
+ async function listDirectUsers(bot) {
1206
+ const collected = [];
1207
+ let truncated = false;
1208
+ let sawSupported = false;
1209
+ let sawFailure = false;
1210
+ let sawSuccess = false;
1211
+ for (const method of ["getFriendList", "getUserList"]) {
1212
+ const page = await paginate((next) => callBotList(bot, method, next));
1213
+ if (page.unsupported) continue;
1214
+ sawSupported = true;
1215
+ truncated = truncated || page.truncated;
1216
+ if (page.failed) sawFailure = true;
1217
+ else sawSuccess = true;
1218
+ for (const item of page.items) {
1219
+ const user = userFromListItem(item);
1220
+ if (user) collected.push(user);
1221
+ }
1222
+ }
1223
+ return {
1224
+ items: collected,
1225
+ truncated,
1226
+ unsupported: !sawSupported,
1227
+ failed: sawSupported && sawFailure && !sawSuccess
1228
+ };
1229
+ }
1230
+ function userFromListItem(item) {
1231
+ if (!item || typeof item !== "object") return void 0;
1232
+ const rec = item;
1233
+ const nested = rec.user && typeof rec.user === "object" ? rec.user : rec;
1234
+ const id = String(nested.id ?? rec.id ?? rec.userId ?? rec.user_id ?? "").trim();
1235
+ if (!id) return void 0;
1236
+ const name2 = String(nested.name ?? nested.nick ?? rec.nick ?? rec.name ?? "").trim();
1237
+ return { id, name: name2 || id, avatar: pickAvatar(nested, rec) };
1238
+ }
1239
+ function pushTarget(targets, kindFilter, target) {
1240
+ if (!wantsKind(kindFilter, target.kind)) return;
1241
+ if (!target.botKeys[0]) return;
1242
+ targets.push(target);
1243
+ }
1244
+ function mergeTarget(merged, incoming) {
1245
+ const key = `${incoming.kind}\0${incoming.id}\0${incoming.guildId}`;
1246
+ const existing = merged.get(key);
1247
+ if (!existing) {
1248
+ merged.set(key, { ...incoming, botKeys: [...incoming.botKeys] });
1249
+ return;
1250
+ }
1251
+ existing.botKeys = unionKeys(existing.botKeys, incoming.botKeys);
1252
+ if (!existing.name || existing.name === existing.id) {
1253
+ existing.name = incoming.name || existing.name;
1254
+ }
1255
+ if (!existing.guildName) existing.guildName = incoming.guildName;
1256
+ if (!existing.avatar) existing.avatar = incoming.avatar;
1257
+ }
1258
+ function unionKeys(left, right) {
1259
+ const seen = new Set(left);
1260
+ const out = [...left];
1261
+ for (const key of right) {
1262
+ if (!key || seen.has(key)) continue;
1263
+ seen.add(key);
1264
+ out.push(key);
1265
+ }
1266
+ return out;
1267
+ }
1268
+ async function paginate(fetch) {
1269
+ const items = [];
1270
+ const seenTokens = /* @__PURE__ */ new Set();
1271
+ let next;
1272
+ let pages = 0;
1273
+ while (true) {
1274
+ pages += 1;
1275
+ if (pages > MAX_LIST_PAGES || items.length >= MAX_LIST_ITEMS) {
1276
+ return { items, truncated: true, unsupported: false, failed: false };
1277
+ }
1278
+ let raw;
1279
+ try {
1280
+ raw = await fetch(next);
1281
+ } catch (error) {
1282
+ if (isUnsupported(error)) {
1283
+ if (items.length > 0) {
1284
+ return { items, truncated: true, unsupported: false, failed: false };
1285
+ }
1286
+ return { items, truncated: false, unsupported: true, failed: false };
1287
+ }
1288
+ return { items, truncated: items.length > 0, unsupported: false, failed: true };
1289
+ }
1290
+ if (raw == null) break;
1291
+ if (typeof raw.then === "function") {
1292
+ raw = await raw;
1293
+ }
1294
+ if (isAsyncIterableObject(raw) && !hasListData(raw)) {
1295
+ try {
1296
+ const collected = await collectFromIterable(raw);
1297
+ items.push(...collected.items);
1298
+ return {
1299
+ items,
1300
+ truncated: collected.truncated,
1301
+ unsupported: false,
1302
+ failed: false
1303
+ };
1304
+ } catch (error) {
1305
+ if (isUnsupported(error) && items.length === 0) {
1306
+ return { items, truncated: false, unsupported: true, failed: false };
1307
+ }
1308
+ return { items, truncated: items.length > 0, unsupported: false, failed: true };
1309
+ }
1310
+ }
1311
+ const page = normalizePage(raw);
1312
+ items.push(...page.data);
1313
+ const token = page.next;
1314
+ if (!token || seenTokens.has(token) || page.data.length === 0) break;
1315
+ seenTokens.add(token);
1316
+ next = token;
1317
+ }
1318
+ return { items, truncated: false, unsupported: false, failed: false };
1319
+ }
1320
+ function normalizePage(raw) {
1321
+ if (Array.isArray(raw)) {
1322
+ return { data: raw };
1323
+ }
1324
+ if (!raw || typeof raw !== "object") {
1325
+ return { data: [] };
1326
+ }
1327
+ const rec = raw;
1328
+ const data = Array.isArray(rec.data) ? rec.data : [];
1329
+ const next = rec.next == null || rec.next === "" ? void 0 : String(rec.next);
1330
+ return { data, next };
1331
+ }
1332
+ function callBotList(bot, method, next, guildId) {
1333
+ const fn = bot[method];
1334
+ if (typeof fn !== "function") {
1335
+ return Promise.reject(new Error(`${method} is not supported`));
1336
+ }
1337
+ if (method === "getChannelList") {
1338
+ return Promise.resolve(fn.call(bot, guildId ?? "", next));
1339
+ }
1340
+ return Promise.resolve(fn.call(bot, next));
1341
+ }
1342
+ function hasListData(value) {
1343
+ return Array.isArray(value.data);
1344
+ }
1345
+ function isAsyncIterableObject(value) {
1346
+ return typeof value === "object" && value != null && Symbol.asyncIterator in value && !Array.isArray(value);
1347
+ }
1348
+ async function collectFromIterable(iterable) {
1349
+ const items = [];
1350
+ for await (const item of iterable) {
1351
+ items.push(item);
1352
+ if (items.length >= MAX_LIST_ITEMS) return { items, truncated: true };
1353
+ }
1354
+ return { items, truncated: false };
1355
+ }
1356
+ function isUnsupported(error) {
1357
+ if (error && typeof error === "object" && "name" in error) {
1358
+ const name2 = String(error.name);
1359
+ if (/unsupported/i.test(name2)) return true;
1360
+ }
1361
+ const message = error instanceof Error ? error.message : String(error);
1362
+ return /not (implemented|supported)|unsupported|is not a function/i.test(message);
1363
+ }
1364
+
1365
+ // src/dbk/recall.ts
1366
+ import { create as create7 } from "@bufbuild/protobuf";
1367
+ var KOISHI_STATUS_ONLINE2 = 1;
1368
+ var KOISHI_STATUS_CONNECT2 = 2;
1369
+ var KOISHI_STATUS_RECONNECT2 = 4;
1370
+ var RECALL_TIMEOUT_MS = 25e3;
1371
+ async function recallMessage(ctx, params) {
1372
+ const bot = findBot3(ctx, params.botKey);
1373
+ if (!bot) {
1374
+ throw new DbkRpcError(3 /* NOT_FOUND */, `bot not found: ${params.botKey.trim() || "(empty)"}`);
1375
+ }
1376
+ if (!botCanRecall(bot)) {
1377
+ return failed("bot has no message.recall capability", false);
1378
+ }
1379
+ const notReady = notReadyResult(bot);
1380
+ if (notReady) return notReady;
1381
+ const messageId = params.messageId.trim();
1382
+ if (!messageId) {
1383
+ return failed("message_id is empty", false);
1384
+ }
1385
+ const channelId = params.target?.id.trim() ?? "";
1386
+ if (!channelId) {
1387
+ return failed("target id is empty", false);
1388
+ }
1389
+ try {
1390
+ await withTimeout(Promise.resolve(bot.deleteMessage(channelId, messageId)), RECALL_TIMEOUT_MS);
1391
+ return create7(RecallResultSchema, {
1392
+ status: 1 /* OK */,
1393
+ reason: "",
1394
+ retryable: false
1395
+ });
1396
+ } catch (error) {
1397
+ return classifyRecallError(error);
1398
+ }
1399
+ }
1400
+ function findBot3(ctx, botKey) {
1401
+ const key = botKey.trim();
1402
+ if (!key) return void 0;
1403
+ return ctx.bots.find((bot) => botKeyOf3(bot) === key);
1404
+ }
1405
+ function botKeyOf3(bot) {
1406
+ const platform = bot.platform ?? "";
1407
+ const selfId = bot.selfId ?? "";
1408
+ return platform && selfId ? `${platform}:${selfId}` : "";
1409
+ }
1410
+ function botCanRecall(bot) {
1411
+ if (typeof bot.deleteMessage !== "function") return false;
1412
+ try {
1413
+ const src = Function.prototype.toString.call(bot.deleteMessage);
1414
+ if (/not implemented|NotImplementedError/i.test(src)) return false;
1415
+ } catch {
1416
+ }
1417
+ return true;
1418
+ }
1419
+ function notReadyResult(bot) {
1420
+ switch (bot.status) {
1421
+ case KOISHI_STATUS_ONLINE2:
1422
+ return void 0;
1423
+ case KOISHI_STATUS_CONNECT2:
1424
+ case KOISHI_STATUS_RECONNECT2:
1425
+ return failed("bot is connecting", true);
1426
+ default:
1427
+ return failed("bot is unavailable", false);
1428
+ }
1429
+ }
1430
+ function failed(reason, retryable) {
1431
+ return create7(RecallResultSchema, {
1432
+ status: 4 /* FAILED */,
1433
+ reason,
1434
+ retryable
1435
+ });
1436
+ }
1437
+ function unknown(reason) {
1438
+ return create7(RecallResultSchema, {
1439
+ status: 3 /* UNKNOWN */,
1440
+ reason,
1441
+ retryable: false
1442
+ });
1443
+ }
1444
+ function classifyRecallError(error) {
1445
+ if (error instanceof RecallTimeoutError) {
1446
+ return unknown(error.message);
1447
+ }
1448
+ const text = errorMessage(error);
1449
+ const lower = text.toLowerCase();
1450
+ const status = httpStatusOf(error);
1451
+ if (isTimeout(error, lower, status)) {
1452
+ return unknown(text || "message.recall timed out");
1453
+ }
1454
+ if (isNotImplemented(lower)) {
1455
+ return failed("bot has no message.recall capability", false);
1456
+ }
1457
+ if (status === 401 || status === 403 || isForbidden(lower)) {
1458
+ return failed(text || "not permitted to recall message", false);
1459
+ }
1460
+ if (status === 400 || status === 404) {
1461
+ return failed(text || "recall rejected", false);
1462
+ }
1463
+ if (status === 429 || status !== void 0 && status >= 500 || isRetryableNetwork(error, lower)) {
1464
+ return failed(text || "recall failed", true);
1465
+ }
1466
+ return failed(text || "message.recall failed", false);
1467
+ }
1468
+ function httpStatusOf(error) {
1469
+ if (!error || typeof error !== "object") return void 0;
1470
+ const record = error;
1471
+ if (typeof record.status === "number") return record.status;
1472
+ if (typeof record.statusCode === "number") return record.statusCode;
1473
+ const response = record.response;
1474
+ if (response && typeof response === "object") {
1475
+ const nested = response.status;
1476
+ if (typeof nested === "number") return nested;
1477
+ }
1478
+ if (typeof record.code === "number" && record.code >= 400 && record.code < 600) return record.code;
1479
+ return void 0;
1480
+ }
1481
+ function errorMessage(error) {
1482
+ if (error instanceof Error) return error.message.trim();
1483
+ if (typeof error === "string") return error.trim();
1484
+ return String(error);
1485
+ }
1486
+ function isTimeout(error, lower, status) {
1487
+ if (status === 408 || status === 504) return true;
1488
+ if (error && typeof error === "object") {
1489
+ const name2 = error.name;
1490
+ if (name2 === "TimeoutError" || name2 === "AbortError") return true;
1491
+ const code = error.code;
1492
+ if (code === "ETIMEDOUT" || code === "UND_ERR_CONNECT_TIMEOUT" || code === "ABORT_ERR") return true;
1493
+ }
1494
+ return lower.includes("timed out") || lower.includes("timeout");
1495
+ }
1496
+ function isNotImplemented(lower) {
1497
+ return lower.includes("not implemented") || lower.includes("unsupported");
1498
+ }
1499
+ function isForbidden(lower) {
1500
+ return lower.includes("forbidden") || lower.includes("not permitted") || lower.includes("missing permission") || lower.includes("missing access") || lower.includes("can't delete") || lower.includes("cannot delete") || lower.includes("message can't be deleted") || lower.includes("not enough rights");
1501
+ }
1502
+ function isRetryableNetwork(error, lower) {
1503
+ if (error && typeof error === "object") {
1504
+ const code = error.code;
1505
+ if (code === "ECONNRESET" || code === "ECONNREFUSED" || code === "ECONNABORTED" || code === "ENOTFOUND" || code === "EAI_AGAIN" || code === "EPIPE" || code === "UND_ERR_SOCKET" || code === "UND_ERR_CONNECT_TIMEOUT") {
1506
+ return true;
1507
+ }
1508
+ }
1509
+ return lower.includes("network") || lower.includes("fetch failed") || lower.includes("socket hang up") || lower.includes("econnreset") || lower.includes("econnrefused");
1510
+ }
1511
+ var RecallTimeoutError = class extends Error {
1512
+ constructor() {
1513
+ super("message.recall timed out");
1514
+ this.name = "RecallTimeoutError";
1515
+ }
1516
+ };
1517
+ function withTimeout(promise, ms) {
1518
+ return new Promise((resolve, reject) => {
1519
+ const timer = setTimeout(() => reject(new RecallTimeoutError()), ms);
1520
+ promise.then(
1521
+ (value) => {
1522
+ clearTimeout(timer);
1523
+ resolve(value);
1524
+ },
1525
+ (error) => {
1526
+ clearTimeout(timer);
1527
+ reject(error);
1528
+ }
1529
+ );
1530
+ });
1531
+ }
1532
+
1533
+ // src/dbk/send.ts
1534
+ import { create as create8 } from "@bufbuild/protobuf";
1535
+ import { h } from "koishi";
1536
+ var SEND_TIMEOUT_MS = 25e3;
1537
+ var MENTION_ALL_FALLBACK = "@\u5168\u4F53\u6210\u5458";
1538
+ async function sendMessage(ctx, request) {
1539
+ const bot = findBot4(ctx, request.botKey);
1540
+ if (!bot) {
1541
+ throw new DbkRpcError(3 /* NOT_FOUND */, `bot not found: ${request.botKey.trim() || "(empty)"}`);
1542
+ }
1543
+ const status = toDbkBotStatus(bot.status);
1544
+ if (status === 3 /* CONNECTING */) {
1545
+ return failed2("bot is connecting", true);
1546
+ }
1547
+ if (status !== 1 /* READY */) {
1548
+ return failed2("bot is unavailable", false);
1549
+ }
1550
+ const channelId = request.target?.id.trim() ?? "";
1551
+ if (!channelId) {
1552
+ return failed2("target id is empty", false);
1553
+ }
1554
+ if (request.units.length === 0) {
1555
+ return failed2("units is empty", false);
1556
+ }
1557
+ const guildId = request.target?.guildId.trim() || void 0;
1558
+ const features = botFeatures(bot);
1559
+ const recallable = features.includes("message.recall") || typeof bot.deleteMessage === "function";
1560
+ const mentionAll = features.includes("mention.all");
1561
+ const receipts = [];
1562
+ const failures = [];
1563
+ const unknownReasons = [];
1564
+ let failuresRetryable = true;
1565
+ let quoteUsed = false;
1566
+ let aborted = false;
1567
+ const nextQuoteId = () => {
1568
+ if (quoteUsed) return void 0;
1569
+ const id = request.replyToMessageId.trim();
1570
+ return id || void 0;
1571
+ };
1572
+ const sendElements = async (elements) => {
1573
+ if (aborted) return;
1574
+ if (elements.length === 0) {
1575
+ failuresRetryable = false;
1576
+ failures.push("no sendable segments");
1577
+ return;
1578
+ }
1579
+ const outgoing = withQuote(elements, nextQuoteId());
1580
+ try {
1581
+ const ids = await bot.sendMessage(channelId, outgoing, guildId);
1582
+ if (aborted) return;
1583
+ quoteUsed = true;
1584
+ const kept = normalizeMessageIds(ids);
1585
+ if (kept.length === 0) {
1586
+ unknownReasons.push("send returned no message id");
1587
+ return;
1588
+ }
1589
+ for (const messageId of kept) {
1590
+ receipts.push(create8(SendReceiptSchema, { messageId, recallable }));
1591
+ }
1592
+ } catch (error) {
1593
+ if (aborted) return;
1594
+ if (isTimeoutError(error)) {
1595
+ quoteUsed = true;
1596
+ unknownReasons.push(errorMessage2(error) || "send timed out");
1597
+ return;
1598
+ }
1599
+ const retryable = isRetryableSendError(error);
1600
+ failuresRetryable = failuresRetryable && retryable;
1601
+ failures.push(errorMessage2(error) || "send failed");
1602
+ }
1603
+ };
1604
+ const work = (async () => {
1605
+ for (const unit of request.units) {
1606
+ if (aborted) break;
1607
+ if (unit.body.case === "normal") {
1608
+ await sendElements(segmentsToElements(unit.body.value.segments, { mentionAll }));
1609
+ } else if (unit.body.case === "forward") {
1610
+ const nodes = unit.body.value.nodes;
1611
+ if (nodes.length === 0) {
1612
+ failuresRetryable = false;
1613
+ failures.push("forward has no nodes");
1614
+ continue;
1615
+ }
1616
+ for (const node of nodes) {
1617
+ if (aborted) break;
1618
+ await sendElements(forwardNodeElements(node, { mentionAll }));
1619
+ }
1620
+ } else {
1621
+ failuresRetryable = false;
1622
+ failures.push("empty send unit");
1623
+ }
1624
+ }
1625
+ return finalize(receipts, failures, unknownReasons, failuresRetryable);
1626
+ })();
1627
+ try {
1628
+ return await withTimeout2(work, SEND_TIMEOUT_MS);
1629
+ } catch (error) {
1630
+ aborted = true;
1631
+ void work.catch(() => void 0);
1632
+ if (error instanceof SendTimeoutError || isTimeoutError(error)) {
1633
+ return create8(SendResultSchema, {
1634
+ status: 3 /* UNKNOWN */,
1635
+ reason: error instanceof Error ? error.message : "send timed out",
1636
+ retryable: false,
1637
+ receipts
1638
+ });
1639
+ }
1640
+ return create8(SendResultSchema, {
1641
+ status: 4 /* FAILED */,
1642
+ reason: errorMessage2(error) || "message.send failed",
1643
+ retryable: isRetryableSendError(error),
1644
+ receipts
1645
+ });
1646
+ }
1647
+ }
1648
+ function segmentsToElements(segments, options) {
1649
+ const elements = [];
1650
+ const quoted = /* @__PURE__ */ new Set();
1651
+ const addQuote = (id) => {
1652
+ const trimmed = id.trim();
1653
+ if (!trimmed || quoted.has(trimmed)) return;
1654
+ quoted.add(trimmed);
1655
+ elements.push(h.quote(trimmed));
1656
+ };
1657
+ if (options?.quoteMessageId) addQuote(options.quoteMessageId);
1658
+ for (const segment of segments) {
1659
+ switch (segment.body.case) {
1660
+ case "text": {
1661
+ elements.push(h.text(segment.body.value.text));
1662
+ break;
1663
+ }
1664
+ case "image": {
1665
+ const uri = segment.body.value.uri.trim();
1666
+ if (uri) elements.push(h.image(uri));
1667
+ break;
1668
+ }
1669
+ case "video": {
1670
+ const uri = segment.body.value.uri.trim();
1671
+ if (uri) elements.push(h.video(uri));
1672
+ break;
1673
+ }
1674
+ case "audio": {
1675
+ const uri = segment.body.value.uri.trim();
1676
+ if (uri) elements.push(h.audio(uri));
1677
+ break;
1678
+ }
1679
+ case "mention": {
1680
+ const id = segment.body.value.id.trim();
1681
+ if (id) elements.push(h.at(id));
1682
+ break;
1683
+ }
1684
+ case "mentionAll": {
1685
+ if (options?.mentionAll === false) {
1686
+ elements.push(h.text(MENTION_ALL_FALLBACK));
1687
+ } else {
1688
+ elements.push(h("at", { type: "all" }));
1689
+ }
1690
+ break;
1691
+ }
1692
+ case "reply": {
1693
+ addQuote(segment.body.value.messageId);
1694
+ break;
1695
+ }
1696
+ case "link": {
1697
+ const url = segment.body.value.url.trim();
1698
+ if (!url) break;
1699
+ const title = segment.body.value.title.trim();
1700
+ elements.push(h("a", { href: url }, title || url));
1701
+ break;
1702
+ }
1703
+ case "unknown":
1704
+ case void 0:
1705
+ break;
1706
+ }
1707
+ }
1708
+ return elements;
1709
+ }
1710
+ function forwardNodeElements(node, options) {
1711
+ const body = segmentsToElements(node.segments, options);
1712
+ const name2 = node.senderName.trim();
1713
+ if (!name2) return body;
1714
+ return [h.text(`${name2}
1715
+ `), ...body];
1716
+ }
1717
+ function withQuote(elements, quoteId) {
1718
+ if (!quoteId) return elements;
1719
+ if (hasQuote(elements, quoteId)) return elements;
1720
+ return [h.quote(quoteId), ...elements];
1721
+ }
1722
+ function hasQuote(elements, messageId) {
1723
+ return elements.some((el) => el.type === "quote" && String(el.attrs?.id ?? "") === messageId);
1724
+ }
1725
+ function normalizeMessageIds(raw) {
1726
+ if (raw == null) return [];
1727
+ const list = Array.isArray(raw) ? raw : [raw];
1728
+ return list.map((id) => String(id).trim()).filter(Boolean);
1729
+ }
1730
+ function findBot4(ctx, botKey) {
1731
+ const key = botKey.trim();
1732
+ if (!key) return void 0;
1733
+ return ctx.bots.find((bot) => !bot.hidden && botKeyOf4(bot) === key);
1734
+ }
1735
+ function botKeyOf4(bot) {
1736
+ const platform = bot.platform ?? "";
1737
+ const selfId = bot.selfId ?? "";
1738
+ return platform && selfId ? `${platform}:${selfId}` : "";
1739
+ }
1740
+ function finalize(receipts, failures, unknownReasons, failuresRetryable) {
1741
+ if (unknownReasons.length > 0) {
1742
+ return create8(SendResultSchema, {
1743
+ status: 3 /* UNKNOWN */,
1744
+ reason: unknownReasons[0] ?? "send outcome unknown",
1745
+ retryable: false,
1746
+ receipts
1747
+ });
1748
+ }
1749
+ if (failures.length === 0) {
1750
+ return create8(SendResultSchema, {
1751
+ status: 1 /* OK */,
1752
+ reason: "",
1753
+ retryable: false,
1754
+ receipts
1755
+ });
1756
+ }
1757
+ if (receipts.length > 0) {
1758
+ return create8(SendResultSchema, {
1759
+ status: 2 /* PARTIAL */,
1760
+ reason: failures.join("; "),
1761
+ retryable: false,
1762
+ receipts
1763
+ });
1764
+ }
1765
+ return create8(SendResultSchema, {
1766
+ status: 4 /* FAILED */,
1767
+ reason: failures.join("; "),
1768
+ retryable: failuresRetryable,
1769
+ receipts: []
1770
+ });
1771
+ }
1772
+ function failed2(reason, retryable) {
1773
+ return create8(SendResultSchema, {
1774
+ status: 4 /* FAILED */,
1775
+ reason,
1776
+ retryable
1777
+ });
1778
+ }
1779
+ function isRetryableSendError(error) {
1780
+ const text = errorMessage2(error);
1781
+ const lower = text.toLowerCase();
1782
+ const status = httpStatusOf2(error);
1783
+ if (status === 401 || status === 403 || isForbidden2(lower)) return false;
1784
+ if (status === 400 || status === 404) return false;
1785
+ if (status === 429 || status !== void 0 && status >= 500) return true;
1786
+ if (isRetryableNetwork2(error, lower)) return true;
1787
+ if (/\brate.?limit\b|too many requests/.test(lower)) return true;
1788
+ return false;
1789
+ }
1790
+ function isTimeoutError(error) {
1791
+ const text = errorMessage2(error).toLowerCase();
1792
+ const status = httpStatusOf2(error);
1793
+ if (error instanceof SendTimeoutError) return true;
1794
+ if (status === 408 || status === 504) return true;
1795
+ if (error && typeof error === "object") {
1796
+ const name2 = error.name;
1797
+ if (name2 === "TimeoutError" || name2 === "AbortError") return true;
1798
+ const code = error.code;
1799
+ if (code === "ETIMEDOUT" || code === "UND_ERR_CONNECT_TIMEOUT" || code === "ABORT_ERR") return true;
1800
+ }
1801
+ return text.includes("timed out") || text.includes("timeout");
1802
+ }
1803
+ function httpStatusOf2(error) {
1804
+ if (!error || typeof error !== "object") return void 0;
1805
+ const record = error;
1806
+ if (typeof record.status === "number") return record.status;
1807
+ if (typeof record.statusCode === "number") return record.statusCode;
1808
+ const response = record.response;
1809
+ if (response && typeof response === "object") {
1810
+ const nested = response.status;
1811
+ if (typeof nested === "number") return nested;
1812
+ }
1813
+ if (typeof record.code === "number" && record.code >= 400 && record.code < 600) return record.code;
1814
+ return void 0;
1815
+ }
1816
+ function errorMessage2(error) {
1817
+ if (error instanceof Error) return error.message.trim();
1818
+ if (typeof error === "string") return error.trim();
1819
+ return String(error);
1820
+ }
1821
+ function isForbidden2(lower) {
1822
+ return lower.includes("forbidden") || lower.includes("missing permission") || lower.includes("missing access") || lower.includes("not permitted") || lower.includes("unauthorized");
1823
+ }
1824
+ function isRetryableNetwork2(error, lower) {
1825
+ if (error && typeof error === "object") {
1826
+ const code = error.code;
1827
+ if (code === "ECONNRESET" || code === "ECONNREFUSED" || code === "ECONNABORTED" || code === "ENOTFOUND" || code === "EAI_AGAIN" || code === "EPIPE" || code === "UND_ERR_SOCKET" || code === "UND_ERR_CONNECT_TIMEOUT") {
1828
+ return true;
1829
+ }
1830
+ }
1831
+ return lower.includes("network") || lower.includes("fetch failed") || lower.includes("socket hang up") || lower.includes("econnreset") || lower.includes("econnrefused");
1832
+ }
1833
+ var SendTimeoutError = class extends Error {
1834
+ constructor() {
1835
+ super("send timed out");
1836
+ this.name = "SendTimeoutError";
1837
+ }
1838
+ };
1839
+ function withTimeout2(promise, ms) {
1840
+ return new Promise((resolve, reject) => {
1841
+ const timer = setTimeout(() => reject(new SendTimeoutError()), ms);
1842
+ promise.then(
1843
+ (value) => {
1844
+ clearTimeout(timer);
1845
+ resolve(value);
1846
+ },
1847
+ (error) => {
1848
+ clearTimeout(timer);
1849
+ reject(error);
1850
+ }
1851
+ );
1852
+ });
1853
+ }
1854
+
1855
+ // src/dbk/handlers.ts
1856
+ function createGatewayHandlers(ctx) {
1857
+ const snapshot = () => create9(ListBotsResponseSchema, { bots: listBots(ctx) });
1858
+ return {
1859
+ [DbkMethod.SESSION_HELLO]: () => create9(HelloResponseSchema, {
1860
+ protocolVersion: PROTOCOL_VERSION,
1861
+ gatewayVersion: GATEWAY_VERSION,
1862
+ bots: snapshot().bots
1863
+ }),
1864
+ [DbkMethod.BOTS_LIST]: snapshot,
1865
+ [DbkMethod.TARGETS_LIST]: (request) => listTargets(ctx, request),
1866
+ [DbkMethod.TARGETS_GET]: (request) => getTarget(ctx, request),
1867
+ [DbkMethod.MESSAGE_SEND]: (request) => sendMessage(ctx, request),
1868
+ [DbkMethod.MESSAGE_RECALL]: (request) => recallMessage(ctx, request)
1869
+ };
1870
+ }
1871
+
1872
+ // src/index.ts
1873
+ var name = "dynamic-bot";
1874
+ var reusable = true;
1875
+ var inject = ["server", "http"];
1876
+ var WebsocketType = /* @__PURE__ */ ((WebsocketType2) => {
1877
+ WebsocketType2["SERVER"] = "server";
1878
+ WebsocketType2["CLIENT"] = "client";
1879
+ return WebsocketType2;
1880
+ })(WebsocketType || {});
1881
+ var Config = Schema.intersect([
1882
+ Schema.object({
1883
+ websocketType: Schema.union([
1884
+ Schema.const("server" /* SERVER */).description(
1885
+ "\u6B63\u5411\u8FDE\u63A5\uFF08dynamic-bot \u8FDE\u63A5\u672C\u63D2\u4EF6\uFF0C\u590D\u7528 Koishi HTTP \u670D\u52A1\uFF09"
1886
+ ),
1887
+ Schema.const("client" /* CLIENT */).description(
1888
+ "\u53CD\u5411\u8FDE\u63A5\uFF08\u672C\u63D2\u4EF6\u8FDE\u63A5 dynamic-bot\uFF09"
1889
+ )
1890
+ ]).description("\u8FDE\u63A5\u6A21\u5F0F").required(),
1891
+ accessToken: Schema.string().role("secret").required().description("\u4E0E dynamic-bot \u63D2\u4EF6\u5171\u4EAB\u7684\u8BBF\u95EE\u4EE4\u724C"),
1892
+ dev: Schema.boolean().default(false).description("\u4F7F\u7528 protojson \u6587\u672C\u5E27\uFF08\u4EC5\u672C\u5730\u8C03\u8BD5\uFF09")
1893
+ }).description("\u52A8\u6001\u673A\u5668\u4EBA\u8FDE\u63A5\u914D\u7F6E"),
1894
+ Schema.union([
1895
+ Schema.object({
1896
+ websocketType: Schema.const("server" /* SERVER */).required(),
1897
+ path: Schema.string().default("/dbk").description(
1898
+ "\u6302\u8F7D\u5728 Koishi HTTP \u670D\u52A1\u4E0A\u7684 WebSocket \u8DEF\u5F84\uFF0C\u65E0\u9700\u5355\u72EC\u5F00\u7AEF\u53E3"
1899
+ )
1900
+ }).description("\u6B63\u5411\u8FDE\u63A5\u914D\u7F6E"),
1901
+ Schema.object({
1902
+ websocketType: Schema.const("client" /* CLIENT */).required(),
1903
+ host: Schema.string().default("127.0.0.1").description("dynamic-bot \u53CD\u5411 WebSocket \u76D1\u542C\u5730\u5740"),
1904
+ port: Schema.number().min(1).max(65535).default(9800).description("dynamic-bot \u53CD\u5411 WebSocket \u7AEF\u53E3"),
1905
+ reconnect: Schema.boolean().default(true).description("\u65AD\u5F00\u540E\u81EA\u52A8\u91CD\u8FDE")
1906
+ }).description("\u53CD\u5411\u8FDE\u63A5\u914D\u7F6E")
1907
+ ])
1908
+ ]);
1909
+ function apply(ctx, config) {
1910
+ const gateway = new DbkGateway(
1911
+ ctx,
1912
+ {
1913
+ encoding: config.dev ? "json" : "binary",
1914
+ accessToken: config.accessToken ?? "",
1915
+ path: config.path ?? "/dbk",
1916
+ host: config.host ?? "127.0.0.1",
1917
+ port: config.port ?? 9800,
1918
+ reconnect: config.reconnect ?? true
1919
+ },
1920
+ createGatewayHandlers(ctx)
1921
+ );
1922
+ if (config.websocketType === "client" /* CLIENT */) {
1923
+ gateway.startReverse();
1924
+ } else {
1925
+ gateway.startForward();
1926
+ }
1927
+ watchBots(ctx, gateway);
1928
+ ctx.on("dispose", () => gateway.stop());
1929
+ }
1930
+ export {
1931
+ Config,
1932
+ DbkEvent,
1933
+ DbkGateway,
1934
+ DbkMethod,
1935
+ DbkRpcError,
1936
+ PROTOCOL_VERSION,
1937
+ WebsocketType,
1938
+ apply,
1939
+ inject,
1940
+ name,
1941
+ reusable
1942
+ };