From dce09f9b41174b73398db2838624cf07f5f8bbc9 Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Mon, 18 Jul 2022 06:40:54 +0800
Subject: [PATCH 01/18] =?UTF-8?q?=F0=9F=94=84=20bump=20@tf2autobot/filter-?=
 =?UTF-8?q?axios-error?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 package-lock.json | 14 +++++++-------
 package.json      |  2 +-
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 8bd0d4c5e..045bc2770 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,7 @@
             "dependencies": {
                 "@tf2autobot/bptf-listings": "^5.6.1",
                 "@tf2autobot/bptf-login": "^2.3.3",
-                "@tf2autobot/filter-axios-error": "^1.3.0",
+                "@tf2autobot/filter-axios-error": "^1.3.1",
                 "@tf2autobot/jsonlint": "^1.0.0",
                 "@tf2autobot/steamcommunity": "^3.45.3",
                 "@tf2autobot/tf2": "^1.2.1",
@@ -1983,9 +1983,9 @@
             }
         },
         "node_modules/@tf2autobot/filter-axios-error": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@tf2autobot/filter-axios-error/-/filter-axios-error-1.3.0.tgz",
-            "integrity": "sha512-6MPCaViEnBBjhIZWMveZmyQHPwp7SSfAKIbg9KKSDigz8YotE6Tkg1/1FhFPT3GePxhL0FT+znDssy51POuxPg=="
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/@tf2autobot/filter-axios-error/-/filter-axios-error-1.3.1.tgz",
+            "integrity": "sha512-eBP5qK1usrpRGnY59DQ/VeTzhjuG3s5U7bLuDoTcOhvcMHpCEqmi2wIxxWx0+bf6uAuoO2/ieudsXurMlYmmVg=="
         },
         "node_modules/@tf2autobot/jsonlint": {
             "version": "1.0.0",
@@ -12451,9 +12451,9 @@
             }
         },
         "@tf2autobot/filter-axios-error": {
-            "version": "1.3.0",
-            "resolved": "https://registry.npmjs.org/@tf2autobot/filter-axios-error/-/filter-axios-error-1.3.0.tgz",
-            "integrity": "sha512-6MPCaViEnBBjhIZWMveZmyQHPwp7SSfAKIbg9KKSDigz8YotE6Tkg1/1FhFPT3GePxhL0FT+znDssy51POuxPg=="
+            "version": "1.3.1",
+            "resolved": "https://registry.npmjs.org/@tf2autobot/filter-axios-error/-/filter-axios-error-1.3.1.tgz",
+            "integrity": "sha512-eBP5qK1usrpRGnY59DQ/VeTzhjuG3s5U7bLuDoTcOhvcMHpCEqmi2wIxxWx0+bf6uAuoO2/ieudsXurMlYmmVg=="
         },
         "@tf2autobot/jsonlint": {
             "version": "1.0.0",
diff --git a/package.json b/package.json
index 656bcd581..c95f657f7 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
     "dependencies": {
         "@tf2autobot/bptf-listings": "^5.6.1",
         "@tf2autobot/bptf-login": "^2.3.3",
-        "@tf2autobot/filter-axios-error": "^1.3.0",
+        "@tf2autobot/filter-axios-error": "^1.3.1",
         "@tf2autobot/jsonlint": "^1.0.0",
         "@tf2autobot/steamcommunity": "^3.45.3",
         "@tf2autobot/tf2": "^1.2.1",

From 632793b7047b5adf8358f7fa72e46d111e5252b3 Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Mon, 18 Jul 2022 06:41:31 +0800
Subject: [PATCH 02/18] =?UTF-8?q?=F0=9F=94=84=20bump=20@tf2autobot/bptf-li?=
 =?UTF-8?q?stings?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 package-lock.json | 18 +++++++++---------
 package.json      |  2 +-
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 045bc2770..dbc0a59ef 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
             "version": "5.0.1",
             "license": "MIT",
             "dependencies": {
-                "@tf2autobot/bptf-listings": "^5.6.1",
+                "@tf2autobot/bptf-listings": "^5.6.2",
                 "@tf2autobot/bptf-login": "^2.3.3",
                 "@tf2autobot/filter-axios-error": "^1.3.1",
                 "@tf2autobot/jsonlint": "^1.0.0",
@@ -1958,11 +1958,11 @@
             "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
         },
         "node_modules/@tf2autobot/bptf-listings": {
-            "version": "5.6.1",
-            "resolved": "https://registry.npmjs.org/@tf2autobot/bptf-listings/-/bptf-listings-5.6.1.tgz",
-            "integrity": "sha512-26vK3ozfnLEz70frqEHZ2MGEY5R6rsMLPaVuFsqbQT78B5CrTNRm+LnP3gV/GPL7j9IGy+oq36Hy4TvBO7agCg==",
+            "version": "5.6.2",
+            "resolved": "https://registry.npmjs.org/@tf2autobot/bptf-listings/-/bptf-listings-5.6.2.tgz",
+            "integrity": "sha512-xsIievP3qWnJMRQziexgHiumdjfJfx1LQVnQY2KRSunzNONHazBMYnBgSi/iATM4sptf6mBDO5oxMYdrLwk2+g==",
             "dependencies": {
-                "@tf2autobot/filter-axios-error": "^1.3.0",
+                "@tf2autobot/filter-axios-error": "^1.3.1",
                 "@tf2autobot/tf2-currencies": "^2.0.1",
                 "@tf2autobot/tf2-sku": "^2.0.3",
                 "async": "^3.2.4",
@@ -12426,11 +12426,11 @@
             "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
         },
         "@tf2autobot/bptf-listings": {
-            "version": "5.6.1",
-            "resolved": "https://registry.npmjs.org/@tf2autobot/bptf-listings/-/bptf-listings-5.6.1.tgz",
-            "integrity": "sha512-26vK3ozfnLEz70frqEHZ2MGEY5R6rsMLPaVuFsqbQT78B5CrTNRm+LnP3gV/GPL7j9IGy+oq36Hy4TvBO7agCg==",
+            "version": "5.6.2",
+            "resolved": "https://registry.npmjs.org/@tf2autobot/bptf-listings/-/bptf-listings-5.6.2.tgz",
+            "integrity": "sha512-xsIievP3qWnJMRQziexgHiumdjfJfx1LQVnQY2KRSunzNONHazBMYnBgSi/iATM4sptf6mBDO5oxMYdrLwk2+g==",
             "requires": {
-                "@tf2autobot/filter-axios-error": "^1.3.0",
+                "@tf2autobot/filter-axios-error": "^1.3.1",
                 "@tf2autobot/tf2-currencies": "^2.0.1",
                 "@tf2autobot/tf2-sku": "^2.0.3",
                 "async": "^3.2.4",
diff --git a/package.json b/package.json
index c95f657f7..65e63a21a 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,7 @@
     "updateMessage": "Bug fixed",
     "homepage": "https://github.com/TF2Autobot/tf2autobot#readme",
     "dependencies": {
-        "@tf2autobot/bptf-listings": "^5.6.1",
+        "@tf2autobot/bptf-listings": "^5.6.2",
         "@tf2autobot/bptf-login": "^2.3.3",
         "@tf2autobot/filter-axios-error": "^1.3.1",
         "@tf2autobot/jsonlint": "^1.0.0",

From 93c639aae488739c13d854e74cd7d7fcbd1307bb Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Mon, 18 Jul 2022 07:13:06 +0800
Subject: [PATCH 03/18] =?UTF-8?q?=E2=9C=85=20properly=20handle=20inventory?=
 =?UTF-8?q?=20fetch?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/classes/Trades.ts | 82 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 69 insertions(+), 13 deletions(-)

diff --git a/src/classes/Trades.ts b/src/classes/Trades.ts
index b7634d170..c86554cce 100644
--- a/src/classes/Trades.ts
+++ b/src/classes/Trades.ts
@@ -48,6 +48,10 @@ export default class Trades {
 
     private retryFetchInventoryTimeout: NodeJS.Timeout;
 
+    private calledRetryFetchFreq = 0;
+
+    private offerChangedAcc: { offer: TradeOffer; oldState: number; timeTakenToComplete: number }[] = [];
+
     constructor(private readonly bot: Bot) {
         this.bot = bot;
     }
@@ -1654,27 +1658,79 @@ export default class Trades {
         // https://github.com/TF2Autobot/tf2autobot/issues/527
         this.bot.client.gamesPlayed([]);
 
-        // Canceled offer, declined countered offer => new item assetid
-        void this.bot.inventoryManager.getInventory
-            .fetch()
-            .then(() => this.bot.handler.onTradeOfferChanged(offer, oldState, timeTakenToComplete))
-            .catch(err => {
-                log.warn('Error fetching inventory: ', err);
-                log.debug('Retrying to fetch inventory in 30 seconds...');
-                clearTimeout(this.retryFetchInventoryTimeout);
-                this.retryFetchInventory(offer, oldState, timeTakenToComplete);
-            });
+        this.offerChangedAcc.push({ offer, oldState, timeTakenToComplete });
+
+        if (this.offerChangedAcc.length <= 1) {
+            // Only call `fetch` if accumulated offerChanged is less than or equal to 1
+            // Prevent never ending "The request is a duplicate and the action has already occurred in the pass, ignored this time"
+
+            // Canceled offer, declined countered offer => new item assetid
+            void this.bot.inventoryManager.getInventory
+                .fetch()
+                .then(() => {
+                    if (this.offerChangedAcc.length > 0) {
+                        this.offerChangedAcc.forEach(el => {
+                            this.bot.handler.onTradeOfferChanged(el.offer, el.oldState, el.timeTakenToComplete);
+                        });
+
+                        // Reset to empty array
+                        this.offerChangedAcc.length = 0;
+                    }
+                })
+                .catch(err => {
+                    log.warn('Error fetching inventory: ', err);
+                    log.debug('Retrying to fetch inventory in 30 seconds...');
+                    this.calledRetryFetchFreq++;
+
+                    if (this.calledRetryFetchFreq === 1) {
+                        // Only call this once (before reset)
+                        this.retryFetchInventory();
+                    }
+                });
+        }
     }
 
-    private retryFetchInventory(offer: TradeOffer, oldState: number, timeTakenToComplete: number): void {
+    private retryFetchInventory(): void {
+        clearTimeout(this.retryFetchInventoryTimeout);
         this.retryFetchInventoryTimeout = setTimeout(() => {
             this.bot.inventoryManager.getInventory
                 .fetch()
-                .then(() => this.bot.handler.onTradeOfferChanged(offer, oldState, timeTakenToComplete))
+                .then(() => {
+                    if (this.offerChangedAcc.length > 0) {
+                        this.offerChangedAcc.forEach(el => {
+                            this.bot.handler.onTradeOfferChanged(el.offer, el.oldState, el.timeTakenToComplete);
+                        });
+
+                        // Reset to empty array
+                        this.offerChangedAcc.length = 0;
+                    }
+
+                    // Reset to 0
+                    this.calledRetryFetchFreq = 0;
+                })
                 .catch(err => {
                     log.warn('Error fetching inventory: ', err);
                     log.debug('Retrying to fetch inventory in 30 seconds...');
-                    this.retryFetchInventory(offer, oldState, timeTakenToComplete);
+                    this.calledRetryFetchFreq++;
+
+                    if (this.calledRetryFetchFreq > 3) {
+                        // If more than 3 times failed, then just proceed with an outdated inventory
+                        if (this.offerChangedAcc.length > 0) {
+                            this.offerChangedAcc.forEach(el => {
+                                this.bot.handler.onTradeOfferChanged(el.offer, el.oldState, el.timeTakenToComplete);
+                            });
+
+                            // Reset to empty array
+                            this.offerChangedAcc.length = 0;
+                        }
+
+                        // Reset to 0
+                        this.calledRetryFetchFreq = 0;
+
+                        return;
+                    }
+
+                    this.retryFetchInventory();
                 });
         }, 30 * 1000);
     }

From 93b3716b7edabd5cdaaa30cbe3169f520c22a4dd Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Mon, 18 Jul 2022 17:12:39 +0800
Subject: [PATCH 04/18] =?UTF-8?q?=F0=9F=94=89=20add=20some=20debugging=20l?=
 =?UTF-8?q?og?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/classes/Trades.ts | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/classes/Trades.ts b/src/classes/Trades.ts
index c86554cce..1d37a619d 100644
--- a/src/classes/Trades.ts
+++ b/src/classes/Trades.ts
@@ -1659,13 +1659,15 @@ export default class Trades {
         this.bot.client.gamesPlayed([]);
 
         this.offerChangedAcc.push({ offer, oldState, timeTakenToComplete });
+        log.debug('Accumulated offerChanged: ', this.offerChangedAcc.length);
 
         if (this.offerChangedAcc.length <= 1) {
             // Only call `fetch` if accumulated offerChanged is less than or equal to 1
             // Prevent never ending "The request is a duplicate and the action has already occurred in the pass, ignored this time"
 
             // Canceled offer, declined countered offer => new item assetid
-            void this.bot.inventoryManager.getInventory
+            log.debug('Fetching our inventory...');
+            return void this.bot.inventoryManager.getInventory
                 .fetch()
                 .then(() => {
                     if (this.offerChangedAcc.length > 0) {
@@ -1688,6 +1690,8 @@ export default class Trades {
                     }
                 });
         }
+
+        log.debug('Not fetching inventory this time...');
     }
 
     private retryFetchInventory(): void {

From 9549e57baaaedf36a65a10edabea4a61f9b2c20e Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Mon, 18 Jul 2022 17:13:19 +0800
Subject: [PATCH 05/18] =?UTF-8?q?=F0=9F=94=84=20should=20be=20here?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/classes/Trades.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/classes/Trades.ts b/src/classes/Trades.ts
index 1d37a619d..0b7e1ccf5 100644
--- a/src/classes/Trades.ts
+++ b/src/classes/Trades.ts
@@ -1714,8 +1714,6 @@ export default class Trades {
                 })
                 .catch(err => {
                     log.warn('Error fetching inventory: ', err);
-                    log.debug('Retrying to fetch inventory in 30 seconds...');
-                    this.calledRetryFetchFreq++;
 
                     if (this.calledRetryFetchFreq > 3) {
                         // If more than 3 times failed, then just proceed with an outdated inventory
@@ -1734,6 +1732,8 @@ export default class Trades {
                         return;
                     }
 
+                    log.debug('Retrying to fetch inventory in 30 seconds...');
+                    this.calledRetryFetchFreq++;
                     this.retryFetchInventory();
                 });
         }, 30 * 1000);

From 7dc6a71696e3f8ac93a4becd014f0e80656594be Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Mon, 18 Jul 2022 17:13:51 +0800
Subject: [PATCH 06/18] =?UTF-8?q?=F0=9F=94=A8=20fix=20typo?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/classes/Trades.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/classes/Trades.ts b/src/classes/Trades.ts
index 0b7e1ccf5..06aa66e67 100644
--- a/src/classes/Trades.ts
+++ b/src/classes/Trades.ts
@@ -1663,7 +1663,7 @@ export default class Trades {
 
         if (this.offerChangedAcc.length <= 1) {
             // Only call `fetch` if accumulated offerChanged is less than or equal to 1
-            // Prevent never ending "The request is a duplicate and the action has already occurred in the pass, ignored this time"
+            // Prevent never ending "The request is a duplicate and the action has already occurred in the past, ignored this time"
 
             // Canceled offer, declined countered offer => new item assetid
             log.debug('Fetching our inventory...');

From 833eeef7024abe1540f0d8d9ebd0dbb560707065 Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Mon, 18 Jul 2022 17:29:26 +0800
Subject: [PATCH 07/18] =?UTF-8?q?=E2=9C=85=20Don't=20fetch=20inventory=20i?=
 =?UTF-8?q?f=20still=20active?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/classes/Trades.ts | 70 ++++++++++++++++++++++++-------------------
 1 file changed, 39 insertions(+), 31 deletions(-)

diff --git a/src/classes/Trades.ts b/src/classes/Trades.ts
index 06aa66e67..2395b5d03 100644
--- a/src/classes/Trades.ts
+++ b/src/classes/Trades.ts
@@ -1658,40 +1658,48 @@ export default class Trades {
         // https://github.com/TF2Autobot/tf2autobot/issues/527
         this.bot.client.gamesPlayed([]);
 
-        this.offerChangedAcc.push({ offer, oldState, timeTakenToComplete });
-        log.debug('Accumulated offerChanged: ', this.offerChangedAcc.length);
-
-        if (this.offerChangedAcc.length <= 1) {
-            // Only call `fetch` if accumulated offerChanged is less than or equal to 1
-            // Prevent never ending "The request is a duplicate and the action has already occurred in the past, ignored this time"
-
-            // Canceled offer, declined countered offer => new item assetid
-            log.debug('Fetching our inventory...');
-            return void this.bot.inventoryManager.getInventory
-                .fetch()
-                .then(() => {
-                    if (this.offerChangedAcc.length > 0) {
-                        this.offerChangedAcc.forEach(el => {
-                            this.bot.handler.onTradeOfferChanged(el.offer, el.oldState, el.timeTakenToComplete);
-                        });
+        if (
+            offer.state === TradeOfferManager.ETradeOfferState['Active'] ||
+            offer.state === TradeOfferManager.ETradeOfferState['CreatedNeedsConfirmation']
+        ) {
+            // Offer is active, no need to fetch
+            // Do nothing
+        } else {
+            this.offerChangedAcc.push({ offer, oldState, timeTakenToComplete });
+            log.debug('Accumulated offerChanged: ', this.offerChangedAcc.length);
+
+            if (this.offerChangedAcc.length <= 1) {
+                // Only call `fetch` if accumulated offerChanged is less than or equal to 1
+                // Prevent never ending "The request is a duplicate and the action has already occurred in the past, ignored this time"
+
+                // Canceled offer, declined countered offer => new item assetid
+                log.debug('Fetching our inventory...');
+                return void this.bot.inventoryManager.getInventory
+                    .fetch()
+                    .then(() => {
+                        if (this.offerChangedAcc.length > 0) {
+                            this.offerChangedAcc.forEach(el => {
+                                this.bot.handler.onTradeOfferChanged(el.offer, el.oldState, el.timeTakenToComplete);
+                            });
 
-                        // Reset to empty array
-                        this.offerChangedAcc.length = 0;
-                    }
-                })
-                .catch(err => {
-                    log.warn('Error fetching inventory: ', err);
-                    log.debug('Retrying to fetch inventory in 30 seconds...');
-                    this.calledRetryFetchFreq++;
+                            // Reset to empty array
+                            this.offerChangedAcc.length = 0;
+                        }
+                    })
+                    .catch(err => {
+                        log.warn('Error fetching inventory: ', err);
+                        log.debug('Retrying to fetch inventory in 30 seconds...');
+                        this.calledRetryFetchFreq++;
+
+                        if (this.calledRetryFetchFreq === 1) {
+                            // Only call this once (before reset)
+                            this.retryFetchInventory();
+                        }
+                    });
+            }
 
-                    if (this.calledRetryFetchFreq === 1) {
-                        // Only call this once (before reset)
-                        this.retryFetchInventory();
-                    }
-                });
+            log.debug('Not fetching inventory this time...');
         }
-
-        log.debug('Not fetching inventory this time...');
     }
 
     private retryFetchInventory(): void {

From 39b4a27849ad3ad2885a9571f51e139e1e4fe5d0 Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Mon, 18 Jul 2022 17:39:55 +0800
Subject: [PATCH 08/18] =?UTF-8?q?=E2=9C=85=20Also=20don't=20fetch=20on=20C?=
 =?UTF-8?q?ountered=20or=20Declined=20Countered?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/classes/Trades.ts | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/classes/Trades.ts b/src/classes/Trades.ts
index 2395b5d03..5a6ed63c3 100644
--- a/src/classes/Trades.ts
+++ b/src/classes/Trades.ts
@@ -1660,9 +1660,12 @@ export default class Trades {
 
         if (
             offer.state === TradeOfferManager.ETradeOfferState['Active'] ||
-            offer.state === TradeOfferManager.ETradeOfferState['CreatedNeedsConfirmation']
+            offer.state === TradeOfferManager.ETradeOfferState['CreatedNeedsConfirmation'] ||
+            offer.state === TradeOfferManager.ETradeOfferState['Countered'] ||
+            (oldState === TradeOfferManager.ETradeOfferState['Countered'] &&
+                offer.state === TradeOfferManager.ETradeOfferState['Declined'])
         ) {
-            // Offer is active, no need to fetch
+            // Offer is active, or countered, or declined countered, no need to fetch
             // Do nothing
         } else {
             this.offerChangedAcc.push({ offer, oldState, timeTakenToComplete });
@@ -1672,7 +1675,7 @@ export default class Trades {
                 // Only call `fetch` if accumulated offerChanged is less than or equal to 1
                 // Prevent never ending "The request is a duplicate and the action has already occurred in the past, ignored this time"
 
-                // Canceled offer, declined countered offer => new item assetid
+                // Accepted, Invalid trade (possible) => new item assetid
                 log.debug('Fetching our inventory...');
                 return void this.bot.inventoryManager.getInventory
                     .fetch()

From bb40635fe316ec1b907f5eff831dabd9e19323a0 Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Mon, 18 Jul 2022 19:33:44 +0800
Subject: [PATCH 09/18] =?UTF-8?q?=F0=9F=92=AF=20allow=20Discord=20bot=20to?=
 =?UTF-8?q?=20chat=20with=20non-admin?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/classes/Bot.ts                       |  6 ++--
 src/classes/Commands/Commands.ts         | 38 +++++++++++++++++---
 src/classes/Commands/sub-classes/Help.ts | 19 ++++++++++
 src/classes/DiscordBot.ts                | 19 ++++++++--
 src/classes/MyHandler/MyHandler.ts       | 44 ++++++++++++++++--------
 5 files changed, 101 insertions(+), 25 deletions(-)

diff --git a/src/classes/Bot.ts b/src/classes/Bot.ts
index 2faaf9ee8..365171aa1 100644
--- a/src/classes/Bot.ts
+++ b/src/classes/Bot.ts
@@ -1239,9 +1239,6 @@ export default class Bot {
     }
 
     sendMessage(steamID: SteamID | string, message: string): void {
-        const steamID64 = steamID.toString();
-        const friend = this.friends.getFriend(steamID64);
-
         if (steamID instanceof SteamID && steamID.redirectAnswerTo) {
             const origMessage = steamID.redirectAnswerTo;
             if (origMessage instanceof DiscordMessage) {
@@ -1252,6 +1249,9 @@ export default class Bot {
             return;
         }
 
+        const steamID64 = steamID.toString();
+        const friend = this.friends.getFriend(steamID64);
+
         if (!friend) {
             // If not friend, we send message with chatMessage
             this.client.chatMessage(steamID, message);
diff --git a/src/classes/Commands/Commands.ts b/src/classes/Commands/Commands.ts
index 59ab67282..435585574 100644
--- a/src/classes/Commands/Commands.ts
+++ b/src/classes/Commands/Commands.ts
@@ -97,6 +97,7 @@ export default class Commands {
         const command = CommandParser.getCommand(message.toLowerCase());
         const isAdmin = this.bot.isAdmin(steamID);
         const isWhitelisted = this.bot.isWhitelisted(steamID);
+        const isInvalidType = steamID.type === 0;
 
         const checkMessage = message.split(' ').filter(word => word.includes(`!${command}`)).length;
 
@@ -104,6 +105,8 @@ export default class Commands {
             return this.bot.sendMessage(steamID, "⛔ Don't spam");
         }
 
+        log.debug('Read processMessage');
+
         if (message.startsWith('!')) {
             if (command === 'help') {
                 void this.help.helpCommand(steamID);
@@ -112,34 +115,63 @@ export default class Commands {
             } else if (['price', 'pc'].includes(command)) {
                 this.priceCommand(steamID, message);
             } else if (['buy', 'b', 'sell', 's'].includes(command)) {
+                if (isInvalidType) {
+                    return this.bot.sendMessage(steamID, '❌ Command not available.');
+                }
                 this.buyOrSellCommand(steamID, message, command as Instant);
             } else if (command === 'buycart') {
+                if (isInvalidType) {
+                    return this.bot.sendMessage(steamID, '❌ Command not available.');
+                }
                 this.buyCartCommand(steamID, message);
             } else if (command === 'sellcart') {
+                if (isInvalidType) {
+                    return this.bot.sendMessage(steamID, '❌ Command not available.');
+                }
                 this.sellCartCommand(steamID, message);
             } else if (command === 'cart') {
+                if (isInvalidType) {
+                    return this.bot.sendMessage(steamID, '❌ Command not available.');
+                }
                 this.cartCommand(steamID);
             } else if (command === 'clearcart') {
+                if (isInvalidType) {
+                    return this.bot.sendMessage(steamID, '❌ Command not available.');
+                }
                 this.clearCartCommand(steamID);
             } else if (command === 'checkout') {
+                if (isInvalidType) {
+                    return this.bot.sendMessage(steamID, '❌ Command not available.');
+                }
                 this.checkoutCommand(steamID);
             } else if (command === 'cancel') {
+                if (isInvalidType) {
+                    return this.bot.sendMessage(steamID, '❌ Command not available.');
+                }
                 this.cancelCommand(steamID);
             } else if (command === 'queue') {
+                if (isInvalidType) {
+                    return this.bot.sendMessage(steamID, '❌ Command not available.');
+                }
                 this.queueCommand(steamID);
             } else if (['time', 'uptime', 'pure', 'rate', 'owner', 'discord', 'stock'].includes(command)) {
                 if (command === 'stock') {
                     return this.misc.miscCommand(steamID, command as Misc, message);
                 }
                 this.misc.miscCommand(steamID, command as Misc);
+            } else if (command === 'sku') {
+                this.getSKU(steamID, message);
+            } else if (command === 'message') {
+                if (isInvalidType) {
+                    return this.bot.sendMessage(steamID, '❌ Command not available.');
+                }
+                this.message.message(steamID, message);
             } else if (command === 'paints' && isAdmin) {
                 this.misc.paintsCommand(steamID);
             } else if (command === 'more') {
                 this.help.moreCommand(steamID);
             } else if (command === 'autokeys') {
                 this.manager.autokeysCommand(steamID);
-            } else if (command === 'message') {
-                this.message.message(steamID, message);
             } else if (['craftweapon', 'craftweapons', 'uncraftweapon', 'uncraftweapons'].includes(command)) {
                 void this.misc.weaponCommand(
                     steamID,
@@ -247,8 +279,6 @@ export default class Commands {
                 this.donateCartCommand(steamID);
             } else if (command === 'premium' && isAdmin) {
                 this.buyBPTFPremiumCommand(steamID, message);
-            } else if (command === 'sku' && isAdmin) {
-                this.getSKU(steamID, message);
             } else if (command === 'refreshschema' && isAdmin) {
                 this.manager.refreshSchema(steamID);
             } else if (command === 'crafttoken' && isAdmin) {
diff --git a/src/classes/Commands/sub-classes/Help.ts b/src/classes/Commands/sub-classes/Help.ts
index 7a0a3df33..659af65a6 100644
--- a/src/classes/Commands/sub-classes/Help.ts
+++ b/src/classes/Commands/sub-classes/Help.ts
@@ -11,6 +11,25 @@ export default class HelpCommands {
         const isAdmin = this.bot.isAdmin(steamID);
         const isCustomPricer = this.bot.pricelist.isUseCustomPricer;
 
+        if (steamID instanceof SteamID && steamID.redirectAnswerTo && steamID.type === 0) {
+            return this.bot.sendMessage(
+                steamID,
+                `\nDo not include characters <> nor [ ] - <> means required and [] means optional.` +
+                    "\n\n📜 Here's a list of my commands:" +
+                    '\n- ' +
+                    [
+                        '!help - Get a list of commands',
+                        '!how2trade - Guide on how to trade with the bot',
+                        '!price [amount] <name> - Get the price and stock of an item 💲📦',
+                        "!sku <Full Item Name|Item's sku> - Get the sku of an item.",
+                        "!owner - Get the owner's Steam profile and Backpack.tf links",
+                        '!message <your message> - Send a message to the owner of the bot 💬',
+                        "!discord - Get a link to join TF2Autobot and/or the owner's discord server 💬",
+                        '!more - Show more available commands list 📜'
+                    ].join('\n- ')
+            );
+        }
+
         this.bot.sendMessage(
             steamID,
             `📌 Note 📌${
diff --git a/src/classes/DiscordBot.ts b/src/classes/DiscordBot.ts
index d5a2d104e..090cc838b 100644
--- a/src/classes/DiscordBot.ts
+++ b/src/classes/DiscordBot.ts
@@ -61,15 +61,28 @@ export default class DiscordBot {
         log.info(
             `Got new message ${String(message.content)} from ${message.author.tag} (${String(message.author.id)})`
         );
-        if (!this.isDiscordAdmin(message.author.id)) {
-            return; // obey only admins
-        }
 
         if (!this.bot.isReady) {
             this.sendAnswer(message, '🛑 The bot is still booting up, please wait');
             return;
         }
 
+        if (!this.isDiscordAdmin(message.author.id)) {
+            try {
+                // Will return default invalid value
+                const dummySteamID = new SteamID(null);
+                dummySteamID.redirectAnswerTo = message;
+                await this.bot.handler.onMessage(dummySteamID, message.content);
+            } catch (err) {
+                log.error(err);
+                message.channel
+                    .send(`❌ Error:\n${JSON.stringify(err)}`)
+                    .catch(err => log.error('Failed to send error message to Discord:', err));
+            }
+
+            return;
+        }
+
         try {
             const adminID = this.getAdminBy(message.author.id);
             adminID.redirectAnswerTo = message;
diff --git a/src/classes/MyHandler/MyHandler.ts b/src/classes/MyHandler/MyHandler.ts
index e14d98ad4..da11bc921 100644
--- a/src/classes/MyHandler/MyHandler.ts
+++ b/src/classes/MyHandler/MyHandler.ts
@@ -459,25 +459,39 @@ export default class MyHandler extends Handler {
             return this.bot.sendMessage(steamID, '⚠️ The bot is updating, please wait until I am back online.');
         }
 
-        const steamID64 = steamID.toString();
-        if (!this.bot.friends.isFriend(steamID64)) {
-            return;
-        }
+        if (steamID.type !== 0) {
+            const steamID64 = steamID.toString();
+            if (!this.bot.friends.isFriend(steamID64)) {
+                return;
+            }
 
-        const friend = this.bot.friends.getFriend(steamID64);
+            const friend = this.bot.friends.getFriend(steamID64);
 
-        if (friend === null) {
-            log.info(`Message from ${steamID64}: ${message}`);
-        } else {
-            log.info(`Message from ${friend.player_name} (${steamID64}): ${message}`);
-        }
+            if (friend === null) {
+                log.info(`Message from ${steamID64}: ${message}`);
+            } else {
+                log.info(`Message from ${friend.player_name} (${steamID64}): ${message}`);
+            }
 
-        if (this.recentlySentMessage[steamID64] !== undefined && this.recentlySentMessage[steamID64] >= 1) {
-            return;
-        }
+            if (this.recentlySentMessage[steamID64] !== undefined && this.recentlySentMessage[steamID64] >= 1) {
+                return;
+            }
 
-        this.recentlySentMessage[steamID64] =
-            (this.recentlySentMessage[steamID64] === undefined ? 0 : this.recentlySentMessage[steamID64]) + 1;
+            this.recentlySentMessage[steamID64] =
+                (this.recentlySentMessage[steamID64] === undefined ? 0 : this.recentlySentMessage[steamID64]) + 1;
+        } else if (steamID instanceof SteamID && steamID.redirectAnswerTo) {
+            if (
+                this.recentlySentMessage[steamID.redirectAnswerTo.author.id] !== undefined &&
+                this.recentlySentMessage[steamID.redirectAnswerTo.author.id] >= 1
+            ) {
+                return;
+            }
+
+            this.recentlySentMessage[steamID.redirectAnswerTo.author.id] =
+                (this.recentlySentMessage[steamID.redirectAnswerTo.author.id] === undefined
+                    ? 0
+                    : this.recentlySentMessage[steamID.redirectAnswerTo.author.id]) + 1;
+        }
 
         await this.commands.processMessage(steamID, message);
     }

From 0025d5e74081d4cf705672305a31832ff6517d6c Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Mon, 18 Jul 2022 19:36:54 +0800
Subject: [PATCH 10/18] =?UTF-8?q?=F0=9F=9B=91=20ignore=20message=20if=20no?=
 =?UTF-8?q?t=20starts=20with=20!?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/classes/DiscordBot.ts | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/classes/DiscordBot.ts b/src/classes/DiscordBot.ts
index 090cc838b..faab101ed 100644
--- a/src/classes/DiscordBot.ts
+++ b/src/classes/DiscordBot.ts
@@ -58,6 +58,10 @@ export default class DiscordBot {
             return; // Ignore webhook messages
         }
 
+        if (!message.content.startsWith('!')) {
+            return; // Ignore message that not start with !
+        }
+
         log.info(
             `Got new message ${String(message.content)} from ${message.author.tag} (${String(message.author.id)})`
         );

From 17ed3640552e80f0e288acd1e83b8a4b3bb8d4f4 Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Mon, 18 Jul 2022 22:43:30 +0800
Subject: [PATCH 11/18] =?UTF-8?q?=F0=9F=9B=91=20throw=20an=20error=20on=20?=
 =?UTF-8?q?reptf=20invalid=20response=20message?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/lib/bans.ts | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/lib/bans.ts b/src/lib/bans.ts
index fb0314b11..bdb841501 100644
--- a/src/lib/bans.ts
+++ b/src/lib/bans.ts
@@ -106,6 +106,13 @@ async function getFromReptf(
             .then(response => {
                 const bans = response.data as RepTF;
 
+                const v = ['bptfBans', 'srBans', 'mpBans'];
+                for (let i = 0; i < v.length; i++) {
+                    if (bans[v[i] as 'bptfBans' | 'srBans' | 'mpBans'].message.includes('Failed to get data')) {
+                        throw `Failed to get data (${v[i]})`;
+                    }
+                }
+
                 const isBptfBanned = bans.bptfBans ? bans.bptfBans.banned === 'bad' : false;
                 const isSteamRepBanned = bans.srBans ? bans.srBans.banned === 'bad' : false;
                 const isMptfBanned = bans.mpBans ? bans.mpBans.banned === 'bad' : false;

From 84ba0f5054068244d3d2553da000e7990657a6bf Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Mon, 18 Jul 2022 22:44:54 +0800
Subject: [PATCH 12/18] =?UTF-8?q?=F0=9F=9A=A7=20just=20in=20case?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/lib/bans.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/lib/bans.ts b/src/lib/bans.ts
index bdb841501..027a6e183 100644
--- a/src/lib/bans.ts
+++ b/src/lib/bans.ts
@@ -108,7 +108,7 @@ async function getFromReptf(
 
                 const v = ['bptfBans', 'srBans', 'mpBans'];
                 for (let i = 0; i < v.length; i++) {
-                    if (bans[v[i] as 'bptfBans' | 'srBans' | 'mpBans'].message.includes('Failed to get data')) {
+                    if (bans[v[i] as 'bptfBans' | 'srBans' | 'mpBans']?.message?.includes('Failed to get data')) {
                         throw `Failed to get data (${v[i]})`;
                     }
                 }

From a056bdd98d4b7bb854d81b1d94c98afee8cf2552 Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Mon, 18 Jul 2022 23:14:29 +0800
Subject: [PATCH 13/18] =?UTF-8?q?=F0=9F=94=84=20separate=20mptf=20ban?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/lib/bans.ts | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/lib/bans.ts b/src/lib/bans.ts
index 027a6e183..4b8dbe50d 100644
--- a/src/lib/bans.ts
+++ b/src/lib/bans.ts
@@ -106,7 +106,7 @@ async function getFromReptf(
             .then(response => {
                 const bans = response.data as RepTF;
 
-                const v = ['bptfBans', 'srBans', 'mpBans'];
+                const v = ['bptfBans', 'srBans'];
                 for (let i = 0; i < v.length; i++) {
                     if (bans[v[i] as 'bptfBans' | 'srBans' | 'mpBans']?.message?.includes('Failed to get data')) {
                         throw `Failed to get data (${v[i]})`;
@@ -123,6 +123,10 @@ async function getFromReptf(
                 };
 
                 if (checkMptf) {
+                    if (bans.mpBans.message === 'Failed to get data') {
+                        throw `Failed to get data (mpBans)`;
+                    }
+
                     bansResult['Marketplace.tf'] = isMptfBanned
                         ? `banned - ${removeHTMLTags(bans.mpBans.message)}`
                         : 'clean';

From f043deeba6139c11616306f738ef3ea415431182 Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Mon, 18 Jul 2022 23:15:13 +0800
Subject: [PATCH 14/18] =?UTF-8?q?=F0=9F=9A=AE=20remove=20unrelated=20type?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/lib/bans.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/lib/bans.ts b/src/lib/bans.ts
index 4b8dbe50d..f90a008df 100644
--- a/src/lib/bans.ts
+++ b/src/lib/bans.ts
@@ -108,7 +108,7 @@ async function getFromReptf(
 
                 const v = ['bptfBans', 'srBans'];
                 for (let i = 0; i < v.length; i++) {
-                    if (bans[v[i] as 'bptfBans' | 'srBans' | 'mpBans']?.message?.includes('Failed to get data')) {
+                    if (bans[v[i] as 'bptfBans' | 'srBans']?.message?.includes('Failed to get data')) {
                         throw `Failed to get data (${v[i]})`;
                     }
                 }

From 730247ff9792948e3a6acc1206f1a63024e90945 Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Mon, 18 Jul 2022 23:49:10 +0800
Subject: [PATCH 15/18] =?UTF-8?q?=E2=9C=A8=20add=20`!links`=20command?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/classes/Commands/Commands.ts         |  2 ++
 src/classes/Commands/sub-classes/Help.ts | 15 ++++++++-------
 src/classes/Commands/sub-classes/Misc.ts | 11 +++++++++++
 3 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/src/classes/Commands/Commands.ts b/src/classes/Commands/Commands.ts
index 435585574..b2a178a26 100644
--- a/src/classes/Commands/Commands.ts
+++ b/src/classes/Commands/Commands.ts
@@ -159,6 +159,8 @@ export default class Commands {
                     return this.misc.miscCommand(steamID, command as Misc, message);
                 }
                 this.misc.miscCommand(steamID, command as Misc);
+            } else if (['link', 'links'].includes(command)) {
+                this.misc.links(steamID);
             } else if (command === 'sku') {
                 this.getSKU(steamID, message);
             } else if (command === 'message') {
diff --git a/src/classes/Commands/sub-classes/Help.ts b/src/classes/Commands/sub-classes/Help.ts
index 659af65a6..7c4816bc1 100644
--- a/src/classes/Commands/sub-classes/Help.ts
+++ b/src/classes/Commands/sub-classes/Help.ts
@@ -18,14 +18,15 @@ export default class HelpCommands {
                     "\n\n📜 Here's a list of my commands:" +
                     '\n- ' +
                     [
-                        '!help - Get a list of commands',
-                        '!how2trade - Guide on how to trade with the bot',
-                        '!price [amount] <name> - Get the price and stock of an item 💲📦',
+                        '!help - Get a list of commands.',
+                        '!how2trade - Guide on how to trade with the bot.',
+                        "!links - Links to the bot's Steam, Backpack.tf, and Rep.tf.",
+                        '!price [amount] <name> - Get the price and stock of an item.',
                         "!sku <Full Item Name|Item's sku> - Get the sku of an item.",
-                        "!owner - Get the owner's Steam profile and Backpack.tf links",
-                        '!message <your message> - Send a message to the owner of the bot 💬',
-                        "!discord - Get a link to join TF2Autobot and/or the owner's discord server 💬",
-                        '!more - Show more available commands list 📜'
+                        "!owner - Get the owner's Steam profile and Backpack.tf links.",
+                        '!message <your message> - Send a message to the owner of the bot.',
+                        "!discord - Get a link to join TF2Autobot and/or the owner's discord server.",
+                        '!more - Show more available commands list.'
                     ].join('\n- ')
             );
         }
diff --git a/src/classes/Commands/sub-classes/Misc.ts b/src/classes/Commands/sub-classes/Misc.ts
index 8f97f8a34..12eec61d9 100644
--- a/src/classes/Commands/sub-classes/Misc.ts
+++ b/src/classes/Commands/sub-classes/Misc.ts
@@ -17,6 +17,17 @@ export default class MiscCommands {
         this.bot = bot;
     }
 
+    links(SteamID: SteamID): void {
+        const botSteamID = this.bot.client.steamID.getSteamID64();
+
+        this.bot.sendMessage(
+            SteamID,
+            `Steam: <https://steamcommunity.com/profiles/${botSteamID}>` +
+                `\nBackpack.tf: <https://backpack.tf/u/${botSteamID}>` +
+                `\nRep.tf: <https://rep.tf/${botSteamID}>`
+        );
+    }
+
     miscCommand(steamID: SteamID, command: Misc, message?: string): void {
         const opt = this.bot.options.commands[command];
         if (!opt.enable) {

From 32910ecdc0c8c16bfc34d85d68737dbc595f5153 Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Tue, 19 Jul 2022 09:24:28 +0800
Subject: [PATCH 16/18] =?UTF-8?q?=F0=9F=94=84=20update=20`!stock`=20comman?=
 =?UTF-8?q?d=20-=20include=20assetids?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/classes/Commands/sub-classes/Misc.ts | 79 ++++++++++++++++++------
 1 file changed, 61 insertions(+), 18 deletions(-)

diff --git a/src/classes/Commands/sub-classes/Misc.ts b/src/classes/Commands/sub-classes/Misc.ts
index 12eec61d9..23802ae6b 100644
--- a/src/classes/Commands/sub-classes/Misc.ts
+++ b/src/classes/Commands/sub-classes/Misc.ts
@@ -8,6 +8,8 @@ import CommandParser from '../../CommandParser';
 import Bot from '../../Bot';
 import { Discord, Stock } from '../../Options';
 import { pure, timeNow, uptime, testPriceKey } from '../../../lib/tools/export';
+import { killstreakersData, sheensData, spellsData } from '../../../lib/data';
+import { Paints, StrangeParts } from '@tf2autobot/tf2-schema';
 
 type Misc = 'time' | 'uptime' | 'pure' | 'rate' | 'owner' | 'discord' | 'stock';
 type CraftUncraft = 'craftweapon' | 'uncraftweapon';
@@ -109,31 +111,60 @@ export default class MiscCommands {
         } else {
             const itemNameOrSku = CommandParser.removeCommand(removeLinkProtocol(message));
             let reply = '';
-            let isWithSomething = false;
 
             if (itemNameOrSku !== '!sku') {
-                if (!testPriceKey(itemNameOrSku)) {
-                    // Receive name
-                    const sku = this.bot.schema.getSkuFromName(itemNameOrSku);
-                    if (itemNameOrSku !== '!stock') {
+                // I don't remember why not `!sku` here.
+                let sku: string = itemNameOrSku;
+                if (itemNameOrSku !== '!stock') {
+                    if (!testPriceKey(itemNameOrSku)) {
+                        // Receive name
+                        sku = this.bot.schema.getSkuFromName(itemNameOrSku);
+
                         if (sku.includes('null') || sku.includes('undefined')) {
-                            reply = `/pre Generated sku: ${sku}\nPlease check the name. If correct, please let us know. Thank you.`;
-                            isWithSomething = true;
-                        } else {
-                            const assetids = this.bot.inventoryManager.getInventory.findBySKU(sku);
-                            reply = `/pre I currently have ${assetids.length} of ${itemNameOrSku} (${sku}).`;
-                            isWithSomething = true;
+                            return this.bot.sendMessage(
+                                steamID,
+                                `/pre ❌ Generated sku: ${sku}\nPlease check the name. If correct, please let us know. Thank you.`
+                            );
                         }
                     }
-                } else {
-                    // Receive sku
-                    const assetids = this.bot.inventoryManager.getInventory.findBySKU(itemNameOrSku);
-                    const name = this.bot.schema.getName(SKU.fromString(itemNameOrSku), false);
 
-                    reply = `/pre I currently have ${assetids.length} of ${name} (${itemNameOrSku}).`;
-                    isWithSomething = true;
+                    const itemDicts = this.bot.inventoryManager.getInventory.getItems[sku] ?? [];
+                    const name = this.bot.schema.getName(SKU.fromString(sku), false);
+
+                    reply = `/pre I currently have ${itemDicts.length} of ${name} (${sku}).`;
+
+                    const assetids: string[] = [];
+                    if (itemDicts.length > 0) {
+                        const hv: string[] = [];
+                        itemDicts.forEach(item => {
+                            if (item.hv) {
+                                Object.keys(item.hv).forEach(attachment => {
+                                    for (const pSku in item.hv[attachment]) {
+                                        if (!Object.prototype.hasOwnProperty.call(item.hv[attachment], pSku)) {
+                                            continue;
+                                        }
+
+                                        const hvName = getAttachmentName(
+                                            attachment,
+                                            pSku,
+                                            this.bot.schema.paints,
+                                            this.bot.strangeParts
+                                        );
+
+                                        hv.push(hvName);
+                                    }
+                                });
+                            }
+
+                            assetids.push(`${item.id}${hv.length > 0 ? ` (${hv.join(', ')})` : ''}`);
+                        });
+                    }
+
+                    reply += assetids.length > 0 ? '\n\nAssetids:\n- ' + assetids.join('\n- ') : '';
+                    return this.bot.sendMessage(steamID, reply);
                 }
             }
+
             const inventory = this.bot.inventoryManager.getInventory;
             const dict = inventory.getItems;
             const items: { amount: number; name: string }[] = [];
@@ -207,7 +238,7 @@ export default class MiscCommands {
             reply += custom
                 ? custom.replace(/%stocklist%/g, stock.join(', \n'))
                 : `${
-                      isWithSomething ? '\n\n' : steamID.redirectAnswerTo instanceof DiscordMessage ? '/pre2' : '/pre '
+                      steamID.redirectAnswerTo instanceof DiscordMessage ? '/pre2' : '/pre '
                   }📜 Here's a list of all the items that I have in my inventory:\n${stock.join(', \n')}`;
 
             if (left > 0) {
@@ -343,3 +374,15 @@ export default class MiscCommands {
         return stock;
     }
 }
+
+function getKeyByValue(object: { [key: string]: any }, value: any): string {
+    return Object.keys(object).find(key => object[key] === value);
+}
+
+function getAttachmentName(attachment: string, pSku: string, paints: Paints, parts: StrangeParts): string {
+    if (attachment === 's') return getKeyByValue(spellsData, pSku);
+    else if (attachment === 'sp') return getKeyByValue(parts, pSku);
+    else if (attachment === 'ke') return getKeyByValue(killstreakersData, pSku);
+    else if (attachment === 'ks') return getKeyByValue(sheensData, pSku);
+    else if (attachment === 'p') return getKeyByValue(paints, parseInt(pSku.replace('p', '')));
+}

From a39cf2cfb28fe93e2b154abd46ec64d13f2a5acf Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Tue, 19 Jul 2022 14:12:36 +0800
Subject: [PATCH 17/18] =?UTF-8?q?=F0=9F=9A=AE=20remove=20`!message`=20comm?=
 =?UTF-8?q?and=20(Discord)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/classes/Commands/sub-classes/Help.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/classes/Commands/sub-classes/Help.ts b/src/classes/Commands/sub-classes/Help.ts
index 7c4816bc1..829d551f5 100644
--- a/src/classes/Commands/sub-classes/Help.ts
+++ b/src/classes/Commands/sub-classes/Help.ts
@@ -24,7 +24,6 @@ export default class HelpCommands {
                         '!price [amount] <name> - Get the price and stock of an item.',
                         "!sku <Full Item Name|Item's sku> - Get the sku of an item.",
                         "!owner - Get the owner's Steam profile and Backpack.tf links.",
-                        '!message <your message> - Send a message to the owner of the bot.',
                         "!discord - Get a link to join TF2Autobot and/or the owner's discord server.",
                         '!more - Show more available commands list.'
                     ].join('\n- ')

From 483dbbee85b50bbdbbec61e8ebdd4d1a0ad2067a Mon Sep 17 00:00:00 2001
From: IdiNium <47635037+idinium96@users.noreply.github.com>
Date: Tue, 19 Jul 2022 14:14:28 +0800
Subject: [PATCH 18/18] =?UTF-8?q?=F0=9F=94=A8=20refactor=20try-catch=20dup?=
 =?UTF-8?q?lication?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/classes/DiscordBot.ts | 15 +++------------
 1 file changed, 3 insertions(+), 12 deletions(-)

diff --git a/src/classes/DiscordBot.ts b/src/classes/DiscordBot.ts
index faab101ed..c345dd4fe 100644
--- a/src/classes/DiscordBot.ts
+++ b/src/classes/DiscordBot.ts
@@ -71,23 +71,14 @@ export default class DiscordBot {
             return;
         }
 
-        if (!this.isDiscordAdmin(message.author.id)) {
-            try {
+        try {
+            if (!this.isDiscordAdmin(message.author.id)) {
                 // Will return default invalid value
                 const dummySteamID = new SteamID(null);
                 dummySteamID.redirectAnswerTo = message;
-                await this.bot.handler.onMessage(dummySteamID, message.content);
-            } catch (err) {
-                log.error(err);
-                message.channel
-                    .send(`❌ Error:\n${JSON.stringify(err)}`)
-                    .catch(err => log.error('Failed to send error message to Discord:', err));
+                return await this.bot.handler.onMessage(dummySteamID, message.content);
             }
 
-            return;
-        }
-
-        try {
             const adminID = this.getAdminBy(message.author.id);
             adminID.redirectAnswerTo = message;
             await this.bot.handler.onMessage(adminID, message.content);