Compare commits

..

No commits in common. "ef71c67683d94491127a93055699a96a57e73ebb" and "1b550e65034a3f261417a489efd01f45cd345345" have entirely different histories.

4 changed files with 29 additions and 117 deletions

View File

@ -3,7 +3,6 @@ Use cases для работы с документами
""" """
from uuid import UUID from uuid import UUID
from typing import BinaryIO, Optional from typing import BinaryIO, Optional
import httpx
from src.domain.entities.document import Document from src.domain.entities.document import Document
from src.domain.repositories.document_repository import IDocumentRepository from src.domain.repositories.document_repository import IDocumentRepository
from src.domain.repositories.collection_repository import ICollectionRepository from src.domain.repositories.collection_repository import ICollectionRepository
@ -11,7 +10,6 @@ from src.domain.repositories.collection_access_repository import ICollectionAcce
from src.application.services.document_parser_service import DocumentParserService from src.application.services.document_parser_service import DocumentParserService
from src.application.services.rag_service import RAGService from src.application.services.rag_service import RAGService
from src.shared.exceptions import NotFoundError, ForbiddenError from src.shared.exceptions import NotFoundError, ForbiddenError
from src.shared.config import settings
class DocumentUseCases: class DocumentUseCases:
@ -62,34 +60,12 @@ class DocumentUseCases:
) )
return await self.document_repository.create(document) return await self.document_repository.create(document)
async def _send_telegram_notification(self, telegram_id: str, message: str):
"""Отправить уведомление пользователю через Telegram Bot API"""
if not settings.TELEGRAM_BOT_TOKEN:
return
try:
url = f"https://api.telegram.org/bot{settings.TELEGRAM_BOT_TOKEN}/sendMessage"
async with httpx.AsyncClient(timeout=5.0) as client:
response = await client.post(
url,
json={
"chat_id": telegram_id,
"text": message,
"parse_mode": "HTML"
}
)
if response.status_code != 200:
print(f"Failed to send Telegram notification: {response.status_code}")
except Exception as e:
print(f"Error sending Telegram notification: {e}")
async def upload_and_parse_document( async def upload_and_parse_document(
self, self,
collection_id: UUID, collection_id: UUID,
file: BinaryIO, file: BinaryIO,
filename: str, filename: str,
user_id: UUID, user_id: UUID
telegram_id: Optional[str] = None
) -> Document: ) -> Document:
"""Загрузить и распарсить документ, затем автоматически проиндексировать""" """Загрузить и распарсить документ, затем автоматически проиндексировать"""
collection = await self.collection_repository.get_by_id(collection_id) collection = await self.collection_repository.get_by_id(collection_id)
@ -110,37 +86,11 @@ class DocumentUseCases:
) )
document = await self.document_repository.create(document) document = await self.document_repository.create(document)
if self.rag_service and telegram_id: if self.rag_service:
try: try:
await self._send_telegram_notification( await self.rag_service.index_document(document)
telegram_id,
"🔄 <b>Начинаю индексацию документа...</b>\n\n"
f"📄 <b>Документ:</b> {title}\n\n"
f"Это может занять некоторое время.\n"
f"Вы получите уведомление по завершении."
)
chunks = await self.rag_service.index_document(document)
await self._send_telegram_notification(
telegram_id,
"✅ <b>Индексация завершена!</b>\n\n"
f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
f"📄 <b>Документ:</b> {title}\n"
f"📊 <b>Проиндексировано чанков:</b> {len(chunks)}\n\n"
f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
f"💡 <b>Теперь вы можете задавать вопросы по этому документу!</b>\n"
f"Просто напишите ваш вопрос, и я найду ответ на основе загруженного документа."
)
except Exception as e: except Exception as e:
print(f"Ошибка при автоматической индексации документа {document.document_id}: {e}") print(f"Ошибка при автоматической индексации документа {document.document_id}: {e}")
if telegram_id:
await self._send_telegram_notification(
telegram_id,
"⚠️ <b>Ошибка при индексации</b>\n\n"
f"Документ загружен, но индексация не завершена.\n"
f"Ошибка: {str(e)[:200]}"
)
return document return document

View File

@ -69,8 +69,7 @@ async def upload_document(
collection_id=collection_id, collection_id=collection_id,
file=file.file, file=file.file,
filename=file.filename, filename=file.filename,
user_id=current_user.user_id, user_id=current_user.user_id
telegram_id=current_user.telegram_id
) )
return DocumentResponse.from_entity(document) return DocumentResponse.from_entity(document)

View File

@ -360,9 +360,8 @@ async def process_upload_document(message: Message, state: FSMContext):
if result: if result:
await message.answer( await message.answer(
f"<b>✅ Документ загружен и добавлен в коллекцию</b>\n\n" f"<b>Документ загружен</b>\n\n"
f"<b>Название:</b> {result.get('title', filename)}\n\n" f"Название: <b>{result.get('title', filename)}</b>",
f"📄 Документ сейчас индексируется. Вы получите уведомление, когда индексация завершится.\n\n",
parse_mode="HTML" parse_mode="HTML"
) )
else: else:

View File

@ -3,8 +3,6 @@ from aiogram.types import Message
from tg_bot.config.settings import settings from tg_bot.config.settings import settings
from tg_bot.domain.user_service import UserService, User from tg_bot.domain.user_service import UserService, User
from tg_bot.application.services.rag_service import RAGService from tg_bot.application.services.rag_service import RAGService
import re
router = Router() router = Router()
rag_service = RAGService() rag_service = RAGService()
@ -62,37 +60,22 @@ async def process_premium_question(message: Message, user: User, question_text:
# Беседа уже сохранена в бэкенде через API /rag/question # Беседа уже сохранена в бэкенде через API /rag/question
import re
formatted_answer = answer
formatted_answer = re.sub(r'\*\*(.+?)\*\*', r'<b>\1</b>', formatted_answer)
formatted_answer = re.sub(r'^(\d+)\.\s+', r'\1. ', formatted_answer, flags=re.MULTILINE)
formatted_answer = formatted_answer.replace("- ", "")
response = ( response = (
f"<b>Ваш вопрос:</b>\n" f"<b>Ваш вопрос:</b>\n"
f"<i>{question_text[:200]}</i>\n\n" f"<i>{question_text[:200]}</i>\n\n"
f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n" f"<b>Ответ:</b>\n{answer}\n\n"
f"💬 <b>Ответ:</b>\n\n"
f"{formatted_answer}\n\n"
) )
if sources: if sources:
response += f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n" response += f"<b>Источники:</b>\n"
response += f"📚 <b>Источники:</b>\n"
for idx, source in enumerate(sources[:5], 1): for idx, source in enumerate(sources[:5], 1):
title = source.get('title', 'Без названия') title = source.get('title', 'Без названия')
try: response += f"{idx}. {title}\n"
from urllib.parse import unquote response += "\n<i>Используйте /mycollections для просмотра всех коллекций</i>\n\n"
title = unquote(title)
except:
pass
response += f" {idx}. {title}\n"
response += "\n<i>💡 Используйте /mycollections для просмотра всех коллекций</i>\n\n"
response += ( response += (
f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n" f"<b>Статус:</b> Premium (вопросов безлимитно)\n"
f"✨ <b>Статус:</b> Premium (вопросов безлимитно)\n" f"<b>Всего вопросов:</b> {user.questions_used}"
f"📊 <b>Всего вопросов:</b> {user.questions_used}"
) )
except Exception as e: except Exception as e:
@ -100,12 +83,9 @@ async def process_premium_question(message: Message, user: User, question_text:
response = ( response = (
f"<b>Ваш вопрос:</b>\n" f"<b>Ваш вопрос:</b>\n"
f"<i>{question_text[:200]}</i>\n\n" f"<i>{question_text[:200]}</i>\n\n"
f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n" f"Ошибка при генерации ответа. Попробуйте позже.\n\n"
f"❌ <b>Ошибка при генерации ответа.</b>\n" f"<b>Статус:</b> Premium\n"
f"Попробуйте позже.\n\n" f"<b>Всего вопросов:</b> {user.questions_used}"
f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
f"✨ <b>Статус:</b> Premium\n"
f"📊 <b>Всего вопросов:</b> {user.questions_used}"
) )
await message.answer(response, parse_mode="HTML") await message.answer(response, parse_mode="HTML")
@ -129,56 +109,40 @@ async def process_free_question(message: Message, user: User, question_text: str
# Уже все сохранили через /rag/question # Уже все сохранили через /rag/question
formatted_answer = answer
formatted_answer = re.sub(r'\*\*(.+?)\*\*', r'<b>\1</b>', formatted_answer)
formatted_answer = re.sub(r'^(\d+)\.\s+', r'\1. ', formatted_answer, flags=re.MULTILINE)
formatted_answer = formatted_answer.replace("- ", "")
response = ( response = (
f"<b>Ваш вопрос:</b>\n" f"<b>Ваш вопрос:</b>\n"
f"<i>{question_text[:200]}</i>\n\n" f"<i>{question_text[:200]}</i>\n\n"
f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n" f"<b>Ответ:</b>\n{answer}\n\n"
f"💬 <b>Ответ:</b>\n\n"
f"{formatted_answer}\n\n"
) )
if sources: if sources:
response += f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n" response += f"<b>Источники:</b>\n"
response += f"📚 <b>Источники:</b>\n"
for idx, source in enumerate(sources[:5], 1): for idx, source in enumerate(sources[:5], 1):
title = source.get('title', 'Без названия') title = source.get('title', 'Без названия')
try: response += f"{idx}. {title}\n"
from urllib.parse import unquote response += "\n<i>Используйте /mycollections для просмотра всех коллекций</i>\n\n"
title = unquote(title)
except:
pass
response += f" {idx}. {title}\n"
response += "\n<i>💡 Используйте /mycollections для просмотра всех коллекций</i>\n\n"
response += ( response += (
f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n" f"<b>Статус:</b> Бесплатный доступ\n"
f"📊 <b>Статус:</b> Бесплатный доступ\n" f"<b>Использовано вопросов:</b> {user.questions_used}/{settings.FREE_QUESTIONS_LIMIT}\n"
f"📈 <b>Использовано вопросов:</b> {user.questions_used}/{settings.FREE_QUESTIONS_LIMIT}\n" f"<b>Осталось бесплатных:</b> {remaining}\n\n"
f"🎯 <b>Осталось бесплатных:</b> {remaining}\n\n"
) )
if remaining <= 3 and remaining > 0: if remaining <= 3 and remaining > 0:
response += f"⚠️ <i>Осталось мало вопросов! Для продолжения используйте /buy</i>\n\n" response += f"<i>Осталось мало вопросов! Для продолжения используйте /buy</i>\n\n"
response += f"💎 <i>Для безлимитного доступа: /buy</i>" response += f"<i>Для безлимитного доступа: /buy</i>"
except Exception as e: except Exception as e:
print(f"Error generating answer: {e}") print(f"Error generating answer: {e}")
response = ( response = (
f"<b>Ваш вопрос:</b>\n" f"<b>Ваш вопрос:</b>\n"
f"<i>{question_text[:200]}</i>\n\n" f"<i>{question_text[:200]}</i>\n\n"
f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n" f"Ошибка при генерации ответа. Попробуйте позже.\n\n"
f"❌ <b>Ошибка при генерации ответа.</b>\n" f"<b>Статус:</b> Бесплатный доступ\n"
f"Попробуйте позже.\n\n" f"<b>Использовано вопросов:</b> {user.questions_used}/{settings.FREE_QUESTIONS_LIMIT}\n"
f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n" f"<b>Осталось бесплатных:</b> {remaining}\n\n"
f"📊 <b>Статус:</b> Бесплатный доступ\n" f"<i>Для безлимитного доступа: /buy</i>"
f"📈 <b>Использовано вопросов:</b> {user.questions_used}/{settings.FREE_QUESTIONS_LIMIT}\n"
f"🎯 <b>Осталось бесплатных:</b> {remaining}\n\n"
f"💎 <i>Для безлимитного доступа: /buy</i>"
) )
await message.answer(response, parse_mode="HTML") await message.answer(response, parse_mode="HTML")