@starim-io/bot-sdk 0.1.4

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,727 @@
1
+ import { Server } from 'node:http';
2
+
3
+ /**
4
+ * 与 bot-service BotUpdateDispatcher / Webhook 投递载荷对齐的 Update 类型。
5
+ * @see server/services/bot/src/services/BotUpdateDispatcher.js
6
+ */
7
+ type UpdateType = 'message' | 'photo' | 'document' | 'video' | 'audio' | 'location' | 'edited_message' | 'message_deleted' | 'message_read' | 'message_delivered' | 'callback_query' | 'inline_query' | 'friend_request' | 'my_chat_member' | 'chat_member' | 'chat_join_request';
8
+ interface StarIMUser {
9
+ id: string;
10
+ username?: string;
11
+ first_name?: string;
12
+ is_bot: boolean;
13
+ }
14
+ interface Chat {
15
+ id: string;
16
+ type: 'private' | 'group';
17
+ title?: string;
18
+ }
19
+ interface MessageFile {
20
+ file_id: string;
21
+ file_name?: string;
22
+ mime_type?: string;
23
+ file_size?: number;
24
+ url?: string;
25
+ thumbnail_url?: string;
26
+ }
27
+ interface MessageLocation {
28
+ latitude: number;
29
+ longitude: number;
30
+ name?: string;
31
+ address?: string;
32
+ }
33
+ interface InlineKeyboardButton {
34
+ text: string;
35
+ callback_data?: string;
36
+ url?: string;
37
+ }
38
+ interface KeyboardButton {
39
+ text: string;
40
+ }
41
+ /** Inline 按钮键盘 */
42
+ interface ReplyMarkupInline {
43
+ inline_keyboard: InlineKeyboardButton[][];
44
+ }
45
+ /** 底部回复键盘(ReplyKeyboard) */
46
+ interface ReplyMarkupReplyKeyboard {
47
+ keyboard: (string | KeyboardButton)[][];
48
+ resize_keyboard?: boolean;
49
+ one_time_keyboard?: boolean;
50
+ is_persistent?: boolean;
51
+ input_field_placeholder?: string;
52
+ }
53
+ interface ReplyMarkupRemove {
54
+ remove_keyboard: true;
55
+ selective?: boolean;
56
+ }
57
+ type ReplyMarkup = ReplyMarkupInline | ReplyMarkupReplyKeyboard | ReplyMarkupRemove;
58
+ interface BotCommand {
59
+ command: string;
60
+ description: string;
61
+ }
62
+ interface SetMyCommandsParams {
63
+ commands: BotCommand[];
64
+ scope?: string | {
65
+ type: string;
66
+ };
67
+ language_code?: string;
68
+ }
69
+ interface GetMyCommandsParams {
70
+ scope?: string;
71
+ language_code?: string;
72
+ }
73
+ interface DeleteMyCommandsParams {
74
+ scope?: string | {
75
+ type: string;
76
+ };
77
+ language_code?: string;
78
+ }
79
+ /** Webhook / Update 中的 message 对象(字段随 type 增减) */
80
+ interface WebhookMessage {
81
+ message_id: string;
82
+ from?: StarIMUser;
83
+ chat: Chat;
84
+ text?: string;
85
+ date: number;
86
+ file?: MessageFile;
87
+ location?: MessageLocation;
88
+ reply_markup?: ReplyMarkup;
89
+ metadata?: Record<string, unknown>;
90
+ }
91
+ interface CallbackQuery {
92
+ id: string;
93
+ from: StarIMUser;
94
+ message?: WebhookMessage;
95
+ data: string;
96
+ chat_instance?: string;
97
+ created_at?: string | number;
98
+ }
99
+ /** Inline Mode 查询载荷 */
100
+ interface InlineQuery {
101
+ id: string;
102
+ from: StarIMUser;
103
+ query: string;
104
+ offset: string;
105
+ chat_type?: 'private' | 'group' | string;
106
+ }
107
+ /** 用户向机器人发起好友申请(Bot friend_request_mode = manual) */
108
+ interface FriendRequestUpdate {
109
+ /** 对应 Friendship._id */
110
+ id: string;
111
+ from: StarIMUser;
112
+ request_message: string;
113
+ source: string;
114
+ date: number;
115
+ }
116
+ /** 群成员状态片段(status 为字符串枚举) */
117
+ interface ChatMember {
118
+ user: StarIMUser;
119
+ status: string;
120
+ }
121
+ /** my_chat_member / chat_member 载荷 */
122
+ interface ChatMemberUpdated {
123
+ chat: Chat;
124
+ from: StarIMUser;
125
+ date: number;
126
+ old_chat_member: ChatMember;
127
+ new_chat_member: ChatMember;
128
+ }
129
+ /** 入群申请(管理员机器人可收) */
130
+ interface ChatJoinRequestUpdate {
131
+ chat: Chat;
132
+ from: StarIMUser;
133
+ user_chat_id: string;
134
+ date: number;
135
+ bio?: string;
136
+ }
137
+ interface AnswerFriendRequestParams {
138
+ friendship_id: string;
139
+ action: 'accept' | 'reject';
140
+ }
141
+ type FriendRequestMode = 'auto_accept' | 'auto_reject' | 'manual';
142
+ interface SetMyFriendRequestModeParams {
143
+ friend_request_mode: FriendRequestMode;
144
+ }
145
+ /** GET /bots/getFriendRequests 单条(与测试机器人后管列表字段对齐) */
146
+ interface BotFriendRequestListItem {
147
+ id: string;
148
+ update_id: string;
149
+ ts: string;
150
+ fromUserId: string;
151
+ fromUsername: string;
152
+ requestMessage: string;
153
+ source: string;
154
+ status: 'pending' | 'accepted' | 'rejected';
155
+ date?: number;
156
+ }
157
+ interface Update {
158
+ update_id: string;
159
+ type: UpdateType;
160
+ bot_id: string;
161
+ message?: WebhookMessage;
162
+ callback_query?: CallbackQuery;
163
+ inline_query?: InlineQuery;
164
+ friend_request?: FriendRequestUpdate;
165
+ my_chat_member?: ChatMemberUpdated;
166
+ chat_member?: ChatMemberUpdated;
167
+ chat_join_request?: ChatJoinRequestUpdate;
168
+ date?: number;
169
+ /** polling 模式(getUpdates)专属:单调递增序号,用于实现 offset ack 语义 */
170
+ update_seq?: number;
171
+ }
172
+ /** getUpdates 入参 */
173
+ interface GetUpdatesParams {
174
+ /**
175
+ * 拉取 update_seq >= offset 的条目。
176
+ * 推荐用「上一批最后一条 update_seq + 1」作为下次 offset,等价 ack。
177
+ */
178
+ offset?: number;
179
+ /** 1..100,默认 100 */
180
+ limit?: number;
181
+ /** long-poll 秒数,0 立即返回,最大 50;默认 0 */
182
+ timeout?: number;
183
+ /** 仅返回 type 命中的;缺省 = 全部 */
184
+ allowed_updates?: UpdateType[] | string[];
185
+ }
186
+ interface GetUpdatesResult {
187
+ updates: Update[];
188
+ }
189
+ interface SetWebhookParams {
190
+ url: string;
191
+ secret_token?: string;
192
+ allowed_updates?: string[];
193
+ allowed_ips?: string[];
194
+ }
195
+ interface SendMessageParams {
196
+ chat_id: string;
197
+ text: string;
198
+ reply_markup?: ReplyMarkup;
199
+ }
200
+ interface SendMediaParams {
201
+ chat_id: string;
202
+ file_id: string;
203
+ caption?: string;
204
+ /** 时长(秒),语音/视频等;不传则 App 端可能显示为 0″ */
205
+ duration?: number;
206
+ /** 与 Bot API `length` 同义(视频笔记时长) */
207
+ length?: number;
208
+ /** 视频等;与网关 sendVideo 一致 */
209
+ width?: number;
210
+ height?: number;
211
+ performer?: string;
212
+ title?: string;
213
+ /** 网关字段 `thumbnail_url` */
214
+ thumbnail_url?: string;
215
+ }
216
+ interface SendLocationParams {
217
+ chat_id: string;
218
+ latitude: number;
219
+ longitude: number;
220
+ name?: string;
221
+ address?: string;
222
+ }
223
+ interface SendVenueParams {
224
+ chat_id: string;
225
+ latitude: number;
226
+ longitude: number;
227
+ title: string;
228
+ address?: string;
229
+ reply_markup?: ReplyMarkup;
230
+ }
231
+ interface SendDiceParams {
232
+ chat_id: string;
233
+ emoji?: string;
234
+ reply_markup?: ReplyMarkup;
235
+ }
236
+ interface SendPollParams {
237
+ chat_id: string;
238
+ question: string;
239
+ options: string[];
240
+ is_anonymous?: boolean;
241
+ type?: 'regular' | 'quiz';
242
+ correct_option_id?: number;
243
+ reply_markup?: ReplyMarkup;
244
+ }
245
+ interface SendContactParams {
246
+ chat_id: string;
247
+ phone_number: string;
248
+ first_name: string;
249
+ last_name?: string;
250
+ reply_markup?: ReplyMarkup;
251
+ }
252
+ /** sendMediaGroup:与网关约定 type = photo|video|document|audio */
253
+ interface SendMediaGroupItem {
254
+ type: 'photo' | 'image' | 'video' | 'document' | 'audio';
255
+ /** 兼容字段名(与 file_id 二选一) */
256
+ media?: string;
257
+ file_id?: string;
258
+ caption?: string;
259
+ }
260
+ interface SendMediaGroupParams {
261
+ chat_id: string;
262
+ media: SendMediaGroupItem[];
263
+ reply_markup?: ReplyMarkup;
264
+ }
265
+ interface EditMessageParams {
266
+ message_id: string;
267
+ text?: string;
268
+ content?: string;
269
+ reply_markup?: ReplyMarkup | null;
270
+ }
271
+ interface EditMessageReplyMarkupParams {
272
+ message_id: string;
273
+ reply_markup?: ReplyMarkup | null;
274
+ }
275
+ interface AnswerCallbackQueryParams {
276
+ callback_query_id: string;
277
+ text?: string;
278
+ show_alert?: boolean;
279
+ url?: string;
280
+ cache_time?: number;
281
+ }
282
+ /** answerInlineQuery:首期仅支持 article + InputTextMessageContent(与平台校验一致) */
283
+ interface AnswerInlineQueryParams {
284
+ inline_query_id: string;
285
+ results: Record<string, unknown>[];
286
+ cache_time?: number;
287
+ is_personal?: boolean;
288
+ next_offset?: string;
289
+ }
290
+ /** 群治理:chat_id 为群会话 Mongo _id(与 sendMessage 一致) */
291
+ interface KickChatMemberParams {
292
+ chat_id: string;
293
+ user_id: string;
294
+ }
295
+ interface BanChatMemberParams extends KickChatMemberParams {
296
+ reason?: string;
297
+ }
298
+ interface DeleteMessageParams {
299
+ message_id: string;
300
+ }
301
+ interface IssueUploadCredentialsParams {
302
+ fileName: string;
303
+ fileSize: number;
304
+ fileType?: string;
305
+ checksum?: string;
306
+ metadata?: Record<string, unknown>;
307
+ }
308
+ interface CompleteUploadParams {
309
+ key: string;
310
+ fileName: string;
311
+ fileSize: number;
312
+ fileType?: string;
313
+ checksum?: string;
314
+ category?: string;
315
+ isPublic?: boolean;
316
+ }
317
+ /** file.getS3Credentials 返回(与 s3UploadService.generateUploadCredentials 对齐) */
318
+ interface UploadCredentials {
319
+ uploadUrl: string;
320
+ method: string;
321
+ key: string;
322
+ bucket: string;
323
+ region: string;
324
+ expiresIn: number;
325
+ maxSize: number;
326
+ contentType: string;
327
+ objectUrl: string;
328
+ }
329
+ /** complete 登记返回 */
330
+ interface CompletedUpload {
331
+ id: string;
332
+ url: string;
333
+ objectUrl?: string;
334
+ key?: string;
335
+ fileName?: string;
336
+ originalName?: string;
337
+ size?: number;
338
+ mimeType?: string;
339
+ }
340
+ type LoggerFn = (level: 'debug' | 'info' | 'warn' | 'error', msg: string, meta?: object) => void;
341
+ interface StarIMBotClientOptions {
342
+ token: string;
343
+ /** 含 `/api/v1` 前缀的网关根,如 `https://your-gateway.example/api/v1` */
344
+ baseUrl?: string;
345
+ timeoutMs?: number;
346
+ /** 为 true 时通过 logger 输出请求元信息(绝不打印 token) */
347
+ debug?: boolean;
348
+ logger?: Pick<Console, 'debug' | 'info' | 'warn' | 'error'>;
349
+ }
350
+ interface StarIMBotOptions extends StarIMBotClientOptions {
351
+ /** 与 setWebhook 时配置的 secret_token 一致,用于校验 X-StarIM-Signature */
352
+ secretToken: string;
353
+ /** 去重缓存大小,默认 1024 */
354
+ dedupeSize?: number;
355
+ }
356
+
357
+ declare class StarIMBotClient {
358
+ readonly token: string;
359
+ private readonly baseUrl;
360
+ private readonly timeoutMs;
361
+ private readonly debug;
362
+ /** 暴露给上层(如 StarIMBot polling 链路)以记录非致命错误 */
363
+ readonly logger: Pick<Console, 'debug' | 'info' | 'warn' | 'error'>;
364
+ readonly files: {
365
+ issueUploadCredentials: (p: IssueUploadCredentialsParams) => Promise<UploadCredentials>;
366
+ completeUpload: (p: CompleteUploadParams) => Promise<CompletedUpload>;
367
+ };
368
+ constructor(opts: StarIMBotClientOptions);
369
+ private log;
370
+ private request;
371
+ /** GET /bots/me */
372
+ getMe(): Promise<Record<string, unknown>>;
373
+ /** GET /bots/getChat?chat_id= */
374
+ getChat(chat_id: string): Promise<Record<string, unknown>>;
375
+ getChatMember(chat_id: string, user_id: string): Promise<Record<string, unknown>>;
376
+ getChatMemberCount(chat_id: string): Promise<Record<string, unknown>>;
377
+ getChatAdministrators(chat_id: string): Promise<Record<string, unknown>>;
378
+ setChatTitle(chat_id: string, title: string): Promise<unknown>;
379
+ setChatDescription(chat_id: string, description: string): Promise<unknown>;
380
+ setChatPhoto(chat_id: string, photo: string): Promise<unknown>;
381
+ pinChatMessage(body: Record<string, unknown>): Promise<unknown>;
382
+ unpinChatMessage(body: Record<string, unknown>): Promise<unknown>;
383
+ unpinAllChatMessages(chat_id: string): Promise<unknown>;
384
+ forwardMessage(body: Record<string, unknown>): Promise<unknown>;
385
+ copyMessage(body: Record<string, unknown>): Promise<unknown>;
386
+ sendChatAction(chat_id: string, action: string): Promise<unknown>;
387
+ getFile(file_id: string): Promise<Record<string, unknown>>;
388
+ /** POST /bots/sendMessage */
389
+ sendMessage(params: SendMessageParams): Promise<unknown>;
390
+ /** POST /bots/setMyCommands */
391
+ setMyCommands(params: SetMyCommandsParams): Promise<unknown>;
392
+ /** GET /bots/getMyCommands */
393
+ getMyCommands(params?: GetMyCommandsParams): Promise<{
394
+ commands: BotCommand[];
395
+ }>;
396
+ /** POST /bots/deleteMyCommands */
397
+ deleteMyCommands(params?: DeleteMyCommandsParams): Promise<unknown>;
398
+ /** POST /bots/setMyShortDescription · 设置短描述(≤120 字) */
399
+ setMyShortDescription(short_description: string): Promise<{
400
+ short_description: string;
401
+ }>;
402
+ /** GET /bots/getMyShortDescription · 获取短描述 */
403
+ getMyShortDescription(): Promise<{
404
+ short_description: string;
405
+ }>;
406
+ /** POST /bots/setMyDescription · 设置 Bot 主页大段「关于」文本(≤512 字) */
407
+ setMyDescription(description: string): Promise<{
408
+ description: string;
409
+ }>;
410
+ /** GET /bots/getMyDescription · 获取 Bot 主页大段「关于」文本 */
411
+ getMyDescription(): Promise<{
412
+ description: string;
413
+ }>;
414
+ setWebhook(params: SetWebhookParams): Promise<unknown>;
415
+ deleteWebhook(): Promise<unknown>;
416
+ /** GET /bots/getWebhookInfo */
417
+ getWebhookInfo(): Promise<Record<string, unknown>>;
418
+ /**
419
+ * POST /bots/getUpdates · 长轮询拉取 polling 模式 update。
420
+ *
421
+ * - 调本接口前请确保已 `deleteWebhook`,否则平台返回 409。
422
+ * - HTTP 客户端的超时会按 `timeout + 5s` 自动放宽,避免比服务端先断开。
423
+ *
424
+ * @example
425
+ * ```ts
426
+ * let offset = 0;
427
+ * while (true) {
428
+ * const { updates } = await client.getUpdates({ timeout: 30, offset });
429
+ * for (const u of updates) {
430
+ * console.log(u);
431
+ * if (typeof u.update_seq === 'number') offset = u.update_seq + 1;
432
+ * }
433
+ * }
434
+ * ```
435
+ */
436
+ getUpdates(params?: GetUpdatesParams): Promise<GetUpdatesResult>;
437
+ sendPhoto(params: SendMediaParams): Promise<unknown>;
438
+ sendDocument(params: SendMediaParams): Promise<unknown>;
439
+ sendVideo(params: SendMediaParams): Promise<unknown>;
440
+ sendAudio(params: SendMediaParams): Promise<unknown>;
441
+ sendVoice(params: SendMediaParams): Promise<unknown>;
442
+ sendVideoNote(params: SendMediaParams): Promise<unknown>;
443
+ sendAnimation(params: SendMediaParams): Promise<unknown>;
444
+ sendSticker(params: SendMediaParams): Promise<unknown>;
445
+ sendDice(params: SendDiceParams): Promise<unknown>;
446
+ sendPoll(params: SendPollParams): Promise<unknown>;
447
+ sendContact(params: SendContactParams): Promise<unknown>;
448
+ sendMediaGroup(params: SendMediaGroupParams): Promise<unknown>;
449
+ private buildSendMediaBody;
450
+ private buildSendVideoNoteBody;
451
+ sendLocation(params: SendLocationParams): Promise<unknown>;
452
+ sendVenue(params: SendVenueParams): Promise<unknown>;
453
+ editMessage(params: EditMessageParams): Promise<unknown>;
454
+ editMessageReplyMarkup(params: EditMessageReplyMarkupParams): Promise<unknown>;
455
+ answerCallbackQuery(params: AnswerCallbackQueryParams): Promise<unknown>;
456
+ answerInlineQuery(params: AnswerInlineQueryParams): Promise<unknown>;
457
+ /** POST /bots/answerFriendRequest · manual 模式下处理用户发起的好友申请 */
458
+ answerFriendRequest(params: AnswerFriendRequestParams): Promise<unknown>;
459
+ /** POST /bots/setMyFriendRequestMode · 配置当前机器人好友申请处理策略 */
460
+ setMyFriendRequestMode(params: SetMyFriendRequestModeParams): Promise<{
461
+ friend_request_mode: string;
462
+ }>;
463
+ /** GET /bots/getMyFriendRequestMode · 获取当前机器人好友申请处理策略 */
464
+ getMyFriendRequestMode(): Promise<{
465
+ friend_request_mode: string;
466
+ }>;
467
+ /** GET /bots/getFriendRequests · 待处理好友申请(数据库 Friendship) */
468
+ getFriendRequests(): Promise<{
469
+ friend_requests: BotFriendRequestListItem[];
470
+ }>;
471
+ kickChatMember(params: KickChatMemberParams): Promise<unknown>;
472
+ banChatMember(params: BanChatMemberParams): Promise<unknown>;
473
+ unbanChatMember(params: KickChatMemberParams): Promise<unknown>;
474
+ deleteMessage(params: DeleteMessageParams): Promise<unknown>;
475
+ issueUploadCredentials(p: IssueUploadCredentialsParams): Promise<UploadCredentials>;
476
+ completeUpload(p: CompleteUploadParams): Promise<CompletedUpload>;
477
+ /**
478
+ * 校验 Token(公开接口,不强制带 Bearer,仅 body.token)。
479
+ */
480
+ verifyToken(): Promise<{
481
+ bot?: Record<string, unknown>;
482
+ }>;
483
+ /** 本地路径 → 上传 S3 → sendPhoto */
484
+ sendPhotoFromFile(chatId: string, filePath: string, options?: {
485
+ caption?: string;
486
+ fileName?: string;
487
+ fileType?: string;
488
+ }): Promise<unknown>;
489
+ /** 本地路径 → 上传 S3 → sendDocument */
490
+ sendDocumentFromFile(chatId: string, filePath: string, options?: {
491
+ caption?: string;
492
+ fileName?: string;
493
+ fileType?: string;
494
+ }): Promise<unknown>;
495
+ }
496
+
497
+ /** V2 时间戳容忍窗口(秒)。客户端时钟与服务端偏差超过此值则拒收,防 replay。 */
498
+ declare const DEFAULT_TIMESTAMP_TOLERANCE_SEC = 300;
499
+ /**
500
+ * V2 签名:HMAC-SHA256(`${timestamp}.${body}`),hex 小写。
501
+ *
502
+ * timestamp 为 unix 秒(与服务端 `X-StarIM-Timestamp` 头一致)。
503
+ */
504
+ declare function computeWebhookSignatureV2(secret: string, timestampSec: number | string, rawBody: Buffer | string): string;
505
+ interface VerifyWebhookOptions {
506
+ /** V2 时间戳容忍窗口(秒),默认 300 */
507
+ timestampToleranceSec?: number;
508
+ /** 注入时钟,便于测试 */
509
+ nowSec?: () => number;
510
+ }
511
+ /**
512
+ * 校验 webhook V2 签名(须同时提供 X-StarIM-Signature-V2 与 X-StarIM-Timestamp)。
513
+ *
514
+ * @throws {StarIMSignatureError} 校验失败
515
+ */
516
+ declare function verifyWebhookSignature(secret: string, rawBody: Buffer | string, signatureHeaderV2: string | undefined, timestampHeader: string | undefined, opts?: VerifyWebhookOptions): void;
517
+ interface WebhookRequest {
518
+ method?: string;
519
+ headers: Record<string, string | string[] | undefined>;
520
+ /** express.raw / body-parser raw 解析后的 Buffer */
521
+ body?: unknown;
522
+ /** 可选:在 json 解析前由中间件挂载的原始字节 */
523
+ rawBody?: Buffer;
524
+ }
525
+ interface WebhookResponse {
526
+ statusCode?: number;
527
+ setHeader?(name: string, value: string | number | readonly string[]): unknown;
528
+ end(chunk?: string | Uint8Array): void;
529
+ }
530
+ interface DedupeStore {
531
+ /** 若已处理过该 update_id 返回 true */
532
+ has(updateId: string): boolean;
533
+ add(updateId: string): void;
534
+ }
535
+ /** 简单 LRU:超过容量时删除最旧条目 */
536
+ declare class LruDedupe implements DedupeStore {
537
+ private readonly maxSize;
538
+ private readonly order;
539
+ private readonly set;
540
+ constructor(maxSize: number);
541
+ has(updateId: string): boolean;
542
+ add(updateId: string): void;
543
+ }
544
+ interface ProcessWebhookOptions {
545
+ secretToken: string;
546
+ dedupe?: DedupeStore;
547
+ /** 透传给 verifyWebhookSignature */
548
+ verify?: VerifyWebhookOptions;
549
+ }
550
+ interface ProcessWebhookResult {
551
+ update: Update;
552
+ duplicate: boolean;
553
+ }
554
+ /**
555
+ * 校验签名、解析 Update、可选幂等去重。
556
+ * duplicate 为 true 时表示 update_id 已见过,业务层可选择跳过重复投递。
557
+ */
558
+ declare function processWebhook(req: WebhookRequest, opts: ProcessWebhookOptions): ProcessWebhookResult;
559
+ interface WebhookMiddlewareOptions extends ProcessWebhookOptions {
560
+ onUpdate: (update: Update, ctx: {
561
+ rawBody: Buffer;
562
+ duplicate: boolean;
563
+ }) => void | Promise<void>;
564
+ /** 重复 update_id 时仍调用 onUpdate,默认 false */
565
+ invokeOnDuplicate?: boolean;
566
+ }
567
+ /**
568
+ * Connect/Express 风格中间件:请务必在 JSON 解析前保留 raw body。
569
+ */
570
+ declare function webhookMiddleware(opts: WebhookMiddlewareOptions): (req: WebhookRequest, res: WebhookResponse, next?: (err?: unknown) => void) => Promise<void>;
571
+
572
+ type BotContext = {
573
+ update: Update;
574
+ /** 原始 POST body(与签名校验一致的字节) */
575
+ rawBody: Buffer;
576
+ /** 平台重试导致的重复 update_id */
577
+ duplicate: boolean;
578
+ client: StarIMBotClient;
579
+ /** 当前载荷中的 message(编辑/删除场景字段可能不全) */
580
+ message: WebhookMessage;
581
+ chat: WebhookMessage['chat'];
582
+ /** InlineKeyboard 点击事件,仅 callback_query Update 存在 */
583
+ callbackQuery?: CallbackQuery;
584
+ /** Inline Mode 查询,仅 inline_query Update 存在 */
585
+ inlineQuery?: InlineQuery;
586
+ /** 用户发起的好友申请,仅 friend_request Update 存在 */
587
+ friendRequest?: FriendRequestUpdate;
588
+ /** 机器人自身被加入/移出/状态变化,仅 my_chat_member Update 存在 */
589
+ myChatMember?: ChatMemberUpdated;
590
+ /** 群内其它成员状态变化(机器人需是管理员),仅 chat_member Update 存在 */
591
+ chatMember?: ChatMemberUpdated;
592
+ /** 群入群申请,仅 chat_join_request Update 存在 */
593
+ chatJoinRequest?: ChatJoinRequestUpdate;
594
+ /** 快捷回复文本到当前会话 */
595
+ reply: (text: string, options?: {
596
+ reply_markup?: ReplyMarkup;
597
+ }) => ReturnType<StarIMBotClient['sendMessage']>;
598
+ /** 快捷回应当前 callback_query */
599
+ answerCallback: (params?: Omit<AnswerCallbackQueryParams, 'callback_query_id'>) => ReturnType<StarIMBotClient['answerCallbackQuery']>;
600
+ /** 快捷应答当前 inline_query */
601
+ answerInlineQuery: (params: Omit<AnswerInlineQueryParams, 'inline_query_id'>) => ReturnType<StarIMBotClient['answerInlineQuery']>;
602
+ };
603
+ type Handler = (ctx: BotContext) => void | Promise<void>;
604
+ declare class StarIMBot {
605
+ readonly client: StarIMBotClient;
606
+ private readonly secretToken;
607
+ private readonly dedupe;
608
+ private readonly handlers;
609
+ private readonly commands;
610
+ private pollingActive;
611
+ private pollingOffset;
612
+ constructor(opts: StarIMBotOptions);
613
+ on(type: UpdateType | '*', handler: Handler): this;
614
+ /**
615
+ * 仅匹配文本消息中以 `/name` 开头的命令(大小写不敏感)。
616
+ */
617
+ command(name: string, handler: Handler): this;
618
+ private dispatch;
619
+ /**
620
+ * Express / Connect 中间件:先挂载 express.raw(例如 type: application/json),再使用本回调。
621
+ */
622
+ webhookCallback(): (req: WebhookRequest, res: WebhookResponse, next?: (err?: unknown) => void) => Promise<void>;
623
+ /**
624
+ * 长轮询模式:循环调用 getUpdates,把 update 喂给与 Webhook 相同的 dispatch 链路。
625
+ *
626
+ * 适用场景:
627
+ * - 没有公网 IP / HTTPS 证书的本机开发;
628
+ * - CI / 测试环境快速接入。
629
+ *
630
+ * 使用前请先 `await client.deleteWebhook()`,否则平台返回 409。
631
+ * 内部维护 offset、自动捕获错误并指数退避重试;调用 `stopPolling()` 优雅退出。
632
+ *
633
+ * @example
634
+ * ```ts
635
+ * await bot.client.deleteWebhook();
636
+ * await bot.startPolling({ timeout: 30 });
637
+ * ```
638
+ */
639
+ startPolling(options?: {
640
+ timeout?: number;
641
+ limit?: number;
642
+ allowedUpdates?: string[];
643
+ /** 平台返回错误时的最大退避秒数,默认 30 */
644
+ maxBackoffSec?: number;
645
+ }): Promise<void>;
646
+ /** 停止 startPolling 的循环(不会立刻打断当前 long-poll,等当次 RPC 返回后退出) */
647
+ stopPolling(): void;
648
+ /**
649
+ * 内置 HTTP 服务,便于快速起 Webhook(生产环境建议使用反向代理 + TLS)。
650
+ */
651
+ start(options: {
652
+ port: number;
653
+ host?: string;
654
+ path?: string;
655
+ }): Promise<Server>;
656
+ private createContext;
657
+ }
658
+
659
+ /**
660
+ * Bot API 或 Webhook 处理中的可恢复错误。
661
+ */
662
+ declare class StarIMApiError extends Error {
663
+ readonly code?: string | number;
664
+ readonly statusCode: number;
665
+ readonly responseBody?: unknown;
666
+ constructor(opts: {
667
+ message: string;
668
+ statusCode: number;
669
+ code?: string | number;
670
+ responseBody?: unknown;
671
+ });
672
+ }
673
+ /**
674
+ * Webhook 签名校验失败。
675
+ */
676
+ declare class StarIMSignatureError extends Error {
677
+ constructor(message?: string);
678
+ }
679
+
680
+ /** 从本地路径读取并上传,返回登记后的 file id */
681
+ declare function uploadFileFromPath(client: StarIMBotClient, filePath: string, options?: {
682
+ fileName?: string;
683
+ fileType?: string;
684
+ }): Promise<CompletedUpload>;
685
+ declare function uploadFromUrl(client: StarIMBotClient, sourceUrl: string, options?: {
686
+ fileName?: string;
687
+ fileType?: string;
688
+ }): Promise<CompletedUpload>;
689
+ declare function sendPhotoFromPath(client: StarIMBotClient, chatId: string, filePath: string, options?: {
690
+ caption?: string;
691
+ fileName?: string;
692
+ fileType?: string;
693
+ }): Promise<unknown>;
694
+ declare function sendDocumentFromPath(client: StarIMBotClient, chatId: string, filePath: string, options?: {
695
+ caption?: string;
696
+ fileName?: string;
697
+ fileType?: string;
698
+ }): Promise<unknown>;
699
+ /**
700
+ * Buffer 上传后发送图片。
701
+ */
702
+ declare function sendPhotoFromBuffer(client: StarIMBotClient, chatId: string, buffer: Buffer, options?: {
703
+ caption?: string;
704
+ fileName?: string;
705
+ fileType?: string;
706
+ }): Promise<unknown>;
707
+ /**
708
+ * 远程 URL 下载后发送图片。
709
+ */
710
+ declare function sendPhotoFromUrl(client: StarIMBotClient, chatId: string, sourceUrl: string, options?: {
711
+ caption?: string;
712
+ fileName?: string;
713
+ fileType?: string;
714
+ }): Promise<unknown>;
715
+ /**
716
+ * 远程 URL 下载后发送视频(先上传再 sendVideo)。
717
+ */
718
+ declare function sendVideoFromUrl(client: StarIMBotClient, chatId: string, sourceUrl: string, options?: {
719
+ caption?: string;
720
+ fileName?: string;
721
+ fileType?: string;
722
+ duration?: number;
723
+ width?: number;
724
+ height?: number;
725
+ }): Promise<unknown>;
726
+
727
+ export { type AnswerCallbackQueryParams, type AnswerFriendRequestParams, type AnswerInlineQueryParams, type BanChatMemberParams, type BotCommand, type BotContext, type BotFriendRequestListItem, type CallbackQuery, type Chat, type CompleteUploadParams, type CompletedUpload, DEFAULT_TIMESTAMP_TOLERANCE_SEC, type DedupeStore, type DeleteMessageParams, type DeleteMyCommandsParams, type EditMessageParams, type EditMessageReplyMarkupParams, type FriendRequestUpdate, type GetMyCommandsParams, type InlineKeyboardButton, type InlineQuery, type IssueUploadCredentialsParams, type KeyboardButton, type KickChatMemberParams, type LoggerFn, LruDedupe, type MessageFile, type MessageLocation, type ProcessWebhookOptions, type ProcessWebhookResult, type ReplyMarkup, type ReplyMarkupInline, type ReplyMarkupRemove, type ReplyMarkupReplyKeyboard, type SendContactParams, type SendDiceParams, type SendLocationParams, type SendMediaGroupItem, type SendMediaGroupParams, type SendMediaParams, type SendMessageParams, type SendPollParams, type SendVenueParams, type SetMyCommandsParams, type SetWebhookParams, StarIMApiError, StarIMBot, StarIMBotClient, type StarIMBotClientOptions, type StarIMBotOptions, StarIMSignatureError, type StarIMUser, type Update, type UpdateType, type UploadCredentials, type VerifyWebhookOptions, type WebhookMessage, type WebhookMiddlewareOptions, type WebhookRequest, type WebhookResponse, computeWebhookSignatureV2, processWebhook, sendDocumentFromPath, sendPhotoFromBuffer, sendPhotoFromPath, sendPhotoFromUrl, sendVideoFromUrl, uploadFileFromPath, uploadFromUrl, verifyWebhookSignature, webhookMiddleware };