SteamTrade Matcher Bulk Notification Deleter

Select notifications via UI, navigate to each page, fetch token, send POST, and continue sequentially

// ==UserScript==
// @name         SteamTrade Matcher Bulk Notification Deleter
// @namespace    typpi.online
// @version      2.8
// @description  Select notifications via UI, navigate to each page, fetch token, send POST, and continue sequentially
// @author       Nick2bad4u
// @match        *://*.steamtradematcher.com/*
// @grant        none
// @license      Unlicense
// @homepageURL  https://github.com/Nick2bad4u/UserStyles
// @supportURL   https://github.com/Nick2bad4u/UserStyles/issues
// @icon         https://www.google.com/s2/favicons?sz=64&domain=steamtradematcher.com
// @icon64       https://www.google.com/s2/favicons?sz=64&domain=steamtradematcher.com
//
// ==/UserScript==

(function () {
	'use strict';

	let selectedNotifications = JSON.parse(sessionStorage.getItem('selectedNotifications')) || [];
	let currentIndex = parseInt(localStorage.getItem('currentIndex'), 10) || 0;
	let isProcessing = false;

	// Create a UI panel
	const controlPanel = document.createElement('div');
	controlPanel.style.cssText = `
			position: fixed;
			bottom: 10px;
			right: 10px;
			background-color: #333;
			color: #fff;
			border: 1px solid #444;
			padding: 10px;
			z-index: 10000;
			width: 200px;
			height: 30px; /* Start minimized */
	`;

	// Add a class for minimized state
	controlPanel.classList.add('minimized');

	// Add CSS for minimized state
	const style = document.createElement('style');
	style.innerHTML = `
			.minimized {
					background-color: transparent !important;
					border: none !important;
					padding: 0 !important;
			}
	`;
	document.head.appendChild(style);
	controlPanel.innerHTML = `
			<h4 style="margin: 0; display: none;">Notification Deletion Tool</h4>
			<div style="display: none;">
					<button id="selectAllBtn">Select All</button>
					<button id="deselectAllBtn">Deselect All</button>
			</div>
			<button id="startProcessBtn" style="margin-top: 10px; display: none;">Start Deletion</button>
			<button id="stopProcessBtn" style="margin-top: 10px; display: none;">Stop Deletion</button>
			<div id="status" style="margin-top: 10px; font-size: 12px; display: none;"></div>
			<button id="minimizeBtn" style="position: absolute; top: 5px; right: 5px; display: none;">-</button>
			<button id="maximizeBtn" style="position: absolute; top: 5px; right: 5px;">+</button>
	`;
	document.body.appendChild(controlPanel);

	// Add checkboxes to each notification
	const notifications = document.querySelectorAll('a[id^="notification_"]');
	notifications.forEach((notification) => {
		const checkbox = document.createElement('input');
		checkbox.type = 'checkbox';
		checkbox.style.marginRight = '5px';

		const parent = notification.parentNode;
		parent.insertBefore(checkbox, notification);
	});

	// Event listener for "Select All" button
	document.getElementById('selectAllBtn').addEventListener('click', () => {
		notifications.forEach((notification) => {
			notification.previousSibling.checked = true; // Check all checkboxes
		});
	});

	// Event listener for "Deselect All" button
	document.getElementById('deselectAllBtn').addEventListener('click', () => {
		notifications.forEach((notification) => {
			notification.previousSibling.checked = false; // Uncheck all checkboxes
		});
	});

	// Event listener for "Start Deletion" button
	document.getElementById('startProcessBtn').addEventListener('click', () => {
		selectedNotifications = Array.from(notifications)
			.filter(
				(notification) => notification.previousSibling.checked, // Get selected notifications
			)
			.map((notification) => notification.href); // Get the href attribute
		currentIndex = 0; // Reset currentIndex to 0
		isProcessing = true;

		if (selectedNotifications.length === 0) {
			alert('No notifications selected. Please check the boxes next to the notifications you want to delete.');
			return;
		}

		if (confirm(`Are you sure you want to delete ${selectedNotifications.length} notification(s)?`)) {
			document.getElementById('status').textContent = `Deleting ${selectedNotifications.length} notification(s)...`;
			sessionStorage.setItem('selectedNotifications', JSON.stringify(selectedNotifications));
			sessionStorage.setItem('currentIndex', currentIndex);
			navigateToNotification(selectedNotifications[currentIndex]);
		}
	});

	// Event listener for "Stop Deletion" button
	document.getElementById('stopProcessBtn').addEventListener('click', () => {
		isProcessing = false;
		document.getElementById('status').textContent = 'Deletion process stopped.';
	});

	// Event listener for "Minimize" button
	document.getElementById('minimizeBtn').addEventListener('click', () => {
		controlPanel.style.height = '30px';
		controlPanel.querySelectorAll('h4, div, #startProcessBtn, #stopProcessBtn, #status, #minimizeBtn').forEach((el) => (el.style.display = 'none'));
		document.getElementById('maximizeBtn').style.display = 'block';
	});

	// Event listener for "Maximize" button
	document.getElementById('maximizeBtn').addEventListener('click', () => {
		controlPanel.style.height = 'auto';
		controlPanel.querySelectorAll('h4, div, #startProcessBtn, #stopProcessBtn, #status, #minimizeBtn').forEach((el) => (el.style.display = 'block'));
		document.getElementById('maximizeBtn').style.display = 'none';
	});

	// Navigate to the notification page
	function navigateToNotification(link) {
		if (isProcessing) {
			console.log(`Navigating to ${link}`);
			window.location.href = link;
		}
	}

	// Fetch token and delete the notification directly via POST
	function deleteNotificationDirectly() {
		const tokenElement = document.querySelector('input[name="_token"]'); // Fetch CSRF token
		const deleteForm = document.querySelector('form[action*="/notification/delete/"]');

		if (tokenElement && deleteForm) {
			const token = tokenElement.value; // Get token value
			const actionUrl = deleteForm.getAttribute('action'); // Get delete URL

			fetch(actionUrl, {
				method: 'POST',
				headers: {
					'Content-Type': 'application/x-www-form-urlencoded',
				},
				body: `_token=${encodeURIComponent(token)}`,
			})
				.then((response) => {
					if (response.ok) {
						console.log(`Notification deleted successfully!`);
					} else {
						console.error(`Failed to delete notification.`);
					}
				})
				.catch((error) => {
					console.error('Error:', error);
				})
				.finally(() => {
					// Move to the next notification or finish
					currentIndex++;
					localStorage.setItem('currentIndex', currentIndex);
					if (currentIndex < selectedNotifications.length && isProcessing) {
						setTimeout(() => {
							navigateToNotification(selectedNotifications[currentIndex]);
						}, 1000); // Delay for navigation
					} else {
						sessionStorage.removeItem('selectedNotifications');
						sessionStorage.removeItem('currentIndex');
						alert('All selected notifications deleted.');
						console.log('Completed deleting all selected notifications.');
						window.location.href = '/notification'; // Return to notifications list
					}
				});
		} else {
			console.error('Token or delete form not found!');
			// Skip to the next notification if the form is missing
			currentIndex++;
			if (currentIndex < selectedNotifications.length && isProcessing) {
				setTimeout(() => {
					navigateToNotification(selectedNotifications[currentIndex]);
				}, 1000); // Delay for navigation
			} else {
				alert('All selected notifications deleted.');
				console.log('Completed deleting all selected notifications.');
				window.location.href = '/notification'; // Return to notifications list
			}
		}
	}

	// Check if on a notification page and trigger deletion
	if (window.location.pathname.includes('/notification/show/')) {
		setTimeout(deleteNotificationDirectly, 1000); // Add a delay before calling deleteNotificationDirectly
	}
})();