diff --git a/src/core/__init__.py b/src/core/__init__.py index 35dc9bc..0257735 100644 --- a/src/core/__init__.py +++ b/src/core/__init__.py @@ -1,7 +1,7 @@ # Developed by KuiToi Dev # File core.__init__.py # Written by: SantaSpeen -# Version 1.1 +# Version 1.2 # Licence: FPA # (c) kuitoi.su 2023 # Special thanks to: AI Sage(https://poe.com/Sage), AI falcon-40b-v7(https://OpenBuddy.ai) @@ -28,7 +28,7 @@ import builtins import os import webbrowser -from prompt_toolkit.shortcuts import input_dialog, yes_no_dialog +import prompt_toolkit.shortcuts as shortcuts from .utils import get_logger from modules import ConfigProvider, EventsSystem, PluginsLoader @@ -46,13 +46,12 @@ if args.config: config_path = args.config config_provider = ConfigProvider(config_path) config = config_provider.open_config() -log.info("Use %s for config." % config_path) if config.Server['debug'] is True: utils.set_debug_status() - log.info("Getting new logging with DEBUG level!") + log.info("Debug enabled!") log = get_logger("init") log.debug("Debug mode enabled!") - log.debug("Use %s for config." % config) + log.debug(f"Server config: {config}") # i18n init log.debug("Initializing i18n...") @@ -60,28 +59,44 @@ ml = MultiLanguage() ml.set_language(args.language) ml.builtins_hook() -log.info(i18n.hello) - +log.debug("Initializing EventsSystem...") ev = EventsSystem() ev.builtins_hook() -# Key handler.. -if not config.Auth['key']: - log.warn("Key needed for starting the server!") - url = "https://beammp.com/k/keys" - if yes_no_dialog( - title='BEAMP Server Key', - text='Key needed for starting the server!\n' - 'Do you need to open the web link to obtain the key?').run(): - webbrowser.open(url, new=2) +log.info(i18n.hello) +log.info(i18n.config_path.format(config_path)) - config.Auth['key'] = input_dialog( +log.debug("Initializing BEAMP Server system...") +# Key handler.. +private = ((config.Auth['key'] is None or config.Auth['key'] == "") and config.Auth['private']) +if not private: + log.warn(i18n.auth_need_key) + url = "https://beammp.com/k/keys" + if shortcuts.yes_no_dialog( + title='BEAMP Server Key', + text=i18n.GUI_need_key_message, + yes_text=i18n.GUI_yes, + no_text=i18n.GUI_no).run(): + try: + log.debug("Opening browser...") + webbrowser.open(url, new=2) + except Exception as e: + log.error(i18n.auth_cannot_open_browser.format(e)) + log.info(i18n.auth_use_link.format(url)) + shortcuts.message_dialog( + title='BEAMP Server Key', + text=i18n.GUI_cannot_open_browser.format(url), + ok_text=i18n.GUI_ok).run() + + config.Auth['key'] = shortcuts.input_dialog( title='BEAMP Server Key', - text='Please type your key:').run() + text=i18n.GUI_enter_key_message, + ok_text=i18n.GUI_ok, + cancel_text=i18n.GUI_cancel).run() config_provider.save_config() -if not config.Auth['key']: - log.error("Key is empty!") - log.error("Server stopped!") +if not private: + log.error(i18n.auth_empty_key) + log.info(i18n.stop) exit(1) builtins.config = config @@ -90,14 +105,16 @@ log.debug("Initializing console...") console = Console() console.builtins_hook() # console.logger_hook() -console.add_command("stop", console.stop, "stop - Just shutting down the server.\nUsage: stop", "Server shutdown.") -console.add_command("exit", console.stop, "stop - Just shutting down the server.\nUsage: stop", "Server shutdown.") +console.add_command("stop", console.stop, i18n.man_message_stop, i18n.help_message_stop) +console.add_command("exit", console.stop, i18n.man_message_exit, i18n.help_message_exit) if not os.path.exists("mods"): os.mkdir("mods") + + +log.debug("Initializing PluginsLoader...") if not os.path.exists("plugins"): os.mkdir("plugins") - pl = PluginsLoader("plugins") pl.load_plugins() @@ -107,4 +124,4 @@ builtins.MB = KB * 1024 builtins.GB = MB * 1024 builtins.TB = GB * 1024 -log.info(i18n.init) +log.info(i18n.init_ok) diff --git a/src/core/core.py b/src/core/core.py index 03f02c7..b352ac6 100644 --- a/src/core/core.py +++ b/src/core/core.py @@ -14,12 +14,13 @@ from .udp_server import UDPServer class Client: - def __init__(self, reader, writer): + def __init__(self, reader, writer, core): self.reader = reader self.writer = writer self.log = utils.get_logger("client(None:0)") self.addr = writer.get_extra_info("sockname") self.loop = asyncio.get_event_loop() + self.Core = core self.cid = 0 self.key = None self.nick = None @@ -137,11 +138,11 @@ class Core: def __init__(self): self.log = utils.get_logger("core") + self.loop = asyncio.get_event_loop() self.clients = {} self.clients_counter = 0 self.server_ip = config.Server["server_ip"] self.server_port = config.Server["server_port"] - self.loop = asyncio.get_event_loop() self.tcp = TCPServer self.udp = UDPServer @@ -152,14 +153,16 @@ class Core: return self.clients.get(sock.getsockname()) def insert_client(self, client): + self.log.debug(f"Inserting client: {client.cid}") self.clients.update({client.cid: client, client.nick: client}) def create_client(self, *args, **kwargs): - cl = Client(*args, **kwargs) - self.clients_counter = self.clients_counter + 1 - cl.id = self.clients_counter - cl._update_logger() - return cl + client = Client(*args, **kwargs) + self.clients_counter += 1 + client.id = self.clients_counter + client._update_logger() + self.log.debug(f"Create client: {client.cid}; clients_counter: {self.clients_counter}") + return client async def check_alive(self): await asyncio.sleep(5) @@ -174,7 +177,8 @@ class Core: self.udp = self.udp(self, self.server_ip, self.server_port) tasks = [self.tcp.start(), self.udp.start(), console.start()] # self.check_alive() t = asyncio.wait(tasks, return_when=asyncio.FIRST_EXCEPTION) - self.log.info(i18n.ready) + self.log.info(i18n.start) + # TODO: Server auth ev.call_event("on_started") await t # while True: @@ -193,5 +197,5 @@ class Core: asyncio.run(self.main()) def stop(self): - self.log.info("Goodbye!") + self.log.info(i18n.stop) exit(0) diff --git a/src/core/core.pyi b/src/core/core.pyi index 3ee28cc..17cf740 100644 --- a/src/core/core.pyi +++ b/src/core/core.pyi @@ -5,21 +5,22 @@ # Licence: FPA # (c) kuitoi.su 2023 import asyncio -import socket -from asyncio import StreamWriter, AbstractEventLoop, StreamReader -from asyncio.trsock import TransportSocket +from asyncio import StreamWriter, StreamReader from core import utils from .tcp_server import TCPServer from .udp_server import UDPServer + + class Client: - def __init__(self, reader: StreamReader, writer: StreamWriter): + def __init__(self, reader: StreamReader, writer: StreamWriter, core: Core) -> "Client": self.reader = reader self.writer = writer self.log = utils.get_logger("client(id: )") self.addr = writer.get_extra_info("sockname") self.loop = asyncio.get_event_loop() + self.Core = core self.cid = 0 self.key: str = None self.nick: str = None @@ -32,6 +33,7 @@ class Client: async def sync_resources(self) -> None: ... async def recv(self) -> bytes: ... async def last_handle(self) -> bytes: ... + def _update_logger(self) -> None: ... class Core: diff --git a/src/modules/ConfigProvider/config_provider.py b/src/modules/ConfigProvider/config_provider.py index be004aa..c0ec705 100644 --- a/src/modules/ConfigProvider/config_provider.py +++ b/src/modules/ConfigProvider/config_provider.py @@ -25,16 +25,16 @@ class Config: class ConfigProvider: - def __init__(self, config_patch): - self.config_patch = config_patch + def __init__(self, config_path): + self.config_path = config_path self.config = Config() def open_config(self): - if not os.path.exists(self.config_patch): - with open(self.config_patch, "w", encoding="utf-8") as f: + if not os.path.exists(self.config_path): + with open(self.config_path, "w", encoding="utf-8") as f: yaml.dump(self.config, f) try: - with open(self.config_patch, "r", encoding="utf-8") as f: + with open(self.config_path, "r", encoding="utf-8") as f: self.config = yaml.load(f.read(), yaml.Loader) except yaml.YAMLError: print("You have errors in the YAML syntax.") @@ -44,5 +44,5 @@ class ConfigProvider: return self.config def save_config(self): - with open(self.config_patch, "w", encoding="utf-8") as f: + with open(self.config_path, "w", encoding="utf-8") as f: yaml.dump(self.config, f) diff --git a/src/modules/ConsoleSystem/console_system.py b/src/modules/ConsoleSystem/console_system.py index 3b9e9b8..a121efb 100644 --- a/src/modules/ConsoleSystem/console_system.py +++ b/src/modules/ConsoleSystem/console_system.py @@ -6,7 +6,6 @@ # Version 1.1 # Licence: FPA # (c) kuitoi.su 2023 -import asyncio import builtins import logging from typing import AnyStr @@ -37,14 +36,9 @@ class Console: self.__man = dict() self.__desc = dict() self.__print_logger = get_logger("print") - self.add_command("man", self.__create_man_message, "man - display the manual page.\n" - "Usage: man COMMAND", "Display the manual page", + self.add_command("man", self.__create_man_message, i18n.man_message_man, i18n.help_message_man, custom_completer={"man": {}}) - self.add_command("help", self.__create_help_message, - "help - display names and brief descriptions of available commands.\n" - "Usage: help [--raw]\n" - "The `help` command displays a list of all available commands along with a brief description " - "of each command.", "Display names and brief descriptions of available commands.", + self.add_command("help", self.__create_help_message, i18n.man_message_help, i18n.help_message_help, custom_completer={"help": {"--raw": None}}) self.completer = NestedCompleter.from_nested_dict(self.__alias) @@ -68,16 +62,19 @@ class Console: return i def __create_man_message(self, argv: list) -> AnyStr: + if len(argv) == 0: + return self.__man.get("man") x = argv[0] - if x in ['']: - return "man COMMAND" + if self.__alias.get(x) is None: + return i18n.man_command_not_found.format(x) + man_message = self.__man.get(x) - if man_message is None: - return "man: Manual message not found." if man_message: return man_message - return f'man: command "{x}" not found' + else: + return i18n.man_message_not_found + # noinspection PyStringFormat def __create_help_message(self, argv: list) -> AnyStr: self.__debug("creating help message") raw = False @@ -95,7 +92,7 @@ class Console: if raw: message += f"%-{max_len}s; %-{max_len_v}s; %s\n" % ("Key", "Function", "Description") else: - message += f" %-{max_len}s : %s\n" % ("Command", "Help message") + message += f" %-{max_len}s : %s\n" % (i18n.help_command, i18n.help_message) for k, v in self.__func.items(): doc = self.__desc.get(k) @@ -105,7 +102,7 @@ class Console: else: if doc is None: - doc = "No help message found" + doc = i18n.help_message_not_found message += f" %-{max_len}s : %s\n" % (k, doc) return message @@ -122,7 +119,7 @@ class Console: self.__alias.update(custom_completer or {key: None}) self.__alias["man"].update({key: None}) self.__func.update({key: {"f": func}}) - self.__man.update({key: f'htmlManual for command {key}\n{man}' if man else None}) + self.__man.update({key: f'html{i18n.man_for} {key}\n{man}' if man else None}) self.__desc.update({key: desc}) self.__update_completer() return self.__alias.copy() diff --git a/src/modules/i18n/files/en.json b/src/modules/i18n/files/en.json index 0270ee8..7447b66 100644 --- a/src/modules/i18n/files/en.json +++ b/src/modules/i18n/files/en.json @@ -1,8 +1,45 @@ { + "": "Basic phases", "hello": "Hello from KuiToi-Server!", - "config_file": "Use %s for config.", - "debug": "Getting new logging with DEBUG level!", - "config_info": "Server config: %s", - "init": "Initializing ready.", - "ready": "Server started!" + "config_path": "Use {} for config.", + "init_ok": "Initializing ready.", + "start": "Server started!", + "stop": "Goodbye!", + + "": "Server auth", + "auth_need_key": "BEAM key needed for starting the server!", + "auth_empty_key": "Key is empty!", + "auth_cannot_open_browser": "Cannot open browser: {}", + "auth_use_link": "Use this link: {}", + + "": "GUI phases", + "GUI_yes": "Yes", + "GUI_no": "No", + "GUI_ok": "Ok", + "GUI_cancel": "Cancel", + "GUI_need_key_message": "BEAM key needed for starting the server!\nDo you need to open the web link to obtain the key?", + "GUI_enter_key_message": "Please type your key:", + "GUI_cannot_open_browser": "Cannot open browser.\nUse this link: {}", + + "": "Command: man", + "man_message_man": "man - display the manual page for COMMAND.\nUsage: man COMMAND", + "help_message_man": "Display the manual page for COMMAND.", + "man_for": "Manual for command", + "man_message_not_found": "man: Manual message not found.", + "man_command_not_found": "man: command \"{}\" not found!", + + "": "Command: help", + "man_message_help": "help - display names and brief descriptions of available commands.\nUsage: help [--raw]\nThe `help` command displays a list of all available commands along with a brief description of each command.", + "help_message_help": "Display names and brief descriptions of available commands", + "help_command": "Command", + "help_message": "Help message", + "help_message_not_found": "No help message found", + + "": "Command: stop", + "man_message_stop": "stop - Just shutting down the server.\nUsage: stop", + "help_message_stop": "Server shutdown.", + + "": "Command: exit", + "man_message_exit": "exit - Just shutting down the server.\nUsage: stop", + "help_message_exit": "Server shutdown." } \ No newline at end of file diff --git a/src/modules/i18n/i18n-builtins.pyi b/src/modules/i18n/i18n-builtins.pyi index da8744e..86acae2 100644 --- a/src/modules/i18n/i18n-builtins.pyi +++ b/src/modules/i18n/i18n-builtins.pyi @@ -1,10 +1,90 @@ class i18n: - hello: str = "Hello from KuiToi-Server!" - config_file: str = "Use kuitoi.yml for config." - debug: str = "Getting new logging with DEBUG level!" - config_info: str = "Server config: %s" - init: str = "Initializing ready." - ready: str = "Server started!" -i18n_data = {"hello":"Hello from KuiToi-Server!","config_file":"Use kuitoi.yml for config.", - "debug":"Getting new logging with DEBUG level!","config_info":"Server config: %s", - "init":"Initializing ready.","ready":"Server started!"} + # Basic phases + hello: str = data["hello"] + config_path: str = data["config_path"] + init_ok: str = data["init_ok"] + start: str = data["start"] + stop: str = data["stop"] + + # Server auth + auth_need_key: str = data["auth_need_key"] + auth_empty_key: str = data["auth_empty_key"] + auth_cannot_open_browser: str = data["auth_cannot_open_browser"] + auth_use_link: str = data["auth_use_link"] + + # GUI phases + GUI_yes: str = data["GUI_yes"] + GUI_no: str = data["GUI_no"] + GUI_ok: str = data["GUI_ok"] + GUI_cancel: str = data["GUI_cancel"] + GUI_need_key_message: str = data["GUI_need_key_message"] + GUI_enter_key_message: str = data["GUI_enter_key_message"] + GUI_cannot_open_browser: str = data["GUI_cannot_open_browser"] + + # Command: man + man_message_man: str = data["man_message_man"] + help_message_man: str = data["help_message_man"] + man_for: str = data["man_for"] + man_message_not_found: str = data["man_message_not_found"] + man_command_not_found: str = data["man_command_not_found"] + + # Command: help + man_message_help: str = data["man_message_help"] + help_message_help: str = data["help_message_help"] + help_command: str = data["help_command"] + help_message: str = data["help_message"] + help_message_not_found: str = data["help_message_not_found"] + + # Command: stop + man_message_stop: str = data["man_message_stop"] + help_message_stop: str = data["help_message_stop"] + + # Command: exit + man_message_exit: str = data["man_message_exit"] + help_message_exit: str = data["help_message_exit"] + + data = { + "": "Basic phases", + "hello": "Hello from KuiToi-Server!", + "config_path": "Use {} for config.", + "init_ok": "Initializing ready.", + "start": "Server started!", + "stop": "Goodbye!", + + "": "Server auth", + "auth_need_key": "BEAM key needed for starting the server!", + "auth_empty_key": "Key is empty!", + "auth_cannot_open_browser": "Cannot open browser: {}", + "auth_use_link": "Use this link: {}", + + "": "GUI phases", + "GUI_yes": "Yes", + "GUI_no": "No", + "GUI_ok": "Ok", + "GUI_cancel": "Cancel", + "GUI_need_key_message": "BEAM key needed for starting the server!\nDo you need to open the web link to obtain the key?", + "GUI_enter_key_message": "Please type your key:", + "GUI_cannot_open_browser": "Cannot open browser.\nUse this link: {}", + + "": "Command: man", + "man_message_man": "man - display the manual page for COMMAND.\nUsage: man COMMAND", + "help_message_man": "Display the manual page for COMMAND.", + "man_for": "Manual for command", + "man_message_not_found": "man: Manual message not found.", + "man_command_not_found": "man: command \"{}\" not found!", + + "": "Command: help", + "man_message_help": "help - display names and brief descriptions of available commands.\nUsage: help [--raw]\nThe `help` command displays a list of all available commands along with a brief description of each command.", + "help_message_help": "Display names and brief descriptions of available commands", + "help_command": "Command", + "help_message": "Help message", + "help_message_not_found": "No help message found", + + "": "Command: stop", + "man_message_stop": "stop - Just shutting down the server.\nUsage: stop", + "help_message_stop": "Server shutdown.", + + "": "Command: exit", + "man_message_exit": "exit - Just shutting down the server.\nUsage: stop", + "help_message_exit": "Server shutdown." +} diff --git a/src/modules/i18n/i18n.py b/src/modules/i18n/i18n.py index 477fb77..4f216cc 100644 --- a/src/modules/i18n/i18n.py +++ b/src/modules/i18n/i18n.py @@ -16,12 +16,51 @@ from core.utils import get_logger class i18n: def __init__(self, data): + # Basic phases self.hello: str = data["hello"] - self.config_file: str = data["config_file"] - self.debug: str = data["debug"] - self.config_info: str = data["config_info"] - self.init: str = data["init"] - self.ready: str = data["ready"] + self.config_path: str = data["config_path"] + self.init_ok: str = data["init_ok"] + self.start: str = data["start"] + self.stop: str = data["stop"] + + # Server auth + self.auth_need_key: str = data["auth_need_key"] + self.auth_empty_key: str = data["auth_empty_key"] + self.auth_cannot_open_browser: str = data["auth_cannot_open_browser"] + self.auth_use_link: str = data["auth_use_link"] + + # GUI phases + self.GUI_yes: str = data["GUI_yes"] + self.GUI_no: str = data["GUI_no"] + self.GUI_ok: str = data["GUI_ok"] + self.GUI_cancel: str = data["GUI_cancel"] + self.GUI_need_key_message: str = data["GUI_need_key_message"] + self.GUI_enter_key_message: str = data["GUI_enter_key_message"] + self.GUI_cannot_open_browser: str = data["GUI_cannot_open_browser"] + + # Command: man + self.man_message_man: str = data["man_message_man"] + self.help_message_man: str = data["help_message_man"] + self.man_for: str = data["man_for"] + self.man_message_not_found: str = data["man_message_not_found"] + self.man_command_not_found: str = data["man_command_not_found"] + + # Command: help + self.man_message_help: str = data["man_message_help"] + self.help_message_help: str = data["help_message_help"] + self.help_command: str = data["help_command"] + self.help_message: str = data["help_message"] + self.help_message_not_found: str = data["help_message_not_found"] + + # Command: help + self.man_message_stop: str = data["man_message_stop"] + self.help_message_stop: str = data["help_message_stop"] + + # Command: exit + self.man_message_exit: str = data["man_message_exit"] + self.help_message_exit: str = data["help_message_exit"] + + self.data = data class MultiLanguage: @@ -45,13 +84,51 @@ class MultiLanguage: if language != "en": self.open_file() else: + # noinspection PyDictDuplicateKeys self.__data = { + "": "Basic phases", "hello": "Hello from KuiToi-Server!", - "config_file": "Use %s for config.", - "debug": "Getting new logging with DEBUG level!", - "config_info": "Server config: %s", - "init": "Initializing ready.", - "ready": "Server started!" + "config_path": "Use {} for config.", + "init_ok": "Initializing ready.", + "start": "Server started!", + "stop": "Goodbye!", + + "": "Server auth", + "auth_need_key": "BEAM key needed for starting the server!", + "auth_empty_key": "Key is empty!", + "auth_cannot_open_browser": "Cannot open browser: {}", + "auth_use_link": "Use this link: {}", + + "": "GUI phases", + "GUI_yes": "Yes", + "GUI_no": "No", + "GUI_ok": "Ok", + "GUI_cancel": "Cancel", + "GUI_need_key_message": "BEAM key needed for starting the server!\nDo you need to open the web link to obtain the key?", + "GUI_enter_key_message": "Please type your key:", + "GUI_cannot_open_browser": "Cannot open browser.\nUse this link: {}", + + "": "Command: man", + "man_message_man": "man - display the manual page for COMMAND.\nUsage: man COMMAND", + "help_message_man": "Display the manual page for COMMAND.", + "man_for": "Manual for command", + "man_message_not_found": "man: Manual message not found.", + "man_command_not_found": "man: command \"{}\" not found!", + + "": "Command: help", + "man_message_help": "help - display names and brief descriptions of available commands.\nUsage: help [--raw]\nThe `help` command displays a list of all available commands along with a brief description of each command.", + "help_message_help": "Display names and brief descriptions of available commands", + "help_command": "Command", + "help_message": "Help message", + "help_message_not_found": "No help message found", + + "": "Command: stop", + "man_message_stop": "stop - Just shutting down the server.\nUsage: stop", + "help_message_stop": "Server shutdown.", + + "": "Command: exit", + "man_message_exit": "exit - Just shutting down the server.\nUsage: stop", + "help_message_exit": "Server shutdown." } self.__i18n = i18n(self.__data) @@ -63,7 +140,8 @@ class MultiLanguage: self.__data.update(json.load(f)) return except JSONDecodeError: - self.log.error(f"Localisation \"{self.language}.json\" have JsonDecodeError. Using default localisation: en.") + self.log.error( + f"Localisation \"{self.language}.json\" have JsonDecodeError. Using default localisation: en.") except FileNotFoundError: self.log.warning(f"Localisation \"{self.language}.json\" not found; Using default localisation: en.") self.set_language("en")