@minitool/feishu-bot 0.1.0

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.
@@ -0,0 +1,345 @@
1
+ /** @-提醒参数 */
2
+ export declare interface AtOptions {
3
+ /** @ 指定用户 open_id 列表 */
4
+ atUserIds?: string[];
5
+ /** @ 所有人 */
6
+ atAll?: boolean;
7
+ }
8
+
9
+ /**
10
+ * 构造 image 消息。
11
+ *
12
+ * 注意:自定义机器人直发 image 消息只认 image_key(形如 `img_xxx`)。
13
+ * 想要直接发送本地文件,请使用 FeishuBot.sendImage() 或 FeishuBot.uploadImage()。
14
+ */
15
+ export declare function buildImage(imageKey: string): ImageMessage;
16
+
17
+ /**
18
+ * 构造卡片(interactive)消息。
19
+ *
20
+ * 直接透传 card 结构。支持 card schema 2.0 或旧版 header/elements 格式:
21
+ *
22
+ * buildInteractive({
23
+ * schema: "2.0",
24
+ * header: { title: { tag: "plain_text", content: "标题" } },
25
+ * body: { elements: [...] },
26
+ * });
27
+ *
28
+ * // 或旧版:
29
+ * buildInteractive({
30
+ * config: { wide_screen_mode: true },
31
+ * header: { template: "blue", title: { tag: "plain_text", content: "标题" } },
32
+ * elements: [...],
33
+ * });
34
+ */
35
+ export declare function buildInteractive(card: InteractiveCard): InteractiveMessage;
36
+
37
+ /**
38
+ * 构造富文本(post)消息。
39
+ *
40
+ * 用户构造 PostContent(支持 zh_cn/en_us/ja_jp 三语言),每个语言下是 `content: PostTag[][]` 的二维数组:
41
+ * 外层是段落(行),内层是行内的标签(text/a/at/img)。
42
+ *
43
+ * 示例:
44
+ * buildPost({
45
+ * zh_cn: {
46
+ * title: "标题",
47
+ * content: [
48
+ * [{ tag: "text", text: "第一段: " }, { tag: "a", text: "点这里", href: "https://..." }],
49
+ * [{ tag: "img", image_key: "img_xxx" }],
50
+ * ],
51
+ * },
52
+ * });
53
+ */
54
+ export declare function buildPost(post: PostContent): PostMessage;
55
+
56
+ /**
57
+ * 构造分享群名片(share_chat)消息。
58
+ *
59
+ * @param shareChatId 群 chat_id(形如 `oc_xxx`)
60
+ */
61
+ export declare function buildShareChat(shareChatId: string): ShareChatMessage;
62
+
63
+ /**
64
+ * 构造 text 消息。
65
+ *
66
+ * @-提醒说明(来自飞书文档):
67
+ * - @ 所有人:`<at user_id="all">所有人</at>`(仅群里能用,必须机器人所在群支持)
68
+ * - @ 指定用户(需已知 open_id):`<at user_id="ou_xxx"></at>`
69
+ *
70
+ * 示例:
71
+ * buildText("hello", { atAll: true })
72
+ * // => { msg_type: "text", content: { text: "hello <at user_id=\"all\">所有人</at>" } }
73
+ */
74
+ export declare function buildText(text: string, opts?: AtOptions): TextMessage;
75
+
76
+ /**
77
+ * 获取当前 Unix 秒时间戳。
78
+ */
79
+ export declare function currentTimestamp(): number;
80
+
81
+ /**
82
+ * 调用飞书 OpenAPI 或 webhook 后,返回 code !== 0 或 HTTP 非 2xx 时抛出。
83
+ */
84
+ export declare class FeishuApiError extends FeishuBotError {
85
+ readonly code: number;
86
+ readonly response: unknown;
87
+ constructor(message: string, code: number, response: unknown);
88
+ }
89
+
90
+ /** 飞书 OpenAPI 统一返回结构 */
91
+ export declare interface FeishuApiResponse<T = unknown> {
92
+ code: number;
93
+ msg: string;
94
+ data?: T;
95
+ }
96
+
97
+ /**
98
+ * 飞书自定义机器人 SDK 主类。
99
+ *
100
+ * 构造期不会报错;缺失配置时延迟到 send/upload 调用时抛出 FeishuConfigError,
101
+ * 便于「先 new 再注入配置」的使用模式。
102
+ *
103
+ * 使用示例:
104
+ * const bot = new FeishuBot(); // 从 env 读配置
105
+ * await bot.sendText("hello", { atAll: true });
106
+ * await bot.sendImage("./banner.png"); // 自动上传得到 image_key 再发送
107
+ */
108
+ export declare class FeishuBot {
109
+ private readonly webhook?;
110
+ private readonly secret?;
111
+ private readonly appId?;
112
+ private readonly appSecret?;
113
+ private readonly fetchImpl?;
114
+ private readonly timeout?;
115
+ private readonly baseUrl;
116
+ private tokenManager;
117
+ private imageUploader;
118
+ constructor(options?: FeishuBotOptions);
119
+ /**
120
+ * 原子发送:接收已构造好的 payload,负责注入签名并 POST 到 webhook。
121
+ * code !== 0 时抛 FeishuApiError。
122
+ */
123
+ send<T = unknown>(payload: MessagePayload): Promise<FeishuApiResponse<T>>;
124
+ sendText(text: string, opts?: AtOptions): Promise<FeishuApiResponse>;
125
+ sendPost(post: PostContent): Promise<FeishuApiResponse>;
126
+ sendShareChat(shareChatId: string): Promise<FeishuApiResponse>;
127
+ sendInteractive(card: InteractiveCard): Promise<FeishuApiResponse>;
128
+ /**
129
+ * 发送图片。智能识别三种入参:
130
+ * - string 且以 `img_` 开头 → 直接当 image_key 使用
131
+ * - string 否则 → 视为本地文件路径,先上传再发送
132
+ * - Buffer / Uint8Array → 直接上传再发送
133
+ */
134
+ sendImage(input: ImageSource): Promise<FeishuApiResponse>;
135
+ /**
136
+ * 暴露底层图片上传,便于调用方复用 image_key。
137
+ * 需要 appId / appSecret 配置。
138
+ */
139
+ uploadImage(file: ImageSource): Promise<string>;
140
+ private ensureWebhook;
141
+ private ensureAppCredentials;
142
+ private getTokenManager;
143
+ private getImageUploader;
144
+ }
145
+
146
+ /**
147
+ * 所有飞书机器人相关错误的基类。
148
+ */
149
+ export declare class FeishuBotError extends Error {
150
+ constructor(message: string);
151
+ }
152
+
153
+ /**
154
+ * 飞书自定义机器人 SDK 的类型定义。
155
+ */
156
+ /** SDK 构造配置 */
157
+ export declare interface FeishuBotOptions {
158
+ /** 机器人 webhook URL。默认读 `process.env.FEISHU_BOT_WEBHOOK` */
159
+ webhook?: string;
160
+ /** 签名校验密钥。若设置,会在每次发送时自动附加 timestamp / sign */
161
+ secret?: string;
162
+ /** 应用 App ID,仅图片上传需要。默认读 `process.env.FEISHU_APP_ID` */
163
+ appId?: string;
164
+ /** 应用 App Secret,仅图片上传需要。默认读 `process.env.FEISHU_APP_SECRET` */
165
+ appSecret?: string;
166
+ /** 自定义 fetch 实现,用于测试注入。默认用 globalThis.fetch */
167
+ fetch?: typeof fetch;
168
+ /** 请求超时,单位毫秒。默认 10000 */
169
+ timeout?: number;
170
+ /** 飞书开放平台基础 URL,默认 https://open.feishu.cn */
171
+ baseUrl?: string;
172
+ }
173
+
174
+ /**
175
+ * 配置相关错误:如未提供 webhook、secret、appId、appSecret 等。
176
+ * 构造 FeishuBot 实例时不会抛;延迟到 send/upload 调用时才抛。
177
+ */
178
+ export declare class FeishuConfigError extends FeishuBotError {
179
+ constructor(message: string);
180
+ }
181
+
182
+ /**
183
+ * 生成飞书自定义机器人签名。
184
+ *
185
+ * 算法(来自飞书官方文档,反直觉之处:HMAC 的 key 是 stringToSign 本身,data 是空字符串):
186
+ * stringToSign = `${timestamp}\n${secret}`
187
+ * sign = Base64(HmacSHA256(key = stringToSign, data = ''))
188
+ *
189
+ * @param timestamp Unix 秒时间戳(飞书要求 ±1 小时窗口)
190
+ * @param secret 机器人「安全设置 → 签名校验」得到的 secret
191
+ */
192
+ export declare function genSign(timestamp: number | string, secret: string): string;
193
+
194
+ /** 图片消息 */
195
+ export declare interface ImageMessage {
196
+ msg_type: 'image';
197
+ content: {
198
+ image_key: string;
199
+ };
200
+ }
201
+
202
+ /** 支持的图片源:文件路径字符串 / Buffer / Uint8Array */
203
+ export declare type ImageSource = string | Buffer | Uint8Array;
204
+
205
+ /**
206
+ * 图片上传器:调用 im/v1/images 接口,返回 image_key。
207
+ * - string: 作为文件路径用 fs/promises.readFile 读成 Buffer
208
+ * - Buffer/Uint8Array: 直接作为 Blob 数据
209
+ * 用 globalThis 的 FormData + Blob(Node 18+ 内置),不依赖 form-data 包。
210
+ */
211
+ export declare class ImageUploader {
212
+ private readonly tokenManager;
213
+ private readonly fetchImpl?;
214
+ private readonly timeout?;
215
+ private readonly baseUrl;
216
+ constructor(options: ImageUploaderOptions);
217
+ /**
218
+ * 上传图片,返回 image_key。
219
+ */
220
+ uploadImage(file: ImageSource): Promise<string>;
221
+ private resolveSource;
222
+ }
223
+
224
+ declare interface ImageUploaderOptions {
225
+ tokenManager: TokenManager;
226
+ fetch?: typeof fetch;
227
+ timeout?: number;
228
+ baseUrl?: string;
229
+ }
230
+
231
+ /** 卡片消息(透传 card schema 2.0 或旧版) */
232
+ export declare type InteractiveCard = Record<string, unknown>;
233
+
234
+ export declare interface InteractiveMessage {
235
+ msg_type: 'interactive';
236
+ card: InteractiveCard;
237
+ }
238
+
239
+ /** 所有支持的消息类型联合 */
240
+ export declare type MessagePayload = TextMessage | PostMessage | ImageMessage | ShareChatMessage | InteractiveMessage;
241
+
242
+ /** 富文本多语言内容 */
243
+ export declare interface PostContent {
244
+ zh_cn?: PostLocale;
245
+ en_us?: PostLocale;
246
+ ja_jp?: PostLocale;
247
+ }
248
+
249
+ /** 富文本单语言内容 */
250
+ export declare interface PostLocale {
251
+ title?: string;
252
+ content: PostTag[][];
253
+ }
254
+
255
+ /** 富文本消息 */
256
+ export declare interface PostMessage {
257
+ msg_type: 'post';
258
+ content: {
259
+ post: PostContent;
260
+ };
261
+ }
262
+
263
+ /** 富文本消息中的内容标签 */
264
+ export declare type PostTag = {
265
+ tag: 'text';
266
+ text: string;
267
+ un_escape?: boolean;
268
+ } | {
269
+ tag: 'a';
270
+ text: string;
271
+ href: string;
272
+ } | {
273
+ tag: 'at';
274
+ user_id: string;
275
+ user_name?: string;
276
+ } | {
277
+ tag: 'img';
278
+ image_key: string;
279
+ };
280
+
281
+ /** 分享群名片消息 */
282
+ export declare interface ShareChatMessage {
283
+ msg_type: 'share_chat';
284
+ content: {
285
+ share_chat_id: string;
286
+ };
287
+ }
288
+
289
+ /** 带签名字段的最终 webhook 请求体 */
290
+ export declare type SignedPayload = MessagePayload & {
291
+ timestamp?: string;
292
+ sign?: string;
293
+ };
294
+
295
+ /** 获取 tenant_access_token 的返回 */
296
+ export declare interface TenantAccessTokenResponse {
297
+ code: number;
298
+ msg: string;
299
+ tenant_access_token?: string;
300
+ expire?: number;
301
+ }
302
+
303
+ /** 文本消息 */
304
+ export declare interface TextMessage {
305
+ msg_type: 'text';
306
+ content: {
307
+ text: string;
308
+ };
309
+ }
310
+
311
+ /**
312
+ * tenant_access_token 缓存与自动刷新。
313
+ * 并发去重:多次 getToken() 在 in-flight 期间共享同一个 Promise,避免重复请求。
314
+ */
315
+ export declare class TokenManager {
316
+ private readonly appId;
317
+ private readonly appSecret;
318
+ private readonly fetchImpl?;
319
+ private readonly timeout?;
320
+ private readonly baseUrl;
321
+ private cached;
322
+ private inflight;
323
+ constructor(options: TokenManagerOptions);
324
+ /**
325
+ * 获取有效 token。优先使用缓存;过期/即将过期时刷新。
326
+ */
327
+ getToken(): Promise<string>;
328
+ private isCacheFresh;
329
+ private fetchToken;
330
+ }
331
+
332
+ declare interface TokenManagerOptions {
333
+ appId: string;
334
+ appSecret: string;
335
+ fetch?: typeof fetch;
336
+ timeout?: number;
337
+ baseUrl?: string;
338
+ }
339
+
340
+ /** 上传图片返回数据 */
341
+ export declare interface UploadImageResult {
342
+ image_key: string;
343
+ }
344
+
345
+ export { }