Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
c051817
enhance parameter parsing to split only valid key-value pairs
ad1nath Jan 7, 2025
b9b406d
add support for 'changename' command for admins
ad1nath Jan 7, 2025
b5af886
update help command to use 'changeName' syntax for changing bot's name
ad1nath Jan 7, 2025
a983e17
refactor change name command to use 'changeName' syntax and add confi…
ad1nath Jan 7, 2025
33b848a
add tests to improve parameter parsing for commands with special char…
ad1nath Jan 7, 2025
6964003
✨ expressload: add new ExpressLoad API integration class
Osc44r Mar 14, 2025
b2ab77d
✨ inventoryapi: add optional getHeaders method to allow custom headers
Osc44r Mar 14, 2025
56604a6
✨ inventorygetter: include ExpressLoad API integration
Osc44r Mar 14, 2025
5867466
🔧 options: add ExpressLoad configuration support
Osc44r Mar 14, 2025
24f5dc4
🔧 schema: add expressLoad to options schema
Osc44r Mar 14, 2025
f9bc269
🔑 templates: add EXPRESSLOAD_API_KEY environment variable
Osc44r Mar 14, 2025
f34e52c
Merge branch 'TF2Autobot:master' into express-load-release
Osc44r May 22, 2025
4e54ff9
Merge branch 'TF2Autobot:master' into express-load-release
Osc44r May 27, 2025
30f7b24
Merge branch 'TF2Autobot:master' into express-load-release
Osc44r May 29, 2025
9e31af5
🔄️ bump steam-user
idinium96 May 29, 2025
29bc789
🔀 Merge pull request #1792 from Osc44r/express-load-release
idinium96 May 29, 2025
0735fb0
🎨 apply prettier
idinium96 May 29, 2025
9c3146a
🔀 Merge pull request #1789 from ad1nath/update-name-command
idinium96 May 29, 2025
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
3 changes: 3 additions & 0 deletions .example/options.json
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,9 @@
},
"steamApis": {
"enable": false
},
"expressLoad": {
"enable": false
}
},
"discordChat": {
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"semver": "^7.7.2",
"socket.io-client": "^4.8.1",
"steam-totp": "^2.1.2",
"steam-user": "^5.2.2",
"steam-user": "^5.2.3",
"steamid": "^2.1.0",
"url": "^0.11.4",
"valid-url": "^1.0.9",
Expand Down
7 changes: 6 additions & 1 deletion src/classes/CommandParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ export default class CommandParser {

static parseParams(paramString: string): UnknownDictionaryKnownValues {
const params: UnknownDictionaryKnownValues = parseJSON(
'{"' + paramString.replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g, '":"') + '"}'
'{"' +
paramString
.replace(/"/g, '\\"')
.replace(/&(?!&)(?=[^&=]+=[^&]*)/g, '","') // Split only valid key-value pairs
.replace(/=/g, '":"') +
'"}'
);

const parsed: UnknownDictionaryKnownValues = {};
Expand Down
2 changes: 2 additions & 0 deletions src/classes/Commands/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ export default class Commands {
this.manager.TF2GCCommand(steamID, message, command as TF2GC);
} else if (['name', 'avatar'].includes(command) && isAdmin) {
this.manager.nameAvatarCommand(steamID, message, command as NameAvatar, prefix);
} else if (command === 'changename' && isAdmin) {
this.manager.changeNameCommand(steamID, message, prefix);
} else if (['block', 'unblock'].includes(command) && isAdmin) {
this.manager.blockUnblockCommand(steamID, message, command as BlockUnblock);
} else if (['blockedlist', 'blocklist', 'blist'].includes(command) && isAdmin) {
Expand Down
2 changes: 1 addition & 1 deletion src/classes/Commands/sub-classes/Help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export default class HelpCommands {
`haltstatus - Get the info whether the bot is paused or not ⏯`,
`refreshautokeys - Refresh the bot's autokeys settings `,
`refreshlist - Refresh sell listings 🔄`,
`name <new_name> - Change the bot's name `,
`changeName name=<new_name> - Change the bot's name `,
`avatar <image_URL> - Change the bot's avatar `,
`donatebptf (sku|name|defindex)=<a>&amount=<integer> - Donate to backpack.tf (https://backpack.tf/donate) 💰`,
`premium months=<integer> - Purchase backpack.tf premium using keys (https://backpack.tf/premium/subscribe) 👑`,
Expand Down
65 changes: 44 additions & 21 deletions src/classes/Commands/sub-classes/Manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,30 +245,18 @@ export default class ManagerCommands {
'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/f5/f57685d33224e32436f366d1acb4a1769bdfa60f_full.jpg';
const input = CommandParser.removeCommand(message);

if (!input || input === `!${command}`) {
return this.bot.sendMessage(
steamID,
`❌ You forgot to add ${command === 'name' ? 'a name' : 'an image url'}. Example: "!${
command === 'name' ? 'name IdiNium' : `avatar ${example}`
} "`
);
}

if (command === 'name') {
this.bot.community.editProfile(
{
name: input
},
err => {
if (err) {
log.warn('Error while changing name: ', err);
return this.bot.sendMessage(steamID, `❌ Error while changing name: ${err.message}`);
}

this.bot.sendMessage(steamID, '✅ Successfully changed name.');
}
this.bot.sendMessage(
steamID,
`The ${prefix}name command has been updated to ${prefix}changeName. Please use the new command going forward!`
);
} else {
if (!input || input === `!${command}`) {
return this.bot.sendMessage(
steamID,
`❌ You forgot to add an image url'. Example: "!${`avatar ${example}`} "`
);
}
if (!validUrl.isUri(input)) {
return this.bot.sendMessage(steamID, `❌ Your url is not valid. Example: "${prefix}avatar ${example}"`);
}
Expand All @@ -284,6 +272,41 @@ export default class ManagerCommands {
}
}

changeNameCommand(steamID: SteamID, message: string, prefix: string): void {
const params = CommandParser.parseParams(CommandParser.removeCommand(message));
const inputName = params.name as string;

if (inputName !== undefined) {
if (params.i_am_sure !== 'yes_i_am') {
return this.bot.sendMessage(
steamID,
`⚠️ Are you sure that you want to change your bot's name?` +
`\nChanging the name will result in a trading cooldown for a few hours on your bot's account` +
`\nIf yes, retry by sending ${prefix}changeName name=${inputName}&i_am_sure=yes_i_am`
);
} else {
this.bot.community.editProfile(
{
name: inputName
},
err => {
if (err) {
log.warn('Error while changing name: ', err);
return this.bot.sendMessage(steamID, `❌ Error while changing name: ${err.message}`);
}

this.bot.sendMessage(steamID, '✅ Successfully changed name.');
}
);
}
} else {
return this.bot.sendMessage(
steamID,
`⚠️ Missing name property. Example: "${prefix}changeName name=IdiNium`
);
}
}

blockedListCommand(steamID: SteamID): void {
if (this.isSendingBlockedList) {
return;
Expand Down
24 changes: 24 additions & 0 deletions src/classes/InventoryApis/ExpressLoad.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { UnknownDictionaryKnownValues } from 'src/types/common';
import Bot from '../Bot';
import InventoryApi from './InventoryApi';

export default class ExpressLoad extends InventoryApi {
constructor(bot: Bot) {
super(bot, 'expressLoad');
}

protected getURLAndParams(
steamID: string,
appID: number,
contextID: string
): [string, UnknownDictionaryKnownValues] {
return [`https://api.express-load.com/inventory/${steamID}/${appID}/${contextID}`, {}];
}

protected getHeaders(): UnknownDictionaryKnownValues {
return {
'X-API-KEY': this.getApiKey(),
'User-Agent': 'TF2Autobot'
};
}
}
10 changes: 9 additions & 1 deletion src/classes/InventoryApis/InventoryApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ export default class InventoryApi {
return ['', {}];
}

// This method may be defined by a class extending InventoryAPI
protected getHeaders(): UnknownDictionaryKnownValues {
return {};
}

// Adapted from node-steamcommunity
public getUserInventoryContents(
userID: SteamID | string,
Expand Down Expand Up @@ -58,6 +63,8 @@ export default class InventoryApi {
return;
}

const headers = this.getHeaders();

let pos = 1;
get([], []);

Expand All @@ -67,7 +74,8 @@ export default class InventoryApi {
params: {
...apiCallParams,
start_assetid: start
}
},
headers: headers
}).then(
response => {
const result = response.data as GetUserInventoryContentsResult;
Expand Down
3 changes: 2 additions & 1 deletion src/classes/InventoryGetter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import SteamID from 'steamid';
import { EconItem } from '@tf2autobot/tradeoffer-manager';

import Bot from './Bot';
import ExpressLoad from './InventoryApis/ExpressLoad';
import InventoryApi from './InventoryApis/InventoryApi';
import SteamSupply from './InventoryApis/SteamSupply';
import SteamApis from './InventoryApis/SteamApis';
Expand All @@ -10,7 +11,7 @@ export default class InventoryGetter {
private readonly inventoryApis: InventoryApi[];

constructor(private readonly bot: Bot) {
this.inventoryApis = [new SteamSupply(this.bot), new SteamApis(this.bot)];
this.inventoryApis = [new SteamSupply(this.bot), new SteamApis(this.bot), new ExpressLoad(this.bot)];
}

getUserInventoryContents(
Expand Down
6 changes: 6 additions & 0 deletions src/classes/Options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,9 @@ export const DEFAULTS: JsonOptions = {
},
steamApis: {
enable: false
},
expressLoad: {
enable: false
}
},

Expand Down Expand Up @@ -1612,6 +1615,7 @@ interface ManualReview extends OnlyEnable {
interface InventoryApis {
steamSupply?: OnlyEnable;
steamApis?: OnlyEnable;
expressLoad?: OnlyEnable;
}

// ------------ Discord Chat ---------------
Expand Down Expand Up @@ -2195,6 +2199,7 @@ export default interface Options extends JsonOptions {
discordBotToken?: string;
steamSupplyApiKey?: string;
steamApisApiKey?: string;
expressLoadApiKey?: string;

admins?: adminData[];
keep?: string[];
Expand Down Expand Up @@ -2512,6 +2517,7 @@ export function loadOptions(options?: Options): Options {
discordBotToken: getOption('discordBotToken', '', String, incomingOptions),
steamSupplyApiKey: getOption('steamsupplyApiKey', '', String, incomingOptions),
steamApisApiKey: getOption('steamapisApiKey', '', String, incomingOptions),
expressLoadApiKey: getOption('expressloadApiKey', '', String, incomingOptions),

admins: getOption('admins', [], jsonParseAdminData, incomingOptions),
keep: getOption('keep', [], jsonParseArray, incomingOptions),
Expand Down
14 changes: 14 additions & 0 deletions src/classes/__tests__/CommandParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,17 @@ it('can parse item id commands', () => {
const params = CommandParser.parseParams(CommandParser.removeCommand(message));
expect(params).toEqual({ id: 10151297782, intent: 'sell', sell: { keys: 1 } });
});

it('can parse values with &', () => {
let message = '!changeName name=Buying spells & hats&i_am_sure=i_am_sure';
let params = CommandParser.parseParams(CommandParser.removeCommand(message));
expect(params).toEqual({ name: 'Buying spells & hats', i_am_sure: 'i_am_sure' });

message = `!test key1=value1&key2=value2&&key3=3`;
params = CommandParser.parseParams(CommandParser.removeCommand(message));
expect(params).toEqual({ key1: 'value1', key2: 'value2&', key3: 3 });

message = `!test key1=value1&key2=&value2&&key3=[value3]`;
params = CommandParser.parseParams(CommandParser.removeCommand(message));
expect(params).toEqual({ key1: 'value1', key2: '&value2&', key3: ['value3'] });
});
5 changes: 4 additions & 1 deletion src/schemas/options-json/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1525,9 +1525,12 @@ export const optionsSchema: jsonschema.Schema = {
},
steamApis: {
$ref: '#/definitions/only-enable'
},
expressLoad: {
$ref: '#/definitions/only-enable'
}
},
required: ['steamSupply', 'steamApis'],
required: ['steamSupply', 'steamApis', 'expressLoad'],
additionalProperties: false
},
discordChat: {
Expand Down
1 change: 1 addition & 0 deletions template.ecosystem.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"DISCORD_BOT_TOKEN": "",
"STEAMSUPPLY_API_KEY": "",
"STEAMAPIS_API_KEY": "",
"EXPRESSLOAD_API_KEY": "",

"ADMINS": [{ "steam": "<your steamid 64>", "discord": null }],
"KEEP": ["<steamid of person to keep in friendslist>"],
Expand Down
1 change: 1 addition & 0 deletions template.env
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ MPTF_API_KEY=""
DISCORD_BOT_TOKEN=""
STEAMSUPPLY_API_KEY=""
STEAMAPIS_API_KEY=""
EXPRESSLOAD_API_KEY=""

ADMINS=[{ "steam": "<your steamid 64>", "discord": null }]
KEEP=["<steamid of person to keep in friendslist>"]
Expand Down
Loading