adding debt app

This commit is contained in:
2025-06-20 10:31:59 +07:00
parent 047290e2ea
commit b1068ecd2c
18 changed files with 208 additions and 526 deletions
+16
View File
@@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Run Bot",
"type": "debugpy",
"request": "launch",
"program": "main.py",
"console": "integratedTerminal",
"envFile": "${workspaceFolder}/.env"
}
]
}
+7 -7
View File
@@ -1,8 +1,8 @@
import discord
from core.bot import bot, user_repo
# import discord
# from core.bot import bot, user_repo
@bot.command(name='exp')
async def show_exp(ctx):
user_id = ctx.author.id
exp = await user_repo.get_exp(user_id)
await ctx.send(f"{ctx.author.mention} has {exp} experience points!")
# @bot.command(name='exp')
# async def show_exp(ctx):
# user_id = ctx.author.id
# exp = await user_repo.get_exp(user_id)
# await ctx.send(f"{ctx.author.mention} has {exp} experience points!")
+63 -100
View File
@@ -1,131 +1,94 @@
import discord
from datetime import datetime
from core.bot import bot, server_repo, channel_repo, channel_app_repo, home_debt_repo
from models import DiscordServer, DiscordChannel, DiscordChannelApp
from typing import Any
from core.bot import bot, server_repo, channel_repo, home_debt_repo
@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:
# Get or create server
# 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
# Ensure server is in database
server = await server_repo.get(str(interaction.guild_id))
if not server:
server = DiscordServer(
id=str(interaction.guild_id),
name=interaction.guild.name,
created_at=datetime.now(),
updated_at=datetime.now()
)
server = await server_repo.create(server)
await interaction.response.send_message("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
# Get or create channel
discord_channel = await channel_repo.get(str(channel.id))
# Ensure channel was registered in database
discord_channel = await channel_repo.get_channel(channel.id)
if not discord_channel:
discord_channel = DiscordChannel(
id=str(channel.id),
server_id=str(interaction.guild_id),
name=channel.name,
type='text',
created_at=datetime.now(),
updated_at=datetime.now()
)
discord_channel = await channel_repo.create(discord_channel)
# Create or update home debt app
apps = await channel_app_repo.get_by_channel(str(channel.id))
home_debt_app = next((app for app in apps if app.app_type == 'home_debt'), None)
if home_debt_app:
home_debt_app.config = {'enabled': True}
await channel_app_repo.update(home_debt_app)
await channel_repo.create_channel(interaction.guild_id, channel.id, 'home_debt')
else:
home_debt_app = DiscordChannelApp(
id=f"{channel.id}_home_debt",
channel_id=str(channel.id),
app_type='home_debt',
config={'enabled': True},
created_at=datetime.now(),
updated_at=datetime.now()
)
await channel_app_repo.create(home_debt_app)
await channel_repo.update_channel(channel.id, 'home_debt')
await interaction.response.send_message(f"Home debt app has been set up for {channel.mention}")
await interaction.response.send_message(f"home_debt app đã được cấu hình cho {channel.mention}")
except Exception as e:
await interaction.response.send_message(f"Error setting up home debt app: {str(e)}")
await interaction.response.send_message(f"Lỗi khi cấu hình home_debt app: {str(e)}")
@bot.tree.command(name="add_member", description="Thêm thành viên mới vào nhà")
async def add_member(interaction: discord.Interaction, member: discord.Member):
"""Thêm thành viên mới vào nhà"""
try:
# Kiểm tra xem thành viên đã tồn tại chưa
existing_member = await home_debt_repo.get_member(member.id)
if existing_member:
await interaction.response.send_message(f"Thành viên {member.name} đã tồn tại trong hệ thống!", ephemeral=True)
# Decorator to check if the channel is registered and configured for home_debt app
def check_channel_home_debt():
def decorator(func):
async def wrapper(interaction: discord.Interaction, *args, **kwargs):
channel = await channel_repo.get_channel(interaction.channel_id)
if not channel:
await interaction.response.send_message("Channel chưa được đăng ký!", ephemeral=True)
return
if channel.app != 'home_debt':
await interaction.response.send_message("Lệnh này chỉ có thể sử dụng trong channel đã được cấu hình cho home_debt app!", ephemeral=True)
return
await func(interaction, *args, **kwargs)
return wrapper
return decorator
# Thêm thành vn mới
await home_debt_repo.add_member(member.id, member.name)
await interaction.response.send_message(f"Đã thêm thành viên {member.name} vào hệ thống!", ephemeral=True)
@bot.tree.command(name="home-debt-add", description="Thêm khoản chi tu mới")
# @check_channel_home_debt()
async def home_debt_add(interaction: discord.Interaction, amount: float, description: str):
"""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:
# Get info other user from home_debt table
other_user = await home_debt_repo.get_other(interaction.user.id)
other_user.value += amount / 2
await home_debt_repo.update_home_debt(other_user)
await interaction.response.send_message(f"Đã thêm khoản chi tiêu: {description} - {amount / 2}đ cho {other_user.user_id}", ephemeral=False)
except Exception as e:
await interaction.response.send_message(f"Có lỗi xảy ra: {str(e)}", ephemeral=True)
@bot.tree.command(name="add_expense", description="Thêm khoản chi tiêu mới")
async def add_expense(interaction: discord.Interaction, amount: float, description: str):
"""Thêm khoản chi tiêu mới"""
@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):
"""Kiểm tra số dư của mọi người"""
try:
# Kiểm tra xem người dùng có phải là thành viên không
member = await home_debt_repo.get_member(interaction.user.id)
if not member:
await interaction.response.send_message("Bạn chưa được thêm vào hệ thống! Vui lòng sử dụng lệnh /add_member trước.", ephemeral=True)
return
# Thêm khoản chi tiêu
expense = await home_debt_repo.add_expense(amount, description, interaction.user.id)
await interaction.response.send_message(f"Đã thêm khoản chi tiêu: {description} - {amount}đ", ephemeral=True)
# Lấy số dư của mọi người
home_debts = await home_debt_repo.get_all()
await interaction.response.send_message(f"Số dư của mọi người: {home_debts}", ephemeral=True)
except Exception as e:
await interaction.response.send_message(f"Có lỗi xảy ra: {str(e)}", ephemeral=True)
@bot.tree.command(name="check_balance", description="Kiểm tra số dư của bạn")
async def check_balance(interaction: discord.Interaction):
"""Kiểm tra số dư của bạn"""
@bot.tree.command(name="vay-debt", description="Vay nợ")
async def vay_debt(interaction: discord.Interaction, amount: float, description: str):
"""Vay nợ"""
try:
# Kiểm tra xem người dùng có phải là thành viên không
member = await home_debt_repo.get_member(interaction.user.id)
if not member:
await interaction.response.send_message("Bạn chưa được thêm vào hệ thống! Vui lòng sử dụng lệnh /add_member trước.", ephemeral=True)
return
# Get info user from home_debt table
user = await home_debt_repo.get(interaction.user.id)
user.value += amount
await home_debt_repo.update_home_debt(user)
# Lấy số dư
balance = await home_debt_repo.get_balance(interaction.user.id)
await interaction.response.send_message(f"Số dư của bạn: {balance}đ", ephemeral=True)
# Send message to user
await interaction.response.send_message(f"Đã vay nợ: {description} - {amount}đ cho {user.user_id}", ephemeral=False)
except Exception as e:
await interaction.response.send_message(f"Có lỗi xảy ra: {str(e)}", ephemeral=True)
@bot.tree.command(name="list_expenses", description="Xem danh sách chi tiêu")
async def list_expenses(interaction: discord.Interaction):
"""Xem danh sách chi tiêu"""
@bot.tree.command(name="tra-debt", description="Trả nợ")
async def tra_debt(interaction: discord.Interaction, amount: float):
"""Trả nợ"""
try:
# Kiểm tra xem người dùng có phải là thành viên không
member = await home_debt_repo.get_member(interaction.user.id)
if not member:
await interaction.response.send_message("Bạn chưa được thêm vào hệ thống! Vui lòng sử dụng lệnh /add_member trước.", ephemeral=True)
return
# Get info user from home_debt table
user = await home_debt_repo.get(interaction.user.id)
user.value -= amount
await home_debt_repo.update_home_debt(user)
# Lấy danh sách chi tiêu
expenses = await home_debt_repo.get_expenses()
if not expenses:
await interaction.response.send_message("Chưa có khoản chi tiêu nào!", ephemeral=True)
return
# Tạo embed message
embed = discord.Embed(title="Danh sách chi tiêu", color=discord.Color.blue())
for expense in expenses:
member = await home_debt_repo.get_member(expense.member_id)
embed.add_field(
name=f"{expense.description} - {expense.amount}đ",
value=f"Người chi: {member.name}",
inline=False
)
await interaction.response.send_message(embed=embed, ephemeral=True)
# Send message to user
await interaction.response.send_message(f"Đã trả nợ: {amount}đ cho {user.user_id}", ephemeral=False)
except Exception as e:
await interaction.response.send_message(f"Có lỗi xảy ra: {str(e)}", ephemeral=True)
+11 -21
View File
@@ -1,28 +1,18 @@
import discord
from datetime import datetime
from core.bot import bot, server_repo, channel_repo, channel_app_repo
from models import DiscordServer, DiscordChannel, DiscordChannelApp
@bot.tree.command(name='test', description='Test command')
async def test(interaction: discord.Interaction):
try:
# Get server
server = await server_repo.get(str(interaction.guild_id))
if server:
await interaction.response.send_message(f"Server: {server.name}")
else:
await interaction.response.send_message("Server not found")
except Exception as e:
await interaction.response.send_message(f"Error: {e}")
from core.bot import bot, server_repo
@bot.tree.command(name='help', description='Show help')
async def help(interaction: discord.Interaction):
embed = discord.Embed(title="Help", description="Here is a list of commands you can use:")
embed.add_field(name="!exp", value="Show your experience points", inline=True)
embed.add_field(name="!test", value="Test command", inline=True)
embed.add_field(name="!help", value="Show help", inline=True)
embed.add_field(name="!set-home-debt", value="Set channel to home debt", inline=True)
await interaction.response.send_message(embed=embed)
"""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):
+2 -2
View File
@@ -16,12 +16,12 @@ intents.guilds = True
bot = commands.Bot(command_prefix='!', intents=intents)
# Initialize repositories
from repositories import ServerRepository, ChannelRepository, ChannelAppRepository, UserRepository
from repositories import ServerRepository, ChannelRepository, ChannelAppRepository, HomeDebtRepository
server_repo = ServerRepository()
channel_repo = ChannelRepository()
channel_app_repo = ChannelAppRepository()
user_repo = UserRepository()
home_debt_repo = HomeDebtRepository()
# Store user cooldowns
from typing import Dict, Set
+5 -55
View File
@@ -1,58 +1,8 @@
from datetime import datetime
from .bot import bot, user_repo, user_cooldowns, active_voice_users, CHAT_EXP_POINTS, CHAT_COOLDOWN, VOICE_EXP_POINTS_PER_MINUTE
from core.bot import bot
from core.tasks import sync_commands
@bot.event
async def on_ready():
print(f'{bot.user} has connected to Discord!')
from .tasks import update_voice_exp
update_voice_exp.start()
try:
synced = await bot.tree.sync()
print(f"Synced {len(synced)} command(s)")
except Exception as e:
print(f"Failed to sync commands: {e}")
@bot.event
async def on_message(message):
if message.author.bot:
return
# Handle chat experience points
user_id = message.author.id
current_time = datetime.now()
if user_id not in user_cooldowns or (current_time - user_cooldowns[user_id]).total_seconds() >= CHAT_COOLDOWN:
current_exp = await user_repo.get_exp(user_id)
new_exp = current_exp + CHAT_EXP_POINTS
await user_repo.update_exp(user_id, new_exp)
user_cooldowns[user_id] = current_time
print(f"Added {CHAT_EXP_POINTS} exp points to {message.author.name}. Total: {new_exp}")
await bot.process_commands(message)
@bot.event
async def on_voice_state_update(member, before, after):
if member.bot:
return
# User joined a voice channel
if before.channel is None and after.channel is not None:
current_time = datetime.now().isoformat()
await user_repo.update_voice_time(member.id, current_time)
active_voice_users.add(member.id)
print(f"{member.name} joined voice channel {after.channel.name}")
# User left a voice channel
elif before.channel is not None and after.channel is None:
if member.id in active_voice_users:
active_voice_users.remove(member.id)
last_join_time = await user_repo.get_voice_time(member.id)
if last_join_time:
join_time = datetime.fromisoformat(last_join_time)
time_spent = datetime.now() - join_time
minutes = int(time_spent.total_seconds() / 60)
exp_points = minutes * VOICE_EXP_POINTS_PER_MINUTE
current_exp = await user_repo.get_exp(member.id)
new_exp = current_exp + exp_points
await user_repo.update_exp(member.id, new_exp)
print(f"{member.name} spent {minutes} minutes in voice. Added {exp_points} exp points. Total: {new_exp}")
print(f'{bot.user} đã tham gia vào server!')
await sync_commands.start()
print("Đã đồng bộ lệnh!")
+19 -15
View File
@@ -1,18 +1,22 @@
from discord.ext import tasks
from datetime import datetime
from .bot import bot, user_repo, active_voice_users, VOICE_EXP_POINTS_PER_MINUTE
from core.bot import bot
# @tasks.loop(minutes=1)
# async def update_voice_exp():
# current_time = datetime.now()
# for user_id in list(active_voice_users):
# last_join_time = await user_repo.get_voice_time(user_id)
# if last_join_time:
# join_time = datetime.fromisoformat(last_join_time)
# time_spent = current_time - join_time
# if time_spent.total_seconds() >= 60:
# current_exp = await user_repo.get_exp(user_id)
# new_exp = current_exp + VOICE_EXP_POINTS_PER_MINUTE
# await user_repo.update_exp(user_id, new_exp)
# await user_repo.update_voice_time(user_id, current_time.isoformat())
# print(f"Thêm {VOICE_EXP_POINTS_PER_MINUTE} điểm kinh nghiệm cho user {user_id} cho thời gian voice")
@tasks.loop(minutes=1)
async def update_voice_exp():
current_time = datetime.now()
for user_id in list(active_voice_users):
last_join_time = await user_repo.get_voice_time(user_id)
if last_join_time:
join_time = datetime.fromisoformat(last_join_time)
time_spent = current_time - join_time
if time_spent.total_seconds() >= 60:
current_exp = await user_repo.get_exp(user_id)
new_exp = current_exp + VOICE_EXP_POINTS_PER_MINUTE
await user_repo.update_exp(user_id, new_exp)
await user_repo.update_voice_time(user_id, current_time.isoformat())
print(f"Added {VOICE_EXP_POINTS_PER_MINUTE} exp points to user {user_id} for voice time")
async def sync_commands():
await bot.tree.sync()
print("Đã đồng bộ lệnh!")
-35
View File
@@ -1,35 +0,0 @@
from supabase import create_client, Client
import os
from dotenv import load_dotenv
from infra.db import PostgresConnection
load_dotenv()
class Database:
def __init__(self):
uri: str = os.getenv('SUPABASE_URI')
if not uri:
raise ValueError("SUPABASE_URI environment variable is not set")
# Parse URI to get URL and key
# Format: postgresql://postgres:[YOUR-PASSWORD]@db.[YOUR-PROJECT-REF].supabase.co:5432/postgres
parts = uri.split('@')
if len(parts) != 2:
raise ValueError("Invalid SUPABASE_URI format")
# Extract project reference from the host
host = parts[1].split(':')[0]
project_ref = host.split('.')[1]
# Construct Supabase URL and key
url = f"https://{project_ref}.supabase.co"
key = os.getenv('SUPABASE_KEY') # Still need the anon key for API access
if not key:
raise ValueError("SUPABASE_KEY environment variable is not set")
self.supabase: Client = create_client(url, key)
class BaseRepository:
def __init__(self):
self.db = PostgresConnection()
-45
View File
@@ -1,45 +0,0 @@
from typing import Optional, List
from .base import BaseRepository
from models.channel import DiscordChannel
class ChannelRepository(BaseRepository):
def __init__(self):
super().__init__()
self.table = self.db.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(**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, channel_id: int, server_id: int, channel_name: str) -> Optional[DiscordChannel]:
try:
channel = DiscordChannel(channel_id=channel_id, server_id=server_id, channel_name=channel_name)
response = self.table.insert(channel.dict()).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, channel_name: str) -> Optional[DiscordChannel]:
try:
response = self.table.update({'channel_name': channel_name}).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
-43
View File
@@ -1,43 +0,0 @@
from typing import Optional, List
from .base import BaseRepository
from models.channel_app import DiscordChannelApp
class ChannelAppRepository(BaseRepository):
def __init__(self):
super().__init__()
self.table = self.db.get_table('discord_channel_app')
async def get_channel_app(self, channel_id: int, app_name: str) -> Optional[DiscordChannelApp]:
try:
response = self.table.select('*').eq('channel_id', channel_id).eq('app_name', app_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_channel_apps(self, channel_id: int) -> List[DiscordChannelApp]:
try:
response = self.table.select('*').eq('channel_id', channel_id).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, channel_id: int, app_name: str) -> Optional[DiscordChannelApp]:
try:
app = DiscordChannelApp(channel_id=channel_id, app_name=app_name)
response = self.table.insert(app.dict()).execute()
return DiscordChannelApp(**response.data[0])
except Exception as e:
print(f"Error creating 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
-40
View File
@@ -1,40 +0,0 @@
from typing import Optional
from .base import BaseRepository
from models.server import DiscordServer
class ServerRepository(BaseRepository):
def __init__(self):
super().__init__()
self.table = self.db.get_table('discord_server')
async def get_server(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(**response.data[0])
return None
except Exception as e:
print(f"Error getting server: {e}")
return None
async def create_server(self, server_id: int, server_name: str) -> Optional[DiscordServer]:
"""Create new Discord server"""
try:
server = DiscordServer(server_id=server_id, server_name=server_name)
response = self.table.insert(server.dict()).execute()
return DiscordServer(**response.data[0])
except Exception as e:
print(f"Error creating server: {e}")
return None
async def update_server(self, server_id: int, server_name: str) -> Optional[DiscordServer]:
"""Update Discord server"""
try:
response = self.table.update({'server_name': server_name}).eq('server_id', server_id).execute()
if response.data:
return DiscordServer(**response.data[0])
return None
except Exception as e:
print(f"Error updating server: {e}")
return None
+3 -3
View File
@@ -1,11 +1,11 @@
import os
from dotenv import load_dotenv
from core.bot import bot
from core import events, tasks
from apps import experience, server
from core.bot import bot
from apps import home_debt, server
# Load environment variables
load_dotenv()
# Run the bot
bot.run(os.getenv('DISCORD_TOKEN'))
bot.run(os.getenv('BOT_TOKEN'))
+5 -4
View File
@@ -1,5 +1,6 @@
from .server import DiscordServer
from .channel import DiscordChannel
from .channel_app import DiscordChannelApp
from models.server import DiscordServer
from models.channel import DiscordChannel
from models.channel_app import DiscordChannelApp
from models.home_debt import DiscordHomeDebt
__all__ = ['DiscordServer', 'DiscordChannel', 'DiscordChannelApp']
__all__ = ['DiscordServer', 'DiscordChannel', 'DiscordChannelApp', 'DiscordHomeDebt']
-12
View File
@@ -1,12 +0,0 @@
from dataclasses import dataclass
from datetime import datetime
from typing import Optional
@dataclass
class DiscordDebtGroup:
id: Optional[int]
name: str
description: str
created_at: datetime
updated_at: datetime
members: List[DiscordHomeDebt]
+26
View File
@@ -0,0 +1,26 @@
from dataclasses import dataclass
from datetime import datetime
from typing import Optional
@dataclass
class DiscordHomeDebt:
id: Optional[int]
user_id: int
value: int
created_at: datetime
updated_at: datetime
def __str__(self):
return f"ID: {self.id}, User ID: {self.user_id}, Value: {self.value}, Created At: {self.created_at}, Updated At: {self.updated_at}"
def __repr__(self):
return self.__str__()
def to_dict(self):
return {
"id": self.id,
"user_id": self.user_id,
"value": self.value,
"created_at": self.created_at,
"updated_at": self.updated_at
}
+2 -2
View File
@@ -1,6 +1,6 @@
from .server import ServerRepository
from .channel import ChannelRepository
from .channel_app import ChannelAppRepository
from .user import UserRepository
from .home_debt import HomeDebtRepository
__all__ = ['ServerRepository', 'ChannelRepository', 'ChannelAppRepository', 'UserRepository']
__all__ = ['ServerRepository', 'ChannelRepository', 'ChannelAppRepository', 'HomeDebtRepository']
+44 -90
View File
@@ -1,103 +1,57 @@
from typing import List, Optional
from datetime import datetime
from models.debt_group import HomeMember, Expense, ExpenseShare
from models.home_debt import DiscordHomeDebt
from infra.db import postgres
class HomeDebtRepository:
_instance = None
_initialized = False
def __new__(cls):
if cls._instance is None:
cls._instance = super(HomeDebtRepository, cls).__new__(cls)
return cls._instance
def __init__(self):
if not HomeDebtRepository._initialized:
self.db = postgres.get_table('home_members')
HomeDebtRepository._initialized = True
self.table = postgres.get_table('discord_home_debt')
async def add_member(self, discord_user_id: int, name: str) -> HomeMember:
"""Thêm thành viên mới vào nhà"""
data = await self.db.table("home_members").insert({
"discord_user_id": discord_user_id,
"name": name
}).execute()
return HomeMember(**data.data[0])
async def get_member(self, discord_user_id: int) -> Optional[HomeMember]:
"""Lấy thông tin thành viên theo discord_user_id"""
data = await self.db.table("home_members").select("*").eq("discord_user_id", discord_user_id).execute()
if not data.data:
async def get(self, discord_user_id: int) -> Optional[DiscordHomeDebt]:
"""Lấy thông tin thành viên"""
try:
response = self.table.select('*').eq('user_id', discord_user_id).execute()
if response.data:
return DiscordHomeDebt(**response.data[0])
return None
return HomeMember(**data.data[0])
async def get_all_members(self) -> List[HomeMember]:
"""Lấy danh sách tất cả thành viên"""
data = await self.db.table("home_members").select("*").execute()
return [HomeMember(**member) for member in data.data]
async def add_expense(self, amount: float, description: str, paid_by: int) -> Expense:
"""Thêm khoản chi tiêu mới"""
# Tạo expense
expense_data = await self.db.table("expenses").insert({
"amount": amount,
"description": description,
"paid_by": paid_by
}).execute()
expense = Expense(**expense_data.data[0])
# Lấy danh sách thành viên
members = await self.get_all_members()
share_amount = amount / len(members)
# Tạo expense shares cho từng thành viên
for member in members:
await self.db.table("expense_shares").insert({
"expense_id": expense.id,
"member_id": member.discord_user_id,
"share_amount": share_amount,
"is_paid": member.discord_user_id == paid_by # Người trả tiền được đánh dấu là đã trả
}).execute()
return expense
async def get_expense(self, expense_id: int) -> Optional[Expense]:
"""Lấy thông tin khoản chi tiêu"""
data = await self.db.table("expenses").select("*").eq("id", expense_id).execute()
if not data.data:
except Exception as e:
print(f"Error getting user: {e}")
return None
return Expense(**data.data[0])
async def get_all_expenses(self) -> List[Expense]:
"""Lấy danh sách tất cả khoản chi tiêu"""
data = await self.db.table("expenses").select("*").order("created_at", desc=True).execute()
return [Expense(**expense) for expense in data.data]
async def get_expense_shares(self, expense_id: int) -> List[ExpenseShare]:
"""Lấy danh sách chia tiền của một khoản chi tiêu"""
data = await self.db.table("expense_shares").select("*").eq("expense_id", expense_id).execute()
return [ExpenseShare(**share) for share in data.data]
async def mark_share_as_paid(self, share_id: int) -> Optional[ExpenseShare]:
"""Đánh dấu một phần chia tiền đã được trả"""
data = await self.db.table("expense_shares").update({
"is_paid": True
}).eq("id", share_id).execute()
if not data.data:
async def get_other(self, discord_user_id: int) -> Optional[DiscordHomeDebt]:
"""Lấy thông tin thành viên khác"""
try:
response = self.table.select('*').neq('user_id', discord_user_id).execute()
if response.data:
return DiscordHomeDebt(**response.data[0])
return None
except Exception as e:
print(f"Error getting other member: {e}")
return None
return ExpenseShare(**data.data[0])
async def get_member_balance(self, discord_user_id: int) -> float:
"""Tính số dư của một thành viên (số tiền phải trả - số tiền đã trả)"""
# Lấy tất cả expense shares của thành viên
data = await self.db.table("expense_shares").select("*").eq("member_id", discord_user_id).execute()
shares = [ExpenseShare(**share) for share in data.data]
async def create_home_debt(self, user_id: int, value: int) -> Optional[DiscordHomeDebt]:
"""Tạo mới khoản nợ"""
try:
home_debt = DiscordHomeDebt(user_id=user_id, value=value)
response = self.table.insert(home_debt.dict(exclude_none=True)).execute()
return DiscordHomeDebt(**response.data[0])
except Exception as e:
print(f"Error creating home debt: {e}")
return None
# Tính tổng số tiền phải trả
total_owed = sum(share.share_amount for share in shares)
async def update_home_debt(self, home_debt: DiscordHomeDebt) -> Optional[DiscordHomeDebt]:
"""Cập nhật khoản nợ"""
try:
response = self.table.update(home_debt.dict(exclude_none=True)).eq('user_id', home_debt.user_id).execute()
return DiscordHomeDebt(**response.data[0])
except Exception as e:
print(f"Error updating home debt: {e}")
return None
# Tính tổng số tiền đã trả
total_paid = sum(share.share_amount for share in shares if share.is_paid)
return total_owed - total_paid
async def get_all(self) -> List[DiscordHomeDebt]:
"""Lấy tất cả khoản nợ"""
try:
response = self.table.select('*').execute()
return [DiscordHomeDebt(**home_debt) for home_debt in response.data]
except Exception as e:
print(f"Error getting all home debts: {e}")
return []
-47
View File
@@ -1,47 +0,0 @@
from typing import Optional
from infra.db import postgres
class UserRepository:
def __init__(self):
self.exp_table = postgres.get_table('user_exp')
self.voice_table = postgres.get_table('voice_time')
async def get_exp(self, user_id: int) -> int:
"""Get user's experience points from database"""
try:
response = self.exp_table.select('exp_points').eq('user_id', user_id).execute()
if response.data:
return response.data[0]['exp_points']
return 0
except Exception as e:
print(f"Error getting user exp: {e}")
return 0
async def update_exp(self, user_id: int, exp_points: int) -> bool:
"""Update user's experience points in database"""
try:
response = self.exp_table.upsert({'user_id': user_id, 'exp_points': exp_points}).execute()
return len(response.data) > 0
except Exception as e:
print(f"Error updating user exp: {e}")
return False
async def get_voice_time(self, user_id: int) -> Optional[str]:
"""Get user's voice time from database"""
try:
response = self.voice_table.select('last_join_time').eq('user_id', user_id).execute()
if response.data:
return response.data[0]['last_join_time']
return None
except Exception as e:
print(f"Error getting user voice time: {e}")
return None
async def update_voice_time(self, user_id: int, join_time: str) -> bool:
"""Update user's voice time in database"""
try:
response = self.voice_table.upsert({'user_id': user_id, 'last_join_time': join_time}).execute()
return len(response.data) > 0
except Exception as e:
print(f"Error updating user voice time: {e}")
return False