#!/usr/bin/env python3

# ============================================================
#   TELEGRAM BOT ENKRIPSI KODE v3.0
#   Author  : IKYY x CLAUDE
#   Version : 3.0 (Encoding Fix)
# ============================================================

import os, re, sys, json, time, random, string, hashlib, base64
import zipfile, logging, tempfile, shutil, requests
from pathlib import Path
from datetime import datetime

MAX_FILE_SIZE = 20 * 1024 * 1024
CONFIG_FILE   = os.path.expanduser("~/.enkripsi_bot_config.json")
USERS_FILE    = os.path.expanduser("~/.enkripsi_bot_users.json")
OFFSET_FILE   = os.path.expanduser("~/.enkripsi_bot_offset")

def load_or_ask_config():
    if os.path.exists(CONFIG_FILE):
        with open(CONFIG_FILE, 'r') as f:
            config = json.load(f)
        print(f"\n✅ Config ditemukan! Token: {config['token'][:20]}...")
        return config['token'], config['chat_id']
    print("\n" + "="*50)
    print("  SETUP BOT ENKRIPSI — IKYY x CLAUDE")
    print("="*50)
    token   = input("  Bot Token : ").strip()
    chat_id = input("  Chat ID   : ").strip()
    with open(CONFIG_FILE, 'w') as f:
        json.dump({'token': token, 'chat_id': chat_id}, f)
    os.chmod(CONFIG_FILE, 0o600)
    return token, chat_id

BOT_TOKEN, ALLOWED_CHAT_ID = load_or_ask_config()

LOG_FILE_PATH = os.path.expanduser("~/.enkripsi_bot.log")
logging.basicConfig(level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[logging.FileHandler(LOG_FILE_PATH), logging.StreamHandler()])
log = logging.getLogger(__name__)

# ============================================================
# MULTI-USER
# ============================================================
def load_users():
    if os.path.exists(USERS_FILE):
        try:
            with open(USERS_FILE, 'r') as f:
                return json.load(f)
        except: return {"allowed": [], "blocked": []}
    return {"allowed": [], "blocked": []}

def save_users(users):
    with open(USERS_FILE, 'w') as f:
        json.dump(users, f, indent=2)

def is_allowed(chat_id):
    if str(chat_id) == str(ALLOWED_CHAT_ID): return True
    if is_blocked(chat_id): return False
    users = load_users()
    return str(chat_id) in [str(u) for u in users.get("allowed", [])]

def is_blocked(chat_id):
    users = load_users()
    return str(chat_id) in [str(u) for u in users.get("blocked", [])]

def add_user(chat_id):
    users = load_users()
    if str(chat_id) not in [str(u) for u in users["allowed"]]:
        users["allowed"].append(str(chat_id))
        save_users(users)
        return True
    return False

def remove_user(chat_id):
    users = load_users()
    users["allowed"] = [u for u in users["allowed"] if str(u) != str(chat_id)]
    save_users(users)

def block_user(chat_id):
    users = load_users()
    users["allowed"] = [u for u in users.get("allowed", []) if str(u) != str(chat_id)]
    if str(chat_id) not in [str(u) for u in users.get("blocked", [])]:
        users.setdefault("blocked", []).append(str(chat_id))
        save_users(users)
        return True
    save_users(users)
    return False

def get_users():
    users = load_users()
    return users.get("allowed", []), users.get("blocked", [])

# ============================================================
# TELEGRAM API
# ============================================================
API_URL = f"https://api.telegram.org/bot{BOT_TOKEN}"

def send_message(chat_id, text, parse_mode="HTML"):
    try:
        requests.post(f"{API_URL}/sendMessage",
            data={"chat_id": chat_id, "text": text, "parse_mode": parse_mode}, timeout=30)
    except Exception as e:
        log.error(f"Send message error: {e}")

def send_document(chat_id, file_path, caption=""):
    try:
        if not os.path.exists(file_path):
            log.error(f"File tidak ditemukan: {file_path}")
            send_message(chat_id, "❌ Gagal kirim file — file tidak ditemukan.")
            return
        with open(file_path, 'rb') as f:
            requests.post(f"{API_URL}/sendDocument",
                data={"chat_id": chat_id, "caption": caption},
                files={"document": f}, timeout=60)
    except Exception as e:
        log.error(f"Send document error: {e}")

def send_typing(chat_id):
    try:
        requests.post(f"{API_URL}/sendChatAction",
            data={"chat_id": chat_id, "action": "typing"}, timeout=10)
    except: pass

def download_file(file_id, dest_path):
    try:
        r = requests.get(f"{API_URL}/getFile?file_id={file_id}", timeout=30)
        if r.status_code != 200: return False
        result = r.json().get('result')
        if not result: return False
        file_url = f"https://api.telegram.org/file/bot{BOT_TOKEN}/{result['file_path']}"
        r2 = requests.get(file_url, timeout=60)
        if r2.status_code != 200: return False
        with open(dest_path, 'wb') as f:
            f.write(r2.content)
        return True
    except Exception as e:
        log.error(f"Download error: {e}")
        return False

# ============================================================
# ENKRIPSI ENGINE
# ============================================================
def random_var(length=8):
    return ''.join(random.choices(string.ascii_letters, k=length))

def generate_xor_key(length=16):
    return ''.join(random.choices(string.ascii_letters + string.digits, k=length))

# BUG 1 FIX: xor_encrypt handle Unicode dengan benar
def xor_encrypt_bytes(data_bytes: bytes, key: str) -> bytes:
    key_bytes = key.encode('utf-8')
    return bytes([b ^ key_bytes[i % len(key_bytes)] for i, b in enumerate(data_bytes)])

# ============================================================
# PHP OBFUSCATOR
# ============================================================
def remove_php_comments(code):
    result = []
    i = 0
    in_single = False
    in_double = False
    while i < len(code):
        c = code[i]
        if c == "'" and not in_double:
            in_single = not in_single; result.append(c); i += 1
        elif c == '"' and not in_single:
            in_double = not in_double; result.append(c); i += 1
        elif not in_single and not in_double:
            if code[i:i+2] == '//':
                while i < len(code) and code[i] != '\n': i += 1
                result.append('\n')
            elif code[i:i+2] == '/*':
                while i < len(code) and code[i:i+2] != '*/': i += 1
                i += 2
            elif c == '#':
                while i < len(code) and code[i] != '\n': i += 1
                result.append('\n')
            else:
                result.append(c); i += 1
        else:
            result.append(c); i += 1
    return ''.join(result)

def obfuscate_php(code):
    try:
        code = remove_php_comments(code)
        inner = re.sub(r'<\?php', '', code)
        inner = re.sub(r'\?>', '', inner).strip()
        # BUG 2 FIX: cek PHP kosong
        if not inner.strip():
            return "<?php // Empty file ?>"
        encoded = base64.b64encode(inner.encode('utf-8')).decode()
        v1, v2, v3, v4, v5 = [random_var() for _ in range(5)]
        return f"""<?php
${v1}=base64_decode('{base64.b64encode(b"base64_decode").decode()}');
${v2}=base64_decode('{base64.b64encode(b"str_rot13").decode()}');
${v3}=str_rot13(base64_decode('{base64.b64encode(base64.b64encode(encoded.encode()).decode().encode()).decode()}'));
${v4}=${v1}(${v3});
${v5}=base64_decode('{base64.b64encode(b"eval").decode()}');
${v5}(${v4});
?>"""
    except Exception as e:
        return f"ERROR: {e}"

def obfuscate_php_strong(code):
    try:
        code = remove_php_comments(code)
        inner = re.sub(r'<\?php', '', code)
        inner = re.sub(r'\?>', '', inner).strip()
        # BUG 2 FIX: cek PHP kosong
        if not inner.strip():
            return "<?php // Empty file ?>"
        # BUG 1 FIX: encode ke bytes dulu sebelum XOR
        xor_key = generate_xor_key(16)
        inner_bytes = inner.encode('utf-8')
        xor_encrypted = xor_encrypt_bytes(inner_bytes, xor_key)
        b64_1 = base64.b64encode(xor_encrypted).decode()
        b64_2 = base64.b64encode(b64_1.encode()).decode()
        b64_key = base64.b64encode(xor_key.encode()).decode()
        v1, v2, v3, v4, v5, v6, v7 = [random_var() for _ in range(7)]
        return f"""<?php
${v1}=base64_decode(base64_decode('{b64_2}'));
${v2}=base64_decode('{b64_key}');
${v3}='';
${v6}=strlen(${v2});
for(${v4}=0;${v4}<strlen(${v1});${v4}++){{
${v3}.=chr(ord(${v1}[${v4}])^ord(${v2}[${v4}%${v6}]));}}
${v5}=base64_decode('{base64.b64encode(b"eval").decode()}');
${v5}(${v3});
?>"""
    except Exception as e:
        return f"ERROR: {e}"

# ============================================================
# JS OBFUSCATOR
# ============================================================
def remove_js_comments(code):
    result = []
    i = 0
    in_single = False
    in_double = False
    in_template = False
    while i < len(code):
        c = code[i]
        if c == "'" and not in_double and not in_template:
            in_single = not in_single; result.append(c); i += 1
        elif c == '"' and not in_single and not in_template:
            in_double = not in_double; result.append(c); i += 1
        elif c == '`' and not in_single and not in_double:
            in_template = not in_template; result.append(c); i += 1
        elif not in_single and not in_double and not in_template:
            if code[i:i+2] == '//':
                while i < len(code) and code[i] != '\n': i += 1
                result.append('\n')
            elif code[i:i+2] == '/*':
                while i < len(code) and code[i:i+2] != '*/': i += 1
                i += 2
            else:
                result.append(c); i += 1
        else:
            result.append(c); i += 1
    return ''.join(result)

def obfuscate_js(code):
    try:
        code = remove_js_comments(code)
        b64 = base64.b64encode(code.encode('utf-8')).decode()
        v1, v2, v3 = [random_var() for _ in range(3)]
        return f"(function(){{{v1}='{b64}';{v2}=atob||function(s){{return Buffer.from(s,'base64').toString()}};{v3}=eval;{v3}({v2}({v1}))}})();"
    except Exception as e:
        return f"ERROR: {e}"

def obfuscate_js_strong(code):
    try:
        code = remove_js_comments(code)
        # BUG 1 FIX: encode ke bytes dulu
        xor_key = generate_xor_key(16)
        code_bytes = code.encode('utf-8')
        xor_encrypted = xor_encrypt_bytes(code_bytes, xor_key)
        b64_data = base64.b64encode(xor_encrypted).decode()
        b64_key  = base64.b64encode(xor_key.encode()).decode()
        b64_data2 = base64.b64encode(b64_data.encode()).decode()
        v1, v2, v3, v4, v5 = [random_var() for _ in range(5)]
        return f"""(function(){{
var {v1}=new Uint8Array(Array.from(atob(atob('{b64_data2}')),c=>c.charCodeAt(0)));
var {v2}=atob('{b64_key}');
var {v3}='';
for(var {v4}=0;{v4}<{v1}.length;{v4}++){{
{v3}+=String.fromCharCode({v1}[{v4}]^{v2}.charCodeAt({v4}%{v2}.length));}}
var {v5}=eval;{v5}({v3});
}})();"""
    except Exception as e:
        return f"ERROR: {e}"

# ============================================================
# HTML, CSS, PYTHON, SQL
# ============================================================
def obfuscate_html(code):
    try:
        code = re.sub(r'<!--.*?-->', '', code, flags=re.DOTALL)
        code = re.sub(r'\s+', ' ', code)
        code = re.sub(r'> <', '><', code)
        return code.strip()
    except Exception as e:
        return f"ERROR: {e}"

# BUG 5 FIX: obfuscate_css tidak merusak URL
def obfuscate_css(code):
    try:
        code = re.sub(r'/\*.*?\*/', '', code, flags=re.DOTALL)
        # Hapus whitespace berlebih tapi jaga string di dalam quotes
        code = re.sub(r'\s+', ' ', code)
        code = re.sub(r'\s*{\s*', '{', code)
        code = re.sub(r'\s*}\s*', '}', code)
        code = re.sub(r'\s*;\s*', ';', code)
        code = re.sub(r'\s*,\s*', ',', code)
        # BUG 5 FIX: Tidak collapse spasi di sekitar ':' karena bisa merusak URL
        # Hanya hapus spasi di selector (sebelum '{'), bukan di dalam nilai property
        return code.strip()
    except Exception as e:
        return f"ERROR: {e}"

def obfuscate_python(code):
    try:
        code = re.sub(r'(?m)^[ \t]*#.*$', '', code)
        encoded = base64.b64encode(code.encode('utf-8')).decode()
        v1, v2 = random_var(), random_var()
        return f"import base64\n{v1}='{encoded}'\n{v2}=base64.b64decode({v1}).decode()\nexec({v2})"
    except Exception as e:
        return f"ERROR: {e}"

def minify_sql(code):
    try:
        code = re.sub(r'--.*?\n', '\n', code)
        code = re.sub(r'/\*.*?\*/', '', code, flags=re.DOTALL)
        code = re.sub(r'\s+', ' ', code)
        return code.strip()
    except Exception as e:
        return f"ERROR: {e}"

def minify_php(code):
    code = remove_php_comments(code)
    code = re.sub(r'\s+', ' ', code)
    return code.strip()

def minify_js(code):
    code = remove_js_comments(code)
    code = re.sub(r'\s+', ' ', code)
    return code.strip()

def minify_html(code):
    code = re.sub(r'<!--.*?-->', '', code, flags=re.DOTALL)
    code = re.sub(r'\s+', ' ', code)
    code = re.sub(r'> <', '><', code)
    return code.strip()

def remove_comments(code):
    code = remove_php_comments(code)
    code = re.sub(r'<!--.*?-->', '', code, flags=re.DOTALL)
    code = re.sub(r'--.*?\n', '\n', code)
    return code.strip()

def encode_base64(text):  return base64.b64encode(text.encode()).decode()
def decode_base64(text):
    try: return base64.b64decode(text.encode()).decode()
    except: return "ERROR: Bukan format Base64 yang valid!"
def decode_url(text):
    try:
        from urllib.parse import unquote
        return unquote(text)
    except Exception as e: return f"ERROR: {e}"
def generate_md5(text):    return hashlib.md5(text.encode()).hexdigest()
def generate_sha256(text): return hashlib.sha256(text.encode()).hexdigest()
def generate_random_key(length=32):
    return ''.join(random.choices(string.ascii_letters + string.digits + "!@#$%^&*", k=length))
def count_lines(code): return len(code.strip().split('\n'))

def detect_language(code):
    code_lower = code.lower()
    if '<?php' in code_lower or '$_post' in code_lower: return 'PHP'
    elif 'function' in code_lower and ('var ' in code_lower or 'const ' in code_lower or 'let ' in code_lower): return 'JavaScript'
    elif '<html' in code_lower or '<!doctype' in code_lower: return 'HTML'
    elif '{' in code_lower and ':' in code_lower and ';' in code_lower: return 'CSS'
    elif 'def ' in code_lower or 'import ' in code_lower: return 'Python'
    elif 'select ' in code_lower or 'insert ' in code_lower: return 'SQL'
    return 'Unknown'

def process_file_by_extension(content, ext, strong=False):
    ext = ext.lower()
    if ext == 'php':   return (obfuscate_php_strong(content) if strong else obfuscate_php(content)), 'php'
    elif ext == 'js':  return (obfuscate_js_strong(content) if strong else obfuscate_js(content)), 'js'
    elif ext in ['html', 'htm']: return obfuscate_html(content), 'html'
    elif ext == 'css': return obfuscate_css(content), 'css'
    elif ext == 'py':  return obfuscate_python(content), 'py'
    elif ext == 'sql': return minify_sql(content), 'sql'
    return content, ext

# BUG 3 FIX: Tambah UnicodeEncodeError ke exception list
def process_zip(input_path, output_path, strong=False):
    processed = 0
    skipped = 0
    supported_ext = ['php', 'js', 'html', 'htm', 'css', 'py', 'sql']
    with zipfile.ZipFile(input_path, 'r') as zin:
        with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zout:
            for item in zin.infolist():
                data = zin.read(item.name)
                ext = item.name.rsplit('.', 1)[-1].lower() if '.' in item.name else ''
                if ext in supported_ext:
                    try:
                        content = data.decode('utf-8')
                        encoded, _ = process_file_by_extension(content, ext, strong)
                        zout.writestr(item, encoded.encode('utf-8'))
                        processed += 1
                    except (UnicodeDecodeError, UnicodeEncodeError, ValueError, AttributeError) as e:
                        log.warning(f"Skip {item.name}: {e}")
                        zout.writestr(item, data)
                        skipped += 1
                else:
                    zout.writestr(item, data)
                    skipped += 1
    return processed, skipped

# ============================================================
# HONEYPOT ENGINE
# ============================================================

def detect_form_info(html_content):
    """Deteksi info form dari HTML — tipe, ID, method, AJAX atau tidak"""
    info = {
        'form_ids': [],
        'is_ajax': False,
        'ajax_type': None,
        'has_honeypot': False,
        'honeypot_field': None,
        'honeypot_position': None,
        'honeypot_correct': False,
    }

    # Deteksi semua ID form
    form_ids = re.findall(r'<form[^>]*id=["\']([^"\']+)["\']', html_content, re.IGNORECASE)
    info['form_ids'] = form_ids

    # Deteksi AJAX
    if re.search(r'\.serialize\(\)', html_content): 
        info['is_ajax'] = True
        info['ajax_type'] = 'jquery_serialize'
    elif re.search(r'new FormData', html_content):
        info['is_ajax'] = True
        info['ajax_type'] = 'formdata'
    elif re.search(r'fetch\(', html_content):
        info['is_ajax'] = True
        info['ajax_type'] = 'fetch'
    elif re.search(r'\$\.ajax|\.ajax\(', html_content):
        info['is_ajax'] = True
        info['ajax_type'] = 'jquery_ajax'

    # Deteksi honeypot yang sudah ada
    honeypot_patterns = [
        r'name=["\']website["\']',
        r'name=["\']url["\']',
        r'name=["\']_gotcha["\']',
        r'name=["\']email_confirm["\']',
        r'<!--\s*[Hh]oneypot\s*-->',
    ]
    for pattern in honeypot_patterns:
        match = re.search(pattern, html_content)
        if match:
            info['has_honeypot'] = True
            info['honeypot_field'] = match.group(0)
            # Cek apakah posisinya benar (di dalam form, sebelum button)
            pos = match.start()
            form_start = html_content.rfind('<form', 0, pos)
            form_end = html_content.find('</form>', pos)
            button_pos = html_content.rfind('<button', 0, pos)
            if form_start != -1 and form_end != -1:
                info['honeypot_position'] = 'inside_form'
                # Cek ada display:none atau style="display:none"
                nearby = html_content[max(0, pos-50):pos+200]
                if re.search(r'display:\s*none|tabindex=["\']?-1', nearby):
                    info['honeypot_correct'] = True
            break

    return info

def detect_php_info(php_content):
    """Deteksi info dari file PHP"""
    info = {
        'has_honeypot_check': False,
        'has_antispam': False,
        'framework': 'php',
    }

    if re.search(r'\$_POST\[["\']website["\']\]|\$_POST\[["\']url["\']\]', php_content):
        info['has_honeypot_check'] = True
    if re.search(r'anti_spam\.php|AntiSpam|honeypot', php_content, re.IGNORECASE):
        info['has_antispam'] = True
    if re.search(r'Laravel|Illuminate|artisan', php_content):
        info['framework'] = 'laravel'
    elif re.search(r'CodeIgniter|CI_Controller', php_content):
        info['framework'] = 'codeigniter'

    return info

def inject_honeypot_html(html_content, field_name='website'):
    """Inject honeypot ke dalam HTML form secara otomatis"""
    honeypot_html = f'''
    <!-- Honeypot — IKYY x CLAUDE Anti Spam -->
    <input type="text" name="{field_name}" 
      style="display:none !important; visibility:hidden; position:absolute; left:-9999px;" 
      tabindex="-1" 
      autocomplete="off"
      aria-hidden="true">'''

    # Cek apakah sudah ada honeypot
    if re.search(rf'name=["\']website["\']|name=["\']_gotcha["\']', html_content):
        return None, "already_exists"

    # Cari posisi terbaik — sebelum button submit di dalam form
    # Strategy 1: sebelum </form>
    button_match = re.search(r'(<button[^>]*type=["\']submit["\'][^>]*>|<input[^>]*type=["\']submit["\'][^>]*>)', html_content, re.IGNORECASE)
    if button_match:
        pos = button_match.start()
        result = html_content[:pos] + honeypot_html + '\n    ' + html_content[pos:]
        return result, "injected_before_button"

    # Strategy 2: sebelum </form>
    form_end = html_content.rfind('</form>')
    if form_end != -1:
        result = html_content[:form_end] + honeypot_html + '\n  ' + html_content[form_end:]
        return result, "injected_before_form_end"

    return None, "no_form_found"

def inject_honeypot_php(php_content, field_name='website', framework='php'):
    """Inject validasi honeypot ke PHP file"""

    if framework == 'php':
        honeypot_check = f'''<?php
// === Anti Spam Honeypot — IKYY x CLAUDE ===
if (!empty($_POST['{field_name}'])) {{
    http_response_code(429);
    echo json_encode(['status' => 'error', 'message' => 'Bot detected']);
    exit;
}}
// Atau jika sudah pakai anti_spam.php, cukup:
// require_once './anti_spam.php';
// ==========================================
'''
        # Inject setelah <?php opening tag
        result = re.sub(r'<\?php\s*\n?', honeypot_check, php_content, count=1)
        return result

    elif framework == 'laravel':
        return f'''// Tambahkan di Controller atau Request:
if ($request->filled('{field_name}')) {{
    abort(429, 'Bot detected');
}}

// Atau di FormRequest rules():
'{field_name}' => 'size:0',
'''

    elif framework == 'codeigniter':
        return f'''// Tambahkan di Controller:
if ($this->input->post('{field_name}')) {{
    show_error('Bot detected', 429);
}}
'''

def scan_file_security(content, filename):
    """Scan file untuk backdoor, malware, dan vulnerability"""
    issues = []
    ext = filename.rsplit('.', 1)[-1].lower() if '.' in filename else ''
    lines = content.split('\n')

    # === BACKDOOR PATTERNS ===
    backdoor_patterns = [
        # PHP backdoor
        (r'eval\s*\(\s*base64_decode\s*\(', 'CRITICAL', 'PHP Backdoor: eval(base64_decode()) — teknik classic webshell'),
        (r'eval\s*\(\s*\$_(GET|POST|REQUEST|COOKIE)', 'CRITICAL', 'PHP Backdoor: eval() dari user input langsung'),
        (r'system\s*\(\s*\$_(GET|POST|REQUEST|COOKIE)', 'CRITICAL', 'PHP Backdoor: system() dari user input — RCE vulnerability'),
        (r'shell_exec\s*\(\s*\$_(GET|POST|REQUEST|COOKIE)', 'CRITICAL', 'PHP Backdoor: shell_exec() dari user input — RCE vulnerability'),
        (r'passthru\s*\(\s*\$_(GET|POST|REQUEST|COOKIE)', 'CRITICAL', 'PHP Backdoor: passthru() dari user input — RCE vulnerability'),
        (r'exec\s*\(\s*\$_(GET|POST|REQUEST|COOKIE)', 'CRITICAL', 'PHP Backdoor: exec() dari user input — RCE vulnerability'),
        (r'preg_replace\s*\([^,]*\/e["\'\s]', 'CRITICAL', 'PHP Backdoor: preg_replace /e flag — code execution'),
        (r'assert\s*\(\s*\$_(GET|POST|REQUEST|COOKIE)', 'CRITICAL', 'PHP Backdoor: assert() dari user input'),
        (r'\$\{?\$_(GET|POST|REQUEST|COOKIE)\b', 'CRITICAL', 'PHP Backdoor: variable variable dari user input'),

        # Obfuscation mencurigakan (bukan dari bot enkripsi kita)
        (r'str_rot13\s*\(\s*base64_decode\s*\(\s*str_rot13', 'HIGH', 'Obfuscasi mencurigakan: multi-layer rot13+base64'),
        (r'gzinflate\s*\(\s*base64_decode', 'HIGH', 'Obfuscasi mencurigakan: gzinflate+base64 — sering dipakai malware'),
        (r'str_replace\s*\(.{0,20}base64_decode', 'HIGH', 'Obfuscasi mencurigakan: str_replace+base64'),

        # Webshell signature
        (r'FilesMan|c99shell|r57shell|WSO\s*Shell|b374k', 'CRITICAL', 'Webshell terdeteksi — hapus segera!'),
        (r'uname\s*-a|/etc/passwd|/proc/version', 'HIGH', 'Command sensitif Linux — kemungkinan backdoor'),

        # SQL Injection
        (r'\$_(GET|POST|REQUEST)\[.{0,30}\]\s*(?:\.|\s)*(?:WHERE|UNION|SELECT|INSERT|UPDATE|DELETE)', 'HIGH', 'SQL Injection: user input langsung masuk ke query tanpa sanitasi'),
        (r'mysql_query\s*\(\s*["\'].*\$_(GET|POST)', 'HIGH', 'SQL Injection: query dengan user input tanpa prepared statement'),

        # XSS
        (r'echo\s+\$_(GET|POST|REQUEST|COOKIE)', 'MEDIUM', 'XSS: echo user input langsung tanpa escape — reflected XSS'),
        (r'print\s+\$_(GET|POST|REQUEST|COOKIE)', 'MEDIUM', 'XSS: print user input langsung tanpa escape'),
        (r'innerHTML\s*=\s*location\.(search|hash|href)', 'MEDIUM', 'XSS: innerHTML dari URL — DOM XSS vulnerability'),
        (r'document\.write\s*\(\s*(?:location|document\.URL)', 'MEDIUM', 'XSS: document.write dari URL — DOM XSS vulnerability'),

        # File inclusion
        (r'(?:include|require)(?:_once)?\s*\(\s*\$_(GET|POST|REQUEST)', 'CRITICAL', 'LFI/RFI: file inclusion dari user input — sangat berbahaya'),

        # Info disclosure
        (r'phpinfo\s*\(\s*\)', 'MEDIUM', 'Info disclosure: phpinfo() — hapus dari production'),
        (r'var_dump\s*\(\s*\$_(GET|POST|SESSION|SERVER)', 'LOW', 'Info disclosure: var_dump user data — jangan di production'),

        # Suspicious encoded
        (r'\\x[0-9a-fA-F]{2}\\x[0-9a-fA-F]{2}\\x[0-9a-fA-F]{2}\\x[0-9a-fA-F]{2}\\x[0-9a-fA-F]{2}', 'HIGH', 'Kode hex encoding mencurigakan — kemungkinan obfuscasi malware'),
        (r'chr\(\d+\)\.chr\(\d+\)\.chr\(\d+\)\.chr\(\d+\)', 'HIGH', 'chr() chaining mencurigakan — teknik obfuscasi malware'),

        # Suspicious JS
        (r'document\[.{0,20}\]\[.{0,20}\]\(atob\(', 'HIGH', 'JS Backdoor: dynamic method call dengan atob — mencurigakan'),
        (r'eval\(atob\(atob\(', 'HIGH', 'JS Backdoor: eval(atob(atob())) — double encoding mencurigakan'),
        (r'String\.fromCharCode\(\d+,\d+,\d+,\d+,\d+,\d+,\d+,\d+,\d+,\d+', 'HIGH', 'JS Obfuscasi: String.fromCharCode panjang — kemungkinan malware'),
    ]

    for i, line in enumerate(lines, 1):
        for pattern, severity, description in backdoor_patterns:
            if re.search(pattern, line, re.IGNORECASE):
                # Skip kalau ini file hasil enkripsi bot kita sendiri
                if 'IKYY x CLAUDE' in content[:500]:
                    continue
                issues.append({
                    'line': i,
                    'severity': severity,
                    'description': description,
                    'code': line.strip()[:100]
                })

    return issues

def format_scan_report(issues, filename):
    """Format laporan scan untuk dikirim ke Telegram"""
    if not issues:
        return f"✅ <b>SCAN BERSIH!</b>\n\nFile <code>{filename}</code> tidak ditemukan ancaman keamanan.\n\n🛡 Aman untuk diupload ke hosting."

    # Group by severity
    critical = [i for i in issues if i['severity'] == 'CRITICAL']
    high     = [i for i in issues if i['severity'] == 'HIGH']
    medium   = [i for i in issues if i['severity'] == 'MEDIUM']
    low      = [i for i in issues if i['severity'] == 'LOW']

    msg = f"🚨 <b>HASIL SCAN KEAMANAN</b>\n"
    msg += f"📄 File: <code>{filename}</code>\n\n"
    msg += f"📊 Temuan: {len(issues)} masalah\n"

    if critical: msg += f"🔴 CRITICAL : {len(critical)}\n"
    if high:     msg += f"🟠 HIGH     : {len(high)}\n"
    if medium:   msg += f"🟡 MEDIUM   : {len(medium)}\n"
    if low:      msg += f"🟢 LOW      : {len(low)}\n"

    msg += "\n"

    all_issues = critical + high + medium + low
    for issue in all_issues[:10]:  # Max 10 tampil
        emoji = {'CRITICAL': '🔴', 'HIGH': '🟠', 'MEDIUM': '🟡', 'LOW': '🟢'}.get(issue['severity'], '⚪')
        msg += f"{emoji} <b>Line {issue['line']}</b> — {issue['description']}\n"
        msg += f"   <code>{issue['code']}</code>\n\n"

    if len(all_issues) > 10:
        msg += f"... dan {len(all_issues) - 10} temuan lainnya\n\n"

    if critical:
        msg += "⚠️ <b>JANGAN UPLOAD ke hosting sebelum diperbaiki!</b>"
    else:
        msg += "💡 Perbaiki temuan di atas sebelum upload ke production."

    return msg

# ============================================================
# STATE
# ============================================================
user_state = {}

def set_state(chat_id, state, data=None):
    user_state[str(chat_id)] = {'state': state, 'data': data or {}}

def get_state(chat_id):
    return user_state.get(str(chat_id), {})

def clear_state(chat_id):
    user_state.pop(str(chat_id), None)

def load_offset():
    try:
        if os.path.exists(OFFSET_FILE):
            with open(OFFSET_FILE, 'r') as f:
                return int(f.read().strip())
    except: pass
    return 0

def save_offset(offset):
    try:
        with open(OFFSET_FILE, 'w') as f:
            f.write(str(offset))
    except Exception as e:
        log.error(f"Gagal simpan offset: {e}")

# ============================================================
# HANDLE MESSAGE
# ============================================================
def handle_message(message):
    chat_id  = str(message.get('chat', {}).get('id', ''))
    text     = message.get('text', '')
    document = message.get('document')

    if is_blocked(chat_id):
        send_message(chat_id, "❌ Akses lo diblokir oleh admin."); return

    if not is_allowed(chat_id):
        send_message(chat_id, f"❌ Akses ditolak! Bot ini private.\n\nChat ID lo: <code>{chat_id}</code>")
        send_message(ALLOWED_CHAT_ID, f"⚠️ Ada yang coba akses!\nChat ID: <code>{chat_id}</code>\n\nGunakan /add {chat_id} untuk berikan akses.")
        return

    send_typing(chat_id)
    state    = get_state(chat_id)
    is_admin = str(chat_id) == str(ALLOWED_CHAT_ID)

    # ---- HANDLE FILE ----
    if document:
        file_name = document.get('file_name', 'file.txt')
        file_size = document.get('file_size', 0)
        file_id   = document.get('file_id')
        ext = file_name.rsplit('.', 1)[-1].lower() if '.' in file_name else ''
        strong = state.get('data', {}).get('strong', False)
        current_mode = state.get('data', {}).get('mode', 'encode')

        if file_size > MAX_FILE_SIZE:
            send_message(chat_id, "❌ File terlalu besar! Maksimal 20MB."); return

        mode_label = {
            'encode': '⏳ Mengenkripsi',
            'honeypot_inject': '🍯 Menginjeksi honeypot ke',
            'honeypot_check': '🔍 Mengecek honeypot di',
            'scan': '🔍 Scanning keamanan',
        }.get(current_mode, '⏳ Memproses')

        send_message(chat_id, f"{mode_label} <b>{file_name}</b>...")

        tmpdir = tempfile.mkdtemp()
        try:
            input_path = os.path.join(tmpdir, file_name)
            if not download_file(file_id, input_path):
                send_message(chat_id, "❌ Gagal download file!"); return

            if ext == 'zip':
                if current_mode == 'scan':
                    # Scan semua file dalam ZIP
                    try:
                        all_issues = []
                        with zipfile.ZipFile(input_path, 'r') as zin:
                            for item in zin.infolist():
                                item_ext = item.name.rsplit('.', 1)[-1].lower() if '.' in item.name else ''
                                if item_ext in ['php', 'js', 'html', 'htm']:
                                    try:
                                        content = zin.read(item.name).decode('utf-8', errors='ignore')
                                        issues = scan_file_security(content, item.name)
                                        all_issues.extend(issues)
                                    except: pass

                        if not all_issues:
                            send_message(chat_id, f"✅ <b>SCAN ZIP BERSIH!</b>\n\n📦 <code>{file_name}</code>\nTidak ditemukan ancaman keamanan. Aman diupload!")
                        else:
                            critical = len([i for i in all_issues if i['severity'] == 'CRITICAL'])
                            high     = len([i for i in all_issues if i['severity'] == 'HIGH'])
                            medium   = len([i for i in all_issues if i['severity'] == 'MEDIUM'])
                            low      = len([i for i in all_issues if i['severity'] == 'LOW'])
                            msg = f"🚨 <b>HASIL SCAN ZIP</b>\n📦 <code>{file_name}</code>\n\n"
                            msg += f"📊 Total: {len(all_issues)} masalah\n"
                            if critical: msg += f"🔴 CRITICAL: {critical}\n"
                            if high:     msg += f"🟠 HIGH    : {high}\n"
                            if medium:   msg += f"🟡 MEDIUM  : {medium}\n"
                            if low:      msg += f"🟢 LOW     : {low}\n\n"
                            for issue in all_issues[:10]:
                                emoji = {'CRITICAL':'🔴','HIGH':'🟠','MEDIUM':'🟡','LOW':'🟢'}.get(issue['severity'],'⚪')
                                msg += f"{emoji} <b>{issue['file'] if 'file' in issue else 'Line ' + str(issue['line'])}</b>\n"
                                msg += f"   {issue['description']}\n\n"
                            if len(all_issues) > 10:
                                msg += f"... dan {len(all_issues)-10} temuan lainnya\n\n"
                            if critical:
                                msg += "⚠️ <b>JANGAN UPLOAD sebelum diperbaiki!</b>"
                            send_message(chat_id, msg)
                    except Exception as e:
                        send_message(chat_id, f"❌ Error scan ZIP: {e}")
                else:
                    suffix = '_strong_encoded.zip' if strong else '_encoded.zip'
                    output_name = file_name.replace('.zip', suffix)
                    output_path = os.path.join(tmpdir, output_name)
                    processed, skipped = process_zip(input_path, output_path, strong)
                    label = "🔐 STRONG" if strong else "🔒 STANDARD"
                    send_document(chat_id, output_path,
                        f"✅ ZIP diproses! {label}\n📁 {processed} file dienkripsi\n⏭ {skipped} file diskip")

            elif ext in ['php', 'js', 'html', 'htm', 'css', 'py', 'sql']:
                try:
                    with open(input_path, 'r', encoding='utf-8', errors='ignore') as f:
                        content = f.read()

                    if current_mode == 'honeypot_inject':
                        if ext in ['html', 'htm']:
                            result, status = inject_honeypot_html(content)
                            if status == 'already_exists':
                                send_message(chat_id, "ℹ️ <b>Honeypot sudah ada!</b>\nGunakan /honeypot_check untuk verifikasi.")
                            elif status == 'no_form_found':
                                send_message(chat_id, "❌ Tidak ada <code>&lt;form&gt;</code> di file ini.")
                            else:
                                output_name = file_name.replace(f'.{ext}', f'_honeypot.{ext}')
                                output_path = os.path.join(tmpdir, output_name)
                                with open(output_path, 'w', encoding='utf-8') as f:
                                    f.write(result)
                                info = detect_form_info(content)
                                ajax_note = ""
                                if info['is_ajax'] and info['ajax_type'] == 'jquery_serialize':
                                    ajax_note = "\n\n✅ Pakai <code>.serialize()</code> — honeypot otomatis ikut!"
                                elif info['is_ajax']:
                                    ajax_note = "\n\n⚠️ Pakai AJAX — pastikan honeypot field ikut di request."
                                send_document(chat_id, output_path,
                                    f"✅ <b>Honeypot diinjeksi!</b>{ajax_note}\n\n💡 Tambahkan di PHP:\n<code>require_once './anti_spam.php';</code>")
                        elif ext == 'php':
                            info = detect_php_info(content)
                            if info['has_honeypot_check']:
                                send_message(chat_id, "ℹ️ <b>Validasi honeypot sudah ada</b> di file ini!")
                            else:
                                result = inject_honeypot_php(content, framework=info['framework'])
                                output_name = file_name.replace('.php', '_honeypot.php')
                                output_path = os.path.join(tmpdir, output_name)
                                with open(output_path, 'w', encoding='utf-8') as f:
                                    f.write(result)
                                send_document(chat_id, output_path,
                                    f"✅ <b>Validasi honeypot diinjeksi ke PHP!</b>")
                        else:
                            send_message(chat_id, f"❌ Honeypot hanya support .html dan .php")

                    elif current_mode == 'honeypot_check':
                        if ext in ['html', 'htm']:
                            info = detect_form_info(content)
                            msg = f"🔍 <b>HONEYPOT CHECK</b>\n📄 <code>{file_name}</code>\n\n"
                            if info['form_ids']:
                                msg += f"📋 Form: {', '.join([f'<code>#{fid}</code>' for fid in info['form_ids']])}\n"
                            msg += f"⚡ Tipe: {'AJAX (' + info['ajax_type'] + ')' if info['is_ajax'] else 'Form POST biasa'}\n\n"
                            if not info['has_honeypot']:
                                msg += "❌ <b>Honeypot TIDAK ditemukan!</b>\n\n💡 Kirim file dengan /honeypot untuk inject otomatis."
                            elif info['honeypot_correct']:
                                msg += "✅ <b>Honeypot ADA dan BENAR!</b>\n"
                                if info['is_ajax'] and info['ajax_type'] == 'jquery_serialize':
                                    msg += "\n✅ .serialize() — honeypot otomatis ikut terkirim!"
                            else:
                                msg += "⚠️ <b>Honeypot ADA tapi kurang lengkap!</b>\n"
                                msg += "Pastikan ada: <code>style=\"display:none\"</code> dan <code>tabindex=\"-1\"</code>"
                            send_message(chat_id, msg)
                        elif ext == 'php':
                            info = detect_php_info(content)
                            msg = f"🔍 <b>HONEYPOT CHECK PHP</b>\n📄 <code>{file_name}</code>\n\n"
                            msg += "✅ Validasi honeypot ADA\n" if info['has_honeypot_check'] else "❌ Validasi honeypot TIDAK ADA\n"
                            msg += "✅ anti_spam.php sudah di-include\n" if info['has_antispam'] else "⚠️ anti_spam.php belum di-include\n"
                            if not info['has_antispam']:
                                msg += "\n💡 Tambahkan:\n<code>require_once './anti_spam.php';</code>"
                            send_message(chat_id, msg)
                        else:
                            send_message(chat_id, "❌ Honeypot check hanya support .html dan .php")

                    elif current_mode == 'scan':
                        issues = scan_file_security(content, file_name)
                        report = format_scan_report(issues, file_name)
                        send_message(chat_id, report)

                    else:
                        # Mode encode normal
                        encoded, out_ext = process_file_by_extension(content, ext, strong)
                        suffix = f'_strong_encoded.{out_ext}' if strong else f'_encoded.{out_ext}'
                        output_name = file_name.replace(f'.{ext}', suffix)
                        output_path = os.path.join(tmpdir, output_name)
                        with open(output_path, 'w', encoding='utf-8') as f:
                            f.write(encoded)
                        label = "🔐 STRONG" if strong else "🔒 STANDARD"
                        size_before = len(content)
                        size_after  = len(encoded)
                        reduction   = round((1 - size_after/size_before) * 100, 1) if size_before > 0 else 0
                        send_document(chat_id, output_path,
                            f"✅ <b>{file_name}</b> dienkripsi! {label}\n📊 {count_lines(content)} baris\n📦 {size_before}→{size_after} bytes ({reduction}%)")

                except Exception as e:
                    send_message(chat_id, f"❌ Error: {e}")
            else:
                send_message(chat_id, f"❌ Format .{ext} tidak didukung!\nSupport: .php .js .html .css .py .sql .zip")
        finally:
            # BUG 4 FIX: Cleanup setelah upload selesai
            try: shutil.rmtree(tmpdir)
            except: pass

        clear_state(chat_id)
        return

    # ---- HANDLE STATE ----
    if state.get('state') == 'waiting_file' and text and not text.startswith('/'):
        send_message(chat_id, "📎 Kirim file dulu ya, bukan teks.\nKetik /cancel untuk batalkan.")
        return

    if state.get('state') and state.get('state') != 'waiting_file' and text and not text.startswith('/'):
        current_state = state['state']
        result = None
        label  = ""

        state_map = {
            'encode_php':        (obfuscate_php,       "PHP Obfuscated 🔒"),
            'encode_php_strong': (obfuscate_php_strong,"PHP STRONG 🔐"),
            'encode_js':         (obfuscate_js,        "JS Obfuscated 🔒"),
            'encode_js_strong':  (obfuscate_js_strong, "JS STRONG 🔐"),
            'encode_html':       (obfuscate_html,      "HTML Minified"),
            'encode_css':        (obfuscate_css,       "CSS Minified"),
            'encode_python':     (obfuscate_python,    "Python Obfuscated"),
            'encode_sql':        (minify_sql,          "SQL Minified"),
            'minify_php':        (minify_php,          "PHP Minified"),
            'minify_js':         (minify_js,           "JS Minified"),
            'minify_css':        (obfuscate_css,       "CSS Minified"),
            'minify_html':       (minify_html,         "HTML Minified"),
            'remove_comment':    (remove_comments,     "Komentar Dihapus"),
            'base64_encode':     (encode_base64,       "Base64 Encoded"),
            'base64_decode':     (decode_base64,       "Base64 Decoded"),
            'decode_url':        (decode_url,          "URL Decoded"),
            'md5':               (generate_md5,        "MD5 Hash"),
            'sha256':            (generate_sha256,     "SHA256 Hash"),
        }

        if current_state == 'count_lines':
            lines = count_lines(text)
            lang  = detect_language(text)
            send_message(chat_id, f"📊 <b>Info Kode:</b>\n\n📝 Baris: <code>{lines}</code>\n💻 Bahasa: <code>{lang}</code>")
        elif current_state == 'detect_lang':
            send_message(chat_id, f"💻 <b>Bahasa:</b> <code>{detect_language(text)}</code>")
        elif current_state in state_map:
            func, label = state_map[current_state]
            result = func(text)

        if result:
            if len(result) <= 4000:
                send_message(chat_id, f"✅ <b>{label}:</b>\n\n<code>{result}</code>")
            else:
                tmp = tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False, encoding='utf-8')
                tmp.write(result); tmp_path = tmp.name; tmp.close()
                send_document(chat_id, tmp_path, f"✅ {label}")
                try: os.unlink(tmp_path)
                except: pass

        clear_state(chat_id)
        return

    # ---- HANDLE COMMANDS ----
    if not text: return
    cmd  = text.split()[0].lower().split('@')[0]
    args = text.split()[1:] if len(text.split()) > 1 else []

    if cmd == '/start':
        send_message(chat_id, f"🔐 <b>IKYY x CLAUDE — Bot Enkripsi v3.0</b>\n\nKetik /help untuk semua command.")

    elif cmd == '/help':
        send_message(chat_id, """📋 <b>SEMUA COMMAND</b>

🔒 <b>Standard:</b>
/encode_php /encode_js /encode_html /encode_css /encode_python /encode_sql

🔐 <b>Strong (VPS recommended):</b>
/encode_php_strong /encode_js_strong

🔧 <b>Minify:</b>
/minify_php /minify_js /minify_css /minify_html /remove_comment

🛠 <b>Tools:</b>
/base64_encode /base64_decode /decode_url
/md5 /sha256 /random_key /count_lines /detect_lang

🍯 <b>Honeypot:</b>
/honeypot — inject honeypot ke file HTML/PHP
/honeypot_check — cek honeypot sudah benar atau belum

🔍 <b>Security Scan:</b>
/scan — scan file untuk backdoor/malware/vulnerability

📁 Kirim file langsung → auto enkripsi
📦 Kirim ZIP → enkripsi/scan semua isi""" + ("""

👥 <b>Admin:</b>
/add /remove /block /users""" if is_admin else ""))

    elif cmd == '/random_key':
        send_message(chat_id, f"🔑 <b>RANDOM KEY</b>\n\n16: <code>{generate_random_key(16)}</code>\n32: <code>{generate_random_key(32)}</code>\n64: <code>{generate_random_key(64)}</code>")

    elif cmd == '/add' and is_admin:
        if not args: send_message(chat_id, "❌ Format: /add [chat_id]")
        elif add_user(args[0]):
            send_message(chat_id, f"✅ User <code>{args[0]}</code> ditambahkan!")
            send_message(args[0], "✅ Akses diberikan! Ketik /help.")
        else: send_message(chat_id, f"ℹ User <code>{args[0]}</code> sudah punya akses.")

    elif cmd == '/remove' and is_admin:
        if not args: send_message(chat_id, "❌ Format: /remove [chat_id]")
        else:
            remove_user(args[0])
            send_message(chat_id, f"✅ Akses <code>{args[0]}</code> dicabut!")

    elif cmd == '/block' and is_admin:
        if not args: send_message(chat_id, "❌ Format: /block [chat_id]")
        elif block_user(args[0]):
            send_message(chat_id, f"🚫 User <code>{args[0]}</code> diblokir!")
        else: send_message(chat_id, f"ℹ Sudah diblokir.")

    elif cmd == '/users' and is_admin:
        allowed, blocked = get_users()
        msg = "👥 <b>USERS</b>\n\n✅ Diizinkan:\n"
        msg += "\n".join([f"  • <code>{u}</code>" for u in allowed]) or "  (kosong)"
        msg += "\n\n🚫 Diblokir:\n"
        msg += "\n".join([f"  • <code>{u}</code>" for u in blocked]) or "  (kosong)"
        send_message(chat_id, msg)

    elif cmd in ['/add', '/remove', '/block', '/users'] and not is_admin:
        send_message(chat_id, "❌ Hanya admin!")

    elif cmd == '/honeypot':
        set_state(chat_id, 'waiting_file', {'mode': 'honeypot_inject'})
        send_message(chat_id, """🍯 <b>HONEYPOT INJECT</b>

Kirim file HTML atau PHP lo sekarang.

Bot akan otomatis:
📄 <b>HTML</b> → inject input honeypot di dalam form
🐘 <b>PHP</b> → inject validasi honeypot di awal file

✅ Support form biasa & AJAX jQuery .serialize()
✅ Deteksi posisi form otomatis
✅ Kirim balik file yang sudah siap pakai""")

    elif cmd == '/honeypot_check':
        set_state(chat_id, 'waiting_file', {'mode': 'honeypot_check'})
        send_message(chat_id, """🔍 <b>HONEYPOT CHECK</b>

Kirim file HTML atau PHP lo sekarang.

Bot akan cek:
✅ Apakah honeypot sudah ada
✅ Apakah posisinya benar (di dalam form)
✅ Apakah attribute lengkap (display:none, tabindex=-1)
✅ Apakah PHP sudah validasi honeypot
✅ Apakah anti_spam.php sudah di-include""")

    elif cmd == '/scan':
        set_state(chat_id, 'waiting_file', {'mode': 'scan'})
        send_message(chat_id, """🔍 <b>SECURITY SCAN</b>

Kirim file PHP/JS/HTML atau ZIP lo sekarang.

Bot akan scan untuk:
🔴 Backdoor & webshell
🔴 RCE (Remote Code Execution)
🟠 SQL Injection vulnerability
🟠 XSS vulnerability
🟠 File inclusion vulnerability
🟡 Info disclosure
🟡 Kode obfuscasi mencurigakan
🟢 Debug code di production

Max file: 20MB per file
ZIP: semua file di dalam akan discan""")

    elif cmd == '/cancel':
        clear_state(chat_id)
        send_message(chat_id, "✅ Mode dibatalkan. Ketik /help untuk melihat command.")

    else:
        cmd_state_map = {
            '/encode_php': 'encode_php', '/encode_php_strong': 'encode_php_strong',
            '/encode_js': 'encode_js', '/encode_js_strong': 'encode_js_strong',
            '/encode_html': 'encode_html', '/encode_css': 'encode_css',
            '/encode_python': 'encode_python', '/encode_sql': 'encode_sql',
            '/minify_php': 'minify_php', '/minify_js': 'minify_js',
            '/minify_css': 'minify_css', '/minify_html': 'minify_html',
            '/remove_comment': 'remove_comment', '/base64_encode': 'base64_encode',
            '/base64_decode': 'base64_decode', '/decode_url': 'decode_url',
            '/md5': 'md5', '/sha256': 'sha256',
            '/count_lines': 'count_lines', '/detect_lang': 'detect_lang',
        }
        prompt_map = {
            '/encode_php': '📝 Kirim kode PHP:', '/encode_php_strong': '📝 Kirim kode PHP (STRONG):',
            '/encode_js': '📝 Kirim kode JS:', '/encode_js_strong': '📝 Kirim kode JS (STRONG):',
            '/encode_html': '📝 Kirim kode HTML:', '/encode_css': '📝 Kirim kode CSS:',
            '/encode_python': '📝 Kirim kode Python:', '/encode_sql': '📝 Kirim query SQL:',
            '/minify_php': '📝 Kirim kode PHP:', '/minify_js': '📝 Kirim kode JS:',
            '/minify_css': '📝 Kirim kode CSS:', '/minify_html': '📝 Kirim kode HTML:',
            '/remove_comment': '📝 Kirim kode:', '/base64_encode': '📝 Kirim teks:',
            '/base64_decode': '📝 Kirim Base64:', '/decode_url': '📝 Kirim URL:',
            '/md5': '📝 Kirim teks:', '/sha256': '📝 Kirim teks:',
            '/count_lines': '📝 Kirim kode:', '/detect_lang': '📝 Kirim kode:',
        }
        if cmd in cmd_state_map:
            set_state(chat_id, cmd_state_map[cmd])
            send_message(chat_id, prompt_map[cmd])
        elif text.startswith('/'):
            send_message(chat_id, "❓ Command tidak dikenal. Ketik /help")

# ============================================================
# MAIN
# ============================================================
def main():
    log.info("=== Bot Enkripsi IKYY x CLAUDE v3.0 dimulai ===")
    offset = load_offset()
    log.info(f"Offset: {offset}")

    while True:
        try:
            r = requests.get(f"{API_URL}/getUpdates",
                params={"offset": offset, "timeout": 30}, timeout=35)
            if r.status_code != 200:
                time.sleep(5); continue
            for update in r.json().get('result', []):
                offset = update['update_id'] + 1
                save_offset(offset)
                message = update.get('message', {})
                if message:
                    try: handle_message(message)
                    except Exception as e: log.error(f"Handle error: {e}")
        except requests.exceptions.Timeout:
            continue
        except Exception as e:
            log.error(f"Loop error: {e}")
            time.sleep(5)

if __name__ == '__main__':
    main()
