diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index 120963b..db29ace 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -12,10 +12,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Use Node.js 16.x + - name: Use Node.js 20.x uses: actions/setup-node@v3 with: - node-version: 16.x + node-version: 20.x - name: Building.. run: | diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3a93013..11629e9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -9,10 +9,10 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Use Node.js 16.x + - name: Use Node.js 20.x uses: actions/setup-node@v3 with: - node-version: 16.x + node-version: 20.x - name: Building.. run: | diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index b5bf43b..fd5b055 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node-version: [12.x, 14.x, 16.x] + node-version: [20.x] steps: - uses: actions/checkout@v3 @@ -23,9 +23,10 @@ jobs: npm install npm run test - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v5 with: flags: unittests name: codecov-umbrella fail_ci_if_error: true + token: ${{ secrets.CODECOV_TOKEN }} verbose: true \ No newline at end of file diff --git a/README.md b/README.md index ce8fa10..e8f8c9d 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,12 @@ -

logo Steam Enchanter

+

logo Steam Enchanter

This is extension of Google Chrome browsers for [Steam](https://store.steampowered.com) users. Automate processes for getting badges. --- +[![store](https://img.shields.io/chrome-web-store/v/ddjjembcdeddfaebkemgchgfbabbkppl)](https://chrome.google.com/webstore/detail/steam-enchanter/ddjjembcdeddfaebkemgchgfbabbkppl) +[![rating](https://img.shields.io/chrome-web-store/rating/ddjjembcdeddfaebkemgchgfbabbkppl)](https://chrome.google.com/webstore/detail/steam-enchanter/ddjjembcdeddfaebkemgchgfbabbkppl) + + [![codecov](https://codecov.io/gh/BigTows/Steam-Enchanter/branch/main/graph/badge.svg?token=S3ZP1I7M06)](https://codecov.io/gh/BigTows/Steam-Enchanter) ## Features diff --git a/package-lock.json b/package-lock.json index 6fd19a1..4806eed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "steam-enchanter", - "version": "1.1.0", + "version": "1.1.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "steam-enchanter", - "version": "1.1.0", + "version": "1.1.2", "license": "MIT", "dependencies": { "axios": "^0.27.2", @@ -1732,6 +1732,7 @@ "version": "0.27.2", "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.14.9", "form-data": "^4.0.0" @@ -2005,6 +2006,19 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2486,6 +2500,20 @@ "node": ">=8" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.219", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.219.tgz", @@ -2575,12 +2603,57 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", "dev": true }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2916,15 +2989,16 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -2935,12 +3009,15 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -2997,10 +3074,13 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/gensync": { "version": "1.0.0-beta.2", @@ -3020,6 +3100,30 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -3029,6 +3133,19 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -3108,6 +3225,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -3135,6 +3264,45 @@ "node": ">=8" } }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/html-encoding-sniffer": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", @@ -4471,6 +4639,15 @@ "tmpl": "1.0.5" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/memory-fs": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", @@ -7904,6 +8081,15 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -8276,6 +8462,16 @@ } } }, + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, "electron-to-chromium": { "version": "1.4.219", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.219.tgz", @@ -8344,12 +8540,41 @@ "is-arrayish": "^0.2.1" } }, + "es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, "es-module-lexer": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", "dev": true }, + "es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "requires": { + "es-errors": "^1.3.0" + } + }, + "es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "requires": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -8604,17 +8829,19 @@ } }, "follow-redirects": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", - "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==" }, "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, @@ -8657,10 +8884,9 @@ "optional": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" }, "gensync": { "version": "1.0.0-beta.2", @@ -8674,12 +8900,38 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + } + }, "get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true }, + "get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "requires": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + } + }, "get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -8735,6 +8987,11 @@ "slash": "^3.0.0" } }, + "gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" + }, "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -8756,6 +9013,27 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" + }, + "has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "requires": { + "has-symbols": "^1.0.3" + } + }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "requires": { + "function-bind": "^1.1.2" + } + }, "html-encoding-sniffer": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", @@ -9782,6 +10060,11 @@ "tmpl": "1.0.5" } }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" + }, "memory-fs": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", diff --git a/package.json b/package.json index 55ff89a..ac73069 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "steam-enchanter", - "version": "1.1.0", + "version": "1.1.2", "description": "Google Chrome extension for Steam. Automating some actions for Level up your account.", "main": "index.js", "scripts": { diff --git a/public/logo.png b/public/logo.png deleted file mode 100644 index 6c713ba..0000000 Binary files a/public/logo.png and /dev/null differ diff --git a/public/manifest.json b/public/manifest.json index 4362d02..4a29b0d 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -2,11 +2,17 @@ "manifest_version": 3, "name": "Steam Enchanter", "description": "Automating some actions for Level up your Steam account.", - "version": "1.1.0", + "version": "1.1.2", "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyPmdRVJ9XxGnV4qLbRm7pEM8kUzOa6MnJFJ43fstWn6wIktC6lWZ7Qz9oIyvGua9ytYSTaqsavQksPa8eBY8L9V+Hd6xOEap82SfT353N0P9M/ZLQsG1X/KtUijaoMxoPl5VNBz+h0tuVhNHFYjeFqtujTU1JrY+Dv4U0COQv4wiGUUfdh0QVIxLg/ELK/ltmM4xsIz6EehrowY6GGjQiT7PJPda6MwS0WaJeJa6HgvIAILISVDNMFD2CipiuHZjcxC3FQQGMabciNwiv5a3sY7wyum1zWUJb71DAJV3X0eqLuDXuCtoJm+O0HOFnhtVXeR9b4WtqYyOGk92yDuFmwIDAQAB", "action": { "default_icon": "icon.png" }, + "icons": { + "16": "icon.png", + "32": "icon.png", + "48": "icon.png", + "128": "icon.png" + }, "content_scripts": [ { "matches": [ diff --git a/src/__tests__/SteamMarketApiTest.ts b/src/__tests__/SteamMarketApiTest.ts index 320566c..ea7b81f 100644 --- a/src/__tests__/SteamMarketApiTest.ts +++ b/src/__tests__/SteamMarketApiTest.ts @@ -45,6 +45,35 @@ test("Create order", async () => { }); +test("Can't create order, need confirmation", async () => { + const mockHttp = mock(); + const steamMarketApi = new SteamMarketApiImpl("localhost", mockHttp); + + mockHttp.post.mockReturnValue(new Promise((resolve) => { + resolve( + { + success: 22, + confirmation:{ + confirmation_id: "124124124", + } + } + ); + })); + + await expect(() => { + return steamMarketApi.createOrder( + { + sessionId: "124", + currency: 5, + appId: 730, + marketHashName: "Any", + priceTotal: 23, + quantity: 151 + } + ); + }).rejects.toThrowError("You need to confirm your order (124124124)."); +}); + test("Create order, unsuccessful", async () => { const mockHttp = mock(); const steamMarketApi = new SteamMarketApiImpl("localhost", mockHttp); diff --git a/src/__tests__/pages/SteamPageLoaderTest.ts b/src/__tests__/pages/SteamPageLoaderTest.ts index 9470cd0..f6a04d2 100644 --- a/src/__tests__/pages/SteamPageLoaderTest.ts +++ b/src/__tests__/pages/SteamPageLoaderTest.ts @@ -1,22 +1,67 @@ import "reflect-metadata"; +import { anyObject, mock } from "jest-mock-extended"; +import HttpClient from "../../component/http/HttpClient"; +import GameCardsPage from "../../steam/pages/GameCardsPage"; +import SteamPageLoader from "../../steam/pages/SteamPageLoader"; +import CardMarketPage from "../../steam/pages/CardMarketPage"; +import UserCompletedBadgesPage from "../../steam/pages/UserCompletedBadgesPage"; +const host = "http://localhost"; -test("Load game card", () => { - // TODO const httpClientMock = mock(); - // const host = "http://localhost"; - // - // const steamPageLoader = new SteamPageLoader(host, httpClientMock); - // const steamId = "12414124"; - // const appId = 1244; - // - // const mockedPage = mock(); - // - // console.log(`${host}/profiles/${steamId}/gamecards/${appId}`) - // httpClientMock.get - // .mockReturnValue(new Promise(resolve => resolve(mockedPage))); - // - // const result = steamPageLoader.loadGameCard(steamId, appId); - // - // expect(result).toBe(mockedPage); - -}); \ No newline at end of file +test("Load game card", async () => { + const httpClientMock = mock(); + + const steamPageLoader = new SteamPageLoader(host, httpClientMock); + const steamId = "12414124"; + const appId = 1244; + + const mockedPage = mock(); + + httpClientMock.get + .calledWith(`${host}/profiles/${steamId}/gamecards/${appId}`, anyObject()) + .mockReturnValue(new Promise(resolve => { + resolve(mockedPage) + })); + + const result = await steamPageLoader.loadGameCard(steamId, appId); + expect(result).toBe(mockedPage); +}); + +test("Load card market page", async ()=>{ + const httpClientMock = mock(); + + const steamPageLoader = new SteamPageLoader(host, httpClientMock); + + const link = 'ajkfklajkaakfjalf'; + + const mockedPage = mock(); + + httpClientMock.get + .calledWith(link, anyObject()) + .mockReturnValue(new Promise(resolve => { + resolve(mockedPage) + })); + + const result = await steamPageLoader.loadCardMarketPage(link); + expect(result).toBe(mockedPage); +}) + + +test("Load user compled badges", async ()=>{ + const httpClientMock = mock(); + + const steamPageLoader = new SteamPageLoader(host, httpClientMock); + const steamId = "12414124"; + const page = 1244; + + const mockedPage = mock(); + + httpClientMock.get + .calledWith(`${host}/profiles/${steamId}/badges/?sort=c&p=${page}`, anyObject()) + .mockReturnValue(new Promise(resolve => { + resolve(mockedPage) + })); + + const result = await steamPageLoader.loadUserCompletedBadges(steamId, page); + expect(result).toBe(mockedPage); +}) \ No newline at end of file diff --git a/src/__tests__/resources/html/steamCardMarketPage.html b/src/__tests__/resources/html/steamCardMarketPage.html index 45c7dcc..dc81320 100644 --- a/src/__tests__/resources/html/steamCardMarketPage.html +++ b/src/__tests__/resources/html/steamCardMarketPage.html @@ -486,7 +486,7 @@

User Information

-
+

Buy multiple types of items

diff --git a/src/__tests__/service/SteamCardTraderServiceTest.ts b/src/__tests__/service/SteamCardTraderServiceTest.ts new file mode 100644 index 0000000..6850ad1 --- /dev/null +++ b/src/__tests__/service/SteamCardTraderServiceTest.ts @@ -0,0 +1,54 @@ +import "reflect-metadata"; +import SteamMarketApi from "../../api/steam/SteamMarketApi"; +import { mock } from "jest-mock-extended"; +import SteamCardTraderService from "../../service/SteamCardTraderService"; +import Cookies from "js-cookie"; +import { Status } from "../../service/SteamCardTraderProcess"; + +test("Create trader process.", async () => { + + const steamApiMock = mock(); + const sessionId = "12414"; + Cookies.set("sessionid", sessionId);//TODO side effect :( + + const service = new SteamCardTraderService(steamApiMock); + + + steamApiMock.createOrder.calledWith( + { + sessionId: sessionId, + currency: 5, + appId: 10, + marketHashName: "112", + priceTotal: (91 + 200) * 2, + quantity: 2 + }).mockReturnValue(new Promise(resolve => resolve("order-1"))); + + const process = await service.createTrader([ + { + appId: 10, + hashName: "112", + quantity: 2, + price: 91 + } + ], 5); + + expect(process.getCurrentStatus()).toBe(Status.pending); +}); + +test("When session id is not initialized", async () => { + const steamApiMock = mock(); + Cookies.remove('sessionid') + const service = new SteamCardTraderService(steamApiMock); + + await expect(async () => { + await service.createTrader([ + { + appId: 10, + hashName: "112", + quantity: 2, + price: 91 + } + ], 5); + }).rejects.toThrowError("Session id is not initialized"); +}); \ No newline at end of file diff --git a/src/api/steam/SteamMarketApiImpl.ts b/src/api/steam/SteamMarketApiImpl.ts index 4bdf943..2bc2f0f 100644 --- a/src/api/steam/SteamMarketApiImpl.ts +++ b/src/api/steam/SteamMarketApiImpl.ts @@ -1,99 +1,122 @@ import HttpClient from "../../component/http/HttpClient"; -import { inject, injectable } from "tsyringe"; -import { Tokens } from "../../configuration/Injector"; -import SteamMarketApi, { CreateMarkerOrderRequest, OrderStatusResponse } from "./SteamMarketApi"; +import {inject, injectable} from "tsyringe"; +import {Tokens} from "../../configuration/Injector"; +import SteamMarketApi, {CreateMarkerOrderRequest, OrderStatusResponse} from "./SteamMarketApi"; +import StreamApiException from "./exception/StreamApiException"; +import NeedConfirmationStreamApiException from "./exception/NeedConfirmationStreamApiException"; interface CreateMarketOrderResponse { - success: number, - buy_orderid: string + success: number, + buy_orderid: string + confirmation?: { + confirmation_id: string + } } interface OrderStatusResponseRaw { - success: number, - active: number, - purchased: number, - quantity: string, - quantity_remaining: string + success: number, + active: number, + purchased: number, + quantity: string, + quantity_remaining: string } @injectable() class SteamMarketApiImpl implements SteamMarketApi { - private readonly steamHost: string; - private readonly httpClient: HttpClient; - - private static readonly COUNT_RETRIES = 6; - - private static readonly STEAM_RETRIES_CODES = [ - 10,//The game's servers are currently too busy. Your pu…unds have been exchanged. Please try again later - 40,//Tod many request - 107//Извините! Серверы Steam не ответили на запрос о ва…полнен. Если это не так, повторите попытку позже. - ]; - - constructor(@inject(Tokens.STEAM_API) host: string, @inject(Tokens.HTTP_CLIENT) httpClient: HttpClient) { - this.steamHost = host; - this.httpClient = httpClient; - } - - /** - * Create order at steam market. - */ - public async createOrder(request: CreateMarkerOrderRequest): Promise { - const data = new URLSearchParams(); - data.append("sessionid", request.sessionId); - data.append("currency", request.currency as unknown as string); - data.append("appid", request.appId as unknown as string); - data.append("market_hash_name", request.marketHashName); - data.append("price_total", request.priceTotal as unknown as string); - data.append("quantity", request.quantity as unknown as string); - data.append("billing_state", ""); - data.append("save_my_address", "0"); - - const result = await this.httpClient.post(`${this.steamHost}/market/createbuyorder/`, data, { - retries: { - count: SteamMarketApiImpl.COUNT_RETRIES, - needRetry: (result) => { - return SteamMarketApiImpl.STEAM_RETRIES_CODES.includes(result.success); + private readonly steamHost: string; + private readonly httpClient: HttpClient; + + private static readonly COUNT_RETRIES = 6; + + /** + * 22 - The order has been placed, but you need to confirm it. + * @private + */ + private static readonly STEAM_NEED_CONFIRMATION = 22; + + /** + * Slow down, You have tried too many purchases without confirmation. Please wait a few minutes before trying again. + * @private + */ + private static readonly STEAM_TOO_MANY_PURCHASED = 84; + + private static readonly STEAM_RETRIES_CODES = [ + 10,//The game's servers are currently too busy. Your pu…unds have been exchanged. Please try again later + 40,//Tod many request + 107//Извините! Серверы Steam не ответили на запрос о ва…полнен. Если это не так, повторите попытку позже. + ]; + + constructor(@inject(Tokens.STEAM_API) host: string, @inject(Tokens.HTTP_CLIENT) httpClient: HttpClient) { + this.steamHost = host; + this.httpClient = httpClient; + } + + /** + * Create order at steam market. + * @return The promise of order id + */ + public async createOrder(request: CreateMarkerOrderRequest): Promise { + const data = new URLSearchParams(); + data.append("sessionid", request.sessionId); + data.append("currency", request.currency as unknown as string); + data.append("appid", request.appId as unknown as string); + data.append("market_hash_name", request.marketHashName); + data.append("price_total", request.priceTotal as unknown as string); + data.append("quantity", request.quantity as unknown as string); + data.append("billing_state", ""); + data.append("save_my_address", "0"); + + const result = await this.httpClient.post(`${this.steamHost}/market/createbuyorder/`, data, { + retries: { + count: SteamMarketApiImpl.COUNT_RETRIES, + needRetry: (result) => { + return SteamMarketApiImpl.STEAM_RETRIES_CODES.includes(result.success); + } + } + }); + + if (result.success === 1) { + return result.buy_orderid; + } + + if (result.success === SteamMarketApiImpl.STEAM_NEED_CONFIRMATION) { + throw new NeedConfirmationStreamApiException(result.confirmation!.confirmation_id); } - } - }); - if (result.success === 1) { - return result.buy_orderid; + throw new StreamApiException("Can't place order at Steam :( Code: " + result.success); } - throw new Error("Can't place order at Steam :("); - } - public async getOrderStatus(sessionId: string, orderId: string): Promise { - const resultRaw = await this.httpClient.get(`${this.steamHost}/market/getbuyorderstatus/?sessionid=${sessionId}&buy_orderid=${orderId}`); + public async getOrderStatus(sessionId: string, orderId: string): Promise { + const resultRaw = await this.httpClient.get(`${this.steamHost}/market/getbuyorderstatus/?sessionid=${sessionId}&buy_orderid=${orderId}`); - if (resultRaw.success !== 1) { - throw new Error("Can't get order status at Steam :("); - } + if (resultRaw.success !== 1) { + throw new Error("Can't get order status at Steam :("); + } - return { - success: resultRaw.success === 1, - active: resultRaw.active === 1, - purchased: resultRaw.purchased, - quantity: parseInt(resultRaw.quantity), - quantityRemaining: parseInt(resultRaw.quantity_remaining) - }; - } + return { + success: resultRaw.success === 1, + active: resultRaw.active === 1, + purchased: resultRaw.purchased, + quantity: parseInt(resultRaw.quantity), + quantityRemaining: parseInt(resultRaw.quantity_remaining) + }; + } - public async cancelOrder(sessionId: string, orderId: string): Promise { + public async cancelOrder(sessionId: string, orderId: string): Promise { - const data = new URLSearchParams(); - data.append("sessionid", sessionId); - data.append("buy_orderid", orderId); + const data = new URLSearchParams(); + data.append("sessionid", sessionId); + data.append("buy_orderid", orderId); - const result = await this.httpClient.post(`${this.steamHost}/market/cancelbuyorder/`, data); + const result = await this.httpClient.post(`${this.steamHost}/market/cancelbuyorder/`, data); - if (result.success !== 1) { - throw new Error("Can't cancel order at Steam :("); + if (result.success !== 1) { + throw new Error("Can't cancel order at Steam :("); + } } - } } diff --git a/src/api/steam/exception/NeedConfirmationStreamApiException.ts b/src/api/steam/exception/NeedConfirmationStreamApiException.ts new file mode 100644 index 0000000..80f18ac --- /dev/null +++ b/src/api/steam/exception/NeedConfirmationStreamApiException.ts @@ -0,0 +1,13 @@ +import StreamApiException from "./StreamApiException"; + +class NeedConfirmationStreamApiException extends StreamApiException { + + readonly confirmationId: string; + + constructor(confirmationId: string) { + super(`You need to confirm your order (${confirmationId}).`); + this.confirmationId = confirmationId; + } +} + +export default NeedConfirmationStreamApiException; \ No newline at end of file diff --git a/src/api/steam/exception/StreamApiException.ts b/src/api/steam/exception/StreamApiException.ts new file mode 100644 index 0000000..283cec3 --- /dev/null +++ b/src/api/steam/exception/StreamApiException.ts @@ -0,0 +1,8 @@ +class StreamApiException extends Error { + + constructor(message: string) { + super(message); + } +} + +export default StreamApiException; \ No newline at end of file diff --git a/src/component/http/AxiosHttpClient.ts b/src/component/http/AxiosHttpClient.ts index 3f3ab1b..f496141 100644 --- a/src/component/http/AxiosHttpClient.ts +++ b/src/component/http/AxiosHttpClient.ts @@ -1,77 +1,77 @@ -import HttpClient, { HttpClientOptions } from "./HttpClient"; -import { Axios, AxiosRequestConfig } from "axios"; +import HttpClient, {HttpClientOptions} from "./HttpClient"; +import {Axios, AxiosRequestConfig} from "axios"; class AxiosHttpClient implements HttpClient { - private readonly axiosInstance; - - constructor() { - this.axiosInstance = new Axios({ - withCredentials: true, - transformResponse: [function transformResponse(data, headers) { - if (headers?.["content-type"].includes("text/html")) { - const root = document.createElement("html"); - root.innerHTML = data; - return root; - } else { - return JSON.parse(data); - } - }] - }); - } - - - async get(url: string, options?: HttpClientOptions): Promise { - return this.executeRequest({ - url: url, - method: "GET" - }, options); - } - - post(url: string, data?: D, options?: HttpClientOptions): Promise { - return this.executeRequest({ - url: url, - method: "POST", - data: data - }, options); - } - - - private async executeRequest(configuration: AxiosRequestConfig, options?: HttpClientOptions): Promise { - const result = await this.axiosInstance.request(configuration); - let resultData = result.data; - - - try { - if (options?.transformer) { - resultData = options.transformer(resultData); - } - } catch (err) { - return this.processRetries(configuration, options); + private readonly axiosInstance; + + constructor() { + this.axiosInstance = new Axios({ + withCredentials: true, + transformResponse: [function transformResponse(data, headers) { + if (headers?.["content-type"].includes("text/html")) { + const root = document.createElement("html"); + root.innerHTML = data; + return root; + } else { + return JSON.parse(data); + } + }] + }); + } + + + async get(url: string, options?: HttpClientOptions): Promise { + return this.executeRequest({ + url: url, + method: "GET" + }, options); } - if (options?.retries !== undefined && options.retries.needRetry(resultData)) { - return this.processRetries(configuration, options); + post(url: string, data?: D, options?: HttpClientOptions): Promise { + return this.executeRequest({ + url: url, + method: "POST", + data: data + }, options); } - return await new Promise((resolve) => { - return resolve(resultData); - }); - } - private async processRetries(configuration: AxiosRequestConfig, options?: HttpClientOptions): Promise { - if (options?.retries === undefined) { - throw new Error("Can't process request"); + private async executeRequest(configuration: AxiosRequestConfig, options?: HttpClientOptions): Promise { + const result = await this.axiosInstance.request(configuration); + let resultData = result.data; + + + try { + if (options?.transformer) { + resultData = options.transformer(resultData); + } + } catch (err) { + return this.processRetries(configuration, options, err); + } + + if (options?.retries !== undefined && options.retries.needRetry(resultData)) { + return this.processRetries(configuration, options); + } + + return await new Promise((resolve) => { + return resolve(resultData); + }); } - if (options.retries.count > 0) { - options.retries.count -= 1; - console.log(`Request ${configuration.url}, was not successfully, retry ${options.retries.count}`); - await new Promise(s => setTimeout(s, 1500)); - return await this.executeRequest(configuration, options); + private async processRetries(configuration: AxiosRequestConfig, options?: HttpClientOptions, err?: any): Promise { + if (options?.retries === undefined) { + throw new Error("Can't process request"); + } + + if (options.retries.count > 0) { + options.retries.count -= 1; + console.warn(`Request ${configuration.url}, was not successfully (${err?.message}), retry ${options.retries.count}`); + await new Promise(s => setTimeout(s, 1500)); + return await this.executeRequest(configuration, options); + } + throw new Error("Retries was end."); } - throw new Error("Retries was end."); - } } diff --git a/src/service/SteamCardTraderProcess.ts b/src/service/SteamCardTraderProcess.ts index 124cf88..fe83b53 100644 --- a/src/service/SteamCardTraderProcess.ts +++ b/src/service/SteamCardTraderProcess.ts @@ -1,80 +1,81 @@ import SteamMarketApi from "../api/steam/SteamMarketApi"; export interface CardOrderOperationContext { - orderId: string, - quantity: number + orderId: string, + quantity: number } export enum Status { - pending, - finished, - error + pending, + need_confirmation, + finished, + error } class SteamCardTraderProcess { - private readonly steamApi: SteamMarketApi; - private readonly cardOrderOperationContexts: Array; - private readonly interval: NodeJS.Timer; - private readonly sessionId: string; + private readonly steamApi: SteamMarketApi; + private readonly cardOrderOperationContexts: Array; + private readonly interval: NodeJS.Timer; + private readonly sessionId: string; - private currentStatus: Status = Status.pending; + private currentStatus: Status = Status.pending; - constructor(steamApi: SteamMarketApi, sessionId: string, cardOrderOperationContexts: Array) { - this.steamApi = steamApi; - this.sessionId = sessionId; - this.cardOrderOperationContexts = cardOrderOperationContexts; + constructor(steamApi: SteamMarketApi, sessionId: string, cardOrderOperationContexts: Array) { + this.steamApi = steamApi; + this.sessionId = sessionId; + this.cardOrderOperationContexts = cardOrderOperationContexts; - const self = this; - this.interval = setInterval(async () => { - await this.checkOrders(self); - }, 3000); - } - - private async checkOrders(self: SteamCardTraderProcess) { - if (self.cardOrderOperationContexts.length === 0) { - console.log("Terminate task"); - clearInterval(self.interval); - this.currentStatus = Status.error; + const self = this; + this.interval = setInterval(async () => { + await this.checkOrders(self); + }, 3000); } - for (let context of self.cardOrderOperationContexts) { - const currentStatus = await self.steamApi.getOrderStatus(self.sessionId, context.orderId); - if (!currentStatus.active || currentStatus.purchased === context.quantity) { - this.deleteOrder(context.orderId); - } - console.log(currentStatus.purchased); - } + private async checkOrders(self: SteamCardTraderProcess) { + if (self.cardOrderOperationContexts.length === 0) { + console.log("Terminate task"); + clearInterval(self.interval); + this.currentStatus = Status.error; + } - if (self.cardOrderOperationContexts.length === 0) { - console.log("Finished task"); - clearInterval(self.interval); - this.currentStatus = Status.finished; + for (let context of self.cardOrderOperationContexts) { + const currentStatus = await self.steamApi.getOrderStatus(self.sessionId, context.orderId); + if (!currentStatus.active || currentStatus.purchased === context.quantity) { + this.deleteOrder(context.orderId); + } + console.log(currentStatus.purchased); + } + + if (self.cardOrderOperationContexts.length === 0) { + console.log("Finished task"); + clearInterval(self.interval); + this.currentStatus = Status.finished; + } } - } - public getCurrentStatus(): Status { - return this.currentStatus; - } + public getCurrentStatus(): Status { + return this.currentStatus; + } - public async cancel() { - for (let context of this.cardOrderOperationContexts) { - await this.steamApi.cancelOrder(this.sessionId, context.orderId); - this.deleteOrder(context.orderId); + public async cancel() { + for (let context of this.cardOrderOperationContexts) { + await this.steamApi.cancelOrder(this.sessionId, context.orderId); + this.deleteOrder(context.orderId); + } } - } - private deleteOrder(orderId: string) { - const result = this.cardOrderOperationContexts.find(context => { - return context.orderId === orderId; - }); - if (result !== undefined) { - console.log(`delete order ${orderId}`); - this.cardOrderOperationContexts.splice( - this.cardOrderOperationContexts.indexOf(result), 1 - ); + private deleteOrder(orderId: string) { + const result = this.cardOrderOperationContexts.find(context => { + return context.orderId === orderId; + }); + if (result !== undefined) { + console.log(`delete order ${orderId}`); + this.cardOrderOperationContexts.splice( + this.cardOrderOperationContexts.indexOf(result), 1 + ); + } } - } } export default SteamCardTraderProcess; \ No newline at end of file diff --git a/src/service/SteamCardTraderService.ts b/src/service/SteamCardTraderService.ts index b9f9bd4..9018990 100644 --- a/src/service/SteamCardTraderService.ts +++ b/src/service/SteamCardTraderService.ts @@ -1,62 +1,58 @@ -import SteamCardTraderProcess, { CardOrderOperationContext } from "./SteamCardTraderProcess"; -import { CardMarketPosition } from "../steam/pages/component/CardBuyerTable"; +import SteamCardTraderProcess, {CardOrderOperationContext} from "./SteamCardTraderProcess"; +import {CardMarketPosition} from "../steam/pages/component/CardBuyerTable"; import Cookies from "js-cookie"; -import { inject, injectable } from "tsyringe"; +import {inject, injectable} from "tsyringe"; import SteamMarketApi from "../api/steam/SteamMarketApi"; -import { Tokens } from "../configuration/Injector"; +import {Tokens} from "../configuration/Injector"; -interface TradeOptions { - -} @injectable() class SteamCardTraderService { - private readonly steamMarketApi: SteamMarketApi; - - constructor(@inject(Tokens.STEAM_MARKET_API) steamMarketApi: SteamMarketApi) { - this.steamMarketApi = steamMarketApi; - } + private readonly steamMarketApi: SteamMarketApi; + constructor(@inject(Tokens.STEAM_MARKET_API) steamMarketApi: SteamMarketApi) { + this.steamMarketApi = steamMarketApi; + } - public async createTrader(positions: Array, currencyId: number): Promise { - const sessionId = this.getSessionId(); - const cardOrderOperationContexts: Array = []; + public async createTrader(positions: Array, currencyId: number): Promise { + const sessionId = this.getSessionId(); - for (const position of positions) { + const cardOrderOperationContexts: Array = []; - const result = await this.createOrder(sessionId, position, currencyId); - cardOrderOperationContexts.push( - { - orderId: result, - quantity: position.quantity + for (const position of positions) { + const result = await this.createOrder(sessionId, position, currencyId); + cardOrderOperationContexts.push( + { + orderId: result, + quantity: position.quantity + } + ); } - ); + return new SteamCardTraderProcess(this.steamMarketApi, sessionId, cardOrderOperationContexts); } - return new SteamCardTraderProcess(this.steamMarketApi, sessionId, cardOrderOperationContexts); - } - private async createOrder(sessionId: string, position: CardMarketPosition, currencyId: number): Promise { - const maximumOverprice = 200;// TODO 200 is maximum overprice for position, move to options. + private async createOrder(sessionId: string, position: CardMarketPosition, currencyId: number): Promise { + const maximumOverprice = 200;// TODO 200 is maximum overprice for position, move to options. - return await this.steamMarketApi.createOrder({ - sessionId: sessionId, - currency: currencyId, - appId: position.appId, - marketHashName: position.hashName, - priceTotal: (position.price + maximumOverprice) * position.quantity, - quantity: position.quantity - }); - } + return await this.steamMarketApi.createOrder({ + sessionId: sessionId, + currency: currencyId, + appId: position.appId, + marketHashName: position.hashName, + priceTotal: (position.price + maximumOverprice) * position.quantity, + quantity: position.quantity + }); + } - private getSessionId(): string { - const sessionId = Cookies.get("sessionid"); - if (sessionId === undefined) { - throw new Error("Session id is not initialized"); + private getSessionId(): string { + const sessionId = Cookies.get("sessionid"); + if (sessionId === undefined) { + throw new Error("Session id is not initialized"); + } + return sessionId; } - return sessionId; - } } diff --git a/src/steam/pages/CardMarketPage.ts b/src/steam/pages/CardMarketPage.ts index 3832380..fceb5a5 100644 --- a/src/steam/pages/CardMarketPage.ts +++ b/src/steam/pages/CardMarketPage.ts @@ -17,7 +17,7 @@ class CardMarketPage extends SteamPage { private static readonly configuration: Array = [ { name: Components.Table, - selector: "#BG_bottom > table", + selector: "#multibuy_ctn > table", component: new ComponentLoader(CardBuyerTable) }, { diff --git a/src/steam/pages/SteamPageLoader.ts b/src/steam/pages/SteamPageLoader.ts index 1c3989a..fda2d33 100644 --- a/src/steam/pages/SteamPageLoader.ts +++ b/src/steam/pages/SteamPageLoader.ts @@ -17,7 +17,6 @@ class SteamPageLoader { } public async loadGameCard(steamId: string, appId: number): Promise { - console.log(`${this.host}/profiles/${steamId}/gamecards/${appId}`) return await this.httpClient.get(`${this.host}/profiles/${steamId}/gamecards/${appId}`, { retries: { count: 4, diff --git a/src/ui/react/component/Badge.tsx b/src/ui/react/component/Badge.tsx index 25c8cd9..5ad638d 100644 --- a/src/ui/react/component/Badge.tsx +++ b/src/ui/react/component/Badge.tsx @@ -6,6 +6,7 @@ import { injector } from "../../../configuration/Injector"; import SteamCardTraderService from "../../../service/SteamCardTraderService"; import { CardMarketPosition } from "../../../steam/pages/component/CardBuyerTable"; import { Status } from "../../../service/SteamCardTraderProcess"; +import NeedConfirmationStreamApiException from "../../../api/steam/exception/NeedConfirmationStreamApiException"; interface BadgeProperties { steamId: string, @@ -17,6 +18,7 @@ enum BadgeStatus { Waiting, Processing, PriceLoaded, + NeedConfirmation = 4, Completed, Error } @@ -98,13 +100,20 @@ Items will be purchased at the cheapest price available, so the order may end up ); case BadgeStatus.PriceLoaded: return ( - PLACE ORDER ); case BadgeStatus.Completed: return ( ✔️ ); + case BadgeStatus.NeedConfirmation: + return ( + + Check Steam App, try again + + ); case BadgeStatus.Error: return ( @@ -163,8 +172,12 @@ Items will be purchased at the cheapest price available, so the order may end up } }, 1000); }).catch(error => { - console.error(error); - this.setState({ status: BadgeStatus.Error }); + if (error instanceof NeedConfirmationStreamApiException){ + this.setState({ status: BadgeStatus.NeedConfirmation }); + }else { + console.error(error); + this.setState({status: BadgeStatus.Error}); + } }); }