From 6825bd8cc8a3fc1a9af947f07b68b15b4b1a64b2 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\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