Football Career Simulator

Telegram Bot • Cloudflare Workers

Не настроен
Активные сессии 🎮
0
макс. параллельных
Всего игроков 👥
0
зарегистрировано
Текущий сезон 📅
из 15-20
Следующая генерация
минут

📰 Последние события

Нет активных событий. Запустите бота для начала.

🏅 Рейтинг игроков

Рейтинг будет доступен после начала карьеры.
\\'; } // ==================== MAIN WORKER EXPORT ==================== export default { async fetch(request, env, ctx) { const url = new URL(request.url); const KV = env.GAME_KV; globalThis.BOT_TOKEN = env.BOT_TOKEN || ''; // Serve admin panel if (request.method === 'GET' && (url.pathname === '/' || url.pathname === '')) { return new Response(getAdminHTML(), { headers: { 'Content-Type': 'text/html; charset=utf-8' } }); } // Setup webhook if (url.pathname === '/setup') { const token = url.searchParams.get('token') || env.BOT_TOKEN; const webhookUrl = url.origin + '/webhook'; const res = await fetch('https://api.telegram.org/bot' + token + '/setWebhook?url=' + encodeURIComponent(webhookUrl)); const data = await res.json(); return new Response(JSON.stringify(data, null, 2), { headers: { 'Content-Type': 'application/json' } }); } // API endpoints if (url.pathname === '/api/status') { const state = await getGameState(KV); const players = state.playerOrder.filter(id => !state.players[id].awaitingName).map(id => { const p = state.players[id]; return { name: p.name, club: p.club, overall: p.overall, goals: p.career.totalGoals, retired: p.isRetired }; }); return new Response(JSON.stringify({ status: state.status, playerCount: state.playerOrder.length, maxPlayers: state.maxPlayers, currentSeason: state.currentSeason, generationCount: state.generationCount, players }), { headers: { 'Content-Type': 'application/json' } }); } if (url.pathname === '/api/reset' && request.method === 'POST') { await KV.put('game_state', JSON.stringify(null)); return new Response('{"ok":true}', { headers: { 'Content-Type': 'application/json' } }); } if (url.pathname === '/api/generate' && request.method === 'POST') { ctx.waitUntil(generateNextHalf(KV)); return new Response('{"ok":true,"message":"Generation started"}', { headers: { 'Content-Type': 'application/json' } }); } // Telegram webhook if (url.pathname === '/webhook' && request.method === 'POST') { try { const update = await request.json(); if (update.message && update.message.text) { const chatId = update.message.chat.id; const oderId = update.message.from.id; // User ID const oderName = update.message.from.username || update.message.from.first_name || 'User' + oderId; const text = update.message.text.trim(); const isGroup = update.message.chat.type === 'group' || update.message.chat.type === 'supergroup'; // Убираем @botname из команд в группе const cleanText = text.replace(/@\\w+/g, '').trim(); // Check if user is inputting name (not a command) if (!cleanText.startsWith('/')) { const handled = await handleNameInput(chatId, oderId, oderName, cleanText, KV); if (handled) return new Response('OK'); } // Command handling if (cleanText === '/start' || cleanText.startsWith('/start ')) { await handleStart(chatId, oderId, oderName, KV, isGroup); } else if (cleanText === '/my_stats') { await handleMyStats(chatId, oderId, oderName, KV); } else if (cleanText === '/career_log') { await handleCareerLog(chatId, oderId, oderName, KV); } else if (cleanText === '/hall_of_fame') { await handleHallOfFame(chatId, KV); } else if (cleanText === '/world_news') { await handleWorldNews(chatId, KV); } else if (cleanText.startsWith('/action')) { const action = cleanText.split(' ')[1] || ''; await handleAction(chatId, oderId, oderName, action, KV); } else if (cleanText === '/status') { const state = await getGameState(KV); const targetChat = state.groupChatId || chatId; let statusMsg = '📊 Статус игры\\n\\n'; statusMsg += '🎮 Статус: ' + state.status + '\\n'; statusMsg += '👥 Игроков: ' + state.playerOrder.length + '/' + state.maxPlayers + '\\n'; statusMsg += '📅 Сезон: ' + state.currentSeason + ' (пол. ' + (state.currentHalf + 1) + ')\\n'; statusMsg += '⏱️ Генераций: ' + state.generationCount + '\\n'; if (state.status === 'waiting') { statusMsg += '\\n📝 Для регистрации: /start'; } await sendMessage(targetChat, statusMsg); } else if (cleanText === '/reset') { // Admin reset await KV.put('game_state', JSON.stringify(null)); await sendMessage(chatId, '🔄 Игра сброшена! Используйте /start для новой игры.'); } else if (cleanText === '/force_gen') { // Force generation (admin) await generateNextHalf(KV); } else if (cleanText === '/help') { const state = await getGameState(KV); const targetChat = state.groupChatId || chatId; await sendMessage(targetChat, '⚽ Football Career Simulator\\n\\n' + '📌 Команды:\\n' + '/start — Регистрация в игре\\n' + '/my_stats — Ваша статистика\\n' + '/career_log — История карьеры\\n' + '/hall_of_fame — Зал славы\\n' + '/world_news — Мировые новости\\n' + '/status — Статус игры\\n' + '/action [вариант] — Действие\\n\\n' + '🎯 Действия между сезонами:\\n' + '/action request_transfer\\n' + '/action train_hard\\n' + '/action rest\\n' + '/action media\\n\\n' + '⚙️ Админ:\\n' + '/reset — Сбросить игру\\n' + '/force_gen — Принудительная генерация'); } } } catch (e) { console.error('Webhook error:', e); } return new Response('OK'); } return new Response('Football Career Simulator Bot', { status: 200 }); }, async scheduled(event, env, ctx) { globalThis.BOT_TOKEN = env.BOT_TOKEN || ''; const KV = env.GAME_KV; const state = await getGameState(KV); if (state.status !== 'active') return; const elapsed = Date.now() - state.lastGeneration; const intervalMs = (state.intervalMinutes || 30) * 60 * 1000; if (elapsed >= intervalMs) { await generateNextHalf(KV); } } };`; // Display the worker code document.getElementById('worker-code-display').textContent = WORKER_CODE; // Worker Light code (simplified version) const WORKER_LIGHT = `// ═══════════════════════════════════════════════════════════════════════════ // ⚽ FOOTBALL CAREER SIMULATOR — CLOUDFLARE WORKER // ═══════════════════════════════════════════════════════════════════════════ // DEPLOYMENT: // 1. Cloudflare Dashboard → Workers & Pages → Create Worker // 2. Paste this code // 3. Settings → KV Namespace Bindings → Variable: GAME_KV // 4. Triggers → Cron: */30 * * * * // 5. Open: https://your-worker.workers.dev/setup to set webhook // ═══════════════════════════════════════════════════════════════════════════ const BOT_TOKEN = "8327733648:AAFCtZfAK3-e0fYKG3J1Bo-QjFhFi3Tim3Y"; // ───────────────────────────────────────────────────────────────────────────── // TELEGRAM API // ───────────────────────────────────────────────────────────────────────────── async function sendTelegram(method, params) { const url = \`https://api.telegram.org/bot\${BOT_TOKEN}/\${method}\`; const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(params) }); return res.json(); } async function sendMessage(chatId, text) { return sendTelegram('sendMessage', { chat_id: chatId, text, parse_mode: 'HTML' }); } // ───────────────────────────────────────────────────────────────────────────── // KV STORAGE // ───────────────────────────────────────────────────────────────────────────── async function getState(KV) { const data = await KV.get('game_state', 'json'); return data || { status: 'waiting', groupChatId: null, players: {}, playerOrder: [], playerNames: {}, currentSeason: 0, currentHalf: 0, maxPlayers: 15, minSeasons: 15, maxSeasons: 20, intervalMinutes: 30, lastGeneration: 0, generationCount: 0, worldNews: [] }; } async function saveState(KV, state) { await KV.put('game_state', JSON.stringify(state)); } // ───────────────────────────────────────────────────────────────────────────── // PLAYER DATA // ───────────────────────────────────────────────────────────────────────────── const POSITIONS = [ { name: "GK", fullName: "Goalkeeper", emoji: "🧤" }, { name: "CB", fullName: "Center Back", emoji: "🛡️" }, { name: "CM", fullName: "Midfielder", emoji: "🎯" }, { name: "ST", fullName: "Striker", emoji: "🔥" } ]; const CLUBS = [ { name: "Torpedo Moscow", league: "FNL", flag: "🇷🇺" }, { name: "Barnsley", league: "League One", flag: "🏴󠁧󠁢󠁥󠁮󠁧󠁿" }, { name: "Leganes", league: "Segunda", flag: "🇪🇸" }, { name: "Modena", league: "Serie B", flag: "🇮🇹" } ]; function createPlayer(name) { const pos = POSITIONS[Math.floor(Math.random() * POSITIONS.length)]; const club = CLUBS[Math.floor(Math.random() * CLUBS.length)]; return { name, position: pos, age: 17 + Math.floor(Math.random() * 3), overall: 40 + Math.floor(Math.random() * 18), potential: 65 + Math.floor(Math.random() * 34), club: club.name, league: club.league, countryFlag: club.flag, stats: { form: 60, morale: 70 }, career: { totalGoals: 0, totalAssists: 0, totalApps: 0, trophies: [], awards: [] }, isRetired: false }; } // ───────────────────────────────────────────────────────────────────────────── // REQUEST HANDLER // ───────────────────────────────────────────────────────────────────────────── async function handleRequest(request, env, ctx) { const url = new URL(request.url); const path = url.pathname; if (path === '/setup') { const webhookUrl = url.origin + '/webhook'; const result = await sendTelegram('setWebhook', { url: webhookUrl, drop_pending_updates: true }); return new Response(JSON.stringify(result, null, 2), { headers: { 'Content-Type': 'application/json' } }); } if (path === '/webhook' && request.method === 'POST') { const update = await request.json(); if (update.message?.text) { await processMessage(env, update.message); } return new Response('OK'); } if (path === '/api/status') { const state = await getState(env.GAME_KV); return new Response(JSON.stringify({ status: state.status, players: state.playerOrder.length, season: state.currentSeason })); } if (path === '/api/reset') { await env.GAME_KV.put('game_state', 'null'); return new Response('{"ok":true}'); } if (path === '/api/generate') { ctx.waitUntil(triggerGeneration(env)); return new Response('{"ok":true}'); } return new Response('

⚽ Football Career Bot

Setup Webhook

', { headers: { 'Content-Type': 'text/html' } }); } // ───────────────────────────────────────────────────────────────────────────── // MESSAGE PROCESSOR // ───────────────────────────────────────────────────────────────────────────── async function processMessage(env, msg) { const KV = env.GAME_KV; const state = await getState(KV); const chatId = msg.chat.id; const userId = msg.from.id; const username = msg.from.username || msg.from.first_name || 'User'; const text = msg.text.replace(/@\\w+/g, '').trim(); const isGroup = ['group', 'supergroup'].includes(msg.chat.type); if (isGroup && !state.groupChatId) { state.groupChatId = chatId; await saveState(KV, state); } const target = state.groupChatId || chatId; // Commands if (text === '/start') { if (state.players[userId] && !state.players[userId].awaitingName) { await sendMessage(target, \`✅ @\${username}, вы уже зарегистрированы!\`); return; } state.players[userId] = { awaitingName: true }; state.playerNames[userId] = username; await saveState(KV, state); await sendMessage(target, \`⚽ @\${username}, введите ФИО футболиста:\`); } else if (text === '/my_stats') { const p = state.players[userId]; if (!p || p.awaitingName) { await sendMessage(target, \`❌ @\${username}, сначала /start\`); return; } await sendMessage(target, \`📋 \${p.name}\\n\${p.position.emoji} \${p.position.fullName}\\n📊 \${p.overall} OVR | 🎂 \${p.age} лет\\n🏟️ \${p.club}\\n⚽ Голы: \${p.career.totalGoals}\`); } else if (text === '/status') { await sendMessage(target, \`📊 Статус: \${state.status}\\n👥 Игроков: \${state.playerOrder.length}/\${state.maxPlayers}\\n📅 Сезон: \${state.currentSeason}\`); } else if (text === '/force_gen') { await triggerGeneration(env); } else if (text === '/reset') { await saveState(KV, { status: 'waiting', groupChatId: null, players: {}, playerOrder: [], playerNames: {}, currentSeason: 0, currentHalf: 0, maxPlayers: 15, minSeasons: 15, maxSeasons: 20, intervalMinutes: 30, lastGeneration: 0, generationCount: 0, worldNews: [] }); await sendMessage(target, '🔄 Игра сброшена!'); } else if (text.startsWith('/action')) { const action = text.split(' ')[1]; const p = state.players[userId]; if (!p || p.awaitingName) return; if (action === 'train') { p.overall = Math.min(99, p.overall + Math.floor(Math.random() * 3) + 1); await sendMessage(target, \`💪 @\${username} тренируется! Новый OVR: \${p.overall}\`); } else if (action === 'rest') { p.stats.morale = Math.min(99, p.stats.morale + 10); await sendMessage(target, \`😴 @\${username} отдыхает! Мораль: \${p.stats.morale}\`); } await saveState(KV, state); } else if (!text.startsWith('/') && state.players[userId]?.awaitingName) { const name = text.trim(); if (name.length < 2 || name.length > 50) { await sendMessage(target, \`❌ @\${username}, введите корректное имя (2-50 символов)\`); return; } const profile = createPlayer(name); state.players[userId] = profile; if (!state.playerOrder.includes(userId)) state.playerOrder.push(userId); const remaining = state.maxPlayers - state.playerOrder.length; await sendMessage(target, \`✅ Игрок создан!\\n\\n👤 \${profile.name} (@\${username})\\n\${profile.position.emoji} \${profile.position.fullName}\\n📊 \${profile.overall} OVR\\n🏟️ \${profile.club} (\${profile.league})\\n\\n👥 \${state.playerOrder.length}/\${state.maxPlayers}\\n\${remaining > 0 ? \`⏳ Ждём \${remaining} игроков\` : '🎉 СТАРТ!'}\`); if (remaining <= 0) { state.status = 'active'; state.currentSeason = 1; state.lastGeneration = Date.now(); await sendMessage(target, '🎉🎉🎉 КАРЬЕРА НАЧИНАЕТСЯ! 🎉🎉🎉'); } await saveState(KV, state); } } // ───────────────────────────────────────────────────────────────────────────── // GENERATION ENGINE // ───────────────────────────────────────────────────────────────────────────── async function triggerGeneration(env) { const KV = env.GAME_KV; const state = await getState(KV); if (state.status !== 'active' || !state.groupChatId) return; const chatId = state.groupChatId; const season = state.currentSeason; const isFirst = state.currentHalf === 0; await sendMessage(chatId, \`⚡ Сезон \${season}, \${isFirst ? '1-я' : '2-я'} половина\\n\\n🔄 Генерация...\`); let results = \`📊 РЕЗУЛЬТАТЫ\\n\\n\`; for (const id of state.playerOrder) { const p = state.players[id]; if (!p || p.awaitingName || p.isRetired) continue; const apps = 15 + Math.floor(Math.random() * 8); const isAtt = ['ST','CF','LW','RW'].includes(p.position.name); let goals = 0, assists = 0; for (let i = 0; i < apps; i++) { if (Math.random() < (isAtt ? 0.35 : 0.1)) goals++; if (Math.random() < (isAtt ? 0.2 : 0.15)) assists++; } p.career.totalGoals += goals; p.career.totalAssists += assists; p.career.totalApps += apps; p.age += 0.5; if (p.age <= 27) p.overall = Math.min(p.potential, p.overall + Math.floor(Math.random() * 2)); else if (p.age >= 32) p.overall = Math.max(40, p.overall - Math.floor(Math.random() * 2)); results += \`👤 \${p.name} | \${p.overall} OVR\\n \${apps} матч | ⚽ \${goals} | 🅰️ \${assists}\\n\\n\`; } await sendMessage(chatId, results); if (isFirst) { state.currentHalf = 1; } else { state.currentHalf = 0; state.currentSeason++; if (state.currentSeason > state.maxSeasons) { state.status = 'finished'; await sendMessage(chatId, '🏁 КАРЬЕРЫ ЗАВЕРШЕНЫ!'); } } state.lastGeneration = Date.now(); state.generationCount++; await saveState(KV, state); } // ───────────────────────────────────────────────────────────────────────────── // CRON // ───────────────────────────────────────────────────────────────────────────── async function handleScheduled(event, env) { const state = await getState(env.GAME_KV); if (state.status !== 'active') return; const elapsed = Date.now() - state.lastGeneration; if (elapsed >= state.intervalMinutes * 60 * 1000) { await triggerGeneration(env); } } // ───────────────────────────────────────────────────────────────────────────── // EXPORT // ───────────────────────────────────────────────────────────────────────────── export default { fetch: handleRequest, scheduled: handleScheduled };`; document.getElementById('worker-light-display').textContent = WORKER_LIGHT; function copyWorkerLight() { const code = document.getElementById('worker-light-display').textContent; navigator.clipboard.writeText(code).then(() => { alert('Worker.js скопирован!'); }).catch(() => { const ta = document.createElement('textarea'); ta.value = code; document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta); alert('Код скопирован!'); }); }