// UserScript
// @name SteamGifts Train Assistant
// @namespace sg-train-tools
// @version 4.1
// @description Highlights the giveaway header based on Steam/SG status; optionally auto-skips in trains; adds compact πŸ‘/πŸ‘Ž rating and a top-right settings panel.
// @match https://www.steamgifts.com/*
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @connect store.steampowered.com
// @run-at document-idle
// /UserScript

/* AI helper notes (for anyone inspecting the script with an AI tool)
What this userscript does (high level):
Parses the current SteamGifts giveaway page and identifies the target game (app/sub).
Fetches the user’s Steam dynamicstore userdata (Owned / Wishlist / Not Interested) and their SteamGifts hidden list (from filters page). Both are cached briefly.
Visually marks the giveaway header:
Owned β†’ green, Wishlist β†’ blue, Hidden (Steam or SG) β†’ red.

Adds a small diagonal ribbon with the status text.
Adds compact πŸ‘ / πŸ‘Ž buttons next to the giveaway title. The choice is stored locally (by canonical id). A πŸ‘Ž can be configured to be skipped automatically when riding a β€œtrain”.
Provides a settings panel (gear icon in the top-right of the header, English UI). You can:
Toggle using Steam data and/or SteamGifts hidden list.
Choose skip rules for trains and set a delay (0 = instant).
View and remove your locally saved disliked entries.
When skip rules match (e.g., Owned or Not Interested, SG Hidden, or πŸ‘Ž), the script finds the Next giveaway link in the description and navigates automatically, showing a small toast summary.

Privacy & scope:
Only reads Steam’s public dynamicstore/userdata for the logged-in user and your SG filters page to infer hidden app ids.
No data is sent anywhere else; preferences persist in localStorage.

(() => {
'use strict';

/ ===================== CONSTANTS ===================== /
const ARALIK_MS = 8000;
const STEAM_TTL = ARALIK_MS;
const SG_TTL = ARALIK_MS;
const SUB_TTL = 72460601000;

const RENKLER = { sgHidden:'#d32d2d', owned:'#228b22', wish:'#1e90ff', ignored:'#d32d2d' };
const OVERLAY_OP=0.55, SERIT_OP=0.28, PARILTI_A=0.35;

// Storage keys
const LS_SG_IDS='tr_sg_hidden_appids_v1', LS_SG_TS='tr_sg_hidden_appids_ts_v1';
const LS_STEAM='tr_steam_userdata_v1', LS_ST_TS='tr_steam_userdata_ts_v1';
const LS_AYAR ='sgtr_settings_v3';
const LS_OYLAMA='sgtr_ratings_v1';

// Skip summaries (session)
const SS_SKIP_ADET='sgtr_skip_count';
const SS_SKIP_SON_BASLIK='sgtr_skip_last_title';
const SS_SKIP_SON_NEDEN='sgtr_skip_last_reason';

const SG_FILTRE_URL = 'https://www.steamgifts.com/account/settings/giveaways/filters';
const STEAM_KULLANICI = 'https://store.steampowered.com/dynamicstore/userdata/?t=';

/ ===================== HELPERS ===================== /
const disKapsul = ()=>document.querySelector('.featured__outer-wrap');
const lsAl=(k,fb=null)=>{ try{const v=localStorage.getItem(k); return v==null?fb:JSON.parse(v);}catch{ return fb; } };
const lsYaz=(k,v)=> localStorage.setItem(k, JSON.stringify(v));
const bayatMi=(tsKey,ttl)=> Date.now()-Number(localStorage.getItem(tsKey)||0) > ttl;
const dizi2Kume=(arr)=> (Array.isArray(arr)?arr:[]).reduce((m,id)=>{m[id]=1;return m;},{}); // Set-like map

function renkDegerAta(el, hex){
const s=hex.replace('#',''); const n=s.length===3?s.split('').map(c=>c+c).join(''):s;
const r=parseInt(n.slice(0,2),16), g=parseInt(n.slice(2,4),16), b=parseInt(n.slice(4,6),16);
el.style.setProperty('--trsg-rgb', ${r}, ${g}, ${b});
el.style.setProperty('--trsg-hex', #${n});
}
function seritKaldir(el){ el.querySelector('.trsg-serit')?.remove(); }
function durumUygula(el, etiket, hex){
el.classList.add('trsg-durum');
renkDegerAta(el,hex); seritKaldir(el);
const d=document.createElement('div'); d.className='trsg-serit'; d.textContent=etiket; el.appendChild(d);
}
function durumTemizle(el){
el.classList.remove('trsg-durum');
el.style.removeProperty('--trsg-rgb'); el.style.removeProperty('--trsg-hex');
seritKaldir(el);
}

// Clean game title: only text nodes of .featured__heading__medium
function oyunBasligiAl(){
const m = document.querySelector('.featured__heading__medium');
if (m){
let raw = '';
m.childNodes.forEach(n => { if (n.nodeType === Node.TEXT_NODE) raw += n.textContent; });
raw = (raw || m.textContent || '').replace(/\s(\s\d+\sP)\s$/i,'').trim();
if (raw) return raw;
}
const h = document.querySelector('.giveaway__heading__name');
if (h && h.textContent.trim()) return h.textContent.trim();
return (document.title||'').replace(/\s[-|]\sSteamGifts./i,'').replace(/\s(\d+P)\s*$/i,'').trim() || 'Game';
}

GM_addStyle(`
.featured__outer-wrap{position:relative;--trsg-extra-shadow:none}
.trsg-durum{position:relative}
.trsg-durum>*{position:relative;z-index:1}
.trsg-durum::before{content:"";position:absolute;inset:0;pointer-events:none;z-index:0}
.trsg-durum{
--trsg-rgb:128,128,128;--trsg-hex:#888;
box-shadow: inset 0 -10px 0 rgba(var(--trsg-rgb),1), 0 12px 30px rgba(var(--trsg-rgb),${PARILTI_A}), var(--trsg-extra-shadow);
}
.trsg-durum::before{
background:
linear-gradient(90deg,rgba(var(--trsg-rgb),${OVERLAY_OP}) 0%,rgba(var(--trsg-rgb),0) 65%),
repeating-linear-gradient(-45deg,rgba(var(--trsg-rgb),${SERIT_OP}) 0 8px,rgba(0,0,0,0) 8px 16px)
}
.trsg-serit{position:absolute;top:14px;right:-36px;width:240px;transform:rotate(35deg);text-align:center;padding:6px 16px;color:#fff;font-weight:700;font-size:12px;background:var(--trsg-hex);box-shadow:0 6px 18px rgba(0,0,0,.35);z-index:5;text-transform:uppercase}

βŽ—
βœ“
.featured__outer-wrap.trsg-begen    { --trsg-extra-shadow: inset 0 0 0 2px rgba(46,204,113,.7) }
.featured__outer-wrap.trsg-begenme  { --trsg-extra-shadow: inset 0 0 0 2px rgba(231, 76, 60,.7) }

.trsg-oylama{display:inline-flex;gap:6px;align-items:center;margin-left:8px;vertical-align:middle}
.trsg-oybtn{height:26px;min-width:26px;padding:0 10px;border-radius:6px;border:1px solid rgba(255,255,255,.18);background:rgba(255,255,255,.08);color:#fff;cursor:pointer;font-weight:600;line-height:24px}
.trsg-oybtn:hover{filter:brightness(1.05)}
.trsg-oybtn[data-active="1"].up   {background:rgba(46,204,113,.18);border-color:rgba(46,204,113,.6)}
.trsg-oybtn[data-active="1"].down {background:rgba(231, 76, 60,.18); border-color:rgba(231, 76, 60,.6)}

.trsg-disli{position:absolute;top:10px;right:10px;z-index:50}
.trsg-dislibtn{width:28px;height:28px;border-radius:8px;border:1px solid #425166;background:#2f3947;color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 8px rgba(0,0,0,.2)}
.trsg-dislibtn:hover{filter:brightness(1.05)}

/* Settings panel uses body-portal (always on top) */
.trsg-panel{position:fixed;top:0;left:0;min-width:360px;max-width:420px;max-height:60vh;overflow:auto;background:#0f1419;color:#dbe7ff;border:1px solid #2b3645;border-radius:10px;box-shadow:0 18px 60px rgba(0,0,0,.55);padding:12px;display:none;z-index:2147483646}
.trsg-panel.acik{display:block}
.trsg-bolum{border-top:1px solid #202a38;padding:10px 2px}
.trsg-bolum:first-child{border-top:0}
.trsg-baslik{font-weight:700;color:#fff;margin:2px 0 8px}
.trsg-satir{display:grid;grid-template-columns:auto 1fr;gap:10px;align-items:center;margin:6px 0}
.trsg-onay{accent-color:#79c452}
.trsg-sayi{width:80px;border:1px solid #3a4758;border-radius:6px;background:#0b1016;color:#cfe2ff;padding:4px 6px}
.trsg-liste{max-height:180px;overflow:auto;border:1px solid #263244;border-radius:8px;background:#101722;padding:6px}
.trsg-oge{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:8px;padding:5px 6px;border-radius:6px}
.trsg-oge:nth-child(odd){background:rgba(255,255,255,.02)}
.trsg-kaldir{border:1px solid #5c2b2b;background:#7f2e2e;color:#fff;border-radius:6px;padding:1px 6px;cursor:pointer}

.trsg-bildirim{position:fixed;right:18px;bottom:18px;z-index:2147483646;background:#2f3947;color:#fff;padding:10px 12px;border-radius:8px;border:1px solid #1f2731;box-shadow:0 4px 16px rgba(0,0,0,.25);max-width:320px;font:13px/1.4 system-ui,-apple-system,Segoe UI,Roboto,Arial}

`);

/ ===================== SETTINGS ===================== /
const AYAR_DEF = {
showDisliked: true,
steamData: true,
showSGHidden: true,
skip: { all:false, viaDislike:true, viaSteamOwnedIgnored:true, viaSGHidden:true, delaySec:3 }
};
const ayarYukle = () => {
try {
const raw = JSON.parse(localStorage.getItem(LS_AYAR) || 'null');
return Object.assign({}, AYAR_DEF, raw || {}, { skip:Object.assign({}, AYAR_DEF.skip, raw?.skip || {}) });
} catch { return JSON.parse(JSON.stringify(AYAR_DEF)); }
};
const ayarKaydet = (s)=> localStorage.setItem(LS_AYAR, JSON.stringify(s));

const oyHaritasi = () => { let m={}; try{ m=JSON.parse(localStorage.getItem(LS_OYLAMA)||'{}'); }catch{} return m; };
function oyHaritasiAyarla(id, durum, baslik){
const m = oyHaritasi();
if (!durum) delete m[id];
else m[id] = { state:durum, title: baslik || m[id]?.title || oyunBasligiAl() };
localStorage.setItem(LS_OYLAMA, JSON.stringify(m));
}

function bildirim(msj){
let t = document.querySelector('.trsg-bildirim'); if (!t){ t=document.createElement('div'); t.className='trsg-bildirim'; document.body.appendChild(t); }
t.textContent = msj; t.style.opacity = '1';
setTimeout(()=>{ t.style.transition='opacity .35s'; t.style.opacity='0'; }, 2200);
}

/ ============ SETTINGS UI (body-portal) ============ /
(function ayarUIOlustur(){
const host = document.querySelector('.featured__inner-wrap'); if (!host) return;

βŽ—
βœ“
const disli = document.createElement('div');
disli.className='trsg-disli';
disli.innerHTML = `<button class="trsg-dislibtn" title="Settings">βš™</button>`;
host.style.position='relative'; host.appendChild(disli);
const disliBtn = disli.querySelector('button');

const panel = document.createElement('div');
panel.className = 'trsg-panel';
document.body.appendChild(panel);

function ciz(){
  const S = ayarYukle();
  const disliked = Object.entries(oyHaritasi()).filter(([,v])=>v.state==='down').map(([id,v])=>({id, title:v.title||id}));

  panel.innerHTML = `
    <div class="trsg-bolum">
      <div class="trsg-baslik">Disliked items</div>
      ${S.showDisliked ? `
        <div class="trsg-liste">
          ${disliked.length ? disliked.map(x=>`
            <div class="trsg-oge" data-id="${x.id}">
              <span>πŸ‘Ž</span>
              <span style="white-space:nowrap;overflow:hidden;text-overflow:ellipsis">${x.title}</span>
              <button class="trsg-kaldir" title="Remove">βœ–</button>
            </div>`).join('') : `<div style="opacity:.7;padding:4px 6px">No disliked items.</div>`}
        </div>
      ` : `<div class="trsg-satir" style="opacity:.8"><span></span><span>List is hidden.</span></div>`}
      <div class="trsg-satir"><input class="trsg-onay" id="trsg_gosterBegenme" type="checkbox" ${S.showDisliked?'checked':''}/><label for="trsg_gosterBegenme">Show disliked list</label></div>
    </div>

    <div class="trsg-bolum">
      <div class="trsg-baslik">Steam data</div>
      <div class="trsg-satir"><input class="trsg-onay" id="trsg_steamAcik" type="checkbox" ${S.steamData?'checked':''}/><label for="trsg_steamAcik">Use Steam data (Owned / Wishlist / Not interested)</label></div>
    </div>

    <div class="trsg-bolum">
      <div class="trsg-baslik">SteamGifts hidden</div>
      <div class="trsg-satir"><input class="trsg-onay" id="trsg_sgHiddenAcik" type="checkbox" ${S.showSGHidden?'checked':''}/><label for="trsg_sgHiddenAcik">Use SteamGifts hidden list</label></div>
    </div>

    <div class="trsg-bolum">
      <div class="trsg-baslik">Skip options (when on a train)</div>
      <div class="trsg-satir"><input class="trsg-onay trsg-skip" id="trsg_skip_begenme" type="checkbox" ${S.skip.viaDislike?'checked':''}/><label for="trsg_skip_begenme">Skip items marked πŸ‘Ž</label></div>
      <div class="trsg-satir"><input class="trsg-onay trsg-skip" id="trsg_skip_steam" type="checkbox" ${S.skip.viaSteamOwnedIgnored?'checked':''}/><label for="trsg_skip_steam">Skip Steam owned + not interested</label></div>
      <div class="trsg-satir"><input class="trsg-onay trsg-skip" id="trsg_skip_sgh" type="checkbox" ${S.skip.viaSGHidden?'checked':''}/><label for="trsg_skip_sgh">Skip SteamGifts hidden</label></div>
      <div class="trsg-satir"><label>Skip delay (seconds):</label><input class="trsg-sayi" id="trsg_skip_saniye" type="number" min="0" step="1" value="${S.skip.delaySec}"/><span style="opacity:.8">(0 = instant)</span></div>
    </div>
  `;

  panel.querySelector('#trsg_gosterBegenme').onchange = e=>{ const s=ayarYukle(); s.showDisliked=e.target.checked; ayarKaydet(s); ciz(); };
  panel.querySelector('#trsg_steamAcik').onchange     = e=>{ const s=ayarYukle(); s.steamData=e.target.checked; ayarKaydet(s); };
  panel.querySelector('#trsg_sgHiddenAcik').onchange  = e=>{ const s=ayarYukle(); s.showSGHidden=e.target.checked; ayarKaydet(s); };
  panel.querySelector('#trsg_skip_begenme').onchange  = e=>{ const s=ayarYukle(); s.skip.viaDislike=e.target.checked; ayarKaydet(s); };
  panel.querySelector('#trsg_skip_steam').onchange    = e=>{ const s=ayarYukle(); s.skip.viaSteamOwnedIgnored=e.target.checked; ayarKaydet(s); };
  panel.querySelector('#trsg_skip_sgh').onchange      = e=>{ const s=ayarYukle(); s.skip.viaSGHidden=e.target.checked; ayarKaydet(s); };
  panel.querySelector('#trsg_skip_saniye').onchange   = e=>{ const s=ayarYukle(); s.skip.delaySec=Math.max(0,parseInt(e.target.value||'0',10)); ayarKaydet(s); };

  panel.querySelectorAll('.trsg-oge .trsg-kaldir').forEach(btn=>{
    btn.onclick=()=>{ const id=btn.closest('.trsg-oge')?.dataset.id; if(!id) return; oyHaritasiAyarla(id,null); ciz(); };
  });
}

function panelKonumla(){
  const disliBtn = document.querySelector('.trsg-dislibtn'); if (!disliBtn) return;
  const r=disliBtn.getBoundingClientRect();
  const left=Math.max(8, Math.min(window.innerWidth - panel.offsetWidth - 8, r.right - panel.offsetWidth));
  panel.style.top=(r.bottom+8)+'px'; panel.style.left=left+'px';
}

document.querySelector('.trsg-dislibtn')?.addEventListener('click', (e)=>{ e.stopPropagation(); if(panel.classList.contains('acik')){ panel.classList.remove('acik'); } else { ciz(); panel.classList.add('acik'); panelKonumla(); } });
window.addEventListener('scroll', panelKonumla, {passive:true});
window.addEventListener('resize', panelKonumla);
document.addEventListener('click', (e)=>{ if(panel.classList.contains('acik') && !panel.contains(e.target) && !e.target.closest('.trsg-dislibtn')) panel.classList.remove('acik'); });
document.addEventListener('keydown', (e)=>{ if(e.key==='Escape' && panel.classList.contains('acik')) panel.classList.remove('acik'); });

})();

/ ===================== RATING UI ===================== /
function mevcutKanonikId(){
const aApp = document.querySelector('a[href="store.steampowered.com/app/"], a[href="store.steampowered.com/agecheck/app/"]');
if (aApp){ const m=aApp.href.match(/\/app\/(\d+)/); if(m) return app_${m[1]}; }
const aSub = document.querySelector('a[href="store.steampowered.com/sub/"], a[href="store.steampowered.com/agecheck/sub/"]');
if (aSub){ const m=aSub.href.match(/\/sub\/(\d+)/); if(m) return sub_${m[1]}; }
const kod = (location.pathname.match(/\/giveaway\/([A-Za-z0-9]{5})/)||[])[1];
return kod ? ga_${kod} : title_${oyunBasligiAl()};
}
function oylamaUIOlustur(){
const baslikSatiri = document.querySelector('.featured__heading'); if (!baslikSatiri || baslikSatiri.querySelector('.trsg-oylama')) return;
const ui = document.createElement('span'); ui.className='trsg-oylama';
const yap=(t,c,ttl)=>{ const b=document.createElement('button'); b.className=trsg-oybtn ${c}; b.type='button'; b.title=ttl; b.textContent=t; return b; };
const up=yap('πŸ‘','up','Thumbs Up'); const down=yap('πŸ‘Ž','down','Thumbs Down');
ui.append(up,down); baslikSatiri.appendChild(ui);

βŽ—
βœ“
1
2
3
4
5
6
const wrap=disKapsul(); const id=mevcutKanonikId();
const uygula=(s)=>{ up.dataset.active=(s==='up')?'1':'0'; down.dataset.active=(s==='down')?'1':'0'; wrap?.classList.toggle('trsg-begen',s==='up'); wrap?.classList.toggle('trsg-begenme',s==='down'); if (s==='down') izinVerenAtlaKuyrugu('dislike'); };
let kayit = oyHaritasi()[id]?.state || null; uygula(kayit);

up.onclick   = ()=>{ const cur=oyHaritasi()[id]?.state||null; const nxt=(cur==='up')?null:'up';   oyHaritasiAyarla(id,nxt,oyunBasligiAl()); uygula(nxt); };
down.onclick = ()=>{ const cur=oyHaritasi()[id]?.state||null; const nxt=(cur==='down')?null:'down'; oyHaritasiAyarla(id,nxt,oyunBasligiAl()); uygula(nxt); };

}

/ ===================== TRAIN HELPERS ===================== /
const KELIME_SONRA = ['next','sonraki','ileri','sΔ±radaki','forward','advance'];
const KELIME_ONCE = ['back','previous','prev','geri','ΓΆnceki','onceki'];
const OK_SAG = ['⏩','➑','β†’','Β»','β€Ί','>>'];
const OK_SOL = ['βͺ','β¬…','←','Β«','β€Ή','<<'];
const trLower = s => (s||'').toLocaleLowerCase('tr');
function aciklamaKok(doc){ return doc.querySelector('.page__description, .giveaway__description, .markdown, .comment__description') || doc; }
function gaKod(url){ const m=String(url).match(/\/giveaway\/([A-Za-z0-9]{5})(?:\/|$)/); return m?m[1]:null; }
function sonrakiBagBul(mevcutURL){
const cur = gaKod(mevcutURL);
const root = aciklamaKok(document);
let as = Array.from(root.querySelectorAll('a')); if (as.length>50) as=as.slice(0,50);
const items = as.map(a=>{
const href=new URL(a.getAttribute('href')||'', location.href).href;
const code=gaKod(href);
return { href, code, txt:(a.textContent||'').trim() };
}).filter(x=>x.code && x.code!cur);
if (!items.length) return null;
const prevIdx = items.findIndex(x => KELIME_ONCE.some(w=>trLower(x.txt).includes(w)) || OK_SOL.some(s=>x.txt.includes(s)));
const noPrev = items.filter((_,i)=>i!
prevIdx);
const next = noPrev.find(x => KELIME_SONRA.some(w=>trLower(x.txt).includes(w)) || OK_SAG.some(s=>x.txt.includes(s)));
if (next) return next.href;
if (prevIdx>=0){ const afterPrev = items.slice(prevIdx+1).find(x=>x.code!==cur); if (afterPrev) return afterPrev.href; }
return items[items.length-1].href;
}

// Human-readable reason labels
function nedenEtiketi(neden){
if (neden === 'owned') return 'Owned';
if (neden === 'notInterested' || neden === 'sgHidden') return 'Not Interesting';
if (neden === 'dislike') return 'Disliked';
return 'Skipped';
}

// Skip control w/ settings
function skipOzetIsaretle(neden){
const cnt = Number(sessionStorage.getItem(SS_SKIP_ADET)||0);
if (cnt=0){
sessionStorage.setItem(SS_SKIP_SON_BASLIK, oyunBasligiAl());
sessionStorage.setItem(SS_SKIP_SON_NEDEN, neden);
}
sessionStorage.setItem(SS_SKIP_ADET, String(cnt+1));
}
function izinVerenAtlaKuyrugu(neden){
const S = ayarYukle();
const steamGrubu = (neden
='owned' || neden='notInterested');
const uygun = S.skip.all
|| (neden
='dislike' && S.skip.viaDislike)
|| (steamGrubu && S.steamData && S.skip.viaSteamOwnedIgnored)
|| (neden==='sgHidden' && S.showSGHidden && S.skip.viaSGHidden);
if (!uygun) return;

βŽ—
βœ“
1
2
3
4
5
const next = sonrakiBagBul(location.href); if (!next) return;
const d = Math.max(0, Number(S.skip.delaySec)||0)*1000;
bildirim(d ? `Skipping in ${Math.round(d/1000)}s…` : 'Skipping…');
skipOzetIsaretle(neden);
setTimeout(()=>location.assign(next), d);

}
function bekleyenBildirimVarsa(){
const cnt = Number(sessionStorage.getItem(SS_SKIP_ADET)||0);
if (!cnt) return;
if (cnt===1){
const t = sessionStorage.getItem(SS_SKIP_SON_BASLIK)||'Game';
const r = sessionStorage.getItem(SS_SKIP_SON_NEDEN)||'owned';
const etik = nedenEtiketi(r);
bildirim(${t} Skipped (${etik}));
} else {
bildirim(${cnt} Game Skipped);
}
sessionStorage.removeItem(SS_SKIP_ADET);
sessionStorage.removeItem(SS_SKIP_SON_BASLIK);
sessionStorage.removeItem(SS_SKIP_SON_NEDEN);
}

/ ===================== DATA LOADERS ===================== /
let sgKilit=false;
async function sgGizliKumeYukle(){
const cached = lsAl(LS_SG_IDS, []);
if (cached.length && !bayatMi(LS_SG_TS, SG_TTL)) return new Set(cached);
if (sgKilit) return new Set(cached);
sgKilit=true;
try{
const res = await fetch(SG_FILTRE_URL, {credentials:'include'});
const html = await res.text();
const ids=new Set();
html.replace(/store.steampowered.com\/app\/(\d+)/g,(,id)=>{ids.add(id);return ;});
html.replace(/\/apps\/(\d+)\//g,(,id)=>{ids.add(id);return ;});
const arr=[...ids]; lsYaz(LS_SG_IDS,arr); localStorage.setItem(LS_SG_TS,Date.now());
return new Set(arr);
}catch{ return new Set(cached); }
finally{ sgKilit=false; }
}

let steamKilit=false;
function steamVerisiYukle(){
return new Promise((coz)=>{
const cached = lsAl(LS_STEAM,null);
if (cached && !bayatMi(LS_ST_TS, STEAM_TTL)) return coz(cached);
if (steamKilit) return coz(cached);
steamKilit=true;
GM_xmlhttpRequest({
method:'GET', url: STEAM_KULLANICI + Date.now(), timeout: ARALIK_MS,
onload:(res)=>{
try{
const raw=JSON.parse(res.responseText);
const out={ owned:dizi2Kume(raw.rgOwnedApps||[]),
wish:dizi2Kume(raw.rgWishlist||[]),
ignored:dizi2Kume(Object.keys(raw.rgIgnoredApps||{})),
ownedPackages:dizi2Kume(raw.rgOwnedPackages||[]) };
lsYaz(LS_STEAM,out); localStorage.setItem(LS_ST_TS, Date.now()); coz(out);
}catch{ coz(cached); }
},
onerror: ()=>coz(cached),
ontimeout:()=>coz(cached),
onloadend:()=>{ steamKilit=false; }
});
});
}

const altKey=id=>tr_subapps_${id}, altTs=id=>tr_subapps_${id}_ts;
function subUygulamaYukle(subId){
return new Promise((coz)=>{
const k=altKey(subId), kt=altTs(subId);
const cached=lsAl(k,null), ts=Number(localStorage.getItem(kt)||0);
if (cached && Date.now()-ts < SUB_TTL) return coz(cached);
GM_xmlhttpRequest({
method:'GET', url:https://store.steampowered.com/api/packagedetails/?packageids=${subId}, timeout:ARALIK_MS,
onload:(res)=>{
try{
const json=JSON.parse(res.responseText);
const node=json?.[subId]?.data;
const apps=Array.isArray(node?.apps) ? node.apps.map(a=>String(a.appid)) : [];
lsYaz(k,apps); localStorage.setItem(kt,Date.now()); coz(apps);
}catch{ coz(cached||[]); }
},
onerror: ()=>coz(cached||[]),
ontimeout:()=>coz(cached||[])
});
});
}

/ ===================== TARGET & EVAL ===================== /
function hedefCoz(){
const aApp = document.querySelector('a[href="store.steampowered.com/app/"], a[href="store.steampowered.com/agecheck/app/"]');
if (aApp){ const m=aApp.href.match(/\/app\/(\d+)/); if(m) return {tur:'app', id:m[1]}; }
const aSub = document.querySelector('a[href="store.steampowered.com/sub/"], a[href="store.steampowered.com/agecheck/sub/"]');
if (aSub){ const m=aSub.href.match(/\/sub\/(\d+)/); if(m) return {tur:'sub', id:m[1]}; }
const m2=document.documentElement.innerHTML.match(/store.steampowered.com\/app\/(\d+)/);
if (m2) return {tur:'app', id:m2[1]};
return null;
}

let sonKey=null, sonEtiket=null, bildirimGosterildi=false;

async function degerlendir(){
const wrap = disKapsul(); if (!wrap) return;
oylamaUIOlustur();

βŽ—
βœ“
const tgt = hedefCoz();
if (!tgt){ durumTemizle(wrap); sonKey=null; sonEtiket=null; if(!bildirimGosterildi){ bekleyenBildirimVarsa(); bildirimGosterildi=true; } return; }

const S = ayarYukle();
let etiket=null, hex=null, neden=null;

// SG Hidden (if enabled)
if (S.showSGHidden){
  const sgSet = await sgGizliKumeYukle();
  if (tgt.tur==='app' && sgSet.has(tgt.id)) { etiket='HIDE'; hex=RENKLER.sgHidden; neden='sgHidden'; }
  if (!etiket && tgt.tur==='sub'){
    const apps = await subUygulamaYukle(tgt.id);
    if (apps.some(id=>sgSet.has(id))) { etiket='HIDE'; hex=RENKLER.sgHidden; neden='sgHidden'; }
  }
}

// Steam data (if enabled)
if (!etiket && S.steamData){
  const ud = await steamVerisiYukle();
  if (ud){
    if (tgt.tur==='app'){
      const a=tgt.id;
      if      (ud.owned[a])   { etiket='Owned';        hex=RENKLER.owned;   neden='owned'; }
      else if (ud.wish[a])    { etiket='Wishlist';     hex=RENKLER.wish; }
      else if (ud.ignored[a]) { etiket='HIDE (Steam)'; hex=RENKLER.ignored; neden='notInterested'; }
    } else {
      const subId=tgt.id;
      if      (ud.ownedPackages && ud.ownedPackages[subId]) { etiket='Owned'; hex=RENKLER.owned; neden='owned'; }
      else {
        const apps = await subUygulamaYukle(subId);
        if      (apps.some(id=>ud.owned[id]))   { etiket='Owned';        hex=RENKLER.owned;   neden='owned'; }
        else if (apps.some(id=>ud.wish[id]))    { etiket='Wishlist';     hex=RENKLER.wish; }
        else if (apps.some(id=>ud.ignored[id])) { etiket='HIDE (Steam)'; hex=RENKLER.ignored; neden='notInterested'; }
      }
    }
  }
}

const key = `${tgt.tur}:${tgt.id}`;
if (etiket){
  if (sonKey!==key || sonEtiket!==etiket){ durumUygula(wrap, etiket, hex); sonKey=key; sonEtiket=etiket; }
  if (neden) izinVerenAtlaKuyrugu(neden);
  else if(!bildirimGosterildi){ bekleyenBildirimVarsa(); bildirimGosterildi=true; }
} else {
  if (sonEtiket!==null){ durumTemizle(wrap); sonKey=key; sonEtiket=null; }
  if(!bildirimGosterildi){ bekleyenBildirimVarsa(); bildirimGosterildi=true; }
}

}

/ ===================== BOOT ===================== /
if (!disKapsul()) return;
degerlendir();
setTimeout(degerlendir, 600);
setInterval(()=>{ if (!document.hidden) { bildirimGosterildi=false; degerlendir(); } }, ARALIK_MS);
document.addEventListener('visibilitychange', ()=>{ if (!document.hidden){ bildirimGosterildi=false; degerlendir(); } });
})();

Edit

Pub: 19 Sep 2025 18:19 UTC

Edit: 20 Sep 2025 09:59 UTC

Views: 16