тестово events события вместо telegram updates
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from typing import Any
|
||||
from typing import Annotated, Literal, Union
|
||||
|
||||
from fastapi.routing import APIRouter
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from src import deps, dto
|
||||
|
||||
@@ -58,210 +58,74 @@ async def attach_channel_to_workspace(input: AttachChannelToWorkspaceRequest) ->
|
||||
return await deps.get_usecase().attach_channel_to_workspace(input=input_data)
|
||||
|
||||
|
||||
@internal_router.post('/telegram/updates')
|
||||
async def handle_telegram_update(update: dict[str, Any]) -> None:
|
||||
"""Обрабатывает системные события от Golang бота (echotron формат)"""
|
||||
import json
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Логируем что пришло для отладки
|
||||
log.info('Received update from Golang bot, keys: %s', list(update.keys()))
|
||||
|
||||
# Обрабатываем ChatJoinRequest (запрос на вступление в канал)
|
||||
if 'chat_join_request' in update:
|
||||
log.info('ChatJoinRequest event: %s', json.dumps(update['chat_join_request'], ensure_ascii=False))
|
||||
await _handle_chat_join_request(update['chat_join_request'])
|
||||
return
|
||||
|
||||
# Обрабатываем ChatMember (пользователь вступил/вышел из канала)
|
||||
if 'chat_member' in update:
|
||||
event = update['chat_member']
|
||||
old_status = event.get('old_chat_member', {}).get('status')
|
||||
new_status = event.get('new_chat_member', {}).get('status')
|
||||
log.info('ChatMember event: status change %s -> %s', old_status, new_status)
|
||||
await _handle_chat_member_updated(event)
|
||||
return
|
||||
|
||||
# Обрабатываем MyChatMember (бот добавлен/удален из канала)
|
||||
if 'my_chat_member' in update:
|
||||
event = update['my_chat_member']
|
||||
old_status = event.get('old_chat_member', {}).get('status')
|
||||
new_status = event.get('new_chat_member', {}).get('status')
|
||||
log.info('MyChatMember event: bot status changed %s -> %s in chat', old_status, new_status)
|
||||
await _handle_my_chat_member(event)
|
||||
return
|
||||
|
||||
log.warning('Unknown update type from Golang bot: %s', list(update.keys()))
|
||||
class SubscriptionEventRequest(BaseModel):
|
||||
type: Literal['subscription']
|
||||
user_telegram_id: int
|
||||
invite_link: str
|
||||
username: str | None = None
|
||||
first_name: str | None = None
|
||||
last_name: str | None = None
|
||||
|
||||
|
||||
async def _handle_chat_join_request(event: dict[str, Any]) -> None:
|
||||
"""Обработка запроса на вступление в канал через invite link"""
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
chat = event.get('chat', {})
|
||||
if chat.get('type') not in ['channel', 'supergroup']:
|
||||
return
|
||||
|
||||
from_user = event.get('from')
|
||||
if not from_user or not from_user.get('id'):
|
||||
user_chat_id = event.get('user_chat_id')
|
||||
if not user_chat_id:
|
||||
log.error('No user or user_chat_id in chat_join_request event')
|
||||
return
|
||||
from_user = {'id': user_chat_id}
|
||||
|
||||
invite_link_obj = event.get('invite_link', {})
|
||||
invite_link = invite_link_obj.get('invite_link')
|
||||
if not invite_link:
|
||||
log.debug('No invite_link in chat_join_request event')
|
||||
return
|
||||
|
||||
await deps.get_usecase().handle_subscription(
|
||||
user_telegram_id=from_user['id'],
|
||||
username=from_user.get('username'),
|
||||
invite_link=invite_link,
|
||||
first_name=from_user.get('first_name'),
|
||||
last_name=from_user.get('last_name'),
|
||||
)
|
||||
class UnsubscriptionEventRequest(BaseModel):
|
||||
type: Literal['unsubscription']
|
||||
user_telegram_id: int
|
||||
channel_telegram_id: int
|
||||
|
||||
|
||||
async def _handle_chat_member_updated(event: dict[str, Any]) -> None:
|
||||
"""Обработка изменения статуса участника канала (вступление/выход)"""
|
||||
import logging
|
||||
class BotAddedEventRequest(dto.ConnectProjectInput):
|
||||
type: Literal['bot_added']
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
chat = event.get('chat', {})
|
||||
if chat.get('type') not in ['channel', 'supergroup']:
|
||||
return
|
||||
class BotRemovedEventRequest(dto.DisconnectProjectByTgIdInput):
|
||||
type: Literal['bot_removed']
|
||||
|
||||
member_user = event.get('new_chat_member', {}).get('user')
|
||||
if not member_user:
|
||||
log.error('No user in chat_member_updated event')
|
||||
return
|
||||
|
||||
old_member = event.get('old_chat_member', {})
|
||||
new_member = event.get('new_chat_member', {})
|
||||
class BotPermissionsEventRequest(dto.UpdateProjectPermissionsInput):
|
||||
type: Literal['bot_permissions']
|
||||
|
||||
old_status = old_member.get('status')
|
||||
new_status = new_member.get('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
|
||||
EventRequest = Annotated[
|
||||
Union[
|
||||
SubscriptionEventRequest,
|
||||
UnsubscriptionEventRequest,
|
||||
BotAddedEventRequest,
|
||||
BotRemovedEventRequest,
|
||||
BotPermissionsEventRequest,
|
||||
],
|
||||
Field(discriminator='type'),
|
||||
]
|
||||
|
||||
# Проверяем что пользователь покинул канал
|
||||
was_member = old_status in ['member', 'administrator', 'creator', 'restricted']
|
||||
is_now_not_member = new_status in ['left', 'kicked']
|
||||
user_left = was_member and is_now_not_member
|
||||
|
||||
if user_joined:
|
||||
invite_link_obj = event.get('invite_link', {})
|
||||
invite_link = invite_link_obj.get('invite_link') if invite_link_obj else None
|
||||
if not invite_link:
|
||||
log.debug('No invite_link in chat_member_updated event (user joined)')
|
||||
return
|
||||
|
||||
@internal_router.post('/events')
|
||||
async def handle_event(input: EventRequest) -> None:
|
||||
if isinstance(input, SubscriptionEventRequest):
|
||||
await deps.get_usecase().handle_subscription(
|
||||
user_telegram_id=member_user['id'],
|
||||
username=member_user.get('username'),
|
||||
invite_link=invite_link,
|
||||
first_name=member_user.get('first_name'),
|
||||
last_name=member_user.get('last_name'),
|
||||
user_telegram_id=input.user_telegram_id,
|
||||
username=input.username,
|
||||
invite_link=input.invite_link,
|
||||
first_name=input.first_name,
|
||||
last_name=input.last_name,
|
||||
)
|
||||
return
|
||||
|
||||
elif user_left:
|
||||
if isinstance(input, UnsubscriptionEventRequest):
|
||||
await deps.get_usecase().handle_unsubscription(
|
||||
user_telegram_id=member_user['id'],
|
||||
channel_telegram_id=chat['id'],
|
||||
user_telegram_id=input.user_telegram_id,
|
||||
channel_telegram_id=input.channel_telegram_id,
|
||||
)
|
||||
|
||||
|
||||
async def _handle_my_chat_member(event: dict[str, Any]) -> None:
|
||||
"""Обработка добавления/удаления бота из канала"""
|
||||
import logging
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
chat = event.get('chat', {})
|
||||
if chat.get('type') not in ['channel', 'supergroup']:
|
||||
return
|
||||
|
||||
from_user = event.get('from')
|
||||
if not from_user:
|
||||
log.error('No user in my_chat_member event')
|
||||
if isinstance(input, BotAddedEventRequest):
|
||||
data = dto.ConnectProjectInput(**input.dict(exclude={'type'}))
|
||||
await deps.get_usecase().tg_add_project(input=data)
|
||||
return
|
||||
|
||||
old_member = event.get('old_chat_member', {})
|
||||
new_member = event.get('new_chat_member', {})
|
||||
|
||||
old_status = old_member.get('status')
|
||||
new_status = new_member.get('status')
|
||||
|
||||
# Проверяем что бот был удален из канала
|
||||
was_member = old_status in ['administrator', 'member']
|
||||
is_now_not_member = new_status in ['left', 'kicked']
|
||||
bot_removed = was_member and is_now_not_member
|
||||
|
||||
if bot_removed:
|
||||
# Отключаем проект
|
||||
disconnect_input = dto.DisconnectProjectByTgIdInput(
|
||||
telegram_id=chat['id'],
|
||||
user_telegram_id=from_user['id'],
|
||||
)
|
||||
try:
|
||||
await deps.get_usecase().disconnect_project_by_tg_id(input=disconnect_input)
|
||||
log.info('Bot removed from channel %s by user %s', chat['id'], from_user['id'])
|
||||
except Exception as e:
|
||||
log.error('Failed to disconnect project: %s', e)
|
||||
if isinstance(input, BotRemovedEventRequest):
|
||||
data = dto.DisconnectProjectByTgIdInput(**input.dict(exclude={'type'}))
|
||||
await deps.get_usecase().disconnect_project_by_tg_id(input=data)
|
||||
return
|
||||
|
||||
# Проверяем изменение прав администратора
|
||||
permissions_changed = old_status == 'administrator' and new_status == 'administrator'
|
||||
|
||||
# Извлекаем права бота
|
||||
bot_permissions = dto.ChannelBotPermissions(
|
||||
is_admin=(new_status == 'administrator'),
|
||||
can_invite_users=new_member.get('can_invite_users', False),
|
||||
can_restrict_members=new_member.get('can_restrict_members', False),
|
||||
)
|
||||
|
||||
if permissions_changed:
|
||||
# Обновляем права бота
|
||||
permissions_input = dto.UpdateProjectPermissionsInput(
|
||||
telegram_id=chat['id'],
|
||||
permissions=bot_permissions,
|
||||
chat_title=chat.get('title') or f'Channel {chat["id"]}',
|
||||
user_telegram_id=from_user['id'],
|
||||
)
|
||||
try:
|
||||
await deps.get_usecase().update_project_permissions(input=permissions_input)
|
||||
log.info('Bot permissions updated in channel %s', chat['id'])
|
||||
except Exception as e:
|
||||
log.error('Failed to update permissions: %s', e)
|
||||
return
|
||||
|
||||
# Проверяем что бот был добавлен в канал
|
||||
was_not_member = old_status in ['left', 'kicked']
|
||||
is_now_member = new_status in ['administrator', 'member']
|
||||
bot_added = was_not_member and is_now_member
|
||||
|
||||
if bot_added:
|
||||
# Добавляем канал как проект
|
||||
connect_input = dto.ConnectProjectInput(
|
||||
telegram_id=chat['id'],
|
||||
title=chat.get('title') or f'Channel {chat["id"]}',
|
||||
username=chat.get('username'),
|
||||
user_telegram_id=from_user['id'],
|
||||
bot_permissions=bot_permissions,
|
||||
)
|
||||
try:
|
||||
await deps.get_usecase().tg_add_project(input=connect_input)
|
||||
log.info('Bot added to channel %s by user %s', chat['id'], from_user['id'])
|
||||
except Exception as e:
|
||||
log.error('Failed to add project: %s', e)
|
||||
if isinstance(input, BotPermissionsEventRequest):
|
||||
data = dto.UpdateProjectPermissionsInput(**input.dict(exclude={'type'}))
|
||||
await deps.get_usecase().update_project_permissions(input=data)
|
||||
|
||||
Reference in New Issue
Block a user