diff --git a/README.md b/README.md index a223e39c0..4d080e63f 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ A free and open source fully automated TF2 trading bot advertising on www.backpa ![License](https://img.shields.io/github/license/idinium96/tf2autobot) **TF2Autobot made by IdiNium** -[![profile](https://user-images.githubusercontent.com/47635037/100916111-4945eb80-3510-11eb-8036-86c032cb6627.png)](https://backpack.tf/profiles/76561198013127982) +[![profile](https://user-images.githubusercontent.com/47635037/112201149-edcc7480-8c4a-11eb-9756-fcf1509a74d0.png)](https://backpack.tf/profiles/76561198013127982) Before you install the bot, there are a few things you will need: @@ -45,7 +45,7 @@ If you run into trouble when running the bot, make sure to head to [Common Error Join the [TF2Autobot Discord server](https://discord.gg/D2GNnp7tv8) and head over to [`#๐Ÿ†šroles`](https://discordapp.com/channels/664971400678998016/719391430669500447/771188962550611988) channel and react to the first message to get notified whenever an update has been released! -
update-noti
+
update-noti
\*\*There are also giveaways held every Thursday, 8 AM - 8 PM (12 hours) Malaysia time! diff --git a/package-lock.json b/package-lock.json index d6944d30f..139548a8d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1809,9 +1809,9 @@ "dev": true }, "@types/cheerio": { - "version": "0.22.27", - "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.27.tgz", - "integrity": "sha512-UpmYZewEWNEE6Ya24RzAQ2X2OYwz32AaLyzYinpM8qqFGRyYufqKSvxPjjZkvS+h16bajfXl7VojrAxWzG/+mA==", + "version": "0.22.28", + "resolved": "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.28.tgz", + "integrity": "sha512-ehUMGSW5IeDxJjbru4awKYMlKGmo1wSSGUVqXtYwlgmUM8X1a0PZttEIm6yEY7vHsY/hh6iPnklF213G0UColw==", "dev": true, "requires": { "@types/node": "*" @@ -2038,13 +2038,13 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.18.0.tgz", - "integrity": "sha512-Lzkc/2+7EoH7+NjIWLS2lVuKKqbEmJhtXe3rmfA8cyiKnZm3IfLf51irnBcmow8Q/AptVV0XBZmBJKuUJTe6cQ==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.19.0.tgz", + "integrity": "sha512-CRQNQ0mC2Pa7VLwKFbrGVTArfdVDdefS+gTw0oC98vSI98IX5A8EVH4BzJ2FOB0YlCmm8Im36Elad/Jgtvveaw==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "4.18.0", - "@typescript-eslint/scope-manager": "4.18.0", + "@typescript-eslint/experimental-utils": "4.19.0", + "@typescript-eslint/scope-manager": "4.19.0", "debug": "^4.1.1", "functional-red-black-tree": "^1.0.1", "lodash": "^4.17.15", @@ -2054,43 +2054,43 @@ }, "dependencies": { "@typescript-eslint/experimental-utils": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.18.0.tgz", - "integrity": "sha512-92h723Kblt9JcT2RRY3QS2xefFKar4ZQFVs3GityOKWQYgtajxt/tuXIzL7sVCUlM1hgreiV5gkGYyBpdOwO6A==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.19.0.tgz", + "integrity": "sha512-9/23F1nnyzbHKuoTqFN1iXwN3bvOm/PRIXSBR3qFAYotK/0LveEOHr5JT1WZSzcD6BESl8kPOG3OoDRKO84bHA==", "dev": true, "requires": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/scope-manager": "4.18.0", - "@typescript-eslint/types": "4.18.0", - "@typescript-eslint/typescript-estree": "4.18.0", + "@typescript-eslint/scope-manager": "4.19.0", + "@typescript-eslint/types": "4.19.0", + "@typescript-eslint/typescript-estree": "4.19.0", "eslint-scope": "^5.0.0", "eslint-utils": "^2.0.0" } }, "@typescript-eslint/scope-manager": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.18.0.tgz", - "integrity": "sha512-olX4yN6rvHR2eyFOcb6E4vmhDPsfdMyfQ3qR+oQNkAv8emKKlfxTWUXU5Mqxs2Fwe3Pf1BoPvrwZtwngxDzYzQ==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.19.0.tgz", + "integrity": "sha512-GGy4Ba/hLXwJXygkXqMzduqOMc+Na6LrJTZXJWVhRrSuZeXmu8TAnniQVKgj8uTRKe4igO2ysYzH+Np879G75g==", "dev": true, "requires": { - "@typescript-eslint/types": "4.18.0", - "@typescript-eslint/visitor-keys": "4.18.0" + "@typescript-eslint/types": "4.19.0", + "@typescript-eslint/visitor-keys": "4.19.0" } }, "@typescript-eslint/types": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.18.0.tgz", - "integrity": "sha512-/BRociARpj5E+9yQ7cwCF/SNOWwXJ3qhjurMuK2hIFUbr9vTuDeu476Zpu+ptxY2kSxUHDGLLKy+qGq2sOg37A==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.19.0.tgz", + "integrity": "sha512-A4iAlexVvd4IBsSTNxdvdepW0D4uR/fwxDrKUa+iEY9UWvGREu2ZyB8ylTENM1SH8F7bVC9ac9+si3LWNxcBuA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.18.0.tgz", - "integrity": "sha512-wt4xvF6vvJI7epz+rEqxmoNQ4ZADArGQO9gDU+cM0U5fdVv7N+IAuVoVAoZSOZxzGHBfvE3XQMLdy+scsqFfeg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.19.0.tgz", + "integrity": "sha512-3xqArJ/A62smaQYRv2ZFyTA+XxGGWmlDYrsfZG68zJeNbeqRScnhf81rUVa6QG4UgzHnXw5VnMT5cg75dQGDkA==", "dev": true, "requires": { - "@typescript-eslint/types": "4.18.0", - "@typescript-eslint/visitor-keys": "4.18.0", + "@typescript-eslint/types": "4.19.0", + "@typescript-eslint/visitor-keys": "4.19.0", "debug": "^4.1.1", "globby": "^11.0.1", "is-glob": "^4.0.1", @@ -2099,12 +2099,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.18.0.tgz", - "integrity": "sha512-Q9t90JCvfYaN0OfFUgaLqByOfz8yPeTAdotn/XYNm5q9eHax90gzdb+RJ6E9T5s97Kv/UHWKERTmqA0jTKAEHw==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.19.0.tgz", + "integrity": "sha512-aGPS6kz//j7XLSlgpzU2SeTqHPsmRYxFztj2vPuMMFJXZudpRSehE3WCV+BaxwZFvfAqMoSd86TEuM0PQ59E/A==", "dev": true, "requires": { - "@typescript-eslint/types": "4.18.0", + "@typescript-eslint/types": "4.19.0", "eslint-visitor-keys": "^2.0.0" } }, @@ -2115,9 +2115,9 @@ "dev": true }, "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -2140,41 +2140,41 @@ } }, "@typescript-eslint/parser": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.18.0.tgz", - "integrity": "sha512-W3z5S0ZbecwX3PhJEAnq4mnjK5JJXvXUDBYIYGoweCyWyuvAKfGHvzmpUzgB5L4cRBb+cTu9U/ro66dx7dIimA==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.19.0.tgz", + "integrity": "sha512-/uabZjo2ZZhm66rdAu21HA8nQebl3lAIDcybUoOxoI7VbZBYavLIwtOOmykKCJy+Xq6Vw6ugkiwn8Js7D6wieA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "4.18.0", - "@typescript-eslint/types": "4.18.0", - "@typescript-eslint/typescript-estree": "4.18.0", + "@typescript-eslint/scope-manager": "4.19.0", + "@typescript-eslint/types": "4.19.0", + "@typescript-eslint/typescript-estree": "4.19.0", "debug": "^4.1.1" }, "dependencies": { "@typescript-eslint/scope-manager": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.18.0.tgz", - "integrity": "sha512-olX4yN6rvHR2eyFOcb6E4vmhDPsfdMyfQ3qR+oQNkAv8emKKlfxTWUXU5Mqxs2Fwe3Pf1BoPvrwZtwngxDzYzQ==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.19.0.tgz", + "integrity": "sha512-GGy4Ba/hLXwJXygkXqMzduqOMc+Na6LrJTZXJWVhRrSuZeXmu8TAnniQVKgj8uTRKe4igO2ysYzH+Np879G75g==", "dev": true, "requires": { - "@typescript-eslint/types": "4.18.0", - "@typescript-eslint/visitor-keys": "4.18.0" + "@typescript-eslint/types": "4.19.0", + "@typescript-eslint/visitor-keys": "4.19.0" } }, "@typescript-eslint/types": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.18.0.tgz", - "integrity": "sha512-/BRociARpj5E+9yQ7cwCF/SNOWwXJ3qhjurMuK2hIFUbr9vTuDeu476Zpu+ptxY2kSxUHDGLLKy+qGq2sOg37A==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.19.0.tgz", + "integrity": "sha512-A4iAlexVvd4IBsSTNxdvdepW0D4uR/fwxDrKUa+iEY9UWvGREu2ZyB8ylTENM1SH8F7bVC9ac9+si3LWNxcBuA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.18.0.tgz", - "integrity": "sha512-wt4xvF6vvJI7epz+rEqxmoNQ4ZADArGQO9gDU+cM0U5fdVv7N+IAuVoVAoZSOZxzGHBfvE3XQMLdy+scsqFfeg==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.19.0.tgz", + "integrity": "sha512-3xqArJ/A62smaQYRv2ZFyTA+XxGGWmlDYrsfZG68zJeNbeqRScnhf81rUVa6QG4UgzHnXw5VnMT5cg75dQGDkA==", "dev": true, "requires": { - "@typescript-eslint/types": "4.18.0", - "@typescript-eslint/visitor-keys": "4.18.0", + "@typescript-eslint/types": "4.19.0", + "@typescript-eslint/visitor-keys": "4.19.0", "debug": "^4.1.1", "globby": "^11.0.1", "is-glob": "^4.0.1", @@ -2183,12 +2183,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.18.0.tgz", - "integrity": "sha512-Q9t90JCvfYaN0OfFUgaLqByOfz8yPeTAdotn/XYNm5q9eHax90gzdb+RJ6E9T5s97Kv/UHWKERTmqA0jTKAEHw==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.19.0.tgz", + "integrity": "sha512-aGPS6kz//j7XLSlgpzU2SeTqHPsmRYxFztj2vPuMMFJXZudpRSehE3WCV+BaxwZFvfAqMoSd86TEuM0PQ59E/A==", "dev": true, "requires": { - "@typescript-eslint/types": "4.18.0", + "@typescript-eslint/types": "4.19.0", "eslint-visitor-keys": "^2.0.0" } }, @@ -2199,9 +2199,9 @@ "dev": true }, "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -3009,9 +3009,9 @@ } }, "bptf-listings-2": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/bptf-listings-2/-/bptf-listings-2-1.3.5.tgz", - "integrity": "sha512-fS7RnevnFXAftFrXVM2cF+vJHCWq4YyTo/kMAsFipTDivcvTKyAHwK5VZWnn2VhGlUs04AYY+KZv5NBeGq1f9w==", + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/bptf-listings-2/-/bptf-listings-2-1.3.6.tgz", + "integrity": "sha512-zL41l3KSYOCwd65khzZnjEUA3OfJME+4R6ZbS8eiXfXmw6JnJfjYijmxvdU4ay2FS5bdDHpuSpjKln+r60PL9A==", "requires": { "async": "^3.2.0", "events": "^3.2.0", @@ -10895,9 +10895,9 @@ } }, "tf2-schema-2": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/tf2-schema-2/-/tf2-schema-2-1.5.0.tgz", - "integrity": "sha512-sNhOVEZ3nYTY3QbIhxwTbAd2vgeYK1l65HdfbCk18oUjoyQe9XQYO7Bh1ywzCWZ4GHTS2c7tTBXDEDDrzwSk6A==", + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/tf2-schema-2/-/tf2-schema-2-1.5.1.tgz", + "integrity": "sha512-CFY1Mtrg9hER3sDT7toTyUNG/mulI9gQAA4lWo81quwkhhLWywKnMlPepwAu+/9xuehkToRsib3j1eitO5PuKQ==", "requires": { "async": "^3.2.0", "events": "^3.2.0", @@ -10908,15 +10908,6 @@ "vdf": "0.0.2" }, "dependencies": { - "tf2-sku-2": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tf2-sku-2/-/tf2-sku-2-1.1.1.tgz", - "integrity": "sha512-mQexJmjcogSjVJAvD/SOH9cjOUgvIPLqmSWHfpmWl+sdHYMyqMn9DbRmmb8NREnsMqteWZ5UcSO2oAutrvvvxQ==", - "requires": { - "defaults": "^1.0.3", - "object-prettify": "^1.0.0" - } - }, "util": { "version": "0.12.3", "resolved": "https://registry.npmjs.org/util/-/util-0.12.3.tgz", diff --git a/package.json b/package.json index ef78e7f4d..a388e3a2d 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "bugs": { "url": "https://github.com/TF2Autobot/tf2autobot/issues" }, - "updateMessage": "โ€ข <=v3.6.8 - Check your Node.js version. Make sure it's at least version 14 before updating your bot to v3.7.0. Manual update recommended.", + "updateMessage": "โ€ข <=v3.6.8 - Check your Node.js version. Make sure it's at least version 14 before updating your bot to the latest version. Manual update recommended.\nโ€ข v3.7.0 - Clear to update with \"!updaterepo\" command.", "homepage": "https://github.com/TF2Autobot/tf2autobot#readme", "dependencies": { "@tf2autobot/tradeoffer-manager": "^2.11.2", @@ -30,7 +30,7 @@ "bluebird": "^3.7.2", "bluebird-global": "^1.0.1", "body-parser": "^1.19.0", - "bptf-listings-2": "^1.3.5", + "bptf-listings-2": "^1.3.6", "bptf-login-2": "^1.0.2", "callback-queue": "^3.0.0", "change-case": "^4.1.2", @@ -61,7 +61,7 @@ "steamid": "^1.1.3", "tf2": "^3.0.2", "tf2-currencies-2": "^1.2.5", - "tf2-schema-2": "^1.5.0", + "tf2-schema-2": "^1.5.1", "tf2-sku-2": "^1.1.1", "url": "^0.11.0", "valid-url": "^1.0.9", @@ -74,7 +74,7 @@ "@babel/preset-typescript": "^7.13.0", "@types/async": "^3.2.5", "@types/bluebird-global": "^3.5.12", - "@types/cheerio": "^0.22.27", + "@types/cheerio": "^0.22.28", "@types/death": "^1.1.1", "@types/express": "^4.17.11", "@types/graceful-fs": "^4.1.5", @@ -85,8 +85,8 @@ "@types/semver": "^7.3.4", "@types/socket.io-client": "^1.4.34", "@types/valid-url": "^1.0.3", - "@typescript-eslint/eslint-plugin": "^4.18.0", - "@typescript-eslint/parser": "^4.18.0", + "@typescript-eslint/eslint-plugin": "^4.19.0", + "@typescript-eslint/parser": "^4.19.0", "eslint": "^7.22.0", "eslint-plugin-jest": "^24.3.2", "eslint-plugin-prettier": "^3.3.1", diff --git a/src/classes/Commands/Commands.ts b/src/classes/Commands/Commands.ts index 7b1a8bd05..c4197497a 100644 --- a/src/classes/Commands/Commands.ts +++ b/src/classes/Commands/Commands.ts @@ -59,7 +59,7 @@ export default class Commands { this.message = new c.MessageCommand(bot); this.misc = new c.MiscCommands(bot); this.opt = new c.OptionsCommand(bot); - this.pManager = new c.PricelistManager(bot); + this.pManager = new c.PricelistManager(bot, pricer); this.request = new c.RequestCommands(bot, pricer); this.review = new c.ReviewCommands(bot); this.status = new c.StatusCommands(bot); @@ -200,7 +200,7 @@ export default class Commands { this.pManager.getCommand(steamID, message); } else if (command === 'getall' && isAdmin) { void this.pManager.getAllCommand(steamID, message); - } else if (command === 'getslots' && isAdmin) { + } else if (['getslots', 'listings'].includes(command) && isAdmin) { void this.pManager.getSlotsCommand(steamID); } else if (command === 'autoadd' && isAdmin) { void this.pManager.autoAddCommand(steamID, message); diff --git a/src/classes/Commands/sub-classes/Help.ts b/src/classes/Commands/sub-classes/Help.ts index 46cbeca24..19d36d54d 100644 --- a/src/classes/Commands/sub-classes/Help.ts +++ b/src/classes/Commands/sub-classes/Help.ts @@ -34,7 +34,7 @@ export default class HelpCommands { '!shuffle - Shuffle pricelist entries.', '!get (sku|name|defindex|item)= - Get raw information about a pricelist entry', '!getAll [limit=] - Get a list of all items exist in your pricelist. Set limit=-1 to show all', - '!getSlots - Get current used listings slot per cap count.\n\nโœจ=== Bot manager ===โœจ', + '!getSlots or !listings - Get current used listings slot per cap count.\n\nโœจ=== Bot manager ===โœจ', "!expand craftable=(true|false) - Use Backpack Expanders to increase the bot's inventory limit", '!use (sku|assetid)= - Use an item (such as Gift-Stuffed Stocking 2020 - sku: 5923;6;untradable)', "!delete (sku|assetid)= - Delete an item from the bot's inventory (SKU input only) ๐Ÿšฎ", diff --git a/src/classes/Commands/sub-classes/PricelistManager.ts b/src/classes/Commands/sub-classes/PricelistManager.ts index 99be1ee52..0bf3a255b 100644 --- a/src/classes/Commands/sub-classes/PricelistManager.ts +++ b/src/classes/Commands/sub-classes/PricelistManager.ts @@ -11,6 +11,7 @@ import { removeLinkProtocol, testSKU, getItemFromParams, fixSKU } from '../funct import Bot from '../../Bot'; import CommandParser from '../../CommandParser'; import { Entry, EntryData, PricelistChangedSource } from '../../Pricelist'; +import Pricer, { RequestCheckResponse, RequestCheckFn } from '../../Pricer'; import validator from '../../../lib/validator'; import log from '../../../lib/logger'; @@ -25,12 +26,15 @@ export default class PricelistManagerCommands { private executeTimeout: NodeJS.Timeout; + private requestCheck: RequestCheckFn; + stopAutoAddCommand(): void { this.stopAutoAdd = true; } - constructor(private readonly bot: Bot) { + constructor(private readonly bot: Bot, private priceSource: Pricer) { this.bot = bot; + this.requestCheck = this.priceSource.requestCheck.bind(this.priceSource); } addCommand(steamID: SteamID, message: string): void { @@ -40,14 +44,14 @@ export default class PricelistManagerCommands { params.enabled = true; } - if (params.max === undefined) { - params.max = 1; - } - if (params.min === undefined) { params.min = 0; } + if (params.max === undefined) { + params.max = 1; + } + if (params.intent === undefined) { params.intent = 2; } else if (typeof params.intent === 'string') { @@ -130,10 +134,10 @@ export default class PricelistManagerCommands { if (params.group && typeof params.group !== 'string') { // if group parameter is defined, convert anything to string - params.group = (params.group as string).toString(); + params.group = String(params.group); } - if (typeof params.group === undefined) { + if (params.group === undefined) { // If group parameter is not defined, set it to null. params['group'] = 'all'; } @@ -165,6 +169,22 @@ export default class PricelistManagerCommands { steamID, `โœ… Added "${entry.name}" (${entry.sku})` + this.generateAddedReply(isPremium, entry) ); + + void this.requestCheck(params.sku, 'bptf').asCallback( + (err: ErrorRequest, body: RequestCheckResponse) => { + if (err) { + log.debug(`โŒ Failed to request pricecheck for ${entry.sku}: ${JSON.stringify(err)}`); + } + + if (!body) { + log.debug( + `โŒ Error while requesting price check for ${entry.sku} (returned null/undefined).` + ); + } else { + log.debug(`โœ… Requested pricecheck for ${body.name} (${entry.sku}).`); + } + } + ); }) .catch(err => { this.bot.sendMessage(steamID, `โŒ Failed to add the item to the pricelist: ${(err as Error).message}`); @@ -206,14 +226,14 @@ export default class PricelistManagerCommands { params.enabled = true; } - if (params.max === undefined) { - params.max = 1; - } - if (params.min === undefined) { params.min = 0; } + if (params.max === undefined) { + params.max = 1; + } + if (params.intent === undefined) { params.intent = 2; } else if (typeof params.intent === 'string') { @@ -231,7 +251,13 @@ export default class PricelistManagerCommands { if (params.autoprice === undefined) { params.autoprice = false; } + } else if (typeof params.buy !== 'object' && typeof params.sell === 'object') { + params['buy'] = { + keys: 0, + metal: 0 + }; } + if (typeof params.sell === 'object') { params.sell.keys = params.sell.keys || 0; params.sell.metal = params.sell.metal || 0; @@ -239,6 +265,11 @@ export default class PricelistManagerCommands { if (params.autoprice === undefined) { params.autoprice = false; } + } else if (typeof params.sell !== 'object' && typeof params.buy === 'object') { + params['sell'] = { + keys: 0, + metal: 0 + }; } if ( @@ -295,10 +326,10 @@ export default class PricelistManagerCommands { if (params.group && typeof params.group !== 'string') { // if group parameter is defined, convert anything to string - params.group = (params.group as string).toString(); + params.group = String(params.group); } - if (typeof params.group === undefined) { + if (params.group === undefined) { // If group parameter is not defined, set it to null. params['group'] = 'all'; } @@ -397,6 +428,22 @@ export default class PricelistManagerCommands { total - added - skipped - failed } remaining` ); + + void this.requestCheck(params.sku, 'bptf').asCallback( + (err: ErrorRequest, body: RequestCheckResponse) => { + if (err) { + log.debug(`โŒ Failed to request pricecheck for ${entry.sku}: ${JSON.stringify(err)}`); + } + + if (!body) { + log.debug( + `โŒ Error while requesting price check for ${entry.sku} (returned null/undefined).` + ); + } else { + log.debug(`โœ… Requested pricecheck for ${body.name} (${entry.sku}).`); + } + } + ); }) .catch(err => { failed++; @@ -559,16 +606,16 @@ export default class PricelistManagerCommands { } newPricelist.forEach((entry, i) => { - if (params.intent || params.intent === 0) { + if (typeof params.intent === 'number') { entry.intent = params.intent as 0 | 1 | 2; } - if (params.min === 0 || typeof params.min === 'number') { - entry.min = params.min as number; + if (typeof params.min === 'number') { + entry.min = params.min; } - if (params.max === 0 || typeof params.max === 'number') { - entry.max = params.max as number; + if (typeof params.max === 'number') { + entry.max = params.max; } if (typeof params.enabled === 'boolean') { @@ -576,22 +623,25 @@ export default class PricelistManagerCommands { } if (params.group) { - entry.group = (params.group as string).toString(); + entry.group = String(params.group); } - if (params.removenote && typeof params.removenote === 'boolean' && params.removenote === true) { + if (params.removenote === true) { // Sending "!update all=true&removenote=true" will set both // note.buy and note.sell for entire/withgroup entries to null. - if (entry.note) { - entry.note.buy = null; - entry.note.sell = null; - } + entry.note.buy = null; + entry.note.sell = null; + } + + if (params.resetgroup === true) { + entry.group = 'all'; } if (typeof params.autoprice === 'boolean') { if (params.autoprice === false) { entry.time = null; } + entry.autoprice = params.autoprice; } @@ -602,7 +652,7 @@ export default class PricelistManagerCommands { entry.note.sell = params.note.sell || entry.note.sell; } - if (typeof params.buy === 'object' && params.buy !== null) { + if (typeof params.buy === 'object') { entry.buy.keys = params.buy.keys || 0; entry.buy.metal = params.buy.metal || 0; @@ -611,7 +661,7 @@ export default class PricelistManagerCommands { } } - if (typeof params.sell === 'object' && params.sell !== null) { + if (typeof params.sell === 'object') { entry.sell.keys = params.sell.keys || 0; entry.sell.metal = params.sell.metal || 0; @@ -627,8 +677,8 @@ export default class PricelistManagerCommands { sku: entry.sku, enabled: entry.enabled, intent: entry.intent, - max: entry.max, min: entry.min, + max: entry.max, autoprice: entry.autoprice, buy: entry.buy.toJSON(), sell: entry.sell.toJSON(), @@ -650,6 +700,10 @@ export default class PricelistManagerCommands { delete params.removenote; } + if (params.resetgroup) { + delete params.resetgroup; + } + if (params.withgroup) { newPricelist = unTargetedPricelist.concat(newPricelist); delete params.withgroup; @@ -687,24 +741,6 @@ export default class PricelistManagerCommands { return this.bot.sendMessage(steamID, `โŒ "sku" should not be empty or wrong format.`); } - if (typeof params.buy === 'object' && params.buy !== null) { - params.buy.keys = params.buy.keys || 0; - params.buy.metal = params.buy.metal || 0; - - if (params.autoprice === undefined) { - params.autoprice = false; - } - } - - if (typeof params.sell === 'object' && params.sell !== null) { - params.sell.keys = params.sell.keys || 0; - params.sell.metal = params.sell.metal || 0; - - if (params.autoprice === undefined) { - params.autoprice = false; - } - } - if (params.resetgroup) { // if resetgroup (when sending "!update item=&resetgroup=true") is defined, // first check if it's a booelan type @@ -800,6 +836,34 @@ export default class PricelistManagerCommands { const itemEntry = this.bot.pricelist.getPrice(params.sku as string, false); + if (typeof params.buy === 'object') { + params.buy.keys = params.buy.keys || 0; + params.buy.metal = params.buy.metal || 0; + + if (params.autoprice === undefined) { + params.autoprice = false; + } + } else if (typeof params.buy !== 'object' && typeof params.sell === 'object') { + params['buy'] = { + keys: itemEntry.buy.keys, + metal: itemEntry.buy.metal + }; + } + + if (typeof params.sell === 'object') { + params.sell.keys = params.sell.keys || 0; + params.sell.metal = params.sell.metal || 0; + + if (params.autoprice === undefined) { + params.autoprice = false; + } + } else if (typeof params.sell !== 'object' && typeof params.buy === 'object') { + params['sell'] = { + keys: itemEntry.sell.keys, + metal: itemEntry.sell.metal + }; + } + if (typeof params.note === 'object') { params.note.buy = params.note.buy || itemEntry.note.buy; params.note.sell = params.note.sell || itemEntry.note.sell; @@ -851,7 +915,7 @@ export default class PricelistManagerCommands { if (params.group && typeof params.group !== 'string') { // if group parameter is defined, convert anything to string - params.group = (params.group as string).toString(); + params.group = String(params.group); } const entryData = this.bot.pricelist.getPrice(params.sku as string, false).getJSON(); @@ -878,6 +942,22 @@ export default class PricelistManagerCommands { steamID, `โœ… Updated "${entry.name}" (${entry.sku})` + this.generateUpdateReply(isPremium, itemEntry, entry) ); + + void this.requestCheck(params.sku, 'bptf').asCallback( + (err: ErrorRequest, body: RequestCheckResponse) => { + if (err) { + log.debug(`โŒ Failed to request pricecheck for ${entry.sku}: ${JSON.stringify(err)}`); + } + + if (!body) { + log.debug( + `โŒ Error while requesting price check for ${entry.sku} (returned null/undefined).` + ); + } else { + log.debug(`โœ… Requested pricecheck for ${body.name} (${entry.sku}).`); + } + } + ); }) .catch((err: ErrorRequest) => { this.bot.sendMessage( @@ -1117,7 +1197,25 @@ export default class PricelistManagerCommands { this.bot.pricelist .removePrice(params.sku as string, true) - .then(entry => this.bot.sendMessage(steamID, `โœ… Removed "${entry.name}".`)) + .then(entry => { + this.bot.sendMessage(steamID, `โœ… Removed "${entry.name}".`); + + void this.requestCheck(params.sku, 'bptf').asCallback( + (err: ErrorRequest, body: RequestCheckResponse) => { + if (err) { + log.debug(`โŒ Failed to request pricecheck for ${entry.sku}: ${JSON.stringify(err)}`); + } + + if (!body) { + log.debug( + `โŒ Error while requesting price check for ${entry.sku} (returned null/undefined).` + ); + } else { + log.debug(`โœ… Requested pricecheck for ${body.name} (${entry.sku}).`); + } + } + ); + }) .catch(err => this.bot.sendMessage(steamID, `โŒ Failed to remove pricelist entry: ${(err as Error).message}`) ); diff --git a/src/classes/MyHandler/MyHandler.ts b/src/classes/MyHandler/MyHandler.ts index 50b4a593c..811f04f29 100644 --- a/src/classes/MyHandler/MyHandler.ts +++ b/src/classes/MyHandler/MyHandler.ts @@ -294,6 +294,30 @@ export default class MyHandler extends Handler { this.refreshTimeout = setTimeout(() => { this.enableAutoRefreshListings(); }, 5 * 60 * 1000); + + // Send notification to admin/Discord Webhook if there's any item failed to go through updateOldPrices + const failedToUpdateOldPrices = this.bot.pricelist.failedUpdateOldPrices; + + if (failedToUpdateOldPrices.length > 0) { + const dw = this.opt.discordWebhook.sendAlert; + const isDwEnabled = dw.enable && dw.url !== ''; + + if (this.opt.sendAlert.enable && this.opt.sendAlert.failedToUpdateOldPrices) { + if (isDwEnabled) { + sendAlert('failedToUpdateOldPrices', this.bot, '', null, null, failedToUpdateOldPrices); + } else { + this.bot.messageAdmins( + `Failed to update old prices (probably because autoprice is set to true but item does not exist` + + ` on the pricer source):\n\n${failedToUpdateOldPrices.join( + '\n' + )}\n\nAll items above has been temporarily disabled.`, + [] + ); + } + } + + this.bot.pricelist.resetFailedUpdateOldPrices = 0; + } } onShutdown(): Promise { diff --git a/src/classes/MyHandler/offer/accepted/updateListings.ts b/src/classes/MyHandler/offer/accepted/updateListings.ts index 3c46ea2b1..0a459c081 100644 --- a/src/classes/MyHandler/offer/accepted/updateListings.ts +++ b/src/classes/MyHandler/offer/accepted/updateListings.ts @@ -361,7 +361,7 @@ export default function updateListings( `${name} (${sku})\nโ–ธ ` + [ `old: ${oldPrice.buy.toString()}/${oldPrice.sell.toString()}`, - `new: ${data.buy.toString()}/${data.buy.toString()}` + `new: ${data.buy.toString()}/${data.sell.toString()}` ].join('\nโ–ธ '); log.debug(msg); diff --git a/src/classes/Options.ts b/src/classes/Options.ts index d42ae53b7..81e6ea3e3 100644 --- a/src/classes/Options.ts +++ b/src/classes/Options.ts @@ -67,7 +67,8 @@ export const DEFAULTS = { onSuccessUpdatePartialPriced: true, onFailedUpdatePartialPriced: true }, - receivedUnusualNotInPricelist: true + receivedUnusualNotInPricelist: true, + failedToUpdateOldPrices: true }, pricelist: { @@ -1034,6 +1035,7 @@ interface SendAlert extends OnlyEnable { unableToProcessOffer?: boolean; partialPrice?: PartialPrice; receivedUnusualNotInPricelist?: boolean; + failedToUpdateOldPrices?: boolean; } interface PartialPrice { diff --git a/src/classes/Pricelist.ts b/src/classes/Pricelist.ts index b422f59aa..0a69f812e 100644 --- a/src/classes/Pricelist.ts +++ b/src/classes/Pricelist.ts @@ -22,8 +22,8 @@ export interface EntryData { sku: string; enabled: boolean; autoprice: boolean; - max: number; min: number; + max: number; intent: 0 | 1 | 2; // 'buy', 'sell', 'bank' buy?: Currency | null; sell?: Currency | null; @@ -42,10 +42,10 @@ export class Entry { autoprice: boolean; - max: number; - min: number; + max: number; + intent: 0 | 1 | 2; buy: Currencies | null; @@ -65,8 +65,8 @@ export class Entry { this.name = name; this.enabled = entry.enabled; this.autoprice = entry.autoprice; - this.max = entry.max; this.min = entry.min; + this.max = entry.max; this.intent = entry.intent; // TODO: Validate entry @@ -126,8 +126,8 @@ export class Entry { sku: this.sku, enabled: this.enabled, autoprice: this.autoprice, - max: this.max, min: this.min, + max: this.max, intent: this.intent, buy: this.buy === null ? null : this.buy.toJSON(), sell: this.sell === null ? null : this.sell.toJSON(), @@ -176,6 +176,12 @@ export default class Pricelist extends EventEmitter { private retryGetKeyPrices: NodeJS.Timeout; + failedUpdateOldPrices: string[] = []; + + set resetFailedUpdateOldPrices(value: number) { + this.failedUpdateOldPrices.length = value; + } + constructor( private readonly priceSource: Pricer, private readonly schema: SchemaManager.Schema, @@ -539,8 +545,8 @@ export default class Pricelist extends EventEmitter { sku: prices[0].sku, enabled: prices[0].enabled, intent: prices[0].intent, - max: prices[0].max, min: prices[0].min, + max: prices[0].max, autoprice: prices[0].autoprice, buy: prices[0].buy, sell: prices[0].sell, @@ -767,7 +773,21 @@ export default class Pricelist extends EventEmitter { const item = SKU.fromString(sku); // Go through pricestf/custom pricer prices - const grouped = groupedPrices[item.quality][item.killstreak]; + let grouped: Item[]; + try { + grouped = groupedPrices[item.quality][item.killstreak]; + } catch (err) { + if (currPrice.group !== 'failed-updateOldPrices') { + currPrice.enabled = false; + currPrice.group = 'failed-updateOldPrices'; + this.failedUpdateOldPrices.push(sku); + log.warn(`updateOldPrices failed for ${sku}`, err); + pricesChanged = true; + } + + continue; + } + const groupedCount = grouped.length; for (let j = 0; j < groupedCount; j++) { @@ -794,10 +814,10 @@ export default class Pricelist extends EventEmitter { // if optPartialUpdate.enable is true and the item is currently in stock // and difference between latest time and time recorded in pricelist is less than threshold - const isNegativeDiff = newSellValue - currBuyingValue < 0; + const isNegativeDiff = newSellValue - currBuyingValue <= 0; if (isNegativeDiff || currPrice.group === 'isPartialPriced') { - // Only trigger this if difference of new selling price and current buying price is negative + // Only trigger this if difference of new selling price and current buying price is negative or zero // Or item group is "isPartialPriced". if (newBuyValue < currSellingValue) { @@ -964,10 +984,10 @@ export default class Pricelist extends EventEmitter { const currBuyingValue = match.buy.toValue(keyPrice); const currSellingValue = match.sell.toValue(keyPrice); - const isNegativeDiff = newSellValue - currBuyingValue < 0; + const isNegativeDiff = newSellValue - currBuyingValue <= 0; if (isNegativeDiff || match.group === 'isPartialPriced') { - // Only trigger this if difference of new selling price and current buying price is negative + // Only trigger this if difference of new selling price and current buying price is negative or zero // Or item group is "isPartialPriced". let isUpdate = false; diff --git a/src/lib/DiscordWebhook/sendAlert.ts b/src/lib/DiscordWebhook/sendAlert.ts index 5467d8bf1..b407a474e 100644 --- a/src/lib/DiscordWebhook/sendAlert.ts +++ b/src/lib/DiscordWebhook/sendAlert.ts @@ -37,7 +37,8 @@ type AlertType = | 'autoUpdatePartialPriceSuccess' | 'autoUpdatePartialPriceFailed' | 'isPartialPriced' - | 'unusualInvalidItems'; + | 'unusualInvalidItems' + | 'failedToUpdateOldPrices'; export default function sendAlert( type: AlertType, @@ -119,7 +120,7 @@ export default function sendAlert( } else if (type === 'autoUpdatePartialPriceSuccess') { title = 'โœ… Automatically update partially priced item'; description = msg; - color = '16711680'; // red + color = '32768'; // green } else if (type === 'autoUpdatePartialPriceFailed') { title = 'Failed update item prices (Partial price update)'; description = msg; @@ -192,6 +193,12 @@ export default function sendAlert( title = 'Partial price update'; description = msg; color = '16776960'; // yellow + } else if (type === 'failedToUpdateOldPrices') { + title = 'Failed to update old prices'; + description = + `Failed to update old prices (probably because autoprice is set to true but item does not exist` + + ` on the pricer source):\n\n${items.join('\n')}\n\nAll items above has been temporarily disabled.`; + color = '16711680'; // red } else { title = 'High Valued Items'; description = `Someone is trying to take your **${items.join(', ')}** that is not in your pricelist.`; diff --git a/src/schemas/options-json/options.ts b/src/schemas/options-json/options.ts index 5220a3529..89eb66252 100644 --- a/src/schemas/options-json/options.ts +++ b/src/schemas/options-json/options.ts @@ -485,6 +485,9 @@ export const optionsSchema: jsonschema.Schema = { }, receivedUnusualNotInPricelist: { type: 'boolean' + }, + failedToUpdateOldPrices: { + type: 'boolean' } }, required: [