<?php
/**
 * Auto-extracted module from the old large bot.php.
 * Loaded by bot.php in the original execution order.
 */

$__textSettingsService = dirname(__DIR__) . '/Services/TextSettingsService.php';
if (is_file($__textSettingsService)) { require_once $__textSettingsService; }
function normalize_bale_channel_username($input){
    $input = trim((string)$input);
    $input = str_replace(["\r", "\n", "\t", ' '], '', $input);
    $input = preg_replace('~^(https?://)?(www\.)?(t\.me)/~i', '', $input);
    $input = trim($input, "@/ ");
    if(strpos($input, '?') !== false){
        $input = explode('?', $input, 2)[0];
    }
    return $input;
}
function is_bale_numeric_chat_id($input){
    return (bool)preg_match('/^-?\d{5,30}$/', trim((string)$input));
}
function is_valid_bale_channel_username($input){
    $input = normalize_bale_channel_username($input);
    return is_bale_numeric_chat_id($input) || (bool)preg_match('/^[A-Za-z0-9_]{3,64}$/', (string)$input);
}
function bale_channel_display_name($channelRef){
    $channelRef = normalize_bale_channel_username($channelRef);
    if($channelRef === '') return 'تنظیم نشده';
    return is_bale_numeric_chat_id($channelRef) ? 'شناسه عددی '.$channelRef : '@'.$channelRef;
}

function bale_chat_type_label($type){
    $type = (string)$type;
    if($type === 'group' || $type === 'supergroup') return 'گروه';
    if($type === 'channel') return 'کانال';
    return 'کانال/گروه';
}

function bale_order_target_label($channelRef, $fallbackType = ''){
    $label = bale_chat_type_label($fallbackType);
    $name = bale_channel_display_name($channelRef);
    return $label . ' ' . $name;
}

function bale_public_or_invite_url_for_chat($channelRef, $name = ''){
    $channelRef = normalize_bale_channel_username($channelRef);
    if($channelRef === '') return '';
    if(!is_bale_numeric_chat_id($channelRef)){
        return 'https://t.me/' . ltrim($channelRef, '@');
    }
    // برای گروه/کانال خصوصی با شناسه عددی، اگر ربات دسترسی دعوت داشته باشد لینک دعوت می‌سازیم.
    $cachePath = 'lib/kodam/chat_invite_links.json';
    $cache = function_exists('bot_data_get_json') ? bot_data_get_json($cachePath, []) : [];
    if(isset($cache[$channelRef]['url']) && is_string($cache[$channelRef]['url']) && preg_match('~^https?://~', $cache[$channelRef]['url'])){
        return $cache[$channelRef]['url'];
    }
    $params = ['chat_id'=>$channelRef];
    if($name !== '') $params['name'] = function_exists('mb_substr') ? mb_substr((string)$name, 0, 32, 'UTF-8') : substr((string)$name, 0, 32);
    $res = bot('createChatInviteLink', $params);
    $url = '';
    if(isset($res->ok) && $res->ok && isset($res->result->invite_link)){
        $url = (string)$res->result->invite_link;
    }
    if($url !== ''){
        $cache[$channelRef] = ['url'=>$url, 'created_at'=>time(), 'title'=>(string)$name];
        if(function_exists('bot_data_put_json')) bot_data_put_json($cachePath, $cache);
    }
    return $url;
}




function bot_telegram_chat_ref_for_api($ref){
    $ref = trim((string)$ref);
    if($ref === '') return '';
    $ref = str_replace(["\r", "\n", "\t", ' '], '', $ref);
    $ref = preg_replace('~^(https?://)?(www\.)?(t\.me)/~i', '', $ref);
    if(strpos($ref, '?') !== false) $ref = explode('?', $ref, 2)[0];
    $ref = trim($ref, "/ ");
    // A common bad value after Bale→Telegram conversion is @-100xxxxxxxxxx.
    if(preg_match('/^@-?\d{5,30}$/', $ref)) return substr($ref, 1);
    if(preg_match('/^-?\d{5,30}$/', $ref)) return $ref;
    $ref = trim($ref, '@');
    if($ref === '') return '';
    return '@'.$ref;
}

function bot_telegram_order_source_candidates(array $order = []){
    $candidates = [];
    foreach(['post_chat_id','post_channel_id','ads_chat_id','source_chat_id','from_chat_id'] as $k){
        if(isset($order[$k]) && trim((string)$order[$k]) !== '') $candidates[] = (string)$order[$k];
    }
    if(!empty($GLOBALS['chads'])) $candidates[] = (string)$GLOBALS['chads'];
    $out = [];
    foreach($candidates as $raw){
        $apiRef = bot_telegram_chat_ref_for_api($raw);
        if($apiRef !== '') $out[] = $apiRef;
        $norm = function_exists('normalize_bale_channel_username') ? normalize_bale_channel_username($raw) : trim((string)$raw, '@/ ');
        if($norm !== '' && function_exists('bale_get_chat_id_for_channel') && !function_exists('__dummy_never')){
            try{
                $resolved = bale_get_chat_id_for_channel($norm);
                $apiResolved = bot_telegram_chat_ref_for_api($resolved);
                if($apiResolved !== '') $out[] = $apiResolved;
            }catch(Throwable $e){}
        }
    }
    return array_values(array_unique(array_filter($out, function($v){ return $v !== ''; })));
}
function bale_forced_join_channels_file(){ return 'lib/kodam/forced_join_channels.json'; }
function bale_forced_join_channel_key($channelRef){
    $ch = normalize_bale_channel_username($channelRef);
    if($ch === '') return '';
    return is_bale_numeric_chat_id($ch) ? $ch : strtolower($ch);
}
function bale_forced_join_channels_load($includeLegacy = true){
    $raw = function_exists('bot_data_get_json') ? bot_data_get_json(bale_forced_join_channels_file(), []) : [];
    if(!is_array($raw)) $raw = [];
    $rows = [];
    if(isset($raw['channels']) && is_array($raw['channels'])) $rows = $raw['channels'];
    elseif(array_values($raw) === $raw) $rows = $raw;
    else $rows = $raw;

    $out = [];
    foreach($rows as $key=>$row){
        if(is_string($row)) $row = ['ref'=>$row];
        if(!is_array($row)) continue;
        $ref = normalize_bale_channel_username($row['ref'] ?? $row['channel'] ?? $row['username'] ?? $key);
        if($ref === '' || !is_valid_bale_channel_username($ref)) continue;
        $k = bale_forced_join_channel_key($ref);
        if($k === '') continue;
        $out[$k] = [
            'key'=>$k,
            'ref'=>$ref,
            'title'=>trim((string)($row['title'] ?? '')),
            'active'=>array_key_exists('active', $row) ? (bool)$row['active'] : true,
            'created_at'=>(int)($row['created_at'] ?? time()),
            'updated_at'=>(int)($row['updated_at'] ?? ($row['created_at'] ?? time())),
            'source'=>trim((string)($row['source'] ?? 'list')),
            'expires_at'=>(int)($row['expires_at'] ?? 0),
            'sponsor_order_id'=>trim((string)($row['sponsor_order_id'] ?? '')),
            'sponsor_user_id'=>trim((string)($row['sponsor_user_id'] ?? '')),
            'sponsor'=>(!empty($row['sponsor']) || (string)($row['source'] ?? '') === 'sponsor'),
        ];
    }

    if($includeLegacy && function_exists('bot_data_get_text')){
        $legacy = [
            ['path'=>'lib/kodam/channel.txt', 'title'=>'جوین اجباری قبلی ۱', 'source'=>'legacy1'],
            ['path'=>'lib/kodam/channel2.txt', 'title'=>'جوین اجباری قبلی ۲', 'source'=>'legacy2'],
        ];
        foreach($legacy as $l){
            $ref = normalize_bale_channel_username(bot_data_get_text($l['path'], ''));
            if($ref === '' || !is_valid_bale_channel_username($ref)) continue;
            $k = bale_forced_join_channel_key($ref);
            if($k === '') continue;
            if(!isset($out[$k])){
                $out[$k] = [
                    'key'=>$k,
                    'ref'=>$ref,
                    'title'=>$l['title'],
                    'active'=>true,
                    'created_at'=>time(),
                    'updated_at'=>time(),
                    'source'=>$l['source'],
                ];
            }
        }
    }
    return $out;
}
function bale_forced_join_channels_save($rows){
    if(!is_array($rows)) $rows = [];
    $clean = [];
    foreach($rows as $key=>$row){
        if(is_string($row)) $row = ['ref'=>$row];
        if(!is_array($row)) continue;
        $ref = normalize_bale_channel_username($row['ref'] ?? $row['channel'] ?? $key);
        if($ref === '' || !is_valid_bale_channel_username($ref)) continue;
        $k = bale_forced_join_channel_key($ref);
        if($k === '') continue;
        $clean[$k] = [
            'key'=>$k,
            'ref'=>$ref,
            'title'=>trim((string)($row['title'] ?? '')),
            'active'=>array_key_exists('active', $row) ? (bool)$row['active'] : true,
            'created_at'=>(int)($row['created_at'] ?? time()),
            'updated_at'=>time(),
            'source'=>trim((string)($row['source'] ?? 'list')),
            'expires_at'=>(int)($row['expires_at'] ?? 0),
            'sponsor_order_id'=>trim((string)($row['sponsor_order_id'] ?? '')),
            'sponsor_user_id'=>trim((string)($row['sponsor_user_id'] ?? '')),
            'sponsor'=>(!empty($row['sponsor']) || (string)($row['source'] ?? '') === 'sponsor'),
        ];
    }
    if(function_exists('bot_data_put_json')) bot_data_put_json(bale_forced_join_channels_file(), ['channels'=>array_values($clean), 'updated_at'=>time()]);
    else @file_put_contents(bale_forced_join_channels_file(), json_encode(['channels'=>array_values($clean), 'updated_at'=>time()], JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT), LOCK_EX);
    return $clean;
}
function bale_forced_join_channel_add($channelRef, $title = '', $active = true){
    $ref = normalize_bale_channel_username($channelRef);
    if($ref === '' || !is_valid_bale_channel_username($ref)) return false;
    if(function_exists('bale_channel_is_blocked') && bale_channel_is_blocked($ref)) return false;
    $rows = bale_forced_join_channels_load(false);
    $k = bale_forced_join_channel_key($ref);
    $rows[$k] = [
        'key'=>$k,
        'ref'=>$ref,
        'title'=>trim((string)$title),
        'active'=>(bool)$active,
        'created_at'=>(int)($rows[$k]['created_at'] ?? time()),
        'updated_at'=>time(),
        'source'=>'list',
    ];
    return bale_forced_join_channels_save($rows);
}
function bale_forced_join_channel_remove($channelRef){
    $ref = normalize_bale_channel_username($channelRef);
    $key = bale_forced_join_channel_key($ref);
    if($key === '') return false;
    $rows = bale_forced_join_channels_load(false);
    $removed = false;
    foreach(array_keys($rows) as $k){
        if($k === $key || bale_forced_join_channel_key($rows[$k]['ref'] ?? '') === $key){ unset($rows[$k]); $removed = true; }
    }
    // Compatibility: if the removed channel was stored in old fixed files, clear them too.
    if(function_exists('bot_data_get_text') && function_exists('bot_data_put_text')){
        foreach(['lib/kodam/channel.txt','lib/kodam/channel2.txt'] as $path){
            $old = normalize_bale_channel_username(bot_data_get_text($path, ''));
            if($old !== '' && bale_forced_join_channel_key($old) === $key){ bot_data_put_text($path, ''); $removed = true; }
        }
    }
    if($removed) bale_forced_join_channels_save($rows);
    return $removed;
}
function bale_forced_join_channel_set_active($channelRef, $active){
    $key = bale_forced_join_channel_key($channelRef);
    if($key === '') return false;
    $rows = bale_forced_join_channels_load(false);
    $found = false;
    foreach($rows as $k=>$row){
        if($k === $key || bale_forced_join_channel_key($row['ref'] ?? '') === $key){ $rows[$k]['active'] = (bool)$active; $rows[$k]['updated_at'] = time(); $found = true; }
    }
    if(!$found){
        // If this is a legacy-only channel, copy it into the new list with requested status.
        foreach(bale_forced_join_channels_load(true) as $row){
            if(bale_forced_join_channel_key($row['ref'] ?? '') === $key){
                $row['active'] = (bool)$active;
                $row['source'] = 'list';
                $rows[$key] = $row;
                $found = true;
                break;
            }
        }
    }
    if($found) bale_forced_join_channels_save($rows);
    return $found;
}
function bale_forced_join_channels_active(){
    $out = [];
    $changed = false;
    $now = time();
    $rowsAll = bale_forced_join_channels_load(false);
    foreach(bale_forced_join_channels_load(true) as $row){
        $expiresAt = (int)($row['expires_at'] ?? 0);
        if($expiresAt > 0 && $expiresAt <= $now){
            $k = bale_forced_join_channel_key($row['ref'] ?? '');
            if($k !== '' && isset($rowsAll[$k]) && !empty($rowsAll[$k]['active'])){ $rowsAll[$k]['active'] = false; $changed = true; }
            continue;
        }
        if(empty($row['active'])) continue;
        $ref = normalize_bale_channel_username($row['ref'] ?? '');
        if($ref === '') continue;
        if(function_exists('bale_channel_is_blocked') && bale_channel_is_blocked($ref)) continue;
        $out[] = $ref;
    }
    if($changed) bale_forced_join_channels_save($rowsAll);
    return array_values(array_unique($out));
}

function bale_forced_join_channel_add_sponsor($channelRef, $title = '', $sponsorOrderId = '', $sponsorUserId = '', $expiresAt = 0){
    $ref = normalize_bale_channel_username($channelRef);
    if($ref === '' || !is_valid_bale_channel_username($ref)) return false;
    if(function_exists('bale_channel_is_blocked') && bale_channel_is_blocked($ref)) return false;
    $rows = bale_forced_join_channels_load(false);
    $k = bale_forced_join_channel_key($ref);
    $rows[$k] = [
        'key'=>$k,
        'ref'=>$ref,
        'title'=>trim((string)$title),
        'active'=>true,
        'created_at'=>(int)($rows[$k]['created_at'] ?? time()),
        'updated_at'=>time(),
        'source'=>'sponsor',
        'expires_at'=>(int)$expiresAt,
        'sponsor_order_id'=>trim((string)$sponsorOrderId),
        'sponsor_user_id'=>trim((string)$sponsorUserId),
        'sponsor'=>true,
    ];
    return bale_forced_join_channels_save($rows);
}
function bale_forced_join_channel_by_hash($hash){
    $hash = preg_replace('/[^a-f0-9]/i', '', (string)$hash);
    foreach(bale_forced_join_channels_load(true) as $row){
        $ref = normalize_bale_channel_username($row['ref'] ?? '');
        if($ref !== '' && substr(sha1($ref),0,10) === $hash) return $ref;
    }
    return '';
}
function bale_forced_join_channels_text(){
    $rows = bale_forced_join_channels_load(true);
    if(empty($rows)) return "🔐 هنوز هیچ چنلی برای قفل جوین اجباری ثبت نشده است.";
    $lines = ["🔐 چنل‌های قفل‌شده برای عضویت اجباری:"];
    $i = 1;
    foreach($rows as $row){
        $ref = $row['ref'] ?? '';
        $active = !empty($row['active']) ? '✅ فعال' : '⏸ غیرفعال';
        $title = trim((string)($row['title'] ?? ''));
        $extra = '';
        if(!empty($row['expires_at'])) $extra .= "\n   اعتبار تا: ".(function_exists('jdate') ? jdate('Y/m/d H:i', (int)$row['expires_at']) : date('Y-m-d H:i', (int)$row['expires_at']));
        if((string)($row['source'] ?? '') === 'sponsor') $extra .= "\n   نوع: اسپانسر";
        $lines[] = $i.'. '.bale_channel_display_name($ref).' — '.$active.($title !== '' ? "\n   عنوان: ".$title : '').$extra;
        $i++;
    }
    return implode("\n", $lines);
}
function bale_forced_join_channels_keyboard(){
    $rows = [];
    foreach(bale_forced_join_channels_load(true) as $row){
        $ref = normalize_bale_channel_username($row['ref'] ?? '');
        if($ref === '') continue;
        $hash = substr(sha1($ref),0,10);
        $rows[] = [
            ['text'=>(!empty($row['active']) ? '⏸ غیرفعال' : '▶️ فعال').' '.bale_channel_display_name($ref), 'callback_data'=>'fjtoggle-'.$hash],
            ['text'=>'🗑 حذف', 'callback_data'=>'fjremove-'.$hash],
        ];
    }
    $rows[] = [['text'=>'➕ افزودن چنل قفل', 'callback_data'=>'fjadd']];
    $rows[] = [['text'=>'➡️ برگشت به تنظیم کانال', 'callback_data'=>'channel_settings_refresh']];
    return json_encode(['inline_keyboard'=>$rows], JSON_UNESCAPED_UNICODE);
}

function bale_blocked_channels_file(){ return 'lib/kodam/blocked_channels.json'; }
function bale_channel_block_key($channelRef){
    $ch = normalize_bale_channel_username($channelRef);
    if($ch === '') return '';
    return is_bale_numeric_chat_id($ch) ? $ch : strtolower($ch);
}
function bale_blocked_channels_load(){
    $data = bot_data_get_json(bale_blocked_channels_file(), []);
    if(!is_array($data)) $data = [];
    $out = [];
    foreach($data as $key=>$row){
        if(is_array($row)){
            $ref = $row['ref'] ?? $key;
            $blockKey = bale_channel_block_key($row['key'] ?? $ref);
            if($blockKey === '') continue;
            $row['key'] = $blockKey;
            $row['ref'] = normalize_bale_channel_username($ref);
            $out[$blockKey] = $row;
        }else{
            $ref = is_string($key) ? $key : $row;
            $blockKey = bale_channel_block_key($ref);
            if($blockKey === '') continue;
            $out[$blockKey] = ['key'=>$blockKey,'ref'=>normalize_bale_channel_username($ref),'reason'=>'','created_at'=>time()];
        }
    }
    return $out;
}
function bale_blocked_channels_save($data){
    if(!is_array($data)) $data = [];
    $clean = [];
    foreach($data as $key=>$row){
        if(!is_array($row)) continue;
        $ref = $row['ref'] ?? $key;
        $blockKey = bale_channel_block_key($row['key'] ?? $ref);
        if($blockKey === '') continue;
        $row['key'] = $blockKey;
        $row['ref'] = normalize_bale_channel_username($ref);
        $row['reason'] = trim((string)($row['reason'] ?? ''));
        $row['created_at'] = (int)($row['created_at'] ?? time());
        $clean[$blockKey] = $row;
    }
    bot_data_put_json(bale_blocked_channels_file(), $clean);
    return $clean;
}
function bale_channel_block_add($channelRef, $reason = '', $adminId = ''){
    $ref = normalize_bale_channel_username($channelRef);
    if($ref === '' || !is_valid_bale_channel_username($ref)) return false;
    $data = bale_blocked_channels_load();
    $keys = array_values(array_unique(array_filter([
        bale_channel_block_key($ref),
        is_bale_numeric_chat_id($ref) ? '' : bale_channel_block_key('@'.$ref),
    ])));
    foreach($keys as $key){
        $data[$key] = [
            'key'=>$key,
            'ref'=>$ref,
            'reason'=>trim((string)$reason),
            'admin_id'=>(string)$adminId,
            'created_at'=>time(),
        ];
    }
    return bale_blocked_channels_save($data);
}
function bale_channel_block_remove($channelRef){
    $key = bale_channel_block_key($channelRef);
    if($key === '') return false;
    $data = bale_blocked_channels_load();
    $removed = false;
    foreach(array_keys($data) as $k){
        $row = $data[$k];
        if($k === $key || bale_channel_block_key($row['ref'] ?? '') === $key){
            unset($data[$k]);
            $removed = true;
        }
    }
    if($removed) bale_blocked_channels_save($data);
    return $removed;
}
function bale_channel_is_blocked($channelRef){
    $ref = normalize_bale_channel_username($channelRef);
    if($ref === '') return false;
    $data = bale_blocked_channels_load();
    $keys = [bale_channel_block_key($ref)];
    if(!is_bale_numeric_chat_id($ref)) $keys[] = bale_channel_block_key('@'.$ref);
    // If username has a mapped numeric ID, block by numeric ID too.
    if(function_exists('bale_get_chat_id_for_channel')){
        $id = bale_get_chat_id_for_channel($ref);
        if($id !== '') $keys[] = bale_channel_block_key($id);
    }
    foreach(array_unique(array_filter($keys)) as $k){
        if(isset($data[$k])) return true;
    }
    return false;
}
function bale_blocked_channels_text(){
    $data = bale_blocked_channels_load();
    if(empty($data)) return "📋 لیست چنل‌های مسدود خالی است.";
    $lines = ["📋 چنل‌های مسدود:"];
    $i = 1;
    foreach($data as $row){
        $reason = trim((string)($row['reason'] ?? ''));
        $lines[] = $i.'. '.bale_channel_display_name($row['ref'] ?? $row['key']).($reason !== '' ? "\n   دلیل: ".$reason : '');
        $i++;
    }
    return implode("\n", $lines);
}
function bale_blocked_channels_keyboard(){
    $rows = [];
    foreach(bale_blocked_channels_load() as $row){
        $ref = normalize_bale_channel_username($row['ref'] ?? $row['key']);
        if($ref === '') continue;
        $rows[] = [['text'=>'✅ رفع مسدودی '.bale_channel_display_name($ref), 'callback_data'=>'chunblock-'.substr(sha1($ref),0,10)]];
    }
    $rows[] = [['text'=>'➡️ برگشت به تنظیم کانال', 'callback_data'=>'channel_settings_refresh']];
    return json_encode(['inline_keyboard'=>$rows], JSON_UNESCAPED_UNICODE);
}
function bale_blocked_channel_by_hash($hash){
    $hash = preg_replace('/[^a-f0-9]/i', '', (string)$hash);
    foreach(bale_blocked_channels_load() as $row){
        $ref = normalize_bale_channel_username($row['ref'] ?? $row['key']);
        if(substr(sha1($ref),0,10) === $hash) return $ref;
    }
    return '';
}


function bot_normalize_number_digits($value){
    $value = trim((string)$value);
    $fa = ['۰','۱','۲','۳','۴','۵','۶','۷','۸','۹','٠','١','٢','٣','٤','٥','٦','٧','٨','٩'];
    $en = ['0','1','2','3','4','5','6','7','8','9','0','1','2','3','4','5','6','7','8','9'];
    return str_replace($fa, $en, $value);
}
function bale_channel_join_url($channelRef){
    $raw = trim((string)$channelRef);
    if($raw === '') return '';
    if(preg_match('~^https?://~i', $raw)) return $raw;
    if(preg_match('~^(t\.me)/~i', $raw)) return 'https://'.$raw;
    $ch = normalize_bale_channel_username($raw);
    if($ch === '') return '';
    if(is_bale_numeric_chat_id($ch)) return 'https://t.me/';
    return 'https://t.me/'.$ch;
}
function bot_forced_join_button_settings_file(){ return 'lib/kodam/forced_join_button_settings.json'; }
function bot_forced_join_button_settings(){
    $def = [
        'join_text' => 'عضویت در کانال {n} - {name}',
        'check_text' => 'عضو شدم / بررسی عضویت',
        'join_style' => 'primary',
        'check_style' => 'success',
    ];
    $data = function_exists('bot_data_get_json') ? bot_data_get_json(bot_forced_join_button_settings_file(), []) : [];
    if(!is_array($data)) $data = [];
    $out = array_merge($def, $data);
    foreach(['join_style','check_style'] as $k){
        $out[$k] = bot_forced_join_button_style($out[$k] ?? '');
        if($out[$k] === '') $out[$k] = $def[$k];
    }
    foreach(['join_text','check_text'] as $k){
        $out[$k] = trim((string)($out[$k] ?? $def[$k]));
        if($out[$k] === '') $out[$k] = $def[$k];
    }
    return $out;
}
function bot_forced_join_button_settings_save(array $patch){
    $cur = bot_forced_join_button_settings();
    foreach($patch as $k=>$v) $cur[$k] = $v;
    if(function_exists('bot_data_put_json')) return bot_data_put_json(bot_forced_join_button_settings_file(), $cur);
    $path = bot_forced_join_button_settings_file();
    $dir = dirname($path); if($dir && !is_dir($dir)) @mkdir($dir, 0775, true);
    return @file_put_contents($path, json_encode($cur, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT), LOCK_EX) !== false;
}
function bot_forced_join_button_style($style){
    $style = strtolower(trim((string)$style));
    return in_array($style, ['primary','success','danger','default',''], true) ? ($style === 'default' ? '' : $style) : '';
}
function bot_forced_join_keyboard_button($text, array $extra, $style = ''){
    $button = array_merge(['text'=>(string)$text], $extra);
    $style = bot_forced_join_button_style($style);
    if($style !== '') $button['style'] = $style;
    return $button;
}
function bot_forced_join_keyboard($channels){
    $rows = [];
    $n = 1;
    $btnSettings = bot_forced_join_button_settings();
    foreach($channels as $channelRef){
        $channelRef = trim((string)$channelRef);
        if($channelRef === '') continue;
        $url = bale_channel_join_url($channelRef);
        if($url === '') continue;
        $name = bale_channel_display_name($channelRef);
        $joinText = str_replace(['{n}','{name}'], [(string)$n, $name], (string)$btnSettings['join_text']);
        $rows[] = [bot_forced_join_keyboard_button($joinText, ['url' => $url], (string)$btnSettings['join_style'])];
        $n++;
    }
    $rows[] = [bot_forced_join_keyboard_button((string)$btnSettings['check_text'], ['callback_data' => "forced_join_check"], (string)$btnSettings['check_style'])];
    return json_encode(['inline_keyboard'=>$rows], JSON_UNESCAPED_UNICODE);
}
function bot_send_forced_join_message($chatID, $channels){
    $lines = [];
    foreach($channels as $channelRef){
        $channelRef = trim((string)$channelRef);
        if($channelRef !== '') $lines[] = '🆔 '.bale_channel_display_name($channelRef);
    }
    $channelsText = empty($lines) ? '' : implode("
", $lines);
    if(function_exists('bot_text_settings_get')){
        $text = bot_text_settings_get('forced_join');
        $vars = ['CHANNELS'=>$channelsText];
        $text = function_exists('bot_text_settings_apply_vars') ? bot_text_settings_apply_vars($text, $vars) : str_replace('CHANNELS', $channelsText, $text);
    }else{
        $listText = empty($lines) ? '' : "

".$channelsText;
        $text = "🔐 کاربر گرامی، برای ادامه استفاده از ربات باید ابتدا در کانال‌های زیر عضو شوید.".$listText."

بعد از عضویت، روی دکمه «عضو شدم / بررسی عضویت» بزنید یا /start را ارسال کنید.";
    }
    bot('sendMessage',[
        'chat_id'=>$chatID,
        'text'=>$text,
        'parse_mode'=>'HTML',
        'reply_markup'=>bot_forced_join_keyboard($channels)
    ]);
}


function bot_start_join_ad_file(){ return 'lib/kodam/start_join_ad.json'; }
function bot_start_join_ad_seen_file(){ return 'lib/kodam/start_join_ad_seen.json'; }
function bot_start_join_ad_default(){
    return [
        'enabled'=>'❌غیر فعال',
        'text'=>'📢 به ربات خوش آمدید.\nبرای مشاهده کانال ما روی دکمه زیر بزنید.',
        'button_text'=>'📢 ورود به کانال',
        'button_url'=>'',
        'show_mode'=>'once_per_update',
        'updated_at'=>time(),
    ];
}
function bot_start_join_ad_load(){
    $def = bot_start_join_ad_default();
    $data = function_exists('bot_data_get_json') ? bot_data_get_json(bot_start_join_ad_file(), $def) : [];
    if(!is_array($data)) $data = [];
    return array_merge($def, $data);
}
function bot_start_join_ad_save(array $data){
    $old = bot_start_join_ad_load();
    $merged = array_merge($old, $data);
    $merged['enabled'] = (($merged['enabled'] ?? '') === '✅فعال') ? '✅فعال' : '❌غیر فعال';
    $merged['text'] = trim((string)($merged['text'] ?? ''));
    $merged['button_text'] = trim((string)($merged['button_text'] ?? ''));
    $merged['button_url'] = trim((string)($merged['button_url'] ?? ''));
    $merged['show_mode'] = 'once_per_update';
    $merged['updated_at'] = time();
    if(function_exists('bot_data_put_json')) return bot_data_put_json(bot_start_join_ad_file(), $merged);
    @file_put_contents(bot_start_join_ad_file(), json_encode($merged, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT), LOCK_EX);
    return true;
}
function bot_start_join_ad_enabled(){
    $cfg = bot_start_join_ad_load();
    return (($cfg['enabled'] ?? '') === '✅فعال') && trim((string)($cfg['text'] ?? '')) !== '';
}
function bot_start_join_ad_url($raw){
    $raw = trim((string)$raw);
    if($raw === '') return '';
    if(preg_match('~^https?://~i', $raw)) return $raw;
    if(preg_match('~^t\.me/~i', $raw)) return 'https://'.$raw;
    if(function_exists('bale_channel_join_url')){
        $url = bale_channel_join_url($raw);
        if($url !== '' && $url !== 'https://t.me/') return $url;
    }
    $ch = function_exists('normalize_bale_channel_username') ? normalize_bale_channel_username($raw) : trim($raw, '@ ');
    if($ch !== '' && !preg_match('/^-?\d+$/', $ch)) return 'https://t.me/'.$ch;
    return $raw;
}
function bot_start_join_ad_keyboard(){
    $cfg = bot_start_join_ad_load();
    $url = bot_start_join_ad_url($cfg['button_url'] ?? '');
    $btnText = trim((string)($cfg['button_text'] ?? ''));
    if($url === '' || $btnText === '') return null;
    return json_encode(['inline_keyboard'=>[[['text'=>$btnText, 'url'=>$url]]]], JSON_UNESCAPED_UNICODE);
}
function bot_start_join_ad_seen_load(){
    $data = function_exists('bot_data_get_json') ? bot_data_get_json(bot_start_join_ad_seen_file(), []) : [];
    return is_array($data) ? $data : [];
}
function bot_start_join_ad_seen_save(array $data){
    // نگه داشتن فایل سبک: فقط آخرین ۲۰۰۰۰ کاربر حفظ می‌شوند.
    if(count($data) > 20000){
        uasort($data, function($a,$b){ return (int)($b['seen_at'] ?? 0) <=> (int)($a['seen_at'] ?? 0); });
        $data = array_slice($data, 0, 20000, true);
    }
    if(function_exists('bot_data_put_json')) return bot_data_put_json(bot_start_join_ad_seen_file(), $data);
    @file_put_contents(bot_start_join_ad_seen_file(), json_encode($data, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT), LOCK_EX);
    return true;
}
function bot_start_join_ad_should_show($userId){
    $userId = preg_replace('/[^0-9]/', '', (string)$userId);
    if($userId === '' || !bot_start_join_ad_enabled()) return false;
    $cfg = bot_start_join_ad_load();
    $updated = (int)($cfg['updated_at'] ?? 0);
    $seen = bot_start_join_ad_seen_load();
    if(isset($seen[$userId]) && (int)($seen[$userId]['ad_updated_at'] ?? 0) >= $updated) return false;
    return true;
}
function bot_start_join_ad_mark_seen($userId){
    $userId = preg_replace('/[^0-9]/', '', (string)$userId);
    if($userId === '') return false;
    $cfg = bot_start_join_ad_load();
    $seen = bot_start_join_ad_seen_load();
    $seen[$userId] = ['seen_at'=>time(), 'ad_updated_at'=>(int)($cfg['updated_at'] ?? 0)];
    return bot_start_join_ad_seen_save($seen);
}
function bot_start_join_ad_send($chatID, $userId, $force = false){
    if(!$force && !bot_start_join_ad_should_show($userId)) return false;
    $cfg = bot_start_join_ad_load();
    $text = trim((string)($cfg['text'] ?? ''));
    if($text === '') return false;
    $payload = [
        'chat_id'=>$chatID,
        'text'=>$text,
        'parse_mode'=>'HTML',
    ];
    $keyboard = bot_start_join_ad_keyboard();
    if($keyboard !== null) $payload['reply_markup'] = $keyboard;
    $res = function_exists('bot') ? bot('sendMessage', $payload) : false;
    if(!$force) bot_start_join_ad_mark_seen($userId);
    return $res;
}
function bot_start_join_ad_clear_seen(){
    if(function_exists('bot_data_put_json')) return bot_data_put_json(bot_start_join_ad_seen_file(), []);
    @file_put_contents(bot_start_join_ad_seen_file(), '{}', LOCK_EX);
    return true;
}
function bot_start_join_ad_summary(){
    $cfg = bot_start_join_ad_load();
    $enabled = (($cfg['enabled'] ?? '') === '✅فعال') ? '✅ فعال' : '❌ غیرفعال';
    $text = trim((string)($cfg['text'] ?? ''));
    $btn = trim((string)($cfg['button_text'] ?? ''));
    $url = trim((string)($cfg['button_url'] ?? ''));
    $seen = bot_start_join_ad_seen_load();
    $h = function($v){ return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8'); };
    return "📢 <b>تبلیغ بعد از جوین اجباری</b>\n\n".
        "وضعیت: <b>{$enabled}</b>\n".
        "تعداد کاربران دیده‌شده برای تبلیغ فعلی/قبلی: <b>".count($seen)."</b>\n\n".
        "متن فعلی:\n<code>".$h($text !== '' ? $text : 'تنظیم نشده')."</code>\n\n".
        "دکمه: <code>".$h($btn !== '' ? $btn : 'بدون دکمه')."</code>\n".
        "لینک/چنل: <code>".$h($url !== '' ? $url : 'تنظیم نشده')."</code>\n\n".
        "این پیام فقط بعد از تأیید عضویت نمایش داده می‌شود و تا وقتی متن/تنظیمات تبلیغ را تغییر ندهی، برای همان کاربر تکراری ارسال نمی‌شود.";
}
function bot_start_join_ad_admin_keyboard(){
    $cfg = bot_start_join_ad_load();
    $toggle = (($cfg['enabled'] ?? '') === '✅فعال') ? '❌ خاموش کردن تبلیغ' : '✅ روشن کردن تبلیغ';
    return json_encode(['inline_keyboard'=>[
        [['text'=>$toggle, 'callback_data'=>'startad_toggle']],
        [['text'=>'✏️ تنظیم متن تبلیغ', 'callback_data'=>'startad_set_text']],
        [['text'=>'🔘 متن دکمه', 'callback_data'=>'startad_set_button_text'], ['text'=>'🔗 لینک/چنل دکمه', 'callback_data'=>'startad_set_button_url']],
        [['text'=>'👁 پیش‌نمایش', 'callback_data'=>'startad_preview'], ['text'=>'♻️ نمایش دوباره برای همه', 'callback_data'=>'startad_clear_seen']],
        [['text'=>'➡️ برگشت به تنظیم کانال', 'callback_data'=>'channel_settings_refresh']],
    ], 'resize_keyboard'=>true], JSON_UNESCAPED_UNICODE);
}

function bale_is_admin_status($status){
    $status = strtolower(trim((string)$status));
    return in_array($status, ['administrator','creator','owner'], true);
}
function bale_is_member_status($status){
    $status = strtolower(trim((string)$status));
    return in_array($status, ['member','administrator','creator','owner'], true);
}
function bale_channel_map_file(){ return 'lib/kodam/channel_map.json'; }
function bale_load_channel_map(){
    $file = bale_channel_map_file();
    if(!file_exists($file)) return [];
    $map = bot_data_get_json($file);
    return is_array($map) ? $map : [];
}
function bale_save_channel_map_entry($username, $id, $title = ''){
    $username = normalize_bale_channel_username($username);
    $id = trim((string)$id);
    if($id === '') return;
    $map = bale_load_channel_map();
    $entry = ['id'=>$id, 'username'=>$username, 'title'=>(string)$title, 'updated_at'=>time()];
    if($username !== '') $map[strtolower($username)] = $entry;
    $map[$id] = $entry;
    @bot_data_put_json(bale_channel_map_file(), $map);
}
function bale_chat_object_to_ref($chat){
    if(!is_object($chat)) return '';
    $id = isset($chat->id) ? (string)$chat->id : '';
    $username = isset($chat->username) ? normalize_bale_channel_username($chat->username) : '';
    $title = isset($chat->title) ? (string)$chat->title : '';
    if($id !== '') bale_save_channel_map_entry($username, $id, $title);
    return $id !== '' ? $id : $username;
}
function bale_extract_channel_ref_from_message($message){
    if(is_object($message)){
        if(isset($message->forward_from_chat) && is_object($message->forward_from_chat)){
            return bale_chat_object_to_ref($message->forward_from_chat);
        }
        if(isset($message->sender_chat) && is_object($message->sender_chat)){
            return bale_chat_object_to_ref($message->sender_chat);
        }
        if(isset($message->chat) && is_object($message->chat) && isset($message->chat->type) && in_array($message->chat->type, ['channel','supergroup'], true)){
            return bale_chat_object_to_ref($message->chat);
        }
        if(isset($message->text)) return normalize_bale_channel_username($message->text);
        if(isset($message->caption)) return normalize_bale_channel_username($message->caption);
    }
    return '';
}
function bale_channel_failure_cache_key($channelRef){
    $channelRef = function_exists('normalize_bale_channel_username') ? normalize_bale_channel_username($channelRef) : trim((string)$channelRef);
    return 'channel_api_bad_' . sha1(strtolower((string)$channelRef));
}
function bale_channel_failure_should_cooldown($description){
    $description = strtolower((string)$description);
    if($description === '') return false;
    $patterns = [
        'member list is inaccessible',
        'chat not found',
        'channel not found',
        'bot is not a member',
        'bot was kicked',
        'not enough rights',
        'forbidden',
        'have no rights',
        'need administrator',
        'admin',
        'administrator',
        'کانال یافت نشد',
        'دسترسی',
        'ادمین',
    ];
    foreach($patterns as $pattern){ if(strpos($description, $pattern) !== false) return true; }
    return false;
}
function bale_channel_failure_note($channelRef, $description, $ttl = 900){
    $channelRef = function_exists('normalize_bale_channel_username') ? normalize_bale_channel_username($channelRef) : trim((string)$channelRef);
    if($channelRef === '') return false;
    $payload = ['channel'=>$channelRef, 'description'=>substr((string)$description,0,250), 'until'=>time()+max(60,(int)$ttl), 'created_at'=>time()];
    if(function_exists('bot_scale_cache_set')) return bot_scale_cache_set('channel_failure', bale_channel_failure_cache_key($channelRef), $payload);
    $dir = defined('BOT_ROOT_DIR') ? BOT_ROOT_DIR.'/cache' : __DIR__.'/../../cache';
    if(!is_dir($dir)) @mkdir($dir,0775,true);
    return @file_put_contents($dir.'/'.bale_channel_failure_cache_key($channelRef).'.json', json_encode($payload, JSON_UNESCAPED_UNICODE), LOCK_EX) !== false;
}
function bale_channel_failure_in_cooldown($channelRef){
    $channelRef = function_exists('normalize_bale_channel_username') ? normalize_bale_channel_username($channelRef) : trim((string)$channelRef);
    if($channelRef === '') return false;
    $payload = null;
    if(function_exists('bot_scale_cache_get')) $payload = bot_scale_cache_get('channel_failure', bale_channel_failure_cache_key($channelRef), 3600);
    if(!is_array($payload)){
        $file = (defined('BOT_ROOT_DIR') ? BOT_ROOT_DIR.'/cache/' : __DIR__.'/../../cache/') . bale_channel_failure_cache_key($channelRef).'.json';
        if(is_file($file)) $payload = json_decode((string)@file_get_contents($file), true);
    }
    return is_array($payload) && (int)($payload['until'] ?? 0) > time();
}

function bale_get_self_bot_info(){
    static $self = null;
    if($self !== null) return $self;
    $res = bot('getMe');
    $self = [
        'ok' => isset($res->ok) ? (bool)$res->ok : false,
        'id' => isset($res->result->id) ? (string)$res->result->id : '',
        'username' => isset($res->result->username) ? strtolower((string)$res->result->username) : '',
        'description' => $res->description ?? null,
    ];
    return $self;
}
function bale_get_chat_id_for_channel($channelRef){
    $channelRef = normalize_bale_channel_username($channelRef);
    if($channelRef === '') return '';
    if(is_bale_numeric_chat_id($channelRef)) return $channelRef;

    $map = bale_load_channel_map();
    $key = strtolower($channelRef);
    if(isset($map[$key]['id']) && $map[$key]['id'] !== '') return (string)$map[$key]['id'];

    foreach(['@'.$channelRef, $channelRef] as $chatId){
        $res = bot('getChat', ['chat_id'=>$chatId]);
        if(isset($res->ok) && $res->ok && isset($res->result) && isset($res->result->id)){
            $id = (string)$res->result->id;
            $username = isset($res->result->username) ? (string)$res->result->username : $channelRef;
            $title = isset($res->result->title) ? (string)$res->result->title : '';
            bale_save_channel_map_entry($username, $id, $title);
            return $id;
        }
    }
    return '';
}
function bale_chat_id_candidates_for_channel($channelRef){
    $channelRef = normalize_bale_channel_username($channelRef);
    if($channelRef === '') return [];
    $out = [];
    $resolvedId = bale_get_chat_id_for_channel($channelRef);
    if($resolvedId !== '') $out[] = $resolvedId;
    if(!is_bale_numeric_chat_id($channelRef)){
        $out[] = '@'.$channelRef;
        $out[] = $channelRef;
    }
    return array_values(array_unique(array_filter($out, function($v){ return $v !== ''; })));
}
function bale_get_channel_member_status_info($channelRef, $userId){
    $last = ['ok'=>false, 'status'=>null, 'description'=>'not_checked', 'chat_id_used'=>null];
    if(function_exists('bale_channel_failure_in_cooldown') && bale_channel_failure_in_cooldown($channelRef)){
        return ['ok'=>false, 'status'=>null, 'description'=>'channel_check_cooldown', 'chat_id_used'=>null];
    }
    foreach(bale_chat_id_candidates_for_channel($channelRef) as $chatId){
        $res = bot('getChatMember', ['chat_id'=>$chatId, 'user_id'=>$userId]);
        $status = $res->result->status ?? null;
        $last = [
            'ok'=>isset($res->ok) ? (bool)$res->ok : false,
            'status'=>$status,
            'description'=>$res->description ?? null,
            'chat_id_used'=>$chatId,
            'api'=>$res,
        ];
        if($status !== null) return $last;
        $desc = (string)($res->description ?? '');
        if(function_exists('bale_channel_failure_should_cooldown') && bale_channel_failure_should_cooldown($desc)){
            if(function_exists('bale_channel_failure_note')) bale_channel_failure_note($channelRef, $desc);
            break;
        }
    }
    return $last;
}

function bale_get_channel_member_status_cached($channelRef, $userId, $ttl = null, $force = false){
    $ttl = $ttl === null ? (defined('BOT_FORCED_JOIN_CACHE_TTL') ? (int)BOT_FORCED_JOIN_CACHE_TTL : 90) : (int)$ttl;
    $channelRef = normalize_bale_channel_username($channelRef);
    $userId = (string)$userId;
    if($channelRef === '' || $userId === '' || $userId === '0') return null;
    static $memory = [];
    $key = strtolower($channelRef).'|'.$userId;
    if(!$force && isset($memory[$key]) && (time() - (int)$memory[$key]['ts']) <= $ttl) return $memory[$key]['status'];
    if(!$force && function_exists('bot_scale_cache_get')){
        $cached = bot_scale_cache_get('join_status', sha1($key), $ttl);
        if(is_array($cached) && array_key_exists('status', $cached)){
            $memory[$key] = ['ts'=>time(), 'status'=>$cached['status']];
            return $cached['status'];
        }
    }
    $info = bale_get_channel_member_status_info($channelRef, $userId);
    $status = $info['status'] ?? null;
    if($status !== null){
        $payload = ['status'=>$status, 'checked_at'=>time(), 'channel'=>$channelRef, 'user_id'=>$userId];
        $memory[$key] = ['ts'=>time(), 'status'=>$status];
        if(function_exists('bot_scale_cache_set')) bot_scale_cache_set('join_status', sha1($key), $payload);
        return $status;
    }
    if(function_exists('bot_scale_cache_get')){
        $cached = bot_scale_cache_get('join_status', sha1($key), max($ttl * 5, 300));
        if(is_array($cached) && array_key_exists('status', $cached)) return $cached['status'];
    }
    return null;
}
function bot_forced_join_missing_channels(array $channels, $userId, $force = false){
    $missing = [];
    foreach($channels as $channelRef){
        $channelRef = trim((string)$channelRef);
        if($channelRef === '') continue;
        $status = function_exists('bale_get_channel_member_status_cached')
            ? bale_get_channel_member_status_cached($channelRef, $userId, null, $force)
            : bale_get_channel_member_status($channelRef, $userId);
        // اگر وضعیت به دلیل خطای کانال/ادمین نبودن ربات نامشخص است، کاربر را پشت قفل جوین نگه ندار.
        if($status === null) continue;
        if(!bale_is_member_status($status)) $missing[] = $channelRef;
    }
    return array_values(array_unique($missing));
}

function bale_get_channel_member_status($channelRef, $userId){
    $info = bale_get_channel_member_status_info($channelRef, $userId);
    return $info['status'] ?? null;
}
function bale_bot_channel_status($channelRef){
    global $id_adady, $boter_id;
    $channelRef = normalize_bale_channel_username($channelRef);
    if(!is_valid_bale_channel_username($channelRef)){
        return ['ok'=>false, 'admin'=>false, 'status'=>null, 'description'=>'invalid_channel_ref', 'method'=>'validate'];
    }

    $self = bale_get_self_bot_info();
    $botIds = [];
    if(!empty($self['id'])) $botIds[] = (string)$self['id'];
    if(!empty($id_adady)) $botIds[] = (string)$id_adady;
    $botIds = array_values(array_unique(array_filter($botIds, function($v){ return $v !== '' && $v !== '[*[idaddy]*]'; })));

    $botUsernames = [];
    if(!empty($self['username'])) $botUsernames[] = strtolower(ltrim($self['username'], '@'));
    if(!empty($boter_id)) $botUsernames[] = strtolower(ltrim((string)$boter_id, '@'));
    $botUsernames = array_values(array_unique(array_filter($botUsernames, function($v){ return $v !== '' && strpos($v, '[*[') === false; })));

    $last = ['ok'=>false, 'admin'=>false, 'status'=>null, 'description'=>null, 'method'=>'getChatMember'];
    foreach(bale_chat_id_candidates_for_channel($channelRef) as $chatId){
        foreach($botIds as $botId){
            $res = bot('getChatMember', ['chat_id'=>$chatId, 'user_id'=>$botId]);
            $status = $res->result->status ?? null;
            $last = [
                'ok'=>isset($res->ok) ? (bool)$res->ok : false,
                'admin'=>bale_is_admin_status($status),
                'status'=>$status,
                'description'=>$res->description ?? null,
                'method'=>'getChatMember',
                'bot_id'=>$botId,
                'chat_id_used'=>$chatId,
            ];
            if($last['admin']) return $last;
        }
    }

    foreach(bale_chat_id_candidates_for_channel($channelRef) as $chatId){
        $admins = bot('getChatAdministrators', ['chat_id'=>$chatId]);
        if(isset($admins->ok) && $admins->ok && isset($admins->result) && is_array($admins->result)){
            foreach($admins->result as $member){
                $u = $member->user ?? null;
                $mid = isset($u->id) ? (string)$u->id : '';
                $mun = isset($u->username) ? strtolower(ltrim((string)$u->username, '@')) : '';
                $status = $member->status ?? 'administrator';
                if(($mid !== '' && in_array($mid, $botIds, true)) || ($mun !== '' && in_array($mun, $botUsernames, true))){
                    return ['ok'=>true, 'admin'=>true, 'status'=>$status, 'description'=>null, 'method'=>'getChatAdministrators', 'bot_id'=>$mid, 'chat_id_used'=>$chatId];
                }
            }
            return ['ok'=>true, 'admin'=>false, 'status'=>'not_in_admin_list', 'description'=>null, 'method'=>'getChatAdministrators', 'chat_id_used'=>$chatId];
        }
        if($last['description'] === null && isset($admins->description)){
            $last['description'] = $admins->description;
            $last['method'] = 'getChatAdministrators';
            $last['chat_id_used'] = $chatId;
        }
    }
    return $last;
}
function is_bale_bot_admin_channel($channelUsername){
    $check = bale_bot_channel_status($channelUsername);
    return !empty($check['admin']);
}
function bale_save_channel_setting_with_check($chatID, &$user, $userID, $file, $label, $channelUsername){
    $channelUsername = normalize_bale_channel_username($channelUsername);
    if(!is_valid_bale_channel_username($channelUsername)){
        send_channel_username_error($chatID);
        return false;
    }
    if(function_exists('bale_channel_is_blocked') && bale_channel_is_blocked($channelUsername)){
        bot('sendMessage',[
            'chat_id'=>$chatID,
            'text'=>"❌ این کانال در لیست مسدودی است و نمی‌تواند به عنوان {$label} تنظیم شود.

".bale_channel_display_name($channelUsername),
            'parse_mode'=>'HTML',
            'reply_markup'=>bale_channel_settings_keyboard()
        ]);
        return false;
    }
    $check = bale_bot_channel_status($channelUsername);
    bot_data_put_text($file, $channelUsername);
    if(in_array($file, ['lib/kodam/channel.txt','lib/kodam/channel2.txt'], true) && function_exists('bale_forced_join_channel_add')){
        bale_forced_join_channel_add($channelUsername, $label, true);
    }
    $user['step'] = 'none';
    saveJson("melat/$userID.json", $user);
    if(!empty($check['admin'])){
        $text = "✅ {$label} به ".bale_channel_display_name($channelUsername)." تنظیم شد.";
    }else{
        $status = $check['status'] ?: 'نامشخص';
        $desc = $check['description'] ?: 'بدون توضیح';
        $text = "⚠️ {$label} به ".bale_channel_display_name($channelUsername)." ذخیره شد، اما API تلگرام ادمین بودن ربات را تأیید نکرد.

".
                "وضعیت دریافتی: {$status}
".
                "توضیح API: {$desc}

".
                "اگر مطمئنی ربات ادمین است، یک‌بار ربات را از کانال حذف و دوباره با دسترسی ادمین اضافه کن. همچنین در config.php مقدار id_adady باید همان عدد قبل از دو‌نقطه توکن باشد.";
    }
    bot('sendMessage',[
        'chat_id'=>$chatID,
        'text'=>$text."

".bale_channel_settings_text(),
        'parse_mode'=>'HTML',
        'reply_to_message_id'=>null,
        'reply_markup'=>bale_channel_settings_keyboard()
    ]);
    return true;
}
function send_channel_username_error($chatID){
    bot('sendMessage',[
        'chat_id'=>$chatID,
        'text'=>"❌ آیدی کانال نامعتبر است.\nآیدی را بدون فاصله بفرستید؛ با @، بدون @، لینک t.me یا شناسه عددی کانال قبول است.\nمثال: my_channel",
        'parse_mode'=>'HTML',
        'reply_markup'=>json_encode(['keyboard'=>[[['text'=>"🔙بازگشت به منو"]]], 'resize_keyboard'=>true, 'one_time_keyboard'=>true])
    ]);
}
function send_bot_not_admin_channel_error($chatID, $channelUsername){
    bot('sendMessage',[
        'chat_id'=>$chatID,
        'text'=>"❌ ربات ادمین کانال @{$channelUsername} نیست یا آیدی کانال اشتباه است.\nاول ربات را داخل کانال ادمین کن، بعد دوباره آیدی کانال را بفرست.",
        'parse_mode'=>'HTML',
        'reply_markup'=>json_encode(['keyboard'=>[[['text'=>"🔙بازگشت به منو"]]], 'resize_keyboard'=>true, 'one_time_keyboard'=>true])
    ]);
}

function bale_channel_display_value($file){
    // SQL-aware read. In full database mode lib/kodam/*.txt may not exist as files,
    // so display must use bot_data_get_text instead of direct file_get_contents().
    $value = function_exists('bot_data_get_text') ? trim((string)bot_data_get_text($file, '')) : (is_file($file) ? trim((string)file_get_contents($file)) : '');
    return bale_channel_display_name($value);
}
function bale_channel_settings_text(){
    $forcedCount = function_exists('bale_forced_join_channels_active') ? count(bale_forced_join_channels_active()) : 0;
    return "گزینه مورد نظر را انتخاب کنید

".
        "📋 کانال تبلیغات: ".bale_channel_display_value('lib/kodam/cht.txt')."
".
        "🎁 کانال کد هدیه: ".bale_channel_display_value('lib/kodam/giftch.txt')."
".
        "🔐 قفل عضویت اجباری: **".$forcedCount." چنل فعال**
".
        "   قدیمی ۱: ".bale_channel_display_value('lib/kodam/channel.txt')."
".
        "   قدیمی ۲: ".bale_channel_display_value('lib/kodam/channel2.txt')."
".
        "⏱ فاصله دریافت مجدد از هر کانال: **".bale_reward_cooldown_days()." روز**
".
        "🏠 دریافت از کانال خود: **".(function_exists('bale_self_reward_status_text') ? bale_self_reward_status_text() : '❌ غیرفعال / هر 3 روز')."**
".
        "🚫 چنل‌های مسدود: **".count(function_exists('bale_blocked_channels_load') ? bale_blocked_channels_load() : [])."**";
}
function bale_channel_settings_keyboard(){
    return json_encode([
        'resize_keyboard'=>true,
        'keyboard'=>[
            [['text'=>"📋تنظیم کانال تبلیغات"],['text'=>"🎁تنظیم کانال کدهدیه"]],
            [['text'=>"➕افزودن چنل قفل"],['text'=>"📋لیست چنل‌های قفل"]],
            [['text'=>"🔐کانال جوین اجباری اول"],['text'=>"🔐کانال جوین اجباری دوم"]],
            [['text'=>"🗑حذف کانال تبلیغات"],['text'=>"🗑حذف کانال کدهدیه"]],
            [['text'=>"🗑حذف همه قفل‌های جوین"],['text'=>"🗑حذف جوین اجباری اول"]],
            [['text'=>"🗑حذف جوین اجباری دوم"]],
            [['text'=>"🔄بررسی فوری لفت‌ها"],['text'=>"⏱فاصله دریافت مجدد"]],
            [['text'=>"🏠دریافت از کانال خود"],['text'=>"📢 تبلیغ بعد از جوین"]],
            [['text'=>"🚫مسدودسازی چنل"],['text'=>"✅رفع مسدودی چنل"]],
            [['text'=>"📋لیست مسدودی چنل‌ها"]],
            [['text'=>"🔙بازگشت به منو"]]
        ]
    ]);
}
