<?php
/**
 * 🛠️ THE TRIANGLE SYNC BOT (V17.0 - EDITOR EDITION)
 * --------------------------------------------------
 * Platforms: Telegram | Bale | Eitaa (Eitaayar)
 * New Feature: LIVE EDIT (Add/Update channels in existing connections)
 * * HOW TO USE EDIT:
 * 1. Click '📋 List' to see Group IDs.
 * 2. Click '✏️ Edit'.
 * 3. Enter Group ID.
 * 4. Choose Platform (Eitaa, Telegram, Bale).
 * 5. Send new Channel ID.
 */

ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', __DIR__ . '/error_log.txt');
ini_set('max_execution_time', 300);
ini_set('memory_limit', '512M');

// ====================================================
// 1. CONFIGURATION
// ====================================================

define('TG_TOKEN', '8009685219:AAHQgFP6g9U-r498ZFDvUfDDfV4ibV_BhPI'); // توکن ربات تلگرام
define('BALE_TOKEN', '1328108468:hixrUhgTUq82uuwgMW2IGICvCpwHemVg4lk');   // توکن ربات بل
define('EITAA_TOKEN', 'bot447343:86241830-e299-4186-a8c7-d0b0b3544a4e');
define('ADMIN_IDS', [
    'telegram' => [239053159],
    'bale'     => [1589915724]
]);

define('TG_PROXY', ''); 
define('DB_FILE', __DIR__ . '/database.sqlite');

// ====================================================
// 2. DATABASE LAYER
// ====================================================

class DB {
    private $pdo;
    public function __construct() {
        if (!file_exists(DB_FILE)) { touch(DB_FILE); chmod(DB_FILE, 0777); }
        $this->pdo = new PDO("sqlite:" . DB_FILE);
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $this->init();
    }

    private function init() {
        $this->pdo->exec("CREATE TABLE IF NOT EXISTS channels (
            id INTEGER PRIMARY KEY AUTOINCREMENT, tg_id TEXT, bale_id TEXT, eitaa_id TEXT
        )");
        $this->pdo->exec("CREATE TABLE IF NOT EXISTS message_map (
            id INTEGER PRIMARY KEY AUTOINCREMENT, origin_key TEXT, 
            tg_msg_id TEXT, bale_msg_id TEXT, eitaa_msg_id TEXT, timestamp INTEGER
        )");
        $this->pdo->exec("CREATE TABLE IF NOT EXISTS user_states (user_id TEXT PRIMARY KEY, step TEXT, data TEXT)");
    }

    // CRUD Operations
    public function addGroup($d) { 
        $this->pdo->prepare("INSERT INTO channels (tg_id, bale_id, eitaa_id) VALUES (?,?,?)")
             ->execute([$d['tg'], $d['bale'], $d['eitaa']]); 
    }
    
    public function getGroups() { return $this->pdo->query("SELECT * FROM channels")->fetchAll(PDO::FETCH_ASSOC); }
    public function getGroup($id) { // دریافت یک گروه خاص برای ویرایش
        $stmt = $this->pdo->prepare("SELECT * FROM channels WHERE id = ?");
        $stmt->execute([$id]); return $stmt->fetch(PDO::FETCH_ASSOC);
    }
    
    public function updateGroup($id, $col, $val) { // متد جدید آپدیت
        // جلوگیری از تزریق SQL در نام ستون
        $allowed = ['tg_id', 'bale_id', 'eitaa_id'];
        if(!in_array($col, $allowed)) return false;
        
        $stmt = $this->pdo->prepare("UPDATE channels SET $col = ? WHERE id = ?");
        return $stmt->execute([$val, $id]);
    }

    public function deleteGroup($id) { $this->pdo->prepare("DELETE FROM channels WHERE id = ?")->execute([$id]); }
    
    public function findGroup($chatId, $platform) {
        $col = ['telegram'=>'tg_id', 'bale'=>'bale_id'][$platform] ?? null;
        if (!$col) return null;
        $stmt = $this->pdo->prepare("SELECT * FROM channels WHERE $col = ?");
        $stmt->execute([$chatId]); return $stmt->fetch(PDO::FETCH_ASSOC);
    }

    public function saveMap($key, $ids) {
        $this->pdo->prepare("INSERT INTO message_map (origin_key, tg_msg_id, bale_msg_id, eitaa_msg_id, timestamp) VALUES (?,?,?,?,?)")
             ->execute([$key, $ids['telegram'], $ids['bale'], $ids['eitaa'], time()]);
    }

    public function getMap($plat, $chatId, $msgId) {
        $key = "{$plat}_{$chatId}_{$msgId}";
        $stmt = $this->pdo->prepare("SELECT * FROM message_map WHERE origin_key = ?");
        $stmt->execute([$key]); return $stmt->fetch(PDO::FETCH_ASSOC);
    }
    
    // State Manager
    public function setState($u, $s, $d=[]) { $this->pdo->prepare("INSERT OR REPLACE INTO user_states (user_id, step, data) VALUES (?, ?, ?)")->execute([$u, $s, json_encode($d)]); }
    public function getState($u) { $stmt = $this->pdo->prepare("SELECT * FROM user_states WHERE user_id = ?"); $stmt->execute([$u]); $r=$stmt->fetch(PDO::FETCH_ASSOC); return $r ? ['step'=>$r['step'], 'data'=>json_decode($r['data'],true)] : null; }
    public function clearState($u) { $this->pdo->prepare("DELETE FROM user_states WHERE user_id = ?")->execute([$u]); }
}

// ====================================================
// 3. NETWORK DRIVER
// ====================================================

class Network {
    private function getBase($p) {
        switch($p) {
            case 'telegram': return "https://api.telegram.org/bot".TG_TOKEN;
            case 'bale':     return "https://tapi.bale.ai/bot".BALE_TOKEN;
            case 'eitaa':    return "https://eitaayar.ir/api/".EITAA_TOKEN;
        }
        return "";
    }

    public function req($method, $data, $plat, $isFile=false) {
        $url = $this->getBase($plat) . "/$method";
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_POST, 1);
        
        if($isFile) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
        } else {
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
            curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
        }
        
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        if($plat=='telegram' && TG_PROXY) curl_setopt($ch, CURLOPT_PROXY, TG_PROXY);
        
        $res = curl_exec($ch);
        curl_close($ch);
        return json_decode($res, true);
    }

    public function download($fileId, $plat) {
        $res = $this->req('getFile', ['file_id'=>$fileId], $plat);
        if(!isset($res['result']['file_path'])) return null;
        $path = $res['result']['file_path'];
        
        $url = "";
        if ($plat == 'telegram') $url = "https://api.telegram.org/file/bot".TG_TOKEN."/$path";
        elseif ($plat == 'bale') $url = "https://tapi.bale.ai/file/bot".BALE_TOKEN."/$path";
        
        if(!$url) return null;
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        if($plat=='telegram' && TG_PROXY) curl_setopt($ch, CURLOPT_PROXY, TG_PROXY);
        return curl_exec($ch);
    }
}

// ====================================================
// 4. LOGIC CORE (Brain)
// ====================================================

class Robot {
    private $db, $net;
    public function __construct() { $this->db = new DB(); $this->net = new Network(); }

    public function start() {
        $input = file_get_contents("php://input");
        $update = json_decode($input, true);
        $src = $_GET['source'] ?? ''; 

        if(!$update) { echo "Triangle Sync Bot V17.0 (With Edit) Online."; return; }
        if(in_array($src, ['telegram', 'bale'])) $this->handle($update, $src);
    }

    private function handle($up, $plat) {
        $msg = $up['message'] ?? $up['channel_post'] ?? $up['edited_channel_post'] ?? null;
        if(!$msg) return;

        $cid = $msg['chat']['id'];
        $uid = $msg['from']['id'] ?? $cid;
        $txt = $msg['text'] ?? $msg['caption'] ?? '';
        $isEdit = isset($up['edited_channel_post']);

        // 1. ADMIN PANEL (Private Chat)
        if(($msg['chat']['type']??'') == 'private') {
            if(in_array($uid, ADMIN_IDS[$plat]??[])) {
                $this->wizard($uid, $cid, $txt, $plat);
                return;
            }
        }

        // 2. CHANNEL SYNC
        if(($msg['chat']['type']??'') == 'channel') {
            // Edit Sync
            if($isEdit) { 
                $this->syncEdit($cid, $msg['message_id'], $msg, $plat); 
                return; 
            }
            // Normal Message
            $grp = $this->db->findGroup($cid, $plat);
            if($grp) $this->distribute($grp, $msg, $plat);
        }
    }

    private function distribute($grp, $msg, $src) {
        $targets = [];
        if($src != 'telegram' && $grp['tg_id']) $targets['telegram'] = $grp['tg_id'];
        if($src != 'bale' && $grp['bale_id'])   $targets['bale'] = $grp['bale_id'];
        if($grp['eitaa_id']) $targets['eitaa'] = $grp['eitaa_id'];

        $cleanText = str_replace(['#nosync'], '', $msg['text']??$msg['caption']??'');
        if(stripos($msg['text']??'', '#nosync') !== false) return;

        $ids = ['telegram'=>null, 'bale'=>null, 'eitaa'=>null];
        $ids[$src] = $msg['message_id'];

        foreach($targets as $dest => $destId) {
            $res = null;
            if(isset($msg['text'])) {
                if ($dest == 'eitaa') {
                    $res = $this->net->req('sendMessage', ['chat_id'=>$destId, 'text'=>$cleanText], 'eitaa');
                } else {
                    $res = $this->sendTxt($destId, $cleanText, $dest);
                }
            } else {
                $res = $this->sendMedia($destId, $msg, $src, $dest, $cleanText);
            }

            if($res && (isset($res['result']['message_id']) || isset($res['message_id']))) {
                $ids[$dest] = $res['result']['message_id'] ?? $res['message_id'];
            }
        }
        $this->db->saveMap("{$src}_{$msg['chat']['id']}_{$msg['message_id']}", $ids);
    }

    private function sendMedia($tid, $msg, $src, $dest, $cap) {
        $type = null;
        foreach(['photo','video','voice','audio','document','sticker','animation'] as $t) if(isset($msg[$t])) $type=$t;
        if(!$type) return null;

        $fObj = ($type=='photo') ? end($msg[$type]) : $msg[$type];
        $bin = $this->net->download($fObj['file_id'], $src);
        if(!$bin) return null;

        $ext = 'dat';
        if($type=='photo') $ext='jpg';
        if($type=='video' || $type=='animation') $ext='mp4';
        if($type=='voice') $ext='ogg'; 
        if($type=='audio') $ext='mp3'; 
        if($type=='sticker') $ext='webp';
        
        $tmp = sys_get_temp_dir() . "/s_".time().rand().".$ext";
        file_put_contents($tmp, $bin);
        $cfile = new CURLFile($tmp, mime_content_type($tmp), basename($tmp));

        $res = null;
        if ($dest == 'eitaa') { // Eitaayar
            $data = ['chat_id' => $tid, 'caption' => $cap, 'file' => $cfile];
            if ($type == 'sticker') $data['title'] = 'webp';
            if ($type == 'animation') $data['title'] = 'gif';
            $res = $this->net->req('sendFile', $data, 'eitaa', true);
        } else {
            $method = ['photo'=>'sendPhoto','video'=>'sendVideo','voice'=>'sendVoice','document'=>'sendDocument','sticker'=>'sendSticker','animation'=>'sendAnimation'][$type] ?? 'sendDocument';
            $res = $this->net->req($method, ['chat_id'=>$tid, 'caption'=>$cap, $type=>$cfile], $dest, true);
        }
        @unlink($tmp);
        return $res;
    }

    private function syncEdit($cid, $mid, $msg, $src) {
        $txt = trim($msg['text'] ?? $msg['caption'] ?? '');
        $map = $this->db->getMap($src, $cid, $mid);
        if(!$map) return;

        if($txt === '-') { // Ghost Delete
            $grp = $this->db->findGroup($cid, $src);
            if($grp) {
                if($grp['tg_id'] && $map['tg_msg_id']) $this->net->req('deleteMessage', ['chat_id'=>$grp['tg_id'], 'message_id'=>$map['tg_msg_id']], 'telegram');
                if($grp['bale_id'] && $map['bale_msg_id']) $this->net->req('deleteMessage', ['chat_id'=>$grp['bale_id'], 'message_id'=>$map['bale_msg_id']], 'bale');
            }
            return;
        }

        $grp = $this->db->findGroup($cid, $src);
        $targets = ['telegram'=>'tg', 'bale'=>'bale'];
        foreach($targets as $p => $pre) {
            if($p == $src || empty($map["{$pre}_msg_id"])) continue;
            $res = $this->net->req('editMessageCaption', ['chat_id'=>$grp["{$pre}_id"], 'message_id'=>$map["{$pre}_msg_id"], 'caption'=>$txt], $p);
            if(!($res['ok']??false)) $this->net->req('editMessageText', ['chat_id'=>$grp["{$pre}_id"], 'message_id'=>$map["{$pre}_msg_id"], 'text'=>$txt], $p);
        }
    }

    // --- ADMIN WIZARD (With EDIT) ---
    private function wizard($uid, $cid, $txt, $plat) {
        $st = $this->db->getState($uid);
        $step = $st['step'] ?? 'MAIN';

        if($txt=='/start' || $txt=='بازگشت' || $txt=='Back') {
            $this->db->clearState($uid);
            $this->sendTxt($cid, "منوی مدیریت:", $plat, null, [['➕ اتصال جدید','✏️ ویرایش'],['➖ حذف','📋 لیست']]);
            return;
        }

        if($step == 'MAIN') {
            if($txt=='➕ اتصال جدید') { $this->db->setState($uid, 'GET_TG'); $this->sendTxt($cid, "1. آیدی کانال تلگرام (یا skip):", $plat); }
            elseif($txt=='📋 لیست') {
                $ls = $this->db->getGroups(); $out="لیست اتصالات:\n";
                foreach($ls as $g) $out.="🆔 {$g['id']}\nTG: {$g['tg_id']}\nBale: {$g['bale_id']}\nEitaa: {$g['eitaa_id']}\n----------\n";
                $this->sendTxt($cid, $out?:'لیست خالی است.', $plat);
            }
            elseif($txt=='✏️ ویرایش') { 
                $this->db->setState($uid, 'EDIT_GET_ID'); 
                $this->sendTxt($cid, "شماره شناسه (🆔 ID) گروهی که می‌خواهید ویرایش کنید را بفرستید:\n(از لیست بردارید)", $plat, null, [['بازگشت']]); 
            }
            elseif($txt=='➖ حذف') { $this->db->setState($uid,'DEL'); $this->sendTxt($cid,"آیدی عددی اتصال (ID) برای حذف:",$plat); }
        }
        // --- ADD FLOW ---
        elseif($step == 'GET_TG') { 
            $this->db->setState($uid, 'GET_BALE', ['tg'=>$this->clean($txt)]); $this->sendTxt($cid, "2. آیدی بله (یا skip):", $plat); 
        }
        elseif($step == 'GET_BALE') { 
            $d=$st['data']; $d['bale']=$this->clean($txt); $this->db->setState($uid, 'GET_EITAA', $d); $this->sendTxt($cid, "3. آیدی ایتا (یا skip):", $plat); 
        }
        elseif($step == 'GET_EITAA') { 
            $val=$this->clean($txt); if($val && $val[0]=='@') $val=substr($val,1);
            $d=$st['data']; $d['eitaa']=$val;
            $this->db->addGroup($d); $this->db->clearState($uid);
            $this->sendTxt($cid, "✅ متصل شد.", $plat, null, [['➕ اتصال جدید','📋 لیست']]); 
        }
        // --- EDIT FLOW (NEW) ---
        elseif($step == 'EDIT_GET_ID') {
            $grp = $this->db->getGroup($txt);
            if(!$grp) { $this->sendTxt($cid, "❌ شناسه یافت نشد. دوباره تلاش کنید:", $plat); return; }
            
            $info = "گروه پیدا شد:\nTG: {$grp['tg_id']}\nBale: {$grp['bale_id']}\nEitaa: {$grp['eitaa_id']}\n\nکدام را تغییر می‌دهید؟";
            $this->db->setState($uid, 'EDIT_SELECT_PLAT', ['id'=>$txt]);
            $this->sendTxt($cid, $info, $plat, null, [['تلگرام','بله','ایتا'],['بازگشت']]);
        }
        elseif($step == 'EDIT_SELECT_PLAT') {
            $col = '';
            if($txt=='تلگرام') $col='tg_id';
            elseif($txt=='بله') $col='bale_id';
            elseif($txt=='ایتا') $col='eitaa_id';
            else { $this->sendTxt($cid, "لطفا از دکمه‌ها انتخاب کنید.", $plat); return; }
            
            $d=$st['data']; $d['col']=$col;
            $this->db->setState($uid, 'EDIT_SET_VAL', $d);
            $this->sendTxt($cid, "آیدی جدید را وارد کنید (یا بنویسید skip برای خالی کردن):", $plat, null, [['بازگشت']]);
        }
        elseif($step == 'EDIT_SET_VAL') {
            $val = $this->clean($txt);
            if($st['data']['col']=='eitaa_id' && $val && $val[0]=='@') $val=substr($val,1);
            
            $this->db->updateGroup($st['data']['id'], $st['data']['col'], $val);
            $this->db->clearState($uid);
            $this->sendTxt($cid, "✅ ویرایش انجام شد.", $plat, null, [['✏️ ویرایش','📋 لیست']]);
        }
        // --- DELETE FLOW ---
        elseif($step == 'DEL') { 
            $this->db->deleteGroup($txt); $this->db->clearState($uid); $this->sendTxt($cid, "حذف شد.", $plat, null, [['📋 لیست']]); 
        }
    }

    private function clean($t) { return (strtolower($t)=='skip') ? null : trim($t); }
    private function sendTxt($cid, $t, $p, $r=null, $k=null) {
        $d = ['chat_id'=>$cid, 'text'=>$t];
        if($k) $d['reply_markup'] = json_encode(['keyboard'=>$k, 'resize_keyboard'=>true]);
        return $this->net->req('sendMessage', $d, $p);
    }
}

try { $bot = new Robot(); $bot->start(); } 
catch(Exception $e) { file_put_contents('error_log.txt', $e->getMessage(), FILE_APPEND); }
?>