import express from 'express'; import type { Request, Response, NextFunction } from 'express'; import fs from 'fs'; import path from 'path'; const app = express(); const PORT = process.env.PORT || 6100; // 中间件:解析 JSON 请求体 app.use(express.json({ limit: '10mb' })); // 中间件:解析 URL 编码的请求体 app.use(express.urlencoded({ extended: true, limit: '10mb' })); // 中间件:解析原始请求体 app.use(express.raw({ type: '*/*', limit: '10mb' })); // 自定义中间件:打印所有请求的详细信息 /* app.use((req: Request, res: Response, next: NextFunction) => { const timestamp = new Date().toISOString(); console.log('\n' + '='.repeat(80)); console.log(`[${timestamp}] 收到请求:`); console.log(`方法: ${req.method}`); console.log(`路径: ${req.path}`); console.log(`完整URL: ${req.originalUrl}`); console.log(`查询参数:`, req.query); console.log(`请求头:`, req.headers); // 打印请求体(如果存在) if (req.body) { if (Buffer.isBuffer(req.body)) { console.log(`请求体 (原始):`, req.body.toString()); } else if (typeof req.body === 'object' && Object.keys(req.body).length > 0) { console.log(`请求体 (JSON):`, req.body); } else if (req.body) { console.log(`请求体:`, req.body); } } console.log(`客户端IP: ${req.ip}`); console.log(`User-Agent: ${req.get('User-Agent')}`); console.log('='.repeat(80)); next(); }); */ // QQ机器人回显功能 app.post('/', async (req: Request, res: Response) => { // 检查是否是消息类型的请求 if (!req.body || req.body.post_type != 'message') return const { target_id, raw_message, message_type, user_id } = req.body; console.log(`\n[QQ机器人] 收到${message_type}消息`); console.log(`发送者ID: ${user_id || target_id}`); console.log(`消息内容: ${raw_message}`); // Match Douyin URL // Like: https://v.douyin.com/YqgJL_phY_k/ const douyinUrlPattern = /https?:\/\/v\.douyin\.com\/[a-zA-Z0-9_-]+/; const douyinMatch = raw_message.match(douyinUrlPattern); try { // 如果检测到抖音链接,调用解析API if (douyinMatch) { const douyinUrl = douyinMatch[0]; console.log(`[抖音链接检测] 发现抖音链接: ${douyinUrl}`); // 调用抖音视频解析API const apiUrl = `http://localhost:6101/api/hybrid/video_data?url=${encodeURIComponent(douyinUrl)}`; console.log(`[抖音解析] 调用API: ${apiUrl}`); const apiResponse = await fetch(apiUrl, { method: 'GET', headers: { 'Content-Type': 'application/json', } }); if (!apiResponse.ok) throw new Error(`抖音API调用失败: ${apiResponse.status} ${apiResponse.statusText}`); let vD: any = await apiResponse.json(); if (!vD.data) throw new Error('抖音API返回数据格式错误'); vD = vD.data; // 发送视频信息 sendMsg(`[抖音下载] ${vD.author.nickname}: ${vD.caption}\n`, target_id); // 下载视频 const downloadUrl = `http://localhost:6101/api/download?url=${encodeURIComponent(douyinUrl)}&prefix=true&with_watermark=false`; console.log(`[抖音下载] 调用下载API: ${downloadUrl}`); const downloadResponse = await fetch(downloadUrl); if (!downloadResponse.ok) { throw new Error(`视频下载失败: ${downloadResponse.status} ${downloadResponse.statusText}`); } // 创建下载目录 const downloadDir = process.env.DOWNLOAD_DIR || path.join(__dirname, 'downloads'); if (!fs.existsSync(downloadDir)) { fs.mkdirSync(downloadDir, { recursive: true }); } // 生成文件名 const timestamp = Date.now(); // 清理文件名中的非法字符 const cleanCaption = vD.caption.replace(/[<>:"/\\|?*]/g, '_').substring(0, 50); const fileName = `${cleanCaption}_${timestamp}.mp4`; const filePath = path.join(downloadDir, fileName); // 保存视频文件 console.log(`[抖音下载] 保存视频到: ${filePath}`); const buffer = await downloadResponse.arrayBuffer(); fs.writeFileSync(filePath, Buffer.from(buffer)); // 发送视频消息 console.log(`[抖音发送] 发送视频文件`); await sendVideoMsg(filePath, target_id); } } catch (error) { console.error(`[错误] 处理消息时发生错误:`, error); sendMsg(String(error), target_id); } }); function sendMsg(msg: string, target_id: string) { const replyMessage = { user_id: String(target_id), message: [ { type: "text", data: { text: msg } } ] } const replyUrl = `http://localhost:30000/send_private_msg`; return fetch(replyUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(replyMessage) }); } function sendVideoMsg(filePath: string, target_id: string) { const videoMessage = { user_id: String(target_id), message: [ { type: "video", data: { file: `file://${filePath}` } } ] } const replyUrl = `http://localhost:30000/send_private_msg`; return fetch(replyUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(videoMessage) }); } app.use((error: any, req: Request, res: Response, next: NextFunction) => { const timestamp = new Date().toISOString(); console.error(`\n[${timestamp}] 错误:`, error); res.status(500).json({ success: false, message: '服务器内部错误', error: error.message }); }); // 启动服务器 app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); console.log(`With env:`, { DOWNLOAD_DIR: process.env.DOWNLOAD_DIR, PORT: process.env.PORT }); });