adding nối từ game
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"agentCanUpdateSnapshot": true
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
# Trò Chơi Nối Từ - Discord Bot
|
||||
|
||||
## Mô tả
|
||||
Trò chơi nối từ là một mini-game trong Discord bot, cho phép người chơi nối các từ có 2 chữ cái theo quy tắc: chữ cái đầu của từ mới phải trùng với chữ cái cuối của từ trước.
|
||||
|
||||
## Tính năng
|
||||
|
||||
### 🎮 Game Commands
|
||||
- `!start` - Bắt đầu trò chơi nối từ
|
||||
- `!end` - Kết thúc trò chơi
|
||||
|
||||
### 👑 Admin Commands
|
||||
- `!add <từ> [nghĩa]` - Thêm từ mới vào từ điển (chỉ admin)
|
||||
- `!remove <từ>` - Xóa từ khỏi từ điển (chỉ admin)
|
||||
|
||||
## Luật chơi
|
||||
|
||||
1. **Từ hợp lệ**: Mỗi từ phải có đúng 2 chữ cái
|
||||
2. **Quy tắc nối**: Chữ cái đầu của từ mới phải trùng với chữ cái cuối của từ trước
|
||||
3. **Không lặp lại**: Không được sử dụng từ đã được nêu trước đó
|
||||
4. **Thời gian**: Mỗi lượt có tối đa 30 giây để trả lời
|
||||
5. **Từ điển**: Từ phải tồn tại trong cơ sở dữ liệu
|
||||
|
||||
## Cách chơi
|
||||
|
||||
1. Admin hoặc bất kỳ ai có thể bắt đầu game bằng lệnh `!start`
|
||||
2. Bot sẽ chọn một từ ngẫu nhiên để bắt đầu
|
||||
3. Người chơi gõ từ tiếp theo theo quy tắc nối từ
|
||||
4. Bot sẽ phản hồi:
|
||||
- ✅ Nếu từ hợp lệ
|
||||
- ❌ Nếu từ không hợp lệ
|
||||
5. Game tiếp tục cho đến khi hết thời gian hoặc không ai trả lời được
|
||||
|
||||
## Ví dụ
|
||||
|
||||
```
|
||||
Bot: Từ đầu tiên: "ma"
|
||||
User1: "an" ✅
|
||||
Bot: Từ tiếp theo phải bắt đầu bằng: "N"
|
||||
User2: "no" ✅
|
||||
Bot: Từ tiếp theo phải bắt đầu bằng: "O"
|
||||
User3: "oi" ✅
|
||||
```
|
||||
|
||||
## Cài đặt
|
||||
|
||||
### Environment Variables
|
||||
Thêm vào file `.env`:
|
||||
```
|
||||
CHANNEL_NOI_TU_ID=1234567890123456789
|
||||
```
|
||||
|
||||
### Database
|
||||
Cần có bảng `dictionary_vietnamese_two_words` với cấu trúc:
|
||||
- `id` (primary key)
|
||||
- `word` (varchar, unique)
|
||||
- `meaning` (varchar, nullable)
|
||||
|
||||
## Quản lý từ điển
|
||||
|
||||
### Thêm từ mới
|
||||
```
|
||||
!add ma mẹ
|
||||
!add an ăn
|
||||
!add no nói
|
||||
```
|
||||
|
||||
### Xóa từ
|
||||
```
|
||||
!remove ma
|
||||
!remove an
|
||||
```
|
||||
|
||||
## Lưu ý
|
||||
|
||||
- Chỉ hoạt động trong channel được chỉ định trong `CHANNEL_NOI_TU_ID`
|
||||
- Chỉ admin mới có thể thêm/xóa từ
|
||||
- Game tự động kết thúc sau 30 giây không có người trả lời
|
||||
- Tất cả từ phải có trong cơ sở dữ liệu để được chấp nhận
|
||||
@@ -0,0 +1,20 @@
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
from core.bot import bot, currency_repo
|
||||
|
||||
@bot.command(name="ncheck", description="Kiểm tra thông tin tài khoản của bạn")
|
||||
async def currency_check(ctx: commands.Context):
|
||||
"""Kiểm tra thông tin tài khoản của bạn"""
|
||||
# Check if user is registered in database. If not, create a new one
|
||||
currency = await currency_repo.get(ctx.author.id)
|
||||
if not currency:
|
||||
await ctx.send("Bạn chưa có tài khoản currency")
|
||||
await currency_repo.create(ctx.author.id, 0)
|
||||
currency = await currency_repo.get(ctx.author.id)
|
||||
|
||||
# Kiểm tra lại sau khi tạo
|
||||
if not currency:
|
||||
await ctx.send("Có lỗi xảy ra khi tạo tài khoản currency")
|
||||
return
|
||||
|
||||
await ctx.send(f"Thông tin tài khoản của bạn: {currency.balance}")
|
||||
@@ -1,8 +0,0 @@
|
||||
# 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!")
|
||||
+389
@@ -0,0 +1,389 @@
|
||||
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
|
||||
|
||||
# Lazy load repository để tránh lỗi database connection
|
||||
noi_tu_repo = None
|
||||
|
||||
def get_noi_tu_repo():
|
||||
"""Lazy load repository"""
|
||||
global noi_tu_repo
|
||||
if noi_tu_repo is None:
|
||||
from repositories.noi_tu import NoiTuRepository
|
||||
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):
|
||||
self.is_active = False
|
||||
self.current_word = ""
|
||||
self.used_words: Set[str] = set()
|
||||
self.last_player_id = None
|
||||
self.last_player_name = None # Thêm tên người chơi cuối
|
||||
self.last_message_time = None
|
||||
self.timeout_task = None
|
||||
self.channel = None
|
||||
self.timer_message = None # Tin nhắn hiển thị thời gian
|
||||
self.timer_task = None # Task cập nhật thời gian
|
||||
self.start_time = None # Thời gian bắt đầu game
|
||||
|
||||
# Khởi tạo game state
|
||||
game = NoiTuGame()
|
||||
|
||||
def is_admin(ctx):
|
||||
"""Kiểm tra xem user có phải là admin không"""
|
||||
return ctx.author.guild_permissions.administrator
|
||||
|
||||
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_NOI_TU_ID
|
||||
|
||||
def get_first_word(word: str) -> str:
|
||||
return word.strip().split()[0] if word else ''
|
||||
|
||||
def get_last_word(word: str) -> str:
|
||||
return word.strip().split()[-1] if word else ''
|
||||
|
||||
def format_time_remaining(seconds: int) -> str:
|
||||
"""Format thời gian còn lại"""
|
||||
if seconds <= 0:
|
||||
return "⏰ Hết thời gian!"
|
||||
return f"⏰ Còn lại: {seconds} giây"
|
||||
|
||||
async def update_timer_message():
|
||||
"""Cập nhật tin nhắn thời gian mỗi 1 giây"""
|
||||
start_time = game.last_message_time
|
||||
if not start_time:
|
||||
return
|
||||
|
||||
for remaining in range(30, -1, -1): # Đếm ngược từ 30 đến 0
|
||||
if not game.is_active or not game.timer_message:
|
||||
break
|
||||
|
||||
try:
|
||||
# Cập nhật embed
|
||||
embed = game.timer_message.embeds[0]
|
||||
embed.title = format_time_remaining(remaining)
|
||||
|
||||
# Thay đổi màu sắc theo thời gian
|
||||
if remaining <= 5:
|
||||
embed.color = discord.Color.red()
|
||||
elif remaining <= 10:
|
||||
embed.color = discord.Color.orange()
|
||||
elif remaining <= 20:
|
||||
embed.color = discord.Color.yellow()
|
||||
else:
|
||||
embed.color = discord.Color.blue()
|
||||
|
||||
await game.timer_message.edit(embed=embed)
|
||||
|
||||
# Dừng nếu hết thời gian
|
||||
if remaining <= 0:
|
||||
break
|
||||
|
||||
await asyncio.sleep(1) # Đợi đúng 1 giây
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error updating timer: {e}")
|
||||
break
|
||||
|
||||
@bot.command(name='start')
|
||||
async def start_game(ctx):
|
||||
"""Bắt đầu trò chơi nối từ"""
|
||||
if not is_correct_channel(ctx):
|
||||
return
|
||||
|
||||
if game.is_active:
|
||||
await ctx.send("❌ Trò chơi đã đang diễn ra!")
|
||||
return
|
||||
|
||||
# Lấy repository
|
||||
repo = get_noi_tu_repo()
|
||||
|
||||
# Lấy từ ngẫu nhiên để bắt đầu
|
||||
start_word = await repo.get_random_word()
|
||||
if not start_word:
|
||||
await ctx.send("❌ Không có từ nào trong cơ sở dữ liệu!")
|
||||
return
|
||||
|
||||
# Khởi tạo game
|
||||
game.is_active = True
|
||||
game.current_word = start_word
|
||||
game.used_words = {start_word}
|
||||
game.last_player_id = None
|
||||
game.last_player_name = None
|
||||
game.channel = ctx.channel
|
||||
game.last_message_time = datetime.now()
|
||||
game.start_time = datetime.now()
|
||||
|
||||
# Tạo timeout task
|
||||
game.timeout_task = asyncio.create_task(game_timeout())
|
||||
|
||||
embed = discord.Embed(
|
||||
title="🎮 Trò chơi Nối Từ đã bắt đầu!",
|
||||
description=f"Từ đầu tiên: **{start_word}**\n\n"
|
||||
f"📝 **Luật chơi:**\n"
|
||||
f"• Mỗi từ gồm 2 từ ghép tiếng Việt (VD: 'âm cao', 'cao độ')\n"
|
||||
f"• Từ đầu của từ mới phải trùng với từ cuối của từ trước\n"
|
||||
f"• Không được lặp lại từ đã dùng\n"
|
||||
f"• Thời gian trả lời tối đa: 30 giây\n\n"
|
||||
f"⏰ Thời gian bắt đầu: {datetime.now().strftime('%H:%M:%S')}",
|
||||
color=discord.Color.green()
|
||||
)
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@bot.command(name='end')
|
||||
async def end_game(ctx):
|
||||
"""Kết thúc trò chơi nối từ"""
|
||||
if not is_correct_channel(ctx):
|
||||
return
|
||||
|
||||
if not game.is_active:
|
||||
await ctx.send("❌ Không có trò chơi nào đang diễn ra!")
|
||||
return
|
||||
|
||||
# Dừng các task
|
||||
if game.timeout_task:
|
||||
game.timeout_task.cancel()
|
||||
if game.timer_task:
|
||||
game.timer_task.cancel()
|
||||
|
||||
# Tính thời gian chơi
|
||||
game_duration = ""
|
||||
if game.start_time:
|
||||
duration = datetime.now() - game.start_time
|
||||
minutes = int(duration.total_seconds() // 60)
|
||||
seconds = int(duration.total_seconds() % 60)
|
||||
game_duration = f"{minutes} phút {seconds} giây"
|
||||
|
||||
# Tạo thông báo kết thúc
|
||||
embed = discord.Embed(
|
||||
title="🏁 Trò chơi Nối Từ đã kết thúc!",
|
||||
description=f"📊 **Thống kê:**\n"
|
||||
f"• Số từ đã sử dụng: {len(game.used_words)}\n"
|
||||
f"• Từ cuối cùng: {game.current_word if game.current_word else 'N/A'}\n"
|
||||
f"• Thời gian chơi: {game_duration}",
|
||||
color=discord.Color.red()
|
||||
)
|
||||
|
||||
# Thêm thông tin người chiến thắng
|
||||
if game.last_player_name:
|
||||
embed.add_field(
|
||||
name="👑 Người chiến thắng",
|
||||
value=f"**{game.last_player_name}** - Từ cuối: **{game.current_word}**",
|
||||
inline=False
|
||||
)
|
||||
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
# Reset game state
|
||||
game.is_active = False
|
||||
game.current_word = ""
|
||||
game.used_words.clear()
|
||||
game.last_player_id = None
|
||||
game.last_player_name = None
|
||||
game.channel = None
|
||||
game.last_message_time = None
|
||||
game.timeout_task = None
|
||||
game.timer_message = None
|
||||
game.timer_task = None
|
||||
game.start_time = None
|
||||
|
||||
@bot.command(name='add')
|
||||
async def add_word(ctx, *, word: str, meaning: str = None):
|
||||
"""Thêm từ mới vào cơ sở dữ liệu (chỉ admin)"""
|
||||
if not is_correct_channel(ctx):
|
||||
return
|
||||
|
||||
if not is_admin(ctx):
|
||||
await ctx.send("❌ Chỉ admin mới có thể thêm từ!")
|
||||
return
|
||||
|
||||
# Kiểm tra từ có hợp lệ không
|
||||
if not await get_noi_tu_repo().is_valid_word(word):
|
||||
await ctx.send("❌ Từ phải có đúng 2 từ ghép!")
|
||||
return
|
||||
|
||||
# Kiểm tra từ đã tồn tại chưa
|
||||
if await get_noi_tu_repo().is_exist(word):
|
||||
await ctx.send(f"❌ Từ '{word}' đã tồn tại trong cơ sở dữ liệu!")
|
||||
return
|
||||
|
||||
# Thêm từ
|
||||
success = await get_noi_tu_repo().add(word, meaning)
|
||||
if success:
|
||||
embed = discord.Embed(
|
||||
title="✅ Thêm từ thành công!",
|
||||
description=f"Từ: **{word}**\n"
|
||||
f"Nghĩa: {meaning if meaning else 'Không có'}",
|
||||
color=discord.Color.green()
|
||||
)
|
||||
await ctx.send(embed=embed)
|
||||
else:
|
||||
await ctx.send("❌ Có lỗi xảy ra khi thêm từ!")
|
||||
|
||||
@bot.command(name='remove')
|
||||
async def remove_word(ctx, *, word: str):
|
||||
"""Xóa từ khỏi cơ sở dữ liệu (chỉ admin)"""
|
||||
if not is_correct_channel(ctx):
|
||||
return
|
||||
|
||||
if not is_admin(ctx):
|
||||
await ctx.send("❌ Chỉ admin mới có thể xóa từ!")
|
||||
return
|
||||
|
||||
# Kiểm tra từ có tồn tại không
|
||||
if not await get_noi_tu_repo().is_exist(word):
|
||||
await ctx.send(f"❌ Từ '{word}' không tồn tại trong cơ sở dữ liệu!")
|
||||
return
|
||||
|
||||
# Xóa từ
|
||||
success = await get_noi_tu_repo().remove(word)
|
||||
if success:
|
||||
embed = discord.Embed(
|
||||
title="✅ Xóa từ thành công!",
|
||||
description=f"Đã xóa từ: **{word}**",
|
||||
color=discord.Color.green()
|
||||
)
|
||||
await ctx.send(embed=embed)
|
||||
else:
|
||||
await ctx.send("❌ Có lỗi xảy ra khi xóa từ!")
|
||||
|
||||
async def game_timeout():
|
||||
"""Xử lý timeout của game"""
|
||||
try:
|
||||
# Đợi đúng 30 giây
|
||||
for i in range(30):
|
||||
if not game.is_active:
|
||||
return
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# Kiểm tra lại trước khi timeout
|
||||
if game.is_active and game.last_message_time:
|
||||
time_diff = datetime.now() - game.last_message_time
|
||||
if time_diff.total_seconds() >= 30:
|
||||
# Game timeout
|
||||
embed = discord.Embed(
|
||||
title="⏰ Hết thời gian!",
|
||||
description=f"Không ai trả lời trong 30 giây.\n"
|
||||
f"Từ cuối cùng: **{game.current_word}**\n"
|
||||
f"Trò chơi kết thúc!",
|
||||
color=discord.Color.orange()
|
||||
)
|
||||
|
||||
# Thêm thông tin người chiến thắng
|
||||
if game.last_player_name:
|
||||
embed.add_field(
|
||||
name="👑 Người chiến thắng",
|
||||
value=f"**{game.last_player_name}** - Từ cuối: **{game.current_word}**",
|
||||
inline=False
|
||||
)
|
||||
|
||||
if game.channel:
|
||||
await game.channel.send(embed=embed)
|
||||
|
||||
# Reset game
|
||||
game.is_active = False
|
||||
game.current_word = ""
|
||||
game.used_words.clear()
|
||||
game.last_player_id = None
|
||||
game.last_player_name = None
|
||||
game.channel = None
|
||||
game.last_message_time = None
|
||||
game.timeout_task = None
|
||||
game.timer_message = None
|
||||
game.timer_task = None
|
||||
game.start_time = None
|
||||
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
|
||||
@bot.listen('on_message')
|
||||
async def handle_game_message(message):
|
||||
"""Xử lý tin nhắn trong game"""
|
||||
# Bỏ qua tin nhắn từ bot
|
||||
if message.author.bot:
|
||||
return
|
||||
|
||||
# Chỉ xử lý trong channel được chỉ định
|
||||
if message.channel.id != CHANNEL_NOI_TU_ID:
|
||||
return
|
||||
|
||||
# Nếu game không active, bỏ qua
|
||||
if not game.is_active:
|
||||
return
|
||||
|
||||
# Kiểm tra xem tin nhắn có phải là từ không
|
||||
word = message.content.strip().lower()
|
||||
|
||||
# Kiểm tra từ có hợp lệ không
|
||||
if not await get_noi_tu_repo().is_valid_word(word):
|
||||
return
|
||||
|
||||
# Kiểm tra người vừa trả lời có trả lời tiếp không
|
||||
if game.last_player_id == message.author.id:
|
||||
await message.add_reaction('❌')
|
||||
await message.channel.send(f"❌ **{message.author.display_name}**, hãy để người khác trả lời!")
|
||||
return
|
||||
|
||||
# Kiểm tra từ có tồn tại trong DB không
|
||||
if not await get_noi_tu_repo().is_exist(word):
|
||||
await message.add_reaction('❌')
|
||||
return
|
||||
|
||||
# Kiểm tra từ đã được sử dụng chưa
|
||||
if word in game.used_words:
|
||||
await message.add_reaction('❌')
|
||||
await message.channel.send(f"❌ Từ '{word}' đã được sử dụng!")
|
||||
return
|
||||
|
||||
# Kiểm tra quy tắc nối từ ghép
|
||||
if game.current_word:
|
||||
last = get_last_word(game.current_word)
|
||||
first = get_first_word(word)
|
||||
if first != last:
|
||||
await message.add_reaction('❌')
|
||||
await message.channel.send(f"❌ Từ mới phải bắt đầu bằng từ: '{last.upper()}'!")
|
||||
return
|
||||
|
||||
# Từ hợp lệ
|
||||
await message.add_reaction('✅')
|
||||
|
||||
# Cập nhật game state
|
||||
game.current_word = word
|
||||
game.used_words.add(word)
|
||||
game.last_player_id = message.author.id
|
||||
game.last_player_name = message.author.display_name # Lưu tên người chơi
|
||||
game.last_message_time = datetime.now()
|
||||
|
||||
# Reset timeout
|
||||
if game.timeout_task:
|
||||
game.timeout_task.cancel()
|
||||
game.timeout_task = asyncio.create_task(game_timeout())
|
||||
|
||||
# Dừng timer task cũ nếu có
|
||||
if game.timer_task:
|
||||
game.timer_task.cancel()
|
||||
|
||||
# Thông báo từ tiếp theo
|
||||
next_hint = get_last_word(word).upper()
|
||||
embed = discord.Embed(
|
||||
title="⏰ Còn lại: 30 giây",
|
||||
color=discord.Color.blue()
|
||||
)
|
||||
|
||||
# Gửi tin nhắn mới và lưu reference
|
||||
game.timer_message = await message.channel.send(embed=embed)
|
||||
|
||||
# Bắt đầu task cập nhật thời gian
|
||||
game.timer_task = asyncio.create_task(update_timer_message())
|
||||
|
||||
+2
-1
@@ -16,12 +16,13 @@ intents.guilds = True
|
||||
bot = commands.Bot(command_prefix='!', intents=intents)
|
||||
|
||||
# Initialize repositories
|
||||
from repositories import ServerRepository, ChannelRepository, ChannelAppRepository, HomeDebtRepository
|
||||
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
|
||||
|
||||
+4
-13
@@ -1,21 +1,12 @@
|
||||
import discord
|
||||
from core.bot import bot
|
||||
from core.tasks import sync_commands
|
||||
# from core.tasks import sync_commands
|
||||
|
||||
# Using for sync commands to all guilds
|
||||
@bot.event
|
||||
async def on_ready():
|
||||
print(f'{bot.user} đã tham gia vào server!')
|
||||
await sync_commands.start()
|
||||
# await sync_commands.start()
|
||||
TEST_GUILD_ID = discord.Object(id=536422615649091595)
|
||||
await bot.tree.sync(guild=TEST_GUILD_ID)
|
||||
print("Đã đồng bộ lệnh!")
|
||||
|
||||
# @bot.event
|
||||
# async def on_ready():
|
||||
# guild_id = 536422615649091595
|
||||
# guild_obj = discord.Object(id=guild_id)
|
||||
|
||||
# try:
|
||||
# synced = await bot.tree.sync(guild=guild_obj)
|
||||
# print(f"🔁 Synced {len(synced)} command(s) to guild {guild_id}")
|
||||
# except Exception as e:
|
||||
# print(f"❌ Failed to sync commands to guild {guild_id}: {e}")
|
||||
@@ -15,8 +15,3 @@ from core.bot import bot
|
||||
# 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 sync_commands():
|
||||
await bot.tree.sync()
|
||||
print("Đã đồng bộ lệnh!")
|
||||
@@ -2,7 +2,7 @@ import os
|
||||
from dotenv import load_dotenv
|
||||
from core import events, tasks
|
||||
from core.bot import bot
|
||||
from apps import home_debt, server
|
||||
from apps import home_debt, server, currency, noi_tu
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
+10
-1
@@ -2,5 +2,14 @@ from models.server import DiscordServer
|
||||
from models.channel import DiscordChannel
|
||||
from models.channel_app import DiscordChannelApp
|
||||
from models.home_debt import DiscordHomeDebt
|
||||
from models.currency import DiscordCurrency
|
||||
from models.noi_tu import DiscordNoiTu
|
||||
|
||||
__all__ = ['DiscordServer', 'DiscordChannel', 'DiscordChannelApp', 'DiscordHomeDebt']
|
||||
__all__ = [
|
||||
'DiscordServer',
|
||||
'DiscordChannel',
|
||||
'DiscordChannelApp',
|
||||
'DiscordHomeDebt',
|
||||
'DiscordCurrency',
|
||||
'DiscordNoiTu'
|
||||
]
|
||||
@@ -0,0 +1,24 @@
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
@dataclass
|
||||
class DiscordCurrency:
|
||||
id: Optional[int]
|
||||
user_id: int
|
||||
balance: int
|
||||
updated_at: datetime
|
||||
|
||||
def __str__(self):
|
||||
return f"ID: {self.id}, User ID: {self.user_id}, Balance: {self.balance}, Updated At: {self.updated_at}"
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"id": self.id,
|
||||
"user_id": self.user_id,
|
||||
"balance": self.balance,
|
||||
"updated_at": self.updated_at
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
from pydantic import BaseModel
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
import json
|
||||
|
||||
class DiscordNoiTu(BaseModel):
|
||||
id: Optional[int] = None
|
||||
word: str
|
||||
meaning: Optional[str] = None
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
'id': self.id,
|
||||
'word': self.word,
|
||||
'meaning': self.meaning,
|
||||
}
|
||||
|
||||
def to_json(self):
|
||||
return json.dumps(self.to_dict())
|
||||
@@ -2,5 +2,6 @@ from .server import ServerRepository
|
||||
from .channel import ChannelRepository
|
||||
from .channel_app import ChannelAppRepository
|
||||
from .home_debt import HomeDebtRepository
|
||||
from .currency import CurrencyRepository
|
||||
|
||||
__all__ = ['ServerRepository', 'ChannelRepository', 'ChannelAppRepository', 'HomeDebtRepository']
|
||||
__all__ = ['ServerRepository', 'ChannelRepository', 'ChannelAppRepository', 'HomeDebtRepository', 'CurrencyRepository']
|
||||
@@ -0,0 +1,111 @@
|
||||
from typing import List, Optional
|
||||
from models.currency import DiscordCurrency
|
||||
from infra.db import postgres
|
||||
from datetime import datetime
|
||||
|
||||
class CurrencyRepository:
|
||||
def __init__(self):
|
||||
self.table = postgres.get_table('discord_currency')
|
||||
|
||||
async def get(self, user_id: int) -> Optional[DiscordCurrency]:
|
||||
"""Lấy thông tin thành viên"""
|
||||
try:
|
||||
response = self.table.select('*').eq('user_id', user_id).execute()
|
||||
if response.data:
|
||||
data = response.data[0]
|
||||
return DiscordCurrency(
|
||||
id=data.get('id'),
|
||||
user_id=data['user_id'],
|
||||
balance=data['balance'],
|
||||
updated_at=datetime.fromisoformat(data['updated_at'].replace('Z', '+00:00')) if data['updated_at'] else datetime.now()
|
||||
)
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Error getting user: {e}")
|
||||
return None
|
||||
|
||||
async def create(self, user_id: int, balance: int) -> Optional[DiscordCurrency]:
|
||||
"""Tạo thông tin thành viên"""
|
||||
try:
|
||||
response = self.table.insert({'user_id': user_id, 'balance': balance}).execute()
|
||||
if response.data:
|
||||
data = response.data[0]
|
||||
return DiscordCurrency(
|
||||
id=data.get('id'),
|
||||
user_id=data['user_id'],
|
||||
balance=data['balance'],
|
||||
updated_at=datetime.fromisoformat(data['updated_at'].replace('Z', '+00:00')) if data['updated_at'] else datetime.now()
|
||||
)
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Error creating user: {e}")
|
||||
return None
|
||||
|
||||
async def update(self, user_id: int, balance: int) -> Optional[DiscordCurrency]:
|
||||
"""Cập nhật thông tin thành viên"""
|
||||
try:
|
||||
response = self.table.update({'balance': balance}).eq('user_id', user_id).execute()
|
||||
if response.data:
|
||||
data = response.data[0]
|
||||
return DiscordCurrency(
|
||||
id=data.get('id'),
|
||||
user_id=data['user_id'],
|
||||
balance=data['balance'],
|
||||
updated_at=datetime.fromisoformat(data['updated_at'].replace('Z', '+00:00')) if data['updated_at'] else datetime.now()
|
||||
)
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Error updating user: {e}")
|
||||
return None
|
||||
|
||||
async def get_all(self) -> List[DiscordCurrency]:
|
||||
"""Lấy tất cả thông tin thành viên"""
|
||||
try:
|
||||
response = self.table.select('*').execute()
|
||||
currencies = []
|
||||
for data in response.data:
|
||||
currencies.append(DiscordCurrency(
|
||||
id=data.get('id'),
|
||||
user_id=data['user_id'],
|
||||
balance=data['balance'],
|
||||
updated_at=datetime.fromisoformat(data['updated_at'].replace('Z', '+00:00')) if data['updated_at'] else datetime.now()
|
||||
))
|
||||
return currencies
|
||||
except Exception as e:
|
||||
print(f"Error getting all users: {e}")
|
||||
return []
|
||||
|
||||
async def get_all_with_balance(self) -> List[DiscordCurrency]:
|
||||
"""Lấy tất cả thông tin thành viên và số dư"""
|
||||
try:
|
||||
response = self.table.select('*').execute()
|
||||
currencies = []
|
||||
for data in response.data:
|
||||
currencies.append(DiscordCurrency(
|
||||
id=data.get('id'),
|
||||
user_id=data['user_id'],
|
||||
balance=data['balance'],
|
||||
updated_at=datetime.fromisoformat(data['updated_at'].replace('Z', '+00:00')) if data['updated_at'] else datetime.now()
|
||||
))
|
||||
return currencies
|
||||
except Exception as e:
|
||||
print(f"Error getting all users with balance: {e}")
|
||||
return []
|
||||
|
||||
# Get all with sort by balance
|
||||
async def get_all_with_sort_by_balance(self) -> List[DiscordCurrency]:
|
||||
"""Lấy tất cả thông tin thành viên và sắp xếp theo số dư"""
|
||||
try:
|
||||
response = self.table.select('*').order('balance', desc=True).execute()
|
||||
currencies = []
|
||||
for data in response.data:
|
||||
currencies.append(DiscordCurrency(
|
||||
id=data.get('id'),
|
||||
user_id=data['user_id'],
|
||||
balance=data['balance'],
|
||||
updated_at=datetime.fromisoformat(data['updated_at'].replace('Z', '+00:00')) if data['updated_at'] else datetime.now()
|
||||
))
|
||||
return currencies
|
||||
except Exception as e:
|
||||
print(f"Error getting all users with sort by balance: {e}")
|
||||
return []
|
||||
@@ -0,0 +1,86 @@
|
||||
from typing import List, Optional
|
||||
from models.noi_tu import DiscordNoiTu
|
||||
from infra.db import postgres
|
||||
from datetime import datetime
|
||||
import random
|
||||
|
||||
class NoiTuRepository:
|
||||
def __init__(self):
|
||||
self.table = postgres.get_table('dictionary_vietnamese_two_words')
|
||||
|
||||
async def is_exist(self, word: str) -> bool:
|
||||
"""Kiểm tra xem từ có tồn tại trong bảng không"""
|
||||
try:
|
||||
word = word.strip()
|
||||
response = self.table.select('*').eq('word', word).execute()
|
||||
return len(response.data) > 0
|
||||
except Exception as e:
|
||||
print(f"Error checking if word exists: {e}")
|
||||
return False
|
||||
|
||||
async def add(self, word: str, meaning: Optional[str] = None) -> bool:
|
||||
"""Thêm từ vào bảng"""
|
||||
try:
|
||||
response = self.table.insert({
|
||||
'word': word,
|
||||
'meaning': meaning
|
||||
}).execute()
|
||||
return response.data is not None
|
||||
except Exception as e:
|
||||
print(f"Error adding word: {e}")
|
||||
return False
|
||||
|
||||
async def remove(self, word: str) -> bool:
|
||||
"""Xóa từ khỏi bảng"""
|
||||
try:
|
||||
response = self.table.delete().eq('word', word).execute()
|
||||
return response.data is not None
|
||||
except Exception as e:
|
||||
print(f"Error removing word: {e}")
|
||||
return False
|
||||
|
||||
async def get_random_word(self) -> Optional[str]:
|
||||
"""Lấy một từ ngẫu nhiên từ bảng"""
|
||||
try:
|
||||
response = self.table.select('word').execute()
|
||||
if response.data and len(response.data) > 0:
|
||||
# Lọc từ có đúng 2 từ ghép, mỗi từ chỉ gồm chữ cái
|
||||
words = []
|
||||
for item in response.data:
|
||||
word = item['word'].strip()
|
||||
parts = word.split()
|
||||
if len(parts) == 2 and all(part.isalpha() for part in parts):
|
||||
words.append(word)
|
||||
if words:
|
||||
return random.choice(words)
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Error getting random word: {e}")
|
||||
return None
|
||||
|
||||
async def get_all_words(self) -> List[str]:
|
||||
"""Lấy tất cả từ trong bảng"""
|
||||
try:
|
||||
response = self.table.select('word').execute()
|
||||
if response.data:
|
||||
# Lọc từ có đúng 2 từ ghép, mỗi từ chỉ gồm chữ cái
|
||||
words = []
|
||||
for item in response.data:
|
||||
word = item['word'].strip()
|
||||
parts = word.split()
|
||||
if len(parts) == 2 and all(part.isalpha() for part in parts):
|
||||
words.append(word)
|
||||
return words
|
||||
return []
|
||||
except Exception as e:
|
||||
print(f"Error getting all words: {e}")
|
||||
return []
|
||||
|
||||
async def is_valid_word(self, word: str) -> bool:
|
||||
"""Kiểm tra từ có hợp lệ không (2 từ ghép, mỗi từ chỉ gồm chữ cái)"""
|
||||
word = word.strip()
|
||||
parts = word.split()
|
||||
if len(parts) != 2:
|
||||
return False
|
||||
return all(part.isalpha() for part in parts)
|
||||
|
||||
Reference in New Issue
Block a user