dsh-remote-web 0.7.12-beta.2 → 0.7.12-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +237 -69
- package/lib/index.js +215 -7
- package/package.json +1 -1
- package/runtime/clients/dsh-remote/dingtalk-bot.mjs +49 -6
- package/runtime/clients/dsh-remote/docs/feishu-bot-contract.md +56 -16
- package/runtime/clients/dsh-remote/dsh-bridge.mjs +6 -4
- package/runtime/clients/dsh-remote/feishu-bot.mjs +197 -13
|
@@ -132,15 +132,32 @@ export const SDK_MISSING_HINT =
|
|
|
132
132
|
/** 长连接不支持代理(官方文档)→ 连接失败时给这条。 */
|
|
133
133
|
export const PROXY_HINT = "飞书长连接不支持代理:如果公司网络必须走代理,这条通道在当前网络下连不上(可改用手机远程)。";
|
|
134
134
|
|
|
135
|
-
/**
|
|
135
|
+
/**
|
|
136
|
+
* 飞书官方文档 / 注册入口 + 配置向导(配置页直接渲染;本模块不画 UI)。
|
|
137
|
+
*
|
|
138
|
+
* 业主 2026-11-08 明确的产品口径:**接收人(open_id/chat_id)不让用户填** —— 用户本来也
|
|
139
|
+
* 不知道去哪拿,默认就是他/她本人。所以主路径只有两步(填 App ID/Secret + 发一句话自动绑定),
|
|
140
|
+
* 后台的 7 项配置折叠成「需要更细的步骤」。
|
|
141
|
+
*/
|
|
136
142
|
export const FEISHU_SETUP_GUIDE = Object.freeze({
|
|
137
143
|
docs: Object.freeze({
|
|
144
|
+
createApp: "https://open.feishu.cn/app?lang=zh-CN",
|
|
145
|
+
cardInteractiveBot: "https://open.feishu.cn/document/uAjLw4CM/ukzMukzMukzM/feishu-cards/quick-start/develop-a-card-interactive-bot",
|
|
138
146
|
longConnectionEvent: "https://open.feishu.cn/document/server-docs/event-subscription-guide/event-subscription-configure-/request-url-configuration-case",
|
|
139
147
|
longConnectionCallback: "https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/event-subscription-guide/callback-subscription/configure-callback-request-address",
|
|
140
148
|
sendMessage: "https://open.feishu.cn/document/server-docs/im-v1/message/create",
|
|
141
149
|
receiveMessage: "https://open.feishu.cn/document/server-docs/im-v1/message/events/receive",
|
|
142
150
|
registerApp: "https://open.feishu.cn/app"
|
|
143
151
|
}),
|
|
152
|
+
/** 主路径(展示在第一层,只要两件事)。 */
|
|
153
|
+
quickPath: Object.freeze([
|
|
154
|
+
"在飞书开放平台创建一个企业自建应用,复制 **App ID / App Secret**(只填这两样,接收人不用填)",
|
|
155
|
+
"填进本面板 → 下载依赖 → 在飞书里给机器人**发一句话**:接收人自动绑定,之后通知/拍板都发给你"
|
|
156
|
+
]),
|
|
157
|
+
quickPathHint: "应用能力/权限/事件与回调/发版这些后台设置仍需做一次(点开「需要更细的步骤」照着做)。",
|
|
158
|
+
detailsLabel: "需要更细的步骤",
|
|
159
|
+
/** 接收人自动绑定的口径(面板可展示成一句说明)。 */
|
|
160
|
+
bindingHint: "接收人不需要填:机器人收到你的第一条消息后自动记住这个会话;在群里 @ 它则记住那个群。",
|
|
144
161
|
steps: Object.freeze([
|
|
145
162
|
"打开飞书开放平台(open.feishu.cn/app)→ 创建**企业自建应用**(商店应用不支持长连接)",
|
|
146
163
|
"「凭证与基础信息」→ 复制 **App ID / App Secret**(App ID 形如 cli_ 开头的一串字符)",
|
|
@@ -149,7 +166,7 @@ export const FEISHU_SETUP_GUIDE = Object.freeze({
|
|
|
149
166
|
"「事件与回调 → 事件配置」→ 订阅方式选**使用长连接接收事件** → 添加「接收消息 v2.0」",
|
|
150
167
|
"「事件与回调 → 回调配置」→ 订阅方式选**使用长连接接收回调** → 添加 card.action.trigger(拍板卡片用)",
|
|
151
168
|
"「版本管理与发布」→ 创建版本并**发布**(不发布不生效);「可用范围」包含你自己/目标成员",
|
|
152
|
-
"回到本面板:填 App ID / Secret → 「下载依赖」→「测试连接」→
|
|
169
|
+
"回到本面板:填 App ID / Secret → 「下载依赖」→「测试连接」→ 在飞书里给机器人**发一句话**(接收人自动绑定,无需填写)"
|
|
153
170
|
]),
|
|
154
171
|
// 最容易被忽略的顺序坑:保存事件配置时,必须已经有一个长连接在线。
|
|
155
172
|
orderWarning: "顺序很重要:先在本机填好凭据让长连接在线,再去开发者后台保存「事件/回调」配置(否则保存会提示「应用未建立长连接」)。",
|
|
@@ -731,6 +748,10 @@ export function createFeishuBot({
|
|
|
731
748
|
receiveIdType = "",
|
|
732
749
|
allowFrom = [],
|
|
733
750
|
cardApproval = true,
|
|
751
|
+
/** 配置里预填的绑定点(可选;兼容旧配置/手工排障)。正常流程由 chat.list 或首条消息自动绑定。 */
|
|
752
|
+
boundChatId = "",
|
|
753
|
+
/** 连接成功后用 chat.list 自动选群:每页条数。 */
|
|
754
|
+
chatListPageSize = 20,
|
|
734
755
|
relayDir = "",
|
|
735
756
|
env = process.env,
|
|
736
757
|
logger,
|
|
@@ -755,6 +776,7 @@ export function createFeishuBot({
|
|
|
755
776
|
const seenCards = new Map();
|
|
756
777
|
let sender = null;
|
|
757
778
|
let wsClient = null;
|
|
779
|
+
let sdkRef = null; // 懒加载后的 SDK(chat.list 自动选群用)
|
|
758
780
|
let dispatcherHandlers = null;
|
|
759
781
|
let pollTimer = null;
|
|
760
782
|
let stopped = false;
|
|
@@ -768,6 +790,10 @@ export function createFeishuBot({
|
|
|
768
790
|
lastConnectedAt: 0,
|
|
769
791
|
lastMessageAt: 0,
|
|
770
792
|
boundChatId: "",
|
|
793
|
+
boundChatType: "", // "chat_id"(群,来自 chat.list/群消息)| "open_id"(单聊,来自消息)
|
|
794
|
+
boundChatName: "", // 群名(面板展示"已自动绑定:<群名或私聊>")
|
|
795
|
+
boundSource: "", // "auto_group"(chat.list 自动选群)| "auto"(首条消息/预填)
|
|
796
|
+
chatCount: null, // 机器人所在群数(chat.list 结果;null=没查到/没权限)
|
|
771
797
|
sdkVersion: "",
|
|
772
798
|
ackCount: 0,
|
|
773
799
|
decisionCount: 0
|
|
@@ -800,6 +826,7 @@ export function createFeishuBot({
|
|
|
800
826
|
state.lastConnectedAt = Number(clock()) || Date.now();
|
|
801
827
|
log.info("长连接已就绪");
|
|
802
828
|
try { emitter.emit("ready", status()); } catch { /* 忽略 */ }
|
|
829
|
+
void autoBindFromChatList();
|
|
803
830
|
} else if (!connected) {
|
|
804
831
|
state.connected = false;
|
|
805
832
|
state.authenticated = false;
|
|
@@ -830,6 +857,7 @@ export function createFeishuBot({
|
|
|
830
857
|
state.lastConnectedAt = Number(clock()) || Date.now();
|
|
831
858
|
try { emitter.emit("ready", status()); } catch { /* 忽略 */ }
|
|
832
859
|
emitStatus();
|
|
860
|
+
void autoBindFromChatList();
|
|
833
861
|
}
|
|
834
862
|
}
|
|
835
863
|
|
|
@@ -910,7 +938,17 @@ export function createFeishuBot({
|
|
|
910
938
|
void sender?.sendText?.(inbound.chatId, "你不在这个通道的白名单里,无法指挥这台电脑。", { receiveIdType: "chat_id" });
|
|
911
939
|
return;
|
|
912
940
|
}
|
|
913
|
-
|
|
941
|
+
/*
|
|
942
|
+
* 首次说话即绑定(与企微/钉钉一致):接收人不让用户填。
|
|
943
|
+
* · 群聊 → 记 chat_id(后续按 chat_id 推)
|
|
944
|
+
* · 单聊 → 记 sender.open_id(后续按 open_id 推,用户换群也不会发错地方)
|
|
945
|
+
* 绑定信息只放内存 + 状态文件(桥写),配置里不需要 receiveId。
|
|
946
|
+
*/
|
|
947
|
+
const isGroup = inbound.chatType === "group";
|
|
948
|
+
state.boundChatId = isGroup ? inbound.chatId : (inbound.openId || inbound.chatId);
|
|
949
|
+
state.boundChatType = isGroup ? "chat_id" : "open_id"; // 直接就是后续发送用的 receive_id_type
|
|
950
|
+
state.boundChatName = ""; // 消息里没有群名;面板显示「私聊/群」即可
|
|
951
|
+
state.boundSource = "auto"; // 用户发消息即改绑(覆盖 chat.list 的 auto_group)
|
|
914
952
|
state.lastMessageAt = Number(clock()) || Date.now();
|
|
915
953
|
emitStatus();
|
|
916
954
|
try { emitter.emit("message", inbound); } catch (e) { log.warn(`message 处理异常:${truncateText(e?.message || e, 100)}`); }
|
|
@@ -934,6 +972,133 @@ export function createFeishuBot({
|
|
|
934
972
|
return { dispatcher, handlers };
|
|
935
973
|
}
|
|
936
974
|
|
|
975
|
+
/**
|
|
976
|
+
* 接收人来源:
|
|
977
|
+
* · config —— 用户在高级场景里手工填了 receiveId(优先)
|
|
978
|
+
* · auto_group —— 连接成功后 chat.list 自动选中了机器人所在的群
|
|
979
|
+
* · auto —— 首条消息自动绑定(私聊 open_id / 群 chat_id),或配置预填
|
|
980
|
+
* · none —— 还没绑定(通知会跳过并记日志,不报错)
|
|
981
|
+
*/
|
|
982
|
+
function receiveIdSource() {
|
|
983
|
+
if (String(receiveId || "").trim()) return "config";
|
|
984
|
+
if (!state.boundChatId) return "none";
|
|
985
|
+
return state.boundSource === "auto_group" ? "auto_group" : "auto";
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
/** 自动绑定后的推送类型(直接就是 receive_id_type)。 */
|
|
989
|
+
function boundTargetType() {
|
|
990
|
+
if (state.boundChatType === "chat_id") return "chat_id";
|
|
991
|
+
if (state.boundChatType === "open_id") return "open_id";
|
|
992
|
+
return "";
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* **连接成功后自动选群**(业主/Lead 实测:`client.im.v1.chat.list()` 能列出机器人所在群):
|
|
997
|
+
* · 有群 → 记为接收人(多个群取第一个,用户发消息会改绑)→ receiveIdSource=auto_group
|
|
998
|
+
* · 空 → 不绑定,等首条消息
|
|
999
|
+
* · 无权限/失败 → 只记日志,**不影响连接与"发消息绑定"**
|
|
1000
|
+
* 配置里填了 receiveId 时只记录群数,不覆盖(配置优先)。
|
|
1001
|
+
*/
|
|
1002
|
+
async function autoBindFromChatList() {
|
|
1003
|
+
if (autoBindTried) return;
|
|
1004
|
+
autoBindTried = true;
|
|
1005
|
+
const cfgTo = String(receiveId || "").trim();
|
|
1006
|
+
let client = null;
|
|
1007
|
+
try {
|
|
1008
|
+
if (!sdkRef || typeof sdkRef.Client !== "function") {
|
|
1009
|
+
log.info("SDK 未提供 Client(跳过自动选群,等首条消息绑定)");
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
const opts = { appId, appSecret };
|
|
1013
|
+
if (normalizeDomain(domain) === "lark" && sdkRef?.Domain?.Lark) opts.domain = sdkRef.Domain.Lark;
|
|
1014
|
+
client = new sdkRef.Client(opts);
|
|
1015
|
+
} catch (e) {
|
|
1016
|
+
log.warn(`创建 Client 失败(跳过自动选群,不影响消息绑定):${truncateText(e?.message || e, 120)}`);
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
const listFn = client?.im?.v1?.chat?.list;
|
|
1020
|
+
if (typeof listFn !== "function") {
|
|
1021
|
+
log.info("SDK 没有 chat.list(跳过自动选群,等首条消息绑定)");
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
let resp = null;
|
|
1025
|
+
try {
|
|
1026
|
+
// 官方 1.76 的调用形态是 { params: {…} };兼容直接传参数 / 无参(Lead 实测无参也能列出群)
|
|
1027
|
+
try {
|
|
1028
|
+
resp = await listFn.call(client.im.v1.chat, { params: { page_size: chatListPageSize } });
|
|
1029
|
+
} catch (e1) {
|
|
1030
|
+
try {
|
|
1031
|
+
resp = await listFn.call(client.im.v1.chat, { page_size: chatListPageSize });
|
|
1032
|
+
} catch (e2) {
|
|
1033
|
+
resp = await listFn.call(client.im.v1.chat);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
} catch (e) {
|
|
1037
|
+
// 常见:应用没申请 im:chat:readonly → 只记日志(不影响连接/发消息/消息绑定)
|
|
1038
|
+
log.warn(`chat.list 失败(忽略,不影响消息绑定;如需自动选群请加 im:chat:readonly):${truncateText(e?.message || e, 120)}`);
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
const code = Number(resp?.code ?? resp?.data?.code);
|
|
1042
|
+
if (Number.isFinite(code) && code !== 0) {
|
|
1043
|
+
log.warn(`chat.list 返回错误码 ${code}(忽略;如需自动选群请加 im:chat:readonly);${feishuErrorHint(code, resp?.msg || "")}`);
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
const items = Array.isArray(resp?.data?.items) ? resp.data.items : (Array.isArray(resp?.items) ? resp.items : []);
|
|
1047
|
+
state.chatCount = items.length;
|
|
1048
|
+
// 显式指定(receiveId)或配置预填 boundChatId 都算"用户已经定了目标"→ 只记录群数,不覆盖
|
|
1049
|
+
const prefilled = String(boundChatId || "").trim();
|
|
1050
|
+
if (cfgTo || prefilled) {
|
|
1051
|
+
// 不覆盖用户指定的目标,但尽量补上群名(面板显示「已自动绑定:<群名>」)
|
|
1052
|
+
const wanted = cfgTo || prefilled;
|
|
1053
|
+
const hit = items.find((it) => String(it?.chat_id || it?.chatId || "") === wanted);
|
|
1054
|
+
if (hit && !state.boundChatName) state.boundChatName = String(hit.name || "");
|
|
1055
|
+
log.info(`接收人已指定(${cfgTo ? "receiveId" : "配置预填 boundChatId"},优先),chat.list 仅记录机器人所在群数=${items.length}`);
|
|
1056
|
+
emitStatus();
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
if (!items.length) {
|
|
1060
|
+
log.info("机器人还没进任何群:等用户私聊或拉群后自动绑定");
|
|
1061
|
+
emitStatus();
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
const first = items[0] || {};
|
|
1065
|
+
const chatId = String(first.chat_id || first.chatId || "");
|
|
1066
|
+
if (!chatId) {
|
|
1067
|
+
emitStatus();
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
state.boundChatId = chatId;
|
|
1071
|
+
state.boundChatType = "chat_id";
|
|
1072
|
+
state.boundChatName = String(first.name || "");
|
|
1073
|
+
state.boundSource = "auto_group";
|
|
1074
|
+
log.info(`已自动绑定到群 chat=${chatIdHash(chatId)} 群数=${items.length}${items.length > 1 ? "(多个群取第一个;用户发消息会改绑)" : ""}`);
|
|
1075
|
+
emitStatus();
|
|
1076
|
+
}
|
|
1077
|
+
let autoBindTried = false;
|
|
1078
|
+
|
|
1079
|
+
/**
|
|
1080
|
+
* 解析一次发送的 target 与 receive_id_type(产品口径:接收人默认自动绑定)。
|
|
1081
|
+
*
|
|
1082
|
+
* 优先级(任务书第 3 条):
|
|
1083
|
+
* ① 调用方显式传的 receiveIdType(内部回复用,最明确)
|
|
1084
|
+
* ② 配置里手工填的 receiveIdType(高级场景)
|
|
1085
|
+
* ③ 按 boundChatType 自动判断(群→chat_id / 单聊→open_id)
|
|
1086
|
+
* ④ 兜底 open_id(老行为)
|
|
1087
|
+
* target 优先级:显式 to > 配置 receiveId > 自动绑定的 boundChatId
|
|
1088
|
+
*/
|
|
1089
|
+
function resolveSendTarget(explicitTo, explicitType) {
|
|
1090
|
+
const cfgTo = String(receiveId || "").trim();
|
|
1091
|
+
const to = String(explicitTo || "").trim() || cfgTo || state.boundChatId || "";
|
|
1092
|
+
let type = normalizeReceiveIdType(explicitType || "", "");
|
|
1093
|
+
if (!type && cfgTo) {
|
|
1094
|
+
// 只有"用户显式填了 receiveId"时,receiveIdType 才有意义(插件的默认 open_id 不能套到群上)
|
|
1095
|
+
type = normalizeReceiveIdType(receiveIdType, "") || DEFAULT_RECEIVE_ID_TYPE;
|
|
1096
|
+
}
|
|
1097
|
+
// 显式 to(内部回复,未传 type)沿用老行为 open_id;自动绑定则按 boundChatType 判断
|
|
1098
|
+
if (!type) type = explicitTo ? DEFAULT_RECEIVE_ID_TYPE : (boundTargetType() || DEFAULT_RECEIVE_ID_TYPE);
|
|
1099
|
+
return { to, type };
|
|
1100
|
+
}
|
|
1101
|
+
|
|
937
1102
|
function status() {
|
|
938
1103
|
return {
|
|
939
1104
|
enabled: started && !stopped,
|
|
@@ -944,6 +1109,12 @@ export function createFeishuBot({
|
|
|
944
1109
|
lastConnectedAt: state.lastConnectedAt,
|
|
945
1110
|
lastMessageAt: state.lastMessageAt,
|
|
946
1111
|
boundChatId: state.boundChatId,
|
|
1112
|
+
boundChatType: state.boundChatType,
|
|
1113
|
+
boundChatName: state.boundChatName,
|
|
1114
|
+
chatCount: state.chatCount,
|
|
1115
|
+
// 接收人来源:config / auto_group(chat.list 自动选群)/ auto(首条消息)/ none
|
|
1116
|
+
receiveIdSource: receiveIdSource(),
|
|
1117
|
+
receiveIdConfigured: Boolean(String(receiveId || "").trim()),
|
|
947
1118
|
appId: maskAppId(appId),
|
|
948
1119
|
domain: normalizeDomain(domain),
|
|
949
1120
|
receiveIdType: normalizeReceiveIdType(receiveIdType, ""),
|
|
@@ -984,7 +1155,15 @@ export function createFeishuBot({
|
|
|
984
1155
|
return { ok: false, reason: "sdk_missing", status: status() };
|
|
985
1156
|
}
|
|
986
1157
|
const sdk = loaded.sdk || {};
|
|
1158
|
+
sdkRef = sdk;
|
|
987
1159
|
state.sdkVersion = String(loaded.status?.version || "");
|
|
1160
|
+
// 配置里预填的绑定点(可选):oc_ 开头视为群 chat_id,其余视为 open_id
|
|
1161
|
+
const seeded = String(boundChatId || "").trim();
|
|
1162
|
+
if (seeded && !state.boundChatId) {
|
|
1163
|
+
state.boundChatId = seeded;
|
|
1164
|
+
state.boundChatType = /^oc_/i.test(seeded) ? "chat_id" : "open_id";
|
|
1165
|
+
state.boundSource = "auto";
|
|
1166
|
+
}
|
|
988
1167
|
|
|
989
1168
|
// ③ 发送器(REST)
|
|
990
1169
|
sender = createFeishuSender({
|
|
@@ -1041,13 +1220,12 @@ export function createFeishuBot({
|
|
|
1041
1220
|
return { ok: true };
|
|
1042
1221
|
},
|
|
1043
1222
|
|
|
1044
|
-
/**
|
|
1223
|
+
/** 发文本(自动按飞书通道格式化;接收人默认自动绑定)。 */
|
|
1045
1224
|
async sendText(to, text, opts = {}) {
|
|
1046
|
-
const target = String(to || receiveId || state.boundChatId || "");
|
|
1047
1225
|
if (!sender) return { ok: false, reason: "发送器未初始化(连接尚未建立)" };
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1050
|
-
});
|
|
1226
|
+
const { to: target, type } = resolveSendTarget(to, opts.receiveIdType);
|
|
1227
|
+
if (!target) return { ok: false, reason: "还没绑定接收人:先在飞书里给机器人发一句话" };
|
|
1228
|
+
const r = await sender.sendText(target, text, { receiveIdType: type });
|
|
1051
1229
|
if (r.ok) {
|
|
1052
1230
|
state.lastMessageAt = Number(clock()) || Date.now();
|
|
1053
1231
|
emitStatus();
|
|
@@ -1057,11 +1235,10 @@ export function createFeishuBot({
|
|
|
1057
1235
|
|
|
1058
1236
|
/** 发审批卡片(返回值带 messageId,供回执后原地更新)。 */
|
|
1059
1237
|
async sendCard(to, card, opts = {}) {
|
|
1060
|
-
const target = String(to || receiveId || state.boundChatId || "");
|
|
1061
1238
|
if (!sender) return { ok: false, reason: "发送器未初始化(连接尚未建立)" };
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
});
|
|
1239
|
+
const { to: target, type } = resolveSendTarget(to, opts.receiveIdType);
|
|
1240
|
+
if (!target) return { ok: false, reason: "还没绑定接收人:先在飞书里给机器人发一句话" };
|
|
1241
|
+
return sender.sendCard(target, card, { receiveIdType: type });
|
|
1065
1242
|
},
|
|
1066
1243
|
|
|
1067
1244
|
/** 更新卡片(best-effort)。 */
|
|
@@ -1254,6 +1431,12 @@ export function createFeishuBotController(options = {}) {
|
|
|
1254
1431
|
lastConnectedAt: bs?.lastConnectedAt || 0,
|
|
1255
1432
|
lastMessageAt: bs?.lastMessageAt || 0,
|
|
1256
1433
|
boundChatId: (bs && bs.boundChatId) || "",
|
|
1434
|
+
boundChatType: (bs && bs.boundChatType) || "",
|
|
1435
|
+
boundChatName: (bs && bs.boundChatName) || "",
|
|
1436
|
+
chatCount: Number.isInteger(bs?.chatCount) ? bs.chatCount : null,
|
|
1437
|
+
// 面板据此显示「接收人:自动绑定(已/未)/ 手动配置」;receiveId 用户不用填
|
|
1438
|
+
receiveIdSource: (bs && bs.receiveIdSource) || (configNow?.receiveId ? "config" : "none"),
|
|
1439
|
+
receiveIdConfigured: Boolean(configNow?.receiveId),
|
|
1257
1440
|
appId: bs?.appId || maskAppId(""),
|
|
1258
1441
|
domain: bs?.domain || "feishu",
|
|
1259
1442
|
receiveIdType: bs?.receiveIdType || "",
|
|
@@ -1342,7 +1525,7 @@ export function createFeishuBotController(options = {}) {
|
|
|
1342
1525
|
if (!notifyEnabled || !bot) return;
|
|
1343
1526
|
const target = targetOf();
|
|
1344
1527
|
if (!target) {
|
|
1345
|
-
log.info("
|
|
1528
|
+
log.info("有事件要推,但还没绑定接收方:不用填接收人 —— 在飞书里给机器人发一句话,就会自动绑定到你这个会话");
|
|
1346
1529
|
return;
|
|
1347
1530
|
}
|
|
1348
1531
|
if (!notifyRateOk()) {
|
|
@@ -1570,6 +1753,7 @@ export function createFeishuBotController(options = {}) {
|
|
|
1570
1753
|
receiveId: cfg.receiveId,
|
|
1571
1754
|
receiveIdType: cfg.receiveIdType,
|
|
1572
1755
|
allowFrom: cfg.allowFrom,
|
|
1756
|
+
boundChatId: cfg.boundChatId,
|
|
1573
1757
|
cardApproval: cardApprovalAllowed && cfg.cardApproval,
|
|
1574
1758
|
relayDir,
|
|
1575
1759
|
env,
|