feat: Tái cấu trúc bot sang kiến trúc cog, thêm hỗ trợ đa máy chủ, giới thiệu tính năng đăng ký bóng đá, giao diện web và quản lý cấu hình.
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
from typing import List, Optional, Any
|
||||
from sqlalchemy import select
|
||||
from models.config import BotConfig
|
||||
from infra.db import postgres
|
||||
|
||||
class ConfigRepository:
|
||||
def __init__(self):
|
||||
self.Session = postgres.get_sessionmaker()
|
||||
|
||||
async def get(self, guild_id: int, key: str, default: Any = None) -> Any:
|
||||
"""Lấy giá trị config theo key"""
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(BotConfig).where(
|
||||
BotConfig.guild_id == guild_id,
|
||||
BotConfig.key == key
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
config = result.scalar_one_or_none()
|
||||
return config.value if config else default
|
||||
except Exception as e:
|
||||
print(f"Error getting config {key} for guild {guild_id}: {e}")
|
||||
return default
|
||||
|
||||
async def set(self, guild_id: int, key: str, value: str, description: str = None) -> Optional[BotConfig]:
|
||||
"""Cập nhật hoặc tạo mới config"""
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(BotConfig).where(
|
||||
BotConfig.guild_id == guild_id,
|
||||
BotConfig.key == key
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
config = result.scalar_one_or_none()
|
||||
|
||||
if config:
|
||||
config.value = str(value)
|
||||
if description:
|
||||
config.description = description
|
||||
else:
|
||||
config = BotConfig(guild_id=guild_id, key=key, value=str(value), description=description)
|
||||
session.add(config)
|
||||
|
||||
await session.commit()
|
||||
await session.refresh(config)
|
||||
return config
|
||||
except Exception as e:
|
||||
print(f"Error setting config {key} for guild {guild_id}: {e}")
|
||||
return None
|
||||
|
||||
async def get_all(self, guild_id: int) -> List[BotConfig]:
|
||||
"""Lấy tất cả config"""
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(BotConfig).where(BotConfig.guild_id == guild_id)
|
||||
result = await session.execute(stmt)
|
||||
return list(result.scalars().all())
|
||||
except Exception as e:
|
||||
print(f"Error getting all configs for guild {guild_id}: {e}")
|
||||
return []
|
||||
|
||||
async def delete(self, guild_id: int, key: str) -> bool:
|
||||
"""Xóa config"""
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(BotConfig).where(
|
||||
BotConfig.guild_id == guild_id,
|
||||
BotConfig.key == key
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
config = result.scalar_one_or_none()
|
||||
|
||||
if config:
|
||||
await session.delete(config)
|
||||
await session.commit()
|
||||
return True
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"Error deleting config {key} for guild {guild_id}: {e}")
|
||||
return False
|
||||
@@ -0,0 +1,56 @@
|
||||
from typing import List, Optional
|
||||
from sqlalchemy import select
|
||||
from models.feature_toggle import FeatureToggle
|
||||
from infra.db import postgres
|
||||
|
||||
class FeatureToggleRepository:
|
||||
def __init__(self):
|
||||
self.Session = postgres.get_sessionmaker()
|
||||
|
||||
async def get(self, guild_id: int, feature_name: str) -> bool:
|
||||
"""Check if feature is enabled for guild"""
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(FeatureToggle).where(
|
||||
FeatureToggle.guild_id == guild_id,
|
||||
FeatureToggle.feature_name == feature_name
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
toggle = result.scalar_one_or_none()
|
||||
return toggle.is_enabled if toggle else False # Default Disabled
|
||||
except Exception as e:
|
||||
print(f"Error getting feature {feature_name} for guild {guild_id}: {e}")
|
||||
return False
|
||||
|
||||
async def set(self, guild_id: int, feature_name: str, is_enabled: bool) -> Optional[FeatureToggle]:
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(FeatureToggle).where(
|
||||
FeatureToggle.guild_id == guild_id,
|
||||
FeatureToggle.feature_name == feature_name
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
toggle = result.scalar_one_or_none()
|
||||
|
||||
if toggle:
|
||||
toggle.is_enabled = is_enabled
|
||||
else:
|
||||
toggle = FeatureToggle(guild_id=guild_id, feature_name=feature_name, is_enabled=is_enabled)
|
||||
session.add(toggle)
|
||||
|
||||
await session.commit()
|
||||
await session.refresh(toggle)
|
||||
return toggle
|
||||
except Exception as e:
|
||||
print(f"Error setting feature {feature_name} for guild {guild_id}: {e}")
|
||||
return None
|
||||
|
||||
async def get_all_for_guild(self, guild_id: int) -> List[FeatureToggle]:
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(FeatureToggle).where(FeatureToggle.guild_id == guild_id)
|
||||
result = await session.execute(stmt)
|
||||
return list(result.scalars().all())
|
||||
except Exception as e:
|
||||
print(f"Error getting all features for guild {guild_id}: {e}")
|
||||
return []
|
||||
@@ -0,0 +1,67 @@
|
||||
from typing import List, Optional
|
||||
from sqlalchemy import select, delete
|
||||
from models.football import FootballSubscription
|
||||
from infra.db import postgres
|
||||
|
||||
class FootballRepository:
|
||||
def __init__(self):
|
||||
self.Session = postgres.get_sessionmaker()
|
||||
|
||||
async def add_subscription(self, guild_id: int, channel_id: int, team_name: str, team_id: int = None) -> bool:
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
# Check exist
|
||||
stmt = select(FootballSubscription).where(
|
||||
FootballSubscription.guild_id == guild_id,
|
||||
FootballSubscription.team_name == team_name
|
||||
)
|
||||
existing = await session.execute(stmt)
|
||||
if existing.scalar_one_or_none():
|
||||
return False
|
||||
|
||||
sub = FootballSubscription(
|
||||
guild_id=guild_id,
|
||||
channel_id=channel_id,
|
||||
team_name=team_name,
|
||||
team_id=team_id
|
||||
)
|
||||
session.add(sub)
|
||||
await session.commit()
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Error adding subscription: {e}")
|
||||
return False
|
||||
|
||||
async def remove_subscription(self, guild_id: int, team_name: str) -> bool:
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = delete(FootballSubscription).where(
|
||||
FootballSubscription.guild_id == guild_id,
|
||||
FootballSubscription.team_name == team_name
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
await session.commit()
|
||||
return result.rowcount > 0
|
||||
except Exception as e:
|
||||
print(f"Error removing subscription: {e}")
|
||||
return False
|
||||
|
||||
async def get_all_subscriptions(self) -> List[FootballSubscription]:
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(FootballSubscription)
|
||||
result = await session.execute(stmt)
|
||||
return list(result.scalars().all())
|
||||
except Exception as e:
|
||||
print(f"Error getting subscriptions: {e}")
|
||||
return []
|
||||
|
||||
async def get_guild_subscriptions(self, guild_id: int) -> List[FootballSubscription]:
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(FootballSubscription).where(FootballSubscription.guild_id == guild_id)
|
||||
result = await session.execute(stmt)
|
||||
return list(result.scalars().all())
|
||||
except Exception as e:
|
||||
print(f"Error getting guild subscriptions: {e}")
|
||||
return []
|
||||
@@ -0,0 +1,49 @@
|
||||
from typing import List, Optional
|
||||
from sqlalchemy import select
|
||||
from models.guild import Guild
|
||||
from infra.db import postgres
|
||||
|
||||
class GuildRepository:
|
||||
def __init__(self):
|
||||
self.Session = postgres.get_sessionmaker()
|
||||
|
||||
async def get(self, guild_id: int) -> Optional[Guild]:
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(Guild).where(Guild.id == guild_id)
|
||||
result = await session.execute(stmt)
|
||||
return result.scalar_one_or_none()
|
||||
except Exception as e:
|
||||
print(f"Error getting guild {guild_id}: {e}")
|
||||
return None
|
||||
|
||||
async def create_or_update(self, guild_id: int, name: str) -> Optional[Guild]:
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(Guild).where(Guild.id == guild_id)
|
||||
result = await session.execute(stmt)
|
||||
guild = result.scalar_one_or_none()
|
||||
|
||||
if guild:
|
||||
guild.name = name
|
||||
guild.is_active = True
|
||||
else:
|
||||
guild = Guild(id=guild_id, name=name, is_active=True)
|
||||
session.add(guild)
|
||||
|
||||
await session.commit()
|
||||
await session.refresh(guild)
|
||||
return guild
|
||||
except Exception as e:
|
||||
print(f"Error upserting guild {guild_id}: {e}")
|
||||
return None
|
||||
|
||||
async def get_all(self) -> List[Guild]:
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(Guild).where(Guild.is_active == True)
|
||||
result = await session.execute(stmt)
|
||||
return list(result.scalars().all())
|
||||
except Exception as e:
|
||||
print(f"Error getting all guilds: {e}")
|
||||
return []
|
||||
@@ -9,33 +9,39 @@ class HomeDebtRepository:
|
||||
def __init__(self):
|
||||
self.Session = postgres.get_sessionmaker()
|
||||
|
||||
async def get(self, discord_user_id: int) -> Optional[HomeDebt]:
|
||||
async def get(self, guild_id: int, discord_user_id: int) -> Optional[HomeDebt]:
|
||||
"""Lấy thông tin thành viên"""
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(HomeDebt).where(HomeDebt.user_id == discord_user_id)
|
||||
stmt = select(HomeDebt).where(
|
||||
HomeDebt.guild_id == guild_id,
|
||||
HomeDebt.user_id == discord_user_id
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
return result.scalar_one_or_none()
|
||||
except Exception as e:
|
||||
print(f"Error getting user: {e}")
|
||||
return None
|
||||
|
||||
async def get_other(self, discord_user_id: int) -> Optional[HomeDebt]:
|
||||
async def get_other(self, guild_id: int, discord_user_id: int) -> Optional[HomeDebt]:
|
||||
"""Lấy thông tin thành viên khác"""
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(HomeDebt).where(HomeDebt.user_id != discord_user_id).limit(1)
|
||||
stmt = select(HomeDebt).where(
|
||||
HomeDebt.guild_id == guild_id,
|
||||
HomeDebt.user_id != discord_user_id
|
||||
).limit(1)
|
||||
result = await session.execute(stmt)
|
||||
return result.scalar_one_or_none()
|
||||
except Exception as e:
|
||||
print(f"Error getting other member: {e}")
|
||||
return None
|
||||
|
||||
async def create_home_debt(self, user_id: int, value: int) -> Optional[HomeDebt]:
|
||||
async def create_home_debt(self, guild_id: int, user_id: int, value: int) -> Optional[HomeDebt]:
|
||||
"""Tạo mới khoản nợ"""
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
home_debt = HomeDebt(user_id=user_id, value=value)
|
||||
home_debt = HomeDebt(guild_id=guild_id, user_id=user_id, value=value)
|
||||
session.add(home_debt)
|
||||
await session.commit()
|
||||
await session.refresh(home_debt)
|
||||
@@ -57,13 +63,13 @@ class HomeDebtRepository:
|
||||
print(f"Error updating home debt: {e}")
|
||||
return None
|
||||
|
||||
async def get_all(self) -> List[HomeDebt]:
|
||||
async def get_all(self, guild_id: int) -> List[HomeDebt]:
|
||||
"""Lấy tất cả khoản nợ"""
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(HomeDebt)
|
||||
stmt = select(HomeDebt).where(HomeDebt.guild_id == guild_id)
|
||||
result = await session.execute(stmt)
|
||||
return result.scalars().all()
|
||||
return list(result.scalars().all())
|
||||
except Exception as e:
|
||||
print(f"Error getting all home debts: {e}")
|
||||
return []
|
||||
+22
-15
@@ -11,22 +11,25 @@ class ScoreRepository:
|
||||
def __init__(self):
|
||||
self.Session = postgres.get_sessionmaker()
|
||||
|
||||
async def get(self, user_id: int) -> Optional[Score]:
|
||||
async def get(self, guild_id: int, user_id: int) -> Optional[Score]:
|
||||
"""Lấy thông tin thành viên"""
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(Score).where(Score.user_id == user_id)
|
||||
stmt = select(Score).where(
|
||||
Score.guild_id == guild_id,
|
||||
Score.user_id == user_id
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
return result.scalar_one_or_none()
|
||||
except Exception as e:
|
||||
print(f"Error getting user: {e}")
|
||||
return None
|
||||
|
||||
async def create(self, user_id: int, point: int) -> Optional[Score]:
|
||||
async def create(self, guild_id: int, user_id: int, point: int) -> Optional[Score]:
|
||||
"""Tạo thông tin thành viên"""
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
currency = Score(user_id=user_id, point=point)
|
||||
currency = Score(guild_id=guild_id, user_id=user_id, point=point)
|
||||
session.add(currency)
|
||||
await session.commit()
|
||||
await session.refresh(currency)
|
||||
@@ -35,13 +38,16 @@ class ScoreRepository:
|
||||
print(f"Error creating user: {e}")
|
||||
return None
|
||||
|
||||
async def update(self, user_id: int, point: int) -> Optional[Score]:
|
||||
async def update(self, guild_id: int, user_id: int, point: int) -> Optional[Score]:
|
||||
"""Cập nhật thông tin thành viên"""
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = (
|
||||
update(Score)
|
||||
.where(Score.user_id == user_id)
|
||||
.where(
|
||||
Score.guild_id == guild_id,
|
||||
Score.user_id == user_id
|
||||
)
|
||||
.values(point=point)
|
||||
.returning(Score)
|
||||
)
|
||||
@@ -52,22 +58,22 @@ class ScoreRepository:
|
||||
print(f"Error updating user: {e}")
|
||||
return None
|
||||
|
||||
async def get_all(self) -> List[Score]:
|
||||
async def get_all(self, guild_id: int) -> List[Score]:
|
||||
"""Lấy tất cả thông tin thành viên"""
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(Score).order_by(Score.point.desc())
|
||||
stmt = select(Score).where(Score.guild_id == guild_id).order_by(Score.point.desc())
|
||||
result = await session.execute(stmt)
|
||||
return result.scalars().all()
|
||||
except Exception as e:
|
||||
print(f"Error getting all users: {e}")
|
||||
return []
|
||||
|
||||
async def get_all_with_point(self) -> List[Score]:
|
||||
async def get_all_with_point(self, guild_id: int) -> List[Score]:
|
||||
"""Lấy tất cả thông tin thành viên và số dư"""
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(Score)
|
||||
stmt = select(Score).where(Score.guild_id == guild_id)
|
||||
result = await session.execute(stmt)
|
||||
return result.scalars().all()
|
||||
except Exception as e:
|
||||
@@ -75,28 +81,29 @@ class ScoreRepository:
|
||||
return []
|
||||
|
||||
# Get all with sort by point
|
||||
async def get_all_with_sort_by_point(self) -> List[Score]:
|
||||
async def get_all_with_sort_by_point(self, guild_id: int) -> List[Score]:
|
||||
"""Lấy tất cả thông tin thành viên và sắp xếp theo số dư"""
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
stmt = select(Score).order_by(Score.point.desc())
|
||||
stmt = select(Score).where(Score.guild_id == guild_id).order_by(Score.point.desc())
|
||||
result = await session.execute(stmt)
|
||||
return result.scalars().all()
|
||||
except Exception as e:
|
||||
print(f"Error getting all users with sort by point: {e}")
|
||||
return []
|
||||
|
||||
async def upsert_or_increment_point(self, user_id: str, user_name: str, amount: int) -> Optional[int]:
|
||||
async def upsert_or_increment_point(self, guild_id: int, user_id: str, user_name: str, amount: int) -> Optional[int]:
|
||||
try:
|
||||
async with self.Session() as session:
|
||||
# Sử dụng PostgreSQL UPSERT (ON CONFLICT)
|
||||
stmt = pg_insert(Score).values(
|
||||
guild_id=int(guild_id),
|
||||
user_id=int(user_id),
|
||||
user_name=user_name,
|
||||
user_name=str(user_name),
|
||||
point=amount
|
||||
)
|
||||
stmt = stmt.on_conflict_do_update(
|
||||
index_elements=['user_id'],
|
||||
index_elements=['guild_id', 'user_id'], # Must match UniqueConstraint/Index
|
||||
set_=dict(
|
||||
point=Score.point + amount,
|
||||
user_name=stmt.excluded.user_name
|
||||
|
||||
Reference in New Issue
Block a user