0% found this document useful (0 votes)
7 views147 pages

DNS & Port Scanner Tool Script

The document contains two Python scripts: GRABBER.PY, which performs DNS and port scanning, and JTR.PY, which is a password recovery tool for ZIP files. GRABBER.PY includes functionalities for domain cleaning, Nmap scanning, and analyzing steganography, while JTR.PY focuses on brute-force password cracking and archive analysis. Both scripts utilize various libraries and handle errors gracefully, providing informative outputs and logging capabilities.

Uploaded by

mwjuan2214
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views147 pages

DNS & Port Scanner Tool Script

The document contains two Python scripts: GRABBER.PY, which performs DNS and port scanning, and JTR.PY, which is a password recovery tool for ZIP files. GRABBER.PY includes functionalities for domain cleaning, Nmap scanning, and analyzing steganography, while JTR.PY focuses on brute-force password cracking and archive analysis. Both scripts utilize various libraries and handle errors gracefully, providing informative outputs and logging capabilities.

Uploaded by

mwjuan2214
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd

GRABBER.

PY

import subprocess
import re
import sys
import json
import time
import base64
from datetime import datetime

# --- KONFIGURASI WARNA ---


class Colors:
HEADER = '\033[95m'
BLUE = '\033[94m'
CYAN = '\033[96m'
GREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'

def print_banner():
print([Link] + [Link] + """
╔══════════════════════════════════════════════════╗
║ ULTIMATE DNS & PORT HUNTER ║
║ Clean URL | Dig | Stegano | GeoIP | Nmap ║
╚══════════════════════════════════════════════════╝
""" + [Link])

# --- MODUL PEMBERSIH DOMAIN ---


def clean_domain(url):
domain = [Link](r'^https?://', '', url)
domain = [Link]('/')[0]
domain = [Link](':')[0]
return [Link]()

# --- MODUL NMAP (PORT SCANNER) ---


def run_nmap(target, log_file=None):
"""Menjalankan Nmap untuk mendeteksi Port yang terbuka"""
print([Link] + f"\n[*] Memulai Nmap Scan pada: {target}..." +
[Link])
print([Link] + " (Ini mungkin memakan waktu beberapa detik...)" +
[Link])

try:
# Perintah Nmap: -F (Fast scan/100 port populer), -sV (Cek versi layanan)
cmd = ['nmap', '-F', '-sV', '--open', target]
nmap_output = subprocess.check_output(cmd, encoding='utf-8',
stderr=[Link])

if nmap_output:
result_text = f"\n{[Link]}[!] HASIL NMAP SCAN (Port Terbuka):
{[Link]}\n{nmap_output}"
print(result_text)
if log_file:
log_file.write("\n--- NMAP SCAN RESULT ---\n")
log_file.write(nmap_output + "\n")
else:
print([Link] + " [-] Nmap tidak menemukan port terbuka." +
[Link])

except FileNotFoundError:
print([Link] + " [!] Error: Nmap belum terinstall di sistem kamu."
+ [Link])
except Exception as e:
print([Link] + f" [!] Gagal menjalankan Nmap: {e}" + [Link])

# --- MODUL STEGANOGRAFI ---


def analyze_steganography(text):
results = []
clean_text = [Link]('"', '').strip()
if len(clean_text) > 8:
try:
if [Link](r'^[A-Za-z0-9+/]+={0,2}$', clean_text):
decoded = base64.b64decode(clean_text).decode('utf-8',
errors='ignore')
if any([Link]() for c in decoded) and len(decoded) > 3:
[Link](f"[!] Terdeteksi Base64: {decoded}")
except: pass
try:
if all(c in '0123456789abcdefABCDEF' for c in clean_text):
decoded_hex = [Link](clean_text).decode('utf-8',
errors='ignore')
if len(decoded_hex) > 3:
[Link](f"[!] Terdeteksi Hex: {decoded_hex}")
except: pass
return results

def run_command(command_list):
try:
return subprocess.check_output(command_list, encoding='utf-8',
stderr=[Link]).strip()
except: return None

def get_ip_details(ip):
try:
cmd = ['curl', '-s', f'[Link]
fields=status,country,city,isp']
response = run_command(cmd)
if response:
data = [Link](response)
if [Link]('status') == 'success':
return f"{[Link]('city')}, {[Link]('country')} | ISP:
{[Link]('isp')}"
except: pass
return "Lokasi tidak diketahui"

def recursive_grabber(domain, processed=None, log_file=None):


if processed is None: processed = set()
if domain in processed or not domain: return
[Link](domain)

header = f"\n{'='*60}\nGRABBING DNS: {domain}\n{'='*60}"


print([Link] + header + [Link])
if log_file: log_file.write(header + "\n")

record_types = ['A', 'CNAME', 'MX', 'TXT', 'NS']


found_cnames = []

for r_type in record_types:


raw_output = run_command(['dig', domain, r_type, '+noall', '+answer'])
if raw_output:
print([Link] + f"[+] {r_type} RECORDS:" + [Link])
print(raw_output)
if log_file: log_file.write(f"[+] {r_type} RECORDS:\n{raw_output}\n")

if r_type == 'A':
ips = [Link](r'\b(?:\d{1,3}\.){3}\d{1,3}\b', raw_output)
for ip in set(ips):
info = get_ip_details(ip)
print([Link] + f" -> INFO IP {ip}: {info}" +
[Link])

if r_type == 'TXT':
content = [Link](r'"([^"]*)"', raw_output)
for text in content:
for found in analyze_steganography(text):
print([Link] + [Link] + f" {found}" +
[Link])

if r_type == 'CNAME':
for line in raw_output.splitlines():
parts = [Link]()
if parts: found_cnames.append(parts[-1].rstrip('.'))

# Auto-Follow CNAME
for next_domain in found_cnames:
if next_domain not in processed:
print([Link] + f"\n[!] Melacak CNAME ke: {next_domain}" +
[Link])
recursive_grabber(next_domain, processed, log_file)

def main():
print_banner()
raw_input = input([Link] + "Masukkan Domain Link: " + [Link]).strip()
if not raw_input: return

target = clean_domain(raw_input)
print([Link] + f"[*] Target Teridentifikasi: {target}" + [Link])

filename = f"recon_{[Link]('.','_')}.txt"
with open(filename, "w") as f:
[Link](f"FULL RECON REPORT: {target}\nGenerated: {[Link]()}\n")

# 1. Jalankan DNS Discovery


recursive_grabber(target, log_file=f)

# 2. Jalankan Nmap (Fitur Ganas Baru)


run_nmap(target, log_file=f)
print(f"\n{[Link]}Selesai! Report lengkap: {filename}{[Link]}")

if __name__ == "__main__":
try: main()
except KeyboardInterrupt: print("\n[!] Dihentikan.")

[Link]

import re
import base64
import hashlib
import time
import os
import sys
import itertools
import subprocess
import json
import signal
import pickle
import random
import shutil
import zipfile
from shlex import quote
from datetime import datetime, timedelta
from typing import Dict, List, Iterable, Optional, Tuple

# ==============================================================================
# SEKTOR 01: TERMINAL_CTL_INFERENCE
# ==============================================================================
try:
from colorama import Fore, Style, init
init(autoreset=True)

CLEAR_LINE_TO_END = '\r\033[K'
CURSOR_UP_1 = '\033[1A'

except ImportError:
class DummyColor:
def getattr(self, name): return ""
Fore = DummyColor()
Style = DummyColor()
CLEAR_LINE_TO_END = '\r'
CURSOR_UP_1 = ''

CLEAR_LINE = '\r' + ' ' * 200 + '\r'

# ==============================================================================
# SEKTOR 02: CORE_CONFIG_DEPLOYMENT (HYPER-TUNED V41)
# ==============================================================================
CPU_COUNT = os.cpu_count() or 4
CONFIG = {
"MAX_PPL": 8,
"SESSION_NAME": "JTR_BFA_SESSION",
# [TUNING V41] Log diperlambat agar CPU tidak sibuk menulis file status
"LOG_INTERVAL_WORKER": 3.0,
"LOG_INTERVAL_MONITOR": 0.5,
"FILES": {
"KEY_TMP": "FOUND_KEY.tmp", "TIME_TMP": "[Link]",
"LOCK": "TERMINAL_LOCK.lock",
"REPORT": "CRACK_REPORT.txt", "STATUS_PREFIX": "STATUS_P", "STATUS_SUFFIX":
".tmp"
},
"WEBHOOK_URL": "",
"MAX_CORES": CPU_COUNT,
# [TUNING V41] Batch dinaikkan ke 3000 untuk mengurangi "File Open Overhead"
"BATCH_SIZE_CRACK": 3000,
}

# --- CHARSETS (97 Characters) ---


CHARSETS = {
"NUMBERS": "0123456789",
"UPPER": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"LOWER": "abcdefghijklmnopqrstuvwxyz",
"SYMBOLS": "!@#$%^&*()-_+=[]{}|;:,.<>?/~`'\"\\ "
}
GLOBAL_CHARSET = (
CHARSETS["NUMBERS"] +
CHARSETS["UPPER"] +
CHARSETS["LOWER"] +
CHARSETS["SYMBOLS"]
)
# --------------------------------------------------------

WORKER_ID_SIGNAL = 0
IS_SIGNALED = False
TOTAL_WORKERS_GLOBAL = CPU_COUNT
CURRENT_ACTIVE_WORKERS = CPU_COUNT

# ==============================================================================
# SEKTOR 03: SYNC_DATA_HANDLER
# ==============================================================================

def signal_handler(sig, frame):


global IS_SIGNALED
IS_SIGNALED = True

def format_time(seconds: float) -> str:


try:
seconds = int(seconds)
hours = seconds // 3600
minutes = (seconds % 3600) // 60
secs = seconds % 60
return f"{hours:02d}:{minutes:02d}:{secs:02d}"
except:
return "99:99:99"

def check_found_key():
key_path = CONFIG["FILES"]["KEY_TMP"]
time_path = CONFIG["FILES"]["TIME_TMP"]
key = None; elapsed = None
if [Link](key_path):
try:
with open(key_path, 'r') as f: key = [Link]().strip()
if [Link](time_path):
with open(time_path, 'r') as f: elapsed = [Link]().strip()
try:
[Link](key_path)
if [Link](time_path): [Link](time_path)
except Exception: pass
return key, elapsed
except Exception: pass
return None, None

def write_found_key(key: str, elapsed: str):


key_path = CONFIG["FILES"]["KEY_TMP"]
time_path = CONFIG["FILES"]["TIME_TMP"]
report_path = CONFIG["FILES"]["REPORT"]
try:
with open(key_path, 'w') as f: [Link](key)
with open(time_path, 'w') as f: [Link](elapsed)
except Exception: pass
report = (
f"\n========================================\n"
f" JTR KEY RECOVERY REPORT\n"
f"========================================\n"
f"PASSWORD_FOUND : {key}\n"
f"ELAPSED_TIME : {elapsed}\n"
f"TIMESTAMP : {[Link]().strftime('%Y-%m-%d %H:%M:%S')}\n"
f"========================================\n"
)
with open(report_path, 'w') as f: [Link](report)

def update_worker_status_file(worker_index: int, status_data: Dict):


status_file = f"{CONFIG['FILES']['STATUS_PREFIX']}{worker_index}
{CONFIG['FILES']['STATUS_SUFFIX']}"
try:
with open(status_file, 'w') as f: [Link](status_data, f)
except Exception: pass

def get_worker_status(worker_index: int) -> Dict:


status_file = f"{CONFIG['FILES']['STATUS_PREFIX']}{worker_index}
{CONFIG['FILES']['STATUS_SUFFIX']}"
default_status = {
"time": "00:00:00", "speed": "0 p/s", "pwd": "INIT...",
"ppl": 0, "tested_count": 0, "ts": 0
}
if [Link](status_file):
try:
with open(status_file, 'r') as f: data = [Link](f)
# Worker dianggap 'active' jika status file diperbarui dalam 10 detik
(karena log interval diperlambat)
if [Link]() - [Link]("ts", 0) < 10: return data
except Exception: pass
return default_status

# ==============================================================================
# SEKTOR 04: ARCHIVE_ANALYSIS_ENGINE (V41 OPTIMIZED)
# ==============================================================================

def analyze_archive(path: str) -> Dict:


meta = {"MD5": "N/A", "Size": "0 B", "Enc": "Unknown", "Files": "0"}
try:
h = hashlib.md5()
with open(path, "rb") as f:
for c in iter(lambda: [Link](4096), b""): [Link](c)
meta["MD5"] = [Link]()
meta["Size"] = f"{[Link](path):,} Bytes"
except: pass

try:
with [Link](path, 'r') as zf:
pass
meta["Enc"] = f"{[Link]}ENCRYPTED (ZIP){Style.RESET_ALL}"
except [Link]:
meta["Enc"] = f"{[Link]}ERROR / CORRUPT ARCHIVE{Style.RESET_ALL}"
except Exception:
meta["Enc"] = f"{[Link]}ENCRYPTED (ZIP){Style.RESET_ALL}"

return meta

def test_password_batch(path: str, passwords: List[str]) -> Tuple[bool,


Optional[str]]:
"""
V41 HYPER-TURBO:
1. Buka File Sekali.
2. Cache ZipInfo Object (bukan nama string) untuk akses memori lebih cepat.
3. Loop ketat tanpa overhead function call lain.
"""
try:
# Buka ZIP hanya SEKALI untuk 3000 password
with [Link](path) as zf:

# [OPTIMASI V41] Ambil ZipInfo object langsung.


# Menggunakan namelist() berulang-ulang membuang cycle CPU untuk
parsing header.
# infolist()[0] memberikan objek metadata yang siap pakai.
try:
target_info = [Link]()[0]
except IndexError:
return False, None

# Loop Kritis - Dibuat se-ringan mungkin


for pwd in passwords:
try:
# Set password (operasi memori)
[Link]([Link]('utf-8'))
# Read menggunakan Info Object (bypass lookup nama file)
[Link](target_info)

# Jika lolos tanpa error -> SUCCESS


return True, pwd

except (RuntimeError, [Link]):


# Password Salah (Jalur Paling Sering Dilewati - Harus Cepat)
continue
except Exception:
continue

except Exception:
return False, None

return False, None

def gen_brute(ppl, fppc, charset, start, step, total):


"""
Generator Brute Force Efisien.
"""
rem_len = ppl - 1
if rem_len > 0:
charset_len = len(charset)
for i in range(start, total, step):
curr = ""
temp = i
for _ in range(rem_len):
curr = charset[temp % charset_len] + curr
temp //= charset_len
yield fppc + curr
elif ppl == 1 and start == 0 and fppc and len(fppc) == 1:
yield fppc

# ==============================================================================
# SEKTOR 05: POST_CRACK_EXTRACTION
# ==============================================================================

def extract_archive(path: str, pwd: str, dest_dir: str):

if not [Link](dest_dir):
[Link](dest_dir)

print(f"\n{[Link]}>> [PROCESS] Initiating Auto-Extraction Routine...


{Style.RESET_ALL}")
print(f"{[Link]}>> UTILITY:{Style.RESET_ALL} {[Link]}ZIPFILE (Python
Native){Style.RESET_ALL}")
print(f"{[Link]}>> OUTPUT_PATH:{Style.RESET_ALL} {dest_dir}")

try:
with [Link](path, 'r') as zf:
[Link]([Link]('utf-8'))
[Link](dest_dir)

print(f"{[Link]}[SUCCESS] Extraction Complete. Integrity Verified.


{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{[Link]}[FAILURE] Extraction Exception:{Style.RESET_ALL}
{str(e)}")
return False

# ==============================================================================
# SEKTOR 06: INTERFACE_OUTPUT_RENDER
# ==============================================================================

def slow_print_status(status_text: str, workers_count: int, total_speed: float,


total_tested: int, elapsed_seconds: float, current_ppl: int, max_length: int = 30):

total_delay = 0.2; padded_pwd = status_text.ljust(max_length); char_delay =


total_delay / max_length

if total_speed > 0:
kps = total_speed / 1000.0; speed_str = f"{kps:,.2f}"; unit_str = "K/S"
else:
speed_str = "0"; unit_str = "P/S"

elapsed_time_str = format_time(elapsed_seconds)

header = (
f"{[Link]}KEY_LEN {current_ppl}{Style.RESET_ALL} | "
f"{[Link]}SPEED ({unit_str}):{Style.RESET_ALL} {[Link]}{speed_str}
{Style.RESET_ALL} | "
f"{[Link]}TESTED_TOTAL:{Style.RESET_ALL} {[Link]}{total_tested:,}
{Style.RESET_ALL} "
f"(RUNTIME: {[Link]}{elapsed_time_str}{Style.RESET_ALL})"
)
key_test_prefix = f"{[Link]}KEY_TEST:{Style.RESET_ALL} "

[Link](CURSOR_UP_1 * 3)
[Link]()

[Link](CLEAR_LINE_TO_END)
[Link](header)
[Link]('\n')

[Link](CLEAR_LINE_TO_END)
[Link](key_test_prefix)

for i, char in enumerate(padded_pwd):


[Link](f"{[Link]}{[Link]}{char}{Style.RESET_ALL}")
[Link]()
[Link](char_delay)

[Link](' ' * (max_length + 10))


[Link]('\r')

[Link]('\n')
[Link]()

[Link](CONFIG["LOG_INTERVAL_MONITOR"] - total_delay)

def animate_found_key(key: str, duration: float = 0.8):

RANDOM_TRIAL_DURATION = 1.2
SLOW_PRINT_DELAY = 0.007
base_num_trials = int(RANDOM_TRIAL_DURATION / SLOW_PRINT_DELAY)
num_trials = [Link](base_num_trials, base_num_trials + 20)

anim_charset = GLOBAL_CHARSET
key_length = len(key)
BRIGHT_STYLE = [Link]
PADDING = 60
CURSOR_UP_LINES = 2

print(f"\
n{[Link]}========================================{Style.RESET_ALL}")
print(f"{[Link]}[SYSTEM ALERT] KEY FOUND. VALIDATION SUCCESS.
{Style.RESET_ALL}")

print(f"{[Link]}========================================{Style.RESET_ALL}")

print("\n")
[Link](f"CANDIDATE KEY: {[Link]}INITIALIZING...
{Style.RESET_ALL}\n")
[Link](f"VERIFIED KEY: {[Link]}INITIALIZING...{Style.RESET_ALL}\
n")
[Link]()

[Link](CURSOR_UP_1 * CURSOR_UP_LINES)
[Link]()

for i in range(key_length):
correct_part = key[:i]
target_char = key[i]

for j in range(num_trials):
random_char = [Link](anim_charset)
output_trial = correct_part + random_char + ([Link] + '?' *
(key_length - i - 1) + Style.RESET_ALL)

[Link](f"\r{CURSOR_UP_1}")
[Link](f"CANDIDATE KEY: {BRIGHT_STYLE}{[Link]}
{output_trial:<{PADDING}}{Style.RESET_ALL}")

output_verified = correct_part + ([Link] + '?' * (key_length - i) +


Style.RESET_ALL)
[Link](f"\nVERIFIED KEY: {BRIGHT_STYLE}{[Link]}
{output_verified:<{PADDING}}{Style.RESET_ALL}")

[Link]()
[Link](SLOW_PRINT_DELAY)

current_reveal_trial = correct_part + target_char + ([Link] + '?' *


(key_length - i - 1) + Style.RESET_ALL)
[Link](f"\r{CURSOR_UP_1}")
[Link](f"CANDIDATE KEY: {BRIGHT_STYLE}{[Link]}
{current_reveal_trial:<{PADDING}}{Style.RESET_ALL}")

output_verified_final = correct_part + target_char + ([Link] + '?' *


(key_length - (i + 1)) + Style.RESET_ALL)
[Link](f"\nVERIFIED KEY: {BRIGHT_STYLE}{[Link]}
{output_verified_final:<{PADDING}}{Style.RESET_ALL}")
[Link]()

[Link](0.5)

[Link]('\n')
final_key_display = f"{BRIGHT_STYLE}{[Link]}{key}{Style.RESET_ALL}"

print(f"\n{[Link]}>> FINAL REPORT <<{Style.RESET_ALL}")


print(f"{[Link]}>> Decryption Passphrase: {final_key_display}")

# ==============================================================================
# SEKTOR 07: PROCESS_WORKER_EXECUTION
# ==============================================================================

def worker_routine(idx, args):

global WORKER_ID_SIGNAL, IS_SIGNALED

WORKER_ID_SIGNAL = idx
[Link]([Link], signal_handler)

path = args['path']; mode = args['mode']


start_time = [Link](); last_log = [Link]()
tested_local = 0; tested_since_log = 0
total_workers = args['workers']
batch_size = CONFIG["BATCH_SIZE_CRACK"]

if mode == 'BRUTE':
fppc = args['fppc']; current_ppl = args['current_ppl']

ppl_len = current_ppl; charset_len = len(GLOBAL_CHARSET)

rem_combs = 1 if ppl_len == 1 and fppc in GLOBAL_CHARSET else charset_len


** (ppl_len - 1) if ppl_len > 1 else 0

if rem_combs <= 0:
return

start_iter = idx
gen = gen_brute(ppl_len, fppc, GLOBAL_CHARSET, start_iter, total_workers,
rem_combs)

password_batch = []
last_pwd = ""

for pwd in gen:


if IS_SIGNALED: return

password_batch.append(pwd)
last_pwd = pwd

if len(password_batch) >= batch_size:

if [Link](CONFIG["FILES"]["KEY_TMP"]): return

found, key = test_password_batch(path, password_batch)

tested_count = len(password_batch)
tested_local += tested_count
tested_since_log += tested_count
password_batch = []

if found:
write_found_key(key, format_time([Link]() - start_time))
[Link](f"tmux kill-session -t {CONFIG['SESSION_NAME']}")
return

now = [Link]()

# [TUNING V41] Update Log lebih jarang (3 detik sekali)


if now - last_log > CONFIG["LOG_INTERVAL_WORKER"]:
speed = tested_since_log / (now - last_log) if (now - last_log)
> 0 else 0
elapsed = now - start_time

status_data = {
"time": format_time(elapsed), "speed": f"{speed:.0f} p/s",
"pwd": last_pwd, "ppl": current_ppl, "tested_count":
tested_local, "ts": now
}
update_worker_status_file(idx, status_data)
last_log = now; tested_since_log = 0

if password_batch:
if not [Link](CONFIG["FILES"]["KEY_TMP"]):
found, key = test_password_batch(path, password_batch)
tested_local += len(password_batch)
if found:
write_found_key(key, format_time([Link]() - start_time))
[Link](f"tmux kill-session -t {CONFIG['SESSION_NAME']}")
return

now = [Link]()
speed = tested_since_log / (now - last_log) if (now - last_log) > 0 else 0
elapsed = now - start_time
status_data = {
"time": format_time(elapsed), "speed": f"{speed:.0f} p/s",
"pwd": last_pwd if last_pwd else "EXHAUSTED", "ppl": current_ppl,
"tested_count": tested_local, "ts": now
}
update_worker_status_file(idx, status_data)

return
else:
return

# ==============================================================================
# SEKTOR 08: MAIN_OP_CENTER_INIT
# ==============================================================================

def get_user_input_safe(prompt):
try:
return input(prompt)
except KeyboardInterrupt:
raise
except EOFError:
raise KeyboardInterrupt

def main_setup():
global TOTAL_WORKERS_GLOBAL, CURRENT_ACTIVE_WORKERS

[Link]("clear")

print(f"{[Link]}============================================={Style.RESET_ALL}
")
print(f"ARCHIVE KEY RECOVERY UTILITY [JTR-BFA CORE V6.1] (RELEASE V41 - HYPER
TURBO)")
print(f"MODULE: High-Performance Brute Force & Dictionary Analysis Core")

print(f"{[Link]}============================================={Style.RESET_ALL}
")

cpu_count = os.cpu_count() or 4

CONFIG["MAX_CORES"] = cpu_count
TOTAL_WORKERS_GLOBAL = cpu_count
CURRENT_ACTIVE_WORKERS = cpu_count

print(f"SYSTEM: Processor Cores Initialized: {[Link]}


{CONFIG['MAX_CORES']}/{cpu_count} Cores{Style.RESET_ALL} (MAX_THROUGHPUT)")
print(f"SYSTEM: Key Length Policy: {[Link]}FIXED LENGTH
ONLY{Style.RESET_ALL}")
print(f"SYSTEM: Supported Archive: {[Link]}ZIP ONLY{Style.RESET_ALL}")
print(f"SYSTEM: Batch Size: {[Link]}{CONFIG['BATCH_SIZE_CRACK']}
Keys/Pass{Style.RESET_ALL}")

target_dir = "/storage/emulated/0/FFN"
archive_path = ""

files_to_clean = list(CONFIG["FILES"].values())
for file_name in files_to_clean:
if file_name not in ["STATUS_PREFIX", "STATUS_SUFFIX"] and
[Link](file_name):
try: [Link](file_name)
except: pass

for i in range(cpu_count):
status_file = f"{CONFIG['FILES']['STATUS_PREFIX']}{i}{CONFIG['FILES']
['STATUS_SUFFIX']}"
if [Link](status_file):
try: [Link](status_file)
except: pass

if [Link](target_dir):
[Link](target_dir)
files = [f for f in [Link]() if [Link]().endswith(('.zip'))]
if files:
print(f"\n[SCAN] Target Directory [{target_dir}] Archive Files:");
for i, f in enumerate(files): print(f" [{i+1}] {f}")
print(" [0] Manual Archive Path Entry")
while True:
sel = get_user_input_safe(f"{[Link]}[>] Select Archive Index
(0 for Manual Path):{Style.RESET_ALL} ").strip()
if [Link]():
idx = int(sel)
if 0 < idx <= len(files):
archive_path = [Link](files[idx-1]); break
elif idx == 0:
archive_path = get_user_input_safe("Enter Full Archive
Path (.zip): ").strip()
if archive_path.lower().endswith('.zip'): break
else: print(f"{[Link]}[ERROR] File must be a .zip
archive.{Style.RESET_ALL}")

else: print(f"{[Link]}[ERROR] Invalid Selection.


{Style.RESET_ALL}")
else: archive_path = get_user_input_safe("Enter Full Archive Path (.zip):
").strip()
else: archive_path = get_user_input_safe("Enter Full Archive Path (.zip):
").strip()

if not [Link](archive_path) or not


archive_path.lower().endswith('.zip'):
[Link]("[ERROR] Target Archive Not Found or is not a .zip file.")

atype = 'zip'

print("-" * 40)
meta = analyze_archive(archive_path)
print(f"TARGET_FILE: {[Link](archive_path)}")
print(f"ARCHIVE_SIZE: {meta['Size']} | MD5_HASH: {meta['MD5']}")

print(f"ENCRYPTION_STATUS: {meta['Enc']}")
print(f"ANALYSIS_MODE: {[Link]}ZIP NATIVE (Hyper-Turbo Optimized)
{Style.RESET_ALL}")

print("-" * 40)

print("\nATTACK_VECTOR:"); print(" [1] Brute Force (Alphanumeric+Symbols)")


print(" [2] Dictionary (Wordlist) - [DISABLED IN FIXED PPL MODE]")
mode_sel = get_user_input_safe(f"{[Link]}[>] Select Attack Mode (1 or 2):
{Style.RESET_ALL} ").strip()
mode = "DICT" if mode_sel == '2' else "BRUTE"

base_args = {
"path": archive_path, "type": atype, "mode": mode, "workers":
CONFIG["MAX_CORES"],
"ifile": "N/A", "tmode": "FAST",
}

current_ppl = 0

if mode == "BRUTE":
while True:
try:
fixed_ppl = int(get_user_input_safe(f"TARGET_KEY_LENGTH (N, Max
{CONFIG['MAX_PPL']}): "))
if 1 <= fixed_ppl <= CONFIG['MAX_PPL']: break
elif fixed_ppl > CONFIG['MAX_PPL']: print(f"{[Link]}[ERROR] Key
Length exceeds Max PPL ({CONFIG['MAX_PPL']}).{Style.RESET_ALL}")
except ValueError:
print(f"{[Link]}[ERROR] Invalid input. Please enter a number.
{Style.RESET_ALL}")
except KeyboardInterrupt:
raise

current_ppl = fixed_ppl
base_args['current_ppl'] = current_ppl

print(f"ATTACK RANGE: Key Length Fixed: {[Link]}{current_ppl}


Char{Style.RESET_ALL}")

while True:
fppc = get_user_input_safe("INITIAL_CHARACTER (First Position):
").strip()
if len(fppc) == 1 and fppc in GLOBAL_CHARSET:
base_args['fppc'] = fppc; break
elif len(fppc) != 1:
print(f"{[Link]}[ERROR] Initial Character must be single
character.{Style.RESET_ALL}")
else:
print(f"{[Link]}[ERROR] Initial Character '{fppc}' not in
supported charset. Charset size: {len(GLOBAL_CHARSET)}.{Style.RESET_ALL}")
else:
while True:
wlist = get_user_input_safe("WORDLIST_PATH: ").strip()
if [Link](wlist): base_args['wlist'] = [Link](wlist);
break
print("[ERROR] Wordlist Not Found!")

start_monitor_time = [Link]()
KEY_FOUND_FLAG = False
FOUND_KEY = None
target_archive_path = archive_path

if mode == 'BRUTE':
print(f"\n[PROCESS] INITIATING PROCESSORS ({CONFIG['MAX_CORES']} Workers)
for KEY_LEN {current_ppl}...")
[Link](1)
[Link](["tmux", "kill-session", "-t", CONFIG["SESSION_NAME"]],
stderr=[Link])

serialized_args = base64.b64encode([Link](base_args)).decode()
script = [Link](__file__)

cmd = f"python3 {quote(script)} WORKER 0 {quote(serialized_args)}"


[Link](["tmux", "new-session", "-d", "-s", CONFIG["SESSION_NAME"],
cmd], check=True)

for i in range(1, CONFIG["MAX_CORES"]):


cmd = f"python3 {quote(script)} WORKER {i} {quote(serialized_args)}"
split_flag = "-h" if i % 2 != 0 else "-v"
[Link](["tmux", "split-window", "-t", CONFIG["SESSION_NAME"],
split_flag, cmd], check=True)
[Link](["tmux", "select-layout", "-t", CONFIG["SESSION_NAME"],
"tiled"], check=True)

print(f"{[Link]}[SYSTEM] Workers Deployed. Monitoring Key Status


(Refresh: {CONFIG['LOG_INTERVAL_MONITOR']}s)...{Style.RESET_ALL}")

[Link]('\n')
[Link]('\n' * 3)
[Link]()
[Link](CURSOR_UP_1 * 3)
[Link]()

current_worker_index = 0

try:
while True:
tmux_status = [Link](["tmux", "has-session", "-t",
CONFIG["SESSION_NAME"]],
stdout=[Link],
stderr=[Link]).returncode
key, elapsed = check_found_key()

if key:
KEY_FOUND_FLAG = True
FOUND_KEY = key
[Link]('\n' * 3)
animate_found_key(key, duration=0.8)
[Link](["tmux", "kill-session", "-t",
CONFIG["SESSION_NAME"]], stderr=[Link])
break

if tmux_status != 0:
[Link]('\n' * 3)
print(f"{[Link]}[INFO] KEY_LEN {current_ppl} Range
Exhausted. Analysis Finished.{Style.RESET_ALL}")
break

all_statuses = []; total_speed = 0.0; max_tested_local = 0


for i in range(CONFIG["MAX_CORES"]):
status = get_worker_status(i)
all_statuses.append(status)
if status['ts'] > 0 and status['speed'] != '0 p/s':
try:
speed_val = float(status['speed'].split(' ')
[0].replace(',', ''))
total_speed += speed_val
if [Link]('tested_count', 0) > max_tested_local:
max_tested_local = status['tested_count']
except: pass

total_tested_ppl = max_tested_local * CONFIG["MAX_CORES"]


elapsed_seconds = [Link]() - start_monitor_time

active_workers = [i for i, s in enumerate(all_statuses) if s['ts']


> 0]
if active_workers:
if current_worker_index not in active_workers:
current_worker_index = active_workers[0]
else:
try:
current_idx_in_active =
active_workers.index(current_worker_index)
next_idx_in_active = (current_idx_in_active + 1) %
len(active_workers)
current_worker_index =
active_workers[next_idx_in_active]
except ValueError: current_worker_index = active_workers[0]
status_to_show = all_statuses[current_worker_index]['pwd']
else:
status_to_show = "INITIALIZING / EXHAUSTED"

slow_print_status(status_to_show, CONFIG["MAX_CORES"], total_speed,


total_tested_ppl, elapsed_seconds, current_ppl)
[Link](CONFIG["LOG_INTERVAL_MONITOR"])
except KeyboardInterrupt:
raise

if FOUND_KEY:
extract_choice = get_user_input_safe(f"[INPUT] Execute Extraction Routine
(y/n): ").lower().strip()
if extract_choice == 'y':
dest_dir = "/storage/emulated/0/FFN/Extracted_zip"
print(f"{[Link]}[INFO] Extraction Output Target:{Style.RESET_ALL}
{dest_dir}")
extract_archive(target_archive_path, FOUND_KEY, dest_dir)
else:
print(f"{[Link]}[INFO] Extraction Skipped by User Request.
{Style.RESET_ALL}")

if not KEY_FOUND_FLAG and mode == 'BRUTE':


print(f"\n{[Link]}[FAILURE] Defined Range ({current_ppl} Char) Exhausted.
Key Not Found.{Style.RESET_ALL}")

if __name__ == "__main__":
if len([Link]) > 1 and [Link][1] == "WORKER":
try:
idx = int([Link][2])
args = [Link](base64.b64decode([Link][3]))
worker_routine(idx, args)
except Exception:
[Link](10)
else:
try:
main_setup()
except KeyboardInterrupt:
print(f"\n{[Link]}[ALERT] SYSTEM INTERRUPT DETECTED. TERMINATING ALL
ACTIVE SESSIONS.{Style.RESET_ALL}")
[Link](["tmux", "kill-session", "-t", CONFIG["SESSION_NAME"]],
stdout=[Link], stderr=[Link])
for i in range(os.cpu_count() or 4):
status_file = f"{CONFIG['FILES']['STATUS_PREFIX']}{i}
{CONFIG['FILES']['STATUS_SUFFIX']}"
if [Link](status_file):
try: [Link](status_file)
except: pass
if [Link](CONFIG["FILES"]["LOCK"]): [Link](CONFIG["FILES"]
["LOCK"])
[Link](0)
HASHCAT (NUMERIC).PY

import re
import base64
import hashlib
import time
import os
import math
import sys
from tabulate import tabulate
import itertools
import subprocess
from shlex import quote
import zipfile
from typing import Dict, List, Iterable

# --- Import Colorama ---


try:
from colorama import Fore, Style, init
init(autoreset=True)
CLEAR_LINE = '\r' + ' ' * 80 + '\r'
CURSOR_UP_2 = '\033[2A'

except ImportError:
class DummyColor:
def __getattr__(self, name): return ""
Fore = DummyColor()
Style = DummyColor()
CLEAR_LINE = '\r'
CURSOR_UP_2 = ''

# --------------------------------------------------------------------
# --- KONFIGURASI V56: PURE NUMERIC & NO PADDING ---
# --------------------------------------------------------------------
TOTAL_COMBINATIONS = 10**12

# KONFIGURASI CHUNKING
CHUNK_SIZE = 100_000
TOTAL_WORKERS = 4

FOUND_KEY = None

# Mutex dan Sync


LOG_INTERVAL = 10
SYNC_CHECK_INTERVAL = 100000
SESSION_NAME = "BFA_PYTHON_RUNNER_ARCHIVE"
LOG_FILE = "FOUND_KEY.log"
LOCK_FILE = "TERMINAL_LOCK.lock"
FOUND_KEY_NUMBER_FILE = "FOUND_KEY_NUMBER.tmp"

# VIRTUAL CHUNK UNIT


VIRTUAL_CHUNK_UNIT = CHUNK_SIZE // TOTAL_WORKERS

# INPUT GLOBAL
ARCHIVE_PATH = ""
ARCHIVE_TYPE = ""
INTERNAL_FILE_NAME = ""
TEST_MODE = "FAST"
# Variabel yang Dihapus: BRUTE_MODE, QR_STRING_PREFIX
# --------------------------------------------------------------------

# --- FUNGSI UTILITY ---


def format_elapsed_time(seconds: float) -> str:
seconds = int(seconds)
hours = seconds // 3600
minutes = (seconds % 3600) // 60
seconds = seconds % 60
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"

def time_to_seconds(time_str: str) -> float:


parts = time_str.split(':')
if len(parts) == 3:
hours, minutes, seconds = map(int, parts)
return float(hours * 3600 + minutes * 60 + seconds)
return 0.0

def check_for_found_key() -> tuple[str | None, str | None, str | None]:


if [Link](LOG_FILE) and [Link](FOUND_KEY_NUMBER_FILE):
try:
with open(LOG_FILE, 'r') as f:
key_line, time_line = [Link]().strip().split('\n')
with open(FOUND_KEY_NUMBER_FILE, 'r') as f:
key_number_line = [Link]().strip()
return key_line, time_line, key_number_line
except IOError:
pass
return None, None, None

def write_found_key(key: str, total_time: str):


try:
with open(LOG_FILE, 'w') as f:
[Link](f"{key}\n{total_time}")
with open(FOUND_KEY_NUMBER_FILE, 'w') as f:
[Link](key)
except IOError as e:
print(f"{[Link]}❌ ERROR: Gagal menulis ke file log/tested: {e}
{Style.RESET_ALL}")

# --- FUNGSI DAFTAR ISI ARSIP (Tidak Berubah) ---


def list_archive_contents(archive_path: str, archive_type: str) -> List[str]:
"""Menggunakan 7z/unzip untuk mendapatkan daftar isi arsip."""

if archive_type == 'zip':
command = ["unzip", "-l", archive_path]
elif archive_type == '7z':
command = ["7z", "l", archive_path]
else:
return []

try:
result = [Link](
command,
capture_output=True,
text=True,
check=False,
timeout=10
)

if [Link] != 0:
return []

output_lines = [Link]().split('\n')
contents = []

if archive_type == 'zip':
in_file_list = False
for line in output_lines:
if [Link](r'^\s*-+\s*$', line):
in_file_list = True
continue
if in_file_list and not [Link](r'files?$', line) and
[Link]():
parts = [Link]().split()
if len(parts) >= 4:
file_name = parts[-1]
if file_name not in ['.', '..'] and not
file_name.endswith('/'):
[Link](file_name)

elif archive_type == '7z':


in_file_list = False
for line in output_lines:
if [Link](r'^\s*-+\s*$', line):
if in_file_list:
break
in_file_list = True
continue

if in_file_list and [Link]() and not [Link]('Path'):


parts = [Link]()
if len(parts) > 5:
file_name = ' '.join(parts[5:])
if file_name and not file_name.endswith('/'):
[Link](file_name)

return list(set([[Link]() for c in contents if [Link]()]))

except Exception:
return []

# --- FUNGSI PENGUJIAN FILE ARSIP (Tidak Berubah) ---


def test_archive_password(archive_path: str, password: str, archive_type: str,
internal_file_name: str, test_mode: str) -> bool:
"""
Menguji password dengan mode Test (cepat) atau Verify (lambat/ekstraksi).
"""
password_quoted = quote(password)
internal_file_quoted = quote(internal_file_name)

success = False

if test_mode == 'FAST':
# MODE 1: Uji Integritas (Cepat)
if archive_type == 'zip':
command = [
"unzip", "-qq", "-t", "-P", password_quoted, archive_path
]

elif archive_type == '7z':


command = [
"7z", "t", archive_path, f"-p{password_quoted}"
]
else:
return False

try:
result = [Link](command, stdout=[Link],
stderr=[Link], check=False, timeout=5)

if archive_type == 'zip':
success = [Link] in [0, 1]
elif archive_type == '7z':
success = [Link] == 0

except Exception:
pass

elif test_mode == 'VERIFY':


# MODE 2: Ekstraksi File Internal (Lambat)
temp_dir = [Link]([Link](), f"temp_extract_{[Link]()}")
[Link](temp_dir, exist_ok=True)

try:
if archive_type == 'zip':
command = ["unzip", "-j", "-P", password_quoted, archive_path,
internal_file_quoted, "-d", temp_dir]
elif archive_type == '7z':
command = ["7z", "e", archive_path, internal_file_quoted, f"-
p{password_quoted}", f"-o{temp_dir}", "-y"]
else:
return False

result = [Link](command, stdout=[Link],


stderr=[Link], check=False, timeout=15)

extracted_base_name = [Link](internal_file_name.replace('\\',
'/'))
extracted_file_path = [Link](temp_dir, extracted_base_name)

if (archive_type == 'zip' and [Link] in [0, 1]) or


(archive_type == '7z' and [Link] == 0):
if [Link](extracted_file_path) and
[Link](extracted_file_path) > 0:
success = True

except Exception:
pass

finally:
try:
if [Link](temp_dir):
for item in [Link](temp_dir):
[Link]([Link](temp_dir, item))
[Link](temp_dir)
except Exception:
pass

return success

# --------------------------------------------------------------------
# --- FUNGSI WORKER BFA PURE NUMERIC ---
# --------------------------------------------------------------------
def brute_force_sequential(
worker_index: int,
session_start_time: float,
archive_path: str,
archive_type: str,
internal_file_name: str,
test_mode: str,
# Argumen yang Dihapus: qr_string_prefix
) -> str | None:

global FOUND_KEY

start_num = worker_index
step_size = TOTAL_WORKERS

total_tested_by_this_worker = 0

last_checked_time = [Link]()
last_tested_count = 0

pane_label = f"P{worker_index + 1}"


label_map = {0: [Link] + "I+0", 1: [Link] + "I+1", 2: [Link] +
"I+2", 3: [Link] + "I+3"}
index_label = f"({label_map.get(worker_index, [Link] + '??')}
{Style.RESET_ALL})"

for i in range(start_num, TOTAL_COMBINATIONS, step_size):

# --- MURNI ANGKA (NO PADDING) ---


numeric_part = str(i)
password = numeric_part # TIDAK ADA PREFIX QR
# -------------------------------

total_tested_by_this_worker += 1

virtual_chunk_num = (total_tested_by_this_worker - 1) // VIRTUAL_CHUNK_UNIT


+ 1
# Sinkronisasi & Cek Kunci
if total_tested_by_this_worker % SYNC_CHECK_INTERVAL == 0:
found_key_sync, _, _ = check_for_found_key()
if found_key_sync:
[Link](CLEAR_LINE)
[Link]()
print(f"{[Link]}🛑 STOPPING! Kunci '{found_key_sync}'
ditemukan oleh worker lain.{Style.RESET_ALL}")
return found_key_sync

# --- MUTEX LOGIC START (Progress Bar & Pt/s) ---


if total_tested_by_this_worker % LOG_INTERVAL == 0:

current_time = [Link]()
time_diff = current_time - last_checked_time
tested_diff = total_tested_by_this_worker - last_tested_count

pt_per_second = tested_diff / time_diff if time_diff > 0 else 0

last_checked_time = current_time
last_tested_count = total_tested_by_this_worker

try:
# Ambil Mutex Lock
lock_fd = [Link](LOCK_FILE, os.O_CREAT | os.O_EXCL | os.O_WRONLY)
[Link](lock_fd)

session_elapsed = [Link]() - session_start_time


session_time_str = format_elapsed_time(session_elapsed)

[Link](CURSOR_UP_2)
[Link](CLEAR_LINE)

pt_s_str = f"{pt_per_second:,.0f}"

progress_info = (
f"\r[{[Link]}{pane_label}/C{virtual_chunk_num}
{Style.RESET_ALL}] "
f"{index_label} "
f"({[Link]}{session_time_str}{Style.RESET_ALL}) "
f"({[Link]}Pt/s: {pt_s_str}{Style.RESET_ALL}): "
f"{[Link]}{password}{Style.RESET_ALL}\n"
f"{[Link]}STATUS: {[Link]}Running...{Style.RESET_ALL}"
)

[Link](progress_info)
[Link]()

[Link](LOCK_FILE)

except FileExistsError:
pass
except Exception:
pass
# --- MUTEX LOGIC END ---

# --- Pengujian Kunci Arsip ---


is_correct = test_archive_password(archive_path, password, archive_type,
internal_file_name, test_mode)

if is_correct:

session_elapsed = [Link]() - session_start_time


session_time_str = format_elapsed_time(session_elapsed)

write_found_key(password, session_time_str)

[Link](CLEAR_LINE)
[Link]()
FOUND_KEY = password

print(f"{[Link]}🎉 Kunci Ditemukan ({password})! Mengirim sinyal


penutupan sesi Tmux...{Style.RESET_ALL}")
try:
[Link](f"tmux kill-session -t {SESSION_NAME}")
except Exception as e:
print(f"{[Link]}❌ Gagal mengirim sinyal kill-session: {e}
{Style.RESET_ALL}")

return password

[Link](CLEAR_LINE)
[Link]()
return None

# --------------------------------------------------------------------
# --- FUNGSI UTAMA & SETUP TMUX (Layout 2x2) ---
# --------------------------------------------------------------------
def run_worker(worker_index: int, archive_path_encoded: str, archive_type: str,
internal_file_name_encoded: str, test_mode_encoded: str):
# Argumen yang Dihapus: qr_string_prefix_encoded

global ARCHIVE_PATH, ARCHIVE_TYPE, INTERNAL_FILE_NAME, TEST_MODE

# Dekode dari argumen command line


ARCHIVE_PATH = base64.b64decode(archive_path_encoded).decode()
ARCHIVE_TYPE = archive_type
INTERNAL_FILE_NAME = base64.b64decode(internal_file_name_encoded).decode()
TEST_MODE = base64.b64decode(test_mode_encoded).decode()
# Argumen Dihapus: QR_STRING_PREFIX

pane_label = f"P{worker_index + 1}"


print(f"{[Link]}--- {pane_label} STARTING LOOP | MODE: {TEST_MODE} (Index
{worker_index}) ---{Style.RESET_ALL}")

session_start_time = [Link]()
found_key = brute_force_sequential(
worker_index, session_start_time, ARCHIVE_PATH, ARCHIVE_TYPE,
INTERNAL_FILE_NAME, TEST_MODE # Argumen Dihapus: ""
)

if found_key:
[Link](CLEAR_LINE)
[Link]()
print(f"{[Link]}✅ Worker berhenti. Kunci ditemukan: {found_key}
{Style.RESET_ALL}")
[Link](5)

def setup_tmux_and_run():

global ARCHIVE_PATH, ARCHIVE_TYPE, INTERNAL_FILE_NAME, TEST_MODE

# --- Bersihkan file-file lama ---


if [Link](LOG_FILE): [Link](LOG_FILE)
if [Link](LOCK_FILE): [Link](LOCK_FILE)
if [Link](FOUND_KEY_NUMBER_FILE): [Link](FOUND_KEY_NUMBER_FILE)

# --- Tampilan Utama ---

print(f"{[Link]}============================================={Style.RESET_ALL}
")
print(f"JtR V56 ({[Link]}BRUTE FORCE TOOLS{Style.RESET_ALL})")

print(f"{[Link]}============================================={Style.RESET_ALL}
")
print(f"Pane: {[Link]}{TOTAL_WORKERS}{Style.RESET_ALL}")
print(f"📦 key passpharse: *{TOTAL_COMBINATIONS:,}* (0 hingga 999,999,999,999)")
print(f"❗{[Link]} Utilty: John The Ripper Mail Edition{Style.RESET_ALL}")
print("-" * 50)

# --- 1. PILIH MODE OPERASI UJI ---


# Logika pemilihan brute force (QR/Numeric) sudah dihapus
print(f"{[Link]}Pilih Mode Operasi Uji:{Style.RESET_ALL}")
print(f" {[Link]}[1]{Style.RESET_ALL} Mode Cepat (FAST): Menggunakan '7z
t'. (Direkomendasikan)")
print(f" {[Link]}[2]{Style.RESET_ALL} Mode Verifikasi (VERIFY):
Menggunakan '7z e'.")

mode_choice = input(f"{[Link]}Pilih Mode Uji (1/2):{Style.RESET_ALL}


").strip()

if mode_choice == '2':
TEST_MODE = 'VERIFY'
print(f"{[Link]}✅ Mode Verifikasi dipilih.{Style.RESET_ALL}")
else:
TEST_MODE = 'FAST'
print(f"{[Link]}✅ Mode Cepat dipilih.{Style.RESET_ALL}")

print("-" * 50)

# --- 2. Input File Arsip ---


storage_path = "/storage/emulated/0/"
current_cwd = [Link]()
archive_path = ""

try:
if [Link](storage_path):
print(f"{[Link]}Navigasi ke: {storage_path}{Style.RESET_ALL}")
[Link](storage_path)

print(f"\n{[Link]}Daftar File Arsip (zip/7z di


/storage/emulated/0/):{Style.RESET_ALL}")

files = [f for f in [Link]() if [Link]().endswith(('.zip', '.7z'))


and [Link](f)]
if files:
for i, f in enumerate(files):
print(f" {[Link]}[{i+1}]{Style.RESET_ALL} {f}")
print(f" {[Link]}[0]{Style.RESET_ALL} Masukkan jalur file
secara manual.")

choice = input(f"{[Link]}2. Pilih Angka File Arsip atau


Masukkan Jalur Manual:{Style.RESET_ALL} ").strip()

if [Link]() and 1 <= int(choice) <= len(files):


archive_path = files[int(choice) - 1]
archive_path = [Link](storage_path, archive_path)
else:
archive_path = choice if choice != '0' else
input(f"{[Link]}Masukkan Jalur File Arsip Lengkap (.zip atau .7z):
{Style.RESET_ALL} ").strip()
archive_path = [Link](archive_path)
else:
print(f"{[Link]}Tidak ada file .zip atau .7z yang ditemukan di
direktori ini.{Style.RESET_ALL}")
archive_path = input(f"{[Link]}2. Masukkan Jalur File Arsip
Lengkap (.zip atau .7z):{Style.RESET_ALL} ").strip()
archive_path = [Link](archive_path)
else:
print(f"{[Link]}❌ Direktori {storage_path} tidak dapat diakses.
Pastikan Termux memiliki izin storage.{Style.RESET_ALL}")
archive_path = input(f"{[Link]}2. Masukkan Jalur File Arsip Lengkap
(.zip atau .7z):{Style.RESET_ALL} ").strip()
archive_path = [Link](archive_path)

finally:
[Link](current_cwd)

if not [Link](archive_path):
print(f"{[Link]}❌ File arsip tidak ditemukan di jalur: {archive_path}
{Style.RESET_ALL}")
return

ARCHIVE_PATH = archive_path

if ARCHIVE_PATH.lower().endswith('.zip'):
ARCHIVE_TYPE = 'zip'
elif ARCHIVE_PATH.lower().endswith('.7z'):
ARCHIVE_TYPE = '7z'
else:
print(f"{[Link]}❌ Format file tidak didukung ({ARCHIVE_PATH}). Hanya .zip
dan .7z.{Style.RESET_ALL}")
return

print(f"\n{[Link]}File Target Arsip: {ARCHIVE_PATH}{Style.RESET_ALL}")


print(f"{[Link]}Tipe Arsip Terdeteksi: {ARCHIVE_TYPE.upper()}
{Style.RESET_ALL}")

# --- 3. Tampilkan dan Pilih File Internal (Jika VERIFY mode) ---
INTERNAL_FILE_NAME = "N/A (FAST MODE)"
if TEST_MODE == 'VERIFY':
print("\n" + "-" * 50)
print(f"{[Link]}Membaca Daftar File di Dalam Arsip (Diperlukan untuk
VERIFY mode)...{Style.RESET_ALL}")

internal_files = list_archive_contents(ARCHIVE_PATH, ARCHIVE_TYPE)

if not internal_files:
print(f"{[Link]}❌ Gagal mendapatkan daftar file internal atau arsip
kosong. Verifikasi Gagal.{Style.RESET_ALL}")
return

print(f"\n{[Link]}Daftar File Internal ({len(internal_files)}):


{Style.RESET_ALL}")

for i, f in enumerate(internal_files):
print(f" {[Link]}[{i+1}]{Style.RESET_ALL} {f}")

print("-" * 50)

file_choice = input(f"{[Link]}3. Pilih Angka File TUNGGAL yang akan


dijadikan target decrypt:{Style.RESET_ALL} ").strip()

if file_choice.isdigit() and 1 <= int(file_choice) <= len(internal_files):


INTERNAL_FILE_NAME = internal_files[int(file_choice) - 1]
else:
print(f"{[Link]}❌ Pilihan tidak valid. Setup dibatalkan.
{Style.RESET_ALL}")
return

print(f"\n{[Link]}File Internal Target: {INTERNAL_FILE_NAME}


{Style.RESET_ALL}")

# --- Setup Tmux ---


print(f"\n{[Link]}Memulai Setup Paralel Tmux ({TOTAL_WORKERS} Workers)...
{Style.RESET_ALL}")

try:
[Link](["tmux", "-V"], check=True, capture_output=True)
except FileNotFoundError:
print(f"{[Link]}❌ Error: Tmux tidak ditemukan. Harap instal Tmux.
{Style.RESET_ALL}")
[Link](1)

[Link](["tmux", "kill-session", "-t", SESSION_NAME],


stderr=[Link])

script_path = [Link](__file__)

# Enkode argumen
archive_path_encoded = base64.b64encode(ARCHIVE_PATH.encode()).decode()
internal_file_name_encoded =
base64.b64encode(INTERNAL_FILE_NAME.encode()).decode()
test_mode_encoded = base64.b64encode(TEST_MODE.encode()).decode()
# Argumen yang Dihapus: qr_string_prefix_encoded

try:
# P0: Buat sesi baru
cmd0 = f'python3 {quote(script_path)} 0 {quote(archive_path_encoded)}
{ARCHIVE_TYPE} {quote(internal_file_name_encoded)} {quote(test_mode_encoded)}'
[Link](["tmux", "new-session", "-d", "-s", SESSION_NAME, cmd0],
check=True)
# P1: Split window secara vertikal (P0 di kiri atas, P1 di kiri bawah)
cmd1 = f'python3 {quote(script_path)} 1 {quote(archive_path_encoded)}
{ARCHIVE_TYPE} {quote(internal_file_name_encoded)} {quote(test_mode_encoded)}'
[Link](["tmux", "split-window", "-t", f"{SESSION_NAME}:0.0", "-v",
cmd1], check=True)

# P2: Split window secara vertikal lagi di sebelah kanan P0 (P2 di kanan
atas)
cmd2 = f'python3 {quote(script_path)} 2 {quote(archive_path_encoded)}
{ARCHIVE_TYPE} {quote(internal_file_name_encoded)} {quote(test_mode_encoded)}'
[Link](["tmux", "split-window", "-t", f"{SESSION_NAME}:0.0", "-h",
cmd2], check=True)

# P3: Split window secara vertikal di bawah P2 (P3 di kanan bawah)


cmd3 = f'python3 {quote(script_path)} 3 {quote(archive_path_encoded)}
{ARCHIVE_TYPE} {quote(internal_file_name_encoded)} {quote(test_mode_encoded)}'
[Link](["tmux", "split-window", "-t", f"{SESSION_NAME}:0.2", "-v",
cmd3], check=True)

# Atur Layout: Tiled (2x2)


[Link](["tmux", "select-layout", "tiled"], check=True)

print(f"{[Link]}✅ Setup Selesai. Otomatis masuk ke sesi Tmux


'{SESSION_NAME}'. Layout: 2x2 (Tiled).{Style.RESET_ALL}")
[Link](["tmux", "attach-session", "-t", SESSION_NAME])

# --- LOGIKA DEKODE AKHIR DI SHELL UTAMA ---


final_key, total_time_str, key_number_str = check_for_found_key()

# Bersihkan LOCK/DATA FILE


if [Link](LOCK_FILE): [Link](LOCK_FILE)
if [Link](FOUND_KEY_NUMBER_FILE): [Link](FOUND_KEY_NUMBER_FILE)

print('\n' + '-' * 70)

if final_key:

total_attempts_gabungan = int(key_number_str) + 1
total_time_seconds = time_to_seconds(total_time_str)
tpa_micro = (total_time_seconds / total_attempts_gabungan) * 1_000_000
if total_time_seconds > 0 else 0

finder_worker = f"P{int(final_key) % TOTAL_WORKERS + 1}"

print(f"\
n{[Link]}======================================================================
====={Style.RESET_ALL}")
print(f"{[Link]}BFA Arsip Berhasil! Password ditemukan: {[Link]}
{final_key}{Style.RESET_ALL} (Ditemukan oleh Worker {finder_worker})")
print(f"💾 File Arsip: {ARCHIVE_PATH}")
print(f"📄 Mode Uji: {TEST_MODE}")
if TEST_MODE == 'VERIFY':
print(f"📄 File Internal Uji: {INTERNAL_FILE_NAME}")
# Argumen yang Dihapus: QR String Prefix
print(f"🔑 Tipe Brute Force: {[Link]}PURE NUMERIC (No Padding)
{Style.RESET_ALL}")
print(f"⏰ Total waktu pemulihan: {[Link]}{total_time_str}
{Style.RESET_ALL}")
print(f" Rata-rata Waktu Per Percobaan (TPA): {[Link]}
{tpa_micro:,.3f} \u03bc s{Style.RESET_ALL}")
print(f"\
n{[Link]}======================================================================
====={Style.RESET_ALL}")

[Link](["tmux", "kill-session", "-t", SESSION_NAME],


stderr=[Link])
else:
print(f"\n{[Link]}Sesi {SESSION_NAME} selesai/dikeluarkan tanpa
menemukan kunci.{Style.RESET_ALL}")

except [Link] as e:
print(f"{[Link]}❌ Gagal mengeksekusi perintah Tmux: {e}
{Style.RESET_ALL}")
[Link](1)

if __name__ == "__main__":
if len([Link]) == 6:
try:
worker_index = int([Link][1])
archive_path_encoded = [Link][2]
archive_type = [Link][3]
internal_file_name_encoded = [Link][4]
test_mode_encoded = [Link][5]

run_worker(worker_index, archive_path_encoded, archive_type,


internal_file_name_encoded, test_mode_encoded)
except ValueError:
print(f"{[Link]}❌ Error: Argumen tidak valid saat dijalankan
otomatis.{Style.RESET_ALL}")
[Link](1)
else:
setup_tmux_and_run()

[Link]
import base64
import hashlib
import os
import sys
from [Link] import Cipher, algorithms, modes
from [Link] import padding
from [Link] import default_backend
from colorama import Fore, Style, init

init(autoreset=True)

# Fungsi Kriptografi
def derive_key(password: str) -> bytes:
"""Menggunakan SHA256 untuk mendapatkan kunci 16-byte (AES-128)."""
return hashlib.sha256([Link]('utf-8')).digest()[:16]

def decrypt_aes_128_cbc(iv_bytes: bytes, ciphertext_bytes: bytes, password: str) ->


str | None:
"""Mendekripsi data menggunakan password."""
try:
key = derive_key(password)
cipher = Cipher([Link](key), [Link](iv_bytes),
backend=default_backend())
decryptor = [Link]()

decrypted_padded = [Link](ciphertext_bytes) +
[Link]()

# Unpadding (Menggunakan PKCS7)


padder = padding.PKCS7([Link].block_size).unpadder()
decrypted_bytes = [Link](decrypted_padded) + [Link]()

# Cek validitas data terdekripsi (Harus dimulai dengan "Nama:" atau field
valid)
decoded_str = decrypted_bytes.decode('utf-8')
if not decoded_str.startswith("Nama:"):
return None
return decoded_str
except Exception:
return None

def encrypt_aes_128_cbc(plaintext: str, password: str) -> tuple[bytes, bytes]:


"""Mengenripsi data menggunakan password baru."""
key = derive_key(password)

# Generate IV baru untuk enkripsi baru


iv = [Link](16)

# Padding
padder = padding.PKCS7([Link].block_size).padder()
padded_data = [Link]([Link]('utf-8')) + [Link]()

# Enkripsi
cipher = Cipher([Link](key), [Link](iv), backend=default_backend())
encryptor = [Link]()
ciphertext = [Link](padded_data) + [Link]()

return iv, ciphertext

# Fungsi Utama
def run_password_reset():

print(f"\
n{[Link]}============================================={Style.RESET_ALL}")
print(f"{[Link]}🔑 QR IDENTITY PASSWORD RESET V13.0{Style.RESET_ALL}
(Dekripsi -> Enkripsi Ulang)")

print(f"{[Link]}============================================={Style.RESET_ALL}
")

qr_data_raw = input(f"{[Link]}Masukkan QR Yang ingin di ubah:


{Style.RESET_ALL} ").strip()

# 1. Parsing Ciphertext Lama


if not qr_data_raw.startswith("AES128|"):
print(f"{[Link]}❌ Data tidak terdeteksi dienkripsi dengan AES128.
{Style.RESET_ALL}")
return
parts = qr_data_raw.split('|')
iv_b64 = next(([Link](':')[1] for p in parts if [Link]('IV:')), None)
ciphertext_b64 = next(([Link](':')[1] for p in parts if [Link]('CT:')),
None)

if not iv_b64 or not ciphertext_b64:


print(f"{[Link]}❌ Format data AES tidak lengkap.{Style.RESET_ALL}")
return
try:
iv_bytes_lama = base64.b64decode(iv_b64)
ciphertext_bytes_lama = base64.b64decode(ciphertext_b64)
except Exception:
print(f"{[Link]}❌ Kesalahan Base64 Decode pada QR input.
{Style.RESET_ALL}")
return

# 2. Input Kata Sandi Lama


password_lama = input(f"{[Link]}Masukkan Password lama:{Style.RESET_ALL}
").strip()

# 3. Dekripsi (Verifikasi Password Lama)


print(f"\n{[Link]}--- Verifikasi Password Lama...{Style.RESET_ALL}")
plaintext_data = decrypt_aes_128_cbc(iv_bytes_lama, ciphertext_bytes_lama,
password_lama)

if plaintext_data is None:
print(f"{[Link]}❌ GAGAL: Password Lama tidak valid atau data rusak.
{Style.RESET_ALL}")
return

print(f"{[Link]}✅ Password Lama VALID. Data Asli berhasil didekripsi.


{Style.RESET_ALL}")
print(f" {[Link]}Data Asli:{Style.RESET_ALL} {plaintext_data[:70]}...")

# 4. Input Kata Sandi Baru


password_baru = input(f"{[Link]}\nMasukkan password baru:{Style.RESET_ALL}
").strip()
if not password_baru:
print(f"{[Link]}❌ Password baru tidak boleh kosong.{Style.RESET_ALL}")
return

# 5. Enkripsi Ulang dengan Password Baru


print(f"\n{[Link]}--- Melakukan Enkripsi Ulang dengan Password Baru...
{Style.RESET_ALL}")
iv_bytes_baru, ciphertext_bytes_baru = encrypt_aes_128_cbc(plaintext_data,
password_baru)

# 6. Formatting Output QR Baru


iv_b64_baru = base64.b64encode(iv_bytes_baru).decode('utf-8')
ciphertext_b64_baru = base64.b64encode(ciphertext_bytes_baru).decode('utf-8')

new_qr_data = f"AES128|IV:{iv_b64_baru}|CT:{ciphertext_b64_baru}"

# 7. Hasil Akhir
print(f"{[Link]}✅ BERHASIL! QR Identity Baru telah dibuat.
{Style.RESET_ALL}")
print(f"{[Link]}Password Baru:{Style.RESET_ALL} {[Link]}{password_baru}
{Style.RESET_ALL}")
print("-" * 50)
print(f"{[Link]}QR Identity BARU:{Style.RESET_ALL} {new_qr_data}")
print("-" * 50)

if __name__ == "__main__":
try:
# Periksa dependensi
if 'cryptography' not in [Link]:
print(f"Error: Library 'cryptography' tidak ditemukan. Harap instal
dengan: pip install cryptography")
[Link]()

run_password_reset()

except Exception as e:
print(f"{[Link]}Terjadi Error Fatal: {e}{Style.RESET_ALL}")

[Link]
import subprocess
import re
import requests
import json
import time
import os
import sys # Import sys untuk [Link] dan [Link]

# --- ANSI Color Configuration ---


class Colors:
RED = '\033[31m'
GREEN = '\033[32m'
YELLOW = '\033[33m'
BLUE = '\033[34m'
MAGENTA = '\033[35m'
CYAN = '\033[36m'
WHITE = '\033[37m'
GRAY = '\033[90m'
RESET = '\033[0m'

# --- Fungsi untuk Mencetak Teks Secara Bertahap (Hacker Effect) ---
def print_slowly(text, delay=0.01, end='\n'):
"""Prints text character by character with a delay."""
for char in text:
[Link](char)
[Link]()
[Link](delay)
[Link](end)
[Link]()

def get_network_info():
"""Detects network interface, IP, and CIDR."""
try:
result = [Link](['ip', '-4', 'addr', 'show', 'dev', 'wlan0'],
capture_output=True, text=True, check=False)
if [Link] != 0:
result = [Link](['ip', '-4', 'route', 'get', '[Link]'],
capture_output=True, text=True, check=True)
iface_match = [Link](r'dev (\S+)', [Link])
iface = iface_match.group(1) if iface_match else None

if not iface:
print_slowly(f"{[Link]}[!] Failed to detect network interface.
Make sure you are connected to Wi-Fi.{[Link]}")
return None, None, None

result = [Link](['ip', '-4', 'addr', 'show', iface],


capture_output=True, text=True, check=True)
else:
iface = "wlan0"

cidr_match = [Link](r'inet (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\


d{1,2})', [Link])
cidr = cidr_match.group(1) if cidr_match else None

if not cidr:
print_slowly(f"{[Link]}[!] Failed to get CIDR for {iface}. Ensure
IP is configured.{[Link]}")
return None, None, None
my_ip = [Link]('/')[0]

return iface, cidr, my_ip

except [Link] as e:
print_slowly(f"{[Link]}[!] Error running 'ip' command: {e}
{[Link]}")
print_slowly(f" Stderr: {[Link]}")
return None, None, None
except Exception as e:
print_slowly(f"{[Link]}[!] An unexpected error occurred: {e}
{[Link]}")
return None, None, None

def get_vendor_info(mac_address):
"""Retrieves vendor information for a given MAC address from an online API."""
if not mac_address or mac_address == "N/A" or
mac_address.startswith("00:00:00"):
return "N/A"

url = f"[Link]

try:
response = [Link](url, timeout=2)
response.raise_for_status()
data = [Link]()
return [Link]('company', 'N/A')
except [Link]:
return "N/A (Connection/API Error)"
except [Link]:
return "N/A (Invalid API format)"
except Exception:
return "N/A (Other Error)"

def scan_arp_cache(iface):
"""Scans the ARP cache for IPv4 devices on the specified interface."""
detected_devices = []

try:
# Using 'ip -4 neigh show dev IFACE' to directly filter IPv4 entries
result = [Link](['ip', '-4', 'neigh', 'show', 'dev', iface],
capture_output=True, text=True, check=True, timeout=10)
lines = [Link]()

for line in lines:


# Regex to match IPv4, MAC address, and status
# Example: "[Link] lladdr 3c:f6:52:10:cf:38 REACHABLE "
match = [Link](r"(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+lladdr\s+([0-
9a-fA-F:]+)\s+(\S+)", line)

if match:
ip = [Link](1)
mac = [Link](2)
status_raw = [Link](3)

status_clean = "N/A"
color = [Link]
if "REACHABLE" in status_raw:
status_clean = "REACHABLE"
color = [Link]
elif "STALE" in status_raw:
status_clean = "STALE"
color = [Link]
elif "DELAY" in status_raw:
status_clean = "DELAY"
color = [Link]
elif "PROBE" in status_raw:
status_clean = "PROBE"
color = [Link]
elif "INCOMPLETE" in status_raw:
status_clean = "INCOMPLETE"
color = [Link]
else:
status_clean = "UNKNOWN"
color = [Link]

if mac != "00:00:00:00:00:00": # Filter out invalid MACs


detected_devices.append({
'ip': ip,
'mac': mac,
'status': status_clean,
'color': color,
'vendor': "Fetching..."
})
except [Link]:
pass
except FileNotFoundError:
print_slowly(f"{[Link]}[!] 'ip' command not found. Please install
'iproute2' (pkg install iproute2).{[Link]}")
[Link](1)
except Exception as e:
print_slowly(f"{[Link]}[!] An error occurred during ARP scan: {e}
{[Link]}")

return detected_devices

# --- Main Program ---


if __name__ == "__main__":
try:
import requests
except ImportError:
print_slowly(f"{[Link]}[!] 'requests' module not found. Please install
it with: pip install requests{[Link]}")
[Link](1)

print_slowly(f"{[Link]}[*] Detecting network information...


{[Link]}")
IFACE, CIDR, MYIP = get_network_info()

if not IFACE or not CIDR or not MYIP:


[Link](1)

try:
# MAIN LOOP WITH 4-SECOND REFRESH
while True:
[Link]('clear') # Clear screen on each refresh
current_time = [Link]("%Y-%m-%d %H:%M:%S %Z", [Link]())
# Get current local time with timezone

# Using print_slowly for header and info

print_slowly(f"{[Link]}====================================================
==={[Link]}", delay=0.005)
print_slowly(f"{[Link]} NETSCAN - Network Scanner
(LIVE) {[Link]}", delay=0.005)

print_slowly(f"{[Link]}====================================================
==={[Link]}", delay=0.005)
print_slowly(f"{[Link]}[*] Network Interface :
{[Link]}{IFACE}{[Link]}", delay=0.01)
print_slowly(f"{[Link]}[*] Network Subnet :
{[Link]}{CIDR}{[Link]}", delay=0.01)
print_slowly(f"{[Link]}[*] Your Local IP :
{[Link]}{MYIP}{[Link]}", delay=0.01)
print_slowly(f"{[Link]}[*] Real-time Vendor Lookup :
{[Link]}Yes (Requires Internet){[Link]}", delay=0.01)
print_slowly(f"{[Link]}[*] Last Updated :
{[Link]}{current_time}{[Link]}", delay=0.01)

print_slowly(f"{[Link]}----------------------------------------------------
---{[Link]}", delay=0.005)
print() # Blank line remains instant

# Header tabel tetap instan agar cepat muncul


print_slowly(f"{'IP Address':<16} {'MAC Address':<18} {'Status':<10}
{'Vendor':<25}", delay=0.005)
print_slowly(f"{'-'*16} {'-'*18} {'-'*10} {'-'*25}", delay=0.005)
print()

print_slowly(f"{[Link]}[*] Scanning ARP cache for IPv4 devices...


{[Link]}", delay=0.02)

devices = scan_arp_cache(IFACE)

print()
print_slowly(f"{[Link]}[*] Scan Results:{[Link]}",
delay=0.02)
print_slowly(f"{'-'*75}", delay=0.005)

if not devices:
print_slowly(f"{[Link]}No IPv4 devices detected in ARP
cache.{[Link]}", delay=0.02)

# INI BAGIAN YANG DIUBAH: Output tabel sekarang pakai print_slowly


for device in devices:
vendor_name = get_vendor_info(device['mac'])
print_slowly(f"{device['ip']:<16} {device['mac']:<18}
{device['color']}{device['status']:<10}{[Link]} {vendor_name:<25}",
delay=0.005) # Delay lebih kecil untuk baris data
print() # Blank line remains instant
print() # Blank line remains instant

print_slowly(f"{[Link]}----------------------------------------------------
---{[Link]}", delay=0.005)
print_slowly(f"Total devices detected: {[Link]}{len(devices)}
{[Link]}", delay=0.02)

print_slowly(f"{[Link]}====================================================
==={[Link]}", delay=0.005)
print()

[Link](4) # Wait for 4 seconds before the next refresh

except KeyboardInterrupt:
print_slowly(f"\n{[Link]}[*] Scan interrupted by user. Exiting
gracefully.{[Link]}", delay=0.02)
[Link](0)
except Exception as e:
print_slowly(f"\n{[Link]}[!] An unexpected error occurred: {e}
{[Link]}", delay=0.02)
[Link](1)

[Link]

import speedtest
import time
from tqdm import tqdm
import os
import requests # Tambahkan library requests untuk keperluan pengecekan

# --- KONFIGURASI PENGATURAN ---


INTERVAL_DETIK = 5
THREADS_COUNT = 4

# Variabel global
start_time = 0
progress_bar = None
MAX_RETRIES = 3 # Maksimal percobaan ulang jika terjadi error konfigurasi
def format_speed(speed_bps):
return f"{(speed_bps / 1_000_000):.2f} Mbps"

# ----------------------------------------------------
# --- FUNGSI CALLBACK TQDM ---
# ----------------------------------------------------

def tqdm_callback(block, client, **kwargs):


global progress_bar, start_time

if 'start' in kwargs or 'end' in kwargs:


return

bytes_transferred = [Link]
elapsed_time = [Link]() - start_time

if elapsed_time > 0:
current_speed_bps = bytes_transferred / elapsed_time
current_speed_mbps = current_speed_bps / 1_000_000
progress_bar.set_description(f"Speed: {current_speed_mbps:.2f} Mbps")

progress_bar.update(len(block))

# ----------------------------------------------------
# --- FUNGSI UTAMA TES (SATU ITERASI) ---
# ----------------------------------------------------

def jalankan_speedtest_dengan_tqdm():
global start_time, progress_bar

print("\n" + "="*55)
print(f"🚀 MEMULAI TES BARU (Multicore: {THREADS_COUNT}) | Waktu:
{[Link]('%Y-%m-%d %H:%M:%S')}")
print("="*55)

# 1. Inisialisasi Speedtest dengan Retry


st = None
for retry in range(MAX_RETRIES):
try:
# Pengecekan koneksi dasar sebelum memanggil speedtest
[Link]("[Link] timeout=5)
st = [Link]()
break
except [Link]:
print(f"[Cek Koneksi]: Gagal Ping Google. Retrying {retry +
1}/{MAX_RETRIES}...")
[Link](2)
except [Link]:
print(f"[Config Error]: Gagal mengambil konfigurasi. Retrying {retry +
1}/{MAX_RETRIES}...")
[Link](2)
except Exception as e:
print(f"[Error Tak Terduga]: {e}. Retrying {retry +
1}/{MAX_RETRIES}...")
[Link](2)

if st is None:
print("❌ Gagal mendapatkan konfigurasi setelah beberapa kali percobaan.
Skip tes ini.")
return

# --- Lanjutkan Tes Jika Inisialisasi Berhasil ---


try:
# 2. Pilih Server Terbaik (Logika Robust)
print("⏳ Mencari server terbaik (latensi terendah)...")
st.get_servers() # Ambil daftar server
server_terbaik = st.get_best_server()

# Cek jika server ID ada


if not server_terbaik or 'id' not in server_terbaik:
print("❌ Gagal memilih server stabil (Metadata error). Skip tes ini.")
return

print(f"✅ Server Terpilih: {server_terbaik['name']} (Ping Awal:


{server_terbaik['latency']:.2f} ms)")

# 3. Dapatkan Ukuran File Uji Sebenarnya (untuk TQDM Akurat)


# Menggunakan logika yang lebih sederhana karena tes harus diulang
# untuk mendapatkan nilai ini, kita gunakan nilai dari test file yang
dipilih
TOTAL_BYTES_FINAL = 500_000 # Default jika gagal ditemukan
try:
# Ini adalah perkiraan, nilai yang benar akan didapatkan setelah
download dimulai
# Kita set estimasi awal yang wajar
TOTAL_BYTES_FINAL = st.get_best_server()['dlspeed'] * 1024 * 1024 / 8 #
Estimasi berdasarkan speed * 10 detik
if TOTAL_BYTES_FINAL < 500_000: TOTAL_BYTES_FINAL = 500_000
except Exception:
pass

# 4. Tes Download
print("\n⬇️ Memulai Tes Download...")
start_time = [Link]()
progress_bar = tqdm(total=int(TOTAL_BYTES_FINAL * 2), unit='B',
unit_scale=True, desc="Download")
[Link](threads=THREADS_COUNT, callback=tqdm_callback)
progress_bar.close()

# 5. Tes Upload
print("⬆️ Memulai Tes Upload...")
start_time = [Link]()
progress_bar = tqdm(total=int(TOTAL_BYTES_FINAL * 2), unit='B',
unit_scale=True, desc="Upload")
[Link](threads=THREADS_COUNT, callback=tqdm_callback)
progress_bar.close()

# 6. Tampilkan Hasil Akhir


results_dict = [Link]()

final_ping = results_dict['ping']
download_mbps = (results_dict['download'] / 1_000_000)
upload_mbps = (results_dict['upload'] / 1_000_000)

print("\n--- STATISTIK HASIL AKHIR ---")


print(f"Ping Rata-rata: {final_ping:.2f} ms")
print(f"Kecepatan Download (Rata-rata): {download_mbps:.2f} Mbps")
print(f"Kecepatan Upload (Rata-rata): {upload_mbps:.2f} Mbps")
print("------------------------------")

except Exception as e:
print(f"\n❌ Terjadi kesalahan fatal saat tes: {e}")

# ----------------------------------------------------
# --- FUNGSI LOOPING UTAMA ---
# ----------------------------------------------------

def main_loop():
"""Fungsi utama untuk menjalankan tes dalam loop tanpa batas."""
print("Memastikan pustaka 'speedtest-cli', 'tqdm', dan 'requests'
terinstal...")

try:
while True:
jalankan_speedtest_dengan_tqdm()
print(f"\nJeda selama {INTERVAL_DETIK} detik sebelum tes
berikutnya...")
[Link](INTERVAL_DETIK)

except KeyboardInterrupt:
print("\n\n👋 Tes berulang dihentikan oleh pengguna (Ctrl+C). Terima
kasih!")

except Exception as e:
print(f"\nTerjadi kesalahan fatal pada loop: {e}")

if __name__ == "__main__":
main_loop()

[Link]

import socket
import json
import time
import sys
import os
from datetime import datetime
import requests
import re # Pastikan re diimpor

# --- Konfigurasi Warna ANSI ---


class Colors:
RED = '\033[31m'
GREEN = '\033[32m'
YELLOW = '\033[33m'
BLUE = '\033[34m'
MAGENTA = '\033[35m'
CYAN = '\033[36m'
WHITE = '\033[37m'
GRAY = '\033[90m'
RESET = '\033[0m'

# --- Fungsi untuk Mencetak Teks Secara Bertahap (Efek Hacker) ---
def print_slowly(text, delay=0.01, end='\n'):
"""Mencetak teks karakter per karakter dengan jeda."""
for char in text:
[Link](char)
[Link]()
[Link](delay)
[Link](end)
[Link]()

# --- Fungsi Deteksi Banner yang Ditingkatkan ---


def get_banner(ip, port, timeout=1):
try:
sock = [Link](socket.AF_INET, socket.SOCK_STREAM)
[Link](timeout)
[Link]((ip, port))

if port in [80, 443, 8080, 8000, 8443]:


[Link](b"HEAD / HTTP/1.1\r\nHost: " + [Link]() + b"\r\nUser-
Agent: IPublic_Scanner/5.0\r\nConnection: close\r\n\r\n")
response = [Link](4096).decode('utf-8', errors='ignore')
if response:
server_header = next((line for line in [Link]() if
[Link]().startswith('server:')), None)
if server_header:
return server_header.strip()
elif "HTTP/" in response:
return [Link]()[0].strip() + " (HTTP/S Response)"
return "No specific HTTP/S banner (Service Running)"
elif port == 21:
banner = [Link](1024).decode('utf-8', errors='ignore').strip()
if [Link]('220'):
return banner
return "No specific FTP banner (Service Running)"
elif port == 22:
banner = [Link](1024).decode('utf-8', errors='ignore').strip()
if [Link]('SSH-'):
return banner
return "No specific SSH banner (Service Running)"
else:
banner = [Link](1024).decode('utf-8', errors='ignore').strip()
if banner:
return banner
return "No specific banner (Service Running)"
except [Link]:
return "Gagal mengambil banner: timed out"
except (ConnectionRefusedError, OSError):
return "Gagal mengambil banner: connection refused or error"
except Exception as e:
return f"Gagal mengambil banner: {e}"
finally:
[Link]()

# --- Fungsi Deteksi OS Dasar dari Banner ---


def get_os_from_banner(banner):
banner_lower = [Link]()
if "ubuntu" in banner_lower or "debian" in banner_lower:
return "Linux (Ubuntu/Debian)"
elif "centos" in banner_lower or "red hat" in banner_lower or "rhel" in
banner_lower:
return "Linux (CentOS/Red Hat)"
elif "windows" in banner_lower or "microsoft" in banner_lower or "iis" in
banner_lower:
return "Windows"
elif "freebsd" in banner_lower or "netbsd" in banner_lower or "openbsd" in
banner_lower:
return "BSD"
elif "mikrotik" in banner_lower or "routeros" in banner_lower or "lancom" in
banner_lower:
return "MikroTik RouterOS / Network Device"
elif "cisco" in banner_lower:
return "Cisco IOS / Network Device"
return "Tidak Dikenal/Generic"

# --- Fungsi GeoIP Lookup ---


def get_geoip_info(ip_address):
print_slowly(f"\n[*] Melakukan GeoIP lookup untuk {ip_address}...", delay=0.02)
try:
response = [Link](f"[Link]
fields=status,message,country,countryCode,regionName,city,zip,lat,lon,isp,org,as,qu
ery")
data = [Link]()
if data['status'] == 'success':
return data
else:
return {"status": "error", "message": [Link]('message', 'Unknown
error')}
except [Link] as e:
print_slowly(f"{[Link]}[!] Gagal melakukan GeoIP lookup: {e}
{[Link]}", delay=0.01)
return {"status": "error", "message": f"Request error: {e}"}
except Exception as e:
print_slowly(f"{[Link]}[!] Terjadi kesalahan tak terduga pada GeoIP:
{e}{[Link]}", delay=0.01)
return {"status": "error", "message": f"Unexpected error: {e}"}

# --- Fungsi WHOIS Lookup ---


def perform_whois_lookup(ip_address, timeout=10):
print_slowly(f"\n[*] Melakukan WHOIS lookup untuk {ip_address}...", delay=0.02)
whois_server = "[Link]"

try:
sock = [Link](socket.AF_INET, socket.SOCK_STREAM)
[Link](timeout)
[Link]((whois_server, 43))
[Link](f"{ip_address}\r\n".encode())

response = b""
while True:
data = [Link](4096)
if not data:
break
response += data

[Link]()

whois_output = [Link]('utf-8', errors='ignore')

referral_match = None
for line in whois_output.splitlines():
line_lower = [Link]().lower()
if line_lower.startswith("referralserver:"):
referral_server = [Link](":")[1].strip()
if not referral_server.startswith("whois.") and "." in
referral_server:
referral_server = "whois." + referral_server
referral_match = referral_server
break
elif "[Link]" in line_lower and "referral" not in line_lower:
referral_match = "[Link]"
break
elif "[Link]" in line_lower and "referral" not in line_lower:
referral_match = "[Link]"
break
elif "[Link]" in line_lower and "referral" not in line_lower:
referral_match = "[Link]"
break
elif "[Link]" in line_lower and "referral" not in
line_lower:
referral_match = "[Link]"
break

if referral_match and referral_match != whois_server:


print_slowly(f"{[Link]}[*] Ditemukan referral ke
{referral_match}. Melakukan WHOIS ulang...{[Link]}", delay=0.01)
try:
sock = [Link](socket.AF_INET, socket.SOCK_STREAM)
[Link](timeout)
[Link]((referral_match, 43))
[Link](f"{ip_address}\r\n".encode())

response_referred = b""
while True:
data = [Link](4096)
if not data:
break
response_referred += data
[Link]()
whois_output = response_referred.decode('utf-8', errors='ignore')
print_slowly(f"{[Link]}[+] Referral WHOIS berhasil diikuti.
{[Link]}", delay=0.01)
except Exception as e:
print_slowly(f"{[Link]}[!] Gagal mengikuti referral WHOIS: {e}
{[Link]}", delay=0.01)

return whois_output

except [Link]:
print_slowly(f"{[Link]}[!] WHOIS lookup timed out.{[Link]}",
delay=0.01)
return "WHOIS lookup timed out."
except Exception as e:
print_slowly(f"{[Link]}[!] Gagal melakukan WHOIS lookup: {e}
{[Link]}", delay=0.01)
return f"Gagal melakukan WHOIS lookup: {e}"

# --- Fungsi untuk mendapatkan informasi Port dan Risiko ---


def get_port_info(port, banner):
problem = "Normal/Informasional"
function = "Layanan Umum"
banner_lower = [Link]()

if port == 20:
function = "FTP Data (File Transfer Protocol)"
problem = "Biasanya digunakan dengan FTP. Jika port 21 juga terbuka, bisa
jadi ada risiko transfer data tidak terenkripsi."
elif port == 21:
function = "FTP Control (File Transfer Protocol)"
problem = "Sering digunakan untuk transfer file. Rentan terhadap brute-
force, kebocoran kredensial (jika tanpa FTPS/SFTP) dan kerentanan server FTP."
if "proftpd" in banner_lower or "vsftpd" in banner_lower:
problem += " Periksa versi untuk CVEs yang diketahui."
elif port == 22:
function = "SSH (Secure Shell)"
problem = "Akses remote terenkripsi. Risiko brute-force, kata sandi lemah,
atau kerentanan OpenSSH lama. Pastikan hanya menggunakan autentikasi key-based."
if "openssh" in banner_lower:
problem += " Periksa versi OpenSSH untuk CVEs."
elif port == 23:
function = "Telnet"
problem = "!!! RISIKO TINGGI: Tidak terenkripsi, semua lalu lintas
(termasuk kredensial) dapat disadap. Sering digunakan pada perangkat IoT/embedded
dengan default credentials."
if "login" in banner_lower or "busybox" in banner_lower or "buildroot" in
banner_lower:
problem += " Indikasi akses login langsung dan mungkin perangkat
embedded."
elif port == 80:
function = "HTTP (Web Server)"
problem = "Web server tidak terenkripsi. Rentan terhadap serangan aplikasi
web (SQLi, XSS, dll.) dan kebocoran informasi. Seharusnya dialihkan ke HTTPS."
if "apache" in banner_lower or "nginx" in banner_lower or "iis" in
banner_lower:
problem += f" Server web: {[Link]('Server:')[-1].strip() if
'server:' in banner_lower else banner}."
if "server: 360" in banner_lower or "waf" in banner_lower or len(banner) >
100:
problem += " Kemungkinan di belakang WAF/Proxy. Perlu eksplorasi lebih
lanjut."
elif port == 110:
function = "POP3 (Post Office Protocol 3)"
problem = "Menerima email. Seringkali tanpa enkripsi, rentan terhadap
penyadapan kredensial."
elif port == 143:
function = "IMAP (Internet Message Access Protocol)"
problem = "Menerima email. Seringkali tanpa enkripsi, rentan terhadap
penyadapan kredensial."
elif port == 443:
function = "HTTPS (Secure Web Server)"
problem = "Web server terenkripsi (SSL/TLS). Risiko serangan aplikasi web,
konfigurasi SSL/TLS yang lemah (misal: TLS 1.0/1.1, cipher suite lemah), atau
sertifikat kadaluarsa."
if "server:" in banner_lower:
problem += f" Server web: {[Link]('Server:')[-1].strip() if
'server:' in banner_lower else banner}."
elif "connection refused or error" in banner_lower:
problem += " Gagal mengambil banner SSL/TLS, mungkin karena timeout
atau konfigurasi ketat. Perlu cek manual."
elif port == 993:
function = "IMAPS (Secure IMAP)"
problem = "IMAP terenkripsi. Lebih aman, tapi tetap periksa konfigurasi
SSL/TLS dan versi server."
elif port == 995:
function = "POP3S (Secure POP3)"
problem = "POP3 terenkripsi. Lebih aman, tapi tetap periksa konfigurasi
SSL/TLS dan versi server."
elif port == 3389:
function = "RDP (Remote Desktop Protocol)"
problem = "!!! RISIKO TINGGI: Akses remote ke desktop Windows. Sangat
rentan terhadap brute-force, NTLM relay, dan kerentanan sistem RDP."
elif port == 5900:
function = "VNC (Virtual Network Computing)"
problem = "!!! RISIKO TINGGI: Akses remote ke desktop. Seringkali tanpa
enkripsi atau dengan autentikasi lemah. Sangat berisiko."
elif port == 8000 or port == 8080 or port == 8443:
function = "HTTP/S Alternatif (Web Server/Proxy)"
problem = "Server web atau proxy. Sama seperti port 80/443, periksa
aplikasi web dan konfigurasinya."
if "apache" in banner_lower or "nginx" in banner_lower or "iis" in
banner_lower:
problem += f" Server web: {[Link]('Server:')[-1].strip() if
'server:' in banner_lower else banner}."
if "server: 360" in banner_lower or "waf" in banner_lower or len(banner) >
100:
problem += " Kemungkinan di belakang WAF/Proxy. Perlu eksplorasi lebih
lanjut."
else:
problem = "Layanan yang tidak umum atau tidak teridentifikasi. Perlu
penyelidikan lebih lanjut."
return problem, function

# --- Main Program ---


def main():
[Link]('clear' if [Link] == 'posix' else 'cls')

scan_results = []
whois_full_output = ""
geoip_results = {}
abuse_email_found = "Tidak ditemukan"
print_slowly(f"{[Link]}====================================================
==={[Link]}", delay=0.005)
print_slowly(f"{[Link]} SIMPLE PUBLIC IP SCANNER (Python)
{[Link]}", delay=0.005)
print_slowly(f"{[Link]} (Penetration Tester Tools) {[Link]}",
delay=0.005)

print_slowly(f"{[Link]}====================================================
==={[Link]}", delay=0.005)
print()

target_ip = input(f"{[Link]}Masukkan IP Publik Target (Contoh: [Link]):


{[Link]}")
print([Link], end='')

ports = [20, 21, 22, 23, 80, 110, 143, 443, 993, 995, 3389, 5900, 8000, 8080,
8443]
open_ports_count = 0
scan_timeout = 0.7

print_slowly(f"\n[*] Memulai pemindaian port pada {target_ip}...", delay=0.02)


print_slowly(f" (Ini mungkin butuh waktu tergantung jumlah port dan timeout
{scan_timeout} detik per port)", delay=0.01)

for port in ports:


try:
sock = [Link](socket.AF_INET, socket.SOCK_STREAM)
[Link](scan_timeout)
result = sock.connect_ex((target_ip, port))

if result == 0:
open_ports_count += 1
banner = get_banner(target_ip, port, timeout=scan_timeout)
os_guess = get_os_from_banner(banner)

problem_desc, function_desc = get_port_info(port, banner)

scan_results.append({
"port": port,
"banner": banner,
"os_guess": os_guess,
"problem": problem_desc,
"function": function_desc
})

print_slowly(f"{[Link]}[+] Port {port} Terbuka!...",


delay=0.005)
print_slowly(f" Banner: {banner}{[Link]}", delay=0.001)
if os_guess != "Tidak Dikenal/Generic":
print_slowly(f" OS Dugaan: {os_guess}{[Link]}",
delay=0.001)
[Link]()
except [Link]:
print_slowly(f"{[Link]}[!] Gagal resolving hostname: {target_ip}
{[Link]}")
[Link]()
except [Link] as e:
print_slowly(f"{[Link]}[!] Kesalahan koneksi pada port {port}: {e}
{[Link]}", delay=0.005)
except Exception as e:
print_slowly(f"{[Link]}[!] Terjadi kesalahan tak terduga pada port
{port}: {e}{[Link]}", delay=0.005)

print_slowly(f"\n[+] Pemindaian selesai. Ditemukan {open_ports_count} port


terbuka.", delay=0.02)

# WHOIS & GeoIP Lookup


# No user interaction for WHOIS/GeoIP, always perform it for auto-download
geoip_results = get_geoip_info(target_ip)
if geoip_results['status'] == 'success':
print_slowly(f"\n--- HASIL GEOIP ---{[Link]}", delay=0.01)
print_slowly(f" Negara: {geoip_results.get('country')}
({geoip_results.get('countryCode')})", delay=0.001)
print_slowly(f" Wilayah: {geoip_results.get('regionName')}", delay=0.001)
print_slowly(f" Kota: {geoip_results.get('city')}", delay=0.001)
print_slowly(f" ISP: {geoip_results.get('isp')}", delay=0.001)
print_slowly(f" Organisasi: {geoip_results.get('org')}", delay=0.001)
print_slowly(f" ASN: {geoip_results.get('as')}", delay=0.001)
print_slowly(f"--- AKHIR HASIL GEOIP ---{[Link]}", delay=0.01)
else:
print_slowly(f"{[Link]}[!] GeoIP lookup gagal:
{geoip_results.get('message')}{[Link]}", delay=0.01)

whois_full_output = perform_whois_lookup(target_ip)
print_slowly(f"\n--- HASIL WHOIS ---{[Link]}", delay=0.01)
print_slowly(whois_full_output, delay=0.001)
print_slowly(f"\n--- AKHIR HASIL WHOIS ---{[Link]}", delay=0.01)

abuse_patterns = ["abuse-mailbox:", "orgabuseemail:", "abuse@", "abuse_c:",


"abuse-c:", "[Link]:",
"contact abuse", "report abuse to", "email for abuse",
"spam@"]

for line in whois_full_output.splitlines():


line_lower = [Link]()
for pattern in abuse_patterns:
if pattern in line_lower:
if "@" in line_lower:
email_match = None
email_regex = [Link](r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.
[A-Z|a-z]{2,}\b', line)
if email_regex:
email_match = email_regex.group(0)

if email_match:
abuse_email_found = email_match
break
if abuse_email_found != "Tidak ditemukan":
break

print_slowly(f"\n[+] Kemungkinan Alamat Email Abuse/Kontak Keamanan:


{[Link]}{abuse_email_found}{[Link]}", delay=0.01)

# --- Bagian Ringkasan dan Kesimpulan ---


print_slowly(f"\n{[Link]}--- RINGKASAN PEMINDAIAN ---{[Link]}",
delay=0.02)
print_slowly(f"Target IP: {target_ip}", delay=0.01)
print_slowly(f"Jumlah Port Terbuka: {open_ports_count}", delay=0.01)

if geoip_results and geoip_results['status'] == 'success':


print_slowly(f"Lokasi: {geoip_results.get('city')},
{geoip_results.get('regionName')}, {geoip_results.get('country')}", delay=0.01)
print_slowly(f"ISP: {geoip_results.get('isp')}", delay=0.01)
print_slowly(f"Organisasi: {geoip_results.get('org')}", delay=0.01)
else:
print_slowly("Lokasi/ISP: Informasi GeoIP tidak tersedia.", delay=0.01)

print_slowly(f"Kontak Abuse Ditemukan: {abuse_email_found}", delay=0.01)

# --- Tabel Ringkasan Port Terbuka ---


print_slowly(f"\n{[Link]}--- TABEL RINGKASAN PORT TERBUKA ---
{[Link]}", delay=0.02)
if scan_results:
# Menentukan lebar kolom maksimum untuk formatting
max_os_len = max(len("OS Dugaan"), max(len(res["os_guess"]) for res in
scan_results))
max_port_len = max(len("Port"), max(len(str(res["port"])) for res in
scan_results))
max_problem_len = max(len("Potensi Masalah/Risiko"),
max(len(res["problem"]) for res in scan_results))
max_function_len = max(len("Fungsi Umum"), max(len(res["function"]) for res
in scan_results))

# Batasi panjang string agar tidak terlalu lebar di terminal


max_problem_len = min(max_problem_len, 70) # Batasi problem deskripsi
max_function_len = min(max_function_len, 30) # Batasi fungsi deskripsi

header_format = f"{{:<{max_os_len}}} {{:<{max_port_len}}}


{{:<{max_problem_len}}} {{:<{max_function_len}}}"
print_slowly(header_format.format("OS Dugaan", "Port", "Potensi
Masalah/Risiko", "Fungsi Umum"), delay=0.005)
print_slowly(header_format.format("-" * max_os_len, "-" * max_port_len, "-"
* max_problem_len, "-" * max_function_len), delay=0.005)

for res in scan_results:


# Memotong string jika terlalu panjang
problem_truncated = (res["problem"][:max_problem_len-3] + "...") if
len(res["problem"]) > max_problem_len else res["problem"]
function_truncated = (res["function"][:max_function_len-3] + "...") if
len(res["function"]) > max_function_len else res["function"]

print_slowly(header_format.format(
res["os_guess"],
res["port"],
problem_truncated,
function_truncated
), delay=0.001)
else:
print_slowly("Tidak ada port terbuka yang ditemukan.", delay=0.01)

print_slowly(f"\n{[Link]}--- KESIMPULAN AWAL ---{[Link]}",


delay=0.02)
conclusion_notes = []

if open_ports_count == 0:
conclusion_notes.append(f"{[Link]}Sistem terlihat cukup tertutup dari
luar. Tidak ada port umum yang ditemukan terbuka. Ini adalah konfigurasi keamanan
yang baik.{[Link]}")
else:
conclusion_notes.append(f"{[Link]}Ditemukan {open_ports_count} port
terbuka. Ini meningkatkan 'surface area' yang dapat dieksploitasi.{[Link]}")

has_critical_port = False
for res in scan_results:
if res['port'] in [23, 3389, 5900]: # Telnet, RDP, VNC
conclusion_notes.append(f"{[Link]}[RISIKO TINGGI] Port
{res['port']} ({res['function']}) Terbuka: {res['problem']}{[Link]}")
has_critical_port = True
elif res['port'] in [21, 22]: # FTP, SSH
conclusion_notes.append(f"{[Link]}[PERINGATAN] Port
{res['port']} ({res['function']}) Terbuka: {res['problem']}{[Link]}")
elif res['port'] in [80, 443, 8000, 8080, 8443]: # HTTP/S
conclusion_notes.append(f"{[Link]}[INFORMASI] Port
{res['port']} ({res['function']}) Terbuka: {res['problem']}{[Link]}")
else: # Port lain yang mungkin tidak biasa
conclusion_notes.append(f"{[Link]}[PERINGATAN] Port
{res['port']} ({res['function']}) Terbuka: {res['problem']}{[Link]}")

if has_critical_port:
conclusion_notes.append(f"\n{[Link]}Tindakan mendesak diperlukan
untuk mengamankan port-port berisiko tinggi. Konsultasikan dengan admin sistem atau
penyedia hosting.{[Link]}")
else:
conclusion_notes.append(f"\n{[Link]}Meskipun ada port terbuka,
tidak ada kerentanan *sangat kritis* yang langsung terdeteksi dari pemindaian port
dasar ini. Tetap perhatikan keamanan aplikasi dan layanan yang berjalan.
{[Link]}")

for note in conclusion_notes:


print_slowly(note, delay=0.01)

# --- Rekomendasi ---


print_slowly(f"\n{[Link]}--- REKOMENDASI ---{[Link]}", delay=0.02)
recommendations = []

if open_ports_count > 0:
[Link](f"{[Link]}Secara umum, tinjau kembali setiap
port yang ditemukan terbuka. Tanyakan: Apakah layanan ini benar-benar diperlukan
untuk diakses dari internet? Jika tidak, tutup atau batasi aksesnya.
{[Link]}")

for res in scan_results:


if res['port'] == 23: # Telnet
[Link](f"{[Link]}* Port 23 (Telnet): Segera
nonaktifkan Telnet dan ganti dengan SSH (Port 22) untuk akses remote terenkripsi.
{[Link]}")
elif res['port'] == 3389: # RDP
[Link](f"{[Link]}* Port 3389 (RDP): Batasi
akses RDP hanya dari IP yang dikenal melalui firewall, gunakan VPN, terapkan kata
sandi kuat, dan aktifkan MFA jika tersedia.{[Link]}")
elif res['port'] == 5900: # VNC
[Link](f"{[Link]}* Port 5900 (VNC): Nonaktifkan
VNC jika tidak perlu. Jika harus digunakan, batasi akses IP, gunakan kata sandi
yang sangat kuat, dan pastikan enkripsi diaktifkan atau tunnel melalui SSH.
{[Link]}")
elif res['port'] == 21: # FTP
[Link](f"{[Link]}* Port 21 (FTP): Migrasikan
ke SFTP/FTPS untuk transfer file terenkripsi. Nonaktifkan akses anonim dan gunakan
kata sandi kuat.{[Link]}")
elif res['port'] == 22: # SSH
[Link](f"{[Link]}* Port 22 (SSH): Wajibkan
autentikasi berbasis kunci SSH. Nonaktifkan login root langsung. Pertimbangkan
penggunaan Fail2Ban.{[Link]}")
elif res['port'] == 80: # HTTP
[Link](f"{[Link]}* Port 80 (HTTP): Wajibkan
pengalihan ke HTTPS (Port 443) untuk semua lalu lintas. Terapkan HSTS.
{[Link]}")
elif res['port'] == 443: # HTTPS
[Link](f"{[Link]}* Port 443 (HTTPS): Pastikan
konfigurasi SSL/TLS kuat (minimal TLS 1.2/1.3), gunakan cipher suite yang aman, dan
sertifikat SSL/TLS yang valid.{[Link]}")
elif res['port'] in [110, 143]: # POP3/IMAP
[Link](f"{[Link]}* Port {res['port']}
(Email): Gunakan versi terenkripsi (POP3S/995 atau IMAPS/993) dan nonaktifkan port
tidak terenkripsi jika memungkinkan.{[Link]}")
elif res['port'] in [8000, 8080, 8443]: # Alternatif HTTP/S
[Link](f"{[Link]}* Port {res['port']} (HTTP/S
Alternatif): Terapkan praktik keamanan yang sama seperti pada Port 80 dan 443.
{[Link]}")
else: # Port lainnya
[Link](f"{[Link]}* Port {res['port']}:
Lakukan penyelidikan lebih lanjut untuk mengidentifikasi layanan yang berjalan dan
risiko terkait. Jika tidak diperlukan, tutup port ini.{[Link]}")

[Link](f"\n{[Link]}--- Rekomendasi Keamanan Umum ---


{[Link]}")
[Link](f"{[Link]}* Terapkan prinsip *Least Privilege*
(hanya buka port yang benar-benar diperlukan).{[Link]}")
[Link](f"{[Link]}* Konfigurasikan *firewall* secara
ketat.{[Link]}")
[Link](f"{[Link]}* Lakukan pembaruan sistem operasi
dan aplikasi secara rutin.{[Link]}")
[Link](f"{[Link]}* Aktifkan dan pantau *logging*
untuk semua layanan.{[Link]}")
[Link](f"{[Link]}* Gunakan kata sandi yang kuat dan
autentikasi multifaktor (MFA).{[Link]}")
[Link](f"{[Link]}* Pertimbangkan segmentasi jaringan
untuk memisahkan server yang terekspos.{[Link]}")
[Link](f"{[Link]}* Lakukan *penetration testing*
secara berkala.{[Link]}")
[Link](f"{[Link]}* Manfaatkan email kontak abuse
(jika ditemukan) untuk melaporkan aktivitas mencurigakan.{[Link]}")
else:
[Link](f"{[Link]}Tidak ada port umum yang ditemukan
terbuka, sehingga rekomendasi spesifik port tidak diperlukan. Pertahankan
konfigurasi keamanan yang ketat ini.{[Link]}")

for rec in recommendations:


print_slowly(rec, delay=0.01)

# --- Otomatis Simpan Hasil ke File (Auto Download) ---


print_slowly(f"\n{[Link]}[*] Secara otomatis menyimpan hasil pemindaian ke
file...", delay=0.02)
# Default ke Markdown
file_extension = ".md"

# BARIS INI YANG DIUBAH!


filename = f"{target_ip.replace('.', '_')}_report{file_extension}"

try:
with open(filename, 'w') as f:
# Menulis konten ke file
[Link](f"# Laporan Pemindaian IP Publik: {target_ip}\n" if
file_extension == ".md" else f"Laporan Pemindaian IP Publik: {target_ip}\n")
[Link](f"**Tanggal Pemindaian**: {[Link]().strftime('%Y-%m-%d
%H:%M:%S')}\n\n" if file_extension == ".md" else f"Tanggal Pemindaian:
{[Link]().strftime('%Y-%m-%d %H:%M:%S')}\n\n")

[Link]("## Ringkasan Pemindaian\n" if file_extension == ".md" else


"RINGKASAN PEMINDAIAN\n")
[Link](f"- **Target IP**: {target_ip}\n" if file_extension == ".md"
else f"Target IP: {target_ip}\n")
[Link](f"- **Jumlah Port Terbuka**: {open_ports_count}\n" if
file_extension == ".md" else f"Jumlah Port Terbuka: {open_ports_count}\n")
if geoip_results and geoip_results['status'] == 'success':
[Link](f"- **Lokasi**: {geoip_results.get('city')},
{geoip_results.get('regionName')}, {geoip_results.get('country')}\n" if
file_extension == ".md" else f"Lokasi: {geoip_results.get('city')},
{geoip_results.get('regionName')}, {geoip_results.get('country')}\n")
[Link](f"- **ISP**: {geoip_results.get('isp')}\n" if
file_extension == ".md" else f"ISP: {geoip_results.get('isp')}\n")
[Link](f"- **Organisasi**: {geoip_results.get('org')}\n" if
file_extension == ".md" else f"Organisasi: {geoip_results.get('org')}\n")
else:
[Link]("- **Lokasi/ISP**: Informasi GeoIP tidak tersedia.\n" if
file_extension == ".md" else "Lokasi/ISP: Informasi GeoIP tidak tersedia.\n")
[Link](f"- **Kontak Abuse Ditemukan**: `{abuse_email_found}`\n\n" if
file_extension == ".md" else f"Kontak Abuse Ditemukan: {abuse_email_found}\n\n")

[Link]("## Tabel Ringkasan Port Terbuka\n" if file_extension == ".md"


else "TABEL RINGKASAN PORT TERBUKA\n")
if scan_results:
header_format_file = f"{{:<{max_os_len}}} {{:<{max_port_len}}}
{{:<{max_problem_len}}} {{:<{max_function_len}}}"
[Link](header_format_file.format("OS Dugaan", "Port", "Potensi
Masalah/Risiko", "Fungsi Umum") + "\n")
[Link](header_format_file.format("-" * max_os_len, "-" *
max_port_len, "-" * max_problem_len, "-" * max_function_len) + "\n")

for res in scan_results:


problem_truncated = (res["problem"][:max_problem_len-3] +
"...") if len(res["problem"]) > max_problem_len else res["problem"]
function_truncated = (res["function"][:max_function_len-3] +
"...") if len(res["function"]) > max_function_len else res["function"]
[Link](header_format_file.format(
res["os_guess"],
res["port"],
problem_truncated,
function_truncated
) + "\n")
else:
[Link]("Tidak ada port terbuka yang ditemukan.\n")
[Link]("\n")

[Link]("## Kesimpulan Awal\n" if file_extension == ".md" else


"KESIMPULAN AWAL\n")
for note in conclusion_notes:
clean_note = [Link]([Link], '').replace([Link],
'').replace([Link], '').replace([Link], '').replace([Link], '')
[Link](f"- {clean_note}\n" if file_extension == ".md" else
f"{clean_note}\n")
[Link]("\n")

[Link]("## Rekomendasi\n" if file_extension == ".md" else


"REKOMENDASI\n")
for rec_line in recommendations:
clean_rec_line = rec_line.replace([Link],
'').replace([Link], '').replace([Link], '').replace([Link],
'').replace([Link], '')
[Link](f"- {clean_rec_line}\n" if file_extension == ".md" else
f"{clean_rec_line}\n")
[Link]("\n")

[Link]("## Informasi GeoIP Lengkap\n" if file_extension == ".md" else


"INFORMASI GEOIP LENGKAP\n")
if geoip_results and geoip_results['status'] == 'success':
[Link]("```json\n" if file_extension == ".md" else "")
[Link]([Link](geoip_results, indent=2))
[Link]("\n```\n" if file_extension == ".md" else "\n")
else:
[Link](f"GeoIP lookup gagal: {geoip_results.get('message', 'Tidak
ada data.')}\n")
[Link]("\n")

[Link]("## Hasil WHOIS Lookup Lengkap\n" if file_extension == ".md"


else "HASIL WHOIS LOOKUP LENGKAP\n")
[Link]("```\n" if file_extension == ".md" else "")
[Link](whois_full_output)
[Link]("\n```\n" if file_extension == ".md" else "\n")

print_slowly(f"{[Link]}[+] Hasil pemindaian disimpan ke: {filename}


{[Link]}", delay=0.01)
except Exception as e:
print_slowly(f"{[Link]}[!] Gagal menyimpan hasil ke file: {e}
{[Link]}", delay=0.01)

print_slowly(f"\
n{[Link]}======================================================={[Link]
ET}", delay=0.005)

if __name__ == "__main__":
try:
import requests
except ImportError:
print(f"{[Link]}Error: Library 'requests' tidak ditemukan.")
print(f"Silakan instal dengan perintah: pip install
requests{[Link]}")
[Link](1)
try:
main()
except KeyboardInterrupt:
print_slowly(f"\n{[Link]}[*] Program dihentikan oleh pengguna.
Keluar dengan rapi.{[Link]}", delay=0.02)
[Link](0)
except Exception as e:
print_slowly(f"\n{[Link]}[!] Terjadi kesalahan tak terduga: {e}
{[Link]}", delay=0.02)
[Link](1)

[Link]

import re
import base64
import hashlib
import time
import os
import math
import sys
from tabulate import tabulate
from periodictable import elements
from typing import Dict, List, Tuple, Iterable
import itertools
import subprocess
from shlex import quote

# --- Kriptografi Modern: Import cryptography ---


try:
from [Link] import Cipher, algorithms, modes
from [Link] import padding
from [Link] import default_backend
except ImportError:
print(f"Error: Library 'cryptography' tidak ditemukan. Harap instal dengan: pip
install cryptography")
[Link]()

# --- Import Colorama ---


try:
from colorama import Fore, Style, init
init(autoreset=True)
CLEAR_LINE = '\r' + ' ' * 80 + '\r'
CURSOR_UP_2 = '\033[2A'
except ImportError:
class DummyColor:
def __getattr__(self, name): return ""
Fore = DummyColor()
Style = DummyColor()
CLEAR_LINE = '\r'
CURSOR_UP_2 = ''

# GENDER MAPPING
GENDER_ATOM_DECODING: Dict[str, int] = {"P": 15, "S": 16}
GENDER_DECODED_MAPPING: Dict[int, str] = {15: "Pria", 16: "Wanita"}

# --------------------------------------------------------------------
# --- KONFIGURASI V46.16: Fix SyntaxWarning \m ---
# --------------------------------------------------------------------
TOTAL_COMBINATIONS = 10**12 # Batas atas (0 hingga 999,999,999,999)

# KONFIGURASI CHUNKING
CHUNK_SIZE = 100_000
TOTAL_WORKERS = 2

FOUND_KEY = None

# Mutex dan Sync


LOG_INTERVAL = 25
SYNC_CHECK_INTERVAL = 100000

SESSION_NAME = "BFA_PYTHON_RUNNER"
LOG_FILE = "FOUND_KEY.log"
LOCK_FILE = "TERMINAL_LOCK.lock"
FOUND_KEY_NUMBER_FILE = "FOUND_KEY_NUMBER.tmp"

# VIRTUAL CHUNK UNIT


VIRTUAL_CHUNK_UNIT = CHUNK_SIZE // TOTAL_WORKERS # 50,000
# --------------------------------------------------------------------

# --- FUNGSI UTILITY & KRIPTOGRAFI ---


def format_elapsed_time(seconds: float) -> str:
seconds = int(seconds)
if seconds >= 86400:
days = seconds // 86400
seconds %= 86400
hours = seconds // 3600
minutes = (seconds % 3600) // 60
seconds = seconds % 60
return f"{days:02d}:{hours:02d}:{minutes:02d}:{seconds:02d}"
else:
hours = seconds // 3600
minutes = (seconds % 3600) // 60
seconds = seconds % 60
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"

def time_to_seconds(time_str: str) -> float:


parts = time_str.split(':')
if len(parts) == 4:
days, hours, minutes, seconds = map(int, parts)
return float(days * 86400 + hours * 3600 + minutes * 60 + seconds)
elif len(parts) == 3: # Format HH:MM:SS
hours, minutes, seconds = map(int, parts)
return float(hours * 3600 + minutes * 60 + seconds)
return 0.0

def decrypt_aes_128_cbc(iv_bytes: bytes, ciphertext_bytes: bytes, password: str) ->


str | None:
try:
key = hashlib.sha256([Link]('utf-8')).digest()[:16]
iv = iv_bytes
ciphertext = ciphertext_bytes
cipher = Cipher([Link](key), [Link](iv),
backend=default_backend())
decryptor = [Link]()
decrypted_padded = [Link](ciphertext) + [Link]()
padder = padding.PKCS7([Link].block_size).unpadder()
decrypted_bytes = [Link](decrypted_padded) + [Link]()
return decrypted_bytes.decode('utf-8')
except (ValueError, TypeError, Exception):
return None

def check_for_found_key() -> Tuple[str | None, str | None, str | None]:


if [Link](LOG_FILE) and [Link](FOUND_KEY_NUMBER_FILE):
try:
with open(LOG_FILE, 'r') as f:
key_line, time_line = [Link]().strip().split('\n')
with open(FOUND_KEY_NUMBER_FILE, 'r') as f:
key_number_line = [Link]().strip()
return key_line, time_line, key_number_line
except IOError:
pass
return None, None, None

def write_found_key(key: str, total_time: str):


try:
with open(LOG_FILE, 'w') as f:
[Link](f"{key}\n{total_time}")
with open(FOUND_KEY_NUMBER_FILE, 'w') as f:
[Link](key)
except IOError as e:
print(f"{[Link]}❌ ERROR: Gagal menulis ke file log/tested: {e}
{Style.RESET_ALL}")

# --- FUNGSI WORKER BFA DENGAN STRIPING (ITERASI +2) ---


def brute_force_sequential(
iv_bytes: bytes,
ciphertext_bytes: bytes,
worker_index: int,
chunk_id_str: str,
session_start_time: float
) -> Tuple[str | None, str | None]:

global FOUND_KEY

start_num = worker_index
step_size = TOTAL_WORKERS

total_tested_by_this_worker = 0

last_checked_time = [Link]()
last_tested_count = 0

pane_label = f"P{worker_index + 1}"

is_even_start = (worker_index == 0)
if is_even_start:
odd_even_label = f"({[Link]}Genap{Style.RESET_ALL})"
else:
odd_even_label = f"({[Link]}Ganjil{Style.RESET_ALL})"

for i in range(start_num, TOTAL_COMBINATIONS, step_size):

password = str(i)
total_tested_by_this_worker += 1

virtual_chunk_num = (total_tested_by_this_worker - 1) // VIRTUAL_CHUNK_UNIT


+ 1

# Sinkronisasi
if total_tested_by_this_worker % SYNC_CHECK_INTERVAL == 0:
found_key_sync, _, _ = check_for_found_key()
if found_key_sync:
[Link](CLEAR_LINE)
[Link]()
print(f"{[Link]}🛑 STOPPING! Kunci '{found_key_sync}'
ditemukan oleh worker lain.{Style.RESET_ALL}")
return found_key_sync, None

# --- MUTEX LOGIC START (Progress Bar & Pt/s) ---


if total_tested_by_this_worker % LOG_INTERVAL == 0:

current_time = [Link]()
time_diff = current_time - last_checked_time
tested_diff = total_tested_by_this_worker - last_tested_count

pt_per_second = tested_diff / time_diff if time_diff > 0 else 0

last_checked_time = current_time
last_tested_count = total_tested_by_this_worker

try:
# Ambil Mutex Lock
lock_fd = [Link](LOCK_FILE, os.O_CREAT | os.O_EXCL | os.O_WRONLY)
[Link](lock_fd)

# WAKTU TOTAL SESI (TIDAK RESET)


session_elapsed = [Link]() - session_start_time
session_time_str = format_elapsed_time(session_elapsed)

[Link](CURSOR_UP_2)
[Link](CLEAR_LINE)

pt_s_str = f"{pt_per_second:,.0f}"

# LOGIKA DISPLAY
progress_info = (
f"\r[{[Link]}{pane_label}/C{virtual_chunk_num}
{Style.RESET_ALL}] "
f"{odd_even_label} "
f"({[Link]}{session_time_str}{Style.RESET_ALL}) "
f"({[Link]}Pt/s: {pt_s_str}{Style.RESET_ALL}): "
f"{[Link]}{password}{Style.RESET_ALL}\n"
f"{[Link]}STATUS: {[Link]}Running...{Style.RESET_ALL}"
)

[Link](progress_info)
[Link]()

# Lepaskan lock
[Link](LOCK_FILE)

except FileExistsError:
pass
except Exception:
pass
# --- MUTEX LOGIC END ---

result = decrypt_aes_128_cbc(iv_bytes, ciphertext_bytes, password)

if result is not None:

session_elapsed = [Link]() - session_start_time


session_time_str = format_elapsed_time(session_elapsed)

# Tulis password yang ditemukan ke file log (sebagai dasar Pt/m)


write_found_key(password, session_time_str)

[Link](CLEAR_LINE)
[Link]()
FOUND_KEY = password

print(f"{[Link]}🎉 Kunci Ditemukan ({password})! Mengirim sinyal


penutupan sesi Tmux...{Style.RESET_ALL}")
try:
[Link](f"tmux kill-session -t {SESSION_NAME}")
except Exception as e:
print(f"{[Link]}❌ Gagal mengirim sinyal kill-session: {e}
{Style.RESET_ALL}")

return password, result

[Link](CLEAR_LINE)
[Link]()
return None, None

# --- FUNGSI DEKODE DATA & SETUP TMUX (TETAP SAMA) ---
def from_hex(hex_str: str) -> str:
try:
return [Link]("".join(hex_str.split(' '))).decode('utf-8')
except Exception:
return f"{[Link]}[DECODE ERROR: Invalid Hex Data]{Style.RESET_ALL}"

def periodic_symbol_to_number(symbol: str) -> str:


symbol = [Link]().capitalize()
if symbol in GENDER_ATOM_DECODING:
atomic_number = GENDER_ATOM_DECODING[symbol]
gender = GENDER_DECODED_MAPPING.get(atomic_number, "N/A")
return f"{atomic_number} (Unsur: {symbol}, {[Link]}Asumsi Data: {gender}
{Style.RESET_ALL})"
try:
for element in elements:
if [Link] == symbol:
data_asli = "Umur atau Dua Digit Tahun Tahun Lahir"
return f"{[Link]} (Unsur: {[Link]}, Data Asli:
{data_asli})"
except NameError:
pass
return f"{[Link]}[DECODE ERROR: Unrecognized Symbol/Code]
{Style.RESET_ALL}"

def process_decoded_data(qr_data_encoded: str) -> List[List[str]]:


data_tabel_decoded = []
relevant_keys = ["Nama", "Pekerjaan", "Tempat_Tinggal", "Status",
"Kewarganegaraan", "Agama", "Umur", "Jenis_Kelamin", "Kelahiran_Tahun"]
for field in qr_data_encoded.split(' | '):
if ':' in field:
try:
key_encoded, value_encoded = [Link](':', 1)
value_encoded = value_encoded.strip()
original_key = key_encoded.replace('_', ' ')
if key_encoded in relevant_keys:
if key_encoded in ["Umur", "Jenis_Kelamin", "Kelahiran_Tahun"]:
decoded_value = periodic_symbol_to_number(value_encoded)
metode = f"{[Link]}Simbol Unsur ->
Angka{Style.RESET_ALL}"
else:
decoded_value = from_hex(value_encoded)
metode = f"{[Link]}Hex -> Teks{Style.RESET_ALL}"
data_tabel_decoded.append([original_key, value_encoded,
decoded_value, metode])
except Exception:
pass
return data_tabel_decoded

def run_worker(worker_index: int, chunk_id_str: str, qr_data_raw: str):

pane_label = f"P{worker_index + 1}"


print(f"{[Link]}--- {pane_label} STARTING TRUE STRIPING LOOP (I + 2) ---
{Style.RESET_ALL}")

parts = qr_data_raw.split('|')
iv_b64 = next(([Link](':')[1] for p in parts if [Link]('IV:')), None)
ciphertext_b64 = next(([Link](':')[1] for p in parts if [Link]('CT:')),
None)

try:
iv_bytes = base64.b64decode(iv_b64)
ciphertext_bytes = base64.b64decode(ciphertext_b64)
except Exception:
print(f"{[Link]}❌ Kesalahan Base64 Decode di Worker.{Style.RESET_ALL}")
return

session_start_time = [Link]()
found_key, decoded_data = brute_force_sequential(
iv_bytes, ciphertext_bytes, worker_index, chunk_id_str, session_start_time
)
if found_key and not decoded_data:
[Link](CLEAR_LINE)
[Link]()
print(f"{[Link]}✅ Worker berhenti. Kunci ditemukan oleh worker lain:
{found_key}{Style.RESET_ALL}")

[Link](5)

def setup_tmux_and_run():

global qr_data_raw

# Bersihkan file-file lama


if [Link](LOG_FILE):
[Link](LOG_FILE)
if [Link](LOCK_FILE):
[Link](LOCK_FILE)
if [Link](FOUND_KEY_NUMBER_FILE):
[Link](FOUND_KEY_NUMBER_FILE)

# Tampilan utama

print(f"{[Link]}============================================={Style.RESET_ALL}
")
# FIX: Menggunakan r-string untuk menghindari SyntaxWarning invalid escape
sequence '\m'
print(r"Advanced Brute Force Attack")

print(f"{[Link]}============================================={Style.RESET_ALL}
")
print(f" Pane Dikerahkan: {[Link]}{TOTAL_WORKERS} (P1: Genap (0, 2, 4...),
P2: Ganjil (1, 3, 5...)){Style.RESET_ALL}")
print(f"📦 Chunk Size (Virtual): *{CHUNK_SIZE:,}*")
print(f"📦 Total Kombinasi: *{TOTAL_COMBINATIONS:,}* (0 hingga
999,999,999,999)")
print(f" Update Progress Setiap: *{LOG_INTERVAL}* Iterasi ({[Link]}Memerlukan
File Mutex{Style.RESET_ALL})")
print("-" * 50)

qr_data_raw = input(f"{[Link]}Silakan masukkan String Data QR:


{Style.RESET_ALL} ").strip()

if not qr_data_raw.startswith("AES128|"):
print(f"{[Link]}❌ Data tidak terdeteksi dienkripsi dengan AES128. Setup
dibatalkan.{Style.RESET_ALL}")
return

print(f"\n{[Link]}Memulai Setup Paralel Tmux...{Style.RESET_ALL}")

try:
[Link](["tmux", "-V"], check=True, capture_output=True)
except FileNotFoundError:
print(f"{[Link]}❌ Error: Tmux tidak ditemukan. Harap instal Tmux.
{Style.RESET_ALL}")
[Link](1)

[Link](["tmux", "kill-session", "-t", SESSION_NAME],


stderr=[Link])
script_path = [Link](__file__)

# Worker 0 (Indeks 0) - Label P1


cmd1 = f'python3 {quote(script_path)} 0 "P1" {quote(qr_data_raw)}'

# Worker 1 (Indeks 1) - Label P2


cmd2 = f'python3 {quote(script_path)} 1 "P2" {quote(qr_data_raw)}'

try:
[Link](["tmux", "new-session", "-d", "-s", SESSION_NAME, cmd1],
check=True)
[Link](["tmux", "split-window", "-t", f"{SESSION_NAME}:0", cmd2],
check=True)
[Link](["tmux", "select-layout", "tiled"], check=True)

print(f"{[Link]}✅ Setup Selesai. Otomatis masuk ke sesi Tmux


'{SESSION_NAME}'{Style.RESET_ALL}")
[Link](["tmux", "attach-session", "-t", SESSION_NAME])

# --- LOGIKA DEKODE AKHIR DI SHELL UTAMA ---


final_key, total_time_str, key_number_str = check_for_found_key()

# Bersihkan LOCK/DATA FILE jika masih ada


if [Link](LOCK_FILE): [Link](LOCK_FILE)
if [Link](FOUND_KEY_NUMBER_FILE): [Link](FOUND_KEY_NUMBER_FILE)

print('\n' + '-' * 70)

if final_key:

# --- Hitung TPA (Time Per Attempt) Gabungan ---

total_attempts_gabungan = int(key_number_str) + 1
total_time_seconds = time_to_seconds(total_time_str)

tpa_seconds = 0.0

if total_time_seconds > 0:
# Waktu rata-rata per percobaan
tpa_seconds = total_time_seconds / total_attempts_gabungan

# Konversi TPA ke Mikrosekon (1 detik = 1,000,000 µs)


tpa_micro = tpa_seconds * 1_000_000

# Tentukan Worker yang menemukan kunci (P1: Genap, P2: Ganjil)


finder_worker = "P1" if int(final_key) % 2 == 0 else "P2"

# Tampilan Output Final


print(f"\
n{[Link]}======================================================================
====={Style.RESET_ALL}")
print(f"{[Link]}BFA Berhasil! Password ditemukan: {[Link]}
{final_key}{Style.RESET_ALL} (Ditemukan oleh Worker {finder_worker})")
print(f"⏰ Total waktu pemulihan: {[Link]}{total_time_str}
{Style.RESET_ALL}")
# Menggunakan karakter Unicode \u03bc untuk simbol Mikrosekon (µ) agar
lebih kompatibel daripada LaTeX.
print(f" Rata-rata Waktu Per Percobaan (TPA): {[Link]}
{tpa_micro:,.3f} \u03bc s{Style.RESET_ALL}")
print(f"\n{[Link]}HASIL DEKODE QR IDENTITY:{Style.RESET_ALL}")

parts = qr_data_raw.split('|')
iv_b64 = next(([Link](':')[1] for p in parts if [Link]('IV:')),
None)
ciphertext_b64 = next(([Link](':')[1] for p in parts if
[Link]('CT:')), None)

try:
iv_bytes = base64.b64decode(iv_b64)
ciphertext_bytes = base64.b64decode(ciphertext_b64)
except Exception:
print(f"{[Link]}❌ Kesalahan Base64 Decode pada proses akhir.
{Style.RESET_ALL}")
return

decoded_data = decrypt_aes_128_cbc(iv_bytes, ciphertext_bytes,


final_key)

if decoded_data:
data_tabel_decoded = process_decoded_data(decoded_data)

headers = [f"{[Link]}Bidang{Style.RESET_ALL}", f"{[Link]}Kode


Data (Input){Style.RESET_ALL}", f"{[Link]}Nilai yang Didekode{Style.RESET_ALL}",
f"{[Link]}Metode{Style.RESET_ALL}"]
print(tabulate(data_tabel_decoded, headers=headers,
tablefmt="grid"))
print(f"\
n{[Link]}======================================================================
====={Style.RESET_ALL}")
print(f"✅ DEKODE SELESAI")
else:
print(f"{[Link]}❌ Gagal mendekripsi data dengan kunci
{final_key} yang ditemukan.{Style.RESET_ALL}")

[Link](["tmux", "kill-session", "-t", SESSION_NAME],


stderr=[Link])
else:
print(f"\n{[Link]}Sesi {SESSION_NAME} selesai/dikeluarkan tanpa
menemukan kunci.{Style.RESET_ALL}")

except [Link] as e:
print(f"{[Link]}❌ Gagal mengeksekusi perintah Tmux: {e}
{Style.RESET_ALL}")
[Link](1)

if __name__ == "__main__":
if len([Link]) == 4:
try:
worker_index = int([Link][1])
chunk_id_str = [Link][2]
qr_data = [Link][3]
run_worker(worker_index, chunk_id_str, qr_data)
except ValueError:
print(f"{[Link]}❌ Error: Argumen tidak valid saat dijalankan
otomatis.{Style.RESET_ALL}")
[Link](1)
else:
setup_tmux_and_run()

[Link]

import sys
import os
import re
import base64
import hashlib
import qrcode
from datetime import datetime
from tabulate import tabulate
from typing import Dict, Any, List, Tuple

# --- Kriptografi dan Library Tambahan ---


try:
from [Link] import Cipher, algorithms, modes
from [Link] import padding
from [Link] import default_backend
from periodictable import elements
except ImportError:
print("Error: Beberapa library tidak ditemukan.")
print("Harap instal: pip install cryptography qrcode tabulate periodictable")
[Link](1)

# ==============================================================================
# DATA NIK DECODE & KONFIGURASI KRIPTOGRAFI
# ==============================================================================

# Peta Substitusi Acak untuk NIK (Digit ke Huruf Acak)


NIK_SUBSTITUTION_MAP: Dict[str, str] = {
'0': 'A', '1': 'S', '2': 'K', '3': 'T', '4': 'M',
'5': 'L', '6': 'B', '7': 'R', '8': 'G', '9': 'C'
}
# Peta Substitusi Acak Kebalikan (Huruf ke Digit)
NIK_REVERSE_MAP: Dict[str, str] = {v: k for k, v in NIK_SUBSTITUTION_MAP.items()}

# Konfigurasi Unsur Kimia (Atom)


MAX_ATOMIC_NUMBER = 118
GENDER_ATOM_MAPPING: Dict[str, int] = {
"LAKI-LAKI": 15, # Phosphorus (P)
"PEREMPUAN": 16 # Sulfur (S)
}

# Mapping Romawi
ROMAN_MAP = {
1000: 'M', 900: 'CM', 500: 'D', 400: 'CD', 100: 'C',
90: 'XC', 50: 'L', 40: 'XL', 10: 'X', 9: 'IX',
5: 'V', 4: 'IV', 1: 'I'
}

# ==============================================================================
# FUNGSI TRANSFORMASI BARU
# ==============================================================================

def to_roman(text: str) -> str:


"""
Mengubah setiap karakter pada string menjadi nilai ASCII, lalu mengkonversi
ASCII tersebut ke Angka Romawi, dipisahkan koma.
Contoh: 'A' (65) -> 'LXV'
"""
roman_string = ""
for char in text:
n = ord(char)

# Konversi Angka ke Romawi


result = ""
for value, symbol in ROMAN_MAP.items():
while n >= value:
result += symbol
n -= value

# Ganti spasi dengan underscore untuk menjaga format


if char == ' ':
roman_string += "SP_"
else:
roman_string += result + ","

# Hapus koma terakhir jika ada


return roman_string.rstrip(',')

def nik_to_cryptic(nik_number: str) -> str:


"""
Mengubah NIK 16 Digit menjadi string huruf acak tanpa spasi
menggunakan NIK_SUBSTITUTION_MAP.
"""
# Hanya mengambil 16 digit pertama dan memfilter non-digit
nik_clean = "".join(filter([Link], nik_number))

if len(nik_clean) != 16:
return f"ERROR: Panjang NIK ({len(nik_clean)}) tidak 16 digit."

cryptic_string = "".join(NIK_SUBSTITUTION_MAP.get(digit, 'Z') for digit in


nik_clean)
return cryptic_string

def number_to_periodic_element(number_str: str) -> str:


"""
Mengubah angka (Umur) menjadi unsur periodik.
"""
try:
number = int(number_str)
if 1 <= number <= MAX_ATOMIC_NUMBER:
return elements[number].symbol
return f"OUT{number}"
except ValueError:
return "N/A"

def gender_to_periodic_element(gender: str) -> str:


"""
Mengubah Jenis Kelamin menjadi unsur periodik (P/S).
"""
gender_norm = [Link]().upper()
atomic_number = GENDER_ATOM_MAPPING.get(gender_norm)

if atomic_number:
return elements[atomic_number].symbol

return f"LEN{len(gender)}"

# ==============================================================================
# NIK DECODER (Untuk mendapatkan Umur dan Jenis Kelamin)
# ==============================================================================

def decode_nik_for_data(nik_number: str) -> Tuple[str, str, str]:


"""
Mendekode NIK untuk mendapatkan Tanggal Lahir, Jenis Kelamin, dan Prediksi
Usia.
Mengembalikan tuple (jenis_kelamin, prediksi_usia, error_message).
"""
if len(nik_number) != 16 or not nik_number.isdigit():
return "", "", "[!] ERROR NIK: NIK harus 16 digit angka."

dd_raw = int(nik_number[6:8])
mm = nik_number[8:10]
yy = nik_number[10:12]

# 1. Tentukan Jenis Kelamin & Tanggal Lahir Final


if dd_raw > 31:
jenis_kelamin = "PEREMPUAN"
dd_final = str(dd_raw - 40).zfill(2)
else:
jenis_kelamin = "LAKI-LAKI"
dd_final = str(dd_raw).zfill(2)

# 2. Hitung Prediksi Usia


try:
current_two_digit_year = [Link]().year % 100
tahun_lengkap = int(f"20{yy}") if int(yy) <= current_two_digit_year else
int(f"19{yy}")

tgl_lahir_obj = [Link](f"{dd_final}/{mm}/{tahun_lengkap}",
'%d/%m/%Y')
today = [Link]()

usia = [Link] - tgl_lahir_obj.year


if ([Link], [Link]) < (tgl_lahir_obj.month, tgl_lahir_obj.day):
usia -= 1
prediksi_usia = str(usia)

except ValueError:
prediksi_usia = "0"
return "", "", "[!] ERROR NIK: Tanggal Lahir (digit 7-12) tidak valid."

return jenis_kelamin, prediksi_usia, ""

# ==============================================================================
# FUNGSI KRIPTOGRAFI AES (ENKRIPSI)
# ==============================================================================

def encrypt_aes_128_cbc(raw_data: str, password: str) -> Dict[str, str]:


"""Mengenkripsi data menggunakan AES-128-CBC."""
key = hashlib.sha256([Link]('utf-8')).digest()[:16]
backend = default_backend()
iv = [Link](16)
padder = padding.PKCS7([Link].block_size).padder()
padded_data = [Link](raw_data.encode('utf-8')) + [Link]()
cipher = Cipher([Link](key), [Link](iv), backend=backend)
encryptor = [Link]()
ciphertext = [Link](padded_data) + [Link]()

return {
"iv": base64.b64encode(iv).decode('utf-8'),
"ciphertext": base64.b64encode(ciphertext).decode('utf-8')
}

# ==============================================================================
# FUNGSI UTAMA GENERATOR
# ==============================================================================

def run_cryptic_id_generator():
"""Meminta 4 input, mendekode NIK, mentransformasi, dan membuat QR Code."""

print("\n=============================================")
print("MODE: ADVANCED CRYPTIC ID GENERATOR (V1.0)")
print("=============================================")

# 1. GATHERING DATA
nama = input("Masukkan Nama: ").strip()
pekerjaan = input("Masukkan Pekerjaan: ").strip()
status = input("Masukkan Status: ").strip()
nik_number = input("Masukkan NIK 16 Digit: ").strip()

if not all([nama, pekerjaan, status, nik_number]):


print("❌ Semua bidang input harus diisi.")
return

# Dekode NIK untuk mendapatkan data tersembunyi


jenis_kelamin, umur, nik_error = decode_nik_for_data(nik_number)
if nik_error:
print(nik_error)
return

print("-" * 50)
password_key = input("Masukkan Kunci Enkripsi (Teks/Angka Apa Pun): ").strip()
if not password_key:
print("❌ Kunci enkripsi tidak boleh kosong. Keluar.")
return
print("-" * 50)

# 2. PROSES TRANSFORMASI DATA

data_input: Dict[str, str] = {


"Nama": nama,
"Pekerjaan": pekerjaan,
"Status": status,
"Jenis Kelamin (dari NIK)": jenis_kelamin,
"Umur (dari NIK)": umur,
"NIK 16 Digit": nik_number
}

data_transformed: Dict[str, str] = {


# Angka Romawi
"Nama_T": to_roman(data_input["Nama"]),
"Pekerjaan_T": to_roman(data_input["Pekerjaan"]),
"Status_T": to_roman(data_input["Status"]),

# Unsur Atom
"Jenis_Kelamin_T": gender_to_periodic_element(data_input["Jenis Kelamin
(dari NIK)"]),
"Umur_T": number_to_periodic_element(data_input["Umur (dari NIK)"]),

# Substitusi Acak
"NIK_T": nik_to_cryptic(data_input["NIK 16 Digit"]),
}

# 3. TAMPILKAN DATA TERKONVERSI

print("==========================================================================="
)
print("📊 DATA IDENTITAS TERKONVERSI (Romawi, Unsur Kimia, & Huruf Acak)")

print("==========================================================================="
)

data_tabel = [
["Nama", data_input["Nama"], data_transformed['Nama_T'], "Romawi (ASCII)"],
["Pekerjaan", data_input["Pekerjaan"], data_transformed['Pekerjaan_T'],
"Romawi (ASCII)"],
["Status", data_input["Status"], data_transformed['Status_T'], "Romawi
(ASCII)"],
["---", "---", "---", "---"],
["Jenis Kelamin", data_input["Jenis Kelamin (dari NIK)"],
data_transformed['Jenis_Kelamin_T'], "Unsur Atom (P/S)"],
["Umur", data_input["Umur (dari NIK)"], data_transformed['Umur_T'], "Unsur
Atom (Nomor Atom)"],
["---", "---", "---", "---"],
["NIK 16 Digit", data_input["NIK 16 Digit"], data_transformed['NIK_T'],
"Huruf Acak (Substitusi)"]
]

headers = ["Bidang", "Data Asli/Didekode", "Hasil Konversi", "Metode"]


print(tabulate(data_tabel, headers=headers, tablefmt="fancy_grid"))
print("-" * 75)

# 4. GABUNGKAN DATA UNTUK ENKRIPSI


qr_data_fields = [
f"Nama:{data_transformed['Nama_T']}",
f"Pekerjaan:{data_transformed['Pekerjaan_T']}",
f"Status:{data_transformed['Status_T']}",
f"Jenis_Kelamin:{data_transformed['Jenis_Kelamin_T']}",
f"Umur:{data_transformed['Umur_T']}",
f"NIK:{data_transformed['NIK_T']}",
]
raw_data_string = " | ".join(qr_data_fields)

# 5. ENKRIPSI DATA MENTAH


print(f"🔒 Mengenkripsi data mentah ({len(raw_data_string)} bytes)...")
encrypted_data = encrypt_aes_128_cbc(raw_data_string, password_key)

# 6. BUAT STRING FINAL QR CODE


qr_data_encrypted_final = (
f"ADV_CRYP_ID|AES128|"
f"IV:{encrypted_data['iv']}|"
f"CT:{encrypted_data['ciphertext']}"
)

# 7. BUAT DAN SIMPAN QR CODE


qr = [Link](
version=1, error_correction=[Link].ERROR_CORRECT_H,
box_size=10, border=4
)
qr.add_data(qr_data_encrypted_final)
[Link](fit=True)

img = qr.make_image(fill_color="black", back_color="white")


timestamp = [Link]().strftime("%Y%m%d_%H%M%S")
file_name = f"QR_Cryptic_ID_{timestamp}.png"
[Link](file_name)

# 8. TAMPILKAN RINGKASAN
print("\n=============================================")
print("✅ QR CODE BERHASIL DIBUAT (Advanced Cryptic ID!)")
print("=============================================")
print(f"Kunci Enkripsi (Rahasia): **{password_key}**")
print(f"Nama File: {file_name}")
print(f"Data QR Code (Terenkripsi): '{qr_data_encrypted_final}'")

print("==========================================================================="
)

# ==============================================================================
# EKSEKUSI PROGRAM
# ==============================================================================

if __name__ == "__main__":

# Skrip ini sekarang hanya memiliki 1 mode: Advanced Cryptic ID Generator.


try:
run_cryptic_id_generator()
except KeyboardInterrupt:
print("\nProgram dihentikan oleh pengguna. Terima kasih!")
[Link](0)
except Exception as e:
print(f"\n[!] Terjadi kesalahan fatal: {e}")
[Link](1)

[Link]

import random
import string
import sys
from colorama import Fore, Style, init

# Inisialisasi colorama untuk tampilan yang lebih baik di terminal


init(autoreset=True)

# ==============================================================================
# 1. DEFINISI KARAKTER SET
# ==============================================================================

# Karakter yang TIDAK boleh menjadi karakter pertama (Simbol)


SIMBOL_CHARSET = [Link]

# Karakter yang boleh menjadi karakter pertama (Huruf & Angka)


NON_SIMBOL_CHARSET = string.ascii_letters + [Link] # Huruf Besar, Kecil, dan
Angka

# Semua karakter yang digunakan untuk sisa password


FULL_CHARSET = NON_SIMBOL_CHARSET + SIMBOL_CHARSET

# ==============================================================================
# 2. FUNGSI UTAMA GENERATOR
# ==============================================================================

def generate_safe_password(ppl: int) -> str:


"""
Menghasilkan password acak dengan panjang PPL.
Karakter pertama dijamin BUKAN simbol.
"""
if ppl <= 0:
return ""

# Langkah 1: Tentukan karakter pertama (Wajib Non-Simbol)


# Pilih karakter acak dari set Non-Simbol (Huruf/Angka)
first_char = [Link](NON_SIMBOL_CHARSET)

# Langkah 2: Tentukan sisa karakter (PPL - 1)


remaining_length = ppl - 1

# Pilih karakter acak dari set Penuh (Huruf/Angka/Simbol) untuk sisa panjang
remaining_chars = [[Link](FULL_CHARSET) for _ in
range(remaining_length)]

# Gabungkan karakter pertama dengan sisa karakter


password_list = [first_char] + remaining_chars

# Acak posisi karakter sisa


[Link](password_list)

# Catatan: Karena kita ingin karakter pertama BUKAN simbol,


# kita perlu memastikan karakter pertama yang dipilih di Langkah 1
# tetap berada di posisi pertama JIKA tidak di-shuffle.
# Jika di-shuffle, kita pastikan karakter non-simbol yang dihasilkan
# secara acak berada di indeks 0.

# KITA AKAN MELAKUKAN SEDIKIT MODIFIKASI agar first_char TEPAT di posisi 0


# dan sisanya di-shuffle di posisi 1 sampai akhir.

# Ulangi Langkah 2 & 3 tanpa shuffle penuh, hanya untuk memastikan


# non-simbol di posisi 0:

# Tentukan kembali first_char (tanpa shuffle)


first_char = [Link](NON_SIMBOL_CHARSET)

# Tentukan sisa karakter (diizinkan simbol)


remaining_chars = [[Link](FULL_CHARSET) for _ in
range(remaining_length)]

# Gabungkan, lalu acak SISA karakter untuk variasi


# Ini menjamin karakter pertama tetap non-simbol.

# Implementasi yang LEBIH KUAT: Pastikan index 0 adalah NON_SIMBOL, lalu


shuffle sisanya.

# Buat seluruh list karakter


full_list = [[Link](FULL_CHARSET) for _ in range(ppl)]

# Pastikan elemen di index 0 adalah NON_SIMBOL


if full_list[0] in SIMBOL_CHARSET:
# Jika index 0 adalah simbol, ganti dengan karakter dari NON_SIMBOL_CHARSET
full_list[0] = [Link](NON_SIMBOL_CHARSET)

# Shuffle sisa karakter untuk memastikan keragaman posisi


if ppl > 1:
# Ambil sisanya
tail = full_list[1:]
[Link](tail)
# Gabungkan kembali
password_list = [full_list[0]] + tail
else:
password_list = full_list

return "".join(password_list)
# ==============================================================================
# 3. INTERFACE PENGGUNA (MAIN LOOP)
# ==============================================================================

def main():
"""Menjalankan interface pengguna untuk mendapatkan input PPL."""

print(f"{[Link]}=========================================={Style.RESET_ALL}")
print(f"{[Link]} 🔑 Password Generator (PPL max 8){Style.RESET_ALL}")

print(f"{[Link]}=========================================={Style.RESET_ALL}")
print(f"{[Link]}Aturan:{Style.RESET_ALL} Karakter pertama dijamin bukan
simbol.")

MAX_PPL = 64

while True:
try:
# Meminta input dari pengguna
ppl_input = input(f"\n{[Link]}Input PPL (Panjang Password, Max
{MAX_PPL} Character): {Style.RESET_ALL}").strip()

if not ppl_input:
print(f"{[Link]}❌ Input tidak boleh kosong.{Style.RESET_ALL}")
continue

ppl = int(ppl_input)

if ppl <= 0:
print(f"{[Link]}❌ Panjang harus lebih dari 0.{Style.RESET_ALL}")
elif ppl > MAX_PPL:
print(f"{[Link]}❌ Panjang maksimal adalah {MAX_PPL} karakter.
{Style.RESET_ALL}")
else:
# Jika input valid, generate password
password = generate_safe_password(ppl)

# Tampilkan hasil
print(f"\n{[Link]}✅ Password yang Dihasilkan ({ppl}
karakter):{Style.RESET_ALL}")
print(f"{[Link]}{[Link]}{password}{Style.RESET_ALL}")

# Cek apakah pengguna ingin generate lagi


again = input(f"\n{[Link]}Generate lagi? (y/n):
{Style.RESET_ALL}").lower()
if again != 'y':
print(f"\n{[Link]}Terima kasih, sampai jumpa! 👋
{Style.RESET_ALL}")
[Link](0)

except ValueError:
print(f"{[Link]}❌ Input harus berupa angka bulat.{Style.RESET_ALL}")
except KeyboardInterrupt:
print(f"\n\n{[Link]}Proses dihentikan pengguna. 👋{Style.RESET_ALL}")
[Link](0)

if __name__ == "__main__":
main()

[Link]

import unittest
import math
import time
import sys
import traceback
import random
import statistics
import logging
from typing import Dict, List, Tuple
from multiprocessing import cpu_count
from [Link] import ProcessPoolExecutor
from joblib import Parallel, delayed
import [Link] as plt
import os
import numpy as np

# Model Koefisien Seret (Cd) Standar (Penggunaan float standar untuk presisi)
# Data tabel ini mewakili Koefisien Seret terhadap Mach Number (x: Mach, y: Cd)
G1_DRAG_TABLE = [(x, y) for x, y in [
(0.00, 0.220), (0.10, 0.219), (0.20, 0.217), (0.30, 0.214), (0.40, 0.208),
(0.50, 0.199), (0.60, 0.187), (0.70, 0.170), (0.80, 0.153), (0.90, 0.137),
(1.00, 0.125), (1.10, 0.122), (1.20, 0.121), (1.30, 0.120), (1.40, 0.120),
(1.50, 0.120), (1.60, 0.120), (1.70, 0.120), (1.80, 0.120), (1.90, 0.120),
(2.00, 0.120), (2.20, 0.120), (2.40, 0.120), (2.60, 0.120), (2.80, 0.120),
(3.00, 0.120), (3.20, 0.120), (3.40, 0.120), (3.60, 0.120), (3.80, 0.120),
(4.00, 0.1505), (4.20, 0.1500), (4.40, 0.1495), (4.60, 0.1490), (4.80, 0.1485),
(5.00, 0.1480)
]]
G2_DRAG_TABLE = [(x, y) for x, y in [
(0.00, 0.204), (0.10, 0.203), (0.20, 0.202), (0.30, 0.200), (0.40, 0.196),
(0.50, 0.190), (0.60, 0.181), (0.70, 0.169), (0.80, 0.155), (0.90, 0.141),
(1.00, 0.128), (1.10, 0.125), (1.20, 0.124), (1.30, 0.123), (1.40, 0.123),
(1.50, 0.123), (1.60, 0.123), (1.70, 0.123), (1.80, 0.123), (1.90, 0.123),
(2.00, 0.123), (2.20, 0.122), (2.40, 0.122), (2.60, 0.122), (2.80, 0.122),
(3.00, 0.121), (3.20, 0.121), (3.40, 0.121), (3.60, 0.120), (3.80, 0.120),
(4.00, 0.120), (4.20, 0.120), (4.40, 0.120), (4.60, 0.120), (4.80, 0.120),
(5.00, 0.120)
]]
G3_DRAG_TABLE = [(x, y) for x, y in [
(0.00, 0.443), (0.10, 0.441), (0.20, 0.437), (0.30, 0.430), (0.40, 0.419),
(0.50, 0.402), (0.60, 0.378), (0.70, 0.350), (0.80, 0.316), (0.90, 0.285),
(1.00, 0.260), (1.10, 0.252), (1.20, 0.245), (1.30, 0.240), (1.40, 0.237),
(1.50, 0.235), (1.60, 0.233), (1.70, 0.231), (1.80, 0.230), (1.90, 0.229),
(2.00, 0.228), (2.20, 0.227), (2.40, 0.226), (2.60, 0.225), (2.80, 0.224),
(3.00, 0.223), (3.20, 0.222), (3.40, 0.221), (3.60, 0.220), (3.80, 0.219),
(4.00, 0.218), (4.20, 0.217), (4.40, 0.216), (4.60, 0.215), (4.80, 0.214),
(5.00, 0.213)
]]
G4_DRAG_TABLE = [(x, y) for x, y in [
(0.00, 0.219), (0.10, 0.218), (0.20, 0.216), (0.30, 0.213), (0.40, 0.207),
(0.50, 0.198), (0.60, 0.186), (0.70, 0.169), (0.80, 0.152), (0.90, 0.136),
(1.00, 0.124), (1.10, 0.121), (1.20, 0.120), (1.30, 0.119), (1.40, 0.119),
(1.50, 0.119), (1.60, 0.119), (1.70, 0.119), (1.80, 0.119), (1.90, 0.119),
(2.00, 0.119), (2.20, 0.118), (2.40, 0.118), (2.60, 0.118), (2.80, 0.118),
(3.00, 0.117), (3.20, 0.117), (3.40, 0.117), (3.60, 0.116), (3.80, 0.116),
(4.00, 0.116), (4.20, 0.115), (4.40, 0.115), (4.60, 0.115), (4.80, 0.114),
(5.00, 0.114)
]]
G5_DRAG_TABLE = [(x, y) for x, y in [
(0.00, 0.224), (0.10, 0.223), (0.20, 0.221), (0.30, 0.218), (0.40, 0.212),
(0.50, 0.203), (0.60, 0.191), (0.70, 0.174), (0.80, 0.157), (0.90, 0.141),
(1.00, 0.128), (1.10, 0.125), (1.20, 0.124), (1.30, 0.123), (1.40, 0.123),
(1.50, 0.123), (1.60, 0.123), (1.70, 0.123), (1.80, 0.123), (1.90, 0.123),
(2.00, 0.123), (2.20, 0.122), (2.40, 0.122), (2.60, 0.122), (2.80, 0.122),
(3.00, 0.121), (3.20, 0.121), (3.40, 0.121), (3.60, 0.120), (3.80, 0.120),
(4.00, 0.120), (4.20, 0.120), (4.40, 0.120), (4.60, 0.120), (4.80, 0.120),
(5.00, 0.120)
]]
G6_DRAG_TABLE = [(x, y) for x, y in [
(0.00, 0.245), (0.10, 0.244), (0.20, 0.242), (0.30, 0.239), (0.40, 0.232),
(0.50, 0.222), (0.60, 0.209), (0.70, 0.191), (0.80, 0.173), (0.90, 0.156),
(1.00, 0.142), (1.10, 0.139), (1.20, 0.138), (1.30, 0.137), (1.40, 0.137),
(1.50, 0.137), (1.60, 0.137), (1.70, 0.137), (1.80, 0.137), (1.90, 0.137),
(2.00, 0.137), (2.20, 0.136), (2.40, 0.136), (2.60, 0.136), (2.80, 0.136),
(3.00, 0.135), (3.20, 0.135), (3.40, 0.135), (3.60, 0.134), (3.80, 0.134),
(4.00, 0.134), (4.20, 0.133), (4.40, 0.133), (4.60, 0.133), (4.80, 0.132),
(5.00, 0.132)
]]
G7_DRAG_TABLE = [(x, y) for x, y in [
(0.00, 0.2300), (0.10, 0.2295), (0.20, 0.2285), (0.30, 0.2265),
(0.40, 0.2230), (0.50, 0.2175), (0.60, 0.2095), (0.70, 0.1980),
(0.80, 0.1830), (0.90, 0.1690), (0.95, 0.1645), (1.00, 0.1620),
(1.05, 0.1610), (1.10, 0.1605), (1.15, 0.1600), (1.20, 0.1595),
(1.30, 0.1590), (1.40, 0.1585), (1.50, 0.1580), (1.60, 0.1575),
(1.70, 0.1570), (1.80, 0.1565), (1.90, 0.1560), (2.00, 0.1555),
(2.20, 0.1550), (2.40, 0.1545), (2.60, 0.1540), (2.80, 0.1535),
(3.00, 0.1530), (3.20, 0.1525), (3.40, 0.1520), (3.60, 0.1515),
(3.80, 0.1510), (4.00, 0.1505), (4.20, 0.1500), (4.40, 0.1495),
(4.60, 0.1490), (4.80, 0.1485), (5.00, 0.1480)
]]
G8_DRAG_TABLE = [(x, y) for x, y in [
(0.00, 0.204), (0.10, 0.203), (0.20, 0.202), (0.30, 0.200), (0.40, 0.196),
(0.50, 0.190), (0.60, 0.181), (0.70, 0.169), (0.80, 0.155), (0.90, 0.141),
(1.00, 0.128), (1.10, 0.125), (1.20, 0.124), (1.30, 0.123), (1.40, 0.123),
(1.50, 0.123), (1.60, 0.123), (1.70, 0.123), (1.80, 0.123), (1.90, 0.123),
(2.00, 0.123), (2.20, 0.122), (2.40, 0.122), (2.60, 0.122), (2.80, 0.122),
(3.00, 0.121), (3.20, 0.121), (3.40, 0.121), (3.60, 0.120), (3.80, 0.120),
(4.00, 0.120), (4.20, 0.120), (4.40, 0.120), (4.60, 0.120), (4.80, 0.120),
(5.00, 0.120)
]]
DRAG_MODELS: Dict[str, List[Tuple[float, float]]] = {
'G1': G1_DRAG_TABLE, 'G2': G2_DRAG_TABLE, 'G3': G3_DRAG_TABLE, 'G4':
G4_DRAG_TABLE,
'G5': G5_DRAG_TABLE, 'G6': G6_DRAG_TABLE, 'G7': G7_DRAG_TABLE, 'G8':
G8_DRAG_TABLE
}

# --- PARAMETER STANDAR DAN KONSTANTA FISIKA ---

SIMULATION_DT = 0.01 # Interval waktu integrasi (Detik)


MAX_SIMULATIONS = 10000
EARTH_ANGULAR_VELOCITY = 7.2921159e-5 # Kecepatan Sudut Rotasi Bumi (Rad/s)
R_GAS = 8.314462618 # Konstanta Gas Ideal (J/(mol·K))
MOLAR_MASS_AIR = 0.0289644 # Massa Molar Udara Kering (kg/mol)
MOLAR_MASS_WATER = 0.01801528 # Massa Molar Air (kg/mol)
GRAVITY = 9.80665 # Percepatan Gravitasi Standar (m/s^2)
C_MAGNUS = 0.0000002 # Konstanta Koefisien Magnus (Estimasi)
GAMMA = 1.4 # Rasio Kapasitas Panas Spesifik Udara (Adibiatik)
R_SPECIFIC = 287.058 # Konstanta Gas Spesifik Udara (J/(kg·K))
TOLERANCE = 1e-6 # Toleransi untuk solver DP87/Bulirsch-Stoer

# --- FUNGSI UTILITAS LBT (LAPORAN BALISTIK TAKTIS) ---

def slow_print(text, speed=1/100):


"""Fungsi tampilan naratif, disimulasikan sebagai transmisi data."""
for char in text:
[Link](char)
[Link]()
[Link](speed)
print()

def get_input_with_retry(prompt, type_func, min_val=None, max_val=None,


choices=None):
"""Fungsi validasi input pengguna untuk parameter balistik."""
while True:
try:
user_input = input(prompt)
if choices:
# Opsi Pilihan Tepat (mis. y/t, G1/G7)
if user_input.lower() not in choices:
print(f"**[ERROR.001]** Pilihan input tidak valid. Tersedia:
{', '.join(choices)}. Ulangi Masuk.")
continue
return user_input.lower()

value = type_func(user_input)
# Validasi Batasan Nilai
if min_val is not None and value < min_val:
print(f"**[ERROR.002]** Nilai masukan terlalu rendah. Min:
{min_val}. Ulangi Masuk.")
continue
if max_val is not None and value > max_val:
print(f"**[ERROR.003]** Nilai masukan terlalu tinggi. Max:
{max_val}. Ulangi Masuk.")
continue
return value
except ValueError:
print("**[ERROR.004]** Input format tidak valid. Masukkan nilai
numerik. Ulangi Masuk.")

def parse_unit_input(user_input: str) -> Tuple[float, str]:


"""Mendeteksi otomatis satuan meter (m) atau yard (y) dari input string."""
raw = user_input.lower().strip()
try:
if [Link]('y'):
val = float([Link]('y', ''))
return val * 0.9144, 'Yard'
elif [Link]('m'):
val = float([Link]('m', ''))
return val, 'Meter'
else:
# Default jika hanya angka
val = float(raw)
return val, 'Meter'
except ValueError:
return 0.0, 'Error'

# --- MODEL LINGKUNGAN DAN BALISTIK EKSTERNAL ---


def get_drag_coefficient(velocity, temperature_celsius, drag_model='G7'):
"""Interpolasi Koefisien Seret (Cd) berdasarkan Mach Number dan Model G-
Function."""
temp_kelvin = temperature_celsius + 273.15
speed_of_sound = [Link](GAMMA * R_SPECIFIC * temp_kelvin)
mach = velocity / speed_of_sound

drag_table = DRAG_MODELS.get(drag_model.upper(), G7_DRAG_TABLE)

if mach <= drag_table[0][0]:


return drag_table[0][1]
if mach >= drag_table[-1][0]:
return drag_table[-1][1]

# Interpolasi Linier
for i in range(len(drag_table) - 1):
if drag_table[i][0] <= mach < drag_table[i+1][0]:
x1, y1 = drag_table[i]
x2, y2 = drag_table[i+1]
if x2 - x1 == 0:
return y1
return y1 + (y2 - y1) * (mach - x1) / (x2 - x1)
return 0.2

def smart_drag_model_selection(velocity: float, temperature_celsius: float) -> str:


"""Pemilihan Model Drag (G-Function) optimal berdasarkan Mach Number awal."""
temp_kelvin = temperature_celsius + 273.15
speed_of_sound = [Link](GAMMA * R_SPECIFIC * temp_kelvin)
mach = velocity / speed_of_sound

# Pemilihan model adalah heuristic, G7 ideal untuk proyektil meriam/jarak jauh

if mach < 0.9:


return 'G1'
elif 0.9 <= mach < 1.1:
return 'G5'
elif 1.1 <= mach < 1.5:
return 'G2'
elif 1.5 <= mach < 2.0:
return 'G3'
elif 2.0 <= mach < 3.0:
return 'G4'
else:
return 'G7'

def calculate_air_density(temperature_celsius: float, pressure_hPa: float,


humidity_percent: float):
"""
Menghitung Densitas Udara (Rho) menggunakan persamaan Gas Ideal yang
dimodifikasi.
"""
T_kelvin = temperature_celsius + 273.15
P_pascal = pressure_hPa * 100
P_saturation = 6.1078 * 10**((7.5 * temperature_celsius) / (237.3 +
temperature_celsius)) * 100
P_vapor = (humidity_percent / 100) * P_saturation
P_dry_air = P_pascal - P_vapor
rho_dry_air = (P_dry_air * MOLAR_MASS_AIR) / (R_GAS * T_kelvin)
rho_vapor = (P_vapor * MOLAR_MASS_WATER) / (R_GAS * T_kelvin)
return rho_dry_air + rho_vapor

def get_wind_vector(altitude_m, wind_data):


"""Interpolasi kecepatan dan arah angin dari data multi-lapisan (Multi-Layer
Wind)."""
if not wind_data or len(wind_data) == 1:
single_wind = wind_data[0]
return float(single_wind['speed']), float(single_wind['angle'])

# Interpolasi Linier untuk Wind Shear/Gradient

for i in range(len(wind_data) - 1):


h1 = float(wind_data[i]['altitude'])
h2 = float(wind_data[i+1]['altitude'])
if h1 <= altitude_m < h2:
s1 = float(wind_data[i]['speed'])
s2 = float(wind_data[i+1]['speed'])
a1 = float(wind_data[i]['angle'])
a2 = float(wind_data[i+1]['angle'])

interp_factor = (altitude_m - h1) / (h2 - h1)

interpolated_speed = s1 + (s2 - s1) * interp_factor


interpolated_angle = a1 + (a2 - a1) * interp_factor

return interpolated_speed, interpolated_angle

return float(wind_data[-1]['speed']), float(wind_data[-1]['angle'])


# --- SUB-FUNGSI INTEGRATOR 6-DOF (SIX DEGREES OF FREEDOM) ---

def get_acceleration_6dof(state, t, mass, cross_sectional_area,


temperature_celsius,
pressure_hPa, humidity_percent, wind_vector_x,
wind_vector_y, wind_vector_z,
spin_rate_rad_s, bullet_diameter_m, shooter_latitude_rad,
drag_model,
shooter_direction_rad):
"""Menghitung vektor percepatan proyektil (Drag, Gravity, Coriolis, Magnus,
Eotvos)."""
x, y, z, vx, vy, vz = state

# Koreksi parameter atmosfer terhadap ketinggian (y)

current_pressure_hPa = pressure_hPa * (1 - (y * 0.0065 / (temperature_celsius +


273.15)))**5.256
current_humidity_percent = humidity_percent * (1 - y/10000)
current_rho = calculate_air_density(temperature_celsius, current_pressure_hPa,
current_humidity_percent)

# Kecepatan Relatif terhadap Angin

v_rel_x = vx - wind_vector_x
v_rel_y = vy - wind_vector_y
v_rel_z = vz - wind_vector_z

squared_total = v_rel_x**2 + v_rel_y**2 + v_rel_z**2


if squared_total < 0:
squared_total = 0.0
v_rel_total = [Link](squared_total)

vx_earth, vy_earth, vz_earth = vx, vy, vz

# Vektor Rotasi Bumi (Omega)

omega_x = 0.0
omega_y = EARTH_ANGULAR_VELOCITY * [Link](shooter_latitude_rad)
omega_z = EARTH_ANGULAR_VELOCITY * [Link](shooter_latitude_rad)

# 1. Drag (Hambatan Udara) Force

drag_coefficient = get_drag_coefficient(v_rel_total, temperature_celsius,


drag_model)
ax_drag, ay_drag, az_drag = 0.0, 0.0, 0.0
if v_rel_total > 0:
drag_force = 0.5 * current_rho * drag_coefficient * cross_sectional_area *
v_rel_total**2
ax_drag = -drag_force * v_rel_x / (mass * v_rel_total)
ay_drag = -drag_force * v_rel_y / (mass * v_rel_total)
az_drag = -drag_force * v_rel_z / (mass * v_rel_total)

# 2. Gravitational Force

ay_gravity = -GRAVITY

# 3. Coriolis Force (Efek Rotasi Bumi)


ax_coriolis = -2 * (omega_y * vz_earth - omega_z * vy_earth)
ay_coriolis = -2 * (omega_z * vx_earth - omega_x * vz_earth)
az_coriolis = -2 * (omega_x * vy_earth - omega_y * vx_earth)

# 4. Magnus Force (Efek Putaran Proyektil/Spin)

ax_magnus, ay_magnus, az_magnus = 0.0, 0.0, 0.0


if spin_rate_rad_s > 0 and v_rel_total > 0:
spin_vector = [spin_rate_rad_s, 0.0, 0.0] # Asumsi spin di sumbu X
(lintasan)

magnus_x = spin_vector[1] * v_rel_z - spin_vector[2] * v_rel_y


magnus_y = spin_vector[2] * v_rel_x - spin_vector[0] * v_rel_z
magnus_z = spin_vector[0] * v_rel_y - spin_vector[1] * v_rel_x

magnus_factor = C_MAGNUS * current_rho * (bullet_diameter_m**3)


ax_magnus = magnus_factor * magnus_x / mass
ay_magnus = magnus_factor * magnus_y / mass
az_magnus = magnus_factor * magnus_z / mass

# 5. Eotvos Force (Komponen Coriolis vertikal yang dihasilkan oleh gerakan


lintang)

eotvos_accel = 0.0
# Eotvos adalah bagian dari Coriolis, hanya berlaku untuk komponen Vertikal (Y)
# Tanda positif untuk gerakan ke Timur, negatif untuk Barat (asumsi arah 0-180
= Timur/Utara, 180-360 = Barat/Selatan)
if [Link](shooter_direction_rad) > 0: # Gerakan ke Utara/Timur (Longitude
Positif)
eotvos_accel = 2 * vx_earth * EARTH_ANGULAR_VELOCITY *
[Link](shooter_latitude_rad)
else:
eotvos_accel = -2 * vx_earth * EARTH_ANGULAR_VELOCITY *
[Link](shooter_latitude_rad)

# Total Vektor Percepatan

ax = ax_drag + ax_coriolis + ax_magnus


ay = ay_drag + ay_gravity + ay_coriolis + ay_magnus + eotvos_accel
az = az_drag + az_coriolis + az_magnus

return [ax, ay, az]

# --- INTEGRATOR BALISTIK BULIRSCH-STOER ---

def modified_midpoint_step(state_start, t_start, H, num_steps, mass,


cross_sectional_area, temperature_celsius,
pressure_hPa, humidity_percent, wind_vector_x,
wind_vector_y, wind_vector_z,
spin_rate_rad_s, bullet_diameter_m,
shooter_latitude_rad, drag_model, shooter_direction_rad):
"""
Melakukan satu langkah Modifikasi Midpoint untuk Integrator Bulirsch-Stoer.
Digunakan untuk mencapai presisi tinggi melalui Ekstrapolasi Richardson.
"""
dt = H / num_steps
state = state_start[:]
t = t_start
# Langkah Pertama (Euler/Estimasi Awal)

accel = get_acceleration_6dof(state, t, mass, cross_sectional_area,


temperature_celsius, pressure_hPa, humidity_percent,
wind_vector_x, wind_vector_y, wind_vector_z,
spin_rate_rad_s, bullet_diameter_m, shooter_latitude_rad, drag_model,
shooter_direction_rad)

y_prev = state[0:3]
y = [state[i] + dt * state[i+3] for i in range(3)]
v = [state[i+3] + dt * accel[i] for i in range(3)]

# Langkah Berikutnya (Modified Midpoint/Frog-Leap)


for _ in range(num_steps - 1):
t += dt
accel = get_acceleration_6dof(y + v, t, mass, cross_sectional_area,
temperature_celsius, pressure_hPa, humidity_percent,
wind_vector_x, wind_vector_y, wind_vector_z,
spin_rate_rad_s, bullet_diameter_m, shooter_latitude_rad, drag_model,
shooter_direction_rad)

y_temp = y[:]
for i in range(3):
# Rumus Midpoint (Leapfrog-esque)
y[i] = y_prev[i] + 2 * dt * v[i]
v[i] = v[i] + 2 * dt * accel[i]

y_prev = y_temp

return y + v

def solve_trajectory_bulirsch_stoer(params_tuple):
"""
Solver Balistik 6-DOF presisi tinggi menggunakan Bulirsch-Stoer.
"""
try:
(angle, mass, velocity, target_distance, wind_data, shooter_direction,
bullet_diameter_mm,
temperature_celsius, pressure_hPa, humidity_percent, spin_rate_rpm,
shooter_latitude_deg, drag_model) = params_tuple

bullet_diameter_m = bullet_diameter_mm / 1000.0


cross_sectional_area = [Link] * (bullet_diameter_m / 2.0)**2
shooter_direction_rad = [Link](shooter_direction)
shooter_latitude_rad = [Link](shooter_latitude_deg)
spin_rate_rad_s = spin_rate_rpm * (2 * [Link] / 60.0)

t = 0.0
x, y, z = 0.0, 0.0, 0.0
vx = velocity * [Link](angle)
vy = velocity * [Link](angle)
vz = 0.0

state = [x, y, z, vx, vy, vz]

wind_speed, wind_angle = get_wind_vector(y, wind_data)


relative_wind_angle = wind_angle - [Link](shooter_direction_rad)
relative_wind_angle_rad = [Link](relative_wind_angle)
wind_vector_x = -wind_speed * [Link](relative_wind_angle_rad)
wind_vector_z = -wind_speed * [Link](relative_wind_angle_rad)
wind_vector_y = 0.0

# Loop Integrasi Utama

while y >= -1.0 and x < target_distance * 1.5:


H = SIMULATION_DT # Interval Utama

# Sequence Langkah Sub-Integrasi (Ekstrapolasi Richardson)

steps_sequence = [2, 4, 6, 8]
results = []

for n_steps in steps_sequence:


next_state = modified_midpoint_step(state, t, H, n_steps, mass,
cross_sectional_area, temperature_celsius,
pressure_hPa, humidity_percent,
wind_vector_x, wind_vector_y, wind_vector_z,
spin_rate_rad_s,
bullet_diameter_m, shooter_latitude_rad, drag_model, shooter_direction_rad)
[Link](next_state)

# Ekstrapolasi Richardson (Polinomial)


try:
extrapolated_results = [[0.0] * 6 for _ in
range(len(steps_sequence))]

for i in range(len(steps_sequence)):
extrapolated_results[i] = results[i][:]

# Formula Ekstrapolasi Polinomial

for k in range(1, len(steps_sequence)):


for j in range(len(steps_sequence) - k):
ratio_sq = (steps_sequence[j+k] / steps_sequence[j])**2
for i in range(6):
extrapolated_results[j][i] = (ratio_sq *
extrapolated_results[j+1][i] - extrapolated_results[j][i]) / (ratio_sq - 1)

state = extrapolated_results[0]

except Exception as e:
# Fallback ke hasil langkah terakhir jika Ekstrapolasi gagal
state = results[-1]

x, y, z, vx, vy, vz = state


t += H

final_velocity = [Link](vx**2 + vy**2 + vz**2)


final_ke = 0.5 * mass * final_velocity**2
final_momentum = mass * final_velocity

# Interpolasi Linear untuk titik R-Max (Jarak Target)

last_x, last_y, last_z = x - vx * H, y - vy * H, z - vz * H


if x != last_x:
interp_factor = (target_distance - last_x) / (x - last_x)
drop_at_target = last_y + (y - last_y) * interp_factor
drift_at_target = last_z + (z - last_z) * interp_factor
t_interp = t - H + (H * interp_factor)
return (-drop_at_target, drift_at_target, t_interp, final_ke,
final_momentum)
else:
return (-y, z, t, final_ke, final_momentum)
except Exception as e:
return None

def solve_trajectory_for_bisection(params_tuple):
"""Solver yang disederhanakan untuk metode Bisection (hanya mengembalikan jarak
horizontal)."""
(angle, mass, velocity, target_distance, wind_data, shooter_direction,
bullet_diameter_mm,
temperature_celsius, pressure_hPa, humidity_percent, spin_rate_rpm,
shooter_latitude_deg, drag_model) = params_tuple

bullet_diameter_m = bullet_diameter_mm / 1000.0


cross_sectional_area = [Link] * (bullet_diameter_m / 2.0)**2
shooter_direction_rad = [Link](shooter_direction)
shooter_latitude_rad = [Link](shooter_latitude_deg)
spin_rate_rad_s = spin_rate_rpm * (2 * [Link] / 60.0)

t = 0.0
x, y, z = 0.0, 0.0, 0.0
vx = velocity * [Link](angle)
vy = velocity * [Link](angle)
vz = 0.0
state = [x, y, z, vx, vy, vz]

wind_speed, wind_angle = get_wind_vector(y, wind_data)


relative_wind_angle = wind_angle - [Link](shooter_direction_rad)
relative_wind_angle_rad = [Link](relative_wind_angle)
wind_vector_x = -wind_speed * [Link](relative_wind_angle_rad)
wind_vector_z = -wind_speed * [Link](relative_wind_angle_rad)
wind_vector_y = 0.0

# Loop Integrasi

while y >= -1.0 and x < target_distance * 1.5:


H = SIMULATION_DT
steps_sequence = [2, 4, 6, 8]
results = []

for n_steps in steps_sequence:


next_state = modified_midpoint_step(state, t, H, n_steps, mass,
cross_sectional_area, temperature_celsius,
pressure_hPa, humidity_percent,
wind_vector_x, wind_vector_y, wind_vector_z,
spin_rate_rad_s, bullet_diameter_m,
shooter_latitude_rad, drag_model, shooter_direction_rad)
[Link](next_state)

try:
# Ekstrapolasi
extrapolated_results = [[0.0] * 6 for _ in range(len(steps_sequence))]
for i in range(len(steps_sequence)):
extrapolated_results[i] = results[i][:]

for k in range(1, len(steps_sequence)):


for j in range(len(steps_sequence) - k):
ratio_sq = (steps_sequence[j+k] / steps_sequence[j])**2
for i in range(6):
extrapolated_results[j][i] = (ratio_sq *
extrapolated_results[j+1][i] - extrapolated_results[j][i]) / (ratio_sq - 1)

state = extrapolated_results[0]
except Exception:
state = results[-1]

x = state[0]
y = state[1]
t += H

return x

def solve_single_mc_run(params_dict):
"""Pembungkus fungsi untuk eksekusi Monte Carlo."""
return solve_trajectory_bulirsch_stoer((
params_dict['angle'], params_dict['mass'], params_dict['velocity'],
params_dict['target_distance'], params_dict['wind_data'],
params_dict['shooter_direction'], params_dict['bullet_diameter_mm'],
params_dict['temperature_celsius'], params_dict['pressure_hPa'],
params_dict['humidity_percent'], params_dict['spin_rate_rpm'],
params_dict['shooter_latitude_deg'], params_dict['drag_model']
))

def solve_trajectory_for_plot(params_tuple):
"""
Menjalankan satu run Integrator Bulirsch-Stoer dan mengembalikan
daftar koordinat (X, Y, Z) untuk visualisasi lintasan.
"""
try:
(angle, mass, velocity, target_distance, wind_data, shooter_direction,
bullet_diameter_mm,
temperature_celsius, pressure_hPa, humidity_percent, spin_rate_rpm,
shooter_latitude_deg, drag_model) = params_tuple

bullet_diameter_m = bullet_diameter_mm / 1000.0


cross_sectional_area = [Link] * (bullet_diameter_m / 2.0)**2
shooter_direction_rad = [Link](shooter_direction)
shooter_latitude_rad = [Link](shooter_latitude_deg)
spin_rate_rad_s = spin_rate_rpm * (2 * [Link] / 60.0)

t = 0.0
x, y, z = 0.0, 0.0, 0.0
vx = velocity * [Link](angle)
vy = velocity * [Link](angle)
vz = 0.0
state = [x, y, z, vx, vy, vz]
trajectory_points = [(x, y, z)]

wind_speed, wind_angle = get_wind_vector(y, wind_data)


relative_wind_angle = wind_angle - [Link](shooter_direction_rad)
relative_wind_angle_rad = [Link](relative_wind_angle)
wind_vector_x = -wind_speed * [Link](relative_wind_angle_rad)
wind_vector_z = -wind_speed * [Link](relative_wind_angle_rad)
wind_vector_y = 0.0
while y >= -1.0 and x < target_distance * 1.5:
H = SIMULATION_DT
steps_sequence = [2, 4, 6, 8]
results = []

for n_steps in steps_sequence:


next_state = modified_midpoint_step(state, t, H, n_steps, mass,
cross_sectional_area, temperature_celsius,
pressure_hPa, humidity_percent,
wind_vector_x, wind_vector_y, wind_vector_z,
spin_rate_rad_s,
bullet_diameter_m, shooter_latitude_rad, drag_model, shooter_direction_rad)
[Link](next_state)

try:
extrapolated_results = [[0.0] * 6 for _ in
range(len(steps_sequence))]
for i in range(len(steps_sequence)):
extrapolated_results[i] = results[i][:]

for k in range(1, len(steps_sequence)):


for j in range(len(steps_sequence) - k):
ratio_sq = (steps_sequence[j+k] / steps_sequence[j])**2
for i in range(6):
extrapolated_results[j][i] = (ratio_sq *
extrapolated_results[j+1][i] - extrapolated_results[j][i]) / (ratio_sq - 1)

state = extrapolated_results[0]
except Exception:
state = results[-1]

x, y, z, vx, vy, vz = state


t += H
trajectory_points.append((x, y, z))
return trajectory_points
except Exception as e:
print(f"**[[Link]]** Gagal dalam integrasi plot lintasan: {e}")
return []

def plot_trajectory_matplotlib(trajectory_points, target_distance,


filename="LBT_Lintasan.jpg"):
"""
Membuat dan menyimpan plot Matplotlib dari Lintasan Proyektil (X vs Y).

"""
if not trajectory_points:
print("**[INFO]** Tidak ada data lintasan yang valid untuk diplot.")
return

x_values = [p[0] for p in trajectory_points]


y_values = [p[1] for p in trajectory_points]

fig, ax1 = [Link](1, 1, figsize=(10, 6))

# Plot Lintasan Vertikal (X vs Y)

[Link](x_values, y_values, label="Lintasan Proyektil", color='blue')


[Link](x=target_distance, color='red', linestyle='--', label='Jarak Tempuh
Horizontal (R-Max)')
[Link](y=0, color='black', linestyle='-')
ax1.set_title("Visualisasi Balistik Eksternal (Deviasi Vertikal vs Jarak)")
ax1.set_xlabel("Jarak Horizontal (R, meter)")
ax1.set_ylabel("Deviasi Vertikal (Y, meter)")
[Link](True)
[Link]()

plt.tight_layout()
[Link](block=False)

save_choice = get_input_with_retry("\n**[INPUT]** LBT: Simpan Plot Visualisasi?


(y/t): ", str, choices=['y', 't'])
if save_choice == 'y':
save_name = input("Nama Berkas Gambar (mis. OPERASI_A01): ")
if not save_name:
save_name = filename
elif not save_name.endswith(('.jpg', '.png', '.jpeg')):
save_name += '.jpg'

# Simpan di direktori standar Android/Penyimpanan Internal


save_path = "/storage/emulated/0/stcof/LBT_Reports/"
[Link](save_path, exist_ok=True)
full_path = [Link](save_path, save_name)
[Link](full_path)
print(f"\n**[LOG]** Plot Lintasan (LBT) disimpan sebagai: **{full_path}**")

[Link](fig)

# --- UNIT TEST (Verifikasi Numerik Dasar) ---

class TrajectoryTests([Link]):
def setUp(self):
[Link] = 0.01089
[Link] = 823.0

def test_ke(self):
ke = 0.5 * [Link] * [Link]**2
[Link](ke, 3688.0, places=0)

def test_momentum(self):
momentum = [Link] * [Link]
[Link](momentum, 8.96, places=2)

def test_momentum(self):
"""Uji Momentum (kg·m/s) pada Kecepatan Moncong."""
momentum = [Link] * [Link]
[Link](momentum, 8.964, places=1)

# --- BLOK EKSEKUSI UTAMA (Laporan Balistik Taktis) ---

if __name__ == "__main__":

# Konfigurasi Log (Simulasi Log Militer)

[Link](
level=[Link],
format='[LOG:%(levelname)s] [%(asctime)s] - %(message)s',
datefmt='%H:%M:%S'
)

[Link](argv=['first-arg-is-ignored'], exit=False)

slow_print("\n>>> **SISTEM BALISTIK TERINTEGRASI (SBT) v7.1.A AKTIF** <<<")


slow_print("----------------------------------------------------------")
slow_print("MEMULAI PROSES KOMPUTASI **LAPORAN BALISTIK TAKTIS (LBT)**")

while True:
try:
# --- INPUT PARAMETER DASAR AMUNISI & MISI ---

slow_print("\n## FASE 1: KONFIGURASI PROYEKTIL & PLATFORM")


mass = float(get_input_with_retry("[INPUT] Massa Proyektil (kg, mis.
0.0109): ", float, 0.001, 100))
target_distance = float(get_input_with_retry("[INPUT] Jarak Tempuh
Horizontal (R-Max, meter): ", float, 1, 50000))
velocity = float(get_input_with_retry("[INPUT] Kecepatan Moncong Awal
(Vmo, m/s): ", float, 100, 3000))
bullet_diameter_mm = float(get_input_with_retry("[INPUT] Kaliber
Proyektil (Diameter, mm): ", float, 1, 500))

spin_type = get_input_with_retry("[INPUT] Konfigurasi Laras


(Smoothbore/Rifled): s/r ", str, choices=['s', 'r'])

spin_rate_rpm = 0.0
if spin_type == 'r':
while True:
spin_input = input("[INPUT] Twist Laras (cth: 1/15) atau RPM
Stabilisasi (cth: 300000): ")
if '/' in spin_input:
try:
parts = spin_input.split('/')
if len(parts) == 2:
twist_rate_in_inches = float(parts[1])
if twist_rate_in_inches > 0:
spin_rate_rpm = (velocity * 720) /
twist_rate_in_inches
slow_print(f"[LOG] RPM Proyektil Taksiran:
**{spin_rate_rpm:.1f}**")
break
else:
print("**[[Link]]** Nilai twist laras
(pembagi) tidak valid.")
else:
print("**[[Link]]** Format twist laras tidak
valid. Gunakan format '1/x'.")
except (ValueError, IndexError):
print("**[[Link]]** Format twist laras tidak valid.
Gunakan format '1/x'.")
else:
try:
spin_rate_rpm = float(spin_input)
if spin_rate_rpm > 0:
twist_rate_in_inches = (velocity * 720) /
spin_rate_rpm
slow_print(f"[LOG] Twist Laras Ekuivalen:
**1/{twist_rate_in_inches:.1f}**")
break
else:
print("**[[Link]]** Nilai RPM harus lebih besar
dari 0.")
except ValueError:
print("**[[Link]]** Input tidak valid. Masukkan
'1/x' atau nilai RPM.")
else: # spin_type == 's'
spin_rate_rpm = 0.0
slow_print("[LOG] Laras Non-Rifled. Mengatur RPM Proyektil ke
**0.0**.")

gas_choice = get_input_with_retry("[INPUT] Termasuk Efek Dorongan


Gas/Plasma? (y/t): ", str, choices=['y', 't'])
gas_boost = 0.0
if gas_choice == 'y':
gas_type = get_input_with_retry("[INPUT] Jenis Gas
(CO/N2O/Propelan): ", str, choices=['co', 'n2o', 'propelan'])
gas_boost = 0.001 * velocity * 0.005 # Estimasi dorongan kecil
slow_print(f"[LOG] Menerapkan **{gas_boost:.3f} m/s** Dorongan
Kecepatan Inisial.")
velocity += gas_boost

# --- INPUT PARAMETER ATMOSFER & LOKASI ---

slow_print("\n## FASE 2: KONFIGURASI LINGKUNGAN (ATMOSFER &


KORIOLLIS)")
temperature_celsius = float(get_input_with_retry("[INPUT] Suhu Udara
(Celcius): ", float, -50, 60))
pressure_hPa = float(get_input_with_retry("[INPUT] Tekanan Barometrik
(hPa, Std: 1013.25): ", float, 300, 1100))
humidity_percent = float(get_input_with_retry("[INPUT] Kelembaban
Relatif (%RH, 0-100): ", float, 0, 100))
shooter_direction = float(get_input_with_retry("[INPUT] Azimuth
Penembakan (Derajat Kompas, 0=Utara): ", float, 0, 360))
shooter_latitude_deg = float(get_input_with_retry("[INPUT] Lintang
Geografis (Derajat, Positif=Utara): ", float, -90, 90))

drag_model = smart_drag_model_selection(velocity, temperature_celsius)


slow_print(f"[LOG] Sistem Mengalibrasi Drag Model Optimal:
**'{drag_model}'**.")

# --- INPUT PARAMETER ANGIN (WIND SHEAR) ---

wind_data = []
multilayer_choice = get_input_with_retry("[INPUT] Input Angin Multi-
Lapisan (Wind Shear)? (y/t): ", str, choices=['y', 't'])
if multilayer_choice == 'y':
num_layers = get_input_with_retry("[INPUT] Jumlah Lapisan Angin: ",
int, 1, 10)
for i in range(num_layers):
altitude = get_input_with_retry(f"[INPUT] Ketinggian Lapisan
{i+1} (meter): ", float, 0, None)
speed_kmh = get_input_with_retry(f"[INPUT] Kecepatan Angin
Lapisan {i+1} (km/h): ", float, 0, 3000)
angle = get_input_with_retry(f"[INPUT] Arah Angin Lapisan {i+1}
(Derajat Kompas): ", float, 0, 360)
wind_data.append({'altitude': altitude, 'speed': speed_kmh /
3.6, 'angle': angle}) # Konversi ke m/s
wind_data.sort(key=lambda x: x['altitude'])
else:
wind_speed_kmh = float(get_input_with_retry("[INPUT] Kecepatan
Angin Tunggal (km/h): ", float, 0, 3000))
wind_angle = float(get_input_with_retry("[INPUT] Arah Angin Tunggal
(Derajat Kompas): ", float, 0, 360))
wind_data.append({'altitude': 0.0, 'speed': wind_speed_kmh / 3.6,
'angle': wind_angle})

num_simulations = get_input_with_retry("[INPUT] Jumlah Iterasi Monte-


Carlo (N, mis. 5000): ", int, 1, MAX_SIMULATIONS)

# --- FASE 3: PENENTUAN SUDUT ELEVASI OPTIMAL (BISECTION) ---

slow_print("\n## FASE 3: KOMPUTASI SOLUSI TRAJECTORY FUNDAMENTAL


(Bisection)")
slow_print("[LOG] Menganalisis Sudut Elevasi Tembak Optimal (Zero-
Angle)...")

low_angle = 0.0
high_angle = [Link] / 4.0 # Batas awal diatur 45 derajat (Angle
Stopper)
# 20 Iterasi Bisection cukup untuk presisi tinggi (10^-6 rad)
for _ in range(20):
mid_angle = (low_angle + high_angle) / 2.0
params_to_test = (mid_angle, mass, velocity, target_distance,
wind_data, shooter_direction, bullet_diameter_mm,
temperature_celsius, pressure_hPa,
humidity_percent, spin_rate_rpm, shooter_latitude_deg, drag_model)
final_range = solve_trajectory_for_bisection(params_to_test)
if final_range is None: raise Exception("Bisection Solver Failure")
if final_range > target_distance:
high_angle = mid_angle
else:
low_angle = mid_angle
best_angle = (low_angle + high_angle) / 2.0 if [Link]((low_angle
+ high_angle) / 2.0) <= 45 else [Link]("\n[ABORT] CRITICAL: Sudut Melebihi Batas
Militer 45°!")

# --- FASE 4: SIMULASI MONTE CARLO (Toleransi Stochastik) ---

slow_print("\n## FASE 4: SIMULASI STATISTIK MONTE CARLO (N-ITERASI)")


slow_print(f"[LOG] Menggunakan **{cpu_count()}** Proses Komputasi
Paralel.")
slow_print("[LOG] **STATUS: PROSES PERHITUNGAN MULTI-PROSESOR
AKTIF...**")
print("\n== Progress Iterasi MC ==")

all_results = []
total_runs_completed = 0
batch_size = 8 # Ukuran batch awal eksponensial

# Distribusi normal untuk kecepatan (0.5%) dan angin (kecepatan 0.5


m/s, sudut 5 deg)
VMO_STD_DEV = 0.005
WIND_SPEED_STD_DEV = 0.5
WIND_ANGLE_STD_DEV = 5.0

with ProcessPoolExecutor(max_workers=cpu_count()) as executor:


start_time_all = [Link]()
while total_runs_completed < num_simulations:

runs_in_this_batch = min(batch_size, num_simulations -


total_runs_completed)
if runs_in_this_batch <= 0: break

batch_params = []
for i in range(runs_in_this_batch):
# Terapkan Variasi Stochastik (Simulasi Toleransi
Amunisi/Lingkungan)
velocity_factor = [Link](1.0, VMO_STD_DEV) # Vmo

stochastic_params = {
'angle': best_angle,
'mass': mass,
'velocity': velocity * velocity_factor,
'target_distance': target_distance,
'wind_data': [],
'shooter_direction': shooter_direction,
'bullet_diameter_mm': bullet_diameter_mm,
'temperature_celsius': temperature_celsius,
'pressure_hPa': pressure_hPa,
'humidity_percent': humidity_percent,
'spin_rate_rpm': spin_rate_rpm,
'shooter_latitude_deg': shooter_latitude_deg,
'drag_model': drag_model
}

if wind_data:
original_wind = wind_data[0]
stochastic_wind_speed =
[Link](original_wind['speed'], WIND_SPEED_STD_DEV)
stochastic_wind_angle =
[Link](original_wind['angle'], WIND_ANGLE_STD_DEV)
stochastic_params['wind_data'].append({'altitude':
original_wind['altitude'], 'speed': stochastic_wind_speed, 'angle':
stochastic_wind_angle})

batch_params.append(stochastic_params)

start_time_batch = [Link]()
# Eksekusi Paralel
batch_results = list([Link](solve_single_mc_run,
batch_params))
end_time_batch = [Link]()
execution_time_batch = end_time_batch - start_time_batch

all_results.extend(batch_results)
total_runs_completed += runs_in_this_batch

print(f"**[PROGRESS]** Iterasi Selesai:


{total_runs_completed}/{num_simulations} | Waktu Batch:
{execution_time_batch:.2f}s")

# Eksponensial Batching
batch_size = min(batch_size * 2, num_simulations -
total_runs_completed)

end_time_all = [Link]()
total_execution_time = end_time_all - start_time_all

valid_results = [r for r in all_results if r is not None]

if not valid_results:
slow_print("\n**[CRITICAL ERROR]** Semua simulasi gagal. Verifikasi
Parameter Input.")
continue

all_drops = [r[0] for r in valid_results]


all_drifts = [r[1] for r in valid_results]
all_t_final = [r[2] for r in valid_results]
all_final_ke = [r[3] for r in valid_results]
all_final_momentum = [r[4] for r in valid_results]

avg_drop = [Link](all_drops)
avg_drift = [Link](all_drifts)
avg_t_final = [Link](all_t_final)
avg_final_ke = [Link](all_final_ke)
avg_final_momentum = [Link](all_final_momentum)

std_dev_drop = [Link](all_drops) if len(all_drops) > 1 else 0


std_dev_drift = [Link](all_drifts) if len(all_drifts) > 1
else 0

# --- FASE 5: OUTPUT LAPORAN BALISTIK TAKTIS (LBT) ---

slow_print("\n\n#####################################################")
slow_print("## LAPORAN BALISTIK TAKTIS (LBT) - Selesai")
slow_print("#####################################################")

# --- RINGKASAN TEKNIS ---

print("\n### Ringkasan Eksekusi Sistem (SBT)")


print(f"Integrator Balistik: Bulirsch-Stoer (6-DOF) & Monte Carlo
(N={len(valid_results)})")
print(f"Model Seret (Cd): {drag_model} (Otomatis)")
print(f"Waktu Komputasi Total:{total_execution_time:.2f} detik")

# --- DATA HASIL LBT ---

print("\n### Data Balistik Terminal (R-Max:


{:.0f}m)".format(target_distance))
print(f"Sudut Elevasi Tembak (E-Angle):{[Link](best_angle):.3f}
derajat")
print(f"Waktu Terbang (TOF) Rata-rata:{avg_t_final:.3f} detik")
print(f"Energi Kinetik Sisa (KE) Rata-rata:{avg_final_ke:.0f} Joule")
print(f"Momentum Sisa (P) Rata-rata:{avg_final_momentum:.3f} kg·m/s")

# --- KOREKSI TEMBAKAN (FIRE SOLUTION) ---

print("\n### Koreksi Tembakan & Deviasi (Fire Solution)")

drop_mil, drop_clicks = calculate_reticle_adjustments(avg_drop,


target_distance)
print("#### Koreksi Vertikal (Drop/Elevation):")
print(f"Deviasi Vertikal (Mean): {avg_drop:.3f} meter")
print(f"Penyebaran Vertikal (StDev): {std_dev_drop:.3f} meter")
print(f"Koreksi Sudut (MIL):{drop_mil:.2f} MIL")
print(f"Klik Turret (0.1 MIL):{drop_clicks:.1f} Klik Up")

drift_mil, drift_clicks = calculate_reticle_adjustments(avg_drift,


target_distance)
print("#### Koreksi Lateral (Drift/Windage):")
print(f"Deviasi Lateral (Mean): {avg_drift:.3f} meter")
print(f"Penyebaran Lateral (StDev):{std_dev_drift:.3f} meter")
print(f"Koreksi Sudut (MIL):{drift_mil:.2f} MIL")

# Menentukan arah klik lateral

lateral_direction = "Kanan" if avg_drift > 0 else "Kiri"


print(f"Klik Turret (0.1 MIL):{drift_clicks:.1f} Klik
{lateral_direction}")

# --- PANDUAN RETICLE ---

print("\n### Panduan Reticle Taktis (Sniper Precision)")

elevation_hold = "DI ATAS (Holdover)" if avg_drop >= 0 else "DI BAWAH


(Underhold)"
windage_hold = "KIRI (Hold-Left)" if avg_drift > 0 else "KANAN (Hold-
Right)"

print(f"Tahan Vertikal: Bidik {drop_mil:.2f} MIL {elevation_hold} dari


titik pusat.")
print(f"Tahan Lateral : Bidik {abs(drift_mil):.2f} MIL ke
{windage_hold} dari titik pusat.")

print(f"\n[ANALISIS SNIPER] Keraguan Horizontal (CEP):


{std_dev_drift:.3f}m")
print(f"[ANALISIS SNIPER] Keraguan Vertikal (CEP):
{std_dev_drop:.3f}m")

# --- VISUALISASI LINTASAN ---

plot_params = (best_angle, mass, velocity, target_distance, wind_data,


shooter_direction, bullet_diameter_mm,
temperature_celsius, pressure_hPa,
humidity_percent, spin_rate_rpm, shooter_latitude_deg, drag_model)
trajectory_data = solve_trajectory_for_plot(plot_params)

if trajectory_data:
plot_trajectory_matplotlib(trajectory_data, target_distance)

except Exception as e:
slow_print("\n**[CRITICAL [Link]]** Gagal Eksekusi LBT. Log
Traceback:")
slow_print(f"Error Spesifik: {e}")
traceback.print_exc()

if get_input_with_retry("\n[INPUT] LBT: Jalankan Simulasi Lanjutan? (y/t):


", str, choices=['y', 't']) != 'y':
slow_print("\n>>> SBT SHUTDOWN <<<")
break
[Link]

import unittest
import math
import time
import sys
import traceback
import random
import statistics
import logging
from typing import Dict, List, Tuple
from multiprocessing import cpu_count
from [Link] import ProcessPoolExecutor
from joblib import Parallel, delayed
import [Link] as plt
import os

# Standard drag models and constants (using standard float for precision)
G1_DRAG_TABLE = [(x, y) for x, y in [
(0.00, 0.220), (0.10, 0.219), (0.20, 0.217), (0.30, 0.214), (0.40, 0.208),
(0.50, 0.199), (0.60, 0.187), (0.70, 0.170), (0.80, 0.153), (0.90, 0.137),
(1.00, 0.125), (1.10, 0.122), (1.20, 0.121), (1.30, 0.120), (1.40, 0.120),
(1.50, 0.120), (1.60, 0.120), (1.70, 0.120), (1.80, 0.120), (1.90, 0.120),
(2.00, 0.120), (2.20, 0.120), (2.40, 0.120), (2.60, 0.120), (2.80, 0.120),
(3.00, 0.120), (3.20, 0.120), (3.40, 0.120), (3.60, 0.120), (3.80, 0.120),
(4.00, 0.1505), (4.20, 0.1500), (4.40, 0.1495), (4.60, 0.1490), (4.80, 0.1485),
(5.00, 0.1480)
]]
G2_DRAG_TABLE = [(x, y) for x, y in [
(0.00, 0.204), (0.10, 0.203), (0.20, 0.202), (0.30, 0.200), (0.40, 0.196),
(0.50, 0.190), (0.60, 0.181), (0.70, 0.169), (0.80, 0.155), (0.90, 0.141),
(1.00, 0.128), (1.10, 0.125), (1.20, 0.124), (1.30, 0.123), (1.40, 0.123),
(1.50, 0.123), (1.60, 0.123), (1.70, 0.123), (1.80, 0.123), (1.90, 0.123),
(2.00, 0.123), (2.20, 0.122), (2.40, 0.122), (2.60, 0.122), (2.80, 0.122),
(3.00, 0.121), (3.20, 0.121), (3.40, 0.121), (3.60, 0.120), (3.80, 0.120),
(4.00, 0.120), (4.20, 0.120), (4.40, 0.120), (4.60, 0.120), (4.80, 0.120),
(5.00, 0.120)
]]
G3_DRAG_TABLE = [(x, y) for x, y in [
(0.00, 0.443), (0.10, 0.441), (0.20, 0.437), (0.30, 0.430), (0.40, 0.419),
(0.50, 0.402), (0.60, 0.378), (0.70, 0.350), (0.80, 0.316), (0.90, 0.285),
(1.00, 0.260), (1.10, 0.252), (1.20, 0.245), (1.30, 0.240), (1.40, 0.237),
(1.50, 0.235), (1.60, 0.233), (1.70, 0.231), (1.80, 0.230), (1.90, 0.229),
(2.00, 0.228), (2.20, 0.227), (2.40, 0.226), (2.60, 0.225), (2.80, 0.224),
(3.00, 0.223), (3.20, 0.222), (3.40, 0.221), (3.60, 0.220), (3.80, 0.219),
(4.00, 0.218), (4.20, 0.217), (4.40, 0.216), (4.60, 0.215), (4.80, 0.214),
(5.00, 0.213)
]]
G4_DRAG_TABLE = [(x, y) for x, y in [
(0.00, 0.219), (0.10, 0.218), (0.20, 0.216), (0.30, 0.213), (0.40, 0.207),
(0.50, 0.198), (0.60, 0.186), (0.70, 0.169), (0.80, 0.152), (0.90, 0.136),
(1.00, 0.124), (1.10, 0.121), (1.20, 0.120), (1.30, 0.119), (1.40, 0.119),
(1.50, 0.119), (1.60, 0.119), (1.70, 0.119), (1.80, 0.119), (1.90, 0.119),
(2.00, 0.119), (2.20, 0.118), (2.40, 0.118), (2.60, 0.118), (2.80, 0.118),
(3.00, 0.117), (3.20, 0.117), (3.40, 0.117), (3.60, 0.116), (3.80, 0.116),
(4.00, 0.116), (4.20, 0.115), (4.40, 0.115), (4.60, 0.115), (4.80, 0.114),
(5.00, 0.114)
]]
G5_DRAG_TABLE = [(x, y) for x, y in [
(0.00, 0.224), (0.10, 0.223), (0.20, 0.221), (0.30, 0.218), (0.40, 0.212),
(0.50, 0.203), (0.60, 0.191), (0.70, 0.174), (0.80, 0.157), (0.90, 0.141),
(1.00, 0.128), (1.10, 0.125), (1.20, 0.124), (1.30, 0.123), (1.40, 0.123),
(1.50, 0.123), (1.60, 0.123), (1.70, 0.123), (1.80, 0.123), (1.90, 0.123),
(2.00, 0.123), (2.20, 0.122), (2.40, 0.122), (2.60, 0.122), (2.80, 0.122),
(3.00, 0.121), (3.20, 0.121), (3.40, 0.121), (3.60, 0.120), (3.80, 0.120),
(4.00, 0.120), (4.20, 0.120), (4.40, 0.120), (4.60, 0.120), (4.80, 0.120),
(5.00, 0.120)
]]
G6_DRAG_TABLE = [(x, y) for x, y in [
(0.00, 0.245), (0.10, 0.244), (0.20, 0.242), (0.30, 0.239), (0.40, 0.232),
(0.50, 0.222), (0.60, 0.209), (0.70, 0.191), (0.80, 0.173), (0.90, 0.156),
(1.00, 0.142), (1.10, 0.139), (1.20, 0.138), (1.30, 0.137), (1.40, 0.137),
(1.50, 0.137), (1.60, 0.137), (1.70, 0.137), (1.80, 0.137), (1.90, 0.137),
(2.00, 0.137), (2.20, 0.136), (2.40, 0.136), (2.60, 0.136), (2.80, 0.136),
(3.00, 0.135), (3.20, 0.135), (3.40, 0.135), (3.60, 0.134), (3.80, 0.134),
(4.00, 0.134), (4.20, 0.133), (4.40, 0.133), (4.60, 0.133), (4.80, 0.132),
(5.00, 0.132)
]]
G7_DRAG_TABLE = [(x, y) for x, y in [
(0.00, 0.2300), (0.10, 0.2295), (0.20, 0.2285), (0.30, 0.2265),
(0.40, 0.2230), (0.50, 0.2175), (0.60, 0.2095), (0.70, 0.1980),
(0.80, 0.1830), (0.90, 0.1690), (0.95, 0.1645), (1.00, 0.1620),
(1.05, 0.1610), (1.10, 0.1605), (1.15, 0.1600), (1.20, 0.1595),
(1.30, 0.1590), (1.40, 0.1585), (1.50, 0.1580), (1.60, 0.1575),
(1.70, 0.1570), (1.80, 0.1565), (1.90, 0.1560), (2.00, 0.1555),
(2.20, 0.1550), (2.40, 0.1545), (2.60, 0.1540), (2.80, 0.1535),
(3.00, 0.1530), (3.20, 0.1525), (3.40, 0.1520), (3.60, 0.1515),
(3.80, 0.1510), (4.00, 0.1505), (4.20, 0.1500), (4.40, 0.1495),
(4.60, 0.1490), (4.80, 0.1485), (5.00, 0.1480)
]]
G8_DRAG_TABLE = [(x, y) for x, y in [
(0.00, 0.204), (0.10, 0.203), (0.20, 0.202), (0.30, 0.200), (0.40, 0.196),
(0.50, 0.190), (0.60, 0.181), (0.70, 0.169), (0.80, 0.155), (0.90, 0.141),
(1.00, 0.128), (1.10, 0.125), (1.20, 0.124), (1.30, 0.123), (1.40, 0.123),
(1.50, 0.123), (1.60, 0.123), (1.70, 0.123), (1.80, 0.123), (1.90, 0.123),
(2.00, 0.123), (2.20, 0.122), (2.40, 0.122), (2.60, 0.122), (2.80, 0.122),
(3.00, 0.121), (3.20, 0.121), (3.40, 0.121), (3.60, 0.120), (3.80, 0.120),
(4.00, 0.120), (4.20, 0.120), (4.40, 0.120), (4.60, 0.120), (4.80, 0.120),
(5.00, 0.120)
]]
DRAG_MODELS: Dict[str, List[Tuple[float, float]]] = {
'G1': G1_DRAG_TABLE, 'G2': G2_DRAG_TABLE, 'G3': G3_DRAG_TABLE, 'G4':
G4_DRAG_TABLE,
'G5': G5_DRAG_TABLE, 'G6': G6_DRAG_TABLE, 'G7': G7_DRAG_TABLE, 'G8':
G8_DRAG_TABLE
}

# Konstanta
SIMULATION_DT = 0.01
MAX_SIMULATIONS = 10000
EARTH_ANGULAR_VELOCITY = 7.2921159e-5
R_GAS = 8.314462618
MOLAR_MASS_AIR = 0.0289644
MOLAR_MASS_WATER = 0.01801528
GRAVITY = 9.80665
C_MAGNUS = 0.0000002
GAMMA = 1.4
R_SPECIFIC = 287.058
TOLERANCE = 1e-6 # Tolerance for the DP87 solver

# --- FUNGSI UTILITAS ---


def slow_print(text, speed=1/100):
for char in text:
[Link](char)
[Link]()
[Link](speed)
print()

def get_input_with_retry(prompt, type_func, min_val=None, max_val=None,


choices=None):
while True:
try:
user_input = input(prompt)
if choices:
if user_input.lower() not in choices:
print(f"Error: Pilihan tidak valid. Silakan coba lagi. Pilihan
yang tersedia: {', '.join(choices)}")
continue
return user_input.lower()

value = type_func(user_input)
if min_val is not None and value < min_val:
print(f"Error: Nilai harus lebih besar dari {min_val}. Silakan coba
lagi.")
continue
if max_val is not None and value > max_val:
print(f"Error: Nilai harus lebih kecil dari {max_val}. Silakan coba
lagi.")
continue
return value
except ValueError:
print("Error: Input tidak valid. Silakan masukkan angka.")

def calculate_reticle_adjustments(value, target_distance):


"""Menghitung mil dan turret clicks dari nilai drop/drift."""
mil = (abs(value) / target_distance) * 1000
turret_clicks = mil * 10
return mil, turret_clicks

# --- MODEL ATMOSFER DAN DRAG ---


def get_drag_coefficient(velocity, temperature_celsius, drag_model='G7'):
"""Interpolates drag coefficient using standard floats."""
temp_kelvin = temperature_celsius + 273.15
speed_of_sound = [Link](GAMMA * R_SPECIFIC * temp_kelvin)
mach = velocity / speed_of_sound

drag_table = DRAG_MODELS.get(drag_model.upper(), G7_DRAG_TABLE)

if mach <= drag_table[0][0]:


return drag_table[0][1]
if mach >= drag_table[-1][0]:
return drag_table[-1][1]

for i in range(len(drag_table) - 1):


if drag_table[i][0] <= mach < drag_table[i+1][0]:
x1, y1 = drag_table[i]
x2, y2 = drag_table[i+1]
if x2 - x1 == 0:
return y1
return y1 + (y2 - y1) * (mach - x1) / (x2 - x1)
return 0.2

def smart_drag_model_selection(velocity: float, temperature_celsius: float) -> str:


"""Memilih model drag terbaik berdasarkan Mach number."""
temp_kelvin = temperature_celsius + 273.15
speed_of_sound = [Link](GAMMA * R_SPECIFIC * temp_kelvin)
mach = velocity / speed_of_sound

if mach < 1.0:


return 'G1'
elif 1.0 <= mach < 1.5:
return 'G5'
elif 1.5 <= mach < 2.0:
return 'G2'
elif 2.0 <= mach < 2.5:
return 'G3'
elif 2.5 <= mach < 3.0:
return 'G4'
elif 3.0 <= mach < 3.5:
return 'G6'
else:
return 'G7'

def calculate_air_density(temperature_celsius: float, pressure_hPa: float,


humidity_percent: float):
"""
Calculates air density using standard floats.
"""
T_kelvin = temperature_celsius + 273.15
P_pascal = pressure_hPa * 100
P_saturation = 6.1078 * 10**((7.5 * temperature_celsius) / (237.3 +
temperature_celsius)) * 100
P_vapor = (humidity_percent / 100) * P_saturation
P_dry_air = P_pascal - P_vapor
rho_dry_air = (P_dry_air * MOLAR_MASS_AIR) / (R_GAS * T_kelvin)
rho_vapor = (P_vapor * MOLAR_MASS_WATER) / (R_GAS * T_kelvin)
return rho_dry_air + rho_vapor

def get_wind_vector(altitude_m, wind_data):


"""Interpolates wind speed and angle from multi-layer data."""
if not wind_data or len(wind_data) == 1:
single_wind = wind_data[0]
return float(single_wind['speed']), float(single_wind['angle'])

for i in range(len(wind_data) - 1):


h1 = float(wind_data[i]['altitude'])
h2 = float(wind_data[i+1]['altitude'])
if h1 <= altitude_m < h2:
s1 = float(wind_data[i]['speed'])
s2 = float(wind_data[i+1]['speed'])
a1 = float(wind_data[i]['angle'])
a2 = float(wind_data[i+1]['angle'])

interp_factor = (altitude_m - h1) / (h2 - h1)

interpolated_speed = s1 + (s2 - s1) * interp_factor


interpolated_angle = a1 + (a2 - a1) * interp_factor

return interpolated_speed, interpolated_angle

return float(wind_data[-1]['speed']), float(wind_data[-1]['angle'])

# --- TRAJECTORY SOLVER FUNCTIONS ---


def get_acceleration_6dof(state, t, mass, cross_sectional_area,
temperature_celsius,
pressure_hPa, humidity_percent, wind_vector_x,
wind_vector_y, wind_vector_z,
spin_rate_rad_s, bullet_diameter_m, shooter_latitude_rad,
drag_model,
shooter_direction_rad):
"""Calculates bullet acceleration at each step using standard floats."""
x, y, z, vx, vy, vz = state

current_pressure_hPa = pressure_hPa * (1 - (y * 0.0065 / (temperature_celsius +


273.15)))**5.256
current_humidity_percent = humidity_percent * (1 - y/10000)
current_rho = calculate_air_density(temperature_celsius, current_pressure_hPa,
current_humidity_percent)

v_rel_x = vx - wind_vector_x
v_rel_y = vy - wind_vector_y
v_rel_z = vz - wind_vector_z

# Perbaikan: Tambahkan validasi untuk mencegah nilai negatif akibat presisi


floating-point
squared_total = v_rel_x**2 + v_rel_y**2 + v_rel_z**2
if squared_total < 0:
squared_total = 0.0
v_rel_total = [Link](squared_total)

vx_earth, vy_earth, vz_earth = vx, vy, vz

omega_x = 0.0
omega_y = EARTH_ANGULAR_VELOCITY * [Link](shooter_latitude_rad)
omega_z = EARTH_ANGULAR_VELOCITY * [Link](shooter_latitude_rad)

# Drag Force
drag_coefficient = get_drag_coefficient(v_rel_total, temperature_celsius,
drag_model)
ax_drag, ay_drag, az_drag = 0.0, 0.0, 0.0
if v_rel_total > 0:
drag_force = 0.5 * current_rho * drag_coefficient * cross_sectional_area *
v_rel_total**2
ax_drag = -drag_force * v_rel_x / (mass * v_rel_total)
ay_drag = -drag_force * v_rel_y / (mass * v_rel_total)
az_drag = -drag_force * v_rel_z / (mass * v_rel_total)

# Gravitational Force
ay_gravity = -GRAVITY

# Coriolis Force
ax_coriolis = -2 * (omega_y * vz_earth - omega_z * vy_earth)
ay_coriolis = -2 * (omega_z * vx_earth - omega_x * vz_earth)
az_coriolis = -2 * (omega_x * vy_earth - omega_y * vx_earth)

# Magnus Force
ax_magnus, ay_magnus, az_magnus = 0.0, 0.0, 0.0
if spin_rate_rad_s > 0 and v_rel_total > 0:
spin_vector = [spin_rate_rad_s, 0.0, 0.0]

magnus_x = spin_vector[1] * v_rel_z - spin_vector[2] * v_rel_y


magnus_y = spin_vector[2] * v_rel_x - spin_vector[0] * v_rel_z
magnus_z = spin_vector[0] * v_rel_y - spin_vector[1] * v_rel_x

magnus_factor = C_MAGNUS * current_rho * (bullet_diameter_m**3)


ax_magnus = magnus_factor * magnus_x / mass
ay_magnus = magnus_factor * magnus_y / mass
az_magnus = magnus_factor * magnus_z / mass

# Eotvos Force
eotvos_accel = 0.0
if shooter_direction_rad >= 0 and shooter_direction_rad <= [Link]:
eotvos_accel = 2 * vx_earth * EARTH_ANGULAR_VELOCITY *
[Link](shooter_latitude_rad)
else:
eotvos_accel = -2 * vx_earth * EARTH_ANGULAR_VELOCITY *
[Link](shooter_latitude_rad)

ax = ax_drag + ax_coriolis + ax_magnus


ay = ay_drag + ay_gravity + ay_coriolis + ay_magnus + eotvos_accel
az = az_drag + az_coriolis + az_magnus

return [ax, ay, az]

def solve_trajectory_dp87(params_tuple):
"""
Performs one run of the Dormand-Prince (DP87) integrator.
"""
try:
(angle, mass, velocity, target_distance, wind_data, shooter_direction,
bullet_diameter_mm,
temperature_celsius, pressure_hPa, humidity_percent, spin_rate_rpm,
shooter_latitude_deg, drag_model) = params_tuple

bullet_diameter_m = bullet_diameter_mm / 1000.0


cross_sectional_area = [Link] * (bullet_diameter_m / 2.0)**2
shooter_direction_rad = [Link](shooter_direction)
shooter_latitude_rad = [Link](shooter_latitude_deg)
spin_rate_rad_s = spin_rate_rpm * (2 * [Link] / 60.0)

# DP87 Coefficients
c = [0.0, 1/5, 3/10, 4/5, 8/9, 1.0, 1.0]
a = [[0.0], [1/5], [3/40, 9/40], [44/45, -56/15, 32/9],
[19372/6561, -25360/2187, 64448/6561, -212/729],
[9017/3168, -355/33, 46732/5247, 49/176, -5103/18656],
[35/384, 0.0, 500/1113, 125/192, -2187/6784, 11/84]]
b8 = [35/384, 0.0, 500/1113, 125/192, -2187/6784, 11/84, 0.0]
b7 = [5179/57600, 0.0, 7571/16695, 393/640, -92097/339200, 187/2100, 1/40]

t = 0.0
dt = SIMULATION_DT
x, y, z = 0.0, 0.0, 0.0
vx = velocity * [Link](angle)
vy = velocity * [Link](angle)
vz = 0.0

state = [x, y, z, vx, vy, vz]

wind_speed, wind_angle = get_wind_vector(y, wind_data)


relative_wind_angle = wind_angle - [Link](shooter_direction_rad)
relative_wind_angle_rad = [Link](relative_wind_angle)
wind_vector_x = -wind_speed * [Link](relative_wind_angle_rad)
wind_vector_z = -wind_speed * [Link](relative_wind_angle_rad)
wind_vector_y = 0.0

while y >= -1.0 and x < target_distance * 1.5:

k = []

# Stage 1
k1 = get_acceleration_6dof(state, t, mass, cross_sectional_area,
temperature_celsius, pressure_hPa, humidity_percent,
wind_vector_x, wind_vector_y, wind_vector_z,
spin_rate_rad_s, bullet_diameter_m, shooter_latitude_rad, drag_model,
shooter_direction_rad)
[Link](k1)

# Remaining Stages
for i in range(1, 7):
temp_state_pos = [0.0] * 3
temp_state_vel = [0.0] * 3

sum_a_k_pos = [0.0] * 3
sum_a_k_vel = [0.0] * 3

for l in range(len(a[i-1])):
for j in range(3):
sum_a_k_pos[j] += a[i-1][l] * k[l][j]

temp_state_pos = [state[j] + dt * state[j+3] for j in range(3)]


temp_state_vel = [state[j+3] + dt * sum_a_k_pos[j] for j in
range(3)]

temp_state = temp_state_pos + temp_state_vel

new_k = get_acceleration_6dof(temp_state, t + c[i] * dt, mass,


cross_sectional_area, temperature_celsius, pressure_hPa, humidity_percent,
wind_vector_x, wind_vector_y,
wind_vector_z, spin_rate_rad_s, bullet_diameter_m, shooter_latitude_rad,
drag_model, shooter_direction_rad)
[Link](new_k)

# Update state for order 8


next_state_8 = [0.0] * 6
next_state_7 = [0.0] * 6

for j in range(3):
sum_b8_k_pos = 0.0
sum_b7_k_pos = 0.0
for i in range(7):
sum_b8_k_pos += b8[i] * k[i][j]
sum_b7_k_pos += b7[i] * k[i][j]

next_state_8[j] = state[j] + dt * state[j+3]


next_state_7[j] = state[j] + dt * state[j+3]
next_state_8[j+3] = state[j+3] + dt * sum_b8_k_pos
next_state_7[j+3] = state[j+3] + dt * sum_b7_k_pos

error = [Link](sum([(next_state_8[i] - next_state_7[i])**2 for i in


range(6)]))

if error < TOLERANCE:


t += dt
state = next_state_8
x, y, z, vx, vy, vz = state

if error > 0:
dt_new = 0.9 * dt * (TOLERANCE / error)**(1/8.0)
dt = dt_new
else:
dt *= 1.1

final_velocity = [Link](vx**2 + vy**2 + vz**2)


final_ke = 0.5 * mass * final_velocity**2
final_momentum = mass * final_velocity

# Linear interpolation for target distance


last_x, last_y, last_z = x - vx * dt, y - vy * dt, z - vz * dt
if x != last_x:
interp_factor = (target_distance - last_x) / (x - last_x)
drop_at_target = last_y + (y - last_y) * interp_factor
drift_at_target = last_z + (z - last_z) * interp_factor
t_interp = t - dt + (dt * interp_factor)
return (-drop_at_target, drift_at_target, t_interp, final_ke,
final_momentum)
else:
return (-y, z, t, final_ke, final_momentum)
except Exception as e:
return None

def solve_trajectory_for_bisection(params_tuple):
"""Function optimized for bisection (returns horizontal distance)."""
(angle, mass, velocity, target_distance, wind_data, shooter_direction,
bullet_diameter_mm,
temperature_celsius, pressure_hPa, humidity_percent, spin_rate_rpm,
shooter_latitude_deg, drag_model) = params_tuple

bullet_diameter_m = bullet_diameter_mm / 1000.0


cross_sectional_area = [Link] * (bullet_diameter_m / 2.0)**2
shooter_direction_rad = [Link](shooter_direction)
shooter_latitude_rad = [Link](shooter_latitude_deg)
spin_rate_rad_s = spin_rate_rpm * (2 * [Link] / 60.0)

# DP87 Coefficients
c = [0.0, 1/5, 3/10, 4/5, 8/9, 1.0, 1.0]
a = [[0.0], [1/5], [3/40, 9/40], [44/45, -56/15, 32/9],
[19372/6561, -25360/2187, 64448/6561, -212/729],
[9017/3168, -355/33, 46732/5247, 49/176, -5103/18656],
[35/384, 0.0, 500/1113, 125/192, -2187/6784, 11/84]]
b8 = [35/384, 0.0, 500/1113, 125/192, -2187/6784, 11/84, 0.0]
b7 = [5179/57600, 0.0, 7571/16695, 393/640, -92097/339200, 187/2100, 1/40]

t = 0.0
dt = SIMULATION_DT
x, y, z = 0.0, 0.0, 0.0
vx = velocity * [Link](angle)
vy = velocity * [Link](angle)
vz = 0.0

state = [x, y, z, vx, vy, vz]

wind_speed, wind_angle = get_wind_vector(y, wind_data)


relative_wind_angle = wind_angle - [Link](shooter_direction_rad)
relative_wind_angle_rad = [Link](relative_wind_angle)
wind_vector_x = -wind_speed * [Link](relative_wind_angle_rad)
wind_vector_z = -wind_speed * [Link](relative_wind_angle_rad)
wind_vector_y = 0.0

while y >= -1.0 and x < target_distance * 1.5:


k = []
k1 = get_acceleration_6dof(state, t, mass, cross_sectional_area,
temperature_celsius, pressure_hPa, humidity_percent,
wind_vector_x, wind_vector_y, wind_vector_z,
spin_rate_rad_s, bullet_diameter_m, shooter_latitude_rad, drag_model,
shooter_direction_rad)
[Link](k1)

for i in range(1, 7):


temp_state_vel = [0.0] * 3

sum_a_k_vel = [0.0] * 3
for l in range(len(a[i-1])):
for j in range(3):
sum_a_k_vel[j] += a[i-1][l] * k[l][j]

temp_state_pos = [state[j] + dt * state[j+3] for j in range(3)]


temp_state_vel = [state[j+3] + dt * sum_a_k_vel[j] for j in range(3)]

temp_state = temp_state_pos + temp_state_vel

new_k = get_acceleration_6dof(temp_state, t + c[i] * dt, mass,


cross_sectional_area, temperature_celsius, pressure_hPa, humidity_percent,
wind_vector_x, wind_vector_y,
wind_vector_z, spin_rate_rad_s, bullet_diameter_m, shooter_latitude_rad,
drag_model, shooter_direction_rad)
[Link](new_k)

next_state_8 = [0.0] * 6
next_state_7 = [0.0] * 6

for j in range(3):
sum_b8_k_pos = 0.0
sum_b7_k_pos = 0.0
for i in range(7):
sum_b8_k_pos += b8[i] * k[i][j]
sum_b7_k_pos += b7[i] * k[i][j]

next_state_8[j] = state[j] + dt * state[j+3]


next_state_7[j] = state[j] + dt * state[j+3]
next_state_8[j+3] = state[j+3] + dt * sum_b8_k_pos
next_state_7[j+3] = state[j+3] + dt * sum_b7_k_pos

error = [Link](sum([(next_state_8[i] - next_state_7[i])**2 for i in


range(6)]))

if error < TOLERANCE:


t += dt
state = next_state_8
x, y, z, vx, vy, vz = state

if error > 0:
dt_new = 0.9 * dt * (TOLERANCE / error)**(1/8.0)
dt = dt_new
else:
dt *= 1.1

return x

def solve_single_mc_run(params_dict):
"""Wrapper function to run a single Monte Carlo simulation."""
return solve_trajectory_dp87((
params_dict['angle'], params_dict['mass'], params_dict['velocity'],
params_dict['target_distance'], params_dict['wind_data'],
params_dict['shooter_direction'], params_dict['bullet_diameter_mm'],
params_dict['temperature_celsius'], params_dict['pressure_hPa'],
params_dict['humidity_percent'], params_dict['spin_rate_rpm'],
params_dict['shooter_latitude_deg'], params_dict['drag_model']
))

def solve_trajectory_for_plot(params_tuple):
"""
Performs one run of the Dormand-Prince (DP87) integrator and returns a list
of (x, y, z) coordinates for plotting.
"""
try:
(angle, mass, velocity, target_distance, wind_data, shooter_direction,
bullet_diameter_mm,
temperature_celsius, pressure_hPa, humidity_percent, spin_rate_rpm,
shooter_latitude_deg, drag_model) = params_tuple

bullet_diameter_m = bullet_diameter_mm / 1000.0


cross_sectional_area = [Link] * (bullet_diameter_m / 2.0)**2
shooter_direction_rad = [Link](shooter_direction)
shooter_latitude_rad = [Link](shooter_latitude_deg)
spin_rate_rad_s = spin_rate_rpm * (2 * [Link] / 60.0)

c = [0.0, 1/5, 3/10, 4/5, 8/9, 1.0, 1.0]


a = [[0.0], [1/5], [3/40, 9/40], [44/45, -56/15, 32/9],
[19372/6561, -25360/2187, 64448/6561, -212/729],
[9017/3168, -355/33, 46732/5247, 49/176, -5103/18656],
[35/384, 0.0, 500/1113, 125/192, -2187/6784, 11/84]]
b8 = [35/384, 0.0, 500/1113, 125/192, -2187/6784, 11/84, 0.0]
b7 = [5179/57600, 0.0, 7571/16695, 393/640, -92097/339200, 187/2100, 1/40]

t = 0.0
dt = SIMULATION_DT
x, y, z = 0.0, 0.0, 0.0
vx = velocity * [Link](angle)
vy = velocity * [Link](angle)
vz = 0.0

state = [x, y, z, vx, vy, vz]


trajectory_points = [(x, y, z)] # Inisialisasi list untuk menyimpan titik

wind_speed, wind_angle = get_wind_vector(y, wind_data)


relative_wind_angle = wind_angle - [Link](shooter_direction_rad)
relative_wind_angle_rad = [Link](relative_wind_angle)
wind_vector_x = -wind_speed * [Link](relative_wind_angle_rad)
wind_vector_z = -wind_speed * [Link](relative_wind_angle_rad)
wind_vector_y = 0.0

while y >= -1.0 and x < target_distance * 1.5:


k = []
k1 = get_acceleration_6dof(state, t, mass, cross_sectional_area,
temperature_celsius, pressure_hPa, humidity_percent,
wind_vector_x, wind_vector_y, wind_vector_z,
spin_rate_rad_s, bullet_diameter_m, shooter_latitude_rad, drag_model,
shooter_direction_rad)
[Link](k1)

for i in range(1, 7):


temp_state_pos = [0.0] * 3
temp_state_vel = [0.0] * 3
sum_a_k_pos = [0.0] * 3
for l in range(len(a[i-1])):
for j in range(3):
sum_a_k_pos[j] += a[i-1][l] * k[l][j]

temp_state_pos = [state[j] + dt * state[j+3] for j in range(3)]


temp_state_vel = [state[j+3] + dt * sum_a_k_pos[j] for j in
range(3)]
temp_state = temp_state_pos + temp_state_vel

new_k = get_acceleration_6dof(temp_state, t + c[i] * dt, mass,


cross_sectional_area, temperature_celsius, pressure_hPa, humidity_percent,
wind_vector_x, wind_vector_y,
wind_vector_z, spin_rate_rad_s, bullet_diameter_m, shooter_latitude_rad,
drag_model, shooter_direction_rad)
[Link](new_k)

next_state_8 = [0.0] * 6
next_state_7 = [0.0] * 6
for j in range(3):
sum_b8_k_pos = 0.0
sum_b7_k_pos = 0.0
for i in range(7):
sum_b8_k_pos += b8[i] * k[i][j]
sum_b7_k_pos += b7[i] * k[i][j]

next_state_8[j] = state[j] + dt * state[j+3]


next_state_7[j] = state[j] + dt * state[j+3]
next_state_8[j+3] = state[j+3] + dt * sum_b8_k_pos
next_state_7[j+3] = state[j+3] + dt * sum_b7_k_pos

error = [Link](sum([(next_state_8[i] - next_state_7[i])**2 for i in


range(6)]))

if error < TOLERANCE:


t += dt
state = next_state_8
x, y, z, vx, vy, vz = state
trajectory_points.append((x, y, z)) # Simpan titik

if error > 0:
dt_new = 0.9 * dt * (TOLERANCE / error)**(1/8.0)
dt = dt_new
else:
dt *= 1.1
return trajectory_points
except Exception as e:
print(f"Error in trajectory plot solver: {e}")
return []

def ascii_plot(trajectory_points, target_distance, width=80, height=20):


"""
Creates an ASCII plot of the bullet's trajectory.
"""
if not trajectory_points:
return ""

# Scale data to fit plot dimensions


max_x = max(p[0] for p in trajectory_points)
min_y = min(p[1] for p in trajectory_points)
max_y = max(p[1] for p in trajectory_points)

x_scale = width / max_x


y_scale = height / (max_y - min_y)

grid = [[' ' for _ in range(width)] for _ in range(height)]

# Draw trajectory points


for x_val, y_val, z_val in trajectory_points:
grid_x = int(x_val * x_scale)
grid_y = int((max_y - y_val) * y_scale)

if 0 <= grid_x < width and 0 <= grid_y < height:


grid[grid_y][grid_x] = '#'

# Draw axes
# Vertical Axis (Y)
for i in range(height):
grid[i][0] = '|'

# Horizontal Axis (X)


grid[int((max_y - 0) * y_scale)][:] = ['_'] * width

# Draw plot string


plot_str = "\n".join("".join(row) for row in grid)

# Add labels
plot_str += f"\n0m{' ' * (width - 6)}{target_distance:.0f}m\n"
plot_str += f"Target at approx. X={int(target_distance * x_scale)}
Y={int((max_y - 0) * y_scale)}\n"

return plot_str

def plot_trajectory_matplotlib(trajectory_points, target_distance,


filename="[Link]"):
"""
Creates and saves a Matplotlib plot of the bullet's trajectory, without Z-axis
drift.
"""
if not trajectory_points:
print("Tidak ada data lintasan untuk diplot.")
return

x_values = [p[0] for p in trajectory_points]


y_values = [p[1] for p in trajectory_points]

fig, ax1 = [Link](1, 1, figsize=(10, 6))

# Plot Lintasan Vertikal (X vs Y)


[Link](x_values, y_values, label="Lintasan Peluru", color='blue')
[Link](x=target_distance, color='red', linestyle='--', label='Jarak
Target')
[Link](y=0, color='black', linestyle='-')
ax1.set_title("Visualisasi Lintasan Peluru (Ketinggian vs Jarak)")
ax1.set_xlabel("Jarak (meter)")
ax1.set_ylabel("Ketinggian (meter)")
[Link](True)
[Link]()

plt.tight_layout()
[Link](block=False)

save_choice = get_input_with_retry("\nApakah gambar mau disimpan? (y/t): ",


str, choices=['y', 't'])
if save_choice == 'y':
save_name = input("Gambar akan dinamai?: ")
if not save_name:
save_name = filename
elif not save_name.endswith('.jpg'):
save_name += '.jpg'

save_path = "/storage/emulated/0/stcof/"
[Link](save_path, exist_ok=True)
full_path = [Link](save_path, save_name)
[Link](full_path)
print(f"\nPlot lintasan berhasil disimpan sebagai {full_path}")
[Link](fig)

# --- UNIT TEST ---


class TrajectoryTests([Link]):
def setUp(self):
[Link] = 5.20
[Link] = 1680

def test_ke(self):
"""Test kinetic energy using standard floats."""
ke = 0.5 * [Link] * [Link]**2
[Link](ke, 7338240, places=0)

def test_momentum(self):
"""Test momentum using standard floats."""
momentum = [Link] * [Link]
[Link](momentum, 8736, places=0)

# --- MAIN LOOP ---


if __name__ == "__main__":
# Konfigurasi Logging
[Link](
level=[Link],
format='%(asctime)s - %(levelname)s - %(message)s',
datefmt='%H:%M:%S'
)

[Link](argv=['first-arg-is-ignored'], exit=False)

slow_print("Selamat Datang Di STC (Smart Trajectory Calculator)")


slow_print("--------------------------------------------------")

while True:
try:
mass = float(get_input_with_retry("Masukkan massa peluru (kg): ",
float, 0.005, 100))
target_distance = float(get_input_with_retry("Masukkan jarak target
(meter): ", float, 1, 50000))
velocity = float(get_input_with_retry("Masukkan kecepatan peluru (m/s):
", float, 100, 3000))
bullet_diameter_mm = float(get_input_with_retry("Masukkan diameter
peluru (mm): ", float, 1, 500))

spin_type = get_input_with_retry("SmoothBore atau rifled: s/r ", str,


choices=['s', 'r'])

spin_rate_rpm = 0.0
if spin_type == 'r':
while True:
spin_input = input("Masukkan twist Laras (cth: 1/15) atau RPM
(cth: 300000): ")
if '/' in spin_input:
try:
parts = spin_input.split('/')
if len(parts) == 2:
twist_rate_in_inches = float(parts[1])
if twist_rate_in_inches > 0:
spin_rate_rpm = (velocity * 720) /
twist_rate_in_inches
print(f"RPM: {spin_rate_rpm:.1f}")
break
else:
print("Nilai twist laras tidak valid.")
else:
print("Format twist laras tidak valid. Gunakan
format '1/x'.")
except (ValueError, IndexError):
print("Format twist laras tidak valid. Gunakan format
'1/x'.")
else:
try:
spin_rate_rpm = float(spin_input)
if spin_rate_rpm > 0:
twist_rate_in_inches = (velocity * 720) /
spin_rate_rpm
print(f"Twist Laras: 1/{twist_rate_in_inches:.1f}")
break
else:
print("Nilai RPM harus lebih besar dari 0.")
except ValueError:
print("Input tidak valid. Silakan coba lagi.")
else: # spin_type == 's'
spin_rate_rpm = 0.0
slow_print("Laras halus (Smoothbore) dipilih. Mengatur RPM menjadi
0.")

gas_choice = get_input_with_retry("Apakah Anda ingin menyertakan efek


gas? (y/t): ", str, choices=['y', 't'])
gas_boost = 0.0
if gas_choice == 'y':
gas_type = get_input_with_retry("Masukkan jenis gas (CO/N2O/Powder
Gas): ", str, choices=['co', 'n2o', 'powder gas'])
gas_boost = 0.001 * velocity * 0.005
slow_print(f"Menerapkan dorongan gas **{gas_boost:.3f} m/s** pada
kecepatan awal.")
velocity += gas_boost

temperature_celsius = float(get_input_with_retry("Masukkan suhu udara


(Celcius): ", float))
pressure_hPa = float(get_input_with_retry("Masukkan tekanan udara (hPa,
biasanya sekitar 1013): ", float, 300, 1100))
humidity_percent = float(get_input_with_retry("Masukkan kelembaban
relatif (%RH, 0-100): ", float, 0, 100))
shooter_direction = float(get_input_with_retry("Masukkan arah tembakan
(derajat Kompas, 0=Utara): ", float, 0, 360))
shooter_latitude_deg = float(get_input_with_retry("Masukkan lintang
penembak (derajat, positif untuk utara): ", float, -90, 90))

drag_model = smart_drag_model_selection(velocity, temperature_celsius)


slow_print(f"Sistem memilih drag model **'{drag_model}'** secara
otomatis berdasarkan kecepatan dan kaliber.")
slow_print(f"Menggunakan solver **Dormand-Prince (DP87)** dengan
presisi standar Python.")

wind_data = []
multilayer_choice = get_input_with_retry("Apakah Anda ingin menggunakan
multi-layer wind? (y/t): ", str, choices=['y', 't'])
if multilayer_choice == 'y':
num_layers = get_input_with_retry("Berapa lapisan angin yang akan
dimasukkan?: ", int, 1, 10)
for i in range(num_layers):
altitude = get_input_with_retry(f"Masukkan ketinggian lapisan
{i+1} (meter): ", float, 0, None)
speed_kmh = get_input_with_retry(f"Masukkan kecepatan angin
lapisan {i+1} (km/h): ", float, 0, 3000)
angle = get_input_with_retry(f"Masukkan arah angin lapisan
{i+1} (derajat Kompas): ", float, 0, 360)
wind_data.append({'altitude': altitude, 'speed': speed_kmh /
3.6, 'angle': angle})
wind_data.sort(key=lambda x: x['altitude'])
else:
wind_speed_kmh = float(get_input_with_retry("Masukkan kecepatan
angin tunggal (km/h): ", float, 0, 3000))
wind_angle = float(get_input_with_retry("Masukkan arah angin
tunggal derajat Kompas: ", float, 0, 360))
wind_data.append({'altitude': 0.0, 'speed': wind_speed_kmh / 3.6,
'angle': wind_angle})

num_simulations = get_input_with_retry("Masukkan jumlah perhitungan


Monte-Carlo (mis. 1000 atau 5000): ", int, 1, 10000)

# Bisection with standard float


low_angle = 0.0
high_angle = [Link] / 2.0
for _ in range(20):
mid_angle = (low_angle + high_angle) / 2.0
params_to_test = (mid_angle, mass, velocity, target_distance,
wind_data, shooter_direction, bullet_diameter_mm,
temperature_celsius, pressure_hPa,
humidity_percent, spin_rate_rpm, shooter_latitude_deg, drag_model)
final_range = solve_trajectory_for_bisection(params_to_test)
if final_range > target_distance:
high_angle = mid_angle
else:
low_angle = mid_angle
best_angle = (low_angle + high_angle) / 2.0

# Generate trajectory data for plotting (using the optimal angle)


plot_params = (best_angle, mass, velocity, target_distance, wind_data,
shooter_direction, bullet_diameter_mm,
temperature_celsius, pressure_hPa,
humidity_percent, spin_rate_rpm, shooter_latitude_deg, drag_model)
trajectory_data = solve_trajectory_for_plot(plot_params)

# --- Monte Carlo Simulation with Exponential Progress Display ---


slow_print("\n--- Menjalankan Simulasi Monte Carlo ---")
print(f"Menggunakan {cpu_count()} proses untuk mempercepat simulasi.")
print("Sedang Melakukan Proses Perhitungan, Mohon Tunggu sebentar.")
print("\n==Progress Perhitungan==")

all_results = []
total_runs_completed = 0

# Tentukan ukuran batch awal (misal: 8) dan gandakan


batch_size = 8

with ProcessPoolExecutor(max_workers=cpu_count()) as executor:


start_time_all = [Link]()
while total_runs_completed < num_simulations:

runs_in_this_batch = min(batch_size, num_simulations -


total_runs_completed)

if runs_in_this_batch <= 0:
break

batch_params = []
for i in range(runs_in_this_batch):
velocity_factor = [Link](0.995, 1.005)
wind_speed_variation = [Link](-0.5, 0.5)
wind_angle_variation = [Link](-5.0, 5.0)

stochastic_params = {
'angle': best_angle,
'mass': mass,
'velocity': velocity * velocity_factor,
'target_distance': target_distance,
'wind_data': [],
'shooter_direction': shooter_direction,
'bullet_diameter_mm': bullet_diameter_mm,
'temperature_celsius': temperature_celsius,
'pressure_hPa': pressure_hPa,
'humidity_percent': humidity_percent,
'spin_rate_rpm': spin_rate_rpm,
'shooter_latitude_deg': shooter_latitude_deg,
'drag_model': drag_model
}

if wind_data:
original_wind = wind_data[0]
stochastic_wind_speed = original_wind['speed'] +
wind_speed_variation
stochastic_wind_angle = original_wind['angle'] +
wind_angle_variation
stochastic_params['wind_data'].append({'altitude':
original_wind['altitude'], 'speed': stochastic_wind_speed, 'angle':
stochastic_wind_angle})

batch_params.append(stochastic_params)

start_time_batch = [Link]()
batch_results = list([Link](solve_single_mc_run,
batch_params))
end_time_batch = [Link]()
execution_time_batch = end_time_batch - start_time_batch

all_results.extend(batch_results)
total_runs_completed += runs_in_this_batch

print(f"{total_runs_completed} run =
{execution_time_batch:.2f}s")

# Gandakan ukuran batch untuk laporan berikutnya


batch_size *= 2

end_time_all = [Link]()
total_execution_time = end_time_all - start_time_all

valid_results = [r for r in all_results if r is not None]

if not valid_results:
slow_print("\nSemua simulasi gagal. Mohon periksa kembali input dan
log error.")
continue

all_drops = [r[0] for r in valid_results]


all_drifts = [r[1] for r in valid_results]
all_t_final = [r[2] for r in valid_results]
all_final_ke = [r[3] for r in valid_results]
all_final_momentum = [r[4] for r in valid_results]

avg_drop = [Link](all_drops)
avg_drift = [Link](all_drifts)
avg_t_final = [Link](all_t_final)
avg_final_ke = [Link](all_final_ke)
avg_final_momentum = [Link](all_final_momentum)

std_dev_drop = [Link](all_drops) if len(all_drops) > 1 else 0


std_dev_drift = [Link](all_drifts) if len(all_drifts) > 1
else 0

print(f"\n\nWaktu eksekusi total: **{total_execution_time:.2f}


detik**")
print(f"Validasi hasil berhasil. ✅")
print(f"Drag Model Digunakan: **{drag_model}**")
print(f"Metode Solver: **Dormand-Prince (DP87)** dengan **Monte
Carlo**.")
print(f"Sudut Elevasi yang Dibutuhkan: {[Link](best_angle):.2f}
derajat")
print(f"Waktu Tempuh (TOF) Rata-rata: {avg_t_final:.2f} detik")
print(f"Sisa Energi Kinetik Rata-rata: {avg_final_ke:.2f} Joule")
print(f"Sisa Momentum Rata-rata: {avg_final_momentum:.2f} kg·m/s")

print("\n--- Penyesuaian Reticle Turret ---")


drop_mil, drop_clicks = calculate_reticle_adjustments(avg_drop,
target_distance)
print("Vertikal (Drop):")
print(f" Total Drop Rata-rata: {avg_drop:.2f} meter (Standar
Deviasi: {std_dev_drop:.2f} m)")
print(f" Mil: {drop_mil:.2f} mil")
print(f" Turret Clicks (0.1 mil/klik): {drop_clicks:.2f} klik")

drift_mil, drift_clicks = calculate_reticle_adjustments(avg_drift,


target_distance)
print("Horizontal (Drift):")
print(f" Total Drift Rata-rata: {avg_drift:.2f} meter (Standar
Deviasi: {std_dev_drift:.2f} m)")
print(f" Mil: {drift_mil:.2f} mil")
print(f" Turret Clicks (0.1 mil/klik): {drift_clicks:.2f} klik")

print("\n--- Panduan Reticle Taktis (Holdover) ---")


if avg_drop >= 0:
print(f"Elevasi: Bidik {drop_mil:.0f} titik di atas center
crosshair.")
else:
print(f"Elevasi: Bidik {abs(drop_mil):.0f} titik di bawah center
crosshair.")

if avg_drift > 0:
print(f"Windage: Bidik {abs(drift_mil):.0f} titik ke kiri dari
center crosshair.")
else:
print(f"Windage: Bidik {abs(drift_mil):.0f} titik ke kanan dari
center crosshair.")

# Plot Matplotlib dan simpan sebagai JPG


if trajectory_data:
plot_trajectory_matplotlib(trajectory_data, target_distance)

except Exception as e:
slow_print("\nTerjadi error saat menjalankan program. Detail error:")
slow_print(f"Error spesifik: {e}")
traceback.print_exc()

if get_input_with_retry("\nApakah Anda ingin menjalankan simulasi lagi?


(y/t): ", str, choices=['y', 't']) != 'y':
break

[Link]

import sys
from typing import Dict, Tuple, List
from collections import Counter

# --- KONFIGURASI DAN DATA (V21.0: DATA OPERATOR BARU) ---


class Colors:
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED = '\033[91m'
BLUE = '\033[94m'
MAGENTA = '\033[95m'
CYAN = '\033[96m'
ENDC = '\033[0m'
HEADER_SEP = '=' * 60
SECTION_SEP = '-' * 60

# Data Operator (Diperbarui V21.0 - Total 44 Prefix)


OPERATOR_CODES: Dict[str, str] = {
# 1. TELKOMSEL (Simpati, Kartu As, Loop, Kartu Halo, by.U)
"811": "Telkomsel (KartuHalo/Simpati)",
"812": "Telkomsel (KartuHalo/Simpati)",
"813": "Telkomsel (KartuHalo/Simpati)",
"821": "Telkomsel (Simpati)",
"822": "Telkomsel (Simpati/Loop)",
"823": "Telkomsel (Kartu As)",
"851": "Telkomsel (Kartu As/by.U)",
"852": "Telkomsel (Kartu As)",
"853": "Telkomsel (Kartu As)",

# 2. INDOSAT OOREDOO HUTCHISON (IM3, Mentari, Matrix)


"814": "Indosat (M2 Broadband)", # Note: M2 Broadband status is legacy
"815": "Indosat (Matrix/Mentari)",
"816": "Indosat (Matrix/Mentari)",
"855": "Indosat (Matrix)",
"856": "Indosat (IM3)",
"857": "Indosat (IM3)",
"858": "Indosat (Mentari)",

# 3. XL AXIATA (XL Prabayar & Prioritas)


"817": "XL Axiata (Prabayar/Prioritas)",
"818": "XL Axiata (Prabayar/Prioritas)",
"819": "XL Axiata (Prabayar/Prioritas)",
"859": "XL Axiata (Prabayar/Prioritas)",
"877": "XL Axiata (Prabayar/Prioritas)",
"878": "XL Axiata (Prabayar/Prioritas)",

# 4. AXIS (Bagian dari XL Axiata)


"831": "AXIS (XL Axiata Group)",
"832": "AXIS (XL Axiata Group)",
"833": "AXIS (XL Axiata Group)",
"838": "AXIS (XL Axiata Group)",

# 5. TRI (3) (Bagian dari Indosat Ooredoo Hutchison)


"895": "Tri (Indosat Ooredoo Hutchison Group)",
"896": "Tri (Indosat Ooredoo Hutchison Group)",
"897": "Tri (Indosat Ooredoo Hutchison Group)",
"898": "Tri (Indosat Ooredoo Hutchison Group)",
"899": "Tri (Indosat Ooredoo Hutchison Group)",

# 6. SMARTFREN
"881": "Smartfren",
"882": "Smartfren",
"883": "Smartfren",
"884": "Smartfren",
"885": "Smartfren",
"886": "Smartfren",
"887": "Smartfren",
"888": "Smartfren",
"889": "Smartfren",
}

PSTN_CODES: Dict[str, str] = {


"21": "Fixed Line (Jakarta)", "22": "Fixed Line (Bandung)", "31": "Fixed Line
(Surabaya)",
"61": "Fixed Line (Medan)", "361": "Fixed Line (Bali)",
}

# DATA ADMINISTRATIF JATIM (TERMASUK KECAMATAN) - TIDAK BERUBAH


DATA_JAWA_TIMUR: Dict[str, Dict] = {
"01": {"nama": "KABUPATEN PACITAN", "kecamatan": {"01": "Donorojo", "02":
"Punung", "03": "Pringkuku", "04": "Pacitan", "05": "Kebonagung", "06": "Arjosari",
"07": "Nawangan", "08": "Bandar", "09": "Tegalombo", "10": "Tulakan", "11":
"Ngadirojo", "12": "Sudimoro"}},
"02": {"nama": "KABUPATEN PONOROGO", "kecamatan": {"01": "Ngrayun", "02":
"Slahung", "03": "Sambit", "04": "Sawoo", "05": "Mlarak", "06": "Jetis", "07":
"Siman", "08": "Balong", "09": "Kauman", "10": "Badegan", "11": "Sampung", "12":
"Sukorejo", "13": "Bungkal", "14": "Jambon", "15": "Pudak", "16": "Pulung", "17":
"Sooko", "18": "Ponorogo", "19": "Babadan", "20": "Jenangan", "21": "Maesan"}},
"03": {"nama": "KABUPATEN TRENGGALEK", "kecamatan": {"01": "Panggul", "02":
"Dongko", "03": "Pule", "04": "Karangan", "05": "Suruh", "06": "Kecamatan
Trenggalek", "07": "Bendungan", "08": "Tugu", "09": "Gandusari", "10": "Durenan",
"11": "Watulimo", "12": "Kampak", "13": "Munjungan", "14": "Pogalan"}},
"04": {"nama": "KABUPATEN TULUNGAGUNG", "kecamatan": {"01": "Tulungagung",
"02": "Kedungwaru", "03": "Ngantru", "04": "Karangrejo", "05": "Kauman", "06":
"Campurdarat", "07": "Boyolangu", "08": "Sumbergempol", "09": "Kalidawir", "10":
"Pucanglaban", "11": "Rejotangan", "12": "Ngunut", "13": "Srengat", "14":
"Pagersari", "15": "Gondang", "16": "Bandung", "17": "Pakel", "18": "Besuki", "19":
"Tanggunggunung"}},
"05": {"nama": "KABUPATEN BLITAR", "kecamatan": {"01": "Wonotirto", "02":
"Bakung", "03": "Panggungrejo", "04": "Wates", "05": "Binangun", "06":
"Rejotangan", "07": "Doko", "08": "Kesamben", "09": "Selorejo", "10": "Gandusari",
"11": "Wlingi", "12": "Talun", "13": "Garum", "14": "Nglegok", "15": "Sanankulon",
"16": "Kademangan", "17": "Kanigoro", "18": "Ponggok", "19": "Udanawu", "20":
"Srengat", "21": "Wonodadi", "22": "Selopuro"}},
"06": {"nama": "KABUPATEN KEDIRI", "kecamatan": {"01": "Mojo", "02":
"Banyakan", "03": "Grogol", "04": "Tarokan", "05": "Ngadiluwih", "06": "Kras",
"07": "Ringinrejo", "08": "Kandat", "09": "Wates", "10": "Ngancar", "11":
"Plosoklaten", "12": "Gurah", "13": "Pagu", "14": "Kayen Kidul", "15": "Plemahan",
"16": "Purwoasri", "17": "Papar", "18": "Kunjang", "19": "Pare", "20": "Badas",
"21": "Kandangan", "22": "Kepung", "23": "Puncu", "24": "Semen", "25":
"Gampengrejo", "26": "Ngasem"}},
"07": {"nama": "KABUPATEN MALANG", "kecamatan": {"01": "Donomulyo", "02":
"Pagak", "03": "Bantur", "04": "Gedangan", "05": "Sumbermanjing Wetan", "06":
"Tirtoyudo", "07": "Ampelgading", "08": "Dampit", "09": "Turen", "10":
"Sumberpucung", "11": "Kromengan", "12": "Ngajum", "13": "Wajak", "14":
"Bululawang", "15": "Gondanglegi", "16": "Kepanjan", "17": "Pagelaran", "18":
"Pujon", "19": "Ngantang", "20": "Kasembon", "21": "Karangploso", "22": "Lawang",
"23": "Singosari", "24": "Jabung", "25": "Pakis", "26": "Tumpang", "27":
"Poncokusumo", "28": "Tajinan", "29": "Wonosari", "30": "Wagird", "31": "Pakisaji",
"32": "Dau", "33": "Junrejo"}},
"08": {"nama": "KABUPATEN LUMAJANG", "kecamatan": {"01": "Tempeh", "02":
"Kunir", "03": "Yosowilangun", "04": "Rowokangkung", "05": "Tampin", "06":
"Pasirian", "07": "Candipuro", "08": "Pronojiwo", "09": "Tempursari", "10":
"Senduro", "11": "Gucialit", "12": "Padang", "13": "Pasrujambe", "14": "Klakah",
"15": "Ranuyoso", "16": "Jatiroto", "17": "Sukodono", "18": "Tekung", "19":
"Lumajang", "20": "Kedungjajang", "21": "Sumbersuko"}},
"09": {"nama": "KABUPATEN JEMBER", "kecamatan": {"01": "Jombang", "02":
"Kencong", "03": "Gumukmas", "04": "Puger", "05": "Wuluhan", "06": "Ambulu", "07":
"Tempurejo", "08": "Mayang", "09": "Mumbulsari", "10": "Rambipuji", "11": "Panti",
"12": "Sukorambi", "13": "Arjasa", "14": "Patrang", "15": "Kaliwates", "16":
"Sumbersari", "17": "Ajung", "18": "Jenggawah", "19": "Bangsalsari", "20":
"Tanggul", "21": "Semboro", "22": "Sumberbaru", "23": "Ledokombo", "24": "Silo",
"25": "Jelbuk", "26": "Pakusari", "27": "Kalisat", "28": "Balung", "29":
"Umbulsari", "30": "Curi"}},
"10": {"nama": "KABUPATEN BANYUWANGI", "kecamatan": {"01": "Pesanggaran", "02":
"Siliragung", "03": "Tegaldlimo", "04": "Purwoharjo", "05": "Cluring", "06":
"Gambiran", "07": "Tegalsari", "08": "Bangorejo", "09": "Kalibaru", "10":
"Genteng", "11": "Glenmore", "12": "Srono", "13": "Sempu", "14": "Singojuruh",
"15": "Rogojampi", "16": "Kabat", "17": "Glagah", "18": "Banyuwangi", "19": "Giri",
"20": "Wongsorejo", "21": "Kalipuro", "22": "Licin", "23": "Songgon", "24":
"Blimbingsari"}},
"11": {"nama": "KABUPATEN BONDOWOSO", "kecamatan": {"01": "Wonosari", "02":
"Tapen", "03": "Wringin", "04": "Tegalampel", "05": "Curahdami", "06": "Bondowoso",
"07": "Tenggarang", "08": "Tamanan", "09": "Pujer", "10": "Maesan", "11":
"Grujugan", "12": "Jambesari Darus Sholah", "13": "Pakes", "14": "Sukosari", "15":
"Cermee", "16": "Botolinggo", "17": "Sempol", "18": "Prajekan", "19": "Klabang",
"20": "Tlogosari", "21": "Arjasa", "22": "Jelbuk", "23": "Sumberwringin"}},
"12": {"nama": "KABUPATEN SITUBONDO", "kecamatan": {"01": "Besuki", "02":
"Jatibanteng", "03": "Suboh", "04": "Mlandingan", "05": "Banyuglugur", "06":
"Bungatan", "07": "Kendit", "08": "Panarukan", "09": "Sumbermalang", "10":
"Mangaran", "11": "Situbondo", "12": "Panji", "13": "Kapongan", "14": "Arjasa",
"15": "Jangkar", "16": "Asembagus", "17": "Banyuputih"}},
"13": {"nama": "KABUPATEN PROBOLINGGO", "kecamatan": {"01": "Sukapura", "02":
"Sumber", "03": "Kuripan", "04": "Bantaran", "05": "Leces", "06": "Banyuanyar",
"07": "Tiris", "08": "Krucil", "09": "Gading", "10": "Pakuniran", "11":
"Kotaanyar", "12": "Paiton", "13": "Besuk", "14": "Kraksaan", "15": "Krejengan",
"16": "Pajarakan", "17": "Maron", "18": "Gending", "19": "Dringu", "20":
"Tegalsiwalan", "21": "Sumberasih", "22": "Wonomerto", "23": "Tongas", "24":
"Lumbang"}},
"14": {"nama": "KABUPATEN PASURUAN", "kecamatan": {"01": "Purwodadi", "02":
"Tutur", "03": "Puspo", "04": "Lumbang", "05": "Pasrepan", "06": "Kejayan", "07":
"Wonorejo", "08": "Purwosari", "09": "Sukorejo", "10": "Prigen", "11": "Pandaan",
"12": "Gempol", "13": "Beji", "14": "Bangil", "15": "Rembang", "16": "Kraton",
"17": "Pohjentrek", "18": "Gondangwetan", "19": "Winongan", "20": "Grati", "21":
"Nguling", "22": "Lekok", "23": "Rejoso", "24": "Tosari"}},
"15": {"nama": "KABUPATEN SIDOARJO", "kecamatan": {"01": "Tarik", "02":
"Prambon", "03": "Krembung", "04": "Porong", "05": "Jabon", "06": "Tanggulangin",
"07": "Candi", "08": "Sidoarjo", "09": "Tulangan", "10": "Wonoayu", "11": "Krian",
"12": "Balongbendo", "13": "Taman", "14": "Sukodono", "15": "Buduran", "16":
"Gedangan", "17": "Sedati", "18": "Waru"}},
"16": {"nama": "KABUPATEN MOJOKERTO", "kecamatan": {"01": "Jatirejo", "02":
"Gondang", "03": "Pacet", "04": "Trawas", "05": "Ngoro", "06": "Pungging", "07":
"Kutorejo", "08": "Mojosari", "09": "Dlanggu", "10": "Bangsal", "11": "Puri", "12":
"Trowulan", "13": "Sooko", "14": "Gedeg", "15": "Kemlagi", "16": "Jetis", "17":
"Dawarblandong", "18": "Mojoanyar"}},
"17": {"nama": "KABUPATEN JOMBANG", "kecamatan": {"01": "Perak", "02": "Gudo",
"03": "Ngoro", "04": "Bareng", "05": "Wonosalam", "06": "Mojoagung", "07":
"Mojowarno", "08": "Diwek", "09": "Jombang", "10": "Peterongan", "11": "Sumobito",
"12": "Kesamben", "13": "Tembelang", "14": "Ploso", "15": "Plandaan", "16":
"Kabuh", "17": "Kudu", "18": "Bandarkedungmulyo", "19": "Jogoroto", "20":
"Megaluh", "21": "Ngusikan"}},
"18": {"nama": "KABUPATEN NGANJUK", "kecamatan": {"01": "Sawahan", "02":
"Ngetos", "03": "Berbek", "04": "Loceret", "05": "Pace", "06": "Prambon", "07":
"Ngronggot", "08": "Jatikalen", "09": "Rejoso", "10": "Grogol", "11": "Nganjuk",
"12": "Bagor", "13": "Wilangan", "14": "Baron", "15": "Tanjunganom", "16":
"Kertosono", "17": "Patianrowo", "18": "Gondang", "19": "Lengkong", "20":
"Ngluyu"}},
"19": {"nama": "KABUPATEN MADIUN", "kecamatan": {"01": "Kebonsari", "02":
"Dolopo", "03": "Geger", "04": "Dagangan", "05": "Kare", "06": "Gemarang", "07":
"Wungu", "08": "Madiun", "09": "Jiwan", "10": "Balerejo", "11": "Mejayan", "12":
"Saradan", "13": "Pilangkenceng", "14": "Sawahan", "15": "Wonoasri"}},
"20": {"nama": "KABUPATEN MAGETAN", "kecamatan": {"01": "Poncol", "02":
"Parang", "03": "Lembeyan", "04": "Takeran", "05": "Nguntoronadi", "06":
"Kawedanan", "07": "Barat", "08": "Kartoharjo", "09": "Sukomoro", "10": "Maospati",
"11": "Karangrejo", "12": "Karas", "13": "Bendo", "14": "Magetan", "15":
"Ngariboyo", "16": "Plaosan", "17": "Sidorejo", "18": "Panekan"}},
"21": {"nama": "KABUPATEN NGAWI", "kecamatan": {"01": "Sine", "02": "Ngrambe",
"03": "Jogorogo", "04": "Kendal", "05": "Mantingan", "06": "Widodaren", "07":
"Kedunggalar", "08": "Karangjati", "09": "Karanganyar", "10": "Paron", "11":
"Padas", "12": "Geneng", "13": "Gerih", "14": "Kwadungan", "15": "Pangkur", "16":
"Bringin", "17": "Pitu", "18": "Ngawi", "19": "Kasreman"}},
"22": {"nama": "KABUPATEN BOJONEGORO", "kecamatan": {"01": "Margomulyo", "02":
"Ngraho", "03": "Tambakrejo", "04": "Ngambon", "05": "Sekar", "06": "Bubulan",
"07": "Gondang", "08": "Malangbong", "09": "Kasiman", "10": "Ngewo", "11": "Kanor",
"12": "Bojonegoro", "13": "Balen", "14": "Kapas", "15": "Sumberrejo", "16":
"Dander", "17": "Kalitidu", "18": "Padangan", "19": "Puri", "20": "Purwosari",
"21": "Trucuk", "22": "Sumberejo", "23": "Baureno", "24": "Kepohbaru", "25":
"Kedungpring", "26": "Sugihwaras", "27": "Temayang", "28": "Ngasem"}},
"23": {"nama": "KABUPATEN TUBAN", "kecamatan": {"01": "Bancar", "02":
"Jatirogo", "03": "Kenduruan", "04": "Bangilan", "05": "Senori", "06": "Singgahan",
"07": "Montong", "08": "Parengan", "09": "Semen", "10": "Gedongombo", "11":
"Plumpang", "12": "Rengel", "13": "Soko", "14": "Semanding", "15": "Merakurak",
"16": "Tuban", "17": "Jenu", "18": "Palang", "19": "Widang", "20": "Grabagan"}},
"24": {"nama": "KABUPATEN LAMONGAN", "kecamatan": {"01": "Sukorame", "02":
"Bluluk", "03": "Modo", "04": "Ngimbang", "05": "Babat", "06": "Kedungpring", "07":
"Brondong", "08": "Laren", "09": "Sekaran", "10": "Maduran", "11": "Sambeng", "12":
"Sugio", "13": "Pucuk", "14": "Paciran", "15": "Solokuro", "16": "Mantup", "17":
"Sukodadi", "18": "Karanggeneng", "19": "Kembangbahu", "20": "Kalitengah", "21":
"Turi", "22": "Lamongan", "23": "Tikung", "24": "Karangbinangun", "25": "Deket",
"26": "Glagah", "27": "Sarirejo"}},
"25": {"nama": "KABUPATEN GRESIK", "kecamatan": {"01": "Wringinanom", "02":
"Kedamean", "03": "Driyorejo", "04": "Menganti", "05": "Balongpanggang", "06":
"Benjeng", "07": "Panceng", "08": "Gresik", "09": "Kebomas", "10": "Duduk
Sampeyan", "11": "Cerme", "12": "Manyar", "13": "Bungah", "14": "Sidayu", "15":
"Dukun", "16": "Ujungpangkah", "17": "Panceng", "18": "Tambak"}},
"26": {"nama": "KABUPATEN BANGKALAN", "kecamatan": {"01": "Bangkalan", "02":
"Socah", "03": "Burneh", "04": "Kamal", "05": "Arosbaya", "06": "Geger", "07":
"Klampis", "08": "Sepulu", "09": "Tanjung Bumi", "10": "Kokop", "11": "Kwanyar",
"12": "Labang", "13": "Tanah Merah", "14": "Tragah", "15": "Blega", "16": "Modung",
"17": "Konang", "18": "Galis"}},
"27": {"nama": "KABUPATEN SAMPANG", "kecamatan": {"01": "Sreseh", "02":
"Torjun", "03": "Sampang", "04": "Camplong", "05": "Omben", "06": "Kedungdung",
"07": "Jrengik", "08": "Tambelangan", "09": "Banyuates", "10": "Robatal", "11":
"Sokobanah", "12": "Ketapang", "13": "Pangarengan", "14": "Karangpenang"}},
"28": {"nama": "KABUPATEN PAMEKASAN", "kecamatan": {"01": "Tlanakan", "02":
"Pademawu", "03": "Pamekasan", "04": "Proppo", "05": "Palenggaan", "06":
"Pegantenan", "07": "Larangan", "08": "Galis", "09": "Pakong", "10": "Pasean",
"11": "Waru", "12": "Batumar", "13": "Kadur"}},
"29": {"nama": "KABUPATEN SUMENEP", "kecamatan": {"01": "Pragaan", "02":
"Bluto", "03": "Saronggi", "04": "Guluk-Guluk", "05": "Lenteng", "06": "Ganding",
"07": "Mandong", "08": "Batuan", "09": "Gapura", "10": "Kota Sumenep", "11":
"Kalianget", "12": "Talango", "13": "Raas", "14": "Sapeken", "15": "Nonggunong",
"16": "Gayam", "17": "Giligenteng", "18": "Masalembu", "19": "Pasongsongan", "20":
"Ambunten", "21": "Batu Putih", "22": "Dasuk", "23": "Rubaru", "24": "Batang-
Batang", "25": "Dungkek", "26": "Arjasa (Kangean)", "27": "Kangayan"}},
"71": {"nama": "KOTA KEDIRI", "kecamatan": {"01": "Mojoroto", "02": "Kota
Kediri", "03": "Pesantren"}},
"72": {"nama": "KOTA BLITAR", "kecamatan": {"01": "Kepanjenkidul", "02":
"Sananwetan", "03": "Sukorejo"}},
"73": {"nama": "KOTA MALANG", "kecamatan": {"01": "Klojen", "02": "Blimbing",
"03": "Lowokwaru", "04": "Sukun", "05": "Kedungkandang"}},
"74": {"nama": "KOTA PROBOLINGGO", "kecamatan": {"01": "Mayangan", "02":
"Kademangan", "03": "Wonoasih", "04": "Kedopok", "05": "Kanigaran"}},
"75": {"nama": "KOTA PASURUAN", "kecamatan": {"01": "Bugul Kidul", "02":
"Purworejo", "03": "Panggungrejo", "04": "Gadingrejo"}},
"76": {"nama": "KOTA MOJOKERTO", "kecamatan": {"01": "Prajuritkulon", "02":
"Magersari", "03": "Kranggan"}},
"77": {"nama": "KOTA MADIUN", "kecamatan": {"01": "Manguharjo", "02": "Taman",
"03": "Kartoharjo"}},
"78": {"nama": "KOTA SURABAYA", "kecamatan": {"01": "Genteng", "02":
"Tegalsari", "03": "Bubutan", "04": "Simokerto", "05": "Tambaksari", "06":
"Gubeng", "07": "Rungkut", "08": "Tenggilis Mejoyo", "09": "Gunung Anyar", "10":
"Sukolilo", "11": "Mulyorejo", "12": "Sawahan", "13": "Wonokromo", "14": "Dukuh
Pakis", "15": "Karangpilang", "16": "Wiyung", "17": "Wonocolo", "18": "Jambangan",
"19": "Gayungan", "20": "Tandes", "21": "Asem Rowo", "22": "Sukomanunggal", "23":
"Benowo", "24": "Pakal", "25": "Lakarsantri", "26": "Sambikerep", "27": "Pabean
Cantian", "28": "Semampir", "29": "Krembangan", "30": "Kenjeran", "31": "Bulak"}},
"79": {"nama": "KOTA BATU", "kecamatan": {"01": "Batu", "02": "Bumiaji", "03":
"Junrejo"}}
}

# HLR REGION MAP - TIDAK BERUBAH (Hanya menggunakan prefix 812, 857, 878)
HLR_REGION_MAP: Dict[str, str] = {}
for code_kab_kota, data in DATA_JAWA_TIMUR.items():
name = data['nama']

# Entri 5-Digit (Kabupaten/Kota)


HLR_REGION_MAP[f"812{code_kab_kota}"] = f"{name} (HLR: TSEL)"
HLR_REGION_MAP[f"857{code_kab_kota}"] = f"{name} (HLR: ISAT)"
HLR_REGION_MAP[f"878{code_kab_kota}"] = f"{name} (HLR: XL)"

# Entri 7-Digit (Kecamatan)


for code_kecamatan, name_kecamatan in data['kecamatan'].items():
prefix_7d = f"{code_kab_kota}{code_kecamatan}"
HLR_REGION_MAP[f"812{prefix_7d}"] = f"{name}, KEC. {name_kecamatan.upper()}
(HLR: TSEL)"
HLR_REGION_MAP[f"857{prefix_7d}"] = f"{name}, KEC. {name_kecamatan.upper()}
(HLR: ISAT)"
HLR_REGION_MAP[f"878{prefix_7d}"] = f"{name}, KEC. {name_kecamatan.upper()}
(HLR: XL)"

# HLR 3D Umum (Diperluas sesuai daftar baru)


HLR_REGION_MAP.update({
"8120": "Jabodetabek/Jawa Barat (HLR Umum)", "8570": "Jawa Barat/Banten (HLR
Umum)",
"8780": "Jabodetabek/Sumatera (HLR Umum)",

# Prefix Telkomsel
"811": "Telkomsel (HLR Umum Nasional)", "812": "Telkomsel (HLR Umum Nasional)",
"813": "Telkomsel (HLR Umum Nasional)",
"821": "Telkomsel (HLR Umum Nasional)", "822": "Telkomsel (HLR Umum Nasional)",
"823": "Telkomsel (HLR Umum Nasional)",
"851": "Telkomsel (HLR Umum Nasional)", "852": "Telkomsel (HLR Umum Nasional)",
"853": "Telkomsel (HLR Umum Nasional)",

# Prefix Indosat Ooredoo Hutchison


"814": "Indosat (HLR Umum Nasional)", "815": "Indosat (HLR Umum Nasional)",
"816": "Indosat (HLR Umum Nasional)",
"855": "Indosat (HLR Umum Nasional)", "856": "Indosat (HLR Umum Nasional)",
"857": "Indosat (HLR Umum Nasional)",
"858": "Indosat (HLR Umum Nasional)",

# Prefix XL Axiata (XL & AXIS)


"817": "XL Axiata (HLR Umum Nasional)", "818": "XL Axiata (HLR Umum Nasional)",
"819": "XL Axiata (HLR Umum Nasional)",
"859": "XL Axiata (HLR Umum Nasional)", "877": "XL Axiata (HLR Umum Nasional)",
"878": "XL Axiata (HLR Umum Nasional)",
"831": "AXIS (HLR Umum Nasional)", "832": "AXIS (HLR Umum Nasional)", "833":
"AXIS (HLR Umum Nasional)", "838": "AXIS (HLR Umum Nasional)",

# Prefix Tri
"895": "Tri (HLR Umum Nasional)", "896": "Tri (HLR Umum Nasional)", "897": "Tri
(HLR Umum Nasional)",
"898": "Tri (HLR Umum Nasional)", "899": "Tri (HLR Umum Nasional)",

# Prefix Smartfren
"881": "Smartfren (HLR Umum Nasional)", "882": "Smartfren (HLR Umum Nasional)",
"883": "Smartfren (HLR Umum Nasional)",
"884": "Smartfren (HLR Umum Nasional)", "885": "Smartfren (HLR Umum Nasional)",
"886": "Smartfren (HLR Umum Nasional)",
"887": "Smartfren (HLR Umum Nasional)", "888": "Smartfren (HLR Umum Nasional)",
"889": "Smartfren (HLR Umum Nasional)",
})

# --- GLOBAL SESSION MEMORY (EPHEMERAL) ---


PROCESSED_HOTLIST: set = set()
INPUT_TALLY: Counter = Counter()

# ==============================================================================
# FUNGSI PENDUKUNG (NORMALIZATION & IDENTIFICATION)
# ==============================================================================

def is_valid_indonesian_input(phone_number: str) -> bool:


clean_num = ''.join(filter([Link], phone_number.replace('+', ''))).strip()
if len(clean_num) < 9: return False
if not (clean_num.startswith(('0', '62', '8'))): return False
return True

def validate_length(digits: str) -> Tuple[str, str]:


"""Validasi Panjang Nomor."""
length = len(digits)
if length < 6: return f"TOO SHORT ({length}D)", [Link]
elif 9 <= length <= 12: return f"IDEAL ({length}D)", [Link]
elif 13 <= length <= 15: return f"STANDARD ({length}D)", [Link]
else: return f"SUSPECT ({length}D)", [Link]

def clean_and_normalize_number(phone_number: str) -> Tuple[str, str, str, str,


str]:
clean_number = ''.join(filter([Link], phone_number.replace('+', '')))
if not clean_number: return "", "", "", "INVALID", ""
if clean_number.startswith('62'): prefix_digits = clean_number[2:];
final_number = f"+{clean_number}"
elif clean_number.startswith('0'): prefix_digits = clean_number[1:];
final_number = f"+62{clean_number[1:]}"
else: prefix_digits = clean_number; final_number = f"+62{clean_number}"

prefix = ""; number_type = "Tidak Dikenal"; remaining_digits = ""


pstn_prefix_3 = prefix_digits[:3]; pstn_prefix_2 = prefix_digits[:2]

if pstn_prefix_3 in PSTN_CODES: prefix = pstn_prefix_3; number_type = "Fixed


Line (PSTN)"; remaining_digits = prefix_digits[3:]
elif pstn_prefix_2 in PSTN_CODES: prefix = pstn_prefix_2; number_type = "Fixed
Line (PSTN)"; remaining_digits = prefix_digits[2:]
elif len(prefix_digits) >= 3 and prefix_digits[:3].startswith('8'): prefix =
prefix_digits[:3]; number_type = "Mobile (Seluler)"; remaining_digits =
prefix_digits[3:]
else: prefix = prefix_digits; number_type = "Format Tidak Biasa";
remaining_digits = ""

return final_number, "+62", prefix, number_type, remaining_digits

def identify_operator(prefix: str, number_type: str) -> Tuple[str, str]:


if number_type == "Fixed Line (PSTN)":
location = PSTN_CODES.get(prefix, "Lokasi Fixed Line Unknown")
return f"Area Code: {location}", [Link]
if number_type == "Mobile (Seluler)":
operator = OPERATOR_CODES.get(prefix)
if operator: return operator, [Link]
return "Mobile Operator Unknown", [Link]
return number_type, [Link]

def get_full_prefix_digits(normalized_number: str) -> str:


if normalized_number.startswith('+62'):
return normalized_number[3:]
return ""

# ==============================================================================
# FUNGSI ANALISIS & DISPLAY UTAMA (V21.0 - ROBUST MODE)
# ==============================================================================

def get_analysis_data(original_number: str, index: int) -> Dict[str, str]:

if not is_valid_indonesian_input(original_number):
return {"Status": "Filtered", "Catatan": f"{[Link]}Input Filtered
(Irrelevant Format){[Link]}", "ID": str(index)}

normalized_number, country_code, prefix, number_type, remaining_digits =


clean_and_normalize_number(original_number)

if number_type == "INVALID":
return {"Status": "Error", "Catatan": "Invalid Phone Number Input", "ID":
str(index)}

# --- Identification & Validation ---


length_status, _ = validate_length(remaining_digits)
operator_name, operator_color = identify_operator(prefix, number_type)

# V21.0: HLR REGION INFERENCE - ROBUST MODE


hlr_area = "Tidak Dapat Diidentifikasi (HLR Umum)"
full_digits = get_full_prefix_digits(normalized_number)

# Simpan hasil analisis HLR regional untuk ditampilkan


specific_hlr_info: Dict[str, str] = {}

if number_type == "Mobile (Seluler)":


# 1. Cek 7 Digit (Kecamatan) - Hanya untuk TSEL/ISAT/XL JATIM
if len(full_digits) >= 7:
prefix_7d = full_digits[:7]
if prefix_7d in HLR_REGION_MAP and prefix in ["812", "857", "878"]:
hlr_area = HLR_REGION_MAP[prefix_7d]
specific_hlr_info['Prefix_HLR_7D'] = prefix_7d
specific_hlr_info['Level'] = 'Kecamatan'

# 2. Cek 5 Digit (Kabupaten/Kota) - ROBUST MODE


if not specific_hlr_info and len(full_digits) >= 5:
prefix_5d = full_digits[:5]
kode_kab_kota_2d = prefix_5d[3:]

if kode_kab_kota_2d in DATA_JAWA_TIMUR:
name_kab_kota = DATA_JAWA_TIMUR[kode_kab_kota_2d]['nama']

# Menggunakan operator_name yang sudah dijamin string


operator_short_name = operator_name.split(' ')[0]

hlr_area = f"{name_kab_kota} (HLR: {operator_short_name})"


specific_hlr_info['Prefix_HLR_5D'] = prefix_5d
specific_hlr_info['Level'] = 'Kabupaten/Kota (Manual/Robust)'
else:
# BLOK HLR TIDAK COCOK DENGAN PETA JATIM
hlr_area = f"{[Link]}BLOK HLR TIDAK COCOK DENGAN PETA JATIM
({prefix_5d}){[Link]}"
specific_hlr_info['Prefix_5D_Mismatch'] = prefix_5d
specific_hlr_info['Level'] = 'Unknown/Non-Jatim Block'

# 3. Default ke 3 Digit (Operator Umum)


if not specific_hlr_info:
if prefix in HLR_REGION_MAP:
hlr_area = HLR_REGION_MAP[prefix]
elif prefix in OPERATOR_CODES:
hlr_area = f"{operator_name.split(' ')[0]} (HLR Umum Nasional)"

elif number_type == "Fixed Line (PSTN)":


hlr_area = operator_name.replace("Area Code: Fixed Line (", "").rstrip(")")

# Kumpulkan Data Mentah


result = {
"ID": str(index), "Input_Asli": original_number, "Status": "OK",
"Normalized_Number": normalized_number, "Tipe_Nomor": number_type,
"Prefix_Kode": prefix,
"Operator_Lokasi": operator_name, "Lokasi_HLR": hlr_area,
"Validasi_Digit": length_status, "Kode_Warna": operator_color,
"Prefix_5D": full_digits[:5], "Prefix_7D": full_digits[:7],
"Specific_HLR": specific_hlr_info
}
return result

def display_informant_report(data: Dict[str, str], hotlist_status: str):


"""Menampilkan laporan ringkas Informan (HLR ROBUST MODE V21.0)."""

if data['Status'] == 'Filtered':
print(f"\n{[Link]}--- REPORT ID {data['ID']} ---{[Link]}")
print(f"[!] {data['Catatan']}")
print(Colors.SECTION_SEP)
return
operator_color = [Link]("Kode_Warna", [Link])

print(f"\n{[Link]}--- REPORT ID {data['ID']}


[{data['Tipe_Nomor'].upper()}] {hotlist_status} {[Link]}---{[Link]}")
print(Colors.SECTION_SEP)
print(f"INPUT_ASLI: {operator_color}{data['Input_Asli']}{[Link]}")
print(f"NOMOR_NORM: {data['Normalized_Number']}")

print(f"\n-- DATA DASAR --")


print(f"OPERATOR: {operator_color}{data['Operator_Lokasi']}{[Link]}
(Prefix: {data['Prefix_Kode']})")
print(f"PANJANG_VALID: {data['Validasi_Digit']}")

print(f"\n-- LOKASI REGISTRASI (HLR) --")

hlr_info = data['Specific_HLR']

if data['Tipe_Nomor'] == 'Fixed Line (PSTN)':


print(f"LOKASI_FISIK: {[Link]}{data['Lokasi_HLR']}{[Link]}")
else:
# Tampilkan HLR spesifik (Kecamatan/Kab/Kota) jika ditemukan
if 'Level' in hlr_info and hlr_info['Level'] in ['Kecamatan',
'Kabupaten/Kota', 'Kabupaten/Kota (Manual/Robust)']:
print(f"REG_AREA_DETAIL: {[Link]}{data['Lokasi_HLR']}
{[Link]}")

# Tampilkan Mismatch/HLR Umum


elif 'Level' in hlr_info and hlr_info['Level'] == 'Unknown/Non-Jatim
Block':
print(f"REG_AREA_DETAIL: {data['Lokasi_HLR']}")

# Jika tidak ada yang spesifik, tampilkan HLR 3D umum


else:
print(f"LOKASI_HLR_UMUM: {[Link]}{data['Lokasi_HLR']}
{[Link]}")

print(f"LOKASI_LIVE: {[Link]}TIDAK DAPAT DIKETAHUI (Mobile)


{[Link]}")

print(Colors.SECTION_SEP)

def display_session_summary(total_numbers: int, all_analysis_results:


List[Dict[str, str]]):
"""Menampilkan ringkasan sesi."""

total_mobile = 0; total_pstn = 0; total_filtered = 0; proximity_counts:


Dict[str, int] = {}

for data in all_analysis_results:


if [Link]('Status') == 'OK':
if [Link]('Tipe_Nomor') == 'Mobile (Seluler)':
total_mobile += 1
prefix_5d = [Link]('Prefix_5D')
if prefix_5d and len(prefix_5d) == 5: proximity_counts[prefix_5d] =
proximity_counts.get(prefix_5d, 0) + 1
elif [Link]('Tipe_Nomor') == 'Fixed Line (PSTN)': total_pstn += 1
elif [Link]('Status') == 'Filtered': total_filtered += 1
print(f"\n{[Link]}{Colors.HEADER_SEP}{[Link]}")
print(f"{[Link]}[SESSION SUMMARY]{[Link]} | Hotlist Active:
{len(PROCESSED_HOTLIST)} items")
print(Colors.SECTION_SEP)
print(f"Jumlah Proses: {total_numbers}")
print(f"Mobile (Seluler):{total_mobile}")
print(f"Fixed Line (PSTN):{total_pstn}")
print(f"Input Filtered: {total_filtered}")

# Analisis Kedekatan Blok (Top 3)


if proximity_counts:
sorted_prox = sorted(proximity_counts.items(), key=lambda item: item[1],
reverse=True)
print(f"\n[ANALISIS KEDEKATAN BLOK: TOP 3 5-DIGIT]")
for prefix, count in sorted_prox[:3]:
# Coba cari nama wilayah untuk 5 digit tersebut
wilayah_code = prefix[3:]
wilayah_name = "Unknown"
if wilayah_code in DATA_JAWA_TIMUR:
wilayah_name = DATA_JAWA_TIMUR[wilayah_code]['nama']

print(f"-> [{prefix}XXXX] Tally: {count}x ({wilayah_name})")

print(f"\n[STATUS] {[Link]}Processing Complete. Standby.{[Link]}")


print(f"{[Link]}{Colors.HEADER_SEP}{[Link]}")

# ==============================================================================
# FUNGSI UTAMA (V21.0 FINAL)
# ==============================================================================

def run_identifier_final_edition():

print(f"\n{[Link]}{Colors.HEADER_SEP}{[Link]}")
print(f"{[Link]}[+] ANALYZER HLR V21.0: LOKASI REGISTRASI AWAL (ROBUST
MODE){[Link]}")
print(f"{[Link]}[INFO] Data Prefix Operator Diperbarui. Total
{len(OPERATOR_CODES)} Prefix Mobile Terdaftar.{[Link]}")
print(f"{[Link]}{Colors.HEADER_SEP}{[Link]}")
print(f"{[Link]}[INFO] Mode: ROBUST. Analisis HLR mengutamakan Prefix
5D/7D meskipun nomor pendek.{[Link]}")
print(f"{[Link]}[!] EPHEMERAL MODE ACTIVE: Data resident in RAM only
({len(PROCESSED_HOTLIST)} items).{[Link]}")

numbers_to_analyze: List[str] = []

# --- INPUT MODE: Kunci ke MANUAL MULTI-GRABBER ---


print(f"\n[INPUT MODE] Kunci: Manual Multi-Grabber.")
print(f"Input target numbers line-by-line. Press ENTER twice to finalize list.\
n")

i = 1
while True:
try:
user_input = input(f"TARGET_ID {i:03}: ").strip()
except EOFError:
break

if not user_input: break


if user_input.lower() in ['exit', 'quit']: [Link](0)

numbers_to_analyze.append(user_input)
i += 1

if not numbers_to_analyze:
print(f"{[Link]}[FATAL] No targets defined. Aborting session.
{[Link]}"); return

# 2. Analisis Batch dan Display


print(f"\n{[Link]}{Colors.HEADER_SEP}{[Link]}")
print(f"[STATUS] Analyzing {len(numbers_to_analyze)} Targets (V21.0 HLR
Robust)...")

all_analysis_results: List[Dict[str, str]] = []

for idx, original_number in enumerate(numbers_to_analyze, 1):

normalized_number, _, _, _, _ = clean_and_normalize_number(original_number)

# HOTLIST CHECK (Session Memory)


hotlist_status = ""
if normalized_number in PROCESSED_HOTLIST:
hotlist_status = f"{[Link]}[DUPLICATE HIT]{[Link]}"
else:
PROCESSED_HOTLIST.add(normalized_number)

# SMART TALLY FILTER


INPUT_TALLY[normalized_number] += 1
if INPUT_TALLY[normalized_number] > 5:
analysis_data = {"Status": "Filtered", "Catatan": f"{[Link]}Input
Filtered (Repeated > 5x in session){[Link]}", "ID": str(idx)}
else:
analysis_data = get_analysis_data(original_number, idx)

all_analysis_results.append(analysis_data)
display_informant_report(analysis_data, hotlist_status)

# 3. Laporan Ringkasan Sesi (Final)


display_session_summary(len(numbers_to_analyze), all_analysis_results)

# ==============================================================================
# EKSEKUSI PROGRAM
# ==============================================================================

if __name__ == "__main__":
try:
run_identifier_final_edition()
except KeyboardInterrupt:
print(f"\n[STATUS] {[Link]}Program interrupted. Aborting.
{[Link]}")
[Link](0)
except Exception as e:
print(f"\n[FATAL] Unhandled error: {e}")
[Link](1)
[Link]

import sys
import time
import subprocess
import re
import requests
import os
import ipaddress
import platform
from [Link] import urlparse

# --- KONFIGURASI API ---


LOCATIONIQ_KEY = "pk.9e4078dfffc114746a6b7580d1bfb9bc"

# --- KONFIGURASI TAMPILAN ---


RED = "\033[91m"
GREEN = "\033[92m"
YELLOW = "\033[93m"
CYAN = "\033[96m"
MAGENTA = "\033[95m"
RESET = "\033[0m"
BOLD = "\033[1m"
WHITE = "\033[97m"

def clear_screen():
[Link]('cls' if [Link] == 'nt' else 'clear')

def typing_effect(text, delay=0.03, color=GREEN):


for char in text:
[Link](color + char + RESET)
[Link]()
[Link](delay)
print("")

# --- AUTO PARSING URL ---


def smart_parse_input(user_input):
user_input = user_input.strip()
if user_input.startswith("[Link] or user_input.startswith("[Link]
try:
parsed_url = urlparse(user_input)
domain = parsed_url.netloc
return domain
except:
return user_input
return user_input

# --- CEK TIPE IP ---


def validate_ip(input_str):
try:
ip_obj = ipaddress.ip_address(input_str)
return "IPv6" if isinstance(ip_obj, ipaddress.IPv6Address) else "IPv4"
except ValueError:
return None

# --- GET BASIC IP DATA ---


def get_ip_details(ip):
try:
if ipaddress.ip_address(ip).is_private:
return {'status': 'private'}
except: pass
try:
headers = {'User-Agent': 'curl/7.68.0'}
response = [Link](f"[Link]
fields=status,message,country,city,lat,lon,isp,org,as", headers=headers, timeout=4)
return [Link]()
except:
return None

# --- REVERSE GEOCODING (LocationIQ) ---


def get_detailed_address(lat, lon):
try:
url = f"[Link]
key={LOCATIONIQ_KEY}&lat={lat}&lon={lon}&format=json"
response = [Link](url, timeout=5)
data = [Link]()
if 'address' in data:
addr = data['address']
return {
'road': [Link]('road', '-'),
'village': [Link]('village', [Link]('hamlet', '-')),
'county': [Link]('county', [Link]('district', '-')),
'city': [Link]('city', [Link]('town', '-')),
'state': [Link]('state', '-'),
'country': [Link]('country', '-'),
'postcode': [Link]('postcode', '-')
}
except:
return None
return None

# --- [RESTORED] DISTRIBUTED PROBING ENGINE ---


def run_distributed_probe(target):
print(MAGENTA + BOLD + "\n[+] INITIATING GLOBAL DISTRIBUTED SENSORS..." +
RESET)
print(f"{YELLOW}[*] Requesting telemetry from satellite nodes...{RESET}")

# Menggunakan API Check-Host


api_url = f"[Link]
headers = {'Accept': 'application/json'}

try:
req = [Link](api_url, headers=headers, timeout=10)
data = [Link]()

if [Link]('ok') != 1:
print(RED + "[!] Failed to initiate global nodes." + RESET)
return

request_id = [Link]('request_id')
nodes = [Link]('nodes', {})

print(f"{GREEN}[+] Nodes Acquired. Syncing Data...{RESET}")

# Loading Animation
for i in range(5):
[Link](f"\r{CYAN}[WAITING] {'|/-\\'[i % 4]} Receiving
packets...{RESET}")
[Link]()
[Link](1)
print("\n")

result_url = f"[Link]
result_req = [Link](result_url, headers=headers, timeout=10)
results = result_req.json()

print(f"{BOLD}{'NODE LOCATION':<25} | {'STATUS':<10} | {'LATENCY':<15}


{RESET}")
print("-" * 60)

for node_code, node_data in [Link]():


country_code = node_code[:2].upper()
# Ambil nama lokasi dari metadata nodes
location_name = [Link](node_code, [country_code, "", ""])[1] or
country_code
location_display = f"[{country_code}] {location_name}"

if not node_data:
print(f"{location_display:<25} | {YELLOW}WAITING{RESET} | ...")
continue

# Parsing hasil ping dari node


pings = [p[1] for p in node_data[0] if isinstance(p, list) and len(p) >
1 and p[1] != 'OK']

if pings:
try:
avg_ping = sum([float(p) for p in pings if isinstance(p, (int,
float))]) / len(pings)
latency_ms = avg_ping * 1000

if latency_ms > 300: status = f"{YELLOW}HIGH LAT{RESET}"


else: status = f"{GREEN}ONLINE{RESET}"

latency_text = f"{latency_ms:.2f} ms"


except:
status = f"{GREEN}ONLINE{RESET}"
latency_text = "OK"
else:
status = f"{RED}OFFLINE{RESET}"
latency_text = "TIMEOUT"

print(f"{location_display:<25} | {status:<10} | {latency_text:<15}")

except Exception as e:
print(RED + f"[!] Distributed Probe Error: {e}" + RESET)

# --- LOCAL SEQUENCE PING ---


def run_sequence_ping(target, count=5):
print(MAGENTA + BOLD + "\n[+] INITIATING LOCAL SEQUENCE PING..." + RESET)
print(f"{YELLOW}[*] Targeting: {target} | Packets: {count}{RESET}")
param = '-n' if [Link]().lower() == 'windows' else '-c'
cmd = ['ping', param, str(count), target]
print(f"{BOLD}{'SEQ':<5} | {'BYTES':<8} | {'TTL':<5} | {'LATENCY':<15} |
{'STATUS':<10}{RESET}")
print("-" * 60)
try:
proc = [Link](cmd, stdout=[Link],
stderr=[Link], text=True)
seq = 1
for line in [Link]:
line = [Link]()
if not line: continue
bytes_match = [Link](r'(\d+)\s*bytes', [Link]())
time_match = [Link](r'time[=<]([\d\.]+)', [Link]())
ttl_match = [Link](r'ttl[=<](\d+)', [Link]())
if time_match:
latency = float(time_match.group(1))
ttl = ttl_match.group(1) if ttl_match else "N/A"
b_size = bytes_match.group(1) if bytes_match else "64"
if latency < 50: lat_color = GREEN
elif latency < 150: lat_color = YELLOW
else: lat_color = RED
print(f"{seq:<5} | {b_size:<8} | {ttl:<5} | {lat_color}
{latency:.2f} ms{RESET:<15} | {GREEN}REPLY{RESET}")
seq += 1
[Link](0.05)
elif "timed out" in [Link]() or "unreachable" in [Link]():
print(f"{seq:<5} | {'---':<8} | {'N/A':<5} |
{RED}TIMEOUT{RESET:<15} | {RED}LOSS{RESET}")
seq += 1
[Link]()
except Exception as e:
print(RED + f"[!] Ping Error: {e}" + RESET)

# --- TRACEROUTE ---


def run_traceroute(target, ip_version="IPv4"):
print(YELLOW + f"\n[+] STARTING HOP-BY-HOP TRACE ({ip_version})..." + RESET)
print(f"{BOLD}{'HOP':<4} | {'IP ADDRESS':<25} | {'LOCATION/ISP':<30}{RESET}")
print("-" * 65)
cmd = ["tracert", "-d", "-h", "20", target] if [Link]().lower() ==
'windows' else ["traceroute", "-n", "-m", "20", target]
if ip_version == "IPv6" and [Link]().lower() != 'windows':
[Link](1, "-6")

try:
proc = [Link](cmd, stdout=[Link],
stderr=[Link], text=True)
last_hop_ip = target
for line in [Link]:
line = [Link]()
if not line: continue
if [Link]("traceroute") or [Link]("Tracing"):
continue
hop_match = [Link](r'^\s*(\d+)', line)
ip_match = [Link](r'([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-fA-F0-9:]+:+
[a-fA-F0-9:]+)', line)
if hop_match and ip_match:
hop_num = hop_match.group(1)
ip_addr = ip_match.group(1)
details = get_ip_details(ip_addr)
display_info = "Unknown"
color = RESET
if details:
if [Link]('status') == 'private':
display_info = f"{YELLOW}LOCAL / PRIVATE NETWORK{RESET}"
color = CYAN
elif [Link]('status') == 'success':
isp = [Link]('isp', 'Unknown')
country = [Link]('country', '')
display_info = f"{isp} ({country})"
color = GREEN
last_hop_ip = ip_addr
print(f"{hop_num:<4} | {color}{ip_addr:<25}{RESET} |
{display_info}")
[Link](0.05)
return last_hop_ip
except:
return target

# --- ANIMASI ---


def triangulation_animation():
frames = [
"""
[SAT-1] [SAT-2]
. .
. .
[TARGET]
""",
"""
[SAT-1] [SAT-2]
o o
o o
[TARGET] [SAT-3]... LOCKING
""",
"""
[SAT-1]-------[SAT-2]
\\ /
\\ /
[TARGET]-------[SAT-3]
"""
]
print(RED + BOLD + "\n WARNING: INITIATING SATELLITE TRIANGULATION..." +
RESET)
[Link](1)
for _ in range(3):
for frame in frames:
[Link]("\033[K")
print(YELLOW + frame + RESET)
[Link]("\033[F" * 6)
[Link](0.3)
print("\n" * 6)

# --- MAP ZOOM ---


def display_map_chafa(lat, lon):
print(CYAN + "\n[+] INITIATING SATELLITE ZOOM SEQUENCE..." + RESET)
[Link](1)
zoom_levels = [6, 9, 12, 15, 17]
for zoom in zoom_levels:
clear_screen()
print(f"{GREEN}[INFO] ORBITAL LEVEL: {zoom}x | LOCKED: {lat}, {lon}
{RESET}")
print(f"{YELLOW}[*] DOWNLOADING HIGH-RES TILE...{RESET}")
try:
map_url = f"[Link]
{lat}&z={zoom}&l=sat&size=650,450"
cmd = f"curl -s '{map_url}' | chafa -c full --
symbols=block+border+space -s 100x40 -"
[Link](cmd)
[Link](1.5)
except:
print(RED + "[!] Render Error." + RESET)
[Link](1)

# --- MAIN PROGRAM ---


def main():
clear_screen()
typing_effect("INITIALIZING GEO-TRACKER V7.0 (HYBRID PROBE)...", 0.05)

raw_input = input(BOLD + "\nInput IPv4/IPv6, URL, or Domain: " + RESET)


user_input = smart_parse_input(raw_input)
target_ip = ""

# 1. RESOLVING
print(YELLOW + "\n[+] EXECUTING DNS LOOKUP..." + RESET)
try:
if validate_ip(user_input):
target_ip = user_input
else:
ns_cmd = ["nslookup", user_input] if [Link]().lower() ==
'windows' else ["dig", "+short", user_input]
target_ip_raw = subprocess.check_output(ns_cmd, text=True)
ip_match = [Link](r'([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)',
target_ip_raw)
if ip_match:
target_ip = ip_match.group(1)
print(f"{GREEN}[+] RESOLVED IP: {target_ip}{RESET}")
else:
raise Exception("DNS Fail")
except:
print(RED + "[-] Host Unreachable." + RESET)
return

# 2. DISTRIBUTED PROBE (GLOBAL) - FITUR LAMA DIKEMBALIKAN


run_distributed_probe(user_input)
# 3. SEQUENCE PING (LOCAL) - FITUR BARU DIPERTAHANKAN
run_sequence_ping(target_ip, count=5)

# 4. ANIMASI
triangulation_animation()

# 5. TRACEROUTE
last_hop = run_traceroute(target_ip)

# 6. GEOLOCATION & DETAIL


final_target = last_hop if validate_ip(last_hop) else target_ip

ip_data = get_ip_details(final_target)

if ip_data and ip_data.get('status') == 'success':


lat, lon = ip_data['lat'], ip_data['lon']

print(YELLOW + "\n[+] AUTHENTICATING WITH LOCATIONIQ SATELLITE..." + RESET)


address_data = get_detailed_address(lat, lon)

print("\n" + WHITE + "="*50 + RESET)


print(f"{BOLD}{CYAN} GEOSPATIAL INTELLIGENCE REPORT {RESET}")
print(WHITE + "="*50 + RESET)

if address_data:
print(f"{'Nama Jalan':<12}: {GREEN}{address_data['road']}{RESET}")
print(f"{'Kel/Desa':<12}: {GREEN}{address_data['village']}{RESET}")
print(f"{'Kab/Kota':<12}: {GREEN}{address_data['county']}{RESET}")
print(f"{'Provinsi':<12}: {GREEN}{address_data['state']}{RESET}")
print(f"{'Negara':<12}: {GREEN}{address_data['country']}{RESET}")
else:
print(f"{'Kota':<12}: {GREEN}{ip_data.get('city')}{RESET}")
print(f"{'Negara':<12}: {GREEN}{ip_data.get('country')}{RESET}")

print(WHITE + "-"*50 + RESET)


print(f"{BOLD}Lat : {lat}{RESET}")
print(f"{BOLD}Long: {lon}{RESET}")
print(WHITE + "="*50 + RESET)

# 7. MAP ZOOM
input(BOLD + "\n[PRESS ENTER TO INITIATE SATELLITE FEED]" + RESET)
display_map_chafa(lat, lon)
else:
print(RED + "\n[!] Could not retrieve location data." + RESET)

print(GREEN + "\n[+] TARGET TRACKED SUCCESSFULLY." + RESET)

if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n[!] ABORTED.")
[Link]

import os
import sys
import time
import requests

# --- Strategic Signal Codes ---


class Signal:
CRITICAL = '\033[31m'
ACTIVE = '\033[32m'
PENDING = '\033[33m'
DATA = '\033[36m'
BOLD = '\033[1m'
RESET = '\033[0m'

def transmit(text, speed=0.005):


for char in text:
[Link](char)
[Link]()
[Link](speed)
print()

def open_google_earth(lat, lon):


"""Injeksi koordinat ke Google Earth / Maps via Android Intent"""
transmit(f"{[Link]}[!] DEEP SCAN DETECTED: EXCEEDING SATELLITE LIMIT
16x...{[Link]}")
transmit(f"{[Link]}[*] INITIATING GOOGLE EARTH DEEP-LINK PROTOCOL...
{[Link]}")

# URL untuk Google Maps Satellite di level zoom maksimal (z=21 atau lebih)
# Ini akan memicu 'Open with' di Android
geo_url = f"[Link]

# Perintah Termux untuk membuka URL di browser/aplikasi eksternal


[Link](f"termux-open-url '{geo_url}'")

def fetch_and_render(lat, lon, z):


if int(z) > 16:
open_google_earth(lat, lon)
return

filename = f"prec_hd.png"
url = f"[Link]
{lat}&z={z}&l=sat&size=650,450"

headers = {'User-Agent': 'Mozilla/5.0'}

try:
r = [Link](url, headers=headers, timeout=10)
if r.status_code == 200:
with open(filename, 'wb') as f:
[Link]([Link])

[Link]('clear')
print(f"{[Link]}{[Link]}[SYSTEM] HYBRID MODE | ZOOM: {z}x |
STATUS: STABLE{[Link]}")
# Resolusi 146x100p sesuai permintaan sebelumnya
[Link](f"chafa -c full --symbols block+vhalf --dither none --size
146x100 --stretch {filename}")
else:
print(f"{[Link]}[!] Uplink Failed.{[Link]}")
except Exception as e:
print(f"{[Link]}[!] Error: {e}{[Link]}")

def main():
[Link]('clear')
transmit(f"{[Link]}{[Link]} [ SATELLITE HYBRID: TERMINAL + GOOGLE
EARTH ]{[Link]}")

target_ip = input(f"{[Link]}Target IP: {[Link]}")

try:
geo = [Link](f"[Link]
lat, lon = geo['lat'], geo['lon']
transmit(f"{[Link]}[+] Target Locked: {geo['city']},
{geo['country']}{[Link]}")
except: return

while True:
try:
z_input = input(f"{[Link]}{[Link]}ZOOM_CMD (1-21) >
{[Link]}")
if z_input == '0': break
if not z_input.isdigit(): continue

fetch_and_render(lat, lon, z_input)


except KeyboardInterrupt: break

if __name__ == "__main__":
main()
[Link]

import os
import sys
import time
import requests
import re

# --- Strategic Signal Codes ---


class Signal:
CRITICAL = '\033[31m'
ACTIVE = '\033[32m'
PENDING = '\033[33m'
DATA = '\033[36m'
BOLD = '\033[1m'
RESET = '\033[0m'

def transmit(text, speed=0.005):


for char in text:
[Link](char)
[Link]()
[Link](speed)
print()

def dms_to_dd(degrees, minutes, seconds, direction):


"""Mengonversi format DMS ke Decimal Degrees"""
dd = float(degrees) + float(minutes)/60 + float(seconds)/(60*60)
if direction in ['S', 'W', 's', 'w']:
dd *= -1
return dd

def parse_coordinates(input_str):
"""Mendeteksi apakah input adalah Desimal atau DMS"""
# Pattern 1: Decimal (e.g., -8.74, 115.16)
decimal_match = [Link](r"^(-?\d+\.?\d*)\s*,\s*(-?\d+\.?\d*)$", input_str)
if decimal_match:
return float(decimal_match.group(1)), float(decimal_match.group(2))

# Pattern 2: DMS (e.g., 8°44'45"S 115°10'05"E)


# Mencari pola angka-derajat-menit-detik-arah
dms_regex = r"(\d+)°(\d+)'([\d\.]+)\"([NSEWnsew])"
matches = [Link](dms_regex, input_str)

if len(matches) == 2:
lat = dms_to_dd(*matches[0])
lon = dms_to_dd(*matches[1])
return lat, lon

return None, None

def open_google_satellite(lat, lon, z):


transmit(f"{[Link]}[!] DEEP SCAN: EXCEEDING TERMINAL PREVIEW...
{[Link]}")
transmit(f"{[Link]}[*] LAUNCHING GOOGLE MAPS HYBRID...{[Link]}")
# URL Google Maps yang benar untuk koordinat
geo_url = f"[Link]
[Link](f"termux-open-url '{geo_url}'")

def fetch_and_render(lat, lon, z, tag):


if int(z) > 17:
open_google_satellite(lat, lon, z)
z = 17

filename = "prec_hd.png"
# Yandex Static Maps API
url = f"[Link]
{lat}&z={z}&l=sat&size=650,450"

try:
r = [Link](url, timeout=10)
if r.status_code == 200:
with open(filename, 'wb') as f:
[Link]([Link])

[Link]('clear')
print(f"{[Link]}{[Link]}[ V18 HYBRID SURVEILLANCE ]
{[Link]}")
print(f"{[Link]}TARGET : {tag}{[Link]}")
print(f"{[Link]}COORD : {lat}, {lon}{[Link]}")
print(f"{[Link]}MODE : {'TERMINAL' if int(z) <= 17 else
'EXTERNAL BROWSER'}{[Link]}")
print("-" * 65)

# Render dengan Chafa


[Link](f"chafa -c full --symbols block+vhalf --size 100x40 --stretch
{filename}")

print(f"\n{[Link]}{[Link]}[Zoom 1-17: Term | 18-21: G-Maps


| 0: New Target]{[Link]}")
else:
print(f"{[Link]}[!] Uplink Failed. Server rejected request.
{[Link]}")
except Exception as e:
print(f"{[Link]}[!] Error: {e}{[Link]}")

def main():
[Link]('clear')
print(f"{[Link]}{[Link]}")
print(" ╔══════════════════════════════════════════════════╗")
print(" ║ V18: TERMINAL SATELLITE & DEEP SCAN ║")
print(" ║ SUPPORT: IP, DECIMAL, & DMS COORDINATES ║")
print(" ╚══════════════════════════════════════════════════╝{[Link]}")

target_input = input(f"{[Link]}Input IP/Coord (Ex: 8°44'45\"S


115°10'05\"E): {[Link]}").strip()

lat, lon = parse_coordinates(target_input)


tag = "Manual Target"

if lat is not None:


transmit(f"{[Link]}[+] Coordinates Parsed: {lat}, {lon}
{[Link]}")
elif [Link](r"^\d{1,3}(\.\d{1,3}){3}$", target_input):
transmit(f"{[Link]}[*] Tracing IP Location...{[Link]}")
try:
geo = [Link](f"[Link]
fields=status,lat,lon,city,isp").json()
if geo['status'] == 'success':
lat, lon = geo['lat'], geo['lon']
tag = f"{[Link]('isp')} ({[Link]('city')})"
transmit(f"{[Link]}[+] IP Uplink Secure: {tag}
{[Link]}")
else:
print(f"{[Link]}[!] IP Not Found.{[Link]}")
return
except:
print(f"{[Link]}[!] Network Error.{[Link]}")
return
else:
print(f"{[Link]}[!] Format unrecognized! Use IP or Coord
(Decimal/DMS){[Link]}")
[Link](2)
main()
return

while True:
try:
z_input = input(f"{[Link]}{[Link]}ZOOM_LEVEL (1-21) [0 to
Back] > {[Link]}")
if z_input == '0':
main()
break
if not z_input.isdigit():
continue
fetch_and_render(lat, lon, z_input, tag)
except KeyboardInterrupt:
print("\nExiting...")
break

if __name__ == "__main__":
main()
[Link]

import os, subprocess, threading, time, socket, re, requests


import [Link], socketserver
from [Link] import urlparse, parse_qs

# --- KONFIGURASI ---


PORT = 8080
WWW_DIR = [Link]("~/ghost_ultimate")
STREAM_DIR = [Link](WWW_DIR, "stream")
TOR_PROXY = {'http': 'socks5h://[Link]:9050', 'https':
'socks5h://[Link]:9050'}

# State untuk Digital Zoom & Panning


zoom_state = {"scale": 1, "x": 0, "y": 0}
SELECTED_RES = "1280:720" # Default resolution

for d in [WWW_DIR, STREAM_DIR]:


if not [Link](d): [Link](d)

def start_tor_auto():
try:
check_tor = [Link](['pgrep', 'tor'], capture_output=True)
if check_tor.returncode != 0:
print("[*] Menjalankan TOR di background...")
[Link](['tor'], stdout=[Link],
stderr=[Link])
[Link](5)
except: pass

def get_cctv_list(domain):
print(f"[*] Menelusuri daftar kamera di {domain}...")
gallery_url = f"{domain}/cctv"
cameras = []
try:
r = [Link](gallery_url, proxies=TOR_PROXY, timeout=10)
found = [Link](r'monitor=(\d+)', [Link])
unique_ids = sorted(list(set(found)), key=int)

for m_id in unique_ids:


[Link]({
"name": f"Monitor {m_id}",
"url": f"[Link]
mode=jpeg&monitor={m_id}&user=view&pass=K0minfo"
})
except:
manual_ids = [1, 2, 7, 13, 14, 21]
for mid in manual_ids:
[Link]({"name": f"Cam {mid} (Manual Check)", "url":
f"[Link]
mode=jpeg&monitor={mid}&user=view&pass=K0minfo"})
return cameras

def get_wlan_ip():
try:
s = [Link](socket.AF_INET, socket.SOCK_DGRAM)
[Link](("[Link]", 80))
return [Link]()[0]
except: return "[Link]"

def create_ui():
html = """
<!DOCTYPE html>
<html>
<head>
<title>GHOST SELECTOR</title>
<script src="[Link]
<style>
body { background: #000; color: #0f0; font-family: monospace; text-
align: center; }
video { width: 95%; max-width: 900px; border: 1px solid #0f0; margin-
top: 10px; }
.controls { display: grid; grid-template-columns: repeat(3, 1fr); max-
width: 350px; margin: 15px auto; gap: 8px; }
button { background: #000; color: #0f0; border: 1px solid #0f0;
padding: 12px; cursor: pointer; }
button:active { background: #0f0; color: #000; }
</style>
</head>
<body>
<h3>:: GHOST RELAY SELECTOR ::</h3>
<video id="video" autoplay muted playsinline></video>
<div class="controls">
<button onclick="cmd('zoom_in')">ZOOM +</button>
<button onclick="cmd('w')">UP</button>
<button onclick="cmd('zoom_out')">ZOOM -</button>
<button onclick="cmd('a')">LEFT</button>
<button onclick="cmd('s')">DOWN</button>
<button onclick="cmd('d')">RIGHT</button>
<button style="grid-column: span 3" onclick="cmd('reset')">RESET
VIEW</button>
</div>
<script>
var video = [Link]('video');
function start() {
if([Link]()) {
var hls = new Hls();
[Link]('stream/index.m3u8');
[Link](video);
}
}
function cmd(k) { fetch('/control?key=' + k); }
setInterval(() => { if([Link]) start(); }, 3000);
start();
</script>
</body>
</html>
"""
with open([Link](WWW_DIR, "[Link]"), "w") as f: [Link](html)

class GhostServer([Link]):
def do_GET(self):
if "/control" in [Link]:
query = parse_qs(urlparse([Link]).query)
key = [Link]('key', [None])[0]
global zoom_state
if key == 'zoom_in' and zoom_state["scale"] < 6: zoom_state["scale"] +=
1
elif key == 'zoom_out' and zoom_state["scale"] > 1: zoom_state["scale"]
-= 1
elif key == 'w': zoom_state["y"] -= 60
elif key == 's': zoom_state["y"] += 60
elif key == 'a': zoom_state["x"] -= 60
elif key == 'd': zoom_state["x"] += 60
elif key == 'reset': zoom_state = {"scale": 1, "x": 0, "y": 0}
self.send_response(200); self.end_headers()
else: super().do_GET()

def run_ffmpeg(url):
while True:
output = [Link](STREAM_DIR, "index.m3u8")
crop_filter = f"crop=iw/{zoom_state['scale']}:ih/{zoom_state['scale']}:
(iw/2-ow/2)+{zoom_state['x']}:(ih/2-oh/2)+{zoom_state['y']}"

# Menggunakan SELECTED_RES pada filter scale


cmd = [
'proxychains4', '-q', 'ffmpeg', '-y', '-hide_banner', '-loglevel',
'error',
'-i', url, '-vf', f"{crop_filter},scale={SELECTED_RES}",
'-c:v', 'libx264', '-preset', 'ultrafast', '-tune', 'zerolatency',
'-g', '30', '-hls_time', '1', '-hls_list_size', '3',
'-hls_flags', 'delete_segments', output
]
proc = [Link](cmd)
curr = zoom_state.copy()
while [Link]() is None:
if curr != zoom_state:
[Link](); break
[Link](0.5)

if __name__ == "__main__":
[Link]('clear')
start_tor_auto()

print("="*60)
print(" CCTV EVERYWHERE")
print("="*60)

cam_list = get_cctv_list("[Link]

print("\n[!] PILIH KAMERA YANG AKAN DI RE TRANSMIT:")


for i, cam in enumerate(cam_list):
print(f" [{i+1}] {cam['name']}")

try:
pilihan = int(input("\nNomor Kamera > ")) - 1
target_url = cam_list[pilihan]['url']
# --- INPUT RESOLUSI BARU ---
print("\n[?] Format: Lebar:Tinggi (Contoh: 1280:720, 640:480, 1920:1080)")
input_res = input("R-Transmit Res: ").strip()
if input_res:
SELECTED_RES = input_res
# ---------------------------

except:
print("[!] Pilihan salah atau error."); exit()

create_ui()
my_ip = get_wlan_ip()

[Link](WWW_DIR)
[Link](target=[Link](("", PORT),
GhostServer).serve_forever, daemon=True).start()

print(f"\n[🌐] LIVE AT: [Link] (Res: {SELECTED_RES})")


run_ffmpeg(target_url)

V2

import os, subprocess, threading, time, socket, re, requests, shutil, getpass, sys,
signal
import [Link], socketserver, json
from [Link] import urlparse, parse_qs

# --- KONFIGURASI UTAMA ---


PORT = 8080
WWW_DIR = [Link]("~/ghost_ultimate")
STREAM_DIR = [Link](WWW_DIR, "stream")
TOR_PROXY = {'http': 'socks5h://[Link]:9050', 'https':
'socks5h://[Link]:9050'}

# --- STATE GLOBAL ---


zoom_state = {"scale": 1, "x": 0, "y": 0}
current_usage = {"bitrate": "0k", "speed": "0x", "fps": "0"}
OPTIMAL_BITRATE = "600k"
httpd = None # Untuk menyimpan instance server agar bisa dimatikan

def cleanup_and_exit(sig, frame):


"""Logika Tutup Port & Cleanup saat Ctrl + C"""
print("\n\033[91m[!] Menutup System & Menutup Port...")
if httpd:
[Link]()
httpd.server_close()

# Bunuh semua proses ffmpeg yang masih nyangkut


[Link](['pkill', 'ffmpeg'], capture_output=True)

# Bersihkan file sampah stream


if [Link](STREAM_DIR):
[Link](STREAM_DIR)

print("[✓] Port 8080 Closed. Session Ended.\033[0m")


[Link](0)

# Daftarkan signal handler untuk Ctrl + C


[Link]([Link], cleanup_and_exit)

def cool_effects():
[Link]('clear' if [Link] == 'posix' else 'cls')
print("\033[92m[*] Initializing Ghost System...")
[Link](1)
print(f"[*] Getting IP Address...")
[Link](1.5)
print(f"[*] Local Node: {[Link]([Link]())}")
print("[*] Tor Circuit: Established")
print("-" * 35)
user = input("Username: ")
pw = [Link]("Password: ")
print("\033[92m[+] Access Granted. Loading Dashboard...\033[0m")
[Link](1)

def start_tor_auto():
try:
check_tor = [Link](['pgrep', 'tor'], capture_output=True)
if check_tor.returncode != 0:
[Link](['tor'], stdout=[Link],
stderr=[Link])
[Link](3)
except: pass

def get_cctv_list(domain):
print(f"[*] Scrapping CCTV Database (Full Scan)...")
try:
r = [Link](f"{domain}/cctv", proxies=TOR_PROXY, timeout=10)
found = [Link](r'monitor=(\d+)', [Link])
unique_ids = sorted(list(set(found)), key=int)
if unique_ids:
return [{"name": f"Monitor {m_id}", "url":
f"[Link]
mode=jpeg&monitor={m_id}&user=view&pass=K0minfo"} for m_id in unique_ids]
except: pass
return [{"name": f"Cam {mid}", "url": f"[Link]
bin/nph-zms?mode=jpeg&monitor={mid}&user=view&pass=K0minfo"} for mid in [1, 2, 7,
13, 14, 20, 25]]

def create_ui():
html = """
<!DOCTYPE html>
<html>
<head>
<title>GHOST V6 ULTIMATE</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="[Link]
<style>
body { background: #000; color: #0f0; font-family: 'Consolas',
monospace; text-align: center; margin: 0; -webkit-user-select: none; }
.header { padding: 10px; border-bottom: 1px solid #0f0; background:
#050505; font-size: 14px; }
.video-container { position: relative; width: 98%; max-width: 900px;
margin: 10px auto; border: 1px solid #0f0; background: #000; }
.overlay { position: absolute; top: 0; left: 0; width: 100%; height:
100%; z-index: 10; cursor: default; }
video { width: 100%; display: block; filter: contrast(1.1); }
.controls { display: grid; grid-template-columns: repeat(3, 1fr); max-
width: 400px; margin: 15px auto; gap: 8px; padding: 10px; }
button { background: #000; color: #0f0; border: 1px solid #0f0;
padding: 15px; cursor: pointer; font-size: 12px; font-weight: bold; }
button:active { background: #0f0; color: #000; }
.stats { font-size: 11px; color: #aff; padding-bottom: 5px; }
</style>
</head>
<body oncontextmenu="return false;">
<div class="header">GHOST V6 GOLD | <span id="sync">SYNCING
1.0x</span></div>
<div class="video-container">
<div class="overlay"></div>
<video id="video" autoplay muted playsinline></video>
</div>
<div class="stats">SPD: <span id="speed">0x</span> | FPS: <span
id="fps">0</span> | <span id="bitrate">0</span></div>
<div class="controls">
<button onclick="cmd('zoom_in')">ZOOM +</button>
<button onclick="cmd('w')">UP</button>
<button onclick="cmd('zoom_out')">ZOOM -</button>
<button onclick="cmd('a')">LEFT</button>
<button onclick="cmd('s')">DOWN</button>
<button onclick="cmd('d')">RIGHT</button>
<button onclick="togglePiP()" style="grid-column: span 3; color: #0af;
border-color: #0af;">ENTER PiP MODE</button>
<button onclick="cmd('reset')" style="grid-column: span 3; color: #f00;
border-color: #f00;">RESET VIEW</button>
</div>
<script>
var video = [Link]('video');
function init() {
if([Link]()) {
var hls = new Hls({ lowLatencyMode: true, liveSyncDuration: 1.5
});
[Link]('stream/index.m3u8');
[Link](video);
}
}
function cmd(k) { fetch('/control?key=' + k); }
async function togglePiP() {
if (video !== [Link]) await
[Link]();
else await [Link]();
}
setInterval(() => {
fetch('/stats').then(r => [Link]()).then(d => {
[Link]('speed').innerText = [Link];
[Link]('fps').innerText = [Link];
[Link]('bitrate').innerText = [Link];
});
}, 2000);
init();
</script>
</body>
</html>
"""
with open([Link](WWW_DIR, "[Link]"), "w", encoding="utf-8") as f:
[Link](html)

class GhostServer([Link]):
def end_headers(self):
self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate,
max-age=0')
super().end_headers()
def do_GET(self):
if "/control" in [Link]:
query = parse_qs(urlparse([Link]).query)
key = [Link]('key', [None])[0]
global zoom_state
if key == 'zoom_in': zoom_state["scale"] = min(zoom_state["scale"] + 1,
6)
elif key == 'zoom_out': zoom_state["scale"] = max(zoom_state["scale"] -
1, 1)
elif key == 'w': zoom_state["y"] -= 60
elif key == 's': zoom_state["y"] += 60
elif key == 'a': zoom_state["x"] -= 60
elif key == 'd': zoom_state["x"] += 60
elif key == 'reset': zoom_state = {"scale": 1, "x": 0, "y": 0}
self.send_response(200); self.end_headers()
elif "/stats" in [Link]:
self.send_response(200); self.send_header('Content-Type',
'application/json'); self.end_headers()
[Link]([Link](current_usage).encode())
else: super().do_GET()

def run_ffmpeg(url):
global current_usage
while True:
output = [Link](STREAM_DIR, "index.m3u8")
crop = f"crop=iw/{zoom_state['scale']}:ih/{zoom_state['scale']}:(iw/2-ow/
2)+{zoom_state['x']}:(ih/2-oh/2)+{zoom_state['y']}"

cmd = [
'proxychains4', '-q', 'ffmpeg', '-y',
'-use_wallclock_as_timestamps', '1',
'-fflags', '+genpts+nobuffer',
'-re', # FORCE 1.0x SPEED
'-i', url,
'-vf', f"{crop},fps=20",
'-c:v', 'libx264', '-preset', 'ultrafast', '-tune', 'zerolatency',
'-b:v', OPTIMAL_BITRATE, '-maxrate', OPTIMAL_BITRATE, '-bufsize',
'300k',
'-g', '40', '-f', 'hls', '-hls_time', '2', '-hls_list_size', '3',
'-hls_flags', 'delete_segments', output
]

proc = [Link](cmd, stdout=[Link],


stderr=[Link], universal_newlines=True)
curr_state = zoom_state.copy()
try:
for line in [Link]:
if "bitrate=" in line:
s = [Link](r'speed=\s*([\d.x]+)', line)
f = [Link](r'fps=\s*([\d.]+)', line)
if s: current_usage["speed"] = [Link](1)
if f: current_usage["fps"] = [Link](1)
current_usage["bitrate"] = OPTIMAL_BITRATE
if curr_state != zoom_state:
[Link]()
break
except: break
[Link](1)

if __name__ == "__main__":
cool_effects()
start_tor_auto()

cam_list = get_cctv_list("[Link]
for i, cam in enumerate(cam_list): print(f" [{i+1}] {cam['name']}")

try:
pilihan = int(input("\nNomor Kamera > ")) - 1
target_url = cam_list[pilihan]['url']
except: exit()

create_ui()
[Link](WWW_DIR)

# Simpan instance server ke variabel global httpd


httpd = [Link](("", PORT), GhostServer)
[Link](target=httpd.serve_forever, daemon=True).start()

print(f"\n[🌐] SYSTEM READY: [Link]


print("[!] PRESS CTRL+C TO SHUTDOWN CLEANLY")

run_ffmpeg(target_url)

[Link]

import os
import sys
import time
import random
import json # [NEW] Modul untuk menyimpan ingatan
import subprocess
from collections import Counter
from joblib import Parallel, delayed
from mpmath import mp
from colorama import Fore, Back, Style, init

# ==========================================
# SYSTEM CONFIGURATION & INITIALIZATION
# ==========================================
init(autoreset=True)
[Link] = 100

class SystemConfig:
VERSION = "7.5.0-NSP" # Updated Version
CODENAME = "GOD_EYE_PERMANENT"
CPU_CORES = 8
MC_TRIALS = 1024
TTS_SPEED = "1.3"
MEMORY_FILE = "nsp_core.json" # File penyimpanan otak

# ==========================================
# DATABASE MODULE (WITH NSP)
# ==========================================
class DisasterDatabase:
def __init__(self):
[Link] = [
"General / Unknown", "Apple Brook", "Arch Park", "Coastal Quickrun",
"Devastation Station", "Factory Frenzy", "Fort Indestructible",
"Furious Station", "Glass Office", "Green Hill", "Happy Home",
"Heights School", "Launch Land", "Lucky Mart", "Manic Mansion",
"Party Palace", "Prison Panic", "Rakish Refinery", "Roblox HQ",
"Sky Tower", "Sunny Ranch", "Surf Central", "Trailer Park"
]

[Link] = {
"Acid Rain": {
"weather": "Cloudy", "rating": "LOW",
"visual": "Langit berubah hijau kekuningan.",
"warning": "ACID RAIN DETECTED. DO NOT TOUCH GRASS.",
"strat": "Cari atap beton. Struktur kayu aman tapi bisa
berlubang.",
"map_strat": {"Factory Frenzy": "Masuk lantai dasar pabrik."}
},
"Avalanche": {
"weather": "Sunny", "rating": "HIGH",
"visual": "Gunung Salju muncul di horizon.",
"warning": "AVALANCHE IMMINENT. SEEK COVER.",
"strat": "Sembunyi di sisi bangunan yang membelakangi gunung.",
"map_strat": {"Sky Tower": "TURUN KE LANTAI DASAR SEGERA!"}
},
"Blizzard": {
"weather": "Foggy", "rating": "MEDIUM",
"visual": "Kabut putih tebal, visibility 0%.",
"warning": "EXTREME COLD. FIND SHELTER.",
"strat": "Masuk ruangan tertutup total. Jauhi pintu.",
"map_strat": {"Fort Indestructible": "Masuk sel penjara."}
},
"Deadly Virus": {
"weather": "Sunny", "rating": "MEDIUM",
"visual": "Partikel virus hijau pada player.",
"warning": "BIOHAZARD. MAINTAIN DISTANCE.",
"strat": "Isolasi diri di tempat tinggi/terpencil.",
"map_strat": {"Roblox HQ": "Naik tangga darurat ke atap."}
},
"Earthquake": {
"weather": "Sunny", "rating": "EXTREME",
"visual": "Screen shake intens.",
"warning": "SEISMIC ACTIVITY. EVACUATE BUILDINGS.",
"strat": "Keluar ke tanah lapang. Jauhi struktur tinggi.",
"map_strat": {"Glass Office": "GEDUNG AKAN RUNTUH TOTAL. LARI!"}
},
"Fire": {
"weather": "Sunny", "rating": "MEDIUM",
"visual": "Asap hitam dan api kecil.",
"warning": "INFERNO DETECTED. AVOID FLAMES.",
"strat": "Cari area batu/pasir/aspal.",
"map_strat": {"Green Hill": "Lari ke pantai pasir."}
},
"Flash Flood": {
"weather": "Cloudy", "rating": "HIGH",
"visual": "Air laut pasang dengan cepat.",
"warning": "RISING WATER LEVELS. CLIMB UP.",
"strat": "Naik ke objek tertinggi yang statis.",
"map_strat": {"Launch Land": "Naik ke puncak Roket."}
},
"Meteor Shower": {
"weather": "Sunny", "rating": "HIGH",
"visual": "Bayangan bulat di tanah.",
"warning": "IMPACT INBOUND. KEEP MOVING.",
"strat": "Zig-zag pattern. Jangan diam di satu titik.",
"map_strat": {"Arch Park": "Sembunyi di bawah Arch batu."}
},
"Sandstorm": {
"weather": "Foggy", "rating": "MEDIUM",
"visual": "Kabut kuning, angin kencang.",
"warning": "HIGH WINDS. DODGE DEBRIS.",
"strat": "Berlindung di balik tembok tebal.",
"map_strat": {"Furious Station": "Masuk terowongan rel."}
},
"Thunderstorm": {
"weather": "Cloudy", "rating": "HIGH",
"visual": "Awan hitam pekat, suara guruh.",
"warning": "LIGHTNING STRIKES. STAY LOW.",
"strat": "Jangan jadi objek tertinggi. Tiarap/turun.",
"map_strat": {"Sky Tower": "JANGAN DI ATAP/TANGGA LUAR."}
},
"Tornado": {
"weather": "Cloudy", "rating": "EXTREME",
"visual": "Corong angin berputar.",
"warning": "TORNADO DETECTED. RUN AWAY.",
"strat": "Lari memutar berlawanan arah tornado.",
"map_strat": {"Prison Panic": "Lari ke lapangan basket."}
},
"Tsunami": {
"weather": "Sunny", "rating": "EXTREME",
"visual": "Air laut surut drastis.",
"warning": "TSUNAMI WAVE. HIGH GROUND.",
"strat": "Naik gedung tinggi/bukit. Jauhi kaca.",
"map_strat": {"Glass Office": "CARI BALON HIJAU! GEDUNG MATI."}
},
"Volcanic Eruption": {
"weather": "Sunny", "rating": "HIGH",
"visual": "Gunung api tumbuh dari tanah.",
"warning": "VOLCANIC ACTIVITY. DODGE LAVA.",
"strat": "Jauhi kawah. Perhatikan bayangan lava.",
"map_strat": {"Party Palace": "Tetap di jalan setapak luar."}
}
}

# Initialize Default Weights


[Link] = {k: 1.0 for k in [Link]}
self.saved_stats = {"total": 0, "hits": 0} # Default Stats

# [NSP] Load Memory on Init


self.load_neural_memory()

# [NSP] FEATURE: LOAD MEMORY


def load_neural_memory(self):
if [Link](SystemConfig.MEMORY_FILE):
try:
with open(SystemConfig.MEMORY_FILE, 'r') as f:
memory = [Link](f)
[Link] = [Link]("weights", [Link])
self.saved_stats = [Link]("stats", self.saved_stats)
print([Link] + f"[NSP] Neural Pattern Loaded from
{SystemConfig.MEMORY_FILE}")
[Link](0.5)
except Exception as e:
print([Link] + f"[NSP] Memory Corrupted. Creating new brain.
Error: {e}")
else:
print([Link] + "[NSP] No Previous Memory. Initializing Fresh
Neural Network.")

# [NSP] FEATURE: SAVE MEMORY


def save_neural_memory(self, current_stats):
memory = {
"weights": [Link],
"stats": current_stats
}
try:
with open(SystemConfig.MEMORY_FILE, 'w') as f:
[Link](memory, f, indent=4)
print([Link] + f"\n[NSP] Neural State Saved to
{SystemConfig.MEMORY_FILE}")
print([Link] + "[NSP] Patterns Persisted for next session.")
except Exception as e:
print([Link] + f"\n[NSP] FAILED TO SAVE MEMORY: {e}")

def get_eligible(self, weather_code):


eligible = []
for name, info in [Link]():
w = info['weather']
if weather_code == 'S' and w == 'Sunny': [Link](name)
elif weather_code == 'C' and (w == 'Cloudy' or w == 'Foggy'):
[Link](name)
return eligible

def update_weights(self, actual_disaster):


if actual_disaster in [Link]:
# Algorithm: Aggressive Suppression
[Link][actual_disaster] = 0.15
for k in [Link]:
if k != actual_disaster:
[Link][k] += 0.35
if [Link][k] > 6.0: [Link][k] = 6.0

# ==========================================
# ENGINE MODULE
# ==========================================
class MonteCarloEngine:
def __init__(self, db):
[Link] = db

def _simulate_pane(self, eligible, trials):


results = []
secure_rng = [Link]()
pool = []

# Weighted Pool Generation


for disaster in eligible:
w = [Link](disaster, 1.0)
tickets = int(w * 15)
if tickets < 1: tickets = 1
[Link]([disaster] * tickets)

if not pool: return Counter()

for _ in range(trials):
[Link](secure_rng.choice(pool))
return Counter(results)

def run(self, eligible):


print([Link] + f"[SYSTEM] Spawning {SystemConfig.CPU_CORES} Process
Threads...")

results = Parallel(n_jobs=SystemConfig.CPU_CORES)(
delayed(self._simulate_pane)(eligible, SystemConfig.MC_TRIALS)
for _ in range(SystemConfig.CPU_CORES)
)

final_tally = Counter()
for r in results: final_tally.update(r)
return final_tally

# ==========================================
# INTERFACE & AUDIO MODULE
# ==========================================
class UserInterface:
def __init__(self, initial_stats):
[Link] = []
# Load stats from NSP memory instead of 0
[Link] = initial_stats
def boot_sequence(self):
[Link]('clear') # Gunakan 'cls' jika di Windows
steps = [
"Initializing Kernel...",
"Loading Disaster Database...",
"Connecting to Joblib Parallel Backend...",
"Restoring Neural Save Program (NSP)...",
"System Ready."
]
for step in steps:
print([Link] + f"[BOOT] {step}")
[Link](0.15)
[Link](0.5)

def speak(self, text):


try:
[Link](
["termux-tts-speak", "-r", SystemConfig.TTS_SPEED, text],
check=False, stderr=[Link]
)
except: pass

def draw_dashboard(self, current_map):


[Link]('clear')
# Header
print([Link] + "╔" + "═"*58 + "╗")
print([Link] + "║" + [Link] + [Link] + f" PROJECT OMEGA: NSP
ENABLED v{[Link]}".center(58) + [Link] + "║")
print([Link] + "╠" + "═"*58 + "╣")

# Stats Row
acc = 0.0
if [Link]['total'] > 0:
acc = ([Link]['hits'] / [Link]['total']) * 100

stat_str = f" ACCURACY: {acc:.1f}% | ROUNDS: {[Link]['total']} | MAP:


{current_map}"
print([Link] + "║" + [Link] + stat_str.center(58) + [Link] +
"║")
print([Link] + "╚" + "═"*58 + "╝")

# History
if [Link]:
print([Link] + "\n[ RECENT LOGS ]")
for i, h in enumerate([Link][-3:]):
status = f"{[Link]}HIT " if h['hit'] else f"{[Link]}MISS"
print(f" #{i+1} | {h['map'][:10]}... | {h['pred']} -> {h['actual']}
[{status}{[Link]}]")

def show_results(self, top3, total_sims, current_map, db_ref):


print([Link] + "\n" + "═"*60)

# RANK 1 DETAILED
r1_name = top3[0][0]
r1_prob = (top3[0][1] / total_sims) * 100
r1_info = db_ref.data[r1_name]

print(f"{[Link]}PRIMARY PREDICTION: {[Link] + [Link]}


{r1_name.upper()}")
print(f"{[Link]}PROBABILITY: {[Link]}{r1_prob:.2f}% {[Link]}|
RATING: {[Link]}{r1_info['rating']}")
print([Link] + "-"*60)
print(f"{[Link]}VISUAL : {[Link]}{r1_info['visual']}")
print(f"{[Link]}WARNING: {[Link] + [Link]}
{r1_info['warning']}")

strat = r1_info.get('map_strat', {}).get(current_map, r1_info['strat'])


print(f"{[Link]}TACTICS: {[Link]}{strat}")
print([Link] + "═"*60)

if len(top3) > 1:
r2_name = top3[1][0]
r2_prob = (top3[1][1] / total_sims) * 100
print(f"{[Link]}SECONDARY : {r2_name} ({r2_prob:.1f}%)")
if len(top3) > 2:
r3_name = top3[2][0]
r3_prob = (top3[2][1] / total_sims) * 100
print(f"{[Link]}TERTIARY : {r3_name} ({r3_prob:.1f}%)")

return strat

# ==========================================
# MAIN APPLICATION LOOP
# ==========================================
def main():
# Initialize Modules
db = DisasterDatabase()
# Pass saved stats to UI so accuracy persists
ui = UserInterface(db.saved_stats)
engine = MonteCarloEngine(db)

ui.boot_sequence()
current_map = "General / Unknown"

try:
while True:
ui.draw_dashboard(current_map)

# --- INPUT FASE ---


print([Link] + "\n[ SYSTEM INPUT ]")
cmd = input(f"{[Link]}Command (S/C/Map/Exit): ").strip().upper()

if cmd == 'EXIT':
print("Initiating NSP Backup Sequence...")
db.save_neural_memory([Link]) # SAVE ON EXIT
[Link](1)
print("Shutting down OMEGA system...")
[Link]()

elif cmd == 'MAP':


print("\n" + [Link] + "--- AVAILABLE MAPS ---")
for i, m in enumerate([Link]):
print(f"[{i:02d}] {m}")
try:
idx = int(input("Select Map ID: "))
current_map = [Link][idx]
except: pass
continue

elif cmd in ['S', 'C']:


# --- PROCESSING FASE ---
eligible = db.get_eligible(cmd)
if not eligible:
print([Link] + "Invalid Weather Configuration.")
[Link](1)
continue

# Run Simulation
tally = [Link](eligible)
top3 = tally.most_common(3)
total_sims = SystemConfig.CPU_CORES * SystemConfig.MC_TRIALS

# Show Results
tactics = ui.show_results(top3, total_sims, current_map, db)

# Audio Output
tts_msg = f"Alert. {top3[0][0]} Incoming. {tactics}"
Parallel(n_jobs=1)(delayed([Link])(tts_msg) for _ in range(1))

# --- FEEDBACK FASE ---


print([Link] + "\n[ REALITY CHECK ]")
print("Confirm Actual Disaster:")
valid_opts = []
for i, (name, _) in enumerate(top3):
print(f"[{i}] {name}")
valid_opts.append(name)
print("[X] Miss/Other")

val = input("Input: ").upper()

hit = False
actual = "Unknown"

try:
if [Link]() and int(val) < 3:
actual = valid_opts[int(val)]
hit = True
db.update_weights(actual)
# Auto-Save after every correction (Optional but safer)
# db.save_neural_memory([Link])
else:
hit = False
except: pass

# Update Stats
[Link]['total'] += 1
if hit: [Link]['hits'] += 1

[Link]({
"map": current_map,
"pred": top3[0][0],
"actual": actual,
"hit": hit
})

print([Link] + "System calibrated. Reloading..." if hit else


[Link] + "Calibration adjusted.")
[Link](1.5)

except KeyboardInterrupt:
# [NSP] Save on CTRL+C Force Close
print([Link] + "\n\n[!] EMERGENCY INTERRUPTION DETECTED")
print([Link] + "[NSP] Attempting Emergency Memory Dump...")
db.save_neural_memory([Link])
[Link]()

if __name__ == "__main__":
main()

You might also like