Skip to content
4 changes: 4 additions & 0 deletions src/classes/MyHandler/MyHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ import filterAxiosError from '@tf2autobot/filter-axios-error';
import sendTf2SystemMessage from '../../lib/DiscordWebhook/sendTf2SystemMessage';
import sendTf2DisplayNotification from '../../lib/DiscordWebhook/sendTf2DisplayNotification';
import sendTf2ItemBroadcast from '../../lib/DiscordWebhook/sendTf2ItemBroadcast';
import WebhookHandler from '../../lib/Webhooks/Webhook';

const filterReasons = (reasons: string[]) => {
const filtered = new Set(reasons);
Expand All @@ -65,6 +66,8 @@ export default class MyHandler extends Handler {

readonly cartQueue: CartQueue;

readonly webhookHandler: WebhookHandler;

private groupsStore: string[];

private get opt(): Options {
Expand Down Expand Up @@ -203,6 +206,7 @@ export default class MyHandler extends Handler {
this.commands = new Commands(bot, priceSource);
this.cartQueue = new CartQueue(bot);
this.autokeys = new Autokeys(bot);
this.webhookHandler = new WebhookHandler(bot);

this.paths = genPaths(this.opt.steamAccountName);

Expand Down
9 changes: 9 additions & 0 deletions src/classes/MyHandler/offer/accepted/processAccepted.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,15 @@ export default function processAccepted(
offer.data('processOfferTime')) as number;
const timeTakenToCounterOffer = offer.data('processCounterTime') as number | undefined;

bot.handler.webhookHandler.tradeSummery(
offer,
accepted,
bot,
timeTakenToComplete,
timeTakenToProcessOrConstruct,
timeTakenToCounterOffer,
isOfferSent
);
if (isWebhookEnabled) {
void sendTradeSummary(
offer,
Expand Down
12 changes: 12 additions & 0 deletions src/classes/Options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2116,6 +2116,17 @@ interface StrangeParts {
'Robots Killed During Halloween'?: string;
}

interface Webhooks {
tradeSummary?: Omit<TradeSummaryDW, 'mentionOwner' | 'misc'>;
declinedTrade?: DeclinedTradeDW;
messages?: Omit<MessagesDW, 'isMention'>;
priceUpdate?: PriceUpdateDW;
sendAlert?: Omit<SendAlertStatsDW, 'isMention'>;
sendStats?: SendStatsDW;
sendTf2Events?: SendTf2Events;
webhookSecret?: string;
}

// ------------ JsonOptions ------------

export interface JsonOptions {
Expand All @@ -2139,6 +2150,7 @@ export interface JsonOptions {
customMessage?: CustomMessage;
commands?: Commands;
detailsExtra?: DetailsExtra;
webhooks?: Webhooks;
}

export default interface Options extends JsonOptions {
Expand Down
154 changes: 154 additions & 0 deletions src/lib/Webhooks/Webhook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import filterAxiosError from '@tf2autobot/filter-axios-error';
import axios, { AxiosError } from 'axios';
import Bot from '../../classes/Bot';
import { TradeSummery } from './tradeSummery';
import * as tradeSummery from './tradeSummery';

// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface WebhookHandler extends TradeSummery {}

export enum WebhookType {
priceUpdate = 'priceUpdate',
tradeSummary = 'tradeSummary',
declinedTrade = 'declinedTrade',
sendStats = 'sendStats',
tf2SystemMessage = 'tf2SystemMessage',
tf2ItemBroadcast = 'tf2ItemBroadcast',
tf2DisplayNotification = 'tf2DisplayNotification',
sendAlert = 'sendAlert',
messages = 'messages'
}

class WebhookHandler {
// Lets make a list of what is enabled
// and what is not

// WebhookHandler is a class that will handle
// all the webhooks that are enabled in the config
// and will send them to the correct place
private enablePriceListUpdate: boolean;

private enableTradeSummary: boolean;

private enableTradeDeclined: boolean;

private enableStats: boolean;

private enableTf2SystemMessage: boolean;

private enableTf2ItemBroadcast: boolean;

private enableTf2DisplayNotification: boolean;

private enableAlert: boolean;

private enableMessages: boolean;

private webhookSecret: string;

constructor(private readonly bot: Bot) {
this.init();
}

private init() {
// Lets get webhook settings from bot
const { webhooks } = this.bot.options;
this.enablePriceListUpdate = webhooks.priceUpdate.enable;
this.enableTradeSummary = webhooks.tradeSummary.enable;
this.enableTradeDeclined = webhooks.declinedTrade.enable;
this.enableStats = webhooks.sendStats.enable;
this.enableTf2SystemMessage = webhooks.sendTf2Events.systemMessage.enable;
this.enableTf2ItemBroadcast = webhooks.sendTf2Events.itemBroadcast.enable;
this.enableTf2DisplayNotification = webhooks.sendTf2Events.displayNotification.enable;
this.enableAlert = webhooks.sendAlert.enable;
this.enableMessages = webhooks.messages.enable;
this.webhookSecret = webhooks.webhookSecret;
}

private getUrl(type: WebhookType) {
switch (type) {
case WebhookType.priceUpdate:
return this.bot.options.webhooks.priceUpdate.url;
case WebhookType.tradeSummary:
return this.bot.options.webhooks.tradeSummary.url;
case WebhookType.declinedTrade:
return this.bot.options.webhooks.declinedTrade.url;
case WebhookType.sendStats:
return this.bot.options.webhooks.sendStats.url;
case WebhookType.tf2DisplayNotification:
return this.bot.options.webhooks.sendTf2Events.displayNotification.url;
case WebhookType.tf2ItemBroadcast:
return this.bot.options.webhooks.sendTf2Events.itemBroadcast.url;
case WebhookType.tf2SystemMessage:
return this.bot.options.webhooks.sendTf2Events.systemMessage.url;
case WebhookType.sendAlert:
return this.bot.options.webhooks.sendAlert.url.main;
case WebhookType.messages:
return this.bot.options.webhooks.messages.url;
default:
return null;
}
}

private isEnabled(type: WebhookType) {
switch (type) {
case WebhookType.priceUpdate:
return this.enablePriceListUpdate;
case WebhookType.tradeSummary:
return this.enableTradeSummary;
case WebhookType.declinedTrade:
return this.enableTradeDeclined;
case WebhookType.sendStats:
return this.enableStats;
case WebhookType.tf2DisplayNotification:
return this.enableTf2DisplayNotification;
case WebhookType.tf2ItemBroadcast:
return this.enableTf2ItemBroadcast;
case WebhookType.tf2SystemMessage:
return this.enableTf2SystemMessage;
case WebhookType.sendAlert:
return this.enableAlert;
case WebhookType.messages:
return this.enableMessages;
default:
return false;
}
}

async sendWebhook(type: WebhookType, data: Record<any, any>) {
return new Promise((resolve, reject) => {
const url = this.getUrl(type);

const urls = Array.isArray(url) ? url : [url];
// Lets check if the webhook is enabled
if (this.isEnabled(type)) {
// Lets send the webhook
for (const url of urls) {
void axios({
method: 'POST',
url,
headers: {
'Content-Type': 'application/json',
'x-webhook-secret': this.webhookSecret ?? ''
},
data: JSON.stringify(data)
})
.then(() => {
resolve();
})
.catch((err: AxiosError) => {
reject({ err: filterAxiosError(err) });
});
}
} else {
// Webhook is disabled
resolve();
}
});
}
}

// Assign tradeSummery to WebhookHandler
Object.assign(WebhookHandler.prototype, tradeSummery);

export default WebhookHandler;
1 change: 1 addition & 0 deletions src/lib/Webhooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default './Webhooks';
74 changes: 74 additions & 0 deletions src/lib/Webhooks/tradeSummery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { ItemsDict, TradeOffer } from '@tf2autobot/tradeoffer-manager';
import WebhookHandler, { WebhookType } from './Webhook';
import Bot from '../../classes/Bot';
import * as t from '../tools/export';
import { getPartnerDetails } from '../DiscordWebhook/utils';

export interface TradeSummery {
tradeSummery: (
offer: TradeOffer,
accepted: Accepted,
bot: Bot,
timeTakenToComplete: number,
timeTakenToProcessOrConstruct: number,
timeTakenToCounterOffer: number | undefined,
isOfferSent: boolean | undefined
) => void;
}

interface Accepted {
invalidItems: string[];
disabledItems: string[];
overstocked: string[];
understocked: string[];
highValue: string[];
isMention: boolean;
}

export async function tradeSummery(
this: WebhookHandler,
offer: TradeOffer,
accepted: Accepted,
bot: Bot,
timeTakenToComplete: number,
timeTakenToProcessOrConstruct: number,
timeTakenToCounterOffer: number | undefined,
isOfferSent: boolean | undefined
) {
const value = t.valueDiff(offer);

const dict = offer.data('dict') as ItemsDict;

const details = await getPartnerDetails(offer, bot);

const links = t.generateLinks(offer.partner.toString());

const slots = bot.tf2.backpackSlots;
const autokeys = bot.handler.autokeys;
const status = autokeys.getOverallStatus;

const message = t.replace.specialChar(offer.message);
const itemsToGiveCount = offer.itemsToGive.length;
const isAdmin = bot.isAdmin(offer.partner);
const isDonate = t.isDonate(offer) && itemsToGiveCount === 0 && !isAdmin;

const botInfo = bot.handler.getBotInfo;

const data = {
bot: botInfo,
value,
links,
slots,
status,
message,
isDonate,
details,
dict,
timeTakenToComplete,
timeTakenToProcessOrConstruct,
timeTakenToCounterOffer,
isOfferSent
};

void this.sendWebhook(WebhookType.tradeSummary, data);
}
4 changes: 3 additions & 1 deletion src/lib/tools/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import summarize, { summarizeToChat } from './summarizeOffer';
import profit from './profit';
import itemStats from './itemStats';
import testPriceKey from './testPriceKey';
import isDonate from './isDonate';

export {
getHighValueItems,
Expand All @@ -28,5 +29,6 @@ export {
convertTime,
profit,
itemStats,
testPriceKey
testPriceKey,
isDonate
};
38 changes: 38 additions & 0 deletions src/lib/tools/isDonate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { TradeOffer } from '@tf2autobot/tradeoffer-manager';

export default function isDonate(offer: TradeOffer) {
// Check if the message is a donation
const offerMessage = offer.message.toLowerCase();
const isGift = [
'gift',
'donat', // So that 'donate' or 'donation' will also be accepted
'tip', // All others are synonyms
'tribute',
'souvenir',
'favor',
'giveaway',
'bonus',
'grant',
'bounty',
'present',
'contribution',
'award',
'nice', // Up until here actually
'happy', // All below people might also use
'thank',
'goo', // For 'good', 'goodie' or anything else
'awesome',
'rep',
'joy',
'cute', // right?
'enjoy',
'prize',
'free',
'tnx',
'ty',
'love',
'<3'
].some(word => offerMessage.includes(word));

return isGift;
}
Loading