/* Galvanic Corrosion - Rec Room custom server for communities. Copyright (C) 2025 @zombieb (Discord / proxnet Gitea) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ import Logging from "@proxnet/undead-logging"; import { SteamAuth, SteamAuthResult, SteamAuthRes } from "./SteamAuthTypes.ts"; import { SteamPlayer } from "./SteamCommonTypes.ts"; const log = new Logging("Steam"); const steamkey = Deno.env.get("STEAMKEY"); function buildSteamUrl(steaminterface: string, endpoint: string) { return `https://api.steampowered.com/${steaminterface}/${endpoint}`; } export enum PersonaState { Offline, Online, Busy, Away, Snooze, LookingToTrade, LookingToPlay } export enum CommunityVisibilityState { NotVisible, PubliclyVisible = 3 } class SteamBase { async GetPlayerSummaries(steamids: string[]) { if (!steamkey) return []; const params = new URLSearchParams(); params.append('key', steamkey); params.append('steamids', steamids.join(',')) try { const res = await fetch(`${buildSteamUrl('ISteamUser', 'GetPlayerSummaries/v2')}?${params}`); if (res.status !== 200) return []; const resjson = await res.json() as { response: { players: SteamPlayer[] } }; return resjson.response.players; } catch (err) { log.e(`Could not fetch Steam player summaries: ${(err as Error).stack}`); return []; } } async AuthenticateUserTicket(ticket: string, userid: string): Promise { if (!steamkey) return { valid: SteamAuthResult.NotConfigured }; // always authenticate if no steam API key was found const params = new URLSearchParams(); params.append('key', steamkey); params.append('appid', "471710"); params.append('ticket', ticket); try { const res = await fetch(`${buildSteamUrl('ISteamUserAuth', 'AuthenticateUserTicket/v1')}?${params}`); const resjson = (await res.json()) as SteamAuthRes; if (resjson.response.error) { log.w(`Steam Authentication failed: (${resjson.response.error.errorcode}) ${resjson.response.error.errordesc}`); // add more error codes later if needed const conditions = [ resjson.response.error.errorcode == 100 ].includes(true); if (conditions) log.w('This error indicates a client problem.'); return { valid: SteamAuthResult.Failure, res: resjson.response.error }; } //log.d(JSON.stringify(resjson.response)); if (resjson.response.params) { // since rec room is not eligible for family sharing on Steam const valid = resjson.response.params.steamid === userid && resjson.response.params.ownersteamid === userid; if (valid) return { valid: SteamAuthResult.Success, res: resjson.response.params } else throw new Error('`ownersteamid` is not equal to `steamid`, report me to GC devs!'); } else { log.w("Steam Authentication failed: Steam response did not contain params or error! This should never be logged!"); return { valid: SteamAuthResult.Failure, res: { errorcode: -1, errordesc: 'Steam response error' } }; } } catch (err) { log.w(`Steam Authentication failed: ${(err as Error).message}`); return { valid: SteamAuthResult.Failure, res: { errorcode: -1, errordesc: 'Steam response error' } }; } } } const Steam = new SteamBase(); export default Steam;