一、创建飞书应用
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_token
Obj.tenant_access_token_time = new Date().getTime()
fs.writeFileSync(configFile, JSON.stringify(Obj))
re(Obj.tenant_access_token)
} else
if (!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=>{
})
}