SGTools Notifier

Notifies the user when a SGTools rules check is complete and optionally redirects to the giveaway.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==UserScript==
// @name SGTools Notifier
// @namespace https://rafaelgssa.gitlab.io/monkey-scripts
// @version 5.0.1
// @author rafaelgssa
// @description Notifies the user when a SGTools rules check is complete and optionally redirects to the giveaway.
// @match https://www.sgtools.info/*
// @match https://www.steamgifts.com/giveaway/*
// @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
// @require https://greatest.deepsurf.us/scripts/405813-monkey-utils/code/Monkey%20Utils.js?version=821710
// @require https://greatest.deepsurf.us/scripts/405802-monkey-dom/code/Monkey%20DOM.js?version=821769
// @require https://greatest.deepsurf.us/scripts/405831-monkey-storage/code/Monkey%20Storage.js?version=821709
// @require https://greatest.deepsurf.us/scripts/405840-monkey-wizard/code/Monkey%20Wizard.js?version=821711
// @run-at document-idle
// @grant GM.info
// @grant GM.setValue
// @grant GM.getValue
// @grant GM.deleteValue
// @grant GM_info
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @noframes
// ==/UserScript==

/* global DOM, PersistentStorage, SettingsWizard */

(async () => {
	'use strict';

	const scriptId = 'sgtn';
	const scriptName = GM.info.script.name;

	const schemas = /** @type {WizardSchema[]} */ ([
		{
			type: 'multi',
			id: 'doRedirect',
			message: 'Do you want to be redirected to the giveaway when the check is complete?',
			defaultValue: false,
			choices: [
				{
					id: 'y',
					template: "'%' for yes",
					value: true,
				},
				{
					id: 'n',
					template: "'%' for no",
					value: false,
				},
			],
		},
	]);

	const defaultValues = /** @type {StorageValues} */ ({
		settings: Object.fromEntries(schemas.map((schema) => [schema.id, schema.defaultValue])),
	});

	const isInSgTools = window.location.hostname === 'www.sgtools.info';

	let doRedirect = false;

	/** @type {HTMLElement | null} */
	let checkButton;

	/**
	 * Loads the script.
	 * @returns {Promise<void>}
	 */
	const load = async () => {
		if (!isInSgTools) {
			return removePageUrlFragment();
		}
		checkButton = document.querySelector('#check');
		if (!checkButton) {
			return;
		}
		doRedirect = /** @type {boolean} */ (await PersistentStorage.getSetting('doRedirect'));
		checkButton.addEventListener('click', waitForRulesCheck);
	};

	/**
	 * Removes the fragment from the page URL and notifies the user if exists.
	 */
	const removePageUrlFragment = () => {
		if (window.location.hash !== `#${scriptId}`) {
			return;
		}
		window.history.replaceState(
			'',
			document.title,
			`${window.location.origin}${window.location.pathname}${window.location.search}`
		);
		notifyUser(true);
	};

	/**
	 * Waits until the check is complete and notifies the user.
	 * @returns {Promise<void>}
	 */
	const waitForRulesCheck = async () => {
		if (!checkButton) {
			return;
		}
		const element = await DOM.dynamicQuerySelector('#getlink, #error_alert:not(.hidden)', 60 * 30);
		if (!element) {
			// Rules have not been checked after 30 minutes.
			return;
		}
		if (!element.matches('#getlink')) {
			// User failed to pass the rules.
			return notifyUser(false);
		}
		if (!doRedirect) {
			return notifyUser(true);
		}
		checkButton.removeEventListener('click', waitForRulesCheck);
		checkButton.dispatchEvent(new MouseEvent('click', { bubbles: true }));
		return waitForGiveawayLink();
	};

	/**
	 * Waits for the giveaway link, redirects to the giveaway and notifies the user.
	 * @returns {Promise<void>}
	 */
	const waitForGiveawayLink = async () => {
		const link = /** @type {HTMLAnchorElement | undefined} */ (await DOM.dynamicQuerySelector(
			'#gaurl a'
		));
		if (link) {
			window.location.href = `${link.href}#${scriptId}`;
		} else {
			notifyUser(true);
		}
	};

	/**
	 * Notifies the user.
	 * @param {boolean} isSuccess Whether the user passed the rules or not.
	 */
	const notifyUser = (isSuccess) => {
		const [emoji, message] = isSuccess
			? ['✔️', 'You passed the rules!']
			: ['❌', 'You failed to pass the rules.'];
		if (isInSgTools) {
			document.title = `${emoji} ${document.title}`;
		}
		if (document.hidden) {
			// Only show a browser notification if the user is away from the tab.
			showBrowserNotification(`${emoji} ${message}`);
		}
	};

	/**
	 * Shows a browser notification.
	 * @param {string} body The message to show.
	 * @return {Promise<void>}
	 */
	const showBrowserNotification = async (body) => {
		if (Notification.permission !== 'granted') {
			await Notification.requestPermission();
		}
		if (Notification.permission === 'granted') {
			new Notification(scriptName, { body });
		}
	};

	try {
		await PersistentStorage.init(scriptId, defaultValues);
		await SettingsWizard.init(scriptId, scriptName, schemas);
		await load();
	} catch (err) {
		console.log(`Failed to load ${scriptName}: `, err);
	}
})();