hypixel-auction-notifier/app/index.ts

176 lines
5.9 KiB
TypeScript

import axios from 'axios';
import { WebhookClient, EmbedBuilder, Embed } from 'discord.js';
import { Worker } from 'worker_threads';
import { asyncInterval, addNotation } from './src/helperFunctions';
import { string } from 'prismarine-nbt';
import { SqlSystem } from './src/sqlFunctions';
import { setupErrorHandlers } from './src/errorHandler';
import { ItemCompactData } from './src/Types';
setupErrorHandlers();
import { loadConfig } from './src/configLoader';
const config = loadConfig();
let worker_count = config.data.worker_count ?? 1;
let lastUpdated = 0;
let doneWorkers = 0;
let startingTime: number;
let maxPrice = 0;
let itemDatas: Record<string, ItemCompactData> = {};
const workers: Worker[] = [];
const webhookRegex = /https:\/\/discord.com\/api\/webhooks\/(.+)\/(.+)/;
const bazaarPrice = {
RECOMBOBULATOR_3000: 0,
HOT_POTATO_BOOK: 0,
FUMING_POTATO_BOOK: 0
};
async function initialize() {
await SqlSystem.InitDB()
const matches = process.env.WEBHOOK_URL.match(webhookRegex);
if (!matches) return console.log(`[Main thread] Couldn't parse Webhook URL`);
const webhook = new WebhookClient({ id: matches[1], token: matches[2] });
await getBzData();
await getMoulberry();
await getLBINs();
for (let j = 0; j < worker_count; j++) {
workers[j] = new Worker('/app/dist/AuctionHandler.worker.js', {
workerData: {
itemDatas: itemDatas,
bazaarData: bazaarPrice,
workerNumber: j,
maxPrice: maxPrice
}
});
workers[j].on('message', async (result) => {
if (result.itemData !== undefined) {
let averagePrice: number | null = itemDatas[result.itemData.id]?.cleanPrice || null;
if (
result.auctionData.lbin - result.auctionData.price >= config.data.minSnipeProfit &&
(averagePrice || averagePrice! - result.auctionData.price >= config.data.minAvgProfit)
) {
let mustBuyMessage = '';
const embed = new EmbedBuilder()
.setTitle(`**${result.itemData.name.replace(/§./g, '')}**`)
.setColor('#2e3137')
.setThumbnail(`https://sky.shiiyu.moe/item/${result.itemData.id}`)
.setDescription(
`${mustBuyMessage}\nAuction:
\`\`\`/viewauction ${result.auctionData.auctionID}\`\`\`
\nProfit: \`${addNotation('oneLetters', result.auctionData.profit)} (${result.auctionData.percentProfit
}%)\`
\nCost: \`${addNotation('oneLetters', result.auctionData.price)}\`
\nLBIN: \`${addNotation('oneLetters', result.auctionData.lbin)}\`
\nSales/Day: \`${addNotation('oneLetters', result.auctionData.sales)}\`
\nType: \`${result.auctionData.ahType}\`
\nAverage Price: \`${averagePrice ? addNotation('oneLetters', averagePrice) : 'N/A'}\``
);
await webhook.send({
username: process.env.WEBHOOK_NAME,
avatarURL: process.env.WEBHOOK_PROFILE_PICTURE,
embeds: [embed]
});
}
} else if (result === 'finished') {
doneWorkers++;
if (doneWorkers === worker_count) {
doneWorkers = 0;
console.log(`Completed in ${(Date.now() - startingTime) / 1000} seconds`);
startingTime = 0;
workers[0].emit('done');
}
}
});
}
asyncInterval(
async () => {
await getLBINs();
workers.forEach((worker) => {
worker.postMessage({ type: 'moulberry', data: itemDatas });
});
},
'lbin',
60000
);
asyncInterval(
async () => {
await getMoulberry();
workers.forEach((worker) => {
worker.postMessage({ type: 'moulberry', data: itemDatas });
});
},
'avg',
60e5
);
asyncInterval(
async () => {
return new Promise(async (resolve) => {
const ahFirstPage = await axios.get('https://api.hypixel.net/skyblock/auctions?page=0');
const totalPages = ahFirstPage.data.totalPages;
if (ahFirstPage.data.lastUpdated === lastUpdated) {
resolve();
} else {
lastUpdated = ahFirstPage.data.lastUpdated;
startingTime = Date.now();
console.log('Getting auctions..');
workers.forEach((worker) => {
worker.postMessage({ type: 'pageCount', data: totalPages });
});
workers[0].once('done', () => {
resolve();
});
}
});
},
'check',
0
);
}
async function getLBINs(): Promise<void> {
const lbins = await axios.get('https://moulberry.codes/lowestbin.json');
const lbinData = lbins.data;
for (const item of Object.keys(lbinData)) {
if (!itemDatas[item]) itemDatas[item] = new ItemCompactData();
itemDatas[item].lbin = lbinData[item];
}
}
async function getMoulberry(): Promise<void> {
const moulberryAvgs = await axios.get('https://moulberry.codes/auction_averages/3day.json');
const avgData = moulberryAvgs.data;
const cleanPriceAvgs = await axios.get('https://moulberry.codes/auction_averages_lbin/1day.json');
const cleanPriceData = cleanPriceAvgs.data;
for (const item of Object.keys(avgData)) {
if (!itemDatas[item]) itemDatas[item] = new ItemCompactData();
const itemInfo = avgData[item];
itemDatas[item].sales = itemInfo.sales !== undefined ? itemInfo.sales : 0;
itemDatas[item].cleanPrice =
cleanPriceData[item] !== undefined
? Math.round(cleanPriceData[item])
: itemInfo.clean_price !== undefined
? itemInfo.clean_price
: itemInfo.price;
}
}
async function getBzData(): Promise<void> {
const bzData = await axios.get('https://api.hypixel.net/skyblock/bazaar');
bazaarPrice['RECOMBOBULATOR_3000'] = bzData.data.products.RECOMBOBULATOR_3000.quick_status.buyPrice;
bazaarPrice['HOT_POTATO_BOOK'] = bzData.data.products.HOT_POTATO_BOOK.quick_status.buyPrice;
bazaarPrice['FUMING_POTATO_BOOK'] = bzData.data.products.FUMING_POTATO_BOOK.quick_status.buyPrice;
}
initialize();