Files
TG_autoposter/app/database/member_repository.py
Andrew K. Choi 48f8c6f0eb UserBot Integration Complete: Fixed container startup, integrated UserBot menu to main bot
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
2025-12-21 12:09:11 +09:00

299 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()