add dockerfile and release first version
This commit is contained in:
@@ -0,0 +1,65 @@
|
|||||||
|
# Git
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
.Python
|
||||||
|
env
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
.tox
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.log
|
||||||
|
.git
|
||||||
|
.mypy_cache
|
||||||
|
.pytest_cache
|
||||||
|
.hypothesis
|
||||||
|
|
||||||
|
# Virtual environments
|
||||||
|
.venv
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env/
|
||||||
|
.venv/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
README.md
|
||||||
|
LICENSE
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# Environment variables
|
||||||
|
.env
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
FROM python:3.9-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN pip install uv
|
||||||
|
|
||||||
|
COPY pyproject.toml uv.lock ./
|
||||||
|
|
||||||
|
RUN uv sync --frozen
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN useradd --create-home --shell /bin/bash app && \
|
||||||
|
chown -R app:app /app
|
||||||
|
USER app
|
||||||
|
|
||||||
|
CMD ["uv", "run", "python", "main.py"]
|
||||||
@@ -1 +1,104 @@
|
|||||||
# virtus-bot
|
# Virtus Bot
|
||||||
|
|
||||||
|
Một Discord bot để quản lý điểm kinh nghiệm người dùng (user experience points).
|
||||||
|
|
||||||
|
## Tính năng
|
||||||
|
|
||||||
|
- Quản lý điểm kinh nghiệm người dùng
|
||||||
|
- Tích hợp với Supabase database
|
||||||
|
- Hệ thống sự kiện và tác vụ tự động
|
||||||
|
|
||||||
|
## Yêu cầu hệ thống
|
||||||
|
|
||||||
|
- Python 3.9+
|
||||||
|
- uv package manager
|
||||||
|
- Discord Bot Token
|
||||||
|
- Supabase credentials
|
||||||
|
|
||||||
|
## Cài đặt và chạy với Docker
|
||||||
|
|
||||||
|
### 1. Clone repository
|
||||||
|
```bash
|
||||||
|
git clone <repository-url>
|
||||||
|
cd virtus-bot
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Tạo file .env
|
||||||
|
Tạo file `.env` với các biến môi trường cần thiết:
|
||||||
|
```env
|
||||||
|
BOT_TOKEN=your_discord_bot_token
|
||||||
|
SUPABASE_URL=your_supabase_url
|
||||||
|
SUPABASE_KEY=your_supabase_key
|
||||||
|
DATABASE_URL=your_database_url
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Build và chạy với Docker Compose
|
||||||
|
```bash
|
||||||
|
# Build image
|
||||||
|
docker-compose build
|
||||||
|
|
||||||
|
# Chạy bot
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# Xem logs
|
||||||
|
docker-compose logs -f
|
||||||
|
|
||||||
|
# Dừng bot
|
||||||
|
docker-compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Chạy với Docker trực tiếp
|
||||||
|
```bash
|
||||||
|
# Build image
|
||||||
|
docker build -t virtus-bot .
|
||||||
|
|
||||||
|
# Chạy container
|
||||||
|
docker run -d \
|
||||||
|
--name virtus-bot \
|
||||||
|
--env-file .env \
|
||||||
|
--restart unless-stopped \
|
||||||
|
virtus-bot
|
||||||
|
```
|
||||||
|
|
||||||
|
## Phát triển local
|
||||||
|
|
||||||
|
### 1. Cài đặt dependencies
|
||||||
|
```bash
|
||||||
|
# Cài đặt uv
|
||||||
|
pip install uv
|
||||||
|
|
||||||
|
# Sync dependencies
|
||||||
|
uv sync
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Chạy bot
|
||||||
|
```bash
|
||||||
|
uv run python main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cấu trúc dự án
|
||||||
|
|
||||||
|
```
|
||||||
|
virtus-bot/
|
||||||
|
├── apps/ # Các ứng dụng Discord commands
|
||||||
|
├── core/ # Core bot functionality
|
||||||
|
├── models/ # Database models
|
||||||
|
├── repositories/ # Data access layer
|
||||||
|
├── utils/ # Utility functions
|
||||||
|
├── infra/ # Infrastructure code
|
||||||
|
├── main.py # Entry point
|
||||||
|
├── pyproject.toml # Project configuration
|
||||||
|
└── uv.lock # Locked dependencies
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- discord.py >= 2.3.2
|
||||||
|
- python-dotenv >= 1.0.0
|
||||||
|
- sqlalchemy >= 2.0.0
|
||||||
|
- supabase >= 2.3.0
|
||||||
|
- asyncpg >= 0.29.0
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Xem file [LICENSE](LICENSE) để biết thêm chi tiết.
|
||||||
+82
-33
@@ -1,7 +1,7 @@
|
|||||||
import discord
|
import discord
|
||||||
from core.bot import bot, server_repo, channel_repo, home_debt_repo
|
from core.bot import bot, server_repo, channel_repo, home_debt_repo
|
||||||
|
from utils.common import format_vnd
|
||||||
|
|
||||||
@discord.app_commands.guilds(discord.Object(id=536422615649091595))
|
|
||||||
@bot.tree.command(name='set-home-debt', description='Set channel to home debt')
|
@bot.tree.command(name='set-home-debt', description='Set channel to home debt')
|
||||||
async def set_home_debt(interaction: discord.Interaction, channel: discord.TextChannel):
|
async def set_home_debt(interaction: discord.Interaction, channel: discord.TextChannel):
|
||||||
try:
|
try:
|
||||||
@@ -10,10 +10,12 @@ async def set_home_debt(interaction: discord.Interaction, channel: discord.TextC
|
|||||||
await interaction.response.send_message("Bạn cần có quyền Administrator để sử dụng lệnh này!", ephemeral=True)
|
await interaction.response.send_message("Bạn cần có quyền Administrator để sử dụng lệnh này!", ephemeral=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
await interaction.response.defer(ephemeral=False)
|
||||||
|
|
||||||
# Ensure server is in database
|
# Ensure server is in database
|
||||||
server = await server_repo.get(str(interaction.guild_id))
|
server = await server_repo.get(str(interaction.guild_id))
|
||||||
if not server:
|
if not 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)
|
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
|
return
|
||||||
|
|
||||||
# Ensure channel was registered in database
|
# Ensure channel was registered in database
|
||||||
@@ -23,75 +25,122 @@ async def set_home_debt(interaction: discord.Interaction, channel: discord.TextC
|
|||||||
else:
|
else:
|
||||||
await channel_repo.update_channel(channel.id, 'home_debt')
|
await channel_repo.update_channel(channel.id, 'home_debt')
|
||||||
|
|
||||||
await interaction.response.send_message(f"home_debt app đã được cấu hình cho {channel.mention}")
|
await interaction.followup.send(f"home_debt app đã được cấu hình cho {channel.mention}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await interaction.response.send_message(f"Lỗi khi cấu hình home_debt app: {str(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)
|
||||||
|
|
||||||
# Decorator to check if the channel is registered and configured for home_debt app
|
# Check if the channel is registered and configured for home_debt app
|
||||||
def check_channel_home_debt():
|
async def check_channel_home_debt(channel_id: int) -> bool:
|
||||||
def decorator(func):
|
# Check if channel is registered in database
|
||||||
async def wrapper(interaction: discord.Interaction, *args, **kwargs):
|
channel = await channel_repo.get_channel(channel_id)
|
||||||
channel = await channel_repo.get_channel(interaction.channel_id)
|
if not channel or channel.app != 'home_debt':
|
||||||
if not channel:
|
return False
|
||||||
await interaction.response.send_message("Channel chưa được đăng ký!", ephemeral=True)
|
|
||||||
return
|
return True
|
||||||
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
|
|
||||||
|
|
||||||
@bot.tree.command(name="home-debt-add", description="Thêm khoản chi tiêu mới")
|
@bot.tree.command(name="home-debt-add", description="Thêm khoản chi tiêu mới")
|
||||||
# @check_channel_home_debt()
|
async def home_debt_add(interaction: discord.Interaction, amount: int, description: str = "Không có lý do"):
|
||||||
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"""
|
"""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:
|
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 other user from home_debt table
|
# 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(interaction.user.id)
|
||||||
other_user.value += amount / 2
|
other_user.value += round(amount / 2)
|
||||||
await home_debt_repo.update_home_debt(other_user)
|
resp = 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)
|
if not resp:
|
||||||
|
await interaction.followup.send("Có lỗi xảy ra khi cập nhật dữ liệu", ephemeral=True)
|
||||||
|
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)
|
||||||
except Exception as e:
|
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 interaction.response.send_message(f"Có lỗi xảy ra: {str(e)}", ephemeral=True)
|
||||||
|
|
||||||
@discord.app_commands.guilds(discord.Object(id=536422615649091595))
|
|
||||||
@bot.tree.command(name="home-debt-check", description="Kiểm tra số dư của bạn")
|
@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):
|
async def home_debt_check(interaction: discord.Interaction):
|
||||||
"""Kiểm tra số dư của mọi người"""
|
"""Kiểm tra số dư của mọi người"""
|
||||||
try:
|
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)
|
||||||
# Lấy số dư của mọi người
|
# Lấy số dư của mọi người
|
||||||
home_debts = await home_debt_repo.get_all()
|
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)
|
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:
|
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 interaction.response.send_message(f"Có lỗi xảy ra: {str(e)}", ephemeral=True)
|
||||||
|
|
||||||
@discord.app_commands.guilds(discord.Object(id=536422615649091595))
|
|
||||||
@bot.tree.command(name="vay-debt", description="Vay nợ")
|
@bot.tree.command(name="vay-debt", description="Vay nợ")
|
||||||
async def vay_debt(interaction: discord.Interaction, amount: float, description: str):
|
async def vay_debt(interaction: discord.Interaction, amount: int, description: str = "Không có lý do"):
|
||||||
"""Vay nợ"""
|
"""Vay nợ"""
|
||||||
try:
|
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
|
# Get info user from home_debt table
|
||||||
user = await home_debt_repo.get(interaction.user.id)
|
user = await home_debt_repo.get(interaction.user.id)
|
||||||
user.value += amount
|
user.value += amount
|
||||||
await home_debt_repo.update_home_debt(user)
|
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)
|
||||||
|
return
|
||||||
|
|
||||||
# Send message to user
|
# Send message to user
|
||||||
await interaction.response.send_message(f"Đã vay nợ: {description} - {amount}đ cho {user.user_id}", ephemeral=False)
|
await interaction.followup.send(f"Đã vay {format_vnd(amount * 1000)} bởi {interaction.user.name}", ephemeral=False)
|
||||||
except Exception as e:
|
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 interaction.response.send_message(f"Có lỗi xảy ra: {str(e)}", ephemeral=True)
|
||||||
|
|
||||||
@discord.app_commands.guilds(discord.Object(id=536422615649091595))
|
|
||||||
@bot.tree.command(name="tra-debt", description="Trả nợ")
|
@bot.tree.command(name="tra-debt", description="Trả nợ")
|
||||||
async def tra_debt(interaction: discord.Interaction, amount: float):
|
async def tra_debt(interaction: discord.Interaction, amount: int):
|
||||||
"""Trả nợ"""
|
"""Trả nợ"""
|
||||||
try:
|
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
|
# Get info user from home_debt table
|
||||||
user = await home_debt_repo.get(interaction.user.id)
|
user = await home_debt_repo.get(interaction.user.id)
|
||||||
user.value -= amount
|
user.value -= amount
|
||||||
await home_debt_repo.update_home_debt(user)
|
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)
|
||||||
|
return
|
||||||
|
|
||||||
# Send message to user
|
# Send message to user
|
||||||
await interaction.response.send_message(f"Đã trả nợ: {amount}đ cho {user.user_id}", ephemeral=False)
|
await interaction.followup.send(f"Đã trả {format_vnd(amount * 1000)} từ {interaction.user.name}", ephemeral=False)
|
||||||
except Exception as e:
|
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 interaction.response.send_message(f"Có lỗi xảy ra: {str(e)}", ephemeral=True)
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
virtus-bot:
|
||||||
|
build: .
|
||||||
|
container_name: virtus-bot
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- BOT_TOKEN=${BOT_TOKEN}
|
||||||
|
- SUPABASE_URL=${SUPABASE_URL}
|
||||||
|
- SUPABASE_KEY=${SUPABASE_KEY}
|
||||||
|
- DATABASE_URL=${DATABASE_URL}
|
||||||
|
volumes:
|
||||||
|
# Mount logs directory nếu cần
|
||||||
|
- ./logs:/app/logs
|
||||||
|
# Không cần expose port vì Discord bot không cần HTTP server
|
||||||
|
# ports:
|
||||||
|
# - "8000:8000"
|
||||||
@@ -9,3 +9,13 @@ class DiscordChannel(BaseModel):
|
|||||||
server_id: int
|
server_id: int
|
||||||
channel_id: int
|
channel_id: int
|
||||||
app: str
|
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
|
||||||
|
}
|
||||||
@@ -17,3 +17,6 @@ class DiscordServer(BaseModel):
|
|||||||
'server_id': self.server_id,
|
'server_id': self.server_id,
|
||||||
'name': self.name
|
'name': self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def to_json(self):
|
||||||
|
return self.model_dump_json(exclude_none=True)
|
||||||
@@ -10,7 +10,7 @@ class ChannelRepository:
|
|||||||
try:
|
try:
|
||||||
response = self.table.select('*').eq('channel_id', channel_id).execute()
|
response = self.table.select('*').eq('channel_id', channel_id).execute()
|
||||||
if response.data:
|
if response.data:
|
||||||
return DiscordChannel(**response.data[0])
|
return DiscordChannel.model_validate(response.data[0])
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error getting channel: {e}")
|
print(f"Error getting channel: {e}")
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class HomeDebtRepository:
|
|||||||
async def update_home_debt(self, home_debt: DiscordHomeDebt) -> Optional[DiscordHomeDebt]:
|
async def update_home_debt(self, home_debt: DiscordHomeDebt) -> Optional[DiscordHomeDebt]:
|
||||||
"""Cập nhật khoản nợ"""
|
"""Cập nhật khoản nợ"""
|
||||||
try:
|
try:
|
||||||
response = self.table.update(home_debt.dict(exclude_none=True)).eq('user_id', home_debt.user_id).execute()
|
response = self.table.update(home_debt.to_dict()).eq('user_id', home_debt.user_id).execute()
|
||||||
return DiscordHomeDebt(**response.data[0])
|
return DiscordHomeDebt(**response.data[0])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error updating home debt: {e}")
|
print(f"Error updating home debt: {e}")
|
||||||
|
|||||||
@@ -6,33 +6,33 @@ class ServerRepository:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.table = postgres.get_table('discord_server')
|
self.table = postgres.get_table('discord_server')
|
||||||
|
|
||||||
async def get_server(self, server_id: int) -> Optional[DiscordServer]:
|
async def get(self, server_id: int) -> Optional[DiscordServer]:
|
||||||
"""Get Discord server by ID"""
|
"""Get Discord server by ID"""
|
||||||
try:
|
try:
|
||||||
response = self.table.select('*').eq('server_id', server_id).execute()
|
response = self.table.select('*').eq('server_id', server_id).execute()
|
||||||
if response.data:
|
if response.data:
|
||||||
return DiscordServer(**response.data[0])
|
return DiscordServer.model_validate(response.data[0])
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error getting server: {e}")
|
print(f"Error getting server: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def create_server(self, server_id: int, name: str) -> Optional[DiscordServer]:
|
async def create(self, server_id: int, name: str) -> Optional[DiscordServer]:
|
||||||
"""Create new Discord server"""
|
"""Create new Discord server"""
|
||||||
try:
|
try:
|
||||||
server = DiscordServer(server_id=server_id, name=name)
|
server = DiscordServer(server_id=server_id, name=name)
|
||||||
response = self.table.insert(server.dict(exclude_none=True)).execute()
|
response = self.table.insert(server.to_dict()).execute()
|
||||||
return DiscordServer(**response.data[0])
|
return DiscordServer.model_validate(response.data[0])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error creating server: {e}")
|
print(f"Error creating server: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def update_server(self, server_id: int, name: str) -> Optional[DiscordServer]:
|
async def update(self, server_id: int, name: str) -> Optional[DiscordServer]:
|
||||||
"""Update Discord server"""
|
"""Update Discord server"""
|
||||||
try:
|
try:
|
||||||
response = self.table.update({'name': name}).eq('server_id', server_id).execute()
|
response = self.table.update({'name': name}).eq('server_id', server_id).execute()
|
||||||
if response.data:
|
if response.data:
|
||||||
return DiscordServer(**response.data[0])
|
return DiscordServer.model_validate(response.data[0])
|
||||||
return None
|
return None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error updating server: {e}")
|
print(f"Error updating server: {e}")
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
def format_vnd(amount: int) -> str:
|
||||||
|
return f"{amount:,.0f}đ".replace(",", ".")
|
||||||
Reference in New Issue
Block a user