Greasy Fork is available in English.
Check every web page for game, dlc and package links to the steam store and mark using icons whether it's owned, unowned, wishlisted, ignored (not interested), DLC, removed/delisted (decommissioned), has low confidence metric, has cards, or is bundled.
// ==UserScript==// @author Revadike// @connect bartervg.com// @connect steam-tracker.com// @connect store.steampowered.com// @contributor Barter.vg// @contributor Black3ird// @contributor Lex// @contributor Luckz// @description Check every web page for game, dlc and package links to the steam store and mark using icons whether it's owned, unowned, wishlisted, ignored (not interested), DLC, removed/delisted (decommissioned), has low confidence metric, has cards, or is bundled.// @exclude /^https?\:\/\/(.+.steampowered|steamcommunity).com\/(?!groups\/groupbuys).*/// @grant GM_addStyle// @grant GM_getValue// @grant GM_info// @grant GM_openInTab// @grant GM_registerMenuCommand// @grant GM_setValue// @grant GM_xmlhttpRequest// @grant unsafeWindow// @homepageURL https://www.steamgifts.com/discussion/y9vVm/// @icon https://store.steampowered.com/favicon.ico// @include /^https?\:\/\/.+/// @name Steam Web Integration// @namespace Royalgamer06// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.slim.min.js// @require https://cdn.jsdelivr.net/gh/kapetan/[email protected]/jquery-observe.js// @run-at document-start// @supportURL https://github.com/Revadike/SteamWebIntegration/issues/// @version 1.9.7// ==/UserScript==// ==Code==// eslint-disable-next-line no-multi-assignthis.$ = this.jQuery = jQuery.noConflict(true);let settings;let boxNode;function displaySettings() {const { name, version, author } = GM_info.script;$(`#title`).text(`${name} (${version}) by ${author}`);$(`#settings`).show();$(`#notinstalled`).hide();Object.keys(settings).forEach((setting) => {const value = settings[setting];if (typeof value === `boolean`) {$(`#${setting}`).prop(`checked`, value);} else {$(`#${setting}`).val(value);}});}function showToast() {$(`#snackbar`).addClass(`show`);setTimeout(() => $(`#snackbar`).removeClass(`show`), 3000);}function onChange(elem) {const name = $(elem).attr(`name`);const val = $(elem).val();if (elem.type === `checkbox`) {settings[name] = $(elem).prop(`checked`);} else {settings[elem.name] = Number.isFinite(val) ? parseInt(val, 10) : val;}GM_setValue(`swi_settings`, JSON.stringify(settings));showToast();}function createBoxNode() {return $(`<div/>`, {"class": `swi-block${settings.boxed ? ` boxed` : ``}`});}function getIconHTML(color, str, lcs, icon, link) {const { name, version, author } = GM_info.script;const titlePlus = `\nLast updated at ${lcs}\n${name} (${version}) by ${author}`;if (link) {return `<span title="${str}\n${titlePlus}"><a style="color: ${color} !important;" href="${link}" target="_blank">${icon}</a></span>`;}return `<span style="color: ${color} !important;" title="${str} on Steam\n${titlePlus}">${icon}</span>`;}function getBoxNode(html, appID, subID) {const node = boxNode.clone(false, false);if (subID) {node.attr(`data-subid`, subID);} else {node.attr(`data-appid`, appID);}node.html(html);return node;}function refreshDecommissioned(callback) {if (!settings.wantDecommissioned) {callback();return;}const cachedDecommissioned = JSON.parse(GM_getValue(`swi_decommissioned`, null));const lastCachedDecommissioned = GM_getValue(`swi_decommissioned_last`, 0);if (cachedDecommissioned && Date.now() - lastCachedDecommissioned < settings.decommissionedRefreshInterval * 60000) {callback(cachedDecommissioned);return;}GM_xmlhttpRequest({"method": `GET`,"url": `https://steam-tracker.com/api?action=GetAppListV3`,"timeout": 30000,"onload": (response) => {let json = null;try {json = JSON.parse(response.responseText);if (json.success) {GM_setValue(`swi_decommissioned`, JSON.stringify(json.removed_apps));GM_setValue(`swi_decommissioned_last`, Date.now());}callback(json.removed_apps);return;} catch (error) {console.log(`[Steam Web Integration] Unable to parse removed steam games data. Using cached data...`, error);}callback(cachedDecommissioned);},"onerror": () => {console.log(`[Steam Web Integration] An error occurred while refreshing removed steam games data. Using cached data...`);callback(cachedDecommissioned);},"ontimeout": () => {console.log(`[Steam Web Integration] It took too long to refresh removed steam games data. Using cached data...`);callback(cachedDecommissioned);}});}function refreshDLC(callback) {if (!settings.wantDLC) {callback();return;}const cachedDLC = JSON.parse(GM_getValue(`swi_dlc`, null));const lastCachedDLC = parseInt(GM_getValue(`swi_dlc_last`, 0), 10) || 1;if (cachedDLC && Object.keys(cachedDLC).length > 7000 && Date.now() - lastCachedDLC < settings.dlcRefreshInterval * 60000) {callback(cachedDLC);return;}GM_xmlhttpRequest({"method": `GET`,"url": `https://bartervg.com/browse/dlc/json/`,"timeout": 30000,"onload": (response) => {let json = null;try {json = JSON.parse(response.responseText);if (Object.keys(json).length > 7000) { // sanity checkGM_setValue(`swi_dlc`, JSON.stringify(json));GM_setValue(`swi_dlc_last`, Date.now());}callback(json);return;} catch (error) {console.log(`[Steam Web Integration] Unable to parse Barter.vg downloadable content data. Using cached data...`, error);}callback(cachedDLC);},"onerror": (response) => {console.log(`[Steam Web Integration] An error occurred while refreshing Barter.vg downloadable content data. Using cached data...`, response);callback(cachedDLC);},"ontimeout": () => {console.log(`[Steam Web Integration] It took too long to refresh Barter.vg downloadable content data. Using cached data...`);callback(cachedDLC);}});}function refreshLimited(callback) {if (!settings.wantLimited) {callback();return;}const cachedLimited = JSON.parse(GM_getValue(`swi_limited`, null));const lastCachedLimited = parseInt(GM_getValue(`swi_limited_last`, 0), 10) || 1;if (cachedLimited && Object.keys(cachedLimited).length > 7000 && Date.now() - lastCachedLimited < settings.limitedRefreshInterval * 60000) {callback(cachedLimited);return;}GM_xmlhttpRequest({"method": `GET`,"url": `https://bartervg.com/browse/tag/481/json/`,"timeout": 30000,"onload": (response) => {let json = null;try {json = JSON.parse(response.responseText);if (Object.keys(json).length > 7000) { // sanity checkGM_setValue(`swi_limited`, JSON.stringify(json));GM_setValue(`swi_limited_last`, Date.now());}callback(json);return;} catch (error) {console.log(`[Steam Web Integration] Unable to parse Barter.vg low confidence metric data. Using cached data...`, error);}callback(cachedLimited);},"onerror": (response) => {console.log(`[Steam Web Integration] An error occurred while refreshing Barter.vg low confidence metric data. Using cached data...`, response);callback(cachedLimited);},"ontimeout": () => {console.log(`[Steam Web Integration] It took too long to refresh Barter.vg low confidence metric data. Using cached data...`);callback(cachedLimited);}});}function refreshCards(callback) {if (!settings.wantCards) {callback();return;}const cachedCards = JSON.parse(GM_getValue(`swi_tradingcards`, null));const lastCachedCards = parseInt(GM_getValue(`swi_tradingcards_last`, 0), 10) || 1;if (cachedCards && Object.keys(cachedCards).length > 7000 && Object.values(cachedCards)[0].marketable && Date.now() - lastCachedCards < settings.cardsRefreshInterval * 60000) {callback(cachedCards);return;}GM_xmlhttpRequest({"method": `GET`,"url": `https://bartervg.com/browse/cards/json/`,"timeout": 30000,"onload": (response) => {let json = null;try {json = JSON.parse(response.responseText);if (Object.keys(json).length > 7000) { // sanity checkGM_setValue(`swi_tradingcards`, JSON.stringify(json));GM_setValue(`swi_tradingcards_last`, Date.now());}callback(json);return;} catch (error) {console.log(`[Steam Web Integration] Unable to parse Barter.vg trading cards data. Using cached data...`, error);}callback(cachedCards);},"onerror": (response) => {console.log(`[Steam Web Integration] An error occurred while refreshing Barter.vg trading cards data. Using cached data...`, response);callback(cachedCards);},"ontimeout": () => {console.log(`[Steam Web Integration] It took too long to refresh Barter.vg trading cards data. Using cached data...`);callback(cachedCards);}});}function refreshBundles(callback) {if (!settings.wantBundles) {callback();return;}const cachedBundles = JSON.parse(GM_getValue(`swi_bundles`, null));const lastCachedBundles = parseInt(GM_getValue(`swi_bundles_last`, 0), 10) || 1;if (cachedBundles && Object.keys(cachedBundles).length > 7000 && Object.values(cachedBundles)[0].bundles && Date.now() - lastCachedBundles < settings.bundlesRefreshInterval * 60000) {callback(cachedBundles);return;}GM_xmlhttpRequest({"method": `GET`,"url": `https://bartervg.com/browse/bundles/json/`,"timeout": 30000,"onload": (response) => {let json = null;try {json = JSON.parse(response.responseText);if (Object.keys(json).length > 7000) { // sanity checkGM_setValue(`swi_bundles`, JSON.stringify(json));GM_setValue(`swi_bundles_last`, Date.now());}callback(json);return;} catch (error) {console.log(`[Steam Web Integration] Unable to parse Barter.vg bundles data. Using cached data...`, error);}callback(cachedBundles);},"onerror": (response) => {console.log(`[Steam Web Integration] An error occurred while refreshing Barter.vg bundles data. Using cached data...`, response);callback(cachedBundles);},"ontimeout": () => {console.log(`[Steam Web Integration] It took too long to refresh Barter.vg bundles data. Using cached data...`);callback(cachedBundles);}});}function doApp(elem, wishlist, ownedApps, ignoredApps, decommissioned, limited, cards, bundles, dlc, lcs, dlcs, dlclcs, llcs, clcs, blcs) {$(elem).addClass(`swi`);const attr = settings.attributes.find((a) => /apps?\//g.test($(elem).attr(a)));const attrVal = $(elem).attr(attr);const appID = parseInt(attrVal.match(/apps?\/[0-9]+/g)[0].split(/apps?\//)[1], 10);if (Number.isNaN(appID)) {return;}setTimeout(() => {let html;let subject;if (dlc && dlc[appID]) {subject = `DLC`;} else if (!dlc) {subject = `Game or DLC`;} else {subject = `Game`;}if (ownedApps && ownedApps.includes(appID)) { // if ownedhtml = getIconHTML(settings.ownedColor, `${subject} (${appID}) owned`, lcs, settings.ownedIcon); // ✔} else if (wishlist.includes(appID)) { // if not owned and wishlistedhtml = getIconHTML(settings.wishlistColor, `${subject} (${appID}) wishlisted`, lcs, settings.wishlistIcon); // ❤} else { // else not owned and not wishlistedhtml = getIconHTML(settings.unownedColor, `${subject} (${appID}) not owned`, lcs, settings.unownedIcon); // ✘}if (settings.wantIgnores && ignoredApps && ignoredApps.includes(appID)) { // if ignored and enabledhtml += getIconHTML(settings.ignoredColor, `${subject} (${appID}) ignored`, llcs, settings.ignoredIcon); // 🛇}if (settings.wantDLC && dlc && dlc[appID]) { // if DLC and enabledconst base = dlc[appID].base_appID;const ownsBase = ownedApps.includes(base);html += getIconHTML(settings.dlcColor, `${subject} (${appID}) is downloadable content for an ${ownsBase ? `` : `un`}owned base game (${base})`, dlclcs, settings.dlcIcon); // ⇩}if (settings.wantDecommissioned && decommissioned) { // if decommissioned and have cache or new dataconst app = decommissioned.find((obj) => obj.appid === appID.toString());if (app) { // if decommissioned?html += getIconHTML(settings.decommissionedColor, `The ${app.type} '${app.name.replace(/"|'/g, ``)}' (${appID}) is ${app.category.toLowerCase()} and has only ${app.count} confirmed owner${app.count === 1 ? `` : `s`} on Steam`, dlcs, settings.decommissionedIcon, `https://steam-tracker.com/app/${appID}/`); // 🗑}}if (settings.wantLimited && limited && limited[appID]) { // if is limitedhtml += getIconHTML(settings.limitedColor, `Game (${appID}) has profile features limited`, llcs, settings.limitedIcon); // ⚙}if (settings.wantCards && cards && cards[appID] && cards[appID].cards && cards[appID].cards > 0) { // if has cards and enabledhtml += getIconHTML(settings.cardColor, `Game (${appID}) has ${cards[appID].cards} ${cards[appID].marketable ? `` : `un`}marketable card${cards[appID].cards === 1 ? `` : `s`}`, clcs, settings.cardIcon, `https://www.steamcardexchange.net/index.php?gamepage-appid-${appID}`);}if (settings.wantBundles && bundles && bundles[appID] && bundles[appID].bundles && bundles[appID].bundles > 0) { // if is bundled and enabledhtml += getIconHTML(settings.bundleColor, `Game (${appID}) has been in ${bundles[appID].bundles} bundle${bundles[appID].bundles === 1 ? `` : `s`}`, blcs, settings.bundleIcon, `https://barter.vg/steam/app/${appID}/#bundles`);}if (settings.prefix) {$(elem).before(getBoxNode(html, appID));} else {$(elem).after(getBoxNode(html, appID));}$(elem).parent().css(`overflow`, `visible`);}, 0);}function doSub(elem, ownedPackages, lcs) {$(elem).addClass(`swi`);const subID = parseInt(elem.href.match(/sub\/[0-9]+/g)[0].split(`sub/`)[1], 10);if (Number.isNaN(subID)) {return;}setTimeout(() => {let html;if (ownedPackages.includes(subID)) { // if ownedhtml = getIconHTML(settings.ownedColor, `Package (${subID}) owned`, lcs, settings.ownedIcon); // ✔} else { // else not ownedhtml = getIconHTML(settings.unownedColor, `Package (${subID}) not owned`, lcs, settings.unownedIcon); // ✖}if (settings.prefix) {$(elem).before(getBoxNode(html, undefined, subID));} else {$(elem).after(getBoxNode(html, undefined, subID));}$(elem).parent().css(`overflow`, `visible`);}, 0);}function integrate(userdata, decommissioned, cards, bundles, limited, dlc, lastCached) {const ignoredApps = Object.keys(userdata.rgIgnoredApps).map((a) => parseInt(a, 10));const ownedApps = userdata.rgOwnedApps;const ownedPackages = userdata.rgOwnedPackages;const wishlist = userdata.rgWishlist;const lcs = settings.dateOverride ? new Date(lastCached).toLocaleString(`sv-SE`) : new Date(lastCached).toLocaleString();const dlcs = new Date(GM_getValue(`swi_decommissioned_last`, 0)).toLocaleString(settings.dateOverride ? `sv-SE` : undefined);const dlclcs = new Date(GM_getValue(`swi_dlc_last`, 0)).toLocaleString(settings.dateOverride ? `sv-SE` : undefined);const llcs = new Date(GM_getValue(`swi_limited_last`, 0)).toLocaleString(settings.dateOverride ? `sv-SE` : undefined);const clcs = new Date(GM_getValue(`swi_tradingcards_last`, 0)).toLocaleString(settings.dateOverride ? `sv-SE` : undefined);const blcs = new Date(GM_getValue(`swi_bundles_last`, 0)).toLocaleString(settings.dateOverride ? `sv-SE` : undefined);const appSelector = [`[href*="steamcommunity.com/app/"]`,`[href*="steamdb.info/app/"]`,`[href*="store.steampowered.com/agecheck/app/"]`,`[href*="store.steampowered.com/app/"]`,`[style*="cdn.akamai.steamstatic.com/steam/apps/"]`,`[style*="cdn.edgecast.steamstatic.com/steam/apps/"]`,`[style*="steamcdn-a.akamaihd.net/steam/apps/"]`,`[style*="steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/"]`,`[style*="steamcdn-a.opskins.media/steam/apps/"]`,`[style*="steamcdn-a.opskins.media/steamcommunity/public/images/apps/"]`,`[style*="steamdb.info/static/camo/apps/"]`,`img[src*="cdn.akamai.steamstatic.com/steam/apps/"]`,`img[src*="cdn.edgecast.steamstatic.com/steam/apps/"]`,`img[src*="steamcdn-a.akamaihd.net/steam/apps/"]`,`img[src*="steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/"]`,`img[src*="steamcdn-a.opskins.media/steam/apps/"]`,`img[src*="steamcdn-a.opskins.media/steamcommunity/public/images/apps/"]`,`img[src*="steamdb.info/static/camo/apps/"]`].filter((s) => settings.attributes.find((a) => s.includes(`[${a}`))).map((s) => `${s}:not(.swi)`).join(`, `);const subSelector = [`[href*="steamdb.info/sub/"]`,`[href*="store.steampowered.com/sub/"]`].map((s) => `${s}:not(.swi)`).join(`, `);let delaySWI;const doSWI = (delay = 750) => {if (delaySWI) {clearTimeout(delaySWI);}delaySWI = setTimeout(() => {if (settings.dynamicContent !== `ping`) {console.log(`[Steam Web Integration] Executing`);}clearTimeout(delaySWI);$(appSelector, document.body).get().forEach((elem) => doApp(elem, wishlist, ownedApps, ignoredApps, decommissioned, limited, cards, bundles, dlc, lcs, dlcs, dlclcs, llcs, clcs, blcs));$(subSelector, document.body).get().forEach((elem) => doSub(elem, ownedPackages, lcs), 0);}, delay);};const clearSWI = () => {console.log(`[Steam Web Integration] Clearing`);$(`.swi-block`).remove();$(`.swi`).removeClass(`swi`);};const reloadSWI = () => {clearSWI();doSWI(0);};$(document).ready(() => {doSWI(0);GM_registerMenuCommand(`Run again`, () => doSWI(0));GM_registerMenuCommand(`Clear all`, clearSWI);GM_registerMenuCommand(`Clear and run (reload)`, reloadSWI);unsafeWindow.doSWI = doSWI;unsafeWindow.clearSWI = clearSWI;unsafeWindow.reloadSWI = reloadSWI;if (settings.dynamicContent === `disabled`) {return;}if (settings.dynamicContent === `observe`) {$(`body`).observe({ "added": true, "attributes": true, "attributeFilter": settings.attributes }, appSelector, () => doSWI());$(`body`).observe({ "added": true, "attributes": true, "attributeFilter": [`href`] }, subSelector, () => doSWI());} else if (settings.dynamicContent === `ping`) {setInterval(doSWI, 1500);}});}function refresh() {const cachedJson = GM_getValue(`swi_data`, null);let lastCached = GM_getValue(`swi_last`, 0);if (cachedJson && Date.now() - lastCached < settings.userRefreshInterval * 60000) {const userdata = JSON.parse(cachedJson);refreshDecommissioned((decommissioned) => refreshDLC((dlc) => refreshLimited((limited) => refreshCards((cards) => refreshBundles((bundles) => integrate(userdata, decommissioned, cards, bundles, limited, dlc, lastCached))))));return;}GM_xmlhttpRequest({"method": `GET`,"url": `https://store.steampowered.com/dynamicstore/userdata/?t=${Date.now()}`,"onload": (response) => {let userdata = JSON.parse(response.responseText);if (userdata.rgOwnedApps.length === 0 && userdata.rgOwnedPackages.length === 0 && userdata.rgIgnoredApps.length === 0 && userdata.rgWishlist.length === 0) { // not logged inif (!cachedJson) {console.log(`[Steam Web Integration] No cached information available. Please login to Steam to fix this.`);return;}userdata = JSON.parse(cachedJson);} else {lastCached = Date.now();GM_setValue(`swi_last`, lastCached);GM_setValue(`swi_data`, JSON.stringify(userdata));}refreshDecommissioned((decommissioned) => refreshDLC((dlc) => refreshLimited((limited) => refreshCards((cards) => refreshBundles((bundles) => integrate(userdata, decommissioned, cards, bundles, limited, dlc, lastCached))))));}});}function init() {const settingsuri = `https://revadike.com/swi/settings`;const defaults = {"attributes": [`href`, `src`, `style`],"blackList": ``,"boxed": true,"bundleColor": `#ffff00`,"bundleIcon": `🎁︎`,"bundlesRefreshInterval": 2880,"cardColor": `#0000ff`,"cardIcon": `🂡`,"cardsRefreshInterval": 2880,"dateOverride": false,"decommissionedColor": `#ffffff`,"decommissionedIcon": `☠`,"decommissionedRefreshInterval": 1440,"dlcColor": `#a655b2`,"dlcIcon": `⇩`,"dlcRefreshInterval": 1440,"dynamicContent": `observe`,"ignoredColor": `#808080`,"ignoredIcon": `🚫︎`,"limitedColor": `#00ffff`,"limitedIcon": `⚙`,"limitedRefreshInterval": 2880,"ownedColor": `#008000`,"ownedIcon": `✔`,"prefix": false,"unownedColor": `#ff0000`,"unownedIcon": `✘`,"userRefreshInterval": 1,"wantBundles": true,"wantCards": true,"wantDecommissioned": true,"wantDLC": true,"wantIgnores": true,"wantLimited": true,"wishlistColor": `#ff69b4`,"wishlistIcon": `❤`};const stylesheet = `.swi-block {display: inline-block;line-height: initial;}.swi-block.boxed {background: rgba(0, 0, 0, 0.7);border-radius: 5px;margin: auto 4px auto 4px;padding: 2px 4px 2px 4px;position: relative;}.swi-block span {cursor: help;margin: 2px;}.swi-block a {text-decoration: none;}`;settings = JSON.parse(GM_getValue(`swi_settings`, JSON.stringify(defaults)));Object.keys(defaults).forEach((setting) => {if (settings[setting] !== undefined) {return;}settings[setting] = defaults[setting];});if (unsafeWindow.location.href.startsWith(settingsuri)) {unsafeWindow.onChange = onChange;unsafeWindow.scriptInfo = GM_info.script;unsafeWindow.settings = settings;$(document).ready(displaySettings);} else if (!settings.blackList.split(`\n`).find((url) => unsafeWindow.location.href.includes(url.trim()))) {boxNode = createBoxNode();GM_addStyle(stylesheet);GM_registerMenuCommand(`Change settings`, () => unsafeWindow.open(settingsuri, `_blank`));refresh();}}init();// ==/Code==