Снимок состояния до переноса серверного UI

This commit is contained in:
AidarKC 2026-06-03 14:49:03 +04:00
parent 2c2aad1355
commit c97b3e3ec3
6 changed files with 41 additions and 54 deletions

View File

@ -0,0 +1,14 @@
# Диагностика больших voice/audio в Telegram-боте
- краткое описание фичи:
- Бот при большом voice/audio больше не отказывается заранее по метаданным Telegram. Теперь он сначала сообщает, что пробует скачать файл, затем отдельно сообщает об успешном скачивании и только после этого переходит к подготовке аудио и распознаванию через OpenAI.
- что именно проверять:
- Отправить в бота большой `voice` или `audio`, который раньше попадал под ранний отказ.
- Проверить, что сначала приходит сообщение о попытке скачать большой файл.
- Проверить два сценария:
- скачивание удалось: бот пишет об успешной загрузке и продолжает распознавание;
- скачивание не удалось: бот пишет именно о неудачном скачивании из Telegram, без ложной привязки к ошибке OpenAI.
- ожидаемый результат:
- Пользователь видит понятную поэтапную диагностику: попытка скачивания, результат скачивания и только потом следующий этап обработки.
- статус:
- pending

View File

@ -57,6 +57,7 @@ python3 SHiNE-agent-bot-coder/py_bot_service.py --selftest-codex "Ответь
## Длинные voice/audio ## Длинные voice/audio
- Если аудио короткое, бот отправляет его в OpenAI как раньше. - Если аудио короткое, бот отправляет его в OpenAI как раньше.
- Если аудио большое или длинное, бот локально пережимает его через `ffmpeg`, при необходимости режет на куски и распознаёт последовательно. - Если аудио большое или длинное, бот локально пережимает его через `ffmpeg`, при необходимости режет на куски и распознаёт последовательно.
- Если Telegram заранее сообщает большой размер файла, бот больше не отказывается сразу: сначала явно пишет, что пробует скачать файл, затем отдельно сообщает, удалось ли скачивание, и только после успешной загрузки переходит к подготовке аудио и OpenAI.
- Для очень больших файлов упираемся не только в OpenAI, но и в лимит обычного облачного Telegram Bot API на скачивание файла ботом. Для таких случаев нужно использовать локальный `telegram-bot-api` сервер и указать его через `TELEGRAM_API_BASE_URL`. - Для очень больших файлов упираемся не только в OpenAI, но и в лимит обычного облачного Telegram Bot API на скачивание файла ботом. Для таких случаев нужно использовать локальный `telegram-bot-api` сервер и указать его через `TELEGRAM_API_BASE_URL`.
## Запуск как systemd-сервис ## Запуск как systemd-сервис

View File

@ -18,7 +18,7 @@ import time
import traceback import traceback
import uuid import uuid
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any, Callable
from urllib import error, request from urllib import error, request
DEFAULT_ALLOWED_PLAYERS = ",".join([ DEFAULT_ALLOWED_PLAYERS = ",".join([
@ -1509,7 +1509,10 @@ class ShinePyBotService:
try: try:
if job.get("type") == "voice": if job.get("type") == "voice":
self._safe_send(chat_id, f"#{job_num}: распознаю голосовое...", reply_to=message_id) self._safe_send(chat_id, f"#{job_num}: распознаю голосовое...", reply_to=message_id)
recognized = self._transcribe_voice_job(job) recognized = self._transcribe_voice_job(
job,
status_cb=lambda note: self._safe_send(chat_id, f"#{job_num}: {note}", reply_to=message_id),
)
job["text"] = recognized job["text"] = recognized
self._append_history(history_path, "voice_transcription", {"jobId": job_id, "jobNum": job_num, "text": recognized}) self._append_history(history_path, "voice_transcription", {"jobId": job_id, "jobNum": job_num, "text": recognized})
preview = recognized.strip() preview = recognized.strip()
@ -2206,7 +2209,12 @@ class ShinePyBotService:
raise VoiceReplyError("OpenAI вернул пустой аудиофайл.") raise VoiceReplyError("OpenAI вернул пустой аудиофайл.")
return audio return audio
def _transcribe_voice_job(self, job: dict[str, Any]) -> str: def _transcribe_voice_job(
self,
job: dict[str, Any],
*,
status_cb: Callable[[str], Any] | None = None,
) -> str:
if not self.cfg.openai_api_key: if not self.cfg.openai_api_key:
raise VoiceTranscriptionError( raise VoiceTranscriptionError(
"не настроен ключ OpenAI для распознавания.", "не настроен ключ OpenAI для распознавания.",
@ -2225,17 +2233,11 @@ class ShinePyBotService:
media_type = (job.get("telegram_media_type") or "voice").strip() media_type = (job.get("telegram_media_type") or "voice").strip()
duration_seconds = int(job.get("telegram_duration_seconds") or 0) duration_seconds = int(job.get("telegram_duration_seconds") or 0)
telegram_file_size = int(job.get("telegram_file_size") or 0) telegram_file_size = int(job.get("telegram_file_size") or 0)
if self._telegram_cloud_download_is_likely_too_big(telegram_file_size): file_looks_big_for_cloud = self._telegram_cloud_download_is_likely_too_big(telegram_file_size)
limit_mb = self._bytes_to_mb(20 * 1024 * 1024) if file_looks_big_for_cloud and status_cb is not None:
actual_mb = self._bytes_to_mb(telegram_file_size) status_cb(
raise VoiceTranscriptionError( "файл большой, всё равно пробую скачать его из Telegram. "
( f"Предварительный размер около {self._bytes_to_mb(telegram_file_size)} MB."
f"Telegram не даст этому боту скачать такой файл через обычный Bot API "
f"(примерно {actual_mb} MB при лимите около {limit_mb} MB). "
f"Для очень длинных аудио нужен локальный `telegram-bot-api` сервер или другой способ доставки файла."
),
stage="telegram_get_file_too_big",
retryable=False,
) )
started_at = time.time() started_at = time.time()
print(f"[py-bot] transcribe start job={job_id} num={job_num} media={media_type}", flush=True) print(f"[py-bot] transcribe start job={job_id} num={job_num} media={media_type}", flush=True)
@ -2244,6 +2246,11 @@ class ShinePyBotService:
f"[py-bot] transcribe downloaded job={job_id} filename={filename} size={len(file_bytes)} bytes", f"[py-bot] transcribe downloaded job={job_id} filename={filename} size={len(file_bytes)} bytes",
flush=True, flush=True,
) )
if file_looks_big_for_cloud and status_cb is not None:
status_cb(
"скачивание из Telegram прошло успешно. "
f"Фактический размер около {self._bytes_to_mb(len(file_bytes))} MB, дальше готовлю аудио и отправляю в OpenAI."
)
prepared_parts = self._prepare_audio_parts_for_transcription( prepared_parts = self._prepare_audio_parts_for_transcription(
file_bytes, file_bytes,
filename, filename,
@ -2290,7 +2297,9 @@ class ShinePyBotService:
detail = str(e) detail = str(e)
if "file is too big" in detail.lower(): if "file is too big" in detail.lower():
raise VoiceTranscriptionError( raise VoiceTranscriptionError(
"Telegram считает файл слишком большим для скачивания через текущий Bot API. Для такого аудио нужен локальный `telegram-bot-api` сервер или другой способ передать файл боту.", "Файл большой: я попробовал скачать его через текущий Telegram Bot API, "
"но Telegram не дал это сделать. Для такого аудио нужен локальный `telegram-bot-api` "
"сервер или другой способ передать файл боту.",
stage="telegram_get_file_too_big", stage="telegram_get_file_too_big",
retryable=False, retryable=False,
detail=detail, detail=detail,

View File

@ -1,2 +1,2 @@
client.version=1.2.115 client.version=1.2.116
server.version=1.2.107 server.version=1.2.108

View File

@ -1,19 +0,0 @@
# UI deploy
Актуальный UI-деплой выполняется одной командой:
```bash
./gradlew deployUI
```
По умолчанию:
- хост: `player@93.170.12.154`
- домен: `https://shineup.me`
- путь: `/home/player/SHiNE/SHiNE-UI`
Переопределение при необходимости:
```bash
REMOTE_HOST=player@93.170.12.154 REMOTE_BASE_DIR=/home/player/SHiNE bash deploy_shine-PWA.sh
```

View File

@ -1,18 +0,0 @@
# MVP notes: Web Push
## Временное поведение (сделано для тестового стенда)
- Клиент отправляет push-подписку на сервер при каждом запуске после авторизации, даже если подписка не изменилась.
- Причина: на тестовом сервере/после переустановки БД запись о токене может пропасть, а клиент этого не узнает.
## Что доработать для production
- Вернуть режим "отправлять только при изменении подписки" как основной.
- Добавить безопасный механизм ресинхронизации:
- Вариант 1: периодическая принудительная отправка (например, 1 раз в N дней).
- Вариант 2: endpoint на сервере "есть ли подписка", и отправка только при отсутствии/рассинхроне.
- В логах разделить обычную отправку и принудительную, чтобы видеть лишний трафик.
- Добавить e2e-тесты сценариев:
- Переустановка сервера (потеря токена в БД).
- Смена браузерной подписки.
- Повторный запуск клиента без изменений.