feat: переименование доменной области
This commit is contained in:
26
src/usecase/project/disconnect_project_by_tg_id.py
Normal file
26
src/usecase/project/disconnect_project_by_tg_id.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from src import domain, dto
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .. import Usecase
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def disconnect_project_by_tg_id(self: 'Usecase', input: dto.DisconnectProjectByTgIdInput) -> None:
|
||||
user = await self.database.get_user(telegram_id=input.user_telegram_id)
|
||||
if not user:
|
||||
log.warning(f'User with telegram_id {input.user_telegram_id} not found when disconnecting channel')
|
||||
return
|
||||
|
||||
project = await self.database.get_project_for_user_by_telegram(user.id, input.telegram_id)
|
||||
if not project:
|
||||
log.warning(f'Project channel {input.telegram_id} not found')
|
||||
raise domain.ProjectNotFound()
|
||||
|
||||
project.status = domain.ProjectStatus.ARCHIVED
|
||||
await self.database.update_project(project)
|
||||
|
||||
log.info('Project %s archived for channel %s', project.id, input.telegram_id)
|
||||
27
src/usecase/project/get_workspace_projects.py
Normal file
27
src/usecase/project/get_workspace_projects.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from src import dto
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .. import Usecase
|
||||
|
||||
|
||||
async def get_workspace_projects(
|
||||
self: 'Usecase', input: dto.GetWorkspaceProjectsInput
|
||||
) -> dto.GetWorkspaceProjectsOutput:
|
||||
await self.ensure_workspace_access(input.workspace_id, input.user_id)
|
||||
|
||||
projects = await self.database.get_workspace_projects(input.workspace_id)
|
||||
|
||||
return dto.GetWorkspaceProjectsOutput(
|
||||
projects=[
|
||||
dto.ProjectOutput(
|
||||
id=project.id,
|
||||
telegram_id=project.channel.telegram_id,
|
||||
title=project.channel.title,
|
||||
username=project.channel.username,
|
||||
status=project.status,
|
||||
)
|
||||
for project in projects
|
||||
]
|
||||
)
|
||||
157
src/usecase/project/tg_add_project_1.py
Normal file
157
src/usecase/project/tg_add_project_1.py
Normal file
@@ -0,0 +1,157 @@
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from aiogram.types import InlineKeyboardButton
|
||||
|
||||
from src import domain, dto
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .. import Usecase
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def tg_add_project_1(self: 'Usecase', input: dto.ConnectProjectInput) -> dto.ProjectOutput | None:
|
||||
permissions = input.bot_permissions
|
||||
|
||||
if not permissions.is_admin:
|
||||
log.warning(f'Bot is not admin in channel {input.telegram_id}. Attempted by user {input.user_telegram_id}')
|
||||
await self.telegram_bot.send_message(
|
||||
f'⚠️ Бот был добавлен в канал "{input.title}", но не является админом.\n\n'
|
||||
'Пожалуйста, сделайте бота администратором канала.',
|
||||
input.user_telegram_id,
|
||||
)
|
||||
raise domain.ChannelNoAdminRights()
|
||||
|
||||
missing_permissions = []
|
||||
if not permissions.can_invite_users:
|
||||
missing_permissions.append('Создание инвайт-ссылок')
|
||||
if not permissions.can_restrict_members:
|
||||
missing_permissions.append('Управление пользователями (видеть вступления)')
|
||||
|
||||
if missing_permissions:
|
||||
log.warning(
|
||||
f'Bot lacks required permissions in channel {input.telegram_id}: {missing_permissions}. '
|
||||
f'Attempted by user {input.user_telegram_id}'
|
||||
)
|
||||
permissions_text = '\n'.join(f'• {p}' for p in missing_permissions)
|
||||
await self.telegram_bot.send_message(
|
||||
f'⚠️ Бот был добавлен в канал "{input.title}", но не имеет необходимых прав.\n\n'
|
||||
f'Отсутствующие права:\n{permissions_text}\n\n'
|
||||
'Пожалуйста, предоставьте эти права боту в настройках канала.',
|
||||
input.user_telegram_id,
|
||||
)
|
||||
raise domain.ChannelNoAdminRights()
|
||||
|
||||
user = await self.database.get_user(telegram_id=input.user_telegram_id)
|
||||
if not user:
|
||||
log.warning(f'User {input.user_telegram_id} not found when trying to connect channel {input.telegram_id}')
|
||||
await self.telegram_bot.send_message(
|
||||
f'⚠️ Канал "{input.title}" не может быть подключен.\n\n'
|
||||
'Вы должны сначала авторизоваться в веб-панели перед подключением каналов.',
|
||||
input.user_telegram_id,
|
||||
)
|
||||
raise domain.UserNotFound()
|
||||
|
||||
memberships = await self.database.get_user_workspaces(user.id)
|
||||
workspaces: list[domain.Workspace] = []
|
||||
for membership in memberships:
|
||||
workspace: domain.Workspace | None = membership.workspace
|
||||
if not workspace:
|
||||
workspace = await self.database.get_workspace(membership.workspace_id)
|
||||
|
||||
if workspace:
|
||||
workspaces.append(workspace)
|
||||
|
||||
if not workspaces:
|
||||
workspace = await self.get_or_create_personal_workspace(user)
|
||||
workspaces = [workspace]
|
||||
|
||||
if len(workspaces) == 1:
|
||||
workspace = workspaces[0]
|
||||
|
||||
channel = await self.database.get_channel(workspace.id, telegram_id=input.telegram_id)
|
||||
if channel:
|
||||
channel.title = input.title
|
||||
channel.username = input.username
|
||||
await self.database.update_channel(channel)
|
||||
else:
|
||||
channel = domain.Channel(
|
||||
telegram_id=input.telegram_id,
|
||||
title=input.title,
|
||||
username=input.username,
|
||||
workspace_id=workspace.id,
|
||||
)
|
||||
await self.database.create_channel(channel)
|
||||
|
||||
project = await self.database.get_project(workspace.id, channel_id=channel.id)
|
||||
if project:
|
||||
project.status = domain.ProjectStatus.ACTIVE
|
||||
await self.database.update_project(project)
|
||||
else:
|
||||
project = domain.Project(
|
||||
workspace_id=workspace.id,
|
||||
channel_id=channel.id,
|
||||
status=domain.ProjectStatus.ACTIVE,
|
||||
)
|
||||
await self.database.create_project(project)
|
||||
|
||||
await self.ensure_active_purchase_plan(project)
|
||||
|
||||
log.info(
|
||||
'Project for channel %s connected/updated successfully in workspace %s by user %s',
|
||||
input.telegram_id,
|
||||
workspace.id,
|
||||
input.user_telegram_id,
|
||||
)
|
||||
|
||||
await self.telegram_bot.send_message(
|
||||
f'✅ Канал "{input.title}" добавлен в рабочее пространство "{workspace.name}".', input.user_telegram_id
|
||||
)
|
||||
|
||||
return dto.ProjectOutput(
|
||||
id=project.id,
|
||||
telegram_id=channel.telegram_id,
|
||||
title=channel.title,
|
||||
username=channel.username,
|
||||
status=project.status,
|
||||
)
|
||||
|
||||
current_state = await self.database.get_telegram_state(user.telegram_id)
|
||||
if current_state and current_state.state == domain.TelegramStateEnum.PROJECT_WAITING_WORKSPACE:
|
||||
await self.telegram_bot.send_message(
|
||||
'⚠️ Сначала завершите выбор рабочего пространства для предыдущего канала.', user.telegram_id
|
||||
)
|
||||
return None
|
||||
|
||||
context: dict[str, object] = {
|
||||
'channel_data': {
|
||||
'telegram_id': input.telegram_id,
|
||||
'title': input.title,
|
||||
'username': input.username,
|
||||
'permissions': input.bot_permissions.model_dump(),
|
||||
}
|
||||
}
|
||||
|
||||
if current_state:
|
||||
context['previous_state'] = current_state.state.value
|
||||
context['previous_context'] = dict(current_state.context or {})
|
||||
|
||||
await self.database.set_telegram_state(
|
||||
user.telegram_id,
|
||||
domain.TelegramStateEnum.PROJECT_WAITING_WORKSPACE,
|
||||
context,
|
||||
)
|
||||
|
||||
buttons = [
|
||||
[InlineKeyboardButton(text=workspace.name, callback_data=f'tg_add_project_2:{workspace.id}')]
|
||||
for workspace in workspaces
|
||||
]
|
||||
|
||||
await self.telegram_bot.send_message_with_inline_keyboard(
|
||||
(f'✨ Подключение канала\n\nВыберите рабочее пространство, в которое добавить канал "{input.title}":'),
|
||||
user.telegram_id,
|
||||
buttons,
|
||||
)
|
||||
|
||||
return None
|
||||
109
src/usecase/project/tg_add_project_2.py
Normal file
109
src/usecase/project/tg_add_project_2.py
Normal file
@@ -0,0 +1,109 @@
|
||||
import logging
|
||||
import uuid
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from src import domain
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .. import Usecase
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def tg_add_project_2(
|
||||
self: 'Usecase', telegram_id: int, chat_id: int, callback_data: str, message_id: int
|
||||
) -> None:
|
||||
state = await self.database.get_telegram_state(telegram_id)
|
||||
if not state or state.state != domain.TelegramStateEnum.PROJECT_WAITING_WORKSPACE:
|
||||
log.debug('User %s has no active project workspace selection flow', telegram_id)
|
||||
return
|
||||
|
||||
context = state.context or {}
|
||||
previous_state_value = context.get('previous_state')
|
||||
previous_context = context.get('previous_context') or {}
|
||||
previous_state = None
|
||||
if previous_state_value:
|
||||
try:
|
||||
previous_state = domain.TelegramStateEnum(previous_state_value)
|
||||
except ValueError:
|
||||
previous_state = None
|
||||
|
||||
if not callback_data.startswith('tg_add_project_2:'):
|
||||
log.warning('Invalid project workspace callback data: %s', callback_data)
|
||||
return
|
||||
|
||||
workspace_id_str = callback_data.split(':', 1)[1]
|
||||
try:
|
||||
workspace_id = uuid.UUID(workspace_id_str)
|
||||
except ValueError:
|
||||
log.error('Invalid workspace UUID in callback data: %s', workspace_id_str)
|
||||
await self.telegram_bot.send_message('❌ Некорректное рабочее пространство.', chat_id)
|
||||
if previous_state:
|
||||
await self.database.set_telegram_state(telegram_id, previous_state, previous_context)
|
||||
else:
|
||||
await self.database.clear_telegram_state(telegram_id)
|
||||
return
|
||||
|
||||
channel_data = context.get('channel_data')
|
||||
if not channel_data:
|
||||
log.error('Missing channel data for user %s in project flow', telegram_id)
|
||||
await self.telegram_bot.send_message('❌ Не удалось подключить канал. Попробуйте снова.', chat_id)
|
||||
if previous_state:
|
||||
await self.database.set_telegram_state(telegram_id, previous_state, previous_context)
|
||||
else:
|
||||
await self.database.clear_telegram_state(telegram_id)
|
||||
return
|
||||
|
||||
user = await self.database.get_user(telegram_id=telegram_id)
|
||||
if not user:
|
||||
await self.telegram_bot.send_message('Вы не зарегистрированы на платформе.', chat_id)
|
||||
await self.database.clear_telegram_state(telegram_id)
|
||||
return
|
||||
|
||||
workspace = await self.database.get_workspace_for_user(workspace_id, user.id)
|
||||
if not workspace:
|
||||
await self.telegram_bot.send_message('❌ У вас нет доступа к выбранному рабочему пространству.', chat_id)
|
||||
return
|
||||
|
||||
telegram_channel_id = int(channel_data['telegram_id'])
|
||||
channel = await self.database.get_channel(workspace.id, telegram_id=telegram_channel_id)
|
||||
if channel:
|
||||
channel.title = str(channel_data['title'])
|
||||
channel.username = channel_data.get('username')
|
||||
await self.database.update_channel(channel)
|
||||
else:
|
||||
channel = domain.Channel(
|
||||
telegram_id=telegram_channel_id,
|
||||
title=str(channel_data['title']),
|
||||
username=channel_data.get('username'),
|
||||
workspace_id=workspace.id,
|
||||
)
|
||||
await self.database.create_channel(channel)
|
||||
|
||||
project = await self.database.get_project(workspace.id, channel_id=channel.id)
|
||||
if project:
|
||||
project.status = domain.ProjectStatus.ACTIVE
|
||||
await self.database.update_project(project)
|
||||
else:
|
||||
project = domain.Project(workspace_id=workspace.id, channel_id=channel.id, status=domain.ProjectStatus.ACTIVE)
|
||||
await self.database.create_project(project)
|
||||
|
||||
await self.ensure_active_purchase_plan(project)
|
||||
|
||||
log.info(
|
||||
'Channel %s connected/updated successfully in workspace %s by user %s',
|
||||
channel.telegram_id,
|
||||
workspace.id,
|
||||
telegram_id,
|
||||
)
|
||||
|
||||
await self.telegram_bot.send_message(
|
||||
f'✅ Канал "{channel.title}" добавлен в рабочее пространство "{workspace.name}".', telegram_id
|
||||
)
|
||||
|
||||
await self.telegram_bot.edit_message_reply_markup(chat_id, message_id)
|
||||
|
||||
if previous_state:
|
||||
await self.database.set_telegram_state(telegram_id, previous_state, previous_context)
|
||||
else:
|
||||
await self.database.clear_telegram_state(telegram_id)
|
||||
58
src/usecase/project/update_project_permissions.py
Normal file
58
src/usecase/project/update_project_permissions.py
Normal file
@@ -0,0 +1,58 @@
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from src import domain, dto
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .. import Usecase
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def update_project_permissions(self: 'Usecase', input: dto.UpdateProjectPermissionsInput) -> None:
|
||||
missing_permissions = []
|
||||
if not input.permissions.can_invite_users:
|
||||
missing_permissions.append('Создание инвайт-ссылок')
|
||||
if not input.permissions.can_restrict_members:
|
||||
missing_permissions.append('Управление пользователями')
|
||||
|
||||
# Получаем user_id по telegram_id
|
||||
user = await self.database.get_user(telegram_id=input.user_telegram_id)
|
||||
if not user:
|
||||
log.warning(f'User with telegram_id {input.user_telegram_id} not found when updating channel permissions')
|
||||
return
|
||||
|
||||
project = await self.database.get_project_for_user_by_telegram(user.id, input.telegram_id)
|
||||
if not project:
|
||||
log.warning(f'Project channel {input.telegram_id} not found when permissions changed')
|
||||
raise domain.ProjectNotFound()
|
||||
|
||||
if not missing_permissions:
|
||||
if project.status != domain.ProjectStatus.ACTIVE:
|
||||
project.status = domain.ProjectStatus.ACTIVE
|
||||
await self.database.update_project(project)
|
||||
|
||||
await self.telegram_bot.send_message(
|
||||
f'✅ Канал "{input.chat_title}" был активирован.\n\nВсе необходимые права боту предоставлены!',
|
||||
input.user_telegram_id,
|
||||
)
|
||||
log.info(f'Project channel {input.telegram_id} reactivated - all permissions granted')
|
||||
return
|
||||
|
||||
if project.status == domain.ProjectStatus.ACTIVE:
|
||||
project.status = domain.ProjectStatus.INACTIVE
|
||||
await self.database.update_project(project)
|
||||
|
||||
missed_permissions = '\n'.join(f'• {p}' for p in missing_permissions)
|
||||
await self.telegram_bot.send_message(
|
||||
(
|
||||
f'⚠️ Канал "{input.chat_title}" был деактивирован.\n\n'
|
||||
f'Боту убрали необходимые права:\n{missed_permissions}'
|
||||
),
|
||||
input.user_telegram_id,
|
||||
)
|
||||
log.warning(
|
||||
'Project channel %s deactivated due to missing permissions: %s',
|
||||
input.telegram_id,
|
||||
missing_permissions,
|
||||
)
|
||||
Reference in New Issue
Block a user