Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: enhanced command handling #1476

Open
wants to merge 32 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
100f4b2
feat: created command handler (#1475)
Tolfx Jan 17, 2023
3adeec5
feat: added command handler to my handlers (#1475)
Tolfx Jan 17, 2023
1f26a2e
feat: created help command (#1475)
Tolfx Jan 17, 2023
aa19c7a
feat: created how2trade command (#1475)
Tolfx Jan 17, 2023
4003721
feat: created price command (#1475)
Tolfx Jan 17, 2023
7cf7c41
feat: created index information commands (#1475)
Tolfx Jan 17, 2023
dbd2caf
feat: removed commands from handler (#1475)
Tolfx Jan 17, 2023
2d491a5
feat: created buy and sell command (#1475)
Tolfx Jan 17, 2023
89d056e
feat: created buy cart command (#1475)
Tolfx Jan 17, 2023
404be75
feat: created cancel command (#1475)
Tolfx Jan 17, 2023
555c33e
feat: created cart command (#1475)
Tolfx Jan 17, 2023
efadad2
feat: created checkout command (#1475)
Tolfx Jan 17, 2023
9333849
feat: created clear cart command (#1475)
Tolfx Jan 17, 2023
704223f
feat: created sell cart command (#1475)
Tolfx Jan 17, 2023
abb4635
feat: modified price command (#1475)
Tolfx Jan 17, 2023
986cd82
feat: modified how2trade command (#1475)
Tolfx Jan 17, 2023
ff21907
feat: modified help command (#1475)
Tolfx Jan 17, 2023
d342506
feat: created default export for cart commands (#1475)
Tolfx Jan 17, 2023
46cf31a
feat: importing cart commands (#1475)
Tolfx Jan 17, 2023
5a21849
Merge branch 'development' of https://github.com/TF2Autobot/tf2autobo…
Tolfx Jan 17, 2023
463246e
feat: added missing opts (#1475)
Tolfx Jan 17, 2023
1e6f66d
refactor: replaced commands with command handler (#1475)
Tolfx Jan 17, 2023
3e8df11
feat: created queue command (#1475)
Tolfx Jan 18, 2023
a76014a
feat: modified command handler (#1475)
Tolfx Jan 18, 2023
176cd6e
feat: created misc command (#1475)
Tolfx Jan 18, 2023
877fee0
feat: created link command (#1475)
Tolfx Jan 18, 2023
fa7111f
feat: updated index information exporter (#1475)
Tolfx Jan 18, 2023
cce6ee4
feat: modified help command (#1475)
Tolfx Jan 18, 2023
f6596f1
feat: created inventory commands (#1475)
Tolfx Jan 18, 2023
6509ae5
feat: modified command handler (#1475)
Tolfx Jan 18, 2023
869231d
feat: created message command (#1475)
Tolfx Jan 18, 2023
dc84164
feat: fixed logic errors (#1475)
Tolfx Jan 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/classes/Bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,7 @@ export default class Bot {
void sendStats(this);
} else {
this.getAdmins.forEach(admin => {
this.handler.commands.useStatsCommand(admin);
this.handler.commandHandler.useStatsCommand(admin);
});
}
}
Expand Down
261 changes: 261 additions & 0 deletions src/classes/Commands/CommandHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
import SteamID from 'steamid';
import Bot from '../Bot';
import IPricer from '../IPricer';
import log from '../../lib/logger';
import CommandParser from '../CommandParser';
import CartQueue from '../Carts/CartQueue';
import Cart from '../Carts/Cart';
import { UnknownDictionary } from '../../types/common';
import Inventory from '../Inventory';
import * as c from './sub-classes/export';

// Import all commands
import InformationCommands from './commands/information';
import CartCommands from './commands/cart';
import InventoryCommands from './commands/inventory';

export interface ICommand {
/**
* Name of the command
*/
name: string;
/**
* The description of the command
*/
description: string;
/**
* Instance of the bot class
*/
bot: Bot;
/**
* Instance of the pricer class
*/
pricer: IPricer;
/**
* The command can have aliases which will point to the command
*/
aliases?: string[];
/**
* How to use the command
*/
usage?: string;
/**
* Allow the steamID to be invalid
*/
dontAllowInvalidType?: boolean;
/**
* Is only allowed for admins
*/
isAdminCommand?: boolean;
/**
* Is only allowed for whitelisted users
*/
isWhitelistCommand?: boolean;
/**
* When the command is called
*/
execute: (steamID: SteamID, message: string, command?: any) => Promise<void> | void;
}

export type Instant = 'buy' | 'b' | 'sell' | 's';
export type CraftUncraft = 'craftweapon' | 'uncraftweapon';
export type Misc = 'time' | 'uptime' | 'pure' | 'rate' | 'owner' | 'discord' | 'stock';
export type BlockUnblock = 'block' | 'unblock';
export type NameAvatar = 'name' | 'avatar';
export type TF2GC = 'expand' | 'use' | 'delete';
export type ActionOnTrade = 'accept' | 'accepttrade' | 'decline' | 'declinetrade';
export type ForceAction = 'faccept' | 'fdecline';

function hasAliases(command: ICommand): command is ICommand & { aliases: string[] } {
return command.aliases !== undefined;
}

export default class CommandHandler {
manager: c.ManagerCommands;

private message: c.MessageCommand;

help: c.HelpCommands;

misc: c.MiscCommands;

private opt: c.OptionsCommand;

private pManager: c.PricelistManager;

private request: c.RequestCommands;

private review: c.ReviewCommands;

private status: c.StatusCommands;

private crafting: c.CraftingCommands;

isDonating = false;

adminInventoryReset: NodeJS.Timeout;

adminInventory: UnknownDictionary<Inventory> = {};

private readonly commands: Map<string, ICommand>;

private readonly commandsPointAliases: Map<string, string>;

constructor(private readonly bot: Bot, private readonly pricer: IPricer) {
this.commands = new Map();
this.commandsPointAliases = new Map();
this.help = new c.HelpCommands(this.bot);
this.manager = new c.ManagerCommands(bot);
this.message = new c.MessageCommand(bot);
this.misc = new c.MiscCommands(bot);
this.opt = new c.OptionsCommand(bot);
this.pManager = new c.PricelistManager(bot, pricer);
this.request = new c.RequestCommands(bot, pricer);
this.review = new c.ReviewCommands(bot);
this.status = new c.StatusCommands(bot);
this.crafting = new c.CraftingCommands(bot);
}

public registerCommands(): void {
log.info('Registering commands');
// We will later on register all our commands here, by importing them
// We will also include aliases and point them to the command

// We want to initialize all our commands
const commands = [...InformationCommands, ...CartCommands, ...InventoryCommands];
for (const command of commands) {
const cmd = new command(this.bot, this.pricer, this);
this.commands.set(cmd.name, cmd);
if (hasAliases(cmd)) {
for (const alias of cmd.aliases) {
this.commandsPointAliases.set(alias, cmd.name);
}
}
}
}

public async handleCommand(steamID: SteamID, message: string): Promise<void> {
const prefix = this.bot.getPrefix(steamID);
const command = CommandParser.getCommand(message.toLowerCase(), prefix);
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;

if (checkMessage > 1 && !isAdmin) {
return this.bot.sendMessage(steamID, "⛔ Don't spam");
}

log.debug(`Received command ${command} from ${steamID.getSteamID64()}`);

if (command === null) {
const custom = this.bot.options.customMessage.commandNotFound;

this.bot.sendMessage(
steamID,
custom ? custom.replace('%command%', command) : `❌ Command "${command}" not found!`
);
return;
}

const cmd = this.commands.get(command) ?? this.commands.get(this.commandsPointAliases.get(command) ?? '');

if (cmd === undefined) {
return;
}

// Check if the command is an admin command
if (cmd.isAdminCommand && !isAdmin) {
// But we should also check if the command is a whitelist command
if (cmd.isWhitelistCommand && !isWhitelisted) {
return;
}
return;
}

// By default dontAllowInvalidType is false
if (cmd.dontAllowInvalidType && isInvalidType) {
return this.bot.sendMessage(steamID, '❌ Command not available.');
}

await cmd.execute(steamID, message, command);
}

get cartQueue(): CartQueue {
return this.bot.handler.cartQueue;
}

get weaponsAsCurrency(): { enable: boolean; withUncraft: boolean } {
return {
enable: this.bot.options.miscSettings.weaponsAsCurrency.enable,
withUncraft: this.bot.options.miscSettings.weaponsAsCurrency.withUncraft
};
}

addCartToQueue(cart: Cart, isDonating: boolean, isBuyingPremium: boolean): void {
const activeOfferID = this.bot.trades.getActiveOffer(cart.partner);

const custom = this.bot.options.commands.addToQueue;

if (activeOfferID !== null) {
return this.bot.sendMessage(
cart.partner,
custom.alreadyHaveActiveOffer
? custom.alreadyHaveActiveOffer.replace(
/%tradeurl%/g,
`https://steamcommunity.com/tradeoffer/${activeOfferID}/`
)
: `❌ You already have an active offer! Please finish it before requesting a new one: https://steamcommunity.com/tradeoffer/${activeOfferID}/`
);
}

const currentPosition = this.cartQueue.getPosition(cart.partner);

if (currentPosition !== -1) {
if (currentPosition === 0) {
this.bot.sendMessage(
cart.partner,
custom.alreadyInQueueProcessingOffer
? custom.alreadyInQueueProcessingOffer
: '⚠️ You are already in the queue! Please wait while I process your offer.'
);
} else {
this.bot.sendMessage(
cart.partner,
custom.alreadyInQueueWaitingTurn
? custom.alreadyInQueueWaitingTurn
.replace(/%isOrAre%/g, currentPosition !== 1 ? 'are' : 'is')
.replace(/%currentPosition%/g, String(currentPosition))
: '⚠️ You are already in the queue! Please wait your turn, there ' +
(currentPosition !== 1 ? 'are' : 'is') +
` ${currentPosition} in front of you.`
);
}
return;
}

const position = this.cartQueue.enqueue(cart, isDonating, isBuyingPremium);

if (position !== 0) {
this.bot.sendMessage(
cart.partner,
custom.addedToQueueWaitingTurn
? custom.addedToQueueWaitingTurn
.replace(/%isOrAre%/g, position !== 1 ? 'are' : 'is')
.replace(/%position%/g, String(position))
: '✅ You have been added to the queue! Please wait your turn, there ' +
(position !== 1 ? 'are' : 'is') +
` ${position} in front of you.`
);
}
}

useStatsCommand(steamID: SteamID): void {
void this.status.statsCommand(steamID);
}

useUpdateOptionsCommand(steamID: SteamID | null, message: string): void {
this.opt.updateOptionsCommand(steamID, message);
}
}
71 changes: 71 additions & 0 deletions src/classes/Commands/commands/cart/BuyAndSell.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import SteamID from 'steamid';
import CommandHandler, { ICommand, Instant } from '../../CommandHandler';
import { getItemAndAmount } from '../../functions/utils';
import Bot from '../../../Bot';
import IPricer from '../../../IPricer';
import CommandParser from '../../../CommandParser';
import UserCart from '../../../Carts/UserCart';

export default class BuyAndSellCommand implements ICommand {
name = 'buy';

aliases = ['b', 'sell', 's'];

usage = '[amount] <name>';

description = 'Instantly buy an item 💲';

dontAllowInvalidType = true;

constructor(
public readonly bot: Bot,
public readonly pricer: IPricer,
public readonly commandHandler: CommandHandler
) {
this.bot = bot;
this.pricer = pricer;
this.commandHandler = commandHandler;
}

execute(steamID: SteamID, message: string, command: Instant) {
const opt = this.bot.options.commands[command === 'b' ? 'buy' : command === 's' ? 'sell' : command];
const prefix = this.bot.getPrefix(steamID);

if (!opt.enable) {
if (!this.bot.isAdmin(steamID)) {
const custom = opt.customReply.disabled;
return this.bot.sendMessage(steamID, custom ? custom : '❌ This command is disabled by the owner.');
}
}

const info = getItemAndAmount(
steamID,
CommandParser.removeCommand(message),
this.bot,
prefix,
command === 'b' ? 'buy' : command === 's' ? 'sell' : command
);

if (info === null) {
return;
}

const cart = new UserCart(
steamID,
this.bot,
this.commandHandler.weaponsAsCurrency.enable ? this.bot.craftWeapons : [],
this.commandHandler.weaponsAsCurrency.enable && this.commandHandler.weaponsAsCurrency.withUncraft
? this.bot.uncraftWeapons
: []
);

cart.setNotify = true;
if (['b', 'buy'].includes(command)) {
cart.addOurItem(info.priceKey, info.amount);
} else {
cart.addTheirItem(info.match.sku, info.amount);
}

this.commandHandler.addCartToQueue(cart, false, false);
}
}
Loading