init commit
This commit is contained in:
33
app/database/__init__.py
Normal file
33
app/database/__init__.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
|
||||
from sqlalchemy import pool
|
||||
from app.models import Base
|
||||
import os
|
||||
|
||||
DATABASE_URL = os.getenv(
|
||||
'DATABASE_URL',
|
||||
'sqlite+aiosqlite:///./autoposter.db'
|
||||
)
|
||||
|
||||
engine = create_async_engine(
|
||||
DATABASE_URL,
|
||||
echo=False,
|
||||
poolclass=pool.NullPool
|
||||
)
|
||||
|
||||
AsyncSessionLocal = async_sessionmaker(
|
||||
engine,
|
||||
class_=AsyncSession,
|
||||
expire_on_commit=False
|
||||
)
|
||||
|
||||
|
||||
async def init_db():
|
||||
"""Инициализация БД - создание всех таблиц"""
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
|
||||
|
||||
async def get_session():
|
||||
"""Получить сессию БД"""
|
||||
async with AsyncSessionLocal() as session:
|
||||
yield session
|
||||
258
app/database/member_repository.py
Normal file
258
app/database/member_repository.py
Normal file
@@ -0,0 +1,258 @@
|
||||
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 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()
|
||||
205
app/database/repository.py
Normal file
205
app/database/repository.py
Normal file
@@ -0,0 +1,205 @@
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.future import select
|
||||
from sqlalchemy.orm import selectinload
|
||||
from app.models import Group, Message, MessageGroup
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List, Optional
|
||||
|
||||
|
||||
class GroupRepository:
|
||||
"""Репозиторий для работы с группами"""
|
||||
|
||||
def __init__(self, session: AsyncSession):
|
||||
self.session = session
|
||||
|
||||
async def add_group(self, chat_id: str, title: str, slow_mode_delay: int = 0) -> Group:
|
||||
"""Добавить новую группу"""
|
||||
group = Group(
|
||||
chat_id=chat_id,
|
||||
title=title,
|
||||
slow_mode_delay=slow_mode_delay
|
||||
)
|
||||
self.session.add(group)
|
||||
await self.session.commit()
|
||||
await self.session.refresh(group)
|
||||
return group
|
||||
|
||||
async def get_group_by_chat_id(self, chat_id: str) -> Optional[Group]:
|
||||
"""Получить группу по ID чата"""
|
||||
result = await self.session.execute(
|
||||
select(Group).where(Group.chat_id == chat_id)
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
async def get_all_active_groups(self) -> List[Group]:
|
||||
"""Получить все активные группы"""
|
||||
result = await self.session.execute(
|
||||
select(Group).where(Group.is_active == True)
|
||||
)
|
||||
return result.scalars().all()
|
||||
|
||||
async def update_group_slow_mode(self, group_id: int, delay: int) -> None:
|
||||
"""Обновить slow mode задержку группы"""
|
||||
group = await self.session.get(Group, group_id)
|
||||
if group:
|
||||
group.slow_mode_delay = delay
|
||||
group.updated_at = datetime.utcnow()
|
||||
await self.session.commit()
|
||||
|
||||
async def update_last_message_time(self, group_id: int) -> None:
|
||||
"""Обновить время последнего сообщения"""
|
||||
group = await self.session.get(Group, group_id)
|
||||
if group:
|
||||
group.last_message_time = datetime.utcnow()
|
||||
await self.session.commit()
|
||||
|
||||
async def deactivate_group(self, group_id: int) -> None:
|
||||
"""Деактивировать группу"""
|
||||
group = await self.session.get(Group, group_id)
|
||||
if group:
|
||||
group.is_active = False
|
||||
await self.session.commit()
|
||||
|
||||
async def activate_group(self, group_id: int) -> None:
|
||||
"""Активировать группу"""
|
||||
group = await self.session.get(Group, group_id)
|
||||
if group:
|
||||
group.is_active = True
|
||||
await self.session.commit()
|
||||
|
||||
|
||||
class MessageRepository:
|
||||
"""Репозиторий для работы с сообщениями"""
|
||||
|
||||
def __init__(self, session: AsyncSession):
|
||||
self.session = session
|
||||
|
||||
async def add_message(self, text: str, title: str, parse_mode: str = 'HTML') -> Message:
|
||||
"""Добавить новое сообщение"""
|
||||
message = Message(
|
||||
text=text,
|
||||
title=title,
|
||||
parse_mode=parse_mode
|
||||
)
|
||||
self.session.add(message)
|
||||
await self.session.commit()
|
||||
await self.session.refresh(message)
|
||||
return message
|
||||
|
||||
async def get_message(self, message_id: int) -> Optional[Message]:
|
||||
"""Получить сообщение по ID"""
|
||||
result = await self.session.execute(
|
||||
select(Message).where(Message.id == message_id)
|
||||
)
|
||||
return result.scalar_one_or_none()
|
||||
|
||||
async def get_all_messages(self, active_only: bool = True) -> List[Message]:
|
||||
"""Получить все сообщения"""
|
||||
query = select(Message)
|
||||
if active_only:
|
||||
query = query.where(Message.is_active == True)
|
||||
result = await self.session.execute(query)
|
||||
return result.scalars().all()
|
||||
|
||||
async def update_message(self, message_id: int, text: str = None, title: str = None) -> None:
|
||||
"""Обновить сообщение"""
|
||||
message = await self.session.get(Message, message_id)
|
||||
if message:
|
||||
if text:
|
||||
message.text = text
|
||||
if title:
|
||||
message.title = title
|
||||
message.updated_at = datetime.utcnow()
|
||||
await self.session.commit()
|
||||
|
||||
async def deactivate_message(self, message_id: int) -> None:
|
||||
"""Деактивировать сообщение"""
|
||||
message = await self.session.get(Message, message_id)
|
||||
if message:
|
||||
message.is_active = False
|
||||
await self.session.commit()
|
||||
|
||||
async def delete_message(self, message_id: int) -> None:
|
||||
"""Удалить сообщение"""
|
||||
message = await self.session.get(Message, message_id)
|
||||
if message:
|
||||
await self.session.delete(message)
|
||||
await self.session.commit()
|
||||
|
||||
|
||||
class MessageGroupRepository:
|
||||
"""Репозиторий для работы со связями сообщение-группа"""
|
||||
|
||||
def __init__(self, session: AsyncSession):
|
||||
self.session = session
|
||||
|
||||
async def add_message_to_group(self, message_id: int, group_id: int) -> MessageGroup:
|
||||
"""Добавить сообщение в группу"""
|
||||
# Проверить, не существует ли уже
|
||||
result = await self.session.execute(
|
||||
select(MessageGroup).where(
|
||||
(MessageGroup.message_id == message_id) &
|
||||
(MessageGroup.group_id == group_id)
|
||||
)
|
||||
)
|
||||
existing = result.scalar_one_or_none()
|
||||
if existing:
|
||||
return existing
|
||||
|
||||
link = MessageGroup(message_id=message_id, group_id=group_id)
|
||||
self.session.add(link)
|
||||
await self.session.commit()
|
||||
await self.session.refresh(link)
|
||||
return link
|
||||
|
||||
async def get_message_groups_to_send(self, message_id: int) -> List[MessageGroup]:
|
||||
"""Получить группы, куда еще не отправлено сообщение"""
|
||||
result = await self.session.execute(
|
||||
select(MessageGroup)
|
||||
.where((MessageGroup.message_id == message_id) & (MessageGroup.is_sent == False))
|
||||
.options(selectinload(MessageGroup.group))
|
||||
)
|
||||
return result.scalars().all()
|
||||
|
||||
async def get_unsent_messages_for_group(self, group_id: int) -> List[MessageGroup]:
|
||||
"""Получить неотправленные сообщения для группы"""
|
||||
result = await self.session.execute(
|
||||
select(MessageGroup)
|
||||
.where((MessageGroup.group_id == group_id) & (MessageGroup.is_sent == False))
|
||||
.options(selectinload(MessageGroup.message))
|
||||
)
|
||||
return result.scalars().all()
|
||||
|
||||
async def mark_as_sent(self, message_group_id: int, error: str = None) -> None:
|
||||
"""Отметить как отправленное"""
|
||||
link = await self.session.get(MessageGroup, message_group_id)
|
||||
if link:
|
||||
link.is_sent = True
|
||||
link.sent_at = datetime.utcnow()
|
||||
if error:
|
||||
link.error = error
|
||||
link.is_sent = False
|
||||
await self.session.commit()
|
||||
|
||||
async def get_messages_for_group(self, group_id: int) -> List[MessageGroup]:
|
||||
"""Получить все сообщения для группы с их статусом"""
|
||||
result = await self.session.execute(
|
||||
select(MessageGroup)
|
||||
.where(MessageGroup.group_id == group_id)
|
||||
.options(selectinload(MessageGroup.message))
|
||||
.order_by(MessageGroup.created_at.desc())
|
||||
)
|
||||
return result.scalars().all()
|
||||
|
||||
async def remove_message_from_group(self, message_id: int, group_id: int) -> None:
|
||||
"""Удалить сообщение из группы"""
|
||||
result = await self.session.execute(
|
||||
select(MessageGroup).where(
|
||||
(MessageGroup.message_id == message_id) &
|
||||
(MessageGroup.group_id == group_id)
|
||||
)
|
||||
)
|
||||
link = result.scalar_one_or_none()
|
||||
if link:
|
||||
await self.session.delete(link)
|
||||
await self.session.commit()
|
||||
Reference in New Issue
Block a user