@ycodium-ai/plugin-im 0.2.3202 → 0.2.3210
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/assets/ui.js +17 -4
- package/dist/index.js +1379 -487
- package/locales/en.json +65 -14
- package/locales/zh-CN.json +65 -14
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -137,7 +137,7 @@ var require_punycode = __commonJS({
|
|
|
137
137
|
}
|
|
138
138
|
return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
|
|
139
139
|
};
|
|
140
|
-
var
|
|
140
|
+
var decode3 = function(input) {
|
|
141
141
|
const output = [];
|
|
142
142
|
const inputLength = input.length;
|
|
143
143
|
let i = 0;
|
|
@@ -249,7 +249,7 @@ var require_punycode = __commonJS({
|
|
|
249
249
|
};
|
|
250
250
|
var toUnicode = function(input) {
|
|
251
251
|
return mapDomain(input, function(string) {
|
|
252
|
-
return regexPunycode.test(string) ?
|
|
252
|
+
return regexPunycode.test(string) ? decode3(string.slice(4).toLowerCase()) : string;
|
|
253
253
|
});
|
|
254
254
|
};
|
|
255
255
|
var toASCII = function(input) {
|
|
@@ -275,7 +275,7 @@ var require_punycode = __commonJS({
|
|
|
275
275
|
"decode": ucs2decode,
|
|
276
276
|
"encode": ucs2encode
|
|
277
277
|
},
|
|
278
|
-
"decode":
|
|
278
|
+
"decode": decode3,
|
|
279
279
|
"encode": encode2,
|
|
280
280
|
"toASCII": toASCII,
|
|
281
281
|
"toUnicode": toUnicode
|
|
@@ -18475,7 +18475,7 @@ var require_utils = __commonJS({
|
|
|
18475
18475
|
return acc;
|
|
18476
18476
|
}, target2);
|
|
18477
18477
|
};
|
|
18478
|
-
var
|
|
18478
|
+
var decode3 = function(str2, defaultDecoder, charset) {
|
|
18479
18479
|
var strWithoutPlus = str2.replace(/\+/g, " ");
|
|
18480
18480
|
if (charset === "iso-8859-1") {
|
|
18481
18481
|
return strWithoutPlus.replace(/%[0-9a-f]{2}/gi, unescape);
|
|
@@ -18601,7 +18601,7 @@ var require_utils = __commonJS({
|
|
|
18601
18601
|
assign,
|
|
18602
18602
|
combine,
|
|
18603
18603
|
compact,
|
|
18604
|
-
decode:
|
|
18604
|
+
decode: decode3,
|
|
18605
18605
|
encode: encode2,
|
|
18606
18606
|
isBuffer,
|
|
18607
18607
|
isOverflow,
|
|
@@ -23360,7 +23360,7 @@ var require_websocket = __commonJS({
|
|
|
23360
23360
|
var http = __require("http");
|
|
23361
23361
|
var net = __require("net");
|
|
23362
23362
|
var tls = __require("tls");
|
|
23363
|
-
var { randomBytes, createHash:
|
|
23363
|
+
var { randomBytes, createHash: createHash8 } = __require("crypto");
|
|
23364
23364
|
var { Duplex, Readable } = __require("stream");
|
|
23365
23365
|
var { URL: URL2 } = __require("url");
|
|
23366
23366
|
var PerMessageDeflate = require_permessage_deflate();
|
|
@@ -24028,7 +24028,7 @@ var require_websocket = __commonJS({
|
|
|
24028
24028
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
24029
24029
|
return;
|
|
24030
24030
|
}
|
|
24031
|
-
const digest =
|
|
24031
|
+
const digest = createHash8("sha1").update(key + GUID).digest("base64");
|
|
24032
24032
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
24033
24033
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
24034
24034
|
return;
|
|
@@ -24397,7 +24397,7 @@ var require_websocket_server = __commonJS({
|
|
|
24397
24397
|
var EventEmitter = __require("events");
|
|
24398
24398
|
var http = __require("http");
|
|
24399
24399
|
var { Duplex } = __require("stream");
|
|
24400
|
-
var { createHash:
|
|
24400
|
+
var { createHash: createHash8 } = __require("crypto");
|
|
24401
24401
|
var extension = require_extension();
|
|
24402
24402
|
var PerMessageDeflate = require_permessage_deflate();
|
|
24403
24403
|
var subprotocol = require_subprotocol();
|
|
@@ -24704,7 +24704,7 @@ var require_websocket_server = __commonJS({
|
|
|
24704
24704
|
);
|
|
24705
24705
|
}
|
|
24706
24706
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
24707
|
-
const digest =
|
|
24707
|
+
const digest = createHash8("sha1").update(key + GUID).digest("base64");
|
|
24708
24708
|
const headers = [
|
|
24709
24709
|
"HTTP/1.1 101 Switching Protocols",
|
|
24710
24710
|
"Upgrade: websocket",
|
|
@@ -24912,7 +24912,7 @@ var require_base64 = __commonJS({
|
|
|
24912
24912
|
return String.fromCharCode.apply(String, chunk.slice(0, i2));
|
|
24913
24913
|
};
|
|
24914
24914
|
var invalidEncoding = "invalid encoding";
|
|
24915
|
-
base64.decode = function
|
|
24915
|
+
base64.decode = function decode3(string, buffer, offset) {
|
|
24916
24916
|
var start = offset;
|
|
24917
24917
|
var j = 0, t;
|
|
24918
24918
|
for (var i2 = 0; i2 < string.length; ) {
|
|
@@ -121305,7 +121305,7 @@ var require_lib2 = __commonJS({
|
|
|
121305
121305
|
Header.encodeDelimited = function encodeDelimited(message, writer) {
|
|
121306
121306
|
return this.encode(message, writer).ldelim();
|
|
121307
121307
|
};
|
|
121308
|
-
Header.decode = function
|
|
121308
|
+
Header.decode = function decode4(reader, length) {
|
|
121309
121309
|
if (!(reader instanceof $Reader))
|
|
121310
121310
|
reader = $Reader.create(reader);
|
|
121311
121311
|
let end = length === void 0 ? reader.len : reader.pos + length, message = new $root.pbbp2.Header();
|
|
@@ -121451,7 +121451,7 @@ var require_lib2 = __commonJS({
|
|
|
121451
121451
|
Frame.encodeDelimited = function encodeDelimited(message, writer) {
|
|
121452
121452
|
return this.encode(message, writer).ldelim();
|
|
121453
121453
|
};
|
|
121454
|
-
Frame.decode = function
|
|
121454
|
+
Frame.decode = function decode4(reader, length) {
|
|
121455
121455
|
if (!(reader instanceof $Reader))
|
|
121456
121456
|
reader = $Reader.create(reader);
|
|
121457
121457
|
let end = length === void 0 ? reader.len : reader.pos + length, message = new $root.pbbp2.Frame();
|
|
@@ -121678,7 +121678,7 @@ var require_lib2 = __commonJS({
|
|
|
121678
121678
|
})();
|
|
121679
121679
|
return pbbp22;
|
|
121680
121680
|
})();
|
|
121681
|
-
var
|
|
121681
|
+
var decode3 = (buffer) => {
|
|
121682
121682
|
return pbbp2.Frame.decode(buffer);
|
|
121683
121683
|
};
|
|
121684
121684
|
var WSConfig = class {
|
|
@@ -122177,7 +122177,7 @@ var require_lib2 = __commonJS({
|
|
|
122177
122177
|
const wsInstance = this.wsConfig.getWSInstance();
|
|
122178
122178
|
wsInstance === null || wsInstance === void 0 ? void 0 : wsInstance.on("message", (buffer) => __awaiter(this, void 0, void 0, function* () {
|
|
122179
122179
|
this.clearLiveness();
|
|
122180
|
-
const data =
|
|
122180
|
+
const data = decode3(buffer);
|
|
122181
122181
|
const { method } = data;
|
|
122182
122182
|
if (method === FrameType.control) {
|
|
122183
122183
|
yield this.handleControlData(data);
|
|
@@ -125575,14 +125575,14 @@ ${lines.join("\n")}
|
|
|
125575
125575
|
const senderOpenId = event.sender.sender_id.open_id;
|
|
125576
125576
|
const senderFallbackId = (_d = (_c = event.sender.sender_id.user_id) !== null && _c !== void 0 ? _c : event.sender.sender_id.union_id) !== null && _d !== void 0 ? _d : "";
|
|
125577
125577
|
const senderId = senderOpenId !== null && senderOpenId !== void 0 ? senderOpenId : senderFallbackId;
|
|
125578
|
-
const
|
|
125578
|
+
const senderName2 = senderOpenId ? (_e = opts.resolveSenderName) === null || _e === void 0 ? void 0 : _e.call(opts, senderOpenId) : void 0;
|
|
125579
125579
|
const createMs = msg.create_time ? parseInt(msg.create_time, 10) : 0;
|
|
125580
125580
|
return {
|
|
125581
125581
|
messageId: msg.message_id,
|
|
125582
125582
|
chatId: msg.chat_id,
|
|
125583
125583
|
chatType: msg.chat_type,
|
|
125584
125584
|
senderId,
|
|
125585
|
-
senderName,
|
|
125585
|
+
senderName: senderName2,
|
|
125586
125586
|
content,
|
|
125587
125587
|
rawContentType: msg.message_type,
|
|
125588
125588
|
resources,
|
|
@@ -126850,6 +126850,7 @@ var SETTINGS_REMOVE_TARGET_COMMAND = "settings.removeTarget";
|
|
|
126850
126850
|
var LARK_SETUP_SESSION_COMMAND = "larkSetup.session";
|
|
126851
126851
|
var BOT_ACTION_COMMANDS = {
|
|
126852
126852
|
checkConnection: "im.settings.checkConnection",
|
|
126853
|
+
reconnect: "im.settings.reconnect",
|
|
126853
126854
|
copyDiagnostics: "im.settings.copyDiagnostics",
|
|
126854
126855
|
larkRepair: "im.settings.larkRepair"
|
|
126855
126856
|
};
|
|
@@ -127416,6 +127417,31 @@ var makeChatQueue = (core) => {
|
|
|
127416
127417
|
// src/chatSetup.ts
|
|
127417
127418
|
import { threadOrNull } from "@ycodium-ai/plugin-api";
|
|
127418
127419
|
|
|
127420
|
+
// src/cooldown.ts
|
|
127421
|
+
var makeCooldown = (windowMs, now, maxKeys = Number.POSITIVE_INFINITY) => {
|
|
127422
|
+
const lastAt = /* @__PURE__ */ new Map();
|
|
127423
|
+
return {
|
|
127424
|
+
tryAcquire: (key) => {
|
|
127425
|
+
const current = now();
|
|
127426
|
+
const previous = lastAt.get(key);
|
|
127427
|
+
if (previous !== void 0 && current - previous < windowMs) return false;
|
|
127428
|
+
lastAt.delete(key);
|
|
127429
|
+
lastAt.set(key, current);
|
|
127430
|
+
if (lastAt.size > maxKeys) {
|
|
127431
|
+
for (const [candidate, at] of lastAt) {
|
|
127432
|
+
if (current - at >= windowMs) lastAt.delete(candidate);
|
|
127433
|
+
}
|
|
127434
|
+
while (lastAt.size > maxKeys) {
|
|
127435
|
+
const oldest = lastAt.keys().next().value;
|
|
127436
|
+
if (oldest === void 0) break;
|
|
127437
|
+
lastAt.delete(oldest);
|
|
127438
|
+
}
|
|
127439
|
+
}
|
|
127440
|
+
return true;
|
|
127441
|
+
}
|
|
127442
|
+
};
|
|
127443
|
+
};
|
|
127444
|
+
|
|
127419
127445
|
// src/projectList.ts
|
|
127420
127446
|
var PROJECT_LIST_MAX = 20;
|
|
127421
127447
|
var projectLine = (row, position) => `${String(position)}. ${row.title.replace(/\s+/g, " ").trim()}${row.current ? " \u2605" : ""}`;
|
|
@@ -127449,6 +127475,14 @@ var pickProject = (arg, listed, rows) => {
|
|
|
127449
127475
|
return prefixed.length === 1 ? prefixed[0]?.projectId ?? null : null;
|
|
127450
127476
|
};
|
|
127451
127477
|
|
|
127478
|
+
// src/messageIdentityFormat.ts
|
|
127479
|
+
var IDENTITY_VERSION = "im1";
|
|
127480
|
+
var identityPrefix = (operation) => `${IDENTITY_VERSION}-${operation}-`;
|
|
127481
|
+
var identityDigest = (id, operation) => {
|
|
127482
|
+
const prefix = identityPrefix(operation);
|
|
127483
|
+
return id.startsWith(prefix) ? id.slice(prefix.length) : void 0;
|
|
127484
|
+
};
|
|
127485
|
+
|
|
127452
127486
|
// src/sessionList.ts
|
|
127453
127487
|
var SESSION_LIST_MAX = 20;
|
|
127454
127488
|
var isSessionRunning = (status) => status === "running";
|
|
@@ -127463,7 +127497,10 @@ var ageLabel = (updatedAt, now, t) => {
|
|
|
127463
127497
|
if (hours < 24) return t("sessions.age.hours", { count: hours });
|
|
127464
127498
|
return t("sessions.age.days", { count: Math.floor(hours / 24) });
|
|
127465
127499
|
};
|
|
127466
|
-
var shortId = (threadId) => threadId.slice(
|
|
127500
|
+
var shortId = (threadId) => threadId.slice(
|
|
127501
|
+
0,
|
|
127502
|
+
(identityDigest(threadId, "thread") === void 0 ? 0 : identityPrefix("thread").length) + SESSION_ID_SHORT_LENGTH
|
|
127503
|
+
);
|
|
127467
127504
|
var sessionLine = (row, position, now, t) => {
|
|
127468
127505
|
const title = row.title.replace(/\s+/g, " ").trim() || t("sessions.untitled");
|
|
127469
127506
|
const age = ageLabel(row.updatedAt, now, t);
|
|
@@ -127504,16 +127541,21 @@ var pickSession = (arg, listed, rows) => {
|
|
|
127504
127541
|
var makeChatSetup = (core, deps) => {
|
|
127505
127542
|
const { host, t, adapterFor, commit, deliver, bindingOf: bindingOf2, hasPanel } = core;
|
|
127506
127543
|
const { projectRowsFor, listedProjectsByChat } = deps.rows;
|
|
127507
|
-
const
|
|
127508
|
-
|
|
127509
|
-
|
|
127544
|
+
const pendingMessages = makeBoundedMap(100);
|
|
127545
|
+
const projectPromptCooldown = makeCooldown(3e4, () => Date.now());
|
|
127546
|
+
const loadProjectRows = async (binding) => {
|
|
127510
127547
|
try {
|
|
127511
|
-
|
|
127548
|
+
return await projectRowsFor(chatKeyOf(binding));
|
|
127512
127549
|
} catch (error) {
|
|
127513
127550
|
host.log.warn("chat could not list the projects", { error: describeError(error) });
|
|
127514
127551
|
await deliver(binding, t("panel.loadFailed"));
|
|
127515
|
-
return;
|
|
127552
|
+
return null;
|
|
127516
127553
|
}
|
|
127554
|
+
};
|
|
127555
|
+
const promptProject = async (binding, available) => {
|
|
127556
|
+
const chatKey = chatKeyOf(binding);
|
|
127557
|
+
const rows = available ?? await loadProjectRows(binding);
|
|
127558
|
+
if (rows === null) return;
|
|
127517
127559
|
listedProjectsByChat.set(chatKey, listedProjectIds(rows));
|
|
127518
127560
|
if (rows.length > 0 && hasPanel(binding)) {
|
|
127519
127561
|
try {
|
|
@@ -127533,14 +127575,8 @@ var makeChatSetup = (core, deps) => {
|
|
|
127533
127575
|
return;
|
|
127534
127576
|
}
|
|
127535
127577
|
const chatKey = chatKeyOf(binding);
|
|
127536
|
-
|
|
127537
|
-
|
|
127538
|
-
rows = await projectRowsFor(chatKey);
|
|
127539
|
-
} catch (error) {
|
|
127540
|
-
host.log.warn("chat could not list the projects", { error: describeError(error) });
|
|
127541
|
-
await deliver(binding, t("panel.loadFailed"));
|
|
127542
|
-
return;
|
|
127543
|
-
}
|
|
127578
|
+
const rows = await loadProjectRows(binding);
|
|
127579
|
+
if (rows === null) return;
|
|
127544
127580
|
const picked = pickProject(arg, listedProjectsByChat.get(chatKey), rows);
|
|
127545
127581
|
if (picked === null) {
|
|
127546
127582
|
await deliver(binding, t("repo.notFound", { query: arg }));
|
|
@@ -127572,6 +127608,9 @@ var makeChatSetup = (core, deps) => {
|
|
|
127572
127608
|
listedProjectsByChat.delete(chatKey);
|
|
127573
127609
|
host.log.info("chat picked a project", { bot: binding.displayName, projectId: picked });
|
|
127574
127610
|
await deliver(binding, t("repo.done", { title: target2.title }));
|
|
127611
|
+
const pending = pendingMessages.get(chatKey);
|
|
127612
|
+
pendingMessages.forget((key) => key === chatKey);
|
|
127613
|
+
if (pending !== void 0) await deps.resumeMessage(binding, pending);
|
|
127575
127614
|
};
|
|
127576
127615
|
const handleGroup = async (binding, name, senderId) => {
|
|
127577
127616
|
const adapter = adapterFor(binding.platform);
|
|
@@ -127599,7 +127638,27 @@ var makeChatSetup = (core, deps) => {
|
|
|
127599
127638
|
await deliver(binding, t("group.created", { name }));
|
|
127600
127639
|
await promptProject(bindingOf2(binding, chatId));
|
|
127601
127640
|
};
|
|
127602
|
-
|
|
127641
|
+
const prepareMessage = async (binding, message) => {
|
|
127642
|
+
const chatKey = chatKeyOf(binding);
|
|
127643
|
+
const asked = message.text.trim();
|
|
127644
|
+
if (/^[1-9]\d{0,2}$/u.test(asked) && listedProjectsByChat.has(chatKey)) {
|
|
127645
|
+
await handleRepo(binding, asked);
|
|
127646
|
+
return false;
|
|
127647
|
+
}
|
|
127648
|
+
const rows = await loadProjectRows(binding);
|
|
127649
|
+
if (rows === null) return false;
|
|
127650
|
+
const only = rows.length === 1 ? rows[0] : void 0;
|
|
127651
|
+
if (only !== void 0) {
|
|
127652
|
+
commit("chat-updated", { chatKey, projectId: only.projectId });
|
|
127653
|
+
pendingMessages.forget((key) => key === chatKey);
|
|
127654
|
+
await deliver(binding, t("repo.autoSelected", { title: only.title }));
|
|
127655
|
+
return true;
|
|
127656
|
+
}
|
|
127657
|
+
pendingMessages.set(chatKey, message);
|
|
127658
|
+
if (projectPromptCooldown.tryAcquire(chatKey)) await promptProject(binding, rows);
|
|
127659
|
+
return false;
|
|
127660
|
+
};
|
|
127661
|
+
return { promptProject, handleRepo, handleGroup, prepareMessage };
|
|
127603
127662
|
};
|
|
127604
127663
|
|
|
127605
127664
|
// src/commands.ts
|
|
@@ -127692,7 +127751,6 @@ var parseChatCommand = (input) => {
|
|
|
127692
127751
|
return query === "" ? { kind: "selectTeam" } : { kind: "help" };
|
|
127693
127752
|
}
|
|
127694
127753
|
if (isThreadCommand(input.text, input.chinese)) return { kind: "selectThread" };
|
|
127695
|
-
if (/^\/[a-z][a-z0-9_-]*/i.test(trimmed)) return { kind: "help" };
|
|
127696
127754
|
if (input.chinese) {
|
|
127697
127755
|
const alias = ZH_ALIASES[trimmed];
|
|
127698
127756
|
if (alias !== void 0) return { kind: alias };
|
|
@@ -128084,30 +128142,36 @@ var startConnections = (core, inboundHandlersFor) => {
|
|
|
128084
128142
|
}
|
|
128085
128143
|
};
|
|
128086
128144
|
};
|
|
128087
|
-
const
|
|
128145
|
+
const plan = () => {
|
|
128088
128146
|
const bots = disposed ? [] : core.bots();
|
|
128089
128147
|
const wanted = /* @__PURE__ */ new Map();
|
|
128090
|
-
const
|
|
128148
|
+
const credentials2 = /* @__PURE__ */ new Map();
|
|
128091
128149
|
const duplicates = /* @__PURE__ */ new Map();
|
|
128092
128150
|
for (const bot of bots) {
|
|
128093
128151
|
const adapter = adapterFor(bot.platform);
|
|
128094
128152
|
if (adapter === void 0) continue;
|
|
128095
128153
|
const fingerprint = `${bot.platform}:${adapter.columns.map((column) => bot.values[column.key] ?? "").join(" ")}`;
|
|
128096
|
-
const duplicateOf =
|
|
128154
|
+
const duplicateOf = credentials2.get(fingerprint);
|
|
128097
128155
|
if (duplicateOf !== void 0) {
|
|
128098
128156
|
duplicates.set(bot.rowId, duplicateOf);
|
|
128099
|
-
if (statuses.get(bot.rowId)?.duplicateOf !== duplicateOf) {
|
|
128100
|
-
record3(bot.rowId, { state: "duplicate", duplicateOf });
|
|
128101
|
-
}
|
|
128102
|
-
host.log.warn("chat did not connect a bot that repeats another bot's credentials", {
|
|
128103
|
-
bot: bot.displayName,
|
|
128104
|
-
platform: bot.platform
|
|
128105
|
-
});
|
|
128106
128157
|
continue;
|
|
128107
128158
|
}
|
|
128108
|
-
|
|
128159
|
+
credentials2.set(fingerprint, bot.rowId);
|
|
128109
128160
|
wanted.set(bot.rowId, { bot, key: connectionKey(adapter, bot) });
|
|
128110
128161
|
}
|
|
128162
|
+
return { bots, wanted, duplicates };
|
|
128163
|
+
};
|
|
128164
|
+
const reconcile = () => {
|
|
128165
|
+
const { bots, wanted, duplicates } = plan();
|
|
128166
|
+
for (const bot of bots) {
|
|
128167
|
+
const duplicateOf = duplicates.get(bot.rowId);
|
|
128168
|
+
if (duplicateOf === void 0) continue;
|
|
128169
|
+
record3(bot.rowId, { state: "duplicate", duplicateOf });
|
|
128170
|
+
host.log.warn("chat did not connect a bot that repeats another bot's credentials", {
|
|
128171
|
+
bot: bot.displayName,
|
|
128172
|
+
platform: bot.platform
|
|
128173
|
+
});
|
|
128174
|
+
}
|
|
128111
128175
|
for (const [rowId, connection] of live) {
|
|
128112
128176
|
if (wanted.get(rowId)?.key === connection.key) continue;
|
|
128113
128177
|
connection.close();
|
|
@@ -128128,6 +128192,23 @@ var startConnections = (core, inboundHandlersFor) => {
|
|
|
128128
128192
|
});
|
|
128129
128193
|
return {
|
|
128130
128194
|
reconcile,
|
|
128195
|
+
reconnect: (rowId) => {
|
|
128196
|
+
live.get(rowId)?.close();
|
|
128197
|
+
live.delete(rowId);
|
|
128198
|
+
const { wanted, duplicates } = plan();
|
|
128199
|
+
const duplicateOf = duplicates.get(rowId);
|
|
128200
|
+
if (duplicateOf !== void 0) {
|
|
128201
|
+
record3(rowId, { state: "duplicate", duplicateOf });
|
|
128202
|
+
return { started: false, reason: "duplicate" };
|
|
128203
|
+
}
|
|
128204
|
+
const target2 = wanted.get(rowId);
|
|
128205
|
+
if (target2 === void 0) {
|
|
128206
|
+
remove(rowId);
|
|
128207
|
+
return { started: false, reason: disposed ? "inactive" : "notConfigured" };
|
|
128208
|
+
}
|
|
128209
|
+
live.set(rowId, open(target2.bot, target2.key));
|
|
128210
|
+
return { started: true };
|
|
128211
|
+
},
|
|
128131
128212
|
subscribe: (listener) => {
|
|
128132
128213
|
if (!disposed) listeners.add(listener);
|
|
128133
128214
|
listener(Object.fromEntries(statuses));
|
|
@@ -128396,7 +128477,7 @@ var makeImCore = (input) => {
|
|
|
128396
128477
|
init: emptyState,
|
|
128397
128478
|
apply: foldPluginEvent(input.pluginId)
|
|
128398
128479
|
});
|
|
128399
|
-
const
|
|
128480
|
+
const chatReady = stateHandle.get().then(
|
|
128400
128481
|
(restored) => {
|
|
128401
128482
|
state = restored;
|
|
128402
128483
|
},
|
|
@@ -128404,6 +128485,7 @@ var makeImCore = (input) => {
|
|
|
128404
128485
|
host.log.warn("chat could not restore its saved state", { error: describeError(error) });
|
|
128405
128486
|
}
|
|
128406
128487
|
);
|
|
128488
|
+
const ready = Promise.all([chatReady, input.inboundReady]).then(() => void 0);
|
|
128407
128489
|
let persisting = Promise.resolve();
|
|
128408
128490
|
const commit = (name, data) => {
|
|
128409
128491
|
state = foldImEvent(state, name, data);
|
|
@@ -128936,79 +129018,51 @@ var makeInboundAttachments = (core) => {
|
|
|
128936
129018
|
// src/inbound.ts
|
|
128937
129019
|
import { projectOrNull } from "@ycodium-ai/plugin-api";
|
|
128938
129020
|
|
|
128939
|
-
// src/
|
|
128940
|
-
var
|
|
128941
|
-
|
|
128942
|
-
|
|
128943
|
-
|
|
128944
|
-
|
|
128945
|
-
|
|
128946
|
-
|
|
128947
|
-
|
|
128948
|
-
|
|
128949
|
-
|
|
128950
|
-
|
|
128951
|
-
|
|
128952
|
-
|
|
128953
|
-
}
|
|
128954
|
-
};
|
|
128955
|
-
};
|
|
128956
|
-
|
|
128957
|
-
// src/cooldown.ts
|
|
128958
|
-
var makeCooldown = (windowMs, now, maxKeys = Number.POSITIVE_INFINITY) => {
|
|
128959
|
-
const lastAt = /* @__PURE__ */ new Map();
|
|
128960
|
-
return {
|
|
128961
|
-
tryAcquire: (key) => {
|
|
128962
|
-
const current = now();
|
|
128963
|
-
const previous = lastAt.get(key);
|
|
128964
|
-
if (previous !== void 0 && current - previous < windowMs) return false;
|
|
128965
|
-
lastAt.delete(key);
|
|
128966
|
-
lastAt.set(key, current);
|
|
128967
|
-
if (lastAt.size > maxKeys) {
|
|
128968
|
-
for (const [candidate, at] of lastAt) {
|
|
128969
|
-
if (current - at >= windowMs) lastAt.delete(candidate);
|
|
128970
|
-
}
|
|
128971
|
-
while (lastAt.size > maxKeys) {
|
|
128972
|
-
const oldest = lastAt.keys().next().value;
|
|
128973
|
-
if (oldest === void 0) break;
|
|
128974
|
-
lastAt.delete(oldest);
|
|
128975
|
-
}
|
|
128976
|
-
}
|
|
128977
|
-
return true;
|
|
128978
|
-
}
|
|
129021
|
+
// src/greetings.ts
|
|
129022
|
+
var GREETING_COOLDOWN_MS = 12 * 60 * 6e4;
|
|
129023
|
+
var GREETING_KEY_LIMIT = 1e3;
|
|
129024
|
+
var makeGreetings = (core, enqueueForChat, now) => {
|
|
129025
|
+
const cooldown = makeCooldown(GREETING_COOLDOWN_MS, now, GREETING_KEY_LIMIT);
|
|
129026
|
+
return (rowId, chatId, senderId) => {
|
|
129027
|
+
const bot = core.bots().find((candidate) => candidate.rowId === rowId);
|
|
129028
|
+
if (bot === void 0 || !isAllowedSender(bot, senderId)) return;
|
|
129029
|
+
if (!cooldown.tryAcquire(`${rowId}:${senderId}`)) return;
|
|
129030
|
+
enqueueForChat(chatKeyOf({ rowId, chatId }), async () => {
|
|
129031
|
+
const current = core.bots().find((candidate) => candidate.rowId === rowId);
|
|
129032
|
+
if (current === void 0 || !isAllowedSender(current, senderId)) return;
|
|
129033
|
+
await core.deliver(core.bindingOf(current, chatId), core.t("reply.welcome"));
|
|
129034
|
+
});
|
|
128979
129035
|
};
|
|
128980
129036
|
};
|
|
128981
129037
|
|
|
128982
129038
|
// src/inbound.ts
|
|
128983
|
-
var SEEN_MESSAGE_LIMIT = 256;
|
|
128984
129039
|
var ARCHIVED_REPLY_COOLDOWN_MS = 10 * 6e4;
|
|
128985
129040
|
var UNSUPPORTED_REPLY_COOLDOWN_MS = 6e4;
|
|
128986
129041
|
var NOT_ALLOWED_REPLY_COOLDOWN_MS = 10 * 6e4;
|
|
128987
129042
|
var NOT_ALLOWED_REPLY_KEY_LIMIT = 1e3;
|
|
128988
|
-
var PROJECT_PROMPT_COOLDOWN_MS = 3e4;
|
|
128989
129043
|
var makeInbound = (core, deps) => {
|
|
128990
129044
|
const { host, t, adapterFor, commit, deliver, bindingOf: bindingOf2, bots } = core;
|
|
128991
129045
|
const { enqueueForChat } = deps;
|
|
129046
|
+
const now = deps.now ?? (() => Date.now());
|
|
129047
|
+
const greet = makeGreetings(core, enqueueForChat, now);
|
|
128992
129048
|
const { handleCommand, targetOf } = deps.commands;
|
|
128993
|
-
const {
|
|
128994
|
-
const { listedProjectsByChat } = deps.rows;
|
|
129049
|
+
const { prepareMessage } = deps.chatSetup;
|
|
128995
129050
|
const { questionFlow, handleButtonClick } = deps.interactions;
|
|
128996
129051
|
const { answerByToken } = deps.approvals;
|
|
128997
129052
|
const { handlePanelClick } = deps.panelWiring;
|
|
128998
129053
|
const { startTurn, receiveAttachmentsOnly, hasWaitingAttachments } = deps.turns;
|
|
128999
129054
|
const { startTeamGoal } = deps.teams;
|
|
129000
129055
|
const { markWorking } = deps.acknowledgements;
|
|
129001
|
-
const archivedReplyCooldown = makeCooldown(ARCHIVED_REPLY_COOLDOWN_MS,
|
|
129002
|
-
const unsupportedReplyCooldown = makeCooldown(UNSUPPORTED_REPLY_COOLDOWN_MS,
|
|
129056
|
+
const archivedReplyCooldown = makeCooldown(ARCHIVED_REPLY_COOLDOWN_MS, now);
|
|
129057
|
+
const unsupportedReplyCooldown = makeCooldown(UNSUPPORTED_REPLY_COOLDOWN_MS, now);
|
|
129003
129058
|
const notAllowedReplyCooldown = makeCooldown(
|
|
129004
129059
|
NOT_ALLOWED_REPLY_COOLDOWN_MS,
|
|
129005
|
-
|
|
129060
|
+
now,
|
|
129006
129061
|
NOT_ALLOWED_REPLY_KEY_LIMIT
|
|
129007
129062
|
);
|
|
129008
|
-
const
|
|
129009
|
-
const
|
|
129010
|
-
const
|
|
129011
|
-
const claimCallback = (rowId, callbackId) => callbackId === void 0 || callbackId === "" || !alreadyHandled(`c:${rowId}:${callbackId}`);
|
|
129063
|
+
const { inboundState } = deps;
|
|
129064
|
+
const alreadyHandled = (rowId, key) => !inboundState.claim(rowId, key);
|
|
129065
|
+
const claimCallback = (rowId, callbackId) => callbackId === void 0 || callbackId === "" || !alreadyHandled(rowId, `c:${callbackId}`);
|
|
129012
129066
|
const turnAwayStranger = (target2, sender) => {
|
|
129013
129067
|
host.log.warn("chat ignored a message from someone not on the allow list", {
|
|
129014
129068
|
bot: target2.displayName,
|
|
@@ -129039,6 +129093,7 @@ var makeInbound = (core, deps) => {
|
|
|
129039
129093
|
commit("chat-aliased", { rowId: bot.rowId, alias, chatId });
|
|
129040
129094
|
};
|
|
129041
129095
|
const settleChat = (bot, message) => {
|
|
129096
|
+
message = { ...message, originalChatId: message.originalChatId ?? message.chatId };
|
|
129042
129097
|
if (message.chatAlias === void 0) return message;
|
|
129043
129098
|
const known = chatAliasOf(core.state, bot.rowId, message.chatAlias);
|
|
129044
129099
|
if (known === void 0) noteChatAlias(bot, message.chatAlias, message.chatId);
|
|
@@ -129048,6 +129103,61 @@ var makeInbound = (core, deps) => {
|
|
|
129048
129103
|
const threadId = await startTurn(speaker, message);
|
|
129049
129104
|
if (threadId !== null) await markWorking(speaker, threadId, message.messageId);
|
|
129050
129105
|
};
|
|
129106
|
+
const resumeMessage = async (current, message) => {
|
|
129107
|
+
inheritFromParent(current, message.chatId, message.parentChatId);
|
|
129108
|
+
let target2 = {
|
|
129109
|
+
...bindingOf2(current, message.chatId),
|
|
129110
|
+
reply: anchorOfMessage(adapterFor(current.platform), message)
|
|
129111
|
+
};
|
|
129112
|
+
const chatKey = chatKeyOf(target2);
|
|
129113
|
+
if (await handleCommand(target2, message.text, message.senderId)) return;
|
|
129114
|
+
if (target2.projectId === "") {
|
|
129115
|
+
if (!await prepareMessage(target2, message)) return;
|
|
129116
|
+
target2 = { ...bindingOf2(current, message.chatId), reply: target2.reply };
|
|
129117
|
+
}
|
|
129118
|
+
const boundThread = chatOf(core.state, chatKey).threadId;
|
|
129119
|
+
if (boundThread !== null && message.text !== "" && await questionFlow.answerByText(chatKey, boundThread, message.text)) {
|
|
129120
|
+
return;
|
|
129121
|
+
}
|
|
129122
|
+
const project = await projectOrNull(host.projects, target2.projectId);
|
|
129123
|
+
if (project === null || project.archivedAt != null) {
|
|
129124
|
+
if (project === null) {
|
|
129125
|
+
host.log.warn("chat cannot start work: the chat's project is gone", {
|
|
129126
|
+
bot: target2.displayName,
|
|
129127
|
+
projectId: target2.projectId
|
|
129128
|
+
});
|
|
129129
|
+
}
|
|
129130
|
+
if (archivedReplyCooldown.tryAcquire(chatKey)) {
|
|
129131
|
+
await deliver(
|
|
129132
|
+
target2,
|
|
129133
|
+
project === null ? t("reply.projectMissing") : t("reply.projectArchived", { title: project.title })
|
|
129134
|
+
);
|
|
129135
|
+
}
|
|
129136
|
+
return;
|
|
129137
|
+
}
|
|
129138
|
+
const routed = targetOf(target2);
|
|
129139
|
+
const attachmentsOnly = message.text.trim() === "" && message.resources.length > 0;
|
|
129140
|
+
if (routed.kind === "team") {
|
|
129141
|
+
if (message.resources.length > 0) await deliver(target2, t("reply.attachmentsNoTeam"));
|
|
129142
|
+
if (attachmentsOnly) return;
|
|
129143
|
+
const leadThreadId = await startTeamGoal(target2, message);
|
|
129144
|
+
if (leadThreadId !== null) await markWorking(target2, leadThreadId, message.messageId);
|
|
129145
|
+
return;
|
|
129146
|
+
}
|
|
129147
|
+
if (attachmentsOnly) {
|
|
129148
|
+
await receiveAttachmentsOnly(target2, message);
|
|
129149
|
+
return;
|
|
129150
|
+
}
|
|
129151
|
+
const branchChatId = hasWaitingAttachments(chatKey) ? void 0 : message.branchChatId;
|
|
129152
|
+
if (branchChatId === void 0) {
|
|
129153
|
+
await openTurn(target2, message);
|
|
129154
|
+
return;
|
|
129155
|
+
}
|
|
129156
|
+
enqueueForChat(chatKeyOf({ rowId: current.rowId, chatId: branchChatId }), async () => {
|
|
129157
|
+
inheritFromParent(current, branchChatId, message.chatId);
|
|
129158
|
+
await openTurn({ ...bindingOf2(current, branchChatId), reply: target2.reply }, message);
|
|
129159
|
+
});
|
|
129160
|
+
};
|
|
129051
129161
|
const inboundHandlersFor = (bot) => ({
|
|
129052
129162
|
onError: (message) => host.log.warn("chat inbound connection failed", {
|
|
129053
129163
|
platform: bot.platform,
|
|
@@ -129065,12 +129175,13 @@ var makeInbound = (core, deps) => {
|
|
|
129065
129175
|
},
|
|
129066
129176
|
noteChatAlias: (alias, chatId) => noteChatAlias(bot, alias, chatId),
|
|
129067
129177
|
knowsChatAlias: (alias) => chatAliasOf(core.state, bot.rowId, alias) !== void 0,
|
|
129178
|
+
onGreeting: (chatId, senderId) => greet(bot.rowId, chatId, senderId),
|
|
129068
129179
|
onMessage: (received) => {
|
|
129069
129180
|
const current = currentBot(bot.rowId);
|
|
129070
129181
|
if (current === void 0) return;
|
|
129071
129182
|
const message = settleChat(current, received);
|
|
129072
129183
|
const arrived = bindingOf2(current, message.chatId);
|
|
129073
|
-
if (alreadyHandled(`m:${
|
|
129184
|
+
if (alreadyHandled(current.rowId, `m:${message.chatId}:${message.messageId}`)) return;
|
|
129074
129185
|
if (message.chatKind !== void 0 && chatOf(core.state, chatKeyOf(arrived)).chatKind !== message.chatKind) {
|
|
129075
129186
|
commit("chat-updated", { chatKey: chatKeyOf(arrived), chatKind: message.chatKind });
|
|
129076
129187
|
}
|
|
@@ -129084,7 +129195,7 @@ var makeInbound = (core, deps) => {
|
|
|
129084
129195
|
if (!unsupportedReplyCooldown.tryAcquire(chatKeyOf(arrived))) return;
|
|
129085
129196
|
await deliver(
|
|
129086
129197
|
arrived,
|
|
129087
|
-
unsupported === "folder" ? t("reply.unsupportedFolder") : t("reply.unsupportedType", { type: unsupported })
|
|
129198
|
+
unsupported === "folder" ? t("reply.unsupportedFolder") : unsupported === "voice" ? t("reply.unsupportedVoice") : t("reply.unsupportedType", { type: unsupported })
|
|
129088
129199
|
);
|
|
129089
129200
|
});
|
|
129090
129201
|
return;
|
|
@@ -129097,69 +129208,129 @@ var makeInbound = (core, deps) => {
|
|
|
129097
129208
|
});
|
|
129098
129209
|
return;
|
|
129099
129210
|
}
|
|
129100
|
-
enqueueForChat(chatKeyOf(arrived),
|
|
129101
|
-
|
|
129102
|
-
|
|
129103
|
-
|
|
129104
|
-
|
|
129105
|
-
|
|
129106
|
-
|
|
129107
|
-
|
|
129108
|
-
|
|
129109
|
-
|
|
129110
|
-
|
|
129111
|
-
|
|
129112
|
-
|
|
129113
|
-
|
|
129114
|
-
|
|
129115
|
-
|
|
129116
|
-
|
|
129117
|
-
|
|
129118
|
-
|
|
129119
|
-
|
|
129211
|
+
enqueueForChat(chatKeyOf(arrived), () => resumeMessage(current, message));
|
|
129212
|
+
}
|
|
129213
|
+
});
|
|
129214
|
+
return { inboundHandlersFor, resumeMessage };
|
|
129215
|
+
};
|
|
129216
|
+
|
|
129217
|
+
// src/inboundStore.ts
|
|
129218
|
+
import * as NodeFSP from "node:fs/promises";
|
|
129219
|
+
import * as NodePath from "node:path";
|
|
129220
|
+
var SEEN_LIMIT = 1e3;
|
|
129221
|
+
var fileIo = {
|
|
129222
|
+
readFile: (path) => NodeFSP.readFile(path, "utf8"),
|
|
129223
|
+
mkdir: async (path) => {
|
|
129224
|
+
await NodeFSP.mkdir(path, { recursive: true });
|
|
129225
|
+
},
|
|
129226
|
+
writeFile: (path, contents) => NodeFSP.writeFile(path, contents, "utf8"),
|
|
129227
|
+
rename: (from, to) => NodeFSP.rename(from, to)
|
|
129228
|
+
};
|
|
129229
|
+
var isRecord2 = (value2) => typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
129230
|
+
var decode = (contents) => {
|
|
129231
|
+
const value2 = JSON.parse(contents);
|
|
129232
|
+
if (!isRecord2(value2) || value2["version"] !== 1 || !isRecord2(value2["bots"]))
|
|
129233
|
+
throw new Error("invalid inbound store");
|
|
129234
|
+
const bots = /* @__PURE__ */ new Map();
|
|
129235
|
+
for (const [rowId, row] of Object.entries(value2["bots"])) {
|
|
129236
|
+
if (!isRecord2(row) || !Array.isArray(row["seenIds"]) || !row["seenIds"].every((id) => typeof id === "string"))
|
|
129237
|
+
throw new Error("invalid inbound bot row");
|
|
129238
|
+
const telegram = row["telegram"];
|
|
129239
|
+
if (telegram !== void 0 && (!isRecord2(telegram) || typeof telegram["tokenHash"] !== "string" || telegram["tokenHash"] === "" || typeof telegram["offset"] !== "number" || !Number.isSafeInteger(telegram["offset"]) || telegram["offset"] < 0))
|
|
129240
|
+
throw new Error("invalid telegram cursor");
|
|
129241
|
+
bots.set(rowId, {
|
|
129242
|
+
seenIds: row["seenIds"].slice(-SEEN_LIMIT),
|
|
129243
|
+
...telegram === void 0 ? {} : {
|
|
129244
|
+
telegram: {
|
|
129245
|
+
tokenHash: telegram["tokenHash"],
|
|
129246
|
+
offset: telegram["offset"]
|
|
129120
129247
|
}
|
|
129121
|
-
|
|
129122
|
-
|
|
129123
|
-
|
|
129124
|
-
|
|
129125
|
-
|
|
129126
|
-
|
|
129127
|
-
|
|
129128
|
-
|
|
129129
|
-
|
|
129130
|
-
|
|
129131
|
-
|
|
129132
|
-
|
|
129133
|
-
|
|
129248
|
+
}
|
|
129249
|
+
});
|
|
129250
|
+
}
|
|
129251
|
+
return bots;
|
|
129252
|
+
};
|
|
129253
|
+
var makeInboundStore = (input) => {
|
|
129254
|
+
const io = input.io ?? fileIo;
|
|
129255
|
+
const path = NodePath.join(input.dataDir, "inbound.json");
|
|
129256
|
+
const temporary = `${path}.tmp`;
|
|
129257
|
+
let bots = /* @__PURE__ */ new Map();
|
|
129258
|
+
let dirty = false;
|
|
129259
|
+
let writing;
|
|
129260
|
+
const warn = (message, error) => input.log.warn(message, { error: describeError(error) });
|
|
129261
|
+
const ready = io.readFile(path).then(
|
|
129262
|
+
(contents) => {
|
|
129263
|
+
try {
|
|
129264
|
+
bots = decode(contents);
|
|
129265
|
+
} catch (error) {
|
|
129266
|
+
warn("chat could not restore inbound state", error);
|
|
129267
|
+
}
|
|
129268
|
+
},
|
|
129269
|
+
(error) => {
|
|
129270
|
+
if (!isRecord2(error) || error["code"] !== "ENOENT")
|
|
129271
|
+
warn("chat could not restore inbound state", error);
|
|
129272
|
+
}
|
|
129273
|
+
);
|
|
129274
|
+
const write = () => {
|
|
129275
|
+
writing ??= Promise.resolve().then(async () => {
|
|
129276
|
+
try {
|
|
129277
|
+
while (dirty) {
|
|
129278
|
+
dirty = false;
|
|
129279
|
+
const contents = JSON.stringify({ version: 1, bots: Object.fromEntries(bots) });
|
|
129280
|
+
try {
|
|
129281
|
+
await io.mkdir(input.dataDir);
|
|
129282
|
+
await io.writeFile(temporary, contents);
|
|
129283
|
+
await io.rename(temporary, path);
|
|
129284
|
+
} catch (error) {
|
|
129285
|
+
dirty = true;
|
|
129286
|
+
warn("chat could not save inbound state", error);
|
|
129287
|
+
break;
|
|
129134
129288
|
}
|
|
129135
|
-
return;
|
|
129136
|
-
}
|
|
129137
|
-
const routed = targetOf(target2);
|
|
129138
|
-
const attachmentsOnly = message.text.trim() === "" && message.resources.length > 0;
|
|
129139
|
-
if (routed.kind === "team") {
|
|
129140
|
-
if (message.resources.length > 0) await deliver(target2, t("reply.attachmentsNoTeam"));
|
|
129141
|
-
if (attachmentsOnly) return;
|
|
129142
|
-
const leadThreadId = await startTeamGoal(target2, message);
|
|
129143
|
-
if (leadThreadId !== null) await markWorking(target2, leadThreadId, message.messageId);
|
|
129144
|
-
return;
|
|
129145
|
-
}
|
|
129146
|
-
if (attachmentsOnly) {
|
|
129147
|
-
await receiveAttachmentsOnly(target2, message);
|
|
129148
|
-
return;
|
|
129149
|
-
}
|
|
129150
|
-
const branchChatId = hasWaitingAttachments(chatKey) ? void 0 : message.branchChatId;
|
|
129151
|
-
if (branchChatId === void 0) {
|
|
129152
|
-
await openTurn(target2, message);
|
|
129153
|
-
return;
|
|
129154
129289
|
}
|
|
129155
|
-
|
|
129156
|
-
|
|
129157
|
-
|
|
129158
|
-
|
|
129159
|
-
|
|
129290
|
+
} finally {
|
|
129291
|
+
writing = void 0;
|
|
129292
|
+
}
|
|
129293
|
+
});
|
|
129294
|
+
return writing;
|
|
129295
|
+
};
|
|
129296
|
+
const changed = () => {
|
|
129297
|
+
dirty = true;
|
|
129298
|
+
void write();
|
|
129299
|
+
};
|
|
129300
|
+
const rowOf = (rowId) => {
|
|
129301
|
+
let row = bots.get(rowId);
|
|
129302
|
+
if (row === void 0) {
|
|
129303
|
+
row = { seenIds: [] };
|
|
129304
|
+
bots.set(rowId, row);
|
|
129160
129305
|
}
|
|
129161
|
-
|
|
129162
|
-
|
|
129306
|
+
return row;
|
|
129307
|
+
};
|
|
129308
|
+
return {
|
|
129309
|
+
ready,
|
|
129310
|
+
claim: (rowId, id) => {
|
|
129311
|
+
const row = rowOf(rowId);
|
|
129312
|
+
if (row.seenIds.includes(id)) return false;
|
|
129313
|
+
row.seenIds.push(id);
|
|
129314
|
+
if (row.seenIds.length > SEEN_LIMIT) row.seenIds.shift();
|
|
129315
|
+
changed();
|
|
129316
|
+
return true;
|
|
129317
|
+
},
|
|
129318
|
+
readCursor: (rowId, tokenHash) => {
|
|
129319
|
+
const cursor = bots.get(rowId)?.telegram;
|
|
129320
|
+
return cursor?.tokenHash === tokenHash ? cursor.offset : void 0;
|
|
129321
|
+
},
|
|
129322
|
+
saveCursor: (rowId, tokenHash, offset) => {
|
|
129323
|
+
const row = rowOf(rowId);
|
|
129324
|
+
if (row.telegram?.tokenHash === tokenHash && row.telegram.offset === offset) return;
|
|
129325
|
+
if (row.telegram?.tokenHash !== tokenHash) row.seenIds = [];
|
|
129326
|
+
row.telegram = { tokenHash, offset };
|
|
129327
|
+
changed();
|
|
129328
|
+
},
|
|
129329
|
+
flush: async () => {
|
|
129330
|
+
await ready;
|
|
129331
|
+
if (dirty || writing) await write();
|
|
129332
|
+
}
|
|
129333
|
+
};
|
|
129163
129334
|
};
|
|
129164
129335
|
|
|
129165
129336
|
// src/interactions.ts
|
|
@@ -129925,6 +130096,8 @@ var consolePath = {
|
|
|
129925
130096
|
var APP_EVENTS = ["im.message.receive_v1"];
|
|
129926
130097
|
var CARD_CALLBACKS = ["card.action.trigger"];
|
|
129927
130098
|
var SCOPES = [
|
|
130099
|
+
// 让「补全权限」查询应用自身已开通的权限,并列出缺项。
|
|
130100
|
+
"application:application:self_manage",
|
|
129928
130101
|
// 收发消息、改卡片、读消息。
|
|
129929
130102
|
"im:message",
|
|
129930
130103
|
// 群里 @ 机器人的消息(入站的主路径)。
|
|
@@ -129949,7 +130122,7 @@ var SCOPES = [
|
|
|
129949
130122
|
// 「在弄了」表情回执(`larkApi.ts` 的 `addReaction`)。unconfirmed
|
|
129950
130123
|
"im:message.reactions:write_only"
|
|
129951
130124
|
];
|
|
129952
|
-
var isTenantScope = (name) => name.startsWith("im:");
|
|
130125
|
+
var isTenantScope = (name) => name.startsWith("im:") || name === "application:application:self_manage";
|
|
129953
130126
|
var appMarker = (environmentId) => `[ycodium:${environmentId.slice(0, 8)}]`;
|
|
129954
130127
|
var DEFAULT_APP_NAME = "Ycodium Agent";
|
|
129955
130128
|
var DEFAULT_APP_DESCRIPTION = "Ycodium chat bot";
|
|
@@ -130000,6 +130173,16 @@ var makeFlowItemSchema = (shape) => ({
|
|
|
130000
130173
|
},
|
|
130001
130174
|
fills: shape.fills,
|
|
130002
130175
|
messageKey: { type: "string" },
|
|
130176
|
+
tone: { type: "string", enum: ["caution"] },
|
|
130177
|
+
link: {
|
|
130178
|
+
type: "object",
|
|
130179
|
+
additionalProperties: false,
|
|
130180
|
+
required: ["url", "labelKey"],
|
|
130181
|
+
properties: {
|
|
130182
|
+
url: { type: "string", pattern: "^https://" },
|
|
130183
|
+
labelKey: { type: "string" }
|
|
130184
|
+
}
|
|
130185
|
+
},
|
|
130003
130186
|
warnings: {
|
|
130004
130187
|
type: "array",
|
|
130005
130188
|
items: {
|
|
@@ -130013,6 +130196,7 @@ var makeFlowItemSchema = (shape) => ({
|
|
|
130013
130196
|
}
|
|
130014
130197
|
},
|
|
130015
130198
|
reasonKey: { type: "string" },
|
|
130199
|
+
noteKey: { type: "string" },
|
|
130016
130200
|
detail: { type: "string" }
|
|
130017
130201
|
},
|
|
130018
130202
|
oneOf: [
|
|
@@ -131656,8 +131840,8 @@ var resourceOf = (element, index) => {
|
|
|
131656
131840
|
}
|
|
131657
131841
|
return null;
|
|
131658
131842
|
};
|
|
131659
|
-
var placeholderOf = (
|
|
131660
|
-
var serializeElement = (element,
|
|
131843
|
+
var placeholderOf = (resource, index) => `<${resource.media} ${String(index)}>`;
|
|
131844
|
+
var serializeElement = (element, resourceText, botIds) => {
|
|
131661
131845
|
switch (element.tag) {
|
|
131662
131846
|
case "text":
|
|
131663
131847
|
return styledText(element.text ?? "", element.style);
|
|
@@ -131680,12 +131864,12 @@ ${element.text ?? ""}
|
|
|
131680
131864
|
case "img":
|
|
131681
131865
|
case "media":
|
|
131682
131866
|
case "file":
|
|
131683
|
-
return
|
|
131867
|
+
return resourceText ?? "";
|
|
131684
131868
|
default:
|
|
131685
131869
|
return "";
|
|
131686
131870
|
}
|
|
131687
131871
|
};
|
|
131688
|
-
var serializePost = (content, botIds = /* @__PURE__ */ new Set()) => {
|
|
131872
|
+
var serializePost = (content, botIds = /* @__PURE__ */ new Set(), renderResource = placeholderOf) => {
|
|
131689
131873
|
let parsed;
|
|
131690
131874
|
try {
|
|
131691
131875
|
parsed = JSON.parse(content);
|
|
@@ -131694,31 +131878,31 @@ var serializePost = (content, botIds = /* @__PURE__ */ new Set()) => {
|
|
|
131694
131878
|
}
|
|
131695
131879
|
const elements = (parsed.content ?? []).flat();
|
|
131696
131880
|
const resources = [];
|
|
131697
|
-
const
|
|
131881
|
+
const resourceTextOf = /* @__PURE__ */ new Map();
|
|
131698
131882
|
for (const element of elements) {
|
|
131699
131883
|
const resource = resourceOf(element, resources.length + 1);
|
|
131700
131884
|
if (resource === null) continue;
|
|
131701
131885
|
resources.push(resource);
|
|
131702
|
-
|
|
131886
|
+
resourceTextOf.set(element, renderResource(resource, resources.length));
|
|
131703
131887
|
}
|
|
131704
131888
|
const md2 = parsed.md;
|
|
131705
131889
|
if (typeof md2 === "string" && md2.trim() !== "") {
|
|
131706
|
-
const
|
|
131890
|
+
const resourceTextByKey = /* @__PURE__ */ new Map();
|
|
131707
131891
|
for (const element of elements) {
|
|
131708
|
-
const
|
|
131892
|
+
const resourceText = resourceTextOf.get(element);
|
|
131709
131893
|
const key = element.tag === "img" ? element.image_key : element.file_key;
|
|
131710
|
-
if (
|
|
131711
|
-
|
|
131894
|
+
if (resourceText !== void 0 && nonEmpty4(key)) {
|
|
131895
|
+
resourceTextByKey.set(key, resourceText);
|
|
131712
131896
|
}
|
|
131713
131897
|
}
|
|
131714
131898
|
const text5 = md2.replace(
|
|
131715
131899
|
/!\[(?:img|video|file)\]\(([^)]+)\)/g,
|
|
131716
|
-
(whole, rawKey) =>
|
|
131900
|
+
(whole, rawKey) => resourceTextByKey.get(rawKey.trim()) ?? whole
|
|
131717
131901
|
);
|
|
131718
131902
|
return { text: text5, resources };
|
|
131719
131903
|
}
|
|
131720
131904
|
const paragraphs = (parsed.content ?? []).map(
|
|
131721
|
-
(row) => row.map((element) => serializeElement(element,
|
|
131905
|
+
(row) => row.map((element) => serializeElement(element, resourceTextOf.get(element), botIds)).join("")
|
|
131722
131906
|
);
|
|
131723
131907
|
const text4 = paragraphs.map((line) => line.trim()).filter((line) => line !== "").join("\n");
|
|
131724
131908
|
if (text4 === "" && resources.length === 0) return void 0;
|
|
@@ -131859,6 +132043,7 @@ var LARK_TEXT_MAX_CHARS = 3500;
|
|
|
131859
132043
|
var LarkApiError = class extends Error {
|
|
131860
132044
|
operation;
|
|
131861
132045
|
code;
|
|
132046
|
+
msg;
|
|
131862
132047
|
status;
|
|
131863
132048
|
retryAfterMs;
|
|
131864
132049
|
constructor(operation, code2, message, http = {}) {
|
|
@@ -131866,11 +132051,13 @@ var LarkApiError = class extends Error {
|
|
|
131866
132051
|
this.name = "LarkApiError";
|
|
131867
132052
|
this.operation = operation;
|
|
131868
132053
|
this.code = code2;
|
|
132054
|
+
this.msg = message;
|
|
131869
132055
|
this.status = http.status;
|
|
131870
132056
|
this.retryAfterMs = http.retryAfterMs;
|
|
131871
132057
|
}
|
|
131872
132058
|
};
|
|
131873
|
-
var
|
|
132059
|
+
var isLarkPermissionCode = (code2) => code2 === 99991672 || code2 === 99991679;
|
|
132060
|
+
var isLarkPermissionError = (error) => error instanceof LarkApiError && (isLarkPermissionCode(error.code) || error.status === 401 || error.status === 403);
|
|
131874
132061
|
var LARK_RATE_LIMITED = 99991400;
|
|
131875
132062
|
var isReplyRejected = (error) => error instanceof LarkApiError && error.status === void 0 && error.code !== LARK_RATE_LIMITED;
|
|
131876
132063
|
var larkSendVerdict = (error) => {
|
|
@@ -131885,14 +132072,20 @@ var larkSendVerdict = (error) => {
|
|
|
131885
132072
|
};
|
|
131886
132073
|
var readEnvelope = async (operation, response) => {
|
|
131887
132074
|
if (!response.ok) {
|
|
131888
|
-
await response.
|
|
131889
|
-
|
|
131890
|
-
|
|
131891
|
-
|
|
131892
|
-
|
|
131893
|
-
|
|
131894
|
-
|
|
131895
|
-
|
|
132075
|
+
const body2 = await response.json().catch(() => void 0);
|
|
132076
|
+
const envelope2 = typeof body2 === "object" && body2 !== null ? body2 : void 0;
|
|
132077
|
+
throw new LarkApiError(
|
|
132078
|
+
operation,
|
|
132079
|
+
typeof envelope2?.code === "number" ? envelope2.code : void 0,
|
|
132080
|
+
typeof envelope2?.msg === "string" ? envelope2.msg : `HTTP ${String(response.status)}`,
|
|
132081
|
+
{
|
|
132082
|
+
status: response.status,
|
|
132083
|
+
retryAfterMs: parseRetryAfterMs(
|
|
132084
|
+
response.headers.get("retry-after") ?? response.headers.get("x-ogw-ratelimit-reset"),
|
|
132085
|
+
Date.now()
|
|
132086
|
+
) ?? void 0
|
|
132087
|
+
}
|
|
132088
|
+
);
|
|
131896
132089
|
}
|
|
131897
132090
|
const body = await response.json();
|
|
131898
132091
|
const envelope = body;
|
|
@@ -131913,6 +132106,19 @@ var fetchTenantToken = async (input) => {
|
|
|
131913
132106
|
}
|
|
131914
132107
|
return { token: body.tenant_access_token, expiresInSeconds: body.expire ?? 0 };
|
|
131915
132108
|
};
|
|
132109
|
+
var fetchAppTenantScopes = async (input) => {
|
|
132110
|
+
const response = await input.http(`${LARK_BASE}/application/v6/applications/me?lang=zh_cn`, {
|
|
132111
|
+
headers: { authorization: `Bearer ${input.token}` }
|
|
132112
|
+
});
|
|
132113
|
+
const body = await readEnvelope("applications/me", response);
|
|
132114
|
+
const scopes = body.data?.app?.scopes;
|
|
132115
|
+
if (!Array.isArray(scopes)) return void 0;
|
|
132116
|
+
if (scopes.some(
|
|
132117
|
+
(entry) => entry === null || typeof entry !== "object" || typeof entry.scope !== "string" || !Array.isArray(entry.token_types)
|
|
132118
|
+
))
|
|
132119
|
+
return void 0;
|
|
132120
|
+
return scopes.filter((entry) => entry.token_types.includes("tenant")).map((entry) => entry.scope);
|
|
132121
|
+
};
|
|
131916
132122
|
var postMessage = async (input) => {
|
|
131917
132123
|
const headers = {
|
|
131918
132124
|
authorization: `Bearer ${input.token}`,
|
|
@@ -131950,16 +132156,31 @@ var postMessage = async (input) => {
|
|
|
131950
132156
|
});
|
|
131951
132157
|
return sent?.message_id ?? null;
|
|
131952
132158
|
};
|
|
131953
|
-
var
|
|
132159
|
+
var readMessageItems = async (input) => {
|
|
131954
132160
|
const response = await input.http(
|
|
131955
|
-
`${LARK_BASE}/im/v1/messages/${encodeURIComponent(input.messageId)}`,
|
|
132161
|
+
`${LARK_BASE}/im/v1/messages/${encodeURIComponent(input.messageId)}${input.withSenderName ? "?with_sender_name=true" : ""}`,
|
|
131956
132162
|
{
|
|
131957
132163
|
headers: { authorization: `Bearer ${input.token}` },
|
|
131958
132164
|
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
131959
132165
|
}
|
|
131960
132166
|
);
|
|
131961
|
-
const body = await readEnvelope(
|
|
131962
|
-
return body.data?.items
|
|
132167
|
+
const body = await readEnvelope(input.operation, response);
|
|
132168
|
+
return body.data?.items;
|
|
132169
|
+
};
|
|
132170
|
+
var readMessage = async (input) => (await readMessageItems({
|
|
132171
|
+
...input,
|
|
132172
|
+
operation: "message.get"
|
|
132173
|
+
}))?.[0];
|
|
132174
|
+
var fetchMergeForwardMessages = async (input) => {
|
|
132175
|
+
const items = await readMessageItems({
|
|
132176
|
+
...input,
|
|
132177
|
+
operation: "merge_forward.get",
|
|
132178
|
+
withSenderName: true
|
|
132179
|
+
});
|
|
132180
|
+
if (!items?.length) {
|
|
132181
|
+
throw new LarkApiError("merge_forward.get", 0, "the response carried no messages");
|
|
132182
|
+
}
|
|
132183
|
+
return items;
|
|
131963
132184
|
};
|
|
131964
132185
|
var fetchMessageThreadId = async (input) => (await readMessage(input))?.thread_id || void 0;
|
|
131965
132186
|
var fetchMessageReference = async (input) => {
|
|
@@ -132203,9 +132424,9 @@ function getDecodeCache(exclude) {
|
|
|
132203
132424
|
}
|
|
132204
132425
|
return cache;
|
|
132205
132426
|
}
|
|
132206
|
-
function
|
|
132427
|
+
function decode2(string, exclude) {
|
|
132207
132428
|
if (typeof exclude !== "string") {
|
|
132208
|
-
exclude =
|
|
132429
|
+
exclude = decode2.defaultChars;
|
|
132209
132430
|
}
|
|
132210
132431
|
const cache = getDecodeCache(exclude);
|
|
132211
132432
|
return string.replace(/(%[a-f0-9]{2})+/gi, function(seq) {
|
|
@@ -132264,9 +132485,9 @@ function decode(string, exclude) {
|
|
|
132264
132485
|
return result;
|
|
132265
132486
|
});
|
|
132266
132487
|
}
|
|
132267
|
-
|
|
132268
|
-
|
|
132269
|
-
var decode_default =
|
|
132488
|
+
decode2.defaultChars = ";/?:@&=+$,#";
|
|
132489
|
+
decode2.componentChars = "";
|
|
132490
|
+
var decode_default = decode2;
|
|
132270
132491
|
|
|
132271
132492
|
// ../../node_modules/.pnpm/mdurl@2.1.0/node_modules/mdurl/lib/encode.mjs
|
|
132272
132493
|
var encodeCache = {};
|
|
@@ -133287,12 +133508,12 @@ var LinkifyIt = class {
|
|
|
133287
133508
|
*
|
|
133288
133509
|
* See [twitter mentions example](https://github.com/markdown-it/linkify-it/blob/master/examples/twitter.mjs).
|
|
133289
133510
|
*/
|
|
133290
|
-
add(schema,
|
|
133291
|
-
if (!
|
|
133511
|
+
add(schema, definition2 = null) {
|
|
133512
|
+
if (!definition2) delete this.__schemas__[schema];
|
|
133292
133513
|
else {
|
|
133293
133514
|
const def = {
|
|
133294
133515
|
normalize: (match, self2) => self2.normalize(match),
|
|
133295
|
-
...
|
|
133516
|
+
...definition2
|
|
133296
133517
|
};
|
|
133297
133518
|
this.__schemas__[schema] = def;
|
|
133298
133519
|
}
|
|
@@ -137533,6 +137754,50 @@ var buildChoiceCard = (card) => {
|
|
|
137533
137754
|
|
|
137534
137755
|
// src/larkInbound.ts
|
|
137535
137756
|
var import_node_sdk = __toESM(require_lib2(), 1);
|
|
137757
|
+
|
|
137758
|
+
// src/larkConnectionHints.ts
|
|
137759
|
+
var larkConnectionHint = (error) => {
|
|
137760
|
+
if (error instanceof LarkApiError) {
|
|
137761
|
+
if (error.operation === "auth") {
|
|
137762
|
+
if (error.code === 10003 || error.code === 20002) return "larkCredentials";
|
|
137763
|
+
if (error.code === 10014) {
|
|
137764
|
+
const msg = error.msg.toLowerCase();
|
|
137765
|
+
if (msg.includes("app id not exists") || msg.includes("app secret invalid")) {
|
|
137766
|
+
return "larkCredentials";
|
|
137767
|
+
}
|
|
137768
|
+
return msg.includes("unauthorized") ? "larkAppUnavailable" : void 0;
|
|
137769
|
+
}
|
|
137770
|
+
}
|
|
137771
|
+
switch (error.code) {
|
|
137772
|
+
case 99991672:
|
|
137773
|
+
return "larkScope";
|
|
137774
|
+
case 230006:
|
|
137775
|
+
case 232025:
|
|
137776
|
+
return "larkBotOff";
|
|
137777
|
+
case 232034:
|
|
137778
|
+
return "larkAppUnavailable";
|
|
137779
|
+
default:
|
|
137780
|
+
return void 0;
|
|
137781
|
+
}
|
|
137782
|
+
}
|
|
137783
|
+
const code2 = error instanceof Error ? /^pullConnectConfig failed: code=(\d+), msg=/u.exec(error.message)?.[1] : void 0;
|
|
137784
|
+
return code2 === "1000040344" || code2 === "1000040345" || code2 === "1000040346" ? "larkCredentials" : void 0;
|
|
137785
|
+
};
|
|
137786
|
+
|
|
137787
|
+
// src/larkCredentials.ts
|
|
137788
|
+
var isLarkAppId = (appId) => /^cli_[0-9a-fA-F]{16}$/u.test(appId);
|
|
137789
|
+
var larkCredentials = (bot) => {
|
|
137790
|
+
const appId = bot.values["larkAppId"] ?? "";
|
|
137791
|
+
const appSecret = bot.values["larkAppSecret"] ?? "";
|
|
137792
|
+
const issues = [];
|
|
137793
|
+
if (appId === "") issues.push({ column: "larkAppId", code: "required" });
|
|
137794
|
+
else if (!isLarkAppId(appId)) issues.push({ column: "larkAppId", code: "format" });
|
|
137795
|
+
if (appSecret === "") issues.push({ column: "larkAppSecret", code: "required" });
|
|
137796
|
+
const error = issues.some((issue) => issue.code === "required") ? "missing app id or secret in saved config" : issues.length > 0 ? "malformed app id in saved config" : void 0;
|
|
137797
|
+
return { appId, appSecret, issues, error };
|
|
137798
|
+
};
|
|
137799
|
+
|
|
137800
|
+
// src/larkInbound.ts
|
|
137536
137801
|
var isOwnMention = (mention, appId) => mention.id?.app_id === appId || mention.mentioned_type === "bot";
|
|
137537
137802
|
var mentionsApp = (event, appId) => (event.message?.mentions ?? []).some((mention) => isOwnMention(mention, appId));
|
|
137538
137803
|
var mentionsSomeoneElse = (event, appId) => (event.message?.mentions ?? []).some(
|
|
@@ -137546,12 +137811,12 @@ var passesGroupGate = (event, appId, groupMention, messageType) => {
|
|
|
137546
137811
|
};
|
|
137547
137812
|
var RESOURCE_MESSAGE_TYPES = /* @__PURE__ */ new Set(["image", "file", "audio", "media"]);
|
|
137548
137813
|
var UNSUPPORTED_MESSAGE_TYPES = /* @__PURE__ */ new Set([
|
|
137814
|
+
"audio",
|
|
137549
137815
|
"sticker",
|
|
137550
137816
|
"folder",
|
|
137551
137817
|
"share_chat",
|
|
137552
137818
|
"share_user",
|
|
137553
137819
|
"system",
|
|
137554
|
-
"merge_forward",
|
|
137555
137820
|
"interactive"
|
|
137556
137821
|
]);
|
|
137557
137822
|
var botIdsOf = (mentions, appId) => {
|
|
@@ -137589,10 +137854,6 @@ var readResource = (messageType, content) => {
|
|
|
137589
137854
|
const name = key("file_name") ?? "file";
|
|
137590
137855
|
return { fileKey, kind: "file", name, mimeType: mimeFromName(name) };
|
|
137591
137856
|
}
|
|
137592
|
-
if (messageType === "audio") {
|
|
137593
|
-
const fileKey = key("file_key");
|
|
137594
|
-
return fileKey === null ? null : { fileKey, kind: "file", name: "voice.opus", mimeType: "audio/ogg" };
|
|
137595
|
-
}
|
|
137596
137857
|
if (messageType === "media") {
|
|
137597
137858
|
const fileKey = key("file_key");
|
|
137598
137859
|
if (fileKey === null) return null;
|
|
@@ -137621,7 +137882,11 @@ var parseInboundMessage = (event, appId, groupMention = "always", topicOnMention
|
|
|
137621
137882
|
const rootGiven = typeof message.root_id === "string" && message.root_id !== "";
|
|
137622
137883
|
const rootId = rootGiven && message.root_id !== void 0 ? message.root_id : messageId;
|
|
137623
137884
|
const topic = chatType === "p2p" || !inThread ? {} : threadId === void 0 ? { topic: { rootMessageId: messageId } } : { topic: { rootMessageId: rootId, threadId, rootGiven } };
|
|
137624
|
-
if (
|
|
137885
|
+
if (messageType === "audio") {
|
|
137886
|
+
const fileKey = readJson(message.content)?.["file_key"];
|
|
137887
|
+
if (typeof fileKey !== "string" || fileKey === "") return null;
|
|
137888
|
+
}
|
|
137889
|
+
if (messageType === "merge_forward" || UNSUPPORTED_MESSAGE_TYPES.has(messageType)) {
|
|
137625
137890
|
return {
|
|
137626
137891
|
messageId,
|
|
137627
137892
|
chatId,
|
|
@@ -137631,7 +137896,7 @@ var parseInboundMessage = (event, appId, groupMention = "always", topicOnMention
|
|
|
137631
137896
|
resources: [],
|
|
137632
137897
|
inThread,
|
|
137633
137898
|
...topic,
|
|
137634
|
-
unsupportedType: messageType
|
|
137899
|
+
...messageType === "merge_forward" ? { mergeForward: true, ...parentMessageId ? { parentMessageId } : {} } : { unsupportedType: messageType === "audio" ? "voice" : messageType }
|
|
137635
137900
|
};
|
|
137636
137901
|
}
|
|
137637
137902
|
const mentions = message.mentions ?? [];
|
|
@@ -137723,7 +137988,6 @@ var cardActionOperator = (event) => {
|
|
|
137723
137988
|
}
|
|
137724
137989
|
return null;
|
|
137725
137990
|
};
|
|
137726
|
-
var isLarkAppId = (appId) => /^cli_[0-9a-fA-F]{16}$/u.test(appId);
|
|
137727
137991
|
var startLarkInbound = (input) => {
|
|
137728
137992
|
const dispatcher = new import_node_sdk.EventDispatcher({}).register({
|
|
137729
137993
|
"im.message.receive_v1": (event) => {
|
|
@@ -137766,7 +138030,11 @@ var startLarkInbound = (input) => {
|
|
|
137766
138030
|
}
|
|
137767
138031
|
});
|
|
137768
138032
|
if (!isLarkAppId(input.appId)) {
|
|
137769
|
-
input.onConnectionState?.({
|
|
138033
|
+
input.onConnectionState?.({
|
|
138034
|
+
state: "failed",
|
|
138035
|
+
reason: "invalid-app-id",
|
|
138036
|
+
hint: "larkCredentials"
|
|
138037
|
+
});
|
|
137770
138038
|
return { close: () => void 0 };
|
|
137771
138039
|
}
|
|
137772
138040
|
let closed = false;
|
|
@@ -137775,7 +138043,13 @@ var startLarkInbound = (input) => {
|
|
|
137775
138043
|
};
|
|
137776
138044
|
const failed = (error) => {
|
|
137777
138045
|
if (!closed) {
|
|
137778
|
-
|
|
138046
|
+
const hint = larkConnectionHint(error);
|
|
138047
|
+
input.onConnectionState?.({
|
|
138048
|
+
state: "failed",
|
|
138049
|
+
reason: hint === "larkCredentials" ? "credentials-rejected" : "disconnected",
|
|
138050
|
+
error,
|
|
138051
|
+
...hint === void 0 ? {} : { hint }
|
|
138052
|
+
});
|
|
137779
138053
|
}
|
|
137780
138054
|
};
|
|
137781
138055
|
const client = new import_node_sdk.WSClient({
|
|
@@ -137801,6 +138075,24 @@ var startLarkInbound = (input) => {
|
|
|
137801
138075
|
};
|
|
137802
138076
|
};
|
|
137803
138077
|
|
|
138078
|
+
// src/boundedSeen.ts
|
|
138079
|
+
var makeBoundedSeen = (limit) => {
|
|
138080
|
+
const seen = /* @__PURE__ */ new Set();
|
|
138081
|
+
return {
|
|
138082
|
+
has: (key) => seen.has(key),
|
|
138083
|
+
/** 记下这个键:第一次见返回 true;已经记着返回 false(不重排,最早记的先被忘)。 */
|
|
138084
|
+
claim: (key) => {
|
|
138085
|
+
if (seen.has(key)) return false;
|
|
138086
|
+
seen.add(key);
|
|
138087
|
+
if (seen.size > limit) {
|
|
138088
|
+
const oldest = seen.values().next().value;
|
|
138089
|
+
if (oldest !== void 0) seen.delete(oldest);
|
|
138090
|
+
}
|
|
138091
|
+
return true;
|
|
138092
|
+
}
|
|
138093
|
+
};
|
|
138094
|
+
};
|
|
138095
|
+
|
|
137804
138096
|
// src/deadline.ts
|
|
137805
138097
|
var withDeadline2 = async (timeoutMs, load) => {
|
|
137806
138098
|
const signal = AbortSignal.timeout(timeoutMs);
|
|
@@ -137993,15 +138285,157 @@ var makeLarkMessageContext = (deps) => {
|
|
|
137993
138285
|
if (message.chatType !== "group" || message.senderOpenId === null) return {};
|
|
137994
138286
|
const name = contactDenied ? null : userName(message.senderOpenId);
|
|
137995
138287
|
const chat = chatInfo(message.chatId);
|
|
137996
|
-
const fields = (
|
|
137997
|
-
...
|
|
138288
|
+
const fields = (senderName2, info) => ({
|
|
138289
|
+
...senderName2 ? { senderName: senderName2 } : {},
|
|
137998
138290
|
...info?.name ? { chatTitle: info.name } : {}
|
|
137999
138291
|
});
|
|
138000
|
-
return name instanceof Promise || chat instanceof Promise ? Promise.all([name, chat]).then(([
|
|
138292
|
+
return name instanceof Promise || chat instanceof Promise ? Promise.all([name, chat]).then(([senderName2, info]) => fields(senderName2, info)) : fields(name, chat);
|
|
138001
138293
|
};
|
|
138002
138294
|
return { chatInfo, forMessage, loadReference, topicRootForSession };
|
|
138003
138295
|
};
|
|
138004
138296
|
|
|
138297
|
+
// src/larkMergeForward.ts
|
|
138298
|
+
var MAX_MESSAGES = 100;
|
|
138299
|
+
var MAX_MESSAGE_CHARS = 2e3;
|
|
138300
|
+
var MAX_BODY_CHARS = 3e4;
|
|
138301
|
+
var clip2 = (text4, limit, suffix = "\u2026") => {
|
|
138302
|
+
if (text4.length <= limit) return text4;
|
|
138303
|
+
const end = limit - suffix.length;
|
|
138304
|
+
const safeEnd = /[\uD800-\uDBFF]/u.test(text4[end - 1] ?? "") ? end - 1 : end;
|
|
138305
|
+
return text4.slice(0, safeEnd) + suffix;
|
|
138306
|
+
};
|
|
138307
|
+
var senderName = (message, t) => {
|
|
138308
|
+
const names = message.sender?.sender_i18n_names;
|
|
138309
|
+
const name = [
|
|
138310
|
+
t.language === "zh-CN" ? names?.zh_cn : names?.en_us,
|
|
138311
|
+
names?.zh_cn,
|
|
138312
|
+
names?.en_us,
|
|
138313
|
+
names?.ja_jp
|
|
138314
|
+
].find((value2) => value2?.trim());
|
|
138315
|
+
return clip2(
|
|
138316
|
+
name?.trim() ?? t(message.sender?.sender_type === "app" ? "mergeForward.bot" : "mergeForward.someone"),
|
|
138317
|
+
120
|
|
138318
|
+
).replace(/\s+/gu, " ");
|
|
138319
|
+
};
|
|
138320
|
+
var localTime = (value2, t) => {
|
|
138321
|
+
const date = new Date(Number(value2 ?? NaN));
|
|
138322
|
+
if (Number.isNaN(date.getTime())) return t("mergeForward.unknownTime");
|
|
138323
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
138324
|
+
return `${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
138325
|
+
};
|
|
138326
|
+
var resourceLabel = (kind, name, t) => {
|
|
138327
|
+
const type = t.optional(`mergeForward.resource.${kind}`) ?? kind;
|
|
138328
|
+
return typeof name === "string" && name.trim() ? t("mergeForward.unavailableNamed", { type, name: name.trim() }) : t("mergeForward.unavailable", { type });
|
|
138329
|
+
};
|
|
138330
|
+
var messageText = (message, t) => {
|
|
138331
|
+
const type = message.msg_type ?? "unknown";
|
|
138332
|
+
const content = message.body?.content;
|
|
138333
|
+
let text4;
|
|
138334
|
+
if (type === "post") {
|
|
138335
|
+
const post = serializePost(
|
|
138336
|
+
content ?? "{}",
|
|
138337
|
+
void 0,
|
|
138338
|
+
(resource) => resourceLabel(resource.media, resource.fileName, t)
|
|
138339
|
+
);
|
|
138340
|
+
text4 = post?.text ?? "[post]";
|
|
138341
|
+
} else if (type === "text") {
|
|
138342
|
+
const value2 = readJson(content)?.["text"];
|
|
138343
|
+
text4 = typeof value2 === "string" ? value2 : "[text]";
|
|
138344
|
+
} else if (["image", "file", "audio", "media", "folder"].includes(type)) {
|
|
138345
|
+
text4 = resourceLabel(type === "media" ? "video" : type, readJson(content)?.["file_name"], t);
|
|
138346
|
+
} else {
|
|
138347
|
+
text4 = `[${type}]`;
|
|
138348
|
+
}
|
|
138349
|
+
const mentions = new Map((message.mentions ?? []).map((mention) => [mention.key, mention.name]));
|
|
138350
|
+
text4 = text4.replace(
|
|
138351
|
+
/@_user_\d+|@_all/gu,
|
|
138352
|
+
(key) => `@${mentions.get(key)?.trim() || t("mergeForward.someone")}`
|
|
138353
|
+
);
|
|
138354
|
+
return clip2(text4.trim(), MAX_MESSAGE_CHARS, t("mergeForward.truncated"));
|
|
138355
|
+
};
|
|
138356
|
+
var orderedMessages = (rootId, items) => {
|
|
138357
|
+
const byId = new Map(
|
|
138358
|
+
items.filter((item) => item.message_id !== rootId).map((item) => [item.message_id, item])
|
|
138359
|
+
);
|
|
138360
|
+
const children = /* @__PURE__ */ new Map();
|
|
138361
|
+
for (const item of byId.values()) {
|
|
138362
|
+
const parent = item.upper_message_id ?? rootId;
|
|
138363
|
+
const siblings = children.get(parent) ?? [];
|
|
138364
|
+
siblings.push(item);
|
|
138365
|
+
children.set(parent, siblings);
|
|
138366
|
+
}
|
|
138367
|
+
const compare = (a, b) => Number(a.create_time ?? 0) - Number(b.create_time ?? 0);
|
|
138368
|
+
for (const siblings of children.values()) siblings.sort(compare);
|
|
138369
|
+
const rows = [];
|
|
138370
|
+
const seen = /* @__PURE__ */ new Set([rootId]);
|
|
138371
|
+
const walk = (first) => {
|
|
138372
|
+
const stack = [{ message: first, depth: 0 }];
|
|
138373
|
+
while (stack.length) {
|
|
138374
|
+
const frame = stack.pop();
|
|
138375
|
+
if (frame === void 0) break;
|
|
138376
|
+
if ("end" in frame) {
|
|
138377
|
+
const row = rows[frame.end];
|
|
138378
|
+
if (row) row.descendants = rows.length - frame.end - 1;
|
|
138379
|
+
continue;
|
|
138380
|
+
}
|
|
138381
|
+
if (seen.has(frame.message.message_id)) continue;
|
|
138382
|
+
seen.add(frame.message.message_id);
|
|
138383
|
+
const end = rows.length;
|
|
138384
|
+
rows.push({ ...frame, descendants: 0 });
|
|
138385
|
+
stack.push({ end });
|
|
138386
|
+
for (const child of (children.get(frame.message.message_id) ?? []).toReversed()) {
|
|
138387
|
+
stack.push({ message: child, depth: frame.depth + 1 });
|
|
138388
|
+
}
|
|
138389
|
+
}
|
|
138390
|
+
};
|
|
138391
|
+
for (const item of children.get(rootId) ?? []) walk(item);
|
|
138392
|
+
for (const item of [...byId.values()].sort(compare)) walk(item);
|
|
138393
|
+
return rows;
|
|
138394
|
+
};
|
|
138395
|
+
var serializeMergeForward = (rootId, items, t) => {
|
|
138396
|
+
const rows = orderedMessages(rootId, items);
|
|
138397
|
+
const lines = [t("mergeForward.title", { count: rows.length })];
|
|
138398
|
+
let length = lines[0]?.length ?? 0;
|
|
138399
|
+
let included = 0;
|
|
138400
|
+
for (const { message, depth, descendants } of rows) {
|
|
138401
|
+
if (included >= MAX_MESSAGES) break;
|
|
138402
|
+
const indent = " ".repeat(depth);
|
|
138403
|
+
const body = message.msg_type === "merge_forward" ? t("mergeForward.nested", { count: descendants }) : messageText(message, t);
|
|
138404
|
+
const line = `${indent}${senderName(message, t)} ${localTime(message.create_time, t)}: ${body.replaceAll("\n", `
|
|
138405
|
+
${indent} `)}`;
|
|
138406
|
+
const remaining = rows.length - included - 1;
|
|
138407
|
+
const tail = remaining > 0 ? t("mergeForward.omitted", { count: remaining }).length + 1 : 0;
|
|
138408
|
+
if (length + line.length + 1 + tail > MAX_BODY_CHARS) break;
|
|
138409
|
+
lines.push(line);
|
|
138410
|
+
length += line.length + 1;
|
|
138411
|
+
included += 1;
|
|
138412
|
+
}
|
|
138413
|
+
if (included < rows.length)
|
|
138414
|
+
lines.push(t("mergeForward.omitted", { count: rows.length - included }));
|
|
138415
|
+
return lines.join("\n");
|
|
138416
|
+
};
|
|
138417
|
+
var loadMergeForward = async (message, deps) => {
|
|
138418
|
+
try {
|
|
138419
|
+
const items = await withDeadline2(1e4, async (signal) => {
|
|
138420
|
+
const token = await deps.tenantToken();
|
|
138421
|
+
signal.throwIfAborted();
|
|
138422
|
+
return fetchMergeForwardMessages({
|
|
138423
|
+
http: deps.http,
|
|
138424
|
+
token,
|
|
138425
|
+
messageId: message.messageId,
|
|
138426
|
+
signal
|
|
138427
|
+
});
|
|
138428
|
+
});
|
|
138429
|
+
return { ...message, text: serializeMergeForward(message.messageId, items, deps.translate) };
|
|
138430
|
+
} catch (error) {
|
|
138431
|
+
deps.warn("lark could not read merged forward messages", {
|
|
138432
|
+
messageId: message.messageId,
|
|
138433
|
+
error: describeError(error)
|
|
138434
|
+
});
|
|
138435
|
+
return { ...message, unsupportedType: "merge_forward" };
|
|
138436
|
+
}
|
|
138437
|
+
};
|
|
138438
|
+
|
|
138005
138439
|
// src/larkAdapter.ts
|
|
138006
138440
|
var TOKEN_CACHE_MAX_AGE_MS = 30 * 6e4;
|
|
138007
138441
|
var COLUMNS = [
|
|
@@ -138033,21 +138467,9 @@ var splitTopicChatId = (chatId) => {
|
|
|
138033
138467
|
return at < 0 ? { chatId } : { chatId: chatId.slice(0, at), rootMessageId: chatId.slice(at + 1) };
|
|
138034
138468
|
};
|
|
138035
138469
|
var topicAliasOf = (chatId, threadId) => `${chatId}:${threadId}`;
|
|
138036
|
-
var credentials = (binding) => ({
|
|
138037
|
-
appId: binding.values["larkAppId"] ?? "",
|
|
138038
|
-
appSecret: binding.values["larkAppSecret"] ?? ""
|
|
138039
|
-
});
|
|
138040
|
-
var larkConfigIssues = (binding) => {
|
|
138041
|
-
const { appId, appSecret } = credentials(binding);
|
|
138042
|
-
const issues = [];
|
|
138043
|
-
if (appId === "") issues.push({ column: "larkAppId", code: "required" });
|
|
138044
|
-
else if (!isLarkAppId(appId)) issues.push({ column: "larkAppId", code: "format" });
|
|
138045
|
-
if (appSecret === "") issues.push({ column: "larkAppSecret", code: "required" });
|
|
138046
|
-
return issues;
|
|
138047
|
-
};
|
|
138048
138470
|
var makeLarkAdapter = (deps) => {
|
|
138049
138471
|
const tenantToken = async (binding) => {
|
|
138050
|
-
const { appId, appSecret } =
|
|
138472
|
+
const { appId, appSecret } = larkCredentials(binding);
|
|
138051
138473
|
const key = `lark-tenant-token.${appId}`;
|
|
138052
138474
|
const cached = await deps.cacheGet(key, TOKEN_CACHE_MAX_AGE_MS);
|
|
138053
138475
|
if (typeof cached?.token === "string") return cached.token;
|
|
@@ -138120,6 +138542,7 @@ var makeLarkAdapter = (deps) => {
|
|
|
138120
138542
|
};
|
|
138121
138543
|
return {
|
|
138122
138544
|
platform: "lark",
|
|
138545
|
+
botIdentity: (bot) => larkCredentials(bot).appId,
|
|
138123
138546
|
icon: "lark",
|
|
138124
138547
|
label: "Lark",
|
|
138125
138548
|
columns: COLUMNS,
|
|
@@ -138128,10 +138551,10 @@ var makeLarkAdapter = (deps) => {
|
|
|
138128
138551
|
const { chatId: groupId, rootMessageId } = splitTopicChatId(chatId);
|
|
138129
138552
|
return rootMessageId === void 0 ? void 0 : groupId;
|
|
138130
138553
|
},
|
|
138131
|
-
validate:
|
|
138132
|
-
isConfigured: (binding) =>
|
|
138554
|
+
validate: (binding) => larkCredentials(binding).issues,
|
|
138555
|
+
isConfigured: (binding) => larkCredentials(binding).issues.every((issue) => issue.code !== "required"),
|
|
138133
138556
|
connect: (binding, handlers) => {
|
|
138134
|
-
const { appId, appSecret } =
|
|
138557
|
+
const { appId, appSecret } = larkCredentials(binding);
|
|
138135
138558
|
const port = {
|
|
138136
138559
|
noteChatAlias: handlers.noteChatAlias,
|
|
138137
138560
|
knowsChatAlias: handlers.knowsChatAlias
|
|
@@ -138168,11 +138591,17 @@ var makeLarkAdapter = (deps) => {
|
|
|
138168
138591
|
arg: click.arg
|
|
138169
138592
|
}) ?? null,
|
|
138170
138593
|
onMessage: (message) => {
|
|
138594
|
+
const prepared = message.mergeForward ? loadMergeForward(message, {
|
|
138595
|
+
http: deps.http,
|
|
138596
|
+
tenantToken: () => tenantToken(binding),
|
|
138597
|
+
translate: deps.translate,
|
|
138598
|
+
warn: deps.log.warn
|
|
138599
|
+
}) : message;
|
|
138171
138600
|
const { topic } = message;
|
|
138172
138601
|
const source = context.forMessage(message);
|
|
138173
138602
|
const parentMessageId = message.parentMessageId;
|
|
138174
138603
|
const reference2 = parentMessageId === void 0 ? null : context.loadReference(parentMessageId);
|
|
138175
|
-
const deliver = (names, replyTo) => handlers.onMessage({
|
|
138604
|
+
const deliver = (ready, names, replyTo) => handlers.onMessage({
|
|
138176
138605
|
messageId: message.messageId,
|
|
138177
138606
|
// 已经开出来的话题就是它自己那个聊天;将要开出来的(主窗口里 @ 机器人)先算群的,
|
|
138178
138607
|
// 由核心决定要不要分出去。
|
|
@@ -138186,7 +138615,7 @@ var makeLarkAdapter = (deps) => {
|
|
|
138186
138615
|
},
|
|
138187
138616
|
senderId: message.senderOpenId,
|
|
138188
138617
|
...names,
|
|
138189
|
-
text:
|
|
138618
|
+
text: ready.text,
|
|
138190
138619
|
topicRootForSession: context.topicRootForSession(message),
|
|
138191
138620
|
...replyTo === void 0 ? {} : { replyTo },
|
|
138192
138621
|
chatKind: message.chatType === "p2p" ? "private" : "group",
|
|
@@ -138197,7 +138626,7 @@ var makeLarkAdapter = (deps) => {
|
|
|
138197
138626
|
mimeType: resource.mimeType,
|
|
138198
138627
|
kind: resource.kind
|
|
138199
138628
|
})),
|
|
138200
|
-
...
|
|
138629
|
+
...ready.unsupportedType === void 0 ? {} : { unsupportedType: ready.unsupportedType }
|
|
138201
138630
|
});
|
|
138202
138631
|
const pending = topic?.threadId === void 0 ? null : topicUnsettled(binding.rowId, message.chatId, {
|
|
138203
138632
|
threadId: topic.threadId,
|
|
@@ -138205,12 +138634,12 @@ var makeLarkAdapter = (deps) => {
|
|
|
138205
138634
|
});
|
|
138206
138635
|
const key = JSON.stringify([message.chatId, topic?.threadId]);
|
|
138207
138636
|
const earlier = pendingDeliveries.get(key);
|
|
138208
|
-
if (earlier === void 0 && pending === null && reference2 === null && !(source instanceof Promise)) {
|
|
138209
|
-
deliver(source);
|
|
138637
|
+
if (earlier === void 0 && pending === null && reference2 === null && !(prepared instanceof Promise) && !(source instanceof Promise)) {
|
|
138638
|
+
deliver(prepared, source);
|
|
138210
138639
|
} else {
|
|
138211
|
-
const completed = Promise.all([earlier, pending, reference2, source]).then(([, , replyTo, names]) => {
|
|
138640
|
+
const completed = Promise.all([earlier, pending, reference2, source, prepared]).then(([, , replyTo, names, ready]) => {
|
|
138212
138641
|
if (pendingDeliveries.get(key) === completed) pendingDeliveries.delete(key);
|
|
138213
|
-
if (open) deliver(names, replyTo ?? void 0);
|
|
138642
|
+
if (open) deliver(ready, names, replyTo ?? void 0);
|
|
138214
138643
|
}).catch((error) => handlers.onError(describeError(error))).finally(() => {
|
|
138215
138644
|
if (pendingDeliveries.get(key) === completed) pendingDeliveries.delete(key);
|
|
138216
138645
|
});
|
|
@@ -138249,17 +138678,15 @@ var makeLarkAdapter = (deps) => {
|
|
|
138249
138678
|
}
|
|
138250
138679
|
},
|
|
138251
138680
|
checkConnection: async (bot) => {
|
|
138252
|
-
const { appId, appSecret } =
|
|
138253
|
-
if (
|
|
138681
|
+
const { appId, appSecret, error: credentialError } = larkCredentials(bot);
|
|
138682
|
+
if (credentialError !== void 0) {
|
|
138254
138683
|
return {
|
|
138255
138684
|
stage: "credentials",
|
|
138256
138685
|
ok: false,
|
|
138257
|
-
detail:
|
|
138686
|
+
detail: credentialError,
|
|
138687
|
+
hint: "larkCredentials"
|
|
138258
138688
|
};
|
|
138259
138689
|
}
|
|
138260
|
-
if (!isLarkAppId(appId)) {
|
|
138261
|
-
return { stage: "credentials", ok: false, detail: "malformed app id in saved config" };
|
|
138262
|
-
}
|
|
138263
138690
|
try {
|
|
138264
138691
|
const { expiresInSeconds } = await fetchTenantToken({ http: deps.http, appId, appSecret });
|
|
138265
138692
|
return {
|
|
@@ -138268,7 +138695,13 @@ var makeLarkAdapter = (deps) => {
|
|
|
138268
138695
|
detail: `token ok; expiresIn=${String(expiresInSeconds)}s`
|
|
138269
138696
|
};
|
|
138270
138697
|
} catch (error) {
|
|
138271
|
-
|
|
138698
|
+
const hint = larkConnectionHint(error);
|
|
138699
|
+
return {
|
|
138700
|
+
stage: "tenant_access_token",
|
|
138701
|
+
ok: false,
|
|
138702
|
+
detail: describeError(error),
|
|
138703
|
+
...hint === void 0 ? {} : { hint }
|
|
138704
|
+
};
|
|
138272
138705
|
}
|
|
138273
138706
|
},
|
|
138274
138707
|
sendImage: async (binding, image2) => {
|
|
@@ -138606,6 +139039,7 @@ var en_default = {
|
|
|
138606
139039
|
projectArchived: 'Project "{{title}}" is archived. Restore it, or send /repo to pick another project, then message again.',
|
|
138607
139040
|
projectMissing: "The project this chat was working on is no longer on this machine (it may have been deleted), so a thread cannot be started. Send /repo to pick another one.",
|
|
138608
139041
|
threadGone: "The earlier session was deleted or archived, so this chat now starts a new one.",
|
|
139042
|
+
turnFailedToStart: "This message couldn't be dispatched. Please send it again later. Reference {{ref}}.",
|
|
138609
139043
|
turnFailed: "This turn didn't finish: {{detail}}\nReference {{ref}}. Send the message again; if it keeps failing, send /new to start a new conversation.",
|
|
138610
139044
|
turnFailedNoDetail: "This turn didn't finish. Reference {{ref}}. Send the message again; if it keeps failing, send /new to start a new conversation.",
|
|
138611
139045
|
turnFailedBrief: "This turn didn't finish: {{detail}}\nSend the message again; if it keeps failing, send /new to start a new conversation.",
|
|
@@ -138622,9 +139056,11 @@ var en_default = {
|
|
|
138622
139056
|
attachmentsNoTeam: "Files cannot be sent to a team yet, so they were left out. Send /thread to go back to a normal session.",
|
|
138623
139057
|
unsupportedType: "I can't process {{type}} messages yet.",
|
|
138624
139058
|
unsupportedFolder: "Folders can't be downloaded. Zip it and send the archive instead.",
|
|
139059
|
+
unsupportedVoice: "Voice messages aren't supported yet. Please send text.",
|
|
138625
139060
|
sendingFile: "Sending {{name}}:",
|
|
138626
139061
|
notAllowedYet: `You are not on the allow list yet. Your user id is {{id}}. Add it under Settings \u2192 Plugins \u2192 Chat, in this bot's "Allowed people" box.`,
|
|
138627
|
-
received: "Got it, working on it."
|
|
139062
|
+
received: "Got it, working on it.",
|
|
139063
|
+
welcome: "Connected. Send text or images to get started, or send /help to see the available commands."
|
|
138628
139064
|
},
|
|
138629
139065
|
approval: {
|
|
138630
139066
|
allow: "Allow",
|
|
@@ -138928,6 +139364,7 @@ var en_default = {
|
|
|
138928
139364
|
}
|
|
138929
139365
|
},
|
|
138930
139366
|
repo: {
|
|
139367
|
+
autoSelected: "Using project {{title}}.",
|
|
138931
139368
|
needProject: "This chat has no project yet, so I won't start any work. Pick one and I will.",
|
|
138932
139369
|
currentLine: 'This chat works on project "{{title}}".',
|
|
138933
139370
|
noProjects: "There are no projects on this machine yet. Add one in Ycodium first.",
|
|
@@ -138958,21 +139395,41 @@ var en_default = {
|
|
|
138958
139395
|
failed: "Connection failed",
|
|
138959
139396
|
notSaved: "Save this row first (credentials are read from saved config only).",
|
|
138960
139397
|
hint: {
|
|
139398
|
+
connecting: "Still connecting. There is usually a result within 20 seconds; check again in a moment.",
|
|
138961
139399
|
badToken: "This does not look like a bot token. Copy it again from BotFather: digits, a colon, then the key.",
|
|
138962
|
-
unauthorized: "Telegram does not accept this token. It may have been revoked; get the current one from BotFather.",
|
|
139400
|
+
unauthorized: "Telegram does not accept this token. It may have been revoked; get the current one from BotFather. Save the corrected token here to reconnect automatically; if nothing needs changing, use Reconnect below.",
|
|
138963
139401
|
notBot: "This token does not belong to a bot. Use the token BotFather gave you.",
|
|
138964
139402
|
webhookActive: "This bot has a webhook set by another service, so it cannot receive messages here. Remove the webhook there, then check again.",
|
|
139403
|
+
larkCredentials: "Lark does not accept this App ID or App Secret. Copy both from Credentials & Basic Info for your self-built app in the Lark developer console. Save the corrected App ID and App Secret here to reconnect automatically; if nothing needs changing, use Reconnect below.",
|
|
139404
|
+
larkScope: "This app is missing a required permission. In the Lark developer console, open Permission Management, add the permissions listed in the details, then publish a version.",
|
|
139405
|
+
larkBotOff: "This app's bot capability is not enabled. In the Lark developer console, open App Capabilities \u2192 Bot, enable the bot, then publish a version.",
|
|
139406
|
+
larkAppUnavailable: "This app is unavailable in your organization. In the Lark developer console, check that the app is enabled and published; ask your administrator to confirm it is installed and enabled for your organization.",
|
|
139407
|
+
wecomBotCredentials: "WeCom does not accept this Bot ID or Secret. Open this smart bot's configuration in the WeCom admin console, select API mode \u2192 Long connection, and copy its Bot ID and Secret. These are not the callback Token or EncodingAESKey. Save the corrected Bot ID and Secret here to reconnect automatically; if nothing needs changing, use Reconnect below.",
|
|
139408
|
+
wecomLongConnOff: "This smart bot's long connection mode is not enabled. Open its configuration in the WeCom admin console, select API mode \u2192 Long connection and save; the bot reconnects on its own within a minute, or use Reconnect below.",
|
|
139409
|
+
wecomTakenOver: "Another machine or program is using the same Bot ID. Stop using this smart bot there, then use Reconnect below.",
|
|
138965
139410
|
wecomTrustedIp: "Add this machine's outbound public IP to the app's trusted IP list in the WeCom admin console.",
|
|
138966
139411
|
wecomInvalidUser: "Check that the recipient, department or tag exists and is in this app's visible range.",
|
|
138967
|
-
wecomInvalidAgent: "Check the AgentId against this self-built application in the WeCom admin console."
|
|
139412
|
+
wecomInvalidAgent: "Check the AgentId against this self-built application in the WeCom admin console.",
|
|
139413
|
+
wecomCallbackNotSeen: "Credentials are valid, but no verified callback has arrived since this startup. In the app's Receive messages settings in the WeCom admin console, paste the callback URL shown on this bot's settings card and save; WeCom will send a verification request. This machine must be reachable by WeCom over the public internet.",
|
|
139414
|
+
wecomCallbackSignature: "A callback arrived, but its signature or decryption failed; no verified callback has arrived since this startup. Match the Callback Token and EncodingAESKey to this app's Receive messages settings in the WeCom admin console, then save there to verify again."
|
|
138968
139415
|
}
|
|
138969
139416
|
},
|
|
139417
|
+
reconnect: {
|
|
139418
|
+
label: "Reconnect",
|
|
139419
|
+
description: "Disconnect and reconnect this bot using its saved settings.",
|
|
139420
|
+
notAvailable: "This platform can't be connected yet, so reconnecting won't help.",
|
|
139421
|
+
running: "Starting to reconnect {{rowId}}\u2026",
|
|
139422
|
+
done: "Reconnection started. Check the bot's connection status for the result.",
|
|
139423
|
+
notSaved: "Save this row first before reconnecting. Only saved settings are used.",
|
|
139424
|
+
notConfigured: "Complete and save this bot's settings before reconnecting.",
|
|
139425
|
+
duplicate: "Another bot row uses these credentials. Resolve the duplicate before reconnecting.",
|
|
139426
|
+
inactive: "The chat bridge is turned off. Enable it before reconnecting."
|
|
139427
|
+
},
|
|
138970
139428
|
copyDiagnostics: {
|
|
138971
139429
|
label: "Copy diagnostics",
|
|
138972
139430
|
description: "Run a connection probe and produce copy-paste diagnostics (no secrets).",
|
|
138973
139431
|
running: "Building diagnostics for {{rowId}}\u2026",
|
|
138974
|
-
done: "
|
|
138975
|
-
failed: "Diagnostics failed",
|
|
139432
|
+
done: "Diagnostic report ready \u2014 see details below",
|
|
138976
139433
|
notSaved: "Save this row first before copying diagnostics."
|
|
138977
139434
|
},
|
|
138978
139435
|
diagnostic: {
|
|
@@ -138980,16 +139437,23 @@ var en_default = {
|
|
|
138980
139437
|
},
|
|
138981
139438
|
larkRepair: {
|
|
138982
139439
|
label: "Repair scopes",
|
|
138983
|
-
description: "
|
|
139440
|
+
description: "List missing required scopes and a link to request them on the Feishu open platform; an app administrator then publishes the app version manually.",
|
|
138984
139441
|
step: {
|
|
138985
|
-
|
|
138986
|
-
|
|
138987
|
-
|
|
138988
|
-
|
|
138989
|
-
|
|
138990
|
-
|
|
138991
|
-
|
|
138992
|
-
|
|
139442
|
+
token: "Get a tenant token with the saved credentials",
|
|
139443
|
+
scopes: "Check the app's granted tenant scopes"
|
|
139444
|
+
},
|
|
139445
|
+
missing: "Some required scopes are missing.",
|
|
139446
|
+
allGranted: "All required scopes are enabled.",
|
|
139447
|
+
unverified: "Unable to check scopes automatically.",
|
|
139448
|
+
missingScopes: "Missing scopes:\n{{scopes}}",
|
|
139449
|
+
requiredScopes: "Required scopes \u2014 check each one manually:\n{{scopes}}\n\nApps without application:application:self_manage cannot report their granted scopes, so this list could not be checked automatically.",
|
|
139450
|
+
openApplyLink: "Request scopes on the Feishu open platform",
|
|
139451
|
+
openCheckLink: "Check and request scopes on the Feishu open platform",
|
|
139452
|
+
applyLink: "Open platform URL:\n{{url}}",
|
|
139453
|
+
publishHint: "As an app administrator, select the scopes at that link. Then publish a version via App release \u2192 Version management & release \u2192 Create version, and return here to click \u201CCheck connection\u201D.",
|
|
139454
|
+
botHint: "To enable the bot, go to App \u2192 Add app capability \u2192 Bot. Bot capability is not checked here.",
|
|
139455
|
+
eventHint: "To receive messages, set both subscriptions in Events & Callbacks to Long connection, then add these (not checked here):\nEvents: {{events}}\nCallbacks: {{callbacks}}",
|
|
139456
|
+
failed: "Could not check the app's required scopes. Review the error details and try again.",
|
|
138993
139457
|
notLark: "This row is not a saved Lark bot."
|
|
138994
139458
|
}
|
|
138995
139459
|
},
|
|
@@ -139036,11 +139500,13 @@ var en_default = {
|
|
|
139036
139500
|
connection: {
|
|
139037
139501
|
failed: "The last connection attempt failed.",
|
|
139038
139502
|
details: "Details: {{error}}",
|
|
139503
|
+
reconnect: "You can try again with Reconnect below.",
|
|
139039
139504
|
reason: {
|
|
139040
139505
|
"not-available": "This platform can't be connected yet.",
|
|
139041
139506
|
"invalid-app-id": "The App ID isn't in the right format.",
|
|
139042
139507
|
disconnected: "The connection to the platform is down.",
|
|
139043
139508
|
"subscription-failed": "The platform didn't confirm the subscription.",
|
|
139509
|
+
"taken-over": "This connection was taken over elsewhere, so automatic reconnection has stopped here.",
|
|
139044
139510
|
"token-unavailable": "Couldn't get an access token.",
|
|
139045
139511
|
"credentials-rejected": "The platform rejected the credentials."
|
|
139046
139512
|
},
|
|
@@ -139064,7 +139530,7 @@ var en_default = {
|
|
|
139064
139530
|
done: "Done.",
|
|
139065
139531
|
close: "Close",
|
|
139066
139532
|
failedToRun: "Couldn't run this on the machine.",
|
|
139067
|
-
saveFirst: "Save or undo your changes first. These
|
|
139533
|
+
saveFirst: "Save or undo your changes first. These actions use the saved settings.",
|
|
139068
139534
|
qrTitle: "QR code to scan with the Lark app"
|
|
139069
139535
|
},
|
|
139070
139536
|
step: {
|
|
@@ -139090,6 +139556,24 @@ var en_default = {
|
|
|
139090
139556
|
callbackFollowsKeys: "This URL changes when Token or EncodingAESKey changes. Paste it into WeCom again after changing them."
|
|
139091
139557
|
}
|
|
139092
139558
|
},
|
|
139559
|
+
mergeForward: {
|
|
139560
|
+
title: "[Forwarded chat history, messages: {{count}}]",
|
|
139561
|
+
nested: "[Nested forward, messages: {{count}}]",
|
|
139562
|
+
someone: "Someone",
|
|
139563
|
+
bot: "A bot",
|
|
139564
|
+
unknownTime: "[unknown time]",
|
|
139565
|
+
resource: {
|
|
139566
|
+
image: "Image",
|
|
139567
|
+
file: "File",
|
|
139568
|
+
audio: "Audio",
|
|
139569
|
+
video: "Video",
|
|
139570
|
+
folder: "Folder"
|
|
139571
|
+
},
|
|
139572
|
+
unavailable: "[{{type}}: could not be retrieved]",
|
|
139573
|
+
unavailableNamed: "[{{type}} {{name}}: could not be retrieved]",
|
|
139574
|
+
omitted: "\u2026 not shown: {{count}} more",
|
|
139575
|
+
truncated: "\u2026 [message truncated]"
|
|
139576
|
+
},
|
|
139093
139577
|
team: {
|
|
139094
139578
|
noRoster: "This project has no team roster yet. Save a roster in the Teams tab and choose a lead first.",
|
|
139095
139579
|
unavailable: "Could not read this project's team roster. Check that the Teams plugin is enabled and try again.",
|
|
@@ -139308,6 +139792,7 @@ var zh_CN_default = {
|
|
|
139308
139792
|
projectArchived: "\u9879\u76EE\u300C{{title}}\u300D\u5DF2\u5F52\u6863\u3002\u6062\u590D\u5B83\uFF0C\u6216\u53D1 /repo \u6362\u4E2A\u9879\u76EE\uFF0C\u518D\u53D1\u6D88\u606F\u3002",
|
|
139309
139793
|
projectMissing: "\u8FD9\u4E2A\u804A\u5929\u539F\u6765\u505A\u7684\u9879\u76EE\u5DF2\u7ECF\u4E0D\u5728\u8FD9\u53F0\u673A\u5668\u4E0A\u4E86\uFF08\u53EF\u80FD\u88AB\u5220\u4E86\uFF09\uFF0C\u5F00\u4E0D\u4E86\u4F1A\u8BDD\u3002\u53D1 /repo \u91CD\u65B0\u9009\u4E00\u4E2A\u3002",
|
|
139310
139794
|
threadGone: "\u4E4B\u524D\u90A3\u6761\u4F1A\u8BDD\u5DF2\u88AB\u5220\u9664\u6216\u5F52\u6863\uFF0C\u8FD9\u4E2A\u804A\u5929\u6539\u4E3A\u5F00\u4E00\u6761\u65B0\u4F1A\u8BDD\u3002",
|
|
139795
|
+
turnFailedToStart: "\u8FD9\u6761\u6D88\u606F\u6CA1\u80FD\u6D3E\u51FA\u53BB\uFF0C\u8BF7\u7A0D\u540E\u91CD\u53D1\u3002\u53C2\u8003\u53F7 {{ref}}\u3002",
|
|
139311
139796
|
turnFailed: "\u8FD9\u4E00\u8F6E\u6CA1\u80FD\u5B8C\u6210\uFF1A{{detail}}\n\u53C2\u8003\u53F7 {{ref}}\u3002\u53EF\u4EE5\u76F4\u63A5\u518D\u53D1\u4E00\u6B21\uFF1B\u4E00\u76F4\u4E0D\u884C\u5C31\u53D1 /new \u5F00\u4E2A\u65B0\u4F1A\u8BDD\u3002",
|
|
139312
139797
|
turnFailedNoDetail: "\u8FD9\u4E00\u8F6E\u6CA1\u80FD\u5B8C\u6210\u3002\u53C2\u8003\u53F7 {{ref}}\u3002\u53EF\u4EE5\u76F4\u63A5\u518D\u53D1\u4E00\u6B21\uFF1B\u4E00\u76F4\u4E0D\u884C\u5C31\u53D1 /new \u5F00\u4E2A\u65B0\u4F1A\u8BDD\u3002",
|
|
139313
139798
|
turnFailedBrief: "\u8FD9\u4E00\u8F6E\u6CA1\u80FD\u5B8C\u6210\uFF1A{{detail}}\n\u53EF\u4EE5\u76F4\u63A5\u518D\u53D1\u4E00\u6B21\uFF1B\u4E00\u76F4\u4E0D\u884C\u5C31\u53D1 /new \u5F00\u4E2A\u65B0\u4F1A\u8BDD\u3002",
|
|
@@ -139324,9 +139809,11 @@ var zh_CN_default = {
|
|
|
139324
139809
|
attachmentsNoTeam: "\u6587\u4EF6\u8FD8\u53D1\u4E0D\u5230\u56E2\u961F\uFF0C\u8FD9\u6B21\u6CA1\u5E26\u4E0A\u3002\u53D1 /thread \u56DE\u5230\u666E\u901A\u4F1A\u8BDD\u3002",
|
|
139325
139810
|
unsupportedType: "\u6211\u8FD8\u4E0D\u80FD\u5904\u7406 {{type}} \u7C7B\u578B\u7684\u6D88\u606F\u3002",
|
|
139326
139811
|
unsupportedFolder: "\u6587\u4EF6\u5939\u6CA1\u6CD5\u4E0B\u8F7D\uFF0C\u8BF7\u6253\u5305\u6210\u538B\u7F29\u6587\u4EF6\u518D\u53D1\u3002",
|
|
139812
|
+
unsupportedVoice: "\u6682\u65F6\u4E0D\u652F\u6301\u8BED\u97F3\uFF0C\u8BF7\u53D1\u6587\u5B57\u3002",
|
|
139327
139813
|
sendingFile: "\u6B63\u5728\u53D1\u9001 {{name}}\uFF1A",
|
|
139328
139814
|
notAllowedYet: "\u4F60\u8FD8\u4E0D\u5728\u5141\u8BB8\u540D\u5355\u91CC\uFF0C\u4F60\u7684\u7528\u6237 id \u662F {{id}}\uFF0C\u8BF7\u628A\u5B83\u586B\u8FDB \u8BBE\u7F6E \u2192 \u63D2\u4EF6 \u2192 \u804A\u5929 \u2192 \u8FD9\u4E2A\u673A\u5668\u4EBA\u7684\u300C\u5141\u8BB8\u7684\u4EBA\u300D\u3002",
|
|
139329
|
-
received: "\u6536\u5230\uFF0C\u5728\u5904\u7406\u3002"
|
|
139815
|
+
received: "\u6536\u5230\uFF0C\u5728\u5904\u7406\u3002",
|
|
139816
|
+
welcome: "\u5DF2\u8FDE\u4E0A\u3002\u76F4\u63A5\u53D1\u6587\u5B57\u6216\u56FE\u7247\u5C31\u80FD\u5F00\u59CB\uFF0C\u53D1 /help \u770B\u80FD\u7528\u7684\u547D\u4EE4\u3002"
|
|
139330
139817
|
},
|
|
139331
139818
|
approval: {
|
|
139332
139819
|
allow: "\u5141\u8BB8",
|
|
@@ -139630,6 +140117,7 @@ var zh_CN_default = {
|
|
|
139630
140117
|
}
|
|
139631
140118
|
},
|
|
139632
140119
|
repo: {
|
|
140120
|
+
autoSelected: "\u4F7F\u7528\u9879\u76EE\u300C{{title}}\u300D\u3002",
|
|
139633
140121
|
needProject: "\u8FD9\u4E2A\u804A\u5929\u8FD8\u6CA1\u9009\u9879\u76EE\uFF0C\u9009\u5B9A\u4E4B\u524D\u6211\u4E0D\u5F00\u5DE5\u3002\u9009\u4E00\u4E2A\u5C31\u5F00\u59CB\u3002",
|
|
139634
140122
|
currentLine: "\u8FD9\u4E2A\u804A\u5929\u73B0\u5728\u5728\u9879\u76EE\u300C{{title}}\u300D\u4E0A\u3002",
|
|
139635
140123
|
noProjects: "\u8FD9\u53F0\u673A\u5668\u4E0A\u8FD8\u6CA1\u6709\u9879\u76EE\uFF0C\u5148\u5230 Ycodium \u91CC\u6DFB\u52A0\u4E00\u4E2A\u3002",
|
|
@@ -139660,21 +140148,41 @@ var zh_CN_default = {
|
|
|
139660
140148
|
failed: "\u8FDE\u63A5\u5931\u8D25",
|
|
139661
140149
|
notSaved: "\u8BF7\u5148\u4FDD\u5B58\u8FD9\u4E00\u884C\u518D\u68C0\u67E5\u8FDE\u63A5\uFF08\u51ED\u636E\u53EA\u4ECE\u5DF2\u4FDD\u5B58\u914D\u7F6E\u8BFB\u53D6\uFF09\u3002",
|
|
139662
140150
|
hint: {
|
|
140151
|
+
connecting: "\u8FD8\u5728\u8FDE\u63A5\uFF0C\u4E00\u822C 20 \u79D2\u5185\u4F1A\u6709\u7ED3\u679C\uFF0C\u7A0D\u540E\u518D\u68C0\u67E5\u4E00\u6B21\u3002",
|
|
139663
140152
|
badToken: "\u8FD9\u4E0D\u50CF\u673A\u5668\u4EBA\u7684 token\u3002\u5230 BotFather \u91CD\u65B0\u590D\u5236\u4E00\u4EFD\uFF1A\u6570\u5B57\u3001\u5192\u53F7\uFF0C\u540E\u9762\u662F\u5BC6\u94A5\u3002",
|
|
139664
|
-
unauthorized: "Telegram \u4E0D\u8BA4\u8FD9\u4E2A token\uFF0C\u53EF\u80FD\u5DF2\u7ECF\u4F5C\u5E9F\u3002\u5230 BotFather \u53D6\u73B0\u5728\u7684\u90A3\u4E00\u4EFD\u3002",
|
|
140153
|
+
unauthorized: "Telegram \u4E0D\u8BA4\u8FD9\u4E2A token\uFF0C\u53EF\u80FD\u5DF2\u7ECF\u4F5C\u5E9F\u3002\u5230 BotFather \u53D6\u73B0\u5728\u7684\u90A3\u4E00\u4EFD\u3002\u6539\u597D\u540E\u5728\u8FD9\u91CC\u4FDD\u5B58\uFF0C\u673A\u5668\u4EBA\u4F1A\u81EA\u52A8\u91CD\u65B0\u8FDE\u63A5\uFF1B\u6CA1\u6709\u8981\u6539\u7684\u4E5F\u53EF\u4EE5\u70B9\u4E0B\u65B9\u7684\u300C\u91CD\u65B0\u8FDE\u63A5\u300D\u3002",
|
|
139665
140154
|
notBot: "\u8FD9\u4E2A token \u4E0D\u662F\u673A\u5668\u4EBA\u7684\u3002\u8BF7\u7528 BotFather \u7ED9\u7684\u90A3\u4E00\u4EFD\u3002",
|
|
139666
140155
|
webhookActive: "\u8FD9\u4E2A\u673A\u5668\u4EBA\u88AB\u522B\u7684\u670D\u52A1\u8BBE\u4E86 webhook\uFF0C\u5728\u8FD9\u91CC\u6536\u4E0D\u5230\u6D88\u606F\u3002\u5148\u5230\u90A3\u8FB9\u64A4\u6389 webhook\uFF0C\u518D\u68C0\u67E5\u4E00\u6B21\u3002",
|
|
140156
|
+
larkCredentials: "\u98DE\u4E66\u4E0D\u8BA4\u8FD9\u4E2A App ID \u6216 App Secret\u3002\u5230\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u8FD9\u4E2A\u4F01\u4E1A\u81EA\u5EFA\u5E94\u7528\u7684\u300C\u51ED\u8BC1\u4E0E\u57FA\u7840\u4FE1\u606F\u300D\u91CD\u65B0\u590D\u5236\u4E24\u9879\u3002\u6539\u597D\u540E\u5728\u8FD9\u91CC\u4FDD\u5B58\uFF0C\u673A\u5668\u4EBA\u4F1A\u81EA\u52A8\u91CD\u65B0\u8FDE\u63A5\uFF1B\u6CA1\u6709\u8981\u6539\u7684\u4E5F\u53EF\u4EE5\u70B9\u4E0B\u65B9\u7684\u300C\u91CD\u65B0\u8FDE\u63A5\u300D\u3002",
|
|
140157
|
+
larkScope: "\u8FD9\u4E2A\u5E94\u7528\u7F3A\u5C11\u6240\u9700\u6743\u9650\u3002\u5230\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u7684\u300C\u6743\u9650\u7BA1\u7406\u300D\uFF0C\u6DFB\u52A0\u8BE6\u60C5\u4E2D\u5217\u51FA\u7684\u6743\u9650\uFF0C\u518D\u53D1\u5E03\u4E00\u4E2A\u7248\u672C\u3002",
|
|
140158
|
+
larkBotOff: "\u8FD9\u4E2A\u5E94\u7528\u6CA1\u6709\u542F\u7528\u673A\u5668\u4EBA\u80FD\u529B\u3002\u5230\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u7684\u300C\u5E94\u7528\u80FD\u529B \u2192 \u673A\u5668\u4EBA\u300D\u542F\u7528\u673A\u5668\u4EBA\uFF0C\u518D\u53D1\u5E03\u4E00\u4E2A\u7248\u672C\u3002",
|
|
140159
|
+
larkAppUnavailable: "\u8FD9\u4E2A\u5E94\u7528\u5728\u5F53\u524D\u4F01\u4E1A\u4E0D\u53EF\u7528\u3002\u5230\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u786E\u8BA4\u5E94\u7528\u5DF2\u542F\u7528\u5E76\u53D1\u5E03\uFF0C\u518D\u8BF7\u7BA1\u7406\u5458\u786E\u8BA4\u5F53\u524D\u4F01\u4E1A\u5DF2\u5B89\u88C5\u5E76\u542F\u7528\u8FD9\u4E2A\u5E94\u7528\u3002",
|
|
140160
|
+
wecomBotCredentials: "\u4F01\u4E1A\u5FAE\u4FE1\u4E0D\u8BA4\u8FD9\u4E2A Bot ID \u6216 Secret\u3002\u5230\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u8FD9\u4E2A\u667A\u80FD\u673A\u5668\u4EBA\u7684\u914D\u7F6E\u9875\uFF0C\u9009\u62E9\u300CAPI \u6A21\u5F0F \u2192 \u957F\u8FDE\u63A5\u300D\uFF0C\u91CD\u65B0\u590D\u5236 Bot ID \u548C Secret\uFF1B\u8FD9\u91CC\u7528\u7684\u4E0D\u662F\u56DE\u8C03\u6A21\u5F0F\u7684 Token \u6216 EncodingAESKey\u3002\u6539\u597D\u540E\u5728\u8FD9\u91CC\u4FDD\u5B58\uFF0C\u667A\u80FD\u673A\u5668\u4EBA\u4F1A\u81EA\u52A8\u91CD\u65B0\u8FDE\u63A5\uFF1B\u6CA1\u6709\u8981\u6539\u7684\u4E5F\u53EF\u4EE5\u70B9\u4E0B\u65B9\u7684\u300C\u91CD\u65B0\u8FDE\u63A5\u300D\u3002",
|
|
140161
|
+
wecomLongConnOff: "\u8FD9\u4E2A\u667A\u80FD\u673A\u5668\u4EBA\u6CA1\u6709\u5F00\u542F\u957F\u8FDE\u63A5\u6A21\u5F0F\u3002\u5230\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u8FD9\u4E2A\u667A\u80FD\u673A\u5668\u4EBA\u7684\u914D\u7F6E\u9875\u9009\u62E9\u300CAPI \u6A21\u5F0F \u2192 \u957F\u8FDE\u63A5\u300D\u5E76\u4FDD\u5B58\uFF0C\u8FD9\u91CC\u4F1A\u5728\u4E00\u5206\u949F\u5185\u81EA\u52A8\u91CD\u65B0\u8FDE\u4E0A\uFF1B\u4E5F\u53EF\u4EE5\u76F4\u63A5\u70B9\u4E0B\u65B9\u7684\u300C\u91CD\u65B0\u8FDE\u63A5\u300D\u3002",
|
|
140162
|
+
wecomTakenOver: "\u53E6\u4E00\u53F0\u673A\u5668\u6216\u7A0B\u5E8F\u5728\u7528\u540C\u4E00\u4E2A Bot ID\u3002\u8BA9\u5B83\u505C\u7528\u8FD9\u4E2A\u667A\u80FD\u673A\u5668\u4EBA\u540E\uFF0C\u70B9\u4E0B\u65B9\u7684\u300C\u91CD\u65B0\u8FDE\u63A5\u300D\u3002",
|
|
139667
140163
|
wecomTrustedIp: "\u628A\u8FD9\u53F0\u673A\u5668\u7684\u51FA\u7AD9\u516C\u7F51 IP \u52A0\u8FDB\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u8FD9\u4E2A\u5E94\u7528\u7684\u4F01\u4E1A\u53EF\u4FE1 IP\u3002",
|
|
139668
140164
|
wecomInvalidUser: "\u786E\u8BA4\u6536\u4EF6\u4EBA\u3001\u90E8\u95E8\u6216\u6807\u7B7E\u5B58\u5728\uFF0C\u5E76\u5728\u8FD9\u4E2A\u81EA\u5EFA\u5E94\u7528\u7684\u53EF\u89C1\u8303\u56F4\u5185\u3002",
|
|
139669
|
-
wecomInvalidAgent: "\u6838\u5BF9\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u8FD9\u4E2A\u81EA\u5EFA\u5E94\u7528\u7684 AgentId\u3002"
|
|
140165
|
+
wecomInvalidAgent: "\u6838\u5BF9\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u8FD9\u4E2A\u81EA\u5EFA\u5E94\u7528\u7684 AgentId\u3002",
|
|
140166
|
+
wecomCallbackNotSeen: "\u51ED\u636E\u6709\u6548\uFF0C\u4F46\u81EA\u672C\u6B21\u542F\u52A8\u4EE5\u6765\u8FD8\u6CA1\u6536\u5230\u8FC7\u901A\u8FC7\u6821\u9A8C\u7684\u56DE\u8C03\u3002\u5230\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u8FD9\u4E2A\u5E94\u7528\u7684\u300C\u63A5\u6536\u6D88\u606F\u300D\uFF0C\u586B\u5165\u673A\u5668\u4EBA\u8BBE\u7F6E\u5361\u4E0A\u663E\u793A\u7684\u56DE\u8C03\u5730\u5740\u5E76\u4FDD\u5B58\uFF1B\u4F01\u5FAE\u4F1A\u53D1\u6765\u4E00\u6B21\u6821\u9A8C\u8BF7\u6C42\u3002\u8FD9\u53F0\u673A\u5668\u5FC5\u987B\u80FD\u88AB\u4F01\u5FAE\u4ECE\u516C\u7F51\u8BBF\u95EE\u5230\u3002",
|
|
140167
|
+
wecomCallbackSignature: "\u6536\u5230\u8FC7\u56DE\u8C03\uFF0C\u4F46\u7B7E\u540D\u6821\u9A8C\u6216\u89E3\u5BC6\u5931\u8D25\uFF1B\u81EA\u672C\u6B21\u542F\u52A8\u4EE5\u6765\u8FD8\u6CA1\u6536\u5230\u8FC7\u901A\u8FC7\u6821\u9A8C\u7684\u56DE\u8C03\u3002\u8BF7\u628A\u56DE\u8C03 Token \u548C EncodingAESKey \u4E0E\u4F01\u4E1A\u5FAE\u4FE1\u540E\u53F0\u8FD9\u4E2A\u5E94\u7528\u7684\u300C\u63A5\u6536\u6D88\u606F\u300D\u8BBE\u7F6E\u4FDD\u6301\u4E00\u81F4\uFF0C\u518D\u5230\u540E\u53F0\u4FDD\u5B58\u4E00\u6B21\u4EE5\u91CD\u65B0\u6821\u9A8C\u3002"
|
|
139670
140168
|
}
|
|
139671
140169
|
},
|
|
140170
|
+
reconnect: {
|
|
140171
|
+
label: "\u91CD\u65B0\u8FDE\u63A5",
|
|
140172
|
+
description: "\u6309\u5DF2\u4FDD\u5B58\u7684\u8BBE\u7F6E\u65AD\u5F00\u5E76\u91CD\u65B0\u8FDE\u63A5\u8FD9\u4E2A\u673A\u5668\u4EBA\u3002",
|
|
140173
|
+
notAvailable: "\u8FD9\u4E2A\u5E73\u53F0\u8FD8\u4E0D\u80FD\u8FDE\u63A5\uFF0C\u91CD\u65B0\u8FDE\u63A5\u4E5F\u8FDE\u4E0D\u4E0A\u3002",
|
|
140174
|
+
running: "\u6B63\u5728\u91CD\u65B0\u8FDE\u63A5 {{rowId}} \u2026",
|
|
140175
|
+
done: "\u5DF2\u5F00\u59CB\u91CD\u65B0\u8FDE\u63A5\uFF0C\u7ED3\u679C\u8BF7\u770B\u673A\u5668\u4EBA\u7684\u8FDE\u63A5\u72B6\u6001\u3002",
|
|
140176
|
+
notSaved: "\u8BF7\u5148\u4FDD\u5B58\u8FD9\u4E00\u884C\u518D\u91CD\u65B0\u8FDE\u63A5\uFF0C\u52A8\u4F5C\u53EA\u4F7F\u7528\u5DF2\u4FDD\u5B58\u7684\u8BBE\u7F6E\u3002",
|
|
140177
|
+
notConfigured: "\u8BF7\u5148\u8865\u5168\u5E76\u4FDD\u5B58\u8FD9\u4E2A\u673A\u5668\u4EBA\u7684\u8BBE\u7F6E\uFF0C\u518D\u91CD\u65B0\u8FDE\u63A5\u3002",
|
|
140178
|
+
duplicate: "\u53E6\u4E00\u4E2A\u673A\u5668\u4EBA\u884C\u6B63\u5728\u4F7F\u7528\u8FD9\u4EFD\u51ED\u636E\uFF0C\u8BF7\u5148\u5904\u7406\u91CD\u590D\u914D\u7F6E\u518D\u91CD\u65B0\u8FDE\u63A5\u3002",
|
|
140179
|
+
inactive: "\u804A\u5929\u6865\u5DF2\u505C\u7528\uFF0C\u8BF7\u5148\u542F\u7528\u518D\u91CD\u65B0\u8FDE\u63A5\u3002"
|
|
140180
|
+
},
|
|
139672
140181
|
copyDiagnostics: {
|
|
139673
140182
|
label: "\u590D\u5236\u8BCA\u65AD",
|
|
139674
140183
|
description: "\u8DD1\u4E00\u6B21\u8FDE\u63A5\u63A2\u6D4B\uFF0C\u751F\u6210\u53EF\u590D\u5236\u7684\u8BCA\u65AD\u6587\u672C\uFF08\u4E0D\u542B\u5BC6\u94A5\uFF09\u3002",
|
|
139675
140184
|
running: "\u6B63\u5728\u751F\u6210\u8BCA\u65AD {{rowId}} \u2026",
|
|
139676
|
-
done: "\u8BCA\u65AD\u5DF2\u751F\u6210\uFF0C\u89C1\u4E0B\u65B9\u8BE6\u60C5",
|
|
139677
|
-
failed: "\u8BCA\u65AD\u5931\u8D25",
|
|
140185
|
+
done: "\u8BCA\u65AD\u62A5\u544A\u5DF2\u751F\u6210\uFF0C\u89C1\u4E0B\u65B9\u8BE6\u60C5",
|
|
139678
140186
|
notSaved: "\u8BF7\u5148\u4FDD\u5B58\u8FD9\u4E00\u884C\u518D\u590D\u5236\u8BCA\u65AD\u3002"
|
|
139679
140187
|
},
|
|
139680
140188
|
diagnostic: {
|
|
@@ -139682,16 +140190,23 @@ var zh_CN_default = {
|
|
|
139682
140190
|
},
|
|
139683
140191
|
larkRepair: {
|
|
139684
140192
|
label: "\u8865\u5168\u6743\u9650",
|
|
139685
|
-
description: "\
|
|
140193
|
+
description: "\u5217\u51FA\u7F3A\u5C11\u7684\u5FC5\u9700\u6743\u9650\u548C\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u7533\u8BF7\u94FE\u63A5\uFF0C\u518D\u7531\u7BA1\u7406\u5458\u624B\u52A8\u53D1\u5E03\u5E94\u7528\u7248\u672C\u3002",
|
|
139686
140194
|
step: {
|
|
139687
|
-
|
|
139688
|
-
|
|
139689
|
-
|
|
139690
|
-
|
|
139691
|
-
|
|
139692
|
-
|
|
139693
|
-
|
|
139694
|
-
|
|
140195
|
+
token: "\u7528\u5DF2\u4FDD\u5B58\u7684\u51ED\u636E\u6362\u53D6 tenant token",
|
|
140196
|
+
scopes: "\u6838\u5BF9\u5E94\u7528\u5DF2\u5F00\u901A\u7684\u79DF\u6237\u6743\u9650"
|
|
140197
|
+
},
|
|
140198
|
+
missing: "\u6709\u5FC5\u9700\u6743\u9650\u5C1A\u672A\u5F00\u901A\u3002",
|
|
140199
|
+
allGranted: "\u5FC5\u9700\u6743\u9650\u90FD\u5DF2\u5F00\u901A\u3002",
|
|
140200
|
+
unverified: "\u65E0\u6CD5\u81EA\u52A8\u6838\u5BF9\u6743\u9650\u3002",
|
|
140201
|
+
missingScopes: "\u7F3A\u5C11\u7684\u6743\u9650\uFF1A\n{{scopes}}",
|
|
140202
|
+
requiredScopes: "\u5FC5\u9700\u6743\u9650\u6E05\u5355\uFF0C\u8BF7\u9010\u9879\u624B\u52A8\u6838\u5BF9\uFF1A\n{{scopes}}\n\n\u5E94\u7528\u7F3A\u5C11 application:application:self_manage \u65F6\u65E0\u6CD5\u67E5\u8BE2\u81EA\u8EAB\u5DF2\u5F00\u901A\u7684\u6743\u9650\uFF0C\u6240\u4EE5\u8FD9\u4EFD\u6E05\u5355\u6CA1\u80FD\u81EA\u52A8\u6838\u5BF9\u3002",
|
|
140203
|
+
openApplyLink: "\u6253\u5F00\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u7533\u8BF7\u8FD9\u4E9B\u6743\u9650",
|
|
140204
|
+
openCheckLink: "\u6253\u5F00\u98DE\u4E66\u5F00\u653E\u5E73\u53F0\u6838\u5BF9\u5E76\u7533\u8BF7\u8FD9\u4E9B\u6743\u9650",
|
|
140205
|
+
applyLink: "\u5F00\u653E\u5E73\u53F0\u5730\u5740\uFF1A\n{{url}}",
|
|
140206
|
+
publishHint: "\u8BF7\u5E94\u7528\u7BA1\u7406\u5458\u6253\u5F00\u4E0A\u8FF0\u94FE\u63A5\u52FE\u9009\u6743\u9650\uFF0C\u518D\u5230\u300C\u5E94\u7528\u53D1\u5E03 \u2192 \u7248\u672C\u7BA1\u7406\u4E0E\u53D1\u5E03 \u2192 \u521B\u5EFA\u7248\u672C\u300D\u53D1\u5E03\u65B0\u7248\u672C\uFF0C\u56DE\u6765\u70B9\u300C\u68C0\u67E5\u8FDE\u63A5\u300D\u786E\u8BA4\u3002",
|
|
140207
|
+
botHint: "\u542F\u7528\u673A\u5668\u4EBA\u8BF7\u5230\u300C\u5E94\u7528 \u2192 \u6DFB\u52A0\u5E94\u7528\u80FD\u529B \u2192 \u673A\u5668\u4EBA\u300D\uFF1B\u8FD9\u91CC\u4E0D\u68C0\u67E5\u673A\u5668\u4EBA\u80FD\u529B\u3002",
|
|
140208
|
+
eventHint: "\u6536\u6D88\u606F\u9700\u8981\u5728\u300C\u4E8B\u4EF6\u4E0E\u56DE\u8C03\u300D\u91CC\u628A\u4E8B\u4EF6\u8BA2\u9605\u548C\u56DE\u8C03\u8BA2\u9605\u90FD\u8BBE\u4E3A\u300C\u4F7F\u7528\u957F\u8FDE\u63A5\u300D\uFF0C\u518D\u6DFB\u52A0\u4E0B\u9762\u8FD9\u4E9B\uFF08\u8FD9\u91CC\u4E0D\u68C0\u67E5\uFF09\uFF1A\n\u4E8B\u4EF6\uFF1A{{events}}\n\u56DE\u8C03\uFF1A{{callbacks}}",
|
|
140209
|
+
failed: "\u672A\u80FD\u6838\u5BF9\u5E94\u7528\u7684\u5FC5\u9700\u6743\u9650\uFF0C\u8BF7\u67E5\u770B\u9519\u8BEF\u8BE6\u60C5\u540E\u91CD\u8BD5\u3002",
|
|
139695
140210
|
notLark: "\u8FD9\u4E00\u884C\u4E0D\u662F\u5DF2\u4FDD\u5B58\u7684\u98DE\u4E66\u673A\u5668\u4EBA\u3002"
|
|
139696
140211
|
}
|
|
139697
140212
|
},
|
|
@@ -139738,11 +140253,13 @@ var zh_CN_default = {
|
|
|
139738
140253
|
connection: {
|
|
139739
140254
|
failed: "\u4E0A\u6B21\u8FDE\u63A5\u6CA1\u6709\u6210\u529F\u3002",
|
|
139740
140255
|
details: "\u8BE6\u60C5\uFF1A{{error}}",
|
|
140256
|
+
reconnect: "\u53EF\u4EE5\u70B9\u4E0B\u65B9\u7684\u300C\u91CD\u65B0\u8FDE\u63A5\u300D\u518D\u8BD5\u3002",
|
|
139741
140257
|
reason: {
|
|
139742
140258
|
"not-available": "\u8FD9\u4E2A\u5E73\u53F0\u8FD8\u4E0D\u80FD\u8FDE\u63A5\u3002",
|
|
139743
140259
|
"invalid-app-id": "App ID \u683C\u5F0F\u4E0D\u6B63\u786E\u3002",
|
|
139744
140260
|
disconnected: "\u548C\u5E73\u53F0\u7684\u8FDE\u63A5\u65AD\u4E86\u3002",
|
|
139745
140261
|
"subscription-failed": "\u5E73\u53F0\u6CA1\u6709\u786E\u8BA4\u8BA2\u9605\u3002",
|
|
140262
|
+
"taken-over": "\u8FD9\u91CC\u7684\u8FDE\u63A5\u5DF2\u88AB\u522B\u5904\u53D6\u4EE3\uFF0C\u5DF2\u505C\u6B62\u81EA\u52A8\u91CD\u8FDE\u3002",
|
|
139746
140263
|
"token-unavailable": "\u6CA1\u80FD\u6362\u5230\u8BBF\u95EE\u4EE4\u724C\u3002",
|
|
139747
140264
|
"credentials-rejected": "\u5E73\u53F0\u62D2\u7EDD\u4E86\u51ED\u636E\u3002"
|
|
139748
140265
|
},
|
|
@@ -139766,7 +140283,7 @@ var zh_CN_default = {
|
|
|
139766
140283
|
done: "\u5B8C\u6210\u3002",
|
|
139767
140284
|
close: "\u5173\u95ED",
|
|
139768
140285
|
failedToRun: "\u6CA1\u80FD\u5728\u8FD9\u53F0\u673A\u5668\u4E0A\u8FD0\u884C\u3002",
|
|
139769
|
-
saveFirst: "\u5148\u4FDD\u5B58\u6216\u64A4\u9500\u6539\u52A8\u3002\u8FD9\
|
|
140286
|
+
saveFirst: "\u5148\u4FDD\u5B58\u6216\u64A4\u9500\u6539\u52A8\u3002\u8FD9\u4E9B\u52A8\u4F5C\u4F7F\u7528\u5DF2\u4FDD\u5B58\u7684\u8BBE\u7F6E\u3002",
|
|
139770
140287
|
qrTitle: "\u7528\u98DE\u4E66 App \u626B\u7684\u4E8C\u7EF4\u7801"
|
|
139771
140288
|
},
|
|
139772
140289
|
step: {
|
|
@@ -139792,6 +140309,24 @@ var zh_CN_default = {
|
|
|
139792
140309
|
callbackFollowsKeys: "\u6539\u4E86 Token \u6216 EncodingAESKey\uFF0C\u8FD9\u4E2A\u5730\u5740\u4F1A\u53D8\uFF0C\u8981\u91CD\u65B0\u8D34\u8FDB\u4F01\u4E1A\u5FAE\u4FE1\u3002"
|
|
139793
140310
|
}
|
|
139794
140311
|
},
|
|
140312
|
+
mergeForward: {
|
|
140313
|
+
title: "[\u5408\u5E76\u8F6C\u53D1\u7684\u804A\u5929\u8BB0\u5F55\uFF0C\u5171 {{count}} \u6761]",
|
|
140314
|
+
nested: "[\u5D4C\u5957\u8F6C\u53D1\uFF0C\u5171 {{count}} \u6761]",
|
|
140315
|
+
someone: "\u67D0\u4EBA",
|
|
140316
|
+
bot: "\u673A\u5668\u4EBA",
|
|
140317
|
+
unknownTime: "[\u65F6\u95F4\u672A\u77E5]",
|
|
140318
|
+
resource: {
|
|
140319
|
+
image: "\u56FE\u7247",
|
|
140320
|
+
file: "\u6587\u4EF6",
|
|
140321
|
+
audio: "\u8BED\u97F3",
|
|
140322
|
+
video: "\u89C6\u9891",
|
|
140323
|
+
folder: "\u6587\u4EF6\u5939"
|
|
140324
|
+
},
|
|
140325
|
+
unavailable: "[{{type}}\uFF1A\u6CA1\u80FD\u53D6\u5230]",
|
|
140326
|
+
unavailableNamed: "[{{type}} {{name}}\uFF1A\u6CA1\u80FD\u53D6\u5230]",
|
|
140327
|
+
omitted: "\u2026\u2026\u8FD8\u6709 {{count}} \u6761\u6CA1\u5217\u51FA",
|
|
140328
|
+
truncated: "\u2026\u2026[\u6B63\u6587\u5DF2\u622A\u65AD]"
|
|
140329
|
+
},
|
|
139795
140330
|
team: {
|
|
139796
140331
|
noRoster: "\u8FD9\u4E2A\u9879\u76EE\u8FD8\u6CA1\u6709\u56E2\u961F\u540D\u5355\uFF0C\u5148\u5728 Teams \u6807\u7B7E\u91CC\u4FDD\u5B58\u540D\u5355\u5E76\u9009\u4E00\u540D\u8D1F\u8D23\u4EBA\u3002",
|
|
139797
140332
|
unavailable: "\u6682\u65F6\u8BFB\u4E0D\u5230\u8FD9\u4E2A\u9879\u76EE\u7684\u56E2\u961F\u540D\u5355\uFF0C\u8BF7\u786E\u8BA4 Teams \u63D2\u4EF6\u5DF2\u542F\u7528\u540E\u91CD\u8BD5\u3002",
|
|
@@ -140069,7 +140604,10 @@ var makeNotifications = (core, deps) => {
|
|
|
140069
140604
|
const said = threadsWithSomethingToReport.delete(threadId);
|
|
140070
140605
|
await ready;
|
|
140071
140606
|
const targets = targetsForProject(threadId, await projectOfThread(threadId));
|
|
140072
|
-
const
|
|
140607
|
+
const owes = Object.values(conclusionDebtsOf(core.state)).some(
|
|
140608
|
+
(debt2) => debt2.threadId === threadId
|
|
140609
|
+
);
|
|
140610
|
+
const { debt, handled, execution } = said || owes || targets.length > 0 ? await conclusions.forTurn(threadId) : { debt: void 0, handled: false, execution: null };
|
|
140073
140611
|
if (debt !== void 0) {
|
|
140074
140612
|
const binding = bindingForChatKey(debt.chatKey);
|
|
140075
140613
|
if (binding !== void 0 && !streamsTo(binding, threadId) && execution?.session?.activeTurnId == null && execution?.latestTurn?.state !== "running" && execution?.latestTurn?.state !== "error" && payload.session?.status !== "error") {
|
|
@@ -140254,7 +140792,7 @@ var PANEL_PAGE_SIZE = 8;
|
|
|
140254
140792
|
var PANEL_CARD_LIMIT = 200;
|
|
140255
140793
|
var LABEL_MAX_CHARS = 60;
|
|
140256
140794
|
var PICK_KINDS = /* @__PURE__ */ new Set(["sessions", "models", "projects"]);
|
|
140257
|
-
var
|
|
140795
|
+
var clip3 = (label) => {
|
|
140258
140796
|
const chars = Array.from(label);
|
|
140259
140797
|
return chars.length <= LABEL_MAX_CHARS ? label : `${chars.slice(0, LABEL_MAX_CHARS - 1).join("")}\u2026`;
|
|
140260
140798
|
};
|
|
@@ -140279,7 +140817,7 @@ var makePanelController = (deps) => {
|
|
|
140279
140817
|
}
|
|
140280
140818
|
};
|
|
140281
140819
|
const button = (label, op, arg, danger) => ({
|
|
140282
|
-
label:
|
|
140820
|
+
label: clip3(label),
|
|
140283
140821
|
action: { op, arg },
|
|
140284
140822
|
...danger === true ? { danger } : {}
|
|
140285
140823
|
});
|
|
@@ -140698,6 +141236,20 @@ var makeFailureNotices = (core) => {
|
|
|
140698
141236
|
const heldForCard = /* @__PURE__ */ new Map();
|
|
140699
141237
|
const reasonOf2 = (detail) => detail?.trim() ?? "";
|
|
140700
141238
|
return {
|
|
141239
|
+
/** 派发的参考号沿用同一次请求;原始错误只进日志。 */
|
|
141240
|
+
forDispatchFailure: (threadId, requestId, error) => {
|
|
141241
|
+
const reference2 = stableDiagnosticRef(
|
|
141242
|
+
"msg",
|
|
141243
|
+
(identityDigest(requestId, "turn") ?? requestId).replaceAll("-", "")
|
|
141244
|
+
);
|
|
141245
|
+
core.host.log.warn("chat could not start a turn", {
|
|
141246
|
+
threadId,
|
|
141247
|
+
requestId,
|
|
141248
|
+
reference: reference2,
|
|
141249
|
+
error: describeError(error)
|
|
141250
|
+
});
|
|
141251
|
+
return core.t("reply.turnFailedToStart", { ref: reference2 });
|
|
141252
|
+
},
|
|
140701
141253
|
/** 一次失败通知的那句话(带新参考号,同号进日志):实时失败事件与重启补发各取一次。 */
|
|
140702
141254
|
forFailure: (threadId, detail) => {
|
|
140703
141255
|
const reference2 = diagnosticRef("msg");
|
|
@@ -141473,7 +142025,7 @@ var makeSessions = (core) => {
|
|
|
141473
142025
|
|
|
141474
142026
|
// src/teamGoals.ts
|
|
141475
142027
|
import * as NodeCrypto4 from "node:crypto";
|
|
141476
|
-
var
|
|
142028
|
+
var isRecord3 = (value2) => typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
141477
142029
|
var teamGoalRequestId = (binding, messageId) => {
|
|
141478
142030
|
const identity = JSON.stringify([
|
|
141479
142031
|
"im",
|
|
@@ -141515,7 +142067,7 @@ var makeTeamGoals = (core) => {
|
|
|
141515
142067
|
requestId
|
|
141516
142068
|
}
|
|
141517
142069
|
});
|
|
141518
|
-
if (
|
|
142070
|
+
if (isRecord3(result) && result._tag === "TeamGoalRejection") {
|
|
141519
142071
|
await deliver(
|
|
141520
142072
|
binding,
|
|
141521
142073
|
t(
|
|
@@ -141524,7 +142076,7 @@ var makeTeamGoals = (core) => {
|
|
|
141524
142076
|
);
|
|
141525
142077
|
return null;
|
|
141526
142078
|
}
|
|
141527
|
-
if (!
|
|
142079
|
+
if (!isRecord3(result) || typeof result.leadThreadId !== "string" || result.leadThreadId === "" || result.leadName !== null && typeof result.leadName !== "string") {
|
|
141528
142080
|
throw new Error("team startGoal returned an invalid result");
|
|
141529
142081
|
}
|
|
141530
142082
|
topicRoot?.confirmConsumed();
|
|
@@ -141547,6 +142099,38 @@ var makeTeamGoals = (core) => {
|
|
|
141547
142099
|
return { hasRosterFor, startTeamGoal };
|
|
141548
142100
|
};
|
|
141549
142101
|
|
|
142102
|
+
// src/telegramAdapter.ts
|
|
142103
|
+
import * as NodeCrypto5 from "node:crypto";
|
|
142104
|
+
|
|
142105
|
+
// src/telegramApiResponse.ts
|
|
142106
|
+
var TelegramApiError = class extends Error {
|
|
142107
|
+
code;
|
|
142108
|
+
/** 限流(429)时平台给的等待秒数;别的错误没有。 */
|
|
142109
|
+
retryAfterSeconds;
|
|
142110
|
+
constructor(method, code2, description, retryAfterSeconds) {
|
|
142111
|
+
super(`telegram ${method} failed (${String(code2 ?? "?")}): ${description}`);
|
|
142112
|
+
this.name = "TelegramApiError";
|
|
142113
|
+
this.code = code2;
|
|
142114
|
+
this.retryAfterSeconds = retryAfterSeconds;
|
|
142115
|
+
}
|
|
142116
|
+
};
|
|
142117
|
+
var isTelegramCredentialsRejected = (error) => error instanceof TelegramApiError && (error.code === 401 || error.code === 404);
|
|
142118
|
+
var readTelegramResponse = async (method, response) => {
|
|
142119
|
+
const envelope = await response.json().catch((error) => {
|
|
142120
|
+
if (response.ok) throw error;
|
|
142121
|
+
return { ok: false, description: response.statusText || void 0 };
|
|
142122
|
+
});
|
|
142123
|
+
if (!response.ok || envelope?.ok !== true) {
|
|
142124
|
+
throw new TelegramApiError(
|
|
142125
|
+
method,
|
|
142126
|
+
response.status === 401 ? 401 : envelope?.error_code ?? (response.ok ? void 0 : response.status),
|
|
142127
|
+
envelope?.description ?? "no reason",
|
|
142128
|
+
envelope?.parameters?.retry_after
|
|
142129
|
+
);
|
|
142130
|
+
}
|
|
142131
|
+
return envelope.result;
|
|
142132
|
+
};
|
|
142133
|
+
|
|
141550
142134
|
// src/telegramApi.ts
|
|
141551
142135
|
var API_BASE = "https://api.telegram.org";
|
|
141552
142136
|
var POLL_TIMEOUT_SECONDS = 45;
|
|
@@ -141571,18 +142155,11 @@ var target = (chatId) => {
|
|
|
141571
142155
|
const { chat, thread } = splitChatId(chatId);
|
|
141572
142156
|
return { chat_id: chat, ...thread === null ? {} : { message_thread_id: thread } };
|
|
141573
142157
|
};
|
|
141574
|
-
var
|
|
141575
|
-
|
|
141576
|
-
|
|
141577
|
-
/** 限流(429)时平台给的等待秒数;别的错误没有。 */
|
|
141578
|
-
retryAfterSeconds;
|
|
141579
|
-
constructor(method, code2, description, retryAfterSeconds) {
|
|
141580
|
-
super(`telegram ${method} failed (${String(code2 ?? "?")}): ${description}`);
|
|
141581
|
-
this.name = "TelegramApiError";
|
|
141582
|
-
this.code = code2;
|
|
141583
|
-
this.retryAfterSeconds = retryAfterSeconds;
|
|
141584
|
-
}
|
|
142158
|
+
var replyParameters = (messageId) => {
|
|
142159
|
+
const id = Number(messageId);
|
|
142160
|
+
return messageId === void 0 || !Number.isSafeInteger(id) ? {} : { reply_parameters: { message_id: id, allow_sending_without_reply: true } };
|
|
141585
142161
|
};
|
|
142162
|
+
var chatOnly = (chatId) => ({ chat_id: splitChatId(chatId).chat });
|
|
141586
142163
|
var telegramSendVerdict = (error) => error instanceof TelegramApiError && (error.code === 429 || error.code === 503 && error.retryAfterSeconds !== void 0) ? {
|
|
141587
142164
|
retry: true,
|
|
141588
142165
|
afterMs: error.retryAfterSeconds === void 0 ? void 0 : error.retryAfterSeconds * 1e3
|
|
@@ -141617,7 +142194,6 @@ var resourceOf2 = (message) => {
|
|
|
141617
142194
|
};
|
|
141618
142195
|
}
|
|
141619
142196
|
const media = [
|
|
141620
|
-
[message.voice, "voice.ogg", "audio/ogg"],
|
|
141621
142197
|
[message.audio, "audio", "application/octet-stream"],
|
|
141622
142198
|
[message.video, "video.mp4", "video/mp4"],
|
|
141623
142199
|
[message.video_note, "video-note.mp4", "video/mp4"],
|
|
@@ -141637,7 +142213,15 @@ var resourceOf2 = (message) => {
|
|
|
141637
142213
|
return null;
|
|
141638
142214
|
};
|
|
141639
142215
|
var unsupportedTypeOf = (message) => {
|
|
141640
|
-
for (const type of [
|
|
142216
|
+
for (const type of [
|
|
142217
|
+
"voice",
|
|
142218
|
+
"sticker",
|
|
142219
|
+
"location",
|
|
142220
|
+
"venue",
|
|
142221
|
+
"contact",
|
|
142222
|
+
"poll",
|
|
142223
|
+
"dice"
|
|
142224
|
+
]) {
|
|
141641
142225
|
if (message[type] !== void 0) return type;
|
|
141642
142226
|
}
|
|
141643
142227
|
return null;
|
|
@@ -141691,24 +142275,17 @@ var makeTelegramApi = (input) => {
|
|
|
141691
142275
|
},
|
|
141692
142276
|
timeoutMs
|
|
141693
142277
|
);
|
|
141694
|
-
|
|
141695
|
-
if (envelope.ok !== true) {
|
|
141696
|
-
throw new TelegramApiError(
|
|
141697
|
-
method,
|
|
141698
|
-
envelope.error_code,
|
|
141699
|
-
envelope.description ?? "no reason",
|
|
141700
|
-
envelope.parameters?.retry_after
|
|
141701
|
-
);
|
|
141702
|
-
}
|
|
141703
|
-
return envelope.result;
|
|
142278
|
+
return readTelegramResponse(method, response);
|
|
141704
142279
|
};
|
|
141705
|
-
const upload = async (method, field, chatId, file) => {
|
|
142280
|
+
const upload = async (method, field, chatId, file, replyMessageId) => {
|
|
141706
142281
|
const form = new FormData();
|
|
141707
142282
|
const where = target(chatId);
|
|
141708
142283
|
form.set("chat_id", where.chat_id);
|
|
141709
142284
|
if (where.message_thread_id !== void 0) {
|
|
141710
142285
|
form.set("message_thread_id", String(where.message_thread_id));
|
|
141711
142286
|
}
|
|
142287
|
+
const reply = replyParameters(replyMessageId).reply_parameters;
|
|
142288
|
+
if (reply !== void 0) form.set("reply_parameters", JSON.stringify(reply));
|
|
141712
142289
|
form.set(field, new Blob([new Uint8Array(file.bytes)], { type: file.mimeType }), file.name);
|
|
141713
142290
|
const response = await send(
|
|
141714
142291
|
method,
|
|
@@ -141716,24 +142293,17 @@ var makeTelegramApi = (input) => {
|
|
|
141716
142293
|
{ method: "POST", body: form },
|
|
141717
142294
|
UPLOAD_TIMEOUT_MS
|
|
141718
142295
|
);
|
|
141719
|
-
|
|
141720
|
-
if (envelope.ok !== true) {
|
|
141721
|
-
throw new TelegramApiError(
|
|
141722
|
-
method,
|
|
141723
|
-
envelope.error_code,
|
|
141724
|
-
envelope.description ?? "no reason",
|
|
141725
|
-
envelope.parameters?.retry_after
|
|
141726
|
-
);
|
|
141727
|
-
}
|
|
142296
|
+
await readTelegramResponse(method, response);
|
|
141728
142297
|
};
|
|
141729
142298
|
const keyboard = (rows) => ({
|
|
141730
142299
|
inline_keyboard: rows.map(
|
|
141731
142300
|
(row) => row.map((button) => ({ text: button.text, callback_data: button.data }))
|
|
141732
142301
|
)
|
|
141733
142302
|
});
|
|
141734
|
-
const sendSingle = async (method, chatId, body, rows) => {
|
|
142303
|
+
const sendSingle = async (method, chatId, body, rows, replyMessageId) => {
|
|
141735
142304
|
const sent = await call(method, {
|
|
141736
142305
|
...target(chatId),
|
|
142306
|
+
...replyParameters(replyMessageId),
|
|
141737
142307
|
...body,
|
|
141738
142308
|
...rows === void 0 ? {} : { reply_markup: keyboard(rows) }
|
|
141739
142309
|
});
|
|
@@ -141793,10 +142363,11 @@ var makeTelegramApi = (input) => {
|
|
|
141793
142363
|
},
|
|
141794
142364
|
POLL_CALL_TIMEOUT_MS
|
|
141795
142365
|
),
|
|
141796
|
-
sendMessage: async (chatId, text4, retry) => {
|
|
141797
|
-
for (const chunk of splitForTelegram(text4)) {
|
|
142366
|
+
sendMessage: async (chatId, text4, retry, replyMessageId) => {
|
|
142367
|
+
for (const [index, chunk] of splitForTelegram(text4).entries()) {
|
|
142368
|
+
const reply = index === 0 ? replyMessageId : void 0;
|
|
141798
142369
|
await sendWithRetry(
|
|
141799
|
-
() =>
|
|
142370
|
+
() => sendSingle("sendMessage", chatId, { text: chunk }, void 0, reply),
|
|
141800
142371
|
telegramSendVerdict,
|
|
141801
142372
|
retry
|
|
141802
142373
|
);
|
|
@@ -141806,12 +142377,12 @@ var makeTelegramApi = (input) => {
|
|
|
141806
142377
|
* 发一条消息,回它的 id。调用方保证 `text` 不超过单条上限(边写边更新的回复要自己
|
|
141807
142378
|
* 在上限处翻篇,这里不替它切)。带 `rows` 就挂一块内联键盘。
|
|
141808
142379
|
*/
|
|
141809
|
-
sendOne: (chatId, text4, rows) => sendSingle("sendMessage", chatId, { text: text4 }, rows),
|
|
142380
|
+
sendOne: (chatId, text4, rows, replyMessageId) => sendSingle("sendMessage", chatId, { text: text4 }, rows, replyMessageId),
|
|
141810
142381
|
/**
|
|
141811
142382
|
* 发一条富文本,回它的 id。`markdown` 已是平台要的样子。带 `rows` 就挂一块内联键盘。
|
|
141812
142383
|
* 调用方保证不超富文本单条上限(这里不替它切)。
|
|
141813
142384
|
*/
|
|
141814
|
-
sendRichOne: (chatId, markdown, rows) => sendSingle("sendRichMessage", chatId, { rich_message: { markdown } }, rows),
|
|
142385
|
+
sendRichOne: (chatId, markdown, rows, replyMessageId) => sendSingle("sendRichMessage", chatId, { rich_message: { markdown } }, rows, replyMessageId),
|
|
141815
142386
|
/**
|
|
141816
142387
|
* 改一条已发出的消息。内容没变(`message is not modified`)当成功。`rows` 不给就是
|
|
141817
142388
|
* 去掉按钮:改了文字又要保留按钮的调用方得自己再传一次。
|
|
@@ -141823,20 +142394,21 @@ var makeTelegramApi = (input) => {
|
|
|
141823
142394
|
*/
|
|
141824
142395
|
editRich: (chatId, messageId, markdown, rows = []) => editSingle(chatId, messageId, { rich_message: { markdown } }, rows),
|
|
141825
142396
|
/** 带按钮的审批消息,一行一个按钮;回消息 id,之后才改得了它(平台没给就是 null)。 */
|
|
141826
|
-
sendButtons: (chatId, text4, buttons) => sendSingle(
|
|
142397
|
+
sendButtons: (chatId, text4, buttons, replyMessageId) => sendSingle(
|
|
141827
142398
|
"sendMessage",
|
|
141828
142399
|
chatId,
|
|
141829
142400
|
{ text: text4 },
|
|
141830
|
-
buttons.map((button) => [{ text: button.label, data: button.token }])
|
|
142401
|
+
buttons.map((button) => [{ text: button.label, data: button.token }]),
|
|
142402
|
+
replyMessageId
|
|
141831
142403
|
),
|
|
141832
142404
|
/** 答完点击,不然客户端上那个按钮一直转圈。带 `text` 时点击者会看到一句提示。 */
|
|
141833
142405
|
answerCallback: (callbackQueryId, text4) => call("answerCallbackQuery", {
|
|
141834
142406
|
callback_query_id: callbackQueryId,
|
|
141835
142407
|
...text4 === void 0 ? {} : { text: text4 }
|
|
141836
142408
|
}),
|
|
141837
|
-
sendPhoto: (chatId, photo) => upload("sendPhoto", "photo", chatId, photo),
|
|
142409
|
+
sendPhoto: (chatId, photo, replyMessageId) => upload("sendPhoto", "photo", chatId, photo, replyMessageId),
|
|
141838
142410
|
/** 以文件形式发(不压缩、不限尺寸比例):什么类型都行,音频与视频也走这条。 */
|
|
141839
|
-
sendDocument: (chatId, file) => upload("sendDocument", "document", chatId, file),
|
|
142411
|
+
sendDocument: (chatId, file, replyMessageId) => upload("sendDocument", "document", chatId, file, replyMessageId),
|
|
141840
142412
|
/** 取一个文件的字节。要两步:先换路径,再按路径下载。 */
|
|
141841
142413
|
fetchFile: async (fileId, maxBytes) => {
|
|
141842
142414
|
const file = await call("getFile", {
|
|
@@ -142016,7 +142588,7 @@ var toTelegramRichMarkdown = (value2) => {
|
|
|
142016
142588
|
|
|
142017
142589
|
// src/plainTurnText.ts
|
|
142018
142590
|
var RUNNING_LABEL_MAX_CHARS = 200;
|
|
142019
|
-
var
|
|
142591
|
+
var clip4 = (text4, max) => {
|
|
142020
142592
|
const chars = Array.from(text4);
|
|
142021
142593
|
return chars.length <= max ? text4 : `${chars.slice(0, max - 1).join("")}\u2026`;
|
|
142022
142594
|
};
|
|
@@ -142025,7 +142597,7 @@ var turnTextParts = (view, t) => {
|
|
|
142025
142597
|
if (view.omitted > 0) names.unshift(t("card.step.omitted", { count: view.omitted }));
|
|
142026
142598
|
const headLines = [view.title === "" ? t("card.untitled") : view.title];
|
|
142027
142599
|
if (names.length > 0) headLines.push(names.join(" \u2192 "));
|
|
142028
|
-
if (view.running !== null) headLines.push(`\u{1F527} ${
|
|
142600
|
+
if (view.running !== null) headLines.push(`\u{1F527} ${clip4(view.running, RUNNING_LABEL_MAX_CHARS)}`);
|
|
142029
142601
|
const body = view.body.trim();
|
|
142030
142602
|
const footLines = [];
|
|
142031
142603
|
if (view.status === "working") {
|
|
@@ -142048,7 +142620,7 @@ var makeTurnWriter = (deps) => {
|
|
|
142048
142620
|
const put = async (text4) => {
|
|
142049
142621
|
const bounded = text4.length <= deps.limit ? text4 : text4.slice(0, deps.limit);
|
|
142050
142622
|
if (live === null) {
|
|
142051
|
-
const id = await deps.messages.send(bounded);
|
|
142623
|
+
const id = await deps.messages.send(bounded, first === null);
|
|
142052
142624
|
if (id === null) throw new Error("telegram sent a message without an id");
|
|
142053
142625
|
live = id;
|
|
142054
142626
|
first ??= id;
|
|
@@ -142099,6 +142671,7 @@ var TELEGRAM_CARD_THROTTLE_MS = 1200;
|
|
|
142099
142671
|
var FLOOD_WAIT_MAX_SECONDS = 30;
|
|
142100
142672
|
var REMEMBERED_CARD_LIMIT = 200;
|
|
142101
142673
|
var tokenOf = (binding) => (binding.values["telegramToken"] ?? "").trim();
|
|
142674
|
+
var tokenFingerprint = (bot) => NodeCrypto5.createHash("sha256").update(tokenOf(bot)).digest("hex");
|
|
142102
142675
|
var REACTION_WORKING = "\u{1F440}";
|
|
142103
142676
|
var REACTION_DONE = "\u{1F44D}";
|
|
142104
142677
|
var REACTION_FAILED = "\u{1F44E}";
|
|
@@ -142188,26 +142761,27 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142188
142761
|
limit: TELEGRAM_TEXT_MAX_CHARS,
|
|
142189
142762
|
...resume === void 0 ? {} : { resume },
|
|
142190
142763
|
messages: {
|
|
142191
|
-
send: (text4) =>
|
|
142192
|
-
|
|
142193
|
-
|
|
142194
|
-
|
|
142195
|
-
|
|
142764
|
+
send: (text4, first) => {
|
|
142765
|
+
const reply = first ? slot.binding.reply?.messageId : void 0;
|
|
142766
|
+
return withRichFallback(
|
|
142767
|
+
toTelegramRichMarkdown(text4),
|
|
142768
|
+
async (markdown) => (await apiFor(slot.binding)).sendRichOne(chatId, markdown, void 0, reply),
|
|
142769
|
+
async () => (await apiFor(slot.binding)).sendOne(chatId, text4, void 0, reply)
|
|
142770
|
+
);
|
|
142771
|
+
},
|
|
142196
142772
|
edit: (messageId, text4) => withRichFallback(
|
|
142197
142773
|
toTelegramRichMarkdown(text4),
|
|
142198
|
-
async (markdown) =>
|
|
142199
|
-
|
|
142200
|
-
},
|
|
142201
|
-
async () => {
|
|
142202
|
-
await (await apiFor(slot.binding)).editMessage(chatId, messageId, text4);
|
|
142203
|
-
}
|
|
142774
|
+
async (markdown) => (await apiFor(slot.binding)).editRich(chatId, messageId, markdown),
|
|
142775
|
+
async () => (await apiFor(slot.binding)).editMessage(chatId, messageId, text4)
|
|
142204
142776
|
)
|
|
142205
142777
|
}
|
|
142206
142778
|
});
|
|
142207
142779
|
return {
|
|
142208
142780
|
platform: "telegram",
|
|
142781
|
+
botIdentity: tokenFingerprint,
|
|
142209
142782
|
icon: "telegram",
|
|
142210
142783
|
label: "Telegram",
|
|
142784
|
+
quotesReplies: true,
|
|
142211
142785
|
columns: COLUMNS2,
|
|
142212
142786
|
validate: telegramConfigIssues,
|
|
142213
142787
|
isConfigured: (binding) => telegramConfigIssues(binding).length === 0,
|
|
@@ -142216,7 +142790,12 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142216
142790
|
};
|
|
142217
142791
|
const stopped = new Promise((resolve) => stop = resolve);
|
|
142218
142792
|
let running = true;
|
|
142219
|
-
|
|
142793
|
+
const tokenHash = tokenFingerprint(binding);
|
|
142794
|
+
let offset = deps.readCursor(binding.rowId, tokenHash);
|
|
142795
|
+
const advanceOffset = (update) => {
|
|
142796
|
+
offset = nextOffset([update], offset ?? 0);
|
|
142797
|
+
deps.saveCursor(binding.rowId, tokenHash, offset);
|
|
142798
|
+
};
|
|
142220
142799
|
let acknowledged = 0;
|
|
142221
142800
|
let backoff = BACKOFF_START_MS;
|
|
142222
142801
|
let botUsername;
|
|
@@ -142229,7 +142808,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142229
142808
|
});
|
|
142230
142809
|
const publishAck = () => {
|
|
142231
142810
|
const api = liveApi;
|
|
142232
|
-
if (api === void 0 || offset <= acknowledged) return;
|
|
142811
|
+
if (api === void 0 || offset === void 0 || offset <= acknowledged) return;
|
|
142233
142812
|
const next = offset;
|
|
142234
142813
|
acknowledged = next;
|
|
142235
142814
|
void api.getUpdates(next, 0).catch(() => void 0);
|
|
@@ -142258,6 +142837,12 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142258
142837
|
});
|
|
142259
142838
|
});
|
|
142260
142839
|
}
|
|
142840
|
+
if (offset === void 0) {
|
|
142841
|
+
const backlog = await api.getUpdates(-1, 0);
|
|
142842
|
+
if (!running) return;
|
|
142843
|
+
offset = nextOffset(backlog, 0);
|
|
142844
|
+
deps.saveCursor(binding.rowId, tokenHash, offset);
|
|
142845
|
+
}
|
|
142261
142846
|
acknowledged = offset;
|
|
142262
142847
|
const updates = await api.getUpdates(offset);
|
|
142263
142848
|
if (!running) return;
|
|
@@ -142270,13 +142855,13 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142270
142855
|
if (callback?.data !== void 0) {
|
|
142271
142856
|
const operatorId = callback.from === void 0 ? null : String(callback.from.id);
|
|
142272
142857
|
const notice = callback.data.startsWith(PANEL_DATA_PREFIX) ? panelClick(binding.rowId, callback, operatorId, handlers) : handlers.onButton(callback.data, operatorId, callback.id);
|
|
142273
|
-
|
|
142858
|
+
advanceOffset(update);
|
|
142274
142859
|
await api.answerCallback(callback.id, notice ?? void 0).catch(() => void 0);
|
|
142275
142860
|
continue;
|
|
142276
142861
|
}
|
|
142277
142862
|
const message = update.message;
|
|
142278
142863
|
if (message === void 0) {
|
|
142279
|
-
|
|
142864
|
+
advanceOffset(update);
|
|
142280
142865
|
continue;
|
|
142281
142866
|
}
|
|
142282
142867
|
const resource = resourceOf2(message);
|
|
@@ -142285,13 +142870,13 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142285
142870
|
botUsername
|
|
142286
142871
|
);
|
|
142287
142872
|
if (text4 === null) {
|
|
142288
|
-
|
|
142873
|
+
advanceOffset(update);
|
|
142289
142874
|
continue;
|
|
142290
142875
|
}
|
|
142291
142876
|
const gated = !passesGroupGate2({
|
|
142292
142877
|
message,
|
|
142293
142878
|
text: text4,
|
|
142294
|
-
hasResource: resource !== null,
|
|
142879
|
+
hasResource: resource !== null || message.voice !== void 0,
|
|
142295
142880
|
botUsername: botUsername ?? null,
|
|
142296
142881
|
groupMention: binding.groupMention
|
|
142297
142882
|
});
|
|
@@ -142299,16 +142884,16 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142299
142884
|
const solo = binding.groupMention === "always" && await isSoloGroup(String(message.chat.id));
|
|
142300
142885
|
if (!running) break;
|
|
142301
142886
|
if (!solo) {
|
|
142302
|
-
|
|
142887
|
+
advanceOffset(update);
|
|
142303
142888
|
continue;
|
|
142304
142889
|
}
|
|
142305
142890
|
}
|
|
142306
142891
|
if (!running) break;
|
|
142307
|
-
|
|
142892
|
+
advanceOffset(update);
|
|
142308
142893
|
const senderId = message.from === void 0 ? null : String(message.from.id);
|
|
142309
|
-
const
|
|
142894
|
+
const senderName2 = telegramDisplayName(message.from);
|
|
142310
142895
|
const chatKind = message.chat.type === "private" ? "private" : "group";
|
|
142311
|
-
if (text4 === "" && resource === null) {
|
|
142896
|
+
if (message.voice !== void 0 || text4 === "" && resource === null) {
|
|
142312
142897
|
const unsupported = unsupportedTypeOf(message);
|
|
142313
142898
|
if (unsupported === null) continue;
|
|
142314
142899
|
handlers.onMessage({
|
|
@@ -142330,7 +142915,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142330
142915
|
...parentChatId === void 0 ? {} : { parentChatId },
|
|
142331
142916
|
senderId,
|
|
142332
142917
|
text: text4,
|
|
142333
|
-
...
|
|
142918
|
+
...senderName2 ? { senderName: senderName2 } : {},
|
|
142334
142919
|
...message.chat.title ? { chatTitle: message.chat.title } : {},
|
|
142335
142920
|
...replyTo === null ? {} : { replyTo },
|
|
142336
142921
|
chatKind,
|
|
@@ -142347,6 +142932,17 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142347
142932
|
}
|
|
142348
142933
|
} catch (error) {
|
|
142349
142934
|
if (!running) return;
|
|
142935
|
+
if (isTelegramCredentialsRejected(error)) {
|
|
142936
|
+
running = false;
|
|
142937
|
+
stop();
|
|
142938
|
+
handlers.onConnectionState?.({
|
|
142939
|
+
state: "failed",
|
|
142940
|
+
reason: "credentials-rejected",
|
|
142941
|
+
hint: "unauthorized",
|
|
142942
|
+
error
|
|
142943
|
+
});
|
|
142944
|
+
return;
|
|
142945
|
+
}
|
|
142350
142946
|
if (checkingConnection) handlers.onConnectionState?.({ state: "failed", error });
|
|
142351
142947
|
handlers.onError(error instanceof Error ? error.message : String(error));
|
|
142352
142948
|
await Promise.race([(deps.sleep ?? defaultSleep)(backoff), stopped]);
|
|
@@ -142358,16 +142954,16 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142358
142954
|
deps.log.info("telegram polling", { bot: binding.displayName });
|
|
142359
142955
|
return {
|
|
142360
142956
|
close: () => {
|
|
142957
|
+
if (running) publishAck();
|
|
142361
142958
|
running = false;
|
|
142362
142959
|
stop();
|
|
142363
|
-
publishAck();
|
|
142364
142960
|
apis.delete(binding.rowId);
|
|
142365
142961
|
}
|
|
142366
142962
|
};
|
|
142367
142963
|
},
|
|
142368
142964
|
sendText: async (binding, text4, options) => {
|
|
142369
142965
|
const api = await apiFor(binding);
|
|
142370
|
-
await api.sendMessage(binding.chatId, text4, options?.retry);
|
|
142966
|
+
await api.sendMessage(binding.chatId, text4, options?.retry, binding.reply?.messageId);
|
|
142371
142967
|
},
|
|
142372
142968
|
checkConnection: async (bot) => {
|
|
142373
142969
|
const token = tokenOf(bot);
|
|
@@ -142391,12 +142987,11 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142391
142987
|
}
|
|
142392
142988
|
username = me.username !== void 0 && me.username !== "" ? `@${me.username}` : "(no username)";
|
|
142393
142989
|
} catch (error) {
|
|
142394
|
-
const rejected = error instanceof TelegramApiError && (error.code === 401 || error.code === 404);
|
|
142395
142990
|
return {
|
|
142396
142991
|
stage: "getMe",
|
|
142397
142992
|
ok: false,
|
|
142398
142993
|
detail: describeError(error),
|
|
142399
|
-
...
|
|
142994
|
+
...isTelegramCredentialsRejected(error) ? { hint: "unauthorized" } : {}
|
|
142400
142995
|
};
|
|
142401
142996
|
}
|
|
142402
142997
|
try {
|
|
@@ -142436,25 +143031,30 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142436
143031
|
sendImage: async (binding, image2) => {
|
|
142437
143032
|
const api = await apiFor(binding);
|
|
142438
143033
|
try {
|
|
142439
|
-
await api.sendPhoto(binding.chatId, image2);
|
|
143034
|
+
await api.sendPhoto(binding.chatId, image2, binding.reply?.messageId);
|
|
142440
143035
|
} catch (error) {
|
|
142441
143036
|
if (!(error instanceof TelegramApiError && error.code === 400)) throw error;
|
|
142442
143037
|
deps.log.info("telegram could not send the picture as a photo, sending it as a file", {
|
|
142443
143038
|
name: image2.name,
|
|
142444
143039
|
reason: error.message
|
|
142445
143040
|
});
|
|
142446
|
-
await api.sendDocument(binding.chatId, image2);
|
|
143041
|
+
await api.sendDocument(binding.chatId, image2, binding.reply?.messageId);
|
|
142447
143042
|
}
|
|
142448
143043
|
},
|
|
142449
143044
|
sendFile: async (binding, file) => {
|
|
142450
143045
|
const api = await apiFor(binding);
|
|
142451
|
-
await api.sendDocument(binding.chatId, file);
|
|
143046
|
+
await api.sendDocument(binding.chatId, file, binding.reply?.messageId);
|
|
142452
143047
|
},
|
|
142453
143048
|
// 单张照片 10 MB、单个文件 50 MB。这两个数来自 Bot API 文档的记忆,本仓库里没有核对过。
|
|
142454
143049
|
outboundLimits: { imageBytes: 10 * 1024 * 1024, fileBytes: 50 * 1024 * 1024 },
|
|
142455
143050
|
askApproval: async (binding, request) => {
|
|
142456
143051
|
const api = await apiFor(binding);
|
|
142457
|
-
return api.sendButtons(
|
|
143052
|
+
return api.sendButtons(
|
|
143053
|
+
binding.chatId,
|
|
143054
|
+
request.body,
|
|
143055
|
+
request.options,
|
|
143056
|
+
binding.reply?.messageId
|
|
143057
|
+
);
|
|
142458
143058
|
},
|
|
142459
143059
|
settleApproval: async (binding, cardId, settlement) => {
|
|
142460
143060
|
const api = await apiFor(binding);
|
|
@@ -142506,7 +143106,7 @@ ${line}`
|
|
|
142506
143106
|
const api = await apiFor(binding);
|
|
142507
143107
|
const rendered = renderPanel(card, 1);
|
|
142508
143108
|
const cardId = await withFloodWait(
|
|
142509
|
-
() => api.sendOne(binding.chatId, rendered.text, rendered.rows)
|
|
143109
|
+
() => api.sendOne(binding.chatId, rendered.text, rendered.rows, binding.reply?.messageId)
|
|
142510
143110
|
);
|
|
142511
143111
|
if (cardId !== null) {
|
|
142512
143112
|
panelKeyboards.set(`${binding.rowId}:${binding.chatId}:${cardId}`, {
|
|
@@ -142531,7 +143131,9 @@ ${line}`
|
|
|
142531
143131
|
open: async (binding, card) => {
|
|
142532
143132
|
const api = await apiFor(binding);
|
|
142533
143133
|
const rendered = renderChoiceCard(card);
|
|
142534
|
-
return withFloodWait(
|
|
143134
|
+
return withFloodWait(
|
|
143135
|
+
() => api.sendOne(binding.chatId, rendered.text, rendered.rows, binding.reply?.messageId)
|
|
143136
|
+
);
|
|
142535
143137
|
},
|
|
142536
143138
|
update: async (binding, cardId, card) => {
|
|
142537
143139
|
const api = await apiFor(binding);
|
|
@@ -142551,12 +143153,96 @@ ${line}`
|
|
|
142551
143153
|
|
|
142552
143154
|
// src/turns.ts
|
|
142553
143155
|
import { projectOrNull as projectOrNull2, threadOrNull as threadOrNull8 } from "@ycodium-ai/plugin-api";
|
|
143156
|
+
|
|
143157
|
+
// src/dispatchRetry.ts
|
|
143158
|
+
var RETRY_DELAYS_MS = [1e3, 3e3];
|
|
143159
|
+
var retryable2 = (error) => {
|
|
143160
|
+
if (typeof error !== "object" || error === null || !("_tag" in error)) return true;
|
|
143161
|
+
const reason = "reason" in error ? error.reason : void 0;
|
|
143162
|
+
switch (error._tag) {
|
|
143163
|
+
case "PluginCapabilityError":
|
|
143164
|
+
return reason === void 0 || reason === "host-unavailable";
|
|
143165
|
+
case "DataCommandError":
|
|
143166
|
+
return reason === "unavailable";
|
|
143167
|
+
case "EnvironmentAuthorizationError":
|
|
143168
|
+
return false;
|
|
143169
|
+
case "OrchestrationDispatchCommandError":
|
|
143170
|
+
return reason == null && (!("rejection" in error) || error.rejection == null);
|
|
143171
|
+
default:
|
|
143172
|
+
return true;
|
|
143173
|
+
}
|
|
143174
|
+
};
|
|
143175
|
+
var dispatchTurnWithRetry = async (start, input, options = {}) => {
|
|
143176
|
+
const sleep3 = options.sleep ?? abortableSleep;
|
|
143177
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
143178
|
+
if (options.signal?.aborted) throw new Error("aborted");
|
|
143179
|
+
try {
|
|
143180
|
+
return await start(input);
|
|
143181
|
+
} catch (error) {
|
|
143182
|
+
const delay = RETRY_DELAYS_MS[attempt];
|
|
143183
|
+
if (delay === void 0 || !retryable2(error)) throw error;
|
|
143184
|
+
await sleep3(delay, options.signal);
|
|
143185
|
+
}
|
|
143186
|
+
}
|
|
143187
|
+
};
|
|
143188
|
+
|
|
143189
|
+
// src/dispatchReceipt.ts
|
|
143190
|
+
var dispatchTurnWithReceipt = async (host, input, options) => {
|
|
143191
|
+
try {
|
|
143192
|
+
await dispatchTurnWithRetry((next) => host.threads.turns.start(next), input, options);
|
|
143193
|
+
} catch (error) {
|
|
143194
|
+
if (options.signal.aborted) throw error;
|
|
143195
|
+
let receipt;
|
|
143196
|
+
try {
|
|
143197
|
+
receipt = await host.commands.getReceipt({
|
|
143198
|
+
commandId: input.requestId,
|
|
143199
|
+
commandType: "thread.turn.start",
|
|
143200
|
+
threadId: input.threadId
|
|
143201
|
+
});
|
|
143202
|
+
} catch (receiptError) {
|
|
143203
|
+
host.log.warn("chat could not read a turn command receipt", {
|
|
143204
|
+
requestId: input.requestId,
|
|
143205
|
+
threadId: input.threadId,
|
|
143206
|
+
error: describeError(receiptError)
|
|
143207
|
+
});
|
|
143208
|
+
throw error;
|
|
143209
|
+
}
|
|
143210
|
+
if (options.signal.aborted) throw error;
|
|
143211
|
+
if (receipt.status === "accepted") return;
|
|
143212
|
+
if (receipt.status === "rejected")
|
|
143213
|
+
throw new Error(receipt.error ?? "turn command rejected", { cause: error });
|
|
143214
|
+
throw error;
|
|
143215
|
+
}
|
|
143216
|
+
};
|
|
143217
|
+
|
|
143218
|
+
// src/messageIdentity.ts
|
|
143219
|
+
import * as NodeCrypto6 from "node:crypto";
|
|
143220
|
+
var messageIdentity = (operation, bot, message, adapter) => {
|
|
143221
|
+
const tuple = [
|
|
143222
|
+
IDENTITY_VERSION,
|
|
143223
|
+
operation,
|
|
143224
|
+
adapter.platform,
|
|
143225
|
+
adapter.botIdentity(bot),
|
|
143226
|
+
message.originalChatId ?? message.chatId,
|
|
143227
|
+
message.messageId
|
|
143228
|
+
];
|
|
143229
|
+
return `${identityPrefix(operation)}${NodeCrypto6.createHash("sha256").update(JSON.stringify(tuple)).digest("hex")}`;
|
|
143230
|
+
};
|
|
143231
|
+
|
|
143232
|
+
// src/turns.ts
|
|
142554
143233
|
var TITLE_MAX_CHARS = 72;
|
|
142555
143234
|
var titleFrom = (text4) => text4.replace(/\s+/g, " ").slice(0, TITLE_MAX_CHARS) || "Chat thread";
|
|
142556
143235
|
var makeTurns = (core, deps) => {
|
|
142557
143236
|
const { host, t, commit, bindChat, deliver } = core;
|
|
142558
143237
|
const { queueFlow, rememberThreadProject } = deps;
|
|
142559
143238
|
const { pendingByChat, pendingFor, collectResources } = deps.attachments;
|
|
143239
|
+
const dispatchAbort = new AbortController();
|
|
143240
|
+
host.onDispose(() => dispatchAbort.abort());
|
|
143241
|
+
const identity = (operation, binding, message) => {
|
|
143242
|
+
const adapter = core.adapterFor(binding.platform);
|
|
143243
|
+
if (adapter === void 0) throw new Error(`missing IM adapter: ${binding.platform}`);
|
|
143244
|
+
return messageIdentity(operation, binding, message, adapter);
|
|
143245
|
+
};
|
|
142560
143246
|
const threadStillUsable = async (binding, threadId) => {
|
|
142561
143247
|
try {
|
|
142562
143248
|
const thread = await threadOrNull8(host.threads, threadId, { archived: "include" });
|
|
@@ -142570,7 +143256,7 @@ var makeTurns = (core, deps) => {
|
|
|
142570
143256
|
return waiting !== void 0 && waiting.threadId === threadId ? waiting : void 0;
|
|
142571
143257
|
};
|
|
142572
143258
|
const interactionModeOf = (chatKey) => chatOf(core.state, chatKey).plan ? "plan" : "default";
|
|
142573
|
-
const ensureThread = async (binding,
|
|
143259
|
+
const ensureThread = async (binding, message) => {
|
|
142574
143260
|
const chatKey = chatKeyOf(binding);
|
|
142575
143261
|
let threadId = chatOf(core.state, chatKey).threadId ?? void 0;
|
|
142576
143262
|
if (threadId !== void 0 && !await threadStillUsable(binding, threadId)) {
|
|
@@ -142600,12 +143286,12 @@ var makeTurns = (core, deps) => {
|
|
|
142600
143286
|
await deliver(binding, t("reply.noDefaultModel", { title: project.title }));
|
|
142601
143287
|
return null;
|
|
142602
143288
|
}
|
|
142603
|
-
const createdThreadId =
|
|
143289
|
+
const createdThreadId = identity("thread", binding, message);
|
|
142604
143290
|
await host.threads.create({
|
|
142605
|
-
requestId:
|
|
143291
|
+
requestId: identity("create", binding, message),
|
|
142606
143292
|
threadId: createdThreadId,
|
|
142607
143293
|
projectId: binding.projectId,
|
|
142608
|
-
title: titleFrom(
|
|
143294
|
+
title: titleFrom(message.text || message.resources[0]?.name || "Chat"),
|
|
142609
143295
|
modelSelection,
|
|
142610
143296
|
runtimeMode: binding.runtimeMode,
|
|
142611
143297
|
interactionMode: interactionModeOf(chatKey),
|
|
@@ -142624,7 +143310,7 @@ var makeTurns = (core, deps) => {
|
|
|
142624
143310
|
const startTurn = async (binding, message) => {
|
|
142625
143311
|
const chatKey = chatKeyOf(binding);
|
|
142626
143312
|
const text4 = message.text;
|
|
142627
|
-
const threadId = await ensureThread(binding,
|
|
143313
|
+
const threadId = await ensureThread(binding, message);
|
|
142628
143314
|
if (threadId === null) return null;
|
|
142629
143315
|
const topicRoot = await message.topicRootForSession?.(threadId);
|
|
142630
143316
|
const carried = waitingFor(chatKey, threadId);
|
|
@@ -142638,7 +143324,7 @@ var makeTurns = (core, deps) => {
|
|
|
142638
143324
|
);
|
|
142639
143325
|
const attachments = [...carried?.attachments ?? [], ...own.attachments];
|
|
142640
143326
|
const notes = [...carried?.notes ?? [], ...own.notes];
|
|
142641
|
-
const userMessageId =
|
|
143327
|
+
const userMessageId = identity("msg", binding, message);
|
|
142642
143328
|
deps.anchors.note(threadId, userMessageId, binding);
|
|
142643
143329
|
let intentAction;
|
|
142644
143330
|
if (binding.queueMode === "steer") {
|
|
@@ -142650,33 +143336,39 @@ var makeTurns = (core, deps) => {
|
|
|
142650
143336
|
kind: "steer",
|
|
142651
143337
|
source: "automatic",
|
|
142652
143338
|
turnId: activeTurnId,
|
|
142653
|
-
commandId:
|
|
143339
|
+
commandId: identity("steer", binding, message)
|
|
142654
143340
|
};
|
|
142655
143341
|
}
|
|
142656
143342
|
} catch {
|
|
142657
143343
|
}
|
|
142658
143344
|
}
|
|
142659
143345
|
const registered = deps.conclusions.starting(threadId);
|
|
143346
|
+
const requestId = identity("turn", binding, message);
|
|
143347
|
+
let failureNotice;
|
|
142660
143348
|
try {
|
|
142661
|
-
await
|
|
142662
|
-
|
|
142663
|
-
|
|
142664
|
-
|
|
142665
|
-
|
|
142666
|
-
|
|
142667
|
-
|
|
142668
|
-
|
|
142669
|
-
|
|
142670
|
-
|
|
142671
|
-
|
|
142672
|
-
(
|
|
142673
|
-
|
|
143349
|
+
await dispatchTurnWithReceipt(
|
|
143350
|
+
host,
|
|
143351
|
+
{
|
|
143352
|
+
requestId,
|
|
143353
|
+
threadId,
|
|
143354
|
+
message: {
|
|
143355
|
+
messageId: userMessageId,
|
|
143356
|
+
role: "user",
|
|
143357
|
+
text: [
|
|
143358
|
+
textWithInboundContext(message, binding.rowId, t, topicRoot?.reference),
|
|
143359
|
+
...notes
|
|
143360
|
+
].join("\n"),
|
|
143361
|
+
attachments: attachments.map(
|
|
143362
|
+
(attachment) => attachment.type === "image" ? { ...attachment, type: "image" } : { ...attachment, type: "file" }
|
|
143363
|
+
)
|
|
143364
|
+
},
|
|
143365
|
+
titleSeed: titleFrom(text4 || attachments[0]?.name || "Chat"),
|
|
143366
|
+
runtimeMode: binding.runtimeMode,
|
|
143367
|
+
interactionMode: interactionModeOf(chatKey),
|
|
143368
|
+
...intentAction !== void 0 ? { intentAction } : {}
|
|
142674
143369
|
},
|
|
142675
|
-
|
|
142676
|
-
|
|
142677
|
-
interactionMode: interactionModeOf(chatKey),
|
|
142678
|
-
...intentAction !== void 0 ? { intentAction } : {}
|
|
142679
|
-
});
|
|
143370
|
+
{ sleep: deps.dispatchSleep, signal: dispatchAbort.signal }
|
|
143371
|
+
);
|
|
142680
143372
|
topicRoot?.confirmConsumed();
|
|
142681
143373
|
if (intentAction?.kind !== "steer") {
|
|
142682
143374
|
deps.conclusions.owe({
|
|
@@ -142688,10 +143380,15 @@ var makeTurns = (core, deps) => {
|
|
|
142688
143380
|
}
|
|
142689
143381
|
} catch (error) {
|
|
142690
143382
|
if (carried !== void 0) pendingByChat.set(chatKey, carried);
|
|
142691
|
-
|
|
143383
|
+
if (dispatchAbort.signal.aborted) return null;
|
|
143384
|
+
failureNotice = deps.failures.forDispatchFailure(threadId, requestId, error);
|
|
142692
143385
|
} finally {
|
|
142693
143386
|
registered();
|
|
142694
143387
|
}
|
|
143388
|
+
if (failureNotice !== void 0) {
|
|
143389
|
+
await deliver(binding, failureNotice);
|
|
143390
|
+
return null;
|
|
143391
|
+
}
|
|
142695
143392
|
deps.noteInboundUserMessage?.(userMessageId);
|
|
142696
143393
|
host.log.info("chat started a turn from a message", {
|
|
142697
143394
|
bot: binding.displayName,
|
|
@@ -142708,7 +143405,7 @@ var makeTurns = (core, deps) => {
|
|
|
142708
143405
|
return threadId;
|
|
142709
143406
|
};
|
|
142710
143407
|
const receiveAttachmentsOnly = async (binding, message) => {
|
|
142711
|
-
const threadId = await ensureThread(binding, message
|
|
143408
|
+
const threadId = await ensureThread(binding, message);
|
|
142712
143409
|
if (threadId === null) return;
|
|
142713
143410
|
const waiting = pendingFor(chatKeyOf(binding), threadId);
|
|
142714
143411
|
const collected = await collectResources(
|
|
@@ -142790,6 +143487,84 @@ var makeWebSync = (core) => {
|
|
|
142790
143487
|
return { noteInboundUserMessage };
|
|
142791
143488
|
};
|
|
142792
143489
|
|
|
143490
|
+
// src/lark/setup/repairScopes.ts
|
|
143491
|
+
var SUBSCRIPTIONS = { events: APP_EVENTS.join(", "), callbacks: CARD_CALLBACKS.join(", ") };
|
|
143492
|
+
var repairLarkScopes = async (http, bot, emit, ref, redactor) => {
|
|
143493
|
+
const { appId, appSecret, error: credentialError } = larkCredentials(bot);
|
|
143494
|
+
const steps = (token2, scopes) => emit({
|
|
143495
|
+
kind: "steps",
|
|
143496
|
+
steps: [
|
|
143497
|
+
{ id: "token", labelKey: "rowAction.larkRepair.step.token", state: token2 },
|
|
143498
|
+
...scopes === void 0 ? [] : [{ id: "scopes", labelKey: "rowAction.larkRepair.step.scopes", state: scopes }]
|
|
143499
|
+
]
|
|
143500
|
+
});
|
|
143501
|
+
const fail = (error, credentials2 = false) => {
|
|
143502
|
+
const hint = credentials2 ? "larkCredentials" : larkConnectionHint(error);
|
|
143503
|
+
emit({
|
|
143504
|
+
kind: "failed",
|
|
143505
|
+
reasonKey: hint === void 0 ? "rowAction.larkRepair.failed" : connectionHintKey(hint),
|
|
143506
|
+
params: { ref, ...hint === void 0 ? {} : { hint } },
|
|
143507
|
+
detail: redactor.redact(describeError(error))
|
|
143508
|
+
});
|
|
143509
|
+
};
|
|
143510
|
+
steps("running", "pending");
|
|
143511
|
+
if (credentialError !== void 0) {
|
|
143512
|
+
steps("failed");
|
|
143513
|
+
fail(new Error(credentialError), true);
|
|
143514
|
+
return;
|
|
143515
|
+
}
|
|
143516
|
+
let token;
|
|
143517
|
+
try {
|
|
143518
|
+
({ token } = await fetchTenantToken({ http, appId, appSecret }));
|
|
143519
|
+
redactor.register(token);
|
|
143520
|
+
} catch (error) {
|
|
143521
|
+
steps("failed");
|
|
143522
|
+
fail(error);
|
|
143523
|
+
return;
|
|
143524
|
+
}
|
|
143525
|
+
steps("done", "running");
|
|
143526
|
+
let granted;
|
|
143527
|
+
try {
|
|
143528
|
+
granted = await fetchAppTenantScopes({ http, token });
|
|
143529
|
+
} catch (error) {
|
|
143530
|
+
if (error instanceof LarkApiError && (error.status === 400 || error.status === 403 || isLarkPermissionCode(error.code))) {
|
|
143531
|
+
granted = void 0;
|
|
143532
|
+
} else {
|
|
143533
|
+
steps("done", "failed");
|
|
143534
|
+
fail(error);
|
|
143535
|
+
return;
|
|
143536
|
+
}
|
|
143537
|
+
}
|
|
143538
|
+
const missing = granted === void 0 ? SCOPES : SCOPES.filter((scope) => !granted.includes(scope));
|
|
143539
|
+
steps("done", granted === void 0 ? "failed" : "done");
|
|
143540
|
+
const needsAction = missing.length > 0;
|
|
143541
|
+
const url = `${CONSOLE_ORIGIN}${CONSOLE_APP_PATH}/${appId}/auth?q=${missing.join(",")}&op_from=openapi&token_type=tenant`;
|
|
143542
|
+
emit({
|
|
143543
|
+
kind: "done",
|
|
143544
|
+
fills: {},
|
|
143545
|
+
...granted === void 0 || needsAction ? { tone: "caution" } : {},
|
|
143546
|
+
...needsAction ? {
|
|
143547
|
+
link: {
|
|
143548
|
+
url,
|
|
143549
|
+
labelKey: granted === void 0 ? "rowAction.larkRepair.openCheckLink" : "rowAction.larkRepair.openApplyLink"
|
|
143550
|
+
}
|
|
143551
|
+
} : {},
|
|
143552
|
+
messageKey: granted === void 0 ? "rowAction.larkRepair.unverified" : needsAction ? "rowAction.larkRepair.missing" : "rowAction.larkRepair.allGranted",
|
|
143553
|
+
warnings: [
|
|
143554
|
+
...needsAction ? [
|
|
143555
|
+
{
|
|
143556
|
+
key: granted === void 0 ? "rowAction.larkRepair.requiredScopes" : "rowAction.larkRepair.missingScopes",
|
|
143557
|
+
params: { scopes: missing.join("\n") }
|
|
143558
|
+
},
|
|
143559
|
+
{ key: "rowAction.larkRepair.applyLink", params: { url } },
|
|
143560
|
+
{ key: "rowAction.larkRepair.publishHint" }
|
|
143561
|
+
] : [],
|
|
143562
|
+
{ key: "rowAction.larkRepair.botHint" },
|
|
143563
|
+
{ key: "rowAction.larkRepair.eventHint", params: SUBSCRIPTIONS }
|
|
143564
|
+
]
|
|
143565
|
+
});
|
|
143566
|
+
};
|
|
143567
|
+
|
|
142793
143568
|
// src/settingsRowActions.ts
|
|
142794
143569
|
var ROW_PAYLOAD_SCHEMA = {
|
|
142795
143570
|
type: "object",
|
|
@@ -142857,19 +143632,21 @@ var emitReport = (emit, report, action) => {
|
|
|
142857
143632
|
});
|
|
142858
143633
|
return;
|
|
142859
143634
|
}
|
|
142860
|
-
const
|
|
143635
|
+
const connectionReasonKey = report.hint === void 0 ? "rowAction.checkConnection.failed" : connectionHintKey(report.hint);
|
|
143636
|
+
const copying = action.keyPrefix === "rowAction.copyDiagnostics";
|
|
142861
143637
|
emit({
|
|
142862
143638
|
kind: "failed",
|
|
142863
|
-
reasonKey:
|
|
143639
|
+
reasonKey: copying ? "rowAction.copyDiagnostics.done" : connectionReasonKey,
|
|
142864
143640
|
// 参考号由界面写在细节第一行(「参考号:…」),结论句里不带。
|
|
142865
143641
|
params: {
|
|
142866
143642
|
ref: report.ref,
|
|
142867
|
-
...
|
|
143643
|
+
...report.hint === void 0 ? {} : { hint: report.hint }
|
|
142868
143644
|
},
|
|
143645
|
+
...copying ? { noteKey: connectionReasonKey } : {},
|
|
142869
143646
|
detail
|
|
142870
143647
|
});
|
|
142871
143648
|
};
|
|
142872
|
-
var registerSettingsRowActions = (ctx, adapters) => {
|
|
143649
|
+
var registerSettingsRowActions = (ctx, adapters, connections) => {
|
|
142873
143650
|
const resolveBot = (rowId) => botOf(ctx.config, adapters, rowId);
|
|
142874
143651
|
const registerProbeAction = (action) => {
|
|
142875
143652
|
ctx.stream({
|
|
@@ -142897,6 +143674,26 @@ var registerSettingsRowActions = (ctx, adapters) => {
|
|
|
142897
143674
|
keyPrefix: "rowAction.checkConnection",
|
|
142898
143675
|
copyable: false
|
|
142899
143676
|
});
|
|
143677
|
+
ctx.stream({
|
|
143678
|
+
name: BOT_ACTION_COMMANDS.reconnect,
|
|
143679
|
+
description: "Restart only this IM bot's connection using its saved settings.",
|
|
143680
|
+
scope: "operate",
|
|
143681
|
+
payloadSchema: ROW_PAYLOAD_SCHEMA,
|
|
143682
|
+
itemSchema: ROW_ACTION_ITEM_SCHEMA,
|
|
143683
|
+
buffer: { latest: 8 },
|
|
143684
|
+
handle: async (payload, emit) => {
|
|
143685
|
+
const rowId = typeof payload["rowId"] === "string" ? payload["rowId"] : "";
|
|
143686
|
+
emit({ kind: "status", statusKey: "rowAction.reconnect.running", params: { rowId } });
|
|
143687
|
+
if (resolveBot(rowId) === void 0) {
|
|
143688
|
+
emit({ kind: "failed", reasonKey: "rowAction.reconnect.notSaved" });
|
|
143689
|
+
return;
|
|
143690
|
+
}
|
|
143691
|
+
const result = connections.reconnect(rowId);
|
|
143692
|
+
emit(
|
|
143693
|
+
result.started ? { kind: "done", fills: {}, messageKey: "rowAction.reconnect.done" } : { kind: "failed", reasonKey: `rowAction.reconnect.${result.reason}` }
|
|
143694
|
+
);
|
|
143695
|
+
}
|
|
143696
|
+
});
|
|
142900
143697
|
registerProbeAction({
|
|
142901
143698
|
name: BOT_ACTION_COMMANDS.copyDiagnostics,
|
|
142902
143699
|
summary: "Probe an IM bot and return a copy-paste diagnostic report.",
|
|
@@ -142905,7 +143702,7 @@ var registerSettingsRowActions = (ctx, adapters) => {
|
|
|
142905
143702
|
});
|
|
142906
143703
|
ctx.stream({
|
|
142907
143704
|
name: BOT_ACTION_COMMANDS.larkRepair,
|
|
142908
|
-
description: "
|
|
143705
|
+
description: "Check required Lark scopes through the public API and list manual repair steps.",
|
|
142909
143706
|
scope: "operate",
|
|
142910
143707
|
payloadSchema: ROW_PAYLOAD_SCHEMA,
|
|
142911
143708
|
itemSchema: ROW_ACTION_ITEM_SCHEMA,
|
|
@@ -142914,15 +143711,6 @@ var registerSettingsRowActions = (ctx, adapters) => {
|
|
|
142914
143711
|
const rowId = typeof payload["rowId"] === "string" ? payload["rowId"] : "";
|
|
142915
143712
|
const send = (item) => emit(item);
|
|
142916
143713
|
const ref = diagnosticRef("setup");
|
|
142917
|
-
send({
|
|
142918
|
-
kind: "steps",
|
|
142919
|
-
steps: [
|
|
142920
|
-
{ id: "scopes", labelKey: "rowAction.larkRepair.step.scopes", state: "skipped" },
|
|
142921
|
-
{ id: "events", labelKey: "rowAction.larkRepair.step.events", state: "skipped" },
|
|
142922
|
-
{ id: "publish", labelKey: "rowAction.larkRepair.step.publish", state: "skipped" },
|
|
142923
|
-
{ id: "verify", labelKey: "rowAction.larkRepair.step.verify", state: "running" }
|
|
142924
|
-
]
|
|
142925
|
-
});
|
|
142926
143714
|
const bot = resolveBot(rowId);
|
|
142927
143715
|
if (bot === void 0 || bot.platform !== "lark") {
|
|
142928
143716
|
send({
|
|
@@ -142932,46 +143720,20 @@ var registerSettingsRowActions = (ctx, adapters) => {
|
|
|
142932
143720
|
});
|
|
142933
143721
|
return;
|
|
142934
143722
|
}
|
|
142935
|
-
const
|
|
142936
|
-
|
|
142937
|
-
|
|
142938
|
-
|
|
142939
|
-
|
|
142940
|
-
|
|
142941
|
-
|
|
142942
|
-
|
|
142943
|
-
id: "verify",
|
|
142944
|
-
labelKey: "rowAction.larkRepair.step.verify",
|
|
142945
|
-
state: report.ok ? "done" : "failed"
|
|
142946
|
-
}
|
|
142947
|
-
]
|
|
142948
|
-
});
|
|
142949
|
-
if (report.ok) {
|
|
142950
|
-
send({
|
|
142951
|
-
kind: "done",
|
|
142952
|
-
fills: {},
|
|
142953
|
-
messageKey: "rowAction.larkRepair.done",
|
|
142954
|
-
warnings: [
|
|
142955
|
-
{
|
|
142956
|
-
key: "rowAction.larkRepair.hint",
|
|
142957
|
-
params: { ref, detail: formatDiagnosticCopy(report) }
|
|
142958
|
-
}
|
|
142959
|
-
]
|
|
142960
|
-
});
|
|
142961
|
-
return;
|
|
142962
|
-
}
|
|
142963
|
-
send({
|
|
142964
|
-
kind: "failed",
|
|
142965
|
-
reasonKey: "rowAction.larkRepair.failed",
|
|
142966
|
-
params: { ref },
|
|
142967
|
-
detail: formatDiagnosticCopy(report)
|
|
142968
|
-
});
|
|
143723
|
+
const lark = adapters.find((adapter) => adapter.platform === "lark");
|
|
143724
|
+
await repairLarkScopes(
|
|
143725
|
+
ctx.http,
|
|
143726
|
+
bot,
|
|
143727
|
+
send,
|
|
143728
|
+
ref,
|
|
143729
|
+
botRedactor(lark?.columns ?? [], bot.values)
|
|
143730
|
+
);
|
|
142969
143731
|
}
|
|
142970
143732
|
});
|
|
142971
143733
|
};
|
|
142972
143734
|
|
|
142973
143735
|
// src/settingsView.ts
|
|
142974
|
-
var
|
|
143736
|
+
var isRecord4 = (value2) => typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
142975
143737
|
var hasStoredSecret = (row, key) => {
|
|
142976
143738
|
const value2 = row[key];
|
|
142977
143739
|
return typeof value2 === "string" && value2 !== "";
|
|
@@ -142989,10 +143751,10 @@ var targetTable = () => ({
|
|
|
142989
143751
|
});
|
|
142990
143752
|
var targetViews = (config2) => {
|
|
142991
143753
|
const table2 = config2[DELIVERY_TARGETS_FIELD];
|
|
142992
|
-
if (!
|
|
143754
|
+
if (!isRecord4(table2)) return [];
|
|
142993
143755
|
const columns = deliveryTargetColumns();
|
|
142994
143756
|
return Object.entries(table2).flatMap(([rowId, raw]) => {
|
|
142995
|
-
if (!
|
|
143757
|
+
if (!isRecord4(raw)) return [];
|
|
142996
143758
|
const issues = targetIssues(raw);
|
|
142997
143759
|
const values = Object.fromEntries(
|
|
142998
143760
|
columns.map((column) => [column.key, readString(raw, column.key)])
|
|
@@ -143004,9 +143766,9 @@ var buildSettingsView = (config2, adapters) => {
|
|
|
143004
143766
|
const columns = buildBotColumns(adapters);
|
|
143005
143767
|
const table2 = config2[BOTS_FIELD];
|
|
143006
143768
|
const bots = [];
|
|
143007
|
-
if (
|
|
143769
|
+
if (isRecord4(table2)) {
|
|
143008
143770
|
for (const [rowId, raw] of Object.entries(table2)) {
|
|
143009
|
-
if (!
|
|
143771
|
+
if (!isRecord4(raw)) continue;
|
|
143010
143772
|
const platform = platformOf(raw);
|
|
143011
143773
|
const values = {};
|
|
143012
143774
|
const secrets = {};
|
|
@@ -143069,10 +143831,10 @@ var applyRowEdit = (config2, { field, columns }, edit) => {
|
|
|
143069
143831
|
for (const key of clearSecrets) {
|
|
143070
143832
|
if (!secretColumns.has(key)) return columnError("not-a-secret", key);
|
|
143071
143833
|
}
|
|
143072
|
-
const table2 =
|
|
143834
|
+
const table2 = isRecord4(config2[field]) ? config2[field] : {};
|
|
143073
143835
|
const requested = edit.rowId;
|
|
143074
143836
|
const current = requested === void 0 ? {} : Object.hasOwn(table2, requested) ? table2[requested] : void 0;
|
|
143075
|
-
if (!
|
|
143837
|
+
if (!isRecord4(current)) return { ok: false, code: "unknown-row" };
|
|
143076
143838
|
const rowId = requested ?? nextRowId(table2);
|
|
143077
143839
|
const nextRow = { ...current, ...edit.values };
|
|
143078
143840
|
const clearing = clearSecrets.filter((key) => !Object.hasOwn(secrets, key));
|
|
@@ -143090,7 +143852,7 @@ var applyBotEdit = (config2, adapters, edit) => applyRowEdit(config2, { field: B
|
|
|
143090
143852
|
var applyTargetEdit = (config2, edit) => applyRowEdit(config2, targetTable(), edit);
|
|
143091
143853
|
var removeRow = (config2, field, rowId) => {
|
|
143092
143854
|
const table2 = config2[field];
|
|
143093
|
-
if (!
|
|
143855
|
+
if (!isRecord4(table2) || !Object.hasOwn(table2, rowId)) return null;
|
|
143094
143856
|
const nextTable = { ...table2 };
|
|
143095
143857
|
delete nextTable[rowId];
|
|
143096
143858
|
return { ...config2, [field]: nextTable };
|
|
@@ -143294,6 +144056,7 @@ var wechatConfigIssues = (bot) => tokenOf2(bot) === "" ? [{ column: "wechatBotTo
|
|
|
143294
144056
|
var makeWechatAdapter = (deps) => {
|
|
143295
144057
|
return {
|
|
143296
144058
|
platform: "wechat",
|
|
144059
|
+
botIdentity: () => "wechat-stub",
|
|
143297
144060
|
icon: "wechat",
|
|
143298
144061
|
// 平台名与设置表的平台选项同一个键,免得默认机器人名与选项写成两种叫法。
|
|
143299
144062
|
label: deps.translate("platform.wechat"),
|
|
@@ -143329,7 +144092,7 @@ var makeWechatAdapter = (deps) => {
|
|
|
143329
144092
|
};
|
|
143330
144093
|
|
|
143331
144094
|
// src/wecomUpload.ts
|
|
143332
|
-
import * as
|
|
144095
|
+
import * as NodeCrypto7 from "node:crypto";
|
|
143333
144096
|
var WECOM_CHUNK_BYTES = 512 * 1024;
|
|
143334
144097
|
var WECOM_MAX_CHUNKS = 100;
|
|
143335
144098
|
var WECOM_MIN_UPLOAD_BYTES = 5;
|
|
@@ -143362,7 +144125,7 @@ var uploadWecomMedia = async (input) => {
|
|
|
143362
144125
|
filename: clampName(input.filename),
|
|
143363
144126
|
total_size: bytes.byteLength,
|
|
143364
144127
|
total_chunks: totalChunks,
|
|
143365
|
-
md5:
|
|
144128
|
+
md5: NodeCrypto7.createHash("md5").update(bytes).digest("hex")
|
|
143366
144129
|
});
|
|
143367
144130
|
const uploadId = init.upload_id;
|
|
143368
144131
|
if (typeof uploadId !== "string" || uploadId === "") {
|
|
@@ -143541,6 +144304,26 @@ var parseWecomMessage = (frame, botId) => {
|
|
|
143541
144304
|
};
|
|
143542
144305
|
var splitForWecom = (text4, limit = WECOM_TEXT_MAX_CHARS) => splitMessage(text4, limit);
|
|
143543
144306
|
var decoder = new TextDecoder();
|
|
144307
|
+
var WecomAckError = class extends Error {
|
|
144308
|
+
code;
|
|
144309
|
+
constructor(code2, msg) {
|
|
144310
|
+
super(`WeCom ack failed: ${String(code2)} ${msg}`);
|
|
144311
|
+
this.name = "WecomAckError";
|
|
144312
|
+
this.code = code2;
|
|
144313
|
+
}
|
|
144314
|
+
};
|
|
144315
|
+
var subscriptionHint = (error) => {
|
|
144316
|
+
if (!(error instanceof WecomAckError)) return void 0;
|
|
144317
|
+
switch (error.code) {
|
|
144318
|
+
case 853e3:
|
|
144319
|
+
case 40058:
|
|
144320
|
+
return "wecomBotCredentials";
|
|
144321
|
+
case 846601:
|
|
144322
|
+
return "wecomLongConnOff";
|
|
144323
|
+
default:
|
|
144324
|
+
return void 0;
|
|
144325
|
+
}
|
|
144326
|
+
};
|
|
143544
144327
|
var runWecomClient = (input) => {
|
|
143545
144328
|
let sequence = 0;
|
|
143546
144329
|
const nextRequestId = (kind) => `${kind}_${++sequence}`;
|
|
@@ -143571,32 +144354,47 @@ var runWecomClient = (input) => {
|
|
|
143571
144354
|
if (typeof frame !== "object" || frame === null) return;
|
|
143572
144355
|
const requestId = frame.headers?.req_id;
|
|
143573
144356
|
if (typeof frame.cmd !== "string") {
|
|
143574
|
-
|
|
144357
|
+
const error = typeof frame.errcode === "number" && frame.errcode !== 0 ? new WecomAckError(frame.errcode, frame.errmsg ?? "") : null;
|
|
144358
|
+
if (typeof requestId !== "string") {
|
|
144359
|
+
if (error !== null) settleAck(subscribeId, error, frame);
|
|
144360
|
+
return;
|
|
144361
|
+
}
|
|
143575
144362
|
if (requestId.startsWith("ping")) {
|
|
143576
144363
|
missedPong = 0;
|
|
143577
144364
|
return;
|
|
143578
144365
|
}
|
|
143579
|
-
const error = typeof frame.errcode === "number" && frame.errcode !== 0 ? new Error(`WeCom ack failed: ${String(frame.errcode)} ${frame.errmsg ?? ""}`) : null;
|
|
143580
144366
|
const stream = openStreams.get(requestId);
|
|
143581
144367
|
if (stream !== void 0 && error !== null) stream.failed = true;
|
|
143582
144368
|
settleAck(requestId, error, frame);
|
|
143583
144369
|
return;
|
|
143584
144370
|
}
|
|
144371
|
+
if (frame.cmd === "aibot_event_callback" && frame.body?.event?.eventtype === "disconnected_event") {
|
|
144372
|
+
finish("taken-over");
|
|
144373
|
+
return;
|
|
144374
|
+
}
|
|
143585
144375
|
if (frame.cmd !== "aibot_msg_callback") return;
|
|
143586
144376
|
const message = parseWecomMessage(frame, input.botId);
|
|
143587
144377
|
if (message !== null) input.handlers.onMessage(message);
|
|
143588
144378
|
};
|
|
143589
|
-
const finish = (error = new Error("WeCom connection closed")
|
|
144379
|
+
const finish = (reason = "disconnected", error = reason === "taken-over" ? void 0 : new Error("WeCom connection closed")) => {
|
|
143590
144380
|
if (finished) return;
|
|
143591
144381
|
finished = true;
|
|
143592
|
-
|
|
144382
|
+
const hint = reason === "taken-over" ? "wecomTakenOver" : reason === "subscription-failed" ? subscriptionHint(error) : void 0;
|
|
144383
|
+
const failureReason = hint === "wecomBotCredentials" ? "credentials-rejected" : reason;
|
|
144384
|
+
if (!closed)
|
|
144385
|
+
input.handlers.onConnectionState?.({
|
|
144386
|
+
state: "failed",
|
|
144387
|
+
...error === void 0 ? {} : { error },
|
|
144388
|
+
reason: failureReason,
|
|
144389
|
+
...hint === void 0 ? {} : { hint }
|
|
144390
|
+
});
|
|
143593
144391
|
clearInterval(heartbeat);
|
|
143594
144392
|
openStreams.clear();
|
|
143595
144393
|
void input.socket.close().catch(() => void 0);
|
|
143596
144394
|
for (const requestId of pendingAcks.keys()) {
|
|
143597
144395
|
settleAck(requestId, new Error("WeCom connection closed"));
|
|
143598
144396
|
}
|
|
143599
|
-
ended.resolve();
|
|
144397
|
+
ended.resolve(failureReason === "subscription-failed" ? "disconnected" : failureReason);
|
|
143600
144398
|
};
|
|
143601
144399
|
const readLoop = async () => {
|
|
143602
144400
|
try {
|
|
@@ -143607,7 +144405,7 @@ var runWecomClient = (input) => {
|
|
|
143607
144405
|
if (!closed) {
|
|
143608
144406
|
input.handlers.onError(error instanceof Error ? error.message : String(error));
|
|
143609
144407
|
}
|
|
143610
|
-
finish(error);
|
|
144408
|
+
finish("disconnected", error);
|
|
143611
144409
|
}
|
|
143612
144410
|
finish();
|
|
143613
144411
|
};
|
|
@@ -143615,7 +144413,7 @@ var runWecomClient = (input) => {
|
|
|
143615
144413
|
if (finished) return;
|
|
143616
144414
|
if (missedPong >= MAX_MISSED_PONGS) {
|
|
143617
144415
|
input.handlers.onError("socket hang up: missed pong");
|
|
143618
|
-
finish(new Error("socket hang up: missed pong"));
|
|
144416
|
+
finish("disconnected", new Error("socket hang up: missed pong"));
|
|
143619
144417
|
return;
|
|
143620
144418
|
}
|
|
143621
144419
|
missedPong += 1;
|
|
@@ -143642,7 +144440,7 @@ var runWecomClient = (input) => {
|
|
|
143642
144440
|
},
|
|
143643
144441
|
(error) => {
|
|
143644
144442
|
if (!closed) input.handlers.onError(error instanceof Error ? error.message : String(error));
|
|
143645
|
-
finish(
|
|
144443
|
+
finish("subscription-failed", error);
|
|
143646
144444
|
return false;
|
|
143647
144445
|
}
|
|
143648
144446
|
);
|
|
@@ -143777,7 +144575,7 @@ var runWecomClient = (input) => {
|
|
|
143777
144575
|
};
|
|
143778
144576
|
|
|
143779
144577
|
// src/wecomMedia.ts
|
|
143780
|
-
import * as
|
|
144578
|
+
import * as NodeCrypto8 from "node:crypto";
|
|
143781
144579
|
|
|
143782
144580
|
// src/wecomPadding.ts
|
|
143783
144581
|
var PADDING_BLOCK = 32;
|
|
@@ -143808,7 +144606,7 @@ var decryptWecomMedia = (cipher, aesKey) => {
|
|
|
143808
144606
|
if (cipher.byteLength === 0 || cipher.byteLength % AES_BLOCK !== 0) {
|
|
143809
144607
|
throw new WecomDecryptError("the ciphertext is not a whole number of blocks");
|
|
143810
144608
|
}
|
|
143811
|
-
const decipher =
|
|
144609
|
+
const decipher = NodeCrypto8.createDecipheriv("aes-256-cbc", key, key.subarray(0, IV_BYTES));
|
|
143812
144610
|
decipher.setAutoPadding(false);
|
|
143813
144611
|
const plain = Buffer.concat([decipher.update(cipher), decipher.final()]);
|
|
143814
144612
|
const unpadded = stripPadding(plain);
|
|
@@ -143872,13 +144670,13 @@ var COLUMNS4 = [
|
|
|
143872
144670
|
{ key: "wecomBotSecret", kind: "password", labelKey: "column.wecomBotSecret.label" }
|
|
143873
144671
|
];
|
|
143874
144672
|
var defaultSleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
143875
|
-
var
|
|
144673
|
+
var credentials = (binding) => ({
|
|
143876
144674
|
botId: binding.values["wecomBotId"] ?? "",
|
|
143877
144675
|
botSecret: binding.values["wecomBotSecret"] ?? "",
|
|
143878
144676
|
endpoint: normalizeWecomEndpoint(binding.values["wecomEndpoint"] || DEFAULT_WECOM_ENDPOINT)
|
|
143879
144677
|
});
|
|
143880
144678
|
var wecomConfigIssues = (binding) => {
|
|
143881
|
-
const { botId, botSecret } =
|
|
144679
|
+
const { botId, botSecret } = credentials(binding);
|
|
143882
144680
|
const issues = [];
|
|
143883
144681
|
if (botId === "") issues.push({ column: "wecomBotId", code: "required" });
|
|
143884
144682
|
if (botSecret === "") issues.push({ column: "wecomBotSecret", code: "required" });
|
|
@@ -143886,6 +144684,7 @@ var wecomConfigIssues = (binding) => {
|
|
|
143886
144684
|
};
|
|
143887
144685
|
var makeWecomAdapter = (deps) => {
|
|
143888
144686
|
const clients = /* @__PURE__ */ new Map();
|
|
144687
|
+
const managedConnections = /* @__PURE__ */ new Map();
|
|
143889
144688
|
const t = deps.translate;
|
|
143890
144689
|
const liveStreams = /* @__PURE__ */ new Map();
|
|
143891
144690
|
const rememberStream = (key, entry) => {
|
|
@@ -143924,6 +144723,7 @@ ${kept}`;
|
|
|
143924
144723
|
};
|
|
143925
144724
|
return {
|
|
143926
144725
|
platform: "wecom",
|
|
144726
|
+
botIdentity: (bot) => credentials(bot).botId,
|
|
143927
144727
|
icon: "wecom",
|
|
143928
144728
|
label: "WeCom",
|
|
143929
144729
|
columns: COLUMNS4,
|
|
@@ -143931,15 +144731,22 @@ ${kept}`;
|
|
|
143931
144731
|
validate: wecomConfigIssues,
|
|
143932
144732
|
isConfigured: (binding) => wecomConfigIssues(binding).length === 0,
|
|
143933
144733
|
connect: (binding, handlers) => {
|
|
143934
|
-
const { botId, botSecret, endpoint } =
|
|
144734
|
+
const { botId, botSecret, endpoint } = credentials(binding);
|
|
144735
|
+
const managed = { endpoint };
|
|
144736
|
+
managedConnections.set(binding.rowId, managed);
|
|
143935
144737
|
let stopped = false;
|
|
143936
144738
|
let current = null;
|
|
143937
144739
|
const stopSignal = Promise.withResolvers();
|
|
144740
|
+
const onConnectionState = (state) => {
|
|
144741
|
+
if (stopped) return;
|
|
144742
|
+
managed.state = state;
|
|
144743
|
+
handlers.onConnectionState?.(state);
|
|
144744
|
+
};
|
|
143938
144745
|
const runOnce = async () => {
|
|
143939
144746
|
const socket = await deps.connectSocket(endpoint);
|
|
143940
144747
|
if (stopped) {
|
|
143941
144748
|
await socket.close();
|
|
143942
|
-
return false;
|
|
144749
|
+
return { healthy: false, stopRetrying: false };
|
|
143943
144750
|
}
|
|
143944
144751
|
const client = runWecomClient({
|
|
143945
144752
|
socket: {
|
|
@@ -143951,9 +144758,7 @@ ${kept}`;
|
|
|
143951
144758
|
botSecret,
|
|
143952
144759
|
handlers: {
|
|
143953
144760
|
onError: handlers.onError,
|
|
143954
|
-
onConnectionState
|
|
143955
|
-
if (!stopped) handlers.onConnectionState?.(state);
|
|
143956
|
-
},
|
|
144761
|
+
onConnectionState,
|
|
143957
144762
|
onMessage: (message) => {
|
|
143958
144763
|
if (message.chatType !== "single") return;
|
|
143959
144764
|
client.rememberRequest(message.chatId, message.requestId);
|
|
@@ -143983,20 +144788,25 @@ ${kept}`;
|
|
|
143983
144788
|
if (ok) deps.log.info("wecom connected", { bot: binding.displayName });
|
|
143984
144789
|
return ok;
|
|
143985
144790
|
});
|
|
143986
|
-
await client.ended;
|
|
144791
|
+
const reason = await client.ended;
|
|
143987
144792
|
if (clients.get(binding.rowId) === client) clients.delete(binding.rowId);
|
|
143988
144793
|
current = null;
|
|
143989
|
-
return
|
|
144794
|
+
return {
|
|
144795
|
+
healthy: await healthy,
|
|
144796
|
+
stopRetrying: reason === "taken-over" || reason === "credentials-rejected"
|
|
144797
|
+
};
|
|
143990
144798
|
};
|
|
143991
144799
|
const supervise = async () => {
|
|
143992
144800
|
let backoff = BACKOFF_START_MS2;
|
|
143993
144801
|
for (; ; ) {
|
|
143994
144802
|
if (stopped) return;
|
|
143995
144803
|
try {
|
|
143996
|
-
|
|
144804
|
+
const result = await runOnce();
|
|
144805
|
+
if (result.stopRetrying) return;
|
|
144806
|
+
if (result.healthy) backoff = BACKOFF_START_MS2;
|
|
143997
144807
|
} catch (error) {
|
|
143998
144808
|
if (stopped) return;
|
|
143999
|
-
|
|
144809
|
+
onConnectionState({ state: "failed", error, reason: "disconnected" });
|
|
144000
144810
|
handlers.onError(error instanceof Error ? error.message : String(error));
|
|
144001
144811
|
}
|
|
144002
144812
|
if (stopped) return;
|
|
@@ -144011,6 +144821,9 @@ ${kept}`;
|
|
|
144011
144821
|
stopped = true;
|
|
144012
144822
|
stopSignal.resolve();
|
|
144013
144823
|
current?.close();
|
|
144824
|
+
if (managedConnections.get(binding.rowId) === managed) {
|
|
144825
|
+
managedConnections.delete(binding.rowId);
|
|
144826
|
+
}
|
|
144014
144827
|
}
|
|
144015
144828
|
};
|
|
144016
144829
|
},
|
|
@@ -144023,12 +144836,33 @@ ${kept}`;
|
|
|
144023
144836
|
await client.sendText(binding.chatId, text4);
|
|
144024
144837
|
},
|
|
144025
144838
|
checkConnection: async (bot) => {
|
|
144026
|
-
const
|
|
144839
|
+
const managed = managedConnections.get(bot.rowId);
|
|
144840
|
+
if (managed !== void 0) {
|
|
144841
|
+
const state = managed.state;
|
|
144842
|
+
const detail = `managed connection, ${managed.endpoint}`;
|
|
144843
|
+
if (state?.state === "connected") {
|
|
144844
|
+
return {
|
|
144845
|
+
stage: "aibot_subscribe",
|
|
144846
|
+
ok: true,
|
|
144847
|
+
detail: `subscription confirmed (${detail})`
|
|
144848
|
+
};
|
|
144849
|
+
}
|
|
144850
|
+
return {
|
|
144851
|
+
stage: "aibot_subscribe",
|
|
144852
|
+
ok: false,
|
|
144853
|
+
detail: state === void 0 ? `connecting (${detail})` : `${state.error === void 0 ? state.reason ?? "connection failed" : describeError(state.error)} (${detail})`,
|
|
144854
|
+
// 还没回报状态只是开连到订阅回执之间:宿主开连 10 秒、订阅回执 10 秒(wecomApi 的 SUBSCRIBE_TIMEOUT_MS)
|
|
144855
|
+
// 都会超时转 failed,所以不会一直停在「还在连接」。
|
|
144856
|
+
...state === void 0 ? { hint: "connecting" } : state.state === "failed" && state.hint !== void 0 ? { hint: state.hint } : {}
|
|
144857
|
+
};
|
|
144858
|
+
}
|
|
144859
|
+
const { botId, botSecret, endpoint } = credentials(bot);
|
|
144027
144860
|
if (botId === "" || botSecret === "") {
|
|
144028
144861
|
return {
|
|
144029
144862
|
stage: "credentials",
|
|
144030
144863
|
ok: false,
|
|
144031
|
-
detail: "missing bot id or secret in saved config"
|
|
144864
|
+
detail: "missing bot id or secret in saved config",
|
|
144865
|
+
hint: "wecomBotCredentials"
|
|
144032
144866
|
};
|
|
144033
144867
|
}
|
|
144034
144868
|
let stage = "websocket";
|
|
@@ -144036,6 +144870,7 @@ ${kept}`;
|
|
|
144036
144870
|
const socket = await deps.connectSocket(endpoint);
|
|
144037
144871
|
stage = "aibot_subscribe";
|
|
144038
144872
|
let detail = "WeCom connection closed before subscribe ack";
|
|
144873
|
+
let hint;
|
|
144039
144874
|
let closing;
|
|
144040
144875
|
const client = runWecomClient({
|
|
144041
144876
|
socket: {
|
|
@@ -144047,6 +144882,9 @@ ${kept}`;
|
|
|
144047
144882
|
botSecret,
|
|
144048
144883
|
handlers: {
|
|
144049
144884
|
onMessage: () => void 0,
|
|
144885
|
+
onConnectionState: (state) => {
|
|
144886
|
+
if (state.state === "failed") hint = state.hint;
|
|
144887
|
+
},
|
|
144050
144888
|
onError: (error) => {
|
|
144051
144889
|
detail = error;
|
|
144052
144890
|
}
|
|
@@ -144054,7 +144892,12 @@ ${kept}`;
|
|
|
144054
144892
|
});
|
|
144055
144893
|
try {
|
|
144056
144894
|
const ok = await client.ready;
|
|
144057
|
-
return {
|
|
144895
|
+
return {
|
|
144896
|
+
stage,
|
|
144897
|
+
ok,
|
|
144898
|
+
detail: ok ? `subscription confirmed (${endpoint})` : detail,
|
|
144899
|
+
...!ok && hint !== void 0 ? { hint } : {}
|
|
144900
|
+
};
|
|
144058
144901
|
} finally {
|
|
144059
144902
|
client.close();
|
|
144060
144903
|
await closing;
|
|
@@ -144406,7 +145249,7 @@ import * as NodeHttp2 from "node:http";
|
|
|
144406
145249
|
import * as NodeNet from "node:net";
|
|
144407
145250
|
|
|
144408
145251
|
// src/wecomAppCrypto.ts
|
|
144409
|
-
import * as
|
|
145252
|
+
import * as NodeCrypto9 from "node:crypto";
|
|
144410
145253
|
var AES_KEY_BYTES = 32;
|
|
144411
145254
|
var RANDOM_BYTES = 16;
|
|
144412
145255
|
var WecomAppCryptoError = class extends Error {
|
|
@@ -144429,13 +145272,13 @@ var timingSafeEqualString2 = (a, b) => {
|
|
|
144429
145272
|
const left = Buffer.from(a);
|
|
144430
145273
|
const right = Buffer.from(b);
|
|
144431
145274
|
if (left.length === 0 || left.length !== right.length) return false;
|
|
144432
|
-
return
|
|
145275
|
+
return NodeCrypto9.timingSafeEqual(left, right);
|
|
144433
145276
|
};
|
|
144434
|
-
var computeSignature = (token, timestamp, nonce, encrypt) =>
|
|
145277
|
+
var computeSignature = (token, timestamp, nonce, encrypt) => NodeCrypto9.createHash("sha1").update([token, timestamp, nonce, encrypt].sort().join("")).digest("hex");
|
|
144435
145278
|
var verifySignature = (token, timestamp, nonce, encrypt, signature) => timingSafeEqualString2(computeSignature(token, timestamp, nonce, encrypt), signature);
|
|
144436
145279
|
var decrypt = (encodingAESKey, encryptText, expectReceiveId) => {
|
|
144437
145280
|
const { key, iv } = parseEncodingAESKey(encodingAESKey);
|
|
144438
|
-
const decipher =
|
|
145281
|
+
const decipher = NodeCrypto9.createDecipheriv("aes-256-cbc", key, iv);
|
|
144439
145282
|
decipher.setAutoPadding(false);
|
|
144440
145283
|
let deciphered;
|
|
144441
145284
|
try {
|
|
@@ -144494,10 +145337,10 @@ var createWecomAppCrypto = (input) => {
|
|
|
144494
145337
|
decrypt: (encryptText) => decrypt(encodingAESKey, encryptText, corpId).message
|
|
144495
145338
|
};
|
|
144496
145339
|
};
|
|
144497
|
-
var deriveCallbackPathSecret = (token, encodingAESKey) =>
|
|
145340
|
+
var deriveCallbackPathSecret = (token, encodingAESKey) => NodeCrypto9.createHash("sha256").update(`${token.trim()}:${encodingAESKey.trim()}`).digest("hex").slice(0, 24);
|
|
144498
145341
|
|
|
144499
145342
|
// src/wecomAppParse.ts
|
|
144500
|
-
var
|
|
145343
|
+
var isRecord5 = (value2) => value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
144501
145344
|
var isXmlFormat = (raw) => {
|
|
144502
145345
|
const trimmed = raw.trim();
|
|
144503
145346
|
return trimmed.startsWith("<") && trimmed.endsWith(">");
|
|
@@ -144531,7 +145374,6 @@ var parseWecomAppPlainMessage = (raw) => {
|
|
|
144531
145374
|
text: xml["Content"] !== void 0 ? { content: xml["Content"] } : void 0,
|
|
144532
145375
|
image: xml["PicUrl"] ? { url: xml["PicUrl"] } : void 0,
|
|
144533
145376
|
mediaId: xml["MediaId"] ?? xml["mediaid"],
|
|
144534
|
-
recognition: xml["Recognition"],
|
|
144535
145377
|
event: (xml["Event"] ?? xml["event"] ?? "").toLowerCase() || void 0,
|
|
144536
145378
|
eventKey: xml["EventKey"],
|
|
144537
145379
|
chatid: xml["ChatId"]
|
|
@@ -144543,7 +145385,7 @@ var parseWecomAppPlainMessage = (raw) => {
|
|
|
144543
145385
|
} catch {
|
|
144544
145386
|
return null;
|
|
144545
145387
|
}
|
|
144546
|
-
if (!
|
|
145388
|
+
if (!isRecord5(data)) return null;
|
|
144547
145389
|
const fromRaw = data["from"];
|
|
144548
145390
|
const textRaw = data["text"];
|
|
144549
145391
|
const imageRaw = data["image"];
|
|
@@ -144552,12 +145394,11 @@ var parseWecomAppPlainMessage = (raw) => {
|
|
|
144552
145394
|
msgtype: String(data["msgtype"] ?? data["MsgType"] ?? "").toLowerCase(),
|
|
144553
145395
|
createTime: data["createTime"] !== void 0 ? String(data["createTime"]) : data["CreateTime"] !== void 0 ? String(data["CreateTime"]) : void 0,
|
|
144554
145396
|
agentId: data["agentid"] !== void 0 ? String(data["agentid"]) : data["AgentID"] !== void 0 ? String(data["AgentID"]) : void 0,
|
|
144555
|
-
from:
|
|
145397
|
+
from: isRecord5(fromRaw) ? { userid: typeof fromRaw["userid"] === "string" ? fromRaw["userid"] : void 0 } : typeof data["FromUserName"] === "string" ? { userid: data["FromUserName"] } : void 0,
|
|
144556
145398
|
to: typeof data["to"] === "string" ? data["to"] : void 0,
|
|
144557
|
-
text:
|
|
144558
|
-
image:
|
|
145399
|
+
text: isRecord5(textRaw) ? { content: typeof textRaw["content"] === "string" ? textRaw["content"] : void 0 } : void 0,
|
|
145400
|
+
image: isRecord5(imageRaw) ? { url: typeof imageRaw["url"] === "string" ? imageRaw["url"] : void 0 } : void 0,
|
|
144559
145401
|
mediaId: typeof data["mediaid"] === "string" ? data["mediaid"] : void 0,
|
|
144560
|
-
recognition: typeof data["recognition"] === "string" ? data["recognition"] : void 0,
|
|
144561
145402
|
event: String(data["event"] ?? data["eventtype"] ?? "").toLowerCase() || void 0,
|
|
144562
145403
|
eventKey: typeof data["eventkey"] === "string" ? data["eventkey"] : void 0,
|
|
144563
145404
|
chatid: typeof data["chatid"] === "string" ? data["chatid"] : void 0
|
|
@@ -144621,13 +145462,16 @@ var startWecomAppCallbackServer = async (options) => {
|
|
|
144621
145462
|
}
|
|
144622
145463
|
try {
|
|
144623
145464
|
if (!crypto2.verifySignature(signature, timestamp, nonce, echostr)) {
|
|
145465
|
+
route.onVerification?.(false);
|
|
144624
145466
|
writeText(res, 401, "unauthorized");
|
|
144625
145467
|
return;
|
|
144626
145468
|
}
|
|
144627
145469
|
const plaintext = crypto2.decrypt(echostr);
|
|
145470
|
+
route.onVerification?.(true);
|
|
144628
145471
|
route.log?.info("wecom-app URL verification succeeded", { bot: route.botRowId });
|
|
144629
145472
|
writeText(res, 200, plaintext);
|
|
144630
145473
|
} catch (error) {
|
|
145474
|
+
route.onVerification?.(false);
|
|
144631
145475
|
route.log?.warn("wecom-app URL verification failed", {
|
|
144632
145476
|
bot: route.botRowId,
|
|
144633
145477
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -144689,11 +145533,13 @@ var startWecomAppCallbackServer = async (options) => {
|
|
|
144689
145533
|
let plaintext;
|
|
144690
145534
|
try {
|
|
144691
145535
|
if (!crypto2.verifySignature(signature, timestamp, nonce, encrypt)) {
|
|
145536
|
+
route.onVerification?.(false);
|
|
144692
145537
|
writeText(res, 401, "unauthorized");
|
|
144693
145538
|
return;
|
|
144694
145539
|
}
|
|
144695
145540
|
plaintext = crypto2.decrypt(encrypt);
|
|
144696
145541
|
} catch (error) {
|
|
145542
|
+
route.onVerification?.(false);
|
|
144697
145543
|
route.log?.warn("wecom-app callback signature or decrypt failed", {
|
|
144698
145544
|
bot: route.botRowId,
|
|
144699
145545
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -144701,6 +145547,7 @@ var startWecomAppCallbackServer = async (options) => {
|
|
|
144701
145547
|
writeText(res, 401, "unauthorized");
|
|
144702
145548
|
return;
|
|
144703
145549
|
}
|
|
145550
|
+
route.onVerification?.(true);
|
|
144704
145551
|
const message = parseWecomAppPlainMessage(plaintext);
|
|
144705
145552
|
if (message === null) {
|
|
144706
145553
|
writeText(res, 400, "invalid message");
|
|
@@ -144879,11 +145726,11 @@ var validateWecomAppBot = (bot) => {
|
|
|
144879
145726
|
};
|
|
144880
145727
|
var readWecomAppConnectConfig = (bot) => {
|
|
144881
145728
|
const issues = validateWecomAppBot(bot);
|
|
144882
|
-
const
|
|
144883
|
-
const listenPort = parseListenPort(
|
|
144884
|
-
const listenHost = parseListenHost(
|
|
145729
|
+
const credentials2 = wecomAppCredentials(bot);
|
|
145730
|
+
const listenPort = parseListenPort(credentials2.listenPortRaw);
|
|
145731
|
+
const listenHost = parseListenHost(credentials2.listenHostRaw);
|
|
144885
145732
|
if (issues.length > 0 || listenPort === null || listenHost === null) return { ok: false, issues };
|
|
144886
|
-
return { ok: true, credentials:
|
|
145733
|
+
return { ok: true, credentials: credentials2, listenHost, listenPort };
|
|
144887
145734
|
};
|
|
144888
145735
|
|
|
144889
145736
|
// src/wecomAppAdapter.ts
|
|
@@ -144943,6 +145790,7 @@ var WECOM_APP_MIN_BYTES = 5;
|
|
|
144943
145790
|
var WECOM_APP_IMAGE_TYPES = /* @__PURE__ */ new Set(["image/jpeg", "image/png"]);
|
|
144944
145791
|
var CREDENTIAL_ERROR_CODES = /* @__PURE__ */ new Set(["40001", "40013", "40014", "40056", "42001", "60020"]);
|
|
144945
145792
|
var joinCallbackUrl = (baseOrOrigin, path) => `${baseOrOrigin}${path}`;
|
|
145793
|
+
var callbackHealthKey = (rowId, c) => JSON.stringify([rowId, c.corpId, c.token, c.encodingAESKey]);
|
|
144946
145794
|
var wecomAppPublicFields = (bot) => {
|
|
144947
145795
|
const c = wecomAppCredentials(bot);
|
|
144948
145796
|
if (c.token === "" || c.encodingAESKey === "") {
|
|
@@ -144979,6 +145827,7 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
144979
145827
|
fetch: deps.fetch
|
|
144980
145828
|
});
|
|
144981
145829
|
const callbackHub = makeWecomAppCallbackHub();
|
|
145830
|
+
const callbackHealth = /* @__PURE__ */ new Map();
|
|
144982
145831
|
const connectionFor = (binding) => {
|
|
144983
145832
|
const api = apis.get(binding.rowId);
|
|
144984
145833
|
if (api === void 0) {
|
|
@@ -145008,7 +145857,6 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
145008
145857
|
});
|
|
145009
145858
|
};
|
|
145010
145859
|
const toInbound = (message, handlers, botName) => {
|
|
145011
|
-
if (message.msgtype === "event") return;
|
|
145012
145860
|
if (message.chatid !== void 0 && message.chatid !== "") {
|
|
145013
145861
|
deps.log.info("wecom-app ignored a group message; only direct messages are supported", {
|
|
145014
145862
|
bot: botName
|
|
@@ -145017,6 +145865,12 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
145017
145865
|
}
|
|
145018
145866
|
const chatId = message.from?.userid ?? "";
|
|
145019
145867
|
if (chatId === "") return;
|
|
145868
|
+
if (message.msgtype === "event") {
|
|
145869
|
+
if (message.event === "subscribe" || message.event === "enter_agent") {
|
|
145870
|
+
handlers.onGreeting?.(chatId, chatId);
|
|
145871
|
+
}
|
|
145872
|
+
return;
|
|
145873
|
+
}
|
|
145020
145874
|
const messageId = message.msgid ?? `${chatId}:${message.createTime ?? Date.now()}`;
|
|
145021
145875
|
const base2 = { messageId, chatId, senderId: chatId };
|
|
145022
145876
|
if (message.msgtype === "text") {
|
|
@@ -145042,6 +145896,10 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
145042
145896
|
};
|
|
145043
145897
|
return {
|
|
145044
145898
|
platform: "wecom-app",
|
|
145899
|
+
botIdentity: (bot) => {
|
|
145900
|
+
const { corpId, agentId } = wecomAppCredentials(bot);
|
|
145901
|
+
return JSON.stringify([corpId, agentId]);
|
|
145902
|
+
},
|
|
145045
145903
|
icon: "wecom",
|
|
145046
145904
|
label: deps.translate("platform.wecom-app"),
|
|
145047
145905
|
columns: COLUMNS5,
|
|
@@ -145067,8 +145925,7 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
145067
145925
|
};
|
|
145068
145926
|
},
|
|
145069
145927
|
/**
|
|
145070
|
-
*
|
|
145071
|
-
* 凭据——和真实发送同一条路,向 gettoken 换一次 access_token。
|
|
145928
|
+
* 出站凭据走真实发送的 gettoken;入站只能报告本次运行已观察到的回调。
|
|
145072
145929
|
*/
|
|
145073
145930
|
checkConnection: async (bot) => {
|
|
145074
145931
|
const outbound = validateWecomAppBot(bot).filter(
|
|
@@ -145089,11 +145946,21 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
145089
145946
|
};
|
|
145090
145947
|
}
|
|
145091
145948
|
try {
|
|
145092
|
-
|
|
145949
|
+
const credentials2 = wecomAppCredentials(bot);
|
|
145950
|
+
await newApi(credentials2).getAccessToken();
|
|
145951
|
+
const health = callbackHealth.get(callbackHealthKey(bot.rowId, credentials2));
|
|
145952
|
+
if (health?.lastReceivedAt === void 0) {
|
|
145953
|
+
return {
|
|
145954
|
+
stage: "callback",
|
|
145955
|
+
ok: false,
|
|
145956
|
+
hint: health?.lastRejectedAt === void 0 ? "wecomCallbackNotSeen" : "wecomCallbackSignature",
|
|
145957
|
+
detail: health?.lastRejectedAt === void 0 ? "access_token ok; no verified callback since this startup" : `access_token ok; last callback signature or decryption failure since this startup: ${health.lastRejectedAt}`
|
|
145958
|
+
};
|
|
145959
|
+
}
|
|
145093
145960
|
return {
|
|
145094
|
-
stage: "
|
|
145961
|
+
stage: "callback",
|
|
145095
145962
|
ok: true,
|
|
145096
|
-
detail:
|
|
145963
|
+
detail: `access_token ok; last verified callback since this startup: ${health.lastReceivedAt}`
|
|
145097
145964
|
};
|
|
145098
145965
|
} catch (error) {
|
|
145099
145966
|
return {
|
|
@@ -145131,7 +145998,14 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
145131
145998
|
encodingAESKey: c.encodingAESKey,
|
|
145132
145999
|
corpId: c.corpId,
|
|
145133
146000
|
log: deps.log,
|
|
145134
|
-
onInbound: (message) => toInbound(message, handlers, bot.displayName)
|
|
146001
|
+
onInbound: (message) => toInbound(message, handlers, bot.displayName),
|
|
146002
|
+
onVerification: (verified) => {
|
|
146003
|
+
const key = callbackHealthKey(bot.rowId, c);
|
|
146004
|
+
callbackHealth.set(key, {
|
|
146005
|
+
...callbackHealth.get(key),
|
|
146006
|
+
...verified ? { lastReceivedAt: (/* @__PURE__ */ new Date()).toISOString() } : { lastRejectedAt: (/* @__PURE__ */ new Date()).toISOString() }
|
|
146007
|
+
});
|
|
146008
|
+
}
|
|
145135
146009
|
});
|
|
145136
146010
|
} catch (error) {
|
|
145137
146011
|
apis.delete(bot.rowId);
|
|
@@ -145221,10 +146095,18 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
145221
146095
|
};
|
|
145222
146096
|
|
|
145223
146097
|
// src/index.ts
|
|
145224
|
-
var
|
|
146098
|
+
var definition = {
|
|
145225
146099
|
id: "im",
|
|
145226
|
-
apiVersion:
|
|
145227
|
-
|
|
146100
|
+
apiVersion: 20,
|
|
146101
|
+
// 第二个参数只给插件自己的测试夹具:换掉文件 IO 与派发重试的等待。宿主只传 ctx。
|
|
146102
|
+
activate: async (ctx, testing = {}) => {
|
|
146103
|
+
const { dispatchSleep } = testing;
|
|
146104
|
+
const inboundState = makeInboundStore({
|
|
146105
|
+
dataDir: ctx.paths.dataDir,
|
|
146106
|
+
log: ctx.log,
|
|
146107
|
+
io: testing.inboundIo
|
|
146108
|
+
});
|
|
146109
|
+
ctx.onDispose(inboundState.flush);
|
|
145228
146110
|
const { t, setLanguage } = makeLiveTranslate(
|
|
145229
146111
|
parseReplyLanguage(ctx.config[REPLY_LANGUAGE_FIELD])
|
|
145230
146112
|
);
|
|
@@ -145246,6 +146128,8 @@ var imPlugin = definePlugin({
|
|
|
145246
146128
|
// Telegram 在不少网络里要绕代理,而绕不绕、绕哪个是按行配的 —— 这是插件面上
|
|
145247
146129
|
// `ctx.proxies` 的第一个真实消费者。
|
|
145248
146130
|
fetchVia: (proxy) => ctx.proxies.fetch(proxy),
|
|
146131
|
+
readCursor: inboundState.readCursor,
|
|
146132
|
+
saveCursor: inboundState.saveCursor,
|
|
145249
146133
|
translate: t,
|
|
145250
146134
|
log: {
|
|
145251
146135
|
info: (message, fields) => ctx.log.info(message, fields),
|
|
@@ -145283,13 +146167,13 @@ var imPlugin = definePlugin({
|
|
|
145283
146167
|
ctx.settingsCard(buildSettingsCard(adapters));
|
|
145284
146168
|
const updateSettings = makeSettingsUpdater(ctx.settings);
|
|
145285
146169
|
registerLarkSetup(ctx, { updateSettings });
|
|
145286
|
-
registerSettingsRowActions(ctx, adapters);
|
|
145287
146170
|
const core = makeImCore({
|
|
145288
146171
|
host: {
|
|
145289
146172
|
log: ctx.log,
|
|
145290
146173
|
projections: ctx.projections,
|
|
145291
146174
|
projects: ctx.projects,
|
|
145292
146175
|
threads: ctx.threads,
|
|
146176
|
+
commands: ctx.commands,
|
|
145293
146177
|
approvals: ctx.approvals,
|
|
145294
146178
|
plugins: ctx.plugins,
|
|
145295
146179
|
attachments: ctx.attachments,
|
|
@@ -145302,7 +146186,8 @@ var imPlugin = definePlugin({
|
|
|
145302
146186
|
config: () => ctx.config,
|
|
145303
146187
|
derivedState: ctx.derivedState,
|
|
145304
146188
|
t,
|
|
145305
|
-
adapters
|
|
146189
|
+
adapters,
|
|
146190
|
+
inboundReady: inboundState.ready
|
|
145306
146191
|
});
|
|
145307
146192
|
const acknowledgements = makeAcknowledgements(core);
|
|
145308
146193
|
const failures = makeFailureNotices(core);
|
|
@@ -145338,7 +146223,9 @@ var imPlugin = definePlugin({
|
|
|
145338
146223
|
queueFlow: interactions.queueFlow,
|
|
145339
146224
|
rememberThreadProject: notifications.rememberThreadProject,
|
|
145340
146225
|
noteInboundUserMessage: webSync.noteInboundUserMessage,
|
|
145341
|
-
anchors: replyAnchors
|
|
146226
|
+
anchors: replyAnchors,
|
|
146227
|
+
failures,
|
|
146228
|
+
dispatchSleep
|
|
145342
146229
|
});
|
|
145343
146230
|
const sessions = makeSessions(core);
|
|
145344
146231
|
const modelPlan = makeModelPlan(core, turns);
|
|
@@ -145353,6 +146240,7 @@ var imPlugin = definePlugin({
|
|
|
145353
146240
|
});
|
|
145354
146241
|
const chatSetup = makeChatSetup(core, {
|
|
145355
146242
|
rows: projectRows,
|
|
146243
|
+
resumeMessage: (bot, message) => inbound.resumeMessage(bot, message),
|
|
145356
146244
|
openPicker: (chatKey, rows, reply) => panelWiring.panel.openProjects(chatKey, rows, reply)
|
|
145357
146245
|
});
|
|
145358
146246
|
const commands = makeCommands(core, {
|
|
@@ -145365,10 +146253,10 @@ var imPlugin = definePlugin({
|
|
|
145365
146253
|
openPanel: (chatKey, reply) => panelWiring.panel.open(chatKey, reply)
|
|
145366
146254
|
});
|
|
145367
146255
|
const inbound = makeInbound(core, {
|
|
146256
|
+
inboundState,
|
|
145368
146257
|
enqueueForChat: chatQueue.enqueueForChat,
|
|
145369
146258
|
commands,
|
|
145370
146259
|
chatSetup,
|
|
145371
|
-
rows: projectRows,
|
|
145372
146260
|
interactions,
|
|
145373
146261
|
approvals,
|
|
145374
146262
|
panelWiring,
|
|
@@ -145394,6 +146282,7 @@ var imPlugin = definePlugin({
|
|
|
145394
146282
|
]
|
|
145395
146283
|
});
|
|
145396
146284
|
const connections = startConnections(core, inbound.inboundHandlersFor);
|
|
146285
|
+
registerSettingsRowActions(ctx, adapters, connections);
|
|
145397
146286
|
registerSettingsCommands(ctx, adapters, updateSettings, connections);
|
|
145398
146287
|
const { bots, connecting } = connections.reconcile();
|
|
145399
146288
|
const deliveryHttp = makeDeliveryHttp({
|
|
@@ -145412,8 +146301,11 @@ var imPlugin = definePlugin({
|
|
|
145412
146301
|
}
|
|
145413
146302
|
ctx.log.info("chat plugin active", { bots, connecting });
|
|
145414
146303
|
}
|
|
145415
|
-
}
|
|
146304
|
+
};
|
|
146305
|
+
var activateImPlugin = definition.activate;
|
|
146306
|
+
var imPlugin = definePlugin(definition);
|
|
145416
146307
|
var index_default = imPlugin;
|
|
145417
146308
|
export {
|
|
146309
|
+
activateImPlugin,
|
|
145418
146310
|
index_default as default
|
|
145419
146311
|
};
|