665 lines
23 KiB
Python
665 lines
23 KiB
Python
"""成语接龙游戏"""
|
||
import json
|
||
import random
|
||
import logging
|
||
import time
|
||
import re
|
||
from pathlib import Path
|
||
from typing import Optional, Tuple, Dict, Any, List
|
||
from pypinyin import pinyin, Style
|
||
from games.base import BaseGame
|
||
from utils.parser import CommandParser
|
||
from config import GAME_CONFIG
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class IdiomGame(BaseGame):
|
||
"""成语接龙游戏"""
|
||
|
||
def __init__(self):
|
||
"""初始化游戏"""
|
||
super().__init__()
|
||
self.config = GAME_CONFIG.get('idiom', {})
|
||
self.max_history_display = self.config.get('max_history_display', 10)
|
||
self.starter_idioms = self.config.get('starter_idioms', [
|
||
"一马当先", "龙马精神", "马到成功", "开门见山"
|
||
])
|
||
self._blacklist = None
|
||
self.blacklist_file = Path(__file__).parent.parent / "data" / "idiom_blacklist.json"
|
||
|
||
async def handle(self, command: str, chat_id: int, user_id: int) -> str:
|
||
"""处理成语接龙指令
|
||
|
||
Args:
|
||
command: 指令,如 ".idiom start" 或 ".idiom 马到成功"
|
||
chat_id: 会话ID
|
||
user_id: 用户ID
|
||
|
||
Returns:
|
||
回复消息
|
||
"""
|
||
try:
|
||
# 提取参数
|
||
_, args = CommandParser.extract_command_args(command)
|
||
args = args.strip()
|
||
|
||
# 没有参数,显示帮助
|
||
if not args:
|
||
return self.get_help()
|
||
|
||
# 解析参数
|
||
parts = args.split(maxsplit=1)
|
||
action = parts[0].lower()
|
||
|
||
# 开始游戏
|
||
if action in ['start', '开始']:
|
||
starter = parts[1].strip() if len(parts) > 1 else None
|
||
return self._start_game(chat_id, user_id, starter)
|
||
|
||
# 结束游戏
|
||
if action in ['stop', '结束', 'end']:
|
||
return self._stop_game(chat_id, user_id)
|
||
|
||
# 查看状态
|
||
if action in ['status', '状态']:
|
||
return self._show_status(chat_id)
|
||
|
||
# 查看黑名单
|
||
if action in ['blacklist', '黑名单']:
|
||
return self._show_blacklist(chat_id)
|
||
|
||
# 查看帮助
|
||
if action in ['help', '帮助']:
|
||
return self.get_help()
|
||
|
||
# 裁判拒绝
|
||
if action in ['reject', '拒绝']:
|
||
if len(parts) < 2:
|
||
return "❌ 请指定要拒绝的词语,如:`.idiom reject 词语`"
|
||
idiom_to_reject = parts[1].strip()
|
||
return self._reject_idiom(chat_id, user_id, idiom_to_reject)
|
||
|
||
# 指定下一位
|
||
if action in ['next', '下一位']:
|
||
if len(parts) < 2:
|
||
return "❌ 请@要指定的用户"
|
||
mentioned = self._parse_mentioned_user(args)
|
||
if not mentioned:
|
||
return "❌ 未识别到@的用户"
|
||
return self._set_next_user(chat_id, user_id, mentioned)
|
||
|
||
# 默认:接龙
|
||
# 整个args都是成语(可能包含@用户)
|
||
return self._make_chain(chat_id, user_id, args)
|
||
|
||
except Exception as e:
|
||
logger.error(f"处理成语接龙指令错误: {e}", exc_info=True)
|
||
return f"❌ 处理指令出错: {str(e)}"
|
||
|
||
def _load_blacklist(self) -> List[str]:
|
||
"""懒加载全局黑名单
|
||
|
||
Returns:
|
||
黑名单列表
|
||
"""
|
||
if self._blacklist is None:
|
||
try:
|
||
if self.blacklist_file.exists():
|
||
with open(self.blacklist_file, 'r', encoding='utf-8') as f:
|
||
data = json.load(f)
|
||
self._blacklist = data.get('blacklist', [])
|
||
else:
|
||
self._blacklist = []
|
||
logger.info(f"黑名单加载完成,共 {len(self._blacklist)} 个词语")
|
||
except Exception as e:
|
||
logger.error(f"加载黑名单失败: {e}")
|
||
self._blacklist = []
|
||
return self._blacklist
|
||
|
||
def _save_blacklist(self):
|
||
"""保存黑名单到文件"""
|
||
try:
|
||
self.blacklist_file.parent.mkdir(parents=True, exist_ok=True)
|
||
data = {
|
||
"blacklist": self._blacklist if self._blacklist is not None else [],
|
||
"description": "成语接龙游戏全局黑名单,被拒绝的词语将永久不可使用"
|
||
}
|
||
with open(self.blacklist_file, 'w', encoding='utf-8') as f:
|
||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||
logger.info(f"黑名单已保存,共 {len(data['blacklist'])} 个词语")
|
||
except Exception as e:
|
||
logger.error(f"保存黑名单失败: {e}")
|
||
|
||
def _get_pinyin(self, char: str, all_readings: bool = True) -> list:
|
||
"""获取单字拼音
|
||
|
||
Args:
|
||
char: 单个汉字
|
||
all_readings: 是否返回多音字的所有读音
|
||
|
||
Returns:
|
||
拼音列表
|
||
"""
|
||
try:
|
||
result = pinyin(char, style=Style.NORMAL, heteronym=all_readings)
|
||
return result[0] if result else []
|
||
except Exception as e:
|
||
logger.error(f"获取拼音错误: {e}")
|
||
return []
|
||
|
||
def _check_pinyin_match(self, last_char: str, first_char: str) -> Tuple[bool, str, str]:
|
||
"""检查拼音匹配
|
||
|
||
Args:
|
||
last_char: 上一个成语的最后一个字
|
||
first_char: 当前成语的第一个字
|
||
|
||
Returns:
|
||
(是否匹配, 上一个拼音, 当前拼音)
|
||
"""
|
||
last_pinyins = self._get_pinyin(last_char)
|
||
first_pinyins = self._get_pinyin(first_char)
|
||
|
||
# 只要有任何一个读音匹配就算成功
|
||
for lp in last_pinyins:
|
||
for fp in first_pinyins:
|
||
if lp.lower() == fp.lower():
|
||
return True, lp, fp
|
||
|
||
# 没有匹配,返回第一个读音
|
||
return False, last_pinyins[0] if last_pinyins else '', first_pinyins[0] if first_pinyins else ''
|
||
|
||
def _parse_mentioned_user(self, content: str) -> Optional[int]:
|
||
"""解析消息中@的用户ID
|
||
|
||
Args:
|
||
content: 消息内容
|
||
|
||
Returns:
|
||
用户ID或None
|
||
"""
|
||
# 简化实现:查找@后的数字
|
||
# 实际WPS API可能有特定格式,需要根据文档调整
|
||
match = re.search(r'@.*?(\d+)', content)
|
||
if match:
|
||
try:
|
||
return int(match.group(1))
|
||
except ValueError:
|
||
pass
|
||
return None
|
||
|
||
def _can_answer(self, state_data: Dict, user_id: int) -> Tuple[bool, str]:
|
||
"""检查用户是否可以接龙
|
||
|
||
Args:
|
||
state_data: 游戏状态数据
|
||
user_id: 用户ID
|
||
|
||
Returns:
|
||
(是否可以, 错误消息)
|
||
"""
|
||
# 不能是上一个接龙的人
|
||
if state_data.get('last_user_id') == user_id:
|
||
return False, "❌ 不能连续接龙哦!让其他人来吧"
|
||
|
||
# 如果指定了下一位,必须是指定的人
|
||
if state_data.get('next_user_id') is not None:
|
||
if state_data['next_user_id'] != user_id:
|
||
return False, f"❌ 现在轮到指定的人接龙了"
|
||
|
||
return True, ""
|
||
|
||
def _validate_idiom(self, idiom: str, state_data: Dict) -> Tuple[bool, str]:
|
||
"""验证词语有效性
|
||
|
||
Args:
|
||
idiom: 待验证的词语
|
||
state_data: 游戏状态数据
|
||
|
||
Returns:
|
||
(是否有效, 错误消息)
|
||
"""
|
||
# 检查长度
|
||
if len(idiom) != 4:
|
||
return False, "❌ 词语必须是4个字"
|
||
|
||
# 检查是否已使用
|
||
if idiom in state_data.get('used_idioms', []):
|
||
return False, f"❌ 「{idiom}」已经用过了"
|
||
|
||
# 检查是否在全局黑名单
|
||
blacklist = self._load_blacklist()
|
||
if idiom in blacklist:
|
||
return False, f"❌ 「{idiom}」在黑名单中(永久禁用)"
|
||
|
||
# 检查拼音匹配
|
||
current_idiom = state_data.get('current_idiom', '')
|
||
if current_idiom:
|
||
last_char = current_idiom[-1]
|
||
first_char = idiom[0]
|
||
is_match, last_py, first_py = self._check_pinyin_match(last_char, first_char)
|
||
|
||
if not is_match:
|
||
return False, f"❌ 首字「{first_char}」拼音[{first_py}]不匹配上个成语尾字「{last_char}」拼音[{last_py}]"
|
||
|
||
return True, ""
|
||
|
||
def _start_game(self, chat_id: int, user_id: int, starter_idiom: Optional[str]) -> str:
|
||
"""开始新游戏
|
||
|
||
Args:
|
||
chat_id: 会话ID
|
||
user_id: 用户ID
|
||
starter_idiom: 起始成语(可选)
|
||
|
||
Returns:
|
||
提示消息
|
||
"""
|
||
# 检查是否已有进行中的游戏(user_id=0表示群级别状态)
|
||
state = self.db.get_game_state(chat_id, 0, 'idiom')
|
||
if state:
|
||
return "⚠️ 已经有一个进行中的游戏了!\n\n输入 `.idiom stop` 结束当前游戏"
|
||
|
||
# 确定起始成语
|
||
if starter_idiom:
|
||
# 验证起始成语
|
||
if len(starter_idiom) != 4:
|
||
return "❌ 起始成语必须是4个字"
|
||
idiom = starter_idiom
|
||
else:
|
||
# 随机选择
|
||
idiom = random.choice(self.starter_idioms)
|
||
|
||
# 获取最后一个字的拼音
|
||
last_char = idiom[-1]
|
||
last_pinyin_list = self._get_pinyin(last_char)
|
||
last_pinyin = last_pinyin_list[0] if last_pinyin_list else ''
|
||
|
||
# 创建游戏状态
|
||
state_data = {
|
||
'creator_id': user_id,
|
||
'current_idiom': idiom,
|
||
'current_pinyin_last': last_pinyin,
|
||
'last_user_id': user_id, # 发起人可以接第一个
|
||
'next_user_id': None,
|
||
'used_idioms': [idiom],
|
||
'chain_length': 1,
|
||
'participants': {},
|
||
'history': [
|
||
{
|
||
'user_id': user_id,
|
||
'idiom': idiom,
|
||
'timestamp': int(time.time())
|
||
}
|
||
],
|
||
'status': 'playing'
|
||
}
|
||
|
||
# 保存群状态(user_id=0)
|
||
self.db.save_game_state(chat_id, 0, 'idiom', state_data)
|
||
|
||
text = f"## 🀄 成语接龙开始!\n\n"
|
||
text += f"**起始成语**:{idiom} [{last_pinyin}]\n\n"
|
||
text += f"任何人都可以接龙,输入 `.idiom [成语]` 开始吧!\n\n"
|
||
text += f"💡 提示:可以用 `.idiom [成语] @某人` 指定下一位"
|
||
|
||
return text
|
||
|
||
def _make_chain(self, chat_id: int, user_id: int, args: str) -> str:
|
||
"""玩家接龙
|
||
|
||
Args:
|
||
chat_id: 会话ID
|
||
user_id: 用户ID
|
||
args: 参数(成语 + 可能的@用户)
|
||
|
||
Returns:
|
||
结果消息
|
||
"""
|
||
# 获取群状态
|
||
state = self.db.get_game_state(chat_id, 0, 'idiom')
|
||
if not state:
|
||
return "⚠️ 还没有开始游戏呢!\n\n输入 `.idiom start` 开始游戏"
|
||
|
||
state_data = state['state_data']
|
||
|
||
# 检查用户权限
|
||
can_answer, error_msg = self._can_answer(state_data, user_id)
|
||
if not can_answer:
|
||
return error_msg
|
||
|
||
# 解析成语和@用户
|
||
# 提取成语(去除@部分)
|
||
idiom_match = re.match(r'^([^\s@]+)', args)
|
||
if not idiom_match:
|
||
return "❌ 请输入4个字的词语"
|
||
|
||
idiom = idiom_match.group(1).strip()
|
||
|
||
# 验证词语
|
||
is_valid, error_msg = self._validate_idiom(idiom, state_data)
|
||
if not is_valid:
|
||
return error_msg
|
||
|
||
# 解析@用户
|
||
mentioned_user_id = self._parse_mentioned_user(args)
|
||
|
||
# 获取拼音
|
||
last_char = idiom[-1]
|
||
last_pinyin_list = self._get_pinyin(last_char)
|
||
last_pinyin = last_pinyin_list[0] if last_pinyin_list else ''
|
||
|
||
# 更新状态
|
||
state_data['current_idiom'] = idiom
|
||
state_data['current_pinyin_last'] = last_pinyin
|
||
state_data['last_user_id'] = user_id
|
||
state_data['next_user_id'] = mentioned_user_id
|
||
state_data['used_idioms'].append(idiom)
|
||
state_data['chain_length'] += 1
|
||
|
||
# 更新参与者统计
|
||
if str(user_id) not in state_data['participants']:
|
||
state_data['participants'][str(user_id)] = 0
|
||
state_data['participants'][str(user_id)] += 1
|
||
|
||
# 记录历史
|
||
state_data['history'].append({
|
||
'user_id': user_id,
|
||
'idiom': idiom,
|
||
'timestamp': int(time.time())
|
||
})
|
||
|
||
# 保存状态
|
||
self.db.save_game_state(chat_id, 0, 'idiom', state_data)
|
||
|
||
# 构建回复
|
||
text = f"## ✅ 接龙成功!\n\n"
|
||
text += f"**{idiom}** [{last_pinyin}]\n\n"
|
||
text += f"**当前链长**:{state_data['chain_length']}\n\n"
|
||
|
||
user_count = state_data['participants'][str(user_id)]
|
||
text += f"@用户{user_id} 成功次数:{user_count}\n\n"
|
||
|
||
if mentioned_user_id:
|
||
text += f"已指定 @用户{mentioned_user_id} 接龙\n\n"
|
||
else:
|
||
text += "任何人都可以接龙\n\n"
|
||
|
||
text += "继续加油!💪"
|
||
|
||
return text
|
||
|
||
def _set_next_user(self, chat_id: int, user_id: int, next_user_id: int) -> str:
|
||
"""指定下一位接龙者
|
||
|
||
Args:
|
||
chat_id: 会话ID
|
||
user_id: 当前用户ID
|
||
next_user_id: 指定的下一位用户ID
|
||
|
||
Returns:
|
||
提示消息
|
||
"""
|
||
# 获取群状态
|
||
state = self.db.get_game_state(chat_id, 0, 'idiom')
|
||
if not state:
|
||
return "⚠️ 还没有开始游戏呢!"
|
||
|
||
state_data = state['state_data']
|
||
|
||
# 检查是否是最后接龙的人
|
||
if state_data.get('last_user_id') != user_id:
|
||
return "❌ 只有最后接龙成功的人可以指定下一位"
|
||
|
||
# 更新状态
|
||
state_data['next_user_id'] = next_user_id
|
||
self.db.save_game_state(chat_id, 0, 'idiom', state_data)
|
||
|
||
return f"✅ 已指定 @用户{next_user_id} 接龙"
|
||
|
||
def _reject_idiom(self, chat_id: int, user_id: int, idiom: str) -> str:
|
||
"""裁判拒绝词语
|
||
|
||
Args:
|
||
chat_id: 会话ID
|
||
user_id: 用户ID
|
||
idiom: 要拒绝的词语
|
||
|
||
Returns:
|
||
提示消息
|
||
"""
|
||
# 获取群状态
|
||
state = self.db.get_game_state(chat_id, 0, 'idiom')
|
||
if not state:
|
||
return "⚠️ 还没有开始游戏呢!"
|
||
|
||
state_data = state['state_data']
|
||
|
||
# 检查权限(仅发起人)
|
||
if state_data.get('creator_id') != user_id:
|
||
return "❌ 只有游戏发起人可以执行裁判操作"
|
||
|
||
# 添加到全局黑名单
|
||
blacklist = self._load_blacklist()
|
||
if idiom not in blacklist:
|
||
blacklist.append(idiom)
|
||
self._save_blacklist()
|
||
logger.info(f"词语「{idiom}」已加入全局黑名单")
|
||
|
||
# 如果是最后一个成语,回退状态
|
||
if state_data.get('current_idiom') == idiom and len(state_data['history']) > 1:
|
||
# 移除最后一条历史
|
||
removed = state_data['history'].pop()
|
||
removed_user = str(removed['user_id'])
|
||
|
||
# 减少该用户的计数
|
||
if removed_user in state_data['participants']:
|
||
state_data['participants'][removed_user] -= 1
|
||
if state_data['participants'][removed_user] <= 0:
|
||
del state_data['participants'][removed_user]
|
||
|
||
# 恢复到上一个成语
|
||
if state_data['history']:
|
||
last_entry = state_data['history'][-1]
|
||
last_idiom = last_entry['idiom']
|
||
last_char = last_idiom[-1]
|
||
last_pinyin_list = self._get_pinyin(last_char)
|
||
|
||
state_data['current_idiom'] = last_idiom
|
||
state_data['current_pinyin_last'] = last_pinyin_list[0] if last_pinyin_list else ''
|
||
state_data['last_user_id'] = last_entry['user_id']
|
||
state_data['next_user_id'] = None
|
||
state_data['chain_length'] -= 1
|
||
|
||
# 从已使用列表中移除
|
||
if idiom in state_data['used_idioms']:
|
||
state_data['used_idioms'].remove(idiom)
|
||
|
||
# 保存状态
|
||
self.db.save_game_state(chat_id, 0, 'idiom', state_data)
|
||
|
||
text = f"✅ 已将「{idiom}」加入全局黑名单(永久禁用)"
|
||
if state_data.get('current_idiom') != idiom:
|
||
text += f"\n\n当前成语:{state_data['current_idiom']}"
|
||
else:
|
||
text += "\n\n游戏状态已回退"
|
||
|
||
return text
|
||
|
||
def _show_status(self, chat_id: int) -> str:
|
||
"""显示游戏状态
|
||
|
||
Args:
|
||
chat_id: 会话ID
|
||
|
||
Returns:
|
||
状态信息
|
||
"""
|
||
# 获取群状态
|
||
state = self.db.get_game_state(chat_id, 0, 'idiom')
|
||
if not state:
|
||
return "⚠️ 还没有开始游戏呢!\n\n输入 `.idiom start` 开始游戏"
|
||
|
||
state_data = state['state_data']
|
||
|
||
text = f"## 🀄 成语接龙状态\n\n"
|
||
text += f"**当前成语**:{state_data['current_idiom']} [{state_data['current_pinyin_last']}]\n\n"
|
||
text += f"**链长**:{state_data['chain_length']}\n\n"
|
||
|
||
# 下一位
|
||
if state_data.get('next_user_id'):
|
||
text += f"**下一位**:@用户{state_data['next_user_id']}\n\n"
|
||
else:
|
||
text += f"**下一位**:任何人都可以接龙\n\n"
|
||
|
||
# 参与者排行
|
||
if state_data['participants']:
|
||
text += f"### 🏆 参与者排行\n\n"
|
||
sorted_participants = sorted(
|
||
state_data['participants'].items(),
|
||
key=lambda x: x[1],
|
||
reverse=True
|
||
)
|
||
for idx, (uid, count) in enumerate(sorted_participants[:5], 1):
|
||
text += f"{idx}. @用户{uid} - {count}次\n"
|
||
text += "\n"
|
||
|
||
# 最近成语
|
||
history = state_data.get('history', [])
|
||
if history:
|
||
display_count = min(self.max_history_display, len(history))
|
||
recent = history[-display_count:]
|
||
text += f"### 📜 最近{display_count}个成语\n\n"
|
||
text += " → ".join([h['idiom'] for h in recent])
|
||
|
||
return text
|
||
|
||
def _show_blacklist(self, chat_id: int) -> str:
|
||
"""显示全局黑名单
|
||
|
||
Args:
|
||
chat_id: 会话ID(保留参数以保持接口一致性)
|
||
|
||
Returns:
|
||
黑名单信息
|
||
"""
|
||
# 加载全局黑名单
|
||
blacklist = self._load_blacklist()
|
||
|
||
if not blacklist:
|
||
return "📋 全局黑名单为空\n\n💡 发起人可使用 `.idiom reject [词语]` 添加不合适的词语到黑名单"
|
||
|
||
text = f"## 📋 全局黑名单词语(永久禁用)\n\n"
|
||
text += f"**共 {len(blacklist)} 个词语**\n\n"
|
||
text += "、".join(blacklist)
|
||
text += "\n\n💡 这些词语在所有游戏中都不可使用"
|
||
|
||
return text
|
||
|
||
def _stop_game(self, chat_id: int, user_id: int) -> str:
|
||
"""结束游戏
|
||
|
||
Args:
|
||
chat_id: 会话ID
|
||
user_id: 用户ID
|
||
|
||
Returns:
|
||
总结消息
|
||
"""
|
||
# 获取群状态
|
||
state = self.db.get_game_state(chat_id, 0, 'idiom')
|
||
if not state:
|
||
return "⚠️ 还没有开始游戏呢!"
|
||
|
||
state_data = state['state_data']
|
||
|
||
# 构建总结
|
||
text = f"## 🎮 游戏结束!\n\n"
|
||
text += f"**总链长**:{state_data['chain_length']}\n\n"
|
||
text += f"**参与人数**:{len(state_data['participants'])}\n\n"
|
||
|
||
# 排行榜
|
||
if state_data['participants']:
|
||
text += f"### 🏆 排行榜\n\n"
|
||
sorted_participants = sorted(
|
||
state_data['participants'].items(),
|
||
key=lambda x: x[1],
|
||
reverse=True
|
||
)
|
||
for idx, (uid, count) in enumerate(sorted_participants, 1):
|
||
text += f"{idx}. @用户{uid} - {count}次\n"
|
||
# 更新统计
|
||
try:
|
||
for _ in range(count):
|
||
self.db.update_game_stats(int(uid), 'idiom', win=True)
|
||
except Exception as e:
|
||
logger.error(f"更新统计失败: {e}")
|
||
text += "\n"
|
||
|
||
# 完整接龙
|
||
history = state_data.get('history', [])
|
||
if history:
|
||
text += f"### 📜 完整接龙\n\n"
|
||
idioms = [h['idiom'] for h in history]
|
||
text += " → ".join(idioms)
|
||
|
||
# 删除游戏状态
|
||
self.db.delete_game_state(chat_id, 0, 'idiom')
|
||
|
||
return text
|
||
|
||
def _format_history(self, history: list, count: int) -> str:
|
||
"""格式化历史记录
|
||
|
||
Args:
|
||
history: 历史记录列表
|
||
count: 显示数量
|
||
|
||
Returns:
|
||
格式化的字符串
|
||
"""
|
||
if not history:
|
||
return ""
|
||
|
||
display_count = min(count, len(history))
|
||
recent = history[-display_count:]
|
||
return " → ".join([h['idiom'] for h in recent])
|
||
|
||
def get_help(self) -> str:
|
||
"""获取帮助信息"""
|
||
return """## 🀄 成语接龙
|
||
|
||
### 基础用法
|
||
- `.idiom start [成语]` - 开始游戏(可指定起始成语)
|
||
- `.idiom [成语]` - 接龙
|
||
- `.idiom [成语] @某人` - 接龙并指定下一位
|
||
- `.idiom stop` - 结束游戏(任何人可执行)
|
||
|
||
### 其他指令
|
||
- `.idiom status` - 查看游戏状态
|
||
- `.idiom blacklist` - 查看黑名单
|
||
- `.idiom reject [词语]` - 裁判拒绝词语(仅发起人)
|
||
- `.idiom next @某人` - 指定下一位(仅最后接龙者)
|
||
|
||
### 游戏规则
|
||
- 词语必须是4个字
|
||
- 首字拼音必须匹配上个成语尾字拼音(忽略声调)
|
||
- 不能重复使用成语
|
||
- 不能连续接龙
|
||
- 黑名单词语不可使用
|
||
- 任何人都可以结束游戏
|
||
|
||
### 示例
|
||
```
|
||
.idiom start 一马当先 # 开始游戏
|
||
.idiom 先声夺人 # 接龙
|
||
.idiom 人山人海 @张三 # 接龙并指定下一位
|
||
.idiom reject 某词 # 发起人拒绝某词
|
||
.idiom stop # 结束游戏
|
||
```
|
||
|
||
💡 提示:支持多音字和谐音接龙
|
||
"""
|
||
|