Имеется веб-приложение с играми (@g_amee) в тг. Напримере этой статьи , попытался отправить фейковый запрос в игре. Но сам код не работает, из-за проблемы с библиотекой. (Не удалось получить токены авторизации. Прерываю выполнение.) // gamee_score_sender.ts // --- 1. Импорты (необходимые для MD5 и работы с датами) --- import { md5 } from "@takker/md5"; import dayjs from "https://cdn.skypack.dev/dayjs"; import utc from "https://cdn.skypack.dev/dayjs/plugin/utc"; import timezone from "https://cdn.skypack.dev/dayjs/plugin/timezone"; dayjs.extend(utc); dayjs.extend(timezone); // --- 2. Ваши основные данные --- // ВНИМАНИЕ: gameAuthUrlPath НУЖНО ЗАМЕНИТЬ НА СВОЙ АКТУАЛЬНЫЙ URL ИЗ ПРИГЛАШЕНИЯ В ИГРУ! // Пример: "/game-bot/astrocat-c6d185e817410ccca2fe1d3a8fa929403e1c68ac" const gameAuthUrlPath = "/game-bot/***"; // <--- замена // Секретный ключ для checksum const checksumSecretSuffix = "crmjbjm3lczhlgnek9uaxz2l9svlfjw14npauhen"; // --- 3. Функции для выполнения действий --- // Функция для получения токенов авторизации async function getAuthTokens(): Promise<{ authToken: string; installUuid: string } | null> { console.log("1. Пытаюсь авторизоваться и получить токены..."); try { const authRes = await fetch("https://api.gamee.com/", { method: "post", body: JSON.stringify({ jsonrpc: "2.0", id: "oauth.token", method: "oauth.token", params: { gameUrl: gameAuthUrlPath, // UUID устройства. Можно использовать `crypto.randomUUID()` для нового, // или тот, что был в твоем "слизанном" запросе авторизации. uuid: crypto.randomUUID(), // генерация нового }, }), headers: { "Content-Type": "text/plain;charset=UTF-8", "Origin": "https://prizes.gamee.com", }, }); const authData = await authRes.json(); if (authData.error) { console.error("Ошибка авторизации:", authData.error.message, authData.error.data); return null; } console.log("Авторизация прошла успешно!"); return { authToken: authData.result.accessToken.token, installUuid: authData.result.installUuid, }; } catch (error) { console.error("Произошла ошибка при запросе авторизации:", error); return null; } } // Функция для отправки игровых данных async function sendGameplayData(tokens: { authToken: string; installUuid: string }) { console.log("\n2. Готовлю данные для отправки игрового раунда..."); // --- ВАЖНО: Эти значения ты можешь менять для каждого нового раунда --- const gameId = 125; // ID игры const scoreToSend = 5000; // ТВОЙ ЖЕЛАЕМЫЙ СЧЕТ! Укажи здесь сколько хочешь очков. const playTimeToSend = 180; // Время игры в секундах. const gameUrl = "/game-bot/***"; // URL конкретной игры const releaseNumber = 6; const gameStateData = null; // Если есть состояние игры, оно пойдет сюда (может быть JSON-строкой) const gameplayId = 129; // Этот ID может быть статичным или должен увеличиваться (проверь) // Генерируем новый UUID для каждого раунда, чтобы избежать ошибки "Gameplay already exists" const roundUuid = crypto.randomUUID(); // Форматируем текущее время в нужный формат "YYYY-MM-DDTHH:mm:ss+**:mm" const currentCreatedTime = dayjs().format("YYYY-MM-DDTHH:mm:ssZ"); // Вычисляем checksum по сложной формуле, которую мы нашли const calculatedChecksum = md5( `<span class="math-inline">\{scoreToSend ?? 0\}\:</span>{playTimeToSend ?? 0}:<span class="math-inline">\{gameUrl\}\:</span>{ gameStateData ?? "" }:<span class="math-inline">\{roundUuid\}\:</span>{checksumSecretSuffix}`, ); console.log("Вычисленный checksum:", calculatedChecksum); console.log("\n3. Отправляю данные игрового раунда..."); try { const res = await fetch("https://api.gamee.com/", { method: "post", body: JSON.stringify({ jsonrpc: "2.0", id: "game.saveWebGameplay", method: "game.saveWebGameplay", params: { gameplayData: { gameId: gameId, score: scoreToSend, playTime: playTimeToSend, gameUrl: gameUrl, releaseNumber: releaseNumber, createdTime: currentCreatedTime, metadata: { gameplayId: gameplayId, }, isSaveState: false, gameStateData: gameStateData, gameplayOrigin: "game", replayData: null, replayVariant: null, replayDataChecksum: null, uuid: roundUuid, checksum: calculatedChecksum, }, }, }), headers: { "Authorization": `Bearer ${tokens.authToken}`, // Используем полученный токен "Content-Type": "text/plain;charset=UTF-8", "Origin": "https://prizes.gamee.com", "Accept": "*/*", "Referer": "https://prizes.gamee.com/", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15", "Accept-Language": "en-US", "Sec-Fetch-Dest": "empty", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Site": "same-site", "client-language": "en", "x-install-uuid": tokens.installUuid, // Используем полученный installUuid }, }); const data = await res.json(); console.log("\n--- Ответ сервера после сохранения геймплея ---"); console.log(data); if (data.error) { console.error("\n!!! Ошибка при сохранении геймплея:", data.error.message); if (data.error.data && data.error.data.reason) { console.error("Причина ошибки:", data.error.data.reason); } } else { console.log("\n Геймплей успешно сохранен!"); } } catch (error) { console.error("\n Произошла ошибка при выполнении запроса сохранения геймплея:", error); } } // --- 4. Запускаем все действия по порядку --- async function main() { // Сначала получаем токены const tokens = await getAuthTokens(); // Если токены получены успешно, отправляем данные геймплея if (tokens) { await sendGameplayData(tokens); } else { console.error("Не удалось получить токены авторизации. Прерываю выполнение."); } } // Вызываем основную функцию main(); Code // gamee_score_sender.ts // --- 1. Импорты (необходимые для MD5 и работы с датами) --- import { md5 } from "@takker/md5"; import dayjs from "https://cdn.skypack.dev/dayjs"; import utc from "https://cdn.skypack.dev/dayjs/plugin/utc"; import timezone from "https://cdn.skypack.dev/dayjs/plugin/timezone"; dayjs.extend(utc); dayjs.extend(timezone); // --- 2. Ваши основные данные --- // ВНИМАНИЕ: gameAuthUrlPath НУЖНО ЗАМЕНИТЬ НА СВОЙ АКТУАЛЬНЫЙ URL ИЗ ПРИГЛАШЕНИЯ В ИГРУ! // Пример: "/game-bot/astrocat-c6d185e817410ccca2fe1d3a8fa929403e1c68ac" const gameAuthUrlPath = "/game-bot/***"; // <--- замена // Секретный ключ для checksum const checksumSecretSuffix = "crmjbjm3lczhlgnek9uaxz2l9svlfjw14npauhen"; // --- 3. Функции для выполнения действий --- // Функция для получения токенов авторизации async function getAuthTokens(): Promise<{ authToken: string; installUuid: string } | null> { console.log("1. Пытаюсь авторизоваться и получить токены..."); try { const authRes = await fetch("https://api.gamee.com/", { method: "post", body: JSON.stringify({ jsonrpc: "2.0", id: "oauth.token", method: "oauth.token", params: { gameUrl: gameAuthUrlPath, // UUID устройства. Можно использовать `crypto.randomUUID()` для нового, // или тот, что был в твоем "слизанном" запросе авторизации. uuid: crypto.randomUUID(), // генерация нового }, }), headers: { "Content-Type": "text/plain;charset=UTF-8", "Origin": "https://prizes.gamee.com", }, }); const authData = await authRes.json(); if (authData.error) { console.error("Ошибка авторизации:", authData.error.message, authData.error.data); return null; } console.log("Авторизация прошла успешно!"); return { authToken: authData.result.accessToken.token, installUuid: authData.result.installUuid, }; } catch (error) { console.error("Произошла ошибка при запросе авторизации:", error); return null; } } // Функция для отправки игровых данных async function sendGameplayData(tokens: { authToken: string; installUuid: string }) { console.log("\n2. Готовлю данные для отправки игрового раунда..."); // --- ВАЖНО: Эти значения ты можешь менять для каждого нового раунда --- const gameId = 125; // ID игры const scoreToSend = 5000; // ТВОЙ ЖЕЛАЕМЫЙ СЧЕТ! Укажи здесь сколько хочешь очков. const playTimeToSend = 180; // Время игры в секундах. const gameUrl = "/game-bot/***"; // URL конкретной игры const releaseNumber = 6; const gameStateData = null; // Если есть состояние игры, оно пойдет сюда (может быть JSON-строкой) const gameplayId = 129; // Этот ID может быть статичным или должен увеличиваться (проверь) // Генерируем новый UUID для каждого раунда, чтобы избежать ошибки "Gameplay already exists" const roundUuid = crypto.randomUUID(); // Форматируем текущее время в нужный формат "YYYY-MM-DDTHH:mm:ss+**:mm" const currentCreatedTime = dayjs().format("YYYY-MM-DDTHH:mm:ssZ"); // Вычисляем checksum по сложной формуле, которую мы нашли const calculatedChecksum = md5( `<span class="math-inline">\{scoreToSend ?? 0\}\:</span>{playTimeToSend ?? 0}:<span class="math-inline">\{gameUrl\}\:</span>{ gameStateData ?? "" }:<span class="math-inline">\{roundUuid\}\:</span>{checksumSecretSuffix}`, ); console.log("Вычисленный checksum:", calculatedChecksum); console.log("\n3. Отправляю данные игрового раунда..."); try { const res = await fetch("https://api.gamee.com/", { method: "post", body: JSON.stringify({ jsonrpc: "2.0", id: "game.saveWebGameplay", method: "game.saveWebGameplay", params: { gameplayData: { gameId: gameId, score: scoreToSend, playTime: playTimeToSend, gameUrl: gameUrl, releaseNumber: releaseNumber, createdTime: currentCreatedTime, metadata: { gameplayId: gameplayId, }, isSaveState: false, gameStateData: gameStateData, gameplayOrigin: "game", replayData: null, replayVariant: null, replayDataChecksum: null, uuid: roundUuid, checksum: calculatedChecksum, }, }, }), headers: { "Authorization": `Bearer ${tokens.authToken}`, // Используем полученный токен "Content-Type": "text/plain;charset=UTF-8", "Origin": "https://prizes.gamee.com", "Accept": "*/*", "Referer": "https://prizes.gamee.com/", "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15", "Accept-Language": "en-US", "Sec-Fetch-Dest": "empty", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Site": "same-site", "client-language": "en", "x-install-uuid": tokens.installUuid, // Используем полученный installUuid }, }); const data = await res.json(); console.log("\n--- Ответ сервера после сохранения геймплея ---"); console.log(data); if (data.error) { console.error("\n!!! Ошибка при сохранении геймплея:", data.error.message); if (data.error.data && data.error.data.reason) { console.error("Причина ошибки:", data.error.data.reason); } } else { console.log("\n Геймплей успешно сохранен!"); } } catch (error) { console.error("\n Произошла ошибка при выполнении запроса сохранения геймплея:", error); } } // --- 4. Запускаем все действия по порядку --- async function main() { // Сначала получаем токены const tokens = await getAuthTokens(); // Если токены получены успешно, отправляем данные геймплея if (tokens) { await sendGameplayData(tokens); } else { console.error("Не удалось получить токены авторизации. Прерываю выполнение."); } } // Вызываем основную функцию main(); Очень прошу совета опытного реверсера .