159 lines
4.3 KiB
Python
159 lines
4.3 KiB
Python
"""指令解析器"""
|
|
import re
|
|
import logging
|
|
from typing import Optional, Tuple
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class CommandParser:
|
|
"""指令解析器"""
|
|
|
|
# 指令映射表
|
|
COMMAND_MAP = {
|
|
# 用户注册系统(必须在骰娘之前)
|
|
'.register': 'register',
|
|
'.注册': 'register',
|
|
|
|
# 骰娘
|
|
'.r': 'dice',
|
|
'.roll': 'dice',
|
|
|
|
# 石头剪刀布
|
|
'.rps': 'rps',
|
|
|
|
# 运势占卜
|
|
'.fortune': 'fortune',
|
|
'.运势': 'fortune',
|
|
|
|
# 猜数字
|
|
'.guess': 'guess',
|
|
'.猜数字': 'guess',
|
|
|
|
# 问答
|
|
'.quiz': 'quiz',
|
|
'.问答': 'quiz',
|
|
|
|
# 成语接龙
|
|
'.idiom': 'idiom',
|
|
'.成语接龙': 'idiom',
|
|
'.成语': 'idiom',
|
|
|
|
# 五子棋
|
|
'.gomoku': 'gomoku',
|
|
'.五子棋': 'gomoku',
|
|
'.gobang': 'gomoku',
|
|
|
|
# 积分系统
|
|
'.points': 'points',
|
|
'.积分': 'points',
|
|
'.checkin': 'points',
|
|
'.签到': 'points',
|
|
'.打卡': 'points',
|
|
|
|
# 炼金系统
|
|
'.alchemy': 'alchemy',
|
|
'.炼金': 'alchemy',
|
|
|
|
# 冒险系统
|
|
'.adventure': 'adventure',
|
|
'.冒险': 'adventure',
|
|
|
|
# 积分赠送系统
|
|
'.gift': 'gift',
|
|
'.赠送': 'gift',
|
|
'.送': 'gift',
|
|
|
|
# AI对话系统
|
|
'.ai': 'ai_chat',
|
|
'.aiconfig': 'ai_chat',
|
|
|
|
# 复述
|
|
'.say': 'say',
|
|
'.说': 'say',
|
|
'.复述': 'say',
|
|
|
|
# 私聊
|
|
'.talk': 'talk',
|
|
'.私聊': 'talk',
|
|
|
|
# 帮助
|
|
'.help': 'help',
|
|
'.帮助': 'help',
|
|
|
|
# 统计
|
|
'.stats': 'stats',
|
|
'.统计': 'stats',
|
|
|
|
# 赌场系统
|
|
'.赌场': 'casino',
|
|
'.casino': 'casino',
|
|
}
|
|
|
|
# 机器人名称模式(用于从@消息中提取)
|
|
AT_PATTERN = re.compile(r'@[^\s]+\s+(.+)', re.DOTALL)
|
|
|
|
@classmethod
|
|
def parse(cls, content: str) -> Optional[Tuple[str, str]]:
|
|
"""解析消息内容,提取游戏类型和指令
|
|
|
|
Args:
|
|
content: 消息内容
|
|
|
|
Returns:
|
|
(游戏类型, 完整指令) 或 None
|
|
"""
|
|
# 去除首尾空格
|
|
content = content.strip()
|
|
|
|
# 尝试提取@后的内容
|
|
at_match = cls.AT_PATTERN.search(content)
|
|
if at_match:
|
|
content = at_match.group(1).strip()
|
|
|
|
# 拦截全角空格与全角标点(不允许)
|
|
# 范围包含:全角空格\u3000、全角标点\uFF01-\uFF60、兼容区\uFFE0-\uFFEE
|
|
if re.search(r"[\u3000\uFF01-\uFF60\uFFE0-\uFFEE]", content):
|
|
logger.debug(f"包含全角字符,忽略: {content}")
|
|
return None
|
|
|
|
# 大小写不敏感匹配(仅用于匹配,不改变返回的原始内容)
|
|
content_lower = content.lower()
|
|
|
|
# 使用最长前缀优先,避免 .r 误匹配 .roll 等更长前缀
|
|
command_keys_sorted = sorted(cls.COMMAND_MAP.keys(), key=len, reverse=True)
|
|
for cmd_prefix in command_keys_sorted:
|
|
if content_lower.startswith(cmd_prefix.lower()):
|
|
return cls.COMMAND_MAP[cmd_prefix], content
|
|
|
|
# 特殊处理:.ai 和 .aiconfig 指令支持参数
|
|
if content.startswith('.ai '):
|
|
return 'ai_chat', content
|
|
if content.startswith('.aiconfig '):
|
|
return 'ai_chat', content
|
|
|
|
# 没有匹配的指令
|
|
logger.debug(f"未识别的指令: {content}")
|
|
return None
|
|
|
|
@classmethod
|
|
def extract_command_args(cls, command: str) -> Tuple[str, str]:
|
|
"""提取指令和参数
|
|
|
|
Args:
|
|
command: 完整指令,如 ".r 1d20" 或 ".guess 50"
|
|
|
|
Returns:
|
|
(指令前缀, 参数部分)
|
|
"""
|
|
parts = command.split(maxsplit=1)
|
|
cmd = parts[0] if parts else ""
|
|
args = parts[1] if len(parts) > 1 else ""
|
|
return cmd, args
|
|
|
|
@classmethod
|
|
def is_help_command(cls, command: str) -> bool:
|
|
"""判断是否为帮助指令"""
|
|
return command.strip() in ['.help', '.帮助', 'help', '帮助']
|
|
|