您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Automatically adds all the Epix friends links from the gamescom discord server
当前为
// ==UserScript== // @name Epix Auto Friend // @namespace Violentmonkey Scripts // @match https://discord.com/channels/574865170694799400/1259933715409145966* // @inject-into content // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @version 1.4 // @author UpDownLeftDie // @contributionURL https://www.patreon.com/camkitties // @supportURL https://discord.gg/hWvWGUDf // @license MIT // @description Automatically adds all the Epix friends links from the gamescom discord server // ==/UserScript== let EPIX_IDS = []; let DISCORD_TOKEN = ''; let EPIX_FRIENDS = []; let EPIX_BUTTON; function main() { EPIX_FRIENDS = [...new Set(GM_getValue('epixFriends') || [])]; EPIX_IDS = [...new Set(GM_getValue('epixIds') || [])]; DISCORD_TOKEN = getDiscordToken(); console.log({EPIX_FRIENDS, EPIX_IDS, DISCORD_TOKEN: !!DISCORD_TOKEN}); EPIX_BUTTON = document.createElement('button'); EPIX_BUTTON.setAttribute('id', 'epixButton'); EPIX_BUTTON.setAttribute('type', 'button'); EPIX_BUTTON.innerHTML = 'Run Epix Friend Adder'; document.querySelector('h1').appendChild(EPIX_BUTTON); document.getElementById("epixButton").addEventListener("click", handleButton, false); } async function handleButton(event) { await getEpixIds(); let count = 0; disableButton('Running: added 0'); let messages = []; do { const minId = GM_getValue('discordMinId'); console.log({minId}); messages = await getDiscordMessages(minId); const codes = getEpixCodes(messages); console.log({messages, codes}); for(let i in codes) { const promises = EPIX_IDS.map(epixId => connectRequest(epixId, codes[i].code)); let responses = await Promise.all(promises).catch(err => { disableButton("ERROR"); throw err; }) let json = await responses[0].json(); let status = json?.data?.status.toUpperCase(); for(let i in responses) { const resp = responses[i]; console.log({resp}) if (!resp.ok) { if (resp.status === 400 || json?.message?.toLowerCase() === "user not found") { status = "USER_NOT_FOUND"; } else { disableButton("ERROR"); throw resp; } } } count++; disableButton(`Running: added ${count}`); updateFriends(status, codes[i]); } if (messages.length > 0) { GM_setValue('discordMinId', messages[messages.length - 1][0].id); } } while(messages.length >= 25); disableButton("Done!"); } async function getEpixIds() { return new Promise((resolve, reject) => { const inputValue = EPIX_IDS?.length ? EPIX_IDS.join(',') : ''; const dialog = document.createElement('dialog'); dialog.setAttribute('open', true); dialog.setAttribute('id', 'epixIdsDialog') dialog.innerHTML = ` <p>Enter your Epix user id(s) (<strong>NOT the same as your invite id!</strong>)</p> To get this go to your <a href="https://www.gamescom.global/en/epix" target="_blank">profile</a>: <ol> <li>open dev tools</li> <li>refresh the page</li> <li>look at network requests for "user?userId=XXXXXXX"</li> </ol> <form method="dialog"> <label for="epixIds">Epix Id(s):</label> <input required id="epixIds" placeholder="b5629b160f555ab4b08ef8e49568b7dd, a49f9b160f555vd4b08ef8e49568b7a2" value="${inputValue}" /> <label for="discordToken">Discord token:</label> <span style="display:flex;"> <input required id="discordToken" style="flex: 1 0 0;" placeholder="MTMkaJ9HshK2dAx.Fna4Jk.qsKrjSk42jKns9Js32-G3pnH_qcnIskQzy" value="${DISCORD_TOKEN ? DISCORD_TOKEN : ''}" /> <button id="getDiscordToken" ${!!DISCORD_TOKEN ? "disabled" : null}>Get</button> </span> <button id="epixIdAddButton" type="submit">START</button> <span id="epixError" style="color: red; font-weight: bold;"></span> </form> `; document.body.appendChild(dialog); document.getElementById("getDiscordToken").addEventListener("click", (e) => { e.preventDefault(); DISCORD_TOKEN = getDiscordToken(); document.getElementById("discordToken").value = DISCORD_TOKEN; }); document.getElementById("epixIdAddButton").addEventListener("click", (e) => { e.preventDefault(); const idStr = document.getElementById("epixIds").value; const ids = idStr.split(',').reduce((acc, curr) => { const id = curr.replace(/\W/gi, ''); if (!id) return acc; acc.push(id); return acc; }, []); EPIX_IDS = ids; if(!DISCORD_TOKEN || !EPIX_IDS?.length) { document.getElementById("epixError").innerHTML = "ERROR: missing Epix User ID(s) or Discord Token"; return; } GM_setValue('epixIds', EPIX_IDS); dialog.parentNode.removeChild(dialog); resolve(); }, false); }); } function getEpixCodes(discordMessages) { const codes = discordMessages.reduce((acc, curr) => { const message = curr[0] const matches = message.content.matchAll(/epix-connect=([\w\d]{7})/ig); for (const match of matches) { if (match?.[1] && !EPIX_FRIENDS.includes(match[1])) { acc.push({code: match[1], messageId: message.id}); } } return acc; }, []) return codes; } function updateFriends(status, code) { if (status === "CONNECTION_SUCCESSFUL" || status === "ALREADY_MATCHED" || status === "USER_NOT_FOUND") { EPIX_FRIENDS.push(code.code); GM_setValue('epixFriends', EPIX_FRIENDS); GM_setValue('discordMinId', code.messageId); } } /** * @param {string} [text] - Button text * @returns {null} */ function disableButton(text = '') { EPIX_BUTTON.toggleAttribute('disabled', true); if (text) { updateButton(text); } } /** * @param {string} [text] - Button text * @returns {null} */ function enableButton(text = '') { EPIX_BUTTON.toggleAttribute('disabled', false); if (text) { updateButton(text); } } /** * @param {string} [text] - Button text * @returns {null} */ function updateButton(text = "Run Epix Friend Adder") { EPIX_BUTTON.innerHTML = text; } //--- Style our newly added elements using CSS. GM_addStyle (` #epixButton { margin-left: 10px; } #epixIdsDialog { position: absolute; top: 5rem; z-index: 100; } #epixIdsDialog ol { list-style: auto; padding-left: 35px; margin-bottom: 10px; } #epixIdsDialog input { width: 100%; } #epixIdsDialog button { margin: 5px auto; display: block; font-size: large; background: greenyellow; padding: 2px 10px; } `); async function connectRequest(userId, profileId) { return fetch("https://wfppjum4x2.execute-api.eu-central-1.amazonaws.com/production/connection-request", { "referrer": "https://www.gamescom.global/", "referrerPolicy": "strict-origin-when-cross-origin", "body": `{"userId":"${userId}","profileId":"${profileId}"}`, "method": "POST", "mode": "cors", "credentials": "omit" }); } // A lot of this function was adapted from: https://github.com/victornpb/undiscord/blob/master/deleteDiscordMessages.user.js#L652-L712 async function getDiscordMessages(minId) { const params = queryString([ ['limit', 25], ['channel_id', '1259933715409145966'], ['min_id', minId], ['sort_by', 'timestamp'], ['sort_order', 'asc'], ['has','link'], ]); let resp; try { resp = await fetch(`https://discord.com/api/v9/guilds/574865170694799400/messages/search?${params}`, { "headers": { "accept": "*/*", "authorization": DISCORD_TOKEN }, "referrer": "https://discord.com/channels/574865170694799400/1259933715409145966", "referrerPolicy": "strict-origin-when-cross-origin", "method": "GET", "mode": "cors", "credentials": "include" }); } catch (err) { this.state.running = false; console.error('Search request threw an error:', err); disableButton("ERROR"); throw err; } // not indexed yet if (resp.status === 202) { let w = (await resp.json()).retry_after * 1000 || 30 * 1000; console.warn(`This channel isn't indexed yet. Waiting ${w}ms for discord to index it...`); await wait(w); return await getDiscordMessages(minId); } if (!resp.ok) { // searching messages too fast if (resp.status === 429) { let w = (await resp.json()).retry_after * 1000 || 30 * 1000; console.warn(`Being rate limited by the API for ${w}ms! Increasing search delay...`); console.warn(`Cooling down for ${w * 2}ms before retrying...`); await wait(w * 2); return await getDiscordMessages(minId); } else { console.error(`Error searching messages, API responded with status ${resp.status}!\n`, await resp.json()); disableButton("ERROR"); throw resp; } } const data = await resp.json(); return data.messages; } function getDiscordToken() { window.dispatchEvent(new Event('beforeunload')); const LS = document.body.appendChild(document.createElement('iframe')).contentWindow.localStorage; try { return JSON.parse(LS.token); } catch { console.info('Could not automatically detect Authorization Token in local storage!'); console.info('Attempting to grab token using webpack'); return (window.webpackChunkdiscord_app?.push([[''], {}, e => { window.m = []; for (let c in e.c) window.m.push(e.c[c]); }]), window.m)?.find(m => m?.exports?.default?.getToken !== void 0).exports.default.getToken(); } } const wait = async ms => new Promise(done => setTimeout(done, ms)); const queryString = params => params.filter(p => p[1] !== undefined).map(p => p[0] + '=' + encodeURIComponent(p[1])).join('&'); (new MutationObserver(check)).observe(document, {childList: true, subtree: true}); function check(changes, observer) { if(document.querySelector('h1')) { observer.disconnect(); main(); } }