@ycodium-ai/plugin-im 0.2.3208 → 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 +1002 -289
- package/locales/en.json +64 -14
- package/locales/zh-CN.json +64 -14
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -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",
|
|
@@ -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
|
};
|
|
@@ -127474,6 +127475,14 @@ var pickProject = (arg, listed, rows) => {
|
|
|
127474
127475
|
return prefixed.length === 1 ? prefixed[0]?.projectId ?? null : null;
|
|
127475
127476
|
};
|
|
127476
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
|
+
|
|
127477
127486
|
// src/sessionList.ts
|
|
127478
127487
|
var SESSION_LIST_MAX = 20;
|
|
127479
127488
|
var isSessionRunning = (status) => status === "running";
|
|
@@ -127488,7 +127497,10 @@ var ageLabel = (updatedAt, now, t) => {
|
|
|
127488
127497
|
if (hours < 24) return t("sessions.age.hours", { count: hours });
|
|
127489
127498
|
return t("sessions.age.days", { count: Math.floor(hours / 24) });
|
|
127490
127499
|
};
|
|
127491
|
-
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
|
+
);
|
|
127492
127504
|
var sessionLine = (row, position, now, t) => {
|
|
127493
127505
|
const title = row.title.replace(/\s+/g, " ").trim() || t("sessions.untitled");
|
|
127494
127506
|
const age = ageLabel(row.updatedAt, now, t);
|
|
@@ -128130,30 +128142,36 @@ var startConnections = (core, inboundHandlersFor) => {
|
|
|
128130
128142
|
}
|
|
128131
128143
|
};
|
|
128132
128144
|
};
|
|
128133
|
-
const
|
|
128145
|
+
const plan = () => {
|
|
128134
128146
|
const bots = disposed ? [] : core.bots();
|
|
128135
128147
|
const wanted = /* @__PURE__ */ new Map();
|
|
128136
|
-
const
|
|
128148
|
+
const credentials2 = /* @__PURE__ */ new Map();
|
|
128137
128149
|
const duplicates = /* @__PURE__ */ new Map();
|
|
128138
128150
|
for (const bot of bots) {
|
|
128139
128151
|
const adapter = adapterFor(bot.platform);
|
|
128140
128152
|
if (adapter === void 0) continue;
|
|
128141
128153
|
const fingerprint = `${bot.platform}:${adapter.columns.map((column) => bot.values[column.key] ?? "").join(" ")}`;
|
|
128142
|
-
const duplicateOf =
|
|
128154
|
+
const duplicateOf = credentials2.get(fingerprint);
|
|
128143
128155
|
if (duplicateOf !== void 0) {
|
|
128144
128156
|
duplicates.set(bot.rowId, duplicateOf);
|
|
128145
|
-
if (statuses.get(bot.rowId)?.duplicateOf !== duplicateOf) {
|
|
128146
|
-
record3(bot.rowId, { state: "duplicate", duplicateOf });
|
|
128147
|
-
}
|
|
128148
|
-
host.log.warn("chat did not connect a bot that repeats another bot's credentials", {
|
|
128149
|
-
bot: bot.displayName,
|
|
128150
|
-
platform: bot.platform
|
|
128151
|
-
});
|
|
128152
128157
|
continue;
|
|
128153
128158
|
}
|
|
128154
|
-
|
|
128159
|
+
credentials2.set(fingerprint, bot.rowId);
|
|
128155
128160
|
wanted.set(bot.rowId, { bot, key: connectionKey(adapter, bot) });
|
|
128156
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
|
+
}
|
|
128157
128175
|
for (const [rowId, connection] of live) {
|
|
128158
128176
|
if (wanted.get(rowId)?.key === connection.key) continue;
|
|
128159
128177
|
connection.close();
|
|
@@ -128174,6 +128192,23 @@ var startConnections = (core, inboundHandlersFor) => {
|
|
|
128174
128192
|
});
|
|
128175
128193
|
return {
|
|
128176
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
|
+
},
|
|
128177
128212
|
subscribe: (listener) => {
|
|
128178
128213
|
if (!disposed) listeners.add(listener);
|
|
128179
128214
|
listener(Object.fromEntries(statuses));
|
|
@@ -128982,6 +129017,25 @@ var makeInboundAttachments = (core) => {
|
|
|
128982
129017
|
|
|
128983
129018
|
// src/inbound.ts
|
|
128984
129019
|
import { projectOrNull } from "@ycodium-ai/plugin-api";
|
|
129020
|
+
|
|
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
|
+
});
|
|
129035
|
+
};
|
|
129036
|
+
};
|
|
129037
|
+
|
|
129038
|
+
// src/inbound.ts
|
|
128985
129039
|
var ARCHIVED_REPLY_COOLDOWN_MS = 10 * 6e4;
|
|
128986
129040
|
var UNSUPPORTED_REPLY_COOLDOWN_MS = 6e4;
|
|
128987
129041
|
var NOT_ALLOWED_REPLY_COOLDOWN_MS = 10 * 6e4;
|
|
@@ -128989,6 +129043,8 @@ var NOT_ALLOWED_REPLY_KEY_LIMIT = 1e3;
|
|
|
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
129049
|
const { prepareMessage } = deps.chatSetup;
|
|
128994
129050
|
const { questionFlow, handleButtonClick } = deps.interactions;
|
|
@@ -128997,11 +129053,11 @@ var makeInbound = (core, deps) => {
|
|
|
128997
129053
|
const { startTurn, receiveAttachmentsOnly, hasWaitingAttachments } = deps.turns;
|
|
128998
129054
|
const { startTeamGoal } = deps.teams;
|
|
128999
129055
|
const { markWorking } = deps.acknowledgements;
|
|
129000
|
-
const archivedReplyCooldown = makeCooldown(ARCHIVED_REPLY_COOLDOWN_MS,
|
|
129001
|
-
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);
|
|
129002
129058
|
const notAllowedReplyCooldown = makeCooldown(
|
|
129003
129059
|
NOT_ALLOWED_REPLY_COOLDOWN_MS,
|
|
129004
|
-
|
|
129060
|
+
now,
|
|
129005
129061
|
NOT_ALLOWED_REPLY_KEY_LIMIT
|
|
129006
129062
|
);
|
|
129007
129063
|
const { inboundState } = deps;
|
|
@@ -129037,6 +129093,7 @@ var makeInbound = (core, deps) => {
|
|
|
129037
129093
|
commit("chat-aliased", { rowId: bot.rowId, alias, chatId });
|
|
129038
129094
|
};
|
|
129039
129095
|
const settleChat = (bot, message) => {
|
|
129096
|
+
message = { ...message, originalChatId: message.originalChatId ?? message.chatId };
|
|
129040
129097
|
if (message.chatAlias === void 0) return message;
|
|
129041
129098
|
const known = chatAliasOf(core.state, bot.rowId, message.chatAlias);
|
|
129042
129099
|
if (known === void 0) noteChatAlias(bot, message.chatAlias, message.chatId);
|
|
@@ -129118,6 +129175,7 @@ var makeInbound = (core, deps) => {
|
|
|
129118
129175
|
},
|
|
129119
129176
|
noteChatAlias: (alias, chatId) => noteChatAlias(bot, alias, chatId),
|
|
129120
129177
|
knowsChatAlias: (alias) => chatAliasOf(core.state, bot.rowId, alias) !== void 0,
|
|
129178
|
+
onGreeting: (chatId, senderId) => greet(bot.rowId, chatId, senderId),
|
|
129121
129179
|
onMessage: (received) => {
|
|
129122
129180
|
const current = currentBot(bot.rowId);
|
|
129123
129181
|
if (current === void 0) return;
|
|
@@ -129137,7 +129195,7 @@ var makeInbound = (core, deps) => {
|
|
|
129137
129195
|
if (!unsupportedReplyCooldown.tryAcquire(chatKeyOf(arrived))) return;
|
|
129138
129196
|
await deliver(
|
|
129139
129197
|
arrived,
|
|
129140
|
-
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 })
|
|
129141
129199
|
);
|
|
129142
129200
|
});
|
|
129143
129201
|
return;
|
|
@@ -130038,6 +130096,8 @@ var consolePath = {
|
|
|
130038
130096
|
var APP_EVENTS = ["im.message.receive_v1"];
|
|
130039
130097
|
var CARD_CALLBACKS = ["card.action.trigger"];
|
|
130040
130098
|
var SCOPES = [
|
|
130099
|
+
// 让「补全权限」查询应用自身已开通的权限,并列出缺项。
|
|
130100
|
+
"application:application:self_manage",
|
|
130041
130101
|
// 收发消息、改卡片、读消息。
|
|
130042
130102
|
"im:message",
|
|
130043
130103
|
// 群里 @ 机器人的消息(入站的主路径)。
|
|
@@ -130062,7 +130122,7 @@ var SCOPES = [
|
|
|
130062
130122
|
// 「在弄了」表情回执(`larkApi.ts` 的 `addReaction`)。unconfirmed
|
|
130063
130123
|
"im:message.reactions:write_only"
|
|
130064
130124
|
];
|
|
130065
|
-
var isTenantScope = (name) => name.startsWith("im:");
|
|
130125
|
+
var isTenantScope = (name) => name.startsWith("im:") || name === "application:application:self_manage";
|
|
130066
130126
|
var appMarker = (environmentId) => `[ycodium:${environmentId.slice(0, 8)}]`;
|
|
130067
130127
|
var DEFAULT_APP_NAME = "Ycodium Agent";
|
|
130068
130128
|
var DEFAULT_APP_DESCRIPTION = "Ycodium chat bot";
|
|
@@ -130113,6 +130173,16 @@ var makeFlowItemSchema = (shape) => ({
|
|
|
130113
130173
|
},
|
|
130114
130174
|
fills: shape.fills,
|
|
130115
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
|
+
},
|
|
130116
130186
|
warnings: {
|
|
130117
130187
|
type: "array",
|
|
130118
130188
|
items: {
|
|
@@ -130126,6 +130196,7 @@ var makeFlowItemSchema = (shape) => ({
|
|
|
130126
130196
|
}
|
|
130127
130197
|
},
|
|
130128
130198
|
reasonKey: { type: "string" },
|
|
130199
|
+
noteKey: { type: "string" },
|
|
130129
130200
|
detail: { type: "string" }
|
|
130130
130201
|
},
|
|
130131
130202
|
oneOf: [
|
|
@@ -131769,8 +131840,8 @@ var resourceOf = (element, index) => {
|
|
|
131769
131840
|
}
|
|
131770
131841
|
return null;
|
|
131771
131842
|
};
|
|
131772
|
-
var placeholderOf = (
|
|
131773
|
-
var serializeElement = (element,
|
|
131843
|
+
var placeholderOf = (resource, index) => `<${resource.media} ${String(index)}>`;
|
|
131844
|
+
var serializeElement = (element, resourceText, botIds) => {
|
|
131774
131845
|
switch (element.tag) {
|
|
131775
131846
|
case "text":
|
|
131776
131847
|
return styledText(element.text ?? "", element.style);
|
|
@@ -131793,12 +131864,12 @@ ${element.text ?? ""}
|
|
|
131793
131864
|
case "img":
|
|
131794
131865
|
case "media":
|
|
131795
131866
|
case "file":
|
|
131796
|
-
return
|
|
131867
|
+
return resourceText ?? "";
|
|
131797
131868
|
default:
|
|
131798
131869
|
return "";
|
|
131799
131870
|
}
|
|
131800
131871
|
};
|
|
131801
|
-
var serializePost = (content, botIds = /* @__PURE__ */ new Set()) => {
|
|
131872
|
+
var serializePost = (content, botIds = /* @__PURE__ */ new Set(), renderResource = placeholderOf) => {
|
|
131802
131873
|
let parsed;
|
|
131803
131874
|
try {
|
|
131804
131875
|
parsed = JSON.parse(content);
|
|
@@ -131807,31 +131878,31 @@ var serializePost = (content, botIds = /* @__PURE__ */ new Set()) => {
|
|
|
131807
131878
|
}
|
|
131808
131879
|
const elements = (parsed.content ?? []).flat();
|
|
131809
131880
|
const resources = [];
|
|
131810
|
-
const
|
|
131881
|
+
const resourceTextOf = /* @__PURE__ */ new Map();
|
|
131811
131882
|
for (const element of elements) {
|
|
131812
131883
|
const resource = resourceOf(element, resources.length + 1);
|
|
131813
131884
|
if (resource === null) continue;
|
|
131814
131885
|
resources.push(resource);
|
|
131815
|
-
|
|
131886
|
+
resourceTextOf.set(element, renderResource(resource, resources.length));
|
|
131816
131887
|
}
|
|
131817
131888
|
const md2 = parsed.md;
|
|
131818
131889
|
if (typeof md2 === "string" && md2.trim() !== "") {
|
|
131819
|
-
const
|
|
131890
|
+
const resourceTextByKey = /* @__PURE__ */ new Map();
|
|
131820
131891
|
for (const element of elements) {
|
|
131821
|
-
const
|
|
131892
|
+
const resourceText = resourceTextOf.get(element);
|
|
131822
131893
|
const key = element.tag === "img" ? element.image_key : element.file_key;
|
|
131823
|
-
if (
|
|
131824
|
-
|
|
131894
|
+
if (resourceText !== void 0 && nonEmpty4(key)) {
|
|
131895
|
+
resourceTextByKey.set(key, resourceText);
|
|
131825
131896
|
}
|
|
131826
131897
|
}
|
|
131827
131898
|
const text5 = md2.replace(
|
|
131828
131899
|
/!\[(?:img|video|file)\]\(([^)]+)\)/g,
|
|
131829
|
-
(whole, rawKey) =>
|
|
131900
|
+
(whole, rawKey) => resourceTextByKey.get(rawKey.trim()) ?? whole
|
|
131830
131901
|
);
|
|
131831
131902
|
return { text: text5, resources };
|
|
131832
131903
|
}
|
|
131833
131904
|
const paragraphs = (parsed.content ?? []).map(
|
|
131834
|
-
(row) => row.map((element) => serializeElement(element,
|
|
131905
|
+
(row) => row.map((element) => serializeElement(element, resourceTextOf.get(element), botIds)).join("")
|
|
131835
131906
|
);
|
|
131836
131907
|
const text4 = paragraphs.map((line) => line.trim()).filter((line) => line !== "").join("\n");
|
|
131837
131908
|
if (text4 === "" && resources.length === 0) return void 0;
|
|
@@ -131972,6 +132043,7 @@ var LARK_TEXT_MAX_CHARS = 3500;
|
|
|
131972
132043
|
var LarkApiError = class extends Error {
|
|
131973
132044
|
operation;
|
|
131974
132045
|
code;
|
|
132046
|
+
msg;
|
|
131975
132047
|
status;
|
|
131976
132048
|
retryAfterMs;
|
|
131977
132049
|
constructor(operation, code2, message, http = {}) {
|
|
@@ -131979,11 +132051,13 @@ var LarkApiError = class extends Error {
|
|
|
131979
132051
|
this.name = "LarkApiError";
|
|
131980
132052
|
this.operation = operation;
|
|
131981
132053
|
this.code = code2;
|
|
132054
|
+
this.msg = message;
|
|
131982
132055
|
this.status = http.status;
|
|
131983
132056
|
this.retryAfterMs = http.retryAfterMs;
|
|
131984
132057
|
}
|
|
131985
132058
|
};
|
|
131986
|
-
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);
|
|
131987
132061
|
var LARK_RATE_LIMITED = 99991400;
|
|
131988
132062
|
var isReplyRejected = (error) => error instanceof LarkApiError && error.status === void 0 && error.code !== LARK_RATE_LIMITED;
|
|
131989
132063
|
var larkSendVerdict = (error) => {
|
|
@@ -131998,14 +132072,20 @@ var larkSendVerdict = (error) => {
|
|
|
131998
132072
|
};
|
|
131999
132073
|
var readEnvelope = async (operation, response) => {
|
|
132000
132074
|
if (!response.ok) {
|
|
132001
|
-
await response.
|
|
132002
|
-
|
|
132003
|
-
|
|
132004
|
-
|
|
132005
|
-
|
|
132006
|
-
|
|
132007
|
-
|
|
132008
|
-
|
|
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
|
+
);
|
|
132009
132089
|
}
|
|
132010
132090
|
const body = await response.json();
|
|
132011
132091
|
const envelope = body;
|
|
@@ -132026,6 +132106,19 @@ var fetchTenantToken = async (input) => {
|
|
|
132026
132106
|
}
|
|
132027
132107
|
return { token: body.tenant_access_token, expiresInSeconds: body.expire ?? 0 };
|
|
132028
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
|
+
};
|
|
132029
132122
|
var postMessage = async (input) => {
|
|
132030
132123
|
const headers = {
|
|
132031
132124
|
authorization: `Bearer ${input.token}`,
|
|
@@ -132063,16 +132156,31 @@ var postMessage = async (input) => {
|
|
|
132063
132156
|
});
|
|
132064
132157
|
return sent?.message_id ?? null;
|
|
132065
132158
|
};
|
|
132066
|
-
var
|
|
132159
|
+
var readMessageItems = async (input) => {
|
|
132067
132160
|
const response = await input.http(
|
|
132068
|
-
`${LARK_BASE}/im/v1/messages/${encodeURIComponent(input.messageId)}`,
|
|
132161
|
+
`${LARK_BASE}/im/v1/messages/${encodeURIComponent(input.messageId)}${input.withSenderName ? "?with_sender_name=true" : ""}`,
|
|
132069
132162
|
{
|
|
132070
132163
|
headers: { authorization: `Bearer ${input.token}` },
|
|
132071
132164
|
...input.signal === void 0 ? {} : { signal: input.signal }
|
|
132072
132165
|
}
|
|
132073
132166
|
);
|
|
132074
|
-
const body = await readEnvelope(
|
|
132075
|
-
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;
|
|
132076
132184
|
};
|
|
132077
132185
|
var fetchMessageThreadId = async (input) => (await readMessage(input))?.thread_id || void 0;
|
|
132078
132186
|
var fetchMessageReference = async (input) => {
|
|
@@ -137646,6 +137754,50 @@ var buildChoiceCard = (card) => {
|
|
|
137646
137754
|
|
|
137647
137755
|
// src/larkInbound.ts
|
|
137648
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
|
|
137649
137801
|
var isOwnMention = (mention, appId) => mention.id?.app_id === appId || mention.mentioned_type === "bot";
|
|
137650
137802
|
var mentionsApp = (event, appId) => (event.message?.mentions ?? []).some((mention) => isOwnMention(mention, appId));
|
|
137651
137803
|
var mentionsSomeoneElse = (event, appId) => (event.message?.mentions ?? []).some(
|
|
@@ -137659,12 +137811,12 @@ var passesGroupGate = (event, appId, groupMention, messageType) => {
|
|
|
137659
137811
|
};
|
|
137660
137812
|
var RESOURCE_MESSAGE_TYPES = /* @__PURE__ */ new Set(["image", "file", "audio", "media"]);
|
|
137661
137813
|
var UNSUPPORTED_MESSAGE_TYPES = /* @__PURE__ */ new Set([
|
|
137814
|
+
"audio",
|
|
137662
137815
|
"sticker",
|
|
137663
137816
|
"folder",
|
|
137664
137817
|
"share_chat",
|
|
137665
137818
|
"share_user",
|
|
137666
137819
|
"system",
|
|
137667
|
-
"merge_forward",
|
|
137668
137820
|
"interactive"
|
|
137669
137821
|
]);
|
|
137670
137822
|
var botIdsOf = (mentions, appId) => {
|
|
@@ -137702,10 +137854,6 @@ var readResource = (messageType, content) => {
|
|
|
137702
137854
|
const name = key("file_name") ?? "file";
|
|
137703
137855
|
return { fileKey, kind: "file", name, mimeType: mimeFromName(name) };
|
|
137704
137856
|
}
|
|
137705
|
-
if (messageType === "audio") {
|
|
137706
|
-
const fileKey = key("file_key");
|
|
137707
|
-
return fileKey === null ? null : { fileKey, kind: "file", name: "voice.opus", mimeType: "audio/ogg" };
|
|
137708
|
-
}
|
|
137709
137857
|
if (messageType === "media") {
|
|
137710
137858
|
const fileKey = key("file_key");
|
|
137711
137859
|
if (fileKey === null) return null;
|
|
@@ -137734,7 +137882,11 @@ var parseInboundMessage = (event, appId, groupMention = "always", topicOnMention
|
|
|
137734
137882
|
const rootGiven = typeof message.root_id === "string" && message.root_id !== "";
|
|
137735
137883
|
const rootId = rootGiven && message.root_id !== void 0 ? message.root_id : messageId;
|
|
137736
137884
|
const topic = chatType === "p2p" || !inThread ? {} : threadId === void 0 ? { topic: { rootMessageId: messageId } } : { topic: { rootMessageId: rootId, threadId, rootGiven } };
|
|
137737
|
-
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)) {
|
|
137738
137890
|
return {
|
|
137739
137891
|
messageId,
|
|
137740
137892
|
chatId,
|
|
@@ -137744,7 +137896,7 @@ var parseInboundMessage = (event, appId, groupMention = "always", topicOnMention
|
|
|
137744
137896
|
resources: [],
|
|
137745
137897
|
inThread,
|
|
137746
137898
|
...topic,
|
|
137747
|
-
unsupportedType: messageType
|
|
137899
|
+
...messageType === "merge_forward" ? { mergeForward: true, ...parentMessageId ? { parentMessageId } : {} } : { unsupportedType: messageType === "audio" ? "voice" : messageType }
|
|
137748
137900
|
};
|
|
137749
137901
|
}
|
|
137750
137902
|
const mentions = message.mentions ?? [];
|
|
@@ -137836,7 +137988,6 @@ var cardActionOperator = (event) => {
|
|
|
137836
137988
|
}
|
|
137837
137989
|
return null;
|
|
137838
137990
|
};
|
|
137839
|
-
var isLarkAppId = (appId) => /^cli_[0-9a-fA-F]{16}$/u.test(appId);
|
|
137840
137991
|
var startLarkInbound = (input) => {
|
|
137841
137992
|
const dispatcher = new import_node_sdk.EventDispatcher({}).register({
|
|
137842
137993
|
"im.message.receive_v1": (event) => {
|
|
@@ -137879,7 +138030,11 @@ var startLarkInbound = (input) => {
|
|
|
137879
138030
|
}
|
|
137880
138031
|
});
|
|
137881
138032
|
if (!isLarkAppId(input.appId)) {
|
|
137882
|
-
input.onConnectionState?.({
|
|
138033
|
+
input.onConnectionState?.({
|
|
138034
|
+
state: "failed",
|
|
138035
|
+
reason: "invalid-app-id",
|
|
138036
|
+
hint: "larkCredentials"
|
|
138037
|
+
});
|
|
137883
138038
|
return { close: () => void 0 };
|
|
137884
138039
|
}
|
|
137885
138040
|
let closed = false;
|
|
@@ -137888,7 +138043,13 @@ var startLarkInbound = (input) => {
|
|
|
137888
138043
|
};
|
|
137889
138044
|
const failed = (error) => {
|
|
137890
138045
|
if (!closed) {
|
|
137891
|
-
|
|
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
|
+
});
|
|
137892
138053
|
}
|
|
137893
138054
|
};
|
|
137894
138055
|
const client = new import_node_sdk.WSClient({
|
|
@@ -138124,15 +138285,157 @@ var makeLarkMessageContext = (deps) => {
|
|
|
138124
138285
|
if (message.chatType !== "group" || message.senderOpenId === null) return {};
|
|
138125
138286
|
const name = contactDenied ? null : userName(message.senderOpenId);
|
|
138126
138287
|
const chat = chatInfo(message.chatId);
|
|
138127
|
-
const fields = (
|
|
138128
|
-
...
|
|
138288
|
+
const fields = (senderName2, info) => ({
|
|
138289
|
+
...senderName2 ? { senderName: senderName2 } : {},
|
|
138129
138290
|
...info?.name ? { chatTitle: info.name } : {}
|
|
138130
138291
|
});
|
|
138131
|
-
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);
|
|
138132
138293
|
};
|
|
138133
138294
|
return { chatInfo, forMessage, loadReference, topicRootForSession };
|
|
138134
138295
|
};
|
|
138135
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
|
+
|
|
138136
138439
|
// src/larkAdapter.ts
|
|
138137
138440
|
var TOKEN_CACHE_MAX_AGE_MS = 30 * 6e4;
|
|
138138
138441
|
var COLUMNS = [
|
|
@@ -138164,21 +138467,9 @@ var splitTopicChatId = (chatId) => {
|
|
|
138164
138467
|
return at < 0 ? { chatId } : { chatId: chatId.slice(0, at), rootMessageId: chatId.slice(at + 1) };
|
|
138165
138468
|
};
|
|
138166
138469
|
var topicAliasOf = (chatId, threadId) => `${chatId}:${threadId}`;
|
|
138167
|
-
var credentials = (binding) => ({
|
|
138168
|
-
appId: binding.values["larkAppId"] ?? "",
|
|
138169
|
-
appSecret: binding.values["larkAppSecret"] ?? ""
|
|
138170
|
-
});
|
|
138171
|
-
var larkConfigIssues = (binding) => {
|
|
138172
|
-
const { appId, appSecret } = credentials(binding);
|
|
138173
|
-
const issues = [];
|
|
138174
|
-
if (appId === "") issues.push({ column: "larkAppId", code: "required" });
|
|
138175
|
-
else if (!isLarkAppId(appId)) issues.push({ column: "larkAppId", code: "format" });
|
|
138176
|
-
if (appSecret === "") issues.push({ column: "larkAppSecret", code: "required" });
|
|
138177
|
-
return issues;
|
|
138178
|
-
};
|
|
138179
138470
|
var makeLarkAdapter = (deps) => {
|
|
138180
138471
|
const tenantToken = async (binding) => {
|
|
138181
|
-
const { appId, appSecret } =
|
|
138472
|
+
const { appId, appSecret } = larkCredentials(binding);
|
|
138182
138473
|
const key = `lark-tenant-token.${appId}`;
|
|
138183
138474
|
const cached = await deps.cacheGet(key, TOKEN_CACHE_MAX_AGE_MS);
|
|
138184
138475
|
if (typeof cached?.token === "string") return cached.token;
|
|
@@ -138251,6 +138542,7 @@ var makeLarkAdapter = (deps) => {
|
|
|
138251
138542
|
};
|
|
138252
138543
|
return {
|
|
138253
138544
|
platform: "lark",
|
|
138545
|
+
botIdentity: (bot) => larkCredentials(bot).appId,
|
|
138254
138546
|
icon: "lark",
|
|
138255
138547
|
label: "Lark",
|
|
138256
138548
|
columns: COLUMNS,
|
|
@@ -138259,10 +138551,10 @@ var makeLarkAdapter = (deps) => {
|
|
|
138259
138551
|
const { chatId: groupId, rootMessageId } = splitTopicChatId(chatId);
|
|
138260
138552
|
return rootMessageId === void 0 ? void 0 : groupId;
|
|
138261
138553
|
},
|
|
138262
|
-
validate:
|
|
138263
|
-
isConfigured: (binding) =>
|
|
138554
|
+
validate: (binding) => larkCredentials(binding).issues,
|
|
138555
|
+
isConfigured: (binding) => larkCredentials(binding).issues.every((issue) => issue.code !== "required"),
|
|
138264
138556
|
connect: (binding, handlers) => {
|
|
138265
|
-
const { appId, appSecret } =
|
|
138557
|
+
const { appId, appSecret } = larkCredentials(binding);
|
|
138266
138558
|
const port = {
|
|
138267
138559
|
noteChatAlias: handlers.noteChatAlias,
|
|
138268
138560
|
knowsChatAlias: handlers.knowsChatAlias
|
|
@@ -138299,11 +138591,17 @@ var makeLarkAdapter = (deps) => {
|
|
|
138299
138591
|
arg: click.arg
|
|
138300
138592
|
}) ?? null,
|
|
138301
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;
|
|
138302
138600
|
const { topic } = message;
|
|
138303
138601
|
const source = context.forMessage(message);
|
|
138304
138602
|
const parentMessageId = message.parentMessageId;
|
|
138305
138603
|
const reference2 = parentMessageId === void 0 ? null : context.loadReference(parentMessageId);
|
|
138306
|
-
const deliver = (names, replyTo) => handlers.onMessage({
|
|
138604
|
+
const deliver = (ready, names, replyTo) => handlers.onMessage({
|
|
138307
138605
|
messageId: message.messageId,
|
|
138308
138606
|
// 已经开出来的话题就是它自己那个聊天;将要开出来的(主窗口里 @ 机器人)先算群的,
|
|
138309
138607
|
// 由核心决定要不要分出去。
|
|
@@ -138317,7 +138615,7 @@ var makeLarkAdapter = (deps) => {
|
|
|
138317
138615
|
},
|
|
138318
138616
|
senderId: message.senderOpenId,
|
|
138319
138617
|
...names,
|
|
138320
|
-
text:
|
|
138618
|
+
text: ready.text,
|
|
138321
138619
|
topicRootForSession: context.topicRootForSession(message),
|
|
138322
138620
|
...replyTo === void 0 ? {} : { replyTo },
|
|
138323
138621
|
chatKind: message.chatType === "p2p" ? "private" : "group",
|
|
@@ -138328,7 +138626,7 @@ var makeLarkAdapter = (deps) => {
|
|
|
138328
138626
|
mimeType: resource.mimeType,
|
|
138329
138627
|
kind: resource.kind
|
|
138330
138628
|
})),
|
|
138331
|
-
...
|
|
138629
|
+
...ready.unsupportedType === void 0 ? {} : { unsupportedType: ready.unsupportedType }
|
|
138332
138630
|
});
|
|
138333
138631
|
const pending = topic?.threadId === void 0 ? null : topicUnsettled(binding.rowId, message.chatId, {
|
|
138334
138632
|
threadId: topic.threadId,
|
|
@@ -138336,12 +138634,12 @@ var makeLarkAdapter = (deps) => {
|
|
|
138336
138634
|
});
|
|
138337
138635
|
const key = JSON.stringify([message.chatId, topic?.threadId]);
|
|
138338
138636
|
const earlier = pendingDeliveries.get(key);
|
|
138339
|
-
if (earlier === void 0 && pending === null && reference2 === null && !(source instanceof Promise)) {
|
|
138340
|
-
deliver(source);
|
|
138637
|
+
if (earlier === void 0 && pending === null && reference2 === null && !(prepared instanceof Promise) && !(source instanceof Promise)) {
|
|
138638
|
+
deliver(prepared, source);
|
|
138341
138639
|
} else {
|
|
138342
|
-
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]) => {
|
|
138343
138641
|
if (pendingDeliveries.get(key) === completed) pendingDeliveries.delete(key);
|
|
138344
|
-
if (open) deliver(names, replyTo ?? void 0);
|
|
138642
|
+
if (open) deliver(ready, names, replyTo ?? void 0);
|
|
138345
138643
|
}).catch((error) => handlers.onError(describeError(error))).finally(() => {
|
|
138346
138644
|
if (pendingDeliveries.get(key) === completed) pendingDeliveries.delete(key);
|
|
138347
138645
|
});
|
|
@@ -138380,17 +138678,15 @@ var makeLarkAdapter = (deps) => {
|
|
|
138380
138678
|
}
|
|
138381
138679
|
},
|
|
138382
138680
|
checkConnection: async (bot) => {
|
|
138383
|
-
const { appId, appSecret } =
|
|
138384
|
-
if (
|
|
138681
|
+
const { appId, appSecret, error: credentialError } = larkCredentials(bot);
|
|
138682
|
+
if (credentialError !== void 0) {
|
|
138385
138683
|
return {
|
|
138386
138684
|
stage: "credentials",
|
|
138387
138685
|
ok: false,
|
|
138388
|
-
detail:
|
|
138686
|
+
detail: credentialError,
|
|
138687
|
+
hint: "larkCredentials"
|
|
138389
138688
|
};
|
|
138390
138689
|
}
|
|
138391
|
-
if (!isLarkAppId(appId)) {
|
|
138392
|
-
return { stage: "credentials", ok: false, detail: "malformed app id in saved config" };
|
|
138393
|
-
}
|
|
138394
138690
|
try {
|
|
138395
138691
|
const { expiresInSeconds } = await fetchTenantToken({ http: deps.http, appId, appSecret });
|
|
138396
138692
|
return {
|
|
@@ -138399,7 +138695,13 @@ var makeLarkAdapter = (deps) => {
|
|
|
138399
138695
|
detail: `token ok; expiresIn=${String(expiresInSeconds)}s`
|
|
138400
138696
|
};
|
|
138401
138697
|
} catch (error) {
|
|
138402
|
-
|
|
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
|
+
};
|
|
138403
138705
|
}
|
|
138404
138706
|
},
|
|
138405
138707
|
sendImage: async (binding, image2) => {
|
|
@@ -138737,6 +139039,7 @@ var en_default = {
|
|
|
138737
139039
|
projectArchived: 'Project "{{title}}" is archived. Restore it, or send /repo to pick another project, then message again.',
|
|
138738
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.",
|
|
138739
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}}.",
|
|
138740
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.",
|
|
138741
139044
|
turnFailedNoDetail: "This turn didn't finish. Reference {{ref}}. Send the message again; if it keeps failing, send /new to start a new conversation.",
|
|
138742
139045
|
turnFailedBrief: "This turn didn't finish: {{detail}}\nSend the message again; if it keeps failing, send /new to start a new conversation.",
|
|
@@ -138753,9 +139056,11 @@ var en_default = {
|
|
|
138753
139056
|
attachmentsNoTeam: "Files cannot be sent to a team yet, so they were left out. Send /thread to go back to a normal session.",
|
|
138754
139057
|
unsupportedType: "I can't process {{type}} messages yet.",
|
|
138755
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.",
|
|
138756
139060
|
sendingFile: "Sending {{name}}:",
|
|
138757
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.`,
|
|
138758
|
-
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."
|
|
138759
139064
|
},
|
|
138760
139065
|
approval: {
|
|
138761
139066
|
allow: "Allow",
|
|
@@ -139090,21 +139395,41 @@ var en_default = {
|
|
|
139090
139395
|
failed: "Connection failed",
|
|
139091
139396
|
notSaved: "Save this row first (credentials are read from saved config only).",
|
|
139092
139397
|
hint: {
|
|
139398
|
+
connecting: "Still connecting. There is usually a result within 20 seconds; check again in a moment.",
|
|
139093
139399
|
badToken: "This does not look like a bot token. Copy it again from BotFather: digits, a colon, then the key.",
|
|
139094
|
-
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.",
|
|
139095
139401
|
notBot: "This token does not belong to a bot. Use the token BotFather gave you.",
|
|
139096
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.",
|
|
139097
139410
|
wecomTrustedIp: "Add this machine's outbound public IP to the app's trusted IP list in the WeCom admin console.",
|
|
139098
139411
|
wecomInvalidUser: "Check that the recipient, department or tag exists and is in this app's visible range.",
|
|
139099
|
-
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."
|
|
139100
139415
|
}
|
|
139101
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
|
+
},
|
|
139102
139428
|
copyDiagnostics: {
|
|
139103
139429
|
label: "Copy diagnostics",
|
|
139104
139430
|
description: "Run a connection probe and produce copy-paste diagnostics (no secrets).",
|
|
139105
139431
|
running: "Building diagnostics for {{rowId}}\u2026",
|
|
139106
|
-
done: "
|
|
139107
|
-
failed: "Diagnostics failed",
|
|
139432
|
+
done: "Diagnostic report ready \u2014 see details below",
|
|
139108
139433
|
notSaved: "Save this row first before copying diagnostics."
|
|
139109
139434
|
},
|
|
139110
139435
|
diagnostic: {
|
|
@@ -139112,16 +139437,23 @@ var en_default = {
|
|
|
139112
139437
|
},
|
|
139113
139438
|
larkRepair: {
|
|
139114
139439
|
label: "Repair scopes",
|
|
139115
|
-
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.",
|
|
139116
139441
|
step: {
|
|
139117
|
-
|
|
139118
|
-
|
|
139119
|
-
|
|
139120
|
-
|
|
139121
|
-
|
|
139122
|
-
|
|
139123
|
-
|
|
139124
|
-
|
|
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.",
|
|
139125
139457
|
notLark: "This row is not a saved Lark bot."
|
|
139126
139458
|
}
|
|
139127
139459
|
},
|
|
@@ -139168,11 +139500,13 @@ var en_default = {
|
|
|
139168
139500
|
connection: {
|
|
139169
139501
|
failed: "The last connection attempt failed.",
|
|
139170
139502
|
details: "Details: {{error}}",
|
|
139503
|
+
reconnect: "You can try again with Reconnect below.",
|
|
139171
139504
|
reason: {
|
|
139172
139505
|
"not-available": "This platform can't be connected yet.",
|
|
139173
139506
|
"invalid-app-id": "The App ID isn't in the right format.",
|
|
139174
139507
|
disconnected: "The connection to the platform is down.",
|
|
139175
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.",
|
|
139176
139510
|
"token-unavailable": "Couldn't get an access token.",
|
|
139177
139511
|
"credentials-rejected": "The platform rejected the credentials."
|
|
139178
139512
|
},
|
|
@@ -139196,7 +139530,7 @@ var en_default = {
|
|
|
139196
139530
|
done: "Done.",
|
|
139197
139531
|
close: "Close",
|
|
139198
139532
|
failedToRun: "Couldn't run this on the machine.",
|
|
139199
|
-
saveFirst: "Save or undo your changes first. These
|
|
139533
|
+
saveFirst: "Save or undo your changes first. These actions use the saved settings.",
|
|
139200
139534
|
qrTitle: "QR code to scan with the Lark app"
|
|
139201
139535
|
},
|
|
139202
139536
|
step: {
|
|
@@ -139222,6 +139556,24 @@ var en_default = {
|
|
|
139222
139556
|
callbackFollowsKeys: "This URL changes when Token or EncodingAESKey changes. Paste it into WeCom again after changing them."
|
|
139223
139557
|
}
|
|
139224
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
|
+
},
|
|
139225
139577
|
team: {
|
|
139226
139578
|
noRoster: "This project has no team roster yet. Save a roster in the Teams tab and choose a lead first.",
|
|
139227
139579
|
unavailable: "Could not read this project's team roster. Check that the Teams plugin is enabled and try again.",
|
|
@@ -139440,6 +139792,7 @@ var zh_CN_default = {
|
|
|
139440
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",
|
|
139441
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",
|
|
139442
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",
|
|
139443
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",
|
|
139444
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",
|
|
139445
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",
|
|
@@ -139456,9 +139809,11 @@ var zh_CN_default = {
|
|
|
139456
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",
|
|
139457
139810
|
unsupportedType: "\u6211\u8FD8\u4E0D\u80FD\u5904\u7406 {{type}} \u7C7B\u578B\u7684\u6D88\u606F\u3002",
|
|
139458
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",
|
|
139459
139813
|
sendingFile: "\u6B63\u5728\u53D1\u9001 {{name}}\uFF1A",
|
|
139460
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",
|
|
139461
|
-
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"
|
|
139462
139817
|
},
|
|
139463
139818
|
approval: {
|
|
139464
139819
|
allow: "\u5141\u8BB8",
|
|
@@ -139793,21 +140148,41 @@ var zh_CN_default = {
|
|
|
139793
140148
|
failed: "\u8FDE\u63A5\u5931\u8D25",
|
|
139794
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",
|
|
139795
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",
|
|
139796
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",
|
|
139797
|
-
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",
|
|
139798
140154
|
notBot: "\u8FD9\u4E2A token \u4E0D\u662F\u673A\u5668\u4EBA\u7684\u3002\u8BF7\u7528 BotFather \u7ED9\u7684\u90A3\u4E00\u4EFD\u3002",
|
|
139799
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",
|
|
139800
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",
|
|
139801
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",
|
|
139802
|
-
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"
|
|
139803
140168
|
}
|
|
139804
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
|
+
},
|
|
139805
140181
|
copyDiagnostics: {
|
|
139806
140182
|
label: "\u590D\u5236\u8BCA\u65AD",
|
|
139807
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",
|
|
139808
140184
|
running: "\u6B63\u5728\u751F\u6210\u8BCA\u65AD {{rowId}} \u2026",
|
|
139809
|
-
done: "\u8BCA\u65AD\u5DF2\u751F\u6210\uFF0C\u89C1\u4E0B\u65B9\u8BE6\u60C5",
|
|
139810
|
-
failed: "\u8BCA\u65AD\u5931\u8D25",
|
|
140185
|
+
done: "\u8BCA\u65AD\u62A5\u544A\u5DF2\u751F\u6210\uFF0C\u89C1\u4E0B\u65B9\u8BE6\u60C5",
|
|
139811
140186
|
notSaved: "\u8BF7\u5148\u4FDD\u5B58\u8FD9\u4E00\u884C\u518D\u590D\u5236\u8BCA\u65AD\u3002"
|
|
139812
140187
|
},
|
|
139813
140188
|
diagnostic: {
|
|
@@ -139815,16 +140190,23 @@ var zh_CN_default = {
|
|
|
139815
140190
|
},
|
|
139816
140191
|
larkRepair: {
|
|
139817
140192
|
label: "\u8865\u5168\u6743\u9650",
|
|
139818
|
-
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",
|
|
139819
140194
|
step: {
|
|
139820
|
-
|
|
139821
|
-
|
|
139822
|
-
|
|
139823
|
-
|
|
139824
|
-
|
|
139825
|
-
|
|
139826
|
-
|
|
139827
|
-
|
|
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",
|
|
139828
140210
|
notLark: "\u8FD9\u4E00\u884C\u4E0D\u662F\u5DF2\u4FDD\u5B58\u7684\u98DE\u4E66\u673A\u5668\u4EBA\u3002"
|
|
139829
140211
|
}
|
|
139830
140212
|
},
|
|
@@ -139871,11 +140253,13 @@ var zh_CN_default = {
|
|
|
139871
140253
|
connection: {
|
|
139872
140254
|
failed: "\u4E0A\u6B21\u8FDE\u63A5\u6CA1\u6709\u6210\u529F\u3002",
|
|
139873
140255
|
details: "\u8BE6\u60C5\uFF1A{{error}}",
|
|
140256
|
+
reconnect: "\u53EF\u4EE5\u70B9\u4E0B\u65B9\u7684\u300C\u91CD\u65B0\u8FDE\u63A5\u300D\u518D\u8BD5\u3002",
|
|
139874
140257
|
reason: {
|
|
139875
140258
|
"not-available": "\u8FD9\u4E2A\u5E73\u53F0\u8FD8\u4E0D\u80FD\u8FDE\u63A5\u3002",
|
|
139876
140259
|
"invalid-app-id": "App ID \u683C\u5F0F\u4E0D\u6B63\u786E\u3002",
|
|
139877
140260
|
disconnected: "\u548C\u5E73\u53F0\u7684\u8FDE\u63A5\u65AD\u4E86\u3002",
|
|
139878
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",
|
|
139879
140263
|
"token-unavailable": "\u6CA1\u80FD\u6362\u5230\u8BBF\u95EE\u4EE4\u724C\u3002",
|
|
139880
140264
|
"credentials-rejected": "\u5E73\u53F0\u62D2\u7EDD\u4E86\u51ED\u636E\u3002"
|
|
139881
140265
|
},
|
|
@@ -139899,7 +140283,7 @@ var zh_CN_default = {
|
|
|
139899
140283
|
done: "\u5B8C\u6210\u3002",
|
|
139900
140284
|
close: "\u5173\u95ED",
|
|
139901
140285
|
failedToRun: "\u6CA1\u80FD\u5728\u8FD9\u53F0\u673A\u5668\u4E0A\u8FD0\u884C\u3002",
|
|
139902
|
-
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",
|
|
139903
140287
|
qrTitle: "\u7528\u98DE\u4E66 App \u626B\u7684\u4E8C\u7EF4\u7801"
|
|
139904
140288
|
},
|
|
139905
140289
|
step: {
|
|
@@ -139925,6 +140309,24 @@ var zh_CN_default = {
|
|
|
139925
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"
|
|
139926
140310
|
}
|
|
139927
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
|
+
},
|
|
139928
140330
|
team: {
|
|
139929
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",
|
|
139930
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",
|
|
@@ -140390,7 +140792,7 @@ var PANEL_PAGE_SIZE = 8;
|
|
|
140390
140792
|
var PANEL_CARD_LIMIT = 200;
|
|
140391
140793
|
var LABEL_MAX_CHARS = 60;
|
|
140392
140794
|
var PICK_KINDS = /* @__PURE__ */ new Set(["sessions", "models", "projects"]);
|
|
140393
|
-
var
|
|
140795
|
+
var clip3 = (label) => {
|
|
140394
140796
|
const chars = Array.from(label);
|
|
140395
140797
|
return chars.length <= LABEL_MAX_CHARS ? label : `${chars.slice(0, LABEL_MAX_CHARS - 1).join("")}\u2026`;
|
|
140396
140798
|
};
|
|
@@ -140415,7 +140817,7 @@ var makePanelController = (deps) => {
|
|
|
140415
140817
|
}
|
|
140416
140818
|
};
|
|
140417
140819
|
const button = (label, op, arg, danger) => ({
|
|
140418
|
-
label:
|
|
140820
|
+
label: clip3(label),
|
|
140419
140821
|
action: { op, arg },
|
|
140420
140822
|
...danger === true ? { danger } : {}
|
|
140421
140823
|
});
|
|
@@ -140834,6 +141236,20 @@ var makeFailureNotices = (core) => {
|
|
|
140834
141236
|
const heldForCard = /* @__PURE__ */ new Map();
|
|
140835
141237
|
const reasonOf2 = (detail) => detail?.trim() ?? "";
|
|
140836
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
|
+
},
|
|
140837
141253
|
/** 一次失败通知的那句话(带新参考号,同号进日志):实时失败事件与重启补发各取一次。 */
|
|
140838
141254
|
forFailure: (threadId, detail) => {
|
|
140839
141255
|
const reference2 = diagnosticRef("msg");
|
|
@@ -141686,6 +142102,35 @@ var makeTeamGoals = (core) => {
|
|
|
141686
142102
|
// src/telegramAdapter.ts
|
|
141687
142103
|
import * as NodeCrypto5 from "node:crypto";
|
|
141688
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
|
+
|
|
141689
142134
|
// src/telegramApi.ts
|
|
141690
142135
|
var API_BASE = "https://api.telegram.org";
|
|
141691
142136
|
var POLL_TIMEOUT_SECONDS = 45;
|
|
@@ -141715,17 +142160,6 @@ var replyParameters = (messageId) => {
|
|
|
141715
142160
|
return messageId === void 0 || !Number.isSafeInteger(id) ? {} : { reply_parameters: { message_id: id, allow_sending_without_reply: true } };
|
|
141716
142161
|
};
|
|
141717
142162
|
var chatOnly = (chatId) => ({ chat_id: splitChatId(chatId).chat });
|
|
141718
|
-
var TelegramApiError = class extends Error {
|
|
141719
|
-
code;
|
|
141720
|
-
/** 限流(429)时平台给的等待秒数;别的错误没有。 */
|
|
141721
|
-
retryAfterSeconds;
|
|
141722
|
-
constructor(method, code2, description, retryAfterSeconds) {
|
|
141723
|
-
super(`telegram ${method} failed (${String(code2 ?? "?")}): ${description}`);
|
|
141724
|
-
this.name = "TelegramApiError";
|
|
141725
|
-
this.code = code2;
|
|
141726
|
-
this.retryAfterSeconds = retryAfterSeconds;
|
|
141727
|
-
}
|
|
141728
|
-
};
|
|
141729
142163
|
var telegramSendVerdict = (error) => error instanceof TelegramApiError && (error.code === 429 || error.code === 503 && error.retryAfterSeconds !== void 0) ? {
|
|
141730
142164
|
retry: true,
|
|
141731
142165
|
afterMs: error.retryAfterSeconds === void 0 ? void 0 : error.retryAfterSeconds * 1e3
|
|
@@ -141760,7 +142194,6 @@ var resourceOf2 = (message) => {
|
|
|
141760
142194
|
};
|
|
141761
142195
|
}
|
|
141762
142196
|
const media = [
|
|
141763
|
-
[message.voice, "voice.ogg", "audio/ogg"],
|
|
141764
142197
|
[message.audio, "audio", "application/octet-stream"],
|
|
141765
142198
|
[message.video, "video.mp4", "video/mp4"],
|
|
141766
142199
|
[message.video_note, "video-note.mp4", "video/mp4"],
|
|
@@ -141780,7 +142213,15 @@ var resourceOf2 = (message) => {
|
|
|
141780
142213
|
return null;
|
|
141781
142214
|
};
|
|
141782
142215
|
var unsupportedTypeOf = (message) => {
|
|
141783
|
-
for (const type of [
|
|
142216
|
+
for (const type of [
|
|
142217
|
+
"voice",
|
|
142218
|
+
"sticker",
|
|
142219
|
+
"location",
|
|
142220
|
+
"venue",
|
|
142221
|
+
"contact",
|
|
142222
|
+
"poll",
|
|
142223
|
+
"dice"
|
|
142224
|
+
]) {
|
|
141784
142225
|
if (message[type] !== void 0) return type;
|
|
141785
142226
|
}
|
|
141786
142227
|
return null;
|
|
@@ -141834,16 +142275,7 @@ var makeTelegramApi = (input) => {
|
|
|
141834
142275
|
},
|
|
141835
142276
|
timeoutMs
|
|
141836
142277
|
);
|
|
141837
|
-
|
|
141838
|
-
if (envelope.ok !== true) {
|
|
141839
|
-
throw new TelegramApiError(
|
|
141840
|
-
method,
|
|
141841
|
-
envelope.error_code,
|
|
141842
|
-
envelope.description ?? "no reason",
|
|
141843
|
-
envelope.parameters?.retry_after
|
|
141844
|
-
);
|
|
141845
|
-
}
|
|
141846
|
-
return envelope.result;
|
|
142278
|
+
return readTelegramResponse(method, response);
|
|
141847
142279
|
};
|
|
141848
142280
|
const upload = async (method, field, chatId, file, replyMessageId) => {
|
|
141849
142281
|
const form = new FormData();
|
|
@@ -141861,15 +142293,7 @@ var makeTelegramApi = (input) => {
|
|
|
141861
142293
|
{ method: "POST", body: form },
|
|
141862
142294
|
UPLOAD_TIMEOUT_MS
|
|
141863
142295
|
);
|
|
141864
|
-
|
|
141865
|
-
if (envelope.ok !== true) {
|
|
141866
|
-
throw new TelegramApiError(
|
|
141867
|
-
method,
|
|
141868
|
-
envelope.error_code,
|
|
141869
|
-
envelope.description ?? "no reason",
|
|
141870
|
-
envelope.parameters?.retry_after
|
|
141871
|
-
);
|
|
141872
|
-
}
|
|
142296
|
+
await readTelegramResponse(method, response);
|
|
141873
142297
|
};
|
|
141874
142298
|
const keyboard = (rows) => ({
|
|
141875
142299
|
inline_keyboard: rows.map(
|
|
@@ -142164,7 +142588,7 @@ var toTelegramRichMarkdown = (value2) => {
|
|
|
142164
142588
|
|
|
142165
142589
|
// src/plainTurnText.ts
|
|
142166
142590
|
var RUNNING_LABEL_MAX_CHARS = 200;
|
|
142167
|
-
var
|
|
142591
|
+
var clip4 = (text4, max) => {
|
|
142168
142592
|
const chars = Array.from(text4);
|
|
142169
142593
|
return chars.length <= max ? text4 : `${chars.slice(0, max - 1).join("")}\u2026`;
|
|
142170
142594
|
};
|
|
@@ -142173,7 +142597,7 @@ var turnTextParts = (view, t) => {
|
|
|
142173
142597
|
if (view.omitted > 0) names.unshift(t("card.step.omitted", { count: view.omitted }));
|
|
142174
142598
|
const headLines = [view.title === "" ? t("card.untitled") : view.title];
|
|
142175
142599
|
if (names.length > 0) headLines.push(names.join(" \u2192 "));
|
|
142176
|
-
if (view.running !== null) headLines.push(`\u{1F527} ${
|
|
142600
|
+
if (view.running !== null) headLines.push(`\u{1F527} ${clip4(view.running, RUNNING_LABEL_MAX_CHARS)}`);
|
|
142177
142601
|
const body = view.body.trim();
|
|
142178
142602
|
const footLines = [];
|
|
142179
142603
|
if (view.status === "working") {
|
|
@@ -142247,6 +142671,7 @@ var TELEGRAM_CARD_THROTTLE_MS = 1200;
|
|
|
142247
142671
|
var FLOOD_WAIT_MAX_SECONDS = 30;
|
|
142248
142672
|
var REMEMBERED_CARD_LIMIT = 200;
|
|
142249
142673
|
var tokenOf = (binding) => (binding.values["telegramToken"] ?? "").trim();
|
|
142674
|
+
var tokenFingerprint = (bot) => NodeCrypto5.createHash("sha256").update(tokenOf(bot)).digest("hex");
|
|
142250
142675
|
var REACTION_WORKING = "\u{1F440}";
|
|
142251
142676
|
var REACTION_DONE = "\u{1F44D}";
|
|
142252
142677
|
var REACTION_FAILED = "\u{1F44E}";
|
|
@@ -142353,6 +142778,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142353
142778
|
});
|
|
142354
142779
|
return {
|
|
142355
142780
|
platform: "telegram",
|
|
142781
|
+
botIdentity: tokenFingerprint,
|
|
142356
142782
|
icon: "telegram",
|
|
142357
142783
|
label: "Telegram",
|
|
142358
142784
|
quotesReplies: true,
|
|
@@ -142364,7 +142790,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142364
142790
|
};
|
|
142365
142791
|
const stopped = new Promise((resolve) => stop = resolve);
|
|
142366
142792
|
let running = true;
|
|
142367
|
-
const tokenHash =
|
|
142793
|
+
const tokenHash = tokenFingerprint(binding);
|
|
142368
142794
|
let offset = deps.readCursor(binding.rowId, tokenHash);
|
|
142369
142795
|
const advanceOffset = (update) => {
|
|
142370
142796
|
offset = nextOffset([update], offset ?? 0);
|
|
@@ -142450,7 +142876,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142450
142876
|
const gated = !passesGroupGate2({
|
|
142451
142877
|
message,
|
|
142452
142878
|
text: text4,
|
|
142453
|
-
hasResource: resource !== null,
|
|
142879
|
+
hasResource: resource !== null || message.voice !== void 0,
|
|
142454
142880
|
botUsername: botUsername ?? null,
|
|
142455
142881
|
groupMention: binding.groupMention
|
|
142456
142882
|
});
|
|
@@ -142465,9 +142891,9 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142465
142891
|
if (!running) break;
|
|
142466
142892
|
advanceOffset(update);
|
|
142467
142893
|
const senderId = message.from === void 0 ? null : String(message.from.id);
|
|
142468
|
-
const
|
|
142894
|
+
const senderName2 = telegramDisplayName(message.from);
|
|
142469
142895
|
const chatKind = message.chat.type === "private" ? "private" : "group";
|
|
142470
|
-
if (text4 === "" && resource === null) {
|
|
142896
|
+
if (message.voice !== void 0 || text4 === "" && resource === null) {
|
|
142471
142897
|
const unsupported = unsupportedTypeOf(message);
|
|
142472
142898
|
if (unsupported === null) continue;
|
|
142473
142899
|
handlers.onMessage({
|
|
@@ -142489,7 +142915,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142489
142915
|
...parentChatId === void 0 ? {} : { parentChatId },
|
|
142490
142916
|
senderId,
|
|
142491
142917
|
text: text4,
|
|
142492
|
-
...
|
|
142918
|
+
...senderName2 ? { senderName: senderName2 } : {},
|
|
142493
142919
|
...message.chat.title ? { chatTitle: message.chat.title } : {},
|
|
142494
142920
|
...replyTo === null ? {} : { replyTo },
|
|
142495
142921
|
chatKind,
|
|
@@ -142506,6 +142932,17 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142506
142932
|
}
|
|
142507
142933
|
} catch (error) {
|
|
142508
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
|
+
}
|
|
142509
142946
|
if (checkingConnection) handlers.onConnectionState?.({ state: "failed", error });
|
|
142510
142947
|
handlers.onError(error instanceof Error ? error.message : String(error));
|
|
142511
142948
|
await Promise.race([(deps.sleep ?? defaultSleep)(backoff), stopped]);
|
|
@@ -142517,9 +142954,9 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142517
142954
|
deps.log.info("telegram polling", { bot: binding.displayName });
|
|
142518
142955
|
return {
|
|
142519
142956
|
close: () => {
|
|
142957
|
+
if (running) publishAck();
|
|
142520
142958
|
running = false;
|
|
142521
142959
|
stop();
|
|
142522
|
-
publishAck();
|
|
142523
142960
|
apis.delete(binding.rowId);
|
|
142524
142961
|
}
|
|
142525
142962
|
};
|
|
@@ -142550,12 +142987,11 @@ var makeTelegramAdapter = (deps) => {
|
|
|
142550
142987
|
}
|
|
142551
142988
|
username = me.username !== void 0 && me.username !== "" ? `@${me.username}` : "(no username)";
|
|
142552
142989
|
} catch (error) {
|
|
142553
|
-
const rejected = error instanceof TelegramApiError && (error.code === 401 || error.code === 404);
|
|
142554
142990
|
return {
|
|
142555
142991
|
stage: "getMe",
|
|
142556
142992
|
ok: false,
|
|
142557
142993
|
detail: describeError(error),
|
|
142558
|
-
...
|
|
142994
|
+
...isTelegramCredentialsRejected(error) ? { hint: "unauthorized" } : {}
|
|
142559
142995
|
};
|
|
142560
142996
|
}
|
|
142561
142997
|
try {
|
|
@@ -142717,12 +143153,96 @@ ${line}`
|
|
|
142717
143153
|
|
|
142718
143154
|
// src/turns.ts
|
|
142719
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
|
|
142720
143233
|
var TITLE_MAX_CHARS = 72;
|
|
142721
143234
|
var titleFrom = (text4) => text4.replace(/\s+/g, " ").slice(0, TITLE_MAX_CHARS) || "Chat thread";
|
|
142722
143235
|
var makeTurns = (core, deps) => {
|
|
142723
143236
|
const { host, t, commit, bindChat, deliver } = core;
|
|
142724
143237
|
const { queueFlow, rememberThreadProject } = deps;
|
|
142725
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
|
+
};
|
|
142726
143246
|
const threadStillUsable = async (binding, threadId) => {
|
|
142727
143247
|
try {
|
|
142728
143248
|
const thread = await threadOrNull8(host.threads, threadId, { archived: "include" });
|
|
@@ -142736,7 +143256,7 @@ var makeTurns = (core, deps) => {
|
|
|
142736
143256
|
return waiting !== void 0 && waiting.threadId === threadId ? waiting : void 0;
|
|
142737
143257
|
};
|
|
142738
143258
|
const interactionModeOf = (chatKey) => chatOf(core.state, chatKey).plan ? "plan" : "default";
|
|
142739
|
-
const ensureThread = async (binding,
|
|
143259
|
+
const ensureThread = async (binding, message) => {
|
|
142740
143260
|
const chatKey = chatKeyOf(binding);
|
|
142741
143261
|
let threadId = chatOf(core.state, chatKey).threadId ?? void 0;
|
|
142742
143262
|
if (threadId !== void 0 && !await threadStillUsable(binding, threadId)) {
|
|
@@ -142766,12 +143286,12 @@ var makeTurns = (core, deps) => {
|
|
|
142766
143286
|
await deliver(binding, t("reply.noDefaultModel", { title: project.title }));
|
|
142767
143287
|
return null;
|
|
142768
143288
|
}
|
|
142769
|
-
const createdThreadId =
|
|
143289
|
+
const createdThreadId = identity("thread", binding, message);
|
|
142770
143290
|
await host.threads.create({
|
|
142771
|
-
requestId:
|
|
143291
|
+
requestId: identity("create", binding, message),
|
|
142772
143292
|
threadId: createdThreadId,
|
|
142773
143293
|
projectId: binding.projectId,
|
|
142774
|
-
title: titleFrom(
|
|
143294
|
+
title: titleFrom(message.text || message.resources[0]?.name || "Chat"),
|
|
142775
143295
|
modelSelection,
|
|
142776
143296
|
runtimeMode: binding.runtimeMode,
|
|
142777
143297
|
interactionMode: interactionModeOf(chatKey),
|
|
@@ -142790,7 +143310,7 @@ var makeTurns = (core, deps) => {
|
|
|
142790
143310
|
const startTurn = async (binding, message) => {
|
|
142791
143311
|
const chatKey = chatKeyOf(binding);
|
|
142792
143312
|
const text4 = message.text;
|
|
142793
|
-
const threadId = await ensureThread(binding,
|
|
143313
|
+
const threadId = await ensureThread(binding, message);
|
|
142794
143314
|
if (threadId === null) return null;
|
|
142795
143315
|
const topicRoot = await message.topicRootForSession?.(threadId);
|
|
142796
143316
|
const carried = waitingFor(chatKey, threadId);
|
|
@@ -142804,7 +143324,7 @@ var makeTurns = (core, deps) => {
|
|
|
142804
143324
|
);
|
|
142805
143325
|
const attachments = [...carried?.attachments ?? [], ...own.attachments];
|
|
142806
143326
|
const notes = [...carried?.notes ?? [], ...own.notes];
|
|
142807
|
-
const userMessageId =
|
|
143327
|
+
const userMessageId = identity("msg", binding, message);
|
|
142808
143328
|
deps.anchors.note(threadId, userMessageId, binding);
|
|
142809
143329
|
let intentAction;
|
|
142810
143330
|
if (binding.queueMode === "steer") {
|
|
@@ -142816,33 +143336,39 @@ var makeTurns = (core, deps) => {
|
|
|
142816
143336
|
kind: "steer",
|
|
142817
143337
|
source: "automatic",
|
|
142818
143338
|
turnId: activeTurnId,
|
|
142819
|
-
commandId:
|
|
143339
|
+
commandId: identity("steer", binding, message)
|
|
142820
143340
|
};
|
|
142821
143341
|
}
|
|
142822
143342
|
} catch {
|
|
142823
143343
|
}
|
|
142824
143344
|
}
|
|
142825
143345
|
const registered = deps.conclusions.starting(threadId);
|
|
143346
|
+
const requestId = identity("turn", binding, message);
|
|
143347
|
+
let failureNotice;
|
|
142826
143348
|
try {
|
|
142827
|
-
await
|
|
142828
|
-
|
|
142829
|
-
|
|
142830
|
-
|
|
142831
|
-
|
|
142832
|
-
|
|
142833
|
-
|
|
142834
|
-
|
|
142835
|
-
|
|
142836
|
-
|
|
142837
|
-
|
|
142838
|
-
(
|
|
142839
|
-
|
|
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 } : {}
|
|
142840
143369
|
},
|
|
142841
|
-
|
|
142842
|
-
|
|
142843
|
-
interactionMode: interactionModeOf(chatKey),
|
|
142844
|
-
...intentAction !== void 0 ? { intentAction } : {}
|
|
142845
|
-
});
|
|
143370
|
+
{ sleep: deps.dispatchSleep, signal: dispatchAbort.signal }
|
|
143371
|
+
);
|
|
142846
143372
|
topicRoot?.confirmConsumed();
|
|
142847
143373
|
if (intentAction?.kind !== "steer") {
|
|
142848
143374
|
deps.conclusions.owe({
|
|
@@ -142854,10 +143380,15 @@ var makeTurns = (core, deps) => {
|
|
|
142854
143380
|
}
|
|
142855
143381
|
} catch (error) {
|
|
142856
143382
|
if (carried !== void 0) pendingByChat.set(chatKey, carried);
|
|
142857
|
-
|
|
143383
|
+
if (dispatchAbort.signal.aborted) return null;
|
|
143384
|
+
failureNotice = deps.failures.forDispatchFailure(threadId, requestId, error);
|
|
142858
143385
|
} finally {
|
|
142859
143386
|
registered();
|
|
142860
143387
|
}
|
|
143388
|
+
if (failureNotice !== void 0) {
|
|
143389
|
+
await deliver(binding, failureNotice);
|
|
143390
|
+
return null;
|
|
143391
|
+
}
|
|
142861
143392
|
deps.noteInboundUserMessage?.(userMessageId);
|
|
142862
143393
|
host.log.info("chat started a turn from a message", {
|
|
142863
143394
|
bot: binding.displayName,
|
|
@@ -142874,7 +143405,7 @@ var makeTurns = (core, deps) => {
|
|
|
142874
143405
|
return threadId;
|
|
142875
143406
|
};
|
|
142876
143407
|
const receiveAttachmentsOnly = async (binding, message) => {
|
|
142877
|
-
const threadId = await ensureThread(binding, message
|
|
143408
|
+
const threadId = await ensureThread(binding, message);
|
|
142878
143409
|
if (threadId === null) return;
|
|
142879
143410
|
const waiting = pendingFor(chatKeyOf(binding), threadId);
|
|
142880
143411
|
const collected = await collectResources(
|
|
@@ -142956,6 +143487,84 @@ var makeWebSync = (core) => {
|
|
|
142956
143487
|
return { noteInboundUserMessage };
|
|
142957
143488
|
};
|
|
142958
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
|
+
|
|
142959
143568
|
// src/settingsRowActions.ts
|
|
142960
143569
|
var ROW_PAYLOAD_SCHEMA = {
|
|
142961
143570
|
type: "object",
|
|
@@ -143023,19 +143632,21 @@ var emitReport = (emit, report, action) => {
|
|
|
143023
143632
|
});
|
|
143024
143633
|
return;
|
|
143025
143634
|
}
|
|
143026
|
-
const
|
|
143635
|
+
const connectionReasonKey = report.hint === void 0 ? "rowAction.checkConnection.failed" : connectionHintKey(report.hint);
|
|
143636
|
+
const copying = action.keyPrefix === "rowAction.copyDiagnostics";
|
|
143027
143637
|
emit({
|
|
143028
143638
|
kind: "failed",
|
|
143029
|
-
reasonKey:
|
|
143639
|
+
reasonKey: copying ? "rowAction.copyDiagnostics.done" : connectionReasonKey,
|
|
143030
143640
|
// 参考号由界面写在细节第一行(「参考号:…」),结论句里不带。
|
|
143031
143641
|
params: {
|
|
143032
143642
|
ref: report.ref,
|
|
143033
|
-
...
|
|
143643
|
+
...report.hint === void 0 ? {} : { hint: report.hint }
|
|
143034
143644
|
},
|
|
143645
|
+
...copying ? { noteKey: connectionReasonKey } : {},
|
|
143035
143646
|
detail
|
|
143036
143647
|
});
|
|
143037
143648
|
};
|
|
143038
|
-
var registerSettingsRowActions = (ctx, adapters) => {
|
|
143649
|
+
var registerSettingsRowActions = (ctx, adapters, connections) => {
|
|
143039
143650
|
const resolveBot = (rowId) => botOf(ctx.config, adapters, rowId);
|
|
143040
143651
|
const registerProbeAction = (action) => {
|
|
143041
143652
|
ctx.stream({
|
|
@@ -143063,6 +143674,26 @@ var registerSettingsRowActions = (ctx, adapters) => {
|
|
|
143063
143674
|
keyPrefix: "rowAction.checkConnection",
|
|
143064
143675
|
copyable: false
|
|
143065
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
|
+
});
|
|
143066
143697
|
registerProbeAction({
|
|
143067
143698
|
name: BOT_ACTION_COMMANDS.copyDiagnostics,
|
|
143068
143699
|
summary: "Probe an IM bot and return a copy-paste diagnostic report.",
|
|
@@ -143071,7 +143702,7 @@ var registerSettingsRowActions = (ctx, adapters) => {
|
|
|
143071
143702
|
});
|
|
143072
143703
|
ctx.stream({
|
|
143073
143704
|
name: BOT_ACTION_COMMANDS.larkRepair,
|
|
143074
|
-
description: "
|
|
143705
|
+
description: "Check required Lark scopes through the public API and list manual repair steps.",
|
|
143075
143706
|
scope: "operate",
|
|
143076
143707
|
payloadSchema: ROW_PAYLOAD_SCHEMA,
|
|
143077
143708
|
itemSchema: ROW_ACTION_ITEM_SCHEMA,
|
|
@@ -143080,15 +143711,6 @@ var registerSettingsRowActions = (ctx, adapters) => {
|
|
|
143080
143711
|
const rowId = typeof payload["rowId"] === "string" ? payload["rowId"] : "";
|
|
143081
143712
|
const send = (item) => emit(item);
|
|
143082
143713
|
const ref = diagnosticRef("setup");
|
|
143083
|
-
send({
|
|
143084
|
-
kind: "steps",
|
|
143085
|
-
steps: [
|
|
143086
|
-
{ id: "scopes", labelKey: "rowAction.larkRepair.step.scopes", state: "skipped" },
|
|
143087
|
-
{ id: "events", labelKey: "rowAction.larkRepair.step.events", state: "skipped" },
|
|
143088
|
-
{ id: "publish", labelKey: "rowAction.larkRepair.step.publish", state: "skipped" },
|
|
143089
|
-
{ id: "verify", labelKey: "rowAction.larkRepair.step.verify", state: "running" }
|
|
143090
|
-
]
|
|
143091
|
-
});
|
|
143092
143714
|
const bot = resolveBot(rowId);
|
|
143093
143715
|
if (bot === void 0 || bot.platform !== "lark") {
|
|
143094
143716
|
send({
|
|
@@ -143098,40 +143720,14 @@ var registerSettingsRowActions = (ctx, adapters) => {
|
|
|
143098
143720
|
});
|
|
143099
143721
|
return;
|
|
143100
143722
|
}
|
|
143101
|
-
const
|
|
143102
|
-
|
|
143103
|
-
|
|
143104
|
-
|
|
143105
|
-
|
|
143106
|
-
|
|
143107
|
-
|
|
143108
|
-
|
|
143109
|
-
id: "verify",
|
|
143110
|
-
labelKey: "rowAction.larkRepair.step.verify",
|
|
143111
|
-
state: report.ok ? "done" : "failed"
|
|
143112
|
-
}
|
|
143113
|
-
]
|
|
143114
|
-
});
|
|
143115
|
-
if (report.ok) {
|
|
143116
|
-
send({
|
|
143117
|
-
kind: "done",
|
|
143118
|
-
fills: {},
|
|
143119
|
-
messageKey: "rowAction.larkRepair.done",
|
|
143120
|
-
warnings: [
|
|
143121
|
-
{
|
|
143122
|
-
key: "rowAction.larkRepair.hint",
|
|
143123
|
-
params: { ref, detail: formatDiagnosticCopy(report) }
|
|
143124
|
-
}
|
|
143125
|
-
]
|
|
143126
|
-
});
|
|
143127
|
-
return;
|
|
143128
|
-
}
|
|
143129
|
-
send({
|
|
143130
|
-
kind: "failed",
|
|
143131
|
-
reasonKey: "rowAction.larkRepair.failed",
|
|
143132
|
-
params: { ref },
|
|
143133
|
-
detail: formatDiagnosticCopy(report)
|
|
143134
|
-
});
|
|
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
|
+
);
|
|
143135
143731
|
}
|
|
143136
143732
|
});
|
|
143137
143733
|
};
|
|
@@ -143460,6 +144056,7 @@ var wechatConfigIssues = (bot) => tokenOf2(bot) === "" ? [{ column: "wechatBotTo
|
|
|
143460
144056
|
var makeWechatAdapter = (deps) => {
|
|
143461
144057
|
return {
|
|
143462
144058
|
platform: "wechat",
|
|
144059
|
+
botIdentity: () => "wechat-stub",
|
|
143463
144060
|
icon: "wechat",
|
|
143464
144061
|
// 平台名与设置表的平台选项同一个键,免得默认机器人名与选项写成两种叫法。
|
|
143465
144062
|
label: deps.translate("platform.wechat"),
|
|
@@ -143495,7 +144092,7 @@ var makeWechatAdapter = (deps) => {
|
|
|
143495
144092
|
};
|
|
143496
144093
|
|
|
143497
144094
|
// src/wecomUpload.ts
|
|
143498
|
-
import * as
|
|
144095
|
+
import * as NodeCrypto7 from "node:crypto";
|
|
143499
144096
|
var WECOM_CHUNK_BYTES = 512 * 1024;
|
|
143500
144097
|
var WECOM_MAX_CHUNKS = 100;
|
|
143501
144098
|
var WECOM_MIN_UPLOAD_BYTES = 5;
|
|
@@ -143528,7 +144125,7 @@ var uploadWecomMedia = async (input) => {
|
|
|
143528
144125
|
filename: clampName(input.filename),
|
|
143529
144126
|
total_size: bytes.byteLength,
|
|
143530
144127
|
total_chunks: totalChunks,
|
|
143531
|
-
md5:
|
|
144128
|
+
md5: NodeCrypto7.createHash("md5").update(bytes).digest("hex")
|
|
143532
144129
|
});
|
|
143533
144130
|
const uploadId = init.upload_id;
|
|
143534
144131
|
if (typeof uploadId !== "string" || uploadId === "") {
|
|
@@ -143707,6 +144304,26 @@ var parseWecomMessage = (frame, botId) => {
|
|
|
143707
144304
|
};
|
|
143708
144305
|
var splitForWecom = (text4, limit = WECOM_TEXT_MAX_CHARS) => splitMessage(text4, limit);
|
|
143709
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
|
+
};
|
|
143710
144327
|
var runWecomClient = (input) => {
|
|
143711
144328
|
let sequence = 0;
|
|
143712
144329
|
const nextRequestId = (kind) => `${kind}_${++sequence}`;
|
|
@@ -143737,32 +144354,47 @@ var runWecomClient = (input) => {
|
|
|
143737
144354
|
if (typeof frame !== "object" || frame === null) return;
|
|
143738
144355
|
const requestId = frame.headers?.req_id;
|
|
143739
144356
|
if (typeof frame.cmd !== "string") {
|
|
143740
|
-
|
|
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
|
+
}
|
|
143741
144362
|
if (requestId.startsWith("ping")) {
|
|
143742
144363
|
missedPong = 0;
|
|
143743
144364
|
return;
|
|
143744
144365
|
}
|
|
143745
|
-
const error = typeof frame.errcode === "number" && frame.errcode !== 0 ? new Error(`WeCom ack failed: ${String(frame.errcode)} ${frame.errmsg ?? ""}`) : null;
|
|
143746
144366
|
const stream = openStreams.get(requestId);
|
|
143747
144367
|
if (stream !== void 0 && error !== null) stream.failed = true;
|
|
143748
144368
|
settleAck(requestId, error, frame);
|
|
143749
144369
|
return;
|
|
143750
144370
|
}
|
|
144371
|
+
if (frame.cmd === "aibot_event_callback" && frame.body?.event?.eventtype === "disconnected_event") {
|
|
144372
|
+
finish("taken-over");
|
|
144373
|
+
return;
|
|
144374
|
+
}
|
|
143751
144375
|
if (frame.cmd !== "aibot_msg_callback") return;
|
|
143752
144376
|
const message = parseWecomMessage(frame, input.botId);
|
|
143753
144377
|
if (message !== null) input.handlers.onMessage(message);
|
|
143754
144378
|
};
|
|
143755
|
-
const finish = (error = new Error("WeCom connection closed")
|
|
144379
|
+
const finish = (reason = "disconnected", error = reason === "taken-over" ? void 0 : new Error("WeCom connection closed")) => {
|
|
143756
144380
|
if (finished) return;
|
|
143757
144381
|
finished = true;
|
|
143758
|
-
|
|
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
|
+
});
|
|
143759
144391
|
clearInterval(heartbeat);
|
|
143760
144392
|
openStreams.clear();
|
|
143761
144393
|
void input.socket.close().catch(() => void 0);
|
|
143762
144394
|
for (const requestId of pendingAcks.keys()) {
|
|
143763
144395
|
settleAck(requestId, new Error("WeCom connection closed"));
|
|
143764
144396
|
}
|
|
143765
|
-
ended.resolve();
|
|
144397
|
+
ended.resolve(failureReason === "subscription-failed" ? "disconnected" : failureReason);
|
|
143766
144398
|
};
|
|
143767
144399
|
const readLoop = async () => {
|
|
143768
144400
|
try {
|
|
@@ -143773,7 +144405,7 @@ var runWecomClient = (input) => {
|
|
|
143773
144405
|
if (!closed) {
|
|
143774
144406
|
input.handlers.onError(error instanceof Error ? error.message : String(error));
|
|
143775
144407
|
}
|
|
143776
|
-
finish(error);
|
|
144408
|
+
finish("disconnected", error);
|
|
143777
144409
|
}
|
|
143778
144410
|
finish();
|
|
143779
144411
|
};
|
|
@@ -143781,7 +144413,7 @@ var runWecomClient = (input) => {
|
|
|
143781
144413
|
if (finished) return;
|
|
143782
144414
|
if (missedPong >= MAX_MISSED_PONGS) {
|
|
143783
144415
|
input.handlers.onError("socket hang up: missed pong");
|
|
143784
|
-
finish(new Error("socket hang up: missed pong"));
|
|
144416
|
+
finish("disconnected", new Error("socket hang up: missed pong"));
|
|
143785
144417
|
return;
|
|
143786
144418
|
}
|
|
143787
144419
|
missedPong += 1;
|
|
@@ -143808,7 +144440,7 @@ var runWecomClient = (input) => {
|
|
|
143808
144440
|
},
|
|
143809
144441
|
(error) => {
|
|
143810
144442
|
if (!closed) input.handlers.onError(error instanceof Error ? error.message : String(error));
|
|
143811
|
-
finish(
|
|
144443
|
+
finish("subscription-failed", error);
|
|
143812
144444
|
return false;
|
|
143813
144445
|
}
|
|
143814
144446
|
);
|
|
@@ -143943,7 +144575,7 @@ var runWecomClient = (input) => {
|
|
|
143943
144575
|
};
|
|
143944
144576
|
|
|
143945
144577
|
// src/wecomMedia.ts
|
|
143946
|
-
import * as
|
|
144578
|
+
import * as NodeCrypto8 from "node:crypto";
|
|
143947
144579
|
|
|
143948
144580
|
// src/wecomPadding.ts
|
|
143949
144581
|
var PADDING_BLOCK = 32;
|
|
@@ -143974,7 +144606,7 @@ var decryptWecomMedia = (cipher, aesKey) => {
|
|
|
143974
144606
|
if (cipher.byteLength === 0 || cipher.byteLength % AES_BLOCK !== 0) {
|
|
143975
144607
|
throw new WecomDecryptError("the ciphertext is not a whole number of blocks");
|
|
143976
144608
|
}
|
|
143977
|
-
const decipher =
|
|
144609
|
+
const decipher = NodeCrypto8.createDecipheriv("aes-256-cbc", key, key.subarray(0, IV_BYTES));
|
|
143978
144610
|
decipher.setAutoPadding(false);
|
|
143979
144611
|
const plain = Buffer.concat([decipher.update(cipher), decipher.final()]);
|
|
143980
144612
|
const unpadded = stripPadding(plain);
|
|
@@ -144038,13 +144670,13 @@ var COLUMNS4 = [
|
|
|
144038
144670
|
{ key: "wecomBotSecret", kind: "password", labelKey: "column.wecomBotSecret.label" }
|
|
144039
144671
|
];
|
|
144040
144672
|
var defaultSleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
144041
|
-
var
|
|
144673
|
+
var credentials = (binding) => ({
|
|
144042
144674
|
botId: binding.values["wecomBotId"] ?? "",
|
|
144043
144675
|
botSecret: binding.values["wecomBotSecret"] ?? "",
|
|
144044
144676
|
endpoint: normalizeWecomEndpoint(binding.values["wecomEndpoint"] || DEFAULT_WECOM_ENDPOINT)
|
|
144045
144677
|
});
|
|
144046
144678
|
var wecomConfigIssues = (binding) => {
|
|
144047
|
-
const { botId, botSecret } =
|
|
144679
|
+
const { botId, botSecret } = credentials(binding);
|
|
144048
144680
|
const issues = [];
|
|
144049
144681
|
if (botId === "") issues.push({ column: "wecomBotId", code: "required" });
|
|
144050
144682
|
if (botSecret === "") issues.push({ column: "wecomBotSecret", code: "required" });
|
|
@@ -144052,6 +144684,7 @@ var wecomConfigIssues = (binding) => {
|
|
|
144052
144684
|
};
|
|
144053
144685
|
var makeWecomAdapter = (deps) => {
|
|
144054
144686
|
const clients = /* @__PURE__ */ new Map();
|
|
144687
|
+
const managedConnections = /* @__PURE__ */ new Map();
|
|
144055
144688
|
const t = deps.translate;
|
|
144056
144689
|
const liveStreams = /* @__PURE__ */ new Map();
|
|
144057
144690
|
const rememberStream = (key, entry) => {
|
|
@@ -144090,6 +144723,7 @@ ${kept}`;
|
|
|
144090
144723
|
};
|
|
144091
144724
|
return {
|
|
144092
144725
|
platform: "wecom",
|
|
144726
|
+
botIdentity: (bot) => credentials(bot).botId,
|
|
144093
144727
|
icon: "wecom",
|
|
144094
144728
|
label: "WeCom",
|
|
144095
144729
|
columns: COLUMNS4,
|
|
@@ -144097,15 +144731,22 @@ ${kept}`;
|
|
|
144097
144731
|
validate: wecomConfigIssues,
|
|
144098
144732
|
isConfigured: (binding) => wecomConfigIssues(binding).length === 0,
|
|
144099
144733
|
connect: (binding, handlers) => {
|
|
144100
|
-
const { botId, botSecret, endpoint } =
|
|
144734
|
+
const { botId, botSecret, endpoint } = credentials(binding);
|
|
144735
|
+
const managed = { endpoint };
|
|
144736
|
+
managedConnections.set(binding.rowId, managed);
|
|
144101
144737
|
let stopped = false;
|
|
144102
144738
|
let current = null;
|
|
144103
144739
|
const stopSignal = Promise.withResolvers();
|
|
144740
|
+
const onConnectionState = (state) => {
|
|
144741
|
+
if (stopped) return;
|
|
144742
|
+
managed.state = state;
|
|
144743
|
+
handlers.onConnectionState?.(state);
|
|
144744
|
+
};
|
|
144104
144745
|
const runOnce = async () => {
|
|
144105
144746
|
const socket = await deps.connectSocket(endpoint);
|
|
144106
144747
|
if (stopped) {
|
|
144107
144748
|
await socket.close();
|
|
144108
|
-
return false;
|
|
144749
|
+
return { healthy: false, stopRetrying: false };
|
|
144109
144750
|
}
|
|
144110
144751
|
const client = runWecomClient({
|
|
144111
144752
|
socket: {
|
|
@@ -144117,9 +144758,7 @@ ${kept}`;
|
|
|
144117
144758
|
botSecret,
|
|
144118
144759
|
handlers: {
|
|
144119
144760
|
onError: handlers.onError,
|
|
144120
|
-
onConnectionState
|
|
144121
|
-
if (!stopped) handlers.onConnectionState?.(state);
|
|
144122
|
-
},
|
|
144761
|
+
onConnectionState,
|
|
144123
144762
|
onMessage: (message) => {
|
|
144124
144763
|
if (message.chatType !== "single") return;
|
|
144125
144764
|
client.rememberRequest(message.chatId, message.requestId);
|
|
@@ -144149,20 +144788,25 @@ ${kept}`;
|
|
|
144149
144788
|
if (ok) deps.log.info("wecom connected", { bot: binding.displayName });
|
|
144150
144789
|
return ok;
|
|
144151
144790
|
});
|
|
144152
|
-
await client.ended;
|
|
144791
|
+
const reason = await client.ended;
|
|
144153
144792
|
if (clients.get(binding.rowId) === client) clients.delete(binding.rowId);
|
|
144154
144793
|
current = null;
|
|
144155
|
-
return
|
|
144794
|
+
return {
|
|
144795
|
+
healthy: await healthy,
|
|
144796
|
+
stopRetrying: reason === "taken-over" || reason === "credentials-rejected"
|
|
144797
|
+
};
|
|
144156
144798
|
};
|
|
144157
144799
|
const supervise = async () => {
|
|
144158
144800
|
let backoff = BACKOFF_START_MS2;
|
|
144159
144801
|
for (; ; ) {
|
|
144160
144802
|
if (stopped) return;
|
|
144161
144803
|
try {
|
|
144162
|
-
|
|
144804
|
+
const result = await runOnce();
|
|
144805
|
+
if (result.stopRetrying) return;
|
|
144806
|
+
if (result.healthy) backoff = BACKOFF_START_MS2;
|
|
144163
144807
|
} catch (error) {
|
|
144164
144808
|
if (stopped) return;
|
|
144165
|
-
|
|
144809
|
+
onConnectionState({ state: "failed", error, reason: "disconnected" });
|
|
144166
144810
|
handlers.onError(error instanceof Error ? error.message : String(error));
|
|
144167
144811
|
}
|
|
144168
144812
|
if (stopped) return;
|
|
@@ -144177,6 +144821,9 @@ ${kept}`;
|
|
|
144177
144821
|
stopped = true;
|
|
144178
144822
|
stopSignal.resolve();
|
|
144179
144823
|
current?.close();
|
|
144824
|
+
if (managedConnections.get(binding.rowId) === managed) {
|
|
144825
|
+
managedConnections.delete(binding.rowId);
|
|
144826
|
+
}
|
|
144180
144827
|
}
|
|
144181
144828
|
};
|
|
144182
144829
|
},
|
|
@@ -144189,12 +144836,33 @@ ${kept}`;
|
|
|
144189
144836
|
await client.sendText(binding.chatId, text4);
|
|
144190
144837
|
},
|
|
144191
144838
|
checkConnection: async (bot) => {
|
|
144192
|
-
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);
|
|
144193
144860
|
if (botId === "" || botSecret === "") {
|
|
144194
144861
|
return {
|
|
144195
144862
|
stage: "credentials",
|
|
144196
144863
|
ok: false,
|
|
144197
|
-
detail: "missing bot id or secret in saved config"
|
|
144864
|
+
detail: "missing bot id or secret in saved config",
|
|
144865
|
+
hint: "wecomBotCredentials"
|
|
144198
144866
|
};
|
|
144199
144867
|
}
|
|
144200
144868
|
let stage = "websocket";
|
|
@@ -144202,6 +144870,7 @@ ${kept}`;
|
|
|
144202
144870
|
const socket = await deps.connectSocket(endpoint);
|
|
144203
144871
|
stage = "aibot_subscribe";
|
|
144204
144872
|
let detail = "WeCom connection closed before subscribe ack";
|
|
144873
|
+
let hint;
|
|
144205
144874
|
let closing;
|
|
144206
144875
|
const client = runWecomClient({
|
|
144207
144876
|
socket: {
|
|
@@ -144213,6 +144882,9 @@ ${kept}`;
|
|
|
144213
144882
|
botSecret,
|
|
144214
144883
|
handlers: {
|
|
144215
144884
|
onMessage: () => void 0,
|
|
144885
|
+
onConnectionState: (state) => {
|
|
144886
|
+
if (state.state === "failed") hint = state.hint;
|
|
144887
|
+
},
|
|
144216
144888
|
onError: (error) => {
|
|
144217
144889
|
detail = error;
|
|
144218
144890
|
}
|
|
@@ -144220,7 +144892,12 @@ ${kept}`;
|
|
|
144220
144892
|
});
|
|
144221
144893
|
try {
|
|
144222
144894
|
const ok = await client.ready;
|
|
144223
|
-
return {
|
|
144895
|
+
return {
|
|
144896
|
+
stage,
|
|
144897
|
+
ok,
|
|
144898
|
+
detail: ok ? `subscription confirmed (${endpoint})` : detail,
|
|
144899
|
+
...!ok && hint !== void 0 ? { hint } : {}
|
|
144900
|
+
};
|
|
144224
144901
|
} finally {
|
|
144225
144902
|
client.close();
|
|
144226
144903
|
await closing;
|
|
@@ -144572,7 +145249,7 @@ import * as NodeHttp2 from "node:http";
|
|
|
144572
145249
|
import * as NodeNet from "node:net";
|
|
144573
145250
|
|
|
144574
145251
|
// src/wecomAppCrypto.ts
|
|
144575
|
-
import * as
|
|
145252
|
+
import * as NodeCrypto9 from "node:crypto";
|
|
144576
145253
|
var AES_KEY_BYTES = 32;
|
|
144577
145254
|
var RANDOM_BYTES = 16;
|
|
144578
145255
|
var WecomAppCryptoError = class extends Error {
|
|
@@ -144595,13 +145272,13 @@ var timingSafeEqualString2 = (a, b) => {
|
|
|
144595
145272
|
const left = Buffer.from(a);
|
|
144596
145273
|
const right = Buffer.from(b);
|
|
144597
145274
|
if (left.length === 0 || left.length !== right.length) return false;
|
|
144598
|
-
return
|
|
145275
|
+
return NodeCrypto9.timingSafeEqual(left, right);
|
|
144599
145276
|
};
|
|
144600
|
-
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");
|
|
144601
145278
|
var verifySignature = (token, timestamp, nonce, encrypt, signature) => timingSafeEqualString2(computeSignature(token, timestamp, nonce, encrypt), signature);
|
|
144602
145279
|
var decrypt = (encodingAESKey, encryptText, expectReceiveId) => {
|
|
144603
145280
|
const { key, iv } = parseEncodingAESKey(encodingAESKey);
|
|
144604
|
-
const decipher =
|
|
145281
|
+
const decipher = NodeCrypto9.createDecipheriv("aes-256-cbc", key, iv);
|
|
144605
145282
|
decipher.setAutoPadding(false);
|
|
144606
145283
|
let deciphered;
|
|
144607
145284
|
try {
|
|
@@ -144660,7 +145337,7 @@ var createWecomAppCrypto = (input) => {
|
|
|
144660
145337
|
decrypt: (encryptText) => decrypt(encodingAESKey, encryptText, corpId).message
|
|
144661
145338
|
};
|
|
144662
145339
|
};
|
|
144663
|
-
var deriveCallbackPathSecret = (token, encodingAESKey) =>
|
|
145340
|
+
var deriveCallbackPathSecret = (token, encodingAESKey) => NodeCrypto9.createHash("sha256").update(`${token.trim()}:${encodingAESKey.trim()}`).digest("hex").slice(0, 24);
|
|
144664
145341
|
|
|
144665
145342
|
// src/wecomAppParse.ts
|
|
144666
145343
|
var isRecord5 = (value2) => value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
@@ -144697,7 +145374,6 @@ var parseWecomAppPlainMessage = (raw) => {
|
|
|
144697
145374
|
text: xml["Content"] !== void 0 ? { content: xml["Content"] } : void 0,
|
|
144698
145375
|
image: xml["PicUrl"] ? { url: xml["PicUrl"] } : void 0,
|
|
144699
145376
|
mediaId: xml["MediaId"] ?? xml["mediaid"],
|
|
144700
|
-
recognition: xml["Recognition"],
|
|
144701
145377
|
event: (xml["Event"] ?? xml["event"] ?? "").toLowerCase() || void 0,
|
|
144702
145378
|
eventKey: xml["EventKey"],
|
|
144703
145379
|
chatid: xml["ChatId"]
|
|
@@ -144723,7 +145399,6 @@ var parseWecomAppPlainMessage = (raw) => {
|
|
|
144723
145399
|
text: isRecord5(textRaw) ? { content: typeof textRaw["content"] === "string" ? textRaw["content"] : void 0 } : void 0,
|
|
144724
145400
|
image: isRecord5(imageRaw) ? { url: typeof imageRaw["url"] === "string" ? imageRaw["url"] : void 0 } : void 0,
|
|
144725
145401
|
mediaId: typeof data["mediaid"] === "string" ? data["mediaid"] : void 0,
|
|
144726
|
-
recognition: typeof data["recognition"] === "string" ? data["recognition"] : void 0,
|
|
144727
145402
|
event: String(data["event"] ?? data["eventtype"] ?? "").toLowerCase() || void 0,
|
|
144728
145403
|
eventKey: typeof data["eventkey"] === "string" ? data["eventkey"] : void 0,
|
|
144729
145404
|
chatid: typeof data["chatid"] === "string" ? data["chatid"] : void 0
|
|
@@ -144787,13 +145462,16 @@ var startWecomAppCallbackServer = async (options) => {
|
|
|
144787
145462
|
}
|
|
144788
145463
|
try {
|
|
144789
145464
|
if (!crypto2.verifySignature(signature, timestamp, nonce, echostr)) {
|
|
145465
|
+
route.onVerification?.(false);
|
|
144790
145466
|
writeText(res, 401, "unauthorized");
|
|
144791
145467
|
return;
|
|
144792
145468
|
}
|
|
144793
145469
|
const plaintext = crypto2.decrypt(echostr);
|
|
145470
|
+
route.onVerification?.(true);
|
|
144794
145471
|
route.log?.info("wecom-app URL verification succeeded", { bot: route.botRowId });
|
|
144795
145472
|
writeText(res, 200, plaintext);
|
|
144796
145473
|
} catch (error) {
|
|
145474
|
+
route.onVerification?.(false);
|
|
144797
145475
|
route.log?.warn("wecom-app URL verification failed", {
|
|
144798
145476
|
bot: route.botRowId,
|
|
144799
145477
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -144855,11 +145533,13 @@ var startWecomAppCallbackServer = async (options) => {
|
|
|
144855
145533
|
let plaintext;
|
|
144856
145534
|
try {
|
|
144857
145535
|
if (!crypto2.verifySignature(signature, timestamp, nonce, encrypt)) {
|
|
145536
|
+
route.onVerification?.(false);
|
|
144858
145537
|
writeText(res, 401, "unauthorized");
|
|
144859
145538
|
return;
|
|
144860
145539
|
}
|
|
144861
145540
|
plaintext = crypto2.decrypt(encrypt);
|
|
144862
145541
|
} catch (error) {
|
|
145542
|
+
route.onVerification?.(false);
|
|
144863
145543
|
route.log?.warn("wecom-app callback signature or decrypt failed", {
|
|
144864
145544
|
bot: route.botRowId,
|
|
144865
145545
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -144867,6 +145547,7 @@ var startWecomAppCallbackServer = async (options) => {
|
|
|
144867
145547
|
writeText(res, 401, "unauthorized");
|
|
144868
145548
|
return;
|
|
144869
145549
|
}
|
|
145550
|
+
route.onVerification?.(true);
|
|
144870
145551
|
const message = parseWecomAppPlainMessage(plaintext);
|
|
144871
145552
|
if (message === null) {
|
|
144872
145553
|
writeText(res, 400, "invalid message");
|
|
@@ -145045,11 +145726,11 @@ var validateWecomAppBot = (bot) => {
|
|
|
145045
145726
|
};
|
|
145046
145727
|
var readWecomAppConnectConfig = (bot) => {
|
|
145047
145728
|
const issues = validateWecomAppBot(bot);
|
|
145048
|
-
const
|
|
145049
|
-
const listenPort = parseListenPort(
|
|
145050
|
-
const listenHost = parseListenHost(
|
|
145729
|
+
const credentials2 = wecomAppCredentials(bot);
|
|
145730
|
+
const listenPort = parseListenPort(credentials2.listenPortRaw);
|
|
145731
|
+
const listenHost = parseListenHost(credentials2.listenHostRaw);
|
|
145051
145732
|
if (issues.length > 0 || listenPort === null || listenHost === null) return { ok: false, issues };
|
|
145052
|
-
return { ok: true, credentials:
|
|
145733
|
+
return { ok: true, credentials: credentials2, listenHost, listenPort };
|
|
145053
145734
|
};
|
|
145054
145735
|
|
|
145055
145736
|
// src/wecomAppAdapter.ts
|
|
@@ -145109,6 +145790,7 @@ var WECOM_APP_MIN_BYTES = 5;
|
|
|
145109
145790
|
var WECOM_APP_IMAGE_TYPES = /* @__PURE__ */ new Set(["image/jpeg", "image/png"]);
|
|
145110
145791
|
var CREDENTIAL_ERROR_CODES = /* @__PURE__ */ new Set(["40001", "40013", "40014", "40056", "42001", "60020"]);
|
|
145111
145792
|
var joinCallbackUrl = (baseOrOrigin, path) => `${baseOrOrigin}${path}`;
|
|
145793
|
+
var callbackHealthKey = (rowId, c) => JSON.stringify([rowId, c.corpId, c.token, c.encodingAESKey]);
|
|
145112
145794
|
var wecomAppPublicFields = (bot) => {
|
|
145113
145795
|
const c = wecomAppCredentials(bot);
|
|
145114
145796
|
if (c.token === "" || c.encodingAESKey === "") {
|
|
@@ -145145,6 +145827,7 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
145145
145827
|
fetch: deps.fetch
|
|
145146
145828
|
});
|
|
145147
145829
|
const callbackHub = makeWecomAppCallbackHub();
|
|
145830
|
+
const callbackHealth = /* @__PURE__ */ new Map();
|
|
145148
145831
|
const connectionFor = (binding) => {
|
|
145149
145832
|
const api = apis.get(binding.rowId);
|
|
145150
145833
|
if (api === void 0) {
|
|
@@ -145174,7 +145857,6 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
145174
145857
|
});
|
|
145175
145858
|
};
|
|
145176
145859
|
const toInbound = (message, handlers, botName) => {
|
|
145177
|
-
if (message.msgtype === "event") return;
|
|
145178
145860
|
if (message.chatid !== void 0 && message.chatid !== "") {
|
|
145179
145861
|
deps.log.info("wecom-app ignored a group message; only direct messages are supported", {
|
|
145180
145862
|
bot: botName
|
|
@@ -145183,6 +145865,12 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
145183
145865
|
}
|
|
145184
145866
|
const chatId = message.from?.userid ?? "";
|
|
145185
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
|
+
}
|
|
145186
145874
|
const messageId = message.msgid ?? `${chatId}:${message.createTime ?? Date.now()}`;
|
|
145187
145875
|
const base2 = { messageId, chatId, senderId: chatId };
|
|
145188
145876
|
if (message.msgtype === "text") {
|
|
@@ -145208,6 +145896,10 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
145208
145896
|
};
|
|
145209
145897
|
return {
|
|
145210
145898
|
platform: "wecom-app",
|
|
145899
|
+
botIdentity: (bot) => {
|
|
145900
|
+
const { corpId, agentId } = wecomAppCredentials(bot);
|
|
145901
|
+
return JSON.stringify([corpId, agentId]);
|
|
145902
|
+
},
|
|
145211
145903
|
icon: "wecom",
|
|
145212
145904
|
label: deps.translate("platform.wecom-app"),
|
|
145213
145905
|
columns: COLUMNS5,
|
|
@@ -145233,8 +145925,7 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
145233
145925
|
};
|
|
145234
145926
|
},
|
|
145235
145927
|
/**
|
|
145236
|
-
*
|
|
145237
|
-
* 凭据——和真实发送同一条路,向 gettoken 换一次 access_token。
|
|
145928
|
+
* 出站凭据走真实发送的 gettoken;入站只能报告本次运行已观察到的回调。
|
|
145238
145929
|
*/
|
|
145239
145930
|
checkConnection: async (bot) => {
|
|
145240
145931
|
const outbound = validateWecomAppBot(bot).filter(
|
|
@@ -145255,11 +145946,21 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
145255
145946
|
};
|
|
145256
145947
|
}
|
|
145257
145948
|
try {
|
|
145258
|
-
|
|
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
|
+
}
|
|
145259
145960
|
return {
|
|
145260
|
-
stage: "
|
|
145961
|
+
stage: "callback",
|
|
145261
145962
|
ok: true,
|
|
145262
|
-
detail:
|
|
145963
|
+
detail: `access_token ok; last verified callback since this startup: ${health.lastReceivedAt}`
|
|
145263
145964
|
};
|
|
145264
145965
|
} catch (error) {
|
|
145265
145966
|
return {
|
|
@@ -145297,7 +145998,14 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
145297
145998
|
encodingAESKey: c.encodingAESKey,
|
|
145298
145999
|
corpId: c.corpId,
|
|
145299
146000
|
log: deps.log,
|
|
145300
|
-
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
|
+
}
|
|
145301
146009
|
});
|
|
145302
146010
|
} catch (error) {
|
|
145303
146011
|
apis.delete(bot.rowId);
|
|
@@ -145389,12 +146097,14 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
145389
146097
|
// src/index.ts
|
|
145390
146098
|
var definition = {
|
|
145391
146099
|
id: "im",
|
|
145392
|
-
apiVersion:
|
|
145393
|
-
|
|
146100
|
+
apiVersion: 20,
|
|
146101
|
+
// 第二个参数只给插件自己的测试夹具:换掉文件 IO 与派发重试的等待。宿主只传 ctx。
|
|
146102
|
+
activate: async (ctx, testing = {}) => {
|
|
146103
|
+
const { dispatchSleep } = testing;
|
|
145394
146104
|
const inboundState = makeInboundStore({
|
|
145395
146105
|
dataDir: ctx.paths.dataDir,
|
|
145396
146106
|
log: ctx.log,
|
|
145397
|
-
io: inboundIo
|
|
146107
|
+
io: testing.inboundIo
|
|
145398
146108
|
});
|
|
145399
146109
|
ctx.onDispose(inboundState.flush);
|
|
145400
146110
|
const { t, setLanguage } = makeLiveTranslate(
|
|
@@ -145457,13 +146167,13 @@ var definition = {
|
|
|
145457
146167
|
ctx.settingsCard(buildSettingsCard(adapters));
|
|
145458
146168
|
const updateSettings = makeSettingsUpdater(ctx.settings);
|
|
145459
146169
|
registerLarkSetup(ctx, { updateSettings });
|
|
145460
|
-
registerSettingsRowActions(ctx, adapters);
|
|
145461
146170
|
const core = makeImCore({
|
|
145462
146171
|
host: {
|
|
145463
146172
|
log: ctx.log,
|
|
145464
146173
|
projections: ctx.projections,
|
|
145465
146174
|
projects: ctx.projects,
|
|
145466
146175
|
threads: ctx.threads,
|
|
146176
|
+
commands: ctx.commands,
|
|
145467
146177
|
approvals: ctx.approvals,
|
|
145468
146178
|
plugins: ctx.plugins,
|
|
145469
146179
|
attachments: ctx.attachments,
|
|
@@ -145513,7 +146223,9 @@ var definition = {
|
|
|
145513
146223
|
queueFlow: interactions.queueFlow,
|
|
145514
146224
|
rememberThreadProject: notifications.rememberThreadProject,
|
|
145515
146225
|
noteInboundUserMessage: webSync.noteInboundUserMessage,
|
|
145516
|
-
anchors: replyAnchors
|
|
146226
|
+
anchors: replyAnchors,
|
|
146227
|
+
failures,
|
|
146228
|
+
dispatchSleep
|
|
145517
146229
|
});
|
|
145518
146230
|
const sessions = makeSessions(core);
|
|
145519
146231
|
const modelPlan = makeModelPlan(core, turns);
|
|
@@ -145570,6 +146282,7 @@ var definition = {
|
|
|
145570
146282
|
]
|
|
145571
146283
|
});
|
|
145572
146284
|
const connections = startConnections(core, inbound.inboundHandlersFor);
|
|
146285
|
+
registerSettingsRowActions(ctx, adapters, connections);
|
|
145573
146286
|
registerSettingsCommands(ctx, adapters, updateSettings, connections);
|
|
145574
146287
|
const { bots, connecting } = connections.reconcile();
|
|
145575
146288
|
const deliveryHttp = makeDeliveryHttp({
|