Guest User

Untitled

a guest
Aug 4th, 2026
43
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name         NovaAlpha - Temu price adjustment checker
  3. // @namespace    http://tampermonkey.net/
  4. // @version      0.1
  5. // @description  Scans your Temu orders page for orders eligible for a price adjustment refund and pops up direct links to claim them
  6. // @author       NovaAlpha
  7. // @match        *://*.temu.com/*bgt_orders.html*
  8. // @grant        GM_openInTab
  9. // ==/UserScript==
  10.  
  11.  
  12. /*
  13.  * Run this below to remove the saved popup position/size data
  14.  *
  15. localStorage.removeItem('popupTop_' + window.location.origin);
  16. localStorage.removeItem('popupLeft_' + window.location.origin);
  17.  
  18. */
  19.  
  20.  
  21. (function()
  22. {
  23.     'use strict';
  24.  
  25.     var reminderDiv = document.createElement('div');
  26.     var contentHTML = '';
  27.     var DisplayPopup = false;
  28.     var ordersProcessed = false;
  29.  
  30.  
  31.     // Popup UI (same drag/resize/toggle popup used by the reminder script,
  32.     // kept here so this script can stand alone)
  33.  
  34.     function createPopup()
  35.     {
  36.         if (DisplayPopup === true)
  37.         {
  38.             var reminderDiv = document.createElement('div');
  39.             reminderDiv.id = 'yourPopupId';
  40.  
  41.             var dragHandle = document.createElement('div');
  42.             dragHandle.style.height = '20px';
  43.             dragHandle.style.backgroundColor = '#ccc';
  44.             dragHandle.style.cursor = 'move';
  45.             dragHandle.innerHTML = 'Drag here';
  46.             dragHandle.style.textAlign = 'center';
  47.             reminderDiv.appendChild(dragHandle);
  48.  
  49.             var toggleButton = document.createElement('button');
  50.             toggleButton.innerHTML = 'Show / Hide';
  51.             toggleButton.style.marginTop = '10px';
  52.             toggleButton.style.marginBottom = '10px';
  53.             reminderDiv.appendChild(toggleButton);
  54.  
  55.             var contentDiv = document.createElement('div');
  56.             contentDiv.innerHTML = contentHTML;
  57.             reminderDiv.appendChild(contentDiv);
  58.  
  59.             reminderDiv.style.position = 'fixed';
  60.             reminderDiv.style.padding = '10px 10px 0px 10px';
  61.             reminderDiv.style.backgroundColor = 'cyan';
  62.             reminderDiv.style.border = '2px solid black';
  63.             reminderDiv.style.zIndex = '10000';
  64.             reminderDiv.style.width = localStorage.getItem('popupWidth_' + window.location.origin) || '290px';
  65.             reminderDiv.style.maxWidth = '700px';
  66.             reminderDiv.style.maxHeight = '850px';
  67.             reminderDiv.style.wordWrap = 'break-word';
  68.             reminderDiv.style.resize = 'both';
  69.             reminderDiv.style.overflow = 'auto';
  70.             reminderDiv.style.top = localStorage.getItem('popupTop_' + window.location.origin) || '50%';
  71.             reminderDiv.style.left = localStorage.getItem('popupLeft_' + window.location.origin) || '10px';
  72.  
  73.             // Check if isContentVisible is not found in local storage, default to true
  74.             var isContentVisible = localStorage.getItem('isContentVisible_' + window.location.origin);
  75.             if (isContentVisible === null) {
  76.                 isContentVisible = true;
  77.             } else {
  78.                 isContentVisible = isContentVisible === 'true';
  79.             }
  80.  
  81.             if (!isContentVisible) {
  82.                 contentDiv.style.display = 'none';
  83.                 reminderDiv.style.height = 'auto';
  84.                 localStorage.setItem('popupOriginalWidth_' + window.location.origin, reminderDiv.style.width);
  85.                 dragHandle.style.width = toggleButton.offsetWidth + 'px';
  86.                 reminderDiv.style.width = 'auto';
  87.                 dragHandle.style.width = '';
  88.             }
  89.  
  90.             toggleButton.addEventListener('click', function () {
  91.                 isContentVisible = !isContentVisible;
  92.                 localStorage.setItem('isContentVisible_' + window.location.origin, isContentVisible);
  93.  
  94.                 if (!isContentVisible) {
  95.                     contentDiv.style.display = 'none';
  96.                     reminderDiv.style.height = 'auto';
  97.                     localStorage.setItem('popupOriginalWidth_' + window.location.origin, reminderDiv.style.width);
  98.                     dragHandle.style.width = toggleButton.offsetWidth + 'px';
  99.                     reminderDiv.style.width = 'auto';
  100.                 } else {
  101.                     contentDiv.style.display = 'block';
  102.                     reminderDiv.style.height = 'auto';
  103.                     reminderDiv.style.width = localStorage.getItem('popupOriginalWidth_' + window.location.origin);
  104.                     dragHandle.style.width = '';
  105.                 }
  106.             });
  107.  
  108.             dragHandle.addEventListener('mousedown', function (e) {
  109.                 var offsetX = e.clientX - parseInt(window.getComputedStyle(reminderDiv).left);
  110.                 var offsetY = e.clientY - parseInt(window.getComputedStyle(reminderDiv).top);
  111.  
  112.                 function mouseMoveHandler(e) {
  113.                     reminderDiv.style.top = (e.clientY - offsetY) + 'px';
  114.                     reminderDiv.style.left = (e.clientX - offsetX) + 'px';
  115.                 }
  116.  
  117.                 function mouseUpHandler() {
  118.                     window.removeEventListener('mousemove', mouseMoveHandler);
  119.                     window.removeEventListener('mouseup', mouseUpHandler);
  120.                     localStorage.setItem('popupTop_' + window.location.origin, reminderDiv.style.top);
  121.                     localStorage.setItem('popupLeft_' + window.location.origin, reminderDiv.style.left);
  122.                 }
  123.  
  124.                 window.addEventListener('mousemove', mouseMoveHandler);
  125.                 window.addEventListener('mouseup', mouseUpHandler);
  126.             });
  127.  
  128.             document.body.appendChild(reminderDiv);
  129.  
  130.             // Calculate maximum allowable top and left positions
  131.             var maxTop = window.innerHeight - reminderDiv.offsetHeight;
  132.             var maxLeft = window.innerWidth - reminderDiv.offsetWidth;
  133.  
  134.             var storedTop = localStorage.getItem('popupTop_' + window.location.origin);
  135.             var storedLeft = localStorage.getItem('popupLeft_' + window.location.origin);
  136.  
  137.             storedTop = storedTop !== null ? parseInt(storedTop) : 10;
  138.             storedLeft = storedLeft !== null ? parseInt(storedLeft) : 10;
  139.  
  140.             reminderDiv.style.top = Math.min(Math.max(storedTop, 30), maxTop) + 'px';
  141.             reminderDiv.style.left = Math.min(Math.max(storedLeft, 0), maxLeft) + 'px';
  142.  
  143.             reminderDiv.addEventListener('mousemove', function () {
  144.                 if (isContentVisible) {
  145.                     localStorage.setItem('popupWidth_' + window.location.origin, reminderDiv.style.width);
  146.                     localStorage.setItem('popupHeight_' + window.location.origin, reminderDiv.style.height);
  147.                 }
  148.             });
  149.         }
  150.     }
  151.  
  152.     function displayPopup() {
  153.         if (DisplayPopup === true)
  154.         {
  155.             var existingPopup = document.getElementById('yourPopupId');
  156.  
  157.             if (!existingPopup) {
  158.                 createPopup();
  159.             } else {
  160.                 reminderDiv.style.position = 'fixed';
  161.  
  162.                 var popupWidth = localStorage.getItem('popupWidth_' + window.location.origin) || '290px';
  163.                 var popupHeight = localStorage.getItem('popupHeight_' + window.location.origin) || 'auto';
  164.  
  165.                 var maxTop = window.innerHeight - existingPopup.offsetHeight;
  166.                 var maxLeft = window.innerWidth - existingPopup.offsetWidth;
  167.  
  168.                 var storedTop = localStorage.getItem('popupTop_' + window.location.origin);
  169.                 var storedLeft = localStorage.getItem('popupLeft_' + window.location.origin);
  170.  
  171.                 storedTop = storedTop !== null ? parseInt(storedTop) : 10;
  172.                 storedLeft = storedLeft !== null ? parseInt(storedLeft) : 10;
  173.  
  174.                 existingPopup.style.top = Math.min(Math.max(storedTop, 0), maxTop) + 'px';
  175.                 existingPopup.style.left = Math.min(Math.max(storedLeft, 0), maxLeft) + 'px';
  176.                 existingPopup.style.width = popupWidth;
  177.                 existingPopup.style.height = popupHeight;
  178.             }
  179.         }
  180.     }
  181.  
  182.  
  183.     // Order scanning + price adjustment eligibility check
  184.  
  185.     function extractOrderData() {
  186.         const orders = [];
  187.         const seenIDs = new Set();
  188.  
  189.         // Find leaf nodes whose text is an Order Time label
  190.         const leafNodes = Array.from(document.querySelectorAll('span, div, p'))
  191.             .filter(el => el.children.length === 0);
  192.  
  193.         leafNodes.forEach(el => {
  194.             const text = el.textContent.trim();
  195.             if (!/^Order\s*[Tt]ime/i.test(text) || text.length > 20) return;
  196.  
  197.             // Walk up to find a card that also contains an Order ID
  198.             let card = el.parentElement;
  199.             for (let depth = 0; depth < 12; depth++) {
  200.                 if (!card || card === document.body) break;
  201.                 if (/Order\s*(ID|no\.?|number)/i.test(card.textContent)) {
  202.                     // Extract all leaf text nodes within this card
  203.                     const cardLeafs = Array.from(card.querySelectorAll('span, div, p'))
  204.                         .filter(n => n.children.length === 0);
  205.  
  206.                     let orderTime = null;
  207.                     let orderID = null;
  208.  
  209.                     for (let i = 0; i < cardLeafs.length; i++) {
  210.                         const t = cardLeafs[i].textContent.trim();
  211.                         if (/^Order\s*[Tt]ime/i.test(t) && t.length <= 20) {
  212.                             // Next leaf with a 4-digit year is the value
  213.                             for (let j = i + 1; j < Math.min(i + 5, cardLeafs.length); j++) {
  214.                                 const candidate = cardLeafs[j].textContent.trim();
  215.                                 if (/\d{4}/.test(candidate) && candidate.length <= 30) {
  216.                                     orderTime = candidate;
  217.                                     break;
  218.                                 }
  219.                             }
  220.                         }
  221.                         if (/^Order\s*(ID|no\.?):?$/i.test(t)) {
  222.                             // Next non-empty leaf is the value
  223.                             for (let j = i + 1; j < Math.min(i + 5, cardLeafs.length); j++) {
  224.                                 const candidate = cardLeafs[j].textContent.trim();
  225.                                 if (candidate.length > 0 && !/^Order/i.test(candidate)) {
  226.                                     orderID = candidate;
  227.                                     break;
  228.                                 }
  229.                             }
  230.                         }
  231.                     }
  232.  
  233.                     if (orderTime && orderID && !seenIDs.has(orderID)) {
  234.                         seenIDs.add(orderID);
  235.                         orders.push({ orderTime, orderID });
  236.                     }
  237.                     break;
  238.                 }
  239.                 card = card.parentElement;
  240.             }
  241.         });
  242.  
  243.         return orders;
  244.     }
  245.  
  246.     async function checkPriceAdjustment(orderID) {
  247.         const url = `https://www.temu.com/w/bgas_refund_difference.html?parent_order_sn=${orderID}&biz_source=1-000-5&after_sales_type=1&belongTab=AO&_x_sessn_id=dwjzf47d2c`;
  248.         try {
  249.             const resp = await fetch(url, { credentials: 'include' });
  250.             const html = await resp.text();
  251.  
  252.             // Try to extract embedded page data (Next.js / SSR patterns)
  253.             const embeddedPatterns = [
  254.                 /<script[^>]+id="__NEXT_DATA__"[^>]*>([\s\S]*?)<\/script>/,
  255.                 /window\.__INITIAL_STATE__\s*=\s*({[\s\S]*?});\s*<\/script>/,
  256.                 /window\.__SSR_DATA__\s*=\s*({[\s\S]*?});\s*<\/script>/,
  257.             ];
  258.             for (const pat of embeddedPatterns) {
  259.                 const m = html.match(pat);
  260.                 if (!m) continue;
  261.                 try {
  262.                     const str = JSON.stringify(JSON.parse(m[1]));
  263.                     console.log('[Temu Price Adj] embedded JSON for', orderID, ':', str.substring(0, 800));
  264.                     // Positive signals
  265.                     if (/["']can_apply["']\s*:\s*true/.test(str)) { console.log('[Temu Price Adj] POSITIVE: can_apply=true'); return true; }
  266.                     if (/["'](price_reduce|reduce|refund)_amount["']\s*:\s*[1-9]/.test(str)) { console.log('[Temu Price Adj] POSITIVE: amount>0'); return true; }
  267.                     if (/["'](eligible|is_eligible|can_claim)["']\s*:\s*true/.test(str)) { console.log('[Temu Price Adj] POSITIVE: eligible=true'); return true; }
  268.                     // Negative signals
  269.                     if (/["']can_apply["']\s*:\s*false/.test(str)) { console.log('[Temu Price Adj] NEGATIVE: can_apply=false'); return false; }
  270.                     if (/["'](price_reduce|reduce|refund)_amount["']\s*:\s*0[,}"']/.test(str)) { console.log('[Temu Price Adj] NEGATIVE: amount=0'); return false; }
  271.                     if (/["'](eligible|is_eligible|can_claim)["']\s*:\s*false/.test(str)) { console.log('[Temu Price Adj] NEGATIVE: eligible=false'); return false; }
  272.                     console.log('[Temu Price Adj] embedded JSON found but no eligibility signal matched for', orderID);
  273.                 } catch(e) { console.log('[Temu Price Adj] JSON parse error', e); }
  274.             }
  275.  
  276.             // Ineligible orders redirect to the order detail page (empty main div + bgt_order_detail i18n keys)
  277.             if (html.includes('<div id="main"></div>')) {
  278.                 console.log('[Temu Price Adj] NEGATIVE: empty main div (redirected to order detail) for', orderID);
  279.                 return false;
  280.             }
  281.  
  282.             console.log('[Temu Price Adj] POSITIVE: price adjustment page rendered for', orderID);
  283.             return true;
  284.         } catch(e) {
  285.             console.log('[Temu Price Adj] fetch error for', orderID, e);
  286.             return true;
  287.         }
  288.     }
  289.  
  290.     async function processOrders() {
  291.         if (ordersProcessed) return;
  292.         const orders = extractOrderData();
  293.         if (orders.length === 0) {
  294.             console.log('[Temu Script] No orders found yet');
  295.             return;
  296.         }
  297.  
  298.         ordersProcessed = true;
  299.         observer.disconnect();
  300.         console.log('[Temu Script] Found orders:', orders);
  301.  
  302.         for (const { orderTime, orderID } of orders) {
  303.             console.log('[Temu Script] Order:', orderID, orderTime, '| within 30 days:', isWithin30Days(orderTime));
  304.             if (!isWithin30Days(orderTime)) continue;
  305.  
  306.             const eligible = await checkPriceAdjustment(orderID);
  307.             console.log('[Temu Script] Price adjustment eligible:', orderID, eligible);
  308.             if (!eligible) continue;
  309.  
  310.             const link = `https://www.temu.com/w/bgas_refund_difference.html?parent_order_sn=${orderID}&biz_source=1-000-5&after_sales_type=1&belongTab=AO&_x_sessn_id=dwjzf47d2c`;
  311.             contentHTML += `<br><br><a href="${link}">${link}</a>`;
  312.             DisplayPopup = true;
  313.             GM_openInTab(link, { active: false, insert: true });
  314.         }
  315.  
  316.         if (DisplayPopup) {
  317.             createPopup();
  318.             displayPopup();
  319.         }
  320.     }
  321.  
  322.     function isWithin30Days(dateString) {
  323.         var orderDate = new Date(dateString);
  324.         var currentDate = new Date();
  325.  
  326.         // Calculate the difference in milliseconds
  327.         var difference = currentDate - orderDate;
  328.  
  329.         // Calculate the difference in days
  330.         var daysDifference = difference / (1000 * 60 * 60 * 24);
  331.  
  332.         return daysDifference <= 30;
  333.     }
  334.  
  335.     // Watch for DOM changes (SPA dynamic load)
  336.     var observer = new MutationObserver(function() { if (!ordersProcessed) processOrders(); });
  337.     observer.observe(document.body, { childList: true, subtree: true });
  338.  
  339.     // Also retry on fixed intervals as fallback
  340.     setTimeout(processOrders, 3000);
  341.     setTimeout(processOrders, 6000);
  342.     setTimeout(processOrders, 12000);
  343.     setTimeout(function() { observer.disconnect(); }, 30000);
  344.  
  345. })();
Advertisement
Add Comment
Please, Sign In to add comment