allow game noi_tu for multiple server/channel
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
# Ví dụ cấu hình .env
|
||||
|
||||
## Format cơ bản
|
||||
|
||||
```bash
|
||||
# Bot Token
|
||||
BOT_TOKEN=your_bot_token_here
|
||||
|
||||
# Database Configuration
|
||||
POSTGRES_URL=postgresql+asyncpg://user:password@localhost:5432/database_name
|
||||
|
||||
# Channel IDs
|
||||
CHANNEL_HOME_DEBT_ID=1234567890123456789
|
||||
|
||||
# Game Nối Từ - Hỗ trợ nhiều channel với dấu phẩy
|
||||
CHANNEL_NOI_TU_ID=1383424686708363336,9876543210987654321,5555555555555555555
|
||||
```
|
||||
|
||||
## Các ví dụ khác nhau
|
||||
|
||||
### 1 channel
|
||||
```bash
|
||||
CHANNEL_NOI_TU_ID=1383424686708363336
|
||||
```
|
||||
|
||||
### 2 channel
|
||||
```bash
|
||||
CHANNEL_NOI_TU_ID=1383424686708363336,9876543210987654321
|
||||
```
|
||||
|
||||
### 3+ channel
|
||||
```bash
|
||||
CHANNEL_NOI_TU_ID=1383424686708363336,9876543210987654321,5555555555555555555
|
||||
```
|
||||
|
||||
## Lưu ý quan trọng
|
||||
|
||||
- ✅ **Đúng**: `CHANNEL_NOI_TU_ID=123,456,789`
|
||||
- ❌ **Sai**: `CHANNEL_NOI_TU_ID=123, 456, 789` (có khoảng trắng)
|
||||
- ❌ **Sai**: `CHANNEL_NOI_TU_ID="123,456,789"` (có dấu ngoặc kép)
|
||||
- ❌ **Sai**: `CHANNEL_NOI_TU_ID=123,abc,789` (có ký tự không phải số)
|
||||
|
||||
## Cách test
|
||||
|
||||
1. Cập nhật file `.env` với format mới
|
||||
2. Restart bot
|
||||
3. Kiểm tra log để xem bot có parse đúng channel IDs không
|
||||
4. Test `!start` trong từng channel để đảm bảo hoạt động
|
||||
@@ -0,0 +1,108 @@
|
||||
# Hướng dẫn sử dụng Bot với nhiều Channel
|
||||
|
||||
## Tổng quan
|
||||
|
||||
Bot đã được refactor để hỗ trợ chạy game nối từ trên nhiều channel đồng thời với cùng một bot token.
|
||||
|
||||
## Cấu hình Environment Variables
|
||||
|
||||
### Format đơn giản với dấu phẩy
|
||||
|
||||
```bash
|
||||
# Nhiều channel, phân cách bằng dấu phẩy (không có khoảng trắng)
|
||||
CHANNEL_NOI_TU_ID=1383424686708363336,9876543210987654321,5555555555555555555
|
||||
```
|
||||
|
||||
### Ví dụ thực tế
|
||||
|
||||
```bash
|
||||
# 1 channel
|
||||
CHANNEL_NOI_TU_ID=1383424686708363336
|
||||
|
||||
# 2 channel
|
||||
CHANNEL_NOI_TU_ID=1383424686708363336,9876543210987654321
|
||||
|
||||
# 3 channel
|
||||
CHANNEL_NOI_TU_ID=1383424686708363336,9876543210987654321,5555555555555555555
|
||||
```
|
||||
|
||||
### Lưu ý
|
||||
- Không có khoảng trắng xung quanh dấu phẩy
|
||||
- Mỗi ID phải là số nguyên hợp lệ
|
||||
- Bot sẽ tự động parse và hỗ trợ tất cả channel trong danh sách
|
||||
|
||||
## Cách hoạt động
|
||||
|
||||
### Game State riêng biệt
|
||||
- Mỗi channel có game state hoàn toàn độc lập
|
||||
- Channel A có thể đang chơi game với từ "âm cao" → "cao độ"
|
||||
- Channel B có thể đang chơi game khác với từ "mặt trời" → "trời mưa"
|
||||
- Không có xung đột giữa các game
|
||||
|
||||
### Database chung
|
||||
- Tất cả channel sử dụng chung database PostgreSQL
|
||||
- Từ điển nối từ được chia sẻ giữa các channel
|
||||
- Admin có thể thêm/xóa từ từ bất kỳ channel nào
|
||||
|
||||
## Lệnh hỗ trợ
|
||||
|
||||
### Lệnh cơ bản
|
||||
- `!start` - Bắt đầu game nối từ
|
||||
- `!end` - Kết thúc game nối từ
|
||||
|
||||
### Lệnh admin (chỉ admin server)
|
||||
- `!add <từ>` - Thêm từ mới vào database
|
||||
- `!remove <từ>` - Xóa từ khỏi database
|
||||
|
||||
### Lệnh khác
|
||||
- `/help` - Hiển thị danh sách lệnh cho channel hiện tại
|
||||
|
||||
## Ví dụ sử dụng
|
||||
|
||||
### Server A (Channel #game-1)
|
||||
```
|
||||
User: !start
|
||||
Bot: 🎮 Trò chơi Nối Từ đã bắt đầu!
|
||||
Từ đầu tiên: âm cao
|
||||
|
||||
User: cao độ
|
||||
Bot: ✅
|
||||
|
||||
User: độ cao
|
||||
Bot: ✅
|
||||
```
|
||||
|
||||
### Server B (Channel #game-2) - Đồng thời
|
||||
```
|
||||
User: !start
|
||||
Bot: 🎮 Trò chơi Nối Từ đã bắt đầu!
|
||||
Từ đầu tiên: mặt trời
|
||||
|
||||
User: trời mưa
|
||||
Bot: ✅
|
||||
|
||||
User: mưa gió
|
||||
Bot: ✅
|
||||
```
|
||||
|
||||
## Lưu ý quan trọng
|
||||
|
||||
1. **Bot Token**: Chỉ cần 1 bot token duy nhất
|
||||
2. **Database**: Tất cả channel dùng chung database
|
||||
3. **Game State**: Mỗi channel có game state riêng biệt
|
||||
4. **Admin**: Admin của server có thể quản lý từ điển
|
||||
5. **Timeout**: Mỗi game có timeout 30 giây độc lập
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Bot không phản hồi trong channel
|
||||
- Kiểm tra channel ID có trong `CHANNEL_NOI_TU_IDS` không
|
||||
- Kiểm tra bot có quyền gửi tin nhắn trong channel không
|
||||
|
||||
### Game không bắt đầu được
|
||||
- Kiểm tra xem đã có game đang chạy trong channel đó chưa
|
||||
- Kiểm tra database có từ nào không
|
||||
|
||||
### Lỗi database
|
||||
- Kiểm tra kết nối PostgreSQL
|
||||
- Kiểm tra biến môi trường database
|
||||
+26
-11
@@ -1,7 +1,7 @@
|
||||
import discord
|
||||
import asyncio
|
||||
from core.bot import bot, CHANNEL_NOI_TU_ID
|
||||
from typing import Set
|
||||
from core.bot import bot, CHANNEL_NOI_TU_IDS
|
||||
from typing import Set, Dict
|
||||
from datetime import datetime
|
||||
from apps.score import incr
|
||||
|
||||
@@ -32,8 +32,14 @@ class NoiTuGame:
|
||||
self.start_time = None # Thời gian bắt đầu game
|
||||
self.lock = asyncio.Lock()
|
||||
|
||||
# Khởi tạo game state
|
||||
game = NoiTuGame()
|
||||
# Dictionary để lưu game state cho từng channel
|
||||
games: Dict[int, NoiTuGame] = {}
|
||||
|
||||
def get_game_for_channel(channel_id: int) -> NoiTuGame:
|
||||
"""Lấy game state cho channel cụ thể"""
|
||||
if channel_id not in games:
|
||||
games[channel_id] = NoiTuGame()
|
||||
return games[channel_id]
|
||||
|
||||
def is_admin(ctx):
|
||||
"""Kiểm tra xem user có phải là admin không"""
|
||||
@@ -41,7 +47,7 @@ def is_admin(ctx):
|
||||
|
||||
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
|
||||
return ctx.channel.id in CHANNEL_NOI_TU_IDS
|
||||
|
||||
def get_first_word(word: str) -> str:
|
||||
return word.strip().split()[0] if word else ''
|
||||
@@ -58,7 +64,7 @@ def format_time_remaining(seconds: int) -> str:
|
||||
return "⏰ Hết thời gian!"
|
||||
return f"⏰ Còn lại: {seconds} giây"
|
||||
|
||||
async def update_timer_message():
|
||||
async def update_timer_message(game: NoiTuGame):
|
||||
"""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:
|
||||
@@ -101,6 +107,9 @@ async def start_game(ctx):
|
||||
if not is_correct_channel(ctx):
|
||||
return
|
||||
|
||||
# Lấy game state cho channel này
|
||||
game = get_game_for_channel(ctx.channel.id)
|
||||
|
||||
if game.is_active:
|
||||
await ctx.send("❌ Trò chơi đã đang diễn ra!")
|
||||
return
|
||||
@@ -125,7 +134,7 @@ async def start_game(ctx):
|
||||
game.start_time = datetime.now()
|
||||
|
||||
# Tạo timeout task
|
||||
game.timeout_task = asyncio.create_task(game_timeout())
|
||||
game.timeout_task = asyncio.create_task(game_timeout(game))
|
||||
|
||||
embed = discord.Embed(
|
||||
title="🎮 Trò chơi Nối Từ đã bắt đầu!",
|
||||
@@ -147,6 +156,9 @@ async def end_game(ctx):
|
||||
if not is_correct_channel(ctx):
|
||||
return
|
||||
|
||||
# Lấy game state cho channel này
|
||||
game = get_game_for_channel(ctx.channel.id)
|
||||
|
||||
if not game.is_active:
|
||||
await ctx.send("❌ Không có trò chơi nào đang diễn ra!")
|
||||
return
|
||||
@@ -257,7 +269,7 @@ async def remove_word(ctx, *, word: str):
|
||||
else:
|
||||
await ctx.send("❌ Có lỗi xảy ra khi xóa từ!")
|
||||
|
||||
async def game_timeout():
|
||||
async def game_timeout(game: NoiTuGame):
|
||||
"""Xử lý timeout của game"""
|
||||
try:
|
||||
# Đợi đúng 30 giây
|
||||
@@ -316,9 +328,12 @@ async def handle_game_message(message):
|
||||
return
|
||||
|
||||
# Chỉ xử lý trong channel được chỉ định
|
||||
if message.channel.id != CHANNEL_NOI_TU_ID:
|
||||
if message.channel.id not in CHANNEL_NOI_TU_IDS:
|
||||
return
|
||||
|
||||
# Lấy game state cho channel này
|
||||
game = get_game_for_channel(message.channel.id)
|
||||
|
||||
# Nếu game không active, bỏ qua
|
||||
if not game.is_active:
|
||||
return
|
||||
@@ -372,7 +387,7 @@ async def handle_game_message(message):
|
||||
# Reset timeout
|
||||
if game.timeout_task:
|
||||
game.timeout_task.cancel()
|
||||
game.timeout_task = asyncio.create_task(game_timeout())
|
||||
game.timeout_task = asyncio.create_task(game_timeout(game))
|
||||
|
||||
# Dừng timer task cũ nếu có
|
||||
if game.timer_task:
|
||||
@@ -389,5 +404,5 @@ async def handle_game_message(message):
|
||||
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())
|
||||
game.timer_task = asyncio.create_task(update_timer_message(game))
|
||||
|
||||
|
||||
+22
-5
@@ -21,7 +21,19 @@ home_debt_repo = HomeDebtRepository()
|
||||
score_repo = ScoreRepository()
|
||||
|
||||
CHANNEL_HOME_DEBT_ID = int(os.getenv('CHANNEL_HOME_DEBT_ID', 0))
|
||||
CHANNEL_NOI_TU_ID = int(os.getenv('CHANNEL_NOI_TU_ID', 0))
|
||||
|
||||
# Hỗ trợ nhiều channel cho game nối từ với format: ID1,ID2,ID3
|
||||
CHANNEL_NOI_TU_IDS = []
|
||||
channel_noi_tu_env = os.getenv('CHANNEL_NOI_TU_ID', '')
|
||||
if channel_noi_tu_env:
|
||||
for channel_id in channel_noi_tu_env.split(','):
|
||||
try:
|
||||
CHANNEL_NOI_TU_IDS.append(int(channel_id.strip()))
|
||||
except ValueError:
|
||||
print(f"Invalid channel ID: {channel_id}")
|
||||
|
||||
# Giữ lại CHANNEL_NOI_TU_ID cho backward compatibility (lấy ID đầu tiên)
|
||||
CHANNEL_NOI_TU_ID = CHANNEL_NOI_TU_IDS[0] if CHANNEL_NOI_TU_IDS else 0
|
||||
|
||||
|
||||
@bot.tree.command(name='help', description='Show help')
|
||||
@@ -37,12 +49,17 @@ async def help(interaction: discord.Interaction):
|
||||
"!hdtra <số tiền>",
|
||||
"!hdvay <số tiền>"
|
||||
],
|
||||
CHANNEL_NOI_TU_ID: [
|
||||
"!start",
|
||||
"!end"
|
||||
],
|
||||
}
|
||||
|
||||
# Thêm help cho tất cả channel nối từ
|
||||
for channel_id in CHANNEL_NOI_TU_IDS:
|
||||
help_commands[channel_id] = [
|
||||
"!start",
|
||||
"!end",
|
||||
"!add <từ> (admin only)",
|
||||
"!remove <từ> (admin only)"
|
||||
]
|
||||
|
||||
# Kiểm tra xem channel có trong danh sách không
|
||||
if channel.id in help_commands:
|
||||
embed = discord.Embed(
|
||||
|
||||
Reference in New Issue
Block a user