Compare commits

...

2 Commits

Author SHA1 Message Date
ef71c67683 styling add
All checks were successful
continuous-integration/drone/push Build is passing
2025-12-24 15:49:39 +03:00
570f0b7ea7 Messages for indexing 2025-12-24 15:45:35 +03:00
4 changed files with 117 additions and 29 deletions

View File

@ -3,6 +3,7 @@ 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
@ -10,6 +11,7 @@ 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:
@ -60,12 +62,34 @@ 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)
@ -86,11 +110,37 @@ class DocumentUseCases:
) )
document = await self.document_repository.create(document) document = await self.document_repository.create(document)
if self.rag_service: if self.rag_service and telegram_id:
try: try:
await self.rag_service.index_document(document) await self._send_telegram_notification(
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,7 +69,8 @@ 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,8 +360,9 @@ 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>{result.get('title', filename)}</b>", f"<b>Название:</b> {result.get('title', filename)}\n\n"
f"📄 Документ сейчас индексируется. Вы получите уведомление, когда индексация завершится.\n\n",
parse_mode="HTML" parse_mode="HTML"
) )
else: else:

View File

@ -3,6 +3,8 @@ 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()
@ -60,22 +62,37 @@ 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"<b>Ответ:</b>\n{answer}\n\n" f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
f"💬 <b>Ответ:</b>\n\n"
f"{formatted_answer}\n\n"
) )
if sources: if sources:
response += f"<b>Источники:</b>\n" response += f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\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', 'Без названия')
response += f"{idx}. {title}\n" try:
response += "\n<i>Используйте /mycollections для просмотра всех коллекций</i>\n\n" from urllib.parse import unquote
title = unquote(title)
except:
pass
response += f" {idx}. {title}\n"
response += "\n<i>💡 Используйте /mycollections для просмотра всех коллекций</i>\n\n"
response += ( response += (
f"<b>Статус:</b> Premium (вопросов безлимитно)\n" f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
f"<b>Всего вопросов:</b> {user.questions_used}" f"✨ <b>Статус:</b> Premium (вопросов безлимитно)\n"
f"📊 <b>Всего вопросов:</b> {user.questions_used}"
) )
except Exception as e: except Exception as e:
@ -83,9 +100,12 @@ 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> Premium\n" f"❌ <b>Ошибка при генерации ответа.</b>\n"
f"<b>Всего вопросов:</b> {user.questions_used}" f"Попробуйте позже.\n\n"
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")
@ -109,40 +129,56 @@ 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"<b>Ответ:</b>\n{answer}\n\n" f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
f"💬 <b>Ответ:</b>\n\n"
f"{formatted_answer}\n\n"
) )
if sources: if sources:
response += f"<b>Источники:</b>\n" response += f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\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', 'Без названия')
response += f"{idx}. {title}\n" try:
response += "\n<i>Используйте /mycollections для просмотра всех коллекций</i>\n\n" from urllib.parse import unquote
title = unquote(title)
except:
pass
response += f" {idx}. {title}\n"
response += "\n<i>💡 Используйте /mycollections для просмотра всех коллекций</i>\n\n"
response += ( response += (
f"<b>Статус:</b> Бесплатный доступ\n" f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
f"<b>Использовано вопросов:</b> {user.questions_used}/{settings.FREE_QUESTIONS_LIMIT}\n" f"📊 <b>Статус:</b> Бесплатный доступ\n"
f"<b>Осталось бесплатных:</b> {remaining}\n\n" f"📈 <b>Использовано вопросов:</b> {user.questions_used}/{settings.FREE_QUESTIONS_LIMIT}\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"<b>Использовано вопросов:</b> {user.questions_used}/{settings.FREE_QUESTIONS_LIMIT}\n" f"Попробуйте позже.\n\n"
f"<b>Осталось бесплатных:</b> {remaining}\n\n" f"━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
f"<i>Для безлимитного доступа: /buy</i>" f"📊 <b>Статус:</b> Бесплатный доступ\n"
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")