Guest User

Untitled

a guest
Jun 18th, 2025
34
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         Meta AI Auto Year-of-Birth (Virtualized Robust)
  3. // @namespace    https://meta.ai/
  4. // @version      4.0
  5. // @description  Reliably sets 1995 for year of birth on meta.ai, handling virtualized dropdowns and preventing self-interruptions.
  6. // @match        https://www.meta.ai/*
  7. // @grant        none
  8. // ==/UserScript==
  9.  
  10. (function () {
  11.   'use strict';
  12.  
  13.   const STORAGE_KEY = '__metaai_birthyear_selected__';
  14.   let runningLock = false; // Prevent re-entrancy
  15.  
  16.   if (sessionStorage.getItem(STORAGE_KEY)) return;
  17.  
  18.   function showToast(message, duration = 1200) {
  19.     let toast = document.createElement('div');
  20.     toast.textContent = message;
  21.     Object.assign(toast.style, {
  22.       position: 'fixed',
  23.       bottom: '40px',
  24.       left: '50%',
  25.       transform: 'translateX(-50%)',
  26.       background: '#1a1a1a',
  27.       color: '#fff',
  28.       padding: '7px 20px',
  29.       borderRadius: '6px',
  30.       fontSize: '15px',
  31.       opacity: 0.93,
  32.       zIndex: 2147483646,
  33.       boxShadow: '0 2px 12px rgba(0,0,0,.22)',
  34.       fontFamily: 'system-ui,sans-serif',
  35.       pointerEvents: 'none'
  36.     });
  37.     document.body.appendChild(toast);
  38.     setTimeout(() => (toast.style.opacity = '0'), duration - 250);
  39.     setTimeout(() => toast.remove(), duration);
  40.   }
  41.  
  42.   function fireAllClicks(el) {
  43.     if (!el) return;
  44.     ['mousedown', 'mouseup', 'click'].forEach(type =>
  45.       el.dispatchEvent(new MouseEvent(type, {bubbles: true, cancelable: true, view: window})));
  46.   }
  47.  
  48.   // Wait for a specific condition (returns truthy), with polling and timeout
  49.   function waitFor(condition, timeout = 3000, pollInterval = 65) {
  50.     return new Promise((resolve, reject) => {
  51.       const deadline = Date.now() + timeout;
  52.       (function poll() {
  53.         let result = condition();
  54.         if (result) return resolve(result);
  55.         if (Date.now() > deadline) return reject(new Error('Timeout waiting for element'));
  56.         setTimeout(poll, pollInterval);
  57.       })();
  58.     });
  59.   }
  60.  
  61.   // Scroll year listbox to try to force 1995 to render, if virtualized
  62.   function forceScrollYearListboxTo(yearStr) {
  63.     // Find any popup/virtualized year scroller (with role="listbox")
  64.     const yearValue = parseInt(yearStr, 10);
  65.     const listboxes = document.querySelectorAll('div[role="listbox"]');
  66.     for (const lb of listboxes) {
  67.       // Try scrolling to the right offset
  68.       // For Meta, years usually decrease from top to bottom
  69.       const minYear = 1920, maxYear = new Date().getFullYear();
  70.       if(lb.scrollHeight > lb.clientHeight) {
  71.         const totalYears = maxYear - minYear + 1;
  72.         let percent = (maxYear - yearValue) / totalYears;
  73.         if (percent < 0) percent = 0; if (percent > 1) percent = 1;
  74.         lb.scrollTop = lb.scrollHeight * percent;
  75.       }
  76.     }
  77.   }
  78.  
  79.   async function selectYear(yearStr = "1995") {
  80.     // 1. Find the dialog
  81.     let dialog = Array.from(document.querySelectorAll('[role="dialog"]')).find(dlg =>
  82.       dlg.textContent.match(/welcome to meta ai/i) ||
  83.       dlg.textContent.match(/providing your birthday/i)
  84.     );
  85.     if (!dialog) return false;
  86.  
  87.     // 2. Find the year dropdown button inside dialog
  88.     let yearBtn = Array.from(dialog.querySelectorAll('div[role="button"],button')).find(
  89.       btn => /year/i.test(btn.textContent) || /^\d{4}$/.test(btn.textContent.trim())
  90.     );
  91.     if (!yearBtn) return false;
  92.  
  93.     // 3. Open the dropdown (if not already showing a listbox, open it)
  94.     fireAllClicks(yearBtn);
  95.  
  96.     // 4. Wait for the listbox to appear in the DOM
  97.     await waitFor(() => document.querySelector('div[role="listbox"]'), 2000).catch(()=>{});
  98.     let yearOption = null;
  99.  
  100.     // 5. Try find the option for 1995, scrolling if needed (for up to 2.5s)
  101.     const maxTries = 25;
  102.     for (let i = 0; i < maxTries; ++i) {
  103.       // In case the dropdown closes, re-open
  104.       if (!document.querySelector('div[role="listbox"]')) fireAllClicks(yearBtn);
  105.       forceScrollYearListboxTo(yearStr);
  106.       yearOption = Array.from(document.querySelectorAll('div[role="listbox"] span, div[role="listbox"] div'))
  107.         .find(e => e.textContent.trim() === yearStr && e.offsetParent !== null);
  108.       if (yearOption) break;
  109.       await new Promise(r => setTimeout(r, 100));
  110.     }
  111.     // Try <div role="option"> also, for robustness
  112.     if (!yearOption) {
  113.       yearOption = Array.from(document.querySelectorAll('div[role="option"]')).find(
  114.         el => el.textContent.trim() === yearStr && el.offsetParent !== null
  115.       );
  116.     }
  117.  
  118.     // 6. Click the year if found
  119.     if (yearOption) {
  120.       fireAllClicks(yearOption);
  121.       showToast('Set year to ' + yearStr);
  122.       // Wait for the yearBtn to show 1995 as the value
  123.       for (let confirmTries = 0; confirmTries < 10; confirmTries++) {
  124.         await new Promise(r => setTimeout(r, 80));
  125.         if (yearBtn.textContent.includes(yearStr)) break;
  126.       }
  127.       return true;
  128.     } else {
  129.       showToast('Could NOT find year ' + yearStr, 2000);
  130.       return false;
  131.     }
  132.   }
  133.  
  134.   // Try to select year and continue
  135.   async function trySelectYearAndContinue() {
  136.     if (sessionStorage.getItem(STORAGE_KEY)) return;
  137.     if (runningLock) return;
  138.     runningLock = true;
  139.     try {
  140.       const yearOK = await selectYear("1995");
  141.       if (!yearOK) {
  142.         runningLock = false;
  143.         return;
  144.       }
  145.       // Find dialog and Continue button after setting year
  146.       let dialog = Array.from(document.querySelectorAll('[role="dialog"]')).find(dlg =>
  147.         dlg.textContent.match(/welcome to meta ai/i) ||
  148.         dlg.textContent.match(/providing your birthday/i)
  149.       );
  150.       if (!dialog) {
  151.         runningLock = false;
  152.         return;
  153.       }
  154.       // Wait for Continue to be enabled (max 2.5s)
  155.       let continueBtn = null;
  156.       for (let i = 0; i < 25; ++i) {
  157.         continueBtn = Array.from(dialog.querySelectorAll('div[role="button"],button'))
  158.           .find(btn => /continue/i.test(btn.textContent) && !btn.getAttribute('aria-disabled'));
  159.         if (continueBtn) break;
  160.         await new Promise(r => setTimeout(r, 100));
  161.       }
  162.       if (continueBtn) {
  163.         fireAllClicks(continueBtn);
  164.         showToast("Continuing...");
  165.         sessionStorage.setItem(STORAGE_KEY, '1');
  166.       } else {
  167.         showToast("Could not find enabled Continue button", 2000);
  168.       }
  169.     } finally {
  170.       runningLock = false;
  171.     }
  172.   }
  173.  
  174.   // Mutation observer setup
  175.   const observer = new MutationObserver(() => {
  176.     trySelectYearAndContinue();
  177.   });
  178.   observer.observe(document.body, { childList: true, subtree: true });
  179.  
  180.   // Also attempt on load (in case dialog already present)
  181.   setTimeout(trySelectYearAndContinue, 400);
  182.  
  183. })();
Advertisement
Add Comment
Please, Sign In to add comment