├── requirements.txt ├── main.py ├── tools ├── impl │ ├── __init__.py │ ├── SSH.py │ ├── cfxFinder.py │ └── Pinger.py └── __init__.py ├── LICENSE ├── README.md ├── .gitignore └── common └── __init__.py /requirements.txt: -------------------------------------------------------------------------------- 1 | pystyle==1.1 2 | aioconsole~=0.4.1 3 | aioping~=0.3.1 4 | aiohttp~=3.8.1 5 | asyncoro~=4.5.6 6 | yarl~=1.7.2 7 | aiocfscrape~=1.0.0 8 | asyncssh~=2.10.0 -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from asyncio import run 2 | from contextlib import suppress 3 | 4 | from common import Console 5 | 6 | if __name__ == '__main__': 7 | console = Console() 8 | run(console.banner()) 9 | while 1: 10 | with suppress(KeyboardInterrupt): 11 | print() 12 | run(console.run()) 13 | -------------------------------------------------------------------------------- /tools/impl/__init__.py: -------------------------------------------------------------------------------- 1 | from tools.impl.Pinger import Pinger 2 | from tools.impl.cfxFinder import Cfxfinder 3 | from tools.impl.SSH import SSH 4 | 5 | __all__ = ["handle"] 6 | 7 | 8 | async def handle(console, cmd, *args): 9 | if {cmd} & {"PING", "PINGER"}: 10 | await Pinger.run(console, *args) 11 | return True 12 | elif {cmd} & {"CFX"}: 13 | await Cfxfinder.run(console, *args) 14 | return True 15 | elif {cmd} & {"SSH"}: 16 | await SSH.run(console, *args) 17 | return True 18 | return False 19 | -------------------------------------------------------------------------------- /tools/impl/SSH.py: -------------------------------------------------------------------------------- 1 | from contextlib import suppress 2 | from aioconsole import aprint 3 | from asyncssh import SSHClientConnection, connect 4 | from tools import Tool 5 | 6 | 7 | class SSH(Tool): 8 | @staticmethod 9 | async def run(console, *args): 10 | assert len(args) == 2, "Usage: SSH " 11 | ip, username = str(args[0]).split(":"), args[1] 12 | password = await console.cinput("Enter Password", hide_value=True) 13 | console.using.set() 14 | console.loading_text = f"Connecting To {ip[0]}" 15 | with suppress(KeyboardInterrupt): 16 | Connection: SSHClientConnection 17 | 18 | async with connect(ip[0], port=int(ip[1]), username=username, password=password, 19 | known_hosts=None, connect_timeout=5) as Connection: 20 | await console.banner() 21 | while True: 22 | inputcmd = await console.cinput(f"{username}@{ip[0]}") 23 | output = await Connection.run(inputcmd) 24 | await aprint(output.stdout) 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Matrix Team 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

pytools

2 | 3 |

PyTools - The Best Tools

4 |
(Programming Language - Python 3)
5 | 6 |

7 | PyTools forks 8 | PyTools last commit (main) 9 | PyTools Repo stars 10 | PyTools License 11 | MatrixTM issues 12 | 13 | ## Pre View 14 |

preview

15 | 16 | #### Issues ? 17 | * Soon... 18 | * [Issues Page](https://github.com/MatrixTM/PyTools/issues) 19 | #### Like the project? Leave a star on the repository! 20 | 21 | ## Documentation 22 | 23 | You can read it from [GitHub Wiki](https://github.com/MatrixTM/PyTools/wiki) 24 | 25 | ## Clone and Install Script 26 | 27 | ```shell script 28 | git clone https://github.com/MatrixTM/PyTools.git 29 | cd PyTools 30 | pip install -r requirements.txt 31 | ``` 32 | 33 | ## Video Tutorial 34 | Youtube : soon... 35 | -------------------------------------------------------------------------------- /tools/impl/cfxFinder.py: -------------------------------------------------------------------------------- 1 | from asyncio import sleep, TimeoutError 2 | from contextlib import suppress 3 | from aiocfscrape import CloudflareScraper 4 | 5 | from aioconsole import aprint 6 | from pystyle import Colorate, Colors 7 | from tools import Tool 8 | from re import compile 9 | 10 | cfx_regex = compile(u"(?:cfx[.]re/join/|)(\w+)") 11 | 12 | 13 | class Cfxfinder(Tool): 14 | @staticmethod 15 | async def run(console, *args): 16 | assert len(args) == 1, "Usage: cfx " 17 | 18 | cfx_code = cfx_regex.search(args[0].lower()) 19 | 20 | assert cfx_code, "The CFX code is not valid" 21 | cfx_code = cfx_code.group(1) 22 | assert len(cfx_code) == 6, "The CFX code is not valid" 23 | 24 | with suppress(KeyboardInterrupt): 25 | console.using.set() 26 | console.loading_text = "Getting Information From (%s)" % ("https://cfx.re/join/" + cfx_code) 27 | await sleep(.9) 28 | 29 | cfx_info = await Cfxfinder.getinfo(cfx_code) 30 | 31 | await aprint( 32 | Colorate.Horizontal(Colors.green_to_cyan, 33 | 'Information received from cfx server [%s] {\nAddress: %s\nClients: ' 34 | '%s\nLocale: %s\nsvMaxclients: %s\nownerName: %s\n}' % ( 35 | cfx_info['name'], cfx_info['host'], cfx_info['clients'], 36 | cfx_info['locale'], cfx_info['maxclients'], cfx_info['ow']))) 37 | 38 | @staticmethod 39 | async def getinfo(cfxcode): 40 | with suppress(TimeoutError): 41 | async with CloudflareScraper() as s, \ 42 | s.get('https://servers-frontend.fivem.net/api/servers/single/' + cfxcode) as res: 43 | assert res.status == 200, f"Status Code :{res.status}" 44 | 45 | cfx_json = await res.json() 46 | assert "error" not in cfx_json, cfx_json['error'] 47 | 48 | return { 49 | 'name': cfx_json['Data']['hostname'], 50 | 'host': cfx_json['Data']['connectEndPoints'][0], 51 | 'clients': cfx_json['Data']['clients'], 52 | 'maxclients': cfx_json['Data']['svMaxclients'], 53 | 'locale': cfx_json['Data']['vars']['locale'], 54 | 'ow': cfx_json['Data']['ownerName'] 55 | } 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/python 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=python 3 | 4 | ### Python ### 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | share/python-wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .nox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | *.py,cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | cover/ 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | db.sqlite3-journal 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | 78 | # PyBuilder 79 | .pybuilder/ 80 | target/ 81 | 82 | # Jupyter Notebook 83 | .ipynb_checkpoints 84 | 85 | # IPython 86 | profile_default/ 87 | ipython_config.py 88 | 89 | # pyenv 90 | # For a library or package, you might want to ignore these files since the code is 91 | # intended to run in multiple environments; otherwise, check them in: 92 | # .python-version 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # poetry 102 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 103 | # This is especially recommended for binary packages to ensure reproducibility, and is more 104 | # commonly ignored for libraries. 105 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 106 | #poetry.lock 107 | 108 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 109 | __pypackages__/ 110 | 111 | # Celery stuff 112 | celerybeat-schedule 113 | celerybeat.pid 114 | 115 | # SageMath parsed files 116 | *.sage.py 117 | 118 | # Environments 119 | .env 120 | .venv 121 | env/ 122 | venv/ 123 | ENV/ 124 | env.bak/ 125 | venv.bak/ 126 | 127 | # Spyder project settings 128 | .spyderproject 129 | .spyproject 130 | 131 | # Rope project settings 132 | .ropeproject 133 | 134 | # mkdocs documentation 135 | /site 136 | 137 | # mypy 138 | .mypy_cache/ 139 | .dmypy.json 140 | dmypy.json 141 | 142 | # Pyre type checker 143 | .pyre/ 144 | 145 | # pytype static type analyzer 146 | .pytype/ 147 | 148 | # Cython debug symbols 149 | cython_debug/ 150 | 151 | # PyCharm 152 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 153 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 154 | .idea 155 | -------------------------------------------------------------------------------- /tools/__init__.py: -------------------------------------------------------------------------------- 1 | from contextlib import suppress 2 | from os import urandom 3 | from re import compile, IGNORECASE, MULTILINE 4 | from socket import inet_ntop, inet_ntoa, AF_INET6 5 | from string import ascii_letters 6 | from struct import pack 7 | from struct import pack as data_pack 8 | from sys import maxsize 9 | from typing import Callable, Any, List 10 | from time import time 11 | 12 | __all__ = ["Patterns", "Random", "Tool", "Math", "Timer"] 13 | 14 | 15 | class Random: 16 | latters: List[str] = list(ascii_letters) 17 | rand_str: Callable[[int], str] = lambda length=16: ''.join( 18 | Random.rand_choice(*Random.latters) for _ in range(length)) 19 | rand_char: Callable[[int], chr] = lambda length=16: "".join( 20 | [chr(Random.rand_int(0, 1000)) for _ in range(length)]) 21 | rand_ipv4: Callable[[], str] = lambda: inet_ntoa( 22 | data_pack('>I', Random.rand_int(1, 0xffffffff))) 23 | rand_ipv6: Callable[[], str] = lambda: inet_ntop( 24 | AF_INET6, pack('>QQ', Random.rand_bits(64), Random.rand_bits(64))) 25 | rand_int: Callable[[int, int], 26 | int] = lambda minimum=0, maximum=maxsize: int( 27 | Random.rand_float(minimum, maximum)) 28 | rand_choice: Callable[[List[Any]], Any] = lambda *data: data[ 29 | (Random.rand_int(maximum=len(data) - 2) or 0)] 30 | rand: Callable[ 31 | [], int] = lambda: (int.from_bytes(urandom(7), 'big') >> 3) * (2 ** -53) 32 | 33 | @staticmethod 34 | def rand_bits(maximum: int = 255) -> int: 35 | numbytes = (maximum + 7) // 8 36 | return int.from_bytes(urandom(numbytes), 37 | 'big') >> (numbytes * 8 - maximum) 38 | 39 | @staticmethod 40 | def rand_float(minimum: float = 0.0, 41 | maximum: float = (maxsize * 1.0)) -> float: 42 | with suppress(ZeroDivisionError): 43 | return abs((Random.rand() * maximum) % (minimum - 44 | (maximum + 1))) + minimum 45 | return 0.0 46 | 47 | 48 | class Patterns: 49 | Port = compile( 50 | "^((6553[0-5])|(655[0-2][0-9])|(65[0-4][0-9]" 51 | "{2})|(6[0-4][0-9]{3})|([1-5][0-9]{4})|([0-5]{0,5})|([0-9]{1,4}))$") 52 | IP = compile("^((?:\d{1,3}\.){3}\d{1,3})$") 53 | IPPort = compile("^((?:\d{1,3}\.){3}\d{1,3}):(\d{1,5})$") 54 | Proxy = compile( 55 | r"^(?:\[|)(?:\s+|)(socks[45]|http(?:s|))(?:[]|]|)(?:\s+|)(?:](?:\s+|)|\|(?:\s+|)|://(?:\s+|)|)" 56 | r"((?:\d+.){3}\d+|\S+[.]\w{2,3})" 57 | r"(?:[:]|)(\d+|)" 58 | r"(?::(.+):(.+)|)$", IGNORECASE | MULTILINE) 59 | URL = compile("\S+[.]\w{2,3}") 60 | 61 | @staticmethod 62 | def parsecommand(cmd: str): 63 | with suppress(Exception): 64 | sp = cmd.split(" ") 65 | return sp[0].lower(), sp[1:] 66 | return None, [] 67 | 68 | 69 | class Tool: 70 | @staticmethod 71 | async def run(console, *args): 72 | raise NotImplementedError 73 | 74 | 75 | class Math: 76 | percenctage = lambda part, whole: 100 * float(part) / float(whole) 77 | 78 | @staticmethod 79 | def ping_sizer(lists): 80 | return (min(lists, key=int), max(lists, key=int), round(sum(lists) / len(lists), 2)) if lists else (0, 0, 0) 81 | 82 | 83 | class Timer: 84 | _start: time 85 | _done: time 86 | 87 | def __enter__(self): 88 | self._start = time() 89 | return self 90 | 91 | def __exit__(self, exc_type, exc_val, exc_tb): 92 | self._done = time() 93 | 94 | def __aenter__(self): 95 | self._start = time() 96 | return self 97 | 98 | def __aexit__(self, exc_type, exc_val, exc_tb): 99 | self._done = time() 100 | 101 | def is_done(self): 102 | return self._done is None 103 | 104 | def currentms(self): 105 | return round((time() - self._start) * 1000, 2) 106 | 107 | def result(self): 108 | return round((self._done - self._start) * 1000, 2) 109 | -------------------------------------------------------------------------------- /tools/impl/Pinger.py: -------------------------------------------------------------------------------- 1 | from asyncio import sleep, open_connection, TimeoutError, wait_for 2 | from contextlib import suppress 3 | 4 | from aioconsole import aprint 5 | from aiohttp import ClientSession 6 | from aioping import ping 7 | from pystyle import Colorate, Colors 8 | from yarl import URL 9 | 10 | from tools import Tool, Timer 11 | 12 | 13 | # noinspection PyUnusedLocal 14 | class Pinger(Tool): 15 | @staticmethod 16 | async def run(console, *args): 17 | assert len(args) == 2, "Usage: Ping " 18 | 19 | bad = 0 20 | counter = 0 21 | pings = [] 22 | 23 | target = URL(args[1] if args[1].startswith("http") else ("http://" + args[1])) 24 | method = Pinger.select(args[0].upper()) 25 | 26 | port = target.port or 80 27 | 28 | assert method, "invalid method" 29 | 30 | address = target.human_repr() if method == Pinger.HTTP else target.host 31 | with suppress(KeyboardInterrupt): 32 | console.using.set() 33 | console.loading_text = "Pinging %s%susing %s protocol" % (address, 34 | (f" port {port} " if args[0].upper() not in [ 35 | "ICMP", "HTTP"] else " "), 36 | args[0].upper()) 37 | 38 | while 1: 39 | request = await method(address, port) 40 | if not request[1] and request[1] != 408: 41 | bad += 1 42 | 43 | counter += 1 44 | 45 | await sleep(0.5) 46 | pings.append(request[0]) 47 | 48 | await aprint( 49 | Colorate.Horizontal( 50 | Colors.green_to_cyan if request[1] and request[1] != 408 else Colors.red_to_purple, 51 | "[%s] Reply from %s%sstatus %s protocol %s time: %sms" % ( 52 | counter, 53 | address, 54 | (f" port {port} " if method not in [Pinger.ICMP, Pinger.HTTP] else " "), 55 | Pinger.status(request, method), 56 | args[0].upper(), 57 | request[0]))) 58 | 59 | @staticmethod 60 | async def ICMP(ip, port): 61 | with suppress(TimeoutError): 62 | return round(await ping(ip, timeout=5) * 1000, 2), True 63 | return 5000, False 64 | 65 | @staticmethod 66 | async def HTTP(ip, port): 67 | with suppress(TimeoutError), Timer() as timer: 68 | async with ClientSession(trust_env=True) as session, \ 69 | session.get(ip, timeout=5) as r: 70 | result = r.status 71 | return timer.currentms(), result 72 | return timer.result(), 408 73 | 74 | @staticmethod 75 | async def TCP(ip, port): 76 | with suppress(TimeoutError), Timer() as timer: 77 | await wait_for(open_connection(ip, port), timeout=5) 78 | return timer.currentms(), True 79 | return timer.result(), False 80 | 81 | # @staticmethod 82 | # async def UDP(IP, PORT): 83 | # with suppress(asyncio.TimeoutError), Timer() as timer: 84 | # with asyncoro.AsynCoroSocket(socket.socket(socket.AF_INET, socket.SOCK_DGRAM)) as sock: 85 | # await sock.sendto(b'\0x00', (IP, PORT)) 86 | # await sock.recvfrom(1) 87 | # return timer.currentMs(), True 88 | # return timer.currentMs(), False 89 | 90 | @staticmethod 91 | def select(txt): 92 | if txt == "TCP": 93 | return Pinger.TCP 94 | # elif TXT == "UDP": 95 | # return Pinger.UDP 96 | elif txt == "ICMP": 97 | return Pinger.ICMP 98 | elif txt == "HTTP": 99 | return Pinger.HTTP 100 | else: 101 | return None 102 | 103 | @staticmethod 104 | def status(request, method): 105 | if method is Pinger.HTTP: 106 | s = str(request[1]) 107 | # elif method is Pinger.UDP: 108 | # s = "LOAD OR FILTER" 109 | elif request[1]: 110 | s = "LOAD" 111 | else: 112 | s = "OVERLOAD" 113 | return s 114 | -------------------------------------------------------------------------------- /common/__init__.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from asyncio import sleep, Event, create_task 3 | from contextlib import suppress 4 | from getpass import getpass 5 | from itertools import cycle 6 | from os import name, system 7 | from socket import gethostname 8 | 9 | from aioconsole import aprint, ainput 10 | from pystyle import Colorate, Colors, Center 11 | 12 | import tools 13 | from tools.impl import handle as tools_handle 14 | 15 | 16 | # noinspection PyMethodMayBeStatic 17 | class Console: 18 | def __init__(self): 19 | self.using = None 20 | self.dots = cycle(["|", "/", "-", "\\"]) 21 | self.loading_text = "" 22 | 23 | async def banner(self): 24 | await self.clear() 25 | await aprint(Colorate.Diagonal(Colors.yellow_to_red, Center.XCenter(""" 26 | ╔════════════════════════════════════════════════════════════════════════════════╗ 27 | ║ ooooooooo. ooooooooooooo oooo ║ 28 | ║ `888 `Y88. 8' 888 `8 `888 ║ 29 | ║ 888 .d88' oooo ooo 888 .ooooo. .ooooo. 888 .oooo.o ║ 30 | ║ 888ooo88P' `88. .8' 888 d88' `88b d88' `88b 888 d88( "8 ║ 31 | ║ 888 `88..8' 888 888 888 888 888 888 `"Y88b. ║ 32 | ║ 888 `888' 888 888 888 888 888 888 o. )88b ║ 33 | ║ o888o .8' o888o `Y8bod8P' `Y8bod8P' o888o 8""888P' ║ 34 | ║ .o..P' ║ 35 | ║ `Y8P' ║ 36 | ╚═══════════════════════╦═════════════════════════════════╦══════════════════════╝ 37 | ║ Coded By : MXTeam - V 1.0 ║ 38 | ╚═════════════════════════════════╝\n\n\n\n"""))) 39 | 40 | async def loader(self): 41 | while 1: 42 | if not self.using.is_set(): 43 | await self.using.wait() 44 | await aprint(Colorate.Horizontal(Colors.rainbow, 45 | f"[{next(self.dots)}] {self.loading_text}"), 46 | end="\r") 47 | await sleep(.05) 48 | 49 | async def input(self, *messages, hide_value=False): 50 | if not hide_value: 51 | return await ainput(' '.join([str(txt) for txt in messages])) 52 | return getpass(' '.join([str(txt) for txt in messages])) 53 | 54 | async def cinput(self, *messages, hide_value=False): 55 | return await self.input(Colorate.Horizontal(Colors.yellow_to_red, 56 | f"╔═══[{gethostname()}@PyTools]" 57 | f"{(' (' + ' '.join([txt for txt in messages]) + ')') if messages else ''} " 58 | f"\n╚══════> "), hide_value=hide_value) 59 | 60 | async def error(self, *messages): 61 | await aprint(Colorate.Diagonal(Colors.red_to_white, "[X] " + ' '.join([str(txt) for txt in messages]))) 62 | 63 | async def clear(self): 64 | if self.using: 65 | self.using.clear() 66 | await aprint(' ' * len(self.loading_text), end="\r") 67 | self.loading_text = '' 68 | await self.command('cls' if name == 'nt' else 'clear') 69 | 70 | async def run(self): 71 | self.using = Event() 72 | create_task(self.loader()) 73 | while 1: 74 | inp = await self.cinput() 75 | if not inp: 76 | pass 77 | await self.handle(inp.strip()) 78 | self.using.clear() 79 | 80 | async def command(self, cmd): 81 | with suppress(Exception): 82 | return await (await asyncio.create_subprocess_shell(cmd=cmd)).communicate() 83 | system(cmd) 84 | 85 | async def handle(self, inp): 86 | cmd, args = tools.Patterns.parsecommand(inp) 87 | cmd = cmd.upper() 88 | 89 | try: 90 | if {cmd} & {"HELP", "?"}: 91 | await aprint("help ?") 92 | elif {cmd} & {"EXIT", "QUIT", "LOGOUT", "EXIT()"}: 93 | exit(-1) 94 | elif {cmd} & {"CLEAR", "CLS"}: 95 | await self.banner() 96 | elif await tools_handle(self, cmd, *args): 97 | pass 98 | elif cmd: 99 | await self.error(f"\"{cmd.lower()}\" command not found") 100 | 101 | except Exception as e: 102 | self.using.clear() 103 | await self.error((str(e) or repr(e)) + ' ' * 50) 104 | 105 | async def info(self, *messages): 106 | await aprint(Colorate.Diagonal(Colors.blue_to_white, "[!] " + ' '.join([txt for txt in messages]))) 107 | --------------------------------------------------------------------------------