From 807390a266ca7a9b0417cb8da73f640119d629e4 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sun, 25 Aug 2024 13:22:11 +0700 Subject: [PATCH 01/42] Create hello.txt --- libs/hello.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 libs/hello.txt diff --git a/libs/hello.txt b/libs/hello.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/libs/hello.txt @@ -0,0 +1 @@ + From 2393e6efd4db63c3cad614a69daaa1c0c5026960 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sun, 25 Aug 2024 13:24:11 +0700 Subject: [PATCH 02/42] Add files via upload Ini adalah module dari aplikasi, dan jangan pernah dirubah --- libs/capture_network.py | 36 ++ libs/cmd_filter.py | 725 +++++++++++++++++++++++++++++++++++++++ libs/commandcheck.py | 456 ++++++++++++++++++++++++ libs/errrorHandler.py | 68 ++++ libs/filemanager.py | 384 +++++++++++++++++++++ libs/filterror.py | 682 ++++++++++++++++++++++++++++++++++++ libs/hellpper.py | 264 ++++++++++++++ libs/helperegex.py | 535 +++++++++++++++++++++++++++++ libs/https.py | 112 ++++++ libs/randoms.py | 134 ++++++++ libs/system_manajemen.py | 84 +++++ libs/timeout.py | 47 +++ libs/titlecommand.py | 25 ++ 13 files changed, 3552 insertions(+) create mode 100644 libs/capture_network.py create mode 100644 libs/cmd_filter.py create mode 100644 libs/commandcheck.py create mode 100644 libs/errrorHandler.py create mode 100644 libs/filemanager.py create mode 100644 libs/filterror.py create mode 100644 libs/hellpper.py create mode 100644 libs/helperegex.py create mode 100644 libs/https.py create mode 100644 libs/randoms.py create mode 100644 libs/system_manajemen.py create mode 100644 libs/timeout.py create mode 100644 libs/titlecommand.py diff --git a/libs/capture_network.py b/libs/capture_network.py new file mode 100644 index 0000000..0d4b679 --- /dev/null +++ b/libs/capture_network.py @@ -0,0 +1,36 @@ +import json +from selenium import webdriver +from selenium.webdriver.support.ui import WebDriverWait +from chromedriver_py import binary_path +# Initialize Chrome WebDriver with performance logging enabled +svc = webdriver.ChromeService(executable_path=binary_path) +chrome_options = webdriver.ChromeOptions() +chrome_options.add_argument('--remote-debugging-port=9222') # Enable DevTools Protocol +chrome_options.add_argument("--incognito") +chrome_options.add_argument('--enable-logging') +chrome_options.add_argument("--ignore-certificate-errors") +chrome_options.add_argument("--disable-web-security") +chrome_options.add_argument("--allow-running-insecure-content") +driver = webdriver.Chrome(service=svc, options=chrome_options) + + +# Navigate to the target website +driver.get("https://www.linkvideo.download/") +WebDriverWait(driver, 30).until( + lambda d: d.execute_script('return document.readyState') == 'complete' +) +test = driver.execute_async_script( + """ + var performance = window.performance || window.mozPerformance || window.msPerformance || window.webkitPerformance || { }; + var callback = arguments[arguments.length - 1]; + if (performance) { + if (typeof(performance.getEntries)==='function'){ + performance = performance.getEntriesByType('resource').map(entry => entry.name); + }; + callback(performance); + } else { callback(null);}""" + ) +#response = driver.execute_async_script("""var performance = window.performance || window.mozPerformance || window.msPerformance || window.webkitPerformance || { }; return performance;""") +print(test) +# Close the WebDriver +driver.quit() \ No newline at end of file diff --git a/libs/cmd_filter.py b/libs/cmd_filter.py new file mode 100644 index 0000000..10176bd --- /dev/null +++ b/libs/cmd_filter.py @@ -0,0 +1,725 @@ +import json, subprocess, os, sys, threading, time, psutil, platform, random + +from typing import Iterable, Container, Protocol, List, Tuple + +from dataclasses import dataclass + +from functools import wraps, lru_cache +try: + from .helperegex import split, findpositions, re as regex + from .randoms import random, randomDigits + +except: + try: + from helperegex import split, findpositions, re as regex + from randoms import random, randomDigits + except: + from libs.helperegex import split, findpositions, re as regex + from libs.randoms import random, randomDigits + + +try: + rangeX = range +except: + rangeX = xrange + range = xrange + + + +@dataclass(order=True) +class ResultContainer: + results = ( + [] + ) # Mutable - anything inside this list will be accesable anywher in your program + + +def save_result(cls): + def decorator(func): + def wrapper(args, *kwargs): + # get result from the function + + func_result = func(args, *kwargs) + + # Pass the result into mutable list in our ResultContainer class + + cls.results.append(func_result) + + # Return result from the function + + return func_result + + return wrapper + + return decorator + + +@save_result(ResultContainer) +def func(a, b): + return a * b + +@lru_cache(maxsize=128) +def remwithre(text, there=regex.compile(regex.escape("=") + ".*")): + return there.sub("", text) + +@lru_cache(maxsize=128) +def compiltesV2(cmd: str) -> List[str]: + def find_indices_of_substring(substring, string): + return [m.start() for m in regex.finditer(regex.escape(substring), string)] + + if "#" in cmd: + command_part, comment_part = cmd.split("#", 1) + comment_part = "#" + comment_part + else: + command_part = cmd + comment_part = "" + + # Handle quotes + single_quote_count = find_indices_of_substring("'", command_part).__len__() + double_quote_count = find_indices_of_substring('"', command_part).__len__() + + if single_quote_count % 2 == 0 and single_quote_count > 0: + record = findpositions(r"'(.*?)'", command_part) + keysplit = "'" + elif double_quote_count % 2 == 0 and double_quote_count > 0: + record = findpositions(r"\"(.*?)\"", command_part) + keysplit = '"' + else: + return + + recordlist = [substr for substr in record] + + for idx, substr in enumerate(recordlist): + recordlist[idx] = list(substr) + placeholder = f"__PLACEHOLDER_{idx}__" + command_part = command_part.replace(recordlist[idx][0][0], placeholder) + + # Preserve & inside quotes and comments + segments: List[Tuple[str]] = [] + in_single_quote = in_double_quote = False + + i:int = 0 + while i < len(command_part): + char = command_part[i] + if char == "'": + in_single_quote = not in_single_quote + elif char == '"': + in_double_quote = not in_double_quote + + if char == "&" and not (in_single_quote or in_double_quote): + if i + 1 < len(command_part) and command_part[i + 1] == "&": + segments.append("&&") + i += 1 + else: + segments.append("&") + else: + start = i + while i < len(command_part) and ( + char != "&" + or (in_single_quote or in_double_quote) + or (i + 1 < len(command_part) and command_part[i + 1] != "&") + ): + i += 1 + if i < len(command_part): + char = command_part[i] + segments.append(command_part[start:i]) + continue + i += 1 + + # Reconstruct commands based on segments + commands: List[Tuple[str]] = [] + outputs:List[Tuple[str]] = [] + command:str = "" + for segment in segments: + if segment == "&&": + commands.append(command.strip()) + command = "" + else: + command += segment + + if command: + commands.append(command.strip()) + + for command in commands: + # Restore quoted substrings from placeholders + for i, substr in enumerate(recordlist): + placeholder = f"__PLACEHOLDER_{i}__" + command = command.replace(placeholder, recordlist[i][0][0]) + outputs.append(command) + return outputs + +@lru_cache(maxsize=128) +def compiltesV3(cmd: str) -> List[str]: + def find_indices_of_substring(substring, string): + return [m.start() for m in regex.finditer(regex.escape(substring), string)] + + def find_positions(pattern, string): + return [(m.start(), m.group(0)) for m in regex.finditer(pattern, string)] + + # Split the command and comments + if "#" in cmd: + command_part, comment_part = cmd.split("#", 1) + comment_part = "#" + comment_part + else: + command_part = cmd + comment_part = "" + + # Handle quotes + single_quote_count = len(find_indices_of_substring("'", command_part)) + double_quote_count = len(find_indices_of_substring('"', command_part)) + + if single_quote_count % 2 == 0 and single_quote_count > 0: + quoted_strings = find_positions(r"'(.*?)'", command_part) + quote_char = "'" + elif double_quote_count % 2 == 0 and double_quote_count > 0: + quoted_strings = find_positions(r'"(.*?)"', command_part) + quote_char = '"' + else: + return [] + + # Replace quoted substrings with placeholders + for i, (pos, substr) in enumerate(quoted_strings): + placeholder = f"__PLACEHOLDER_{i}__" + command_part = ( + command_part[:pos] + placeholder + command_part[pos + len(substr) :] + ) + + # Preserve & inside quotes and comments + segments: List[Tuple[str]] = [] + i:int = 0 + while i < len(command_part): + char = command_part[i] + if char == quote_char: + # Skip quoted parts + i += 1 + while i < len(command_part) and command_part[i] != quote_char: + i += 1 + i += 1 + elif char == "&": + if i + 1 < len(command_part) and command_part[i + 1] == "&": + segments.append("&&") + i += 1 + else: + segments.append("&") + else: + start = i + while i < len(command_part) and ( + command_part[i] != "&" + or (i + 1 < len(command_part) and command_part[i + 1] != "&") + ): + i += 1 + segments.append(command_part[start:i]) + continue + i += 1 + + # Reconstruct commands based on segments + commands: List[Tuple[str]] = [] + command:str = "" + for segment in segments: + if segment == "&&": + commands.append(command.strip()) + command = "" + else: + command += segment + + if command: + commands.append(command.strip()) + + # Restore quoted substrings from placeholders + outputs: List[Tuple[str]] = [] + for command in commands: + for i, (pos, substr) in enumerate(quoted_strings): + placeholder = f"__PLACEHOLDER_{i}__" + command = command.replace(placeholder, substr) + outputs.append(command) + + return outputs + +@lru_cache(maxsize=128) +def compiltes(string: str) -> List[str]: + if string == "": + return [] + + _output: List[str] = [] + + xout = findpositions(r"'(.*?)?'|\"(.*?)?\"", string) + lenght = len(xout) + cv = 0 + checkpoint: Tuple[str] = () + checkpoint2: List[Tuple[str]] = [] + place = "<%%" + newstring = string + keyplace = "" + + maxpend: List[int] = [] + digit: str = "1" + xct = regex.finditer(r"<%%(.*?)?>", string) + for xc in xct: + if xc: + if xc.group(0).count(" ") == 0: + maxdigit = xc.group(1) + maxpend.append(int(maxdigit)) + + if maxpend: + _s_s = len(str(max(maxpend))) + 1 + digit = str(_s_s) + + score_record: List[str] = [] + for y in xout: + for x in y: + if "&&" in x[0]: + keyplace = "&&" + elif "&" in x[0]: + keyplace = "&" + + if lenght != 0: + score = randomDigits(int(digit)) + while score in score_record: + score = randomDigits(int(digit)) + score_record.append(score) + + newword = "".join([place, str(score), ">"]) + newpad = regex.sub(r"&&|&", newword, x[0]) + if x[0].count("&") == 0: + cv += 1 + else: + if lenght == len(xout) - cv: + pass + else: + minus = len(newword) + len(keyplace) + checkpoint2.append(( + x[1][0] + minus - len(keyplace), + x[1][-1] + minus + 1, + )) + + string = string.replace(x[0], newpad, 1) + checkpoint = checkpoint + ((x[0], newword, score, keyplace),) + lenght -= 1 + + # Handle splitting based on characters following '&' + temp_output = [] + i = 0 + while i < len(string): + if string[i] == "&": + if i + 1 < len(string) and (string[i + 1] == " " or string[i + 1] == "&"): + # Split and handle based on next character + if i > 0: + temp_output.append(string[:i].strip()) + string = string[i + 1:].lstrip() + i = 0 + else: + i += 1 + else: + i += 1 + + if string: + temp_output.append(string.strip()) + # Process checkpoints to replace placeholders + for x in temp_output: + for y in checkpoint: + if y[1] in x: + oldstring = regex.search(r"'(.*?)?'", x) or regex.search(r'"(.*?)?"', x) + if oldstring: + x = x.replace(oldstring.group(0), y[0], 1) + else: + x = x.replace(y[1], y[3]) + _output.append(x.strip()) + + return _output + + +@lru_cache(maxsize=128) +def functionclean(functions, powershell=True): + # fff = regex.search(r'function(.*)\(', functions, regex.MULTILINE) + + fff = regex.search(r"function(.*)\{", functions, regex.DOTALL or regex.MULTILINE) + + if fff: + remove_scp = regex.sub(r"\s+", "", fff.group(1), regex.UNICODE) + + _functioname = regex.match(r"(.\S+)?\(", remove_scp).group(1) + + if _functioname.count("(") != 0 and _functioname.endswith("}") == False: + _functioname = regex.match("(.*?)\(", _functioname).group(1) + + remove_scp = regex.match("(.*?){", remove_scp).group(1) + + # print(fff.group(1), "\nFunName:",_functioname, "\nRem:", remove_scp, "\n") + + _argsx = regex.match(r"{names}\((.*)\)".format(names=_functioname), remove_scp) + + _argsx = _argsx.group(1) + + if powershell == True: + # print(_argsx) + + # print(regex.findall(r"(\[.\S+\])", "([string]$helllo=$fo, [int]$dddd)".format(args=_argsx))) + + stringsl = regex.sub(r"(,)", r"\1 ", "({args})".format(args=_argsx)) + + for scleans in regex.findall(r"(\[.\S+\])", stringsl): + _argsx = _argsx.replace(scleans, "", 1) + + _argsx = _argsx.replace("$", "") + + _spaces = "" + + if _argsx.count('"') != 0 and _argsx.count('"') % 2 == 0: + _spaces = '"' + + elif _argsx.count("'") != 0 and _argsx.count("'") % 2 == 0: + _spaces = "'" + + else: + reps = regex.sub(r"(=)", r"\1'", _argsx) + + if reps: + xappend: List[Tuple[str]] = [] + + for xsplit in reps.split(","): + if xsplit.count("'") != 0: + xappend.append(str(xsplit[: xsplit.__len__()] + "'")) + + else: + xappend.append(str(xsplit + "=None")) + + _argsx = ",".join(xappend) + + if _spaces: + clos = regex.findall(r"{args}(.*){args}".format(args=_spaces), _argsx) + + xxs: List[Tuple[str]] = [] + + if clos.__len__() != 0: + for xin in clos: + _argsx = _argsx.replace(xin, "", 1) + + splitz = _argsx.split(",") + + for xin in splitz: + if xin.find("=") != -1: + xxs.append(xin) + + elif xin.find("=") == -1: + xxs.append(xin + "=''") + + splitz = None + + _argsx = ", ".join(xxs) + + xxs = None + + functionmake = """def {functioname}({arguments}):pass""".format( + functioname=_functioname, arguments=_argsx + ) + + exec( + functionmake + + "\nf_code = {functioname}.__code__".format(functioname=_functioname) + ) + + get_arguments = eval( + """f_code.co_varnames[:f_code.co_argcount + f_code.co_kwonlyargcount]""" + ) + + """if _argsx.count("=") !=0: + + xcs = [] + + for a in _argsx.split(",", _argsx.count("=")): + + if a.find("=") != -1: + + xcs.append(remwithre( _argsx))""" + + return dict((x, y) for x, y in [(_functioname, list(get_arguments))]) + + # except: + + # return _functioname, [] + +@lru_cache(maxsize=128) +def classclean(classname, powershell=True): + classreg = regex.search(r"class(.*?)\{", classname, regex.DOTALL or regex.MULTILINE) + + if classreg: + _argsx = "" + + remove_scp = regex.sub(r"\s+", "", classreg.group(1), regex.UNICODE) + + try: + _classsname = regex.match(r"(.\S+)?\(", remove_scp).group(1) + + except: + _classsname = regex.match(r"(.\S+)?", remove_scp).group(1) + + if powershell == True: + argsx_ = classreg.group(1) + + _argsx = regex.match( + r"{names}+\((.*)\)".format(names=_classsname), remove_scp + ) + + if _argsx: + stringsl = regex.sub( + r"(,)", r"\1 ", "({args})".format(args=_argsx.group(1)) + ) + + _argsx = _argsx.group(1) + + else: + pass + + for scleans in regex.findall(r"(\[.\S+\])", stringsl): + _argsx = _argsx.replace(scleans, "", 1) + + _argsx = _argsx.replace("$", "") + + # print( regex.search(r"{(.*)}" , classname, regex.DOTALL).group(1)) + + # print( r"{xxgro}(.*){endsx}".format(xxgro= classreg.group(0), endsx = "\}") ) + + if _argsx.__len__() == 0: + argsx_ = regex.search(r"{(.*)}", classname, regex.DOTALL) + + if argsx_: + if powershell == True: + splits = argsx_.group(1).split("\n") + + sout = "" + + for sout in splits: + init_function = regex.match( + r"(.*){names}+\((.*)\)?".format(names=_classsname), + sout.strip(), + ) + + # print("after:", init_function) + + if sout.strip().startswith("[") and init_function: + if init_function.group(1): + sout = sout.replace(init_function.group(1), "", 1) + + if sout.endswith("{"): + sout = sout[:-1].strip() + + break + + elif sout.strip().startswith(_classsname) and init_function: + # if init_function.group(1): + + if init_function.group(0): + sout = init_function.group(0).strip()[:-1] + + elif sout and sout.endswith("{"): + sout = sout.strip()[:-1] + + break + + if sout: + s_argsx = regex.match( + r"{names}+\((.*)\)".format(names=_classsname), sout + ) + + cleansub = regex.sub( + r"(,)", r"\1 ", "({args})".format(args=s_argsx.group(1)) + ) + + for scleans in regex.findall(r"(\[.\S+\])", cleansub): + sout = sout.replace(scleans, "") + + sout = regex.search(r"\((.*?)?\)", sout.replace("$", "")) + + if sout: + _argsx = sout.group(1).strip() + + if _argsx.count("="): + xx = "function xx({args})".format(args=_argsx) + cleans = functionclean(xx + "{ pass}", True) + _argsx = ",".join(cleans["xx"]) + + functionmake = """def {_classsname}({arguments}):pass""".format( + _classsname=_classsname, arguments=_argsx + ) + + exec( + functionmake + + "\nf_code = {_classsname}.__code__".format(_classsname=_classsname) + ) + + get_arguments = eval( + """f_code.co_varnames[:f_code.co_argcount + f_code.co_kwonlyargcount]""" + ) + + return dict((x, y) for x, y in [(_classsname, list(get_arguments))]) + + +# functions = """function max_x2([string]$helllo='$fo', [int]$dddd){ pass }""" + +# print(functionclean(functions=functions, powershell=True)) + +def map_function_arguments(class_str): + # Regex untuk menemukan fungsi beserta argumennya + func_pattern = regex.compile(r'[\[.*?\]]?(\w+)\((.*?)\)') + matches = func_pattern.findall(class_str) + + func_args_map = {} + for match in matches: + func_name, args_str = match + # Memisahkan argumen + args = [arg.strip() for arg in args_str.split(',')] + func_args_map[func_name] = args + + return func_args_map +#print(map_function_arguments(classname)) + +def validate_folder(path: str) -> bool: + """ + Validates whether the given path is within the directory of the script. + + Parameters: + - path (str): The folder path to validate. + + Returns: + - bool: True if the path is valid and within the script directory, False otherwise. + """ + # Absolute path of the script's directory + script_dir = os.path.abspath(os.path.dirname(__file__)).replace("\\", "/") + # Convert to lowercase and normalize paths + script_dir = script_dir.lower() + path = path.lower().replace("\\", "/") + + n = str(script_dir).split("/") + Syscript = str("/".join(n[: n.__len__() - 1])).strip() + # Check if path starts with the script directory path + if path.startswith(Syscript) or path.startswith(script_dir): + return False + return True + +def shorten_path(path: str, max_length: int) -> str: + """ + Shorten a given path to a specified maximum length, inserting '...' + to represent omitted sections of the path. + + Parameters: + - path (str): The full path to shorten. + - max_length (int): The maximum length of the shortened path. + + Returns: + - str: The shortened path. + """ + if len(path) <= max_length: + return path + + # Replace backslashes with forward slashes for consistency + parts = path.replace("\\", "/").split("/") + + # Handle Windows drive letter + if ":" in parts[0]: + drive = parts.pop(0) + "/" + else: + drive = "" + + # If the path is too long, we need to shorten it + result = drive + parts[0] + "/.../" + parts[-1] + + # Remove parts from the middle until the length is acceptable + while len(result) > max_length and len(parts) > 2: + parts.pop(1) + result = drive + parts[0] + "/.../" + parts[-1] + + # If it's still too long, truncate the last part + if len(result) > max_length: + part_length = max_length - len(drive) - 6 # 6 for "/.../" + if part_length > 0: + truncated_last_part = parts[-1][:part_length] + "..." + result = drive + parts[0] + "/.../" + truncated_last_part + else: + result = drive + "..." + + return result + +def maxsize(): + usage = round(psutil.disk_usage(get_system_partitions()[0]).percent, 2) + try: + MAX_INT = sys.maxsize + except: + MAX_INT = sys.maxint + return round(MAX_INT / usage) + + +def get_system_partitions(): + platformname = platform.system().lower() + if platformname == "windows": + partitions = ( + os.popen("wmic logicaldisk get name") + .read() + .strip() + .replace("\n", "") + .split("\n")[1:] + ) + elif platformname == "linux" or platformname == "unix" or platformname == "darwin": + partitions = ( + os.popen("df -h | grep \"^/dev/\" | awk '{print $1}'") + .read() + .strip() + .replace("\n", "") + .split("\n") + ) + else: + partitions: List[Tuple[str]] = [] + if partitions: + for i in rangeX(0, partitions.__len__() - 1): + try: + if partitions[i].__len__() > 0: + pass + else: + del partitions[i] + except: + pass + return partitions + + +def safe_load_json(json_string): + try: + # Memeriksa ukuran JSON + if ( + json_string.__len__() > maxsize() + ): # Tetapkan batasan ukuran sesuai kebutuhan + raise ValueError("JSON data too large") + + # Mengurai JSON + data = json.loads(json_string) + + # Validasi struktur JSON + if not isinstance(data, dict): # Contoh validasi sederhana + raise ValueError("Invalid JSON structure: expected a dictionary") + + return data + except json.JSONDecodeError as e: + pass + except ValueError as e: + pass + except Exception as e: + pass + + return None + + +def filter_json(data, keys): + """ + Memfilter data JSON berdasarkan kunci yang diberikan. + + :param data: Data JSON yang akan difilter (dalam bentuk dict atau list). + :param keys: Daftar kunci yang ingin diambil. + :return: JSON yang sudah difilter sebagai string. + """ + if isinstance(data, dict): + return {k: data.get(k) for k in keys if k in data} + + elif isinstance(data, list): + return [{k: item.get(k) for k in keys if k in item} for item in data] + +#https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x64.exe +#https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x86.exe +#https://download.microsoft.com/download/0/6/4/064F84EA-D1DB-4EAA-9A5C-CC2F0FF6A638/vc_redist.x64.exe +#https://download.microsoft.com/download/0/6/4/064F84EA-D1DB-4EAA-9A5C-CC2F0FF6A638/vc_redist.x86.exe \ No newline at end of file diff --git a/libs/commandcheck.py b/libs/commandcheck.py new file mode 100644 index 0000000..e3eb15a --- /dev/null +++ b/libs/commandcheck.py @@ -0,0 +1,456 @@ +try: + from .helperegex import fullmacth +except: + from helperegex import fullmacth + +from os import getppid, getenv +import os, subprocess, threading, time, psutil, ctypes +from functools import lru_cache +from ctypes import wintypes +try: + from GPUtil import getGPUs +except: + def getGPUs(): + # Contoh fungsi untuk mendapatkan penggunaan GPU + # Implementasi nyata bergantung pada library dan hardware yang digunakan + # Untuk sekarang kita kembalikan None, artinya GPU tidak digunakan + return None + +def yime(): + # Get CPU usage percentage + cpu_usage = psutil.cpu_percent() + + # Get GPU usage percentage if available + gpu_usage = getGPUs() + + if gpu_usage is not None: + usage = gpu_usage + else: + usage = cpu_usage + + # Get RAM usage percentage + ram_usage = psutil.virtual_memory().percent + + # Determine the divisor based on usage level + if usage > 50: + cpu_usage_divided = usage / 20 # Higher divisor for higher usage + else: + cpu_usage_divided = usage / 10 # Lower divisor for lower usage + + if ram_usage > 50: + ram_usage_divided = ram_usage / 20 # Higher divisor for higher usage + else: + ram_usage_divided = ram_usage / 10 # Lower divisor for lower usage + + # Adjust for very low usage values + if cpu_usage_divided < 0.1: + cpu_usage_divided *= 2 + if ram_usage_divided < 0.1: + ram_usage_divided *= 2 + + # Calculate average usage and return + data = [round(cpu_usage_divided, 1), round(ram_usage_divided, 1)] + outputdata = round(sum(data) / len(data), 1) / 2 + if outputdata>0.9: + pass + else: + outputdata = 1 + return outputdata + +class metablocks(type): + def __setattr__(self, name, value): + raise ValueError(name) + + +class detectcommand(metaclass=metablocks): + def __init__(self): + try: + #from psutil import Process + + #self.shell = Process(getppid()).name().lower() + x, self.shell = get_console_process_name() + except: + process = subprocess.Popen( + ["tasklist", "/fi", f"PID eq {getppid()}", "/fo", "csv", "/nh"], + stdout=subprocess.PIPE, + ) + stdout, _ = process.communicate() + self.shell = stdout.decode().strip().split(",")[0].strip('"').lower() + + def __dir__(self): + pass + + def __repr__(self) -> str: + return self.shell + + @property + def isPowershell(self): + isPowershell = False + if fullmacth( + r"pswh|pswh.*|pswh.exe|pswh.*.exe|powershell.exe|powershell.*.exe", + self.shell, + ): + isPowershell = True + return isPowershell + + @property + def iscmd(self): + iscmd = False + if fullmacth(r"cmd|cmd.*|cmd.exe|cmd.*.exe", self.shell): + iscmd = True + return iscmd + + @property + def ispy(self): + ispy = False + if fullmacth( + r"py.*|py.exe|py.*.exe|python.*|python.*.exe|python.exe", self.shell + ): + ispy = True + return ispy + + @property + def isbash(self): + checkbash = ( + self.shell.count("bash") + or self.shell.count("sh") + or self.shell.count("dash") + or self.shell.count("ash") + ) + return checkbash != 0 + + def commands(self): + parent_process = getenv("PROCESSOR_IDENTIFIER", "") + + @lru_cache(maxsize=10) + def auto(self): + check = ["isPowershell", "iscmd", "ispy", "isbash"] + xx_output = {} + + for values in check: + xcmd = eval("self.{cmd}".format(cmd=values)) + if xcmd == True: + xx_output["type_command"] = values + break + return xx_output + + +def stream_output(pipe, name): + for line in iter(pipe.readline, ""): + print(f"{line.strip()}") + pipe.close() + +@lru_cache(maxsize=10) +def stream_command_output(command: str): + process = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, text=True + ) + + stdout_thread = threading.Thread(target=stream_output, args=(process.stdout, "")) + stderr_thread = threading.Thread(target=stream_output, args=(process.stderr, "")) + + stdout_thread.start() + stderr_thread.start() + + stdout_thread.join() + stderr_thread.join() + + return process.poll() + +@lru_cache(maxsize=10) +def run_powershell_command_as_admin(command): + # Command to run PowerShell as administrator and execute the given command + # ps_command = f'powershell -Command "Start-Process powershell -ArgumentList \'-NoProfile -ExecutionPolicy Bypass -Command {command}\' -WindowStyle Hidden"' + + # Execute the command + # result = subprocess.call(ps_command, shell=True) + # Run the command and capture the output + + result = subprocess.run( + ["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", command], + capture_output=True, + text=True, + ) + + # Ambil output dari perintah PowerShell + if result: + return result.stdout or result.stderr + + return False + +@lru_cache(maxsize=10) +def run_powershell_File_as_admin(command: str): + # Command to run PowerShell as administrator and execute the given command + # ps_command = f'powershell -Command "Start-Process powershell -ArgumentList \'-NoProfile -ExecutionPolicy Bypass -Command {command}\' -WindowStyle Hidden"' + + # Execute the command + # result = subprocess.call(ps_command, shell=True) + # Run the command and capture the output + result = subprocess.run( + ["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", command], + capture_output=True, + text=True, + ) + + # Ambil output dari perintah PowerShell + if result: + return result.stderr or result.stdout + + return False + +@lru_cache(maxsize=10) +def run_cmd_command_as_admin(command: str): + # Command to run commandprompt as administrator and execute the given command + # Execute the command + # Run the command and capture the output + + command = f"powershell -Command \"Start-Process cmd -ArgumentList \'/c {command}' -Verb runAs -WindowStyle Hidden\"" + time.sleep(2) + result = subprocess.run(command, capture_output=True, text=True, shell=True) + + # Ambil output dari perintah PowerShell + if result: + return True + return False + +@lru_cache(maxsize=10) +def checkcommand(command: str, timeout=None): + # Mulai proses + ps = subprocess.Popen( + command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT + ) + + # Fungsi untuk menunggu proses selesai + def target(): + try: + ps.communicate() + except Exception as e: + print(f"Error: {e}") + + ouputs = 0 + # Buat dan mulai thread + thread = threading.Thread(target=target) + thread.start() + + # Tunggu sampai selesai atau timeout + thread.join(timeout) + if thread.is_alive(): + ps.kill() # Mengirim sinyal penghentian + thread.join() # Tunggu sampai thread selesai + ouputs = 1 + if ouputs: + ouputs = 0 + return ouputs + +@lru_cache(maxsize=10) +def checkcommandV2(command: str, timeout: int=0) -> str: + """ + Menjalankan perintah dengan batasan waktu menggunakan thread. + Jika perintah melebihi waktu yang ditentukan, proses akan dihentikan secara paksa. + + Args: + command (str): Perintah yang akan dijalankan. + timeout (int): Waktu maksimum (dalam detik) untuk menjalankan perintah. + + Returns: + str: Output dari perintah jika berhasil, atau pesan error jika proses dihentikan atau gagal. + """ + if timeout <= 0: + timeout = round(yime())*2 + def target(): + nonlocal result, process + try: + process = subprocess.Popen( + "powershell -Command {command}".format(command=command), + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + stdout, stderr = process.communicate() + result = (process.returncode, stdout, stderr) + except Exception as e: + result = e + + result = None + process = None + thread = threading.Thread(target=target) + thread.start() + thread.join(timeout) + + if thread.is_alive(): + # Proses melebihi waktu yang ditentukan, hentikan secara paksa + if process is not None: + parent = psutil.Process(process.pid) + for child in parent.children(recursive=True): # Membunuh semua child processes + child.kill() + parent.kill() + thread.join() # Pastikan thread selesai + return result + + if isinstance(result, tuple): + returncode, stdout, stderr = result + if returncode == 0: + return result + else: + return result + elif isinstance(result, Exception): + return (1, None, None) + else: + return (1, None, None) + +@lru_cache(maxsize=10) +def checkcommandPrompt(command: str, timeout: int=0) -> str: + """ + Menjalankan perintah dengan batasan waktu menggunakan thread. + Jika perintah melebihi waktu yang ditentukan, proses akan dihentikan secara paksa. + + Args: + command (str): Perintah yang akan dijalankan. + timeout (int): Waktu maksimum (dalam detik) untuk menjalankan perintah. + + Returns: + str: Output dari perintah jika berhasil, atau pesan error jika proses dihentikan atau gagal. + """ + if timeout <= 0: + timeout = round(yime())*2 + def target(): + nonlocal result, process + try: + process = subprocess.Popen( + command, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + stdout, stderr = process.communicate() + result = (process.returncode, stdout, stderr) + except Exception as e: + result = e + + result = None + process = None + thread = threading.Thread(target=target) + thread.start() + thread.join(timeout) + + if thread.is_alive(): + # Proses melebihi waktu yang ditentukan, hentikan secara paksa + if process is not None: + parent = psutil.Process(process.pid) + for child in parent.children(recursive=True): # Membunuh semua child processes + child.kill() + parent.kill() + thread.join() # Pastikan thread selesai + return result + + if isinstance(result, tuple): + returncode, stdout, stderr = result + if returncode == 0: + return result + else: + return result + elif isinstance(result, Exception): + return (1, None, None) + else: + return (1, None, None) + +def get_console_process_name(): + """ + Mengembalikan nama proses yang menjalankan konsol saat ini (cmd atau powershell). + """ + kernel32 = ctypes.windll.kernel32 + user32 = ctypes.windll.user32 + psapi = ctypes.windll.psapi + + # Mendapatkan handle dari jendela konsol saat ini + hwnd = kernel32.GetConsoleWindow() + if not hwnd: + return None + + # Mendapatkan ID proses dari jendela konsol + pid = wintypes.DWORD() + user32.GetWindowThreadProcessId(hwnd, ctypes.byref(pid)) + + # Membuka proses untuk membaca informasi + h_process = kernel32.OpenProcess(0x1000, False, pid.value) + if not h_process: + return None + + # Mendapatkan nama file dari proses + exe_name = ctypes.create_string_buffer(512) + psapi.GetModuleFileNameExA(h_process, None, exe_name, 512) + kernel32.CloseHandle(h_process) + + head, tail = os.path.split(exe_name.value.decode('utf-8')) + return head, tail + + +def restart_powershell_as_admin(): + """ + Merestart PowerShell dalam mode administrator, memulai proses PowerShell baru, dan menjalankan file Python itu sendiri. + """ + def is_admin(): + """ + Mengecek apakah skrip dijalankan dengan hak administrator. + """ + try: + return ctypes.windll.shell32.IsUserAnAdmin() + except: + return False + try: + # Dapatkan path file Python saat ini + script_path = os.path.abspath(__file__) + + # Perintah untuk menjalankan PowerShell sebagai administrator dan menjalankan file Python ini + command = f'Start-Process powershell -ArgumentList \'-NoExit -Command "Start-Sleep -Seconds 1; Set-Location -Path \'{os.getcwd()}\'; python \\"{script_path}\\" "\'\' -Verb RunAs' + + # Jalankan perintah menggunakan subprocess + subprocess.run(["powershell", "-Command", command], shell=True) + + # Keluar dari proses Python saat ini + os._exit(0) + except Exception as e: + print(f"An error occurred: {e}") + + +detectcommandprompt = detectcommand() +#print(detectcommandprompt.auto()) + + +##################### + + +def get_current_directory_partition(current_dir:str): + partitions = psutil.disk_partitions() + current_dir = current_dir.replace("\\", "/") + for partition in partitions: + if current_dir.lower().startswith(partition.mountpoint.lower().replace("\\", "/")): + return get_detailed_partition_info(partition) + + return None + +def get_detailed_partition_info(partition): + usage = psutil.disk_usage(partition.mountpoint) + return { + 'device': partition.device, + 'mountpoint': partition.mountpoint, + 'fstype': partition.fstype, + 'total': usage.total, + 'used': usage.used, + 'free': usage.free, + 'percent': usage.percent + } + + + +if __name__ == "__main__": + command = "$pass = Read-Host '{command}' -AsSecureString; $plainTextPass = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pass)); Write-Output $plainTextPass".format(command="prompt") # Contoh perintah Python yang tidur selama 60 detik + #timeout = round(yime()) # Waktu maksimum yang diizinkan (dalam detik) + + start_time = time.time() + output = checkcommandV2(command) + end_time = time.time() + + elapsed_time = end_time - start_time + print(f"Output:\n{output}") + print(f"Elapsed time: {elapsed_time:.2f} seconds") \ No newline at end of file diff --git a/libs/errrorHandler.py b/libs/errrorHandler.py new file mode 100644 index 0000000..621d331 --- /dev/null +++ b/libs/errrorHandler.py @@ -0,0 +1,68 @@ +import logging +import time +from typing import Callable, Any, Optional +from functools import wraps +import traceback + +# Configure the logging +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') +logger = logging.getLogger(__name__) + + +def handle_errors(func): + ####Menggunakan fungsi wrap + @wraps(func) + def wrapper(*arg, **kwargs): + start_time = time.time() + try: + result = func(*arg, **kwargs) + except Exception as e: + execution_time = time.time() - start_time + tb = traceback.extract_tb(e.__traceback__) + filename, lineno, funcname, text = tb[-1] + error_message = ( + f"An error occurred in function {func.__name__} at {filename}:{lineno} - {text}" + ) + logger.error(f"Function {func.__name__} failed after {execution_time:.4f} seconds with error: {e}") + logger.error(error_message) + return wrapper + +def complex_handle_errors(loggering=None, log_message: Optional[str] = None) -> Callable[[Callable[..., Any]], Callable[..., Any]]: + def decorator(func: Callable[..., Any]) -> Callable[..., Any]: + @wraps(func) + def wrapper(*args, **kwargs) -> Any: + start_time = time.time() + try: + if 'nomessagesNormal' in kwargs: + nomessagesNormal = kwargs['nomessagesNormal'] + else: + nomessagesNormal = False + except: + nomessagesNormal = True + try: + loggering.info(f"Executing function: {func.__name__} with args: {args} and kwargs: {kwargs}") + result = func(*args, **kwargs) + if nomessagesNormal == True: + execution_time = time.time() - start_time + loggering.info(f"Function {func.__name__} executed successfully in {execution_time:.4f} seconds") + return result + except Exception as e: + execution_time = time.time() - start_time + tb = traceback.extract_tb(e.__traceback__) + filename, lineno, funcname, text = tb[-1] + error_message = ( + log_message or + f"An error occurred in function {func.__name__} at {filename}:{lineno} - {text}" + ) + loggering.error(f"Function {func.__name__} failed after {execution_time:.4f} seconds with error: {e}") + loggering.error(error_message) + return "" # Atau nilai default sesuai kebutuhan + + + return wrapper + + # Check if decorator is used without arguments + if callable(log_message): + return decorator(log_message) + + return decorator diff --git a/libs/filemanager.py b/libs/filemanager.py new file mode 100644 index 0000000..a920820 --- /dev/null +++ b/libs/filemanager.py @@ -0,0 +1,384 @@ +import os, sys, time, shutil, psutil, inspect, importlib, pkg_resources, pkgutil, json, logging, threading + +try: + from .helperegex import ( + searchmissing, + searching, + fullmacth, + rremovelist, + clean_string, + rreplace, + cleanstring, + ) + from .cmd_filter import filter_json, safe_load_json + from .system_manajemen import set_low_priority, SafeProcessExecutor + from .timeout import timeout_v1, timeout_v2 + from .https import Fetch +except: + try: + from helperegex import ( + searchmissing, + searching, + fullmacth, + rremovelist, + clean_string, + rreplace, + cleanstring, + ) + from cmd_filter import filter_json, safe_load_json + from system_manajemen import set_low_priority, SafeProcessExecutor + from timeout import timeout_v1, timeout_v2 + from https import Fetch + except: + from libs.helperegex import ( + searchmissing, + searching, + fullmacth, + rremovelist, + clean_string, + rreplace, + cleanstring, + ) + from libs.cmd_filter import filter_json, safe_load_json + from libs.system_manajemen import set_low_priority, SafeProcessExecutor + from libs.timeout import timeout_v1, timeout_v2 + from libs.https import Fetch + +if __name__ == "__main__": + set_low_priority(os.getpid()) + + +script_dir = os.path.dirname(os.path.realpath(__file__)).replace("\\", "/") +all_system_paths = ["/".join(script_dir.split("/")[:-1]), script_dir] + + +class StreamFile: + def __init__(self, file_path: str, buffer_size: int = 8192, print_delay: float = 2): + """ + Inisialisasi StreamFile untuk membaca file baris demi baris dengan delay dan menulis dengan buffer. + + :param file_path: Path ke file yang akan dibaca atau ditulis. + :param buffer_size: Ukuran buffer sebelum data ditulis ke file. + :param print_delay: Waktu jeda (dalam detik) antara print setiap baris. + """ + self.file_path = file_path + self.buffer_size = buffer_size or 0 + self.print_delay = print_delay + self.buffer = bytearray() + + def readlines(self): + """ + Membaca file dengan buffer size dan menghasilkan setiap baris satu per satu dengan delay. + + :yield: Baris dari file. + """ + + with open(self.file_path, "r+") as f: + buffer = self.buffer + while True: + chunk = f.read(self.buffer_size) + if not chunk: + break + + buffer.extend( + chunk.encode("utf-8") + ) # Encode chunk to bytes if necessary + + while b"\n" in buffer: + line, buffer = buffer.split(b"\n", 1) + yield line.decode("utf-8") + time.sleep(self.print_delay) + + if buffer: + yield buffer.decode("utf-8") + self.buffer_size = 0 + + def write(self, data): + """ + Menulis data ke buffer dan secara otomatis menulis ke file ketika buffer penuh. + + :param data: Data yang akan ditulis ke buffer. + """ + self.buffer.extend(data) + while len(self.buffer) >= self.buffer_size: + with open(self.file_path, "ab+") as f: + f.write(self.buffer[: self.buffer_size]) + self.buffer = self.buffer[self.buffer_size :] + + def writelines(self, lines): + """ + Menulis baris-baris data ke file dengan delay antara setiap baris. + + :param lines: List atau generator yang menghasilkan baris-baris data untuk ditulis. + """ + for line in lines: + self.write(line.encode("utf-8")) + time.sleep(self.print_delay + timeout_v1()) + self.close() # Memastikan buffer ditulis dan ditutup setelah penulisan selesai + + def eraseFile(self): + with open(self.file_path, "rb+") as f: + f.truncate(0) + + def close(self): + """ + Menulis sisa data di buffer ke file dan membersihkan buffer. + """ + if self.buffer and self.buffer_size: + with open(self.file_path, "ab+") as f: + f.write(self.buffer) + self.buffer.clear() + else: + pass + + +class ModuleInspector: + def __init__(self): + self.modules = self.getsys_module() + self.curents = self.modules + self.curentpath = sys.path.copy() + self.modulepathnow = [] + + def getsys_module(self): + return sorted( + [ + module.name + for module in pkgutil.iter_modules([x for x in sys.path if x]) + if not module.name.strip().startswith("~") + and not module.name.strip().startswith("__pycache__") + ] + ) + + def get_module(self, paths:list=[]): + def getmodules(path:list, result:list): + result.extend(sorted( + [ + module.name + for module in pkgutil.iter_modules(path) + if not module.name.strip().startswith("~") + and not module.name.strip().startswith("__pycache__") + ] + )) + + threads, result = [[], self.curents] + if paths.__len__()<1: + paths = [os.getcwd()] + else: + pass + + + for path in paths: + thread = threading.Thread(target=getmodules, args=([path], result)) + thread.start() + threads.append(thread) + + for thread in threads: + thread.join() + + self.modulepathnow = paths + return result + + + def list_classes(self, module): + try: + imported_module = importlib.import_module(module) + classes = [ + obj + for name, obj in inspect.getmembers(imported_module) + if inspect.isclass(obj) + ] + if not classes: + pass + return classes + except Exception as e: + return [] + + def get_class_details(self, cls): + details = {"name": cls.__name__, "variables": [], "functions": []} + + for name, obj in inspect.getmembers(cls): + if inspect.isfunction(obj): + func_details = {"name": name, "params": str(inspect.signature(obj))} + details["functions"].append(func_details) + elif not name.startswith("__") and not inspect.ismodule(obj): + details["variables"].append(name) + + return details + + def get_global_variables(self, module): + try: + imported_module = importlib.import_module(module) + # global_vars = {name: self.serialize_value(value) for name, value in vars(imported_module).items() + # if not (inspect.isclass(value) or inspect.isfunction(value)) and not name.startswith('__')} + global_vars = [ + name + for name, value in vars(imported_module).items() + if not (inspect.isclass(value) or inspect.isfunction(value)) + and not name.startswith("__") + ] + return global_vars + except Exception as e: + return [] + + def serialize_value(self, value): + """Serialize values for JSON compatibility.""" + if isinstance(value, (int, float, str, bool, list, dict)): + return value + elif callable(value): + return f"Function: {value.__name__}" + else: + return str(value) # Convert other types to string + + def inspect_module(self, module_name): + if self.modulepathnow.__len__()>=1: + sys.path.extend(self.modulepathnow) + self.modulepathnow = [] + try: + classes = self.list_classes(module_name) + global_vars = self.get_global_variables(module_name) + result = { + "module": module_name, + "global_variables": global_vars, + "classes": [], + } + + for cls in classes: + class_details = self.get_class_details(cls) + result["classes"].append(class_details) + + # Convert the result to JSON and print it + + sys.path = self.curentpath + return result + except Exception as e: + sys.path = self.curentpath + return None + + +def create_file_or_folder(path: str) -> str: + """ + Membuat file atau folder di path yang diberikan. + + Args: + path (str): Path lengkap tempat file atau folder akan dibuat. + + Returns: + str: Pesan konfirmasi yang menunjukkan apakah file atau folder berhasil dibuat. + """ + + if not path: + return "Path is empty." + + if os.path.isdir(path): + return f"The folder '{os.path.basename(path)}' already exists." + + if os.path.isfile(path): + return f"The file '{os.path.basename(path)}' already exists." + + folder, filename = os.path.split(path) + if "." in os.path.basename(path) and os.path.exists(folder): + # Membuat file + try: + if folder and not os.path.exists(folder): + return f"Failed to create the file '{filename}'" + with open(path, "wb") as f: + pass # Membuat file kosong + return f"The file '{filename}' has been successfully created." + except Exception as e: + return f"Failed to create the file '{filename}'" + elif os.path.exists(folder) and folder: + # Membuat folder + try: + os.makedirs(path) + return ( + f"The folder '{os.path.basename(path)}' has been successfully created." + ) + except FileExistsError: + return f"The folder '{os.path.basename(path)}' already exists." + except Exception as e: + return f"Failed to create the folder '{os.path.basename(path)}'." + else: + return "Something happened." + + +def is_binary_file(file_path): + """ + Menentukan apakah file adalah file biner atau bukan. + + Args: + file_path (str): Path ke file yang akan diperiksa. + + Returns: + bool: True jika file adalah file biner, False jika bukan. + """ + try: + with open(file_path, "rb") as file: + chunk = file.read(1024) # Membaca bagian pertama file (1KB) + # Cek apakah file memiliki karakter yang tidak biasa untuk teks + if b"\0" in chunk: # Null byte adalah indikator umum dari file biner + return True + # Cek apakah file sebagian besar berisi karakter teks (misalnya ASCII) + text_chars = b"".join([bytes((i,)) for i in range(32, 127)]) + b"\n\r\t\b" + non_text_chars = chunk.translate(None, text_chars) + if ( + len(non_text_chars) / len(chunk) > 0.30 + ): # Jika lebih dari 30% karakter non-teks + return True + return False + except Exception as e: + return False + + +def check_class_in_package(package_name, class_name): + try: + # Import the package + package = importlib.import_module(package_name) + # Cek apakah kelas ada di dalam modul + if hasattr(package, class_name): + cls = getattr(package, class_name) + # Pastikan itu adalah kelas, bukan atribut atau fungsi + if inspect.isclass(cls): + return True, "ClassFound" + return False, "ClassNotFound" + except ModuleNotFoundError: + return False, "ModuleNotFoundError" + + +def resolve_relative_path(current_path: str, relative_path: str) -> str: + # Menggabungkan current_path dengan relative_path (misalnya "../") + target_path: str = os.path.normpath(os.path.join(current_path, relative_path)) + return target_path + + +def resolve_relative_path_v2(path: str) -> str: + target_folder: str = resolve_relative_path( + os.getcwd().replace("\\", "/"), path.replace("\\", "/") + ) + return target_folder + + +def get_latest_version(package_name): + with Fetch() as req: + response = req.get( + f"https://pypi.org/pypi/{package_name}/json", + max_retries=3, + timeout=8, + follow_redirects=True, + ) + data = response.json() + if filter_json(data=data, keys=["info"]): + return data["info"]["version"] + return None + + +def check_update(package_name): + installed_version = pkg_resources.get_distribution(package_name).version + latest_version = get_latest_version(package_name) + + if installed_version != latest_version: + print( + f"Package {package_name} can be updated from version {installed_version} to {latest_version}." + ) + else: + print(f"Package {package_name} is up to date.") + diff --git a/libs/filterror.py b/libs/filterror.py new file mode 100644 index 0000000..9365391 --- /dev/null +++ b/libs/filterror.py @@ -0,0 +1,682 @@ +import re, os, sys, compileall, collections, traceback +try: + import gc +except: + pass +from contextlib import contextmanager + +class Decoration: + def __init__(self, param_foo='a', param_bar='b'): + self.param_foo = param_foo + self.param_bar = param_bar + + def __call__(self, func): + def my_logic(*args, **kwargs): + print(self.param_bar) + # including the call to the decorated function (if you want to do that) + result = func(*args, **kwargs) + return result + + return my_logic + +class PYTHON_ERORCODE: + all_error = collections.OrderedDict() + all_error[Exception] = Exception + all_error[TypeError] = TypeError + all_error[TimeoutError] = TimeoutError + all_error[RecursionError] = RecursionError + all_error[ReferenceError] = RecursionError + all_error[MemoryError] = MemoryError + all_error[ModuleNotFoundError] = ModuleNotFoundError + all_error[ChildProcessError] = ChildProcessError + all_error[ConnectionAbortedError] = ConnectionAbortedError + all_error[ConnectionError] = ConnectionError + all_error[ConnectionRefusedError] = ConnectionRefusedError + all_error[ConnectionResetError] = ConnectionResetError + all_error[OSError] = OSError + all_error[OverflowError] = OverflowError + all_error[EnvironmentError] =EnvironmentError + all_error[EOFError] = EOFError + all_error[UnicodeDecodeError] = UnicodeDecodeError + all_error[UnicodeEncodeError] = UnicodeEncodeError + all_error[UnicodeTranslateError] = UnicodeTranslateError + all_error[UnboundLocalError] = UnboundLocalError + all_error[AttributeError] = AttributeError + all_error[ValueError] = ValueError + all_error[AssertionError] =AssertionError + all_error[ZeroDivisionError] = ZeroDivisionError + all_error[FloatingPointError] = FloatingPointError + all_error[FileNotFoundError] =FileNotFoundError + all_error[FileExistsError] = FileExistsError + all_error[KeyboardInterrupt] = KeyboardInterrupt + all_error[NameError] = NameError + + +class ValidationError(PYTHON_ERORCODE): + global __clasesserr + def __init__(self, coderror=None, messages=None): + + global messages_error + + messages_error= messages + __, __clasesserr = None, None + + if coderror in self.all_error and messages: + try: + for keys in self.all_error.keys(): + if coderror == str(keys): + coderror = str(self.all_error[keys]) + break + else: + code = self.all_error[keys] + if str(keys).count(".") != 0: + newkeys = re.findall(r"'(.*?)'", str(keys).strip()) or re.findall(r"\"(.*?)\"", str(keys).strip()) + if newkeys.__len__()!=0: + newkeys = newkeys[-1:][0] + assert str(newkeys) == code + coderror = str(newkeys) + break + if coderror.startswith(" 0: + + try: + + __ = eval("{coderrorx}(\"{messages}\")".format(coderrorx=regex[-1:][0], messages=str(messages))) + + except: + + __ = "\"{messages}\"".format(messages=str(messages)) + + finally: + __clasesserr = "{coderrorx}".format(coderrorx=regex[-1:][0]) + + + #return __ + self.compliterror = __ + self.classeseror = __clasesserr + if self.compliterror == None: + messages_error = str(messages) + + def __call__(self, coderror=None, messages=None): + global messages_error + + messages_error= messages + __, __clasesserr = None, None + + if coderror in self.all_error and messages: + + for keys in self.all_error.keys(): + if coderror == str(keys): + coderror = self.all_error[keys] + break + else: + code = self.all_error[keys] + if keys.count(".") != 0: + newkeys = re.findall(r"'(.*?)'", str(keys).strip()) or re.findall(r"\"(.*?)\"", str(keys).strip()) + if newkeys.__len__()!=0: + newkeys = newkeys[-1:][0] + assert str(newkeys) == code + coderror = newkeys + break + + try: + __ = eval("{coderrorx}(\"{messages}\")".format(coderrorx=coderror, messages=str(messages))) + + except: + + __ = "\"{messages}\"".format(messages=str(messages)) + finally: + __clasesserr = "{coderrorx}".format(coderrorx=str(coderror)) + + + + else: + + regex = [] + + for keys in self.all_error.keys(): + + if coderror == str(keys): + + regex = re.findall(r"'(.*?)'", str(keys).strip()) or re.findall(r"\"(.*?)\"", str(keys).strip()) + break + else: + try: + code = self.all_error[keys] + if keys.count(".") != 0: + newkeys = re.findall(r"'(.*?)'", str(keys).strip()) or re.findall(r"\"(.*?)\"", str(keys).strip()) + if newkeys.__len__()!=0: + newkeys = newkeys[-1:][0] + assert str(newkeys) == code + regex = [newkeys] + break + except: + pass + + if regex.__len__() > 0: + + try: + + __ = eval("{coderrorx}(\"{messages}\")".format(coderrorx=regex[-1:][0], messages=str(messages))) + + except: + + __ = "\"{messages}\"".format(messages=str(messages)) + + finally: + __clasesserr = "{coderrorx}".format(coderrorx=regex[-1:][0]) + + + #return __ + exc_type, exc_obj, exc_tb = sys.exc_info() + + typess = str(exc_type).split(".")[-1:][0].replace("'>", "", 1).strip() + self.compliterror = __ + self.classeseror = __clasesserr + if self.classeseror == None or self.classeseror == "" or self.classeseror == "None": + if coderror == None: + coderror = "" + coderrorx = re.findall(r"'(.*?)'", str(coderror).strip()) or re.findall(r"\"(.*?)\"", str(coderror).strip()) + for keys in self.all_error.keys(): + try: + splikeys = re.findall(r"'(.*?)'", str(self.all_error[keys]).strip()) or re.findall(r"\"(.*?)\"", str(self.all_error[keys]).strip()) + + except: + splikeys = re.findall(r"'(.*?)'", str(keys).strip()) or re.findall(r"\"(.*?)\"", str(keys).strip()) + + newkeys = str(splikeys[-1:][0]).split(".")[-1:][0] + if newkeys == str(coderrorx[-1:][0]).split(".")[-1:][0] and newkeys == typess: + self.classeseror = keys + break + elif newkeys == str(coderrorx[-1:][0]).split(".")[-1:][0]: + self.classeseror = keys + break + elif coderror == "" and newkeys == typess or coderror == "_" and newkeys == typess: + self.classeseror = keys + break + if self.compliterror == None or self.compliterror == "": + if messages == None: + self.compliterror = str(exc_obj) + messages_error = self.compliterror + else: + self.compliterror = str(messages) + + + return self + + def __enter__(self): + return self + + + def __str__(self): + return "" + + def __exit__(self, exc_type, exc_value, traceback): + pass + + def __str__(self): + return self.__dict__ + + def __dir__(self): + return ['results'] + + def update(self, run:bool, **kwarg:dict): + if kwarg.__len__() != 0: + for x in kwarg.keys(): + + keys, values = x, kwarg[x] + if keys: + + valuesx = """class {classname}(Exception):\n\tpass""".format(classname=keys) + + exec(valuesx) + self.all_error[eval(keys)] = eval(keys) + #setattr(__builtins__, "{classname}".format(classname=values), eval(keys)) + + else: + if isinstance(values, str): + + if values.find(".") != 0 and values.find(".") > 0: + + values = values.split(".")[-1:][0] + + + elif values.startswith("."): + + if values.count(".") > 0: + + values = values.split(".")[-1:][0] + + else: + + values = values.replace(".", "", 1) + + valuesx = """ + class {classname}(Exception): + + def __init__(self, code): + self.code = code + + def __str__(self): + return repr(self.code) + + """.format(classname=values) + + exec(valuesx) + self.all_error[eval(values)] = eval(values) + #setattr(__builtins__, "{classname}".format(classname=values), eval(values)) + + elif isinstance(values,object): + self.all_error[eval(values)] = eval(values) + #setattr(__builtins__, "{classname}".format(classname=values), eval(values)) + else: + pass + + #print(valuesx) + #try: + # @Decoration(param_bar=self) + # def example(): + # return self + # return example() + #except: + # return self.__init__(coderror=values) + + @property + def results(self): + + if self.__dict__.keys().__contains__("jsonoutput"): + + json = self.jsonoutput + + self.jsonoutput = collections.OrderedDict() + + + return json + + + def manual(self): + + self.jsonoutput = collections.OrderedDict() + exc_type, exc_obj, exc_tb = sys.exc_info() + try: + class A(eval(self.classeseror)): + + def __init__(self, code): + self.code = code + + def __str__(self): + return repr(self.code) + except: + try: + class A(self.classeseror): + + def __init__(self, code): + self.code = code + + def __str__(self): + return repr(self.code) + except: + self.jsonoutput['Type_Error'], self.jsonoutput['pathname'], self.jsonoutput["filename"]\ + , self.jsonoutput['error_line'], self.jsonoutput['error_message'] = None, None, None, None, None + pname, fname = os.path.split(exc_tb.tb_frame.f_code.co_filename) + if fname == "": + try: + fname = os.chdir() + except: + fname = "." + + #if exc_type not in self.all_error: + exc_typeinject = re.findall(r"'(.*?)'", str(exc_type).strip()) or re.findall(r"\"(.*?)\"", str(exc_type).strip()) + exc_typeinject = ".".join(["filterror", str(exc_typeinject[-1:][0].split(".")[-1:][0]) ]) + for keys in self.all_error.keys(): + newkeys = str(keys) + if newkeys.find(exc_typeinject) != 0 or newkeys.find(exc_type) != 0: + self.jsonoutput['Type_Error'] = newkeys.split(".")[-1:][0] + self.jsonoutput['pathname'] = pname + self.jsonoutput["filename"] = fname + self.jsonoutput['error_line'] = exc_tb.tb_lineno + self.jsonoutput['error_message'] = str(exc_obj) + break + error_type = re.findall(r"'(.*?)'", str(self.jsonoutput['Type_Error']).strip()) or re.findall(r"\"(.*?)\"", str(self.jsonoutput['Type_Error']).strip()) + if error_type.__len__() == 0: + + error_type = str(self.jsonoutput['Type_Error']) + + else: + + error_type = str(error_type[-1:][0]).split(".")[-1:][0] + self.jsonoutput.update({"Type_Error": error_type}) + return + + messages = str(self.compliterror) + + if messages.startswith(str(self.classeseror)): + + messages = messages.replace(str(self.classeseror), "", 1) + + try: + + try: + + raise A(messages) + + finally: + + raise A(messages) + + except A as output: + #print("Type Error:", self.classeseror) + error_type = re.findall(r"'(.*?)'", str(self.classeseror).strip()) or re.findall(r"\"(.*?)\"", str(self.classeseror).strip()) + if error_type.__len__() == 0: + + error_type = str(self.classeseror) + + else: + + error_type = str(error_type[-1:][0]).split(".")[-1:][0] + + #exc_type, exc_obj, exc_tb = sys.exc_info() + roscz = [e for e in str(output).split("\n") if e.__len__()!=0] + + check_filename, lines_X, code_messages = [], "", "" + + if messages_error != "None" or messages_error != None: + __ = traceback.format_exception(exc_type, exc_obj, exc_tb) + code_messages = str(__[-1:][0]).replace("ValidationError.manual..A:", "", 1).strip() + else: + code_messages = str(exc_obj) + + + + for index_output in roscz: + + if str(index_output).strip().startswith(("File \"", "File: \"", "file \"")): + + cleanx = str(index_output).strip().rstrip() + + check_filename = re.findall(r"'(.*?)'", cleanx) \ + or re.findall(r"\"(.*?)\"", cleanx) + + lines_X = cleanx + else: + if check_filename.__len__() == 0: + check_filename = [] + + if check_filename.__len__() != 0: + + pname, fname = os.path.split(check_filename[-1:][0].strip()) + + linex = re.findall(r"line\s+(.*),", lines_X) or re.findall(r"line\s+(.*,)", lines_X) + if linex: + linerror = int(linex[-1:][0]) + #errotypes = roscz[-1:][0] + self.jsonoutput['Type_Error'] = error_type + + self.jsonoutput['pathname'] = pname + + self.jsonoutput["filename"] = fname + + self.jsonoutput['error_line'] = linerror + else: + self.jsonoutput['error_message'] = code_messages + else: + self.jsonoutput['Type_Error'] = error_type + if str(output).find("File"): + roscz = [e.strip() for e in str(output).split("\n") if e.__len__()!=0] + + check_filename, lines_X = [], "" + + for xfile in roscz: + + if xfile.startswith(("File", "file")): + cleanx = str(xfile).strip().rstrip() + + check_filename = re.findall(r"'(.*?)'", cleanx) \ + or re.findall(r"\"(.*?)\"", cleanx) + + lines_X = cleanx + + if check_filename.__len__(): + + linex = re.findall(r"line\s+(.*),", lines_X) or re.findall(r"line\s+(.*,)", lines_X) + + linerror = (linex[-1:][0]) + #errotypes = roscz[-1:][0] + pname, fname = os.path.split(check_filename[-1:][0].strip()) + + self.jsonoutput['Type_Error'] = error_type + + self.jsonoutput['pathname'] = pname + + self.jsonoutput["filename"] = fname + + self.jsonoutput['error_line'] = linerror + + else: + self.jsonoutput['error_message'] = code_messages + + #self.jsonoutput['error_message'] = str(output) + else: + self.jsonoutput['error_message'] = code_messages + + + if self.jsonoutput.get('filename') == "": + del self.jsonoutput['filename'] + try: + self.jsonoutput['pathname'] = os.chdir() + except: + self.jsonoutput['pathname'] = "." + + if self.jsonoutput.get('error_message'): + exc_type, exc_obj, exc_tb = sys.exc_info() + try: + _modules = os.path.abspath(str(sys.modules['__main__'].__file__ )) + pname, fname = os.path.split(str(__file__).strip()) + self.jsonoutput['pathname'] = pname + self.jsonoutput["filename"] = fname + + except: + self.jsonoutput['pathname'] = "." + + #messages = self.jsonoutput["error_message"] + self.jsonoutput['error_line'] = exc_tb.tb_lineno + del self.jsonoutput["error_message"] + self.jsonoutput['error_message'] = code_messages + + else: + self.jsonoutput['error_line'] = exc_tb.tb_lineno + self.jsonoutput['error_message'] = code_messages + + self.compliterror = None + + self.classeseror = None + + regex = re.findall(r"'(.*?)'", str(error_type).strip()) or re.findall(r"\"(.*?)\"", str(error_type).strip()) + if regex.__len__() != 0: + self.jsonoutput['Type_Error'].update({'Type_Error': regex[-1:][0] }) + #self.jsonoutput['error_line'] = linex + #regex = re.findall(r"'(.*?)'", str(errotypes).strip()) or re.findall(r"\"(.*?)\"", str(errotypes).strip()) + + + #exc_type, exc_obj, exc_tb = sys.exc_info() + #pname, fname = os.path.split(exc_tb.tb_frame.f_code.co_filename) + #print("EROR2 ", exc_type, fname, exc_tb.tb_lineno) + #print(tb) + + def auto(self): + + """ + + + + """ + exc_type, exc_obj, exc_tb = sys.exc_info() + + pname, fname = os.path.split(exc_tb.tb_frame.f_code.co_filename) + + typeserror = re.findall(r"'(.*?)'", str(exc_type).strip()) \ + or re.findall(r"\"(.*?)\"", str(exc_type).strip()) + + self.jsonoutput = collections.OrderedDict() + + self.jsonoutput['Type_Error'] = typeserror[-1:][0] + + self.jsonoutput['pathname'] = pname + + self.jsonoutput["filename"] = fname + + self.jsonoutput['error_line'] = exc_tb.tb_lineno + + if self.jsonoutput.get('filename') == "": + del self.jsonoutput['filename'] + try: + self.jsonoutput['pathname'] = os.chdir() + except: + self.jsonoutput['pathname'] = "." + + self.jsonoutput['error_message'] = str(exc_obj) + + def capture(self, types=2, *args): + if types == 2 or types == 1: + def foo(exctype, value, tb): + trace_back = traceback.extract_tb(tb) + stack_trace = [] + for trace in trace_back: + stack_trace.append("File : %s , Line : %d, Func.Name : %s, Message : %s" % (trace[0], trace[1], trace[2], trace[3])) + print('My Error Information') + print('Type:', exctype) + print('Value:', value) + print('Traceback:', "\n".join(stack_trace)) + + sys.excepthook = foo + else: + def handle_exception(exc_type, exc_value, exc_traceback): + if issubclass(exc_type, KeyboardInterrupt): + sys.__excepthook__(exc_type, exc_value, exc_traceback) + return + #logging.critical(exc_value, exc_info=(exc_type, exc_value, exc_traceback)) + + + def handle_error(func): + def __inner(*args, **kwargs): + try: + return func(*args, **kwargs) + except Exception as e: + stack_trace = [] + exc_type, exc_value, exc_tb = sys.exc_info() + trace_back = traceback.extract_tb(exc_tb) + for trace in trace_back: + stack_trace.append("File : %s , Line : %d, Func.Name : %s, Message : %s" % (trace[0], trace[1], trace[2], trace[3])) + handle_exception(exc_type, exc_value, exc_tb) + print('My Error Information') + print('Type:', exc_type) + print('Value:', exc_value) + print('Traceback:', "\n".join(stack_trace)) + + return __inner + return handle_error + +example = """Traceback (most recent call last): +File "C:/Users/pc/Documents/WindowsPowerShell/libs/filterror.py", line 136, in +import sdd +ModuleNotFoundError: No module named 'sdd' +""" + + + +"""""try: + myfunc() +except: + type, val, tb = sys.exc_info() + #print(sys.exc_info()) + traceback.clear_frames(tb) +# some cleanup code +gc.collect() +# and then use the tb: +if tb: + print("EROR2 :", val, type, tb) + +import sys, os + +""""" +#simple_test = ValidationError("", example) + +#Simple +#simple_test.capture() +#simple_test.manual() +#print(simple_test.results) +#sgsgs() + +#try: +# gsgs() +#except Exception as e: +# with ValidationError() as simple_test: +# simple_test.auto() +# print(simple_test.results) + +#try: +# gsgs() +#except Exception as e: +# with ValidationError("", messages=e) as simple_test: +# simple_test.manual() +# print(simple_test.results) + + #print("\nDete:", simple_test.__dict__) + + + +#@simple_test.capture(1) +#def main(): +# raise RuntimeError("RuntimeError") + + +#if __name__ == "__main__": +# for _ in range(1, 20): +# main() +__all__ = ['PYTHON_ERORCODE', 'ValidationError'] \ No newline at end of file diff --git a/libs/hellpper.py b/libs/hellpper.py new file mode 100644 index 0000000..ebb4fe8 --- /dev/null +++ b/libs/hellpper.py @@ -0,0 +1,264 @@ +import sys, os, platform +import ctypes +from ctypes import wintypes as w + +def version(osname=None): + if osname: + try: + assert osname in ['windows', 'linux', 'darwin'] + except: + if ['windows', 'linux', 'darwin'].index(osname): + pass + else: + return + versions = sys.getwindowsversion().major or platform.version().split('.')[0] + else: + versions = "0.2" + return versions + + +def os_platform(): + platforms = platform.system() or os.name or sys.platform + if platforms.startswith(("windows", "Windows", "nt", "win")): + platformname = "windows" + elif platforms.startswith(("darwin", "Darwin", "Air", "air", "posix")): + if os.name =="posix" and platform.system() == "Darwin": + platformname = "darwin" + else: + platformname = "darwin" + elif platforms.startswith(("posix","fedora", "Fedora", "unix", "Unix", "Linux", "linux", "ubuntu", 'Ubuntu', "debian" + , "Debian", "arch", "Arch", "redhat", "Redhat")): + platformname = "linux" + else: + platformname = "unknow" + return platformname + +def sizewindow(): + from ctypes import windll, byref + from ctypes.wintypes import SMALL_RECT + + STDOUT = -11 + + hdl = windll.kernel32.GetStdHandle(STDOUT) + rect = SMALL_RECT(0, 50, 150, 80) # (left, top, right, bottom) + windll.kernel32.SetConsoleWindowInfo(hdl, True, byref(rect)) + +#os.system("mode con cols=93 lines=45") +def fontsize(): + import ctypes + + STD_OUTPUT_HANDLE = -11 + + class COORD(ctypes.Structure): + _fields_ = [("X", ctypes.c_short), ("Y", ctypes.c_short)] + + class CONSOLE_FONT_INFOEX(ctypes.Structure): + _fields_ = [("cbSize", ctypes.c_ulong), + ("nFont", ctypes.c_ulong), + ("dwFontSize", COORD)] + + font = CONSOLE_FONT_INFOEX() + font.cbSize = ctypes.sizeof(CONSOLE_FONT_INFOEX) + font.nFont = 12 + font.dwFontSize.X = 10 # in your case X=10 + font.dwFontSize.Y = 18 # and Y=18 + + + + handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE) + ctypes.windll.kernel32.SetCurrentConsoleFontEx( + handle,False, ctypes.byref(font)) + + +def getTerminalSize(platformname): + #import platform + current_os = platform.system() + tuple_xy = None + try: + if platformname == 'windows': + tuple_xy = _getTerminalSize_windows() + if tuple_xy is None: + tuple_xy = _getTerminalSize_tput() + # needed for window's python in cygwin's xterm! + if platformname == 'linux' or platformname == 'darwin' or current_os.startswith('CYGWIN'): + tuple_xy = _getTerminalSize_linux() + if tuple_xy is None: + tuple_xy = (80, 25) # default value + except: + import shutil + columns, lines = shutil.get_terminal_size((80, 20)) + return columns, lines + return tuple_xy + +def _getTerminalSize_windows(): + res=None + try: + from ctypes import windll, create_string_buffer + + # stdin handle is -10 + # stdout handle is -11 + # stderr handle is -12 + + h = windll.kernel32.GetStdHandle(-12) + csbi = create_string_buffer(22) + res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi) + except: + return None + if res: + import struct + (bufx, bufy, curx, cury, wattr, + left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw) + sizex = right - left + 1 + sizey = bottom - top + 1 + return sizex, sizey + else: + return None + +def _getTerminalSize_tput(): + # get terminal width + # src: http://stackoverflow.com/questions/263890/how-do-i-find-the-width-height-of-a-terminal-window + try: + import subprocess + proc=subprocess.Popen(["tput", "cols"],stdin=subprocess.PIPE,stdout=subprocess.PIPE) + output=proc.communicate(input=None) + cols=int(output[0]) + proc=subprocess.Popen(["tput", "lines"],stdin=subprocess.PIPE,stdout=subprocess.PIPE) + output=proc.communicate(input=None) + rows=int(output[0]) + return (cols,rows) + except: + return None + + +def _getTerminalSize_linux(): + def ioctl_GWINSZ(fd): + try: + import fcntl, termios, struct, os + cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ,'1234')) + except: + return None + return cr + cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2) + if not cr: + try: + fd = os.open(os.ctermid(), os.O_RDONLY) + cr = ioctl_GWINSZ(fd) + os.close(fd) + except: + pass + if not cr: + try: + cr = (env['LINES'], env['COLUMNS']) + except: + return None + return int(cr[1]), int(cr[0]) + + +all = ["fontsize"] + +from ptpython.python_input import PythonInput + + +def main(): + prompt = PythonInput() + + text = prompt.app.run() + print("You said: " + text) + +import pathlib +import collections +collections.Callable = collections.abc.Callable +try: + import readline +except ImportError: + import pyreadline as readline + +def complete_path(text, state): + incomplete_path = pathlib.Path(text) + if incomplete_path.is_dir(): + completions = [p.as_posix() for p in incomplete_path.iterdir()] + elif incomplete_path.exists(): + completions = [incomplete_path] + else: + exists_parts = pathlib.Path('.') + for part in incomplete_path.parts: + test_next_part = exists_parts / part + if test_next_part.exists(): + exists_parts = test_next_part + + completions = [] + for p in exists_parts.iterdir(): + p_str = p.as_posix() + if p_str.startswith(text): + completions.append(p_str) + return completions[state] + + +# we want to treat '/' as part of a word, so override the delimiters +#readline.set_completer_delims(' \t\n;') +#readline.parse_and_bind("tab: complete") +#readline.set_completer(complete_path) +#while True: +# print(input('tab complete a filename: ')) +#if __name__ == '__main__': + #sizex,sizey=getTerminalSize(os_platform()) + #print('width =',sizex,'height =',sizey) + #answer = prompt('Give me some input: ') + #print('You said: %s' % answer) + + +# From the documentation at +# https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messageboxw +MB_OKCANCEL = 1 +IDCANCEL = 2 +IDOK = 1 + +user32 = ctypes.WinDLL('user32') +MessageBox = user32.MessageBoxW +MessageBox.argtypes = w.HWND,w.LPCWSTR,w.LPCWSTR,w.UINT +MessageBox.restype = ctypes.c_int + +ret = MessageBox(None, 'message', 'title', MB_OKCANCEL) +if ret == IDOK: + print('OK') +elif ret == IDCANCEL: + print('CANCEL') +else: + print('ret =', ret) + +import curses, textwrap +from editor.editor import Editor + + +def xxsx(): + stdscr = curses.initscr() + xxx = Editor(stdscr, box=False) + while True: + xxx.stdscr.move(xxx.cur_pos_y, xxx.cur_pos_x) + loop = xxx.get_key() + if loop is False: + break + xxx.display() + print("\n".join(["".join(i) for i in xxx.text])) + +screen = curses.initscr() +screen.immedok(True) +try: + screen.border(0) + screen.addstr("Hello! Dropping you in to a command prompt...\n") + box1 = curses.newwin(20, 40, 6, 50) + box2 = curses.newwin(18,38,7,51) + box1.immedok(True) + box2.immedok(True) + text = "I want all of this text to stay inside its box. Why does it keep going outside its borders?" + text = "The quick brown fox jumped over the lazy dog." + text = "A long time ago, in a galaxy far, far away, there lived a young man named Luke Skywalker." + box1.box() + box2.addstr(1, 0, textwrap.fill(text, 38)) + + #box1.addstr("Hello World of Curses!") + + screen.getch() + +finally: + curses.endwin() \ No newline at end of file diff --git a/libs/helperegex.py b/libs/helperegex.py new file mode 100644 index 0000000..6074cc6 --- /dev/null +++ b/libs/helperegex.py @@ -0,0 +1,535 @@ +import re, os, string, psutil, random +from functools import lru_cache +from typing import List, Tuple, TypeVar, Generic, Any +try: + from itertools import izip as zip + + range = xrange +except: + pass + +def analyze_line(line): + """ + Analyzes a line of text to determine if it contains assignment or comparison. + + Parameters: + - line (str): The line of text to analyze. + + Returns: + - str: Description of the line content. + """ + # Regex pattern to find single '=' (assignment) and multiple '=' (comparison) + assignment_pattern = re.compile(r'^\s*[$]?\w+\s*=\s*.*$') #re.compile(r'^\s*\w+\s*=\s*.*$') + comparison_pattern = re.compile(r'==|>=|<=|!=|>|\<') + + # Check for assignment + if assignment_pattern.match(line): + # Further check if it contains comparison operators + if comparison_pattern.search(line): + return 0 + else: + return 1 + else: + return 0 + + +def rremovelist(data: list): + unicude = {}.fromkeys(data) + return list(unicude)[::-1] + + +def remove_duplicates_from_right(lst: list): + seen = set() + result: List[Tuple[str, int, Any]] = [] + + # Iterasi dari akhir ke awal + for item in reversed(lst): + if item not in seen: + seen.add(item) + result.append(item) + + # Membalikkan hasil untuk mempertahankan urutan awal + result.reverse() + + # Buat list akhir dengan mempertahankan posisi index + final_result: List[Tuple[str, int, Any]] = [] + seen.clear() + + for item in lst: + if item in result and item not in seen: + final_result.append(item) + seen.add(item) + + return final_result + +def lremove_duplicates_from_left(lst: list, limit: int = 1, total: int = 0): + removal_count = 0 + result: List[Tuple[str, int, Any]] = [] + if total == 0: + total = round(lst.__len__() / 2) + if total % 2 == 0: + total = int(total / 2) + elif total % 3: + total = round(total / 2) + else: + pass + if lst.__len__() > limit + total: + for item in lst: + if removal_count < limit: + removal_count += 1 + else: + result.append(item) + else: + result = lst + return result + +#clean +def clean_string(strings, regex, newstring): + s = re.sub(regex, newstring, strings) + # Hapus spasi putih di awal dan akhir string + s = s.strip() + return s + +# Fungsi ini menghapus karakter terakhir didalam input string + +def remove_laststring(data, spec, new=""): + maxreplace = data.count(spec) - 1 + return new.join(data.rsplit(spec, maxreplace)) + + +# Fungsi ini mereplace karakter terakhir didalam input string dan menggantikannya dengan karakter baru + +def rreplace(s:str, old:str, new:str, count:int): + return (s[::-1].replace(old[::-1], new[::-1], count))[::-1] + + +# Fungsi ini mereplace karakter berdasarkan posisi didalam input string dan menggantikannya dengan karakter baru + +def replaceusinglenght(data: str, lenght: int): + return data[-data.__len__() : data.__len__() - lenght] + + +# Fungsi ini mereplace satu kata atau karakter berdasarkan posisi didalam input string dan menggantikannya dengan karakter baru + +def replace_char_at_position(s, position, new_char): + if position < 0 or position >= len(s): + return s + + # Create a new string with the character replaced + new_string = s[:position] + new_char + s[position + 1 :] + + return new_string + + +# Fungsi ini mereplace semua kata atau karakter berdasarkan posisi didalam input string dan menggantikannya dengan karakter baru menggunakan regex + +def replacebypost(data: str, new_character: str, regex=r"\((.*)\)"): + for position in re.finditer(regex, data): + if position: + if (isinstance(position, list) and position.__len__() == 2) or ( + isinstance(position, tuple) and position.__len__() == 2 + ): + data = ( + data[: position[0]] + new_character + data[position[1] :].strip() + ) # data[position[1]+1:] + return data + + +# Fungsi ini untuk memanipulasi string dengan mengganti karakter khusus tertentu dengan karakter lain, kecuali karakter yang berada dalam tanda kutip. + +def cleanstring(data: str, spec: str, place: str, maximun=None, xplace=""): + """ + data: String input yang akan diproses. + spec: Karakter khusus yang akan diganti. + place: Karakter pengganti untuk spec. + maximun: Batas maksimum jumlah penggantian. + xplace: Karakter pengganti sementara untuk spec di luar tanda kutip.""" + + sessionplace: List[Tuple[str]] = ["", ""] # yang satu kalimat asli dan satunya kalimat pengganti + if "$(-%places%-)" in data: + numb, stop = 0, True + while stop != False: + places = "$(-%places%-{number})".format(number=str(numb)) + if stop != False and places not in data: + data = data.replace("$(-%places%-)", places) + sessionplace[0] = "$(-%places%-)" + sessionplace[1] = places + stop = False + break + numb += 1 + numb = 0 + + try: + select_x = re.findall(r"\"(.*?)\"", data.strip()) or re.findall( + r"'(.*?)'", data.strip() + ) + if select_x.__len__() != 0: + # mob = data.replace(spec, "${place}".format(place=place)) + for xe in select_x: + new = xe.replace(spec, "$(-%places%-)") + data = data.replace(xe, new) + + # replacebypost(regex=r"$\((.*)\)") + if isinstance(maximun, int): + maxcount = data.count("$(-%places%-)") + if maximun > 0 and maximun <= maxcount: + data = data.replace(spec, "").replace( + "$(-%places%-)", place, maximun + ) + else: + data = data.replace(spec, xplace).replace("$(-%places%-)", place) + else: + if maximun == None: + maximun = int(data.count(spec)) + data = data.replace(spec, xplace, maximun) + except: + pass + + if sessionplace[1].__len__() > 1: + data = data.replace(sessionplace[1], sessionplace[0]) + return data + + +def fullmacth(macth, data): + return re.fullmatch(macth, data) + + +def searching(patternRegex, data: str): + # Define the regex pattern to match the function name + pattern = re.compile(patternRegex) + + # Search for the pattern in the function definition + match = pattern.search(data) + + # If a match is found, return the function name + if match: + return match.group(1) + else: + return None + +####menemkan karakter diluar tanda kutipp +def count_character_outside_quotes(input_string, character): + """ + Count occurrences of a specified character outside of quotes. + + Parameters: + - input_string (str): The input string to process. + - character (str): The character to count. + + Returns: + - int: The number of occurrences of the character outside quotes. + """ + inside_quotes = False + count = 0 + quote_char = '' + + for i, char in enumerate(input_string): + if char in ('"', "'"): + if not inside_quotes: + # Entering a quoted section + inside_quotes = True + quote_char = char + elif char == quote_char: + # Exiting a quoted section + inside_quotes = False + elif char == character and not inside_quotes: + count += 1 + + return count + + +##### menghitung spasi sebelum karakter +def count_leading_spaces(line): + """ + Count the number of leading spaces in a given string. + + Parameters: + - line (str): The string to check. + + Returns: + - int: The number of leading spaces. + """ + match = re.match(r'^\s*', line) + if match: + return len(match.group(0)) + return 0 + +# Menemukan posisi text didalam variable string dengan mencocokan pola menggunakan regex dan menyimpannya kedalam tuple + +def findpositions(regex, string: str): + """ + regex: Pola regex yang digunakan untuk menemukan teks + string: String input yang akan diproses. + """ + postion_list: List[Tuple[str, int, Any]] = [] + try: + compiles = re.compile(regex, re.IGNORECASE) + for match in compiles.finditer(string): + if match: + # postion_list.append( (match.group(), match.span())) + postion_list.append(zip([match.group()], [match.span()])) + except: + for match in re.finditer(regex, string): + if match: + # postion_list.append( (match.group(), match.span())) + postion_list.append(zip([match.group()], [match.span()])) + return tuple(postion_list) + + +# findpostion("'(.*?)?'", "'sssss' hshs 'cbcbcbc'") + + +def split(regex, string: str, inject: str = "", post: list = [], mode: bool = False): + result: List[Tuple[str, int, Any]] = [] + if isinstance(regex, list) or isinstance(regex, tuple): + indices = list(regex) + if mode == False: + if indices.count(None) >= 1: + if indices[-1:][0] == None: + pass + else: + indices.append(None) + else: + indices.append(None) + result = [string[i:j] for i, j in zip(indices, indices[1:])] + else: + if indices[0] == 0: + pass + else: + indices.remove(0) + indices.insert(0, 0) + if indices[-1:][0] == string.__len__(): + pass + else: + indices.append(string.__len__()) + indices = sorted(set(indices)) + result = [string[x:y] for x, y in zip(indices, indices[1:])] + + elif isinstance(regex, str): + try: + if string.count(regex) > 1: + position = findpositions(regex=regex, string=string) + if position.__len__() != 0: + all_lines: List[Tuple[str, int, Any]] = [] + for x in position: + for c in x: + all_lines.append(c[1][0]) + if all_lines.__len__() != 0: + x = split(all_lines, string, mode=True) + if regex.count(" ") != 0: + for y in range(x.__len__()): + x[y] = x[y].replace(regex, "") + else: + for y in range(x.__len__()): + x[y] = x[y].replace(regex, "") + + x = list(filter(lambda v: v != "", x)) + result = x + else: + assert 12 == 11 + else: + try: + position = findpositions(regex=regex, string=string) + if position.__len__() != 0: + all_lines, textsearch = [[], []] + for x in position: + for c in x: + all_lines.append(c[1][0]) + textsearch.append(c[0]) + if all_lines.__len__() != 0: + x = split(all_lines, string, mode=True) + for y in range(x.__len__()): + for regex in textsearch: + x[y] = x[y].replace(regex, "") + + result = x + else: + assert 12 == 11 + except: + m = re.search(regex, string) + result = [string[: m.start()], string[m.end() :]] + except: + result = string.split(str(regex)) + elif isinstance(regex, int): + result = string[:regex], string[regex:] + else: + return + + result = list(filter(lambda x: x != "", result)) + if post.__len__() != 0: + if inject: + lenght = result.__len__() + for p in post: + if p > lenght: + result.insert(lenght, inject) + else: + result.insert(p, inject) + + return result + + +# Fungsi ini cukup efisien dalam menemukan semua posisi di mana substring dimulai dalam string lengkap, dengan menggunakan pendekatan langsung dan fallback regex jika diperlukan. + +def find_indices_of_substring(full_string, sub_string): + return [ + index + for index in range(len(full_string)) + if full_string.startswith(sub_string, index) + ] or [m.start() for m in re.finditer(re.escape(sub_string), full_string)] + + +def searchmissing(s, t): + """ + s: Kalimat input yang ingin dibandingkan. + t: Kalimat input yang ingin dibandingkan. + \ncontoh: + s = "I like eating apples and bananas" + t = "I apples bananas" + missing_words = searchmissing(s, t) + print(missing_words) # Output: ['like', 'eating', 'and'] + """ + res: List[Tuple[str, int, Any]] = [] # Daftar untuk menyimpan kata-kata yang hilang + t_words = t.split() # Memecah string `t` menjadi daftar kata + s_words = s.split() # Memecah string `s` menjadi daftar kata + size = s_words.__len__() # Mendapatkan jumlah kata dalam `s` + i = 0 # Indeks untuk melacak kata dalam `t` + j = 0 # Indeks untuk melacak kata dalam `s` + for j in range(size): + if s_words[j] == t_words[i]: + i += 1 # Jika kata dalam `s` sama dengan kata dalam `t`, pindah ke kata berikutnya di `t` + if i >= t_words.__len__(): + break # Jika semua kata dalam `t` ditemukan, hentikan loop + else: + res.append( + s_words[j] + ) # Jika kata dalam `s` tidak ada di `t`, tambahkan ke hasil + # Tambahkan kata-kata yang tersisa dalam `s` setelah indeks `j` + for k in range(j + 1, size): + res.append(s_words[k]) + return res # Kembalikan daftar kata yang hilang + + +def split_by_length(s, length): + # Create a list to store the split parts + parts: List[Tuple[str, int, Any]] = [] + + # Loop through the string, incrementing by the length each time + for i in range(0, len(s), length): + parts.append(s[i : i + length]) + + return parts + +def find_regex_in_list(pattern, lst: list, limit: int = None): + # Compile the regex pattern + regex = re.compile(pattern, re.IGNORECASE) + + # List to store the positions + positions: List[Tuple[str, int, Any]] = [] + + # Iterate through the list and search for the pattern + for i, item in enumerate(lst): + if limit == 0: + break + if regex.search(item): + positions.append(i) + + if limit != None and limit != 0: + limit -= 1 + return positions + + +def find_and_split(text, pattern): + """ + Menemukan teks setelah pola tertentu dan membagi string satu kali berdasarkan pola tersebut. + :param text: String input yang akan diproses + :param pattern: Pola regex yang digunakan untuk menemukan teks + :return: Tuple yang berisi dua bagian string yang dipisahkan oleh pola + """ + match = re.search(pattern, text) + if match: + # Menemukan posisi akhir dari pola yang cocok + split_pos = match.end() + # Memisahkan string satu kali berdasarkan posisi akhir dari pola yang cocok + part1 = text[:split_pos] + part2 = text[split_pos:] + return part1, part2 + else: + return text, None + + +# Fungsi mengembalikan daftar posisi karakter yang berada di luar kutipan. + +def find_unquoted_brace_positions(text, searchchar): + positions: List[Tuple[int, Any]] = [] + in_single_quote = False + in_double_quote = False + + for i, char in enumerate(text): + if char == "'" and not in_double_quote: + in_single_quote = not in_single_quote + elif char == '"' and not in_single_quote: + in_double_quote = not in_double_quote + elif char == searchchar and not in_single_quote and not in_double_quote: + positions.append(i) + + return positions + +def extract_quoted_text(text): + # Pola regex untuk menemukan teks di dalam tanda kutip tunggal atau ganda + pattern = r'(["\'])(.*?)(\1)' + matches = re.findall(pattern, text) + + # Mengambil hanya teks di dalam tanda kutip + quoted_texts = [match[1] for match in matches] + return quoted_texts + +def reversed(datalist: list | tuple): + process = datalist[::-1] + return process + +def reindent_function_blocks(input_string): + """ + Re-indent function blocks in the given string. + + Parameters: + - input_string (str): The input string containing function blocks. + + Returns: + - str: The re-indented string. + """ + def indent_lines(match): + """ + Indent lines within a function block. + """ + lines = match.group(0).splitlines() + indented_lines = [lines[0]] # Keep the function declaration line + indented_lines += [" " + line if line.strip() else "" for line in lines[1:]] + return "\n".join(indented_lines) + + # Use regex to find function blocks and apply indentation + pattern = re.compile(r'function|class\s+\w+\s*\{[^{}]*\}', re.DOTALL) + result = re.sub(pattern, indent_lines, input_string) + + return result + +def add_indent_to_lines(text, indent=' '): + """ + Add a specified indentation to each line of a given text. + + Parameters: + - text (str): The input text to be indented. + - indent (str): The string to use as indentation (default is 4 spaces). + + Returns: + - str: The text with added indentation. + """ + # Split text into lines + lines = text.splitlines() + + # Add indentation to each line + indented_lines = [indent + line for line in lines] + + # Join lines back into a single string + indented_text = '\n'.join(indented_lines) + + return indented_text diff --git a/libs/https.py b/libs/https.py new file mode 100644 index 0000000..53f8437 --- /dev/null +++ b/libs/https.py @@ -0,0 +1,112 @@ +try: + import httpx as requests +except ImportError: + import requests +from requests.exceptions import ConnectionError, Timeout, HTTPError +import importlib, inspect + +DEFAULT_HEADERS = { + "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36", + "connection": "keep-alive", +} + + +class Fetch: + def __init__(self, headers: dict = {}): + self.headers = headers or DEFAULT_HEADERS + try: + self.sync_client = requests.Client(headers=self.headers) + self.async_client = requests.AsyncClient(headers=self.headers) + except AttributeError: + self.sync_client = requests.Session() + self.sync_client.headers.update(self.headers) + self.async_client = self.sync_client + + def check_class_in_package(package_name, class_name): + try: + # Import the package + package = importlib.import_module(package_name) + # Cek apakah kelas ada di dalam modul + if hasattr(package, class_name): + cls = getattr(package, class_name) + # Pastikan itu adalah kelas, bukan atribut atau fungsi + if inspect.isclass(cls): + return True, "ClassFound" + return False, "ClassNotFound" + except ModuleNotFoundError: + return False, "ModuleNotFoundError" + + self.check_class_in_package = check_class_in_package + + def _request_sync(self, method, url, max_retries=5, timeout=5, **kwargs): + retries = 0 + while retries < max_retries: + try: + response = self.sync_client.request( + method, url, timeout=timeout, **kwargs + ) + response.raise_for_status() + return response + except (ConnectionError, Timeout) as e: + retries += 1 + print(f"Connection failed ({e}). Retrying {retries}/{max_retries}...") + except HTTPError as http_err: + print(f"HTTP error occurred: {http_err}") + break + raise Exception(f"Failed to connect to {url} after {max_retries} retries.") + + async def _request_async(self, method, url, max_retries=5, timeout=5, **kwargs): + retries = 0 + itsAsync, _ = self.check_class_in_package(requests.__name__, "AsyncClient") + while retries < max_retries: + try: + if itsAsync: + response = await self.async_client.request( + method, url, timeout=timeout, **kwargs + ) + else: + response = self.async_client.request( + method, url, timeout=timeout, **kwargs + ) + response.raise_for_status() + return response + except (ConnectionError, Timeout) as e: + retries += 1 + print(f"Connection failed ({e}). Retrying {retries}/{max_retries}...") + except HTTPError as http_err: + print(f"HTTP error occurred: {http_err}") + break + raise Exception(f"Failed to connect to {url} after {max_retries} retries.") + + def get(self, url, max_retries=5, timeout=5, async_mode=False, **kwargs): + """Performs a GET request, either synchronously or asynchronously.""" + if async_mode: + return self._request_async("GET", url, max_retries, timeout, **kwargs) + else: + return self._request_sync("GET", url, max_retries, timeout, **kwargs) + + def post(self, url, max_retries=5, timeout=5, async_mode=False, **kwargs): + """Performs a POST request, either synchronously or asynchronously.""" + if async_mode: + return self._request_async("POST", url, max_retries, timeout, **kwargs) + else: + return self._request_sync("POST", url, max_retries, timeout, **kwargs) + + def __enter__(self): + """Enter the runtime context related to this object.""" + return self + + def __exit__(self, exc_type, exc_value, traceback): + """Exit the runtime context related to this object.""" + self.sync_client.close() + + async def __aenter__(self): + """Enter the async runtime context related to this object.""" + return self + + async def __aexit__(self, exc_type, exc_value, traceback): + """Exit the async runtime context related to this object.""" + try: + await self.async_client.aclose() + except: + self.async_client.close() diff --git a/libs/randoms.py b/libs/randoms.py new file mode 100644 index 0000000..86304cb --- /dev/null +++ b/libs/randoms.py @@ -0,0 +1,134 @@ +import random +try: + from .helperegex import split, findpositions, searchmissing, re as regex +except: + from helperegex import split, findpositions, searchmissing, re as regex + + +paterns = regex.compile(r''' + \s # one whitespace character, though I think this is perhaps unnecessary + \d* # 0 or more digits + \. # a dot + \d{2} # 2 digits + ''', regex.VERBOSE) + + + +def randomDigits(digits): + try: + lower = 10**(digits-1) + upper = 10**digits - 1 + return random.randint(lower, upper) + except: + lower = "0"*(digits-1) + upper = "9"*(digits-1) + if lower != "" and upper != "": + lower = "".join(["1", lower]) + upper = "".join(["9", upper]) + else: + lower = "1" + upper = "9" + return random.randrange(int(lower), int(upper), upper.__len__()) + + + + + +#string = "echo 'fhfhf & ddd' && 'fhfhfddd' && cd \"fhfhf && ddd\"" +"""""def compiltes(string:str): + if string == "": + return [] + _output = [] + + xout = findpositions(r"'(.*?)?'|\"(.*?)?\"", string) + lenght = xout.__len__() + cv = 0 + checkpoint = () + checkpoint2 = [] + place = "<%%" + newstring = string + keyplace = "" + + maxpend = [] + digit = 1 + xct = regex.finditer(r"<%%(.*?)?>", string) + for xc in xct: + if xc: + if xc.group(0).count(" ") == 0: + #search = regex.search("/d", xc.group(0)) + maxdigit = xc.group(1) + maxpend.append(int(maxdigit)) + #print(maxpend) + + if maxpend.__len__() != 0: + _s_s = str(max(maxpend)).__len__()+1 + digit = int(_s_s) + + score_record = [] + for y in xout: + for x in y: + + if x[0].find("&&") and x[0].count("&&") != 0: + keyplace = "&&" + elif x[0].find("&") and x[0].count("&") != 0: + keyplace = "&" + + #print(keyplace) + if lenght != 0: + + score = randomDigits(digit) + if score not in score_record: + score_record.append(score) + else: + if score in score_record: + score = randomDigits(digit) + if score in score_record: + score = randomDigits(digit) + score_record.clear() + else: + score_record.append(score) + + newword = "".join([place, str(score), ">"]) + newpad = regex.sub(r"&&|&", newword, x[0]) #.replace(keyplace, newword)####bugss + if x[0].count("&") == 0: + cv +=1 + #pass + else: + if lenght == xout.__len__() - cv: + pass + else: + minus = newword.__len__()+keyplace.__len__() + checkpoint2.append((x[1][0]+minus-keyplace.__len__(), x[1][-1:][0]+minus+1)) + + + string = string.replace(x[0], newpad, 1) + checkpoint = checkpoint+((x[0], newword, score, keyplace), ) + + lenght -= 1 + + #print("checkpoint:", checkpoint, "\n") + #print("Newkeys:", string) + for x in split("&", string): + for y in checkpoint: + + if x.count(y[1]) !=0: + oldstring = regex.search("'(.*?)?'", x) or regex.search("\"(.*?)?\"", x) + if oldstring: + x = x.replace(oldstring.group(0), y[0], 1) + # print(1) + else: + x = x.replace(y[1], y[3]) + # print(2) + #print(oldstring, y) + + _output.append(x.strip()) + checkpoint = tuple(set(checkpoint)- set(checkpoint)) + return _output""""" + + #print("Missing :", searchmissing(string, newstring)) + #print(checkpoint, checkpoint2) + #print(split( checkpoint2[0], string)) + + #import re + #re.sub + #print(string.split(keyplace)) diff --git a/libs/system_manajemen.py b/libs/system_manajemen.py new file mode 100644 index 0000000..d42d3b5 --- /dev/null +++ b/libs/system_manajemen.py @@ -0,0 +1,84 @@ +import signal +import sys +import psutil +import os +import threading +import time +import multiprocessing, signal +from concurrent.futures import ProcessPoolExecutor, as_completed + +def terminate_process(pid): + try: + process = psutil.Process(pid) + process.terminate() # Mengirim sinyal untuk menghentikan proses + process.wait(timeout=3) # Menunggu proses benar-benar berhenti + except psutil.NoSuchProcess: + os._exit(0) + except psutil.TimeoutExpired: + os._exit(0) + + +def set_low_priority(pid): + def signal_handler(signum, frame): + if signum == signal.SIGINT: # Ctrl+C + print("[INFO] Detected Ctrl+C. Shutting down gracefully...") + terminate_process(pid) + # SIGTSTP handling only if available + elif signum == getattr(signal, 'SIGTSTP', None): # Ctrl+Z (Unix-like only) + print("[INFO] Detected Ctrl+Z. Process suspended.") + terminate_process(pid) + + # Set up signal handlers + signal.signal(signal.SIGINT, signal_handler) + if hasattr(signal, 'SIGTSTP'): + signal.signal(signal.SIGTSTP, signal_handler) + + if os.name == 'nt': + process = psutil.Process(pid) + process.nice(psutil.BELOW_NORMAL_PRIORITY_CLASS) + else: # Unix-like systems + os.nice(19) + + +def initializer(stop_event): + global stop_flag + stop_flag = stop_event + signal.signal(signal.SIGINT, handle_child_signal) + +def handle_child_signal(signum, frame): + print(f"Child process received signal {signum}.") + stop_flag.set() + +class SafeProcessExecutor: + def __init__(self, max_workers=None): + # Membuat Event menggunakan Manager untuk sinkronisasi antar proses + self.manager = multiprocessing.Manager() + self.stop_flag = self.manager.Event() + self.executor = ProcessPoolExecutor( + max_workers=max_workers, + initializer=initializer, + initargs=(self.stop_flag,) + ) + self.futures = [] + + def submit(self, fn, *args, **kwargs): + """Submit a function to the executor for execution.""" + future = self.executor.submit(fn, *args, **kwargs) + self.futures.append(future) + return future + + def shutdown(self, wait=True): + """Shut down the executor and terminate all running processes.""" + self.stop_flag.set() # Menghentikan semua proses yang sedang berjalan + self.executor.shutdown(wait=wait) + + def get_results(self): + """Get results from all completed futures.""" + results = [] + for future in as_completed(self.futures): + try: + result = future.result() + results.append(result) + except Exception as e: + results.append(f"Error retrieving result: {e}") + return results diff --git a/libs/timeout.py b/libs/timeout.py new file mode 100644 index 0000000..333aaca --- /dev/null +++ b/libs/timeout.py @@ -0,0 +1,47 @@ +import os, sys, psutil, shutil +try: + from GPUtil import getGPUs +except: + getGPUs = None + +def timeout_v1(): + # Get CPU or GPU usage percentage + if getGPUs==None: + cpu_usage = psutil.cpu_percent() + else: + cpu_usage = getGPUs() + # Get RAM usage percentage + ram_usage = psutil.virtual_memory().percent + # Divide both percentages by 1000 + cpu_usage_divided = cpu_usage / 100 + ram_usage_divided = ram_usage / 100 + if cpu_usage_divided<0.1: + cpu_usage_divided = cpu_usage_divided*2 + if ram_usage_divided<0.1: + ram_usage_divided = ram_usage_divided*2 + data = [round(cpu_usage_divided, 1), round(ram_usage_divided, 1)] + return round(sum(data) / data.__len__(), 1) + + +def timeout_v2(pid=os.getpid()): + # Mendapatkan ID proses saat ini + process = psutil.Process(pid) + + # Mendapatkan penggunaan CPU dan RAM untuk proses tersebut + if getGPUs==None: + cpu_usage = psutil.cpu_percent() + else: + cpu_usage = getGPUs() + ram_usage = process.memory_percent() # Persentase penggunaan RAM oleh proses + + # Pembagian dengan 100 untuk mendapatkan nilai antara 0 dan 1 + cpu_usage_divided = cpu_usage / 100 + ram_usage_divided = ram_usage / 100 + + if cpu_usage_divided < 0.1: + cpu_usage_divided = cpu_usage_divided * 2 + if ram_usage_divided < 0.1: + ram_usage_divided = ram_usage_divided * 2 + + data = [round(cpu_usage_divided, 1), round(ram_usage_divided, 1)] + return round(sum(data) / len(data), 1) diff --git a/libs/titlecommand.py b/libs/titlecommand.py new file mode 100644 index 0000000..0adeab1 --- /dev/null +++ b/libs/titlecommand.py @@ -0,0 +1,25 @@ +import os, ctypes, logging +try: + from .errrorHandler import complex_handle_errors +except: + try: + from errrorHandler import complex_handle_errors + except: + from libs.errrorHandler import complex_handle_errors + +@complex_handle_errors(loggering=logging) +def set_console_title(title): + """Mengatur judul jendela Command Prompt.""" + ctypes.windll.kernel32.SetConsoleTitleW(title) + +@complex_handle_errors(loggering=logging) +def get_console_title(): + """Mendapatkan judul jendela Command Prompt.""" + # Buffer untuk menyimpan judul + buffer_size = 256 + buffer = ctypes.create_unicode_buffer(buffer_size) + + # Memanggil fungsi API Windows untuk mendapatkan judul jendela + ctypes.windll.kernel32.GetConsoleTitleW(buffer, buffer_size) + + return buffer.value \ No newline at end of file From 7a3ac66c48d33209bf5b11c6cb58e019146e33f6 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sun, 25 Aug 2024 13:27:00 +0700 Subject: [PATCH 03/42] Delete libs/hello.txt --- libs/hello.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 libs/hello.txt diff --git a/libs/hello.txt b/libs/hello.txt deleted file mode 100644 index 8b13789..0000000 --- a/libs/hello.txt +++ /dev/null @@ -1 +0,0 @@ - From 28d0d9ab40e8e7c034a3b8c07926690176c03e87 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sun, 25 Aug 2024 13:31:22 +0700 Subject: [PATCH 04/42] Add files via upload ini file utama aplikasi --- supernano.py | 674 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 supernano.py diff --git a/supernano.py b/supernano.py new file mode 100644 index 0000000..0bbde5a --- /dev/null +++ b/supernano.py @@ -0,0 +1,674 @@ +import urwid +import pyperclip +import os, sys, shutil, logging, time, threading, argparse +from datetime import datetime +try: + from libs.titlecommand import get_console_title, set_console_title + from libs.cmd_filter import shorten_path, validate_folder + from libs.errrorHandler import complex_handle_errors + from libs.system_manajemen import set_low_priority, SafeProcessExecutor + from libs.timeout import timeout_v2, timeout_v1 + from libs.filemanager import ( + StreamFile, + create_file_or_folder, + resolve_relative_path_v2, + all_system_paths, + ) +except: + try: + from .titlecommand import get_console_title, set_console_title + from .cmd_filter import shorten_path, validate_folder + from .errrorHandler import complex_handle_errors + from .system_manajemen import set_low_priority, SafeProcessExecutor + from .timeout import timeout_v2, timeout_v1 + from .filemanager import ( + StreamFile, + create_file_or_folder, + resolve_relative_path_v2, + all_system_paths, + ) + except: + from titlecommand import get_console_title, set_console_title + from cmd_filter import shorten_path, validate_folder + from errrorHandler import complex_handle_errors + from system_manajemen import set_low_priority, SafeProcessExecutor + from timeout import timeout_v2, timeout_v1 + from filemanager import ( + StreamFile, + create_file_or_folder, + resolve_relative_path_v2, + all_system_paths, + ) + + + +set_low_priority(os.getpid()) +#########mendapatkan process terbaik tanpa membebani ram dan cpu +thisfolder, _x = all_system_paths +__version__ = "1.5.3" + +fileloogiing = os.path.join(thisfolder, "cache", "file_browser.log").replace("\\", "/") + +if not os.path.isfile(fileloogiing): + open(fileloogiing, "a+") +elif os.path.getsize(fileloogiing) > 0: + with open(fileloogiing, "wb+") as f: + f.truncate(0) + +for handler in logging.root.handlers[:]: + logging.root.removeHandler(handler) + +logging.basicConfig( + filename=fileloogiing, + filemode="a", + format="%(asctime)s, %(msecs)d %(name)s %(levelname)s [ %(filename)s-%(module)s-%(lineno)d ] : %(message)s", + datefmt="%H:%M:%S", + level=logging.DEBUG, +) + + +def setTitle(title: str): + """ + Fungsi setTitle bertugas untuk mengatur judul konsol (console title) berdasarkan parameter title yang diberikan.\n + Jika inputan title memiliki panjang lebih dari 30 karakter maka potong karakternya + """ + process = title + Getitles = get_console_title() + if os.path.isdir(process) or os.path.isfile(process): + length = int(process.__len__() / 2) + if length < 28: + x = process.__len__() + nexts = int(50 - x) - (x / 2) + if nexts < 28: + length = int((28 - nexts) + nexts) + else: + length = nexts + elif length > 50: + length = 28 + process = shorten_path(process, length) + + if Getitles.startswith("Win-SuperNano"): + output = str("Win-SuperNano {titles}".format(titles=process)) + else: + output = title + set_console_title(output) + + +@complex_handle_errors(loggering=logging) +def parse_args(): + """ + Fungsi parse_args bertugas untuk mendapatkan\menangkap argument konsol (console title) yang diberikan oleh user.\n + """ + parser = argparse.ArgumentParser( + description="An extension on nano for editing directories in CLI." + ) + parser.add_argument("path", help="Target file or directory to edit.") + args = vars(parser.parse_args()) + path = args.get("path", ".").strip().replace("\\", "/") + if os.path.exists(path): + if validate_folder(path=path): + pass + else: + logging.error(f"ERROR - {path} path cannot access") + exit() + else: + logging.error(f"ERROR - {path} path does not exist") + exit() + + return resolve_relative_path_v2(path).replace("\\", "/") + + +class PlainButton(urwid.Button): + """ + Class PlainButton bertugas untuk mengkoustomisasi button dan menghilangkan karakter < dan >.\n + """ + button_left = urwid.Text("") + button_right = urwid.Text("") + + +class ClipboardTextBox(urwid.Edit): + def keypress(self, size, key): + if key == "ctrl c": + self.copy_to_clipboard() + elif key == "ctrl v": + self.paste_from_clipboard() + else: + return super().keypress(size, key) + + def copy_to_clipboard(self): + self.clipboard = self.get_edit_text() + + def paste_from_clipboard(self): + if hasattr(self, "clipboard"): + cursor_pos = self.edit_pos + text = self.get_edit_text() + self.set_edit_text(text[:cursor_pos] + self.clipboard + text[cursor_pos:]) + self.edit_pos = cursor_pos + len(self.clipboard) + + +class FileBrowserApp: + """ + Kelas FileBrowserApp yang sedang Anda kembangkan adalah text editor berbasis console yang menggunakan Python 3.6 ke atas dengan dukungan urwid[curses]. + + Pembuat: Ramsyan Tungga Kiansantang (ID) | Github: LcfherShell + + Tanggal dibuat: 21 Agustus 2024 + + Jika ada bug silahkan kunjungi git yang telah tertera diatas + """ + @complex_handle_errors(loggering=logging) + def __init__(self, start_path="."): + "Mengatur path awal, judul aplikasi, widget, dan layout utama. Juga mengatur alarm untuk memuat menu utama dan memulai loop aplikasi." + self.current_path = start_path + self.current_file_name = None # Track current file name + self.undo_stack, self.redo_stack = [[], []] # Stack for undo # Stack for redo + self.overlay = None # Overlay untuk popup + + # Set title + setTitle("Win-SuperNano v{version}".format(version=__version__)) + + # Create widgets + + self.title_widget = urwid.Text( + "Win-SuperNano v{version} CopyRight: LcfherShell@{year}\n".format( + version=__version__, year=datetime.now().year + ), + align="center", + ) + self.loading_widget = urwid.Text("Loading, please wait...", align="center") + self.main_layout = urwid.Filler( + urwid.Pile([self.title_widget, self.loading_widget]), valign="middle" + ) + + # Create menu + self.menu_columns = urwid.Columns([]) + self.menu_pile = urwid.Pile([self.menu_columns]) + + # Create footer and status text + self.footer_text = urwid.Text("Press ctrl + q to exit, Arrow keys to navigate") + self.status_text = urwid.Text( + "Ctrl+S : Save file Ctrl+D : Delete File Ctrl+Z : Undo Edit Ctrl+Y : Redo Edit F1 : Redirects input to copy paste Ctrl+R : Refresh UI ESC: Quit " + ) + + # Event loop + self.loop = urwid.MainLoop(self.main_layout, unhandled_input=self.handle_input) + + self.loading_alarm = self.loop.set_alarm_in( + round(timeout_v1() * timeout_v2(), 1) + 1, + lambda loop, user_data: self.load_main_menu(), + ) + self.system_alarm = None + + @complex_handle_errors(loggering=logging) + def load_main_menu(self): + "Menyiapkan dan menampilkan menu utama setelah periode loading, dan menghapus alarm loading." + # self.loading_widget.set_text("Press key R") + self.loop.remove_alarm(self.loading_alarm) # Hentikan alarm + self.loading_alarm = None + self.switch_to_secondary_layout() + + def switch_to_secondary_layout(self): + "Mengubah layout aplikasi ke menu utama yang telah disiapkan." + self.setup_main_menu() + self.loop.widget = self.main_layout + + @complex_handle_errors(loggering=logging) + def setup_main_menu(self): + "Menyiapkan dan mengatur widget untuk menu utama, termasuk daftar file, editor teks, dan tombol-tombol fungsional. Mengatur layout untuk tampilan aplikasi." + # Define widgets + self.file_list = urwid.SimpleFocusListWalker(self.get_file_list()) + self.file_list_box = urwid.ListBox(self.file_list) + self.text_editor = urwid.Edit(multiline=True) + self.current_focus = 0 # 0 for textbox1, 1 for textbox2 + # Wrap text_editor with BoxAdapter for scrollable content + self.text_editor_scrollable = urwid.LineBox( + urwid.Filler(self.text_editor, valign="top") + ) + + # Define menu widgets + self.quit_button = PlainButton("Quit", align="center") + urwid.connect_signal(self.quit_button, "click", self.quit_app) + + self.search_edit = urwid.Edit( + "Search or Create: ", multiline=False, align="left" + ) + search_limited = urwid.BoxAdapter( + urwid.Filler(self.search_edit, valign="top"), height=1 + ) + + self.search_button = PlainButton("Execute", align="center") + urwid.connect_signal(self.search_button, "click", self.search_file) + + padded_button = urwid.Padding( + self.search_button, align="center", width=("relative", 50) + ) # Tombol berada di tengah dengan lebar 50% dari total layar + padded_button = urwid.AttrMap( + padded_button, None, focus_map="reversed" + ) # Mengatur warna saat tombol difokuskan + + urwid.connect_signal( + self.text_editor.base_widget, "change", self.set_focus_on_click, 0 + ) + urwid.connect_signal( + self.search_edit.base_widget, "change", self.set_focus_on_click, 1 + ) + # Menu layout + self.menu_columns = urwid.Columns( + [ + ( + "weight", + 2, + urwid.AttrMap(search_limited, None, focus_map="reversed"), + ), + ( + "weight", + 3, + urwid.AttrMap(padded_button, None, focus_map="reversed"), + ), + ( + "weight", + 1, + urwid.AttrMap(self.quit_button, None, focus_map="reversed"), + ), + ] + ) + + self.menu_pile = urwid.Pile([self.menu_columns]) + + # Layout + self.main_layout = urwid.Frame( + header=self.menu_pile, + body=urwid.Columns( + [ + ( + "weight", + 1, + urwid.LineBox(self.file_list_box, title="Directory Files"), + ), + ( + "weight", + 2, + urwid.LineBox( + urwid.Pile([self.text_editor_scrollable]), title="TextBox" + ), + ), + ] + ), + footer=urwid.Pile([self.footer_text, self.status_text]), + ) + urwid.TrustedLoop(self.loop).set_widget(self.main_layout) + self.system_alarm = self.loop.set_alarm_in( + timeout_v2() + 1, + lambda loop, user_data: self.system_usage(), + ) + + @complex_handle_errors(loggering=logging) + def setup_popup(self, options, title, descrip: str = ""): + "Menyiapkan konten dan layout untuk menu popup dengan judul, deskripsi, dan opsi yang diberikan." + # Konten popup + menu_items = [] + if descrip: + menu_items = [urwid.Text(descrip, align="center"), urwid.Divider("-")] + + # Tambahkan opsi ke dalam menu popup + for option in options: + menu_items.append(option) + + # Tambahkan tombol untuk menutup popup + menu_items.append(PlainButton("Close", on_press=self.close_popup)) + + # Buat listbox dari opsi yang sudah ada + popup_content = urwid.ListBox(urwid.SimpleFocusListWalker(menu_items)) + + # Tambahkan border dengan judul + self.popup = urwid.LineBox(popup_content, title=title) + + def on_option_selected(self, button): + "Menangani pilihan opsi dari popup dengan menutup popup dan mengembalikan label opsi yang dipilih." + urwid.emit_signal(button, "click") + getbutton = button.get_label() + self.close_popup(None) + return getbutton + + @complex_handle_errors(loggering=logging) + def show_popup(self, title: str, descrip: str, menus: list): + "Menampilkan popup menu dengan judul, deskripsi, dan daftar opsi yang diberikan." + # Siapkan popup dengan judul, descrip, dan opsi + self.setup_popup(title=title, descrip=descrip, options=menus) + + # Tentukan ukuran dan posisi popup + popup_width = 35 + popup_height = 25 + self.overlay = urwid.Overlay( + self.popup, + self.main_layout, + "center", + ("relative", popup_width), + "middle", + ("relative", popup_height), + ) + self.loop.widget = self.overlay + + @complex_handle_errors(loggering=logging) + def close_popup(self, button): + "Menutup popup menu dan mengembalikan tampilan ke layout utama." + self.overlay = None + self.loop.widget = self.main_layout + + @complex_handle_errors(loggering=logging) + def get_file_list(self): + "Mengambil daftar file dan direktori di path saat ini, termasuk opsi untuk naik satu level di direktori jika bukan di direktori root." + files = [] + if self.current_path != ".": # Cek apakah bukan di direktori root + button = PlainButton("...") + urwid.connect_signal(button, "click", self.go_up_directory) + files.append(urwid.AttrMap(button, None, focus_map="reversed")) + + for f in os.listdir(self.current_path): + if os.path.isdir(os.path.join(self.current_path, f)): + f = f + "/" + button = PlainButton(f) + urwid.connect_signal(button, "click", self.open_file, f) + files.append(urwid.AttrMap(button, None, focus_map="reversed")) + return files + + def handle_input(self, key): + "Menangani input keyboard dari pengguna untuk berbagai tindakan seperti keluar, menyimpan, menghapus, undo, redo, copy, paste, dan refresh UI." + if key in ("ctrl q", "ctrl Q", "esc"): + self.show_popup( + menus=[PlainButton("OK", on_press=lambda _x: self.quit_app())], + title="Confirm Quit", + descrip="Are you sure you Quit", + ) + + elif key in ("ctrl s", "ctrl S"): + # self.save_file() + self.show_popup( + menus=[ + PlainButton( + "OK", + on_press=lambda _x: self.close_popup(None) + if self.save_file() + else None, + ) + ], + title="Save File", + descrip="Are you sure you want to save the file changes", + ) + + elif key in ("ctrl d", "ctrl D"): + self.show_popup( + menus=[ + PlainButton( + "OK", + on_press=lambda _x: self.close_popup(None) + if self.delete_file() + else None, + ) + ], + title="Delete File", + descrip="Are you sure you want to delete the file", + ) + elif key in ("ctrl z", "ctrl Z"): + self.undo_edit() + elif key in ("ctrl y", "ctrl Y"): + self.redo_edit() + elif key in ("ctrl c", "ctrl C"): + self.copy_text_to_clipboard() + elif key in ("ctrl v", "ctrl V"): + self.paste_text_from_clipboard() + elif key in ("ctrl r", "ctrl R"): + self.switch_to_secondary_layout() + elif key in ("f1", "ctrl e", "ctrl E"): + self.current_focus = 1 if self.current_focus == 0 else 0 + + @complex_handle_errors(loggering=logging) + def get_current_edit(self): + "Mengembalikan widget edit yang sedang difokuskan (text editor atau search edit)." + if self.current_focus == 0: + return self.text_editor.base_widget + elif self.current_focus == 1: + return self.search_edit.base_widget + return None + + def set_focus_on_click(self, widget, new_edit_text, index): + "Mengatur fokus pada widget edit berdasarkan klik dan indeks." + self.current_focus = index + + @complex_handle_errors(loggering=logging) + def copy_text_to_clipboard(self): + "Menyalin teks dari widget edit yang sedang aktif ke clipboard." + current_edit = self.get_current_edit() + if current_edit: + if hasattr(current_edit, "edit_pos") and hasattr( + current_edit, "get_edit_text" + ): + self.footer_text.set_text("Text copied to clipboard.") + cursor_position = current_edit.edit_pos + pyperclip.copy( + current_edit.get_edit_text()[cursor_position:] + or current_edit.get_edit_text() + ) + + @complex_handle_errors(loggering=logging) + def paste_text_from_clipboard(self): + "Menempelkan teks dari clipboard ke widget edit yang sedang aktif." + pasted_text = pyperclip.paste() # Mengambil teks dari clipboard + current_edit = self.get_current_edit() + if current_edit: + if hasattr(current_edit, "edit_pos") and hasattr( + current_edit, "get_edit_text" + ): + current_text = ( + current_edit.get_edit_text() + ) # Mendapatkan teks saat ini di widget Edit + cursor_position = ( + current_edit.edit_pos + ) # Mendapatkan posisi kursor saat ini + + # Membagi teks berdasarkan posisi kursor + text_before_cursor = current_text[:cursor_position] + text_after_cursor = current_text[cursor_position:] + + # Gabungkan teks sebelum kursor, teks yang ditempelkan, dan teks setelah kursor + new_text = text_before_cursor + pasted_text + text_after_cursor + + # Set teks baru dan sesuaikan posisi kursor + current_edit.set_edit_text(new_text) + current_edit.set_edit_pos(cursor_position + len(pasted_text)) + self.footer_text.set_text("Text paste from clipboard.") + + @complex_handle_errors(loggering=logging) + def go_up_directory(self, button): + "Naik satu level ke direktori atas dan memperbarui daftar file." + self.current_path = os.path.dirname(self.current_path) + self.file_list[:] = self.get_file_list() + + @complex_handle_errors(loggering=logging) + def open_file(self, button, file_name): + "Membuka file yang dipilih, membaca isinya, dan menampilkannya di text editor. Jika itu adalah direktori, berpindah ke direktori tersebut." + file_path = os.path.join(self.current_path, file_name) + if os.path.isdir(file_path): + if validate_folder(file_path): + self.current_path = file_path + self.file_list[:] = self.get_file_list() + else: + self.footer_text.set_text("Folder access denied!") + else: + if validate_folder(os.path.dirname(file_path)): + try: + with open(file_path, "r+", encoding="utf-8") as f: + content = f.read() + except UnicodeDecodeError: + with open(file_path, "r+", encoding="latin-1") as f: + content = f.read() + content = content.replace("\t", " " * 4) + + self.undo_stack.append(content) + self.text_editor.set_edit_text(content) + self.current_file_name = file_name # Track the current file name + self.main_layout.body.contents[1][0].set_title(file_name) + else: + self.footer_text.set_text("File access denied!") + + @complex_handle_errors(loggering=logging) + def save_file(self): + "Menyimpan perubahan yang dilakukan pada file saat ini dan mengembalikan status keberhasilan." + if self.current_file_name: + file_path = os.path.join(self.current_path, self.current_file_name) + with open(file_path, "w+", encoding="utf-8") as f: + f.write(self.text_editor.get_edit_text()) + self.footer_text.set_text("File saved successfully!") + return True + + @complex_handle_errors(loggering=logging) + def delete_file(self): + "Menghapus file yang dipilih dan memperbarui daftar file serta text editor dan mengembalikan status keberhasilan." + if self.current_file_name: + file_path = os.path.join(self.current_path, self.current_file_name) + if os.path.isfile(file_path): + os.remove(file_path) + self.text_editor.set_edit_text("") + self.file_list[:] = self.get_file_list() + self.footer_text.set_text("File deleted successfully!") + self.current_file_name = None # Clear the current file name + else: + self.footer_text.set_text("File does not exist!") + return True + + @complex_handle_errors(loggering=logging) + def save_undo_state(self): + "Menyimpan status saat ini dari text editor ke stack undo dan mengosongkan stack redo." + # Save the current content of the text editor for undo + current_text = self.text_editor.get_edit_text() + self.undo_stack.append(current_text) + self.redo_stack.clear() # Clear redo stack on new change + + @complex_handle_errors(loggering=logging) + def undo_edit(self): + "Melakukan undo terhadap perubahan terakhir pada text editor dengan mengembalikan status dari stack undo." + if self.undo_stack: + # Save the current state to redo stack + self.redo_stack.append(self.text_editor.get_edit_text()) + + # Restore the last state + last_state = self.undo_stack.pop() + self.text_editor.set_edit_text(last_state) + self.footer_text.set_text("Undo performed.") + + @complex_handle_errors(loggering=logging) + def redo_edit(self): + "Melakukan redo terhadap perubahan terakhir yang diundo dengan mengembalikan status dari stack redo." + if self.redo_stack: + # Save the current state to undo stack + self.undo_stack.append(self.text_editor.get_edit_text()) + + # Restore the last redone state + last_state = self.redo_stack.pop() + self.text_editor.set_edit_text(last_state) + self.footer_text.set_text("Redo performed.") + + @complex_handle_errors(loggering=logging) + def search_file(self, button): + "Mencari file atau folder berdasarkan input pencarian, membuka file jika ditemukan, atau memperbarui daftar file jika folder ditemukan." + search_query = self.search_edit.get_edit_text().replace("\\", "/").strip() + if search_query: + if ":" in search_query: + if os.path.isfile(search_query): + dirname, file_name = os.path.dirname( + search_query + ), os.path.basename(search_query) + try: + with open(search_query, "r+", encoding="utf-8") as f: + content = f.read() + except UnicodeDecodeError: + with open(search_query, "r+", encoding="latin-1") as f: + content = f.read() + content = content.replace("\t", " " * 4) + + self.undo_stack.append(content) + self.text_editor.set_edit_text(content) + self.current_file_name = file_name # Track the current file name + self.main_layout.body.contents[1][0].set_title(file_name) + + elif os.path.isdir(search_query): + dirname = search_query + else: + x, _y = os.path.split(search_query) + if self.current_path.replace("\\", "/") == x.replace( + "\\", "/" + ) and os.path.isdir(x): + search_query = str(create_file_or_folder(search_query)) + self.update_ui() + self.file_list[:] = self.get_file_list() + + dirname = None + + if dirname: + self.current_path = dirname + self.file_list[:] = self.get_file_list() + else: + search_results = [ + f for f in os.listdir(self.current_path) if search_query in f + ] + self.file_list[:] = self.create_file_list(search_results) + else: + self.file_list[:] = self.get_file_list() + self.footer_text.set_text(f"Search results for '{search_query}'") + + @complex_handle_errors(loggering=logging) + def create_file_list(self, files): + "Membuat daftar widget untuk file yang ditemukan sesuai hasil pencarian." + widgets = [] + for f in files: + if os.path.isdir(os.path.join(self.current_path, f)): + f = f + "/" + button = PlainButton(f) + urwid.connect_signal(button, "click", self.open_file, f) + widgets.append(urwid.AttrMap(button, None, focus_map="reversed")) + return widgets + + def system_usage(self): + "Memantau penggunaan CPU dan menampilkan peringatan jika konsumsi CPU tinggi." + timemming = timeout_v1() + if timemming > 0.87: + self.footer_text.set_text("High CPU utilization alert") + + def update_ui(self): + "Memperbarui tampilan UI aplikasi." + self.loop.draw_screen() + + def quit_app(self, button=None): + "Menghentikan aplikasi dan menghapus alarm sistem jika ada." + if self.system_alarm != None: + self.loop.remove_alarm(self.system_alarm) # Hentikan alarm + self.system_alarm = None + raise urwid.ExitMainLoop() + + def run(self): + "Memulai loop utama urwid untuk menjalankan aplikasi." + self.loop.run() + + +def main(path: str): + app = FileBrowserApp(start_path=path) + app.run() + + +if __name__ == "__main__": + set_low_priority(os.getpid()) + #########mendapatkan process terbaik tanpa membebani ram dan cpu + + safe_executor = SafeProcessExecutor(max_workers=2) #########mendapatkan process terbaik tanpa membebani cpu + safe_executor.submit(main, path=parse_args()) + time.sleep(timeout_v2()) + safe_executor.shutdown(wait=True)###mmenunggu process benar-benar berhenti tanpa memaksanya + rd = StreamFile( + file_path=fileloogiing, + buffer_size=os.path.getsize(fileloogiing) + 2, + print_delay=timeout_v2(), + ) #########mendapatkan process terbaik membaca file logging tanpa membebani cpu + for r in rd.readlines(): + print(r) + rd.eraseFile() #membersihkan loggging + rd.close() From 3d49e7388b593225b9d684fac73518b2c494a2e2 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sun, 25 Aug 2024 13:33:14 +0700 Subject: [PATCH 05/42] Create file_browser.log --- cache/file_browser.log | 1 + 1 file changed, 1 insertion(+) create mode 100644 cache/file_browser.log diff --git a/cache/file_browser.log b/cache/file_browser.log new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/cache/file_browser.log @@ -0,0 +1 @@ + From c2fc1e38d45b94347253499ffa349a05b2d41283 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sun, 25 Aug 2024 15:41:09 +0700 Subject: [PATCH 06/42] supernano.py --- supernano.py | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/supernano.py b/supernano.py index 0bbde5a..38eaae1 100644 --- a/supernano.py +++ b/supernano.py @@ -125,30 +125,9 @@ class PlainButton(urwid.Button): button_left = urwid.Text("") button_right = urwid.Text("") - -class ClipboardTextBox(urwid.Edit): - def keypress(self, size, key): - if key == "ctrl c": - self.copy_to_clipboard() - elif key == "ctrl v": - self.paste_from_clipboard() - else: - return super().keypress(size, key) - - def copy_to_clipboard(self): - self.clipboard = self.get_edit_text() - - def paste_from_clipboard(self): - if hasattr(self, "clipboard"): - cursor_pos = self.edit_pos - text = self.get_edit_text() - self.set_edit_text(text[:cursor_pos] + self.clipboard + text[cursor_pos:]) - self.edit_pos = cursor_pos + len(self.clipboard) - - -class FileBrowserApp: +class SuperNano: """ - Kelas FileBrowserApp yang sedang Anda kembangkan adalah text editor berbasis console yang menggunakan Python 3.6 ke atas dengan dukungan urwid[curses]. + Kelas SuperNano yang sedang Anda kembangkan adalah text editor berbasis TUI yang menggunakan Python versi 3.6 ke atas dan module python urwid[curses]. Pembuat: Ramsyan Tungga Kiansantang (ID) | Github: LcfherShell @@ -651,7 +630,7 @@ def run(self): def main(path: str): - app = FileBrowserApp(start_path=path) + app = SuperNano(start_path=path) app.run() From 8fc5c5f60b4cb90eb7ad13aedffda028483985da Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sun, 25 Aug 2024 15:53:32 +0700 Subject: [PATCH 07/42] head file v1.5.3 --- supernano.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/supernano.py b/supernano.py index 38eaae1..bf060ad 100644 --- a/supernano.py +++ b/supernano.py @@ -102,7 +102,13 @@ def parse_args(): parser = argparse.ArgumentParser( description="An extension on nano for editing directories in CLI." ) - parser.add_argument("path", help="Target file or directory to edit.") + parser.add_argument( + "path", + default=os.path.split(thisfolder)[0], + nargs="?", + type=str, + help="Target file or directory to edit.", + ) args = vars(parser.parse_args()) path = args.get("path", ".").strip().replace("\\", "/") if os.path.exists(path): From 202607eecd471689a8d7ec15b3266f5d83975600 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 26 Aug 2024 00:59:51 +0700 Subject: [PATCH 08/42] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b895af6..e10f8cd 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ # SuperNano -Ini adalah Aplikasi Editor berbasis TUI +Ini adalah Aplikasi Text-Editor berbasis TUI From 9889360097f49822cc72d5521df309add6fba5e2 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 26 Aug 2024 13:27:45 +0700 Subject: [PATCH 09/42] Update supernano.py --- supernano.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/supernano.py b/supernano.py index bf060ad..a2cbe1a 100644 --- a/supernano.py +++ b/supernano.py @@ -60,13 +60,13 @@ logging.basicConfig( filename=fileloogiing, - filemode="a", + filemode="w", + encoding=sys.getfilesystemencoding(), format="%(asctime)s, %(msecs)d %(name)s %(levelname)s [ %(filename)s-%(module)s-%(lineno)d ] : %(message)s", datefmt="%H:%M:%S", - level=logging.DEBUG, + level=logging.ERROR, ) - def setTitle(title: str): """ Fungsi setTitle bertugas untuk mengatur judul konsol (console title) berdasarkan parameter title yang diberikan.\n From 56bec08389c52101a9578838e5f59ae95c89a2b9 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 26 Aug 2024 13:31:04 +0700 Subject: [PATCH 10/42] Update supernano.py --- supernano.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/supernano.py b/supernano.py index a2cbe1a..c98703f 100644 --- a/supernano.py +++ b/supernano.py @@ -482,7 +482,7 @@ def open_file(self, button, file_name): else: if validate_folder(os.path.dirname(file_path)): try: - with open(file_path, "r+", encoding="utf-8") as f: + with open(file_path, "r+", encoding=sys.getfilesystemencoding()) as f: content = f.read() except UnicodeDecodeError: with open(file_path, "r+", encoding="latin-1") as f: @@ -501,8 +501,12 @@ def save_file(self): "Menyimpan perubahan yang dilakukan pada file saat ini dan mengembalikan status keberhasilan." if self.current_file_name: file_path = os.path.join(self.current_path, self.current_file_name) - with open(file_path, "w+", encoding="utf-8") as f: - f.write(self.text_editor.get_edit_text()) + try: + with open(file_path, "w+", encoding=sys.getfilesystemencoding()) as f: + f.write(self.text_editor.get_edit_text()) + except: + with open(file_path, "w+", encoding="latin-1") as f: + f.write(self.text_editor.get_edit_text()) self.footer_text.set_text("File saved successfully!") return True From a6db755e3afc899f762d4d963b5d52e3934e0f64 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 26 Aug 2024 14:32:33 +0700 Subject: [PATCH 11/42] Update supernano.py --- supernano.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/supernano.py b/supernano.py index c98703f..5f8687e 100644 --- a/supernano.py +++ b/supernano.py @@ -195,6 +195,9 @@ def load_main_menu(self): def switch_to_secondary_layout(self): "Mengubah layout aplikasi ke menu utama yang telah disiapkan." self.setup_main_menu() + if self.loading_alarm != None: + self.loop.remove_alarm(self.loading_alarm) # Hentikan alarm loading jika masih ada + self.loading_alarm = None self.loop.widget = self.main_layout @complex_handle_errors(loggering=logging) From 036ccb40391be6ac3680ffc7177055186f79a993 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 26 Aug 2024 19:55:52 +0700 Subject: [PATCH 12/42] Update README.md --- README.md | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e10f8cd..a11c572 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,60 @@ -# SuperNano -Ini adalah Aplikasi Text-Editor berbasis TUI +Berikut adalah dokumentasi untuk script `SuperNano`, sebuah text editor berbasis console yang menggunakan Python 3.6 ke atas dan pustaka `urwid[curses]`. + +--- + +# Dokumentasi SuperNano + +## Deskripsi +`SuperNano` adalah sebuah text editor berbasis console yang dikembangkan menggunakan Python dan pustaka `urwid[curses]`. Aplikasi ini dirancang untuk memberikan pengguna kemampuan untuk mengedit teks, mengelola file, dan melakukan inspeksi modul Python langsung dari antarmuka berbasis console. `SuperNano` mendukung beberapa fitur seperti undo-redo, clipboard (copy-paste), pencarian file, dan inspeksi modul Python. + +## Fitur Utama +- **Text Editing**: Editor teks dengan dukungan multiline, undo-redo, copy-paste, dan penyimpanan file. +- **File Management**: Memungkinkan navigasi direktori, membuka dan menyimpan file, serta membuat dan menghapus file. + +## Kelas dan Metode + +### 1. `SuperNano` +`SuperNano` adalah kelas utama yang mengatur seluruh aplikasi, termasuk inisialisasi, pembuatan menu, dan manajemen UI. + +#### Atribut: +- **current_path**: Menyimpan path direktori saat ini. +- **current_file_name**: Menyimpan nama file yang sedang dibuka. +- **undo_stack**, **redo_stack**: Stack yang digunakan untuk menyimpan state teks guna mendukung fitur undo-redo. +- **overlay**: Widget yang digunakan untuk menampilkan popup. +- **loop**: Objek `urwid.MainLoop` yang menangani event loop aplikasi. +- **loading_alarm**, **system_alarm**: Alarm untuk mengatur timing penggantian layout dan memonitor sistem. + +#### Metode: +- **`__init__(self, start_path=".")`**: Inisialisasi kelas, menyiapkan path awal, widget, dan memulai event loop. +- **`load_main_menu(self)`**: Menyiapkan dan menampilkan menu utama setelah periode loading. +- **`switch_to_secondary_layout(self)`**: Mengubah layout aplikasi ke menu utama. +- **`setup_main_menu(self)`**: Mengatur widget untuk menu utama, termasuk daftar file, editor teks, dan tombol-tombol fungsional. +- **`setup_popup(self, options, title, descrip="")`**: Menyiapkan konten dan layout untuk menu popup. +- **`show_popup(self, title, descrip, menus)`**: Menampilkan popup menu dengan judul, deskripsi, dan opsi yang diberikan. +- **`close_popup(self, button)`**: Menutup popup dan mengembalikan tampilan ke layout utama. +- **`get_file_list(self)`**: Mengambil daftar file dan direktori di path saat ini. +- **`handle_input(self, key)`**: Menangani input keyboard untuk berbagai tindakan seperti keluar, menyimpan, menghapus, undo, redo, copy-paste, dan refresh UI. +- **`get_current_edit(self)`**: Mengembalikan widget edit yang sedang difokuskan (text editor atau search edit). +- **`set_focus_on_click(self, widget, new_edit_text, index)`**: Mengatur fokus pada widget edit berdasarkan klik dan indeks. +- **`copy_text_to_clipboard(self)`**: Menyalin teks dari widget edit yang sedang aktif ke clipboard. +- **`paste_text_from_clipboard(self)`**: Menempelkan teks dari clipboard ke widget edit yang sedang aktif. + + +## Penggunaan +1. **Menjalankan Aplikasi**: Jalankan script `SuperNano` dengan Python 3.6 ke atas di terminal Anda. +2. **Navigasi File**: Gunakan panah atas dan bawah untuk memilih file di direktori. Tekan Enter untuk membuka file. +3. **Edit Teks**: Setelah file terbuka, teks dapat diedit langsung di editor. Gunakan `Ctrl+S` untuk menyimpan perubahan. +4. **Undo-Redo**: Gunakan `Ctrl+Z` untuk undo dan `Ctrl+Y` untuk redo. +5. **Copy-Paste**: Gunakan `Ctrl+C` untuk copy dan `Ctrl+V` untuk paste. +6. **Keluar dari Aplikasi**: Tekan `Ctrl+Q` atau `ESC` untuk keluar dari aplikasi. + +## Lisensi +Aplikasi ini dibuat oleh Ramsyan Tungga Kiansantang dan dilisensikan di bawah [Lisensi GPL v3](https://www.gnu.org/licenses/gpl-3.0.html). Untuk kontribusi atau pelaporan bug, silakan kunjungi repositori Github yang telah disediakan. + +## Versi +- **Versi**: V1.5.3 +- **Tanggal Rilis**: 21 Agustus 2024 + +--- + +Dengan dokumentasi ini, Anda dapat memahami bagaimana `SuperNano` bekerja dan bagaimana menggunakannya secara efektif. From 510ea8d016b9c24b4f62c568d6e4535e06c18bab Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 26 Aug 2024 20:12:24 +0700 Subject: [PATCH 13/42] Update README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index a11c572..15a2974 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,12 @@ Berikut adalah dokumentasi untuk script `SuperNano`, sebuah text editor berbasis 5. **Copy-Paste**: Gunakan `Ctrl+C` untuk copy dan `Ctrl+V` untuk paste. 6. **Keluar dari Aplikasi**: Tekan `Ctrl+Q` atau `ESC` untuk keluar dari aplikasi. +## Cara Penggunaan +Jalankan script ini melalui command line dengan memberikan argumen berupa path file atau direktori yang ingin diedit. Contoh: +``` +python supernano.py /path/to/directory_or_file +``` + ## Lisensi Aplikasi ini dibuat oleh Ramsyan Tungga Kiansantang dan dilisensikan di bawah [Lisensi GPL v3](https://www.gnu.org/licenses/gpl-3.0.html). Untuk kontribusi atau pelaporan bug, silakan kunjungi repositori Github yang telah disediakan. From 1755dd8ee5306ec4e2b2789531e473679a87e150 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 26 Aug 2024 20:13:49 +0700 Subject: [PATCH 14/42] Update README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 15a2974..7573100 100644 --- a/README.md +++ b/README.md @@ -63,4 +63,8 @@ Aplikasi ini dibuat oleh Ramsyan Tungga Kiansantang dan dilisensikan di bawah [L --- -Dengan dokumentasi ini, Anda dapat memahami bagaimana `SuperNano` bekerja dan bagaimana menggunakannya secara efektif. +## Kesimpulan +`SuperNano` adalah editor teks berbasis konsol yang dirancang untuk memudahkan pengelolaan file dan direktori secara langsung dari command line. Aplikasi ini menawarkan alat yang kuat untuk pengguna yang bekerja di lingkungan berbasis teks. + +Jika ada pertanyaan atau butuh bantuan lebih lanjut terkait implementasi, jangan ragu untuk menghubungi pengembang atau melihat dokumentasi tambahan yang mungkin tersedia. + From 9a9194b5847a65b98fc6a2cc64e52cbe00af7fb8 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 26 Aug 2024 23:47:11 +0700 Subject: [PATCH 15/42] Create requirements.txt Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- requirements.txt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..41cee53 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +windows-curses==2.3.3 +urwid==2.6.15 +pyperclip==1.9.0 +psutil +logging +httpx From a14a5fc8eb3c9dcb6d664dc568f1b31b86fa6cb6 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Fri, 30 Aug 2024 20:39:45 +0700 Subject: [PATCH 16/42] README.md Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7573100..d88c516 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +Banner + Berikut adalah dokumentasi untuk script `SuperNano`, sebuah text editor berbasis console yang menggunakan Python 3.6 ke atas dan pustaka `urwid[curses]`. --- From 35c44fd9b2effcd782cadfccb065ea26a749bce6 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sat, 31 Aug 2024 09:42:56 +0700 Subject: [PATCH 17/42] supernano.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- supernano.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/supernano.py b/supernano.py index 5f8687e..a333392 100644 --- a/supernano.py +++ b/supernano.py @@ -10,6 +10,7 @@ from libs.timeout import timeout_v2, timeout_v1 from libs.filemanager import ( StreamFile, + validate_file, create_file_or_folder, resolve_relative_path_v2, all_system_paths, @@ -23,6 +24,7 @@ from .timeout import timeout_v2, timeout_v1 from .filemanager import ( StreamFile, + validate_file, create_file_or_folder, resolve_relative_path_v2, all_system_paths, @@ -35,6 +37,7 @@ from timeout import timeout_v2, timeout_v1 from filemanager import ( StreamFile, + validate_file, create_file_or_folder, resolve_relative_path_v2, all_system_paths, @@ -483,7 +486,9 @@ def open_file(self, button, file_name): else: self.footer_text.set_text("Folder access denied!") else: - if validate_folder(os.path.dirname(file_path)): + if validate_folder(os.path.dirname(file_path)) and validate_file( + file_path, os.path.getsize(file_path) or 20, 6 + ): try: with open(file_path, "r+", encoding=sys.getfilesystemencoding()) as f: content = f.read() From 669a75cd89145a9710ceec5116d0cb4106013d16 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sat, 31 Aug 2024 09:45:25 +0700 Subject: [PATCH 18/42] filemanager.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- libs/filemanager.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/libs/filemanager.py b/libs/filemanager.py index a920820..a55fe87 100644 --- a/libs/filemanager.py +++ b/libs/filemanager.py @@ -328,6 +328,36 @@ def is_binary_file(file_path): except Exception as e: return False +def validate_file(file_path, max_size_mb=100, max_read_time=2): + try: + # Periksa ukuran file + file_size = os.path.getsize(file_path) + if file_size > max_size_mb * 1024 * 1024: + return False + + # Mulai waktu pembacaan + start_time = time.time() + + # Baca file + with open(file_path, "rb+", + encoding=sys.getfilesystemencoding()) as f: + # Baca bagian pertama file untuk memeriksa apakah file biner + first_bytes = f.read(1024) + if b"\x00" in first_bytes: + return False + + # Lanjutkan membaca file + while f.read(1024): + # Periksa waktu yang telah digunakan untuk membaca + elapsed_time = time.time() - start_time + if elapsed_time > max_read_time: + return False + + # Jika semua pemeriksaan lolos, file valid + return True + + except Exception as e: + return False def check_class_in_package(package_name, class_name): try: From f42756a0fe935731dba4726346490fc4eb557fa9 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sat, 31 Aug 2024 10:05:08 +0700 Subject: [PATCH 19/42] filemanager.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- libs/filemanager.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libs/filemanager.py b/libs/filemanager.py index a55fe87..0419046 100644 --- a/libs/filemanager.py +++ b/libs/filemanager.py @@ -339,8 +339,7 @@ def validate_file(file_path, max_size_mb=100, max_read_time=2): start_time = time.time() # Baca file - with open(file_path, "rb+", - encoding=sys.getfilesystemencoding()) as f: + with open(file_path, "rb") as f: # Baca bagian pertama file untuk memeriksa apakah file biner first_bytes = f.read(1024) if b"\x00" in first_bytes: From be8de0dbf0a24ad4a426b65c61d32624729534a2 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sun, 1 Sep 2024 14:24:15 +0700 Subject: [PATCH 20/42] supernano.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- supernano.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/supernano.py b/supernano.py index a333392..66c1a0b 100644 --- a/supernano.py +++ b/supernano.py @@ -97,7 +97,7 @@ def setTitle(title: str): set_console_title(output) -@complex_handle_errors(loggering=logging) +@complex_handle_errors(loggering=logging, nomessagesNormal=False) def parse_args(): """ Fungsi parse_args bertugas untuk mendapatkan\menangkap argument konsol (console title) yang diberikan oleh user.\n @@ -112,8 +112,8 @@ def parse_args(): type=str, help="Target file or directory to edit.", ) - args = vars(parser.parse_args()) - path = args.get("path", ".").strip().replace("\\", "/") + args = parser.parse_args() + path = resolve_relative_path(args.path, "") if os.path.exists(path): if validate_folder(path=path): pass From 76f3ebe28c1703185eab927b90b0ac129d09e5e4 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sun, 1 Sep 2024 14:30:45 +0700 Subject: [PATCH 21/42] supernano.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- supernano.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supernano.py b/supernano.py index 66c1a0b..84b1267 100644 --- a/supernano.py +++ b/supernano.py @@ -113,7 +113,7 @@ def parse_args(): help="Target file or directory to edit.", ) args = parser.parse_args() - path = resolve_relative_path(args.path, "") + path = resolve_relative_path(args.path, "") or "." if os.path.exists(path): if validate_folder(path=path): pass From 7849cbc01cb92ecbb4774f0a956023139ededc36 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sun, 1 Sep 2024 21:33:14 +0700 Subject: [PATCH 22/42] supernano.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- supernano.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/supernano.py b/supernano.py index 84b1267..fa75440 100644 --- a/supernano.py +++ b/supernano.py @@ -651,7 +651,10 @@ def main(path: str): app = SuperNano(start_path=path) app.run() - +if '__file__' in globals(): + print("This script should not be imported. Run it directly.") + sys.exit() + if __name__ == "__main__": set_low_priority(os.getpid()) #########mendapatkan process terbaik tanpa membebani ram dan cpu From e286793d2fcf64d8e082e831c5b2d4132e894ea8 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sun, 1 Sep 2024 22:42:53 +0700 Subject: [PATCH 23/42] README.md Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- README.md | 105 +++++++++++++++++++++++++++--------------------------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index d88c516..ba89763 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,73 @@ Banner -Berikut adalah dokumentasi untuk script `SuperNano`, sebuah text editor berbasis console yang menggunakan Python 3.6 ke atas dan pustaka `urwid[curses]`. +Here is the documentation for the `SuperNano` script, a powerful console-based text editor specialized for Windows 8, 10, 11 platforms. --- -# Dokumentasi SuperNano +# SuperNano Documentation -## Deskripsi -`SuperNano` adalah sebuah text editor berbasis console yang dikembangkan menggunakan Python dan pustaka `urwid[curses]`. Aplikasi ini dirancang untuk memberikan pengguna kemampuan untuk mengedit teks, mengelola file, dan melakukan inspeksi modul Python langsung dari antarmuka berbasis console. `SuperNano` mendukung beberapa fitur seperti undo-redo, clipboard (copy-paste), pencarian file, dan inspeksi modul Python. +## Description +`SuperNano` is a console-based text editor developed using Python and the `urwid[curses]` library. It is designed to give users the ability to edit text, manage files, and inspect Python modules directly from a console-based interface. SuperNano supports several features such as undo-redo, clipboard (copy-paste), file search, and Python module inspection. -## Fitur Utama -- **Text Editing**: Editor teks dengan dukungan multiline, undo-redo, copy-paste, dan penyimpanan file. -- **File Management**: Memungkinkan navigasi direktori, membuka dan menyimpan file, serta membuat dan menghapus file. +## Key Features +- **Text Editing**: Text editor with multiline support, undo-redo, copy-paste, and file saving. +- **File Management**: Allows directory navigation, opening and saving files, and creating and deleting files. -## Kelas dan Metode +## Classes and Methods ### 1. `SuperNano` -`SuperNano` adalah kelas utama yang mengatur seluruh aplikasi, termasuk inisialisasi, pembuatan menu, dan manajemen UI. - -#### Atribut: -- **current_path**: Menyimpan path direktori saat ini. -- **current_file_name**: Menyimpan nama file yang sedang dibuka. -- **undo_stack**, **redo_stack**: Stack yang digunakan untuk menyimpan state teks guna mendukung fitur undo-redo. -- **overlay**: Widget yang digunakan untuk menampilkan popup. -- **loop**: Objek `urwid.MainLoop` yang menangani event loop aplikasi. -- **loading_alarm**, **system_alarm**: Alarm untuk mengatur timing penggantian layout dan memonitor sistem. - -#### Metode: -- **`__init__(self, start_path=".")`**: Inisialisasi kelas, menyiapkan path awal, widget, dan memulai event loop. -- **`load_main_menu(self)`**: Menyiapkan dan menampilkan menu utama setelah periode loading. -- **`switch_to_secondary_layout(self)`**: Mengubah layout aplikasi ke menu utama. -- **`setup_main_menu(self)`**: Mengatur widget untuk menu utama, termasuk daftar file, editor teks, dan tombol-tombol fungsional. -- **`setup_popup(self, options, title, descrip="")`**: Menyiapkan konten dan layout untuk menu popup. -- **`show_popup(self, title, descrip, menus)`**: Menampilkan popup menu dengan judul, deskripsi, dan opsi yang diberikan. -- **`close_popup(self, button)`**: Menutup popup dan mengembalikan tampilan ke layout utama. -- **`get_file_list(self)`**: Mengambil daftar file dan direktori di path saat ini. -- **`handle_input(self, key)`**: Menangani input keyboard untuk berbagai tindakan seperti keluar, menyimpan, menghapus, undo, redo, copy-paste, dan refresh UI. -- **`get_current_edit(self)`**: Mengembalikan widget edit yang sedang difokuskan (text editor atau search edit). -- **`set_focus_on_click(self, widget, new_edit_text, index)`**: Mengatur fokus pada widget edit berdasarkan klik dan indeks. -- **`copy_text_to_clipboard(self)`**: Menyalin teks dari widget edit yang sedang aktif ke clipboard. -- **`paste_text_from_clipboard(self)`**: Menempelkan teks dari clipboard ke widget edit yang sedang aktif. - - -## Penggunaan -1. **Menjalankan Aplikasi**: Jalankan script `SuperNano` dengan Python 3.6 ke atas di terminal Anda. -2. **Navigasi File**: Gunakan panah atas dan bawah untuk memilih file di direktori. Tekan Enter untuk membuka file. -3. **Edit Teks**: Setelah file terbuka, teks dapat diedit langsung di editor. Gunakan `Ctrl+S` untuk menyimpan perubahan. -4. **Undo-Redo**: Gunakan `Ctrl+Z` untuk undo dan `Ctrl+Y` untuk redo. -5. **Copy-Paste**: Gunakan `Ctrl+C` untuk copy dan `Ctrl+V` untuk paste. -6. **Keluar dari Aplikasi**: Tekan `Ctrl+Q` atau `ESC` untuk keluar dari aplikasi. - -## Cara Penggunaan -Jalankan script ini melalui command line dengan memberikan argumen berupa path file atau direktori yang ingin diedit. Contoh: +`SuperNano` is the main class that manages the entire application, including initialization, menu creation, and UI management. + +#### Attributes: +- **current_path**: Stores the current directory path. +- **current_file_name**: Stores the name of the current file. +- **undo_stack**, **redo_stack**: Stack used to store text state to support undo-redo feature. +- **overlay**: Widget used to display a popup. +- **loop**: The `urwid.MainLoop` object that handles application loop events. +- **loading_alarm**, **system_alarm**: Alarms for timing layout changes and monitoring the system. + +#### Methods: +- **`__init__(self, start_path=“.”)`**: Initialize the class, set up the start path, widgets, and start the event loop. +- **`load_main_menu(self)`**: Set up and display the main menu after the loading period. +- **`switch_to_secondary_layout(self)`**: Changes the application layout to the main menu. +- **`setup_main_menu(self)`**: Set up widgets for the main menu, including the file list, text editor, and functional buttons. +- **`setup_popup(self, options, title, descrip=“”)`**: Sets up the content and layout for the popup menu. +- **`show_popup(self, title, descrip, menus)`**: Displays the popup menu with the given title, description, and options. +- **`close_popup(self, button)`**: Closes the popup and returns to the main layout. +- **`get_file_list(self)`**: Retrieve a list of files and directories in the current path. +- **`handle_input(self, key)`**: Handles keyboard input for various actions such as exit, save, delete, undo, redo, copy-paste, and UI refresh. +- **`get_current_edit(self)`**: Returns the currently focused edit widget (text editor or search edit). +- **`set_focus_on_click(self, widget, new_edit_text, index)`**: Sets the focus on the edit widget based on click and index. +- **`copy_text_to_clipboard(self)`**: Copies the text from the current edit widget to the clipboard. +- **`paste_text_from_clipboard(self)`**: Paste text from the clipboard into the current edit widget. + + +## Usage +1. **Running the Application**: Run the `SuperNano` script with Python 3.6 and above in your terminal. +2. **Navigate Files**: Use the up and down arrows to select files in the directory. Press Enter to open the file. +3. **Edit Text**: Once the file is open, the text can be edited directly in the editor. Use `Ctrl+S` to save changes. +4. **Undo-Redo**: Use `Ctrl+Z` to undo and `Ctrl+Y` to redo. +5. **Copy-Paste**: Use `Ctrl+C` to copy and `Ctrl+V` to paste. +6. **Exit Application**: Press `Ctrl+Q` or `ESC` to exit the application. + +## How to use +Run this script through the command line by giving an argument in the form of the path of the file or directory you want to edit. Example: ``` python supernano.py /path/to/directory_or_file ``` +or visit [main](https://github.com/LcfherShell/SuperNano/tree/main) -## Lisensi -Aplikasi ini dibuat oleh Ramsyan Tungga Kiansantang dan dilisensikan di bawah [Lisensi GPL v3](https://www.gnu.org/licenses/gpl-3.0.html). Untuk kontribusi atau pelaporan bug, silakan kunjungi repositori Github yang telah disediakan. +## License +This application was created by Ramsyan Tungga Kiansantang and is licensed under the [GPL v3 License](https://www.gnu.org/licenses/gpl-3.0.html). For contributions or bug reporting, please visit the provided Github repository. -## Versi -- **Versi**: V1.5.3 -- **Tanggal Rilis**: 21 Agustus 2024 +## Version +- **Version**: V1.5.3 +- **Release Date**: August 21, 2024 --- -## Kesimpulan -`SuperNano` adalah editor teks berbasis konsol yang dirancang untuk memudahkan pengelolaan file dan direktori secara langsung dari command line. Aplikasi ini menawarkan alat yang kuat untuk pengguna yang bekerja di lingkungan berbasis teks. +## Conclusion +`SuperNano` is a console-based text editor designed to make it easy to manage files and directories directly from the command line. It offers powerful tools for users working in a text-based environment. -Jika ada pertanyaan atau butuh bantuan lebih lanjut terkait implementasi, jangan ragu untuk menghubungi pengembang atau melihat dokumentasi tambahan yang mungkin tersedia. +If you have any questions or need further assistance with the implementation, feel free to contact the developer or check out any additional documentation that may be available. [Email Support](mailto:alfiandecker2@gmail.com,ramstungga2@gmail.com) From 29a35e7bf852ff561441ea73263403020b1be731 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sun, 1 Sep 2024 23:04:39 +0700 Subject: [PATCH 24/42] Add files via upload Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- GPL-3.0.txt | 154 ++++++++++++++++++++++++++++++++++++++++++++++++ MIT-LICENSE.txt | 21 +++++++ 2 files changed, 175 insertions(+) create mode 100644 GPL-3.0.txt create mode 100644 MIT-LICENSE.txt diff --git a/GPL-3.0.txt b/GPL-3.0.txt new file mode 100644 index 0000000..b9c1343 --- /dev/null +++ b/GPL-3.0.txt @@ -0,0 +1,154 @@ +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) [2024] [Ramsyan Tungga Kiansantang][LcfherShell] +Copyright (C) [2024] Free Software Foundation, Inc. +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +=== + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + + Preamble + +The GNU General Public License is a free, copyleft license for software +and other kinds of works. The licenses for most software and other +practical works are designed to take away your freedom to share and +change the works. By contrast, our General Public Licenses are intended +to guarantee your freedom to share and change all versions of a program +to make sure it remains free software for all its users. + +When we speak of free software, we are referring to freedom, not price. +Our General Public Licenses are designed to make sure that you have the +freedom to distribute copies of free software (and charge for this service +if you wish), that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free programs, +and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone +to deny you these rights or to ask you to surrender the rights. These +restrictions translate to certain responsibilities for you if you distribute +copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis +or for a fee, you must pass on the same freedoms to the recipients that you +received. You must make sure that they, too, receive or can get the source +code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If +the software is modified by someone else and passed on, we want its +recipients to know that what they have is not the original, so that any +problems introduced by others will not reflect on the original authors' +reputations. + +Finally, any free program is threatened constantly by software patents. +We wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program +proprietary. To prevent this, we have made it clear that any patent must +be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + +0. Definitions. + +"This License" refers to version 3 of the GNU General Public License. + +"Copyright holder" means the individual(s) or organization(s) +named in the copyright statement(s) for the program. + +"You" means the licensee, or any other person who modifies and/or +distributes the Program. + +"Program" means the work licensed under this License. + +"Modified version" means the Program with changes made to it. + +"Source code" means the preferred form of the Program for making +modifications to it. + +"Object code" means any non-source form of a work. + +"Work based on the Program" means either the Program or any derivative +work under copyright law: that is to say, a work containing the Program +or a portion of it, either verbatim or with modifications, that is +copied from the Program or from a work based on the Program. + +"Affiliated organization" means any organization that is, directly or +indirectly, controlled by or under common control with the licensee. + +1. Source Code. + +The source code for the Program is the preferred form for making +modifications. The source code must be distributed as part of the Program. + +2. Copyleft. + +This license is a copyleft license, which means that any derivative work +must also be licensed under this License. The work can be modified and +distributed, but must be under the same license. + +3. Distribution of Modified Code. + +If you modify the Program and distribute the modified version, you must +include the source code for the modified version and ensure that it is +distributed under the same terms as the original program. + +4. License. + +This License is designed to ensure that the Program remains free. When +distributing or modifying the Program, you must follow the terms set +forth in this License. + +5. No Warranty. + +There is no warranty for the Program. It is provided "as is" without +any express or implied warranties. + +6. Termination. + +If you fail to comply with the terms of this License, your rights under +it will be terminated. + +7. Additional Terms. + +You may not impose additional restrictions on the rights granted by this +License. + +8. Acceptance. + +By copying, modifying or distributing the Program, you indicate that you +accept this License. + +9. Miscellaneous. + +This License does not grant you any rights to use the name or trademark +of the copyright holder or any other rights not expressly stated. + +END OF TERMS AND CONDITIONS diff --git a/MIT-LICENSE.txt b/MIT-LICENSE.txt new file mode 100644 index 0000000..d41968e --- /dev/null +++ b/MIT-LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [2024] [Ramsyan Tungga Kiansantang] [LcfherShell] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 15c3f4614fc3ac4c0d4ffc9ff5f600c914c8bdf7 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sun, 1 Sep 2024 23:19:50 +0700 Subject: [PATCH 25/42] README.md Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ba89763..d2b023f 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ python supernano.py /path/to/directory_or_file or visit [main](https://github.com/LcfherShell/SuperNano/tree/main) ## License -This application was created by Ramsyan Tungga Kiansantang and is licensed under the [GPL v3 License](https://www.gnu.org/licenses/gpl-3.0.html). For contributions or bug reporting, please visit the provided Github repository. +This application was created by Ramsyan Tungga Kiansantang and is licensed under the [GPL v3 License](https://github.com/LcfherShell/SuperNano/blob/V1.5.3/GPL-3.0.txt. For contributions or bug reporting, please visit the provided Github repository. ## Version - **Version**: V1.5.3 From 18baf0f54f7f9df77b6f2bb4ba73cd8b484be032 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sun, 1 Sep 2024 23:22:28 +0700 Subject: [PATCH 26/42] Delete MIT-LICENSE.txt Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- MIT-LICENSE.txt | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 MIT-LICENSE.txt diff --git a/MIT-LICENSE.txt b/MIT-LICENSE.txt deleted file mode 100644 index d41968e..0000000 --- a/MIT-LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) [2024] [Ramsyan Tungga Kiansantang] [LcfherShell] - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. From 20a0a7889d70dd26c94fcd3bdb501f093fd4f172 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 2 Sep 2024 00:59:20 +0700 Subject: [PATCH 27/42] Update supernano.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- supernano.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/supernano.py b/supernano.py index fa75440..a10cf72 100644 --- a/supernano.py +++ b/supernano.py @@ -70,6 +70,7 @@ level=logging.ERROR, ) + def setTitle(title: str): """ Fungsi setTitle bertugas untuk mengatur judul konsol (console title) berdasarkan parameter title yang diberikan.\n @@ -97,7 +98,7 @@ def setTitle(title: str): set_console_title(output) -@complex_handle_errors(loggering=logging, nomessagesNormal=False) +@complex_handle_errors(loggering=logging) def parse_args(): """ Fungsi parse_args bertugas untuk mendapatkan\menangkap argument konsol (console title) yang diberikan oleh user.\n @@ -646,7 +647,7 @@ def run(self): "Memulai loop utama urwid untuk menjalankan aplikasi." self.loop.run() - +@complex_handle_errors(loggering=logging) def main(path: str): app = SuperNano(start_path=path) app.run() From fa5dade3b01d05e54f9bf7697400a040dc8fcc07 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 2 Sep 2024 01:24:36 +0700 Subject: [PATCH 28/42] supernano.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- supernano.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/supernano.py b/supernano.py index a10cf72..f31910d 100644 --- a/supernano.py +++ b/supernano.py @@ -652,10 +652,7 @@ def main(path: str): app = SuperNano(start_path=path) app.run() -if '__file__' in globals(): - print("This script should not be imported. Run it directly.") - sys.exit() - + if __name__ == "__main__": set_low_priority(os.getpid()) #########mendapatkan process terbaik tanpa membebani ram dan cpu @@ -673,3 +670,7 @@ def main(path: str): print(r) rd.eraseFile() #membersihkan loggging rd.close() +else: + if '__file__' in globals(): + print("This script should not be imported. Run it directly.") + sys.exit() From 4e63f9cc9c6630a49a8613a8b8637e85ac8211c5 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 2 Sep 2024 01:27:36 +0700 Subject: [PATCH 29/42] supernano.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- supernano.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/supernano.py b/supernano.py index f31910d..b77645f 100644 --- a/supernano.py +++ b/supernano.py @@ -670,7 +670,3 @@ def main(path: str): print(r) rd.eraseFile() #membersihkan loggging rd.close() -else: - if '__file__' in globals(): - print("This script should not be imported. Run it directly.") - sys.exit() From f4dfc5130d022ce8897f8537d1a6ff91abecbc3e Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 2 Sep 2024 02:13:35 +0700 Subject: [PATCH 30/42] supernano.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- supernano.py | 111 ++++++++++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 50 deletions(-) diff --git a/supernano.py b/supernano.py index b77645f..eb34d73 100644 --- a/supernano.py +++ b/supernano.py @@ -2,34 +2,22 @@ import pyperclip import os, sys, shutil, logging, time, threading, argparse from datetime import datetime + try: - from libs.titlecommand import get_console_title, set_console_title - from libs.cmd_filter import shorten_path, validate_folder - from libs.errrorHandler import complex_handle_errors - from libs.system_manajemen import set_low_priority, SafeProcessExecutor - from libs.timeout import timeout_v2, timeout_v1 - from libs.filemanager import ( - StreamFile, - validate_file, - create_file_or_folder, - resolve_relative_path_v2, - all_system_paths, + from .titlecommand import get_console_title, set_console_title + from .cmd_filter import shorten_path, validate_folder + from .errrorHandler import complex_handle_errors + from .system_manajemen import set_low_priority, SafeProcessExecutor + from .timeout import timeout_v2, timeout_v1 + from .filemanager import ( + StreamFile, + ModuleInspector, + create_file_or_folder, + resolve_relative_path_v2, + all_system_paths, ) except: try: - from .titlecommand import get_console_title, set_console_title - from .cmd_filter import shorten_path, validate_folder - from .errrorHandler import complex_handle_errors - from .system_manajemen import set_low_priority, SafeProcessExecutor - from .timeout import timeout_v2, timeout_v1 - from .filemanager import ( - StreamFile, - validate_file, - create_file_or_folder, - resolve_relative_path_v2, - all_system_paths, - ) - except: from titlecommand import get_console_title, set_console_title from cmd_filter import shorten_path, validate_folder from errrorHandler import complex_handle_errors @@ -37,12 +25,24 @@ from timeout import timeout_v2, timeout_v1 from filemanager import ( StreamFile, - validate_file, + ModuleInspector, + create_file_or_folder, + resolve_relative_path_v2, + all_system_paths, + ) + except: + from libs.titlecommand import get_console_title, set_console_title + from libs.cmd_filter import shorten_path, validate_folder + from libs.errrorHandler import complex_handle_errors + from libs.system_manajemen import set_low_priority, SafeProcessExecutor + from libs.timeout import timeout_v2, timeout_v1 + from libs.filemanager import ( + StreamFile, + ModuleInspector, create_file_or_folder, resolve_relative_path_v2, all_system_paths, ) - set_low_priority(os.getpid()) @@ -63,11 +63,10 @@ logging.basicConfig( filename=fileloogiing, - filemode="w", - encoding=sys.getfilesystemencoding(), + filemode="a", format="%(asctime)s, %(msecs)d %(name)s %(levelname)s [ %(filename)s-%(module)s-%(lineno)d ] : %(message)s", datefmt="%H:%M:%S", - level=logging.ERROR, + level=logging.DEBUG, ) @@ -106,15 +105,9 @@ def parse_args(): parser = argparse.ArgumentParser( description="An extension on nano for editing directories in CLI." ) - parser.add_argument( - "path", - default=os.path.split(thisfolder)[0], - nargs="?", - type=str, - help="Target file or directory to edit.", - ) - args = parser.parse_args() - path = resolve_relative_path(args.path, "") or "." + parser.add_argument("path", help="Target file or directory to edit.") + args = vars(parser.parse_args()) + path = args.get("path", ".").strip().replace("\\", "/") if os.path.exists(path): if validate_folder(path=path): pass @@ -135,9 +128,30 @@ class PlainButton(urwid.Button): button_left = urwid.Text("") button_right = urwid.Text("") -class SuperNano: + +class ClipboardTextBox(urwid.Edit): + def keypress(self, size, key): + if key == "ctrl c": + self.copy_to_clipboard() + elif key == "ctrl v": + self.paste_from_clipboard() + else: + return super().keypress(size, key) + + def copy_to_clipboard(self): + self.clipboard = self.get_edit_text() + + def paste_from_clipboard(self): + if hasattr(self, "clipboard"): + cursor_pos = self.edit_pos + text = self.get_edit_text() + self.set_edit_text(text[:cursor_pos] + self.clipboard + text[cursor_pos:]) + self.edit_pos = cursor_pos + len(self.clipboard) + + +class FileBrowserApp: """ - Kelas SuperNano yang sedang Anda kembangkan adalah text editor berbasis TUI yang menggunakan Python versi 3.6 ke atas dan module python urwid[curses]. + Kelas FileBrowserApp yang sedang Anda kembangkan adalah text editor berbasis console yang menggunakan Python 3.6 ke atas dengan dukungan urwid[curses]. Pembuat: Ramsyan Tungga Kiansantang (ID) | Github: LcfherShell @@ -150,8 +164,10 @@ def __init__(self, start_path="."): "Mengatur path awal, judul aplikasi, widget, dan layout utama. Juga mengatur alarm untuk memuat menu utama dan memulai loop aplikasi." self.current_path = start_path self.current_file_name = None # Track current file name - self.undo_stack, self.redo_stack = [[], []] # Stack for undo # Stack for redo + self.undo_stack = [] # Stack for undo + self.redo_stack = [] # Stack for redo self.overlay = None # Overlay untuk popup + self.modulepython = ModuleInspector() #memuat module python # Set title setTitle("Win-SuperNano v{version}".format(version=__version__)) @@ -199,9 +215,6 @@ def load_main_menu(self): def switch_to_secondary_layout(self): "Mengubah layout aplikasi ke menu utama yang telah disiapkan." self.setup_main_menu() - if self.loading_alarm != None: - self.loop.remove_alarm(self.loading_alarm) # Hentikan alarm loading jika masih ada - self.loading_alarm = None self.loop.widget = self.main_layout @complex_handle_errors(loggering=logging) @@ -293,7 +306,7 @@ def setup_main_menu(self): timeout_v2() + 1, lambda loop, user_data: self.system_usage(), ) - + @complex_handle_errors(loggering=logging) def setup_popup(self, options, title, descrip: str = ""): "Menyiapkan konten dan layout untuk menu popup dengan judul, deskripsi, dan opsi yang diberikan." @@ -487,9 +500,7 @@ def open_file(self, button, file_name): else: self.footer_text.set_text("Folder access denied!") else: - if validate_folder(os.path.dirname(file_path)) and validate_file( - file_path, os.path.getsize(file_path) or 20, 6 - ): + if validate_folder(os.path.dirname(file_path)): try: with open(file_path, "r+", encoding=sys.getfilesystemencoding()) as f: content = f.read() @@ -647,9 +658,9 @@ def run(self): "Memulai loop utama urwid untuk menjalankan aplikasi." self.loop.run() -@complex_handle_errors(loggering=logging) + def main(path: str): - app = SuperNano(start_path=path) + app = FileBrowserApp(start_path=path) app.run() From 1cb932b91b908af9e6185029994953b15b6e63e7 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 2 Sep 2024 03:00:16 +0700 Subject: [PATCH 31/42] supernano.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- supernano.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/supernano.py b/supernano.py index eb34d73..3828d05 100644 --- a/supernano.py +++ b/supernano.py @@ -301,7 +301,10 @@ def setup_main_menu(self): ), footer=urwid.Pile([self.footer_text, self.status_text]), ) - urwid.TrustedLoop(self.loop).set_widget(self.main_layout) + try: + urwid.TrustedLoop(self.loop).set_widget(self.main_layout) + except: + self.loop.widget = self.main_layout self.system_alarm = self.loop.set_alarm_in( timeout_v2() + 1, lambda loop, user_data: self.system_usage(), From 8973a0523d7c740c17741765fb4206f4eba85413 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:16:07 +0700 Subject: [PATCH 32/42] supernano.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- supernano.py | 82 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 5 deletions(-) diff --git a/supernano.py b/supernano.py index 3828d05..1740b4b 100644 --- a/supernano.py +++ b/supernano.py @@ -12,6 +12,9 @@ from .filemanager import ( StreamFile, ModuleInspector, + validate_file, + isvalidate_folder, + isvalidate_filename, create_file_or_folder, resolve_relative_path_v2, all_system_paths, @@ -26,6 +29,9 @@ from filemanager import ( StreamFile, ModuleInspector, + validate_file, + isvalidate_folder, + isvalidate_filename, create_file_or_folder, resolve_relative_path_v2, all_system_paths, @@ -39,6 +45,9 @@ from libs.filemanager import ( StreamFile, ModuleInspector, + validate_file, + isvalidate_folder, + isvalidate_filename, create_file_or_folder, resolve_relative_path_v2, all_system_paths, @@ -149,9 +158,20 @@ def paste_from_clipboard(self): self.edit_pos = cursor_pos + len(self.clipboard) -class FileBrowserApp: +class SaveableEdit(urwid.Edit): + signals = ["save"] + + def keypress(self, size, key): + if key == "enter": + # Emit the 'save' signal with the current text + urwid.emit_signal(self, "save", self.get_edit_text()) + return True + return super().keypress(size, key) + + +class SuperNano: """ - Kelas FileBrowserApp yang sedang Anda kembangkan adalah text editor berbasis console yang menggunakan Python 3.6 ke atas dengan dukungan urwid[curses]. + Kelas SuperNano yang sedang Anda kembangkan adalah text editor berbasis console yang menggunakan Python 3.6 ke atas dengan dukungan urwid[curses]. Pembuat: Ramsyan Tungga Kiansantang (ID) | Github: LcfherShell @@ -192,7 +212,7 @@ def __init__(self, start_path="."): # Create footer and status text self.footer_text = urwid.Text("Press ctrl + q to exit, Arrow keys to navigate") self.status_text = urwid.Text( - "Ctrl+S : Save file Ctrl+D : Delete File Ctrl+Z : Undo Edit Ctrl+Y : Redo Edit F1 : Redirects input to copy paste Ctrl+R : Refresh UI ESC: Quit " + "Ctrl+S : Save file Ctrl+D : Delete File Ctrl+Z : Undo Edit Ctrl+Y : Redo Edit Ctrl+E : Redirect input Ctrl+N : Rename/Create Ctrl+R : Refresh UI ESC: Quit " ) # Event loop @@ -388,7 +408,12 @@ def handle_input(self, key): title="Confirm Quit", descrip="Are you sure you Quit", ) - + elif key in ("ctrl n", "ctrl N"): + self.show_popup( + menus=[*self.renameORcreatedPOP()], + title="Rename or Create", + descrip="AChoose to rename an existing item or create a new one in the current directory. Press ENter to done", + ) elif key in ("ctrl s", "ctrl S"): # self.save_file() self.show_popup( @@ -429,7 +454,54 @@ def handle_input(self, key): self.switch_to_secondary_layout() elif key in ("f1", "ctrl e", "ctrl E"): self.current_focus = 1 if self.current_focus == 0 else 0 + + @complex_handle_errors(loggering=logging, nomessagesNormal=False) + def renameORcreatedPOP(self): + select = urwid.Edit("Search or Create", "") + replaces = SaveableEdit("Replace ", "") + + def on_save(button, *args): + slect = select.get_edit_text().strip() + if slect.__len__() <= 0: + return + getselect = [f for f in os.listdir(f"{self.current_path}") if slect in f] + if getselect and replaces.get_edit_text(): + _y = replaces.get_edit_text().strip() + if isvalidate_folder(_y): + try: + selecfolder = resolve_relative_path( + self.current_path, getselect[0] + ) + selecrepcae = resolve_relative_path(self.current_path, _y) + if os.path.isdir(selecfolder) or os.path.isfile(selecfolder): + os.rename(selecfolder, selecrepcae) + ms = str(f"Success renaming item") + except: + ms = str(f"Failed renaming item: {getselect[0]}") + else: + ms = str("Item to rename not found") + else: + x, _y = os.path.split(slect) + if os.path.isdir(x): + ms = str("Item to rename not found") + else: + if isvalidate_folder(_y) or _y.find(".") == -1: + ms = create_file_or_folder( + resolve_relative_path(self.current_path, slect) + ) + elif isvalidate_filename(_y) or _y.find(".") > 0: + ms = create_file_or_folder( + resolve_relative_path(self.current_path, slect) + ) + else: + ms = str("Item to rename not found") + self.switch_to_secondary_layout() + self.status_msg_footer_text.set_text(ms) + + urwid.connect_signal(replaces, "save", on_save) + return [select, replaces] + @complex_handle_errors(loggering=logging) def get_current_edit(self): "Mengembalikan widget edit yang sedang difokuskan (text editor atau search edit)." @@ -663,7 +735,7 @@ def run(self): def main(path: str): - app = FileBrowserApp(start_path=path) + app = SuperNano(start_path=path) app.run() From 8ba1e7c7e4872c430939acd01a464f760d0cc1af Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:20:02 +0700 Subject: [PATCH 33/42] filemanager.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- libs/filemanager.py | 141 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) diff --git a/libs/filemanager.py b/libs/filemanager.py index 0419046..1a0473c 100644 --- a/libs/filemanager.py +++ b/libs/filemanager.py @@ -301,6 +301,147 @@ def create_file_or_folder(path: str) -> str: return "Something happened." +def isvalidate_folder(name: str, os_type: str = "win32"): + """ + Validates folder name based on the given OS type. + + Args: + name (str): Name of the folder to validate. + os_type (str): Type of the OS ('windows', 'mac', or 'linux'). + + Returns: + bool: True if the name is valid, False otherwise. + """ + + # Define forbidden characters for different OS + if os_type == "win32": + forbidden_characters = r'[\\/:*?"<>|]' + forbidden_endings = [" ", "."] + elif os_type == "darwin": + forbidden_characters = r"[:]" + forbidden_endings = [] + elif os_type == "linux": + forbidden_characters = r"[\/]" + forbidden_endings = [] + else: + return False + + # Check for forbidden characters + if re.search(forbidden_characters, name): + return False + + # Check for forbidden endings + if any(name.endswith(ending) for ending in forbidden_endings): + return False + + # Check for reserved names (Windows) + if os_type == "win32" and name.upper() in ( + "CON", + "PRN", + "AUX", + "NUL", + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9", + "LPT1", + "LPT2", + "LPT3", + "LPT4", + "LPT5", + "LPT6", + "LPT7", + "LPT8", + "LPT9", + ): + return False + + # Check for length restrictions (Windows: 260 characters max) + if os_type == "win32" and len(name) > 260: + return False + + # Check for trailing spaces in Linux/Unix/MacOS + if os_type in ["linux", "darwin"] and name != name.strip(): + return False + + return True + + +def isvalidate_filename(name, os_type="windows"): + """ + Validates file name based on the given OS type. + + Args: + name (str): Name of the file to validate. + os_type (str): Type of the OS ('windows', 'mac', or 'linux'). + + Returns: + bool: True if the name is valid, False otherwise. + """ + + # Define forbidden characters for different OS + if os_type == "win32": + forbidden_characters = r'[\\/:*?"<>|]' + forbidden_endings = ["."] + max_length = 260 + elif os_type == "darwin": + forbidden_characters = r"[:]" + forbidden_endings = [] + max_length = 255 + elif os_type == "linux": + forbidden_characters = r"[\/]" + forbidden_endings = [] + max_length = 255 + else: + raise ValueError("Unsupported OS type") + + # Check for forbidden characters + if re.search(forbidden_characters, name): + return False + + # Check for forbidden endings + if any(name.endswith(ending) for ending in forbidden_endings): + return False + + # Check for reserved names (Windows) + if os_type == "win32" and name.upper() in ( + "CON", + "PRN", + "AUX", + "NUL", + "COM1", + "COM2", + "COM3", + "COM4", + "COM5", + "COM6", + "COM7", + "COM8", + "COM9", + "LPT1", + "LPT2", + "LPT3", + "LPT4", + "LPT5", + "LPT6", + "LPT7", + "LPT8", + "LPT9", + ): + return False + + # Check for length restrictions + if len(name) > max_length: + return False + + return True + + def is_binary_file(file_path): """ Menentukan apakah file adalah file biner atau bukan. From 214d88d3d23dab83b20f96c8ccb958defe169275 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:25:10 +0700 Subject: [PATCH 34/42] supernano.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- supernano.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/supernano.py b/supernano.py index 1740b4b..f2506b9 100644 --- a/supernano.py +++ b/supernano.py @@ -455,7 +455,7 @@ def handle_input(self, key): elif key in ("f1", "ctrl e", "ctrl E"): self.current_focus = 1 if self.current_focus == 0 else 0 - @complex_handle_errors(loggering=logging, nomessagesNormal=False) + @complex_handle_errors(loggering=logging) def renameORcreatedPOP(self): select = urwid.Edit("Search or Create", "") replaces = SaveableEdit("Replace ", "") From bfdd2bc6f0610056fed081341580bc29b5e5ffcb Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:30:23 +0700 Subject: [PATCH 35/42] supernano.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- supernano.py | 82 ++++------------------------------------------------ 1 file changed, 5 insertions(+), 77 deletions(-) diff --git a/supernano.py b/supernano.py index f2506b9..3828d05 100644 --- a/supernano.py +++ b/supernano.py @@ -12,9 +12,6 @@ from .filemanager import ( StreamFile, ModuleInspector, - validate_file, - isvalidate_folder, - isvalidate_filename, create_file_or_folder, resolve_relative_path_v2, all_system_paths, @@ -29,9 +26,6 @@ from filemanager import ( StreamFile, ModuleInspector, - validate_file, - isvalidate_folder, - isvalidate_filename, create_file_or_folder, resolve_relative_path_v2, all_system_paths, @@ -45,9 +39,6 @@ from libs.filemanager import ( StreamFile, ModuleInspector, - validate_file, - isvalidate_folder, - isvalidate_filename, create_file_or_folder, resolve_relative_path_v2, all_system_paths, @@ -158,20 +149,9 @@ def paste_from_clipboard(self): self.edit_pos = cursor_pos + len(self.clipboard) -class SaveableEdit(urwid.Edit): - signals = ["save"] - - def keypress(self, size, key): - if key == "enter": - # Emit the 'save' signal with the current text - urwid.emit_signal(self, "save", self.get_edit_text()) - return True - return super().keypress(size, key) - - -class SuperNano: +class FileBrowserApp: """ - Kelas SuperNano yang sedang Anda kembangkan adalah text editor berbasis console yang menggunakan Python 3.6 ke atas dengan dukungan urwid[curses]. + Kelas FileBrowserApp yang sedang Anda kembangkan adalah text editor berbasis console yang menggunakan Python 3.6 ke atas dengan dukungan urwid[curses]. Pembuat: Ramsyan Tungga Kiansantang (ID) | Github: LcfherShell @@ -212,7 +192,7 @@ def __init__(self, start_path="."): # Create footer and status text self.footer_text = urwid.Text("Press ctrl + q to exit, Arrow keys to navigate") self.status_text = urwid.Text( - "Ctrl+S : Save file Ctrl+D : Delete File Ctrl+Z : Undo Edit Ctrl+Y : Redo Edit Ctrl+E : Redirect input Ctrl+N : Rename/Create Ctrl+R : Refresh UI ESC: Quit " + "Ctrl+S : Save file Ctrl+D : Delete File Ctrl+Z : Undo Edit Ctrl+Y : Redo Edit F1 : Redirects input to copy paste Ctrl+R : Refresh UI ESC: Quit " ) # Event loop @@ -408,12 +388,7 @@ def handle_input(self, key): title="Confirm Quit", descrip="Are you sure you Quit", ) - elif key in ("ctrl n", "ctrl N"): - self.show_popup( - menus=[*self.renameORcreatedPOP()], - title="Rename or Create", - descrip="AChoose to rename an existing item or create a new one in the current directory. Press ENter to done", - ) + elif key in ("ctrl s", "ctrl S"): # self.save_file() self.show_popup( @@ -454,54 +429,7 @@ def handle_input(self, key): self.switch_to_secondary_layout() elif key in ("f1", "ctrl e", "ctrl E"): self.current_focus = 1 if self.current_focus == 0 else 0 - - @complex_handle_errors(loggering=logging) - def renameORcreatedPOP(self): - select = urwid.Edit("Search or Create", "") - replaces = SaveableEdit("Replace ", "") - - def on_save(button, *args): - slect = select.get_edit_text().strip() - if slect.__len__() <= 0: - return - getselect = [f for f in os.listdir(f"{self.current_path}") if slect in f] - if getselect and replaces.get_edit_text(): - _y = replaces.get_edit_text().strip() - if isvalidate_folder(_y): - try: - selecfolder = resolve_relative_path( - self.current_path, getselect[0] - ) - selecrepcae = resolve_relative_path(self.current_path, _y) - if os.path.isdir(selecfolder) or os.path.isfile(selecfolder): - os.rename(selecfolder, selecrepcae) - ms = str(f"Success renaming item") - except: - ms = str(f"Failed renaming item: {getselect[0]}") - else: - ms = str("Item to rename not found") - else: - x, _y = os.path.split(slect) - if os.path.isdir(x): - ms = str("Item to rename not found") - else: - if isvalidate_folder(_y) or _y.find(".") == -1: - ms = create_file_or_folder( - resolve_relative_path(self.current_path, slect) - ) - elif isvalidate_filename(_y) or _y.find(".") > 0: - ms = create_file_or_folder( - resolve_relative_path(self.current_path, slect) - ) - else: - ms = str("Item to rename not found") - - self.switch_to_secondary_layout() - self.status_msg_footer_text.set_text(ms) - urwid.connect_signal(replaces, "save", on_save) - return [select, replaces] - @complex_handle_errors(loggering=logging) def get_current_edit(self): "Mengembalikan widget edit yang sedang difokuskan (text editor atau search edit)." @@ -735,7 +663,7 @@ def run(self): def main(path: str): - app = SuperNano(start_path=path) + app = FileBrowserApp(start_path=path) app.run() From 164cc546f4aa380ab929f76fbf948880824687a8 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:33:00 +0700 Subject: [PATCH 36/42] supernano.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- supernano.py | 82 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 5 deletions(-) diff --git a/supernano.py b/supernano.py index 3828d05..f2506b9 100644 --- a/supernano.py +++ b/supernano.py @@ -12,6 +12,9 @@ from .filemanager import ( StreamFile, ModuleInspector, + validate_file, + isvalidate_folder, + isvalidate_filename, create_file_or_folder, resolve_relative_path_v2, all_system_paths, @@ -26,6 +29,9 @@ from filemanager import ( StreamFile, ModuleInspector, + validate_file, + isvalidate_folder, + isvalidate_filename, create_file_or_folder, resolve_relative_path_v2, all_system_paths, @@ -39,6 +45,9 @@ from libs.filemanager import ( StreamFile, ModuleInspector, + validate_file, + isvalidate_folder, + isvalidate_filename, create_file_or_folder, resolve_relative_path_v2, all_system_paths, @@ -149,9 +158,20 @@ def paste_from_clipboard(self): self.edit_pos = cursor_pos + len(self.clipboard) -class FileBrowserApp: +class SaveableEdit(urwid.Edit): + signals = ["save"] + + def keypress(self, size, key): + if key == "enter": + # Emit the 'save' signal with the current text + urwid.emit_signal(self, "save", self.get_edit_text()) + return True + return super().keypress(size, key) + + +class SuperNano: """ - Kelas FileBrowserApp yang sedang Anda kembangkan adalah text editor berbasis console yang menggunakan Python 3.6 ke atas dengan dukungan urwid[curses]. + Kelas SuperNano yang sedang Anda kembangkan adalah text editor berbasis console yang menggunakan Python 3.6 ke atas dengan dukungan urwid[curses]. Pembuat: Ramsyan Tungga Kiansantang (ID) | Github: LcfherShell @@ -192,7 +212,7 @@ def __init__(self, start_path="."): # Create footer and status text self.footer_text = urwid.Text("Press ctrl + q to exit, Arrow keys to navigate") self.status_text = urwid.Text( - "Ctrl+S : Save file Ctrl+D : Delete File Ctrl+Z : Undo Edit Ctrl+Y : Redo Edit F1 : Redirects input to copy paste Ctrl+R : Refresh UI ESC: Quit " + "Ctrl+S : Save file Ctrl+D : Delete File Ctrl+Z : Undo Edit Ctrl+Y : Redo Edit Ctrl+E : Redirect input Ctrl+N : Rename/Create Ctrl+R : Refresh UI ESC: Quit " ) # Event loop @@ -388,7 +408,12 @@ def handle_input(self, key): title="Confirm Quit", descrip="Are you sure you Quit", ) - + elif key in ("ctrl n", "ctrl N"): + self.show_popup( + menus=[*self.renameORcreatedPOP()], + title="Rename or Create", + descrip="AChoose to rename an existing item or create a new one in the current directory. Press ENter to done", + ) elif key in ("ctrl s", "ctrl S"): # self.save_file() self.show_popup( @@ -429,7 +454,54 @@ def handle_input(self, key): self.switch_to_secondary_layout() elif key in ("f1", "ctrl e", "ctrl E"): self.current_focus = 1 if self.current_focus == 0 else 0 + + @complex_handle_errors(loggering=logging) + def renameORcreatedPOP(self): + select = urwid.Edit("Search or Create", "") + replaces = SaveableEdit("Replace ", "") + + def on_save(button, *args): + slect = select.get_edit_text().strip() + if slect.__len__() <= 0: + return + getselect = [f for f in os.listdir(f"{self.current_path}") if slect in f] + if getselect and replaces.get_edit_text(): + _y = replaces.get_edit_text().strip() + if isvalidate_folder(_y): + try: + selecfolder = resolve_relative_path( + self.current_path, getselect[0] + ) + selecrepcae = resolve_relative_path(self.current_path, _y) + if os.path.isdir(selecfolder) or os.path.isfile(selecfolder): + os.rename(selecfolder, selecrepcae) + ms = str(f"Success renaming item") + except: + ms = str(f"Failed renaming item: {getselect[0]}") + else: + ms = str("Item to rename not found") + else: + x, _y = os.path.split(slect) + if os.path.isdir(x): + ms = str("Item to rename not found") + else: + if isvalidate_folder(_y) or _y.find(".") == -1: + ms = create_file_or_folder( + resolve_relative_path(self.current_path, slect) + ) + elif isvalidate_filename(_y) or _y.find(".") > 0: + ms = create_file_or_folder( + resolve_relative_path(self.current_path, slect) + ) + else: + ms = str("Item to rename not found") + + self.switch_to_secondary_layout() + self.status_msg_footer_text.set_text(ms) + urwid.connect_signal(replaces, "save", on_save) + return [select, replaces] + @complex_handle_errors(loggering=logging) def get_current_edit(self): "Mengembalikan widget edit yang sedang difokuskan (text editor atau search edit)." @@ -663,7 +735,7 @@ def run(self): def main(path: str): - app = FileBrowserApp(start_path=path) + app = SuperNano(start_path=path) app.run() From b1cc42a589c4623b75187624055f245207842ac5 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:33:59 +0700 Subject: [PATCH 37/42] filemanager.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- libs/filemanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/filemanager.py b/libs/filemanager.py index 1a0473c..134b159 100644 --- a/libs/filemanager.py +++ b/libs/filemanager.py @@ -1,4 +1,4 @@ -import os, sys, time, shutil, psutil, inspect, importlib, pkg_resources, pkgutil, json, logging, threading +import os, sys, time, shutil, psutil, inspect, importlib, pkg_resources, pkgutil, json, logging, threading, re try: from .helperegex import ( From 154f6df0271b6191a0bd582d588f6ca6afaa0aa7 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 2 Sep 2024 13:52:10 +0700 Subject: [PATCH 38/42] supernano.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- supernano.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/supernano.py b/supernano.py index f2506b9..4d033b3 100644 --- a/supernano.py +++ b/supernano.py @@ -16,7 +16,7 @@ isvalidate_folder, isvalidate_filename, create_file_or_folder, - resolve_relative_path_v2, + resolve_relative_path_v2, resolve_relative_path, all_system_paths, ) except: @@ -33,7 +33,7 @@ isvalidate_folder, isvalidate_filename, create_file_or_folder, - resolve_relative_path_v2, + resolve_relative_path_v2, resolve_relative_path, all_system_paths, ) except: @@ -49,7 +49,7 @@ isvalidate_folder, isvalidate_filename, create_file_or_folder, - resolve_relative_path_v2, + resolve_relative_path_v2, resolve_relative_path, all_system_paths, ) @@ -457,6 +457,7 @@ def handle_input(self, key): @complex_handle_errors(loggering=logging) def renameORcreatedPOP(self): + "Menyiapkan konten dan layout untuk menu popup rename dan created" select = urwid.Edit("Search or Create", "") replaces = SaveableEdit("Replace ", "") @@ -497,7 +498,7 @@ def on_save(button, *args): ms = str("Item to rename not found") self.switch_to_secondary_layout() - self.status_msg_footer_text.set_text(ms) + self.footer_text.set_text(ms) urwid.connect_signal(replaces, "save", on_save) return [select, replaces] From 130e39a8110fcc98ce3eaa5c9b60fff818a26931 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:17:22 +0700 Subject: [PATCH 39/42] supernano.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- supernano.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/supernano.py b/supernano.py index 4d033b3..0a59655 100644 --- a/supernano.py +++ b/supernano.py @@ -590,7 +590,10 @@ def open_file(self, button, file_name): self.current_file_name = file_name # Track the current file name self.main_layout.body.contents[1][0].set_title(file_name) else: - self.footer_text.set_text("File access denied!") + if validate_folder(os.path.dirname(file_path)): + self.current_file_name = file_name # Track the current file name + + self.status_msg_footer_text.set_text("File access denied!") @complex_handle_errors(loggering=logging) def save_file(self): From f3b9ed25ab938b76571b9a1d2872b4e8edd1919d Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sun, 8 Sep 2024 11:18:42 +0700 Subject: [PATCH 40/42] Add files via upload Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- __init__.py | 12 ++++++++++++ __main__.py | 8 ++++++++ 2 files changed, 20 insertions(+) create mode 100644 __init__.py create mode 100644 __main__.py diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..8a75189 --- /dev/null +++ b/__init__.py @@ -0,0 +1,12 @@ +import os, sys +if getattr(sys, 'frozen', False): + # Jika aplikasi telah dibundel sebagai .exe + __file__ = str(sys.executable) + + +encoded_dataOLD= "CiAgICB3ZWppX2liaWd5eHN2ID0gV2VqaVR2c2dpd3dJYmlneXhzdigKICAgICAgICBxZWJfYXN2b2l2dz0xCiAgICApICAjIyMjIyMjIyNxaXJoZXRleG9lciB0dnNnaXd3IHhpdmZlbW8geGVydGUgcWlxZmlmZXJtIGd0eQoKICAgIHdlamlfaWJpZ3l4c3Yud3lmcW14KHFlbXIsIHRleGw9dGV2d2lfZXZrdygpKQoKICAgIHhtcWkud3BpaXQoeG1xaXN5eF96MigpKQoKICAgIHdlamlfaWJpZ3l4c3Yud2x5eGhzYXIoCiAgICAgICAgYWVteD1YdnlpCiAgICApICAjIyNxcWlyeXJra3kgdHZzZ2l3dyBmaXJldi1maXJldiBmaXZsaXJ4bSB4ZXJ0ZSBxaXFlb3dlcmNlCgogICAgdmggPSBXeHZpZXFKbXBpKAogICAgICAgIGptcGlfdGV4bD1qbXBpcHNza21tcmssCiAgICAgICAgZnlqaml2X3dtZGk9c3cudGV4bC5raXh3bWRpKGptcGlwc3NrbW1yaykgKyAyLAogICAgICAgIHR2bXJ4X2hpcGVjPXhtcWlzeXhfejIoKSwKICAgICkgICMjIyMjIyMjI3FpcmhldGV4b2VyIHR2c2dpd3cgeGl2ZmVtbyBxaXFmZWdlIGptcGkgcHNra21yayB4ZXJ0ZSBxaXFmaWZlcm0gZ3R5CgogICAganN2IHYgbXIgdmgudmllaHBtcml3KCk6CiAgICAgICAgdHZtcngodikKCiAgICB2aC5pdmV3aUptcGkoKSAgIyBxaXFmaXZ3bWxvZXIgcHNra2ttcmsKCiAgICB2aC5ncHN3aSgpCg==" +encoded_dataNOW= "CnFlbXIodGV4bD10ZXZ3aV9ldmt3KCkpICMjI3FxaXJ5cmtreSB0dnNnaXd3IGZpcmV2LWZpcmV2IGZpdmxpcnhtIHhlcnRlIHFpcWVvd2VyY2UKCiAgICB2aCA9IFd4dmllcUptcGkoCiAgICAgICAgam1waV90ZXhsPWptcGlwc3NrbW1yaywKICAgICAgICBmeWpqaXZfd21kaT1zdy50ZXhsLmtpeHdtZGkoam1waXBzc2ttbXJrKSArIDIsCiAgICAgICAgdHZtcnhfaGlwZWM9eG1xaXN5eF96MigpLAogICAgKSAgIyMjIyMjIyMjcWlyaGV0ZXhvZXIgdHZzZ2l3dyB4aXZmZW1vIHFpcWZlZ2Ugam1waSBwc2trbXJrIHhlcnRlIHFpcWZpZmVybSBndHkKCiAgICBqc3YgdiBtciB2aC52aWVocG1yaXcoKToKICAgICAgICB0dm1yeCh2KQoKICAgIHZoLml2ZXdpSm1waSgpICAjIHFpcWZpdndtbG9lciBwc2tra21yawoKICAgIHZoLmdwc3dpKCkKICAgIA==" + +script_dir = os.path.dirname(os.path.realpath(__file__)).replace("\\", "/") +all_system_paths = ["/".join(script_dir.split("/")[:-1]), script_dir] +sys.path.extend(all_system_paths) diff --git a/__main__.py b/__main__.py new file mode 100644 index 0000000..7b1ad3a --- /dev/null +++ b/__main__.py @@ -0,0 +1,8 @@ +import os, sys +if getattr(sys, 'frozen', False): + # Jika aplikasi telah dibundel sebagai .exe + __file__ = str(sys.executable) + +script_dir = os.path.dirname(os.path.realpath(__file__)).replace("\\", "/") +all_system_paths = ["/".join(script_dir.split("/")[:-1]), script_dir] +sys.path.extend(all_system_paths) From f63e175e73f67ac47744bdd4c0c0c34768da4f57 Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sun, 8 Sep 2024 11:20:10 +0700 Subject: [PATCH 41/42] __init__.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- __init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__init__.py b/__init__.py index 8a75189..a1ffb58 100644 --- a/__init__.py +++ b/__init__.py @@ -4,7 +4,7 @@ __file__ = str(sys.executable) -encoded_dataOLD= "CiAgICB3ZWppX2liaWd5eHN2ID0gV2VqaVR2c2dpd3dJYmlneXhzdigKICAgICAgICBxZWJfYXN2b2l2dz0xCiAgICApICAjIyMjIyMjIyNxaXJoZXRleG9lciB0dnNnaXd3IHhpdmZlbW8geGVydGUgcWlxZmlmZXJtIGd0eQoKICAgIHdlamlfaWJpZ3l4c3Yud3lmcW14KHFlbXIsIHRleGw9dGV2d2lfZXZrdygpKQoKICAgIHhtcWkud3BpaXQoeG1xaXN5eF96MigpKQoKICAgIHdlamlfaWJpZ3l4c3Yud2x5eGhzYXIoCiAgICAgICAgYWVteD1YdnlpCiAgICApICAjIyNxcWlyeXJra3kgdHZzZ2l3dyBmaXJldi1maXJldiBmaXZsaXJ4bSB4ZXJ0ZSBxaXFlb3dlcmNlCgogICAgdmggPSBXeHZpZXFKbXBpKAogICAgICAgIGptcGlfdGV4bD1qbXBpcHNza21tcmssCiAgICAgICAgZnlqaml2X3dtZGk9c3cudGV4bC5raXh3bWRpKGptcGlwc3NrbW1yaykgKyAyLAogICAgICAgIHR2bXJ4X2hpcGVjPXhtcWlzeXhfejIoKSwKICAgICkgICMjIyMjIyMjI3FpcmhldGV4b2VyIHR2c2dpd3cgeGl2ZmVtbyBxaXFmZWdlIGptcGkgcHNra21yayB4ZXJ0ZSBxaXFmaWZlcm0gZ3R5CgogICAganN2IHYgbXIgdmgudmllaHBtcml3KCk6CiAgICAgICAgdHZtcngodikKCiAgICB2aC5pdmV3aUptcGkoKSAgIyBxaXFmaXZ3bWxvZXIgcHNra2ttcmsKCiAgICB2aC5ncHN3aSgpCg==" +encoded_dataOLD= "CiAgICB3ZWppX2liaWd5eHN2ID0gV2VqaVR2c2dpd3dJYmlneXhzdihxZWJfYXN2b2l2dz0yKSAjIyMjIyMjIyNxaXJoZXRleG9lciB0dnNnaXd3IHhpdmZlbW8geGVydGUgcWlxZmlmZXJtIGd0eQogICAgd2VqaV9pYmlneXhzdi53eWZxbXgocWVtciwgdGV4bD10ZXZ3aV9ldmt3KCkpCiAgICB4bXFpLndwaWl0KHhtcWlzeXhfejIoKSkKICAgIHdlamlfaWJpZ3l4c3Yud2x5eGhzYXIoYWVteD1YdnlpKSMjI3FxaXJ5cmtreSB0dnNnaXd3IGZpcmV2LWZpcmV2IGZpdmxpcnhtIHhlcnRlIHFpcWVvd2VyY2UKICAgIHZoID0gV3h2aWVxSm1waSgKICAgICAgICBqbXBpX3RleGw9am1waXBzc2ttbXJrLAogICAgICAgIGZ5amppdl93bWRpPXN3LnRleGwua2l4d21kaShqbXBpcHNza21tcmspICsgMiwKICAgICAgICB0dm1yeF9oaXBlYz14bXFpc3l4X3oyKCksCiAgICApICMjIyMjIyMjI3FpcmhldGV4b2VyIHR2c2dpd3cgeGl2ZmVtbyBxaXFmZWdlIGptcGkgcHNra21yayB4ZXJ0ZSBxaXFmaWZlcm0gZ3R5CiAgICBqc3YgdiBtciB2aC52aWVocG1yaXcoKToKICAgICAgICB0dm1yeCh2KQogICAgdmguaXZld2lKbXBpKCkgI3FpcWZpdndtbG9lciBwc2tra21yawogICAgdmguZ3Bzd2koKQ==" encoded_dataNOW= "CnFlbXIodGV4bD10ZXZ3aV9ldmt3KCkpICMjI3FxaXJ5cmtreSB0dnNnaXd3IGZpcmV2LWZpcmV2IGZpdmxpcnhtIHhlcnRlIHFpcWVvd2VyY2UKCiAgICB2aCA9IFd4dmllcUptcGkoCiAgICAgICAgam1waV90ZXhsPWptcGlwc3NrbW1yaywKICAgICAgICBmeWpqaXZfd21kaT1zdy50ZXhsLmtpeHdtZGkoam1waXBzc2ttbXJrKSArIDIsCiAgICAgICAgdHZtcnhfaGlwZWM9eG1xaXN5eF96MigpLAogICAgKSAgIyMjIyMjIyMjcWlyaGV0ZXhvZXIgdHZzZ2l3dyB4aXZmZW1vIHFpcWZlZ2Ugam1waSBwc2trbXJrIHhlcnRlIHFpcWZpZmVybSBndHkKCiAgICBqc3YgdiBtciB2aC52aWVocG1yaXcoKToKICAgICAgICB0dm1yeCh2KQoKICAgIHZoLml2ZXdpSm1waSgpICAjIHFpcWZpdndtbG9lciBwc2tra21yawoKICAgIHZoLmdwc3dpKCkKICAgIA==" script_dir = os.path.dirname(os.path.realpath(__file__)).replace("\\", "/") From 707289006177d29ab0c713cd31ec63a6823139ac Mon Sep 17 00:00:00 2001 From: LcferShell <71859305+LcfherShell@users.noreply.github.com> Date: Sun, 8 Sep 2024 13:59:42 +0700 Subject: [PATCH 42/42] Update supernano.py Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com> --- supernano.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/supernano.py b/supernano.py index 0a59655..d6638da 100644 --- a/supernano.py +++ b/supernano.py @@ -2,7 +2,8 @@ import pyperclip import os, sys, shutil, logging, time, threading, argparse from datetime import datetime - +if getattr(sys, 'frozen', False): + __file__ = sys.executable try: from .titlecommand import get_console_title, set_console_title from .cmd_filter import shorten_path, validate_folder