From 7b31ea8f00976f2a587e800c96c59c384c2be077 Mon Sep 17 00:00:00 2001 From: Alexander Chapchuk Date: Sun, 28 Aug 2022 10:25:33 +0300 Subject: [PATCH 1/8] update e --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index ce8fa10..33baf67 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,10 @@ 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 From caca097b503eb6e40bb95a7fabb6f8c552d9518a Mon Sep 17 00:00:00 2001 From: Alexander Chapchuk Date: Sun, 28 Aug 2022 11:35:38 +0300 Subject: [PATCH 2/8] Add few new tests. --- README.md | 2 +- package.json | 2 +- public/logo.png | Bin 13870 -> 0 bytes public/manifest.json | 2 +- src/__tests__/pages/SteamPageLoaderTest.ts | 83 ++++++++++++++++----- src/steam/pages/SteamPageLoader.ts | 1 - 6 files changed, 67 insertions(+), 23 deletions(-) delete mode 100644 public/logo.png diff --git a/README.md b/README.md index 33baf67..e8f8c9d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

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. diff --git a/package.json b/package.json index 55ff89a..8be4fec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "steam-enchanter", - "version": "1.1.0", + "version": "1.1.1", "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 6c713bac6fe71b0f2e349a34db01e25ad73a9992..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13870 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 wE}hR;0HjzAu=Hzh$=<^M4%m4rY diff --git a/public/manifest.json b/public/manifest.json index 4362d02..6755914 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 3, "name": "Steam Enchanter", "description": "Automating some actions for Level up your Steam account.", - "version": "1.1.0", + "version": "1.1.1", "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyPmdRVJ9XxGnV4qLbRm7pEM8kUzOa6MnJFJ43fstWn6wIktC6lWZ7Qz9oIyvGua9ytYSTaqsavQksPa8eBY8L9V+Hd6xOEap82SfT353N0P9M/ZLQsG1X/KtUijaoMxoPl5VNBz+h0tuVhNHFYjeFqtujTU1JrY+Dv4U0COQv4wiGUUfdh0QVIxLg/ELK/ltmM4xsIz6EehrowY6GGjQiT7PJPda6MwS0WaJeJa6HgvIAILISVDNMFD2CipiuHZjcxC3FQQGMabciNwiv5a3sY7wyum1zWUJb71DAJV3X0eqLuDXuCtoJm+O0HOFnhtVXeR9b4WtqYyOGk92yDuFmwIDAQAB", "action": { "default_icon": "icon.png" 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/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, From 7eb452269f20cedcd3357763d0ff6bf9cf26b3d6 Mon Sep 17 00:00:00 2001 From: Alexander Chapchuk Date: Wed, 31 Aug 2022 07:48:30 +0300 Subject: [PATCH 3/8] Add test. --- .../service/SteamCardTraderServiceTest.ts | 37 +++++++++++++++++++ src/api/steam/SteamMarketApiImpl.ts | 1 + 2 files changed, 38 insertions(+) create mode 100644 src/__tests__/service/SteamCardTraderServiceTest.ts diff --git a/src/__tests__/service/SteamCardTraderServiceTest.ts b/src/__tests__/service/SteamCardTraderServiceTest.ts new file mode 100644 index 0000000..8866122 --- /dev/null +++ b/src/__tests__/service/SteamCardTraderServiceTest.ts @@ -0,0 +1,37 @@ +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); + + 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) +}); \ No newline at end of file diff --git a/src/api/steam/SteamMarketApiImpl.ts b/src/api/steam/SteamMarketApiImpl.ts index 4bdf943..b6ae5c4 100644 --- a/src/api/steam/SteamMarketApiImpl.ts +++ b/src/api/steam/SteamMarketApiImpl.ts @@ -37,6 +37,7 @@ class SteamMarketApiImpl implements SteamMarketApi { /** * Create order at steam market. + * @return The promise of order id */ public async createOrder(request: CreateMarkerOrderRequest): Promise { const data = new URLSearchParams(); From fd12d98c1a1f63e56b5f17e5d3959fc2ac17da0d Mon Sep 17 00:00:00 2001 From: Alexander Chapchuk Date: Wed, 31 Aug 2022 07:56:02 +0300 Subject: [PATCH 4/8] Up coverage. --- .../service/SteamCardTraderServiceTest.ts | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/__tests__/service/SteamCardTraderServiceTest.ts b/src/__tests__/service/SteamCardTraderServiceTest.ts index 8866122..738b238 100644 --- a/src/__tests__/service/SteamCardTraderServiceTest.ts +++ b/src/__tests__/service/SteamCardTraderServiceTest.ts @@ -33,5 +33,22 @@ test("Create trader process.", async () => { } ], 5); - expect(process.getCurrentStatus()).toBe(Status.pending) + expect(process.getCurrentStatus()).toBe(Status.pending); +}); + +test("When session id is not initialized", async () => { + const steamApiMock = mock(); + + 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 From 09483f4f15ecf776d9562cc138ac544ea20fffcb Mon Sep 17 00:00:00 2001 From: Alexander Chapchuk Date: Wed, 31 Aug 2022 07:59:25 +0300 Subject: [PATCH 5/8] Fix. --- src/__tests__/service/SteamCardTraderServiceTest.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/__tests__/service/SteamCardTraderServiceTest.ts b/src/__tests__/service/SteamCardTraderServiceTest.ts index 738b238..6850ad1 100644 --- a/src/__tests__/service/SteamCardTraderServiceTest.ts +++ b/src/__tests__/service/SteamCardTraderServiceTest.ts @@ -9,7 +9,7 @@ test("Create trader process.", async () => { const steamApiMock = mock(); const sessionId = "12414"; - Cookies.set("sessionid", sessionId); + Cookies.set("sessionid", sessionId);//TODO side effect :( const service = new SteamCardTraderService(steamApiMock); @@ -38,7 +38,7 @@ test("Create trader process.", async () => { test("When session id is not initialized", async () => { const steamApiMock = mock(); - + Cookies.remove('sessionid') const service = new SteamCardTraderService(steamApiMock); await expect(async () => { From 09c16df75a94a79d37fc545ed4d91521ff77a40a Mon Sep 17 00:00:00 2001 From: Alexander Chapchuk Date: Wed, 5 Nov 2025 23:44:44 +0300 Subject: [PATCH 6/8] Fix #10 (#11) * Fix #10 * Rollback, --- package-lock.json | 327 ++++++++++++++++-- package.json | 2 +- public/manifest.json | 8 +- src/__tests__/SteamMarketApiTest.ts | 29 ++ .../resources/html/steamCardMarketPage.html | 2 +- src/api/steam/SteamMarketApiImpl.ts | 168 +++++---- .../NeedConfirmationStreamApiException.ts | 13 + src/api/steam/exception/StreamApiException.ts | 8 + src/component/http/AxiosHttpClient.ts | 126 +++---- src/service/SteamCardTraderProcess.ts | 111 +++--- src/service/SteamCardTraderService.ts | 78 ++--- src/steam/pages/CardMarketPage.ts | 2 +- src/ui/react/component/Badge.tsx | 19 +- 13 files changed, 632 insertions(+), 261 deletions(-) create mode 100644 src/api/steam/exception/NeedConfirmationStreamApiException.ts create mode 100644 src/api/steam/exception/StreamApiException.ts 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 8be4fec..ac73069 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "steam-enchanter", - "version": "1.1.1", + "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/manifest.json b/public/manifest.json index 6755914..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.1", + "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__/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/api/steam/SteamMarketApiImpl.ts b/src/api/steam/SteamMarketApiImpl.ts index b6ae5c4..2bc2f0f 100644 --- a/src/api/steam/SteamMarketApiImpl.ts +++ b/src/api/steam/SteamMarketApiImpl.ts @@ -1,100 +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. - * @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); + 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/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}); + } }); } From 601836fe66fc0f5285c33024bb4c57ff1831d580 Mon Sep 17 00:00:00 2001 From: Alexander bigtows Chapchuk Date: Wed, 5 Nov 2025 23:51:13 +0300 Subject: [PATCH 7/8] Update testing. --- .github/workflows/testing.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index b5bf43b..dcbefc3 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -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 From 74052a268f0e7c6153ecc8f8232a471fd84045be Mon Sep 17 00:00:00 2001 From: Alexander bigtows Chapchuk Date: Wed, 5 Nov 2025 23:53:28 +0300 Subject: [PATCH 8/8] Update CI --- .github/workflows/pre-release.yml | 4 ++-- .github/workflows/publish.yml | 4 ++-- .github/workflows/testing.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) 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 dcbefc3..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