{"version":3,"file":"index.cjs","sources":["../src/config.ts","../src/shared/hash.ts","../src/shared/util.ts","../src/error.ts","../src/http/assert.ts","../src/http/simple.ts","../src/auth/session.ts","../src/resource.ts","../src/resources/card/instances.ts","../src/resources/card/card.ts","../src/resources/downloader.ts","../src/resources/uploader.ts","../src/robots/interfaces/outgoing.ts","../src/robots/interfaces/incoming.ts","../src/robots/webhooks/customize.ts","../src/robots/webhooks/session.ts","../src/robots/message/sayable.ts","../src/robots/message/robot.ts","../src/robots/message/room.ts","../src/robots/message/talker.ts","../src/robots/message/message.ts","../src/stream/subscription.ts","../src/stream/client.ts","../src/index.ts"],"sourcesContent":["export const CLIENT_ID = process.env.DINGTALK_CLIENT_ID;\n\nexport const SECRET_KEY = process.env.DINGTALK_SECRET_KEY;\n\n/**\n * API 网关地址\n */\nexport const API_GATEWAY_URL = 'https://api.dingtalk.com/v1.0';\n\n/**\n * 企业内部应用网关地址\n */\nexport const OPEN_API_GATEWAY_URL = 'https://oapi.dingtalk.com/v1.0';\n\n/**\n * 配置\n */\nexport type Configuration = {\n  /**\n   * 应用的唯一标识\n   */\n  clientId?: string | undefined;\n\n  /**\n   * 应用的密钥\n   */\n  clientSecret?: string | undefined;\n\n  // 允许动态属性\n  [key: string]: any;\n};\n\nexport const defaultConfig: Configuration = {\n  clientId: CLIENT_ID,\n  clientSecret: SECRET_KEY,\n};\n","import {\n  type BinaryLike,\n  type BinaryToTextEncoding,\n  createHmac,\n  type KeyObject,\n} from 'node:crypto';\n\nexport function sha256(\n  data: BinaryLike,\n  key: BinaryLike | KeyObject,\n  encoding: BinaryToTextEncoding = 'base64',\n) {\n  return createHmac('sha256', key).update(data).digest(encoding);\n}\n","import { createHmac } from 'node:crypto';\n\nexport const castToError = (err: any): Error => {\n  if (err instanceof Error) return err;\n  return new Error(err);\n};\n\nexport const safeJSON = (text: string) => {\n  try {\n    return JSON.parse(text);\n  } catch (err) {\n    return undefined;\n  }\n};\n\nexport function md5(str: string): string {\n  return createHmac('md5', str).digest('hex');\n}\n","import { castToError } from './shared';\n\nexport class DingtalkError extends Error {}\n\nexport class InvalidArgumentError extends DingtalkError {}\n\nexport class InvalidResultError extends DingtalkError {}\n\nexport class APIError extends DingtalkError {\n  readonly status: number | undefined;\n  readonly headers: Headers | undefined;\n  readonly error: NonNullable<unknown> | undefined;\n\n  readonly code: string | null | undefined;\n\n  constructor(\n    status: number | undefined,\n    error: NonNullable<unknown> | undefined,\n    message: string | undefined,\n    headers: Headers | undefined,\n  ) {\n    super(`${APIError.makeMessage(status, error, message)}`);\n    this.status = status;\n    this.headers = headers;\n\n    const data = error as Record<string, any>;\n    this.error = data;\n    this.code = data?.['code'];\n  }\n\n  private static makeMessage(\n    status: number | undefined,\n    error: any,\n    message: string | undefined,\n  ) {\n    const msg = error?.message\n      ? typeof error.message === 'string'\n        ? error.message\n        : JSON.stringify(error.message)\n      : error\n        ? JSON.stringify(error)\n        : message;\n\n    if (status && msg) {\n      return `${status} ${msg}`;\n    }\n    if (status) {\n      return `${status} status code (no body)`;\n    }\n    if (msg) {\n      return msg;\n    }\n    return '(no status code or body)';\n  }\n\n  static generate(\n    status: number | undefined,\n    errorResponse: NonNullable<unknown> | undefined,\n    message?: string | undefined,\n    headers?: Headers | undefined,\n  ) {\n    if (!status) {\n      return new APIConnectionError({ cause: castToError(errorResponse) });\n    }\n\n    const error =\n      (errorResponse as Record<string, any>)?.['errmsg'] || errorResponse;\n\n    if (status === 400) {\n      return new BadRequestError(status, error, message, headers);\n    }\n\n    if (status === 401) {\n      return new AuthenticationError(status, error, message, headers);\n    }\n\n    if (status === 403) {\n      return new PermissionDeniedError(status, error, message, headers);\n    }\n\n    if (status === 404) {\n      return new NotFoundError(status, error, message, headers);\n    }\n\n    if (status === 409) {\n      return new ConflictError(status, error, message, headers);\n    }\n\n    if (status === 422) {\n      return new UnprocessableEntityError(status, error, message, headers);\n    }\n\n    if (status >= 500) {\n      return new InternalServerError(status, error, message, headers);\n    }\n\n    return new APIError(status, error, message, headers);\n  }\n}\n\nexport class APIUserAbortError extends APIError {\n  override readonly status: undefined = undefined;\n\n  constructor({ message }: { message?: string } = {}) {\n    super(undefined, undefined, message || 'Request was aborted.', undefined);\n  }\n}\n\nexport class APIConnectionError extends APIError {\n  override readonly status: undefined = undefined;\n\n  constructor({\n    message,\n    cause,\n  }: {\n    message?: string;\n    cause?: Error | undefined;\n  }) {\n    super(undefined, undefined, message || 'Connection error.', undefined);\n    // in some environments the \"cause\" property is already declared\n\n    if (cause) this.cause = cause;\n  }\n}\n\nexport class APIConnectionTimeoutError extends APIConnectionError {\n  constructor({ message }: { message?: string } = {}) {\n    super({ message: message ?? 'Request timed out.' });\n  }\n}\n\nexport class BadRequestError extends APIError {\n  override readonly status = 400;\n}\n\nexport class AuthenticationError extends APIError {\n  override readonly status = 401;\n}\n\nexport class PermissionDeniedError extends APIError {\n  override readonly status = 403;\n}\n\nexport class NotFoundError extends APIError {\n  override readonly status = 404;\n}\n\nexport class ConflictError extends APIError {\n  override readonly status = 409;\n}\n\nexport class UnprocessableEntityError extends APIError {\n  override readonly status = 422;\n}\n\nexport class InternalServerError extends APIError {}\n","import { APIError, InvalidResultError } from '../error';\nimport { castToError, safeJSON } from '../shared';\n\nexport type DingtalkResult = Record<string, any>;\n\nexport async function assertJsonFromResponse(\n  responseInit: Response | Promise<Response>,\n): Promise<void> {\n  const response = await responseInit;\n\n  await assertResponse(response);\n  assertResultSync(await response.json());\n}\n\nexport function assertResultSync(result: DingtalkResult): void {\n  if (typeof result.errcode === 'undefined' || result.errcode === 0) return;\n\n  throw new InvalidResultError(result.errmsg);\n}\n\nexport async function assertResponse(response: Response): Promise<void> {\n  if (response.ok) return;\n\n  const errText = await response.text().catch(e => castToError(e).message);\n  const errJSON = safeJSON(errText);\n  const errMessage = errJSON ? undefined : errText;\n\n  throw APIError.generate(\n    response.status,\n    errJSON,\n    errMessage,\n    response.headers as unknown as globalThis.Headers,\n  );\n}\n","import {\n  assertResponse,\n  assertResultSync,\n  type DingtalkResult,\n} from './assert';\n\nexport type SimpleRequestInit = {\n  method: string;\n  headers: Record<string, string>;\n  body?: RequestInit['body'];\n  signal?: AbortSignal;\n};\n\nexport function simple_post_json<R = DingtalkResult>(\n  input: RequestInfo | URL,\n  body?: RequestInit['body'] | undefined,\n  init?: Omit<SimpleRequestInit, 'method' | 'body'>,\n): Promise<R> {\n  const request = new Request(input, {\n    method: 'POST',\n    headers: {\n      Accept: 'application/json',\n      'Content-Type': 'application/json',\n    },\n    body: body,\n  });\n\n  return simple_json(request, init);\n}\n\nexport async function simple_json<R = DingtalkResult>(\n  input: RequestInfo | URL,\n  init?: RequestInit | undefined,\n): Promise<R> {\n  const response = await fetch(input, init);\n\n  await assertResponse(response);\n\n  const result = await response.json();\n\n  assertResultSync(result);\n\n  return result;\n}\n","import { OPEN_API_GATEWAY_URL } from '../config';\nimport { assertResponse, assertResultSync } from '../http';\nimport { type Credentials } from './credentials';\n\ntype TokenResponse = {\n  errcode: number;\n  errmsg: string;\n  access_token: string;\n  expires_in: number; // 7200s\n};\n\n/**\n * 获取企业内部应用的身份凭证\n *\n * @see https://open.dingtalk.com/document/orgapp/obtain-the-access_token-of-an-internal-app\n */\nexport class SessionCredentials {\n  #cache?: TokenResponse;\n  #expiredAt?: number;\n  #promise?: Promise<TokenResponse>;\n\n  constructor(protected credentials: Credentials) {}\n\n  getAccessToken(): Promise<string> {\n    return this.process().then(res => res.access_token);\n  }\n\n  protected process() {\n    const promise = this.#promise;\n    if (promise) return promise;\n\n    if (this.#cache && this.#expiredAt && this.#expiredAt > Date.now()) {\n      return Promise.resolve(this.#cache);\n    }\n\n    this.#promise = this.request().then(res => {\n      this.#cache = res;\n      this.#expiredAt = Date.now() + (res.expires_in - 120) * 1000;\n      return res;\n    });\n\n    this.#promise.finally(() => {\n      this.#promise = undefined;\n    });\n\n    return this.#promise;\n  }\n\n  protected async request(): Promise<TokenResponse> {\n    const { clientId, clientSecret } = this.credentials;\n\n    // cSpell: ignore appkey appsecret gettoken\n    const url = new URL('/gettoken', OPEN_API_GATEWAY_URL);\n\n    const { searchParams } = url;\n\n    searchParams.set('appkey', clientId);\n    searchParams.set('appsecret', clientSecret);\n\n    const response = await fetch(url, {\n      method: 'GET',\n      headers: {\n        Accept: 'application/json',\n        'Content-Type': 'application/json',\n      },\n    });\n\n    await assertResponse(response);\n\n    const result = await response.json();\n\n    assertResultSync(result);\n\n    return result as TokenResponse;\n  }\n}\n","import type { DingtalkClient } from './index';\n\nexport class APIResource {\n  protected client: DingtalkClient;\n\n  protected post: DingtalkClient['post'];\n\n  constructor(client: DingtalkClient) {\n    this.client = client;\n    this.post = client.post.bind(client);\n  }\n}\n","import { APIError } from '../../error';\nimport { APIResource } from '../../resource';\n\nexport type MaybePromise<T> = T | Promise<T>;\n\n/**\n * 互动卡片\n */\nexport class Instances extends APIResource {\n  /**\n   * 创建卡片\n   *\n   * @param params -\n   * @returns\n   */\n  async create(params: CardInstancesCreateParams): Promise<string> {\n    return this.afterResponse<string>(\n      this.post('/card/instances', JSON.stringify(params)),\n    );\n  }\n\n  /**\n   * 投放卡片\n   *\n   * @param params -\n   */\n  async deliver(params: CardInstancesDeliverParams) {\n    return this.afterResponse<unknown[]>(\n      this.post('/card/instances/deliver', JSON.stringify(params)),\n    );\n  }\n\n  /**\n   * 发送卡片\n   *\n   * @param params -\n   * @returns\n   */\n  async send(params: CardInstancesSendParams) {\n    // 私聊发送 chatBotId 和 robotCode 会报错\n    if (params.conversationType === 0) {\n      delete params.chatBotId\n      delete params.robotCode\n    }\n\n    return this.afterResponse<{ processQueryKey: string }>(\n      this.post('/im/interactiveCards/send', JSON.stringify(params)),\n    );\n  }\n\n  protected async afterResponse<T>(\n    resultInit: MaybePromise<CardInstanceResponse<T> | CardInstanceError>,\n  ): Promise<T> {\n    const result = await resultInit;\n\n    if ('success' in result && result.success) {\n      return result.result;\n    }\n\n    throw APIError.generate(\n      400,\n      undefined,\n      (result as CardInstanceError).message,\n    );\n  }\n}\n\n/**\n * 创建互动卡片实例\n *\n * @see https://open.dingtalk.com/document/orgapp/interface-for-creating-a-card-instance\n */\nexport interface CardInstancesCreateParams {\n  /**\n   * 卡片创建者的用户 ID\n   */\n  userId?: string;\n\n  /**\n   * 用户 ID 类型\n   *\n   * 1 userId\n   * 2 unionId\n   */\n  userIdType?: number;\n\n  /**\n   * 卡片内容模板ID\n   *\n   * @see https://open-dev.dingtalk.com/fe/card\n   */\n  cardTemplateId: string;\n\n  /**\n   * 外部卡片实例Id\n   */\n  outTrackId: string;\n\n  /**\n   * 卡片回调的类型\n   *\n   * - STREAM\n   * - HTTP\n   *\n   * 注意参数均为大写\n   */\n  callbackType?: 'STREAM' | 'HTTP';\n\n  /**\n   * 卡片回调HTTP模式时的路由 Key，用于查询注册的 callbackUrl\n   */\n  callbackRouteKey?: string;\n\n  /**\n   * 卡片数据\n   */\n  cardData: CardData;\n  /**\n   * 私有数据\n   *\n   * key 是用户 ID\n   */\n  privateData?: Record<string, CardData>;\n\n  /**\n   * 动态数据源配置\n   */\n  openDynamicDataConfig?: {\n    /**\n     * 动态数据源配置列表\n     */\n    dynamicDataSourceConfigs?: OpenDynamicDataConfig.SourceConfig[];\n  };\n  /**\n   * \tIM 群聊场域信息\n   */\n  imGroupOpenSpaceModel?: OpenSpaceModel;\n\n  /**\n   * IM 单聊场域信息\n   */\n  imRobotOpenSpaceModel?: OpenSpaceModel;\n\n  /**\n   * 协作场域信息\n   */\n  coFeedOpenSpaceModel?: {\n    /**\n     * 卡片标题\n     */\n    title?: string;\n\n    /**\n     * 吊顶场域属性，通过增加spaeType使卡片支持吊顶场域\n     */\n    topOpenSpaceModel?: {\n      spaceType: string;\n    };\n  };\n}\n\n/**\n * 卡片模板内容替换参数\n */\nexport type CardParamMap = Record<string, string>;\n\nexport type CardData = {\n  cardParamMap?: CardParamMap & { sys_full_json_obj?: string };\n};\n\nexport namespace OpenDynamicDataConfig {\n  export enum PullStrategy {\n    /**\n     * 不拉取，无动态数据\n     */\n    None = 'NONE',\n    /**\n     * 间隔拉取\n     */\n    Interval = 'INTERVAL',\n    /**\n     * 拉取一次\n     */\n    Once = 'ONCE',\n  }\n\n  export enum TimeUnit {\n    Seconds = 'SECONDS',\n    Minutes = 'MINUTES',\n    Hours = 'HOURS',\n    Days = 'DAYS',\n  }\n\n  export type SourceConfig = {\n    /**\n     * 数据源的唯一 ID, 调用方指定\n     */\n    dynamicDataSourceId?: string;\n    /**\n     * 回调数据源时回传的固定参数\n     */\n    constParams?: Record<string, string>;\n    /**\n     * 数据源拉取配置\n     */\n    pullConfig?: {\n      /**\n       * 拉取策略\n       */\n      pullStrategy?: PullStrategy;\n      /**\n       * 拉取的间隔时间\n       *\n       * - 只在将pullStrategy设置为INTERVAL的时候生效。\n       * - 最小拉取间隔时间3s。\n       */\n      interval?: number;\n      /**\n       * 拉取的间隔时间的单位\n       */\n      timeUnit?: TimeUnit;\n    };\n  };\n}\n\nexport type OpenSpaceModel = {\n  /**\n   * 是否支持转发\n   */\n  supportForward: boolean;\n\n  /**\n   * 支持国际化的LastMessage，目前支持的语言枚举值：\n   *\n   * ZH_CN：简体中文\n   * ZH_TW：繁体中文:\n   * EN_US：英文\n   * JA_JP：日语\n   * VI_VN：越南语\n   */\n  lastMessageI18n?: Map<I18n.Language, string>;\n\n  /**\n   * 支持卡片消息可被搜索字段\n   */\n  searchSupport?: {\n    /**\n     * 类型的icon，供搜索展示使用。\n     */\n    searchIcon?: string;\n    /**\n     * 卡片类型名\n     */\n    searchTypeName?: string;\n\n    /**\n     * 供消息展示与搜索的字段\n     */\n    searchDesc?: string;\n  };\n\n  /**\n   * 通知信息\n   */\n  notification?: {\n    /**\n     * 通知内容\n     *\n     * @defaultValue 如你收到 1 条新消息\n     */\n    alertContent?: string;\n\n    /**\n     * 是否关闭推送通知\n     */\n    notificationOff?: boolean;\n  };\n};\n\nexport namespace I18n {\n  export enum Language {\n    /**\n     * 简体中文\n     */\n    ZH_CN = 'ZH_CN',\n    /**\n     * 繁体中文\n     */\n    ZH_TW = 'ZH_TW',\n    /**\n     * 英文\n     */\n    EN_US = 'EN_US',\n    /**\n     * 日语\n     */\n    JA_JP = 'JA_JP',\n    /**\n     * 越南语\n     */\n    VI_VN = 'VI_VN',\n  }\n}\n\nexport type CardInstanceResponse<T> = {\n  success: boolean;\n  result: T;\n};\n\nexport type CardInstanceError = {\n  code: string;\n  requestId: string;\n  message: string;\n};\n\nexport type CardInstancesDeliverParams = {\n  /**\n   * 外部卡片实例 ID\n   */\n  outTrackId: string;\n\n  /**\n   * 卡片投放的场域\n   */\n  openSpaceId: string;\n\n  /**\n   * IM机器人单聊投放参数\n   */\n  imRobotOpenDeliverModel?: {\n    robotCode?: string;\n    spaceType?: string;\n    extension?: Record<string, string>;\n  };\n\n  /**\n   * 群聊投放参数\n   */\n  imGroupOpenDeliverModel?: {\n    robotCode?: string;\n    atUserIds: Record<string, string>;\n    recipients?: string[];\n    extension?: Record<string, string>;\n  };\n\n  /**\n   * 吊顶投放参数\n   */\n  topOpenDeliverModel?: {\n    expiredTimeMillis?: string;\n    userIds?: string[];\n    platforms: string[];\n  };\n\n  /**\n   * 协作投放参数\n   */\n  coFeedOpenDeliverModel?: {\n    /**\n     * 业务标识\n     */\n    bizTag?: string;\n    /**\n     * 协作场域下的排序时间\n     */\n    gmtTimeLine: string;\n  };\n\n  /**\n   * 文档投放参数\n   */\n  docOpenDeliverModel?: {\n    userId?: string;\n  };\n\n  userIdType?: number;\n};\n\n/**\n * 发送互动卡片\n *\n * @see https://open.dingtalk.com/document/orgapp-server/send-dingtalk-interactive-cards\n */\nexport type CardInstancesSendParams = {\n  /**\n   * 互动卡片的消息模板ID\n   */\n  cardTemplateId: string;\n\n  /**\n   * 唯一标示卡片的外部编码\n   */\n  outTrackId: string;\n\n  /**\n   * 机器人唯一编码\n   */\n  robotCode?: string;\n\n  /**\n   * 机器人ID\n   */\n  chatBotId?: string;\n\n  /**\n   * 群ID\n   */\n  openConversationId: string;\n\n  /**\n   * 会话类型\n   *\n   * - 0 单聊\n   * - 1 群聊\n   *\n   * @remarks 和消息的会话类型不同\n   */\n  conversationType: number;\n\n  /**\n   * 接收用户列表\n   */\n  receiverUserIdList: string[];\n\n  /**\n   * 消息提及的人\n   */\n  atOpenIds?: Record<string, string>;\n\n  /**\n   * 用户ID类型\n   */\n  userIdType?: number;\n\n  /**\n   * 卡片回调时的路由Key，用于查询注册的callbackUrl\n   */\n  callbackRouteKey?: string;\n\n  /**\n   * 卡片数据\n   */\n  cardData: CardData;\n\n  /**\n   * 卡片选项\n   */\n  cardOptions?: {\n    supportForward: boolean;\n  };\n\n  /**\n   * 私有数据\n   */\n  privateData?: Record<string, CardData>;\n\n  /**\n   * 拉取策略\n   *\n   * - true：开启卡片纯拉模式\n   * - false：不开启卡片纯拉模式\n   */\n  pullStrategy?: boolean;\n};\n","import { APIResource } from '../../resource'\n\nimport * as InstancesAPI from './instances'\n\nexport class Card extends APIResource {\n  instances: InstancesAPI.Instances = new InstancesAPI.Instances(this.client);\n}\n","import { createWriteStream } from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { Readable } from 'node:stream';\nimport { finished } from 'node:stream/promises';\nimport { type ReadableStream as WebReadableStream } from 'node:stream/web';\n\nimport { API_GATEWAY_URL } from '../config';\nimport { InvalidResultError } from '../error';\nimport { APIResource } from '../resource';\n\ntype ExchangeCodeResult = {\n  // cSpell: ignore requestid\n  requestid: string;\n  message?: string;\n  code?: string;\n  /**\n   * 错误码\n   */\n  errcode: number;\n  /**\n   * 错误信息\n   */\n  errmsg: string;\n  /**\n   * 文件下载地址\n   */\n  downloadUrl: string;\n};\n\n/**\n * 临时下载码\n *\n * @see https://open.dingtalk.com/document/orgapp/download-the-file-content-of-the-robot-receiving-message\n */\nexport type DownloadCode = string;\n\nexport class Downloader extends APIResource {\n  endpoint = '/robot/messageFiles/download';\n\n  /**\n   * 使用下载码交换文件地址\n   *\n   * @param downloadCode - 下载码\n   * @param robotCode - 机器人Code\n   */\n  async exchangeCode(\n    downloadCode: DownloadCode,\n    robotCode?: string, // TODO 支持从全局配置的企业应用ID中获取\n  ): Promise<string> {\n    const { client } = this;\n    const url = new URL(`${API_GATEWAY_URL}${this.endpoint}`);\n\n    const response = await fetch(url, {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n        'x-acs-dingtalk-access-token': await client.getAccessToken(),\n      },\n      body: JSON.stringify({\n        downloadCode: downloadCode,\n        robotCode: robotCode || client.clientId,\n      }),\n    });\n\n    const result: ExchangeCodeResult = await response.json();\n\n    if (result.downloadUrl) return result.downloadUrl;\n\n    throw new InvalidResultError(result.errmsg || result.message);\n  }\n\n  /**\n   * 下载文件到指定位置\n   *\n   * @param url - 下载地址\n   * @param filePath - 文件路径\n   * @returns 文件路径\n   */\n  downloadToPath(url: string, filePath: string): Promise<string> {\n    return this.fetchStream(url).then(stream => this.saveTo(stream, filePath));\n  }\n\n  /**\n   * 下载文件到临时目录\n   *\n   * @param url - 下载地址\n   * @param filename - 文件名\n   * @returns 临时文件路径\n   */\n  downloadToTmp(url: string, name?: string): Promise<string> {\n    return this.fetchStream(url).then(stream => {\n      const filename = name || resolveNameFromURl(url);\n      return this.saveTo(stream, path.join(os.tmpdir(), filename));\n    });\n  }\n\n  protected fetchStream(url: string): Promise<WebReadableStream> {\n    return fetch(url).then(res => {\n      const stream = res.body! as WebReadableStream;\n      if (stream) return stream;\n\n      throw new TypeError('No readable stream');\n    });\n  }\n\n  /**\n   * 保存数据流到文件\n   *\n   * @param stream - 数据流\n   * @param filePath - 绝对路径\n   * @returns 文件路径\n   */\n  protected saveTo(\n    stream: WebReadableStream,\n    filePath: string,\n  ): Promise<string> {\n    const writeStream = createWriteStream(filePath);\n\n    Readable.fromWeb(stream).pipe(writeStream);\n\n    return finished(writeStream).then(() => filePath);\n  }\n}\n\nfunction resolveNameFromURl(url: string) {\n  const { pathname } = new URL(url);\n  return path.basename(pathname);\n}\n","import { readFileSync } from 'node:fs';\n\nimport { OPEN_API_GATEWAY_URL } from '../config';\nimport { InvalidResultError } from '../error';\nimport { APIResource } from '../resource';\n\n/**\n * 媒体文件类型\n *\n * - image：图片，图片最大20MB。支持上传jpg、gif、png、bmp格式。\n * - voice：语音，语音文件最大2MB。支持上传amr、mp3、wav格式。\n * - video：视频，视频最大20MB。支持上传mp4格式。\n * - file：普通文件，最大20MB。支持上传doc、docx、xls、xlsx、ppt、pptx、zip、pdf、rar格式。\n */\nexport type MediaType = 'image' | 'voice' | 'file';\n\n/**\n * 上传结果\n */\nexport type UploadResult<T extends MediaType = MediaType> = {\n  /**\n   * 错误码\n   */\n  errcode: number;\n  /**\n   * 错误信息\n   */\n  errmsg: string;\n  /**\n   * 媒体文件类型\n   */\n  type: T;\n  /**\n   * 媒体文件上传后获取的唯一标识。\n   */\n  media_id: string;\n  /**\n   * 媒体文件上传时间戳。\n   */\n  created_at: number;\n};\n\nexport class Uploader extends APIResource {\n  endpoint = '/media/upload';\n\n  /**\n   * 从远程链接获取文件并上传\n   *\n   * @param type - 媒体文件类型\n   * @param url - 下载地址\n   * @param name - 文件名\n   * @returns 上传结果\n   */\n  async putFromURL<Type extends MediaType = MediaType>(\n    type: Type,\n    url: string,\n    name?: string,\n  ): Promise<UploadResult<Type>> {\n    const downloader = this.client.downloader;\n    const filePath = await downloader.downloadToTmp(url, name);\n\n    return this.putFromPath(type, filePath, name);\n  }\n\n  /**\n   * 从本地文件上传\n   *\n   * @param type - 上传文件类型\n   * @param path - 文件路径\n   * @param filename - 文件名\n   * @returns 上传结果\n   */\n  async putFromPath<Type extends MediaType = MediaType>(\n    type: Type,\n    path: string,\n    name?: string,\n  ): Promise<UploadResult<Type>> {\n    return this.putFromObject(type, readFileSync(path), name);\n  }\n\n  /**\n   * 从文件内容上传\n   *\n   * @see https://open.dingtalk.com/document/orgapp/upload-media-files\n   * @param type - 上传类型\n   * @param data - 文件内容\n   * @param filename - 文件名\n   * @returns 上传结果\n   */\n  async putFromObject<Type extends MediaType = MediaType>(\n    type: Type,\n    data: BlobPart,\n    filename?: string,\n  ): Promise<UploadResult<Type>> {\n    const { client } = this;\n    const url = new URL(this.endpoint, OPEN_API_GATEWAY_URL);\n\n    url.searchParams.set('access_token', await client.getAccessToken());\n\n    const form = new FormData();\n\n    form.append('type', type);\n    form.append('media', new Blob([data]), filename);\n\n    const response = await fetch(url, {\n      method: 'POST',\n      headers: {\n        Accept: 'application/json',\n      },\n      body: form,\n    });\n\n    const result: UploadResult<Type> = await response.json();\n\n    if (result.errcode === 0) return result;\n\n    throw new InvalidResultError(result.errmsg);\n  }\n}\n","export namespace Outgoing {\n  /**\n   * 空消息\n   *\n   * 当不想回复消息到群里时，可以使用空消息\n   */\n  export type EmptyMessage = {\n    msgtype: 'empty';\n  };\n\n  /**\n   * 文本消息\n   */\n  export type TextMessage = WithAt<{\n    msgtype: 'text';\n    text: {\n      content: string;\n    };\n  }>;\n\n  /**\n   * Markdown 消息\n   */\n  export type MarkdownMessage = WithAt<{\n    msgtype: 'markdown';\n    markdown: {\n      title: string;\n      text: string;\n    };\n  }>;\n\n  /**\n   * 链接消息\n   */\n  export type LinkMessage = {\n    msgtype: 'link';\n    link: {\n      text: string;\n      title: string;\n      picUrl: string;\n      messageUrl: string;\n    };\n  };\n\n  /**\n   * webhook 方式不支持\n   */\n  export type ImageMessage = {\n    msgtype: 'picture';\n    image: {\n      photoURL: string;\n    };\n  };\n\n  export type AudioMessage = {\n    msgtype: 'audio';\n    audio: {\n      mediaId: string;\n      duration: number;\n    };\n  };\n\n  export type VideoMessage = {\n    msgtype: 'video';\n    video: {\n      /**\n       * 视频封面图片媒体文件ID\n       *\n       * 通过上传媒体文件接口得到的 mediaId\n       */\n      picMediaId: string;\n      /**\n       * 视频媒体文件ID\n       *\n       * 通过上传媒体文件接口得到的 mediaId\n       */\n      videoMediaId: string;\n      /**\n       * 视频类型，支持mp4格式。\n       */\n      videoType: number;\n      /**\n       * 视频时长，单位：秒\n       */\n      duration: string;\n      /**\n       * 视频展示宽度，单位px\n       */\n      width?: string;\n      /**\n       * 视频展示高度，单位px\n       */\n      height?: string;\n    };\n  };\n\n  export type FileMessage = {\n    msgtype: 'file';\n    file: {\n      mediaId: string;\n      fileName: string;\n      fileType: string;\n    };\n  };\n\n  export type SingleButtonActionCard = {\n    /**\n     * 首屏会话透出的展示内容\n     */\n    title: string;\n    /**\n     * markdown 格式的消息\n     */\n    text: string;\n    /**\n     * 单个按钮的标题\n     */\n    singleTitle: string;\n    /**\n     * 点击 singleTitle 按钮触发的URL\n     */\n    singleURL: string;\n  };\n\n  export enum ActionCardButtonOrientation {\n    Vertical = '0',\n    Horizontal = '1',\n  }\n\n  export type ActionCardButtonStyle = {\n    /**\n     * 按钮标题\n     */\n    title: string;\n    /**\n     * 点击按钮触发的URL\n     */\n    actionURL: string;\n  };\n\n  export type MultiButtonActionCard = {\n    /**\n     * 首屏会话透出的展示内容\n     */\n    title: string;\n    /**\n     * markdown 格式的消息\n     */\n    text: string;\n    /**\n     * 按钮排列顺序\n     *\n     * 0: 按钮竖直排列\n     * 1: 按钮横向排列\n     */\n    btnOrientation: ActionCardButtonOrientation;\n    /**\n     * 按钮列表\n     */\n    btns: ActionCardButtonStyle[];\n  };\n\n  /**\n   * ActionCard 消息\n   */\n  export type ActionCardMessage = {\n    msgtype: 'actionCard';\n    actionCard: SingleButtonActionCard | MultiButtonActionCard;\n  };\n\n  export type FeedLink = {\n    title: string;\n    messageURL: string;\n    picURL: string;\n  };\n\n  /**\n   * FeedCard 消息\n   *\n   * 接口方式不支持\n   */\n  export type FeedCardMessage = {\n    msgtype: 'feedCard';\n    feedCard: {\n      links: FeedLink[];\n    };\n  };\n\n  /**\n   * 机器人发送的消息类型\n   */\n  export enum ContentType {\n    Empty = 'empty',\n    Text = 'text',\n    Markdown = 'markdown',\n    Link = 'link',\n    Image = 'image',\n    Audio = 'audio',\n    Video = 'video',\n    File = 'file',\n    ActionCard = 'actionCard',\n    FeedCard = 'feedCard',\n  }\n\n  type At = {\n    atUserIds?: string[];\n    atMobiles?: string[];\n    isAtAll?: boolean;\n  };\n\n  type WithAt<T> = T & { at?: At };\n\n  type MessageMap = {\n    [ContentType.Empty]: EmptyMessage;\n    [ContentType.Text]: TextMessage;\n    [ContentType.Markdown]: MarkdownMessage;\n    [ContentType.Link]: LinkMessage;\n    [ContentType.Image]: ImageMessage;\n    [ContentType.Audio]: AudioMessage;\n    [ContentType.Video]: VideoMessage;\n    [ContentType.File]: FileMessage;\n    [ContentType.ActionCard]: ActionCardMessage;\n    [ContentType.FeedCard]: FeedCardMessage;\n  };\n\n  /**\n   * 机器人发送的消息类型\n   *\n   * https://open.dingtalk.com/document/orgapp/robot-message-types-and-data-format\n   */\n  export type Message = MessageMap[ContentType];\n}\n","import { Outgoing } from './outgoing';\n\nexport namespace Incoming {\n  /**\n   * 机器人发送的消息类型\n   */\n  export enum ContentType {\n    Text = 'text',\n    Audio = 'audio',\n    Video = 'video',\n    Image = 'picture',\n    RichText = 'richText',\n    File = 'file',\n  }\n\n  export namespace RichText {\n    export type TextSection = {\n      text: string;\n    };\n\n    export type ImageSection = {\n      type: 'picture';\n      downloadCode: string;\n      pictureDownloadCode: string;\n    };\n\n    export type Section = TextSection | ImageSection;\n  }\n\n  export type RichTextSection = RichText.Section;\n\n  export type RichTextMessage = {\n    msgtype: 'richText';\n    content: {\n      richText: RichTextSection[];\n    };\n  };\n\n  export type ImageMessage = {\n    msgtype: 'picture';\n    content: {\n      downloadCode: string;\n      pictureDownloadCode: string;\n    };\n  };\n\n  export type AudioMessage = {\n    msgtype: 'audio';\n    content: {\n      duration: number;\n      downloadCode: string;\n      recognition: string; // 语音识别结果\n    };\n  };\n\n  export type VideoMessage = {\n    msgtype: 'video';\n    content: {\n      duration: number;\n      downloadCode: string;\n      videoType: string;\n    };\n  };\n\n  export type FileMessage = {\n    msgtype: 'file';\n    content: {\n      spaceId: string;\n      field: string;\n      downloadCode: string;\n      fileName: string;\n    };\n  };\n\n  type MessageMap = {\n    [ContentType.Text]: Outgoing.TextMessage;\n    [ContentType.RichText]: RichTextMessage;\n    [ContentType.Image]: ImageMessage;\n    [ContentType.Audio]: AudioMessage;\n    [ContentType.Video]: VideoMessage;\n    [ContentType.File]: FileMessage;\n  };\n\n  export type Message = MessageMap[ContentType];\n}\n","import { debuglog } from 'node:util';\n\nimport { OPEN_API_GATEWAY_URL } from '../../config';\nimport { simple_post_json } from '../../http';\nimport { sha256 } from '../../shared/hash';\nimport { Outgoing } from '../interfaces/outgoing';\n\nconst debug = debuglog('dingtalk.robots.session-robot');\n\nexport type CustomizeWebhookSettings = {\n  /**\n   * 钉钉开放平台网关地址\n   *\n   * @defaultValue https://oapi.dingtalk.com\n   */\n  apiBaseUrl?: string;\n\n  /**\n   * 机器人接口地址\n   */\n  endpoint?: string;\n\n  /**\n   * 授权令牌\n   */\n  token: string;\n\n  /**\n   * 加密密钥\n   *\n   * see https://open.dingtalk.com/document/robots/customize-robot-security-settings\n   */\n  secret?: string;\n};\n\n/**\n * 自定义机器人接入\n *\n * @see https://open.dingtalk.com/document/isvapp/custom-bot-access-send-message\n */\nexport class CustomizeWebhook {\n  token: string;\n  secret?: string;\n\n  apiBaseUrl: string;\n  endpoint: string;\n\n  constructor(settings: CustomizeWebhookSettings) {\n    this.token = settings.token;\n    this.secret = settings.secret;\n\n    this.apiBaseUrl = settings.apiBaseUrl ?? OPEN_API_GATEWAY_URL;\n    this.endpoint = settings.endpoint ?? '/robot/send';\n  }\n\n  say(sayable: string | Outgoing.Message): Promise<void> {\n    if (typeof sayable === 'string') {\n      return this.send({\n        msgtype: 'text',\n        text: {\n          content: sayable,\n        },\n      });\n    }\n\n    return this.send(sayable);\n  }\n\n  async send(message: Outgoing.Message): Promise<void> {\n    await simple_post_json(this.buildURL(), JSON.stringify(message));\n  }\n\n  protected buildURL(): string {\n    const url = new URL(`${this.apiBaseUrl}${this.endpoint}`);\n\n    const { searchParams } = url;\n\n    searchParams.set('access_token', this.token);\n\n    if (this.secret) {\n      const timestamp = Date.now();\n      const stringToSign = `${timestamp}\\n${this.secret}`;\n      const signature = encodeURIComponent(sha256(stringToSign, this.secret));\n\n      searchParams.set('timestamp', timestamp.toString());\n      searchParams.set('sign', signature);\n    }\n\n    debug('url: %s', url.toString());\n\n    return url.toString();\n  }\n}\n","import { debuglog } from 'node:util';\n\nimport { simple_post_json } from '../../http';\nimport { Outgoing } from '../interfaces/outgoing';\n\nconst debug = debuglog('dingtalk.robots.session-robot');\n\nexport type SessionWebhookSettings = {\n  /**\n   * 当前会话的 Webhook 地址\n   */\n  sessionWebhook: string;\n\n  /**\n   * 当前会话的 Webhook 地址过期时间\n   */\n  sessionWebhookExpiredTime: number;\n};\n\n/**\n * see https://open.dingtalk.com/document/orgapp/receive-message\n */\nexport class SessionWebhook {\n  sessionWebhook: string;\n  sessionWebhookExpiredTime: number;\n\n  constructor(settings: SessionWebhookSettings) {\n    this.sessionWebhook = settings.sessionWebhook;\n    this.sessionWebhookExpiredTime = settings.sessionWebhookExpiredTime;\n  }\n\n  is_expired(): boolean {\n    return this.sessionWebhookExpiredTime < Date.now();\n  }\n\n  async send(message: Outgoing.Message): Promise<void> {\n    // 忽略过期的 Webhook\n    if (this.is_expired()) {\n      debug('[SessionRobot] %s Webhook 过期，已忽略发送的消息');\n      return;\n    }\n\n    await simple_post_json(this.sessionWebhook, JSON.stringify(message));\n  }\n}\n","import { Outgoing } from '../interfaces';\n\nexport type Sayable = string | Outgoing.Message;\n\nexport abstract class SayableSayer {\n  async say(sayable: Sayable): Promise<any>;\n  async say(sayable: Sayable, atAll: true): Promise<any>;\n  async say(sayable: Sayable, mentionList: string[]): Promise<any>;\n  async say(sayable: Sayable, mentionList?: true | string[]): Promise<any> {\n    const message = await this.resolve(sayable);\n\n    if (!mentionList) return this.send(message);\n\n    return this.send(this.mention(message, mentionList as string[]));\n  }\n\n  protected abstract send(message: Outgoing.Message): Promise<void>;\n\n  protected mention(message: Outgoing.Message): Outgoing.Message;\n  protected mention(message: Outgoing.Message, atAll: true): Outgoing.Message;\n  protected mention(\n    message: Outgoing.Message,\n    mentionList: string[],\n  ): Outgoing.Message;\n  protected mention(\n    message: Outgoing.Message,\n    mentionList?: true | string[],\n  ): Outgoing.Message {\n    if (!mentionList) return message;\n\n    if (mentionList === true) {\n      message = this.atAll(message);\n    } else {\n      message = this.atUserIds(message, mentionList);\n    }\n\n    return message;\n  }\n\n  protected atAll(message: Outgoing.Message): Outgoing.Message {\n    if (\n      message.msgtype === Outgoing.ContentType.Text ||\n      message.msgtype === Outgoing.ContentType.Markdown\n    ) {\n      message.at = { isAtAll: true };\n    }\n\n    return message;\n  }\n\n  protected atUserIds(\n    message: Outgoing.Message,\n    userIds: string[],\n  ): Outgoing.Message {\n    if (\n      userIds.length === 0 ||\n      !(\n        Outgoing.ContentType.Text === message.msgtype ||\n        Outgoing.ContentType.Markdown === message.msgtype\n      )\n    ) {\n      return message;\n    }\n\n    if (userIds.length === 0) return message;\n\n    if (message.msgtype === Outgoing.ContentType.Text) {\n      message.text.content += ` ${userIds.map(id => `@${id}`).join(' ')}`;\n    } else if (message.msgtype === Outgoing.ContentType.Markdown) {\n      message.markdown.text += ` \\n\\n------\\n\\n ###### 此消息通知对象: ${userIds\n        .map(userId => `@${userId}`)\n        .join(' ')}`;\n    }\n\n    message.at = {\n      atUserIds: userIds,\n    };\n\n    return message;\n  }\n\n  // TODO 支持媒体消息，并且自动上传\n  protected async resolve(sayable: Sayable): Promise<Outgoing.Message> {\n    if (typeof sayable === 'string') {\n      return {\n        msgtype: Outgoing.ContentType.Text,\n        text: {\n          content: sayable,\n        },\n      };\n    }\n\n    return sayable;\n  }\n}\n","import { Outgoing, type RobotProperties } from '../interfaces';\nimport { SessionWebhook } from '../webhooks/session';\nimport { type IncomingMessage } from './message';\nimport { SayableSayer } from './sayable';\n\nexport class Robot extends SayableSayer {\n  id: string;\n\n  code: string;\n\n  corpId: string;\n\n  webhook: SessionWebhook;\n\n  constructor(\n    public incoming: IncomingMessage, // TODO 暂定，用于调用其他 API\n    {\n      chatbotUserId,\n      robotCode,\n      chatbotCorpId,\n      sessionWebhook,\n      sessionWebhookExpiredTime,\n    }: RobotProperties,\n  ) {\n    super();\n\n    this.id = chatbotUserId;\n    this.code = robotCode;\n    this.corpId = chatbotCorpId;\n\n    this.webhook = new SessionWebhook({\n      sessionWebhook,\n      sessionWebhookExpiredTime,\n    });\n  }\n\n  protected send(message: Outgoing.Message): Promise<void> {\n    return this.webhook.send(message);\n  }\n}\n","import { type ConversationProperties } from '../interfaces/conversation';\nimport { type IncomingMessage } from './message';\nimport { type Sayable } from './sayable';\n\nexport class Room {\n  id: string;\n\n  name: string;\n\n  constructor(\n    protected incoming: IncomingMessage,\n    properties: ConversationProperties,\n  ) {\n    this.id = properties.conversationId;\n    this.name = properties.conversationTitle!;\n  }\n\n  // 快捷方式\n  async say(sayable: Sayable): Promise<any>;\n  async say(sayable: Sayable, atAll: true): Promise<any>;\n  async say(sayable: Sayable, mentionList: string[]): Promise<any>;\n  async say(sayable: Sayable, mentionList?: true | string[]): Promise<any> {\n    return this.incoming.say(sayable, mentionList as string[]);\n  }\n\n  async reply(sayable: Sayable) {\n    return this.incoming.reply(sayable);\n  }\n}\n","import { type TalkerProperties } from '../interfaces';\nimport { type IncomingMessage } from './message';\nimport { type Sayable } from './sayable';\n\nexport class Talker {\n  /**\n   * 用户ID\n   *\n   */\n  id: string;\n  /**\n   * 企业ID\n   */\n  corpId: string;\n  /**\n   * 员工ID\n   */\n  staffId: string;\n  /**\n   * 昵称\n   */\n  name: string;\n  /**\n   * 是否管理员\n   */\n  isAdmin: boolean;\n\n  constructor(\n    protected incoming: IncomingMessage,\n    properties: TalkerProperties,\n  ) {\n    this.id = properties.senderId;\n    this.corpId = properties.senderCorpId;\n    this.staffId = properties.senderStaffId;\n    this.name = properties.senderNick;\n    this.isAdmin = properties.isAdmin;\n  }\n\n  reply(sayable: Sayable): Promise<void> {\n    return this.incoming.reply(sayable);\n  }\n}\n","import DingtalkClient from '../../index';\nimport { type Conversation, Incoming } from '../interfaces';\nimport { SessionWebhook } from '../webhooks/session';\nimport { Robot } from './robot';\nimport { Room } from './room';\nimport { type Sayable } from './sayable';\nimport { Talker } from './talker';\n\n// TODO 支持获取引用的消息\n// TODO 支持消息转文件\nexport class IncomingMessage {\n  Type = Incoming.ContentType;\n\n  conversationId: string;\n  conversationType: '2' | '1';\n\n  id: string;\n  type: Incoming.ContentType;\n  text: string;\n  age: number;\n  createAt: number;\n\n  room?: Room | undefined;\n\n  robot: Robot;\n\n  talker: Talker;\n\n  webhook: SessionWebhook;\n\n  constructor(\n    protected client: DingtalkClient,\n    public properties: Conversation,\n  ) {\n    const {\n      // 会话\n      conversationId,\n      conversationType,\n      // 消息\n      msgId,\n      msgtype,\n      createAt,\n      // Webhook\n      sessionWebhook,\n      sessionWebhookExpiredTime,\n    } = properties;\n\n    this.conversationId = conversationId;\n    this.conversationType = conversationType;\n\n    this.id = msgId;\n    this.type = msgtype as Incoming.ContentType;\n    this.age = Date.now() - createAt;\n    this.createAt = createAt;\n\n    this.webhook = new SessionWebhook({\n      sessionWebhook,\n      sessionWebhookExpiredTime,\n    });\n\n    if (conversationType === '2') {\n      this.room = new Room(this, properties);\n    }\n\n    this.robot = new Robot(this, properties);\n    this.talker = new Talker(this, properties);\n\n    // 从支持的消息类型中获取文本内容\n    this.text = resolveTextContent(properties);\n  }\n\n  /**\n   * 获取富文本内容\n   *\n   * @returns 富文本节点\n   */\n  richText(): Incoming.RichTextSection[] | void {\n    const { properties } = this;\n\n    if (properties.msgtype === Incoming.ContentType.RichText) {\n      return properties.content.richText;\n    }\n  }\n\n  /**\n   * 直接回复消息给发送者\n   *\n   * @param sayable - 消息内容\n   * @returns\n   */\n  reply(sayable: Sayable) {\n    return this.say(sayable, [this.talker.staffId]);\n  }\n\n  /**\n   * 可以发送给任何消息给有权限的人\n   *\n   * @param sayable - 消息内容\n   */\n  async say(sayable: Sayable): Promise<void>;\n  async say(sayable: Sayable, atAll: true): Promise<void>;\n  async say(sayable: Sayable, mentionList: string[]): Promise<void>;\n  async say(sayable: Sayable, mentionList?: true | string[]): Promise<void> {\n    if (this.conversationType === '1') {\n      return this.robot.say(sayable);\n    }\n\n    return this.robot.say(sayable, mentionList as string[]);\n  }\n\n  async unstable__getFileURL(): Promise<string | void> {\n    const {\n      properties,\n      client: { downloader },\n    } = this;\n\n    if (properties.msgtype === Incoming.ContentType.Image) {\n      return downloader.exchangeCode(properties.content.downloadCode);\n    }\n\n    if (properties.msgtype === Incoming.ContentType.File) {\n      return downloader.exchangeCode(properties.content.downloadCode);\n    }\n  }\n\n  /**\n   * 保存文件到临时目录中\n   *\n   * @param filename - 临时文件名\n   * @returns 文件路径\n   */\n  async unstable__saveFileToTmpdir(\n    filename?: string,\n  ): Promise<string | undefined> {\n    const url = await this.unstable__getFileURL();\n    if (!url) return;\n\n    const {\n      properties,\n      client: { downloader },\n    } = this;\n\n    // 修复文件名丢失的问题，如修复：`xxxx.file` 的文件名\n    if (properties.msgtype === Incoming.ContentType.File) {\n      filename = properties.content.fileName;\n    }\n\n    return downloader.downloadToTmp(url, filename);\n  }\n\n  /**\n   * 获取消息包含的文件名称\n   *\n   * @returns 文件名称\n   */\n  unstable__filenameSync(): string | void {\n    const { properties } = this;\n    if (properties.msgtype === Incoming.ContentType.File) {\n      return properties.content.fileName;\n    }\n  }\n}\n\nfunction resolveTextContent(message: Incoming.Message) {\n  const { msgtype } = message;\n\n  if (msgtype === Incoming.ContentType.Text) {\n    return message.text.content;\n  }\n\n  if (msgtype === Incoming.ContentType.RichText) {\n    const data = message.content.richText.map(item => {\n      return 'text' in item ? item.text : '';\n    });\n\n    return data.filter(Boolean).join('\\n');\n  }\n\n  if (msgtype === Incoming.ContentType.Audio) {\n    return message.content.recognition;\n  }\n\n  return '';\n}\n","export enum SubscriptionType {\n  ImMessage = 'im.message',\n}\n\nexport type SubscriptionItem = {\n  type: (string & NonNullable<unknown>) | 'EVENT' | 'CALLBACK';\n  topic: string;\n};\n\nexport type SubscriptionData = {\n  specVersion: string;\n  type: string;\n  headers: {\n    appId: string;\n    connectionId: string;\n    IncomingType: string;\n    messageId: string;\n    time: string;\n    topic: string;\n    eventType?: string;\n    eventBornTime?: string;\n    eventId?: string;\n    eventCorpId?: string;\n    eventUnifiedAppId?: string;\n  };\n  data: string;\n};\n\nexport const Subscriptions = {\n  AllEvent: {\n    type: 'EVENT',\n    topic: '*',\n  } as SubscriptionItem,\n  ImMessage: {\n    type: 'CALLBACK',\n    topic: '/v1.0/im/bot/messages/get',\n  } as SubscriptionItem,\n} as const;\n\n// TODO 将订阅功能从客户端分离出来，解耦 事件 和 回调 的处理\nexport class SubscriptionManager {\n  // pass\n}\n","import { EventEmitter } from 'node:events';\n\nimport WebSocket from 'ws';\n\nimport { API_GATEWAY_URL } from '../config';\nimport { InvalidResultError } from '../error';\nimport { DingtalkClient } from '../index';\nimport { IncomingMessage } from '../robots';\nimport {\n  type SubscriptionData,\n  type SubscriptionItem,\n  Subscriptions,\n  SubscriptionType,\n} from './subscription';\n\nconst DEFAULT_PING_INTERVAL_IN_MS = 8 * 1000;\n\nexport interface StreamClientOptions {\n  /**\n   * 订阅的事件\n   */\n  subscriptions?: SubscriptionItem[];\n\n  /**\n   * ping 服务器的默认间隔\n   *\n   * @defaultValue 8 * 1000\n   */\n  keepAliveIntervalInMilliseconds?: number;\n}\n\nexport class StreamClient extends EventEmitter {\n  #subscriptions: SubscriptionItem[];\n\n  #socket?: WebSocket;\n  #connected: boolean = false;\n\n  #living = false;\n  #keepAliveTimerId?: NodeJS.Timeout;\n  #keepAliveIntervalInMilliseconds: number;\n\n  constructor(\n    protected client: DingtalkClient,\n    options?: StreamClientOptions,\n  ) {\n    super();\n\n    const {\n      keepAliveIntervalInMilliseconds = DEFAULT_PING_INTERVAL_IN_MS,\n      subscriptions = [Subscriptions.AllEvent, Subscriptions.ImMessage],\n    } = options || {};\n\n    this.#subscriptions = subscriptions;\n    this.#keepAliveIntervalInMilliseconds = keepAliveIntervalInMilliseconds;\n  }\n\n  async connect(): Promise<void> {\n    if (this.#connected) return;\n\n    const { endpoint, ticket } = await this.getEndpoint();\n    const connection = new WebSocket(`${endpoint}?ticket=${ticket}`, {\n      rejectUnauthorized: true,\n    });\n\n    connection.on('open', () => {\n      this.#connected = true;\n      console.info('[' + new Date().toISOString() + '] connect success');\n\n      this.enableHeartbeat();\n    });\n\n    connection.on('message', (data: string) => {\n      const event = JSON.parse(data) as SubscriptionData;\n\n      switch (event.type) {\n        case 'SYSTEM':\n          this.handleSystem(event);\n          break;\n        case 'CALLBACK':\n          this.handleCallback(event);\n          break;\n        case 'EVENT':\n          this.handleEvent(event);\n          break;\n      }\n    });\n\n    connection.on('close', () => {\n      this.#socket = undefined;\n      this.#connected = false;\n      this.disableHeartbeat();\n      this.emit('close');\n    });\n\n    connection.on('error', error => {\n      this.#socket = undefined;\n      this.#connected = false;\n      this.disableHeartbeat();\n      this.emit('error', error);\n    });\n\n    this.#socket = connection;\n  }\n\n  isConnecting() {\n    return this.#connected;\n  }\n\n  disconnect() {\n    this.#connected = false;\n    this.#socket?.close();\n  }\n\n  sendGraphAPIResponse(messageId: string, value: GraphAPIResponse) {\n    if (!messageId) {\n      throw new Error('send: messageId must be defined');\n    }\n\n    const socket = this.#socket;\n    if (!socket) {\n      throw new Error('send: ws must be defined');\n    }\n\n    socket.send(\n      JSON.stringify({\n        code: 200,\n        headers: {\n          IncomingType: 'application/json',\n          messageId: messageId,\n        },\n        message: 'OK',\n        data: JSON.stringify(value),\n      }),\n    );\n  }\n\n  onImMessage(callback: (message: IncomingMessage) => void) {\n    this.on(SubscriptionType.ImMessage, callback);\n  }\n\n  protected handleSystem(message: SubscriptionData) {\n    switch (message.headers.topic) {\n      case 'REGISTERED': {\n        this.#connected = false;\n        break;\n      }\n\n      case 'disconnect': {\n        this.#connected = false;\n        break;\n      }\n\n      case 'KEEPALIVE': {\n        this.#living = true;\n        break;\n      }\n\n      case 'ping': {\n        this.#socket!.send(\n          JSON.stringify({\n            code: 200,\n            headers: message.headers,\n            message: 'OK',\n            data: message.data,\n          }),\n        );\n        break;\n      }\n    }\n  }\n\n  protected handleCallback(event: SubscriptionData) {\n    switch (event.headers.topic) {\n      case Subscriptions.ImMessage.topic: {\n        this.emit(\n          SubscriptionType.ImMessage,\n          new IncomingMessage(this.client, JSON.parse(event.data)),\n        );\n        break;\n      }\n      default:\n        this.emit(event.headers.topic, event);\n    }\n  }\n\n  protected handleEvent(event: SubscriptionData) {\n    this.emit('event', event);\n  }\n\n  protected async getEndpoint() {\n    const { client } = this;\n\n    const res = await fetch(`${API_GATEWAY_URL}/gateway/connections/open`, {\n      method: 'POST',\n      headers: await this.defaultHeaders(),\n      body: JSON.stringify({\n        clientId: client.clientId,\n        clientSecret: client.clientSecret,\n        subscriptions: this.#subscriptions,\n        ua: '',\n      }),\n    });\n\n    const data = await res.json();\n\n    if (!data.endpoint || !data.ticket) {\n      throw new InvalidResultError('endpoint or ticket is null');\n    }\n\n    return data;\n  }\n\n  protected enableHeartbeat() {\n    const socket = this.#socket;\n    if (!socket) return;\n\n    // clear previous heartbeat\n    this.disableHeartbeat();\n\n    socket.on('pong', () => {\n      this.#living = true;\n    });\n\n    this.#living = true;\n    this.#keepAliveTimerId = setInterval(() => {\n      // if ping-pong need to much time, longer than heartbeat, terminate socket connection\n      if (this.#living === false) {\n        console.error(\n          'TERMINATE SOCKET: Ping Pong does not transfer heartbeat within heartbeat interval',\n        );\n        return socket.terminate();\n      }\n\n      // if ping-pong ok, prepare next one\n      this.#living = false;\n      socket.ping('', true);\n    }, this.#keepAliveIntervalInMilliseconds);\n  }\n\n  protected disableHeartbeat() {\n    if (this.#keepAliveTimerId) {\n      clearInterval(this.#keepAliveTimerId);\n    }\n  }\n\n  async authHeaders() {\n    return {\n      'access-token': await this.client.getAccessToken(),\n    }\n  }\n\n  protected async defaultHeaders() {\n    return {\n      Accept: 'application/json',\n      'Content-Type': 'application/json',\n      ...(await this.authHeaders()),\n    };\n  }\n}\n\nexport interface GraphAPIResponse {\n  response: {\n    statusLine: {\n      code?: number;\n      reasonPhrase?: string;\n    };\n    headers: {\n      [key: string]: string;\n    };\n    body: string;\n  };\n}\n","import { type Credentials, SessionCredentials } from './auth';\nimport { API_GATEWAY_URL, type Configuration, defaultConfig } from './config';\nimport * as Errors from './error';\nimport * as API from './resources';\nimport * as Robots from './robots';\nimport * as EventBus from './stream';\n\nexport class DingtalkClient {\n  clientId: string;\n\n  clientSecret: string;\n\n  baseUrl = API_GATEWAY_URL;\n\n  config: Configuration;\n\n  constructor(options: Configuration = {}) {\n    const config = Object.assign({}, defaultConfig, options);\n\n    if (!(config.clientId && config.clientSecret)) {\n      throw new Errors.InvalidArgumentError(\n        'Missing Access token or Secret key',\n      );\n    }\n\n    this.config = config;\n\n    this.clientId = config.clientId;\n    this.clientSecret = config.clientSecret;\n  }\n\n  session = new SessionCredentials(this);\n\n  uploader = new API.Uploader(this);\n\n  downloader = new API.Downloader(this);\n\n  card = new API.Card(this);\n\n  async post<T>(\n    url: string,\n    body?: BodyInit,\n    options?: Omit<RequestInit, 'body'>,\n  ): Promise<T> {\n    const headers = await this.defaultHeaders();\n\n    const resource = await fetch(`${this.baseUrl}${url}`, {\n      ...options,\n      method: 'POST',\n      headers: {\n        ...options?.headers,\n        ...headers,\n      },\n      body,\n    });\n\n    return await resource.json();\n  }\n\n  /**\n   * 获取临时授权码\n   *\n   * @returns 临时授权码\n   */\n  getAccessToken(): Promise<string> {\n    return this.session.getAccessToken();\n  }\n\n  /**\n   * 授权请求头\n   *\n   * @returns 授权请求头\n   */\n  async authHeaders(): Promise<Record<string, string>> {\n    const token = await this.getAccessToken();\n    console.log(token)\n\n    return {\n      'x-acs-dingtalk-access-token': token,\n    };\n  }\n\n  /**\n   * 默认请求头\n   *\n   * @returns 默认请求头\n   */\n  async defaultHeaders(): Promise<Record<string, string>> {\n    const authHeaders = await this.authHeaders();\n\n    return {\n      'Content-Type': 'application/json',\n      ...authHeaders,\n    };\n  }\n\n  static config = defaultConfig;\n\n  static SubscriptionType = EventBus.SubscriptionType;\n  static Subscriptions = EventBus.Subscriptions;\n\n  static SessionWebhook = Robots.SessionWebhook;\n  static CustomizeWebhook = Robots.CustomizeWebhook;\n\n  static IncomingType = Robots.Incoming.ContentType;\n  static OutgoingType = Robots.Outgoing.ContentType;\n}\n\nexport const {\n  DingtalkError,\n  InvalidArgumentError,\n  APIError,\n  APIUserAbortError,\n  APIConnectionError,\n  APIConnectionTimeoutError,\n  BadRequestError,\n  AuthenticationError,\n  PermissionDeniedError,\n  NotFoundError,\n  ConflictError,\n  UnprocessableEntityError,\n  InternalServerError,\n} = Errors;\n\nexport const { StreamClient, SubscriptionType } = EventBus;\n\nexport const { SessionWebhook, CustomizeWebhook } = Robots;\n\nexport const { Downloader, Uploader } = API;\n\nexport { SessionCredentials, type Credentials };\n\nexport type RichTextSectionImage = Robots.Incoming.RichText.ImageSection;\n\nexport namespace DingtalkClient {\n  export type MediaType = API.MediaType;\n\n  export type Uploader = API.Uploader;\n  export type UploadResult = API.UploadResult;\n\n  export type Downloader = API.Downloader;\n\n  export type SubscriptionType = EventBus.SubscriptionType;\n  export type SubscriptionItem = EventBus.SubscriptionItem;\n\n  export type IncomingMessage = Robots.IncomingMessage;\n\n  export type RichTextSectionImage = Robots.Incoming.RichText.ImageSection;\n}\n\nexport default DingtalkClient;\n"],"names":["CLIENT_ID","SECRET_KEY","API_GATEWAY_URL","OPEN_API_GATEWAY_URL","defaultConfig","sha256","data","key","encoding","createHmac","castToError","err","safeJSON","text","DingtalkError","InvalidResultError","APIError","status","error","message","headers","msg","errorResponse","APIConnectionError","BadRequestError","AuthenticationError","PermissionDeniedError","NotFoundError","ConflictError","UnprocessableEntityError","InternalServerError","cause","assertResultSync","result","assertResponse","response","errText","e","errJSON","errMessage","simple_post_json","input","body","init","request","simple_json","SessionCredentials","credentials","#cache","#expiredAt","#promise","res","promise","clientId","clientSecret","url","searchParams","APIResource","client","Instances","params","resultInit","OpenDynamicDataConfig","PullStrategy","TimeUnit","I18n","Language","Card","InstancesAPI.Instances","downloadCode","robotCode","filePath","stream","name","filename","resolveNameFromURl","path","os","writeStream","createWriteStream","Readable","finished","pathname","type","readFileSync","form","Outgoing","ActionCardButtonOrientation","ContentType","Incoming","debug","debuglog","CustomizeWebhook$1","settings","sayable","timestamp","stringToSign","signature","SessionWebhook$1","SayableSayer","mentionList","userIds","id","userId","Robot","incoming","chatbotUserId","chatbotCorpId","sessionWebhook","sessionWebhookExpiredTime","SessionWebhook","Room","properties","Talker","IncomingMessage","conversationId","conversationType","msgId","msgtype","createAt","resolveTextContent","downloader","item","SubscriptionType","Subscriptions","SubscriptionManager","DEFAULT_PING_INTERVAL_IN_MS","EventEmitter","options","keepAliveIntervalInMilliseconds","subscriptions","#subscriptions","#keepAliveIntervalInMilliseconds","#socket","#connected","#living","#keepAliveTimerId","endpoint","ticket","connection","WebSocket","event","messageId","value","socket","callback","DingtalkClient","config","Errors.InvalidArgumentError","API.Uploader","API.Downloader","API.Card","token","EventBus.SubscriptionType","EventBus.Subscriptions","Robots.SessionWebhook","Robots.CustomizeWebhook","Robots.Incoming","Robots.Outgoing","InvalidArgumentError","APIUserAbortError","APIConnectionTimeoutError","Errors","StreamClient","EventBus","CustomizeWebhook","Robots","Downloader","Uploader","API"],"mappings":"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"}