feat: subs and views
This commit is contained in:
@@ -5,6 +5,7 @@ from src.controller.http.creatives import creatives_router
|
||||
from src.controller.http.external_channels import external_channels_router
|
||||
from src.controller.http.purchases import purchases_router
|
||||
from src.controller.http.target_channels import target_channels_router
|
||||
from src.controller.http.views import views_router
|
||||
|
||||
api_router = APIRouter()
|
||||
|
||||
@@ -17,5 +18,6 @@ api_v1_router.include_router(target_channels_router)
|
||||
api_v1_router.include_router(external_channels_router)
|
||||
api_v1_router.include_router(creatives_router)
|
||||
api_v1_router.include_router(purchases_router)
|
||||
api_v1_router.include_router(views_router)
|
||||
|
||||
api_router.include_router(api_v1_router)
|
||||
|
||||
59
src/controller/http/views.py
Normal file
59
src/controller/http/views.py
Normal file
@@ -0,0 +1,59 @@
|
||||
import datetime
|
||||
import uuid
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends
|
||||
from fastapi.routing import APIRouter
|
||||
|
||||
from src import dependencies, dto
|
||||
from src.adapter.jwt import JWTPayload
|
||||
|
||||
views_router = APIRouter(prefix='/purchases/{purchase_id}/views', tags=['views'])
|
||||
|
||||
|
||||
@views_router.get('/history')
|
||||
async def get_views_history(
|
||||
purchase_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(dependencies.get_current_user)],
|
||||
from_date: datetime.datetime | None = None,
|
||||
to_date: datetime.datetime | None = None,
|
||||
) -> dto.GetViewsHistoryOutput:
|
||||
"""Получить историю просмотров для закупа."""
|
||||
input_data = dto.GetViewsHistoryInput(
|
||||
purchase_id=purchase_id,
|
||||
user_id=current_user.user_id,
|
||||
from_date=from_date,
|
||||
to_date=to_date,
|
||||
)
|
||||
|
||||
return await dependencies.get_usecase().get_views_history(input=input_data)
|
||||
|
||||
|
||||
@views_router.post('/fetch')
|
||||
async def fetch_views(
|
||||
purchase_id: uuid.UUID,
|
||||
current_user: Annotated[JWTPayload, Depends(dependencies.get_current_user)],
|
||||
) -> dto.FetchViewsOutput:
|
||||
"""Получить актуальные просмотры через Telegram API."""
|
||||
input_data = dto.FetchViewsInput(
|
||||
purchase_id=purchase_id,
|
||||
user_id=current_user.user_id,
|
||||
)
|
||||
|
||||
return await dependencies.get_usecase().fetch_views(input=input_data)
|
||||
|
||||
|
||||
@views_router.post('/manual')
|
||||
async def update_views_manually(
|
||||
purchase_id: uuid.UUID,
|
||||
views_count: int,
|
||||
current_user: Annotated[JWTPayload, Depends(dependencies.get_current_user)],
|
||||
) -> dto.PurchaseOutput:
|
||||
"""Обновить просмотры вручную."""
|
||||
input_data = dto.UpdateViewsManuallyInput(
|
||||
purchase_id=purchase_id,
|
||||
user_id=current_user.user_id,
|
||||
views_count=views_count,
|
||||
)
|
||||
|
||||
return await dependencies.get_usecase().update_views_manually(input=input_data)
|
||||
@@ -3,4 +3,4 @@ from aiogram import Router
|
||||
telegram_callback_router = Router()
|
||||
|
||||
# Import handlers to register them
|
||||
from . import my_chat_member, start_with_login # noqa: E402, F401
|
||||
from . import chat_join_request, chat_member_updated, my_chat_member, start_with_login # noqa: E402, F401
|
||||
|
||||
37
src/controller/telegram_callback/chat_join_request.py
Normal file
37
src/controller/telegram_callback/chat_join_request.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import logging
|
||||
|
||||
from aiogram.types import ChatJoinRequest
|
||||
|
||||
from src import dependencies
|
||||
from src.controller.telegram_callback import telegram_callback_router
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@telegram_callback_router.chat_join_request()
|
||||
async def on_chat_join_request(event: ChatJoinRequest) -> None:
|
||||
"""
|
||||
Обрабатывает запросы на вступление в канал через приватную invite_link с одобрением.
|
||||
|
||||
Событие приходит когда пользователь отправляет запрос на вступление в канал.
|
||||
"""
|
||||
if event.chat.type not in {'channel', 'supergroup'}:
|
||||
return
|
||||
|
||||
if not event.from_user:
|
||||
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 = dependencies.get_usecase()
|
||||
|
||||
await usecase.handle_subscription(
|
||||
user_telegram_id=event.from_user.id,
|
||||
username=event.from_user.username,
|
||||
invite_link=invite_link.invite_link,
|
||||
)
|
||||
48
src/controller/telegram_callback/chat_member_updated.py
Normal file
48
src/controller/telegram_callback/chat_member_updated.py
Normal file
@@ -0,0 +1,48 @@
|
||||
import logging
|
||||
|
||||
from aiogram.types import ChatMemberUpdated
|
||||
|
||||
from src import dependencies
|
||||
from src.controller.telegram_callback import telegram_callback_router
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@telegram_callback_router.chat_member()
|
||||
async def on_chat_member_updated(event: ChatMemberUpdated) -> None:
|
||||
"""
|
||||
Обрабатывает события подписки пользователей в канал через открытую invite_link.
|
||||
|
||||
Событие приходит когда пользователь вступает в канал напрямую (без запроса на вступление).
|
||||
"""
|
||||
if event.chat.type not in {'channel', 'supergroup'}:
|
||||
return
|
||||
|
||||
if not event.from_user:
|
||||
log.error('Failed to get user data from chat member update')
|
||||
return
|
||||
|
||||
old_status = event.old_chat_member.status
|
||||
new_status = event.new_chat_member.status
|
||||
|
||||
# Проверяем что пользователь присоединился к каналу
|
||||
was_not_member = old_status in ('left', 'kicked')
|
||||
is_now_member = new_status in ('member', 'administrator', 'creator')
|
||||
user_joined = was_not_member and is_now_member
|
||||
|
||||
if not user_joined:
|
||||
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 member update event')
|
||||
return
|
||||
|
||||
usecase = dependencies.get_usecase()
|
||||
|
||||
await usecase.handle_subscription(
|
||||
user_telegram_id=event.from_user.id,
|
||||
username=event.from_user.username,
|
||||
invite_link=invite_link.invite_link,
|
||||
)
|
||||
Reference in New Issue
Block a user