Last active
July 6, 2025 19:10
-
-
Save Aracturat/08b29ab7d00374f5f501b67443919b43 to your computer and use it in GitHub Desktop.
Create geoguessr map with bad rounds
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
async function createMap(name) { | |
const response = await fetch('https://www.geoguessr.com/api/v4/user-maps/drafts', { | |
headers: { | |
accept: '*/*', | |
'content-type': 'application/json', | |
}, | |
body: JSON.stringify({ name, mode: 'coordinates' }), | |
method: 'POST', | |
mode: 'cors', | |
credentials: 'include', | |
}).then((e) => e.json()); | |
return response.id; | |
} | |
async function updateCoordinates(id, coordinates, version) { | |
await fetch(`https://www.geoguessr.com/api/v4/user-maps/drafts/${id}`, { | |
headers: { | |
'content-type': 'application/json', | |
}, | |
body: JSON.stringify({ | |
avatar: { background: 'morning', decoration: 'palmtrees', ground: 'green', landscape: 'grassmountains' }, | |
regions: [], | |
customCoordinates: coordinates, | |
tags: [], | |
maxErrorDistance: 17499125, | |
hasCustomErrorDistance: false, | |
version, | |
}), | |
method: 'PUT', | |
mode: 'cors', | |
credentials: 'include', | |
}); | |
} | |
async function publishMap(id) { | |
await fetch(`https://www.geoguessr.com/api/v4/user-maps/drafts/${id}/publish/`, { | |
headers: { | |
'content-type': 'application/json', | |
}, | |
body: '{}', | |
method: 'PUT', | |
mode: 'cors', | |
credentials: 'include', | |
}); | |
} | |
async function createAndPublishMap(name, coordinates) { | |
const id = await createMap(name); | |
await updateCoordinates(id, coordinates, 1); | |
await publishMap(id); | |
return `https://www.geoguessr.com/maps/${id}`; | |
} | |
const getMyGames = (paginationToken) => { | |
return fetch(`https://www.geoguessr.com/api/v4/feed/private?count=10&paginationToken=${paginationToken}`, { | |
headers: { | |
'content-type': 'application/json', | |
}, | |
method: 'GET', | |
credentials: 'include', | |
}).then((e) => e.json()); | |
}; | |
const getMyLastDuels = async (count) => { | |
let paginationToken = ''; | |
const games = []; | |
while (true) { | |
const { entries: newGames, paginationToken: newPaginationToken } = await getMyGames(paginationToken); | |
const subGames = newGames | |
.flatMap((game) => { | |
if (game.payload) { | |
return JSON.parse(game.payload); | |
} else { | |
return null; | |
} | |
}) | |
.filter((e) => e?.payload?.gameMode === 'Duels') | |
.map((e) => e?.payload?.gameId); | |
games.push(...subGames); | |
paginationToken = newPaginationToken; | |
if (games.length >= count || !newPaginationToken) { | |
break; | |
} | |
} | |
return games.slice(0, count); | |
}; | |
async function getBadPanaramas(userId, gameId, minScore, maxScore, gameMode) { | |
const out = await fetch(`https://game-server.geoguessr.com/api/duels/${gameId}`, { credentials: 'include' }).then( | |
(res) => res.json() | |
); | |
let player_index; | |
if (out.teams[0].players[0].playerId === userId) { | |
player_index = 0; | |
} else if (out.teams[1].players[0].playerId === userId) { | |
player_index = 1; | |
} else { | |
return []; | |
} | |
const outGameMode = out.movementOptions.forbidZooming | |
? 'NMPZ' | |
: out.movementOptions.forbidMoving | |
? 'NO MOVE' | |
: 'MOVE'; | |
if (outGameMode !== gameMode && gameMode !== 'ANY') { | |
return []; | |
} | |
return out.teams[player_index].players[0].guesses | |
.filter((e) => e.score <= maxScore && e.score >= minScore) | |
.map((e) => e.roundNumber) | |
.map((e) => out.rounds[e - 1].panorama); | |
} | |
const analyzeAndCreateMap = async (gameMode, minScore, maxScore, lastGamesCount, countryCode) => { | |
const myDuels = await getMyLastDuels(lastGamesCount); | |
const userId = JSON.parse(document.getElementById('__NEXT_DATA__').innerText).props.accountProps.account.user.userId; | |
let badPanoramas = []; | |
for (let i = 0; i < myDuels.length; i++) { | |
console.log(`Evaluate game ${i + 1}/${myDuels.length}`); | |
const gameId = myDuels[i]; | |
const duelBadPanaramas = await getBadPanaramas(userId, gameId, minScore, maxScore, gameMode); | |
await new Promise((resolve) => setTimeout(resolve, 1000)); | |
badPanoramas.push(...duelBadPanaramas); | |
} | |
let mapName = `Rounds with score in [${minScore}, ${maxScore}] in the last ${lastGamesCount} ${gameMode} games`; | |
if (countryCode) { | |
badPanoramas = badPanoramas.filter((e) => e.countryCode === countryCode); | |
mapName += ` for ${countryCode}`; | |
} | |
if (badPanoramas.length < 5) { | |
console.log('Увеличьте кол-во игр, так как нужно минимум 5 раундов для создания карты'); | |
return; | |
} | |
const link = await createAndPublishMap(mapName, badPanoramas); | |
console.log(link); | |
}; | |
// Варианты игр 'NMPZ' 'NO MOVE' 'MOVE' 'ANY' | |
// Последний параметр - страна, если нужно создать карту только для одной страны, к примеру 'ar' | |
analyzeAndCreateMap('ANY', 1000, 3000, 100, null); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment