Initial commit: 七牛云上传飞书机器人
功能: - 飞书交互卡片支持 - 七牛云文件上传 - 自动 CDN 刷新 - 多存储桶配置 - 跨平台部署(Linux/macOS/Windows) - Docker 支持
This commit is contained in:
151
src/feishu-api.js
Normal file
151
src/feishu-api.js
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* 飞书 API 封装
|
||||
*/
|
||||
|
||||
const axios = require('axios');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
|
||||
class FeishuAPI {
|
||||
constructor() {
|
||||
this.appId = process.env.FEISHU_APP_ID;
|
||||
this.appSecret = process.env.FEISHU_APP_SECRET;
|
||||
this.baseURL = 'https://open.feishu.cn/open-apis';
|
||||
this.tokenCache = null;
|
||||
this.tokenExpiry = 0;
|
||||
}
|
||||
|
||||
// 获取访问令牌
|
||||
async getAccessToken() {
|
||||
if (this.tokenCache && Date.now() < this.tokenExpiry) {
|
||||
return this.tokenCache;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${this.baseURL}/auth/v3/tenant_access_token/internal`,
|
||||
{
|
||||
app_id: this.appId,
|
||||
app_secret: this.appSecret
|
||||
},
|
||||
{
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}
|
||||
);
|
||||
|
||||
const { tenant_access_token, expire } = response.data;
|
||||
|
||||
if (response.data.code !== 0) {
|
||||
throw new Error(`获取 token 失败:${response.data.msg}`);
|
||||
}
|
||||
|
||||
this.tokenCache = tenant_access_token;
|
||||
this.tokenExpiry = Date.now() + (expire - 300) * 1000; // 提前 5 分钟过期
|
||||
|
||||
return tenant_access_token;
|
||||
} catch (error) {
|
||||
throw new Error(`飞书 API 错误:${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 发送文本消息
|
||||
async sendMessage(chatId, payload) {
|
||||
const token = await this.getAccessToken();
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${this.baseURL}/im/v1/messages`,
|
||||
{
|
||||
receive_id: chatId,
|
||||
msg_type: payload.msg_type,
|
||||
content: payload.content
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (response.data.code !== 0) {
|
||||
throw new Error(`发送消息失败:${response.data.msg}`);
|
||||
}
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throw new Error(`飞书消息发送失败:${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 发送卡片消息
|
||||
async sendCard(chatId, card) {
|
||||
return this.sendMessage(chatId, {
|
||||
msg_type: 'interactive',
|
||||
content: JSON.stringify(card)
|
||||
});
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
async downloadFile(fileKey) {
|
||||
const token = await this.getAccessToken();
|
||||
const tempDir = path.join(process.cwd(), 'temp');
|
||||
|
||||
if (!fs.existsSync(tempDir)) {
|
||||
fs.mkdirSync(tempDir, { recursive: true });
|
||||
}
|
||||
|
||||
const tempFile = path.join(tempDir, `feishu_${Date.now()}_${fileKey}`);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const url = `${this.baseURL}/im/v1/files/${fileKey}/download`;
|
||||
|
||||
https.get(url, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
}, (res) => {
|
||||
if (res.statusCode !== 200) {
|
||||
reject(new Error(`下载失败:${res.statusCode}`));
|
||||
return;
|
||||
}
|
||||
|
||||
const file = fs.createWriteStream(tempFile);
|
||||
res.pipe(file);
|
||||
file.on('finish', () => {
|
||||
file.close();
|
||||
resolve(tempFile);
|
||||
});
|
||||
}).on('error', reject);
|
||||
});
|
||||
}
|
||||
|
||||
// 回复消息
|
||||
async replyMessage(messageId, payload) {
|
||||
const token = await this.getAccessToken();
|
||||
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${this.baseURL}/im/v1/messages`,
|
||||
{
|
||||
reply_id: messageId,
|
||||
msg_type: payload.msg_type,
|
||||
content: payload.content
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
throw new Error(`飞书回复失败:${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { FeishuAPI };
|
||||
Reference in New Issue
Block a user