From 9e2dcaee53dbf156fefda4dd36a4f8caa1f5fcdc Mon Sep 17 00:00:00 2001 From: harold Date: Sat, 8 Feb 2025 11:41:20 +0500 Subject: [PATCH] add notify for chat --- .../sql_scripts/create_table.sql | 3 +- .../src/core/database/chats.py | 13 +++++++-- .../src/core/database/successes.py | 19 +++++++++++-- .../src/core/settings/base.py | 3 ++ .../src/core/settings/notify.py | 5 ++++ .../src/core/tg_service/service.py | 23 ++++++++++++++- .../src/core/tg_service/utils.py | 28 +++++++++++++++++++ telegram-application/src/core/workers/crud.py | 24 +++++++++++++++- .../src/core/workers/rmq_worker_handler.py | 17 +++++++++++ .../src/core/workers/schemas.py | 4 +-- 10 files changed, 129 insertions(+), 10 deletions(-) create mode 100644 telegram-application/src/core/settings/notify.py diff --git a/telegram-application/sql_scripts/create_table.sql b/telegram-application/sql_scripts/create_table.sql index 1128e4b..eb321da 100644 --- a/telegram-application/sql_scripts/create_table.sql +++ b/telegram-application/sql_scripts/create_table.sql @@ -26,6 +26,7 @@ CREATE TABLE tg_messages ( CREATE TABLE successed ( id BIGSERIAL PRIMARY KEY UNIQUE, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, - chat_id BIGINT NOT NULL REFERENCES tgchats(id) ON DELETE CASCADE, + reason TEXT NOT NULL, + chat_id UUID NOT NULL REFERENCES tgchats(slice_id) ON DELETE CASCADE, created_at TIMESTAMP DEFAULT NOW() NOT NULL ); diff --git a/telegram-application/src/core/database/chats.py b/telegram-application/src/core/database/chats.py index f98d748..3eac3f9 100644 --- a/telegram-application/src/core/database/chats.py +++ b/telegram-application/src/core/database/chats.py @@ -1,8 +1,17 @@ -from sqlalchemy.orm import Mapped, mapped_column +from typing import TYPE_CHECKING + +from sqlalchemy.orm import Mapped, relationship from src.core.database import Base +if TYPE_CHECKING: + from src.core.database import TgMessage + class TgChat(Base): chat_type: Mapped[str] - title: Mapped[str] \ No newline at end of file + title: Mapped[str] + + message_relationship: Mapped[list["TgMessage"]] = relationship( + backref="tgchat_relationship", + ) \ No newline at end of file diff --git a/telegram-application/src/core/database/successes.py b/telegram-application/src/core/database/successes.py index cd57110..9de3e19 100644 --- a/telegram-application/src/core/database/successes.py +++ b/telegram-application/src/core/database/successes.py @@ -1,10 +1,15 @@ from datetime import datetime +from typing import TYPE_CHECKING +from uuid import UUID from sqlalchemy import ForeignKey, func -from sqlalchemy.orm import Mapped, mapped_column +from sqlalchemy.orm import Mapped, mapped_column, relationship from src.core.database import Base +if TYPE_CHECKING: + from src.core.database import User, TgChat + class Success(Base): __tablename__ = 'successed' @@ -16,10 +21,18 @@ class Success(Base): user_id: Mapped[str] = mapped_column( ForeignKey('users.id', ondelete='CASCADE'), ) - chat_id: Mapped[int] = mapped_column( - ForeignKey('tgchats.id', ondelete='CASCADE'), + slice_id: Mapped[UUID] = mapped_column( + ForeignKey('tgchats.slice_id', ondelete='CASCADE'), ) + reason: Mapped[str] created_at: Mapped[datetime] = mapped_column( default=datetime.utcnow, server_default=func.now() + ) + + user_relationship: Mapped["User"] = relationship( + backref="success_relationship" + ) + chat_relationship: Mapped["TgChat"] = relationship( + backref="success_relationship" ) \ No newline at end of file diff --git a/telegram-application/src/core/settings/base.py b/telegram-application/src/core/settings/base.py index 9a97279..ac044f0 100644 --- a/telegram-application/src/core/settings/base.py +++ b/telegram-application/src/core/settings/base.py @@ -5,6 +5,7 @@ from src.core.common.constants import PydanticEnvPrefixEnum, EnvFileLocation, En from src.core.settings.database import DatabaseSettings from src.core.settings.gemini import GeminiSettings from src.core.settings.groq import GroqSettings +from src.core.settings.notify import NotifySettings from src.core.settings.rabbitmq import RabbitmqSettings from src.core.settings.redis import RedisSettings @@ -23,6 +24,8 @@ class Settings(BaseSettings): POSTGRES: DatabaseSettings + NOTIFY: NotifySettings = NotifySettings() + GROQ: GroqSettings GEMINI: GeminiSettings REDIS: RedisSettings diff --git a/telegram-application/src/core/settings/notify.py b/telegram-application/src/core/settings/notify.py new file mode 100644 index 0000000..3f5d344 --- /dev/null +++ b/telegram-application/src/core/settings/notify.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + + +class NotifySettings(BaseModel): + CHAT_ID: int = -4781950241 \ No newline at end of file diff --git a/telegram-application/src/core/tg_service/service.py b/telegram-application/src/core/tg_service/service.py index 8860ba5..e7a8e4b 100644 --- a/telegram-application/src/core/tg_service/service.py +++ b/telegram-application/src/core/tg_service/service.py @@ -2,13 +2,17 @@ import uuid from pyrogram.types.user_and_chats import User as PyroUser, Chat as PyroChat -from src.core.database import TgChat, User +from src.core.database import TgChat, User, TgMessage +from src.core.database.successes import Success from src.core.rabbitmq.connect import message_handler_publisher from src.core.redis_helper.redis_connect import redis_client +from src.core.settings.base import settings from src.core.tg_service.constants import MESSAGE_CHANG_SIZE from src.core.tg_service.schemas import UserFromMessageSchema, MessagesForSendToWorkersSchema, MessageFromChatSchema from src.core.database.connect import db_helper from src.core.tg_service import crud as tg_crud +from src.core.tg_service.utils import create_and_format_message +from src.main import app async def check_user_exists( @@ -88,4 +92,21 @@ async def check_chunk_state_and_publish( data[chat_id].append(message_schema) +async def notify_for_success( + messages: list[TgMessage], + chat: TgChat, + success_reason: Success, + user_model: User, +) -> None: + message = create_and_format_message( + messages=messages, + reason=success_reason.reason, + user_model=user_model, + chat=chat, + ) + await app.send_message( + chat_id=settings.NOTIFY.CHAT_ID, + message=message, + parse_mode="Markdown", + ) diff --git a/telegram-application/src/core/tg_service/utils.py b/telegram-application/src/core/tg_service/utils.py index e4fe738..d05fa01 100644 --- a/telegram-application/src/core/tg_service/utils.py +++ b/telegram-application/src/core/tg_service/utils.py @@ -1,6 +1,9 @@ from pyrogram.types import Message from pyrogram.enums import ChatType +from src.core.database import TgMessage, User, TgChat + + def check_message_condition( message: Message, ) -> bool: @@ -11,3 +14,28 @@ def check_message_condition( ) return all(conditions) + +def create_and_format_message( + messages: list[TgMessage], + reason: str, + chat: TgChat, + user_model: User, +) -> str: + if user_model.username: + user_link = f"{user_model.username}](tg://user?id={user_model.id})" + else: + user_link = f"ID: {user_model.id}" + + messages_text = "\n".join( + f"{msg.message_time.isoformat()}: {msg.text}\n" for msg in messages + ) + chat_link = f"https://t.me/c/{str(chat.id)[4:]}" if str(chat.id).startswith( + "-100") else f"https://t.me/{chat.id}" + + return f"""🔥 *Найдена успешка!* + 👤 *Пользователь:* {user_link} + 🐩 *Чат*: [{chat.title}]({chat_link}) + 📌 *Причина:* {reason} + + 📝 *Диалог:* {messages_text} + """ \ No newline at end of file diff --git a/telegram-application/src/core/workers/crud.py b/telegram-application/src/core/workers/crud.py index bd5ccbf..2d869a0 100644 --- a/telegram-application/src/core/workers/crud.py +++ b/telegram-application/src/core/workers/crud.py @@ -1,6 +1,10 @@ -from sqlalchemy import insert +from uuid import UUID + +from sqlalchemy import insert, select +from sqlalchemy.orm import selectinload, joinedload from sqlalchemy.ext.asyncio import AsyncSession +from src.core.database import TgMessage, TgChat from src.core.workers.schemas import ResponseFromGeminiSchema from src.core.database.successes import Success @@ -19,3 +23,21 @@ async def bulk_create_success_reasons( ) await session.execute(stmt) await session.commit() + + +async def get_messages_by_slice_id( + session: AsyncSession, + slice_id: UUID +) -> Success: + stmt = ( + select(Success) + .options( + joinedload(Success.user_relationship), + joinedload(Success.chat_relationship) + .selectinload(TgChat.message_relationship) + ) + .where(Success.slice_id == slice_id) + ) + return await session.scalar(stmt) + + diff --git a/telegram-application/src/core/workers/rmq_worker_handler.py b/telegram-application/src/core/workers/rmq_worker_handler.py index ad5059a..23db79e 100644 --- a/telegram-application/src/core/workers/rmq_worker_handler.py +++ b/telegram-application/src/core/workers/rmq_worker_handler.py @@ -4,6 +4,7 @@ from sqlalchemy.ext.asyncio import AsyncSession from faststream import Depends +from src.core.tg_service.service import notify_for_success from src.core.workers.schemas import ResponseFromGeminiSchema from src.core.database.connect import db_helper from src.core.rabbitmq.connect import success_gemini_subscriber @@ -20,5 +21,21 @@ async def create_success_record( session=session, ) + for success_message in message.success: + success_mod_with_relation = await workers_crud.get_messages_by_slice_id( + session=session, + slice_id=success_message.slice_id, + ) + + await notify_for_success( + messages=success_mod_with_relation.chat_relationship.message_relationship, + chat=success_mod_with_relation.chat_relationship, + user_model=success_mod_with_relation.user_relationship, + success_reason=success_mod_with_relation,, + ) + + + + diff --git a/telegram-application/src/core/workers/schemas.py b/telegram-application/src/core/workers/schemas.py index fc56e61..69ba69c 100644 --- a/telegram-application/src/core/workers/schemas.py +++ b/telegram-application/src/core/workers/schemas.py @@ -1,9 +1,9 @@ -from pydantic import BaseModel, PositiveInt, NegativeInt +from pydantic import BaseModel, PositiveInt, NegativeInt, UUID4 class SuccessChatFromAiSchema(BaseModel): user_id: PositiveInt | None - chat_id: NegativeInt | None + slice_id: UUID4 | None reason: str | None