From c203d49aec8d984e3358443c30eb378af697abb7 Mon Sep 17 00:00:00 2001 From: Vaica <94612053+zqlit@users.noreply.github.com> Date: Wed, 24 Jun 2026 17:58:29 +0800 Subject: [PATCH] 1758 --- themes/Ying/assets/css/shortcodes.css | 764 ++++++++++++++++++-------- write-server/src/lib/bot/handlers.ts | 15 +- write-server/src/lib/bot/poll.ts | 16 +- 3 files changed, 561 insertions(+), 234 deletions(-) diff --git a/themes/Ying/assets/css/shortcodes.css b/themes/Ying/assets/css/shortcodes.css index 312a7f18..85f91e32 100644 --- a/themes/Ying/assets/css/shortcodes.css +++ b/themes/Ying/assets/css/shortcodes.css @@ -1,9 +1,540 @@ -/* ============================================================ - Shortcodes 样式 - 实况照片、语音、音乐卡片 - 移植自 Hugo-Theme-Amigo - ============================================================ */ +/* ========================================================================== + Shortcodes 样式 - 1:1 移植自 Hugo-Theme-Amigo + ========================================================================== */ + +/* ---- CSS 变量定义(Amigo 主题) ---- */ +:root { + /* 字体 */ + --font-family: 'ZQL', -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; + + /* 核心配色 - 亮色模式 */ + --bg-body: #f2f2f2; + --bg-card: #ffffff; + --bg-header: rgba(255, 255, 255, 0.85); + --bg-header-transparent: rgba(255, 255, 255, 0); + --bg-hover: rgba(0, 0, 0, 0.05); + --bg-active: rgba(0, 0, 0, 0.1); + --bg-input: #f7f7f7; + --bg-code: rgba(27, 31, 35, 0.05); + --bg-code-block: #f6f8fa; + --bg-popover: #ffffff; + --bg-mask: rgba(20, 20, 20, 0.85); + + /* 文字颜色 */ + --text-main: #333333; + --text-desc: #777777; + --text-muted: #999999; + --text-link: #576b95; + --text-white: #ffffff; + --text-on-cover: #ffffff; + --text-quote: #6a737d; + + /* 功能性颜色 */ + --theme-color: #07c160; /* 微信绿 */ + --border-main: #eeeeee; + --border-quote: #dfe2e5; + --shadow-main: rgba(0, 0, 0, 0.03); + --shadow-header: rgba(0, 0, 0, 0.05); + --shadow-popover: 0 4px 20px rgba(0, 0, 0, 0.1); + --shadow-button: 0 2px 8px rgba(0, 0, 0, 0.1); + --shadow-button-hover: 0 4px 12px rgba(0, 0, 0, 0.15); + --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.1); + --shadow-text: 0 1px 1px rgba(0, 0, 0, 0.5); + + /* 布局参数 */ + --max-width: 680px; +} + +/* 深色模式变量 */ +[data-theme="dark"] { + --bg-body: #111111; + --bg-card: #1e1e1e; + --bg-header: rgba(30, 30, 30, 0.85); + --bg-header-transparent: rgba(30, 30, 30, 0); + --bg-hover: rgba(255, 255, 255, 0.1); + --bg-active: rgba(255, 255, 255, 0.15); + --bg-input: #222222; + --bg-code: rgba(200, 200, 200, 0.1); + --bg-code-block: #2d2d2d; + --bg-popover: #2d2d2d; + --bg-mask: rgba(0, 0, 0, 0.9); + + --text-main: #e0e0e0; + --text-desc: #a0a0a0; + --text-muted: #777777; + --text-link: #7a95cc; + --text-quote: #999999; + + --border-main: #333333; + --border-quote: #444444; + --shadow-main: rgba(0, 0, 0, 0.3); + --shadow-header: rgba(0, 0, 0, 0.5); + --shadow-popover: 0 4px 20px rgba(0, 0, 0, 0.4); + --shadow-button: 0 2px 8px rgba(0, 0, 0, 0.3); + --shadow-button-hover: 0 4px 12px rgba(0, 0, 0, 0.45); + --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.3); + --shadow-text: 0 1px 1px rgba(0, 0, 0, 0.8); +} + +/* ========================================================================== + Voice Bubble - 语音气泡(1:1 移植自 Amigo) + ========================================================================== */ + +.amigo-voice-bubble { + position: relative; + background: #42c27a; + color: #111; + width: auto; + max-width: 200px; + min-width: 100px; + height: 36px; + border-radius: 8px; + display: inline-flex; + align-items: center; + justify-content: flex-start; + padding: 0 18px; + font-size: 18px; + font-weight: 500; + cursor: pointer; + -webkit-user-select: none; + user-select: none; + transition: background 0.2s, transform 0.1s; +} + +/* 左侧尾巴 */ +.amigo-voice-bubble::before { + content: ""; + position: absolute; + left: -8px; + top: 14px; + width: 0; + height: 0; + border-right: 10px solid #42c27a; + border-top: 6px solid transparent; + border-bottom: 6px solid transparent; +} + +.amigo-voice-bubble:active { + transform: scale(0.97); +} + +.amigo-voice-bubble.is-playing { + background: #38b56e; +} + +.amigo-voice-bubble.is-playing::before { + border-right-color: #38b56e; +} + +/* 语音图标动画 */ +.amigo-voice-icon { + display: flex; + align-items: center; + gap: 2px; + height: 16px; + margin-right: 8px; +} + +.amigo-voice-icon span { + display: block; + width: 3px; + border-radius: 2px; + background: currentColor; + animation: amigo-voice-wave 1.2s ease-in-out infinite; +} + +.amigo-voice-icon span:nth-child(1) { + height: 8px; + animation-delay: 0s; +} + +.amigo-voice-icon span:nth-child(2) { + height: 12px; + animation-delay: 0.15s; +} + +.amigo-voice-icon span:nth-child(3) { + height: 6px; + animation-delay: 0.3s; +} + +@keyframes amigo-voice-wave { + 0%, 100% { transform: scaleY(0.5); } + 50% { transform: scaleY(1); } +} + +.amigo-voice-duration { + color: #111; + font-size: 16px; + font-weight: 500; +} + +/* 首页语音气泡:柔和风格 */ +.moments-feed .amigo-voice-bubble { + background: var(--bg-hover); + color: var(--text-main); + border: 1px solid var(--border-main); + height: 34px; + min-width: 180px; + max-width: 170px; + padding: 0 14px; + font-size: 15px; + border-radius: 8px; +} + +.moments-feed .amigo-voice-bubble::before { + display: none; +} + +.moments-feed .amigo-voice-bubble.is-playing { + background: var(--bg-active); +} + +.moments-feed .amigo-voice-icon span { + background: var(--text-desc); +} + +.moments-feed .amigo-voice-duration { + color: var(--text-desc); + font-size: 15px; +} + +/* ========================================================================== + Music Card - 音乐卡片(1:1 移植自 Amigo) + ========================================================================== */ + +.amigo-music-card { + --music-accent: #d43c33; + --music-progress: 0%; + --music-card-bg: #f7f7f8; + --music-card-border: rgba(0, 0, 0, 0.05); + --music-card-hover-border: rgba(0, 0, 0, 0.08); + --music-card-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.75); + --music-cover-bg: #ececef; + --music-cover-image-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08); + --music-title-color: var(--text-main); + --music-artist-color: var(--text-desc); + --music-meta-color: var(--text-muted); + --music-button-border: rgba(0, 0, 0, 0.05); + --music-button-bg: linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(248, 248, 249, 0.92)); + --music-button-color: color-mix(in srgb, var(--music-accent) 60%, #111 40%); + --music-button-shadow: 0 6px 12px rgba(0, 0, 0, 0.08), 0 1px 2px rgba(0, 0, 0, 0.04); + --music-track-bg: rgba(0, 0, 0, 0.06); + --music-track-fill-shadow: 0 1px 3px color-mix(in srgb, var(--music-accent) 30%, transparent); + --music-thumb-bg: #fff; + --music-thumb-border: var(--music-accent); + --music-thumb-shadow: 0 1px 4px rgba(0, 0, 0, 0.15); + --music-equalizer-bg: rgba(0, 0, 0, 0.55); + padding: 12px; + border-radius: 14px; + border: 1px solid var(--music-card-border); + background: var(--music-card-bg); + box-shadow: var(--music-card-shadow); + transition: border-color 0.2s ease; +} + +.amigo-music-card__hero { + display: grid; + grid-template-columns: 62px minmax(0, 1fr); + gap: 12px; + align-items: center; +} + +.amigo-music-card__art { + position: relative; + width: 62px; + min-width: 62px; + border-radius: 11px; + overflow: hidden; +} + +.amigo-music-card__cover { + width: 62px; + height: 62px; + border-radius: 11px; + background: var(--music-cover-bg); +} + +.amigo-music-card__cover--image { + display: block; + object-fit: cover; + box-shadow: var(--music-cover-image-shadow); +} + +.amigo-music-card__meta { + min-width: 0; + display: flex; + flex-direction: column; + gap: 4px; + overflow: hidden; +} + +.amigo-music-card__title { + display: block; + overflow: hidden; + color: var(--music-title-color); + font-size: 16px; + font-weight: 600; + line-height: 1.22; + text-overflow: ellipsis; + white-space: nowrap; +} + +.amigo-music-card__subtitle { + display: flex; + align-items: center; + gap: 5px; + min-width: 0; +} + +.amigo-music-card__subtitle > span { + min-width: 0; +} + +.amigo-music-card__artist { + overflow: hidden; + font-size: 12px; + line-height: 1.3; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--music-artist-color); +} + +.amigo-music-card__play { + position: absolute; + right: 4px; + bottom: 4px; + z-index: 2; + display: inline-flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + border: 1px solid var(--music-button-border); + border-radius: 50%; + background: var(--music-button-bg); + color: var(--music-button-color); + box-shadow: var(--music-button-shadow); + cursor: pointer; + transition: border-color 0.16s ease, color 0.16s ease, box-shadow 0.16s ease; +} + +.amigo-music-card__play-icon { + width: 0; + height: 0; + margin-left: 1px; + border-top: 4px solid transparent; + border-bottom: 4px solid transparent; + border-left: 6px solid; +} + +.amigo-music-card[data-playing="true"] .amigo-music-card__play-icon { + width: 6px; + height: 8px; + margin-left: 0; + border: 0; + background: linear-gradient(90deg, currentColor 0 2px, transparent 2px 4px, currentColor 4px 6px, transparent 6px 100%); +} + +.amigo-music-card__equalizer { + position: absolute; + top: 5px; + left: 5px; + z-index: 2; + display: inline-flex; + align-items: end; + gap: 2px; + width: 0; + height: 12px; + padding: 0; + overflow: hidden; + border-radius: 999px; + background: var(--music-equalizer-bg); + opacity: 0; + transition: width 0.16s ease, padding 0.16s ease, opacity 0.16s ease; +} + +.amigo-music-card[data-playing="true"] .amigo-music-card__equalizer { + width: 16px; + padding: 2px 3px; + opacity: 0.9; +} + +.amigo-music-card__equalizer span { + width: 3px; + height: 4px; + border-radius: 999px; + background: #fff; + transform-origin: bottom center; +} + +.amigo-music-card[data-playing="true"] .amigo-music-card__equalizer span { + animation: amigo-music-bars 1s ease-in-out infinite; +} + +.amigo-music-card[data-playing="true"] .amigo-music-card__equalizer span:nth-child(2) { + animation-delay: 0.12s; +} + +.amigo-music-card[data-playing="true"] .amigo-music-card__equalizer span:nth-child(3) { + animation-delay: 0.24s; +} + +@keyframes amigo-music-bars { + 0%, 100% { height: 4px; } + 50% { height: 12px; } +} + +/* 时间线 */ +.amigo-music-card__timeline { + --music-track-height: 4px; + --music-thumb-size: 12px; + display: grid; + grid-template-columns: auto minmax(0, 1fr) auto; + grid-template-areas: "seek seek seek" "current . duration"; + align-items: center; + margin-top: 6px; + row-gap: 6px; + column-gap: 8px; +} + +.amigo-music-card__time { + min-width: 28px; + font-size: 10px; + font-weight: 500; + font-variant-numeric: tabular-nums; + line-height: 1; +} + +.amigo-music-card__time--current { + grid-area: current; + text-align: left; + color: var(--music-meta-color); +} + +.amigo-music-card__time--duration { + grid-area: duration; + text-align: right; + color: var(--music-meta-color); +} + +.amigo-music-card__seek-wrap { + grid-area: seek; + position: relative; + height: var(--music-thumb-size); + display: flex; + align-items: center; +} + +.amigo-music-card__seek { + -webkit-appearance: none; + appearance: none; + width: 100%; + height: var(--music-track-height); + border-radius: 999px; + background: var(--music-track-bg); + outline: none; + cursor: pointer; +} + +.amigo-music-card__seek::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: var(--music-thumb-size); + height: var(--music-thumb-size); + border-radius: 50%; + background: var(--music-thumb-bg); + border: 2px solid var(--music-thumb-border); + box-shadow: var(--music-thumb-shadow); + cursor: pointer; +} + +.amigo-music-card__seek::-moz-range-thumb { + width: var(--music-thumb-size); + height: var(--music-thumb-size); + border-radius: 50%; + background: var(--music-thumb-bg); + border: 2px solid var(--music-thumb-border); + box-shadow: var(--music-thumb-shadow); + cursor: pointer; +} + +.amigo-music-card__seek-thumb { + position: absolute; + left: 0; + top: 50%; + transform: translate(-50%, -50%); + width: var(--music-thumb-size); + height: var(--music-thumb-size); + border-radius: 50%; + background: var(--music-thumb-bg); + border: 2px solid var(--music-thumb-border); + box-shadow: var(--music-thumb-shadow); + pointer-events: none; + opacity: 0; + transition: opacity 0.15s ease; +} + +.amigo-music-card__seek:active ~ .amigo-music-card__seek-thumb { + opacity: 1; +} + +/* 响应式 */ +@media (max-width: 640px) { + .amigo-music-card { + padding: 11px; + } + .amigo-music-card__hero { + grid-template-columns: 56px minmax(0, 1fr); + gap: 10px; + } + .amigo-music-card__art, + .amigo-music-card__cover { + width: 56px; + height: 56px; + } + .amigo-music-card__title { + font-size: 15px; + } + .amigo-music-card__play { + right: 3px; + bottom: 3px; + } + .amigo-music-card__timeline { + --music-thumb-size: 11px; + row-gap: 5px; + } + .amigo-music-card__time { + min-width: 24px; + font-size: 9px; + } +} + +/* 深色模式 */ +[data-theme="dark"] .amigo-music-card { + --music-card-bg: #232427; + --music-card-border: rgba(255, 255, 255, 0.06); + --music-card-hover-border: rgba(255, 255, 255, 0.1); + --music-card-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03), 0 1px 3px rgba(0, 0, 0, 0.3); + --music-cover-bg: #2a2b2e; + --music-cover-image-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.06); + --music-title-color: #e8e8e8; + --music-artist-color: #9a9a9a; + --music-meta-color: #6a6a6a; + --music-button-border: rgba(255, 255, 255, 0.08); + --music-button-bg: linear-gradient(180deg, rgba(50, 50, 54, 0.98), rgba(40, 40, 44, 0.92)); + --music-button-color: color-mix(in srgb, var(--music-accent) 70%, #ddd 30%); + --music-button-shadow: 0 4px 10px rgba(0, 0, 0, 0.4), 0 1px 2px rgba(0, 0, 0, 0.2); + --music-track-bg: rgba(255, 255, 255, 0.08); + --music-thumb-bg: #3a3b3e; + --music-thumb-border: var(--music-accent); + --music-thumb-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); +} + +/* ========================================================================== + Live Photo - 实况照片 + ========================================================================== */ -/* ---- Live Photo ---- */ .live-photo { --border-radius: 4px; --live-photo-aspect: 4 / 5; @@ -120,226 +651,3 @@ .live-photo-mute-btn[data-muted="false"] .icon-muted { display: none; } - -/* ---- Voice Bubble ---- */ -.amigo-voice-bubble { - display: inline-flex; - align-items: center; - gap: 12px; - padding: 12px 16px; - background: linear-gradient(135deg, #95ec69 0%, #7dd652 100%); - border-radius: 16px; - cursor: pointer; - position: relative; - max-width: 200px; - transition: transform 0.2s ease, box-shadow 0.2s ease; -} - -.amigo-voice-bubble:hover { - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); -} - -.amigo-voice-bubble:active { - transform: translateY(0); -} - -.amigo-voice-icon { - display: flex; - align-items: center; - gap: 2px; - height: 20px; -} - -.amigo-voice-icon span { - display: block; - width: 3px; - background: #2d2d2d; - border-radius: 2px; - animation: voiceWave 1.2s ease-in-out infinite; -} - -.amigo-voice-icon span:nth-child(1) { - height: 8px; - animation-delay: 0s; -} - -.amigo-voice-icon span:nth-child(2) { - height: 14px; - animation-delay: 0.2s; -} - -.amigo-voice-icon span:nth-child(3) { - height: 10px; - animation-delay: 0.4s; -} - -.amigo-voice-bubble.playing .amigo-voice-icon span { - animation: voiceWaveActive 0.6s ease-in-out infinite; -} - -.amigo-voice-duration { - font-size: 14px; - color: #2d2d2d; - font-weight: 500; - min-width: 24px; -} - -.amigo-voice-unread { - position: absolute; - top: -4px; - right: -4px; - width: 8px; - height: 8px; - background: #ff4d4f; - border-radius: 50%; - border: 2px solid #fff; -} - -@keyframes voiceWave { - 0%, 100% { transform: scaleY(0.5); } - 50% { transform: scaleY(1); } -} - -@keyframes voiceWaveActive { - 0%, 100% { transform: scaleY(0.3); } - 50% { transform: scaleY(1); } -} - -/* ---- Music Card ---- */ -.amigo-music-card { - display: flex; - align-items: center; - gap: 16px; - padding: 16px; - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - border-radius: 12px; - max-width: 400px; - margin: 16px 0; - box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); - transition: transform 0.2s ease, box-shadow 0.2s ease; -} - -.amigo-music-card:hover { - transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(102, 126, 234, 0.6); -} - -.amigo-music-card__art { - position: relative; - width: 80px; - height: 80px; - flex-shrink: 0; -} - -.amigo-music-card__cover { - width: 100%; - height: 100%; - object-fit: cover; - border-radius: 8px; - box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); -} - -.amigo-music-card__play { - position: absolute; - inset: 0; - display: flex; - align-items: center; - justify-content: center; - background: rgba(0, 0, 0, 0.3); - border-radius: 8px; - border: none; - cursor: pointer; - opacity: 0; - transition: opacity 0.2s ease; -} - -.amigo-music-card__art:hover .amigo-music-card__play { - opacity: 1; -} - -.amigo-music-card__play-icon { - width: 36px; - height: 36px; - background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M8 5v14l11-7z'/%3E%3C/svg%3E") no-repeat center; - background-size: contain; -} - -.amigo-music-card__meta { - flex: 1; - min-width: 0; -} - -.amigo-music-card__title { - display: block; - font-size: 16px; - font-weight: 600; - color: #fff; - margin-bottom: 4px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.amigo-music-card__artist { - display: block; - font-size: 14px; - color: rgba(255, 255, 255, 0.8); - margin-bottom: 12px; -} - -.amigo-music-card__timeline { - display: flex; - align-items: center; - gap: 8px; -} - -.amigo-music-card__time { - font-size: 12px; - color: rgba(255, 255, 255, 0.7); - min-width: 36px; -} - -.amigo-music-card__seek-wrap { - flex: 1; - position: relative; -} - -.amigo-music-card__seek { - width: 100%; - height: 4px; - -webkit-appearance: none; - appearance: none; - background: rgba(255, 255, 255, 0.3); - border-radius: 2px; - outline: none; - cursor: pointer; -} - -.amigo-music-card__seek::-webkit-slider-thumb { - -webkit-appearance: none; - width: 12px; - height: 12px; - background: #fff; - border-radius: 50%; - cursor: pointer; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); -} - -/* Dark theme support */ -[data-theme="dark"] .live-photo { - background: #1a1a1a; -} - -[data-theme="dark"] .amigo-voice-bubble { - background: linear-gradient(135deg, #3d3d3d 0%, #2d2d2d 100%); -} - -[data-theme="dark"] .amigo-voice-icon span, -[data-theme="dark"] .amigo-voice-duration { - color: #fff; -} - -[data-theme="dark"] .amigo-music-card { - background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%); -} diff --git a/write-server/src/lib/bot/handlers.ts b/write-server/src/lib/bot/handlers.ts index befafd97..efd1d555 100644 --- a/write-server/src/lib/bot/handlers.ts +++ b/write-server/src/lib/bot/handlers.ts @@ -158,7 +158,8 @@ handlers.help = async (chatId) => { `💡 提示:`, `• 直接发送文字追加到正文`, `• 发送图片添加到文章`, - `• 发送语音自动转文字`, + `• 发送语音自动转文字并生成语音播放器`, + `• 发送 MP3 自动生成音乐卡片`, `• 输入 /cancel 取消写作`, ].join("\n")); } else { @@ -193,6 +194,18 @@ handlers.help = async (chatId) => { `🌐 快速访问:`, `├─ 📊 后台:https://post.usj.cc`, `└─ 🌍 博客:https://usj.cc`, + ``, + `━━━━━━━━━━━━━━━━━━`, + ``, + `📱 媒体文件说明:`, + `├─ 🎙️ 语音消息:自动转文字 + 生成语音播放器`, + `├─ 🎵 MP3 文件:交互式问答生成音乐卡片`, + `├─ 📷 普通图片:直接插入文章`, + `├─ 📷 HEIC 图片(iOS 实况照片):`, + `│ └─ 动态效果无法自动提取`, + `│ └─ 需先转为 GIF/视频再发送`, + `├─ 🎬 视频/GIF:直接插入文章`, + `└─ 📄 文档:保存到文章目录`, ].join("\n")); } }; diff --git a/write-server/src/lib/bot/poll.ts b/write-server/src/lib/bot/poll.ts index ff6d1e88..e5e421b0 100644 --- a/write-server/src/lib/bot/poll.ts +++ b/write-server/src/lib/bot/poll.ts @@ -542,15 +542,21 @@ async function handlePhotoEnhanced(chatId: number, photos: { file_id: string }[] const imgCaption = caption || "image"; appendContent(chatId, `\n![${imgCaption}](${filename})\n`); - await sendMessage(chatId, `📷 图片已保存:${filename} + // 只对 HEIC 文件(iOS 实况照片)显示提示 + const isHEIC = ext.toLowerCase() === "heic" || ext.toLowerCase() === "heif"; + if (isHEIC) { + await sendMessage(chatId, `📷 图片已保存:${filename} + +💡 检测到 iOS 实况照片(HEIC 格式) -💡 提示:如果您发送的是 iOS 实况照片(Live Photo), 动态效果无法自动提取。如需保留动态效果,请: - -1. 将实况照片转为视频/GIF 后重新发送 -2. 或者单独发送视频部分 +1. 在 iPhone 上使用「快捷指令」将实况照片转为 GIF +2. 或使用第三方 App 导出为视频后重新发送 继续发送其他内容...`, { parse_mode: "HTML" }); + } else { + await sendMessage(chatId, `📷 图片已保存:${filename}`); + } } catch (e) { console.error("[bot:photo]", e instanceof Error ? e.message : String(e)); await sendMessage(chatId, "❌ 图片保存失败");