chattercatcher 0.1.11 → 0.1.13
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/dist/cli.js +106 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +16 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ import fs13 from "fs/promises";
|
|
|
8
8
|
// package.json
|
|
9
9
|
var package_default = {
|
|
10
10
|
name: "chattercatcher",
|
|
11
|
-
version: "0.1.
|
|
11
|
+
version: "0.1.13",
|
|
12
12
|
description: "\u672C\u5730\u4F18\u5148\u7684\u98DE\u4E66/Lark \u5BB6\u5EAD\u7FA4\u77E5\u8BC6\u5E93\u673A\u5668\u4EBA",
|
|
13
13
|
type: "module",
|
|
14
14
|
main: "dist/index.js",
|
|
@@ -88,6 +88,7 @@ var appConfigSchema = z.object({
|
|
|
88
88
|
feishu: z.object({
|
|
89
89
|
domain: z.enum(["feishu", "lark"]).default("feishu"),
|
|
90
90
|
appId: z.string().default(""),
|
|
91
|
+
botOpenId: z.string().default(""),
|
|
91
92
|
groupPolicy: z.enum(["open", "allowlist", "disabled"]).default("open"),
|
|
92
93
|
requireMention: z.boolean().default(true)
|
|
93
94
|
}),
|
|
@@ -1944,6 +1945,80 @@ async function restoreLocalData(input2) {
|
|
|
1944
1945
|
};
|
|
1945
1946
|
}
|
|
1946
1947
|
|
|
1948
|
+
// src/feishu/bot-info.ts
|
|
1949
|
+
function getOpenApiBaseUrl(domain) {
|
|
1950
|
+
return domain === "lark" ? "https://open.larksuite.com/open-apis" : "https://open.feishu.cn/open-apis";
|
|
1951
|
+
}
|
|
1952
|
+
async function readFeishuJson(response) {
|
|
1953
|
+
if (!response.ok) {
|
|
1954
|
+
throw new Error(`\u98DE\u4E66\u63A5\u53E3\u8BF7\u6C42\u5931\u8D25\uFF1AHTTP ${response.status}`);
|
|
1955
|
+
}
|
|
1956
|
+
return response.json();
|
|
1957
|
+
}
|
|
1958
|
+
function assertFeishuSuccess(payload, fallbackMessage) {
|
|
1959
|
+
if (!payload || typeof payload !== "object") {
|
|
1960
|
+
throw new Error(fallbackMessage);
|
|
1961
|
+
}
|
|
1962
|
+
const code = payload.code;
|
|
1963
|
+
if (code !== 0) {
|
|
1964
|
+
const message = payload.msg;
|
|
1965
|
+
throw new Error(typeof message === "string" ? message : fallbackMessage);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
async function resolveFeishuBotOpenId(config, secrets, options = {}) {
|
|
1969
|
+
if (!config.feishu.appId || !secrets.feishu.appSecret) {
|
|
1970
|
+
throw new Error("\u98DE\u4E66 App ID \u6216 App Secret \u672A\u914D\u7F6E\u3002");
|
|
1971
|
+
}
|
|
1972
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
1973
|
+
const baseUrl = getOpenApiBaseUrl(config.feishu.domain);
|
|
1974
|
+
const tokenPayload = await readFeishuJson(
|
|
1975
|
+
await fetchImpl(`${baseUrl}/auth/v3/tenant_access_token/internal`, {
|
|
1976
|
+
method: "POST",
|
|
1977
|
+
headers: { "content-type": "application/json" },
|
|
1978
|
+
body: JSON.stringify({
|
|
1979
|
+
app_id: config.feishu.appId,
|
|
1980
|
+
app_secret: secrets.feishu.appSecret
|
|
1981
|
+
})
|
|
1982
|
+
})
|
|
1983
|
+
);
|
|
1984
|
+
assertFeishuSuccess(tokenPayload, "\u83B7\u53D6\u98DE\u4E66 tenant_access_token \u5931\u8D25\u3002");
|
|
1985
|
+
const tenantAccessToken = tokenPayload.tenant_access_token;
|
|
1986
|
+
if (typeof tenantAccessToken !== "string" || !tenantAccessToken) {
|
|
1987
|
+
throw new Error("\u98DE\u4E66 tenant_access_token \u54CD\u5E94\u7F3A\u5C11 token\u3002");
|
|
1988
|
+
}
|
|
1989
|
+
const botInfoPayload = await readFeishuJson(
|
|
1990
|
+
await fetchImpl(`${baseUrl}/bot/v3/info`, {
|
|
1991
|
+
method: "GET",
|
|
1992
|
+
headers: { Authorization: `Bearer ${tenantAccessToken}` }
|
|
1993
|
+
})
|
|
1994
|
+
);
|
|
1995
|
+
assertFeishuSuccess(botInfoPayload, "\u83B7\u53D6\u98DE\u4E66\u673A\u5668\u4EBA\u4FE1\u606F\u5931\u8D25\u3002");
|
|
1996
|
+
const bot = botInfoPayload.bot;
|
|
1997
|
+
if (!bot || typeof bot !== "object") {
|
|
1998
|
+
throw new Error("\u98DE\u4E66\u673A\u5668\u4EBA\u4FE1\u606F\u54CD\u5E94\u7F3A\u5C11 bot\u3002");
|
|
1999
|
+
}
|
|
2000
|
+
const openId = bot.open_id;
|
|
2001
|
+
if (typeof openId !== "string" || !openId) {
|
|
2002
|
+
throw new Error("\u98DE\u4E66\u673A\u5668\u4EBA\u4FE1\u606F\u54CD\u5E94\u7F3A\u5C11 open_id\u3002");
|
|
2003
|
+
}
|
|
2004
|
+
return openId;
|
|
2005
|
+
}
|
|
2006
|
+
async function ensureFeishuBotOpenId(config, secrets, options = {}) {
|
|
2007
|
+
if (config.feishu.botOpenId) {
|
|
2008
|
+
return config.feishu.botOpenId;
|
|
2009
|
+
}
|
|
2010
|
+
const openId = await resolveFeishuBotOpenId(config, secrets, options);
|
|
2011
|
+
const previousOpenId = config.feishu.botOpenId;
|
|
2012
|
+
config.feishu.botOpenId = openId;
|
|
2013
|
+
try {
|
|
2014
|
+
await options.onSave?.();
|
|
2015
|
+
} catch (error) {
|
|
2016
|
+
config.feishu.botOpenId = previousOpenId;
|
|
2017
|
+
throw error;
|
|
2018
|
+
}
|
|
2019
|
+
return openId;
|
|
2020
|
+
}
|
|
2021
|
+
|
|
1947
2022
|
// src/feishu/gateway.ts
|
|
1948
2023
|
import * as lark2 from "@larksuiteoapi/node-sdk";
|
|
1949
2024
|
|
|
@@ -2106,12 +2181,22 @@ function stripMentions(text, mentions) {
|
|
|
2106
2181
|
}
|
|
2107
2182
|
return result.replace(/@/g, " ").replace(/\s+/g, " ").trim();
|
|
2108
2183
|
}
|
|
2109
|
-
function
|
|
2184
|
+
function isMentionForBot(mention, config) {
|
|
2185
|
+
if (!config.feishu.botOpenId) {
|
|
2186
|
+
return false;
|
|
2187
|
+
}
|
|
2188
|
+
return mention.id?.open_id === config.feishu.botOpenId;
|
|
2189
|
+
}
|
|
2190
|
+
function getBotMentions(payload, config) {
|
|
2191
|
+
const message = payload.event?.message;
|
|
2192
|
+
return (message?.mentions ?? []).filter((mention) => isMentionForBot(mention, config));
|
|
2193
|
+
}
|
|
2194
|
+
function isFeishuMessageAddressedToBot(payload, config) {
|
|
2110
2195
|
const message = payload.event?.message;
|
|
2111
2196
|
if (!message || message.message_type !== "text") {
|
|
2112
2197
|
return false;
|
|
2113
2198
|
}
|
|
2114
|
-
return (
|
|
2199
|
+
return getBotMentions(payload, config).length > 0;
|
|
2115
2200
|
}
|
|
2116
2201
|
function getFeishuQuestionDecision(payload, config) {
|
|
2117
2202
|
const message = payload.event?.message;
|
|
@@ -2121,9 +2206,9 @@ function getFeishuQuestionDecision(payload, config) {
|
|
|
2121
2206
|
reason: "\u4E0D\u662F\u53EF\u56DE\u7B54\u7684\u6587\u672C\u6D88\u606F\u3002"
|
|
2122
2207
|
};
|
|
2123
2208
|
}
|
|
2124
|
-
const mentions =
|
|
2209
|
+
const mentions = getBotMentions(payload, config);
|
|
2125
2210
|
const text = parseTextContent(message.content);
|
|
2126
|
-
const hasMention = isFeishuMessageAddressedToBot(payload);
|
|
2211
|
+
const hasMention = isFeishuMessageAddressedToBot(payload, config);
|
|
2127
2212
|
if (config.feishu.requireMention && !hasMention) {
|
|
2128
2213
|
return {
|
|
2129
2214
|
shouldAnswer: false,
|
|
@@ -2300,7 +2385,7 @@ function createFeishuEventDispatcher(options) {
|
|
|
2300
2385
|
return new lark2.EventDispatcher({}).register({
|
|
2301
2386
|
"im.message.receive_v1": async (data2) => {
|
|
2302
2387
|
const payload = { event: data2 };
|
|
2303
|
-
if (options.questionHandler && isFeishuMessageAddressedToBot(payload)) {
|
|
2388
|
+
if (options.questionHandler && isFeishuMessageAddressedToBot(payload, options.config)) {
|
|
2304
2389
|
const platformMessageId = data2?.message?.message_id;
|
|
2305
2390
|
if (platformMessageId && answeredMessageIds.has(platformMessageId)) {
|
|
2306
2391
|
console.log("\u98DE\u4E66\u63D0\u95EE\u91CD\u590D\u6295\u9012\uFF1A\u5DF2\u8DF3\u8FC7\u56DE\u7B54\u3002");
|
|
@@ -3584,6 +3669,7 @@ async function promptForConfiguration(config, secrets) {
|
|
|
3584
3669
|
secrets.feishu.appSecret,
|
|
3585
3670
|
await password({ message: secrets.feishu.appSecret ? "\u98DE\u4E66 App Secret\uFF08\u7559\u7A7A\u4FDD\u7559\uFF09" : "\u98DE\u4E66 App Secret", mask: "*" })
|
|
3586
3671
|
);
|
|
3672
|
+
await tryEnsureFeishuBotOpenId(config, secrets);
|
|
3587
3673
|
config.llm.baseUrl = await input({ message: "LLM Base URL\uFF08OpenAI-compatible\uFF09", default: config.llm.baseUrl });
|
|
3588
3674
|
secrets.llm.apiKey = applySecretInput(
|
|
3589
3675
|
secrets.llm.apiKey,
|
|
@@ -3611,10 +3697,21 @@ async function promptForConfiguration(config, secrets) {
|
|
|
3611
3697
|
config.embedding.dimension = dimension ?? null;
|
|
3612
3698
|
config.web.port = await number({ message: "Web UI \u7AEF\u53E3", default: config.web.port, required: true }) ?? config.web.port;
|
|
3613
3699
|
config.feishu.requireMention = await confirm({
|
|
3614
|
-
message: "\u7FA4\u804A\u56DE\u7B54\u662F\u5426\u8981\u6C42 @
|
|
3700
|
+
message: "\u7FA4\u804A\u56DE\u7B54\u662F\u5426\u8981\u6C42 @ \u673A\u5668\u4EBA\uFF1F",
|
|
3615
3701
|
default: config.feishu.requireMention
|
|
3616
3702
|
});
|
|
3617
3703
|
}
|
|
3704
|
+
async function tryEnsureFeishuBotOpenId(config, secrets) {
|
|
3705
|
+
if (config.feishu.botOpenId || !config.feishu.appId || !secrets.feishu.appSecret) {
|
|
3706
|
+
return;
|
|
3707
|
+
}
|
|
3708
|
+
try {
|
|
3709
|
+
const openId = await ensureFeishuBotOpenId(config, secrets, { onSave: () => saveConfig(config) });
|
|
3710
|
+
console.log(`\u5DF2\u81EA\u52A8\u83B7\u53D6\u98DE\u4E66\u673A\u5668\u4EBA Open ID\uFF1A${openId}`);
|
|
3711
|
+
} catch (error) {
|
|
3712
|
+
console.log(`\u6682\u65F6\u65E0\u6CD5\u81EA\u52A8\u83B7\u53D6\u98DE\u4E66\u673A\u5668\u4EBA Open ID\uFF1A${error instanceof Error ? error.message : String(error)}`);
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3618
3715
|
function printSettings(config, secrets) {
|
|
3619
3716
|
console.log(JSON.stringify(
|
|
3620
3717
|
{
|
|
@@ -3713,6 +3810,7 @@ async function startGatewayForegroundCommand() {
|
|
|
3713
3810
|
await startWebServer(config);
|
|
3714
3811
|
return;
|
|
3715
3812
|
}
|
|
3813
|
+
await tryEnsureFeishuBotOpenId(config, secrets);
|
|
3716
3814
|
writeGatewayPidRecord(void 0, {
|
|
3717
3815
|
...pidRecordBase,
|
|
3718
3816
|
mode: "gateway"
|
|
@@ -3767,6 +3865,7 @@ async function startGatewayCommand(options = {}) {
|
|
|
3767
3865
|
}
|
|
3768
3866
|
const config = await loadConfig();
|
|
3769
3867
|
const secrets = await loadSecrets();
|
|
3868
|
+
await tryEnsureFeishuBotOpenId(config, secrets);
|
|
3770
3869
|
const result = await startDetachedGateway({ config, secrets });
|
|
3771
3870
|
console.log(result.message);
|
|
3772
3871
|
if (result.pid) {
|