Steam Web Integration

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.

  1. // ==UserScript==
  2. // @author Revadike
  3. // @connect bartervg.com
  4. // @connect steam-tracker.com
  5. // @connect store.steampowered.com
  6. // @contributor Barter.vg
  7. // @contributor Black3ird
  8. // @contributor Lex
  9. // @contributor Luckz
  10. // @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.
  11. // @exclude /^https?\:\/\/(.+.steampowered|steamcommunity).com\/(?!groups\/groupbuys).*/
  12. // @grant GM_addStyle
  13. // @grant GM_getValue
  14. // @grant GM_info
  15. // @grant GM_openInTab
  16. // @grant GM_registerMenuCommand
  17. // @grant GM_setValue
  18. // @grant GM_xmlhttpRequest
  19. // @grant unsafeWindow
  20. // @homepageURL https://www.steamgifts.com/discussion/y9vVm/
  21. // @icon https://store.steampowered.com/favicon.ico
  22. // @include /^https?\:\/\/.+/
  23. // @name Steam Web Integration
  24. // @namespace Royalgamer06
  25. // @require https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.slim.min.js
  26. // @require https://cdn.jsdelivr.net/gh/kapetan/jquery-observe@2.0.3/jquery-observe.js
  27. // @run-at document-start
  28. // @supportURL https://github.com/Revadike/SteamWebIntegration/issues/
  29. // @version 1.9.7
  30. // ==/UserScript==
  31.  
  32. // ==Code==
  33. // eslint-disable-next-line no-multi-assign
  34. this.$ = this.jQuery = jQuery.noConflict(true);
  35. let settings;
  36. let boxNode;
  37.  
  38. function displaySettings() {
  39. const { name, version, author } = GM_info.script;
  40. $(`#title`).text(`${name} (${version}) by ${author}`);
  41. $(`#settings`).show();
  42. $(`#notinstalled`).hide();
  43.  
  44. Object.keys(settings).forEach((setting) => {
  45. const value = settings[setting];
  46. if (typeof value === `boolean`) {
  47. $(`#${setting}`).prop(`checked`, value);
  48. } else {
  49. $(`#${setting}`).val(value);
  50. }
  51. });
  52. }
  53.  
  54. function showToast() {
  55. $(`#snackbar`).addClass(`show`);
  56. setTimeout(() => $(`#snackbar`).removeClass(`show`), 3000);
  57. }
  58.  
  59. function onChange(elem) {
  60. const name = $(elem).attr(`name`);
  61. const val = $(elem).val();
  62.  
  63. if (elem.type === `checkbox`) {
  64. settings[name] = $(elem).prop(`checked`);
  65. } else {
  66. settings[elem.name] = Number.isFinite(val) ? parseInt(val, 10) : val;
  67. }
  68.  
  69. GM_setValue(`swi_settings`, JSON.stringify(settings));
  70. showToast();
  71. }
  72.  
  73. function createBoxNode() {
  74. return $(`<div/>`, {
  75. "class": `swi-block${settings.boxed ? ` boxed` : ``}`
  76. });
  77. }
  78.  
  79. function getIconHTML(color, str, lcs, icon, link) {
  80. const { name, version, author } = GM_info.script;
  81. const titlePlus = `\nLast updated at ${lcs}\n${name} (${version}) by ${author}`;
  82. if (link) {
  83. return `<span title="${str}\n${titlePlus}"><a style="color: ${color} !important;" href="${link}" target="_blank">${icon}</a></span>`;
  84. }
  85.  
  86. return `<span style="color: ${color} !important;" title="${str} on Steam\n${titlePlus}">${icon}</span>`;
  87. }
  88.  
  89. function getBoxNode(html, appID, subID) {
  90. const node = boxNode.clone(false, false);
  91. if (subID) {
  92. node.attr(`data-subid`, subID);
  93. } else {
  94. node.attr(`data-appid`, appID);
  95. }
  96. node.html(html);
  97. return node;
  98. }
  99.  
  100. function refreshDecommissioned(callback) {
  101. if (!settings.wantDecommissioned) {
  102. callback();
  103. return;
  104. }
  105.  
  106. const cachedDecommissioned = JSON.parse(GM_getValue(`swi_decommissioned`, null));
  107. const lastCachedDecommissioned = GM_getValue(`swi_decommissioned_last`, 0);
  108. if (cachedDecommissioned && Date.now() - lastCachedDecommissioned < settings.decommissionedRefreshInterval * 60000) {
  109. callback(cachedDecommissioned);
  110. return;
  111. }
  112.  
  113. GM_xmlhttpRequest({
  114. "method": `GET`,
  115. "url": `https://steam-tracker.com/api?action=GetAppListV3`,
  116. "timeout": 30000,
  117. "onload": (response) => {
  118. let json = null;
  119. try {
  120. json = JSON.parse(response.responseText);
  121. if (json.success) {
  122. GM_setValue(`swi_decommissioned`, JSON.stringify(json.removed_apps));
  123. GM_setValue(`swi_decommissioned_last`, Date.now());
  124. }
  125. callback(json.removed_apps);
  126. return;
  127. } catch (error) {
  128. console.log(`[Steam Web Integration] Unable to parse removed steam games data. Using cached data...`, error);
  129. }
  130. callback(cachedDecommissioned);
  131. },
  132. "onerror": () => {
  133. console.log(`[Steam Web Integration] An error occurred while refreshing removed steam games data. Using cached data...`);
  134. callback(cachedDecommissioned);
  135. },
  136. "ontimeout": () => {
  137. console.log(`[Steam Web Integration] It took too long to refresh removed steam games data. Using cached data...`);
  138. callback(cachedDecommissioned);
  139. }
  140. });
  141. }
  142.  
  143. function refreshDLC(callback) {
  144. if (!settings.wantDLC) {
  145. callback();
  146. return;
  147. }
  148.  
  149. const cachedDLC = JSON.parse(GM_getValue(`swi_dlc`, null));
  150. const lastCachedDLC = parseInt(GM_getValue(`swi_dlc_last`, 0), 10) || 1;
  151. if (cachedDLC && Object.keys(cachedDLC).length > 7000 && Date.now() - lastCachedDLC < settings.dlcRefreshInterval * 60000) {
  152. callback(cachedDLC);
  153. return;
  154. }
  155.  
  156. GM_xmlhttpRequest({
  157. "method": `GET`,
  158. "url": `https://bartervg.com/browse/dlc/json/`,
  159. "timeout": 30000,
  160. "onload": (response) => {
  161. let json = null;
  162. try {
  163. json = JSON.parse(response.responseText);
  164. if (Object.keys(json).length > 7000) { // sanity check
  165. GM_setValue(`swi_dlc`, JSON.stringify(json));
  166. GM_setValue(`swi_dlc_last`, Date.now());
  167. }
  168. callback(json);
  169. return;
  170. } catch (error) {
  171. console.log(`[Steam Web Integration] Unable to parse Barter.vg downloadable content data. Using cached data...`, error);
  172. }
  173. callback(cachedDLC);
  174. },
  175. "onerror": (response) => {
  176. console.log(`[Steam Web Integration] An error occurred while refreshing Barter.vg downloadable content data. Using cached data...`, response);
  177. callback(cachedDLC);
  178. },
  179. "ontimeout": () => {
  180. console.log(`[Steam Web Integration] It took too long to refresh Barter.vg downloadable content data. Using cached data...`);
  181. callback(cachedDLC);
  182. }
  183. });
  184. }
  185.  
  186. function refreshLimited(callback) {
  187. if (!settings.wantLimited) {
  188. callback();
  189. return;
  190. }
  191.  
  192. const cachedLimited = JSON.parse(GM_getValue(`swi_limited`, null));
  193. const lastCachedLimited = parseInt(GM_getValue(`swi_limited_last`, 0), 10) || 1;
  194. if (cachedLimited && Object.keys(cachedLimited).length > 7000 && Date.now() - lastCachedLimited < settings.limitedRefreshInterval * 60000) {
  195. callback(cachedLimited);
  196. return;
  197. }
  198.  
  199. GM_xmlhttpRequest({
  200. "method": `GET`,
  201. "url": `https://bartervg.com/browse/tag/481/json/`,
  202. "timeout": 30000,
  203. "onload": (response) => {
  204. let json = null;
  205. try {
  206. json = JSON.parse(response.responseText);
  207. if (Object.keys(json).length > 7000) { // sanity check
  208. GM_setValue(`swi_limited`, JSON.stringify(json));
  209. GM_setValue(`swi_limited_last`, Date.now());
  210. }
  211. callback(json);
  212. return;
  213. } catch (error) {
  214. console.log(`[Steam Web Integration] Unable to parse Barter.vg low confidence metric data. Using cached data...`, error);
  215. }
  216. callback(cachedLimited);
  217. },
  218. "onerror": (response) => {
  219. console.log(`[Steam Web Integration] An error occurred while refreshing Barter.vg low confidence metric data. Using cached data...`, response);
  220. callback(cachedLimited);
  221. },
  222. "ontimeout": () => {
  223. console.log(`[Steam Web Integration] It took too long to refresh Barter.vg low confidence metric data. Using cached data...`);
  224. callback(cachedLimited);
  225. }
  226. });
  227. }
  228.  
  229. function refreshCards(callback) {
  230. if (!settings.wantCards) {
  231. callback();
  232. return;
  233. }
  234.  
  235. const cachedCards = JSON.parse(GM_getValue(`swi_tradingcards`, null));
  236. const lastCachedCards = parseInt(GM_getValue(`swi_tradingcards_last`, 0), 10) || 1;
  237. if (cachedCards && Object.keys(cachedCards).length > 7000 && Object.values(cachedCards)[0].marketable && Date.now() - lastCachedCards < settings.cardsRefreshInterval * 60000) {
  238. callback(cachedCards);
  239. return;
  240. }
  241.  
  242. GM_xmlhttpRequest({
  243. "method": `GET`,
  244. "url": `https://bartervg.com/browse/cards/json/`,
  245. "timeout": 30000,
  246. "onload": (response) => {
  247. let json = null;
  248. try {
  249. json = JSON.parse(response.responseText);
  250. if (Object.keys(json).length > 7000) { // sanity check
  251. GM_setValue(`swi_tradingcards`, JSON.stringify(json));
  252. GM_setValue(`swi_tradingcards_last`, Date.now());
  253. }
  254. callback(json);
  255. return;
  256. } catch (error) {
  257. console.log(`[Steam Web Integration] Unable to parse Barter.vg trading cards data. Using cached data...`, error);
  258. }
  259. callback(cachedCards);
  260. },
  261. "onerror": (response) => {
  262. console.log(`[Steam Web Integration] An error occurred while refreshing Barter.vg trading cards data. Using cached data...`, response);
  263. callback(cachedCards);
  264. },
  265. "ontimeout": () => {
  266. console.log(`[Steam Web Integration] It took too long to refresh Barter.vg trading cards data. Using cached data...`);
  267. callback(cachedCards);
  268. }
  269. });
  270. }
  271.  
  272. function refreshBundles(callback) {
  273. if (!settings.wantBundles) {
  274. callback();
  275. return;
  276. }
  277.  
  278. const cachedBundles = JSON.parse(GM_getValue(`swi_bundles`, null));
  279. const lastCachedBundles = parseInt(GM_getValue(`swi_bundles_last`, 0), 10) || 1;
  280. if (cachedBundles && Object.keys(cachedBundles).length > 7000 && Object.values(cachedBundles)[0].bundles && Date.now() - lastCachedBundles < settings.bundlesRefreshInterval * 60000) {
  281. callback(cachedBundles);
  282. return;
  283. }
  284.  
  285. GM_xmlhttpRequest({
  286. "method": `GET`,
  287. "url": `https://bartervg.com/browse/bundles/json/`,
  288. "timeout": 30000,
  289. "onload": (response) => {
  290. let json = null;
  291. try {
  292. json = JSON.parse(response.responseText);
  293. if (Object.keys(json).length > 7000) { // sanity check
  294. GM_setValue(`swi_bundles`, JSON.stringify(json));
  295. GM_setValue(`swi_bundles_last`, Date.now());
  296. }
  297. callback(json);
  298. return;
  299. } catch (error) {
  300. console.log(`[Steam Web Integration] Unable to parse Barter.vg bundles data. Using cached data...`, error);
  301. }
  302. callback(cachedBundles);
  303. },
  304. "onerror": (response) => {
  305. console.log(`[Steam Web Integration] An error occurred while refreshing Barter.vg bundles data. Using cached data...`, response);
  306. callback(cachedBundles);
  307. },
  308. "ontimeout": () => {
  309. console.log(`[Steam Web Integration] It took too long to refresh Barter.vg bundles data. Using cached data...`);
  310. callback(cachedBundles);
  311. }
  312. });
  313. }
  314.  
  315. function doApp(elem, wishlist, ownedApps, ignoredApps, decommissioned, limited, cards, bundles, dlc, lcs, dlcs, dlclcs, llcs, clcs, blcs) {
  316. $(elem).addClass(`swi`);
  317.  
  318. const attr = settings.attributes.find((a) => /apps?\//g.test($(elem).attr(a)));
  319. const attrVal = $(elem).attr(attr);
  320. const appID = parseInt(attrVal.match(/apps?\/[0-9]+/g)[0].split(/apps?\//)[1], 10);
  321. if (Number.isNaN(appID)) {
  322. return;
  323. }
  324.  
  325. setTimeout(() => {
  326. let html;
  327. let subject;
  328. if (dlc && dlc[appID]) {
  329. subject = `DLC`;
  330. } else if (!dlc) {
  331. subject = `Game or DLC`;
  332. } else {
  333. subject = `Game`;
  334. }
  335.  
  336. if (ownedApps && ownedApps.includes(appID)) { // if owned
  337. html = getIconHTML(settings.ownedColor, `${subject} (${appID}) owned`, lcs, settings.ownedIcon); // ✔
  338. } else if (wishlist.includes(appID)) { // if not owned and wishlisted
  339. html = getIconHTML(settings.wishlistColor, `${subject} (${appID}) wishlisted`, lcs, settings.wishlistIcon); // ❤
  340. } else { // else not owned and not wishlisted
  341. html = getIconHTML(settings.unownedColor, `${subject} (${appID}) not owned`, lcs, settings.unownedIcon); // ✘
  342. }
  343.  
  344. if (settings.wantIgnores && ignoredApps && ignoredApps.includes(appID)) { // if ignored and enabled
  345. html += getIconHTML(settings.ignoredColor, `${subject} (${appID}) ignored`, llcs, settings.ignoredIcon); // 🛇
  346. }
  347.  
  348. if (settings.wantDLC && dlc && dlc[appID]) { // if DLC and enabled
  349. const base = dlc[appID].base_appID;
  350. const ownsBase = ownedApps.includes(base);
  351. html += getIconHTML(settings.dlcColor, `${subject} (${appID}) is downloadable content for an ${ownsBase ? `` : `un`}owned base game (${base})`, dlclcs, settings.dlcIcon); // ⇩
  352. }
  353.  
  354. if (settings.wantDecommissioned && decommissioned) { // if decommissioned and have cache or new data
  355. const app = decommissioned.find((obj) => obj.appid === appID.toString());
  356. if (app) { // if decommissioned?
  357. 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}/`); // 🗑
  358. }
  359. }
  360.  
  361. if (settings.wantLimited && limited && limited[appID]) { // if is limited
  362. html += getIconHTML(settings.limitedColor, `Game (${appID}) has profile features limited`, llcs, settings.limitedIcon); // ⚙
  363. }
  364.  
  365. if (settings.wantCards && cards && cards[appID] && cards[appID].cards && cards[appID].cards > 0) { // if has cards and enabled
  366. html += 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}`);
  367. }
  368.  
  369. if (settings.wantBundles && bundles && bundles[appID] && bundles[appID].bundles && bundles[appID].bundles > 0) { // if is bundled and enabled
  370. html += 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`);
  371. }
  372.  
  373. if (settings.prefix) {
  374. $(elem).before(getBoxNode(html, appID));
  375. } else {
  376. $(elem).after(getBoxNode(html, appID));
  377. }
  378.  
  379. $(elem).parent().css(`overflow`, `visible`);
  380. }, 0);
  381. }
  382.  
  383. function doSub(elem, ownedPackages, lcs) {
  384. $(elem).addClass(`swi`);
  385. const subID = parseInt(elem.href.match(/sub\/[0-9]+/g)[0].split(`sub/`)[1], 10);
  386. if (Number.isNaN(subID)) {
  387. return;
  388. }
  389.  
  390. setTimeout(() => {
  391. let html;
  392.  
  393. if (ownedPackages.includes(subID)) { // if owned
  394. html = getIconHTML(settings.ownedColor, `Package (${subID}) owned`, lcs, settings.ownedIcon); // ✔
  395. } else { // else not owned
  396. html = getIconHTML(settings.unownedColor, `Package (${subID}) not owned`, lcs, settings.unownedIcon); // ✖
  397. }
  398.  
  399. if (settings.prefix) {
  400. $(elem).before(getBoxNode(html, undefined, subID));
  401. } else {
  402. $(elem).after(getBoxNode(html, undefined, subID));
  403. }
  404.  
  405. $(elem).parent().css(`overflow`, `visible`);
  406. }, 0);
  407. }
  408.  
  409. function integrate(userdata, decommissioned, cards, bundles, limited, dlc, lastCached) {
  410. const ignoredApps = Object.keys(userdata.rgIgnoredApps).map((a) => parseInt(a, 10));
  411. const ownedApps = userdata.rgOwnedApps;
  412. const ownedPackages = userdata.rgOwnedPackages;
  413. const wishlist = userdata.rgWishlist;
  414.  
  415. const lcs = settings.dateOverride ? new Date(lastCached).toLocaleString(`sv-SE`) : new Date(lastCached).toLocaleString();
  416. const dlcs = new Date(GM_getValue(`swi_decommissioned_last`, 0)).toLocaleString(settings.dateOverride ? `sv-SE` : undefined);
  417. const dlclcs = new Date(GM_getValue(`swi_dlc_last`, 0)).toLocaleString(settings.dateOverride ? `sv-SE` : undefined);
  418. const llcs = new Date(GM_getValue(`swi_limited_last`, 0)).toLocaleString(settings.dateOverride ? `sv-SE` : undefined);
  419. const clcs = new Date(GM_getValue(`swi_tradingcards_last`, 0)).toLocaleString(settings.dateOverride ? `sv-SE` : undefined);
  420. const blcs = new Date(GM_getValue(`swi_bundles_last`, 0)).toLocaleString(settings.dateOverride ? `sv-SE` : undefined);
  421.  
  422. const appSelector = [
  423. `[href*="steamcommunity.com/app/"]`,
  424. `[href*="steamdb.info/app/"]`,
  425. `[href*="store.steampowered.com/agecheck/app/"]`,
  426. `[href*="store.steampowered.com/app/"]`,
  427. `[style*="cdn.akamai.steamstatic.com/steam/apps/"]`,
  428. `[style*="cdn.edgecast.steamstatic.com/steam/apps/"]`,
  429. `[style*="steamcdn-a.akamaihd.net/steam/apps/"]`,
  430. `[style*="steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/"]`,
  431. `[style*="steamcdn-a.opskins.media/steam/apps/"]`,
  432. `[style*="steamcdn-a.opskins.media/steamcommunity/public/images/apps/"]`,
  433. `[style*="steamdb.info/static/camo/apps/"]`,
  434. `img[src*="cdn.akamai.steamstatic.com/steam/apps/"]`,
  435. `img[src*="cdn.edgecast.steamstatic.com/steam/apps/"]`,
  436. `img[src*="steamcdn-a.akamaihd.net/steam/apps/"]`,
  437. `img[src*="steamcdn-a.akamaihd.net/steamcommunity/public/images/apps/"]`,
  438. `img[src*="steamcdn-a.opskins.media/steam/apps/"]`,
  439. `img[src*="steamcdn-a.opskins.media/steamcommunity/public/images/apps/"]`,
  440. `img[src*="steamdb.info/static/camo/apps/"]`
  441. ].filter((s) => settings.attributes.find((a) => s.includes(`[${a}`))).map((s) => `${s}:not(.swi)`).join(`, `);
  442.  
  443. const subSelector = [
  444. `[href*="steamdb.info/sub/"]`,
  445. `[href*="store.steampowered.com/sub/"]`
  446. ].map((s) => `${s}:not(.swi)`).join(`, `);
  447.  
  448. let delaySWI;
  449. const doSWI = (delay = 750) => {
  450. if (delaySWI) {
  451. clearTimeout(delaySWI);
  452. }
  453.  
  454. delaySWI = setTimeout(() => {
  455. if (settings.dynamicContent !== `ping`) {
  456. console.log(`[Steam Web Integration] Executing`);
  457. }
  458.  
  459. clearTimeout(delaySWI);
  460. $(appSelector, document.body).get().forEach((elem) => doApp(elem, wishlist, ownedApps, ignoredApps, decommissioned, limited, cards, bundles, dlc, lcs, dlcs, dlclcs, llcs, clcs, blcs));
  461. $(subSelector, document.body).get().forEach((elem) => doSub(elem, ownedPackages, lcs), 0);
  462. }, delay);
  463. };
  464.  
  465. const clearSWI = () => {
  466. console.log(`[Steam Web Integration] Clearing`);
  467. $(`.swi-block`).remove();
  468. $(`.swi`).removeClass(`swi`);
  469. };
  470.  
  471. const reloadSWI = () => {
  472. clearSWI();
  473. doSWI(0);
  474. };
  475.  
  476. $(document).ready(() => {
  477. doSWI(0);
  478.  
  479. GM_registerMenuCommand(`Run again`, () => doSWI(0));
  480. GM_registerMenuCommand(`Clear all`, clearSWI);
  481. GM_registerMenuCommand(`Clear and run (reload)`, reloadSWI);
  482.  
  483. unsafeWindow.doSWI = doSWI;
  484. unsafeWindow.clearSWI = clearSWI;
  485. unsafeWindow.reloadSWI = reloadSWI;
  486.  
  487. if (settings.dynamicContent === `disabled`) {
  488. return;
  489. }
  490.  
  491. if (settings.dynamicContent === `observe`) {
  492. $(`body`).observe({ "added": true, "attributes": true, "attributeFilter": settings.attributes }, appSelector, () => doSWI());
  493. $(`body`).observe({ "added": true, "attributes": true, "attributeFilter": [`href`] }, subSelector, () => doSWI());
  494. } else if (settings.dynamicContent === `ping`) {
  495. setInterval(doSWI, 1500);
  496. }
  497. });
  498. }
  499.  
  500. function refresh() {
  501. const cachedJson = GM_getValue(`swi_data`, null);
  502. let lastCached = GM_getValue(`swi_last`, 0);
  503.  
  504. if (cachedJson && Date.now() - lastCached < settings.userRefreshInterval * 60000) {
  505. const userdata = JSON.parse(cachedJson);
  506. refreshDecommissioned((decommissioned) => refreshDLC((dlc) => refreshLimited((limited) => refreshCards((cards) => refreshBundles((bundles) => integrate(userdata, decommissioned, cards, bundles, limited, dlc, lastCached))))));
  507. return;
  508. }
  509.  
  510. GM_xmlhttpRequest({
  511. "method": `GET`,
  512. "url": `https://store.steampowered.com/dynamicstore/userdata/?t=${Date.now()}`,
  513. "onload": (response) => {
  514. let userdata = JSON.parse(response.responseText);
  515.  
  516. if (userdata.rgOwnedApps.length === 0 && userdata.rgOwnedPackages.length === 0 && userdata.rgIgnoredApps.length === 0 && userdata.rgWishlist.length === 0) { // not logged in
  517. if (!cachedJson) {
  518. console.log(`[Steam Web Integration] No cached information available. Please login to Steam to fix this.`);
  519. return;
  520. }
  521.  
  522. userdata = JSON.parse(cachedJson);
  523. } else {
  524. lastCached = Date.now();
  525. GM_setValue(`swi_last`, lastCached);
  526. GM_setValue(`swi_data`, JSON.stringify(userdata));
  527. }
  528.  
  529. refreshDecommissioned((decommissioned) => refreshDLC((dlc) => refreshLimited((limited) => refreshCards((cards) => refreshBundles((bundles) => integrate(userdata, decommissioned, cards, bundles, limited, dlc, lastCached))))));
  530. }
  531. });
  532. }
  533.  
  534. function init() {
  535. const settingsuri = `https://revadike.com/swi/settings`;
  536.  
  537. const defaults = {
  538. "attributes": [`href`, `src`, `style`],
  539. "blackList": ``,
  540. "boxed": true,
  541. "bundleColor": `#ffff00`,
  542. "bundleIcon": `&#127873;&#xFE0E;`,
  543. "bundlesRefreshInterval": 2880,
  544. "cardColor": `#0000ff`,
  545. "cardIcon": `&#x1F0A1`,
  546. "cardsRefreshInterval": 2880,
  547. "dateOverride": false,
  548. "decommissionedColor": `#ffffff`,
  549. "decommissionedIcon": `&#9760;`,
  550. "decommissionedRefreshInterval": 1440,
  551. "dlcColor": `#a655b2`,
  552. "dlcIcon": `&#8681;`,
  553. "dlcRefreshInterval": 1440,
  554. "dynamicContent": `observe`,
  555. "ignoredColor": `#808080`,
  556. "ignoredIcon": `&#128683;&#xFE0E;`,
  557. "limitedColor": `#00ffff`,
  558. "limitedIcon": `&#9881;`,
  559. "limitedRefreshInterval": 2880,
  560. "ownedColor": `#008000`,
  561. "ownedIcon": `&#10004;`,
  562. "prefix": false,
  563. "unownedColor": `#ff0000`,
  564. "unownedIcon": `&#10008;`,
  565. "userRefreshInterval": 1,
  566. "wantBundles": true,
  567. "wantCards": true,
  568. "wantDecommissioned": true,
  569. "wantDLC": true,
  570. "wantIgnores": true,
  571. "wantLimited": true,
  572. "wishlistColor": `#ff69b4`,
  573. "wishlistIcon": `&#10084;`
  574. };
  575.  
  576. const stylesheet = `
  577. .swi-block {
  578. display: inline-block;
  579. line-height: initial;
  580. }
  581. .swi-block.boxed {
  582. background: rgba(0, 0, 0, 0.7);
  583. border-radius: 5px;
  584. margin: auto 4px auto 4px;
  585. padding: 2px 4px 2px 4px;
  586. position: relative;
  587. }
  588. .swi-block span {
  589. cursor: help;
  590. margin: 2px;
  591. }
  592. .swi-block a {
  593. text-decoration: none;
  594. }
  595. `;
  596.  
  597. settings = JSON.parse(GM_getValue(`swi_settings`, JSON.stringify(defaults)));
  598. Object.keys(defaults).forEach((setting) => {
  599. if (settings[setting] !== undefined) {
  600. return;
  601. }
  602.  
  603. settings[setting] = defaults[setting];
  604. });
  605.  
  606. if (unsafeWindow.location.href.startsWith(settingsuri)) {
  607. unsafeWindow.onChange = onChange;
  608. unsafeWindow.scriptInfo = GM_info.script;
  609. unsafeWindow.settings = settings;
  610. $(document).ready(displaySettings);
  611. } else if (!settings.blackList.split(`\n`).find((url) => unsafeWindow.location.href.includes(url.trim()))) {
  612. boxNode = createBoxNode();
  613. GM_addStyle(stylesheet);
  614. GM_registerMenuCommand(`Change settings`, () => unsafeWindow.open(settingsuri, `_blank`));
  615. refresh();
  616. }
  617. }
  618.  
  619. init();
  620. // ==/Code==