node-red-contrib-feishu
Version:
Node-RED节点用于发送飞书消息
139 lines (127 loc) • 5.43 kB
JavaScript
/**
* 钉钉机器人节点模块
* 该模块实现了一个Node-RED节点,用于向钉钉机器人发送消息
* 支持文本、Markdown等多种类型的消息
*/
module.exports = function(RED) {
/**
* 钉钉节点构造函数
* @param {Object} config - 节点配置对象,包含用户在界面上配置的参数
*/
function DingtalkNode(config) {
// 创建节点实例,继承Node-RED节点的基本功能
RED.nodes.createNode(this, config);
const node = this;
// 引入axios用于发送HTTP请求
const axios = require('axios');
const crypto = require('crypto');
/**
* 生成钉钉机器人签名
* @param {string} secret - 加签密钥
* @param {string} timestamp - 时间戳
* @returns {string} - 签名字符串
*/
function generateSign(secret, timestamp) {
const stringToSign = `${timestamp}\n${secret}`;
const hmac = crypto.createHmac('sha256', secret);
hmac.update(stringToSign);
return hmac.digest('base64');
}
/**
* 处理输入消息事件
* 当节点收到上游数据时触发
* @param {Object} msg - 输入消息对象
*/
node.on('input', async function(msg) {
// 获取配置参数,支持通过msg动态覆盖配置
const accessToken = config.accessToken || msg.accessToken;
const secret = config.secret || msg.secret;
const messageType = config.messageType || msg.messageType || 'text';
let content = msg.payload;
// 验证必要参数
if (!accessToken) {
node.error('Access Token is required');
return;
}
try {
// 构建webhook URL
const webhookUrl = `https://oapi.dingtalk.com/robot/send?access_token=${accessToken}`;
if (!accessToken) {
node.error('Invalid webhook URL: missing access_token');
return;
}
// 构建请求URL(使用原始webhook URL)
let requestUrl = webhookUrl;
// 如果配置了加签密钥,添加签名参数
if (secret) {
const timestamp = Date.now();
const sign = generateSign(secret, timestamp);
requestUrl += `×tamp=${timestamp}&sign=${encodeURIComponent(sign)}`;
}
// 根据消息类型构建不同格式的消息内容
let messageContent;
if (messageType === 'text') {
// 构建纯文本消息格式
messageContent = {
msgtype: 'text',
text: {
content: typeof content === 'string' ? content : JSON.stringify(content)
}
};
} else if (messageType === 'markdown') {
// 构建Markdown消息格式
messageContent = {
msgtype: 'markdown',
markdown: {
title: msg.title || '通知消息',
text: typeof content === 'string' ? content : JSON.stringify(content)
}
};
}
// 发送HTTP POST请求到钉钉机器人API
const response = await axios.post(requestUrl, messageContent);
// 处理响应结果
if (response.data.errcode === 0) {
// 发送成功,设置节点状态为绿色
node.status({fill: 'green', shape: 'dot', text: '发送成功'});
msg.payload = response.data;
node.send(msg);
} else {
// 业务层面的错误
throw new Error(response.data.errmsg || '发送失败');
}
} catch (error) {
// 发送失败,设置节点状态为红色
node.status({fill: 'red', shape: 'ring', text: '发送失败'});
node.error(error.message, msg);
}
});
/**
* 节点关闭事件处理
* 清除节点状态
*/
node.on('close', function() {
node.status({});
});
}
// 注册节点类型
RED.nodes.registerType('dingtalk', DingtalkNode, {
// 节点默认配置
defaults: {
name: {value: ''}, // 节点名称
accessToken: {value: '', required: true}, // 必填的Access Token
secret: {value: ''}, // 加签密钥
messageType: {value: 'text'} // 默认使用text类型
},
category: 'social', // 节点分类
color: '#1890FF', // 节点颜色
inputs: 1, // 输入连接点数量
outputs: 1, // 输出连接点数量
icon: 'font-awesome/fa-commenting-o', // 节点图标
// 节点标签显示函数
label: function() {
return this.name || '钉钉消息';
},
paletteLabel: '钉钉消息' // 面板中显示的节点名称
});
}