- // ==UserScript==
- // @name Bundle Helper
- // @version 2.0.4
- // @author Dillon Regimbal
- // @namespace https://dillonr.com
- // @description Highlights games you already own on Steam, on other sites. Modified from https://gf.qytechs.cn/scripts/16105-bundle-helper/
- // @match *://cubicbundle.com/*
- // @match *://dailyindiegame.com/*
- // @match *://forums.steampowered.com/forums/showthread.php?*
- // @match *://www.gogobundle.com/latest/bundles/*
- // @match *://otakumaker.com/*
- // @match *://www.otakumaker.com/*
- // @match *://otakubundle.com/latest/bundles/*
- // @match *://steamcommunity.com/*/home*
- // @match *://steamcommunity.com/groups/*/announcements*
- // @match *://steamcompanion.com/gifts/*
- // @match *://steamground.com/*
- // @match *://store.steampowered.com/
- // @match *://store.steampowered.com/account/notinterested/*
- // @match *://store.steampowered.com/app/*
- // @match *://store.steampowered.com/widget/*
- // @match *://store.steampowered.com/search/*
- // @match *://whosgamingnow.net/*
- // @match *://www.bunchkeys.com/*
- // @match *://www.bundlekings.com/*
- // @match *://www.fanatical.com/*
- // @match *://www.dailyindiegame.com/*
- // @match *://www.gamebundle.com/*
- // @match *://www.hrkgame.com/*
- // @match *://www.humblebundle.com/*
- // @match *://www.indiegala.com/*
- // @match *://www.orlygift.com/*
- // @match *://www.reddit.com/r/*/comments/*
- // @match *://www.superduperbundle.com/*
- // @match *://www.sgtools.info/*
- // @match *://steamkeys.ovh/*
- // @match *://steamdb.info/*
- // @match *://itch.io/*
- // @match *://*.itch.io/*
- // @run-at document-start
- // @grant GM_addStyle
- // @grant GM_xmlhttpRequest
- // @grant GM_getValue
- // @grant GM_setValue
- // @connect store.steampowered.com
- // @connect www.hrkgame.com
- // @connect www.fanatical.com
- // @connect www.steamgifts.com
- // @icon https://store.steampowered.com/favicon.ico
- // @license GPL-3.0-only
- // @noframes
- // ==/UserScript==
-
- // Connect to store.steampowered.com to get owner info
- // Connect to www.hrkgame.com and www.fanatical.com to get Steam ID of each products
- // Connect to www.steamgifts.com to get bundle threads
-
- // License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html
-
- // Since 2016-01-10
- // https://gf.qytechs.cn/scripts/16105-bundle-helper/
-
- // Since 2020-05-21
- // https://gf.qytechs.cn/scripts/403878-bundle-helper
- // https://github.com/dregimbal/UserScripts/blob/master/BundleHelper.user.js
-
- (function () {
- 'use strict'
-
- let write_console_messages = true
-
- let name_profile_json = 'bh_profile_json'
- let name_profile_time = 'bh_profile_time'
- let owned_item_class = 'bh_owned'
- let steam_profile = getSteamProfile()
-
- let default_steam_url_selector = 'a[href*=\'store.steampowered.com/\']'
-
- let divButton = document.createElement('div')
-
- function attachOnLoad(callback) {
- window.addEventListener('load', function (e) {
- callback()
- })
- }
-
- function attachOnReady(callback) {
- document.addEventListener('DOMContentLoaded', function (e) {
- callback()
- })
- }
-
- function writeConsoleMessage(message) {
- if (write_console_messages) {
- console.log(message)
- }
- }
-
- let timeoutList = []
- let intervalList = []
-
- function setTimeoutCustom(func, tm, params) {
- let id = setTimeout(func, tm, params)
- timeoutList.push(id)
- return id
- }
-
- function clearTimeoutAll() {
- for (let i = 0; i < timeoutList.length; i++) {
- clearTimeout(timeoutList[i])
- }
- }
-
- function clearIntervalAll() {
- for (let i = 0; i < intervalList.length; i++) {
- clearInterval(intervalList[i])
- }
- }
-
- function getUnixTimestamp() {
- return parseInt(Date.now() / 1000)
- }
-
- function isProfileCacheExpired() {
- let isExpired = false
- let timestampExpired = 15 * 60
-
- let profileTimestamp = GM_getValue(name_profile_time, 0)
-
- let profileTimestampDiff = getUnixTimestamp() - profileTimestamp
- if (profileTimestampDiff > timestampExpired) {
- isExpired = true
- }
-
- if (!isExpired) {
- writeConsoleMessage('Profile Cache Updated ' + profileTimestampDiff + 's ago')
- } else {
- writeConsoleMessage('Profile Cache Expired: ' + profileTimestampDiff)
- }
-
- return isExpired
- }
-
- function setProfileCache(json) {
- GM_setValue(name_profile_json, json)
- GM_setValue(name_profile_time, getUnixTimestamp())
- }
-
- function getSteamProfile() {
- if (isProfileCacheExpired()) {
- updateSteamProfileCache()
- }
- return GM_getValue(name_profile_json, 0)
- }
-
- function markOwned(query, getElementCallback, getProductIdCallback
- , classOwned, classNotInterested, classWished, getCountCallback) {
- if (!document.querySelector(query)) {
- // writeConsoleMessage("markOwned: empty");
- return
- }
-
- if (!getElementCallback) {
- getElementCallback = function (ele, type) {
- // type -> 1: Owned, 2: Ignored, 3: Wishlist
- return ele
- }
- }
-
- if (!getProductIdCallback) {
- getProductIdCallback = function (ele) {
- return ele.getAttribute('href')
- }
- }
-
- if (!getCountCallback) {
- getCountCallback = function (appCount, subCount, appOwned, subOwned) {
- }
- }
-
- if (!classOwned) {
- classOwned = ''
- }
- if (!classNotInterested) {
- classNotInterested = ''
- }
- if (!classWished) {
- classWished = ''
- }
-
- let rgxId = /[0-9]{3,}/g
- let rgxApp = /((:\/\/(store\.steampowered\.com|steamcommunity\.com|steamdb\.info)(\/agecheck)?\/app|\/steam\/apps)\/[0-9]+|^[0-9]{3,}$)/i
- let rgxSub = /(:\/\/(store\.steampowered\.com|steamdb\.info)\/sub|\/steam\/subs)\/[0-9]+/i
-
- let markFromJson = function (dataRes) {
- if (!dataRes) {
- writeConsoleMessage('markFromJson: empty')
- return
- }
-
- let countOwned = [0, 0]
- let countAll = [0, 0]
-
- let eleApps = document.querySelectorAll(query)
- writeConsoleMessage(eleApps)
- for (let i = 0; i < eleApps.length; i++) {
- let attrHref = getProductIdCallback(eleApps[i])
- let ids = attrHref.match(rgxId)
- if (ids) {
- // writeConsoleMessage('Matched ID "' + ids[0] + '" from url: ' + attrHref)
- let valId = parseInt(ids[0])
- if (rgxApp.test(attrHref)) {
- if (isAppOwned(valId)) {
- let ele = getElementCallback(eleApps[i], 1)
- if (ele && classOwned !== '') {
- ele.classList.add(classOwned)
- }
- countOwned[0]++
- } else if (isAppWishlisted(valId)) {
- let ele = getElementCallback(eleApps[i], 3)
- if (ele && classWished !== '') {
- ele.classList.add(classWished)
- }
- } else if (isAppIgnored(valId)) {
- let ele = getElementCallback(eleApps[i], 2)
- if (ele && classNotInterested !== '') {
- ele.classList.add(classNotInterested)
- }
- } else {
- // writeConsoleMessage('App: Unowned - https://store.steampowered.com/app/' + valId + '/')
- }
-
- countAll[0]++
- } else if (rgxSub.test(attrHref)) {
- if (steam_profile.rgOwnedPackages.indexOf(valId) > -1) {
- writeConsoleMessage('Sub: owned - https://store.steampowered.com/sub/' + valId + '/')
- let ele = getElementCallback(eleApps[i], 1)
- if (ele && classOwned !== '') {
- ele.classList.add(classOwned)
- }
- countOwned[1]++
- } else {
- // writeConsoleMessage('Sub: not owned - https://store.steampowered.com/sub/' + valId + '/')
- }
- countAll[1]++
- } else {
- writeConsoleMessage('Cannot determine url type: ' + attrHref)
- }
- } else {
- writeConsoleMessage('Cannot match ID from url: ' + attrHref)
- }
- }
-
- writeConsoleMessage('App: Owned ' + countOwned[0] + '/' + countAll[0])
- writeConsoleMessage('Sub: Owned ' + countOwned[1] + '/' + countAll[1])
-
- getCountCallback(countAll[0], countAll[1], countOwned[0], countOwned[1])
- }
-
- markFromJson(steam_profile)
- }
-
- function updateSteamProfileCache() {
- GM_xmlhttpRequest(
- {
- method: 'GET',
- url: 'https://store.steampowered.com/dynamicstore/userdata/?t=' + getUnixTimestamp(),
- onload: function (response) {
- writeConsoleMessage('Steam User Data: ' + response.responseText.length + ' bytes')
-
- let dataRes = JSON.parse(response.responseText)
-
- setProfileCache(dataRes)
- steam_profile = dataRes
- }
- })
- }
-
- // eslint-disable-next-line no-unused-vars
- function createCacheResetButton() {
- let divCacheResetButton = document.createElement('div')
- divCacheResetButton.classList.add('bh_button')
- divCacheResetButton.id = 'bh_cacheReset'
-
- let cacheResetA = document.createElement('a')
- cacheResetA.setAttribute('onclick', 'return false;')
- cacheResetA.textContent = 'Reset Bundle Helper Cache'
-
- divCacheResetButton.appendChild(cacheResetA)
- document.body.appendChild(divCacheResetButton)
-
- divCacheResetButton.addEventListener('click',
- function () {
- updateSteamProfileCache()
- })
- }
-
- function addMarkBtnHandler(onClickFunction, argsArray) {
- if (!document.body.contains(divButton)) {
- document.body.appendChild(divButton)
- }
-
- divButton.addEventListener('click', () => {
- onClickFunction.apply(null, argsArray)
- })
- }
-
- function setElementOwned(element) {
- if (typeof element !== 'undefined' && element !== null) {
- element.classList.add(owned_item_class)
- }
- }
-
- /**
- * @description Checks the Steam game's ID against the owned apps
- * @param {number} steamID The ID to check
- * @returns {boolean} True when the game is owned
- */
- function isAppOwned(steamID) {
- if (steam_profile.rgOwnedApps.includes(parseInt(steamID))) {
- writeConsoleMessage('App: Owned - https://store.steampowered.com/app/' + steamID + '/')
- return true
- }
- // writeConsoleMessage('App: Unowned - https://store.steampowered.com/app/' + steamID + '/')
- return false
- }
-
- /**
- * @description Checks the Steam game's ID against the wishlisted apps
- * @param {number} steamID The ID to check
- * @returns {boolean} True when the game is wishlisted
- */
- function isAppWishlisted(steamID) {
- if (steam_profile.rgWishlist.includes(parseInt(steamID))) {
- writeConsoleMessage('App: Wishlisted - https://store.steampowered.com/app/' + steamID + '/')
- return true
- }
- return false
- }
-
- /**
- * @description Checks the Steam game's ID against the ignored apps
- * @param {number} steamID The ID to check
- * @returns {boolean} True when the game is ignored
- */
- function isAppIgnored(steamID) {
- if (typeof steam_profile.rgIgnoredApps[steamID] !== 'undefined') {
- writeConsoleMessage('App: Ignored - https://store.steampowered.com/app/' + steamID + '/')
- return true
- }
- return false
- }
-
- /**
- * @description Parses a string for a Steam game ID
- * @param {string} str The string/URL that contains the Steam game ID
- * @returns {number} Steam game ID
- */
- function getSteamIDFromString(str) {
- let rgxId = /[0-9]{3,}/g
- let matches = str.match(rgxId)
- if (matches) {
- return parseInt(matches[0])
- }
- return null
- }
-
- /**
- * Searches the document for Steam game ownership
- * @param {string|null} steamLinkSelector The CSS selector to match Steam links
- * @param {HTMLElement} elementToMark The element to mark as owned
- * @returns {undefined}
- */
- function markBySteamLinkSelector(steamLinkSelector, elementToMark) {
- let selectorQuery
- if (typeof steamLinkSelector === 'undefined' || steamLinkSelector === null) {
- selectorQuery = default_steam_url_selector
- } else {
- selectorQuery = steamLinkSelector
- }
-
- document.querySelectorAll(selectorQuery).forEach(steamStoreLink => {
- let steamID = getSteamIDFromString(steamStoreLink.href)
- if (steamID !== null) {
- if (isAppOwned(steamID)) {
- if (typeof elementToMark === 'undefined' || elementToMark === null) {
- // No element passed, mark the link element itself
- setElementOwned(steamStoreLink)
- } else if (typeof elementToMark === 'function') {
- // Function passed, call the function passing in the link element
- let element = elementToMark(steamStoreLink)
- setElementOwned(element)
- } else {
- // Element passed, attempt to mark
- setElementOwned(elementToMark)
- }
- }
- }
- })
- }
-
- /**
- * Checks a page for Steam game ownership
- * @param {string} storePageUrl The store page that contains the Steam link
- * @param {string|null} steamLinkSelector The CSS selector to match Steam links
- * @param {HTMLElement} elementToMark The element to mark as owned
- * @returns {undefined}
- */
- function markByStorePageUrl(storePageUrl, steamLinkSelector, elementToMark) {
- let selector
- if (typeof steamLinkSelector === 'undefined' || steamLinkSelector === null) {
- selector = default_steam_url_selector
- } else {
- selector = steamLinkSelector
- }
-
- GM_xmlhttpRequest({
- method: 'GET',
- url: storePageUrl,
- onload: function (response) {
- let parser = new DOMParser()
- let storePage = parser.parseFromString(response.responseText, 'text/html')
-
- let steamLink = storePage.querySelector(selector)
- if (steamLink !== null) {
- let steamID = getSteamIDFromString(steamLink.href)
- if (steamID !== null) {
- if (isAppOwned(steamID)) {
- setElementOwned(elementToMark)
- }
- }
- } else {
- writeConsoleMessage(`No steam links found on page "${storePageUrl}" with selector "${selector}"`)
- }
- }
- })
- return
- }
-
- /**
- * Checks all matching links for Steam game ownership
- * @param {string} storePageSelector The CSS selector to match store links
- * @param {string} steamLinkSelector The CSS selector to match Steam links
- * @param {HTMLElement} elementToMark The element to mark as owned
- * @returns {undefined}
- */
- function markByStorePageSelector(storePageSelector, steamLinkSelector, elementToMark) {
- let storePageLinkElements = document.querySelectorAll(storePageSelector)
-
- storePageLinkElements.forEach(storePageLinkElement => {
- if (typeof elementToMark === 'undefined' || elementToMark === null) {
- markByStorePageUrl(storePageLinkElement.href, steamLinkSelector, storePageLinkElement)
- } else if (typeof elementToMark === 'function') {
- // Function passed, call the function passing in the link element
- let element = elementToMark(storePageLinkElement)
- markByStorePageUrl(storePageLinkElement.href, steamLinkSelector, element)
- } else {
- writeConsoleMessage(storePageLinkElement)
- markByStorePageUrl(storePageLinkElement, steamLinkSelector, elementToMark)
- }
- })
- return
- }
-
- async function GetSteamAppList() {
- return new Promise(function (resolve, reject) {
- setTimeout(function () {
- GM_xmlhttpRequest({
- method: 'GET',
- url: 'https://api.steampowered.com/ISteamApps/GetAppList/v2/',
- onload: response => {
- setTimeout(function () {
- if (response.status !== 200) {
- reject([
- response.status,
- response.statusText,
- response.readyState,
- response.responseHeaders,
- response.finalUrl
- ].join(', '))
- } else {
- let appList = JSON.parse(response.responseText).applist.apps
- appList.forEach(app => {
- app.name = app.name.toLowerCase().replace(/[^a-z0-9]/g, '')
- })
- resolve(appList)
- }
- }, 0)
- }
- })
- }, 0)
- })
- }
-
- function main() {
- if (window !== window.parent) {
- // https://developer.mozilla.org/en-US/docs/Web/API/Window/parent
- // Don't run inside of a frame
- return
- }
-
- if (!divButton) {
- divButton = document.createElement('div')
- }
- divButton.classList.add('bh_button')
- divButton.id = 'bh_markOwned'
-
- let eleA = document.createElement('a')
- eleA.setAttribute('onclick', 'return false;')
- eleA.textContent = 'Mark Owned'
-
- divButton.appendChild(eleA)
-
- // Create button to refresh profile details
- // createCacheResetButton()
-
- GM_addStyle(
- ' .bh_button { '
- + ' border-radius: 2px; border: medium none; padding: 10px; display: inline-block; '
- + ' cursor: pointer; background: #67C1F5 none repeat scroll 0% 0%; '
- + ' width: 120px; text-align: center; } '
- + ' .bh_button a { '
- + ' text-decoration: none !important; color: #FFF !important; '
- + ' padding: 0px 2px; } '
- + ' .bh_button:hover a { '
- + ' color: #0079BF !important; } '
- + ' .bh_button, .bh_button a { '
- + ' font-family: Verdana; font-size: 12px; '
- + ' line-height: 16px; } '
- + ' .bh_owned { background-color: #7CA156 !important; '
- + ' transition: background 500ms ease 0s; } '
- + ' #bh_markOwned { '
- + ' position: fixed; right: 20px; bottom: 20px; z-index: 33; } '
- + ' #bh_cacheReset { '
- + ' position: fixed; right: 20px; bottom: 60px; z-index: 33; } '
- + ' #bh_OpenLib { '
- + ' position: fixed; right: 20px; bottom: 65px; z-index: 33; } '
- )
-
- let url = document.documentURI
-
- if (url.includes('hrkgame.com')) {
- GM_addStyle(
- ' .bh_owned { background-color: #2B823A !important;/* background-color: #97BA22 !important;*/ } '
- + ' #bh_markOwned { bottom: 40px !important; } '
- + ' #bh_cacheReset { bottom: 80px !important; } '
- + '.catalog.ui.items .item.bh_owned .content a, .catalog.ui.items .item.bh_owned .content .description {color: #333 !important;}'
- + '.hrktable_content a.browse_catalogues_items div.label.bh_owned { background-color: #2B823A!important }'
- )
-
- if (url.includes('/randomkeyshop/make-bundle')) {
- let onClickFunction = function () {
- document.querySelectorAll('#result div.header[data-href*=\'/games/product/\']').forEach(link => {
- markByStorePageUrl(link.getAttribute('data-href'), 'a.item[href*=\'store.steampowered.com/\']', element => element.parentElement.parentElement)
- })
- }
- addMarkBtnHandler(onClickFunction)
- } else if (url.includes('/games/products/?search')) {
- addMarkBtnHandler(markByStorePageSelector, ['.item a.header[href*=\'/games/product/\']', 'a.item[href*=\'store.steampowered.com/\']', element => element.parentElement.parentElement])
- } else if (url.includes('/games/product/')) {
- addMarkBtnHandler(markBySteamLinkSelector, ['a.item[href*=\'store.steampowered.com/\']', document.querySelector('.ui.maincontainer')])
- } else {
- let onClickFunction = function () {
- markByStorePageSelector('.offer_column a[href*=\'/games/product/\']', 'a.item[href*=\'store.steampowered.com/\']', element => element.parentElement)
-
- document.querySelectorAll('a.browse_catalogues_items[href*=\'/games/product/\']').forEach(link => {
- if (link.textContent.includes('Steam')) {
- let elementToMark = link.querySelector('div.label')
- markByStorePageUrl(link.href, 'a.item[href*=\'store.steampowered.com/\']', elementToMark)
- }
- })
- }
- addMarkBtnHandler(onClickFunction)
- }
- } else if (url.includes('itch.io')) {
- if (url.includes('/my-collections') || url.includes('/my-purchases') || url.includes('/games') || url.includes('/s/') || url.includes('/c/') || url.includes('/b/')) {
- GM_addStyle(
- ' .grid_outer .game_grid_widget .game_cell.bh_owned span, .grid_outer .game_grid_widget .game_cell.bh_owned div, .grid_outer .game_grid_widget .game_cell.bh_owned a { color: #ffffff !important; } '
- )
-
- addMarkBtnHandler(() => {
- let storePageLinkElements = document.querySelectorAll('.game_cell_data a.game_link')
-
- storePageLinkElements.forEach(storePageLinkElement => {
- if (!storePageLinkElement.href.includes('/b/')) {
- // Don't search bundle pages for Steam links
- markByStorePageUrl(storePageLinkElement.href, default_steam_url_selector, storePageLinkElement.parentElement.parentElement.parentElement)
- }
- })
- })
- } else if (url.includes('/recommendations')) {
- GM_addStyle(
- ' .index_game_cell_widget.game_cell.bh_owned span, .index_game_cell_widget.game_cell.bh_owned div, .index_game_cell_widget.game_cell.bh_owned a.user_link { color: #ffffff; } '
- )
- addMarkBtnHandler(markByStorePageSelector, ['a.title', default_steam_url_selector, element => element.parentElement.parentElement])
- }
- } else if (url.includes('fanatical.com')) {
- GM_addStyle(
- ' .bh_owned { background-color: #0c6c22 !important; } '
- + ' .bh_owned .card-body div.card-body { background-color: #0c6c22 !important; } '
- + ' .bh_owned .card-body { background-color: #0c6c22; } '
- )
-
- if (url.includes('/game/')) {
- addMarkBtnHandler(markBySteamLinkSelector, [default_steam_url_selector, () => document.querySelector('.details-content-container')])
- } else if (url.includes('/bundle/')) {
- let obTarget_root = document.querySelector('#root')
- if (obTarget_root) {
- let tmOb_root = -1
- let obMu_root = new MutationObserver(function (mutations) {
- mutations.forEach(function (mutation) {
- if (mutation.type !== 'attributes'
- || mutation.target.tagName === 'TR') {
- clearTimeout(tmOb_root)
- tmOb_root = setTimeoutCustom(function () {
- markBySteamLinkSelector(default_steam_url_selector, element => element.parentElement
- .parentElement.parentElement.parentElement)
- }, 200)
- }
- })
- })
-
- let obConfig_root = { childList: true, subtree: true }
- obMu_root.observe(obTarget_root, obConfig_root)
- }
- } else if (url.includes('/pick-and-mix/')) {
- let onClickFunction = function () {
- let hook = __REACT_DEVTOOLS_GLOBAL_HOOK__
- let rootFragmentFiber = Array.from(hook.getFiberRoots(1))[0].current
- let rootCompFiber = rootFragmentFiber.child
- let rootComp = rootCompFiber.stateNode
- let state = rootComp.props.store.getState()
- let bundles = [...state.pickAndMix.all]
- bundles.forEach(bundle => {
- let productsArr = bundle.products
- let ownedGames = []
- productsArr.forEach(product => {
- let id = product._id
- if (isAppOwned(id)) {
- writeConsoleMessage('You own game ID: ' + id + ' - "' + product.name + '"')
- ownedGames.push(product.name)
- }
- })
- if (ownedGames.length > 0) {
- document.querySelectorAll('.card-overlay p').forEach(p => {
- if (ownedGames.includes(p.textContent)) {
- setElementOwned(p.parentElement.parentElement.parentElement.parentElement)
- }
- })
- }})
-
- }
- addMarkBtnHandler(onClickFunction)
- }
-
- let onClickFunction = function () {
- let timeouts = []
- let gameUrls = []
- document.querySelectorAll('a[href*=\'/game/\']').forEach(game => {
- if (gameUrls.includes(game.href)) {
- return
- }
- gameUrls.push(game.href)
- timeouts.push(function () {
- let gamePage = game.href.replace('/en', '').replace('/game/', '/api/products/') + '/en'
- GM_xmlhttpRequest({
- method: 'GET',
- headers: {
- 'Host': 'www.fanatical.com',
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0',
- 'Accept': 'application/json',
- 'Content-Type': 'application/x-www-form-urlencoded',
- 'Referer': game.href
- },
- url: gamePage,
- onload: function (response) {
- // writeConsoleMessage('status ' + response.status + ' ' + gamePage)
- if (response.status === 200) {
- let apiResponse = JSON.parse(response.responseText)
- if (typeof apiResponse.steam.id !== 'undefined') {
- if (isAppOwned(apiResponse.steam.id)) {
- setElementOwned(game.parentElement.parentElement.parentElement.parentElement)
- }
- }
- if (timeouts.length > 0) {
- setTimeout(timeouts.pop(), 50)
- }
- } else if (timeouts.length > 0) {
- setTimeout(timeouts.pop(), 400)
- }
- }
- })
- })
- })
- setTimeout(timeouts.pop(), 100)
- }
- addMarkBtnHandler(onClickFunction)
- } else if (url.includes('reddit.com')) {
- GM_addStyle(
- ' .bh_owned , .md .bh_owned code { background-color: #DFF0D8 !important; } '
- + ' li > .bh_owned, div > p > .bh_owned { padding: 0px 2px 0px 2px; } '
- )
-
- addMarkBtnHandler(markOwned, ['td > a[href*=\'store.steampowered.com/\']', function (ele) {
- return ele.parentElement.parentElement
- }, null, 'bh_owned'])
-
-
- setTimeout(function () {
- markOwned('td > a[href*=\'store.steampowered.com/\']', function (ele) {
- return ele.parentElement.parentElement
- }, null, 'bh_owned')
-
- markOwned('li > a[href*=\'store.steampowered.com/\']', function (ele) {
- return ele.parentElement
- }, null, 'bh_owned')
-
- markOwned('li > p > a[href*=\'store.steampowered.com/\']', function (ele) {
- return ele.parentElement.parentElement
- }, null, 'bh_owned')
-
- markOwned('div > p > a[href*=\'store.steampowered.com/\']'
- , null, null, 'bh_owned')
- }, 1000)
- } else if (url.includes('indiegala.com')) {
- GM_addStyle(
- ' #bh_markOwned {bottom: 70px !important;}'
- + ' .bh_owned, .bh_owned .bundle-item-trading { background-color: rgba(125, 174, 45, 0.9) !important; } '
- + ' .ig-bundle { padding-left: 3px; padding-right: 3px; margin-bottom: 3px; } '
- + ' .bh_owned.ig-bundle { background-color: rgba(125, 174, 45) !important; } '
- + ' .bh_owned.ig-bundle .bundle-item-trading { background-color: rgba(125, 174, 45, 0) !important; } '
- + ' .bh_owned .add-info-button-cont .left, .bh_owned .add-info-button-cont .palette-background-2 { '
- + ' background-color: #7DAE2D !important; } '
- + ' .bh_owned .add-info-button-cont .right .inner-info, .bh_owned .add-info-button-cont .right .palette-border-2 { '
- + ' border-color: #7DAE2D !important; } '
- + ' .bh_owned.medium-game .game-cover-medium { border: 3px solid #7DAE2D; background-color: rgba(125, 174, 45, 0.4); } '
- + ' .bh_owned.game-data-cont { background-color: #76AD1C !important; } '
- + ' .bundle-item-trading-cards-cont span { opacity: 0.7; } '
- + ' .span-title .title_game, .span-title .title_drm, .span-title .title_music { '
- + ' line-height: 43px !important; margin: 10px 0px 10px 15px !important; '
- + ' padding-left: 10px !important; border-radius: 3px !important; } '
- + ' .medium-game { min-height: 146px; } '
- )
-
- // Insert email to bundle section
- let countRetryEmail = 10
- let tmRetryEmail = setInterval(function () {
- let eleEmail = document.querySelector('.account-email')
- let eleInput = document.querySelector('.email-input')
- if (eleEmail && eleInput) {
- let email = eleEmail.textContent.trim()
- if (email !== '') {
- eleInput.value = email
- clearInterval(tmRetryEmail)
- }
- }
-
- if (countRetryEmail < 0) {
- clearInterval(tmRetryEmail)
- }
- countRetryEmail--
- }, 3000)
-
- // Change title
- let countRetryTitle = 10
- let tmRetryTitle = setInterval(function () {
- let elesPrice = document.querySelectorAll('.bundle-claim-phrase')
- for (let i = elesPrice.length - 1; i > -1; i--) {
- let elePrice = elesPrice[i].querySelector('span')
- if (elePrice) {
- let price = elePrice.textContent.trim()
- if (price.indexOf('$') === 0) {
- document.title = price + ' ' + document.title
- clearInterval(tmRetryTitle)
- break
- }
- }
- }
-
- if (countRetryTitle < 0) {
- clearInterval(tmRetryTitle)
- }
- countRetryTitle--
- }, 3000)
-
- if (url.includes('indiegala.com/store/') || url.includes('indiegala.com/games') || url === 'https://www.indiegala.com/') {
- let onClickFunction = function () {
- let gameBrowserLinks = document.querySelectorAll('a.main-list-item-clicker')
- for (let i = 0; i < gameBrowserLinks.length; i++) {
- let steamID = getSteamIDFromString(gameBrowserLinks[i].href)
- if (steamID !== null) {
- if (isAppOwned(steamID)) {
- setElementOwned(gameBrowserLinks[i].parentElement)
- }
- }
- }
-
- let smallListLinks = document.querySelectorAll('a.fit-click')
- for (let i = 0; i < smallListLinks.length; i++) {
- let steamID = getSteamIDFromString(smallListLinks[i].href)
- if (steamID !== null) {
- if (isAppOwned(steamID)) {
- setElementOwned(smallListLinks[i].parentElement.querySelector('.item-inner'))
- }
- }
- }
- }
- addMarkBtnHandler(onClickFunction)
- }
- } else if (url.includes('orlygift.com')) {
- addMarkBtnHandler(markByStorePageSelector, ['a[href*=\'/games/\']', default_steam_url_selector, element => element.parentElement])
- } else if (url.includes('cubicbundle.com')) {
- GM_addStyle(
- ' .bh_owned { background-color: #91BA07 !important; } '
- )
- addMarkBtnHandler(markOwned, ['.price a[href*=\'store.steampowered.com/\']', function (ele) {
- return ele.parentElement.parentElement.parentElement.parentElement.parentElement
- }, null, 'bh_owned'])
- } else if (url.includes('dailyindiegame.com')) {
- GM_addStyle(
- ' .bh_owned, .bh_owned a, .bh_owned a:not(:visited) .DIG2content { color: #202020 !important; } '
- )
-
- let onClickFunction = function () {
- let markMap = [{
- selector: '.DIG-content a[href*=\'store.steampowered.com/\']',
- callback: function (ele) {
- return ele.parentElement
- .parentElement.parentElement
- .parentElement.parentElement
- }
- },
- {
- selector: '.DIG2content a[href*=\'store.steampowered.com/\']',
- callback: function (ele) {
- return ele.parentElement.parentElement
- }
- },
- {
- selector: '.DIG3_14_Gray a[href*=\'store.steampowered.com/\']',
- callback: function (ele) {
- return ele.parentElement.parentElement.parentElement
- }
- }]
- for (let i = 0; i < markMap.length; i++) {
- if (document.querySelectorAll(markMap[i].selector).length > 0) {
- markOwned(markMap[i].selector, markMap[i].callback, null, 'bh_owned')
- }
- }
- }
- addMarkBtnHandler(onClickFunction)
- } else if (url.includes('bundlekings.com')) {
- addMarkBtnHandler(markOwned, ['.content-wrap a[href*=\'store.steampowered.com/\']', function (ele) {
- return ele.parentElement.parentElement.parentElement
- }, null, 'bh_owned'])
- } else if (url.includes('otakumaker.com')) {
- GM_addStyle(
- ' .bh_owned { background-color: #91BA07 !important; } '
- )
- addMarkBtnHandler(markOwned, ['.gantry-width-spacer a[href*=\'store.steampowered.com/\']', function (ele) {
- return ele.parentElement.parentElement
- }, null, 'bh_owned'])
- } else if (url.includes('otakubundle.com')) {
- GM_addStyle(
- ' .bh_owned { background-color: #91BA07 !important; } '
- )
- addMarkBtnHandler(markOwned, ['#hikashop_product_left_part > .g-grid > .g-block > .g-block > a[href*=\'store.steampowered.com/\']',
- function (ele) {
- return ele.parentElement.parentElement
- },
- null,
- 'bh_owned'])
- } else if (url.includes('gogobundle.com')) {
- GM_addStyle(
- ' .bh_owned { background-color: #91BA07 !important; border: 1px solid white; } '
- )
-
- addMarkBtnHandler(markOwned, ['.g-block > .g-block > a[href*=\'store.steampowered.com/\']', function (ele) {
- return ele.parentElement.parentElement
- }, null, 'bh_owned'])
- } else if (url.includes('superduperbundle.com')) {
- addMarkBtnHandler(markOwned, ['#gameslist a[href*=\'store.steampowered.com/\']', function (ele) {
- return ele.parentElement.parentElement
- }, null, 'bh_owned'])
- } else if (url.includes('gamebundle.com')) {
- GM_addStyle(
- ' .bh_owned { background-color: #A0CC41 !important; border-bottom: 45px solid rgba(233, 233, 233, 0.5); } '
- + ' .bh_owned .activebundle_game_bundle_debut_title { background-color: #A0CC41 !important; } '
- )
- addMarkBtnHandler(markOwned, ['.activebundle_game_section_full a[href*=\'store.steampowered.com/\']', function (ele) {
- return ele.parentElement
- }, null, 'bh_owned'])
- } else if (url.includes('humblebundle.com')) {
- GM_addStyle(
- ' .game-box img { max-height: 180px !important; max-width: 130px !important; } '
- + ' .image-grid { animation: none !important; } ' +
- ' .bh_owned .entity-details, .bh_owned div.entity-meta, .bh_owned div.entity-meta span.entity-title { background: #7CA156 !important; color: #FFFFFF !important; } ' +
- ' div.slick-track .bh_owned div.entity-meta { background: transparent !important; } '
- )
-
- if (url.includes('/games/')) {
- GM_addStyle(
- ' .bh-owned div.dd-image-box-caption-container { background: #7CA156 !important; } ' +
- ' .bh-owned span.front-page-art-image-text { color: #FFFFFF !important; } '
- )
- let onClickFunction = function () {
- GetSteamAppList()
- .then(appList => {
- let gameTitleElements = document.querySelectorAll('span.front-page-art-image-text')
- for (let gameTitleElement of gameTitleElements) {
- let gameName = gameTitleElement.textContent.toLowerCase().replace(/[^a-z0-9]/g, '')
- let matches = appList.filter(app => app.name === gameName)
- for (let match of matches) {
- if (isAppOwned(match.appid)) {
- setElementOwned(gameTitleElement.parentElement.parentElement.parentElement.parentElement)
- break
- }
- }
- }
- })
- }
- addMarkBtnHandler(onClickFunction)
- } else if (url.includes('/store')) {
- let onClickFunction = function () {
- GetSteamAppList()
- .then(appList => {
- let gameTitleElements = document.querySelectorAll('span.entity-title')
- for (let gameTitleElement of gameTitleElements) {
- let gameName = gameTitleElement.textContent.toLowerCase().replace(/[^a-z0-9]/g, '')
- let matches = appList.filter(app => app.name === gameName)
- for (let match of matches) {
- if (isAppOwned(match.appid)) {
- setElementOwned(gameTitleElement.parentElement.parentElement)
- break
- }
- }
- }
- })
- }
- addMarkBtnHandler(onClickFunction)
- } else if (url.includes('/subscription')) {
- GM_addStyle(
- ' .bh-owned div { background: #7CA156 !important; } ' +
- ' .bh-owned .content-choice-title { color: #FFFFFF !important; } '
- )
- let onClickFunction = function () {
- GetSteamAppList()
- .then(appList => {
- let gameTitleElements = document.querySelectorAll('span.content-choice-title')
- for (let gameTitleElement of gameTitleElements) {
- let gameName = gameTitleElement.textContent.toLowerCase().replace(/[^a-z0-9]/g, '')
- let matches = appList.filter(app => app.name === gameName)
- for (let match of matches) {
- if (isAppOwned(match.appid)) {
- setElementOwned(gameTitleElement.parentElement.parentElement)
- break
- }
- }
- }
- })
- }
- addMarkBtnHandler(onClickFunction)
- }
- } else if (url.includes('steamcompanion.com')) {
- GM_addStyle(
- ' .bh_owned.banner { margin-bottom: 5px !important; margin-top: 35px !important; '
- + ' padding-bottom: 15px !important; padding-top: 15px !important; } '
- + ' .bh_owned.giveaway-links { opacity: 0.75; } '
- )
-
- markOwned('#hero a[href*=\'store.steampowered.com/\']'
- , null, null, 'bh_owned')
-
- // Mark
- {
- let query = '.giveaway-links img[src^=\'https://steamcdn-a.akamaihd.net/steam/apps/\']'
- let getLabelCallback = function (ele) {
- return ele.parentElement.parentElement.parentElement
- }
-
- let apps = []
-
- let eleApps = document.querySelectorAll(query)
-
- for (let i = 0; i < eleApps.length; i++) {
- let app = /[0-9]+/.exec(eleApps[i].getAttribute('src'))
- if (app !== null) {
- apps.push(app[0])
- }
- }
-
- apps = apps.filter(function (elem, index, self) {
- return index === self.indexOf(elem)
- })
-
- writeConsoleMessage('Apps: ' + apps.length)
- let appAll = apps.join(',')
-
- GM_xmlhttpRequest(
- {
- method: 'GET',
- headers:
- {
- 'Cache-Control': 'max-age=0'
- },
- url: 'https://store.steampowered.com/api/appuserdetails/?appids=' + appAll,
- onload: function (response) {
- let dataRes = JSON.parse(response.responseText)
-
- let countOwned = 0
-
- let elementApps = document.querySelectorAll(query)
- for (let i = 0; i < elementApps.length; i++) {
- let appUrl = elementApps[i].getAttribute('src')
- if (appurl.includes('https://steamcdn-a.akamaihd.net/steam/apps/')) {
- let app = /[0-9]+/.exec(appUrl)
- if (app !== null) {
- if (typeof dataRes[app] !== 'undefined') {
- if (dataRes[app].success) {
- if (dataRes[app].data.is_owned) {
- let eleLabel = getLabelCallback(elementApps[i])
- eleLabel.classList.add('bh_owned')
- countOwned++
- } else {
- // writeConsoleMessage("App: not owned - http://store.steampowered.com/app/" + app + "/");
- }
- } else {
- // writeConsoleMessage("App: not success - https://steamdb.info/app/" + app + "/");
- }
- }
- }
- }
- }
-
- writeConsoleMessage('Apps: owned - ' + countOwned)
- }
- // End onload
- })
- }
- } else if (url.includes('store.steampowered.com')) {
- if (url.includes('/widget/')) {
- GM_addStyle(
- ' .bh_owned { background-color: transparent !important; } '
- + ' .bh_owned a { color: #71A034 !important; }'
- )
-
- markOwned('.main_text a[href*=\'store.steampowered.com/\']', function (ele) {
- return ele.parentElement
- }, null, 'bh_owned')
- } else if (url.includes('/app/')) {
- GM_addStyle(
- ' .bh_owned { '
- + ' background-color: #6D8C1A !important; '
- + ' padding: 0px 2px 0px 2px; '
- + ' } '
- )
-
- markOwned(
- '.glance_details p > a[href*=\'store.steampowered.com/\']'
- + ', .game_area_dlc_bubble a[href*=\'store.steampowered.com/\']'
- ,
- null,
- null,
- 'bh_owned')
- } else if (url.includes('/notinterested/')) {
- GM_addStyle(
- ' .bh_owned { '
- + ' background-color: #6D8C1A !important; '
- + ' padding: 5px 100px 5px 5px !important; '
- + ' margin-left: -5px; margin-right: 50px; '
- + ' } '
- )
-
- markOwned('.ignoredapps > a[href*=\'store.steampowered.com/\']'
- , null, null, 'bh_owned')
- } else if (url.includes('/search/')) {
- GM_addStyle(
- ' .bh_owned { '
- + ' background-color: #6D8C1A66 !important; '
- + ' } '
- )
-
- markOwned('.search_result_row[href*=\'store.steampowered.com/\']'
- , null, null, 'bh_owned')
- }
- } else if (url.includes('steamcommunity.com')) {
- GM_addStyle(
- ' .bh_owned { background-color: #71A034 !important; '
- + ' padding: 0px 2px 0px 2px; } '
- + ' .bh_owned.blotter_userstatus_game { padding: 0px; border-color: #71A034; } '
- )
-
- if (url.includes('/home')) {
- let querySteamHome = '.blotter_gamepurchase_details a[href*=\'store.steampowered.com/\']:not(.bh_owned) '
- + ', .blotter_author_block a[href*=\'store.steampowered.com/\']:not(.bh_owned) '
- + ', .blotter_author_block a[href*=\'steamcommunity.com/app/\']:not(.bh_owned) '
- + ', .blotter_daily_rollup_line a[href*=\'steamcommunity.com/app/\']:not(.bh_owned) '
- markOwned(querySteamHome, function (ele, type) {
- if (type === 1) {
- if (ele.classList.contains('blotter_userstats_game')) {
- ele.parentElement.classList.add('bh_owned')
- } else {
- ele.classList.add('bh_owned')
- }
- }
- })
-
- let targetObMark = document.getElementById('blotter_content')
- if (targetObMark) {
- let tmObMark = -1
- let obMark = new MutationObserver(function (mutations) {
- mutations.forEach(function () {
- clearTimeout(tmObMark)
- tmObMark = setTimeout(function (querySteamH) {
- markOwned(querySteamH, function (ele, type) {
- if (type === 1 && !ele.classList.contains('blotter_userstats_game')) {
- ele.classList.add('bh_owned')
- }
- })
- }, 100, querySteamHome)
- })
- })
-
- let configObMark = { childList: true }
- obMark.observe(targetObMark, configObMark)
- }
- } else if (url.includes('/announcements')) {
- markOwned('.announcement_body a[href*=\'store.steampowered.com/\']'
- , null, null, 'bh_owned')
- }
- } else if (url.includes('forums.steampowered.com')) {
- GM_addStyle(
- ' .bh_owned { background-color: #71A034 !important; '
- + ' padding: 0px 2px 0px 2px;'
- + ' } '
- )
-
- markOwned('div[id^=\'post_message\'] a[href*=\'store.steampowered.com/\']'
- , null, null, 'bh_owned')
- } else if (url.includes('whosgamingnow.net')) {
- if (url.includes('/discussion')) {
- GM_addStyle(
- ' .bh_owned { '
- + ' padding: 0px 2px 0px 2px;'
- + ' } '
- )
-
- markOwned('.MessageList a[href*=\'store.steampowered.com/\']'
- , null, null, 'bh_owned')
- } else if (url.includes('/redeem')) {
- GM_addStyle(
- ' .bh_owned { '
- + ' border: 1px solid #FFF;'
- + ' } '
- + ' .bh_owned .BoxArt { '
- + ' border: 0px !important;'
- + ' } '
- )
-
- markOwned('.GameInfo a[href*=\'store.steampowered.com/\']', function (ele) {
- return ele.parentElement.parentElement.parentElement
- })
- } else if (url.includes('/giveaway')) {
- GM_addStyle(
- ' .bh_owned { '
- + ' border: 5px solid #7CA156;'
- + ' } '
- )
-
- markOwned('img[src*=\'://cdn.akamai.steamstatic.com/steam/\']'
- , null, null, 'bh_owned')
- }
- } else if (url.includes('steamground.com') && url.includes('/wholesale')) {
- GM_addStyle(
- ' .bh_owned { background-color: #48B24B !important; } '
- + ' .bh_owned .wholesale-card_title { color: #373d41 !important; } '
- + ' .bh_steam { display: none; } '
- )
-
- let elesTitle = document.querySelectorAll('.wholesale-card_title')
- if (elesTitle.length > 0) {
- GM_xmlhttpRequest(
- {
- method: 'GET',
- url: 'https://www.steamgifts.com/discussion/iy081/steamground-wholesale-build-a-bundle',
- onload: function (response) {
- let data = response.responseText
- let eleContainer = document.createElement('div')
- eleContainer.innerHTML = data
-
- let eleComment = eleContainer.querySelector('.comment__description')
- if (eleComment) {
- let elesGame = eleComment.querySelectorAll('table td:nth-child(1) a[href*=\'store.steampowered.com/\']')
- if (elesGame.length > 0) {
- let arrTitle = []
- for (let i = 0; i < elesTitle.length; i++) {
- arrTitle.push(elesTitle[i].textContent.trim())
- }
-
- for (let i = 0; i < elesGame.length; i++) {
- let isMatch = false
- let game = elesGame[i].textContent.trim().toLowerCase()
- for (let j = 0; j < elesTitle.length; j++) {
- let title = elesTitle[j].textContent.trim().toLowerCase()
- if (game === title
- || (title.indexOf('|') > -1 && game === title.replace('|', ':'))
- || (game === 'ball of light' && title === 'ball of light (journey)')
- || (game === 'its your last chance in new school' && title === 'it is yоur last chance in new schооl')
- || (game === 'shake your money simulator 2016' && title === 'shake your money simulator')
- || (game === 'spakoyno: back to the ussr 2.0' && title === 'spakoyno back to the ussr 2.0')
- || (game === 'or' && title === 'or!')) {
- isMatch = true
-
- arrTitle = arrTitle.filter(function (value) {
- return value !== elesTitle[j].textContent.trim()
- })
- }
-
- if (isMatch) {
- let elemA = document.createElement('a')
- elemA.classList.add('bh_steam')
- elemA.href = elesGame[i].href
- elesTitle[j].parentElement.parentElement.appendChild(elemA)
-
- break
- }
- }
- if (!isMatch) {
- writeConsoleMessage('Not match: ' + elesGame[i].href + ' ' + elesGame[i].textContent)
- }
- }
-
- if (arrTitle.length > 0) {
- writeConsoleMessage('Not match: ' + arrTitle.length)
- for (let i = 0; i < arrTitle.length; i++) {
- writeConsoleMessage('Not match: ' + arrTitle[i])
- }
- }
-
- markOwned('.wholesale-card > a[href*=\'store.steampowered.com/\']', function (ele) {
- return ele.parentElement
- }, null, 'bh_owned')
- }
- }
- }
- // End onload
- })
- }
- } else if (url.includes('bunchkeys.com')) {
- GM_addStyle(
- ' .bh_owned { border: #B5D12E 3px solid !important; '
- + ' margin-left: -3px; margin-top: -3px; } '
- )
-
- addMarkBtnHandler(markOwned, [default_steam_url_selector, function (ele) {
- return ele.parentElement
- }, null, 'bh_owned'])
- } else if (url.includes('sgtools.info')) {
- GM_addStyle(
- ' .bh_owned { background-color: #71A034 !important; } '
- )
- if (url.includes('/lastbundled')) {
- markOwned('#content > div > table > tbody > tr > td > a[href*=\'store.steampowered.com/\']', function (ele) {
- return ele.parentElement.parentElement
- }, null, 'bh_owned')
- } else if (url.includes('/deals')) {
- markOwned('.deal_game_image > img[src*=\'cdn.akamai.steamstatic.com/steam/\']', function (ele) {
- return ele.parentElement
- }, null, 'bh_owned')
- } else if (url.includes('/whitelisted')) {
- markOwned('.cmGame > a[href*=\'store.steampowered.com/\']', function (ele) {
- return ele.parentElement
- }, null, 'bh_owned')
- }
- } else if (url.includes('steamkeys.ovh')) {
- markOwned('td > a[href*=\'store.steampowered.com/\']', function (ele) {
- return ele.parentElement.parentElement
- }, null, 'bh_owned')
- } else if (url.includes('steamdb.info')) {
- if (window !== window.parent) {
- return
- }
-
- GM_addStyle(
- ' .bh_owned, tr.bh_owned td { background-color: #DDF7D3 !important; } '
- + ' .bh_owned_transparent { background-color: #bcf0a880 !important; } '
- )
-
- markOwned(' \
- #apps .app \
- , #dlc .app \
- , .container > .table .app \
- , .sales-section .app \
- , .page-search .app \
- ', null, function (ele) {
- return ele.getAttribute('data-appid')
- }, 'bh_owned')
-
- markOwned(' \
- #subs .package \
- , .sales-section .package \
- , .page-search .package \
- ', null, function (ele) {
- return '/steam/subs/' + ele.getAttribute('data-subid')
- }, 'bh_owned')
-
- markOwned('.table-products .app'
- , null, function (ele) {
- return ele.getAttribute('data-appid')
- }, 'bh_owned_transparent')
-
- markOwned('.app-history .appid'
- , function (ele) {
- return ele.parentElement
- }, function (ele) {
- return ele.textContent.trim()
- }, 'bh_owned')
- }
-
- window.addEventListener('beforeunload', function () {
- clearTimeoutAll()
- clearIntervalAll()
- })
- }
-
- attachOnReady(main)
- }())