diff --git a/src/index.js b/src/index.js index 8240d9d..0bf7d6c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,13 +1,12 @@ #!/usr/bin/env node /** - * 七牛云上传 - 飞书独立应用 v4 - * 主动触发 + 确认上传流程 + * 七牛云上传 - 飞书独立应用 v5 + * 简化流程:上传配置 + 一键上传 */ require('dotenv').config(); const express = require('express'); -const crypto = require('crypto'); const path = require('path'); const fs = require('fs'); @@ -30,7 +29,7 @@ function loadFullConfig() { return JSON.parse(fs.readFileSync(configPath, 'utf-8')); } -// 用户临时状态存储 +// 用户状态存储 const userStates = {}; function getUserState(chatId) { @@ -56,7 +55,6 @@ async function handleFeishuEvent(req, res) { const { decrypt } = require('@larksuiteoapi/node-sdk'); decryptedEvent = decrypt(event.encrypt, process.env.FEISHU_ENCRYPT_KEY); } catch (e) { - log('❌ 解密失败:', e.message); res.status(500).send('Decrypt error'); return; } @@ -65,7 +63,6 @@ async function handleFeishuEvent(req, res) { const eventType = decryptedEvent.event_type || decryptedEvent.header?.event_type || decryptedEvent.type; - log('收到飞书事件:', eventType); if (eventType === 'url_verification') { res.json({ challenge: decryptedEvent.challenge || event.challenge }); @@ -86,213 +83,199 @@ async function handleFeishuEvent(req, res) { } async function handleMessage(event) { - try { - const messageData = event.event?.message || event.message; - if (!messageData) { - log('❌ 未找到消息数据'); - return; - } - - const messageContent = JSON.parse(messageData.content); - const text = messageContent.text || ''; - const chatId = messageData.chat_id; - const messageType = messageData.message_type || 'text'; - - log(`处理消息:${chatId} - 类型:${messageType}`); - - const feishu = new FeishuAPI(); - const uploader = new QiniuUploader(); - - if (text.startsWith('/upload') || text.startsWith('/u ')) { - // 主动触发上传流程 - log('📤 用户主动触发上传流程'); - setUserState(chatId, { uploadFlow: true }); - await feishu.sendMessage(chatId, { - msg_type: 'text', - content: { text: '📎 请发送要上传的文件' } - }); - } else if (text.startsWith('/config') || text.startsWith('/qc ')) { - await handleConfigCommandV2(messageData, messageContent, feishu, uploader); - } else if (text.startsWith('/path')) { - await handlePathCommandV2(messageData, messageContent, feishu); - } else if (text.startsWith('/help') || text.startsWith('/qh')) { - await handleHelpCommandV2(chatId, feishu); - } else if (messageType === 'file' || messageContent.file_key) { - const state = getUserState(chatId); - if (state.uploadFlow) { - // 用户上传流程中的文件 - log('🔍 用户上传流程 - 发送选择卡片'); - await handleFileReceivedWithCard(messageData, feishu, uploader); - } else { - // 普通消息,显示欢迎卡片 - await sendWelcomeCard(chatId, feishu); - } - } else { - await sendWelcomeCard(chatId, feishu); - } - } catch (error) { - log('❌ 消息处理失败:', error.message); + const messageData = event.event?.message || event.message; + if (!messageData) return; + + const messageContent = JSON.parse(messageData.content); + const text = messageContent.text || ''; + const chatId = messageData.chat_id; + const messageType = messageData.message_type || 'text'; + + const feishu = new FeishuAPI(); + const uploader = new QiniuUploader(); + + if (text.startsWith('/upload') || text.startsWith('/u ')) { + // 显示上传配置卡片 + await showProfileCard(chatId, feishu, uploader); + } else if (text.startsWith('/config') || text.startsWith('/qc ')) { + await handleConfigCommandV2(messageData, messageContent, feishu, uploader); + } else if (text.startsWith('/path')) { + await handlePathCommandV2(messageData, messageContent, feishu); + } else if (text.startsWith('/help') || text.startsWith('/qh')) { + await handleHelpCommandV2(chatId, feishu); + } else if (messageType === 'file' || messageContent.file_key) { + // 收到文件,显示配置选择 + await handleFileReceived(messageData, feishu, uploader); + } else { + await sendWelcomeCard(chatId, feishu); } } async function handleCardInteraction(event) { - try { - const eventData = event.event; - const operator = eventData?.operator; - const actionData = eventData?.action; - const message = eventData?.message; + const eventData = event.event; + const actionData = eventData?.action; + const message = eventData?.message; + + if (!actionData) return; + + const action = actionData.value?.action; + let chatId = message?.chat_id || actionData.value?.chat_id; + + if (!chatId && eventData?.operator?.open_id) { + chatId = eventData.operator.open_id; + } + + log('卡片交互:', action, 'chatId:', chatId); + + const feishu = new FeishuAPI(); + const uploader = new QiniuUploader(); + + switch (action) { + case 'start_upload': + await showProfileCard(chatId, feishu, uploader); + break; - if (!actionData) { - log('❌ 卡片交互:缺少 action 数据'); - return; + case 'select_profile': { + const { profile_name, bucket, path: upload_path } = actionData.value; + log('📋 选择上传配置:', profile_name); + + setUserState(chatId, { + profile_name, + bucket, + upload_path + }); + + await feishu.sendMessage(chatId, { + msg_type: 'text', + content: { text: `✅ 已选择配置:**${profile_name}**\n\n📤 请发送文件,或点击"📎 选择文件上传"` } + }); + break; } - const action = actionData.value?.action; - let chatId = message?.chat_id || actionData.value?.chat_id; - - if (!chatId && operator?.open_id) { - chatId = operator.open_id; + case 'upload_with_profile': { + // 从配置卡片直接上传(需要先发送文件) + const { profile_name, bucket, path: upload_path } = actionData.value; + const state = getUserState(chatId); + + if (!state.file_key) { + await feishu.sendMessage(chatId, { + msg_type: 'text', + content: { text: '📎 请先发送文件' } + }); + return; + } + + await doUpload(chatId, feishu, uploader, { + file_key: state.file_key, + file_name: state.file_name, + message_id: state.message_id, + bucket, + upload_path, + path_label: profile_name + }); + clearUserState(chatId); + break; } - log('卡片交互:', action, 'chatId:', chatId); - - const feishu = new FeishuAPI(); - const uploader = new QiniuUploader(); - - switch (action) { - case 'start_upload': - // 用户点击欢迎卡片的"上传文件" - log('📤 用户点击上传文件按钮'); - setUserState(chatId, { uploadFlow: true }); - await feishu.sendMessage(chatId, { - msg_type: 'text', - content: { text: '📎 请发送要上传的文件' } - }); - break; - - case 'set_bucket': { - const { bucket, chat_id } = actionData.value; - const stateChatId = chat_id || chatId; - log('🪣 选择存储桶:', bucket); - - setUserState(stateChatId, { bucket }); - - // 检查是否已选择路径 - const state = getUserState(stateChatId); - if (state.upload_path) { - // 已选择路径,显示确认卡片 - await showConfirmCard(stateChatId, feishu, uploader, state); - } else { - await feishu.sendMessage(chatId, { - msg_type: 'text', - content: { text: `✅ 已选择存储桶:**${bucket}**\n\n请继续选择路径` } - }); - } - break; - } - - case 'set_path': { - const { upload_path, path_label, chat_id } = actionData.value; - const stateChatId = chat_id || chatId; - const pathDesc = path_label || '原文件名'; - log('📁 选择路径:', pathDesc); - - setUserState(stateChatId, { upload_path, path_label }); - - // 检查是否已选择存储桶 - const state = getUserState(stateChatId); - if (state.bucket) { - // 已选择存储桶,显示确认卡片 - await showConfirmCard(stateChatId, feishu, uploader, state); - } else { - await feishu.sendMessage(chatId, { - msg_type: 'text', - content: { text: `✅ 已选择路径:**${pathDesc}**\n\n请继续选择存储桶` } - }); - } - break; - } - - case 'confirm_upload': { - const { file_key, file_name, message_id, chat_id, bucket, upload_path, path_label } = actionData.value; - const targetBucket = bucket || 'default'; - let targetKey = upload_path || file_name; - const pathDesc = path_label || '原文件名'; - - if (targetKey.startsWith('/')) targetKey = targetKey.substring(1); - - log('✅ 确认上传:', file_name, 'bucket:', targetBucket, 'path:', targetKey); - - await feishu.sendMessage(chatId, { - msg_type: 'text', - content: { text: `📥 正在下载:${file_name}` } - }); - - try { - const tempFile = await feishu.downloadFile(file_key, message_id, chatId); - log('✅ 文件下载完成:', tempFile); - - await feishu.sendMessage(chatId, { - msg_type: 'text', - content: { text: `📤 上传中:${targetKey} → ${targetBucket}` } - }); - - const result = await uploader.upload(tempFile, targetKey, targetBucket); - await uploader.refreshCDN(targetBucket, targetKey); - - await feishu.sendMessage(chatId, { - msg_type: 'text', - content: { - text: `✅ 上传成功!\n\n` + - `📦 文件:${targetKey}\n` + - `🔗 链接:${result.url}\n` + - `💾 原文件:${file_name}\n` + - `🪣 存储桶:${targetBucket}\n` + - `📁 路径:${pathDesc}` - } - }); - - fs.unlinkSync(tempFile); - log('🗑️ 临时文件已清理:', tempFile); - clearUserState(chatId); - - } catch (error) { - log('上传失败:', error.message); - await feishu.sendMessage(chatId, { - msg_type: 'text', - content: { text: `❌ 上传失败:${error.message}` } - }); - } - break; - } - - case 'cancel_upload': - log('❌ 取消上传'); - clearUserState(chatId); - await feishu.sendMessage(chatId, { - msg_type: 'text', - content: { text: '❌ 已取消上传' } - }); - break; - - case 'config': { - const configData = await uploader.listConfig(); - const configCard = createConfigCard(configData); - await feishu.sendCard(chatId, configCard); - break; - } - - case 'help': - await handleHelpCommandV2(chatId, feishu); - break; + case 'confirm_upload': { + const { file_key, file_name, message_id, bucket, upload_path, path_label } = actionData.value; + await doUpload(chatId, feishu, uploader, { + file_key, file_name, message_id, bucket, upload_path, path_label + }); + clearUserState(chatId); + break; } - } catch (error) { - log('❌ 卡片交互处理失败:', error.message); + + case 'cancel': + clearUserState(chatId); + await feishu.sendMessage(chatId, { + msg_type: 'text', + content: { text: '❌ 已取消' } + }); + break; + + case 'config': { + const configData = await uploader.listConfig(); + await feishu.sendCard(chatId, createConfigCard(configData)); + break; + } + + case 'help': + await handleHelpCommandV2(chatId, feishu); + break; } } -// 显示选择卡片(存储桶 + 路径) -async function handleFileReceivedWithCard(messageData, feishu, uploader) { +// 显示上传配置卡片 +async function showProfileCard(chatId, feishu, uploader) { + const fullConfig = loadFullConfig(); + const profiles = fullConfig.uploadProfiles || {}; + + const profileButtons = Object.entries(profiles).map(([name, config]) => ({ + tag: 'button', + text: { tag: 'plain_text', content: name }, + type: 'primary', + value: { + action: 'select_profile', + profile_name: name, + bucket: config.bucket, + path: config.path || '' + } + })); + + const card = { + config: { wide_screen_mode: true }, + header: { + template: 'blue', + title: { content: '📤 选择上传配置', tag: 'plain_text' } + }, + elements: [ + { + tag: 'div', + text: { + tag: 'lark_md', + content: '**选择一个上传配置,然后发送文件:**' + } + }, + { + tag: 'action', + actions: profileButtons + }, + { + tag: 'hr' + }, + { + tag: 'div', + text: { + tag: 'lark_md', + content: '💡 **提示:**\n• 选择配置后发送文件\n• 或直接发送文件后选择配置' + } + }, + { + tag: 'action', + actions: [ + { + tag: 'button', + text: { tag: 'plain_text', content: '⚙️ 配置' }, + type: 'default', + value: { action: 'config' } + }, + { + tag: 'button', + text: { tag: 'plain_text', content: '❓ 帮助' }, + type: 'default', + value: { action: 'help' } + } + ] + } + ] + }; + + await feishu.sendCard(chatId, card); +} + +// 处理文件接收 +async function handleFileReceived(messageData, feishu, uploader) { const chatId = messageData.chat_id; const messageId = messageData.message_id; const messageContent = JSON.parse(messageData.content); @@ -302,90 +285,6 @@ async function handleFileReceivedWithCard(messageData, feishu, uploader) { if (!fileKey) return; - log('📎 收到文件,发送选择卡片:', fileName); - - // 获取存储桶列表 - const configData = await uploader.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: 'primary', - value: { - action: 'set_bucket', - bucket: name, - chat_id: chatId - } - })); - - // 构建路径选择按钮 - 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, - chat_id: chatId, - // 保存文件信息用于确认卡片 - file_key: fileKey, - file_name: fileName, - message_id: messageId - } - })); - - const card = { - config: { wide_screen_mode: true }, - header: { - template: 'blue', - title: { content: '📎 文件上传配置', tag: 'plain_text' } - }, - elements: [ - { - tag: 'div', - text: { - tag: 'lark_md', - content: `**文件名:** ${fileName}\n\n**1️⃣ 选择存储桶:**` - } - }, - { - tag: 'action', - actions: bucketButtons - }, - { - tag: 'hr' - }, - { - tag: 'div', - text: { - tag: 'lark_md', - content: `**2️⃣ 选择路径:**` - } - }, - { - tag: 'action', - actions: pathButtons - }, - { - tag: 'hr' - }, - { - tag: 'div', - text: { - tag: 'lark_md', - content: `💡 **提示:**\n• 选择存储桶和路径后,会显示确认卡片\n• 点击"确认上传"才开始上传` - } - } - ] - }; - // 保存文件信息到状态 setUserState(chatId, { file_key: fileKey, @@ -393,23 +292,91 @@ async function handleFileReceivedWithCard(messageData, feishu, uploader) { message_id: messageId }); - await feishu.sendCard(chatId, card); + const state = getUserState(chatId); + + // 如果已选择配置,显示确认卡片 + if (state.bucket && state.upload_path !== undefined) { + await showConfirmCard(chatId, feishu, { + file_key: fileKey, + file_name: fileName, + message_id: messageId, + bucket: state.bucket, + upload_path: state.upload_path, + path_label: state.profile_name || '自定义' + }); + } else { + // 未选择配置,显示配置选择卡片 + const fullConfig = loadFullConfig(); + const profiles = fullConfig.uploadProfiles || {}; + + const profileButtons = Object.entries(profiles).map(([name, config]) => ({ + tag: 'button', + text: { tag: 'plain_text', content: `${name}` }, + type: 'primary', + value: { + action: 'confirm_upload', + file_key: fileKey, + file_name: fileName, + message_id: messageId, + chat_id: chatId, + bucket: config.bucket, + upload_path: config.path || '', + path_label: name + } + })); + + const card = { + config: { wide_screen_mode: true }, + header: { + template: 'blue', + title: { content: '📎 文件已收到', tag: 'plain_text' } + }, + elements: [ + { + tag: 'div', + text: { + tag: 'lark_md', + content: `**文件:** ${fileName}\n\n**选择配置后确认上传:**` + } + }, + { + tag: 'action', + actions: profileButtons + }, + { + tag: 'hr' + }, + { + tag: 'div', + text: { + tag: 'lark_md', + content: '💡 点击配置按钮直接上传' + } + }, + { + tag: 'action', + actions: [ + { + tag: 'button', + text: { tag: 'plain_text', content: '❌ 取消' }, + type: 'default', + value: { action: 'cancel' } + } + ] + } + ] + }; + + await feishu.sendCard(chatId, card); + } } // 显示确认卡片 -async function showConfirmCard(chatId, feishu, uploader, state) { - const fileKey = state.file_key; - const fileName = state.file_name; - const messageId = state.message_id; - const bucket = state.bucket || 'default'; - const upload_path = state.upload_path || ''; - const path_label = state.path_label || '原文件名'; - - let targetKey = upload_path || fileName; +async function showConfirmCard(chatId, feishu, info) { + const { file_name, bucket, upload_path, path_label } = info; + let targetKey = upload_path || file_name; if (targetKey.startsWith('/')) targetKey = targetKey.substring(1); - log('📋 显示确认卡片:', fileName, 'bucket:', bucket, 'path:', targetKey); - const card = { config: { wide_screen_mode: true }, header: { @@ -421,7 +388,7 @@ async function showConfirmCard(chatId, feishu, uploader, state) { tag: 'div', text: { tag: 'lark_md', - content: `**文件:** ${fileName}\n**存储桶:** ${bucket}\n**路径:** ${path_label}\n**目标:** ${targetKey}\n\n点击"确认上传"开始上传到七牛云` + content: `**文件:** ${file_name}\n**配置:** ${path_label}\n**存储桶:** ${bucket}\n**路径:** ${targetKey || '(原文件名)'}\n\n点击"确认上传"开始上传` } }, { @@ -433,22 +400,20 @@ async function showConfirmCard(chatId, feishu, uploader, state) { type: 'primary', value: { action: 'confirm_upload', - file_key: fileKey, - file_name: fileName, - message_id: messageId, + file_key: info.file_key, + file_name: info.file_name, + message_id: info.message_id, chat_id: chatId, - bucket: bucket, - upload_path: upload_path, - path_label: path_label + bucket, + upload_path, + path_label } }, { tag: 'button', text: { tag: 'plain_text', content: '❌ 取消' }, type: 'default', - value: { - action: 'cancel_upload' - } + value: { action: 'cancel' } } ] } @@ -458,6 +423,55 @@ async function showConfirmCard(chatId, feishu, uploader, state) { await feishu.sendCard(chatId, card); } +// 执行上传 +async function doUpload(chatId, feishu, uploader, info) { + const { file_key, file_name, message_id, bucket, upload_path, path_label } = info; + let targetKey = upload_path || file_name; + if (targetKey.startsWith('/')) targetKey = targetKey.substring(1); + + log('📤 开始上传:', file_name, '→', bucket, '/', targetKey); + + try { + await feishu.sendMessage(chatId, { + msg_type: 'text', + content: { text: `📥 正在下载:${file_name}` } + }); + + const tempFile = await feishu.downloadFile(file_key, message_id, chatId); + log('✅ 文件下载完成:', tempFile); + + await feishu.sendMessage(chatId, { + msg_type: 'text', + content: { text: `📤 上传中:${targetKey} → ${bucket}` } + }); + + const result = await uploader.upload(tempFile, targetKey, bucket); + await uploader.refreshCDN(bucket, targetKey); + + await feishu.sendMessage(chatId, { + msg_type: 'text', + content: { + text: `✅ 上传成功!\n\n` + + `📦 文件:${targetKey}\n` + + `🔗 链接:${result.url}\n` + + `💾 原文件:${file_name}\n` + + `🪣 存储桶:${bucket}\n` + + `📁 配置:${path_label}` + } + }); + + fs.unlinkSync(tempFile); + log('🗑️ 临时文件已清理'); + + } catch (error) { + log('❌ 上传失败:', error.message); + await feishu.sendMessage(chatId, { + msg_type: 'text', + content: { text: `❌ 上传失败:${error.message}` } + }); + } +} + async function handleConfigCommandV2(message, content, feishu, uploader) { const chatId = message.chat_id; const text = content.text || ''; @@ -467,18 +481,14 @@ async function handleConfigCommandV2(message, content, feishu, uploader) { try { if (subCommand === 'list' || !subCommand) { const configData = await uploader.listConfig(); - const configCard = createConfigCard(configData); - await feishu.sendCard(chatId, configCard); + await feishu.sendCard(chatId, createConfigCard(configData)); } else if (subCommand === 'set') { const [keyPath, value] = args.slice(1); - if (!keyPath || !value) throw new Error('用法:/config set '); await uploader.setConfigValue(keyPath, value); await feishu.sendMessage(chatId, { msg_type: 'text', content: { text: `✅ 已设置 ${keyPath} = ${value}` } }); - } else { - throw new Error(`未知命令:${subCommand}`); } } catch (error) { await feishu.sendMessage(chatId, { @@ -499,7 +509,7 @@ async function handlePathCommandV2(message, content, feishu) { try { const fullConfig = loadFullConfig(); - if (subCommand === 'list' || !subCommand) { + if (subCommand === 'list') { const paths = fullConfig.uploadPaths || {}; let pathText = '**预设路径列表:**\n\n'; for (const [name, pathValue] of Object.entries(paths)) { @@ -510,38 +520,14 @@ async function handlePathCommandV2(message, content, feishu) { content: { text: pathText } }); } else if (subCommand === 'add') { - if (args.length < 3) { - throw new Error('用法:/path add <名称> <路径>'); - } const name = args[1]; const pathValue = args[2]; - - fullConfig.uploadPaths = fullConfig.uploadPaths || {}; fullConfig.uploadPaths[name] = pathValue; - fs.writeFileSync(configPath, JSON.stringify(fullConfig, null, 2)); await feishu.sendMessage(chatId, { msg_type: 'text', content: { text: `✅ 已添加预设路径:**${name}** → ${pathValue}` } }); - } else if (subCommand === 'remove' || subCommand === 'del') { - if (args.length < 2) { - throw new Error('用法:/path remove <名称>'); - } - const name = args[1]; - - if (!fullConfig.uploadPaths || !fullConfig.uploadPaths[name]) { - throw new Error(`预设路径 "${name}" 不存在`); - } - - delete fullConfig.uploadPaths[name]; - fs.writeFileSync(configPath, JSON.stringify(fullConfig, null, 2)); - await feishu.sendMessage(chatId, { - msg_type: 'text', - content: { text: `✅ 已删除预设路径:**${name}**` } - }); - } else { - throw new Error(`未知命令:${subCommand}\n可用命令:list, add, remove`); } } catch (error) { await feishu.sendMessage(chatId, { @@ -557,25 +543,27 @@ async function handleHelpCommandV2(chatId, feishu) { content: { text: ` 🍙 七牛云上传 - 使用帮助 -📤 上传文件: - 1. 发送 /upload 命令 - 2. 或点击"📎 上传文件"按钮 - 3. 发送文件 - 4. 选择存储桶和路径 - 5. 点击"确认上传" +📤 上传方式: + +**方式 1:选择配置 → 发送文件** +1. 发送 /upload +2. 选择上传配置 +3. 发送文件 +4. 确认上传 + +**方式 2:发送文件 → 选择配置** +1. 直接发送文件 +2. 选择上传配置 +3. 确认上传 ⚙️ 配置管理: - /config list - 查看配置 - /config set - 修改配置 - -📁 路径管理: - /path list - 查看预设路径 - /path add <名称> <路径> - 添加预设路径 - /path remove <名称> - 删除预设路径 +/config list - 查看配置 +/path list - 查看预设路径 +/path add <名称> <路径> - 添加路径 💡 提示: -- 支持多存储桶配置 -- 支持预设路径 +- 上传配置在 config/qiniu-config.json 中配置 +- 支持多存储桶 - 上传同名文件会自动覆盖 ` } }); @@ -593,7 +581,7 @@ async function sendWelcomeCard(chatId, feishu) { tag: 'div', text: { tag: 'lark_md', - content: '你好!我是七牛云上传机器人。\n\n**使用方式:**\n• 发送 /upload 命令\n• 或点击下方"📎 上传文件"\n• 发送文件后选择配置\n\n**命令:**\n• /config - 查看配置\n• /path - 路径管理\n• /help - 查看帮助' + content: '你好!我是七牛云上传机器人。\n\n**使用方式:**\n• /upload - 选择配置上传\n• 直接发送文件\n\n**命令:**\n• /config - 配置\n• /path - 路径\n• /help - 帮助' } }, { @@ -601,7 +589,7 @@ async function sendWelcomeCard(chatId, feishu) { actions: [ { tag: 'button', - text: { tag: 'plain_text', content: '📎 上传文件' }, + text: { tag: 'plain_text', content: '📤 上传文件' }, type: 'primary', value: { action: 'start_upload' } }, @@ -610,12 +598,6 @@ async function sendWelcomeCard(chatId, feishu) { text: { tag: 'plain_text', content: '⚙️ 配置' }, type: 'default', value: { action: 'config' } - }, - { - tag: 'button', - text: { tag: 'plain_text', content: '❓ 帮助' }, - type: 'default', - value: { action: 'help' } } ] } @@ -644,31 +626,7 @@ app.get('/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString(), port: PORT }); }); -function cleanupTempFiles() { - const tempDir = path.join(process.cwd(), 'temp'); - const maxAgeMs = 60 * 60 * 1000; - if (!fs.existsSync(tempDir)) return; - const now = Date.now(); - let cleaned = 0; - fs.readdirSync(tempDir).forEach(file => { - const filePath = path.join(tempDir, file); - try { - const stats = fs.statSync(filePath); - if (now - stats.mtimeMs > maxAgeMs) { - fs.unlinkSync(filePath); - log('🗑️ 清理过期临时文件:', file); - cleaned++; - } - } catch (e) {} - }); - if (cleaned > 0) log(`✅ 清理完成:${cleaned} 个文件`); -} - -setInterval(cleanupTempFiles, 60 * 60 * 1000); -log('⏰ 临时文件清理任务已启动(每小时执行一次)'); - app.listen(PORT, () => { - log(`🚀 七牛云上传机器人启动 (v4 - 主动触发 + 确认上传)`); + log(`🚀 七牛云上传机器人启动 (v5 - 简化流程)`); log(`📍 端口:${PORT}`); - setTimeout(cleanupTempFiles, 5000); });