From 424fd389517b7137834e83fcbec29a6e22ea2e0b Mon Sep 17 00:00:00 2001
From: poryajp <118083331+poryajp@users.noreply.github.com>
Date: Sat, 22 Nov 2025 21:08:39 +0330
Subject: [PATCH 01/18] Update functions.php
---
src/includes/functions.php | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/includes/functions.php b/src/includes/functions.php
index 2390fdb..f19704e 100644
--- a/src/includes/functions.php
+++ b/src/includes/functions.php
@@ -1074,7 +1074,7 @@ function showPlansForCategoryAndServer($chat_id, $category_id, $server_id) {
$message = "ðïž ÙŸÙÙâÙØ§Û Ø³Ø±ÙØ± «{$server_name}» \nÙ
ÙØ¬ÙØ¯Û ØŽÙ
ا: " . number_format($user_balance) . " تÙÙ
اÙ\n\nÙØ·Ùا ÙŸÙÙ Ù
ÙØ±Ø¯ ÙØžØ± Ø®ÙØ¯ را Ø§ÙØªØ®Ø§Øš Ú©ÙÛØ¯:";
$keyboard_buttons = [];
foreach ($active_plans as $plan) {
- $button_text = "{$plan['name']} | {$plan['volume_gb']}GB | " . number_format($plan['price']) . " تÙÙ
اÙ";
+ $button_text = "{$plan['name']} | " . number_format($plan['price']) . " تÙÙ
ا٠| {$plan['volume_gb']} GB";
$keyboard_buttons[] = [['text' => $button_text, 'callback_data' => "buy_plan_{$plan['id']}"]];
}
// ÙØ±Ù
ت callback Ø¬Ø¯ÛØ¯ ØšØ±Ø§Û Ú©Ø¯ تخÙÛÙ: apply_discount_code_{cat_ID}_{srv_ID}
@@ -1247,7 +1247,7 @@ function completePurchase($user_id, $plan_id, $custom_name, $final_price, $disco
$first_name = $user_data['first_name'];
// ساخت ÙØ§Ù
Ú©Ø§Ø±ØšØ±Û Ú©Ø§Ù
Ù Ù Ûکتا ØšØ±Ø§Û ÙŸÙÙ
- $plan['full_username'] = preg_replace('/[^a-zA-Z0-9_.]/', '', $custom_name) . '_user' . $user_id . '_' . time();
+ $plan['full_username'] = $user_id . '_' . rand(10, 99);
$panel_user_data = createPanelUser($plan, $user_id, $plan_id);
@@ -1331,4 +1331,4 @@ function completePurchase($user_id, $plan_id, $custom_name, $final_price, $disco
'success' => false,
'error_message' => "â Ù
ØªØ§Ø³ÙØ§Ù٠در Ø§ÛØ¬Ø§Ø¯ سرÙÛØ³ ØŽÙ
ا Ù
ØŽÚ©ÙÛ ÙŸÛØŽ Ø¢Ù
د. ÙØ·Ùا ؚا ÙŸØŽØªÛØšØ§ÙÛ ØªÙ
اس ØšÚ¯ÛØ±Ûد. Ù
ØšÙØºÛ از ØØ³Ø§Øš ØŽÙ
ا کسر ÙØŽØ¯Ù است."
];
-}
\ No newline at end of file
+}
From 0972da201a9d047c08543f2195db9ea5a9f72d3d Mon Sep 17 00:00:00 2001
From: poryajp <118083331+poryajp@users.noreply.github.com>
Date: Sat, 22 Nov 2025 23:14:40 +0330
Subject: [PATCH 02/18] Update cron.php
---
src/cron.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/cron.php b/src/cron.php
index 7c24b2a..e6fbb19 100644
--- a/src/cron.php
+++ b/src/cron.php
@@ -8,7 +8,7 @@
require_once __DIR__ . '/includes/config.php';
require_once __DIR__ . '/includes/db.php';
require_once __DIR__ . '/includes/functions.php';
-require_once __DIR__ . '/includes/marzban_api.php';
+require_once __DIR__ . '/api/marzban_api.php';
echo "Cron job started at " . date('Y-m-d H:i:s') . "\n";
@@ -128,4 +128,4 @@ function checkInactiveUsers()
echo "An error occurred: " . $e->getMessage() . "\n";
}
-echo "Cron job finished at " . date('Y-m-d H:i:s') . "\n";
\ No newline at end of file
+echo "Cron job finished at " . date('Y-m-d H:i:s') . "\n";
From cdb46eda7d2ed8e4c6e40770326ef30e91e69be6 Mon Sep 17 00:00:00 2001
From: poryajp <118083331+poryajp@users.noreply.github.com>
Date: Tue, 25 Nov 2025 02:03:38 +0330
Subject: [PATCH 03/18] Update README.md
---
README.md | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 2a4b294..93edad5 100644
--- a/README.md
+++ b/README.md
@@ -128,7 +128,26 @@ expand_less
ð€ Ù
؎ارکت در ٟرÙÚÙ
-از Ù
؎ارکت ØŽÙ
ا در ØªÙØ³Ø¹Ù Ù ØšÙØšÙد ÙÛØ²Ø§Ø±Ø¯ ÙŸÙÙ Ø§Ø³ØªÙØšØ§Ù Ù
ÛâÚ©ÙÛÙ
. Ù
ÛâØªÙØ§ÙÛØ¯ از طرÛÙ Pull Request تغÛÛØ±Ø§Øª Ø®ÙØ¯ را ارسا٠کÙÛØ¯ ÛØ§ ؚا ثؚت Issue Ù
ØŽÚ©ÙØ§Øª Ù ÙŸÛØŽÙÙØ§Ø¯Ø§Øª Ø®ÙØ¯ را ؚا Ù
ا در Ù
ÛØ§Ù ØšÚ¯Ø°Ø§Ø±ÛØ¯.
+## ð ØªØºÛÛØ±Ø§Øª Ù Ø§ØµÙØ§ØØ§Øª Ø§Ø®ÛØ± (Recent Updates \u0026 Fixes)
+
+اÛÙ ÙØ³Ø®Ù ؎اÙ
Ù Ø§ØµÙØ§ØØ§Øª Ø²ÛØ± Ø¬ÙØª ØšÙØšÙد عÙ
Ùکرد Ù Ø±ÙØ¹ Ø§ÛØ±Ø§Ø¯Ø§Øª ÙØ³Ø®Ù اصÙÛ Ù
ÛâØšØ§ØŽØ¯:
+
+### 1ïžâ£ Ø§ØµÙØ§Ø ÙØ±Ù
ت ÙØ§Ù
Ú©Ø§Ø±ØšØ±Û (Username Generation)
+- **ÙØ§ÛÙ:** `src/includes/functions.php`
+- **تغÛÛØ±:** اÙÚ¯ÙÛ ØªÙÙÛØ¯ ÙØ§Ù
Ú©Ø§Ø±ØšØ±Û ØšØ±Ø§Û Ø³Ø±ÙÛØ³âÙØ§Û Ø¬Ø¯ÛØ¯ تغÛÛØ± کرد.
+- **ÙØ±Ù
ت Ø¬Ø¯ÛØ¯:** `ChatID` + `_` + `2 Random Digits`
+- **Ù
ثاÙ:** `123456789_45`
+
+### 2ïžâ£ Ø§ØµÙØ§Ø ÙÙ
Ø§ÛØŽ Ø¯Ú©Ù
ÙâÙØ§Û Ø®Ø±ÛØ¯ (RTL Fix)
+- **ÙØ§ÛÙâÙØ§:** `src/includes/functions.php` Ù `src/bot.php`
+- **تغÛÛØ±:** جاÛگا٠«ÙÛÙ
ت» Ù Â«ØØ¬Ù
» در دکÙ
ÙâÙØ§Û ØŽÛØŽÙâØ§Û Ø¬Ø§ØšØ¬Ø§ ؎د.
+- **ÙØ¯Ù:** ØÙ Ù
ØŽÚ©Ù ØšÙÙ
âØ±ÛØ®ØªÚ¯Û Ù
ØªÙ ÙØ§Ø±Ø³Û در تÙگراÙ
(ÙØ±Ø§Ø±Ú¯ÛØ±Û ØµØÛØ Ú©ÙÙ
Ù "تÙÙ
اÙ").
+- **ÙØ±Ù
ت ÙÙ
Ø§ÛØŽ:** `20,000 تÙÙ
ا٠| 10 GB`
+
+### 3ïžâ£ Ø±ÙØ¹ Ø®Ø·Ø§Û Ù
Ø³ÛØ±Ø¯ÙÛ Ú©Ø±ÙÙâØ¬Ø§Øš (Cron Job Path Fix)
+- **ÙØ§ÛÙ:** `src/cron.php`
+- **تغÛÛØ±:** Ø§ØµÙØ§Ø Ù
Ø³ÛØ± ÙØ±Ø§Ø®ÙاÙÛ (Require) ÙØ§ÛÙâÙØ§Û API.
+- **ØªÙØ¶ÛØ:** Ù
Ø³ÛØ± ÙØ§ÛÙâÙØ§ از ÙŸÙØŽÙ ا؎تؚا٠`includes/` ØšÙ ÙŸÙØŽÙ صØÛØ `api/` تغÛÛØ± ÛØ§Ùت تا از Ø®Ø·Ø§Û `PHP Fatal error: Failed opening required` جÙÙÚ¯ÛØ±Û ØŽÙØ¯.
ð Ù
Ø¬ÙØ² (License)
From 542ac61bb331729469aa44bb093503e8e6aa8ccd Mon Sep 17 00:00:00 2001
From: poryajp <118083331+poryajp@users.noreply.github.com>
Date: Fri, 28 Nov 2025 01:27:21 +0330
Subject: [PATCH 04/18] Add files via upload
add web panel
---
src/includes/functions.php | 319 ++++++++------
src/install.php | 646 ++++++++++++++++++++---------
src/web/README.md | 50 +++
src/web/assets/css/style.css | 430 +++++++++++++++++++
src/web/assets/js/main.js | 70 ++++
src/web/dashboard.php | 177 ++++++++
src/web/includes/auth.php | 53 +++
src/web/includes/session.php | 56 +++
src/web/includes/web_functions.php | 182 ++++++++
src/web/index.php | 207 +++++++++
src/web/pages/admins.php | 280 +++++++++++++
src/web/pages/broadcast.php | 480 +++++++++++++++++++++
src/web/pages/categories.php | 147 +++++++
src/web/pages/discount.php | 260 ++++++++++++
src/web/pages/guides.php | 239 +++++++++++
src/web/pages/payments.php | 256 ++++++++++++
src/web/pages/plans.php | 299 +++++++++++++
src/web/pages/servers.php | 369 ++++++++++++++++
src/web/pages/settings.php | 345 +++++++++++++++
src/web/pages/stats.php | 190 +++++++++
src/web/pages/users.php | 324 +++++++++++++++
21 files changed, 5056 insertions(+), 323 deletions(-)
create mode 100644 src/web/README.md
create mode 100644 src/web/assets/css/style.css
create mode 100644 src/web/assets/js/main.js
create mode 100644 src/web/dashboard.php
create mode 100644 src/web/includes/auth.php
create mode 100644 src/web/includes/session.php
create mode 100644 src/web/includes/web_functions.php
create mode 100644 src/web/index.php
create mode 100644 src/web/pages/admins.php
create mode 100644 src/web/pages/broadcast.php
create mode 100644 src/web/pages/categories.php
create mode 100644 src/web/pages/discount.php
create mode 100644 src/web/pages/guides.php
create mode 100644 src/web/pages/payments.php
create mode 100644 src/web/pages/plans.php
create mode 100644 src/web/pages/servers.php
create mode 100644 src/web/pages/settings.php
create mode 100644 src/web/pages/stats.php
create mode 100644 src/web/pages/users.php
diff --git a/src/includes/functions.php b/src/includes/functions.php
index f19704e..44e1fc0 100644
--- a/src/includes/functions.php
+++ b/src/includes/functions.php
@@ -10,7 +10,8 @@
// =====================================================================
-function handleKeyboard($keyboard, $handleMainMenu = false) {
+function handleKeyboard($keyboard, $handleMainMenu = false)
+{
if (USER_INLINE_KEYBOARD) {
if (is_null($keyboard)) {
@@ -24,8 +25,7 @@ function handleKeyboard($keyboard, $handleMainMenu = false) {
]
]
];
- }
- else {
+ } else {
if (isset($keyboard['keyboard'])) {
$keyboard = convertToInlineKeyboard($keyboard);
}
@@ -42,13 +42,13 @@ function handleKeyboard($keyboard, $handleMainMenu = false) {
if (is_null($keyboard)) {
return null;
- }
- else {
+ } else {
return json_encode($keyboard);
}
}
-function convertToInlineKeyboard($keyboard) {
+function convertToInlineKeyboard($keyboard)
+{
$inlineKeyboard = [];
if (isset($keyboard['keyboard'])) {
@@ -66,15 +66,15 @@ function convertToInlineKeyboard($keyboard) {
$inlineKeyboard[] = $inlineRow;
}
}
- }
- else {
+ } else {
return null;
}
return ['inline_keyboard' => $inlineKeyboard];
}
-function array_str_contains(array $array, string|array $needle): bool {
+function array_str_contains(array $array, string|array $needle): bool
+{
if (is_array($needle)) {
foreach ($needle as $n) {
if (array_str_contains($array, $n)) {
@@ -89,15 +89,15 @@ function array_str_contains(array $array, string|array $needle): bool {
if (array_str_contains($item, $needle)) {
return true;
}
- }
- elseif (is_string($item) && stripos($item, $needle) !== false) {
+ } elseif (is_string($item) && stripos($item, $needle) !== false) {
return true;
}
}
return false;
}
-function sendMessage($chat_id, $text, $keyboard = null, $handleMainMenu = false) {
+function sendMessage($chat_id, $text, $keyboard = null, $handleMainMenu = false)
+{
$params = ['chat_id' => $chat_id, 'text' => $text, 'reply_markup' => handleKeyboard($keyboard, $handleMainMenu), 'parse_mode' => 'HTML'];
global $update, $oneTimeEdit;
@@ -111,51 +111,56 @@ function sendMessage($chat_id, $text, $keyboard = null, $handleMainMenu = false)
return apiRequest('sendMessage', $params);
}
return $result;
- }
- else {
+ } else {
return apiRequest('sendMessage', $params);
}
}
-function forwardMessage($to_chat_id, $from_chat_id, $message_id) {
+function forwardMessage($to_chat_id, $from_chat_id, $message_id)
+{
$params = ['chat_id' => $to_chat_id, 'from_chat_id' => $from_chat_id, 'message_id' => $message_id];
return apiRequest('forwardMessage', $params);
}
-function sendPhoto($chat_id, $photo, $caption, $keyboard = null) {
+function sendPhoto($chat_id, $photo, $caption, $keyboard = null)
+{
$params = ['chat_id' => $chat_id, 'photo' => $photo, 'caption' => $caption, 'reply_markup' => handleKeyboard($keyboard), 'parse_mode' => 'HTML'];
return apiRequest('sendPhoto', $params);
}
-function editMessageText($chat_id, $message_id, $text, $keyboard = null) {
+function editMessageText($chat_id, $message_id, $text, $keyboard = null)
+{
$params = ['chat_id' => $chat_id, 'message_id' => $message_id, 'text' => $text, 'reply_markup' => handleKeyboard($keyboard), 'parse_mode' => 'HTML'];
global $oneTimeEdit;
if (USER_INLINE_KEYBOARD && $oneTimeEdit) {
$oneTimeEdit = false;
return apiRequest('editMessageText', $params);
- }
- else {
-
+ } else {
+
unset($params['message_id']);
return apiRequest('sendMessage', $params);
}
}
-function editMessageCaption($chat_id, $message_id, $caption, $keyboard = null) {
+function editMessageCaption($chat_id, $message_id, $caption, $keyboard = null)
+{
$params = ['chat_id' => $chat_id, 'message_id' => $message_id, 'caption' => $caption, 'reply_markup' => handleKeyboard($keyboard), 'parse_mode' => 'HTML'];
return apiRequest('editMessageCaption', $params);
}
-function deleteMessage($chat_id, $message_id) {
+function deleteMessage($chat_id, $message_id)
+{
global $update, $oneTimeEdit;
- if (USER_INLINE_KEYBOARD && !$oneTimeEdit && isset($update['callback_query']['message']['message_id']) && $update['callback_query']['message']['message_id'] == $message_id) return false;
+ if (USER_INLINE_KEYBOARD && !$oneTimeEdit && isset($update['callback_query']['message']['message_id']) && $update['callback_query']['message']['message_id'] == $message_id)
+ return false;
$params = ['chat_id' => $chat_id, 'message_id' => $message_id];
return apiRequest('deleteMessage', $params);
}
-function apiRequest($method, $params = []) {
+function apiRequest($method, $params = [])
+{
global $apiRequest;
$apiRequest = true;
@@ -180,7 +185,8 @@ function apiRequest($method, $params = []) {
// =====================================================================
// --- Ù
Ø¯ÛØ±Ûت کارؚرا٠---
-function getUserData($chat_id, $first_name = 'کارؚر') {
+function getUserData($chat_id, $first_name = 'کارؚر')
+{
pdo()
->prepare("UPDATE users SET last_seen_at = CURRENT_TIMESTAMP, reminder_sent = 0 WHERE chat_id = ?")
->execute([$chat_id]);
@@ -191,7 +197,7 @@ function getUserData($chat_id, $first_name = 'کارؚر') {
if (!$user) {
$settings = getSettings();
- $welcome_gift = (int)($settings['welcome_gift_balance'] ?? 0);
+ $welcome_gift = (int) ($settings['welcome_gift_balance'] ?? 0);
$stmt = pdo()->prepare("INSERT INTO users (chat_id, first_name, balance, user_state) VALUES (?, ?, ?, 'main_menu')");
$stmt->execute([$chat_id, $first_name, $welcome_gift]);
@@ -211,47 +217,53 @@ function getUserData($chat_id, $first_name = 'کارؚر') {
return $user;
}
-function updateUserData($chat_id, $state, $data = []) {
+function updateUserData($chat_id, $state, $data = [])
+{
$state_data_json = json_encode($data, JSON_UNESCAPED_UNICODE);
$stmt = pdo()->prepare("UPDATE users SET user_state = ?, state_data = ? WHERE chat_id = ?");
$stmt->execute([$state, $state_data_json, $chat_id]);
}
-function updateUserBalance($chat_id, $amount, $operation = 'add') {
+function updateUserBalance($chat_id, $amount, $operation = 'add')
+{
if ($operation == 'add') {
$stmt = pdo()->prepare("UPDATE users SET balance = balance + ? WHERE chat_id = ?");
- }
- else {
+ } else {
$stmt = pdo()->prepare("UPDATE users SET balance = balance - ? WHERE chat_id = ?");
}
$stmt->execute([$amount, $chat_id]);
}
-function setUserStatus($chat_id, $status) {
+function setUserStatus($chat_id, $status)
+{
$stmt = pdo()->prepare("UPDATE users SET status = ? WHERE chat_id = ?");
$stmt->execute([$status, $chat_id]);
}
-function getAllUsers() {
+function getAllUsers()
+{
return pdo()
->query("SELECT chat_id FROM users WHERE status = 'active'")
->fetchAll(PDO::FETCH_COLUMN);
}
-function increaseAllUsersBalance($amount) {
+function increaseAllUsersBalance($amount)
+{
$stmt = pdo()->prepare("UPDATE users SET balance = balance + ? WHERE status = 'active'");
$stmt->execute([$amount]);
return $stmt->rowCount();
}
-function resetAllUsersTestCount() {
+function resetAllUsersTestCount()
+{
$stmt = pdo()->prepare("UPDATE users SET test_config_count = 0");
$stmt->execute();
return $stmt->rowCount();
}
// --- Ù
Ø¯ÛØ±Ûت ادÙ
ÛÙâÙØ§ ---
-function getAdmins() {
+function getAdmins()
+{
$stmt = pdo()->prepare("SELECT * FROM admins WHERE is_super_admin = 0");
$stmt->execute();
$admins_from_db = $stmt->fetchAll(PDO::FETCH_ASSOC);
@@ -265,22 +277,26 @@ function getAdmins() {
return $admins;
}
-function addAdmin($chat_id, $first_name) {
+function addAdmin($chat_id, $first_name, $permissions = [])
+{
$stmt = pdo()->prepare("INSERT INTO admins (chat_id, first_name, permissions, is_super_admin) VALUES (?, ?, ?, ?)");
- return $stmt->execute([$chat_id, $first_name, json_encode([]), 0]);
+ return $stmt->execute([$chat_id, $first_name, json_encode($permissions), 0]);
}
-function removeAdmin($chat_id) {
+function removeAdmin($chat_id)
+{
$stmt = pdo()->prepare("DELETE FROM admins WHERE chat_id = ? AND is_super_admin = 0");
return $stmt->execute([$chat_id]);
}
-function updateAdminPermissions($chat_id, $permissions) {
+function updateAdminPermissions($chat_id, $permissions)
+{
$stmt = pdo()->prepare("UPDATE admins SET permissions = ? WHERE chat_id = ?");
return $stmt->execute([json_encode($permissions), $chat_id]);
}
-function isUserAdmin($chat_id) {
+function isUserAdmin($chat_id)
+{
if ($chat_id == ADMIN_CHAT_ID) {
return true;
}
@@ -289,7 +305,8 @@ function isUserAdmin($chat_id) {
return $stmt->fetchColumn() > 0;
}
-function hasPermission($chat_id, $permission) {
+function hasPermission($chat_id, $permission)
+{
if ($chat_id == ADMIN_CHAT_ID) {
return true;
}
@@ -306,7 +323,8 @@ function hasPermission($chat_id, $permission) {
}
// --- Ù
Ø¯ÛØ±Ûت ØªÙØžÛÙ
ات ---
-function getSettings() {
+function getSettings()
+{
$stmt = pdo()->query("SELECT * FROM settings");
$settings_from_db = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
@@ -342,7 +360,8 @@ function getSettings() {
return $settings_from_db;
}
-function saveSettings($settings) {
+function saveSettings($settings)
+{
foreach ($settings as $key => $value) {
if (is_array($value)) {
$value = json_encode($value, JSON_UNESCAPED_UNICODE);
@@ -353,7 +372,8 @@ function saveSettings($settings) {
}
// --- Ù
Ø¯ÛØ±Ûت دستÙâØšÙØ¯ÛâÙØ§Ø ÙŸÙÙâÙØ§ ٠سرÙÛØ³âÙØ§ ---
-function getCategories($only_active = false) {
+function getCategories($only_active = false)
+{
$sql = "SELECT * FROM categories";
if ($only_active) {
$sql .= " WHERE status = 'active'";
@@ -363,31 +383,36 @@ function getCategories($only_active = false) {
->fetchAll(PDO::FETCH_ASSOC);
}
-function getPlans() {
+function getPlans()
+{
return pdo()
->query("SELECT * FROM plans WHERE is_test_plan = 0")
->fetchAll(PDO::FETCH_ASSOC);
}
-function getPlansForCategory($category_id) {
+function getPlansForCategory($category_id)
+{
$stmt = pdo()->prepare("SELECT * FROM plans WHERE category_id = ? AND status = 'active' AND is_test_plan = 0");
$stmt->execute([$category_id]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
-function getPlanById($plan_id) {
+function getPlanById($plan_id)
+{
$stmt = pdo()->prepare("SELECT * FROM plans WHERE id = ?");
$stmt->execute([$plan_id]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
-function getTestPlan() {
+function getTestPlan()
+{
return pdo()
->query("SELECT * FROM plans WHERE is_test_plan = 1 AND status = 'active' LIMIT 1")
->fetch(PDO::FETCH_ASSOC);
}
-function getUserServices($chat_id) {
+function getUserServices($chat_id)
+{
$stmt = pdo()->prepare("
SELECT s.*, p.name as plan_name
FROM services s
@@ -399,12 +424,14 @@ function getUserServices($chat_id) {
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
-function saveUserService($chat_id, $serviceData) {
+function saveUserService($chat_id, $serviceData)
+{
$stmt = pdo()->prepare("INSERT INTO services (owner_chat_id, server_id, marzban_username, custom_name, plan_id, sub_url, expire_timestamp, volume_gb) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$chat_id, $serviceData['server_id'], $serviceData['username'], $serviceData['custom_name'], $serviceData['plan_id'], $serviceData['sub_url'], $serviceData['expire_timestamp'], $serviceData['volume_gb']]);
}
-function deleteUserService($chat_id, $username, $server_id) {
+function deleteUserService($chat_id, $username, $server_id)
+{
$stmt = pdo()->prepare("DELETE FROM services WHERE owner_chat_id = ? AND marzban_username = ? AND server_id = ?");
return $stmt->execute([$chat_id, $username, $server_id]);
}
@@ -413,7 +440,8 @@ function deleteUserService($chat_id, $username, $server_id) {
// --- ØªÙØ§ØšØ¹ Ú©Ù
Ú©Û Ù Ø¹Ù
ÙÙ
Û ---
// =====================================================================
-function getPermissionMap() {
+function getPermissionMap()
+{
return [
'manage_categories' => 'ð Ù
Ø¯ÛØ±Ûت دستÙâØšÙØ¯ÛâÙØ§',
'manage_plans' => 'ð Ù
Ø¯ÛØ±Ûت ÙŸÙÙâÙØ§',
@@ -431,7 +459,8 @@ function getPermissionMap() {
];
}
-function checkJoinStatus($user_id) {
+function checkJoinStatus($user_id)
+{
$settings = getSettings();
$channel_id = $settings['join_channel_id'];
if ($settings['join_channel_status'] !== 'on' || empty($channel_id)) {
@@ -445,18 +474,21 @@ function checkJoinStatus($user_id) {
return false;
}
-function generateQrCodeUrl($text) {
+function generateQrCodeUrl($text)
+{
return 'https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=' . urlencode($text);
}
-function formatBytes($bytes, $precision = 2) {
+function formatBytes($bytes, $precision = 2)
+{
if ($bytes <= 0) {
return "0 GB";
}
return round(floatval($bytes) / pow(1024, 3), $precision) . ' GB';
}
-function calculateIncomeStats() {
+function calculateIncomeStats()
+{
$stats = [
'today' =>
pdo()
@@ -482,7 +514,8 @@ function calculateIncomeStats() {
// --- ØªÙØ§ØšØ¹ ÙÙ
Ø§ÛØŽ Ù
ÙÙÙØ§ ---
// =====================================================================
-function generateGuideList($chat_id) {
+function generateGuideList($chat_id)
+{
$stmt = pdo()->query("SELECT id, button_name, status FROM guides ORDER BY id DESC");
$guides = $stmt->fetchAll(PDO::FETCH_ASSOC);
@@ -506,7 +539,8 @@ function generateGuideList($chat_id) {
}
}
-function showGuideSelectionMenu($chat_id) {
+function showGuideSelectionMenu($chat_id)
+{
$stmt = pdo()->query("SELECT id, button_name FROM guides WHERE status = 'active' ORDER BY id ASC");
$guides = $stmt->fetchAll(PDO::FETCH_ASSOC);
@@ -524,7 +558,8 @@ function showGuideSelectionMenu($chat_id) {
sendMessage($chat_id, $message, ['inline_keyboard' => $keyboard_buttons]);
}
-function generateDiscountCodeList($chat_id) {
+function generateDiscountCodeList($chat_id)
+{
$stmt = pdo()->query("SELECT * FROM discount_codes ORDER BY id DESC");
$codes = $stmt->fetchAll(PDO::FETCH_ASSOC);
@@ -553,7 +588,8 @@ function generateDiscountCodeList($chat_id) {
}
}
-function generateCategoryList($chat_id) {
+function generateCategoryList($chat_id)
+{
$categories = getCategories();
if (empty($categories)) {
sendMessage($chat_id, "ÙÛÚ Ø¯Ø³ØªÙâØšÙØ¯ÛâØ§Û ÛØ§Ùت ÙØŽØ¯.");
@@ -574,7 +610,8 @@ function generateCategoryList($chat_id) {
}
}
-function generatePlanList($chat_id) {
+function generatePlanList($chat_id)
+{
$plans = pdo()
->query("SELECT p.*, s.name as server_name, s.type as server_type FROM plans p LEFT JOIN servers s ON p.server_id = s.id ORDER BY p.is_test_plan DESC, p.id ASC")
->fetchAll(PDO::FETCH_ASSOC);
@@ -597,19 +634,18 @@ function generatePlanList($chat_id) {
$plan_info = "";
if ($plan['is_test_plan']) {
$plan_info .= "𧪠(ÙŸÙ٠تست) {$plan['name']} \n";
- }
- else {
+ } else {
$plan_info .= "{$status_icon} {$plan['name']} \n";
}
$plan_info .= "â«ïž Ø³Ø±ÙØ±: {$server_name} \n";
-
+
if ($plan['server_type'] === 'sanaei' && !empty($plan['inbound_id'])) {
$plan_info .= "â«ïž اÛÙØšØ§Ùد: {$plan['inbound_id']} \n";
} elseif ($plan['server_type'] === 'marzneshin' && !empty($plan['marzneshin_service_id'])) {
$plan_info .= "â«ïž سرÙÛØ³: {$plan['marzneshin_service_id']} \n";
}
-
+
$plan_info .= "â«ïž دستÙâØšÙØ¯Û: {$cat_name}\n" . "â«ïž ÙÛÙ
ت: " . number_format($plan['price']) . " تÙÙ
اÙ\n" . "â«ïž ØØ¬Ù
: {$plan['volume_gb']} Ú¯ÛÚ¯Ø§ØšØ§ÛØª | " . "Ù
دت: {$plan['duration_days']} Ø±ÙØ²\n";
if ($plan['purchase_limit'] > 0) {
@@ -622,8 +658,7 @@ function generatePlanList($chat_id) {
if ($plan['is_test_plan']) {
$keyboard_buttons[] = [['text' => 'âïž ØªØšØ¯ÛÙ ØšÙ ÙŸÙ٠عادÛ', 'callback_data' => "make_plan_normal_{$plan_id}"]];
- }
- else {
+ } else {
$keyboard_buttons[] = [['text' => 'ð§ª ØªÙØžÛÙ
ؚ٠عÙÙØ§Ù ÙŸÙ٠تست', 'callback_data' => "set_as_test_plan_{$plan_id}"]];
}
@@ -635,7 +670,8 @@ function generatePlanList($chat_id) {
}
}
-function showServersForCategory($chat_id, $category_id) {
+function showServersForCategory($chat_id, $category_id)
+{
$category_stmt = pdo()->prepare("SELECT name FROM categories WHERE id = ?");
$category_stmt->execute([$category_id]);
$category_name = $category_stmt->fetchColumn();
@@ -662,14 +698,15 @@ function showServersForCategory($chat_id, $category_id) {
$message = "ðïž Ø¯Ø³ØªÙâØšÙØ¯Û «{$category_name}» \n\nÙØ·ÙØ§Ù Ø³Ø±ÙØ± (ÙÙÚ©ÛØŽÙ) Ù
ÙØ±Ø¯ ÙØžØ± Ø®ÙØ¯ را Ø§ÙØªØ®Ø§Øš Ú©ÙÛØ¯:";
$keyboard_buttons = [];
foreach ($servers as $server) {
-
+
$keyboard_buttons[] = [['text' => "ð¥ {$server['name']}", 'callback_data' => "show_plans_cat_{$category_id}_srv_{$server['id']}"]];
}
$keyboard_buttons[] = [['text' => 'âïž ØšØ§Ø²Ú¯ØŽØª ؚ٠دستÙâØšÙØ¯ÛâÙØ§', 'callback_data' => 'back_to_categories']];
sendMessage($chat_id, $message, ['inline_keyboard' => $keyboard_buttons]);
}
-function showAdminManagementMenu($chat_id) {
+function showAdminManagementMenu($chat_id)
+{
$admins = getAdmins();
$message = "ðšâðŒ Ù
Ø¯ÛØ±Ûت ادÙ
ÛÙâÙØ§ \n\nدر اÛ٠ؚخ؎ Ù
ÛâØªÙØ§ÙÛØ¯ ادÙ
ÛÙâÙØ§Û رؚات ٠دسترسÛâÙØ§Û Ø¢ÙâÙØ§ را Ù
Ø¯ÛØ±Ûت Ú©ÙÛØ¯. (ØØ¯Ø§Ú©Ø«Ø± Û±Û° ادÙ
ÛÙ)";
$keyboard_buttons = [];
@@ -690,7 +727,8 @@ function showAdminManagementMenu($chat_id) {
sendMessage($chat_id, $message, ['inline_keyboard' => $keyboard_buttons]);
}
-function showPermissionEditor($chat_id, $message_id, $target_admin_id) {
+function showPermissionEditor($chat_id, $message_id, $target_admin_id)
+{
$admins = getAdmins();
$target_admin = $admins[$target_admin_id] ?? null;
if (!$target_admin) {
@@ -725,7 +763,8 @@ function showPermissionEditor($chat_id, $message_id, $target_admin_id) {
editMessageText($chat_id, $message_id, $message, ['inline_keyboard' => $keyboard_buttons]);
}
-function handleMainMenu($chat_id, $first_name, $is_start_command = false) {
+function handleMainMenu($chat_id, $first_name, $is_start_command = false)
+{
$isAnAdmin = isUserAdmin($chat_id);
$user_data = getUserData($chat_id, $first_name);
@@ -733,8 +772,7 @@ function handleMainMenu($chat_id, $first_name, $is_start_command = false) {
if ($is_start_command) {
$message = "Ø³ÙØ§Ù
$first_name Ø¹Ø²ÛØ²!\nؚ٠رؚات ÙØ±ÙØŽ کاÙÙÛÚ¯ Ø®ÙØŽ Ø¢Ù
Ø¯ÛØ¯. ð¹";
- }
- else {
+ } else {
$message = "ØšÙ Ù
ÙÙÛ Ø§ØµÙÛ ØšØ§Ø²Ú¯ØŽØªÛØ¯. ÙØ·Ùا گزÛÙÙ Ù
ÙØ±Ø¯ ÙØžØ± را Ø§ÙØªØ®Ø§Øš Ú©ÙÛØ¯.";
}
@@ -754,8 +792,7 @@ function handleMainMenu($chat_id, $first_name, $is_start_command = false) {
if ($admin_view_mode === 'admin') {
if ($is_start_command) {
$message = "ادÙ
ÛÙ Ø¹Ø²ÛØ²Ø ØšÙ ÙŸÙÙ Ù
Ø¯ÛØ±Ûت Ø®ÙØŽ Ø¢Ù
Ø¯ÛØ¯.";
- }
- else {
+ } else {
$message = "ØšÙ ÙŸÙÙ Ù
Ø¯ÛØ±Ûت ØšØ§Ø²Ú¯ØŽØªÛØ¯.";
}
$admin_keyboard = [];
@@ -778,7 +815,7 @@ function handleMainMenu($chat_id, $first_name, $is_start_command = false) {
}
if (hasPermission($chat_id, 'manage_payment')) {
$rows[3][] = ['text' => 'ð³ Ù
Ø¯ÛØ±Ûت ٟرداخت'];
- $rows[3][] = ['text' => 'ð³ Ù
Ø¯ÛØ±Ûت درگا٠ٟرداخت'];
+ $rows[3][] = ['text' => 'ð³ Ù
Ø¯ÛØ±Ûت درگا٠ٟرداخت'];
}
if (hasPermission($chat_id, 'manage_marzban')) {
$rows[4][] = ['text' => 'ð Ù
Ø¯ÛØ±Ûت Ø³Ø±ÙØ±Ùا'];
@@ -810,8 +847,7 @@ function handleMainMenu($chat_id, $first_name, $is_start_command = false) {
}
$admin_keyboard[] = [['text' => 'â©ïž ؚازگ؎ت ØšÙ Ù
ÙÙÛ Ú©Ø§Ø±ØšØ±Û']];
$keyboard_buttons = $admin_keyboard;
- }
- else {
+ } else {
$keyboard_buttons[] = [['text' => 'ð ÙØ±Ùد ØšÙ ÙŸÙÙ Ù
Ø¯ÛØ±Ûت']];
}
}
@@ -830,8 +866,7 @@ function handleMainMenu($chat_id, $first_name, $is_start_command = false) {
'text' => 'ð ',
'reply_markup' => json_encode(['remove_keyboard' => true])
]), true)['result']['message_id'];
- }
- elseif (!USER_INLINE_KEYBOARD && $inline_keyboard == 1) {
+ } elseif (!USER_INLINE_KEYBOARD && $inline_keyboard == 1) {
$stmt = pdo()->prepare("UPDATE users SET inline_keyboard = '0' WHERE chat_id = ?");
$stmt->execute([$chat_id]);
}
@@ -847,7 +882,8 @@ function handleMainMenu($chat_id, $first_name, $is_start_command = false) {
}
-function showVerificationManagementMenu($chat_id) {
+function showVerificationManagementMenu($chat_id)
+{
$settings = getSettings();
$current_method = $settings['verification_method'];
$iran_only_icon = $settings['verification_iran_only'] == 'on' ? 'ð®ð·' : 'ð';
@@ -855,8 +891,7 @@ function showVerificationManagementMenu($chat_id) {
$method_text = 'ØºÛØ±ÙعاÙ';
if ($current_method == 'phone') {
$method_text = 'ØŽÙ
ار٠تÙÙÙ';
- }
- elseif ($current_method == 'button') {
+ } elseif ($current_method == 'button') {
$method_text = 'دکÙ
Ù ØŽÛØŽÙâØ§Û';
}
@@ -882,8 +917,7 @@ function showVerificationManagementMenu($chat_id) {
$message_id = $update['callback_query']['message']['message_id'] ?? null;
if ($message_id) {
editMessageText($chat_id, $message_id, $message, $keyboard);
- }
- else {
+ } else {
sendMessage($chat_id, $message, $keyboard);
}
}
@@ -892,7 +926,8 @@ function showVerificationManagementMenu($chat_id) {
// --- ØªÙØ§ØšØ¹ Ø§ÙØªØ²Ø§Ø¹Û ØšØ±Ø§Û Ù
Ø¯ÛØ±Ûت ÙŸÙÙâÙØ§ ---
// =====================================================================
-function getPanelUser($username, $server_id) {
+function getPanelUser($username, $server_id)
+{
$stmt = pdo()->prepare("SELECT type FROM servers WHERE id = ?");
$stmt->execute([$server_id]);
$type = $stmt->fetchColumn();
@@ -909,7 +944,8 @@ function getPanelUser($username, $server_id) {
}
}
-function createPanelUser($plan, $chat_id, $plan_id) {
+function createPanelUser($plan, $chat_id, $plan_id)
+{
$stmt = pdo()->prepare("SELECT type FROM servers WHERE id = ?");
$stmt->execute([$plan['server_id']]);
$type = $stmt->fetchColumn();
@@ -926,7 +962,8 @@ function createPanelUser($plan, $chat_id, $plan_id) {
}
}
-function deletePanelUser($username, $server_id) {
+function deletePanelUser($username, $server_id)
+{
$stmt = pdo()->prepare("SELECT type FROM servers WHERE id = ?");
$stmt->execute([$server_id]);
$type = $stmt->fetchColumn();
@@ -943,7 +980,8 @@ function deletePanelUser($username, $server_id) {
}
}
-function modifyPanelUser($username, $server_id, $data) {
+function modifyPanelUser($username, $server_id, $data)
+{
$stmt = pdo()->prepare("SELECT type FROM servers WHERE id = ?");
$stmt->execute([$server_id]);
$type = $stmt->fetchColumn();
@@ -994,32 +1032,34 @@ function showPlanEditor($chat_id, $message_id, $plan_id, $prompt = null)
editMessageText($chat_id, $message_id, $message_text, $keyboard);
}
-function fetchAndParseSubscriptionUrl($sub_url, $server_id) {
+function fetchAndParseSubscriptionUrl($sub_url, $server_id)
+{
if (empty($sub_url)) {
return [];
}
-
+
$stmt = pdo()->prepare("SELECT url, sub_host FROM servers WHERE id = ?");
$stmt->execute([$server_id]);
$server_info = $stmt->fetch();
- if (!$server_info) return [];
-
- $base_sub_url = !empty($server_info['sub_host']) ? rtrim($server_info['sub_host'], '/') : rtrim($server_info['url'], '/');
-
+ if (!$server_info)
+ return [];
+
+ $base_sub_url = !empty($server_info['sub_host']) ? rtrim($server_info['sub_host'], '/') : rtrim($server_info['url'], '/');
+
$stmt_type = pdo()->prepare("SELECT type FROM servers WHERE id = ?");
$stmt_type->execute([$server_id]);
$server_type = $stmt_type->fetchColumn();
$sub_path = '';
-
+
if ($server_type === 'marzban' || $server_type === 'sanaei') {
$sub_path_raw = strstr($sub_url, '/sub/');
if ($sub_path_raw !== false) {
$sub_path = $sub_path_raw;
}
}
-
-
+
+
if (empty($sub_path)) {
$sub_path = parse_url($sub_url, PHP_URL_PATH);
}
@@ -1049,13 +1089,14 @@ function fetchAndParseSubscriptionUrl($sub_url, $server_id) {
if ($decoded_links === false) {
$decoded_links = $response_body;
}
-
+
$links_array = preg_split("/\r\n|\n|\r/", trim($decoded_links));
-
+
return array_filter($links_array);
}
-function showPlansForCategoryAndServer($chat_id, $category_id, $server_id) {
+function showPlansForCategoryAndServer($chat_id, $category_id, $server_id)
+{
// Ø¯Ø±ÛØ§Ùت ÙØ§Ù
Ø¯Ø³ØªÙ ØšÙØ¯Û Ù Ø³Ø±ÙØ± ØšØ±Ø§Û ÙÙ
Ø§ÛØŽ Ø¯Ø± ÙŸÛØ§Ù
$category_name = pdo()->prepare("SELECT name FROM categories WHERE id = ?")->execute([$category_id]) ? pdo()->lastInsertId() : 'ÙØ§Ù
؎خص';
$server_name = pdo()->prepare("SELECT name FROM servers WHERE id = ?")->execute([$server_id]) ? pdo()->lastInsertId() : 'ÙØ§Ù
؎خص';
@@ -1084,7 +1125,8 @@ function showPlansForCategoryAndServer($chat_id, $category_id, $server_id) {
sendMessage($chat_id, $message, ['inline_keyboard' => $keyboard_buttons]);
}
-function applyRenewal($chat_id, $username, $days_to_add, $gb_to_add) {
+function applyRenewal($chat_id, $username, $days_to_add, $gb_to_add)
+{
$stmt = pdo()->prepare("SELECT server_id FROM services WHERE owner_chat_id = ? AND marzban_username = ?");
$stmt->execute([$chat_id, $username]);
$server_id = $stmt->fetchColumn();
@@ -1120,19 +1162,19 @@ function applyRenewal($chat_id, $username, $days_to_add, $gb_to_add) {
}
if (empty($update_data)) {
- return ['success' => false, 'message' => 'ÙÛÚ ØªØºÛÛØ±Û ØšØ±Ø§Û Ø§Ø¹Ù
Ø§Ù ÙØ¬Ùد ÙØ¯Ø§ØŽØª.'];
+ return ['success' => false, 'message' => 'ÙÛÚ ØªØºÛÛØ±Û ØšØ±Ø§Û Ø§Ø¹Ù
Ø§Ù ÙØ¬Ùد ÙØ¯Ø§ØŽØª.'];
}
$result = modifyPanelUser($username, $server_id, $update_data);
-
+
// ØšØ±ÙØ²Ø±Ø³Ø§ÙÛ Ø¯ÛØªØ§ØšÛس Ù
ØÙÛ
if ($result && !isset($result['detail'])) {
- if(isset($update_data['expire'])){
- pdo()->prepare("UPDATE services SET expire_timestamp = ? WHERE marzban_username = ? AND server_id = ?")->execute([$update_data['expire'], $username, $server_id]);
+ if (isset($update_data['expire'])) {
+ pdo()->prepare("UPDATE services SET expire_timestamp = ? WHERE marzban_username = ? AND server_id = ?")->execute([$update_data['expire'], $username, $server_id]);
}
- if(isset($update_data['data_limit'])){
- $new_volume_gb = ($update_data['data_limit'] / (1024*1024*1024));
- pdo()->prepare("UPDATE services SET volume_gb = ? WHERE marzban_username = ? AND server_id = ?")->execute([$new_volume_gb, $username, $server_id]);
+ if (isset($update_data['data_limit'])) {
+ $new_volume_gb = ($update_data['data_limit'] / (1024 * 1024 * 1024));
+ pdo()->prepare("UPDATE services SET volume_gb = ? WHERE marzban_username = ? AND server_id = ?")->execute([$new_volume_gb, $username, $server_id]);
}
return ['success' => true];
}
@@ -1140,13 +1182,14 @@ function applyRenewal($chat_id, $username, $days_to_add, $gb_to_add) {
return ['success' => false, 'message' => 'خطا در ارتؚاط ؚا ÙŸÙÙ ØšØ±Ø§Û Ø§Ø¹Ù
ا٠تغÛÛØ±Ø§Øª.'];
}
-function showRenewalManagementMenu($chat_id, $message_id = null) {
+function showRenewalManagementMenu($chat_id, $message_id = null)
+{
$settings = getSettings();
$status_icon = ($settings['renewal_status'] ?? 'off') == 'on' ? 'â
' : 'â';
$message = "ð Ù
Ø¯ÛØ±Ûت تÙ
Ø¯ÛØ¯ سرÙÛØ³ \n\n" .
- "â«ïž ÙØ¶Ø¹Ûت Ú©ÙÛ: " . ($status_icon == 'â
' ? 'ÙØ¹Ø§Ù ' : 'ØºÛØ±Ùعا٠') . "\n" .
- "â«ïž ÙØ²ÛÙÙ ÙØ± Ø±ÙØ² تÙ
Ø¯ÛØ¯: " . number_format($settings['renewal_price_per_day'] ?? 1000) . " تÙÙ
ا٠\n" .
- "â«ïž ÙØ²ÛÙÙ ÙØ± Ú¯ÛÚ¯Ø§ØšØ§ÛØª تÙ
Ø¯ÛØ¯: " . number_format($settings['renewal_price_per_gb'] ?? 2000) . " تÙÙ
ا٠";
+ "â«ïž ÙØ¶Ø¹Ûت Ú©ÙÛ: " . ($status_icon == 'â
' ? 'ÙØ¹Ø§Ù ' : 'ØºÛØ±Ùعا٠') . "\n" .
+ "â«ïž ÙØ²ÛÙÙ ÙØ± Ø±ÙØ² تÙ
Ø¯ÛØ¯: " . number_format($settings['renewal_price_per_day'] ?? 1000) . " تÙÙ
ا٠\n" .
+ "â«ïž ÙØ²ÛÙÙ ÙØ± Ú¯ÛÚ¯Ø§ØšØ§ÛØª تÙ
Ø¯ÛØ¯: " . number_format($settings['renewal_price_per_gb'] ?? 2000) . " تÙÙ
ا٠";
$keyboard = [
'inline_keyboard' => [
@@ -1164,7 +1207,8 @@ function showRenewalManagementMenu($chat_id, $message_id = null) {
}
}
-function showMarzbanProtocolEditor($chat_id, $message_id, $server_id) {
+function showMarzbanProtocolEditor($chat_id, $message_id, $server_id)
+{
$stmt_server = pdo()->prepare("SELECT name, marzban_protocols FROM servers WHERE id = ?");
$stmt_server->execute([$server_id]);
$server = $stmt_server->fetch();
@@ -1175,13 +1219,14 @@ function showMarzbanProtocolEditor($chat_id, $message_id, $server_id) {
}
$all_protocols = ['vless', 'vmess', 'trojan', 'shadowsocks'];
-
- $enabled_protocols = $server['marzban_protocols'] ? json_decode($server['marzban_protocols'], true) : ['vless'];
- if (!is_array($enabled_protocols)) $enabled_protocols = ['vless'];
-
+
+ $enabled_protocols = $server['marzban_protocols'] ? json_decode($server['marzban_protocols'], true) : ['vless'];
+ if (!is_array($enabled_protocols))
+ $enabled_protocols = ['vless'];
+
$message = "âïž ØªÙØžÛÙ
ÙŸØ±ÙØªÚ©ÙâÙØ§Û Ø³Ø±ÙØ±: {$server['name']} \n\n";
$message .= "ÙŸØ±ÙØªÚ©ÙâÙØ§ÛÛ Ø±Ø§ Ú©Ù Ù
ÛâØ®ÙØ§ÙÛØ¯ ØšØ±Ø§Û Ú©Ø§Ø±ØšØ±Ø§Ù Ø¬Ø¯ÛØ¯ در اÛÙ Ø³Ø±ÙØ± Ø§ÛØ¬Ø§Ø¯ ØŽÙÙØ¯Ø Ø§ÙØªØ®Ø§Øš Ú©ÙÛØ¯.";
-
+
$keyboard_buttons = [];
$row = [];
foreach ($all_protocols as $protocol) {
@@ -1195,17 +1240,18 @@ function showMarzbanProtocolEditor($chat_id, $message_id, $server_id) {
if (!empty($row)) {
$keyboard_buttons[] = $row;
}
-
+
$keyboard_buttons[] = [['text' => 'âïž ØšØ§Ø²Ú¯ØŽØª ØšÙ Ø³Ø±ÙØ±', 'callback_data' => "view_server_{$server_id}"]];
-
+
editMessageText($chat_id, $message_id, $message, ['inline_keyboard' => $keyboard_buttons]);
}
-function createZarinpalLink($chat_id, $amount, $description, $metadata = []) {
+function createZarinpalLink($chat_id, $amount, $description, $metadata = [])
+{
$settings = getSettings();
$merchant_id = $settings['zarinpal_merchant_id'];
$script_url = 'https://' . $_SERVER['HTTP_HOST'] . rtrim(dirname($_SERVER['PHP_SELF']), '/') . '/verify_payment.php';
-
+
$data = [
"merchant_id" => $merchant_id,
"amount" => $amount * 10, // تؚدÛ٠تÙÙ
Ø§Ù ØšÙ Ø±ÛØ§Ù
@@ -1221,18 +1267,18 @@ function createZarinpalLink($chat_id, $amount, $description, $metadata = []) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Content-Length: ' . strlen($jsonData)]);
-
+
$result = curl_exec($ch);
curl_close($ch);
$result = json_decode($result, true);
-
+
if (empty($result['errors'])) {
$authority = $result['data']['authority'];
-
+
// ثؚت ØªØ±Ø§Ú©ÙØŽ Ø¯Ø± Ø¯ÛØªØ§ØšÛس
$stmt = pdo()->prepare("INSERT INTO transactions (user_id, amount, authority, description, metadata) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$chat_id, $amount, $authority, $description, json_encode($metadata)]);
-
+
$payment_url = 'https://www.zarinpal.com/pg/StartPay/' . $authority;
return ['success' => true, 'url' => $payment_url];
} else {
@@ -1241,7 +1287,8 @@ function createZarinpalLink($chat_id, $amount, $description, $metadata = []) {
}
}
-function completePurchase($user_id, $plan_id, $custom_name, $final_price, $discount_code, $discount_object, $discount_applied) {
+function completePurchase($user_id, $plan_id, $custom_name, $final_price, $discount_code, $discount_object, $discount_applied)
+{
$plan = getPlanById($plan_id);
$user_data = getUserData($user_id);
$first_name = $user_data['first_name'];
@@ -1265,9 +1312,9 @@ function completePurchase($user_id, $plan_id, $custom_name, $final_price, $disco
if ($discount_applied && $discount_object) {
pdo()->prepare("UPDATE discount_codes SET usage_count = usage_count + 1 WHERE id = ?")->execute([$discount_object['id']]);
}
-
+
$expire_timestamp = $panel_user_data['expire'] ?? (isset($panel_user_data['expire_date']) ? strtotime($panel_user_data['expire_date']) : (time() + $plan['duration_days'] * 86400));
-
+
saveUserService($user_id, [
'server_id' => $plan['server_id'],
'username' => $panel_user_data['username'],
@@ -1277,7 +1324,7 @@ function completePurchase($user_id, $plan_id, $custom_name, $final_price, $disco
'expire_timestamp' => $expire_timestamp,
'volume_gb' => $plan['volume_gb'],
]);
-
+
$new_balance = $user_data['balance'] - $final_price;
$sub_link = $panel_user_data['subscription_url'];
$qr_code_url = generateQrCodeUrl($sub_link);
@@ -1292,12 +1339,12 @@ function completePurchase($user_id, $plan_id, $custom_name, $final_price, $disco
if ($plan['show_sub_link']) {
$caption .= "ð ÙÛÙÚ© ا؎تراک (Subscription):\n" . htmlspecialchars($sub_link) . "\n\n";
}
-
+
$caption .= "ð° Ù
ÙØ¬ÙØ¯Û Ø¬Ø¯ÛØ¯ ØŽÙ
ا: " . number_format($new_balance) . " تÙÙ
اÙ";
$chat_info_response = apiRequest('getChat', ['chat_id' => $user_id]);
$chat_info = json_decode($chat_info_response, true);
-
+
$profile_link_html = "ð€ کارؚر: " . htmlspecialchars($first_name) . " ($user_id)\n";
$admin_notification = "â
Ø®Ø±ÛØ¯ Ø¬Ø¯ÛØ¯ \n\n";
@@ -1312,7 +1359,7 @@ function completePurchase($user_id, $plan_id, $custom_name, $final_price, $disco
} else {
$admin_notification .= "ð³ Ù
ØšÙØº ٟرداخت ؎دÙ: " . number_format($final_price) . " تÙÙ
اÙ";
}
-
+
$keyboard_buttons = [];
if ($plan['show_conf_links'] && !empty($panel_user_data['links'])) {
$keyboard_buttons[] = [['text' => 'ð Ø¯Ø±ÛØ§Ùت کاÙÙÛÚ¯âÙØ§', 'callback_data' => "get_configs_{$panel_user_data['username']}"]];
@@ -1326,7 +1373,7 @@ function completePurchase($user_id, $plan_id, $custom_name, $final_price, $disco
'admin_notification' => $admin_notification,
];
}
-
+
return [
'success' => false,
'error_message' => "â Ù
ØªØ§Ø³ÙØ§Ù٠در Ø§ÛØ¬Ø§Ø¯ سرÙÛØ³ ØŽÙ
ا Ù
ØŽÚ©ÙÛ ÙŸÛØŽ Ø¢Ù
د. ÙØ·Ùا ؚا ÙŸØŽØªÛØšØ§ÙÛ ØªÙ
اس ØšÚ¯ÛØ±Ûد. Ù
ØšÙØºÛ از ØØ³Ø§Øš ØŽÙ
ا کسر ÙØŽØ¯Ù است."
diff --git a/src/install.php b/src/install.php
index ea38861..f678a43 100644
--- a/src/install.php
+++ b/src/install.php
@@ -15,19 +15,23 @@
$configFile = __DIR__ . '/includes/config.php';
$botFileUrl = 'https://' . $_SERVER['HTTP_HOST'] . rtrim(dirname($_SERVER['PHP_SELF']), '/') . '/bot.php';
-$step = isset($_POST['step']) ? (int)$_POST['step'] : 1;
+$step = isset($_POST['step']) ? (int) $_POST['step'] : 1;
$errors = [];
$successMessages = [];
// --- دادÙâÙØ§Û ÙØ±Ù
---
$bot_token = trim($_POST['bot_token'] ?? '');
$admin_id = trim($_POST['admin_id'] ?? '');
+$web_username = trim($_POST['web_username'] ?? '');
+$web_password = trim($_POST['web_password'] ?? '');
-function generateRandomString(int $length = 32): string {
+function generateRandomString(int $length = 32): string
+{
return bin2hex(random_bytes($length / 2));
}
-function getDbBaseSchemaSQL(): string {
+function getDbBaseSchemaSQL(): string
+{
return "
CREATE TABLE IF NOT EXISTS `users` ( `chat_id` BIGINT NOT NULL, `first_name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci, `balance` DECIMAL(10,2) NOT NULL DEFAULT 0.00, `user_state` VARCHAR(255) DEFAULT 'main_menu', `state_data` TEXT, `status` VARCHAR(20) NOT NULL DEFAULT 'active', `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`chat_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `admins` ( `chat_id` BIGINT NOT NULL PRIMARY KEY, `first_name` VARCHAR(255), `permissions` TEXT, `is_super_admin` TINYINT(1) NOT NULL DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
@@ -70,7 +74,8 @@ function getDbBaseSchemaSQL(): string {
";
}
-function columnExists(PDO $pdo, string $tableName, string $columnName): bool {
+function columnExists(PDO $pdo, string $tableName, string $columnName): bool
+{
try {
$stmt = $pdo->prepare("SHOW COLUMNS FROM `$tableName` LIKE ?");
$stmt->execute([$columnName]);
@@ -80,7 +85,8 @@ function columnExists(PDO $pdo, string $tableName, string $columnName): bool {
}
}
-function runDbUpgrades(PDO $pdo): array {
+function runDbUpgrades(PDO $pdo): array
+{
$messages = [];
if (columnExists($pdo, 'users', 'state') && !columnExists($pdo, 'users', 'user_state')) {
@@ -109,7 +115,7 @@ function runDbUpgrades(PDO $pdo): array {
$pdo->exec("ALTER TABLE `services` ADD `sanaei_uuid` VARCHAR(255) NULL DEFAULT NULL AFTER `sanaei_inbound_id`;");
$messages[] = "â
ستÙÙ `sanaei_uuid` ØšØ±Ø§Û ÙŸÙÙ Ø³ÙØ§ÛÛ ØšÙ Ø¬Ø¯ÙÙ `services` اضاÙ٠؎د.";
}
-
+
// --- Ø§Ø±ØªÙØ§ÙØ§Û Ù
Ø±ØšÙØ· ØšÙ Ø§Ø¹ÙØ§ÙâÙØ§ Ù Ø±Ø¯ÛØ§ØšÛ کارؚرا٠---
if (!columnExists($pdo, 'users', 'last_seen_at')) {
$pdo->exec("ALTER TABLE `users` ADD `last_seen_at` TIMESTAMP NULL DEFAULT NULL AFTER `status`;");
@@ -173,24 +179,40 @@ function runDbUpgrades(PDO $pdo): array {
// --- Ù
Ø¯ÛØ±Ûت Ù
ÙØ·Ù Ù
راØÙ ---
if ($step === 2) {
- if (empty($bot_token)) $errors[] = 'تÙک٠رؚات Ø§ÙØ²Ø§Ù
Û Ø§Ø³Øª.';
- if (empty($admin_id) || !is_numeric($admin_id)) $errors[] = 'Ø¢ÛØ¯Û Ø¹Ø¯Ø¯Û Ø§Ø¯Ù
ÛÙ Ø§ÙØ²Ø§Ù
Û Ù ØšØ§ÛØ¯ عدد ؚا؎د.';
- if (!empty($errors)) $step = 1;
-}
-elseif ($step === 3) {
+ if (empty($bot_token))
+ $errors[] = 'تÙک٠رؚات Ø§ÙØ²Ø§Ù
Û Ø§Ø³Øª.';
+ if (empty($admin_id) || !is_numeric($admin_id))
+ $errors[] = 'Ø¢ÛØ¯Û Ø¹Ø¯Ø¯Û Ø§Ø¯Ù
ÛÙ Ø§ÙØ²Ø§Ù
Û Ù ØšØ§ÛØ¯ عدد ؚا؎د.';
+
+ // اگر username/password خاÙÛ ØšÙØ¯Ø Ù
ÙØ§Ø¯Ûر ÙŸÛØŽâÙØ±Ø¶ تÙÙÛØ¯ ØŽÙØ¯
+ if (empty($web_username)) {
+ $web_username = 'admin';
+ }
+ if (empty($web_password)) {
+ $web_password = generateRandomString(16);
+ }
+
+ if (!empty($errors))
+ $step = 1;
+} elseif ($step === 3) {
$db_host = trim($_POST['db_host'] ?? 'localhost');
$db_name = trim($_POST['db_name'] ?? '');
$db_user = trim($_POST['db_user'] ?? '');
$db_pass = trim($_POST['db_pass'] ?? '');
- if (empty($db_name)) $errors[] = 'ÙØ§Ù
Ø¯ÛØªØ§ØšÛس Ø§ÙØ²Ø§Ù
Û Ø§Ø³Øª.';
- if (empty($db_user)) $errors[] = 'ÙØ§Ù
Ú©Ø§Ø±ØšØ±Û Ø¯ÛØªØ§ØšÛس Ø§ÙØ²Ø§Ù
Û Ø§Ø³Øª.';
-
+ if (empty($db_name))
+ $errors[] = 'ÙØ§Ù
Ø¯ÛØªØ§ØšÛس Ø§ÙØ²Ø§Ù
Û Ø§Ø³Øª.';
+ if (empty($db_user))
+ $errors[] = 'ÙØ§Ù
Ú©Ø§Ø±ØšØ±Û Ø¯ÛØªØ§ØšÛس Ø§ÙØ²Ø§Ù
Û Ø§Ø³Øª.';
+
if (empty($errors)) {
- if (!is_dir(__DIR__ . '/includes')) @mkdir(__DIR__ . '/includes', 0755, true);
- if (!file_exists($configFile)) @file_put_contents($configFile, "exec(getDbBaseSchemaSQL());
$successMessages[] = "â
ساختار ٟاÛ٠جداÙ٠ؚا Ù
ÙÙÙÛØª Ø§ÛØ¬Ø§Ø¯/ØšØ±Ø±Ø³Û ØŽØ¯.";
-
+
$secretToken = generateRandomString(64);
+ $web_password_hash = password_hash($web_password, PASSWORD_BCRYPT);
+
$config_content = '{$web_username} | Password={$web_password} ";
+
$upgradeMessages = runDbUpgrades($pdo);
if (!empty($upgradeMessages)) {
$successMessages = array_merge($successMessages, $upgradeMessages);
@@ -231,11 +259,11 @@ function runDbUpgrades(PDO $pdo): array {
('notification_inactive_days', '30'),
('renewal_status', 'off'), ('renewal_price_per_day', '1000'), ('renewal_price_per_gb', '2000'), ('payment_gateway_status', 'off'), ('zarinpal_merchant_id', '');");
$successMessages[] = "â
ØªÙØžÛÙ
ات ÙŸÛØŽâÙØ±Ø¶ ؚا Ù
ÙÙÙÛØª Ø§ÙØ²Ùد٠؎د.";
-
+
$apiUrl = "https://api.telegram.org/bot$bot_token/setWebhook?secret_token=$secretToken&url=" . urlencode($botFileUrl);
$response = @file_get_contents($apiUrl);
$response_data = json_decode($response, true);
-
+
if (!$response || !$response_data['ok']) {
$errors[] = 'خطا در ثؚت ÙØšÙÙÚ©: ' . ($response_data['description'] ?? 'ٟاسخ ÙØ§Ù
عتؚر از تÙگراÙ
. از ØµØØª تÙÚ©Ù Ù
Ø·Ù
ØŠÙ ØŽÙÛØ¯.');
} else {
@@ -251,6 +279,7 @@ function runDbUpgrades(PDO $pdo): array {
?>
+
@@ -258,198 +287,441 @@ function runDbUpgrades(PDO $pdo): array {
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
Û±
-
Ø§Ø·ÙØ§Ø¹Ø§Øª رؚات
-
-
-
-
Û³
-
ÙŸØ§ÛØ§Ù ÙØµØš
-
-
+ ?>
+
-
-
-
خطا!
-
- " . htmlspecialchars($error) . ""; ?>
+
+
Û±
+
Ø§Ø·ÙØ§Ø¹Ø§Øª رؚات
+
+
+
+
Û³
+
ÙŸØ§ÛØ§Ù ÙØµØš
+
-
-
-
- آدرس ÙØšÙÙÚ© ØŽÙ
ا:
-
-
-
-
-
-
-
-
-
-
ÙØµØš ؚا Ù
ÙÙÙÛØª ØšÙ ÙŸØ§ÛØ§Ù Ø±Ø³ÛØ¯!
-
-
-
- Ù
ÙÙ
: اÛÙ ÙØ§ÛÙ Ø¬ÙØª Ø§ÙØ²Ø§ÛØŽ اÙ
ÙÛØª تا ÚÙØ¯ ثاÙÛ٠دÛگر ØšÙ ØµÙØ±Øª Ø®ÙØ¯Ú©Ø§Ø± ØØ°Ù Ø®ÙØ§Ùد ؎د .
-
-
-
-
ÙØµØš ؚا خطا Ù
ÙØ§Ø¬Ù ؎د!
-
- " . htmlspecialchars($error) . ""; ?>
-
-
-
-
+
+
+
خطا!
+
- " . htmlspecialchars($error) . ""; ?>
+
+
+
+
+
+ آدرس ÙØšÙÙÚ© ØŽÙ
ا:
+
+
+
+
+
+
+
+
+
+
ÙØµØš ؚا Ù
ÙÙÙÛØª ØšÙ ÙŸØ§ÛØ§Ù Ø±Ø³ÛØ¯!
+
+
+
+ Ù
ÙÙ
: اÛÙ ÙØ§ÛÙ Ø¬ÙØª Ø§ÙØ²Ø§ÛØŽ اÙ
ÙÛØª تا ÚÙØ¯ ثاÙÛ٠دÛگر ØšÙ ØµÙØ±Øª Ø®ÙØ¯Ú©Ø§Ø± ØØ°Ù Ø®ÙØ§Ùد
+ ؎د .
+
+
+
+
ÙØµØš ؚا خطا Ù
ÙØ§Ø¬Ù ؎د!
+
- " . htmlspecialchars($error) . ""; ?>
+
+
+
+
+
-
-
-
-
-
+ })
+ .catch(function (error) { console.error('خطا در ØØ°Ù Ø®ÙØ¯Ú©Ø§Ø± ÙØ§ÛÙ:', error); });
+ }, 5000); // 5-second delay
+
+
+
\ No newline at end of file
diff --git a/src/web/README.md b/src/web/README.md
new file mode 100644
index 0000000..cb35ddf
--- /dev/null
+++ b/src/web/README.md
@@ -0,0 +1,50 @@
+# ÙŸÙÙ ØªØØª ÙØš v2ray - راÙÙÙ
Ø§Û Ø§Ø³ØªÙØ§Ø¯Ù
+
+## ÙØµØš
+
+1. ÙØ§ÛÙ `install.php` را در Ù
Ø±ÙØ±Ú¯Ø± ؚاز Ú©ÙÛØ¯
+2. در Ù
رØÙ٠اÙÙØ Ø§Ø·ÙØ§Ø¹Ø§Øª Ø²ÛØ± را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:
+ - تÙک٠رؚات تÙگراÙ
+ - Ø¢ÛØ¯Û Ø¹Ø¯Ø¯Û Ø§Ø¯Ù
ÛÙ
+ - **(Ø§Ø®ØªÛØ§Ø±Û)** ÙØ§Ù
Ú©Ø§Ø±ØšØ±Û Ù Ø±Ù
ز Ø¹ØšÙØ± ÙŸÙÙ ÙØš
+3. در Ù
رØÙ٠دÙÙ
Ø Ø§Ø·ÙØ§Ø¹Ø§Øª Ø¯ÛØªØ§ØšÛس را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯
+4. ؚعد از ÙØµØš Ù
ÙÙÙØ Ø§Ø·ÙØ§Ø¹Ø§Øª ÙØ±Ùد ØšÙ ÙŸÙÙ ÙØš ÙÙ
Ø§ÛØŽ Ø¯Ø§Ø¯Ù Ù
ÛâØŽÙØ¯
+
+## ÙØ±Ùد ØšÙ ÙŸÙÙ
+
+1. ؚ٠آدرس `http://your-domain/web/` ؚرÙÛØ¯
+2. ؚا ÙØ§Ù
Ú©Ø§Ø±ØšØ±Û Ù Ø±Ù
ز Ø¹ØšÙØ± ÙØ§Ø±Ø¯ ØŽÙÛØ¯
+3. ØšÙ Ø¯Ø§ØŽØšÙØ±Ø¯ ÙØ¯Ø§Ûت Ù
ÛâØŽÙÛØ¯
+
+## ÙØ§ØšÙÛØªâÙØ§
+
+### â
ÙØ¹Ø§Ù ؎دÙ:
+- ð Ø³ÛØ³ØªÙ
Ø§ØØ±Ø§Ø² ÙÙÛØª اÙ
Ù
+- ð Ø¯Ø§ØŽØšÙØ±Ø¯ ؚا Ø¢Ù
ار Ú©ÙÛ
+- ð Ù
Ø¯ÛØ±Ûت دستÙâØšÙØ¯ÛâÙØ§ (Ø§ÙØ²ÙØ¯ÙØ ØØ°ÙØ ÙØ¹Ø§Ù/ØºÛØ±ÙعاÙ)
+- ð¥ Ù
Ø¯ÛØ±Ûت کارؚرا٠(Ø¬Ø³ØªØ¬ÙØ Ø§ÙØ²Ø§ÛØŽ/Ú©Ø§ÙØŽ Ù
ÙØ¬ÙØ¯ÛØ Ù
Ø³Ø¯ÙØ¯/Ø¢Ø²Ø§Ø¯Ø Ù
ØŽØ§ÙØ¯Ù سرÙÛØ³âÙØ§)
+- ð Ø¢Ù
ار ٠گزار؎ات (Ø¢Ù
ار Ú©Ø§Ø±ØšØ±Ø§ÙØ سرÙÛØ³âÙØ§Ø درآÙ
د)
+- âïž ØªÙØžÛÙ
ات (ÙØ¶Ø¹Ûت Ø±ØšØ§ØªØ Ú©Ø§ÙØ§Ù Ø§Ø¬ØšØ§Ø±ÛØ ÙØ¯ÛÙ Ø®ÙØŽâØ¢Ù
Ø¯Ø Ø¯Ø±Ú¯Ø§Ù ÙŸØ±Ø¯Ø§Ø®Øª)
+- ðš Ø·Ø±Ø§ØÛ Ù
Ø¯Ø±Ù Ù Ø²ÛØšØ§
+- ð± Responsive ØšØ±Ø§Û Ù
ÙØšØ§ÛÙ
+
+### ð Ø¯Ø± ØØ§Ù ØªÙØ³Ø¹Ù:
+- ð Ù
Ø¯ÛØ±Ûت ÙŸÙÙâÙØ§
+- ð Ù
Ø¯ÛØ±Ûت Ø³Ø±ÙØ±Ùا
+- ðšâðŒ Ù
Ø¯ÛØ±Ûت ادÙ
ÛÙâÙØ§
+- ð Ù
Ø¯ÛØ±Ûت Ú©Ø¯ÙØ§Û تخÙÛÙ
+- ð Ù
Ø¯ÛØ±Ûت راÙÙÙ
ا
+- ð³ Ù
Ø¯ÛØ±Ûت ٟرداختâÙØ§Û دستÛ
+- ð£ Ø§Ø±Ø³Ø§Ù ÙÙ
گاÙÛ
+
+## ÙÛÚÚ¯ÛâÙØ§Û اÙ
ÙÛØªÛ
+
+- â
Hash کرد٠رÙ
ز Ø¹ØšÙØ± ؚا bcrypt
+- â
Session security
+- â
Ù
ØØ§Ù؞ت از تÙ
اÙ
ØµÙØØ§Øª ؚا requireLogin
+- â
Sanitization ÙØ±ÙدÛâÙØ§
+- â
Ø§Ø³ØªÙØ§Ø¯Ù از Prepared Statements
+
+## ÙŸØŽØªÛØšØ§ÙÛ
+
+ØšØ±Ø§Û Ú¯Ø²Ø§Ø±ØŽ Ù
ØŽÚ©ÙØ§Øª ÛØ§ ÙŸÛØŽÙÙØ§Ø¯Ø§ØªØ ؚا ØªÙØ³Ø¹ÙâØ¯ÙÙØ¯Ù تÙ
اس ØšÚ¯ÛØ±Ûد.
diff --git a/src/web/assets/css/style.css b/src/web/assets/css/style.css
new file mode 100644
index 0000000..a7d699c
--- /dev/null
+++ b/src/web/assets/css/style.css
@@ -0,0 +1,430 @@
+:root {
+ --bg-main: #0a0e1a;
+ --bg-container: #1e293b;
+ --bg-card: #2d3748;
+ --bg-input: #111827;
+ --primary: #8b5cf6;
+ --primary-hover: #7c3aed;
+ --success: #10b981;
+ --danger: #ef4444;
+ --warning: #f59e0b;
+ --info: #3b82f6;
+ --blue: #3b82f6;
+ --green: #10b981;
+ --purple: #8b5cf6;
+ --orange: #f97316;
+ --teal: #14b8a6;
+ --red: #ef4444;
+ --text-light: #f8fafc;
+ --text-muted: #94a3b8;
+ --border-color: rgba(148, 163, 184, 0.2);
+ --shadow-color: rgba(0, 0, 0, 0.5);
+ --sidebar-width: 280px;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+ font-family: Vazirmatn, sans-serif;
+}
+
+body {
+ background-color: var(--bg-main);
+ background-image: url('data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"%3E%3Cg fill-rule="evenodd"%3E%3Cg fill="%231e293b" fill-opacity="0.2"%3E%3Cpath d="M0 38.59l2.83-2.83 1.41 1.41L1.41 40H0v-1.41zM0 1.4l2.83 2.83 1.41-1.41L1.41 0H0v1.41zM38.59 40l-2.83-2.83 1.41-1.41L40 38.59V40h-1.41zM40 1.41l-2.83 2.83-1.41-1.41L38.59 0H40v1.41zM20 18.6l2.83-2.83 1.41 1.41L21.41 20l2.83 2.83-1.41 1.41L20 21.41l-2.83 2.83-1.41-1.41L18.59 20l-2.83-2.83 1.41-1.41L20 18.59z"/%3E%3C/g%3E%3C/g%3E%3C/svg%3E');
+ min-height: 100vh;
+ color: var(--text-light);
+}
+
+/* Layout */
+.layout {
+ display: flex;
+ min-height: 100vh;
+}
+
+.main-content {
+ flex: 1;
+ margin-right: var(--sidebar-width);
+ transition: margin-right 0.3s;
+}
+
+/* Sidebar */
+.sidebar {
+ position: fixed;
+ right: 0;
+ top: 0;
+ width: var(--sidebar-width);
+ height: 100vh;
+ background: var(--bg-container);
+ border-left: 1px solid var(--border-color);
+ display: flex;
+ flex-direction: column;
+ z-index: 1000;
+ transition: transform 0.3s;
+}
+
+.sidebar-header {
+ padding: 30px 20px;
+ border-bottom: 1px solid var(--border-color);
+}
+
+.sidebar-header h2 {
+ font-size: 1.5rem;
+ margin-bottom: 5px;
+}
+
+.sidebar-header .username {
+ font-size: 0.9rem;
+ color: var(--text-muted);
+}
+
+.sidebar-nav {
+ flex: 1;
+ padding: 20px 0;
+ overflow-y: auto;
+}
+
+.sidebar-nav a {
+ display: flex;
+ align-items: center;
+ padding: 12px 20px;
+ color: var(--text-muted);
+ text-decoration: none;
+ transition: all 0.3s;
+ border-right: 3px solid transparent;
+}
+
+.sidebar-nav a i {
+ width: 24px;
+ margin-left: 12px;
+}
+
+.sidebar-nav a:hover {
+ background-color: rgba(139, 92, 246, 0.1);
+ color: var(--text-light);
+}
+
+.sidebar-nav a.active {
+ background-color: rgba(139, 92, 246, 0.15);
+ color: var(--primary);
+ border-right-color: var(--primary);
+}
+
+.sidebar-footer {
+ padding: 20px;
+ border-top: 1px solid var(--border-color);
+}
+
+.logout-btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 12px;
+ background: linear-gradient(135deg, var(--danger), #dc2626);
+ color: white;
+ text-decoration: none;
+ border-radius: 8px;
+ transition: transform 0.2s;
+ font-weight: 500;
+}
+
+.logout-btn:hover {
+ transform: translateY(-2px);
+}
+
+.logout-btn i {
+ margin-left: 8px;
+}
+
+/* Topbar */
+.topbar {
+ display: flex;
+ align-items: center;
+ padding: 20px 30px;
+ background: var(--bg-container);
+ border-bottom: 1px solid var(--border-color);
+ position: sticky;
+ top: 0;
+ z-index: 100;
+}
+
+.topbar h1 {
+ font-size: 1.5rem;
+ font-weight: 600;
+}
+
+.menu-toggle {
+ display: none;
+ background: transparent;
+ border: none;
+ color: var(--text-light);
+ font-size: 1.5rem;
+ cursor: pointer;
+ margin-left: 15px;
+}
+
+/* Content Area */
+.content-area {
+ padding: 30px;
+}
+
+/* Stats Grid */
+.stats-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ gap: 20px;
+ margin-bottom: 30px;
+}
+
+.stat-card {
+ background: var(--bg-container);
+ border: 1px solid var(--border-color);
+ border-radius: 12px;
+ padding: 24px;
+ display: flex;
+ align-items: center;
+ gap: 20px;
+ transition: transform 0.2s, box-shadow 0.2s;
+}
+
+.stat-card:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
+}
+
+.stat-icon {
+ width: 60px;
+ height: 60px;
+ border-radius: 12px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 1.5rem;
+}
+
+.stat-icon.blue { background: rgba(59, 130, 246, 0.2); color: var(--blue); }
+.stat-icon.green { background: rgba(16, 185, 129, 0.2); color: var(--green); }
+.stat-icon.purple { background: rgba(139, 92, 246, 0.2); color: var(--purple); }
+.stat-icon.orange { background: rgba(249, 115, 22, 0.2); color: var(--orange); }
+.stat-icon.teal { background: rgba(20, 184, 166, 0.2); color: var(--teal); }
+.stat-icon.red { background: rgba(239, 68, 68, 0.2); color: var(--red); }
+
+.stat-info {
+ flex: 1;
+}
+
+.stat-value {
+ font-size: 2rem;
+ font-weight: 700;
+ margin-bottom: 5px;
+}
+
+.stat-label {
+ color: var(--text-muted);
+ font-size: 0.9rem;
+}
+
+.stat-sub {
+ color: var(--success);
+ font-size: 0.85rem;
+ margin-top: 5px;
+}
+
+/* Cards */
+.card {
+ background: var(--bg-container);
+ border: 1px solid var(--border-color);
+ border-radius: 12px;
+ overflow: hidden;
+ margin-bottom: 20px;
+}
+
+.card-header {
+ padding: 20px;
+ border-bottom: 1px solid var(--border-color);
+}
+
+.card-header h3 {
+ font-size: 1.25rem;
+ font-weight: 600;
+}
+
+.card-header h3 i {
+ margin-left: 10px;
+}
+
+.card-body {
+ padding: 20px;
+}
+
+/* Tables */
+.data-table {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+.data-table thead {
+ background: rgba(139, 92, 246, 0.1);
+}
+
+.data-table th {
+ padding: 12px;
+ text-align: right;
+ font-weight: 600;
+ color: var(--text-light);
+}
+
+.data-table tbody tr {
+ border-bottom: 1px solid var(--border-color);
+ transition: background-color 0.2s;
+}
+
+.data-table tbody tr:hover {
+ background-color: rgba(255, 255, 255, 0.02);
+}
+
+.data-table td {
+ padding: 12px;
+ text-align: right;
+}
+
+/* Forms */
+.form-group {
+ margin-bottom: 20px;
+}
+
+label {
+ display: block;
+ margin-bottom: 8px;
+ color: var(--text-muted);
+ font-weight: 500;
+}
+
+input[type="text"],
+input[type="password"],
+input[type="number"],
+input[type="email"],
+select,
+textarea {
+ width: 100%;
+ padding: 12px;
+ background: var(--bg-input);
+ border: 1px solid var(--border-color);
+ border-radius: 8px;
+ color: var(--text-light);
+ font-size: 1rem;
+ transition: border-color 0.3s, box-shadow 0.3s;
+}
+
+input:focus,
+select:focus,
+textarea:focus {
+ border-color: var(--primary);
+ box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.3);
+ outline: none;
+}
+
+/* Buttons */
+.btn {
+ padding: 12px 24px;
+ border: none;
+ border-radius: 8px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: transform 0.2s, box-shadow 0.2s;
+ text-decoration: none;
+ display: inline-block;
+ text-align: center;
+}
+
+.btn:hover {
+ transform: translateY(-2px);
+}
+
+.btn-primary {
+ background: linear-gradient(135deg, var(--primary), var(--primary-hover));
+ color: white;
+}
+
+.btn-success {
+ background: linear-gradient(135deg, var(--success), #059669);
+ color: white;
+}
+
+.btn-danger {
+ background: linear-gradient(135deg, var(--danger), #dc2626);
+ color: white;
+}
+
+/* Alerts */
+.alert {
+ padding: 15px;
+ border-radius: 8px;
+ margin-bottom: 20px;
+ border-right-width: 4px;
+ border-right-style: solid;
+}
+
+.alert-success {
+ background-color: rgba(16, 185, 129, 0.1);
+ border-right-color: var(--success);
+ color: #a7f3d0;
+}
+
+.alert-danger {
+ background-color: rgba(239, 68, 68, 0.1);
+ border-right-color: var(--danger);
+ color: #fca5a5;
+}
+
+.alert-warning {
+ background-color: rgba(245, 158, 11, 0.1);
+ border-right-color: var(--warning);
+ color: #fcd34d;
+}
+
+.alert-info {
+ background-color: rgba(59, 130, 246, 0.1);
+ border-right-color: var(--info);
+ color: #93c5fd;
+}
+
+/* Utilities */
+.text-muted {
+ color: var(--text-muted);
+}
+
+.text-center {
+ text-align: center;
+}
+
+.mb-20 {
+ margin-bottom: 20px;
+}
+
+.mt-20 {
+ margin-top: 20px;
+}
+
+/* Responsive */
+@media (max-width: 768px) {
+ .sidebar {
+ transform: translateX(100%);
+ }
+
+ .sidebar.active {
+ transform: translateX(0);
+ }
+
+ .main-content {
+ margin-right: 0;
+ }
+
+ .menu-toggle {
+ display: block;
+ }
+
+ .stats-grid {
+ grid-template-columns: 1fr;
+ }
+}
diff --git a/src/web/assets/js/main.js b/src/web/assets/js/main.js
new file mode 100644
index 0000000..b77d979
--- /dev/null
+++ b/src/web/assets/js/main.js
@@ -0,0 +1,70 @@
+// Main JavaScript for Web Panel
+document.addEventListener('DOMContentLoaded', function() {
+ // Menu Toggle for Mobile
+ const menuToggle = document.getElementById('menuToggle');
+ const sidebar = document.getElementById('sidebar');
+
+ if (menuToggle && sidebar) {
+ menuToggle.addEventListener('click', function() {
+ sidebar.classList.toggle('active');
+ });
+
+ // Close sidebar when clicking outside on mobile
+ document.addEventListener('click', function(e) {
+ if (window.innerWidth <= 768) {
+ if (!sidebar.contains(e.target) && !menuToggle.contains(e.target)) {
+ sidebar.classList.remove('active');
+ }
+ }
+ });
+ }
+
+ // Auto-hide alerts after 5 seconds
+ const alerts = document.querySelectorAll('.alert');
+ alerts.forEach(alert => {
+ setTimeout(() => {
+ alert.style.opacity = '0';
+ setTimeout(() => {
+ alert.remove();
+ }, 300);
+ }, 5000);
+ });
+});
+
+/**
+ * Show toast notification
+ */
+function showToast(message, type = 'info') {
+ const toast = document.createElement('div');
+ toast.className = `alert alert-${type}`;
+ toast.style.position = 'fixed';
+ toast.style.top = '20px';
+ toast.style.left = '50%';
+ toast.style.transform = 'translateX(-50%)';
+ toast.style.zIndex = '9999';
+ toast.style.minWidth = '300px';
+ toast.textContent = message;
+
+ document.body.appendChild(toast);
+
+ setTimeout(() => {
+ toast.style.opacity = '0';
+ setTimeout(() => {
+ toast.remove();
+ }, 300);
+ }, 3000);
+}
+
+/**
+ * Confirm dialog
+ */
+function confirmAction(message) {
+ return confirm(message);
+}
+
+/**
+ * Format number with thousands separator
+ */
+function formatNumber(num) {
+ return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+}
diff --git a/src/web/dashboard.php b/src/web/dashboard.php
new file mode 100644
index 0000000..d40aa6e
--- /dev/null
+++ b/src/web/dashboard.php
@@ -0,0 +1,177 @@
+query("SELECT COUNT(*) FROM users");
+$stats['total_users'] = $stmt->fetchColumn();
+
+// Active users
+$stmt = pdo()->query("SELECT COUNT(*) FROM users WHERE status = 'active'");
+$stats['active_users'] = $stmt->fetchColumn();
+
+// Total services
+$stmt = pdo()->query("SELECT COUNT(*) FROM services");
+$stats['total_services'] = $stmt->fetchColumn();
+
+// Today income
+$today_income = pdo()
+ ->query("SELECT SUM(p.price) FROM services s JOIN plans p ON s.plan_id = p.id WHERE DATE(s.purchase_date) = CURDATE()")
+ ->fetchColumn() ?? 0;
+$stats['today_income'] = $today_income;
+
+// Month income
+$month_income = pdo()
+ ->query("SELECT SUM(p.price) FROM services s JOIN plans p ON s.plan_id = p.id WHERE MONTH(s.purchase_date) = MONTH(CURDATE()) AND YEAR(s.purchase_date) = YEAR(CURDATE())")
+ ->fetchColumn() ?? 0;
+$stats['month_income'] = $month_income;
+
+// Total servers
+$stmt = pdo()->query("SELECT COUNT(*) FROM servers WHERE status = 'active'");
+$stats['total_servers'] = $stmt->fetchColumn();
+
+// Pending payments
+$stmt = pdo()->query("SELECT COUNT(*) FROM payment_requests WHERE status = 'pending'");
+$stats['pending_payments'] = $stmt->fetchColumn();
+
+// Recent services (last 5)
+$stmt = pdo()->query("
+ SELECT s.*, p.name as plan_name, u.first_name
+ FROM services s
+ JOIN plans p ON s.plan_id = p.id
+ JOIN users u ON s.owner_chat_id = u.chat_id
+ ORDER BY s.id DESC
+ LIMIT 5
+");
+$recent_services = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+renderHeader('Ø¯Ø§ØŽØšÙØ±Ø¯');
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ک٠کارؚراÙ
+
ÙØ¹Ø§Ù
+
+
+
+
+
+
+
+
+
+
ک٠سرÙÛØ³âÙØ§
+
+
+
+
+
+
+
+
+
+
درآÙ
د اÙ
Ø±ÙØ² (تÙÙ
اÙ)
+
+
+
+
+
+
+
+
+
+
درآÙ
د Ù
ا٠(تÙÙ
اÙ)
+
+
+
+
+
+
+
+
+
+
Ø³Ø±ÙØ±ÙØ§Û ÙØ¹Ø§Ù
+
+
+
+
+
+
+
+
+
+
ٟرداختâÙØ§Û در Ø§ÙØªØžØ§Ø±
+
+
+
+
+
+
+
+
+
+
ÙÛÚ Ø³Ø±ÙÛØ³Û ÛØ§Ùت ÙØŽØ¯.
+
+
+
+
+ کارؚر
+ ÙŸÙÙ
+ ÙØ§Ù
سرÙÛØ³
+ ØªØ§Ø±ÛØ® Ø®Ø±ÛØ¯
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/web/includes/auth.php b/src/web/includes/auth.php
new file mode 100644
index 0000000..14e21b4
--- /dev/null
+++ b/src/web/includes/auth.php
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
- ÙŸÙÙ Ù
Ø¯ÛØ±Ûت
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ' . htmlspecialchars($message) . '
';
+}
+
+/**
+ * Show error message
+ */
+function showError($message)
+{
+ echo '' . htmlspecialchars($message) . '
';
+}
+
+/**
+ * Sanitize input
+ */
+function sanitizeInput($data)
+{
+ return htmlspecialchars(strip_tags(trim($data)));
+}
+
+/**
+ * Helper wrappers for functions.php
+ */
+function getUserBalance($chat_id)
+{
+ $stmt = pdo()->prepare("SELECT balance FROM users WHERE chat_id = ?");
+ $stmt->execute([$chat_id]);
+ return $stmt->fetchColumn() ?? 0;
+}
+
+function getServers()
+{
+ $stmt = pdo()->query("SELECT * FROM servers WHERE status = 'active' ORDER BY id DESC");
+ return $stmt->fetchAll(PDO::FETCH_ASSOC);
+}
diff --git a/src/web/index.php b/src/web/index.php
new file mode 100644
index 0000000..66a4ffc
--- /dev/null
+++ b/src/web/index.php
@@ -0,0 +1,207 @@
+
+
+
+
+
+
+
+ ÙØ±Ùد ØšÙ ÙŸÙÙ Ù
Ø¯ÛØ±Ûت
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/web/pages/admins.php b/src/web/pages/admins.php
new file mode 100644
index 0000000..00998d9
--- /dev/null
+++ b/src/web/pages/admins.php
@@ -0,0 +1,280 @@
+
+
+
+
+
+
+
+
+
+
+
â
+
+
+
+
â
+
+
+
+
+
+
+
+
+
+
+
ک٠ادÙ
ÛÙâÙØ§
+
؎اÙ
٠سÙٟر ادÙ
ÛÙ
+
+
+
+
+
+
+
+
+
+
ادÙ
ÛÙâÙØ§Û عادÛ
+
+
+
+
+
+
+
+
+
+
اÙÙØ§Ø¹ دسترسÛ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ÙÛÚ Ø§Ø¯Ù
ÛÙÛ ÛØ§Ùت ÙØŽØ¯. از ÙØ±Ù
ØšØ§ÙØ§ ØšØ±Ø§Û Ø§ÙØ²Ùد٠ادÙ
ÛÙ Ø¬Ø¯ÛØ¯ Ø§Ø³ØªÙØ§Ø¯Ù Ú©ÙÛØ¯.
+
+
+ $admin): ?>
+
+
+
+
+
+
+
+ دسترسÛâÙØ§
+
+ ( Ù
ÙØ±Ø¯)
+
+
+
+
+
+
+
+
+
دسترسÛâÙØ§Û ÙØ¹ÙÛ:
+
+
+ ÙÛÚ Ø¯Ø³ØªØ±Ø³Û ÙØ¯Ø§Ø±Ø¯
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/web/pages/broadcast.php b/src/web/pages/broadcast.php
new file mode 100644
index 0000000..041ed63
--- /dev/null
+++ b/src/web/pages/broadcast.php
@@ -0,0 +1,480 @@
+ false, 'error' => 'BOT_TOKEN تعرÛÙ ÙØŽØ¯Ù است. ÙØ·ÙØ§Ù ÙØ§ÛÙ config.php را ØšØ±Ø±Ø³Û Ú©ÙÛØ¯.']);
+ exit;
+ }
+
+ if (BOT_TOKEN === 'TOKEN' || empty(BOT_TOKEN)) {
+ echo json_encode(['success' => false, 'error' => 'BOT_TOKEN در ÙØ§ÛÙ config.php ØšÙ Ø¯Ø±Ø³ØªÛ ØªÙØžÛÙ
ÙØŽØ¯Ù است.']);
+ exit;
+ }
+
+ $offset = (int) ($_POST['offset'] ?? 0);
+ $batch_size = 50; // Process 50 users at a time
+ $message_text = $_POST['message_text'] ?? '';
+ $target_group = $_POST['target_group'] ?? 'all';
+ $photo_id = sanitizeInput($_POST['photo_id'] ?? '');
+
+ if (empty($message_text)) {
+ echo json_encode(['success' => false, 'error' => 'Ù
ØªÙ ÙŸÛØ§Ù
خاÙÛ Ø§Ø³Øª']);
+ exit;
+ }
+
+ // Get target users based on selection
+ switch ($target_group) {
+ case 'all':
+ $stmt = pdo()->query("SELECT chat_id FROM users WHERE status = 'active' LIMIT $offset, $batch_size");
+ $count_stmt = pdo()->query("SELECT COUNT(*) FROM users WHERE status = 'active'");
+ break;
+ case 'with_service':
+ $stmt = pdo()->query("
+ SELECT DISTINCT u.chat_id
+ FROM users u
+ JOIN services s ON u.chat_id = s.owner_chat_id
+ WHERE u.status = 'active'
+ LIMIT $offset, $batch_size
+ ");
+ $count_stmt = pdo()->query("SELECT COUNT(DISTINCT u.chat_id) FROM users u JOIN services s ON u.chat_id = s.owner_chat_id WHERE u.status = 'active'");
+ break;
+ case 'no_service':
+ $stmt = pdo()->query("
+ SELECT chat_id
+ FROM users
+ WHERE status = 'active'
+ AND chat_id NOT IN (SELECT DISTINCT owner_chat_id FROM services)
+ LIMIT $offset, $batch_size
+ ");
+ $count_stmt = pdo()->query("SELECT COUNT(*) FROM users WHERE status = 'active' AND chat_id NOT IN (SELECT DISTINCT owner_chat_id FROM services)");
+ break;
+ case 'active_service':
+ $now = time();
+ $stmt = pdo()->prepare("
+ SELECT DISTINCT u.chat_id
+ FROM users u
+ JOIN services s ON u.chat_id = s.owner_chat_id
+ WHERE u.status = 'active'
+ AND s.expire_timestamp > ?
+ LIMIT $offset, $batch_size
+ ");
+ $stmt->execute([$now]);
+ $count_stmt = pdo()->prepare("SELECT COUNT(DISTINCT u.chat_id) FROM users u JOIN services s ON u.chat_id = s.owner_chat_id WHERE u.status = 'active' AND s.expire_timestamp > ?");
+ $count_stmt->execute([$now]);
+ break;
+ default:
+ $stmt = pdo()->query("SELECT chat_id FROM users WHERE status = 'active' LIMIT $offset, $batch_size");
+ $count_stmt = pdo()->query("SELECT COUNT(*) FROM users WHERE status = 'active'");
+ }
+
+ $users = $stmt->fetchAll(PDO::FETCH_COLUMN);
+ $total_users = $count_stmt->fetchColumn();
+
+ $sent = 0;
+ $failed = 0;
+ $error_details = [];
+
+ // Send messages to this batch
+ foreach ($users as $chat_id) {
+ try {
+ if (!empty($photo_id)) {
+ // Send photo with caption using cURL for better error handling
+ $url = "https://api.telegram.org/bot" . BOT_TOKEN . "/sendPhoto";
+ $data = [
+ 'chat_id' => $chat_id,
+ 'photo' => $photo_id,
+ 'caption' => $message_text,
+ 'parse_mode' => 'HTML'
+ ];
+
+ $ch = curl_init($url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_POST, true);
+ curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
+ curl_setopt($ch, CURLOPT_TIMEOUT, 10);
+ $result = curl_exec($ch);
+ $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ curl_close($ch);
+
+ if ($result && $http_code == 200) {
+ $response = json_decode($result, true);
+ if ($response && isset($response['ok']) && $response['ok']) {
+ $sent++;
+ } else {
+ $failed++;
+ $error_details[] = "Chat $chat_id: " . ($response['description'] ?? 'Unknown error');
+ }
+ } else {
+ $failed++;
+ $error_details[] = "Chat $chat_id: HTTP $http_code";
+ }
+ } else {
+ // Send text message
+ $response = sendMessage($chat_id, $message_text);
+ $decoded = json_decode($response, true);
+ if ($decoded && isset($decoded['ok']) && $decoded['ok']) {
+ $sent++;
+ } else {
+ $failed++;
+ $error_details[] = "Chat $chat_id: " . ($decoded['description'] ?? 'sendMessage failed');
+ }
+ }
+
+ // Small delay to avoid rate limiting
+ usleep(30000); // 30ms delay
+ } catch (Exception $e) {
+ $failed++;
+ $error_details[] = "Chat $chat_id: Exception - " . $e->getMessage();
+ }
+ }
+
+ $response_data = [
+ 'success' => true,
+ 'sent' => $sent,
+ 'failed' => $failed,
+ 'total' => $total_users,
+ 'processed' => $offset + count($users),
+ 'has_more' => ($offset + count($users)) < $total_users
+ ];
+
+ // Include error details in debug mode (first batch only)
+ if ($offset == 0 && !empty($error_details)) {
+ $response_data['debug_errors'] = array_slice($error_details, 0, 5); // First 5 errors
+ }
+
+ echo json_encode($response_data);
+ exit;
+}
+
+// Get statistics for display
+$stats = [];
+$stats['all'] = pdo()->query("SELECT COUNT(*) FROM users WHERE status = 'active'")->fetchColumn();
+$stats['with_service'] = pdo()->query("SELECT COUNT(DISTINCT owner_chat_id) FROM services s JOIN users u ON s.owner_chat_id = u.chat_id WHERE u.status = 'active'")->fetchColumn();
+$stats['no_service'] = $stats['all'] - $stats['with_service'];
+$now = time();
+$stmt = pdo()->prepare("SELECT COUNT(DISTINCT s.owner_chat_id) FROM services s JOIN users u ON s.owner_chat_id = u.chat_id WHERE u.status = 'active' AND s.expire_timestamp > ?");
+$stmt->execute([$now]);
+$stats['active_service'] = $stmt->fetchColumn();
+
+renderHeader('ÙŸÛØ§Ù
ÙÙ
گاÙÛ');
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ در ØØ§Ù Ø¢Ù
ادÙâØ³Ø§Ø²Û...
+ 0%
+
+
+
+
+
+
0
+
+
ارسا٠Ù
ÙÙÙ
+
+
+
0
+
+
Ø§Ø±Ø³Ø§Ù ÙØ§Ù
ÙÙÙ
+
+
+
0
+
+
ک٠کارؚراÙ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ÙÙ
Ù Ú©Ø§Ø±ØšØ±Ø§Ù ÙØ¹Ø§Ù
+
+
+
+
+
+
+
+
+
+
+
+
ؚدÙ٠سرÙÛØ³
+
+
+
+
+
+
+
+
+
+
سرÙÛØ³ ÙØ¹Ø§Ù
+
+
+
+
+
+
+
+
+
+ â ïž ÙØŽØ¯Ø§Ø±: ÙŸÛØ§Ù
ؚ٠تÙ
اÙ
کارؚرا٠گرÙÙ Ø§ÙØªØ®Ø§ØšÛ Ø§Ø±Ø³Ø§Ù Ø®ÙØ§Ùد ؎د. اÛ٠عÙ
Ù ÙØ§ØšÙ ؚرگ؎ت
+ ÙÛØ³Øª!
+
+
+
+
+
+
+
+
+
+
+
+
Ù
ØªÙ ÙŸÛØ§Ù
Ø®ÙØ¯ را ØšÙÙÛØ³Ûد تا اÛÙØ¬Ø§ ÙÙ
Ø§ÛØŽ
+ Ø¯Ø§Ø¯Ù ØŽÙØ¯...
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/web/pages/categories.php b/src/web/pages/categories.php
new file mode 100644
index 0000000..d6b8d8e
--- /dev/null
+++ b/src/web/pages/categories.php
@@ -0,0 +1,147 @@
+prepare("INSERT INTO categories (name, status) VALUES (?, 'active')");
+ if ($stmt->execute([$name])) {
+ $success = 'دستÙâØšÙØ¯Û ؚا Ù
ÙÙÙÛØª اضاÙ٠؎د.';
+ } else {
+ $error = 'خطا در Ø§ÙØ²Ùد٠دستÙâØšÙØ¯Û.';
+ }
+ }
+ }
+}
+
+// Handle delete
+if (isset($_GET['delete'])) {
+ $id = (int) $_GET['delete'];
+ $stmt = pdo()->prepare("DELETE FROM categories WHERE id = ?");
+ if ($stmt->execute([$id])) {
+ $success = 'دستÙâØšÙØ¯Û ØØ°Ù ؎د.';
+ }
+}
+
+// Handle toggle status
+if (isset($_GET['toggle'])) {
+ $id = (int) $_GET['toggle'];
+ $stmt = pdo()->prepare("UPDATE categories SET status = IF(status = 'active', 'inactive', 'active') WHERE id = ?");
+ if ($stmt->execute([$id])) {
+ $success = 'ÙØ¶Ø¹Ûت دستÙâØšÙØ¯Û تغÛÛØ± کرد.';
+ }
+}
+
+// Get all categories
+$categories = getCategories();
+
+renderHeader('Ù
Ø¯ÛØ±Ûت دستÙâØšÙØ¯ÛâÙØ§');
+?>
+
+
+
+
+
+
+
+
+
+
â
+
+
+
+
â
+
+
+
+
+
+
+
+
+ ÙØ§Ù
دستÙâØšÙØ¯Û
+
+
+
+ Ø§ÙØ²ÙدÙ
+
+
+
+
+
+
+
+
+
+
+
ÙÛÚ Ø¯Ø³ØªÙâØšÙØ¯ÛâØ§Û ÛØ§Ùت ÙØŽØ¯.
+
+
+
+
+ ØŽÙØ§Ø³Ù
+ ÙØ§Ù
+ ÙØ¶Ø¹Ûت
+ عÙ
ÙÛØ§Øª
+
+
+
+
+
+
+
+
+
+ â
ÙØ¹Ø§Ù
+
+ â ØºÛØ±ÙØ¹Ø§Ù
+
+
+
+
+
+
+
+ ØØ°Ù
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/web/pages/discount.php b/src/web/pages/discount.php
new file mode 100644
index 0000000..2fd8d79
--- /dev/null
+++ b/src/web/pages/discount.php
@@ -0,0 +1,260 @@
+ 0 && $max_usage > 0) {
+ // Check if code already exists
+ $stmt = pdo()->prepare("SELECT COUNT(*) FROM discount_codes WHERE code = ?");
+ $stmt->execute([$code]);
+ if ($stmt->fetchColumn() > 0) {
+ $error = 'اÛ٠کد تخÙÛÙ ÙØšÙا٠ثؚت ؎د٠است.';
+ } else {
+ $stmt = pdo()->prepare("INSERT INTO discount_codes (code, type, value, max_usage, usage_count, status) VALUES (?, ?, ?, ?, 0, 'active')");
+ if ($stmt->execute([$code, $type, $value, $max_usage])) {
+ $success = 'کد تخÙÛ٠ؚا Ù
ÙÙÙÛØª اضاÙ٠؎د.';
+ } else {
+ $error = 'خطا در Ø§ÙØ²Ùد٠کد تخÙÛÙ.';
+ }
+ }
+ } else {
+ $error = 'ÙØ·Ùا٠تÙ
اÙ
ÙÛÙØ¯Ùا را ØšÙ Ø¯Ø±Ø³ØªÛ ÙŸØ± Ú©ÙÛØ¯.';
+ }
+}
+
+// Handle edit discount
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['edit_discount'])) {
+ $id = (int) $_POST['discount_id'];
+ $code = strtoupper(sanitizeInput($_POST['code']));
+ $type = sanitizeInput($_POST['type']);
+ $value = (float) $_POST['value'];
+ $max_usage = (int) $_POST['max_usage'];
+
+ $stmt = pdo()->prepare("UPDATE discount_codes SET code=?, type=?, value=?, max_usage=? WHERE id=?");
+ if ($stmt->execute([$code, $type, $value, $max_usage, $id])) {
+ $success = 'کد تخÙÛÙ ØšÙØ±ÙزرساÙÛ ØŽØ¯.';
+ } else {
+ $error = 'خطا در ØšÙØ±ÙزرساÙÛ Ú©Ø¯ تخÙÛÙ.';
+ }
+}
+
+// Handle delete
+if (isset($_GET['delete'])) {
+ $id = (int) $_GET['delete'];
+ $stmt = pdo()->prepare("DELETE FROM discount_codes WHERE id = ?");
+ if ($stmt->execute([$id])) {
+ $success = 'کد تخÙÛÙ ØØ°Ù ؎د.';
+ }
+}
+
+// Handle toggle
+if (isset($_GET['toggle'])) {
+ $id = (int) $_GET['toggle'];
+ $stmt = pdo()->prepare("UPDATE discount_codes SET status = IF(status = 'active', 'inactive', 'active') WHERE id = ?");
+ if ($stmt->execute([$id])) {
+ $success = 'ÙØ¶Ø¹Ûت کد تخÙÛ٠تغÛÛØ± کرد.';
+ }
+}
+
+// Get discount for edit
+$edit_discount = null;
+if (isset($_GET['edit'])) {
+ $edit_id = (int) $_GET['edit'];
+ $stmt = pdo()->prepare("SELECT * FROM discount_codes WHERE id = ?");
+ $stmt->execute([$edit_id]);
+ $edit_discount = $stmt->fetch(PDO::FETCH_ASSOC);
+}
+
+// Get all discount codes
+$stmt = pdo()->query("SELECT * FROM discount_codes ORDER BY id DESC");
+$codes = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+renderHeader('Ù
Ø¯ÛØ±Ûت Ú©Ø¯ÙØ§Û تخÙÛÙ');
+?>
+
+
+
+
+
+
+
+
+
+
â
+
+
+
+
â
+
+
+
+
+
+
+
+
+
+
+
+
+
+ کد تخÙÛÙ *
+
+ ÙÙØ· ØØ±Ù٠اÙÚ¯ÙÛØ³Û ٠اعداد
+
+
+
+ ÙÙØ¹ تخÙÛÙ *
+
+ >ð Ø¯Ø±ØµØ¯Û
+ >ð° Ù
ØšÙØº ثاؚت
+
+
+
+
+ Ù
ÙØ¯Ø§Ø± تخÙÛÙ *
+
+ درصد (1-100) ÛØ§ تÙÙ
اÙ
+
+
+
+ ØØ¯Ø§Ú©Ø«Ø± تعداد Ø§Ø³ØªÙØ§Ø¯Ù *
+
+
+
+
+
+
+
+
+
+
+ ÙØºÙ
+
+
+
+
+
+
+
+
+
+
+
+
ÙÛÚ Ú©Ø¯ تخÙÛÙÛ ÛØ§Ùت ÙØŽØ¯.
+
+
+
+
+ کد
+ ÙÙØ¹
+ Ù
ÙØ¯Ø§Ø±
+ Ø§Ø³ØªÙØ§Ø¯Ù
+ ÙØ¶Ø¹Ûت
+ عÙ
ÙÛØ§Øª
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /
+
+
+
+
+ â
ÙØ¹Ø§Ù
+
+ â ØºÛØ±ÙØ¹Ø§Ù
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/web/pages/guides.php b/src/web/pages/guides.php
new file mode 100644
index 0000000..dc19932
--- /dev/null
+++ b/src/web/pages/guides.php
@@ -0,0 +1,239 @@
+prepare("INSERT INTO guides (button_name, content_type, message_text, photo_id, status) VALUES (?, ?, ?, ?, 'active')");
+ if ($stmt->execute([$button_name, $content_type, $message_text, $photo_id])) {
+ $success = 'راÙÙÙ
ا ؚا Ù
ÙÙÙÛØª اضاÙ٠؎د.';
+ } else {
+ $error = 'خطا در Ø§ÙØ²Ùد٠راÙÙÙ
ا.';
+ }
+ } else {
+ $error = 'ÙØ·ÙØ§Ù ÙØ§Ù
دکÙ
٠را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.';
+ }
+}
+
+// Handle edit guide
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['edit_guide'])) {
+ $id = (int) $_POST['guide_id'];
+ $button_name = sanitizeInput($_POST['button_name']);
+ $content_type = sanitizeInput($_POST['content_type']);
+ $message_text = $_POST['message_text'] ?? '';
+ $photo_id = sanitizeInput($_POST['photo_id'] ?? '');
+
+ $stmt = pdo()->prepare("UPDATE guides SET button_name=?, content_type=?, message_text=?, photo_id=? WHERE id=?");
+ if ($stmt->execute([$button_name, $content_type, $message_text, $photo_id, $id])) {
+ $success = 'راÙÙÙ
ا ØšÙØ±ÙزرساÙÛ ØŽØ¯.';
+ } else {
+ $error = 'خطا در ØšÙØ±ÙزرساÙÛ Ø±Ø§ÙÙÙ
ا.';
+ }
+}
+
+// Handle delete
+if (isset($_GET['delete'])) {
+ $id = (int) $_GET['delete'];
+ $stmt = pdo()->prepare("DELETE FROM guides WHERE id = ?");
+ if ($stmt->execute([$id])) {
+ $success = 'راÙÙÙ
ا ØØ°Ù ؎د.';
+ }
+}
+
+// Handle toggle
+if (isset($_GET['toggle'])) {
+ $id = (int) $_GET['toggle'];
+ $stmt = pdo()->prepare("UPDATE guides SET status = IF(status = 'active', 'inactive', 'active') WHERE id = ?");
+ if ($stmt->execute([$id])) {
+ $success = 'ÙØ¶Ø¹Ûت راÙÙÙ
ا تغÛÛØ± کرد.';
+ }
+}
+
+// Get guide for edit
+$edit_guide = null;
+if (isset($_GET['edit'])) {
+ $edit_id = (int) $_GET['edit'];
+ $stmt = pdo()->prepare("SELECT * FROM guides WHERE id = ?");
+ $stmt->execute([$edit_id]);
+ $edit_guide = $stmt->fetch(PDO::FETCH_ASSOC);
+}
+
+// Get all guides
+$stmt = pdo()->query("SELECT * FROM guides ORDER BY id DESC");
+$guides = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+renderHeader('Ù
Ø¯ÛØ±Ûت راÙÙÙ
ا');
+?>
+
+
+
+
+
+
+
+
+
+
â
+
+
+
+
â
+
+
+
+
+
+
+
+
+
+
+
+
+
+ عÙÙØ§Ù دکÙ
Ù *
+
+
+
+
+ ÙÙØ¹ Ù
ØØªÙا *
+
+ >ð Ù
تÙ
+ >ðŒ ØªØµÙÛØ± + Ù
تÙ
+
+
+
+
+
+
+ Ù
ØªÙ ÙŸÛØ§Ù
+
+ از Markdown ØšØ±Ø§Û ÙØ±Ù
ØªâØšÙØ¯Û Ø§Ø³ØªÙØ§Ø¯Ù Ú©ÙÛØ¯
+
+
+
+ ØŽÙØ§Ø³Ù تصÙÛØ± تÙگراÙ
(Photo ID)
+
+ ØšØ±Ø§Û Ø¯Ø±ÛØ§Ùت: ÛÚ© تصÙÛØ± ؚ٠رؚات ارسا٠کÙÛØ¯
+
+
+
+
+
+
+
+
+ ÙØºÙ
+
+
+
+
+
+
+
+
+
+
+
+
ÙÛÚ Ø±Ø§ÙÙÙ
اÛÛ ÛØ§Ùت ÙØŽØ¯.
+
+
+
+
+ ØŽÙØ§Ø³Ù
+ عÙÙØ§Ù دکÙ
Ù
+ ÙÙØ¹ Ù
ØØªÙا
+ ÙØ¶Ø¹Ûت
+ عÙ
ÙÛØ§Øª
+
+
+
+
+
+
+
+
+
+
+
+
+ â
ÙØ¹Ø§Ù
+
+ â ØºÛØ±ÙØ¹Ø§Ù
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/web/pages/payments.php b/src/web/pages/payments.php
new file mode 100644
index 0000000..d3dd364
--- /dev/null
+++ b/src/web/pages/payments.php
@@ -0,0 +1,256 @@
+prepare("SELECT * FROM payment_requests WHERE id = ? AND status = 'pending'");
+ $stmt->execute([$payment_id]);
+ $payment = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ if ($payment) {
+ // Update user balance
+ updateUserBalance($payment['user_id'], $payment['amount'], 'add');
+
+ // Update payment status
+ $stmt = pdo()->prepare("UPDATE payment_requests SET status='approved', processed_at=NOW(), processed_by_admin_id=? WHERE id=?");
+ $stmt->execute([ADMIN_CHAT_ID, $payment_id]);
+
+ // Send notification to user via bot
+ sendMessage($payment['user_id'], "â
Ø¯Ø±Ø®ÙØ§Ø³Øª ØŽØ§Ø±Ú ØØ³Ø§Øš ØŽÙ
ا ØšÙ Ù
ØšÙØº " . number_format($payment['amount']) . " تÙÙ
ا٠تاÛÛØ¯ ؎د.\n\nð° Ù
ÙØ¬ÙØ¯Û Ø¬Ø¯ÛØ¯: " . number_format(getUserBalance($payment['user_id'])) . " تÙÙ
اÙ");
+
+ $success = 'Ø¯Ø±Ø®ÙØ§Ø³Øª تاÛÛØ¯ ؎د Ù Ù
ÙØ¬ÙØ¯Û Ú©Ø§Ø±ØšØ± Ø§ÙØ²Ø§ÛØŽ ÛØ§Ùت.';
+ } else {
+ $error = 'Ø¯Ø±Ø®ÙØ§Ø³Øª ÛØ§Ùت ÙØŽØ¯ ÛØ§ ÙØšÙا٠ٟرداز؎ ؎د٠است.';
+ }
+}
+
+// Handle reject payment
+if (isset($_GET['reject'])) {
+ $payment_id = (int) $_GET['reject'];
+ $stmt = pdo()->prepare("SELECT * FROM payment_requests WHERE id = ? AND status = 'pending'");
+ $stmt->execute([$payment_id]);
+ $payment = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ if ($payment) {
+ $stmt = pdo()->prepare("UPDATE payment_requests SET status='rejected', processed_at=NOW(), processed_by_admin_id=? WHERE id=?");
+ $stmt->execute([ADMIN_CHAT_ID, $payment_id]);
+
+ // Send notification to user via bot
+ sendMessage($payment['user_id'], "â Ø¯Ø±Ø®Ùاست ØŽØ§Ø±Ú ØØ³Ø§Øš ØŽÙ
ا ØšÙ Ù
ØšÙØº " . number_format($payment['amount']) . " تÙÙ
ا٠رد ؎د.\n\nÙØ·Ùا٠ؚا ÙŸØŽØªÛØšØ§ÙÛ ØªÙ
اس ØšÚ¯ÛØ±Ûد.");
+
+ $success = 'Ø¯Ø±Ø®ÙØ§Ø³Øª رد ؎د.';
+ } else {
+ $error = 'Ø¯Ø±Ø®ÙØ§Ø³Øª ÛØ§Ùت ÙØŽØ¯ ÛØ§ ÙØšÙا٠ٟرداز؎ ؎د٠است.';
+ }
+}
+
+// Get pending payment requests
+$stmt = pdo()->query("
+ SELECT pr.*, u.first_name
+ FROM payment_requests pr
+ JOIN users u ON pr.user_id = u.chat_id
+ WHERE pr.status = 'pending'
+ ORDER BY pr.created_at DESC
+");
+$pending_payments = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+// Get processed payment requests (last 50)
+$stmt = pdo()->query("
+ SELECT pr.*, u.first_name
+ FROM payment_requests pr
+ JOIN users u ON pr.user_id = u.chat_id
+ WHERE pr.status != 'pending'
+ ORDER BY pr.processed_at DESC
+ LIMIT 50
+");
+$processed_payments = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+// Get renewal requests
+$stmt = pdo()->query("
+ SELECT rr.*, u.first_name
+ FROM renewal_requests rr
+ JOIN users u ON rr.user_id = u.chat_id
+ WHERE rr.status = 'pending'
+ ORDER BY rr.created_at DESC
+");
+$renewal_requests = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+renderHeader('Ù
Ø¯ÛØ±Ûت ٟرداختâÙØ§ ٠تÙ
Ø¯ÛØ¯Ùا');
+?>
+
+
+
+
+
+
+
+
+
+
â
+
+
+
+
â
+
+
+
+
+
+
+
+
ÙÛÚ Ø¯Ø±Ø®ÙØ§Ø³ØªÛ در Ø§ÙØªØžØ§Ø± ÙÛØ³Øª.
+
+
+
+
+
+ ØŽÙØ§Ø³Ù
+ کارؚر
+ Ù
ØšÙØº
+ ØªØ§Ø±ÛØ®
+ Ø±Ø³ÛØ¯
+ عÙ
ÙÛØ§Øª
+
+
+
+
+
+
+
+
+
+ رد
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ کارؚر
+ سرÙÛØ³
+ Ø±ÙØ²/ØØ¬Ù
+ Ù
ØšÙØº
+ ØªØ§Ø±ÛØ®
+ Ø±Ø³ÛØ¯
+
+
+
+
+
+
+
+ Ø±ÙØ² /
+ GB
+
+ تÙÙ
اÙ
+
+
+
+
+ Ù
ØŽØ§ÙØ¯Ù
+
+
+ -
+
+
+
+
+
+
+
+
+ â¹ïž ØªÙØ¬Ù: ØšØ±Ø§Û ØªØ§ÛÛØ¯/رد Ø¯Ø±Ø®ÙØ§Ø³ØªâÙØ§Û تÙ
Ø¯ÛØ¯Ø از رؚات تÙگراÙ
Ø§Ø³ØªÙØ§Ø¯Ù Ú©ÙÛØ¯.
+
+
+
+
+
+
+
+
+
+
+
ÙÛÚ Ø¯Ø±Ø®ÙØ§Ø³Øª ٟرداز؎ ؎دÙâØ§Û ÙØ¬Ùد ÙØ¯Ø§Ø±Ø¯.
+
+
+
+
+
+ کارؚر
+ Ù
ØšÙØº
+ ØªØ§Ø±ÛØ® ثؚت
+ ØªØ§Ø±ÛØ® ٟرداز؎
+ ÙØ¶Ø¹Ûت
+
+
+
+
+
+
+ تÙÙ
اÙ
+
+
+
+
+
+ â
تاÛÛØ¯ ؎دÙ
+
+ â Ø±Ø¯ ؎دÙ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/web/pages/plans.php b/src/web/pages/plans.php
new file mode 100644
index 0000000..c710a47
--- /dev/null
+++ b/src/web/pages/plans.php
@@ -0,0 +1,299 @@
+ 0 && $category_id > 0) {
+ $stmt = pdo()->prepare("INSERT INTO plans (server_id, category_id, name, price, volume_gb, duration_days, description, show_sub_link, show_conf_links, is_test_plan, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active')");
+ if ($stmt->execute([$server_id, $category_id, $name, $price, $volume_gb, $duration_days, $description, $show_sub_link, $show_conf_links, $is_test_plan])) {
+ $success = 'ÙŸÙ٠ؚا Ù
ÙÙÙÛØª اضاÙ٠؎د.';
+ } else {
+ $error = 'خطا در Ø§ÙØ²Ùد٠ٟÙÙ.';
+ }
+ } else {
+ $error = 'ÙØ·Ùا٠تÙ
اÙ
ÙÛÙØ¯ÙØ§Û Ø§ÙØ²Ø§Ù
Û Ø±Ø§ ٟر Ú©ÙÛØ¯.';
+ }
+}
+
+// Handle edit plan
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['edit_plan'])) {
+ $id = (int) $_POST['plan_id'];
+ $server_id = (int) $_POST['server_id'];
+ $category_id = (int) $_POST['category_id'];
+ $name = sanitizeInput($_POST['name']);
+ $price = (float) $_POST['price'];
+ $volume_gb = (int) $_POST['volume_gb'];
+ $duration_days = (int) $_POST['duration_days'];
+ $description = sanitizeInput($_POST['description']);
+ $show_sub_link = isset($_POST['show_sub_link']) ? 1 : 0;
+ $show_conf_links = isset($_POST['show_conf_links']) ? 1 : 0;
+
+ $stmt = pdo()->prepare("UPDATE plans SET server_id=?, category_id=?, name=?, price=?, volume_gb=?, duration_days=?, description=?, show_sub_link=?, show_conf_links=? WHERE id=?");
+ if ($stmt->execute([$server_id, $category_id, $name, $price, $volume_gb, $duration_days, $description, $show_sub_link, $show_conf_links, $id])) {
+ $success = 'ÙŸÙÙ ØšÙØ±ÙزرساÙÛ ØŽØ¯.';
+ } else {
+ $error = 'خطا در ØšÙØ±ÙزرساÙÛ ÙŸÙÙ.';
+ }
+}
+
+// Handle delete
+if (isset($_GET['delete'])) {
+ $id = (int) $_GET['delete'];
+ $stmt = pdo()->prepare("DELETE FROM plans WHERE id = ?");
+ if ($stmt->execute([$id])) {
+ $success = 'ÙŸÙÙ ØØ°Ù ؎د.';
+ }
+}
+
+// Handle toggle status
+if (isset($_GET['toggle'])) {
+ $id = (int) $_GET['toggle'];
+ $stmt = pdo()->prepare("UPDATE plans SET status = IF(status = 'active', 'inactive', 'active') WHERE id = ?");
+ if ($stmt->execute([$id])) {
+ $success = 'ÙØ¶Ø¹Ûت ÙŸÙ٠تغÛÛØ± کرد.';
+ }
+}
+
+// Get plan for edit
+$edit_plan = null;
+if (isset($_GET['edit'])) {
+ $edit_id = (int) $_GET['edit'];
+ $stmt = pdo()->prepare("SELECT * FROM plans WHERE id = ?");
+ $stmt->execute([$edit_id]);
+ $edit_plan = $stmt->fetch(PDO::FETCH_ASSOC);
+}
+
+// Get servers and categories for form
+$servers = getServers();
+$categories = getCategories();
+
+// Get all plans with server info
+$stmt = pdo()->query("
+ SELECT p.*, s.name as server_name, s.type as server_type, c.name as category_name
+ FROM plans p
+ LEFT JOIN servers s ON p.server_id = s.id
+ LEFT JOIN categories c ON p.category_id = c.id
+ ORDER BY p.is_test_plan DESC, p.id ASC
+");
+$plans = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+renderHeader('Ù
Ø¯ÛØ±Ûت ÙŸÙÙâÙØ§');
+?>
+
+
+
+
+
+
+
+
+
+
â
+
+
+
+
â
+
+
+
+
+
+
+
+
+
+
+
ÙÛÚ ÙŸÙÙÛ ÛØ§Ùت ÙØŽØ¯.
+
+
+
+
+
+ ØŽÙØ§Ø³Ù
+ ÙØ§Ù
+ Ø³Ø±ÙØ±
+ دستÙâØšÙØ¯Û
+ ÙÛÙ
ت
+ ØØ¬Ù
/Ù
دت
+ ÙØ¶Ø¹Ûت
+ عÙ
ÙÛØ§Øª
+
+
+
+
+
+
+
+
+ ð§ª
+
+
+
+
+
+ تÙÙ
اÙ
+ GB / Ø±ÙØ²
+
+
+
+ â
ÙØ¹Ø§Ù
+
+ â ØºÛØ±ÙØ¹Ø§Ù
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/web/pages/servers.php b/src/web/pages/servers.php
new file mode 100644
index 0000000..3ecda27
--- /dev/null
+++ b/src/web/pages/servers.php
@@ -0,0 +1,369 @@
+prepare("INSERT INTO servers (name, url, sub_host, marzban_protocols, username, password, type, status) VALUES (?, ?, ?, ?, ?, ?, ?, 'active')");
+ if ($stmt->execute([$name, $url, $sub_host, $protocols_json, $username, $password, $type])) {
+ $success = 'Ø³Ø±ÙØ± ؚا Ù
ÙÙÙÛØª اضاÙ٠؎د.';
+ } else {
+ $error = 'خطا در Ø§ÙØ²ÙØ¯Ù Ø³Ø±ÙØ±.';
+ }
+ } else {
+ $error = 'ÙØ·Ùا٠تÙ
اÙ
ÙÛÙØ¯Ùا را ٟر Ú©ÙÛØ¯.';
+ }
+}
+
+// Handle edit server
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['edit_server'])) {
+ $id = (int) $_POST['server_id'];
+ $name = sanitizeInput($_POST['name']);
+ $url = sanitizeInput($_POST['url']);
+ $username = sanitizeInput($_POST['username']);
+ $password = $_POST['password'];
+ $type = sanitizeInput($_POST['type']);
+ $sub_host = sanitizeInput($_POST['sub_host'] ?? '');
+
+ // Handle protocols for marzban
+ $protocols_json = null;
+ if ($type === 'marzban') {
+ if (isset($_POST['protocols']) && !empty($_POST['protocols'])) {
+ $protocols = array_map('sanitizeInput', $_POST['protocols']);
+ $protocols_json = json_encode(array_values($protocols));
+ } else {
+ // Default to VLESS if no protocols selected
+ $protocols_json = json_encode(['vless']);
+ }
+ }
+
+ // Only update password if provided
+ if (!empty($password)) {
+ $stmt = pdo()->prepare("UPDATE servers SET name=?, url=?, sub_host=?, marzban_protocols=?, username=?, password=?, type=? WHERE id=?");
+ $success = $stmt->execute([$name, $url, $sub_host, $protocols_json, $username, $password, $type, $id]);
+ } else {
+ $stmt = pdo()->prepare("UPDATE servers SET name=?, url=?, sub_host=?, marzban_protocols=?, username=?, type=? WHERE id=?");
+ $success = $stmt->execute([$name, $url, $sub_host, $protocols_json, $username, $type, $id]);
+ }
+
+ if ($success) {
+ $success = 'Ø³Ø±ÙØ± ØšÙØ±ÙزرساÙÛ ØŽØ¯.';
+ } else {
+ $error = 'خطا در ØšÙØ±ÙزرساÙÛ Ø³Ø±ÙØ±.';
+ }
+}
+
+// Handle delete
+if (isset($_GET['delete'])) {
+ $id = (int) $_GET['delete'];
+ $stmt = pdo()->prepare("DELETE FROM servers WHERE id = ?");
+ if ($stmt->execute([$id])) {
+ $success = 'Ø³Ø±ÙØ± ØØ°Ù ؎د.';
+ }
+}
+
+// Handle toggle status
+if (isset($_GET['toggle'])) {
+ $id = (int) $_GET['toggle'];
+ $stmt = pdo()->prepare("UPDATE servers SET status = IF(status = 'active', 'inactive', 'active') WHERE id = ?");
+ if ($stmt->execute([$id])) {
+ $success = 'ÙØ¶Ø¹Ûت Ø³Ø±ÙØ± تغÛÛØ± کرد.';
+ }
+}
+
+// Get server for edit
+$edit_server = null;
+if (isset($_GET['edit'])) {
+ $edit_id = (int) $_GET['edit'];
+ $stmt = pdo()->prepare("SELECT * FROM servers WHERE id = ?");
+ $stmt->execute([$edit_id]);
+ $edit_server = $stmt->fetch(PDO::FETCH_ASSOC);
+}
+
+// Get all servers
+$stmt = pdo()->query("SELECT * FROM servers ORDER BY id DESC");
+$servers = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+renderHeader('Ù
Ø¯ÛØ±Ûت Ø³Ø±ÙØ±Ùا');
+?>
+
+
+
+
+
+
+
+
+
+
â
+
+
+
+
â
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ÙØ§Ù
Ø³Ø±ÙØ± *
+
+
+
+
+ ÙÙØ¹ ÙŸÙÙ *
+
+ >ð· Marzban
+ >ð¶ Sanaei
+ >ðµ Marzneshin
+
+
+
+
+
+ آدرس ÙŸÙÙ *
+
+ ؚدÙÙ / در Ø§ÙØªÙا
+
+
+
+ آدرس ا؎تراک Ø³ÙØ§Ø±ØŽÛ (Ø§Ø®ØªÛØ§Ø±Û)
+
+ در ØµÙØ±Øª خاÙÛ ØšÙØ¯ÙØ Ø§Ø² آدرس ÙŸÙÙ Ø§Ø³ØªÙØ§Ø¯Ù Ù
ÛâØŽÙØ¯
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ÙØºÙ
+
+
+
+
+
+
+
+
+
+
+
+
ÙÛÚ Ø³Ø±ÙØ±Û ÛØ§Ùت ÙØŽØ¯.
+
+
+
+
+
+ ØŽÙØ§Ø³Ù
+ ÙØ§Ù
+ ÙÙØ¹
+ آدرس
+ ÙŸØ±ÙØªÚ©ÙâÙØ§ / ÙÛÚÚ¯ÛâÙØ§
+ ÙØ¶Ø¹Ûت
+ عÙ
ÙÛØ§Øª
+
+
+
+
+
+
+
+
+ 'ð· Marzban',
+ 'sanaei' => 'ð¶ Sanaei',
+ 'marzneshin' => 'ðµ Marzneshin'
+ ];
+ echo $type_labels[$server['type']] ?? $server['type'];
+ ?>
+
+
+
+
+
+
+ ';
+ foreach ($protocols as $protocol) {
+ echo '' . htmlspecialchars($protocol) . ' ';
+ }
+ echo '';
+ } else {
+ echo 'ÙÙ
Ù ';
+ }
+ ?>
+
+ ð Custom Sub
+
+ â
+
+
+
+
+ â
ÙØ¹Ø§Ù
+
+ â ØºÛØ±ÙØ¹Ø§Ù
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/web/pages/settings.php b/src/web/pages/settings.php
new file mode 100644
index 0000000..0dc13b1
--- /dev/null
+++ b/src/web/pages/settings.php
@@ -0,0 +1,345 @@
+ sanitizeInput($_POST['card_number'] ?? ''),
+ 'card_holder' => sanitizeInput($_POST['card_owner_name'] ?? ''),
+ 'copy_enabled' => true
+ ];
+
+ // Verification
+ $settings_to_update['verification_method'] = $_POST['verification_method'] ?? 'off';
+ $settings_to_update['verification_iran_only'] = $_POST['verification_iran_only'] ?? 'off';
+
+ // Test config
+ $settings_to_update['test_config_usage_limit'] = (int) ($_POST['test_config_usage_limit'] ?? 1);
+
+ // Notifications
+ $settings_to_update['notification_expire_status'] = $_POST['notification_expire_status'] ?? 'off';
+ $settings_to_update['notification_expire_days'] = (int) ($_POST['notification_expire_days'] ?? 3);
+ $settings_to_update['notification_expire_gb'] = (int) ($_POST['notification_expire_gb'] ?? 1);
+ $settings_to_update['notification_inactive_status'] = $_POST['notification_inactive_status'] ?? 'off';
+ $settings_to_update['notification_inactive_days'] = (int) ($_POST['notification_inactive_days'] ?? 30);
+
+ // Renewal
+ $settings_to_update['renewal_status'] = $_POST['renewal_status'] ?? 'off';
+ $settings_to_update['renewal_price_per_day'] = (int) ($_POST['renewal_price_per_day'] ?? 1000);
+ $settings_to_update['renewal_price_per_gb'] = (int) ($_POST['renewal_price_per_gb'] ?? 2000);
+
+ saveSettings($settings_to_update);
+ $success = 'ØªÙØžÛÙ
ات ؚا Ù
ÙÙÙÛØª Ø°Ø®ÛØ±Ù ؎د.';
+}
+
+// Get current settings
+$settings = getSettings();
+
+// Extract card info from payment_method
+$card_number = $settings['payment_method']['card_number'] ?? '';
+$card_owner_name = $settings['payment_method']['card_holder'] ?? '';
+
+renderHeader('ØªÙØžÛÙ
ات');
+?>
+
+
+
+
+
+
+
+
+
+
â
+
+
+
+
â
+
+
+
+
+
+
+
+
+
+ ÙØ¶Ø¹Ûت رؚات
+
+ >â
ÙØ¹Ø§Ù
+ >â ØºÛØ±ÙØ¹Ø§Ù
+
+
+
+
+ ÙØ¶Ø¹Ûت ÙØ±ÙØŽ
+
+ >â
ÙØ¹Ø§Ù
+ >â ØºÛØ±ÙØ¹Ø§Ù
+
+
+
+
+ دکÙ
ÙâÙØ§Û Inline
+
+ >â
ÙØ¹Ø§Ù
+ >â ØºÛØ±ÙØ¹Ø§Ù
+
+
+
+
+
+
+
+
+
+
+
+
+ ÙØ¶Ø¹Ûت عضÙÛØª اجؚارÛ
+
+ >â
ÙØ¹Ø§Ù
+ >â ØºÛØ±ÙØ¹Ø§Ù
+
+
+
+
+ Ø¢ÛØ¯Û Ú©Ø§ÙØ§Ù (ؚا @)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Ù
ØšÙØº ÙØ¯ÛÙ (تÙÙ
اÙ)
+
+
+
+
+
+
+
+
+
+
+
+ ÙØ¶Ø¹Ûت درگا٠زرÛÙâٟاÙ
+
+ >â
ÙØ¹Ø§Ù
+ >â ØºÛØ±ÙØ¹Ø§Ù
+
+
+
+
+ Merchant ID زرÛÙâٟاÙ
+
+
+
+
+
+
+
+
+
+
+
+
+ Ø±ÙØŽ Ø§ØØ±Ø§Ø² ÙÙÛØª
+
+ >â ØºÛØ±ÙØ¹Ø§Ù
+ >ð± ØŽÙ
ار٠تÙÙÙ
+
+
+
+
+ ÙÙØ· ØŽÙ
ارÙâÙØ§Û Ø§ÛØ±Ø§Ù
+
+ >â
ØšÙÙ
+ >â Ø®Ûر
+
+
+
+
+
+
+
+
+
+
+
+ تعداد Ø¯ÙØ¹Ø§Øª Ù
جاز Ø¯Ø±ÛØ§Ùت (ÙØ± کارؚر)
+
+
+
+
+
+
+
+
+
+
+
+
+ Ø§Ø¹ÙØ§Ù اÙÙØ¶Ø§ سرÙÛØ³
+
+ >â
ÙØ¹Ø§Ù
+ >â ØºÛØ±ÙØ¹Ø§Ù
+
+
+
+
+ تعداد Ø±ÙØ² ÙØšÙ از اÙÙØ¶Ø§
+
+
+
+
+ ØØ¬Ù
ؚاÙÛÙ
Ø§ÙØ¯Ù (GB)
+
+
+
+
+
+
+
+
+ Ø§Ø¹ÙØ§Ù عدÙ
ÙØ¹Ø§ÙÛØª
+
+ >â
ÙØ¹Ø§Ù
+ >â ØºÛØ±ÙØ¹Ø§Ù
+
+
+
+
+ تعداد Ø±ÙØ² عدÙ
ÙØ¹Ø§ÙÛØª
+
+
+
+
+
+
+
+
+
+
+
+
+ ÙØ¶Ø¹Ûت تÙ
Ø¯ÛØ¯
+
+ >â
ÙØ¹Ø§Ù
+ >â ØºÛØ±ÙØ¹Ø§Ù
+
+
+
+
+ ÙÛÙ
ت ÙØ± Ø±ÙØ² (تÙÙ
اÙ)
+
+
+
+
+ ÙÛÙ
ت ÙØ± Ú¯ÛÚ¯Ø§ØšØ§ÛØª (تÙÙ
اÙ)
+
+
+
+
+
+
+
+ Ø°Ø®ÛØ±Ù تÙ
اÙ
ØªÙØžÛÙ
ات
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/web/pages/stats.php b/src/web/pages/stats.php
new file mode 100644
index 0000000..99476ba
--- /dev/null
+++ b/src/web/pages/stats.php
@@ -0,0 +1,190 @@
+query("SELECT COUNT(*) FROM users");
+$stats['total_users'] = $stmt->fetchColumn();
+
+$stmt = pdo()->query("SELECT COUNT(*) FROM users WHERE status = 'active'");
+$stats['active_users'] = $stmt->fetchColumn();
+
+$stmt = pdo()->query("SELECT COUNT(*) FROM users WHERE status = 'banned'");
+$stats['banned_users'] = $stmt->fetchColumn();
+
+// Services stats
+$stmt = pdo()->query("SELECT COUNT(*) FROM services");
+$stats['total_services'] = $stmt->fetchColumn();
+
+$now = time();
+$stmt = pdo()->prepare("SELECT COUNT(*) FROM services WHERE expire_timestamp > ?");
+$stmt->execute([$now]);
+$stats['active_services'] = $stmt->fetchColumn();
+
+$stmt = pdo()->prepare("SELECT COUNT(*) FROM services WHERE expire_timestamp <= ?");
+$stmt->execute([$now]);
+$stats['expired_services'] = $stmt->fetchColumn();
+
+// Income stats
+$income_stats = calculateIncomeStats();
+
+renderHeader('Ø¢Ù
ار ٠گزار؎ات');
+?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ک٠کارؚراÙ
+
+
+
+
+
+
+
+
+
+
Ú©Ø§Ø±ØšØ±Ø§Ù ÙØ¹Ø§Ù
+
+
+
+
+
+
+
+
+
+
کارؚرا٠Ù
Ø³Ø¯ÙØ¯
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ک٠سرÙÛØ³âÙØ§
+
+
+
+
+
+
+
+
+
+
سرÙÛØ³âÙØ§Û ÙØ¹Ø§Ù
+
+
+
+
+
+
+
+
+
+
سرÙÛØ³âÙØ§Û Ù
ÙÙØ¶Û ؎دÙ
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
درآÙ
د اÙ
Ø±ÙØ² (تÙÙ
اÙ)
+
+
+
+
+
+
+
+
+
+
درآÙ
د ÙÙØªÙ (تÙÙ
اÙ)
+
+
+
+
+
+
+
+
+
+
درآÙ
د Ù
ا٠(تÙÙ
اÙ)
+
+
+
+
+
+
+
+
+
+
درآÙ
د سا٠(تÙÙ
اÙ)
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/web/pages/users.php b/src/web/pages/users.php
new file mode 100644
index 0000000..e7569f0
--- /dev/null
+++ b/src/web/pages/users.php
@@ -0,0 +1,324 @@
+query("SELECT COUNT(*) FROM users")->fetchColumn();
+$stats['active'] = pdo()->query("SELECT COUNT(*) FROM users WHERE status = 'active'")->fetchColumn();
+$stats['banned'] = pdo()->query("SELECT COUNT(*) FROM users WHERE status = 'banned'")->fetchColumn();
+
+// Pagination for user list
+$page = isset($_GET['page']) ? max(1, (int) $_GET['page']) : 1;
+$per_page = 20;
+$offset = ($page - 1) * $per_page;
+
+// Get total users count
+$total_users = $stats['total'];
+$total_pages = ceil($total_users / $per_page);
+
+// Get all users with pagination
+$stmt = pdo()->prepare("
+ SELECT u.*,
+ COUNT(DISTINCT s.id) as service_count
+ FROM users u
+ LEFT JOIN services s ON u.chat_id = s.owner_chat_id
+ GROUP BY u.chat_id
+ ORDER BY u.created_at DESC
+ LIMIT ? OFFSET ?
+");
+$stmt->execute([$per_page, $offset]);
+$all_users = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+// Handle search
+if (isset($_GET['search'])) {
+ $chat_id = $_GET['chat_id'] ?? '';
+
+ if (!empty($chat_id) && is_numeric($chat_id)) {
+ $stmt = pdo()->prepare("SELECT * FROM users WHERE chat_id = ?");
+ $stmt->execute([$chat_id]);
+ $user_info = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ if (!$user_info) {
+ $error = 'کارؚر ÛØ§Ùت ÙØŽØ¯.';
+ }
+ }
+}
+
+// Handle add balance
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_balance'])) {
+ $chat_id = (int) $_POST['chat_id'];
+ $amount = (int) $_POST['amount'];
+
+ if ($amount != 0) {
+ updateUserBalance($chat_id, abs($amount), $amount > 0 ? 'add' : 'subtract');
+ $success = 'Ù
ÙØ¬ÙØ¯Û Ú©Ø§Ø±ØšØ± ØšÙâØ±ÙØ²Ø±Ø³Ø§ÙÛ ØŽØ¯.';
+
+ // Refresh user info if viewing a user
+ if (isset($_GET['search']) && isset($_GET['chat_id'])) {
+ $stmt = pdo()->prepare("SELECT * FROM users WHERE chat_id = ?");
+ $stmt->execute([$chat_id]);
+ $user_info = $stmt->fetch(PDO::FETCH_ASSOC);
+ }
+ }
+}
+
+// Handle ban/unban
+if (isset($_GET['ban'])) {
+ $chat_id = (int) $_GET['ban'];
+ setUserStatus($chat_id, 'banned');
+ $success = 'کارؚر Ù
Ø³Ø¯ÙØ¯ ؎د.';
+ header('Location: ?');
+ exit;
+}
+
+if (isset($_GET['unban'])) {
+ $chat_id = (int) $_GET['unban'];
+ setUserStatus($chat_id, 'active');
+ $success = 'کارؚر آزاد ؎د.';
+ header('Location: ?');
+ exit;
+}
+
+renderHeader('Ù
Ø¯ÛØ±Ûت کارؚراÙ');
+?>
+
+
+
+
+
+
+
+
+
+
â
+
+
+
+
â
+
+
+
+
+
+
+
+
+
+
+
ک٠کارؚراÙ
+
+
+
+
+
+
+
+
+
+
Ú©Ø§Ø±ØšØ±Ø§Ù ÙØ¹Ø§Ù
+
+
+
+
+
+
+
+
+
+
کارؚرا٠Ù
Ø³Ø¯ÙØ¯
+
+
+
+
+
+
+
+
+
+
+ ØŽÙØ§Ø³Ù Ø¹Ø¯Ø¯Û Ú©Ø§Ø±ØšØ± (Chat ID)
+
+
+
+ جستجÙ
+
+
+
+
+
+
+
+
+
+
+
ÙØ§Ù
:
+
ØŽÙØ§Ø³Ù:
+
Ù
ÙØ¬ÙدÛ: تÙÙ
اÙ
+
ÙØ¶Ø¹Ûت:
+
+ â
ÙØ¹Ø§Ù
+
+ â Ù
Ø³Ø¯ÙØ¯
+
+
+
ØªØ§Ø±ÛØ® ثؚتâÙØ§Ù
:
+
+
+
+
+
+
+
+
+
+
+ تغÛÛØ± Ù
ÙØ¬ÙدÛ
+
+
+
+
+
+ Ù
Ø³Ø¯ÙØ¯ کردÙ
+
+
+
+ آزاد کردÙ
+
+
+
+
+
+
سرÙÛØ³âÙØ§Û کارؚر ()
+
+
کارؚر ÙÛÚ Ø³Ø±ÙÛØ³Û ÙØ¯Ø§Ø±Ø¯.
+
+
+
+
+ ÙŸÙÙ
+ ÙØ§Ù
سرÙÛØ³
+ ØªØ§Ø±ÛØ® Ø®Ø±ÛØ¯
+ اÙÙØ¶Ø§
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ÙÛÚ Ú©Ø§Ø±ØšØ±Û ÛØ§Ùت ÙØŽØ¯.
+
+
+
+
+
+ ÙØ§Ù
+ Chat ID
+ Ù
ÙØ¬ÙدÛ
+ تعداد سرÙÛØ³
+ ÙØ¶Ø¹Ûت
+ ØªØ§Ø±ÛØ® عضÙÛØª
+ عÙ
ÙÛØ§Øª
+
+
+
+
+
+
+
+ تÙÙ
اÙ
+
+
+
+ â
ÙØ¹Ø§Ù
+
+ â Ù
Ø³Ø¯ÙØ¯
+
+
+
+
+
+ Ù
ØŽØ§ÙØ¯Ù
+
+
+
+
+
+
+
+
+
+ 1): ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
From 3e5018c46116f680720f535094d625afac4551fb Mon Sep 17 00:00:00 2001
From: poryajp <118083331+poryajp@users.noreply.github.com>
Date: Sat, 29 Nov 2025 03:47:49 +0330
Subject: [PATCH 05/18] 0.1.1 beta
---
src/bot.php | 1426 ++++++++++++++---------------
src/includes/config.php | 9 +-
src/includes/functions.php | 129 +++
src/web/assets/css/style.css | 284 +++++-
src/web/assets/js/main.js | 104 ++-
src/web/pages/users.php | 347 ++++---
src/web/user/assets/css/style.css | 255 ++++++
src/web/user/assets/js/app.js | 38 +
src/web/user/auth.php | 52 ++
src/web/user/debug.html | 150 +++
src/web/user/guides.php | 85 ++
src/web/user/index.php | 215 +++++
src/web/user/services.php | 248 +++++
src/web/user/session.php | 36 +
src/web/user/shop.php | 312 +++++++
src/web/user/simple-test.html | 72 ++
src/web/user/support.php | 167 ++++
src/web/user/test.php | 28 +
src/web/user/wallet.php | 292 ++++++
19 files changed, 3331 insertions(+), 918 deletions(-)
create mode 100644 src/web/user/assets/css/style.css
create mode 100644 src/web/user/assets/js/app.js
create mode 100644 src/web/user/auth.php
create mode 100644 src/web/user/debug.html
create mode 100644 src/web/user/guides.php
create mode 100644 src/web/user/index.php
create mode 100644 src/web/user/services.php
create mode 100644 src/web/user/session.php
create mode 100644 src/web/user/shop.php
create mode 100644 src/web/user/simple-test.html
create mode 100644 src/web/user/support.php
create mode 100644 src/web/user/test.php
create mode 100644 src/web/user/wallet.php
diff --git a/src/bot.php b/src/bot.php
index 8937b8c..f3e56b7 100644
--- a/src/bot.php
+++ b/src/bot.php
@@ -2,8 +2,7 @@
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
-}
-elseif (function_exists('litespeed_finish_request')) {
+} elseif (function_exists('litespeed_finish_request')) {
litespeed_finish_request();
}
@@ -39,8 +38,7 @@
if (isset($update['callback_query'])) {
$chat_id = $update['callback_query']['message']['chat']['id'];
$first_name = $update['callback_query']['from']['first_name'];
-}
-elseif (isset($update['message']['chat']['id'])) {
+} elseif (isset($update['message']['chat']['id'])) {
$chat_id = $update['message']['chat']['id'];
$first_name = $update['message']['from']['first_name'];
}
@@ -90,8 +88,7 @@
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
deleteMessage($chat_id, $message_id);
handleMainMenu($chat_id, $first_name, true);
- }
- else {
+ } else {
apiRequest('answerCallbackQuery', [
'callback_query_id' => $callback_id,
'text' => 'â ØŽÙ
ا ÙÙÙØ² در Ú©Ø§ÙØ§Ù Ø¹Ø¶Ù ÙØŽØ¯ÙâØ§ÛØ¯!',
@@ -122,67 +119,66 @@
]);
die;
}
-
+
// --- Ù
Ø¯ÛØ±Ûت ٟرداخت Ù
ستÙÛÙ
ØšØ±Ø§Û Ø®Ø±ÛØ¯ ÙŸÙÙ ---
if (strpos($data, 'charge_for_plan_') === 0) {
- $parts = explode('_', $data);
- $amount_to_charge = (int)$parts[3];
- $plan_id_to_buy = (int)$parts[4];
- $discount_code_to_use = (isset($parts[5]) && !empty($parts[5])) ? $parts[5] : null;
- $custom_name_encoded = $parts[6] ?? '';
- $custom_name = base64_decode($custom_name_encoded);
-
- $description = "تکÙ
ÛÙ Ø®Ø±ÛØ¯ ÙŸÙÙ #{$plan_id_to_buy}";
- $metadata = [
- "purpose" => "complete_purchase",
- "plan_id" => $plan_id_to_buy,
- "user_id" => $chat_id,
- "custom_name" => $custom_name // Ø°Ø®ÛØ±Ù ÙØ§Ù
Ø¯ÙØ®ÙاÙ
- ];
- if ($discount_code_to_use) {
- $metadata["discount_code"] = $discount_code_to_use;
- }
+ $parts = explode('_', $data);
+ $amount_to_charge = (int) $parts[3];
+ $plan_id_to_buy = (int) $parts[4];
+ $discount_code_to_use = (isset($parts[5]) && !empty($parts[5])) ? $parts[5] : null;
+ $custom_name_encoded = $parts[6] ?? '';
+ $custom_name = base64_decode($custom_name_encoded);
+
+ $description = "تکÙ
ÛÙ Ø®Ø±ÛØ¯ ÙŸÙÙ #{$plan_id_to_buy}";
+ $metadata = [
+ "purpose" => "complete_purchase",
+ "plan_id" => $plan_id_to_buy,
+ "user_id" => $chat_id,
+ "custom_name" => $custom_name // Ø°Ø®ÛØ±Ù ÙØ§Ù
Ø¯ÙØ®ÙاÙ
+ ];
+ if ($discount_code_to_use) {
+ $metadata["discount_code"] = $discount_code_to_use;
+ }
- $zarinpal_result = createZarinpalLink($chat_id, $amount_to_charge, $description, $metadata);
- if ($zarinpal_result['success']) {
- $message = "Ⳡدر ØØ§Ù Ø§ÙØªÙا٠ؚ٠درگا٠ٟرداخت... ÙØ·Ùا صؚر Ú©ÙÛØ¯.";
- $keyboard = ['inline_keyboard' => [[['text' => 'ð ÙØ±Ùد ØšÙ ØµÙØÙ ÙŸØ±Ø¯Ø§Ø®Øª', 'url' => $zarinpal_result['url']]]]];
- editMessageText($chat_id, $message_id, $message, $keyboard);
- } else {
- editMessageText($chat_id, $message_id, $zarinpal_result['error']);
- }
- die;
-}
- elseif (strpos($data, 'manual_pay_for_plan_') === 0) {
- $parts = explode('_', $data);
- $amount_to_charge = (int)$parts[4];
- $plan_id_to_buy = (int)$parts[5];
- $discount_code_to_use = (isset($parts[6]) && !empty($parts[6])) ? $parts[6] : null;
- $custom_name_encoded = $parts[7] ?? '';
- $custom_name = base64_decode($custom_name_encoded);
-
- $state_data = [
- 'charge_amount' => $amount_to_charge,
- 'purpose' => 'complete_purchase',
- 'plan_id' => $plan_id_to_buy,
- 'custom_name' => $custom_name, // Ø°Ø®ÛØ±Ù ÙØ§Ù
Ø¯ÙØ®Ùا٠در state
- ];
- if ($discount_code_to_use) {
- $state_data['discount_code'] = $discount_code_to_use;
- }
+ $zarinpal_result = createZarinpalLink($chat_id, $amount_to_charge, $description, $metadata);
+ if ($zarinpal_result['success']) {
+ $message = "Ⳡدر ØØ§Ù Ø§ÙØªÙا٠ؚ٠درگا٠ٟرداخت... ÙØ·Ùا صؚر Ú©ÙÛØ¯.";
+ $keyboard = ['inline_keyboard' => [[['text' => 'ð ÙØ±Ùد ØšÙ ØµÙØÙ ÙŸØ±Ø¯Ø§Ø®Øª', 'url' => $zarinpal_result['url']]]]];
+ editMessageText($chat_id, $message_id, $message, $keyboard);
+ } else {
+ editMessageText($chat_id, $message_id, $zarinpal_result['error']);
+ }
+ die;
+ } elseif (strpos($data, 'manual_pay_for_plan_') === 0) {
+ $parts = explode('_', $data);
+ $amount_to_charge = (int) $parts[4];
+ $plan_id_to_buy = (int) $parts[5];
+ $discount_code_to_use = (isset($parts[6]) && !empty($parts[6])) ? $parts[6] : null;
+ $custom_name_encoded = $parts[7] ?? '';
+ $custom_name = base64_decode($custom_name_encoded);
+
+ $state_data = [
+ 'charge_amount' => $amount_to_charge,
+ 'purpose' => 'complete_purchase',
+ 'plan_id' => $plan_id_to_buy,
+ 'custom_name' => $custom_name, // Ø°Ø®ÛØ±Ù ÙØ§Ù
Ø¯ÙØ®Ùا٠در state
+ ];
+ if ($discount_code_to_use) {
+ $state_data['discount_code'] = $discount_code_to_use;
+ }
- updateUserData($chat_id, 'awaiting_payment_screenshot', $state_data);
+ updateUserData($chat_id, 'awaiting_payment_screenshot', $state_data);
- $settings = getSettings();
- $payment_method = $settings['payment_method'];
- $card_number_display = ($payment_method['copy_enabled'] ?? false) ? "{$payment_method['card_number']}" : $payment_method['card_number'];
- $message = "ØšØ±Ø§Û ØªÚ©Ù
ÛÙ Ø®Ø±ÛØ¯ ØšÙ Ù
ØšÙØº " . number_format($amount_to_charge) . " تÙÙ
Ø§Ù Ø ÙØ·Ùا Ù
ØšÙØº را ØšÙ Ø§Ø·ÙØ§Ø¹Ø§Øª Ø²ÛØ± ÙØ§Ø±Ûز ÙÙ
اÛÛØ¯:\n\n" .
- "ð³ ØŽÙ
ار٠کارت:\n" . $card_number_display . "\n" .
- "ð€ ØµØ§ØØš ØØ³Ø§Øš: {$payment_method['card_holder']}\n\n" .
- "ٟس از ÙØ§Ø±ÛØ²Ø ÙØ·Ùا از Ø±Ø³ÛØ¯ ٟرداخت Ø®ÙØ¯ اسکرÛÙâØŽØ§Øª Ú¯Ø±ÙØªÙ ٠در ÙÙ
ÛÙØ¬Ø§ ارسا٠کÙÛØ¯. ٟس از تاÛÛØ¯Ø سرÙÛØ³ ØŽÙ
ا ØšÙ ØµÙØ±Øª Ø®ÙØ¯Ú©Ø§Ø± Ø§ÛØ¬Ø§Ø¯ Ø®ÙØ§Ùد ؎د.";
- editMessageText($chat_id, $message_id, $message);
- die;
-}
+ $settings = getSettings();
+ $payment_method = $settings['payment_method'];
+ $card_number_display = ($payment_method['copy_enabled'] ?? false) ? "{$payment_method['card_number']}" : $payment_method['card_number'];
+ $message = "ØšØ±Ø§Û ØªÚ©Ù
ÛÙ Ø®Ø±ÛØ¯ ØšÙ Ù
ØšÙØº " . number_format($amount_to_charge) . " تÙÙ
Ø§Ù Ø ÙØ·Ùا Ù
ØšÙØº را ØšÙ Ø§Ø·ÙØ§Ø¹Ø§Øª Ø²ÛØ± ÙØ§Ø±Ûز ÙÙ
اÛÛØ¯:\n\n" .
+ "ð³ ØŽÙ
ار٠کارت:\n" . $card_number_display . "\n" .
+ "ð€ ØµØ§ØØš ØØ³Ø§Øš: {$payment_method['card_holder']}\n\n" .
+ "ٟس از ÙØ§Ø±ÛØ²Ø ÙØ·Ùا از Ø±Ø³ÛØ¯ ٟرداخت Ø®ÙØ¯ اسکرÛÙâØŽØ§Øª Ú¯Ø±ÙØªÙ ٠در ÙÙ
ÛÙØ¬Ø§ ارسا٠کÙÛØ¯. ٟس از تاÛÛØ¯Ø سرÙÛØ³ ØŽÙ
ا ØšÙ ØµÙØ±Øª Ø®ÙØ¯Ú©Ø§Ø± Ø§ÛØ¬Ø§Ø¯ Ø®ÙØ§Ùد ؎د.";
+ editMessageText($chat_id, $message_id, $message);
+ die;
+ }
// --- دکÙ
ÙâÙØ§Û Ù
Ø®ØµÙØµ ادÙ
ÛÙâÙØ§ ---
if ($isAnAdmin) {
@@ -193,14 +189,13 @@
sendMessage($chat_id, "ÙØ·Ùا Ù
ØšÙØºÛ Ú©Ù Ù
ÛâØ®ÙØ§ÙÛØ¯ ØšÙ Ù
ÙØ¬ÙØ¯Û Ú©Ø§Ø±ØšØ± $target_id اضاÙÙ Ú©ÙÛØ¯ را ؚ٠تÙÙ
Ø§Ù ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:", $cancelKeyboard);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
die;
- }
- elseif (strpos($data, 'show_user_services_') === 0 && hasPermission($chat_id, 'manage_users')) {
+ } elseif (strpos($data, 'show_user_services_') === 0 && hasPermission($chat_id, 'manage_users')) {
$target_id = str_replace('show_user_services_', '', $data);
$services = getUserServices($target_id);
-
+
$target_user_info = getUserData($target_id);
$target_user_name = htmlspecialchars($target_user_info['first_name'] ?? "کارؚر $target_id");
-
+
if (empty($services)) {
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => "کارؚر {$target_user_name} ÙÛÚ Ø³Ø±ÙÛØ³Û ÙØ¯Ø§Ø±Ø¯.", 'show_alert' => true]);
} else {
@@ -213,28 +208,25 @@
$message_text .= "â«ïž ÙØ§Ù
Ú©Ø§Ø±ØšØ±Û ÙŸÙÙ: {$service['marzban_username']}\n";
$message_text .= "â«ïž ØªØ§Ø±ÛØ® اÙÙØ¶Ø§: {$expire_date}\n---\n";
}
-
+
// ÙŸÛØ§Ù
را در ÛÚ© ÙŸÛØ§Ù
Ø¬Ø¯ÛØ¯ ارسا٠Ù
ÛâÚ©ÙÛÙ
تا Ù
ÙÙÛ Ù
Ø¯ÛØ±Ûت اصÙÛ ØÙØž ØŽÙØ¯
sendMessage($chat_id, $message_text);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
}
die;
- }
- elseif (strpos($data, 'deduct_balance_') === 0 && hasPermission($chat_id, 'manage_users')) {
+ } elseif (strpos($data, 'deduct_balance_') === 0 && hasPermission($chat_id, 'manage_users')) {
$target_id = str_replace('deduct_balance_', '', $data);
updateUserData($chat_id, 'admin_awaiting_amount_for_deduct_balance', ['target_user_id' => $target_id, 'admin_view' => 'admin']);
sendMessage($chat_id, "ÙØ·Ùا Ù
ØšÙØºÛ Ú©Ù Ù
ÛâØ®ÙØ§ÙÛØ¯ از Ù
ÙØ¬ÙØ¯Û Ú©Ø§Ø±ØšØ± $target_id کسر Ú©ÙÛØ¯ را ؚ٠تÙÙ
Ø§Ù ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:", $cancelKeyboard);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
die;
- }
- elseif (strpos($data, 'message_user_') === 0 && hasPermission($chat_id, 'manage_users')) {
+ } elseif (strpos($data, 'message_user_') === 0 && hasPermission($chat_id, 'manage_users')) {
$target_id = str_replace('message_user_', '', $data);
updateUserData($chat_id, 'admin_awaiting_message_for_user', ['target_user_id' => $target_id, 'admin_view' => 'admin']);
sendMessage($chat_id, "ÙŸÛØ§Ù
Ø®ÙØ¯ را ØšØ±Ø§Û Ø§Ø±Ø³Ø§Ù ØšÙ Ú©Ø§Ø±ØšØ± $target_id ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:", $cancelKeyboard);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
die;
- }
- elseif (strpos($data, 'ban_user_') === 0 && hasPermission($chat_id, 'manage_users')) {
+ } elseif (strpos($data, 'ban_user_') === 0 && hasPermission($chat_id, 'manage_users')) {
$target_id = str_replace('ban_user_', '', $data);
if ($target_id == ADMIN_CHAT_ID) {
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â ØŽÙ
ا ÙÙ
ÛâØªÙØ§ÙÛØ¯ Ø®ÙØ¯ØªØ§Ù را Ù
Ø³Ø¯ÙØ¯ Ú©ÙÛØ¯!', 'show_alert' => true]);
@@ -245,16 +237,14 @@
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'کارؚر Ù
Ø³Ø¯ÙØ¯ ؎د']);
}
die;
- }
- elseif (strpos($data, 'unban_user_') === 0 && hasPermission($chat_id, 'manage_users')) {
+ } elseif (strpos($data, 'unban_user_') === 0 && hasPermission($chat_id, 'manage_users')) {
$target_id = str_replace('unban_user_', '', $data);
setUserStatus($target_id, 'active');
sendMessage($target_id, "â
ØŽÙ
ا ØªÙØ³Ø· ادÙ
Û٠از ØØ§Ùت Ù
Ø³Ø¯ÙØ¯Ûت خارج ØŽØ¯ÛØ¯.");
editMessageText($chat_id, $message_id, $update['callback_query']['message']['text'] . "\n\n---\nâ
کارؚر ؚا Ù
ÙÙÙÛØª آزاد ؎د.");
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'کارؚر آزاد ؎د']);
die;
- }
- elseif ($data === 'search_another_user' && hasPermission($chat_id, 'manage_users')) {
+ } elseif ($data === 'search_another_user' && hasPermission($chat_id, 'manage_users')) {
deleteMessage($chat_id, $message_id);
updateUserData($chat_id, 'admin_awaiting_user_search', ['admin_view' => 'admin']);
sendMessage($chat_id, "ÙØ·ÙØ§Ù ØŽÙØ§Ø³Ù Ø¹Ø¯Ø¯Û (Chat ID) کارؚر ØšØ¹Ø¯Û Ø±Ø§ ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:", $cancelKeyboard);
@@ -262,7 +252,7 @@
die;
}
// end
-
+
if (strpos($data, 'delete_cat_') === 0 && hasPermission($chat_id, 'manage_categories')) {
$cat_id = str_replace('delete_cat_', '', $data);
pdo()
@@ -271,82 +261,75 @@
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
ØØ°Ù ؎د']);
deleteMessage($chat_id, $message_id);
generateCategoryList($chat_id);
- }
- elseif (strpos($data, 'charge_zarinpal_') === 0) {
- $amount = (int)str_replace('charge_zarinpal_', '', $data);
- $settings = getSettings();
- $merchant_id = $settings['zarinpal_merchant_id'];
-
- $script_url = 'https://' . $_SERVER['HTTP_HOST'] . rtrim(dirname($_SERVER['PHP_SELF']), '/') . '/verify_payment.php';
-
- $data = [
- "merchant_id" => $merchant_id,
- "amount" => $amount * 10, // تؚدÛ٠تÙÙ
Ø§Ù ØšÙ Ø±ÛØ§Ù
- "callback_url" => $script_url,
- "description" => "ØŽØ§Ø±Ú ØØ³Ø§Øš Ú©Ø§Ø±ØšØ±Û - " . $chat_id,
- "metadata" => ["order_id" => "user_{$chat_id}_" . time()]
- ];
- $jsonData = json_encode($data);
-
- $ch = curl_init('https://api.zarinpal.com/pg/v4/payment/request.json');
- curl_setopt($ch, CURLOPT_USERAGENT, 'ZarinPal Rest Api v4');
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
- curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Content-Length: ' . strlen($jsonData)]);
-
- $result = curl_exec($ch);
- curl_close($ch);
- $result = json_decode($result, true);
-
- if (empty($result['errors'])) {
- $authority = $result['data']['authority'];
-
- // ثؚت ØªØ±Ø§Ú©ÙØŽ Ø¯Ø± Ø¯ÛØªØ§ØšÛس
- $stmt = pdo()->prepare("INSERT INTO transactions (user_id, amount, authority, description) VALUES (?, ?, ?, ?)");
- $stmt->execute([$chat_id, $amount, $authority, "ØŽØ§Ø±Ú ØØ³Ø§Øš"]);
-
- $payment_url = 'https://www.zarinpal.com/pg/StartPay/' . $authority;
-
- $message = "Ⳡدر ØØ§Ù Ø§ÙØªÙا٠ؚ٠درگا٠ٟرداخت... ÙØ·Ùا صؚر Ú©ÙÛØ¯.";
- $keyboard = ['inline_keyboard' => [[['text' => 'ð ÙØ±Ùد ØšÙ ØµÙØÙ ÙŸØ±Ø¯Ø§Ø®Øª', 'url' => $payment_url]]]];
- editMessageText($chat_id, $message_id, $message, $keyboard);
-
- } else {
- $error_code = $result['errors']['code'];
- editMessageText($chat_id, $message_id, "â Ø®Ø·Ø§ در اتصا٠ؚ٠درگا٠ٟرداخت. کد خطا: {$error_code}");
- }
- }
- elseif ($data === 'toggle_gateway_status') {
+ } elseif (strpos($data, 'charge_zarinpal_') === 0) {
+ $amount = (int) str_replace('charge_zarinpal_', '', $data);
+ $settings = getSettings();
+ $merchant_id = $settings['zarinpal_merchant_id'];
+
+ $script_url = 'https://' . $_SERVER['HTTP_HOST'] . rtrim(dirname($_SERVER['PHP_SELF']), '/') . '/verify_payment.php';
+
+ $data = [
+ "merchant_id" => $merchant_id,
+ "amount" => $amount * 10, // تؚدÛ٠تÙÙ
Ø§Ù ØšÙ Ø±ÛØ§Ù
+ "callback_url" => $script_url,
+ "description" => "ØŽØ§Ø±Ú ØØ³Ø§Øš Ú©Ø§Ø±ØšØ±Û - " . $chat_id,
+ "metadata" => ["order_id" => "user_{$chat_id}_" . time()]
+ ];
+ $jsonData = json_encode($data);
+
+ $ch = curl_init('https://api.zarinpal.com/pg/v4/payment/request.json');
+ curl_setopt($ch, CURLOPT_USERAGENT, 'ZarinPal Rest Api v4');
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Content-Length: ' . strlen($jsonData)]);
+
+ $result = curl_exec($ch);
+ curl_close($ch);
+ $result = json_decode($result, true);
+
+ if (empty($result['errors'])) {
+ $authority = $result['data']['authority'];
+
+ // ثؚت ØªØ±Ø§Ú©ÙØŽ Ø¯Ø± Ø¯ÛØªØ§ØšÛس
+ $stmt = pdo()->prepare("INSERT INTO transactions (user_id, amount, authority, description) VALUES (?, ?, ?, ?)");
+ $stmt->execute([$chat_id, $amount, $authority, "ØŽØ§Ø±Ú ØØ³Ø§Øš"]);
+
+ $payment_url = 'https://www.zarinpal.com/pg/StartPay/' . $authority;
+
+ $message = "Ⳡدر ØØ§Ù Ø§ÙØªÙا٠ؚ٠درگا٠ٟرداخت... ÙØ·Ùا صؚر Ú©ÙÛØ¯.";
+ $keyboard = ['inline_keyboard' => [[['text' => 'ð ÙØ±Ùد ØšÙ ØµÙØÙ ÙŸØ±Ø¯Ø§Ø®Øª', 'url' => $payment_url]]]];
+ editMessageText($chat_id, $message_id, $message, $keyboard);
+
+ } else {
+ $error_code = $result['errors']['code'];
+ editMessageText($chat_id, $message_id, "â Ø®Ø·Ø§ در اتصا٠ؚ٠درگا٠ٟرداخت. کد خطا: {$error_code}");
+ }
+ } elseif ($data === 'toggle_gateway_status') {
$settings = getSettings();
$settings['payment_gateway_status'] = ($settings['payment_gateway_status'] ?? 'off') == 'on' ? 'off' : 'on';
saveSettings($settings);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
ÙØ¶Ø¹Ûت تغÛÛØ± کرد.']);
-
- }
- elseif ($data === 'set_zarinpal_merchant_id') {
+
+ } elseif ($data === 'set_zarinpal_merchant_id') {
updateUserData($chat_id, 'admin_awaiting_merchant_id');
editMessageText($chat_id, $message_id, "ÙØ·Ùا Ù
رÚÙØª کد Û³Û¶ Ú©Ø§Ø±Ø§Ú©ØªØ±Û Ø²Ø±ÛÙâÙŸØ§Ù Ø®ÙØ¯ را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:");
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
- }
- elseif ($data === 'toggle_renewal_status') {
- $settings = getSettings();
- $settings['renewal_status'] = ($settings['renewal_status'] ?? 'off') == 'on' ? 'off' : 'on';
- saveSettings($settings);
- apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
ÙØ¶Ø¹Ûت تغÛÛØ± کرد.']);
- showRenewalManagementMenu($chat_id, $message_id);
-}
- elseif ($data === 'set_renewal_price_day') {
+ } elseif ($data === 'toggle_renewal_status') {
+ $settings = getSettings();
+ $settings['renewal_status'] = ($settings['renewal_status'] ?? 'off') == 'on' ? 'off' : 'on';
+ saveSettings($settings);
+ apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
ÙØ¶Ø¹Ûت تغÛÛØ± کرد.']);
+ showRenewalManagementMenu($chat_id, $message_id);
+ } elseif ($data === 'set_renewal_price_day') {
updateUserData($chat_id, 'admin_awaiting_renewal_price_day');
editMessageText($chat_id, $message_id, "ÙØ·Ùا ÙØ²ÛÙ٠تÙ
Ø¯ÛØ¯ ØšÙ Ø§Ø²Ø§Û ÙØ± **Ø±ÙØ²** را ؚ٠تÙÙ
Ø§Ù ÙØ§Ø±Ø¯ Ú©ÙÛØ¯ (ÙÙØ· عدد):");
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
- }
- elseif ($data === 'set_renewal_price_gb') {
+ } elseif ($data === 'set_renewal_price_gb') {
updateUserData($chat_id, 'admin_awaiting_renewal_price_gb');
editMessageText($chat_id, $message_id, "ÙØ·Ùا ÙØ²ÛÙ٠تÙ
Ø¯ÛØ¯ ØšÙ Ø§Ø²Ø§Û ÙØ± **Ú¯ÛÚ¯Ø§ØšØ§ÛØª** را ؚ٠تÙÙ
Ø§Ù ÙØ§Ø±Ø¯ Ú©ÙÛØ¯ (ÙÙØ· عدد):");
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
- }
- elseif (strpos($data, 'toggle_cat_') === 0 && hasPermission($chat_id, 'manage_categories')) {
+ } elseif (strpos($data, 'toggle_cat_') === 0 && hasPermission($chat_id, 'manage_categories')) {
$cat_id = str_replace('toggle_cat_', '', $data);
pdo()
->prepare("UPDATE categories SET status = IF(status = 'active', 'inactive', 'active') WHERE id = ?")
@@ -354,16 +337,14 @@
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
ÙØ¶Ø¹Ûت تغÛÛØ± کرد']);
deleteMessage($chat_id, $message_id);
generateCategoryList($chat_id);
- }
- elseif (strpos($data, 'delete_plan_') === 0 && hasPermission($chat_id, 'manage_plans')) {
+ } elseif (strpos($data, 'delete_plan_') === 0 && hasPermission($chat_id, 'manage_plans')) {
$plan_id = str_replace('delete_plan_', '', $data);
pdo()
->prepare("DELETE FROM plans WHERE id = ?")
->execute([$plan_id]);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
ÙŸÙÙ ØØ°Ù ؎د']);
deleteMessage($chat_id, $message_id);
- }
- elseif (strpos($data, 'toggle_plan_') === 0 && hasPermission($chat_id, 'manage_plans')) {
+ } elseif (strpos($data, 'toggle_plan_') === 0 && hasPermission($chat_id, 'manage_plans')) {
$plan_id = str_replace('toggle_plan_', '', $data);
pdo()
->prepare("UPDATE plans SET status = IF(status = 'active', 'inactive', 'active') WHERE id = ?")
@@ -371,23 +352,20 @@
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
ÙØ¶Ø¹Ûت تغÛÛØ± کرد']);
deleteMessage($chat_id, $message_id);
generatePlanList($chat_id);
- }
- elseif ($data === 'back_to_plan_list' && hasPermission($chat_id, 'manage_plans')) {
+ } elseif ($data === 'back_to_plan_list' && hasPermission($chat_id, 'manage_plans')) {
updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
deleteMessage($chat_id, $message_id);
generatePlanList($chat_id);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
- }
- elseif (strpos($data, 'open_plan_editor_') === 0 && hasPermission($chat_id, 'manage_plans')) {
+ } elseif (strpos($data, 'open_plan_editor_') === 0 && hasPermission($chat_id, 'manage_plans')) {
$plan_id = str_replace('open_plan_editor_', '', $data);
showPlanEditor($chat_id, $message_id, $plan_id);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
- }
- elseif (strpos($data, 'edit_plan_field_') === 0 && hasPermission($chat_id, 'manage_plans')) {
+ } elseif (strpos($data, 'edit_plan_field_') === 0 && hasPermission($chat_id, 'manage_plans')) {
preg_match('/edit_plan_field_(\d+)_(\w+)/', $data, $matches);
$plan_id = $matches[1];
$field = $matches[2];
-
+
$field_map = [
'name' => ['prompt' => 'ð ÙØ·Ùا ÙØ§Ù
Ø¬Ø¯ÛØ¯ ÙŸÙ٠را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:', 'column' => 'name', 'validation' => 'text'],
'price' => ['prompt' => 'ð ÙØ·Ùا ÙÛÙ
ت Ø¬Ø¯ÛØ¯ را ؚ٠تÙÙ
Ø§Ù ÙØ§Ø±Ø¯ Ú©ÙÛØ¯ (ÙÙØ· عدد):', 'column' => 'price', 'validation' => 'numeric'],
@@ -401,18 +379,16 @@
$state_data = [
'editing_plan_id' => $plan_id,
'editing_field_info' => $field_info,
- 'editor_message_id' => $message_id
+ 'editor_message_id' => $message_id
];
updateUserData($chat_id, 'admin_awaiting_plan_edit_input', $state_data);
showPlanEditor($chat_id, $message_id, $plan_id, $field_info['prompt']);
}
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
- }
- elseif (strpos($data, 'back_to_plan_view_') === 0 && hasPermission($chat_id, 'manage_plans')) {
+ } elseif (strpos($data, 'back_to_plan_view_') === 0 && hasPermission($chat_id, 'manage_plans')) {
deleteMessage($chat_id, $message_id);
generatePlanList($chat_id);
- }
- elseif (strpos($data, 'edit_plan_field_') === 0 && hasPermission($chat_id, 'manage_plans')) {
+ } elseif (strpos($data, 'edit_plan_field_') === 0 && hasPermission($chat_id, 'manage_plans')) {
preg_match('/edit_plan_field_(\d+)_(\w+)/', $data, $matches);
$plan_id = $matches[1];
$field = $matches[2];
@@ -470,11 +446,10 @@
if ($field !== 'category' && $field !== 'server') {
deleteMessage($chat_id, $message_id);
}
-
+
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
die;
- }
- elseif (strpos($data, 'set_plan_category_') === 0 && hasPermission($chat_id, 'manage_plans')) {
+ } elseif (strpos($data, 'set_plan_category_') === 0 && hasPermission($chat_id, 'manage_plans')) {
preg_match('/set_plan_category_(\d+)_(\d+)/', $data, $matches);
$plan_id = $matches[1];
$category_id = $matches[2];
@@ -484,8 +459,7 @@
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
دستÙâØšÙØ¯Û ÙŸÙ٠ؚا Ù
ÙÙÙÛØª تغÛÛØ± کرد.']);
deleteMessage($chat_id, $message_id);
generatePlanList($chat_id);
- }
- elseif (strpos($data, 'set_plan_server_') === 0 && hasPermission($chat_id, 'manage_plans')) {
+ } elseif (strpos($data, 'set_plan_server_') === 0 && hasPermission($chat_id, 'manage_plans')) {
preg_match('/set_plan_server_(\d+)_(\d+)/', $data, $matches);
$plan_id = $matches[1];
$server_id = $matches[2];
@@ -495,8 +469,7 @@
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
Ø³Ø±ÙØ± ÙŸÙ٠ؚا Ù
ÙÙÙÛØª تغÛÛØ± کرد.']);
deleteMessage($chat_id, $message_id);
generatePlanList($chat_id);
- }
- elseif (strpos($data, 'p_cat_') === 0 && hasPermission($chat_id, 'manage_plans')) {
+ } elseif (strpos($data, 'p_cat_') === 0 && hasPermission($chat_id, 'manage_plans')) {
$category_id = str_replace('p_cat_', '', $data);
$servers = pdo()
->query("SELECT id, name FROM servers WHERE status = 'active'")
@@ -511,12 +484,11 @@
$keyboard_buttons[] = [['text' => $server['name'], 'callback_data' => "p_server_{$server['id']}_cat_{$category_id}"]];
}
editMessageText($chat_id, $message_id, "اÛÙ ÙŸÙ٠رÙÛ Ú©Ø¯Ø§Ù
Ø³Ø±ÙØ± Ø³Ø§Ø®ØªÙ ØŽÙØ¯Ø", ['inline_keyboard' => $keyboard_buttons]);
- }
- elseif (strpos($data, 'p_server_') === 0 && hasPermission($chat_id, 'manage_plans')) {
+ } elseif (strpos($data, 'p_server_') === 0 && hasPermission($chat_id, 'manage_plans')) {
preg_match('/p_server_(\d+)_cat_(\d+)/', $data, $matches);
$server_id = $matches[1];
$category_id = $matches[2];
-
+
$stmt = pdo()->prepare("SELECT type FROM servers WHERE id = ?");
$stmt->execute([$server_id]);
$server_type = $stmt->fetchColumn();
@@ -535,7 +507,7 @@
editMessageText($chat_id, $message_id, "اÛÙ ÙŸÙ٠ؚ٠کداÙ
اÛÙØšØ§Ùد اضاÙÙ ØŽÙØ¯Ø", ['inline_keyboard' => $keyboard_buttons]);
} elseif ($server_type === 'marzneshin') {
$services = getMarzneshinServices($server_id);
- if (empty($services)) {
+ if (empty($services)) {
editMessageText($chat_id, $message_id, "â ÙÛÚ Ø³Ø±ÙÛØ³Û رÙÛ Ø§ÛÙ Ø³Ø±ÙØ± Ù
Ø±Ø²ÙØŽÛÙ ÛØ§Ùت ÙØŽØ¯. ÙØ·Ùا اؚتدا ÛÚ© سرÙÛØ³ در ÙŸÙÙ Ø®ÙØ¯ ØšØ³Ø§Ø²ÛØ¯.");
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
die;
@@ -554,8 +526,7 @@
sendMessage($chat_id, "1/6 - ÙØ·Ùا ÙØ§Ù
ÙŸÙ٠را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:", $cancelKeyboard);
deleteMessage($chat_id, $message_id);
}
- }
- elseif (strpos($data, 'p_inbound_') === 0 && hasPermission($chat_id, 'manage_plans')) {
+ } elseif (strpos($data, 'p_inbound_') === 0 && hasPermission($chat_id, 'manage_plans')) {
preg_match('/p_inbound_(\d+)_server_(\d+)_cat_(\d+)/', $data, $matches);
$inbound_id = $matches[1];
$server_id = $matches[2];
@@ -569,8 +540,7 @@
updateUserData($chat_id, 'awaiting_plan_name', $state_data);
sendMessage($chat_id, "1/6 - ÙØ·Ùا ÙØ§Ù
ÙŸÙ٠را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:", $cancelKeyboard);
deleteMessage($chat_id, $message_id);
- }
- elseif (strpos($data, 'p_service_') === 0 && hasPermission($chat_id, 'manage_plans')) {
+ } elseif (strpos($data, 'p_service_') === 0 && hasPermission($chat_id, 'manage_plans')) {
preg_match('/p_service_(\d+)_server_(\d+)_cat_(\d+)/', $data, $matches);
$service_id = $matches[1];
$server_id = $matches[2];
@@ -584,8 +554,7 @@
updateUserData($chat_id, 'awaiting_plan_name', $state_data);
sendMessage($chat_id, "1/6 - ÙØ·Ùا ÙØ§Ù
ÙŸÙ٠را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:", $cancelKeyboard);
deleteMessage($chat_id, $message_id);
- }
- elseif (strpos($data, 'copy_toggle_') === 0 && hasPermission($chat_id, 'manage_payment')) {
+ } elseif (strpos($data, 'copy_toggle_') === 0 && hasPermission($chat_id, 'manage_payment')) {
$toggle = str_replace('copy_toggle_', '', $data) === 'yes';
$settings = getSettings();
$settings['payment_method'] = ['card_number' => $user_data['state_data']['temp_card_number'], 'card_holder' => $user_data['state_data']['temp_card_holder'], 'copy_enabled' => $toggle];
@@ -594,8 +563,7 @@
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
ØªÙØžÛÙ
ات Ø°Ø®ÛØ±Ù ؎د']);
editMessageText($chat_id, $message_id, "â
ØªÙØžÛÙ
ات Ø±ÙØŽ ÙŸØ±Ø¯Ø§Ø®Øª ؚا Ù
ÙÙÙÛØª Ø°Ø®ÛØ±Ù ؎د.");
handleMainMenu($chat_id, $first_name);
- }
- elseif (strpos($data, 'approve_') === 0 || strpos($data, 'reject_') === 0) {
+ } elseif (strpos($data, 'approve_') === 0 || strpos($data, 'reject_') === 0) {
list($action, $request_id) = explode('_', $data);
$stmt = pdo()->prepare("SELECT * FROM payment_requests WHERE id = ?");
@@ -632,9 +600,9 @@
// اÛ٠ٟرداخت ØšØ±Ø§Û ØªÚ©Ù
ÛÙ Ø®Ø±ÛØ¯ ÛÚ© ÙŸÙÙ
$plan_id = $metadata['plan_id'];
$discount_code = $metadata['discount_code'] ?? null;
-
+
$plan = getPlanById($plan_id);
- $final_price = (float)$plan['price'];
+ $final_price = (float) $plan['price'];
$discount_applied = false;
$discount_object = null;
@@ -643,7 +611,7 @@
$stmt_discount->execute([$discount_code]);
$discount_object = $stmt_discount->fetch();
if ($discount_object) {
- if ($discount_object['type'] == 'percent') {
+ if ($discount_object['type'] == 'percent') {
$final_price = $plan['price'] - ($plan['price'] * $discount_object['value']) / 100;
} else {
$final_price = $plan['price'] - $discount_object['value'];
@@ -652,19 +620,19 @@
$discount_applied = true;
}
}
-
+
// ØŽØ§Ø±Ú Ù
ÙÙØª ØØ³Ø§Øš کارؚر ؚا Ù
ØšÙØº ٟرداختÛ
updateUserBalance($user_id_to_charge, $amount_to_charge, 'add');
- $custom_name = $metadata['custom_name'] ?? 'سرÙÛØ³';
-$purchase_result = completePurchase($user_id_to_charge, $plan_id, $custom_name, $final_price, $discount_code, $discount_object, $discount_applied);
+ $custom_name = $metadata['custom_name'] ?? 'سرÙÛØ³';
+ $purchase_result = completePurchase($user_id_to_charge, $plan_id, $custom_name, $final_price, $discount_code, $discount_object, $discount_applied);
if ($purchase_result['success']) {
sendPhoto($user_id_to_charge, $purchase_result['qr_code_url'], $purchase_result['caption']);
sendMessage(ADMIN_CHAT_ID, $purchase_result['admin_notification']);
sendMessage($user_id_to_charge, "â
ٟرداخت ØŽÙ
ا تاÛÛØ¯ ٠سرÙÛØ³ ؚا Ù
ÙÙÙÛØª Ø§ÛØ¬Ø§Ø¯ ؎د.");
} else {
- sendMessage($user_id_to_charge, "â ٟرداخت ØŽÙ
ا تاÛÛØ¯ ؎د اÙ
ا در Ø§ÛØ¬Ø§Ø¯ سرÙÛØ³ خطاÛÛ Ø±Ø® داد. Ù
ØšÙØº ٟرداخت ؎د٠ؚ٠Ù
ÙØ¬ÙØ¯Û ØŽÙ
ا اضاÙ٠؎د. ÙØ·Ùا٠ؚا ÙŸØŽØªÛØšØ§ÙÛ ØªÙ
اس ØšÚ¯ÛØ±Ûد.");
+ sendMessage($user_id_to_charge, "â ٟرداخت ØŽÙ
ا تاÛÛØ¯ ؎د اÙ
ا در Ø§ÛØ¬Ø§Ø¯ سرÙÛØ³ خطاÛÛ Ø±Ø® داد. Ù
ØšÙØº ٟرداخت ؎د٠ؚ٠Ù
ÙØ¬ÙØ¯Û ØŽÙ
ا اضاÙ٠؎د. ÙØ·Ùا٠ؚا ÙŸØŽØªÛØšØ§ÙÛ ØªÙ
اس ØšÚ¯ÛØ±Ûد.");
}
updateUserData($user_id_to_charge, 'main_menu');
@@ -678,8 +646,7 @@
editMessageCaption($chat_id, $message_id, $update['callback_query']['message']['caption'] . "\n\nâ
ØªÙØ³Ø· ØŽÙ
ا تاÛÛØ¯ ؎د. ", null);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
Ø¯Ø±Ø®ÙØ§Ø³Øª تاÛÛØ¯ ؎د']);
- }
- elseif ($action == 'reject') {
+ } elseif ($action == 'reject') {
pdo()->prepare("UPDATE payment_requests SET status = 'rejected', processed_by_admin_id = ?, processed_at = NOW() WHERE id = ?")->execute([$admin_who_processed, $request_id]);
sendMessage($user_id_to_charge, "â Ø¯Ø±Ø®Ùاست ØŽØ§Ø±Ú ØØ³Ø§Øš ØŽÙ
ا ØšÙ Ù
ØšÙØº " . number_format($amount_to_charge) . " تÙÙ
Ø§Ù ØªÙØ³Ø· ادÙ
Û٠رد ؎د.");
@@ -687,8 +654,7 @@
editMessageCaption($chat_id, $message_id, $update['callback_query']['message']['caption'] . "\n\nâ ØªÙسط ØŽÙ
ا رد ؎د. ", null);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â Ø¯Ø±Ø®Ùاست رد ؎د']);
}
- }
- elseif ($data === 'manage_servers' && hasPermission($chat_id, 'manage_marzban')) {
+ } elseif ($data === 'manage_servers' && hasPermission($chat_id, 'manage_marzban')) {
$servers = pdo()
->query("SELECT id, name FROM servers")
->fetchAll(PDO::FETCH_ASSOC);
@@ -699,117 +665,112 @@
$keyboard_buttons[] = [['text' => 'âïž ØšØ§Ø²Ú¯ØŽØª ØšÙ ÙŸÙÙ', 'callback_data' => 'back_to_admin_panel']];
editMessageText($chat_id, $message_id, "ð Ù
Ø¯ÛØ±Ûت Ø³Ø±ÙØ±Ùا \n\nØ³Ø±ÙØ± Ù
ÙØ±Ø¯ ÙØžØ± را ØšØ±Ø§Û Ù
ØŽØ§ÙØ¯Ù ÛØ§ ØØ°Ù Ø§ÙØªØ®Ø§Øš Ú©ÙÛØ¯Ø ÛØ§ ÛÚ© Ø³Ø±ÙØ± Ø¬Ø¯ÛØ¯ اضاÙÙ Ú©ÙÛØ¯:", ['inline_keyboard' => $keyboard_buttons]);
- }
- elseif ($data === 'add_server_select_type' && hasPermission($chat_id, 'manage_marzban')) {
- $keyboard = ['inline_keyboard' => [
- [['text' => 'ðµ Ù
رزؚا٠(Marzban)', 'callback_data' => 'add_server_type_marzban']],
- [['text' => 'ð Ø³ÙØ§ÛÛ (3x-ui)', 'callback_data' => 'add_server_type_sanaei']],
- [['text' => 'ð¢ Ù
Ø±Ø²ÙØŽÛÙ (Marzneshin)', 'callback_data' => 'add_server_type_marzneshin']],
- [['text' => 'âïž ØšØ§Ø²Ú¯ØŽØª', 'callback_data' => 'manage_servers']],
- ]];
+ } elseif ($data === 'add_server_select_type' && hasPermission($chat_id, 'manage_marzban')) {
+ $keyboard = [
+ 'inline_keyboard' => [
+ [['text' => 'ðµ Ù
رزؚا٠(Marzban)', 'callback_data' => 'add_server_type_marzban']],
+ [['text' => 'ð Ø³ÙØ§ÛÛ (3x-ui)', 'callback_data' => 'add_server_type_sanaei']],
+ [['text' => 'ð¢ Ù
Ø±Ø²ÙØŽÛÙ (Marzneshin)', 'callback_data' => 'add_server_type_marzneshin']],
+ [['text' => 'âïž ØšØ§Ø²Ú¯ØŽØª', 'callback_data' => 'manage_servers']],
+ ]
+ ];
editMessageText($chat_id, $message_id, "ÙØ·Ùا ÙÙØ¹ ÙŸÙÙ Ø³Ø±ÙØ± را Ø§ÙØªØ®Ø§Øš Ú©ÙÛØ¯:", $keyboard);
- }
- elseif (strpos($data, 'edit_protocols_') === 0 && hasPermission($chat_id, 'manage_marzban')) {
+ } elseif (strpos($data, 'edit_protocols_') === 0 && hasPermission($chat_id, 'manage_marzban')) {
$server_id = str_replace('edit_protocols_', '', $data);
showMarzbanProtocolEditor($chat_id, $message_id, $server_id);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
- }
- elseif (strpos($data, 'toggle_protocol_') === 0 && hasPermission($chat_id, 'manage_marzban')) {
+ } elseif (strpos($data, 'toggle_protocol_') === 0 && hasPermission($chat_id, 'manage_marzban')) {
preg_match('/toggle_protocol_(\d+)_(\w+)/', $data, $matches);
$server_id = $matches[1];
$protocol = $matches[2];
-
+
$stmt_get = pdo()->prepare("SELECT marzban_protocols FROM servers WHERE id = ?");
$stmt_get->execute([$server_id]);
$protocols_json = $stmt_get->fetchColumn();
-
+
$current_protocols = $protocols_json ? json_decode($protocols_json, true) : [];
- if (!is_array($current_protocols)) $current_protocols = [];
+ if (!is_array($current_protocols))
+ $current_protocols = [];
if (in_array($protocol, $current_protocols)) {
$current_protocols = array_diff($current_protocols, [$protocol]);
} else {
$current_protocols[] = $protocol;
}
-
+
$new_protocols_json = json_encode(array_values($current_protocols));
$stmt_update = pdo()->prepare("UPDATE servers SET marzban_protocols = ? WHERE id = ?");
$stmt_update->execute([$new_protocols_json, $server_id]);
-
+
showMarzbanProtocolEditor($chat_id, $message_id, $server_id);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
- }
- elseif (strpos($data, 'add_server_type_') === 0 && hasPermission($chat_id, 'manage_marzban')) {
+ } elseif (strpos($data, 'add_server_type_') === 0 && hasPermission($chat_id, 'manage_marzban')) {
deleteMessage($chat_id, $message_id);
$type = str_replace('add_server_type_', '', $data);
updateUserData($chat_id, 'admin_awaiting_server_name', ['selected_server_type' => $type]);
sendMessage($chat_id, "Ù
رØÙÙ Û±/ÛŽ: ÛÚ© ÙØ§Ù
Ø¯ÙØ®ÙØ§Ù ØšØ±Ø§Û ØŽÙØ§Ø³Ø§ÛÛ Ø³Ø±ÙØ± ÙØ§Ø±Ø¯ Ú©ÙÛØ¯ (Ù
ثاÙ: Ø¢ÙÙ
اÙ-ÙØªØ²Ùر):", $cancelKeyboard);
- }
- elseif (strpos($data, 'view_server_') === 0 && hasPermission($chat_id, 'manage_marzban')) {
+ } elseif (strpos($data, 'view_server_') === 0 && hasPermission($chat_id, 'manage_marzban')) {
$server_id = str_replace('view_server_', '', $data);
$stmt = pdo()->prepare("SELECT * FROM servers WHERE id = ?");
$stmt->execute([$server_id]);
$server = $stmt->fetch();
if ($server) {
$panel_type_text = ucfirst($server['type']);
- if ($server['type'] === 'sanaei') $panel_type_text = 'Ø³ÙØ§ÛÛ (3x-ui)';
- if ($server['type'] === 'marzneshin') $panel_type_text = 'Ù
Ø±Ø²ÙØŽÛÙ';
-
+ if ($server['type'] === 'sanaei')
+ $panel_type_text = 'Ø³ÙØ§ÛÛ (3x-ui)';
+ if ($server['type'] === 'marzneshin')
+ $panel_type_text = 'Ù
Ø±Ø²ÙØŽÛÙ';
+
$msg = "Ù
؎خصات Ø³Ø±ÙØ±: {$server['name']} \n\n";
$msg .= "â«ïž ÙÙØ¹ ÙŸÙÙ: {$panel_type_text} \n";
$msg .= "â«ïž آدرس Ù
Ø¯ÛØ±Ûت ÙŸÙÙ: {$server['url']}\n";
-
+
$keyboard_buttons = [];
-
-
+
+
if ($server['type'] === 'sanaei' || $server['type'] === 'marzban') {
$sub_host_text = !empty($server['sub_host']) ? "{$server['sub_host']}" : "ÙŸÛØŽâÙØ±Ø¶ (Ù
اÙÙØ¯ آدرس ÙŸÙÙ) ";
$msg .= "â«ïž آدرس ÙÛÙÚ© ا؎تراک: {$sub_host_text}\n";
$keyboard_buttons[] = [['text' => 'ð ÙÛØ±Ø§ÛØŽ آدرس ساؚ', 'callback_data' => "edit_sub_host_{$server_id}"]];
}
-
+
if ($server['type'] === 'marzban') {
$keyboard_buttons[] = [['text' => 'âïž ØªÙØžÛÙ
ÙŸØ±ÙØªÚ©ÙâÙØ§', 'callback_data' => "edit_protocols_{$server_id}"]];
}
-
+
$msg .= "â«ïž ÙØ§Ù
کارؚرÛ: {$server['username']}\n";
$keyboard_buttons[] = [['text' => 'ð ØØ°Ù اÛÙ Ø³Ø±ÙØ±', 'callback_data' => "delete_server_{$server_id}"]];
$keyboard_buttons[] = [['text' => 'âïž ØšØ§Ø²Ú¯ØŽØª ØšÙ ÙÛØ³Øª Ø³Ø±ÙØ±Ùا', 'callback_data' => 'manage_servers']];
-
+
$keyboard = ['inline_keyboard' => $keyboard_buttons];
editMessageText($chat_id, $message_id, $msg, $keyboard);
}
- }
- elseif (strpos($data, 'edit_sub_host_') === 0 && hasPermission($chat_id, 'manage_marzban')) {
+ } elseif (strpos($data, 'edit_sub_host_') === 0 && hasPermission($chat_id, 'manage_marzban')) {
$server_id = str_replace('edit_sub_host_', '', $data);
updateUserData($chat_id, 'admin_awaiting_sub_host', ['editing_server_id' => $server_id]);
$prompt = "ÙØ·Ùا آدرس کاÙ
٠٠عÙ
ÙÙ
Û Ú©Ù ØšØ±Ø§Û ÙÛÙÚ© ا؎تراک Ø§Ø³ØªÙØ§Ø¯Ù Ù
ÛâØŽÙØ¯ را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.\nاÛ٠آدرس ØšØ§ÛØ¯ ؎اÙ
Ù http/https Ù ÙŸÙØ±Øª صØÛØ ØšØ§ØŽØ¯ (Ù
ثاÙ: http://your.domain.com:2096).\n\nð¡ ØšØ±Ø§Û ؚازگ؎ت ØšÙ ØØ§Ùت ÙŸÛØŽâÙØ±Ø¶ (Ø§Ø³ØªÙØ§Ø¯Ù از ÙÙ
ا٠آدرس ÙŸÙÙ)Ø Ú©ÙÙ
Ù `reset` را ارسا٠کÙÛØ¯.";
editMessageText($chat_id, $message_id, $prompt);
- }
- elseif (strpos($data, 'delete_server_') === 0 && hasPermission($chat_id, 'manage_marzban')) {
+ } elseif (strpos($data, 'delete_server_') === 0 && hasPermission($chat_id, 'manage_marzban')) {
$server_id = str_replace('delete_server_', '', $data);
$stmt_check = pdo()->prepare("SELECT COUNT(*) FROM plans WHERE server_id = ?");
$stmt_check->execute([$server_id]);
if ($stmt_check->fetchColumn() > 0) {
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â ÙÙ
ÛâØªÙØ§ÙÛØ¯ اÛÙ Ø³Ø±ÙØ± را ØØ°Ù Ú©ÙÛØ¯ Ø²ÛØ±Ø§ ÛÚ© ÛØ§ ÚÙØ¯ ÙŸÙ٠ؚ٠آ٠Ù
ØªØµÙ ÙØ³ØªÙد.', 'show_alert' => true]);
- }
- else {
+ } else {
$stmt = pdo()->prepare("DELETE FROM servers WHERE id = ?");
$stmt->execute([$server_id]);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
Ø³Ø±ÙØ± ؚا Ù
ÙÙÙÛØª ØØ°Ù ؎د.']);
- $data = 'manage_servers';
+ $data = 'manage_servers';
}
- }
- elseif (strpos($data, 'plan_set_sub_') === 0) {
+ } elseif (strpos($data, 'plan_set_sub_') === 0) {
$show_sub = str_replace('plan_set_sub_', '', $data) === 'yes';
$state_data = $user_data['state_data'];
$state_data['temp_plan_data']['show_sub_link'] = $show_sub;
updateUserData($chat_id, 'awaiting_plan_conf_link_setting', $state_data);
$keyboard = ['inline_keyboard' => [[['text' => 'â
ØšÙÙ', 'callback_data' => 'plan_set_conf_yes'], ['text' => 'â Ø®Ûر', 'callback_data' => 'plan_set_conf_no']]]];
editMessageText($chat_id, $message_id, "Ø³ÙØ§Ù Û²/Û²: Ø¢ÛØ§ ÙÛÙÚ©âÙØ§Û ØªÚ©Û Ú©Ø§ÙÙÛÚ¯âÙØ§ ؚ٠کارؚر ÙÙ
Ø§ÛØŽ Ø¯Ø§Ø¯Ù ØŽÙØ¯Ø\n(ÙŸÛØŽÙÙØ§Ø¯Û: ØšÙÙ)", $keyboard);
- }
- elseif (strpos($data, 'plan_set_conf_') === 0) {
+ } elseif (strpos($data, 'plan_set_conf_') === 0) {
$show_conf = str_replace('plan_set_conf_', '', $data) === 'yes';
$final_plan_data = $user_data['state_data']['temp_plan_data'] ?? null;
if ($final_plan_data) {
@@ -834,14 +795,12 @@
editMessageText($chat_id, $message_id, "â
ÙŸÙÙ Ø¬Ø¯ÛØ¯ ؚا تÙ
اÙ
ØªÙØžÛÙ
ات ؚا Ù
ÙÙÙÛØª Ø°Ø®ÛØ±Ù ؎د.");
updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
handleMainMenu($chat_id, $first_name);
- }
- else {
+ } else {
editMessageText($chat_id, $message_id, "â Ø®Ø·Ø§ در Ø°Ø®ÛØ±ÙâØ³Ø§Ø²Û ÙŸÙÙ. ÙØ·Ùا Ù
جددا ØªÙØ§ØŽ Ú©ÙÛØ¯.");
updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
handleMainMenu($chat_id, $first_name);
}
- }
- elseif (strpos($data, 'discount_type_') === 0) {
+ } elseif (strpos($data, 'discount_type_') === 0) {
$type = str_replace('discount_type_', '', $data);
$state_data = $user_data['state_data'];
$state_data['new_discount_type'] = $type;
@@ -849,16 +808,14 @@
$unit = $type == 'percent' ? 'درصد' : 'تÙÙ
اÙ';
editMessageText($chat_id, $message_id, "3/4 - ÙØ·Ùا٠Ù
ÙØ¯Ø§Ø± تخÙÛ٠را ØšÙ $unit ÙØ§Ø±Ø¯ Ú©ÙÛØ¯ (ÙÙØ· عدد):");
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
- }
- elseif (strpos($data, 'delete_discount_') === 0) {
+ } elseif (strpos($data, 'delete_discount_') === 0) {
$code_id = str_replace('delete_discount_', '', $data);
pdo()
->prepare("DELETE FROM discount_codes WHERE id = ?")
->execute([$code_id]);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
کد تخÙÛÙ ØØ°Ù ؎د.']);
deleteMessage($chat_id, $message_id);
- }
- elseif (strpos($data, 'toggle_discount_') === 0) {
+ } elseif (strpos($data, 'toggle_discount_') === 0) {
$code_id = str_replace('toggle_discount_', '', $data);
pdo()
->prepare("UPDATE discount_codes SET status = IF(status = 'active', 'inactive', 'active') WHERE id = ?")
@@ -866,8 +823,7 @@
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
ÙØ¶Ø¹Ûت کد تخÙÛ٠تغÛÛØ± کرد.']);
deleteMessage($chat_id, $message_id);
generateDiscountCodeList($chat_id);
- }
- elseif (strpos($data, 'delete_guide_') === 0 && hasPermission($chat_id, 'manage_guides')) {
+ } elseif (strpos($data, 'delete_guide_') === 0 && hasPermission($chat_id, 'manage_guides')) {
$guide_id = str_replace('delete_guide_', '', $data);
pdo()
->prepare("DELETE FROM guides WHERE id = ?")
@@ -875,8 +831,7 @@
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
راÙÙÙ
ا ØØ°Ù ؎د.']);
deleteMessage($chat_id, $message_id);
generateGuideList($chat_id);
- }
- elseif (strpos($data, 'toggle_guide_') === 0 && hasPermission($chat_id, 'manage_guides')) {
+ } elseif (strpos($data, 'toggle_guide_') === 0 && hasPermission($chat_id, 'manage_guides')) {
$guide_id = str_replace('toggle_guide_', '', $data);
pdo()
->prepare("UPDATE guides SET status = IF(status = 'active', 'inactive', 'active') WHERE id = ?")
@@ -884,8 +839,7 @@
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
ÙØ¶Ø¹Ûت راÙÙÙ
ا تغÛÛØ± کرد.']);
deleteMessage($chat_id, $message_id);
generateGuideList($chat_id);
- }
- elseif (strpos($data, 'reset_plan_count_') === 0 && hasPermission($chat_id, 'manage_plans')) {
+ } elseif (strpos($data, 'reset_plan_count_') === 0 && hasPermission($chat_id, 'manage_plans')) {
$plan_id = str_replace('reset_plan_count_', '', $data);
pdo()
->prepare("UPDATE plans SET purchase_count = 0 WHERE id = ?")
@@ -904,8 +858,7 @@
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
اÛÙ ÙŸÙ٠ؚ٠عÙÙØ§Ù ÙŸÙ٠تست ØªÙØžÛÙ
؎د.']);
deleteMessage($chat_id, $message_id);
generatePlanList($chat_id);
- }
- elseif (strpos($data, 'make_plan_normal_') === 0 && hasPermission($chat_id, 'manage_plans')) {
+ } elseif (strpos($data, 'make_plan_normal_') === 0 && hasPermission($chat_id, 'manage_plans')) {
$plan_id = str_replace('make_plan_normal_', '', $data);
pdo()
->prepare("UPDATE plans SET is_test_plan = 0 WHERE id = ?")
@@ -917,8 +870,7 @@
if (strpos($data, 'admin_notifications_soon') === 0) {
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'اÛ٠ؚخ؎ ØšÙ Ø²ÙØ¯Û ÙØ¹Ø§Ù Ø®ÙØ§Ùد ؎د.', 'show_alert' => true]);
- }
- elseif (($data == 'user_notifications_menu' || $data == 'config_expire_warning' || $data == 'config_inactive_reminder') && hasPermission($chat_id, 'manage_notifications')) {
+ } elseif (($data == 'user_notifications_menu' || $data == 'config_expire_warning' || $data == 'config_inactive_reminder') && hasPermission($chat_id, 'manage_notifications')) {
$settings = getSettings();
$expire_status_icon = ($settings['notification_expire_status'] ?? 'off') == 'on' ? 'â
' : 'â';
$inactive_status_icon = ($settings['notification_inactive_status'] ?? 'off') == 'on' ? 'â
' : 'â';
@@ -941,8 +893,7 @@
],
];
editMessageText($chat_id, $message_id, $message, $keyboard);
- }
- elseif ($data == 'config_expire_warning') {
+ } elseif ($data == 'config_expire_warning') {
$message =
"âïž ØªÙØžÛÙ
ات ÙØŽØ¯Ø§Ø± اÙÙØ¶Ø§ \n\nاÛÙ ÙŸÛØ§Ù
زÙ
اÙÛ ØšØ±Ø§Û Ú©Ø§Ø±ØšØ± ارسا٠Ù
ÛâØŽÙØ¯ Ú©Ù ØØ¬Ù
ÛØ§ زÙ
ا٠سرÙÛØ³ ا٠ر٠ؚ٠اتÙ
اÙ
ؚا؎د.\n\n" .
"â«ïžÙØ¶Ø¹ÛØª: " .
@@ -959,8 +910,7 @@
],
];
editMessageText($chat_id, $message_id, $message, $keyboard);
- }
- elseif ($data == 'config_inactive_reminder') {
+ } elseif ($data == 'config_inactive_reminder') {
$message =
"âïž ØªÙØžÛÙ
ات ÛØ§Ø¯Ø¢Ùر عدÙ
ÙØ¹Ø§ÙÛØª \n\nاÛÙ ÙŸÛØ§Ù
زÙ
اÙÛ ØšØ±Ø§Û Ú©Ø§Ø±ØšØ± ارسا٠Ù
ÛâØŽÙØ¯ Ú©Ù ØšØ±Ø§Û Ù
دت Ø·ÙÙØ§ÙÛ Ø§Ø² رؚات Ø§Ø³ØªÙØ§Ø¯Ù Ùکرد٠ؚا؎د.\n\n" .
"â«ïžÙØ¶Ø¹ÛØª: " .
@@ -977,22 +927,19 @@
];
editMessageText($chat_id, $message_id, $message, $keyboard);
}
- }
- elseif (strpos($data, 'toggle_expire_notification') === 0 && hasPermission($chat_id, 'manage_notifications')) {
+ } elseif (strpos($data, 'toggle_expire_notification') === 0 && hasPermission($chat_id, 'manage_notifications')) {
$settings = getSettings();
$settings['notification_expire_status'] = ($settings['notification_expire_status'] ?? 'off') == 'on' ? 'off' : 'on';
saveSettings($settings);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
ÙØ¶Ø¹Ûت تغÛÛØ± کرد.']);
$data = 'config_expire_warning';
- }
- elseif (strpos($data, 'toggle_inactive_notification') === 0 && hasPermission($chat_id, 'manage_notifications')) {
+ } elseif (strpos($data, 'toggle_inactive_notification') === 0 && hasPermission($chat_id, 'manage_notifications')) {
$settings = getSettings();
$settings['notification_inactive_status'] = ($settings['notification_inactive_status'] ?? 'off') == 'on' ? 'off' : 'on';
saveSettings($settings);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
ÙØ¶Ø¹Ûت تغÛÛØ± کرد.']);
$data = 'config_inactive_reminder';
- }
- elseif (in_array($data, ['set_expire_days', 'set_expire_gb', 'edit_expire_message', 'set_inactive_days', 'edit_inactive_message']) && hasPermission($chat_id, 'manage_notifications')) {
+ } elseif (in_array($data, ['set_expire_days', 'set_expire_gb', 'edit_expire_message', 'set_inactive_days', 'edit_inactive_message']) && hasPermission($chat_id, 'manage_notifications')) {
deleteMessage($chat_id, $message_id);
switch ($data) {
case 'set_expire_days':
@@ -1031,8 +978,7 @@
}
$keyboard_buttons[] = [['text' => 'âïž ØšØ§Ø²Ú¯ØŽØª ØšÙ ÙŸÙÙ', 'callback_data' => 'back_to_admin_panel']];
editMessageText($chat_id, $message_id, "ð Ù
Ø¯ÛØ±Ûت Ø³Ø±ÙØ±Ùا \n\nØ³Ø±ÙØ± Ù
ÙØ±Ø¯ ÙØžØ± را ØšØ±Ø§Û Ù
ØŽØ§ÙØ¯Ù ÛØ§ ØØ°Ù Ø§ÙØªØ®Ø§Øš Ú©ÙÛØ¯Ø ÛØ§ ÛÚ© Ø³Ø±ÙØ± Ø¬Ø¯ÛØ¯ اضاÙÙ Ú©ÙÛØ¯:", ['inline_keyboard' => $keyboard_buttons]);
- }
- else {
+ } else {
$menu_to_refresh = strpos($data, 'inactive') !== false || strpos($user_state, 'inactive') !== false ? 'config_inactive_reminder' : 'config_expire_warning';
$message_id = sendMessage($chat_id, "Ø¯Ø±ØØ§Ù ØšØ§Ø±Ú¯Ø°Ø§Ø±Û Ù
جدد Ù
ÙÙ...")['result']['message_id'];
$data = $menu_to_refresh;
@@ -1062,19 +1008,16 @@
$admins = getAdmins();
if (count($admins) >= 9) {
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â ØØ¯Ø§Ú©Ø«Ø± تعداد ادÙ
ÛÙâÙØ§ (Û±Û°) ثؚت ؎د٠است.', 'show_alert' => true]);
- }
- else {
+ } else {
updateUserData($chat_id, 'admin_awaiting_new_admin_id');
editMessageText($chat_id, $message_id, "ÙØ·Ùا ØŽÙØ§Ø³Ù Ø¹Ø¯Ø¯Û (Chat ID) کارؚر Ù
ÙØ±Ø¯ ÙØžØ± را ØšØ±Ø§Û Ø§ÙØ²Ùد٠ؚ٠ÙÛØ³Øª ادÙ
ÛÙâÙØ§ ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:");
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
}
- }
- elseif (strpos($data, 'edit_admin_permissions_') === 0) {
+ } elseif (strpos($data, 'edit_admin_permissions_') === 0) {
$target_admin_id = str_replace('edit_admin_permissions_', '', $data);
showPermissionEditor($chat_id, $message_id, $target_admin_id);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
- }
- elseif (strpos($data, 'toggle_perm_') === 0) {
+ } elseif (strpos($data, 'toggle_perm_') === 0) {
$payload = substr($data, strlen('toggle_perm_'));
$parts = explode('_', $payload, 2);
if (count($parts) === 2) {
@@ -1085,8 +1028,7 @@
$current_permissions = $admins[$target_admin_id]['permissions'] ?? [];
if (($key = array_search($permission_key, $current_permissions)) !== false) {
unset($current_permissions[$key]);
- }
- else {
+ } else {
$current_permissions[] = $permission_key;
}
updateAdminPermissions($target_admin_id, array_values($current_permissions));
@@ -1094,14 +1036,12 @@
}
}
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
- }
- elseif (strpos($data, 'delete_admin_confirm_') === 0) {
+ } elseif (strpos($data, 'delete_admin_confirm_') === 0) {
$target_admin_id = str_replace('delete_admin_confirm_', '', $data);
$keyboard = ['inline_keyboard' => [[['text' => 'â
ØšÙÙØ ØØ°Ù Ú©Ù', 'callback_data' => "delete_admin_do_{$target_admin_id}"]], [['text' => 'â Ø§ÙصراÙ', 'callback_data' => "edit_admin_permissions_{$target_admin_id}"]]]];
editMessageText($chat_id, $message_id, "â ïž Ø¢ÛØ§ از ØØ°Ù اÛ٠ادÙ
ÛÙ Ù
Ø·Ù
ØŠÙ ÙØ³ØªÛدØ", $keyboard);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
- }
- elseif (strpos($data, 'delete_admin_do_') === 0) {
+ } elseif (strpos($data, 'delete_admin_do_') === 0) {
$target_admin_id = str_replace('delete_admin_do_', '', $data);
$result = removeAdmin($target_admin_id);
if ($result) {
@@ -1118,12 +1058,10 @@
}
$keyboard_buttons[] = [['text' => 'âïž ØšØ§Ø²Ú¯ØŽØª ØšÙ ÙŸÙÙ Ù
Ø¯ÛØ±Ûت', 'callback_data' => 'back_to_admin_panel']];
editMessageText($chat_id, $message_id, $message, ['inline_keyboard' => $keyboard_buttons]);
- }
- else {
+ } else {
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â Ø®Ø·Ø§ در ØØ°Ù ادÙ
ÛÙ.', 'show_alert' => true]);
}
- }
- elseif ($data == 'back_to_admin_list') {
+ } elseif ($data == 'back_to_admin_list') {
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
$admins = getAdmins();
$message = "ðšâðŒ Ù
Ø¯ÛØ±Ûت ادÙ
ÛÙâÙØ§ \n\nدر اÛ٠ؚخ؎ Ù
ÛâØªÙØ§ÙÛØ¯ ادÙ
ÛÙâÙØ§Û رؚات ٠دسترسÛâÙØ§Û Ø¢ÙâÙØ§ را Ù
Ø¯ÛØ±Ûت Ú©ÙÛØ¯. (ØØ¯Ø§Ú©Ø«Ø± Û±Û° ادÙ
ÛÙ)";
@@ -1137,8 +1075,7 @@
}
$keyboard_buttons[] = [['text' => 'âïž ØšØ§Ø²Ú¯ØŽØª ØšÙ ÙŸÙÙ Ù
Ø¯ÛØ±Ûت', 'callback_data' => 'back_to_admin_panel']];
editMessageText($chat_id, $message_id, $message, ['inline_keyboard' => $keyboard_buttons]);
- }
- elseif ($data == 'back_to_admin_panel') {
+ } elseif ($data == 'back_to_admin_panel') {
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
deleteMessage($chat_id, $message_id);
handleMainMenu($chat_id, $first_name);
@@ -1158,51 +1095,47 @@
$ticket_status = $stmt->fetchColumn();
if (!$ticket_status || $ticket_status == 'closed') {
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'اÛ٠تÛکت ؚست٠؎د٠است.', 'show_alert' => true]);
- }
- else {
+ } else {
if ($isAnAdmin) {
updateUserData($chat_id, 'admin_replying_to_ticket', ['replying_to_ticket' => $ticket_id]);
sendMessage($chat_id, "ÙØ·Ùا ٟاسخ Ø®ÙØ¯ را ØšØ±Ø§Û ØªÛکت $ticket_id ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:", $cancelKeyboard);
- }
- else {
+ } else {
updateUserData($chat_id, 'user_replying_to_ticket', ['replying_to_ticket' => $ticket_id]);
sendMessage($chat_id, "ÙØ·Ùا ٟاسخ Ø®ÙØ¯ را ØšØ±Ø§Û ØªÛکت $ticket_id ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:", $cancelKeyboard);
}
}
- }
- elseif (strpos($data, 'approve_renewal_') === 0 || strpos($data, 'reject_renewal_') === 0) {
- list($action, $type, $request_id) = explode('_', $data);
+ } elseif (strpos($data, 'approve_renewal_') === 0 || strpos($data, 'reject_renewal_') === 0) {
+ list($action, $type, $request_id) = explode('_', $data);
- $stmt = pdo()->prepare("SELECT * FROM renewal_requests WHERE id = ?");
- $stmt->execute([$request_id]);
- $request = $stmt->fetch();
+ $stmt = pdo()->prepare("SELECT * FROM renewal_requests WHERE id = ?");
+ $stmt->execute([$request_id]);
+ $request = $stmt->fetch();
- if (!$request || $request['status'] !== 'pending') {
- apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'اÛÙ Ø¯Ø±Ø®ÙØ§Ø³Øª ÙØšÙا ٟرداز؎ ؎د٠است.', 'show_alert' => true]);
- die;
- }
-
- $admin_who_processed = $update['callback_query']['from']['id'];
+ if (!$request || $request['status'] !== 'pending') {
+ apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'اÛÙ Ø¯Ø±Ø®ÙØ§Ø³Øª ÙØšÙا ٟرداز؎ ؎د٠است.', 'show_alert' => true]);
+ die;
+ }
- if ($action == 'approve') {
- $result = applyRenewal($request['user_id'], $request['service_username'], $request['days_to_add'], $request['gb_to_add']);
- if ($result['success']) {
- pdo()->prepare("UPDATE renewal_requests SET status = 'approved', processed_by_admin_id = ?, processed_at = NOW() WHERE id = ?")->execute([$admin_who_processed, $request_id]);
- sendMessage($request['user_id'], "â
Ø¯Ø±Ø®ÙØ§Ø³Øª تÙ
Ø¯ÛØ¯ ØŽÙ
ا ØšØ±Ø§Û Ø³Ø±ÙÛØ³ `{$request['service_username']}` تاÛÛØ¯ ٠ؚا Ù
ÙÙÙÛØª اعÙ
ا٠؎د.");
- editMessageCaption($chat_id, $message_id, $update['callback_query']['message']['caption'] . "\n\nâ
ØªÙØ³Ø· ØŽÙ
ا تاÛÛØ¯ ؎د. ", null);
- apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
تÙ
Ø¯ÛØ¯ تاÛÛØ¯ ؎د.']);
- } else {
- sendMessage($chat_id, "â Ø®Ø·Ø§ در اعÙ
ا٠تÙ
Ø¯ÛØ¯: " . $result['message']);
- apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'خطا در اعÙ
ا٠تÙ
Ø¯ÛØ¯.', 'show_alert' => true]);
- }
- } elseif ($action == 'reject') {
- pdo()->prepare("UPDATE renewal_requests SET status = 'rejected', processed_by_admin_id = ?, processed_at = NOW() WHERE id = ?")->execute([$admin_who_processed, $request_id]);
- sendMessage($request['user_id'], "â Ø¯Ø±Ø®Ùاست تÙ
Ø¯ÛØ¯ ØŽÙ
ا ØšØ±Ø§Û Ø³Ø±ÙÛØ³ `{$request['service_username']}` ØªÙØ³Ø· ادÙ
Û٠رد ؎د.");
- editMessageCaption($chat_id, $message_id, $update['callback_query']['message']['caption'] . "\n\nâ ØªÙسط ØŽÙ
ا رد ؎د. ", null);
- apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â Ø¯Ø±Ø®Ùاست رد ؎د.']);
+ $admin_who_processed = $update['callback_query']['from']['id'];
+
+ if ($action == 'approve') {
+ $result = applyRenewal($request['user_id'], $request['service_username'], $request['days_to_add'], $request['gb_to_add']);
+ if ($result['success']) {
+ pdo()->prepare("UPDATE renewal_requests SET status = 'approved', processed_by_admin_id = ?, processed_at = NOW() WHERE id = ?")->execute([$admin_who_processed, $request_id]);
+ sendMessage($request['user_id'], "â
Ø¯Ø±Ø®ÙØ§Ø³Øª تÙ
Ø¯ÛØ¯ ØŽÙ
ا ØšØ±Ø§Û Ø³Ø±ÙÛØ³ `{$request['service_username']}` تاÛÛØ¯ ٠ؚا Ù
ÙÙÙÛØª اعÙ
ا٠؎د.");
+ editMessageCaption($chat_id, $message_id, $update['callback_query']['message']['caption'] . "\n\nâ
ØªÙØ³Ø· ØŽÙ
ا تاÛÛØ¯ ؎د. ", null);
+ apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â
تÙ
Ø¯ÛØ¯ تاÛÛØ¯ ؎د.']);
+ } else {
+ sendMessage($chat_id, "â Ø®Ø·Ø§ در اعÙ
ا٠تÙ
Ø¯ÛØ¯: " . $result['message']);
+ apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'خطا در اعÙ
ا٠تÙ
Ø¯ÛØ¯.', 'show_alert' => true]);
}
+ } elseif ($action == 'reject') {
+ pdo()->prepare("UPDATE renewal_requests SET status = 'rejected', processed_by_admin_id = ?, processed_at = NOW() WHERE id = ?")->execute([$admin_who_processed, $request_id]);
+ sendMessage($request['user_id'], "â Ø¯Ø±Ø®Ùاست تÙ
Ø¯ÛØ¯ ØŽÙ
ا ØšØ±Ø§Û Ø³Ø±ÙÛØ³ `{$request['service_username']}` ØªÙØ³Ø· ادÙ
Û٠رد ؎د.");
+ editMessageCaption($chat_id, $message_id, $update['callback_query']['message']['caption'] . "\n\nâ ØªÙسط ØŽÙ
ا رد ؎د. ", null);
+ apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â Ø¯Ø±Ø®Ùاست رد ؎د.']);
}
- elseif (strpos($data, 'close_ticket_') === 0) {
+ } elseif (strpos($data, 'close_ticket_') === 0) {
if ($isAnAdmin && !hasPermission($chat_id, 'view_tickets')) {
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'ØŽÙ
ا Ø¯Ø³ØªØ±Ø³Û ÙØ§Ø²Ù
ØšØ±Ø§Û ØšØ³ØªÙ ØªÛکتâÙØ§ را ÙØ¯Ø§Ø±Ûد.', 'show_alert' => true]);
die;
@@ -1225,8 +1158,7 @@
}
editMessageText($chat_id, $message_id, $update['callback_query']['message']['text'] . "\n\n-- â Ø§Û٠تÛکت ؚست٠؎د â -- ", null);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'تÛکت ؚا Ù
ÙÙÙÛØª ؚست٠؎د.']);
- }
- else {
+ } else {
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'خطا: تÛکت ÛØ§Ùت ÙØŽØ¯.', 'show_alert' => true]);
}
}
@@ -1234,7 +1166,7 @@
// --- دکÙ
ÙâÙØ§Û عÙ
ÙÙ
Û Ú©Ø§Ø±ØšØ±Ø§Ù ---
elseif (strpos($data, 'get_configs_') === 0) {
$username = str_replace('get_configs_', '', $data);
-
+
$stmt_service = pdo()->prepare("SELECT server_id FROM services WHERE owner_chat_id = ? AND marzban_username = ?");
$stmt_service->execute([$chat_id, $username]);
$server_id = $stmt_service->fetchColumn();
@@ -1245,7 +1177,7 @@
}
$panel_user = getPanelUser($username, $server_id);
-
+
if ($panel_user && !empty($panel_user['links'])) {
// --- ارسا٠Ù
ستÙÛÙ
ÙÙ
٠کاÙÙÛÚ¯âÙØ§ ---
$all_links_text = implode("\n\n", $panel_user['links']);
@@ -1256,8 +1188,7 @@
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â ÙÛÚ ÙÛÙÚ© کاÙÙÛÚ¯Û ØšØ±Ø§Û Ø§Û٠سرÙÛØ³ ÛØ§Ùت ÙØŽØ¯.', 'show_alert' => true]);
}
die;
- }
- elseif (strpos($data, 'show_guide_') === 0) {
+ } elseif (strpos($data, 'show_guide_') === 0) {
$guide_id = str_replace('show_guide_', '', $data);
$stmt = pdo()->prepare("SELECT * FROM guides WHERE id = ? AND status = 'active'");
$stmt->execute([$guide_id]);
@@ -1270,17 +1201,14 @@
}
if ($guide['content_type'] === 'photo' && !empty($guide['photo_id'])) {
sendPhoto($chat_id, $guide['photo_id'], $guide['message_text'], $keyboard);
- }
- else {
+ } else {
sendMessage($chat_id, $guide['message_text'], $keyboard);
}
- }
- else {
+ } else {
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â Ø§Û٠راÙÙÙ
ا ÛØ§Ùت ÙØŽØ¯ ÛØ§ ØºÛØ±Ùعا٠؎د٠است.', 'show_alert' => true]);
}
- }
- elseif (strpos($data, 'charge_manual_') === 0) {
- $amount = (int)str_replace('charge_manual_', '', $data);
+ } elseif (strpos($data, 'charge_manual_') === 0) {
+ $amount = (int) str_replace('charge_manual_', '', $data);
$settings = getSettings();
$payment_method = $settings['payment_method'] ?? [];
$card_number = $payment_method['card_number'] ?? '';
@@ -1288,32 +1216,29 @@
$copy_enabled = $payment_method['copy_enabled'] ?? false;
if (empty($card_number)) {
- editMessageText($chat_id, $message_id, "â Ø±ÙØŽ ٟرداخت Ø¯Ø³ØªÛ ØªÙØ³Ø· ادÙ
ÛÙ ØªÙØžÛÙ
ÙØŽØ¯Ù است.");
- apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
- die;
+ editMessageText($chat_id, $message_id, "â Ø±ÙØŽ ٟرداخت Ø¯Ø³ØªÛ ØªÙØ³Ø· ادÙ
ÛÙ ØªÙØžÛÙ
ÙØŽØ¯Ù است.");
+ apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
+ die;
}
$card_number_display = $copy_enabled ? "{$card_number}" : $card_number;
$message = "ØšØ±Ø§Û ØŽØ§Ø±Ú ØØ³Ø§Øš ØšÙ Ù
ØšÙØº " . number_format($amount) . " تÙÙ
Ø§Ù Ø ÙØ·Ùا Ù
ØšÙØº را ØšÙ Ø§Ø·ÙØ§Ø¹Ø§Øª Ø²ÛØ± ÙØ§Ø±Ûز ÙÙ
اÛÛØ¯:\n\n" .
- "ð³ ØŽÙ
ار٠کارت:\n" . $card_number_display . "\n" .
- "ð€ ØµØ§ØØš ØØ³Ø§Øš: {$card_holder}\n\n" .
- "ٟس از ÙØ§Ø±ÛØ²Ø ÙØ·Ùا از Ø±Ø³ÛØ¯ ٟرداخت Ø®ÙØ¯ اسکرÛÙâØŽØ§Øª Ú¯Ø±ÙØªÙ ٠در ÙÙ
ÛÙØ¬Ø§ ارسا٠کÙÛØ¯.";
+ "ð³ ØŽÙ
ار٠کارت:\n" . $card_number_display . "\n" .
+ "ð€ ØµØ§ØØš ØØ³Ø§Øš: {$card_holder}\n\n" .
+ "ٟس از ÙØ§Ø±ÛØ²Ø ÙØ·Ùا از Ø±Ø³ÛØ¯ ٟرداخت Ø®ÙØ¯ اسکرÛÙâØŽØ§Øª Ú¯Ø±ÙØªÙ ٠در ÙÙ
ÛÙØ¬Ø§ ارسا٠کÙÛØ¯.";
editMessageText($chat_id, $message_id, $message);
updateUserData($chat_id, 'awaiting_payment_screenshot', ['charge_amount' => $amount]);
- }
- elseif (strpos($data, 'cat_') === 0) {
+ } elseif (strpos($data, 'cat_') === 0) {
$categoryId = str_replace('cat_', '', $data);
showServersForCategory($chat_id, $categoryId);
deleteMessage($chat_id, $message_id);
- }
- elseif (strpos($data, 'show_plans_cat_') === 0) {
+ } elseif (strpos($data, 'show_plans_cat_') === 0) {
preg_match('/show_plans_cat_(\d+)_srv_(\d+)/', $data, $matches);
$category_id = $matches[1];
$server_id = $matches[2];
showPlansForCategoryAndServer($chat_id, $category_id, $server_id);
deleteMessage($chat_id, $message_id);
- }
- elseif (strpos($data, 'apply_discount_code_') === 0) {
+ } elseif (strpos($data, 'apply_discount_code_') === 0) {
$parts = explode('_', $data);
$category_id = $parts[3];
$server_id = $parts[4]; // server_id اضاÙ٠؎د
@@ -1323,46 +1248,44 @@
]);
editMessageText($chat_id, $message_id, "ð ÙØ·Ùا٠کد تخÙÛÙ Ø®ÙØ¯ را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:");
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
- }
- elseif (strpos($data, 'buy_plan_') === 0) {
- $parts = explode('_', $data);
- $plan_id = $parts[2];
- $discount_code = null;
- if (isset($parts[5]) && $parts[3] == 'with' && $parts[4] == 'code') {
- $discount_code = strtoupper($parts[5]);
- }
-
- $plan = getPlanById($plan_id);
- if (!$plan) {
- apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â Ø®Ø·Ø§: ÙŸÙÙ ÛØ§Ùت ÙØŽØ¯.']);
- die;
- }
+ } elseif (strpos($data, 'buy_plan_') === 0) {
+ $parts = explode('_', $data);
+ $plan_id = $parts[2];
+ $discount_code = null;
+ if (isset($parts[5]) && $parts[3] == 'with' && $parts[4] == 'code') {
+ $discount_code = strtoupper($parts[5]);
+ }
- if ($plan['purchase_limit'] > 0 && $plan['purchase_count'] >= $plan['purchase_limit']) {
- apiRequest('answerCallbackQuery', [
- 'callback_query_id' => $callback_id,
- 'text' => 'â Ù
ØªØ§Ø³ÙØ§Ù٠؞رÙÛØª Ø®Ø±ÛØ¯ اÛÙ ÙŸÙ٠ؚ٠اتÙ
اÙ
Ø±Ø³ÛØ¯Ù است.',
- 'show_alert' => true,
- ]);
- die;
- }
+ $plan = getPlanById($plan_id);
+ if (!$plan) {
+ apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â Ø®Ø·Ø§: ÙŸÙÙ ÛØ§Ùت ÙØŽØ¯.']);
+ die;
+ }
- // ØšÙ Ø¬Ø§Û Ø®Ø±ÛØ¯ Ù
ستÙÛÙ
Ø ÙØ¶Ø¹Ûت را ØšØ±Ø§Û Ø¯Ø±ÛØ§Ùت ÙØ§Ù
ØªÙØžÛÙ
Ù
ÛâÚ©ÙÛÙ
- $state_data = [
- 'purchasing_plan_id' => $plan_id,
- 'discount_code' => $discount_code
- ];
- updateUserData($chat_id, 'awaiting_service_name', $state_data);
-
- $message = "â
ÙŸÙÙ Ø§ÙØªØ®Ø§Øš ؎د.\n\nÙØ·Ùا٠ÛÚ© ÙØ§Ù
Ø¯ÙØ®ÙØ§Ù ØšØ±Ø§Û Ø§Û٠سرÙÛØ³ ÙØ§Ø±Ø¯ Ú©ÙÛØ¯ (Ù
Ø«ÙØ§Ù: سرÙÛØ³ ؎خصÛ). اÛÙ ÙØ§Ù
در ÙÛØ³Øª سرÙÛØ³âÙØ§Û ØŽÙ
ا ÙÙ
Ø§ÛØŽ Ø¯Ø§Ø¯Ù Ø®ÙØ§Ùد ؎د.";
-
-
- deleteMessage($chat_id, $message_id);
- sendMessage($chat_id, $message, $cancelKeyboard);
- apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
- die;
-}
- elseif ($data === 'confirm_renewal_payment') {
+ if ($plan['purchase_limit'] > 0 && $plan['purchase_count'] >= $plan['purchase_limit']) {
+ apiRequest('answerCallbackQuery', [
+ 'callback_query_id' => $callback_id,
+ 'text' => 'â Ù
ØªØ§Ø³ÙØ§Ù٠؞رÙÛØª Ø®Ø±ÛØ¯ اÛÙ ÙŸÙ٠ؚ٠اتÙ
اÙ
Ø±Ø³ÛØ¯Ù است.',
+ 'show_alert' => true,
+ ]);
+ die;
+ }
+
+ // ØšÙ Ø¬Ø§Û Ø®Ø±ÛØ¯ Ù
ستÙÛÙ
Ø ÙØ¶Ø¹Ûت را ØšØ±Ø§Û Ø¯Ø±ÛØ§Ùت ÙØ§Ù
ØªÙØžÛÙ
Ù
ÛâÚ©ÙÛÙ
+ $state_data = [
+ 'purchasing_plan_id' => $plan_id,
+ 'discount_code' => $discount_code
+ ];
+ updateUserData($chat_id, 'awaiting_service_name', $state_data);
+
+ $message = "â
ÙŸÙÙ Ø§ÙØªØ®Ø§Øš ؎د.\n\nÙØ·Ùا٠ÛÚ© ÙØ§Ù
Ø¯ÙØ®ÙØ§Ù ØšØ±Ø§Û Ø§Û٠سرÙÛØ³ ÙØ§Ø±Ø¯ Ú©ÙÛØ¯ (Ù
Ø«ÙØ§Ù: سرÙÛØ³ ؎خصÛ). اÛÙ ÙØ§Ù
در ÙÛØ³Øª سرÙÛØ³âÙØ§Û ØŽÙ
ا ÙÙ
Ø§ÛØŽ Ø¯Ø§Ø¯Ù Ø®ÙØ§Ùد ؎د.";
+
+
+ deleteMessage($chat_id, $message_id);
+ sendMessage($chat_id, $message, $cancelKeyboard);
+ apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
+ die;
+ } elseif ($data === 'confirm_renewal_payment') {
$state_data = $user_data['state_data'];
$total_cost = $state_data['renewal_total_cost'];
@@ -1370,52 +1293,51 @@
// ٟرداخت از Ù
ÙØ¬ÙدÛ
editMessageText($chat_id, $message_id, "Ⳡدر ØØ§Ù تÙ
Ø¯ÛØ¯ سرÙÛØ³ ؚا Ø§Ø³ØªÙØ§Ø¯Ù از Ù
ÙØ¬ÙØ¯Û ØŽÙ
ا...");
updateUserBalance($chat_id, $total_cost, 'deduct');
-
+
$result = applyRenewal($chat_id, $state_data['renewal_username'], $state_data['renewal_days'], $state_data['renewal_gb']);
-
+
if ($result['success']) {
$new_balance = number_format($user_data['balance'] - $total_cost);
$success_msg = "â
سرÙÛØ³ ØŽÙ
ا ؚا Ù
ÙÙÙÛØª تÙ
Ø¯ÛØ¯ ؎د.\n\n" .
- "ð° Ù
ØšÙØº " . number_format($total_cost) . " تÙÙ
ا٠از ØØ³Ø§Øš ØŽÙ
ا کسر Ú¯Ø±Ø¯ÛØ¯.\n" .
- "Ù
ÙØ¬ÙØ¯Û Ø¬Ø¯ÛØ¯: {$new_balance} تÙÙ
اÙ.";
+ "ð° Ù
ØšÙØº " . number_format($total_cost) . " تÙÙ
ا٠از ØØ³Ø§Øš ØŽÙ
ا کسر Ú¯Ø±Ø¯ÛØ¯.\n" .
+ "Ù
ÙØ¬ÙØ¯Û Ø¬Ø¯ÛØ¯: {$new_balance} تÙÙ
اÙ.";
editMessageText($chat_id, $message_id, $success_msg);
} else {
editMessageText($chat_id, $message_id, "â Ø®Ø·Ø§ÛÛ Ø¯Ø± تÙ
Ø¯ÛØ¯ سرÙÛØ³ رخ داد: " . $result['message']);
-
+
updateUserBalance($chat_id, $total_cost, 'add');
}
updateUserData($chat_id, 'main_menu');
} else {
-
+
$stmt = pdo()->prepare(
"INSERT INTO renewal_requests (user_id, service_username, days_to_add, gb_to_add, total_cost) VALUES (?, ?, ?, ?, ?)"
);
$stmt->execute([$chat_id, $state_data['renewal_username'], $state_data['renewal_days'], $state_data['renewal_gb'], $total_cost]);
$request_id = pdo()->lastInsertId();
-
+
$state_data['renewal_request_id'] = $request_id;
updateUserData($chat_id, 'awaiting_renewal_screenshot', $state_data);
-
+
$settings = getSettings();
$payment_method = $settings['payment_method'] ?? [];
if (empty($payment_method['card_number'])) {
editMessageText($chat_id, $message_id, "Ù
ÙØ¬ÙØ¯Û ØŽÙ
ا کاÙÛ ÙÛØ³Øª Ù Ø±ÙØŽ ÙŸØ±Ø¯Ø§Ø®Øª کارت ؚ٠کارت ÙÛØ² ØªÙØ³Ø· ادÙ
ÛÙ ØªÙØžÛÙ
ÙØŽØ¯Ù است. ÙØ·Ùا اؚتدا ØØ³Ø§Øš Ø®ÙØ¯ را ØŽØ§Ø±Ú Ú©ÙÛØ¯.");
} else {
- $card_number = $payment_method['card_number'] ?? '';
- $card_holder = $payment_method['card_holder'] ?? '';
- $copy_enabled = $payment_method['copy_enabled'] ?? false;
- $card_number_display = $copy_enabled ? "{$card_number}" : $card_number;
- $message = "Ù
ÙØ¬ÙØ¯Û ØŽÙ
ا کاÙÛ ÙÛØ³Øª. ÙØ·Ùا Ù
ØšÙØº " . number_format($total_cost) . " تÙÙ
ا٠را ØšÙ Ø§Ø·ÙØ§Ø¹Ø§Øª Ø²ÛØ± ÙØ§Ø±Ûز کرد٠٠سٟس اسکرÛÙâØŽØ§Øª Ø±Ø³ÛØ¯ را ارسا٠کÙÛØ¯:\n\n" .
- "ð³ ØŽÙ
ار٠کارت:\n" . $card_number_display . "\n" .
- "ð€ ØµØ§ØØš ØØ³Ø§Øš: {$card_holder}";
- editMessageText($chat_id, $message_id, $message);
+ $card_number = $payment_method['card_number'] ?? '';
+ $card_holder = $payment_method['card_holder'] ?? '';
+ $copy_enabled = $payment_method['copy_enabled'] ?? false;
+ $card_number_display = $copy_enabled ? "{$card_number}" : $card_number;
+ $message = "Ù
ÙØ¬ÙØ¯Û ØŽÙ
ا کاÙÛ ÙÛØ³Øª. ÙØ·Ùا Ù
ØšÙØº " . number_format($total_cost) . " تÙÙ
ا٠را ØšÙ Ø§Ø·ÙØ§Ø¹Ø§Øª Ø²ÛØ± ÙØ§Ø±Ûز کرد٠٠سٟس اسکرÛÙâØŽØ§Øª Ø±Ø³ÛØ¯ را ارسا٠کÙÛØ¯:\n\n" .
+ "ð³ ØŽÙ
ار٠کارت:\n" . $card_number_display . "\n" .
+ "ð€ ØµØ§ØØš ØØ³Ø§Øš: {$card_holder}";
+ editMessageText($chat_id, $message_id, $message);
}
}
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
- }
- elseif ($data == 'back_to_categories') {
+ } elseif ($data == 'back_to_categories') {
deleteMessage($chat_id, $message_id);
$categories = getCategories(true);
$keyboard_buttons = [];
@@ -1423,96 +1345,94 @@
$keyboard_buttons[] = [['text' => 'ð ' . $category['name'], 'callback_data' => 'cat_' . $category['id']]];
}
sendMessage($chat_id, "ÙØ·Ùا ÛÚ©Û Ø§Ø² دستÙâØšÙØ¯ÛâÙØ§Û Ø²ÛØ± را Ø§ÙØªØ®Ø§Øš Ú©ÙÛØ¯:", ['inline_keyboard' => $keyboard_buttons]);
- }
- elseif (strpos($data, 'service_details_') === 0) {
- $username = str_replace('service_details_', '', $data);
- if (isset($update['callback_query']['message']['photo'])) {
- editMessageCaption($chat_id, $message_id, "Ⳡدر ØØ§Ù Ø¯Ø±ÛØ§Ùت Ø§Ø·ÙØ§Ø¹Ø§Øª ØšÙâØ±ÙØ² سرÙÛØ³Ø ÙØ·Ùا صؚر Ú©ÙÛØ¯...");
- } else {
- editMessageText($chat_id, $message_id, "Ⳡدر ØØ§Ù Ø¯Ø±ÛØ§Ùت Ø§Ø·ÙØ§Ø¹Ø§Øª ØšÙâØ±ÙØ² سرÙÛØ³Ø ÙØ·Ùا صؚر Ú©ÙÛØ¯...");
- }
+ } elseif (strpos($data, 'service_details_') === 0) {
+ $username = str_replace('service_details_', '', $data);
+ if (isset($update['callback_query']['message']['photo'])) {
+ editMessageCaption($chat_id, $message_id, "Ⳡدر ØØ§Ù Ø¯Ø±ÛØ§Ùت Ø§Ø·ÙØ§Ø¹Ø§Øª ØšÙâØ±ÙØ² سرÙÛØ³Ø ÙØ·Ùا صؚر Ú©ÙÛØ¯...");
+ } else {
+ editMessageText($chat_id, $message_id, "Ⳡدر ØØ§Ù Ø¯Ø±ÛØ§Ùت Ø§Ø·ÙØ§Ø¹Ø§Øª ØšÙâØ±ÙØ² سرÙÛØ³Ø ÙØ·Ùا صؚر Ú©ÙÛØ¯...");
+ }
- $stmt_local = pdo()->prepare("SELECT s.*, p.name as plan_name, p.show_sub_link, p.show_conf_links FROM services s JOIN plans p ON s.plan_id = p.id WHERE s.owner_chat_id = ? AND s.marzban_username = ?");
- $stmt_local->execute([$chat_id, $username]);
- $local_service = $stmt_local->fetch();
+ $stmt_local = pdo()->prepare("SELECT s.*, p.name as plan_name, p.show_sub_link, p.show_conf_links FROM services s JOIN plans p ON s.plan_id = p.id WHERE s.owner_chat_id = ? AND s.marzban_username = ?");
+ $stmt_local->execute([$chat_id, $username]);
+ $local_service = $stmt_local->fetch();
- if ($local_service) {
- $stmt_server = pdo()->prepare("SELECT * FROM servers WHERE id = ?");
- $stmt_server->execute([$local_service['server_id']]);
- $server_info = $stmt_server->fetch();
+ if ($local_service) {
+ $stmt_server = pdo()->prepare("SELECT * FROM servers WHERE id = ?");
+ $stmt_server->execute([$local_service['server_id']]);
+ $server_info = $stmt_server->fetch();
- $dynamic_sub_url = $local_service['sub_url'];
- if ($server_info) {
- $base_sub_url = !empty($server_info['sub_host']) ? rtrim($server_info['sub_host'], '/') : rtrim($server_info['url'], '/');
- $sub_path = strstr($local_service['sub_url'], '/sub/');
- if ($sub_path === false) {
- $sub_path = parse_url($local_service['sub_url'], PHP_URL_PATH);
- }
- $dynamic_sub_url = $base_sub_url . $sub_path;
- }
+ $dynamic_sub_url = $local_service['sub_url'];
+ if ($server_info) {
+ $base_sub_url = !empty($server_info['sub_host']) ? rtrim($server_info['sub_host'], '/') : rtrim($server_info['url'], '/');
+ $sub_path = strstr($local_service['sub_url'], '/sub/');
+ if ($sub_path === false) {
+ $sub_path = parse_url($local_service['sub_url'], PHP_URL_PATH);
+ }
+ $dynamic_sub_url = $base_sub_url . $sub_path;
+ }
- $panel_user = getPanelUser($username, $local_service['server_id']);
-
- if ($panel_user && !isset($panel_user['detail'])) {
- $qr_code_url = generateQrCodeUrl($dynamic_sub_url);
-
- $total_gb_from_db = $local_service['volume_gb'];
- $used_bytes_from_panel = $panel_user['used_traffic'];
-
- $total_text = ($total_gb_from_db > 0) ? "{$total_gb_from_db} Ú¯ÛÚ¯Ø§ØšØ§ÛØª" : 'ÙØ§Ù
ØØ¯Ùد';
- $used_text = formatBytes($used_bytes_from_panel);
-
- $remaining_text = 'ÙØ§Ù
ØØ¯Ùد';
- if ($total_gb_from_db > 0) {
- $total_bytes_from_db = $total_gb_from_db * 1024 * 1024 * 1024;
- $remaining_bytes = $total_bytes_from_db - $used_bytes_from_panel;
- $remaining_text = formatBytes(max(0, $remaining_bytes));
- }
+ $panel_user = getPanelUser($username, $local_service['server_id']);
- $expire_date = $panel_user['expire'] ? date('Y-m-d', $panel_user['expire']) : 'ÙØ§Ù
ØØ¯Ùد';
- $status_text = ($panel_user['status'] === 'active' && ($panel_user['expire'] == 0 || $panel_user['expire'] > time())) ? 'ÙØ¹Ø§Ù' : 'ØºÛØ±ÙعاÙ';
-
- $caption =
- "Ù
؎خصات سرÙÛØ³: {$local_service['plan_name']} \n" .
- "ââââââââââ\n" .
- "â«ïž ÙØ¶Ø¹Ûت: {$status_text} \n" .
- "ð ØªØ§Ø±ÛØ® اÙÙØ¶Ø§: {$expire_date} \n\n" .
- "ð ØØ¬Ù
Ú©Ù: " . $total_text . "\n" .
- "ð ØØ¬Ù
Ù
صرÙÛ: " . $used_text . "\n" .
- "ð ØØ¬Ù
ؚاÙÛâÙ
Ø§ÙØ¯Ù: " . $remaining_text . "\n" .
- "ââââââââââ\n";
-
- if ($local_service['show_sub_link']) {
- $caption .= "\nð ÙÛÙÚ© ا؎تراک (Subscription):\n" . htmlspecialchars($dynamic_sub_url) . "\n";
- } else {
- $caption .= "\nð ÙÛÙÚ© ا؎تراک ØšØ±Ø§Û Ø§ÛÙ ÙŸÙÙ ÙÙ
Ø§ÛØŽ Ø¯Ø§Ø¯Ù ÙÙ
ÛâØŽÙØ¯.\n";
- }
+ if ($panel_user && !isset($panel_user['detail'])) {
+ $qr_code_url = generateQrCodeUrl($dynamic_sub_url);
-
- $keyboard_buttons = [
- [['text' => 'â»ïž تÙ
Ø¯ÛØ¯ سرÙÛØ³', 'callback_data' => "renew_service_{$username}"]],
- ];
+ $total_gb_from_db = $local_service['volume_gb'];
+ $used_bytes_from_panel = $panel_user['used_traffic'];
- if ($local_service['show_conf_links'] && !empty($panel_user['links'])) {
- $keyboard_buttons[0][] = ['text' => 'ð Ø¯Ø±ÛØ§Ùت کاÙÙÛÚ¯âÙØ§', 'callback_data' => "get_configs_{$username}"];
- }
-
- $keyboard_buttons[] = [['text' => 'ð ØØ°Ù سرÙÛØ³', 'callback_data' => "delete_service_confirm_{$username}"]];
- $keyboard_buttons[] = [['text' => 'âïž ØšØ§Ø²Ú¯ØŽØª ØšÙ ÙÛØ³Øª', 'callback_data' => 'back_to_services']];
-
+ $total_text = ($total_gb_from_db > 0) ? "{$total_gb_from_db} Ú¯ÛÚ¯Ø§ØšØ§ÛØª" : 'ÙØ§Ù
ØØ¯Ùد';
+ $used_text = formatBytes($used_bytes_from_panel);
- $keyboard = ['inline_keyboard' => $keyboard_buttons];
+ $remaining_text = 'ÙØ§Ù
ØØ¯Ùد';
+ if ($total_gb_from_db > 0) {
+ $total_bytes_from_db = $total_gb_from_db * 1024 * 1024 * 1024;
+ $remaining_bytes = $total_bytes_from_db - $used_bytes_from_panel;
+ $remaining_text = formatBytes(max(0, $remaining_bytes));
+ }
- deleteMessage($chat_id, $message_id);
- sendPhoto($chat_id, $qr_code_url, trim($caption), $keyboard);
- } else {
- editMessageText($chat_id, $message_id, "â Ø®Ø·Ø§ÛÛ Ø¯Ø± Ø¯Ø±ÛØ§Ùت Ø§Ø·ÙØ§Ø¹Ø§Øª سرÙÛØ³ از Ø³Ø±ÙØ± رخ داد ÛØ§ سرÙÛØ³ ÛØ§Ùت ÙØŽØ¯. Ù
Ù
ک٠است ØªÙØ³Ø· ادÙ
ÛÙ ØØ°Ù ؎د٠ؚا؎د.");
- }
+ $expire_date = $panel_user['expire'] ? date('Y-m-d', $panel_user['expire']) : 'ÙØ§Ù
ØØ¯Ùد';
+ $status_text = ($panel_user['status'] === 'active' && ($panel_user['expire'] == 0 || $panel_user['expire'] > time())) ? 'ÙØ¹Ø§Ù' : 'ØºÛØ±ÙعاÙ';
+
+ $caption =
+ "Ù
؎خصات سرÙÛØ³: {$local_service['plan_name']} \n" .
+ "ââââââââââ\n" .
+ "â«ïž ÙØ¶Ø¹Ûت: {$status_text} \n" .
+ "ð ØªØ§Ø±ÛØ® اÙÙØ¶Ø§: {$expire_date} \n\n" .
+ "ð ØØ¬Ù
Ú©Ù: " . $total_text . "\n" .
+ "ð ØØ¬Ù
Ù
صرÙÛ: " . $used_text . "\n" .
+ "ð ØØ¬Ù
ؚاÙÛâÙ
Ø§ÙØ¯Ù: " . $remaining_text . "\n" .
+ "ââââââââââ\n";
+
+ if ($local_service['show_sub_link']) {
+ $caption .= "\nð ÙÛÙÚ© ا؎تراک (Subscription):\n" . htmlspecialchars($dynamic_sub_url) . "\n";
} else {
- editMessageText($chat_id, $message_id, "â Ø³Ø±ÙÛØ³ در Ø¯ÛØªØ§ØšÛس رؚات ÛØ§Ùت ÙØŽØ¯.");
+ $caption .= "\nð ÙÛÙÚ© ا؎تراک ØšØ±Ø§Û Ø§ÛÙ ÙŸÙÙ ÙÙ
Ø§ÛØŽ Ø¯Ø§Ø¯Ù ÙÙ
ÛâØŽÙØ¯.\n";
}
+
+
+ $keyboard_buttons = [
+ [['text' => 'â»ïž تÙ
Ø¯ÛØ¯ سرÙÛØ³', 'callback_data' => "renew_service_{$username}"]],
+ ];
+
+ if ($local_service['show_conf_links'] && !empty($panel_user['links'])) {
+ $keyboard_buttons[0][] = ['text' => 'ð Ø¯Ø±ÛØ§Ùت کاÙÙÛÚ¯âÙØ§', 'callback_data' => "get_configs_{$username}"];
+ }
+
+ $keyboard_buttons[] = [['text' => 'ð ØØ°Ù سرÙÛØ³', 'callback_data' => "delete_service_confirm_{$username}"]];
+ $keyboard_buttons[] = [['text' => 'âïž ØšØ§Ø²Ú¯ØŽØª ØšÙ ÙÛØ³Øª', 'callback_data' => 'back_to_services']];
+
+
+ $keyboard = ['inline_keyboard' => $keyboard_buttons];
+
+ deleteMessage($chat_id, $message_id);
+ sendPhoto($chat_id, $qr_code_url, trim($caption), $keyboard);
+ } else {
+ editMessageText($chat_id, $message_id, "â Ø®Ø·Ø§ÛÛ Ø¯Ø± Ø¯Ø±ÛØ§Ùت Ø§Ø·ÙØ§Ø¹Ø§Øª سرÙÛØ³ از Ø³Ø±ÙØ± رخ داد ÛØ§ سرÙÛØ³ ÛØ§Ùت ÙØŽØ¯. Ù
Ù
ک٠است ØªÙØ³Ø· ادÙ
ÛÙ ØØ°Ù ؎د٠ؚا؎د.");
}
- elseif (strpos($data, 'renew_service_') === 0) {
+ } else {
+ editMessageText($chat_id, $message_id, "â Ø³Ø±ÙÛØ³ در Ø¯ÛØªØ§ØšÛس رؚات ÛØ§Ùت ÙØŽØ¯.");
+ }
+ } elseif (strpos($data, 'renew_service_') === 0) {
$settings = getSettings();
if (($settings['renewal_status'] ?? 'off') !== 'on') {
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id, 'text' => 'â ÙØ§ØšÙÛØª تÙ
Ø¯ÛØ¯ سرÙÛØ³ در ØØ§Ù ØØ§Ø¶Ø± ØºÛØ±Ùعا٠است.', 'show_alert' => true]);
@@ -1521,22 +1441,20 @@
$username = str_replace('renew_service_', '', $data);
updateUserData($chat_id, 'user_awaiting_renewal_days', ['renewal_username' => $username]);
-
+
$price_day = number_format($settings['renewal_price_per_day'] ?? 1000);
$message = "تÙ
Ø¯ÛØ¯ سرÙÛØ³ \n\n" .
- "Û±. ÚÙØ¯ **Ø±ÙØ²** ؚ٠اعتؚار سرÙÛØ³ ØŽÙ
ا اضاÙÙ ØŽÙØ¯Ø\n\n" .
- "â«ïž ÙØ²ÛÙÙ ÙØ± Ø±ÙØ²: {$price_day} تÙÙ
اÙ\n" .
- "ð¡ ØšØ±Ø§Û رد ؎د٠٠عدÙ
تÙ
Ø¯ÛØ¯ زÙ
Ø§ÙØ عدد `0` را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.";
-
+ "Û±. ÚÙØ¯ **Ø±ÙØ²** ؚ٠اعتؚار سرÙÛØ³ ØŽÙ
ا اضاÙÙ ØŽÙØ¯Ø\n\n" .
+ "â«ïž ÙØ²ÛÙÙ ÙØ± Ø±ÙØ²: {$price_day} تÙÙ
اÙ\n" .
+ "ð¡ ØšØ±Ø§Û رد ؎د٠٠عدÙ
تÙ
Ø¯ÛØ¯ زÙ
Ø§ÙØ عدد `0` را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.";
+
editMessageCaption($chat_id, $message_id, $message, null);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
- }
- elseif (strpos($data, 'delete_service_confirm_') === 0) {
+ } elseif (strpos($data, 'delete_service_confirm_') === 0) {
$username = str_replace('delete_service_confirm_', '', $data);
$keyboard = ['inline_keyboard' => [[['text' => 'â
ØšÙÙØ ØØ°Ù Ú©Ù', 'callback_data' => "delete_service_do_{$username}"], ['text' => 'â Ø®ÛØ±Ø ÙØºÙ', 'callback_data' => "service_details_{$username}"]]]];
editMessageCaption($chat_id, $message_id, "â ïž Ø¢ÛØ§ از ØØ°Ù اÛ٠سرÙÛØ³ Ù
Ø·Ù
ØŠÙ ÙØ³ØªÛØ¯Ø \nاÛ٠عÙ
Ù ØºÛØ±Ùاؚ٠ؚازگ؎ت است ٠تÙ
اÙ
Ø§Ø·ÙØ§Ø¹Ø§Øª سرÙÛØ³ ٟاک Ø®ÙØ§Ùد ؎د.", $keyboard);
- }
- elseif (strpos($data, 'delete_service_do_') === 0) {
+ } elseif (strpos($data, 'delete_service_do_') === 0) {
$username = str_replace('delete_service_do_', '', $data);
editMessageCaption($chat_id, $message_id, "Ⳡدر ØØ§Ù ØØ°Ù سرÙÛØ³...");
@@ -1549,23 +1467,19 @@
deleteUserService($chat_id, $username, $server_id);
if ($result_panel) {
editMessageCaption($chat_id, $message_id, "â
سرÙÛØ³ ØŽÙ
ا ؚا Ù
ÙÙÙÛØª ØØ°Ù ؎د.");
- }
- else {
+ } else {
editMessageCaption($chat_id, $message_id, "â ïž Ø³Ø±ÙÛØ³ از ÙÛØ³Øª ØŽÙ
ا ØØ°Ù ØŽØ¯Ø Ø§Ù
ا Ù
Ù
ک٠است در ØØ°Ù از ÙŸÙ٠اصÙÛ Ù
ØŽÚ©ÙÛ Ø±Ø® داد٠ؚا؎د. ÙØ·Ùا ØšÙ ÙŸØŽØªÛØšØ§ÙÛ Ø§Ø·ÙØ§Ø¹ دÙÛØ¯.");
error_log("Failed to delete panel user {$username} on server {$server_id}. Response: " . json_encode($result_panel));
}
- }
- else {
+ } else {
editMessageCaption($chat_id, $message_id, "â Ø®Ø·Ø§ÛÛ Ø¯Ø± ÛØ§ÙØªÙ Ø§Ø·ÙØ§Ø¹Ø§Øª Ø³Ø±ÙØ± ØšØ±Ø§Û Ø§Û٠سرÙÛØ³ رخ داد.");
}
- }
- elseif ($data == 'back_to_services') {
+ } elseif ($data == 'back_to_services') {
deleteMessage($chat_id, $message_id);
$services = getUserServices($chat_id);
if (empty($services)) {
sendMessage($chat_id, "ØŽÙ
ا ÙÛÚ Ø³Ø±ÙÛØ³ ÙØ¹Ø§ÙÛ ÙØ¯Ø§Ø±Ûد.");
- }
- else {
+ } else {
$keyboard_buttons = [];
$now = time();
foreach ($services as $service) {
@@ -1582,8 +1496,7 @@
handleMainMenu($chat_id, $first_name, true);
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
die;
- }
- elseif ($apiRequest) {
+ } elseif ($apiRequest) {
apiRequest('answerCallbackQuery', ['callback_query_id' => $callback_id]);
die;
}
@@ -1605,8 +1518,7 @@
$keyboard = ['keyboard' => [[['text' => 'ð Ø§ØŽØªØ±Ø§Ú©âÚ¯Ø°Ø§Ø±Û ØŽÙ
ار٠تÙÙÙ', 'request_contact' => true]]], 'resize_keyboard' => true, 'one_time_keyboard' => true];
sendMessage($chat_id, $message, $keyboard);
die;
- }
- elseif ($verification_method === 'button') {
+ } elseif ($verification_method === 'button') {
$message = "Ø³ÙØ§Ù
! ØšØ±Ø§Û Ø§Ø·Ù
ÛÙØ§Ù از اÛÙÚ©Ù ØŽÙ
ا ÛÚ© کارؚر ÙØ§ÙØ¹Û ÙØ³ØªÛØ¯Ø ÙØ·Ùا٠رÙÛ Ø¯Ú©Ù
Ù Ø²ÛØ± Ú©ÙÛÚ© Ú©ÙÛØ¯.";
$keyboard = ['inline_keyboard' => [[['text' => 'â
تاÛÛØ¯ Ù
ÛâÚ©ÙÙ
', 'callback_data' => 'verify_by_button']]]];
sendMessage($chat_id, $message, $keyboard);
@@ -1621,18 +1533,18 @@
$amount = $state_data['charge_amount'];
$user_id = $update['message']['from']['id'];
$photo_id = $update['message']['photo'][count($update['message']['photo']) - 1]['file_id'];
-
+
// --- Ø¢Ù
ادÙâØ³Ø§Ø²Û metadata ---
$metadata_to_save = null;
if (isset($state_data['purpose']) && $state_data['purpose'] === 'complete_purchase') {
$metadata_to_save = json_encode([
- 'purpose' => 'complete_purchase',
- 'plan_id' => $state_data['plan_id'],
- 'discount_code' => $state_data['discount_code'] ?? null,
- 'custom_name' => $state_data['custom_name'] ?? 'سرÙÛØ³' // اضاÙÙ Ú©Ø±Ø¯Ù ÙØ§Ù
Ø¯ÙØ®ÙاÙ
-]);
-}
-
+ 'purpose' => 'complete_purchase',
+ 'plan_id' => $state_data['plan_id'],
+ 'discount_code' => $state_data['discount_code'] ?? null,
+ 'custom_name' => $state_data['custom_name'] ?? 'سرÙÛØ³' // اضاÙÙ Ú©Ø±Ø¯Ù ÙØ§Ù
Ø¯ÙØ®ÙاÙ
+ ]);
+ }
+
$stmt = pdo()->prepare("INSERT INTO payment_requests (user_id, amount, photo_file_id, metadata) VALUES (?, ?, ?, ?)");
$stmt->execute([$user_id, $amount, $photo_id, $metadata_to_save]);
@@ -1680,8 +1592,7 @@
$stmt->execute([$phone_number, $chat_id]);
sendMessage($chat_id, "â
Ø§ØØ±Ø§Ø² ÙÙÛØª ØŽÙ
ا ؚا Ù
ÙÙÙÛØª Ø§ÙØ¬Ø§Ù
؎د. از ÙÙ
راÙÛ ØŽÙ
ا سٟاسگزارÛÙ
!");
handleMainMenu($chat_id, $first_name);
- }
- else {
+ } else {
$message = "â Ù
ØªØ§Ø³ÙØ§ÙÙ ØŽÙ
ار٠ارساÙÛ ØŽÙ
ا Ù
ÙØ±Ø¯ تاÛÛØ¯ ÙÛØ³Øª. اÛ٠رؚات ÙÙØ· ØšØ±Ø§Û ØŽÙ
ارÙâÙØ§Û Ø§ÛØ±Ø§Ù (+98) ÙØ¹Ø§Ù است.";
$keyboard = ['keyboard' => [[['text' => 'ð Ø§ØŽØªØ±Ø§Ú©âÚ¯Ø°Ø§Ø±Û ØŽÙ
ار٠تÙÙÙ', 'request_contact' => true]]], 'resize_keyboard' => true, 'one_time_keyboard' => true];
sendMessage($chat_id, $message, $keyboard);
@@ -1707,8 +1618,7 @@
if ($isAnAdmin && (strpos($user_state, 'admin_') === 0 || $admin_view_mode === 'admin')) {
updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
handleMainMenu($chat_id, $first_name, false);
- }
- else {
+ } else {
updateUserData($chat_id, 'main_menu', ['admin_view' => 'user']);
handleMainMenu($chat_id, $first_name, false);
}
@@ -1739,95 +1649,97 @@
if ($user_state !== 'main_menu') {
switch ($user_state) {
-
+
case 'awaiting_service_name':
- $custom_name = trim($text);
- if (empty($custom_name) || mb_strlen($custom_name) > 50) {
- sendMessage($chat_id, "â ÙØ§Ù
ÙØ§Ø±Ø¯ ØŽØ¯Ù ÙØ§Ù
عتؚر است. ÙØ·Ùا٠ÛÚ© ÙØ§Ù
Ú©ÙØªØ§ÙâØªØ± (ØØ¯Ø§Ú©Ø«Ø± 50 کاراکتر) ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.", $cancelKeyboard);
- break;
- }
+ $custom_name = trim($text);
+ if (empty($custom_name) || mb_strlen($custom_name) > 50) {
+ sendMessage($chat_id, "â ÙØ§Ù
ÙØ§Ø±Ø¯ ØŽØ¯Ù ÙØ§Ù
عتؚر است. ÙØ·Ùا٠ÛÚ© ÙØ§Ù
Ú©ÙØªØ§ÙâØªØ± (ØØ¯Ø§Ú©Ø«Ø± 50 کاراکتر) ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.", $cancelKeyboard);
+ break;
+ }
- $state_data = $user_data['state_data'];
- $plan_id = $state_data['purchasing_plan_id'];
- $discount_code = $state_data['discount_code'] ?? null;
-
- $plan = getPlanById($plan_id);
- if (!$plan) {
- sendMessage($chat_id, "â Ø®Ø·Ø§ÛÛ Ø±Ø® داد. ÙŸÙÙ ÛØ§Ùت ÙØŽØ¯.");
- updateUserData($chat_id, 'main_menu');
- break;
- }
+ $state_data = $user_data['state_data'];
+ $plan_id = $state_data['purchasing_plan_id'];
+ $discount_code = $state_data['discount_code'] ?? null;
- // --- Ú©ÙŸÛ Ú©Ø±Ø¯Ù Ù
ÙØ·Ù ØšØ±Ø±Ø³Û Ù
ÙØ¬ÙØ¯Û Ù ÙÛÙ
ت ÙÙØ§ÛÛ Ø§Ø² کد ÙØšÙÛ ---
- $final_price = (float)$plan['price'];
- $discount_applied = false;
- $discount_object = null;
- if ($discount_code) {
- $stmt = pdo()->prepare("SELECT * FROM discount_codes WHERE code = ? AND status = 'active' AND usage_count < max_usage");
- $stmt->execute([$discount_code]);
- $discount = $stmt->fetch();
- if ($discount) {
- if ($discount['type'] == 'percent') {
- $final_price = $plan['price'] - ($plan['price'] * $discount['value']) / 100;
- } else {
- $final_price = $plan['price'] - $discount['value'];
- }
- $final_price = max(0, $final_price);
- $discount_applied = true;
- $discount_object = $discount;
- }
- }
-
- $user_balance = $user_data['balance'];
-
- if ($user_balance >= $final_price) {
- sendMessage($chat_id, "â³ ÙØ§Ù
سرÙÛØ³ تاÛÛØ¯ ؎د. ÙØ·Ùا٠صؚر Ú©ÙÛØ¯... در ØØ§Ù Ø§ÛØ¬Ø§Ø¯ سرÙÛØ³ ØŽÙ
ا ÙØ³ØªÛÙ
.");
- $purchase_result = completePurchase($chat_id, $plan_id, $custom_name, $final_price, $discount_code, $discount_object, $discount_applied);
-
- if ($purchase_result['success']) {
- sendPhoto($chat_id, $purchase_result['qr_code_url'], $purchase_result['caption'], $purchase_result['keyboard']);
- sendMessage(ADMIN_CHAT_ID, $purchase_result['admin_notification']);
- } else {
- sendMessage($chat_id, $purchase_result['error_message']);
- sendMessage(ADMIN_CHAT_ID, "â ïž Ø®Ø·Ø§Û Ø³Ø§Ø®Øª سرÙÛØ³ \n\nکارؚر ؚا ØŽÙØ§Ø³Ù $chat_id ÙØµØ¯ Ø®Ø±ÛØ¯ ÙŸÙÙ '{$plan['name']}' را دا؎ت اÙ
ا ارتؚاط ؚا ÙŸÙÙ ÙØ§Ù
ÙÙÙ ØšÙØ¯.");
- }
- updateUserData($chat_id, 'main_menu');
- handleMainMenu($chat_id, $first_name);
+ $plan = getPlanById($plan_id);
+ if (!$plan) {
+ sendMessage($chat_id, "â Ø®Ø·Ø§ÛÛ Ø±Ø® داد. ÙŸÙÙ ÛØ§Ùت ÙØŽØ¯.");
+ updateUserData($chat_id, 'main_menu');
+ break;
+ }
- } else {
- // کارؚر Ù
ÙØ¬ÙØ¯Û Ú©Ø§ÙÛ ÙØ¯Ø§Ø±Ø¯Ø ÙØ§Ú©ØªÙر صادر ØŽÙØ¯
- $needed_amount = $final_price - $user_balance;
- $settings = getSettings();
-
- $encoded_name = base64_encode($custom_name);
-
- $keyboard_buttons = [];
- if (($settings['payment_gateway_status'] ?? 'off') == 'on' && !empty($settings['zarinpal_merchant_id'])) {
- $callback_data_online = "charge_for_plan_{$needed_amount}_{$plan_id}";
- if ($discount_code) $callback_data_online .= "_{$discount_code}";
- $callback_data_online .= "_{$encoded_name}"; // اضاÙÙ Ú©Ø±Ø¯Ù ÙØ§Ù
ØšÙ Ø§ÙØªÙا
- $keyboard_buttons[] = [['text' => 'ð ٟرداخت Ø¢ÙÙØ§ÛÙ (زرÛÙâٟاÙ)', 'callback_data' => $callback_data_online]];
- }
- if (!empty($settings['payment_method']['card_number'])) {
- $callback_data_manual = "manual_pay_for_plan_{$needed_amount}_{$plan_id}";
- if ($discount_code) $callback_data_manual .= "_{$discount_code}";
- $callback_data_manual .= "_{$encoded_name}"; // اضاÙÙ Ú©Ø±Ø¯Ù ÙØ§Ù
ØšÙ Ø§ÙØªÙا
- $keyboard_buttons[] = [['text' => 'ð³ ٟرداخت کارت ؚ٠کارت', 'callback_data' => $callback_data_manual]];
- }
+ // --- Ú©ÙŸÛ Ú©Ø±Ø¯Ù Ù
ÙØ·Ù ØšØ±Ø±Ø³Û Ù
ÙØ¬ÙØ¯Û Ù ÙÛÙ
ت ÙÙØ§ÛÛ Ø§Ø² کد ÙØšÙÛ ---
+ $final_price = (float) $plan['price'];
+ $discount_applied = false;
+ $discount_object = null;
+ if ($discount_code) {
+ $stmt = pdo()->prepare("SELECT * FROM discount_codes WHERE code = ? AND status = 'active' AND usage_count < max_usage");
+ $stmt->execute([$discount_code]);
+ $discount = $stmt->fetch();
+ if ($discount) {
+ if ($discount['type'] == 'percent') {
+ $final_price = $plan['price'] - ($plan['price'] * $discount['value']) / 100;
+ } else {
+ $final_price = $plan['price'] - $discount['value'];
+ }
+ $final_price = max(0, $final_price);
+ $discount_applied = true;
+ $discount_object = $discount;
+ }
+ }
+
+ $user_balance = $user_data['balance'];
+
+ if ($user_balance >= $final_price) {
+ sendMessage($chat_id, "â³ ÙØ§Ù
سرÙÛØ³ تاÛÛØ¯ ؎د. ÙØ·Ùا٠صؚر Ú©ÙÛØ¯... در ØØ§Ù Ø§ÛØ¬Ø§Ø¯ سرÙÛØ³ ØŽÙ
ا ÙØ³ØªÛÙ
.");
+ $purchase_result = completePurchase($chat_id, $plan_id, $custom_name, $final_price, $discount_code, $discount_object, $discount_applied);
+
+ if ($purchase_result['success']) {
+ sendPhoto($chat_id, $purchase_result['qr_code_url'], $purchase_result['caption'], $purchase_result['keyboard']);
+ sendMessage(ADMIN_CHAT_ID, $purchase_result['admin_notification']);
+ } else {
+ sendMessage($chat_id, $purchase_result['error_message']);
+ sendMessage(ADMIN_CHAT_ID, "â ïž Ø®Ø·Ø§Û Ø³Ø§Ø®Øª سرÙÛØ³ \n\nکارؚر ؚا ØŽÙØ§Ø³Ù $chat_id ÙØµØ¯ Ø®Ø±ÛØ¯ ÙŸÙÙ '{$plan['name']}' را دا؎ت اÙ
ا ارتؚاط ؚا ÙŸÙÙ ÙØ§Ù
ÙÙÙ ØšÙØ¯.");
+ }
+ updateUserData($chat_id, 'main_menu');
+ handleMainMenu($chat_id, $first_name);
+
+ } else {
+ // کارؚر Ù
ÙØ¬ÙØ¯Û Ú©Ø§ÙÛ ÙØ¯Ø§Ø±Ø¯Ø ÙØ§Ú©ØªÙر صادر ØŽÙØ¯
+ $needed_amount = $final_price - $user_balance;
+ $settings = getSettings();
+
+ $encoded_name = base64_encode($custom_name);
+
+ $keyboard_buttons = [];
+ if (($settings['payment_gateway_status'] ?? 'off') == 'on' && !empty($settings['zarinpal_merchant_id'])) {
+ $callback_data_online = "charge_for_plan_{$needed_amount}_{$plan_id}";
+ if ($discount_code)
+ $callback_data_online .= "_{$discount_code}";
+ $callback_data_online .= "_{$encoded_name}"; // اضاÙÙ Ú©Ø±Ø¯Ù ÙØ§Ù
ØšÙ Ø§ÙØªÙا
+ $keyboard_buttons[] = [['text' => 'ð ٟرداخت Ø¢ÙÙØ§ÛÙ (زرÛÙâٟاÙ)', 'callback_data' => $callback_data_online]];
+ }
+ if (!empty($settings['payment_method']['card_number'])) {
+ $callback_data_manual = "manual_pay_for_plan_{$needed_amount}_{$plan_id}";
+ if ($discount_code)
+ $callback_data_manual .= "_{$discount_code}";
+ $callback_data_manual .= "_{$encoded_name}"; // اضاÙÙ Ú©Ø±Ø¯Ù ÙØ§Ù
ØšÙ Ø§ÙØªÙا
+ $keyboard_buttons[] = [['text' => 'ð³ ٟرداخت کارت ؚ٠کارت', 'callback_data' => $callback_data_manual]];
+ }
+
+ if (empty($keyboard_buttons)) {
+ sendMessage($chat_id, "â Ù
ÙØ¬ÙØ¯Û ØŽÙ
ا کاÙÛ ÙÛØ³Øª Ù ÙÛÚ Ø±ÙØŽ ÙŸØ±Ø¯Ø§Ø®ØªÛ ØªÙØ³Ø· ادÙ
ÛÙ ÙØ¹Ø§Ù ÙØŽØ¯Ù است.");
+ } else {
+ $message = "â ïž Ù
ÙØ¬ÙØ¯Û ØŽÙ
ا کاÙÛ ÙÛØ³Øª!\n\n" .
+ "â«ïž ÙÛÙ
ت ÙŸÙÙ: " . number_format($final_price) . " تÙÙ
اÙ\n" .
+ "â«ïž Ù
ÙØ¬ÙØ¯Û ØŽÙ
ا: " . number_format($user_balance) . " تÙÙ
اÙ\n" .
+ "ð° Ù
ØšÙØº Ù
ÙØ±Ø¯ ÙÛØ§Ø²: " . number_format($needed_amount) . " تÙÙ
ا٠\n\n" .
+ "ÙØ·ÙØ§Ù Ø±ÙØŽ ÙŸØ±Ø¯Ø§Ø®Øª ØšØ±Ø§Û ØªÚ©Ù
ÛÙ Ø®Ø±ÛØ¯ را Ø§ÙØªØ®Ø§Øš Ú©ÙÛØ¯:";
+ sendMessage($chat_id, $message, ['inline_keyboard' => $keyboard_buttons]);
+ }
+ }
+ break;
- if (empty($keyboard_buttons)) {
- sendMessage($chat_id, "â Ù
ÙØ¬ÙØ¯Û ØŽÙ
ا کاÙÛ ÙÛØ³Øª Ù ÙÛÚ Ø±ÙØŽ ÙŸØ±Ø¯Ø§Ø®ØªÛ ØªÙØ³Ø· ادÙ
ÛÙ ÙØ¹Ø§Ù ÙØŽØ¯Ù است.");
- } else {
- $message = "â ïž Ù
ÙØ¬ÙØ¯Û ØŽÙ
ا کاÙÛ ÙÛØ³Øª!\n\n" .
- "â«ïž ÙÛÙ
ت ÙŸÙÙ: " . number_format($final_price) . " تÙÙ
اÙ\n" .
- "â«ïž Ù
ÙØ¬ÙØ¯Û ØŽÙ
ا: " . number_format($user_balance) . " تÙÙ
اÙ\n" .
- "ð° Ù
ØšÙØº Ù
ÙØ±Ø¯ ÙÛØ§Ø²: " . number_format($needed_amount) . " تÙÙ
ا٠\n\n" .
- "ÙØ·ÙØ§Ù Ø±ÙØŽ ÙŸØ±Ø¯Ø§Ø®Øª ØšØ±Ø§Û ØªÚ©Ù
ÛÙ Ø®Ø±ÛØ¯ را Ø§ÙØªØ®Ø§Øš Ú©ÙÛØ¯:";
- sendMessage($chat_id, $message, ['inline_keyboard' => $keyboard_buttons]);
- }
- }
- break;
-
case 'admin_awaiting_user_search':
if ($isAnAdmin && hasPermission($chat_id, 'manage_users')) {
if (!is_numeric($text)) {
@@ -1839,10 +1751,10 @@
sendMessage($chat_id, "â Ú©Ø§Ø±ØšØ±Û ØšØ§ اÛÙ ØŽÙØ§Ø³Ù ÛØ§Ùت ÙØŽØ¯. ÙØ·ÙØ§Ù ØŽÙØ§Ø³Ù را ØšØ±Ø±Ø³Û Ú©Ø±Ø¯Ù Ù Ù
Ø¬Ø¯Ø¯Ø§Ù ØªÙØ§ØŽ Ú©ÙÛØ¯.", $cancelKeyboard);
break;
}
-
+
$chat_info_response = apiRequest('getChat', ['chat_id' => $target_user['chat_id']]);
$chat_info = json_decode($chat_info_response, true);
-
+
$profile_link_html = '';
if ($chat_info['ok'] && !empty($chat_info['result']['username'])) {
$username = $chat_info['result']['username'];
@@ -1850,84 +1762,86 @@
} else {
$profile_link_html = "ð€ ØØ³Ø§Øš کارؚرÛ: Ù
ØŽØ§ÙØ¯Ù ٟرÙÙØ§ÛÙ (ؚدÙÙ ÛÙØ²Ø±ÙÛÙ
) \n";
}
-
+
// ÙÙ
Ø§ÛØŽ Ø§Ø·ÙØ§Ø¹Ø§Øª ٠دکÙ
ÙâÙØ§Û Ù
Ø¯ÛØ±ÛتÛ
$balance = $target_user['balance'] ?? 0;
$status_text = ($target_user['status'] ?? 'active') === 'active' ? 'ÙØ¹Ø§Ù â
' : 'Ù
Ø³Ø¯ÙØ¯ ð«';
$message = "Ø§Ø·ÙØ§Ø¹Ø§Øª کارؚر: " . htmlspecialchars($target_user['first_name']) . "\n\n" .
- "â«ïž ØŽÙØ§Ø³Ù: {$target_user['chat_id']}\n" .
- $profile_link_html .
- "ð° Ù
ÙØ¬ÙدÛ: " . number_format($balance) . " تÙÙ
اÙ\n" .
- "â«ïž ÙØ¶Ø¹Ûت: {$status_text} \n\n" .
- "ÙØ·Ùا٠عÙ
ÙÛØ§Øª Ù
ÙØ±Ø¯ ÙØžØ± را Ø§ÙØªØ®Ø§Øš Ú©ÙÛØ¯:";
+ "â«ïž ØŽÙØ§Ø³Ù: {$target_user['chat_id']}\n" .
+ $profile_link_html .
+ "ð° Ù
ÙØ¬ÙدÛ: " . number_format($balance) . " تÙÙ
اÙ\n" .
+ "â«ïž ÙØ¶Ø¹Ûت: {$status_text} \n\n" .
+ "ÙØ·Ùا٠عÙ
ÙÛØ§Øª Ù
ÙØ±Ø¯ ÙØžØ± را Ø§ÙØªØ®Ø§Øš Ú©ÙÛØ¯:";
$status_button_text = ($target_user['status'] ?? 'active') === 'active' ? 'ð« Ù
Ø³Ø¯ÙØ¯ کردÙ' : 'â
آزاد کردÙ';
$status_callback = ($target_user['status'] ?? 'active') === 'active' ? "ban_user_{$target_user['chat_id']}" : "unban_user_{$target_user['chat_id']}";
- $keyboard = ['inline_keyboard' => [
- [
- ['text' => 'â Ø§ÙØ²Ø§ÛØŽ Ù
ÙØ¬ÙدÛ', 'callback_data' => "add_balance_{$target_user['chat_id']}"],
- ['text' => 'â Ú©Ø§ÙØŽ Ù
ÙØ¬ÙدÛ', 'callback_data' => "deduct_balance_{$target_user['chat_id']}"]
- ],
- [
- ['text' => 'âïž Ø§Ø±Ø³Ø§Ù ÙŸÛØ§Ù
', 'callback_data' => "message_user_{$target_user['chat_id']}"],
- ['text' => 'ð§ Ø³Ø±ÙÛØ³âÙØ§Û کارؚر', 'callback_data' => "show_user_services_{$target_user['chat_id']}"]
- ],
- [
- ['text' => $status_button_text, 'callback_data' => $status_callback]
- ],
- [
- ['text' => 'ð Ø¬Ø³ØªØ¬ÙÛ Ú©Ø§Ø±ØšØ± دÛگر', 'callback_data' => 'search_another_user']
+ $keyboard = [
+ 'inline_keyboard' => [
+ [
+ ['text' => 'â Ø§ÙØ²Ø§ÛØŽ Ù
ÙØ¬ÙدÛ', 'callback_data' => "add_balance_{$target_user['chat_id']}"],
+ ['text' => 'â Ú©Ø§ÙØŽ Ù
ÙØ¬ÙدÛ', 'callback_data' => "deduct_balance_{$target_user['chat_id']}"]
+ ],
+ [
+ ['text' => 'âïž Ø§Ø±Ø³Ø§Ù ÙŸÛØ§Ù
', 'callback_data' => "message_user_{$target_user['chat_id']}"],
+ ['text' => 'ð§ Ø³Ø±ÙÛØ³âÙØ§Û کارؚر', 'callback_data' => "show_user_services_{$target_user['chat_id']}"]
+ ],
+ [
+ ['text' => $status_button_text, 'callback_data' => $status_callback]
+ ],
+ [
+ ['text' => 'ð Ø¬Ø³ØªØ¬ÙÛ Ú©Ø§Ø±ØšØ± دÛگر', 'callback_data' => 'search_another_user']
+ ]
]
- ]];
+ ];
sendMessage($chat_id, $message, $keyboard);
// ÙØ¶Ø¹Ûت را ØšÙ ØØ§Ùت Ø§ÙØªØžØ§Ø± ØšØ±Ø§Û Ø¬Ø³ØªØ¬ÙÛ ØšØ¹Ø¯Û ØšØ±Ù
ÛâگرداÙÛÙ
تا ادÙ
ÛÙ ØšØªÙØ§Ùد ٟ؎ت سر ÙÙ
Ø¬Ø³ØªØ¬Ù Ú©ÙØ¯
updateUserData($chat_id, 'admin_awaiting_user_search', ['admin_view' => 'admin']);
}
break;
-
+
case 'admin_awaiting_renewal_price_day':
- if ($isAnAdmin && is_numeric($text) && $text >= 0) {
- $settings = getSettings();
- $settings['renewal_price_per_day'] = (int)$text;
- saveSettings($settings);
- sendMessage($chat_id, "â
ÙÛÙ
ت ؚا Ù
ÙÙÙÛØª ØªÙØžÛÙ
؎د.");
- updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
- showRenewalManagementMenu($chat_id);
- } else {
- sendMessage($chat_id, "â ÙØ·Ùا ÙÙØ· عدد ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.");
- }
- break;
-
- case 'admin_awaiting_merchant_id':
+ if ($isAnAdmin && is_numeric($text) && $text >= 0) {
+ $settings = getSettings();
+ $settings['renewal_price_per_day'] = (int) $text;
+ saveSettings($settings);
+ sendMessage($chat_id, "â
ÙÛÙ
ت ؚا Ù
ÙÙÙÛØª ØªÙØžÛÙ
؎د.");
+ updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
+ showRenewalManagementMenu($chat_id);
+ } else {
+ sendMessage($chat_id, "â ÙØ·Ùا ÙÙØ· عدد ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.");
+ }
+ break;
+
+ case 'admin_awaiting_merchant_id':
if ($isAnAdmin && strlen($text) === 36) {
$settings = getSettings();
$settings['zarinpal_merchant_id'] = $text;
saveSettings($settings);
sendMessage($chat_id, "â
Ù
رÚÙØª کد ؚا Ù
ÙÙÙÛØª Ø°Ø®ÛØ±Ù ؎د.");
updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
-
+
} else {
sendMessage($chat_id, "â Ù
رÚÙØª کد ÙØ§Ù
عتؚر است. ØšØ§ÛØ¯ دÙÛÙØ§ Û³Û¶ کاراکتر ؚا؎د.");
}
break;
-
+
case 'admin_awaiting_renewal_price_gb':
- if ($isAnAdmin && is_numeric($text) && $text >= 0) {
- $settings = getSettings();
- $settings['renewal_price_per_gb'] = (int)$text;
- saveSettings($settings);
- sendMessage($chat_id, "â
ÙÛÙ
ت ؚا Ù
ÙÙÙÛØª ØªÙØžÛÙ
؎د.");
- updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
- showRenewalManagementMenu($chat_id);
- } else {
- sendMessage($chat_id, "â ÙØ·Ùا ÙÙØ· عدد ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.");
- }
- break;
-
+ if ($isAnAdmin && is_numeric($text) && $text >= 0) {
+ $settings = getSettings();
+ $settings['renewal_price_per_gb'] = (int) $text;
+ saveSettings($settings);
+ sendMessage($chat_id, "â
ÙÛÙ
ت ؚا Ù
ÙÙÙÛØª ØªÙØžÛÙ
؎د.");
+ updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
+ showRenewalManagementMenu($chat_id);
+ } else {
+ sendMessage($chat_id, "â ÙØ·Ùا ÙÙØ· عدد ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.");
+ }
+ break;
+
case 'admin_awaiting_category_name':
if (!hasPermission($chat_id, 'manage_categories')) {
break;
@@ -1958,7 +1872,7 @@
break;
}
$state_data = $user_data['state_data'];
- $state_data['new_plan_price'] = (int)$text;
+ $state_data['new_plan_price'] = (int) $text;
updateUserData($chat_id, 'awaiting_plan_volume', $state_data);
sendMessage($chat_id, "3/6 - ÙØ·Ùا ØØ¬Ù
ÙŸÙ٠را ØšÙ Ú¯ÛÚ¯Ø§ØšØ§ÛØª (GB) ÙØ§Ø±Ø¯ Ú©ÙÛØ¯ (ÙÙØ· عدد):", $cancelKeyboard);
break;
@@ -1972,7 +1886,7 @@
break;
}
$state_data = $user_data['state_data'];
- $state_data['new_plan_volume'] = (int)$text;
+ $state_data['new_plan_volume'] = (int) $text;
updateUserData($chat_id, 'awaiting_plan_duration', $state_data);
sendMessage($chat_id, "4/6 - ÙØ·Ùا Ù
دت زÙ
ا٠ٟÙ٠را ØšÙ Ø±ÙØ² ÙØ§Ø±Ø¯ Ú©ÙÛØ¯ (ÙÙØ· عدد):", $cancelKeyboard);
break;
@@ -1986,7 +1900,7 @@
break;
}
$state_data = $user_data['state_data'];
- $state_data['new_plan_duration'] = (int)$text;
+ $state_data['new_plan_duration'] = (int) $text;
updateUserData($chat_id, 'awaiting_plan_description', $state_data);
$keyboard = ['keyboard' => [[['text' => 'رد ؎دÙ'], ['text' => 'âïž ØšØ§Ø²Ú¯ØŽØª ØšÙ Ù
ÙÙÛ Ø§ØµÙÛ']]], 'resize_keyboard' => true];
sendMessage($chat_id, "5/6 - در ØµÙØ±Øª تÙ
اÛÙØ ØªÙØ¶ÛØØ§Øª Ù
Ø®ØªØµØ±Û ØšØ±Ø§Û ÙŸÙÙ ÙØ§Ø±Ø¯ Ú©ÙÛØ¯ (Ø§Ø®ØªÛØ§Ø±Û):", $keyboard);
@@ -2006,11 +1920,11 @@
sendMessage($chat_id, "6/6 - تعداد Ù
جاز Ø®Ø±ÛØ¯ ØšØ±Ø§Û Ø§ÛÙ ÙŸÙ٠را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯ (ÙÙØ· عدد).\n\nØšØ±Ø§Û ÙØ±ÙØŽ ÙØ§Ù
ØØ¯ÙØ¯Ø Ø¹Ø¯Ø¯ `0` را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.", $keyboard);
break;
- case 'awaiting_plan_purchase_limit':
+ case 'awaiting_plan_purchase_limit':
if (!hasPermission($chat_id, 'manage_plans')) {
break;
}
- if (!is_numeric($text) || (int)$text < 0) {
+ if (!is_numeric($text) || (int) $text < 0) {
sendMessage($chat_id, "â ÙØ·Ùا ÙÙØ· ÛÚ© عدد صØÛØ (Ù
ثؚت ÛØ§ ØµÙØ±) ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.", $cancelKeyboard);
break;
}
@@ -2026,7 +1940,7 @@
'volume_gb' => $state_data['new_plan_volume'],
'duration_days' => $state_data['new_plan_duration'],
'description' => $state_data['new_plan_description'],
- 'purchase_limit' => (int)$text,
+ 'purchase_limit' => (int) $text,
];
updateUserData($chat_id, 'awaiting_plan_sub_link_setting', ['temp_plan_data' => $new_plan_data]);
@@ -2034,15 +1948,16 @@
$keyboard = ['inline_keyboard' => [[['text' => 'â
ØšÙÙ', 'callback_data' => 'plan_set_sub_yes'], ['text' => 'â Ø®Ûر', 'callback_data' => 'plan_set_sub_no']]]];
sendMessage($chat_id, "Ø³ÙØ§Ù Û±/Û²: Ø¢ÛØ§ ÙÛÙÚ© ا؎تراک (Subscription) ؚ٠کارؚر ÙÙ
Ø§ÛØŽ Ø¯Ø§Ø¯Ù ØŽÙØ¯Ø\n(ÙŸÛØŽÙÙØ§Ø¯Û: ØšÙÙ)", $keyboard);
break;
-
- case 'admin_awaiting_sub_host':
- if (!hasPermission($chat_id, 'manage_marzban')) break;
+
+ case 'admin_awaiting_sub_host':
+ if (!hasPermission($chat_id, 'manage_marzban'))
+ break;
$state_data = $user_data['state_data'];
$server_id = $state_data['editing_server_id'];
$new_sub_host = null;
$message_text = "";
-
+
if (strtolower($text) === 'reset') {
$new_sub_host = null;
$message_text = "â
آدرس ÙÛÙÚ© ا؎تراک ؚا Ù
ÙÙÙÛØª ØšÙ ØØ§Ùت ÙŸÛØŽâÙØ±Ø¶ ØšØ§Ø²ÙØŽØ§ÙÛ ØŽØ¯.";
@@ -2056,10 +1971,10 @@
$stmt = pdo()->prepare("UPDATE servers SET sub_host = ? WHERE id = ?");
$stmt->execute([$new_sub_host, $server_id]);
-
+
updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
sendMessage($chat_id, $message_text);
-
+
$servers = pdo()->query("SELECT id, name FROM servers")->fetchAll(PDO::FETCH_ASSOC);
$keyboard_buttons = [[['text' => 'â Ø§ÙØ²ÙØ¯Ù Ø³Ø±ÙØ± Ø¬Ø¯ÛØ¯', 'callback_data' => 'add_server_select_type']]];
foreach ($servers as $server) {
@@ -2068,7 +1983,7 @@
$keyboard_buttons[] = [['text' => 'âïž ØšØ§Ø²Ú¯ØŽØª ØšÙ ÙŸÙÙ', 'callback_data' => 'back_to_admin_panel']];
sendMessage($chat_id, "ð ÙÛØ³Øª Ø³Ø±ÙØ±Ùا ØšÙâØ±ÙØ² ؎د. ", ['inline_keyboard' => $keyboard_buttons]);
break;
-
+
case 'admin_awaiting_card_number':
if (!hasPermission($chat_id, 'manage_payment')) {
break;
@@ -2155,15 +2070,16 @@
} else {
$error_message = "â ïž ÙØŽØ¯Ø§Ø±: رؚات ÙØªÙØ§ÙØ³Øª ØšÙ Ø³Ø±ÙØ± Ø¬Ø¯ÛØ¯ Ù
ØªØµÙ ØŽÙØ¯. ÙØ·Ùا Ø§Ø·ÙØ§Ø¹Ø§Øª ÙØ§Ø±Ø¯ ؎د٠را ØšØ±Ø±Ø³Û Ú©Ø±Ø¯Ù Ù Ø¯Ø± ØµÙØ±Øª ÙÛØ§Ø² Ø³Ø±ÙØ± را ØØ°Ù Ù Ù
جددا٠اضاÙÙ Ú©ÙÛØ¯.";
if ($connection_error) {
- $error_message .= "\n\nØ¬Ø²ØŠÛØ§Øª خطا: \n" . htmlspecialchars($connection_error) . "";
+ $error_message .= "\n\nØ¬Ø²ØŠÛØ§Øª خطا: \n" . htmlspecialchars($connection_error) . "";
}
sendMessage($chat_id, $error_message);
}
handleMainMenu($chat_id, $first_name);
break;
-
- case 'admin_awaiting_plan_edit_input':
- if (!hasPermission($chat_id, 'manage_plans')) break;
+
+ case 'admin_awaiting_plan_edit_input':
+ if (!hasPermission($chat_id, 'manage_plans'))
+ break;
$state_data = $user_data['state_data'];
$plan_id = $state_data['editing_plan_id'];
@@ -2173,7 +2089,7 @@
$validation = $field_info['validation'];
$value = $text;
$user_message_id = $update['message']['message_id'];
-
+
$is_valid = false;
if ($validation === 'text' && !empty($value)) {
$is_valid = true;
@@ -2186,25 +2102,25 @@
deleteMessage($chat_id, $user_message_id);
break;
}
-
+
$stmt = pdo()->prepare("UPDATE plans SET `{$column}` = ? WHERE id = ?");
$stmt->execute([$value, $plan_id]);
-
+
updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
showPlanEditor($chat_id, $editor_message_id, $plan_id, "â
Ù
ÙØ¯Ø§Ø± ؚا Ù
ÙÙÙÛØª ØšÙâØ±ÙØ² ؎د.");
deleteMessage($chat_id, $user_message_id);
break;
- case 'awaiting_charge_amount':
+ case 'awaiting_charge_amount':
if (!is_numeric($text) || $text <= 0) {
sendMessage($chat_id, "â ÙØ·Ùا ÛÚ© Ù
ØšÙØº Ù
عتؚر (عدد Ù
ثؚت) ؚ٠تÙÙ
Ø§Ù ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.", $cancelKeyboard);
break;
}
- $amount = (int)$text;
+ $amount = (int) $text;
$settings = getSettings();
-
+
$keyboard_buttons = [];
-
+
if (!empty($settings['payment_method']['card_number'])) {
$keyboard_buttons[] = [['text' => 'ð³ ٟرداخت کارت ؚ٠کارت', 'callback_data' => "charge_manual_{$amount}"]];
}
@@ -2212,7 +2128,7 @@
if (($settings['payment_gateway_status'] ?? 'off') == 'on' && !empty($settings['zarinpal_merchant_id'])) {
$keyboard_buttons[] = [['text' => 'ð ٟرداخت Ø¢ÙÙØ§ÛÙ (زرÛÙâٟاÙ)', 'callback_data' => "charge_zarinpal_{$amount}"]];
}
-
+
if (empty($keyboard_buttons)) {
sendMessage($chat_id, "Ù
ØªØ§Ø³ÙØ§ÙÙ ÙÛÚ Ø±ÙØŽ ÙŸØ±Ø¯Ø§Ø®ØªÛ ØªÙØ³Ø· ادÙ
ÛÙ ÙØ¹Ø§Ù ÙØŽØ¯Ù است.");
updateUserData($chat_id, 'main_menu');
@@ -2298,8 +2214,7 @@
$user_keyboard = ['inline_keyboard' => [[['text' => 'ð¬ ٟاسخ Ù
جدد', 'callback_data' => "reply_ticket_{$ticket_id}"], ['text' => 'âïž ØšØ³ØªÙ ØªÛکت', 'callback_data' => "close_ticket_{$ticket_id}"]]]];
sendMessage($target_user_id, $user_message, $user_keyboard);
sendMessage($chat_id, "â
ٟاسخ ØŽÙ
ا ØšØ±Ø§Û Ú©Ø§Ø±ØšØ± ارسا٠؎د.");
- }
- else {
+ } else {
sendMessage($chat_id, "â Ø®Ø·Ø§ÛÛ Ø¯Ø± ارسا٠ٟاسخ رخ داد. تÛکت ÛØ§ کارؚر ÛØ§Ùت ÙØŽØ¯.");
}
updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
@@ -2328,7 +2243,7 @@
}
$state_data = $user_data['state_data'];
$target_id = $state_data['target_user_id'];
- updateUserBalance($target_id, (int)$text, 'add');
+ updateUserBalance($target_id, (int) $text, 'add');
$new_balance_data = getUserData($target_id, '');
sendMessage($chat_id, "â
Ù
ØšÙØº " . number_format($text) . " تÙÙ
ا٠ؚا Ù
ÙÙÙÛØª ØšÙ Ù
ÙØ¬ÙØ¯Û Ú©Ø§Ø±ØšØ± $target_id اضاÙ٠؎د.");
sendMessage($target_id, "â
Ù
ØšÙØº " . number_format($text) . " تÙÙ
Ø§Ù ØªÙØ³Ø· ادÙ
ÛÙ ØšÙ Ù
ÙØ¬ÙØ¯Û ØŽÙ
ا اضاÙ٠؎د.\nÙ
ÙØ¬ÙØ¯Û Ø¬Ø¯ÛØ¯: " . number_format($new_balance_data['balance']) . " تÙÙ
اÙ.");
@@ -2359,11 +2274,11 @@
$state_data = $user_data['state_data'];
$target_id = $state_data['target_user_id'];
$target_user_data = getUserData($target_id, '');
- if ($target_user_data['balance'] < (int)$text) {
+ if ($target_user_data['balance'] < (int) $text) {
sendMessage($chat_id, "â Ù
ÙØ¬ÙØ¯Û Ú©Ø§Ø±ØšØ± ØšØ±Ø§Û Ú©Ø³Ø± اÛÙ Ù
ØšÙØº کاÙÛ ÙÛØ³Øª.\nÙ
ÙØ¬ÙØ¯Û ÙØ¹ÙÛ: " . number_format($target_user_data['balance']) . " تÙÙ
اÙ", $cancelKeyboard);
break;
}
- updateUserBalance($target_id, (int)$text, 'deduct');
+ updateUserBalance($target_id, (int) $text, 'deduct');
$new_balance_data = getUserData($target_id, '');
sendMessage($chat_id, "â
Ù
ØšÙØº " . number_format($text) . " تÙÙ
ا٠ؚا Ù
ÙÙÙÛØª از Ù
ÙØ¬ÙØ¯Û Ú©Ø§Ø±ØšØ± $target_id کسر ؎د.");
sendMessage($target_id, "âïž Ù
ØšÙØº " . number_format($text) . " تÙÙ
Ø§Ù ØªÙØ³Ø· ادÙ
Û٠از Ù
ÙØ¬ÙØ¯Û ØŽÙ
ا کسر ؎د.\nÙ
ÙØ¬ÙØ¯Û Ø¬Ø¯ÛØ¯: " . number_format($new_balance_data['balance']) . " تÙÙ
اÙ.");
@@ -2394,8 +2309,7 @@
$decoded_result = json_decode($result, true);
if ($decoded_result && $decoded_result['ok']) {
sendMessage($chat_id, "â
ÙŸÛØ§Ù
ØŽÙ
ا ؚا Ù
ÙÙÙÛØª ؚ٠کارؚر $target_id ارسا٠؎د.");
- }
- else {
+ } else {
sendMessage($chat_id, "â Ø§Ø±Ø³Ø§Ù ÙŸÛØ§Ù
ؚ٠کارؚر $target_id ÙØ§Ù
ÙÙÙ ØšÙØ¯. Ù
Ù
ک٠است کارؚر رؚات را ØšÙØ§Ú© کرد٠ؚا؎د.");
}
updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
@@ -2481,7 +2395,7 @@
break;
}
$settings = getSettings();
- $settings['welcome_gift_balance'] = (int)$text;
+ $settings['welcome_gift_balance'] = (int) $text;
saveSettings($settings);
sendMessage($chat_id, "â
ÙØ¯Û٠عضÙÛØª ØšØ±Ø§Û Ú©Ø§Ø±ØšØ±Ø§Ù Ø¬Ø¯ÛØ¯ رÙÛ " . number_format($text) . " تÙÙ
Ø§Ù ØªÙØžÛÙ
؎د.");
updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
@@ -2497,7 +2411,7 @@
break;
}
sendMessage($chat_id, "ⳠعÙ
ÙÛØ§Øª Ø§ÙØ²ÙØ¯Ù ØØ¬Ù
ؚ٠تÙ
اÙ
سرÙÛØ³âÙØ§ ØŽØ±ÙØ¹ ؎د. اÛÙ ÙØ±Ø¢ÛÙØ¯ Ù
Ù
ک٠است Ú©Ù
Û Ø·Ù٠ؚک؎د...");
- $data_to_add_gb = (float)$text;
+ $data_to_add_gb = (float) $text;
$bytes_to_add = $data_to_add_gb * 1024 * 1024 * 1024;
$all_services = pdo()
->query("SELECT marzban_username, server_id FROM services")
@@ -2520,13 +2434,11 @@
$result = modifyPanelUser($username, $server_id, ['data_limit' => $new_limit]);
if ($result && !isset($result['detail'])) {
$success_count++;
- }
- else {
+ } else {
$fail_count++;
}
}
- }
- else {
+ } else {
$fail_count++;
}
usleep(100000);
@@ -2545,7 +2457,7 @@
break;
}
sendMessage($chat_id, "ⳠعÙ
ÙÛØ§Øª Ø§ÙØ²Ùد٠زÙ
ا٠ؚ٠تÙ
اÙ
سرÙÛØ³âÙØ§ ØŽØ±ÙØ¹ ؎د. اÛÙ ÙØ±Ø¢ÛÙØ¯ Ù
Ù
ک٠است Ú©Ù
Û Ø·Ù٠ؚک؎د...");
- $days_to_add = (int)$text;
+ $days_to_add = (int) $text;
$seconds_to_add = $days_to_add * 86400;
$all_services = pdo()
->query("SELECT marzban_username, server_id FROM services")
@@ -2568,13 +2480,11 @@
$result = modifyPanelUser($username, $server_id, ['expire' => $new_expire]);
if ($result && !isset($result['detail'])) {
$success_count++;
- }
- else {
+ } else {
$fail_count++;
}
}
- }
- else {
+ } else {
$fail_count++;
}
usleep(100000);
@@ -2592,7 +2502,7 @@
sendMessage($chat_id, "â ØŽÙØ§Ø³Ù ÙØ§Ø±Ø¯ ØŽØ¯Ù ÙØ§Ù
عتؚر است. ÙØ·Ùا ÙÙØ· عدد ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.", $cancelKeyboard);
break;
}
- $target_id = (int)$text;
+ $target_id = (int) $text;
if ($target_id == ADMIN_CHAT_ID) {
sendMessage($chat_id, "â ØŽÙ
ا ÙÙ
ÛâØªÙØ§ÙÛØ¯ Ø®ÙØ¯ØªØ§Ù را ؚ٠عÙÙØ§Ù ادÙ
Û٠اضاÙÙ Ú©ÙÛØ¯.", $cancelKeyboard);
break;
@@ -2613,8 +2523,7 @@
$target_first_name = "کارؚر {$target_id}";
if ($chat_info['ok'] && isset($chat_info['result']['first_name'])) {
$target_first_name = $chat_info['result']['first_name'];
- }
- else {
+ } else {
sendMessage($chat_id, "â ïž ÙØªÙØ§ÙØ³ØªÙ
ÙØ§Ù
کارؚر را از تÙگراÙ
Ø¯Ø±ÛØ§Ùت Ú©ÙÙ
. ؚا ÙØ§Ù
ÙŸÛØŽâÙØ±Ø¶ ثؚت ؎د.");
}
addAdmin($target_id, $target_first_name);
@@ -2636,7 +2545,7 @@
break;
}
$state_data = $user_data['state_data'];
- $state_data['new_discount_value'] = (int)$text;
+ $state_data['new_discount_value'] = (int) $text;
updateUserData($chat_id, 'admin_awaiting_discount_usage', $state_data);
sendMessage($chat_id, "4/4 - ØØ¯Ø§Ú©Ø«Ø± تعداد Ø§Ø³ØªÙØ§Ø¯Ù از اÛ٠کد را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯ (ÙÙØ· عدد):", $cancelKeyboard);
break;
@@ -2648,30 +2557,30 @@
}
$discount_data = $user_data['state_data'];
$stmt = pdo()->prepare("INSERT INTO discount_codes (code, type, value, max_usage) VALUES (?, ?, ?, ?)");
- $stmt->execute([$discount_data['new_discount_code'], $discount_data['new_discount_type'], $discount_data['new_discount_value'], (int)$text]);
+ $stmt->execute([$discount_data['new_discount_code'], $discount_data['new_discount_type'], $discount_data['new_discount_value'], (int) $text]);
sendMessage($chat_id, "â
کد تخÙÛÙ `{$discount_data['new_discount_code']}` ؚا Ù
ÙÙÙÛØª Ø§ÛØ¬Ø§Ø¯ ؎د.");
updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
$current_first_name = $update['message']['from']['first_name'];
handleMainMenu($chat_id, $current_first_name);
break;
- case 'user_awaiting_discount_code':
+ case 'user_awaiting_discount_code':
$code = strtoupper(trim($text));
$category_id = $user_data['state_data']['target_category_id'];
$server_id = $user_data['state_data']['target_server_id'];
-
+
$stmt = pdo()->prepare("SELECT * FROM discount_codes WHERE code = ? AND status = 'active' AND usage_count < max_usage");
$stmt->execute([$code]);
$discount = $stmt->fetch();
if (!$discount) {
sendMessage($chat_id, "â کد تخÙÛÙ ÙØ§Ø±Ø¯ ØŽØ¯Ù ÙØ§Ù
عتؚر ÛØ§ Ù
ÙÙØ¶Û ؎د٠است.");
-
- showPlansForCategoryAndServer($chat_id, $category_id, $server_id);
+
+ showPlansForCategoryAndServer($chat_id, $category_id, $server_id);
updateUserData($chat_id, 'main_menu');
break;
}
-
+
$plan_stmt = pdo()->prepare("SELECT * FROM plans WHERE category_id = ? AND server_id = ? AND status = 'active' AND is_test_plan = 0");
$plan_stmt->execute([$category_id, $server_id]);
$active_plans = $plan_stmt->fetchAll(PDO::FETCH_ASSOC);
@@ -2685,15 +2594,14 @@
$discounted_price = 0;
if ($discount['type'] == 'percent') {
$discounted_price = $original_price - ($original_price * $discount['value']) / 100;
- }
- else {
+ } else {
$discounted_price = $original_price - $discount['value'];
}
$discounted_price = max(0, $discounted_price);
$button_text = "{$plan['name']} | " . number_format($original_price) . " â¬
ïž " . number_format($discounted_price) . " تÙÙ
اÙ";
$keyboard_buttons[] = [['text' => $button_text, 'callback_data' => "buy_plan_{$plan['id']}_with_code_{$code}"]];
}
-
+
$keyboard_buttons[] = [['text' => 'âïž ØšØ§Ø²Ú¯ØŽØª', 'callback_data' => "show_plans_cat_{$category_id}_srv_{$server_id}"]];
sendMessage($chat_id, $message, ['inline_keyboard' => $keyboard_buttons]);
updateUserData($chat_id, 'main_menu');
@@ -2707,7 +2615,7 @@
sendMessage($chat_id, "â ÙØ·Ùا ÛÚ© Ù
ØšÙØº Ù
عتؚر (عدد Ù
ثؚت) ؚ٠تÙÙ
Ø§Ù ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.", $cancelKeyboard);
break;
}
- $amount_to_add = (int)$text;
+ $amount_to_add = (int) $text;
sendMessage($chat_id, "ⳠعÙ
ÙÛØ§Øª Ø§ÙØ²Ø§ÛØŽ Ù
ÙØ¬ÙØ¯Û ÙÙ
گاÙÛ ØŽØ±ÙØ¹ ؎د...");
$updated_users_count = increaseAllUsersBalance($amount_to_add);
sendMessage($chat_id, "â
عÙ
ÙÛØ§Øª ؚا Ù
ÙÙÙÛØª Ø§ÙØ¬Ø§Ù
؎د.\nÙ
ØšÙØº " . number_format($amount_to_add) . " تÙÙ
ا٠ؚ٠Ù
ÙØ¬ÙØ¯Û {$updated_users_count} کارؚر ÙØ¹Ø§Ù اضاÙÙ Ú¯Ø±Ø¯ÛØ¯.");
@@ -2732,8 +2640,7 @@
$state_data['new_guide_content_type'] = 'photo';
$state_data['new_guide_photo_id'] = $update['message']['photo'][count($update['message']['photo']) - 1]['file_id'];
$state_data['new_guide_message_text'] = $update['message']['caption'] ?? '';
- }
- else {
+ } else {
$state_data['new_guide_content_type'] = 'text';
$state_data['new_guide_photo_id'] = null;
$state_data['new_guide_message_text'] = $text;
@@ -2754,7 +2661,7 @@
break;
}
$settings = getSettings();
- $settings['test_config_usage_limit'] = (int)$text;
+ $settings['test_config_usage_limit'] = (int) $text;
saveSettings($settings);
sendMessage($chat_id, "â
تعداد Ù
جاز ØšØ±Ø§Û ÙØ± کارؚر رÙÛ {$text} ؚار ØªÙØžÛÙ
؎د.");
updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
@@ -2799,7 +2706,7 @@
break;
}
$settings = getSettings();
- $settings['notification_expire_days'] = (int)$text;
+ $settings['notification_expire_days'] = (int) $text;
saveSettings($settings);
sendMessage($chat_id, "â
ؚا Ù
ÙÙÙÛØª رÙÛ {$text} Ø±ÙØ² ØªÙØžÛÙ
؎د.");
updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
@@ -2815,7 +2722,7 @@
break;
}
$settings = getSettings();
- $settings['notification_expire_gb'] = (int)$text;
+ $settings['notification_expire_gb'] = (int) $text;
saveSettings($settings);
sendMessage($chat_id, "â
ؚا Ù
ÙÙÙÛØª رÙÛ {$text} Ú¯ÛÚ¯Ø§ØšØ§ÛØª ØªÙØžÛÙ
؎د.");
updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
@@ -2843,7 +2750,7 @@
break;
}
$settings = getSettings();
- $settings['notification_inactive_days'] = (int)$text;
+ $settings['notification_inactive_days'] = (int) $text;
saveSettings($settings);
sendMessage($chat_id, "â
ؚا Ù
ÙÙÙÛØª رÙÛ {$text} Ø±ÙØ² ØªÙØžÛÙ
؎د.");
updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
@@ -2861,22 +2768,22 @@
updateUserData($chat_id, 'main_menu', ['admin_view' => 'admin']);
$data = 'config_inactive_reminder';
break;
-
+
case 'user_awaiting_renewal_days':
if (!is_numeric($text) || $text < 0) {
sendMessage($chat_id, "â ÙØ·Ùا ÙÙØ· ÛÚ© عدد صØÛØ (Ù
ثؚت ÛØ§ ØµÙØ±) ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.");
break;
}
$state_data = $user_data['state_data'];
- $state_data['renewal_days'] = (int)$text;
+ $state_data['renewal_days'] = (int) $text;
updateUserData($chat_id, 'user_awaiting_renewal_gb', $state_data);
$settings = getSettings();
$price_gb = number_format($settings['renewal_price_per_gb'] ?? 2000);
$message = "تÙ
Ø¯ÛØ¯ سرÙÛØ³ \n\n" .
- "Û². ÚÙØ¯ **Ú¯ÛÚ¯Ø§ØšØ§ÛØª** ØšÙ ØØ¬Ù
سرÙÛØ³ ØŽÙ
ا اضاÙÙ ØŽÙØ¯Ø\n\n" .
- "â«ïž ÙØ²ÛÙÙ ÙØ± Ú¯ÛÚ¯: {$price_gb} تÙÙ
اÙ\n" .
- "ð¡ ØšØ±Ø§Û رد ؎د٠٠عدÙ
تÙ
Ø¯ÛØ¯ ØØ¬Ù
Ø Ø¹Ø¯Ø¯ `0` را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.";
+ "Û². ÚÙØ¯ **Ú¯ÛÚ¯Ø§ØšØ§ÛØª** ØšÙ ØØ¬Ù
سرÙÛØ³ ØŽÙ
ا اضاÙÙ ØŽÙØ¯Ø\n\n" .
+ "â«ïž ÙØ²ÛÙÙ ÙØ± Ú¯ÛÚ¯: {$price_gb} تÙÙ
اÙ\n" .
+ "ð¡ ØšØ±Ø§Û رد ؎د٠٠عدÙ
تÙ
Ø¯ÛØ¯ ØØ¬Ù
Ø Ø¹Ø¯Ø¯ `0` را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯.";
sendMessage($chat_id, $message);
break;
@@ -2887,18 +2794,18 @@
}
$state_data = $user_data['state_data'];
$days_to_add = $state_data['renewal_days'];
- $gb_to_add = (int)$text;
-
+ $gb_to_add = (int) $text;
+
if ($days_to_add == 0 && $gb_to_add == 0) {
sendMessage($chat_id, "ØŽÙ
ا ÙÛÚ Ù
ÙØ¯Ø§Ø±Û ØšØ±Ø§Û ØªÙ
Ø¯ÛØ¯ ÙØ§Ø±Ø¯ ÙÚ©Ø±Ø¯ÛØ¯. عÙ
ÙÛØ§Øª ÙØºÙ ؎د.");
updateUserData($chat_id, 'main_menu');
handleMainMenu($chat_id, $first_name);
break;
}
-
+
$settings = getSettings();
- $cost_days = $days_to_add * (int)($settings['renewal_price_per_day'] ?? 1000);
- $cost_gb = $gb_to_add * (int)($settings['renewal_price_per_gb'] ?? 2000);
+ $cost_days = $days_to_add * (int) ($settings['renewal_price_per_day'] ?? 1000);
+ $cost_gb = $gb_to_add * (int) ($settings['renewal_price_per_gb'] ?? 2000);
$total_cost = $cost_days + $cost_gb;
$state_data['renewal_gb'] = $gb_to_add;
@@ -2906,44 +2813,48 @@
updateUserData($chat_id, 'user_confirming_renewal', $state_data);
$summary = "Ø®ÙØ§ØµÙ Ø¯Ø±Ø®ÙØ§Ø³Øª تÙ
Ø¯ÛØ¯ ØŽÙ
ا: \n\n" .
- "â«ïž Ø§ÙØ²Ø§ÛØŽ زÙ
اÙ: {$days_to_add} Ø±ÙØ² \n" .
- "â«ïž Ø§ÙØ²Ø§ÛØŽ ØØ¬Ù
: {$gb_to_add} Ú¯ÛÚ¯Ø§ØšØ§ÛØª \n\n" .
- "ð° ÙØ²ÛÙÙ Ú©Ù: " . number_format($total_cost) . " تÙÙ
ا٠\n\n" .
- "Ù
ÙØ¬ÙØ¯Û ÙØ¹ÙÛ ØŽÙ
ا: " . number_format($user_data['balance']) . " تÙÙ
اÙ\n\n" .
- "Ø¢ÛØ§ تاÛÛØ¯ Ù
ÛâÚ©ÙÛØ¯Ø";
+ "â«ïž Ø§ÙØ²Ø§ÛØŽ زÙ
اÙ: {$days_to_add} Ø±ÙØ² \n" .
+ "â«ïž Ø§ÙØ²Ø§ÛØŽ ØØ¬Ù
: {$gb_to_add} Ú¯ÛÚ¯Ø§ØšØ§ÛØª \n\n" .
+ "ð° ÙØ²ÛÙÙ Ú©Ù: " . number_format($total_cost) . " تÙÙ
ا٠\n\n" .
+ "Ù
ÙØ¬ÙØ¯Û ÙØ¹ÙÛ ØŽÙ
ا: " . number_format($user_data['balance']) . " تÙÙ
اÙ\n\n" .
+ "Ø¢ÛØ§ تاÛÛØ¯ Ù
ÛâÚ©ÙÛØ¯Ø";
$keyboard = ['inline_keyboard' => [[['text' => 'â
ØšÙÙØ ٟرداخت Ú©Ù', 'callback_data' => 'confirm_renewal_payment']]]];
sendMessage($chat_id, $summary, $keyboard);
break;
-
+
case 'awaiting_renewal_screenshot':
if (isset($update['message']['photo'])) {
$state_data = $user_data['state_data'];
$photo_id = $update['message']['photo'][count($update['message']['photo']) - 1]['file_id'];
-
+
$stmt = pdo()->prepare("UPDATE renewal_requests SET photo_file_id = ? WHERE id = ?");
$stmt->execute([$photo_id, $state_data['renewal_request_id']]);
-
+
$request_id = $state_data['renewal_request_id'];
$caption = "Ø¯Ø±Ø®ÙØ§Ø³Øª تÙ
Ø¯ÛØ¯ سرÙÛØ³ Ø¬Ø¯ÛØ¯ \n\n" .
- "ð€ کارؚر: " . htmlspecialchars($first_name) . " ({$chat_id})\n" .
- "â«ïž سرÙÛØ³: {$state_data['renewal_username']}\n" .
- "ⰠتÙ
Ø¯ÛØ¯ زÙ
اÙ: {$state_data['renewal_days']} Ø±ÙØ²\n" .
- "ð ØªÙ
Ø¯ÛØ¯ ØØ¬Ù
: {$state_data['renewal_gb']} Ú¯ÛÚ¯\n" .
- "ð° ÙØ²ÛÙÙ: " . number_format($state_data['renewal_total_cost']) . " تÙÙ
اÙ\n" .
- "â«ïž ØŽÙ
Ø§Ø±Ù Ø¯Ø±Ø®ÙØ§Ø³Øª: #R-{$request_id}";
-
- $keyboard = ['inline_keyboard' => [[
- ['text' => 'â
تاÛÛØ¯ تÙ
Ø¯ÛØ¯', 'callback_data' => "approve_renewal_{$request_id}"],
- ['text' => 'â Ø±Ø¯ تÙ
Ø¯ÛØ¯', 'callback_data' => "reject_renewal_{$request_id}"]
- ]]];
+ "ð€ کارؚر: " . htmlspecialchars($first_name) . " ({$chat_id})\n" .
+ "â«ïž سرÙÛØ³: {$state_data['renewal_username']}\n" .
+ "ⰠتÙ
Ø¯ÛØ¯ زÙ
اÙ: {$state_data['renewal_days']} Ø±ÙØ²\n" .
+ "ð ØªÙ
Ø¯ÛØ¯ ØØ¬Ù
: {$state_data['renewal_gb']} Ú¯ÛÚ¯\n" .
+ "ð° ÙØ²ÛÙÙ: " . number_format($state_data['renewal_total_cost']) . " تÙÙ
اÙ\n" .
+ "â«ïž ØŽÙ
Ø§Ø±Ù Ø¯Ø±Ø®ÙØ§Ø³Øª: #R-{$request_id}";
+
+ $keyboard = [
+ 'inline_keyboard' => [
+ [
+ ['text' => 'â
تاÛÛØ¯ تÙ
Ø¯ÛØ¯', 'callback_data' => "approve_renewal_{$request_id}"],
+ ['text' => 'â Ø±Ø¯ تÙ
Ø¯ÛØ¯', 'callback_data' => "reject_renewal_{$request_id}"]
+ ]
+ ]
+ ];
$all_admins = getAdmins();
$all_admins[ADMIN_CHAT_ID] = [];
foreach (array_keys($all_admins) as $admin_id) {
if (hasPermission($admin_id, 'manage_payment')) {
- sendPhoto($admin_id, $photo_id, $caption, $keyboard);
+ sendPhoto($admin_id, $photo_id, $caption, $keyboard);
}
}
@@ -2965,8 +2876,7 @@
$categories = getCategories(true);
if (empty($categories)) {
sendMessage($chat_id, "Ù
ØªØ§Ø³ÙØ§Ù٠در ØØ§Ù ØØ§Ø¶Ø± ÙÛÚ Ø³Ø±ÙÛØ³Û ØšØ±Ø§Û ÙØ±ÙØŽ Ù
ÙØ¬Ùد ÙÛØ³Øª.");
- }
- else {
+ } else {
$keyboard_buttons = [];
foreach ($categories as $category) {
$keyboard_buttons[] = [['text' => 'ð ' . $category['name'], 'callback_data' => 'cat_' . $category['id']]];
@@ -3120,27 +3030,8 @@
sendMessage($chat_id, "ÙØ·ÙØ§Ù ØŽÙØ§Ø³Ù Ø¹Ø¯Ø¯Û (Chat ID) کارؚر Ù
ÙØ±Ø¯ ÙØžØ± را ØšØ±Ø§Û Ø¬Ø³ØªØ¬Ù ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:", $cancelKeyboard);
}
break;
-
- case 'ð° Ø§ÙØ²Ø§ÛØŽ Ù
ÙØ¬ÙØ¯Û ÙÙ
گاÙÛ':
- if ($isAnAdmin && hasPermission($chat_id, 'manage_users')) {
- updateUserData($chat_id, 'admin_awaiting_bulk_balance_amount', ['admin_view' => 'admin']);
- sendMessage($chat_id, "ÙØ·Ùا Ù
ØšÙØºÛ Ú©Ù Ù
ÛâØ®ÙØ§ÙÛØ¯ ØšÙ Ù
ÙØ¬ÙØ¯Û ØªÙ
اÙ
Ú©Ø§Ø±ØšØ±Ø§Ù ÙØ¹Ø§Ù اضاÙÙ ØŽÙØ¯ را ؚ٠تÙÙ
Ø§Ù ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:", $cancelKeyboard);
- }
- break;
- case 'â Ø§ÙØ²ÙØ¯Ù ØØ¬Ù
ÙÙ
گاÙÛ':
- if ($isAnAdmin && hasPermission($chat_id, 'manage_users')) {
- updateUserData($chat_id, 'admin_awaiting_bulk_data_amount', ['admin_view' => 'admin']);
- sendMessage($chat_id, "ÙØ·Ùا Ù
ÙØ¯Ø§Ø± ØØ¬Ù
Û Ú©Ù Ù
ÛâØ®ÙØ§ÙÛØ¯ ؚ٠تÙ
اÙ
سرÙÛØ³âÙØ§ اضاÙÙ ØŽÙØ¯ را ØšÙ Ú¯ÛÚ¯Ø§ØšØ§ÛØª (GB) ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:", $cancelKeyboard);
- }
- break;
- case 'â Ø§ÙØ²ÙØ¯Ù زÙ
ا٠ÙÙ
گاÙÛ':
- if ($isAnAdmin && hasPermission($chat_id, 'manage_users')) {
- updateUserData($chat_id, 'admin_awaiting_bulk_time_amount', ['admin_view' => 'admin']);
- sendMessage($chat_id, "ÙØ·Ùا تعداد Ø±ÙØ²Û Ú©Ù Ù
ÛâØ®ÙØ§ÙÛØ¯ ؚ٠تÙ
اÙ
سرÙÛØ³âÙØ§ اضاÙÙ ØŽÙØ¯ را ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:", $cancelKeyboard);
- }
- break;
case 'ð£ Ø§Ø±Ø³Ø§Ù ÙÙ
گاÙÛ':
if ($isAnAdmin && hasPermission($chat_id, 'broadcast')) {
@@ -3257,16 +3148,16 @@
sendMessage($chat_id, "Ù
رØÙÙ Û±/Û³: ØŽÙ
ار٠کارت Û±Û¶ رÙÙ
Û Ø±Ø§ ÙØ§Ø±Ø¯ Ú©ÙÛØ¯:", $cancelKeyboard);
}
break;
-
+
case 'ð³ Ù
Ø¯ÛØ±Ûت درگا٠ٟرداخت':
if ($isAnAdmin) {
$settings = getSettings();
$status_icon = ($settings['payment_gateway_status'] ?? 'off') == 'on' ? 'â
' : 'â';
$merchant_id = $settings['zarinpal_merchant_id'] ?? 'ØªÙØžÛÙ
ÙØŽØ¯Ù ';
-
+
$message = "ð³ Ù
Ø¯ÛØ±Ûت درگا٠ٟرداخت زرÛÙâٟا٠\n\n" .
- "â«ïž ÙØ¶Ø¹Ûت Ú©ÙÛ: " . ($status_icon == 'â
' ? 'ÙØ¹Ø§Ù ' : 'ØºÛØ±Ùعا٠') . "\n" .
- "â«ïž Ù
رÚÙØª کد: {$merchant_id}";
+ "â«ïž ÙØ¶Ø¹Ûت Ú©ÙÛ: " . ($status_icon == 'â
' ? 'ÙØ¹Ø§Ù ' : 'ØºÛØ±Ùعا٠') . "\n" .
+ "â«ïž Ù
رÚÙØª کد: {$merchant_id}";
$keyboard = [
'inline_keyboard' => [
@@ -3278,7 +3169,7 @@
sendMessage($chat_id, $message, $keyboard);
}
break;
-
+
case 'ð Ø¢Ù
ار Ú©ÙÛ':
if ($isAnAdmin && hasPermission($chat_id, 'view_stats')) {
$total_users = pdo()
@@ -3340,12 +3231,12 @@
sendMessage($chat_id, "ð ØšØ®ØŽ Ù
Ø¯ÛØ±Ûت Ú©Ø¯ÙØ§Û تخÙÛÙ:", $keyboard);
}
break;
-
+
case 'ð Ù
Ø¯ÛØ±Ûت تÙ
Ø¯ÛØ¯':
- if ($isAnAdmin) {
+ if ($isAnAdmin) {
showRenewalManagementMenu($chat_id);
}
- break;
+ break;
case 'â Ø§ÙØ²ÙØ¯Ù کد تخÙÛÙ':
if ($isAnAdmin) {
@@ -3384,8 +3275,7 @@
foreach ($services as $service) {
if ($service['expire_timestamp'] < $now) {
$expired_services_count++;
- }
- else {
+ } else {
$active_services_count++;
}
}
@@ -3409,8 +3299,7 @@
$services = getUserServices($chat_id);
if (empty($services)) {
sendMessage($chat_id, "ØŽÙ
ا ÙÛÚ Ø³Ø±ÙÛØ³ ÙØ¹Ø§ÙÛ ÙØ¯Ø§Ø±Ûد.");
- }
- else {
+ } else {
$keyboard_buttons = [];
$now = time();
foreach ($services as $service) {
@@ -3440,7 +3329,7 @@
}
$settings = getSettings();
- $usage_limit = (int)($settings['test_config_usage_limit'] ?? 1);
+ $usage_limit = (int) ($settings['test_config_usage_limit'] ?? 1);
if ($user_data['test_config_count'] >= $usage_limit) {
sendMessage($chat_id, "â ØŽÙ
ا ÙØšÙا از ØØ¯Ø§Ú©Ø«Ø± تعداد کاÙÙÛÚ¯ تست Ø®ÙØ¯ Ø§Ø³ØªÙØ§Ø¯Ù کردÙâØ§ÛØ¯.");
@@ -3498,6 +3387,17 @@
}
break;
+ case 'ð± ÙŸÙ٠کارؚرÛ':
+ $web_app_url = BASE_URL . '/web/user/index.php';
+ $message = "ØšØ±Ø§Û ÙØ±Ùد ØšÙ ÙŸÙÙ Ú©Ø§Ø±ØšØ±Û Ø±ÙÛ Ø¯Ú©Ù
Ù Ø²ÛØ± Ú©ÙÛÚ© Ú©ÙÛØ¯:";
+ $keyboard = [
+ 'inline_keyboard' => [
+ [['text' => 'ð± ÙØ±Ùد ØšÙ ÙŸÙ٠کارؚرÛ', 'web_app' => ['url' => $web_app_url]]]
+ ]
+ ];
+ sendMessage($chat_id, $message, $keyboard);
+ break;
+
default:
if ($user_state === 'main_menu' && !$apiRequest) {
sendMessage($chat_id, "Ø¯Ø³ØªÙØ± ØŽÙ
ا را Ù
ØªÙØ¬Ù ÙØŽØ¯Ù
. ÙØ·Ùا از دکÙ
ÙâÙØ§Û Ù
ÙØ¬Ùد Ø§Ø³ØªÙØ§Ø¯Ù Ú©ÙÛØ¯.");
diff --git a/src/includes/config.php b/src/includes/config.php
index 759d3c0..78d42d0 100644
--- a/src/includes/config.php
+++ b/src/includes/config.php
@@ -10,6 +10,9 @@
define('SECRET_TOKEN', 'SECRET');
+// آدرس داÙ
ÙÙ ØšØ±Ø§Û ÙØšâاٟ (ؚدÙÙ Ø§Ø³ÙØŽ Ø¢Ø®Ø±)
+define('BASE_URL', 'https://your-domain.com');
+
// ---------------------------------------------------------------------
// --- Ù
Ø³ÛØ±ÙØ§Û ÙŸØ±ÙÚÙ ---
// ---------------------------------------------------------------------
@@ -21,9 +24,9 @@
// --- ØªÙØžÛÙ
ات اتصا٠ؚ٠ٟاÛگا٠داد٠---
// ---------------------------------------------------------------------
-define('DB_HOST', 'localhost');
-define('DB_NAME', 'NAME');
-define('DB_USER', 'USER');
+define('DB_HOST', 'localhost');
+define('DB_NAME', 'NAME');
+define('DB_USER', 'USER');
define('DB_PASS', 'PASSWORD');
// ---------------------------------------------------------------------
diff --git a/src/includes/functions.php b/src/includes/functions.php
index 44e1fc0..736c792 100644
--- a/src/includes/functions.php
+++ b/src/includes/functions.php
@@ -261,6 +261,119 @@ function resetAllUsersTestCount()
return $stmt->rowCount();
}
+/**
+ * Add volume (GB) to all active services - Updates both database AND panel servers
+ * Returns array with success and fail counts
+ */
+function addVolumeToAllServices($volume_gb)
+{
+ $bytes_to_add = $volume_gb * 1024 * 1024 * 1024;
+
+ $all_services = pdo()
+ ->query("SELECT marzban_username, server_id FROM services")
+ ->fetchAll(PDO::FETCH_ASSOC);
+
+ $success_count = 0;
+ $fail_count = 0;
+
+ foreach ($all_services as $service) {
+ $username = $service['marzban_username'];
+ $server_id = $service['server_id'];
+
+ if (!$server_id) {
+ $fail_count++;
+ continue;
+ }
+
+ // Get current user data from panel
+ $current_user_data = getPanelUser($username, $server_id);
+
+ if ($current_user_data && !isset($current_user_data['detail'])) {
+ $current_limit = $current_user_data['data_limit'];
+
+ if ($current_limit > 0) {
+ $new_limit = $current_limit + $bytes_to_add;
+
+ // Update on panel server via API
+ $result = modifyPanelUser($username, $server_id, ['data_limit' => $new_limit]);
+
+ if ($result && !isset($result['detail'])) {
+ $success_count++;
+ } else {
+ $fail_count++;
+ }
+ } else {
+ // User has unlimited data
+ $fail_count++;
+ }
+ } else {
+ $fail_count++;
+ }
+
+ // Small delay to avoid overwhelming the API
+ usleep(100000); // 0.1 second
+ }
+
+ return ['success' => $success_count, 'fail' => $fail_count];
+}
+
+/**
+ * Add time (days) to all active services - Updates both database AND panel servers
+ * Returns array with success and fail counts
+ */
+function addTimeToAllServices($days)
+{
+ $seconds_to_add = $days * 86400; // 86400 seconds in a day
+
+ $all_services = pdo()
+ ->query("SELECT marzban_username, server_id FROM services")
+ ->fetchAll(PDO::FETCH_ASSOC);
+
+ $success_count = 0;
+ $fail_count = 0;
+
+ foreach ($all_services as $service) {
+ $username = $service['marzban_username'];
+ $server_id = $service['server_id'];
+
+ if (!$server_id) {
+ $fail_count++;
+ continue;
+ }
+
+ // Get current user data from panel
+ $current_user_data = getPanelUser($username, $server_id);
+
+ if ($current_user_data && !isset($current_user_data['detail'])) {
+ $current_expire = $current_user_data['expire'] ?? 0;
+
+ if ($current_expire > 0) {
+ // If already expired, start from now. Otherwise add to current expiry
+ $new_expire = $current_expire < time() ? time() + $seconds_to_add : $current_expire + $seconds_to_add;
+
+ // Update on panel server via API
+ $result = modifyPanelUser($username, $server_id, ['expire' => $new_expire]);
+
+ if ($result && !isset($result['detail'])) {
+ $success_count++;
+ } else {
+ $fail_count++;
+ }
+ } else {
+ // User has unlimited time
+ $fail_count++;
+ }
+ } else {
+ $fail_count++;
+ }
+
+ // Small delay to avoid overwhelming the API
+ usleep(100000); // 0.1 second
+ }
+
+ return ['success' => $success_count, 'fail' => $fail_count];
+}
+
// --- Ù
Ø¯ÛØ±Ûت ادÙ
ÛÙâÙØ§ ---
function getAdmins()
{
@@ -788,6 +901,9 @@ function handleMainMenu($chat_id, $first_name, $is_start_command = false)
$keyboard_buttons[] = [['text' => 'ð Ø±Ø§ÙÙÙ
ا']];
}
+ // Add User Panel Button
+ $keyboard_buttons[] = [['text' => 'ð± ÙŸÙ٠کارؚرÛ']];
+
if ($isAnAdmin) {
if ($admin_view_mode === 'admin') {
if ($is_start_command) {
@@ -1379,3 +1495,16 @@ function completePurchase($user_id, $plan_id, $custom_name, $final_price, $disco
'error_message' => "â Ù
ØªØ§Ø³ÙØ§Ù٠در Ø§ÛØ¬Ø§Ø¯ سرÙÛØ³ ØŽÙ
ا Ù
ØŽÚ©ÙÛ ÙŸÛØŽ Ø¢Ù
د. ÙØ·Ùا ؚا ÙŸØŽØªÛØšØ§ÙÛ ØªÙ
اس ØšÚ¯ÛØ±Ûد. Ù
ØšÙØºÛ از ØØ³Ø§Øš ØŽÙ
ا کسر ÙØŽØ¯Ù است."
];
}
+
+function getServers()
+{
+ $stmt = pdo()->query("SELECT * FROM servers ORDER BY id DESC");
+ return $stmt->fetchAll(PDO::FETCH_ASSOC);
+}
+
+function getServerById($id)
+{
+ $stmt = pdo()->prepare("SELECT * FROM servers WHERE id = ?");
+ $stmt->execute([$id]);
+ return $stmt->fetch(PDO::FETCH_ASSOC);
+}
diff --git a/src/web/assets/css/style.css b/src/web/assets/css/style.css
index a7d699c..7c95c49 100644
--- a/src/web/assets/css/style.css
+++ b/src/web/assets/css/style.css
@@ -46,6 +46,9 @@ body {
flex: 1;
margin-right: var(--sidebar-width);
transition: margin-right 0.3s;
+ width: 100%;
+ min-width: 0;
+ /* Important for flex child */
}
/* Sidebar */
@@ -61,6 +64,7 @@ body {
flex-direction: column;
z-index: 1000;
transition: transform 0.3s;
+ overflow-y: auto;
}
.sidebar-header {
@@ -151,6 +155,7 @@ body {
.topbar h1 {
font-size: 1.5rem;
font-weight: 600;
+ flex: 1;
}
.menu-toggle {
@@ -161,6 +166,12 @@ body {
font-size: 1.5rem;
cursor: pointer;
margin-left: 15px;
+ padding: 8px;
+}
+
+.menu-toggle:hover {
+ background-color: rgba(139, 92, 246, 0.1);
+ border-radius: 8px;
}
/* Content Area */
@@ -200,17 +211,42 @@ body {
align-items: center;
justify-content: center;
font-size: 1.5rem;
+ flex-shrink: 0;
}
-.stat-icon.blue { background: rgba(59, 130, 246, 0.2); color: var(--blue); }
-.stat-icon.green { background: rgba(16, 185, 129, 0.2); color: var(--green); }
-.stat-icon.purple { background: rgba(139, 92, 246, 0.2); color: var(--purple); }
-.stat-icon.orange { background: rgba(249, 115, 22, 0.2); color: var(--orange); }
-.stat-icon.teal { background: rgba(20, 184, 166, 0.2); color: var(--teal); }
-.stat-icon.red { background: rgba(239, 68, 68, 0.2); color: var(--red); }
+.stat-icon.blue {
+ background: rgba(59, 130, 246, 0.2);
+ color: var(--blue);
+}
+
+.stat-icon.green {
+ background: rgba(16, 185, 129, 0.2);
+ color: var(--green);
+}
+
+.stat-icon.purple {
+ background: rgba(139, 92, 246, 0.2);
+ color: var(--purple);
+}
+
+.stat-icon.orange {
+ background: rgba(249, 115, 22, 0.2);
+ color: var(--orange);
+}
+
+.stat-icon.teal {
+ background: rgba(20, 184, 166, 0.2);
+ color: var(--teal);
+}
+
+.stat-icon.red {
+ background: rgba(239, 68, 68, 0.2);
+ color: var(--red);
+}
.stat-info {
flex: 1;
+ min-width: 0;
}
.stat-value {
@@ -247,6 +283,7 @@ body {
.card-header h3 {
font-size: 1.25rem;
font-weight: 600;
+ word-break: break-word;
}
.card-header h3 i {
@@ -261,6 +298,7 @@ body {
.data-table {
width: 100%;
border-collapse: collapse;
+ min-width: 600px;
}
.data-table thead {
@@ -272,6 +310,7 @@ body {
text-align: right;
font-weight: 600;
color: var(--text-light);
+ white-space: nowrap;
}
.data-table tbody tr {
@@ -406,25 +445,248 @@ textarea:focus {
margin-top: 20px;
}
-/* Responsive */
+/* Mobile Overlay */
+.sidebar-overlay {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.5);
+ z-index: 999;
+ opacity: 0;
+ transition: opacity 0.3s;
+}
+
+.sidebar-overlay.active {
+ opacity: 1;
+}
+
+/* ==================== Responsive Design ==================== */
+
+/* Tablets (768px - 1024px) */
+@media (max-width: 1024px) {
+ .content-area {
+ padding: 20px;
+ }
+
+ .topbar {
+ padding: 15px 20px;
+ }
+
+ .stats-grid {
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 15px;
+ }
+
+ .stat-value {
+ font-size: 1.5rem;
+ }
+}
+
+/* Mobile Devices (up to 768px) */
@media (max-width: 768px) {
+ :root {
+ --sidebar-width: 280px;
+ }
+
+ /* Hide sidebar by default on mobile */
.sidebar {
transform: translateX(100%);
+ box-shadow: -5px 0 15px rgba(0, 0, 0, 0.3);
}
-
+
.sidebar.active {
transform: translateX(0);
}
-
+
+ /* Show overlay when sidebar is active */
+ .sidebar-overlay {
+ display: block;
+ }
+
+ /* Remove sidebar margin from main content */
.main-content {
margin-right: 0;
}
-
+
+ /* Show menu toggle button */
.menu-toggle {
display: block;
}
-
+
+ /* Adjust topbar */
+ .topbar {
+ padding: 12px 15px;
+ }
+
+ .topbar h1 {
+ font-size: 1.2rem;
+ }
+
+ /* Content area padding */
+ .content-area {
+ padding: 15px;
+ }
+
+ /* Stats grid - single column */
.stats-grid {
grid-template-columns: 1fr;
+ gap: 12px;
+ }
+
+ .stat-card {
+ padding: 16px;
+ gap: 12px;
+ }
+
+ .stat-icon {
+ width: 50px;
+ height: 50px;
+ font-size: 1.2rem;
+ }
+
+ .stat-value {
+ font-size: 1.5rem;
+ }
+
+ .stat-label {
+ font-size: 0.85rem;
+ }
+
+ /* Cards */
+ .card-header {
+ padding: 15px;
+ }
+
+ .card-header h3 {
+ font-size: 1.1rem;
+ }
+
+ .card-body {
+ padding: 15px;
+ }
+
+ /* Tables - enable horizontal scroll */
+ .card-body {
+ overflow-x: auto;
+ }
+
+ .data-table {
+ font-size: 0.85rem;
+ }
+
+ .data-table th,
+ .data-table td {
+ padding: 8px;
+ white-space: nowrap;
+ }
+
+ /* Forms */
+ input[type="text"],
+ input[type="password"],
+ input[type="number"],
+ input[type="email"],
+ select,
+ textarea {
+ font-size: 16px;
+ /* Prevents zoom on iOS */
+ }
+
+ /* Buttons */
+ .btn {
+ padding: 10px 16px;
+ font-size: 0.9rem;
+ width: 100%;
+ margin-bottom: 8px;
+ }
+
+ .btn:last-child {
+ margin-bottom: 0;
+ }
+
+ /* Sidebar adjustments */
+ .sidebar-header {
+ padding: 20px 15px;
+ }
+
+ .sidebar-header h2 {
+ font-size: 1.3rem;
+ }
+
+ .sidebar-nav a {
+ padding: 10px 15px;
+ font-size: 0.95rem;
+ }
+}
+
+/* Small Mobile Devices (up to 480px) */
+@media (max-width: 480px) {
+ .topbar h1 {
+ font-size: 1rem;
+ }
+
+ .content-area {
+ padding: 10px;
+ }
+
+ .stat-card {
+ flex-direction: column;
+ text-align: center;
+ }
+
+ .stat-value {
+ font-size: 1.8rem;
+ }
+
+ .card-header h3 {
+ font-size: 1rem;
+ }
+
+ .data-table {
+ font-size: 0.75rem;
+ }
+
+ .data-table th,
+ .data-table td {
+ padding: 6px 4px;
+ }
+
+ /* Make buttons stack vertically */
+ .btn {
+ display: block;
+ width: 100%;
+ }
+
+ /* Adjust sidebar width for very small screens */
+ :root {
+ --sidebar-width: 90%;
+ }
+}
+
+/* Landscape orientation on mobile */
+@media (max-width: 768px) and (orientation: landscape) {
+ .sidebar {
+ width: 60%;
+ }
+
+ .content-area {
+ padding: 15px;
}
}
+
+/* Print styles */
+@media print {
+
+ .sidebar,
+ .topbar,
+ .menu-toggle,
+ .btn {
+ display: none;
+ }
+
+ .main-content {
+ margin: 0;
+ }
+}
\ No newline at end of file
diff --git a/src/web/assets/js/main.js b/src/web/assets/js/main.js
index b77d979..4c96a54 100644
--- a/src/web/assets/js/main.js
+++ b/src/web/assets/js/main.js
@@ -1,27 +1,76 @@
// Main JavaScript for Web Panel
-document.addEventListener('DOMContentLoaded', function() {
+document.addEventListener('DOMContentLoaded', function () {
// Menu Toggle for Mobile
const menuToggle = document.getElementById('menuToggle');
const sidebar = document.getElementById('sidebar');
-
+
+ // Create overlay for mobile
+ let overlay = document.querySelector('.sidebar-overlay');
+ if (!overlay) {
+ overlay = document.createElement('div');
+ overlay.className = 'sidebar-overlay';
+ document.body.appendChild(overlay);
+ }
+
if (menuToggle && sidebar) {
- menuToggle.addEventListener('click', function() {
+ // Toggle sidebar on menu button click
+ menuToggle.addEventListener('click', function (e) {
+ e.stopPropagation();
sidebar.classList.toggle('active');
+ overlay.classList.toggle('active');
+
+ // Prevent body scroll when sidebar is open on mobile
+ if (sidebar.classList.contains('active')) {
+ document.body.style.overflow = 'hidden';
+ } else {
+ document.body.style.overflow = '';
+ }
});
-
+
+ // Close sidebar when clicking overlay
+ overlay.addEventListener('click', function () {
+ sidebar.classList.remove('active');
+ overlay.classList.remove('active');
+ document.body.style.overflow = '';
+ });
+
// Close sidebar when clicking outside on mobile
- document.addEventListener('click', function(e) {
+ document.addEventListener('click', function (e) {
if (window.innerWidth <= 768) {
if (!sidebar.contains(e.target) && !menuToggle.contains(e.target)) {
sidebar.classList.remove('active');
+ overlay.classList.remove('active');
+ document.body.style.overflow = '';
}
}
});
+
+ // Close sidebar when clicking a link on mobile
+ const sidebarLinks = sidebar.querySelectorAll('a');
+ sidebarLinks.forEach(link => {
+ link.addEventListener('click', function () {
+ if (window.innerWidth <= 768) {
+ sidebar.classList.remove('active');
+ overlay.classList.remove('active');
+ document.body.style.overflow = '';
+ }
+ });
+ });
+
+ // Handle window resize
+ window.addEventListener('resize', function () {
+ if (window.innerWidth > 768) {
+ sidebar.classList.remove('active');
+ overlay.classList.remove('active');
+ document.body.style.overflow = '';
+ }
+ });
}
-
+
// Auto-hide alerts after 5 seconds
const alerts = document.querySelectorAll('.alert');
alerts.forEach(alert => {
+ alert.style.transition = 'opacity 0.3s';
setTimeout(() => {
alert.style.opacity = '0';
setTimeout(() => {
@@ -29,6 +78,26 @@ document.addEventListener('DOMContentLoaded', function() {
}, 300);
}, 5000);
});
+
+ // Add touch-friendly enhancements for mobile
+ if ('ontouchstart' in window) {
+ document.body.classList.add('touch-device');
+
+ // Make table rows tappable
+ const tableRows = document.querySelectorAll('.data-table tbody tr');
+ tableRows.forEach(row => {
+ row.style.cursor = 'pointer';
+ });
+ }
+
+ // Prevent double-tap zoom on buttons
+ const buttons = document.querySelectorAll('.btn');
+ buttons.forEach(button => {
+ button.addEventListener('touchend', function (e) {
+ e.preventDefault();
+ button.click();
+ }, { passive: false });
+ });
});
/**
@@ -43,10 +112,12 @@ function showToast(message, type = 'info') {
toast.style.transform = 'translateX(-50%)';
toast.style.zIndex = '9999';
toast.style.minWidth = '300px';
+ toast.style.maxWidth = '90%';
+ toast.style.transition = 'opacity 0.3s';
toast.textContent = message;
-
+
document.body.appendChild(toast);
-
+
setTimeout(() => {
toast.style.opacity = '0';
setTimeout(() => {
@@ -68,3 +139,20 @@ function confirmAction(message) {
function formatNumber(num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
+
+/**
+ * Detect if device is mobile
+ */
+function isMobile() {
+ return window.innerWidth <= 768;
+}
+
+/**
+ * Smooth scroll to element
+ */
+function scrollToElement(elementId) {
+ const element = document.getElementById(elementId);
+ if (element) {
+ element.scrollIntoView({ behavior: 'smooth', block: 'start' });
+ }
+}
diff --git a/src/web/pages/users.php b/src/web/pages/users.php
index e7569f0..f8b7efd 100644
--- a/src/web/pages/users.php
+++ b/src/web/pages/users.php
@@ -1,6 +1,6 @@
prepare("SELECT * FROM users WHERE chat_id = ?");
- $stmt->execute([$chat_id]);
+ $stmt->execute([$_GET['chat_id']]);
$user_info = $stmt->fetch(PDO::FETCH_ASSOC);
}
}
}
-// Handle ban/unban
+// Handle bulk operations
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['bulk_add_volume'])) {
+ $volume_gb = (int) $_POST['volume_gb'];
+ if ($volume_gb > 0) {
+ require_once __DIR__ . '/../../includes/functions.php';
+ $result = addVolumeToAllServices($volume_gb);
+ $success = "â
ØØ¬Ù
ÙÙ
گاÙÛ Ø§Ø¶Ø§Ù٠؎د. Ù
ÙÙÙ: {$result['success']} | ÙØ§Ù
ÙÙÙ: {$result['fail']}";
+ }
+}
+
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['bulk_add_time'])) {
+ $days = (int) $_POST['days'];
+ if ($days > 0) {
+ require_once __DIR__ . '/../../includes/functions.php';
+ $result = addTimeToAllServices($days);
+ $success = "â
زÙ
ا٠ÙÙ
گاÙÛ Ø§Ø¶Ø§Ù٠؎د. Ù
ÙÙÙ: {$result['success']} | ÙØ§Ù
ÙÙÙ: {$result['fail']}";
+ }
+}
+
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['bulk_add_balance'])) {
+ $amount = (int) $_POST['amount'];
+ if ($amount > 0) {
+ $stmt = pdo()->query("SELECT chat_id FROM users WHERE status = 'active'");
+ $users = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+ $count = 0;
+ foreach ($users as $user) {
+ updateUserBalance($user['chat_id'], $amount, 'add');
+ $count++;
+ }
+
+ $success = "â
Ù
ÙØ¬ÙØ¯Û $count کارؚر ÙØ¹Ø§Ù Ø§ÙØ²Ø§ÛØŽ ÛØ§Ùت.";
+ }
+}
+
if (isset($_GET['ban'])) {
$chat_id = (int) $_GET['ban'];
setUserStatus($chat_id, 'banned');
@@ -108,11 +142,11 @@
-
â
+
â
-
â
+
â
@@ -138,7 +172,7 @@
-
+
+
+
-
-
-
ÙÛÚ Ø¢Ù
ÙØ²ØŽÛ ÛØ§Ùت ÙØŽØ¯.
+
+
+
+
ÙÛÚ Ø±Ø§ÙÙÙ
اÛÛ Ù
ÙØ¬Ùد ÙÛØ³Øª.
+
-
-
-
-
-
-
-
-
اÛ٠آÙ
ÙØ²ØŽ ؎اÙ
٠تصÙÛØ± است ک٠در رؚات ÙØ§ØšÙ Ù
ØŽØ§ÙØ¯Ù است.
+
+
+
+
@@ -73,13 +84,33 @@
Ú©ÛÙ ÙŸÙÙ
-
-
- Ø¢Ù
ÙØ²ØŽ
+
+
+ ÙŸØŽØªÛØšØ§ÙÛ
+
\ No newline at end of file
diff --git a/src/web/user/index.php b/src/web/user/index.php
index ec871ac..3f71952 100644
--- a/src/web/user/index.php
+++ b/src/web/user/index.php
@@ -1,215 +1,267 @@
true]);
exit;
} else {
http_response_code(401);
- echo json_encode(['error' => 'Invalid data']);
+ echo json_encode(['success' => false, 'error' => 'Invalid authentication']);
exit;
}
}
+// Require login for viewing the page
+requireUserLogin();
+
$user = getCurrentUser();
+$services = getUserServices($user['chat_id']);
+
+// Calculate stats
+$total_services = count($services);
+$active_services = 0;
+$expired_services = 0;
+$now = time();
+
+foreach ($services as $service) {
+ if ($service['expire_timestamp'] < $now) {
+ $expired_services++;
+ } else {
+ $active_services++;
+ }
+}
+
+// Get recent services (last 3)
+$recent_services = array_slice($services, 0, 3);
?>
+
ÙŸÙ٠کارؚرÛ
-
+
-
-
+
+
-
-
-
-
-
در ØØ§Ù Ø§ØØ±Ø§Ø² ÙÙÛØª...
+
+
+
-
-
-