added docker, environment variables, renamed vars

This commit is contained in:
Ulysia 2024-12-28 11:23:40 +01:00 committed by Heli-o
parent 46b0fe21cb
commit 02337bdb2d
12 changed files with 1567 additions and 209 deletions

7
.dockerignore Normal file
View file

@ -0,0 +1,7 @@
config*
docker*
Docker*
README*
node_modules/
.*
pnpm-lock*

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
node_modules
.env

8
.prettierrc Normal file
View file

@ -0,0 +1,8 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"parser": "babel-flow",
"bracketSameLine": true
}

View file

@ -1,114 +1,176 @@
const { default: axios } = require("axios"); const { default: axios } = require('axios');
const { getParsed, getProfit, splitNumber, getRawCraft } = require("./src/helperFunctions"); const {
const { parentPort, workerData } = require("worker_threads"); getParsed,
const config = require("./config.json"); getProfit,
splitNumber,
getRawCraft,
} = require('./src/helperFunctions');
const { parentPort, workerData } = require('worker_threads');
const config = require('./config.json');
let minProfit = config.data.minSnipeProfit; let minProfit = config.data.minSnipeProfit;
let minPercentProfit = config.data.minSnipePP; let minPercentProfit = config.data.minSnipePP;
let ignoredAuctions = []; let ignoredAuctions = [];
const { Item } = require("./src/Item"); const { Item } = require('./src/Item');
const threadsToUse = require("./config.json").data["threadsToUse/speed"]; const threadsToUse = require('./config.json').data['threadsToUse/speed'];
const promises = []; const promises = [];
console.log(`[Worker ${workerData.workerNumber}] Worker started`); console.log(`[Worker ${workerData.workerNumber}] Worker started`);
parentPort.on("message", async (message) => { parentPort.on('message', async (message) => {
console.log(`[Worker ${workerData.workerNumber}] Received message: ${message.type}`); console.log(
if (message.type === "pageCount") { `[Worker ${workerData.workerNumber}] Received message: ${message.type}`
await doTask(message.data); );
} else if (message.type === "moulberry") { if (message.type === 'pageCount') {
workerData.itemDatas = message.data; await doTask(message.data);
console.log(`[Worker ${workerData.workerNumber}] Updated item data`); } else if (message.type === 'moulberry') {
} workerData.itemDatas = message.data;
console.log(`[Worker ${workerData.workerNumber}] Updated item data`);
}
}); });
async function parsePage(i) { async function parsePage(i) {
console.log(`[Worker ${workerData.workerNumber}] Parsing page ${i}`); console.log(`[Worker ${workerData.workerNumber}] Parsing page ${i}`);
try { try {
const auctionPage = await axios.get(`https://api.hypixel.net/skyblock/auctions?page=${i}`); const auctionPage = await axios.get(
for (const auction of auctionPage.data.auctions) { `https://api.hypixel.net/skyblock/auctions?page=${i}`
if (!auction.bin) continue; );
const uuid = auction.uuid; for (const auction of auctionPage.data.auctions) {
if (ignoredAuctions.includes(uuid) || config.data.ignoreCategories[auction.category]) continue; if (!auction.bin) continue;
const item = await getParsed(auction.item_bytes); const uuid = auction.uuid;
const extraAtt = item["i"][0].tag.ExtraAttributes; if (
const itemID = extraAtt.id; ignoredAuctions.includes(uuid) ||
let startingBid = auction.starting_bid; config.data.ignoreCategories[auction.category]
const itemData = workerData.itemDatas[itemID]; )
if (!itemData) continue; continue;
const lbin = itemData.lbin; const item = await getParsed(auction.item_bytes);
const sales = itemData.sales; const extraAtt = item['i'][0].tag.ExtraAttributes;
const prettyItem = new Item(item.i[0].tag.display.Name, uuid, startingBid, auction.tier, extraAtt.enchantments, const itemID = extraAtt.id;
extraAtt.hot_potato_count > 10 ? 10 : extraAtt.hot_potato_count, extraAtt.hot_potato_count > 10 ? let startingBid = auction.starting_bid;
extraAtt.hot_potato_count - 10 : 0, extraAtt.rarity_upgrades === 1, const itemData = workerData.itemDatas[itemID];
extraAtt.art_of_war_count === 1, extraAtt.dungeon_item_level, if (!itemData) continue;
extraAtt.gems, itemID, auction.category, 0, 0, lbin, sales, auction.item_lore); const lbin = itemData.lbin;
const unstableOrMarketManipulated = Math.abs((lbin - itemData.cleanPrice) / lbin) > config.data.maxAvgLbinDiff; const sales = itemData.sales;
ignoredAuctions.push(uuid); const prettyItem = new Item(
const rcCost = config.data.includeCraftCost ? getRawCraft(prettyItem, workerData.bazaarData, workerData.itemDatas) : 0; item.i[0].tag.display.Name,
const carriedByRC = rcCost >= config.data.rawCraftMaxWeightPP * lbin; uuid,
startingBid,
auction.tier,
extraAtt.enchantments,
extraAtt.hot_potato_count > 10 ? 10 : extraAtt.hot_potato_count,
extraAtt.hot_potato_count > 10 ? extraAtt.hot_potato_count - 10 : 0,
extraAtt.rarity_upgrades === 1,
extraAtt.art_of_war_count === 1,
extraAtt.dungeon_item_level,
extraAtt.gems,
itemID,
auction.category,
0,
0,
lbin,
sales,
auction.item_lore
);
const unstableOrMarketManipulated =
Math.abs((lbin - itemData.cleanPrice) / lbin) >
config.data.maxAvgLbinDiff;
ignoredAuctions.push(uuid);
const rcCost = config.data.includeCraftCost
? getRawCraft(prettyItem, workerData.bazaarData, workerData.itemDatas)
: 0;
const carriedByRC = rcCost >= config.data.rawCraftMaxWeightPP * lbin;
if (carriedByRC || unstableOrMarketManipulated || sales <= config.data.minSales || !sales) continue; if (
carriedByRC ||
unstableOrMarketManipulated ||
sales <= config.data.minSales ||
!sales
)
continue;
if (config.filters.nameFilter.find((name) => itemID.includes(name)) === undefined) { if (
if ((lbin + rcCost) - startingBid > minProfit) { config.filters.itemIDExclusions.find((name) =>
const profitData = getProfit(startingBid, rcCost, lbin); itemID.includes(name)
let auctionType = null; ) === undefined
if (rcCost > (lbin - startingBid) && profitData.snipeProfit < minProfit) { ) {
auctionType = "VALUE"; if (lbin + rcCost - startingBid > minProfit) {
} else if (profitData.snipeProfit >= minProfit && rcCost < (lbin - startingBid)) { const profitData = getProfit(startingBid, rcCost, lbin);
auctionType = "SNIPE"; let auctionType = null;
} else if (profitData.snipeProfit >= minProfit && rcCost > 0) { if (
auctionType = "BOTH"; rcCost > lbin - startingBid &&
} profitData.snipeProfit < minProfit
) {
auctionType = 'VALUE';
} else if (
profitData.snipeProfit >= minProfit &&
rcCost < lbin - startingBid
) {
auctionType = 'SNIPE';
} else if (profitData.snipeProfit >= minProfit && rcCost > 0) {
auctionType = 'BOTH';
}
prettyItem.auctionData.ahType = auctionType; prettyItem.auctionData.ahType = auctionType;
if (auctionType === "VALUE" || auctionType === "BOTH") { if (auctionType === 'VALUE' || auctionType === 'BOTH') {
if (profitData.RCProfit > config.data.minCraftProfit && profitData.RCPP > config.data.minCraftPP) { if (
prettyItem.auctionData.profit = profitData.RCProfit; profitData.RCProfit > config.data.minCraftProfit &&
prettyItem.auctionData.percentProfit = profitData.RCPP; profitData.RCPP > config.data.minCraftPP
parentPort.postMessage(prettyItem); ) {
} prettyItem.auctionData.profit = profitData.RCProfit;
} else { prettyItem.auctionData.percentProfit = profitData.RCPP;
if (profitData.snipeProfit > minProfit && profitData.snipePP > minPercentProfit) { parentPort.postMessage(prettyItem);
prettyItem.auctionData.profit = profitData.snipeProfit;
prettyItem.auctionData.percentProfit = profitData.snipePP;
parentPort.postMessage(prettyItem);
}
}
}
} }
} else {
if (
profitData.snipeProfit > minProfit &&
profitData.snipePP > minPercentProfit
) {
prettyItem.auctionData.profit = profitData.snipeProfit;
prettyItem.auctionData.percentProfit = profitData.snipePP;
parentPort.postMessage(prettyItem);
}
}
} }
} catch (error) { }
console.error(`[Worker ${workerData.workerNumber}] Error parsing page ${i}:`, error);
} }
} catch (error) {
console.error(
`[Worker ${workerData.workerNumber}] Error parsing page ${i}:`,
error
);
}
} }
async function doTask(totalPages) { async function doTask(totalPages) {
console.log(`[Worker ${workerData.workerNumber}] Starting task for ${totalPages} pages`); console.log(
let startingPage = 0; `[Worker ${workerData.workerNumber}] Starting task for ${totalPages} pages`
const pagePerThread = splitNumber(totalPages, threadsToUse); );
let startingPage = 0;
const pagePerThread = splitNumber(totalPages, threadsToUse);
if (workerData.workerNumber !== 0 && startingPage === 0) { if (workerData.workerNumber !== 0 && startingPage === 0) {
const clonedStarting = pagePerThread.slice(); const clonedStarting = pagePerThread.slice();
clonedStarting.splice(workerData.workerNumber, 9999); clonedStarting.splice(workerData.workerNumber, 9999);
clonedStarting.forEach((pagePer) => { clonedStarting.forEach((pagePer) => {
startingPage += pagePer; startingPage += pagePer;
}); });
} }
let pageToStop = parseInt(startingPage) + parseInt(pagePerThread[workerData.workerNumber]); let pageToStop =
parseInt(startingPage) + parseInt(pagePerThread[workerData.workerNumber]);
if (pageToStop !== totalPages) { if (pageToStop !== totalPages) {
pageToStop -= 1; pageToStop -= 1;
} }
console.log(`[Worker ${workerData.workerNumber}] Processing pages from ${startingPage} to ${pageToStop}`); console.log(
`[Worker ${workerData.workerNumber}] Processing pages from ${startingPage} to ${pageToStop}`
);
for (let i = startingPage; i < pageToStop; i++) { for (let i = startingPage; i < pageToStop; i++) {
promises.push(parsePage(i)); promises.push(parsePage(i));
} }
await Promise.all(promises); await Promise.all(promises);
console.log(`[Worker ${workerData.workerNumber}] Finished task`); console.log(`[Worker ${workerData.workerNumber}] Finished task`);
parentPort.postMessage("finished"); parentPort.postMessage('finished');
} }

9
Dockerfile Normal file
View file

@ -0,0 +1,9 @@
FROM node:23-alpine3.20
COPY . /app
WORKDIR /app
RUN npm install
CMD ["node", "index.js"]

View file

@ -19,3 +19,20 @@
- Ability to make some high tier enchantments worthless (like Looking 4, Luck 6 etc..) - Ability to make some high tier enchantments worthless (like Looking 4, Luck 6 etc..)
- Bad Enchantment filter - Bad Enchantment filter
- And more.. - And more..
# Setup
## .env
`.env` file is needed due to docker compose. Even if you are not using docker and docker compose, **.ENV IS NEEDED**
the example .env is
```
webhook_url=<url_of_webhook>
webhook_name=Flipper
webhook_profile_picture_link=https://cdn.discordapp.com/avatars/486155512568741900/164084b936b4461fe9505398f7383a0e.png?size=4096
```
You can also add these as environment variables into your system when running Node or run the app via `node --env-file=.env index.js`
## config.json
### filters
- itemIDExclusions: exclusion based on itemID (contains, doesnt have to be the full itemID name)
- EnchantThreshold and EnchantThresholdConditionalBypass are used for **Raw Crafting**. I suggest leaving them as they are.

View file

@ -23,13 +23,8 @@
"includeCraftCost": true, "includeCraftCost": true,
"minPriceForRawcraft": 5000000 "minPriceForRawcraft": 5000000
}, },
"webhook": {
"discordWebhookUrl": "WEBHOOK_URL",
"webhookName": "Flipper",
"webhookPFP": "https://cdn.discordapp.com/avatars/486155512568741900/164084b936b4461fe9505398f7383a0e.png?size=4096"
},
"filters": { "filters": {
"rawCraftIgnoreEnchants": { "EnchantThresholdConditionalBypass": {
"WITHER_": { "WITHER_": {
"growth": 6, "growth": 6,
"protection": 6 "protection": 6
@ -39,7 +34,7 @@
"protection": 6 "protection": 6
}, },
"NECROMANCER": { "NECROMANCER": {
"growth": 6, "growth": 4,
"protection": 6 "protection": 6
}, },
"SHREDDED": { "SHREDDED": {
@ -50,7 +45,7 @@
"protection": 6 "protection": 6
} }
}, },
"badEnchants": { "EnchantThreshold": {
"giant_killer": 6, "giant_killer": 6,
"growth": 6, "growth": 6,
"power": 6, "power": 6,
@ -63,12 +58,12 @@
"vampirism": 6, "vampirism": 6,
"luck": 6, "luck": 6,
"syphon": 4, "syphon": 4,
"ultimate_soul_eater": 3, "ultimate_soul_eater": 6,
"ultimate_wise": 4, "ultimate_wise": 5,
"ultimate_wisdom": 4, "ultimate_wisdom": 5,
"ultimate_legion": 3 "ultimate_legion": 3
}, },
"nameFilter": [ "itemIDExclusions": [
"SALMON", "SALMON",
"PERFECT", "PERFECT",
"BEASTMASTER", "BEASTMASTER",

12
docker-compose.yml Normal file
View file

@ -0,0 +1,12 @@
---
services:
hypixel-flipper:
container_name: hypixel-flipper
build: .
restart: unless-stopped
env_file:
- .env
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- ./config.json:/app/config.json

252
index.js
View file

@ -1,10 +1,11 @@
const { default: axios } = require("axios"); const { default: axios } = require('axios');
const config = require("./config.json"); const config = require('./config.json');
const { WebhookClient, MessageEmbed } = require('discord.js'); const { WebhookClient, EmbedBuilder, Embed } = require('discord.js');
const { Worker } = require("worker_threads"); const { Worker } = require('worker_threads');
const { asyncInterval, addNotation } = require("./src/helperFunctions"); const { asyncInterval, addNotation } = require('./src/helperFunctions');
const { string } = require('prismarine-nbt');
let threadsToUse = config.data["threadsToUse/speed"] ?? 1; let threadsToUse = config.data['threadsToUse/speed'] ?? 1;
let lastUpdated = 0; let lastUpdated = 0;
let doneWorkers = 0; let doneWorkers = 0;
let startingTime; let startingTime;
@ -14,124 +15,171 @@ const workers = [];
const webhookRegex = /https:\/\/discord.com\/api\/webhooks\/(.+)\/(.+)/; const webhookRegex = /https:\/\/discord.com\/api\/webhooks\/(.+)\/(.+)/;
const bazaarPrice = { const bazaarPrice = {
"RECOMBOBULATOR_3000": 0, RECOMBOBULATOR_3000: 0,
"HOT_POTATO_BOOK": 0, HOT_POTATO_BOOK: 0,
"FUMING_POTATO_BOOK": 0 FUMING_POTATO_BOOK: 0,
}; };
async function initialize() { async function initialize() {
const matches = config.webhook.discordWebhookUrl.match(webhookRegex); const matches = process.env.webhook_url.match(webhookRegex);
if (!matches) return console.log(`[Main thread] Couldn't parse Webhook URL`); if (!matches) return console.log(`[Main thread] Couldn't parse Webhook URL`);
const webhook = new WebhookClient({ id: matches[1], token: matches[2] }); const webhook = new WebhookClient({ id: matches[1], token: matches[2] });
await getBzData(); await getBzData();
await getMoulberry(); await getMoulberry();
await getLBINs(); await getLBINs();
for (let j = 0; j < threadsToUse; j++) { for (let j = 0; j < threadsToUse; j++) {
workers[j] = new Worker('./AuctionHandler.js', { workers[j] = new Worker('./AuctionHandler.js', {
workerData: { workerData: {
itemDatas: itemDatas, itemDatas: itemDatas,
bazaarData: bazaarPrice, bazaarData: bazaarPrice,
workerNumber: j, workerNumber: j,
maxPrice: maxPrice maxPrice: maxPrice,
} },
}); });
workers[j].on("message", async (result) => { workers[j].on('message', async (result) => {
if (result.itemData !== undefined) { if (result.itemData !== undefined) {
let averagePrice = itemDatas[result.itemData.id]?.cleanPrice || "N/A"; let averagePrice = itemDatas[result.itemData.id]?.cleanPrice || 'N/A';
if (result.auctionData.lbin - result.auctionData.price >= config.data.minSnipeProfit && averagePrice - result.auctionData.price >= config.data.minAvgProfit) { if (
let mustBuyMessage = ''; result.auctionData.lbin - result.auctionData.price >=
const embed = new MessageEmbed() config.data.minSnipeProfit &&
.setTitle(`**${(result.itemData.name).replace(/§./g, '')}**`) averagePrice - result.auctionData.price >= config.data.minAvgProfit
.setColor("#2e3137") ) {
.setThumbnail(`https://sky.shiiyu.moe/item/${result.itemData.id}`) let mustBuyMessage = '';
.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: \`${addNotation("oneLetters", averagePrice)}\``); 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: \`${addNotation('oneLetters', averagePrice)}\``
);
await webhook.send({ await webhook.send({
username: config.webhook.webhookName, username: process.env.webhook_name,
avatarURL: config.webhook.webhookPFP, avatarURL: process.env.webhook_profile_picture_link,
embeds: [embed] embeds: [embed],
}); });
} }
} else if (result === "finished") { } else if (result === 'finished') {
doneWorkers++; doneWorkers++;
if (doneWorkers === threadsToUse) { if (doneWorkers === threadsToUse) {
doneWorkers = 0; doneWorkers = 0;
console.log(`Completed in ${(Date.now() - startingTime) / 1000} seconds`); console.log(
startingTime = 0; `Completed in ${(Date.now() - startingTime) / 1000} seconds`
workers[0].emit("done"); );
} startingTime = 0;
} workers[0].emit('done');
}); }
} }
});
}
asyncInterval(async () => { asyncInterval(
await getLBINs(); async () => {
workers.forEach((worker) => { await getLBINs();
worker.postMessage({ type: "moulberry", data: itemDatas }); workers.forEach((worker) => {
}); worker.postMessage({ type: 'moulberry', data: itemDatas });
}, "lbin", 60000); });
},
'lbin',
60000
);
asyncInterval(async () => { asyncInterval(
await getMoulberry(); async () => {
workers.forEach((worker) => { await getMoulberry();
worker.postMessage({ type: "moulberry", data: itemDatas }); workers.forEach((worker) => {
}); worker.postMessage({ type: 'moulberry', data: itemDatas });
}, "avg", 60e5); });
},
'avg',
60e5
);
asyncInterval(async () => { asyncInterval(
return new Promise(async (resolve) => { async () => {
const ahFirstPage = await axios.get("https://api.hypixel.net/skyblock/auctions?page=0"); return new Promise(async (resolve) => {
const totalPages = ahFirstPage.data.totalPages; const ahFirstPage = await axios.get(
if (ahFirstPage.data.lastUpdated === lastUpdated) { 'https://api.hypixel.net/skyblock/auctions?page=0'
resolve(); );
} else { const totalPages = ahFirstPage.data.totalPages;
lastUpdated = ahFirstPage.data.lastUpdated; if (ahFirstPage.data.lastUpdated === lastUpdated) {
startingTime = Date.now(); resolve();
console.log("Getting auctions.."); } else {
workers.forEach((worker) => { lastUpdated = ahFirstPage.data.lastUpdated;
worker.postMessage({ type: "pageCount", data: totalPages }); startingTime = Date.now();
}); console.log('Getting auctions..');
workers[0].once("done", () => { workers.forEach((worker) => {
resolve(); worker.postMessage({ type: 'pageCount', data: totalPages });
}); });
} workers[0].once('done', () => {
}); resolve();
}, "check", 0); });
}
});
},
'check',
0
);
} }
async function getLBINs() { async function getLBINs() {
const lbins = await axios.get("https://moulberry.codes/lowestbin.json"); const lbins = await axios.get('https://moulberry.codes/lowestbin.json');
const lbinData = lbins.data; const lbinData = lbins.data;
for (const item of Object.keys(lbinData)) { for (const item of Object.keys(lbinData)) {
if (!itemDatas[item]) itemDatas[item] = {}; if (!itemDatas[item]) itemDatas[item] = {};
itemDatas[item].lbin = lbinData[item]; itemDatas[item].lbin = lbinData[item];
} }
} }
async function getMoulberry() { async function getMoulberry() {
const moulberryAvgs = await axios.get("https://moulberry.codes/auction_averages/3day.json"); const moulberryAvgs = await axios.get(
const avgData = moulberryAvgs.data; '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 cleanPriceAvgs = await axios.get(
const cleanPriceData = cleanPriceAvgs.data; 'https://moulberry.codes/auction_averages_lbin/1day.json'
);
const cleanPriceData = cleanPriceAvgs.data;
for (const item of Object.keys(avgData)) { for (const item of Object.keys(avgData)) {
if (!itemDatas[item]) itemDatas[item] = {}; if (!itemDatas[item]) itemDatas[item] = {};
const itemInfo = avgData[item]; const itemInfo = avgData[item];
itemDatas[item].sales = itemInfo.sales !== undefined ? itemInfo.sales : 0; 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); itemDatas[item].cleanPrice =
} cleanPriceData[item] !== undefined
? Math.round(cleanPriceData[item])
: itemInfo.clean_price !== undefined
? itemInfo.clean_price
: itemInfo.price;
}
} }
async function getBzData() { async function getBzData() {
const bzData = await axios.get("https://api.hypixel.net/skyblock/bazaar"); const bzData = await axios.get('https://api.hypixel.net/skyblock/bazaar');
bazaarPrice["RECOMBOBULATOR_3000"] = bzData.data.products.RECOMBOBULATOR_3000.quick_status.buyPrice; bazaarPrice['RECOMBOBULATOR_3000'] =
bazaarPrice["HOT_POTATO_BOOK"] = bzData.data.products.HOT_POTATO_BOOK.quick_status.buyPrice; bzData.data.products.RECOMBOBULATOR_3000.quick_status.buyPrice;
bazaarPrice["FUMING_POTATO_BOOK"] = bzData.data.products.FUMING_POTATO_BOOK.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(); initialize();

View file

@ -4,7 +4,7 @@
"dependencies": { "dependencies": {
"axios": "^0.24.0", "axios": "^0.24.0",
"copy-paste": "^1.3.0", "copy-paste": "^1.3.0",
"discord.js": "^12.5.3", "discord.js": "^14.16.3",
"express": "^4.17.1", "express": "^4.17.1",
"node-notifier": "^10.0.0", "node-notifier": "^10.0.0",
"open": "^8.4.0", "open": "^8.4.0",
@ -14,7 +14,6 @@
}, },
"description": "Hypixel Skyblock Auction House Flip Notifier", "description": "Hypixel Skyblock Auction House Flip Notifier",
"main": "index.js", "main": "index.js",
"devDependencies": {},
"scripts": { "scripts": {
"start": "node ." "start": "node ."
}, },

1191
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff

View file

@ -90,17 +90,25 @@ function splitNumber (num = 1, parts = 1) {
function getRawCraft(item, bazaarPrice, lbins) { function getRawCraft(item, bazaarPrice, lbins) {
let price = 0 let price = 0
const ignoreMatch = Object.keys(config.filters.rawCraftIgnoreEnchants).find((key) => { const ignoreMatch = Object.keys(
if (item.itemData.id.includes(key)) return true config.filters.EnchantThresholdConditionalBypass
}) ).find((key) => {
if (item.itemData.id.includes(key)) return true;
});
if (item.auctionData.lbin < config.data.minPriceForRawcraft) return 0 if (item.auctionData.lbin < config.data.minPriceForRawcraft) return 0
let isInIgnore = ignoreMatch ? ignoreMatch : false let isInIgnore = ignoreMatch ? ignoreMatch : false
if (item.itemData.enchants && !item.itemData.id.includes(';')) { if (item.itemData.enchants && !item.itemData.id.includes(';')) {
for (const enchant of Object.keys(item.itemData.enchants)) { for (const enchant of Object.keys(item.itemData.enchants)) {
const degree = item.itemData.enchants[enchant] const degree = item.itemData.enchants[enchant]
const badEnchant = typeof config.filters.badEnchants[enchant] === 'number' ? degree >= config.filters.badEnchants[enchant] : false const badEnchant =
typeof config.filters.EnchantThreshold[enchant] === 'number'
? degree >= config.filters.EnchantThreshold[enchant]
: false;
if (isInIgnore) { if (isInIgnore) {
const enchantMinValue = config.filters.rawCraftIgnoreEnchants[ignoreMatch][enchant] const enchantMinValue =
config.filters.EnchantThresholdConditionalBypass[ignoreMatch][
enchant
];
if (enchantMinValue >= degree) continue if (enchantMinValue >= degree) continue
} }
if (badEnchant) { if (badEnchant) {