diff --git a/discord/bot.js b/discord/bot.js index 39da4231..a2fb743f 100644 --- a/discord/bot.js +++ b/discord/bot.js @@ -9,8 +9,8 @@ import { ButtonBuilder, ButtonStyle, ActivityType, - ModalBuilder, - TextInputBuilder, + ModalBuilder, + TextInputBuilder, TextInputStyle } from "discord.js"; import cron from "node-cron"; @@ -37,7 +37,7 @@ import { renderProfile, renderCompetitiveMatchHistory } from "./embed.js"; -import { authUser, fetchRiotClientVersion, getUser, getUserList, getRegion, getUserInfo } from "../valorant/auth.js"; +import { authUser, getUser, getUserList, getRegion, getUserInfo } from "../valorant/auth.js"; import { getBalance } from "../valorant/shop.js"; import { getSkin, fetchData, searchSkin, searchBundle, getBundle, clearCache } from "../valorant/cache.js"; import { @@ -61,10 +61,11 @@ import { fetchChannel, fetchMaintenances, getProxyManager, initProxyManager, removeAlertActionRow, skinNameAndEmoji, - valNamesToDiscordNames, WeaponTypeUuid, + WeaponTypeUuid, WeaponType, fetch, - calcLength + calcLength, + fetchRiotVersionData, } from "../misc/util.js"; import config, { loadConfig, saveConfig } from "../misc/config.js"; import { localError, localLog, sendConsoleOutput } from "../misc/logger.js"; @@ -104,7 +105,7 @@ client.on("ready", async () => { console.log("Loading skins..."); fetchData().then(() => console.log("Skins loaded!")); - fetchRiotClientVersion().then(() => console.log("Fetched latest Riot user-agent!")); + fetchRiotVersionData().then(() => console.log("Fetched latest Riot user-agent!")); initProxyManager().then(() => { if (getProxyManager().enabled) { console.log(`Proxy manager loaded ${getProxyManager().allProxies.length} proxies!`); @@ -158,7 +159,7 @@ export const scheduleTasks = () => { if (config.logToChannel && config.logFrequency) cronTasks.push(cron.schedule(config.logFrequency, sendConsoleOutput)); // check for a new riot client version (new user agent) every 15mins - if (config.updateUserAgent) cronTasks.push(cron.schedule(config.updateUserAgent, fetchRiotClientVersion)); + if (config.updateUserAgent) cronTasks.push(cron.schedule(config.updateUserAgent, fetchRiotVersionData)); } export const destroyTasks = () => { @@ -905,7 +906,7 @@ client.on("interactionCreate", async (interaction) => { embeds: [basicEmbed(s(interaction).info.ACCOUNT_UPDATED.f({ u: user.username }, interaction))], }); break; - } + } case "testalerts": { if (!valorantUser) return await interaction.reply({ embeds: [basicEmbed(s(interaction).error.NOT_REGISTERED)], @@ -925,7 +926,7 @@ client.on("interactionCreate", async (interaction) => { } case "login": { await defer(interaction, true); - + const json = readUserJson(interaction.user.id); if (json && json.accounts.length >= config.maxAccountsPerUser) { return await interaction.followUp({ @@ -979,7 +980,7 @@ client.on("interactionCreate", async (interaction) => { break; } - case "logout": + case "logout": case "forget": { const accountCount = getNumberOfAccounts(interaction.user.id); if (accountCount === 0) return await interaction.reply({ @@ -1631,14 +1632,14 @@ client.on("interactionCreate", async (interaction) => { async function clwpage() { const weaponType = Object.values(WeaponTypeUuid)[parseInt(weaponTypeIndex)]; - + let user; if (userId !== interaction.user.id) user = getUser(userId); else user = valorantUser; - + const skinsResponse = await getSkins(user); if (!skinsResponse.success) return await interaction.reply(authFailureMessage(interaction, skinsResponse, s(interaction).error.AUTH_ERROR_COLLECTION, userId !== interaction.user.id)); - + await interaction.update(await collectionOfWeaponEmbed(interaction, userId, user, weaponType, skinsResponse.skins, parseInt(pageIndex-1))); } } diff --git a/misc/config.js b/misc/config.js index ec499d4e..1b18efc0 100644 --- a/misc/config.js +++ b/misc/config.js @@ -27,15 +27,18 @@ export const loadConfig = (filename="config.json") => { return console.error("You forgot to put your bot token in config.json!"); if(loadedConfig.HDevTokenAlert && !loadedConfig.HDevToken || loadedConfig.HDevToken === ""){ - console.error("You forgot to put your HDevToken in config.json!"); - console.error("The Profile command works without an HDEV token, but you can view up to 2 different accounts per hour."); - console.error("If you need more than 2 accounts per hour, see https://discord.gg/B7AarTMZMK"); - console.error("If you don't want to see this notification again, set HDevTokenAlert to false in config.json file"); + console.error("Looks like you didn't put a HDevToken in config.json!"); + console.error("The /profile command won't work without one. To get a key, see https://discord.gg/B7AarTMZMK"); + console.error("If you don't want to see this notification again, set HDevTokenAlert to false in config.json"); } + // backwards compatibility loadedConfig.fetchSkinPrices = loadedConfig.showSkinPrices; loadedConfig.fetchSkinRarities = loadedConfig.showSkinRarities; + // to see what these keys do, check here: + // https://github.com/giorgi-o/SkinPeek/wiki/SkinPeek-Admin-Guide#the-option-list + applyConfig(loadedConfig, "token", "token goes here"); applyConfig(loadedConfig, "HDevToken", ""); applyConfig(loadedConfig, "HDevTokenAlert", true); diff --git a/misc/util.js b/misc/util.js index 9c7fe6de..36ad2ae4 100644 --- a/misc/util.js +++ b/misc/util.js @@ -428,6 +428,65 @@ export const itemTypes = { TITLE: "de7caa6b-adf7-4588-bbd1-143831e786c6" } +// example riotVersionData: { +// "manifestId": "C330A20409C5FDF2", +// "branch": "release-08.09", +// "version": "08.09.00.2521387", +// "buildVersion": "57", +// "engineVersion": "4.27.2.0", +// "riotClientVersion": "release-08.09-shipping-57-2521387", +// "riotClientBuild": "86.0.3.1523.3366", +// "buildDate": "2024-05-13T00:00:00Z" +// } +let riotVersionData = null; + +export const getRiotVersionData = () => { + if(riotVersionData === null) { + throw "Tried to get Riot version data before it was loaded! Might be a race condition."; + } + + return riotVersionData; +} + +export const fetchRiotVersionData = async () => { + console.log("Fetching latest Valorant version number..."); + + const req = await fetch("https://valorant-api.com/v1/version"); + if(req.statusCode !== 200) { + console.log(`Riot version data status code is ${req.statusCode}!`); + console.log(req); + + return null; + } + + const json = JSON.parse(req.body); + riotVersionData = json.data; + + return riotVersionData; +} + +// TODO: find out what how to automatically get the latest one of these +const platformOsVersion = "10.0.19042.1.256.64bit"; + +export const riotClientHeaders = () => { + const clientPlatformData = { + platformType: "PC", + platformOS: "Windows", + platformOSVersion: platformOsVersion, + platformChipset: "Unknown", + } + + // JSON stringify prettyfied with 1 tab and \r\n, then base64 encode + const clientPlatformDataJson = JSON.stringify(clientPlatformData, null, "\t"); + const clientPlatformDataBuffer = Buffer.from(clientPlatformDataJson.replace(/\n/g, "\r\n")); + const clientPlatformDataBase64 = clientPlatformDataBuffer.toString("base64"); + + return { + "X-Riot-ClientPlatform": clientPlatformDataBase64, + "X-Riot-ClientVersion": getRiotVersionData().riotClientVersion, + } +} + export const parseSetCookie = (setCookie) => { if(!setCookie) { console.error("Riot didn't return any cookies during the auth request! Cloudflare might have something to do with it..."); diff --git a/valorant/auth.js b/valorant/auth.js index 5cd17b1b..216f9dd3 100644 --- a/valorant/auth.js +++ b/valorant/auth.js @@ -389,72 +389,12 @@ export const refreshToken = async (id, account=null) => { return response; } -let riotClientVersion; -let userAgentFetchPromise; -export const fetchRiotClientVersion = async (attempt=1) => { - if(userAgentFetchPromise) return userAgentFetchPromise; - - let resolve; - if(!userAgentFetchPromise) { - console.log("Fetching latest Riot user-agent..."); // only log it the first time - userAgentFetchPromise = new Promise(r => resolve = r); - } - - const headers = { - "User-Agent": "giorgi-o/skinpeek", - "X-GitHub-Api-Version": "2022-11-28", - }; - if(config.githubToken) headers["Authorization"] = `Bearer ${config.githubToken}`; - - const githubReq = await fetch("https://api.github.com/repos/Morilli/riot-manifests/contents/Riot%20Client/KeystoneFoundationLiveWin?ref=master", { - headers - }); - - let json, versions, error = false; - try { - if(githubReq.statusCode !== 200) error = true; - else { - json = JSON.parse(githubReq.body); - versions = json.map(file => file.name.split('_')[0]); - } - } catch(e) { - error = true - } - if(error) { - if(attempt === 3) { - console.error("Failed to fetch latest Riot user-agent! (tried 3 times)"); - const fallbackVersion = "65.0.2.5073401"; - console.error(`Using version number ${fallbackVersion} instead...`); - } - - console.error(`Failed to fetch latest Riot user-agent! (try ${attempt}/3`); - console.error(githubReq); - - await wait(1000); - return fetchRiotClientVersion(attempt + 1); - } - - const compareVersions = (a, b) => { - const aSplit = a.split("."); - const bSplit = b.split("."); - for(let i = 0; i < aSplit.length; i++) { - if(aSplit[i] > bSplit[i]) return 1; - if(aSplit[i] < bSplit[i]) return -1; - } - return 0; - } - versions.sort((a, b) => compareVersions(b, a)); - - riotClientVersion = versions[0]; - userAgentFetchPromise = null; - resolve?.(); -} const getUserAgent = async () => { // temporary bypass for Riot adding hCaptcha (see github issue #93) - return "ShooterGame/11 Windows/10.0.22621.1.768.64bit"; + return "ShooterGame/13 Windows/10.0.19043.1.256.64bit"; if(!riotClientVersion) await fetchRiotClientVersion(); return `RiotClient/${riotClientVersion}.1234567 rso-auth (Windows;10;;Professional, x64)`; diff --git a/valorant/battlepass.js b/valorant/battlepass.js index 8cd7f17c..96fe0c93 100644 --- a/valorant/battlepass.js +++ b/valorant/battlepass.js @@ -1,8 +1,7 @@ import {authUser, deleteUserAuth, getUser} from "./auth.js"; -import {fetch, isMaintenance, userRegion} from "../misc/util.js"; +import {fetch, isMaintenance, riotClientHeaders, userRegion} from "../misc/util.js"; import {getBattlepassInfo, getBuddy, getCard, getSkin, getSpray, getValorantVersion} from "./cache.js"; -import { RIOT_CLIENT_HEADERS } from "./shop.js"; import {renderBattlepass} from "../discord/embed.js"; import {getEntitlements} from "./inventory.js"; import {l, s} from "../misc/languages.js"; diff --git a/valorant/cache.js b/valorant/cache.js index e8f6f20d..2e20e1c6 100644 --- a/valorant/cache.js +++ b/valorant/cache.js @@ -6,7 +6,7 @@ import fs from "fs"; import { DEFAULT_VALORANT_LANG, discToValLang } from "../misc/languages.js"; import { client } from "../discord/bot.js"; import { sendShardMessage } from "../misc/shardMessage.js"; -import { RIOT_CLIENT_HEADERS } from "./shop.js"; +import { riotClientHeaders } from "../misc/util.js"; const formatVersion = 14; let gameVersion; diff --git a/valorant/inventory.js b/valorant/inventory.js index c89c9cd7..24b6cb50 100644 --- a/valorant/inventory.js +++ b/valorant/inventory.js @@ -3,7 +3,7 @@ import {authUser, deleteUserAuth, getUser} from "./auth.js"; import {authFailureMessage, basicEmbed, skinCollectionSingleEmbed, collectionOfWeaponEmbed} from "../discord/embed.js"; import config from "../misc/config.js"; import {s} from "../misc/languages.js"; -import { RIOT_CLIENT_HEADERS } from "./shop.js"; +import {riotClientHeaders} from "../misc/util.js"; export const getEntitlements = async (user, itemTypeId, itemType="item") => { @@ -12,7 +12,7 @@ export const getEntitlements = async (user, itemTypeId, itemType="item") => { headers: { "Authorization": "Bearer " + user.auth.rso, "X-Riot-Entitlements-JWT": user.auth.ent, - ...RIOT_CLIENT_HEADERS, + ...riotClientHeaders(), } }); @@ -94,7 +94,7 @@ export const getLoadout = async (user, account) => { headers: { "Authorization": "Bearer " + user.auth.rso, "X-Riot-Entitlements-JWT": user.auth.ent, - ...RIOT_CLIENT_HEADERS, + ...riotClientHeaders(), } }); @@ -111,7 +111,7 @@ export const getLoadout = async (user, account) => { headers: { "Authorization": "Bearer " + user.auth.rso, "X-Riot-Entitlements-JWT": user.auth.ent, - ...RIOT_CLIENT_HEADERS, + ...riotClientHeaders(), } }); diff --git a/valorant/shop.js b/valorant/shop.js index a8775802..6fc556c7 100644 --- a/valorant/shop.js +++ b/valorant/shop.js @@ -15,17 +15,6 @@ import config from "../misc/config.js"; import { deleteUser, saveUser } from "./accountSwitcher.js"; import { mqGetShop, useMultiqueue } from "../misc/multiqueue.js"; -export const RIOT_CLIENT_HEADERS = { - // fix for HTTP 400 (thx Zxc and Manuel_Hexe) - "X-Riot-ClientPlatform": "ew0KCSJwbGF0Zm9ybVR5cGUiOiAiUEMiLA0KCSJwbGF0Zm9ybU9TIjogIldpbmRvd3MiLA0KCSJwbGF0Zm9ybU9TVmVyc2lvbiI6ICIxMC4wLjE5MDQyLjEuMjU2LjY0Yml0IiwNCgkicGxhdGZvcm1DaGlwc2V0IjogIlVua25vd24iDQp9", - "X-Riot-ClientVersion": "release-08.09-shipping-57-2521387", - // todo: get ClientVersion from Valorant API to not have to automatically update it. - // before that, got to make a cache for it, so that we don't need to fetch it - // every time someone uses /shop, but make sure it doesn't interfere with all - // the other caches (skins, rarities, etc.) because those need the actual - // uncached client version. -} - export const getShop = async (id, account = null) => { if (useMultiqueue()) return await mqGetShop(id, account); @@ -40,7 +29,7 @@ export const getShop = async (id, account = null) => { headers: { "Authorization": "Bearer " + user.auth.rso, "X-Riot-Entitlements-JWT": user.auth.ent, - ...RIOT_CLIENT_HEADERS + ...riotClientHeaders(), } }); console.assert(req.statusCode === 200, `Valorant skins offers code is ${req.statusCode}!`, req); @@ -135,7 +124,7 @@ export const getBalance = async (id, account = null) => { headers: { "Authorization": "Bearer " + user.auth.rso, "X-Riot-Entitlements-JWT": user.auth.ent, - ...RIOT_CLIENT_HEADERS + ...riotClientHeaders(), } }); console.assert(req.statusCode === 200, `Valorant balance code is ${req.statusCode}!`, req);