

在日常开发中,我们经常需要将系统状态、错误信息、数据报告等通过钉钉机器人发送到群聊中。虽然钉钉提供了 Webhook API,但每次都要手写 HTTP 请求代码,还要处理跨域问题,非常繁琐。
于是我开发了 ddmessage-fruge365 这个 npm 库,让钉钉机器人消息发送变得简单易用。
开发一个统一的 npm 库,提供:
ddmessage-fruge365/
├── src/
│ ├── index.ts # 主要逻辑
│ ├── types.ts # 类型定义
│ └── utils.ts # 工具函数
├── lib/ # 编译输出
├── package.json
├── tsconfig.json
└── rollup.config.js// 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;
}// 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);
}
}这是项目的核心难点。浏览器环境下直接调用钉钉 API 会遇到跨域问题,需要通过代理服务器转发请求。
// vite.config.js
export default {
server: {
proxy: {
'/dd-api': {
target: 'https://oapi.dingtalk.com/robot/send',
changeOrigin: true,
rewrite: (path) => path.replace('/dd-api', ''),
},
},
},
}// 根据是否使用代理选择不同的请求方式
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);
}{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"outDir": "./lib",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"declaration": true,
"sourceMap": true
}
}// 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']
};{
"main": "lib/index.js",
"module": "lib/index.esm.js",
"types": "lib/index.d.ts",
"exports": {
".": {
"import": "./lib/index.esm.js",
"require": "./lib/index.js"
}
}
}const DingTalkRobot = require('ddmessage-fruge365');
const robot = new DingTalkRobot({
accessToken: 'your-access-token'
});
// 发送文本消息
await robot.sendText('系统启动成功!');
// 发送 Markdown 消息
await robot.sendMarkdown('日报', `
## 今日工作总结
- 完成功能开发 ✅
- 修复 3 个 bug 🐛
- 代码审查通过 👍
`);import DingTalkRobot from 'ddmessage-fruge365';
const robot = new DingTalkRobot({
accessToken: 'your-access-token',
proxyPath: '/dd-api' // 使用代理
});
// 发送消息
await robot.sendText('Hello from Vue!');需要同时支持 CommonJS 和 ES 模块:
解决方案:
不同环境需要不同的请求方式:
解决方案:
proxyPath 参数判断是否使用代理确保所有 API 都有完整的类型定义:
解决方案:
// 一行代码发送消息
await robot.sendText('Hello World!');interface DingTalkConfig {
accessToken: string;
proxyPath?: string;
timeout?: number;
}// 只导入需要的功能
import { getCurrentIP } from 'ddmessage-fruge365/lib/utils';// 测试消息发送功能
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);
});
});# 1. 构建项目
npm run build
# 2. 运行测试
npm test
# 3. 发布到 npm
npm publish通过开发 ddmessage-fruge365 这个项目,我深入学习了:
这个库目前已发布到 npm,欢迎大家使用和反馈!
如果这篇文章对你有帮助,欢迎点赞收藏!有任何问题也可以在评论区交流讨论。