Geoguessr change Avatar to Pin/ProfilePicture

Changes the Avatar to your Pin/Profile Picture. Works in Duels, Br, Profile and many more places and not only your Avatar but also the Avatar of others

当前为 2023-06-01 提交的版本,查看 最新版本

// ==UserScript==
// @name         Geoguessr change Avatar to Pin/ProfilePicture
// @description  Changes the Avatar to your Pin/Profile Picture. Works in Duels, Br, Profile and many more places and not only your Avatar but also the Avatar of others
// @version      1.1.0
// @license      MIT
// @author       joniber#5011
// @match        https://www.geoguessr.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=geoguessr.com
// @namespace https://greasyfork.org/users/1072330
// ==/UserScript==

//any questons, feedback, problems or feature request -> Discord joniber#5011

//=====================================================================================\\
//    change these values however you like (make sure to hit ctrl+s afterwards)        \\
//                PIN = PROFILE PICTURE                                                \\
//=====================================================================================\\

const PIN_IN_OWN_PROFILE = true
//                         ^^^^  set this to <false> to NOT change your Avatar to a PIN in your own profile
const PIN_IN_OTHERS_PROFILE = true
//                            ^^^^^  set this to <false> to NOT change the Avatar to a PIN in other profiles
const PIN_IN_DUELS_MATCHMAKING = true
//                         ^^^^  set this to <false> to NOT change the Avatar to a PIN in that area
const PIN_IN_BR_MATCHMAKING = true
//                         ^^^^  set this to <false> to NOT change the Avatar to a PIN in that area
const NO_AVATARS_END_OF_DUEL = true
//                         ^^^^  set this to <false> to NOT change the Avatar to a PIN in that area
const PIN_IN_TEAM_DUEL_PRIVATE_LOBBY = true
//                         ^^^^  set this to <false> to NOT change the Avatar to a PIN in that area
const PIN_IN_FRIEND_TAB = true
//                         ^^^^  set this to <false> to NOT change the Avatar to a PIN in that area

//=====================================================================================\\
//  don't edit anything after this point unless you know what you're doing             \\
//=====================================================================================\\

const GEOGUESSR_USER_ENDPOINT = 'https://geoguessr.com/api/v3/users'

const SCRIPT_PREFIX = 'up__'
const USER_PIN_CLASS = SCRIPT_PREFIX + 'userPin'
const USER_PIN_ID = SCRIPT_PREFIX + 'profilePin'
let av = null
let index = 0
let player1 = null
let player2 = null

async function fillPin(pin, userId) {
    const userData = await getUserData(userId)
    const pinURL = userData.pin.url
    pin.setAttribute('src', '/images/auto/144/144/ce/0/plain/' + pinURL)
}

async function fillLvl(lvl, userId) {
    const userData = await getUserData(userId)
    let level = roundToNearest10(userData.progress.level)
    if(level == 210){
        level = 200
    }
    lvl.firstChild.setAttribute(
        'srcset',
        '/static/avatars/tiers/low-quality/tier-' + level + '.avif'
    )
    lvl.children[1].setAttribute(
        'srcset',
        '/static/avatars/tiers/low-quality/tier-' + level + '.webp'
    )
    lvl.lastChild.setAttribute('src', '/static/avatars/tiers/low-quality/tier-' + level + '.png')
}


function fillImageMultiple(link, id, setLvl){
    if (!link.querySelector(`.${USER_PIN_CLASS}`)) {
        if(wrapper){
            link.firstChild.style.display="none"
            let destination = link
            fillImage(link, link, id, true, setLvl)
        }
    }
}

async function changePinInFriendTab(link) {
    if(link){
        if (!link.querySelector(`.${USER_PIN_CLASS}`)) {
            const wrapper = link.querySelector('[class^="transparent-avatar_imageWrapper__"]')
            if(wrapper){
                wrapper.classList.remove('transparent-avatar_imageWrapper__izUAM')
                wrapper.classList.add('styles_circle__QFYEk', 'styles_variantFloating__Srm_N', 'styles_colorTransparent__2bG5I', 'styles_borderColorTransparent__CwSAk', 'styles_borderSizeFactorZero__MEFpf')
                wrapper.insertAdjacentHTML('afterbegin', wrapperFriend1())
                const destination = link.querySelector('.styles_content__otIVG')
                const pin = destination.firstChild
                let id = link.querySelector('.anchor_variantNoUnderline__SPwsd').href
                const userId = retrieveIdFromLink(id)

                const userData = await getUserData(userId)
                const pinURL = userData.pin.url
                pin.setAttribute('src', '/images/auto/48/48/ce/0/plain/' + pinURL)
                wrapper.lastChild.style="display:none"
            }
        }
    }
}

function fillImage(destination, link, id, c, withLvl){
    if(c){
        destination.insertAdjacentHTML('afterbegin', wrapperDuels())
        destination = link.querySelector('.profile-header_avatarWrapper__5_jDA.up__')
        destination.insertAdjacentHTML('afterbegin', wrapper())
        destination = link.querySelector('.avatar_titleAvatarImage__A51Dx.up__')
        destination.insertAdjacentHTML('beforeend', profilePicture())
        const pin = destination.lastChild
        fillPin(pin, retrieveIdFromLink(id))
        if(withLvl){
            destination = link.querySelector('.styles_rectangle___6gqv.up__')
            destination.insertAdjacentHTML('beforeend', levelBorder())
            const lvl = destination.lastChild
            fillLvl(lvl, retrieveIdFromLink(id))
        }
    }
    else{
        destination.insertAdjacentHTML('beforeend', wrapper())

        destination = document.querySelector('.avatar_titleAvatarImage__A51Dx.up__')
        destination.insertAdjacentHTML('beforeend', profilePicture1())
        const pin = destination.lastChild
        fillPin(pin, retrieveIdFromLink(id))

        if(withLvl){
            destination = document.querySelector('.styles_rectangle___6gqv.up__')
            destination.insertAdjacentHTML('beforeend', levelBorder1())
            const lvl = destination.lastChild
            fillLvl(lvl, retrieveIdFromLink(id))
        }

    }


}

function changeAvatarToPin(link, avatarToRemove) {
    if (!link.querySelector(`.${USER_PIN_CLASS}`)) {
        if (link) {
            let destination = null
            if (avatarToRemove == link) {
                link.querySelector('.styles_image__8M_kp').style = 'display: none'
                destination = link.firstChild
            } else {
                link.querySelector('.player-card_lobbyCardInner__G_xzy').firstChild.remove()
                destination = link.querySelector('.player-card_lobbyCardInner__G_xzy')
            }
            let id = '/me/profile'
            if (link.querySelector('.player-card_userLink__HhoDo') != null) {
                id = link.querySelector('.player-card_userLink__HhoDo').href
            }

            fillImage(destination, link, id, true, true)
        }
    }
}

let pfpToLink = {}
let nameToLink = {}

const originalSend = WebSocket.prototype.send
WebSocket.prototype.send = function(...args) {
    window.ws = this
    if (this.geoguessrClickablePartyCards == 0) return originalSend.call(this, ...args)
    this.geoguessrClickablePartyCards = 0
    const originalOnmessage = this.onmessage
    this.onmessage = ({ data }) => {
        if (originalOnmessage != null) originalOnmessage({ data })
        const received = JSON.parse(data)
        if(received.code == 'PartyUpdated'){
            let payload = JSON.parse(received.payload)
            let players = payload.players
            for (let player of players){
                nameToLink[player.name] = "user/"+player.id
            }
        }
        if (received.code == 'PartyMemberListUpdated') {
            let payload = JSON.parse(received.payload)
            let members = payload.members
            for (let member of members) {
                if (member.pin != "") pfpToLink[member.pin] = "user/"+member.userId
                if (member.fullBodyPin != "") pfpToLink[member.fullBodyPin] = "user/"+member.userId
            }
        }
    }
    return originalSend.call(this, ...args)
}

function addPinToPartyCard(wrapper) {
    const image = wrapper.querySelector('.member-card_imageWrapper__5L2DF img')
    if(image){
        const imgPin = image.src.substr(57)
        const id = pfpToLink[imgPin]
        if (id != null) {
            const link = wrapper.querySelector('.member-card_imageWrapper__5L2DF')
            fillImageMultiple(link, id, false)
        }
    }
}

function addPinToTeamDuelsCard(wrapper){
    const name = wrapper.querySelector('.user-nick_nick__y4VIt').innerHTML.replace('&nbsp', '').replace(';','')
    if(name){
        if(nameToLink.length != 0){
            const id = nameToLink[name]

            if(id){
                const link = wrapper.querySelector('.team-player-card_lobbyCardInner__fT8A_')
                fillImageMultiple(link, id, true)
            }
        }
    }
}
let inBattleRoyale = false
let inDuels = false
let inParty = false
let inTeamDuel = false
let lastOpenedMapHighscoreTab = 0

function onMutationTeamDuel(mutations,observer){
    if (!isTeamDuel()) return

    const cards = document.querySelectorAll('.team-player-card_lobbyCard__7p_OY')
    for(let card of cards){
        addPinToTeamDuelsCard(card)
    }

    if(document.querySelector('.game-finished_avatarContainer__S63IS')){
        onMutationEnd()
    }
}

function onMutationParty(mutations,observer){
    if (location.pathname != "/party") return
    const cards = document.querySelectorAll(".member-card_root__zbBxx")
    for (let card of cards) {
        addPinToPartyCard(card)
    }
}

function onMutationEnd() {
    if (NO_AVATARS_END_OF_DUEL) {
        const gameFinishedAvatarContainer = document.querySelector(
            '.game-finished_avatarContainer__S63IS'
        )
        if (gameFinishedAvatarContainer) {
            if (document.querySelector('.styles_rectangle___6gqv.up__') == null) {
                gameFinishedAvatarContainer.firstChild.remove()
                let destination = gameFinishedAvatarContainer
                destination.style = 'height: fit-content'
                destination.insertAdjacentHTML('afterbegin', wrapperDuels())
                destination = document.querySelector('.profile-header_avatarWrapper__5_jDA.up__')
                destination.insertAdjacentHTML('afterbegin', wrapper())
                document.querySelector('.profile-header_avatar__y6vsp').style =
                    'display:flex; justify-content:center; align-items:center'
                document.querySelector('.avatar_titleAvatar__0pdL9').style = 'width:20rem'
                destination = document.querySelector('.avatar_titleAvatarImage__A51Dx.up__')
                destination.insertAdjacentHTML('beforeend', profilePicture())
                const pin = destination.lastChild
                let id = '/me/profile'
                fillPin(pin, retrieveIdFromLink(id))
                destination = document.querySelector('.styles_rectangle___6gqv.up__')
                destination.insertAdjacentHTML('beforeend', levelBorder())
                const lvl = destination.lastChild
                if (
                    document.querySelector('.shadow-text_root__o3lD9').innerHTML.startsWith('YOU')
                ) {
                    fillLvl(lvl, retrieveIdFromLink(id))
                } else {
                    lvl.lastChild.setAttribute(
                        'src',
                        '/_next/static/images/laurel-wreath-gold-53fd377e1d268ce57fcd6f0dfb9f3727.png'
                    )
                }
            }
        }
    }
}

function onMutationsDuels(mutations, observer) {
    if (player1 == null) {
        player1 = document.querySelectorAll('.lobby_avatarContainer__kN2RK')[0];
        if (player1 != null) {
            player1.children[0].firstChild.remove();
            player1.children[0].remove();
            let destination1 = player1;
            destination1.insertAdjacentHTML('afterbegin', wrapperDuels());
            destination1 = document.querySelector('.profile-header_avatarWrapper__5_jDA.up__');
            destination1.insertAdjacentHTML('afterbegin', wrapper());
            destination1 = document.querySelector('.avatar_titleAvatarImage__A51Dx.up__');
            destination1.insertAdjacentHTML('beforeend', profilePicture());
            const pin = destination1.lastChild;
            let id = player1.lastChild.firstChild.href;

            fillPin(pin, retrieveIdFromLink(id));
            destination1 = document.querySelector('.styles_rectangle___6gqv.up__');
            destination1.insertAdjacentHTML('beforeend', levelBorder());
            const lvl = destination1.lastChild;
            fillLvl(lvl, retrieveIdFromLink(id));
        }
        player2 = document.querySelectorAll('.lobby_avatarContainer__kN2RK')[1];
        if (player2 != null) {
            player2.children[0].firstChild.remove();
            player2.children[0].remove();
            let destination2 = player2;
            destination2.insertAdjacentHTML('afterbegin', wrapperDuels());
            destination2 = document.querySelectorAll(
                '.profile-header_avatarWrapper__5_jDA.up__'
            )[1];
            destination2.insertAdjacentHTML('afterbegin', wrapper());
            destination2 = document.querySelectorAll('.avatar_titleAvatarImage__A51Dx.up__')[1];
            destination2.insertAdjacentHTML('beforeend', profilePicture());
            const pin1 = destination2.lastChild;
            let id1 = player2.lastChild.firstChild.href;

            fillPin(pin1, retrieveIdFromLink(id1));
            destination2 = document.querySelectorAll('.styles_rectangle___6gqv.up__')[1];
            destination2.insertAdjacentHTML('beforeend', levelBorder());
            const lvl1 = destination2.lastChild;
            fillLvl(lvl1, retrieveIdFromLink(id1));
        }
    }
    if (player1 != null && player2 != null) {
        player1 = null;
        player2 = null;
    }

    const endOfDuelsObserver = new MutationObserver(onMutationEnd);
    if (endOfDuelsObserver) {
        endOfDuelsObserver.observe(
            document.querySelector('.overlay_overlay__AR02x'),
            OBSERVER_CONFIG
        );
    }
}
function onMutationsStandard(mutations, observer) {
    if (isDuels() && !inDuels && document.querySelector('.game_hud__fhdo5')) {
        inDuels = true
        const duelsObserver = new MutationObserver(onMutationsDuels)
        duelsObserver.observe(document.querySelector('.game_hud__fhdo5'), OBSERVER_CONFIG)
    } else if (inDuels && !document.querySelector('.game_hud__fhdo5')) {
        inDuels = false
    }
    else if(isParty() && !inParty){
        inParty=true
        const partyObserver = new MutationObserver(onMutationParty)
        partyObserver.observe(document.body, OBSERVER_CONFIG)
    } else if (inParty && !document.querySelector('.party_root__EQz_N')) {
        inParty = false
    }

    else if(isTeamDuel() && !inTeamDuel){
        inTeamDuel=true
        const teamDuelObserver = new MutationObserver(onMutationTeamDuel)
        teamDuelObserver.observe(document.body, OBSERVER_CONFIG1)
    } else if (inParty && !document.querySelector('.lobby_root__Z9W2f')) {
        inTeamDuel = false
    }
    if (inBattleRoyale || inDuels || inParty) {
        return
    }

    if (isProfile()) {
        if (!PIN_IN_OWN_PROFILE && isOwnProfile()) {
            return
        }
        if (!PIN_IN_OTHERS_PROFILE && isOtherProfile()) {
            return
        }
        if (!document.querySelector(`#${USER_PIN_ID}`)) {
            if(document.querySelector('.profile-header_fullBodyAvatar___LRnw')){
            document.querySelector('.profile-header_fullBodyAvatar___LRnw').remove()

            let destination = document.querySelector('.profile-header_avatarWrapper__5_jDA')
            fillImage(destination, document, location.href, false, true)
            }
        }
    }

    if (PIN_IN_BR_MATCHMAKING) {
        // battle royale matchmaking
        for (const link of document.querySelectorAll('.player-card_root__NvK_s')) {
            changeAvatarToPin(link, 'player-card_lobbyCardInner__G_xzy')
        }
    }
    if (PIN_IN_FRIEND_TAB) {
        for (const link of document.querySelectorAll('.chat-friend_content__zbEBt')) {
            changePinInFriendTab(link)
        }
    }
    if (isDuels() && PIN_IN_DUELS_MATCHMAKING) {
        if (!document.querySelector(`#${USER_PIN_ID}`)) {
            if (av == null) {
                av = document.querySelector('.lobby_avatarContainer__kN2RK')
            }
            if (av != null) {
                av.children[0].remove()
                fillImage(av, document, retrieveIdFromLink('/me/profile'), false, true)
            }
            if (av != null) {
                av = null
            }
        }
    }
    if (document.getElementById('60cf2c9800ea7b00015231a2')) {
        document.getElementById('60cf2c9800ea7b00015231a2').remove()
        if (!document.querySelector(`#${USER_PIN_ID}`)) {
            let destination = document.querySelector('.join-party_avatarWrapper__2WzCN')
            fillImage(destination, document, retrieveIdFromLink('/me/profile'), false, true)

        }
    }
}

//helpfunctions
function roundToNearest10(zahl) {
    if (zahl % 10 === 0) {
        return zahl + 10
    } else {
        return Math.ceil(zahl / 10) * 10
    }
}

function retrieveIdFromLink(link) {
    if (link.endsWith('/me/profile')) {
        const data = document.querySelector('#__NEXT_DATA__').text
        const json = JSON.parse(data)
        return json.props.middlewareResults[1].account.user.userId
    }
    return link.split('/').at(-1)
}

function isOtherProfile() {
    return pathMatches('user/.+')
}

function isOwnProfile() {
    return pathMatches('me/profile')
}

function isProfile() {
    return isOwnProfile() || isOtherProfile()
}

function isBattleRoyale() {
    return pathMatches('battle-royale/.+')
}

function isParty(){
    return pathMatches('party')
}

function isTeamDuel(){
    return pathMatches('team-duels/.+')
}

function isDuels() {
    return pathMatches('duels/.+')
}

async function getUserData(id) {
    const response = await fetch(`${GEOGUESSR_USER_ENDPOINT}/${id}`)
    const json = await response.json()
    return json
}

function wrapperFriend1(){
    return `<div class="styles_rectangle___6gqv" style="padding-top: 100%"><div class="styles_innerCircle__Y_L_e"><div class="styles_content__otIVG up__userPin"><img class="styles_image__8M_kp ${USER_PIN_CLASS}" loading="auto" style="object-fit: cover"></div></div></div>`
}

function wrapperFriend() {
    return `<div class="styles_circle__QFYEk  styles_variantFloating__Srm_N styles_colorTransparent__2bG5I styles_borderColorTransparent__CwSAk styles_borderSizeFactorZero__MEFpf"><div class="styles_rectangle___6gqv" style="padding-top: 100%"><div class="styles_innerCircle__Y_L_e"><div class="styles_content__otIVG up__"></div></div></div></div>`
}
function wrapperDuels() {
    return `<div class="profile-header_avatarWrapper__5_jDA up__"></div>`
}

function wrapperEndOfDuels() {
    return `<div class="profile-header_avatar__y6vsp up__"></div>`
}

function wrapper() {
    return `<div class="profile-header_avatar__y6vsp"><div class="avatar_titleAvatar__0pdL9"><div class="styles_rectangle___6gqv up__" style="padding-top: 100%"><div class="avatar_titleAvatarImage__A51Dx up__"></div></div></div></div>`
}

function profilePicture() {
    return `<img class="styles_image__8M_kp ${USER_PIN_CLASS}" loading="auto" style="object-fit:cover">`
}
function profilePicture1() {
    return `<img class="styles_image__8M_kp" id="${USER_PIN_ID}" loading="auto" style="object-fit:cover">`
}

function levelBorder() {
    return `<picture class="avatar_titleAvatarFrame__AT57_ ${USER_PIN_CLASS}"><source type="image/avif"><source type="image/webp"><img class="avatar_titleAvatarFrame__AT57_"></picture>`
}

function levelBorder1() {
    return `<picture class="avatar_titleAvatarFrame__AT57_" id="${USER_PIN_ID}"><source type="image/avif"><source type="image/webp"><img class="avatar_titleAvatarFrame__AT57_"></picture>`
}

const OBSERVER_CONFIG = {
    characterDataOldValue: false,
    subtree: true,
    childList: true,
    characterData: false,
}

const OBSERVER_CONFIG1 = {
    characterDataOldValue: false,
    subtree: true,
    childList: true,
    characterData: true,
    attributes:    true,
    attributeFilter: ["style"]
}

const ERROR_MESSAGE = (wrong) => '${wrong}'

function pathMatches(path) {
    return location.pathname.match(new RegExp(`^/(?:[^/]+/)?${path}$`))
}

const observer = new MutationObserver(onMutationsStandard)

observer.observe(document.body, OBSERVER_CONFIG)