feat: явно указаны зависимости к Protocol для перехода к имплементации

This commit is contained in:
Artem Tsyrulnikov
2025-12-23 10:11:31 +03:00
parent 23eb5ae13d
commit 45a5b59cc8
11 changed files with 32 additions and 36 deletions

View File

@@ -4,11 +4,8 @@ import uuid
import jwt
import pydantic
class JWTConfig(pydantic.BaseModel):
SECRET_KEY: str
ALGORITHM: str = 'HS256'
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 * 24 * 7 # 7 days
from shared.jwt_base import JWTBase
from src.usecase import JWTEncoder
class JWTPayload(pydantic.BaseModel):
@@ -17,10 +14,7 @@ class JWTPayload(pydantic.BaseModel):
username: str | None
class JWT:
def __init__(self, config: JWTConfig) -> None:
self.config = config
class JWT(JWTBase, JWTEncoder):
def encode_access_token(
self,
user_id: uuid.UUID,

View File

@@ -4,6 +4,8 @@ from typing import Any
import httpx
from pydantic import BaseModel
from src.usecase import Parser
log = logging.getLogger(__name__)
@@ -16,23 +18,12 @@ class FetchChannelResponse(BaseModel):
pts: int
class ParserClient:
class ParserClient(Parser):
def __init__(self, base_url: str, timeout: float = 5.0):
self.base_url = base_url.rstrip('/')
self.timeout = timeout
async def fetch_telegram_channel(self, username: str) -> FetchChannelResponse | None:
"""
Проверяет существование канала в Telegram и получает его метаданные.
Returns:
FetchChannelResponse если канал найден
None если канал не найден (404)
Raises:
httpx.HTTPError для других ошибок (таймаут, network error, 5xx)
"""
# Убираем @ если пользователь его указал
username = username.lstrip('@')
url = f'{self.base_url}/fetch-telegram-channel'

View File

@@ -8,11 +8,12 @@ from tortoise.transactions import in_transaction
from shared.datebase_base import DatabaseBase
from src import domain
from usecase import Database
log = logging.getLogger(__name__)
class Postgres(DatabaseBase):
class Postgres(DatabaseBase, Database):
def transaction(self) -> typing.AsyncContextManager[None]:
return in_transaction()

View File

@@ -3,11 +3,12 @@ import logging
from aiogram.types import BotCommand, InlineKeyboardButton, InlineKeyboardMarkup
from shared.telegram_base import TelegramBase
from usecase import TelegramBotWriter
log = logging.getLogger(__name__)
class TelegramBot(TelegramBase):
class TelegramBot(TelegramBase, TelegramBotWriter):
async def send_message(self, text: str, chat_id: int) -> None:
await self.bot.send_message(chat_id=chat_id, text=text)

View File

@@ -3,9 +3,9 @@ from pydantic_settings import BaseSettings, SettingsConfigDict
from shared.config_helper import load_settings
from shared.datebase_base import DatabaseConfig
from shared.jwt_base import JWTConfig
from shared.logger import LoggerConfig
from shared.telegram_base import TelegramConfig
from src.adapter.jwt import JWTConfig
class AppConfig(BaseModel):

View File

@@ -17,15 +17,12 @@ async def on_chat_join_request(event: ChatJoinRequest) -> None:
log.error('Failed to get user data from chat join request')
return
# Получаем invite_link, по которой пользователь отправил запрос
invite_link = event.invite_link
if not invite_link or not invite_link.invite_link:
log.debug('No invite_link in chat join request event')
return
usecase = deps.get_usecase()
await usecase.handle_subscription(
await deps.get_usecase().handle_subscription(
user_telegram_id=event.from_user.id,
username=event.from_user.username,
invite_link=invite_link.invite_link,

View File

@@ -30,17 +30,17 @@ async def on_chat_member_updated(event: ChatMemberUpdated) -> None:
is_now_not_member = new_status in ('left', 'kicked')
user_left = was_member and is_now_not_member
usecase = deps.get_usecase()
if user_joined:
invite_link = event.invite_link.invite_link if event.invite_link else None
if not invite_link:
log.debug('No invite_link in chat member update event')
return
await usecase.handle_subscription(
await deps.get_usecase().handle_subscription(
user_telegram_id=event.from_user.id, username=event.from_user.username, invite_link=invite_link
)
elif user_left:
await usecase.handle_unsubscription(user_telegram_id=event.from_user.id, channel_telegram_id=event.chat.id)
await deps.get_usecase().handle_unsubscription(
user_telegram_id=event.from_user.id, channel_telegram_id=event.chat.id
)

View File

@@ -44,7 +44,7 @@ usecase = Usecase(
database=postgres,
telegram_bot=telegram,
jwt_encoder=jwt_encoder,
parser_client=parser_client,
parser=parser_client,
)
deps.set_usecase(usecase)

View File

@@ -296,7 +296,7 @@ class FetchChannelResponse(typing.Protocol):
title: str
class ParserClient(typing.Protocol):
class Parser(typing.Protocol):
async def fetch_telegram_channel(self, username: str) -> FetchChannelResponse | None: ...
@@ -305,7 +305,7 @@ class Usecase:
database: Database
telegram_bot: TelegramBotWriter
jwt_encoder: JWTEncoder
parser_client: ParserClient
parser: Parser
async def ensure_workspace_access(self, workspace_id: UUID, user_id: UUID) -> domain.Workspace:
workspace = await self.database.get_workspace_for_user(workspace_id, user_id)

View File

@@ -30,7 +30,7 @@ async def attach_channel_to_purchase_plan(
channel = await self.database.get_channel(username=input.username)
if not channel:
parser_response = await self.parser_client.fetch_telegram_channel(input.username)
parser_response = await self.parser.fetch_telegram_channel(input.username)
if not parser_response:
raise domain.TelegramChannelNotFound(input.username)