diff --git a/apps/home_debt.py b/apps/home_debt.py index 57f3c2d..31e47d2 100644 --- a/apps/home_debt.py +++ b/apps/home_debt.py @@ -1,146 +1,84 @@ import discord -from core.bot import bot, server_repo, channel_repo, home_debt_repo +from core.bot import bot, home_debt_repo, CHANNEL_HOME_DEBT_ID from utils.common import format_vnd -@bot.tree.command(name='set-home-debt', description='Set channel to home debt') -async def set_home_debt(interaction: discord.Interaction, channel: discord.TextChannel): - try: - # Kiểm tra xem người dùng có quyền admin không - if not interaction.user.guild_permissions.administrator: - await interaction.response.send_message("Bạn cần có quyền Administrator để sử dụng lệnh này!", ephemeral=True) - return - await interaction.response.defer(ephemeral=False) - - # Ensure server is in database - server = await server_repo.get(str(interaction.guild_id)) - if not server: - await interaction.followup.send("Server chưa được khởi tạo trong database! Vui lòng sử dụng lệnh /init_server trước.", ephemeral=True) - return +def is_correct_channel(ctx): + """Kiểm tra xem command có được thực hiện trong đúng channel không""" + return ctx.channel.id == CHANNEL_HOME_DEBT_ID - # Ensure channel was registered in database - discord_channel = await channel_repo.get_channel(channel.id) - if not discord_channel: - await channel_repo.create_channel(interaction.guild_id, channel.id, 'home_debt') - else: - await channel_repo.update_channel(channel.id, 'home_debt') - - await interaction.followup.send(f"home_debt app đã được cấu hình cho {channel.mention}") - except Exception as e: - if interaction.response.is_done(): - await interaction.followup.send(f"Lỗi khi cấu hình home_debt app: {str(e)}", ephemeral=True) - else: - await interaction.response.send_message(f"Lỗi khi cấu hình home_debt app: {str(e)}", ephemeral=True) - -# Check if the channel is registered and configured for home_debt app -async def check_channel_home_debt(channel_id: int) -> bool: - # Check if channel is registered in database - channel = await channel_repo.get_channel(channel_id) - if not channel or channel.app != 'home_debt': - return False - - return True - -@bot.tree.command(name="home-debt-add", description="Thêm khoản chi tiêu mới") -async def home_debt_add(interaction: discord.Interaction, amount: int, description: str = "Không có lý do"): +@bot.command(name="hdadd", description="Thêm khoản chi tiêu mới") +async def add(ctx, amount: int): """Vì home chỉ có 2 người nên sẽ tự động thêm khoản chi tiêu cho người còn lại""" try: - check = await check_channel_home_debt(interaction.channel_id) - if not check: - await interaction.response.send_message("Channel chưa được đăng ký!", ephemeral=True) + if not is_correct_channel(ctx): return - await interaction.response.defer(ephemeral=False) - # Get info other user from home_debt table - other_user = await home_debt_repo.get_other(interaction.user.id) + other_user = await home_debt_repo.get_other(ctx.author.id) other_user.value += round(amount / 2) resp = await home_debt_repo.update_home_debt(other_user) if not resp: - await interaction.followup.send("Có lỗi xảy ra khi cập nhật dữ liệu", ephemeral=True) + await ctx.send("Có lỗi xảy ra khi cập nhật dữ liệu") return - await interaction.followup.send(f"Đã thêm {format_vnd(round(amount * 1000 / 2))} cho {interaction.user.name}. Số dư hiện tại là {format_vnd(other_user.value * 1000)}", ephemeral=False) + await ctx.send(f"Đã thêm {format_vnd(round(amount * 1000 / 2))} cho {ctx.author.name}. Số dư hiện tại là {format_vnd(other_user.value * 1000)}") except Exception as e: - if interaction.response.is_done(): - await interaction.followup.send(f"Có lỗi xảy ra: {str(e)}", ephemeral=True) - else: - await interaction.response.send_message(f"Có lỗi xảy ra: {str(e)}", ephemeral=True) + await ctx.send(f"Có lỗi xảy ra: {str(e)}") -@bot.tree.command(name="home-debt-check", description="Kiểm tra số dư của bạn") -async def home_debt_check(interaction: discord.Interaction): +@bot.command(name="hdcheck", description="Kiểm tra số dư của bạn") +async def home_debt_check(ctx): """Kiểm tra số dư của mọi người""" try: - check = await check_channel_home_debt(interaction.channel_id) - if not check: - await interaction.response.send_message("Channel chưa được đăng ký!", ephemeral=True) + if not is_correct_channel(ctx): return - await interaction.response.defer(ephemeral=False) - # Lấy số dư của mọi người - home_debts = await home_debt_repo.get_all() - embed = discord.Embed(title="Số dư của mọi người", color=discord.Color.blue()) - for home_debt in home_debts: - user = await bot.fetch_user(home_debt.user_id) - value = format_vnd(home_debt.value * 1000) - embed.add_field(name=f"{user.name}", value=f"{value}", inline=False) - await interaction.followup.send(embed=embed, ephemeral=False) - except Exception as e: - if interaction.response.is_done(): - await interaction.followup.send(f"Có lỗi xảy ra: {str(e)}", ephemeral=True) - else: - await interaction.response.send_message(f"Có lỗi xảy ra: {str(e)}", ephemeral=True) - -@bot.tree.command(name="vay-debt", description="Vay nợ") -async def vay_debt(interaction: discord.Interaction, amount: int, description: str = "Không có lý do"): - """Vay nợ""" - try: - check = await check_channel_home_debt(interaction.channel_id) - if not check: - await interaction.response.send_message("Channel chưa được đăng ký!", ephemeral=True) - return - - await interaction.response.defer(ephemeral=False) - # Get info user from home_debt table - user = await home_debt_repo.get(interaction.user.id) + users = await home_debt_repo.get_all() + embed = discord.Embed(title="Số dư của mọi người", color=discord.Color.blue()) + for user in users: + value = format_vnd(user.value * 1000) + embed.add_field(name=f"{ctx.guild.get_member(user.user_id).display_name}", value=f"{value}", inline=False) + await ctx.send(embed=embed) + except Exception as e: + await ctx.send(f"Có lỗi xảy ra: {str(e)}") + +@bot.command(name="hdvay", description="Vay nợ") +async def vay(ctx, amount: int): + """Vay nợ""" + try: + if not is_correct_channel(ctx): + return + + # Get info user from home_debt table + user = await home_debt_repo.get(ctx.author.id) user.value += amount resp = await home_debt_repo.update_home_debt(user) if not resp: - await interaction.followup.send("Có lỗi xảy ra khi cập nhật dữ liệu", ephemeral=True) + await ctx.send("Có lỗi xảy ra khi cập nhật dữ liệu") return # Send message to user - await interaction.followup.send(f"Đã vay {format_vnd(amount * 1000)} bởi {interaction.user.name}", ephemeral=False) + await ctx.send(f"Đã vay {format_vnd(amount * 1000)} bởi {ctx.author.name}") except Exception as e: - if interaction.response.is_done(): - await interaction.followup.send(f"Có lỗi xảy ra: {str(e)}", ephemeral=True) - else: - await interaction.response.send_message(f"Có lỗi xảy ra: {str(e)}", ephemeral=True) + await ctx.send(f"Có lỗi xảy ra: {str(e)}") -@bot.tree.command(name="tra-debt", description="Trả nợ") -async def tra_debt(interaction: discord.Interaction, amount: int): +@bot.command(name="hdtra", description="Trả nợ") +async def tra(ctx, amount: int): """Trả nợ""" try: - check = await check_channel_home_debt(interaction.channel_id) - if not check: - await interaction.response.send_message("Channel chưa được đăng ký!", ephemeral=True) + if not is_correct_channel(ctx): return - await interaction.response.defer(ephemeral=False) - # Get info user from home_debt table - user = await home_debt_repo.get(interaction.user.id) + user = await home_debt_repo.get(ctx.author.id) user.value -= amount resp = await home_debt_repo.update_home_debt(user) if not resp: - await interaction.followup.send("Có lỗi xảy ra khi cập nhật dữ liệu", ephemeral=True) + await ctx.send("Có lỗi xảy ra khi cập nhật dữ liệu") return # Send message to user - await interaction.followup.send(f"Đã trả {format_vnd(amount * 1000)} từ {interaction.user.name}", ephemeral=False) + await ctx.send(f"Đã trả {format_vnd(amount * 1000)} từ {ctx.author.name}") except Exception as e: - if interaction.response.is_done(): - await interaction.followup.send(f"Có lỗi xảy ra: {str(e)}", ephemeral=True) - else: - await interaction.response.send_message(f"Có lỗi xảy ra: {str(e)}", ephemeral=True) \ No newline at end of file + await ctx.send(f"Có lỗi xảy ra: {str(e)}") \ No newline at end of file diff --git a/apps/noi_tu.py b/apps/noi_tu.py index 6289663..0435972 100644 --- a/apps/noi_tu.py +++ b/apps/noi_tu.py @@ -1,10 +1,8 @@ import discord -import os import asyncio -from core.bot import bot -from discord.ext import commands -from typing import Dict, Set, Optional -from datetime import datetime, timedelta +from core.bot import bot, CHANNEL_NOI_TU_ID +from typing import Set +from datetime import datetime from apps.currency import incr # Lazy load repository để tránh lỗi database connection @@ -18,9 +16,6 @@ def get_noi_tu_repo(): noi_tu_repo = NoiTuRepository() return noi_tu_repo -# Lấy channel ID từ environment -CHANNEL_NOI_TU_ID = int(os.getenv('CHANNEL_NOI_TU_ID', 0)) - # Game state class NoiTuGame: def __init__(self): diff --git a/apps/server.py b/apps/server.py deleted file mode 100644 index 37dc834..0000000 --- a/apps/server.py +++ /dev/null @@ -1,48 +0,0 @@ -import discord -from core.bot import bot, server_repo - -@bot.tree.command(name='help', description='Show help') -async def help(interaction: discord.Interaction): - """Command để hiển thị danh sách các lệnh""" - # Get all commands - commands = bot.tree.get_commands() - command_list = "\n".join([f"• {c.name}: {c.description}" for c in commands]) - - # Create embed with command list - embed = discord.Embed(title="Help", description="Đây là danh sách các lệnh bạn có thể sử dụng:") - embed.add_field(name="Danh sách lệnh", value=command_list, inline=False) - await interaction.response.send_message(embed=embed, ephemeral=False) - print(f"Đã gửi lệnh help cho {interaction.user.name}") - -@bot.tree.command(name="init_server", description="Khởi tạo thông tin server trong database") -async def init_server(interaction: discord.Interaction): - """Command để khởi tạo thông tin server trong database""" - try: - # Kiểm tra quyền admin - if not interaction.user.guild_permissions.administrator: - await interaction.response.send_message("Bạn cần có quyền Administrator để sử dụng lệnh này!", ephemeral=True) - return - - # Gửi response ngay lập tức - await interaction.response.defer(ephemeral=True) - - # Kiểm tra xem server đã tồn tại trong database chưa - existing_server = await server_repo.get_server(interaction.guild_id) - if not existing_server: - # Tạo record mới cho server - await server_repo.create_server(interaction.guild_id, interaction.guild.name) - await interaction.followup.send(f"Đã thêm server {interaction.guild.name} vào database!", ephemeral=True) - else: - # Cập nhật tên server nếu có thay đổi - if existing_server.name != interaction.guild.name: - await server_repo.update_server(interaction.guild_id, interaction.guild.name) - await interaction.followup.send(f"Đã cập nhật tên server trong database!", ephemeral=True) - else: - await interaction.followup.send("Server đã tồn tại trong database!", ephemeral=True) - except Exception as e: - # Nếu chưa gửi response, gửi response lỗi - if not interaction.response.is_done(): - await interaction.response.send_message(f"Có lỗi xảy ra: {str(e)}", ephemeral=True) - else: - # Nếu đã gửi response, gửi followup - await interaction.followup.send(f"Có lỗi xảy ra: {str(e)}", ephemeral=True) \ No newline at end of file diff --git a/core/bot.py b/core/bot.py index 1348afa..6e73f4f 100644 --- a/core/bot.py +++ b/core/bot.py @@ -2,6 +2,7 @@ import os import discord from discord.ext import commands from dotenv import load_dotenv +from repositories import HomeDebtRepository, CurrencyRepository # Load environment variables load_dotenv() @@ -16,22 +17,46 @@ intents.guilds = True bot = commands.Bot(command_prefix='!', intents=intents) # Initialize repositories -from repositories import ServerRepository, ChannelRepository, ChannelAppRepository, HomeDebtRepository, CurrencyRepository - -server_repo = ServerRepository() -channel_repo = ChannelRepository() -channel_app_repo = ChannelAppRepository() home_debt_repo = HomeDebtRepository() currency_repo = CurrencyRepository() -# Store user cooldowns -from typing import Dict, Set -from datetime import datetime +CHANNEL_HOME_DEBT_ID = int(os.getenv('CHANNEL_HOME_DEBT_ID', 0)) +CHANNEL_NOI_TU_ID = int(os.getenv('CHANNEL_NOI_TU_ID', 0)) -user_cooldowns: Dict[int, datetime] = {} -active_voice_users: Set[int] = set() -# Constants -CHAT_EXP_POINTS = 1 -VOICE_EXP_POINTS_PER_MINUTE = 2 -CHAT_COOLDOWN = 60 # seconds \ No newline at end of file +@bot.tree.command(name='help', description='Show help') +async def help(interaction: discord.Interaction): + """Command để hiển thị danh sách các lệnh""" + channel = interaction.channel + + # Dictionary chứa thông tin help cho từng channel + help_commands = { + CHANNEL_HOME_DEBT_ID: [ + "!hdadd ", + "!hdcheck", + "!hdtra ", + "!hdvay " + ], + CHANNEL_NOI_TU_ID: [ + "!start", + "!end" + ] + } + + # Kiểm tra xem channel có trong danh sách không + if channel.id in help_commands: + embed = discord.Embed( + title="Help", + description="Đây là danh sách các lệnh bạn có thể sử dụng:", + color=discord.Color.blue() + ) + + commands_list = "\n".join(help_commands[channel.id]) + embed.add_field(name="", value=commands_list, inline=False) + + await interaction.response.send_message(embed=embed, ephemeral=False) + else: + await interaction.response.send_message( + "Không có lệnh help cho channel này!", + ephemeral=True + ) \ No newline at end of file diff --git a/models/channel.py b/models/channel.py deleted file mode 100644 index 6a656b0..0000000 --- a/models/channel.py +++ /dev/null @@ -1,21 +0,0 @@ -from pydantic import BaseModel -from datetime import datetime -from typing import Optional - -class DiscordChannel(BaseModel): - id: Optional[int] = None - created_at: Optional[datetime] = None - updated_at: Optional[datetime] = None - server_id: int - channel_id: int - app: str - - def to_dict(self): - return { - "id": self.id, - "created_at": self.created_at, - "updated_at": self.updated_at, - "server_id": self.server_id, - "channel_id": self.channel_id, - "app": self.app - } \ No newline at end of file diff --git a/models/channel_app.py b/models/channel_app.py deleted file mode 100644 index 42603c9..0000000 --- a/models/channel_app.py +++ /dev/null @@ -1,10 +0,0 @@ -from pydantic import BaseModel -from datetime import datetime -from typing import Optional - -class DiscordChannelApp(BaseModel): - id: Optional[int] = None - created_at: Optional[datetime] = None - updated_at: Optional[datetime] = None - name: str - description: str \ No newline at end of file diff --git a/models/server.py b/models/server.py deleted file mode 100644 index 48edb9d..0000000 --- a/models/server.py +++ /dev/null @@ -1,22 +0,0 @@ -from pydantic import BaseModel -from datetime import datetime -from typing import Optional - -class DiscordServer(BaseModel): - id: Optional[int] = None - created_at: Optional[datetime] = None - updated_at: Optional[datetime] = None - server_id: int - name: str - - def to_dict(self): - return { - 'id': self.id, - 'created_at': self.created_at, - 'updated_at': self.updated_at, - 'server_id': self.server_id, - 'name': self.name - } - - def to_json(self): - return self.model_dump_json(exclude_none=True) \ No newline at end of file diff --git a/repositories/channel.py b/repositories/channel.py deleted file mode 100644 index 0c13e40..0000000 --- a/repositories/channel.py +++ /dev/null @@ -1,44 +0,0 @@ -from typing import Optional, List -from models.channel import DiscordChannel -from infra.db import postgres - -class ChannelRepository: - def __init__(self): - self.table = postgres.get_table('discord_channel') - - async def get_channel(self, channel_id: int) -> Optional[DiscordChannel]: - try: - response = self.table.select('*').eq('channel_id', channel_id).execute() - if response.data: - return DiscordChannel.model_validate(response.data[0]) - return None - except Exception as e: - print(f"Error getting channel: {e}") - return None - - async def get_channels_by_server(self, server_id: int) -> List[DiscordChannel]: - try: - response = self.table.select('*').eq('server_id', server_id).execute() - return [DiscordChannel(**channel) for channel in response.data] - except Exception as e: - print(f"Error getting channels: {e}") - return [] - - async def create_channel(self, server_id: int, channel_id: int, app: str) -> Optional[DiscordChannel]: - try: - channel = DiscordChannel(server_id=server_id, channel_id=channel_id, app=app) - response = self.table.insert(channel.dict(exclude_none=True)).execute() - return DiscordChannel(**response.data[0]) - except Exception as e: - print(f"Error creating channel: {e}") - return None - - async def update_channel(self, channel_id: int, app: str) -> Optional[DiscordChannel]: - try: - response = self.table.update({'app': app}).eq('channel_id', channel_id).execute() - if response.data: - return DiscordChannel(**response.data[0]) - return None - except Exception as e: - print(f"Error updating channel: {e}") - return None \ No newline at end of file diff --git a/repositories/channel_app.py b/repositories/channel_app.py deleted file mode 100644 index 549199a..0000000 --- a/repositories/channel_app.py +++ /dev/null @@ -1,52 +0,0 @@ -from typing import Optional, List -from models.channel_app import DiscordChannelApp -from infra.db import postgres - -class ChannelAppRepository: - def __init__(self): - self.table = postgres.get_table('discord_channel_app') - - async def get_channel_app(self, name: str) -> Optional[DiscordChannelApp]: - try: - response = self.table.select('*').eq('name', name).execute() - if response.data: - return DiscordChannelApp(**response.data[0]) - return None - except Exception as e: - print(f"Error getting channel app: {e}") - return None - - async def get_all_apps(self) -> List[DiscordChannelApp]: - try: - response = self.table.select('*').execute() - return [DiscordChannelApp(**app) for app in response.data] - except Exception as e: - print(f"Error getting channel apps: {e}") - return [] - - async def create_channel_app(self, name: str, description: str) -> Optional[DiscordChannelApp]: - try: - app = DiscordChannelApp(name=name, description=description) - response = self.table.insert(app.dict(exclude_none=True)).execute() - return DiscordChannelApp(**response.data[0]) - except Exception as e: - print(f"Error creating channel app: {e}") - return None - - async def update_channel_app(self, name: str, description: str) -> Optional[DiscordChannelApp]: - try: - response = self.table.update({'description': description}).eq('name', name).execute() - if response.data: - return DiscordChannelApp(**response.data[0]) - return None - except Exception as e: - print(f"Error updating channel app: {e}") - return None - - async def delete_channel_app(self, channel_id: int, app_name: str) -> bool: - try: - response = self.table.delete().eq('channel_id', channel_id).eq('app_name', app_name).execute() - return len(response.data) > 0 - except Exception as e: - print(f"Error deleting channel app: {e}") - return False \ No newline at end of file diff --git a/repositories/server.py b/repositories/server.py deleted file mode 100644 index 36b540e..0000000 --- a/repositories/server.py +++ /dev/null @@ -1,39 +0,0 @@ -from typing import Optional -from models.server import DiscordServer -from infra.db import postgres - -class ServerRepository: - def __init__(self): - self.table = postgres.get_table('discord_server') - - async def get(self, server_id: int) -> Optional[DiscordServer]: - """Get Discord server by ID""" - try: - response = self.table.select('*').eq('server_id', server_id).execute() - if response.data: - return DiscordServer.model_validate(response.data[0]) - return None - except Exception as e: - print(f"Error getting server: {e}") - return None - - async def create(self, server_id: int, name: str) -> Optional[DiscordServer]: - """Create new Discord server""" - try: - server = DiscordServer(server_id=server_id, name=name) - response = self.table.insert(server.to_dict()).execute() - return DiscordServer.model_validate(response.data[0]) - except Exception as e: - print(f"Error creating server: {e}") - return None - - async def update(self, server_id: int, name: str) -> Optional[DiscordServer]: - """Update Discord server""" - try: - response = self.table.update({'name': name}).eq('server_id', server_id).execute() - if response.data: - return DiscordServer.model_validate(response.data[0]) - return None - except Exception as e: - print(f"Error updating server: {e}") - return None \ No newline at end of file