adding nối từ game

This commit is contained in:
2025-06-23 17:01:31 +07:00
parent 300807e26a
commit 537be0c4ab
15 changed files with 752 additions and 32 deletions
+3
View File
@@ -0,0 +1,3 @@
{
"agentCanUpdateSnapshot": true
}
+79
View File
@@ -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
+20
View File
@@ -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}")
-8
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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}")
-5
View File
@@ -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!")
+1 -1
View File
@@ -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
View File
@@ -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'
]
+24
View File
@@ -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
}
+19
View File
@@ -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 -1
View File
@@ -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']
+111
View File
@@ -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 []
+86
View File
@@ -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)