Archived
1
0
Fork 0
This repository has been archived on 2024-04-26. You can view files and clone it, but cannot push or open issues or pull requests.
akari-bot/core/parser/command.py
2022-07-31 16:33:20 +08:00

133 lines
5.6 KiB
Python

import re
import shlex
import traceback
from typing import Union
from core.elements import Command, Schedule, StartUp, RegexCommand, MessageSession
from core.exceptions import InvalidCommandFormatError, InvalidHelpDocTypeError
from core.utils.docopt import docopt, DocoptExit
class CommandParser:
def __init__(self, args: Union[str, list, tuple, Command, Schedule, StartUp, RegexCommand], command_prefixes: list,
bind_prefix=None,
msg: MessageSession = None):
"""
Format: https://github.com/jazzband/docopt-ng#usage-pattern-format
* {} - Detail help information
"""
self.command_prefixes = command_prefixes
self.bind_prefix = bind_prefix
self.origin_template = args
self.msg: Union[MessageSession, None] = msg
self.options_desc = []
if isinstance(args, Command):
self.bind_prefix = args.bind_prefix
help_doc_list = []
none_doc = True
for match in (args.match_list.set if self.msg is None else args.match_list.get(self.msg.target.targetFrom)):
if match.help_doc is not None:
none_doc = False
help_doc_list = help_doc_list + match.help_doc
if match.options_desc is not None:
for m in match.options_desc:
self.options_desc.append(f'{m} {match.options_desc[m]}')
if not none_doc:
args = help_doc_list
else:
args = None
elif isinstance(args, (Schedule, StartUp, RegexCommand)):
args = None
if args is None:
self.args = None
return
if isinstance(args, str):
args = [args]
self.args_raw = args
elif isinstance(args, tuple):
args = list(args)
if isinstance(args, list):
arglst_raw = []
arglst = []
for x in args:
split = x.split(' ')[0]
if self.bind_prefix is not None:
if split not in [command_prefixes[0] + self.bind_prefix, self.bind_prefix]:
x = f'{command_prefixes[0]}{self.bind_prefix} {x}'
arglst_raw.append(x)
match_detail_help = re.match('(.*){.*}$', x, re.M | re.S)
if match_detail_help:
x = match_detail_help.group(1)
arglst.append(x)
self.args_raw = arglst_raw
self.args = 'Usage:\n ' + '\n '.join(y for y in arglst)
else:
raise InvalidHelpDocTypeError
def return_formatted_help_doc(self) -> str:
if self.args is None:
return '(此模块没有帮助信息)'
args_raw = self.args_raw
if isinstance(args_raw, list):
arglst = []
for x in args_raw:
if x[0] not in self.command_prefixes:
x = self.command_prefixes[0] + x
match_detail_help = re.match('(.*){(.*)}$', x, re.M | re.S)
if match_detail_help:
x = f'{match_detail_help.group(1)}- {match_detail_help.group(2)}'
arglst.append(x)
args = '\n'.join(y for y in arglst)
else:
raise InvalidHelpDocTypeError
if self.options_desc:
args += '\n参数:\n' + '\n'.join(self.options_desc)
return args
def parse(self, command):
if self.args is None:
return None
command = re.sub(r'[“”]', '"', command)
try:
split_command = shlex.split(command)
except ValueError:
split_command = command.split(' ')
try:
if not isinstance(self.origin_template, Command):
if len(split_command) == 1:
return None
return docopt(self.args, argvs=split_command[1:], default_help=False)
else:
if len(split_command) == 1:
for match in (self.origin_template.match_list.set if self.msg is None else
self.origin_template.match_list.get(self.msg.target.targetFrom)):
if match.help_doc is None:
return match, None
raise InvalidCommandFormatError
else:
base_match = docopt(self.args, argvs=split_command[1:], default_help=False)
for match in (self.origin_template.match_list.set if self.msg is None else
self.origin_template.match_list.get(self.msg.target.targetFrom)):
if match.help_doc is None:
continue
try:
sub_args = CommandParser(match.help_doc, bind_prefix=self.bind_prefix,
command_prefixes=self.command_prefixes).args
if sub_args is not None:
get_parse = docopt(sub_args,
argvs=split_command[1:], default_help=False)
else:
continue
except DocoptExit:
continue
correct = True
for g in get_parse:
if g not in base_match or get_parse[g] != base_match[g]:
correct = False
if correct:
return match, get_parse
except DocoptExit:
traceback.print_exc()
raise InvalidCommandFormatError