mod.reddit.com revived

Revive mod.reddit.com

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         mod.reddit.com revived
// @namespace    https://greatest.deepsurf.us/en/users/1574555-littux
// @version      1.3
// @description  Revive mod.reddit.com
// @author       littux
// @match        https://mod-reddit-com.netlify.app/*
// @match        http://localhost:44444/*
// @match        https://*.reddit.com/mail/*
// @connect      www.reddit.com
// @connect      sh.reddit.com
// @connect      gql-fed.reddit.com
// @icon         https://www.redditstatic.com/modmail/favicon/android-icon-192x192.png
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_xmlhttpRequest
// @grant        GM_cookie
// @grant        GM_registerMenuCommand
// @run-at       document-start
// @license      MIT
// ==/UserScript==

const defaultConfig = { useLocalhost: false, loadTrophyData: false };
const configData = GM_getValue("config");
if (!configData) {
	GM_setValue("config", defaultConfig)
}
let { useLocalhost, loadTrophyData } = configData ?? defaultConfig;


if (location.hostname.endsWith("reddit.com")) {
	window.stop();
	location.replace((useLocalhost ? "http://localhost:44444" : "https://mod-reddit-com.netlify.app") + location.pathname + location.search + location.hash);
	return;
};

(async function() {
	GM_registerMenuCommand(`[${useLocalhost ? 'YES' : 'NO'}] Use localhost`, () => {
		useLocalhost = !useLocalhost;
		GM_setValue("config", { useLocalhost, loadTrophyData });
		location.reload();
	});
	GM_registerMenuCommand(`[${loadTrophyData ? 'YES' : 'NO'}] Load trophy data (not recommended; delays user pane load)`, () => {
		loadTrophyData = !loadTrophyData;
		GM_setValue("config", { useLocalhost, loadTrophyData });
		location.reload();
	});


	const gmFetch = (options) => {
		return new Promise((resolve, reject) => {
			GM_xmlhttpRequest({
				...options,
				onload: (res) => resolve(res),
				onerror: (err) => reject(err)
			});
		});
	}

	const getExternalCookie = (name, url) => {
		return new Promise((resolve, reject) => {
			if (typeof window.GM_cookie !== 'undefined') {
				GM_cookie.list({ name, url }, (cookies, error) => {
					if (error) reject(error);
					else resolve(cookies.length > 0 ? cookies[0].value : null);
				});
			} else {
				reject("GM_cookie not supported. Ensure you are using Tampermonkey.");
			}
		});
	};

	unsafeWindow.tokenCache = null, pendingTokenPromise = null;
	const _getToken = async () => {
		try {
			const loid = await getExternalCookie("loid", "https://www.reddit.com/mail/");
			if (!loid) {
				throw new Error("loid cookie missing");
			}

			const tokenKey = `rAPI:accessToken~${loid}`;
			const expiryKey = `rAPI:accessTokenExpiry~${loid}`;

			const cachedToken = GM_getValue(tokenKey);
			const cachedExpiry = GM_getValue(expiryKey);
			if (cachedToken && cachedExpiry > Date.now()) {
				unsafeWindow.tokenCache = { token: cachedToken, expires: cachedExpiry };
				return cachedToken;
			}

			let csrf = await getExternalCookie("csrf_token", "https://sh.reddit.com/mail/");
			if (!csrf) {
				await gmFetch({
					method: "HEAD",
					url: "https://sh.reddit.com/404",
					withCredentials: true
				});
				csrf = await getExternalCookie("csrf_token", "https://sh.reddit.com/mail/");
			};

			try {
				const response = await gmFetch({
					method: "POST",
					url: "https://www.reddit.com/svc/shreddit/token",
					withCredentials: true,
					headers: {
						"Content-Type": "application/json",
						"Origin": "https://www.reddit.com",
						"Referer": "https://www.reddit.com/"
					},
					data: JSON.stringify({ csrf_token: csrf })
				});

				if (response.status === 200) {
					const data = JSON.parse(response.responseText);
					GM_setValue(tokenKey, data.token);
					GM_setValue(expiryKey, data.expires);
					unsafeWindow.tokenCache = data;
					return data.token;
				}
			} catch (e) {
				throw new Error("Token fetch failed: " + e);
			}
		} catch (e) {
			throw new Error("Error getting token: " + e);
		} finally {
			pendingTokenPromise = null
		}
	};

	const getToken = () => {
		if (unsafeWindow.tokenCache?.expires > Date.now()) return unsafeWindow.tokenCache.token;
		if (!pendingTokenPromise) pendingTokenPromise = _getToken();
		return pendingTokenPromise;
	};

	unsafeWindow.gmFetch = gmFetch;
	unsafeWindow.getToken = getToken;


	const eventData = {
		detail: { accessToken: await getToken() }
	};

	console.log("rAPI access token ready.");

	unsafeWindow.__MODREDDITCOM_REVIVED__ = {
		version: 1,
		useLocalhost,
		loadTrophyData,
		event: eventData
	};

	const event = new CustomEvent('rAPI-accessToken-ready', eventData);
	window.dispatchEvent(event);
})();