diff --git a/src/index.js b/src/index.js index 26bd22b..a722152 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,8 @@ #!/usr/bin/env node /** - * 七牛云上传 - 飞书独立应用 v2 + * 七牛云上传 - 飞书独立应用 v3 + * 支持存储桶和路径选择 */ require('dotenv').config(); @@ -23,6 +24,12 @@ function log(...args) { console.log(`[${timestamp}]`, ...args); } +// 加载完整配置(包括预设路径) +function loadFullConfig() { + const configPath = path.join(process.cwd(), 'config', 'qiniu-config.json'); + return JSON.parse(fs.readFileSync(configPath, 'utf-8')); +} + async function handleFeishuEvent(req, res) { const event = req.body; log('📩 收到飞书请求'); @@ -39,17 +46,10 @@ async function handleFeishuEvent(req, res) { } } - // 飞书 v2 schema: event_type 在 header 中 const eventType = decryptedEvent.event_type || decryptedEvent.header?.event_type || decryptedEvent.type; log('收到飞书事件:', eventType); - log('事件结构:', JSON.stringify({ - has_event_type: !!decryptedEvent.event_type, - has_header: !!decryptedEvent.header, - header_event_type: decryptedEvent.header?.event_type, - has_type: !!decryptedEvent.type - }).substring(0, 300)); if (eventType === 'url_verification') { res.json({ challenge: decryptedEvent.challenge || event.challenge }); @@ -97,15 +97,8 @@ async function handleMessage(event) { } else if (text.startsWith('/help') || text.startsWith('/qh')) { await handleHelpCommandV2(chatId, feishu); } else if (messageType === 'file' || messageContent.file_key) { - log('🔍 收到文件消息 - 发送存储桶选择卡片'); + log('🔍 收到文件消息 - 发送配置卡片'); await handleFileReceivedWithCard(messageData, feishu, uploader); - } else if (text.startsWith('/')) { - // 处理路径设置命令 - log('处理路径设置命令:', text); - await feishu.sendMessage(chatId, { - msg_type: 'text', - content: { text: '💡 请先选择存储桶,然后回复路径\n或使用命令:/upload /路径/文件名 存储桶名' } - }); } else { await sendWelcomeCard(chatId, feishu); } @@ -131,7 +124,6 @@ async function handleCardInteraction(event) { if (!chatId && operator?.open_id) { chatId = operator.open_id; - log('使用 operator open_id 作为 chatId:', chatId); } log('卡片交互:', action, 'chatId:', chatId); @@ -194,65 +186,28 @@ async function handleCardInteraction(event) { break; } - case 'select_bucket': { - const { file_key, file_name, message_id, chat_id, bucket } = actionData.value; + case 'set_bucket': { + const { bucket } = actionData.value; log('🪣 选择存储桶:', bucket); - // 发送确认卡片,带路径输入提示 - const card = { - config: { wide_screen_mode: true }, - header: { - template: 'green', - title: { content: '✅ 已选择存储桶', tag: 'plain_text' } - }, - elements: [ - { - tag: 'div', - text: { - tag: 'lark_md', - content: `**存储桶:** ${bucket}\n**文件:** ${file_name}\n\n**请回复消息设置路径:**\n格式:/路径/文件名\n\n例如:/config/test.txt\n\n或直接点击"使用原文件名上传"` - } - }, - { - tag: 'action', - actions: [ - { - tag: 'button', - text: { tag: 'plain_text', content: '✅ 使用原文件名上传' }, - type: 'primary', - value: { - action: 'confirm_upload', - file_key: file_key, - file_name: file_name, - message_id: message_id, - chat_id: chat_id, - bucket: bucket - } - }, - { - tag: 'button', - text: { tag: 'plain_text', content: '🔙 重新选择' }, - type: 'default', - value: { - action: 'reselect' - } - } - ] - } - ] - }; - - await feishu.sendCard(chatId, card); + await feishu.sendMessage(chatId, { + msg_type: 'text', + content: { text: `✅ 已选择存储桶:**${bucket}**\n\n请重新发送文件开始上传` } + }); break; } - case 'reselect': - // 重新发送文件选择卡片(需要用户重新发送文件) + case 'set_path': { + const { upload_path, path_label } = actionData.value; + const pathDesc = path_label || '原文件名'; + log('📁 选择路径:', pathDesc); + await feishu.sendMessage(chatId, { msg_type: 'text', - content: { text: '📎 请重新发送文件' } + content: { text: `✅ 已选择路径:**${pathDesc}**\n\n请重新发送文件开始上传` } }); break; + } case 'cancel_upload': log('取消上传,chatId:', chatId); @@ -298,21 +253,59 @@ async function handleFileReceivedWithCard(messageData, feishu, uploader) { const configData = await uploader2.listConfig(); const bucketNames = Object.keys(configData.buckets); + // 获取预设路径 + const fullConfig = loadFullConfig(); + const uploadPaths = fullConfig.uploadPaths || { '原文件名': '' }; + // 构建存储桶选择按钮 const bucketButtons = bucketNames.map(name => ({ tag: 'button', text: { tag: 'plain_text', content: name }, - type: 'default', + type: 'primary', value: { - action: 'select_bucket', - file_key: fileKey, - file_name: fileName, - message_id: messageId, - chat_id: chatId, + action: 'set_bucket', bucket: name } })); + // 构建路径选择按钮 + const pathButtons = Object.entries(uploadPaths).map(([label, pathValue]) => ({ + tag: 'button', + text: { tag: 'plain_text', content: label }, + type: 'default', + value: { + action: 'set_path', + upload_path: pathValue, + path_label: label + } + })); + + // 构建快速上传按钮(使用默认配置) + const quickUploadButtons = [ + { + tag: 'button', + text: { tag: 'plain_text', content: '✅ 使用默认配置上传' }, + type: 'primary', + value: { + action: 'confirm_upload', + file_key: fileKey, + file_name: fileName, + message_id: messageId, + chat_id: chatId, + bucket: 'default', + upload_path: '' + } + }, + { + tag: 'button', + text: { tag: 'plain_text', content: '❌ 取消' }, + type: 'default', + value: { + action: 'cancel_upload' + } + } + ]; + const card = { config: { wide_screen_mode: true }, header: { @@ -324,7 +317,7 @@ async function handleFileReceivedWithCard(messageData, feishu, uploader) { tag: 'div', text: { tag: 'lark_md', - content: `**文件名:** ${fileName}\n\n**请选择存储桶:**` + content: `**文件名:** ${fileName}\n\n**1️⃣ 选择存储桶:**` } }, { @@ -338,7 +331,35 @@ async function handleFileReceivedWithCard(messageData, feishu, uploader) { tag: 'div', text: { tag: 'lark_md', - content: `💡 **提示:**\n• 选择存储桶后,可以输入路径\n• 使用命令:/upload /路径/文件名 存储桶名` + content: `**2️⃣ 选择路径:**` + } + }, + { + tag: 'action', + actions: pathButtons + }, + { + tag: 'hr' + }, + { + tag: 'div', + text: { + tag: 'lark_md', + content: `**3️⃣ 开始上传:**` + } + }, + { + tag: 'action', + actions: quickUploadButtons + }, + { + tag: 'hr' + }, + { + tag: 'div', + text: { + tag: 'lark_md', + content: `💡 **提示:**\n• 先选择存储桶和路径\n• 点击"使用默认配置上传"开始\n• 或使用命令:/upload /路径/文件名 存储桶名` } } ] @@ -386,8 +407,8 @@ async function handleHelpCommandV2(chatId, feishu) { 📤 上传文件: 1. 直接发送文件给我 - 2. 点击确认卡片上的"✅ 确认上传" - 3. 等待上传完成,获取下载链接 + 2. 选择存储桶和路径 + 3. 点击"使用默认配置上传" ⚙️ 配置管理: /config list - 查看配置 @@ -395,6 +416,7 @@ async function handleHelpCommandV2(chatId, feishu) { 💡 提示: - 支持多存储桶配置 +- 支持预设路径 - 上传同名文件会自动覆盖 ` } }); @@ -412,18 +434,12 @@ async function sendWelcomeCard(chatId, feishu) { tag: 'div', text: { tag: 'lark_md', - content: '你好!我是七牛云上传机器人。\n\n**使用方式:**\n• 直接发送文件给我\n• 点击确认卡片上传\n\n**命令:**\n• /config - 查看配置\n• /help - 查看帮助' + content: '你好!我是七牛云上传机器人。\n\n**使用方式:**\n• 直接发送文件给我\n• 选择存储桶和路径\n• 确认上传\n\n**命令:**\n• /config - 查看配置\n• /help - 查看帮助' } }, { tag: 'action', actions: [ - { - tag: 'button', - text: { tag: 'plain_text', content: '📎 上传文件' }, - type: 'primary', - value: { action: 'upload_file' } - }, { tag: 'button', text: { tag: 'plain_text', content: '⚙️ 配置' }, @@ -487,7 +503,7 @@ setInterval(cleanupTempFiles, 60 * 60 * 1000); log('⏰ 临时文件清理任务已启动(每小时执行一次)'); app.listen(PORT, () => { - log(`🚀 七牛云上传机器人启动 (v2)`); + log(`🚀 七牛云上传机器人启动 (v3 - 支持存储桶和路径选择)`); log(`📍 端口:${PORT}`); setTimeout(cleanupTempFiles, 5000); });