From 827bc97e92bf92cd502604f43dbe2bb40b19a6eb Mon Sep 17 00:00:00 2001 From: Admin Date: Wed, 22 Apr 2026 16:36:40 +0400 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=99=EF=B8=8F=20AI=20Voice=20&?= =?UTF-8?q?=20Video=20recognition=20enabled=20via=20Faster-Whisper.=20Fix?= =?UTF-8?q?=20ffprobe=20path=20issues=20in=20systemd.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DEVELOPMENT_PLAN.md | 16 ++++ domovoy-bot.service | 2 +- handlers/voice.py | 74 ++++++++++-------- main.py | 65 ++++++--------- services/voice_service.py | 30 ++++--- ...4Cs7HLQqtmRAAJ6lwACT5A5S2Bo6N2mNVhEOwQ.oga | Bin 0 -> 28453 bytes 6 files changed, 100 insertions(+), 87 deletions(-) create mode 100644 temp_voice/AwACAgIAAxkBAAIEaWnon8hg8TzKl_27z4Cs7HLQqtmRAAJ6lwACT5A5S2Bo6N2mNVhEOwQ.oga diff --git a/DEVELOPMENT_PLAN.md b/DEVELOPMENT_PLAN.md index 7229cf2..b30d221 100644 --- a/DEVELOPMENT_PLAN.md +++ b/DEVELOPMENT_PLAN.md @@ -427,3 +427,19 @@ v2.0 🔮 Будущее --- *Документ обновляется после каждой версии* + +--- + +## 🤖 v4.3 — AI Voice & Media (Whisper AI) — ЗАВЕРШЕНО (22.04.2026) +**Статус:** ✅ Завершено + +**Функционал:** +- [x] Интеграция модели Faster-Whisper (small) для CPU +- [x] Авто-расшифровка голосовых сообщений (Voice) +- [x] Авто-расшифровка видео-сообщений (Video Notes / Кружочки) +- [x] Исправлена проблема с системными путями ffmpeg/ffprobe в systemd +- [x] Логирование расшифровок в базу данных `voice_recognition_logs` + +**Планы на будущее:** +- [ ] ⏳ **Расшифровка для жильцов:** Разрешить верифицированным пользователям использовать расшифровку (с квотой, например, 3 сообщения в день). +- [ ] 🧠 **AI-Ассистент:** Использование Gemini API для ответов на вопросы жильцов по базе знаний дома. diff --git a/domovoy-bot.service b/domovoy-bot.service index 46d8258..8c9ea66 100644 --- a/domovoy-bot.service +++ b/domovoy-bot.service @@ -7,7 +7,7 @@ Type=simple User=matrixhasyou Group=matrixhasyou WorkingDirectory=/home/matrixhasyou/domovoy_bot -Environment="PATH=/home/matrixhasyou/domovoy_bot/venv/bin" +Environment="PATH=/home/matrixhasyou/domovoy_bot/venv/bin:/usr/local/bin:/usr/bin:/bin" ExecStart=/home/matrixhasyou/domovoy_bot/venv/bin/python /home/matrixhasyou/domovoy_bot/main.py Restart=always RestartSec=10 diff --git a/handlers/voice.py b/handlers/voice.py index 9996d2b..b0bea33 100644 --- a/handlers/voice.py +++ b/handlers/voice.py @@ -1,58 +1,70 @@ import os import logging from aiogram import Router, F, Bot -from aiogram.types import Message, ContentType +from aiogram.types import Message from config import ADMIN_USER_ID from services.voice_service import VoiceService +from pathlib import Path logger = logging.getLogger(__name__) router = Router() -# Initialize VoiceService -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -db_path = os.path.join(BASE_DIR, "database", "domovoy.db") -voice_service = VoiceService(db_path=db_path, model_size="small") +_voice_service = None -@router.message(F.content_type.in_({ContentType.VOICE, ContentType.VIDEO_NOTE})) +def get_voice_service(): + global _voice_service + if _voice_service is None: + DB_PATH = "/home/matrixhasyou/domovoy_bot/database/domovoy.db" + _voice_service = VoiceService(db_path=DB_PATH, model_size="small") + return _voice_service + +@router.message(F.voice | F.video_note) async def handle_voice_message(message: Message, bot: Bot): - # Только для владельца - if message.from_user.id != ADMIN_USER_ID: + if int(message.from_user.id) != int(ADMIN_USER_ID): return - msg_type = "VOICE" if message.voice else "VIDEO_NOTE" - file_id = message.voice.file_id if message.voice else message.video_note.file_id - - # Отправляем статус "печатает" + status_msg = await message.reply("⏳ Расшифровываю...", parse_mode="HTML") await bot.send_chat_action(message.chat.id, "typing") - # Создаем папку для временных файлов если её нет - temp_dir = os.path.join(BASE_DIR, "temp_voice") - os.makedirs(temp_dir, exist_ok=True) + # Абсолютный путь к временной папке + temp_dir = Path("/home/matrixhasyou/domovoy_bot/temp_voice") + temp_dir.mkdir(parents=True, exist_ok=True) try: + if message.voice: + file_id = message.voice.file_id + ext = "oga" + msg_type = "VOICE" + else: + file_id = message.video_note.file_id + ext = "mp4" + msg_type = "VIDEO_NOTE" + file = await bot.get_file(file_id) - file_path = os.path.join(temp_dir, f"{file_id}.oga" if message.voice else f"{file_id}.mp4") - await bot.download_file(file.file_path, file_path) + file_path = temp_dir / f"{file_id}.{ext}" - # Распознавание - text = voice_service.transcribe( - file_path=file_path, - user_id=message.from_user.id, - username=message.from_user.username or "Unknown", - msg_type=msg_type - ) + # Скачиваем + await bot.download_file(file.file_path, str(file_path)) - # Удаляем временный файл - if os.path.exists(file_path): os.remove(file_path) + # Ждем секунду, чтобы ФС успела "переварить" файл + import asyncio + await asyncio.sleep(1) + + if not file_path.exists(): + raise FileNotFoundError(f"Файл {file_path} не был скачан!") + + service = get_voice_service() + text = service.transcribe(str(file_path), message.from_user.id, message.from_user.username or "Owner", msg_type) + + if file_path.exists(): file_path.unlink() if text: - await message.reply(f"📝 Распознанный текст:\n\n{text}", parse_mode="HTML") + await status_msg.edit_text(f"📝 РАСШИФРОВКА:\n\n{text}", parse_mode="HTML") else: - await message.reply("🔇 Аудио сообщение пустое или не распознано.") + await status_msg.edit_text("🔇 Не удалось распознать текст.") except Exception as e: - logger.error(f"Voice handler error: {e}") - await message.reply(f"❌ Ошибка обработки: {e}") + logger.error(f"❌ Voice Error: {e}") + await status_msg.edit_text(f"❌ Ошибка: {e}") -# Export router voice_router = router diff --git a/main.py b/main.py index 43585a5..d09bfe5 100644 --- a/main.py +++ b/main.py @@ -1,15 +1,4 @@ #!/usr/bin/env python3 -""" -🏠 Домовой Бот — Telegram-бот для чата многоквартирного дома - -ВЕРСИЯ: v4.1 — VOICE-REBORN -ДАТА ФИКСАЦИИ: 21 апреля 2026 г. -СТАТУС: ✅ ОБНОВЛЕН - -НОВОЕ В v4.1: -- ✅ Распознавание голосовых сообщений и видеокружков (Whisper Small) -- ✅ Логирование распознанного текста в БД -""" import logging import asyncio import sys @@ -21,18 +10,14 @@ BASE_DIR = Path(__file__).resolve().parent sys.path.insert(0, str(BASE_DIR)) from aiogram import Bot, Dispatcher -from aiogram.client.default import DefaultBotProperties from aiogram.enums import ParseMode -from aiogram.filters import Command -from aiogram.types import Message -from aiogram.client.session.aiohttp import AiohttpSession from aiogram.fsm.storage.memory import MemoryStorage -from config import ( - BOT_TOKEN, ADMIN_USER_ID, ADMIN_CHAT_ID, get_proxy_url, - USE_PROXY, PROXY_TYPE, PROXY_HOST, PROXY_PORT -) +from config import BOT_TOKEN, ADMIN_USER_ID from database.db import init_db + +# Импортируем роутеры +from handlers.voice import voice_router from handlers import ( admin_router, users_router, verification_router, chat_monitor_router, security_router, apartments_router, antimat_router, phones_router, @@ -41,10 +26,8 @@ from handlers import ( toxicity_router, profile_history_router, achievements_router, thanks_router, events_router, initiative_group_router ) -from handlers.voice import voice_router from handlers.mutual_aid import router as mutual_aid_router from services.scheduler import Scheduler -from services.web_server import WebServerService # Настройка логирования logging.basicConfig( @@ -55,7 +38,6 @@ logging.basicConfig( logging.StreamHandler() ] ) - logger = logging.getLogger(__name__) async def on_startup(bot: Bot): @@ -63,33 +45,31 @@ async def on_startup(bot: Bot): scheduler = Scheduler(bot) scheduler.start() try: - await bot.send_message( - ADMIN_USER_ID, - '✅ LKM37 CORE v4.1 VOICE-REBORN АКТИВИРОВАН\n\n' - 'Модуль распознавания речи (Whisper Small) запущен.\n' - 'Шли мне голосовухи — я их теперь "читаю"!', - parse_mode=ParseMode.HTML - ) - except Exception as e: - logger.error(f'Ошибка уведомления админа: {e}') + await bot.send_message(ADMIN_USER_ID, '✅ LKM37 CORE v4.3 АКТИВИРОВАН') + except: pass async def main(): from bot_instance import get_bot bot = get_bot() dp = Dispatcher(storage=MemoryStorage()) - # Регистрация роутеров - routers = [ - admin_router, users_router, verification_router, chat_monitor_router, - security_router, apartments_router, antimat_router, phones_router, - polls_router, antispam_router, ratelimit_router, schedule_router, - ads_router, payments_router, multicount_router, topics_router, - toxicity_router, profile_history_router, achievements_router, - thanks_router, events_router, initiative_group_router, - mutual_aid_router, voice_router # Добавляем голосовой роутер + # КРИТИЧЕСКИ ВАЖНО: voice_router должен быть САМЫМ ПЕРВЫМ + dp.include_router(voice_router) + + # Регистрация остальных + dp.include_router(admin_router) + dp.include_router(users_router) + + other_routers = [ + verification_router, chat_monitor_router, security_router, + apartments_router, antimat_router, phones_router, polls_router, + antispam_router, ratelimit_router, schedule_router, ads_router, + payments_router, multicount_router, topics_router, toxicity_router, + profile_history_router, achievements_router, thanks_router, + events_router, initiative_group_router, mutual_aid_router ] - for r in routers: + for r in other_routers: dp.include_router(r) from handlers.smart_broadcast import router as smart_broadcast_router @@ -98,8 +78,7 @@ async def main(): dp.include_router(digest_router) dp.startup.register(on_startup) - - logger.info('🤖 Ожидание команд субъектов...') + logger.info('🤖 Бот запускается...') await dp.start_polling(bot) if __name__ == '__main__': diff --git a/services/voice_service.py b/services/voice_service.py index 7cf0829..c04b8e5 100644 --- a/services/voice_service.py +++ b/services/voice_service.py @@ -4,14 +4,19 @@ from faster_whisper import WhisperModel import sqlite3 from datetime import datetime from pydub import AudioSegment +from pathlib import Path logger = logging.getLogger(__name__) +# ЖЕСТКИЙ ФИКС ПУТЕЙ К БИНАРНИКАМ +AudioSegment.converter = "/usr/bin/ffmpeg" +AudioSegment.ffprobe = "/usr/bin/ffprobe" + class VoiceService: def __init__(self, db_path: str, model_size: str = "small"): self.db_path = db_path self.model_size = model_size - # CPU-only optimization: int8 is fast on standard CPUs + logger.info(f"💾 Загрузка модели Whisper ({model_size})...") self.model = WhisperModel(model_size, device="cpu", compute_type="int8") self._init_db() @@ -33,28 +38,31 @@ class VoiceService: conn.close() def transcribe(self, file_path: str, user_id: int, username: str, msg_type: str) -> str: + f_path = Path(file_path) + if not f_path.exists(): + raise FileNotFoundError(f"Файл не найден: {file_path}") + try: - start_time = datetime.now() + logger.info(f"🔄 Конвертация {f_path.name}...") + wav_path = str(f_path.with_suffix('.wav')) - # Convert to wav if needed (Telegram uses .oga/.ogg) - wav_path = file_path.rsplit('.', 1)[0] + ".wav" - audio = AudioSegment.from_file(file_path) + # Конвертируем + audio = AudioSegment.from_file(str(f_path)) duration = int(len(audio) / 1000) audio.export(wav_path, format="wav") + logger.info(f"🧠 Расшифровка ({duration}с)...") segments, info = self.model.transcribe(wav_path, beam_size=5) text = " ".join([segment.text for segment in segments]).strip() - # Clean up temp files + # Чистим за собой if os.path.exists(wav_path): os.remove(wav_path) - # Log to DB self._log_to_db(user_id, username, msg_type, duration, text) - return text except Exception as e: - logger.error(f"Transcription error: {e}") - return f"[Ошибка распознавания]: {e}" + logger.error(f"❌ Ошибка транскрибации: {e}", exc_info=True) + raise e def _log_to_db(self, user_id, username, msg_type, duration, text): try: @@ -68,5 +76,3 @@ class VoiceService: conn.close() except Exception as e: logger.error(f"DB Log error: {e}") - -# Global instance will be initialized in main.py or handlers diff --git a/temp_voice/AwACAgIAAxkBAAIEaWnon8hg8TzKl_27z4Cs7HLQqtmRAAJ6lwACT5A5S2Bo6N2mNVhEOwQ.oga b/temp_voice/AwACAgIAAxkBAAIEaWnon8hg8TzKl_27z4Cs7HLQqtmRAAJ6lwACT5A5S2Bo6N2mNVhEOwQ.oga new file mode 100644 index 0000000000000000000000000000000000000000..fbacfee146d93f2d593c545ab6941e14ec54ffb0 GIT binary patch literal 28453 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 z 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 Z