npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

lansenger-sdk-ts

v1.9.0

Published

Lansenger SDK — TypeScript SDK for Lansenger Smart Bot API

Readme

English | 简体中文 | 繁体中文 | 繁体中文香港 | Français

lansenger-sdk-ts

藍信(Lansenger)平臺的 TypeScript SDK——支援藍信應用、組織機械人及個人機械人。

License: MIT TypeScript 5+ Node 18+

零框架依賴——僅依賴 node-fetch (v2,CommonJS 相容)。可適配任何 Node.js 專案。

支援的機械人類型

| 機械人類型 | 認證 | WebSocket 入站 | 全部 API | |------------|------|-----------------|----------| | 藍信應用 | appToken + userToken | ✗(使用 webhook) | ✓ | | 組織機械人 | appToken + userToken | ✗(使用 webhook) | ✓ | | 個人機械人 | appToken | ✓(WebSocket) | ✓(非機械人 API 有部分限制) |

三種機械人類型使用相同的認證機制:appToken 為所有 API 呼叫所必需;userToken 僅在特定使用者級操作時需要(使用者資訊、員工搜尋、日曆等)。

功能特色

  • 非同步客戶端 — LansengerClient 提供 Promise 化 API
  • 憑證與令牌持久化 — CredentialStore 將 app_id、app_secret、URL、appToken、userToken 保存至檔案(重啟不丟失)
  • OAuth2 使用者認證 — 建構授權 URL、換取 userToken、更新令牌
  • 組織與部門 — 組織資訊、部門詳情/子部門/員工列表
  • 員工與通訊錄 — 基本/詳細資訊、ID 映射、部門祖先鏈、搜尋
  • 訊息傳遞 — 3 種私聊通道(機械人、公眾號、人→人)+ 群聊,支援所有訊息類型,含 @提及和真人/機械人發送身分,加急提醒
  • 富卡片 — appCard(支援動態狀態更新)、oacard、linkCard、appArticles
  • 串流訊息 — SSE 即時投遞,專為 AI Agent 設計
  • 媒體上載/下載 — 檔案、圖片、影片,自動偵測類型,媒體路徑取得
  • 訊息管理 — 撤回、動態卡片更新
  • 群組 — 建立、查詢資訊/成員/列表、成員檢查、更新設定與成員、解散
  • 日曆日程 — 主日曆、日程 CRUD + 更新、參會人管理 + 參會人元資料、update_schedule_attendees()
  • 機械人命令 — 創建/查詢/刪除機械人快捷命令
  • 個人應用 — 創建/修改/查詢/刪除/列表個人機械人應用
  • 回呼事件 — 25 種事件類型、結構化訊息解析、AES 解密(4.10.1.4)、SHA1 簽名驗證
  • 聊天讀取 — 取得聊天列表、取得聊天訊息(4.24 MCP)

快速安裝

npm install lansenger-sdk-ts

開發模式:

git clone https://github.com/lansenger-pm/lansenger-sdk-ts.git
cd lansenger-sdk-ts
npm install
npm run build
npm test

1. 認證

appToken — 所有 API 呼叫所必需

每個 SDK 方法都需要 appToken。客戶端使用 app_id + app_secret 自動取得並更新 appToken,你無需手動管理——TokenManager 處理整個生命週期:

  1. 首次呼叫 → 使用 app_id + app_secret 請求 GET /v1/apptoken/create → 回傳 appToken(有效期 2 小時)
  2. 後續呼叫 → 重用已緩存的 appToken 直到過期
  3. Token 過期 → 透過同一端點自動更新
import { LansengerClient } from "lansenger-sdk-ts";

const client = new LansengerClient("你的-appid", "你的-secret");

// 你也可以手動取得/撤銷 token
const token = await client.getToken();
client.invalidateToken(); // 強制下次呼叫時更新

userToken — 僅特定端點需要

userToken 代表特定藍信使用者的授權(透過 OAuth2 取得),僅在以下情況需要:

  • 使用者級資訊(fetchUserInfo、fetchStaffDetail、searchStaff)
  • 日曆日程操作(fetchPrimaryCalendar、createSchedule 等)
  • 以真人發送身分進行群組操作

取得憑證

| 機械人類型 | 如何取得 app_id + app_secret | |------------|------------------------------| | 個人機械人 | 藍信桌面端 → 通訊錄 → 智能機械人 → 個人機械人 → 點擊右側 ℹ️ 圖標(行動端不支援查看憑證) | | 藍信應用 | 在藍信開發者中心建立,可能需要向組織管理員申請 | | 組織機械人 | 在藍信開發者中心建立,可能需要向組織管理員申請 |

OAuth2 使用者級認證

// 建構授權 URL——將使用者重定向到藍信通行證頁面
const url = client.buildAuthorizeUrl("https://myapp.com/callback");

// 使用者授權後,用 code 换取 userToken + refreshToken
const tokenResult = await client.exchangeCode("回呼中的授權碼");

// 更新過期的 userToken
const newToken = await client.refreshUserToken(tokenResult.refresh_token!);

// 取得使用者資料
const userInfo = await client.fetchUserInfo(tokenResult.user_token!);

工廠方法

// 從環境變數(LANSENGER_APP_ID、LANSENGER_APP_SECRET 等)
const client = LansengerClient.fromEnv();

// 從 LansengerConfig 物件
const config = new LansengerConfig("appid", "secret", "https://open.e.lanxin.cn/open/apigw");
const client = LansengerClient.fromConfig(config);

// 從 CredentialStore(讀取持久化憑證)
const client = LansengerClient.fromStore();

External 模式(直接注入 Token)

對於需要自行管理 token 的場景(如 CI/CD 管線、自訂認證系統),可以直接傳入 app_token 和 user_token,完全繞過憑證儲存:

// External 模式 — 直接傳入 token,無需憑證檔案
const client = new LansengerClient(
    "", 
    "",
    "https://your-gateway.example.com",
    "",
    30,
    undefined,
    "",
    "",
    "",
    "your-app-token",
    "your-user-token"
);

// 或者使用 LansengerConfig
const config = new LansengerConfig(
    "", 
    "",
    "https://your-gateway.example.com",
    "",
    30,
    "",
    "",
    "",
    "your-app-token",
    "your-user-token"
);
const client = LansengerClient.fromConfig(config);

External 模式行為:

  • app_token 直接使用,不呼叫 token 刷新 API
  • user_token 直接使用,不經過 OAuth2 流程或刷新
  • 不持久化憑證 — token 僅保存在記憶體中
  • 您需要自行保持 token 有效

2. 組織與部門

// 組織資訊
const org = await client.fetchOrgInfo("orgId");

// 部門層級
const detail = await client.fetchDepartmentDetail("deptId");
const children = await client.fetchDepartmentChildren("deptId");
const staffs = await client.fetchDepartmentStaffs("deptId");

3. 員工與通訊錄

// 基本員工資訊
const staff = await client.fetchStaffBasicInfo("staffOpenId");

// 詳細資料(建議使用 userToken)
const detail = await client.fetchStaffDetail("staffOpenId", { user_token: "ut" });

// 手機號 → staffId 映射
const mapping = await client.fetchStaffIdMapping("orgId", "mobile", "13800138000");

// 員工的部門祖先鏈
const ancestors = await client.fetchDepartmentAncestors("staffOpenId");

// 搜尋員工(需要 userToken)
const results = await client.searchStaff("張三", { user_token: "ut" });

// 組織額外欄位 ID
const fields = await client.fetchOrgExtraFieldIds("orgId");

4. 訊息與媒體

機械人私聊——最常用

const result = await client.sendText("staff123", "你好!");
const result = await client.sendMarkdown("staff123", "**加粗**");
const result = await client.sendFile("staff123", "/path/to/report.pdf");

公眾號通道

const result = await client.sendAccountMessage(
  "text", { text: { content: "系統通知" } },
  ["staff1", "staff2"], undefined,
  { account_id: "524288-xxxx" },
);

人→人通道(需要 userToken)

const result = await client.sendUserMessage(
  "staff456", "text", { text: { content: "你好" } },
  { user_token: "ut" },
);

群聊

// 機械人 → 群組
const result = await client.sendText("group123", "通知", { is_group: true });

// 真人 → 群組(使用 userToken)
const result = await client.sendGroupMessage(
  "group123", "text", { text: { content: "我來處理" } },
  { user_token: "ut" },
);

// 群聊 @提及
const result = await client.sendText("group123", "重要!", { is_group: true, reminder_all: true });

// @提及特定機器人
const result = await client.sendText(
  "group123", "Bot check!", { is_group: true, reminder_bot_ids: ["bot001"] },
);

// 回覆訊息(訊息引用)
const result = await client.sendText(
  "group123", "Got it!", { is_group: true, ref_msg_id: "524288-xxxx" },
);

富卡片

const result = await client.sendAppCard("staff123", "審批", { is_dynamic: true });
const result = await client.sendLinkCard("staff123", "文章", "https://...");
const result = await client.sendAppArticles("staff123", [{ title: "文章1", link: "..." }]);

// 更新動態卡片狀態
const result = await client.updateDynamicCard("msg123", { is_last_update: true });

串流訊息(專為 AI Agent 設計)

const result = await client.createStreamMessage("staff1", "staff", "stream-id-1");
const result = await client.fetchStreamMessage("msg123");

媒體

// 上載
const upload = await client.uploadMediaFile("/path/to/file.pdf");

// 下載
const download = await client.downloadMediaFile("media123");

// 保存至檔案
const filePath = await client.downloadMediaToFile("media123", { target_path: "/tmp/file.pdf" });

// 儲存至檔案(失敗時回傳結果物件,不拋出例外)
const fileResult = await client.downloadMediaToFileResult("media123", { target_path: "/tmp/file.pdf" });

// 取得下載 URL 路徑 (4.5.3)
const pathResult = await client.fetchMediaPathInfo("media123");

// 撤回訊息
const result = await client.revokeMessage(["msg1", "msg2"]);

加急提醒 (4.6.14)

import { REMINDER_TYPE_POPUP, REMINDER_TYPE_SMS } from "lansenger-sdk-ts";

const result = await client.sendReminderMsg(
  "msg123",
  [REMINDER_TYPE_POPUP, REMINDER_TYPE_SMS],
  ["staff1", "staff2"],
);

5. 群組

// 建立群組
const group = await client.createGroup("專案討論", "orgId", { staff_id_list: ["s1", "s2", "s3"] });

// 查詢資訊與成員
const info = await client.fetchGroupInfo("groupOpenId");
const members = await client.fetchGroupMembers("groupOpenId");
const groups = await client.fetchGroupList();

// 成員檢查
const result = await client.checkIsInGroup("groupOpenId", { staff_id: "staff1" });

// 更新設定
await client.updateGroupInfo("groupId", { name: "新名稱", manage_mode: 1 });

// 新增/移除成員
await client.updateGroupMembers("groupId", { add_user_list: ["staff4"], del_user_list: ["staff3"] });

// 解散群組(僅群主,4.28.6)
await client.dismissGroup("groupId");

6. 日曆日程

// 取得主日曆(需要 userToken 或 userId)
const cal = await client.fetchPrimaryCalendar({ user_token: "ut" });

// 建立日程
const schedule = await client.createSchedule(
  cal.calendar_id!, "團隊會議",
  { date: "2024-01-15", time: "10:00", timeZone: "Asia/Shanghai" },
  { date: "2024-01-15", time: "11:00", timeZone: "Asia/Shanghai" },
  [{ staffId: "staff1", attendeeFlag: "yes" }], // 可選值 yes/option/no — 服務器不接受 "required"
  { user_token: "ut" },
);

// 取得/刪除日程
const info = await client.fetchSchedule("cal1", "sch1", { user_token: "ut" });
await client.deleteSchedule("cal1", "sch1", { user_token: "ut" });

// 時間段內的日程列表(最多 42 天)
const schedules = await client.fetchScheduleList("cal1", 1705276800000, 1707940800000, { user_token: "ut" });

// 參會人管理
const attendees = await client.fetchScheduleAttendees("cal1", "sch1", { user_token: "ut" });
await client.addScheduleAttendees("cal1", "sch1", ["staff2"], { user_token: "ut" });
await client.deleteScheduleAttendees("cal1", "sch1", ["staff2"], { user_token: "ut" });

// 更新日程 (4.23.12)
await client.updateSchedule("cal1", "sch1", { summary: "更新的會議", user_token: "ut" });

// 更新參會人元資料 (4.23.17) — RSVP、顏色、忙/閒、提醒
await client.updateScheduleAttendeeMeta("cal1", "sch1", {
  rsvp_status: "accept", busy_free_state: "busy", remind_times: [5, 15], user_token: "ut",
});

7. 聊天讀取 (4.24 MCP)

// 取得聊天列表(私聊 + 羣聊)
const chatList = await client.fetchChatList({ user_token: "ut" });

// 取得聊天訊息
const messages = await client.fetchChatMessages({
  staff_id: "staff1", // 或 group_id: "group1"
  user_token: "ut",
});

8. 回呼事件

SDK 同時支援純 JSON 和 AES 加密回呼載荷(藍信 API 規範 4.10.1.4)。

配置

設定 encoding_key 和 callback_token(來自藍信開發者中心回呼設定):

const client = new LansengerClient("appid", "secret", undefined, undefined, undefined, undefined, "BASE64_AES_KEY", "CALLBACK_TOKEN");

也可透過環境變數:LANSENGER_ENCODING_KEY、LANSENGER_CALLBACK_TOKEN。

解析回呼載荷(自動辨識加密/明文)

import { parseCallbackPayload } from "lansenger-sdk-ts";

// 純 JSON webhook
const events = parseCallbackPayload('{"events": [...]}');

// AES 加密載荷(使用 encodingKey 自動解密)
const events = parseCallbackPayload(encryptedData, {
  encoding_key: "BASE64_AES_KEY",
  known_app_id: "your-appid",
});

驗證簽名

import { verifyCallbackSignature } from "lansenger-sdk-ts";

// sha1(sort(token, timestamp, nonce, dataEncrypt))
const isValid = verifyCallbackSignature(
  timestamp, nonce, signature, encodingKey,
  { data_encrypt: encryptedData, callback_token: "CALLBACK_TOKEN" },
);

事件類型

const types = LansengerClient.getCallbackEventTypes(); // 25 種事件類型,13 個類別

訊息類型能力矩陣

| msgType | Markdown | @提及 | 附件 | 私聊通道 | 羣聊 | 備註 | |---------|----------|-------|------|----------|------|------| | text | ✗ | ✓ (羣) | ✓ | 機械人、公眾號、人→人 | ✓ | 上限 6000 字節 | | formatText | ✓ | ✗ | ✗ | 僅人→人 | ✓ | formatType=1 支援 Markdown | | oacard | ✗ | ✗ | ✗ | 機械人、公眾號、人→人 | ✓ | 簡單卡片含欄位 | | appCard | ✓ (div) | ✗ | ✗ | 機械人、公眾號、人→人 | ✓ | 富卡片,支援動態更新 | | linkCard | ✗ | ✗ | ✗ | 機械人、公眾號 | ✓ | 連結預覽卡片 | | appArticles | ✗ | ✗ | ✗ | 僅機械人私聊 | ✓ | 文章列表(1+ 篇) |

羣聊支援所有訊息類型。只有羣聊支援 @提及。

配置

環境變數

| 變數 | 必填 | 說明 | 預設值 | |------|------|------|--------| | LANSENGER_APP_ID | ✓ | 應用/機械人 ID | — | | LANSENGER_APP_SECRET | ✓ | 應用/機械人 Secret | — | | LANSENGER_API_GATEWAY_URL | ✗ | API 网關 URL | https://open.e.lanxin.cn/open/apigw | | LANSENGER_PASSPORT_URL | ✗ | 通行證 URL(OAuth2) | — | | LANSENGER_REDIRECT_URI | ✗ | OAuth2 回呼地址 | http://localhost:8765 | | LANSENGER_ENCODING_KEY | ✗ | 回呼 AES 加密密鑰(Base64) | — | | LANSENGER_CALLBACK_TOKEN | ✗ | 回呼簽名令牌 | — |

憑證與令牌持久化

預設情況下,憑證和令牌僅保留在記憶體中(程式退出即消失)。啟用檔案持久化使用 storePath:

import { LansengerClient, CredentialStore } from "lansenger-sdk-ts";

// 自動持久化至 ~/.lansenger/sdk_state.json
const client = new LansengerClient("appid", "secret", undefined, undefined, undefined, "~/.lansenger/sdk_state.json", "BASE64_AES_KEY", "CALLBACK_TOKEN");

// 或從環境變數加持久化
const client = LansengerClient.fromEnv("~/.lansenger/sdk_state.json");

// 手動操作儲存
const store = new CredentialStore("~/.lansenger/sdk_state.json");
store.saveCredentials("app_id", "app_secret", "https://open.e.lanxin.cn/open/apigw");
store.saveIdentityType("personal-bot");  // 可選:憑證身份類型 — personal-bot(個人機械人)/ org-app(組織應用)/ org-bot(組織機械人)
store.saveUserToken("user_token", "refresh_token");
const token = store.loadAppToken(); // 過期則回傳 null

身分與權限

身分能力矩陣

藍信平台有三種身分類型,其 API 存取權限不同:

| 命令域 | 個人機械人 | 組織應用(自建) | 組織應用+機械人 | 備註 | |--------|:---:|:---:|:---:|------| | message send-text/markdown/file/... (機械人私聊) | Y | N | Y | 只有機械人可以傳送機械人私聊 | | message send-text --group (羣聊) | Y | N | Y | 個人機械人現已支援羣聊 | | message send-group-message | Y | N | Y | 同上 | | message send-account-message (公眾號) | N | Y | Y | 需要公眾號能力 | | message send-user-message (人→人) | N | Y | Y | 需要 userToken + OAuth2 | | message revoke | Y | Y | Y | 撤回自己傳送的訊息 | | staff * (通訊錄唯讀) | N | Y | Y | search 額外需要 userToken | | department * | N | Y | Y | 僅限組織級應用 | | calendar * | N | Y | Y | 有 userToken = 使用者身分;無 = 機械人身分 | | chat list/messages | N | Y | Y | 僅限組織級應用 | | group * (羣組管理 V2) | N | N | Y | 需要機械人已在羣內 | | media upload | Y | Y | Y | 通用上載 | | media upload-app | Y | Y | Y | 僅限自建應用(非 ISV) | | media download/path | Y | Y | Y | 通用下載 | | oauth * | N | Y | Y | 僅限組織級應用 | | streaming * | N | Y | Y | 僅限組織級應用 | | callback * (事件解析) | N/A | N/A | N/A | 純資料操作,不需要身分 |

個人機械人只能收發訊息和上載/下載檔案。無法存取通訊錄、日曆或 OAuth2。

組織應用 vs 組織應用+機械人:相同的 appID/appSecret。唯一區別在於訊息通道——只有機械人可以傳送機械人私聊和羣聊訊息(因為只有機械人能入羣)。其他所有 API(通訊錄、日曆、聊天、OAuth2、串流)兩者完全一樣。目前只有自建應用支援機械人能力。

開發者中心權限

除身分類型外,特定 API 呼叫還取決於藍信開發者中心的權限開關。組織可能會限制開發者存取,需要管理員協助。

基礎權限(預設開啟):

| 權限 | 說明 | |------|------| | 取得使用者基本資訊 | 取得使用者基本資訊用於系統/工作台登入 | | 傳送通知訊息 | 取得組織訊息通道向使用者/羣組傳送訊息 |

進階權限(預設關閉,需手動開啟):

| 權限 | 說明 | 影響的功能模組 | |------|------|-------------| | 通訊錄唯讀 | 對通訊錄的唯讀存取 | lansenger-staff、lansenger-department | | 通訊錄編輯 | 對通訊錄的編輯存取 | lansenger-staff(建立/更新/刪除) | | 敏感資訊-手機 | 存取使用者手機號碼 | lansenger-staff(詳情、ID 映射) | | 敏感資訊-電子郵件 | 存取使用者電子郵件 | lansenger-staff(詳情、ID 映射) | | 敏感資訊-身分證號 | 存取使用者身分證號 | lansenger-staff | | 敏感資訊-員工編號 | 存取使用者員工編號 | lansenger-staff | | 映射唯一標識到員工 ID | 手機/電子郵件/員工編號映射到員工 ID | lansenger-staff(ID 映射) | | 應用編輯 | 建立和更新應用 | 開發者中心管理 | | 羣組唯讀 | 對羣組的唯讀存取 | lansenger-group(查詢資訊/成員) | | 羣組編輯 | 對羣組的編輯存取 | lansenger-group(建立/更新/解散/成員) | | 日曆唯讀 | 對日曆日程的唯讀存取 | lansenger-calendar(查詢) | | 日曆編輯 | 對日曆日程的編輯存取 | lansenger-calendar(建立/更新/刪除) | | 上載媒體 | 上載媒體檔案權限 | lansenger-media(上載、應用上載) | | 工作台範本讀 | 對工作台範本的唯讀存取 | — | | 工作台範本寫 | 對工作台範本的寫入存取 | — |

遇到權限錯誤時,請先確認身分類型是否支援該操作,然後提示使用者在開發者中心開啟對應的進階權限(無法存取請聯絡組織管理員)。

專案結構

lansenger-sdk-ts/
├── src/
│   ├── index.ts              # 全部导出
│   ├── client.ts             # LansengerClient(非同步)
│   ├── config.ts             # LansengerConfig
│   ├── auth.ts               # TokenManager — appToken 生命週期管理
│   ├── oauth.ts              # OAuth2 輔助
│   ├── constants.ts          # API 端點、媒體類型、OAuth 範圍
│   ├── exceptions.ts         # LansengerError 異常層級
│   ├── models.ts             # 38+ 結果類型
│   ├── http.ts               # doGet、doPost、doPostMultipart、parseApiResponse
│   ├── urlHelpers.ts         # buildApiUrl (支援 pathVars)
│   ├── contacts.ts           # 員工與組織資訊 API
│   ├── departments.ts        # 部門 API
│   ├── accountMessages.ts    # 公眾號通道
│   ├── userMessages.ts       # 人→人通道
│   ├── groupMessages.ts      # 羣聊通道
│   ├── media.ts              # 上載/下載
│   ├── streaming.ts          # SSE 串流
│   ├── persistence.ts        # CredentialStore — 檔案持久化
│   ├── callbacks.ts          # 回呼事件 — 25 種事件、AES 解密、SHA1 簽名驗證
│   ├── groups.ts             # 羣組 API(含解散 4.28.6)
│   ├── calendars.ts          # 日曆日程
│   ├── reminders.ts          # 加急提醒 (4.6.14)
│   ├── chats.ts              # 聊天讀取 (4.24 MCP)
│   └── users.ts              # 使用者資訊
├── tests/                    # 單元測試
├── package.json
├── tsconfig.json
├── jest.config.js
├── LICENSE
└── README*.md                # 5 種語言 README

開發

npm install
npm run build    # 編譯 TypeScript → dist/
npm test         # 執行 Jest 測試
npm run lint     # 僅類型檢查 (tsc --noEmit)

授權

MIT — 見 LICENSE。