首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >钉钉机器人消息发送 npm 库:ddmessage-fruge365

钉钉机器人消息发送 npm 库:ddmessage-fruge365

作者头像
fruge365
发布2025-12-15 12:53:41
发布2025-12-15 12:53:41
1390
举报
在这里插入图片描述
在这里插入图片描述

从零开发一个钉钉机器人消息发送 npm 库:ddmessage-fruge365

前言

在日常开发中,我们经常需要将系统状态、错误信息、数据报告等通过钉钉机器人发送到群聊中。虽然钉钉提供了 Webhook API,但每次都要手写 HTTP 请求代码,还要处理跨域问题,非常繁琐。

于是我开发了 ddmessage-fruge365 这个 npm 库,让钉钉机器人消息发送变得简单易用。

项目背景

遇到的问题
  1. 重复代码:每个项目都要写一遍钉钉 API 调用代码
  2. 跨域限制:浏览器环境无法直接调用钉钉 API
  3. 类型安全:缺少 TypeScript 类型定义
  4. 消息格式:需要手动构造复杂的消息结构
解决方案

开发一个统一的 npm 库,提供:

  • 简洁的 API 接口
  • 完整的 TypeScript 支持
  • 跨域代理解决方案
  • 多种消息类型支持

技术选型

核心技术栈
  • TypeScript:提供类型安全和更好的开发体验
  • Axios:处理 HTTP 请求
  • Rollup:构建 ES 模块版本
  • Jest:单元测试框架
项目结构
代码语言:javascript
复制
ddmessage-fruge365/
├── src/
│   ├── index.ts          # 主要逻辑
│   ├── types.ts          # 类型定义
│   └── utils.ts          # 工具函数
├── lib/                  # 编译输出
├── package.json
├── tsconfig.json
└── rollup.config.js

核心功能实现

1. 基础类型定义
代码语言:javascript
复制
// src/types.ts
export interface DingTalkConfig {
  accessToken: string;    // 机器人 Access Token
  baseUrl?: string;       // API 基础地址
  timeout?: number;       // 请求超时时间
  proxyPath?: string;     // 代理路径
}

export interface TextMessage {
  msgtype: 'text';
  text: {
    content: string;
  };
  at?: {
    atMobiles?: string[];
    atUserIds?: string[];
    isAtAll?: boolean;
  };
}

export interface DingTalkResponse {
  errcode: number;
  errmsg: string;
}
2. 核心类实现
代码语言:javascript
复制
// src/index.ts
export class DingTalkRobot {
  private client: AxiosInstance;
  private accessToken: string;
  private proxyPath?: string;

  constructor(config: DingTalkConfig) {
    this.accessToken = config.accessToken;
    this.proxyPath = config.proxyPath;
    
    // 根据是否使用代理选择不同的 baseURL
    const baseURL = this.proxyPath ? '' : (config.baseUrl || 'https://oapi.dingtalk.com');
    
    this.client = axios.create({
      baseURL,
      timeout: config.timeout || 10000,
      headers: {
        'Content-Type': 'application/json',
      },
    });
  }

  async send(message: Message): Promise<DingTalkResponse> {
    try {
      let response;
      
      if (this.proxyPath) {
        // 使用代理时,直接请求代理路径
        const url = `${this.proxyPath}?access_token=${this.accessToken}`;
        response = await axios.post(url, message, {
          timeout: 10000,
          headers: { 'Content-Type': 'application/json' }
        });
      } else {
        // 不使用代理时,使用配置的 client
        const url = `/robot/send?access_token=${this.accessToken}`;
        response = await this.client.post(url, message);
      }
      
      return response.data;
    } catch (error: any) {
      throw new Error(`发送消息失败: ${error.message}`);
    }
  }

  async sendText(content: string, at?: AtOptions): Promise<DingTalkResponse> {
    const message: TextMessage = {
      msgtype: 'text',
      text: { content },
      ...(at && { at }),
    };
    return this.send(message);
  }
}
3. 跨域问题解决

这是项目的核心难点。浏览器环境下直接调用钉钉 API 会遇到跨域问题,需要通过代理服务器转发请求。

Vite 代理配置
代码语言:javascript
复制
// vite.config.js
export default {
  server: {
    proxy: {
      '/dd-api': {
        target: 'https://oapi.dingtalk.com/robot/send',
        changeOrigin: true,
        rewrite: (path) => path.replace('/dd-api', ''),
      },
    },
  },
}
库中的处理逻辑
代码语言:javascript
复制
// 根据是否使用代理选择不同的请求方式
if (this.proxyPath) {
  // 浏览器环境:使用代理路径
  const url = `${this.proxyPath}?access_token=${this.accessToken}`;
  response = await axios.post(url, message);
} else {
  // Node.js 环境:直接调用钉钉 API
  const url = `/robot/send?access_token=${this.accessToken}`;
  response = await this.client.post(url, message);
}

构建配置

TypeScript 配置
代码语言:javascript
复制
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"],
    "outDir": "./lib",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "declaration": true,
    "sourceMap": true
  }
}
Rollup 配置(ES 模块支持)
代码语言:javascript
复制
// rollup.config.js
const resolve = require('@rollup/plugin-node-resolve');
const commonjs = require('@rollup/plugin-commonjs');
const typescript = require('@rollup/plugin-typescript');

module.exports = {
  input: 'src/index.ts',
  output: {
    file: 'lib/index.esm.js',
    format: 'es'
  },
  plugins: [
    resolve(),
    commonjs(),
    typescript({
      tsconfig: './tsconfig.esm.json'
    })
  ],
  external: ['axios']
};
Package.json 配置
代码语言:javascript
复制
{
  "main": "lib/index.js",
  "module": "lib/index.esm.js",
  "types": "lib/index.d.ts",
  "exports": {
    ".": {
      "import": "./lib/index.esm.js",
      "require": "./lib/index.js"
    }
  }
}

使用示例

Node.js 环境
代码语言:javascript
复制
const DingTalkRobot = require('ddmessage-fruge365');

const robot = new DingTalkRobot({
  accessToken: 'your-access-token'
});

// 发送文本消息
await robot.sendText('系统启动成功!');

// 发送 Markdown 消息
await robot.sendMarkdown('日报', `
## 今日工作总结
- 完成功能开发 ✅
- 修复 3 个 bug 🐛
- 代码审查通过 👍
`);
Vue 项目中使用
代码语言:javascript
复制
import DingTalkRobot from 'ddmessage-fruge365';

const robot = new DingTalkRobot({
  accessToken: 'your-access-token',
  proxyPath: '/dd-api' // 使用代理
});

// 发送消息
await robot.sendText('Hello from Vue!');

遇到的技术难点

1. 模块格式兼容性

需要同时支持 CommonJS 和 ES 模块:

解决方案

  • 使用 TypeScript 编译 CommonJS 版本
  • 使用 Rollup 构建 ES 模块版本
  • 在 package.json 中配置 exports 字段
2. 跨域代理逻辑

不同环境需要不同的请求方式:

解决方案

  • 通过 proxyPath 参数判断是否使用代理
  • 代理环境直接使用 axios 请求代理路径
  • 非代理环境使用配置的 axios 实例
3. TypeScript 类型安全

确保所有 API 都有完整的类型定义:

解决方案

  • 定义完整的接口类型
  • 使用泛型约束参数类型
  • 生成 .d.ts 声明文件

项目亮点

1. 开箱即用
代码语言:javascript
复制
// 一行代码发送消息
await robot.sendText('Hello World!');
2. 完整的 TypeScript 支持
代码语言:javascript
复制
interface DingTalkConfig {
  accessToken: string;
  proxyPath?: string;
  timeout?: number;
}
3. 跨平台兼容
  • Node.js 服务端
  • Vue/React 前端项目
  • 原生 JavaScript
4. 多种消息类型
  • 文本消息(支持 @ 功能)
  • Markdown 消息
  • 链接消息

性能优化

1. 按需加载
代码语言:javascript
复制
// 只导入需要的功能
import { getCurrentIP } from 'ddmessage-fruge365/lib/utils';
2. 请求优化
  • 配置合理的超时时间
  • 复用 axios 实例
  • 错误重试机制
3. 包体积优化
  • 使用 Rollup 进行 Tree Shaking
  • 外部化依赖(axios)
  • 压缩输出代码

测试与发布

单元测试
代码语言:javascript
复制
// 测试消息发送功能
describe('DingTalkRobot', () => {
  test('should send text message', async () => {
    const robot = new DingTalkRobot({ accessToken: 'test' });
    const result = await robot.sendText('test message');
    expect(result.errcode).toBe(0);
  });
});
发布流程
代码语言:javascript
复制
# 1. 构建项目
npm run build

# 2. 运行测试
npm test

# 3. 发布到 npm
npm publish

总结

通过开发 ddmessage-fruge365 这个项目,我深入学习了:

  1. npm 包开发:从设计到发布的完整流程
  2. TypeScript 工程化:类型定义、编译配置、声明文件生成
  3. 跨域问题解决:代理配置、环境适配
  4. 模块化设计:CommonJS 和 ES 模块兼容
  5. 工具链配置:Rollup、Jest、TypeScript 集成

这个库目前已发布到 npm,欢迎大家使用和反馈!

相关链接

  • GitHub:https://github.com/fruge365/DdMessage
  • NPM:https://www.npmjs.com/package/ddmessage-fruge365
  • 作者博客:https://fruge365.blog.csdn.net/

如果这篇文章对你有帮助,欢迎点赞收藏!有任何问题也可以在评论区交流讨论。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-09-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从零开发一个钉钉机器人消息发送 npm 库:ddmessage-fruge365
    • 前言
    • 项目背景
      • 遇到的问题
      • 解决方案
    • 技术选型
      • 核心技术栈
      • 项目结构
    • 核心功能实现
      • 1. 基础类型定义
      • 2. 核心类实现
      • 3. 跨域问题解决
    • 构建配置
      • TypeScript 配置
      • Rollup 配置(ES 模块支持)
      • Package.json 配置
    • 使用示例
      • Node.js 环境
      • Vue 项目中使用
    • 遇到的技术难点
      • 1. 模块格式兼容性
      • 2. 跨域代理逻辑
      • 3. TypeScript 类型安全
    • 项目亮点
      • 1. 开箱即用
      • 2. 完整的 TypeScript 支持
      • 3. 跨平台兼容
      • 4. 多种消息类型
    • 性能优化
      • 1. 按需加载
      • 2. 请求优化
      • 3. 包体积优化
    • 测试与发布
      • 单元测试
      • 发布流程
    • 总结
    • 相关链接
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档