├── .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 | 
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 | 
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 | 
58 |
59 | #### Options page
60 |
61 | 
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 |
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 = `
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 |
--------------------------------------------------------------------------------