diff --git a/.gitignore b/.gitignore index d9d181c..858ec90 100644 --- a/.gitignore +++ b/.gitignore @@ -130,3 +130,5 @@ dmypy.json .vscode .vscode/* + +alias.user.conf \ No newline at end of file diff --git a/README.md b/README.md index b22d7b3..7dff201 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,12 @@ Ice Shell(简称IShell,中文名“冰壳”),是一个Python编写的 **2024,回归更新咕咕咕!** +![Run Shell](./docs/img/runShell.png) + +(老版图片) + + + ## IShell设计初衷 往往我们在用Python完成某个小功能时,我们都会直接新建单个Python文件,写完之后就放在一个不知名的小角落,然后使用的时候直接双击运行。 @@ -95,6 +101,7 @@ IShell本质上作为一个模块化Shell,装载了一些小工具。 - 报错易寻找具体位置 ...... + # 使用方法 1. 下载最新版Release [点我前往Release](https://github.com/lanbinshijie/IceShell/releases) @@ -121,6 +128,12 @@ Demo输出 ![Demo Screen](./docs/img/bashDemo.png) +## Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=lanbinshijie/iceshell&type=Date)](https://star-history.com/#lanbinshijie/iceshell&Date) + + + # 参与开发 您可以联系我的 @@ -128,6 +141,7 @@ Demo输出 或者直接提交issue + ## 近期任务 - [X] 一键检测依赖并询问是否安装 diff --git a/main.py b/main.py index bf4008b..223780d 100644 --- a/main.py +++ b/main.py @@ -14,23 +14,28 @@ from tools.Phraser import PS1 from tools.Phraser import alias from misc.Info import StartAction +from tools.Configure import * # from models.bash import Ish + + ALIAS = alias() +PS1O = PS1() +CONFIGURATION = Configuration() # ish = Ish() def ExecuteModel(args, moduleName): if SelfCheck.CheckModel(moduleName): os.chdir(rf"{ProgramInfo.basedir}/models") if os.path.exists(f"{moduleName}.py"): - command = sys.executable + f" ./{moduleName}.py " + args + command = f"\"{sys.executable}\"" + f" ./{moduleName}.py " + args elif os.path.exists(f"./{moduleName}/main.py"): os.chdir(rf"./{moduleName}") - command = sys.executable + f" ./main.py " + args + command = f"\"{sys.executable}\"" + f" ./main.py " + args else: if ALIAS.exsist(moduleName): command = ALIAS.get(moduleName) - command = sys.executable + f" ./{command}.py " + args + command = f"\"{sys.executable}\"" + f" ./{command}.py " + args os.system(command) os.chdir(rf"{ProgramInfo.basedir}") # elif moduleName in @@ -38,13 +43,58 @@ def ExecuteModel(args, moduleName): # 因为 checkModel 会给出提示信息,所以就不用给出了 ... +def SafetyMode(lock): + if int(lock): + print(Colors.RED + Logo.div_line_n_m + Colors.END) + print(Colors.RED + "Safety Mode is on." + Colors.END) + print(Colors.RED + "Please input your password to continue." + Colors.END) + print(Colors.RED + Logo.div_line_n_m + Colors.END + "\n") + + while True: + try: + userName = input("Username: >> ") + password = input("Password: >> ") + passwordHashed = Hash(password) + if userName == CONFIGURATION.userName and passwordHashed == str(CONFIGURATION.passwordHashed).split("%%")[0]: + print() + print(Colors.GREEN + Logo.div_line_n_m + Colors.END) + print(Colors.GREEN + "Welcome back, " + userName + "!" + Colors.END) + print(Colors.GREEN + Logo.div_line_n_m + Colors.END + "\n") + break + else: + print() + print(Colors.RED + Logo.div_line_n_m + Colors.END) + print(Colors.RED + "Wrong username or password!" + Colors.END) + print(Colors.RED + Logo.div_line_n_m + Colors.END + "\n") + continue + except KeyboardInterrupt: + print("\nBye~") + exit(0) + def IceShell(): extra = "" commandN = input(Colors.RED + extra + PS1.paraphraser() + Colors.END) command = commandN.split(" ") + SECLEVEL = 0 if command[0] == "q": print("Bye~") exit(0) + elif CONFIGURATION.ifexist(str(commandN.split("=")[0]).strip()[1:]) and commandN.startswith("$"): + conf = commandN.split("=") + conf = [str(item).strip() for item in conf] + conf[0] = conf[0][1:] + sec = CONFIGURATION.get(conf[0]).split("%%") # 安全等级,如果越大,需要的等级越高 + sec = int(sec[1]) if len(sec) > 1 else 0 + if sec > SECLEVEL: + Error.printError(30001) + print() + return + if len(conf) == 2: + CONFIGURATION.set_value(conf[0], conf[1]) + CONFIGURATION.save() + print(Colors.GREEN + "Configuration saved." + Colors.END) + elif len(conf) == 1: + print(CONFIGURATION.get(conf[0])) elif command[0] in ProgramInfo.registered_modules or "*" in ProgramInfo.registered_modules: ExecuteModel(" ".join(command[1:]), command[0]) else: @@ -62,7 +112,7 @@ def IceShell(): if os.path.exists(mfol_stp): ExecuteModel(mdls_stp, "bash") print(Colors.BLUE + Logo.div_line_n_m + Colors.END + "\n") - + pass_ = SafetyMode(CONFIGURATION.lock) while True: try: IceShell() diff --git a/misc/Error.py b/misc/Error.py index f1660b4..11dc54c 100644 --- a/misc/Error.py +++ b/misc/Error.py @@ -15,6 +15,12 @@ class Error: "title": "Deleting error!", "solve": "Please check pip\'s environment" }, + 30001: { + "typer": "error", + "model": "SECURITY", + "title": "PERMISSION DENIED!", + "solve": "UPGRADE YOUR PERMISSION" + }, } def printError(code: int): err = Error.error_list[code] diff --git a/misc/Info.py b/misc/Info.py index ff2334a..17aef83 100644 --- a/misc/Info.py +++ b/misc/Info.py @@ -10,7 +10,7 @@ class ProgramInfo: # Program Version version = "v1.0.0-alphav1.2.1" author = "Lanbin" - using_libs = ["os", "sys"] + using_libs = ["os", "sys", "re"] models_path = r"./models/" registered_modules = ["print", "downlib", "dellib", "scan", "models", "shell", "alias", "bash", "*"] debug_mode = True diff --git a/models/alias.py b/models/alias.py index 00abdcd..4a75877 100644 --- a/models/alias.py +++ b/models/alias.py @@ -13,27 +13,38 @@ class Aliaser: """ This class manages command aliases stored in a configuration file. """ - def __init__(self, path=ProgramInfo.basedir + "/tools/alias.conf"): + def __init__(self, path=ProgramInfo.basedir + "/tools/alias.conf", userPath=ProgramInfo.basedir + "/tools/alias.user.conf"): self.path = path + self.userPath = userPath self.alias = alias(path) - def _update_file(self, config): + def _update_file(self, config, user=True): """Writes the updated configuration to the file.""" - with open(self.path, "w") as f: - for alias, command in config.items(): - f.write(f"{alias}={command}\n") + if user: + with open(self.userPath, "w") as f: + for alias, command in config.items(): + f.write(f"{alias}={command}\n") + else: + with open(self.path, "w") as f: + for alias, command in config.items(): + f.write(f"{alias}={command}\n") - def add(self, alias, command): + def add(self, alias, command, user=True): """Adds a new alias.""" - if self.alias.exist(alias): + if self.alias.exsist(alias): print(f"Alias {alias} already exists!") return - with open(self.path, "a") as f: - f.write(f"\n{alias}={command}") + + if user: + with open(self.userPath, "a") as f: + f.write(f"\n{alias}={command}") + else: + with open(self.path, "a") as f: + f.write(f"\n{alias}={command}") def set(self, alias, command): """Sets an existing alias to a new command.""" - if not self.alias.exist(alias): + if not self.alias.exsist(alias): print(f"Alias {alias} does not exist!") return config = self.alias.getAll() @@ -42,7 +53,7 @@ def set(self, alias, command): def delete(self, alias): """Deletes an existing alias.""" - if not self.alias.exist(alias): + if not self.alias.exsist(alias): print(f"Alias {alias} does not exist!") return config = self.alias.getAll() diff --git a/tools/Configure.py b/tools/Configure.py new file mode 100644 index 0000000..f6c75ab --- /dev/null +++ b/tools/Configure.py @@ -0,0 +1,124 @@ +from misc.Info import ProgramInfo +import hashlib + +def Hash(password: str, salt: str = "IceShell"): + return hashlib.sha256((password + salt).encode("utf-8")).hexdigest() + +class ConfigurationData: + def __init__(self, userName="root", passwordHashed=Hash("root"), config={}): + self.userName = userName + self.passwordHashed = passwordHashed + + self.variables = [] + + self.config = config + self.phrase() + if self.config is not {}: + self.loadConfig(config) + self.updateVarList() + + def phrase(self): + # 设置config内容 + self.config = { + "userName": self.userName, # 设置用户名 + "passwordHashed": self.passwordHashed, # 设置密码 + "lock": "0", + "variables": self.variables, + } + self.updateVarList() + + def updateVarList(self): + # self.variables设置为config的key + self.variables = [] + for key in self.config.keys(): + self.variables.append(key) + self.config["variables"] = self.variables + + + def loadConfig(self, config): + # 传入的config是configuration读取的key=value + for key, value in config.items(): + self.config[key] = value + return self.config + + def setKeyValue(self, key: str, value): + # 设置key的值为value + self.config[key] = value + + def __call__(self): + return self.config + + def update_internal_attributes(self, key, value): + if hasattr(self, key): + setattr(self, key, value) + +class Configuration: + def __init__(self, configPath=ProgramInfo.basedir + "/tools/configure.conf", configurationData: ConfigurationData | None = None): + self.__dict__['configPath'] = configPath + if configurationData is None: + self.readConfig(configPath) + else: + self.__dict__['config'] = configurationData + self.save() + + def readConfig(self, configPath): + # 数据格式:key=value + # 读取配置文件,然后创建一个ConfigurationData数据,尝试填入数据 + try: + with open(configPath, "r", encoding="utf-8") as f: + config = f.read().split("\n") + # 删除所有config为空或者没有等号在中间的行 + config = [line for line in config if "=" in line] + + config = { + key: value for key, value in [line.split("=") for line in config] + } + self.config = ConfigurationData(config=config) + except FileNotFoundError as e: + print(e) + self.config = ConfigurationData() + except Exception as e: + print(e) + self.config = ConfigurationData() + + + def get(self, name): + return self.config()[name] + + def __call__(self): + return self.config() + + def __getattr__(self, name): + if 'config' in self.__dict__: + return self.__dict__['config']()[name] + else: + raise AttributeError(f"No such attribute: {name}") + + def __setattr__(self, name, value): + if name in ['config', 'configPath'] or '_config' not in self.__dict__: + self.__dict__[name] = value + elif name in self.__dict__['config'](): + self.__dict__['config'].setKeyValue(name, value) + self.__dict__['config'].update_internal_attributes(name, value) + else: + object.__setattr__(self, name, value) + def save(self): + with open(self.configPath, "w", encoding="utf-8") as f: + for key, value in self.config().items(): + value = str(value) + f.write(key + "=" + value + "\n") + f.flush() + + def ifexist(self, configKey): + if configKey in self.config(): + return True + else: + return False + + def set_value(self, key, value): + if key in self.config(): + self.config.setKeyValue(key, value) + self.config.update_internal_attributes(key, value) + else: + raise KeyError(f"No such configuration key: {key}") + diff --git a/tools/Phraser.py b/tools/Phraser.py index 4985705..6d18854 100644 --- a/tools/Phraser.py +++ b/tools/Phraser.py @@ -19,9 +19,17 @@ def paraphraser(prompt="[IShell] %u> "): return prompt class alias: - def __init__(self, path=ProgramInfo.basedir + "/tools/alias.conf"): - with open(path, "r") as f: - conf = f.read() + def __init__(self, path=ProgramInfo.basedir + "/tools/alias.conf", userPath=ProgramInfo.basedir + "/tools/alias.user.conf", user=True): + if user: + try: + with open(userPath, "r") as f: + conf = f.read() + except: + with open(path, "r") as f: + conf = f.read() + else: + with open(path, "r") as f: + conf = f.read() conf = conf.split("\n") self.conf_dict = {} @@ -44,9 +52,13 @@ def getAll(self): self.reRead() return self.conf_dict - def reRead(self, path="../tools/alias.conf"): - with open(path, "r") as f: - conf = f.read() + def reRead(self, path="../tools/alias.conf", userPath="../tools/alias.user.conf", user=True): + if user: + with open(userPath, "r") as f: + conf = f.read() + else: + with open(path, "r") as f: + conf = f.read() conf = conf.split("\n") self.conf_dict = {} diff --git a/tools/configure.conf b/tools/configure.conf new file mode 100644 index 0000000..69cf0a0 --- /dev/null +++ b/tools/configure.conf @@ -0,0 +1,4 @@ +userName=Lanbin +passwordHashed=8ac8c452cf1deb9785b4d8a3a778f5b8d80a096f8acc9968b4e54234881a4463%%1 +lock=0 +variables=['userName', 'passwordHashed', 'lock', 'variables']