// ==UserScript==
// @name Randomus.ru
// @version 0.4
// @match *://randomus.ru/*
// @run-at document-start
// @description Predictable number generation for Randomus.ru (https://randomus.ru)
// @author Kaimi
// @homepage https://kaimi.io/
// @license GPLv3
// @namespace https://greasyfork.org/users/228137
// ==/UserScript==
// Initialize or retrieve persisted desired_numbers array
let storedNumbers = localStorage.getItem("desired_numbers");
let desired_numbers = storedNumbers ? JSON.parse(storedNumbers) : [62, 2, 39, 52, 335, 264];
var debug = false;
const selector = "p.subtitle.is-7.mb-3";
var num_from;
var num_to;
var button;
var file_path = undefined;
// Ensure the menu item is added after page load
window.addEventListener("load", () => {
addMenuItem();
num_from = document.getElementById('num_from').value;
num_to = document.getElementById('num_to').value;
button = document.getElementsByTagName('button')[0];
onPageLoad();
}, false);
// Function to insert new menu item in the navbar (both desktop and mobile)
function addMenuItem() {
// Check for desktop version (.navbar-dropdown)
var dropdown = document.querySelector('.navbar-dropdown');
if (dropdown) {
// Desktop version found, add item to the dropdown
insertMenuItem(dropdown);
} else {
// Check for mobile version (.navbar-menu and .navbar-burger)
var burgerMenu = document.querySelector('.navbar-burger');
var mobileMenu = document.querySelector('.navbar-menu');
if (burgerMenu && mobileMenu) {
// Ensure mobile menu is visible before adding menu item
burgerMenu.addEventListener('click', () => {
setTimeout(() => {
insertMenuItem(mobileMenu);
}, 500); // Small delay to allow the menu to fully load
});
} else {
console.error('Neither desktop nor mobile menu found.');
}
}
}
// Function to create and insert the new menu item
function insertMenuItem(parentElement) {
// Create the new menu item
const newMenuItem = document.createElement('a');
newMenuItem.classList.add('navbar-item');
newMenuItem.innerHTML = `
<span class="icon">
<svg class="svg-inline--fa fa-pencil-alt" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="pencil-alt" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" data-fa-i2svg="">
<path fill="currentColor" d="M497.9 142.1l-46.1-46.1c-12.5-12.5-32.8-12.5-45.3 0l-56 56 91.4 91.4 56-56c12.5-12.6 12.5-32.8 0-45.3zm-124 124L130.6 510.3c-3.6 3.6-8.2 6.4-13.3 8.1L9.4 511.9c-10.9 3.6-22.3-7.7-18.7-18.7l7.4-107.9c1.6-5.1 4.4-9.7 8.1-13.3L245.9 138l91.4 91.4-63.7 63.7 71.9 71.9z"></path>
</svg>
</span> Edit Desired Numbers`;
// Add click event to prompt for editing desired_numbers
newMenuItem.addEventListener('click', () => {
const input = prompt("Enter the desired numbers (comma-separated):", desired_numbers.join(", "));
if (input !== null) {
desired_numbers = input.split(',').map(Number).filter(n => !isNaN(n));
localStorage.setItem("desired_numbers", JSON.stringify(desired_numbers));
alert("Desired numbers updated!");
}
});
// Append the new item to the parent element
parentElement.appendChild(newMenuItem);
}
async function onPageLoad() {
// Replace first-pass image
var resultImg = document.getElementById('result_main_image');
if (resultImg && desired_numbers.length > 0) {
show_loader("Генерируем результаты");
resultImg.src = '';
// Actual image
await replaceImage();
hide_loader();
}
}
async function replaceImage() {
if (debug) {
logVariable({ desired_numbers });
logVariable({ num_from });
logVariable({ num_to });
}
var resultImg = document.getElementById('result_main_image');
if (resultImg) {
// If there's an existing blob URL, revoke it
if (resultImg.src.startsWith('blob:')) {
URL.revokeObjectURL(resultImg.src);
}
var number = desired_numbers.shift();
localStorage.setItem("desired_numbers", JSON.stringify(desired_numbers));
var imageSrc = await processImages(number, num_from, num_to);
resultImg.src = imageSrc;
// Add event listener to clean up the blob URL when the image is no longer needed
resultImg.onload = () => {
resultImg.classList.remove('fade');
// Optional: if you want to revoke immediately after load
// URL.revokeObjectURL(imageSrc);
};
appendTimestampToElement(file_path, selector);
}
}
async function fetchHtml(url) {
const response = await fetch(url);
const text = await response.text();
const parser = new DOMParser();
return parser.parseFromString(text, 'text/html');
}
function extractImagePath(doc) {
const aTag = doc.querySelector('a[download]') || {};
return aTag.href || null;
}
function loadImage(url) {
if (debug) {
logVariable({ url });
}
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
});
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function processImages(want, from, to) {
// Construct URLs for fetching the images
const firstUrl = '/quick?from=' + want + '&to=' + want + '&count=1';
const secondUrl = '/quick?from=' + from + '&to=' + to + '&count=1';
// Optionally log URLs for debugging
if (debug) {
logVariable({ firstUrl });
logVariable({ secondUrl });
}
// Some delay because of the server
await delay(1000);
// Fetch and load the first image
let doc = await fetchHtml(firstUrl);
let imagePath = extractImagePath(doc);
const firstImage = await loadImage(imagePath);
file_path = imagePath;
// Fetch and load the second image
doc = await fetchHtml(secondUrl);
imagePath = extractImagePath(doc);
const secondImage = await loadImage(imagePath);
// Create a canvas element and get the drawing context
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Set canvas dimensions
const width = firstImage.width;
const height = firstImage.height;
canvas.width = width;
canvas.height = height;
// Draw the first image from height 0 to 400
ctx.drawImage(firstImage, 0, 0, width, 400, 0, 0, width, 400);
// Draw the second image from height 400 to 500
ctx.drawImage(secondImage, 0, 400, width, 100, 0, 400, width, 100);
// Draw the first image from height 500 to the rest of the canvas
ctx.drawImage(firstImage, 0, 500, width, height - 500, 0, 500, width, height - 500);
// Return the resulting image as a data URL
return new Promise((resolve, reject) => {
canvas.toBlob((blob) => {
if (!blob) {
reject(new Error('Failed to create blob'));
return;
}
const url = URL.createObjectURL(blob);
resolve(url);
}, 'image/png');
});
}
function appendTimestampToElement(filePath, selector) {
const timestamp = parseTimestamp(filePath);
if (!timestamp) return;
const targetElement = document.querySelector(selector);
if (!targetElement) {
console.error("Target element not found.");
return;
}
const textNode = Array.from(targetElement.childNodes).find(node => node.nodeType === Node.TEXT_NODE && node.textContent.includes('в'));
if (!textNode) {
console.error("Text node containing date not found.");
return;
}
textNode.textContent = timestamp;
}
function parseTimestamp(filePath) {
const timestampMatch = filePath.match(/quickw_(\d+)_/);
if (!timestampMatch) {
console.error("Timestamp not found in the given string.");
return null;
}
const timestamp = parseInt(timestampMatch[1], 10) * 1000;
const date = new Date(timestamp);
const optionsDate = {
day: '2-digit',
month: '2-digit',
year: 'numeric'
};
const optionsTime = {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
};
const formattedDate = date.toLocaleDateString('ru-RU', optionsDate);
const formattedTime = date.toLocaleTimeString('ru-RU', optionsTime);
return `${formattedDate} в ${formattedTime}`;
}
function logVariable(varObj) {
const timestamp = new Date().toISOString();
const varName = Object.keys(varObj)[0];
const varValue = varObj[varName];
const varType = typeof varValue;
console.log(`[${timestamp}] ${varName} (${varType}):`, varValue);
}