From 0ebdc0f4b4a773bc752130931d528793d5910afe Mon Sep 17 00:00:00 2001
From: idinium96 <idin100msc@gmail.com>
Date: Thu, 30 Jul 2020 03:32:39 +0800
Subject: [PATCH 01/15] fix autobump when bptf down?

---
 src/classes/Listings.ts | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/classes/Listings.ts b/src/classes/Listings.ts
index b833b44bd..cd5c1483f 100644
--- a/src/classes/Listings.ts
+++ b/src/classes/Listings.ts
@@ -22,6 +22,8 @@ export = class Listings {
 
     private autoRelistEnabled = false;
 
+    private autoRelistRetry = false;
+
     private autoRelistTimeout;
 
     private templates: { buy: string; sell: string } = {
@@ -97,6 +99,8 @@ export = class Listings {
         this.getAccountInfo().asCallback((err, info) => {
             if (err) {
                 log.warn('Failed to get account info from backpack.tf: ', err);
+                clearTimeout(this.autoRelistTimeout);
+                this.autoRelistRetry = true;
                 return;
             }
 
@@ -108,6 +112,9 @@ export = class Listings {
                     'Enabling autorelist! - Consider paying for backpack.tf premium instead of forcefully bumping listings: https://backpack.tf/donate'
                 );
                 this.enableAutoRelist();
+            } else if (this.autoRelistRetry) {
+                this.autoRelistRetry = false;
+                this.enableAutoRelist();
             }
         });
     }

From cb0dcca70e7606ee1befc9fec1101fd0cfff12b4 Mon Sep 17 00:00:00 2001
From: idinium96 <idin100msc@gmail.com>
Date: Thu, 30 Jul 2020 03:46:10 +0800
Subject: [PATCH 02/15] add timeout

---
 src/classes/Listings.ts | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/classes/Listings.ts b/src/classes/Listings.ts
index cd5c1483f..fee7e89dc 100644
--- a/src/classes/Listings.ts
+++ b/src/classes/Listings.ts
@@ -24,6 +24,8 @@ export = class Listings {
 
     private autoRelistRetry = false;
 
+    private autoRelistRetryTimeout;
+
     private autoRelistTimeout;
 
     private templates: { buy: string; sell: string } = {
@@ -112,9 +114,13 @@ export = class Listings {
                     'Enabling autorelist! - Consider paying for backpack.tf premium instead of forcefully bumping listings: https://backpack.tf/donate'
                 );
                 this.enableAutoRelist();
-            } else if (this.autoRelistRetry) {
+            } else if (this.autoRelistEnabled && this.autoRelistRetry) {
                 this.autoRelistRetry = false;
-                this.enableAutoRelist();
+                clearTimeout(this.autoRelistRetryTimeout);
+                log.warn('backpack.tf down, will wait for 30 minutes before relist again...');
+                this.autoRelistRetryTimeout = setTimeout(() => {
+                    this.enableAutoRelist();
+                }, 30 * 60 * 1000);
             }
         });
     }

From 2b429572a581382be282761c61395e1fc9c73bf5 Mon Sep 17 00:00:00 2001
From: idinium96 <idin100msc@gmail.com>
Date: Thu, 30 Jul 2020 17:27:48 +0800
Subject: [PATCH 03/15] add a missing clearTimeout

---
 src/classes/Listings.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/classes/Listings.ts b/src/classes/Listings.ts
index fee7e89dc..c74817f0c 100644
--- a/src/classes/Listings.ts
+++ b/src/classes/Listings.ts
@@ -63,6 +63,7 @@ export = class Listings {
 
         this.autoRelistEnabled = true;
 
+        clearTimeout(this.autoRelistRetryTimeout);
         clearTimeout(this.autoRelistTimeout);
 
         const doneWait = (): void => {

From c3fe87f81804f67bdbd186eeedf265794ca68cdf Mon Sep 17 00:00:00 2001
From: idinium96 <idin100msc@gmail.com>
Date: Fri, 31 Jul 2020 14:45:27 +0800
Subject: [PATCH 04/15] add another clearTimeout

---
 src/classes/Listings.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/classes/Listings.ts b/src/classes/Listings.ts
index c74817f0c..6de51f6dc 100644
--- a/src/classes/Listings.ts
+++ b/src/classes/Listings.ts
@@ -103,6 +103,7 @@ export = class Listings {
             if (err) {
                 log.warn('Failed to get account info from backpack.tf: ', err);
                 clearTimeout(this.autoRelistTimeout);
+                clearTimeout(this.autoRelistRetryTimeout);
                 this.autoRelistRetry = true;
                 return;
             }

From e987fce2ccd4cde0f5296099b8e3004cf92f51bd Mon Sep 17 00:00:00 2001
From: idinium96 <idin100msc@gmail.com>
Date: Fri, 31 Jul 2020 14:46:10 +0800
Subject: [PATCH 05/15] reduce retry time to 5 minutes

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

diff --git a/src/classes/Listings.ts b/src/classes/Listings.ts
index 6de51f6dc..6d83c4cbe 100644
--- a/src/classes/Listings.ts
+++ b/src/classes/Listings.ts
@@ -122,7 +122,7 @@ export = class Listings {
                 log.warn('backpack.tf down, will wait for 30 minutes before relist again...');
                 this.autoRelistRetryTimeout = setTimeout(() => {
                     this.enableAutoRelist();
-                }, 30 * 60 * 1000);
+                }, 5 * 60 * 1000);
             }
         });
     }

From 74f73b16f589120ea3acb66b4f3108c6aeac6a35 Mon Sep 17 00:00:00 2001
From: idinium96 <idin100msc@gmail.com>
Date: Fri, 31 Jul 2020 14:50:54 +0800
Subject: [PATCH 06/15] include max slots in inventory command/webhook

---
 src/classes/Commands.ts       |  7 +++++-
 src/classes/DiscordWebhook.ts |  5 +++-
 src/classes/MyHandler.ts      | 43 +++++++++++++++++++++++++++++++++++
 3 files changed, 53 insertions(+), 2 deletions(-)

diff --git a/src/classes/Commands.ts b/src/classes/Commands.ts
index 3b73d6a99..1d2ea1c3a 100644
--- a/src/classes/Commands.ts
+++ b/src/classes/Commands.ts
@@ -422,7 +422,12 @@ export = class Commands {
 
     private inventoryCommand(steamID: SteamID): void {
         const currentItems = this.bot.inventoryManager.getInventory().getTotalItems();
-        this.bot.sendMessage(steamID, `🎒 My crrent items in my inventory: ${currentItems}`);
+        const backpackSlots = (this.bot.handler as MyHandler).getBackpackSlots();
+
+        this.bot.sendMessage(
+            steamID,
+            `🎒 My crrent items in my inventory: ${currentItems + (backpackSlots !== 0 ? '/' + backpackSlots : '')}`
+        );
     }
 
     private autoKeysCommand(steamID: SteamID): void {
diff --git a/src/classes/DiscordWebhook.ts b/src/classes/DiscordWebhook.ts
index 719854ca5..bdbdf51cb 100644
--- a/src/classes/DiscordWebhook.ts
+++ b/src/classes/DiscordWebhook.ts
@@ -321,6 +321,7 @@ export = class DiscordWebhook {
         tradeSummary: string,
         pureStock: string[],
         currentItems: number,
+        backpackSlots: number,
         invalidItemsCombine: string[],
         keyPrice: { buy: Currencies; sell: Currencies },
         value: { diff: number; diffRef: number; diffKey: string },
@@ -483,7 +484,9 @@ export = class DiscordWebhook {
                                   }`
                                 : '') +
                             (isShowPureStock ? `\n💰 Pure stock: ${pureStock.join(', ').toString()}` : '') +
-                            (isShowInventory ? `\n🎒 Total items: ${currentItems}` : '') +
+                            (isShowInventory
+                                ? `\n🎒 Total items: ${currentItems + (backpackSlots !== 0 ? '/' + backpackSlots : '')}`
+                                : '') +
                             (AdditionalNotes ? '\n' + AdditionalNotes : ''),
                         color: botEmbedColor
                     }
diff --git a/src/classes/MyHandler.ts b/src/classes/MyHandler.ts
index ee453cdea..a4be6e1ed 100644
--- a/src/classes/MyHandler.ts
+++ b/src/classes/MyHandler.ts
@@ -7,6 +7,7 @@ import Inventory from './Inventory';
 import { UnknownDictionary } from '../types/common';
 import { Currency } from '../types/TeamFortress2';
 import SKU from 'tf2-sku';
+import request from '@nicklason/request-retry';
 
 import SteamUser from 'steam-user';
 import TradeOfferManager, { TradeOffer, PollData } from 'steam-tradeoffer-manager';
@@ -99,6 +100,8 @@ export = class MyHandler extends Handler {
 
     private scrapAdjustmentValue = 0;
 
+    private backpackSlots = 0;
+
     private isAcceptedWithInvalidItemsOrOverstocked = false;
 
     recentlySentMessage: UnknownDictionary<number> = {};
@@ -239,6 +242,10 @@ export = class MyHandler extends Handler {
         return this.customGameName;
     }
 
+    getBackpackSlots(): number {
+        return this.backpackSlots;
+    }
+
     getUserAutokeys(): {
         enabled: boolean;
         status: boolean;
@@ -304,6 +311,9 @@ export = class MyHandler extends Handler {
         this.bot.client.gamesPlayed([this.customGameName, 440]);
         this.bot.client.setPersona(SteamUser.EPersonaState.Online);
 
+        // GetBackpackSlots
+        this.requestBackpackSlots();
+
         // Smelt / combine metal if needed
         this.keepMetalSupply();
 
@@ -1242,6 +1252,7 @@ export = class MyHandler extends Handler {
                         offer.summarizeWithLink(this.bot.schema),
                         pureStock,
                         currentItems,
+                        this.backpackSlots,
                         invalidItemsCombine,
                         keyPrice,
                         value,
@@ -2389,6 +2400,38 @@ Autokeys status:-
         }
     }
 
+    requestBackpackSlots(): Promise<void> {
+        return new Promise((resolve, reject) => {
+            request(
+                {
+                    url: 'https://api.steampowered.com/IEconItems_440/GetPlayerItems/v0001/',
+                    method: 'GET',
+                    qs: {
+                        key: this.bot.manager.apiKey,
+                        steamid: this.bot.client.steamID.getSteamID64()
+                    },
+                    json: true,
+                    gzip: true
+                },
+                (err, response, body) => {
+                    if (err) {
+                        return reject(err);
+                    }
+
+                    if (body.result.status != 1) {
+                        err = new Error(body.result.statusDetail);
+                        err.status = body.result.status;
+                        return reject(err);
+                    }
+
+                    this.backpackSlots = body.result.num_backpack_slots;
+
+                    return resolve();
+                }
+            );
+        });
+    }
+
     private itemList(offer: TradeOffer): { their: string[]; our: string[] } {
         const items: { our: {}; their: {} } = offer.data('dict');
         const their: string[] = [];

From c6b17bd944901785d0d92fed839d7dc81309bf3f Mon Sep 17 00:00:00 2001
From: idinium96 <idin100msc@gmail.com>
Date: Fri, 31 Jul 2020 15:05:55 +0800
Subject: [PATCH 07/15] if not give price on in_items, accept our<=their

---
 src/classes/MyHandler.ts | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/src/classes/MyHandler.ts b/src/classes/MyHandler.ts
index a4be6e1ed..d6eba2587 100644
--- a/src/classes/MyHandler.ts
+++ b/src/classes/MyHandler.ts
@@ -1035,6 +1035,13 @@ export = class MyHandler extends Handler {
         if (wrongAboutOffer.length !== 0) {
             const reasons = wrongAboutOffer.map(wrong => wrong.reason);
             const uniqueReasons = reasons.filter(reason => reasons.includes(reason));
+            const moreThanOnly =
+                (process.env.DISABLE_GIVE_PRICE_TO_INVALID_ITEMS === 'false' ||
+                    process.env.DISABLE_ACCEPT_OVERSTOCKED_OVERPAY === 'false') &&
+                exchange.our.value < exchange.their.value;
+            const moreThanOrEqualTo =
+                process.env.DISABLE_GIVE_PRICE_TO_INVALID_ITEMS === 'true' &&
+                exchange.our.value <= exchange.their.value;
 
             // TO DO: Counter offer?
             //
@@ -1053,13 +1060,13 @@ export = class MyHandler extends Handler {
                 ((uniqueReasons.includes('🟨INVALID_ITEMS') &&
                     process.env.DISABLE_ACCEPT_INVALID_ITEMS_OVERPAY !== 'true') ||
                     (uniqueReasons.includes('🟦OVERSTOCKED') &&
-                        !(process.env.DISABLE_ACCEPT_OVERSTOCKED_OVERPAY !== 'false'))) &&
+                        process.env.DISABLE_ACCEPT_OVERSTOCKED_OVERPAY !== 'true')) &&
                 !(
                     uniqueReasons.includes('🟥INVALID_VALUE') ||
                     uniqueReasons.includes('🟫DUPED_ITEMS') ||
                     uniqueReasons.includes('🟪DUPE_CHECK_FAILED')
                 ) &&
-                exchange.our.value < exchange.their.value &&
+                (moreThanOnly || moreThanOrEqualTo) &&
                 exchange.our.value !== 0
             ) {
                 this.isAcceptedWithInvalidItemsOrOverstocked = true;

From 686f464390e7339a3175704d1b3c23bac8c41ee9 Mon Sep 17 00:00:00 2001
From: idinium96 <idin100msc@gmail.com>
Date: Fri, 31 Jul 2020 16:08:08 +0800
Subject: [PATCH 08/15] bptf/steam down, put to review instead of ignore

---
 src/classes/DiscordWebhook.ts | 19 ++++++---
 src/classes/MyHandler.ts      | 79 +++++++++++++++++++++--------------
 2 files changed, 61 insertions(+), 37 deletions(-)

diff --git a/src/classes/DiscordWebhook.ts b/src/classes/DiscordWebhook.ts
index bdbdf51cb..b349ec868 100644
--- a/src/classes/DiscordWebhook.ts
+++ b/src/classes/DiscordWebhook.ts
@@ -158,6 +158,7 @@ export = class DiscordWebhook {
     sendOfferReview(
         offer: TradeOffer,
         reason: string,
+        reasons: string,
         pureStock: string[],
         time: string,
         tradeSummary: string,
@@ -177,12 +178,12 @@ export = class DiscordWebhook {
         let noMentionOnInvalidValue = false;
         if (process.env.DISCORD_WEBHOOK_REVIEW_OFFER_DISABLE_MENTION_INVALID_VALUE !== 'false') {
             if (
-                reason.includes('🟥INVALID_VALUE') &&
+                reasons.includes('🟥INVALID_VALUE') &&
                 !(
-                    reason.includes('🟨INVALID_ITEMS') ||
-                    reason.includes('🟦OVERSTOCKED') ||
-                    reason.includes('🟫DUPED_ITEMS') ||
-                    reason.includes('🟪DUPE_CHECK_FAILED')
+                    reasons.includes('🟨INVALID_ITEMS') ||
+                    reasons.includes('🟦OVERSTOCKED') ||
+                    reasons.includes('🟫DUPED_ITEMS') ||
+                    reasons.includes('🟪DUPE_CHECK_FAILED')
                 )
             ) {
                 noMentionOnInvalidValue = true;
@@ -259,7 +260,13 @@ export = class DiscordWebhook {
                         },
                         title: '',
                         description:
-                            `⚠️ An offer sent by ${partnerNameNoFormat} is waiting for review.\nReason: ${reason}\n\n__Offer Summary__:\n` +
+                            `⚠️ An offer sent by ${partnerNameNoFormat} is waiting for review.\nReason: ${
+                                reason === '⬜BACKPACKTF_DOWN'
+                                    ? '⬜BACKPACKTF_DOWN - failed to check banned status'
+                                    : reason === '⬜STEAM_DOWN'
+                                    ? '⬜STEAM_DOWN - failed to check escrow status'
+                                    : reasons
+                            }\n\n__Offer Summary__:\n` +
                             tradeSummary.replace('Asked:', '**Asked:**').replace('Offered:', '**Offered:**') +
                             (value.diff > 0
                                 ? `\n📈 ***Profit from overpay:*** ${value.diffRef} ref` +
diff --git a/src/classes/MyHandler.ts b/src/classes/MyHandler.ts
index d6eba2587..307fdc039 100644
--- a/src/classes/MyHandler.ts
+++ b/src/classes/MyHandler.ts
@@ -954,7 +954,7 @@ export = class MyHandler extends Handler {
             }
         } catch (err) {
             log.warn('Failed to check escrow: ', err);
-            return;
+            return { action: 'skip', reason: '⬜STEAM_DOWN' };
         }
 
         offer.log('info', 'checking bans...');
@@ -968,7 +968,7 @@ export = class MyHandler extends Handler {
             }
         } catch (err) {
             log.warn('Failed to check banned: ', err);
-            return;
+            return { action: 'skip', reason: '⬜BACKPACKTF_DOWN' };
         }
 
         if (this.dupeCheckEnabled && assetidsToCheck.length > 0) {
@@ -1459,40 +1459,51 @@ export = class MyHandler extends Handler {
                     (itemsList.their.includes('5021;6') ? `${value.diffKey}]` : `${value.diffRef} ref]`);
             }
             // Notify partner and admin that the offer is waiting for manual review
-            this.bot.sendMessage(
-                offer.partner,
-                `⚠️ Your offer is waiting for review.\nReason: ${reasons.join(', ')}` +
-                    (process.env.DISABLE_SHOW_REVIEW_OFFER_SUMMARY !== 'true'
-                        ? '\n\nYour offer summary:\n' +
-                          offer
-                              .summarize(this.bot.schema)
-                              .replace('Asked', '  My side')
-                              .replace('Offered', 'Your side') +
-                          (reasons.includes('🟥INVALID_VALUE') && !reasons.includes('🟨INVALID_ITEMS')
-                              ? missingPureNote
-                              : '') +
-                          (process.env.DISABLE_REVIEW_OFFER_NOTE !== 'true'
-                              ? `\n\nNote:\n${reviewReasons.join('\n')}`
-                              : '')
-                        : '') +
-                    (process.env.ADDITIONAL_NOTE
-                        ? '\n\n' +
-                          process.env.ADDITIONAL_NOTE.replace(
-                              /%keyRate%/g,
-                              `${keyPrice.sell.metal.toString()} ref`
-                          ).replace(/%pureStock%/g, pureStock.join(', ').toString())
-                        : '') +
-                    (process.env.DISABLE_SHOW_CURRENT_TIME !== 'true'
-                        ? `\n\nMy owner time is currently at ${timeWithEmojis.emoji} ${timeWithEmojis.time +
-                              (timeWithEmojis.note !== '' ? `. ${timeWithEmojis.note}.` : '.')}`
-                        : '')
-            );
+            if (reason === '⬜BACKPACKTF_DOWN' || reason === '⬜STEAM_DOWN') {
+                this.bot.sendMessage(
+                    offer.partner,
+                    (reason === '⬜BACKPACKTF_DOWN' ? 'Backpack.tf' : 'Steam') +
+                        ' is down and I failed to check your ' +
+                        (reason === '⬜BACKPACKTF_DOWN' ? 'backpack.tf' : 'Escrow') +
+                        ' status, please wait for my owner to manually accept/decline your offer.'
+                );
+            } else {
+                this.bot.sendMessage(
+                    offer.partner,
+                    `⚠️ Your offer is waiting for review.\nReason: ${reasons.join(', ')}` +
+                        (process.env.DISABLE_SHOW_REVIEW_OFFER_SUMMARY !== 'true'
+                            ? '\n\nYour offer summary:\n' +
+                              offer
+                                  .summarize(this.bot.schema)
+                                  .replace('Asked', '  My side')
+                                  .replace('Offered', 'Your side') +
+                              (reasons.includes('🟥INVALID_VALUE') && !reasons.includes('🟨INVALID_ITEMS')
+                                  ? missingPureNote
+                                  : '') +
+                              (process.env.DISABLE_REVIEW_OFFER_NOTE !== 'true'
+                                  ? `\n\nNote:\n${reviewReasons.join('\n')}`
+                                  : '')
+                            : '') +
+                        (process.env.ADDITIONAL_NOTE
+                            ? '\n\n' +
+                              process.env.ADDITIONAL_NOTE.replace(
+                                  /%keyRate%/g,
+                                  `${keyPrice.sell.metal.toString()} ref`
+                              ).replace(/%pureStock%/g, pureStock.join(', ').toString())
+                            : '') +
+                        (process.env.DISABLE_SHOW_CURRENT_TIME !== 'true'
+                            ? `\n\nMy owner time is currently at ${timeWithEmojis.emoji} ${timeWithEmojis.time +
+                                  (timeWithEmojis.note !== '' ? `. ${timeWithEmojis.note}.` : '.')}`
+                            : '')
+                );
+            }
             if (
                 process.env.DISABLE_DISCORD_WEBHOOK_OFFER_REVIEW === 'false' &&
                 process.env.DISCORD_WEBHOOK_REVIEW_OFFER_URL
             ) {
                 this.discord.sendOfferReview(
                     offer,
+                    reason,
                     reasons.join(', '),
                     pureStock,
                     timeWithEmojis.time,
@@ -1510,7 +1521,13 @@ export = class MyHandler extends Handler {
                 const offerMessage = offer.message;
                 this.bot.messageAdmins(
                     `⚠️ Offer #${offer.id} from ${offer.partner} is waiting for review.` +
-                        `\nReason: ${meta.uniqueReasons.join(', ')}` +
+                        `\nReason: ${
+                            reason === '⬜BACKPACKTF_DOWN'
+                                ? '⬜BACKPACKTF_DOWN - failed to check banned status'
+                                : reason === '⬜STEAM_DOWN'
+                                ? '⬜STEAM_DOWN - failed to check escrow status'
+                                : meta.uniqueReasons.join(', ')
+                        }` +
                         `\n\nOffer Summary:\n${offer.summarize(this.bot.schema)}${
                             value.diff > 0
                                 ? `\n📈 Profit from overpay: ${value.diffRef} ref` +

From 5db79c8f43306342a6d2552c8248eaa87314c997 Mon Sep 17 00:00:00 2001
From: idinium96 <idin100msc@gmail.com>
Date: Fri, 31 Jul 2020 20:09:56 +0800
Subject: [PATCH 09/15] much better

---
 src/classes/MyHandler.ts | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/src/classes/MyHandler.ts b/src/classes/MyHandler.ts
index 307fdc039..27297a790 100644
--- a/src/classes/MyHandler.ts
+++ b/src/classes/MyHandler.ts
@@ -1035,13 +1035,14 @@ export = class MyHandler extends Handler {
         if (wrongAboutOffer.length !== 0) {
             const reasons = wrongAboutOffer.map(wrong => wrong.reason);
             const uniqueReasons = reasons.filter(reason => reasons.includes(reason));
-            const moreThanOnly =
-                (process.env.DISABLE_GIVE_PRICE_TO_INVALID_ITEMS === 'false' ||
-                    process.env.DISABLE_ACCEPT_OVERSTOCKED_OVERPAY === 'false') &&
-                exchange.our.value < exchange.their.value;
-            const moreThanOrEqualTo =
-                process.env.DISABLE_GIVE_PRICE_TO_INVALID_ITEMS === 'true' &&
-                exchange.our.value <= exchange.their.value;
+
+            const acceptingCondition =
+                process.env.DISABLE_GIVE_PRICE_TO_INVALID_ITEMS === 'false' ||
+                process.env.DISABLE_ACCEPT_OVERSTOCKED_OVERPAY === 'false'
+                    ? exchange.our.value < exchange.their.value
+                    : process.env.DISABLE_GIVE_PRICE_TO_INVALID_ITEMS === 'true'
+                    ? exchange.our.value <= exchange.their.value
+                    : false;
 
             // TO DO: Counter offer?
             //
@@ -1066,7 +1067,7 @@ export = class MyHandler extends Handler {
                     uniqueReasons.includes('🟫DUPED_ITEMS') ||
                     uniqueReasons.includes('🟪DUPE_CHECK_FAILED')
                 ) &&
-                (moreThanOnly || moreThanOrEqualTo) &&
+                acceptingCondition &&
                 exchange.our.value !== 0
             ) {
                 this.isAcceptedWithInvalidItemsOrOverstocked = true;

From 8e6641ed30b1ae41306810ea870d1f22f2ec5702 Mon Sep 17 00:00:00 2001
From: idinium96 <idin100msc@gmail.com>
Date: Fri, 31 Jul 2020 20:42:27 +0800
Subject: [PATCH 10/15] autokeys: remove key from pricelist

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

diff --git a/src/classes/MyHandler.ts b/src/classes/MyHandler.ts
index 27297a790..0ecb36349 100644
--- a/src/classes/MyHandler.ts
+++ b/src/classes/MyHandler.ts
@@ -344,8 +344,8 @@ export = class MyHandler extends Handler {
             }
 
             if (process.env.ENABLE_AUTO_SELL_AND_BUY_KEYS === 'true' && this.checkAutokeysStatus === true) {
-                log.debug('Disabling Autokeys...');
-                this.updateToDisableAutokeys();
+                log.debug('Disabling Autokeys and removing key from pricelist...');
+                this.removeAutoKeys();
             }
 
             this.bot.listings.removeAll().asCallback(function(err) {
@@ -1774,7 +1774,7 @@ Autokeys status:-
                 this.alreadyUpdatedToBank = false;
                 this.alreadyUpdatedToBuy = false;
                 this.alreadyUpdatedToSell = false;
-                this.updateToDisableAutokeys();
+                this.removeAutoKeys();
             } else if (isRemoveAutoKeys && !isEnableKeyBanking) {
                 // disable Autokeys when conditions to disable Autokeys matched
                 this.isBuyingKeys = false;
@@ -1784,7 +1784,7 @@ Autokeys status:-
                 this.alreadyUpdatedToBank = false;
                 this.alreadyUpdatedToBuy = false;
                 this.alreadyUpdatedToSell = false;
-                this.updateToDisableAutokeys();
+                this.removeAutoKeys();
             } else if (isAlertAdmins && isAlreadyAlert !== true) {
                 // alert admins when low pure
                 this.isBuyingKeys = false;

From 6b5c019ef3655d2f7108c4d727ac243ee6462607 Mon Sep 17 00:00:00 2001
From: idinium96 <idin100msc@gmail.com>
Date: Fri, 31 Jul 2020 20:43:56 +0800
Subject: [PATCH 11/15] update README

---
 README.md | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/README.md b/README.md
index ff5b7b69e..5d284f60e 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 # tf2autobot
 
 A free and open source fully automated TF2 trading bot advertising on www.backpack.tf using prices from www.prices.tf.
-**tf2autobot** is an improved version of the original **tf2-automatic** made by [Nicklason](https://github.com/Nicklason). You can find out more about the original repository [here](https://github.com/Nicklason/tf2-automatic).
+**tf2autobot** is an improved and feature rich version of the original **tf2-automatic** made by [Nicklason](https://github.com/Nicklason). You can find out more about the original repository [here](https://github.com/Nicklason/tf2-automatic).
 
 ![GitHub package version](https://img.shields.io/github/package-json/v/idinium96/tf2autobot.svg)
 [![Build Status](https://img.shields.io/github/workflow/status/idinium96/tf2autobot/CI/development)](https://github.com/idinium96/tf2autobot/actions)
@@ -75,7 +75,7 @@ The original tf2-automatic repository already have a lot of features, but some f
 - automatically restart your bot on queue problem, and automatically relist if backpack.tf does not synchronized with your bot listings on Autokeys (sometimes it's set to automatically buy keys, but at backpack.tf, it's listed to sell.)
 - use emojis on almost all messages
 - list out every items on each offer review reasons
-- New added commands: "!pure", "!time", "!delete", "!check", "!block", "!unblock", "!autokeys", "!craftweapon" and "!uncraftweapon" commands
+- New added commands: "!pure", "!time", "!delete", "!check", "!block", "!unblock", "!autokeys", "!inventory", "!craftweapon" and "!uncraftweapon" commands
 - and more to come!
 
 ## Added features
@@ -143,7 +143,7 @@ Some screenshots:
 
 ![autokeys3](https://user-images.githubusercontent.com/47635037/84581310-9c1cd100-ae12-11ea-80fa-085ad8bff73e.png)
 
-You can see codes on how this feature works [here](https://github.com/idinium96/tf2autobot/blob/master/src/classes/MyHandler.ts#L1546-L2140).
+You can see codes on how this feature works [here](https://github.com/idinium96/tf2autobot/blob/master/src/classes/MyHandler.ts#L1582-L2176).
 
 ### Emojis and more commands added
 
@@ -294,7 +294,7 @@ Time will be use in "!time" command and
 - `DISCORD_WEBHOOK_TRADE_SUMMARY_SHOW_QUICK_LINKS`: [true|false] - Show trade partner quick links to their Steam profile, backpack.tf and SteamREP pages.
 - `DISCORD_WEBHOOK_TRADE_SUMMARY_SHOW_KEY_RATE`: [true|false] - self explained.
 - `DISCORD_WEBHOOK_TRADE_SUMMARY_SHOW_PURE_STOCK`: [true|false] - self explained.
-- `DISCORD_WEBHOOK_TRADE_SUMMARY_SHOW_INVENTORY`: [true|false] - Show total current items in your bot inventory (I have tried to include `/max slot` but it's not working).
+- `DISCORD_WEBHOOK_TRADE_SUMMARY_SHOW_INVENTORY`: [true|false] - Show total current items in your bot inventory.
 - `DISCORD_WEBHOOK_TRADE_SUMMARY_ADDITIONAL_DESCRIPTION_NOTE` - Notes.
 - `DISCORD_WEBHOOK_TRADE_SUMMARY_MENTION_OWNER` [true|false] - Set it to `true` if you want your bot to mention on every successful trades.
 - `DISCORD_WEBHOOK_TRADE_SUMMARY_MENTION_OWNER_ONLY_ITEMS_SKU` [StringArray] - Support multiple items sku, let say you want to be mentioned on every unusual and australium trades, just do `[";5;u", ";11;australium"]`, or if you want to mention on specific item, just fill in the full item sku like `["725;6;uncraftable"]`, then to add more, just separate it with a comma between each sku string.
@@ -321,7 +321,7 @@ Time will be use in "!time" command and
 ### Manual Review settings
 - `ENABLE_MANUAL_REVIEW`: [true|false] - Set to `true` if you want any INVALID_VALUE/INVALID_ITEMS/OVERSTOCKED/DUPED_ITEMS/DUPE_CHECK_FAILED trades to be reviewed by you.
 - `DISABLE_SHOW_REVIEW_OFFER_SUMMARY`: [true|false] - set to `true` if you do not want your bot to show offer summary to trade partner, but it will only notify trade partner that their offer is being hold for a review.
-- `DISABLE_REVIEW_OFFER_NOTE`: [true|false] - If set to `false`, it will show note on [each error](https://github.com/idinium96/tf2autobot/blob/master/src/classes/MyHandler.ts#L1340-L1471)
+- `DISABLE_REVIEW_OFFER_NOTE`: [true|false] - If set to `false`, it will show note on [each error](https://github.com/idinium96/tf2autobot/blob/master/src/classes/MyHandler.ts#L1358-L1572)
 - `DISABLE_SHOW_CURRENT_TIME`: [true|false] - If set to `false`, it will show owner time on offer review notification that trade partner will received.
 
 - `DISABLE_ACCEPT_INVALID_ITEMS_OVERPAY`: [true|false] - Default: `false`. Set to `true` if you do not want your bot to accept a trade with INVALID_ITEMS but with their value more or equal to our value.

From b4680156f59f37281ab7ae83005ca2e3048c439d Mon Sep 17 00:00:00 2001
From: idinium96 <idin100msc@gmail.com>
Date: Fri, 31 Jul 2020 21:01:55 +0800
Subject: [PATCH 12/15] add accepted escrow trade message

---
 src/classes/MyHandler.ts | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/classes/MyHandler.ts b/src/classes/MyHandler.ts
index 0ecb36349..50454b4e8 100644
--- a/src/classes/MyHandler.ts
+++ b/src/classes/MyHandler.ts
@@ -1137,6 +1137,15 @@ export = class MyHandler extends Handler {
                             ? process.env.CUSTOM_SUCCESS_MESSAGE
                             : '/pre ✅ Success! The offer went through successfully.'
                     );
+                } else if (offer.state === TradeOfferManager.ETradeOfferState.InEscrow) {
+                    this.bot.sendMessage(
+                        offer.partner,
+                        '✅ Success! The offer went through successfully, but you will receive your items after ~15 days.' +
+                            ' Please use Steam Guard Mobile Authenticator so you will no longer need to wait like this in the future.' +
+                            '\nRead:\n' +
+                            '• Steam Guard Mobile Authenticator - https://support.steampowered.com/kb_article.php?ref=8625-WRAH-9030' +
+                            '• Steam Guard: How to set up a Steam Guard Mobile Authenticator - https://support.steampowered.com/kb_article.php?ref=4440-RTUI-9218'
+                    );
                 } else if (offer.state === TradeOfferManager.ETradeOfferState.Declined) {
                     const offerReason: { reason: string } = offer.data('action');
                     const keyPrice = this.bot.pricelist.getKeyPrices();

From 555536f719b537e1bc10497c2a733ed460707cdc Mon Sep 17 00:00:00 2001
From: idinium96 <idin100msc@gmail.com>
Date: Fri, 31 Jul 2020 21:48:16 +0800
Subject: [PATCH 13/15] add !adjustrate command to manually adjust keyrate

---
 README.md                |  2 +-
 src/classes/Commands.ts  | 29 +++++++++++++++++++++++++++++
 src/classes/Pricelist.ts | 15 +++++++++++++++
 3 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 5d284f60e..c04875bb7 100644
--- a/README.md
+++ b/README.md
@@ -75,7 +75,7 @@ The original tf2-automatic repository already have a lot of features, but some f
 - automatically restart your bot on queue problem, and automatically relist if backpack.tf does not synchronized with your bot listings on Autokeys (sometimes it's set to automatically buy keys, but at backpack.tf, it's listed to sell.)
 - use emojis on almost all messages
 - list out every items on each offer review reasons
-- New added commands: "!pure", "!time", "!delete", "!check", "!block", "!unblock", "!autokeys", "!inventory", "!craftweapon" and "!uncraftweapon" commands
+- New added commands: "!pure", "!time", "!delete", "!check", "!block", "!unblock", "!autokeys", "!inventory", "!adjustrate", "!craftweapon" and "!uncraftweapon" commands
 - and more to come!
 
 ## Added features
diff --git a/src/classes/Commands.ts b/src/classes/Commands.ts
index 1d2ea1c3a..6172eef6d 100644
--- a/src/classes/Commands.ts
+++ b/src/classes/Commands.ts
@@ -52,6 +52,7 @@ const ADMIN_COMMANDS: string[] = [
     '!withdraw <name=>&<amount=> - Used to withdraw items',
     '!add - Add a pricelist entry ➕',
     '!update - Update a pricelist entry',
+    '!adjustrate buy.metal=<buying price>&sell.metal=<selling price> - Manually adjust key rate (reset on restart, self-update when key rate changes)',
     '!remove <sku=> OR <item=> - Remove a pricelist entry ➖',
     '!get <sku=> OR <item=> - Get raw information about a pricelist entry',
     '!pricecheck <sku=> OR <item=> - Requests an item to be priced by PricesTF',
@@ -126,6 +127,8 @@ export = class Commands {
             this.uncraftweaponCommand(steamID);
         } else if (command === 'rate') {
             this.rateCommand(steamID);
+        } else if (command === 'adjustrate' && isAdmin) {
+            this.adjustKeyRateCommand(steamID, message);
         } else if (command === 'message') {
             this.messageCommand(steamID, message);
         } else if (command === 'cart') {
@@ -533,6 +536,32 @@ export = class Commands {
         );
     }
 
+    private adjustKeyRateCommand(steamID: SteamID, message: string): void {
+        const params = CommandParser.parseParams(CommandParser.removeCommand(message));
+
+        if (!params || (params.buy === undefined && params.sell === undefined)) {
+            this.bot.sendMessage(
+                steamID,
+                '❌ You must include both buy AND sell price, example - "!adjustkeyrate sell.metal=56.33&buy.metal=56.22"'
+            );
+            return;
+        }
+
+        if (+params.buy.metal > +params.sell.metal) {
+            this.bot.sendMessage(steamID, '❌ Sell price must be higher than buy price.');
+            return;
+        }
+
+        const buyKeys = +params.buy.keys || 0;
+        const buyMetal = +params.buy.metal || 0;
+        const sellKeys = +params.sell.keys || 0;
+        const sellMetal = +params.sell.metal || 0;
+        const buy = { keys: buyKeys, metal: buyMetal };
+        const sell = { keys: sellKeys, metal: sellMetal };
+
+        this.bot.pricelist.adjustKeyRate(buy, sell);
+    }
+
     private messageCommand(steamID: SteamID, message: string): void {
         const isAdmin = this.bot.isAdmin(steamID);
         const parts = message.split(' ');
diff --git a/src/classes/Pricelist.ts b/src/classes/Pricelist.ts
index 2c854f7cf..3f0887a07 100644
--- a/src/classes/Pricelist.ts
+++ b/src/classes/Pricelist.ts
@@ -378,6 +378,21 @@ export default class Pricelist extends EventEmitter {
         });
     }
 
+    adjustKeyRate(buy: { keys: number; metal: number }, sell: { keys: number; metal: number }): void {
+        this.keyPrices = {
+            buy: new Currencies(buy),
+            sell: new Currencies(sell)
+        };
+        const entryKey = this.getPrice('5021;6');
+
+        if (entryKey !== null && entryKey.autoprice) {
+            // The price of a key in the pricelist can be different from keyPrices because the pricelist is not updated
+            entryKey.buy = new Currencies(buy);
+            entryKey.sell = new Currencies(sell);
+            entryKey.time = moment().valueOf();
+        }
+    }
+
     private updateOldPrices(old: Entry[]): Promise<void> {
         log.debug('Getting pricelist...');
 

From 01797daa94ae2c07e92dbc33d37fe4aad7d33e26 Mon Sep 17 00:00:00 2001
From: idinium96 <idin100msc@gmail.com>
Date: Fri, 31 Jul 2020 22:01:27 +0800
Subject: [PATCH 14/15] add success on key rate adjustment message

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

diff --git a/src/classes/Commands.ts b/src/classes/Commands.ts
index 6172eef6d..cb78b4103 100644
--- a/src/classes/Commands.ts
+++ b/src/classes/Commands.ts
@@ -560,6 +560,8 @@ export = class Commands {
         const sell = { keys: sellKeys, metal: sellMetal };
 
         this.bot.pricelist.adjustKeyRate(buy, sell);
+
+        this.bot.sendMessage(steamID, '✅ Key rate adjusted to ' + new Currencies(buy) + '/' + new Currencies(sell));
     }
 
     private messageCommand(steamID: SteamID, message: string): void {

From 52cb17fae893e3563b6c78b739cce7ae591c256c Mon Sep 17 00:00:00 2001
From: idinium96 <idin100msc@gmail.com>
Date: Fri, 31 Jul 2020 22:18:11 +0800
Subject: [PATCH 15/15] fix autokeys command

---
 src/classes/Commands.ts  | 10 ++++++++--
 src/classes/MyHandler.ts |  2 +-
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/classes/Commands.ts b/src/classes/Commands.ts
index cb78b4103..f941a3526 100644
--- a/src/classes/Commands.ts
+++ b/src/classes/Commands.ts
@@ -501,11 +501,17 @@ export = class Commands {
                     ? 'Banking' + (autokeys.scrapAdjustmentEnabled ? ' (default price)' : '')
                     : autokeys.isBuying
                     ? 'Buying for ' +
-                      Currencies.toRefined(keyPrices.buy.toValue() + autokeys.scrapAdjustmentValue).toString() +
+                      Currencies.toRefined(
+                          keyPrices.buy.toValue() +
+                              (autokeys.scrapAdjustmentEnabled ? autokeys.scrapAdjustmentValue : 0)
+                      ) +
                       ' ref' +
                       (autokeys.scrapAdjustmentEnabled ? ' (+' + autokeys.scrapAdjustmentValue + ' scrap)' : '')
                     : 'Selling for ' +
-                      Currencies.toRefined(keyPrices.sell.toValue() - autokeys.scrapAdjustmentValue).toString() +
+                      Currencies.toRefined(
+                          keyPrices.sell.toValue() -
+                              (autokeys.scrapAdjustmentEnabled ? autokeys.scrapAdjustmentValue : 0)
+                      ) +
                       ' ref' +
                       (autokeys.scrapAdjustmentEnabled ? ' (-' + autokeys.scrapAdjustmentValue + ' scrap)' : '')
                 : 'Not active'
diff --git a/src/classes/MyHandler.ts b/src/classes/MyHandler.ts
index 50454b4e8..e51d7f5fa 100644
--- a/src/classes/MyHandler.ts
+++ b/src/classes/MyHandler.ts
@@ -166,7 +166,7 @@ export = class MyHandler extends Handler {
             this.scrapAdjustmentValue = scrapValue;
         }
 
-        if (process.env.DISABLE_SCRAP_ADJUSTMENT === 'false') {
+        if (process.env.DISABLE_SCRAP_ADJUSTMENT !== 'true') {
             this.isUsingAutoPrice = false;
         }