Skip to content

Commit

Permalink
🔀Merge pull request #411 from TF2Autobot/development
Browse files Browse the repository at this point in the history
#411

## Added
- Mainly if you're using generic Unusual buy order feature:
    - ✨option to not automatically add bought Unusual item to the pricelist (usual because buying with Generic Unusual buy order - #412, [Wiki](https://github.com/TF2Autobot/tf2autobot/wiki/Configure-your-options.json-file#--automatic-add-invalid-unusual--)) - @joekiller
        - new options added:
            - `pricelist.autoAddInvalidUnusual.enable` (default is `false`)
            - `sendAlert.receivedUnusualNotInPricelist` (default if `true`)
- ✨HTTP API health check (and others in the future - #413, [Wiki](https://github.com/TF2Autobot/tf2autobot/wiki/Configuring-the-bot#api)) - @rennokki
    - new env variables:
       - `ENABLE_HTTP_API` (default is `false`)
       - `HTTP_API_PORT` (default is `3001`)
- ✨option to show proper item name in trade/offer summary (with "The", no short - #415, [Wiki](https://github.com/TF2Autobot/tf2autobot/wiki/Configure-your-options.json-file#-trade-summary-settings-)) - @idinium96
    - new options added:
        - `tradeSummary.showProperName` (default is `false`)

## Updates
- Partial price update feature (updated #337):
    - ⛔ do not partial price Mann Co. Supply Crate Key (#407) - @idinium96
    - ✅ add an option to exclude items for partial price update (#408, [Wiki](https://github.com/TF2Autobot/tf2autobot/wiki/Configure-your-options.json-file#--partial-price-update--)) - @idinium96
        - new options.json property: `pricelist.partialPriceUpdate.excludeSKU` (default is `[]`)
    - 💅 include name in partial price update summary (#409) - @idinium96
    - ⛔ do not perform usual update if grouped (#410) - @idinium96
- 🔨 refactor Bot.ts (#399) - @arik123
- 🔎🔑 keyPrices verification (2) and 🔨 small refactor on Pricelist.ts (#416) - @idinium96
- 🔕 ignore some error on fail action or to accept mobile confirmation (#419) - @idinium96 
- 🔨 refactor: use Map and Set (#420) - @idinium96
- 🎨 show amount offering/offered/taking/taken in overstocked/understocked review/trade summary (#421) - @idinium96
  • Loading branch information
idinium96 committed Mar 6, 2021
2 parents 95c6199 + b2c6102 commit d2b5224
Show file tree
Hide file tree
Showing 27 changed files with 1,057 additions and 406 deletions.
433 changes: 430 additions & 3 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"async": "^3.2.0",
"bluebird": "^3.7.2",
"bluebird-global": "^1.0.1",
"body-parser": "^1.19.0",
"bptf-listings-2": "^1.2.0",
"bptf-login-2": "^1.0.2",
"callback-queue": "^3.0.0",
Expand All @@ -38,6 +39,7 @@
"dot-prop": "^6.0.1",
"dotenv": "^8.2.0",
"eslint-config-prettier": "^8.1.0",
"express": "^4.17.1",
"graceful-fs": "^4.2.6",
"isobject": "^4.0.0",
"js-levenshtein": "^1.1.6",
Expand Down Expand Up @@ -73,6 +75,7 @@
"@types/bluebird-global": "^3.5.12",
"@types/cheerio": "^0.22.24",
"@types/death": "^1.1.1",
"@types/express": "^4.17.11",
"@types/graceful-fs": "^4.1.5",
"@types/jest": "^26.0.20",
"@types/pluralize": "0.0.29",
Expand All @@ -92,7 +95,7 @@
"prettier": "^2.2.1",
"socket.io-mock": "^1.3.1",
"ts-jest": "^26.5.3",
"typescript": "^4.2.2"
"typescript": "^4.2.3"
},
"private": true,
"_moduleAliases": {
Expand Down
11 changes: 11 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const { version: BOT_VERSION } = require('../package.json');
import { getPricer } from '@pricer/pricer';
import Pricer, { GetPricerFn } from './classes/Pricer';
import { loadOptions } from './classes/Options';
import HttpManager from './classes/HttpManager';

process.env.BOT_VERSION = BOT_VERSION as string;

Expand Down Expand Up @@ -130,4 +131,14 @@ void botManager.start(options).asCallback(err => {
if (err) {
throw err;
}

if (options.enableHttpApi) {
const httpManager = new HttpManager(options);

void httpManager.start().asCallback(err => {
if (err) {
throw err;
}
});
}
});
103 changes: 19 additions & 84 deletions src/classes/Bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import semver from 'semver';
import request from 'request-retry-dayjs';

import InventoryManager from './InventoryManager';
import Pricelist, { Entry, EntryData } from './Pricelist';
import Pricelist, { EntryData } from './Pricelist';
import Friends from './Friends';
import Trades from './Trades';
import Listings from './Listings';
Expand Down Expand Up @@ -106,48 +106,6 @@ export default class Bot {

private ready = false;

private handleLoggedOn: OmitThisParameter<() => void>;

private handleMessage: OmitThisParameter<(steamID: SteamID, message: string) => void>;

private handleFriendRelationship: OmitThisParameter<(steamID: SteamID, relationship: number) => void>;

private handleGroupRelationship: OmitThisParameter<(steamID: SteamID, relationship: number) => void>;

private handleWebSession: OmitThisParameter<(sessionID: string, cookies: string[]) => void>;

private handleSteamGuard: OmitThisParameter<
(domain: string, callback: (authCode: string) => void, lastCodeWrong: boolean) => void
>;

private handleLoginKey: OmitThisParameter<(loginKey: string) => void>;

private handleError: OmitThisParameter<(err: CustomError) => void>;

private handleSessionExpired: OmitThisParameter<() => void>;

private handleConfKeyNeeded: OmitThisParameter<
(tag: string, callback: (err: Error | null, time: number, confKey: string) => void) => void
>;

private handlePollData: OmitThisParameter<(pollData: TradeOfferManager.PollData) => void>;

private handleNewOffer: OmitThisParameter<(offer: TradeOfferManager.TradeOffer) => void>;

private handleOfferChanged: OmitThisParameter<(offer: TradeOfferManager.TradeOffer, oldState: number) => void>;

private handleOfferList: OmitThisParameter<
(filter: number, sent: TradeOfferManager.TradeOffer[], received: TradeOfferManager.TradeOffer[]) => void
>;

private handleHeartbeat: OmitThisParameter<(bumped: number) => void>;

private handlePricelist: OmitThisParameter<(pricelist: Entry[]) => void>;

private handlePriceChange: OmitThisParameter<(sku: string, price: Entry | null) => void>;

private receivedOfferChanged: OmitThisParameter<(offer: TradeOfferManager.TradeOffer, oldState: number) => void>;

constructor(public readonly botManager: BotManager, public options: Options, private priceSource: Pricer) {
this.botManager = botManager;

Expand Down Expand Up @@ -208,29 +166,6 @@ export default class Bot {
throw new Error('Invalid Item stats whitelist steamID');
}
});

this.handleLoggedOn = this.handler.onLoggedOn.bind(this.handler);
this.handleMessage = this.onMessage.bind(this);
this.handleFriendRelationship = this.handler.onFriendRelationship.bind(this.handler);
this.handleGroupRelationship = this.handler.onGroupRelationship.bind(this.handler);
this.handleWebSession = this.onWebSession.bind(this);
this.handleSteamGuard = this.onSteamGuard.bind(this);
this.handleLoginKey = this.handler.onLoginKey.bind(this.handler);
this.handleError = this.onError.bind(this);

this.handleSessionExpired = this.onSessionExpired.bind(this);
this.handleConfKeyNeeded = this.onConfKeyNeeded.bind(this);

this.handlePollData = this.handler.onPollData.bind(this.handler);
this.handleNewOffer = this.trades.onNewOffer.bind(this.trades);
this.handleOfferChanged = this.trades.onOfferChanged.bind(this.trades);
this.receivedOfferChanged = this.trades.onOfferChanged.bind(this.trades);
this.handleOfferList = this.trades.onOfferList.bind(this.trades);

this.handleHeartbeat = this.handler.onHeartbeat.bind(this);

this.handlePricelist = this.handler.onPricelist.bind(this.handler);
this.handlePriceChange = this.handler.onPriceChange.bind(this.handler);
}

isAdmin(steamID: SteamID | string): boolean {
Expand Down Expand Up @@ -376,28 +311,28 @@ export default class Bot {
};
let cookies: string[];

this.addListener(this.client, 'loggedOn', this.handleLoggedOn, false);
this.addListener(this.client, 'friendMessage', this.handleMessage, true);
this.addListener(this.client, 'friendRelationship', this.handleFriendRelationship, true);
this.addListener(this.client, 'groupRelationship', this.handleGroupRelationship, true);
this.addListener(this.client, 'webSession', this.handleWebSession, false);
this.addListener(this.client, 'steamGuard', this.handleSteamGuard, false);
this.addListener(this.client, 'loginKey', this.handleLoginKey, false);
this.addListener(this.client, 'error', this.handleError, false);
this.addListener(this.client, 'loggedOn', this.handler.onLoggedOn.bind(this.handler), false);
this.addListener(this.client, 'friendMessage', this.onMessage.bind(this), true);
this.addListener(this.client, 'friendRelationship', this.handler.onFriendRelationship.bind(this.handler), true);
this.addListener(this.client, 'groupRelationship', this.handler.onGroupRelationship.bind(this.handler), true);
this.addListener(this.client, 'webSession', this.onWebSession.bind(this), false);
this.addListener(this.client, 'steamGuard', this.onSteamGuard.bind(this), false);
this.addListener(this.client, 'loginKey', this.handler.onLoginKey.bind(this.handler), false);
this.addListener(this.client, 'error', this.onError.bind(this), false);

this.addListener(this.community, 'sessionExpired', this.handleSessionExpired, false);
this.addListener(this.community, 'confKeyNeeded', this.handleConfKeyNeeded, false);
this.addListener(this.community, 'sessionExpired', this.onSessionExpired.bind(this), false);
this.addListener(this.community, 'confKeyNeeded', this.onConfKeyNeeded.bind(this), false);

this.addListener(this.manager, 'pollData', this.handlePollData, false);
this.addListener(this.manager, 'newOffer', this.handleNewOffer, true);
this.addListener(this.manager, 'sentOfferChanged', this.handleOfferChanged, true);
this.addListener(this.manager, 'receivedOfferChanged', this.receivedOfferChanged, true);
this.addListener(this.manager, 'offerList', this.handleOfferList, true);
this.addListener(this.manager, 'pollData', this.handler.onPollData.bind(this.handler), false);
this.addListener(this.manager, 'newOffer', this.trades.onNewOffer.bind(this.trades), true);
this.addListener(this.manager, 'sentOfferChanged', this.trades.onOfferChanged.bind(this.trades), true);
this.addListener(this.manager, 'receivedOfferChanged', this.trades.onOfferChanged.bind(this.trades), true);
this.addListener(this.manager, 'offerList', this.trades.onOfferList.bind(this.trades), true);

this.addListener(this.listingManager, 'heartbeat', this.handleHeartbeat, true);
this.addListener(this.listingManager, 'heartbeat', this.handler.onHeartbeat.bind(this), true);

this.addListener(this.pricelist, 'pricelist', this.handlePricelist, false);
this.addListener(this.pricelist, 'price', this.handlePriceChange, true);
this.addListener(this.pricelist, 'pricelist', void this.handler.onPricelist.bind(this.handler), false);
this.addListener(this.pricelist, 'price', this.handler.onPriceChange.bind(this.handler), true);

return new Promise((resolve, reject) => {
async.eachSeries(
Expand Down
2 changes: 1 addition & 1 deletion src/classes/Carts/UserCart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,7 @@ export default class UserCart extends Cart {
if (opt.miscSettings.checkUses.duel && sku === '241;6') {
checkedDuel = true;
assetids = getAssetidsWithFullUses(inventoryDict.their[sku]);
} else if (opt.miscSettings.checkUses.noiseMaker && Object.keys(noiseMakers).includes(sku)) {
} else if (opt.miscSettings.checkUses.noiseMaker && noiseMakers.has(sku)) {
checkNoiseMaker = true;
assetids = getAssetidsWithFullUses(inventoryDict.their[sku]);
}
Expand Down
10 changes: 3 additions & 7 deletions src/classes/Commands/sub-classes/Manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -538,16 +538,12 @@ export default class ManagerCommands {
});

// Remove duplicate elements
const newlistingsSKUs: string[] = [];
listingsSKUs.forEach(sku => {
if (!newlistingsSKUs.includes(sku)) {
newlistingsSKUs.push(sku);
}
});
const newlistingsSKUs = new Set(listingsSKUs);
const uniqueSKUs = [...newlistingsSKUs];

const pricelist = this.bot.pricelist.getPrices.filter(entry => {
// First find out if lising for this item from bptf already exist.
const isExist = newlistingsSKUs.find(sku => entry.sku === sku);
const isExist = uniqueSKUs.find(sku => entry.sku === sku);

if (!isExist) {
// undefined - listing does not exist but item is in the pricelist
Expand Down
53 changes: 53 additions & 0 deletions src/classes/HttpManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */

import bodyParser from 'body-parser';
import express from 'express';
import log from '../lib/logger';
import Options from './Options';

export default class HttpManager {
/**
* The Express.js app.
*/
protected app: express.Application;

/**
* Initialize the HTTP manager.
*
* @param options - The options list.
*/
constructor(protected options: Options) {
this.app = express();
this.app.use(bodyParser.json());
this.app.use(bodyParser.urlencoded({ extended: false }));

this.registerRoutes();
}

/**
* Register the routes.
*/
protected registerRoutes(): void {
this.app.get('/health', (req, res) => res.send('OK'));
this.app.get('/uptime', (req, res) => res.json({ uptime: process.uptime() }));
}

/**
* Start the server.
*/
start(): Promise<void> {
return new Promise(resolve => {
this.app.listen(this.options.httpApiPort, () => {
log.debug(`HTTP Server started: http://127.0.0.1:${this.options.httpApiPort}`);
log.info(`This is NOT a HTTP API used to handle data within the bot.
It is solely for managing the bot programatically by providing healthchecks & uptime details.`);
log.info(`Please use the TF2Bot GUI v3+ as the main API source.`);
log.info(`https://github.com/TF2Autobot/tf2autobot-gui`);
resolve();
});
});
}
}
6 changes: 1 addition & 5 deletions src/classes/Inventory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,7 @@ export default class Inventory {
const attributesCount = Object.keys(attributes).length;

const isUses =
sku === '241;6'
? isFull(items[i], 'duel')
: Object.keys(noiseMakers).includes(sku)
? isFull(items[i], 'noise')
: null;
sku === '241;6' ? isFull(items[i], 'duel') : noiseMakers.has(sku) ? isFull(items[i], 'noise') : null;

if (attributesCount === 0 && isUses === null) {
(dict[sku] = dict[sku] || []).push({ id: items[i].id });
Expand Down
2 changes: 1 addition & 1 deletion src/classes/Listings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ export default class Listings {
const isCustomBuyNote = entry.note?.buy && intent === 0;
const isCustomSellNote = entry.note?.sell && intent === 1;
const isDueling = entry.sku === '241;6' && opt.miscSettings.checkUses.duel;
const isNoiseMaker = Object.keys(noiseMakers).includes(entry.sku) && opt.miscSettings.checkUses.noiseMaker;
const isNoiseMaker = noiseMakers.has(entry.sku) && opt.miscSettings.checkUses.noiseMaker;

if (isCustomBuyNote || isCustomSellNote) {
details = replaceDetails(intent === 0 ? entry.note.buy : entry.note.sell, entry, key);
Expand Down
42 changes: 17 additions & 25 deletions src/classes/MyHandler/MyHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -506,16 +506,12 @@ export default class MyHandler extends Handler {
});

// Remove duplicate elements
const newlistingsSKUs: string[] = [];
listingsSKUs.forEach(sku => {
if (!newlistingsSKUs.includes(sku)) {
newlistingsSKUs.push(sku);
}
});
const newlistingsSKUs = new Set(listingsSKUs);
const uniqueSKUs = [...newlistingsSKUs];

const pricelist = this.bot.pricelist.getPrices.filter(entry => {
// First find out if lising for this item from bptf already exist.
const isExist = newlistingsSKUs.find(sku => entry.sku === sku);
const isExist = uniqueSKUs.find(sku => entry.sku === sku);

if (!isExist) {
// undefined - listing does not exist but item is in the pricelist
Expand Down Expand Up @@ -914,12 +910,12 @@ export default class MyHandler extends Handler {
}
}

if (opt.miscSettings.checkUses.noiseMaker && offerSKUs.some(sku => Object.keys(noiseMakers).includes(sku))) {
const noiseMaker = (noiseMakerSKUs: string[], items: Dict) => {
if (opt.miscSettings.checkUses.noiseMaker && offerSKUs.some(sku => noiseMakers.has(sku))) {
const noiseMaker = (items: Dict) => {
let isNot25Uses = false;
const skus: string[] = [];

noiseMakerSKUs.forEach(sku => {
noiseMakers.forEach((name, sku: string) => {
if (items[sku] !== undefined) {
items[sku].forEach(item => {
isNot25Uses = item.isFullUses === false;
Expand All @@ -931,7 +927,7 @@ export default class MyHandler extends Handler {
return [isNot25Uses, skus] as [boolean, string[]];
};

const [isNot25Uses, skus] = noiseMaker(Object.keys(noiseMakers), items.their);
const [isNot25Uses, skus] = noiseMaker(items.their);
const isHasNoiseMaker = skus.some(sku => checkExist.getPrice(sku, true) !== null);
if (isNot25Uses && isHasNoiseMaker) {
// Noise Maker: Only decline if exist in pricelist
Expand Down Expand Up @@ -1095,7 +1091,8 @@ export default class MyHandler extends Handler {
sku: sku,
buying: isBuying,
diff: diff,
amountCanTrade: amountCanTrade
amountCanTrade: amountCanTrade,
amountOffered: amount
});

this.bot.listings.checkBySKU(match.sku, null, which === 'their', true);
Expand Down Expand Up @@ -1124,7 +1121,8 @@ export default class MyHandler extends Handler {
sku: sku,
selling: !isBuying,
diff: diff,
amountCanTrade: amountCanTrade
amountCanTrade: amountCanTrade,
amountTaking: amount
});

this.bot.listings.checkBySKU(match.sku, null, which === 'their', true);
Expand Down Expand Up @@ -1293,7 +1291,8 @@ export default class MyHandler extends Handler {
sku: '5021;6',
buying: isBuying,
diff: diff,
amountCanTrade: amountCanTrade
amountCanTrade: amountCanTrade,
amountOffered: itemsDict['their']['5021;6']
});

log.debug('OVERSTOCKED', {
Expand All @@ -1316,7 +1315,8 @@ export default class MyHandler extends Handler {
sku: '5021;6',
selling: !isBuying,
diff: diff,
amountCanTrade: amountCanTrade
amountCanTrade: amountCanTrade,
amountTaking: itemsDict['our']['5021;6']
});

this.bot.listings.checkBySKU('5021;6', null, false, true);
Expand Down Expand Up @@ -1366,16 +1366,8 @@ export default class MyHandler extends Handler {
}

const filterReasons = (reasons: string[]) => {
const filtered: string[] = [];

// Filter out duplicate reasons
reasons.forEach(reason => {
if (!filtered.includes(reason)) {
filtered.push(reason);
}
});

return filtered;
const filtered = new Set(reasons);
return [...filtered];
};

const manualReviewEnabled = opt.manualReview.enable;
Expand Down
Loading

0 comments on commit d2b5224

Please sign in to comment.