├── LinkScanner.py ├── README.md ├── check_mc_info.py ├── rainbow.py └── sti ├── __init__.py ├── lib.py ├── primitive.py ├── register.py ├── renderfunc.py ├── rendertype.py └── sti ├── __init__.py ├── __pycache__ ├── __init__.cpython-37.pyc ├── lib.cpython-37.pyc ├── primitive.cpython-37.pyc ├── register.cpython-37.pyc ├── renderfunc.cpython-37.pyc └── rendertype.cpython-37.pyc ├── lib.py ├── primitive.py ├── register.py ├── renderfunc.py └── rendertype.py /LinkScanner.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from rainbow import * 4 | import os 5 | from colorama import * 6 | from threading import Thread 7 | from shodan import Shodan 8 | from check_mc_info import * 9 | import ctypes 10 | from datetime import datetime 11 | import re 12 | 13 | # Coded by SerLink04 (discord.gg/comunidad) 14 | 15 | name_cmd = "LinkScanner | discord.gg/comunidad | Coded by SerLink04" 16 | 17 | try: 18 | ctypes.windll.kernel32.SetConsoleTitleW(name_cmd) 19 | except: 20 | pass 21 | def fix_motd(motd): 22 | return re.sub(r'&[0-9A-Za-z]|§[0-9A-Za-z]|\n|\r|\\n', '', motd) 23 | 24 | def scan_mc(ip, puerto, scan_save, file_name, timeout): 25 | mcinfo = StatusPing(ip.strip(),puerto, timeout) 26 | if not mcinfo: 27 | return 28 | else: 29 | try: 30 | status = mcinfo.get_status() 31 | version = status["version"]["name"] 32 | players_on = status["players"]["online"] 33 | players_max = status["players"]["max"] 34 | try: 35 | motd = fix_motd(status["description"]["text"]) 36 | if motd.strip() == '': 37 | motd = fix_motd(status["description"]["extra"]["text"]) 38 | except: 39 | try: 40 | motd = fix_motd(status["description"]["extra"]["text"]) 41 | except: 42 | try: 43 | motd = fix_motd(status["description"]) 44 | except: 45 | motd = "A Minecraft Server" 46 | forge = [i.lower() for i in status] 47 | if "modinfo" in forge and not status["modinfo"]["modList"]: 48 | mods = "N/A" 49 | else: 50 | resultado = "forgedata" in forge or "modinfo" in forge 51 | if resultado: 52 | mods = "Yes" 53 | else: 54 | mods = "N/A" 55 | #rainbow(f"Server found: {ip}:{puerto} | version: {version} | players: {players_on}/{players_max} | motd: {motd} | server of mods: {mods}") 56 | print(Fore.WHITE + "Server found: " + Fore.LIGHTRED_EX + ip + ":" + str(puerto) + Fore.CYAN + " >> " + Fore.WHITE + "version: " + Fore.LIGHTRED_EX + str(version) + Fore.CYAN + 57 | " || " + Fore.WHITE + "players: " + Fore.LIGHTRED_EX + str(players_on) +"/" + str(players_max) + Fore.CYAN + " ||" + 58 | Fore.WHITE + " motd: " + Fore.LIGHTRED_EX + str(motd) + Fore.CYAN + " ||" + Fore.WHITE +" server of mods: " + Fore.LIGHTRED_EX + str(mods)) 59 | print("\n") 60 | if scan_save: 61 | file = open(file_name, 'a') 62 | file.write(f"Server found: {ip}:{puerto} | version: {version} | players: {players_on}/{players_max} | motd: {motd} | server of mods: {mods}" + '\n') 63 | file.close() 64 | except: 65 | return 66 | 67 | def is_a_semirange(ip): 68 | if not "." in ip or not "*" in ip: 69 | return False 70 | count = ip.count(".") 71 | count2 = ip.count("*") 72 | if count != 3 or count2 != 1: 73 | return False 74 | splited = ip.split(".") 75 | rangos_permitidos = [1,2,3] 76 | if len(splited) == 4 and splited[0].isdigit() and splited[1].isdigit() and splited[2].isdigit() and splited[3] == "*": 77 | if len(splited[0]) in rangos_permitidos and len(splited[1]) in rangos_permitidos and len(splited[2]) in rangos_permitidos: 78 | if int(splited[0]) < 0 or int(splited[0]) > 255 or int(splited[1]) < 0 or int(splited[1]) > 255 or int(splited[2]) < 0 or int(splited[2]) > 255: 79 | return False 80 | else: 81 | return True 82 | else: 83 | return False 84 | else: 85 | return False 86 | 87 | def is_an_ip(ip): 88 | if not "." in ip: 89 | return False 90 | count = ip.count(".") 91 | if count != 3: 92 | return False 93 | splited = ip.split(".") 94 | rangos_permitidos = [1,2,3] 95 | if len(splited) == 4 and splited[0].isdigit() and splited[1].isdigit() and splited[2].isdigit() and splited[3].isdigit(): 96 | if len(splited[0]) in rangos_permitidos and len(splited[1]) in rangos_permitidos and len(splited[2]) in rangos_permitidos and len(splited[3]) in rangos_permitidos: 97 | if int(splited[0]) < 0 or int(splited[0]) > 255 or int(splited[1]) < 0 or int(splited[1]) > 255 or int(splited[2]) < 0 or int(splited[2]) > 255 or int(splited[3]) < 0 or int(splited[3]) > 255: 98 | return False 99 | else: 100 | return True 101 | else: 102 | return False 103 | else: 104 | return False 105 | 106 | def fix_ip(semirango, ip, rango1, rango2, save_filee, name_file_, timeout): 107 | semirange_real = semirango.replace("*","") + str(ip) 108 | hilo_scan = Thread(target=range_scan_2, args=(semirange_real, rango1, rango2, save_filee, name_file_, timeout)) 109 | hilo_scan.start() 110 | 111 | def scanning(semirange, rango1, rango2, save_filee, name_file_, timeout): 112 | for i in range(1,256): 113 | scan_thread = Thread(target=fix_ip, args=(semirange, i, rango1, rango2, save_filee, name_file_, timeout)) 114 | scan_thread.start() 115 | 116 | def range_scan(semiranges, rango1, rango2, save_filee, name_file_, timeout): 117 | t1 = datetime.now() 118 | for semirange in semiranges: 119 | if type(rango1) == list: 120 | for ports_range in rango1: 121 | rango1 = int(ports_range.split("-")[0]) 122 | rango2 = int(ports_range.split("-")[1]) 123 | scan_thread2 = Thread(target=scanning, args=(semirange, rango1, rango2, save_filee, name_file_, timeout)) 124 | scan_thread2.start() 125 | else: 126 | scan_thread2 = Thread(target=scanning, args=(semirange, rango1, rango2, save_filee, name_file_, timeout)) 127 | scan_thread2.start() 128 | 129 | while scan_thread2.is_alive(): 130 | pass 131 | time.sleep(4) 132 | t2 = datetime.now() 133 | total = t2 - t1 134 | rainbow("Escaneo finalizado en " + str(total).split(".")[0] + "s") 135 | print("\n") 136 | salida = input(str(rainbow("[LinkScanner] Escaneo finalizado... Presiona enter para volver al menú")).replace("None", "")).split("-") 137 | print("\n") 138 | clear() 139 | main() 140 | 141 | def range_scan_2(ip, rango1, rango2, save_filee, name_file_, timeout): 142 | for puerto in range(rango1,rango2+1): 143 | try: 144 | checked2 = Thread(target=scan_mc, args=(ip,puerto, save_filee, name_file_, timeout)) 145 | checked2.start() 146 | except: 147 | scan_mc(ip,puerto,save_filee,name_file_,timeout) 148 | 149 | def scan(ips, rango1, rango2, scan_save2, name_file, timeout): 150 | t1 = datetime.now() 151 | if type(rango1) == list: 152 | for ip in ips: 153 | for port_range in rango1: 154 | rango_1 = port_range.split("-")[0] 155 | rango_2 = port_range.split("-")[1] 156 | for puerto in range(int(rango_1),int(rango_2)+1): 157 | scanning_mc = Thread(target=scan_mc, args=(ip,puerto, scan_save2, name_file, timeout)) 158 | scanning_mc.start() 159 | else: 160 | for ip in ips: 161 | for puerto in range(rango1,rango2+1): 162 | scanning_mc = Thread(target=scan_mc, args=(ip,puerto, scan_save2, name_file, timeout)) 163 | scanning_mc.start() 164 | time.sleep(1) 165 | scanning_mc.join() 166 | time.sleep(1) 167 | t2 = datetime.now() 168 | total = t2 - t1 169 | rainbow("Escaneo finalizado en " + str(total).split(".")[0] + "s") 170 | print("\n") 171 | salida = input(str(rainbow("[LinkScanner] Escaneo finalizado... Presiona enter para volver al menú")).replace("None", "")).split("-") 172 | print("\n") 173 | clear() 174 | main() 175 | 176 | def clear(): 177 | os.system("cls" if os.name == 'nt' else 'clear') 178 | 179 | def getIpList(file): 180 | with open(file,"r") as f: 181 | file = f.readlines() 182 | files = [f.strip() for f in file] 183 | files = [x for x in files if x != ''] 184 | return files 185 | 186 | def is_a_timeout(timeout): 187 | if not timeout.isdigit(): 188 | return False 189 | if int(timeout) < 0: 190 | return False 191 | return True 192 | 193 | def is_a_port_range(port_range): 194 | if "." in port_range: 195 | ips = port_range.split(".") 196 | for ip in ips: 197 | if not "-" in ip: 198 | return False 199 | for ip in ips: 200 | ip_splited = ip.split("-") 201 | if len(ip_splited) != 2: 202 | return False 203 | if not ip_splited[0].isdigit() or not ip_splited[1].isdigit(): 204 | return False 205 | if int(ip_splited[0]) < 1 or int(ip_splited[0]) > 65535 or int(ip_splited[1]) < 1 or int(ip_splited[1]) > 65535: 206 | return False 207 | if int(ip_splited[0]) >= int(ip_splited[1]): 208 | return False 209 | return True 210 | 211 | else: 212 | if not "-" in port_range: 213 | return False 214 | port_range_splited = port_range.split("-") 215 | if len(port_range_splited) != 2: 216 | return False 217 | if not port_range_splited[0].isdigit() or not port_range_splited[1].isdigit(): 218 | return False 219 | if int(port_range_splited[0]) < 1 or int(port_range_splited[0]) > 65534 or int(port_range_splited[1]) < 1 or int(port_range_splited[1]) > 65535: 220 | return False 221 | if int(port_range_splited[0]) >= int(port_range_splited[1]): 222 | return False 223 | return True 224 | 225 | def main(): 226 | banner = """ 227 | 228 | _ _ _ ____ 229 | | | (_)_ __ | | __/ ___| ___ __ _ _ __ _ __ ___ _ __ 230 | | | | | '_ \| |/ /\___ \ / __/ _` | '_ \| '_ \ / _ \ '__| 231 | | |___| | | | | < ___) | (_| (_| | | | | | | | __/ | 232 | |_____|_|_| |_|_|\_\|____/ \___\__,_|_| |_|_| |_|\___|_| v1.0 233 | 234 | 235 | """ 236 | 237 | rainbow(banner) 238 | rainbow(" Coded by SerLink04 #LinkSquad (discord.gg/comunidad)") 239 | print() 240 | print() 241 | rainbow(" (1) Escanear ips") 242 | print("\n") 243 | rainbow(" (2) Escanear list ip") 244 | print("\n") 245 | rainbow(" (3) Escanear semirangos") 246 | print("\n\n") 247 | opt = input(str(rainbow(" [LinkScanner] Selecciona una opción: ")).replace("None", "")) 248 | if opt == "1": 249 | print() 250 | ips = input(str(rainbow("[LinkScanner] Escribe las ips a escanear separadas por espacios: ")).replace("None", "")).split() 251 | if len(ips) == 0: 252 | print("No has introducido ninguna ip para escanear") 253 | salida = input(str(rainbow("Presiona enter para volver al menú...")).replace("None", "")) 254 | clear() 255 | main() 256 | for ip in ips: 257 | if not is_an_ip(ip): 258 | print("Has introducido una o unas ips erróneas") 259 | salida = input(str(rainbow("Presiona enter para volver al menú...")).replace("None", "")) 260 | clear() 261 | main() 262 | print() 263 | opt_2 = input(str(rainbow("[LinkScanner] ¿Deseas guardar los resultados en un txt? s/n: ")).replace("None", "")).lower().strip() 264 | print() 265 | sis = ["y","yes","yup","yeah","s","si","sip"] 266 | if opt_2 in sis: 267 | scan_save_2 = True 268 | name_file = input(str(rainbow("[LinkScanner] Introduce el nombre al archivo (result_ip_scan.txt by default): ")).replace("None", "")).strip() 269 | if name_file == "": 270 | name_file = 'result_ip_scan.txt' 271 | else: 272 | if not name_file.endswith('.txt'): 273 | name_file = name_file + '.txt' 274 | if " " in name_file: 275 | name_file = name_file.split() 276 | name_file = "_".join(name_file) 277 | print() 278 | else: 279 | scan_save_2 = False 280 | name_file = False 281 | timeout = input(str(rainbow("[LinkScanner] Introduce un timeout (3 seconds by default): ")).replace("None", "")) 282 | if timeout.strip() == "": 283 | timeout = 3 284 | else: 285 | if not is_a_timeout(timeout): 286 | print("Has introducido un timeout incorrecto...") 287 | salida = input(str(rainbow("Presiona enter para volver al menú...")).replace("None", "")) 288 | clear() 289 | main() 290 | 291 | print() 292 | rangos = input(str(rainbow("[LinkScanner] Introduce el rango de puertos a escanear (Ej: 25530-25580): ")).replace("None", "")) 293 | if not is_a_port_range(rangos): 294 | print("Has introducido un rango de puertos incorrecto... Uso correcto: 25530-25580 o 25530-25580.25600-25650") 295 | salida = input(str(rainbow("Presiona enter para volver al menú...")).replace("None", "")) 296 | clear() 297 | main() 298 | print("Esto demorará unos momentos...") 299 | t1 = datetime.now() 300 | if "." in rangos: 301 | rango1 = rangos.split(".") 302 | rango2 = rango1 303 | else: 304 | rangos = rangos.split("-") 305 | rango1 = int(rangos[0]) 306 | rango2 = int(rangos[1]) 307 | try: 308 | hilo = Thread(target=scan, args=(ips, rango1, rango2, scan_save_2, name_file, timeout)) 309 | hilo.start() 310 | except: 311 | hilo = Thread(target=scan, args=(ips, rango1, rango2, scan_save_2, name_file, timeout)) 312 | hilo.start() 313 | hilo.join() 314 | 315 | elif opt == "2": 316 | print() 317 | ips = input(str(rainbow("[LinkScanner] Introduce el directorio del archivo a escanear (ips.txt by default): ")).replace("None", "")).strip() 318 | if ips == "": 319 | file = "ips.txt" 320 | else: 321 | file = ips 322 | if not file.endswith('.txt'): 323 | file = file + '.txt' 324 | if " " in file: 325 | file = file.split() 326 | file = "_".join(file) 327 | filee = open(file, "a") 328 | filee.close() 329 | lines = open(file, "r+") 330 | archivo = lines.readlines() 331 | archivo_striped = [x.strip() for x in archivo] 332 | if len(archivo_striped) == 0: 333 | print() 334 | rainbow("No hay ninguna ip establecida en el archivo") 335 | print() 336 | ips = input(str(rainbow("[LinkScanner] ¿Deseas establecer ips para escanear? y/n: ")).replace("None", "")).strip() 337 | sis = ["y","yes","yup","yeah","s","si","sip"] 338 | if ips.lower() in sis: 339 | print() 340 | versions = input(str(rainbow("[LinkScanner] Escribe las versiones para escanear entre comillas ('spigot 1.8.8' 'paperspigot'): ")).replace("None", "")).strip().split("'") 341 | versions = [x.strip() for x in versions] 342 | versions = [x for x in versions if x != ''] 343 | versions = [x.replace(" ","-") for x in versions] 344 | print("Buscando ips relacionadas a esos parámetros y añadiéndolas a la lista...") 345 | api = Shodan('76uWNv0clygVHb8IzwYoPfWCw8MqCF8W') 346 | ips_to_scan = [] 347 | count = 0 348 | for version in versions: 349 | results = api.search(version) 350 | for result in results['matches']: 351 | ip = str(result['ip_str']) 352 | ips_to_scan.append(ip) 353 | count += 1 354 | for ip in ips_to_scan: 355 | lines.write(f"\n{ip}") 356 | lines.close() 357 | print(Fore.CYAN + f"Han sido agregadas {count} ips a la lista.") 358 | salida = input(str(rainbow("Presiona enter para volver al menú...")).replace("None", "")) 359 | clear() 360 | main() 361 | 362 | else: 363 | print() 364 | rainbow("Volviendo al menú...") 365 | time.sleep(1) 366 | lines.close() 367 | clear() 368 | main() 369 | else: 370 | print() 371 | sis = ["y","yes","yup","yeah","s","si","sip"] 372 | opt2 = input(str(rainbow("[LinkScanner] Se han detectado ips en el txt ¿Deseas revisar las ips antes del escaneo? s/n: ")).replace("None", "")).strip() 373 | if opt2.lower() in sis: 374 | ips = getIpList(file) 375 | count = 0 376 | for ip in ips: 377 | rainbow("IP in the txt: " + ip + "\n") 378 | count += 1 379 | print(Fore.CYAN + "IPS totales: " + Fore.LIGHTBLUE_EX + str(count)) 380 | print() 381 | opt_2 = input(str(rainbow("[LinkScanner] ¿Deseas guardar los resultados en un txt? s/n: ")).replace("None", "")).strip() 382 | print() 383 | if opt_2 in sis: 384 | scan_save = True 385 | name_file = input(str(rainbow("[LinkScanner] Introduce el nombre al archivo (result.txt by default): ")).replace("None", "")).strip() 386 | print() 387 | if name_file.strip() == "": 388 | name_file = 'result.txt' 389 | else: 390 | if not name_file.endswith('.txt'): 391 | name_file = name_file + '.txt' 392 | if " " in name_file: 393 | name_file = name_file.split() 394 | name_file = "_".join(name_file) 395 | else: 396 | scan_save = False 397 | name_file = False 398 | sis = ["y","yes","yup","yeah","s","si","sip"] 399 | opt3 = input(str(rainbow("[LinkScanner] ¿Te gustaría agregar más ips para escanearlas? s/n: ")).replace("None", "")).strip() 400 | if opt3.lower() in sis: 401 | print() 402 | versions = input(str(rainbow("[LinkScanner] Escribe las versiones para escanear entre comillas ('spigot 1.8.8' 'paperspigot'): ")).replace("None", "")).strip().split("'") 403 | versions = [x.strip() for x in versions] 404 | versions = [x for x in versions if x != ''] 405 | versions = [x.replace(" ","-") for x in versions] 406 | print("Buscando ips relacionadas a esos parámetros y añadiéndolas a la lista...") 407 | api = Shodan('76uWNv0clygVHb8IzwYoPfWCw8MqCF8W') 408 | ips_to_scan = [] 409 | count = 0 410 | for version in versions: 411 | results = api.search(version) 412 | for result in results['matches']: 413 | ip = str(result['ip_str']) 414 | ips_to_scan.append(ip) 415 | count += 1 416 | for ip in ips_to_scan: 417 | lines.write(f"\n{ip}") 418 | lines.close() 419 | print(Fore.CYAN + f"Han sido agregadas {count} ips a la lista.") 420 | print() 421 | timeout = input(str(rainbow("[LinkScanner] Introduce un timeout (3 seconds by default): ")).replace("None", "")) 422 | if timeout.strip() == "": 423 | timeout = 3 424 | else: 425 | if not is_a_timeout(timeout): 426 | print("Has introducido un timeout incorrecto...") 427 | salida = input(str(rainbow("Presiona enter para volver al menú...")).replace("None", "")) 428 | clear() 429 | main() 430 | print() 431 | rangos = input(str(rainbow("[LinkScanner] Introduce el rango de puertos a escanear (Ej: 25530-25580): ")).replace("None", "")) 432 | if not is_a_port_range(rangos): 433 | print("Has introducido un rango de puertos incorrecto... Uso correcto: 25530-25580 o 25530-25580.25600-25650") 434 | salida = input(str(rainbow("Presiona enter para volver al menú...")).replace("None", "")) 435 | clear() 436 | main() 437 | if "." in rangos: 438 | rango1 = rangos.split(".") 439 | rango2 = rango1 440 | else: 441 | rangos = rangos.split("-") 442 | rango1 = int(rangos[0]) 443 | rango2 = int(rangos[1]) 444 | print() 445 | starting_scan = input(str(rainbow("[LinkScanner] ¿Deseas iniciar el escaneo? s/n: ")).replace("None", "")).strip() 446 | if starting_scan.lower() in sis: 447 | print("Esto demorará unos momentos...") 448 | try: 449 | hilo = Thread(target=scan, args=(getIpList(file), rango1, rango2, scan_save, name_file, timeout)) 450 | hilo.start() 451 | except: 452 | scan(getIpList(file), rango1, rango2, scan_save, name_file, timeout) 453 | hilo.join() 454 | else: 455 | print() 456 | print("Volviendo al menú principal...") 457 | time.sleep(1) 458 | clear() 459 | main() 460 | 461 | elif opt == "3": 462 | print() 463 | sies = ["y","yes","yup","yeah","s","si","sip"] 464 | rangos_to_scan = input(str(rainbow("[LinkScanner] Introduce los semirangos a escanear (Ej: 142.44.143.* 66.70.180.*): ")).replace("None", "")).split() 465 | if len(rangos_to_scan) == 0: 466 | print("No has introducido ningún semirango para escanear") 467 | salida = input(str(rainbow("Presiona enter para volver al menú...")).replace("None", "")) 468 | clear() 469 | main() 470 | for semirangoo in rangos_to_scan: 471 | if is_a_semirange(semirangoo): 472 | pass 473 | else: 474 | print("Has introducido un o unos semirangos inválidos... (Uso correcto: 142.44.143.* 66.70.180.*)") 475 | salida = input(str(rainbow("Presiona enter para volver al menú...")).replace("None", "")) 476 | clear() 477 | main() 478 | print() 479 | option_2 = input(str(rainbow("[LinkScanner] ¿Deseas guardar los resultados en un txt? s/n: ")).replace("None", "")).lower().strip() 480 | print() 481 | if option_2 in sies: 482 | scan_save = True 483 | name_file = input(str(rainbow("[LinkScanner] Introduce el nombre al archivo (result_linkscanner.txt by default): ")).replace("None", "")).strip() 484 | print() 485 | if name_file == "": 486 | name_file = 'result_linkscanner.txt' 487 | else: 488 | if not name_file.endswith('.txt'): 489 | name_file = name_file + '.txt' 490 | if " " in name_file: 491 | name_file = name_file.split() 492 | name_file = "_".join(name_file) 493 | else: 494 | scan_save = False 495 | name_file = False 496 | timeout = input(str(rainbow("[LinkScanner] Introduce un timeout (3 seconds by default): ")).replace("None", "")) 497 | if timeout.strip() == "": 498 | timeout = 3 499 | else: 500 | if not is_a_timeout(timeout): 501 | print("Has introducido un timeout incorrecto...") 502 | salida = input(str(rainbow("Presiona enter para volver al menú...")).replace("None", "")) 503 | clear() 504 | main() 505 | print() 506 | ports_range = input(str(rainbow("[LinkScanner] Introduce un rango de puertos (Ej: 25530-25580): ")).replace("None", "")) 507 | if not is_a_port_range(ports_range): 508 | print("Has introducido un rango de puertos incorrecto... Uso correcto: 25530-25580 o 25530-25580.25600-25650") 509 | salida = input(str(rainbow("Presiona enter para volver al menú...")).replace("None", "")) 510 | clear() 511 | main() 512 | if "." in ports_range: 513 | rango1 = ports_range.split(".") 514 | rango2 = rango1 515 | else: 516 | rangos = ports_range.split("-") 517 | rango1 = int(rangos[0]) 518 | rango2 = int(rangos[1]) 519 | print("Esto demorará unos momentos...") 520 | range_scan(rangos_to_scan, rango1, rango2, scan_save, name_file, timeout) 521 | 522 | else: 523 | print("\n") 524 | rainbow(" [LinkScanner] Esa opción no existe...") 525 | print("\n") 526 | menu = input(Fore.WHITE + " Pulsa intro para volver al menú...") 527 | clear() 528 | main() 529 | clear() 530 | main() 531 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🚀 LinkScanner 🚀 2 | 3 | ### Apreciaria que entren a mi comunidad. 4 | #### ➜ https://discord.gg/programadores 5 | 6 | ![linkscanner](https://media.discordapp.net/attachments/811334964293140501/814578698577379348/lsq_cmd.PNG) 7 | 8 | ## ⚙️➜ Instalación 9 | ``` 10 | apt-get install python3 11 | apt-get install python3-pip 12 | pip3 install shodan 13 | pip3 install colorama 14 | git clone https://github.com/SerLink04/LinkScanner 15 | cd LinkScanner 16 | python3 LinkScanner.py 17 | ``` 18 | ## 🪐➜ Opciones 19 | Las opciones dentro de la tool 20 | ``` 21 | 1) Escanear ips 22 | 2) Escanear list ip 23 | 3) Escanear semirangos 24 | ``` 25 | ## 🌪➜ Funcionamiento 26 | Funcionamiento de las distintas opciones dentro de la tool 27 | ``` 28 | (1) Con esta opción podrás escanear un montón de ips al mismo tiempo, en un rango totalmente configurable, escribe las ips que quieras escanear seguidas de 29 | espacios (si pones los datos incorrectamente la script te lo notificará y te impedirá seguir), seguidamente puedes optar por guardar los resultados en un 30 | txt, el archivo siempre tiene que ser .txt, puedes poner solamente el nombre que quieras darle al archivo (puedes añadir espacios, los cuales serán sustituidos 31 | por un _) y la propia script te lo guardará en extensión .txt, si añades el .txt al final del nombre no pasará nada, y los resultados se guardaran igualmente 32 | en un txt (si no pones nada se pondrá como n automáticamente) , a continuación podeis insertar un timeout, que es el tiempo que quereis que la script este 33 | intentando comprobar por cada puerto si este se encuentra abierto, de tal manera que si pongo 5 seg de timeout , la script intentará verificar si dicho puerto 34 | con dicha ip esta open durante un máximo tiempo de 5 seg (Esto se debe a que a veces puede tardar 20 seg como 1 seg en verificar si un puerto esta o no open), 35 | finalmente poneis el rango de puertos a escanear, el cual es totalmente configurable, podeis escanear un solo rango de puertos o varios a la vez (Ejemplo: 25530-25580 36 | (Escanearia un total de 50 puertos [comprendidos en ese rango]) o 25530-25580.25600-25650 (Escanearia los puertos comprendidos entre 25530-25580 y 25600-25650)) 37 | ``` 38 | ``` 39 | (2) Primero tienes que poner el nombre del archivo donde generaste las ips, sino lo tienes pulsa enter o pon cualquier nombre al archivo, en el caso de que detecte 40 | de que no hay ips en dicho archivo, simplemente agrega entre comillas simples las versiones / motds que te gustaría que fueran dichos servidores, y la script buscará 41 | servidores y obtendrá sus ips para posteriormente poder escanearlas a partir de esos parámetros. En el caso de que si detecta las ips (RECORDAD VERIFICAR QUE EL 42 | ARCHIVO ES EL CORRECTO) os pedirá a continuación si os gustaría ver las ips que se escanearan, esto os dará el número de ips totales que se escanearan y te mostará 43 | dichas ips. Luego te preguntará si deseas guardar los resultados en un txt, más adelante te pregunta si te gustaría agregar más ips para escanear (sería hacer que 44 | haciamos cuando no detectaba ips, si añadis más versiones las ips que encuentre la script se agregarán al txt), luego introduces el timeout y el rango de puertos y 45 | finalmente nos preguntará si deseamos iniciar el escaneo, escribimos s y pulsamos enter. 46 | ``` 47 | ``` 48 | (3) Introduces los semirangos a escanear separados por espacios, indica si quieres que se guarden el escaneo en un txt, inserta un timeout y finalmente introduce el 49 | rango de puertos a escanear. 50 | ``` 51 | 52 | ###### Hecho con ❤️ Necesitas ayuda? contactame por (``SerLink04#1345``) o contactame en Twitter: [@Link04Ser](https://twitter.com/Link04Ser) 53 | -------------------------------------------------------------------------------- /check_mc_info.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import struct 3 | import json 4 | import time 5 | 6 | 7 | class StatusPing: 8 | """ Get the ping status for the Minecraft server """ 9 | 10 | def __init__(self, host='localhost', port=25565, timeout=5): 11 | """ Init the hostname and the port """ 12 | self._host = host 13 | self._port = port 14 | self._timeout = timeout 15 | 16 | def _unpack_varint(self, sock): 17 | """ Unpack the varint """ 18 | data = 0 19 | for i in range(5): 20 | ordinal = sock.recv(1) 21 | 22 | if len(ordinal) == 0: 23 | break 24 | 25 | byte = ord(ordinal) 26 | data |= (byte & 0x7F) << 7 * i 27 | 28 | if not byte & 0x80: 29 | break 30 | 31 | return data 32 | 33 | def _pack_varint(self, data): 34 | """ Pack the var int """ 35 | ordinal = b'' 36 | 37 | while True: 38 | byte = data & 0x7F 39 | data >>= 7 40 | ordinal += struct.pack('B', byte | (0x80 if data > 0 else 0)) 41 | 42 | if data == 0: 43 | break 44 | 45 | return ordinal 46 | 47 | def _pack_data(self, data): 48 | """ Page the data """ 49 | if type(data) is str: 50 | data = data.encode('utf8') 51 | return self._pack_varint(len(data)) + data 52 | elif type(data) is int: 53 | return struct.pack('H', data) 54 | elif type(data) is float: 55 | return struct.pack('Q', int(data)) 56 | else: 57 | return data 58 | 59 | def _send_data(self, connection, *args): 60 | """ Send the data on the connection """ 61 | data = b'' 62 | 63 | for arg in args: 64 | data += self._pack_data(arg) 65 | 66 | connection.send(self._pack_varint(len(data)) + data) 67 | 68 | def _read_fully(self, connection, extra_varint=False): 69 | """ Read the connection and return the bytes """ 70 | packet_length = self._unpack_varint(connection) 71 | packet_id = self._unpack_varint(connection) 72 | byte = b'' 73 | 74 | if extra_varint: 75 | # Packet contained netty header offset for this 76 | if packet_id > packet_length: 77 | self._unpack_varint(connection) 78 | 79 | extra_length = self._unpack_varint(connection) 80 | 81 | while len(byte) < extra_length: 82 | byte += connection.recv(extra_length) 83 | 84 | else: 85 | byte = connection.recv(packet_length) 86 | 87 | return byte 88 | 89 | def get_status(self): 90 | """ Get the status response """ 91 | with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as connection: 92 | try: 93 | connection.settimeout(self._timeout) 94 | connection.connect((self._host, self._port)) 95 | except socket.timeout: 96 | return False 97 | 98 | # Send handshake + status request 99 | self._send_data(connection, b'\x00\x00', self._host, self._port, b'\x01') 100 | self._send_data(connection, b'\x00') 101 | 102 | # Read response, offset for string length 103 | data = self._read_fully(connection, extra_varint=True) 104 | 105 | # Send and read unix time 106 | self._send_data(connection, b'\x01', time.time() * 1000) 107 | unix = self._read_fully(connection) 108 | 109 | # Load json and return 110 | response = json.loads(data.decode('utf8')) 111 | response['ping'] = int(time.time() * 1000) - struct.unpack('Q', unix)[0] 112 | 113 | return response -------------------------------------------------------------------------------- /rainbow.py: -------------------------------------------------------------------------------- 1 | import sys, random 2 | from sti import register as sti 3 | bg = sti.bg 4 | fg = sti.fg 5 | rojo = fg(255,17,0)+"" 6 | naranja = fg(255,153,0)+"" 7 | amarillo = fg(255,217,0)+"" 8 | lima = fg(191,255,0)+"" 9 | verde = fg(85,255,0)+"" 10 | celeste = fg(0,255,115)+"" 11 | cian = fg(0,255,195)+"" 12 | gris = fg(207,207,207)+"" 13 | azul = fg(0,149,255)+"" 14 | morado = fg(162,0,255)+"" 15 | violeta = fg(255,0,242)+"" 16 | rosa = fg(255,140,215)+"" 17 | blanco = fg(255,255,255)+"" 18 | 19 | def rainbow(message): #Printea en modo arcoiris 20 | scale = [(255,0,0),(255,50,0),(255,100,0),(255,150,0),(255,200,0),(255,255,0),(200,255,0),(150,255,0),(100,255,0),(50,255,0),(0,255,0),(0,255,50),(0,255,100),(0,255,150),(0,255,200),(0,255,255),(0,200,255),(0,150,255),(0,100,255),(0,50,255),(0,0,255),(50,0,255),(100,0,255),(150,0,255),(200,0,255),(255,0,255),(255,0,200),(255,0,150),(255,0,100),(255,0,50),(255,0,0)] 21 | msg = message.replace("","@@@") 22 | bb = msg.split("@@@") 23 | a = random.randint(0,30) 24 | sys.stdout.write ("") 25 | for sc in bb: 26 | if a == 31: a = 0 27 | ls = list(scale[int(a)]) 28 | a = a + 1 29 | sc = sc.replace(sc,fg(str(ls[0]),str(ls[1]),str(ls[2]))+sc+fg.rs) 30 | sys.stdout.write(sc) -------------------------------------------------------------------------------- /sti/__init__.py: -------------------------------------------------------------------------------- 1 | from .primitive import Register, Style 2 | from .register import ef, fg, bg, rs, EfRegister, FgRegister, BgRegister, RsRegister 3 | from .rendertype import * 4 | from .lib import * 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sti/lib.py: -------------------------------------------------------------------------------- 1 | from .primitive import Register 2 | 3 | 4 | def mute(*objects: Register) -> None: 5 | """ 6 | Use this function to mute multiple register-objects at once. 7 | 8 | :param objects: Pass multiple register-objects to the function. 9 | """ 10 | err = ValueError( 11 | "The mute() method can only be used with objects that inherit "\ 12 | "from the 'Register class'." 13 | ) 14 | for obj in objects: 15 | if not isinstance(obj, Register): 16 | raise err 17 | obj.mute() 18 | 19 | 20 | def unmute(*objects: Register) -> None: 21 | """ 22 | Use this function to unmute multiple register-objects at once. 23 | 24 | :param objects: Pass multiple register-objects to the function. 25 | """ 26 | err = ValueError( 27 | "The unmute() method can only be used with objects that inherit "\ 28 | "from the 'Register class'." 29 | ) 30 | for obj in objects: 31 | if not isinstance(obj, Register): 32 | raise err 33 | obj.unmute() 34 | 35 | -------------------------------------------------------------------------------- /sti/primitive.py: -------------------------------------------------------------------------------- 1 | """ 2 | The Register class: Sty's heart. 3 | """ 4 | from collections import namedtuple 5 | from typing import Union, Callable, Dict, List, Tuple, Iterable 6 | from copy import deepcopy 7 | from .rendertype import RenderType 8 | 9 | 10 | class Style(str): 11 | """ 12 | This type stores the different styling rules for the registers and the resulting 13 | ANSI-sequence as a string. 14 | 15 | For example: 16 | 17 | fg.orange = Style(RgbFg(1,5,10), Sgr(1)) 18 | 19 | isinstance(fg.orange, Style) # True 20 | 21 | isinstance(fg.orange, str) # True 22 | 23 | str(fg.orange) # '\x1b[38;2;1;5;10m\x1b[1m' (The ASNI sequence for orange and bold) 24 | 25 | """ 26 | 27 | def __new__(cls, *rules, value=''): 28 | cls = str.__new__(cls, value) 29 | cls.rules = rules 30 | return cls 31 | 32 | 33 | def _render_rules( 34 | renderfuncs, 35 | rules, 36 | ) -> Tuple[str, List[RenderType]]: 37 | 38 | rendered: str = '' 39 | flattened_rules = [] 40 | 41 | if isinstance(rules, RenderType): 42 | f1: Callable = renderfuncs[type(rules)] 43 | rendered += f1(*rules.args) 44 | flattened_rules.append(rules) 45 | 46 | elif isinstance(rules, Style): 47 | r1, r2 = _render_rules(renderfuncs, rules.rules) 48 | rendered += r1 49 | flattened_rules.extend(r2) 50 | 51 | elif isinstance(rules, (list, tuple)): 52 | 53 | for rule in rules: 54 | 55 | r1, r2 = _render_rules(renderfuncs, rule) 56 | rendered += r1 57 | flattened_rules.extend(r2) 58 | 59 | else: 60 | raise ValueError("Parameter 'rules' must be of type Rule or Tuple[Rule].") 61 | 62 | return rendered, flattened_rules 63 | 64 | 65 | class Register: 66 | """ 67 | This is the base Register class. All default registers (fg, bg, ef, rs) are 68 | created from this class. You can use it to create your own custom registers. 69 | """ 70 | 71 | def __init__(self): 72 | self.renderfuncs: Dict[RenderType, Callable] = {} 73 | self.is_muted = False 74 | self.eightbit_call = lambda x: x 75 | self.rgb_call = lambda r, g, b: (r, g, b) 76 | 77 | def __setattr__(self, name: str, value: Union[Style, Tuple[Style]]): 78 | 79 | if isinstance(value, Style): 80 | 81 | if self.is_muted: 82 | rendered_style = Style(value.rules, value='') 83 | else: 84 | rendered, rules = _render_rules(self.renderfuncs, value.rules) 85 | rendered_style = Style(rules, value=rendered) 86 | 87 | return super().__setattr__(name, rendered_style) 88 | else: 89 | return super().__setattr__(name, value) 90 | 91 | def __call__(self, *args, **kwargs) -> str: 92 | """ 93 | This function is to handle calls such as `fg(42)`, `bg(102, 49, 42)`, `fg('red')`. 94 | """ 95 | 96 | # # Return empty str if object is muted. 97 | if self.is_muted: 98 | return '' 99 | 100 | len_args = len(args) 101 | 102 | if len_args == 1: 103 | 104 | # If input is an 8bit color code, run 8bit render function. 105 | if isinstance(args[0], int): 106 | return self.eightbit_call(*args, **kwargs) 107 | 108 | # If input is a string, return attribute with the name that matches 109 | # input. 110 | else: 111 | return getattr(self, args[0]) 112 | 113 | # If input is an 24bit color code, run 24bit render function. 114 | elif len_args == 3: 115 | return self.rgb_call(*args, **kwargs) 116 | 117 | else: 118 | return '' 119 | 120 | def set_style( 121 | self, 122 | name: str, 123 | *rules: Union[RenderType, Tuple[RenderType, ...]], 124 | ) -> None: 125 | """ 126 | DEPRECATED: This method will be removed in favour of the Style() type. 127 | 128 | With this method, you can add or change styles for a register-object. 129 | 130 | :param name: The field name for the new style. 131 | """ 132 | setattr(self, name, Style(*rules)) 133 | 134 | def get_style( 135 | self, 136 | name: str, 137 | ) -> Tuple[RenderType, ...]: 138 | """ 139 | DEPRECATED: This method will be removed in favour of the Style() type. 140 | 141 | Retrieve styling rules from a register-object. 142 | This is useful in case you want to compose new styles out of existing styles. 143 | 144 | :param name: The name of the style for which you want to retrieve the styling rules. 145 | """ 146 | return getattr(self, name).rules 147 | 148 | def set_eightbit_call(self, rendertype: RenderType) -> None: 149 | """ 150 | You can call a register-object directly. A call like this ``fg(144)`` 151 | is a Eightbit-call. With this method you can define the render-type for such calls. 152 | 153 | :param rendertype: The new rendertype that is used for Eightbit-calls. 154 | """ 155 | func: Callable = self.renderfuncs[rendertype] 156 | self.eightbit_call = func 157 | 158 | def set_rgb_call(self, rendertype: RenderType) -> None: 159 | """ 160 | You can call a register-object directly. A call like this ``fg(10, 42, 255)`` 161 | is a RGB-call. With this method you can define the render-type for such calls. 162 | 163 | :param rendertype: The new rendertype that is used for RGB-calls. 164 | """ 165 | func: Callable = self.renderfuncs[rendertype] 166 | self.rgb_call = func 167 | 168 | def set_renderfunc(self, rendertype: RenderType, func: Callable) -> None: 169 | """ 170 | With this method you can add or replace render-functions for a given register-object: 171 | 172 | :param rendertype: The render type for which the new renderfunc is used. 173 | :param func: The new render function. 174 | """ 175 | # Save new render-func in register 176 | self.renderfuncs.update({rendertype: func}) 177 | 178 | # Update style atributes and styles with the new renderfunc. 179 | for attr_name in dir(self): 180 | val = getattr(self, attr_name) 181 | if isinstance(val, Style): 182 | setattr(self, attr_name, val) 183 | 184 | def mute(self): 185 | """ 186 | Sometimes it is useful to disable the formatting for a register-object. You can 187 | do so by invoking this method. 188 | """ 189 | self.is_muted = True 190 | 191 | for attr_name in dir(self): 192 | val = getattr(self, attr_name) 193 | if isinstance(val, Style): 194 | setattr(self, attr_name, val) 195 | 196 | def unmute(self): 197 | """ 198 | Use this method to unmute a previously muted register object. 199 | """ 200 | self.is_muted = False 201 | 202 | for attr_name in dir(self): 203 | val = getattr(self, attr_name) 204 | if isinstance(val, Style): 205 | setattr(self, attr_name, val) 206 | 207 | def as_dict(self) -> Dict[str, str]: 208 | """ 209 | Export color register as dict. 210 | """ 211 | items: Dict[str, str] = {} 212 | 213 | for name in dir(self): 214 | 215 | if not name.startswith("_") and\ 216 | isinstance(getattr(self, name), str): 217 | 218 | items.update({name: str(getattr(self, name))}) 219 | 220 | return items 221 | 222 | def as_namedtuple(self): 223 | """ 224 | Export color register as namedtuple. 225 | """ 226 | d = self.as_dict() 227 | return namedtuple('StyleRegister', d.keys())(*d.values()) 228 | 229 | def copy(self): 230 | """ 231 | Make a deepcopy of a register-object. 232 | """ 233 | return deepcopy(self) 234 | 235 | -------------------------------------------------------------------------------- /sti/register.py: -------------------------------------------------------------------------------- 1 | """ 2 | These are the default registers that sty provides out of the box. 3 | """ 4 | from . import renderfunc 5 | from .primitive import Register, Style 6 | from .rendertype import * 7 | 8 | 9 | class EfRegister(Register): 10 | 11 | def __init__(self): 12 | 13 | super().__init__() 14 | 15 | self.renderfuncs[Sgr] = renderfunc.sgr 16 | 17 | self.b = Style(Sgr(1)) 18 | self.bold = Style(Sgr(1)) 19 | self.dim = Style(Sgr(2)) 20 | self.i = Style(Sgr(3)) 21 | self.italic = Style(Sgr(3)) 22 | self.u = Style(Sgr(4)) 23 | self.underl = Style(Sgr(4)) 24 | self.blink = Style(Sgr(5)) 25 | self.inverse = Style(Sgr(7)) 26 | self.hidden = Style(Sgr(8)) 27 | self.strike = Style(Sgr(9)) 28 | 29 | 30 | class FgRegister(Register): 31 | 32 | def __init__(self): 33 | 34 | super().__init__() 35 | 36 | self.renderfuncs[Sgr] = renderfunc.sgr 37 | self.renderfuncs[EightbitFg] = renderfunc.eightbit_fg 38 | self.renderfuncs[RgbFg] = renderfunc.rgb_fg 39 | 40 | self.set_eightbit_call(EightbitFg) 41 | self.set_rgb_call(RgbFg) 42 | 43 | # Classic terminal foreground color preset. 44 | # These are well supported. 45 | self.black = Style(Sgr(30)) 46 | self.red = Style(Sgr(31)) 47 | self.green = Style(Sgr(32)) 48 | self.yellow = Style(Sgr(33)) 49 | self.blue = Style(Sgr(34)) 50 | self.magenta = Style(Sgr(35)) 51 | self.cyan = Style(Sgr(36)) 52 | self.white = Style(Sgr(37)) 53 | 54 | self.rs = Style(Sgr(39)) 55 | 56 | # These are less good supported. 57 | self.li_black = Style(Sgr(90)) 58 | self.li_red = Style(Sgr(91)) 59 | self.li_green = Style(Sgr(92)) 60 | self.li_yellow = Style(Sgr(93)) 61 | self.li_blue = Style(Sgr(94)) 62 | self.li_magenta = Style(Sgr(95)) 63 | self.li_cyan = Style(Sgr(96)) 64 | self.li_white = Style(Sgr(97)) 65 | 66 | # These are less supported. 67 | self.da_black = Style(EightbitFg(0)) 68 | self.da_red = Style(EightbitFg(88)) 69 | self.da_green = Style(EightbitFg(22)) 70 | self.da_yellow = Style(EightbitFg(58)) 71 | self.da_blue = Style(EightbitFg(18)) 72 | self.da_magenta = Style(EightbitFg(89)) 73 | self.da_cyan = Style(EightbitFg(23)) 74 | self.da_white = Style(EightbitFg(249)) 75 | 76 | 77 | class BgRegister(Register): 78 | 79 | def __init__(self): 80 | 81 | super().__init__() 82 | 83 | self.renderfuncs[Sgr] = renderfunc.sgr 84 | self.renderfuncs[EightbitBg] = renderfunc.eightbit_bg 85 | self.renderfuncs[RgbBg] = renderfunc.rgb_bg 86 | 87 | self.set_eightbit_call(EightbitBg) 88 | self.set_rgb_call(RgbBg) 89 | 90 | # Classic terminal background color preset. 91 | # These are well supported. 92 | self.black = Style(Sgr(40)) 93 | self.red = Style(Sgr(41)) 94 | self.green = Style(Sgr(42)) 95 | self.yellow = Style(Sgr(43)) 96 | self.blue = Style(Sgr(44)) 97 | self.magenta = Style(Sgr(45)) 98 | self.cyan = Style(Sgr(46)) 99 | self.white = Style(Sgr(47)) 100 | 101 | self.rs = Style(Sgr(49)) 102 | 103 | # These are less good supported. 104 | self.li_black = Style(Sgr(100)) 105 | self.li_red = Style(Sgr(101)) 106 | self.li_green = Style(Sgr(102)) 107 | self.li_yellow = Style(Sgr(103)) 108 | self.li_blue = Style(Sgr(104)) 109 | self.li_magenta = Style(Sgr(105)) 110 | self.li_cyan = Style(Sgr(106)) 111 | self.li_white = Style(Sgr(107)) 112 | 113 | # These are less supported. 114 | self.da_black = Style(EightbitBg(0)) 115 | self.da_red = Style(EightbitBg(88)) 116 | self.da_green = Style(EightbitBg(22)) 117 | self.da_yellow = Style(EightbitBg(58)) 118 | self.da_blue = Style(EightbitBg(18)) 119 | self.da_magenta = Style(EightbitBg(89)) 120 | self.da_cyan = Style(EightbitBg(23)) 121 | self.da_white = Style(EightbitBg(249)) 122 | 123 | 124 | class RsRegister(Register): 125 | 126 | def __init__(self): 127 | 128 | super().__init__() 129 | 130 | self.renderfuncs[Sgr] = renderfunc.sgr 131 | 132 | self.all = Style(Sgr(0)) 133 | self.fg = Style(Sgr(39)) 134 | self.bg = Style(Sgr(49)) 135 | 136 | self.bold_dim = Style(Sgr(22)) 137 | self.dim_bold = Style(Sgr(22)) 138 | self.i = Style(Sgr(23)) 139 | self.italic = Style(Sgr(23)) 140 | self.u = Style(Sgr(24)) 141 | self.underl = Style(Sgr(24)) 142 | self.blink = Style(Sgr(25)) 143 | self.inverse = Style(Sgr(27)) 144 | self.hidden = Style(Sgr(28)) 145 | self.strike = Style(Sgr(29)) 146 | 147 | 148 | ef = EfRegister() 149 | fg = FgRegister() 150 | bg = BgRegister() 151 | rs = RsRegister() 152 | 153 | -------------------------------------------------------------------------------- /sti/renderfunc.py: -------------------------------------------------------------------------------- 1 | """ 2 | A selection of render functions. 3 | 4 | These functions generate the escape-sequences that trigger certain colors/effects in 5 | the terminals. 6 | """ 7 | 8 | 9 | def sgr(num: int) -> str: 10 | return '\033[' + str(num) + 'm' 11 | 12 | 13 | def eightbit_fg(num: int) -> str: 14 | return '\033[38;5;' + str(num) + 'm' 15 | 16 | 17 | def eightbit_bg(num: int) -> str: 18 | return '\033[48;5;' + str(num) + 'm' 19 | 20 | 21 | def rgb_fg(r: int, g: int, b: int) -> str: 22 | return '\x1b[38;2;' + str(r) + ';' + str(g) + ';' + str(b) + 'm' 23 | 24 | 25 | def rgb_bg(r: int, g: int, b: int) -> str: 26 | return '\x1b[48;2;' + str(r) + ';' + str(g) + ';' + str(b) + 'm' 27 | 28 | -------------------------------------------------------------------------------- /sti/rendertype.py: -------------------------------------------------------------------------------- 1 | """ 2 | """ 3 | 4 | 5 | class RenderType: 6 | args: list = [] 7 | 8 | 9 | class Sgr(RenderType): 10 | """ 11 | Define SGR styling rule. 12 | 13 | More info about SGR parameters: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR 14 | 15 | :param num: A SGR number. 16 | """ 17 | 18 | def __init__(self, num: int): 19 | self.args = [num] 20 | 21 | 22 | class EightbitFg(RenderType): 23 | """ 24 | Define Eightbit Foreground. 25 | 26 | More info about 8-bit terminal colors: https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit 27 | 28 | :param num: Eightbit number. 29 | """ 30 | 31 | def __init__(self, num: int): 32 | self.args = [num] 33 | 34 | 35 | class EightbitBg(RenderType): 36 | """ 37 | Define Eightbit Background. 38 | 39 | More info about 8-bit terminal colors: https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit 40 | 41 | :param num: Eightbit number. 42 | """ 43 | 44 | def __init__(self, num: int): 45 | self.args = [num] 46 | 47 | 48 | class RgbFg(RenderType): 49 | """ 50 | Define RGB Foreground. 51 | 52 | More info about 24-bit terminal colors: https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit 53 | 54 | :param r: Red. 55 | :param g: Green. 56 | :param b: Blue. 57 | """ 58 | 59 | def __init__(self, r: int, g: int, b: int): 60 | self.args = [r, g, b] 61 | 62 | 63 | class RgbBg(RenderType): 64 | """ 65 | Define RGB Background. 66 | 67 | More info about 24-bit terminal colors: https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit 68 | 69 | :param r: Red. 70 | :param g: Green. 71 | :param b: Blue. 72 | """ 73 | 74 | def __init__(self, r: int, g: int, b: int): 75 | self.args = [r, g, b] 76 | 77 | -------------------------------------------------------------------------------- /sti/sti/__init__.py: -------------------------------------------------------------------------------- 1 | from .primitive import Register, Style 2 | from .register import ef, fg, bg, rs, EfRegister, FgRegister, BgRegister, RsRegister 3 | from .rendertype import * 4 | from .lib import * 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sti/sti/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerLink04/LinkScanner/501be078f49b8529c6a6aa42293ef022598b2da1/sti/sti/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /sti/sti/__pycache__/lib.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerLink04/LinkScanner/501be078f49b8529c6a6aa42293ef022598b2da1/sti/sti/__pycache__/lib.cpython-37.pyc -------------------------------------------------------------------------------- /sti/sti/__pycache__/primitive.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerLink04/LinkScanner/501be078f49b8529c6a6aa42293ef022598b2da1/sti/sti/__pycache__/primitive.cpython-37.pyc -------------------------------------------------------------------------------- /sti/sti/__pycache__/register.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerLink04/LinkScanner/501be078f49b8529c6a6aa42293ef022598b2da1/sti/sti/__pycache__/register.cpython-37.pyc -------------------------------------------------------------------------------- /sti/sti/__pycache__/renderfunc.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerLink04/LinkScanner/501be078f49b8529c6a6aa42293ef022598b2da1/sti/sti/__pycache__/renderfunc.cpython-37.pyc -------------------------------------------------------------------------------- /sti/sti/__pycache__/rendertype.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SerLink04/LinkScanner/501be078f49b8529c6a6aa42293ef022598b2da1/sti/sti/__pycache__/rendertype.cpython-37.pyc -------------------------------------------------------------------------------- /sti/sti/lib.py: -------------------------------------------------------------------------------- 1 | from .primitive import Register 2 | 3 | 4 | def mute(*objects: Register) -> None: 5 | """ 6 | Use this function to mute multiple register-objects at once. 7 | 8 | :param objects: Pass multiple register-objects to the function. 9 | """ 10 | err = ValueError( 11 | "The mute() method can only be used with objects that inherit "\ 12 | "from the 'Register class'." 13 | ) 14 | for obj in objects: 15 | if not isinstance(obj, Register): 16 | raise err 17 | obj.mute() 18 | 19 | 20 | def unmute(*objects: Register) -> None: 21 | """ 22 | Use this function to unmute multiple register-objects at once. 23 | 24 | :param objects: Pass multiple register-objects to the function. 25 | """ 26 | err = ValueError( 27 | "The unmute() method can only be used with objects that inherit "\ 28 | "from the 'Register class'." 29 | ) 30 | for obj in objects: 31 | if not isinstance(obj, Register): 32 | raise err 33 | obj.unmute() 34 | 35 | -------------------------------------------------------------------------------- /sti/sti/primitive.py: -------------------------------------------------------------------------------- 1 | """ 2 | The Register class: Sty's heart. 3 | """ 4 | from collections import namedtuple 5 | from typing import Union, Callable, Dict, List, Tuple, Iterable 6 | from copy import deepcopy 7 | from .rendertype import RenderType 8 | 9 | 10 | class Style(str): 11 | """ 12 | This type stores the different styling rules for the registers and the resulting 13 | ANSI-sequence as a string. 14 | 15 | For example: 16 | 17 | fg.orange = Style(RgbFg(1,5,10), Sgr(1)) 18 | 19 | isinstance(fg.orange, Style) # True 20 | 21 | isinstance(fg.orange, str) # True 22 | 23 | str(fg.orange) # '\x1b[38;2;1;5;10m\x1b[1m' (The ASNI sequence for orange and bold) 24 | 25 | """ 26 | 27 | def __new__(cls, *rules, value=''): 28 | cls = str.__new__(cls, value) 29 | cls.rules = rules 30 | return cls 31 | 32 | 33 | def _render_rules( 34 | renderfuncs, 35 | rules, 36 | ) -> Tuple[str, List[RenderType]]: 37 | 38 | rendered: str = '' 39 | flattened_rules = [] 40 | 41 | if isinstance(rules, RenderType): 42 | f1: Callable = renderfuncs[type(rules)] 43 | rendered += f1(*rules.args) 44 | flattened_rules.append(rules) 45 | 46 | elif isinstance(rules, Style): 47 | r1, r2 = _render_rules(renderfuncs, rules.rules) 48 | rendered += r1 49 | flattened_rules.extend(r2) 50 | 51 | elif isinstance(rules, (list, tuple)): 52 | 53 | for rule in rules: 54 | 55 | r1, r2 = _render_rules(renderfuncs, rule) 56 | rendered += r1 57 | flattened_rules.extend(r2) 58 | 59 | else: 60 | raise ValueError("Parameter 'rules' must be of type Rule or Tuple[Rule].") 61 | 62 | return rendered, flattened_rules 63 | 64 | 65 | class Register: 66 | """ 67 | This is the base Register class. All default registers (fg, bg, ef, rs) are 68 | created from this class. You can use it to create your own custom registers. 69 | """ 70 | 71 | def __init__(self): 72 | self.renderfuncs: Dict[RenderType, Callable] = {} 73 | self.is_muted = False 74 | self.eightbit_call = lambda x: x 75 | self.rgb_call = lambda r, g, b: (r, g, b) 76 | 77 | def __setattr__(self, name: str, value: Union[Style, Tuple[Style]]): 78 | 79 | if isinstance(value, Style): 80 | 81 | if self.is_muted: 82 | rendered_style = Style(value.rules, value='') 83 | else: 84 | rendered, rules = _render_rules(self.renderfuncs, value.rules) 85 | rendered_style = Style(rules, value=rendered) 86 | 87 | return super().__setattr__(name, rendered_style) 88 | else: 89 | return super().__setattr__(name, value) 90 | 91 | def __call__(self, *args, **kwargs) -> str: 92 | """ 93 | This function is to handle calls such as `fg(42)`, `bg(102, 49, 42)`, `fg('red')`. 94 | """ 95 | 96 | # # Return empty str if object is muted. 97 | if self.is_muted: 98 | return '' 99 | 100 | len_args = len(args) 101 | 102 | if len_args == 1: 103 | 104 | # If input is an 8bit color code, run 8bit render function. 105 | if isinstance(args[0], int): 106 | return self.eightbit_call(*args, **kwargs) 107 | 108 | # If input is a string, return attribute with the name that matches 109 | # input. 110 | else: 111 | return getattr(self, args[0]) 112 | 113 | # If input is an 24bit color code, run 24bit render function. 114 | elif len_args == 3: 115 | return self.rgb_call(*args, **kwargs) 116 | 117 | else: 118 | return '' 119 | 120 | def set_style( 121 | self, 122 | name: str, 123 | *rules: Union[RenderType, Tuple[RenderType, ...]], 124 | ) -> None: 125 | """ 126 | DEPRECATED: This method will be removed in favour of the Style() type. 127 | 128 | With this method, you can add or change styles for a register-object. 129 | 130 | :param name: The field name for the new style. 131 | """ 132 | setattr(self, name, Style(*rules)) 133 | 134 | def get_style( 135 | self, 136 | name: str, 137 | ) -> Tuple[RenderType, ...]: 138 | """ 139 | DEPRECATED: This method will be removed in favour of the Style() type. 140 | 141 | Retrieve styling rules from a register-object. 142 | This is useful in case you want to compose new styles out of existing styles. 143 | 144 | :param name: The name of the style for which you want to retrieve the styling rules. 145 | """ 146 | return getattr(self, name).rules 147 | 148 | def set_eightbit_call(self, rendertype: RenderType) -> None: 149 | """ 150 | You can call a register-object directly. A call like this ``fg(144)`` 151 | is a Eightbit-call. With this method you can define the render-type for such calls. 152 | 153 | :param rendertype: The new rendertype that is used for Eightbit-calls. 154 | """ 155 | func: Callable = self.renderfuncs[rendertype] 156 | self.eightbit_call = func 157 | 158 | def set_rgb_call(self, rendertype: RenderType) -> None: 159 | """ 160 | You can call a register-object directly. A call like this ``fg(10, 42, 255)`` 161 | is a RGB-call. With this method you can define the render-type for such calls. 162 | 163 | :param rendertype: The new rendertype that is used for RGB-calls. 164 | """ 165 | func: Callable = self.renderfuncs[rendertype] 166 | self.rgb_call = func 167 | 168 | def set_renderfunc(self, rendertype: RenderType, func: Callable) -> None: 169 | """ 170 | With this method you can add or replace render-functions for a given register-object: 171 | 172 | :param rendertype: The render type for which the new renderfunc is used. 173 | :param func: The new render function. 174 | """ 175 | # Save new render-func in register 176 | self.renderfuncs.update({rendertype: func}) 177 | 178 | # Update style atributes and styles with the new renderfunc. 179 | for attr_name in dir(self): 180 | val = getattr(self, attr_name) 181 | if isinstance(val, Style): 182 | setattr(self, attr_name, val) 183 | 184 | def mute(self): 185 | """ 186 | Sometimes it is useful to disable the formatting for a register-object. You can 187 | do so by invoking this method. 188 | """ 189 | self.is_muted = True 190 | 191 | for attr_name in dir(self): 192 | val = getattr(self, attr_name) 193 | if isinstance(val, Style): 194 | setattr(self, attr_name, val) 195 | 196 | def unmute(self): 197 | """ 198 | Use this method to unmute a previously muted register object. 199 | """ 200 | self.is_muted = False 201 | 202 | for attr_name in dir(self): 203 | val = getattr(self, attr_name) 204 | if isinstance(val, Style): 205 | setattr(self, attr_name, val) 206 | 207 | def as_dict(self) -> Dict[str, str]: 208 | """ 209 | Export color register as dict. 210 | """ 211 | items: Dict[str, str] = {} 212 | 213 | for name in dir(self): 214 | 215 | if not name.startswith("_") and\ 216 | isinstance(getattr(self, name), str): 217 | 218 | items.update({name: str(getattr(self, name))}) 219 | 220 | return items 221 | 222 | def as_namedtuple(self): 223 | """ 224 | Export color register as namedtuple. 225 | """ 226 | d = self.as_dict() 227 | return namedtuple('StyleRegister', d.keys())(*d.values()) 228 | 229 | def copy(self): 230 | """ 231 | Make a deepcopy of a register-object. 232 | """ 233 | return deepcopy(self) 234 | 235 | -------------------------------------------------------------------------------- /sti/sti/register.py: -------------------------------------------------------------------------------- 1 | """ 2 | These are the default registers that sty provides out of the box. 3 | """ 4 | from . import renderfunc 5 | from .primitive import Register, Style 6 | from .rendertype import * 7 | 8 | 9 | class EfRegister(Register): 10 | 11 | def __init__(self): 12 | 13 | super().__init__() 14 | 15 | self.renderfuncs[Sgr] = renderfunc.sgr 16 | 17 | self.b = Style(Sgr(1)) 18 | self.bold = Style(Sgr(1)) 19 | self.dim = Style(Sgr(2)) 20 | self.i = Style(Sgr(3)) 21 | self.italic = Style(Sgr(3)) 22 | self.u = Style(Sgr(4)) 23 | self.underl = Style(Sgr(4)) 24 | self.blink = Style(Sgr(5)) 25 | self.inverse = Style(Sgr(7)) 26 | self.hidden = Style(Sgr(8)) 27 | self.strike = Style(Sgr(9)) 28 | 29 | 30 | class FgRegister(Register): 31 | 32 | def __init__(self): 33 | 34 | super().__init__() 35 | 36 | self.renderfuncs[Sgr] = renderfunc.sgr 37 | self.renderfuncs[EightbitFg] = renderfunc.eightbit_fg 38 | self.renderfuncs[RgbFg] = renderfunc.rgb_fg 39 | 40 | self.set_eightbit_call(EightbitFg) 41 | self.set_rgb_call(RgbFg) 42 | 43 | # Classic terminal foreground color preset. 44 | # These are well supported. 45 | self.black = Style(Sgr(30)) 46 | self.red = Style(Sgr(31)) 47 | self.green = Style(Sgr(32)) 48 | self.yellow = Style(Sgr(33)) 49 | self.blue = Style(Sgr(34)) 50 | self.magenta = Style(Sgr(35)) 51 | self.cyan = Style(Sgr(36)) 52 | self.white = Style(Sgr(37)) 53 | 54 | self.rs = Style(Sgr(39)) 55 | 56 | # These are less good supported. 57 | self.li_black = Style(Sgr(90)) 58 | self.li_red = Style(Sgr(91)) 59 | self.li_green = Style(Sgr(92)) 60 | self.li_yellow = Style(Sgr(93)) 61 | self.li_blue = Style(Sgr(94)) 62 | self.li_magenta = Style(Sgr(95)) 63 | self.li_cyan = Style(Sgr(96)) 64 | self.li_white = Style(Sgr(97)) 65 | 66 | # These are less supported. 67 | self.da_black = Style(EightbitFg(0)) 68 | self.da_red = Style(EightbitFg(88)) 69 | self.da_green = Style(EightbitFg(22)) 70 | self.da_yellow = Style(EightbitFg(58)) 71 | self.da_blue = Style(EightbitFg(18)) 72 | self.da_magenta = Style(EightbitFg(89)) 73 | self.da_cyan = Style(EightbitFg(23)) 74 | self.da_white = Style(EightbitFg(249)) 75 | 76 | 77 | class BgRegister(Register): 78 | 79 | def __init__(self): 80 | 81 | super().__init__() 82 | 83 | self.renderfuncs[Sgr] = renderfunc.sgr 84 | self.renderfuncs[EightbitBg] = renderfunc.eightbit_bg 85 | self.renderfuncs[RgbBg] = renderfunc.rgb_bg 86 | 87 | self.set_eightbit_call(EightbitBg) 88 | self.set_rgb_call(RgbBg) 89 | 90 | # Classic terminal background color preset. 91 | # These are well supported. 92 | self.black = Style(Sgr(40)) 93 | self.red = Style(Sgr(41)) 94 | self.green = Style(Sgr(42)) 95 | self.yellow = Style(Sgr(43)) 96 | self.blue = Style(Sgr(44)) 97 | self.magenta = Style(Sgr(45)) 98 | self.cyan = Style(Sgr(46)) 99 | self.white = Style(Sgr(47)) 100 | 101 | self.rs = Style(Sgr(49)) 102 | 103 | # These are less good supported. 104 | self.li_black = Style(Sgr(100)) 105 | self.li_red = Style(Sgr(101)) 106 | self.li_green = Style(Sgr(102)) 107 | self.li_yellow = Style(Sgr(103)) 108 | self.li_blue = Style(Sgr(104)) 109 | self.li_magenta = Style(Sgr(105)) 110 | self.li_cyan = Style(Sgr(106)) 111 | self.li_white = Style(Sgr(107)) 112 | 113 | # These are less supported. 114 | self.da_black = Style(EightbitBg(0)) 115 | self.da_red = Style(EightbitBg(88)) 116 | self.da_green = Style(EightbitBg(22)) 117 | self.da_yellow = Style(EightbitBg(58)) 118 | self.da_blue = Style(EightbitBg(18)) 119 | self.da_magenta = Style(EightbitBg(89)) 120 | self.da_cyan = Style(EightbitBg(23)) 121 | self.da_white = Style(EightbitBg(249)) 122 | 123 | 124 | class RsRegister(Register): 125 | 126 | def __init__(self): 127 | 128 | super().__init__() 129 | 130 | self.renderfuncs[Sgr] = renderfunc.sgr 131 | 132 | self.all = Style(Sgr(0)) 133 | self.fg = Style(Sgr(39)) 134 | self.bg = Style(Sgr(49)) 135 | 136 | self.bold_dim = Style(Sgr(22)) 137 | self.dim_bold = Style(Sgr(22)) 138 | self.i = Style(Sgr(23)) 139 | self.italic = Style(Sgr(23)) 140 | self.u = Style(Sgr(24)) 141 | self.underl = Style(Sgr(24)) 142 | self.blink = Style(Sgr(25)) 143 | self.inverse = Style(Sgr(27)) 144 | self.hidden = Style(Sgr(28)) 145 | self.strike = Style(Sgr(29)) 146 | 147 | 148 | ef = EfRegister() 149 | fg = FgRegister() 150 | bg = BgRegister() 151 | rs = RsRegister() 152 | 153 | -------------------------------------------------------------------------------- /sti/sti/renderfunc.py: -------------------------------------------------------------------------------- 1 | """ 2 | A selection of render functions. 3 | 4 | These functions generate the escape-sequences that trigger certain colors/effects in 5 | the terminals. 6 | """ 7 | 8 | 9 | def sgr(num: int) -> str: 10 | return '\033[' + str(num) + 'm' 11 | 12 | 13 | def eightbit_fg(num: int) -> str: 14 | return '\033[38;5;' + str(num) + 'm' 15 | 16 | 17 | def eightbit_bg(num: int) -> str: 18 | return '\033[48;5;' + str(num) + 'm' 19 | 20 | 21 | def rgb_fg(r: int, g: int, b: int) -> str: 22 | return '\x1b[38;2;' + str(r) + ';' + str(g) + ';' + str(b) + 'm' 23 | 24 | 25 | def rgb_bg(r: int, g: int, b: int) -> str: 26 | return '\x1b[48;2;' + str(r) + ';' + str(g) + ';' + str(b) + 'm' 27 | 28 | -------------------------------------------------------------------------------- /sti/sti/rendertype.py: -------------------------------------------------------------------------------- 1 | """ 2 | """ 3 | 4 | 5 | class RenderType: 6 | args: list = [] 7 | 8 | 9 | class Sgr(RenderType): 10 | """ 11 | Define SGR styling rule. 12 | 13 | More info about SGR parameters: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR 14 | 15 | :param num: A SGR number. 16 | """ 17 | 18 | def __init__(self, num: int): 19 | self.args = [num] 20 | 21 | 22 | class EightbitFg(RenderType): 23 | """ 24 | Define Eightbit Foreground. 25 | 26 | More info about 8-bit terminal colors: https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit 27 | 28 | :param num: Eightbit number. 29 | """ 30 | 31 | def __init__(self, num: int): 32 | self.args = [num] 33 | 34 | 35 | class EightbitBg(RenderType): 36 | """ 37 | Define Eightbit Background. 38 | 39 | More info about 8-bit terminal colors: https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit 40 | 41 | :param num: Eightbit number. 42 | """ 43 | 44 | def __init__(self, num: int): 45 | self.args = [num] 46 | 47 | 48 | class RgbFg(RenderType): 49 | """ 50 | Define RGB Foreground. 51 | 52 | More info about 24-bit terminal colors: https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit 53 | 54 | :param r: Red. 55 | :param g: Green. 56 | :param b: Blue. 57 | """ 58 | 59 | def __init__(self, r: int, g: int, b: int): 60 | self.args = [r, g, b] 61 | 62 | 63 | class RgbBg(RenderType): 64 | """ 65 | Define RGB Background. 66 | 67 | More info about 24-bit terminal colors: https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit 68 | 69 | :param r: Red. 70 | :param g: Green. 71 | :param b: Blue. 72 | """ 73 | 74 | def __init__(self, r: int, g: int, b: int): 75 | self.args = [r, g, b] 76 | 77 | --------------------------------------------------------------------------------