=== run_user.php === prepare("UPDATE users SET is_active = 0 WHERE user_id = ?"); $up->execute([$userId]); } catch (\Throwable $e) {} exit(0); } // 3. پاکسازی ورکرهای زامبی قدیمی و قفل‌های معلق این کاربر if (function_exists('posix_kill')) { $myPid = getmypid(); foreach (glob('/proc/[0-9]*') as $p) { $pid = (int) basename($p); if ($pid === $myPid) continue; $cmd = @file_get_contents($p . '/cmdline'); if ($cmd && strpos($cmd, "user_{$userId}") !== false && strpos($cmd, 'run_user.php') === false) { @posix_kill($pid, 9); } } } foreach (glob($session . '/*.lock') as $lk) { @unlink($lk); } $lockFile = $sessionDir . '/runner.lock'; $lockFp = fopen($lockFile, 'c+'); if (!$lockFp || !flock($lockFp, LOCK_EX | LOCK_NB)) { // پروسس این کاربر در حال حاضر فعال است exit(0); } ftruncate($lockFp, 0); fwrite($lockFp, (string) getmypid()); fflush($lockFp); register_shutdown_function(function() use ($lockFp, $lockFile) { if ($lockFp) { @flock($lockFp, LOCK_UN); @fclose($lockFp); } @unlink($lockFile); }); require_once __DIR__ . '/vendor/autoload.php'; require_once __DIR__ . '/src/Store.php'; require_once __DIR__ . '/src/SelfBotHandler.php'; // مقداردهی اولیه به Store برای همین کاربر Store::setUser($userId); use danog\MadelineProto\Logger; use danog\MadelineProto\Settings; use danog\MadelineProto\Settings\AppInfo; $config = require __DIR__ . '/config.php'; $settings = new Settings; $appInfo = new AppInfo; $appInfo->setApiId((int) $config['api_id']); $appInfo->setApiHash((string) $config['api_hash']); $settings->setAppInfo($appInfo); $settings->getLogger() ->setType(Logger::FILE_LOGGER) ->setExtra($logFile) ->setLevel(Logger::LEVEL_WARNING) ->setMaxSize(3 * 1024 * 1024); echo "[" . date('Y-m-d H:i:s') . "] Starting SelfBot loop for user {$userId}...\n"; try { SelfBotHandler::startAndLoop($session, $settings); } catch (\Throwable $e) { $msg = $e->getMessage(); echo "[" . date('Y-m-d H:i:s') . "] SelfBot loop for user {$userId} ended: " . $msg . "\n"; if (preg_match('/FLOOD_WAIT_(\d+)/', $msg, $m)) { $waitSeconds = (int) $m[1]; file_put_contents($floodFile, (string)(time() + $waitSeconds + 60)); echo "[" . date('Y-m-d H:i:s') . "] Set FLOOD_WAIT until " . date('Y-m-d H:i:s', time() + $waitSeconds + 60) . " for user {$userId}\n"; } elseif (stripos($msg, 'AUTH_KEY_UNREGISTERED') !== false || stripos($msg, 'SESSION_REVOKED') !== false || stripos($msg, 'SESSION_EXPIRED') !== false || stripos($msg, 'USER_DEACTIVATED') !== false) { echo "[" . date('Y-m-d H:i:s') . "] Session revoked for user {$userId}. Deactivating in database.\n"; try { $db = new PDO('sqlite:/home/in8wswn/data/shop_bot.sqlite3'); $up = $db->prepare("UPDATE users SET is_active = 0 WHERE user_id = ?"); $up->execute([$userId]); } catch (\Throwable $e2) {} } } === config.php === getenv('SB_API_ID') ?: 30794508, 'api_hash' => getenv('SB_API_HASH') ?: '9b51a902e3b7876099944147b855aba6', // آیدی عددی شما - برای پنل و دستورات فقط شما اجازه دارید 'owner_id' => (int) (getenv('SB_OWNER_ID') ?: 6512422794), // پیشوند دستورات 'prefix' => '.', // کانالی که توضیحاتش ساعت میگیره (آیدی عددی، باید ادمین باشید) - اختیاری 'clock_channel_id' => (int) (getenv('SB_CLOCK_CHANNEL') ?: 0), // تایم‌زون‌ها به ترتیب چرخش [برچسب فارسی => نام تایم‌زون] 'timezones' => [ 'تهران' => 'Asia/Tehran', 'دبی' => 'Asia/Dubai', 'استانبول' => 'Europe/Istanbul', 'برلین' => 'Europe/Berlin', 'لندن' => 'Europe/London', ], 'random_font' => true, // فاصله آپدیت (ثانیه) - چون روی کرون هر دقیقه اجرا میشه، این مقدار حداقل باید ~60 باشه 'bio_interval' => 60, 'name_interval' => 60, 'channel_interval' => 90, 'warn_limit' => 3, // مدت زمانی که هر اجرای cron_runner.php آنلاین بمونه و گوش بده (ثانیه) 'run_duration' => 50, ];