From 0e3628f838f42dde25a2f5141fe2ca95eaaad132 Mon Sep 17 00:00:00 2001 From: guangrei <20879950+guangrei@users.noreply.github.com> Date: Wed, 3 Jan 2024 21:35:24 +0700 Subject: [PATCH] optimize asynchronous support, add quick add_command and add_default_command --- README.md | 22 ++++++++ chatrouter.py | 141 ++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 152 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 6f3df5e..a9066be 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,8 @@ func = chatrouter.util.get_func("group_name", "command_name") 7. object storage `chatrouter.data_user`. +8. support asynchronous. + ## installation ``` @@ -96,5 +98,25 @@ if __name__ == '__main__': exit(0) ``` +asynchronous example +```python +#-*-coding:utf8;-*- +import asyncio +import chatrouter + + +chatbot = chatrouter.group("test", asynchronous=True) +@chatbot.add_command("call me {name}") +async def test(name): + return f"hello {name}!" + +async def main(): + user_input = "call me human" + response = await chatrouter.async_run(chatbot, user_input) + print(response) + +if __name__ == '__main__': + asyncio.run(main()) +``` for more complex example, please open [demo/telegram_bot](https://github.com/cirebon-dev/chatrouter/tree/main/demo/telegram_bot). \ No newline at end of file diff --git a/chatrouter.py b/chatrouter.py index d86b4f0..2545a62 100644 --- a/chatrouter.py +++ b/chatrouter.py @@ -6,7 +6,7 @@ """ __author__ = "guangrei" -__version__ = "v0.0.5" +__version__ = "v1.0.0" _data: dict = {} # chatrouter storage data_user: Any = None # data user storage @@ -28,7 +28,7 @@ class group: class ini digunakan untuk membuat command group """ - def __init__(self, name: str, description: str = "no description!") -> None: + def __init__(self, name: str, description: str = "no description!", asynchronous=False) -> None: """ fungsi ini adalah kelas konstruktor """ @@ -37,11 +37,17 @@ def __init__(self, name: str, description: str = "no description!") -> None: if name not in _data: _data[name] = {} _data[name]["/start"] = {} - _data[name]["/start"]["callback"] = self._start + if not asynchronous: + _data[name]["/start"]["callback"] = self._start + else: + _data[name]["/start"]["callback"] = self._async_start _data[name]["/start"]["description"] = "start command!" _data[name]["/start"]["strict"] = True _data[name]["/help"] = {} - _data[name]["/help"]["callback"] = self._help + if not asynchronous: + _data[name]["/help"]["callback"] = self._help + else: + _data[name]["/help"]["callback"] = self._async_help _data[name]["/help"]["description"] = "help command!" _data[name]["/help"]["strict"] = True self.id = name @@ -70,6 +76,18 @@ def dec(func: Callable) -> Callable: return func return dec + def _start(self) -> str: + """ + fungsi ini sebagai default callback /start commands + """ + return self.start_msg + + async def _async_start(self) -> str: + """ + fungsi ini sebagai default callback /start commands + """ + return self.start_msg + def _help(self) -> str: """ fungsi ini sebagai default callback /help commands. @@ -83,11 +101,18 @@ def _help(self) -> str: i = i+1 return hasil.strip() - def _start(self) -> str: + async def _async_help(self) -> str: """ - fungsi ini sebagai default callback /start commands + fungsi ini sebagai default callback /help commands. + hanya menampilkan publik commands (command yang diawali dengan "/" dan memiliki deskripsi) """ - return self.start_msg + hasil = _help_template_.format(group=self.id).strip() + i = 1 + for k, v in _data[self.id].items(): + if k.startswith("/") and len(v["description"]): + hasil = hasil + f'\n{i}. {k} - {v["description"]}' + i = i+1 + return hasil.strip() class util: @@ -100,27 +125,27 @@ def get_func(group: str, route: str) -> Callable: """ route = util.compile(route) return _data[group][route]["callback"] - + def compile(string: str) -> str: """ fungsi ini untuk mengcompile regex pattern """ pattern = re.sub(r'\{([^}]+)\}', r'(.*)', string) return pattern.strip() - + def group_exists(name: str) -> bool: """ fungsi ini untuk mengecek group """ return name in _data - + def command_exists(group: str, command: str) -> bool: """ fungsi ini untuk mengecek command """ command = util.compile(command) return command in _data[group] - + def remove_command(group: str, command: str) -> None: """ fungsi ini untuk menghapus command @@ -141,6 +166,53 @@ def _route(pattern: str, string: str, strict: bool) -> Union[list, bool]: else: return False + async def async_get_func(group: str, route: str) -> Callable: + """ + fungsi utilitas ini dapat digunakan untuk mengambil callback pada group dan route tertentu + """ + route = await util.async_compile(route) + return _data[group][route]["callback"] + + async def async_compile(string: str) -> str: + """ + fungsi ini untuk mengcompile regex pattern + """ + pattern = re.sub(r'\{([^}]+)\}', r'(.*)', string) + return pattern.strip() + + async def async_group_exists(name: str) -> bool: + """ + fungsi ini untuk mengecek group + """ + return name in _data + + async def async_command_exists(group: str, command: str) -> bool: + """ + fungsi ini untuk mengecek command + """ + command = await util.async_compile(command) + return command in _data[group] + + async def async_remove_command(group: str, command: str) -> None: + """ + fungsi ini untuk menghapus command + """ + command = await util.async_compile(command) + del _data[group][command] + + async def _async_route(pattern: str, string: str, strict: bool) -> Union[list, bool]: + """ + fungsi ini private dan berfungsi sebagai route + """ + if strict: + match = re.match(pattern, string.strip()) + else: + match = re.match(pattern, string.strip(), re.IGNORECASE) + if match: + return list(match.groups()) + else: + return False + def run(route: group, msg: str) -> Union[str, None]: """ @@ -158,5 +230,52 @@ def run(route: group, msg: str) -> Union[str, None]: return f"info: no default handler for route {route.id}:{msg}" +async def async_run(route: group, msg: str) -> Union[str, None]: + """ + ini adalah versi async dari function run + """ + if len(msg): + for k, v in _data[route.id].items(): + if k != "__default__": + args = await util._async_route(k, msg, v["strict"]) + if args is not False: + ret = await v["callback"](*args) + return ret + if "__default__" in _data[route.id]: + ret = await _data[route.id]["__default__"]["callback"](msg) + return ret + else: + return f"info: no default handler for route {route.id}:{msg}" + + +def add_command(route: str, description: str = "", strict: bool = False, group_route: str = "main", asynchronous: bool = False) -> Callable: + """ + fungsi untuk menambahkan command secara cepat + """ + group(group_route, asynchronous=asynchronous) + method = util.compile(route) + + def dec(func: Callable) -> Callable: + _data[group_route][method] = {} + _data[group_route][method]["callback"] = func + _data[group_route][method]["description"] = description + _data[group_route][method]["strict"] = strict + return func + return dec + + +def add_default_command(group_route: str = "main", asynchronous: bool = False) -> Callable: + """ + fungsi untuk menambahkan default command secara cepat + """ + group(group_route, asynchronous=asynchronous) + + def dec(func: Callable) -> Callable: + _data[group_route]["__default__"] = {} + _data[group_route]["__default__"]["callback"] = func + return func + return dec + + if __name__ == '__main__': pass