您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Allows you to use many custom emotes and some commands in the Geoguessr chat
当前为
- // ==UserScript==
- // @name Geoguessr Custom Emotes
- // @description Allows you to use many custom emotes and some commands in the Geoguessr chat
- // @version 2.1.0
- // @author victheturtle#5159
- // @license MIT
- // @match https://www.geoguessr.com/*
- // @icon https://www.geoguessr.com/_next/static/images/emote-gg-cf17a1f5d51d0ed53f01c65e941beb6d.png
- // @namespace https://gf.qytechs.cn/users/967692-victheturtle
- // ==/UserScript==
- let geoguessrCustomEmotes = {};
- const customEmotesInjectedClass = "custom-emotes-injected";
- const getAllNewMessages = () => document.querySelectorAll(`div[class*="chat-message_messageContent__"]:not([class*="${customEmotesInjectedClass}"])`);
- const _cndic = {};
- const hrefset = new Set();
- async function scanStyles() {
- for (let node of document.querySelectorAll('head link[rel="preload"], head style[data-n-href*=".css"]')) {
- const href = node.href || location.origin+node.dataset.nHref;
- if (hrefset.has(href)) continue;
- hrefset.add(href);
- await fetch(href)
- .then(res => res.text())
- .then(stylesheet => {
- for (let className of stylesheet.split(".")) {
- const ind = className.indexOf("__");
- if (ind != -1) _cndic[className.substr(0, ind+2)] = className.substr(0, ind+7);
- };
- });
- };
- }
- const cn = (classNameStart) => _cndic[classNameStart]; // cn("status_section__") -> "status_section__8uP8o"
- const emoteInjectionTemplate = (emoteSrc) => `</span>
- <span class="${cn("chat-message_emoteWrapper__")}"><img src="${emoteSrc}" class="${cn("chat-message_messageEmote__")}"></span>
- <span class="${cn("chat-message_messageText__")}">`;
- async function fetchWithCors(url, method, body) {
- return await fetch(url, {
- "headers": {
- "accept": "*/*",
- "accept-language": "en-US,en;q=0.8",
- "content-type": "application/json",
- "sec-fetch-dest": "empty",
- "sec-fetch-mode": "cors",
- "sec-fetch-site": "same-site",
- "sec-gpc": "1",
- "x-client": "web"
- },
- "referrer": "https://www.geoguessr.com/",
- "referrerPolicy": "strict-origin-when-cross-origin",
- "body": (method == "GET") ? null : JSON.stringify(body),
- "method": method,
- "mode": "cors",
- "credentials": "include"
- });
- };
- const getGameId = () => location.pathname.split("/")[2];
- const getPartyId = async () => await fetchWithCors(getLobbyApi(getGameId()), "POST", {})
- .then(it => it.json()).then(it => it.partyId);
- const getPlayerId = async (nick) => await fetchWithCors(getLobbyApi(getGameId()), "POST", {})
- .then(it => it.json()).then(it => it.players.filter(it => it.nick == nick)[0].playerId);
- const getLobbyApi = (gameId) => `https://game-server.geoguessr.com/api/lobby/${gameId}/join`;
- const getKickApi = (gameId) => `https://game-server.geoguessr.com/api/lobby/${gameId}/kick`;
- const getBanApi = (partyId) => `https://www.geoguessr.com/api/v4/parties/${partyId}/ban`;
- const getRoundNumberApi = (gameId) => `https://game-server.geoguessr.com/api/duels/${gameId}/`;
- const getRoundNumber = async () => await fetchWithCors(getRoundNumberApi(getGameId()), "GET")
- .then(it => it.json()).then(it => it.currentRoundNumber);
- const getGuessApi = (gameId) => `https://game-server.geoguessr.com/api/duels/${gameId}/guess`;
- async function ban(nick) {
- const playerId = await getPlayerId(nick);
- const partyId = await getPartyId();
- fetchWithCors(getKickApi(getGameId()), "POST", {playerId: playerId}).catch(e => console.log(e));
- fetchWithCors(getBanApi(partyId), "POST", {userId: playerId, ban: true}).catch(e => console.log(e));
- };
- async function unban(nick) {
- const playerId = await getPlayerId(nick);
- const partyId = await getPartyId();
- fetchWithCors(getBanApi(partyId), "POST", {userId: playerId, ban: false}).catch(e => console.log(e));
- };
- async function openProfile(nick) {
- const playerId = await getPlayerId(nick);
- window.open("/user/"+playerId);
- };
- async function guessEiffelTower() {
- const rn = await getRoundNumber();
- fetchWithCors(getGuessApi(getGameId()), "POST", {"lat": 48.85837, "lng": 2.29448, "roundNumber": rn}).catch(e => console.log(e));
- };
- function handleCommand(type, args, isSelf) {
- try {
- if (type == "/ban") {
- if (args.length != 0 && isSelf) ban(args);
- } else if (type == "/unban") {
- if (args.length != 0 && isSelf) unban(args);
- } else if (type == "/mute") {
- if (args.length != 0 && isSelf) localStorage.setItem("CustomEmotesMuted"+args, "1");
- } else if (type == "/unmute") {
- if (args.length != 0 && isSelf) localStorage.setItem("CustomEmotesMuted"+args, "0");
- } else if (type == "/check") {
- if (args.length != 0 && isSelf) openProfile(args);
- } else if (type == "/eiffel") {
- if (location.pathname.includes("duel") && isSelf) guessEiffelTower();
- }
- } catch (e) { console.log(e); };
- };
- let observer = new MutationObserver((mutations) => {
- const newMessages = getAllNewMessages();
- if (newMessages.length == 0) return;
- scanStyles().then(() => {
- for (let message of newMessages) {
- let words = message.innerHTML.split(/((?:<|>|<|>|,| |\.)+)/g);
- const author = words[6];
- const isSelf = message.parentNode.className.includes("isSelf");
- if (!isSelf && localStorage.getItem("CustomEmotesMuted"+author) == "1") {
- message.innerHTML = words.slice(0, 14).join("") + "[Muted]" + words.slice(words.length-4).join("");
- } else if (words.length >= 19 && words[14][0] == "/") {
- handleCommand(words[14], words.slice(16, words.length-4).join(""), isSelf)
- } else {
- for (let i=0; i<words.length; i+=2) {
- if (words[i] == "") continue;
- const lowercaseWord = words[i].toLowerCase();
- for (let emoteName in geoguessrCustomEmotes) {
- if (lowercaseWord == emoteName.toLowerCase()) {
- words[i] = emoteInjectionTemplate(geoguessrCustomEmotes[emoteName]);
- break;
- }
- }
- }
- message.innerHTML = words.join("");
- }
- message.classList.add(customEmotesInjectedClass);
- }})
- });
- async function fetchEmotesRepository() {
- const lastTimeFetched = localStorage.getItem("CustomEmotesLastFetched")*1
- if (Date.now() - lastTimeFetched < 60*1000) { // Github API has a limit rate of 60 requests per hour so prevent more than 1 request per minute
- return localStorage.getItem("CustomEmotesStored")
- } else {
- const emotesRepositoryContent = await fetch("https://api.github.com/gists/7e5046589b0f020c1ec80629c582cca6")
- .then(it => it.json())
- .then(it => it.files["GeoguessrCustomEmotesRepository.json"].content);
- localStorage.setItem("CustomEmotesStored", emotesRepositoryContent);
- localStorage.setItem("CustomEmotesLastFetched", Date.now());
- return emotesRepositoryContent;
- }
- }
- (() => {
- fetchEmotesRepository().then(emotesRepositoryContent => {
- geoguessrCustomEmotes = JSON.parse(emotesRepositoryContent);
- observer.observe(document.body, { subtree: true, childList: true });
- }).catch(err => console.log(`Geoguessr Custom Emotes error at fetchEmotesRepository(): ${err}`));
- })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址