From 192032d7801adeae23baec25b211d4c8db95bfc1 Mon Sep 17 00:00:00 2001 From: Vaica <94612053+zqlit@users.noreply.github.com> Date: Wed, 24 Jun 2026 17:35:07 +0800 Subject: [PATCH] 1735 --- write-server/next-env.d.ts | 6 ++ write-server/src/lib/bot/poll.ts | 152 ++++++++----------------------- 2 files changed, 46 insertions(+), 112 deletions(-) create mode 100644 write-server/next-env.d.ts diff --git a/write-server/next-env.d.ts b/write-server/next-env.d.ts new file mode 100644 index 00000000..9edff1c7 --- /dev/null +++ b/write-server/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +import "./.next/types/routes.d.ts"; + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/write-server/src/lib/bot/poll.ts b/write-server/src/lib/bot/poll.ts index fa6ff059..ff6d1e88 100644 --- a/write-server/src/lib/bot/poll.ts +++ b/write-server/src/lib/bot/poll.ts @@ -391,6 +391,17 @@ async function handleInlineCallback(chatId: number, queryId: string, data: strin try { await tg("answerCallbackQuery", { callback_query_id: queryId }); } catch (_e) {} } +// ============================================================ +// 新增:处理语音消息(增强版:保存文件 + 转文字 + 生成短代码) +// ============================================================ + +async function handleVoiceEnhanced(chatId: number, fileId: string, duration?: number) { + try { + const fs = await import("fs"); + const path = await import("path"); + const { getSession } = await import("./sessions"); + const { transcribeAudio } = await import("@/lib/voice"); + const s = getSession(chatId); if (!s.dirPath) { await sendMessage(chatId, "❌ 请先发 /new 或 /edit 开始写作"); @@ -438,7 +449,11 @@ async function handleInlineCallback(chatId: number, queryId: string, data: strin } } -async function handleAudio(chatId: number, fileId: string, fileName?: string, mimeType?: string) { +// ============================================================ +// 新增:处理音频文件(增强版:支持封面图) +// ============================================================ + +async function handleAudioEnhanced(chatId: number, fileId: string, fileName?: string) { try { const fs = await import("fs"); const path = await import("path"); @@ -470,7 +485,7 @@ async function handleAudio(chatId: number, fileId: string, fileName?: string, mi fs.writeFileSync(path.join(articleDir, filename), Buffer.from(audioBuffer)); const session = getSession(chatId); - session._pendingAudio = { filename, fileId }; + session._pendingAudio = { filename, fileId, name: null, artist: null }; await tg("sendMessage", { chat_id: chatId, @@ -486,31 +501,8 @@ async function handleAudio(chatId: number, fileId: string, fileName?: string, mi } } -async function handleAudioInfo(chatId: number, text: string): Promise { - try { - const { getSession } = await import("./sessions"); - const s = getSession(chatId); - - if (!s._pendingAudio) { - return false; - } - - const { filename } = s._pendingAudio; - delete s._pendingAudio; - - let name = "未知歌曲"; - let artist = "未知艺术家"; - - const parts = text.split(/[-–—]/); - if (parts.length >= 2) { - name = parts[0].trim(); - artist = parts.slice(1).join("-").trim(); - } else if (text.trim()) { - name = text.trim(); - } - - co// ============================================================ -// 增强版 handlePhoto:支持封面图上传 + 实况照片引导 +// ============================================================ +// 新增:处理图片(增强版:支持封面图上传 + 实况照片引导) // ============================================================ async function handlePhotoEnhanced(chatId: number, photos: { file_id: string }[], caption?: string) { @@ -540,7 +532,6 @@ async function handlePhotoEnhanced(chatId: number, photos: { file_id: string }[] const fileUrl = "https://api.telegram.org/file/bot" + TG_TOKEN + "/" + filePath; const imgBuffer = await fetch(fileUrl).then((r) => r.arrayBuffer()); - // 保存图片 const articleDir = s.dirPath; if (!fs.existsSync(articleDir)) fs.mkdirSync(articleDir, { recursive: true }); @@ -551,7 +542,6 @@ async function handlePhotoEnhanced(chatId: number, photos: { file_id: string }[] const imgCaption = caption || "image"; appendContent(chatId, `\n![${imgCaption}](${filename})\n`); - // 实况照片引导提示 await sendMessage(chatId, `📷 图片已保存:${filename} 💡 提示:如果您发送的是 iOS 实况照片(Live Photo), @@ -561,7 +551,6 @@ async function handlePhotoEnhanced(chatId: number, photos: { file_id: string }[] 2. 或者单独发送视频部分 继续发送其他内容...`, { parse_mode: "HTML" }); - } catch (e) { console.error("[bot:photo]", e instanceof Error ? e.message : String(e)); await sendMessage(chatId, "❌ 图片保存失败"); @@ -569,7 +558,7 @@ async function handlePhotoEnhanced(chatId: number, photos: { file_id: string }[] } // ============================================================ -// 处理音乐封面图 +// 新增:处理音乐封面图 // ============================================================ async function handleAudioCover(chatId: number, photos: { file_id: string }[]) { @@ -584,9 +573,8 @@ async function handleAudioCover(chatId: number, photos: { file_id: string }[]) { return; } - const { filename } = s._pendingAudio; + const { filename, name, artist } = s._pendingAudio; - // 获取最大的图片 const largestPhoto = photos[photos.length - 1]; const fileRes = await tg<{ file_path?: string }>("getFile", { file_id: largestPhoto.file_id }); if (!fileRes.ok || !fileRes.result?.file_path) { @@ -598,25 +586,13 @@ async function handleAudioCover(chatId: number, photos: { file_id: string }[]) { const fileUrl = "https://api.telegram.org/file/bot" + TG_TOKEN + "/" + filePath; const imgBuffer = await fetch(fileUrl).then((r) => r.arrayBuffer()); - // 保存封面图 const articleDir = s.dirPath; const ext = filePath.split(".").pop() || "jpg"; const coverFilename = Date.now() + "-cover." + ext; fs.writeFileSync(path.join(articleDir, coverFilename), Buffer.from(imgBuffer)); - // 更新音频信息 - const audioInfo = s._pendingAudio; delete s._pendingAudio; - // 生成带封面的音乐卡片 - let name = "未知歌曲"; - let artist = "未知艺术家"; - - if (audioInfo.name && audioInfo.artist) { - name = audioInfo.name; - artist = audioInfo.artist; - } - const musicCardShortcode = `\n\n{{< music-card src="${filename}" name="${name}" artist="${artist}" cover="${coverFilename}" >}}\n\n`; appendContent(chatId, musicCardShortcode); @@ -626,7 +602,6 @@ async function handleAudioCover(chatId: number, photos: { file_id: string }[]) { 📌 歌曲:${name} 🎤 歌手:${artist} 🖼️ 封面:${coverFilename}`, { parse_mode: "HTML" }); - } catch (e) { console.error("[bot:audio-cover]", e instanceof Error ? e.message : String(e)); await sendMessage(chatId, "❌ 封面图保存失败"); @@ -634,66 +609,10 @@ async function handleAudioCover(chatId: number, photos: { file_id: string }[]) { } // ============================================================ -// 增强版 handleAudio:支持封面图询问 +// 新增:处理音频信息回复 // ============================================================ -async function handleAudioEnhanced(chatId: number, fileId: string, fileName?: string, mimeType?: string) { - try { - const fs = await import("fs"); - const path = await import("path"); - const { getSession } = await import("./sessions"); - - const s = getSession(chatId); - if (!s.dirPath) { - await sendMessage(chatId, "❌ 请先发 /new 或 /edit 开始写作"); - return; - } - - await sendMessage(chatId, "🎵 正在处理音频文件..."); - - // 获取文件 - const fileRes = await tg<{ file_path?: string }>("getFile", { file_id: fileId }); - if (!fileRes.ok || !fileRes.result?.file_path) { - await sendMessage(chatId, "❌ 音频文件获取失败"); - return; - } - - const filePath = fileRes.result.file_path; - const fileUrl = "https://api.telegram.org/file/bot" + TG_TOKEN + "/" + filePath; - const audioBuffer = await fetch(fileUrl).then(r => r.arrayBuffer()); - - // 保存音频文件 - const articleDir = s.dirPath; - if (!fs.existsSync(articleDir)) fs.mkdirSync(articleDir, { recursive: true }); - - const ext = fileName?.split(".").pop() || filePath.split(".").pop() || "mp3"; - const filename = Date.now() + "-music." + ext; - fs.writeFileSync(path.join(articleDir, filename), Buffer.from(audioBuffer)); - - // 询问歌曲信息 - const session = getSession(chatId); - session._pendingAudio = { filename, fileId, name: null, artist: null }; - - await tg("sendMessage", { - chat_id: chatId, - text: `✅ 音频已保存:${filename}\n\n请输入歌曲信息(格式:歌名 - 歌手):`, - reply_markup: { - force_reply: true, - input_field_placeholder: "例如:晴天 - 周杰伦" - } - }); - - } catch (e) { - console.error("[bot:audio]", e instanceof Error ? e.message : String(e)); - await sendMessage(chatId, "❌ 音频处理失败:" + (e instanceof Error ? e.message : "未知错误")); - } -} - -// ============================================================ -// 增强版 handleAudioInfo:支持封面图询问 -// ============================================================ - -async function handleAudioInfoEnhanced(chatId: number, text: string): Promise { +async function handleAudioInfo(chatId: number, text: string): Promise { try { const { getSession } = await import("./sessions"); const s = getSession(chatId); @@ -702,7 +621,6 @@ async function handleAudioInfoEnhanced(chatId: number, text: string): Promise