feat: sending and receiving messages with files, editing text messages

This commit is contained in:
2025-04-02 15:28:22 +04:00
parent 2cdccb33ca
commit 00522da68f
13 changed files with 361 additions and 70 deletions

View File

@@ -19,9 +19,11 @@ S3_API_KEY = os.environ.get('S3_API_KEY')
BILLING_API_KEY = os.environ.get('BILLING_API_KEY') BILLING_API_KEY = os.environ.get('BILLING_API_KEY')
CHAT_TELEGRAM_BOT_TOKEN = os.environ.get('CHAT_TELEGRAM_BOT_TOKEN')
CHAT_CONNECTOR_API_KEY = os.environ.get('CHAT_CONNECTOR_API_KEY') CHAT_CONNECTOR_API_KEY = os.environ.get('CHAT_CONNECTOR_API_KEY')
CHATS_SYNC_URL = os.environ.get('CHATS_SYNC_URL') CHATS_SYNC_URL = os.environ.get('CHATS_SYNC_URL')
# Kafka
KAFKA_URL = os.environ.get('KAFKA_URL') KAFKA_URL = os.environ.get('KAFKA_URL')
KAFKA_CONSUMER_TOPIC = os.environ.get('KAFKA_CONSUMER_TOPIC') KAFKA_CONSUMER_TOPIC = os.environ.get('KAFKA_CONSUMER_TOPIC')
KAFKA_PRODUCER_TOPIC = os.environ.get('KAFKA_PRODUCER_TOPIC') KAFKA_PRODUCER_TOPIC = os.environ.get('KAFKA_PRODUCER_TOPIC')

View File

@@ -1,5 +1,6 @@
import aiohttp import aiohttp
import jwt import jwt
from fastapi import UploadFile
from backend.config import CHATS_SYNC_URL, CHAT_CONNECTOR_API_KEY from backend.config import CHATS_SYNC_URL, CHAT_CONNECTOR_API_KEY
from external.chat.schemas import * from external.chat.schemas import *
@@ -22,7 +23,6 @@ class ChatClient:
async def _method(self, http_method, method, **kwargs): async def _method(self, http_method, method, **kwargs):
async with aiohttp.ClientSession(headers=self.headers) as session: async with aiohttp.ClientSession(headers=self.headers) as session:
async with session.request(http_method, self.base_url + method, **kwargs) as response: async with session.request(http_method, self.base_url + method, **kwargs) as response:
print(response)
return await response.json() return await response.json()
async def create_group(self, request: ExternalCreateGroupRequest) -> ExternalCreateGroupResponse: async def create_group(self, request: ExternalCreateGroupRequest) -> ExternalCreateGroupResponse:
@@ -34,3 +34,22 @@ class ChatClient:
json_data = request.model_dump() json_data = request.model_dump()
response = await self._method('POST', self.groups_endpoint + '/topic/create', json=json_data) response = await self._method('POST', self.groups_endpoint + '/topic/create', json=json_data)
return ExternalCreateTopicResponse.model_validate(response) return ExternalCreateTopicResponse.model_validate(response)
async def send_messages_with_files(
self,
tg_group_id: str,
tg_topic_id: int,
caption: str,
files: list[UploadFile],
) -> ExternalSendMessagesWithFilesResponse:
query_params = f'?tg_group_id={tg_group_id}&tg_topic_id={tg_topic_id}&caption={caption}'
data = aiohttp.FormData(default_to_multipart=True)
for file in files:
content = await file.read()
data.add_field('files', content, filename=file.filename, content_type=file.content_type)
response = await self._method('POST', self.chats_sync_endpoint + '/send' + query_params, data=data)
return ExternalSendMessagesWithFilesResponse.model_validate(response)

View File

@@ -1,9 +1,25 @@
from typing import Optional from typing import Optional
from uuid import UUID from uuid import UUID
from schemas.base import BaseSchema from schemas.base import BaseSchema, OkMessageSchema
# region Entities
class ExternalSendFileSchema(BaseSchema):
buffer: bytes
file_name: str
file_size: int
class ExternalMessageFileSchema(BaseSchema):
file_path: str
type: str
file_name: str
file_size: int
# endregion
# region Requests # region Requests
class ExternalCreateGroupRequest(BaseSchema): class ExternalCreateGroupRequest(BaseSchema):
@@ -29,4 +45,8 @@ class ExternalCreateGroupResponse(BaseSchema):
class ExternalCreateTopicResponse(BaseSchema): class ExternalCreateTopicResponse(BaseSchema):
tg_topic_id: int tg_topic_id: int
class ExternalSendMessagesWithFilesResponse(OkMessageSchema):
files: list[ExternalMessageFileSchema]
# endregion # endregion

View File

@@ -24,6 +24,7 @@ async def consume_messages():
try: try:
async for message in consumer: async for message in consumer:
print("consume")
await consumer_service.consume_message(message) await consumer_service.consume_message(message)
finally: finally:
await consumer.stop() await consumer.stop()

View File

@@ -1,3 +1,5 @@
from typing import Optional
from schemas.base import OkMessageSchema, BaseSchema from schemas.base import OkMessageSchema, BaseSchema
@@ -10,11 +12,19 @@ class TelegramUserSchema(BaseSchema):
username: str username: str
class MessageFileSchema(BaseSchema):
file_path: str
type: str
file_name: str
file_size: int
class MessageFromTelegramSchema(BaseSchema): class MessageFromTelegramSchema(BaseSchema):
group_id: str group_id: str
tg_topic_id: int tg_topic_id: int
text: str text: Optional[str]
sender: TelegramUserSchema sender: TelegramUserSchema
file: Optional[MessageFileSchema]
# endregion # endregion
@@ -41,4 +51,9 @@ class SendMessageToConnectorResponse(BaseConnectorResponse):
class DeleteMessageResponse(BaseConnectorResponse): class DeleteMessageResponse(BaseConnectorResponse):
message_id: int message_id: int
class EditMessageResponse(BaseConnectorResponse):
message_id: int
text: str
# endregion # endregion

View File

@@ -11,8 +11,11 @@ class BaseMessageSchema(BaseSchema):
group_id: str group_id: str
class MessageSchema(BaseMessageSchema): class EditMessageSchema(BaseMessageSchema):
text: str text: str
class MessageSchema(EditMessageSchema):
topic_id: int topic_id: int
@@ -33,4 +36,8 @@ class SendMessageToConnectorRequest(BaseConnectorRequest):
class SendMessageDeletingToConnectorRequest(BaseConnectorRequest): class SendMessageDeletingToConnectorRequest(BaseConnectorRequest):
pass pass
class SendMessageEditingToConnectorRequest(BaseConnectorRequest):
message: EditMessageSchema
# endregion # endregion

View File

@@ -1,6 +1,5 @@
import pickle import pickle
from datetime import datetime from datetime import datetime
from typing import Optional
from uuid import UUID from uuid import UUID
from aiokafka import ConsumerRecord from aiokafka import ConsumerRecord
@@ -8,13 +7,14 @@ from sqlalchemy import select
from external.kafka.enums import KafkaMessageType from external.kafka.enums import KafkaMessageType
from external.kafka.schemas.consumer import * from external.kafka.schemas.consumer import *
from models import Message, MessageStatus, TgUser, Chat, TgGroup from models import Message, MessageStatus, TgUser, Chat, TgGroup, MessageFile
from services.base import BaseService from services.base import BaseService
class ConsumerService(BaseService): class ConsumerService(BaseService):
async def consume_message(self, message: ConsumerRecord): async def consume_message(self, message: ConsumerRecord):
value = pickle.loads(message.value) value = pickle.loads(message.value)
print("Consumer: received message: ", value)
try: try:
if 'ok' in value: if 'ok' in value:
@@ -36,7 +36,8 @@ class ConsumerService(BaseService):
response = SendMessageToConnectorResponse.model_validate(value) response = SendMessageToConnectorResponse.model_validate(value)
await self._process_connector_send_response(response) await self._process_connector_send_response(response)
case KafkaMessageType.EDIT: case KafkaMessageType.EDIT:
pass response = EditMessageResponse.model_validate(value)
await self._process_connector_edit_response(response)
case KafkaMessageType.DELETE: case KafkaMessageType.DELETE:
response = DeleteMessageResponse.model_validate(value) response = DeleteMessageResponse.model_validate(value)
await self._process_connector_delete_response(response) await self._process_connector_delete_response(response)
@@ -59,6 +60,16 @@ class ConsumerService(BaseService):
message = await self.session.get(Message, response.message_id) message = await self.session.get(Message, response.message_id)
message.is_deleted = True message.is_deleted = True
await self.session.commit()
async def _process_connector_edit_response(self, response: EditMessageResponse):
if not response.ok:
return
message = await self.session.get(Message, response.message_id)
message.text = response.text
message.is_edited = True
await self.session.commit()
async def _get_chat(self, group_id: str, tg_topic_id: int) -> Optional[Chat]: async def _get_chat(self, group_id: str, tg_topic_id: int) -> Optional[Chat]:
stmt = ( stmt = (
@@ -81,12 +92,17 @@ class ConsumerService(BaseService):
if not chat: if not chat:
return return
file = None
if request.message.file:
file = MessageFile(**request.message.file.model_dump())
message = Message( message = Message(
text=request.message.text, text=request.message.text if request.message.text else "",
created_at=datetime.now(), created_at=datetime.now(),
tg_sender_id=tg_sender.id, tg_sender_id=tg_sender.id,
chat_id=chat.id, chat_id=chat.id,
status=MessageStatus.success, status=MessageStatus.success,
file=file,
) )
self.session.add(message) self.session.add(message)
await self.session.commit() await self.session.commit()

View File

@@ -6,12 +6,21 @@ from aiohttp import ClientConnectorError
from backend.config import KAFKA_PRODUCER_TOPIC, CHAT_CONNECTOR_API_KEY from backend.config import KAFKA_PRODUCER_TOPIC, CHAT_CONNECTOR_API_KEY
from external.kafka import producer from external.kafka import producer
from external.kafka.enums import KafkaMessageType from external.kafka.enums import KafkaMessageType
from external.kafka.schemas.producer import MessageSchema, SendMessageToConnectorRequest, \ from external.kafka.schemas.producer import *
SendMessageDeletingToConnectorRequest, BaseMessageSchema
from services.base import BaseService from services.base import BaseService
class ProducerService(BaseService): class ProducerService(BaseService):
@staticmethod
async def _send_message(request: BaseConnectorRequest):
try:
await producer.send(KAFKA_PRODUCER_TOPIC, value=pickle.dumps(request.model_dump()))
except ClientConnectorError:
return False, 'Ошибка подключения к коннектору'
except Exception as e:
return False, str(e)
return True, 'Сообщение отправлено'
@staticmethod @staticmethod
async def send_message_to_connector( async def send_message_to_connector(
message_text: str, message_text: str,
@@ -19,42 +28,50 @@ class ProducerService(BaseService):
topic_id: int, topic_id: int,
message_id: int, message_id: int,
) -> tuple[bool, str]: ) -> tuple[bool, str]:
try: request = SendMessageToConnectorRequest(
request = SendMessageToConnectorRequest( message_type=KafkaMessageType.SEND,
message=MessageSchema( app_auth_key=CHAT_CONNECTOR_API_KEY,
message_id=message_id, message=MessageSchema(
text=message_text, message_id=message_id,
group_id=str(group_id), text=message_text,
topic_id=topic_id, group_id=str(group_id),
), topic_id=topic_id,
message_type=KafkaMessageType.SEND, ),
app_auth_key=CHAT_CONNECTOR_API_KEY, )
) return await ProducerService._send_message(request)
await producer.send(KAFKA_PRODUCER_TOPIC, value=pickle.dumps(request.model_dump()))
except ClientConnectorError:
return False, 'Ошибка подключения к коннектору'
except Exception as e:
return False, str(e)
return True, 'Сообщение отправлено'
@staticmethod @staticmethod
async def send_message_deleting_to_connector(message_id: int, tg_message_id: int, group_id: UUID) -> tuple[bool, str]: async def send_message_deleting_to_connector(
try: message_id: int,
request = SendMessageDeletingToConnectorRequest( tg_message_id: int,
message_type=KafkaMessageType.DELETE, group_id: UUID,
app_auth_key=CHAT_CONNECTOR_API_KEY, ) -> tuple[bool, str]:
message=BaseMessageSchema( request = SendMessageDeletingToConnectorRequest(
message_id=message_id, message_type=KafkaMessageType.DELETE,
tg_message_id=tg_message_id, app_auth_key=CHAT_CONNECTOR_API_KEY,
group_id=str(group_id), message=BaseMessageSchema(
), message_id=message_id,
tg_message_id=tg_message_id,
group_id=str(group_id),
),
)
return await ProducerService._send_message(request)
@staticmethod
async def send_message_editing_to_connector(
message_id: int,
tg_message_id: int,
group_id: UUID,
text: str,
) -> tuple[bool, str]:
request = SendMessageEditingToConnectorRequest(
message_type=KafkaMessageType.EDIT,
app_auth_key=CHAT_CONNECTOR_API_KEY,
message=EditMessageSchema(
message_id=message_id,
tg_message_id=tg_message_id,
group_id=str(group_id),
text=text,
) )
await producer.send(KAFKA_PRODUCER_TOPIC, value=pickle.dumps(request.model_dump())) )
except ClientConnectorError: return await ProducerService._send_message(request)
return False, 'Ошибка подключения к коннектору'
except Exception as e:
return False, str(e)
return True, 'Сообщение отправлено'

View File

@@ -109,6 +109,23 @@ class Chat(BaseModel):
) )
class MessageFile(BaseModel):
__tablename__ = 'message_files'
id: Mapped[int] = mapped_column(primary_key=True)
file_path: Mapped[str] = mapped_column(nullable=False)
type: Mapped[Optional[str]] = mapped_column(nullable=True)
file_name: Mapped[str] = mapped_column(nullable=False)
file_size: Mapped[int] = mapped_column(BigInteger(), nullable=True, comment='Размер файла в байтах')
message_id: Mapped[int] = mapped_column(ForeignKey('messages.id'))
message: Mapped['Message'] = relationship(
'Message',
lazy='noload',
back_populates='file',
)
class MessageStatus(enum.Enum): class MessageStatus(enum.Enum):
sending = 'SENDING' sending = 'SENDING'
success = 'SUCCESS' success = 'SUCCESS'
@@ -125,6 +142,7 @@ class Message(BaseModel):
created_at: Mapped[datetime] = mapped_column(nullable=False) created_at: Mapped[datetime] = mapped_column(nullable=False)
status: Mapped[MessageStatus] = mapped_column(Enum(MessageStatus), nullable=False) status: Mapped[MessageStatus] = mapped_column(Enum(MessageStatus), nullable=False)
is_deleted: Mapped[bool] = mapped_column(default=False, server_default='0', nullable=False) is_deleted: Mapped[bool] = mapped_column(default=False, server_default='0', nullable=False)
is_edited: Mapped[bool] = mapped_column(default=False, server_default='0', nullable=False)
tg_sender_id: Mapped[Optional[int]] = mapped_column( tg_sender_id: Mapped[Optional[int]] = mapped_column(
ForeignKey('tg_users.id'), ForeignKey('tg_users.id'),
@@ -152,3 +170,9 @@ class Message(BaseModel):
lazy='noload', lazy='noload',
back_populates='messages', back_populates='messages',
) )
file: Mapped[Optional[MessageFile]] = relationship(
'MessageFile',
back_populates='message',
lazy='selectin',
)

View File

@@ -25,6 +25,7 @@ aiokafka
python-dotenv python-dotenv
aiohttp aiohttp
aiohttp[speedups] aiohttp[speedups]
requests
openpyxl==3.0.10 openpyxl==3.0.10
lexorank-py lexorank-py
celery[redis] celery[redis]

View File

@@ -1,6 +1,6 @@
from typing import Annotated from typing import Annotated
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends, UploadFile
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from backend.session import get_session from backend.session import get_session
@@ -15,16 +15,42 @@ chat_router = APIRouter(
@chat_router.post( @chat_router.post(
'/message', '/message',
operation_id='send_message', operation_id='send_text_message',
response_model=SendMessageResponse, response_model=SendTextMessageResponse,
) )
async def send_message( async def send_text_message(
session: Annotated[AsyncSession, Depends(get_session)], session: Annotated[AsyncSession, Depends(get_session)],
request: SendMessageRequest, request: SendTextMessageRequest,
): ):
return await ChatService(session).send_message(request) return await ChatService(session).send_message(request)
@chat_router.post(
'/message/repeat',
operation_id='repeat_sending_text_message',
response_model=RepeatSendingTextMessageResponse,
)
async def repeat_sending_text_message(
session: Annotated[AsyncSession, Depends(get_session)],
request: RepeatSendingTextMessageRequest,
):
return await ChatService(session).repeat_sending_message(request)
@chat_router.post(
'/message/files',
operation_id='send_messages_with_files',
response_model=LoadMessagesResponse,
)
async def send_messages_with_files(
session: Annotated[AsyncSession, Depends(get_session)],
files: list[UploadFile],
chat_id: int,
caption: str,
):
return await ChatService(session).send_messages_with_files(files, chat_id, caption)
@chat_router.delete( @chat_router.delete(
'/message/{message_id}', '/message/{message_id}',
operation_id='delete_message', operation_id='delete_message',
@@ -37,6 +63,18 @@ async def delete_message(
return await ChatService(session).delete_message(message_id) return await ChatService(session).delete_message(message_id)
@chat_router.patch(
'/message',
operation_id='edit_message',
response_model=EditMessageResponse,
)
async def edit_message(
session: Annotated[AsyncSession, Depends(get_session)],
request: EditMessageRequest,
):
return await ChatService(session).edit_message(request)
@chat_router.post( @chat_router.post(
'/', '/',
operation_id='get_chat', operation_id='get_chat',
@@ -71,3 +109,13 @@ async def get_messages(
request: GetMessagesRequest, request: GetMessagesRequest,
): ):
return await ChatService(session).get_messages(request) return await ChatService(session).get_messages(request)
@chat_router.get(
'/tg-file/{file_id}',
)
async def get_tg_file(
session: Annotated[AsyncSession, Depends(get_session)],
file_id: int,
):
return await ChatService(session).get_tg_file(file_id)

View File

@@ -1,5 +1,5 @@
from datetime import datetime from datetime import datetime
from typing import Optional from typing import Optional, List
from schemas.base import BaseSchema, OkMessageSchema from schemas.base import BaseSchema, OkMessageSchema
@@ -13,10 +13,12 @@ class TgUserSchema(BaseSchema):
username: str username: str
class MessageFromTgSchema(BaseSchema): class MessageFileSchema(BaseSchema):
chat_id: int id: int
text: str file_path: str
sender: TgUserSchema type: str
file_name: str
file_size: int
class BaseMessageSchema(BaseSchema): class BaseMessageSchema(BaseSchema):
@@ -24,11 +26,21 @@ class BaseMessageSchema(BaseSchema):
chat_id: int chat_id: int
class MessageSchema(BaseMessageSchema): class EditMessageSchema(BaseMessageSchema):
id: int id: int
class MessageSchema(EditMessageSchema):
created_at: datetime created_at: datetime
tg_sender: Optional[TgUserSchema] tg_sender: Optional[TgUserSchema]
status: str status: str
is_edited: bool
file: Optional[MessageFileSchema] = None
class RepeatSendingMessageSchema(BaseMessageSchema):
text: str
id: int
class TgGroupSchema(BaseSchema): class TgGroupSchema(BaseSchema):
@@ -46,10 +58,18 @@ class ChatSchema(BaseSchema):
# region Requests # region Requests
class SendMessageRequest(BaseSchema): class SendTextMessageRequest(BaseSchema):
message: BaseMessageSchema message: BaseMessageSchema
class RepeatSendingTextMessageRequest(BaseSchema):
message: RepeatSendingMessageSchema
class EditMessageRequest(BaseSchema):
message: EditMessageSchema
class GetChatRequest(BaseSchema): class GetChatRequest(BaseSchema):
client_id: int client_id: int
card_id: Optional[int] card_id: Optional[int]
@@ -69,7 +89,15 @@ class GetMessagesRequest(BaseSchema):
# region Responses # region Responses
class SendMessageResponse(OkMessageSchema): class SendTextMessageResponse(OkMessageSchema):
pass
class RepeatSendingTextMessageResponse(OkMessageSchema):
pass
class LoadMessagesResponse(OkMessageSchema):
pass pass
@@ -77,6 +105,10 @@ class DeleteMessageResponse(OkMessageSchema):
pass pass
class EditMessageResponse(OkMessageSchema):
pass
class GetChatResponse(BaseSchema): class GetChatResponse(BaseSchema):
chat: Optional[ChatSchema] chat: Optional[ChatSchema]

View File

@@ -1,14 +1,15 @@
from uuid import UUID import requests
from aiohttp import ClientConnectorError from aiohttp import ClientConnectorError
from fastapi import HTTPException, UploadFile
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
from starlette.responses import StreamingResponse
from backend.config import CHAT_CONNECTOR_API_KEY from backend.config import CHAT_CONNECTOR_API_KEY, CHAT_TELEGRAM_BOT_TOKEN
from external.chat.chat_client import ChatClient from external.chat.chat_client import ChatClient
from external.chat.schemas import ExternalCreateGroupRequest, ExternalCreateTopicRequest from external.chat.schemas import *
from external.kafka.services.producer_service import ProducerService from external.kafka.services.producer_service import ProducerService
from models import Message, Chat, MessageStatus, TgGroup, Client, Card from models import Message, Chat, MessageStatus, TgGroup, Client, Card, MessageFile
from schemas.chat import * from schemas.chat import *
from services.base import BaseService from services.base import BaseService
@@ -161,10 +162,10 @@ class ChatService(BaseService):
messages = (await self.session.scalars(stmt)).all() messages = (await self.session.scalars(stmt)).all()
return GetMessagesResponse(messages=messages) return GetMessagesResponse(messages=messages)
async def send_message(self, request: SendMessageRequest) -> SendMessageResponse: async def send_message(self, request: SendTextMessageRequest) -> SendTextMessageResponse:
chat: Optional[Chat] = await self.session.get(Chat, request.message.chat_id) chat: Optional[Chat] = await self.session.get(Chat, request.message.chat_id)
if not chat: if not chat:
return SendMessageResponse(ok=False, message=f'Чат с ID: {request.message.chat_id} не найден') return SendTextMessageResponse(ok=False, message=f'Чат с ID: {request.message.chat_id} не найден')
message = Message( message = Message(
text=request.message.text, text=request.message.text,
@@ -182,7 +183,67 @@ class ChatService(BaseService):
message.id, message.id,
) )
return SendMessageResponse(ok=ok, message=message) return SendTextMessageResponse(ok=ok, message=message)
async def repeat_sending_message(
self,
request: RepeatSendingTextMessageRequest,
) -> RepeatSendingTextMessageResponse:
message: Optional[Message] = await self._get_message_by_id(request.message.id)
if not message:
return RepeatSendingTextMessageResponse(ok=False, message=f'Сообщение с ID: {request.message.id} не найдено')
ok, msg = await ProducerService.send_message_to_connector(
request.message.text,
message.chat.tg_group_id,
message.chat.tg_topic_id,
message.id,
)
if ok:
message.status = MessageStatus.sending
await self.session.commit()
return RepeatSendingTextMessageResponse(ok=ok, message=msg)
async def send_messages_with_files(
self,
files: list[UploadFile],
chat_id: int,
caption: str,
) -> LoadMessagesResponse:
chat: Optional[Chat] = await self.session.get(Chat, chat_id)
if not chat:
return SendTextMessageResponse(ok=False, message=f'Чат с ID: {chat_id} не найден')
chat_client = ChatClient(api_key=CHAT_CONNECTOR_API_KEY)
response = await chat_client.send_messages_with_files(
str(chat.tg_group_id),
chat.tg_topic_id,
caption,
files
)
last_message = None
for file_schema in response.files:
file = MessageFile(**file_schema.model_dump())
self.session.add(file)
message = Message(
text='',
created_at=datetime.now(),
chat_id=chat_id,
status=MessageStatus.success,
file=file,
)
last_message = message
self.session.add(message)
if last_message:
last_message.text = caption
await self.session.commit()
return LoadMessagesResponse(ok=response.ok, message=response.message)
async def _get_message_by_id(self, message_id: int) -> Optional[Message]: async def _get_message_by_id(self, message_id: int) -> Optional[Message]:
stmt = ( stmt = (
@@ -203,7 +264,35 @@ class ChatService(BaseService):
message.chat.tg_group_id, message.chat.tg_group_id,
) )
message.is_deleted = True
await self.session.commit()
return DeleteMessageResponse(ok=ok, message=msg) return DeleteMessageResponse(ok=ok, message=msg)
async def edit_message(self, request: EditMessageRequest) -> EditMessageResponse:
message: Optional[Message] = await self._get_message_by_id(request.message.id)
if not message:
return EditMessageResponse(ok=False, message=f'Сообщение с ID: {request.message.id} не найдено')
ok, msg = await ProducerService.send_message_editing_to_connector(
message.id,
message.tg_message_id,
message.chat.tg_group_id,
request.message.text,
)
return EditMessageResponse(ok=ok, message=msg)
async def get_tg_file(self, file_id: int) -> StreamingResponse:
file: Optional[MessageFile] = await self.session.get(MessageFile, file_id)
if not file:
raise HTTPException(status_code=404, detail=f'Файл с ID {file_id} не найден')
url: str = f'https://api.telegram.org/file/bot{CHAT_TELEGRAM_BOT_TOKEN}/{file.file_path}'
print(f'URL = {url}')
response = requests.get(url, stream=True)
if response.status_code != 200:
raise HTTPException(status_code=response.status_code, detail="Error fetching file")
content_type = response.headers.get("Content-Type", "application/octet-stream")
return StreamingResponse(response.iter_content(chunk_size=8192), media_type=content_type)