MAJOR FIXES: ✅ Fixed UserBot container startup by making TELEGRAM_BOT_TOKEN optional ✅ Broke circular import chain between app modules ✅ Made Config.validate() conditional for UserBot-only mode ✅ Removed unused celery import from userbot_service.py INTEGRATION: ✅ UserBot menu now accessible from main bot /start command ✅ Added 🤖 UserBot button to main keyboard ✅ Integrated userbot_manager.py handlers: - userbot_menu: Main UserBot interface - userbot_settings: Configuration - userbot_collect_groups: Gather all user groups - userbot_collect_members: Parse group members ✅ UserBot handlers properly registered in ConversationHandler CONTAINERS: ✅ tg_autoposter_bot: Running and handling /start commands ✅ tg_autoposter_userbot: Running as standalone microservice ✅ All dependent services (Redis, PostgreSQL, Celery workers) operational STATUS: Bot is fully operational and ready for testing
299 lines
12 KiB
Python
299 lines
12 KiB
Python
from sqlalchemy import select, and_
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
from typing import List, Optional
|
||
from datetime import datetime
|
||
from ..models.group_members import GroupMember, GroupKeyword, GroupStatistics
|
||
|
||
|
||
class GroupMemberRepository:
|
||
"""Репозиторий для работы с участниками групп"""
|
||
|
||
def __init__(self, session: AsyncSession):
|
||
self.session = session
|
||
|
||
async def add_member(self, group_id: int, user_id: str, username: str = None,
|
||
first_name: str = None, last_name: str = None,
|
||
is_bot: bool = False, is_admin: bool = False,
|
||
is_owner: bool = False) -> GroupMember:
|
||
"""Добавить участника в группу"""
|
||
member = GroupMember(
|
||
group_id=group_id,
|
||
user_id=user_id,
|
||
username=username,
|
||
first_name=first_name,
|
||
last_name=last_name,
|
||
is_bot=is_bot,
|
||
is_admin=is_admin,
|
||
is_owner=is_owner,
|
||
joined_at=datetime.utcnow()
|
||
)
|
||
self.session.add(member)
|
||
await self.session.flush()
|
||
return member
|
||
|
||
async def add_or_update_member(self, data: dict) -> GroupMember:
|
||
"""Добавить или обновить члена группы"""
|
||
group_id = data.get('group_id')
|
||
user_id = str(data.get('user_id'))
|
||
|
||
member = await self.get_member_by_user_id(group_id, user_id)
|
||
|
||
if member:
|
||
# Обновить существующего
|
||
if 'username' in data:
|
||
member.username = data['username']
|
||
if 'first_name' in data:
|
||
member.first_name = data['first_name']
|
||
if 'last_name' in data:
|
||
member.last_name = data['last_name']
|
||
if 'is_bot' in data:
|
||
member.is_bot = data['is_bot']
|
||
if 'is_admin' in data:
|
||
member.is_admin = data['is_admin']
|
||
if 'is_owner' in data:
|
||
member.is_owner = data['is_owner']
|
||
member.updated_at = datetime.utcnow()
|
||
else:
|
||
# Создать нового
|
||
member = GroupMember(
|
||
group_id=group_id,
|
||
user_id=user_id,
|
||
username=data.get('username'),
|
||
first_name=data.get('first_name'),
|
||
last_name=data.get('last_name'),
|
||
is_bot=data.get('is_bot', False),
|
||
is_admin=data.get('is_admin', False),
|
||
is_owner=data.get('is_owner', False),
|
||
joined_at=datetime.utcnow()
|
||
)
|
||
self.session.add(member)
|
||
|
||
await self.session.flush()
|
||
return member
|
||
|
||
async def get_member_by_user_id(self, group_id: int, user_id: str) -> Optional[GroupMember]:
|
||
"""Получить участника по user_id"""
|
||
result = await self.session.execute(
|
||
select(GroupMember).where(
|
||
and_(
|
||
GroupMember.group_id == group_id,
|
||
GroupMember.user_id == user_id
|
||
)
|
||
)
|
||
)
|
||
return result.scalar_one_or_none()
|
||
|
||
async def get_members_by_group(self, group_id: int, is_admin: bool = None) -> List[GroupMember]:
|
||
"""Получить всех участников группы"""
|
||
query = select(GroupMember).where(GroupMember.group_id == group_id)
|
||
if is_admin is not None:
|
||
query = query.where(GroupMember.is_admin == is_admin)
|
||
result = await self.session.execute(query)
|
||
return result.scalars().all()
|
||
|
||
async def update_member(self, group_id: int, user_id: str, **kwargs) -> Optional[GroupMember]:
|
||
"""Обновить данные участника"""
|
||
member = await self.get_member_by_user_id(group_id, user_id)
|
||
if not member:
|
||
return None
|
||
|
||
for key, value in kwargs.items():
|
||
if hasattr(member, key):
|
||
setattr(member, key, value)
|
||
|
||
member.updated_at = datetime.utcnow()
|
||
await self.session.flush()
|
||
return member
|
||
|
||
async def delete_member(self, group_id: int, user_id: str) -> bool:
|
||
"""Удалить участника из группы"""
|
||
member = await self.get_member_by_user_id(group_id, user_id)
|
||
if not member:
|
||
return False
|
||
await self.session.delete(member)
|
||
return True
|
||
|
||
async def bulk_add_members(self, group_id: int, members_data: List[dict]) -> List[GroupMember]:
|
||
"""Массовое добавление участников"""
|
||
members = []
|
||
for data in members_data:
|
||
member = GroupMember(
|
||
group_id=group_id,
|
||
user_id=data.get('user_id'),
|
||
username=data.get('username'),
|
||
first_name=data.get('first_name'),
|
||
last_name=data.get('last_name'),
|
||
is_bot=data.get('is_bot', False),
|
||
is_admin=data.get('is_admin', False),
|
||
is_owner=data.get('is_owner', False),
|
||
joined_at=datetime.utcnow()
|
||
)
|
||
members.append(member)
|
||
self.session.add_all(members)
|
||
await self.session.flush()
|
||
return members
|
||
|
||
async def search_members_by_username(self, group_id: int, keyword: str) -> List[GroupMember]:
|
||
"""Поиск участников по username"""
|
||
result = await self.session.execute(
|
||
select(GroupMember).where(
|
||
and_(
|
||
GroupMember.group_id == group_id,
|
||
GroupMember.username.ilike(f'%{keyword}%')
|
||
)
|
||
)
|
||
)
|
||
return result.scalars().all()
|
||
|
||
async def search_members_by_name(self, group_id: int, keyword: str) -> List[GroupMember]:
|
||
"""Поиск участников по имени"""
|
||
result = await self.session.execute(
|
||
select(GroupMember).where(
|
||
and_(
|
||
GroupMember.group_id == group_id,
|
||
(GroupMember.first_name.ilike(f'%{keyword}%')) |
|
||
(GroupMember.last_name.ilike(f'%{keyword}%'))
|
||
)
|
||
)
|
||
)
|
||
return result.scalars().all()
|
||
|
||
async def get_admin_count(self, group_id: int) -> int:
|
||
"""Получить количество администраторов"""
|
||
result = await self.session.execute(
|
||
select(GroupMember).where(
|
||
and_(
|
||
GroupMember.group_id == group_id,
|
||
GroupMember.is_admin == True
|
||
)
|
||
)
|
||
)
|
||
return len(result.scalars().all())
|
||
|
||
async def get_bot_count(self, group_id: int) -> int:
|
||
"""Получить количество ботов"""
|
||
result = await self.session.execute(
|
||
select(GroupMember).where(
|
||
and_(
|
||
GroupMember.group_id == group_id,
|
||
GroupMember.is_bot == True
|
||
)
|
||
)
|
||
)
|
||
return len(result.scalars().all())
|
||
|
||
async def clear_members(self, group_id: int) -> int:
|
||
"""Очистить всех участников группы"""
|
||
result = await self.session.execute(
|
||
select(GroupMember).where(GroupMember.group_id == group_id)
|
||
)
|
||
members = result.scalars().all()
|
||
count = len(members)
|
||
for member in members:
|
||
await self.session.delete(member)
|
||
return count
|
||
|
||
|
||
class GroupKeywordRepository:
|
||
"""Репозиторий для работы с ключевыми словами"""
|
||
|
||
def __init__(self, session: AsyncSession):
|
||
self.session = session
|
||
|
||
async def add_keywords(self, group_id: int, keywords: str, description: str = None) -> GroupKeyword:
|
||
"""Добавить ключевые слова для группы"""
|
||
keyword = GroupKeyword(
|
||
group_id=group_id,
|
||
keywords=keywords,
|
||
description=description
|
||
)
|
||
self.session.add(keyword)
|
||
await self.session.flush()
|
||
return keyword
|
||
|
||
async def get_keywords(self, group_id: int) -> Optional[GroupKeyword]:
|
||
"""Получить ключевые слова для группы"""
|
||
result = await self.session.execute(
|
||
select(GroupKeyword).where(GroupKeyword.group_id == group_id)
|
||
)
|
||
return result.scalar_one_or_none()
|
||
|
||
async def update_keywords(self, group_id: int, keywords: str, description: str = None) -> Optional[GroupKeyword]:
|
||
"""Обновить ключевые слова"""
|
||
kw = await self.get_keywords(group_id)
|
||
if not kw:
|
||
return None
|
||
kw.keywords = keywords
|
||
if description:
|
||
kw.description = description
|
||
kw.updated_at = datetime.utcnow()
|
||
await self.session.flush()
|
||
return kw
|
||
|
||
async def delete_keywords(self, group_id: int) -> bool:
|
||
"""Удалить ключевые слова"""
|
||
kw = await self.get_keywords(group_id)
|
||
if not kw:
|
||
return False
|
||
await self.session.delete(kw)
|
||
return True
|
||
|
||
|
||
class GroupStatisticsRepository:
|
||
"""Репозиторий для работы со статистикой групп"""
|
||
|
||
def __init__(self, session: AsyncSession):
|
||
self.session = session
|
||
|
||
async def get_or_create_statistics(self, group_id: int) -> GroupStatistics:
|
||
"""Получить или создать статистику"""
|
||
result = await self.session.execute(
|
||
select(GroupStatistics).where(GroupStatistics.group_id == group_id)
|
||
)
|
||
stats = result.scalar_one_or_none()
|
||
if not stats:
|
||
stats = GroupStatistics(group_id=group_id)
|
||
self.session.add(stats)
|
||
await self.session.flush()
|
||
return stats
|
||
|
||
async def update_members_count(self, group_id: int, total: int, admins: int = 0, bots: int = 0):
|
||
"""Обновить количество участников"""
|
||
stats = await self.get_or_create_statistics(group_id)
|
||
stats.total_members = total
|
||
stats.total_admins = admins
|
||
stats.total_bots = bots
|
||
stats.last_updated = datetime.utcnow()
|
||
await self.session.flush()
|
||
|
||
async def increment_sent_messages(self, group_id: int, via_client: bool = False):
|
||
"""Увеличить счетчик отправленных сообщений"""
|
||
stats = await self.get_or_create_statistics(group_id)
|
||
stats.messages_sent += 1
|
||
if via_client:
|
||
stats.messages_via_client += 1
|
||
stats.last_updated = datetime.utcnow()
|
||
await self.session.flush()
|
||
|
||
async def increment_failed_messages(self, group_id: int):
|
||
"""Увеличить счетчик ошибок"""
|
||
stats = await self.get_or_create_statistics(group_id)
|
||
stats.messages_failed += 1
|
||
stats.last_updated = datetime.utcnow()
|
||
await self.session.flush()
|
||
|
||
async def update_send_capabilities(self, group_id: int, can_bot: bool, can_client: bool):
|
||
"""Обновить возможности отправки"""
|
||
stats = await self.get_or_create_statistics(group_id)
|
||
stats.can_send_as_bot = can_bot
|
||
stats.can_send_as_client = can_client
|
||
stats.last_updated = datetime.utcnow()
|
||
await self.session.flush()
|
||
|
||
async def get_statistics(self, group_id: int) -> Optional[GroupStatistics]:
|
||
"""Получить статистику"""
|
||
result = await self.session.execute(
|
||
select(GroupStatistics).where(GroupStatistics.group_id == group_id)
|
||
)
|
||
return result.scalar_one_or_none()
|