UNPKG

node-red-contrib-feishu

Version:

Node-RED节点用于发送飞书消息

139 lines (127 loc) 5.43 kB
/** * 钉钉机器人节点模块 * 该模块实现了一个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 += `&timestamp=${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: '钉钉消息' // 面板中显示的节点名称 }); }