一、创建飞书应用
1.登录飞书开放平台 进入开发者后台 创建自建应用
2.添加应用能力 选择机器人添加
3.添加事件订阅并根据权限开通权限 此处只添加获取消息事件
4.配置应用服务端地址(当事件触发 会触发设置的地址 并发送事件数据)开启Encrypt Key 实现回调数据加密 接收到数据后 通过开发文档 - 飞书开放平台 文档介绍 解密
5.创建测试企业 测试人员 测试应用无压力不要审核
二、搭建服务 接入 https 证书 创建api
const express = require('express')const app = express()const https = require('https');// node接入证书
const options = {cert: fs.readFileSync('./ssl/xyz.pem'),key: fs.readFileSync('./ssl/xyz.key')
}
let server = https.createServer(options, app)const bodyParser = require('body-parser')app.use(bodyParser.urlencoded({extended: false
}))
app.use(bodyParser.json())server.listen(8443, (err) => {if (!err) {console.log('服务器已启动 端口号8443:::')console.log('http://127.0.0.1:8443')}
})
app.all('*', function(req, res, next) {res.header('Access-Control-Allow-Origin', '*')res.header('Access-Control-Allow-Headers', 'X-Requested-With,Content-Type')res.header('Access-Control-Allow-Methods', 'GET,POST,OPTIONS')next()
})
1.引入axios 调用飞书api接口做准备 npm i axios 并封装
// 封装axios
let tenantToken = ''
let tokenTimes = ''
const httpsAxios = async (url, data) => {if(!tenantToken || ((new Date().getTime() - tokenTimes ) / 1000) > 5400) {tokenTimes = new Date().getTime()tenantToken = await getTenantToken()}return new Promise((resolve, reject) => {axios({url,method: 'post',headers: { Authorization: `Bearer ${tenantToken}`, 'Content-Type': 'application/json; charset=utf-8' },data: data}).then(res=>{resolve(res)}).catch(err=>{reject(err)})})
}
2. 获取飞书的access_token 在调用api中 身份象征
// 获取飞书 tenant_access_token 的方法
const getTenantToken = async () => {const Bool = await addFile()console.log(Bool)if(!Bool) {const url ='https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal';const res = await axios.post(url, {app_id: obj.appID,app_secret: obj.EncryptKey});addFile(res.data)return res.data.tenant_access_token;} else {return Bool;}
}
2.1. addFile 存储 access_token 有效期为2小时 则设置为5400秒重新获取 , 没有用数据库 则使用文件json保存到本地目录下
const fs = require('fs');
const path = require('path');
// 存储数据access_token 到本地文件
const addFile = (data) => {const configFile = path.resolve(__dirname, './json/Info.json')let Obj = JSON.parse(fs.readFileSync(configFile, 'UTF-8').toString())return new Promise(re => {if(data) {Obj.tenant_access_token = data.tenant_access_tokenObj.tenant_access_token_time = new Date().getTime()fs.writeFileSync(configFile, JSON.stringify(Obj))re(Obj.tenant_access_token)} elseif (!Obj.tenant_access_token) {re(false)} else {let time = new Date().getTime()console.log((time - Obj.tenant_access_token_time ))// 一个小时三十分钟就重新获取if(((time - Obj.tenant_access_token_time ) / 1000) > 5400) {re(false)} else {re(Obj.tenant_access_token)}}})
}
3.事件订阅接收数据 接收数据 由于加密过的 需要AESCipher 解密 监听 im.message.receive_v1 获取用户发送的数据 如果你有gpt的话 可以接上webGpt
app.post('/gptTest', async (req, res) => {cipher = new AESCipher(obj.encrypt)let data = JSON.parse(cipher.decrypt(req.body.encrypt))console.log(data)//接收到的数据过期 10秒就不做处理if(new Date().getTime() - Number(data.header.create_time) > 10000) {res.send({data: {},message: 'err',code: 400})return}if (data.header.event_type == 'im.message.receive_v1') {let contents = JSON.parse(data.event.message.content)console.log(contents)console.log(data.event.sender.sender_id.open_id)let result = '测试'//GPT识别码if(contents.text.indexOf('GPT') != -1) {setMessages(data.event.sender.sender_id.open_id)result = await webGpt(contents.text.substring((contents.text.indexOf('GPT')+ 3)))console.log(result)if(!result) {result = '网络不稳定,请重试'}}const url = `https://open.feishu.cn/open-apis/im/v1/messages/${data.event.message.message_id}/reply`;// at功能// if (data.event.sender.sender_id.open_id) content = `<at user_id="${data.event.sender.sender_id.open_id}"></at> ${content}`;const getData = await httpsAxios(url, { content: JSON.stringify({ text: result }),"msg_type": "text"})res.send({data: {},message: 'success',code: 200})} else {res.send({data: {},message: 'err',code: 400})}// res.send({// challenge: req.body.challenge,// data: {},// message: 'err',// code: 500// })
})
3.1 AESCipher 解密
// 解密接收事件数据
const crypto = require("crypto");
class AESCipher {constructor(key) {const hash = crypto.createHash('sha256');hash.update(key);this.key = hash.digest();}decrypt(encrypt) {const encryptBuffer = Buffer.from(encrypt, 'base64');const decipher = crypto.createDecipheriv('aes-256-cbc', this.key, encryptBuffer.slice(0, 16));let decrypted = decipher.update(encryptBuffer.slice(16).toString('hex'), 'hex', 'utf8');decrypted += decipher.final('utf8');return decrypted;}
}
3.2 回复用户信息 调取api 开发文档 - 飞书开放平台https://open.feishu.cn/open-apis/im/v1/messages/${data.event.message.message_id}/reply
let url = `https://open.feishu.cn/open-apis/im/v1/messages/${data.event.message.message_id}/reply`
let result = ''
const getData = await httpsAxios(url, { content: JSON.stringify({ text: result }),"msg_type": "text"})
3.3 发送信息给用户 调用 api 开发文档 - 飞书开放平台
https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=open_id
//receive_id 用户open_id
const setMessages = (receive_id) =>{httpsAxios('https://open.feishu.cn/open-apis/im/v1/messages?receive_id_type=open_id', {"receive_id": receive_id,"msg_type": "text","content": "{\"text\":\"等我C一下...\"}"}).then(res=>{}).catch(err=>{})
}