@ycodium-ai/plugin-im 0.2.2488 → 0.2.2526
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/index.js +331 -145
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -10,6 +10,12 @@ Each settings-card row is one group:
|
|
|
10
10
|
|
|
11
11
|
- Pick a project and that group can start work there, and only hears that project.
|
|
12
12
|
- Leave the project empty and the row only reports, on every project.
|
|
13
|
+
- Archived projects are read-only. The project picker hides them unless a row is
|
|
14
|
+
already bound to one (shown as "(archived)"), and a message to a group bound
|
|
15
|
+
to an archived project gets a one-line reply that the project is archived
|
|
16
|
+
instead of starting work, at most once every ten minutes per group. Chat
|
|
17
|
+
commands such as help and status still work.
|
|
18
|
+
Restore the project to resume.
|
|
13
19
|
- Secrets (bot tokens, app credentials) stay in the environment secret store.
|
|
14
20
|
The patch file keeps a reference, not the value.
|
|
15
21
|
- Who may command the bot is an allow-list of that platform's user ids. Empty
|
package/dist/index.js
CHANGED
|
@@ -125980,9 +125980,49 @@ var describeTargets = (target, teams) => {
|
|
|
125980
125980
|
return [current, "", list, "", "\u7528 /team <\u961F\u540D> \u5207\u6362\uFF0C/thread \u5207\u56DE\u666E\u901A\u4F1A\u8BDD\u3002"].join("\n");
|
|
125981
125981
|
};
|
|
125982
125982
|
|
|
125983
|
+
// src/cooldown.ts
|
|
125984
|
+
var makeCooldown = (windowMs, now) => {
|
|
125985
|
+
const lastAt = /* @__PURE__ */ new Map();
|
|
125986
|
+
return {
|
|
125987
|
+
tryAcquire: (key) => {
|
|
125988
|
+
const current = now();
|
|
125989
|
+
const previous = lastAt.get(key);
|
|
125990
|
+
if (previous !== void 0 && current - previous < windowMs) return false;
|
|
125991
|
+
lastAt.set(key, current);
|
|
125992
|
+
return true;
|
|
125993
|
+
}
|
|
125994
|
+
};
|
|
125995
|
+
};
|
|
125996
|
+
|
|
125997
|
+
// src/splitMessage.ts
|
|
125998
|
+
var splitMessage = (text, limit) => {
|
|
125999
|
+
if (text.length <= limit) return [text];
|
|
126000
|
+
const chunks = [];
|
|
126001
|
+
let rest = text;
|
|
126002
|
+
while (rest.length > limit) {
|
|
126003
|
+
const window2 = rest.slice(0, limit);
|
|
126004
|
+
const paragraph = window2.lastIndexOf("\n\n");
|
|
126005
|
+
const line = window2.lastIndexOf("\n");
|
|
126006
|
+
const half = limit / 2;
|
|
126007
|
+
let cut;
|
|
126008
|
+
if (paragraph >= half) cut = paragraph + 2;
|
|
126009
|
+
else if (line >= half) cut = line + 1;
|
|
126010
|
+
else if (paragraph > 0 || line > 0) cut = Math.max(paragraph > 0 ? paragraph + 2 : 0, line + 1);
|
|
126011
|
+
else {
|
|
126012
|
+
cut = limit;
|
|
126013
|
+
const last = window2.charCodeAt(limit - 1);
|
|
126014
|
+
if (last >= 55296 && last <= 56319) cut -= 1;
|
|
126015
|
+
}
|
|
126016
|
+
chunks.push(rest.slice(0, cut));
|
|
126017
|
+
rest = rest.slice(cut);
|
|
126018
|
+
}
|
|
126019
|
+
if (rest.length > 0) chunks.push(rest);
|
|
126020
|
+
return chunks;
|
|
126021
|
+
};
|
|
126022
|
+
|
|
125983
126023
|
// src/larkApi.ts
|
|
125984
126024
|
var LARK_BASE = "https://open.feishu.cn/open-apis";
|
|
125985
|
-
var LARK_TEXT_MAX_CHARS =
|
|
126025
|
+
var LARK_TEXT_MAX_CHARS = 3500;
|
|
125986
126026
|
var LarkApiError = class extends Error {
|
|
125987
126027
|
operation;
|
|
125988
126028
|
code;
|
|
@@ -126122,7 +126162,7 @@ var sendChatCard = async (input) => {
|
|
|
126122
126162
|
const body = await readEnvelope("send.card", response);
|
|
126123
126163
|
return body.data?.message_id ?? null;
|
|
126124
126164
|
};
|
|
126125
|
-
var
|
|
126165
|
+
var splitForLark = (text) => splitMessage(text, LARK_TEXT_MAX_CHARS);
|
|
126126
126166
|
|
|
126127
126167
|
// src/larkInbound.ts
|
|
126128
126168
|
var import_node_sdk = __toESM(require_lib2(), 1);
|
|
@@ -126258,6 +126298,16 @@ var cardActionToken = (event) => {
|
|
|
126258
126298
|
}
|
|
126259
126299
|
return null;
|
|
126260
126300
|
};
|
|
126301
|
+
var cardActionOperator = (event) => {
|
|
126302
|
+
const roots = [event, event?.event];
|
|
126303
|
+
for (const root of roots) {
|
|
126304
|
+
const record = root;
|
|
126305
|
+
for (const candidate of [record?.operator?.open_id, record?.open_id]) {
|
|
126306
|
+
if (typeof candidate === "string" && candidate.length > 0) return candidate;
|
|
126307
|
+
}
|
|
126308
|
+
}
|
|
126309
|
+
return null;
|
|
126310
|
+
};
|
|
126261
126311
|
var startLarkInbound = (input) => {
|
|
126262
126312
|
const dispatcher = new import_node_sdk.EventDispatcher({}).register({
|
|
126263
126313
|
"im.message.receive_v1": (event) => {
|
|
@@ -126265,12 +126315,13 @@ var startLarkInbound = (input) => {
|
|
|
126265
126315
|
if (parsed !== null) input.onMessage(parsed);
|
|
126266
126316
|
return {};
|
|
126267
126317
|
},
|
|
126268
|
-
// 卡片点击走的也是 event
|
|
126269
|
-
//
|
|
126318
|
+
// 卡片点击走的也是 event 帧(实测过),所以在同一个 dispatcher 上注册就行,不用开
|
|
126319
|
+
// webhook。这条不需要 @:点按钮直接推给应用。
|
|
126270
126320
|
"card.action.trigger": (event) => {
|
|
126271
126321
|
const token = cardActionToken(event);
|
|
126272
|
-
if (token
|
|
126273
|
-
|
|
126322
|
+
if (token === null) return {};
|
|
126323
|
+
const notice = input.onCardAction(token, cardActionOperator(event));
|
|
126324
|
+
return notice === null ? {} : { toast: { type: "info", content: notice } };
|
|
126274
126325
|
}
|
|
126275
126326
|
});
|
|
126276
126327
|
const client = new import_node_sdk.WSClient({
|
|
@@ -126337,13 +126388,16 @@ var makeLarkAdapter = (deps) => {
|
|
|
126337
126388
|
});
|
|
126338
126389
|
},
|
|
126339
126390
|
sendText: async (binding, text) => {
|
|
126340
|
-
const
|
|
126341
|
-
|
|
126342
|
-
|
|
126343
|
-
|
|
126344
|
-
|
|
126345
|
-
|
|
126346
|
-
|
|
126391
|
+
const token = await tenantToken(binding);
|
|
126392
|
+
for (const chunk of splitForLark(text)) {
|
|
126393
|
+
const messageId = await sendChatText({
|
|
126394
|
+
http: deps.http,
|
|
126395
|
+
token,
|
|
126396
|
+
chatId: binding.chatId,
|
|
126397
|
+
text: chunk
|
|
126398
|
+
});
|
|
126399
|
+
deps.log.info("lark delivered a message", { messageId, group: binding.displayName });
|
|
126400
|
+
}
|
|
126347
126401
|
},
|
|
126348
126402
|
sendImage: async (binding, image) => {
|
|
126349
126403
|
const token = await tenantToken(binding);
|
|
@@ -126401,6 +126455,14 @@ var makeLarkAdapter = (deps) => {
|
|
|
126401
126455
|
};
|
|
126402
126456
|
};
|
|
126403
126457
|
|
|
126458
|
+
// src/projectArchive.ts
|
|
126459
|
+
var imProjectOptions = (projects, boundIds) => projects.flatMap((project) => {
|
|
126460
|
+
if (project.archivedAt == null) return [{ value: project.projectId, label: project.title }];
|
|
126461
|
+
return boundIds.has(project.projectId) ? [{ value: project.projectId, label: `${project.title} (archived)` }] : [];
|
|
126462
|
+
});
|
|
126463
|
+
var archivedProjectReply = (title) => `\u9879\u76EE\u300C${title}\u300D\u5DF2\u5F52\u6863\uFF0C\u6062\u590D\u540E\u518D\u53D1\u6D88\u606F\u3002`;
|
|
126464
|
+
var missingProjectReply = (group) => `\u300C${group}\u300D\u7ED1\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\u5230 Ycodium \u7684 IM \u8BBE\u7F6E\u91CC\u7ED9\u8FD9\u4E2A\u7FA4\u91CD\u65B0\u9009\u4E00\u4E2A\u9879\u76EE\u3002`;
|
|
126465
|
+
|
|
126404
126466
|
// src/telegramApi.ts
|
|
126405
126467
|
var API_BASE = "https://api.telegram.org";
|
|
126406
126468
|
var POLL_TIMEOUT_SECONDS = 45;
|
|
@@ -126451,15 +126513,17 @@ var resourceOf = (message) => {
|
|
|
126451
126513
|
}
|
|
126452
126514
|
return null;
|
|
126453
126515
|
};
|
|
126454
|
-
var splitForTelegram = (text, limit = TELEGRAM_TEXT_MAX_CHARS) =>
|
|
126455
|
-
|
|
126456
|
-
|
|
126457
|
-
|
|
126458
|
-
|
|
126516
|
+
var splitForTelegram = (text, limit = TELEGRAM_TEXT_MAX_CHARS) => splitMessage(text, limit);
|
|
126517
|
+
var nextOffset = (updates, current) => updates.reduce((offset, update) => Math.max(offset, update.update_id + 1), current);
|
|
126518
|
+
var commandForBot = (text, botUsername) => {
|
|
126519
|
+
const match = text.match(/^(\/[A-Za-z0-9_]+)@([A-Za-z0-9_]+)(?=\s|$)/);
|
|
126520
|
+
if (match === null) return text;
|
|
126521
|
+
const [whole, command = "", addressee = ""] = match;
|
|
126522
|
+
if (botUsername !== null && addressee.toLowerCase() !== botUsername.toLowerCase()) {
|
|
126523
|
+
return null;
|
|
126459
126524
|
}
|
|
126460
|
-
return
|
|
126525
|
+
return command + text.slice(whole.length);
|
|
126461
126526
|
};
|
|
126462
|
-
var nextOffset = (updates, current) => updates.reduce((offset, update) => Math.max(offset, update.update_id + 1), current);
|
|
126463
126527
|
var makeTelegramApi = (input) => {
|
|
126464
126528
|
const call = async (method, body) => {
|
|
126465
126529
|
const response = await input.http(`${API_BASE}/bot${input.token}/${method}`, {
|
|
@@ -126474,6 +126538,8 @@ var makeTelegramApi = (input) => {
|
|
|
126474
126538
|
return envelope.result;
|
|
126475
126539
|
};
|
|
126476
126540
|
return {
|
|
126541
|
+
/** 机器人自己是谁;群里的命令靠它的 `username` 认出是不是发给自己的。 */
|
|
126542
|
+
getMe: () => call("getMe", {}),
|
|
126477
126543
|
/**
|
|
126478
126544
|
* 拉一批更新。挂在服务端最多 `POLL_TIMEOUT_SECONDS` 秒。
|
|
126479
126545
|
*
|
|
@@ -126500,8 +126566,11 @@ var makeTelegramApi = (input) => {
|
|
|
126500
126566
|
])
|
|
126501
126567
|
}
|
|
126502
126568
|
}),
|
|
126503
|
-
/**
|
|
126504
|
-
answerCallback: (callbackQueryId) => call("answerCallbackQuery", {
|
|
126569
|
+
/** 答完点击,不然客户端上那个按钮一直转圈。带 `text` 时点击者会看到一句提示。 */
|
|
126570
|
+
answerCallback: (callbackQueryId, text) => call("answerCallbackQuery", {
|
|
126571
|
+
callback_query_id: callbackQueryId,
|
|
126572
|
+
...text === void 0 ? {} : { text }
|
|
126573
|
+
}),
|
|
126505
126574
|
sendPhoto: async (chatId, photo) => {
|
|
126506
126575
|
const form = new FormData();
|
|
126507
126576
|
form.set("chat_id", chatId);
|
|
@@ -126544,6 +126613,7 @@ var COLUMNS2 = [
|
|
|
126544
126613
|
var BACKOFF_START_MS = 1e3;
|
|
126545
126614
|
var BACKOFF_MAX_MS = 6e4;
|
|
126546
126615
|
var tokenOf = (binding) => binding.values["telegramToken"] ?? "";
|
|
126616
|
+
var defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
126547
126617
|
var makeTelegramAdapter = (deps) => {
|
|
126548
126618
|
const apis = /* @__PURE__ */ new Map();
|
|
126549
126619
|
const apiFor = async (binding) => {
|
|
@@ -126561,32 +126631,41 @@ var makeTelegramAdapter = (deps) => {
|
|
|
126561
126631
|
columns: COLUMNS2,
|
|
126562
126632
|
isConfigured: (binding) => tokenOf(binding) !== "",
|
|
126563
126633
|
connect: async (binding, handlers) => {
|
|
126564
|
-
const api = await apiFor(binding);
|
|
126565
126634
|
let stop = () => {
|
|
126566
126635
|
};
|
|
126567
126636
|
const stopped = new Promise((resolve) => stop = resolve);
|
|
126568
126637
|
let running = true;
|
|
126569
126638
|
let offset = 0;
|
|
126570
126639
|
let backoff = BACKOFF_START_MS;
|
|
126640
|
+
let botUsername;
|
|
126571
126641
|
const poll = async () => {
|
|
126572
126642
|
for (; ; ) {
|
|
126573
126643
|
if (!running) return;
|
|
126574
126644
|
try {
|
|
126645
|
+
const api = await apiFor(binding);
|
|
126646
|
+
botUsername ??= (await api.getMe()).username ?? null;
|
|
126575
126647
|
const updates = await api.getUpdates(offset);
|
|
126648
|
+
if (!running) return;
|
|
126576
126649
|
backoff = BACKOFF_START_MS;
|
|
126577
126650
|
offset = nextOffset(updates, offset);
|
|
126578
126651
|
for (const update of updates) {
|
|
126579
126652
|
const callback = update.callback_query;
|
|
126580
126653
|
if (callback?.data !== void 0) {
|
|
126581
|
-
handlers.onApproval(
|
|
126582
|
-
|
|
126654
|
+
const notice = handlers.onApproval(
|
|
126655
|
+
callback.data,
|
|
126656
|
+
callback.from === void 0 ? null : String(callback.from.id)
|
|
126657
|
+
);
|
|
126658
|
+
await api.answerCallback(callback.id, notice ?? void 0).catch(() => void 0);
|
|
126583
126659
|
continue;
|
|
126584
126660
|
}
|
|
126585
126661
|
const message = update.message;
|
|
126586
126662
|
if (message === void 0) continue;
|
|
126587
126663
|
const resource = resourceOf(message);
|
|
126588
|
-
const text = (
|
|
126589
|
-
|
|
126664
|
+
const text = commandForBot(
|
|
126665
|
+
(message.text ?? message.caption ?? "").trim(),
|
|
126666
|
+
botUsername
|
|
126667
|
+
);
|
|
126668
|
+
if (text === null || text === "" && resource === null) continue;
|
|
126590
126669
|
handlers.onMessage({
|
|
126591
126670
|
messageId: String(message.message_id),
|
|
126592
126671
|
chatId: String(message.chat.id),
|
|
@@ -126603,10 +126682,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
126603
126682
|
} catch (error) {
|
|
126604
126683
|
if (!running) return;
|
|
126605
126684
|
handlers.onError(error instanceof Error ? error.message : String(error));
|
|
126606
|
-
await Promise.race([
|
|
126607
|
-
new Promise((resolve) => setTimeout(resolve, backoff)),
|
|
126608
|
-
stopped
|
|
126609
|
-
]);
|
|
126685
|
+
await Promise.race([(deps.sleep ?? defaultSleep)(backoff), stopped]);
|
|
126610
126686
|
backoff = Math.min(backoff * 2, BACKOFF_MAX_MS);
|
|
126611
126687
|
}
|
|
126612
126688
|
}
|
|
@@ -126670,14 +126746,7 @@ var parseWecomMessage = (frame, botId) => {
|
|
|
126670
126746
|
chatType: mentioned ? "single" : chatType
|
|
126671
126747
|
};
|
|
126672
126748
|
};
|
|
126673
|
-
var splitForWecom = (text, limit = WECOM_TEXT_MAX_CHARS) =>
|
|
126674
|
-
if (text.length <= limit) return [text];
|
|
126675
|
-
const chunks = [];
|
|
126676
|
-
for (let index = 0; index < text.length; index += limit) {
|
|
126677
|
-
chunks.push(text.slice(index, index + limit));
|
|
126678
|
-
}
|
|
126679
|
-
return chunks;
|
|
126680
|
-
};
|
|
126749
|
+
var splitForWecom = (text, limit = WECOM_TEXT_MAX_CHARS) => splitMessage(text, limit);
|
|
126681
126750
|
var decoder = new TextDecoder();
|
|
126682
126751
|
var runWecomClient = (input) => {
|
|
126683
126752
|
let sequence = 0;
|
|
@@ -126687,6 +126756,8 @@ var runWecomClient = (input) => {
|
|
|
126687
126756
|
const subscribeId = nextRequestId("aibot_subscribe");
|
|
126688
126757
|
let missedPong = 0;
|
|
126689
126758
|
let closed = false;
|
|
126759
|
+
let finished = false;
|
|
126760
|
+
const ended = Promise.withResolvers();
|
|
126690
126761
|
const send = (frame) => input.socket.send(JSON.stringify(frame));
|
|
126691
126762
|
const settleAck = (requestId, error) => {
|
|
126692
126763
|
const pending = pendingAcks.get(requestId);
|
|
@@ -126718,6 +126789,16 @@ var runWecomClient = (input) => {
|
|
|
126718
126789
|
const message = parseWecomMessage(frame, input.botId);
|
|
126719
126790
|
if (message !== null) input.handlers.onMessage(message);
|
|
126720
126791
|
};
|
|
126792
|
+
const finish = () => {
|
|
126793
|
+
if (finished) return;
|
|
126794
|
+
finished = true;
|
|
126795
|
+
clearInterval(heartbeat);
|
|
126796
|
+
void input.socket.close().catch(() => void 0);
|
|
126797
|
+
for (const requestId of pendingAcks.keys()) {
|
|
126798
|
+
settleAck(requestId, new Error("WeCom connection closed"));
|
|
126799
|
+
}
|
|
126800
|
+
ended.resolve();
|
|
126801
|
+
};
|
|
126721
126802
|
const readLoop = async () => {
|
|
126722
126803
|
try {
|
|
126723
126804
|
for await (const chunk of input.socket.messages) {
|
|
@@ -126728,12 +126809,13 @@ var runWecomClient = (input) => {
|
|
|
126728
126809
|
input.handlers.onError(error instanceof Error ? error.message : String(error));
|
|
126729
126810
|
}
|
|
126730
126811
|
}
|
|
126812
|
+
finish();
|
|
126731
126813
|
};
|
|
126732
126814
|
const heartbeat = setInterval(() => {
|
|
126733
|
-
if (
|
|
126815
|
+
if (finished) return;
|
|
126734
126816
|
if (missedPong >= MAX_MISSED_PONGS) {
|
|
126735
126817
|
input.handlers.onError("socket hang up: missed pong");
|
|
126736
|
-
|
|
126818
|
+
finish();
|
|
126737
126819
|
return;
|
|
126738
126820
|
}
|
|
126739
126821
|
missedPong += 1;
|
|
@@ -126753,9 +126835,14 @@ var runWecomClient = (input) => {
|
|
|
126753
126835
|
}).catch((error) => {
|
|
126754
126836
|
settleAck(subscribeId, error instanceof Error ? error : new Error(String(error)));
|
|
126755
126837
|
});
|
|
126756
|
-
|
|
126757
|
-
|
|
126758
|
-
|
|
126838
|
+
const ready = subscribed.then(
|
|
126839
|
+
() => true,
|
|
126840
|
+
(error) => {
|
|
126841
|
+
if (!closed) input.handlers.onError(error instanceof Error ? error.message : String(error));
|
|
126842
|
+
finish();
|
|
126843
|
+
return false;
|
|
126844
|
+
}
|
|
126845
|
+
);
|
|
126759
126846
|
const sendText = async (chatId, text) => {
|
|
126760
126847
|
for (const [index, content] of splitForWecom(text).entries()) {
|
|
126761
126848
|
const inboundRequestId = index === 0 ? pendingRequests.get(chatId) : void 0;
|
|
@@ -126800,18 +126887,22 @@ var runWecomClient = (input) => {
|
|
|
126800
126887
|
},
|
|
126801
126888
|
close: () => {
|
|
126802
126889
|
closed = true;
|
|
126803
|
-
|
|
126804
|
-
void input.socket.close();
|
|
126890
|
+
finish();
|
|
126805
126891
|
},
|
|
126892
|
+
ended: ended.promise,
|
|
126893
|
+
ready,
|
|
126806
126894
|
pendingAckCountForTests: () => pendingAcks.size
|
|
126807
126895
|
};
|
|
126808
126896
|
};
|
|
126809
126897
|
|
|
126810
126898
|
// src/wecomAdapter.ts
|
|
126899
|
+
var BACKOFF_START_MS2 = 1e3;
|
|
126900
|
+
var BACKOFF_MAX_MS2 = 6e4;
|
|
126811
126901
|
var COLUMNS3 = [
|
|
126812
126902
|
{ key: "wecomBotId", kind: "text", label: "WeCom bot ID" },
|
|
126813
126903
|
{ key: "wecomBotSecret", kind: "password", label: "WeCom bot secret" }
|
|
126814
126904
|
];
|
|
126905
|
+
var defaultSleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
126815
126906
|
var credentials2 = (binding) => ({
|
|
126816
126907
|
botId: binding.values["wecomBotId"] ?? "",
|
|
126817
126908
|
botSecret: binding.values["wecomBotSecret"] ?? "",
|
|
@@ -126834,39 +126925,73 @@ var makeWecomAdapter = (deps) => {
|
|
|
126834
126925
|
const { botId, botSecret } = credentials2(binding);
|
|
126835
126926
|
return botId !== "" && botSecret !== "";
|
|
126836
126927
|
},
|
|
126837
|
-
connect:
|
|
126928
|
+
connect: (binding, handlers) => {
|
|
126838
126929
|
const { botId, botSecret, endpoint } = credentials2(binding);
|
|
126839
|
-
|
|
126840
|
-
|
|
126841
|
-
|
|
126842
|
-
|
|
126843
|
-
|
|
126844
|
-
|
|
126845
|
-
|
|
126846
|
-
|
|
126847
|
-
|
|
126848
|
-
|
|
126849
|
-
|
|
126850
|
-
|
|
126851
|
-
|
|
126852
|
-
|
|
126853
|
-
|
|
126854
|
-
|
|
126855
|
-
|
|
126856
|
-
|
|
126857
|
-
|
|
126858
|
-
|
|
126859
|
-
|
|
126860
|
-
|
|
126930
|
+
let stopped = false;
|
|
126931
|
+
let current = null;
|
|
126932
|
+
const stopSignal = Promise.withResolvers();
|
|
126933
|
+
const runOnce = async () => {
|
|
126934
|
+
const socket = await deps.connectSocket(endpoint);
|
|
126935
|
+
if (stopped) {
|
|
126936
|
+
await socket.close();
|
|
126937
|
+
return false;
|
|
126938
|
+
}
|
|
126939
|
+
const client = runWecomClient({
|
|
126940
|
+
socket: {
|
|
126941
|
+
send: (data) => socket.send(data),
|
|
126942
|
+
messages: socket.messages,
|
|
126943
|
+
close: () => socket.close()
|
|
126944
|
+
},
|
|
126945
|
+
botId,
|
|
126946
|
+
botSecret,
|
|
126947
|
+
handlers: {
|
|
126948
|
+
onError: handlers.onError,
|
|
126949
|
+
onMessage: (message) => {
|
|
126950
|
+
if (message.chatType !== "single") return;
|
|
126951
|
+
client.rememberRequest(message.chatId, message.requestId);
|
|
126952
|
+
handlers.onMessage({
|
|
126953
|
+
messageId: message.messageId,
|
|
126954
|
+
chatId: message.chatId,
|
|
126955
|
+
senderId: message.senderUserId,
|
|
126956
|
+
text: message.text,
|
|
126957
|
+
// 企微这一版不收附件,先不假装支持。
|
|
126958
|
+
resource: null
|
|
126959
|
+
});
|
|
126960
|
+
}
|
|
126861
126961
|
}
|
|
126962
|
+
});
|
|
126963
|
+
current = client;
|
|
126964
|
+
clients.set(binding.rowId, client);
|
|
126965
|
+
const healthy = client.ready.then((ok) => {
|
|
126966
|
+
if (ok) deps.log.info("wecom connected", { group: binding.displayName });
|
|
126967
|
+
return ok;
|
|
126968
|
+
});
|
|
126969
|
+
await client.ended;
|
|
126970
|
+
if (clients.get(binding.rowId) === client) clients.delete(binding.rowId);
|
|
126971
|
+
current = null;
|
|
126972
|
+
return healthy;
|
|
126973
|
+
};
|
|
126974
|
+
const supervise = async () => {
|
|
126975
|
+
let backoff = BACKOFF_START_MS2;
|
|
126976
|
+
for (; ; ) {
|
|
126977
|
+
if (stopped) return;
|
|
126978
|
+
try {
|
|
126979
|
+
if (await runOnce()) backoff = BACKOFF_START_MS2;
|
|
126980
|
+
} catch (error) {
|
|
126981
|
+
handlers.onError(error instanceof Error ? error.message : String(error));
|
|
126982
|
+
}
|
|
126983
|
+
if (stopped) return;
|
|
126984
|
+
deps.log.warn("wecom reconnecting", { group: binding.displayName, inMs: backoff });
|
|
126985
|
+
await Promise.race([(deps.sleep ?? defaultSleep2)(backoff), stopSignal.promise]);
|
|
126986
|
+
backoff = Math.min(backoff * 2, BACKOFF_MAX_MS2);
|
|
126862
126987
|
}
|
|
126863
|
-
}
|
|
126864
|
-
|
|
126865
|
-
deps.log.info("wecom connected", { group: binding.displayName });
|
|
126988
|
+
};
|
|
126989
|
+
void supervise();
|
|
126866
126990
|
return {
|
|
126867
126991
|
close: () => {
|
|
126868
|
-
|
|
126869
|
-
|
|
126992
|
+
stopped = true;
|
|
126993
|
+
stopSignal.resolve();
|
|
126994
|
+
current?.close();
|
|
126870
126995
|
}
|
|
126871
126996
|
};
|
|
126872
126997
|
},
|
|
@@ -126901,6 +127026,9 @@ var SEEN_MESSAGE_LIMIT = 256;
|
|
|
126901
127026
|
var THREAD_PROJECT_CACHE_LIMIT = 256;
|
|
126902
127027
|
var TITLE_MAX_CHARS = 72;
|
|
126903
127028
|
var DEFAULT_PLATFORM = "lark";
|
|
127029
|
+
var ARCHIVED_REPLY_COOLDOWN_MS = 10 * 6e4;
|
|
127030
|
+
var NOT_ALLOWED_NOTICE = "\u4F60\u4E0D\u5728\u8FD9\u4E2A\u7FA4\u7684\u53EF\u6307\u6325\u540D\u5355\u91CC\uFF0C\u8FD9\u6B21\u70B9\u51FB\u6CA1\u6709\u751F\u6548\u3002";
|
|
127031
|
+
var noDefaultModelReply = (title) => `\u9879\u76EE\u300C${title}\u300D\u8FD8\u6CA1\u6709\u9ED8\u8BA4\u6A21\u578B\uFF0C\u5F00\u4E0D\u4E86\u4F1A\u8BDD\u3002\u5148\u5728 Ycodium \u91CC\u7ED9\u5B83\u9009\u4E00\u4E2A\u9ED8\u8BA4\u6A21\u578B\uFF0C\u518D\u56DE\u6765\u53D1\u6D88\u606F\u3002`;
|
|
126904
127032
|
var INBOUND_RUNTIME_MODES = /* @__PURE__ */ new Set([
|
|
126905
127033
|
"approval-required",
|
|
126906
127034
|
"auto-accept-edits",
|
|
@@ -126908,6 +127036,7 @@ var INBOUND_RUNTIME_MODES = /* @__PURE__ */ new Set([
|
|
|
126908
127036
|
"full-access"
|
|
126909
127037
|
]);
|
|
126910
127038
|
var DEFAULT_INBOUND_RUNTIME_MODE = "auto-accept-edits";
|
|
127039
|
+
var chatKeyOf = (binding) => `${binding.platform}:${binding.chatId}`;
|
|
126911
127040
|
var readString = (row, key) => {
|
|
126912
127041
|
const value = row[key];
|
|
126913
127042
|
return typeof value === "string" ? value.trim() : "";
|
|
@@ -126951,6 +127080,15 @@ var imPlugin = definePlugin({
|
|
|
126951
127080
|
];
|
|
126952
127081
|
const adapterFor = (platform) => adapters.find((adapter) => adapter.platform === platform);
|
|
126953
127082
|
const projects = await ctx.projections.listProjects().catch(() => []);
|
|
127083
|
+
const boundProjectIds = /* @__PURE__ */ new Set();
|
|
127084
|
+
const configuredRows = ctx.config[GROUPS_FIELD];
|
|
127085
|
+
if (typeof configuredRows === "object" && configuredRows !== null) {
|
|
127086
|
+
for (const row of Object.values(configuredRows)) {
|
|
127087
|
+
if (typeof row !== "object" || row === null) continue;
|
|
127088
|
+
const projectId = readString(row, "projectId");
|
|
127089
|
+
if (projectId !== "") boundProjectIds.add(projectId);
|
|
127090
|
+
}
|
|
127091
|
+
}
|
|
126954
127092
|
const platformColumn = {
|
|
126955
127093
|
key: "platform",
|
|
126956
127094
|
kind: "select",
|
|
@@ -127006,7 +127144,7 @@ var imPlugin = definePlugin({
|
|
|
127006
127144
|
description: "Where a message from this group starts its thread. Empty means the row only reports, and reports every project.",
|
|
127007
127145
|
options: [
|
|
127008
127146
|
{ value: "", label: "Report only, all projects" },
|
|
127009
|
-
...projects
|
|
127147
|
+
...imProjectOptions(projects, boundProjectIds)
|
|
127010
127148
|
]
|
|
127011
127149
|
},
|
|
127012
127150
|
{
|
|
@@ -127119,7 +127257,7 @@ var imPlugin = definePlugin({
|
|
|
127119
127257
|
const targetsForProject = (projectId) => readBindings().filter(
|
|
127120
127258
|
(binding) => binding.projectId === "" || binding.projectId === projectId
|
|
127121
127259
|
);
|
|
127122
|
-
const bindingForChat = (chatId) => readBindings().find((binding) => binding.chatId === chatId);
|
|
127260
|
+
const bindingForChat = (platform, chatId) => readBindings().find((binding) => binding.platform === platform && binding.chatId === chatId);
|
|
127123
127261
|
const threadsWithSomethingToReport = /* @__PURE__ */ new Set();
|
|
127124
127262
|
const acknowledgements = /* @__PURE__ */ new Map();
|
|
127125
127263
|
const markWorking = async (binding, threadId, messageId) => {
|
|
@@ -127202,7 +127340,7 @@ ${final.text}`, final.attachments);
|
|
|
127202
127340
|
const binding = chatByRun.get(runId);
|
|
127203
127341
|
if (binding === void 0) return;
|
|
127204
127342
|
chatByRun.delete(runId);
|
|
127205
|
-
await clearWorking(binding, binding
|
|
127343
|
+
await clearWorking(binding, chatKeyOf(binding));
|
|
127206
127344
|
await deliver(binding, event.summary);
|
|
127207
127345
|
});
|
|
127208
127346
|
ctx.events.on("thread.failed", async (event) => {
|
|
@@ -127225,8 +127363,8 @@ ${final.text}`, final.attachments);
|
|
|
127225
127363
|
for (const [key, value] of answersByToken) {
|
|
127226
127364
|
if (value.requestId === answer.requestId) answersByToken.delete(key);
|
|
127227
127365
|
}
|
|
127228
|
-
for (const [
|
|
127229
|
-
if (options.some((option) => option.token === token)) openOptionsByChat.delete(
|
|
127366
|
+
for (const [chatKey, options] of openOptionsByChat) {
|
|
127367
|
+
if (options.some((option) => option.token === token)) openOptionsByChat.delete(chatKey);
|
|
127230
127368
|
}
|
|
127231
127369
|
await ctx.commands.dispatch({
|
|
127232
127370
|
type: "thread.approval.respond",
|
|
@@ -127245,7 +127383,7 @@ ${final.text}`, final.attachments);
|
|
|
127245
127383
|
const threadId = event.threadId;
|
|
127246
127384
|
if (threadId === null) return;
|
|
127247
127385
|
const binding = readBindings().find(
|
|
127248
|
-
(candidate) => threadByChat.get(candidate
|
|
127386
|
+
(candidate) => threadByChat.get(chatKeyOf(candidate)) === threadId
|
|
127249
127387
|
);
|
|
127250
127388
|
if (binding === void 0) return;
|
|
127251
127389
|
const adapter = adapterFor(binding.platform);
|
|
@@ -127262,11 +127400,12 @@ ${final.text}`, final.attachments);
|
|
|
127262
127400
|
answersByToken.set(token, {
|
|
127263
127401
|
threadId,
|
|
127264
127402
|
requestId: approval.requestId,
|
|
127265
|
-
decision: option.decision
|
|
127403
|
+
decision: option.decision,
|
|
127404
|
+
chatKey: chatKeyOf(binding)
|
|
127266
127405
|
});
|
|
127267
127406
|
return { label: option.label, token };
|
|
127268
127407
|
});
|
|
127269
|
-
openOptionsByChat.set(binding
|
|
127408
|
+
openOptionsByChat.set(chatKeyOf(binding), buttons);
|
|
127270
127409
|
try {
|
|
127271
127410
|
await adapter.askApproval(binding, {
|
|
127272
127411
|
title: approval.appName ?? "Ycodium",
|
|
@@ -127278,10 +127417,29 @@ ${final.text}`, final.attachments);
|
|
|
127278
127417
|
ctx.log.warn("chat could not ask for the approval", { error: describeError(error) });
|
|
127279
127418
|
}
|
|
127280
127419
|
});
|
|
127281
|
-
const
|
|
127282
|
-
|
|
127283
|
-
|
|
127284
|
-
|
|
127420
|
+
const isAllowedSender = (binding, sender) => binding.allowedSenders.size === 0 || sender !== null && binding.allowedSenders.has(sender);
|
|
127421
|
+
const handleApprovalClick = (token, operatorId) => {
|
|
127422
|
+
const answer = answersByToken.get(token);
|
|
127423
|
+
if (answer === void 0) return null;
|
|
127424
|
+
const target = readBindings().find((binding) => chatKeyOf(binding) === answer.chatKey);
|
|
127425
|
+
if (target === void 0 || !isAllowedSender(target, operatorId)) {
|
|
127426
|
+
ctx.log.warn("chat ignored an approval click from someone not on the allow list", {
|
|
127427
|
+
group: target?.displayName ?? answer.chatKey,
|
|
127428
|
+
operator: operatorId
|
|
127429
|
+
});
|
|
127430
|
+
return NOT_ALLOWED_NOTICE;
|
|
127431
|
+
}
|
|
127432
|
+
void answerByToken(token).catch((error) => {
|
|
127433
|
+
ctx.log.warn("chat could not answer the approval", { error: describeError(error) });
|
|
127434
|
+
});
|
|
127435
|
+
return null;
|
|
127436
|
+
};
|
|
127437
|
+
const archivedReplyCooldown = makeCooldown(ARCHIVED_REPLY_COOLDOWN_MS, () => Date.now());
|
|
127438
|
+
const alreadyHandled = (target, messageId) => {
|
|
127439
|
+
const key = `${chatKeyOf(target)}:${messageId}`;
|
|
127440
|
+
if (seen.has(key)) return true;
|
|
127441
|
+
seen.add(key);
|
|
127442
|
+
seenOrder.push(key);
|
|
127285
127443
|
if (seenOrder.length > SEEN_MESSAGE_LIMIT) {
|
|
127286
127444
|
const oldest = seenOrder.shift();
|
|
127287
127445
|
if (oldest !== void 0) seen.delete(oldest);
|
|
@@ -127329,7 +127487,7 @@ ${final.text}`, final.attachments);
|
|
|
127329
127487
|
) : []
|
|
127330
127488
|
).catch(() => []);
|
|
127331
127489
|
const command = parseChatCommand({ text: message.text, teams, chinese: true });
|
|
127332
|
-
const
|
|
127490
|
+
const chatKey = chatKeyOf(binding);
|
|
127333
127491
|
if (command.kind !== "message") {
|
|
127334
127492
|
ctx.log.info("chat handled a command", {
|
|
127335
127493
|
group: binding.displayName,
|
|
@@ -127340,19 +127498,19 @@ ${final.text}`, final.attachments);
|
|
|
127340
127498
|
case "message":
|
|
127341
127499
|
return false;
|
|
127342
127500
|
case "selectTeam":
|
|
127343
|
-
targetByChat.set(
|
|
127344
|
-
threadByChat.delete(
|
|
127501
|
+
targetByChat.set(chatKey, { kind: "team", teamId: command.team.teamId });
|
|
127502
|
+
threadByChat.delete(chatKey);
|
|
127345
127503
|
await deliver(binding, `\u597D\uFF0C\u63A5\u4E0B\u6765\u6D3E\u7ED9\u300C${command.team.title}\u300D\u3002`);
|
|
127346
127504
|
return true;
|
|
127347
127505
|
case "selectThread":
|
|
127348
|
-
targetByChat.set(
|
|
127349
|
-
threadByChat.delete(
|
|
127506
|
+
targetByChat.set(chatKey, { kind: "thread" });
|
|
127507
|
+
threadByChat.delete(chatKey);
|
|
127350
127508
|
await deliver(binding, "\u597D\uFF0C\u63A5\u4E0B\u6765\u5F00\u666E\u901A\u4F1A\u8BDD\u3002");
|
|
127351
127509
|
return true;
|
|
127352
127510
|
case "showTargets":
|
|
127353
127511
|
await deliver(
|
|
127354
127512
|
binding,
|
|
127355
|
-
describeTargets(targetByChat.get(
|
|
127513
|
+
describeTargets(targetByChat.get(chatKey) ?? { kind: "thread" }, teams)
|
|
127356
127514
|
);
|
|
127357
127515
|
return true;
|
|
127358
127516
|
case "teamNotFound":
|
|
@@ -127361,7 +127519,7 @@ ${final.text}`, final.attachments);
|
|
|
127361
127519
|
[
|
|
127362
127520
|
`\u6CA1\u627E\u5230\u53EB\u300C${command.query}\u300D\u7684\u961F\uFF0C\u4E5F\u53EF\u80FD\u662F\u4E0D\u6B62\u4E00\u652F\u5BF9\u5F97\u4E0A\u3002`,
|
|
127363
127521
|
"",
|
|
127364
|
-
describeTargets(targetByChat.get(
|
|
127522
|
+
describeTargets(targetByChat.get(chatKey) ?? { kind: "thread" }, teams)
|
|
127365
127523
|
].join("\n")
|
|
127366
127524
|
);
|
|
127367
127525
|
return true;
|
|
@@ -127372,12 +127530,12 @@ ${final.text}`, final.attachments);
|
|
|
127372
127530
|
await deliver(binding, helpText());
|
|
127373
127531
|
return true;
|
|
127374
127532
|
case "new":
|
|
127375
|
-
threadByChat.delete(
|
|
127533
|
+
threadByChat.delete(chatKey);
|
|
127376
127534
|
await deliver(binding, "\u597D\uFF0C\u4E0B\u4E00\u53E5\u8BDD\u5F00\u4E00\u6761\u65B0\u4F1A\u8BDD\u3002");
|
|
127377
127535
|
return true;
|
|
127378
127536
|
case "status": {
|
|
127379
|
-
const threadId = threadByChat.get(
|
|
127380
|
-
const routed = targetByChat.get(
|
|
127537
|
+
const threadId = threadByChat.get(chatKey);
|
|
127538
|
+
const routed = targetByChat.get(chatKey) ?? { kind: "thread" };
|
|
127381
127539
|
const where = routed.kind === "team" ? `\u6D3E\u7ED9\uFF1A${teams.find((team) => team.teamId === routed.teamId)?.title ?? routed.teamId}` : "\u666E\u901A\u4F1A\u8BDD";
|
|
127382
127540
|
if (threadId === void 0) {
|
|
127383
127541
|
await deliver(binding, `${where}\u3002\u73B0\u5728\u6CA1\u6709\u5728\u8DD1\u7684\u6D3B\u513F\u3002`);
|
|
@@ -127396,7 +127554,7 @@ ${final.text}`, final.attachments);
|
|
|
127396
127554
|
return true;
|
|
127397
127555
|
}
|
|
127398
127556
|
case "stop": {
|
|
127399
|
-
const threadId = threadByChat.get(
|
|
127557
|
+
const threadId = threadByChat.get(chatKey);
|
|
127400
127558
|
if (threadId === void 0) {
|
|
127401
127559
|
await deliver(binding, "\u8FD9\u4E2A\u7FA4\u73B0\u5728\u6CA1\u6709\u5728\u8DD1\u7684\u6D3B\u513F\u3002");
|
|
127402
127560
|
return true;
|
|
@@ -127442,9 +127600,9 @@ ${final.text}`, final.attachments);
|
|
|
127442
127600
|
ctx.log.info("chat started a team run", { group: binding.displayName, teamId, runId });
|
|
127443
127601
|
};
|
|
127444
127602
|
const startTurn = async (binding, message) => {
|
|
127445
|
-
const
|
|
127603
|
+
const chatKey = chatKeyOf(binding);
|
|
127446
127604
|
const text = message.text;
|
|
127447
|
-
let threadId = threadByChat.get(
|
|
127605
|
+
let threadId = threadByChat.get(chatKey);
|
|
127448
127606
|
if (threadId === void 0) {
|
|
127449
127607
|
const project = await ctx.projections.getProject(binding.projectId);
|
|
127450
127608
|
if (project === null) {
|
|
@@ -127454,6 +127612,14 @@ ${final.text}`, final.attachments);
|
|
|
127454
127612
|
});
|
|
127455
127613
|
return null;
|
|
127456
127614
|
}
|
|
127615
|
+
if (project.defaultModelSelection === null) {
|
|
127616
|
+
ctx.log.warn("chat cannot start a thread: the project has no default model", {
|
|
127617
|
+
group: binding.displayName,
|
|
127618
|
+
projectId: binding.projectId
|
|
127619
|
+
});
|
|
127620
|
+
await deliver(binding, noDefaultModelReply(project.title));
|
|
127621
|
+
return null;
|
|
127622
|
+
}
|
|
127457
127623
|
threadId = crypto.randomUUID();
|
|
127458
127624
|
await ctx.commands.dispatch({
|
|
127459
127625
|
type: "thread.create",
|
|
@@ -127461,14 +127627,14 @@ ${final.text}`, final.attachments);
|
|
|
127461
127627
|
threadId,
|
|
127462
127628
|
projectId: binding.projectId,
|
|
127463
127629
|
title: titleFrom(text || message.resource?.name || "Chat"),
|
|
127464
|
-
|
|
127630
|
+
modelSelection: project.defaultModelSelection,
|
|
127465
127631
|
runtimeMode: binding.runtimeMode,
|
|
127466
127632
|
interactionMode: "default",
|
|
127467
127633
|
branch: null,
|
|
127468
127634
|
worktreePath: null,
|
|
127469
127635
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
127470
127636
|
});
|
|
127471
|
-
threadByChat.set(
|
|
127637
|
+
threadByChat.set(chatKey, threadId);
|
|
127472
127638
|
projectByThread.set(threadId, binding.projectId);
|
|
127473
127639
|
}
|
|
127474
127640
|
const attachments = await collectAttachment(binding, threadId, message);
|
|
@@ -127489,7 +127655,7 @@ ${final.text}`, final.attachments);
|
|
|
127489
127655
|
});
|
|
127490
127656
|
ctx.log.info("chat started a turn from a message", {
|
|
127491
127657
|
group: binding.displayName,
|
|
127492
|
-
chatId,
|
|
127658
|
+
chatId: binding.chatId,
|
|
127493
127659
|
threadId
|
|
127494
127660
|
});
|
|
127495
127661
|
return threadId;
|
|
@@ -127497,65 +127663,85 @@ ${final.text}`, final.attachments);
|
|
|
127497
127663
|
const receiving = readBindings().filter((binding) => binding.projectId !== "");
|
|
127498
127664
|
const connections = [];
|
|
127499
127665
|
const started = /* @__PURE__ */ new Set();
|
|
127500
|
-
|
|
127501
|
-
|
|
127502
|
-
|
|
127503
|
-
|
|
127504
|
-
|
|
127505
|
-
|
|
127506
|
-
|
|
127507
|
-
|
|
127508
|
-
|
|
127509
|
-
|
|
127666
|
+
let disposed = false;
|
|
127667
|
+
const inboundHandlersFor = (platform) => ({
|
|
127668
|
+
onError: (message) => ctx.log.warn("chat inbound connection failed", { platform, message }),
|
|
127669
|
+
onApproval: handleApprovalClick,
|
|
127670
|
+
onMessage: (message) => {
|
|
127671
|
+
const target = bindingForChat(platform, message.chatId);
|
|
127672
|
+
if (target === void 0 || target.projectId === "") return;
|
|
127673
|
+
if (alreadyHandled(target, message.messageId)) return;
|
|
127674
|
+
if (!isAllowedSender(target, message.senderId)) {
|
|
127675
|
+
ctx.log.warn("chat ignored a message from someone not on the allow list", {
|
|
127676
|
+
group: target.displayName,
|
|
127677
|
+
sender: message.senderId
|
|
127678
|
+
});
|
|
127679
|
+
return;
|
|
127680
|
+
}
|
|
127681
|
+
const open = openOptionsByChat.get(chatKeyOf(target));
|
|
127682
|
+
const answerToken = open === void 0 ? null : adapterFor(target.platform)?.answerByText?.(target, message.text, open) ?? null;
|
|
127683
|
+
if (answerToken !== null) {
|
|
127684
|
+
void answerByToken(answerToken).catch((error) => {
|
|
127510
127685
|
ctx.log.warn("chat could not answer the approval", { error: describeError(error) });
|
|
127511
127686
|
});
|
|
127512
|
-
|
|
127513
|
-
|
|
127514
|
-
|
|
127515
|
-
if (
|
|
127516
|
-
|
|
127517
|
-
if (
|
|
127518
|
-
|
|
127519
|
-
|
|
127520
|
-
ctx.log.warn("chat ignored a message from someone not on the allow list", {
|
|
127687
|
+
return;
|
|
127688
|
+
}
|
|
127689
|
+
void (async () => {
|
|
127690
|
+
if (await handleCommand(target, message)) return;
|
|
127691
|
+
const project = await ctx.projections.getProject(target.projectId);
|
|
127692
|
+
if (project === null || project.archivedAt != null) {
|
|
127693
|
+
if (project === null) {
|
|
127694
|
+
ctx.log.warn("chat cannot start work: the configured project is gone", {
|
|
127521
127695
|
group: target.displayName,
|
|
127522
|
-
|
|
127696
|
+
projectId: target.projectId
|
|
127523
127697
|
});
|
|
127524
|
-
return;
|
|
127525
127698
|
}
|
|
127699
|
+
if (archivedReplyCooldown.tryAcquire(chatKeyOf(target))) {
|
|
127700
|
+
await deliver(
|
|
127701
|
+
target,
|
|
127702
|
+
project === null ? missingProjectReply(target.displayName) : archivedProjectReply(project.title)
|
|
127703
|
+
);
|
|
127704
|
+
}
|
|
127705
|
+
return;
|
|
127526
127706
|
}
|
|
127527
|
-
const
|
|
127528
|
-
|
|
127529
|
-
|
|
127530
|
-
|
|
127531
|
-
ctx.log.warn("chat could not answer the approval", { error: describeError(error) });
|
|
127532
|
-
});
|
|
127707
|
+
const routed = targetByChat.get(chatKeyOf(target)) ?? { kind: "thread" };
|
|
127708
|
+
if (routed.kind === "team") {
|
|
127709
|
+
await startTeamRun(target, routed.teamId, message.text);
|
|
127710
|
+
await markWorking(target, chatKeyOf(target), message.messageId);
|
|
127533
127711
|
return;
|
|
127534
127712
|
}
|
|
127535
|
-
|
|
127536
|
-
|
|
127537
|
-
|
|
127538
|
-
|
|
127539
|
-
|
|
127540
|
-
|
|
127541
|
-
|
|
127542
|
-
|
|
127543
|
-
|
|
127544
|
-
|
|
127545
|
-
|
|
127546
|
-
|
|
127547
|
-
|
|
127548
|
-
|
|
127713
|
+
const threadId = await startTurn(target, message);
|
|
127714
|
+
if (threadId !== null) await markWorking(target, threadId, message.messageId);
|
|
127715
|
+
})().catch((error) => {
|
|
127716
|
+
ctx.log.warn("chat could not handle the message", { error: describeError(error) });
|
|
127717
|
+
});
|
|
127718
|
+
}
|
|
127719
|
+
});
|
|
127720
|
+
for (const binding of receiving) {
|
|
127721
|
+
const adapter = adapterFor(binding.platform);
|
|
127722
|
+
if (adapter === void 0) continue;
|
|
127723
|
+
const fingerprint = `${binding.platform}:${adapter.columns.map((column) => binding.values[column.key] ?? "").join(" ")}`;
|
|
127724
|
+
if (started.has(fingerprint)) continue;
|
|
127725
|
+
started.add(fingerprint);
|
|
127726
|
+
void Promise.resolve().then(() => adapter.connect(binding, inboundHandlersFor(binding.platform))).then((connection) => {
|
|
127727
|
+
if (disposed) connection.close();
|
|
127728
|
+
else connections.push(connection);
|
|
127729
|
+
}).catch((error) => {
|
|
127730
|
+
ctx.log.warn("chat could not start an inbound connection", {
|
|
127731
|
+
group: binding.displayName,
|
|
127732
|
+
platform: binding.platform,
|
|
127733
|
+
error: describeError(error)
|
|
127734
|
+
});
|
|
127549
127735
|
});
|
|
127550
|
-
connections.push(connection);
|
|
127551
127736
|
}
|
|
127552
127737
|
ctx.onDispose(() => {
|
|
127738
|
+
disposed = true;
|
|
127553
127739
|
for (const connection of connections) connection.close();
|
|
127554
127740
|
});
|
|
127555
127741
|
ctx.log.info("chat plugin active", {
|
|
127556
127742
|
groups: readBindings().length,
|
|
127557
127743
|
receiving: receiving.length,
|
|
127558
|
-
|
|
127744
|
+
connecting: started.size
|
|
127559
127745
|
});
|
|
127560
127746
|
}
|
|
127561
127747
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ycodium-ai/plugin-im",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2526",
|
|
4
4
|
"description": "Talk to your agents from Telegram, WeCom or Lark.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"im",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"effect": "4.0.0-beta.103",
|
|
25
25
|
"esbuild": "0.28.1",
|
|
26
26
|
"vite-plus": "0.2.2",
|
|
27
|
-
"@ycodium-ai/plugin-api": "0.2.
|
|
27
|
+
"@ycodium-ai/plugin-api": "0.2.2526"
|
|
28
28
|
},
|
|
29
29
|
"ycodium": {
|
|
30
30
|
"plugin": "./dist/index.js",
|