@excitedjs/feishu-transport 0.0.1
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 +39 -0
- package/dist/contract/access-store.d.ts +23 -0
- package/dist/contract/access-store.d.ts.map +1 -0
- package/dist/contract/access-store.js +16 -0
- package/dist/contract/access-store.js.map +1 -0
- package/dist/contract/outbound.d.ts +39 -0
- package/dist/contract/outbound.d.ts.map +1 -0
- package/dist/contract/outbound.js +16 -0
- package/dist/contract/outbound.js.map +1 -0
- package/dist/contract/types.d.ts +86 -0
- package/dist/contract/types.d.ts.map +1 -0
- package/dist/contract/types.js +10 -0
- package/dist/contract/types.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +10 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +14 -0
- package/dist/json.js.map +1 -0
- package/dist/parse/comment.d.ts +41 -0
- package/dist/parse/comment.d.ts.map +1 -0
- package/dist/parse/comment.js +51 -0
- package/dist/parse/comment.js.map +1 -0
- package/dist/parse/content.d.ts +42 -0
- package/dist/parse/content.d.ts.map +1 -0
- package/dist/parse/content.js +208 -0
- package/dist/parse/content.js.map +1 -0
- package/dist/policy/gate.d.ts +105 -0
- package/dist/policy/gate.d.ts.map +1 -0
- package/dist/policy/gate.js +276 -0
- package/dist/policy/gate.js.map +1 -0
- package/dist/policy/pairing.d.ts +12 -0
- package/dist/policy/pairing.d.ts.map +1 -0
- package/dist/policy/pairing.js +15 -0
- package/dist/policy/pairing.js.map +1 -0
- package/dist/render/render.d.ts +186 -0
- package/dist/render/render.d.ts.map +1 -0
- package/dist/render/render.js +630 -0
- package/dist/render/render.js.map +1 -0
- package/dist/transport/connection.d.ts +25 -0
- package/dist/transport/connection.d.ts.map +1 -0
- package/dist/transport/connection.js +42 -0
- package/dist/transport/connection.js.map +1 -0
- package/dist/transport/feishu.d.ts +222 -0
- package/dist/transport/feishu.d.ts.map +1 -0
- package/dist/transport/feishu.js +431 -0
- package/dist/transport/feishu.js.map +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Feishu platform boundary — the only module in the workspace that imports
|
|
3
|
+
* the Feishu SDK.
|
|
4
|
+
*
|
|
5
|
+
* Everything that talks to Feishu — the inbound long-lived WebSocket and the
|
|
6
|
+
* outbound message API — sits behind the `FeishuTransport` interface. A host
|
|
7
|
+
* depends only on that interface, so its wiring can be exercised against an
|
|
8
|
+
* injected fake with no live connection.
|
|
9
|
+
*
|
|
10
|
+
* The transport is event-type agnostic. `start` is handed a route table mapping
|
|
11
|
+
* each Feishu event_type to a callback and registers every entry with the SDK's
|
|
12
|
+
* event dispatcher; decoding a specific event's payload is the job of that
|
|
13
|
+
* event's handler (see `../parse`), not this module. Adding a new event type to
|
|
14
|
+
* a host therefore never touches this file.
|
|
15
|
+
*
|
|
16
|
+
* Cross-process single-instance election is intentionally **not** here. dreamux
|
|
17
|
+
* gives each dispatcher its own bot identity (election is moot), and claudemux
|
|
18
|
+
* wraps this transport with its own elected-transport layer. Core opens the
|
|
19
|
+
* inbound WebSocket directly; whether exactly one process may do so is the
|
|
20
|
+
* host's concern, layered on top. (claudemux#155 §二 / dreamux#25 §7.2.)
|
|
21
|
+
*
|
|
22
|
+
* Ported from claudemux's `feishu-channel/src/feishu.ts` (the source of truth),
|
|
23
|
+
* with the instance-lock removed, `sendText` upgraded to the structured
|
|
24
|
+
* `send(OutboundTarget, …)` contract, and `botOpenId` renamed to `selfId`.
|
|
25
|
+
*/
|
|
26
|
+
import * as lark from '@larksuiteoapi/node-sdk';
|
|
27
|
+
import { cardToContent, renderMarkdownToCards, FEISHU_CARD_REQUEST_LIMIT_BYTES, } from '../render/render.js';
|
|
28
|
+
import { connectionErrorLogLine, reconnectedLogLine, reconnectingLogLine, startupTimeoutLogLine, } from './connection.js';
|
|
29
|
+
/** Cap on a single WebSocket handshake before it is aborted into a retry. */
|
|
30
|
+
const WS_HANDSHAKE_TIMEOUT_MS = 15_000;
|
|
31
|
+
/**
|
|
32
|
+
* How long the initial connection is given to come up before the channel
|
|
33
|
+
* stops it. Long enough to absorb a brief blip and the SDK's own early
|
|
34
|
+
* retries; past it, an unreachable Feishu would otherwise retry in a tight
|
|
35
|
+
* loop, so the transport cuts the attempt off.
|
|
36
|
+
*/
|
|
37
|
+
const WS_STARTUP_GRACE_MS = 30_000;
|
|
38
|
+
/**
|
|
39
|
+
* A Lark-SDK logger that writes every line to stderr.
|
|
40
|
+
*
|
|
41
|
+
* Hosts that run over an MCP stdio transport reserve stdout for the JSON-RPC
|
|
42
|
+
* stream; the SDK's default logger writes to stdout, which corrupts it. Routing
|
|
43
|
+
* the SDK's logger to stderr keeps stdout clean while the SDK's diagnostics
|
|
44
|
+
* stay visible in the host's log. (Harmless for dreamux, which does not use
|
|
45
|
+
* stdout for a protocol stream.)
|
|
46
|
+
*/
|
|
47
|
+
const sdkLogger = {
|
|
48
|
+
error: (...msg) => console.error('[feishu-sdk]', ...msg),
|
|
49
|
+
warn: (...msg) => console.error('[feishu-sdk]', ...msg),
|
|
50
|
+
info: (...msg) => console.error('[feishu-sdk]', ...msg),
|
|
51
|
+
debug: (...msg) => console.error('[feishu-sdk]', ...msg),
|
|
52
|
+
trace: (...msg) => console.error('[feishu-sdk]', ...msg),
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Build the `content` string for a Feishu plain-text message — the legacy
|
|
56
|
+
* `msg_type: 'text'` payload, used by `editText`'s fallback path so an edit
|
|
57
|
+
* on a message that was sent before this channel switched to interactive
|
|
58
|
+
* cards still works.
|
|
59
|
+
*/
|
|
60
|
+
export function textMessageContent(text) {
|
|
61
|
+
return JSON.stringify({ text });
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Safe ceiling for the serialised card `content` string. Stays a few hundred
|
|
65
|
+
* bytes below the documented 30 KB request-body limit so HTTP headers and the
|
|
66
|
+
* `{ params, data: { receive_id, msg_type, content } }` envelope still fit.
|
|
67
|
+
*/
|
|
68
|
+
export const FEISHU_CARD_CONTENT_SAFE_BYTES = 28 * 1024;
|
|
69
|
+
/**
|
|
70
|
+
* Throw a clear, model-actionable error when a single card's content would
|
|
71
|
+
* exceed Feishu's request-body limit, before the SDK round-trips and returns a
|
|
72
|
+
* low-level Feishu code with no fix path. The renderer normally keeps each card
|
|
73
|
+
* under the cap by splitting at element boundaries; this guards the residual
|
|
74
|
+
* case where a card cannot be split smaller (an un-splittable oversized token).
|
|
75
|
+
* Used by both `send` (preserving dreamux's prior per-card guard) and `editText`
|
|
76
|
+
* (which additionally cannot fan out a multi-card body).
|
|
77
|
+
*/
|
|
78
|
+
function assertCardContentFits(content) {
|
|
79
|
+
const bytes = Buffer.byteLength(content, 'utf8');
|
|
80
|
+
if (bytes > FEISHU_CARD_CONTENT_SAFE_BYTES) {
|
|
81
|
+
throw new Error(`card content is ${bytes} bytes; Feishu rejects a card-message body over ${FEISHU_CARD_REQUEST_LIMIT_BYTES} bytes. ` +
|
|
82
|
+
'Shorten the message, or break up an oversized table or code block the renderer could not split smaller.');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Render `text` as a single v2 card, throwing when the body exceeds what
|
|
87
|
+
* one card can hold. Used by `editText` — an edit patches one message_id
|
|
88
|
+
* in place and cannot fan out, so a multi-card body has no destination.
|
|
89
|
+
*/
|
|
90
|
+
function renderSingleCard(text) {
|
|
91
|
+
const cards = renderMarkdownToCards(text);
|
|
92
|
+
if (cards.length !== 1) {
|
|
93
|
+
throw new Error(`edit body produced ${cards.length} cards, but an edit can only update one ` +
|
|
94
|
+
'card in place. Reduce the body length, drop oversized tables, or send a ' +
|
|
95
|
+
'fresh reply (which the channel splits automatically) instead of editing.');
|
|
96
|
+
}
|
|
97
|
+
// The renderer always returns a non-empty array, but TypeScript can't
|
|
98
|
+
// narrow that — pull the element out with the assertion that we just
|
|
99
|
+
// verified there is exactly one.
|
|
100
|
+
return cards[0];
|
|
101
|
+
}
|
|
102
|
+
/** Document types the drive file-comment API serves; others have no comment API. */
|
|
103
|
+
const COMMENT_FILE_TYPES = ['doc', 'docx', 'sheet', 'file'];
|
|
104
|
+
/** Narrow an event's file_type to one the file-comment API accepts, or `undefined`. */
|
|
105
|
+
function asCommentFileType(fileType) {
|
|
106
|
+
return COMMENT_FILE_TYPES.includes(fileType)
|
|
107
|
+
? fileType
|
|
108
|
+
: undefined;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Pick the comment with `commentId` out of a `fileComment.batchQuery` response
|
|
112
|
+
* and shape it into a `FeishuDocComment`. Returns `null` when the response
|
|
113
|
+
* carried no such comment. Pure: no I/O, never throws — exported so the decode
|
|
114
|
+
* is unit-tested without a live Feishu connection.
|
|
115
|
+
*/
|
|
116
|
+
export function commentFromBatchQuery(items, commentId) {
|
|
117
|
+
const item = items.find((c) => c.comment_id === commentId);
|
|
118
|
+
if (!item)
|
|
119
|
+
return null;
|
|
120
|
+
const replies = (item.reply_list?.replies ?? []).map((reply) => ({
|
|
121
|
+
replyId: reply.reply_id ?? '',
|
|
122
|
+
authorId: reply.user_id ?? '',
|
|
123
|
+
elements: reply.content?.elements ?? [],
|
|
124
|
+
}));
|
|
125
|
+
return { isWhole: item.is_whole ?? true, quote: item.quote ?? '', replies };
|
|
126
|
+
}
|
|
127
|
+
/** Document types the drive metadata API serves. */
|
|
128
|
+
const META_DOC_TYPES = [
|
|
129
|
+
'doc',
|
|
130
|
+
'docx',
|
|
131
|
+
'sheet',
|
|
132
|
+
'bitable',
|
|
133
|
+
'mindnote',
|
|
134
|
+
'file',
|
|
135
|
+
'wiki',
|
|
136
|
+
'folder',
|
|
137
|
+
'synced_block',
|
|
138
|
+
'slides',
|
|
139
|
+
];
|
|
140
|
+
/** Narrow an event's file_type to one the metadata API accepts, or `undefined`. */
|
|
141
|
+
function asMetaDocType(fileType) {
|
|
142
|
+
return META_DOC_TYPES.includes(fileType)
|
|
143
|
+
? fileType
|
|
144
|
+
: undefined;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The real Feishu transport, wrapping the official SDK.
|
|
148
|
+
*
|
|
149
|
+
* Inbound: a `WSClient` opens a long-lived WebSocket and an `EventDispatcher`
|
|
150
|
+
* routes every subscribed event_type to its callback. Outbound: a `Client`
|
|
151
|
+
* calls the `im` message API; it manages the `tenant_access_token` internally.
|
|
152
|
+
* The outbound paths are unit-tested through the `client` seam in
|
|
153
|
+
* `FeishuTransportOptions`; inbound still needs a live Feishu connection.
|
|
154
|
+
*/
|
|
155
|
+
export function createFeishuTransport(creds, options = {}) {
|
|
156
|
+
const client = options.client ??
|
|
157
|
+
new lark.Client({
|
|
158
|
+
appId: creds.appId,
|
|
159
|
+
appSecret: creds.appSecret,
|
|
160
|
+
logger: sdkLogger,
|
|
161
|
+
});
|
|
162
|
+
let wsClient;
|
|
163
|
+
let resolvedSelfId;
|
|
164
|
+
/**
|
|
165
|
+
* Open the inbound WebSocket and dispatch events through `routes`. Core opens
|
|
166
|
+
* the connection directly; a host that needs single-instance election wraps
|
|
167
|
+
* this transport rather than threading a lock through here.
|
|
168
|
+
*/
|
|
169
|
+
async function openInbound(routes) {
|
|
170
|
+
resolvedSelfId = await resolveBotOpenId(client);
|
|
171
|
+
const dispatcher = new lark.EventDispatcher({ logger: sdkLogger }).register(routes);
|
|
172
|
+
// Resolves the first time the connection reaches `ready`; the startup
|
|
173
|
+
// watchdog below races against it.
|
|
174
|
+
let markReady = () => { };
|
|
175
|
+
const ready = new Promise((resolve) => {
|
|
176
|
+
markReady = resolve;
|
|
177
|
+
});
|
|
178
|
+
const ws = new lark.WSClient({
|
|
179
|
+
appId: creds.appId,
|
|
180
|
+
appSecret: creds.appSecret,
|
|
181
|
+
// Route the SDK's own logging to stderr — see `sdkLogger`.
|
|
182
|
+
logger: sdkLogger,
|
|
183
|
+
// Bound a stuck WebSocket handshake so it fails into a retry rather
|
|
184
|
+
// than holding a stuck DNS / NAT path open indefinitely.
|
|
185
|
+
handshakeTimeoutMs: WS_HANDSHAKE_TIMEOUT_MS,
|
|
186
|
+
// autoReconnect stays on: an established connection that drops should
|
|
187
|
+
// self-heal. The callbacks make every step of that loop visible, so a
|
|
188
|
+
// failing connection is observable instead of a silent retry loop.
|
|
189
|
+
autoReconnect: true,
|
|
190
|
+
onReady: () => {
|
|
191
|
+
logConnection('Feishu WebSocket connection is ready');
|
|
192
|
+
markReady();
|
|
193
|
+
},
|
|
194
|
+
onReconnecting: () => logConnection(reconnectingLogLine()),
|
|
195
|
+
onReconnected: () => logConnection(reconnectedLogLine()),
|
|
196
|
+
onError: (err) => logConnection(connectionErrorLogLine(err)),
|
|
197
|
+
});
|
|
198
|
+
wsClient = ws;
|
|
199
|
+
void ws.start({ eventDispatcher: dispatcher }).catch((err) => {
|
|
200
|
+
logConnection(connectionErrorLogLine(err));
|
|
201
|
+
});
|
|
202
|
+
// The SDK retries pullConnectConfig with no delay until it first
|
|
203
|
+
// succeeds — it has no server-provided reconnect interval yet — so a
|
|
204
|
+
// Feishu that is unreachable at startup spins a tight retry loop.
|
|
205
|
+
// Give the initial connection a grace window; if it is still not up,
|
|
206
|
+
// stop it so the loop does not run unbounded and unobserved.
|
|
207
|
+
const cameUp = await raceConnectionReady(ready);
|
|
208
|
+
if (!cameUp) {
|
|
209
|
+
const gaveUp = ws.getConnectionStatus().state === 'failed';
|
|
210
|
+
logConnection(startupTimeoutLogLine(WS_STARTUP_GRACE_MS, gaveUp));
|
|
211
|
+
ws.close();
|
|
212
|
+
// Fail loud rather than leave a dispatcher whose bot is silently dark:
|
|
213
|
+
// the host (dreamux's server) cleans up and surfaces the failure. A host
|
|
214
|
+
// that prefers to stand by on failure catches this in its own wrapper.
|
|
215
|
+
throw new Error(`Feishu inbound WebSocket for app ${creds.appId} did not connect within ${WS_STARTUP_GRACE_MS}ms`);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
get appId() {
|
|
220
|
+
return creds.appId;
|
|
221
|
+
},
|
|
222
|
+
get selfId() {
|
|
223
|
+
return resolvedSelfId;
|
|
224
|
+
},
|
|
225
|
+
async start(routes) {
|
|
226
|
+
await openInbound(routes);
|
|
227
|
+
},
|
|
228
|
+
async send(target, text) {
|
|
229
|
+
// Render the markdown source into one or more v2 cards. Routing per
|
|
230
|
+
// block type — headings to `header.title`, tables to `tag: table`,
|
|
231
|
+
// everything else to `tag: markdown` (lark_md) — keeps GFM tables and
|
|
232
|
+
// ATX headings from leaking through as literal `|` and `#`. A body
|
|
233
|
+
// too large for one card produces several cards, each sent as its own
|
|
234
|
+
// message_id so the recipient sees a threaded continuation.
|
|
235
|
+
//
|
|
236
|
+
// PR1: addressed by `chatId` only — behavior-identical to the old
|
|
237
|
+
// `sendText`. `replyToMessageId` / `mentionUserIds` are wired in PR2.
|
|
238
|
+
const cards = renderMarkdownToCards(text);
|
|
239
|
+
const messageIds = [];
|
|
240
|
+
for (const card of cards) {
|
|
241
|
+
const content = cardToContent(card);
|
|
242
|
+
// Fail fast on a card that could not be split under Feishu's hard cap,
|
|
243
|
+
// preserving dreamux's prior per-card send guard (see assertCardContentFits).
|
|
244
|
+
assertCardContentFits(content);
|
|
245
|
+
const res = await client.im.message.create({
|
|
246
|
+
params: { receive_id_type: 'chat_id' },
|
|
247
|
+
data: {
|
|
248
|
+
receive_id: target.chatId,
|
|
249
|
+
msg_type: 'interactive',
|
|
250
|
+
content,
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
const id = res.data?.message_id;
|
|
254
|
+
if (id)
|
|
255
|
+
messageIds.push(id);
|
|
256
|
+
}
|
|
257
|
+
return { messageIds };
|
|
258
|
+
},
|
|
259
|
+
async addReaction(messageId, emoji) {
|
|
260
|
+
const res = await client.im.messageReaction.create({
|
|
261
|
+
path: { message_id: messageId },
|
|
262
|
+
data: { reaction_type: { emoji_type: emoji } },
|
|
263
|
+
});
|
|
264
|
+
return res.data?.reaction_id ?? '';
|
|
265
|
+
},
|
|
266
|
+
async removeReaction(messageId, reactionId) {
|
|
267
|
+
await client.im.messageReaction.delete({
|
|
268
|
+
path: { message_id: messageId, reaction_id: reactionId },
|
|
269
|
+
});
|
|
270
|
+
},
|
|
271
|
+
async editText(messageId, text) {
|
|
272
|
+
// An edit patches one message_id in place and cannot fan out, so
|
|
273
|
+
// `renderSingleCard` rejects a body the renderer would otherwise split
|
|
274
|
+
// across several cards. `assertCardContentFits` then catches the
|
|
275
|
+
// residual case of a single-card body that still serialises past the
|
|
276
|
+
// 30 KB request cap — both checks surface as actionable errors before
|
|
277
|
+
// any SDK round-trip.
|
|
278
|
+
const card = renderSingleCard(text);
|
|
279
|
+
const cardContent = cardToContent(card);
|
|
280
|
+
assertCardContentFits(cardContent);
|
|
281
|
+
try {
|
|
282
|
+
// The send path produces an interactive card, so the matching edit
|
|
283
|
+
// is `im.message.patch` (card-content update). The original card was
|
|
284
|
+
// sent with `update_multi: true`, which Feishu requires for a later
|
|
285
|
+
// patch on the same message_id to be accepted.
|
|
286
|
+
await client.im.message.patch({
|
|
287
|
+
path: { message_id: messageId },
|
|
288
|
+
data: { content: cardContent },
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
catch (patchErr) {
|
|
292
|
+
// Legacy compatibility: a message_id a host is still holding may
|
|
293
|
+
// belong to a `msg_type: 'text'` message that this channel sent
|
|
294
|
+
// before the upgrade to interactive cards. Feishu rejects `patch`
|
|
295
|
+
// on a non-card target, so fall back to `im.message.update` with
|
|
296
|
+
// the legacy text payload. If the update also fails — auth, rate
|
|
297
|
+
// limit, deleted message — surface the original patch error, which
|
|
298
|
+
// describes the path the channel actually intends to use.
|
|
299
|
+
try {
|
|
300
|
+
await client.im.message.update({
|
|
301
|
+
path: { message_id: messageId },
|
|
302
|
+
data: { msg_type: 'text', content: textMessageContent(text) },
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
throw patchErr;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
async fetchDocComment(fileToken, fileType, commentId) {
|
|
311
|
+
// The file-comment API only serves a subset of document types; for any
|
|
312
|
+
// other type there is no comment to fetch, so skip the call outright.
|
|
313
|
+
const ct = asCommentFileType(fileType);
|
|
314
|
+
if (!ct)
|
|
315
|
+
return null;
|
|
316
|
+
try {
|
|
317
|
+
// `batchQuery` resolves a comment by id and serves both
|
|
318
|
+
// whole-document and local-selection comments. The single-comment
|
|
319
|
+
// `get` endpoint serves only whole-document comments — it returns
|
|
320
|
+
// "not exist" for a comment anchored to a text selection, which is
|
|
321
|
+
// most document comments.
|
|
322
|
+
const res = await client.drive.fileComment.batchQuery({
|
|
323
|
+
path: { file_token: fileToken },
|
|
324
|
+
// Resolve reply authors to open_id, so they match the open_id the
|
|
325
|
+
// event carries and the sender_id of chat messages.
|
|
326
|
+
params: { file_type: ct, user_id_type: 'open_id' },
|
|
327
|
+
data: { comment_ids: [commentId] },
|
|
328
|
+
});
|
|
329
|
+
return commentFromBatchQuery(res.data?.items ?? [], commentId);
|
|
330
|
+
}
|
|
331
|
+
catch (err) {
|
|
332
|
+
console.error(`[feishu-transport] could not fetch comment ${commentId} on ${fileToken}:`, err);
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
async fetchDocMeta(fileToken, fileType) {
|
|
337
|
+
const dt = asMetaDocType(fileType);
|
|
338
|
+
if (!dt)
|
|
339
|
+
return null;
|
|
340
|
+
try {
|
|
341
|
+
const res = await client.drive.meta.batchQuery({
|
|
342
|
+
data: { request_docs: [{ doc_token: fileToken, doc_type: dt }], with_url: true },
|
|
343
|
+
});
|
|
344
|
+
const meta = res.data?.metas?.[0];
|
|
345
|
+
if (!meta)
|
|
346
|
+
return null;
|
|
347
|
+
return { title: meta.title ?? '', url: meta.url ?? '' };
|
|
348
|
+
}
|
|
349
|
+
catch (err) {
|
|
350
|
+
console.error(`[feishu-transport] could not fetch metadata for ${fileToken}:`, err);
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
async close() {
|
|
355
|
+
try {
|
|
356
|
+
wsClient?.close();
|
|
357
|
+
}
|
|
358
|
+
catch (err) {
|
|
359
|
+
// A close on an already-closed socket is expected; anything else
|
|
360
|
+
// (e.g. the SDK's close surface changed) is worth a diagnostic line.
|
|
361
|
+
console.error('[feishu-transport] error while closing the Feishu WebSocket:', err);
|
|
362
|
+
}
|
|
363
|
+
wsClient = undefined;
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
/** How many times to try resolving the bot's open_id before giving up. */
|
|
368
|
+
const BOT_INFO_ATTEMPTS = 3;
|
|
369
|
+
/**
|
|
370
|
+
* Resolve the bot's own open_id, needed for group mention-gating. The SDK does
|
|
371
|
+
* not expose a bot-info method, so this calls the raw endpoint through the
|
|
372
|
+
* client (which still attaches the token).
|
|
373
|
+
*
|
|
374
|
+
* Best-effort: a failure leaves the open_id unknown rather than blocking
|
|
375
|
+
* startup — but it is not silent. An unknown open_id makes `isBotMentioned`
|
|
376
|
+
* never match, so every mention-gated group would drop every message; each
|
|
377
|
+
* failure is logged with that consequence spelled out, and a transient error
|
|
378
|
+
* is retried a few times before the transport gives up.
|
|
379
|
+
*/
|
|
380
|
+
async function resolveBotOpenId(client) {
|
|
381
|
+
for (let attempt = 1; attempt <= BOT_INFO_ATTEMPTS; attempt++) {
|
|
382
|
+
try {
|
|
383
|
+
const res = await client.request({
|
|
384
|
+
method: 'GET',
|
|
385
|
+
url: '/open-apis/bot/v3/info',
|
|
386
|
+
});
|
|
387
|
+
const openId = res.bot?.open_id;
|
|
388
|
+
if (openId)
|
|
389
|
+
return openId;
|
|
390
|
+
// A well-formed response that simply lacks the field will not improve
|
|
391
|
+
// on retry — stop here rather than spend the remaining attempts.
|
|
392
|
+
console.error('[feishu-transport] bot info response carried no open_id — groups that ' +
|
|
393
|
+
'require an @-mention will drop every message until the channel restarts');
|
|
394
|
+
return undefined;
|
|
395
|
+
}
|
|
396
|
+
catch (err) {
|
|
397
|
+
if (attempt < BOT_INFO_ATTEMPTS) {
|
|
398
|
+
await delay(attempt * 500);
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
console.error(`[feishu-transport] could not resolve the bot open_id after ${BOT_INFO_ATTEMPTS} ` +
|
|
402
|
+
'attempts — groups that require an @-mention will drop every message ' +
|
|
403
|
+
'until the channel restarts:', err);
|
|
404
|
+
return undefined;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return undefined;
|
|
408
|
+
}
|
|
409
|
+
/** Resolve after `ms` milliseconds — the backoff between bot-info attempts. */
|
|
410
|
+
function delay(ms) {
|
|
411
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
412
|
+
}
|
|
413
|
+
/** Write a timestamped connection-lifecycle line to the host's stderr log. */
|
|
414
|
+
function logConnection(line) {
|
|
415
|
+
console.error(`[feishu-transport] ${new Date().toISOString()} ${line}`);
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Resolve `true` if `ready` settles within the startup grace window, `false`
|
|
419
|
+
* if the window elapses first. The timer is cleared on the winning path so it
|
|
420
|
+
* does not keep the process alive after the race is decided.
|
|
421
|
+
*/
|
|
422
|
+
function raceConnectionReady(ready) {
|
|
423
|
+
return new Promise((resolve) => {
|
|
424
|
+
const timer = setTimeout(() => resolve(false), WS_STARTUP_GRACE_MS);
|
|
425
|
+
void ready.then(() => {
|
|
426
|
+
clearTimeout(timer);
|
|
427
|
+
resolve(true);
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
//# sourceMappingURL=feishu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feishu.js","sourceRoot":"","sources":["../../src/transport/feishu.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@excitedjs/feishu-transport",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Shared Feishu platform-I/O core for dreamux and claudemux: connect / receive / send / auth / render / parse + stateless policy (issue excitedjs/dreamux#25).",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=22.7"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@larksuiteoapi/node-sdk": "^1.64.0",
|
|
25
|
+
"marked": "^15.0.12"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^22.10.0",
|
|
29
|
+
"typescript": "^5.7.0",
|
|
30
|
+
"vitest": "^2.1.0"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -p tsconfig.json",
|
|
34
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"test:watch": "vitest",
|
|
37
|
+
"clean": "rm -rf dist"
|
|
38
|
+
}
|
|
39
|
+
}
|