1.0
This commit is contained in:
parent
deb523be0f
commit
4423004153
6 changed files with 548 additions and 0 deletions
101
AuctionHandler.js
Normal file
101
AuctionHandler.js
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
const {default: axios} = require("axios");
|
||||||
|
const {getParsed, getProfit, splitNumber, getRawCraft} = require("./src/helperFunctions");
|
||||||
|
const {parentPort, workerData} = require("worker_threads");
|
||||||
|
const config = require("./config.json")
|
||||||
|
let minProfit = config.data.minSnipeProfit
|
||||||
|
let minPercentProfit = config.data.minSnipePP
|
||||||
|
let ignoredAuctions = []
|
||||||
|
const {Item} = require("./src/Item")
|
||||||
|
const threadsToUse = require("./config.json").data["threadsToUse/speed"]
|
||||||
|
const promises = []
|
||||||
|
|
||||||
|
parentPort.on("message", async (message) => {
|
||||||
|
if (message.type === "pageCount") {
|
||||||
|
await doTask(message.data)
|
||||||
|
} else if (message.type === "moulberry") {
|
||||||
|
workerData.itemDatas = message.data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function parsePage(i) {
|
||||||
|
const auctionPage = await axios.get(`https://api.hypixel.net/skyblock/auctions?page=${i}`)
|
||||||
|
for (const auction of auctionPage.data.auctions) {
|
||||||
|
if (!auction.bin) continue
|
||||||
|
const uuid = auction.uuid
|
||||||
|
if (ignoredAuctions.includes(uuid) || config.data.ignoreCategories[auction.category]) continue
|
||||||
|
const item = await getParsed(auction.item_bytes)
|
||||||
|
const extraAtt = item["i"][0].tag.ExtraAttributes
|
||||||
|
const itemID = extraAtt.id
|
||||||
|
let startingBid = auction.starting_bid
|
||||||
|
const itemData = workerData.itemDatas[itemID]
|
||||||
|
if (!itemData) continue
|
||||||
|
const lbin = itemData.lbin
|
||||||
|
const sales = itemData.sales
|
||||||
|
const prettyItem = new Item(item.i[0].tag.display.Name, 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 (config.filters.nameFilter.find((name) => itemID.includes(name)) === undefined) {
|
||||||
|
if ((lbin + rcCost) - startingBid > minProfit) {
|
||||||
|
const profitData = getProfit(startingBid, rcCost, lbin)
|
||||||
|
let auctionType = null
|
||||||
|
if (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
|
||||||
|
|
||||||
|
if (auctionType === "VALUE" || auctionType === "BOTH") {
|
||||||
|
if (profitData.RCProfit > config.data.minCraftProfit && profitData.RCPP > config.data.minCraftPP) {
|
||||||
|
prettyItem.auctionData.profit = profitData.RCProfit
|
||||||
|
prettyItem.auctionData.percentProfit = profitData.RCPP
|
||||||
|
parentPort.postMessage(prettyItem)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (profitData.snipeProfit > minProfit && profitData.snipePP > minPercentProfit) {
|
||||||
|
prettyItem.auctionData.profit = profitData.snipeProfit
|
||||||
|
prettyItem.auctionData.percentProfit = profitData.snipePP
|
||||||
|
parentPort.postMessage(prettyItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doTask(totalPages) {
|
||||||
|
let startingPage = 0
|
||||||
|
const pagePerThread = splitNumber(totalPages, threadsToUse)
|
||||||
|
|
||||||
|
if (workerData.workerNumber !== 0 && startingPage === 0) {
|
||||||
|
const clonedStarting = pagePerThread.slice()
|
||||||
|
clonedStarting.splice(workerData.workerNumber, 9999);
|
||||||
|
clonedStarting.forEach((pagePer) => {
|
||||||
|
startingPage += pagePer
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let pageToStop = parseInt(startingPage) + parseInt(pagePerThread[workerData.workerNumber])
|
||||||
|
|
||||||
|
if (pageToStop !== totalPages) {
|
||||||
|
pageToStop -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = startingPage; i < pageToStop; i++) {
|
||||||
|
promises.push(parsePage(i))
|
||||||
|
}
|
||||||
|
await Promise.all(promises)
|
||||||
|
//parentPort.postMessage("finished")
|
||||||
|
}
|
106
config.json
Normal file
106
config.json
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"threadsToUse/speed": 64,
|
||||||
|
|
||||||
|
"minSnipeProfit": 100000,
|
||||||
|
"minCraftProfit": 100000,
|
||||||
|
|
||||||
|
"maxAvgLbinDiff": 0.35,
|
||||||
|
"rawCraftMaxWeightPP": 0.4,
|
||||||
|
"minSnipePP": 8,
|
||||||
|
"minCraftPP": 8,
|
||||||
|
|
||||||
|
"ignoreCategories": {
|
||||||
|
"weapon": false,
|
||||||
|
"accessories": true,
|
||||||
|
"armor": false,
|
||||||
|
"misc": false,
|
||||||
|
"blocks": false,
|
||||||
|
"consumables": true
|
||||||
|
},
|
||||||
|
"minSales": 4,
|
||||||
|
"includeCraftCost": true,
|
||||||
|
"minPriceForRawcraft": 5000000
|
||||||
|
},
|
||||||
|
"webhook": {
|
||||||
|
"discordWebhookUrl": "WEBHOOK_URL",
|
||||||
|
"webhookName": "Flipper",
|
||||||
|
"webhookPFP": "https://cdn.discordapp.com/avatars/486155512568741900/164084b936b4461fe9505398f7383a0e.png?size=4096"
|
||||||
|
},
|
||||||
|
"filters": {
|
||||||
|
"rawCraftIgnoreEnchants": {
|
||||||
|
"WITHER_": {
|
||||||
|
"growth": 6,
|
||||||
|
"protection": 6
|
||||||
|
},
|
||||||
|
"SHADOW_ASSASSIN": {
|
||||||
|
"growth": 6,
|
||||||
|
"protection": 6
|
||||||
|
},
|
||||||
|
"NECROMANCER": {
|
||||||
|
"growth": 6,
|
||||||
|
"protection": 6
|
||||||
|
},
|
||||||
|
"SHREDDED": {
|
||||||
|
"ultimate_soul_eater": 5
|
||||||
|
},
|
||||||
|
"GOLD_": {
|
||||||
|
"growth": 6,
|
||||||
|
"protection": 6
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"badEnchants": {
|
||||||
|
"giant_killer": 6,
|
||||||
|
"growth": 6,
|
||||||
|
"power": 6,
|
||||||
|
"protection": 6,
|
||||||
|
"sharpness": 6,
|
||||||
|
"ender_slayer": 6,
|
||||||
|
"smite": 6,
|
||||||
|
"critical": 6,
|
||||||
|
"bane_of arthropods": 6,
|
||||||
|
"vampirism": 6,
|
||||||
|
"luck": 6,
|
||||||
|
"syphon": 4,
|
||||||
|
"ultimate_soul_eater": 3,
|
||||||
|
"ultimate_wise": 4,
|
||||||
|
"ultimate_wisdom": 4,
|
||||||
|
"ultimate_legion": 3
|
||||||
|
},
|
||||||
|
"nameFilter": [
|
||||||
|
"SALMON",
|
||||||
|
"PERFECT",
|
||||||
|
"BEASTMASTER",
|
||||||
|
"MASTER_SKULL",
|
||||||
|
"BLAZE",
|
||||||
|
"TITANIUM",
|
||||||
|
"SUPER_HEAVY",
|
||||||
|
"WAND_OF",
|
||||||
|
"FARM_ARMOR",
|
||||||
|
"PURE_MITHRIL",
|
||||||
|
"STEEL_CHESTPLATE",
|
||||||
|
"MIDAS",
|
||||||
|
"TRIBAL_SPEAR",
|
||||||
|
"POWER_SCROLL",
|
||||||
|
"_TRAVEL_SCROLL",
|
||||||
|
"ARTISINAL",
|
||||||
|
"ZOMBIE",
|
||||||
|
"CRYPT",
|
||||||
|
"SOULSTEALER",
|
||||||
|
"GUN",
|
||||||
|
"SPIRIT_DECOY",
|
||||||
|
"EXP",
|
||||||
|
"PET_SKIN",
|
||||||
|
"SEEKER",
|
||||||
|
"MOSQUITO",
|
||||||
|
"REVELATION",
|
||||||
|
"FRAGMENT",
|
||||||
|
"RECALL",
|
||||||
|
"HEAT_CORE",
|
||||||
|
"DIVER",
|
||||||
|
"SPONGE",
|
||||||
|
"CREEPER",
|
||||||
|
"CAKE_SOUL"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
139
index.js
Normal file
139
index.js
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
const {default: axios} = require("axios")
|
||||||
|
const config = require("./config.json")
|
||||||
|
const discord = require('discord.js')
|
||||||
|
const {Worker} = require("worker_threads")
|
||||||
|
const { asyncInterval, addNotation } = require("./src/helperFunctions")
|
||||||
|
let threadsToUse = config.data["threadsToUse/speed"] ?? 1
|
||||||
|
let lastUpdated = 0
|
||||||
|
let doneWorkers = 0
|
||||||
|
let startingTime
|
||||||
|
let maxPrice = 0
|
||||||
|
let itemDatas = {}
|
||||||
|
const workers = []
|
||||||
|
const webhookRegex = /https:\/\/discord.com\/api\/webhooks\/(.+)\/(.+)/
|
||||||
|
|
||||||
|
const bazaarPrice = {
|
||||||
|
"RECOMBOBULATOR_3000": 0,
|
||||||
|
"HOT_POTATO_BOOK": 0,
|
||||||
|
"FUMING_POTATO_BOOK": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initialize() {
|
||||||
|
matches = config.webhook.discordWebhookUrl.match(webhookRegex)
|
||||||
|
if (!matches) return console.log(`[Main thread] Couldn't parse Webhook URL`)
|
||||||
|
const webhook = new discord.WebhookClient(matches[1], matches[2]);
|
||||||
|
|
||||||
|
await getBzData()
|
||||||
|
await getMoulberry()
|
||||||
|
await getLBINs()
|
||||||
|
|
||||||
|
for (let j = 0; j < threadsToUse; j++) {
|
||||||
|
workers[j] = new Worker('./AuctionHandler.js', {
|
||||||
|
workerData: {
|
||||||
|
itemDatas: itemDatas,
|
||||||
|
bazaarData: bazaarPrice,
|
||||||
|
workerNumber: j,
|
||||||
|
maxPrice: maxPrice
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
workers[j].on("message", async (result) => {
|
||||||
|
if (result.itemData !== undefined) {
|
||||||
|
if (result.auctionData.lbin >= result.auctionData.price) {
|
||||||
|
await webhook.send({
|
||||||
|
username: config.webhook.webhookName,
|
||||||
|
avatarURL: config.webhook.webhookPFP,
|
||||||
|
embeds: [new discord.MessageEmbed()
|
||||||
|
.setTitle(`**${(result.itemData.name).replaceAll(/§./g, '')}**`)
|
||||||
|
.setColor("#2e3137")
|
||||||
|
.setThumbnail(`https://sky.shiiyu.moe/item/${result.itemData.id}`)
|
||||||
|
.setDescription(`Auction: \`/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}\``)
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
} else if (result === "finished") {
|
||||||
|
doneWorkers++
|
||||||
|
if (doneWorkers === threadsToUse) {
|
||||||
|
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() {
|
||||||
|
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] = {}
|
||||||
|
itemDatas[item].lbin = lbinData[item]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getMoulberry() {
|
||||||
|
const moulberryAvgs = await axios.get("https://moulberry.codes/auction_averages/3day.json")
|
||||||
|
const avgData = moulberryAvgs.data
|
||||||
|
for (const item of Object.keys(avgData)) {
|
||||||
|
itemDatas[item] = {}
|
||||||
|
const itemInfo = avgData[item]
|
||||||
|
if (itemInfo.sales !== undefined) {
|
||||||
|
itemDatas[item].sales = itemInfo.sales
|
||||||
|
} else {
|
||||||
|
itemDatas[item].sales = 0
|
||||||
|
}
|
||||||
|
if (itemInfo.clean_price) {
|
||||||
|
itemDatas[item].cleanPrice = itemInfo.clean_price
|
||||||
|
} else {
|
||||||
|
itemDatas[item].cleanPrice = itemInfo.price
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getBzData() {
|
||||||
|
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()
|
23
package.json
Normal file
23
package.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "hypixel-auction-flipper",
|
||||||
|
"version": "0.6.9",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.24.0",
|
||||||
|
"copy-paste": "^1.3.0",
|
||||||
|
"discord.js": "^12.5.3",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"node-notifier": "^10.0.0",
|
||||||
|
"open": "^8.4.0",
|
||||||
|
"prismarine-nbt": "^2.0.0",
|
||||||
|
"socket.io": "^4.4.0",
|
||||||
|
"toastify-js": "^1.11.2"
|
||||||
|
},
|
||||||
|
"description": "Hypixel Skyblock Auction House Flip Notifier",
|
||||||
|
"main": "index.js",
|
||||||
|
"devDependencies": {},
|
||||||
|
"scripts": {
|
||||||
|
"start": "node ."
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "DuckySoLucky"
|
||||||
|
}
|
28
src/Item.js
Normal file
28
src/Item.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
function Item(name, auctionID, price, rarity, enchants, hpbs, fpbs, recomb, artofwar, stars, gemstones, id, category, profit, percentProfit, lbin, sales, lore) {
|
||||||
|
this.itemData = {
|
||||||
|
"name": name,
|
||||||
|
"id": id,
|
||||||
|
"stars": stars,
|
||||||
|
"rarity": rarity,
|
||||||
|
"recomb": recomb,
|
||||||
|
"enchants": enchants,
|
||||||
|
"hpbs": hpbs,
|
||||||
|
"fpbs": fpbs,
|
||||||
|
"gemstones": gemstones,
|
||||||
|
"aow": artofwar,
|
||||||
|
"lore": lore
|
||||||
|
}
|
||||||
|
this.auctionData = {
|
||||||
|
"auctionID": auctionID,
|
||||||
|
"category": category,
|
||||||
|
"sales": sales,
|
||||||
|
"price": price,
|
||||||
|
"profit": profit,
|
||||||
|
"percentProfit": percentProfit,
|
||||||
|
"lbin": lbin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Item
|
||||||
|
}
|
151
src/helperFunctions.js
Normal file
151
src/helperFunctions.js
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
const config = require('../config.json')
|
||||||
|
const nbt = require('prismarine-nbt')
|
||||||
|
let currentAsyncIntervals = {}
|
||||||
|
|
||||||
|
function addNotation(type, value) {
|
||||||
|
let returnVal = value;
|
||||||
|
let notList = [];
|
||||||
|
if (type === "shortScale") {
|
||||||
|
notList = [
|
||||||
|
" Thousand",
|
||||||
|
" Million",
|
||||||
|
" Billion",
|
||||||
|
" Trillion",
|
||||||
|
" Quadrillion",
|
||||||
|
" Quintillion"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "oneLetters") {
|
||||||
|
notList = ["K", "M", "B", "T"];
|
||||||
|
}
|
||||||
|
|
||||||
|
let checkNum = 1000;
|
||||||
|
if (type !== "none" && type !== "commas") {
|
||||||
|
let notValue = notList[notList.length - 1];
|
||||||
|
for (let u = notList.length; u >= 1; u--) {
|
||||||
|
notValue = notList.shift();
|
||||||
|
for (let o = 3; o >= 1; o--) {
|
||||||
|
if (value >= checkNum) {
|
||||||
|
returnVal = value / (checkNum / 100);
|
||||||
|
returnVal = Math.floor(returnVal);
|
||||||
|
returnVal = (returnVal / Math.pow(10, o)) * 10;
|
||||||
|
returnVal = +returnVal.toFixed(o - 1) + notValue;
|
||||||
|
}
|
||||||
|
checkNum *= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
returnVal = numberWithCommas(value.toFixed(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getParsed(encoded) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let buf = Buffer.from(encoded, 'base64');
|
||||||
|
nbt.parse(buf, (err, dat) => {
|
||||||
|
if (err) throw err;
|
||||||
|
resolve(nbt.simplify(dat))
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProfit(price, rcCost, lbin) {
|
||||||
|
const profitItem = {}
|
||||||
|
if (price >= 1000000) {
|
||||||
|
profitItem.RCProfit = ((lbin + rcCost) - price)
|
||||||
|
- ((lbin + rcCost) * 0.02);
|
||||||
|
profitItem.RCPP = parseFloat(((profitItem.RCProfit * 100) / lbin).toFixed(1))
|
||||||
|
profitItem.snipeProfit = (lbin - price) - (lbin * 0.02)
|
||||||
|
profitItem.snipePP = parseFloat(((profitItem.snipeProfit * 100) / lbin).toFixed(1))
|
||||||
|
} else {
|
||||||
|
profitItem.RCProfit = ((lbin + rcCost) - price)
|
||||||
|
- ((lbin + rcCost) * 0.01);
|
||||||
|
profitItem.RCPP = parseFloat(((profitItem.RCProfit * 100) / lbin).toFixed(1))
|
||||||
|
profitItem.snipeProfit = (lbin - price) - (lbin * 0.01)
|
||||||
|
profitItem.snipePP = parseFloat(((profitItem.snipeProfit * 100) / lbin).toFixed(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
return profitItem
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitNumber (num = 1, parts = 1) {
|
||||||
|
let n = Math.floor(num / parts);
|
||||||
|
const arr = [];
|
||||||
|
for (let i = 0; i < parts; i++){
|
||||||
|
arr.push(n)
|
||||||
|
}
|
||||||
|
if(arr.reduce((a, b)=> a + b,0) === num){
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
for(let i = 0; i < parts; i++){
|
||||||
|
arr[i]++;
|
||||||
|
if(arr.reduce((a, b) => a + b, 0) === num){
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRawCraft(item, bazaarPrice, lbins) {
|
||||||
|
let price = 0
|
||||||
|
const ignoreMatch = Object.keys(config.filters.rawCraftIgnoreEnchants).find((key) => {
|
||||||
|
if (item.itemData.id.includes(key)) return true
|
||||||
|
})
|
||||||
|
if (item.auctionData.lbin < config.data.minPriceForRawcraft) return 0
|
||||||
|
let isInIgnore = ignoreMatch ? ignoreMatch : false
|
||||||
|
if (item.itemData.enchants && !item.itemData.id.includes(';')) {
|
||||||
|
for (const enchant of Object.keys(item.itemData.enchants)) {
|
||||||
|
const degree = item.itemData.enchants[enchant]
|
||||||
|
const badEnchant = typeof config.filters.badEnchants[enchant] === 'number' ? degree >= config.filters.badEnchants[enchant] : false
|
||||||
|
if (isInIgnore) {
|
||||||
|
const enchantMinValue = config.filters.rawCraftIgnoreEnchants[ignoreMatch][enchant]
|
||||||
|
if (enchantMinValue >= degree) continue
|
||||||
|
}
|
||||||
|
if (badEnchant) {
|
||||||
|
price += lbins[`${enchant.toUpperCase()};${degree.toString()}`] ? lbins[`${enchant.toUpperCase()};${degree.toString()}`].lbin * 0.5 : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item.itemData.aow) {
|
||||||
|
price += lbins['THE_ART_OF_WAR'] * 0.3
|
||||||
|
}
|
||||||
|
if (item.itemData.recomb && (item.auctionData.category === 'weapon' || item.auctionData.category === 'armor' || item.auctionData.category === 'accessories')) {
|
||||||
|
price += bazaarPrice['RECOMBOBULATOR_3000'] * 0.5
|
||||||
|
}
|
||||||
|
price += (item.itemData.hpbs ? item.itemData.hpbs : 0) * bazaarPrice['HOT_POTATO_BOOK'] * 0.05
|
||||||
|
price += (item.itemData.fpbs ? item.itemData.fpbs : 0) * bazaarPrice['FUMING_POTATO_BOOK'] * 0.1
|
||||||
|
|
||||||
|
return price
|
||||||
|
}
|
||||||
|
|
||||||
|
async function asyncInterval(asyncTask, intervalname, timeout) {
|
||||||
|
currentAsyncIntervals[intervalname] = true
|
||||||
|
setTimeout(async function () {
|
||||||
|
if (!currentAsyncIntervals[intervalname]) return
|
||||||
|
asyncTask().then(async function () {
|
||||||
|
await asyncInterval(asyncTask, intervalname, timeout)
|
||||||
|
})
|
||||||
|
}, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopAsyncInterval(intervalname) {
|
||||||
|
currentAsyncIntervals[intervalname] = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function currentIntervals() {
|
||||||
|
return currentAsyncIntervals
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
addNotation,
|
||||||
|
getParsed,
|
||||||
|
getProfit,
|
||||||
|
splitNumber,
|
||||||
|
getRawCraft,
|
||||||
|
asyncInterval,
|
||||||
|
stopAsyncInterval,
|
||||||
|
currentIntervals
|
||||||
|
}
|
Loading…
Reference in a new issue