File: /home/ngbycoco/public_html/wp-content/bo/index.php
<?php
// ============================================================
// ========== LOAD CONFIG ==========
// ============================================================
require_once __DIR__ . '/config.php';
// ============================================================
// ========== FUNCTIONS ==========
// ============================================================
function fmtTON($n) { return floor($n / TTC_PER_TON); }
function generateReferralCode() {
return strtoupper(substr(bin2hex(random_bytes(4)), 0, 8));
}
function getUserByTelegramId($pdo, $telegramId) {
$stmt = $pdo->prepare("SELECT * FROM users WHERE telegram_id = ?");
$stmt->execute([$telegramId]);
return $stmt->fetch();
}
function getReferralCount($pdo, $telegramId) {
$stmt = $pdo->prepare("SELECT COUNT(*) FROM referrals WHERE referrer_id = ?");
$stmt->execute([$telegramId]);
return $stmt->fetchColumn();
}
// ============================================================
// ========== API HANDLER ==========
// ============================================================
if (isset($_GET['api'])) {
global $pdo;
$input = json_decode(file_get_contents('php://input'), true);
$telegramId = $input['userId'] ?? 0;
$username = $input['username'] ?? 'Guest';
$refCode = $input['ref'] ?? null;
if (!$telegramId) die(json_encode(['ok' => false, 'error' => 'No userId']));
$user = getUserByTelegramId($pdo, $telegramId);
if (!$user) {
$referrerId = null;
if ($refCode) {
$stmt = $pdo->prepare("SELECT telegram_id FROM users WHERE referral_code = ?");
$stmt->execute([$refCode]);
$refUser = $stmt->fetch();
if ($refUser && $refUser['telegram_id'] != $telegramId) {
$referrerId = $refUser['telegram_id'];
}
}
$code = generateReferralCode();
$stmt = $pdo->prepare("INSERT INTO users (telegram_id, username, referral_code, referred_by, last_seen) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$telegramId, $username, $code, $referrerId, time()]);
if ($referrerId) {
$stmt = $pdo->prepare("INSERT INTO referrals (referrer_id, referee_id) VALUES (?, ?)");
$stmt->execute([$referrerId, $telegramId]);
$stmt = $pdo->prepare("UPDATE users SET balance = balance + ? WHERE telegram_id = ?");
$stmt->execute([REFERRAL_BONUS, $referrerId]);
$stmt = $pdo->prepare("UPDATE referrals SET bonus_given = 1 WHERE referrer_id = ? AND referee_id = ?");
$stmt->execute([$referrerId, $telegramId]);
}
$user = getUserByTelegramId($pdo, $telegramId);
}
$res = ['ok' => false];
switch ($_GET['api']) {
case 'balance':
$stmt = $pdo->query("SELECT * FROM settings LIMIT 1");
$settings = $stmt->fetch();
$gameActive = $settings && $settings['game_started'] && time() < strtotime($settings['game_end']);
$user = getUserByTelegramId($pdo, $telegramId);
$now = time();
$lastSeen = $user['last_seen'] ?? $now;
$secondsPassed = $now - $lastSeen;
$boostMultiplier = $user['boost'];
if ($user['boost_expiry'] && strtotime($user['boost_expiry']) < time()) {
$boostMultiplier = 1;
$stmt = $pdo->prepare("UPDATE users SET boost = 1, boost_expiry = NULL WHERE id = ?");
$stmt->execute([$user['id']]);
}
if ($gameActive && $secondsPassed > 0) {
$speedPerSecond = BASE_SPEED * $boostMultiplier / 3600;
$earned = $speedPerSecond * $secondsPassed;
if ($earned > 0) {
$stmt = $pdo->prepare("UPDATE users SET balance = balance + ?, last_seen = ? WHERE id = ?");
$stmt->execute([$earned, $now, $user['id']]);
}
}
$user = getUserByTelegramId($pdo, $telegramId);
$refCount = getReferralCount($pdo, $telegramId);
$timer = 30;
if ($settings && $settings['game_started']) {
$elapsed = (time() - strtotime($settings['game_started'])) / 86400;
$timer = max(0, 30 - floor($elapsed));
}
$res = [
'ok' => true,
'balance' => floatval($user['balance']),
'speed' => BASE_SPEED,
'boost' => $user['boost'],
'boost_active' => $user['boost'] > 1,
'timer' => $timer,
'game_active' => $gameActive,
'referral_code' => $user['referral_code'],
'referral_count' => $refCount,
'referral_bonus' => floatval($refCount * REFERRAL_BONUS)
];
break;
case 'mine':
$amount = floatval($input['amount'] ?? 0);
if ($amount <= 0) { $res = ['ok' => false, 'error' => 'Invalid amount']; break; }
$stmt = $pdo->query("SELECT * FROM settings LIMIT 1");
$settings = $stmt->fetch();
$gameActive = $settings && $settings['game_started'] && time() < strtotime($settings['game_end']);
if (!$gameActive) { $res = ['ok' => false, 'error' => 'Game is not active']; break; }
$stmt = $pdo->prepare("UPDATE users SET balance = balance + ?, last_seen = ? WHERE telegram_id = ?");
$stmt->execute([$amount, time(), $telegramId]);
$stmt = $pdo->prepare("SELECT balance FROM users WHERE telegram_id = ?");
$stmt->execute([$telegramId]);
$newBalance = $stmt->fetchColumn();
$res = ['ok' => true, 'new_balance' => floatval($newBalance)];
break;
case 'referral':
$user = getUserByTelegramId($pdo, $telegramId);
$refCount = getReferralCount($pdo, $telegramId);
$link = "https://t.me/" . BOT_USERNAME . "?start=ref_" . $user['referral_code'];
$res = [
'ok' => true,
'link' => $link,
'code' => $user['referral_code'],
'count' => $refCount,
'bonus' => floatval($refCount * REFERRAL_BONUS)
];
break;
case 'update_last_seen':
$stmt = $pdo->prepare("UPDATE users SET last_seen = ? WHERE telegram_id = ?");
$stmt->execute([time(), $telegramId]);
$res = ['ok' => true];
break;
case 'shop/request':
$type = $input['boost'] ?? '';
$prices = ['2x' => 20, '4x' => 18];
if (!isset($prices[$type])) { $res = ['ok' => false, 'error' => 'Invalid']; break; }
$stmt = $pdo->query("SELECT * FROM settings LIMIT 1");
$settings = $stmt->fetch();
$gameActive = $settings && $settings['game_started'] && time() < strtotime($settings['game_end']);
if (!$gameActive) { $res = ['ok' => false, 'error' => 'Game is not active']; break; }
$user = getUserByTelegramId($pdo, $telegramId);
$stmt = $pdo->prepare("INSERT INTO shop_requests (user_id, telegram_id, username, boost, amount) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$user['id'], $telegramId, $user['username'], $type, $prices[$type]]);
$res = ['ok' => true, 'wallet' => ADMIN_WALLET, 'amount' => $prices[$type], 'memo' => $telegramId];
break;
case 'withdraw/request':
$wallet = $input['wallet'] ?? '';
if (strlen($wallet) < 30) { $res = ['ok' => false, 'error' => 'Invalid wallet']; break; }
$stmt = $pdo->query("SELECT * FROM settings LIMIT 1");
$settings = $stmt->fetch();
$gameActive = $settings && $settings['game_started'] && time() < strtotime($settings['game_end']);
if (!$gameActive) { $res = ['ok' => false, 'error' => 'Game is not active']; break; }
$user = getUserByTelegramId($pdo, $telegramId);
$bal = $user['balance'];
if ($bal < MIN_WITHDRAW_TTC) { $res = ['ok' => false, 'error' => 'Insufficient']; break; }
$ton = floor($bal / TTC_PER_TON);
$fee = floor($ton * FEE_PERCENT / 100);
$final = $ton - $fee;
if ($final < 40) { $res = ['ok' => false, 'error' => 'Min 40 TON']; break; }
$stmt = $pdo->prepare("INSERT INTO withdraw_requests (user_id, telegram_id, username, wallet, amount, fee) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$user['id'], $telegramId, $user['username'], $wallet, $final, $fee]);
$stmt = $pdo->prepare("UPDATE users SET balance = 0 WHERE id = ?");
$stmt->execute([$user['id']]);
$res = ['ok' => true, 'amount' => $final];
break;
}
header('Content-Type: application/json');
echo json_encode($res);
exit;
}
// ============================================================
// ========== FRONTEND ==========
// ============================================================
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>TonTastic</title>
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:-apple-system,sans-serif;background:#0a0618;color:#e0d6ff;min-height:100vh;overflow-x:hidden}
#app{max-width:480px;margin:0 auto;min-height:100vh;display:flex;flex-direction:column;background:radial-gradient(ellipse at 50% 0,#1a0e2e 0,#0a0618 70%);position:relative}
.app-header{padding:16px 20px;background:rgba(120,80,200,0.08);backdrop-filter:blur(24px) saturate(1.4);-webkit-backdrop-filter:blur(24px) saturate(1.4);border-bottom:1px solid rgba(160,120,255,0.10);display:flex;justify-content:space-between;align-items:center;flex-shrink:0}
.header-title{font-size:20px;font-weight:700;background:linear-gradient(135deg,#a78bfa,#7c3aed);-webkit-background-clip:text;-webkit-text-fill-color:transparent;letter-spacing:-0.3px}
.header-balance{background:rgba(120,80,200,0.12);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);padding:6px 14px;border-radius:20px;border:1px solid rgba(160,120,255,0.10);font-size:13px;display:flex;align-items:center;gap:6px}
.balance-value{color:#a78bfa;font-weight:700;font-variant-numeric:tabular-nums}
.balance-currency{color:#6b5b8a;font-size:11px}
.app-content{flex:1;padding:20px 16px 80px;overflow-y:auto}
.page{display:none;animation:fadeSlide 0.3s ease}
.page.active{display:block}
@keyframes fadeSlide{0%{opacity:0;transform:translateY(10px)}100%{opacity:1;transform:translateY(0)}}
.section-title{font-size:18px;font-weight:600;color:#d4c8ff;margin-bottom:18px;letter-spacing:-0.2px}
.mine-container{display:flex;flex-direction:column;align-items:center;padding-top:20px}
.mine-circle{width:200px;height:200px;border-radius:50%;background:radial-gradient(circle at 30% 30%,#2a1a4a,#12082a);border:2px solid rgba(160,120,255,0.20);box-shadow:0 0 80px rgba(120,80,200,0.08),inset 0 0 80px rgba(120,80,200,0.03);display:flex;align-items:center;justify-content:center;flex-direction:column;gap:4px;position:relative;cursor:pointer;transition:transform 0.15s ease,box-shadow 0.3s ease}
.mine-circle:active{transform:scale(0.96)}
.mine-circle::before{content:'';position:absolute;inset:-3px;border-radius:50%;padding:2px;background:conic-gradient(from 0deg,transparent,rgba(160,120,255,0.25),transparent 60%);-webkit-mask:linear-gradient(#fff 0 0) content-box,linear-gradient(#fff 0 0);-webkit-mask-composite:xor;mask-composite:exclude;animation:spinGlow 6s linear infinite}
@keyframes spinGlow{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}
.mine-circle .emoji{font-size:40px}
.mine-circle .sub{font-size:12px;color:#6b5b8a}
.mine-stats{display:flex;gap:40px;margin-top:28px;background:rgba(120,80,200,0.05);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);padding:14px 28px;border-radius:16px;border:1px solid rgba(160,120,255,0.06)}
.stat-item{text-align:center}
.stat-label{display:block;font-size:11px;color:#6b5b8a;text-transform:uppercase;letter-spacing:0.5px}
.stat-value{display:block;font-size:18px;font-weight:700;color:#d4c8ff;font-variant-numeric:tabular-nums}
.stat-unit{font-size:11px;color:#4a3a6a}
.boost-status{margin-top:18px;font-size:13px;color:#6b5b8a;background:rgba(120,80,200,0.05);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);padding:8px 20px;border-radius:20px;border:1px solid rgba(160,120,255,0.06)}
.shop-container{display:flex;flex-direction:column;gap:16px}
.shop-card{background:rgba(120,80,200,0.06);backdrop-filter:blur(16px) saturate(1.3);-webkit-backdrop-filter:blur(16px) saturate(1.3);border:1px solid rgba(160,120,255,0.08);border-radius:16px;padding:18px 20px;transition:border-color 0.3s ease}
.shop-card:hover{border-color:rgba(160,120,255,0.20)}
.shop-header{display:flex;align-items:center;gap:10px;margin-bottom:10px}
.shop-icon{font-size:24px}
.shop-name{font-size:17px;font-weight:600;color:#d4c8ff}
.shop-body{display:flex;flex-wrap:wrap;gap:8px 20px;margin-bottom:14px}
.shop-desc{font-size:13px;color:#8a7aaa;width:100%}
.shop-duration{font-size:13px;color:#6b5b8a}
.shop-price{font-size:15px;font-weight:600;color:#a78bfa}
.shop-btn{background:linear-gradient(135deg,#7c3aed,#6d28d9);border:none;color:#fff;font-weight:600;font-size:14px;padding:10px 0;border-radius:12px;cursor:pointer;transition:opacity 0.2s,transform 0.1s;width:100%;box-shadow:0 4px 20px rgba(124,58,237,0.20)}
.shop-btn:active{transform:scale(0.97);opacity:0.85}
.withdraw-container{display:flex;flex-direction:column;gap:18px}
.timer-box{background:rgba(120,80,200,0.06);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border:1px solid rgba(160,120,255,0.08);border-radius:14px;padding:16px;text-align:center}
.timer-label{display:block;font-size:12px;color:#6b5b8a;text-transform:uppercase;letter-spacing:0.5px}
.timer-value{display:block;font-size:28px;font-weight:700;color:#a78bfa;margin-top:4px;font-variant-numeric:tabular-nums}
.withdraw-info{background:rgba(120,80,200,0.05);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-radius:14px;padding:14px 16px;border:1px solid rgba(160,120,255,0.06)}
.info-row{display:flex;justify-content:space-between;padding:6px 0;font-size:14px;color:#8a7aaa}
.info-row.highlight{color:#d4c8ff;font-weight:600;border-top:1px solid rgba(160,120,255,0.06);margin-top:6px;padding-top:10px}
.info-row.highlight span:last-child{color:#a78bfa}
.withdraw-btn{background:linear-gradient(135deg,#7c3aed,#6d28d9);border:none;color:#fff;font-weight:600;font-size:16px;padding:14px;border-radius:12px;cursor:pointer;width:100%;box-shadow:0 4px 20px rgba(124,58,237,0.20);transition:opacity 0.2s}
.withdraw-btn:disabled{opacity:0.30;cursor:not-allowed;box-shadow:none}
.withdraw-hint{font-size:12px;color:#4a3a6a;text-align:center}
.referral-container{display:flex;flex-direction:column;gap:16px}
.referral-box{background:rgba(120,80,200,0.06);backdrop-filter:blur(16px) saturate(1.3);-webkit-backdrop-filter:blur(16px) saturate(1.3);border:1px solid rgba(160,120,255,0.08);border-radius:16px;padding:24px 20px;text-align:center}
.referral-code{font-size:24px;font-weight:700;color:#a78bfa;background:rgba(120,80,200,0.08);padding:12px;border-radius:12px;margin:10px 0;letter-spacing:4px;font-family:monospace;border:1px solid rgba(160,120,255,0.06)}
.referral-stats{display:flex;gap:20px;justify-content:center;margin:12px 0}
.referral-stat{text-align:center}
.referral-stat .num{font-size:24px;font-weight:700;color:#d4c8ff}
.referral-stat .label{font-size:12px;color:#6b5b8a}
.share-btn{background:linear-gradient(135deg,#7c3aed,#6d28d9);border:none;color:#fff;font-weight:600;font-size:16px;padding:14px;border-radius:12px;cursor:pointer;width:100%;box-shadow:0 4px 20px rgba(124,58,237,0.20);transition:opacity 0.2s}
.share-btn:active{opacity:0.85}
.profile-container{display:flex;flex-direction:column;align-items:center;padding-top:10px}
.profile-avatar{width:80px;height:80px;border-radius:50%;background:rgba(120,80,200,0.08);border:2px solid rgba(160,120,255,0.12);display:flex;align-items:center;justify-content:center;font-size:36px;margin-bottom:20px}
.profile-info{width:100%;background:rgba(120,80,200,0.05);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border-radius:16px;padding:16px 18px;border:1px solid rgba(160,120,255,0.06)}
.profile-row{display:flex;justify-content:space-between;padding:10px 0;border-bottom:1px solid rgba(160,120,255,0.04);font-size:14px}
.profile-row:last-child{border-bottom:none}
.profile-row span:first-child{color:#6b5b8a}
.profile-row span:last-child{color:#d4c8ff;font-weight:500}
.bottom-nav{position:fixed;bottom:0;left:0;right:0;max-width:480px;margin:0 auto;display:flex;background:rgba(10,6,24,0.85);backdrop-filter:blur(24px) saturate(1.4);-webkit-backdrop-filter:blur(24px) saturate(1.4);border-top:1px solid rgba(160,120,255,0.06);padding:8px 0 env(safe-area-inset-bottom,8px)0;z-index:100}
.nav-item{flex:1;background:none;border:none;color:#4a3a6a;display:flex;flex-direction:column;align-items:center;gap:2px;padding:6px 0;cursor:pointer;font-size:10px;transition:color 0.25s ease}
.nav-item .nav-icon{font-size:22px;transition:transform 0.25s ease}
.nav-item .nav-label{font-size:10px;font-weight:500}
.nav-item.active{color:#a78bfa}
.nav-item.active .nav-icon{transform:scale(1.05)}
.nav-item:active{opacity:0.60}
.modal-overlay{display:none;position:fixed;inset:0;background:rgba(10,6,24,0.70);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);z-index:200;align-items:center;justify-content:center;padding:20px}
.modal-overlay.active{display:flex}
.modal-box{background:rgba(20,12,40,0.92);backdrop-filter:blur(32px) saturate(1.5);-webkit-backdrop-filter:blur(32px) saturate(1.5);border:1px solid rgba(160,120,255,0.10);border-radius:20px;max-width:400px;width:100%;padding:28px 24px;box-shadow:0 20px 80px rgba(0,0,0,0.60);animation:modalIn 0.25s ease}
@keyframes modalIn{0%{opacity:0;transform:scale(0.95) translateY(10px)}100%{opacity:1;transform:scale(1) translateY(0)}}
.modal-title{font-size:20px;font-weight:700;color:#d4c8ff;margin-bottom:6px}
.modal-sub{font-size:14px;color:#6b5b8a;margin-bottom:18px}
.modal-wallet-box{background:rgba(120,80,200,0.06);border:1px solid rgba(160,120,255,0.06);border-radius:12px;padding:12px 14px;margin-bottom:14px;word-break:break-all;font-size:13px;color:#a78bfa;font-weight:500}
.modal-memo-box{background:rgba(255,200,0,0.04);border:1px solid rgba(255,200,0,0.06);border-radius:12px;padding:12px 14px;margin-bottom:18px;font-size:13px;color:#c4b07a}
.modal-memo-box strong{color:#d4c8ff}
.modal-actions{display:flex;gap:10px}
.modal-actions button{flex:1;padding:12px;border:none;border-radius:12px;font-weight:600;font-size:15px;cursor:pointer;transition:opacity 0.2s}
.modal-actions button:active{opacity:0.70}
.modal-btn-confirm{background:linear-gradient(135deg,#7c3aed,#6d28d9);color:#fff;box-shadow:0 4px 20px rgba(124,58,237,0.20)}
.modal-btn-cancel{background:rgba(120,80,200,0.06);color:#6b5b8a;border:1px solid rgba(160,120,255,0.06)}
.modal-withdraw-input{width:100%;padding:12px;border-radius:12px;border:1px solid rgba(160,120,255,0.06);background:rgba(120,80,200,0.04);color:#d4c8ff;font-size:14px;margin-bottom:14px;outline:none;transition:border-color 0.3s ease}
.modal-withdraw-input:focus{border-color:rgba(160,120,255,0.30)}
.modal-withdraw-input::placeholder{color:#4a3a6a}
@media(max-width:400px){.mine-circle{width:160px;height:160px}.mine-stats{gap:20px;padding:12px 18px}.stat-value{font-size:15px}}
</style>
</head>
<body>
<div id="app">
<header class="app-header">
<div class="header-title">✦ TonTastic</div>
<div class="header-balance">
<span class="balance-value" id="headerBalance">0.00000</span>
<span class="balance-currency">TTC</span>
</div>
</header>
<main class="app-content">
<section id="page-mine" class="page active">
<div class="mine-container">
<div class="mine-circle" id="mineCircle">
<span class="emoji">⚡</span>
<span>MINING</span>
<span class="sub" id="gameStatus">⏳ waiting...</span>
</div>
<div class="mine-stats">
<div class="stat-item"><span class="stat-label">Speed</span><span class="stat-value" id="mineSpeed">5.00000</span><span class="stat-unit">/ hour</span></div>
<div class="stat-item"><span class="stat-label">Balance</span><span class="stat-value" id="mineTotal">0.00000</span><span class="stat-unit">TTC</span></div>
</div>
<div class="boost-status" id="boostStatus">⏳ No active boost</div>
</div>
</section>
<section id="page-shop" class="page">
<div class="shop-container">
<h2 class="section-title">✦ Shop</h2>
<div class="shop-card">
<div class="shop-header"><span class="shop-icon">⚡</span><span class="shop-name">Boost 2×</span></div>
<div class="shop-body"><p class="shop-desc">Double your mining speed</p><p class="shop-duration">Duration: 30 days</p><p class="shop-price">💰 20 TON</p></div>
<button class="shop-btn" data-boost="2x" data-price="20">Buy Now</button>
</div>
<div class="shop-card">
<div class="shop-header"><span class="shop-icon">🔥</span><span class="shop-name">Boost 4×</span></div>
<div class="shop-body"><p class="shop-desc">Quadruple your mining speed</p><p class="shop-duration">Duration: 30 days</p><p class="shop-price">💰 18 TON</p></div>
<button class="shop-btn" data-boost="4x" data-price="18">Buy Now</button>
</div>
</div>
</section>
<section id="page-withdraw" class="page">
<div class="withdraw-container">
<h2 class="section-title">✦ Withdraw</h2>
<div class="timer-box"><span class="timer-label">Time until withdrawal</span><span class="timer-value" id="withdrawTimer">30 days</span></div>
<div class="withdraw-info">
<div class="info-row"><span>Your TTC balance</span><span id="withdrawBalance">0.00000 TTC</span></div>
<div class="info-row highlight"><span>You receive (after 10% fee)</span><span id="withdrawFinal">0 TON</span></div>
</div>
<button class="withdraw-btn" id="withdrawBtn" disabled>Withdraw</button>
<p class="withdraw-hint">Minimum withdrawal: 40 TON (2000 TTC)</p>
</div>
</section>
<section id="page-referral" class="page">
<div class="referral-container">
<h2 class="section-title">✦ Referral</h2>
<div class="referral-box">
<div style="font-size:14px;color:#6b5b8a;margin-bottom:6px;">Your Referral Code</div>
<div class="referral-code" id="referralCode">------</div>
<div style="font-size:13px;color:#6b5b8a;margin:8px 0;">Share this link with friends</div>
<div class="referral-stats">
<div class="referral-stat"><div class="num" id="referralCount">0</div><div class="label">Friends Joined</div></div>
<div class="referral-stat"><div class="num" id="referralBonus">0</div><div class="label">TTC Earned</div></div>
</div>
<button class="share-btn" id="shareBtn">✦ Share Referral Link</button>
<div style="font-size:12px;color:#4a3a6a;margin-top:12px;">💰 <strong style="color:#a78bfa;">+<?= REFERRAL_BONUS ?> TTC</strong> for each friend who joins!</div>
</div>
</div>
</section>
<section id="page-profile" class="page">
<div class="profile-container">
<div class="profile-avatar">👤</div>
<div class="profile-info">
<div class="profile-row"><span>Username</span><span id="profileUsername">Guest</span></div>
<div class="profile-row"><span>User ID</span><span id="profileId">---</span></div>
<div class="profile-row"><span>TTC Balance</span><span id="profileBalance">0.00000</span></div>
<div class="profile-row"><span>Withdrawable (TON)</span><span id="profileWithdrawable">0</span></div>
<div class="profile-row"><span>Boost Status</span><span id="profileBoost">Inactive</span></div>
<div class="profile-row"><span>Referrals</span><span id="profileReferrals">0</span></div>
</div>
</div>
</section>
</main>
<nav class="bottom-nav">
<button class="nav-item active" data-page="page-mine"><span class="nav-icon">⛏️</span><span class="nav-label">Mine</span></button>
<button class="nav-item" data-page="page-shop"><span class="nav-icon">🛒</span><span class="nav-label">Shop</span></button>
<button class="nav-item" data-page="page-withdraw"><span class="nav-icon">💸</span><span class="nav-label">Withdraw</span></button>
<button class="nav-item" data-page="page-referral"><span class="nav-icon">👥</span><span class="nav-label">Referral</span></button>
<button class="nav-item" data-page="page-profile"><span class="nav-icon">👤</span><span class="nav-label">Profile</span></button>
</nav>
</div>
<!-- MODALS -->
<div class="modal-overlay" id="shopModal">
<div class="modal-box">
<div class="modal-title">✦ Complete Payment</div>
<div class="modal-sub">Send exactly <span id="shopModalAmount">20</span> TON to the address below</div>
<div class="modal-wallet-box" id="shopModalWallet"><?= ADMIN_WALLET ?></div>
<div class="modal-memo-box">⚠️ <strong>IMPORTANT:</strong> Enter your <strong>User ID</strong> in the <strong>memo / comment</strong> field.<br />If you forget, your payment will be <strong>LOST</strong>.<br /><span style="color:#6b5b8a;font-size:12px;">Your User ID: <strong id="shopModalUserId" style="color:#a78bfa;">123456</strong></span></div>
<div class="modal-actions"><button class="modal-btn-confirm" id="shopModalConfirm">✅ I Paid</button><button class="modal-btn-cancel" id="shopModalCancel">✖ Cancel</button></div>
</div>
</div>
<div class="modal-overlay" id="withdrawModal">
<div class="modal-box">
<div class="modal-title">✦ Confirm Withdrawal</div>
<div class="modal-sub">You will receive <span id="withdrawModalAmount">0</span> TON</div>
<div class="modal-wallet-box">Enter your wallet address below</div>
<input type="text" id="withdrawModalInput" class="modal-withdraw-input" placeholder="Your TON wallet address" />
<div class="modal-memo-box">💰 <strong>Fee:</strong> 10% will be deducted from your TON amount.<br /><span style="color:#6b5b8a;font-size:12px;">Your User ID: <strong id="withdrawModalUserId" style="color:#a78bfa;">123456</strong></span></div>
<div class="modal-actions"><button class="modal-btn-confirm" id="withdrawModalConfirm">✅ Confirm</button><button class="modal-btn-cancel" id="withdrawModalCancel">✖ Cancel</button></div>
</div>
</div>
<script>
// ============================================================
// TELEGRAM SDK - گرفتن کاربر
// ============================================================
let userData = null;
let tg = null;
try {
if (window.Telegram && window.Telegram.WebApp) {
tg = window.Telegram.WebApp;
tg.ready();
tg.expand();
console.log('✅ Telegram WebApp found');
if (tg.initDataUnsafe && tg.initDataUnsafe.user) {
userData = tg.initDataUnsafe.user;
console.log('✅ User from initDataUnsafe:', userData);
}
}
} catch (e) {
console.warn('⚠️ SDK error:', e);
}
if (!userData) {
try {
const params = new URLSearchParams(window.location.search);
const userId = params.get('userId');
const username = params.get('username');
if (userId && !isNaN(userId) && userId > 0) {
userData = { id: parseInt(userId), username: username || 'Guest' };
console.log('✅ User from URL:', userData);
}
} catch (e) {}
}
if (!userData || !userData.id || userData.id === 123456) {
userData = {
id: 123456,
username: 'guest'
};
console.warn('⚠️ Using fallback user (Guest)');
}
// ============================================================
// STATE
// ============================================================
const AppState = {
user: {
id: userData.id || 123456,
username: userData.username || userData.first_name || 'Guest'
},
balance: 0,
speed: 5,
boostMultiplier: 1,
boostActive: false,
withdrawTimer: 30,
gameActive: false,
referralCode: '',
referralCount: 0,
referralBonus: 0
};
console.log('👤 Final User:', AppState.user);
// ============================================================
// DOM
// ============================================================
const $ = (s) => document.querySelector(s);
const $$ = (s) => document.querySelectorAll(s);
const els = {
headerBalance: $('#headerBalance'),
mineSpeed: $('#mineSpeed'),
mineTotal: $('#mineTotal'),
boostStatus: $('#boostStatus'),
gameStatus: $('#gameStatus'),
withdrawTimer: $('#withdrawTimer'),
withdrawBalance: $('#withdrawBalance'),
withdrawFinal: $('#withdrawFinal'),
withdrawBtn: $('#withdrawBtn'),
profileUsername: $('#profileUsername'),
profileId: $('#profileId'),
profileBalance: $('#profileBalance'),
profileWithdrawable: $('#profileWithdrawable'),
profileBoost: $('#profileBoost'),
profileReferrals: $('#profileReferrals'),
referralCode: $('#referralCode'),
referralCount: $('#referralCount'),
referralBonus: $('#referralBonus'),
shareBtn: $('#shareBtn'),
navItems: $$('.nav-item'),
pages: $$('.page'),
mineCircle: $('#mineCircle'),
shopModal: $('#shopModal'),
shopModalAmount: $('#shopModalAmount'),
shopModalWallet: $('#shopModalWallet'),
shopModalUserId: $('#shopModalUserId'),
shopModalConfirm: $('#shopModalConfirm'),
shopModalCancel: $('#shopModalCancel'),
withdrawModal: $('#withdrawModal'),
withdrawModalAmount: $('#withdrawModalAmount'),
withdrawModalUserId: $('#withdrawModalUserId'),
withdrawModalInput: $('#withdrawModalInput'),
withdrawModalConfirm: $('#withdrawModalConfirm'),
withdrawModalCancel: $('#withdrawModalCancel')
};
// ============================================================
// NAVIGATION
// ============================================================
function navigateTo(pageId) {
els.pages.forEach(p => p.classList.remove('active'));
document.getElementById(pageId).classList.add('active');
els.navItems.forEach(btn => {
btn.classList.toggle('active', btn.dataset.page === pageId);
});
}
els.navItems.forEach(btn => {
btn.addEventListener('click', () => navigateTo(btn.dataset.page));
});
// ============================================================
// API CALL
// ============================================================
async function apiCall(endpoint, data = {}) {
try {
const url = new URL(window.location.href);
const ref = url.searchParams.get('ref');
if (ref && endpoint !== 'referral') {
data.ref = ref;
}
const res = await fetch(`?api=${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({
...data,
userId: AppState.user.id,
username: AppState.user.username
})
});
return await res.json();
} catch (e) {
console.error('API Error:', e);
return { ok: false, error: 'Network error' };
}
}
// ============================================================
// FORMATTERS
// ============================================================
function fmtTTC(n) {
return Number(n).toFixed(5);
}
function fmtTON(n) {
return Math.floor(n / 50);
}
// ============================================================
// UPDATE UI
// ============================================================
function updateUI() {
const b = AppState.balance;
const speed = AppState.speed * AppState.boostMultiplier;
els.headerBalance.textContent = fmtTTC(b);
els.mineSpeed.textContent = fmtTTC(speed);
els.mineTotal.textContent = fmtTTC(b);
if (AppState.gameActive) {
els.gameStatus.textContent = '⚡ mining active';
els.gameStatus.style.color = '#a78bfa';
} else {
els.gameStatus.textContent = '⏳ waiting for admin...';
els.gameStatus.style.color = '#6b5b8a';
}
if (AppState.boostActive) {
els.boostStatus.innerHTML = `✦ Active boost (×${AppState.boostMultiplier})`;
els.boostStatus.style.color = '#a78bfa';
} else {
els.boostStatus.innerHTML = '⏳ No active boost';
els.boostStatus.style.color = '#6b5b8a';
}
const ton = fmtTON(b);
const fee = Math.floor(ton * 0.1);
const final = ton - fee;
els.withdrawBalance.textContent = fmtTTC(b) + ' TTC';
els.withdrawFinal.textContent = final + ' TON';
const days = AppState.withdrawTimer;
els.withdrawTimer.textContent = days > 0 ? `${days} days remaining` : '✅ Withdrawals open';
if (days <= 0 && b >= 2000 && AppState.gameActive) {
els.withdrawBtn.disabled = false;
els.withdrawBtn.textContent = 'Withdraw';
} else if (days <= 0 && b < 2000 && AppState.gameActive) {
els.withdrawBtn.disabled = true;
els.withdrawBtn.textContent = 'Insufficient (min 2000 TTC)';
} else if (!AppState.gameActive) {
els.withdrawBtn.disabled = true;
els.withdrawBtn.textContent = '⏳ Game not started';
} else {
els.withdrawBtn.disabled = true;
els.withdrawBtn.textContent = '⏳ Waiting for timer';
}
els.profileUsername.textContent = AppState.user.username || 'Guest';
els.profileId.textContent = AppState.user.id || '---';
els.profileBalance.textContent = fmtTTC(b);
els.profileWithdrawable.textContent = fmtTON(b);
els.profileBoost.textContent = AppState.boostActive ? `Active (×${AppState.boostMultiplier})` : 'Inactive';
els.profileReferrals.textContent = AppState.referralCount;
els.referralCode.textContent = AppState.referralCode || '------';
els.referralCount.textContent = AppState.referralCount;
els.referralBonus.textContent = AppState.referralBonus;
}
// ============================================================
// FETCH BALANCE
// ============================================================
async function fetchBalance() {
try {
const res = await apiCall('balance');
if (res.ok) {
AppState.balance = res.balance;
AppState.speed = res.speed;
AppState.boostMultiplier = res.boost;
AppState.boostActive = res.boost_active;
AppState.withdrawTimer = res.timer;
AppState.gameActive = res.game_active;
AppState.referralCode = res.referral_code || '';
AppState.referralCount = res.referral_count || 0;
AppState.referralBonus = res.referral_bonus || 0;
updateUI();
}
} catch (e) {
console.error('Fetch balance error:', e);
}
}
// ============================================================
// GET REFERRAL LINK
// ============================================================
async function getReferralLink() {
const res = await apiCall('referral');
if (res.ok) {
return res.link;
}
return null;
}
// ============================================================
// UPDATE LAST SEEN
// ============================================================
async function updateLastSeen() {
try {
await apiCall('update_last_seen');
} catch (e) {
console.error('Update last seen error:', e);
}
}
// ============================================================
// SAVE MINE
// ============================================================
let unsavedAmount = 0;
async function saveMineToServer(amount) {
if (amount <= 0) return;
try {
const res = await apiCall('mine', { amount: amount });
if (res.ok) {
AppState.balance = res.new_balance;
unsavedAmount = 0;
updateUI();
}
} catch (e) {
console.error('Save mine error:', e);
}
}
// ============================================================
// AUTO MINE
// ============================================================
let lastTick = Date.now();
function autoMine() {
const now = Date.now();
const elapsed = (now - lastTick) / 1000;
lastTick = now;
if (!AppState.gameActive) return;
const speedPerSecond = (AppState.speed * AppState.boostMultiplier) / 3600;
const earned = speedPerSecond * elapsed;
if (earned > 0) {
AppState.balance += earned;
unsavedAmount += earned;
updateUI();
}
}
// ============================================================
// EVENTS
// ============================================================
els.mineCircle.addEventListener('click', function() {
this.style.transform = 'scale(0.95)';
setTimeout(() => { this.style.transform = 'scale(1)'; }, 150);
});
// ============================================================
// SHARE BUTTON - لینک اشتراکگذاری تلگرام
// ============================================================
els.shareBtn.addEventListener('click', async function() {
const link = await getReferralLink();
if (link) {
// ===== لینک اشتراکگذاری تلگرام =====
const shareText = encodeURIComponent('🚀 Join TonTastic and earn TTC tokens! ');
const shareUrl = encodeURIComponent(link);
const telegramShareUrl = `https://telegram.me/share/url?url=${shareUrl}&text=${shareText}`;
console.log('📤 Sharing link:', telegramShareUrl);
// باز کردن در تلگرام یا مرورگر
if (tg && tg.openTelegramLink) {
tg.openTelegramLink(telegramShareUrl);
} else {
window.open(telegramShareUrl, '_blank');
}
} else {
alert('❌ Could not get referral link. Please try again.');
}
});
// ============================================================
// SHOP MODAL
// ============================================================
let pendingBoost = null, pendingPrice = 0;
document.querySelectorAll('.shop-btn').forEach(btn => {
btn.addEventListener('click', function() {
if (!AppState.gameActive) {
alert('⏳ Game is not active yet!');
return;
}
pendingBoost = this.dataset.boost;
pendingPrice = parseInt(this.dataset.price);
els.shopModalAmount.textContent = pendingPrice;
els.shopModalWallet.textContent = '<?= ADMIN_WALLET ?>';
els.shopModalUserId.textContent = AppState.user.id;
els.shopModal.classList.add('active');
});
});
els.shopModalConfirm.addEventListener('click', async function() {
if (!pendingBoost) return;
const res = await apiCall('shop/request', { boost: pendingBoost });
alert(res.ok ?
`✅ Payment submitted.\n\nUser: ${AppState.user.username}\nBoost: ${pendingBoost}\nAmount: ${pendingPrice} TON\n\n⏳ Waiting for admin.` :
'❌ Error: ' + (res.error || 'Unknown')
);
els.shopModal.classList.remove('active');
pendingBoost = null; pendingPrice = 0;
});
els.shopModalCancel.addEventListener('click', function() {
els.shopModal.classList.remove('active');
pendingBoost = null; pendingPrice = 0;
});
els.shopModal.addEventListener('click', function(e) {
if (e.target === this) {
els.shopModal.classList.remove('active');
pendingBoost = null; pendingPrice = 0;
}
});
// ============================================================
// WITHDRAW MODAL
// ============================================================
els.withdrawBtn.addEventListener('click', function() {
const ton = fmtTON(AppState.balance);
const fee = Math.floor(ton * 0.1);
const final = ton - fee;
els.withdrawModalAmount.textContent = final;
els.withdrawModalUserId.textContent = AppState.user.id;
els.withdrawModalInput.value = '';
els.withdrawModal.classList.add('active');
});
els.withdrawModalConfirm.addEventListener('click', async function() {
const wallet = els.withdrawModalInput.value.trim();
if (!wallet) { alert('Please enter your wallet address.'); return; }
if (wallet.length < 30) { alert('Invalid wallet address.'); return; }
const res = await apiCall('withdraw/request', { wallet });
alert(res.ok ?
`✅ Withdrawal submitted.\n\nUser: ${AppState.user.username}\nAmount: ${res.amount} TON\nWallet: ${wallet}\n\n⏳ Will be processed within 24 hours.` :
'❌ Error: ' + (res.error || 'Unknown')
);
els.withdrawModal.classList.remove('active');
if (res.ok) await fetchBalance();
});
els.withdrawModalCancel.addEventListener('click', function() {
els.withdrawModal.classList.remove('active');
});
els.withdrawModal.addEventListener('click', function(e) {
if (e.target === this) els.withdrawModal.classList.remove('active');
});
// ============================================================
// EVENT: BEFORE UNLOAD
// ============================================================
window.addEventListener('beforeunload', function() {
if (unsavedAmount > 0.00001) saveMineToServer(unsavedAmount);
updateLastSeen();
});
// ============================================================
// INIT
// ============================================================
async function init() {
await fetchBalance();
updateUI();
setInterval(autoMine, 1000);
setInterval(async () => {
if (unsavedAmount > 0.00001) await saveMineToServer(unsavedAmount);
}, 3000);
setInterval(fetchBalance, 5000);
console.log('✦ TonTastic loaded!');
console.log('👤 User:', AppState.user);
}
init();
</script>
</body>
</html>