├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── BUG-REPORT.yml │ ├── FEATURE-REQUEST.yml │ └── config.yml └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── Images ├── augmented_steam_options.png ├── library_details.png └── steam_store.png ├── LICENSE ├── README.md ├── backend └── main.py ├── bun.lock ├── eslint.config.mjs ├── frontend ├── HltbInjection.ts ├── HtlbData.ts ├── index.tsx └── tsconfig.json ├── helpers ├── build_zip.py ├── clean-maps.mjs ├── convert-manifest.ts ├── generate-metadata.sh ├── publish.sh └── update-version.ts ├── package.json ├── patches └── @steambrew%2Fclient@4.2.1.patch ├── plugin.json ├── release.config.mjs ├── requirements.txt ├── tsconfig.json └── webkit ├── browser-types.ts ├── browser.ts ├── header.ts ├── index.tsx ├── preferences.ts ├── script-loading.ts ├── shared.ts └── tsconfig.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: boss_sloth -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/BUG-REPORT.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Create a bug report to help me find and fix bugs! 3 | title: "[Bug]" 4 | labels: [ bug ] 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Describe the Bug 9 | description: A clear and concise description of what you're experiencing. If applicable, add screenshots to help explain your problem. 10 | validations: 11 | required: true 12 | - type: input 13 | attributes: 14 | label: Plugin Version 15 | description: What version of the plugin are you running? 16 | placeholder: "e.g. v1.2.3" 17 | validations: 18 | required: true 19 | 20 | - type: input 21 | attributes: 22 | label: Millennium Version 23 | description: What version of Millennium are you using? 24 | placeholder: "e.g. v2.17.2" 25 | validations: 26 | required: true 27 | - type: dropdown 28 | attributes: 29 | label: Operating System 30 | options: 31 | - Windows 32 | - Linux 33 | validations: 34 | required: true 35 | - type: textarea 36 | attributes: 37 | label: Expected Behavior 38 | description: A clear and concise description of what you expected to happen. 39 | validations: 40 | required: true 41 | - type: textarea 42 | attributes: 43 | label: Actual Behavior 44 | description: A clear and concise description of what actually happened. 45 | validations: 46 | required: true 47 | - type: checkboxes 48 | attributes: 49 | label: Reproducibility 50 | description: Is this issue consistently reproducible? 51 | options: 52 | - label: Yes, I can reproduce this issue consistently 53 | - type: textarea 54 | attributes: 55 | label: Steps To Reproduce 56 | description: Steps to reproduce the behavior. 57 | placeholder: | 58 | 1. In this environment... 59 | 2. With these settings... 60 | 3. Click '...' 61 | 4. See error... 62 | validations: 63 | required: true 64 | - type: textarea 65 | attributes: 66 | label: Anything else? 67 | description: | 68 | Links? References? Screenshots? Anything that will give me more context about the issue you are encountering! 69 | 70 | Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. 71 | validations: 72 | required: false 73 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/FEATURE-REQUEST.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Suggest an idea for this plugin! 3 | title: "[Feature]" 4 | labels: [ enhancement ] 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Describe the feature you'd like! 9 | description: A clear and concise description of what you want to see to be added to this plugin. 10 | validations: 11 | required: true 12 | - type: textarea 13 | attributes: 14 | label: Anything else? 15 | description: | 16 | Links? References? Screenshots? Anything that will give me more context about the feature you are requesting! 17 | 18 | Tip: You can attach images by clicking this area to highlight it and then dragging files in. 19 | validations: 20 | required: false 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build Plugin 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: 'Version tag (e.g., 1.0.0 or beta-1)' 8 | required: true 9 | type: string 10 | workflow_call: 11 | inputs: 12 | version: 13 | description: 'Version tag (e.g., 1.0.0 or beta-1)' 14 | required: true 15 | type: string 16 | outputs: 17 | artifact-name: 18 | description: "Name of the uploaded artifact" 19 | value: Augmented-Steam-plugin-${{ inputs.version }} 20 | 21 | jobs: 22 | build: 23 | runs-on: ubuntu-latest 24 | 25 | steps: 26 | - name: Checkout code 27 | uses: actions/checkout@v4 28 | with: 29 | token: ${{ secrets.GITHUB_TOKEN }} 30 | submodules: recursive 31 | 32 | - name: Setup Python 33 | uses: actions/setup-python@v4 34 | with: 35 | python-version: '3.12' 36 | 37 | - name: Set up Node.js 38 | uses: actions/setup-node@v2 39 | with: 40 | node-version: '21' 41 | 42 | - name: Setup Bun 43 | uses: oven-sh/setup-bun@v1 44 | with: 45 | bun-version: latest 46 | 47 | - name: Install dependencies 48 | run: | 49 | bun install 50 | pip install -r requirements.txt 51 | 52 | - name: Build and create zip 53 | run: python helpers/build_zip.py 54 | env: 55 | RELEASE_VERSION: ${{ inputs.version }} 56 | 57 | - name: Extract zip for artifact 58 | run: | 59 | mkdir -p temp_artifact 60 | unzip "build/Augmented-Steam-plugin-${{ inputs.version }}.zip" -d temp_artifact/ 61 | 62 | - name: Upload Build Artifact 63 | uses: actions/upload-artifact@v4 64 | with: 65 | name: Augmented-Steam-plugin-${{ inputs.version }} 66 | path: temp_artifact/* 67 | include-hidden-files: true 68 | if-no-files-found: error 69 | overwrite: true 70 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create Release 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | permissions: 7 | contents: write 8 | issues: write 9 | pull-requests: write 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - name: Checkout code 17 | uses: actions/checkout@v4 18 | with: 19 | token: ${{ secrets.GITHUB_TOKEN }} 20 | submodules: recursive 21 | 22 | - name: Setup Python 23 | uses: actions/setup-python@v4 24 | with: 25 | python-version: '3.12' 26 | 27 | - name: Set up Node.js 28 | uses: actions/setup-node@v2 29 | with: 30 | node-version: '21' 31 | 32 | - name: Setup Bun 33 | uses: oven-sh/setup-bun@v1 34 | with: 35 | bun-version: latest 36 | 37 | - name: Install dependencies 38 | run: | 39 | bun install 40 | pip install -r requirements.txt 41 | 42 | - name: Run Semantic Release 43 | run: bun semantic-release 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .venv/ 3 | .millennium/ 4 | build/ 5 | !bun.lockb 6 | metadata.json 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "AugmentedSteam"] 2 | path = AugmentedSteam 3 | url = https://github.com/IsThereAnyDeal/AugmentedSteam 4 | branch = master 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.1.8](https://github.com/BossSloth/AugmentedSteam-Extension-Plugin/compare/v1.1.7...v1.1.8) (2025-04-14) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * add metadata.json to release zip ([305350d](https://github.com/BossSloth/AugmentedSteam-Extension-Plugin/commit/305350da76f10180459beed55f1f4f1475a29345)) 7 | 8 | ## [1.1.7](https://github.com/BossSloth/AugmentedSteam-Extension-Plugin/compare/v1.1.6...v1.1.7) (2025-03-29) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * don't provide name to logger ([4eb1288](https://github.com/BossSloth/AugmentedSteam-Extension-Plugin/commit/4eb1288345d3a2492963b1554a6a9ab4475b5bc4)) 14 | * send error and warning logs to backend ([59d9294](https://github.com/BossSloth/AugmentedSteam-Extension-Plugin/commit/59d9294381b58e378fad2a2bd255380286a4d303)) 15 | 16 | ## [1.1.6](https://github.com/BossSloth/AugmentedSteam-Extension-Plugin/compare/v1.1.5...v1.1.6) (2025-03-17) 17 | 18 | ## [1.1.5](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/compare/v1.1.4...v1.1.5) (2025-02-16) 19 | 20 | 21 | ### Bug Fixes 22 | 23 | * **#11:** Fix styles (tested now) and change banner styles to sit inline with content; prevent committing vscode settings; bun lock changes ([4f4472a](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/4f4472a846ebea542574d360c8c6b3d5b6c72c8a)) 24 | * **#13:** Early Access Game Banner breaks SteamDB achievement group images ([2a9f949](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/2a9f94978a7dffff31b2f9e33e46eb85ca23c89e)), closes [#13](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/issues/13) 25 | * ci not being able to comment on issues ([e2e8b73](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/e2e8b739ab15e077735c8b922f3fb3926dfac43a)) 26 | 27 | 28 | ### Reverts 29 | 30 | * gitignore and bun lockfile ([d71f8a8](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/d71f8a83dfbf2b3842213c4d022ae00f4402595a)) 31 | 32 | ## [1.1.4](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/compare/v1.1.3...v1.1.4) (2025-02-07) 33 | 34 | 35 | ### Performance Improvements 36 | 37 | * exclude img and localization from build to make the build smaller ([f923520](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/f923520d498d454601bc9b2ea54dcbe90688bb00)) 38 | 39 | ## [1.1.3](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/compare/v1.1.2...v1.1.3) (2025-01-29) 40 | 41 | 42 | ### Bug Fixes 43 | 44 | * **#6:** Do better wait on mainDocument ([cab3883](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/cab3883a4b45f0610e21fc204723609a2d88bf12)), closes [#6](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/issues/6) 45 | 46 | ## [1.1.2](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/compare/v1.1.1...v1.1.2) (2025-01-23) 47 | 48 | 49 | ### Bug Fixes 50 | 51 | * **#3,#7:** Do special fetch to get inventory or other pages that need credentials to get rid of not logged in banner, closes [#3](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/issues/3), [#7](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/issues/7) ([b00b9c7](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/b00b9c7ea64902827956c250681fb91b9e66da4f)) 52 | * **#6:** wait on main main window to be loaded ([2e18b5b](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/2e18b5be6ed1ec61671a0d47d4161743a97050c1)), closes [#6](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/issues/6) 53 | * build sometimes not working ([142ae3b](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/142ae3ba73a41c39c2cfc1882efcb96dc2e1ad83)) 54 | * css not properly loading in on preferences page in prod mode ([0cd2902](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/0cd2902f1de31b9a01c9b2a8a9e214001e276e47)) 55 | 56 | ## [1.1.1](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/compare/v1.1.0...v1.1.1) (2025-01-03) 57 | 58 | 59 | ### Bug Fixes 60 | 61 | * AugmentedSteam build not installing dev dependencies ([784f231](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/784f231398af83b543140f0055b976572bc407e4)) 62 | * just fully remove unused react dependency to fix build ([cccdbe4](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/cccdbe4264285b6a2326c7c0b5e258ea3f527bcc)) 63 | * library info sometimes fully disappearing by now try catching it ([8f7e9bd](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/8f7e9bd548a197d07d70b8092667104ca6ac37c3)) 64 | * library info sometimes not loading in ([1dcd728](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/1dcd72859966e9608877bcaa889fd8eca212ae8a)) 65 | * library info sometimes not loading in ([0f699d5](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/0f699d50323b4b118e68b84dffd7752f5c38fa8d)) 66 | 67 | # [1.1.0](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/compare/v1.0.0...v1.1.0) (2025-01-01) 68 | 69 | 70 | ### Bug Fixes 71 | 72 | * .releaserc ([b59d978](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/b59d978ba100b020b0eff0a5d3ce6d514b7e8de5)) 73 | 74 | 75 | ### Features 76 | 77 | * Show HLTB times in library game details ([6532b0d](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/6532b0d7e178e66b2b54603a7e0db42eb4014cc6)) 78 | * Show player count in library game details ([2a8d851](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/2a8d85166fbbebf40817bf40e7fe9e4d5b44f43e)) 79 | 80 | # 1.0.0 (2024-12-30) 81 | 82 | 83 | ### Bug Fixes 84 | 85 | * build_zip ([34205cb](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/34205cbe7e5eaf297c87079284ca40f239732d0e)) 86 | * ci ([d529546](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/d5295467fc354295a3cfd736cf50f98fc6c8d900)) 87 | * ci ([bf8afdd](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/bf8afdd499eb62262cb783d493f1eda76b89d760)) 88 | * **ci:** include hidden files ([39dfc99](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/39dfc99e61d5eadbba20febd43e6f3016e139e05)) 89 | * **ci:** update build command ([bd3ffa6](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/bd3ffa6f198243da2d02d3657e1c5622c3ec94f3)) 90 | * **ci:** use bun to build ([d3e6f31](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/d3e6f3160b1d475e9c9eb1c9e880750166933acd)) 91 | * make sure steamdb links are only opened once ([b760324](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/b7603244d9239e7140af297afc792111780979a5)) 92 | * turn of dev and fix prod build issues ([98870d8](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/98870d88bd1732ca6aa58a59b7a68ddd18d95d50)) 93 | 94 | 95 | ### Features 96 | 97 | * add preferences menu ([5f099dc](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/5f099dc4b252e10278c05cbd15aee0fae885bfe8)) 98 | * open links in external browser or popup ([d36ff58](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/d36ff58b4798219f4ee0d1ec32e525ad1f419787)) 99 | 100 | 101 | ### Performance Improvements 102 | 103 | * improved performance a lot & fixed a bunch of bugs & made plugin more cross compatible by not overriding chrome ([06833d5](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/commit/06833d50426ff14751543d7cb2b441c0f2c5fc95)) 104 | -------------------------------------------------------------------------------- /Images/augmented_steam_options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossSloth/AugmentedSteam-Extension-Plugin/0728119366d138b7a8689a13585a69f2d1776351/Images/augmented_steam_options.png -------------------------------------------------------------------------------- /Images/library_details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossSloth/AugmentedSteam-Extension-Plugin/0728119366d138b7a8689a13585a69f2d1776351/Images/library_details.png -------------------------------------------------------------------------------- /Images/steam_store.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BossSloth/AugmentedSteam-Extension-Plugin/0728119366d138b7a8689a13585a69f2d1776351/Images/steam_store.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 BossSloth 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Augmented Steam Plugin for Millennium 2 | This plugin ports the functionality of the [Augmented Steam Browser Extension](https://github.com/IsThereAnyDeal/AugmentedSteam) to the Steam client using Millennium. 3 | 4 | ## Features 5 | 6 | > Pretty much all the Augmented Steam extension's features are included in this plugin. 7 | 8 | See the [AugmentedSteam Browser Extension](https://github.com/IsThereAnyDeal/AugmentedSteam) for more details about 9 | each feature or the [extension page](https://augmentedsteam.com/) for more images. 10 | Also some features from the extension are ported to the library see 11 | [Library extra details](#extra-library-details) 12 | 13 | ## Installation 14 | 15 | 1. Ensure you have [Millennium](https://github.com/shdwmtr/millennium) installed on your Steam client 16 | 2. Download the [latest release](https://github.com/tddebart/AugmentedSteam-Extension-Plugin/releases) of this plugin 17 | 3. Place the plugin files in your Millennium plugins directory (should be a plugins folder in your Steam client 18 | directory) 19 | 4. Restart your Steam client 20 | 5. Enable the Augmented Steam plugin in the Millennium plugin menu 21 | 6. FULLY restart the Steam client again 22 | 23 | ## Usage 24 | 25 | Once installed it should just work out of the box. 26 |
27 | To see if the plugin is working you can go to any store app page and look for the newly added features (highlighted in 28 | yellow) 29 | ![Augmented Steam features](Images/steam_store.png) 30 | 31 | ## Configuration 32 | 33 | To configure the plugin: 34 | 35 | 1. Open Steam and navigate to the main store page 36 | 2. Click on "Your Store" in the top menu, then select "Preferences" 37 |
2.5. Or just click on your profile top right, then Account details 38 | 3. Look for the "Augmented Steam" button and click it 39 | 40 | Alternatively, you can access the options page directly by entering this URL in your Steam client: 41 | https://store.steampowered.com/account/?augmented-steam=true 42 | 43 | Here you can customize various features and behaviors of the Augmented Steam plugin to suit your preferences. Just like 44 | in the 45 | browser extension. 46 | 47 | ## Images 48 | 49 | #### Store 50 | 51 | ![Augmented Steam store page](Images/steam_store.png) 52 | 53 | ### Extra library details 54 | 55 | After clicking on the (i) in the library you will now also see the HowLongToBeat times and player count in the game 56 | details. These are cached for 1 hour. 57 | ![Library extra details](Images/library_details.png) 58 | 59 | #### Options page 60 | 61 | ![Augmented Steam options](Images/augmented_steam_options.png) 62 | 63 | ## Contributing 64 | 65 | Contributions are welcome! Please feel free to submit a Pull Request. 66 | 67 | ### Steps on how to set up the project 68 | 69 | > It is recommended to put the plugin repository in your Steam plugins folder or make some symbolic links to the 70 | > repository for easier development. 71 | 72 | 1. Clone the repository using `git clone https://github.com/tddebart/AugmentedSteam-Extension-Plugin.git` 73 | 2. Pull the AugmentedSteam submodule using `git submodule update --init` 74 | 3. Run `bun install` or use your favorite package manager 75 | 4. Run `bun build-augmented-steam` to build the AugmentedSteam submodule 76 | 5. Now you can run `bun watch` or `bun dev` to build the plugin and watch for changes 77 | 78 | > Note: `bun dev` will only watch for changes in the webkit and frontend folder and just require a `F5` in the steam 79 | > client to reload. 80 | > For changes to the backend, you will need to fully restart the Steam client. 81 | 82 | ## Credits 83 | 84 | - Original Augmented Steam Extension: [AugmentedSteam](https://github.com/IsThereAnyDeal/AugmentedSteam) 85 | - [Millennium](https://github.com/shdwmtr/millennium) 86 | 87 | ## License 88 | 89 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 90 | 91 | -------------------------------------------------------------------------------- /backend/main.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | 4 | import Millennium 5 | import PluginUtils # type: ignore 6 | import requests 7 | 8 | logger = PluginUtils.Logger() 9 | 10 | CSS_ID = None 11 | DEBUG = False 12 | 13 | class Logger: 14 | @staticmethod 15 | def warn(message: str) -> None: 16 | logger.warn(message) 17 | 18 | @staticmethod 19 | def error(message: str) -> None: 20 | logger.error(message) 21 | 22 | def GetPluginDir(): 23 | return os.path.abspath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', '..')) 24 | 25 | def BackendFetch(url: str) -> str: 26 | response = requests.get(url) 27 | 28 | result = { 29 | 'status': response.status_code, 30 | 'url': url, 31 | 'headers': dict(response.headers), 32 | 'body': response.text, 33 | } 34 | 35 | return json.dumps(result) 36 | 37 | STEAM_ID = None 38 | 39 | def GetSteamId(): 40 | global STEAM_ID 41 | if STEAM_ID is None: 42 | STEAM_ID = Millennium.call_frontend_method('getSteamId') 43 | return STEAM_ID 44 | 45 | RETRIEVE_URL_RESPONSE = None 46 | 47 | def GetRetrieveUrlResponse(): 48 | global RETRIEVE_URL_RESPONSE 49 | value = RETRIEVE_URL_RESPONSE 50 | RETRIEVE_URL_RESPONSE = None 51 | 52 | return value 53 | 54 | def SetRetrieveUrlResponse(response: str): 55 | global RETRIEVE_URL_RESPONSE 56 | 57 | RETRIEVE_URL_RESPONSE = response 58 | 59 | class Plugin: 60 | def _front_end_loaded(self): 61 | return 62 | 63 | def _load(self): 64 | logger.log(f"bootstrapping AugmentedSteam plugin, millennium {Millennium.version()}") 65 | Millennium.ready() # this is required to tell Millennium that the backend is ready. 66 | 67 | def _unload(self): 68 | logger.log("unloading") 69 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import pluginJs from '@eslint/js'; 2 | import stylistic from '@stylistic/eslint-plugin'; 3 | import { globalIgnores } from 'eslint/config'; 4 | import globals from 'globals'; 5 | import tseslint from 'typescript-eslint'; 6 | 7 | export default tseslint.config( 8 | { files: ['**/*.{js,mjs,cjs,ts}'] }, 9 | globalIgnores(['.millennium/', '.venv/', 'AugmentedSteam/', 'helpers/clean-maps.mjs']), 10 | { 11 | languageOptions: { 12 | globals: { ...globals.browser, ...globals.node }, 13 | parserOptions: { 14 | projectService: { 15 | allowDefaultProject: ['*config.mjs'], 16 | }, 17 | tsconfigRootDir: import.meta.dirname, 18 | }, 19 | }, 20 | }, 21 | pluginJs.configs.all, 22 | ...tseslint.configs.all, 23 | stylistic.configs.all, 24 | stylistic.configs['disable-legacy'], 25 | stylistic.configs.customize({ 26 | indent: 2, 27 | semi: true, 28 | quotes: 'single', 29 | braceStyle: '1tbs', 30 | blockSpacing: true, 31 | commaDangle: 'always-multiline', 32 | }), 33 | { 34 | rules: { 35 | // #region Formatting preferences 36 | '@stylistic/function-call-argument-newline': ['error', 'consistent'], 37 | '@stylistic/member-delimiter-style': ['error', { singleline: { requireLast: true } }], 38 | '@stylistic/newline-per-chained-call': ['error', { ignoreChainWithDepth: 3 }], 39 | '@stylistic/padding-line-between-statements': [ 40 | 'error', 41 | { blankLine: 'always', prev: '*', next: ['return', 'function', 'class', 'interface', 'type', 'enum'] }, 42 | ], 43 | '@stylistic/quote-props': ['error', 'as-needed'], 44 | '@stylistic/quotes': ['error', 'single', { avoidEscape: true }], 45 | curly: ['error', 'multi-line'], 46 | 47 | '@stylistic/array-element-newline': 'off', 48 | '@stylistic/implicit-arrow-linebreak': 'off', 49 | '@stylistic/lines-around-comment': 'off', 50 | '@stylistic/multiline-comment-style': 'off', 51 | '@stylistic/object-property-newline': 'off', 52 | // #endregion 53 | 54 | // #region Preferences 55 | '@typescript-eslint/method-signature-style': ['error', 'method'], 56 | '@typescript-eslint/prefer-literal-enum-member': ['error', { allowBitwiseExpressions: true }], 57 | 'func-style': ['error', 'declaration'], 58 | 'max-lines-per-function': ['error', { max: 50, skipBlankLines: true, skipComments: true }], 59 | 60 | '@typescript-eslint/consistent-type-exports': 'off', 61 | '@typescript-eslint/consistent-type-imports': 'off', 62 | '@typescript-eslint/explicit-member-accessibility': 'off', 63 | '@typescript-eslint/init-declarations': 'off', 64 | '@typescript-eslint/max-params': 'off', 65 | '@typescript-eslint/naming-convention': 'off', 66 | '@typescript-eslint/no-invalid-void-type': 'off', 67 | '@typescript-eslint/no-magic-numbers': 'off', 68 | '@typescript-eslint/no-misused-promises': 'off', 69 | '@typescript-eslint/no-unsafe-type-assertion': 'off', 70 | '@typescript-eslint/no-use-before-define': 'off', 71 | '@typescript-eslint/parameter-properties': 'off', 72 | '@typescript-eslint/prefer-destructuring': 'off', 73 | '@typescript-eslint/prefer-enum-initializers': 'off', 74 | '@typescript-eslint/prefer-readonly-parameter-types': 'off', 75 | '@typescript-eslint/prefer-regexp-exec': 'off', 76 | 'capitalized-comments': 'off', 77 | 'guard-for-in': 'off', 78 | 'id-length': 'off', 79 | 'max-lines': 'off', 80 | 'max-statements': 'off', 81 | 'new-cap': 'off', 82 | 'no-bitwise': 'off', 83 | 'no-continue': 'off', 84 | 'no-inline-comments': 'off', 85 | 'no-negated-condition': 'off', 86 | 'no-param-reassign': 'off', 87 | 'no-plusplus': 'off', 88 | 'no-ternary': 'off', 89 | 'no-undefined': 'off', 90 | 'no-warning-comments': 'off', 91 | 'one-var': 'off', 92 | 'prefer-named-capture-group': 'off', 93 | radix: 'off', 94 | 'require-unicode-regexp': 'off', 95 | 'sort-imports': 'off', 96 | 'sort-keys': 'off', 97 | // #endregion 98 | 99 | // #region Project specific 100 | '@typescript-eslint/member-ordering': ['error', { default: ['method', 'field'] }], 101 | 102 | '@typescript-eslint/no-floating-promises': 'off', 103 | '@typescript-eslint/no-empty-function': 'off', 104 | 'no-console': 'off', 105 | 'no-alert': 'off', 106 | // #endregion 107 | }, 108 | }, 109 | ); 110 | -------------------------------------------------------------------------------- /frontend/HltbInjection.ts: -------------------------------------------------------------------------------- 1 | import { Millennium, sleep } from '@steambrew/client'; 2 | import { getHltbData } from './HtlbData'; 3 | 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 | declare const SteamUIStore: any; // TODO: will type later with steam-types 6 | 7 | function createInfoWithUrl(title: string, description: string, url: string): HTMLElement { 8 | const infoItem = document.createElement('div'); 9 | infoItem.classList.add('_2ZcNQxY8YknnhNa4ZvIoU4', 'hltb-info'); 10 | infoItem.innerHTML = ` 11 |
${title}
12 |
13 | ${description} 14 |
15 | `; 16 | 17 | return infoItem; 18 | } 19 | 20 | function toHrs(minutes: number | null): string { 21 | return minutes !== null ? `${(minutes / 60).toFixed(1)} hrs` : '--'; 22 | } 23 | 24 | let mainDocument: Document; 25 | 26 | async function findElement(selector: string): Promise { 27 | try { 28 | const elements = await Millennium.findElement(mainDocument, selector); 29 | const firstElement = elements[0] as T | undefined; 30 | 31 | return firstElement ?? null; 32 | } catch { 33 | return null; 34 | } 35 | } 36 | 37 | async function injectHltbData(appId: string): Promise { 38 | mainDocument.querySelectorAll('.hltb-info').forEach((element) => { 39 | element.remove(); 40 | }); 41 | const infoContainer = await findElement('._3cntzF30xAuwn1ARWJjvCb._1uS70KI6ZbUE94jUB27ioB'); 42 | if (!infoContainer) { 43 | console.error('[AugmentedSteam] Could not find info container.'); 44 | 45 | return; 46 | } 47 | 48 | const appData = await getHltbData(appId); 49 | 50 | if (appData === null) { 51 | return; 52 | } 53 | 54 | const numberFormatter = new Intl.NumberFormat(); 55 | 56 | function createBr(): HTMLElement { 57 | const infoBr = mainDocument.createElement('br'); 58 | infoBr.classList.add('hltb-info'); 59 | 60 | return infoBr; 61 | } 62 | 63 | if (appData.hltb !== undefined) { 64 | infoContainer.appendChild(createBr()); 65 | infoContainer.appendChild(createInfoWithUrl('Main Story:', toHrs(appData.hltb.story), appData.hltb.url)); 66 | infoContainer.appendChild(createInfoWithUrl('Main + Extras:', toHrs(appData.hltb.extras), appData.hltb.url)); 67 | infoContainer.appendChild(createInfoWithUrl('Completionist:', toHrs(appData.hltb.complete), appData.hltb.url)); 68 | } 69 | if (appData.players !== undefined) { 70 | const steamdbUrl = `https://steamdb.info/app/${appId}/charts/`; 71 | infoContainer.appendChild(createBr()); 72 | infoContainer.appendChild(createInfoWithUrl('Playing now:', numberFormatter.format(appData.players.recent), steamdbUrl)); 73 | infoContainer.appendChild(createInfoWithUrl('24-hour peak:', numberFormatter.format(appData.players.peak_today), steamdbUrl)); 74 | infoContainer.appendChild(createInfoWithUrl('All-time peak:', numberFormatter.format(appData.players.peak_all), steamdbUrl)); 75 | } 76 | } 77 | 78 | function attachListeners(appId: string, infoButton: HTMLElement): void { 79 | async function handleInteraction(): Promise { 80 | if (infoButton.getAttribute('hltb-injected') === 'true') { 81 | return; 82 | } 83 | 84 | infoButton.setAttribute('hltb-injected', 'true'); 85 | 86 | await injectHltbData(appId); 87 | } 88 | 89 | infoButton.addEventListener('click', handleInteraction); 90 | infoButton.addEventListener('mouseenter', handleInteraction); 91 | 92 | // Is info element open 93 | if (mainDocument.querySelector('._3s6_6sN8LyrlTHc_z6VfNU')) { 94 | handleInteraction(); 95 | } 96 | } 97 | 98 | async function addHltbData(): Promise { 99 | try { 100 | const appIconElement = await findElement('._3NBxSLAZLbbbnul8KfDFjw._2dzwXkCVAuZGFC-qKgo8XB'); 101 | const infoButton = (await findElement('._3VQUewWB8g6Z5qB4C7dGFr ._3qDWQGB0rtwM3qpXTb11Q-.Focusable .zvLq1GUCH3yLuqv_TXBJ1'))?.parentElement; 102 | 103 | if (!appIconElement || !infoButton) { 104 | setTimeout(addHltbData, 100); 105 | 106 | return; 107 | } 108 | 109 | if (infoButton.getAttribute('hltb-click-listener') === 'true') { 110 | setTimeout(addHltbData, 100); 111 | 112 | return; 113 | } 114 | 115 | const appId = appIconElement.src.match(/assets\/(\d+)/)?.[1]; 116 | if (appId === undefined) { 117 | console.error('[AugmentedSteam] Could not find app id'); 118 | setTimeout(addHltbData, 100); 119 | 120 | return; 121 | } 122 | 123 | infoButton.setAttribute('hltb-click-listener', 'true'); 124 | 125 | attachListeners(appId, infoButton); 126 | 127 | setTimeout(addHltbData, 100); 128 | } catch (error) { 129 | setTimeout(addHltbData, 500); 130 | throw error; 131 | } 132 | } 133 | 134 | export async function initHltbInjection(): Promise { 135 | // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition 136 | while (mainDocument === undefined) { 137 | // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access 138 | mainDocument = (SteamUIStore?.WindowStore?.SteamUIWindows?.[0]?.m_BrowserWindow?.document) as Document; 139 | 140 | // eslint-disable-next-line no-await-in-loop 141 | await sleep(500); 142 | } 143 | 144 | await addHltbData(); 145 | } 146 | -------------------------------------------------------------------------------- /frontend/HtlbData.ts: -------------------------------------------------------------------------------- 1 | const STORAGE_KEY = 'augmented-hltb-cache'; 2 | const CACHE_TIME = 60 * 60 * 1000; // 1 hour in milliseconds 3 | 4 | interface StoreData { 5 | players?: { 6 | recent: number; 7 | peak_today: number; 8 | peak_all: number; 9 | }; 10 | hltb?: { 11 | story: number | null; 12 | extras: number | null; 13 | complete: number | null; 14 | url: string; 15 | }; 16 | } 17 | 18 | interface StorageData { 19 | data: StoreData; 20 | expiry: number; 21 | } 22 | 23 | async function fetchStorePageData(appid: string): Promise { 24 | const response = await fetch(`https://api.augmentedsteam.com/app/${appid}/v2`); 25 | 26 | return response.json() as Promise; 27 | } 28 | 29 | export async function getHltbData(appid: string): Promise { 30 | const cachedData = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '{}') as Record; 31 | let appData = cachedData[appid]; 32 | 33 | if (!appData || appData.expiry < Date.now()) { 34 | const fetchedData = await fetchStorePageData(appid); 35 | appData = { 36 | data: fetchedData, 37 | expiry: Date.now() + CACHE_TIME, 38 | }; 39 | cachedData[appid] = appData; 40 | localStorage.setItem(STORAGE_KEY, JSON.stringify(cachedData)); 41 | } 42 | 43 | return appData.data; 44 | } 45 | -------------------------------------------------------------------------------- /frontend/index.tsx: -------------------------------------------------------------------------------- 1 | import { Millennium } from '@steambrew/client'; 2 | import { initHltbInjection } from './HltbInjection'; 3 | 4 | // #region Steamid 5 | let steamID = '-1'; 6 | 7 | async function getSteamId(): Promise { 8 | const loginUsers = await SteamClient.User.GetLoginUsers(); 9 | 10 | const match = loginUsers[0]?.avatarUrl.match(/avatarcache\/(\d+)/); 11 | if (!match) { 12 | throw new Error('Failed to match avatar URL'); 13 | } 14 | 15 | return match[1] ?? ''; 16 | } 17 | 18 | if (Millennium.exposeObj) { 19 | Millennium.exposeObj({ 20 | getSteamId: () => steamID, 21 | }); 22 | } 23 | // #endregion 24 | 25 | // Entry point on the front end of your plugin 26 | export default async function PluginMain(): Promise { 27 | steamID = await getSteamId(); 28 | 29 | initHltbInjection(); 30 | } 31 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "jsxFactory": "window.SP_REACT.createElement", 5 | "jsxFragmentFactory": "window.SP_REACT.Fragment", 6 | }, 7 | "include": ["."], 8 | } 9 | -------------------------------------------------------------------------------- /helpers/build_zip.py: -------------------------------------------------------------------------------- 1 | import os 2 | import subprocess 3 | import zipfile 4 | from pathlib import Path, PurePosixPath 5 | 6 | # Global configuration 7 | INCLUDED = { 8 | '.millennium/Dist', 'AugmentedSteam/dist', 'backend', 'LICENSE', 9 | 'README.md', 'plugin.json', 'requirements.txt', 'metadata.json', 'CHANGELOG.md' 10 | } 11 | 12 | def run_build(): 13 | print("Running bun build...") 14 | # Change to project root directory 15 | root_dir = Path(__file__).parent.parent 16 | os.chdir(root_dir) 17 | 18 | # Windows needs shell=True 19 | use_shell = os.name == 'nt' 20 | 21 | try: 22 | process = subprocess.Popen( 23 | ['bun', 'run', 'build'], 24 | shell=use_shell, 25 | stdout=subprocess.PIPE, 26 | stderr=subprocess.STDOUT, 27 | universal_newlines=True, 28 | cwd=str(root_dir), 29 | ) 30 | 31 | for line in process.stdout: 32 | print(line, end='') 33 | 34 | process.wait() 35 | 36 | if process.returncode != 0: 37 | print("Build failed") 38 | return False 39 | 40 | print("Build completed successfully") 41 | return True 42 | except Exception as e: 43 | print(f"Error running build: {e}") 44 | return False 45 | 46 | def should_include_file(file_path: Path, root_dir: Path) -> bool: 47 | rel_path = PurePosixPath(file_path.relative_to(root_dir)) # Normalize path for consistent matching 48 | for pattern in INCLUDED: 49 | if rel_path.as_posix().startswith(pattern): # Match patterns as prefixes for directories/files 50 | return True 51 | return False 52 | 53 | def create_zip(): 54 | # Get version from environment variable 55 | version = os.environ.get('RELEASE_VERSION', '') 56 | 57 | if not version: 58 | print("Error: RELEASE_VERSION environment variable is required") 59 | return False 60 | 61 | zip_name = f"Augmented-Steam-plugin-{version}.zip" 62 | 63 | # Root and output directories 64 | root_dir = Path(__file__).parent.parent 65 | build_dir = root_dir / 'build' 66 | build_dir.mkdir(parents=True, exist_ok=True) 67 | zip_path = build_dir / zip_name 68 | 69 | print(f"\nCreating zip file: {zip_path}") 70 | 71 | with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf: 72 | for included_path in INCLUDED: 73 | folder_path = root_dir / included_path 74 | 75 | # Check if the included path exists 76 | if not folder_path.exists(): 77 | print(f"Warning: {folder_path} does not exist. Skipping.") 78 | continue 79 | 80 | # If it's a directory, include all its contents 81 | if folder_path.is_dir(): 82 | for root, _, files in os.walk(folder_path): 83 | for file in files: 84 | file_path = Path(root) / file 85 | rel_path = file_path.relative_to(root_dir) 86 | zip_path_with_root = Path('AugmentedSteam-plugin') / rel_path 87 | print(f"Adding: {zip_path_with_root}") 88 | zipf.write(file_path, str(zip_path_with_root)) 89 | # If it's a file, directly add it 90 | elif folder_path.is_file(): 91 | rel_path = folder_path.relative_to(root_dir) 92 | zip_path_with_root = Path('AugmentedSteam-plugin') / rel_path 93 | print(f"Adding file: {zip_path_with_root}") 94 | zipf.write(folder_path, str(zip_path_with_root)) 95 | 96 | print("\nZip file created successfully!") 97 | return True 98 | 99 | def main(): 100 | if not run_build(): 101 | exit(1) 102 | 103 | if not create_zip(): 104 | exit(1) 105 | print("\nBuild and zip creation completed!") 106 | 107 | if __name__ == "__main__": 108 | main() 109 | -------------------------------------------------------------------------------- /helpers/clean-maps.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'fs/promises'; 2 | import path from 'path'; 3 | import { fileURLToPath } from 'url'; 4 | 5 | // Get the directory name of the current module 6 | const __filename = fileURLToPath(import.meta.url); 7 | const __dirname = path.dirname(__filename); 8 | 9 | const distFolder = path.join(__dirname, '../AugmentedSteam/dist'); // Adjust the path as needed 10 | 11 | async function deleteMapFiles(folder) { 12 | try { 13 | const files = await fs.readdir(folder); 14 | 15 | for (const file of files) { 16 | const filePath = path.join(folder, file); 17 | const stat = await fs.stat(filePath); 18 | 19 | if (stat.isDirectory()) { 20 | if (file === 'img' || file === 'localization') { 21 | await fs.rmdir(filePath, { recursive: true }); 22 | console.log(`Deleted folder: ${filePath}`); 23 | } else { 24 | // Recursively call deleteMapFiles for directories 25 | await deleteMapFiles(filePath); 26 | } 27 | } else if (file.endsWith('.map')) { 28 | await fs.unlink(filePath); 29 | console.log(`Deleted: ${filePath}`); 30 | } 31 | } 32 | } catch (err) { 33 | console.error(`Error processing folder ${folder}: ${err}`); 34 | } 35 | } 36 | 37 | // Run the function 38 | deleteMapFiles(distFolder); 39 | -------------------------------------------------------------------------------- /helpers/convert-manifest.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-loop-func */ 2 | import clipboard from 'clipboardy'; 3 | import escapeStringRegexp from 'escape-string-regexp'; 4 | import * as fs from 'fs'; 5 | 6 | interface ContentScript { 7 | matches: string[]; 8 | exclude_matches?: string[]; 9 | js?: string[]; 10 | css?: string[]; 11 | } 12 | 13 | interface ManifestData { 14 | content_scripts: ContentScript[]; 15 | } 16 | 17 | function urlToRegex(url: string): string { 18 | return `^${escapeStringRegexp(url).replace(/\\\*/g, '.*').replace(/\//g, '\\/')}$`; 19 | } 20 | 21 | function convertToJs(): void { 22 | fs.readFile('AugmentedSteam/dist/prod.chrome/manifest.json', 'utf8', (err, data) => { 23 | if (err) { 24 | console.error('Error reading the manifest file:', err); 25 | 26 | return; 27 | } 28 | 29 | const manifestData = JSON.parse(data) as ManifestData; 30 | const contentScripts = manifestData.content_scripts; 31 | const combinedMatches: Record = {}; 32 | 33 | let output = ''; 34 | 35 | for (const script of contentScripts) { 36 | const matches = script.matches; 37 | const excludes = script.exclude_matches; 38 | const jsFiles = script.js ?? []; 39 | const cssFiles = script.css ?? []; 40 | 41 | let combinedMatchesStr = matches.map(m => `href.match(/${urlToRegex(m)}/)`).join(' || '); 42 | combinedMatchesStr = `(${combinedMatchesStr})`; 43 | // Add the exclude_matches to the combinedMatchesStr 44 | if (excludes) { 45 | combinedMatchesStr += ` && !(${excludes.map(m => `href.match(/${urlToRegex(m)}/)`).join(' || ')})`; 46 | } 47 | if (!combinedMatches[combinedMatchesStr]) { 48 | combinedMatches[combinedMatchesStr] = { js: [], css: [] }; 49 | } 50 | combinedMatches[combinedMatchesStr]?.js.push(...jsFiles); 51 | combinedMatches[combinedMatchesStr]?.css.push(...cssFiles); 52 | } 53 | 54 | for (const [match, { js, css }] of Object.entries(combinedMatches)) { 55 | const uniqueJsFiles = Array.from(new Set(js)); 56 | const uniqueCssFiles = Array.from(new Set(css)); 57 | 58 | if (uniqueJsFiles.length === 0 && uniqueCssFiles.length === 0) { 59 | continue; 60 | } 61 | 62 | output += `if (${match}) {\n`; 63 | uniqueJsFiles.forEach((jsFile) => { 64 | output += ` scripts.push('${jsFile}');\n`; 65 | }); 66 | uniqueCssFiles.forEach((cssFile) => { 67 | output += ` scripts.push('${cssFile}');\n`; 68 | }); 69 | output += '}\n\n'; 70 | } 71 | 72 | output = output.trimEnd(); 73 | 74 | // Copy text to clipboard 75 | clipboard.writeSync(output); 76 | 77 | console.log('Copied to clipboard.'); 78 | }); 79 | } 80 | 81 | convertToJs(); 82 | -------------------------------------------------------------------------------- /helpers/generate-metadata.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | commit_id=$(git rev-parse HEAD) 4 | plugin_id=$(git rev-list --max-parents=0 HEAD) 5 | 6 | echo "{\"id\": \"$plugin_id\", \"commitId\": \"$commit_id\"}" > metadata.json -------------------------------------------------------------------------------- /helpers/publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export RELEASE_VERSION="$1" 3 | 4 | helpers/generate-metadata.sh 5 | python helpers/build_zip.py -------------------------------------------------------------------------------- /helpers/update-version.ts: -------------------------------------------------------------------------------- 1 | import { readFileSync, writeFileSync } from 'fs'; 2 | import { join } from 'path'; 3 | 4 | interface PackageJson { 5 | version: string; 6 | 7 | [key: string]: unknown; 8 | } 9 | 10 | interface PluginJson { 11 | version: string; 12 | 13 | [key: string]: unknown; 14 | } 15 | 16 | const version = process.env.RELEASE_VERSION; 17 | if (version === undefined) { 18 | console.error('RELEASE_VERSION environment variable is not set'); 19 | process.exit(1); 20 | } 21 | 22 | // Update package.json 23 | const packagePath = join(process.cwd(), 'package.json'); 24 | const packageJson = JSON.parse(readFileSync(packagePath, 'utf8')) as PackageJson; 25 | packageJson.version = version; 26 | writeFileSync(packagePath, `${JSON.stringify(packageJson, null, 2)}\n`); 27 | 28 | // Update plugin.json 29 | const pluginPath = join(process.cwd(), 'plugin.json'); 30 | const pluginJson = JSON.parse(readFileSync(pluginPath, 'utf8')) as PluginJson; 31 | pluginJson.version = version; 32 | writeFileSync(pluginPath, `${JSON.stringify(pluginJson, null, 2)}\n`); 33 | 34 | console.log(`Updated version to ${version} in package.json and plugin.json`); 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "millennium-ttc --build dev", 4 | "watch": "nodemon --watch webkit --watch frontend --ext ts,tsx,js,jsx --exec \"millennium-ttc --build dev\"", 5 | "update-augmented-steam": "git submodule update --remote AugmentedSteam && echo Submodules updated. Remember to update the sha in webkit\\shared.ts", 6 | "build-augmented-steam-dev": "cd ./AugmentedSteam && npm install --include=dev && npm run build -- chrome", 7 | "build-augmented-steam": "cd ./AugmentedSteam && npm install --include=dev && npm run build -- chrome --production", 8 | "build": "npm run build-augmented-steam && node ./helpers/clean-maps.mjs && millennium-ttc --build prod" 9 | }, 10 | "devDependencies": { 11 | "@eslint/js": "^9.24.0", 12 | "@semantic-release/changelog": "^6.0.3", 13 | "@semantic-release/exec": "^7.0.3", 14 | "@semantic-release/git": "^10.0.1", 15 | "@semantic-release/github": "^11.0.1", 16 | "@stylistic/eslint-plugin": "^4.2.0", 17 | "@types/node": "^22.14.1", 18 | "clipboardy": "^4.0.0", 19 | "escape-string-regexp": "^5.0.0", 20 | "eslint": "^9.24.0", 21 | "globals": "^16.0.0", 22 | "nodemon": "^3.1.9", 23 | "semantic-release": "^24.2.3", 24 | "steam-types": "^0.1.2", 25 | "typescript-eslint": "^8.29.1" 26 | }, 27 | "type": "module", 28 | "name": "augmented-steam-plugin", 29 | "version": "1.1.8", 30 | "description": "Augmented Steam plugin for Millennium", 31 | "main": "./frontend/index.jsx", 32 | "author": "BossSloth", 33 | "license": "MIT", 34 | "dependencies": { 35 | "@steambrew/api": "^4.2.2", 36 | "@steambrew/client": "^4.2.1", 37 | "@steambrew/ttc": "^1.2.2", 38 | "@steambrew/webkit": "^4.2.1" 39 | }, 40 | "patchedDependencies": { 41 | "@steambrew/client@4.2.1": "patches/@steambrew%2Fclient@4.2.1.patch" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /patches/@steambrew%2Fclient@4.2.1.patch: -------------------------------------------------------------------------------- 1 | diff --git a/build/globals/SteamClient.d.ts b/build/globals/SteamClient.d.ts 2 | index 2357077560c52548b36c727d0fb759faf5be2d44..9a1fb61cb25d8fe7c1ca7ecba23b9f114962c501 100644 3 | --- a/build/globals/SteamClient.d.ts 4 | +++ b/build/globals/SteamClient.d.ts 5 | @@ -1,6 +1,3 @@ 6 | -declare global { 7 | - var SteamClient: SteamClient; 8 | -} 9 | export interface Apps { 10 | RegisterForAppOverviewChanges: any; 11 | RegisterForAppDetails: any; 12 | diff --git a/src/globals/SteamClient.ts b/src/globals/SteamClient.ts 13 | index 0c65707782a81f5975b25f52eec5655f8abebe28..24ed6075372188c745c505523e1e5b938e216aef 100644 14 | --- a/src/globals/SteamClient.ts 15 | +++ b/src/globals/SteamClient.ts 16 | @@ -1,7 +1,3 @@ 17 | -declare global { 18 | - var SteamClient: SteamClient; 19 | -} 20 | - 21 | export interface Apps { 22 | RegisterForAppOverviewChanges: any; 23 | RegisterForAppDetails: any; 24 | -------------------------------------------------------------------------------- /plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/SteamClientHomebrew/Millennium/main/src/sys/plugin-schema.json", 3 | "name": "augmented-steam", 4 | "common_name": "Augmented Steam extension", 5 | "description": "Augmented Steam browser extension for your Steam client", 6 | "version": "1.1.8", 7 | "include": [ 8 | "AugmentedSteam/dist" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /release.config.mjs: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-template-curly-in-string */ 2 | /** 3 | * @type {import('semantic-release').Options} 4 | */ 5 | export default { 6 | tagFormat: 'v${version}', 7 | branches: ['main'], 8 | plugins: [ 9 | '@semantic-release/commit-analyzer', 10 | '@semantic-release/release-notes-generator', 11 | '@semantic-release/changelog', 12 | [ 13 | '@semantic-release/exec', 14 | { 15 | prepareCmd: 'RELEASE_VERSION=${nextRelease.version} bun run helpers/update-version.ts', 16 | }, 17 | ], 18 | [ 19 | '@semantic-release/git', 20 | { 21 | assets: ['package.json', 'plugin.json', 'CHANGELOG.md'], 22 | message: 'chore: bump version to ${nextRelease.version}\n\n${nextRelease.notes}', 23 | }, 24 | ], 25 | [ 26 | '@semantic-release/exec', 27 | { 28 | publishCmd: './helpers/publish.sh ${nextRelease.version}', 29 | }, 30 | ], 31 | [ 32 | '@semantic-release/github', 33 | { 34 | assets: ['build/*.zip'], 35 | }, 36 | ], 37 | ], 38 | }; 39 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": ".millennium/Dist", 4 | "module": "ESNext", 5 | "target": "ES2021", 6 | "jsx": "react", 7 | "declaration": false, 8 | "moduleResolution": "node", 9 | "noUnusedLocals": true, 10 | "noUnusedParameters": true, 11 | "esModuleInterop": true, 12 | "noImplicitReturns": true, 13 | "noImplicitThis": true, 14 | "noImplicitAny": true, 15 | "allowSyntheticDefaultImports": true, 16 | "skipLibCheck": true, 17 | "resolveJsonModule": true, 18 | "strictNullChecks": true, 19 | "noUncheckedIndexedAccess": true, 20 | "types": [ 21 | "steam-types" 22 | ] 23 | }, 24 | "exclude": ["node_modules"] 25 | } 26 | -------------------------------------------------------------------------------- /webkit/browser-types.ts: -------------------------------------------------------------------------------- 1 | export type MessageCallback = (message: unknown, sender: unknown, sendResponse: (response?: unknown) => void) => void | boolean | Promise; 2 | 3 | declare global { 4 | interface Window { 5 | augmentedBrowser: AugmentedBrowser; 6 | } 7 | } 8 | 9 | type storageGet = (items?: string | string[] | Record, callback?: (result: Record) => void) => Promise>; 10 | 11 | type storageSet = (item: Record, callback?: () => void) => Promise; 12 | 13 | type storageRemove = (key: string | string[], callback?: () => void) => Promise; 14 | 15 | export interface AugmentedBrowser { 16 | runtime: { 17 | getManifest(): { version: string; }; 18 | getURL(resource: string): string; 19 | sendMessage(message: unknown, callback?: (response: unknown) => void): void; 20 | 21 | id: string; 22 | onInstalled: { 23 | addListener(callback: () => void): void; 24 | }; 25 | onStartup: { 26 | addListener(callback: () => void): void; 27 | }; 28 | onMessage: { 29 | addListener(callback: MessageCallback): void; 30 | }; 31 | }; 32 | contextMenus: { 33 | onClicked: { 34 | addListener(): void; 35 | hasListener(): boolean; 36 | }; 37 | }; 38 | storage: { 39 | sync: { 40 | get: storageGet; 41 | set: storageSet; 42 | remove: storageRemove; 43 | }; 44 | local: { 45 | get: storageGet; 46 | set: storageSet; 47 | remove: storageRemove; 48 | }; 49 | }; 50 | clients: { 51 | matchAll(): { url: string; }[]; 52 | }; 53 | offscreen: { 54 | closeDocument(): void; 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /webkit/browser.ts: -------------------------------------------------------------------------------- 1 | import { callable } from '@steambrew/webkit'; 2 | import { AugmentedBrowser } from './browser-types'; 3 | import { getCdn, getLoopbackCdn, Logger, sleep, VERSION } from './shared'; 4 | 5 | // In this file we emulate the extension browser api for the Augmented Steam extension 6 | const augmentedBrowser: Partial = {}; 7 | 8 | // #region Defaults 9 | 10 | augmentedBrowser.runtime = { 11 | getManifest: (): { version: string; } => ({ version: VERSION }), 12 | id: 'kdbmhfkmnlmbkgbabkdealhhbfhlmmon', // Chrome 13 | onInstalled: { 14 | addListener: (): void => {}, 15 | }, 16 | onStartup: { 17 | addListener: (): void => {}, 18 | }, 19 | getURL: (resource: string): string => { 20 | // Disable early access badge on achievements page because of incompatibility with SteamDB plugin 21 | if (location.href.includes('/stats') && resource.includes('early_access')) { 22 | const gameLogo = document.querySelector('.gameLogo'); 23 | if (gameLogo) { 24 | gameLogo.classList.add('es_ea_checked'); 25 | } 26 | } 27 | 28 | if (resource.endsWith('.png') || resource.endsWith('.svg') || resource.endsWith('.gif') || resource.startsWith('/localization/')) { 29 | return getLoopbackCdn(resource); 30 | } 31 | 32 | return getCdn(resource); 33 | }, 34 | onMessage: { 35 | addListener, 36 | }, 37 | sendMessage, 38 | }; 39 | 40 | augmentedBrowser.contextMenus = { 41 | onClicked: { 42 | addListener: (): void => {}, 43 | hasListener: (): false => false, 44 | }, 45 | }; 46 | 47 | // #endregion 48 | 49 | // #region Storage 50 | 51 | const SYNC_STORAGE_KEY = 'augmented-options-sync'; 52 | const LOCAL_STORAGE_KEY = 'augmented-options-local'; 53 | 54 | async function storageGet(storageKey: string, items?: string | string[] | Record, callback?: (result: Record) => void): Promise> { 55 | const storedData = localStorage.getItem(storageKey); 56 | const result: Record = {}; 57 | let parsedData: Record; 58 | 59 | try { 60 | parsedData = storedData !== null ? JSON.parse(storedData) as Record : {}; 61 | } catch { 62 | throw new Error(`failed to parse JSON for ${storageKey}`); 63 | } 64 | 65 | if (typeof items === 'string') { 66 | items = [items]; 67 | } 68 | 69 | if (Array.isArray(items)) { 70 | items.forEach((key) => { 71 | if (key in parsedData) { 72 | result[key] = parsedData[key]; 73 | } 74 | }); 75 | } else if (typeof items === 'object') { 76 | for (const key in items) { 77 | result[key] = key in parsedData ? parsedData[key] : items[key]; 78 | } 79 | } 80 | 81 | if (callback) { 82 | callback(result); 83 | } 84 | 85 | return Promise.resolve(result); 86 | } 87 | 88 | async function storageSet(storageKey: string, item: Record, callback?: () => void): Promise { 89 | const storedData = localStorage.getItem(storageKey); 90 | let parsedData: Record; 91 | 92 | try { 93 | parsedData = storedData !== null ? JSON.parse(storedData) as Record : {}; 94 | } catch { 95 | throw new Error(`failed to parse JSON for ${storageKey}`); 96 | } 97 | 98 | Object.assign(parsedData, item); 99 | localStorage.setItem(storageKey, JSON.stringify(parsedData)); 100 | 101 | if (callback) { 102 | callback(); 103 | } 104 | 105 | return Promise.resolve(); 106 | } 107 | 108 | async function storageRemove(storageKey: string, key: string | string[], callback?: () => void): Promise { 109 | const storedData = localStorage.getItem(storageKey); 110 | let parsedData: Record = {}; 111 | 112 | try { 113 | parsedData = storedData !== null ? JSON.parse(storedData) as Record : {}; 114 | } catch { 115 | throw new Error(`failed to parse JSON for ${storageKey}`); 116 | } 117 | 118 | if (!Array.isArray(key)) { 119 | key = [key]; 120 | } 121 | 122 | key.forEach((k) => { 123 | // eslint-disable-next-line @typescript-eslint/no-dynamic-delete 124 | delete parsedData[k]; 125 | }); 126 | 127 | localStorage.setItem(storageKey, JSON.stringify(parsedData)); 128 | 129 | if (callback) { 130 | callback(); 131 | } 132 | 133 | return Promise.resolve(); 134 | } 135 | 136 | augmentedBrowser.storage = { 137 | sync: { 138 | get: async (items?: Record, callback?: (result: Record) => void): Promise> => storageGet(SYNC_STORAGE_KEY, items, callback), 139 | set: async (item: Record, callback?: () => void): Promise => storageSet(SYNC_STORAGE_KEY, item, callback), 140 | remove: async (key: string | string[], callback?: () => void): Promise => storageRemove(SYNC_STORAGE_KEY, key, callback), 141 | }, 142 | local: { 143 | get: async (items?: Record, callback?: (result: Record) => void): Promise> => storageGet(LOCAL_STORAGE_KEY, items, callback), 144 | set: async (item: Record, callback?: () => void): Promise => storageSet(LOCAL_STORAGE_KEY, item, callback), 145 | remove: async (key: string | string[], callback?: () => void): Promise => storageRemove(LOCAL_STORAGE_KEY, key, callback), 146 | }, 147 | }; 148 | 149 | // #endregion 150 | 151 | // #region fake fetch 152 | const oldFetch = window.fetch; 153 | 154 | const interceptedUrls: RegExp[] = [ 155 | /steamcommunity\.com\/id/, 156 | /steamcommunity\.com\/profiles\/\d+\/$/, 157 | /steamcommunity\.com\/profiles\/\d+\/ajaxgetbadgeinfo/, 158 | /steamcommunity\.com\/inventory\//, 159 | /store\.steampowered\.com\/steamaccount\/addfunds/, 160 | ]; 161 | 162 | const backendFetch = callable<[{ url: string; }], string>('BackendFetch'); 163 | 164 | interface BackendResponse { 165 | status: number; 166 | url: string; 167 | headers: Record; 168 | body: string; 169 | } 170 | 171 | export const retrieveUrlQuery = 'retrieveUrl'; 172 | 173 | const getRetrieveUrlResponse = callable('GetRetrieveUrlResponse'); 174 | let requestLocked = false; 175 | 176 | async function waitLock(): Promise { 177 | const startTime = Date.now(); 178 | // eslint-disable-next-line no-unmodified-loop-condition 179 | while (requestLocked && Date.now() - startTime < 15000) { 180 | // eslint-disable-next-line no-await-in-loop 181 | await sleep(250); 182 | } 183 | 184 | return !requestLocked; 185 | } 186 | 187 | async function windowFetch(url: string): Promise { 188 | const lockFreed = await waitLock(); 189 | if (!lockFreed) { 190 | Logger.warn('Failed to retrieve url. Request already in progress.', url); 191 | 192 | return null; 193 | } 194 | 195 | requestLocked = true; 196 | const communityWindow = window.open(`https://steamcommunity.com/?${retrieveUrlQuery}=${url}`, undefined, 'width=0,height=1000000,left=0,top=0'); // We set height really high so for some reason the width becomes 0 197 | if (!communityWindow) { 198 | Logger.error('Failed to open new window for window request.', url); 199 | 200 | return null; 201 | } 202 | 203 | let urlResponse = null; 204 | const startTime = Date.now(); 205 | while (urlResponse === null && Date.now() - startTime < 10000) { 206 | // eslint-disable-next-line no-await-in-loop 207 | urlResponse = await getRetrieveUrlResponse(); 208 | if (urlResponse === 0) { 209 | urlResponse = null; 210 | } 211 | 212 | // eslint-disable-next-line no-await-in-loop 213 | await sleep(250); 214 | } 215 | 216 | if (urlResponse === null) { 217 | Logger.error('Failed to retrieve url. Request timed out after 10 seconds.', url); 218 | 219 | return null; 220 | } 221 | 222 | return urlResponse as string; 223 | } 224 | 225 | async function handleInterceptedFetch(url: string, params?: RequestInit): Promise { 226 | Logger.debug(`intercepting ${url}`); 227 | if (params) { 228 | const headers = Object.keys(params).filter(h => h.toLowerCase() !== 'credentials'); 229 | // We only support the credentials header for now, so if we see any other log a warning 230 | if (headers.length > 0) { 231 | Logger.warn('fetch params not fully supported', params); 232 | } 233 | } 234 | 235 | const response = JSON.parse(await backendFetch({ url: url.toString() })) as BackendResponse; 236 | 237 | let responseObject = new Response(response.body, { status: response.status, headers: response.headers }); 238 | if (response.status === 403 && params?.credentials === 'include') { 239 | // If we need credentials to access this page, and we can't do it via the backend we do it via the browser 240 | let windowResponse = null; 241 | try { 242 | windowResponse = await windowFetch(url.toString()); 243 | } catch (e) { 244 | Logger.error('Failed to retrieve URL', url.toString(), e); 245 | } 246 | requestLocked = false; 247 | if (windowResponse === null) { 248 | return new Response(null, { status: 500 }); 249 | } 250 | 251 | responseObject = new Response(windowResponse, { status: 200 }); 252 | } 253 | 254 | Object.defineProperty(responseObject, 'url', { value: response.url }); 255 | 256 | return responseObject; 257 | } 258 | 259 | window.fetch = async (url: string | URL, params?: RequestInit): Promise => { 260 | for (const intercept of interceptedUrls) { 261 | if (url.toString().match(intercept)) { 262 | return handleInterceptedFetch(url.toString(), params); 263 | } 264 | } 265 | 266 | return oldFetch(url, params); 267 | }; 268 | // #endregion 269 | 270 | // #region Background messaging 271 | augmentedBrowser.clients = { 272 | matchAll: (): { url: string; }[] => [{ url: 'html/offscreen_domparser.html' }], 273 | }; 274 | augmentedBrowser.offscreen = { 275 | closeDocument: (): void => {}, 276 | }; 277 | 278 | type MessageCallback = (message: unknown, sender: object, sendResponse: (response: unknown) => void) => void; 279 | 280 | const messageListeners: MessageCallback[] = []; 281 | 282 | function addListener(callback: MessageCallback): void { 283 | messageListeners.push(callback); 284 | } 285 | 286 | function sendMessage(message: unknown, callback?: (response: unknown) => void): void { 287 | Logger.debug('Sending message', message); 288 | messageListeners.forEach((listener) => { 289 | listener( 290 | message, 291 | { tab: {} }, 292 | (response: unknown) => { 293 | if (callback) { 294 | callback(response); 295 | } 296 | }, 297 | ); 298 | }); 299 | } 300 | 301 | // #endregion 302 | 303 | // #region open newly created tags in own way 304 | // eslint-disable-next-line @typescript-eslint/no-deprecated 305 | const oldCreateElement = document.createElement.bind(document) as typeof document.createElement; 306 | 307 | // TODO: maybe make this an option 308 | const externalLinks = [ 309 | 'twitch.tv', 310 | 'youtube.com', 311 | 'google.com', 312 | 'astats.nl', 313 | 'steamrep.com', 314 | 'steamgifts.com', 315 | 'steamtrades.com', 316 | 'barter.vg', 317 | 'achievementstats.com', 318 | 'backpack.tf', 319 | 'steamcardexchange.net', 320 | 'augmentedsteam.com', 321 | ]; 322 | const popupLinks = [ 323 | 'steamdb.info', 324 | 'isthereanydeal.com', 325 | 'howlongtobeat.com', 326 | 'pcgamingwiki.com', 327 | ]; 328 | const EXTERNAL_PROTOCOL = 'steam://openurl_external/'; 329 | 330 | function modifyHrefForExternalLinks(tag: HTMLAnchorElement): void { 331 | externalLinks.forEach((link) => { 332 | if (tag.href.includes(link)) { 333 | tag.href = EXTERNAL_PROTOCOL + tag.href; 334 | } 335 | }); 336 | } 337 | 338 | function addPopupClickListener(tag: HTMLAnchorElement): void { 339 | popupLinks.forEach((link) => { 340 | if (tag.href.includes(link)) { 341 | tag.onclick = (event): void => { 342 | if (event.ctrlKey) { 343 | return; 344 | } 345 | 346 | event.preventDefault(); 347 | 348 | const ctrlClickEvent = new MouseEvent('click', { 349 | bubbles: true, 350 | cancelable: true, 351 | view: window, 352 | ctrlKey: true, 353 | }); 354 | 355 | tag.dispatchEvent(ctrlClickEvent); 356 | }; 357 | } 358 | }); 359 | } 360 | 361 | function observeAnchorTag(tag: HTMLAnchorElement): void { 362 | const observer = new MutationObserver((mutations) => { 363 | mutations.forEach((mutation) => { 364 | if (mutation.type === 'attributes' && mutation.attributeName === 'href') { 365 | modifyHrefForExternalLinks(tag); 366 | addPopupClickListener(tag); 367 | observer.disconnect(); 368 | } 369 | }); 370 | }); 371 | 372 | observer.observe(tag, { attributes: true }); 373 | } 374 | 375 | // eslint-disable-next-line @typescript-eslint/no-deprecated 376 | document.createElement = (tagName: string, options?: ElementCreationOptions): HTMLElement => { 377 | const tag: HTMLElement = oldCreateElement(tagName, options); 378 | 379 | if (tagName.toLowerCase() === 'a') { 380 | observeAnchorTag(tag as HTMLAnchorElement); 381 | } 382 | 383 | return tag; 384 | }; 385 | // #endregion 386 | 387 | window.augmentedBrowser = augmentedBrowser as AugmentedBrowser; 388 | -------------------------------------------------------------------------------- /webkit/header.ts: -------------------------------------------------------------------------------- 1 | import { callable } from '@steambrew/webkit'; 2 | import { Logger, sleep } from './shared'; 3 | 4 | function getIdFromAppConfig(): string | null { 5 | const appConfig = document.querySelector('#application_config'); 6 | 7 | if (!appConfig) { 8 | Logger.warn('appConfig not found'); 9 | 10 | return null; 11 | } 12 | 13 | const userInfo = appConfig.getAttribute('data-userinfo'); 14 | 15 | if (userInfo === null) { 16 | Logger.warn('data-userinfo not found on application_config'); 17 | 18 | return null; 19 | } 20 | 21 | const info = JSON.parse(userInfo) as { steamid: string; }; 22 | 23 | return info.steamid; 24 | } 25 | 26 | function getIdFromScript(context: Node): string | null { 27 | const script = document.evaluate('//script[contains(text(), \'g_steamID\')]', context, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; 28 | 29 | if (!script) { 30 | Logger.warn('script steamid not found'); 31 | 32 | return null; 33 | } 34 | 35 | return script.textContent?.match(/g_steamID.+?(\d+)/)?.[1] ?? null; 36 | } 37 | 38 | async function getIdFromBackend(): Promise { 39 | const backend = callable<[], string>('GetSteamId'); 40 | 41 | return backend(); 42 | } 43 | 44 | export async function createFakeSteamHeader(): Promise { 45 | const steamid = getIdFromAppConfig() ?? getIdFromScript(document) ?? await getIdFromBackend(); 46 | if (steamid === null) { 47 | throw new Error('Could not get steamid, augmented steam will not work.'); 48 | } 49 | 50 | const isReactPage = document.querySelector('[data-react-nav-root]') !== null; 51 | 52 | if (isReactPage) { 53 | // Wait on react to load 54 | const start = performance.now(); 55 | while (performance.now() - start < 5000) { 56 | // @ts-expect-error any, global react object 57 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, no-underscore-dangle, @typescript-eslint/no-unsafe-member-access 58 | const root = SSR?.reactRoot?._internalRoot; 59 | if (root !== undefined) { 60 | break; 61 | } 62 | 63 | // eslint-disable-next-line no-await-in-loop 64 | await sleep(100); 65 | } 66 | 67 | if (performance.now() - start > 5000) { 68 | throw new Error('Timed out waiting for react root'); 69 | } 70 | 71 | const node = document.createElement('header'); 72 | node.innerHTML = reactFakeHeader.replaceAll('%user_id%', steamid); 73 | const pageContent = document.querySelector('#StoreTemplate'); 74 | pageContent?.prepend(node); 75 | } else { 76 | const node = document.createElement('div'); 77 | node.innerHTML = legacyFakeHeader.replaceAll('%user_id%', steamid); 78 | const pageContent = document.querySelector('.responsive_page_content') ?? document.querySelector('.headerOverride'); 79 | pageContent?.prepend(node); 80 | } 81 | } 82 | 83 | const legacyFakeHeader = ''; 84 | const reactFakeHeader = ''; 85 | -------------------------------------------------------------------------------- /webkit/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-duplicate-imports */ 2 | import { callable, Millennium } from '@steambrew/webkit'; 3 | import './browser'; 4 | import { retrieveUrlQuery } from './browser'; 5 | import { createFakeSteamHeader } from './header'; 6 | import { injectPreferences } from './preferences'; 7 | import { getNeededScripts } from './script-loading'; 8 | import { DEV, getCdn, initCdn, Logger, LOOPBACK_CDN, sleep } from './shared'; 9 | 10 | async function loadScript(src: string): Promise { 11 | let content = await fetch(src).then(async response => response.text()); 12 | content = content 13 | .replaceAll('chrome', 'augmentedBrowser') 14 | .replaceAll('clients', 'augmentedBrowser.clients'); 15 | content += `\n//# sourceURL=${src}`; 16 | 17 | return new Promise((resolve, reject) => { 18 | const script = document.createElement('script'); 19 | // script.src = src; 20 | script.type = 'text/javascript'; 21 | script.async = true; 22 | script.innerHTML = content; 23 | script.setAttribute('original-src', src); 24 | 25 | script.onload = (): void => { 26 | resolve(); 27 | }; 28 | 29 | script.onerror = (): void => { 30 | reject(new Error('Failed to load script')); 31 | }; 32 | 33 | document.head.appendChild(script); 34 | resolve(); 35 | }); 36 | } 37 | 38 | async function loadStyle(src: string): Promise { 39 | let content = await fetch(src).then(async response => response.text()); 40 | content = content.replaceAll('chrome-extension://__MSG_@@extension_id__', LOOPBACK_CDN); 41 | 42 | return new Promise((resolve, reject) => { 43 | const style = document.createElement('style'); 44 | style.setAttribute('original-src', src); 45 | style.innerHTML = content; 46 | 47 | style.onload = (): void => { 48 | resolve(); 49 | }; 50 | 51 | style.onerror = (): void => { 52 | reject(new Error('Failed to load style')); 53 | }; 54 | 55 | document.head.appendChild(style); 56 | }); 57 | } 58 | 59 | async function loadJsScripts(scripts: string[]): Promise { 60 | const promises = []; 61 | const filteredScripts = scripts.filter(script => script.includes('.js')); 62 | 63 | for (const script of filteredScripts) { 64 | promises.push(loadScript(getCdn(script))); 65 | } 66 | 67 | return Promise.all(promises); 68 | } 69 | 70 | async function loadCssScripts(scripts: string[]): Promise { 71 | const promises = []; 72 | const filteredStyles = scripts.filter(script => script.includes('.css')); 73 | 74 | for (const style of filteredStyles) { 75 | promises.push(loadStyle(getCdn(style))); 76 | } 77 | 78 | return Promise.all(promises); 79 | } 80 | 81 | const startTime = performance.now(); 82 | 83 | async function testPerformance(): Promise { 84 | await Millennium.findElement(document, '.es_highlighted_owned'); 85 | 86 | Logger.log(`Took Augmented Steam ${performance.now() - startTime}ms to load`); 87 | 88 | // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition 89 | if (DEV) { 90 | // @ts-expect-error dev property 91 | window.reset = reset; 92 | 93 | const prevTime = localStorage.getItem('time') ?? 0; 94 | localStorage.setItem('time', (performance.now() - startTime + Number(prevTime)).toString()); 95 | const prevCounter = localStorage.getItem('counter') ?? 0; 96 | localStorage.setItem('counter', (Number(prevCounter) + 1).toString()); 97 | Logger.log(`Avg: ${Number(localStorage.getItem('time')) / Number(localStorage.getItem('counter'))}`); 98 | } 99 | } 100 | 101 | function reset(): void { 102 | localStorage.removeItem('time'); 103 | localStorage.removeItem('counter'); 104 | } 105 | 106 | const setRetrieveUrlResponse = callable<[{ response: string; }], void>('SetRetrieveUrlResponse'); 107 | 108 | async function retrieveUrl(): Promise { 109 | const queryParams = new URLSearchParams(window.location.search); 110 | const url = queryParams.get(retrieveUrlQuery); 111 | if (url === null) { 112 | return; 113 | } 114 | 115 | const wnd = window.open(url, undefined, 'width=0,height=1000000,left=0,top=0'); // We set height really high so for some reason the width becomes 0 116 | if (!wnd) { 117 | Logger.error('failed to open new window'); 118 | 119 | return; 120 | } 121 | 122 | let isLoaded = false; 123 | const timeoutTime = Date.now(); 124 | while (!isLoaded && Date.now() - timeoutTime < 5000) { 125 | isLoaded = wnd.document.readyState === 'complete' && wnd.document.body.innerText !== ''; 126 | 127 | // eslint-disable-next-line no-await-in-loop 128 | await sleep(100); 129 | } 130 | 131 | const body = wnd.document.body.innerText; 132 | wnd.close(); 133 | window.close(); 134 | 135 | await setRetrieveUrlResponse({ response: body }); 136 | } 137 | 138 | export default async function WebkitMain(): Promise { 139 | const href = window.location.href; 140 | 141 | if (href.includes('isthereanydeal.com')) { 142 | await Millennium.findElement(document, '.error-message'); 143 | // Page errored, do a force reload 144 | setTimeout(() => { 145 | window.location.reload(); 146 | }, 500); 147 | } 148 | 149 | if (href.includes(`${retrieveUrlQuery}=`)) { 150 | retrieveUrl(); 151 | 152 | return; 153 | } 154 | 155 | if (!href.includes('https://store.steampowered.com') && !href.includes('https://steamcommunity.com')) { 156 | return; 157 | } 158 | 159 | testPerformance(); 160 | 161 | // Log all await calls 162 | // const originalThen = Promise.prototype.then; 163 | 164 | // Promise.prototype.then = function (onFulfilled, onRejected) { 165 | // const stack = new Error().stack; 166 | // console.log("Await called:", stack); 167 | // return originalThen.call(this, onFulfilled, onRejected); 168 | // }; 169 | 170 | Logger.log('plugin is running'); 171 | 172 | await createFakeSteamHeader(); 173 | 174 | await initCdn(); 175 | 176 | const scripts = getNeededScripts(); 177 | 178 | await Promise.all([ 179 | loadCssScripts(scripts), 180 | loadScript(getCdn('js/background.js')), 181 | loadScript(getCdn('js/offscreen_domparser.js')), 182 | ]); 183 | 184 | await loadJsScripts(scripts); 185 | 186 | if (window.location.href.includes('https://store.steampowered.com/account')) { 187 | injectPreferences(); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /webkit/preferences.ts: -------------------------------------------------------------------------------- 1 | import { Millennium } from '@steambrew/webkit'; 2 | import { getCdn, getLoopbackCdn, LOOPBACK_CDN } from './shared'; 3 | 4 | export function injectPreferences(): void { 5 | const sidebarContainer = document.querySelector('.two_column.left'); 6 | const mainContainer = document.querySelector('.two_column.right'); 7 | 8 | if (!sidebarContainer || !mainContainer) { 9 | return; 10 | } 11 | 12 | const augmentedSteamOptions = document.createElement('div'); 13 | augmentedSteamOptions.style.cursor = 'pointer'; 14 | augmentedSteamOptions.classList.add('nav_item'); 15 | augmentedSteamOptions.innerHTML = `logo Augmented Steam`; 16 | 17 | sidebarContainer.appendChild(augmentedSteamOptions); 18 | 19 | augmentedSteamOptions.addEventListener('click', async () => onOptionsClick(sidebarContainer, augmentedSteamOptions, mainContainer)); 20 | 21 | const url = new URL(window.location.href); 22 | if (url.searchParams.get('augmented-steam') === 'true') { 23 | augmentedSteamOptions.click(); 24 | } 25 | } 26 | 27 | async function onOptionsClick(sidebarContainer: Element, augmentedSteamOptions: Element, mainContainer: Element): Promise { 28 | sidebarContainer.querySelectorAll('.active').forEach((element) => { 29 | element.classList.remove('active'); 30 | }); 31 | augmentedSteamOptions.classList.toggle('active'); 32 | 33 | const url = new URL(window.location.href); 34 | url.search = ''; // Removes all searchParams from the URL 35 | url.searchParams.set('augmented-steam', 'true'); 36 | window.history.replaceState({}, '', url.href); 37 | 38 | let optionsHtml = await (await fetch(getCdn('html/options.html'))).text(); 39 | optionsHtml = optionsHtml.replace('', ''); 40 | mainContainer.innerHTML = warningHTML + optionsHtml; 41 | 42 | await Promise.all([ 43 | loadStyle(), 44 | loadScript(), 45 | ]); 46 | 47 | document.dispatchEvent(new Event('initAugmentedSteamOptions')); 48 | 49 | const button = (await Millennium.findElement(document, '.buttons.svelte-1nzirk3'))[0]; 50 | const clearCacheButton = document.createElement('button'); 51 | clearCacheButton.onclick = (): void => { 52 | if (!window.confirm('Are you sure you want to clear the cache?')) { 53 | return; 54 | } 55 | 56 | window.augmentedBrowser.runtime.sendMessage({ action: 'cache.clear' }, () => { 57 | window.location.reload(); 58 | }); 59 | }; 60 | 61 | const span = document.createElement('span'); 62 | span.dataset.tooltipText = 'This may fix some issues with the plugin.'; 63 | span.innerText = 'Clear cache'; 64 | clearCacheButton.appendChild(span); 65 | 66 | button?.appendChild(clearCacheButton); 67 | } 68 | 69 | async function loadStyle(): Promise { 70 | let styleContent = await (await fetch(getCdn('css/options.css'))).text(); 71 | styleContent = styleContent.replace(/(? { 85 | let scriptContent = await (await fetch(getCdn('js/options.js'))).text(); 86 | scriptContent += `\n//# sourceURL=${getCdn('js/options.js')}`; 87 | scriptContent = scriptContent 88 | .replaceAll('chrome', 'augmentedBrowser') 89 | .replaceAll('document.addEventListener("DOMContentLoaded"', 'document.addEventListener("initAugmentedSteamOptions"') 90 | .replaceAll('../img/logo/logo.svg', getLoopbackCdn('img/logo/logo.svg')); 91 | 92 | const script = document.createElement('script'); 93 | script.innerHTML = scriptContent; 94 | document.head.appendChild(script); 95 | } 96 | 97 | const warningHTML = ` 98 |
104 |

WARNING!

105 |

106 | This settings page is a work in progress and will be rewritten in the future. As a result, not all features might work as intended. Please refrain from reporting bugs related to this page. 107 | All community settings don't work. 108 |

109 |
110 | `; 111 | 112 | // Override steam preferences container max-width & overflow when augmented steam options are present 113 | const steamContainerOverrideStyles = ` 114 | #main_content:has(#options) { 115 | width: auto; 116 | max-width: 1155px; 117 | } 118 | 119 | .two_column.right:has(#options) { 120 | overflow: auto; 121 | } 122 | `; 123 | -------------------------------------------------------------------------------- /webkit/script-loading.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable complexity */ 2 | /* eslint-disable max-lines-per-function */ 3 | export function getNeededScripts(): string[] { 4 | const href = window.location.href; 5 | const scripts = []; 6 | 7 | // #region Autogenerated code from convert-manifest.ts 8 | if ((href.match(/^.*:\/\/store\.steampowered\.com\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/.*$/))) { 9 | scripts.push('scriptlets/SteamScriptlet.js'); 10 | } 11 | 12 | if ((href.match(/^.*:\/\/steamcommunity\.com\/app\/.*$/)) && !(href.match(/^.*:\/\/steamcommunity\.com\/app\/.*\/guides$/) || href.match(/^.*:\/\/steamcommunity\.com\/app\/.*\/guides\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/app\/.*\/guides\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/app\/.*\/guides\?.*$/))) { 13 | scripts.push('js/community/app.js'); 14 | scripts.push('css/augmentedsteam.css'); 15 | scripts.push('css/community/app.css'); 16 | } 17 | 18 | if ((href.match(/^.*:\/\/steamcommunity\.com\/tradingcards\/boostercreator$/) || href.match(/^.*:\/\/steamcommunity\.com\/tradingcards\/boostercreator\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/tradingcards\/boostercreator\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/tradingcards\/boostercreator\?.*$/))) { 19 | scripts.push('js/community/booster_creator.js'); 20 | scripts.push('css/augmentedsteam.css'); 21 | scripts.push('css/community/booster_creator.css'); 22 | } 23 | 24 | if ((href.match(/^.*:\/\/steamcommunity\.com\/.*$/)) && !(href.match(/^.*:\/\/steamcommunity\.com\/login$/) || href.match(/^.*:\/\/steamcommunity\.com\/login\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/login\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/openid$/) || href.match(/^.*:\/\/steamcommunity\.com\/openid\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/openid\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/comment$/) || href.match(/^.*:\/\/steamcommunity\.com\/comment\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/comment\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/chat$/) || href.match(/^.*:\/\/steamcommunity\.com\/chat\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/chat\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/tradeoffer\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/market$/) || href.match(/^.*:\/\/steamcommunity\.com\/market\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/market\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/groups\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/app\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/browse$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/browse\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/browse\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/browse\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/browse$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/browse\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/browse\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/browse\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/filedetails$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/filedetails\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/filedetails\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/filedetails$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/filedetails\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/filedetails\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/editguide\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/editguide\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/editguide\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/editguide\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/tradingcards\/boostercreator$/) || href.match(/^.*:\/\/steamcommunity\.com\/tradingcards\/boostercreator\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/tradingcards\/boostercreator\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/tradingcards\/boostercreator\?.*$/))) { 25 | scripts.push('js/community/default.js'); 26 | scripts.push('css/augmentedsteam.css'); 27 | scripts.push('css/community/default.css'); 28 | } 29 | 30 | if ((href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/badges$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/badges\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/badges\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/badges\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/badges$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/badges\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/badges\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/badges\?.*$/))) { 31 | scripts.push('js/community/badges.js'); 32 | scripts.push('css/augmentedsteam.css'); 33 | scripts.push('css/community/badges.css'); 34 | } 35 | 36 | if ((href.match(/^.*:\/\/steamcommunity\.com\/app\/.*\/guides$/) || href.match(/^.*:\/\/steamcommunity\.com\/app\/.*\/guides\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/app\/.*\/guides\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/app\/.*\/guides\?.*$/))) { 37 | scripts.push('js/community/guides.js'); 38 | scripts.push('css/augmentedsteam.css'); 39 | scripts.push('css/community/guides.css'); 40 | } 41 | 42 | if ((href.match(/^.*:\/\/steamcommunity\.com\/market\/search$/) || href.match(/^.*:\/\/steamcommunity\.com\/market\/search\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/market\/search\?.*$/))) { 43 | scripts.push('js/community/market_search.js'); 44 | scripts.push('css/augmentedsteam.css'); 45 | scripts.push('css/community/market_search.css'); 46 | } 47 | 48 | if ((href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/gamecards\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/gamecards\/.*$/))) { 49 | scripts.push('js/community/gamecard.js'); 50 | scripts.push('css/augmentedsteam.css'); 51 | scripts.push('css/community/gamecard.css'); 52 | } 53 | 54 | if ((href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/stats\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/stats\/.*$/))) { 55 | scripts.push('js/community/profile_stats.js'); 56 | scripts.push('css/augmentedsteam.css'); 57 | scripts.push('css/community/profile_stats.css'); 58 | } 59 | 60 | if ((href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/editguide\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/editguide\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/editguide\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/editguide\?.*$/))) { 61 | scripts.push('js/community/edit_guide.js'); 62 | scripts.push('css/augmentedsteam.css'); 63 | scripts.push('css/community/edit_guide.css'); 64 | } 65 | 66 | if ((href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/home$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/home\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/home\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/home\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/home$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/home\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/home\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/home\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/myactivity$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/myactivity\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/myactivity\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/myactivity\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/myactivity$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/myactivity\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/myactivity\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/myactivity\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/friendactivitydetail\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/friendactivitydetail\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/status\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/status\/.*$/))) { 67 | scripts.push('js/community/profile_activity.js'); 68 | scripts.push('css/augmentedsteam.css'); 69 | scripts.push('css/community/profile_activity.css'); 70 | } 71 | 72 | if ((href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/myworkshopfiles\/\?.*browsefilter=mysubscriptions.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/myworkshopfiles\?.*browsefilter=mysubscriptions.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/myworkshopfiles\/\?.*browsefilter=mysubscriptions.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/myworkshopfiles\?.*browsefilter=mysubscriptions.*$/))) { 73 | scripts.push('js/community/myworkshop.js'); 74 | scripts.push('css/augmentedsteam.css'); 75 | scripts.push('css/community/myworkshop.css'); 76 | } 77 | 78 | if ((href.match(/^.*:\/\/steamcommunity\.com\/id\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*$/)) && !(href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/home$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/home\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/home\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/home\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/home$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/home\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/home\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/home\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/myactivity$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/myactivity\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/myactivity\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/myactivity\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/myactivity$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/myactivity\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/myactivity\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/myactivity\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/friendactivitydetail\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/friendactivitydetail\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/status\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/status\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/games$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/games\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/games\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/games\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/games$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/games\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/games\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/games\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/followedgames$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/followedgames\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/followedgames\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/followedgames\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/followedgames$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/followedgames\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/followedgames\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/followedgames\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/edit\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/edit\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/badges$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/badges\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/badges\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/badges\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/badges$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/badges\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/badges\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/badges\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/gamecards\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/gamecards\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/friendsthatplay\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/friendsthatplay\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/friends$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/friends\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/friends\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/friends$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/friends\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/friends\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/groups$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/groups\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/groups\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/groups$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/groups\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/groups\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/following$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/following\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/following\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/following$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/following\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/following\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/inventory$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/inventory\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/inventory\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/inventory\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/inventory$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/inventory\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/inventory\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/inventory\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/stats\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/stats\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/myworkshopfiles\/\?.*browsefilter=mysubscriptions.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/myworkshopfiles\?.*browsefilter=mysubscriptions.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/myworkshopfiles\/\?.*browsefilter=mysubscriptions.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/myworkshopfiles\?.*browsefilter=mysubscriptions.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/recommended$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/recommended\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/recommended\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/recommended\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/recommended$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/recommended\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/recommended\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/recommended\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/reviews$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/reviews\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/reviews\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/reviews\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/reviews$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/reviews\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/reviews\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/reviews\?.*$/))) { 79 | scripts.push('js/community/profile_home.js'); 80 | scripts.push('css/augmentedsteam.css'); 81 | scripts.push('css/community/profile_home.css'); 82 | } 83 | 84 | if ((href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/friendsthatplay\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/friendsthatplay\/.*$/))) { 85 | scripts.push('js/community/friends_that_play.js'); 86 | scripts.push('css/augmentedsteam.css'); 87 | scripts.push('css/community/friends_that_play.css'); 88 | } 89 | 90 | if ((href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/games$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/games\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/games\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/games\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/games$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/games\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/games\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/games\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/followedgames$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/followedgames\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/followedgames\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/followedgames\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/followedgames$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/followedgames\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/followedgames\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/followedgames\?.*$/))) { 91 | scripts.push('js/community/games.js'); 92 | scripts.push('css/augmentedsteam.css'); 93 | scripts.push('css/community/games.css'); 94 | } 95 | 96 | if ((href.match(/^.*:\/\/steamcommunity\.com\/groups\/.*$/))) { 97 | scripts.push('js/community/group_home.js'); 98 | scripts.push('css/augmentedsteam.css'); 99 | scripts.push('css/community/group_home.css'); 100 | } 101 | 102 | if ((href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/friends$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/friends\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/friends\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/friends$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/friends\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/friends\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/groups$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/groups\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/groups\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/groups$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/groups\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/groups\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/following$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/following\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/following\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/following$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/following\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/following\?.*$/))) { 103 | scripts.push('js/community/friends_and_groups.js'); 104 | scripts.push('css/augmentedsteam.css'); 105 | scripts.push('css/community/friends_and_groups.css'); 106 | } 107 | 108 | if ((href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/inventory$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/inventory\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/inventory\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/inventory\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/inventory$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/inventory\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/inventory\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/inventory\?.*$/))) { 109 | scripts.push('js/community/inventory.js'); 110 | scripts.push('css/augmentedsteam.css'); 111 | scripts.push('css/community/inventory.css'); 112 | } 113 | 114 | if ((href.match(/^.*:\/\/steamcommunity\.com\/market\/listings\/.*$/))) { 115 | scripts.push('js/community/market_listing.js'); 116 | scripts.push('css/augmentedsteam.css'); 117 | scripts.push('css/community/market_listing.css'); 118 | } 119 | 120 | if ((href.match(/^.*:\/\/steamcommunity\.com\/tradeoffer\/.*$/))) { 121 | scripts.push('js/community/trade_offer.js'); 122 | scripts.push('css/augmentedsteam.css'); 123 | scripts.push('css/community/trade_offer.css'); 124 | } 125 | 126 | if ((href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/filedetails$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/filedetails\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/filedetails\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/filedetails$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/filedetails\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/filedetails\?.*$/))) { 127 | scripts.push('js/community/shared_files.js'); 128 | scripts.push('css/augmentedsteam.css'); 129 | scripts.push('css/community/shared_files.css'); 130 | } 131 | 132 | if ((href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/browse$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/browse\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/browse\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/browse\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/browse$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/browse\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/browse\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/browse\?.*$/))) { 133 | scripts.push('js/community/workshop_browse.js'); 134 | scripts.push('css/augmentedsteam.css'); 135 | scripts.push('css/community/workshop_browse.css'); 136 | } 137 | 138 | if ((href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/recommended$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/recommended\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/recommended\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/recommended\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/recommended$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/recommended\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/recommended\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/recommended\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/reviews$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/reviews\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/reviews\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/reviews\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/reviews$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/reviews\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/reviews\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/reviews\?.*$/))) { 139 | scripts.push('js/community/recommended.js'); 140 | scripts.push('css/augmentedsteam.css'); 141 | scripts.push('css/community/recommended.css'); 142 | } 143 | 144 | if ((href.match(/^.*:\/\/.*\.steampowered\.com\/bundle\/.*$/))) { 145 | scripts.push('js/store/bundle.js'); 146 | scripts.push('css/augmentedsteam.css'); 147 | scripts.push('css/store/bundle.css'); 148 | } 149 | 150 | if ((href.match(/^.*:\/\/.*\.steampowered\.com\/app\/.*$/))) { 151 | scripts.push('js/store/app.js'); 152 | scripts.push('css/augmentedsteam.css'); 153 | scripts.push('css/store/app.css'); 154 | } 155 | 156 | if ((href.match(/^.*:\/\/.*\.steampowered\.com\/points$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/points\/.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/points\?.*$/))) { 157 | scripts.push('js/store/points_shop.js'); 158 | scripts.push('css/augmentedsteam.css'); 159 | scripts.push('css/store/points_shop.css'); 160 | } 161 | 162 | if ((href.match(/^.*:\/\/.*\.steampowered\.com\/steamaccount\/addfunds$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/steamaccount\/addfunds\/$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/steamaccount\/addfunds\/\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/steamaccount\/addfunds\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/digitalgiftcards\/selectgiftcard$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/digitalgiftcards\/selectgiftcard\/$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/digitalgiftcards\/selectgiftcard\/\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/digitalgiftcards\/selectgiftcard\?.*$/))) { 163 | scripts.push('js/store/funds.js'); 164 | scripts.push('css/augmentedsteam.css'); 165 | scripts.push('css/store/funds.css'); 166 | } 167 | 168 | if ((href.match(/^.*:\/\/steamcommunity\.com\/market$/) || href.match(/^.*:\/\/steamcommunity\.com\/market\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/market\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/market\?.*$/))) { 169 | scripts.push('js/community/market_home.js'); 170 | scripts.push('css/augmentedsteam.css'); 171 | scripts.push('css/community/market_home.css'); 172 | } 173 | 174 | if ((href.match(/^.*:\/\/.*\.steampowered\.com\/account\/registerkey$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account\/registerkey\/$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account\/registerkey\/\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account\/registerkey\?.*$/))) { 175 | scripts.push('js/store/registerkey.js'); 176 | scripts.push('css/augmentedsteam.css'); 177 | scripts.push('css/store/registerkey.css'); 178 | } 179 | 180 | if ((href.match(/^.*:\/\/.*\.steampowered\.com\/search$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/search\/.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/search\?.*$/))) { 181 | scripts.push('js/store/search.js'); 182 | scripts.push('css/augmentedsteam.css'); 183 | scripts.push('css/store/search.css'); 184 | } 185 | 186 | if ((href.match(/^.*:\/\/store\.steampowered\.com\/account\/licenses$/) || href.match(/^.*:\/\/store\.steampowered\.com\/account\/licenses\/$/) || href.match(/^.*:\/\/store\.steampowered\.com\/account\/licenses\/\?.*$/) || href.match(/^.*:\/\/store\.steampowered\.com\/account\/licenses\?.*$/))) { 187 | scripts.push('js/store/licences.js'); 188 | scripts.push('css/augmentedsteam.css'); 189 | scripts.push('css/store/licences.css'); 190 | } 191 | 192 | if ((href.match(/^.*:\/\/.*\.steampowered\.com\/wishlist$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/wishlist\/$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/wishlist\/\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/wishlist\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/wishlist\/id\/.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/wishlist\/profiles\/.*$/))) { 193 | scripts.push('js/store/wishlist.js'); 194 | scripts.push('css/augmentedsteam.css'); 195 | scripts.push('css/store/wishlist.css'); 196 | } 197 | 198 | if ((href.match(/^.*:\/\/steamcommunity\.com\/id\/.*\/edit\/.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/profiles\/.*\/edit\/.*$/))) { 199 | scripts.push('js/community/profile_edit.js'); 200 | scripts.push('css/augmentedsteam.css'); 201 | scripts.push('css/community/profile_edit.css'); 202 | } 203 | 204 | if ((href.match(/^.*:\/\/.*\.steampowered\.com\/sub\/.*$/))) { 205 | scripts.push('js/store/sub.js'); 206 | scripts.push('css/augmentedsteam.css'); 207 | scripts.push('css/store/sub.css'); 208 | } 209 | 210 | if ((href.match(/^.*:\/\/.*\.steampowered\.com\/agecheck\/.*$/))) { 211 | scripts.push('js/store/agecheck.js'); 212 | scripts.push('css/augmentedsteam.css'); 213 | scripts.push('css/store/agecheck.css'); 214 | } 215 | 216 | if ((href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/sharedfiles\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\/\?.*$/) || href.match(/^.*:\/\/steamcommunity\.com\/workshop\?.*$/))) { 217 | scripts.push('js/community/workshop.js'); 218 | scripts.push('css/augmentedsteam.css'); 219 | scripts.push('css/community/workshop.css'); 220 | } 221 | 222 | if ((href.match(/^.*:\/\/.*\.steampowered\.com\/cart$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/cart\/.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/cart\?.*$/))) { 223 | scripts.push('js/store/cart.js'); 224 | scripts.push('css/augmentedsteam.css'); 225 | scripts.push('css/store/cart.css'); 226 | } 227 | 228 | if ((href.match(/^.*:\/\/.*\.steampowered\.com\/account$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account\/$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account\/\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account\?.*$/))) { 229 | scripts.push('js/store/account.js'); 230 | scripts.push('css/augmentedsteam.css'); 231 | scripts.push('css/store/account.css'); 232 | } 233 | 234 | if ((href.match(/^.*:\/\/store\.steampowered\.com\/$/) || href.match(/^.*:\/\/store\.steampowered\.com\/\?.*$/))) { 235 | scripts.push('js/store/frontpage.js'); 236 | scripts.push('css/augmentedsteam.css'); 237 | scripts.push('css/store/frontpage.css'); 238 | } 239 | 240 | if ((href.match(/^.*:\/\/.*\.steampowered\.com\/.*$/)) && !(href.match(/^.*:\/\/store\.steampowered\.com\/dynamicstore\/.*$/) || href.match(/^.*:\/\/store\.steampowered\.com\/supportmessages$/) || href.match(/^.*:\/\/store\.steampowered\.com\/supportmessages\/$/) || href.match(/^.*:\/\/store\.steampowered\.com\/supportmessages\/\?.*$/) || href.match(/^.*:\/\/store\.steampowered\.com\/supportmessages\?.*$/) || href.match(/^.*:\/\/store\.steampowered\.com\/video\/.*$/) || href.match(/^.*:\/\/store\.steampowered\.com\/widget$/) || href.match(/^.*:\/\/store\.steampowered\.com\/widget\/.*$/) || href.match(/^.*:\/\/store\.steampowered\.com\/widget\?.*$/) || href.match(/^.*:\/\/store\.steampowered\.com\/login$/) || href.match(/^.*:\/\/store\.steampowered\.com\/login\/.*$/) || href.match(/^.*:\/\/store\.steampowered\.com\/login\?.*$/) || href.match(/^.*:\/\/store\.steampowered\.com\/join$/) || href.match(/^.*:\/\/store\.steampowered\.com\/join\/.*$/) || href.match(/^.*:\/\/store\.steampowered\.com\/join\?.*$/) || href.match(/^.*:\/\/store\.steampowered\.com\/api\/.*$/) || href.match(/^.*:\/\/api\.steampowered\.com\/.*$/) || href.match(/^.*:\/\/help\.steampowered\.com\/.*$/) || href.match(/^.*:\/\/login\.steampowered\.com\/.*$/) || href.match(/^.*:\/\/checkout\.steampowered\.com\/.*$/) || href.match(/^.*:\/\/partner\.steampowered\.com\/.*$/) || href.match(/^.*:\/\/store\.steampowered\.com\/$/) || href.match(/^.*:\/\/store\.steampowered\.com\/\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/wishlist\/id\/.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/wishlist\/profiles\/.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/search$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/search\/.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/search\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/steamaccount\/addfunds$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/steamaccount\/addfunds\/$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/steamaccount\/addfunds\/\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/steamaccount\/addfunds\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/digitalgiftcards\/selectgiftcard$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/digitalgiftcards\/selectgiftcard\/$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/digitalgiftcards\/selectgiftcard\/\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/digitalgiftcards\/selectgiftcard\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account\/$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account\/\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account\/licenses$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account\/licenses\/$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account\/licenses\/\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account\/licenses\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account\/registerkey$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account\/registerkey\/$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account\/registerkey\/\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/account\/registerkey\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/bundle\/.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/sub\/.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/app\/.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/agecheck\/.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/points$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/points\/.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/points\?.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/cart$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/cart\/.*$/) || href.match(/^.*:\/\/.*\.steampowered\.com\/cart\?.*$/))) { 241 | scripts.push('js/store/default.js'); 242 | scripts.push('css/augmentedsteam.css'); 243 | scripts.push('css/store/default.css'); 244 | } 245 | // #endregion 246 | 247 | return scripts; 248 | } 249 | -------------------------------------------------------------------------------- /webkit/shared.ts: -------------------------------------------------------------------------------- 1 | import { callable } from '@steambrew/webkit'; 2 | 3 | export const VERSION = '4.2.1'; 4 | export let CDN: string; 5 | const COMMIT_SHA = 'a9272e40f9a561cd756304f90b07858b6b2607ed'; 6 | export const LOOPBACK_CDN = `https://cdn.jsdelivr.net/gh/IsThereAnyDeal/AugmentedSteam@${COMMIT_SHA}/src`; 7 | export const DEV = false; 8 | 9 | export function getCdn(path: string): string { 10 | if (path.startsWith('/')) { 11 | return `${CDN}${path}`; 12 | } 13 | 14 | return `${CDN}/${path}`; 15 | } 16 | 17 | export function getLoopbackCdn(path: string): string { 18 | if (path.startsWith('/')) { 19 | return `${LOOPBACK_CDN}${path}`; 20 | } 21 | 22 | return `${LOOPBACK_CDN}/${path}`; 23 | } 24 | 25 | const PLUGIN_DIR_STORAGE = 'augmented_plugin_dir'; 26 | const getPluginDir = callable<[], string>('GetPluginDir'); 27 | 28 | export async function initCdn(): Promise { 29 | let pluginDir = sessionStorage.getItem(PLUGIN_DIR_STORAGE); 30 | if (pluginDir === null) { 31 | pluginDir = await getPluginDir(); 32 | sessionStorage.setItem(PLUGIN_DIR_STORAGE, pluginDir); 33 | } 34 | // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition 35 | const envString = DEV ? 'dev' : 'prod'; 36 | const extensionFolder = `${pluginDir.replace(/.*\\([^\\]+)\\([^\\]+)$/, '/$1/$2')}/AugmentedSteam/dist/${envString}.chrome`; 37 | CDN = `https://pseudo.millennium.app${extensionFolder}`; 38 | } 39 | 40 | const backendError = callable<[{ message: string; }], void>('Logger.error'); 41 | const backendWarn = callable<[{ message: string; }], void>('Logger.warn'); 42 | 43 | export const Logger = { 44 | error: (...message: unknown[]): void => { 45 | console.error('%c AugmentedSteam plugin ', 'background: red; color: white', ...message); 46 | backendError({ message: message.join(' ') }); 47 | }, 48 | log: (...message: unknown[]): void => { 49 | console.log('%c AugmentedSteam plugin ', 'background: purple; color: white', ...message); 50 | }, 51 | debug: (...message: unknown[]): void => { 52 | console.debug('%c AugmentedSteam plugin ', 'background: black; color: white', ...message); 53 | }, 54 | warn: (...message: unknown[]): void => { 55 | console.warn('%c AugmentedSteam plugin ', 'background: orange; color: white', ...message); 56 | backendWarn({ message: message.join(' ') }); 57 | }, 58 | }; 59 | 60 | export async function sleep(ms: number): Promise { 61 | return new Promise((res) => { 62 | setTimeout(res, ms); 63 | }); 64 | } 65 | -------------------------------------------------------------------------------- /webkit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "jsx": "react-jsx", 5 | "jsxImportSource": "preact", 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["."], 10 | } 11 | --------------------------------------------------------------------------------