Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name Meta AI Auto Year-of-Birth (Virtualized Robust)
- // @namespace https://meta.ai/
- // @version 4.0
- // @description Reliably sets 1995 for year of birth on meta.ai, handling virtualized dropdowns and preventing self-interruptions.
- // @match https://www.meta.ai/*
- // @grant none
- // ==/UserScript==
- (function () {
- 'use strict';
- const STORAGE_KEY = '__metaai_birthyear_selected__';
- let runningLock = false; // Prevent re-entrancy
- if (sessionStorage.getItem(STORAGE_KEY)) return;
- function showToast(message, duration = 1200) {
- let toast = document.createElement('div');
- toast.textContent = message;
- Object.assign(toast.style, {
- position: 'fixed',
- bottom: '40px',
- left: '50%',
- transform: 'translateX(-50%)',
- background: '#1a1a1a',
- color: '#fff',
- padding: '7px 20px',
- borderRadius: '6px',
- fontSize: '15px',
- opacity: 0.93,
- zIndex: 2147483646,
- boxShadow: '0 2px 12px rgba(0,0,0,.22)',
- fontFamily: 'system-ui,sans-serif',
- pointerEvents: 'none'
- });
- document.body.appendChild(toast);
- setTimeout(() => (toast.style.opacity = '0'), duration - 250);
- setTimeout(() => toast.remove(), duration);
- }
- function fireAllClicks(el) {
- if (!el) return;
- ['mousedown', 'mouseup', 'click'].forEach(type =>
- el.dispatchEvent(new MouseEvent(type, {bubbles: true, cancelable: true, view: window})));
- }
- // Wait for a specific condition (returns truthy), with polling and timeout
- function waitFor(condition, timeout = 3000, pollInterval = 65) {
- return new Promise((resolve, reject) => {
- const deadline = Date.now() + timeout;
- (function poll() {
- let result = condition();
- if (result) return resolve(result);
- if (Date.now() > deadline) return reject(new Error('Timeout waiting for element'));
- setTimeout(poll, pollInterval);
- })();
- });
- }
- // Scroll year listbox to try to force 1995 to render, if virtualized
- function forceScrollYearListboxTo(yearStr) {
- // Find any popup/virtualized year scroller (with role="listbox")
- const yearValue = parseInt(yearStr, 10);
- const listboxes = document.querySelectorAll('div[role="listbox"]');
- for (const lb of listboxes) {
- // Try scrolling to the right offset
- // For Meta, years usually decrease from top to bottom
- const minYear = 1920, maxYear = new Date().getFullYear();
- if(lb.scrollHeight > lb.clientHeight) {
- const totalYears = maxYear - minYear + 1;
- let percent = (maxYear - yearValue) / totalYears;
- if (percent < 0) percent = 0; if (percent > 1) percent = 1;
- lb.scrollTop = lb.scrollHeight * percent;
- }
- }
- }
- async function selectYear(yearStr = "1995") {
- // 1. Find the dialog
- let dialog = Array.from(document.querySelectorAll('[role="dialog"]')).find(dlg =>
- dlg.textContent.match(/welcome to meta ai/i) ||
- dlg.textContent.match(/providing your birthday/i)
- );
- if (!dialog) return false;
- // 2. Find the year dropdown button inside dialog
- let yearBtn = Array.from(dialog.querySelectorAll('div[role="button"],button')).find(
- btn => /year/i.test(btn.textContent) || /^\d{4}$/.test(btn.textContent.trim())
- );
- if (!yearBtn) return false;
- // 3. Open the dropdown (if not already showing a listbox, open it)
- fireAllClicks(yearBtn);
- // 4. Wait for the listbox to appear in the DOM
- await waitFor(() => document.querySelector('div[role="listbox"]'), 2000).catch(()=>{});
- let yearOption = null;
- // 5. Try find the option for 1995, scrolling if needed (for up to 2.5s)
- const maxTries = 25;
- for (let i = 0; i < maxTries; ++i) {
- // In case the dropdown closes, re-open
- if (!document.querySelector('div[role="listbox"]')) fireAllClicks(yearBtn);
- forceScrollYearListboxTo(yearStr);
- yearOption = Array.from(document.querySelectorAll('div[role="listbox"] span, div[role="listbox"] div'))
- .find(e => e.textContent.trim() === yearStr && e.offsetParent !== null);
- if (yearOption) break;
- await new Promise(r => setTimeout(r, 100));
- }
- // Try <div role="option"> also, for robustness
- if (!yearOption) {
- yearOption = Array.from(document.querySelectorAll('div[role="option"]')).find(
- el => el.textContent.trim() === yearStr && el.offsetParent !== null
- );
- }
- // 6. Click the year if found
- if (yearOption) {
- fireAllClicks(yearOption);
- showToast('Set year to ' + yearStr);
- // Wait for the yearBtn to show 1995 as the value
- for (let confirmTries = 0; confirmTries < 10; confirmTries++) {
- await new Promise(r => setTimeout(r, 80));
- if (yearBtn.textContent.includes(yearStr)) break;
- }
- return true;
- } else {
- showToast('Could NOT find year ' + yearStr, 2000);
- return false;
- }
- }
- // Try to select year and continue
- async function trySelectYearAndContinue() {
- if (sessionStorage.getItem(STORAGE_KEY)) return;
- if (runningLock) return;
- runningLock = true;
- try {
- const yearOK = await selectYear("1995");
- if (!yearOK) {
- runningLock = false;
- return;
- }
- // Find dialog and Continue button after setting year
- let dialog = Array.from(document.querySelectorAll('[role="dialog"]')).find(dlg =>
- dlg.textContent.match(/welcome to meta ai/i) ||
- dlg.textContent.match(/providing your birthday/i)
- );
- if (!dialog) {
- runningLock = false;
- return;
- }
- // Wait for Continue to be enabled (max 2.5s)
- let continueBtn = null;
- for (let i = 0; i < 25; ++i) {
- continueBtn = Array.from(dialog.querySelectorAll('div[role="button"],button'))
- .find(btn => /continue/i.test(btn.textContent) && !btn.getAttribute('aria-disabled'));
- if (continueBtn) break;
- await new Promise(r => setTimeout(r, 100));
- }
- if (continueBtn) {
- fireAllClicks(continueBtn);
- showToast("Continuing...");
- sessionStorage.setItem(STORAGE_KEY, '1');
- } else {
- showToast("Could not find enabled Continue button", 2000);
- }
- } finally {
- runningLock = false;
- }
- }
- // Mutation observer setup
- const observer = new MutationObserver(() => {
- trySelectYearAndContinue();
- });
- observer.observe(document.body, { childList: true, subtree: true });
- // Also attempt on load (in case dialog already present)
- setTimeout(trySelectYearAndContinue, 400);
- })();
Advertisement
Add Comment
Please, Sign In to add comment