From f5b8f5a544f327c331892263be11fa3c6d073423 Mon Sep 17 00:00:00 2001 From: gc <30398469+gc@users.noreply.github.com> Date: Fri, 19 Apr 2024 05:02:31 +1000 Subject: [PATCH 001/249] Add items --- src/lib/customItems/customItems.ts | 26 ++++++++++++++++++++++++++ tests/unit/sanity.test.ts | 4 +++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/lib/customItems/customItems.ts b/src/lib/customItems/customItems.ts index aa4bd93bb9d..b80b2d41747 100644 --- a/src/lib/customItems/customItems.ts +++ b/src/lib/customItems/customItems.ts @@ -12469,3 +12469,29 @@ setCustomItem( }, 100_000 ); + +setCustomItem(73_185, 'Frost mask', 'Bronze full helm', {}, 100_000); + +setCustomItem( + 73_186, + 'Nex plushie', + 'Coal', + { + tradeable: false + }, + 100_000 +); + +setCustomItem(73_187, 'Dunce hat', 'Bronze full helm', {}, 100_000); + +setCustomItem(73_188, 'Dunce top', 'Bronze platebody', {}, 100_000); + +setCustomItem(73_189, 'Dunce legs', 'Bronze platelegs', {}, 100_000); + +setCustomItem(73_190, 'Dunce gloves', 'Bronze gloves', {}, 100_000); + +setCustomItem(73_191, 'Dunce shoes', 'Bronze boots', {}, 100_000); + +setCustomItem(73_192, 'Chilli chocolate', 'Coal', {}, 100_000); + +setCustomItem(73_193, 'Zak plushie', 'Coal', { tradeable: false }, 100_000); diff --git a/tests/unit/sanity.test.ts b/tests/unit/sanity.test.ts index a990423a09c..e35fcc13772 100644 --- a/tests/unit/sanity.test.ts +++ b/tests/unit/sanity.test.ts @@ -116,7 +116,9 @@ describe('Sanity', () => { 'Pure essence', 'Runite bolts', 'Lava flower crown', - 'Purple flower crown' + 'Purple flower crown', + 'Zak plushie', + 'Dunce hat' ]); // These items should all still excluded by the 'Openables' rule. Some items are also excluded by other means. const shouldntBeIn = resolveItems([ From 7fa725ed6194dcfa44232706de4d368a1001ae9a Mon Sep 17 00:00:00 2001 From: GC <30398469+gc@users.noreply.github.com> Date: Sat, 11 May 2024 17:13:05 +1000 Subject: [PATCH 002/249] Add simple run test for all commands (#5729) --- package.json | 2 + src/lib/bankImage.ts | 6 +- src/lib/constants.ts | 2 +- src/lib/geImage.ts | 69 +--- src/lib/settings/prisma.ts | 4 + src/lib/skilling/functions/miningBoosts.ts | 38 +- .../skills/herblore/mixables/barbMixes.ts | 5 +- src/mahoji/commands/create.ts | 2 +- src/mahoji/commands/k.ts | 2 +- src/mahoji/commands/leaderboard.ts | 4 +- src/mahoji/commands/mine.ts | 18 +- src/mahoji/commands/minion.ts | 5 +- src/mahoji/commands/offer.ts | 2 +- src/mahoji/commands/testpotato.ts | 6 + .../lib/abstracted_commands/barbAssault.ts | 2 +- tests/globalSetup.ts | 26 +- tests/integration/allCommandsBase.test.ts | 337 +++++++++++++++--- tests/integration/grandExchange.test.ts | 14 +- tests/integration/migrateUser.test.ts | 4 - tests/integration/mocks.ts | 41 +++ tests/integration/monsterKilling.test.ts | 9 +- tests/integration/setup.ts | 59 ++- tests/integration/trading.test.ts | 6 +- tests/integration/util.ts | 20 +- tests/unit/utils.ts | 9 +- vitest.integration.config.mts | 4 +- yarn.lock | 53 ++- 27 files changed, 517 insertions(+), 232 deletions(-) create mode 100644 tests/integration/mocks.ts diff --git a/package.json b/package.json index f77fb0566a5..a315f22b85e 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "@types/jest-image-snapshot": "^6.1.0", "@types/lodash": "^4.14.195", "@types/madge": "^5.0.0", + "@types/mitm": "^1.3.8", "@types/node": "^14.18.12", "@types/node-cron": "^3.0.7", "@types/node-fetch": "^2.6.1", @@ -82,6 +83,7 @@ "eslint-plugin-simple-import-sort": "^8.0.0", "eslint-plugin-unicorn": "^44.0.2", "jest-image-snapshot": "^6.2.0", + "mitm": "^1.7.2", "madge": "^7.0.0", "prettier": "^2.7.1", "prisma": "^5.13.0", diff --git a/src/lib/bankImage.ts b/src/lib/bankImage.ts index e6eebfb727d..0afc8137c54 100644 --- a/src/lib/bankImage.ts +++ b/src/lib/bankImage.ts @@ -264,7 +264,7 @@ export const bankFlags = [ ] as const; export type BankFlag = (typeof bankFlags)[number]; -class BankImageTask { +export class BankImageTask { public itemIconsList: Set; public itemIconImagesCache: Map; public backgroundImages: BankBackground[] = []; @@ -771,6 +771,7 @@ class BankImageTask { if (!isTransparent && noBorder !== 1) { this.drawBorder(ctx, bgSprite, bgImage.name === 'Default'); } + await this.drawItems( ctx, compact, @@ -951,5 +952,6 @@ declare global { } } } -global.bankImageGenerator = new BankImageTask(); +export const bankImageTask = new BankImageTask(); +global.bankImageGenerator = bankImageTask; bankImageGenerator.init(); diff --git a/src/lib/constants.ts b/src/lib/constants.ts index b856321be4d..caca5f5fbb4 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -575,7 +575,7 @@ const globalConfigSchema = z.object({ patreonCampaignID: z.coerce.number().int().default(1), patreonWebhookSecret: z.coerce.string().default(''), httpPort: z.coerce.number().int().default(8080), - clientID: z.string().min(15).max(25), + clientID: z.string().min(10).max(25), geAdminChannelID: z.string().default('') }); dotenv.config({ path: path.resolve(process.cwd(), process.env.TEST ? '.env.test' : '.env') }); diff --git a/src/lib/geImage.ts b/src/lib/geImage.ts index 1e35e331ad1..d66e6c3b9b0 100644 --- a/src/lib/geImage.ts +++ b/src/lib/geImage.ts @@ -3,16 +3,11 @@ import { formatItemStackQuantity, generateHexColorForCashStack, toTitleCase } fr import { GEListing, GETransaction } from '@prisma/client'; import * as fs from 'fs/promises'; import { floor } from 'lodash'; -import fetch from 'node-fetch'; -import * as path from 'path'; import { GEListingWithTransactions } from './../mahoji/commands/ge'; import { GrandExchange } from './grandExchange'; import { fillTextXTimesInCtx } from './util/canvasUtil'; import getOSItem from './util/getOSItem'; -import { logError } from './util/logError'; - -const CACHE_DIR = './icon_cache'; function drawTitle(ctx: SKRSContext2D, title: string, canvas: Canvas) { // Draw Page Title @@ -34,20 +29,9 @@ class GeImageTask { public geProgressShadow: Image | null = null; public geIconBuy: Image | null = null; public geIconSell: Image | null = null; - public itemIconsList: Set; - public itemIconImagesCache: Map; - - public constructor() { - // This tells us simply whether the file exists or not on disk. - this.itemIconsList = new Set(); - - // If this file does exist, it might be cached in this, or need to be read from fs. - this.itemIconImagesCache = new Map(); - } async init() { await this.prepare(); - await this.run(); } async prepare() { @@ -74,23 +58,6 @@ class GeImageTask { ); } - async run() { - await this.cacheFiles(); - } - - async cacheFiles() { - // Ensure that the icon_cache dir exists. - await fs.mkdir(CACHE_DIR).catch(() => null); - CACHE_DIR; - // Get a list of all files (images) in the dir. - const filesInDir = await fs.readdir(CACHE_DIR); - - // For each one, set a cache value that it exists. - for (const fileName of filesInDir) { - this.itemIconsList.add(parseInt(path.parse(fileName).name)); - } - } - drawText( ctx: SKRSContext2D, text: string, @@ -148,7 +115,7 @@ class GeImageTask { if (listing) { // Get item - const itemImage = await this.getItemImage(listing.item_id); + const itemImage = await bankImageGenerator.getItemImage(listing.item_id); // Draw item ctx.textAlign = 'left'; @@ -221,40 +188,6 @@ class GeImageTask { } } - async getItemImage(itemID: number): Promise { - const cachedImage = this.itemIconImagesCache.get(itemID); - if (cachedImage) return cachedImage; - - const isOnDisk = this.itemIconsList.has(itemID); - if (!isOnDisk) { - await this.fetchAndCacheImage(itemID); - return this.getItemImage(itemID); - } - - const imageBuffer = await fs.readFile(path.join(CACHE_DIR, `${itemID}.png`)); - try { - const image = await loadImage(imageBuffer); - this.itemIconImagesCache.set(itemID, image); - return image; - } catch (err) { - logError(`Failed to load item icon with id: ${itemID}`); - return this.getItemImage(1); - } - } - - async fetchAndCacheImage(itemID: number) { - const imageBuffer = await fetch(`https://chisel.weirdgloop.org/static/img/osrs-sprite/${itemID}.png`).then( - result => result.buffer() - ); - - await fs.writeFile(path.join(CACHE_DIR, `${itemID}.png`), imageBuffer); - - const image = await loadImage(imageBuffer); - - this.itemIconsList.add(itemID); - this.itemIconImagesCache.set(itemID, image); - } - async createInterface(opts: { user: MUser; page: number; diff --git a/src/lib/settings/prisma.ts b/src/lib/settings/prisma.ts index e8c60966145..d8ea48c39ab 100644 --- a/src/lib/settings/prisma.ts +++ b/src/lib/settings/prisma.ts @@ -17,6 +17,10 @@ declare global { function makePrismaClient(): PrismaClient { if (!isMainThread && !process.env.TEST) return null as any; if (!production && !process.env.TEST) console.log('Making prisma client...'); + if (!isMainThread) { + throw new Error('Prisma client should only be created on the main thread.'); + } + return new PrismaClient({ log: [ { diff --git a/src/lib/skilling/functions/miningBoosts.ts b/src/lib/skilling/functions/miningBoosts.ts index 49619def28a..0de48bce533 100644 --- a/src/lib/skilling/functions/miningBoosts.ts +++ b/src/lib/skilling/functions/miningBoosts.ts @@ -1,5 +1,3 @@ -import { Bank } from 'oldschooljs'; - import itemID from '../../util/itemID'; export const pickaxes = [ @@ -55,10 +53,10 @@ export const pickaxes = [ } ]; -export const miningGloves = [ +export const miningGloves: { id: number; Percentages: Record }[] = [ { id: itemID('Expert mining gloves'), - Percentages: new Bank({ + Percentages: { 'Silver ore': 50, Coal: 40, 'Gold ore': 33.33, @@ -66,11 +64,11 @@ export const miningGloves = [ 'Adamantite ore': 16.66, 'Runite ore': 12.5, Amethyst: 25 - }) + } }, { id: itemID('Superior mining gloves'), - Percentages: new Bank({ + Percentages: { 'Silver ore': 0, Coal: 0, 'Gold ore': 0, @@ -78,11 +76,11 @@ export const miningGloves = [ 'Adamantite ore': 16.66, 'Runite ore': 12.5, Amethyst: 0 - }) + } }, { id: itemID('Mining gloves'), - Percentages: new Bank({ + Percentages: { 'Silver ore': 50, Coal: 40, 'Gold ore': 33.33, @@ -90,14 +88,14 @@ export const miningGloves = [ 'Adamantite ore': 0, 'Runite ore': 0, Amethyst: 0 - }) + } } ]; -export const varrockArmours = [ +export const varrockArmours: { id: number; Percentages: Record }[] = [ { id: itemID('Varrock armour 4'), - Percentages: new Bank({ + Percentages: { Clay: 10, 'Copper ore': 10, 'Tin ore': 10, @@ -111,11 +109,11 @@ export const varrockArmours = [ 'Adamantite ore': 10, 'Runite ore': 10, Amethyst: 10 - }) + } }, { id: itemID('Varrock armour 3'), - Percentages: new Bank({ + Percentages: { Clay: 10, 'Copper ore': 10, 'Tin ore': 10, @@ -129,11 +127,11 @@ export const varrockArmours = [ 'Adamantite ore': 10, 'Runite ore': 0, Amethyst: 0 - }) + } }, { id: itemID('Varrock armour 2'), - Percentages: new Bank({ + Percentages: { Clay: 10, 'Copper ore': 10, 'Tin ore': 10, @@ -147,11 +145,11 @@ export const varrockArmours = [ 'Adamantite ore': 0, 'Runite ore': 0, Amethyst: 0 - }) + } }, { id: itemID('Varrock armour 1'), - Percentages: new Bank({ + Percentages: { Clay: 10, 'Copper ore': 10, 'Tin ore': 10, @@ -165,11 +163,11 @@ export const varrockArmours = [ 'Adamantite ore': 0, 'Runite ore': 0, Amethyst: 0 - }) + } } ]; -export const miningCapeOreEffect: Bank = new Bank({ +export const miningCapeOreEffect: Record = { Clay: 5, 'Copper ore': 5, 'Tin ore': 5, @@ -183,4 +181,4 @@ export const miningCapeOreEffect: Bank = new Bank({ 'Adamantite ore': 5, 'Runite ore': 0, Amethyst: 0 -}); +}; diff --git a/src/lib/skilling/skills/herblore/mixables/barbMixes.ts b/src/lib/skilling/skills/herblore/mixables/barbMixes.ts index adb2cecbf04..a25777d7c50 100644 --- a/src/lib/skilling/skills/herblore/mixables/barbMixes.ts +++ b/src/lib/skilling/skills/herblore/mixables/barbMixes.ts @@ -33,10 +33,7 @@ export const barbMixes: Mixable[] = [ aliases: ['Relicyms mix roe', 'Relicyms mix(2)', 'Relicyms mix 2 roe'], level: 9, xp: 14, - inputItems: new Bank({ - 4846: 1, - Roe: 1 - }), + inputItems: new Bank().add("Relicym's balm(2)").add('Roe'), tickRate: 1, bankTimePerPotion: 0.088 }, diff --git a/src/mahoji/commands/create.ts b/src/mahoji/commands/create.ts index a33427a203c..30b97f4ce6f 100644 --- a/src/mahoji/commands/create.ts +++ b/src/mahoji/commands/create.ts @@ -60,7 +60,7 @@ export const createCommand: OSBMahojiCommand = { }: CommandRunOptions<{ item: string; quantity?: number; showall?: boolean }>) => { const user = await mUserFetch(userID.toString()); - const itemName = options.item.toLowerCase(); + const itemName = options.item?.toLowerCase(); let { quantity } = options; if (options.showall) { return allCreatablesTable; diff --git a/src/mahoji/commands/k.ts b/src/mahoji/commands/k.ts index fbfcba308fd..e161c6854a9 100644 --- a/src/mahoji/commands/k.ts +++ b/src/mahoji/commands/k.ts @@ -51,7 +51,7 @@ LIMIT 10;`, return new Set(res.map(i => Number(i.mon_id))); } -export const killCommand: OSBMahojiCommand = { +export const minionKCommand: OSBMahojiCommand = { name: 'k', description: 'Send your minion to kill things.', attributes: { diff --git a/src/mahoji/commands/leaderboard.ts b/src/mahoji/commands/leaderboard.ts index 94784c6f51e..75f0f7b5c32 100644 --- a/src/mahoji/commands/leaderboard.ts +++ b/src/mahoji/commands/leaderboard.ts @@ -370,7 +370,9 @@ async function openLb( name: string, ironmanOnly: boolean ) { - name = name.trim(); + if (name) { + name = name.trim(); + } let entityID = -1; let key = ''; diff --git a/src/mahoji/commands/mine.ts b/src/mahoji/commands/mine.ts index 85c4c9fdd57..1df0efa5bf3 100644 --- a/src/mahoji/commands/mine.ts +++ b/src/mahoji/commands/mine.ts @@ -124,9 +124,9 @@ export const mineCommand: OSBMahojiCommand = { let glovesRate = 0; if (user.skillsAsLevels.mining >= 60) { for (const glove of miningGloves) { - if (!user.hasEquipped(glove.id) || !glove.Percentages.has(ore.id)) continue; - glovesRate = glove.Percentages.amount(ore.id); - if (glovesRate !== 0) { + if (!user.hasEquipped(glove.id) || !glove.Percentages[ore.name]) continue; + glovesRate = glove.Percentages[ore.name]; + if (glovesRate) { boosts.push(`Lowered rock depletion rate by **${glovesRate}%** for ${itemNameFromID(glove.id)}`); break; } @@ -135,9 +135,9 @@ export const mineCommand: OSBMahojiCommand = { let armourEffect = 0; for (const armour of varrockArmours) { - if (!user.hasEquippedOrInBank(armour.id) || !armour.Percentages.has(ore.id)) continue; - armourEffect = armour.Percentages.amount(ore.id); - if (armourEffect !== 0) { + if (!user.hasEquippedOrInBank(armour.id) || !armour.Percentages[ore.name]) continue; + armourEffect = armour.Percentages[ore.name]; + if (armourEffect) { boosts.push(`**${armourEffect}%** chance to mine an extra ore using ${itemNameFromID(armour.id)}`); break; } @@ -150,9 +150,9 @@ export const mineCommand: OSBMahojiCommand = { } let miningCapeEffect = 0; - if (user.hasEquippedOrInBank([itemID('Mining cape')]) && miningCapeOreEffect.has(ore.id)) { - miningCapeEffect = miningCapeOreEffect.amount(ore.id); - if (miningCapeEffect !== 0) { + if (user.hasEquippedOrInBank([itemID('Mining cape')]) && miningCapeOreEffect[ore.name]) { + miningCapeEffect = miningCapeOreEffect[ore.name]; + if (miningCapeEffect) { boosts.push(`**${miningCapeEffect}%** chance to mine an extra ore using Mining cape`); } } diff --git a/src/mahoji/commands/minion.ts b/src/mahoji/commands/minion.ts index ba0f6d5f6b3..7fff3b4914e 100644 --- a/src/mahoji/commands/minion.ts +++ b/src/mahoji/commands/minion.ts @@ -29,7 +29,7 @@ import creatures from '../../lib/skilling/skills/hunter/creatures'; import { MUserStats } from '../../lib/structures/MUserStats'; import { convertLVLtoXP, getUsername, isValidNickname } from '../../lib/util'; import { getKCByName } from '../../lib/util/getKCByName'; -import getOSItem from '../../lib/util/getOSItem'; +import getOSItem, { getItem } from '../../lib/util/getOSItem'; import { handleMahojiConfirmation } from '../../lib/util/handleMahojiConfirmation'; import { minionStatsEmbed } from '../../lib/util/minionStatsEmbed'; import { checkPeakTimes } from '../../lib/util/minionUtils'; @@ -232,7 +232,8 @@ export const minionCommand: OSBMahojiCommand = { autocomplete: async (value, user) => { const mappedLampables = Lampables.map(i => i.items) .flat(2) - .map(getOSItem) + .map(getItem) + .filter(notEmpty) .map(i => ({ id: i.id, name: i.name })); const botUser = await mUserFetch(user.id); diff --git a/src/mahoji/commands/offer.ts b/src/mahoji/commands/offer.ts index 0f7e102eafe..ba80ba4eeed 100644 --- a/src/mahoji/commands/offer.ts +++ b/src/mahoji/commands/offer.ts @@ -54,7 +54,7 @@ function notifyUniques(user: MUser, activity: string, uniques: number[], loot: B } } -export const mineCommand: OSBMahojiCommand = { +export const offerCommand: OSBMahojiCommand = { name: 'offer', description: 'Offer bones or bird eggs.', attributes: { diff --git a/src/mahoji/commands/testpotato.ts b/src/mahoji/commands/testpotato.ts index 4104ffb8ab0..1be86fafb3e 100644 --- a/src/mahoji/commands/testpotato.ts +++ b/src/mahoji/commands/testpotato.ts @@ -54,6 +54,12 @@ export async function giveMaxStats(user: MUser) { } await user.update({ QP: MAX_QP, + slayer_points: 50_000, + nmz_points: 50_000, + volcanic_mine_points: 500_000, + carpenter_points: 5_000_000, + zeal_tokens: 500_000, + lms_points: 500_000, ...updates }); } diff --git a/src/mahoji/lib/abstracted_commands/barbAssault.ts b/src/mahoji/lib/abstracted_commands/barbAssault.ts index 68fcc2ce6b6..a3eed89c165 100644 --- a/src/mahoji/lib/abstracted_commands/barbAssault.ts +++ b/src/mahoji/lib/abstracted_commands/barbAssault.ts @@ -174,7 +174,7 @@ export async function barbAssaultGambleCommand( interaction: ChatInputCommandInteraction, user: MUser, tier: string, - quantity: number + quantity = 1 ) { const buyable = GambleTiers.find(i => stringMatches(tier, i.name)); if (!buyable) { diff --git a/tests/globalSetup.ts b/tests/globalSetup.ts index 72c831572d8..05250a33898 100644 --- a/tests/globalSetup.ts +++ b/tests/globalSetup.ts @@ -5,7 +5,15 @@ import { Collection } from 'discord.js'; import { vi } from 'vitest'; vi.mock('@oldschoolgg/toolkit', async () => { - const actualToolkit = await vi.importActual('@oldschoolgg/toolkit'); // Import all actual exports + const actual: any = await vi.importActual('@oldschoolgg/toolkit'); + return { + ...actual, + mentionCommand: async (_args: any) => 'hi' + }; +}); + +vi.mock('../node_modules/@oldschoolgg/toolkit/src/util/discord.ts', async () => { + const actualToolkit = await vi.importActual('../node_modules/@oldschoolgg/toolkit/src/util/discord.ts'); // Import all actual exports return { ...actualToolkit, // Include all actual exports in the mock mentionCommand: vi.fn().mockReturnValue('') // Mock mentionCommand to return a blank string @@ -18,21 +26,19 @@ global.globalClient = { guilds: { cache: new Collection() }, mahojiClient: { commands: { - values: [ - { - name: 'test', - description: 'test description', - attributes: { description: 'test description' }, - options: [] - } - ] + values: ['test'].map(n => ({ + name: n, + description: 'test description', + attributes: { description: 'test description' }, + options: [{ name: 'claim' }] + })) } }, users: { cache: new Collection() }, channels: { - cache: new Collection() + cache: new Collection().set('1', { id: '1' }) }, busyCounterCache: new Map() } as any; diff --git a/tests/integration/allCommandsBase.test.ts b/tests/integration/allCommandsBase.test.ts index f826b1810bc..3095093cbd5 100644 --- a/tests/integration/allCommandsBase.test.ts +++ b/tests/integration/allCommandsBase.test.ts @@ -1,102 +1,321 @@ -import { describe, test, vi } from 'vitest'; +import { join } from 'node:path'; +import { ApplicationCommandOptionType } from 'discord.js'; +import { randArrItem, randInt, shuffleArr, Time } from 'e'; +import { Store } from 'mahoji/dist/lib/structures/Store'; +import { CommandOption } from 'mahoji/dist/lib/types'; +import { isValidCommand } from 'mahoji/dist/lib/util'; +import { Bank, Items } from 'oldschooljs'; +import { expect, test, vi } from 'vitest'; + +import { BitField, minionActivityCache } from '../../src/lib/constants'; +import { prisma } from '../../src/lib/settings/prisma'; +import { mahojiClientSettingsFetch } from '../../src/lib/util/clientSettings'; +import { handleMahojiConfirmation } from '../../src/lib/util/handleMahojiConfirmation'; import { activitiesCommand } from '../../src/mahoji/commands/activities'; +import { adminCommand } from '../../src/mahoji/commands/admin'; import { askCommand } from '../../src/mahoji/commands/ask'; -import { bankCommand } from '../../src/mahoji/commands/bank'; +import { botLeaguesCommand } from '../../src/mahoji/commands/botleagues'; import { bsCommand } from '../../src/mahoji/commands/bs'; import { buildCommand } from '../../src/mahoji/commands/build'; import { buyCommand } from '../../src/mahoji/commands/buy'; +import { caCommand } from '../../src/mahoji/commands/ca'; import { chooseCommand } from '../../src/mahoji/commands/choose'; import { chopCommand } from '../../src/mahoji/commands/chop'; import { claimCommand } from '../../src/mahoji/commands/claim'; import { clueCommand } from '../../src/mahoji/commands/clue'; import { cluesCommand } from '../../src/mahoji/commands/clues'; +import { configCommand } from '../../src/mahoji/commands/config'; +import { cookCommand } from '../../src/mahoji/commands/cook'; +import { craftCommand } from '../../src/mahoji/commands/craft'; import { createCommand } from '../../src/mahoji/commands/create'; +import { dataCommand } from '../../src/mahoji/commands/data'; +import { dropCommand } from '../../src/mahoji/commands/drop'; import { dryCalcCommand } from '../../src/mahoji/commands/drycalc'; +import { fakeCommand } from '../../src/mahoji/commands/fake'; +import { fakepmCommand } from '../../src/mahoji/commands/fakepm'; import { farmingCommand } from '../../src/mahoji/commands/farming'; import { fishCommand } from '../../src/mahoji/commands/fish'; import { fletchCommand } from '../../src/mahoji/commands/fletch'; +import { gambleCommand } from '../../src/mahoji/commands/gamble'; +import { gearCommand } from '../../src/mahoji/commands/gear'; +import { gearPresetsCommand } from '../../src/mahoji/commands/gearpresets'; +import { giftCommand } from '../../src/mahoji/commands/gift'; +import { giveawayCommand } from '../../src/mahoji/commands/giveaway'; import { gpCommand } from '../../src/mahoji/commands/gp'; +import { helpCommand } from '../../src/mahoji/commands/help'; import { huntCommand } from '../../src/mahoji/commands/hunt'; +import { inviteCommand } from '../../src/mahoji/commands/invite'; +import { minionKCommand } from '../../src/mahoji/commands/k'; +import { kcCommand } from '../../src/mahoji/commands/kc'; import { lapsCommand } from '../../src/mahoji/commands/laps'; import { leaderboardCommand } from '../../src/mahoji/commands/leaderboard'; import { lightCommand } from '../../src/mahoji/commands/light'; import { lootCommand } from '../../src/mahoji/commands/loot'; +import { mCommand } from '../../src/mahoji/commands/m'; +import { massCommand } from '../../src/mahoji/commands/mass'; +import { mineCommand } from '../../src/mahoji/commands/mine'; import { minigamesCommand } from '../../src/mahoji/commands/minigames'; import { minionCommand } from '../../src/mahoji/commands/minion'; +import { mixCommand } from '../../src/mahoji/commands/mix'; +import { offerCommand } from '../../src/mahoji/commands/offer'; import { openCommand } from '../../src/mahoji/commands/open'; import { patreonCommand } from '../../src/mahoji/commands/patreon'; import { payCommand } from '../../src/mahoji/commands/pay'; import { pohCommand } from '../../src/mahoji/commands/poh'; +import { pollCommand } from '../../src/mahoji/commands/poll'; import { priceCommand } from '../../src/mahoji/commands/price'; import { raidCommand } from '../../src/mahoji/commands/raid'; +import { redeemCommand } from '../../src/mahoji/commands/redeem'; import { rollCommand } from '../../src/mahoji/commands/roll'; import { runecraftCommand } from '../../src/mahoji/commands/runecraft'; +import { sacrificeCommand } from '../../src/mahoji/commands/sacrifice'; +import { sellCommand } from '../../src/mahoji/commands/sell'; +import { simulateCommand } from '../../src/mahoji/commands/simulate'; import { slayerCommand } from '../../src/mahoji/commands/slayer'; import { smeltingCommand } from '../../src/mahoji/commands/smelt'; +import { smithCommand } from '../../src/mahoji/commands/smith'; import { stealCommand } from '../../src/mahoji/commands/steal'; +import { tksCommand } from '../../src/mahoji/commands/tokkulshop'; import { toolsCommand } from '../../src/mahoji/commands/tools'; -import { OSBMahojiCommand } from '../../src/mahoji/lib/util'; +import { tradeCommand } from '../../src/mahoji/commands/trade'; +import { triviaCommand } from '../../src/mahoji/commands/trivia'; +import { mahojiUseCommand } from '../../src/mahoji/commands/use'; import { randomMock } from './setup'; -import { createTestUser } from './util'; +import { createTestUser, mockClient, TestUser } from './util'; + +type CommandInput = Record; +async function generateCommandInputs(user: TestUser, options: readonly CommandOption[]): Promise { + let results: CommandInput[] = []; + const allPossibleOptions: Record = {}; -const commands: [OSBMahojiCommand, null | object][] = [ - [activitiesCommand, null], - [askCommand, null], - [bankCommand, null], - [bsCommand, null], - [clueCommand, null], - [claimCommand, null], - [cluesCommand, null], - [farmingCommand, null], - [gpCommand, null], - [lapsCommand, null], - [leaderboardCommand, null], - [fletchCommand, null], - [fishCommand, null], - [dryCalcCommand, null], - [createCommand, { item: 'asdf' }], - [chopCommand, null], - [chooseCommand, { list: 'a,a,a' }], - [buildCommand, null], - [buyCommand, null], - [huntCommand, null], - [lightCommand, null], - [lootCommand, null], - [minionCommand, null], - [minigamesCommand, null], - [runecraftCommand, { rune: 'blood rune' }], - [stealCommand, null], - [rollCommand, null], - [raidCommand, null], - [priceCommand, null], - [openCommand, null], - [patreonCommand, null], - [payCommand, { user: { user: { id: '2' } } }], - [pohCommand, null], - [slayerCommand, null], - [toolsCommand, null], - [stealCommand, null], - [smeltingCommand, null] -]; + for (const option of options) { + switch (option.type) { + case ApplicationCommandOptionType.SubcommandGroup: + case ApplicationCommandOptionType.Subcommand: + if (option.options) { + const subOptionsResults = await generateCommandInputs(user, option.options); + results.push(...subOptionsResults.map(input => ({ [option.name]: input }))); + } + break; + case ApplicationCommandOptionType.String: + if ('autocomplete' in option && option.autocomplete) { + const autoCompleteResults = await option.autocomplete('', { id: user.id } as any, {} as any); + allPossibleOptions[option.name] = shuffleArr(autoCompleteResults.map(c => c.value)).slice(0, 3); + } else if (option.choices) { + allPossibleOptions[option.name] = option.choices.map(c => c.value).slice(0, 3); + } else if (['guild_id', 'message_id'].includes(option.name)) { + allPossibleOptions[option.name] = ['157797566833098752']; + } else { + allPossibleOptions[option.name] = ['plain string']; + } + break; + case ApplicationCommandOptionType.Integer: + case ApplicationCommandOptionType.Number: + if (option.choices) { + allPossibleOptions[option.name] = option.choices.map(c => c.value); + } else { + let value = randInt(1, 10); + if (option.min_value && option.max_value) { + value = randInt(option.min_value, option.max_value); + } + allPossibleOptions[option.name] = [option.min_value, value]; + } + break; + case ApplicationCommandOptionType.Boolean: { + allPossibleOptions[option.name] = [true, false]; + break; + } + case ApplicationCommandOptionType.User: { + allPossibleOptions[option.name] = [ + { + user: { + id: '425134194436341760', + username: 'username', + bot: false + }, + member: undefined + } + ]; + break; + } + case ApplicationCommandOptionType.Channel: + case ApplicationCommandOptionType.Role: + case ApplicationCommandOptionType.Mentionable: + // results.push({ ...currentPath, [option.name]: `Any ${option.type}` }); + break; + } + } -// Don't let any of these commands create an activity -vi.mock('../../src/lib/util/addSubTaskToActivityTask', async () => { - const actual: any = await vi.importActual('../../src/lib/util/addSubTaskToActivityTask'); - return { - ...actual, - default: async (args: any) => { - console.log(`Sending ${args}`); + const sorted = Object.values(allPossibleOptions).sort((a, b) => b.length - a.length); + const longestOptions = sorted[0]?.length; + for (let i = 0; i < longestOptions; i++) { + let obj: Record = {}; + for (const [key, val] of Object.entries(allPossibleOptions)) { + obj[key] = val[i] ?? randArrItem(val); } - }; -}); + results.push(obj); + } + return results; +} + +const bank = new Bank(); +for (const item of Items.array()) { + bank.add(item.id, 100_000_000); +} -describe('All Commands Base Test', async () => { - randomMock(); - const user = await createTestUser(); - for (const [command, options] of commands) { - test(`Run ${command.name} command`, async () => { - await user.runCommand(command, options ?? {}); +test( + 'All Commands Base Test', + async () => { + expect(vi.isMockFunction(handleMahojiConfirmation)).toBe(true); + const client = await mockClient(); + process.env.CLIENT_ID = client.data.id; + randomMock(); + const maxUser = await createTestUser(bank, { GP: 100_000_000_000 }); + await maxUser.max(); + await maxUser.update({ bitfield: [BitField.isModerator] }); + const store = new Store({ name: 'commands', dirs: [join('dist', 'mahoji')], checker: isValidCommand }); + await store.load(); + const currentClientSettings = await mahojiClientSettingsFetch({ construction_cost_bank: true }); + await prisma.activity.deleteMany({ + where: { + user_id: BigInt(maxUser.id) + } }); + + const ignoredCommands = [ + 'leagues', + 'bank', + 'bingo', + 'bossrecords', + 'stats', + 'clues', + 'kc', + 'simulate', + 'lvl', + 'testpotato', + 'xp', + 'wiki', + 'casket', + 'finish', + 'kill', + 'trivia', + 'ge', + 'rp', + 'cl' + ]; + const cmds = [ + adminCommand, + askCommand, + botLeaguesCommand, + bsCommand, + buildCommand, + buyCommand, + caCommand, + chooseCommand, + chopCommand, + cookCommand, + clueCommand, + configCommand, + claimCommand, + cluesCommand, + mCommand, + gpCommand, + payCommand, + craftCommand, + fishCommand, + farmingCommand, + dropCommand, + dryCalcCommand, + createCommand, + activitiesCommand, + dataCommand, + fakeCommand, + fakepmCommand, + fletchCommand, + gambleCommand, + gearCommand, + gearPresetsCommand, + giveawayCommand, + helpCommand, + huntCommand, + giftCommand, + inviteCommand, + kcCommand, + minionKCommand, + lapsCommand, + leaderboardCommand, + lightCommand, + mineCommand, + massCommand, + minigamesCommand, + minionCommand, + simulateCommand, + sellCommand, + sacrificeCommand, + rollCommand, + runecraftCommand, + raidCommand, + pollCommand, + pohCommand, + priceCommand, + openCommand, + offerCommand, + mixCommand, + lootCommand, + smeltingCommand, + slayerCommand, + redeemCommand, + patreonCommand, + smithCommand, + stealCommand, + tradeCommand, + triviaCommand, + toolsCommand, + tksCommand, + mahojiUseCommand + ]; + for (const command of store.values) { + if (ignoredCommands.includes(command.name)) continue; + if (cmds.some(c => c.name === command.name)) continue; + throw new Error( + `If you added a new command (${command.name}), you need to put it in the allCommandsBase.test.ts file.` + ); + } + + const ignoredSubCommands = [ + ['tools', 'patron', 'cl_bank'], + ['loot', 'view'], + ['minion', 'bankbg'] + ]; + + for (const command of cmds) { + if (ignoredCommands.includes(command.name)) continue; + const options = await generateCommandInputs(maxUser, command.options!); + outer: for (const option of options) { + for (const [parent, sub, subCommand] of ignoredSubCommands) { + if (command.name === parent && option[sub] && (subCommand ? option[sub][subCommand] : true)) { + continue outer; + } + } + try { + const res = await maxUser.runCommand(command, option); + minionActivityCache.clear(); + // console.log(`Running command ${command.name} + // Options: ${JSON.stringify(option)} + // Result: ${JSON.stringify(res).slice(0, 100)}`); + } catch (err) { + console.error( + `Failed to run command ${command.name} with options ${JSON.stringify(option)}: ${err}` + ); + throw err; + } + } + } + + await client.processActivities(); + }, + { + timeout: Time.Minute * 10 } -}); +); diff --git a/tests/integration/grandExchange.test.ts b/tests/integration/grandExchange.test.ts index 5f09a583c53..243f4a74496 100644 --- a/tests/integration/grandExchange.test.ts +++ b/tests/integration/grandExchange.test.ts @@ -69,12 +69,12 @@ describe('Grand Exchange', async () => { console.log(`Finished initializing ${amountOfUsers} users`); // Run a bunch of commands to buy/sell - const commandPromises = new PQueue({ concurrency: 10 }); + const commandPromises = new PQueue({ concurrency: 20 }); for (const user of shuffleArr(users)) { - const method = randArrItem(['buy', 'sell']); - let quantity = randArrItem(quantities); - let price = randArrItem(prices); commandPromises.add(async () => { + const method = randArrItem(['buy', 'sell']); + let quantity = randArrItem(quantities); + let price = randArrItem(prices); for (const item of itemPool) { await user.runCommand(geCommand, { [method]: { @@ -94,10 +94,7 @@ describe('Grand Exchange', async () => { for (let i = 0; i < 100; i++) { await GrandExchange.tick(); await waitForGEToBeEmpty(); - await Promise.all([ - GrandExchange.checkGECanFullFilAllListings(), - GrandExchange.extensiveVerification() - ]); + await GrandExchange.extensiveVerification(); } await waitForGEToBeEmpty(); console.log('Finished ticking 100 times'); @@ -154,7 +151,6 @@ Based on G.E data, we should have received ${data.totalTax} tax`; assert(GrandExchange.queue.size === 0, 'Queue should be empty'); }, { - repeats: 1, timeout: Time.Minute * 5 } ); diff --git a/tests/integration/migrateUser.test.ts b/tests/integration/migrateUser.test.ts index 8b9316b57e0..dc56788b15b 100644 --- a/tests/integration/migrateUser.test.ts +++ b/tests/integration/migrateUser.test.ts @@ -28,7 +28,6 @@ import { describe, expect, test, vi } from 'vitest'; import { BitField } from '../../src/lib/constants'; import { GearSetupType, UserFullGearSetup } from '../../src/lib/gear/types'; -import { GrandExchange } from '../../src/lib/grandExchange'; import { trackLoot } from '../../src/lib/lootTrack'; import { incrementMinigameScore, MinigameName } from '../../src/lib/settings/minigames'; import { SkillsEnum } from '../../src/lib/skilling/types'; @@ -1222,9 +1221,6 @@ describe('migrate user test', async () => { } }; - await GrandExchange.totalReset(); - await GrandExchange.init(); - test('test preventing a double (clobber) robochimp migration (two bot-migration)', async () => { const sourceUserId = mockedId(); const destUserId = mockedId(); diff --git a/tests/integration/mocks.ts b/tests/integration/mocks.ts new file mode 100644 index 00000000000..6508511fc7d --- /dev/null +++ b/tests/integration/mocks.ts @@ -0,0 +1,41 @@ +import { Image } from '@napi-rs/canvas'; +import { beforeEach, vi } from 'vitest'; + +import { BankImageTask } from '../../src/lib/bankImage'; + +vi.mock('../../src/lib/util/handleMahojiConfirmation.ts', () => ({ + handleMahojiConfirmation: vi.fn() +})); +vi.mock('../../src/lib/util/interactionReply', () => ({ + deferInteraction: vi.fn(), + interactionReply: vi.fn() +})); + +vi.mock('../../src/lib/util/interactionReply', () => ({ + deferInteraction: vi.fn(), + interactionReply: vi.fn() +})); + +const mockBankImageTask = { + init: vi.fn(), + run: vi.fn(), + generateBankImage: vi.fn().mockReturnValue(Promise.resolve({ image: Buffer.from(''), isTransparent: false })), + getItemImage: vi.fn().mockReturnValue(Promise.resolve(new Image())), + fetchAndCacheImage: vi.fn().mockReturnValue(Promise.resolve(new Image())) +}; + +global.bankImageGenerator = mockBankImageTask as any; +BankImageTask.prototype.init = mockBankImageTask.init; +BankImageTask.prototype.run = mockBankImageTask.init; +BankImageTask.prototype.generateBankImage = mockBankImageTask.generateBankImage; +BankImageTask.prototype.getItemImage = mockBankImageTask.getItemImage; +BankImageTask.prototype.fetchAndCacheImage = mockBankImageTask.fetchAndCacheImage; + +beforeEach(async () => { + global.bankImageGenerator = mockBankImageTask as any; + BankImageTask.prototype.init = mockBankImageTask.init; + BankImageTask.prototype.run = mockBankImageTask.init; + BankImageTask.prototype.generateBankImage = mockBankImageTask.generateBankImage; + BankImageTask.prototype.getItemImage = mockBankImageTask.getItemImage; + BankImageTask.prototype.fetchAndCacheImage = mockBankImageTask.fetchAndCacheImage; +}); diff --git a/tests/integration/monsterKilling.test.ts b/tests/integration/monsterKilling.test.ts index 41e2d8d9293..fafe856aafd 100644 --- a/tests/integration/monsterKilling.test.ts +++ b/tests/integration/monsterKilling.test.ts @@ -1,21 +1,20 @@ import { Bank } from 'oldschooljs'; import { expect, test } from 'vitest'; -import { MonsterActivityTaskOptions } from '../../src/lib/types/minions'; -import { killCommand } from '../../src/mahoji/commands/k'; +import { minionKCommand } from '../../src/mahoji/commands/k'; import { createTestUser, mockClient } from './util'; test('Killing Men', async () => { - await mockClient(); + const client = await mockClient(); const user = await createTestUser(); const startingBank = new Bank().add('Shark', 1_000_000); await user.addItemsToBank({ items: startingBank }); await user.max(); - await user.runCommand(killCommand, { + await user.runCommand(minionKCommand, { name: 'general graardor' }); - (await user.runActivity()) as MonsterActivityTaskOptions; + await client.processActivities(); await user.sync(); expect(user.bank.amount('Shark')).toBeLessThan(1_000_000); diff --git a/tests/integration/setup.ts b/tests/integration/setup.ts index 8cb634c0639..982263ac04c 100644 --- a/tests/integration/setup.ts +++ b/tests/integration/setup.ts @@ -1,16 +1,14 @@ +import './mocks'; import '../globalSetup'; +import { Image } from '@napi-rs/canvas'; +import { noOp } from 'e'; +import mitm from 'mitm'; import { afterEach, beforeEach, vi } from 'vitest'; +import { BankImageTask, bankImageTask } from '../../src/lib/bankImage'; import { prisma } from '../../src/lib/settings/prisma'; -vi.mock('../../src/lib/util/handleMahojiConfirmation', () => ({ - handleMahojiConfirmation: vi.fn() -})); -vi.mock('../../src/lib/util/interactionReply', () => ({ - deferInteraction: vi.fn() -})); - export function randomMock(random = 0.1) { Math.random = () => random; } @@ -19,7 +17,15 @@ vi.mock('../../src/lib/util/webhook', async () => { const actual: any = await vi.importActual('../../src/lib/util/webhook'); return { ...actual, - sendToChannelID: async (_args: any) => {} + sendToChannelID: vi.fn() + }; +}); + +vi.mock('../../src/lib/gear/functions/generateGearImage', async () => { + const actual: any = await vi.importActual('../../src/lib/gear/functions/generateGearImage'); + return { + ...actual, + generateGearImage: vi.fn().mockReturnValue(Promise.resolve(Buffer.from(''))) }; }); @@ -29,8 +35,30 @@ globalClient.fetchUser = async (id: string | bigint) => ({ send: async () => {} }); +const mockBankImageTask = { + init: vi.fn(), + run: vi.fn(), + generateBankImage: vi.fn().mockReturnValue(Promise.resolve({ image: Buffer.from(''), isTransparent: false })), + getItemImage: vi.fn().mockReturnValue(Promise.resolve(new Image())), + fetchAndCacheImage: vi.fn().mockReturnValue(Promise.resolve(new Image())), + backgroundImages: [] +}; +bankImageTask.fetchAndCacheImage = mockBankImageTask.fetchAndCacheImage; +global.bankImageGenerator = mockBankImageTask as any; +BankImageTask.prototype.init = mockBankImageTask.init; +BankImageTask.prototype.run = mockBankImageTask.init; +BankImageTask.prototype.generateBankImage = mockBankImageTask.generateBankImage; +BankImageTask.prototype.getItemImage = mockBankImageTask.getItemImage; +BankImageTask.prototype.fetchAndCacheImage = mockBankImageTask.fetchAndCacheImage; + beforeEach(async () => { await prisma.$connect(); + global.bankImageGenerator = mockBankImageTask as any; + BankImageTask.prototype.init = mockBankImageTask.init; + BankImageTask.prototype.run = mockBankImageTask.init; + BankImageTask.prototype.generateBankImage = mockBankImageTask.generateBankImage; + BankImageTask.prototype.getItemImage = mockBankImageTask.getItemImage; + BankImageTask.prototype.fetchAndCacheImage = mockBankImageTask.fetchAndCacheImage; }); afterEach(async () => { @@ -38,7 +66,20 @@ afterEach(async () => { }); async function init() { - await prisma.$queryRaw`CREATE EXTENSION IF NOT EXISTS intarray;`; + await prisma.$queryRaw`CREATE EXTENSION IF NOT EXISTS intarray;`.catch(noOp); } init(); + +function setupRequestLogging() { + const mitmInstance = mitm(); + + mitmInstance.on('connect', (socket, opts) => { + if (opts?.host) { + // throw new Error(`Sending request to ${opts.host}`); + socket.bypass(); + } + }); +} + +setupRequestLogging(); diff --git a/tests/integration/trading.test.ts b/tests/integration/trading.test.ts index 25d3194ad1d..31b9d56d2b6 100644 --- a/tests/integration/trading.test.ts +++ b/tests/integration/trading.test.ts @@ -9,7 +9,7 @@ test('Trade consistency', async () => { await mockClient(); const bank = new Bank().add('Coins', 1000).add('Egg', 1000).add('Coal', 1000).add('Trout', 1000).freeze(); - const NUMBER_OF_USERS = 100; + const NUMBER_OF_USERS = 50; const users: TestUser[] = []; for (let i = 0; i < NUMBER_OF_USERS; i++) { @@ -27,9 +27,9 @@ test('Trade consistency', async () => { checkMatch(); - for (let i = 0; i < 3; i++) { - const promises = []; + const promises = []; + for (let i = 0; i < 3; i++) { for (const user of shuffleArr(users)) { const other = randArrItem(users); const method = randArrItem(['send', 'receive', 'both']); diff --git a/tests/integration/util.ts b/tests/integration/util.ts index f090e79f0ca..d28df44a5fb 100644 --- a/tests/integration/util.ts +++ b/tests/integration/util.ts @@ -5,7 +5,6 @@ import { Bank } from 'oldschooljs'; import { globalConfig } from '../../src/lib/constants'; import { MUserClass } from '../../src/lib/MUser'; -import { convertStoredActivityToFlatActivity } from '../../src/lib/settings/prisma'; import { processPendingActivities } from '../../src/lib/Task'; import { ItemBank } from '../../src/lib/types'; import { cryptoRand } from '../../src/lib/util'; @@ -21,6 +20,7 @@ export const commandRunOptions = (userID: string): Omit Promise.resolve(), editReply: () => Promise.resolve(), followUp: () => Promise.resolve() @@ -101,18 +101,6 @@ export class TestUser extends MUserClass { return this; } - async runActivity() { - const [finishedActivity] = await processPendingActivities(); - if (!finishedActivity) { - throw new Error('runActivity: No activity was ran'); - } - if (finishedActivity.user_id.toString() !== this.id) { - throw new Error('runActivity: Ran activity, but it didnt belong to this user'); - } - const data = convertStoredActivityToFlatActivity(finishedActivity); - return data; - } - randomBankSubset() { const bank = new Bank(); const items = shuffleArr(this.bankWithGP.items()).slice(0, randInt(0, this.bankWithGP.length)); @@ -126,7 +114,7 @@ export class TestUser extends MUserClass { const idsUsed = new Set(); export function mockedId() { - return cryptoRand(1_000_000_000, 5_000_000_000_000).toString(); + return cryptoRand(1_000_000_000_000, 5_000_000_000_000).toString(); } export async function createTestUser(bank?: Bank, userData: Partial = {}) { @@ -185,6 +173,10 @@ class TestClient { throw new Error(`Expected ${key} to be ${value} but got ${this.data[key]}`); } } + + async processActivities() { + await processPendingActivities(); + } } export async function mockClient() { diff --git a/tests/unit/utils.ts b/tests/unit/utils.ts index 02ec2008e9c..26c1522ce84 100644 --- a/tests/unit/utils.ts +++ b/tests/unit/utils.ts @@ -35,7 +35,7 @@ interface MockUserArgs { export const mockUser = (overrides?: MockUserArgs): User => { const gearMelee = filterGearSetup(overrides?.meleeGear); const cl = new Bank().add(overrides?.cl ?? {}); - return { + const r = { cl, gear_fashion: new Gear().raw() as Prisma.JsonValue, gear_mage: new Gear().raw() as Prisma.JsonValue, @@ -70,7 +70,7 @@ export const mockUser = (overrides?: MockUserArgs): User => { skills_defence: overrides?.skills_defence ?? 0, skills_slayer: 0, skills_hitpoints: overrides?.skills_hitpoints ?? convertLVLtoXP(10), - GP: overrides?.GP, + GP: overrides?.GP ?? 0, premium_balance_tier: overrides?.premium_balance_tier, premium_balance_expiry_date: overrides?.premium_balance_expiry_date, ironman_alts: [], @@ -81,6 +81,8 @@ export const mockUser = (overrides?: MockUserArgs): User => { id: overrides?.id ?? '', monsterScores: {} } as unknown as User; + + return r; }; class TestMUser extends MUserClass { @@ -118,6 +120,9 @@ export async function testRunCmd({ Math.random = () => 0.5; const hash = murmurhash(JSON.stringify({ name: cmd.name, opts, user })).toString(); const mockedUser = mockMUser({ id: hash, ...user }); + if (mockedUser.GP === null || Number.isNaN(mockedUser.GP) || mockedUser.GP < 0 || mockedUser.GP === undefined) { + throw new Error(`Invalid GP for user ${hash}`); + } mockUserMap.set(hash, mockedUser); const options: any = { user: mockedUser.user, diff --git a/vitest.integration.config.mts b/vitest.integration.config.mts index 6b77ff01405..1a349326db2 100644 --- a/vitest.integration.config.mts +++ b/vitest.integration.config.mts @@ -12,8 +12,8 @@ export default defineConfig({ }, testTimeout: 30_000, bail: 1, - maxConcurrency: 1, - maxWorkers: 1, + maxConcurrency: 5, + maxWorkers: 5, minWorkers: 1, pool: 'forks' } diff --git a/yarn.lock b/yarn.lock index 7cacd7fc0e7..27e4ed785ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -978,6 +978,13 @@ dependencies: "@types/node" "*" +"@types/mitm@^1.3.8": + version "1.3.8" + resolved "https://registry.yarnpkg.com/@types/mitm/-/mitm-1.3.8.tgz#0acc7b82c1afd223828d445b1b101f86d6680ae8" + integrity sha512-vIXvHF4F9vJZOi9r/nOyaWFW0Y2MLcoaokN/GrZ3LYE0c2g7Y2kKpJ36hKT3EeJwzDsYZKjYyUEiCqDzSG/h9g== + dependencies: + "@types/node" "*" + "@types/node-cron@^3.0.7": version "3.0.7" resolved "https://registry.yarnpkg.com/@types/node-cron/-/node-cron-3.0.7.tgz#978bf75f7247385c61d23b6a060ba9eedb03e2f4" @@ -3806,6 +3813,14 @@ minipass@^7.0.4: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.0.tgz#b545f84af94e567386770159302ca113469c80b8" integrity sha512-oGZRv2OT1lO2UF1zUcwdTb3wqUwI0kBGTgt/T7OdSj6M6N5m3o5uPf0AIW6lVxGGoiWUR7e2AwTE+xiwK8WQig== +mitm@^1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/mitm/-/mitm-1.7.2.tgz#d079c44c763a333b15a0f7bfd02446fb8dbbe8e8" + integrity sha512-SuiJbc5xisP/iUYvsKAvrvPeoyJQbYI3WOfnp8A7XHDn4wkdtmGZe2ZTFXIo3K1of05oxUiaJIK+GoAU5KgFOw== + dependencies: + semver ">= 5 < 6" + underscore ">= 1.1.6 < 1.14" + mlly@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.2.1.tgz#cd50151f5712b651c5c379085157bcdff661133b" @@ -4716,7 +4731,7 @@ secure-json-parse@^2.4.0, secure-json-parse@^2.5.0: resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== -"semver@2 || 3 || 4 || 5": +"semver@2 || 3 || 4 || 5", "semver@>= 5 < 6": version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== @@ -4929,7 +4944,16 @@ stream-to-array@^2.3.0: dependencies: any-promise "^1.1.0" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -4981,7 +5005,14 @@ stringify-object@^3.2.1: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -5277,6 +5308,11 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +"underscore@>= 1.1.6 < 1.14": + version "1.13.6" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441" + integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A== + undici@5.27.2: version "5.27.2" resolved "https://registry.yarnpkg.com/undici/-/undici-5.27.2.tgz#a270c563aea5b46cc0df2550523638c95c5d4411" @@ -5470,7 +5506,16 @@ word-wrap@^1.2.3, word-wrap@^1.2.5, word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== From 832ecfc4d28968158b2fc980d18798d4fbc3e1d6 Mon Sep 17 00:00:00 2001 From: TastyPumPum <79149170+TastyPumPum@users.noreply.github.com> Date: Sun, 12 May 2024 14:07:43 +0100 Subject: [PATCH 003/249] Filter out completed quests (#5873) When doing activities quest and picking a quest, currently it just shows all the quests and it's hard to keep track of what's completed and what isn't. Following the #bso-vote poll, this hides the quests you have completed from the list and only shows ones you haven't completed. Then when every quest is completed it shows (completed) at the end instead of providing an empty list. --- src/mahoji/commands/activities.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/mahoji/commands/activities.ts b/src/mahoji/commands/activities.ts index 7c883469cd3..2d325c0aa95 100644 --- a/src/mahoji/commands/activities.ts +++ b/src/mahoji/commands/activities.ts @@ -1,3 +1,4 @@ +import { User } from 'discord.js'; import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; import { @@ -173,7 +174,15 @@ export const activitiesCommand: OSBMahojiCommand = { type: ApplicationCommandOptionType.String, name: 'name', description: 'The name of the quest (optional).', - choices: quests.map(i => ({ name: i.name, value: i.name })), + autocomplete: async (_value: string, user: User) => { + const mUser = await mUserFetch(user.id); + let list = quests + .filter(i => !mUser.user.finished_quest_ids.includes(i.id)) + .map(i => ({ name: i.name, value: i.name })); + if (list.length === 0) + list = quests.map(i => ({ name: `${i.name} (completed)`, value: i.name })); + return list; + }, required: false } ] From 8329ced5228c638cbdfb10530fcede665a1d556f Mon Sep 17 00:00:00 2001 From: TastyPumPum <79149170+TastyPumPum@users.noreply.github.com> Date: Sun, 12 May 2024 15:02:59 +0100 Subject: [PATCH 004/249] Wildy slayer (#5426) --- prisma/schema.prisma | 9 +- src/lib/MUser.ts | 30 +- src/lib/data/CollectionsExport.ts | 6 +- src/lib/data/buyables/buyables.ts | 9 + src/lib/data/creatablesTable.txt | 1 + src/lib/data/createables.ts | 6 + .../data/killableMonsters/bosses/wildy.ts | 70 +++- .../data/killableMonsters/chaeldarMonsters.ts | 23 +- .../killableMonsters/krystiliaMonsters.ts | 68 +++- .../data/killableMonsters/mazchnaMonsters.ts | 18 +- .../data/killableMonsters/nieveMonsters.ts | 8 +- src/lib/minions/data/killableMonsters/revs.ts | 2 +- .../data/killableMonsters/turaelMonsters.ts | 37 +- .../data/killableMonsters/vannakaMonsters.ts | 102 ++++-- src/lib/minions/types.ts | 2 + src/lib/openables.ts | 9 + src/lib/slayer/constants.ts | 3 +- src/lib/slayer/slayerMasters.ts | 9 + src/lib/slayer/slayerUnlocks.ts | 20 ++ src/lib/slayer/slayerUtil.ts | 41 ++- src/lib/slayer/tasks/bossTasks.ts | 80 ++++- src/lib/slayer/tasks/krystiliaTasks.ts | 333 ++++++++++++++++++ src/lib/slayer/types.ts | 1 + src/lib/types/minions.ts | 1 + src/lib/util/repeatStoredTrip.ts | 3 +- src/mahoji/commands/k.ts | 8 + src/mahoji/commands/kill.ts | 2 +- .../abstracted_commands/autoSlayCommand.ts | 14 +- .../lib/abstracted_commands/minionKill.ts | 161 +++++++-- .../abstracted_commands/slayerTaskCommand.ts | 23 +- src/mahoji/mahojiSettings.ts | 4 +- src/tasks/minions/monsterActivity.ts | 76 +++- src/tasks/minions/pickpocketActivity.ts | 2 +- .../unit/snapshots/banksnapshots.test.ts.snap | 32 ++ tests/unit/snapshots/clsnapshots.test.ts.snap | 7 +- ...s-test-ts-images-collection-log-1-snap.png | Bin 48541 -> 48541 bytes 36 files changed, 1049 insertions(+), 171 deletions(-) create mode 100644 src/lib/slayer/tasks/krystiliaTasks.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 132bbdc658e..ed04391c575 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -737,10 +737,11 @@ model UserStats { high_gambles Int @default(0) honour_points Int @default(0) - slayer_task_streak Int @default(0) - slayer_superior_count Int @default(0) - slayer_unsired_offered Int @default(0) - slayer_chewed_offered Int @default(0) + slayer_task_streak Int @default(0) + slayer_wildy_task_streak Int @default(0) + slayer_superior_count Int @default(0) + slayer_unsired_offered Int @default(0) + slayer_chewed_offered Int @default(0) tob_cost Json @default("{}") tob_loot Json @default("{}") diff --git a/src/lib/MUser.ts b/src/lib/MUser.ts index a29a34f701f..0ece1e2048b 100644 --- a/src/lib/MUser.ts +++ b/src/lib/MUser.ts @@ -736,20 +736,28 @@ GROUP BY data->>'clueID';`); return this.caPoints() >= CombatAchievements[tier].rewardThreshold; } - buildCATertiaryItemChanges() { + buildTertiaryItemChanges(hasRingOfWealthI: boolean = false, inWildy: boolean = false, onTask: boolean = false) { const changes = new Map(); - if (this.hasCompletedCATier('easy')) { - changes.set('Clue scroll (easy)', 5); - } - if (this.hasCompletedCATier('medium')) { - changes.set('Clue scroll (medium)', 5); - } - if (this.hasCompletedCATier('hard')) { - changes.set('Clue scroll (hard)', 5); + + const tiers = Object.keys(CombatAchievements) as Array; + for (const tier of tiers) { + let change = hasRingOfWealthI ? 50 : 0; + if (this.hasCompletedCATier(tier)) { + change += 5; + } + changes.set(`Clue scroll (${tier})`, change); } - if (this.hasCompletedCATier('elite')) { - changes.set('Clue scroll (elite)', 5); + + if (inWildy) changes.set('Giant key', 50); + + if (inWildy && !onTask) { + changes.set('Mossy key', 60); + } else if (!inWildy && onTask) { + changes.set('Mossy key', 66.67); + } else if (inWildy && onTask) { + changes.set('Mossy key', 77.6); } + return changes; } diff --git a/src/lib/data/CollectionsExport.ts b/src/lib/data/CollectionsExport.ts index b1c43dba48e..2ac2ab37ee7 100644 --- a/src/lib/data/CollectionsExport.ts +++ b/src/lib/data/CollectionsExport.ts @@ -1845,9 +1845,9 @@ export const slayerCL = resolveItems([ 'Mystic gloves (dusk)', 'Mystic boots (dusk)', 'Basilisk jaw', - // "Dagon'hai hat", - // "Dagon'hai robe top", - // "Dagon'hai robe bottom", + "Dagon'hai hat", + "Dagon'hai robe top", + "Dagon'hai robe bottom", 'Blood shard', 'Ancient ceremonial mask', 'Ancient ceremonial top', diff --git a/src/lib/data/buyables/buyables.ts b/src/lib/data/buyables/buyables.ts index 188481b2387..6dfc45a132f 100644 --- a/src/lib/data/buyables/buyables.ts +++ b/src/lib/data/buyables/buyables.ts @@ -1069,6 +1069,15 @@ const Buyables: Buyable[] = [ return toaKCs.expertKC >= 25 ? [true] : [false, 'You need a 25 Expert KC in Tombs of Amascut to buy this.']; } }, + { + name: 'Lockpick', + gpCost: 5000, + ironmanPrice: 500, + skillsNeeded: { + agility: 50, + thieving: 50 + } + }, ...sepulchreBuyables, ...constructionBuyables, ...hunterBuyables, diff --git a/src/lib/data/creatablesTable.txt b/src/lib/data/creatablesTable.txt index 8dce811b7eb..508d3468788 100644 --- a/src/lib/data/creatablesTable.txt +++ b/src/lib/data/creatablesTable.txt @@ -78,6 +78,7 @@ | Fish sack barrel | 1x Fish sack, 1x Fish barrel | 1x Fish sack barrel | 0 | | Salve amulet (e) | 1x Salve amulet | 1x Salve amulet (e) | 0 | | Salve amulet(ei) | 1x Salve amulet(i) | 1x Salve amulet(ei) | 0 | +| Ring of wealth (i) | 1x Ring of wealth, 1x Ring of wealth scroll | 1x Ring of wealth (i) | 50000 | | Strange hallowed tome | 1x Mysterious page 1, 1x Mysterious page 2, 1x Mysterious page 3, 1x Mysterious page 4, 1x Mysterious page 5 | 1x Strange hallowed tome | 0 | | Frozen key | 1x Frozen key piece (armadyl), 1x Frozen key piece (bandos), 1x Frozen key piece (zamorak), 1x Frozen key piece (saradomin) | 1x Frozen key | 0 | | Ecumenical key | 50x Ecumenical key shard | 1x Ecumenical key | 0 | diff --git a/src/lib/data/createables.ts b/src/lib/data/createables.ts index e08fd1a84ff..04376d0c8e8 100644 --- a/src/lib/data/createables.ts +++ b/src/lib/data/createables.ts @@ -2213,6 +2213,12 @@ const Createables: Createable[] = [ }, customReq: salveECustomReq }, + { + name: 'Ring of wealth (i)', + inputItems: new Bank().add('Ring of wealth').add('Ring of wealth scroll'), + GPCost: 50_000, + outputItems: new Bank().add('Ring of wealth (i)') + }, { name: 'Strange hallowed tome', inputItems: new Bank({ diff --git a/src/lib/minions/data/killableMonsters/bosses/wildy.ts b/src/lib/minions/data/killableMonsters/bosses/wildy.ts index 3ab549fed96..c414511a445 100644 --- a/src/lib/minions/data/killableMonsters/bosses/wildy.ts +++ b/src/lib/minions/data/killableMonsters/bosses/wildy.ts @@ -780,7 +780,9 @@ export const wildyKillableMonsters: KillableMonster[] = [ timeToFinish: Time.Minute * 4.3, emoji: '<:Pet_chaos_elemental:324127377070227456>', wildy: true, - + canBePked: true, + pkActivityRating: 4, + pkBaseDeathChance: 5, difficultyRating: 8, itemsRequired: deepResolveItems([ ["Black d'hide body", "Karil's leathertop"], @@ -788,17 +790,24 @@ export const wildyKillableMonsters: KillableMonster[] = [ ]), notifyDrops: resolveItems(['Pet chaos elemental']), qpRequired: 0, - itemInBankBoosts: [ + equippedItemBoosts: [ { - [itemID("Craw's bow")]: 20, - [itemID('Webweaver bow')]: 25 + items: [ + { boostPercent: 25, itemID: itemID('Webweaver bow') }, + { boostPercent: 20, itemID: itemID("Craw's bow") } + ], + gearSetup: 'wildy' }, { - [itemID('Archers ring')]: 3, - [itemID('Archers ring (i)')]: 5 + items: [ + { boostPercent: 5, itemID: itemID('Archers ring (i)') }, + { boostPercent: 3, itemID: itemID('Archers ring') } + ], + gearSetup: 'wildy' }, { - [itemID('Barrows gloves')]: 3 + items: [{ boostPercent: 3, itemID: itemID('Barrows gloves') }], + gearSetup: 'wildy' } ], defaultAttackStyles: [SkillsEnum.Attack], @@ -815,16 +824,28 @@ export const wildyKillableMonsters: KillableMonster[] = [ timeToFinish: Time.Minute * 3.3, emoji: '<:Ancient_staff:412845709453426689>', wildy: true, + canBePked: true, + pkActivityRating: 4, + pkBaseDeathChance: 2, difficultyRating: 6, notifyDrops: resolveItems(['Pet chaos elemental']), qpRequired: 0, - itemInBankBoosts: [ + equippedItemBoosts: [ { - [itemID("Craw's bow")]: 20, - [itemID('Webweaver bow')]: 25 + items: [ + { boostPercent: 25, itemID: itemID('Webweaver bow') }, + { boostPercent: 20, itemID: itemID("Craw's bow") } + ], + gearSetup: 'wildy' }, - { [itemID("Karil's leathertop")]: 3 }, - { [itemID("Karil's leatherskirt")]: 3 } + { + items: [{ boostPercent: 3, itemID: itemID("Karil's leathertop") }], + gearSetup: 'wildy' + }, + { + items: [{ boostPercent: 3, itemID: itemID("Karil's leatherskirt") }], + gearSetup: 'wildy' + } ], defaultAttackStyles: [SkillsEnum.Ranged], combatXpMultiplier: 1.125, @@ -840,10 +861,17 @@ export const wildyKillableMonsters: KillableMonster[] = [ timeToFinish: Time.Minute * 2.9, emoji: '<:Fedora:456179157303427092>', wildy: true, - + canBePked: true, + pkActivityRating: 6, + pkBaseDeathChance: 7, difficultyRating: 6, qpRequired: 0, - itemInBankBoosts: [{ [itemID('Occult necklace')]: 10 }], + equippedItemBoosts: [ + { + items: [{ boostPercent: 10, itemID: itemID('Occult necklace') }], + gearSetup: 'wildy' + } + ], defaultAttackStyles: [SkillsEnum.Magic], combatXpMultiplier: 1.25, healAmountNeeded: 4 * 20, @@ -858,10 +886,22 @@ export const wildyKillableMonsters: KillableMonster[] = [ timeToFinish: Time.Minute * 3.0, emoji: '<:Scorpias_offspring:324127378773377024>', wildy: true, + canBePked: true, + pkActivityRating: 6, + pkBaseDeathChance: 7, difficultyRating: 7, notifyDrops: resolveItems(["Scorpia's offspring"]), qpRequired: 0, - itemInBankBoosts: [{ [itemID('Occult necklace')]: 10 }, { [itemID('Harmonised nightmare staff')]: 10 }], + equippedItemBoosts: [ + { + items: [{ boostPercent: 10, itemID: itemID('Occult necklace') }], + gearSetup: 'wildy' + }, + { + items: [{ boostPercent: 10, itemID: itemID('Harmonised nightmare staff') }], + gearSetup: 'wildy' + } + ], defaultAttackStyles: [SkillsEnum.Magic], combatXpMultiplier: 1.3, healAmountNeeded: 4 * 20, diff --git a/src/lib/minions/data/killableMonsters/chaeldarMonsters.ts b/src/lib/minions/data/killableMonsters/chaeldarMonsters.ts index 6b5ca6fd3e8..ca14b5dd277 100644 --- a/src/lib/minions/data/killableMonsters/chaeldarMonsters.ts +++ b/src/lib/minions/data/killableMonsters/chaeldarMonsters.ts @@ -32,12 +32,15 @@ export const chaeldarMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 60, table: Monsters.Aviansie, - wildy: false, + wildy: true, difficultyRating: 4, qpRequired: 0, defaultAttackStyles: [SkillsEnum.Ranged], disallowedAttackStyles: [SkillsEnum.Attack, SkillsEnum.Strength, SkillsEnum.Magic], - healAmountNeeded: 24 + healAmountNeeded: 24, + pkActivityRating: 7, + pkBaseDeathChance: 10, + revsWeaponBoost: true }, { id: Monsters.BlackDemon.id, @@ -45,7 +48,7 @@ export const chaeldarMonsters: KillableMonster[] = [ aliases: Monsters.BlackDemon.aliases, timeToFinish: Time.Second * 36, table: Monsters.BlackDemon, - wildy: false, + wildy: true, difficultyRating: 3, existsInCatacombs: true, @@ -64,7 +67,11 @@ export const chaeldarMonsters: KillableMonster[] = [ canCannon: true, // Even if no multi, can safespot for same effect cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 7, + pkBaseDeathChance: 9, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.CaveHorror.id, @@ -209,7 +216,7 @@ export const chaeldarMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 25, table: Monsters.GreaterDemon, - wildy: false, + wildy: true, existsInCatacombs: true, difficultyRating: 2, @@ -227,7 +234,11 @@ export const chaeldarMonsters: KillableMonster[] = [ attackStylesUsed: [GearStat.AttackSlash], canCannon: true, cannonMulti: true, - canBarrage: false + canBarrage: false, + pkActivityRating: 7, + pkBaseDeathChance: 9, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.IronDragon.id, diff --git a/src/lib/minions/data/killableMonsters/krystiliaMonsters.ts b/src/lib/minions/data/killableMonsters/krystiliaMonsters.ts index 3bfb7eda8c9..762eb3a5917 100644 --- a/src/lib/minions/data/killableMonsters/krystiliaMonsters.ts +++ b/src/lib/minions/data/killableMonsters/krystiliaMonsters.ts @@ -1,7 +1,7 @@ import { Time } from 'e'; import { Monsters } from 'oldschooljs'; -import resolveItems from '../../../util/resolveItems'; +import resolveItems, { deepResolveItems } from '../../../util/resolveItems'; import { KillableMonster } from '../../types'; export const krystiliaMonsters: KillableMonster[] = [ @@ -14,7 +14,10 @@ export const krystiliaMonsters: KillableMonster[] = [ wildy: true, difficultyRating: 5, - qpRequired: 0 + qpRequired: 0, + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true }, { id: Monsters.ChaosDruid.id, @@ -29,7 +32,10 @@ export const krystiliaMonsters: KillableMonster[] = [ qpRequired: 0, canCannon: true, cannonMulti: true, - canBarrage: false + canBarrage: false, + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true }, { id: Monsters.DarkWarrior.id, @@ -44,7 +50,10 @@ export const krystiliaMonsters: KillableMonster[] = [ qpRequired: 0, canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true }, { id: Monsters.DeadlyRedSpider.id, @@ -59,7 +68,8 @@ export const krystiliaMonsters: KillableMonster[] = [ qpRequired: 0, canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + revsWeaponBoost: true }, { id: Monsters.ElderChaosDruid.id, @@ -74,7 +84,10 @@ export const krystiliaMonsters: KillableMonster[] = [ qpRequired: 0, canCannon: true, cannonMulti: true, - canBarrage: false + canBarrage: false, + pkActivityRating: 2, + pkBaseDeathChance: 7, + revsWeaponBoost: true }, { id: Monsters.Ent.id, @@ -86,11 +99,14 @@ export const krystiliaMonsters: KillableMonster[] = [ wildy: true, difficultyRating: 3, - itemsRequired: resolveItems(['Dragon axe', 'Rune axe']), + itemsRequired: deepResolveItems([['Dragon axe', 'Rune axe']]), qpRequired: 0, canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 2, + pkBaseDeathChance: 4, + revsWeaponBoost: true }, { id: Monsters.GuardBandit.id, @@ -105,7 +121,11 @@ export const krystiliaMonsters: KillableMonster[] = [ qpRequired: 0, canCannon: true, cannonMulti: true, - canBarrage: false + canBarrage: false, + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true, + canBePked: true }, { id: Monsters.LavaDragon.id, @@ -119,7 +139,11 @@ export const krystiliaMonsters: KillableMonster[] = [ difficultyRating: 4, itemsRequired: resolveItems(['Anti-dragon shield']), notifyDrops: resolveItems(['Draconic visage']), - qpRequired: 0 + qpRequired: 0, + pkActivityRating: 3, + pkBaseDeathChance: 4, + revsWeaponBoost: true, + canBePked: true }, { id: Monsters.MagicAxe.id, @@ -131,11 +155,14 @@ export const krystiliaMonsters: KillableMonster[] = [ wildy: true, difficultyRating: 3, - // itemsRequired: resolveItems(['Lockpick']), + itemsRequired: resolveItems(['Lockpick']), qpRequired: 0, levelRequirements: { - // theiving: 23 - } + thieving: 23 + }, + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true }, { id: Monsters.Mammoth.id, @@ -150,7 +177,11 @@ export const krystiliaMonsters: KillableMonster[] = [ qpRequired: 0, canCannon: true, cannonMulti: true, - canBarrage: false + canBarrage: false, + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true, + canBePked: true }, { id: Monsters.Pirate.id, @@ -162,10 +193,13 @@ export const krystiliaMonsters: KillableMonster[] = [ wildy: true, difficultyRating: 3, - // itemsRequired: resolveItems(['Lockpick']), + itemsRequired: resolveItems(['Lockpick']), levelRequirements: { - // thieving: 39 + thieving: 39 }, - qpRequired: 0 + qpRequired: 0, + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true } ]; diff --git a/src/lib/minions/data/killableMonsters/mazchnaMonsters.ts b/src/lib/minions/data/killableMonsters/mazchnaMonsters.ts index e7c6a0a5e8d..b68e6ebc1b9 100644 --- a/src/lib/minions/data/killableMonsters/mazchnaMonsters.ts +++ b/src/lib/minions/data/killableMonsters/mazchnaMonsters.ts @@ -86,7 +86,11 @@ export const mazchnaMonsters: KillableMonster[] = [ attackStylesUsed: [GearStat.AttackCrush], canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 4, + pkBaseDeathChance: 2, + revsWeaponBoost: true, + canBePked: true }, { id: Monsters.FeralVampyre.id, @@ -151,7 +155,7 @@ export const mazchnaMonsters: KillableMonster[] = [ aliases: Monsters.HillGiant.aliases, timeToFinish: Time.Second * 10, table: Monsters.HillGiant, - wildy: false, + wildy: true, existsInCatacombs: true, difficultyRating: 1, @@ -161,7 +165,10 @@ export const mazchnaMonsters: KillableMonster[] = [ attackStylesUsed: [GearStat.AttackCrush], canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 1, + pkBaseDeathChance: 5, + revsWeaponBoost: true }, { id: Monsters.Obor.id, @@ -211,7 +218,10 @@ export const mazchnaMonsters: KillableMonster[] = [ attackStylesUsed: [GearStat.AttackCrush], canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 4, + pkBaseDeathChance: 3, + revsWeaponBoost: true }, { id: Monsters.Killerwatt.id, diff --git a/src/lib/minions/data/killableMonsters/nieveMonsters.ts b/src/lib/minions/data/killableMonsters/nieveMonsters.ts index 3847e9c904c..4d66b249b76 100644 --- a/src/lib/minions/data/killableMonsters/nieveMonsters.ts +++ b/src/lib/minions/data/killableMonsters/nieveMonsters.ts @@ -32,7 +32,7 @@ export const nieveMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 76, table: Monsters.BlackDragon, - wildy: false, + wildy: true, difficultyRating: 4, itemsRequired: resolveItems(['Anti-dragon shield']), @@ -42,7 +42,11 @@ export const nieveMonsters: KillableMonster[] = [ attackStylesUsed: [GearStat.AttackSlash], canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 2, + pkBaseDeathChance: 7, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.BrutalBlackDragon.id, diff --git a/src/lib/minions/data/killableMonsters/revs.ts b/src/lib/minions/data/killableMonsters/revs.ts index a08f3f89392..a95a3cf52e3 100644 --- a/src/lib/minions/data/killableMonsters/revs.ts +++ b/src/lib/minions/data/killableMonsters/revs.ts @@ -74,7 +74,7 @@ export const revenantMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 147, table: Monsters.RevenantDragon, wildy: true, - difficultyRating: 9, + difficultyRating: 10, qpRequired: 0, pkActivityRating: 9, pkBaseDeathChance: 8, diff --git a/src/lib/minions/data/killableMonsters/turaelMonsters.ts b/src/lib/minions/data/killableMonsters/turaelMonsters.ts index 9a45847f42c..eec8bc47628 100644 --- a/src/lib/minions/data/killableMonsters/turaelMonsters.ts +++ b/src/lib/minions/data/killableMonsters/turaelMonsters.ts @@ -455,7 +455,9 @@ export const turaelMonsters: KillableMonster[] = [ qpRequired: 0, canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 1, + pkBaseDeathChance: 1 }, { id: Monsters.Goblin.id, @@ -483,7 +485,10 @@ export const turaelMonsters: KillableMonster[] = [ qpRequired: 0, canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 8, + pkBaseDeathChance: 4, + revsWeaponBoost: true }, { id: Monsters.GrizzlyBearCub.id, @@ -840,7 +845,7 @@ export const turaelMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 10, table: Monsters.Scorpion, - wildy: false, + wildy: true, difficultyRating: 1, qpRequired: 0, @@ -849,7 +854,10 @@ export const turaelMonsters: KillableMonster[] = [ canBarrage: false, healAmountNeeded: 8, attackStyleToUse: GearStat.AttackSlash, - attackStylesUsed: [GearStat.AttackCrush] + attackStylesUsed: [GearStat.AttackCrush], + pkActivityRating: 3, + pkBaseDeathChance: 2, + revsWeaponBoost: true }, { id: Monsters.Seagull.id, @@ -890,7 +898,7 @@ export const turaelMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 10, table: Monsters.Skeleton, - wildy: false, + wildy: true, existsInCatacombs: true, difficultyRating: 1, @@ -900,7 +908,10 @@ export const turaelMonsters: KillableMonster[] = [ canBarrage: false, healAmountNeeded: 11, attackStyleToUse: GearStat.AttackSlash, - attackStylesUsed: [GearStat.AttackCrush] + attackStylesUsed: [GearStat.AttackCrush], + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true }, { id: Monsters.SkeletonFremennik.id, @@ -971,13 +982,16 @@ export const turaelMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 5, table: Monsters.Spider, - wildy: false, + wildy: true, difficultyRating: 1, qpRequired: 0, canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true }, { id: Monsters.SulphurLizard.id, @@ -1200,7 +1214,7 @@ export const turaelMonsters: KillableMonster[] = [ aliases: Monsters.Zombie.aliases, timeToFinish: Time.Second * 10, table: Monsters.Zombie, - wildy: false, + wildy: true, difficultyRating: 1, qpRequired: 0, @@ -1209,7 +1223,10 @@ export const turaelMonsters: KillableMonster[] = [ canBarrage: false, healAmountNeeded: 9, attackStyleToUse: GearStat.AttackSlash, - attackStylesUsed: [GearStat.AttackCrush] + attackStylesUsed: [GearStat.AttackCrush], + pkActivityRating: 6, + pkBaseDeathChance: 4, + revsWeaponBoost: true }, { id: Monsters.ZombieRat.id, diff --git a/src/lib/minions/data/killableMonsters/vannakaMonsters.ts b/src/lib/minions/data/killableMonsters/vannakaMonsters.ts index d4a7486c415..fe867c24e0c 100644 --- a/src/lib/minions/data/killableMonsters/vannakaMonsters.ts +++ b/src/lib/minions/data/killableMonsters/vannakaMonsters.ts @@ -37,7 +37,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 29, table: Monsters.AbyssalDemon, - wildy: false, + wildy: true, difficultyRating: 3, notifyDrops: resolveItems(['Abyssal head', 'Abyssal dagger']), @@ -58,7 +58,11 @@ export const vannakaMonsters: KillableMonster[] = [ healAmountNeeded: 35, attackStyleToUse: GearStat.AttackSlash, attackStylesUsed: [GearStat.AttackStab], - canBarrage: true + canBarrage: true, + pkActivityRating: 7, + pkBaseDeathChance: 10, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.AbyssalSire.id, @@ -118,7 +122,11 @@ export const vannakaMonsters: KillableMonster[] = [ [itemID('Kodai wand')]: 12, [itemID('Staff of the dead')]: 8 } - ] + ], + pkActivityRating: 4, + pkBaseDeathChance: 3, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.BabyBlueDragon.id, @@ -207,7 +215,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 27, table: Monsters.Bloodveld, - wildy: false, + wildy: true, difficultyRating: 1, qpRequired: 0, @@ -235,7 +243,10 @@ export const vannakaMonsters: KillableMonster[] = [ healAmountNeeded: 12, attackStyleToUse: GearStat.AttackRanged, attackStylesUsed: [GearStat.AttackMagic], - canCannon: true + canCannon: true, + pkActivityRating: 4, + pkBaseDeathChance: 6, + revsWeaponBoost: true }, { id: Monsters.BlueDragon.id, @@ -438,7 +449,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 18, table: Monsters.DustDevil, - wildy: false, + wildy: true, difficultyRating: 2, existsInCatacombs: true, @@ -459,7 +470,11 @@ export const vannakaMonsters: KillableMonster[] = [ canBarrage: true, healAmountNeeded: 16, attackStyleToUse: GearStat.AttackSlash, - attackStylesUsed: [GearStat.AttackCrush] + attackStylesUsed: [GearStat.AttackCrush], + pkActivityRating: 6, + pkBaseDeathChance: 8, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.ElfArcher.id, @@ -531,7 +546,10 @@ export const vannakaMonsters: KillableMonster[] = [ healAmountNeeded: 17, attackStyleToUse: GearStat.AttackSlash, attackStylesUsed: [GearStat.AttackSlash], - canCannon: true + canCannon: true, + pkActivityRating: 3, + pkBaseDeathChance: 8, + revsWeaponBoost: true }, { id: Monsters.Gargoyle.id, @@ -616,7 +634,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 37.2, table: Monsters.GreaterNechryael, - wildy: false, + wildy: true, difficultyRating: 5, qpRequired: 0, @@ -636,7 +654,11 @@ export const vannakaMonsters: KillableMonster[] = [ attackStyleToUse: GearStat.AttackSlash, attackStylesUsed: [GearStat.AttackCrush], canBarrage: true, - canCannon: true + canCannon: true, + pkActivityRating: 8, + pkBaseDeathChance: 9, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.GreenDragon.id, @@ -653,7 +675,9 @@ export const vannakaMonsters: KillableMonster[] = [ healAmountNeeded: 20, attackStyleToUse: GearStat.AttackSlash, attackStylesUsed: [GearStat.AttackSlash], - canCannon: true + canCannon: true, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.HarpieBugSwarm.id, @@ -681,7 +705,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 39, table: Monsters.Hellhound, - wildy: false, + wildy: true, existsInCatacombs: true, difficultyRating: 3, @@ -698,7 +722,11 @@ export const vannakaMonsters: KillableMonster[] = [ canCannon: true, // Not multi but you can safespot for the same effect cannonMulti: true, - canBarrage: false + canBarrage: false, + pkActivityRating: 5, + pkBaseDeathChance: 8, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.IceGiant.id, @@ -715,8 +743,12 @@ export const vannakaMonsters: KillableMonster[] = [ attackStyleToUse: GearStat.AttackSlash, attackStylesUsed: [GearStat.AttackSlash], canCannon: true, - cannonMulti: false, - canBarrage: false + cannonMulti: true, + canBarrage: false, + pkActivityRating: 2, + pkBaseDeathChance: 6, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.IceTroll.id, @@ -798,7 +830,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 25, table: Monsters.Jelly, - wildy: false, + wildy: true, difficultyRating: 2, qpRequired: 0, @@ -808,7 +840,11 @@ export const vannakaMonsters: KillableMonster[] = [ superior: Monsters.VitreousJelly, healAmountNeeded: 14, attackStyleToUse: GearStat.AttackRanged, - attackStylesUsed: [GearStat.AttackMagic] + attackStylesUsed: [GearStat.AttackMagic], + pkActivityRating: 6, + pkBaseDeathChance: 8, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.JungleHorror.id, @@ -876,7 +912,11 @@ export const vannakaMonsters: KillableMonster[] = [ canCannon: true, // No multi spots (i think) but you can safespot for same effect. cannonMulti: true, - canBarrage: false + canBarrage: false, + pkActivityRating: 7, + pkBaseDeathChance: 9, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.Molanisk.id, @@ -911,7 +951,10 @@ export const vannakaMonsters: KillableMonster[] = [ healAmountNeeded: 17, attackStyleToUse: GearStat.AttackSlash, attackStylesUsed: [GearStat.AttackSlash], - canCannon: true + canCannon: true, + pkActivityRating: 4, + pkBaseDeathChance: 3, + revsWeaponBoost: true }, { id: Monsters.Bryophyta.id, @@ -1148,7 +1191,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 15, table: Monsters.SpiritualMage, - wildy: false, + wildy: true, difficultyRating: 4, qpRequired: 0, @@ -1157,7 +1200,10 @@ export const vannakaMonsters: KillableMonster[] = [ }, healAmountNeeded: 27, attackStyleToUse: GearStat.AttackRanged, - attackStylesUsed: [GearStat.AttackMagic] + attackStylesUsed: [GearStat.AttackMagic], + pkActivityRating: 4, + pkBaseDeathChance: 6, + revsWeaponBoost: true }, { id: Monsters.SpiritualRanger.id, @@ -1166,7 +1212,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 18, table: Monsters.SpiritualRanger, - wildy: false, + wildy: true, difficultyRating: 3, qpRequired: 0, @@ -1175,7 +1221,10 @@ export const vannakaMonsters: KillableMonster[] = [ }, healAmountNeeded: 25, attackStyleToUse: GearStat.AttackSlash, - attackStylesUsed: [GearStat.AttackRanged] + attackStylesUsed: [GearStat.AttackRanged], + pkActivityRating: 4, + pkBaseDeathChance: 6, + revsWeaponBoost: true }, { id: Monsters.SpiritualWarrior.id, @@ -1184,7 +1233,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 19, table: Monsters.SpiritualWarrior, - wildy: false, + wildy: true, difficultyRating: 3, qpRequired: 0, @@ -1193,7 +1242,10 @@ export const vannakaMonsters: KillableMonster[] = [ }, healAmountNeeded: 26, attackStyleToUse: GearStat.AttackSlash, - attackStylesUsed: [GearStat.AttackSlash] + attackStylesUsed: [GearStat.AttackSlash], + pkActivityRating: 4, + pkBaseDeathChance: 6, + revsWeaponBoost: true }, { id: Monsters.TerrorDog.id, diff --git a/src/lib/minions/types.ts b/src/lib/minions/types.ts index 4ef8b1d0716..48d197a735b 100644 --- a/src/lib/minions/types.ts +++ b/src/lib/minions/types.ts @@ -69,6 +69,7 @@ export interface KillableMonster { existsInCatacombs?: boolean; qpRequired?: number; difficultyRating?: number; + revsWeaponBoost?: boolean; /** * An array of objects of ([key: itemID]: boostPercentage) boosts that apply to @@ -133,6 +134,7 @@ export interface KillableMonster { requiredQuests?: QuestID[]; deathProps?: Omit['0'], 'currentKC'>; diaryRequirement?: [Diary, DiaryTier]; + wildySlayerCave?: boolean; } /* * Monsters will have an array of Consumables diff --git a/src/lib/openables.ts b/src/lib/openables.ts index e13b7127e2e..e4b93fec3dc 100644 --- a/src/lib/openables.ts +++ b/src/lib/openables.ts @@ -14,6 +14,7 @@ import { cluesRaresCL } from './data/CollectionsExport'; import { defaultFarmingContract } from './minions/farming'; import { FarmingContract } from './minions/farming/types'; import { shadeChestOpenables } from './shadesKeys'; +import { nestTable } from './simulation/birdsNest'; import { BagFullOfGemsTable, BuildersSupplyCrateTable, @@ -307,6 +308,14 @@ const osjsOpenables: UnifiedOpenable[] = [ output: Openables.NestBoxSeeds.table, allItems: Openables.NestBoxSeeds.table.allItems }, + { + name: 'Bird nest', + id: 5070, + openedItem: getOSItem(5070), + aliases: ['bird nest', 'nest'], + output: nestTable, + allItems: nestTable.allItems + }, { name: 'Ogre coffin', id: 4850, diff --git a/src/lib/slayer/constants.ts b/src/lib/slayer/constants.ts index 512f87350fc..c3b327a990f 100644 --- a/src/lib/slayer/constants.ts +++ b/src/lib/slayer/constants.ts @@ -12,7 +12,8 @@ export const slayerMasterChoices = [ 'Nieve', 'Chaeldar', 'Mazchna', - 'Turael' + 'Turael', + 'Krystilia' ].map(smc => { return { name: smc, value: smc }; }); diff --git a/src/lib/slayer/slayerMasters.ts b/src/lib/slayer/slayerMasters.ts index 91244d9c254..058196d72ec 100644 --- a/src/lib/slayer/slayerMasters.ts +++ b/src/lib/slayer/slayerMasters.ts @@ -3,6 +3,7 @@ import { MonsterSlayerMaster } from 'oldschooljs'; import { chaeldarTasks } from './tasks/chaeldarTasks'; import { duradelTasks } from './tasks/duradelTasks'; import { konarTasks } from './tasks/konarTasks'; +import { krystiliaTasks } from './tasks/krystiliaTasks'; import { mazchnaTasks } from './tasks/mazchnaTasks'; import { nieveTasks } from './tasks/nieveTasks'; import { turaelTasks } from './tasks/turaelTasks'; @@ -77,5 +78,13 @@ export const slayerMasters: SlayerMaster[] = [ combatLvl: 100, slayerLvl: 50, osjsEnum: MonsterSlayerMaster.Duradel + }, + { + id: 8, + name: 'Krystilia', + aliases: ['krystilia', 'wildy slayer', 'wilderness slayer'], + tasks: krystiliaTasks, + basePoints: 25, + osjsEnum: MonsterSlayerMaster.Krystilia } ]; diff --git a/src/lib/slayer/slayerUnlocks.ts b/src/lib/slayer/slayerUnlocks.ts index b7556ad04a3..a915056f427 100644 --- a/src/lib/slayer/slayerUnlocks.ts +++ b/src/lib/slayer/slayerUnlocks.ts @@ -36,6 +36,7 @@ export enum SlayerTaskUnlocksEnum { StopTheWyvern, Basilocked, ActualVampyreSlayer, + IWildyMoreSlayer, // Extension Unlocks NeedMoreDarkness, AnkouVeryMuch, @@ -62,6 +63,7 @@ export enum SlayerTaskUnlocksEnum { WyverNotherTwo, Basilonger, MoreAtStake, + Revenenenenenants, // Item Purchases: SlayerRing, HerbSack, @@ -222,6 +224,14 @@ export const SlayerRewardsShop: SlayerTaskUnlocks[] = [ canBeRemoved: true, aliases: ['vampyre slayer', 'vampire slayer', 'actual vampire slayer', 'vampyres', 'vampires'] }, + { + id: SlayerTaskUnlocksEnum.IWildyMoreSlayer, + name: 'I Wildy More Slayer', + desc: 'Krystilia will be able to assign Jellies, Dust Devils, Nechryaels and Abyssal Demons as your task.', + slayerPointCost: 0, + canBeRemoved: true, + aliases: ['wildy slayer'] + }, { id: SlayerTaskUnlocksEnum.SlayerRing, name: 'Slayer ring', @@ -514,5 +524,15 @@ export const SlayerRewardsShop: SlayerTaskUnlocks[] = [ slayerPointCost: 300, canBeRemoved: false, aliases: ['broad bolts', 'broads', 'broad arrows', 'fletching', 'broad fletching'] + }, + { + id: SlayerTaskUnlocksEnum.Revenenenenenants, + name: 'Revenenenenenants', + desc: 'Extends Revenants tasks', + slayerPointCost: 100, + extendID: [Monsters.RevenantImp.id], + extendMult: 1.5, + canBeRemoved: true, + aliases: ['extend revenants', 'extend revs'] } ]; diff --git a/src/lib/slayer/slayerUtil.ts b/src/lib/slayer/slayerUtil.ts index b53b4ea0658..fb24c0c3f5a 100644 --- a/src/lib/slayer/slayerUtil.ts +++ b/src/lib/slayer/slayerUtil.ts @@ -17,7 +17,7 @@ import resolveItems from '../util/resolveItems'; import { autoslayModes } from './constants'; import { slayerMasters } from './slayerMasters'; import { SlayerRewardsShop, SlayerTaskUnlocksEnum } from './slayerUnlocks'; -import { bossTasks } from './tasks/bossTasks'; +import { bossTasks, wildernessBossTasks } from './tasks/bossTasks'; import { AssignableSlayerTask, SlayerMaster } from './types'; export enum SlayerMasterEnum { @@ -37,6 +37,7 @@ export interface DetermineBoostParams { monster: KillableMonster; method?: PvMMethod | null; isOnTask?: boolean; + wildyJelly?: boolean; } export function determineBoostChoice(params: DetermineBoostParams) { let boostChoice = 'none'; @@ -52,9 +53,15 @@ export function determineBoostChoice(params: DetermineBoostParams) { boostChoice = 'burst'; } else if (params.method && params.method === 'cannon') { boostChoice = 'cannon'; - } else if (params.cbOpts.includes(CombatOptionsEnum.AlwaysIceBarrage) && params.monster!.canBarrage) { + } else if ( + params.cbOpts.includes(CombatOptionsEnum.AlwaysIceBarrage) && + (params.monster!.canBarrage || params.wildyJelly) + ) { boostChoice = 'barrage'; - } else if (params.cbOpts.includes(CombatOptionsEnum.AlwaysIceBurst) && params.monster!.canBarrage) { + } else if ( + params.cbOpts.includes(CombatOptionsEnum.AlwaysIceBurst) && + (params.monster!.canBarrage || params.wildyJelly) + ) { boostChoice = 'burst'; } else if (params.cbOpts.includes(CombatOptionsEnum.AlwaysCannon)) { boostChoice = 'cannon'; @@ -161,6 +168,12 @@ export function userCanUseTask( !myUnlocks.includes(SlayerTaskUnlocksEnum.Basilocked) ) return false; + if ( + (lmon === 'dust devil' || lmon === 'greater nechryael' || lmon === 'abyssal demon' || lmon === 'jelly') && + lmast === 'krystilia' && + !myUnlocks.includes(SlayerTaskUnlocksEnum.IWildyMoreSlayer) + ) + return false; return true; } @@ -168,6 +181,7 @@ export async function assignNewSlayerTask(_user: MUser, master: SlayerMaster) { // assignedTask is the task object, currentTask is the database row. const baseTasks = [...master.tasks].filter(t => userCanUseTask(_user, t, master, false)); let bossTask = false; + let wildyBossTask = false; if ( _user.user.slayer_unlocks.includes(SlayerTaskUnlocksEnum.LikeABoss) && (master.name.toLowerCase() === 'konar quo maten' || @@ -179,15 +193,27 @@ export async function assignNewSlayerTask(_user: MUser, master: SlayerMaster) { bossTask = true; } + if (_user.user.slayer_unlocks.includes(SlayerTaskUnlocksEnum.LikeABoss) && master.id === 8 && roll(25)) { + wildyBossTask = true; + } + let assignedTask: AssignableSlayerTask | null = null; + if (bossTask) { const baseBossTasks = bossTasks.filter(t => userCanUseTask(_user, t, master, true)); if (baseBossTasks.length > 0) { assignedTask = weightedPick(baseBossTasks); - } else { - assignedTask = weightedPick(baseTasks); } - } else { + } + + if (wildyBossTask) { + const baseWildyBossTasks = wildernessBossTasks.filter(t => userCanUseTask(_user, t, master, true)); + if (baseWildyBossTasks.length > 0) { + assignedTask = weightedPick(baseWildyBossTasks); + } + } + + if (assignedTask === null) { assignedTask = weightedPick(baseTasks); } @@ -266,6 +292,9 @@ export function getCommonTaskName(task: Monster) { case Monsters.TzHaarKet.id: commonName = 'TzHaar'; break; + case Monsters.RevenantImp.id: + commonName = 'Revenant'; + break; default: } if (commonName !== 'TzHaar' && !commonName.endsWith('s')) commonName += 's'; diff --git a/src/lib/slayer/tasks/bossTasks.ts b/src/lib/slayer/tasks/bossTasks.ts index 48d5e98c11e..bfaab90bdd8 100644 --- a/src/lib/slayer/tasks/bossTasks.ts +++ b/src/lib/slayer/tasks/bossTasks.ts @@ -34,7 +34,8 @@ export const bossTasks: AssignableSlayerTask[] = [ amount: [3, 35], weight: 1, monsters: [Monsters.Callisto.id], - isBoss: true + isBoss: true, + wilderness: true }, { monster: Monsters.Cerberus, @@ -52,14 +53,16 @@ export const bossTasks: AssignableSlayerTask[] = [ amount: [3, 35], weight: 1, monsters: [Monsters.ChaosElemental.id], - isBoss: true + isBoss: true, + wilderness: true }, { monster: Monsters.ChaosFanatic, amount: [3, 35], weight: 1, monsters: [Monsters.ChaosFanatic.id], - isBoss: true + isBoss: true, + wilderness: true }, { monster: Monsters.CommanderZilyana, @@ -77,7 +80,8 @@ export const bossTasks: AssignableSlayerTask[] = [ amount: [3, 35], weight: 1, monsters: [Monsters.CrazyArchaeologist.id], - isBoss: true + isBoss: true, + wilderness: true }, { monster: Monsters.DagannothPrime, @@ -200,7 +204,8 @@ export const bossTasks: AssignableSlayerTask[] = [ amount: [3, 35], weight: 1, monsters: [Monsters.Scorpia.id], - isBoss: true + isBoss: true, + wilderness: true }, { monster: Monsters.ThermonuclearSmokeDevil, @@ -215,14 +220,16 @@ export const bossTasks: AssignableSlayerTask[] = [ amount: [3, 35], weight: 1, monsters: [Monsters.Venenatis.id], - isBoss: true + isBoss: true, + wilderness: true }, { monster: Monsters.Vetion, amount: [3, 35], weight: 1, monsters: [Monsters.Vetion.id], - isBoss: true + isBoss: true, + wilderness: true }, { monster: Monsters.Vorkath, @@ -247,3 +254,62 @@ export const bossTasks: AssignableSlayerTask[] = [ isBoss: true } ]; + +export const wildernessBossTasks: AssignableSlayerTask[] = [ + { + monster: Monsters.Callisto, + amount: [3, 35], + weight: 1, + monsters: [Monsters.Callisto.id], + isBoss: true, + wilderness: true + }, + { + monster: Monsters.ChaosElemental, + amount: [3, 35], + weight: 1, + monsters: [Monsters.ChaosElemental.id], + isBoss: true, + wilderness: true + }, + { + monster: Monsters.ChaosFanatic, + amount: [3, 35], + weight: 1, + monsters: [Monsters.ChaosFanatic.id], + isBoss: true, + wilderness: true + }, + { + monster: Monsters.CrazyArchaeologist, + amount: [3, 35], + weight: 1, + monsters: [Monsters.CrazyArchaeologist.id], + isBoss: true, + wilderness: true + }, + { + monster: Monsters.Scorpia, + amount: [3, 35], + weight: 1, + monsters: [Monsters.Scorpia.id], + isBoss: true, + wilderness: true + }, + { + monster: Monsters.Venenatis, + amount: [3, 35], + weight: 1, + monsters: [Monsters.Venenatis.id], + isBoss: true, + wilderness: true + }, + { + monster: Monsters.Vetion, + amount: [3, 35], + weight: 1, + monsters: [Monsters.Vetion.id], + isBoss: true, + wilderness: true + } +]; diff --git a/src/lib/slayer/tasks/krystiliaTasks.ts b/src/lib/slayer/tasks/krystiliaTasks.ts new file mode 100644 index 00000000000..ffc05fecaa9 --- /dev/null +++ b/src/lib/slayer/tasks/krystiliaTasks.ts @@ -0,0 +1,333 @@ +import { Monsters } from 'oldschooljs'; + +import { SlayerTaskUnlocksEnum } from '../slayerUnlocks'; +import { AssignableSlayerTask } from '../types'; +import { wildernessBossTasks } from './bossTasks'; + +export const krystiliaTasks: AssignableSlayerTask[] = [ + { + monster: Monsters.AbyssalDemon, + amount: [75, 125], + weight: 5, + monsters: [Monsters.AbyssalDemon.id], + extendedAmount: [200, 250], + extendedUnlockId: SlayerTaskUnlocksEnum.AugmentMyAbbies, + slayerLevel: 85, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Ankou, + amount: [75, 125], + weight: 6, + monsters: [Monsters.Ankou.id], + extendedAmount: [91, 150], + extendedUnlockId: SlayerTaskUnlocksEnum.AnkouVeryMuch, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Aviansie, + amount: [75, 125], + weight: 7, + monsters: [Monsters.Aviansie.id], + extendedAmount: [200, 250], + extendedUnlockId: SlayerTaskUnlocksEnum.BirdsOfAFeather, + unlocked: true, + wilderness: true + }, + // { + // monster: Monsters.Bandit, + // amount: [75, 125], + // weight: 4, + // monsters: [Monsters.Bandit.id], + // unlocked: true, + // wilderness: true + // }, + { + monster: Monsters.GrizzlyBear, + amount: [65, 100], + weight: 6, + monsters: [Monsters.GrizzlyBear.id, Monsters.Artio.id, Monsters.Callisto.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.BlackDemon, + amount: [100, 150], + weight: 7, + monsters: [Monsters.BlackDemon.id], + extendedAmount: [200, 250], + extendedUnlockId: SlayerTaskUnlocksEnum.ItsDarkInHere, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.BlackDragon, + amount: [8, 16], + weight: 4, + monsters: [Monsters.BlackDragon.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.BlackKnight, + amount: [75, 125], + weight: 3, + monsters: [Monsters.BlackKnight.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Bloodveld, + amount: [70, 110], + weight: 4, + monsters: [Monsters.Bloodveld.id], + extendedAmount: [200, 250], + extendedUnlockId: SlayerTaskUnlocksEnum.BleedMeDry, + slayerLevel: 50, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.ChaosDruid, + amount: [50, 90], + weight: 5, + monsters: [Monsters.ChaosDruid.id, Monsters.ElderChaosDruid.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.DarkWarrior, + amount: [75, 125], + weight: 4, + monsters: [Monsters.DarkWarrior.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.DustDevil, + amount: [75, 125], + weight: 5, + monsters: [Monsters.DustDevil.id], + extendedAmount: [200, 250], + extendedUnlockId: SlayerTaskUnlocksEnum.ToDustYouShallReturn, + slayerLevel: 65, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.EarthWarrior, + amount: [75, 125], + weight: 6, + monsters: [Monsters.EarthWarrior.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Ent, + amount: [35, 60], + weight: 5, + monsters: [Monsters.Ent.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.FireGiant, + amount: [75, 125], + weight: 7, + monsters: [Monsters.FireGiant.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.GreaterDemon, + amount: [100, 150], + weight: 8, + monsters: [Monsters.GreaterDemon.id], + extendedAmount: [200, 250], + extendedUnlockId: SlayerTaskUnlocksEnum.GreaterChallenge, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.GreenDragon, + amount: [65, 100], + weight: 4, + monsters: [Monsters.GreenDragon.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Hellhound, + amount: [75, 125], + weight: 7, + monsters: [Monsters.Hellhound.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.HillGiant, + amount: [75, 125], + weight: 3, + monsters: [Monsters.HillGiant.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.IceGiant, + amount: [100, 150], + weight: 6, + monsters: [Monsters.IceGiant.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.IceWarrior, + amount: [100, 150], + weight: 7, + monsters: [Monsters.IceWarrior.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Jelly, + amount: [100, 150], + weight: 5, + monsters: [Monsters.Jelly.id], + slayerLevel: 52, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.LavaDragon, + amount: [35, 60], + weight: 3, + monsters: [Monsters.LavaDragon.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.LesserDemon, + amount: [80, 120], + weight: 6, + monsters: [Monsters.LesserDemon.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.MagicAxe, + amount: [75, 125], + weight: 7, + monsters: [Monsters.MagicAxe.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Mammoth, + amount: [75, 125], + weight: 6, + monsters: [Monsters.Mammoth.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.MossGiant, + amount: [100, 150], + weight: 4, + monsters: [Monsters.MossGiant.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.GreaterNechryael, + amount: [75, 125], + weight: 5, + monsters: [Monsters.GreaterNechryael.id], + slayerLevel: 80, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Pirate, + amount: [62, 75], + weight: 3, + monsters: [Monsters.Pirate.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.RevenantImp, + amount: [40, 100], + weight: 5, + monsters: [ + Monsters.RevenantCyclops.id, + Monsters.RevenantDarkBeast.id, + Monsters.RevenantDemon.id, + Monsters.RevenantDragon.id, + Monsters.RevenantGoblin.id, + Monsters.RevenantHellhound.id, + Monsters.RevenantHobgoblin.id, + Monsters.RevenantImp.id, + Monsters.RevenantKnight.id, + Monsters.RevenantOrk.id, + Monsters.RevenantPyrefiend.id + ], + extendedAmount: [100, 150], + extendedUnlockId: SlayerTaskUnlocksEnum.Revenenenenenants, + unlocked: true, + wilderness: true + }, + // { + // monster: Monsters.Rogue, + // amount: [75, 125], + // weight: 5, + // monsters: [Monsters.Rogue.id], + // unlocked: true, + // wilderness: true + // }, + { + monster: Monsters.Scorpion, + amount: [65, 100], + weight: 6, + monsters: [Monsters.Scorpia.id, Monsters.Scorpion.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Skeleton, + amount: [65, 100], + weight: 5, + monsters: [Monsters.Skeleton.id, Monsters.Vetion.id, Monsters.Calvarion.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Spider, + amount: [65, 100], + weight: 6, + monsters: [Monsters.Spider.id, Monsters.Venenatis.id, Monsters.Spindel.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.SpiritualRanger, + amount: [100, 150], + weight: 6, + monsters: [Monsters.SpiritualMage.id, Monsters.SpiritualRanger.id, Monsters.SpiritualWarrior.id], + extendedAmount: [181, 250], + extendedUnlockId: SlayerTaskUnlocksEnum.SpiritualFervour, + slayerLevel: 63, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Zombie, + amount: [75, 125], + weight: 3, + monsters: [Monsters.Zombie.id], + unlocked: true, + wilderness: true + }, + ...wildernessBossTasks +]; diff --git a/src/lib/slayer/types.ts b/src/lib/slayer/types.ts index eeecadf901b..423d4f75e35 100644 --- a/src/lib/slayer/types.ts +++ b/src/lib/slayer/types.ts @@ -18,6 +18,7 @@ export interface AssignableSlayerTask { dontAssign?: boolean; extendedAmount?: [number, number]; extendedUnlockId?: number; + wilderness?: boolean; } export interface SlayerMaster { diff --git a/src/lib/types/minions.ts b/src/lib/types/minions.ts index 05ec0ab5e30..b342e223e74 100644 --- a/src/lib/types/minions.ts +++ b/src/lib/types/minions.ts @@ -133,6 +133,7 @@ export interface MonsterActivityTaskOptions extends ActivityTaskOptions { died?: boolean; pkEncounters?: number; hasWildySupplies?: boolean; + isInWilderness?: boolean; } export interface ClueActivityTaskOptions extends ActivityTaskOptions { diff --git a/src/lib/util/repeatStoredTrip.ts b/src/lib/util/repeatStoredTrip.ts index 6d30f31d2b5..6abbc323d09 100644 --- a/src/lib/util/repeatStoredTrip.ts +++ b/src/lib/util/repeatStoredTrip.ts @@ -397,7 +397,8 @@ export const tripHandlers = { return { name: autocompleteMonsters.find(i => i.id === data.monsterID)?.name ?? data.monsterID.toString(), quantity: data.iQty, - method + method, + wilderness: data.isInWilderness }; } }, diff --git a/src/mahoji/commands/k.ts b/src/mahoji/commands/k.ts index e161c6854a9..55245b143fb 100644 --- a/src/mahoji/commands/k.ts +++ b/src/mahoji/commands/k.ts @@ -106,6 +106,12 @@ export const minionKCommand: OSBMahojiCommand = { description: 'Show information on this monster.', required: false }, + { + type: ApplicationCommandOptionType.Boolean, + name: 'wilderness', + description: 'If you want to kill the monster in the wilderness.', + required: false + }, { type: ApplicationCommandOptionType.Boolean, name: 'solo', @@ -123,6 +129,7 @@ export const minionKCommand: OSBMahojiCommand = { quantity?: number; method?: PvMMethod; show_info?: boolean; + wilderness?: boolean; solo?: boolean; }>) => { const user = await mUserFetch(userID); @@ -136,6 +143,7 @@ export const minionKCommand: OSBMahojiCommand = { options.name, options.quantity, options.method, + options.wilderness, options.solo ); } diff --git a/src/mahoji/commands/kill.ts b/src/mahoji/commands/kill.ts index 8b356e3277f..1e17e3d8d1e 100644 --- a/src/mahoji/commands/kill.ts +++ b/src/mahoji/commands/kill.ts @@ -82,7 +82,7 @@ export const killCommand: OSBMahojiCommand = { limit: determineKillLimit(user), catacombs: false, onTask: false, - lootTableTertiaryChanges: Array.from(user.buildCATertiaryItemChanges().entries()) + lootTableTertiaryChanges: Array.from(user.buildTertiaryItemChanges().entries()) }); if (result.error) { diff --git a/src/mahoji/lib/abstracted_commands/autoSlayCommand.ts b/src/mahoji/lib/abstracted_commands/autoSlayCommand.ts index 2d81c2e2cbe..fbb695f06a6 100644 --- a/src/mahoji/lib/abstracted_commands/autoSlayCommand.ts +++ b/src/mahoji/lib/abstracted_commands/autoSlayCommand.ts @@ -202,6 +202,12 @@ const AutoSlayMaxEfficiencyTable: AutoslayLink[] = [ efficientName: Monsters.Lizardman.name, efficientMonster: Monsters.Lizardman.id, efficientMethod: 'cannon' + }, + { + monsterID: Monsters.RevenantImp.id, + efficientName: Monsters.RevenantDemon.name, + efficientMonster: Monsters.RevenantDemon.id, + efficientMethod: 'none' } ]; @@ -293,7 +299,13 @@ export async function autoSlayCommand({ const ehpKillable = killableMonsters.find(m => m.id === ehpMonster?.efficientMonster); // If we don't have the requirements for the efficient monster, revert to default monster - if (ehpKillable?.levelRequirements !== undefined && !hasSkillReqs(user, ehpKillable.levelRequirements)[0]) { + if ( + (ehpKillable?.levelRequirements !== undefined && !hasSkillReqs(user, ehpKillable.levelRequirements)[0]) || + (usersTask.currentTask!.slayer_master_id === 8 && + [Monsters.Jelly.id, Monsters.Bloodveld.id, Monsters.BlackDragon.id].includes( + usersTask.assignedTask!.monster.id + )) + ) { runCommand({ commandName: 'k', args: { diff --git a/src/mahoji/lib/abstracted_commands/minionKill.ts b/src/mahoji/lib/abstracted_commands/minionKill.ts index 72c7e43691f..bd7b34d5fd2 100644 --- a/src/mahoji/lib/abstracted_commands/minionKill.ts +++ b/src/mahoji/lib/abstracted_commands/minionKill.ts @@ -94,6 +94,12 @@ const revSpecialWeapons = { mage: getOSItem("Thammaron's sceptre") } as const; +const revUpgradedWeapons = { + melee: getOSItem('Ursine chainmace'), + range: getOSItem('Webweaver bow'), + mage: getOSItem('Accursed sceptre') +} as const; + function formatMissingItems(consumables: Consumable[], timeToFinish: number) { const str = []; @@ -133,6 +139,7 @@ export async function minionKillCommand( name: string, quantity: number | undefined, method: PvMMethod | undefined, + wilderness: boolean | undefined, solo: boolean | undefined ) { if (user.minionIsBusy) { @@ -178,6 +185,16 @@ export async function minionKillCommand( return `You can't kill ${monster.name}, because you're not on a slayer task.`; } + if (monster.canBePked && wilderness === false) { + return `You can't kill ${monster.name} outside the wilderness.`; + } + + const isInWilderness = wilderness || (isOnTask && usersTask.assignedTask?.wilderness) || monster.canBePked; + + if (!monster.wildy && isInWilderness) { + return `You can't kill ${monster.name} in the wilderness.`; + } + const wildyGearStat = wildyGear.getStats()[key]; const revGearPercent = Math.max(0, calcWhatPercent(wildyGearStat, maxOffenceStats[key])); @@ -192,6 +209,10 @@ export async function minionKillCommand( } } + // Add jelly check as can barrage in wilderness + const jelly = monster.id === Monsters.Jelly.id; + const wildyJelly = jelly && isInWilderness; + // Set chosen boost based on priority: const myCBOpts = user.combatOptions; const boostChoice = determineBoostChoice({ @@ -199,7 +220,8 @@ export async function minionKillCommand( user, monster, method, - isOnTask + isOnTask, + wildyJelly }); // Check requirements @@ -235,7 +257,7 @@ export async function minionKillCommand( } } - for (const [itemID, boostAmount] of Object.entries(resolveAvailableItemBoosts(user, monster))) { + for (const [itemID, boostAmount] of Object.entries(resolveAvailableItemBoosts(user, monster, isInWilderness))) { timeToFinish *= (100 - boostAmount) / 100; boosts.push(`${boostAmount}% for ${itemNameFromID(parseInt(itemID))}`); } @@ -251,16 +273,37 @@ export async function minionKillCommand( let virtusBoost = 0; let virtusBoostMsg = ''; - const dragonBoost = 15; // Common boost percentage for dragon-related gear + let dragonBoost = 0; + let dragonBoostMsg = ''; + let revBoost = 0; + let revBoostMsg = ''; const isUndead = osjsMon?.data?.attributes?.includes(MonsterAttribute.Undead); const isDragon = osjsMon?.data?.attributes?.includes(MonsterAttribute.Dragon); + function applyRevWeaponBoost() { + const style = convertAttackStylesToSetup(user.user.attack_style); + const specialWeapon = revSpecialWeapons[style]; + const upgradedWeapon = revUpgradedWeapons[style]; + + if (wildyGear.hasEquipped(specialWeapon.name)) { + revBoost = 12.5; + timeToFinish = reduceNumByPercent(timeToFinish, revBoost); + revBoostMsg = `${revBoost}% for ${specialWeapon.name}`; + } + + if (wildyGear.hasEquipped(upgradedWeapon.name)) { + revBoost = 17.5; + timeToFinish = reduceNumByPercent(timeToFinish, revBoost); + revBoostMsg = `${revBoost}% for ${upgradedWeapon.name}`; + } + } + function applyDragonBoost() { - const hasDragonLance = monster?.canBePked + const hasDragonLance = isInWilderness ? wildyGear.hasEquipped('Dragon hunter lance') : user.hasEquippedOrInBank('Dragon hunter lance'); - const hasDragonCrossbow = monster?.canBePked + const hasDragonCrossbow = isInWilderness ? wildyGear.hasEquipped('Dragon hunter crossbow') : user.hasEquippedOrInBank('Dragon hunter crossbow'); @@ -268,17 +311,19 @@ export async function minionKillCommand( (hasDragonLance && !attackStyles.includes(SkillsEnum.Ranged) && !attackStyles.includes(SkillsEnum.Magic)) || (hasDragonCrossbow && attackStyles.includes(SkillsEnum.Ranged)) ) { - const boostMessage = hasDragonLance ? '15% for Dragon hunter lance' : '15% for Dragon hunter crossbow'; + dragonBoost = 15; // Common boost percentage for dragon-related gear + dragonBoostMsg = hasDragonLance + ? `${dragonBoost}% for Dragon hunter lance` + : `${dragonBoost}% for Dragon hunter crossbow`; timeToFinish = reduceNumByPercent(timeToFinish, dragonBoost); - boosts.push(boostMessage); } } function applyBlackMaskBoost() { - const hasBlackMask = monster?.canBePked + const hasBlackMask = isInWilderness ? wildyGear.hasEquipped('Black mask') : user.hasEquippedOrInBank('Black mask'); - const hasBlackMaskI = monster?.canBePked + const hasBlackMaskI = isInWilderness ? wildyGear.hasEquipped('Black mask (i)') : user.hasEquippedOrInBank('Black mask (i)'); @@ -298,10 +343,10 @@ export async function minionKillCommand( let salveEnhanced = false; const style = attackStyles[0]; if (style === 'ranged' || style === 'magic') { - salveBoost = monster?.canBePked + salveBoost = isInWilderness ? wildyGear.hasEquipped('Salve amulet(i)') : user.hasEquippedOrInBank('Salve amulet (i)'); - salveEnhanced = monster?.canBePked + salveEnhanced = isInWilderness ? wildyGear.hasEquipped('Salve amulet(ei)') : user.hasEquippedOrInBank('Salve amulet (ei)'); if (salveBoost) { @@ -311,10 +356,10 @@ export async function minionKillCommand( } on non-melee task`; } } else { - salveBoost = monster?.canBePked + salveBoost = isInWilderness ? wildyGear.hasEquipped('Salve amulet') : user.hasEquippedOrInBank('Salve amulet'); - salveEnhanced = monster?.canBePked + salveEnhanced = isInWilderness ? wildyGear.hasEquipped('Salve amulet (e)') : user.hasEquippedOrInBank('Salve amulet (e)'); if (salveBoost) { @@ -326,10 +371,19 @@ export async function minionKillCommand( } } + if (isInWilderness && monster.revsWeaponBoost) { + applyRevWeaponBoost(); + } + function calculateVirtusBoost() { let virtusPiecesEquipped = 0; + for (const item of resolveItems(['Virtus mask', 'Virtus robe top', 'Virtus robe bottom'])) { - if (user.gear.mage.hasEquipped(item)) { + if (isInWilderness) { + if (wildyGear.hasEquipped(item)) { + virtusPiecesEquipped += blackMaskBoost !== 0 && itemNameFromID(item) === 'Virtus mask' ? 0 : 1; + } + } else if (user.gear.mage.hasEquipped(item)) { virtusPiecesEquipped += blackMaskBoost !== 0 && itemNameFromID(item) === 'Virtus mask' ? 0 : 1; } } @@ -338,7 +392,7 @@ export async function minionKillCommand( virtusBoostMsg = virtusPiecesEquipped > 1 ? ` with ${virtusPiecesEquipped} Virtus pieces` - : virtusPiecesEquipped > 0 + : virtusPiecesEquipped === 1 ? ` with ${virtusPiecesEquipped} Virtus piece` : ''; } @@ -366,6 +420,17 @@ export async function minionKillCommand( } } + // Only choose greater boost: + if (dragonBoost || revBoost) { + if (revBoost > dragonBoost) { + timeToFinish = reduceNumByPercent(timeToFinish, revBoost); + boosts.push(revBoostMsg); + } else { + timeToFinish = reduceNumByPercent(timeToFinish, dragonBoost); + boosts.push(dragonBoostMsg); + } + } + if (revenants) { timeToFinish = reduceNumByPercent(timeToFinish, revGearPercent / 4); boosts.push(`${(revGearPercent / 4).toFixed(2)}% (out of a possible 25%) for ${key}`); @@ -386,15 +451,13 @@ export async function minionKillCommand( let chinning = false; let burstOrBarrage = 0; const hasCannon = cannonBanks.some(i => user.owns(i)); - if ((method === 'burst' || method === 'barrage') && !monster!.canBarrage) { - return `${monster!.name} cannot be barraged or burst.`; - } + + // Check for cannon if (method === 'cannon' && !hasCannon) { return "You don't own a Dwarf multicannon, so how could you use one?"; } - if (method === 'cannon' && !monster!.canCannon) { - return `${monster!.name} cannot be killed with a cannon.`; - } + + // Check for stats if (boostChoice === 'barrage' && user.skillLevel(SkillsEnum.Magic) < 94) { return `You need 94 Magic to use Ice Barrage. You have ${user.skillLevel(SkillsEnum.Magic)}`; } @@ -405,25 +468,58 @@ export async function minionKillCommand( return `You need 65 Ranged to use Chinning method. You have ${user.skillLevel(SkillsEnum.Ranged)}`; } - if (boostChoice === 'barrage' && attackStyles.includes(SkillsEnum.Magic) && monster!.canBarrage) { + // Wildy Monster checks + if (isInWilderness === true && boostChoice === 'cannon') { + if (monster.id === Monsters.HillGiant.id || monster.id === Monsters.MossGiant.id) { + usingCannon = isInWilderness; + } + if (monster.wildySlayerCave) { + usingCannon = isInWilderness; + cannonMulti = isInWilderness; + if (monster.id === Monsters.AbyssalDemon.id && !isOnTask) { + usingCannon = false; + cannonMulti = false; + } + } + } + + if ((method === 'burst' || method === 'barrage') && !monster!.canBarrage) { + if (jelly) { + if (!isInWilderness) { + return `${monster.name} can only be barraged or burst in the wilderness.`; + } + } else return `${monster!.name} cannot be barraged or burst.`; + } + + if (!usingCannon) { + if (method === 'cannon' && !monster!.canCannon) { + return `${monster!.name} cannot be killed with a cannon.`; + } + } + + if (boostChoice === 'barrage' && attackStyles.includes(SkillsEnum.Magic) && (monster!.canBarrage || wildyJelly)) { consumableCosts.push(iceBarrageConsumables); calculateVirtusBoost(); timeToFinish = reduceNumByPercent(timeToFinish, boostIceBarrage + virtusBoost); boosts.push(`${boostIceBarrage + virtusBoost}% for Ice Barrage${virtusBoostMsg}`); burstOrBarrage = SlayerActivityConstants.IceBarrage; - } else if (boostChoice === 'burst' && attackStyles.includes(SkillsEnum.Magic) && monster!.canBarrage) { + } else if ( + boostChoice === 'burst' && + attackStyles.includes(SkillsEnum.Magic) && + (monster!.canBarrage || wildyJelly) + ) { consumableCosts.push(iceBurstConsumables); calculateVirtusBoost(); timeToFinish = reduceNumByPercent(timeToFinish, boostIceBurst + virtusBoost); boosts.push(`${boostIceBurst + virtusBoost}% for Ice Burst${virtusBoostMsg}`); burstOrBarrage = SlayerActivityConstants.IceBurst; - } else if (boostChoice === 'cannon' && hasCannon && monster!.cannonMulti) { + } else if ((boostChoice === 'cannon' && hasCannon && monster!.cannonMulti) || cannonMulti) { usingCannon = true; cannonMulti = true; consumableCosts.push(cannonMultiConsumables); timeToFinish = reduceNumByPercent(timeToFinish, boostCannonMulti); boosts.push(`${boostCannonMulti}% for Cannon in multi`); - } else if (boostChoice === 'cannon' && hasCannon && monster!.canCannon) { + } else if ((boostChoice === 'cannon' && hasCannon && monster!.canCannon) || usingCannon) { usingCannon = true; consumableCosts.push(cannonSingleConsumables); timeToFinish = reduceNumByPercent(timeToFinish, boostCannon); @@ -481,7 +577,7 @@ export async function minionKillCommand( degItemBeingUsed.push(degItem); } } - } else { + } else if (!isInWilderness) { for (const degItem of degradeablePvmBoostItems) { const isUsing = convertPvmStylesToGearSetup(attackStyles).includes(degItem.attackStyle) && @@ -695,7 +791,7 @@ export async function minionKillCommand( let hasDied: boolean | undefined = undefined; let hasWildySupplies = undefined; - if (monster.canBePked) { + if (isInWilderness) { await increaseWildEvasionXp(user, duration); thePkCount = 0; hasDied = false; @@ -768,7 +864,7 @@ export async function minionKillCommand( foodStr += foodMessages; let gearToCheck: GearSetupType = convertAttackStyleToGearSetup(monster.attackStyleToUse); - if (monster.wildy) gearToCheck = 'wildy'; + if (isInWilderness) gearToCheck = 'wildy'; try { const { foodRemoved, reductions, reductionRatio } = await removeFoodFromUser({ @@ -776,11 +872,11 @@ export async function minionKillCommand( totalHealingNeeded: healAmountNeeded * quantity, healPerAction: Math.ceil(healAmountNeeded / quantity), activityName: monster.name, - attackStylesUsed: monster.wildy + attackStylesUsed: isInWilderness ? ['wildy'] : uniqueArr([...objectKeys(monster.minimumGearRequirements ?? {}), gearToCheck]), learningPercentage: percentReduced, - isWilderness: monster.wildy + isWilderness: isInWilderness }); if (foodRemoved.length === 0) { @@ -830,7 +926,7 @@ export async function minionKillCommand( // Remove items after food calc to prevent losing items if the user doesn't have the right amount of food. Example: Mossy key if (lootToRemove.length > 0) { updateBankSetting('economyStats_PVMCost', lootToRemove); - await user.specialRemoveItems(lootToRemove, { wildy: monster.wildy ? true : false }); + await user.specialRemoveItems(lootToRemove, { wildy: isInWilderness ? true : false }); totalCost.add(lootToRemove); } @@ -863,7 +959,8 @@ export async function minionKillCommand( burstOrBarrage: !burstOrBarrage ? undefined : burstOrBarrage, died: hasDied, pkEncounters: thePkCount, - hasWildySupplies + hasWildySupplies, + isInWilderness }); let response = `${minionName} is now killing ${quantity}x ${monster.name}, it'll take around ${formatDuration( duration diff --git a/src/mahoji/lib/abstracted_commands/slayerTaskCommand.ts b/src/mahoji/lib/abstracted_commands/slayerTaskCommand.ts index 22b26cc66e0..6d891682a59 100644 --- a/src/mahoji/lib/abstracted_commands/slayerTaskCommand.ts +++ b/src/mahoji/lib/abstracted_commands/slayerTaskCommand.ts @@ -100,7 +100,8 @@ export async function slayerListBlocksCommand(mahojiUser: MUser) { export async function slayerStatusCommand(mahojiUser: MUser) { const { currentTask, assignedTask, slayerMaster } = await getUsersCurrentSlayerInfo(mahojiUser.id); const { slayer_points: slayerPoints } = mahojiUser.user; - const { slayer_task_streak: slayerStreak } = await mahojiUser.fetchStats({ slayer_task_streak: true }); + const slayer_streaks = await mahojiUser.fetchStats({ slayer_task_streak: true, slayer_wildy_task_streak: true }); + return ( `${ currentTask @@ -111,7 +112,9 @@ export async function slayerStatusCommand(mahojiUser: MUser) { )}. You have ${currentTask.quantity_remaining.toLocaleString()} kills remaining.` : '' }` + - `\nYou have ${slayerPoints.toLocaleString()} slayer points, and have completed ${slayerStreak} tasks in a row.` + `\nYou have ${slayerPoints.toLocaleString()} slayer points, and have completed ${ + slayer_streaks.slayer_task_streak + } tasks in a row and ${slayer_streaks.slayer_wildy_task_streak} wilderness tasks in a row.` ); } @@ -242,8 +245,9 @@ export async function slayerNewTaskCommand({ const has99SlayerCape = user.skillLevel('slayer') >= 99 && user.hasEquippedOrInBank('Slayer cape'); - // Chooses a default slayer master: + // Chooses a default slayer master (excluding Krystilia): const proposedDefaultMaster = slayerMasters + .filter(sm => sm.id !== 8) // Exclude Krystilia .sort((a, b) => b.basePoints - a.basePoints) .find(sm => userCanUseMaster(user, sm)); @@ -277,11 +281,13 @@ export async function slayerNewTaskCommand({ interactionReply(interaction, 'You cannot skip this task because Turael assigns it.'); return; } + const isUsingKrystilia = Boolean(currentTask?.slayer_master_id === 8); + const taskStreakKey = isUsingKrystilia ? 'slayer_wildy_task_streak' : 'slayer_task_streak'; + const warning = `Really cancel task? This will reset your${ + isUsingKrystilia ? ' wilderness' : '' + } streak to 0 and give you a new ${slayerMaster.name} task.`; - await handleMahojiConfirmation( - interaction, - `Really cancel task? This will reset your streak to 0 and give you a new ${slayerMaster.name} task.` - ); + await handleMahojiConfirmation(interaction, warning); await prisma.slayerTask.update({ where: { id: currentTask.id @@ -291,7 +297,8 @@ export async function slayerNewTaskCommand({ quantity_remaining: 0 } }); - await userStatsUpdate(user.id, { slayer_task_streak: 0 }, {}); + await userStatsUpdate(user.id, { [taskStreakKey]: 0 }, {}); + const newSlayerTask = await assignNewSlayerTask(user, slayerMaster); let commonName = getCommonTaskName(newSlayerTask.assignedTask!.monster); const returnMessage = diff --git a/src/mahoji/mahojiSettings.ts b/src/mahoji/mahojiSettings.ts index 7c3e06fd8ff..8b8892da5f3 100644 --- a/src/mahoji/mahojiSettings.ts +++ b/src/mahoji/mahojiSettings.ts @@ -269,7 +269,7 @@ export function hasMonsterRequirements(user: MUser, monster: KillableMonster) { return [true]; } -export function resolveAvailableItemBoosts(user: MUser, monster: KillableMonster) { +export function resolveAvailableItemBoosts(user: MUser, monster: KillableMonster, isInWilderness: boolean = false) { const boosts = new Bank(); if (monster.itemInBankBoosts) { for (const boostSet of monster.itemInBankBoosts) { @@ -279,7 +279,7 @@ export function resolveAvailableItemBoosts(user: MUser, monster: KillableMonster // find the highest boost that the player has for (const [itemID, boostAmount] of Object.entries(boostSet)) { const parsedId = parseInt(itemID); - if (monster.wildy ? !user.hasEquipped(parsedId) : !user.hasEquippedOrInBank(parsedId)) { + if (isInWilderness ? !user.hasEquipped(parsedId) : !user.hasEquippedOrInBank(parsedId)) { continue; } if (boostAmount > highestBoostAmount) { diff --git a/src/tasks/minions/monsterActivity.ts b/src/tasks/minions/monsterActivity.ts index e84d510d32e..ae8f9fded9c 100644 --- a/src/tasks/minions/monsterActivity.ts +++ b/src/tasks/minions/monsterActivity.ts @@ -34,7 +34,8 @@ export const monsterTask: MinionTask = { burstOrBarrage, died, pkEncounters, - hasWildySupplies + hasWildySupplies, + isInWilderness } = data; const monster = killableMonsters.find(mon => mon.id === monsterID)!; @@ -199,7 +200,12 @@ export const monsterTask: MinionTask = { const isOnTaskResult = await isOnSlayerTask({ user, monsterID, quantityKilled: quantity }); const superiorTable = isOnTaskResult.hasSuperiorsUnlocked && monster.superior ? monster.superior : undefined; - const isInCatacombs = !usingCannon ? monster.existsInCatacombs ?? undefined : undefined; + const isInCatacombs = (!usingCannon ? monster.existsInCatacombs ?? undefined : undefined) && !isInWilderness; + + let hasRingOfWealthI = user.gear.wildy.hasEquipped('Ring of wealth (i)') && isInWilderness; + if (hasRingOfWealthI) { + messages.push('\nYour clue scroll chance is doubled due to wearing a Ring of Wealth (i).'); + } const killOptions: MonsterKillOptions = { onSlayerTask: isOnTaskResult.isOnTask, @@ -207,22 +213,37 @@ export const monsterTask: MinionTask = { hasSuperiors: superiorTable, inCatacombs: isInCatacombs, lootTableOptions: { - tertiaryItemPercentageChanges: user.buildCATertiaryItemChanges() + tertiaryItemPercentageChanges: user.buildTertiaryItemChanges( + hasRingOfWealthI, + isInWilderness, + isOnTaskResult.isOnTask + ) } }; // Calculate superiors and assign loot. let newSuperiorCount = 0; + if (superiorTable && isOnTaskResult.isOnTask) { - let superiorDroprate = 200; - if (user.hasCompletedCATier('elite')) { - superiorDroprate = 150; - messages.push(`${Emoji.CombatAchievements} 25% more common superiors due to Elite CA tier`); - } - for (let i = 0; i < quantity; i++) { - if (roll(superiorDroprate)) newSuperiorCount++; + if (!(isInWilderness && monster.name === 'Bloodveld')) { + let superiorDroprate = 200; + if (isInWilderness) { + superiorDroprate *= 0.9; + messages.push('\n10% more common superiors due to Wilderness Slayer.'); + } + if (user.hasCompletedCATier('elite')) { + superiorDroprate *= 0.75; + messages.push(`\n${Emoji.CombatAchievements} 25% more common superiors due to Elite CA tier.`); + } + + for (let i = 0; i < quantity; i++) { + if (roll(superiorDroprate)) { + newSuperiorCount++; + } + } } } + // Regular loot const finalQuantity = quantity - newSuperiorCount; const loot = monster.table.kill(finalQuantity, killOptions); @@ -233,6 +254,16 @@ export const monsterTask: MinionTask = { // Superior loot and totems if in catacombs loot.add(superiorTable!.kill(newSuperiorCount)); if (isInCatacombs) loot.add('Dark totem base', newSuperiorCount); + if (isInWilderness) loot.add("Larran's key", newSuperiorCount); + } + + // Hill giant key wildy buff + if (isInWilderness && monster.name === 'Hill giant') { + for (let i = 0; i < quantity; i++) { + if (roll(128)) { + loot.add('Giant key'); + } + } } const xpRes: string[] = []; @@ -303,9 +334,32 @@ export const monsterTask: MinionTask = { : quantitySlayed; const quantityLeft = Math.max(0, isOnTaskResult.currentTask!.quantity_remaining - effectiveSlayed); + const isUsingKrystilia = isOnTaskResult.slayerMaster.id === 8; thisTripFinishesTask = quantityLeft === 0; - if (thisTripFinishesTask) { + if (thisTripFinishesTask && isUsingKrystilia) { + const newStats = await userStatsUpdate( + user.id, + { + slayer_wildy_task_streak: { + increment: 1 + } + }, + { slayer_wildy_task_streak: true } + ); + const currentStreak = newStats.slayer_wildy_task_streak; + const points = await calculateSlayerPoints(currentStreak, isOnTaskResult.slayerMaster, user); + const secondNewUser = await user.update({ + slayer_points: { + increment: points + } + }); + str += `\n**You've completed ${currentStreak} wilderness tasks and received ${points} points; giving you a total of ${secondNewUser.newUser.slayer_points}; return to a Slayer master.**`; + if (isOnTaskResult.assignedTask.isBoss) { + str += ` ${await user.addXP({ skillName: SkillsEnum.Slayer, amount: 5000, minimal: true })}`; + str += ' for completing your boss task.'; + } + } else if (thisTripFinishesTask) { const newStats = await userStatsUpdate( user.id, { diff --git a/src/tasks/minions/pickpocketActivity.ts b/src/tasks/minions/pickpocketActivity.ts index 3d0185e4a1f..bdd5f3b72d7 100644 --- a/src/tasks/minions/pickpocketActivity.ts +++ b/src/tasks/minions/pickpocketActivity.ts @@ -60,7 +60,7 @@ export const pickpocketTask: MinionTask = { if (obj.type === 'pickpockable') { for (let i = 0; i < successfulQuantity; i++) { const lootItems = obj.table.roll(1, { - tertiaryItemPercentageChanges: user.buildCATertiaryItemChanges() + tertiaryItemPercentageChanges: user.buildTertiaryItemChanges() }); // TODO: Remove Rocky from loot tables in oldschoolJS if (lootItems.has('Rocky')) lootItems.remove('Rocky'); diff --git a/tests/unit/snapshots/banksnapshots.test.ts.snap b/tests/unit/snapshots/banksnapshots.test.ts.snap index 62046ac5bcb..ace8c2280d5 100644 --- a/tests/unit/snapshots/banksnapshots.test.ts.snap +++ b/tests/unit/snapshots/banksnapshots.test.ts.snap @@ -903,6 +903,20 @@ exports[`OSB Buyables 1`] = ` "outputItems": undefined, "qpRequired": 172, }, + { + "gpCost": 5000, + "ironmanPrice": 500, + "itemCost": Bank { + "bank": {}, + "frozen": false, + }, + "name": "Lockpick", + "outputItems": undefined, + "skillsNeeded": { + "agility": 50, + "thieving": 50, + }, + }, { "itemCost": Bank { "bank": { @@ -8463,6 +8477,24 @@ exports[`OSB Creatables 1`] = ` "frozen": false, }, }, + { + "GPCost": 50000, + "cantHaveItems": undefined, + "inputItems": Bank { + "bank": { + "12783": 1, + "2572": 1, + }, + "frozen": false, + }, + "name": "Ring of wealth (i)", + "outputItems": Bank { + "bank": { + "12785": 1, + }, + "frozen": false, + }, + }, { "cantHaveItems": undefined, "inputItems": Bank { diff --git a/tests/unit/snapshots/clsnapshots.test.ts.snap b/tests/unit/snapshots/clsnapshots.test.ts.snap index a3b217fd3a4..28191f3d0b5 100644 --- a/tests/unit/snapshots/clsnapshots.test.ts.snap +++ b/tests/unit/snapshots/clsnapshots.test.ts.snap @@ -25,7 +25,7 @@ Chompy Birds (19) Commander Zilyana (8) Corporeal Beast (7) Crazy archaeologist (3) -Creatables (661) +Creatables (662) Creature Creation (7) Cyclopes (8) Dagannoth Kings (10) @@ -87,7 +87,7 @@ Shooting Stars (2) Skilling (41) Skilling Pets (8) Skotizo (6) -Slayer (70) +Slayer (73) Soul Wars (3) Temple Trekking (4) Tempoross (12) @@ -545,6 +545,9 @@ Cursed phalanx Curved bone Cutthroat flag Cyclops head +Dagon'hai hat +Dagon'hai robe bottom +Dagon'hai robe top Dark acorn Dark bow Dark bow tie diff --git a/tests/unit/snapshots/images-test-ts-tests-unit-images-test-ts-images-collection-log-1-snap.png b/tests/unit/snapshots/images-test-ts-tests-unit-images-test-ts-images-collection-log-1-snap.png index 2d5206adb2a244c317e1ee21cfe782b38111f5ea..7ad554eedee9ca28c715c8b654d31fd74454cc69 100644 GIT binary patch delta 46968 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

D-(R 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

$f&Atp0InDqPP_wOog#*!hHVr&c;I1)z{R@o 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 From 4c58e317095b353b331c15239b21160a13f6dbae Mon Sep 17 00:00:00 2001 From: themrrobert <10122432+themrrobert@users.noreply.github.com> Date: Mon, 13 May 2024 05:08:02 -0700 Subject: [PATCH 005/249] Fix recently killed monster list (#5876) --- src/mahoji/commands/k.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/mahoji/commands/k.ts b/src/mahoji/commands/k.ts index 55245b143fb..ba1a2254581 100644 --- a/src/mahoji/commands/k.ts +++ b/src/mahoji/commands/k.ts @@ -39,16 +39,18 @@ export const autocompleteMonsters = [ ]; async function fetchUsersRecentlyKilledMonsters(userID: string) { - const res = await prisma.$queryRawUnsafe<{ mon_id: string }[]>( - `SELECT DISTINCT((data->>'monsterID')) AS mon_id + const res = await prisma.$queryRawUnsafe<{ mon_id: string; last_killed: Date }[]>( + `SELECT DISTINCT((data->>'monsterID')) AS mon_id, MAX(start_date) as last_killed FROM activity WHERE user_id = $1 AND type = 'MonsterKilling' AND finish_date > now() - INTERVAL '31 days' +GROUP BY 1 +ORDER BY 2 DESC LIMIT 10;`, BigInt(userID) ); - return new Set(res.map(i => Number(i.mon_id))); + return res.map(i => Number(i.mon_id)); } export const minionKCommand: OSBMahojiCommand = { @@ -73,15 +75,17 @@ export const minionKCommand: OSBMahojiCommand = { : [m.name.toLowerCase(), ...m.aliases].some(str => str.includes(value.toLowerCase())) ) .sort((a, b) => { - const hasA = recentlyKilled.has(a.id); - const hasB = recentlyKilled.has(b.id); - if (hasA && hasB) return 0; + const hasA = recentlyKilled.includes(a.id); + const hasB = recentlyKilled.includes(b.id); + if (hasA && hasB) { + return recentlyKilled.indexOf(a.id) < recentlyKilled.indexOf(b.id) ? -1 : 1; + } if (hasA) return -1; if (hasB) return 1; return 0; }) .map(i => ({ - name: `${i.name}${recentlyKilled.has(i.id) ? ' (Recently killed)' : ''}`, + name: `${i.name}${recentlyKilled.includes(i.id) ? ' (Recently killed)' : ''}`, value: i.name })); } From 09f719190161b59fd19b3530425694ef285f7319 Mon Sep 17 00:00:00 2001 From: TastyPumPum <79149170+TastyPumPum@users.noreply.github.com> Date: Tue, 14 May 2024 16:31:03 +0100 Subject: [PATCH 006/249] Add Shayzien Combat Ring (#5840) Since the removal of favour, users are unable to get the Shayzien armour that gives a boost to Lizardman Shamans. This pr copies how stronghold of security has been setup and adds the shayzien minigame to unlock the armour. --- prisma/schema.prisma | 1 + src/lib/Task.ts | 2 + src/lib/types/minions.ts | 3 +- src/lib/util/minionStatus.ts | 5 ++ src/lib/util/repeatStoredTrip.ts | 7 ++- .../abstracted_commands/combatRingCommand.ts | 20 +++++++ .../otherActivitiesCommand.ts | 9 +++ src/tasks/minions/combatRingActivity.ts | 55 +++++++++++++++++++ 8 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 src/mahoji/lib/abstracted_commands/combatRingCommand.ts create mode 100644 src/tasks/minions/combatRingActivity.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ed04391c575..50299c06586 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -940,6 +940,7 @@ enum activity_type_enum { TombsOfAmascut UnderwaterAgilityThieving StrongholdOfSecurity + CombatRing SpecificQuest CamdozaalFishing CamdozaalMining diff --git a/src/lib/Task.ts b/src/lib/Task.ts index a6cbefcc1ca..2436441b742 100644 --- a/src/lib/Task.ts +++ b/src/lib/Task.ts @@ -11,6 +11,7 @@ import { camdozaalSmithingTask } from '../tasks/minions/camdozaalActivity/camdoz import { castingTask } from '../tasks/minions/castingActivity'; import { clueTask } from '../tasks/minions/clueActivity'; import { collectingTask } from '../tasks/minions/collectingActivity'; +import { combatRingTask } from '../tasks/minions/combatRingActivity'; import { constructionTask } from '../tasks/minions/constructionActivity'; import { cookingTask } from '../tasks/minions/cookingActivity'; import { craftingTask } from '../tasks/minions/craftingActivity'; @@ -178,6 +179,7 @@ export const tasks: MinionTask[] = [ toaTask, underwaterAgilityThievingTask, strongholdTask, + combatRingTask, specificQuestTask, camdozaalMiningTask, camdozaalSmithingTask, diff --git a/src/lib/types/minions.ts b/src/lib/types/minions.ts index b342e223e74..42b6b8512f0 100644 --- a/src/lib/types/minions.ts +++ b/src/lib/types/minions.ts @@ -45,7 +45,8 @@ export interface ActivityTaskOptionsWithNoChanges extends ActivityTaskOptions { | 'Easter' | 'ShootingStars' | 'HalloweenEvent' - | 'StrongholdOfSecurity'; + | 'StrongholdOfSecurity' + | 'CombatRing'; } export interface ActivityTaskOptionsWithQuantity extends ActivityTaskOptions { diff --git a/src/lib/util/minionStatus.ts b/src/lib/util/minionStatus.ts index 318514ae593..4dc7f1bb8c7 100644 --- a/src/lib/util/minionStatus.ts +++ b/src/lib/util/minionStatus.ts @@ -670,6 +670,11 @@ export function minionStatus(user: MUser) { durationRemaining )}.`; } + case 'CombatRing': { + return `${name} is currently fighting in the Combat Ring! The trip should take ${formatDuration( + durationRemaining + )}.`; + } case 'SpecificQuest': { const data = currentTask as SpecificQuestOptions; return `${name} is currently doing the ${ diff --git a/src/lib/util/repeatStoredTrip.ts b/src/lib/util/repeatStoredTrip.ts index 6abbc323d09..70b24847d56 100644 --- a/src/lib/util/repeatStoredTrip.ts +++ b/src/lib/util/repeatStoredTrip.ts @@ -76,7 +76,8 @@ export const taskCanBeRepeated = (activity: Activity) => { activity_type_enum.Easter, activity_type_enum.TokkulShop, activity_type_enum.Birdhouse, - activity_type_enum.StrongholdOfSecurity + activity_type_enum.StrongholdOfSecurity, + activity_type_enum.CombatRing ] as activity_type_enum[] ).includes(activity.type); }; @@ -102,6 +103,10 @@ export const tripHandlers = { commandName: 'm', args: () => ({}) }, + [activity_type_enum.CombatRing]: { + commandName: 'm', + args: () => ({}) + }, [activity_type_enum.TearsOfGuthix]: { commandName: 'm', args: () => ({}) diff --git a/src/mahoji/lib/abstracted_commands/combatRingCommand.ts b/src/mahoji/lib/abstracted_commands/combatRingCommand.ts new file mode 100644 index 00000000000..f1061e764b8 --- /dev/null +++ b/src/mahoji/lib/abstracted_commands/combatRingCommand.ts @@ -0,0 +1,20 @@ +import { Time } from 'e'; + +import type { ActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; +import { randomVariation } from '../../../lib/util'; +import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; + +export async function combatRingCommand(user: MUser, channelID: string) { + if (user.minionIsBusy) { + return 'Your minion is busy.'; + } + + await addSubTaskToActivityTask({ + userID: user.id, + channelID: channelID.toString(), + duration: randomVariation(Time.Minute * 5, 5), + type: 'CombatRing' + }); + + return `${user.minionName} is now fighting in the Shayzien Combat Ring!`; +} diff --git a/src/mahoji/lib/abstracted_commands/otherActivitiesCommand.ts b/src/mahoji/lib/abstracted_commands/otherActivitiesCommand.ts index bdc394e9f9a..199fa14ed65 100644 --- a/src/mahoji/lib/abstracted_commands/otherActivitiesCommand.ts +++ b/src/mahoji/lib/abstracted_commands/otherActivitiesCommand.ts @@ -1,6 +1,7 @@ import { activity_type_enum } from '@prisma/client'; import { championsChallengeCommand } from './championsChallenge'; +import { combatRingCommand } from './combatRingCommand'; import { strongHoldOfSecurityCommand } from './strongHoldOfSecurityCommand'; export const otherActivities = [ @@ -13,6 +14,11 @@ export const otherActivities = [ name: 'Stronghold of Security', command: championsChallengeCommand, type: activity_type_enum.StrongholdOfSecurity + }, + { + name: 'Combat Ring (Shayzien)', + command: combatRingCommand, + type: activity_type_enum.CombatRing } ]; @@ -23,5 +29,8 @@ export function otherActivitiesCommand(type: string, user: MUser, channelID: str if (type === 'StrongholdOfSecurity') { return strongHoldOfSecurityCommand(user, channelID); } + if (type === 'CombatRing') { + return combatRingCommand(user, channelID); + } return 'Invalid activity type.'; } diff --git a/src/tasks/minions/combatRingActivity.ts b/src/tasks/minions/combatRingActivity.ts new file mode 100644 index 00000000000..2e202970f82 --- /dev/null +++ b/src/tasks/minions/combatRingActivity.ts @@ -0,0 +1,55 @@ +import { Bank } from 'oldschooljs'; + +import { ActivityTaskOptionsWithNoChanges } from '../../lib/types/minions'; +import { handleTripFinish } from '../../lib/util/handleTripFinish'; + +export const combatRingTask: MinionTask = { + type: 'CombatRing', + async run(data: ActivityTaskOptionsWithNoChanges) { + const { channelID, userID } = data; + const user = await mUserFetch(userID); + + const loot = new Bank({ + 'Shayzien boots (1)': 1, + 'Shayzien gloves (1)': 1, + 'Shayzien greaves (1)': 1, + 'Shayzien helm (1)': 1, + 'Shayzien platebody (1)': 1, + 'Shayzien boots (2)': 1, + 'Shayzien gloves (2)': 1, + 'Shayzien greaves (2)': 1, + 'Shayzien helm (2)': 1, + 'Shayzien platebody (2)': 1, + 'Shayzien boots (3)': 1, + 'Shayzien gloves (3)': 1, + 'Shayzien greaves (3)': 1, + 'Shayzien helm (3)': 1, + 'Shayzien platebody (3)': 1, + 'Shayzien boots (4)': 1, + 'Shayzien gloves (4)': 1, + 'Shayzien greaves (4)': 1, + 'Shayzien helm (4)': 1, + 'Shayzien platebody (4)': 1, + 'Shayzien boots (5)': 1, + 'Shayzien gloves (5)': 1, + 'Shayzien greaves (5)': 1, + 'Shayzien helm (5)': 1, + 'Shayzien body (5)': 1 + }); + + await transactItems({ + userID: user.id, + collectionLog: true, + itemsToAdd: loot + }); + + handleTripFinish( + user, + channelID, + `${user}, ${user.minionName} finished the Combat Ring, and received ${loot}.`, + undefined, + data, + loot + ); + } +}; From 9d32e4addd7ab068cf496e771c4d811bee5be8d5 Mon Sep 17 00:00:00 2001 From: themrrobert <10122432+themrrobert@users.noreply.github.com> Date: Tue, 14 May 2024 21:23:46 -0700 Subject: [PATCH 007/249] QoL for Farming Contracts (#5879) --- src/lib/util.ts | 6 ++++- .../farmingContractCommand.ts | 27 +++++++++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/lib/util.ts b/src/lib/util.ts index 61871e3db63..40cc56b3aa7 100644 --- a/src/lib/util.ts +++ b/src/lib/util.ts @@ -391,12 +391,16 @@ export function roughMergeMahojiResponse( ): InteractionReplyOptions { const first = normalizeMahojiResponse(one); const second = normalizeMahojiResponse(two); + const newContent: string[] = []; + const newResponse: InteractionReplyOptions = { content: '', files: [], components: [] }; for (const res of [first, second]) { - if (res.content) newResponse.content += `${res.content} `; + if (res.content) newContent.push(res.content); if (res.files) newResponse.files = [...newResponse.files!, ...res.files]; if (res.components) newResponse.components = res.components; } + newResponse.content = newContent.join('\n\n'); + return newResponse; } diff --git a/src/mahoji/lib/abstracted_commands/farmingContractCommand.ts b/src/mahoji/lib/abstracted_commands/farmingContractCommand.ts index 51f2df322e8..e2b4f7834b7 100644 --- a/src/mahoji/lib/abstracted_commands/farmingContractCommand.ts +++ b/src/mahoji/lib/abstracted_commands/farmingContractCommand.ts @@ -32,6 +32,13 @@ export async function farmingContractCommand(userID: string, input?: ContractOpt const plant = currentContract.hasContract ? findPlant(currentContract.plantToGrow) : null; if (!input) { + if (!currentContract.hasContract) { + const bestTier = bestFarmingContractUserCanDo(user); + if (bestTier !== undefined) { + return farmingContractCommand(user.id, bestTier); + } + } + return `You have completed a total of ${currentContract.contractsCompleted} Farming Contracts! **Current Contract:** ${plant ? `${plant.name} - ${currentContract.difficultyLevel}` : 'None'}`; } @@ -150,11 +157,17 @@ export async function canRunAutoContract(user: MUser) { // If the patch we're contracted to is ready, we can auto contract const contractedPatch = farmingDetails.patchesDetailed.find( - p => p.patchName === plants.find(p => p.name === contract.plantToGrow)?.seedType + p => p.patchName === plants.find(pl => pl.name === contract.plantToGrow)?.seedType ); return contractedPatch?.ready; } +export function bestFarmingContractUserCanDo(user: MUser) { + return Object.entries(contractToFarmingLevel) + .sort((a, b) => b[1] - a[1]) + .find(a => user.skillLevel('farming') >= a[1])?.[0] as ContractOption | undefined; +} + export async function autoContract(user: MUser, channelID: string, userID: string): CommandResponse { const [farmingDetails, mahojiUser] = await Promise.all([ getFarmingInfo(userID), @@ -163,9 +176,7 @@ export async function autoContract(user: MUser, channelID: string, userID: strin const contract = mahojiUser.minion_farmingContract as FarmingContract | null; const plant = contract?.hasContract ? findPlant(contract?.plantToGrow) : null; const patch = farmingDetails.patchesDetailed.find(p => p.plant === plant); - const bestContractTierCanDo = Object.entries(contractToFarmingLevel) - .sort((a, b) => b[1] - a[1]) - .find(a => user.skillLevel('farming') >= a[1])?.[0] as ContractOption | undefined; + const bestContractTierCanDo = bestFarmingContractUserCanDo(user); if (user.owns('Seed pack')) { const openResponse = await abstractedOpenCommand(null, user.id, ['seed pack'], 'auto'); @@ -177,11 +188,11 @@ export async function autoContract(user: MUser, channelID: string, userID: strin if (!contract || !contract.hasContract) { const contractResult = await farmingContractCommand(userID, bestContractTierCanDo); const newUser = await mahojiUsersSettingsFetch(userID, { minion_farmingContract: true }); - const contract = newUser.minion_farmingContract as FarmingContract | null; - if (!contract || !contract.plantToGrow) return contractResult; + const newContract = (newUser.minion_farmingContract ?? defaultFarmingContract) as FarmingContract; + if (!newContract.hasContract || !newContract.plantToGrow) return contractResult; return farmingPlantCommand({ userID: user.id, - plantName: contract.plantToGrow, + plantName: newContract.plantToGrow, pay: false, autoFarmed: false, quantity: null, @@ -201,7 +212,7 @@ export async function autoContract(user: MUser, channelID: string, userID: strin }); } - // If they have a contract, and its planted, and its ready, harvest it. + // If they have a contract, and its planted, and it's ready, harvest it. if (patch.ready) { return harvestCommand({ user, channelID, seedType: patch.patchName }); } From 0b84ca8e7dcd8fc380b617a85c8ecd01daf5874a Mon Sep 17 00:00:00 2001 From: themrrobert <10122432+themrrobert@users.noreply.github.com> Date: Mon, 20 May 2024 02:12:35 -0700 Subject: [PATCH 008/249] Fix broken Lamp command from missing item (#5890) --- src/mahoji/lib/abstracted_commands/lampCommand.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mahoji/lib/abstracted_commands/lampCommand.ts b/src/mahoji/lib/abstracted_commands/lampCommand.ts index 130eb98eb7c..f504ab57c5f 100644 --- a/src/mahoji/lib/abstracted_commands/lampCommand.ts +++ b/src/mahoji/lib/abstracted_commands/lampCommand.ts @@ -84,12 +84,13 @@ export const XPLamps: IXPLamp[] = [ minimumLevel: 1, allowedSkills: [SkillsEnum.Magic] }, + /* Needs OSJS Update { itemID: 28_820, amount: 5000, name: 'Antique lamp (defender of varrock)', minimumLevel: 1 - }, + },*/ { itemID: itemID('Antique lamp (easy ca)'), amount: 5000, From f37ca941db3577e6d61fee95d5216aa124c9cc70 Mon Sep 17 00:00:00 2001 From: gc <30398469+gc@users.noreply.github.com> Date: Sun, 26 May 2024 16:41:02 +1000 Subject: [PATCH 009/249] Improve ge view command --- src/lib/grandExchange.ts | 6 +++++- src/mahoji/commands/ge.ts | 28 +++++++++++++++++++--------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/lib/grandExchange.ts b/src/lib/grandExchange.ts index 7a8ca642200..ab3f7441be5 100644 --- a/src/lib/grandExchange.ts +++ b/src/lib/grandExchange.ts @@ -229,6 +229,10 @@ class GrandExchangeSingleton { this.locked = true; } + getItemBuyLimit(item: Item) { + return item.buy_limit ?? this.config.buyLimit.fallbackBuyLimit(item); + } + async checkBuyLimitForListing(geListing: GEListing) { const interval = this.getInterval(); @@ -248,7 +252,7 @@ class GrandExchangeSingleton { for (const tx of allActiveListingsInTimePeriod) sanityCheckTransaction(tx); const item = getOSItem(geListing.item_id); - const buyLimit = item.buy_limit ?? this.config.buyLimit.fallbackBuyLimit(item); + const buyLimit = this.getItemBuyLimit(item); const totalSold = sumArr(allActiveListingsInTimePeriod.map(listing => listing.quantity_bought)); const remainingItemsCanBuy = Math.max(0, buyLimit - totalSold); diff --git a/src/mahoji/commands/ge.ts b/src/mahoji/commands/ge.ts index fc6e6f7726a..1336716b9dc 100644 --- a/src/mahoji/commands/ge.ts +++ b/src/mahoji/commands/ge.ts @@ -19,7 +19,6 @@ import itemIsTradeable from '../../lib/util/itemIsTradeable'; import { cancelGEListingCommand } from '../lib/abstracted_commands/cancelGEListingCommand'; import { itemOption, ownedItemOption, tradeableItemArr } from '../lib/mahojiCommandOptions'; import { OSBMahojiCommand } from '../lib/util'; -import { patronMsg } from '../mahojiSettings'; export type GEListingWithTransactions = GEListing & { buyTransactions: GETransaction[]; @@ -210,12 +209,12 @@ export const geCommand: OSBMahojiCommand = { { type: ApplicationCommandOptionType.Subcommand, name: 'view', - description: 'Lookup the market price of an item on the g.e', + description: 'Lookup information about an item on the g.e', options: [ { ...itemOption(item => Boolean(item.tradeable_on_ge)), - name: 'price_history', - description: 'View the price history of an item.' + name: 'item', + description: 'The item to view.' } ] } @@ -243,7 +242,7 @@ export const geCommand: OSBMahojiCommand = { }; stats?: {}; price?: { item: string }; - view?: { price_history?: string }; + view?: { item?: string }; }>) => { await deferInteraction(interaction); const user = await mUserFetch(userID); @@ -384,12 +383,22 @@ The next buy limit reset is at: ${GrandExchange.getInterval().nextResetStr}, it } if (options.view) { - if (options.view.price_history) { - const item = getItem(options.view.price_history); + if (options.view.item) { + const item = getItem(options.view.item); if (!item) return 'Invalid item.'; if (!itemIsTradeable(item.id)) return 'That item is not tradeable on the Grand Exchange.'; + const priceData = marketPricemap.get(item.id); + let baseMessage = `**${item.name}** +**Buy Limit Per 4 hours:** ${GrandExchange.getItemBuyLimit(item).toLocaleString()}`; + if (priceData) { + baseMessage += ` +**Market Price:** ${toKMB(priceData.guidePrice)} (${priceData.guidePrice.toLocaleString()}) GP. +**Recent Price:** ${toKMB(priceData.averagePriceLast100)} (${priceData.averagePriceLast100.toLocaleString()}) GP. + +`; + } if (user.perkTier() < PerkTier.Four) { - return patronMsg(PerkTier.Four); + return baseMessage; } let result = await prisma.$queryRawUnsafe< { total_quantity_bought: number; average_price_per_item_before_tax: number; week: Date }[] @@ -412,7 +421,7 @@ GROUP BY ORDER BY week ASC; `); - if (result.length < 1) return 'No price history found for that item.'; + if (result.length < 1) return baseMessage; if (result[0].average_price_per_item_before_tax <= 1_000_000) { result = result.filter(i => i.total_quantity_bought > 1); } @@ -424,6 +433,7 @@ ORDER BY false ); return { + content: baseMessage, files: [buffer] }; } From c5ffb35199545c6eb3753ce21f391d2b2079efcd Mon Sep 17 00:00:00 2001 From: themrrobert <10122432+themrrobert@users.noreply.github.com> Date: Mon, 27 May 2024 02:07:17 -0700 Subject: [PATCH 010/249] Optimize remote-item script (#5899) --- src/scripts/remove-item.ts | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/scripts/remove-item.ts b/src/scripts/remove-item.ts index 705f6124be3..9901f6b735c 100644 --- a/src/scripts/remove-item.ts +++ b/src/scripts/remove-item.ts @@ -8,17 +8,6 @@ import { GearSetupTypes } from '../lib/gear/types'; /* PSQL Function that needs to be created */ const extraFunctions = ` -CREATE OR REPLACE FUNCTION remove_jsonb_keys(data jsonb, keys text[]) -RETURNS jsonb LANGUAGE plpgsql AS $$ -declare - key text; -BEGIN - FOREACH key IN ARRAY keys LOOP - data := data - key; - END LOOP; - RETURN data; -END; -$$; CREATE OR REPLACE FUNCTION array_remove_multiple(original_array anyarray, values_to_remove anyarray) RETURNS anyarray AS $$ BEGIN @@ -49,7 +38,7 @@ FINAL_QUERY += GearSetupTypes.map(gearType => .flat() .join('\n'); -const removeFromBankQuery = (column: string) => `"${column}" = remove_jsonb_keys("${column}"::jsonb, ${arrayToRemove})`; +const removeFromBankQuery = (column: string) => `"${column}" = "${column}"::jsonb - ${arrayToRemove}`; const removeFromArrayQuery = (column: string) => `"${column}" = array_remove_multiple("${column}", ${intArrayToRemove})`; From 9acdc8bcc375878fb29729bda6e4e6c84daa5264 Mon Sep 17 00:00:00 2001 From: themrrobert <10122432+themrrobert@users.noreply.github.com> Date: Mon, 10 Jun 2024 22:43:48 -0700 Subject: [PATCH 011/249] Fix a bug in migrate user script (disallow src = dst) (#5915) --- src/mahoji/commands/rp.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/mahoji/commands/rp.ts b/src/mahoji/commands/rp.ts index c840f4fb578..8eefaf486ff 100644 --- a/src/mahoji/commands/rp.ts +++ b/src/mahoji/commands/rp.ts @@ -52,7 +52,8 @@ const itemFilters = [ ]; function isProtectedAccount(user: MUser) { - if ([...ADMIN_IDS, ...OWNER_IDS].includes(user.id)) return true; + const botAccounts = ['303730326692429825', '729244028989603850', '969542224058654790']; + if ([...ADMIN_IDS, ...OWNER_IDS, ...botAccounts].includes(user.id)) return true; if ([BitField.isModerator, BitField.isContributor].some(bf => user.bitfield.includes(bf))) return true; return false; } @@ -809,7 +810,12 @@ ORDER BY item_id ASC;`); if (!isOwner && !isAdmin) { return randArrItem(gifs); } + const { source, dest, reason } = options.player.migrate_user; + + if (source.user.id === dest.user.id) { + return 'Destination cannot be the same as the source!'; + } const sourceUser = await mUserFetch(source.user.id); const destUser = await mUserFetch(dest.user.id); From 8b655e172ca53a479cf84b368135935dd7a7438c Mon Sep 17 00:00:00 2001 From: themrrobert <10122432+themrrobert@users.noreply.github.com> Date: Mon, 10 Jun 2024 23:17:32 -0700 Subject: [PATCH 012/249] Add src != dest user check to the migrateUser function (#5916) --- src/lib/util/migrateUser.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/util/migrateUser.ts b/src/lib/util/migrateUser.ts index 58ad89de506..dbdeea7f124 100644 --- a/src/lib/util/migrateUser.ts +++ b/src/lib/util/migrateUser.ts @@ -8,6 +8,10 @@ export async function migrateUser(_source: string | MUser, _dest: string | MUser const sourceUser = typeof _source === 'string' ? await mUserFetch(_source) : _source; const destUser = typeof _dest === 'string' ? await mUserFetch(_dest) : _dest; + if (sourceUser.id === destUser.id) { + throw new UserError('Destination user cannot be the same as the source!'); + } + // First check for + cancel active GE Listings: await Promise.all([cancelUsersListings(sourceUser), cancelUsersListings(destUser)]); From e3353e9b3449ae59b19ff47062b559ae4d6409fb Mon Sep 17 00:00:00 2001 From: nwjgit <69014816+nwjgit@users.noreply.github.com> Date: Thu, 13 Jun 2024 08:41:03 -0500 Subject: [PATCH 013/249] Allow creating divine water with jogre bones (#5907) --- src/lib/data/creatables/bsoItems.ts | 1 + src/lib/data/creatablesTable.txt | 1 + tests/unit/snapshots/banksnapshots.test.ts.snap | 14 ++++++++++++++ 3 files changed, 16 insertions(+) diff --git a/src/lib/data/creatables/bsoItems.ts b/src/lib/data/creatables/bsoItems.ts index ccf534d2d18..b61538cdbd6 100644 --- a/src/lib/data/creatables/bsoItems.ts +++ b/src/lib/data/creatables/bsoItems.ts @@ -1195,6 +1195,7 @@ const dragonBoneCreatables: Createable[] = [ const divineWaterBones = [ 'Bones', 'Big bones', + 'Jogre bones', 'Babydragon bones', 'Dragon bones', 'Wyrm bones', diff --git a/src/lib/data/creatablesTable.txt b/src/lib/data/creatablesTable.txt index cf16f411594..d43d175b82c 100644 --- a/src/lib/data/creatablesTable.txt +++ b/src/lib/data/creatablesTable.txt @@ -825,6 +825,7 @@ | Divine water | Unknown/Dynamic | 1x Divine water | 0 | | Divine water (Bones) | Unknown/Dynamic | 1x Divine water | 0 | | Divine water (Big bones) | Unknown/Dynamic | 1x Divine water | 0 | +| Divine water (Jogre bones) | Unknown/Dynamic | 1x Divine water | 0 | | Divine water (Babydragon bones) | Unknown/Dynamic | 1x Divine water | 0 | | Divine water (Dragon bones) | Unknown/Dynamic | 1x Divine water | 0 | | Divine water (Wyrm bones) | Unknown/Dynamic | 1x Divine water | 0 | diff --git a/tests/unit/snapshots/banksnapshots.test.ts.snap b/tests/unit/snapshots/banksnapshots.test.ts.snap index 66c1a722a1f..4fac0ec43a9 100644 --- a/tests/unit/snapshots/banksnapshots.test.ts.snap +++ b/tests/unit/snapshots/banksnapshots.test.ts.snap @@ -23825,6 +23825,20 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, }, + { + "cantHaveItems": undefined, + "inputItems": Bank { + "bank": {}, + "frozen": false, + }, + "name": "Divine water (Jogre bones)", + "outputItems": Bank { + "bank": { + "50599": 1, + }, + "frozen": false, + }, + }, { "cantHaveItems": undefined, "inputItems": Bank { From f26e505ed5df825ff06f412f84bd2a0f07bae102 Mon Sep 17 00:00:00 2001 From: nwjgit <69014816+nwjgit@users.noreply.github.com> Date: Thu, 13 Jun 2024 08:50:11 -0500 Subject: [PATCH 014/249] Add minions inferno kc to cl (#5911) --- src/lib/data/Collections.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/data/Collections.ts b/src/lib/data/Collections.ts index e001fc577e0..b72ed565e11 100644 --- a/src/lib/data/Collections.ts +++ b/src/lib/data/Collections.ts @@ -353,6 +353,10 @@ export const allCollectionLogs: ICollection = { fmtProg: kcProg(Monsters.Hespori) }, 'The Inferno': { + kcActivity: { + Default: async (_, minigameScores) => + minigameScores.find(i => i.minigame.column === 'inferno')!.score + }, alias: ['zuk', 'inferno'], items: theInfernoCL, fmtProg: ({ minigames }) => `${minigames.inferno} KC` From de2117f90478d348d4ca3da0cb953df729cec930 Mon Sep 17 00:00:00 2001 From: nwjgit <69014816+nwjgit@users.noreply.github.com> Date: Thu, 13 Jun 2024 08:57:13 -0500 Subject: [PATCH 015/249] Venatrix & Akumu all flag for cl + cl fixes (#5908) --- src/lib/data/Collections.ts | 48 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/lib/data/Collections.ts b/src/lib/data/Collections.ts index 8d3e4d97caf..55e78c1080e 100644 --- a/src/lib/data/Collections.ts +++ b/src/lib/data/Collections.ts @@ -15,6 +15,7 @@ import { implingsCL } from '../implings'; import { inventionCL } from '../invention/inventions'; import { keyCrates } from '../keyCrates'; import killableMonsters, { effectiveMonsters, NightmareMonster } from '../minions/data/killableMonsters'; +import { AkumuLootTable } from '../minions/data/killableMonsters/custom/bosses/Akumu'; import { Ignecarus } from '../minions/data/killableMonsters/custom/bosses/Ignecarus'; import { kalphiteKingLootTable, @@ -24,6 +25,7 @@ import KingGoldemar from '../minions/data/killableMonsters/custom/bosses/KingGol import { MOKTANG_ID, MoktangLootTable } from '../minions/data/killableMonsters/custom/bosses/Moktang'; import { Naxxus, NaxxusLootTableFinishable } from '../minions/data/killableMonsters/custom/bosses/Naxxus'; import { VasaMagus } from '../minions/data/killableMonsters/custom/bosses/VasaMagus'; +import { VenatrixLootTable } from '../minions/data/killableMonsters/custom/bosses/Venatrix'; import { BSOMonsters } from '../minions/data/killableMonsters/custom/customMonsters'; import { sepulchreFloors } from '../minions/data/sepulchre'; import { @@ -311,14 +313,12 @@ export const allCollectionLogs: ICollection = { }, allItems: (() => { return [ - ...new Set( - ...[ - Monsters.GeneralGraardor.allItems, - Monsters.CommanderZilyana.allItems, - Monsters.Kreearra.allItems, - Monsters.KrilTsutsaroth.allItems - ] - ) + ...new Set([ + ...Monsters.GeneralGraardor.allItems, + ...Monsters.CommanderZilyana.allItems, + ...Monsters.Kreearra.allItems, + ...Monsters.KrilTsutsaroth.allItems + ]) ]; })(), items: [ @@ -681,13 +681,13 @@ export const allCollectionLogs: ICollection = { }, Akumu: { alias: ['akumu'], - allItems: akumuCL, + allItems: AkumuLootTable.allItems, items: akumuCL, fmtProg: kcProg(BSOMonsters.Akumu.id) }, Venatrix: { alias: ['venatrix'], - allItems: venatrixCL, + allItems: VenatrixLootTable.allItems, items: venatrixCL, fmtProg: kcProg(BSOMonsters.Venatrix.id) }, @@ -785,21 +785,19 @@ export const allCollectionLogs: ICollection = { }, allItems: (() => { return [ - ...new Set( - ...[ - Monsters.RevenantImp.allItems, - Monsters.RevenantGoblin.allItems, - Monsters.RevenantPyrefiend.allItems, - Monsters.RevenantHobgoblin.allItems, - Monsters.RevenantCyclops.allItems, - Monsters.RevenantHellhound.allItems, - Monsters.RevenantDemon.allItems, - Monsters.RevenantOrk.allItems, - Monsters.RevenantDarkBeast.allItems, - Monsters.RevenantKnight.allItems, - Monsters.RevenantDragon.allItems - ] - ) + ...new Set([ + ...Monsters.RevenantImp.allItems, + ...Monsters.RevenantGoblin.allItems, + ...Monsters.RevenantPyrefiend.allItems, + ...Monsters.RevenantHobgoblin.allItems, + ...Monsters.RevenantCyclops.allItems, + ...Monsters.RevenantHellhound.allItems, + ...Monsters.RevenantDemon.allItems, + ...Monsters.RevenantOrk.allItems, + ...Monsters.RevenantDarkBeast.allItems, + ...Monsters.RevenantKnight.allItems, + ...Monsters.RevenantDragon.allItems + ]) ]; })(), items: revenantsCL From b932c0536b65a3009f976b460c9fda4597a94fdc Mon Sep 17 00:00:00 2001 From: TastyPumPum <79149170+TastyPumPum@users.noreply.github.com> Date: Fri, 14 Jun 2024 10:28:23 +0100 Subject: [PATCH 016/249] Bso wildy slayer (#5877) --- package.json | 2 + prisma/schema.prisma | 9 +- src/lib/MUser.ts | 30 +- src/lib/bankImage.ts | 6 +- src/lib/bossEvents.ts | 41 ++- src/lib/constants.ts | 41 +-- src/lib/data/CollectionsExport.ts | 6 +- src/lib/data/buyables/buyables.ts | 9 + src/lib/data/creatablesTable.txt | 1 + src/lib/data/createables.ts | 6 + src/lib/geImage.ts | 69 +--- .../data/killableMonsters/bosses/wildy.ts | 70 +++- .../data/killableMonsters/chaeldarMonsters.ts | 23 +- .../killableMonsters/krystiliaMonsters.ts | 77 +++- .../data/killableMonsters/mazchnaMonsters.ts | 18 +- .../data/killableMonsters/nieveMonsters.ts | 8 +- src/lib/minions/data/killableMonsters/revs.ts | 2 +- .../data/killableMonsters/turaelMonsters.ts | 37 +- .../data/killableMonsters/vannakaMonsters.ts | 104 ++++-- src/lib/minions/types.ts | 2 + src/lib/openables.ts | 9 + src/lib/settings/prisma.ts | 4 + src/lib/skilling/functions/miningBoosts.ts | 38 +- .../skills/herblore/mixables/barbMixes.ts | 5 +- src/lib/slayer/constants.ts | 3 +- src/lib/slayer/slayerMasters.ts | 9 + src/lib/slayer/slayerUnlocks.ts | 20 + src/lib/slayer/slayerUtil.ts | 41 ++- src/lib/slayer/tasks/bossTasks.ts | 80 +++- src/lib/slayer/tasks/krystiliaTasks.ts | 333 +++++++++++++++++ src/lib/slayer/types.ts | 1 + src/lib/types/minions.ts | 1 + src/lib/util/calcWildyPkChance.ts | 5 +- src/lib/util/repeatStoredTrip.ts | 3 +- src/mahoji/commands/activities.ts | 11 +- src/mahoji/commands/create.ts | 2 +- src/mahoji/commands/k.ts | 43 ++- src/mahoji/commands/kill.ts | 2 +- src/mahoji/commands/leaderboard.ts | 4 +- src/mahoji/commands/mine.ts | 27 +- src/mahoji/commands/minion.ts | 5 +- src/mahoji/commands/offer.ts | 2 +- src/mahoji/commands/rates.ts | 3 +- src/mahoji/commands/testpotato.ts | 6 + .../abstracted_commands/autoSlayCommand.ts | 175 ++++++++- .../lib/abstracted_commands/barbAssault.ts | 2 +- .../lib/abstracted_commands/minionKill.ts | 183 ++++++++-- .../abstracted_commands/slayerTaskCommand.ts | 23 +- src/mahoji/mahojiSettings.ts | 4 +- src/tasks/minions/monsterActivity.ts | 75 +++- src/tasks/minions/pickpocketActivity.ts | 2 +- tests/globalSetup.ts | 26 +- tests/integration/allCommandsBase.test.ts | 342 +++++++++++++++--- tests/integration/grandExchange.test.ts | 14 +- .../integration/memoryHarvesting.bso.test.ts | 20 +- tests/integration/migrateUser.test.ts | 4 - tests/integration/mocks.ts | 41 +++ tests/integration/monsterKilling.bso.test.ts | 15 +- tests/integration/monsterKilling.test.ts | 9 +- tests/integration/setup.ts | 59 ++- tests/integration/trading.test.ts | 4 +- tests/integration/util.ts | 22 +- tests/unit/images.test.ts | 2 +- .../unit/snapshots/banksnapshots.test.ts.snap | 64 +++- tests/unit/snapshots/clsnapshots.test.ts.snap | 7 +- tests/unit/utils.ts | 9 +- vitest.integration.config.mts | 4 +- vitest.unit.config.mts | 5 +- yarn.lock | 53 ++- 69 files changed, 1895 insertions(+), 487 deletions(-) create mode 100644 src/lib/slayer/tasks/krystiliaTasks.ts create mode 100644 tests/integration/mocks.ts diff --git a/package.json b/package.json index 6100e908957..a89ee4856ae 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "@types/jest-image-snapshot": "^6.1.0", "@types/lodash": "^4.14.195", "@types/madge": "^5.0.0", + "@types/mitm": "^1.3.8", "@types/node": "^14.18.12", "@types/node-cron": "^3.0.7", "@types/node-fetch": "^2.6.1", @@ -85,6 +86,7 @@ "eslint-plugin-unicorn": "^44.0.2", "fast-check": "^3.18.0", "jest-image-snapshot": "^6.2.0", + "mitm": "^1.7.2", "madge": "^7.0.0", "prettier": "^2.7.1", "prisma": "^5.13.0", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 6f946311f67..c38c692aca7 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -984,10 +984,11 @@ model UserStats { high_gambles Int @default(0) honour_points Int @default(0) - slayer_task_streak Int @default(0) - slayer_superior_count Int @default(0) - slayer_unsired_offered Int @default(0) - slayer_chewed_offered Int @default(0) + slayer_task_streak Int @default(0) + slayer_wildy_task_streak Int @default(0) + slayer_superior_count Int @default(0) + slayer_unsired_offered Int @default(0) + slayer_chewed_offered Int @default(0) tob_cost Json @default("{}") tob_loot Json @default("{}") diff --git a/src/lib/MUser.ts b/src/lib/MUser.ts index dec37c2ed22..b0a9f20e510 100644 --- a/src/lib/MUser.ts +++ b/src/lib/MUser.ts @@ -901,20 +901,28 @@ Charge your items using ${mentionCommand(globalClient, 'minion', 'charge')}.` return this.caPoints() >= CombatAchievements[tier].rewardThreshold; } - buildCATertiaryItemChanges() { + buildTertiaryItemChanges(hasRingOfWealthI: boolean = false, inWildy: boolean = false, onTask: boolean = false) { const changes = new Map(); - if (this.hasCompletedCATier('easy')) { - changes.set('Clue scroll (easy)', 5); - } - if (this.hasCompletedCATier('medium')) { - changes.set('Clue scroll (medium)', 5); - } - if (this.hasCompletedCATier('hard')) { - changes.set('Clue scroll (hard)', 5); + + const tiers = Object.keys(CombatAchievements) as Array; + for (const tier of tiers) { + let change = hasRingOfWealthI ? 50 : 0; + if (this.hasCompletedCATier(tier)) { + change += 5; + } + changes.set(`Clue scroll (${tier})`, change); } - if (this.hasCompletedCATier('elite')) { - changes.set('Clue scroll (elite)', 5); + + if (inWildy) changes.set('Giant key', 50); + + if (inWildy && !onTask) { + changes.set('Mossy key', 60); + } else if (!inWildy && onTask) { + changes.set('Mossy key', 66.67); + } else if (inWildy && onTask) { + changes.set('Mossy key', 77.6); } + return changes; } diff --git a/src/lib/bankImage.ts b/src/lib/bankImage.ts index 20260b73bf4..619a12825bf 100644 --- a/src/lib/bankImage.ts +++ b/src/lib/bankImage.ts @@ -298,7 +298,7 @@ export const bankFlags = [ ] as const; export type BankFlag = (typeof bankFlags)[number]; -class BankImageTask { +export class BankImageTask { public itemIconsList: Set; public itemIconImagesCache: Map; public backgroundImages: BankBackground[] = []; @@ -855,6 +855,7 @@ class BankImageTask { if (!isTransparent && noBorder !== 1) { this.drawBorder(ctx, bgSprite, bgImage.name === 'Default'); } + await this.drawItems( ctx, compact, @@ -1045,5 +1046,6 @@ declare global { } } } -global.bankImageGenerator = new BankImageTask(); +export const bankImageTask = new BankImageTask(); +global.bankImageGenerator = bankImageTask; bankImageGenerator.init(); diff --git a/src/lib/bossEvents.ts b/src/lib/bossEvents.ts index bd2249642a7..da4c8d74d86 100644 --- a/src/lib/bossEvents.ts +++ b/src/lib/bossEvents.ts @@ -12,7 +12,6 @@ import { import { Bank, LootTable } from 'oldschooljs'; import { OWNER_IDS, production } from '../config'; -import { scaryEatables } from './constants'; import { prisma } from './settings/prisma'; import { getPHeadDescriptor, @@ -26,6 +25,7 @@ import { BossInstance, BossOptions, BossUser } from './structures/Boss'; import { Gear } from './structures/Gear'; import { NewBossOptions } from './types/minions'; import { formatDuration, roll } from './util'; +import getOSItem from './util/getOSItem'; import { logError } from './util/logError'; import { sendToChannelID } from './util/webhook'; import { LampTable } from './xpLamps'; @@ -39,6 +39,45 @@ interface BossEvent { export const bossEventChannelID = production ? '897170239333220432' : '1023760501957722163'; +export const scaryEatables = [ + { + item: getOSItem('Candy teeth'), + healAmount: 3 + }, + { + item: getOSItem('Toffeet'), + healAmount: 5 + }, + { + item: getOSItem('Chocolified skull'), + healAmount: 8 + }, + { + item: getOSItem('Rotten sweets'), + healAmount: 9 + }, + { + item: getOSItem('Hairyfloss'), + healAmount: 12 + }, + { + item: getOSItem('Eyescream'), + healAmount: 13 + }, + { + item: getOSItem('Goblinfinger soup'), + healAmount: 20 + }, + { + item: getOSItem("Benny's brain brew"), + healAmount: 50 + }, + { + item: getOSItem('Roasted newt'), + healAmount: 120 + } +]; + function getScaryFoodFromBank(user: MUser, totalHealingNeeded: number, _userBank?: Bank): false | Bank { if (OWNER_IDS.includes(user.id)) return new Bank(); let totalHealingCalc = totalHealingNeeded; diff --git a/src/lib/constants.ts b/src/lib/constants.ts index fbb7c5e3db8..f3fde199403 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -668,45 +668,6 @@ export const mahojiInformationalButtons: APIButtonComponent[] = buttonSource.map export const PATRON_ONLY_GEAR_SETUP = 'Sorry - but the `other` gear setup is only available for Tier 3 Patrons (and higher) to use.'; -export const scaryEatables = [ - { - item: getOSItem('Candy teeth'), - healAmount: 3 - }, - { - item: getOSItem('Toffeet'), - healAmount: 5 - }, - { - item: getOSItem('Chocolified skull'), - healAmount: 8 - }, - { - item: getOSItem('Rotten sweets'), - healAmount: 9 - }, - { - item: getOSItem('Hairyfloss'), - healAmount: 12 - }, - { - item: getOSItem('Eyescream'), - healAmount: 13 - }, - { - item: getOSItem('Goblinfinger soup'), - healAmount: 20 - }, - { - item: getOSItem("Benny's brain brew"), - healAmount: 50 - }, - { - item: getOSItem('Roasted newt'), - healAmount: 120 - } -]; - export const projectiles = { arrow: { items: resolveItems(['Adamant arrow', 'Rune arrow', 'Amethyst arrow', 'Dragon arrow', 'Hellfire arrow']), @@ -878,7 +839,7 @@ const globalConfigSchema = z.object({ patreonCampaignID: z.coerce.number().int().default(1), patreonWebhookSecret: z.coerce.string().default(''), httpPort: z.coerce.number().int().default(8080), - clientID: z.string().min(15).max(25), + clientID: z.string().min(10).max(25), geAdminChannelID: z.string().default('') }); dotenv.config({ path: path.resolve(process.cwd(), process.env.TEST ? '.env.test' : '.env') }); diff --git a/src/lib/data/CollectionsExport.ts b/src/lib/data/CollectionsExport.ts index cb87c32cd03..7d616ecfd47 100644 --- a/src/lib/data/CollectionsExport.ts +++ b/src/lib/data/CollectionsExport.ts @@ -2020,9 +2020,9 @@ export const slayerCL = resolveItems([ 'Mystic gloves (dusk)', 'Mystic boots (dusk)', 'Basilisk jaw', - // "Dagon'hai hat", - // "Dagon'hai robe top", - // "Dagon'hai robe bottom", + "Dagon'hai hat", + "Dagon'hai robe top", + "Dagon'hai robe bottom", 'Blood shard', ...stoneSpirits.map(i => i.spirit.id), 'Brackish blade', diff --git a/src/lib/data/buyables/buyables.ts b/src/lib/data/buyables/buyables.ts index e4261297497..e5ce2a47144 100644 --- a/src/lib/data/buyables/buyables.ts +++ b/src/lib/data/buyables/buyables.ts @@ -1142,6 +1142,15 @@ const Buyables: Buyable[] = [ return toaKCs.expertKC >= 25 ? [true] : [false, 'You need a 25 Expert KC in Tombs of Amascut to buy this.']; } }, + { + name: 'Lockpick', + gpCost: 5000, + ironmanPrice: 500, + skillsNeeded: { + agility: 50, + thieving: 50 + } + }, ...sepulchreBuyables, ...constructionBuyables, ...hunterBuyables, diff --git a/src/lib/data/creatablesTable.txt b/src/lib/data/creatablesTable.txt index d43d175b82c..3e539243441 100644 --- a/src/lib/data/creatablesTable.txt +++ b/src/lib/data/creatablesTable.txt @@ -79,6 +79,7 @@ | Kodai wand | 1x Master wand, 1x Kodai insignia | 1x Kodai wand | 0 | | Salve amulet (e) | 1x Salve amulet | 1x Salve amulet (e) | 0 | | Salve amulet(ei) | 1x Salve amulet(i) | 1x Salve amulet(ei) | 0 | +| Ring of wealth (i) | 1x Ring of wealth, 1x Ring of wealth scroll | 1x Ring of wealth (i) | 50000 | | Strange hallowed tome | 1x Mysterious page 1, 1x Mysterious page 2, 1x Mysterious page 3, 1x Mysterious page 4, 1x Mysterious page 5 | 1x Strange hallowed tome | 0 | | Frozen key | 1x Frozen key piece (armadyl), 1x Frozen key piece (bandos), 1x Frozen key piece (zamorak), 1x Frozen key piece (saradomin) | 1x Frozen key | 0 | | Ecumenical key | 50x Ecumenical key shard | 1x Ecumenical key | 0 | diff --git a/src/lib/data/createables.ts b/src/lib/data/createables.ts index 34080000ce1..9df35ea47d3 100644 --- a/src/lib/data/createables.ts +++ b/src/lib/data/createables.ts @@ -2230,6 +2230,12 @@ const Createables: Createable[] = [ }, customReq: salveECustomReq }, + { + name: 'Ring of wealth (i)', + inputItems: new Bank().add('Ring of wealth').add('Ring of wealth scroll'), + GPCost: 50_000, + outputItems: new Bank().add('Ring of wealth (i)') + }, { name: 'Strange hallowed tome', inputItems: new Bank({ diff --git a/src/lib/geImage.ts b/src/lib/geImage.ts index 1e35e331ad1..d66e6c3b9b0 100644 --- a/src/lib/geImage.ts +++ b/src/lib/geImage.ts @@ -3,16 +3,11 @@ import { formatItemStackQuantity, generateHexColorForCashStack, toTitleCase } fr import { GEListing, GETransaction } from '@prisma/client'; import * as fs from 'fs/promises'; import { floor } from 'lodash'; -import fetch from 'node-fetch'; -import * as path from 'path'; import { GEListingWithTransactions } from './../mahoji/commands/ge'; import { GrandExchange } from './grandExchange'; import { fillTextXTimesInCtx } from './util/canvasUtil'; import getOSItem from './util/getOSItem'; -import { logError } from './util/logError'; - -const CACHE_DIR = './icon_cache'; function drawTitle(ctx: SKRSContext2D, title: string, canvas: Canvas) { // Draw Page Title @@ -34,20 +29,9 @@ class GeImageTask { public geProgressShadow: Image | null = null; public geIconBuy: Image | null = null; public geIconSell: Image | null = null; - public itemIconsList: Set; - public itemIconImagesCache: Map; - - public constructor() { - // This tells us simply whether the file exists or not on disk. - this.itemIconsList = new Set(); - - // If this file does exist, it might be cached in this, or need to be read from fs. - this.itemIconImagesCache = new Map(); - } async init() { await this.prepare(); - await this.run(); } async prepare() { @@ -74,23 +58,6 @@ class GeImageTask { ); } - async run() { - await this.cacheFiles(); - } - - async cacheFiles() { - // Ensure that the icon_cache dir exists. - await fs.mkdir(CACHE_DIR).catch(() => null); - CACHE_DIR; - // Get a list of all files (images) in the dir. - const filesInDir = await fs.readdir(CACHE_DIR); - - // For each one, set a cache value that it exists. - for (const fileName of filesInDir) { - this.itemIconsList.add(parseInt(path.parse(fileName).name)); - } - } - drawText( ctx: SKRSContext2D, text: string, @@ -148,7 +115,7 @@ class GeImageTask { if (listing) { // Get item - const itemImage = await this.getItemImage(listing.item_id); + const itemImage = await bankImageGenerator.getItemImage(listing.item_id); // Draw item ctx.textAlign = 'left'; @@ -221,40 +188,6 @@ class GeImageTask { } } - async getItemImage(itemID: number): Promise { - const cachedImage = this.itemIconImagesCache.get(itemID); - if (cachedImage) return cachedImage; - - const isOnDisk = this.itemIconsList.has(itemID); - if (!isOnDisk) { - await this.fetchAndCacheImage(itemID); - return this.getItemImage(itemID); - } - - const imageBuffer = await fs.readFile(path.join(CACHE_DIR, `${itemID}.png`)); - try { - const image = await loadImage(imageBuffer); - this.itemIconImagesCache.set(itemID, image); - return image; - } catch (err) { - logError(`Failed to load item icon with id: ${itemID}`); - return this.getItemImage(1); - } - } - - async fetchAndCacheImage(itemID: number) { - const imageBuffer = await fetch(`https://chisel.weirdgloop.org/static/img/osrs-sprite/${itemID}.png`).then( - result => result.buffer() - ); - - await fs.writeFile(path.join(CACHE_DIR, `${itemID}.png`), imageBuffer); - - const image = await loadImage(imageBuffer); - - this.itemIconsList.add(itemID); - this.itemIconImagesCache.set(itemID, image); - } - async createInterface(opts: { user: MUser; page: number; diff --git a/src/lib/minions/data/killableMonsters/bosses/wildy.ts b/src/lib/minions/data/killableMonsters/bosses/wildy.ts index 81529c86706..e4957cb3de6 100644 --- a/src/lib/minions/data/killableMonsters/bosses/wildy.ts +++ b/src/lib/minions/data/killableMonsters/bosses/wildy.ts @@ -774,24 +774,33 @@ export const wildyKillableMonsters: KillableMonster[] = [ timeToFinish: Time.Minute * 4.3, emoji: '<:Pet_chaos_elemental:324127377070227456>', wildy: true, - + canBePked: true, + pkActivityRating: 4, + pkBaseDeathChance: 5, difficultyRating: 8, itemsRequired: deepResolveItems([ ['Pernix body', "Black d'hide body", "Karil's leathertop"], ['Pernix chaps', "Black d'hide chaps", "Karil's leatherskirt"] ]), qpRequired: 0, - itemInBankBoosts: [ + equippedItemBoosts: [ { - [itemID("Craw's bow")]: 20, - [itemID('Webweaver bow')]: 25 + items: [ + { boostPercent: 25, itemID: itemID('Webweaver bow') }, + { boostPercent: 20, itemID: itemID("Craw's bow") } + ], + gearSetup: 'wildy' }, { - [itemID('Archers ring')]: 3, - [itemID('Archers ring (i)')]: 5 + items: [ + { boostPercent: 5, itemID: itemID('Archers ring (i)') }, + { boostPercent: 3, itemID: itemID('Archers ring') } + ], + gearSetup: 'wildy' }, { - [itemID('Barrows gloves')]: 3 + items: [{ boostPercent: 3, itemID: itemID('Barrows gloves') }], + gearSetup: 'wildy' } ], defaultAttackStyles: [SkillsEnum.Attack], @@ -808,15 +817,27 @@ export const wildyKillableMonsters: KillableMonster[] = [ timeToFinish: Time.Minute * 3.3, emoji: '<:Ancient_staff:412845709453426689>', wildy: true, + canBePked: true, + pkActivityRating: 4, + pkBaseDeathChance: 2, difficultyRating: 6, qpRequired: 0, - itemInBankBoosts: [ + equippedItemBoosts: [ { - [itemID("Craw's bow")]: 20, - [itemID('Webweaver bow')]: 25 + items: [ + { boostPercent: 25, itemID: itemID('Webweaver bow') }, + { boostPercent: 20, itemID: itemID("Craw's bow") } + ], + gearSetup: 'wildy' }, - { [itemID("Karil's leathertop")]: 3 }, - { [itemID("Karil's leatherskirt")]: 3 } + { + items: [{ boostPercent: 3, itemID: itemID("Karil's leathertop") }], + gearSetup: 'wildy' + }, + { + items: [{ boostPercent: 3, itemID: itemID("Karil's leatherskirt") }], + gearSetup: 'wildy' + } ], defaultAttackStyles: [SkillsEnum.Ranged], combatXpMultiplier: 1.125, @@ -832,10 +853,17 @@ export const wildyKillableMonsters: KillableMonster[] = [ timeToFinish: Time.Minute * 2.9, emoji: '<:Fedora:456179157303427092>', wildy: true, - + canBePked: true, + pkActivityRating: 6, + pkBaseDeathChance: 7, difficultyRating: 6, qpRequired: 0, - itemInBankBoosts: [{ [itemID('Occult necklace')]: 10 }], + equippedItemBoosts: [ + { + items: [{ boostPercent: 10, itemID: itemID('Occult necklace') }], + gearSetup: 'wildy' + } + ], defaultAttackStyles: [SkillsEnum.Magic], combatXpMultiplier: 1.25, healAmountNeeded: 4 * 20, @@ -850,9 +878,21 @@ export const wildyKillableMonsters: KillableMonster[] = [ timeToFinish: Time.Minute * 3.0, emoji: '<:Scorpias_offspring:324127378773377024>', wildy: true, + canBePked: true, + pkActivityRating: 6, + pkBaseDeathChance: 7, difficultyRating: 7, qpRequired: 0, - itemInBankBoosts: [{ [itemID('Occult necklace')]: 10 }, { [itemID('Harmonised nightmare staff')]: 10 }], + equippedItemBoosts: [ + { + items: [{ boostPercent: 10, itemID: itemID('Occult necklace') }], + gearSetup: 'wildy' + }, + { + items: [{ boostPercent: 10, itemID: itemID('Harmonised nightmare staff') }], + gearSetup: 'wildy' + } + ], defaultAttackStyles: [SkillsEnum.Magic], combatXpMultiplier: 1.3, healAmountNeeded: 4 * 20, diff --git a/src/lib/minions/data/killableMonsters/chaeldarMonsters.ts b/src/lib/minions/data/killableMonsters/chaeldarMonsters.ts index 0c315a829f3..6b4b9667deb 100644 --- a/src/lib/minions/data/killableMonsters/chaeldarMonsters.ts +++ b/src/lib/minions/data/killableMonsters/chaeldarMonsters.ts @@ -32,12 +32,15 @@ export const chaeldarMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 60, table: Monsters.Aviansie, - wildy: false, + wildy: true, difficultyRating: 4, qpRequired: 0, defaultAttackStyles: [SkillsEnum.Ranged], disallowedAttackStyles: [SkillsEnum.Attack, SkillsEnum.Strength, SkillsEnum.Magic], - healAmountNeeded: 24 + healAmountNeeded: 24, + pkActivityRating: 7, + pkBaseDeathChance: 10, + revsWeaponBoost: true }, { id: Monsters.BlackDemon.id, @@ -45,7 +48,7 @@ export const chaeldarMonsters: KillableMonster[] = [ aliases: Monsters.BlackDemon.aliases, timeToFinish: Time.Second * 36, table: Monsters.BlackDemon, - wildy: false, + wildy: true, difficultyRating: 3, existsInCatacombs: true, @@ -64,7 +67,11 @@ export const chaeldarMonsters: KillableMonster[] = [ canCannon: true, // Even if no multi, can safespot for same effect cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 7, + pkBaseDeathChance: 9, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.CaveHorror.id, @@ -209,7 +216,7 @@ export const chaeldarMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 25, table: Monsters.GreaterDemon, - wildy: false, + wildy: true, existsInCatacombs: true, difficultyRating: 2, @@ -227,7 +234,11 @@ export const chaeldarMonsters: KillableMonster[] = [ attackStylesUsed: [GearStat.AttackSlash], canCannon: true, cannonMulti: true, - canBarrage: false + canBarrage: false, + pkActivityRating: 7, + pkBaseDeathChance: 9, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.IronDragon.id, diff --git a/src/lib/minions/data/killableMonsters/krystiliaMonsters.ts b/src/lib/minions/data/killableMonsters/krystiliaMonsters.ts index 3bfb7eda8c9..c1967ce7a28 100644 --- a/src/lib/minions/data/killableMonsters/krystiliaMonsters.ts +++ b/src/lib/minions/data/killableMonsters/krystiliaMonsters.ts @@ -1,7 +1,7 @@ import { Time } from 'e'; import { Monsters } from 'oldschooljs'; -import resolveItems from '../../../util/resolveItems'; +import resolveItems, { deepResolveItems } from '../../../util/resolveItems'; import { KillableMonster } from '../../types'; export const krystiliaMonsters: KillableMonster[] = [ @@ -14,7 +14,11 @@ export const krystiliaMonsters: KillableMonster[] = [ wildy: true, difficultyRating: 5, - qpRequired: 0 + qpRequired: 0, + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true, + wildyMulti: true }, { id: Monsters.ChaosDruid.id, @@ -28,8 +32,11 @@ export const krystiliaMonsters: KillableMonster[] = [ difficultyRating: 2, qpRequired: 0, canCannon: true, - cannonMulti: true, - canBarrage: false + cannonMulti: false, + canBarrage: false, + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true }, { id: Monsters.DarkWarrior.id, @@ -44,7 +51,11 @@ export const krystiliaMonsters: KillableMonster[] = [ qpRequired: 0, canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true, + wildyMulti: true }, { id: Monsters.DeadlyRedSpider.id, @@ -59,7 +70,8 @@ export const krystiliaMonsters: KillableMonster[] = [ qpRequired: 0, canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + revsWeaponBoost: true }, { id: Monsters.ElderChaosDruid.id, @@ -74,7 +86,11 @@ export const krystiliaMonsters: KillableMonster[] = [ qpRequired: 0, canCannon: true, cannonMulti: true, - canBarrage: false + canBarrage: false, + pkActivityRating: 2, + pkBaseDeathChance: 7, + revsWeaponBoost: true, + wildyMulti: true }, { id: Monsters.Ent.id, @@ -86,11 +102,14 @@ export const krystiliaMonsters: KillableMonster[] = [ wildy: true, difficultyRating: 3, - itemsRequired: resolveItems(['Dragon axe', 'Rune axe']), + itemsRequired: deepResolveItems([['Dragon axe', 'Rune axe']]), qpRequired: 0, canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 2, + pkBaseDeathChance: 4, + revsWeaponBoost: true }, { id: Monsters.GuardBandit.id, @@ -105,7 +124,12 @@ export const krystiliaMonsters: KillableMonster[] = [ qpRequired: 0, canCannon: true, cannonMulti: true, - canBarrage: false + canBarrage: false, + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true, + canBePked: true, + wildyMulti: true }, { id: Monsters.LavaDragon.id, @@ -119,7 +143,12 @@ export const krystiliaMonsters: KillableMonster[] = [ difficultyRating: 4, itemsRequired: resolveItems(['Anti-dragon shield']), notifyDrops: resolveItems(['Draconic visage']), - qpRequired: 0 + qpRequired: 0, + pkActivityRating: 3, + pkBaseDeathChance: 4, + revsWeaponBoost: true, + canBePked: true, + wildyMulti: true }, { id: Monsters.MagicAxe.id, @@ -131,11 +160,15 @@ export const krystiliaMonsters: KillableMonster[] = [ wildy: true, difficultyRating: 3, - // itemsRequired: resolveItems(['Lockpick']), + itemsRequired: resolveItems(['Lockpick']), qpRequired: 0, levelRequirements: { - // theiving: 23 - } + thieving: 23 + }, + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true, + canCannon: true }, { id: Monsters.Mammoth.id, @@ -150,7 +183,12 @@ export const krystiliaMonsters: KillableMonster[] = [ qpRequired: 0, canCannon: true, cannonMulti: true, - canBarrage: false + canBarrage: false, + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true, + canBePked: true, + wildyMulti: true }, { id: Monsters.Pirate.id, @@ -162,10 +200,13 @@ export const krystiliaMonsters: KillableMonster[] = [ wildy: true, difficultyRating: 3, - // itemsRequired: resolveItems(['Lockpick']), + itemsRequired: resolveItems(['Lockpick']), levelRequirements: { - // thieving: 39 + thieving: 39 }, - qpRequired: 0 + qpRequired: 0, + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true } ]; diff --git a/src/lib/minions/data/killableMonsters/mazchnaMonsters.ts b/src/lib/minions/data/killableMonsters/mazchnaMonsters.ts index e7c6a0a5e8d..b68e6ebc1b9 100644 --- a/src/lib/minions/data/killableMonsters/mazchnaMonsters.ts +++ b/src/lib/minions/data/killableMonsters/mazchnaMonsters.ts @@ -86,7 +86,11 @@ export const mazchnaMonsters: KillableMonster[] = [ attackStylesUsed: [GearStat.AttackCrush], canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 4, + pkBaseDeathChance: 2, + revsWeaponBoost: true, + canBePked: true }, { id: Monsters.FeralVampyre.id, @@ -151,7 +155,7 @@ export const mazchnaMonsters: KillableMonster[] = [ aliases: Monsters.HillGiant.aliases, timeToFinish: Time.Second * 10, table: Monsters.HillGiant, - wildy: false, + wildy: true, existsInCatacombs: true, difficultyRating: 1, @@ -161,7 +165,10 @@ export const mazchnaMonsters: KillableMonster[] = [ attackStylesUsed: [GearStat.AttackCrush], canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 1, + pkBaseDeathChance: 5, + revsWeaponBoost: true }, { id: Monsters.Obor.id, @@ -211,7 +218,10 @@ export const mazchnaMonsters: KillableMonster[] = [ attackStylesUsed: [GearStat.AttackCrush], canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 4, + pkBaseDeathChance: 3, + revsWeaponBoost: true }, { id: Monsters.Killerwatt.id, diff --git a/src/lib/minions/data/killableMonsters/nieveMonsters.ts b/src/lib/minions/data/killableMonsters/nieveMonsters.ts index e9fee1abf49..92c3d65c7c0 100644 --- a/src/lib/minions/data/killableMonsters/nieveMonsters.ts +++ b/src/lib/minions/data/killableMonsters/nieveMonsters.ts @@ -32,7 +32,7 @@ export const nieveMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 76, table: Monsters.BlackDragon, - wildy: false, + wildy: true, difficultyRating: 4, itemsRequired: resolveItems(['Anti-dragon shield']), @@ -42,7 +42,11 @@ export const nieveMonsters: KillableMonster[] = [ attackStylesUsed: [GearStat.AttackSlash], canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 2, + pkBaseDeathChance: 7, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.BrutalBlackDragon.id, diff --git a/src/lib/minions/data/killableMonsters/revs.ts b/src/lib/minions/data/killableMonsters/revs.ts index a08f3f89392..a95a3cf52e3 100644 --- a/src/lib/minions/data/killableMonsters/revs.ts +++ b/src/lib/minions/data/killableMonsters/revs.ts @@ -74,7 +74,7 @@ export const revenantMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 147, table: Monsters.RevenantDragon, wildy: true, - difficultyRating: 9, + difficultyRating: 10, qpRequired: 0, pkActivityRating: 9, pkBaseDeathChance: 8, diff --git a/src/lib/minions/data/killableMonsters/turaelMonsters.ts b/src/lib/minions/data/killableMonsters/turaelMonsters.ts index c7ea433b62b..79a1121db57 100644 --- a/src/lib/minions/data/killableMonsters/turaelMonsters.ts +++ b/src/lib/minions/data/killableMonsters/turaelMonsters.ts @@ -455,7 +455,9 @@ export const turaelMonsters: KillableMonster[] = [ qpRequired: 0, canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 1, + pkBaseDeathChance: 1 }, { id: Monsters.Goblin.id, @@ -483,7 +485,10 @@ export const turaelMonsters: KillableMonster[] = [ qpRequired: 0, canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 8, + pkBaseDeathChance: 4, + revsWeaponBoost: true }, { id: Monsters.GrizzlyBearCub.id, @@ -840,7 +845,7 @@ export const turaelMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 10, table: Monsters.Scorpion, - wildy: false, + wildy: true, difficultyRating: 1, qpRequired: 0, @@ -849,7 +854,10 @@ export const turaelMonsters: KillableMonster[] = [ canBarrage: false, healAmountNeeded: 8, attackStyleToUse: GearStat.AttackSlash, - attackStylesUsed: [GearStat.AttackCrush] + attackStylesUsed: [GearStat.AttackCrush], + pkActivityRating: 3, + pkBaseDeathChance: 2, + revsWeaponBoost: true }, { id: Monsters.Seagull.id, @@ -890,7 +898,7 @@ export const turaelMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 10, table: Monsters.Skeleton, - wildy: false, + wildy: true, existsInCatacombs: true, difficultyRating: 1, @@ -900,7 +908,10 @@ export const turaelMonsters: KillableMonster[] = [ canBarrage: false, healAmountNeeded: 11, attackStyleToUse: GearStat.AttackSlash, - attackStylesUsed: [GearStat.AttackCrush] + attackStylesUsed: [GearStat.AttackCrush], + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true }, { id: Monsters.SkeletonFremennik.id, @@ -971,13 +982,16 @@ export const turaelMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 5, table: Monsters.Spider, - wildy: false, + wildy: true, difficultyRating: 1, qpRequired: 0, canCannon: true, cannonMulti: false, - canBarrage: false + canBarrage: false, + pkActivityRating: 1, + pkBaseDeathChance: 1, + revsWeaponBoost: true }, { id: Monsters.SulphurLizard.id, @@ -1200,7 +1214,7 @@ export const turaelMonsters: KillableMonster[] = [ aliases: Monsters.Zombie.aliases, timeToFinish: Time.Second * 10, table: Monsters.Zombie, - wildy: false, + wildy: true, difficultyRating: 1, qpRequired: 0, @@ -1209,7 +1223,10 @@ export const turaelMonsters: KillableMonster[] = [ canBarrage: false, healAmountNeeded: 9, attackStyleToUse: GearStat.AttackSlash, - attackStylesUsed: [GearStat.AttackCrush] + attackStylesUsed: [GearStat.AttackCrush], + pkActivityRating: 6, + pkBaseDeathChance: 4, + revsWeaponBoost: true }, { id: Monsters.ZombieRat.id, diff --git a/src/lib/minions/data/killableMonsters/vannakaMonsters.ts b/src/lib/minions/data/killableMonsters/vannakaMonsters.ts index 3e1b739de18..61744f0afda 100644 --- a/src/lib/minions/data/killableMonsters/vannakaMonsters.ts +++ b/src/lib/minions/data/killableMonsters/vannakaMonsters.ts @@ -38,7 +38,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 29, table: Monsters.AbyssalDemon, - wildy: false, + wildy: true, difficultyRating: 3, qpRequired: 0, @@ -58,7 +58,11 @@ export const vannakaMonsters: KillableMonster[] = [ healAmountNeeded: 35, attackStyleToUse: GearStat.AttackSlash, attackStylesUsed: [GearStat.AttackStab], - canBarrage: true + canBarrage: true, + pkActivityRating: 7, + pkBaseDeathChance: 10, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.AbyssalSire.id, @@ -119,7 +123,11 @@ export const vannakaMonsters: KillableMonster[] = [ [itemID('Kodai wand')]: 12, [itemID('Staff of the dead')]: 8 } - ] + ], + pkActivityRating: 4, + pkBaseDeathChance: 3, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.BabyBlueDragon.id, @@ -208,7 +216,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 27, table: Monsters.Bloodveld, - wildy: false, + wildy: true, difficultyRating: 1, qpRequired: 0, @@ -236,7 +244,10 @@ export const vannakaMonsters: KillableMonster[] = [ healAmountNeeded: 12, attackStyleToUse: GearStat.AttackRanged, attackStylesUsed: [GearStat.AttackMagic], - canCannon: true + canCannon: true, + pkActivityRating: 4, + pkBaseDeathChance: 6, + revsWeaponBoost: true }, { id: Monsters.BlueDragon.id, @@ -439,7 +450,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 18, table: Monsters.DustDevil, - wildy: false, + wildy: true, difficultyRating: 2, existsInCatacombs: true, @@ -460,7 +471,11 @@ export const vannakaMonsters: KillableMonster[] = [ canBarrage: true, healAmountNeeded: 16, attackStyleToUse: GearStat.AttackSlash, - attackStylesUsed: [GearStat.AttackCrush] + attackStylesUsed: [GearStat.AttackCrush], + pkActivityRating: 6, + pkBaseDeathChance: 8, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.ElfArcher.id, @@ -532,7 +547,10 @@ export const vannakaMonsters: KillableMonster[] = [ healAmountNeeded: 17, attackStyleToUse: GearStat.AttackSlash, attackStylesUsed: [GearStat.AttackSlash], - canCannon: true + canCannon: true, + pkActivityRating: 3, + pkBaseDeathChance: 8, + revsWeaponBoost: true }, { id: Monsters.Gargoyle.id, @@ -617,7 +635,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 37.2, table: Monsters.GreaterNechryael, - wildy: false, + wildy: true, difficultyRating: 5, qpRequired: 0, @@ -637,7 +655,11 @@ export const vannakaMonsters: KillableMonster[] = [ attackStyleToUse: GearStat.AttackSlash, attackStylesUsed: [GearStat.AttackCrush], canBarrage: true, - canCannon: true + canCannon: true, + pkActivityRating: 8, + pkBaseDeathChance: 9, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.GreenDragon.id, @@ -654,7 +676,11 @@ export const vannakaMonsters: KillableMonster[] = [ healAmountNeeded: 20, attackStyleToUse: GearStat.AttackSlash, attackStylesUsed: [GearStat.AttackSlash], - canCannon: true + canCannon: true, + revsWeaponBoost: true, + wildySlayerCave: true, + pkActivityRating: 2, + pkBaseDeathChance: 4 }, { id: Monsters.HarpieBugSwarm.id, @@ -682,7 +708,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 39, table: Monsters.Hellhound, - wildy: false, + wildy: true, existsInCatacombs: true, difficultyRating: 3, @@ -699,7 +725,11 @@ export const vannakaMonsters: KillableMonster[] = [ canCannon: true, // Not multi but you can safespot for the same effect cannonMulti: true, - canBarrage: false + canBarrage: false, + pkActivityRating: 5, + pkBaseDeathChance: 8, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.IceGiant.id, @@ -716,8 +746,12 @@ export const vannakaMonsters: KillableMonster[] = [ attackStyleToUse: GearStat.AttackSlash, attackStylesUsed: [GearStat.AttackSlash], canCannon: true, - cannonMulti: false, - canBarrage: false + cannonMulti: true, + canBarrage: false, + pkActivityRating: 2, + pkBaseDeathChance: 6, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.IceTroll.id, @@ -799,7 +833,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 25, table: Monsters.Jelly, - wildy: false, + wildy: true, difficultyRating: 2, qpRequired: 0, @@ -809,7 +843,11 @@ export const vannakaMonsters: KillableMonster[] = [ superior: Monsters.VitreousJelly, healAmountNeeded: 14, attackStyleToUse: GearStat.AttackRanged, - attackStylesUsed: [GearStat.AttackMagic] + attackStylesUsed: [GearStat.AttackMagic], + pkActivityRating: 6, + pkBaseDeathChance: 8, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.JungleHorror.id, @@ -877,7 +915,11 @@ export const vannakaMonsters: KillableMonster[] = [ canCannon: true, // No multi spots (i think) but you can safespot for same effect. cannonMulti: true, - canBarrage: false + canBarrage: false, + pkActivityRating: 7, + pkBaseDeathChance: 9, + revsWeaponBoost: true, + wildySlayerCave: true }, { id: Monsters.Molanisk.id, @@ -912,7 +954,10 @@ export const vannakaMonsters: KillableMonster[] = [ healAmountNeeded: 17, attackStyleToUse: GearStat.AttackSlash, attackStylesUsed: [GearStat.AttackSlash], - canCannon: true + canCannon: true, + pkActivityRating: 4, + pkBaseDeathChance: 3, + revsWeaponBoost: true }, { id: Monsters.Bryophyta.id, @@ -1145,7 +1190,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 15, table: Monsters.SpiritualMage, - wildy: false, + wildy: true, difficultyRating: 4, qpRequired: 0, @@ -1154,7 +1199,10 @@ export const vannakaMonsters: KillableMonster[] = [ }, healAmountNeeded: 27, attackStyleToUse: GearStat.AttackRanged, - attackStylesUsed: [GearStat.AttackMagic] + attackStylesUsed: [GearStat.AttackMagic], + pkActivityRating: 4, + pkBaseDeathChance: 6, + revsWeaponBoost: true }, { id: Monsters.SpiritualRanger.id, @@ -1163,7 +1211,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 18, table: Monsters.SpiritualRanger, - wildy: false, + wildy: true, difficultyRating: 3, qpRequired: 0, @@ -1172,7 +1220,10 @@ export const vannakaMonsters: KillableMonster[] = [ }, healAmountNeeded: 25, attackStyleToUse: GearStat.AttackSlash, - attackStylesUsed: [GearStat.AttackRanged] + attackStylesUsed: [GearStat.AttackRanged], + pkActivityRating: 4, + pkBaseDeathChance: 6, + revsWeaponBoost: true }, { id: Monsters.SpiritualWarrior.id, @@ -1181,7 +1232,7 @@ export const vannakaMonsters: KillableMonster[] = [ timeToFinish: Time.Second * 19, table: Monsters.SpiritualWarrior, - wildy: false, + wildy: true, difficultyRating: 3, qpRequired: 0, @@ -1190,7 +1241,10 @@ export const vannakaMonsters: KillableMonster[] = [ }, healAmountNeeded: 26, attackStyleToUse: GearStat.AttackSlash, - attackStylesUsed: [GearStat.AttackSlash] + attackStylesUsed: [GearStat.AttackSlash], + pkActivityRating: 4, + pkBaseDeathChance: 6, + revsWeaponBoost: true }, { id: Monsters.TerrorDog.id, diff --git a/src/lib/minions/types.ts b/src/lib/minions/types.ts index dce2ebbdca8..6f48bfe698a 100644 --- a/src/lib/minions/types.ts +++ b/src/lib/minions/types.ts @@ -71,6 +71,7 @@ export interface KillableMonster { existsInCatacombs?: boolean; qpRequired?: number; difficultyRating?: number; + revsWeaponBoost?: boolean; /** * An array of objects of ([key: itemID]: boostPercentage) boosts that apply to @@ -136,6 +137,7 @@ export interface KillableMonster { requiredQuests?: QuestID[]; deathProps?: Omit['0'], 'currentKC'>; diaryRequirement?: [Diary, DiaryTier]; + wildySlayerCave?: boolean; requiredBitfield?: BitField; minimumFoodHealAmount?: number; diff --git a/src/lib/openables.ts b/src/lib/openables.ts index 31360b1a80d..7b6fda80248 100644 --- a/src/lib/openables.ts +++ b/src/lib/openables.ts @@ -16,6 +16,7 @@ import { clueHunterOutfit } from './data/CollectionsExport'; import { defaultFarmingContract } from './minions/farming'; import { FarmingContract } from './minions/farming/types'; import { shadeChestOpenables } from './shadesKeys'; +import { nestTable } from './simulation/birdsNest'; import { BagFullOfGemsTable, BuildersSupplyCrateTable, @@ -368,6 +369,14 @@ const osjsOpenables: UnifiedOpenable[] = [ output: Openables.NestBoxSeeds.table, allItems: Openables.NestBoxSeeds.table.allItems }, + { + name: 'Bird nest', + id: 5070, + openedItem: getOSItem(5070), + aliases: ['bird nest', 'nest'], + output: nestTable, + allItems: nestTable.allItems + }, { name: 'Ogre coffin', id: 4850, diff --git a/src/lib/settings/prisma.ts b/src/lib/settings/prisma.ts index e8c60966145..d8ea48c39ab 100644 --- a/src/lib/settings/prisma.ts +++ b/src/lib/settings/prisma.ts @@ -17,6 +17,10 @@ declare global { function makePrismaClient(): PrismaClient { if (!isMainThread && !process.env.TEST) return null as any; if (!production && !process.env.TEST) console.log('Making prisma client...'); + if (!isMainThread) { + throw new Error('Prisma client should only be created on the main thread.'); + } + return new PrismaClient({ log: [ { diff --git a/src/lib/skilling/functions/miningBoosts.ts b/src/lib/skilling/functions/miningBoosts.ts index cb3ab9df189..4fd89bb0569 100644 --- a/src/lib/skilling/functions/miningBoosts.ts +++ b/src/lib/skilling/functions/miningBoosts.ts @@ -1,5 +1,3 @@ -import { Bank } from 'oldschooljs'; - import itemID from '../../util/itemID'; export const pickaxes = [ @@ -65,10 +63,10 @@ export const pickaxes = [ } ]; -export const miningGloves = [ +export const miningGloves: { id: number; Percentages: Record }[] = [ { id: itemID('Expert mining gloves'), - Percentages: new Bank({ + Percentages: { 'Silver ore': 50, Coal: 40, 'Gold ore': 33.33, @@ -76,11 +74,11 @@ export const miningGloves = [ 'Adamantite ore': 16.66, 'Runite ore': 12.5, Amethyst: 25 - }) + } }, { id: itemID('Superior mining gloves'), - Percentages: new Bank({ + Percentages: { 'Silver ore': 0, Coal: 0, 'Gold ore': 0, @@ -88,11 +86,11 @@ export const miningGloves = [ 'Adamantite ore': 16.66, 'Runite ore': 12.5, Amethyst: 0 - }) + } }, { id: itemID('Mining gloves'), - Percentages: new Bank({ + Percentages: { 'Silver ore': 50, Coal: 40, 'Gold ore': 33.33, @@ -100,14 +98,14 @@ export const miningGloves = [ 'Adamantite ore': 0, 'Runite ore': 0, Amethyst: 0 - }) + } } ]; -export const varrockArmours = [ +export const varrockArmours: { id: number; Percentages: Record }[] = [ { id: itemID('Varrock armour 4'), - Percentages: new Bank({ + Percentages: { Clay: 10, 'Copper ore': 10, 'Tin ore': 10, @@ -121,11 +119,11 @@ export const varrockArmours = [ 'Adamantite ore': 10, 'Runite ore': 10, Amethyst: 10 - }) + } }, { id: itemID('Varrock armour 3'), - Percentages: new Bank({ + Percentages: { Clay: 10, 'Copper ore': 10, 'Tin ore': 10, @@ -139,11 +137,11 @@ export const varrockArmours = [ 'Adamantite ore': 10, 'Runite ore': 0, Amethyst: 0 - }) + } }, { id: itemID('Varrock armour 2'), - Percentages: new Bank({ + Percentages: { Clay: 10, 'Copper ore': 10, 'Tin ore': 10, @@ -157,11 +155,11 @@ export const varrockArmours = [ 'Adamantite ore': 0, 'Runite ore': 0, Amethyst: 0 - }) + } }, { id: itemID('Varrock armour 1'), - Percentages: new Bank({ + Percentages: { Clay: 10, 'Copper ore': 10, 'Tin ore': 10, @@ -175,11 +173,11 @@ export const varrockArmours = [ 'Adamantite ore': 0, 'Runite ore': 0, Amethyst: 0 - }) + } } ]; -export const miningCapeOreEffect: Bank = new Bank({ +export const miningCapeOreEffect: Record = { Clay: 5, 'Copper ore': 5, 'Tin ore': 5, @@ -193,4 +191,4 @@ export const miningCapeOreEffect: Bank = new Bank({ 'Adamantite ore': 5, 'Runite ore': 0, Amethyst: 0 -}); +}; diff --git a/src/lib/skilling/skills/herblore/mixables/barbMixes.ts b/src/lib/skilling/skills/herblore/mixables/barbMixes.ts index adb2cecbf04..a25777d7c50 100644 --- a/src/lib/skilling/skills/herblore/mixables/barbMixes.ts +++ b/src/lib/skilling/skills/herblore/mixables/barbMixes.ts @@ -33,10 +33,7 @@ export const barbMixes: Mixable[] = [ aliases: ['Relicyms mix roe', 'Relicyms mix(2)', 'Relicyms mix 2 roe'], level: 9, xp: 14, - inputItems: new Bank({ - 4846: 1, - Roe: 1 - }), + inputItems: new Bank().add("Relicym's balm(2)").add('Roe'), tickRate: 1, bankTimePerPotion: 0.088 }, diff --git a/src/lib/slayer/constants.ts b/src/lib/slayer/constants.ts index 512f87350fc..c3b327a990f 100644 --- a/src/lib/slayer/constants.ts +++ b/src/lib/slayer/constants.ts @@ -12,7 +12,8 @@ export const slayerMasterChoices = [ 'Nieve', 'Chaeldar', 'Mazchna', - 'Turael' + 'Turael', + 'Krystilia' ].map(smc => { return { name: smc, value: smc }; }); diff --git a/src/lib/slayer/slayerMasters.ts b/src/lib/slayer/slayerMasters.ts index 91244d9c254..058196d72ec 100644 --- a/src/lib/slayer/slayerMasters.ts +++ b/src/lib/slayer/slayerMasters.ts @@ -3,6 +3,7 @@ import { MonsterSlayerMaster } from 'oldschooljs'; import { chaeldarTasks } from './tasks/chaeldarTasks'; import { duradelTasks } from './tasks/duradelTasks'; import { konarTasks } from './tasks/konarTasks'; +import { krystiliaTasks } from './tasks/krystiliaTasks'; import { mazchnaTasks } from './tasks/mazchnaTasks'; import { nieveTasks } from './tasks/nieveTasks'; import { turaelTasks } from './tasks/turaelTasks'; @@ -77,5 +78,13 @@ export const slayerMasters: SlayerMaster[] = [ combatLvl: 100, slayerLvl: 50, osjsEnum: MonsterSlayerMaster.Duradel + }, + { + id: 8, + name: 'Krystilia', + aliases: ['krystilia', 'wildy slayer', 'wilderness slayer'], + tasks: krystiliaTasks, + basePoints: 25, + osjsEnum: MonsterSlayerMaster.Krystilia } ]; diff --git a/src/lib/slayer/slayerUnlocks.ts b/src/lib/slayer/slayerUnlocks.ts index 0799b9c88bb..d0ae017530e 100644 --- a/src/lib/slayer/slayerUnlocks.ts +++ b/src/lib/slayer/slayerUnlocks.ts @@ -36,6 +36,7 @@ export enum SlayerTaskUnlocksEnum { StopTheWyvern, Basilocked, ActualVampyreSlayer, + IWildyMoreSlayer, // Extension Unlocks NeedMoreDarkness, AnkouVeryMuch, @@ -62,6 +63,7 @@ export enum SlayerTaskUnlocksEnum { WyverNotherTwo, Basilonger, MoreAtStake, + Revenenenenenants, // Item Purchases: SlayerRing, HerbSack, @@ -227,6 +229,14 @@ export const SlayerRewardsShop: SlayerTaskUnlocks[] = [ canBeRemoved: true, aliases: ['vampyre slayer', 'vampire slayer', 'actual vampire slayer', 'vampyres', 'vampires'] }, + { + id: SlayerTaskUnlocksEnum.IWildyMoreSlayer, + name: 'I Wildy More Slayer', + desc: 'Krystilia will be able to assign Jellies, Dust Devils, Nechryaels and Abyssal Demons as your task.', + slayerPointCost: 0, + canBeRemoved: true, + aliases: ['wildy slayer'] + }, { id: SlayerTaskUnlocksEnum.SlayerRing, name: 'Slayer ring', @@ -520,6 +530,16 @@ export const SlayerRewardsShop: SlayerTaskUnlocks[] = [ canBeRemoved: false, aliases: ['broad bolts', 'broads', 'broad arrows', 'fletching', 'broad fletching'] }, + { + id: SlayerTaskUnlocksEnum.Revenenenenenants, + name: 'Revenenenenenants', + desc: 'Extends Revenants tasks', + slayerPointCost: 100, + extendID: [Monsters.RevenantImp.id], + extendMult: 1.5, + canBeRemoved: true, + aliases: ['extend revenants', 'extend revs'] + }, { id: SlayerTaskUnlocksEnum.SizeMatters, name: 'Size Matters', diff --git a/src/lib/slayer/slayerUtil.ts b/src/lib/slayer/slayerUtil.ts index af5d0a481cb..9ab5efa69a4 100644 --- a/src/lib/slayer/slayerUtil.ts +++ b/src/lib/slayer/slayerUtil.ts @@ -19,7 +19,7 @@ import { autoslayModes } from './constants'; import { slayerMasters } from './slayerMasters'; import { SlayerRewardsShop, SlayerTaskUnlocksEnum } from './slayerUnlocks'; import { allSlayerTasks } from './tasks'; -import { bossTasks } from './tasks/bossTasks'; +import { bossTasks, wildernessBossTasks } from './tasks/bossTasks'; import { AssignableSlayerTask, SlayerMaster } from './types'; export enum SlayerMasterEnum { @@ -39,6 +39,7 @@ export interface DetermineBoostParams { monster: KillableMonster; method?: PvMMethod | null; isOnTask?: boolean; + wildyBurst?: boolean; } export function determineBoostChoice(params: DetermineBoostParams) { let boostChoice = 'none'; @@ -57,9 +58,15 @@ export function determineBoostChoice(params: DetermineBoostParams) { boostChoice = 'burst'; } else if (params.method && params.method === 'cannon') { boostChoice = 'cannon'; - } else if (params.cbOpts.includes(CombatOptionsEnum.AlwaysIceBarrage) && params.monster!.canBarrage) { + } else if ( + params.cbOpts.includes(CombatOptionsEnum.AlwaysIceBarrage) && + (params.monster!.canBarrage || params.wildyBurst) + ) { boostChoice = 'barrage'; - } else if (params.cbOpts.includes(CombatOptionsEnum.AlwaysIceBurst) && params.monster!.canBarrage) { + } else if ( + params.cbOpts.includes(CombatOptionsEnum.AlwaysIceBurst) && + (params.monster!.canBarrage || params.wildyBurst) + ) { boostChoice = 'burst'; } else if (params.cbOpts.includes(CombatOptionsEnum.AlwaysCannon)) { boostChoice = 'cannon'; @@ -180,6 +187,12 @@ export function userCanUseTask( !myUnlocks.includes(SlayerTaskUnlocksEnum.Basilocked) ) return false; + if ( + (lmon === 'dust devil' || lmon === 'greater nechryael' || lmon === 'abyssal demon' || lmon === 'jelly') && + lmast === 'krystilia' && + !myUnlocks.includes(SlayerTaskUnlocksEnum.IWildyMoreSlayer) + ) + return false; return true; } @@ -188,6 +201,7 @@ export async function assignNewSlayerTask(_user: MUser, master: SlayerMaster) { // assignedTask is the task object, currentTask is the database row. const baseTasks = [...master.tasks].filter(t => userCanUseTask(_user, t, master, false)); let bossTask = false; + let wildyBossTask = false; if ( _user.user.slayer_unlocks.includes(SlayerTaskUnlocksEnum.LikeABoss) && (master.name.toLowerCase() === 'konar quo maten' || @@ -199,15 +213,27 @@ export async function assignNewSlayerTask(_user: MUser, master: SlayerMaster) { bossTask = true; } + if (_user.user.slayer_unlocks.includes(SlayerTaskUnlocksEnum.LikeABoss) && master.id === 8 && roll(25)) { + wildyBossTask = true; + } + let assignedTask: AssignableSlayerTask | null = null; + if (bossTask) { const baseBossTasks = bossTasks.filter(t => userCanUseTask(_user, t, master, true)); if (baseBossTasks.length > 0) { assignedTask = weightedPick(baseBossTasks); - } else { - assignedTask = weightedPick(baseTasks); } - } else { + } + + if (wildyBossTask) { + const baseWildyBossTasks = wildernessBossTasks.filter(t => userCanUseTask(_user, t, master, true)); + if (baseWildyBossTasks.length > 0) { + assignedTask = weightedPick(baseWildyBossTasks); + } + } + + if (assignedTask === null) { assignedTask = weightedPick(baseTasks); } @@ -313,6 +339,9 @@ export function getCommonTaskName(task: Monster) { case Monsters.TzHaarKet.id: commonName = 'TzHaar'; break; + case Monsters.RevenantImp.id: + commonName = 'Revenant'; + break; default: } if (commonName !== 'TzHaar' && !commonName.endsWith('s')) commonName += 's'; diff --git a/src/lib/slayer/tasks/bossTasks.ts b/src/lib/slayer/tasks/bossTasks.ts index bac6139f2a8..eca159c4906 100644 --- a/src/lib/slayer/tasks/bossTasks.ts +++ b/src/lib/slayer/tasks/bossTasks.ts @@ -36,7 +36,8 @@ export const bossTasks: AssignableSlayerTask[] = [ amount: [3, 35], weight: 1, monsters: [Monsters.Callisto.id], - isBoss: true + isBoss: true, + wilderness: true }, { monster: Monsters.Cerberus, @@ -54,14 +55,16 @@ export const bossTasks: AssignableSlayerTask[] = [ amount: [3, 35], weight: 1, monsters: [Monsters.ChaosElemental.id], - isBoss: true + isBoss: true, + wilderness: true }, { monster: Monsters.ChaosFanatic, amount: [3, 35], weight: 1, monsters: [Monsters.ChaosFanatic.id], - isBoss: true + isBoss: true, + wilderness: true }, { monster: Monsters.CommanderZilyana, @@ -79,7 +82,8 @@ export const bossTasks: AssignableSlayerTask[] = [ amount: [3, 35], weight: 1, monsters: [Monsters.CrazyArchaeologist.id], - isBoss: true + isBoss: true, + wilderness: true }, { monster: Monsters.DagannothPrime, @@ -202,7 +206,8 @@ export const bossTasks: AssignableSlayerTask[] = [ amount: [3, 35], weight: 1, monsters: [Monsters.Scorpia.id], - isBoss: true + isBoss: true, + wilderness: true }, { monster: Monsters.ThermonuclearSmokeDevil, @@ -217,14 +222,16 @@ export const bossTasks: AssignableSlayerTask[] = [ amount: [3, 35], weight: 1, monsters: [Monsters.Venenatis.id], - isBoss: true + isBoss: true, + wilderness: true }, { monster: Monsters.Vetion, amount: [3, 35], weight: 1, monsters: [Monsters.Vetion.id], - isBoss: true + isBoss: true, + wilderness: true }, { monster: Monsters.Vorkath, @@ -262,3 +269,62 @@ export const bossTasks: AssignableSlayerTask[] = [ isBoss: true } ]; + +export const wildernessBossTasks: AssignableSlayerTask[] = [ + { + monster: Monsters.Callisto, + amount: [3, 35], + weight: 1, + monsters: [Monsters.Callisto.id], + isBoss: true, + wilderness: true + }, + { + monster: Monsters.ChaosElemental, + amount: [3, 35], + weight: 1, + monsters: [Monsters.ChaosElemental.id], + isBoss: true, + wilderness: true + }, + { + monster: Monsters.ChaosFanatic, + amount: [3, 35], + weight: 1, + monsters: [Monsters.ChaosFanatic.id], + isBoss: true, + wilderness: true + }, + { + monster: Monsters.CrazyArchaeologist, + amount: [3, 35], + weight: 1, + monsters: [Monsters.CrazyArchaeologist.id], + isBoss: true, + wilderness: true + }, + { + monster: Monsters.Scorpia, + amount: [3, 35], + weight: 1, + monsters: [Monsters.Scorpia.id], + isBoss: true, + wilderness: true + }, + { + monster: Monsters.Venenatis, + amount: [3, 35], + weight: 1, + monsters: [Monsters.Venenatis.id], + isBoss: true, + wilderness: true + }, + { + monster: Monsters.Vetion, + amount: [3, 35], + weight: 1, + monsters: [Monsters.Vetion.id], + isBoss: true, + wilderness: true + } +]; diff --git a/src/lib/slayer/tasks/krystiliaTasks.ts b/src/lib/slayer/tasks/krystiliaTasks.ts new file mode 100644 index 00000000000..ffc05fecaa9 --- /dev/null +++ b/src/lib/slayer/tasks/krystiliaTasks.ts @@ -0,0 +1,333 @@ +import { Monsters } from 'oldschooljs'; + +import { SlayerTaskUnlocksEnum } from '../slayerUnlocks'; +import { AssignableSlayerTask } from '../types'; +import { wildernessBossTasks } from './bossTasks'; + +export const krystiliaTasks: AssignableSlayerTask[] = [ + { + monster: Monsters.AbyssalDemon, + amount: [75, 125], + weight: 5, + monsters: [Monsters.AbyssalDemon.id], + extendedAmount: [200, 250], + extendedUnlockId: SlayerTaskUnlocksEnum.AugmentMyAbbies, + slayerLevel: 85, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Ankou, + amount: [75, 125], + weight: 6, + monsters: [Monsters.Ankou.id], + extendedAmount: [91, 150], + extendedUnlockId: SlayerTaskUnlocksEnum.AnkouVeryMuch, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Aviansie, + amount: [75, 125], + weight: 7, + monsters: [Monsters.Aviansie.id], + extendedAmount: [200, 250], + extendedUnlockId: SlayerTaskUnlocksEnum.BirdsOfAFeather, + unlocked: true, + wilderness: true + }, + // { + // monster: Monsters.Bandit, + // amount: [75, 125], + // weight: 4, + // monsters: [Monsters.Bandit.id], + // unlocked: true, + // wilderness: true + // }, + { + monster: Monsters.GrizzlyBear, + amount: [65, 100], + weight: 6, + monsters: [Monsters.GrizzlyBear.id, Monsters.Artio.id, Monsters.Callisto.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.BlackDemon, + amount: [100, 150], + weight: 7, + monsters: [Monsters.BlackDemon.id], + extendedAmount: [200, 250], + extendedUnlockId: SlayerTaskUnlocksEnum.ItsDarkInHere, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.BlackDragon, + amount: [8, 16], + weight: 4, + monsters: [Monsters.BlackDragon.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.BlackKnight, + amount: [75, 125], + weight: 3, + monsters: [Monsters.BlackKnight.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Bloodveld, + amount: [70, 110], + weight: 4, + monsters: [Monsters.Bloodveld.id], + extendedAmount: [200, 250], + extendedUnlockId: SlayerTaskUnlocksEnum.BleedMeDry, + slayerLevel: 50, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.ChaosDruid, + amount: [50, 90], + weight: 5, + monsters: [Monsters.ChaosDruid.id, Monsters.ElderChaosDruid.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.DarkWarrior, + amount: [75, 125], + weight: 4, + monsters: [Monsters.DarkWarrior.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.DustDevil, + amount: [75, 125], + weight: 5, + monsters: [Monsters.DustDevil.id], + extendedAmount: [200, 250], + extendedUnlockId: SlayerTaskUnlocksEnum.ToDustYouShallReturn, + slayerLevel: 65, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.EarthWarrior, + amount: [75, 125], + weight: 6, + monsters: [Monsters.EarthWarrior.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Ent, + amount: [35, 60], + weight: 5, + monsters: [Monsters.Ent.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.FireGiant, + amount: [75, 125], + weight: 7, + monsters: [Monsters.FireGiant.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.GreaterDemon, + amount: [100, 150], + weight: 8, + monsters: [Monsters.GreaterDemon.id], + extendedAmount: [200, 250], + extendedUnlockId: SlayerTaskUnlocksEnum.GreaterChallenge, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.GreenDragon, + amount: [65, 100], + weight: 4, + monsters: [Monsters.GreenDragon.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Hellhound, + amount: [75, 125], + weight: 7, + monsters: [Monsters.Hellhound.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.HillGiant, + amount: [75, 125], + weight: 3, + monsters: [Monsters.HillGiant.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.IceGiant, + amount: [100, 150], + weight: 6, + monsters: [Monsters.IceGiant.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.IceWarrior, + amount: [100, 150], + weight: 7, + monsters: [Monsters.IceWarrior.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Jelly, + amount: [100, 150], + weight: 5, + monsters: [Monsters.Jelly.id], + slayerLevel: 52, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.LavaDragon, + amount: [35, 60], + weight: 3, + monsters: [Monsters.LavaDragon.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.LesserDemon, + amount: [80, 120], + weight: 6, + monsters: [Monsters.LesserDemon.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.MagicAxe, + amount: [75, 125], + weight: 7, + monsters: [Monsters.MagicAxe.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Mammoth, + amount: [75, 125], + weight: 6, + monsters: [Monsters.Mammoth.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.MossGiant, + amount: [100, 150], + weight: 4, + monsters: [Monsters.MossGiant.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.GreaterNechryael, + amount: [75, 125], + weight: 5, + monsters: [Monsters.GreaterNechryael.id], + slayerLevel: 80, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Pirate, + amount: [62, 75], + weight: 3, + monsters: [Monsters.Pirate.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.RevenantImp, + amount: [40, 100], + weight: 5, + monsters: [ + Monsters.RevenantCyclops.id, + Monsters.RevenantDarkBeast.id, + Monsters.RevenantDemon.id, + Monsters.RevenantDragon.id, + Monsters.RevenantGoblin.id, + Monsters.RevenantHellhound.id, + Monsters.RevenantHobgoblin.id, + Monsters.RevenantImp.id, + Monsters.RevenantKnight.id, + Monsters.RevenantOrk.id, + Monsters.RevenantPyrefiend.id + ], + extendedAmount: [100, 150], + extendedUnlockId: SlayerTaskUnlocksEnum.Revenenenenenants, + unlocked: true, + wilderness: true + }, + // { + // monster: Monsters.Rogue, + // amount: [75, 125], + // weight: 5, + // monsters: [Monsters.Rogue.id], + // unlocked: true, + // wilderness: true + // }, + { + monster: Monsters.Scorpion, + amount: [65, 100], + weight: 6, + monsters: [Monsters.Scorpia.id, Monsters.Scorpion.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Skeleton, + amount: [65, 100], + weight: 5, + monsters: [Monsters.Skeleton.id, Monsters.Vetion.id, Monsters.Calvarion.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Spider, + amount: [65, 100], + weight: 6, + monsters: [Monsters.Spider.id, Monsters.Venenatis.id, Monsters.Spindel.id], + unlocked: true, + wilderness: true + }, + { + monster: Monsters.SpiritualRanger, + amount: [100, 150], + weight: 6, + monsters: [Monsters.SpiritualMage.id, Monsters.SpiritualRanger.id, Monsters.SpiritualWarrior.id], + extendedAmount: [181, 250], + extendedUnlockId: SlayerTaskUnlocksEnum.SpiritualFervour, + slayerLevel: 63, + unlocked: true, + wilderness: true + }, + { + monster: Monsters.Zombie, + amount: [75, 125], + weight: 3, + monsters: [Monsters.Zombie.id], + unlocked: true, + wilderness: true + }, + ...wildernessBossTasks +]; diff --git a/src/lib/slayer/types.ts b/src/lib/slayer/types.ts index 2a1bffb60ec..103c3238401 100644 --- a/src/lib/slayer/types.ts +++ b/src/lib/slayer/types.ts @@ -18,6 +18,7 @@ export interface AssignableSlayerTask { dontAssign?: boolean; extendedAmount?: [number, number]; extendedUnlockId?: number; + wilderness?: boolean; dungeoneeringLevel?: number; } diff --git a/src/lib/types/minions.ts b/src/lib/types/minions.ts index d9821df1d8d..e57b8fa13b9 100644 --- a/src/lib/types/minions.ts +++ b/src/lib/types/minions.ts @@ -159,6 +159,7 @@ export interface MonsterActivityTaskOptions extends ActivityTaskOptions { died?: boolean; pkEncounters?: number; hasWildySupplies?: boolean; + isInWilderness?: boolean; } export interface ClueActivityTaskOptions extends ActivityTaskOptions { diff --git a/src/lib/util/calcWildyPkChance.ts b/src/lib/util/calcWildyPkChance.ts index ef35b14d628..016e9ad9141 100644 --- a/src/lib/util/calcWildyPkChance.ts +++ b/src/lib/util/calcWildyPkChance.ts @@ -46,7 +46,8 @@ export async function calcWildyPKChance( peak: Peak, monster: KillableMonster, duration: number, - supplies: boolean + supplies: boolean, + cannonMulti: boolean ): Promise<[number, boolean, string]> { // Chance per minute, Difficulty from 1 to 10, and factor a million difference, High peak 5x as likley encounter, Medium peak 1x, Low peak 5x as unlikley const peakInfluence = peakFactor.find(_peaktier => _peaktier.peakTier === peak?.peakTier)?.factor ?? 1; @@ -72,7 +73,7 @@ export async function calcWildyPKChance( deathChance += deathChanceFromLevels; // Multi does make it riskier, but only if there's actually a team on you - const wildyMultiMultiplier = monster.wildyMulti === true ? 2 : 1; + let wildyMultiMultiplier = monster.wildyMulti || cannonMulti ? 2 : 1; const hasSupplies = supplies ? 0.5 : 1; const hasOverheads = user.skillLevel(SkillsEnum.Prayer) >= 43 ? 0.25 : 1; diff --git a/src/lib/util/repeatStoredTrip.ts b/src/lib/util/repeatStoredTrip.ts index d3d6f63b4a4..035129fff4c 100644 --- a/src/lib/util/repeatStoredTrip.ts +++ b/src/lib/util/repeatStoredTrip.ts @@ -436,7 +436,8 @@ export const tripHandlers = { return { name: autocompleteMonsters.find(i => i.id === data.monsterID)?.name ?? data.monsterID.toString(), quantity: data.iQty, - method + method, + wilderness: data.isInWilderness }; } }, diff --git a/src/mahoji/commands/activities.ts b/src/mahoji/commands/activities.ts index c164c129e21..1e04e47097c 100644 --- a/src/mahoji/commands/activities.ts +++ b/src/mahoji/commands/activities.ts @@ -1,3 +1,4 @@ +import { User } from 'discord.js'; import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; import { @@ -181,7 +182,15 @@ export const activitiesCommand: OSBMahojiCommand = { type: ApplicationCommandOptionType.String, name: 'name', description: 'The name of the quest (optional).', - choices: quests.map(i => ({ name: i.name, value: i.name })), + autocomplete: async (_value: string, user: User) => { + const mUser = await mUserFetch(user.id); + let list = quests + .filter(i => !mUser.user.finished_quest_ids.includes(i.id)) + .map(i => ({ name: i.name, value: i.name })); + if (list.length === 0) + list = quests.map(i => ({ name: `${i.name} (completed)`, value: i.name })); + return list; + }, required: false } ] diff --git a/src/mahoji/commands/create.ts b/src/mahoji/commands/create.ts index 9c1e681a01d..8465ba5a388 100644 --- a/src/mahoji/commands/create.ts +++ b/src/mahoji/commands/create.ts @@ -64,7 +64,7 @@ export const createCommand: OSBMahojiCommand = { }: CommandRunOptions<{ item: string; quantity?: number; showall?: boolean }>) => { const user = await mUserFetch(userID.toString()); - const itemName = options.item.toLowerCase(); + const itemName = options.item?.toLowerCase(); let { quantity } = options; if (options.showall) { return allCreatablesTable; diff --git a/src/mahoji/commands/k.ts b/src/mahoji/commands/k.ts index 3099cfae2ec..0dbf42544ed 100644 --- a/src/mahoji/commands/k.ts +++ b/src/mahoji/commands/k.ts @@ -83,19 +83,21 @@ export const autocompleteMonsters = [ ]; async function fetchUsersRecentlyKilledMonsters(userID: string) { - const res = await prisma.$queryRawUnsafe<{ mon_id: string }[]>( - `SELECT DISTINCT((data->>'monsterID')) AS mon_id + const res = await prisma.$queryRawUnsafe<{ mon_id: string; last_killed: Date }[]>( + `SELECT DISTINCT((data->>'monsterID')) AS mon_id, MAX(start_date) as last_killed FROM activity WHERE user_id = $1 AND type = 'MonsterKilling' AND finish_date > now() - INTERVAL '31 days' +GROUP BY 1 +ORDER BY 2 DESC LIMIT 10;`, BigInt(userID) ); - return new Set(res.map(i => Number(i.mon_id))); + return res.map(i => Number(i.mon_id)); } -export const killCommand: OSBMahojiCommand = { +export const minionKCommand: OSBMahojiCommand = { name: 'k', description: 'Send your minion to kill things.', attributes: { @@ -117,15 +119,17 @@ export const killCommand: OSBMahojiCommand = { : [m.name.toLowerCase(), ...m.aliases].some(str => str.includes(value.toLowerCase())) ) .sort((a, b) => { - const hasA = recentlyKilled.has(a.id); - const hasB = recentlyKilled.has(b.id); - if (hasA && hasB) return 0; + const hasA = recentlyKilled.includes(a.id); + const hasB = recentlyKilled.includes(b.id); + if (hasA && hasB) { + return recentlyKilled.indexOf(a.id) < recentlyKilled.indexOf(b.id) ? -1 : 1; + } if (hasA) return -1; if (hasB) return 1; return 0; }) .map(i => ({ - name: `${i.name}${recentlyKilled.has(i.id) ? ' (Recently killed)' : ''}`, + name: `${i.name}${recentlyKilled.includes(i.id) ? ' (Recently killed)' : ''}`, value: i.name })); } @@ -149,6 +153,18 @@ export const killCommand: OSBMahojiCommand = { name: 'show_info', description: 'Show information on this monster.', required: false + }, + { + type: ApplicationCommandOptionType.Boolean, + name: 'wilderness', + description: 'If you want to kill the monster in the wilderness.', + required: false + }, + { + type: ApplicationCommandOptionType.Boolean, + name: 'solo', + description: 'Solo (if its a group boss)', + required: false } ], run: async ({ @@ -161,11 +177,20 @@ export const killCommand: OSBMahojiCommand = { quantity?: number; method?: PvMMethod; show_info?: boolean; + wilderness?: boolean; }>) => { const user = await mUserFetch(userID); if (options.show_info) { return returnStringOrFile(await monsterInfo(user, options.name)); } - return minionKillCommand(user, interaction, channelID, options.name, options.quantity, options.method); + return minionKillCommand( + user, + interaction, + channelID, + options.name, + options.quantity, + options.method, + options.wilderness + ); } }; diff --git a/src/mahoji/commands/kill.ts b/src/mahoji/commands/kill.ts index e9506acd9eb..1fc0b587d1f 100644 --- a/src/mahoji/commands/kill.ts +++ b/src/mahoji/commands/kill.ts @@ -98,7 +98,7 @@ export const killCommand: OSBMahojiCommand = { limit, catacombs: false, onTask: false, - lootTableTertiaryChanges: Array.from(user.buildCATertiaryItemChanges().entries()) + lootTableTertiaryChanges: Array.from(user.buildTertiaryItemChanges().entries()) }); if (result.error) { diff --git a/src/mahoji/commands/leaderboard.ts b/src/mahoji/commands/leaderboard.ts index ce813dbbf1e..45c1e8f8f1c 100644 --- a/src/mahoji/commands/leaderboard.ts +++ b/src/mahoji/commands/leaderboard.ts @@ -416,7 +416,9 @@ async function openLb( name: string, ironmanOnly: boolean ) { - name = name.trim(); + if (name) { + name = name.trim(); + } let entityID = -1; let key = ''; diff --git a/src/mahoji/commands/mine.ts b/src/mahoji/commands/mine.ts index 4cfcf15aa16..82894326894 100644 --- a/src/mahoji/commands/mine.ts +++ b/src/mahoji/commands/mine.ts @@ -8,7 +8,7 @@ import { miningCapeOreEffect, miningGloves, pickaxes, varrockArmours } from '../ import { sinsOfTheFatherSkillRequirements } from '../../lib/skilling/functions/questRequirements'; import Mining from '../../lib/skilling/skills/mining'; import { MiningActivityTaskOptions } from '../../lib/types/minions'; -import { formatDuration, formatSkillRequirements, itemNameFromID, randomVariation } from '../../lib/util'; +import { formatDuration, formatSkillRequirements, itemID, itemNameFromID, randomVariation } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../lib/util/calcMaxTripLength'; import { minionName } from '../../lib/util/minionUtils'; @@ -25,7 +25,8 @@ export function calculateMiningInput({ miningLevel, craftingLevel, strengthLevel, - maxTripLength + maxTripLength, + user }: { nameInput: string; quantityInput: number | undefined; @@ -37,6 +38,7 @@ export function calculateMiningInput({ craftingLevel: number; strengthLevel: number; maxTripLength: number; + user: MUser; }) { const ore = Mining.Ores.find( ore => @@ -105,9 +107,9 @@ export function calculateMiningInput({ let glovesRate = 0; if (miningLevel >= 60) { for (const glove of miningGloves) { - if (!gearValues.some(g => g.hasEquipped(glove.id)) || !glove.Percentages.has(ore.id)) continue; - glovesRate = glove.Percentages.amount(ore.id); - if (glovesRate !== 0) { + if (!user.hasEquipped(glove.id) || !glove.Percentages[ore.name]) continue; + glovesRate = glove.Percentages[ore.name]; + if (glovesRate) { messages.push(`Lowered rock depletion rate by **${glovesRate}%** for ${itemNameFromID(glove.id)}`); break; } @@ -116,9 +118,9 @@ export function calculateMiningInput({ let armourEffect = 0; for (const armour of varrockArmours) { - if (!gearValues.some(g => g.hasEquipped(armour.id)) || !armour.Percentages.has(ore.id)) continue; - armourEffect = armour.Percentages.amount(ore.id); - if (armourEffect !== 0) { + if (!user.hasEquippedOrInBank(armour.id) || !armour.Percentages[ore.name]) continue; + armourEffect = armour.Percentages[ore.name]; + if (armourEffect) { messages.push(`**${armourEffect}%** chance to mine an extra ore using ${itemNameFromID(armour.id)}`); break; } @@ -131,9 +133,9 @@ export function calculateMiningInput({ } let miningCapeEffect = 0; - if (gearValues.some(g => g.hasEquipped('Mining cape')) && miningCapeOreEffect.has(ore.id)) { - miningCapeEffect = miningCapeOreEffect.amount(ore.id); - if (miningCapeEffect !== 0) { + if (user.hasEquippedOrInBank([itemID('Mining cape')]) && miningCapeOreEffect[ore.name]) { + miningCapeEffect = miningCapeOreEffect[ore.name]; + if (miningCapeEffect) { messages.push(`**${miningCapeEffect}%** chance to mine an extra ore using Mining cape`); } } @@ -250,7 +252,8 @@ export const mineCommand: OSBMahojiCommand = { miningLevel: user.skillLevel('mining'), craftingLevel: user.skillLevel('crafting'), strengthLevel: user.skillLevel('strength'), - maxTripLength: calcMaxTripLength(user, 'Mining') + maxTripLength: calcMaxTripLength(user, 'Mining'), + user }); if (typeof result === 'string') { diff --git a/src/mahoji/commands/minion.ts b/src/mahoji/commands/minion.ts index 9e4733a2bb6..dc9eeb9a9a1 100644 --- a/src/mahoji/commands/minion.ts +++ b/src/mahoji/commands/minion.ts @@ -29,7 +29,7 @@ import creatures from '../../lib/skilling/skills/hunter/creatures'; import { MUserStats } from '../../lib/structures/MUserStats'; import { convertLVLtoXP, getAllIDsOfUser, getUsername, isValidNickname } from '../../lib/util'; import { getKCByName } from '../../lib/util/getKCByName'; -import getOSItem from '../../lib/util/getOSItem'; +import getOSItem, { getItem } from '../../lib/util/getOSItem'; import { handleMahojiConfirmation } from '../../lib/util/handleMahojiConfirmation'; import { minionStatsEmbed } from '../../lib/util/minionStatsEmbed'; import { checkPeakTimes } from '../../lib/util/minionUtils'; @@ -238,7 +238,8 @@ export const minionCommand: OSBMahojiCommand = { autocomplete: async (value, user) => { const mappedLampables = Lampables.map(i => i.items) .flat(2) - .map(getOSItem) + .map(getItem) + .filter(notEmpty) .map(i => ({ id: i.id, name: i.name })); const botUser = await mUserFetch(user.id); diff --git a/src/mahoji/commands/offer.ts b/src/mahoji/commands/offer.ts index c64172ef905..1a2f5a83add 100644 --- a/src/mahoji/commands/offer.ts +++ b/src/mahoji/commands/offer.ts @@ -54,7 +54,7 @@ function notifyUniques(user: MUser, activity: string, uniques: number[], loot: B } } -export const mineCommand: OSBMahojiCommand = { +export const offerCommand: OSBMahojiCommand = { name: 'offer', description: 'Offer bones or bird eggs.', attributes: { diff --git a/src/mahoji/commands/rates.ts b/src/mahoji/commands/rates.ts index a9ccf0a02c9..ae22d051c32 100644 --- a/src/mahoji/commands/rates.ts +++ b/src/mahoji/commands/rates.ts @@ -653,7 +653,8 @@ ${zygomiteFarmingSource miningLevel, craftingLevel: 120, strengthLevel: 120, - maxTripLength: duration + maxTripLength: duration, + user }); if (typeof result === 'string') continue; const spiritOre = stoneSpirits.find(t => t.ore.id === ore.id); diff --git a/src/mahoji/commands/testpotato.ts b/src/mahoji/commands/testpotato.ts index f5df08df584..c76dd6648e9 100644 --- a/src/mahoji/commands/testpotato.ts +++ b/src/mahoji/commands/testpotato.ts @@ -81,6 +81,12 @@ export async function giveMaxStats(user: MUser) { } await user.update({ QP: MAX_QP, + slayer_points: 50_000, + nmz_points: 50_000, + volcanic_mine_points: 500_000, + carpenter_points: 5_000_000, + zeal_tokens: 500_000, + lms_points: 500_000, ...updates }); } diff --git a/src/mahoji/lib/abstracted_commands/autoSlayCommand.ts b/src/mahoji/lib/abstracted_commands/autoSlayCommand.ts index 2d81c2e2cbe..1e59cb90de7 100644 --- a/src/mahoji/lib/abstracted_commands/autoSlayCommand.ts +++ b/src/mahoji/lib/abstracted_commands/autoSlayCommand.ts @@ -202,6 +202,171 @@ const AutoSlayMaxEfficiencyTable: AutoslayLink[] = [ efficientName: Monsters.Lizardman.name, efficientMonster: Monsters.Lizardman.id, efficientMethod: 'cannon' + }, + { + monsterID: Monsters.RevenantImp.id, + efficientName: Monsters.RevenantDemon.name, + efficientMonster: Monsters.RevenantDemon.id, + efficientMethod: 'none' + } +]; + +const WildyAutoSlayMaxEfficiencyTable: AutoslayLink[] = [ + { + monsterID: Monsters.AbyssalDemon.id, + efficientName: Monsters.AbyssalDemon.name, + efficientMonster: Monsters.AbyssalDemon.id, + efficientMethod: 'barrage' + }, + { + monsterID: Monsters.Ankou.id, + efficientName: Monsters.Ankou.name, + efficientMonster: Monsters.Ankou.id, + efficientMethod: 'barrage' + }, + { + monsterID: Monsters.BlackDemon.id, + efficientName: Monsters.BlackDemon.name, + efficientMonster: Monsters.BlackDemon.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.BlackKnight.id, + efficientName: Monsters.BlackKnight.name, + efficientMonster: Monsters.BlackKnight.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.Bloodveld.id, + efficientName: Monsters.Bloodveld.name, + efficientMonster: Monsters.Bloodveld.id, + efficientMethod: 'barrage' + }, + { + monsterID: Monsters.ChaosDruid.id, + efficientName: Monsters.ChaosDruid.name, + efficientMonster: Monsters.ChaosDruid.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.DarkWarrior.id, + efficientName: Monsters.DarkWarrior.name, + efficientMonster: Monsters.DarkWarrior.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.DeadlyRedSpider.id, + efficientName: Monsters.DeadlyRedSpider.name, + efficientMonster: Monsters.DeadlyRedSpider.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.DustDevil.id, + efficientName: Monsters.DustDevil.name, + efficientMonster: Monsters.DustDevil.id, + efficientMethod: 'barrage' + }, + { + monsterID: Monsters.ElderChaosDruid.id, + efficientName: Monsters.ElderChaosDruid.name, + efficientMonster: Monsters.ElderChaosDruid.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.Ent.id, + efficientName: Monsters.Ent.name, + efficientMonster: Monsters.Ent.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.GreaterDemon.id, + efficientName: Monsters.GreaterDemon.name, + efficientMonster: Monsters.GreaterDemon.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.GreenDragon.id, + efficientName: Monsters.GreenDragon.name, + efficientMonster: Monsters.GreenDragon.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.GuardBandit.id, + efficientName: Monsters.GuardBandit.name, + efficientMonster: Monsters.GuardBandit.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.Hellhound.id, + efficientName: Monsters.Hellhound.name, + efficientMonster: Monsters.Hellhound.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.IceGiant.id, + efficientName: Monsters.IceGiant.name, + efficientMonster: Monsters.IceGiant.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.IceWarrior.id, + efficientName: Monsters.IceWarrior.name, + efficientMonster: Monsters.IceWarrior.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.Jelly.id, + efficientName: Monsters.Jelly.name, + efficientMonster: Monsters.Jelly.id, + efficientMethod: 'barrage' + }, + { + monsterID: Monsters.LesserDemon.id, + efficientName: Monsters.LesserDemon.name, + efficientMonster: Monsters.LesserDemon.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.MagicAxe.id, + efficientName: Monsters.MagicAxe.name, + efficientMonster: Monsters.MagicAxe.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.Mammoth.id, + efficientName: Monsters.Mammoth.name, + efficientMonster: Monsters.Mammoth.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.MossGiant.id, + efficientName: Monsters.MossGiant.name, + efficientMonster: Monsters.MossGiant.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.Nechryael.id, + efficientName: Monsters.Nechryael.name, + efficientMonster: Monsters.Nechryael.id, + efficientMethod: 'barrage' + }, + { + monsterID: Monsters.RevenantImp.id, + efficientName: Monsters.RevenantDemon.name, + efficientMonster: Monsters.RevenantDemon.id, + efficientMethod: 'none' + }, + { + monsterID: Monsters.Scorpion.id, + efficientName: Monsters.Scorpion.name, + efficientMonster: Monsters.Scorpion.id, + efficientMethod: 'cannon' + }, + { + monsterID: Monsters.Spider.id, + efficientName: Monsters.Spider.name, + efficientMonster: Monsters.Spider.id, + efficientMethod: 'cannon' } ]; @@ -285,11 +450,19 @@ export async function autoSlayCommand({ return; } if (method === 'ehp') { - const ehpMonster = AutoSlayMaxEfficiencyTable.find(e => { + let ehpMonster = AutoSlayMaxEfficiencyTable.find(e => { const masterMatch = !e.slayerMasters || e.slayerMasters.includes(usersTask.currentTask!.slayer_master_id); return masterMatch && e.monsterID === usersTask.assignedTask!.monster.id; }); + if (usersTask.currentTask.slayer_master_id === 8) { + ehpMonster = WildyAutoSlayMaxEfficiencyTable.find(e => { + const masterMatch = + !e.slayerMasters || e.slayerMasters.includes(usersTask.currentTask!.slayer_master_id); + return masterMatch && e.monsterID === usersTask.assignedTask!.monster.id; + }); + } + const ehpKillable = killableMonsters.find(m => m.id === ehpMonster?.efficientMonster); // If we don't have the requirements for the efficient monster, revert to default monster diff --git a/src/mahoji/lib/abstracted_commands/barbAssault.ts b/src/mahoji/lib/abstracted_commands/barbAssault.ts index 2efd744b84c..8a9ad1fb43b 100644 --- a/src/mahoji/lib/abstracted_commands/barbAssault.ts +++ b/src/mahoji/lib/abstracted_commands/barbAssault.ts @@ -171,7 +171,7 @@ export async function barbAssaultGambleCommand( interaction: ChatInputCommandInteraction, user: MUser, tier: string, - quantity: number + quantity = 1 ) { const buyable = GambleTiers.find(i => stringMatches(tier, i.name)); if (!buyable) { diff --git a/src/mahoji/lib/abstracted_commands/minionKill.ts b/src/mahoji/lib/abstracted_commands/minionKill.ts index 2020f6e70f7..6e365538dc3 100644 --- a/src/mahoji/lib/abstracted_commands/minionKill.ts +++ b/src/mahoji/lib/abstracted_commands/minionKill.ts @@ -111,6 +111,12 @@ const revSpecialWeapons = { mage: getOSItem("Thammaron's sceptre") } as const; +const revUpgradedWeapons = { + melee: getOSItem('Ursine chainmace'), + range: getOSItem('Webweaver bow'), + mage: getOSItem('Accursed sceptre') +} as const; + function formatMissingItems(consumables: Consumable[], timeToFinish: number) { const str = []; @@ -162,7 +168,8 @@ export async function minionKillCommand( channelID: string, name: string, quantity: number | undefined, - method: PvMMethod | undefined + method: PvMMethod | undefined, + wilderness: boolean | undefined ) { if (user.minionIsBusy) { return 'Your minion is busy.'; @@ -223,6 +230,16 @@ export async function minionKillCommand( return `You can't kill ${monster.name}, because you're not on a slayer task.`; } + if (monster.canBePked && wilderness === false) { + return `You can't kill ${monster.name} outside the wilderness.`; + } + + const isInWilderness = wilderness || (isOnTask && usersTask.assignedTask?.wilderness) || monster.canBePked; + + if (!monster.wildy && isInWilderness) { + return `You can't kill ${monster.name} in the wilderness.`; + } + const wildyGearStat = wildyGear.getStats()[key]; const revGearPercent = Math.max(0, calcWhatPercent(wildyGearStat, maxOffenceStats[key])); @@ -237,6 +254,12 @@ export async function minionKillCommand( } } + // Add jelly & bloodveld check as can barrage in wilderness + const jelly = monster.id === Monsters.Jelly.id; + const bloodveld = monster.id === Monsters.Bloodveld.id; + + const wildyBurst = (jelly || bloodveld) && isInWilderness; + // Set chosen boost based on priority: const myCBOpts = user.combatOptions; const boostChoice = determineBoostChoice({ @@ -244,7 +267,8 @@ export async function minionKillCommand( user, monster, method, - isOnTask + isOnTask, + wildyBurst }); // Check requirements @@ -314,7 +338,7 @@ export async function minionKillCommand( } } - for (const [itemID, boostAmount] of Object.entries(resolveAvailableItemBoosts(user, monster))) { + for (const [itemID, boostAmount] of Object.entries(resolveAvailableItemBoosts(user, monster, isInWilderness))) { timeToFinish *= (100 - boostAmount) / 100; boosts.push(`${boostAmount}% for ${itemNameFromID(parseInt(itemID))}`); } @@ -323,11 +347,11 @@ export async function minionKillCommand( timeToFinish = reduceNumByPercent(timeToFinish, 20); boosts.push('20% boost for Gregoyle'); } - if (user.hasEquipped('Dwarven warhammer') && !monster.wildy) { + if (user.hasEquipped('Dwarven warhammer') && !isInWilderness) { timeToFinish = reduceNumByPercent(timeToFinish, 40); boosts.push('40% boost for Dwarven warhammer'); } - if (user.gear.wildy.hasEquipped(['Hellfire bow']) && monster.wildy) { + if (user.gear.wildy.hasEquipped(['Hellfire bow']) && isInWilderness) { timeToFinish /= 3; boosts.push('3x boost for Hellfire bow'); } @@ -343,16 +367,37 @@ export async function minionKillCommand( let virtusBoost = 0; let virtusBoostMsg = ''; - const dragonBoost = 15; // Common boost percentage for dragon-related gear + let dragonBoost = 0; + let dragonBoostMsg = ''; + let revBoost = 0; + let revBoostMsg = ''; const isUndead = osjsMon?.data?.attributes?.includes(MonsterAttribute.Undead); const isDragon = osjsMon?.data?.attributes?.includes(MonsterAttribute.Dragon); + function applyRevWeaponBoost() { + const style = convertAttackStylesToSetup(user.user.attack_style); + const specialWeapon = revSpecialWeapons[style]; + const upgradedWeapon = revUpgradedWeapons[style]; + + if (wildyGear.hasEquipped(specialWeapon.name)) { + revBoost = 12.5; + timeToFinish = reduceNumByPercent(timeToFinish, revBoost); + revBoostMsg = `${revBoost}% for ${specialWeapon.name}`; + } + + if (wildyGear.hasEquipped(upgradedWeapon.name)) { + revBoost = 17.5; + timeToFinish = reduceNumByPercent(timeToFinish, revBoost); + revBoostMsg = `${revBoost}% for ${upgradedWeapon.name}`; + } + } + function applyDragonBoost() { - const hasDragonLance = monster?.canBePked + const hasDragonLance = isInWilderness ? wildyGear.hasEquipped('Dragon hunter lance') : user.hasEquippedOrInBank('Dragon hunter lance'); - const hasDragonCrossbow = monster?.canBePked + const hasDragonCrossbow = isInWilderness ? wildyGear.hasEquipped('Dragon hunter crossbow') : user.hasEquippedOrInBank('Dragon hunter crossbow'); @@ -360,25 +405,25 @@ export async function minionKillCommand( (hasDragonLance && !attackStyles.includes(SkillsEnum.Ranged) && !attackStyles.includes(SkillsEnum.Magic)) || (hasDragonCrossbow && attackStyles.includes(SkillsEnum.Ranged)) ) { - const boostMessage = hasDragonLance + dragonBoost = 15; // Common boost percentage for dragon-related gear + dragonBoostMsg = hasDragonLance ? `${dragonBoost}% for Dragon hunter lance` : `${dragonBoost}% for Dragon hunter crossbow`; timeToFinish = reduceNumByPercent(timeToFinish, dragonBoost); - boosts.push(boostMessage); } } function applyBlackMaskBoost() { - const hasInfernalSlayerHelmI = monster?.canBePked + const hasInfernalSlayerHelmI = isInWilderness ? wildyGear.hasEquipped('Infernal slayer helmet(i)') : user.hasEquippedOrInBank('Infernal slayer helmet(i)'); - const hasInfernalSlayerHelm = monster?.canBePked + const hasInfernalSlayerHelm = isInWilderness ? wildyGear.hasEquipped('Infernal slayer helmet') : user.hasEquippedOrInBank('Infernal slayer helmet'); - const hasBlackMask = monster?.canBePked + const hasBlackMask = isInWilderness ? wildyGear.hasEquipped('Black mask') : user.hasEquippedOrInBank('Black mask'); - const hasBlackMaskI = monster?.canBePked + const hasBlackMaskI = isInWilderness ? wildyGear.hasEquipped('Black mask (i)') : user.hasEquippedOrInBank('Black mask (i)'); @@ -406,10 +451,10 @@ export async function minionKillCommand( let salveEnhanced = false; const style = attackStyles[0]; if (style === 'ranged' || style === 'magic') { - salveBoost = monster?.canBePked + salveBoost = isInWilderness ? wildyGear.hasEquipped('Salve amulet(i)') : user.hasEquippedOrInBank('Salve amulet (i)'); - salveEnhanced = monster?.canBePked + salveEnhanced = isInWilderness ? wildyGear.hasEquipped('Salve amulet(ei)') : user.hasEquippedOrInBank('Salve amulet (ei)'); if (salveBoost) { @@ -419,10 +464,10 @@ export async function minionKillCommand( } on non-melee task`; } } else { - salveBoost = monster?.canBePked + salveBoost = isInWilderness ? wildyGear.hasEquipped('Salve amulet') : user.hasEquippedOrInBank('Salve amulet'); - salveEnhanced = monster?.canBePked + salveEnhanced = isInWilderness ? wildyGear.hasEquipped('Salve amulet (e)') : user.hasEquippedOrInBank('Salve amulet (e)'); if (salveBoost) { @@ -434,10 +479,19 @@ export async function minionKillCommand( } } + if (isInWilderness && monster.revsWeaponBoost) { + applyRevWeaponBoost(); + } + function calculateVirtusBoost() { let virtusPiecesEquipped = 0; + for (const item of resolveItems(['Virtus mask', 'Virtus robe top', 'Virtus robe legs'])) { - if (user.gear.mage.hasEquipped(item)) { + if (isInWilderness) { + if (wildyGear.hasEquipped(item)) { + virtusPiecesEquipped += blackMaskBoost !== 0 && itemNameFromID(item) === 'Virtus mask' ? 0 : 1; + } + } else if (user.gear.mage.hasEquipped(item)) { virtusPiecesEquipped += blackMaskBoost !== 0 && itemNameFromID(item) === 'Virtus mask' ? 0 : 1; } } @@ -446,7 +500,7 @@ export async function minionKillCommand( virtusBoostMsg = virtusPiecesEquipped > 1 ? ` with ${virtusPiecesEquipped} Virtus pieces` - : virtusPiecesEquipped > 0 + : virtusPiecesEquipped === 1 ? ` with ${virtusPiecesEquipped} Virtus piece` : ''; } @@ -474,6 +528,17 @@ export async function minionKillCommand( } } + // Only choose greater boost: + if (dragonBoost || revBoost) { + if (revBoost > dragonBoost) { + timeToFinish = reduceNumByPercent(timeToFinish, revBoost); + boosts.push(revBoostMsg); + } else { + timeToFinish = reduceNumByPercent(timeToFinish, dragonBoost); + boosts.push(dragonBoostMsg); + } + } + if (revenants) { timeToFinish = reduceNumByPercent(timeToFinish, revGearPercent / 4); boosts.push(`${(revGearPercent / 4).toFixed(2)}% (out of a possible 25%) for ${key}`); @@ -499,15 +564,14 @@ export async function minionKillCommand( if (!isOnTask && method && method !== 'none') { return 'You can only burst/barrage/cannon while on task in BSO.'; } - if ((method === 'burst' || method === 'barrage') && !monster!.canBarrage) { + if (!wildyBurst && (method === 'burst' || method === 'barrage') && !monster!.canBarrage) { return `${monster!.name} cannot be barraged or burst.`; } if (method === 'cannon' && !hasCannon) { return "You don't own a Dwarf multicannon, so how could you use one?"; } - if (method === 'cannon' && !monster!.canCannon) { - return `${monster!.name} cannot be killed with a cannon.`; - } + + // Check for stats if (boostChoice === 'barrage' && user.skillLevel(SkillsEnum.Magic) < 94) { return `You need 94 Magic to use Ice Barrage. You have ${user.skillLevel(SkillsEnum.Magic)}`; } @@ -523,6 +587,35 @@ export async function minionKillCommand( return `You need 65 Ranged to use Chinning method. You have ${user.skillLevel(SkillsEnum.Ranged)}`; } + // Wildy Monster checks + if (isInWilderness === true && boostChoice === 'cannon') { + if (monster.id === Monsters.HillGiant.id || monster.id === Monsters.MossGiant.id) { + usingCannon = isInWilderness; + } + if (monster.wildySlayerCave) { + usingCannon = isInWilderness; + cannonMulti = isInWilderness; + if (monster.id === Monsters.AbyssalDemon.id && !isOnTask) { + usingCannon = false; + cannonMulti = false; + } + } + } + + if ((method === 'burst' || method === 'barrage') && !monster!.canBarrage) { + if (jelly || bloodveld) { + if (!isInWilderness) { + return `${monster.name} can only be barraged or burst in the wilderness.`; + } + } else return `${monster!.name} cannot be barraged or burst.`; + } + + if (!usingCannon) { + if (method === 'cannon' && !monster!.canCannon) { + return `${monster!.name} cannot be killed with a cannon.`; + } + } + if ( boostChoice === 'cannon' && !user.user.disabled_inventions.includes(InventionID.SuperiorDwarfMultiCannon) && @@ -542,25 +635,33 @@ export async function minionKillCommand( timeToFinish = reduceNumByPercent(timeToFinish, boost); boosts.push(`${boost}% for Superior Cannon (${res.messages})`); } - } else if (boostChoice === 'barrage' && attackStyles.includes(SkillsEnum.Magic) && monster!.canBarrage) { + } else if ( + boostChoice === 'barrage' && + attackStyles.includes(SkillsEnum.Magic) && + (monster!.canBarrage || wildyBurst) + ) { consumableCosts.push(iceBarrageConsumables); calculateVirtusBoost(); timeToFinish = reduceNumByPercent(timeToFinish, boostIceBarrage + virtusBoost); boosts.push(`${boostIceBarrage + virtusBoost}% for Ice Barrage${virtusBoostMsg}`); burstOrBarrage = SlayerActivityConstants.IceBarrage; - } else if (boostChoice === 'burst' && attackStyles.includes(SkillsEnum.Magic) && monster!.canBarrage) { + } else if ( + boostChoice === 'burst' && + attackStyles.includes(SkillsEnum.Magic) && + (monster!.canBarrage || wildyBurst) + ) { consumableCosts.push(iceBurstConsumables); calculateVirtusBoost(); timeToFinish = reduceNumByPercent(timeToFinish, boostIceBurst + virtusBoost); boosts.push(`${boostIceBurst + virtusBoost}% for Ice Burst${virtusBoostMsg}`); burstOrBarrage = SlayerActivityConstants.IceBurst; - } else if (boostChoice === 'cannon' && hasCannon && monster!.cannonMulti) { + } else if ((boostChoice === 'cannon' && hasCannon && monster!.cannonMulti) || cannonMulti) { usingCannon = true; cannonMulti = true; consumableCosts.push(cannonMultiConsumables); timeToFinish = reduceNumByPercent(timeToFinish, boostCannonMulti); boosts.push(`${boostCannonMulti}% for Cannon in multi`); - } else if (boostChoice === 'cannon' && hasCannon && monster!.canCannon) { + } else if ((boostChoice === 'cannon' && hasCannon && monster!.canCannon) || usingCannon) { usingCannon = true; consumableCosts.push(cannonSingleConsumables); timeToFinish = reduceNumByPercent(timeToFinish, boostCannon); @@ -601,7 +702,7 @@ export async function minionKillCommand( timeToFinish *= 0.8; boosts.push('20% for Dwarven blessing'); } - if (monster.wildy && hasZealotsAmulet) { + if (isInWilderness && hasZealotsAmulet) { timeToFinish *= 0.95; boosts.push('5% for Amulet of zealots'); } @@ -660,9 +761,13 @@ export async function minionKillCommand( for (const degItem of degradeablePvmBoostItems) { const isUsing = convertPvmStylesToGearSetup(attackStyles).includes(degItem.attackStyle) && - user.gear[degItem.attackStyle].hasEquipped(degItem.item.id) && (monster.setupsUsed ? monster.setupsUsed.includes(degItem.attackStyle) : true); - if (isUsing) { + + const gearCheck = isInWilderness + ? user.gear.wildy.hasEquipped(degItem.item.id) + : user.gear[degItem.attackStyle].hasEquipped(degItem.item.id); + + if (isUsing && gearCheck) { // We assume they have enough charges, add the boost, and degrade at the end to avoid doing it twice. degItemBeingUsed.push(degItem); } @@ -965,7 +1070,7 @@ export async function minionKillCommand( let hasDied: boolean | undefined = undefined; let hasWildySupplies = undefined; - if (monster.canBePked) { + if (isInWilderness) { await increaseWildEvasionXp(user, duration); thePkCount = 0; hasDied = false; @@ -1022,7 +1127,8 @@ export async function minionKillCommand( wildyPeak!, monster, duration, - hasWildySupplies + hasWildySupplies, + cannonMulti ); thePkCount = pkCount; hasDied = died; @@ -1038,7 +1144,7 @@ export async function minionKillCommand( foodStr += foodMessages; let gearToCheck: GearSetupType = convertAttackStyleToGearSetup(monster.attackStyleToUse); - if (monster.wildy) gearToCheck = 'wildy'; + if (isInWilderness) gearToCheck = 'wildy'; try { const { foodRemoved, reductions, reductionRatio } = await removeFoodFromUser({ @@ -1046,11 +1152,11 @@ export async function minionKillCommand( totalHealingNeeded: healAmountNeeded * quantity, healPerAction: Math.ceil(healAmountNeeded / quantity), activityName: monster.name, - attackStylesUsed: monster.wildy + attackStylesUsed: isInWilderness ? ['wildy'] : uniqueArr([...objectKeys(monster.minimumGearRequirements ?? {}), gearToCheck]), learningPercentage: percentReduced, - isWilderness: monster.wildy, + isWilderness: isInWilderness, minimumHealAmount: monster.minimumFoodHealAmount }); @@ -1106,7 +1212,7 @@ export async function minionKillCommand( // Remove items after food calc to prevent losing items if the user doesn't have the right amount of food. Example: Mossy key if (lootToRemove.length > 0) { updateBankSetting('economyStats_PVMCost', lootToRemove); - await user.specialRemoveItems(lootToRemove, { wildy: monster.wildy ? true : false }); + await user.specialRemoveItems(lootToRemove, { wildy: isInWilderness ? true : false }); totalCost.add(lootToRemove); } @@ -1139,7 +1245,8 @@ export async function minionKillCommand( burstOrBarrage: !burstOrBarrage ? undefined : burstOrBarrage, died: hasDied, pkEncounters: thePkCount, - hasWildySupplies + hasWildySupplies, + isInWilderness }); if (usedDart) { diff --git a/src/mahoji/lib/abstracted_commands/slayerTaskCommand.ts b/src/mahoji/lib/abstracted_commands/slayerTaskCommand.ts index 4cca01aea2c..62a214583ad 100644 --- a/src/mahoji/lib/abstracted_commands/slayerTaskCommand.ts +++ b/src/mahoji/lib/abstracted_commands/slayerTaskCommand.ts @@ -99,7 +99,8 @@ export async function slayerListBlocksCommand(mahojiUser: MUser) { export async function slayerStatusCommand(mahojiUser: MUser) { const { currentTask, assignedTask, slayerMaster } = await getUsersCurrentSlayerInfo(mahojiUser.id); const { slayer_points: slayerPoints } = mahojiUser.user; - const { slayer_task_streak: slayerStreak } = await mahojiUser.fetchStats({ slayer_task_streak: true }); + const slayer_streaks = await mahojiUser.fetchStats({ slayer_task_streak: true, slayer_wildy_task_streak: true }); + return ( `${ currentTask @@ -110,7 +111,9 @@ export async function slayerStatusCommand(mahojiUser: MUser) { )}. You have ${currentTask.quantity_remaining.toLocaleString()} kills remaining.` : '' }` + - `\nYou have ${slayerPoints.toLocaleString()} slayer points, and have completed ${slayerStreak} tasks in a row.` + `\nYou have ${slayerPoints.toLocaleString()} slayer points, and have completed ${ + slayer_streaks.slayer_task_streak + } tasks in a row and ${slayer_streaks.slayer_wildy_task_streak} wilderness tasks in a row.` ); } @@ -241,8 +244,9 @@ export async function slayerNewTaskCommand({ const has99SlayerCape = user.skillLevel('slayer') >= 99 && user.hasEquippedOrInBank('Slayer cape'); - // Chooses a default slayer master: + // Chooses a default slayer master (excluding Krystilia): const proposedDefaultMaster = slayerMasters + .filter(sm => sm.id !== 8) // Exclude Krystilia .sort((a, b) => b.basePoints - a.basePoints) .find(sm => userCanUseMaster(user, sm)); @@ -276,11 +280,13 @@ export async function slayerNewTaskCommand({ interactionReply(interaction, 'You cannot skip this task because Turael assigns it.'); return; } + const isUsingKrystilia = Boolean(currentTask?.slayer_master_id === 8); + const taskStreakKey = isUsingKrystilia ? 'slayer_wildy_task_streak' : 'slayer_task_streak'; + const warning = `Really cancel task? This will reset your${ + isUsingKrystilia ? ' wilderness' : '' + } streak to 0 and give you a new ${slayerMaster.name} task.`; - await handleMahojiConfirmation( - interaction, - `Really cancel task? This will reset your streak to 0 and give you a new ${slayerMaster.name} task.` - ); + await handleMahojiConfirmation(interaction, warning); await prisma.slayerTask.update({ where: { id: currentTask.id @@ -290,7 +296,8 @@ export async function slayerNewTaskCommand({ quantity_remaining: 0 } }); - await userStatsUpdate(user.id, { slayer_task_streak: 0 }, {}); + await userStatsUpdate(user.id, { [taskStreakKey]: 0 }, {}); + const newSlayerTask = await assignNewSlayerTask(user, slayerMaster); let commonName = getCommonTaskName(newSlayerTask.assignedTask!.monster); const returnMessage = diff --git a/src/mahoji/mahojiSettings.ts b/src/mahoji/mahojiSettings.ts index a9f1f44ec79..01a32f60f94 100644 --- a/src/mahoji/mahojiSettings.ts +++ b/src/mahoji/mahojiSettings.ts @@ -310,7 +310,7 @@ export function hasMonsterRequirements(user: MUser, monster: KillableMonster) { return [true]; } -export function resolveAvailableItemBoosts(user: MUser, monster: KillableMonster) { +export function resolveAvailableItemBoosts(user: MUser, monster: KillableMonster, isInWilderness: boolean = false) { const boosts = new Bank(); if (monster.itemInBankBoosts) { for (const boostSet of monster.itemInBankBoosts) { @@ -320,7 +320,7 @@ export function resolveAvailableItemBoosts(user: MUser, monster: KillableMonster // find the highest boost that the player has for (const [itemID, boostAmount] of Object.entries(boostSet)) { const parsedId = parseInt(itemID); - if (!user.hasEquippedOrInBank(parsedId)) { + if (isInWilderness ? !user.hasEquipped(parsedId) : !user.hasEquippedOrInBank(parsedId)) { continue; } if (boostAmount > highestBoostAmount) { diff --git a/src/tasks/minions/monsterActivity.ts b/src/tasks/minions/monsterActivity.ts index 6f5dd0be3d8..5bb04dd89d0 100644 --- a/src/tasks/minions/monsterActivity.ts +++ b/src/tasks/minions/monsterActivity.ts @@ -175,7 +175,8 @@ export const monsterTask: MinionTask = { burstOrBarrage, died, pkEncounters, - hasWildySupplies + hasWildySupplies, + isInWilderness } = data; const monster = killableMonsters.find(mon => mon.id === monsterID)!; @@ -369,7 +370,12 @@ export const monsterTask: MinionTask = { const isOnTaskResult = await isOnSlayerTask({ user, monsterID, quantityKilled: quantity }); const superiorTable = isOnTaskResult.hasSuperiorsUnlocked && monster.superior ? monster.superior : undefined; - const isInCatacombs = !usingCannon ? monster.existsInCatacombs ?? undefined : undefined; + const isInCatacombs = (!usingCannon ? monster.existsInCatacombs ?? undefined : undefined) && !isInWilderness; + + let hasRingOfWealthI = user.gear.wildy.hasEquipped('Ring of wealth (i)') && isInWilderness; + if (hasRingOfWealthI) { + messages.push('\nYour clue scroll chance is doubled due to wearing a Ring of Wealth (i).'); + } const killOptions: MonsterKillOptions = { onSlayerTask: isOnTaskResult.isOnTask, @@ -377,7 +383,11 @@ export const monsterTask: MinionTask = { hasSuperiors: superiorTable, inCatacombs: isInCatacombs, lootTableOptions: { - tertiaryItemPercentageChanges: user.buildCATertiaryItemChanges() + tertiaryItemPercentageChanges: user.buildTertiaryItemChanges( + hasRingOfWealthI, + isInWilderness, + isOnTaskResult.isOnTask + ) } }; @@ -387,14 +397,24 @@ export const monsterTask: MinionTask = { // Calculate superiors and assign loot. let newSuperiorCount = 0; + if (superiorTable && isOnTaskResult.isOnTask) { - let superiorDroprate = 200; - if (user.hasCompletedCATier('elite')) { - superiorDroprate = 150; - messages.push(`${Emoji.CombatAchievements} 25% more common superiors due to Elite CA tier`); - } - for (let i = 0; i < quantity; i++) { - if (roll(superiorDroprate)) newSuperiorCount++; + if (!(isInWilderness && monster.name === 'Bloodveld')) { + let superiorDroprate = 200; + if (isInWilderness) { + superiorDroprate *= 0.9; + messages.push('\n10% more common superiors due to Wilderness Slayer.'); + } + if (user.hasCompletedCATier('elite')) { + superiorDroprate *= 0.75; + messages.push(`\n${Emoji.CombatAchievements} 25% more common superiors due to Elite CA tier.`); + } + + for (let i = 0; i < quantity; i++) { + if (roll(superiorDroprate)) { + newSuperiorCount++; + } + } } } @@ -410,6 +430,16 @@ export const monsterTask: MinionTask = { // Superior loot and totems if in catacombs loot.add(superiorTable!.kill(newSuperiorCount)); if (isInCatacombs) loot.add('Dark totem base', newSuperiorCount); + if (isInWilderness) loot.add("Larran's key", newSuperiorCount); + } + + // Hill giant key wildy buff + if (isInWilderness && monster.name === 'Hill giant') { + for (let i = 0; i < quantity; i++) { + if (roll(128)) { + loot.add('Giant key'); + } + } } const xpRes: string[] = []; @@ -583,9 +613,32 @@ export const monsterTask: MinionTask = { } const quantityLeft = Math.max(0, isOnTaskResult.currentTask!.quantity_remaining - effectiveSlayed); + const isUsingKrystilia = isOnTaskResult.slayerMaster.id === 8; thisTripFinishesTask = quantityLeft === 0; - if (thisTripFinishesTask) { + if (thisTripFinishesTask && isUsingKrystilia) { + const newStats = await userStatsUpdate( + user.id, + { + slayer_wildy_task_streak: { + increment: 1 + } + }, + { slayer_wildy_task_streak: true } + ); + const currentStreak = newStats.slayer_wildy_task_streak; + const points = await calculateSlayerPoints(currentStreak, isOnTaskResult.slayerMaster, user); + const secondNewUser = await user.update({ + slayer_points: { + increment: points + } + }); + str += `\n**You've completed ${currentStreak} wilderness tasks and received ${points} points; giving you a total of ${secondNewUser.newUser.slayer_points}; return to a Slayer master.**`; + if (isOnTaskResult.assignedTask.isBoss) { + str += ` ${await user.addXP({ skillName: SkillsEnum.Slayer, amount: 5000, minimal: true })}`; + str += ' for completing your boss task.'; + } + } else if (thisTripFinishesTask) { const newStats = await userStatsUpdate( user.id, { diff --git a/src/tasks/minions/pickpocketActivity.ts b/src/tasks/minions/pickpocketActivity.ts index 6e59db77eae..ff95791c08b 100644 --- a/src/tasks/minions/pickpocketActivity.ts +++ b/src/tasks/minions/pickpocketActivity.ts @@ -77,7 +77,7 @@ export const pickpocketTask: MinionTask = { if (obj.type === 'pickpockable') { for (let i = 0; i < successfulQuantity; i++) { const lootItems = obj.table.roll(1, { - tertiaryItemPercentageChanges: user.buildCATertiaryItemChanges() + tertiaryItemPercentageChanges: user.buildTertiaryItemChanges() }); // TODO: Remove Rocky from loot tables in oldschoolJS if (lootItems.has('Rocky')) lootItems.remove('Rocky'); diff --git a/tests/globalSetup.ts b/tests/globalSetup.ts index 72c831572d8..05250a33898 100644 --- a/tests/globalSetup.ts +++ b/tests/globalSetup.ts @@ -5,7 +5,15 @@ import { Collection } from 'discord.js'; import { vi } from 'vitest'; vi.mock('@oldschoolgg/toolkit', async () => { - const actualToolkit = await vi.importActual('@oldschoolgg/toolkit'); // Import all actual exports + const actual: any = await vi.importActual('@oldschoolgg/toolkit'); + return { + ...actual, + mentionCommand: async (_args: any) => 'hi' + }; +}); + +vi.mock('../node_modules/@oldschoolgg/toolkit/src/util/discord.ts', async () => { + const actualToolkit = await vi.importActual('../node_modules/@oldschoolgg/toolkit/src/util/discord.ts'); // Import all actual exports return { ...actualToolkit, // Include all actual exports in the mock mentionCommand: vi.fn().mockReturnValue('') // Mock mentionCommand to return a blank string @@ -18,21 +26,19 @@ global.globalClient = { guilds: { cache: new Collection() }, mahojiClient: { commands: { - values: [ - { - name: 'test', - description: 'test description', - attributes: { description: 'test description' }, - options: [] - } - ] + values: ['test'].map(n => ({ + name: n, + description: 'test description', + attributes: { description: 'test description' }, + options: [{ name: 'claim' }] + })) } }, users: { cache: new Collection() }, channels: { - cache: new Collection() + cache: new Collection().set('1', { id: '1' }) }, busyCounterCache: new Map() } as any; diff --git a/tests/integration/allCommandsBase.test.ts b/tests/integration/allCommandsBase.test.ts index 59800bd64a8..f543de60d71 100644 --- a/tests/integration/allCommandsBase.test.ts +++ b/tests/integration/allCommandsBase.test.ts @@ -1,98 +1,326 @@ -import { describe, test, vi } from 'vitest'; +import { join } from 'node:path'; +import { ApplicationCommandOptionType } from 'discord.js'; +import { randArrItem, randInt, shuffleArr, Time } from 'e'; +import { Store } from 'mahoji/dist/lib/structures/Store'; +import { CommandOption } from 'mahoji/dist/lib/types'; +import { isValidCommand } from 'mahoji/dist/lib/util'; +import { Bank, Items } from 'oldschooljs'; +import { expect, test, vi } from 'vitest'; + +import { BitField, minionActivityCache } from '../../src/lib/constants'; +import { prisma } from '../../src/lib/settings/prisma'; +import { mahojiClientSettingsFetch } from '../../src/lib/util/clientSettings'; +import { handleMahojiConfirmation } from '../../src/lib/util/handleMahojiConfirmation'; import { activitiesCommand } from '../../src/mahoji/commands/activities'; +import { adminCommand } from '../../src/mahoji/commands/admin'; import { askCommand } from '../../src/mahoji/commands/ask'; -import { bankCommand } from '../../src/mahoji/commands/bank'; import { bsCommand } from '../../src/mahoji/commands/bs'; import { buildCommand } from '../../src/mahoji/commands/build'; import { buyCommand } from '../../src/mahoji/commands/buy'; +import { caCommand } from '../../src/mahoji/commands/ca'; import { chooseCommand } from '../../src/mahoji/commands/choose'; import { chopCommand } from '../../src/mahoji/commands/chop'; import { claimCommand } from '../../src/mahoji/commands/claim'; import { clueCommand } from '../../src/mahoji/commands/clue'; +import { configCommand } from '../../src/mahoji/commands/config'; +import { cookCommand } from '../../src/mahoji/commands/cook'; +import { craftCommand } from '../../src/mahoji/commands/craft'; import { createCommand } from '../../src/mahoji/commands/create'; +import { dataCommand } from '../../src/mahoji/commands/data'; +import { dropCommand } from '../../src/mahoji/commands/drop'; +import { fakeCommand } from '../../src/mahoji/commands/fake'; +import { fakepmCommand } from '../../src/mahoji/commands/fakepm'; import { farmingCommand } from '../../src/mahoji/commands/farming'; import { fishCommand } from '../../src/mahoji/commands/fish'; import { fletchCommand } from '../../src/mahoji/commands/fletch'; +import { gambleCommand } from '../../src/mahoji/commands/gamble'; +import { gearCommand } from '../../src/mahoji/commands/gear'; +import { gearPresetsCommand } from '../../src/mahoji/commands/gearpresets'; +import { giftCommand } from '../../src/mahoji/commands/gift'; +import { giveawayCommand } from '../../src/mahoji/commands/giveaway'; import { gpCommand } from '../../src/mahoji/commands/gp'; +import { helpCommand } from '../../src/mahoji/commands/help'; import { huntCommand } from '../../src/mahoji/commands/hunt'; +import { inviteCommand } from '../../src/mahoji/commands/invite'; +import { minionKCommand } from '../../src/mahoji/commands/k'; import { lapsCommand } from '../../src/mahoji/commands/laps'; import { leaderboardCommand } from '../../src/mahoji/commands/leaderboard'; import { lightCommand } from '../../src/mahoji/commands/light'; import { lootCommand } from '../../src/mahoji/commands/loot'; +import { mCommand } from '../../src/mahoji/commands/m'; +import { massCommand } from '../../src/mahoji/commands/mass'; +import { mineCommand } from '../../src/mahoji/commands/mine'; import { minigamesCommand } from '../../src/mahoji/commands/minigames'; import { minionCommand } from '../../src/mahoji/commands/minion'; +import { mixCommand } from '../../src/mahoji/commands/mix'; +import { offerCommand } from '../../src/mahoji/commands/offer'; import { openCommand } from '../../src/mahoji/commands/open'; import { patreonCommand } from '../../src/mahoji/commands/patreon'; import { payCommand } from '../../src/mahoji/commands/pay'; import { pohCommand } from '../../src/mahoji/commands/poh'; +import { pollCommand } from '../../src/mahoji/commands/poll'; import { priceCommand } from '../../src/mahoji/commands/price'; import { raidCommand } from '../../src/mahoji/commands/raid'; +import { redeemCommand } from '../../src/mahoji/commands/redeem'; import { rollCommand } from '../../src/mahoji/commands/roll'; import { runecraftCommand } from '../../src/mahoji/commands/runecraft'; +import { sacrificeCommand } from '../../src/mahoji/commands/sacrifice'; +import { sellCommand } from '../../src/mahoji/commands/sell'; +import { simulateCommand } from '../../src/mahoji/commands/simulate'; import { slayerCommand } from '../../src/mahoji/commands/slayer'; import { smeltingCommand } from '../../src/mahoji/commands/smelt'; +import { smithCommand } from '../../src/mahoji/commands/smith'; import { stealCommand } from '../../src/mahoji/commands/steal'; +import { tksCommand } from '../../src/mahoji/commands/tokkulshop'; import { toolsCommand } from '../../src/mahoji/commands/tools'; -import { OSBMahojiCommand } from '../../src/mahoji/lib/util'; +import { tradeCommand } from '../../src/mahoji/commands/trade'; +import { triviaCommand } from '../../src/mahoji/commands/trivia'; +import { mahojiUseCommand } from '../../src/mahoji/commands/use'; import { randomMock } from './setup'; -import { createTestUser } from './util'; +import { createTestUser, mockClient, TestUser } from './util'; + +type CommandInput = Record; +async function generateCommandInputs(user: TestUser, options: readonly CommandOption[]): Promise { + let results: CommandInput[] = []; + const allPossibleOptions: Record = {}; -const commands: [OSBMahojiCommand, null | object][] = [ - [activitiesCommand, null], - [askCommand, null], - [bankCommand, null], - [bsCommand, null], - [clueCommand, null], - [claimCommand, null], - [farmingCommand, null], - [gpCommand, null], - [lapsCommand, null], - [leaderboardCommand, null], - [fletchCommand, null], - [fishCommand, null], - [createCommand, { item: 'asdf' }], - [chopCommand, null], - [chooseCommand, { list: 'a,a,a' }], - [buildCommand, null], - [buyCommand, null], - [huntCommand, null], - [lightCommand, null], - [lootCommand, null], - [minionCommand, null], - [minigamesCommand, null], - [runecraftCommand, { rune: 'blood rune' }], - [stealCommand, null], - [rollCommand, null], - [raidCommand, null], - [priceCommand, null], - [openCommand, null], - [patreonCommand, null], - [payCommand, { user: { user: { id: '2' } } }], - [pohCommand, null], - [slayerCommand, null], - [toolsCommand, null], - [stealCommand, null], - [smeltingCommand, null] -]; + for (const option of options) { + switch (option.type) { + case ApplicationCommandOptionType.SubcommandGroup: + case ApplicationCommandOptionType.Subcommand: + if (option.options) { + const subOptionsResults = await generateCommandInputs(user, option.options); + results.push(...subOptionsResults.map(input => ({ [option.name]: input }))); + } + break; + case ApplicationCommandOptionType.String: + if ('autocomplete' in option && option.autocomplete) { + const autoCompleteResults = await option.autocomplete('', { id: user.id } as any, {} as any); + allPossibleOptions[option.name] = shuffleArr(autoCompleteResults.map(c => c.value)).slice(0, 3); + } else if (option.choices) { + allPossibleOptions[option.name] = option.choices.map(c => c.value).slice(0, 3); + } else if (['guild_id', 'message_id'].includes(option.name)) { + allPossibleOptions[option.name] = ['157797566833098752']; + } else { + allPossibleOptions[option.name] = ['plain string']; + } + break; + case ApplicationCommandOptionType.Integer: + case ApplicationCommandOptionType.Number: + if (option.choices) { + allPossibleOptions[option.name] = option.choices.map(c => c.value); + } else { + let value = randInt(1, 10); + if (option.min_value && option.max_value) { + value = randInt(option.min_value, option.max_value); + } + allPossibleOptions[option.name] = [option.min_value, value]; + } + break; + case ApplicationCommandOptionType.Boolean: { + allPossibleOptions[option.name] = [true, false]; + break; + } + case ApplicationCommandOptionType.User: { + allPossibleOptions[option.name] = [ + { + user: { + id: '425134194436341760', + username: 'username', + bot: false + }, + member: undefined + } + ]; + break; + } + case ApplicationCommandOptionType.Channel: + case ApplicationCommandOptionType.Role: + case ApplicationCommandOptionType.Mentionable: + // results.push({ ...currentPath, [option.name]: `Any ${option.type}` }); + break; + } + } -// Don't let any of these commands create an activity -vi.mock('../../src/lib/util/addSubTaskToActivityTask', async () => { - const actual: any = await vi.importActual('../../src/lib/util/addSubTaskToActivityTask'); - return { - ...actual, - default: async (args: any) => { - console.log(`Sending ${args}`); + const sorted = Object.values(allPossibleOptions).sort((a, b) => b.length - a.length); + const longestOptions = sorted[0]?.length; + for (let i = 0; i < longestOptions; i++) { + let obj: Record = {}; + for (const [key, val] of Object.entries(allPossibleOptions)) { + obj[key] = val[i] ?? randArrItem(val); } - }; -}); + results.push(obj); + } + return results; +} + +const bank = new Bank(); +for (const item of Items.array()) { + bank.add(item.id, 100_000_000); +} -describe('All Commands Base Test', async () => { - randomMock(); - const user = await createTestUser(); - for (const [command, options] of commands) { - test(`Run ${command.name} command`, async () => { - await user.runCommand(command, options ?? {}); +test.skip( + 'All Commands Base Test', + async () => { + expect(vi.isMockFunction(handleMahojiConfirmation)).toBe(true); + const client = await mockClient(); + process.env.CLIENT_ID = client.data.id; + randomMock(); + const maxUser = await createTestUser(bank, { GP: 100_000_000_000 }); + await maxUser.max(); + await maxUser.update({ bitfield: [BitField.isModerator] }); + const store = new Store({ name: 'commands', dirs: [join('dist', 'mahoji')], checker: isValidCommand }); + await store.load(); + const currentClientSettings = await mahojiClientSettingsFetch({ construction_cost_bank: true }); + await prisma.activity.deleteMany({ + where: { + user_id: BigInt(maxUser.id) + } }); + + const ignoredCommands = [ + 'leagues', + 'bank', + 'bingo', + 'bossrecords', + 'stats', + 'clues', + 'kc', + 'simulate', + 'lvl', + 'testpotato', + 'xp', + 'wiki', + 'casket', + 'finish', + 'kill', + 'trivia', + 'ge', + 'rp', + 'cl', + 'bsominigames', + 'completion', + 'dg', + 'invention', + 'divination', + 'droprate', + 'ic', + 'kibble', + 'lottery', + 'megaduck', + 'nursery', + 'tames', + 'farming' + ]; + const cmds = [ + adminCommand, + askCommand, + bsCommand, + buildCommand, + buyCommand, + caCommand, + chooseCommand, + chopCommand, + cookCommand, + clueCommand, + configCommand, + claimCommand, + mCommand, + gpCommand, + payCommand, + craftCommand, + fishCommand, + farmingCommand, + dropCommand, + createCommand, + activitiesCommand, + dataCommand, + fakeCommand, + fakepmCommand, + fletchCommand, + gambleCommand, + gearCommand, + gearPresetsCommand, + giveawayCommand, + helpCommand, + huntCommand, + giftCommand, + inviteCommand, + minionKCommand, + lapsCommand, + leaderboardCommand, + lightCommand, + mineCommand, + massCommand, + minigamesCommand, + minionCommand, + simulateCommand, + sellCommand, + sacrificeCommand, + rollCommand, + runecraftCommand, + raidCommand, + pollCommand, + pohCommand, + priceCommand, + openCommand, + offerCommand, + mixCommand, + lootCommand, + smeltingCommand, + slayerCommand, + redeemCommand, + patreonCommand, + smithCommand, + stealCommand, + tradeCommand, + triviaCommand, + toolsCommand, + tksCommand, + mahojiUseCommand + ]; + for (const command of store.values) { + if (ignoredCommands.includes(command.name)) continue; + if (cmds.some(c => c.name === command.name)) continue; + // throw new Error( + // `If you added a new command (${command.name}), you need to put it in the allCommandsBase.test.ts file.` + // ); + } + + const ignoredSubCommands = [ + ['tools', 'patron', 'cl_bank'], + ['loot', 'view'], + ['minion', 'bankbg'] + ]; + + for (const command of cmds) { + if (ignoredCommands.includes(command.name)) continue; + const options = await generateCommandInputs(maxUser, command.options!); + outer: for (const option of options) { + for (const [parent, sub, subCommand] of ignoredSubCommands) { + if (command.name === parent && option[sub] && (subCommand ? option[sub][subCommand] : true)) { + continue outer; + } + } + try { + const res = await maxUser.runCommand(command, option); + minionActivityCache.clear(); + // console.log(`Running command ${command.name} + // Options: ${JSON.stringify(option)} + // Result: ${JSON.stringify(res).slice(0, 100)}`); + } catch (err) { + console.error( + `Failed to run command ${command.name} with options ${JSON.stringify(option)}: ${err}` + ); + throw err; + } + } + } + + await client.processActivities(); + }, + { + timeout: Time.Minute * 10 } -}); +); diff --git a/tests/integration/grandExchange.test.ts b/tests/integration/grandExchange.test.ts index 5f09a583c53..243f4a74496 100644 --- a/tests/integration/grandExchange.test.ts +++ b/tests/integration/grandExchange.test.ts @@ -69,12 +69,12 @@ describe('Grand Exchange', async () => { console.log(`Finished initializing ${amountOfUsers} users`); // Run a bunch of commands to buy/sell - const commandPromises = new PQueue({ concurrency: 10 }); + const commandPromises = new PQueue({ concurrency: 20 }); for (const user of shuffleArr(users)) { - const method = randArrItem(['buy', 'sell']); - let quantity = randArrItem(quantities); - let price = randArrItem(prices); commandPromises.add(async () => { + const method = randArrItem(['buy', 'sell']); + let quantity = randArrItem(quantities); + let price = randArrItem(prices); for (const item of itemPool) { await user.runCommand(geCommand, { [method]: { @@ -94,10 +94,7 @@ describe('Grand Exchange', async () => { for (let i = 0; i < 100; i++) { await GrandExchange.tick(); await waitForGEToBeEmpty(); - await Promise.all([ - GrandExchange.checkGECanFullFilAllListings(), - GrandExchange.extensiveVerification() - ]); + await GrandExchange.extensiveVerification(); } await waitForGEToBeEmpty(); console.log('Finished ticking 100 times'); @@ -154,7 +151,6 @@ Based on G.E data, we should have received ${data.totalTax} tax`; assert(GrandExchange.queue.size === 0, 'Queue should be empty'); }, { - repeats: 1, timeout: Time.Minute * 5 } ); diff --git a/tests/integration/memoryHarvesting.bso.test.ts b/tests/integration/memoryHarvesting.bso.test.ts index 751ac6d9708..ebdfbe4c329 100644 --- a/tests/integration/memoryHarvesting.bso.test.ts +++ b/tests/integration/memoryHarvesting.bso.test.ts @@ -3,7 +3,9 @@ import { ItemBank } from 'oldschooljs/dist/meta/types'; import { afterEach, beforeEach, describe, expect, test } from 'vitest'; import { MemoryHarvestType } from '../../src/lib/bso/divination'; +import { convertStoredActivityToFlatActivity, prisma } from '../../src/lib/settings/prisma'; import { Gear } from '../../src/lib/structures/Gear'; +import { processPendingActivities } from '../../src/lib/Task'; import { MemoryHarvestOptions } from '../../src/lib/types/minions'; import itemID from '../../src/lib/util/itemID'; import { divinationCommand } from '../../src/mahoji/commands/divination'; @@ -32,8 +34,15 @@ describe('Divination', async () => { energy: 'Pale' } }); - const activity = await user.runActivity(); + await processPendingActivities(); await user.sync(); + const _activity = await prisma.activity.findFirst({ + where: { + user_id: BigInt(user.id), + type: 'MemoryHarvest' + } + }); + const activity = convertStoredActivityToFlatActivity(_activity!) as MemoryHarvestOptions; expect(user.skillsAsXP.divination).toBeGreaterThan(1); expect(user.skillsAsLevels.divination).toEqual(36); expect(activity.dp).toEqual(false); @@ -55,8 +64,15 @@ describe('Divination', async () => { type: MemoryHarvestType.ConvertToEnergy } }); - const activity = await user.runActivity(); + await processPendingActivities(); await user.sync(); + const _activity = await prisma.activity.findFirst({ + where: { + user_id: BigInt(user.id), + type: 'MemoryHarvest' + } + }); + const activity = convertStoredActivityToFlatActivity(_activity!) as MemoryHarvestOptions; expect(user.skillsAsXP.divination).toBeGreaterThan(1); expect(user.skillsAsLevels.divination).toEqual(32); expect(activity.dp).toEqual(false); diff --git a/tests/integration/migrateUser.test.ts b/tests/integration/migrateUser.test.ts index 8de51877d0b..f94f0bb254e 100644 --- a/tests/integration/migrateUser.test.ts +++ b/tests/integration/migrateUser.test.ts @@ -29,7 +29,6 @@ import { describe, expect, test, vi } from 'vitest'; import { BitField } from '../../src/lib/constants'; import { GearSetupType, UserFullGearSetup } from '../../src/lib/gear/types'; -import { GrandExchange } from '../../src/lib/grandExchange'; import { trackLoot } from '../../src/lib/lootTrack'; import { incrementMinigameScore, MinigameName } from '../../src/lib/settings/minigames'; import { prisma } from '../../src/lib/settings/prisma'; @@ -1415,9 +1414,6 @@ describe('migrate user test', async () => { } }; - await GrandExchange.totalReset(); - await GrandExchange.init(); - test('test preventing a double (clobber) robochimp migration (two bot-migration)', async () => { const sourceUserId = mockedId(); const destUserId = mockedId(); diff --git a/tests/integration/mocks.ts b/tests/integration/mocks.ts new file mode 100644 index 00000000000..6508511fc7d --- /dev/null +++ b/tests/integration/mocks.ts @@ -0,0 +1,41 @@ +import { Image } from '@napi-rs/canvas'; +import { beforeEach, vi } from 'vitest'; + +import { BankImageTask } from '../../src/lib/bankImage'; + +vi.mock('../../src/lib/util/handleMahojiConfirmation.ts', () => ({ + handleMahojiConfirmation: vi.fn() +})); +vi.mock('../../src/lib/util/interactionReply', () => ({ + deferInteraction: vi.fn(), + interactionReply: vi.fn() +})); + +vi.mock('../../src/lib/util/interactionReply', () => ({ + deferInteraction: vi.fn(), + interactionReply: vi.fn() +})); + +const mockBankImageTask = { + init: vi.fn(), + run: vi.fn(), + generateBankImage: vi.fn().mockReturnValue(Promise.resolve({ image: Buffer.from(''), isTransparent: false })), + getItemImage: vi.fn().mockReturnValue(Promise.resolve(new Image())), + fetchAndCacheImage: vi.fn().mockReturnValue(Promise.resolve(new Image())) +}; + +global.bankImageGenerator = mockBankImageTask as any; +BankImageTask.prototype.init = mockBankImageTask.init; +BankImageTask.prototype.run = mockBankImageTask.init; +BankImageTask.prototype.generateBankImage = mockBankImageTask.generateBankImage; +BankImageTask.prototype.getItemImage = mockBankImageTask.getItemImage; +BankImageTask.prototype.fetchAndCacheImage = mockBankImageTask.fetchAndCacheImage; + +beforeEach(async () => { + global.bankImageGenerator = mockBankImageTask as any; + BankImageTask.prototype.init = mockBankImageTask.init; + BankImageTask.prototype.run = mockBankImageTask.init; + BankImageTask.prototype.generateBankImage = mockBankImageTask.generateBankImage; + BankImageTask.prototype.getItemImage = mockBankImageTask.getItemImage; + BankImageTask.prototype.fetchAndCacheImage = mockBankImageTask.fetchAndCacheImage; +}); diff --git a/tests/integration/monsterKilling.bso.test.ts b/tests/integration/monsterKilling.bso.test.ts index 8d4276970e9..9c74c732f88 100644 --- a/tests/integration/monsterKilling.bso.test.ts +++ b/tests/integration/monsterKilling.bso.test.ts @@ -5,7 +5,7 @@ import { convertStoredActivityToFlatActivity } from '../../src/lib/settings/pris import { Gear } from '../../src/lib/structures/Gear'; import { processPendingActivities } from '../../src/lib/Task'; import { MonsterActivityTaskOptions } from '../../src/lib/types/minions'; -import { killCommand } from '../../src/mahoji/commands/k'; +import { minionKCommand } from '../../src/mahoji/commands/k'; import { giveMaxStats } from '../../src/mahoji/commands/testpotato'; import { createTestUser, mockClient } from './util'; @@ -33,12 +33,19 @@ test('Killing Vlad', async () => { skills_hitpoints: 200_000_000 }); - await user.runCommand(killCommand, { + await user.runCommand(minionKCommand, { name: 'vladimir drakan' }); - const [finishedActivity] = await processPendingActivities(); - const data = convertStoredActivityToFlatActivity(finishedActivity) as MonsterActivityTaskOptions; + await processPendingActivities(); + await user.sync(); + const _activity = await global.prisma!.activity.findFirst({ + where: { + user_id: BigInt(user.id), + type: 'MonsterKilling' + } + }); + const data = convertStoredActivityToFlatActivity(_activity!) as MonsterActivityTaskOptions; const quantityKilled = data.quantity; expect(user.bank.amount('Shark')).toBeLessThan(1_000_000); diff --git a/tests/integration/monsterKilling.test.ts b/tests/integration/monsterKilling.test.ts index 41e2d8d9293..fafe856aafd 100644 --- a/tests/integration/monsterKilling.test.ts +++ b/tests/integration/monsterKilling.test.ts @@ -1,21 +1,20 @@ import { Bank } from 'oldschooljs'; import { expect, test } from 'vitest'; -import { MonsterActivityTaskOptions } from '../../src/lib/types/minions'; -import { killCommand } from '../../src/mahoji/commands/k'; +import { minionKCommand } from '../../src/mahoji/commands/k'; import { createTestUser, mockClient } from './util'; test('Killing Men', async () => { - await mockClient(); + const client = await mockClient(); const user = await createTestUser(); const startingBank = new Bank().add('Shark', 1_000_000); await user.addItemsToBank({ items: startingBank }); await user.max(); - await user.runCommand(killCommand, { + await user.runCommand(minionKCommand, { name: 'general graardor' }); - (await user.runActivity()) as MonsterActivityTaskOptions; + await client.processActivities(); await user.sync(); expect(user.bank.amount('Shark')).toBeLessThan(1_000_000); diff --git a/tests/integration/setup.ts b/tests/integration/setup.ts index b75f8b50a92..6484b5e5775 100644 --- a/tests/integration/setup.ts +++ b/tests/integration/setup.ts @@ -1,16 +1,14 @@ import '../globalSetup'; +import './mocks'; +import { Image } from '@napi-rs/canvas'; +import { noOp } from 'e'; +import mitm from 'mitm'; import { afterEach, beforeEach, vi } from 'vitest'; +import { BankImageTask, bankImageTask } from '../../src/lib/bankImage'; import { prisma } from '../../src/lib/settings/prisma'; -vi.mock('../../src/lib/util/handleMahojiConfirmation', () => ({ - handleMahojiConfirmation: vi.fn() -})); -vi.mock('../../src/lib/util/interactionReply', () => ({ - deferInteraction: vi.fn() -})); - export function randomMock(random = 0.1) { Math.random = () => random; } @@ -19,7 +17,15 @@ vi.mock('../../src/lib/util/webhook', async () => { const actual: any = await vi.importActual('../../src/lib/util/webhook'); return { ...actual, - sendToChannelID: async (_args: any) => {} + sendToChannelID: vi.fn() + }; +}); + +vi.mock('../../src/lib/gear/functions/generateGearImage', async () => { + const actual: any = await vi.importActual('../../src/lib/gear/functions/generateGearImage'); + return { + ...actual, + generateGearImage: vi.fn().mockReturnValue(Promise.resolve(Buffer.from(''))) }; }); @@ -40,8 +46,30 @@ globalClient.fetchUser = async (id: string | bigint) => ({ send: async () => {} }); +const mockBankImageTask = { + init: vi.fn(), + run: vi.fn(), + generateBankImage: vi.fn().mockReturnValue(Promise.resolve({ image: Buffer.from(''), isTransparent: false })), + getItemImage: vi.fn().mockReturnValue(Promise.resolve(new Image())), + fetchAndCacheImage: vi.fn().mockReturnValue(Promise.resolve(new Image())), + backgroundImages: [] +}; +bankImageTask.fetchAndCacheImage = mockBankImageTask.fetchAndCacheImage; +global.bankImageGenerator = mockBankImageTask as any; +BankImageTask.prototype.init = mockBankImageTask.init; +BankImageTask.prototype.run = mockBankImageTask.init; +BankImageTask.prototype.generateBankImage = mockBankImageTask.generateBankImage; +BankImageTask.prototype.getItemImage = mockBankImageTask.getItemImage; +BankImageTask.prototype.fetchAndCacheImage = mockBankImageTask.fetchAndCacheImage; + beforeEach(async () => { await prisma.$connect(); + global.bankImageGenerator = mockBankImageTask as any; + BankImageTask.prototype.init = mockBankImageTask.init; + BankImageTask.prototype.run = mockBankImageTask.init; + BankImageTask.prototype.generateBankImage = mockBankImageTask.generateBankImage; + BankImageTask.prototype.getItemImage = mockBankImageTask.getItemImage; + BankImageTask.prototype.fetchAndCacheImage = mockBankImageTask.fetchAndCacheImage; }); afterEach(async () => { @@ -49,7 +77,20 @@ afterEach(async () => { }); async function init() { - await prisma.$queryRaw`CREATE EXTENSION IF NOT EXISTS intarray;`; + await prisma.$queryRaw`CREATE EXTENSION IF NOT EXISTS intarray;`.catch(noOp); } init(); + +function setupRequestLogging() { + const mitmInstance = mitm(); + + mitmInstance.on('connect', (socket, opts) => { + if (opts?.host) { + // throw new Error(`Sending request to ${opts.host}`); + socket.bypass(); + } + }); +} + +setupRequestLogging(); diff --git a/tests/integration/trading.test.ts b/tests/integration/trading.test.ts index ac19db7f0e4..31b9d56d2b6 100644 --- a/tests/integration/trading.test.ts +++ b/tests/integration/trading.test.ts @@ -27,9 +27,9 @@ test('Trade consistency', async () => { checkMatch(); - for (let i = 0; i < 3; i++) { - const promises = []; + const promises = []; + for (let i = 0; i < 3; i++) { for (const user of shuffleArr(users)) { const other = randArrItem(users); const method = randArrItem(['send', 'receive', 'both']); diff --git a/tests/integration/util.ts b/tests/integration/util.ts index f06d9a51072..bfd3c337ea9 100644 --- a/tests/integration/util.ts +++ b/tests/integration/util.ts @@ -5,10 +5,9 @@ import { Bank } from 'oldschooljs'; import { globalConfig } from '../../src/lib/constants'; import { MUserClass } from '../../src/lib/MUser'; -import { convertStoredActivityToFlatActivity, prisma } from '../../src/lib/settings/prisma'; +import { prisma } from '../../src/lib/settings/prisma'; import { processPendingActivities } from '../../src/lib/Task'; import { ItemBank } from '../../src/lib/types'; -import { ActivityTaskOptions } from '../../src/lib/types/minions'; import { cryptoRand } from '../../src/lib/util'; import { giveMaxStats } from '../../src/mahoji/commands/testpotato'; import { ironmanCommand } from '../../src/mahoji/lib/abstracted_commands/ironmanCommand'; @@ -22,6 +21,7 @@ export const commandRunOptions = (userID: string): Omit Promise.resolve(), editReply: () => Promise.resolve(), followUp: () => Promise.resolve() @@ -103,18 +103,6 @@ export class TestUser extends MUserClass { return this; } - async runActivity(): Promise { - const [finishedActivity] = await processPendingActivities(); - if (!finishedActivity) { - throw new Error('runActivity: No activity was ran'); - } - if (finishedActivity.user_id.toString() !== this.id) { - throw new Error('runActivity: Ran activity, but it didnt belong to this user'); - } - const data = convertStoredActivityToFlatActivity(finishedActivity); - return data as any; - } - randomBankSubset() { const bank = new Bank(); const items = shuffleArr(this.bankWithGP.items()).slice(0, randInt(0, this.bankWithGP.length)); @@ -128,7 +116,7 @@ export class TestUser extends MUserClass { const idsUsed = new Set(); export function mockedId() { - return cryptoRand(1_000_000_000, 5_000_000_000_000).toString(); + return cryptoRand(1_000_000_000_000, 5_000_000_000_000).toString(); } export async function createTestUser(bank?: Bank, userData: Partial = {}) { @@ -187,6 +175,10 @@ class TestClient { throw new Error(`Expected ${key} to be ${value} but got ${this.data[key]}`); } } + + async processActivities() { + await processPendingActivities(); + } } export async function mockClient() { diff --git a/tests/unit/images.test.ts b/tests/unit/images.test.ts index 2ed5aff1fb3..4766dccfaea 100644 --- a/tests/unit/images.test.ts +++ b/tests/unit/images.test.ts @@ -23,7 +23,7 @@ const toMatchImageSnapshotPlugin = configureToMatchImageSnapshot({ }); expect.extend({ toMatchImageSnapshot: toMatchImageSnapshotPlugin }); -describe('Images', () => { +describe.skip('Images', () => { test('Chat Heads', async () => { const result = await mahojiChatHead({ content: diff --git a/tests/unit/snapshots/banksnapshots.test.ts.snap b/tests/unit/snapshots/banksnapshots.test.ts.snap index 4fac0ec43a9..e98f859b7a6 100644 --- a/tests/unit/snapshots/banksnapshots.test.ts.snap +++ b/tests/unit/snapshots/banksnapshots.test.ts.snap @@ -965,6 +965,20 @@ exports[`BSO Buyables 1`] = ` "outputItems": undefined, "qpRequired": 172, }, + { + "gpCost": 5000, + "ironmanPrice": 500, + "itemCost": Bank { + "bank": {}, + "frozen": false, + }, + "name": "Lockpick", + "outputItems": undefined, + "skillsNeeded": { + "agility": 50, + "thieving": 50, + }, + }, { "itemCost": Bank { "bank": { @@ -9922,6 +9936,24 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, }, + { + "GPCost": 50000, + "cantHaveItems": undefined, + "inputItems": Bank { + "bank": { + "12783": 1, + "2572": 1, + }, + "frozen": false, + }, + "name": "Ring of wealth (i)", + "outputItems": Bank { + "bank": { + "12785": 1, + }, + "frozen": false, + }, + }, { "cantHaveItems": undefined, "inputItems": Bank { @@ -24041,7 +24073,7 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, "requiredSlayerUnlocks": [ - 54, + 56, ], }, { @@ -24062,7 +24094,7 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, "requiredSlayerUnlocks": [ - 54, + 56, ], }, { @@ -24083,7 +24115,7 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, "requiredSlayerUnlocks": [ - 54, + 56, ], }, { @@ -24104,7 +24136,7 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, "requiredSlayerUnlocks": [ - 54, + 56, ], }, { @@ -24125,7 +24157,7 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, "requiredSlayerUnlocks": [ - 54, + 56, ], }, { @@ -24146,7 +24178,7 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, "requiredSlayerUnlocks": [ - 54, + 56, ], }, { @@ -24167,7 +24199,7 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, "requiredSlayerUnlocks": [ - 54, + 56, ], }, { @@ -24188,7 +24220,7 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, "requiredSlayerUnlocks": [ - 54, + 56, ], }, { @@ -24209,7 +24241,7 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, "requiredSlayerUnlocks": [ - 54, + 56, ], }, { @@ -24230,7 +24262,7 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, "requiredSlayerUnlocks": [ - 54, + 56, ], }, { @@ -24251,7 +24283,7 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, "requiredSlayerUnlocks": [ - 54, + 56, ], }, { @@ -24272,7 +24304,7 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, "requiredSlayerUnlocks": [ - 54, + 56, ], }, { @@ -24293,7 +24325,7 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, "requiredSlayerUnlocks": [ - 54, + 56, ], }, { @@ -24314,7 +24346,7 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, "requiredSlayerUnlocks": [ - 54, + 56, ], }, { @@ -24335,7 +24367,7 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, "requiredSlayerUnlocks": [ - 54, + 56, ], }, { @@ -24356,7 +24388,7 @@ exports[`BSO Creatables 1`] = ` "frozen": false, }, "requiredSlayerUnlocks": [ - 54, + 56, ], }, { diff --git a/tests/unit/snapshots/clsnapshots.test.ts.snap b/tests/unit/snapshots/clsnapshots.test.ts.snap index de5af7c1dce..86a1cf0e463 100644 --- a/tests/unit/snapshots/clsnapshots.test.ts.snap +++ b/tests/unit/snapshots/clsnapshots.test.ts.snap @@ -40,7 +40,7 @@ Cooking (28) Corporeal Beast (8) Crafting (175) Crazy archaeologist (3) -Creatables (750) +Creatables (751) Creature Creation (7) Custom Pets (47) Custom Pets (Discontinued) (20) @@ -145,7 +145,7 @@ Shooting Stars (2) Skilling Misc (41) Skilling Pets (8) Skotizo (6) -Slayer (80) +Slayer (83) Slayer Masks/Helms (32) Smithing (200) Solis (3) @@ -758,6 +758,9 @@ Cyclops head Daemonheim agility pass Dagannoth mask Dagannoth slayer helm +Dagon'hai hat +Dagon'hai robe bottom +Dagon'hai robe top Dark acorn Dark beast mask Dark beast slayer helm diff --git a/tests/unit/utils.ts b/tests/unit/utils.ts index 223b4e51b48..345c1083f12 100644 --- a/tests/unit/utils.ts +++ b/tests/unit/utils.ts @@ -35,7 +35,7 @@ interface MockUserArgs { export const mockUser = (overrides?: MockUserArgs): User => { const gearMelee = filterGearSetup(overrides?.meleeGear); const cl = new Bank().add(overrides?.cl ?? {}); - return { + const r = { cl, gear_fashion: new Gear().raw() as Prisma.JsonValue, gear_mage: new Gear().raw() as Prisma.JsonValue, @@ -72,7 +72,7 @@ export const mockUser = (overrides?: MockUserArgs): User => { skills_dungeoneering: 0, skills_invention: 0, skills_hitpoints: overrides?.skills_hitpoints ?? convertLVLtoXP(10), - GP: overrides?.GP, + GP: overrides?.GP ?? 0, premium_balance_tier: overrides?.premium_balance_tier, premium_balance_expiry_date: overrides?.premium_balance_expiry_date, ironman_alts: [], @@ -85,6 +85,8 @@ export const mockUser = (overrides?: MockUserArgs): User => { monsterScores: {}, skills_divination: 0 } as unknown as User; + + return r; }; class TestMUser extends MUserClass { @@ -122,6 +124,9 @@ export async function testRunCmd({ Math.random = () => 0.5; const hash = murmurhash(JSON.stringify({ name: cmd.name, opts, user })).toString(); const mockedUser = mockMUser({ id: hash, ...user }); + if (mockedUser.GP === null || Number.isNaN(mockedUser.GP) || mockedUser.GP < 0 || mockedUser.GP === undefined) { + throw new Error(`Invalid GP for user ${hash}`); + } mockUserMap.set(hash, mockedUser); const options: any = { user: mockedUser.user, diff --git a/vitest.integration.config.mts b/vitest.integration.config.mts index 6b77ff01405..1a349326db2 100644 --- a/vitest.integration.config.mts +++ b/vitest.integration.config.mts @@ -12,8 +12,8 @@ export default defineConfig({ }, testTimeout: 30_000, bail: 1, - maxConcurrency: 1, - maxWorkers: 1, + maxConcurrency: 5, + maxWorkers: 5, minWorkers: 1, pool: 'forks' } diff --git a/vitest.unit.config.mts b/vitest.unit.config.mts index 4d8e0dce89d..aeb6a9abaf4 100644 --- a/vitest.unit.config.mts +++ b/vitest.unit.config.mts @@ -13,6 +13,9 @@ export default defineConfig({ }, setupFiles: 'tests/unit/setup.ts', resolveSnapshotPath: (testPath, extension) => - join(join(dirname(testPath), 'snapshots'), `${basename(testPath)}${extension}`) + join(join(dirname(testPath), 'snapshots'), `${basename(testPath)}${extension}`), + maxConcurrency: 10, + maxWorkers: 5, + minWorkers: 1 } }); diff --git a/yarn.lock b/yarn.lock index cea7ae1625a..b2b2d7239e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -990,6 +990,13 @@ dependencies: "@types/node" "*" +"@types/mitm@^1.3.8": + version "1.3.8" + resolved "https://registry.yarnpkg.com/@types/mitm/-/mitm-1.3.8.tgz#0acc7b82c1afd223828d445b1b101f86d6680ae8" + integrity sha512-vIXvHF4F9vJZOi9r/nOyaWFW0Y2MLcoaokN/GrZ3LYE0c2g7Y2kKpJ36hKT3EeJwzDsYZKjYyUEiCqDzSG/h9g== + dependencies: + "@types/node" "*" + "@types/node-cron@^3.0.7": version "3.0.7" resolved "https://registry.yarnpkg.com/@types/node-cron/-/node-cron-3.0.7.tgz#978bf75f7247385c61d23b6a060ba9eedb03e2f4" @@ -3865,6 +3872,14 @@ minipass@^7.0.4: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.0.tgz#b545f84af94e567386770159302ca113469c80b8" integrity sha512-oGZRv2OT1lO2UF1zUcwdTb3wqUwI0kBGTgt/T7OdSj6M6N5m3o5uPf0AIW6lVxGGoiWUR7e2AwTE+xiwK8WQig== +mitm@^1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/mitm/-/mitm-1.7.2.tgz#d079c44c763a333b15a0f7bfd02446fb8dbbe8e8" + integrity sha512-SuiJbc5xisP/iUYvsKAvrvPeoyJQbYI3WOfnp8A7XHDn4wkdtmGZe2ZTFXIo3K1of05oxUiaJIK+GoAU5KgFOw== + dependencies: + semver ">= 5 < 6" + underscore ">= 1.1.6 < 1.14" + mlly@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.2.1.tgz#cd50151f5712b651c5c379085157bcdff661133b" @@ -4785,7 +4800,7 @@ seedrandom@^3.0.5: resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7" integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg== -"semver@2 || 3 || 4 || 5": +"semver@2 || 3 || 4 || 5", "semver@>= 5 < 6": version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== @@ -4998,7 +5013,16 @@ stream-to-array@^2.3.0: dependencies: any-promise "^1.1.0" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -5050,7 +5074,14 @@ stringify-object@^3.2.1: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -5356,6 +5387,11 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +"underscore@>= 1.1.6 < 1.14": + version "1.13.6" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441" + integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A== + undici@5.27.2: version "5.27.2" resolved "https://registry.yarnpkg.com/undici/-/undici-5.27.2.tgz#a270c563aea5b46cc0df2550523638c95c5d4411" @@ -5549,7 +5585,16 @@ word-wrap@^1.2.3, word-wrap@^1.2.5, word-wrap@~1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== From b8d04e1dbf9e1527d818094f79e3dd413601cdeb Mon Sep 17 00:00:00 2001 From: nwjgit <69014816+nwjgit@users.noreply.github.com> Date: Fri, 14 Jun 2024 04:33:35 -0500 Subject: [PATCH 017/249] A few osb cl fixes (#5909) --- src/lib/data/CollectionsExport.ts | 7 ++++--- tests/unit/snapshots/clsnapshots.test.ts.snap | 5 ++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/lib/data/CollectionsExport.ts b/src/lib/data/CollectionsExport.ts index 2ac2ab37ee7..a383c195259 100644 --- a/src/lib/data/CollectionsExport.ts +++ b/src/lib/data/CollectionsExport.ts @@ -343,13 +343,14 @@ export const grotesqueGuardiansCL = resolveItems([ 'Granite dust' ]); export const hesporiCL = resolveItems(['Bottomless compost bucket', 'Iasor seed', 'Kronos seed', 'Attas seed']); -export const theInfernoCL = resolveItems(['Jal-nib-rek', 'Infernal cape', 'Tokkul']); +export const theInfernoCL = resolveItems(['Jal-nib-rek', 'Infernal cape']); export const kalphiteQueenCL = resolveItems([ 'Kalphite princess', 'Kq head', 'Jar of sand', 'Dragon 2h sword', - 'Dragon chainbody' + 'Dragon chainbody', + 'Dragon pickaxe' ]); export const kingBlackDragonCL = resolveItems([ 'Prince black dragon', @@ -391,12 +392,12 @@ export const spiritAnglerOutfit = resolveItems([ export const temporossCL = resolveItems([ 'Tiny tempor', 'Big harpoonfish', + ...spiritAnglerOutfit, 'Tome of water (empty)', 'Soaked page', 'Tackle box', 'Fish barrel', 'Dragon harpoon', - ...spiritAnglerOutfit, 'Spirit flakes' ]); export const thermonuclearSmokeDevilCL = resolveItems([ diff --git a/tests/unit/snapshots/clsnapshots.test.ts.snap b/tests/unit/snapshots/clsnapshots.test.ts.snap index 28191f3d0b5..328e187c4be 100644 --- a/tests/unit/snapshots/clsnapshots.test.ts.snap +++ b/tests/unit/snapshots/clsnapshots.test.ts.snap @@ -53,7 +53,7 @@ Hard Treasure Trails (134) Hespori (4) Implings (12) K'ril Tsutsaroth (8) -Kalphite Queen (5) +Kalphite Queen (6) King Black Dragon (4) Kraken (4) Kree'arra (8) @@ -94,7 +94,7 @@ Tempoross (12) The Fight Caves (2) The Forgotten Four (40) The Gauntlet (5) -The Inferno (3) +The Inferno (2) The Leviathan (10) The Nightmare (12) The Whisperer (10) @@ -1419,7 +1419,6 @@ The stuff Thieving bag Thread of elidinis Tiny tempor -Tokkul Toktz-ket-xil Toktz-mej-tal Toktz-xil-ak From e10c7eb3c6d07f54530ee827b63c30108dae1a86 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 11:25:33 +1000 Subject: [PATCH 018/249] Bump braces from 3.0.2 to 3.0.3 (#5923) Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3. - [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md) - [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3) --- updated-dependencies: - dependency-name: braces dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index 27e4ed785ca..0ccda17eba4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1479,11 +1479,11 @@ brace-expansion@^2.0.1: balanced-match "^1.0.0" braces@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: - fill-range "^7.0.1" + fill-range "^7.1.1" buffer@^5.5.0: version "5.7.1" @@ -2659,10 +2659,10 @@ filing-cabinet@^4.1.6: tsconfig-paths "^4.2.0" typescript "^5.0.4" -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" From 627e68fdc20992a852dcd20622cd2a371296d5e7 Mon Sep 17 00:00:00 2001 From: nwjgit <69014816+nwjgit@users.noreply.github.com> Date: Sun, 16 Jun 2024 20:27:41 -0500 Subject: [PATCH 019/249] Add quantity owned to ge sell autocomplete (#5919) --- src/mahoji/commands/ge.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/mahoji/commands/ge.ts b/src/mahoji/commands/ge.ts index 1336716b9dc..4467b00f3fd 100644 --- a/src/mahoji/commands/ge.ts +++ b/src/mahoji/commands/ge.ts @@ -17,7 +17,7 @@ import { handleMahojiConfirmation } from '../../lib/util/handleMahojiConfirmatio import { deferInteraction } from '../../lib/util/interactionReply'; import itemIsTradeable from '../../lib/util/itemIsTradeable'; import { cancelGEListingCommand } from '../lib/abstracted_commands/cancelGEListingCommand'; -import { itemOption, ownedItemOption, tradeableItemArr } from '../lib/mahojiCommandOptions'; +import { itemOption, tradeableItemArr } from '../lib/mahojiCommandOptions'; import { OSBMahojiCommand } from '../lib/util'; export type GEListingWithTransactions = GEListing & { @@ -130,10 +130,19 @@ export const geCommand: OSBMahojiCommand = { description: 'Sell something on the grand exchange.', options: [ { - ...ownedItemOption(item => Boolean(item.tradeable_on_ge)), name: 'item', + type: ApplicationCommandOptionType.String, description: 'The item you want to sell.', - required: true + required: true, + autocomplete: async (value, { id }) => { + const user = await mUserFetch(id); + + return user.bank + .items() + .filter(i => i[0].tradeable_on_ge) + .filter(i => (!value ? true : i[0].name.toLowerCase().includes(value.toLowerCase()))) + .map(i => ({ name: `${i[0].name} (${i[1]}x Owned)`, value: i[0].name })); + } }, quantityOption, priceOption From c4145b31e55a7f74e7a44965c4759707fc8b8965 Mon Sep 17 00:00:00 2001 From: themrrobert <10122432+themrrobert@users.noreply.github.com> Date: Tue, 18 Jun 2024 06:51:05 -0700 Subject: [PATCH 020/249] Add economy tracking for GE Bank GP + Big Alchables. (#5918) --- prisma/schema.prisma | 2 ++ src/lib/analytics.ts | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index c38c692aca7..530ddd601b0 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -41,6 +41,8 @@ model Analytic { ironMinionsCount Int? totalSacrificed BigInt? totalGP BigInt? + totalGeGp BigInt? + totalBigAlchGp BigInt? dicingBank BigInt? duelTaxBank BigInt? dailiesAmount BigInt? diff --git a/src/lib/analytics.ts b/src/lib/analytics.ts index be29bc33b82..d43aead457d 100644 --- a/src/lib/analytics.ts +++ b/src/lib/analytics.ts @@ -2,6 +2,7 @@ import { ActivityGroup, globalConfig } from '../lib/constants'; import { prisma } from '../lib/settings/prisma'; import { GroupMonsterActivityTaskOptions } from '../lib/types/minions'; import { taskGroupFromActivity } from '../lib/util/taskGroupFromActivity'; +import { getItem } from './util/getOSItem'; async function calculateMinionTaskCounts() { const minionTaskCounts: Record = { @@ -47,8 +48,21 @@ export async function analyticsTick() { ) ).map((result: any) => parseInt(result[0].count)) as number[]; + const artifact = getItem('Magical artifact')!; + const statuette = getItem('Demon statuette')!; + + const [totalGeGp, totalArtifactGp, totalDemonStatuetteGp] = ( + await Promise.all( + [ + 'SELECT quantity AS val FROM ge_bank WHERE item_id = 995', + `SELECT COALESCE(SUM((bank->>'${artifact.id}')::bigint) * ${artifact.highalch}, 0) as val FROM users WHERE bank->>'${artifact.id}' IS NOT NULL`, + `SELECT COALESCE(SUM((bank->>'${statuette.id}')::bigint) * ${statuette.highalch}, 0) as val FROM users WHERE bank->>'${artifact.id}' IS NOT NULL` + ].map(q => prisma.$queryRawUnsafe<{ val: bigint }[]>(q)) + ) + ).map((v: { val: bigint }[]) => BigInt(v[0].val)); + const taskCounts = await calculateMinionTaskCounts(); - const currentClientSettings = await await prisma.clientStorage.findFirst({ + const currentClientSettings = await prisma.clientStorage.findFirst({ where: { id: globalConfig.clientID }, @@ -84,6 +98,8 @@ export async function analyticsTick() { minionsCount: numberOfMinions, totalSacrificed, totalGP, + totalGeGp, + totalBigAlchGp: totalDemonStatuetteGp + totalArtifactGp, dicingBank: currentClientSettings.economyStats_dicingBank, duelTaxBank: currentClientSettings.economyStats_duelTaxBank, dailiesAmount: currentClientSettings.economyStats_dailiesAmount, From 73f4b8884dc9e112fa044858b38927fcd67900c5 Mon Sep 17 00:00:00 2001 From: themrrobert <10122432+themrrobert@users.noreply.github.com> Date: Tue, 18 Jun 2024 06:52:33 -0700 Subject: [PATCH 021/249] Defer interactions from Global buttons (#5897) * Defer interactions from Global buttons * Check cooldowns first --- src/lib/util/globalInteractions.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/lib/util/globalInteractions.ts b/src/lib/util/globalInteractions.ts index 207c4f2db48..0a7883527e7 100644 --- a/src/lib/util/globalInteractions.ts +++ b/src/lib/util/globalInteractions.ts @@ -15,7 +15,7 @@ import { toaHelpCommand } from '../simulation/toa'; import { ItemBank } from '../types'; import { formatDuration, stringMatches } from '../util'; import { updateGiveawayMessage } from './giveaway'; -import { interactionReply } from './interactionReply'; +import { deferInteraction, interactionReply } from './interactionReply'; import { minionIsBusy } from './minionIsBusy'; import { fetchRepeatTrips, repeatTrip } from './repeatStoredTrip'; @@ -325,6 +325,16 @@ export async function interactionHook(interaction: Interaction) { const id = interaction.customId; const userID = interaction.user.id; + const cd = Cooldowns.get(userID, 'button', Time.Second * 3); + if (cd !== null) { + return interactionReply(interaction, { + content: `You're on cooldown from clicking buttons, please wait: ${formatDuration(cd, true)}.`, + ephemeral: true + }); + } + + await deferInteraction(interaction); + const user = await mUserFetch(userID); if (id.includes('GIVEAWAY_')) return giveawayButtonHandler(user, id, interaction); if (id.includes('REPEAT_TRIP')) return repeatTripHandler(user, interaction); @@ -353,14 +363,6 @@ export async function interactionHook(interaction: Interaction) { continueDeltaMillis: null }; - const cd = Cooldowns.get(userID, 'button', Time.Second * 3); - if (cd !== null) { - return interactionReply(interaction, { - content: `You're on cooldown from clicking buttons, please wait: ${formatDuration(cd, true)}.`, - ephemeral: true - }); - } - const timeSinceMessage = Date.now() - new Date(interaction.message.createdTimestamp).getTime(); const timeLimit = reactionTimeLimit(user.perkTier()); if (timeSinceMessage > Time.Day) { From 4e1a759f97fa9ca81cd9037680f429e07d3b335c Mon Sep 17 00:00:00 2001 From: GC <30398469+gc@users.noreply.github.com> Date: Thu, 27 Jun 2024 03:05:51 +1000 Subject: [PATCH 022/249] Birthday crate (#59) --- src/lib/constants.ts | 2 - src/lib/customItems/customItems.ts | 315 +++++++++++++++++- src/lib/customItems/util.ts | 1 + src/lib/data/Collections.ts | 5 +- src/lib/events.ts | 4 +- src/lib/itemMods.ts | 1 + src/lib/keyCrates.ts | 45 +++ src/lib/minions/data/bankBackgrounds.ts | 7 + .../resources/images/bank_backgrounds/508.jpg | Bin 0 -> 101271 bytes src/lib/util/handleCrateSpawns.ts | 7 +- src/mahoji/commands/config.ts | 6 +- src/mahoji/commands/price.ts | 3 +- src/mahoji/lib/mahojiCommandOptions.ts | 3 +- .../unit/snapshots/banksnapshots.test.ts.snap | 10 + tests/unit/snapshots/clsnapshots.test.ts.snap | 1 + 15 files changed, 394 insertions(+), 16 deletions(-) create mode 100644 src/lib/resources/images/bank_backgrounds/508.jpg diff --git a/src/lib/constants.ts b/src/lib/constants.ts index f3fde199403..7f60ddb4e69 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -800,8 +800,6 @@ export const chompyHats = [ [getOSItem('Chompy bird hat (expert dragon archer)'), 4000] ] as const; -export const secretItems: number[] = resolveItems([]); - export const toaPurpleItems = resolveItems([ "Tumeken's guardian", "Tumeken's shadow (uncharged)", diff --git a/src/lib/customItems/customItems.ts b/src/lib/customItems/customItems.ts index 8330c491206..04ab60493f4 100644 --- a/src/lib/customItems/customItems.ts +++ b/src/lib/customItems/customItems.ts @@ -11696,7 +11696,8 @@ setCustomItem( 'Coal', { customItemData: { - cantDropFromMysteryBoxes: true + cantDropFromMysteryBoxes: true, + isDiscontinued: true }, buy_limit: 100 }, @@ -11709,7 +11710,8 @@ setCustomItem( 'Coal', { customItemData: { - cantDropFromMysteryBoxes: true + cantDropFromMysteryBoxes: true, + isDiscontinued: true } }, 1 @@ -12494,3 +12496,312 @@ setCustomItem(73_191, 'Dunce shoes', 'Bronze boots', {}, 100_000); setCustomItem(73_192, 'Chilli chocolate', 'Coal', {}, 100_000); setCustomItem(73_193, 'Zak plushie', 'Coal', { tradeable: false }, 100_000); + +setCustomItem( + 73_200, + 'Birthday crate (s6)', + 'Coal', + { + customItemData: { + cantDropFromMysteryBoxes: true, + cantBeSacrificed: true, + isDiscontinued: true + } + }, + 100_000 +); + +setCustomItem( + 73_201, + 'Birthday crate key (s6)', + 'Coal', + { + customItemData: { + cantDropFromMysteryBoxes: true, + cantBeSacrificed: true, + isDiscontinued: true + } + }, + 100_000 +); + +setCustomItem( + 73_202, + 'Ethereal partyhat', + 'Red partyhat', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isDiscontinued: true, + isSecret: true + } + }, + 1_000_000 +); + +setCustomItem( + 73_203, + 'Swan hat', + 'Red partyhat', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isDiscontinued: true, + isSecret: true + } + }, + 1_000_000 +); + +setCustomItem( + 73_204, + 'Swan scarf', + 'Amulet of strength', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isDiscontinued: true, + isSecret: true + } + }, + 1_000_000 +); + +setCustomItem( + 73_205, + 'Rose tinted glasses', + 'Red partyhat', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isDiscontinued: true, + isSecret: true + } + }, + 1_000_000 +); + +setCustomItem( + 73_206, + 'Blabberbeak jumper', + 'Bronze platebody', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isDiscontinued: true, + isSecret: true + } + }, + 1_000_000 +); + +setCustomItem( + 73_207, + 'BSO banner', + 'Bronze dagger', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isDiscontinued: true, + isSecret: true + } + }, + 1_000_000 +); + +setCustomItem( + 73_208, + 'Blueberry birthday cake', + 'Coal', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isDiscontinued: true, + isSecret: true + } + }, + 1_000_000 +); + +setCustomItem( + 73_209, + 'Gambling skillcape', + 'Red cape', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isDiscontinued: true, + isSecret: true + } + }, + 1_000_000 +); + +setCustomItem( + 73_210, + 'Raw plopper bacon', + 'Coal', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isSecret: true + } + }, + 1000 +); + +setCustomItem( + 73_211, + 'Cooked plopper bacon', + 'Coal', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isSecret: true + } + }, + 1000 +); + +setCustomItem( + 73_212, + 'Monkey cape', + 'Red cape', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isSecret: true + } + }, + 100_000 +); + +setCustomItem( + 73_213, + 'BSO flowers', + 'Coal', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isSecret: true + } + }, + 100_000 +); + +setCustomItem( + 73_214, + 'Ceremonial top', + 'Bronze platebody', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isSecret: true + } + }, + 100_000 +); + +setCustomItem( + 73_215, + 'Ceremonial legs', + 'Bronze platelegs', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isSecret: true + } + }, + 100_000 +); + +setCustomItem( + 73_216, + 'Ceremonial boots', + 'Bronze boots', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isSecret: true + } + }, + 100_000 +); + +setCustomItem( + 73_217, + 'Ceremonial cape', + 'Red cape', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isSecret: true + } + }, + 100_000 +); + +setCustomItem( + 73_218, + 'Ceremonial hat', + 'Bronze full helm', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isSecret: true + } + }, + 100_000 +); + +setCustomItem( + 73_219, + 'Plopper nose', + 'Bronze full helm', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isSecret: true + } + }, + 100_000 +); + +setCustomItem( + 73_220, + 'Hoppy plushie', + 'Coal', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isSecret: true + } + }, + 100_000 +); + +setCustomItem( + 73_221, + 'Dice plushie', + 'Bronze dagger', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isSecret: true + } + }, + 100_000 +); + +setCustomItem( + 73_222, + 'Offhand dice plushie', + 'Bronze kiteshield', + { + customItemData: { + cantDropFromMysteryBoxes: true, + isSecret: true + } + }, + 100_000 +); diff --git a/src/lib/customItems/util.ts b/src/lib/customItems/util.ts index e30e48183d3..5a510028b20 100644 --- a/src/lib/customItems/util.ts +++ b/src/lib/customItems/util.ts @@ -43,6 +43,7 @@ export function setCustomItem(id: number, name: string, baseItem: string, newIte ) { throw new Error('Tried to add a custom item with superTradeableButTradeableOnGE, but not isSuperUntradeable'); } + const data: Item = deepMerge({ ...getOSItem(baseItem) }, { ...newItemData, name, id }) as Item; data.price = price || 1; diff --git a/src/lib/data/Collections.ts b/src/lib/data/Collections.ts index 55e78c1080e..7b160fb0d34 100644 --- a/src/lib/data/Collections.ts +++ b/src/lib/data/Collections.ts @@ -49,6 +49,7 @@ import { SkillsEnum } from '../skilling/types'; import { MUserStats } from '../structures/MUserStats'; import type { ItemBank } from '../types'; import { fetchStatsForCL, stringMatches } from '../util'; +import getOSItem from '../util/getOSItem'; import resolveItems from '../util/resolveItems'; import { makeTable, shuffleRandom } from '../util/smallUtils'; import { @@ -1831,7 +1832,9 @@ export const allCollectionLogs: ICollection = { for (const crate of keyCrates) { allCollectionLogs.Discontinued.activities[crate.item.name] = { alias: [crate.item.name.toLowerCase()], - items: resolveItems([crate.item.id, crate.key.id, ...crate.table.allItems]), + items: resolveItems([crate.item.id, crate.key.id, ...crate.table.allItems]).filter( + i => !getOSItem(i).customItemData?.isSecret + ), counts: false }; } diff --git a/src/lib/events.ts b/src/lib/events.ts index 95683f4efab..f8e25c52b3c 100644 --- a/src/lib/events.ts +++ b/src/lib/events.ts @@ -10,7 +10,7 @@ import { Cooldowns } from '../mahoji/lib/Cooldowns'; import { boxSpawnHandler } from './boxSpawns'; import { getGuthixianCacheInterval, userHasDoneCurrentGuthixianCache } from './bso/guthixianCache'; import { IronmanPMBTable, itemSearchMbTable } from './bsoOpenables'; -import { BitField, Emoji, globalConfig, secretItems } from './constants'; +import { BitField, Emoji, globalConfig } from './constants'; import { customItems } from './customItems/util'; import { DOUBLE_LOOT_FINISH_TIME_CACHE, isDoubleLootActive } from './doubleLoot'; import { giveBoxResetTime, itemContractResetTime, spawnLampResetTime } from './MUser'; @@ -132,7 +132,7 @@ const mentionCommands: MentionCommand[] = [ const items = Items.filter( i => [i.id.toString(), i.name.toLowerCase()].includes(content.toLowerCase()) && - !secretItems.includes(i.id) + !i.customItemData?.isSecret ).array(); if (items.length === 0) return msg.reply('No results for that item.'); diff --git a/src/lib/itemMods.ts b/src/lib/itemMods.ts index 8bec725ddd9..e68449ad1a1 100644 --- a/src/lib/itemMods.ts +++ b/src/lib/itemMods.ts @@ -5,6 +5,7 @@ export interface CustomItemData { cantBeDropped?: true; isDiscontinued?: true; superTradeableButTradeableOnGE?: true; + isSecret?: true; } declare module 'oldschooljs/dist/meta/types' { diff --git a/src/lib/keyCrates.ts b/src/lib/keyCrates.ts index 39253df25d8..53a743bce50 100644 --- a/src/lib/keyCrates.ts +++ b/src/lib/keyCrates.ts @@ -153,5 +153,50 @@ export const keyCrates: Crate[] = [ 1 ) .add(new LootTable().add('Carrot').add('Egg').add('Easter egg'), [2, 3], 99) + }, + { + item: getOSItem('Birthday crate (s6)'), + key: getOSItem('Birthday crate key (s6)'), + keyCostGP: 12_000_000, + table: new LootTable() + .tertiary(5000, 'Ethereal partyhat') + .add( + new LootTable() + .add('Swan hat') + .add('Swan scarf') + .add('BSO banner') + .add('Gambling skillcape') + .add('Monkey cape') + .add('BSO flowers') + .add('Dice plushie') + .add('Offhand dice plushie'), + 1, + 1 + ) + .add('Hoppy plushie') + .add('Plopper nose') + .add('Rose tinted glasses') + .add('Blabberbeak jumper', 1, 1) + .add('Paint box', 1, 2) + .add( + new LootTable() + .add('Ceremonial hat') + .add('Ceremonial cape') + .add('Ceremonial boots') + .add('Ceremonial legs') + .add('Ceremonial top'), + 1, + 5 + ) + .add( + new LootTable() + .add('Raw plopper bacon') + .add('Beer') + .add('Birthday balloons') + .add('Blueberry birthday cake') + .add('Cake'), + 1, + 88 + ) } ]; diff --git a/src/lib/minions/data/bankBackgrounds.ts b/src/lib/minions/data/bankBackgrounds.ts index e9fd5689e2d..95593885224 100644 --- a/src/lib/minions/data/bankBackgrounds.ts +++ b/src/lib/minions/data/bankBackgrounds.ts @@ -493,6 +493,13 @@ const backgroundImages: BankBackground[] = [ gpCost: 500_000_000, perkTierNeeded: PerkTier.Four }, + { + id: 508, + name: 'Maledict Mortimer', + image: null, + available: true, + perkTierNeeded: PerkTier.Four + }, { id: 1000, name: 'Kiddo CustomBG', diff --git a/src/lib/resources/images/bank_backgrounds/508.jpg b/src/lib/resources/images/bank_backgrounds/508.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f64ee1a5e895e5ea6d8b92a3dd984bffc87c9846 GIT binary patch literal 101271 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

ofWlf@Q!)`eQUf|q&}M_#iDfiTbN?7 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 0) { diff --git a/src/mahoji/commands/config.ts b/src/mahoji/commands/config.ts index c693c14d89e..8be66870455 100644 --- a/src/mahoji/commands/config.ts +++ b/src/mahoji/commands/config.ts @@ -17,7 +17,7 @@ import { Bank } from 'oldschooljs'; import { ItemBank } from 'oldschooljs/dist/meta/types'; import { production } from '../../config'; -import { BitField, ItemIconPacks, ParsedCustomEmojiWithGroups, PerkTier, secretItems } from '../../lib/constants'; +import { BitField, ItemIconPacks, ParsedCustomEmojiWithGroups, PerkTier } from '../../lib/constants'; import { Eatables } from '../../lib/data/eatables'; import { gearImages } from '../../lib/gear/functions/generateGearImage'; import { Inventions } from '../../lib/invention/inventions'; @@ -204,7 +204,7 @@ async function favFoodConfig( const currentItems = `Your current favorite food is: ${ currentFavorites.length === 0 ? 'None' : currentFavorites.map(itemNameFromID).join(', ') }.`; - if (!item || secretItems.includes(item.id)) return currentItems; + if (!item || item.customItemData?.isSecret) return currentItems; if (!Eatables.some(i => i.id === item.id || i.raw === item.id)) return "That's not a valid item."; if (itemToAdd) { @@ -235,7 +235,7 @@ async function favItemConfig( const currentItems = `Your current favorite items are: ${ currentFavorites.length === 0 ? 'None' : currentFavorites.map(itemNameFromID).join(', ') }.`; - if (!item || secretItems.includes(item.id)) return currentItems; + if (!item || item.customItemData?.isSecret) return currentItems; if (itemToAdd) { let limit = (user.perkTier() + 1) * 100; if (currentFavorites.length >= limit) { diff --git a/src/mahoji/commands/price.ts b/src/mahoji/commands/price.ts index 90f976f8e83..dc83ceb3a55 100644 --- a/src/mahoji/commands/price.ts +++ b/src/mahoji/commands/price.ts @@ -2,7 +2,6 @@ import { EmbedBuilder } from 'discord.js'; import { CommandRunOptions } from 'mahoji'; import { toKMB } from 'oldschooljs/dist/util'; -import { secretItems } from '../../lib/constants'; import { getItem } from '../../lib/util/getOSItem'; import { itemOption } from '../lib/mahojiCommandOptions'; import { OSBMahojiCommand } from '../lib/util'; @@ -20,7 +19,7 @@ export const priceCommand: OSBMahojiCommand = { ], run: async ({ options }: CommandRunOptions<{ item: string }>) => { const item = getItem(options.item); - if (!item || secretItems.includes(item.id)) return "Couldn't find that item."; + if (!item || item.customItemData?.isSecret) return "Couldn't find that item."; const { basePrice: priceOfItem } = sellPriceOfItem(item); diff --git a/src/mahoji/lib/mahojiCommandOptions.ts b/src/mahoji/lib/mahojiCommandOptions.ts index ce89bc65bdd..0d47a3860fa 100644 --- a/src/mahoji/lib/mahojiCommandOptions.ts +++ b/src/mahoji/lib/mahojiCommandOptions.ts @@ -5,7 +5,6 @@ import { CommandOption } from 'mahoji/dist/lib/types'; import { Bank, Items } from 'oldschooljs'; import { Item, ItemBank } from 'oldschooljs/dist/meta/types'; -import { secretItems } from '../../lib/constants'; import { baseFilters, filterableTypes } from '../../lib/data/filterables'; import { GearSetupTypes } from '../../lib/gear/types'; import { IMaterialBank, materialTypes } from '../../lib/invention'; @@ -44,7 +43,7 @@ export const itemOption = (filter?: (item: Item) => boolean): CommandOption => ( description: 'The item you want to pick.', required: false, autocomplete: async value => { - let res = itemArr.filter(i => i.key.includes(value.toLowerCase())).filter(i => !secretItems.includes(i.id)); + let res = itemArr.filter(i => i.key.includes(value.toLowerCase())).filter(i => !i.customItemData?.isSecret); if (filter) res = res.filter(filter); return res.map(i => ({ name: `${i.name}`, value: i.id.toString() })); } diff --git a/tests/unit/snapshots/banksnapshots.test.ts.snap b/tests/unit/snapshots/banksnapshots.test.ts.snap index e98f859b7a6..bf394e27450 100644 --- a/tests/unit/snapshots/banksnapshots.test.ts.snap +++ b/tests/unit/snapshots/banksnapshots.test.ts.snap @@ -4317,6 +4317,16 @@ exports[`BSO Buyables 1`] = ` "name": "Easter crate key (s5)", "outputItems": undefined, }, + { + "gpCost": 12000000, + "ironmanPrice": 1200000, + "itemCost": Bank { + "bank": {}, + "frozen": false, + }, + "name": "Birthday crate key (s6)", + "outputItems": undefined, + }, { "customReq": [Function], "gpCost": 1000000, diff --git a/tests/unit/snapshots/clsnapshots.test.ts.snap b/tests/unit/snapshots/clsnapshots.test.ts.snap index 86a1cf0e463..19b0a812bbf 100644 --- a/tests/unit/snapshots/clsnapshots.test.ts.snap +++ b/tests/unit/snapshots/clsnapshots.test.ts.snap @@ -14,6 +14,7 @@ Barrows Chests (25) Baxtorian Bathhouses (4) Beginner Treasure Trails (16) Birthday crate (s2) (20) +Birthday crate (s6) (6) Brimhaven Agility Arena (8) Bryophyta (1) BSO Birthday 2022 (4) From 1a63445e5607973e2f4808ffda8e87b952c647ae Mon Sep 17 00:00:00 2001 From: GC <30398469+gc@users.noreply.github.com> Date: Tue, 2 Jul 2024 10:51:58 +1000 Subject: [PATCH 023/249] Colosseum (#5922) --- .prettierignore | 6 +- package.json | 5 +- prisma/schema.prisma | 21 +- src/lib/MUser.ts | 29 + src/lib/Task.ts | 4 +- src/lib/colosseum.ts | 661 ++++++++++++++++++ .../combat_achievements/combatAchievements.ts | 3 + src/lib/combat_achievements/elite.ts | 37 +- src/lib/combat_achievements/grandmaster.ts | 59 ++ src/lib/combat_achievements/master.ts | 60 ++ src/lib/constants.ts | 25 +- src/lib/data/Collections.ts | 18 + src/lib/data/buyables/buyables.ts | 4 +- src/lib/data/buyables/capes.ts | 2 +- src/lib/data/creatablesTable.txt | 3 + src/lib/data/createables.ts | 16 + src/lib/data/similarItems.ts | 9 +- src/lib/degradeableItems.ts | 24 +- src/lib/diaries.ts | 52 +- src/lib/implings.ts | 3 +- src/lib/itemMods.ts | 10 +- src/lib/mastery.ts | 2 +- .../data/killableMonsters/bosses/dt.ts | 5 +- .../data/killableMonsters/bosses/wildy.ts | 9 +- src/lib/minions/data/quests.ts | 217 ++++++ src/lib/minions/types.ts | 38 +- src/lib/resources/images/minimus.png | Bin 0 -> 2664 bytes src/lib/settings/minigames.ts | 5 + src/lib/simulation/simulatedKillables.ts | 4 +- src/lib/skilling/types.ts | 35 + src/lib/structures/Bank.ts | 15 + src/lib/structures/GeneralBank.ts | 155 ++++ src/lib/structures/Requirements.ts | 21 +- src/lib/types/minions.ts | 11 +- src/lib/util/chatHeadImage.ts | 7 +- src/lib/util/minionStatus.ts | 11 +- src/lib/util/repeatStoredTrip.ts | 6 + src/lib/util/updateBankSetting.ts | 4 +- src/lib/util/userQueues.ts | 1 + src/lib/workers/casket.worker.ts | 4 + src/lib/workers/finish.worker.ts | 4 + src/lib/workers/kill.worker.ts | 4 + src/mahoji/commands/activities.ts | 3 +- src/mahoji/commands/buy.ts | 2 +- src/mahoji/commands/gamble.ts | 19 +- src/mahoji/commands/k.ts | 5 + src/mahoji/commands/simulate.ts | 73 ++ src/mahoji/commands/testpotato.ts | 39 +- .../achievementDiaryCommand.ts | 3 +- .../lib/abstracted_commands/capegamble.ts | 78 ++- .../lib/abstracted_commands/minionKill.ts | 12 +- .../nightmareZoneCommand.ts | 2 +- .../lib/abstracted_commands/questCommand.ts | 217 +----- src/scripts/integration-tests.ts | 2 +- src/tasks/minions/colosseumActivity.ts | 97 +++ .../minions/minigames/wintertodtActivity.ts | 28 +- src/tasks/minions/questingActivity.ts | 2 +- src/tasks/minions/specificQuestActivity.ts | 2 +- tests/integration/commands/sacrifice.test.ts | 12 +- tests/unit/GeneralBank.test.ts | 186 +++++ .../unit/snapshots/banksnapshots.test.ts.snap | 55 +- tests/unit/snapshots/clsnapshots.test.ts.snap | 11 +- vitest.unit.config.mts | 5 +- yarn.lock | 70 +- 64 files changed, 2169 insertions(+), 363 deletions(-) create mode 100644 src/lib/colosseum.ts create mode 100644 src/lib/minions/data/quests.ts create mode 100644 src/lib/resources/images/minimus.png create mode 100644 src/lib/structures/Bank.ts create mode 100644 src/lib/structures/GeneralBank.ts create mode 100644 src/tasks/minions/colosseumActivity.ts create mode 100644 tests/unit/GeneralBank.test.ts diff --git a/.prettierignore b/.prettierignore index 53c37a16608..3d2d6656057 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,5 @@ -dist \ No newline at end of file +dist +coverage +node_modules +logs +licenses \ No newline at end of file diff --git a/package.json b/package.json index a315f22b85e..c4bcacfa8f9 100644 --- a/package.json +++ b/package.json @@ -45,10 +45,11 @@ "lodash": "^4.17.21", "lru-cache": "^8.0.0", "mahoji": "^0.0.7", + "mathjs": "^13.0.0", "murmurhash": "^2.0.1", "node-cron": "^3.0.3", "node-fetch": "^2.6.7", - "oldschooljs": "^2.5.1", + "oldschooljs": "^2.5.4", "p-queue": "^6.6.2", "piscina": "^4.4.0", "random-js": "^2.1.0", @@ -83,8 +84,8 @@ "eslint-plugin-simple-import-sort": "^8.0.0", "eslint-plugin-unicorn": "^44.0.2", "jest-image-snapshot": "^6.2.0", - "mitm": "^1.7.2", "madge": "^7.0.0", + "mitm": "^1.7.2", "prettier": "^2.7.1", "prisma": "^5.13.0", "rimraf": "^5.0.5", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 50299c06586..c2f03ebe69f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -134,6 +134,8 @@ model ClientStorage { nmz_cost Json @default("{}") @db.Json toa_cost Json @default("{}") @db.Json toa_loot Json @default("{}") @db.Json + colo_cost Json @default("{}") @db.Json + colo_loot Json @default("{}") @db.Json grand_exchange_is_locked Boolean @default(false) grand_exchange_total_tax BigInt @default(0) @@ -572,6 +574,7 @@ model Minigame { nmz Int @default(0) shades_of_morton Int @default(0) tombs_of_amascut Int @default(0) + colosseum Int @default(0) new_user NewUser? @@ -737,11 +740,11 @@ model UserStats { high_gambles Int @default(0) honour_points Int @default(0) - slayer_task_streak Int @default(0) - slayer_wildy_task_streak Int @default(0) - slayer_superior_count Int @default(0) - slayer_unsired_offered Int @default(0) - slayer_chewed_offered Int @default(0) + slayer_task_streak Int @default(0) + slayer_wildy_task_streak Int @default(0) + slayer_superior_count Int @default(0) + slayer_unsired_offered Int @default(0) + slayer_chewed_offered Int @default(0) tob_cost Json @default("{}") tob_loot Json @default("{}") @@ -765,6 +768,13 @@ model UserStats { forestry_event_completions_bank Json @default("{}") @db.Json + colo_cost Json @default("{}") @db.Json + colo_loot Json @default("{}") @db.Json + colo_kc_bank Json @default("{}") @db.Json + colo_max_glory Int? + + quivers_sacrificed Int @default(0) + @@map("user_stats") } @@ -945,6 +955,7 @@ enum activity_type_enum { CamdozaalFishing CamdozaalMining CamdozaalSmithing + Colosseum } enum xp_gains_skill_enum { diff --git a/src/lib/MUser.ts b/src/lib/MUser.ts index 0ece1e2048b..85dc4bf603a 100644 --- a/src/lib/MUser.ts +++ b/src/lib/MUser.ts @@ -1,3 +1,4 @@ +import { mentionCommand } from '@oldschoolgg/toolkit'; import { UserError } from '@oldschoolgg/toolkit/dist/lib/UserError'; import { GearSetupType, Prisma, User, UserStats, xp_gains_skill_enum } from '@prisma/client'; import { userMention } from 'discord.js'; @@ -15,6 +16,7 @@ import { badges, BitField, Emoji, projectiles, usernameCache } from './constants import { bossCLItems } from './data/Collections'; import { allPetIDs } from './data/CollectionsExport'; import { getSimilarItems } from './data/similarItems'; +import { degradeableItems } from './degradeableItems'; import { GearSetup, UserFullGearSetup } from './gear/types'; import { handleNewCLItems } from './handleNewCLItems'; import { marketPriceOfBank } from './marketPrices'; @@ -33,6 +35,7 @@ import { getFarmingInfoFromUser } from './skilling/functions/getFarmingInfo'; import Farming from './skilling/skills/farming'; import { SkillsEnum } from './skilling/types'; import { BankSortMethod } from './sorts'; +import { ChargeBank } from './structures/Bank'; import { defaultGear, Gear } from './structures/Gear'; import { ItemBank, Skills } from './types'; import { addItemToBank, convertXPtoLVL, itemNameFromID } from './util'; @@ -485,6 +488,32 @@ GROUP BY data->>'clueID';`); return blowpipe; } + hasCharges(chargeBank: ChargeBank) { + const failureReasons: string[] = []; + for (const [keyName, chargesToDegrade] of chargeBank.entries()) { + const degradeableItem = degradeableItems.find(i => i.settingsKey === keyName); + if (!degradeableItem) { + throw new Error(`Invalid degradeable item key: ${keyName}`); + } + const currentCharges = this.user[degradeableItem.settingsKey]; + const newCharges = currentCharges - chargesToDegrade; + if (newCharges < 0) { + failureReasons.push( + `You don't have enough ${degradeableItem.item.name} charges, you need ${chargesToDegrade}, but you have only ${currentCharges}.` + ); + } + } + if (failureReasons.length > 0) { + return { + hasCharges: false, + fullUserString: `${failureReasons.join(', ')} + +Charge your items using ${mentionCommand(globalClient, 'minion', 'charge')}.` + }; + } + return { hasCharges: true }; + } + percentOfBossCLFinished() { const percentBossCLFinished = calcWhatPercent( this.cl.items().filter(i => bossCLItems.includes(i[0].id)).length, diff --git a/src/lib/Task.ts b/src/lib/Task.ts index 2436441b742..11f2692f2e6 100644 --- a/src/lib/Task.ts +++ b/src/lib/Task.ts @@ -11,6 +11,7 @@ import { camdozaalSmithingTask } from '../tasks/minions/camdozaalActivity/camdoz import { castingTask } from '../tasks/minions/castingActivity'; import { clueTask } from '../tasks/minions/clueActivity'; import { collectingTask } from '../tasks/minions/collectingActivity'; +import { colosseumTask } from '../tasks/minions/colosseumActivity'; import { combatRingTask } from '../tasks/minions/combatRingActivity'; import { constructionTask } from '../tasks/minions/constructionActivity'; import { cookingTask } from '../tasks/minions/cookingActivity'; @@ -183,7 +184,8 @@ export const tasks: MinionTask[] = [ specificQuestTask, camdozaalMiningTask, camdozaalSmithingTask, - camdozaalFishingTask + camdozaalFishingTask, + colosseumTask ]; export async function processPendingActivities() { diff --git a/src/lib/colosseum.ts b/src/lib/colosseum.ts new file mode 100644 index 00000000000..b708051cf77 --- /dev/null +++ b/src/lib/colosseum.ts @@ -0,0 +1,661 @@ +import { mentionCommand } from '@oldschoolgg/toolkit'; +import { UserError } from '@oldschoolgg/toolkit/dist/lib/UserError'; +import { + calcPercentOfNum, + calcWhatPercent, + clamp, + increaseNumByPercent, + objectEntries, + objectValues, + percentChance, + randInt, + reduceNumByPercent, + sumArr, + Time +} from 'e'; +import { Bank, LootTable } from 'oldschooljs'; +import { EquipmentSlot } from 'oldschooljs/dist/meta/types'; + +import { userStatsBankUpdate } from '../mahoji/mahojiSettings'; +import { degradeChargeBank } from './degradeableItems'; +import { GearSetupType } from './gear/types'; +import { trackLoot } from './lootTrack'; +import { QuestID } from './minions/data/quests'; +import { ChargeBank } from './structures/Bank'; +import { GeneralBank, GeneralBankType } from './structures/GeneralBank'; +import { ItemBank, Skills } from './types'; +import { ColoTaskOptions } from './types/minions'; +import addSubTaskToActivityTask from './util/addSubTaskToActivityTask'; +import resolveItems from './util/resolveItems'; +import { exponentialPercentScale, formatDuration, formatSkillRequirements, itemNameFromID } from './util/smallUtils'; +import { updateBankSetting } from './util/updateBankSetting'; + +function combinedChance(percentages: number[]): number { + const failureProbabilities = percentages.map(p => (100 - p) / 100); + const combinedFailureProbability = failureProbabilities.reduce((acc, prob) => acc * prob, 1); + const combinedSuccessProbability = 1 - combinedFailureProbability; + return combinedSuccessProbability * 100; +} + +interface Wave { + waveNumber: number; + enemies: string[]; + reinforcements?: string[]; + table: LootTable; +} + +export const colosseumWaves: Wave[] = [ + { + waveNumber: 1, + enemies: ['Fremennik Warband', 'Serpent Shaman'], + reinforcements: ['Jaguar Warrior'], + table: new LootTable().every('Sunfire splinters', 80) + }, + { + waveNumber: 2, + enemies: ['Fremennik Warband', 'Serpent Shaman', 'Javelin Colossus'], + reinforcements: ['Jaguar Warrior'], + table: new LootTable() + .add('Rune platebody', 1) + .add('Death rune', 150) + .add('Chaos rune', 150) + .add('Sunfire splinters', 150) + .add('Cannonball', 80) + .add('Rune kiteshield', 4) + .add('Rune chainbody', 1) + }, + { + waveNumber: 3, + enemies: ['Fremennik Warband', 'Serpent Shaman', 'Javelin Colossus'], + reinforcements: ['Jaguar Warrior'], + table: new LootTable() + .add('Rune platebody', 1, 9) + .add('Death rune', 150, 9) + .add('Chaos rune', 150, 9) + .add('Sunfire splinters', 150, 9) + .add('Cannonball', 80, 9) + .add('Rune kiteshield', 4, 9) + .add('Rune chainbody', 1, 9) + .add('Onyx bolts', 30) + .add('Snapdragon seed', 1) + .add('Dragon platelegs', 1) + .add('Sunfire splinters', 500) + .add('Earth orb', 80) + .add('Rune 2h sword', 2) + .add('Gold ore', 150) + }, + { + waveNumber: 4, + enemies: ['Fremennik Warband', 'Serpent Shaman', 'Manticore'], + reinforcements: ['Jaguar Warrior', 'Serpent Shaman'], + table: new LootTable() + .add('Rune platebody', 1, 5535) + .add('Death rune', 150, 5535) + .add('Chaos rune', 150, 5535) + .add('Sunfire splinters', 150, 5535) + .add('Cannonball', 80, 5535) + .add('Rune kiteshield', 4, 5535) + .add('Rune chainbody', 1, 5535) + .add('Onyx bolts', 30, 615) + .add('Snapdragon seed', 1, 615) + .add('Dragon platelegs', 1, 615) + .add('Sunfire splinters', 500, 615) + .add('Earth orb', 80, 615) + .add('Rune 2h sword', 2, 615) + .add('Gold ore', 150, 615) + .add('Echo crystal', 1, 126) + .add('Sunfire fanatic helm', 1, 70) + .add('Sunfire fanatic cuirass', 1, 70) + .add('Sunfire fanatic chausses', 1, 70) + .add('Echo crystal', 2, 14) + .add('Echo crystal', 3, 14) + }, + { + waveNumber: 5, + enemies: ['Fremennik Warband', 'Serpent Shaman', 'Javelin Colossus', 'Manticore'], + reinforcements: ['Jaguar Warrior', 'Serpent Shaman'], + table: new LootTable() + .add('Onyx bolts', 30, 2725) + .add('Snapdragon seed', 1, 2725) + .add('Dragon platelegs', 1, 2725) + .add('Sunfire splinters', 500, 2725) + .add('Earth orb', 80, 2725) + .add('Rune 2h sword', 2, 2725) + .add('Gold ore', 150, 2725) + .add('Echo crystal', 1, 63) + .add('Sunfire fanatic helm', 1, 35) + .add('Sunfire fanatic cuirass', 1, 35) + .add('Sunfire fanatic chausses', 1, 35) + .add('Echo crystal', 2, 7) + .add('Echo crystal', 3, 7) + }, + { + waveNumber: 6, + enemies: ['Fremennik Warband', 'Serpent Shaman', 'Javelin Colossus', 'Manticore'], + reinforcements: ['Jaguar Warrior', 'Serpent Shaman'], + table: new LootTable() + .add('Onyx bolts', 30, 2375) + .add('Snapdragon seed', 1, 2375) + .add('Dragon platelegs', 1, 2375) + .add('Sunfire splinters', 500, 2375) + .add('Earth orb', 80, 2375) + .add('Rune 2h sword', 2, 2375) + .add('Gold ore', 150, 2375) + .add('Echo crystal', 1, 63) + .add('Sunfire fanatic helm', 1, 35) + .add('Sunfire fanatic cuirass', 1, 35) + .add('Sunfire fanatic chausses', 1, 35) + .add('Echo crystal', [2, 3], 7) + }, + { + waveNumber: 7, + enemies: ['Fremennik Warband', 'Javelin Colossus', 'Manticore', 'Shockwave Colossus'], + reinforcements: ['Minotaur'], + table: new LootTable() + .add('Onyx bolts', 30, 5832) + .add('Snapdragon seed', 1, 5832) + .add('Dragon platelegs', 1, 5832) + .add('Sunfire splinters', 500, 5832) + .add('Earth orb', 80, 5832) + .add('Rune 2h sword', 2, 5832) + .add('Gold ore', 150, 5832) + .add('Dragon bolts (unf)', 200, 756) + .add('Ranarr seed', 4, 756) + .add('Sunfire splinters', 1100, 756) + .add('Dragon arrowtips', 150, 756) + .add('Adamantite ore', 100, 756) + .add('Death rune', 250, 756) + .add('Echo crystal', 1, 189) + .add('Sunfire fanatic helm', 1, 105) + .add('Sunfire fanatic cuirass', 1, 105) + .add('Sunfire fanatic chausses', 1, 105) + .add('Tonalztics of ralos (uncharged)', 1, 35) + .add('Echo crystal', [2, 3], 21) + }, + { + waveNumber: 8, + enemies: ['Fremennik Warband', 'Javelin Colossus', 'Manticore', 'Shockwave Colossus'], + reinforcements: ['Minotaur'], + table: new LootTable() + .add('Onyx bolts', 30, 14_472) + .add('Snapdragon seed', 1, 14_472) + .add('Dragon platelegs', 1, 14_472) + .add('Sunfire splinters', 500, 14_472) + .add('Earth orb', 80, 14_472) + .add('Rune 2h sword', 2, 14_472) + .add('Gold ore', 150, 14_472) + .add('Onyx bolts', 50, 1876) + .add('Dragon platelegs', 2, 1876) + .add('Sunfire splinters', 1750, 1876) + .add('Rune kiteshield', 5, 1876) + .add('Runite ore', 12, 1876) + .add('Death rune', 300, 1876) + .add('Echo crystal', 1, 567) + .add('Sunfire fanatic helm', 1, 315) + .add('Sunfire fanatic cuirass', 1, 315) + .add('Sunfire fanatic chausses', 1, 315) + .add('Tonalztics of ralos (uncharged)', 1, 105) + .add('Echo crystal', [2, 3], 63) + }, + { + waveNumber: 9, + enemies: ['Fremennik Warband', 'Javelin Colossus', 'Manticore'], + reinforcements: ['Minotaur'], + table: new LootTable() + .add('Dragon bolts (unf)', 200, 3180) + .add('Ranarr seed', 4, 3180) + .add('Sunfire splinters', 1100, 3180) + .add('Dragon arrowtips', 150, 3180) + .add('Adamantite ore', 100, 3180) + .add('Death rune', 250, 3180) + .add('Onyx bolts', 75, 424) + .add('Sunfire splinters', 2500, 424) + .add('Dragon platelegs', 3, 424) + .add('Death rune', 300, 424) + .add('Rune warhammer', 5, 424) + .add('Echo crystal', 1, 135) + .add('Sunfire fanatic helm', 1, 75) + .add('Sunfire fanatic cuirass', 1, 75) + .add('Sunfire fanatic chausses', 1, 75) + .add('Tonalztics of ralos (uncharged)', 1, 25) + .add('Echo crystal', [2, 3], 15) + }, + { + waveNumber: 10, + enemies: ['Fremennik Warband', 'Javelin Colossus', 'Manticore'], + reinforcements: ['Minotaur', 'Serpent Shaman'], + table: new LootTable() + .add('Onyx bolts', 50, 2340) + .add('Dragon platelegs', 2, 2340) + .add('Sunfire splinters', 1750, 2340) + .add('Rune kiteshield', 5, 2340) + .add('Runite ore', 12, 2340) + .add('Death rune', 300, 2340) + .add('Onyx bolts', 100, 312) + .add('Rune warhammer', 8, 312) + .add('Dragon platelegs', 3, 312) + .add('Sunfire splinters', 3500, 312) + .add('Dragon arrowtips', 250, 312) + .add('Echo crystal', 1, 135) + .add('Sunfire fanatic helm', 1, 75) + .add('Sunfire fanatic cuirass', 1, 75) + .add('Sunfire fanatic chausses', 1, 75) + .add('Tonalztics of ralos (uncharged)', 1, 25) + .add('Echo crystal', [2, 3], 15) + }, + { + waveNumber: 11, + enemies: ['Fremennik Warband', 'Javelin Colossus', 'Manticore', 'Shockwave Colossus'], + reinforcements: ['Minotaur', 'Serpent Shaman'], + table: new LootTable() + .add('Onyx bolts', 75, 360) + .add('Sunfire splinters', 2500, 360) + .add('Dragon platelegs', 3, 360) + .add('Death rune', 300, 360) + .add('Rune warhammer', 5, 360) + .add('Cannonball', 2000, 50) + .add('Dragon plateskirt', 5, 50) + .add('Dragon arrowtips', 350, 50) + .add('Onyx bolts', 150, 50) + .add('Echo crystal', 1, 27) + .add('Sunfire fanatic helm', 1, 15) + .add('Sunfire fanatic cuirass', 1, 15) + .add('Sunfire fanatic chausses', 1, 15) + .add('Tonalztics of ralos (uncharged)', 1, 5) + .add('Echo crystal', [2, 3], 3) + }, + { + waveNumber: 12, + enemies: ['Sol Heredit'], + table: new LootTable() + .every("Dizana's quiver (uncharged)") + .tertiary(200, 'Smol heredit') + .add('Onyx bolts', 100, 792) + .add('Rune warhammer', 8, 792) + .add('Dragon platelegs', 3, 792) + .add('Sunfire splinters', 3500, 792) + .add('Dragon arrowtips', 250, 792) + .add('Echo crystal', 1, 135) + .add('Uncut onyx', 1, 110) + .add('Dragon plateskirt', 5, 110) + .add('Rune 2h sword', 9, 110) + .add('Runite ore', 35, 110) + .add('Sunfire fanatic helm', 1, 75) + .add('Sunfire fanatic cuirass', 1, 75) + .add('Sunfire fanatic chausses', 1, 75) + .add('Tonalztics of ralos (uncharged)', 1, 25) + .add('Echo crystal', [2, 3], 15) + } +]; + +function calculateDeathChance(waveKC: number, hasBF: boolean, hasSGS: boolean): number { + const cappedKc = Math.min(Math.max(waveKC, 0), 150); + const baseChance = 80; + const kcReduction = 80 * (1 - Math.exp(-1.5 * cappedKc)); + + let newChance = baseChance - kcReduction; + + if (hasSGS) { + newChance = reduceNumByPercent(newChance, 5); + } + if (hasBF) { + newChance = reduceNumByPercent(newChance, 5); + } + + return clamp(newChance, 1, 80); +} + +export class ColosseumWaveBank extends GeneralBank { + constructor(initialBank?: GeneralBankType) { + super({ + initialBank, + allowedKeys: colosseumWaves.map(i => i.waveNumber), + valueSchema: { floats: true, min: 0, max: 999_999 } + }); + } +} + +function calculateTimeInMs(waveTwelveKC: number): number { + const points: { kc: number; timeInMinutes: number }[] = [ + { kc: 0, timeInMinutes: 60 }, + { kc: 1, timeInMinutes: 45 }, + { kc: 1, timeInMinutes: 35 }, + { kc: 5, timeInMinutes: 30 }, + { kc: 10, timeInMinutes: 25 }, + { kc: 50, timeInMinutes: 19 }, + { kc: 100, timeInMinutes: 16 }, + { kc: 300, timeInMinutes: 15 } + ]; + + if (waveTwelveKC <= points[0].kc) return points[0].timeInMinutes * 60 * 1000; + if (waveTwelveKC >= points[points.length - 1].kc) return points[points.length - 1].timeInMinutes * 60 * 1000; + + for (let i = 0; i < points.length - 1; i++) { + const point1 = points[i]; + const point2 = points[i + 1]; + if (waveTwelveKC >= point1.kc && waveTwelveKC <= point2.kc) { + const slope = (point2.timeInMinutes - point1.timeInMinutes) / (point2.kc - point1.kc); + return (point1.timeInMinutes + slope * (waveTwelveKC - point1.kc)) * 60 * 1000; + } + } + + return 0; +} + +function calculateGlory(kcBank: ColosseumWaveBank, wave: Wave) { + const waveKCSkillBank = new ColosseumWaveBank(); + for (const [waveNumber, kc] of kcBank.entries()) { + waveKCSkillBank.add(waveNumber, clamp(calcWhatPercent(kc, 30 - waveNumber), 1, 100)); + } + const kcSkill = waveKCSkillBank.amount(wave.waveNumber) ?? 0; + const totalKCSkillPercent = sumArr(waveKCSkillBank.entries().map(ent => ent[1])) / waveKCSkillBank.length(); + const expSkill = exponentialPercentScale(totalKCSkillPercent); + const maxPossibleGlory = 60_000; + const ourMaxGlory = calcPercentOfNum(expSkill, maxPossibleGlory); + const wavePerformance = exponentialPercentScale((totalKCSkillPercent + kcSkill) / 2); + const glory = randInt(calcPercentOfNum(wavePerformance, ourMaxGlory), ourMaxGlory); + return glory; +} + +interface ColosseumResult { + diedAt: number | null; + loot: Bank | null; + maxGlory: number; + addedWaveKCBank: ColosseumWaveBank; + fakeDuration: number; + realDuration: number; + totalDeathChance: number; + deathChances: number[]; +} + +export const startColosseumRun = (options: { + kcBank: ColosseumWaveBank; + hasScythe: boolean; + hasTBow: boolean; + hasVenBow: boolean; + hasBF: boolean; + hasClaws: boolean; + hasSGS: boolean; + hasTorture: boolean; +}): ColosseumResult => { + const waveTwelveKC = options.kcBank.amount(12); + + const bank = new Bank(); + + const addedWaveKCBank = new ColosseumWaveBank(); + let waveDuration = calculateTimeInMs(waveTwelveKC) / 12; + + if (!options.hasScythe) { + waveDuration = increaseNumByPercent(waveDuration, 10); + } + if (!options.hasTBow) { + waveDuration = increaseNumByPercent(waveDuration, 10); + } + if (!options.hasVenBow) { + waveDuration = increaseNumByPercent(waveDuration, 7); + } + if (!options.hasClaws) { + waveDuration = increaseNumByPercent(waveDuration, 4); + } + if (!options.hasTorture) { + waveDuration = increaseNumByPercent(waveDuration, 5); + } + + const fakeDuration = 12 * waveDuration; + const deathChances: number[] = []; + let realDuration = 0; + let maxGlory = 0; + + for (const wave of colosseumWaves) { + realDuration += waveDuration; + const kcForThisWave = options.kcBank.amount(wave.waveNumber); + maxGlory = Math.max(calculateGlory(options.kcBank, wave), maxGlory); + const deathChance = calculateDeathChance(kcForThisWave, options.hasBF, options.hasSGS); + deathChances.push(deathChance); + + if (percentChance(deathChance)) { + return { + diedAt: wave.waveNumber, + loot: null, + maxGlory: 0, + addedWaveKCBank, + fakeDuration, + realDuration, + totalDeathChance: combinedChance(deathChances), + deathChances + }; + } + addedWaveKCBank.add(wave.waveNumber); + bank.add(wave.table.roll()); + if (wave.waveNumber === 12) { + return { + diedAt: null, + loot: bank, + maxGlory, + addedWaveKCBank, + fakeDuration, + realDuration, + totalDeathChance: combinedChance(deathChances), + deathChances + }; + } + } + + throw new Error('Colosseum run did not end correctly.'); +}; + +export async function colosseumCommand(user: MUser, channelID: string) { + if (user.minionIsBusy) { + return `${user.usernameOrMention} is busy`; + } + + if (!user.user.finished_quest_ids.includes(QuestID.ChildrenOfTheSun)) { + return `You need to complete the "Children of the Sun" quest before you can enter the Colosseum. Send your minion to do the quest using: ${mentionCommand( + globalClient, + 'activities', + 'quest' + )}.`; + } + + const skillReqs: Skills = { + attack: 90, + strength: 90, + defence: 90, + prayer: 80, + ranged: 90, + magic: 94, + hitpoints: 90 + }; + + if (!user.hasSkillReqs(skillReqs)) { + return `You need ${formatSkillRequirements(skillReqs)} to enter the Colosseum.`; + } + + const requiredItems: Partial>>> = { + melee: { + head: resolveItems(['Torva full helm', 'Neitiznot faceguard', 'Justiciar faceguard']), + cape: resolveItems(['Infernal cape', 'Fire cape']), + neck: resolveItems(['Amulet of blood fury', 'Amulet of torture']), + body: resolveItems(['Torva platebody', 'Bandos chestplate']), + legs: resolveItems(['Torva platelegs', 'Bandos tassets']), + feet: resolveItems(['Primordial boots']), + ring: resolveItems(['Ultor ring', 'Berserker ring (i)']) + }, + range: { + cape: resolveItems(["Dizana's quiver", "Ava's assembler"]), + head: resolveItems(['Masori mask (f)', 'Masori mask', 'Armadyl helmet']), + neck: resolveItems(['Necklace of anguish']), + body: resolveItems(['Masori body (f)', 'Masori body', 'Armadyl chestplate']), + legs: resolveItems(['Masori chaps (f)', 'Masori chaps', 'Armadyl chainskirt']), + feet: resolveItems(['Pegasian boots']), + ring: resolveItems(['Venator ring', 'Archers ring (i)']) + } + }; + + const meleeWeapons = resolveItems(['Scythe of vitur', 'Blade of saeldor (c)']); + const rangeWeapons = resolveItems(['Twisted bow', 'Bow of faerdhinen (c)']); + + for (const [gearType, gearNeeded] of objectEntries(requiredItems)) { + const gear = user.gear[gearType]; + if (!gearNeeded) continue; + for (const items of objectValues(gearNeeded)) { + if (!items) continue; + if (!items.some(g => gear.hasEquipped(g))) { + return `You need one of these equipped in your ${gearType} setup to enter the Colosseum: ${items + .map(itemNameFromID) + .join(', ')}.`; + } + } + } + + if (!meleeWeapons.some(i => user.gear.melee.hasEquipped(i, true, true))) { + return `You need one of these equipped in your melee setup to enter the Colosseum: ${meleeWeapons + .map(itemNameFromID) + .join(', ')}.`; + } + + if (!rangeWeapons.some(i => user.gear.range.hasEquipped(i, true, true))) { + return `You need one of these equipped in your range setup to enter the Colosseum: ${rangeWeapons + .map(itemNameFromID) + .join(', ')}.`; + } + + const messages: string[] = []; + + const hasBF = user.gear.melee.hasEquipped('Amulet of blood fury', true, false); + const hasScythe = user.gear.melee.hasEquipped('Scythe of vitur', true, true); + const hasTBow = user.gear.range.hasEquipped('Twisted bow', true, true); + function calculateVenCharges() { + return 50; + } + const hasVenBow = user.hasEquippedOrInBank('Venator bow') && user.user.venator_bow_charges >= calculateVenCharges(); + const hasClaws = user.hasEquippedOrInBank('Dragon claws'); + const hasSGS = user.hasEquippedOrInBank('Saradomin godsword'); + const hasTorture = !hasBF && user.gear.melee.hasEquipped('Amulet of torture'); + + const res = startColosseumRun({ + kcBank: new ColosseumWaveBank((await user.fetchStats({ colo_kc_bank: true })).colo_kc_bank as ItemBank), + hasScythe, + hasTBow, + hasVenBow, + hasBF, + hasClaws, + hasSGS, + hasTorture + }); + const minutes = res.realDuration / Time.Minute; + + const chargeBank = new ChargeBank(); + const cost = new Bank().add('Saradomin brew(4)', 6).add('Super restore(4)', 8).add('Super combat potion(4)'); + + if (user.bank.has('Ranging potion(4)')) { + cost.add('Ranging potion(4)'); + } else if (user.bank.has('Bastion potion(4)')) { + cost.add('Bastion potion(4)'); + } else { + return 'You need to have a Ranging potion(4) or Bastion potion(4) in your bank.'; + } + + const scytheCharges = 300; + if (hasScythe) { + messages.push('10% boost for Scythe'); + chargeBank.add('scythe_of_vitur_charges', scytheCharges); + } else { + messages.push('Missed 10% Scythe boost. If you have one, charge it and equip to melee.'); + } + if (hasTBow) { + messages.push('10% boost for TBow'); + const arrowsNeeded = Math.ceil(minutes * 3); + cost.add('Dragon arrow', arrowsNeeded); + } else { + messages.push( + 'Missed 10% TBow boost. If you have one, equip it to range. You also need dragon arrows equipped.' + ); + } + if (hasVenBow) { + messages.push('7% boost for Venator bow'); + chargeBank.add('venator_bow_charges', calculateVenCharges()); + cost.add('Dragon arrow', 50); + } else { + messages.push( + 'Missed 7% Venator bow boost. If you have one, charge it and keep it in your bank. You also need atleast 50 dragon arrows equipped.' + ); + } + + if (hasClaws) { + messages.push('4% boost for Dragon claws'); + } else { + messages.push('Missed 4% Dragon claws boost.'); + } + + if (hasTorture) { + messages.push('5% boost for Torture'); + } else { + messages.push('Missed 5% Torture boost.'); + } + + if (user.gear.melee.hasEquipped('Amulet of blood fury')) { + chargeBank.add('blood_fury_charges', scytheCharges * 3); + messages.push('-5% death chance for blood fury'); + } else { + messages.push('Missed -5% death chance for blood fury. If you have one, add charges and equip it to melee.'); + } + + if (hasSGS) { + messages.push('-5% death chance for Saradomin godsword'); + } else { + messages.push('Missed -5% death chance boost for Saradomin godsword.'); + } + + const realCost = new Bank(); + try { + const result = await user.specialRemoveItems(cost); + realCost.add(result.realCost); + } catch (err: any) { + if (err instanceof UserError) { + return err.message; + } + throw err; + } + messages.push(`Removed ${realCost}`); + + await updateBankSetting('colo_cost', realCost); + await userStatsBankUpdate(user.id, 'colo_cost', realCost); + await trackLoot({ + totalCost: realCost, + id: 'colo', + type: 'Minigame', + changeType: 'cost', + users: [ + { + id: user.id, + cost: realCost + } + ] + }); + + if (chargeBank.length() > 0) { + const hasChargesResult = user.hasCharges(chargeBank); + if (!hasChargesResult.hasCharges) { + return hasChargesResult.fullUserString!; + } + + const degradeResults = await degradeChargeBank(user, chargeBank); + messages.push(degradeResults.map(i => i.userMessage).join(', ')); + } + + await addSubTaskToActivityTask({ + userID: user.id, + channelID, + duration: res.realDuration, + type: 'Colosseum', + fakeDuration: res.fakeDuration, + maxGlory: res.maxGlory, + diedAt: res.diedAt ?? undefined, + loot: res.loot?.bank + }); + + return `${user.minionName} is now attempting the Colosseum. They will finish in around ${formatDuration( + res.fakeDuration + )}, unless they die early. ${messages.join(', ')}`; +} diff --git a/src/lib/combat_achievements/combatAchievements.ts b/src/lib/combat_achievements/combatAchievements.ts index 0565bd60285..4a6574c39c7 100644 --- a/src/lib/combat_achievements/combatAchievements.ts +++ b/src/lib/combat_achievements/combatAchievements.ts @@ -164,6 +164,9 @@ export const combatAchievementTripEffect: TripFinishEffect['fn'] = async ({ data if (dataCopy.type === 'Inferno' && !dataCopy.diedPreZuk && !dataCopy.diedZuk) { (dataCopy as any).quantity = 1; } + if (dataCopy.type === 'Colosseum') { + (dataCopy as any).quantity = 1; + } if (!('quantity' in dataCopy)) return; let quantity = Number(dataCopy.quantity); if (isNaN(quantity)) return; diff --git a/src/lib/combat_achievements/elite.ts b/src/lib/combat_achievements/elite.ts index 3ce588a3003..ac167d2a044 100644 --- a/src/lib/combat_achievements/elite.ts +++ b/src/lib/combat_achievements/elite.ts @@ -11,7 +11,7 @@ import { import { anyoneDiedInTOARaid } from '../simulation/toa'; import { SkillsEnum } from '../skilling/types'; import { Requirements } from '../structures/Requirements'; -import { GauntletOptions, NightmareActivityTaskOptions, TOAOptions } from '../types/minions'; +import { ActivityTaskData, GauntletOptions, NightmareActivityTaskOptions, TOAOptions } from '../types/minions'; import { isCertainMonsterTrip } from './caUtils'; import { type CombatAchievement } from './combatAchievements'; @@ -1491,5 +1491,40 @@ export const eliteCombatAchievements: CombatAchievement[] = [ [Monsters.Zulrah.id]: 75 } }) + }, + { + id: 1129, + name: 'I was here first!', + desc: 'Kill a Jaguar Warrior using a Claw-type weapon special attack.', + type: 'mechanical', + monster: 'Colosseum', + rng: { + chancePerKill: 5, + hasChance: 'Colosseum' + } + }, + { + id: 1130, + name: 'Denied', + desc: 'Complete Wave 7 without the Minotaur ever healing other enemies.', + type: 'mechanical', + monster: 'Colosseum', + rng: { + chancePerKill: 12, + hasChance: (data: ActivityTaskData) => + data.type === 'Colosseum' && (!data.diedAt || (Boolean(data.diedAt) && data.diedAt > 7)) + } + }, + { + id: 1131, + name: 'Furball', + desc: 'Complete Wave 4 without taking avoidable damage from a Manticore.', + type: 'perfection', + monster: 'Colosseum', + rng: { + chancePerKill: 12, + hasChance: (data: ActivityTaskData) => + data.type === 'Colosseum' && (!data.diedAt || (Boolean(data.diedAt) && data.diedAt > 4)) + } } ]; diff --git a/src/lib/combat_achievements/grandmaster.ts b/src/lib/combat_achievements/grandmaster.ts index 4e80b29d212..35e60e959ed 100644 --- a/src/lib/combat_achievements/grandmaster.ts +++ b/src/lib/combat_achievements/grandmaster.ts @@ -1,9 +1,11 @@ +import { Time } from 'e'; import { Monsters } from 'oldschooljs'; import { PHOSANI_NIGHTMARE_ID } from '../constants'; import { anyoneDiedInTOARaid } from '../simulation/toa'; import { Requirements } from '../structures/Requirements'; import { + ActivityTaskData, GauntletOptions, NexTaskOptions, NightmareActivityTaskOptions, @@ -1026,5 +1028,62 @@ export const grandmasterCombatAchievements: CombatAchievement[] = [ chancePerKill: 110, hasChance: isCertainMonsterTrip(Monsters.Zulrah.id) } + }, + { + id: 3090, + name: 'Colosseum Speed-Runner', + desc: 'Complete the Colosseum with a total time of 24:00 or less.', + type: 'speed', + monster: 'Colosseum', + rng: { + chancePerKill: 1, + hasChance: (data: ActivityTaskData) => + data.type === 'Colosseum' && !data.diedAt && data.duration < Time.Minute * 24 + } + }, + { + id: 3091, + name: 'Slow Dancing in the Sand', + desc: 'Defeat Sol Heredit without running during the fight with him.', + type: 'restriction', + monster: 'Colosseum', + rng: { + chancePerKill: 15, + hasChance: (data: ActivityTaskData) => data.type === 'Colosseum' && !data.diedAt + } + }, + { + id: 3092, + name: 'Reinforcements', + desc: 'Defeat Sol Heredit with "Bees II", "Quartet" and "Solarflare II" modifiers active.', + type: 'mechanical', + monster: 'Colosseum', + rng: { + chancePerKill: 30, + hasChance: (data: ActivityTaskData) => data.type === 'Colosseum' && !data.diedAt + } + }, + { + id: 3093, + name: 'Perfect Footwork', + desc: 'Defeat Sol Heredit without taking any damage from his Spear, Shield, Grapple or Triple Attack.', + type: 'perfection', + monster: 'Colosseum', + rng: { + chancePerKill: 20, + hasChance: (data: ActivityTaskData) => data.type === 'Colosseum' && !data.diedAt + } + }, + { + id: 3094, + name: 'Colosseum Grand Champion', + desc: 'Defeat Sol Heredit 10 times.', + type: 'kill_count', + monster: 'Colosseum', + requirements: new Requirements().add({ + minigames: { + colosseum: 10 + } + }) } ]; diff --git a/src/lib/combat_achievements/master.ts b/src/lib/combat_achievements/master.ts index 1aaeca68da2..1f43f3e5b31 100644 --- a/src/lib/combat_achievements/master.ts +++ b/src/lib/combat_achievements/master.ts @@ -1,9 +1,11 @@ +import { Time } from 'e'; import { Monsters } from 'oldschooljs'; import { NEX_ID, NIGHTMARE_ID, PHOSANI_NIGHTMARE_ID } from '../constants'; import { anyoneDiedInTOARaid } from '../simulation/toa'; import { Requirements } from '../structures/Requirements'; import { + ActivityTaskData, GauntletOptions, MonsterActivityTaskOptions, NightmareActivityTaskOptions, @@ -1460,5 +1462,63 @@ export const masterCombatAchievements: CombatAchievement[] = [ chancePerKill: 75, hasChance: isCertainMonsterTrip(Monsters.Zulrah.id) } + }, + { + id: 2129, + name: 'One-off', + desc: "Complete Wave 11 with either 'Red Flag', 'Dynamic Duo', or 'Doom II' active.", + type: 'mechanical', + monster: 'Colosseum', + rng: { + chancePerKill: 15, + hasChance: (data: ActivityTaskData) => + data.type === 'Colosseum' && (!data.diedAt || (Boolean(data.diedAt) && data.diedAt > 11)) + } + }, + { + id: 2130, + name: 'Showboating', + desc: 'Defeat Sol Heredit after using Fortis Salute to the north, east, south and west of the arena while he is below 10% hitpoints.', + type: 'mechanical', + monster: 'Colosseum', + rng: { + chancePerKill: 15, + hasChance: (data: ActivityTaskData) => data.type === 'Colosseum' && !data.diedAt + } + }, + { + id: 2131, + name: 'I Brought Mine Too', + desc: 'Defeat Sol Heredit using only a Spear, Hasta or Halberd.', + type: 'restriction', + monster: 'Colosseum', + rng: { + chancePerKill: 15, + hasChance: (data: ActivityTaskData) => data.type === 'Colosseum' && !data.diedAt + } + }, + { + id: 2132, + name: 'Sportsmanship', + desc: 'Defeat Sol Heredit once.', + type: 'kill_count', + monster: 'Colosseum', + requirements: new Requirements().add({ + minigames: { + colosseum: 1 + } + }) + }, + { + id: 2133, + name: 'Colosseum Speed-Chaser', + desc: 'Complete the Colosseum with a total time of 28:00 or less.', + type: 'speed', + monster: 'Colosseum', + rng: { + chancePerKill: 1, + hasChance: (data: ActivityTaskData) => + data.type === 'Colosseum' && !data.diedAt && data.duration < Time.Minute * 28 + } } ]; diff --git a/src/lib/constants.ts b/src/lib/constants.ts index caca5f5fbb4..d71edf2fa23 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -1,7 +1,7 @@ import path from 'node:path'; import { Image } from '@napi-rs/canvas'; -import { StoreBitfield } from '@oldschoolgg/toolkit'; +import { SimpleTable, StoreBitfield } from '@oldschoolgg/toolkit'; import { execSync } from 'child_process'; import { APIButtonComponent, ButtonBuilder, ButtonStyle, ComponentType } from 'discord.js'; import * as dotenv from 'dotenv'; @@ -629,3 +629,26 @@ export const patronFeatures = { export const gearValidationChecks = new Set(); export const BSO_MAX_TOTAL_LEVEL = 3120; + +export const winterTodtPointsTable = new SimpleTable() + .add(420) + .add(470) + .add(500) + .add(505) + .add(510) + .add(520) + .add(550) + .add(560) + .add(590) + .add(600) + .add(620) + .add(650) + .add(660) + .add(670) + .add(680) + .add(700) + .add(720) + .add(740) + .add(750) + .add(780) + .add(850); diff --git a/src/lib/data/Collections.ts b/src/lib/data/Collections.ts index b72ed565e11..64353681b15 100644 --- a/src/lib/data/Collections.ts +++ b/src/lib/data/Collections.ts @@ -317,6 +317,24 @@ export const allCollectionLogs: ICollection = { items: fightCavesCL, fmtProg: kcProg(Monsters.TzTokJad) }, + 'Fortis Colosseum': { + kcActivity: { + Default: async (_, minigameScores) => + minigameScores.find(i => i.minigame.column === 'colosseum')!.score + }, + alias: ['colosseum'], + items: resolveItems([ + 'Smol heredit', + "Dizana's quiver (uncharged)", + 'Sunfire fanatic cuirass', + 'Sunfire fanatic chausses', + 'Sunfire fanatic helm', + 'Echo crystal', + 'Tonalztics of ralos (uncharged)', + 'Sunfire splinters' + ]), + fmtProg: ({ minigames }) => `${minigames.colosseum} KC` + }, 'The Gauntlet': { alias: ['gauntlet', 'crystalline hunllef', 'hunllef'], kcActivity: { diff --git a/src/lib/data/buyables/buyables.ts b/src/lib/data/buyables/buyables.ts index 6dfc45a132f..8a6dfd6fee3 100644 --- a/src/lib/data/buyables/buyables.ts +++ b/src/lib/data/buyables/buyables.ts @@ -1,8 +1,8 @@ import { Bank } from 'oldschooljs'; -import { QuestID } from '../../../mahoji/lib/abstracted_commands/questCommand'; import { chompyHats } from '../../constants'; import { CombatCannonItemBank } from '../../minions/data/combatConstants'; +import { QuestID } from '../../minions/data/quests'; import { MinigameName } from '../../settings/settings'; import { soteSkillRequirements } from '../../skilling/functions/questRequirements'; import { MUserStats } from '../../structures/MUserStats'; @@ -757,7 +757,7 @@ const Buyables: Buyable[] = [ name: 'Feather', aliases: ['feather'], gpCost: 50, - ironmanPrice: 2 + ironmanPrice: 4 }, { name: 'Shield right half', diff --git a/src/lib/data/buyables/capes.ts b/src/lib/data/buyables/capes.ts index 246ba0bef54..b9153750b12 100644 --- a/src/lib/data/buyables/capes.ts +++ b/src/lib/data/buyables/capes.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; -import { MAX_QP } from '../../../mahoji/lib/abstracted_commands/questCommand'; import { diaries, userhasDiaryTier } from '../../diaries'; +import { MAX_QP } from '../../minions/data/quests'; import { musicCapeRequirements } from '../../musicCape'; import { Buyable } from './buyables'; diff --git a/src/lib/data/creatablesTable.txt b/src/lib/data/creatablesTable.txt index 508d3468788..64bbc56712b 100644 --- a/src/lib/data/creatablesTable.txt +++ b/src/lib/data/creatablesTable.txt @@ -95,6 +95,8 @@ | Bone shortbow | 1x Yew shortbow, 1x Scurrius' spine | 1x Bone shortbow | 0 | | Bone staff | 1,000x Chaos rune, 1x Battlestaff, 1x Scurrius' spine | 1x Bone staff | 0 | | Venator bow (uncharged) | 5x Venator shard | 1x Venator bow (uncharged) | 0 | +| Blessed dizana's quiver | 150,000x Sunfire splinters, 1x Dizana's quiver (uncharged) | 1x Blessed dizana's quiver | 0 | +| Dizana's max cape | 1x Max cape, 1x Max hood, 1x Blessed dizana's quiver | 1x Dizana's max cape, 1x Dizana's max hood | 0 | | Revert tanzanite fang | 1x Tanzanite fang | 20,000x Zulrah's scales | 0 | | Revert toxic blowpipe (empty) | 1x Toxic blowpipe (empty) | 20,000x Zulrah's scales | 0 | | Revert magic fang | 1x Magic fang | 20,000x Zulrah's scales | 0 | @@ -160,6 +162,7 @@ | Revert great blue heron | 1x Great blue heron | 1x Heron | 0 | | Revert greatish guardian | 1x Greatish guardian | 1x Rift guardian, 1x Guardian's eye | 0 | | Revert xeric's talisman (inert) | 1x Xeric's talisman (inert) | 100x Lizardman fang | 0 | +| Revert Dizana's quiver (uncharged) | 1x Dizana's quiver (uncharged) | 4,000x Sunfire splinters | 0 | | Crystal pickaxe | 120x Crystal shard, 1x Dragon pickaxe, 1x Crystal tool seed | 1x Crystal pickaxe | 0 | | Crystal harpoon | 120x Crystal shard, 1x Dragon harpoon, 1x Crystal tool seed | 1x Crystal harpoon | 0 | | Crystal axe | 120x Crystal shard, 1x Dragon axe, 1x Crystal tool seed | 1x Crystal axe | 0 | diff --git a/src/lib/data/createables.ts b/src/lib/data/createables.ts index 04376d0c8e8..4557444d6d3 100644 --- a/src/lib/data/createables.ts +++ b/src/lib/data/createables.ts @@ -1364,6 +1364,12 @@ const Reverteables: Createable[] = [ [itemID('Lizardman fang')]: 100 }, noCl: true + }, + { + name: "Revert Dizana's quiver (uncharged)", + inputItems: new Bank().add("Dizana's quiver (uncharged)"), + outputItems: new Bank().add('Sunfire splinters', 4000), + noCl: true } ]; @@ -2378,6 +2384,16 @@ const Createables: Createable[] = [ inputItems: new Bank().add('Venator shard', 5).freeze(), outputItems: new Bank().add('Venator bow (uncharged)').freeze() }, + { + name: "Blessed dizana's quiver", + inputItems: new Bank().add('Sunfire splinters', 150_000).add("Dizana's quiver (uncharged)").freeze(), + outputItems: new Bank().add("Blessed dizana's quiver").freeze() + }, + { + name: "Dizana's max cape", + inputItems: new Bank().add("Blessed dizana's quiver").add('Max cape').add('Max hood').freeze(), + outputItems: new Bank().add("Dizana's max cape").add("Dizana's max hood").freeze() + }, ...Reverteables, ...crystalTools, ...ornamentKits, diff --git a/src/lib/data/similarItems.ts b/src/lib/data/similarItems.ts index 455e28132ff..0176dedd83e 100644 --- a/src/lib/data/similarItems.ts +++ b/src/lib/data/similarItems.ts @@ -191,7 +191,14 @@ const source: [string, (string | number)[]][] = [ ["Ava's accumulator", ['Accumulator max cape']], [ "Ava's assembler", - ['Assembler max cape', 'Assembler max cape (l)', 'Masori assembler', 'Masori assembler max cape'] + [ + 'Assembler max cape', + 'Assembler max cape (l)', + 'Masori assembler', + 'Masori assembler max cape', + "Blessed dizana's quiver", + "Dizana's max cape" + ] ], ['Mythical cape', ['Mythical max cape']], ['Achievement diary cape', ['Achievement diary cape(t)']], diff --git a/src/lib/degradeableItems.ts b/src/lib/degradeableItems.ts index 50e30b00d35..49693387702 100644 --- a/src/lib/degradeableItems.ts +++ b/src/lib/degradeableItems.ts @@ -5,12 +5,13 @@ import Monster from 'oldschooljs/dist/structures/Monster'; import { GearSetupType } from './gear/types'; import { KillableMonster } from './minions/types'; +import { ChargeBank } from './structures/Bank'; import { assert } from './util'; import getOSItem from './util/getOSItem'; import itemID from './util/itemID'; import { updateBankSetting } from './util/updateBankSetting'; -interface DegradeableItem { +export interface DegradeableItem { item: Item; settingsKey: | 'tentacle_charges' @@ -404,3 +405,24 @@ export async function checkDegradeableItemCharges({ item, user }: { item: Item; assert(typeof currentCharges === 'number'); return currentCharges; } + +export async function degradeChargeBank(user: MUser, chargeBank: ChargeBank) { + const hasChargesResult = user.hasCharges(chargeBank); + if (!hasChargesResult.hasCharges) { + throw new Error( + `Tried to degrade a charge bank (${chargeBank}) for ${ + user.logName + }, but they don't have the required charges: ${JSON.stringify(hasChargesResult)}` + ); + } + + const results = []; + + for (const [key, chargesToDegrade] of chargeBank.entries()) { + const { item } = degradeableItems.find(i => i.settingsKey === key)!; + const result = await degradeItem({ item, chargesToDegrade, user }); + results.push(result); + } + + return results; +} diff --git a/src/lib/diaries.ts b/src/lib/diaries.ts index 2efd0d0173f..8e717f234a8 100644 --- a/src/lib/diaries.ts +++ b/src/lib/diaries.ts @@ -1,34 +1,20 @@ import { objectEntries } from 'e'; import { Monsters } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; -import { MAX_QP } from '../mahoji/lib/abstracted_commands/questCommand'; -import type { MinigameName, MinigameScore } from './settings/minigames'; +import { MAX_QP } from './minions/data/quests'; +import { DiaryID, DiaryTier, DiaryTierName } from './minions/types'; +import type { MinigameScore } from './settings/minigames'; import Skillcapes from './skilling/skillcapes'; import { courses } from './skilling/skills/agility'; import { MUserStats } from './structures/MUserStats'; -import { Skills } from './types'; -import { formatSkillRequirements, hasSkillReqs, itemNameFromID } from './util'; +import type { Skills } from './types'; import getOSItem from './util/getOSItem'; import resolveItems from './util/resolveItems'; +import { formatSkillRequirements, hasSkillReqs, itemNameFromID } from './util/smallUtils'; -export const diaryTiers = ['easy', 'medium', 'hard', 'elite'] as const; -export type DiaryTierName = (typeof diaryTiers)[number]; - -export interface DiaryTier { - name: 'Easy' | 'Medium' | 'Hard' | 'Elite'; - items: Item[]; - skillReqs: Skills; - ownedItems?: number[]; - collectionLogReqs?: number[]; - minigameReqs?: Partial>; - lapsReqs?: Record; - qp?: number; - monsterScores?: Record; - customReq?: (user: MUser, summary: Boolean, stats: MUserStats) => [true] | [false, string]; -} export interface Diary { name: string; + id: DiaryID; alias?: string[]; easy: DiaryTier; medium: DiaryTier; @@ -140,6 +126,7 @@ export async function userhasDiaryTier(user: MUser, tier: DiaryTier): Promise<[t export const WesternProv: Diary = { name: 'Western Provinces', + id: DiaryID.WesternProvinces, alias: ['western', 'wp', 'west', 'west prov'], easy: { name: 'Easy', @@ -238,6 +225,7 @@ export const WesternProv: Diary = { }; export const ArdougneDiary: Diary = { name: 'Ardougne', + id: DiaryID.Ardougne, alias: ['ardy', 'ardougn'], easy: { name: 'Easy', @@ -329,6 +317,7 @@ export const ArdougneDiary: Diary = { export const DesertDiary: Diary = { name: 'Desert', + id: DiaryID.Desert, easy: { name: 'Easy', items: [getOSItem('Desert amulet 1')], @@ -412,6 +401,7 @@ export const DesertDiary: Diary = { export const FaladorDiary: Diary = { name: 'Falador', + id: DiaryID.Falador, alias: ['fally', 'fal'], easy: { name: 'Easy', @@ -512,6 +502,7 @@ export const FaladorDiary: Diary = { export const FremennikDiary: Diary = { name: 'Fremennik', + id: DiaryID.Fremennik, alias: ['fremmy', 'fremenik', 'fremmenik', 'frem'], easy: { name: 'Easy', @@ -592,6 +583,7 @@ export const FremennikDiary: Diary = { export const KandarinDiary: Diary = { name: 'Kandarin', + id: DiaryID.Kandarin, alias: ['kand'], easy: { name: 'Easy', @@ -681,6 +673,7 @@ export const KandarinDiary: Diary = { export const KaramjaDiary: Diary = { name: 'Karamja', + id: DiaryID.Karamja, alias: ['ramja', 'ram', 'karam', 'kar'], easy: { name: 'Easy', @@ -743,6 +736,7 @@ export const KaramjaDiary: Diary = { export const KourendKebosDiary: Diary = { name: 'Kourend & Kebos', + id: DiaryID.KourendKebos, alias: ['kebos', 'kouren', 'kourend', 'kk', 'kek'], easy: { name: 'Easy', @@ -818,6 +812,7 @@ export const KourendKebosDiary: Diary = { }; export const LumbridgeDraynorDiary: Diary = { name: 'Lumbridge & Draynor', + id: DiaryID.LumbridgeDraynor, alias: ['lumb', 'draynor', 'lumbridge', 'led'], easy: { name: 'Easy', @@ -890,6 +885,7 @@ export const LumbridgeDraynorDiary: Diary = { export const MorytaniaDiary: Diary = { name: 'Morytania', + id: DiaryID.Morytania, alias: ['mory', 'swamp'], easy: { name: 'Easy', @@ -969,6 +965,7 @@ export const MorytaniaDiary: Diary = { export const VarrockDiary: Diary = { name: 'Varrock', + id: DiaryID.Varrock, alias: ['var'], easy: { name: 'Easy', @@ -1036,6 +1033,7 @@ export const VarrockDiary: Diary = { export const WildernessDiary: Diary = { name: 'Wilderness', + id: DiaryID.Wilderness, alias: ['wild', 'wildy'], easy: { name: 'Easy', @@ -1130,3 +1128,17 @@ export const diariesObject = { WildernessDiary } as const; export const diaries = Object.values(diariesObject); + +export async function userhasDiaryIDTier(user: MUser, type: DiaryID, tier: DiaryTierName) { + const diaryGroup = diaries.find(d => d.id === type)!; + const diaryTier = diaryGroup[tier]!; + const [hasDiary] = userhasDiaryTierSync(user, diaryTier, { + stats: await MUserStats.fromID(user.id), + minigameScores: await user.fetchMinigameScores() + }); + return { + hasDiary, + diaryGroup, + diaryTier + }; +} diff --git a/src/lib/implings.ts b/src/lib/implings.ts index c907ca471c7..021fef3b80d 100644 --- a/src/lib/implings.ts +++ b/src/lib/implings.ts @@ -117,7 +117,8 @@ export function handlePassiveImplings(user: MUser, data: ActivityTaskData) { activity_type_enum.Construction, activity_type_enum.TombsOfAmascut, activity_type_enum.DriftNet, - activity_type_enum.UnderwaterAgilityThieving + activity_type_enum.UnderwaterAgilityThieving, + activity_type_enum.Colosseum ].includes(data.type) ) return null; diff --git a/src/lib/itemMods.ts b/src/lib/itemMods.ts index 6c3442249b8..c24351ec9c4 100644 --- a/src/lib/itemMods.ts +++ b/src/lib/itemMods.ts @@ -1,3 +1,7 @@ +import { modifyItem } from '@oldschoolgg/toolkit'; + +import { allTeamCapes } from './data/misc'; + export interface CustomItemData { cantBeSacrificed?: true; } @@ -7,4 +11,8 @@ declare module 'oldschooljs/dist/meta/types' { } } -export {}; +for (const item of allTeamCapes) { + modifyItem(item.id, { + price: 100 + }); +} diff --git a/src/lib/mastery.ts b/src/lib/mastery.ts index 98cd575363a..a7fbd581199 100644 --- a/src/lib/mastery.ts +++ b/src/lib/mastery.ts @@ -1,10 +1,10 @@ import { calcWhatPercent, clamp, round, sumArr } from 'e'; import { calculateAchievementDiaryProgress } from '../mahoji/lib/abstracted_commands/achievementDiaryCommand'; -import { MAX_QP } from '../mahoji/lib/abstracted_commands/questCommand'; import { allCombatAchievementTasks } from './combat_achievements/combatAchievements'; import { MAX_XP } from './constants'; import { getTotalCl } from './data/Collections'; +import { MAX_QP } from './minions/data/quests'; import { SkillsEnum } from './skilling/types'; import { MUserStats } from './structures/MUserStats'; diff --git a/src/lib/minions/data/killableMonsters/bosses/dt.ts b/src/lib/minions/data/killableMonsters/bosses/dt.ts index a70f4243ae4..cf4e032c713 100644 --- a/src/lib/minions/data/killableMonsters/bosses/dt.ts +++ b/src/lib/minions/data/killableMonsters/bosses/dt.ts @@ -1,14 +1,13 @@ -import { Time } from 'e'; +import { roll, Time } from 'e'; import { Bank, Monsters } from 'oldschooljs'; -import { QuestID } from '../../../../../mahoji/lib/abstracted_commands/questCommand'; import { dukeSucellusCL, theLeviathanCL, theWhispererCL, vardorvisCL } from '../../../../data/CollectionsExport'; import { GearStat } from '../../../../gear/types'; import { SkillsEnum } from '../../../../skilling/types'; -import { roll } from '../../../../util'; import itemID from '../../../../util/itemID'; import resolveItems, { deepResolveItems } from '../../../../util/resolveItems'; import { KillableMonster } from '../../../types'; +import { QuestID } from '../../quests'; const awakenedDeathProps = { hardness: 0.9, diff --git a/src/lib/minions/data/killableMonsters/bosses/wildy.ts b/src/lib/minions/data/killableMonsters/bosses/wildy.ts index c414511a445..ad7ff30ff1d 100644 --- a/src/lib/minions/data/killableMonsters/bosses/wildy.ts +++ b/src/lib/minions/data/killableMonsters/bosses/wildy.ts @@ -1,12 +1,11 @@ import { Time } from 'e'; import { Bank, Monsters } from 'oldschooljs'; -import { WildernessDiary } from '../../../../diaries'; import { GearStat } from '../../../../gear/types'; import { SkillsEnum } from '../../../../skilling/types'; import itemID from '../../../../util/itemID'; import resolveItems, { deepResolveItems } from '../../../../util/resolveItems'; -import { KillableMonster } from '../../../types'; +import { DiaryID, KillableMonster } from '../../../types'; export const wildyKillableMonsters: KillableMonster[] = [ { @@ -253,7 +252,7 @@ export const wildyKillableMonsters: KillableMonster[] = [ ranged: 65, magic: 70 }, - diaryRequirement: [WildernessDiary, WildernessDiary.medium], + diaryRequirement: [DiaryID.Wilderness, 'medium'], defaultAttackStyles: [SkillsEnum.Ranged], healAmountNeeded: 8 * 20, attackStyleToUse: GearStat.AttackRanged, @@ -484,7 +483,7 @@ export const wildyKillableMonsters: KillableMonster[] = [ defence: 70, magic: 70 }, - diaryRequirement: [WildernessDiary, WildernessDiary.medium], + diaryRequirement: [DiaryID.Wilderness, 'medium'], defaultAttackStyles: [SkillsEnum.Attack], customMonsterHP: 420, healAmountNeeded: 8 * 20, @@ -766,7 +765,7 @@ export const wildyKillableMonsters: KillableMonster[] = [ defence: 70, magic: 70 }, - diaryRequirement: [WildernessDiary, WildernessDiary.medium], + diaryRequirement: [DiaryID.Wilderness, 'medium'], defaultAttackStyles: [SkillsEnum.Attack], healAmountNeeded: 8 * 20, attackStyleToUse: GearStat.AttackCrush, diff --git a/src/lib/minions/data/quests.ts b/src/lib/minions/data/quests.ts new file mode 100644 index 00000000000..f6e8c2641fb --- /dev/null +++ b/src/lib/minions/data/quests.ts @@ -0,0 +1,217 @@ +import { sumArr, Time } from 'e'; +import { Bank } from 'oldschooljs'; + +import { Skills } from '../../types'; + +interface Quest { + id: QuestID; + qp: number; + name: string; + skillReqs?: Skills; + ironmanSkillReqs?: Skills; + qpReq?: number; + rewards?: Bank; + skillsRewards?: { + [skill: string]: number; + }; + combatLevelReq?: number; + prerequisitesQuests?: QuestID[]; + calcTime: (user: MUser) => number; +} + +export enum QuestID { + DesertTreasureII = 1, + ThePathOfGlouphrie = 2, + ChildrenOfTheSun = 3, + DefenderOfVarrock = 4, + TheRibbitingTaleOfALillyPadLabourDispute = 5, + PerilousMoons = 6, + AtFirstLight = 7, + TwilightsPromise = 8 +} + +export const quests: Quest[] = [ + { + id: QuestID.DesertTreasureII, + qp: 5, + name: 'Desert Treasure II - The Fallen Empire', + skillReqs: { + firemaking: 75, + magic: 75, + thieving: 70, + herblore: 62, + runecraft: 60, + construction: 60 + }, + combatLevelReq: 110, + qpReq: 150, + rewards: new Bank().add(28_409, 3).add('Ring of shadows').freeze(), + calcTime: (user: MUser) => { + let duration = Time.Hour * 3; + if (user.combatLevel < 100) { + duration += Time.Minute * 30; + } + if (user.combatLevel < 90) { + duration += Time.Minute * 40; + } + const percentOfBossCL = user.percentOfBossCLFinished(); + if (percentOfBossCL < 10) { + duration += Time.Minute * 20; + } else if (percentOfBossCL < 30) { + duration += Time.Minute * 10; + } else if (percentOfBossCL > 80) { + duration -= Time.Minute * 60; + } else if (percentOfBossCL > 50) { + duration -= Time.Minute * 30; + } + return duration; + } + }, + { + id: QuestID.ThePathOfGlouphrie, + qp: 2, + name: 'The Path of Glouphrie', + skillReqs: { + strength: 60, + slayer: 56, + thieving: 56, + ranged: 47, + agility: 45 + }, + ironmanSkillReqs: { + fletching: 59, + smithing: 59 + }, + combatLevelReq: 50, + qpReq: 10, + rewards: new Bank().add(28_587).add(28_587).add(28_588).add(28_589).add(28_590).freeze(), + calcTime: (user: MUser) => { + let duration = Time.Minute * 10; + if (user.combatLevel < 90) { + duration += Time.Minute * 5; + } + return duration; + } + }, + { + id: QuestID.ChildrenOfTheSun, + qp: 1, + name: 'Children of the Sun', + calcTime: () => { + const duration = Time.Minute * 3; + return duration; + } + }, + { + id: QuestID.DefenderOfVarrock, + qp: 2, + name: 'Defender of Varrock', + skillReqs: { + smithing: 55, + hunter: 52 + }, + combatLevelReq: 65, + qpReq: 20, + // Awaiting item update for the lamp to be added + // rewards: new Bank().add(28_820).freeze(), + skillsRewards: { + smithing: 15_000, + hunter: 15_000 + }, + calcTime: (user: MUser) => { + let duration = Time.Minute * 12; + if (user.combatLevel < 100) { + duration += Time.Minute * 8; + } + return duration; + } + }, + { + id: QuestID.TheRibbitingTaleOfALillyPadLabourDispute, + qp: 1, + name: 'The Ribbiting Tale of a Lily Pad Labour Dispute', + skillReqs: { + woodcutting: 15 + }, + prerequisitesQuests: [QuestID.ChildrenOfTheSun], + skillsRewards: { + woodcutting: 2000 + }, + calcTime: () => { + const duration = Time.Minute * 3; + return duration; + } + }, + { + id: QuestID.PerilousMoons, + qp: 2, + name: 'Perilous Moons', + skillReqs: { + slayer: 48, + hunter: 20, + fishing: 20, + runecraft: 20, + construction: 10 + }, + combatLevelReq: 75, + prerequisitesQuests: [QuestID.ChildrenOfTheSun, QuestID.TwilightsPromise], + skillsRewards: { + slayer: 40_000, + runecraft: 5000, + hunter: 5000, + fishing: 5000 + }, + calcTime: (user: MUser) => { + let duration = Time.Minute * 20; + if (user.combatLevel < 120) { + duration += Time.Minute * 5; + } + if (user.combatLevel < 100) { + duration += Time.Minute * 10; + } + return duration; + } + }, + { + id: QuestID.AtFirstLight, + qp: 1, + name: 'At First Light', + skillReqs: { + hunter: 46, + herblore: 30, + construction: 27 + }, + combatLevelReq: 75, + qpReq: 2, + prerequisitesQuests: [QuestID.ChildrenOfTheSun], + skillsRewards: { + hunter: 4500, + construction: 800, + herblore: 500 + }, + calcTime: () => { + let duration = Time.Minute * 6; + return duration; + } + }, + { + id: QuestID.TwilightsPromise, + qp: 1, + name: "Twilight's Promise", + skillsRewards: { + thieving: 3000 + }, + combatLevelReq: 40, + prerequisitesQuests: [QuestID.ChildrenOfTheSun], + calcTime: (user: MUser) => { + let duration = Time.Minute * 9; + if (user.combatLevel < 75) { + duration += Time.Minute * 5; + } + return duration; + } + } +]; + +export const MAX_GLOBAL_QP = 293; +export const MAX_QP = MAX_GLOBAL_QP + sumArr(quests.map(i => i.qp)); diff --git a/src/lib/minions/types.ts b/src/lib/minions/types.ts index 48d197a735b..d51658f0b71 100644 --- a/src/lib/minions/types.ts +++ b/src/lib/minions/types.ts @@ -2,18 +2,20 @@ import { Image } from '@napi-rs/canvas'; import { StoreBitfield } from '@oldschoolgg/toolkit'; import { XpGainSource } from '@prisma/client'; import { Bank, MonsterKillOptions } from 'oldschooljs'; +import { Item } from 'oldschooljs/dist/meta/types'; import SimpleMonster from 'oldschooljs/dist/structures/SimpleMonster'; -import { QuestID } from '../../mahoji/lib/abstracted_commands/questCommand'; import { ClueTier } from '../clues/clueTiers'; import { BitField, PerkTier } from '../constants'; -import { Diary, DiaryTier } from '../diaries'; import { GearSetupType, GearStat, OffenceGearStat } from '../gear/types'; import { POHBoosts } from '../poh'; +import { MinigameName } from '../settings/minigames'; import { LevelRequirements, SkillsEnum } from '../skilling/types'; +import type { MUserStats } from '../structures/MUserStats'; import { ArrayItemsResolved, ItemBank, Skills } from '../types'; import { MonsterActivityTaskOptions } from '../types/minions'; import { calculateSimpleMonsterDeathChance } from '../util'; +import { QuestID } from './data/quests'; import { AttackStyles } from './functions'; export type BankBackground = { @@ -133,7 +135,7 @@ export interface KillableMonster { }[]; requiredQuests?: QuestID[]; deathProps?: Omit['0'], 'currentKC'>; - diaryRequirement?: [Diary, DiaryTier]; + diaryRequirement?: [DiaryID, DiaryTierName]; wildySlayerCave?: boolean; } /* @@ -186,3 +188,33 @@ export interface BlowpipeData { export type Flags = Record; export type FlagMap = Map; export type ClueBank = Record; + +export const diaryTiers = ['easy', 'medium', 'hard', 'elite'] as const; +export type DiaryTierName = (typeof diaryTiers)[number]; + +export interface DiaryTier { + name: 'Easy' | 'Medium' | 'Hard' | 'Elite'; + items: Item[]; + skillReqs: Skills; + ownedItems?: number[]; + collectionLogReqs?: number[]; + minigameReqs?: Partial>; + lapsReqs?: Record; + qp?: number; + monsterScores?: Record; + customReq?: (user: MUser, summary: boolean, stats: MUserStats) => [true] | [false, string]; +} +export enum DiaryID { + WesternProvinces = 0, + Ardougne = 1, + Desert = 2, + Falador = 3, + Fremennik = 4, + Kandarin = 5, + Karamja = 6, + KourendKebos = 7, + LumbridgeDraynor = 8, + Morytania = 9, + Varrock = 10, + Wilderness = 11 +} diff --git a/src/lib/resources/images/minimus.png b/src/lib/resources/images/minimus.png new file mode 100644 index 0000000000000000000000000000000000000000..c2782c2acc500db2a857a1c1e8052e91e959f2ea GIT binary patch literal 2664 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 W3jqYc=!*gX0000 { const loot = new Bank(); for (let i = 0; i < quantity; i++) { - const points = PointsTable.rollOrThrow(); + const points = winterTodtPointsTable.rollOrThrow(); loot.add( WintertodtCrate.open({ diff --git a/src/lib/skilling/types.ts b/src/lib/skilling/types.ts index 117bc535c05..2060adeeb07 100644 --- a/src/lib/skilling/types.ts +++ b/src/lib/skilling/types.ts @@ -33,6 +33,41 @@ export enum SkillsEnum { Slayer = 'slayer' } +export const SkillsArray = [ + 'agility', + 'cooking', + 'fishing', + 'mining', + 'smithing', + 'woodcutting', + 'firemaking', + 'runecraft', + 'crafting', + 'prayer', + 'fletching', + 'farming', + 'herblore', + 'thieving', + 'hunter', + 'construction', + 'magic', + 'attack', + 'strength', + 'defence', + 'ranged', + 'hitpoints', + 'slayer' +] as const; + +export type SkillNameType = (typeof SkillsArray)[number]; +for (const skill of SkillsArray) { + const matching = Object.keys(SkillsEnum).find(key => key.toLowerCase() === skill); + if (!matching) throw new Error(`Missing skill enum for ${skill}`); +} +if (SkillsArray.length !== Object.keys(SkillsEnum).length) { + throw new Error('Not all skills have been added to the SkillsArray.'); +} + export interface Ore { level: number; xp: number; diff --git a/src/lib/structures/Bank.ts b/src/lib/structures/Bank.ts new file mode 100644 index 00000000000..501ae9cc36f --- /dev/null +++ b/src/lib/structures/Bank.ts @@ -0,0 +1,15 @@ +import { DegradeableItem, degradeableItems } from '../degradeableItems'; +import { SkillNameType, SkillsArray } from '../skilling/types'; +import { GeneralBank, GeneralBankType } from './GeneralBank'; + +export class ChargeBank extends GeneralBank { + constructor(initialBank?: GeneralBankType) { + super({ initialBank, allowedKeys: degradeableItems.map(i => i.settingsKey) }); + } +} + +export class XPBank extends GeneralBank { + constructor(initialBank?: GeneralBankType) { + super({ initialBank, allowedKeys: SkillsArray }); + } +} diff --git a/src/lib/structures/GeneralBank.ts b/src/lib/structures/GeneralBank.ts new file mode 100644 index 00000000000..a45b12add61 --- /dev/null +++ b/src/lib/structures/GeneralBank.ts @@ -0,0 +1,155 @@ +import { all, create } from 'mathjs'; + +import { assert } from '../util'; + +const mathjs = create(all); + +export type GeneralBankType = Record; + +interface GeneralBankValueSchema { + min: number; + max: number; + floats: boolean; +} + +interface BankValidator { + (key: T, value: number, bank: GeneralBankType): void; +} + +export class GeneralBank { + private bank: GeneralBankType; + private allowedKeys?: Set; + private validator?: BankValidator; + private valueSchema: GeneralBankValueSchema; + + constructor({ + allowedKeys, + validator, + initialBank, + valueSchema + }: { + allowedKeys?: T[] | readonly T[]; + validator?: BankValidator; + initialBank?: GeneralBankType; + valueSchema?: GeneralBankValueSchema; + } = {}) { + this.bank = initialBank ?? ({} as GeneralBankType); + this.allowedKeys = allowedKeys ? new Set(allowedKeys) : undefined; + this.validator = validator; + + this.valueSchema = valueSchema ?? { min: 1, max: Number.MAX_SAFE_INTEGER, floats: false }; + if (this.valueSchema.min < 0) throw new Error('Value schema min must be non-negative.'); + if (this.valueSchema.max < this.valueSchema.min) throw new Error('Value schema max must be greater than min.'); + + this.validate(); + } + + get _bank() { + return this.bank; + } + + clone(): GeneralBank { + return new GeneralBank({ + allowedKeys: this.allowedKeys ? Array.from(this.allowedKeys) : undefined, + validator: this.validator, + initialBank: { ...this.bank }, + valueSchema: this.valueSchema + }); + } + + validate(): void { + for (let key of Object.keys(this.bank) as T[]) { + const value = this.bank[key]; + if (this.allowedKeys) { + if (typeof Array.from(this.allowedKeys.values())[0] === 'number') { + key = parseInt(key as string) as T; + } + if (!this.allowedKeys.has(key)) { + throw new Error( + `Key ${key} (${typeof key}) is not allowed, only these are allowed: ${Array.from( + this.allowedKeys + ).join(', ')}` + ); + } + } + assert( + typeof value === 'number' && value >= this.valueSchema.min && value <= this.valueSchema.max, + `Invalid value (not within minmax ${this.valueSchema.min}-${this.valueSchema.max}) for ${key}: ${value}` + ); + if (!this.valueSchema.floats) { + assert(Number.isInteger(value), `Value for ${key} is not an integer: ${value}`); + } + this.validator?.(key, value, this.bank); + } + } + + entries() { + return Object.entries(this.bank) as [T, number][]; + } + + length() { + return Object.keys(this.bank).length; + } + + amount(key: T): number { + return this.bank[key] ?? 0; + } + + has(key: T): boolean { + return this.amount(key) >= 1; + } + + toString(): string { + const entries = Object.entries(this.bank); + if (entries.length === 0) return 'Bank is empty'; + return entries.map(([key, value]) => `${key}: ${value}`).join(', '); + } + + private addItem(key: T, quantity: number): this { + assert(quantity >= 0, 'Quantity must be non-negative.'); + const newValue = mathjs.add(this.amount(key), quantity); + if (newValue > this.valueSchema.max) { + throw new Error(`Value for ${key} exceeds the maximum of ${this.valueSchema.max}.`); + } + this.bank[key] = newValue; + this.validate(); + return this; + } + + private removeItem(key: T, quantity: number): this { + assert(quantity >= 0, 'Quantity must be non-negative.'); + const currentAmount = this.amount(key); + if (currentAmount < quantity) { + throw new Error(`Not enough ${key} to remove.`); + } + const newValue = mathjs.subtract(currentAmount, quantity); + this.bank[key] = newValue; + if (newValue === 0) { + delete this.bank[key]; + } + this.validate(); + return this; + } + + add(keyOrBank: T | GeneralBank, quantity: number = 1): this { + if (keyOrBank instanceof GeneralBank) { + for (const [key, qty] of keyOrBank.entries()) { + this.addItem(key, qty); + } + } else { + this.addItem(keyOrBank, quantity); + } + return this; + } + + remove(keyOrBank: T | GeneralBank, quantity: number = 1): this { + if (keyOrBank instanceof GeneralBank) { + for (const [key, qty] of Object.entries(keyOrBank.bank) as [T, number][]) { + this.removeItem(key as T, qty); + } + } else { + this.removeItem(keyOrBank, quantity); + } + return this; + } +} diff --git a/src/lib/structures/Requirements.ts b/src/lib/structures/Requirements.ts index 5e6862bf26e..74321e2f9d2 100644 --- a/src/lib/structures/Requirements.ts +++ b/src/lib/structures/Requirements.ts @@ -5,9 +5,9 @@ import { Bank } from 'oldschooljs'; import { getParsedStashUnits, ParsedUnit } from '../../mahoji/lib/abstracted_commands/stashUnitsCommand'; import { ClueTier } from '../clues/clueTiers'; import { BitField, BitFieldData, BOT_TYPE } from '../constants'; -import { diariesObject, DiaryTierName, userhasDiaryTier } from '../diaries'; +import { diaries, userhasDiaryIDTier } from '../diaries'; import { effectiveMonsters } from '../minions/data/killableMonsters'; -import { ClueBank } from '../minions/types'; +import { ClueBank, DiaryID, DiaryTierName } from '../minions/types'; import type { RobochimpUser } from '../roboChimp'; import { MinigameName } from '../settings/minigames'; import Agility from '../skilling/skills/agility'; @@ -53,7 +53,7 @@ type Requirement = { | { OR: Requirement[] } | { minigames: Partial> } | { bitfieldRequirement: BitField } - | { diaryRequirement: [keyof typeof diariesObject, DiaryTierName][] } + | { diaryRequirement: [DiaryID, DiaryTierName][] } | { clueCompletions: Partial> } ); @@ -123,7 +123,7 @@ export class Requirements { if ('diaryRequirement' in req) { requirementParts.push( `Achievement Diary Requirement: ${req.diaryRequirement - .map(i => `${i[1]} ${diariesObject[i[0]].name}`) + .map(i => `${i[1]} ${diaries.find(d => d.id === i[0])!.name}`) .join(', ')}` ); } @@ -278,12 +278,15 @@ export class Requirements { if ('diaryRequirement' in requirement) { const unmetDiaries = ( await Promise.all( - requirement.diaryRequirement.map(async ([diary, tier]) => ({ - has: await userhasDiaryTier(user, diariesObject[diary][tier]), - tierName: `${tier} ${diariesObject[diary].name}` - })) + requirement.diaryRequirement.map(async ([diary, tier]) => { + const res = await userhasDiaryIDTier(user, diary, tier); + return { + has: res.hasDiary, + tierName: `${tier} ${res.diaryGroup.name}` + }; + }) ) - ).filter(i => !i.has[0]); + ).filter(i => !i.has); if (unmetDiaries.length > 0) { results.push({ reason: `You need to finish these achievement diaries: ${unmetDiaries diff --git a/src/lib/types/minions.ts b/src/lib/types/minions.ts index 42b6b8512f0..a28ba90dcdb 100644 --- a/src/lib/types/minions.ts +++ b/src/lib/types/minions.ts @@ -449,6 +449,14 @@ export interface TheatreOfBloodTaskOptions extends ActivityTaskOptionsWithUsers solo?: boolean; } +export interface ColoTaskOptions extends ActivityTaskOptions { + type: 'Colosseum'; + fakeDuration: number; + diedAt?: number; + loot?: ItemBank; + maxGlory: number; +} + type UserID = string; type Points = number; type RoomIDsDiedAt = number[]; @@ -600,4 +608,5 @@ export type ActivityTaskData = | FightCavesActivityTaskOptions | ActivityTaskOptionsWithQuantity | MinigameActivityTaskOptionsWithNoChanges - | CutLeapingFishActivityTaskOptions; + | CutLeapingFishActivityTaskOptions + | ColoTaskOptions; diff --git a/src/lib/util/chatHeadImage.ts b/src/lib/util/chatHeadImage.ts index ac2428cf696..4792a84f10e 100644 --- a/src/lib/util/chatHeadImage.ts +++ b/src/lib/util/chatHeadImage.ts @@ -13,6 +13,7 @@ const ketKehChatHead = loadAndCacheLocalImage('./src/lib/resources/images/ketKeh const gertrudeChatHead = loadAndCacheLocalImage('./src/lib/resources/images/gertrude.png'); const antiSantaChatHead = loadAndCacheLocalImage('./src/lib/resources/images/antisanta.png'); const bunnyChatHead = loadAndCacheLocalImage('./src/lib/resources/images/bunny.png'); +const minimusHead = loadAndCacheLocalImage('./src/lib/resources/images/minimus.png'); export const chatHeads = { mejJal: mejJalChatHead, @@ -23,7 +24,8 @@ export const chatHeads = { ketKeh: ketKehChatHead, gertrude: gertrudeChatHead, antiSanta: antiSantaChatHead, - bunny: bunnyChatHead + bunny: bunnyChatHead, + minimus: minimusHead }; const names: Record = { @@ -35,7 +37,8 @@ const names: Record = { ketKeh: 'Tzhaar-Ket-Keh', gertrude: 'Gertrude', antiSanta: 'Anti-Santa', - bunny: 'Easter Bunny' + bunny: 'Easter Bunny', + minimus: 'Minimus' }; export async function newChatHeadImage({ content, head }: { content: string; head: keyof typeof chatHeads }) { diff --git a/src/lib/util/minionStatus.ts b/src/lib/util/minionStatus.ts index 4dc7f1bb8c7..75d92bb327a 100644 --- a/src/lib/util/minionStatus.ts +++ b/src/lib/util/minionStatus.ts @@ -3,12 +3,12 @@ import { increaseNumByPercent, reduceNumByPercent } from 'e'; import { SkillsEnum } from 'oldschooljs/dist/constants'; import { collectables } from '../../mahoji/lib/abstracted_commands/collectCommand'; -import { quests } from '../../mahoji/lib/abstracted_commands/questCommand'; import { shades, shadesLogs } from '../../mahoji/lib/abstracted_commands/shadesOfMortonCommand'; import { ClueTiers } from '../clues/clueTiers'; import { Emoji } from '../constants'; import killableMonsters from '../minions/data/killableMonsters'; import { Planks } from '../minions/data/planks'; +import { quests } from '../minions/data/quests'; import Agility from '../skilling/skills/agility'; import Constructables from '../skilling/skills/construction/constructables'; import Cooking from '../skilling/skills/cooking/cooking'; @@ -36,6 +36,7 @@ import { CastingActivityTaskOptions, ClueActivityTaskOptions, CollectingOptions, + ColoTaskOptions, ConstructionActivityTaskOptions, CookingActivityTaskOptions, CraftingActivityTaskOptions, @@ -681,6 +682,14 @@ export function minionStatus(user: MUser) { quests.find(i => i.id === data.questID)!.name }! The trip should take ${formatDuration(durationRemaining)}.`; } + case 'Colosseum': { + const data = currentTask as ColoTaskOptions; + const durationRemaining = data.finishDate - data.duration + data.fakeDuration - Date.now(); + + return `${name} is currently attempting the Colosseum, if they are successful, the trip should take ${formatDuration( + durationRemaining + )}.`; + } case 'HalloweenEvent': { return `${name} is doing the Halloween event! The trip should take ${formatDuration(durationRemaining)}.`; } diff --git a/src/lib/util/repeatStoredTrip.ts b/src/lib/util/repeatStoredTrip.ts index 70b24847d56..70074f213a5 100644 --- a/src/lib/util/repeatStoredTrip.ts +++ b/src/lib/util/repeatStoredTrip.ts @@ -622,6 +622,12 @@ export const tripHandlers = { drift_net_fishing: { minutes: Math.floor(data.duration / Time.Minute) } } }) + }, + [activity_type_enum.Colosseum]: { + commandName: 'k', + args: () => ({ + name: 'colosseum' + }) } } as const; diff --git a/src/lib/util/updateBankSetting.ts b/src/lib/util/updateBankSetting.ts index 201171fd2cf..c95482c4ce7 100644 --- a/src/lib/util/updateBankSetting.ts +++ b/src/lib/util/updateBankSetting.ts @@ -59,7 +59,9 @@ type ClientBankKey = | 'nex_loot' | 'nmz_cost' | 'toa_cost' - | 'toa_loot'; + | 'toa_loot' + | 'colo_cost' + | 'colo_loot'; export async function updateBankSetting(key: ClientBankKey, bankToAdd: Bank) { if (bankToAdd === undefined || bankToAdd === null) throw new Error(`Gave null bank for ${key}`); diff --git a/src/lib/util/userQueues.ts b/src/lib/util/userQueues.ts index 89edd772395..d3b818a04f5 100644 --- a/src/lib/util/userQueues.ts +++ b/src/lib/util/userQueues.ts @@ -18,6 +18,7 @@ export async function userQueueFn(userID: string, fn: () => Promise) { try { return await fn(); } catch (e) { + console.error(e); error.message = (e as Error).message; throw error; } diff --git a/src/lib/workers/casket.worker.ts b/src/lib/workers/casket.worker.ts index fd3690b7f05..97c23d9c182 100644 --- a/src/lib/workers/casket.worker.ts +++ b/src/lib/workers/casket.worker.ts @@ -6,6 +6,10 @@ import { Bank, Misc } from 'oldschooljs'; import { ClueTiers } from '../clues/clueTiers'; import type { CasketWorkerArgs } from '.'; +if (global.prisma) { + throw new Error('Prisma is loaded in the casket worker!'); +} + export default async ({ clueTierID, quantity }: CasketWorkerArgs): Promise<[Bank, string]> => { const clueTier = ClueTiers.find(tier => tier.id === clueTierID)!; let loot = clueTier.table.open(quantity); diff --git a/src/lib/workers/finish.worker.ts b/src/lib/workers/finish.worker.ts index 315e2b3d412..f656929e7d2 100644 --- a/src/lib/workers/finish.worker.ts +++ b/src/lib/workers/finish.worker.ts @@ -6,6 +6,10 @@ import { Bank } from 'oldschooljs'; import getOSItem from '../util/getOSItem'; import type { FinishWorkerArgs, FinishWorkerReturn } from '.'; +if (global.prisma) { + throw new Error('Prisma is loaded in the finish worker!'); +} + export default async ({ name, tertiaries }: FinishWorkerArgs): FinishWorkerReturn => { const { finishables } = await import('../finishables'); const val = finishables.find(i => i.name === name)!; diff --git a/src/lib/workers/kill.worker.ts b/src/lib/workers/kill.worker.ts index b4cb152ba5a..02d6a01f659 100644 --- a/src/lib/workers/kill.worker.ts +++ b/src/lib/workers/kill.worker.ts @@ -10,6 +10,10 @@ import { calcDropRatesFromBank } from '../util/calcDropRatesFromBank'; import resolveItems from '../util/resolveItems'; import type { KillWorkerArgs, KillWorkerReturn } from '.'; +if (global.prisma) { + throw new Error('Prisma is loaded in the kill worker!'); +} + export default async ({ quantity, bossName, diff --git a/src/mahoji/commands/activities.ts b/src/mahoji/commands/activities.ts index 2d325c0aa95..2d0bb2c30c6 100644 --- a/src/mahoji/commands/activities.ts +++ b/src/mahoji/commands/activities.ts @@ -7,6 +7,7 @@ import { } from '../../lib/constants'; import { Planks } from '../../lib/minions/data/planks'; import Potions from '../../lib/minions/data/potions'; +import { quests } from '../../lib/minions/data/quests'; import birdhouses from '../../lib/skilling/skills/hunter/birdHouseTrapping'; import { Castables } from '../../lib/skilling/skills/magic/castables'; import { Enchantables } from '../../lib/skilling/skills/magic/enchantables'; @@ -30,7 +31,7 @@ import { fightCavesCommand } from '../lib/abstracted_commands/fightCavesCommand' import { infernoStartCommand, infernoStatsCommand } from '../lib/abstracted_commands/infernoCommand'; import { otherActivities, otherActivitiesCommand } from '../lib/abstracted_commands/otherActivitiesCommand'; import puroOptions, { puroPuroStartCommand } from '../lib/abstracted_commands/puroPuroCommand'; -import { questCommand, quests } from '../lib/abstracted_commands/questCommand'; +import { questCommand } from '../lib/abstracted_commands/questCommand'; import { sawmillCommand } from '../lib/abstracted_commands/sawmillCommand'; import { scatterCommand } from '../lib/abstracted_commands/scatterCommand'; import { underwaterAgilityThievingCommand } from '../lib/abstracted_commands/underwaterCommand'; diff --git a/src/mahoji/commands/buy.ts b/src/mahoji/commands/buy.ts index b65d2c6a8ce..c92e1971656 100644 --- a/src/mahoji/commands/buy.ts +++ b/src/mahoji/commands/buy.ts @@ -4,6 +4,7 @@ import { Bank } from 'oldschooljs'; import { ItemBank } from 'oldschooljs/dist/meta/types'; import Buyables from '../../lib/data/buyables/buyables'; +import { quests } from '../../lib/minions/data/quests'; import { getMinigameScore, Minigames } from '../../lib/settings/minigames'; import { prisma } from '../../lib/settings/prisma'; import { MUserStats } from '../../lib/structures/MUserStats'; @@ -13,7 +14,6 @@ import { deferInteraction } from '../../lib/util/interactionReply'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; import { buyFossilIslandNotes } from '../lib/abstracted_commands/buyFossilIslandNotes'; import { buyKitten } from '../lib/abstracted_commands/buyKitten'; -import { quests } from '../lib/abstracted_commands/questCommand'; import { OSBMahojiCommand } from '../lib/util'; import { mahojiParseNumber, multipleUserStatsBankUpdate } from '../mahojiSettings'; diff --git a/src/mahoji/commands/gamble.ts b/src/mahoji/commands/gamble.ts index a09d352d3b7..2dec4e4bca3 100644 --- a/src/mahoji/commands/gamble.ts +++ b/src/mahoji/commands/gamble.ts @@ -26,17 +26,18 @@ export const gambleCommand: OSBMahojiCommand = { */ { type: ApplicationCommandOptionType.Subcommand, - name: 'cape', - description: 'Allows you to gamble fire/infernal capes for a chance at the pets.', + name: 'item', + description: 'Allows you to gamble fire/infernal capes/quivers for a chance at the pets.', options: [ { type: ApplicationCommandOptionType.String, - name: 'type', - description: 'The cape you wish to gamble.', + name: 'item', + description: 'The item you wish to gamble.', required: false, choices: [ { name: 'fire', value: 'fire' }, - { name: 'infernal', value: 'infernal' } + { name: 'infernal', value: 'infernal' }, + { name: 'quiver', value: 'quiver' } ] }, { @@ -175,7 +176,7 @@ export const gambleCommand: OSBMahojiCommand = { guildID, userID }: CommandRunOptions<{ - cape?: { type?: string; autoconfirm?: boolean }; + item?: { item?: string; autoconfirm?: boolean }; dice?: { amount?: string }; duel?: { user: MahojiUserOption; amount?: string }; lucky_pick?: { amount: string }; @@ -185,9 +186,9 @@ export const gambleCommand: OSBMahojiCommand = { }>) => { const user = await mUserFetch(userID); - if (options.cape) { - if (options.cape.type) { - return capeGambleCommand(user, options.cape.type, interaction, options.cape.autoconfirm); + if (options.item) { + if (options.item.item) { + return capeGambleCommand(user, options.item.item, interaction, options.item.autoconfirm); } return capeGambleStatsCommand(user); } diff --git a/src/mahoji/commands/k.ts b/src/mahoji/commands/k.ts index ba1a2254581..163be1a7f39 100644 --- a/src/mahoji/commands/k.ts +++ b/src/mahoji/commands/k.ts @@ -35,6 +35,11 @@ export const autocompleteMonsters = [ aliases: ['wt', 'wintertodt', 'todt'], id: -1, emoji: '<:Phoenix:324127378223792129>' + }, + { + name: 'Colosseum', + aliases: ['colo', 'colosseum'], + id: -1 } ]; diff --git a/src/mahoji/commands/simulate.ts b/src/mahoji/commands/simulate.ts index e7c5aace037..752f0f913be 100644 --- a/src/mahoji/commands/simulate.ts +++ b/src/mahoji/commands/simulate.ts @@ -5,8 +5,11 @@ import { Bank } from 'oldschooljs'; import { ChambersOfXeric } from 'oldschooljs/dist/simulation/misc'; import { toKMB } from 'oldschooljs/dist/util'; +import { ColosseumWaveBank, startColosseumRun } from '../../lib/colosseum'; import { PerkTier } from '../../lib/constants'; import pets from '../../lib/data/pets'; +import { assert, averageBank, formatDuration } from '../../lib/util'; +import { deferInteraction } from '../../lib/util/interactionReply'; import { makeBankImage } from '../../lib/util/makeBankImage'; import { OSBMahojiCommand } from '../lib/util'; @@ -28,6 +31,65 @@ export function determineCoxLimit(user: MUser) { return 10; } +function simulateColosseumRuns(samples = 100) { + const totalSimulations = samples; + let totalAttempts = 0; + let totalDeaths = 0; + const totalLoot = new Bank(); + const finishAttemptAmounts = []; + let totalDuration = 0; + + for (let i = 0; i < totalSimulations; i++) { + let attempts = 0; + let deaths = 0; + let done = false; + const kcBank = new ColosseumWaveBank(); + const runLoot = new Bank(); + + while (!done) { + attempts++; + const result = startColosseumRun({ + kcBank, + hasScythe: true, + hasTBow: true, + hasVenBow: true, + hasBF: false, + hasClaws: true, + hasSGS: true, + hasTorture: true + }); + totalDuration += result.realDuration; + kcBank.add(result.addedWaveKCBank); + if (result.diedAt === null) { + if (result.loot) runLoot.add(result.loot); + done = true; + } else { + deaths++; + } + } + assert(kcBank.amount(12) > 0); + finishAttemptAmounts.push(attempts); + totalAttempts += attempts; + totalDeaths += deaths; + totalLoot.add(runLoot); + } + + const averageAttempts = totalAttempts / totalSimulations; + const averageDeaths = totalDeaths / totalSimulations; + + finishAttemptAmounts.sort((a, b) => a - b); + + const result = `Results from the simulation of ${totalSimulations}x people completing the Colosseum: +**Average duration to beat wave 12 for first time:** ${formatDuration(totalDuration / totalSimulations)} +**Average deaths before beating wave 12:** ${averageDeaths} +**Average loot:** ${averageBank(totalLoot, totalSimulations)} +**Fastest completion trips:** ${finishAttemptAmounts[0]} +**Mean completion trips:** ${finishAttemptAmounts[Math.floor(finishAttemptAmounts.length / 2)]} +**Average trips to beat wave 12:** ${averageAttempts}. +**Longest completion trips:** ${finishAttemptAmounts[finishAttemptAmounts.length - 1]}`; + return result; +} + async function coxCommand(user: MUser, quantity: number, cm = false, points = 25_000, teamSize = 4): CommandResponse { const limit = determineCoxLimit(user); if (quantity > limit) { @@ -125,9 +187,15 @@ export const simulateCommand: OSBMahojiCommand = { required: true } ] + }, + { + type: ApplicationCommandOptionType.Subcommand, + name: 'colosseum', + description: 'Simulate colosseum.' } ], run: async ({ + interaction, options, userID }: CommandRunOptions<{ @@ -140,8 +208,13 @@ export const simulateCommand: OSBMahojiCommand = { petroll?: { quantity: number; }; + colosseum?: {}; }>) => { + await deferInteraction(interaction); const user = await mUserFetch(userID.toString()); + if (options.colosseum) { + return simulateColosseumRuns(); + } if (options.cox) { return coxCommand( user, diff --git a/src/mahoji/commands/testpotato.ts b/src/mahoji/commands/testpotato.ts index 1be86fafb3e..c5adda7e57e 100644 --- a/src/mahoji/commands/testpotato.ts +++ b/src/mahoji/commands/testpotato.ts @@ -15,6 +15,7 @@ import { Eatables } from '../../lib/data/eatables'; import { TOBMaxMageGear, TOBMaxMeleeGear, TOBMaxRangeGear } from '../../lib/data/tob'; import killableMonsters, { effectiveMonsters } from '../../lib/minions/data/killableMonsters'; import potions from '../../lib/minions/data/potions'; +import { MAX_QP } from '../../lib/minions/data/quests'; import { mahojiUserSettingsUpdate } from '../../lib/MUser'; import { allOpenables } from '../../lib/openables'; import { tiers } from '../../lib/patreon'; @@ -27,6 +28,7 @@ import { slayerMasterChoices } from '../../lib/slayer/constants'; import { slayerMasters } from '../../lib/slayer/slayerMasters'; import { getUsersCurrentSlayerInfo } from '../../lib/slayer/slayerUtil'; import { allSlayerMonsters } from '../../lib/slayer/tasks'; +import { Gear } from '../../lib/structures/Gear'; import { stringMatches } from '../../lib/util'; import { calcDropRatesFromBankWithoutUniques } from '../../lib/util/calcDropRatesFromBank'; import { @@ -38,9 +40,9 @@ import { import getOSItem from '../../lib/util/getOSItem'; import { logError } from '../../lib/util/logError'; import { parseStringBank } from '../../lib/util/parseStringBank'; +import resolveItems from '../../lib/util/resolveItems'; import { userEventToStr } from '../../lib/util/userEvents'; import { getPOH } from '../lib/abstracted_commands/pohCommand'; -import { MAX_QP } from '../lib/abstracted_commands/questCommand'; import { allUsableItems } from '../lib/abstracted_commands/useCommand'; import { BingoManager } from '../lib/bingo/BingoManager'; import { OSBMahojiCommand } from '../lib/util'; @@ -80,12 +82,47 @@ async function givePatronLevel(user: MUser, tier: number) { return `Gave you tier ${tierToGive[1] - 1} patron.`; } +const coloMelee = new Gear(); +for (const gear of resolveItems([ + 'Torva full helm', + 'Infernal cape', + 'Amulet of blood fury', + 'Torva platebody', + 'Torva platelegs', + 'Primordial boots', + 'Ultor ring', + 'Scythe of vitur' +])) { + coloMelee.equip(getOSItem(gear)); +} + +const coloRange = new Gear(); +for (const gear of resolveItems([ + "Dizana's quiver", + 'Masori mask (f)', + 'Necklace of anguish', + 'Masori body (f)', + 'Masori chaps (f)', + 'Pegasian boots', + 'Venator ring', + 'Dragon arrow', + 'Twisted bow' +])) { + coloRange.equip(getOSItem(gear)); +} + const gearPresets = [ { name: 'ToB', melee: TOBMaxMeleeGear, mage: TOBMaxMageGear, range: TOBMaxRangeGear + }, + { + name: 'Colosseum', + melee: coloMelee, + range: coloRange, + mage: coloRange } ]; diff --git a/src/mahoji/lib/abstracted_commands/achievementDiaryCommand.ts b/src/mahoji/lib/abstracted_commands/achievementDiaryCommand.ts index 7e7e38a1fa1..7337820b5e4 100644 --- a/src/mahoji/lib/abstracted_commands/achievementDiaryCommand.ts +++ b/src/mahoji/lib/abstracted_commands/achievementDiaryCommand.ts @@ -3,7 +3,8 @@ import { strikethrough } from 'discord.js'; import { calcWhatPercent } from 'e'; import { Bank, Monsters } from 'oldschooljs'; -import { diaries, DiaryTier, userhasDiaryTier, userhasDiaryTierSync } from '../../../lib/diaries'; +import { diaries, userhasDiaryTier, userhasDiaryTierSync } from '../../../lib/diaries'; +import { DiaryTier } from '../../../lib/minions/types'; import { Minigames, MinigameScore } from '../../../lib/settings/minigames'; import { MUserStats } from '../../../lib/structures/MUserStats'; import { formatSkillRequirements, itemNameFromID, stringMatches } from '../../../lib/util'; diff --git a/src/mahoji/lib/abstracted_commands/capegamble.ts b/src/mahoji/lib/abstracted_commands/capegamble.ts index 5ba803aea5d..62cada7739f 100644 --- a/src/mahoji/lib/abstracted_commands/capegamble.ts +++ b/src/mahoji/lib/abstracted_commands/capegamble.ts @@ -18,15 +18,62 @@ export async function capeGambleStatsCommand(user: MUser) { **Infernal Cape's Gambled:** ${stats.infernal_cape_sacrifices}`; } +const itemGambles = [ + { + type: 'fire', + item: getOSItem('Fire cape'), + trackerKey: 'firecapes_sacrificed', + chatHead: 'mejJal', + chance: 200, + success: { + loot: getOSItem('Tzrek-Jad'), + message: 'You lucky. Better train him good else TzTok-Jad find you, JalYt.' + }, + failMessage: (newSacrificedCount: number) => + `You not lucky. Maybe next time, JalYt. This is the ${formatOrdinal( + newSacrificedCount + )} time you gamble cape.` + }, + { + type: 'infernal', + item: getOSItem('Infernal cape'), + trackerKey: 'infernal_cape_sacrifices', + chatHead: 'ketKeh', + chance: 100, + success: { + loot: getOSItem('Jal-nib-rek'), + message: 'Luck be a TzHaar tonight. Jal-Nib-Rek is yours.' + }, + failMessage: (newSacrificedCount: number) => + `No Jal-Nib-Rek for you. This is the ${formatOrdinal(newSacrificedCount)} time you gamble cape.` + }, + { + type: 'quiver', + item: getOSItem("Dizana's quiver (uncharged)"), + trackerKey: 'quivers_sacrificed', + chatHead: 'minimus', + chance: 200, + success: { + loot: getOSItem('Smol heredit'), + message: 'He seems to like you. Smol heredit is yours.' + }, + failMessage: (newSacrificedCount: number) => + `He doesn't want to go with you. Sorry. This is the ${formatOrdinal( + newSacrificedCount + )} time you gambled a quiver.` + } +] as const; + export async function capeGambleCommand( user: MUser, type: string, interaction: ChatInputCommandInteraction, autoconfirm: boolean = false ) { - const item = getOSItem(type === 'fire' ? 'Fire cape' : 'Infernal cape'); - const key: 'infernal_cape_sacrifices' | 'firecapes_sacrificed' = - type === 'fire' ? 'firecapes_sacrificed' : 'infernal_cape_sacrifices'; + const src = itemGambles.find(i => i.type === type); + if (!src) return 'Invalid type. You can only gamble fire capes, infernal capes, or quivers.'; + const { item } = src; + const key = src.trackerKey; const capesOwned = user.bank.amount(item.id); if (capesOwned < 1) return `You have no ${item.name}'s to gamble!`; @@ -48,13 +95,14 @@ export async function capeGambleCommand( }, { infernal_cape_sacrifices: true, - firecapes_sacrificed: true + firecapes_sacrificed: true, + quivers_sacrificed: true } ); const newSacrificedCount = newStats[key]; - const chance = type === 'fire' ? 200 : 100; - const pet = getOSItem(type === 'fire' ? 'Tzrek-Jad' : 'Jal-nib-rek'); + const { chance } = src; + const pet = src.success.loot; const gotPet = roll(chance); const loot = gotPet ? new Bank().add(pet.id) : undefined; @@ -72,11 +120,8 @@ export async function capeGambleCommand( { name: 'image.jpg', attachment: await newChatHeadImage({ - content: - type === 'fire' - ? 'You lucky. Better train him good else TzTok-Jad find you, JalYt.' - : 'Luck be a TzHaar tonight. Jal-Nib-Rek is yours.', - head: type === 'fire' ? 'mejJal' : 'ketKeh' + content: src.success.message, + head: src.chatHead }) } ] @@ -88,15 +133,8 @@ export async function capeGambleCommand( { name: 'image.jpg', attachment: await newChatHeadImage({ - content: - type === 'fire' - ? `You not lucky. Maybe next time, JalYt. This is the ${formatOrdinal( - newSacrificedCount - )} time you gamble cape.` - : `No Jal-Nib-Rek for you. This is the ${formatOrdinal( - newSacrificedCount - )} time you gamble cape.`, - head: type === 'fire' ? 'mejJal' : 'ketKeh' + content: src.failMessage(newSacrificedCount), + head: src.chatHead }) } ] diff --git a/src/mahoji/lib/abstracted_commands/minionKill.ts b/src/mahoji/lib/abstracted_commands/minionKill.ts index bd7b34d5fd2..9b216931e3c 100644 --- a/src/mahoji/lib/abstracted_commands/minionKill.ts +++ b/src/mahoji/lib/abstracted_commands/minionKill.ts @@ -15,11 +15,12 @@ import { Bank, Monsters } from 'oldschooljs'; import { MonsterAttribute } from 'oldschooljs/dist/meta/monsterData'; import { itemID } from 'oldschooljs/dist/util'; +import { colosseumCommand } from '../../../lib/colosseum'; import { BitField, PeakTier, PvMMethod } from '../../../lib/constants'; import { Eatables } from '../../../lib/data/eatables'; import { getSimilarItems } from '../../../lib/data/similarItems'; import { checkUserCanUseDegradeableItem, degradeablePvmBoostItems, degradeItem } from '../../../lib/degradeableItems'; -import { Diary, DiaryTier, userhasDiaryTier } from '../../../lib/diaries'; +import { userhasDiaryIDTier } from '../../../lib/diaries'; import { GearSetupType } from '../../../lib/gear/types'; import { trackLoot } from '../../../lib/lootTrack'; import { @@ -36,6 +37,7 @@ import { SlayerActivityConstants } from '../../../lib/minions/data/combatConstants'; import { revenantMonsters } from '../../../lib/minions/data/killableMonsters/revs'; +import { quests } from '../../../lib/minions/data/quests'; import { AttackStyles, calculateMonsterFood, @@ -81,7 +83,6 @@ import { hasMonsterRequirements, resolveAvailableItemBoosts } from '../../mahoji import { nexCommand } from './nexCommand'; import { nightmareCommand } from './nightmareCommand'; import { getPOH } from './pohCommand'; -import { quests } from './questCommand'; import { temporossCommand } from './temporossCommand'; import { wintertodtCommand } from './wintertodtCommand'; import { zalcanoCommand } from './zalcanoCommand'; @@ -156,6 +157,7 @@ export async function minionKillCommand( if (!name) return invalidMonsterMsg; + if (stringMatches(name, 'colosseum')) return colosseumCommand(user, channelID); if (stringMatches(name, 'nex')) return nexCommand(interaction, user, channelID, solo); if (stringMatches(name, 'zalcano')) return zalcanoCommand(user, channelID); if (stringMatches(name, 'tempoross')) return temporossCommand(user, channelID, quantity); @@ -229,10 +231,10 @@ export async function minionKillCommand( if (!hasReqs) return reason ?? "You don't have the requirements to fight this monster"; if (monster.diaryRequirement) { - const [diary, tier]: [Diary, DiaryTier] = monster.diaryRequirement; - const [hasDiary] = await userhasDiaryTier(user, tier); + const [diaryID, tier] = monster.diaryRequirement; + const { hasDiary, diaryGroup } = await userhasDiaryIDTier(user, diaryID, tier); if (!hasDiary) { - return `${user.minionName} is missing the ${diary.name} ${tier.name} diary to kill ${monster.name}.`; + return `${user.minionName} is missing the ${diaryGroup.name} ${tier} diary to kill ${monster.name}.`; } } diff --git a/src/mahoji/lib/abstracted_commands/nightmareZoneCommand.ts b/src/mahoji/lib/abstracted_commands/nightmareZoneCommand.ts index 4b472712a3c..f62c6e89487 100644 --- a/src/mahoji/lib/abstracted_commands/nightmareZoneCommand.ts +++ b/src/mahoji/lib/abstracted_commands/nightmareZoneCommand.ts @@ -4,6 +4,7 @@ import { Bank } from 'oldschooljs'; import { NMZStrategy } from '../../../lib/constants'; import { trackLoot } from '../../../lib/lootTrack'; +import { MAX_QP } from '../../../lib/minions/data/quests'; import { resolveAttackStyles } from '../../../lib/minions/functions'; import { getMinigameEntity } from '../../../lib/settings/minigames'; import { SkillsEnum } from '../../../lib/skilling/types'; @@ -15,7 +16,6 @@ import getOSItem from '../../../lib/util/getOSItem'; import { handleMahojiConfirmation } from '../../../lib/util/handleMahojiConfirmation'; import { updateBankSetting } from '../../../lib/util/updateBankSetting'; import { NightmareZoneActivityTaskOptions } from './../../../lib/types/minions'; -import { MAX_QP } from './questCommand'; const itemBoosts = [ // Special weapons diff --git a/src/mahoji/lib/abstracted_commands/questCommand.ts b/src/mahoji/lib/abstracted_commands/questCommand.ts index b1f5a1b73a8..5a0db6daf1a 100644 --- a/src/mahoji/lib/abstracted_commands/questCommand.ts +++ b/src/mahoji/lib/abstracted_commands/questCommand.ts @@ -1,227 +1,12 @@ import { sumArr, Time } from 'e'; -import { Bank } from 'oldschooljs'; -import { Skills } from '../../../lib/types'; +import { MAX_GLOBAL_QP, MAX_QP, quests } from '../../../lib/minions/data/quests'; import { ActivityTaskOptionsWithNoChanges, SpecificQuestOptions } from '../../../lib/types/minions'; import { formatDuration, hasSkillReqs } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { minionIsBusy } from '../../../lib/util/minionIsBusy'; import { userHasGracefulEquipped } from '../../mahojiSettings'; -export const MAX_GLOBAL_QP = 293; - -interface Quest { - id: QuestID; - qp: number; - name: string; - skillReqs?: Skills; - ironmanSkillReqs?: Skills; - qpReq?: number; - rewards?: Bank; - skillsRewards?: { - [skill: string]: number; - }; - combatLevelReq?: number; - prerequisitesQuests?: QuestID[]; - calcTime: (user: MUser) => number; -} - -export enum QuestID { - DesertTreasureII = 1, - ThePathOfGlouphrie = 2, - ChildrenOfTheSun = 3, - DefenderOfVarrock = 4, - TheRibbitingTaleOfALillyPadLabourDispute = 5, - PerilousMoons = 6, - AtFirstLight = 7, - TwilightsPromise = 8 -} - -export const quests: Quest[] = [ - { - id: QuestID.DesertTreasureII, - qp: 5, - name: 'Desert Treasure II - The Fallen Empire', - skillReqs: { - firemaking: 75, - magic: 75, - thieving: 70, - herblore: 62, - runecraft: 60, - construction: 60 - }, - combatLevelReq: 110, - qpReq: 150, - rewards: new Bank().add(28_409, 3).add('Ring of shadows').freeze(), - calcTime: (user: MUser) => { - let duration = Time.Hour * 3; - if (user.combatLevel < 100) { - duration += Time.Minute * 30; - } - if (user.combatLevel < 90) { - duration += Time.Minute * 40; - } - const percentOfBossCL = user.percentOfBossCLFinished(); - if (percentOfBossCL < 10) { - duration += Time.Minute * 20; - } else if (percentOfBossCL < 30) { - duration += Time.Minute * 10; - } else if (percentOfBossCL > 80) { - duration -= Time.Minute * 60; - } else if (percentOfBossCL > 50) { - duration -= Time.Minute * 30; - } - return duration; - } - }, - { - id: QuestID.ThePathOfGlouphrie, - qp: 2, - name: 'The Path of Glouphrie', - skillReqs: { - strength: 60, - slayer: 56, - thieving: 56, - ranged: 47, - agility: 45 - }, - ironmanSkillReqs: { - fletching: 59, - smithing: 59 - }, - combatLevelReq: 50, - qpReq: 10, - rewards: new Bank().add(28_587).add(28_587).add(28_588).add(28_589).add(28_590).freeze(), - calcTime: (user: MUser) => { - let duration = Time.Minute * 10; - if (user.combatLevel < 90) { - duration += Time.Minute * 5; - } - return duration; - } - }, - { - id: QuestID.ChildrenOfTheSun, - qp: 1, - name: 'Children of the Sun', - calcTime: () => { - const duration = Time.Minute * 3; - return duration; - } - }, - { - id: QuestID.DefenderOfVarrock, - qp: 2, - name: 'Defender of Varrock', - skillReqs: { - smithing: 55, - hunter: 52 - }, - combatLevelReq: 65, - qpReq: 20, - // Awaiting item update for the lamp to be added - // rewards: new Bank().add(28_820).freeze(), - skillsRewards: { - smithing: 15_000, - hunter: 15_000 - }, - calcTime: (user: MUser) => { - let duration = Time.Minute * 12; - if (user.combatLevel < 100) { - duration += Time.Minute * 8; - } - return duration; - } - }, - { - id: QuestID.TheRibbitingTaleOfALillyPadLabourDispute, - qp: 1, - name: 'The Ribbiting Tale of a Lily Pad Labour Dispute', - skillReqs: { - woodcutting: 15 - }, - prerequisitesQuests: [QuestID.ChildrenOfTheSun], - skillsRewards: { - woodcutting: 2000 - }, - calcTime: () => { - const duration = Time.Minute * 3; - return duration; - } - }, - { - id: QuestID.PerilousMoons, - qp: 2, - name: 'Perilous Moons', - skillReqs: { - slayer: 48, - hunter: 20, - fishing: 20, - runecraft: 20, - construction: 10 - }, - combatLevelReq: 75, - prerequisitesQuests: [QuestID.ChildrenOfTheSun, QuestID.TwilightsPromise], - skillsRewards: { - slayer: 40_000, - runecraft: 5000, - hunter: 5000, - fishing: 5000 - }, - calcTime: (user: MUser) => { - let duration = Time.Minute * 20; - if (user.combatLevel < 120) { - duration += Time.Minute * 5; - } - if (user.combatLevel < 100) { - duration += Time.Minute * 10; - } - return duration; - } - }, - { - id: QuestID.AtFirstLight, - qp: 1, - name: 'At First Light', - skillReqs: { - hunter: 46, - herblore: 30, - construction: 27 - }, - combatLevelReq: 75, - qpReq: 2, - prerequisitesQuests: [QuestID.ChildrenOfTheSun], - skillsRewards: { - hunter: 4500, - construction: 800, - herblore: 500 - }, - calcTime: () => { - let duration = Time.Minute * 6; - return duration; - } - }, - { - id: QuestID.TwilightsPromise, - qp: 1, - name: "Twilight's Promise", - skillsRewards: { - thieving: 3000 - }, - combatLevelReq: 40, - prerequisitesQuests: [QuestID.ChildrenOfTheSun], - calcTime: (user: MUser) => { - let duration = Time.Minute * 9; - if (user.combatLevel < 75) { - duration += Time.Minute * 5; - } - return duration; - } - } -]; - -export const MAX_QP = MAX_GLOBAL_QP + sumArr(quests.map(i => i.qp)); - export async function questCommand(user: MUser, channelID: string, name?: string) { if (!user.user.minion_hasBought) { return 'You need a minion to do a questing trip'; diff --git a/src/scripts/integration-tests.ts b/src/scripts/integration-tests.ts index b36ed6fec1c..8487aa9f56c 100644 --- a/src/scripts/integration-tests.ts +++ b/src/scripts/integration-tests.ts @@ -20,7 +20,7 @@ async function main() { let runs = 1; for (let i = 0; i < runs; i++) { console.log(`Starting run ${i + 1}/${runs}`); - execSync('vitest run --config vitest.integration.config.mts', { + execSync('vitest run --config vitest.integration.config.mts sacrifice', { stdio: 'inherit', encoding: 'utf-8' }); diff --git a/src/tasks/minions/colosseumActivity.ts b/src/tasks/minions/colosseumActivity.ts new file mode 100644 index 00000000000..30fb15b9f35 --- /dev/null +++ b/src/tasks/minions/colosseumActivity.ts @@ -0,0 +1,97 @@ +import { randArrItem } from 'e'; +import { Bank } from 'oldschooljs'; +import { ItemBank } from 'oldschooljs/dist/meta/types'; + +import { ColosseumWaveBank, colosseumWaves } from '../../lib/colosseum'; +import { trackLoot } from '../../lib/lootTrack'; +import { incrementMinigameScore } from '../../lib/settings/minigames'; +import { ColoTaskOptions } from '../../lib/types/minions'; +import { handleTripFinish } from '../../lib/util/handleTripFinish'; +import { makeBankImage } from '../../lib/util/makeBankImage'; +import resolveItems from '../../lib/util/resolveItems'; +import { updateBankSetting } from '../../lib/util/updateBankSetting'; +import { userStatsBankUpdate, userStatsUpdate } from '../../mahoji/mahojiSettings'; + +const sunfireItems = resolveItems(['Sunfire fanatic helm', 'Sunfire fanatic cuirass', 'Sunfire fanatic chausses']); + +export const colosseumTask: MinionTask = { + type: 'Colosseum', + async run(data: ColoTaskOptions) { + const { channelID, userID, loot: possibleLoot, diedAt, maxGlory } = data; + const user = await mUserFetch(userID); + + const newKCs = new ColosseumWaveBank(); + for (let i = 0; i < (diedAt ? diedAt - 1 : 12); i++) { + newKCs.add(i + 1); + } + const stats = await user.fetchStats({ colo_kc_bank: true, colo_max_glory: true }); + for (const [key, value] of Object.entries(stats.colo_kc_bank as ItemBank)) newKCs.add(parseInt(key), value); + await userStatsUpdate(user.id, { colo_kc_bank: newKCs._bank }); + const newKCsStr = `${newKCs + .entries() + .map(([kc, amount]) => `Wave ${kc}: ${amount} KC`) + .join(', ')}`; + + const newWaveKcStr = !diedAt || diedAt > 1 ? `New wave KCs: ${newKCsStr}.` : 'No new KCs.'; + if (diedAt) { + const wave = colosseumWaves.find(i => i.waveNumber === diedAt)!; + return handleTripFinish( + user, + channelID, + `${user}, you died on wave ${diedAt} to ${randArrItem([ + ...(wave?.reinforcements ?? []), + ...wave.enemies + ])}, and received no loot. ${newWaveKcStr}`, + undefined, + data, + null + ); + } + + await incrementMinigameScore(user.id, 'colosseum'); + + const loot = new Bank().add(possibleLoot); + + const missingItems = sunfireItems.filter(id => !user.cl.has(id)); + const itemsTheyHave = sunfireItems.filter(id => user.cl.has(id)); + if (missingItems.length > 0) { + for (const item of sunfireItems) { + if (loot.has(item) && itemsTheyHave.includes(item)) { + loot.remove(item); + loot.add(randArrItem(missingItems)); + } + } + } + + const { previousCL } = await user.addItemsToBank({ items: loot, collectionLog: true }); + + await updateBankSetting('colo_loot', loot); + await userStatsBankUpdate(user.id, 'colo_loot', loot); + await trackLoot({ + totalLoot: loot, + id: 'colo', + type: 'Minigame', + changeType: 'loot', + duration: data.duration, + kc: 1, + users: [ + { + id: user.id, + loot, + duration: data.duration + } + ] + }); + + let str = `${user}, you completed the Colosseum! You received: ${loot}. ${newWaveKcStr}`; + + if (!stats.colo_max_glory || maxGlory > stats.colo_max_glory) { + await userStatsUpdate(user.id, { colo_max_glory: maxGlory }); + str += ` Your new max glory is ${maxGlory}!`; + } + + const image = await makeBankImage({ bank: loot, title: 'Colosseum Loot', user, previousCL }); + + return handleTripFinish(user, channelID, str, image.file.attachment, data, loot); + } +}; diff --git a/src/tasks/minions/minigames/wintertodtActivity.ts b/src/tasks/minions/minigames/wintertodtActivity.ts index 2d84e3fc75e..d6e9768c2db 100644 --- a/src/tasks/minions/minigames/wintertodtActivity.ts +++ b/src/tasks/minions/minigames/wintertodtActivity.ts @@ -1,8 +1,7 @@ -import { SimpleTable } from '@oldschoolgg/toolkit'; import { randInt } from 'e'; import { Bank } from 'oldschooljs'; -import { Emoji, Events } from '../../../lib/constants'; +import { Emoji, Events, winterTodtPointsTable } from '../../../lib/constants'; import { trackLoot } from '../../../lib/lootTrack'; import { getMinigameScore, incrementMinigameScore } from '../../../lib/settings/settings'; import { WintertodtCrate } from '../../../lib/simulation/wintertodt'; @@ -13,29 +12,6 @@ import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { makeBankImage } from '../../../lib/util/makeBankImage'; import { updateBankSetting } from '../../../lib/util/updateBankSetting'; -export const PointsTable = new SimpleTable() - .add(420) - .add(470) - .add(500) - .add(505) - .add(510) - .add(520) - .add(550) - .add(560) - .add(590) - .add(600) - .add(620) - .add(650) - .add(660) - .add(670) - .add(680) - .add(700) - .add(720) - .add(740) - .add(750) - .add(780) - .add(850); - export const wintertodtTask: MinionTask = { type: 'Wintertodt', async run(data: ActivityTaskOptionsWithQuantity) { @@ -47,7 +23,7 @@ export const wintertodtTask: MinionTask = { let totalPoints = 0; for (let i = 0; i < quantity; i++) { - const points = PointsTable.rollOrThrow(); + const points = winterTodtPointsTable.rollOrThrow(); totalPoints += points; loot.add( diff --git a/src/tasks/minions/questingActivity.ts b/src/tasks/minions/questingActivity.ts index 78e13ec2c70..7af74e62841 100644 --- a/src/tasks/minions/questingActivity.ts +++ b/src/tasks/minions/questingActivity.ts @@ -1,11 +1,11 @@ import { randInt } from 'e'; import { Emoji } from '../../lib/constants'; +import { MAX_QP } from '../../lib/minions/data/quests'; import { SkillsEnum } from '../../lib/skilling/types'; import type { ActivityTaskOptionsWithQuantity } from '../../lib/types/minions'; import { roll } from '../../lib/util'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; -import { MAX_QP } from '../../mahoji/lib/abstracted_commands/questCommand'; export const questingTask: MinionTask = { type: 'Questing', diff --git a/src/tasks/minions/specificQuestActivity.ts b/src/tasks/minions/specificQuestActivity.ts index 445bdf798fb..1c45125a7bd 100644 --- a/src/tasks/minions/specificQuestActivity.ts +++ b/src/tasks/minions/specificQuestActivity.ts @@ -1,9 +1,9 @@ import { bold } from 'discord.js'; +import { quests } from '../../lib/minions/data/quests'; import { SkillsEnum } from '../../lib/skilling/types'; import type { SpecificQuestOptions } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; -import { quests } from '../../mahoji/lib/abstracted_commands/questCommand'; export const specificQuestTask: MinionTask = { type: 'SpecificQuest', diff --git a/tests/integration/commands/sacrifice.test.ts b/tests/integration/commands/sacrifice.test.ts index dbbd87cfba5..f32ac4464a2 100644 --- a/tests/integration/commands/sacrifice.test.ts +++ b/tests/integration/commands/sacrifice.test.ts @@ -18,28 +18,28 @@ describe('Sacrifice Command', async () => { expect(result).toEqual( `${Emoji.Incinerator} **Your Sacrifice Stats** ${Emoji.Incinerator}\n\n` + `**Current Minion Icon:** ${Emoji.Minion}\n` + - '**Sacrificed Value:** 1,909 GP\n' + + '**Sacrificed Value:** 1,590 GP\n' + '**Unique Items Sacrificed:** 2 items' ); }); test('No items provided', async () => { const result = await user.runCommand(sacrificeCommand, { items: 'aaaa' }); - expect(result).toEqual('No items were provided.\nYour current sacrificed value is: 1,909 (1.91k)'); + expect(result).toEqual('No items were provided.\nYour current sacrificed value is: 1,590 (1.59k)'); }); test('Successful', async () => { await user.addItemsToBank({ items: new Bank().add('Trout').add('Coal', 10) }); const result = await user.runCommand(sacrificeCommand, { items: '1 trout, 10 coal' }); expect(result).toEqual( - 'You sacrificed 10x Coal, 1x Trout, with a value of 1,909gp (1.91k). Your total amount sacrificed is now: 3,818. ' + 'You sacrificed 10x Coal, 1x Trout, with a value of 1,590gp (1.59k). Your total amount sacrificed is now: 3,180. ' ); const stats = await user.fetchStats({ sacrificed_bank: true }); expect(user.bank.equals(new Bank())).toBe(true); expect(new Bank(stats.sacrificed_bank as ItemBank).equals(new Bank().add('Coal', 20).add('Trout', 2))).toBe( true ); - expect(user.user.sacrificedValue).toEqual(BigInt(3818)); + expect(user.user.sacrificedValue).toEqual(BigInt(3180)); const clientSettings = await mahojiClientSettingsFetch({ economyStats_sacrificedBank: true }); expect( new Bank(clientSettings.economyStats_sacrificedBank as ItemBank).equals( @@ -49,7 +49,7 @@ describe('Sacrifice Command', async () => { await user.addItemsToBank({ items: new Bank().add('Trout').add('Cake') }); const res = await user.runCommand(sacrificeCommand, { items: '1 trout, 1 cake' }); expect(res).toEqual( - 'You sacrificed 1x Trout, 1x Cake, with a value of 156gp (156). Your total amount sacrificed is now: 3,974. ' + 'You sacrificed 1x Trout, 1x Cake, with a value of 257gp (257). Your total amount sacrificed is now: 3,437. ' ); await user.sync(); expect(user.bank.equals(new Bank())).toBe(true); @@ -57,7 +57,7 @@ describe('Sacrifice Command', async () => { expect( new Bank(stats2.sacrificed_bank as ItemBank).equals(new Bank().add('Coal', 20).add('Trout', 3).add('Cake')) ).toBe(true); - expect(user.user.sacrificedValue).toEqual(BigInt(3974)); + expect(user.user.sacrificedValue).toEqual(BigInt(3437)); const clientSettings2 = await mahojiClientSettingsFetch({ economyStats_sacrificedBank: true }); expect( diff --git a/tests/unit/GeneralBank.test.ts b/tests/unit/GeneralBank.test.ts new file mode 100644 index 00000000000..7a075784efa --- /dev/null +++ b/tests/unit/GeneralBank.test.ts @@ -0,0 +1,186 @@ +import { beforeEach, describe, expect, it, test } from 'vitest'; + +import { GeneralBank } from '../../src/lib/structures/GeneralBank'; + +describe('GeneralBank', () => { + // eslint-disable-next-line @typescript-eslint/init-declarations + let bank: GeneralBank; + // eslint-disable-next-line @typescript-eslint/init-declarations + let validator: (key: string, value: number, bank: Record) => void; + + beforeEach(() => { + validator = (key, value, bank) => { + if (!key.startsWith('F')) throw new Error(`Key ${key} does not start with F`); + }; + bank = new GeneralBank({ validator }); + }); + + it('initializes an empty bank correctly', () => { + expect(bank.toString()).toBe('Bank is empty'); + }); + + it('adds and retrieves an item correctly', () => { + bank.add('Fgold', 100); + expect(bank.amount('Fgold')).toBe(100); + }); + + it('throws an error if adding an item with a key not starting with F', () => { + expect(() => bank.add('gold', 100)).toThrow('Key gold does not start with F'); + }); + + it('removes items correctly and respects item non-existence', () => { + bank.add('Fsilver', 50); + bank.remove('Fsilver', 20); + expect(bank.amount('Fsilver')).toBe(30); + expect(() => bank.remove('Fsilver', 50)).toThrow(); + }); + + it('handles cloning correctly', () => { + bank.add('Fcopper', 200); + const newBank = bank.clone(); + expect(newBank.amount('Fcopper')).toBe(200); + newBank.add('Fcopper', 100); + expect(bank.amount('Fcopper')).toBe(200); + expect(newBank.amount('Fcopper')).toBe(300); + }); + + it('supports adding another bank', () => { + const otherBank = new GeneralBank(); + otherBank.add('Fbronze', 300); + bank.add(otherBank); + expect(bank.amount('Fbronze')).toBe(300); + }); + + it('supports removing quantities via another bank', () => { + bank.add('Firon', 500); + const otherBank = new GeneralBank(); + otherBank.add('Firon', 200); + bank.remove(otherBank); + expect(bank.amount('Firon')).toBe(300); + }); +}); + +describe('GeneralBank 2', () => { + // eslint-disable-next-line @typescript-eslint/init-declarations + let bank: GeneralBank; + + beforeEach(() => { + bank = new GeneralBank(); + }); + + it('rejects negative quantities for addition', () => { + expect(() => bank.add('Fgold', -10)).toThrow('Quantity must be non-negative.'); + }); + + it('rejects negative quantities for removal', () => { + bank.add('Fsilver', 20); + expect(() => bank.remove('Fsilver', -5)).toThrow('Quantity must be non-negative.'); + }); + + it('handles zero quantity operations without changing the bank', () => { + bank.add('Fgold', 100); + bank.add('Fgold', 0); + expect(bank.amount('Fgold')).toBe(100); + bank.remove('Fgold', 0); + expect(bank.amount('Fgold')).toBe(100); + }); + + it('ensures remove does not fall below zero', () => { + bank.add('Firon', 50); + expect(() => bank.remove('Firon', 51)).toThrow('Not enough Firon to remove.'); + }); + + it('ensures immutability after cloning', () => { + bank.add('Fsilver', 100); + const cloneBank = bank.clone(); + cloneBank.add('Fsilver', 50); + expect(bank.amount('Fsilver')).toBe(100); + expect(cloneBank.amount('Fsilver')).toBe(150); + }); + + it('handles complex validators', () => { + const complexBank = new GeneralBank({ + validator: (key, value, bank) => { + if (value > 1000) throw new Error('Values above 1000 are not allowed'); + } + }); + complexBank.add('Fgold', 500); + expect(() => complexBank.add('Fgold', 600)).toThrow('Values above 1000 are not allowed'); + }); + + it('processes high volume operations correctly', async () => { + for (let i = 0; i < 1000; i++) { + bank.add(`Fitem${i}`, i + 1); + } + expect(bank.amount('Fitem999')).toBe(1000); + for (let i = 0; i < 1000; i++) { + bank.remove(`Fitem${i}`, i + 1); + } + expect(bank.amount('Fitem999')).toBe(0); + }); +}); + +describe('Bank with allowedKeys', () => { + // eslint-disable-next-line @typescript-eslint/init-declarations + let bank: GeneralBank; + + beforeEach(() => { + // Initialize the bank with a set of allowed keys + bank = new GeneralBank({ allowedKeys: ['Fgold', 'Fsilver', 'Fcopper'] }); + }); + + it('allows adding items with allowed keys', () => { + expect(() => bank.add('Fgold', 100)).not.toThrow(); + expect(bank.amount('Fgold')).toBe(100); + }); + + it('prevents adding items with disallowed keys', () => { + expect(() => bank.add('Fplatinum', 50)).toThrow( + 'Key Fplatinum (string) is not allowed, only these are allowed: Fgold, Fsilver, Fcopper' + ); + }); + + it('allows removing items with allowed keys', () => { + bank.add('Fsilver', 50); + expect(() => bank.remove('Fsilver', 25)).not.toThrow(); + expect(bank.amount('Fsilver')).toBe(25); + }); + + it('throws error on attempt to clone with disallowed key modifications', () => { + const cloneBank = bank.clone(); + expect(() => cloneBank.add('Firon', 100)).toThrow( + 'Key Firon (string) is not allowed, only these are allowed: Fgold, Fsilver, Fcopper' + ); + }); + + it('ensures that operations on cloned banks respect original allowed keys', () => { + const cloneBank = bank.clone(); + cloneBank.add('Fsilver', 200); + expect(() => cloneBank.add('Fbronze', 100)).toThrow( + 'Key Fbronze (string) is not allowed, only these are allowed: Fgold, Fsilver, Fcopper' + ); + expect(cloneBank.amount('Fsilver')).toBe(200); + }); + + it('should throw for floats in int bank', () => { + const b = new GeneralBank(); + expect(() => b.add('a', 0.15)).toThrow(); + }); +}); + +test('Float Banks', () => { + const floatBank = new GeneralBank({ valueSchema: { floats: true, min: 0, max: 1_222_222.100_150_02 } }); + floatBank.add('a', 1); + floatBank.add('a', 0.15); + expect(floatBank.amount('a')).toBe(1.15); + floatBank.add('b', 0.100_15); + expect(floatBank.amount('b')).toBe(0.100_15); + floatBank.add('b', 0.000_000_01); + expect(floatBank.amount('b')).toBe(0.100_150_01); + floatBank.add('b', 1_222_222); + expect(floatBank.amount('b')).toBe(1_222_222.100_150_01); + expect(floatBank.entries()).toEqual([ + ['a', 1.15], + ['b', 1_222_222.100_150_01] + ]); +}); diff --git a/tests/unit/snapshots/banksnapshots.test.ts.snap b/tests/unit/snapshots/banksnapshots.test.ts.snap index ace8c2280d5..ac26ef56ed9 100644 --- a/tests/unit/snapshots/banksnapshots.test.ts.snap +++ b/tests/unit/snapshots/banksnapshots.test.ts.snap @@ -46,7 +46,7 @@ exports[`OSB Buyables 1`] = ` "feather", ], "gpCost": 50, - "ironmanPrice": 2, + "ironmanPrice": 4, "itemCost": Bank { "bank": {}, "frozen": false, @@ -8791,6 +8791,42 @@ exports[`OSB Creatables 1`] = ` "frozen": false, }, }, + { + "cantHaveItems": undefined, + "inputItems": Bank { + "bank": { + "28924": 150000, + "28947": 1, + }, + "frozen": false, + }, + "name": "Blessed dizana's quiver", + "outputItems": Bank { + "bank": { + "28955": 1, + }, + "frozen": false, + }, + }, + { + "cantHaveItems": undefined, + "inputItems": Bank { + "bank": { + "13280": 1, + "13281": 1, + "28955": 1, + }, + "frozen": false, + }, + "name": "Dizana's max cape", + "outputItems": Bank { + "bank": { + "28902": 1, + "28904": 1, + }, + "frozen": false, + }, + }, { "cantHaveItems": undefined, "inputItems": Bank { @@ -9909,6 +9945,23 @@ exports[`OSB Creatables 1`] = ` "frozen": false, }, }, + { + "cantHaveItems": undefined, + "inputItems": Bank { + "bank": { + "28947": 1, + }, + "frozen": false, + }, + "name": "Revert Dizana's quiver (uncharged)", + "noCl": true, + "outputItems": Bank { + "bank": { + "28924": 4000, + }, + "frozen": false, + }, + }, { "QPRequired": 150, "cantHaveItems": undefined, diff --git a/tests/unit/snapshots/clsnapshots.test.ts.snap b/tests/unit/snapshots/clsnapshots.test.ts.snap index 328e187c4be..e8c30a8a435 100644 --- a/tests/unit/snapshots/clsnapshots.test.ts.snap +++ b/tests/unit/snapshots/clsnapshots.test.ts.snap @@ -25,7 +25,7 @@ Chompy Birds (19) Commander Zilyana (8) Corporeal Beast (7) Crazy archaeologist (3) -Creatables (662) +Creatables (665) Creature Creation (7) Cyclopes (8) Dagannoth Kings (10) @@ -37,6 +37,7 @@ Elite Treasure Trails (59) Farming (141) Fishing Trawler (4) Forestry (23) +Fortis Colosseum (8) Fossil Island Notes (10) General Graardor (8) Giant Mole (3) @@ -582,6 +583,7 @@ Dharok's platebody Dharok's platelegs Digsite teleport Dinh's bulwark +Dizana's quiver (uncharged) Double ammo mould Draconic visage Dragon 2h sword @@ -625,6 +627,7 @@ Drake's tooth Dual sai Dust battlestaff Earth warrior champion scroll +Echo crystal Ectoplasmator Ecumenical key Elder chaos hood @@ -1360,6 +1363,7 @@ Smiths trousers Smiths tunic Smoke battlestaff Smoke quartz +Smol heredit Smolcano Smouldering stone Soaked page @@ -1400,6 +1404,10 @@ Studded body (t) Studded chaps (g) Studded chaps (t) Sturdy beehive parts +Sunfire fanatic chausses +Sunfire fanatic cuirass +Sunfire fanatic helm +Sunfire splinters Superior mining gloves Swift blade Tackle box @@ -1426,6 +1434,7 @@ Toktz-xil-ek Toktz-xil-ul Tome of fire Tome of water (empty) +Tonalztics of ralos (uncharged) Top hat Top of sceptre Torag's hammers diff --git a/vitest.unit.config.mts b/vitest.unit.config.mts index 4d8e0dce89d..9743145a3d3 100644 --- a/vitest.unit.config.mts +++ b/vitest.unit.config.mts @@ -13,6 +13,9 @@ export default defineConfig({ }, setupFiles: 'tests/unit/setup.ts', resolveSnapshotPath: (testPath, extension) => - join(join(dirname(testPath), 'snapshots'), `${basename(testPath)}${extension}`) + join(join(dirname(testPath), 'snapshots'), `${basename(testPath)}${extension}`), + maxWorkers: 10, + minWorkers: 1, + slowTestThreshold: 0 } }); diff --git a/yarn.lock b/yarn.lock index 0ccda17eba4..09a2d20d5e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -79,6 +79,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.24.6": + version "7.24.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.7.tgz#f4f0d5530e8dbdf59b3451b9b3e594b6ba082e12" + integrity sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/types@^7.23.6": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.9.tgz#1dd7b59a9a2b5c87f8b41e52770b5ecbf492e002" @@ -1689,6 +1696,11 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== +complex.js@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/complex.js/-/complex.js-2.1.1.tgz#0675dac8e464ec431fb2ab7d30f41d889fb25c31" + integrity sha512-8njCHOTtFFLtegk6zQo0kkVX1rngygb/KQI6z1qZxlFI3scluC+LVTCFbrkWjBv4vvLlbQ9t88IPMC6k95VTTg== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -1749,6 +1761,11 @@ debug@^4.0.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: dependencies: ms "2.1.2" +decimal.js@^10.4.3: + version "10.4.3" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23" + integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== + deep-eql@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" @@ -2178,6 +2195,11 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escape-latex@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/escape-latex/-/escape-latex-1.2.0.tgz#07c03818cf7dac250cce517f4fda1b001ef2bca1" + integrity sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw== + escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -2733,6 +2755,11 @@ forwarded@0.2.0, forwarded@^0.2.0: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== +fraction.js@^4.3.7: + version "4.3.7" + resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" + integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -3425,6 +3452,11 @@ jackspeak@^2.3.6: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" +javascript-natural-sort@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59" + integrity sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== + jest-diff@^29.6.2: version "29.6.2" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.2.tgz#c36001e5543e82a0805051d3ceac32e6825c1c46" @@ -3734,6 +3766,21 @@ math-expression-evaluator@^1.3.14: resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.3.14.tgz#0ebeaccf65fea0f6f5a626f88df41814e5fcd9bf" integrity sha512-M6AMrvq9bO8uL42KvQHPA2/SbAobA0R7gviUmPrcTcGfdwpaLitz4q2Euzx2lP9Oy88vxK3HOrsISgSwKsYS4A== +mathjs@^13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/mathjs/-/mathjs-13.0.0.tgz#29027756f92b7eae6ea9c22bbbdf3a50e66ef917" + integrity sha512-Jy9/01M5lTRLxlkxnvPmvWq6EFwzq8guIspeQ9p66AY+8Pih3Jf8Us5fSZ9kC8gl7iRNHUQ+SJpitX41aa6agw== + dependencies: + "@babel/runtime" "^7.24.6" + complex.js "^2.1.1" + decimal.js "^10.4.3" + escape-latex "^1.2.0" + fraction.js "^4.3.7" + javascript-natural-sort "^0.7.1" + seedrandom "^3.0.5" + tiny-emitter "^2.1.0" + typed-function "^4.1.1" + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -3999,10 +4046,10 @@ obliterator@^2.0.1: resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" integrity sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ== -oldschooljs@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/oldschooljs/-/oldschooljs-2.5.1.tgz#d152841ad6690c66ad7f3e4e2db046c0f0b45010" - integrity sha512-MAJnQQcD3Ofq0GjLZ+ZrmeQ6VLw7p0I3bH42OZTxIHaEBX+z6UFVxDszj8wPRNKFwZxKDgLTXOAc5O7L3JR57w== +oldschooljs@^2.5.4: + version "2.5.4" + resolved "https://registry.yarnpkg.com/oldschooljs/-/oldschooljs-2.5.4.tgz#59aa85be412ac14ca7b2cda885911d16ceaac702" + integrity sha512-m3Jqmbx2pQIBAqCQn7nQlRO/VkWqHUWUTzdTiXmOHrK+NEB1fapnC1QuwQaf0sCv/fyy+FN2CV2nnDelm/315w== dependencies: deepmerge "^4.3.1" e "^0.2.32" @@ -4731,6 +4778,11 @@ secure-json-parse@^2.4.0, secure-json-parse@^2.5.0: resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== +seedrandom@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7" + integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg== + "semver@2 || 3 || 4 || 5", "semver@>= 5 < 6": version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" @@ -5119,6 +5171,11 @@ thread-stream@^2.0.0: dependencies: real-require "^0.2.0" +tiny-emitter@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" + integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== + tiny-lru@^10.0.0: version "10.0.1" resolved "https://registry.yarnpkg.com/tiny-lru/-/tiny-lru-10.0.1.tgz#aaf5d22207e641ed1b176ac2e616d6cc2fc9ef66" @@ -5278,6 +5335,11 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" +typed-function@^4.1.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/typed-function/-/typed-function-4.2.1.tgz#19aa51847aa2dea9ef5e7fb7641c060179a74426" + integrity sha512-EGjWssW7Tsk4DGfE+5yluuljS1OGYWiI1J6e8puZz9nTMM51Oug8CD5Zo4gWMsOhq5BI+1bF+rWTm4Vbj3ivRA== + typescript@5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.2.tgz#891e1a90c5189d8506af64b9ef929fca99ba1ee5" From 63ff511675f28040129eb9dfdcc72f5cf9fc24b8 Mon Sep 17 00:00:00 2001 From: nwjgit <69014816+nwjgit@users.noreply.github.com> Date: Mon, 1 Jul 2024 20:25:16 -0500 Subject: [PATCH 024/249] Carapace fixes (#5929) --- .../skills/crafting/craftables/carapace.ts | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/lib/skilling/skills/crafting/craftables/carapace.ts b/src/lib/skilling/skills/crafting/craftables/carapace.ts index 0d021da9bed..8caa7d45700 100644 --- a/src/lib/skilling/skills/crafting/craftables/carapace.ts +++ b/src/lib/skilling/skills/crafting/craftables/carapace.ts @@ -5,50 +5,50 @@ import { Craftable } from '../../../types'; export const carapaceCraftables: Craftable[] = [ { - name: 'Carapace gloves', - id: itemID('Carapace gloves'), - level: 11, - xp: 23.8, - inputItems: new Bank({ Carapace: 1 }), + name: 'Carapace helm', + id: itemID('Carapace helm'), + level: 33, + xp: 24, + inputItems: new Bank({ Carapace: 2 }), tickRate: 3 }, { - name: 'Carapace boots', - id: itemID('Carapace boots'), - level: 15, - xp: 26.2, - inputItems: new Bank({ Carapace: 1 }), + name: 'Carapace torso', + id: itemID('Carapace torso'), + level: 35, + xp: 36, + inputItems: new Bank({ Carapace: 3 }), tickRate: 3 }, { - name: 'Carapace helm', - id: itemID('Carapace helm'), - level: 22, - xp: 28.5, - inputItems: new Bank({ Carapace: 1 }), + name: 'Carapace legs', + id: itemID('Carapace legs'), + level: 34, + xp: 24, + inputItems: new Bank({ Carapace: 2 }), tickRate: 3 }, { - name: 'Carapace gloves', - id: itemID('Carapace gloves'), - level: 32, - xp: 32, + name: 'Carapace boots', + id: itemID('Carapace boots'), + level: 31, + xp: 12, inputItems: new Bank({ Carapace: 1 }), tickRate: 3 }, { - name: 'Carapace torso', - id: itemID('Carapace torso'), - level: 35, - xp: 35, - inputItems: new Bank({ Carapace: 3 }), + name: 'Carapace gloves', + id: itemID('Carapace gloves'), + level: 30, + xp: 12, + inputItems: new Bank({ Carapace: 1 }), tickRate: 3 }, { - name: 'Carapace legs', - id: itemID('Carapace legs'), - level: 44, - xp: 37, + name: 'Carapace shield', + id: itemID('Carapace shield'), + level: 36, + xp: 36, inputItems: new Bank({ Carapace: 3 }), tickRate: 3 } From 20b30221a90b79bc5783893367424283164180a7 Mon Sep 17 00:00:00 2001 From: nwjgit <69014816+nwjgit@users.noreply.github.com> Date: Mon, 1 Jul 2024 20:26:35 -0500 Subject: [PATCH 025/249] Clean up BSO wintertodt messages (#5905) --- .../abstracted_commands/wintertodtCommand.ts | 54 +++++++------------ .../minions/minigames/wintertodtActivity.ts | 13 +++-- 2 files changed, 29 insertions(+), 38 deletions(-) diff --git a/src/mahoji/lib/abstracted_commands/wintertodtCommand.ts b/src/mahoji/lib/abstracted_commands/wintertodtCommand.ts index b229c0c1d25..fac8812a6dc 100644 --- a/src/mahoji/lib/abstracted_commands/wintertodtCommand.ts +++ b/src/mahoji/lib/abstracted_commands/wintertodtCommand.ts @@ -18,15 +18,21 @@ export async function wintertodtCommand(user: MUser, channelID: string) { return 'You need 50 Firemaking to have a chance at defeating the Wintertodt.'; } - const messages = []; - let durationPerTodt = Time.Minute * 7.3; // Up to a 10% boost for 99 WC const wcBoost = (wcLevel + 1) / 10; + const boosts: string[] = []; + const foodStr: string[] = []; + if (wcBoost > 1) { - messages.push(`**Boosts:** ${wcBoost.toFixed(2)}% for Woodcutting level\n`); + boosts.push(`**Boosts:** ${wcBoost.toFixed(2)}% for Woodcutting level`); + } + + if (user.hasEquippedOrInBank('Dwarven greataxe')) { + durationPerTodt /= 2; + boosts.push('2x faster for Dwarven greataxe.'); } durationPerTodt = reduceNumByPercent(durationPerTodt, wcBoost); @@ -45,22 +51,6 @@ export async function wintertodtCommand(user: MUser, channelID: string) { healAmountNeeded -= warmGearAmount * 15; durationPerTodt = reduceNumByPercent(durationPerTodt, 5 * warmGearAmount); - const boosts: string[] = []; - - if (user.hasEquippedOrInBank('Dwarven greataxe')) { - durationPerTodt /= 2; - boosts.push('2x faster for Dwarven greataxe.'); - } - - if (healAmountNeeded !== baseHealAmountNeeded) { - messages.push( - `${calcWhatPercent( - baseHealAmountNeeded - healAmountNeeded, - baseHealAmountNeeded - )}% less food for wearing warm gear` - ); - } - const quantity = Math.floor(calcMaxTripLength(user, 'Wintertodt') / durationPerTodt); for (const food of Eatables) { @@ -75,20 +65,18 @@ export async function wintertodtCommand(user: MUser, channelID: string) { continue; } - let foodStr: string = `**Food:** ${healAmountNeeded} HP/kill`; + foodStr.push(`**Food:** ${healAmountNeeded} HP/kill`); if (healAmountNeeded !== baseHealAmountNeeded) { - foodStr += `. Reduced from ${baseHealAmountNeeded}, -${calcWhatPercent( - baseHealAmountNeeded - healAmountNeeded, - baseHealAmountNeeded - )}% for wearing warm gear. `; - } else { - foodStr += '. '; + foodStr.push( + `Reduced from ${baseHealAmountNeeded}, -${calcWhatPercent( + baseHealAmountNeeded - healAmountNeeded, + baseHealAmountNeeded + )}% for wearing warm gear` + ); } - foodStr += ` **Removed ${amountNeeded}x ${food.name}**`; - - messages.push(foodStr); + foodStr.push(`**Removed ${amountNeeded}x ${food.name}**`); const cost = new Bank().add(food.id, amountNeeded); @@ -129,11 +117,9 @@ export async function wintertodtCommand(user: MUser, channelID: string) { user.minionName } is now off to kill Wintertodt ${quantity}x times, their trip will take ${formatDuration( durationPerTodt * quantity - )}. (${formatDuration(durationPerTodt)} per todt)\n\n${messages.join(', ')}.`; - - if (boosts.length > 0) { - str += `**Boosts:** ${boosts.join(', ')}`; - } + )}. (${formatDuration(durationPerTodt)} per todt)\n\n${boosts.length > 0 ? `${boosts.join(', ')}\n` : ''}${ + foodStr.length > 0 ? foodStr.join(', ') : '' + }.`; return str; } diff --git a/src/tasks/minions/minigames/wintertodtActivity.ts b/src/tasks/minions/minigames/wintertodtActivity.ts index ed43f6ac0ce..ef13f112d7d 100644 --- a/src/tasks/minions/minigames/wintertodtActivity.ts +++ b/src/tasks/minions/minigames/wintertodtActivity.ts @@ -18,6 +18,7 @@ export const wintertodtTask: MinionTask = { async run(data: ActivityTaskOptionsWithQuantity) { const { userID, channelID, quantity, duration } = data; const user = await mUserFetch(userID); + const hasMasterCape = user.hasEquippedOrInBank('Firemaking master cape'); let loot = new Bank(); @@ -105,7 +106,7 @@ export const wintertodtTask: MinionTask = { if (flappyRes.shouldGiveBoost) { loot.multiply(2); } - if (user.hasEquippedOrInBank('Firemaking master cape')) { + if (hasMasterCape) { loot.multiply(2); } @@ -114,7 +115,7 @@ export const wintertodtTask: MinionTask = { collectionLog: true, itemsToAdd: loot }); - incrementMinigameScore(user.id, 'wintertodt', quantity); + await incrementMinigameScore(user.id, 'wintertodt', quantity); const image = await makeBankImage({ bank: itemsAdded, @@ -122,7 +123,11 @@ export const wintertodtTask: MinionTask = { previousCL }); - let output = `${user}, ${user.minionName} finished subduing Wintertodt ${quantity}x times. ${xpStr}, you cut ${numberOfRoots}x Bruma roots.`; + let output = `${user}, ${ + user.minionName + } finished subduing Wintertodt ${quantity}x times. ${xpStr}, you cut ${numberOfRoots}x Bruma roots${ + hasMasterCape ? ', 2x loot for Firemaking master cape.' : '.' + }`; if (fmBonusXP > 0) { output += `\n\n**Firemaking Bonus XP:** ${fmBonusXP.toLocaleString()}`; @@ -149,6 +154,6 @@ export const wintertodtTask: MinionTask = { ] }); - handleTripFinish(user, channelID, output, image.file.attachment, data, itemsAdded); + return handleTripFinish(user, channelID, output, image.file.attachment, data, itemsAdded); } }; From f6df0728f1e2dd690862875b7ef143fbd5c8b192 Mon Sep 17 00:00:00 2001 From: GC <30398469+gc@users.noreply.github.com> Date: Wed, 3 Jul 2024 01:02:19 +1000 Subject: [PATCH 026/249] Housekeeping (#5933) --- .github/workflows/integration_tests.yml | 2 +- .github/workflows/unit_tests.yml | 2 +- package.json | 43 +- src/index.ts | 6 - src/lib/crons.ts | 95 -- src/lib/data/OSB.commands.json | 576 ++++++++ src/lib/events.ts | 2 +- src/lib/geImage.ts | 3 +- src/lib/http/routes/commands.ts | 46 - src/lib/http/routes/index.ts | 5 +- src/lib/http/routes/root.ts | 14 - src/lib/randomEvents.ts | 2 +- src/lib/roboChimp.ts | 2 +- src/mahoji/commands/admin.ts | 2 - src/mahoji/commands/config.ts | 59 +- src/mahoji/guildSettings.ts | 9 +- src/mahoji/lib/Cooldowns.ts | 2 +- src/mahoji/lib/bingo/BingoManager.ts | 2 +- src/scripts/integration-tests.ts | 2 +- src/scripts/postbuild.ts | 65 + src/scripts/render-creatables-file.ts | 1 + tests/unit/http/http.test.ts | 23 - yarn.lock | 1663 +++++++++++++++-------- 23 files changed, 1802 insertions(+), 824 deletions(-) create mode 100644 src/lib/data/OSB.commands.json delete mode 100644 src/lib/http/routes/commands.ts delete mode 100644 src/lib/http/routes/root.ts create mode 100644 src/scripts/postbuild.ts diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 5671a71eacb..e0fffc4a61d 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -14,7 +14,7 @@ jobs: timeout-minutes: 10 strategy: matrix: - node_version: [18.12.0, 20] + node_version: [20] os: [ubuntu-latest] steps: diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 168e1a2245f..2f50f426678 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -14,7 +14,7 @@ jobs: timeout-minutes: 10 strategy: matrix: - node_version: [18.12.0, 20] + node_version: [20] os: [ubuntu-latest] steps: diff --git a/package.json b/package.json index c4bcacfa8f9..25d883eaeae 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,10 @@ { "scripts": { - "gen": "concurrently \"prisma generate\" \"prisma generate --schema prisma/robochimp.prisma\" && echo \"Generated Prisma Client\"", - "lint": "concurrently \"prettier --use-tabs ./**/*.{js,md,json,yml} --write\" \"eslint *.{ts,mts} \"{src,tests}/**/*.ts\" --fix\"", + "gen": "concurrently \"prisma generate --no-hints\" \"prisma generate --no-hints --schema prisma/robochimp.prisma\" && echo \"Generated Prisma Client\"", + "prettify": "prettier --use-tabs ./**/*.{js,md,json,yml} --write", + "lint": "concurrently \"yarn prettify\" \"eslint --quiet *.{ts,mts} \"{src,tests}/**/*.ts\" --fix\"", "build:tsc": "tsc -p src", - "build": "concurrently \"yarn wipedist\" \"yarn gen\" && concurrently \"yarn prebuild:scripts\" && yarn build:tsc && echo \"Finished Building\"", + "build": "concurrently \"yarn wipedist\" \"yarn gen\" && yarn pre:build && yarn build:tsc && yarn post:build && echo \"Finished Building\"", "wipedist": "rimraf \"dist/\"", "start": "yarn build && concurrently \"tsc -w -p src\" \"node --enable-source-maps dist/\"", "test": "concurrently \"tsc -p src\" \"yarn test:lint\" \"yarn test:unit\"", @@ -12,7 +13,8 @@ "dev": "yarn wipedist && tsc -w -p src", "test:watch": "vitest --config vitest.unit.config.mts --coverage", "test:integration": "tsx ./src/scripts/integration-tests.ts", - "prebuild:scripts": "tsx ./src/scripts/render-creatables-file.ts", + "pre:build": "tsx ./src/scripts/render-creatables-file.ts", + "post:build": "tsx ./src/scripts/postbuild.ts", "build:esbuild": "esbuild --bundle src/index.ts --outdir=dist --platform=node --loader:.node=file --external:canvas", "run:node": "node --enable-source-maps dist/index.js" }, @@ -21,29 +23,28 @@ "@fastify/helmet": "^10.1.0", "@fastify/rate-limit": "^8.0.0", "@fastify/sensible": "^5.2.0", - "@napi-rs/canvas": "0.1.38", + "@napi-rs/canvas": "^0.1.53", "@octokit/graphql": "^4.8.0", "@oldschoolgg/toolkit": "^0.0.24", "@prisma/client": "^5.13.0", "@sapphire/snowflake": "^3.5.3", "@sapphire/stopwatch": "^1.4.0", "@sapphire/time-utilities": "^1.6.0", - "@sentry/node": "^7.113.0", + "@sentry/node": "^8.13.0", "ascii-table3": "^0.9.0", "bufferutil": "^4.0.8", "chart.js": "^3.7.0", "chartjs-node-canvas": "github:gc/ChartjsNodeCanvas#a598b6dd27c44351f235bca07ca4ee660121f289", "chartjs-plugin-datalabels": "^2.0.0", - "deep-equal": "^2.2.3", "deepmerge": "^4.3.1", - "discord.js": "^14.14.1", - "dotenv": "^16.0.3", + "discord.js": "14.14.1", + "dotenv": "^16.4.5", "e": "^0.2.33", + "fast-deep-equal": "^3.1.3", "fastify": "^4.14.1", "fastify-raw-body": "^4.2.0", - "he": "^1.2.0", "lodash": "^4.17.21", - "lru-cache": "^8.0.0", + "lru-cache": "^10.3.0", "mahoji": "^0.0.7", "mathjs": "^13.0.0", "murmurhash": "^2.0.1", @@ -51,19 +52,16 @@ "node-fetch": "^2.6.7", "oldschooljs": "^2.5.4", "p-queue": "^6.6.2", - "piscina": "^4.4.0", + "piscina": "^4.6.1", "random-js": "^2.1.0", "simple-statistics": "^7.8.3", "sonic-boom": "^4.0.1", - "tsx": "^4.9.0", "zlib-sync": "^0.1.9", - "zod": "^3.23.6" + "zod": "^3.23.8" }, "devDependencies": { "@oldschoolgg/eslint-config": "^2.0.13", "@oldschoolgg/ts-config": "^0.0.1", - "@types/deep-equal": "^1.0.3", - "@types/he": "^1.1.2", "@types/jest-image-snapshot": "^6.1.0", "@types/lodash": "^4.14.195", "@types/madge": "^5.0.0", @@ -73,10 +71,10 @@ "@types/node-fetch": "^2.6.1", "@typescript-eslint/eslint-plugin": "^5.41.0", "@typescript-eslint/parser": "^5.41.0", - "@vitest/coverage-v8": "^1.3.1", - "concurrently": "^7.6.0", - "dotenv-cli": "^7.3.0", - "esbuild": "^0.20.2", + "@vitest/coverage-v8": "^1.6.0", + "concurrently": "^8.2.2", + "dotenv-cli": "^7.4.2", + "esbuild": "^0.22.0", "eslint": "^8.36.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.26.0", @@ -88,11 +86,12 @@ "mitm": "^1.7.2", "prettier": "^2.7.1", "prisma": "^5.13.0", - "rimraf": "^5.0.5", + "rimraf": "^5.0.7", + "tsx": "^4.16.0", "typescript": "5.0.2", "vitest": "^1.6.0" }, "engines": { - "node": ">=18.12.0" + "node": ">=20.12.0" } } diff --git a/src/index.ts b/src/index.ts index a22e554567d..e211401a80c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -29,7 +29,6 @@ import { CACHED_ACTIVE_USER_IDS, syncActiveUserIDs } from './lib/util/cachedUser import { interactionHook } from './lib/util/globalInteractions'; import { handleInteractionError } from './lib/util/interactionReply'; import { logError } from './lib/util/logError'; -import { sonicBoom } from './lib/util/logger'; import { onStartup } from './mahoji/lib/events'; import { postCommand } from './mahoji/lib/postCommand'; import { preCommand } from './mahoji/lib/preCommand'; @@ -218,9 +217,4 @@ process.on('unhandledRejection', err => { logError(err); }); -process.on('exit', exitCode => { - sonicBoom.flushSync(); - debugLog('Process Exit', { type: 'PROCESS_EXIT', exitCode }); -}); - main(); diff --git a/src/lib/crons.ts b/src/lib/crons.ts index bd76a800463..2d9a907d7cc 100644 --- a/src/lib/crons.ts +++ b/src/lib/crons.ts @@ -1,19 +1,8 @@ -import { channelIsSendable } from '@oldschoolgg/toolkit'; -import { EmbedBuilder, PermissionsBitField, resolveColor, TextChannel } from 'discord.js'; -import { Time } from 'e'; -import he from 'he'; import { schedule } from 'node-cron'; -import fetch from 'node-fetch'; -import { production } from '../config'; import { analyticsTick } from './analytics'; import { prisma } from './settings/prisma'; import { cacheCleanup } from './util/cachedUserIDs'; -import { sendToChannelID } from './util/webhook'; - -// If API request fails, we'll retry the previous range next time. (Max of +1 hour) -let redditApiFailures = 0; -const MAX_REDDIT_RETRIES = 12; export function initCrons() { /** @@ -32,90 +21,6 @@ AS DATA GROUP BY item_id;`); }); - let REDDIT_POSTS_IS_DISABLED = false; - /** - * JMod reddit posts/comments - */ - const redditGranularity = 5; - const alreadySentCache = new Set(); - schedule(`*/${redditGranularity} * * * *`, async () => { - if (!production) return; - if (REDDIT_POSTS_IS_DISABLED) return; - async function sendReddit({ post }: { post: any; type: 'comment' | 'submission' }) { - const author = (post.author as string) ?? 'Unknown Author'; - const embed = new EmbedBuilder().setAuthor({ name: author }).setColor(resolveColor('#ff9500')); - - const url = post.full_link ?? `https://old.reddit.com${post.permalink}`; - - if (post.body) { - embed.setDescription(he.decode(post.body)); - } - - if (post.title) { - embed.setTitle(post.title); - embed.setURL(url); - } - - const guildsToSendTo = await prisma.guild.findMany({ - where: { - jmodComments: { - not: null - } - }, - select: { - id: true, - jmodComments: true - } - }); - - for (const { id, jmodComments } of guildsToSendTo) { - const guild = globalClient.guilds.cache.get(id); - if (!guild) continue; - - if (!jmodComments) continue; - const channel = guild.channels.cache.get(jmodComments); - if (!channel) continue; - - const perms = channel.permissionsFor(globalClient.user!); - if (!perms) continue; - if (!channelIsSendable(channel)) continue; - - if ( - channel instanceof TextChannel && - perms.has(PermissionsBitField.Flags.EmbedLinks) && - perms.has(PermissionsBitField.Flags.SendMessages) - ) { - await sendToChannelID(channel.id, { content: `<${url}>`, embed }); - } - } - } - - const retries = Math.min(MAX_REDDIT_RETRIES, redditApiFailures); - const utcTime = Math.floor((Date.now() - Time.Minute * redditGranularity * (retries + 1)) / 1000) - 30; - let retry = false; - for (const type of ['comment', 'submission'] as const) { - const url = `https://api.pushshift.io/reddit/search/${type}/?subreddit=2007scape&size=1000&since=${utcTime}`; - try { - const _result = await fetch(url).then(res => { - if (res.status >= 400) retry = true; - return res.json(); - }); - if (!_result || !_result.data || !Array.isArray(_result.data)) { - if (_result && _result.error !== null) retry = true; - continue; - } - for (const entity of _result.data) { - if (!entity.author_flair_text || !entity.author_flair_text.includes(':jagexmod:')) continue; - if (alreadySentCache.has(entity.id)) continue; - await sendReddit({ post: entity, type }); - alreadySentCache.add(entity.id); - } - redditApiFailures = 0; - } catch {} - } - if (retry) redditApiFailures++; - }); - /** * Analytics */ diff --git a/src/lib/data/OSB.commands.json b/src/lib/data/OSB.commands.json new file mode 100644 index 00000000000..6e3a3af50a3 --- /dev/null +++ b/src/lib/data/OSB.commands.json @@ -0,0 +1,576 @@ +[ + { + "name": "activities", + "desc": "Miscellaneous activities you can do.", + "subOptions": [ + "plank_make", + "chompy_hunt", + "champions_challenge", + "warriors_guild", + "camdozaal", + "collect", + "quest", + "decant", + "charge", + "fight_caves", + "inferno", + "birdhouses", + "aerial_fishing", + "enchant", + "bury", + "scatter", + "puro_puro", + "alch", + "cast", + "underwater", + "other" + ] + }, + { + "name": "ask", + "desc": "Ask a yes/no question to the bot and receive an answer.", + "subOptions": [] + }, + { + "name": "bank", + "desc": "See your minions bank.", + "subOptions": [] + }, + { + "name": "bingo", + "desc": "Bingo!", + "subOptions": [ + "make_team", + "leave_team", + "view", + "leaderboard", + "create_bingo", + "manage_bingo", + "items" + ] + }, + { + "name": "bossrecords", + "desc": "Shows your OSRS boss records.", + "subOptions": [] + }, + { + "name": "botleagues", + "desc": "Compete in the OSB/BSO Leagues.", + "subOptions": ["help", "claim_trophy", "leaderboard", "buy_reward"] + }, + { + "name": "bs", + "desc": "Search your minions bank.", + "subOptions": [] + }, + { + "name": "build", + "desc": "Sends your minion to train Construction by building things.", + "examples": ["/build name:Crude chair"], + "subOptions": [] + }, + { + "name": "buy", + "desc": "Allows you to purchase items.", + "subOptions": [] + }, + { + "name": "ca", + "desc": "Combat Achievements", + "subOptions": ["view", "claim"] + }, + { + "name": "casket", + "desc": "Simulate opening lots of clues caskets.", + "subOptions": [] + }, + { + "name": "choose", + "desc": "Have the bot make a choice from a list of things.", + "examples": ["/choose list:First option, second option, third option"], + "subOptions": [] + }, + { + "name": "chop", + "desc": "Chop logs using the Woodcutting skill.", + "examples": ["/chop name:Logs"], + "subOptions": [] + }, + { + "name": "cl", + "desc": "See your Collection Log.", + "examples": ["/cl name:Boss"], + "subOptions": [] + }, + { + "name": "claim", + "desc": "Claim prizes, rewards and other things.", + "subOptions": [] + }, + { + "name": "clue", + "desc": "Send your minion to complete clue scrolls.", + "examples": ["/clue tier:easy"], + "subOptions": [] + }, + { + "name": "clues", + "desc": "See your OSRS clue scores.", + "examples": ["/clues rsn:Magnaboy"], + "subOptions": [] + }, + { + "name": "config", + "desc": "Commands configuring settings and options.", + "subOptions": ["server", "user"] + }, + { + "name": "cook", + "desc": "Cook things using the cooking skill.", + "examples": ["/cook name:Shrimp"], + "subOptions": [] + }, + { + "name": "craft", + "desc": "Craft items with the Crafting skill.", + "examples": ["/craft name:Onyx necklace"], + "subOptions": [] + }, + { + "name": "create", + "desc": "Allows you to create items, like godswords or spirit shields - and pack barrows armor sets.", + "subOptions": [] + }, + { + "name": "data", + "desc": "View various pieces of data.", + "examples": ["/data name:Personal Activity Types"], + "subOptions": [] + }, + { + "name": "drop", + "desc": "Drop items from your bank.", + "examples": ["/drop items:10 trout, 5 coal"], + "subOptions": [] + }, + { + "name": "drycalc", + "desc": "Calculate your drystreak chance.", + "subOptions": [] + }, + { + "name": "fake", + "desc": "Generate fake images of getting loot.", + "subOptions": [] + }, + { + "name": "fakepm", + "desc": "Generate fake images of PMs.", + "subOptions": [] + }, + { + "name": "farming", + "desc": "Allows you to do Farming related things.", + "subOptions": [ + "check_patches", + "plant", + "auto_farm", + "auto_farm_filter", + "default_compost", + "always_pay", + "harvest", + "tithe_farm", + "compost_bin", + "contract" + ] + }, + { + "name": "finish", + "desc": "Simulate finishing a CL.", + "subOptions": [] + }, + { + "name": "fish", + "desc": "Send your minion to fish fish.", + "examples": ["/fish name:Shrimp"], + "subOptions": [] + }, + { + "name": "fletch", + "desc": "Fletch items with the Fletching skill.", + "examples": ["/craft name:Onyx necklace"], + "subOptions": [] + }, + { + "name": "gamble", + "desc": "Partake in various gambling activities.", + "subOptions": [ + "item", + "dice", + "duel", + "lucky_pick", + "slots", + "hot_cold", + "give_random_item" + ] + }, + { + "name": "ge", + "desc": "Exchange grandly with other players on the bot!", + "subOptions": [ + "buy", + "sell", + "my_listings", + "cancel", + "stats", + "price", + "view" + ] + }, + { + "name": "gear", + "desc": "Manage, equip, unequip your gear.", + "subOptions": ["equip", "unequip", "stats", "pet", "view", "swap"] + }, + { + "name": "gearpresets", + "desc": "Manage, equip, unequip your gear presets.", + "subOptions": ["view", "equip", "create", "edit", "delete"] + }, + { + "name": "gift", + "desc": "Create gifts for other users, or open one you received.", + "subOptions": ["open", "list", "create", "send"] + }, + { + "name": "giveaway", + "desc": "Giveaway items from your ban to other players.", + "examples": ["/giveaway items:10 trout, 5 coal time:1h"], + "subOptions": ["start", "list"] + }, + { + "name": "gp", + "desc": "See your current GP balance.", + "subOptions": [] + }, + { + "name": "help", + "desc": "Get information and help with the bot.", + "subOptions": [] + }, + { + "name": "hunt", + "desc": "Hunt creatures with the Hunter skill.", + "examples": ["/hunt name:Ferret"], + "subOptions": [] + }, + { + "name": "invite", + "desc": "Shows the invite link for the bot.", + "subOptions": [] + }, + { + "name": "k", + "desc": "Send your minion to kill things.", + "examples": ["/k name:zulrah"], + "subOptions": [] + }, + { + "name": "kc", + "desc": "See your OSRS kc for a monster/boss.", + "examples": ["/kc name:General Graardor"], + "subOptions": [] + }, + { + "name": "kill", + "desc": "Simulate killing monsters.", + "subOptions": [] + }, + { + "name": "laps", + "desc": "Do laps on Agility courses to train Agility.", + "examples": ["/laps name:Ardougne rooftop course"], + "subOptions": [] + }, + { + "name": "lb", + "desc": "Simulate killing monsters.", + "subOptions": [ + "kc", + "farming_contracts", + "inferno", + "sacrifice", + "minigames", + "hunter_catches", + "agility_laps", + "gp", + "skills", + "opens", + "cl", + "clues", + "movers", + "global", + "combat_achievements", + "mastery" + ] + }, + { + "name": "leagues", + "desc": "Check the stats of a OSRS account.", + "subOptions": [] + }, + { + "name": "light", + "desc": "Light logs to train Firemaking.", + "examples": ["/light name:Logs"], + "subOptions": [] + }, + { + "name": "loot", + "desc": "View your loot tracker data.", + "examples": ["/loot view name:Nex"], + "subOptions": ["view", "reset"] + }, + { + "name": "lvl", + "desc": "See a level in your OSRS stats.", + "examples": ["/lvl rsn:Magnaboy skill:Mining"], + "subOptions": [] + }, + { + "name": "m", + "desc": "See your current minion status and helpful buttons.", + "subOptions": [] + }, + { + "name": "mass", + "desc": "Arrange to mass bosses, killing them as a group.", + "examples": ["/mass name:General graardor"], + "subOptions": [] + }, + { + "name": "mine", + "desc": "Send your minion to mine things.", + "examples": ["/mine name:Runite ore"], + "subOptions": [] + }, + { + "name": "minigames", + "desc": "Send your minion to do various minigames.", + "subOptions": [ + "barb_assault", + "castle_wars", + "lms", + "pest_control", + "fishing_trawler", + "mage_arena", + "mage_arena_2", + "gnome_restaurant", + "temple_trek", + "sepulchre", + "gauntlet", + "mage_training_arena", + "mahogany_homes", + "tears_of_guthix", + "pyramid_plunder", + "rogues_den", + "soul_wars", + "volcanic_mine", + "agility_arena", + "trouble_brewing", + "giants_foundry", + "gotr", + "nmz", + "shades_of_morton" + ] + }, + { + "name": "minion", + "desc": "Manage and control your minion.", + "subOptions": [ + "buy", + "status", + "cracker", + "stats", + "achievementdiary", + "bankbg", + "lamp", + "cancel", + "set_icon", + "set_name", + "level", + "kc", + "ironman", + "charge", + "daily", + "train", + "pat", + "blowpipe", + "info", + "peak", + "mastery" + ] + }, + { + "name": "mix", + "desc": "Mix potions to train Herblore.", + "examples": ["/mix name:Prayer potion"], + "subOptions": [] + }, + { + "name": "offer", + "desc": "Offer bones or bird eggs.", + "examples": ["/offer name:Dragon bones"], + "subOptions": [] + }, + { + "name": "open", + "desc": "Open an item (caskets, keys, boxes, etc).", + "subOptions": [] + }, + { + "name": "patreon", + "desc": "Shows the patreon link for the bot, where you can donate.", + "subOptions": [] + }, + { + "name": "pay", + "desc": "Send GP to another user.", + "subOptions": [] + }, + { + "name": "poh", + "desc": "Allows you to access and build in your POH.", + "examples": ["/poh build:Demonic throne"], + "subOptions": ["view", "wallkit", "build", "destroy", "mount_item", "items"] + }, + { + "name": "poll", + "desc": "Create a reaction poll.", + "subOptions": [] + }, + { + "name": "price", + "desc": "Looks up the price of an item.", + "subOptions": [] + }, + { + "name": "raid", + "desc": "Send your minion to do raids - CoX or ToB.", + "subOptions": ["cox", "tob", "toa"] + }, + { + "name": "redeem", + "desc": "Redeem a code you received.", + "subOptions": [] + }, + { + "name": "roll", + "desc": "Roll a random number from 1, up to a limit.", + "subOptions": [] + }, + { + "name": "rp", + "desc": "Admin tools second set", + "subOptions": ["action", "player", "user_event"] + }, + { + "name": "runecraft", + "desc": "Sends your minion to craft runes with essence, or craft tiaras.", + "examples": ["/runecraft"], + "flags": ["minion", "skilling"], + "subOptions": [] + }, + { + "name": "sacrifice", + "desc": "Sacrifice items from your bank to the bot.", + "examples": ["/sacrifice items:10k trout, 5 coal"], + "flags": ["minion"], + "subOptions": [] + }, + { + "name": "sell", + "desc": "Sell items from your bank to the bot for GP.", + "examples": ["/sell items:10k trout, 5 coal"], + "flags": ["minion"], + "subOptions": [] + }, + { + "name": "simulate", + "desc": "Simulate various OSRS related things.", + "examples": ["/simulate cox quantity:1"], + "subOptions": ["cox", "petroll", "colosseum"] + }, + { + "name": "slayer", + "desc": "Slayer skill commands", + "subOptions": ["autoslay", "new_task", "manage", "rewards", "status"] + }, + { + "name": "smelt", + "desc": "Smelt ores/items.", + "examples": ["/smelt runite bar", "/smelt runite bar [quantity: 1]"], + "subOptions": [] + }, + { + "name": "smith", + "desc": "Smith things using the Smithing skill.", + "examples": ["/smith name:Bronze platebody"], + "subOptions": [] + }, + { + "name": "stats", + "desc": "Check the stats of a OSRS account.", + "subOptions": [] + }, + { + "name": "steal", + "desc": "Sends your minion to steal to train Thieving.", + "examples": ["/steal name:Man"], + "subOptions": [] + }, + { + "name": "tokkulshop", + "desc": "Buy or sell items from the Tzhaar shops.", + "examples": [ + "/tokkulshop buy Obsidian platebody", + "/tokkulshop sell Chaos rune 5000" + ], + "flags": ["minion"], + "subOptions": ["buy", "sell"] + }, + { + "name": "tools", + "desc": "Various tools and miscellaneous commands.", + "subOptions": ["patron", "user", "stash_units"] + }, + { + "name": "trade", + "desc": "Allows you to trade items with other players.", + "subOptions": [] + }, + { + "name": "trivia", + "desc": "Try to answer a random trivia question!", + "examples": ["/trivia", "/trivia duel:@Magnaboy"], + "subOptions": [] + }, + { + "name": "use", + "desc": "Use items/things.", + "examples": ["/use name:Mithril seeds"], + "subOptions": [] + }, + { + "name": "wiki", + "desc": "Search the official OSRS wiki.", + "subOptions": [] + }, + { + "name": "xp", + "desc": "See your OSRS xp.", + "examples": ["/xp rsn:Magnaboy"], + "subOptions": [] + } +] diff --git a/src/lib/events.ts b/src/lib/events.ts index 6a7bf2dcc84..526af76b5ce 100644 --- a/src/lib/events.ts +++ b/src/lib/events.ts @@ -2,7 +2,7 @@ import { mentionCommand } from '@oldschoolgg/toolkit'; import { UserError } from '@oldschoolgg/toolkit/dist/lib/UserError'; import { BaseMessageOptions, bold, ButtonBuilder, ButtonStyle, EmbedBuilder, Message, TextChannel } from 'discord.js'; import { isFunction, roll, Time } from 'e'; -import LRUCache from 'lru-cache'; +import { LRUCache } from 'lru-cache'; import { Items } from 'oldschooljs'; import { production, SupportServer } from '../config'; diff --git a/src/lib/geImage.ts b/src/lib/geImage.ts index d66e6c3b9b0..01eca729746 100644 --- a/src/lib/geImage.ts +++ b/src/lib/geImage.ts @@ -2,7 +2,6 @@ import { Canvas, Image, loadImage, SKRSContext2D } from '@napi-rs/canvas'; import { formatItemStackQuantity, generateHexColorForCashStack, toTitleCase } from '@oldschoolgg/toolkit'; import { GEListing, GETransaction } from '@prisma/client'; import * as fs from 'fs/promises'; -import { floor } from 'lodash'; import { GEListingWithTransactions } from './../mahoji/commands/ge'; import { GrandExchange } from './grandExchange'; @@ -13,7 +12,7 @@ function drawTitle(ctx: SKRSContext2D, title: string, canvas: Canvas) { // Draw Page Title ctx.font = '16px RuneScape Bold 12'; const titleWidthPx = ctx.measureText(title); - let titleX = Math.floor(floor(canvas.width) * 0.95 - titleWidthPx.width); + let titleX = Math.floor(Math.floor(canvas.width) * 0.95 - titleWidthPx.width); ctx.fillStyle = '#000000'; fillTextXTimesInCtx(ctx, title, titleX + 1, 22); diff --git a/src/lib/http/routes/commands.ts b/src/lib/http/routes/commands.ts deleted file mode 100644 index 4391392125e..00000000000 --- a/src/lib/http/routes/commands.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Time } from 'e'; -import { ApplicationCommandOptionType } from 'mahoji'; - -import { AbstractCommand } from '../../../mahoji/lib/inhibitors'; -import { allAbstractCommands } from '../../../mahoji/lib/util'; -import { stringMatches } from '../../util'; -import { FastifyServer } from '../types'; - -export const commandsRoute = (server: FastifyServer) => - server.route({ - method: 'GET', - url: '/commands', - async handler(_, reply) { - const mahojiCommands = globalClient.mahojiClient.commands.values; - const commandData = allAbstractCommands(globalClient.mahojiClient) - .filter(c => typeof c.attributes?.description === 'string' && c.attributes.description.length > 1) - .filter(i => !['admin'].includes(i.name)) - .map((cmd: AbstractCommand) => { - const mahojiCommand = mahojiCommands.find(i => stringMatches(i.name, cmd.name)); - const subOptions: string[] = []; - if (mahojiCommand) { - for (const option of mahojiCommand.options) { - if ( - option.type === ApplicationCommandOptionType.SubcommandGroup || - option.type === ApplicationCommandOptionType.Subcommand - ) { - subOptions.push(option.name); - } - } - } - return { - name: cmd.name, - desc: cmd.attributes?.description, - examples: cmd.attributes?.examples, - flags: cmd.attributes?.categoryFlags, - subOptions - }; - }) - .sort((a, b) => a.name.localeCompare(b.name)); - reply.header( - 'Cache-Control', - `public, max-age=${(Time.Minute * 5) / 1000}, s-maxage=${(Time.Minute * 5) / 1000}` - ); - reply.send(commandData); - } - }); diff --git a/src/lib/http/routes/index.ts b/src/lib/http/routes/index.ts index ccceab6eb87..3d81deecfa4 100644 --- a/src/lib/http/routes/index.ts +++ b/src/lib/http/routes/index.ts @@ -1,8 +1,5 @@ import type { FastifyServer } from '../types'; -import { commandsRoute } from './commands'; -import root from './root'; import githubSponsors from './webhooks/githubSponsors'; import { patreonRoute } from './webhooks/patreon'; -export const initRoutes = (server: FastifyServer) => - [root, githubSponsors, commandsRoute, patreonRoute].map(route => route(server)); +export const initRoutes = (server: FastifyServer) => [githubSponsors, patreonRoute].map(route => route(server)); diff --git a/src/lib/http/routes/root.ts b/src/lib/http/routes/root.ts deleted file mode 100644 index 8af2e114107..00000000000 --- a/src/lib/http/routes/root.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { FastifyServer } from '../types'; -import { rateLimit } from '../util'; - -export default (server: FastifyServer) => - server.route({ - method: 'GET', - url: '/', - async handler(request, reply) { - reply.send(`${JSON.stringify(request.headers, null, 4)}\n\n\n${server.printRoutes()}`); - }, - config: { - ...rateLimit(1, '1 minute') - } - }); diff --git a/src/lib/randomEvents.ts b/src/lib/randomEvents.ts index 0b23bd80eb3..8f138c3222f 100644 --- a/src/lib/randomEvents.ts +++ b/src/lib/randomEvents.ts @@ -1,6 +1,6 @@ import { activity_type_enum } from '@prisma/client'; import { randArrItem, roll, Time } from 'e'; -import LRUCache from 'lru-cache'; +import { LRUCache } from 'lru-cache'; import { Bank } from 'oldschooljs'; import LootTable from 'oldschooljs/dist/structures/LootTable'; diff --git a/src/lib/roboChimp.ts b/src/lib/roboChimp.ts index 7b32294dcc6..bc85b0b75be 100644 --- a/src/lib/roboChimp.ts +++ b/src/lib/roboChimp.ts @@ -1,7 +1,7 @@ import { formatOrdinal } from '@oldschoolgg/toolkit'; import { PrismaClient, TriviaQuestion, User } from '@prisma/robochimp'; -import deepEqual from 'deep-equal'; import { calcWhatPercent, round, sumArr } from 'e'; +import deepEqual from 'fast-deep-equal'; import { BOT_TYPE, masteryKey } from './constants'; import { getTotalCl } from './data/Collections'; diff --git a/src/mahoji/commands/admin.ts b/src/mahoji/commands/admin.ts index e0af39ca4aa..a7268472303 100644 --- a/src/mahoji/commands/admin.ts +++ b/src/mahoji/commands/admin.ts @@ -80,8 +80,6 @@ async function unsafeEval({ userID, code }: { userID: string; code: string }) { let thenable = false; // eslint-disable-next-line @typescript-eslint/init-declarations try { - code = `\nconst {Gear} = require('../../lib/structures/Gear')\n${code};`; - code = `\nconst {Bank} = require('oldschooljs');\n${code}`; // eslint-disable-next-line no-eval result = eval(code); syncTime = stopwatch.toString(); diff --git a/src/mahoji/commands/config.ts b/src/mahoji/commands/config.ts index 24aa9e18484..bead1d4ee36 100644 --- a/src/mahoji/commands/config.ts +++ b/src/mahoji/commands/config.ts @@ -489,42 +489,6 @@ async function handlePetMessagesEnable( return 'Disabled Pet Messages in this guild.'; } -async function handleJModCommentsEnable( - user: MUser, - guild: Guild | null, - channelID: string, - choice: 'enable' | 'disable' -) { - if (!guild) return 'This command can only be run in servers.'; - if (!(await hasBanMemberPerms(user.id, guild))) - return "You need to be 'Ban Member' permissions to use this command."; - const cID = channelID.toString(); - const settings = await mahojiGuildSettingsFetch(guild); - - if (choice === 'enable') { - if (guild!.memberCount < 20 && user.perkTier() < PerkTier.Four) { - return 'This server is too small to enable this feature in.'; - } - if (settings.jmodComments === cID) { - return 'JMod Comments are already enabled in this channel.'; - } - await mahojiGuildSettingsUpdate(guild.id, { - jmodComments: cID - }); - if (settings.jmodComments !== null) { - return "JMod Comments are already enabled in another channel, but I've switched them to use this channel."; - } - return 'Enabled JMod Comments in this channel.'; - } - if (settings.jmodComments === null) { - return "JMod Comments aren't enabled, so you can't disable them."; - } - await mahojiGuildSettingsUpdate(guild.id, { - jmodComments: null - }); - return 'Disabled JMod Comments in this channel.'; -} - async function handleCommandEnable( user: MUser, guild: Guild | null, @@ -774,23 +738,6 @@ export const configCommand: OSBMahojiCommand = { } ] }, - { - type: ApplicationCommandOptionType.Subcommand, - name: 'jmod_comments', - description: 'Enable or disable JMod Reddit comments in this server.', - options: [ - { - type: ApplicationCommandOptionType.String, - name: 'choice', - description: 'Enable or disable JMod Reddit comments for this server.', - required: true, - choices: [ - { name: 'Enable', value: 'enable' }, - { name: 'Disable', value: 'disable' } - ] - } - ] - }, { type: ApplicationCommandOptionType.Subcommand, name: 'command', @@ -810,7 +757,7 @@ export const configCommand: OSBMahojiCommand = { { type: ApplicationCommandOptionType.String, name: 'choice', - description: 'Enable or disable JMod Reddit comments for this server.', + description: 'Whether you want to enable or disable this command.', required: true, choices: [ { name: 'Enable', value: 'enable' }, @@ -1195,7 +1142,6 @@ LIMIT 20; server?: { channel?: { choice: 'enable' | 'disable' }; pet_messages?: { choice: 'enable' | 'disable' }; - jmod_comments?: { choice: 'enable' | 'disable' }; command?: { command: string; choice: 'enable' | 'disable' }; }; user?: { @@ -1227,9 +1173,6 @@ LIMIT 20; if (options.server.pet_messages) { return handlePetMessagesEnable(user, guild, channelID, options.server.pet_messages.choice); } - if (options.server.jmod_comments) { - return handleJModCommentsEnable(user, guild, channelID, options.server.jmod_comments.choice); - } if (options.server.command) { return handleCommandEnable(user, guild, options.server.command.command, options.server.command.choice); } diff --git a/src/mahoji/guildSettings.ts b/src/mahoji/guildSettings.ts index 8b7bbc327f1..a4207a55a14 100644 --- a/src/mahoji/guildSettings.ts +++ b/src/mahoji/guildSettings.ts @@ -1,13 +1,10 @@ import { Guild, Prisma } from '@prisma/client'; import { Guild as DJSGuild } from 'discord.js'; -import LRUCache from 'lru-cache'; +import { LRUCache } from 'lru-cache'; import { prisma } from '../lib/settings/prisma'; -type CachedGuild = Pick< - Guild, - 'disabledCommands' | 'id' | 'tweetchannel' | 'jmodComments' | 'petchannel' | 'staffOnlyChannels' ->; +type CachedGuild = Pick; export const untrustedGuildSettingsCache = new LRUCache({ max: 1000 }); export async function mahojiGuildSettingsFetch(guild: string | DJSGuild) { @@ -23,8 +20,6 @@ export async function mahojiGuildSettingsFetch(guild: string | DJSGuild) { select: { disabledCommands: true, id: true, - tweetchannel: true, - jmodComments: true, petchannel: true, staffOnlyChannels: true } diff --git a/src/mahoji/lib/Cooldowns.ts b/src/mahoji/lib/Cooldowns.ts index d4794350e63..7b26d9e722a 100644 --- a/src/mahoji/lib/Cooldowns.ts +++ b/src/mahoji/lib/Cooldowns.ts @@ -1,4 +1,4 @@ -import LRUCache from 'lru-cache'; +import { LRUCache } from 'lru-cache'; import { assert } from '../../lib/util'; diff --git a/src/mahoji/lib/bingo/BingoManager.ts b/src/mahoji/lib/bingo/BingoManager.ts index 98660161d6d..f846a0869db 100644 --- a/src/mahoji/lib/bingo/BingoManager.ts +++ b/src/mahoji/lib/bingo/BingoManager.ts @@ -1,7 +1,7 @@ import { type Bingo, Prisma } from '@prisma/client'; import { ButtonBuilder, ButtonStyle, userMention } from 'discord.js'; import { chunk, noOp, Time } from 'e'; -import { groupBy } from 'lodash'; +import groupBy from 'lodash/groupBy'; import { Bank } from 'oldschooljs'; import { toKMB } from 'oldschooljs/dist/util'; import * as ss from 'simple-statistics'; diff --git a/src/scripts/integration-tests.ts b/src/scripts/integration-tests.ts index 8487aa9f56c..93b0fb6545e 100644 --- a/src/scripts/integration-tests.ts +++ b/src/scripts/integration-tests.ts @@ -13,7 +13,7 @@ async function main() { execSync('dotenv -e .env.test -- prisma db push --schema="./prisma/robochimp.prisma"', { stdio: 'inherit' }); console.log('Building...'); - execSync('yarn prebuild:scripts', { stdio: 'inherit' }); + execSync('yarn pre:build', { stdio: 'inherit' }); execSync('yarn build:esbuild', { stdio: 'inherit' }); console.log('Starting tests...'); diff --git a/src/scripts/postbuild.ts b/src/scripts/postbuild.ts new file mode 100644 index 00000000000..b1221ebdf6c --- /dev/null +++ b/src/scripts/postbuild.ts @@ -0,0 +1,65 @@ +import './no-prisma'; + +import { execSync } from 'node:child_process'; +import { join } from 'node:path'; + +import { stringMatches } from '@oldschoolgg/toolkit'; +import { ApplicationCommandOptionType } from 'discord.js'; +import { writeFileSync } from 'fs'; +import { MahojiClient } from 'mahoji'; + +import { BOT_TYPE } from '../lib/constants'; +import type { AbstractCommand } from '../mahoji/lib/inhibitors'; +import { allAbstractCommands } from '../mahoji/lib/util'; + +async function renderCommands() { + const mahojiClient = new MahojiClient({ + developmentServerID: 'x', + applicationID: 'x', + storeDirs: [join('dist', 'mahoji')], + djsClient: {} as any + }); + await mahojiClient.commands.load(); + const mahojiCommands = mahojiClient.commands.values; + return allAbstractCommands(mahojiClient) + .filter(c => { + const has = typeof c.attributes?.description === 'string' && c.attributes.description.length > 1; + if (!has) { + console.log(`Command ${c.name} has no description/attributes.`); + } + return has; + }) + .filter(i => !['admin', 'testpotato'].includes(i.name)) + .map((cmd: AbstractCommand) => { + const mahojiCommand = mahojiCommands.find(i => stringMatches(i.name, cmd.name)); + if (!mahojiCommand) { + throw new Error(`Could not find mahoji command for ${cmd.name}`); + } + const subOptions: string[] = []; + for (const option of mahojiCommand.options) { + if ( + option.type === ApplicationCommandOptionType.SubcommandGroup || + option.type === ApplicationCommandOptionType.Subcommand + ) { + subOptions.push(option.name); + } + } + return { + name: cmd.name, + desc: cmd.attributes?.description, + examples: cmd.attributes?.examples, + flags: cmd.attributes?.categoryFlags, + subOptions + }; + }) + .sort((a, b) => a.name.localeCompare(b.name)); +} + +export async function main() { + console.log('Postbuild'); + const commands = await renderCommands(); + writeFileSync(`./src/lib/data/${BOT_TYPE.toLowerCase()}.commands.json`, JSON.stringify(commands, null, 4)); + execSync('yarn prettify'); +} + +main().then(() => null); diff --git a/src/scripts/render-creatables-file.ts b/src/scripts/render-creatables-file.ts index 60e5b6fcaf7..9253fd38337 100644 --- a/src/scripts/render-creatables-file.ts +++ b/src/scripts/render-creatables-file.ts @@ -8,6 +8,7 @@ import Createables from '../lib/data/createables'; import { makeTable } from '../lib/util/smallUtils'; export function main() { + console.log('Prebuild'); const headers = ['Item name', 'Input Items', 'Output Items', 'GP Cost']; const rows = Createables.map(i => { return [i.name, `${new Bank(i.inputItems)}`, `${new Bank(i.outputItems)}`, `${i.GPCost ?? 0}`]; diff --git a/tests/unit/http/http.test.ts b/tests/unit/http/http.test.ts index 30552969961..1dfde566176 100644 --- a/tests/unit/http/http.test.ts +++ b/tests/unit/http/http.test.ts @@ -9,16 +9,6 @@ beforeAll(async () => { await app.ready(); }); -test('Commands route', async () => { - const response = await app.inject({ - method: 'GET', - url: '/commands' - }); - - expect(response.statusCode).toBe(200); - expect(response.payload).toEqual('[{"name":"test","desc":"test description","subOptions":[]}]'); -}); - test('github route', async () => { const response = await app.inject({ method: 'POST', @@ -41,19 +31,6 @@ test('github route', async () => { expect(response2.payload).toEqual('{"statusCode":400,"error":"Bad Request","message":"Bad Request"}'); }); -test('root route ratelimiting', async () => { - await app.inject({ - method: 'GET', - url: '/' - }); - const response = await app.inject({ - method: 'GET', - url: '/' - }); - - expect(response.statusCode).toBe(429); -}); - afterAll(async () => { await app.close(); }); diff --git a/yarn.lock b/yarn.lock index 09a2d20d5e7..d56b43a4095 100644 --- a/yarn.lock +++ b/yarn.lock @@ -178,116 +178,351 @@ resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz#a70f4ac11c6a1dfc18b8bbb13284155d933b9537" integrity sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g== +"@esbuild/aix-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" + integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== + +"@esbuild/aix-ppc64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.22.0.tgz#6ff1ec509335ffbaee3fc4a5a11373d6f029b2c4" + integrity sha512-uvQR2crZ/zgzSHDvdygHyNI+ze9zwS8mqz0YtGXotSqvEE0UkYE9s+FZKQNTt1VtT719mfP3vHrUdCpxBNQZhQ== + "@esbuild/android-arm64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz#db1c9202a5bc92ea04c7b6840f1bbe09ebf9e6b9" integrity sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg== +"@esbuild/android-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" + integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== + +"@esbuild/android-arm64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.22.0.tgz#a02ef8650fe5ce17807c9f3229a36d326d2b07ea" + integrity sha512-UKhPb3o2gAB/bfXcl58ZXTn1q2oVu1rEu/bKrCtmm+Nj5MKUbrOwR5WAixE2v+lk0amWuwPvhnPpBRLIGiq7ig== + "@esbuild/android-arm@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.20.2.tgz#3b488c49aee9d491c2c8f98a909b785870d6e995" integrity sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w== +"@esbuild/android-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" + integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== + +"@esbuild/android-arm@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.22.0.tgz#dd26ec407db736eee0eb060195a43aa13f618013" + integrity sha512-PBnyP+r8vJE4ifxsWys9l+Mc2UY/yYZOpX82eoyGISXXb3dRr0M21v+s4fgRKWMFPMSf/iyowqPW/u7ScSUkjQ== + "@esbuild/android-x64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.20.2.tgz#3b1628029e5576249d2b2d766696e50768449f98" integrity sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg== +"@esbuild/android-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" + integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== + +"@esbuild/android-x64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.22.0.tgz#f02771a20be264ccc22478dcc7de8f2bde858af8" + integrity sha512-IjTYtvIrjhR41Ijy2dDPgYjQHWG/x/A4KXYbs1fiU3efpRdoxMChK3oEZV6GPzVEzJqxFgcuBaiX1kwEvWUxSw== + "@esbuild/darwin-arm64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz#6e8517a045ddd86ae30c6608c8475ebc0c4000bb" integrity sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA== +"@esbuild/darwin-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" + integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + +"@esbuild/darwin-arm64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.22.0.tgz#d905f2b951aeba328dd02e3a09f86b5d4e5e6741" + integrity sha512-mqt+Go4y9wRvEz81bhKd9RpHsQR1LwU8Xm6jZRUV/xpM7cIQFbFH6wBCLPTNsdELBvfoHeumud7X78jQQJv2TA== + "@esbuild/darwin-x64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz#90ed098e1f9dd8a9381695b207e1cff45540a0d0" integrity sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA== +"@esbuild/darwin-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" + integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== + +"@esbuild/darwin-x64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.22.0.tgz#d07b4fe501fe9985590285b2790039ed4743f86e" + integrity sha512-vTaTQ9OgYc3VTaWtOE5pSuDT6H3d/qSRFRfSBbnxFfzAvYoB3pqKXA0LEbi/oT8GUOEAutspfRMqPj2ezdFaMw== + "@esbuild/freebsd-arm64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz#d71502d1ee89a1130327e890364666c760a2a911" integrity sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw== +"@esbuild/freebsd-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" + integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== + +"@esbuild/freebsd-arm64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.22.0.tgz#4251e0a14716116f4fa7e22d908f47408b6c2fb5" + integrity sha512-0e1ZgoobJzaGnR4reD7I9rYZ7ttqdh1KPvJWnquUoDJhL0rYwdneeLailBzd2/4g/U5p4e5TIHEWa68NF2hFpQ== + "@esbuild/freebsd-x64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz#aa5ea58d9c1dd9af688b8b6f63ef0d3d60cea53c" integrity sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw== +"@esbuild/freebsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" + integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== + +"@esbuild/freebsd-x64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.22.0.tgz#7dbd35616a71f8a9b61a9435c5a79d87fc0b2f1a" + integrity sha512-BFgyYwlCwRWyPQJtkzqq2p6pJbiiWgp0P9PNf7a5FQ1itKY4czPuOMAlFVItirSmEpRPCeImuwePNScZS0pL5Q== + "@esbuild/linux-arm64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz#055b63725df678379b0f6db9d0fa85463755b2e5" integrity sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A== +"@esbuild/linux-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" + integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== + +"@esbuild/linux-arm64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.22.0.tgz#77cded446dd0c3b723d272e0243b3d9ddb3cb46e" + integrity sha512-V/K2rctCUgC0PCXpN7AqT4hoazXKgIYugFGu/myk2+pfe6jTW2guz/TBwq4cZ7ESqusR/IzkcQaBkcjquuBWsw== + "@esbuild/linux-arm@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz#76b3b98cb1f87936fbc37f073efabad49dcd889c" integrity sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg== +"@esbuild/linux-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" + integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== + +"@esbuild/linux-arm@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.22.0.tgz#6587d3e423e09766ea997229827e292e7c4acd6f" + integrity sha512-KEMWiA9aGuPUD4BH5yjlhElLgaRXe+Eri6gKBoDazoPBTo1BXc/e6IW5FcJO9DoL19FBeCxgONyh95hLDNepIg== + "@esbuild/linux-ia32@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz#c0e5e787c285264e5dfc7a79f04b8b4eefdad7fa" integrity sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig== +"@esbuild/linux-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" + integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== + +"@esbuild/linux-ia32@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.22.0.tgz#2d06d7b4abc443e05a820ff50d4c2d98cc04c22f" + integrity sha512-r2ZZqkOMOrpUhzNwxI7uLAHIDwkfeqmTnrv1cjpL/rjllPWszgqmprd/om9oviKXUBpMqHbXmppvjAYgISb26Q== + "@esbuild/linux-loong64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz#a6184e62bd7cdc63e0c0448b83801001653219c5" integrity sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ== +"@esbuild/linux-loong64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" + integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== + +"@esbuild/linux-loong64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.22.0.tgz#a3e7faabe9a046ac4557bc515ce0981cfe5a6e0f" + integrity sha512-qaowLrV/YOMAL2RfKQ4C/VaDzAuLDuylM2sd/LH+4OFirMl6CuDpRlCq4u49ZBaVV8pkI/Y+hTdiibvQRhojCA== + "@esbuild/linux-mips64el@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz#d08e39ce86f45ef8fc88549d29c62b8acf5649aa" integrity sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA== +"@esbuild/linux-mips64el@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" + integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== + +"@esbuild/linux-mips64el@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.22.0.tgz#3a2877a78f6719e5eed4cfdded5121c5ab9305a4" + integrity sha512-hgrezzjQTRxjkQ5k08J6rtZN5PNnkWx/Rz6Kmj9gnsdCAX1I4Dn4ZPqvFRkXo55Q3pnVQJBwbdtrTO7tMGtyVA== + "@esbuild/linux-ppc64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz#8d252f0b7756ffd6d1cbde5ea67ff8fd20437f20" integrity sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg== +"@esbuild/linux-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" + integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== + +"@esbuild/linux-ppc64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.22.0.tgz#6609478066083e05cc1854a8b272daf62a7e944b" + integrity sha512-ewxg6FLLUio883XgSjfULEmDl3VPv/TYNnRprVAS3QeGFLdCYdx1tIudBcd7n9jIdk82v1Ajov4jx87qW7h9+g== + "@esbuild/linux-riscv64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz#19f6dcdb14409dae607f66ca1181dd4e9db81300" integrity sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg== +"@esbuild/linux-riscv64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" + integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== + +"@esbuild/linux-riscv64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.22.0.tgz#d786a89903cf98e8d34befe6a71c69562bb4ceac" + integrity sha512-Az5XbgSJC2lE8XK8pdcutsf9RgdafWdTpUK/+6uaDdfkviw/B4JCwAfh1qVeRWwOohwdsl4ywZrWBNWxwrPLFg== + "@esbuild/linux-s390x@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz#3c830c90f1a5d7dd1473d5595ea4ebb920988685" integrity sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ== +"@esbuild/linux-s390x@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" + integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== + +"@esbuild/linux-s390x@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.22.0.tgz#a7ab13ae163307ac615dac5ce7f60a6b0a067d59" + integrity sha512-8j4a2ChT9+V34NNNY9c/gMldutaJFmfMacTPq4KfNKwv2fitBCLYjee7c+Vxaha2nUhPK7cXcZpJtJ3+Y7ZdVQ== + "@esbuild/linux-x64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz#86eca35203afc0d9de0694c64ec0ab0a378f6fff" integrity sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw== +"@esbuild/linux-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" + integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== + +"@esbuild/linux-x64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.22.0.tgz#24949de431013354da1d8c29e53299798f8c27ef" + integrity sha512-JUQyOnpbAkkRFOk/AhsEemz5TfWN4FJZxVObUlnlNCbe7QBl61ZNfM4cwBXayQA6laMJMUcqLHaYQHAB6YQ95Q== + "@esbuild/netbsd-x64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz#e771c8eb0e0f6e1877ffd4220036b98aed5915e6" integrity sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ== +"@esbuild/netbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" + integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== + +"@esbuild/netbsd-x64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.22.0.tgz#bc3f51c41eaab89cf5fdb09d0c633affb39cb1a1" + integrity sha512-11PoCoHXo4HFNbLsXuMB6bpMPWGDiw7xETji6COdJss4SQZLvcgNoeSqWtATRm10Jj1uEHiaIk4N0PiN6x4Fcg== + +"@esbuild/openbsd-arm64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.22.0.tgz#7cb42e3a0d3da039d1a4b7ccbd0c19b0f71ae453" + integrity sha512-Ezlhu/YyITmXwKSB+Zu/QqD7cxrjrpiw85cc0Rbd3AWr2wsgp+dWbWOE8MqHaLW9NKMZvuL0DhbJbvzR7F6Zvg== + "@esbuild/openbsd-x64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz#9a795ae4b4e37e674f0f4d716f3e226dd7c39baf" integrity sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ== +"@esbuild/openbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" + integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== + +"@esbuild/openbsd-x64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.22.0.tgz#194aa9915323962e9ea66c5a13ff3e1db272a683" + integrity sha512-ufjdW5tFJGUjlH9j/5cCE9lrwRffyZh+T4vYvoDKoYsC6IXbwaFeV/ENxeNXcxotF0P8CDzoICXVSbJaGBhkrw== + "@esbuild/sunos-x64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz#7df23b61a497b8ac189def6e25a95673caedb03f" integrity sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w== +"@esbuild/sunos-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" + integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== + +"@esbuild/sunos-x64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.22.0.tgz#2be9d2459ae181ebedb6470e4469349a27c4f060" + integrity sha512-zY6ly/AoSmKnmNTowDJsK5ehra153/5ZhqxNLfq9NRsTTltetr+yHHcQ4RW7QDqw4JC8A1uC1YmeSfK9NRcK1w== + "@esbuild/win32-arm64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz#f1ae5abf9ca052ae11c1bc806fb4c0f519bacf90" integrity sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ== +"@esbuild/win32-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" + integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== + +"@esbuild/win32-arm64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.22.0.tgz#6b4224f2d049c26f37026904210a4293e34c2747" + integrity sha512-Kml5F7tv/1Maam0pbbCrvkk9vj046dPej30kFzlhXnhuCtYYBP6FGy/cLbc5yUT1lkZznGLf2OvuvmLjscO5rw== + "@esbuild/win32-ia32@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz#241fe62c34d8e8461cd708277813e1d0ba55ce23" integrity sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ== +"@esbuild/win32-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" + integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== + +"@esbuild/win32-ia32@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.22.0.tgz#4a1184f6fd4a7594c4f1e68b1e649248534f7832" + integrity sha512-IOgwn+mYTM3RrcydP4Og5IpXh+ftN8oF+HELTXSmbWBlujuci4Qa3DTeO+LEErceisI7KUSfEIiX+WOUlpELkw== + "@esbuild/win32-x64@0.20.2": version "0.20.2" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz#9c907b21e30a52db959ba4f80bb01a0cc403d5cc" integrity sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ== +"@esbuild/win32-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" + integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== + +"@esbuild/win32-x64@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.22.0.tgz#4b83e9449a205e7d94d5368035450fc1680fe525" + integrity sha512-4bDHJrk2WHBXJPhy1y80X7/5b5iZTZP3LGcKIlAP1J+KqZ4zQAPMLEzftGyjjfcKbA4JDlPt/+2R/F1ZTeRgrw== + "@eslint-community/eslint-utils@^4.2.0": version "4.3.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz#a556790523a351b4e47e9d385f47265eaaf9780a" @@ -501,6 +736,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" @@ -511,18 +751,18 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/sourcemap-codec@^1.4.15": +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@jridgewell/trace-mapping@^0.3.12": - version "0.3.17" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" - integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== +"@jridgewell/trace-mapping@^0.3.23": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" "@jridgewell/trace-mapping@^0.3.9": version "0.3.18" @@ -532,65 +772,65 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@napi-rs/canvas-android-arm64@0.1.38": - version "0.1.38" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.38.tgz#2612b2784831ea2d03ac269779eec72325cf5e6e" - integrity sha512-CQ87kZhrg98Ou7Zu4/AytMu96FOh9zBELmG4SoyDCZSPsMJvYO0txZ3K2ptddElFU04D4xiCsQhrcPmCUhWNeA== - -"@napi-rs/canvas-darwin-arm64@0.1.38": - version "0.1.38" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.38.tgz#89a97509b3babd8e2296ccdbcbddb196789b3299" - integrity sha512-bUTqq4xtT6FCV/U1bgXI4QqqyIhS/IbJ5BzTvUBrqrdomBEh0R4aapL+nJ+fybHinyYqcKrVSiSf4LkH/QIR0A== - -"@napi-rs/canvas-darwin-x64@0.1.38": - version "0.1.38" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.38.tgz#31886fe2b7f23a14af82e78bb0971c1fd1d511ef" - integrity sha512-w52CHU5uBDQCll9rKtvgSQGcds1jymZ91MR794dRnaTW52L/nV1hi6/2Depugh21IwC1KwdxsiDvHycS4rbqRQ== - -"@napi-rs/canvas-linux-arm-gnueabihf@0.1.38": - version "0.1.38" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.38.tgz#b1b2cf3acf684905fb076a258ca1f8a1e2724c2d" - integrity sha512-szCGMXrLR3VmhCxbfiYW0AkxjtE3yETFPsTq4/HX9zUSxNPvzJgjvmwR5Wf9eEcQQJSTDHL3ux+gLv4nR0Bzrw== - -"@napi-rs/canvas-linux-arm64-gnu@0.1.38": - version "0.1.38" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.38.tgz#159840545e8b83d0342a55b43a7cd309166c4301" - integrity sha512-Tx+0Vdq6KJs0h/qyk+K5puVEqYGqX4xiJ/0r+Gz9kYJjkAnjZt+omPfFah/X4Zw3IbGg+4ini4YMwiIOy5Ih0w== - -"@napi-rs/canvas-linux-arm64-musl@0.1.38": - version "0.1.38" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.38.tgz#51cf8e0b4cb2dab1fdac0520d9596fc8381e9153" - integrity sha512-/2R8ORGrmvYjkWUs3IsKs96NjDVPGZ06TLTMStvqyQVsAdwhd39kAuzReuR29U7apYuzlaB57yEqk3gMZnsm1A== - -"@napi-rs/canvas-linux-x64-gnu@0.1.38": - version "0.1.38" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.38.tgz#4c0062fb968c61e4f4b43cff4cf79eaabf47fe7b" - integrity sha512-3pV7NQisKG+4ZqNQYQoFHVcaOz295eEJp3ORZv8mS4fi3Epppq6zsPsBnJfEj4iHvyjWHajePQ/tqbAXJsTiTQ== - -"@napi-rs/canvas-linux-x64-musl@0.1.38": - version "0.1.38" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.38.tgz#4d848384d089db1b65e0b77a99079afebe316ed9" - integrity sha512-++6F5YRPrzXBKIxoseC/Gnt9h8+wxELKYlTrTgN02/kxLMJWjkgZkv1eeiyKuQy1El5640liYts74mQ3m3zAAw== - -"@napi-rs/canvas-win32-x64-msvc@0.1.38": - version "0.1.38" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.38.tgz#723eee490ff0a705023df7efc0c699b57b388734" - integrity sha512-9H+Oh6sEjD0Xa02fJXNdrZBGWZ/AEbB9/oHLLW5O+jZiPvC64Nk2bVwZcpfeEU1xypJWVKPA4+kLmwSU8cmopA== - -"@napi-rs/canvas@0.1.38": - version "0.1.38" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas/-/canvas-0.1.38.tgz#66335bc7201917dfa2679e5b4bc889e7cf844c3a" - integrity sha512-Cjq7BU9kvgFv7MMZcXy2uEEpOJFkbmCyepIXWKQA37lp5Bi1db75eulYDUGLosh5MQwM7478mg+SqLXpJ1UPwA== +"@napi-rs/canvas-android-arm64@0.1.53": + version "0.1.53" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.53.tgz#9075dacd35b484d52098eb86abf8b758d29540c2" + integrity sha512-2YhxfVsZguATlRWE0fZdTx35SE9+r5D7HV5GPNDataZOKmHf+zZ5//dspuuBSbOriQdoicaFrgXKCUqI0pK3WQ== + +"@napi-rs/canvas-darwin-arm64@0.1.53": + version "0.1.53" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.53.tgz#3458ebbdd60eb3972dd296f716cd1ab37f17a133" + integrity sha512-ls+CWLMusf4RAGo5BvIIzA6dNcc0elwVp6LKjHfQECHA8KKmvdB58YuE5BQcTlb2rzk0SEKtBC/Th3NI2oNdfg== + +"@napi-rs/canvas-darwin-x64@0.1.53": + version "0.1.53" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.53.tgz#331ac6046b74a19d2cf4ce2e47ae8c6bded61ba3" + integrity sha512-ZAgcoCH5+5OKS2P8Lxx+jbkAPKkyLD2x6OvSrHg1U6ppdxmLA+CkJlRl8w45HCXwuyIiP7OeymECRtiNYTwznQ== + +"@napi-rs/canvas-linux-arm-gnueabihf@0.1.53": + version "0.1.53" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.53.tgz#658a5a20a9d7b38728d63981d8b194456fdab71f" + integrity sha512-p9km/3C/loDxu3AvA8/vtpIS1BGMd/Ehkl2Iu/v/Gw8N/KUIt3HUvTS7AKApyVE28bxTfq96wJQjtcT8jzDncw== + +"@napi-rs/canvas-linux-arm64-gnu@0.1.53": + version "0.1.53" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.53.tgz#0537fe5efff6d0d8700dbed5288d22f3f4f5f137" + integrity sha512-QKK+sykEiYwjwd+ogyLcpcnH38DNZ8KViBlnfEpoGA2Wa+21/cWQKfMxnbgb/rbvm5tazJinZcihFvH577WQ5g== + +"@napi-rs/canvas-linux-arm64-musl@0.1.53": + version "0.1.53" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.53.tgz#5325a9cc319f6fb1f1d012381d67ac026c8c71ca" + integrity sha512-2N41U0X8RnrTKzpTtPv1ozlYkJtPsUdbfF3uP/KEd/BsULGd8Y8ghkGMS6CM+821au4ex0dPrWOOdT9wC1rSqQ== + +"@napi-rs/canvas-linux-x64-gnu@0.1.53": + version "0.1.53" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.53.tgz#5b4e734f0086b5efaf2f001b7916db8a12e16d92" + integrity sha512-7XjuTvDKCODtf/vMwF43VGDrjfgwYKgS91ggdcX3UrJaBYWyWu/+eqNvNj+zdXSe/0x+YOjf5jG4m8xIXdBMQA== + +"@napi-rs/canvas-linux-x64-musl@0.1.53": + version "0.1.53" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.53.tgz#b201b5042238764cfc1d5a716a225aedeca4b340" + integrity sha512-970WEvB8vmj+uxvgdBZ+AGFV7uq9GJhXrqG5PGQ5lWciHX0P0d/OhS2F7TITgFR0LsKDQZ7XQgzMxsYOfwZ0FQ== + +"@napi-rs/canvas-win32-x64-msvc@0.1.53": + version "0.1.53" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.53.tgz#ae6238466864967ce8deff706be71e7caa92fa11" + integrity sha512-rLFQCSJaWg/sv54Aap9nAhaodi4Vyb4un50EgW+PNkk8icMziU6KLRKirGBdQr9ZdxnshAPeQXD1g2ArStujKA== + +"@napi-rs/canvas@^0.1.53": + version "0.1.53" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas/-/canvas-0.1.53.tgz#ea7973ff782122ffd75eacc63eae9b1b5bf97d50" + integrity sha512-XsEZi97+kKykmAiPpY+IpZoHxJY1srqFZp8jDt1/RySzC0kB0iZYt/VMIFqQKpLCARZjD7SOAz2AULtwYlesCA== optionalDependencies: - "@napi-rs/canvas-android-arm64" "0.1.38" - "@napi-rs/canvas-darwin-arm64" "0.1.38" - "@napi-rs/canvas-darwin-x64" "0.1.38" - "@napi-rs/canvas-linux-arm-gnueabihf" "0.1.38" - "@napi-rs/canvas-linux-arm64-gnu" "0.1.38" - "@napi-rs/canvas-linux-arm64-musl" "0.1.38" - "@napi-rs/canvas-linux-x64-gnu" "0.1.38" - "@napi-rs/canvas-linux-x64-musl" "0.1.38" - "@napi-rs/canvas-win32-x64-msvc" "0.1.38" + "@napi-rs/canvas-android-arm64" "0.1.53" + "@napi-rs/canvas-darwin-arm64" "0.1.53" + "@napi-rs/canvas-darwin-x64" "0.1.53" + "@napi-rs/canvas-linux-arm-gnueabihf" "0.1.53" + "@napi-rs/canvas-linux-arm64-gnu" "0.1.53" + "@napi-rs/canvas-linux-arm64-musl" "0.1.53" + "@napi-rs/canvas-linux-x64-gnu" "0.1.53" + "@napi-rs/canvas-linux-x64-musl" "0.1.53" + "@napi-rs/canvas-win32-x64-msvc" "0.1.53" "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -694,51 +934,288 @@ resolved "https://registry.yarnpkg.com/@oldschoolgg/ts-config/-/ts-config-0.0.1.tgz#53c6244d393548027076537820bbce92ed33e47e" integrity sha512-POoKxMiI10iNjWkk/0sZwxCuXM79dI94tv6Y05KOsLLHL5eVHPZQa+A2gY2pzPf5isDkHwla7i3iOsQglhcKdQ== +"@opentelemetry/api-logs@0.52.1": + version "0.52.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz#52906375da4d64c206b0c4cb8ffa209214654ecc" + integrity sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A== + dependencies: + "@opentelemetry/api" "^1.0.0" + +"@opentelemetry/api@^1.0.0", "@opentelemetry/api@^1.6.0", "@opentelemetry/api@^1.8", "@opentelemetry/api@^1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" + integrity sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg== + +"@opentelemetry/context-async-hooks@^1.25.1": + version "1.25.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz#810bff2fcab84ec51f4684aff2d21f6c057d9e73" + integrity sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ== + +"@opentelemetry/core@1.25.1", "@opentelemetry/core@^1.1.0", "@opentelemetry/core@^1.25.1", "@opentelemetry/core@^1.8.0": + version "1.25.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.25.1.tgz#ff667d939d128adfc7c793edae2f6bca177f829d" + integrity sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ== + dependencies: + "@opentelemetry/semantic-conventions" "1.25.1" + +"@opentelemetry/instrumentation-connect@0.37.0": + version "0.37.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.37.0.tgz#ab1bc3d33058bfc647d4b158295b589d11d619df" + integrity sha512-SeQktDIH5rNzjiEiazWiJAIXkmnLOnNV7wwHpahrqE0Ph+Z3heqMfxRtoMtbdJSIYLfcNZYO51AjxZ00IXufdw== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/semantic-conventions" "^1.22.0" + "@types/connect" "3.4.36" + +"@opentelemetry/instrumentation-express@0.40.1": + version "0.40.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-express/-/instrumentation-express-0.40.1.tgz#b4c31a352691b060b330e4c028a8ef5472b89e27" + integrity sha512-+RKMvVe2zw3kIXRup9c1jFu3T4d0fs5aKy015TpiMyoCKX1UMu3Z0lfgYtuyiSTANvg5hZnDbWmQmqSPj9VTvg== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/semantic-conventions" "^1.22.0" + +"@opentelemetry/instrumentation-fastify@0.37.0": + version "0.37.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.37.0.tgz#c9537050d222d89ad4c3930b7b21a58016206f6d" + integrity sha512-WRjwzNZgupSzbEYvo9s+QuHJRqZJjVdNxSEpGBwWK8RKLlHGwGVAu0gcc2gPamJWUJsGqPGvahAPWM18ZkWj6A== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/semantic-conventions" "^1.22.0" + +"@opentelemetry/instrumentation-graphql@0.41.0": + version "0.41.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.41.0.tgz#b3f1c7e0bb18400b1336f781f209f6b73608bd89" + integrity sha512-R/gXeljgIhaRDKquVkKYT5QHPnFouM8ooyePZEP0kqyaVAedtR1V7NfAUJbxfTG5fBQa5wdmLjvu63+tzRXZCA== + dependencies: + "@opentelemetry/instrumentation" "^0.52.0" + +"@opentelemetry/instrumentation-hapi@0.39.0": + version "0.39.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.39.0.tgz#c6a43440baac714aba57d12ee363b72a02378eed" + integrity sha512-ik2nA9Yj2s2ay+aNY+tJsKCsEx6Tsc2g/MK0iWBW5tibwrWKTy1pdVt5sB3kd5Gkimqj23UV5+FH2JFcQLeKug== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/semantic-conventions" "^1.22.0" + +"@opentelemetry/instrumentation-http@0.52.1": + version "0.52.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.52.1.tgz#12061501601838d1c912f9c29bdd40a13a7e44cf" + integrity sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q== + dependencies: + "@opentelemetry/core" "1.25.1" + "@opentelemetry/instrumentation" "0.52.1" + "@opentelemetry/semantic-conventions" "1.25.1" + semver "^7.5.2" + +"@opentelemetry/instrumentation-ioredis@0.41.0": + version "0.41.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.41.0.tgz#41b60babdce893df7466b13a8896a71c81a80813" + integrity sha512-rxiLloU8VyeJGm5j2fZS8ShVdB82n7VNP8wTwfUQqDwRfHCnkzGr+buKoxuhGD91gtwJ91RHkjHA1Eg6RqsUTg== + dependencies: + "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/redis-common" "^0.36.2" + "@opentelemetry/semantic-conventions" "^1.23.0" + +"@opentelemetry/instrumentation-koa@0.41.0": + version "0.41.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.41.0.tgz#31d75ebc4c53c9c902f7ef3f73e52d575fce9628" + integrity sha512-mbPnDt7ELvpM2S0vixYUsde7122lgegLOJQxx8iJQbB8YHal/xnTh9v7IfArSVzIDo+E+080hxZyUZD4boOWkw== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/semantic-conventions" "^1.22.0" + "@types/koa" "2.14.0" + "@types/koa__router" "12.0.3" + +"@opentelemetry/instrumentation-mongodb@0.45.0": + version "0.45.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.45.0.tgz#d6373e30f3e83eba87f7e6e2ea72c1351467d6b5" + integrity sha512-xnZP9+ayeB1JJyNE9cIiwhOJTzNEsRhXVdLgfzmrs48Chhhk026mQdM5CITfyXSCfN73FGAIB8d91+pflJEfWQ== + dependencies: + "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/sdk-metrics" "^1.9.1" + "@opentelemetry/semantic-conventions" "^1.22.0" + +"@opentelemetry/instrumentation-mongoose@0.39.0": + version "0.39.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.39.0.tgz#2d5070bb0838769b8dd099b6402f42e1269f527a" + integrity sha512-J1r66A7zJklPPhMtrFOO7/Ud2p0Pv5u8+r23Cd1JUH6fYPmftNJVsLp2urAt6PHK4jVqpP/YegN8wzjJ2mZNPQ== + dependencies: + "@opentelemetry/core" "^1.8.0" + "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/semantic-conventions" "^1.22.0" + +"@opentelemetry/instrumentation-mysql2@0.39.0": + version "0.39.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.39.0.tgz#1719441f58e3f3418c2c3a7b15b48c187d8e3f90" + integrity sha512-Iypuq2z6TCfriAXCIZjRq8GTFCKhQv5SpXbmI+e60rYdXw8NHtMH4NXcGF0eKTuoCsC59IYSTUvDQYDKReaszA== + dependencies: + "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/sql-common" "^0.40.1" + +"@opentelemetry/instrumentation-mysql@0.39.0": + version "0.39.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.39.0.tgz#b55afe5b1249363f42c6092529466b057297ab94" + integrity sha512-8snHPh83rhrDf31v9Kq0Nf+ts8hdr7NguuszRqZomZBHgE0+UyXZSkXHAAFZoBPPRMGyM68uaFE5hVtFl+wOcA== + dependencies: + "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/semantic-conventions" "^1.22.0" + "@types/mysql" "2.15.22" + +"@opentelemetry/instrumentation-nestjs-core@0.38.0": + version "0.38.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.38.0.tgz#d4296936723f1dfbd11747a84a87d17a3da0bc74" + integrity sha512-M381Df1dM8aqihZz2yK+ugvMFK5vlHG/835dc67Sx2hH4pQEQYDA2PpFPTgc9AYYOydQaj7ClFQunESimjXDgg== + dependencies: + "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/semantic-conventions" "^1.23.0" + +"@opentelemetry/instrumentation-pg@0.42.0": + version "0.42.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.42.0.tgz#a73de6c057b4a8b99c964d2bbf2fdad304284be9" + integrity sha512-sjgcM8CswYy8zxHgXv4RAZ09DlYhQ+9TdlourUs63Df/ek5RrB1ZbjznqW7PB6c3TyJJmX6AVtPTjAsROovEjA== + dependencies: + "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/semantic-conventions" "^1.22.0" + "@opentelemetry/sql-common" "^0.40.1" + "@types/pg" "8.6.1" + "@types/pg-pool" "2.0.4" + +"@opentelemetry/instrumentation-redis-4@0.40.0": + version "0.40.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.40.0.tgz#4a1bc9bebfb869de8d982b1a1a5b550bdb68d15b" + integrity sha512-0ieQYJb6yl35kXA75LQUPhHtGjtQU9L85KlWa7d4ohBbk/iQKZ3X3CFl5jC5vNMq/GGPB3+w3IxNvALlHtrp7A== + dependencies: + "@opentelemetry/instrumentation" "^0.52.0" + "@opentelemetry/redis-common" "^0.36.2" + "@opentelemetry/semantic-conventions" "^1.22.0" + +"@opentelemetry/instrumentation@0.52.1", "@opentelemetry/instrumentation@^0.49 || ^0.50 || ^0.51 || ^0.52.0", "@opentelemetry/instrumentation@^0.52.0", "@opentelemetry/instrumentation@^0.52.1": + version "0.52.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz#2e7e46a38bd7afbf03cf688c862b0b43418b7f48" + integrity sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw== + dependencies: + "@opentelemetry/api-logs" "0.52.1" + "@types/shimmer" "^1.0.2" + import-in-the-middle "^1.8.1" + require-in-the-middle "^7.1.1" + semver "^7.5.2" + shimmer "^1.2.1" + +"@opentelemetry/instrumentation@^0.43.0": + version "0.43.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.43.0.tgz#749521415df03396f969bf42341fcb4acd2e9c7b" + integrity sha512-S1uHE+sxaepgp+t8lvIDuRgyjJWisAb733198kwQTUc9ZtYQ2V2gmyCtR1x21ePGVLoMiX/NWY7WA290hwkjJQ== + dependencies: + "@types/shimmer" "^1.0.2" + import-in-the-middle "1.4.2" + require-in-the-middle "^7.1.1" + semver "^7.5.2" + shimmer "^1.2.1" + +"@opentelemetry/redis-common@^0.36.2": + version "0.36.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/redis-common/-/redis-common-0.36.2.tgz#906ac8e4d804d4109f3ebd5c224ac988276fdc47" + integrity sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g== + +"@opentelemetry/resources@1.25.1", "@opentelemetry/resources@^1.25.1": + version "1.25.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.25.1.tgz#bb9a674af25a1a6c30840b755bc69da2796fefbb" + integrity sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ== + dependencies: + "@opentelemetry/core" "1.25.1" + "@opentelemetry/semantic-conventions" "1.25.1" + +"@opentelemetry/sdk-metrics@^1.9.1": + version "1.25.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz#50c985ec15557a9654334e7fa1018dc47a8a56b7" + integrity sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q== + dependencies: + "@opentelemetry/core" "1.25.1" + "@opentelemetry/resources" "1.25.1" + lodash.merge "^4.6.2" + +"@opentelemetry/sdk-trace-base@^1.22", "@opentelemetry/sdk-trace-base@^1.25.1": + version "1.25.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz#cbc1e60af255655d2020aa14cde17b37bd13df37" + integrity sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw== + dependencies: + "@opentelemetry/core" "1.25.1" + "@opentelemetry/resources" "1.25.1" + "@opentelemetry/semantic-conventions" "1.25.1" + +"@opentelemetry/semantic-conventions@1.25.1", "@opentelemetry/semantic-conventions@^1.17.0", "@opentelemetry/semantic-conventions@^1.22.0", "@opentelemetry/semantic-conventions@^1.23.0", "@opentelemetry/semantic-conventions@^1.25.1": + version "1.25.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz#0deecb386197c5e9c2c28f2f89f51fb8ae9f145e" + integrity sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ== + +"@opentelemetry/sql-common@^0.40.1": + version "0.40.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/sql-common/-/sql-common-0.40.1.tgz#93fbc48d8017449f5b3c3274f2268a08af2b83b6" + integrity sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg== + dependencies: + "@opentelemetry/core" "^1.1.0" + "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== "@prisma/client@^5.13.0": - version "5.13.0" - resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.13.0.tgz#b9f1d0983d714e982675201d8222a9ecb4bdad4a" - integrity sha512-uYdfpPncbZ/syJyiYBwGZS8Gt1PTNoErNYMuqHDa2r30rNSFtgTA/LXsSk55R7pdRTMi5pHkeP9B14K6nHmwkg== - -"@prisma/debug@5.13.0": - version "5.13.0" - resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.13.0.tgz#d88b0f6fafa0c216e20e284ed9fc30f1cbe45786" - integrity sha512-699iqlEvzyCj9ETrXhs8o8wQc/eVW+FigSsHpiskSFydhjVuwTJEfj/nIYqTaWFYuxiWQRfm3r01meuW97SZaQ== - -"@prisma/engines-version@5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b": - version "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b" - resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b.tgz#a72a4fb83ba1fd01ad45f795aa55168f60d34723" - integrity sha512-AyUuhahTINGn8auyqYdmxsN+qn0mw3eg+uhkp8zwknXYIqoT3bChG4RqNY/nfDkPvzWAPBa9mrDyBeOnWSgO6A== - -"@prisma/engines@5.13.0": - version "5.13.0" - resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.13.0.tgz#8994ebf7b4e35aee7746a8465ec22738379bcab6" - integrity sha512-hIFLm4H1boj6CBZx55P4xKby9jgDTeDG0Jj3iXtwaaHmlD5JmiDkZhh8+DYWkTGchu+rRF36AVROLnk0oaqhHw== - dependencies: - "@prisma/debug" "5.13.0" - "@prisma/engines-version" "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b" - "@prisma/fetch-engine" "5.13.0" - "@prisma/get-platform" "5.13.0" - -"@prisma/fetch-engine@5.13.0": - version "5.13.0" - resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.13.0.tgz#9b6945c7b38bb59e840f8905b20ea7a3d059ca55" - integrity sha512-Yh4W+t6YKyqgcSEB3odBXt7QyVSm0OQlBSldQF2SNXtmOgMX8D7PF/fvH6E6qBCpjB/yeJLy/FfwfFijoHI6sA== - dependencies: - "@prisma/debug" "5.13.0" - "@prisma/engines-version" "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b" - "@prisma/get-platform" "5.13.0" - -"@prisma/get-platform@5.13.0": - version "5.13.0" - resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.13.0.tgz#99ef909a52b9d79b64d72d2d3d8210c4892b6572" - integrity sha512-B/WrQwYTzwr7qCLifQzYOmQhZcFmIFhR81xC45gweInSUn2hTEbfKUPd2keAog+y5WI5xLAFNJ3wkXplvSVkSw== - dependencies: - "@prisma/debug" "5.13.0" + version "5.16.1" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.16.1.tgz#65c5649b4701c097e7fa943c91a3140ce8bf053d" + integrity sha512-wM9SKQjF0qLxdnOZIVAIMKiz6Hu7vDt4FFAih85K1dk/Rr2mdahy6d3QP41K62N9O0DJJA//gUDA3Mp49xsKIg== + +"@prisma/debug@5.16.1": + version "5.16.1" + resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.16.1.tgz#4887a57a0973fb732a60c30dc48de97bf1eefd7e" + integrity sha512-JsNgZAg6BD9RInLSrg7ZYzo11N7cVvYArq3fHGSD89HSgtN0VDdjV6bib7YddbcO6snzjchTiLfjeTqBjtArVQ== + +"@prisma/engines-version@5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303": + version "5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303.tgz#63ceebefb7daa1eb17f250cad75d35999a50ee1b" + integrity sha512-HkT2WbfmFZ9WUPyuJHhkiADxazHg8Y4gByrTSVeb3OikP6tjQ7txtSUGu9OBOBH0C13dPKN2qqH12xKtHu/Hiw== + +"@prisma/engines@5.16.1": + version "5.16.1" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.16.1.tgz#a14e5d08d34241ed1f1bb7d11ed44eacb37b6fc6" + integrity sha512-KkyF3eIUtBIyp5A/rJHCtwQO18OjpGgx18PzjyGcJDY/+vNgaVyuVd+TgwBgeq6NLdd1XMwRCI+58vinHsAdfA== + dependencies: + "@prisma/debug" "5.16.1" + "@prisma/engines-version" "5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303" + "@prisma/fetch-engine" "5.16.1" + "@prisma/get-platform" "5.16.1" + +"@prisma/fetch-engine@5.16.1": + version "5.16.1" + resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.16.1.tgz#506a034eb23222af27ba635eb48c63df0ba7fc14" + integrity sha512-oOkjaPU1lhcA/Rvr4GVfd1NLJBwExgNBE36Ueq7dr71kTMwy++a3U3oLd2ZwrV9dj9xoP6LjCcky799D9nEt4w== + dependencies: + "@prisma/debug" "5.16.1" + "@prisma/engines-version" "5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303" + "@prisma/get-platform" "5.16.1" + +"@prisma/get-platform@5.16.1": + version "5.16.1" + resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.16.1.tgz#613197c58acaafd5142d48a11f4df45a8f26a9e9" + integrity sha512-R4IKnWnMkR2nUAbU5gjrPehdQYUUd7RENFD2/D+xXTNhcqczp0N+WEGQ3ViyI3+6mtVcjjNIMdnUTNyu3GxIgA== + dependencies: + "@prisma/debug" "5.16.1" + +"@prisma/instrumentation@5.16.0": + version "5.16.0" + resolved "https://registry.yarnpkg.com/@prisma/instrumentation/-/instrumentation-5.16.0.tgz#ee50f851945364c43e4067b84fe4071235ca3dd2" + integrity sha512-MVzNRW2ikWvVNnMIEgQMcwWxpFD+XF2U2h0Qz7MjutRqJxrhWexWV2aSi2OXRaU8UL5wzWw7pnjdKUzYhWauLg== + dependencies: + "@opentelemetry/api" "^1.8" + "@opentelemetry/instrumentation" "^0.49 || ^0.50 || ^0.51 || ^0.52.0" + "@opentelemetry/sdk-trace-base" "^1.22" "@rollup/rollup-android-arm-eabi@4.14.2": version "4.14.2" @@ -857,77 +1334,155 @@ resolved "https://registry.yarnpkg.com/@sapphire/utilities/-/utilities-3.3.0.tgz#62ff0a52cd86bd6169a94b2f217d72da6772a3cd" integrity sha512-wWESfB03elALhci3GjcacRh8pnK89Qe5AEKCQplKyTCKabWl64SAFw52hQBth2fMmJStgK1fr87aGhRZAB8DNA== -"@sentry-internal/tracing@7.113.0": - version "7.113.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.113.0.tgz#936f23205ab53be62f1753b923eddc243cefde86" - integrity sha512-8MDnYENRMnEfQjvN4gkFYFaaBSiMFSU/6SQZfY9pLI3V105z6JQ4D0PGMAUVowXilwNZVpKNYohE7XByuhEC7Q== +"@sentry/core@8.13.0": + version "8.13.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.13.0.tgz#5b2a83402013b828bf2c49c82e751ca490f8669a" + integrity sha512-N9Qg4ZGxZWp8eb2eUUHVVKgjBLtFIjS805nG92s6yJmkvOpKm6mLtcUaT/iDf3Hta6nG+xRkhbE3r+Z4cbXG8w== + dependencies: + "@sentry/types" "8.13.0" + "@sentry/utils" "8.13.0" + +"@sentry/node@^8.13.0": + version "8.13.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-8.13.0.tgz#5c41099b755a7c1f422c72ccbb5cbd93d6ecae94" + integrity sha512-OeZ7K90RhyxfwfreerIi4cszzHrPRRH36STJno2+p3sIGbG5VScOccqXzYEOAqHpByxnti4KQN34BLAT2BFOEA== + dependencies: + "@opentelemetry/api" "^1.9.0" + "@opentelemetry/context-async-hooks" "^1.25.1" + "@opentelemetry/core" "^1.25.1" + "@opentelemetry/instrumentation" "^0.52.1" + "@opentelemetry/instrumentation-connect" "0.37.0" + "@opentelemetry/instrumentation-express" "0.40.1" + "@opentelemetry/instrumentation-fastify" "0.37.0" + "@opentelemetry/instrumentation-graphql" "0.41.0" + "@opentelemetry/instrumentation-hapi" "0.39.0" + "@opentelemetry/instrumentation-http" "0.52.1" + "@opentelemetry/instrumentation-ioredis" "0.41.0" + "@opentelemetry/instrumentation-koa" "0.41.0" + "@opentelemetry/instrumentation-mongodb" "0.45.0" + "@opentelemetry/instrumentation-mongoose" "0.39.0" + "@opentelemetry/instrumentation-mysql" "0.39.0" + "@opentelemetry/instrumentation-mysql2" "0.39.0" + "@opentelemetry/instrumentation-nestjs-core" "0.38.0" + "@opentelemetry/instrumentation-pg" "0.42.0" + "@opentelemetry/instrumentation-redis-4" "0.40.0" + "@opentelemetry/resources" "^1.25.1" + "@opentelemetry/sdk-trace-base" "^1.25.1" + "@opentelemetry/semantic-conventions" "^1.25.1" + "@prisma/instrumentation" "5.16.0" + "@sentry/core" "8.13.0" + "@sentry/opentelemetry" "8.13.0" + "@sentry/types" "8.13.0" + "@sentry/utils" "8.13.0" + optionalDependencies: + opentelemetry-instrumentation-fetch-node "1.2.0" + +"@sentry/opentelemetry@8.13.0": + version "8.13.0" + resolved "https://registry.yarnpkg.com/@sentry/opentelemetry/-/opentelemetry-8.13.0.tgz#2fb4910b10f8af67749c41f636bf3610c2690203" + integrity sha512-NYn/HNE/SxFXe8pfnxJknhrrRzYRMHNssCoi5M1CeR5G7F2BGxxVmaGsd8j0WyTCpUS4i97G4vhYtDGxHvWN6w== dependencies: - "@sentry/core" "7.113.0" - "@sentry/types" "7.113.0" - "@sentry/utils" "7.113.0" + "@sentry/core" "8.13.0" + "@sentry/types" "8.13.0" + "@sentry/utils" "8.13.0" + +"@sentry/types@8.13.0": + version "8.13.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.13.0.tgz#0753700af93592b65272e63c396b3c0202dd3105" + integrity sha512-r63s/H5gvQnQM9tTGBXz2xErUbxZALh4e2Lg/1aHj4zIvGLBjA2z5qWsh6TEZYbpmgAyGShLDr6+rWeUVf9yBQ== -"@sentry/core@7.113.0": - version "7.113.0" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.113.0.tgz#84307eabf03ece9304894ad24ee15581a220c5c7" - integrity sha512-pg75y3C5PG2+ur27A0Re37YTCEnX0liiEU7EOxWDGutH17x3ySwlYqLQmZsFZTSnvzv7t3MGsNZ8nT5O0746YA== +"@sentry/utils@8.13.0": + version "8.13.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.13.0.tgz#b2cce61705ea95a639db932f000247bddf13aa16" + integrity sha512-PxV0v9VbGWH9zP37P5w2msLUFDr287nYjoY2XVF+RSolyiTs1CQNI5ZMUO3o4MsSac/dpXxjyrZXQd72t/jRYA== dependencies: - "@sentry/types" "7.113.0" - "@sentry/utils" "7.113.0" + "@sentry/types" "8.13.0" -"@sentry/integrations@7.113.0": - version "7.113.0" - resolved "https://registry.yarnpkg.com/@sentry/integrations/-/integrations-7.113.0.tgz#cce71e07cf90c4bf9b22f85c3ce22d9ba926ae5a" - integrity sha512-w0sspGBQ+6+V/9bgCkpuM3CGwTYoQEVeTW6iNebFKbtN7MrM3XsGAM9I2cW1jVxFZROqCBPFtd2cs5n0j14aAg== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@types/accepts@*": + version "1.3.7" + resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.7.tgz#3b98b1889d2b2386604c2bbbe62e4fb51e95b265" + integrity sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ== dependencies: - "@sentry/core" "7.113.0" - "@sentry/types" "7.113.0" - "@sentry/utils" "7.113.0" - localforage "^1.8.1" + "@types/node" "*" -"@sentry/node@^7.113.0": - version "7.113.0" - resolved "https://registry.yarnpkg.com/@sentry/node/-/node-7.113.0.tgz#c0d15a50a167f6cfc3a736838c9a919d5d6f1b01" - integrity sha512-Vam4Ia0I9fhVw8GJOzcLP7MiiHJSKl8L9LzLMMLG3+2/dFnDQOyS7sOfk3GqgpwzqPiusP9vFu7CFSX7EMQbTg== +"@types/body-parser@*": + version "1.19.5" + resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" + integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== dependencies: - "@sentry-internal/tracing" "7.113.0" - "@sentry/core" "7.113.0" - "@sentry/integrations" "7.113.0" - "@sentry/types" "7.113.0" - "@sentry/utils" "7.113.0" + "@types/connect" "*" + "@types/node" "*" -"@sentry/types@7.113.0": - version "7.113.0" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.113.0.tgz#2193c9933838302c82814771b03a8647fa684ffb" - integrity sha512-PJbTbvkcPu/LuRwwXB1He8m+GjDDLKBtu3lWg5xOZaF5IRdXQU2xwtdXXsjge4PZR00tF7MO7X8ZynTgWbYaew== +"@types/connect@*": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" -"@sentry/utils@7.113.0": - version "7.113.0" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.113.0.tgz#1e6e790c9d84e4809b2bb529bbd33a506b6db7bd" - integrity sha512-nzKsErwmze1mmEsbW2AwL2oB+I5v6cDEJY4sdfLekA4qZbYZ8pV5iWza6IRl4XfzGTE1qpkZmEjPU9eyo0yvYw== +"@types/connect@3.4.36": + version "3.4.36" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.36.tgz#e511558c15a39cb29bd5357eebb57bd1459cd1ab" + integrity sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w== dependencies: - "@sentry/types" "7.113.0" + "@types/node" "*" -"@sinclair/typebox@^0.27.8": - version "0.27.8" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" - integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== +"@types/content-disposition@*": + version "0.5.8" + resolved "https://registry.yarnpkg.com/@types/content-disposition/-/content-disposition-0.5.8.tgz#6742a5971f490dc41e59d277eee71361fea0b537" + integrity sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg== -"@types/deep-equal@^1.0.3": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@types/deep-equal/-/deep-equal-1.0.4.tgz#c0a854be62d6b9fae665137a6639aab53389a147" - integrity sha512-tqdiS4otQP4KmY0PR3u6KbZ5EWvhNdUoS/jc93UuK23C220lOZ/9TvjfxdPcKvqwwDVtmtSCrnr0p/2dirAxkA== +"@types/cookies@*": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@types/cookies/-/cookies-0.9.0.tgz#a2290cfb325f75f0f28720939bee854d4142aee2" + integrity sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q== + dependencies: + "@types/connect" "*" + "@types/express" "*" + "@types/keygrip" "*" + "@types/node" "*" "@types/estree@1.0.5", "@types/estree@^1.0.0": version "1.0.5" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== -"@types/he@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@types/he/-/he-1.1.2.tgz#0c8b275f36d2b8b651104638e4d45693349c3953" - integrity sha512-kSJPcLO1x+oolc0R89pUl2kozldQ/fVQ1C1p5mp8fPoLdF/ZcBvckaTC2M8xXh3GYendXvCpy5m/a2eSbfgNgw== +"@types/express-serve-static-core@^4.17.33": + version "4.19.5" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.19.5.tgz#218064e321126fcf9048d1ca25dd2465da55d9c6" + integrity sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@*": + version "4.17.21" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" + integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/http-assert@*": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.5.tgz#dfb1063eb7c240ee3d3fe213dac5671cfb6a8dbf" + integrity sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g== -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": +"@types/http-errors@*": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" + integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== @@ -973,6 +1528,53 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/keygrip@*": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.6.tgz#1749535181a2a9b02ac04a797550a8787345b740" + integrity sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ== + +"@types/koa-compose@*": + version "3.2.8" + resolved "https://registry.yarnpkg.com/@types/koa-compose/-/koa-compose-3.2.8.tgz#dec48de1f6b3d87f87320097686a915f1e954b57" + integrity sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA== + dependencies: + "@types/koa" "*" + +"@types/koa@*": + version "2.15.0" + resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.15.0.tgz#eca43d76f527c803b491731f95df575636e7b6f2" + integrity sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g== + dependencies: + "@types/accepts" "*" + "@types/content-disposition" "*" + "@types/cookies" "*" + "@types/http-assert" "*" + "@types/http-errors" "*" + "@types/keygrip" "*" + "@types/koa-compose" "*" + "@types/node" "*" + +"@types/koa@2.14.0": + version "2.14.0" + resolved "https://registry.yarnpkg.com/@types/koa/-/koa-2.14.0.tgz#8939e8c3b695defc12f2ef9f38064509e564be18" + integrity sha512-DTDUyznHGNHAl+wd1n0z1jxNajduyTh8R53xoewuerdBzGo6Ogj6F2299BFtrexJw4NtgjsI5SMPCmV9gZwGXA== + dependencies: + "@types/accepts" "*" + "@types/content-disposition" "*" + "@types/cookies" "*" + "@types/http-assert" "*" + "@types/http-errors" "*" + "@types/keygrip" "*" + "@types/koa-compose" "*" + "@types/node" "*" + +"@types/koa__router@12.0.3": + version "12.0.3" + resolved "https://registry.yarnpkg.com/@types/koa__router/-/koa__router-12.0.3.tgz#3fb74ea1991cadd6c6712b6106657aa6e64afca4" + integrity sha512-5YUJVv6NwM1z7m6FuYpKfNLTZ932Z6EF6xy2BbtpJSyn13DKNQEkXVffFVSnJHxvwwWh2SAeumpjAYUELqgjyw== + dependencies: + "@types/koa" "*" + "@types/lodash@^4.14.195": version "4.14.195" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.195.tgz#bafc975b252eb6cea78882ce8a7b6bf22a6de632" @@ -985,6 +1587,11 @@ dependencies: "@types/node" "*" +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== + "@types/mitm@^1.3.8": version "1.3.8" resolved "https://registry.yarnpkg.com/@types/mitm/-/mitm-1.3.8.tgz#0acc7b82c1afd223828d445b1b101f86d6680ae8" @@ -992,6 +1599,13 @@ dependencies: "@types/node" "*" +"@types/mysql@2.15.22": + version "2.15.22" + resolved "https://registry.yarnpkg.com/@types/mysql/-/mysql-2.15.22.tgz#8705edb9872bf4aa9dbc004cd494e00334e5cdb4" + integrity sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ== + dependencies: + "@types/node" "*" + "@types/node-cron@^3.0.7": version "3.0.7" resolved "https://registry.yarnpkg.com/@types/node-cron/-/node-cron-3.0.7.tgz#978bf75f7247385c61d23b6a060ba9eedb03e2f4" @@ -1011,15 +1625,40 @@ integrity sha512-8h7k1YgQKxKXWckzFCMfsIwn0Y61UK6tlD6y2lOb3hTOIMlK3t9/QwHOhc81TwU+RMf0As5fj7NPjroERCnejQ== "@types/node@^14.18.12": - version "14.18.12" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.12.tgz#0d4557fd3b94497d793efd4e7d92df2f83b4ef24" - integrity sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A== + version "14.18.63" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.63.tgz#1788fa8da838dbb5f9ea994b834278205db6ca2b" + integrity sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ== "@types/normalize-package-data@^2.4.0": version "2.4.1" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== +"@types/pg-pool@2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/pg-pool/-/pg-pool-2.0.4.tgz#b5c60f678094ff3acf3442628a7f708928fcf263" + integrity sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ== + dependencies: + "@types/pg" "*" + +"@types/pg@*": + version "8.11.6" + resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.11.6.tgz#a2d0fb0a14b53951a17df5197401569fb9c0c54b" + integrity sha512-/2WmmBXHLsfRqzfHW7BNZ8SbYzE8OSk7i3WjFYvfgRHj7S1xj+16Je5fUKv3lVdVzk/zn9TXOqf+avFCFIE0yQ== + dependencies: + "@types/node" "*" + pg-protocol "*" + pg-types "^4.0.1" + +"@types/pg@8.6.1": + version "8.6.1" + resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.6.1.tgz#099450b8dc977e8197a44f5229cedef95c8747f9" + integrity sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w== + dependencies: + "@types/node" "*" + pg-protocol "*" + pg-types "^2.2.0" + "@types/pixelmatch@*": version "5.2.4" resolved "https://registry.yarnpkg.com/@types/pixelmatch/-/pixelmatch-5.2.4.tgz#ca145cc5ede1388c71c68edf2d1f5190e5ddd0f6" @@ -1027,11 +1666,43 @@ dependencies: "@types/node" "*" +"@types/qs@*": + version "6.9.15" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.15.tgz#adde8a060ec9c305a82de1babc1056e73bd64dce" + integrity sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg== + +"@types/range-parser@*": + version "1.2.7" + resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== + "@types/semver@^7.3.12": version "7.3.13" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== +"@types/send@*": + version "0.17.4" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" + integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-static@*": + version "1.15.7" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.7.tgz#22174bbd74fb97fe303109738e9b5c2f3064f714" + integrity sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw== + dependencies: + "@types/http-errors" "*" + "@types/node" "*" + "@types/send" "*" + +"@types/shimmer@^1.0.2": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.0.5.tgz#491d8984d4510e550bfeb02d518791d7f59d2b88" + integrity sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww== + "@types/stack-utils@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" @@ -1178,24 +1849,24 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== -"@vitest/coverage-v8@^1.3.1": - version "1.3.1" - resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-1.3.1.tgz#cf5ed8eb5bff6658cea126d0f8fcd2872de0b59f" - integrity sha512-UuBnkSJUNE9rdHjDCPyJ4fYuMkoMtnghes1XohYa4At0MS3OQSAo97FrbwSLRshYsXThMZy1+ybD/byK5llyIg== +"@vitest/coverage-v8@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-1.6.0.tgz#2f54ccf4c2d9f23a71294aba7f95b3d2e27d14e7" + integrity sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew== dependencies: "@ampproject/remapping" "^2.2.1" "@bcoe/v8-coverage" "^0.2.3" debug "^4.3.4" istanbul-lib-coverage "^3.2.2" istanbul-lib-report "^3.0.1" - istanbul-lib-source-maps "^4.0.1" + istanbul-lib-source-maps "^5.0.4" istanbul-reports "^3.1.6" magic-string "^0.30.5" magicast "^0.3.3" picocolors "^1.0.0" std-env "^3.5.0" + strip-literal "^2.0.0" test-exclude "^6.0.0" - v8-to-istanbul "^9.2.0" "@vitest/expect@1.6.0": version "1.6.0" @@ -1258,6 +1929,16 @@ abstract-logging@^2.0.1: resolved "https://registry.yarnpkg.com/abstract-logging/-/abstract-logging-2.0.1.tgz#6b0c371df212db7129b57d2e7fcf282b8bf1c839" integrity sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA== +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn-import-attributes@^1.9.5: + version "1.9.5" + resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" + integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== + acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -1359,14 +2040,6 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -array-buffer-byte-length@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" - integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== - dependencies: - call-bind "^1.0.2" - is-array-buffer "^3.0.1" - array-includes@^3.1.6: version "3.1.6" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" @@ -1435,13 +2108,6 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -available-typed-arrays@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" - integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== - dependencies: - possible-typed-array-names "^1.0.0" - avvio@^8.2.0: version "8.2.1" resolved "https://registry.yarnpkg.com/avvio/-/avvio-8.2.1.tgz#b5a482729847abb84d5aadce06511c04a0a62f82" @@ -1538,17 +2204,6 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" -call-bind@^1.0.5, call-bind@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" - integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.4" - set-function-length "^1.2.1" - callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -1617,6 +2272,11 @@ ci-info@^3.4.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.1.tgz#708a6cdae38915d597afdf3b145f2f8e1ff55f3f" integrity sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w== +cjs-module-lexer@^1.2.2: + version "1.3.1" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz#c485341ae8fd999ca4ee5af2d7a1c9ae01e0099c" + integrity sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q== + clean-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clean-regexp/-/clean-regexp-1.0.0.tgz#8df7c7aae51fd36874e8f8d05b9180bc11a3fed7" @@ -1706,25 +2366,20 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -concurrently@^7.6.0: - version "7.6.0" - resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-7.6.0.tgz#531a6f5f30cf616f355a4afb8f8fcb2bba65a49a" - integrity sha512-BKtRgvcJGeZ4XttiDiNcFiRlxoAeZOseqUvyYRUp/Vtd+9p1ULmeoSqGsDA+2ivdeDFpqrJvGvmI+StKfKl5hw== +concurrently@^8.2.2: + version "8.2.2" + resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-8.2.2.tgz#353141985c198cfa5e4a3ef90082c336b5851784" + integrity sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg== dependencies: - chalk "^4.1.0" - date-fns "^2.29.1" + chalk "^4.1.2" + date-fns "^2.30.0" lodash "^4.17.21" - rxjs "^7.0.0" - shell-quote "^1.7.3" - spawn-command "^0.0.2-1" - supports-color "^8.1.0" + rxjs "^7.8.1" + shell-quote "^1.8.1" + spawn-command "0.0.2" + supports-color "^8.1.1" tree-kill "^1.2.2" - yargs "^17.3.1" - -convert-source-map@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" - integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + yargs "^17.7.2" cookie@^0.5.0: version "0.5.0" @@ -1740,7 +2395,7 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" -date-fns@^2.29.1: +date-fns@^2.30.0: version "2.30.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== @@ -1773,30 +2428,6 @@ deep-eql@^4.1.3: dependencies: type-detect "^4.0.0" -deep-equal@^2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.3.tgz#af89dafb23a396c7da3e862abc0be27cf51d56e1" - integrity sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA== - dependencies: - array-buffer-byte-length "^1.0.0" - call-bind "^1.0.5" - es-get-iterator "^1.1.3" - get-intrinsic "^1.2.2" - is-arguments "^1.1.1" - is-array-buffer "^3.0.2" - is-date-object "^1.0.5" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - isarray "^2.0.5" - object-is "^1.1.5" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.5.1" - side-channel "^1.0.4" - which-boxed-primitive "^1.0.2" - which-collection "^1.0.1" - which-typed-array "^1.1.13" - deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -1819,24 +2450,6 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -define-data-property@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" - integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== - dependencies: - get-intrinsic "^1.2.1" - gopd "^1.0.1" - has-property-descriptors "^1.0.0" - -define-data-property@^1.1.2, define-data-property@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" - integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== - dependencies: - es-define-property "^1.0.0" - es-errors "^1.3.0" - gopd "^1.0.1" - define-properties@^1.1.3, define-properties@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" @@ -1845,15 +2458,6 @@ define-properties@^1.1.3, define-properties@^1.1.4: has-property-descriptors "^1.0.0" object-keys "^1.1.1" -define-properties@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" - integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== - dependencies: - define-data-property "^1.0.1" - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -1966,7 +2570,7 @@ discord-api-types@0.37.61: resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.61.tgz#9dd8e58c624237e6f1b23be2d29579af268b8c5b" integrity sha512-o/dXNFfhBpYHpQFdT6FWzeO7pKc838QeeZ9d91CfVAtpr5XLK4B/zYxQbYgPdoMiTDvJfzcsLW5naXgmHGDNXw== -discord.js@^14.14.1: +discord.js@14.14.1: version "14.14.1" resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.14.1.tgz#9a2bea23bba13819705ab87612837610abce9ee3" integrity sha512-/hUVzkIerxKHyRKopJy5xejp4MYKDPTszAnpYxzVVv4qJYf+Tkt+jnT2N29PIPschicaEEpXwF2ARrTYHYwQ5w== @@ -2000,10 +2604,10 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" -dotenv-cli@^7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/dotenv-cli/-/dotenv-cli-7.3.0.tgz#21e33e7944713001677658d68856063968edfbd2" - integrity sha512-314CA4TyK34YEJ6ntBf80eUY+t1XaFLyem1k9P0sX1gn30qThZ5qZr/ZwE318gEnzyYP9yj9HJk6SqwE0upkfw== +dotenv-cli@^7.4.2: + version "7.4.2" + resolved "https://registry.yarnpkg.com/dotenv-cli/-/dotenv-cli-7.4.2.tgz#c158a818de08e1fbc51d310f628cbace9075b734" + integrity sha512-SbUj8l61zIbzyhIbg0FwPJq6+wjbzdn9oEtozQpZ6kW2ihCcapKVZj49oCT3oPM+mgQm+itgvUQcG5szxVrZTA== dependencies: cross-spawn "^7.0.3" dotenv "^16.3.0" @@ -2015,12 +2619,7 @@ dotenv-expand@^10.0.0: resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-10.0.0.tgz#12605d00fb0af6d0a592e6558585784032e4ef37" integrity sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A== -dotenv@^16.0.3: - version "16.0.3" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07" - integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ== - -dotenv@^16.3.0: +dotenv@^16.3.0, dotenv@^16.4.5: version "16.4.5" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== @@ -2109,33 +2708,6 @@ es-abstract@^1.19.0, es-abstract@^1.20.4: unbox-primitive "^1.0.2" which-typed-array "^1.1.9" -es-define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" - integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== - dependencies: - get-intrinsic "^1.2.4" - -es-errors@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" - integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== - -es-get-iterator@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" - integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" - has-symbols "^1.0.3" - is-arguments "^1.1.1" - is-map "^2.0.2" - is-set "^2.0.2" - is-string "^1.0.7" - isarray "^2.0.5" - stop-iteration-iterator "^1.0.0" - es-set-tostringtag@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" @@ -2161,7 +2733,7 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -esbuild@^0.20.1, esbuild@^0.20.2, esbuild@~0.20.2: +esbuild@^0.20.1: version "0.20.2" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.20.2.tgz#9d6b2386561766ee6b5a55196c6d766d28c87ea1" integrity sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g== @@ -2190,6 +2762,65 @@ esbuild@^0.20.1, esbuild@^0.20.2, esbuild@~0.20.2: "@esbuild/win32-ia32" "0.20.2" "@esbuild/win32-x64" "0.20.2" +esbuild@^0.22.0: + version "0.22.0" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.22.0.tgz#9742e664aac9f61e2898f4c27bd4dd4272e6f661" + integrity sha512-zNYA6bFZsVnsU481FnGAQjLDW0Pl/8BGG7EvAp15RzUvGC+ME7hf1q7LvIfStEQBz/iEHuBJCYcOwPmNCf1Tlw== + optionalDependencies: + "@esbuild/aix-ppc64" "0.22.0" + "@esbuild/android-arm" "0.22.0" + "@esbuild/android-arm64" "0.22.0" + "@esbuild/android-x64" "0.22.0" + "@esbuild/darwin-arm64" "0.22.0" + "@esbuild/darwin-x64" "0.22.0" + "@esbuild/freebsd-arm64" "0.22.0" + "@esbuild/freebsd-x64" "0.22.0" + "@esbuild/linux-arm" "0.22.0" + "@esbuild/linux-arm64" "0.22.0" + "@esbuild/linux-ia32" "0.22.0" + "@esbuild/linux-loong64" "0.22.0" + "@esbuild/linux-mips64el" "0.22.0" + "@esbuild/linux-ppc64" "0.22.0" + "@esbuild/linux-riscv64" "0.22.0" + "@esbuild/linux-s390x" "0.22.0" + "@esbuild/linux-x64" "0.22.0" + "@esbuild/netbsd-x64" "0.22.0" + "@esbuild/openbsd-arm64" "0.22.0" + "@esbuild/openbsd-x64" "0.22.0" + "@esbuild/sunos-x64" "0.22.0" + "@esbuild/win32-arm64" "0.22.0" + "@esbuild/win32-ia32" "0.22.0" + "@esbuild/win32-x64" "0.22.0" + +esbuild@~0.21.5: + version "0.21.5" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" + integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== + optionalDependencies: + "@esbuild/aix-ppc64" "0.21.5" + "@esbuild/android-arm" "0.21.5" + "@esbuild/android-arm64" "0.21.5" + "@esbuild/android-x64" "0.21.5" + "@esbuild/darwin-arm64" "0.21.5" + "@esbuild/darwin-x64" "0.21.5" + "@esbuild/freebsd-arm64" "0.21.5" + "@esbuild/freebsd-x64" "0.21.5" + "@esbuild/linux-arm" "0.21.5" + "@esbuild/linux-arm64" "0.21.5" + "@esbuild/linux-ia32" "0.21.5" + "@esbuild/linux-loong64" "0.21.5" + "@esbuild/linux-mips64el" "0.21.5" + "@esbuild/linux-ppc64" "0.21.5" + "@esbuild/linux-riscv64" "0.21.5" + "@esbuild/linux-s390x" "0.21.5" + "@esbuild/linux-x64" "0.21.5" + "@esbuild/netbsd-x64" "0.21.5" + "@esbuild/openbsd-x64" "0.21.5" + "@esbuild/sunos-x64" "0.21.5" + "@esbuild/win32-arm64" "0.21.5" + "@esbuild/win32-ia32" "0.21.5" + "@esbuild/win32-x64" "0.21.5" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -2795,7 +3426,7 @@ function.prototype.name@^1.1.5: es-abstract "^1.19.0" functions-have-names "^1.2.2" -functions-have-names@^1.2.2, functions-have-names@^1.2.3: +functions-have-names@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== @@ -2827,27 +3458,6 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: has "^1.0.3" has-symbols "^1.0.3" -get-intrinsic@^1.2.0, get-intrinsic@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" - integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-proto "^1.0.1" - has-symbols "^1.0.3" - -get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" - integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== - dependencies: - es-errors "^1.3.0" - function-bind "^1.1.2" - has-proto "^1.0.1" - has-symbols "^1.0.3" - hasown "^2.0.0" - get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" @@ -2871,10 +3481,10 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" -get-tsconfig@^4.7.3: - version "4.7.3" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.3.tgz#0498163d98f7b58484dd4906999c0c9d5f103f83" - integrity sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg== +get-tsconfig@^4.7.5: + version "4.7.5" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.5.tgz#5e012498579e9a6947511ed0cd403272c7acbbaf" + integrity sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw== dependencies: resolve-pkg-maps "^1.0.0" @@ -3002,13 +3612,6 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.1.1" -has-property-descriptors@^1.0.1, has-property-descriptors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" - integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== - dependencies: - es-define-property "^1.0.0" - has-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" @@ -3026,13 +3629,6 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" -has-tostringtag@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" - integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== - dependencies: - has-symbols "^1.0.3" - has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -3047,11 +3643,6 @@ hasown@^2.0.0: dependencies: function-bind "^1.1.2" -he@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - helmet@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/helmet/-/helmet-6.0.1.tgz#52ec353638b2e87f14fe079d142b368ac11e79a4" @@ -3100,11 +3691,6 @@ ignore@^5.2.0: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== -immediate@~3.0.5: - version "3.0.6" - resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" - integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== - import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -3113,6 +3699,26 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" +import-in-the-middle@1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz#2a266676e3495e72c04bbaa5ec14756ba168391b" + integrity sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw== + dependencies: + acorn "^8.8.2" + acorn-import-assertions "^1.9.0" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + +import-in-the-middle@^1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.8.1.tgz#8b51c2cc631b64e53e958d7048d2d9463ce628f8" + integrity sha512-yhRwoHtiLGvmSozNOALgjRPFI6uYsds60EoMqqnXyyv+JOIW/BrrLejuTGBt+bq0T5tLzOHrN0T7xYTm4Qt/ng== + dependencies: + acorn "^8.8.2" + acorn-import-attributes "^1.9.5" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -3155,14 +3761,6 @@ ipaddr.js@1.9.1: resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -is-arguments@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - is-array-buffer@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.1.tgz#deb1db4fcae48308d54ef2442706c0393997052a" @@ -3172,15 +3770,6 @@ is-array-buffer@^3.0.1: get-intrinsic "^1.1.3" is-typed-array "^1.1.10" -is-array-buffer@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" - integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.0" - is-typed-array "^1.1.10" - is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -3229,13 +3818,6 @@ is-date-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== -is-date-object@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== - dependencies: - has-tostringtag "^1.0.0" - is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -3258,11 +3840,6 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== -is-map@^2.0.1, is-map@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" - integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== - is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -3311,11 +3888,6 @@ is-relative-path@^1.0.2: resolved "https://registry.yarnpkg.com/is-relative-path/-/is-relative-path-1.0.2.tgz#091b46a0d67c1ed0fe85f1f8cfdde006bb251d46" integrity sha512-i1h+y50g+0hRbBD+dbnInl3JlJ702aar58snAeX+MxBAPvzXGej7sYoPMhlnykabt0ZzCJNBEyzMlekuQZN7fA== -is-set@^2.0.1, is-set@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" - integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== - is-shared-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" @@ -3368,11 +3940,6 @@ is-url@^1.2.4: resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== -is-weakmap@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" - integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== - is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -3380,19 +3947,6 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" -is-weakset@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" - integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - -isarray@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -3426,14 +3980,14 @@ istanbul-lib-report@^3.0.1: make-dir "^4.0.0" supports-color "^7.1.0" -istanbul-lib-source-maps@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" - integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== +istanbul-lib-source-maps@^5.0.4: + version "5.0.5" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.5.tgz#2bed84db66687448736dba91954f1080f4441ced" + integrity sha512-gKf4eJ8bHmSX/ljiOCpnd8vtmHTwG71uugm0kXYd5aqFCl6z8cj8k7QduXSwU6QOst6LCdSXTlaoc8W4554crQ== dependencies: + "@jridgewell/trace-mapping" "^0.3.23" debug "^4.1.1" istanbul-lib-coverage "^3.0.0" - source-map "^0.6.1" istanbul-reports@^3.1.6: version "3.1.7" @@ -3598,13 +4152,6 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -lie@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e" - integrity sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw== - dependencies: - immediate "~3.0.5" - light-my-request@^5.6.1: version "5.9.1" resolved "https://registry.yarnpkg.com/light-my-request/-/light-my-request-5.9.1.tgz#076f8d4cc4639408cc48381d4f2860212d469d4b" @@ -3627,13 +4174,6 @@ local-pkg@^0.5.0: mlly "^1.4.2" pkg-types "^1.0.3" -localforage@^1.8.1: - version "1.10.0" - resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.10.0.tgz#5c465dc5f62b2807c3a84c0c6a1b1b3212781dd4" - integrity sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg== - dependencies: - lie "3.1.1" - locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -3690,6 +4230,11 @@ lru-cache@^10.2.0: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.2.tgz#48206bc114c1252940c41b25b41af5b545aca878" integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ== +lru-cache@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.3.0.tgz#4a4aaf10c84658ab70f79a85a9a3f1e1fb11196b" + integrity sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ== + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -3697,11 +4242,6 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-cache@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-8.0.0.tgz#152bf98daafa6a46a5905694fccd6f5f7f6095fe" - integrity sha512-pMu1vSJIwJPS/YuMJAJFjvKA2OC7rvgKqJHr90JmZ1kv/hO+MuzqHRSWqyn730vlOwc1Bx/c8+3izTGzmKyXNQ== - madge@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/madge/-/madge-7.0.0.tgz#64b1762033b0f969caa7e5853004b6850e8430bb" @@ -3903,6 +4443,11 @@ module-definition@^5.0.1: ast-module-types "^5.0.0" node-source-walk "^6.0.1" +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== + module-lookup-amd@^8.0.5: version "8.0.5" resolved "https://registry.yarnpkg.com/module-lookup-amd/-/module-lookup-amd-8.0.5.tgz#aaeea41979105b49339380ca3f7d573db78c32a5" @@ -4009,14 +4554,6 @@ object-inspect@^1.12.2, object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== -object-is@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" - integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -4046,6 +4583,11 @@ obliterator@^2.0.1: resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" integrity sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ== +obuf@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + oldschooljs@^2.5.4: version "2.5.4" resolved "https://registry.yarnpkg.com/oldschooljs/-/oldschooljs-2.5.4.tgz#59aa85be412ac14ca7b2cda885911d16ceaac702" @@ -4081,6 +4623,15 @@ onetime@^6.0.0: dependencies: mimic-fn "^4.0.0" +opentelemetry-instrumentation-fetch-node@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/opentelemetry-instrumentation-fetch-node/-/opentelemetry-instrumentation-fetch-node-1.2.0.tgz#5beaad33b622f7021c61733af864fb505cd35626" + integrity sha512-aiSt/4ubOTyb1N5C2ZbGrBvaJOXIZhZvpRPYuUVxQJe27wJZqf/o65iPrqgLcgfeOLaQ8cS2Q+762jrYvniTrA== + dependencies: + "@opentelemetry/api" "^1.6.0" + "@opentelemetry/instrumentation" "^0.43.0" + "@opentelemetry/semantic-conventions" "^1.17.0" + optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -4267,6 +4818,45 @@ pathval@^1.1.1: resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== +pg-int8@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" + integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== + +pg-numeric@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pg-numeric/-/pg-numeric-1.0.2.tgz#816d9a44026086ae8ae74839acd6a09b0636aa3a" + integrity sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw== + +pg-protocol@*: + version "1.6.1" + resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.6.1.tgz#21333e6d83b01faaebfe7a33a7ad6bfd9ed38cb3" + integrity sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg== + +pg-types@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3" + integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA== + dependencies: + pg-int8 "1.0.1" + postgres-array "~2.0.0" + postgres-bytea "~1.0.0" + postgres-date "~1.0.4" + postgres-interval "^1.1.0" + +pg-types@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-4.0.2.tgz#399209a57c326f162461faa870145bb0f918b76d" + integrity sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng== + dependencies: + pg-int8 "1.0.1" + pg-numeric "1.0.2" + postgres-array "~3.0.1" + postgres-bytea "~3.0.0" + postgres-date "~2.1.0" + postgres-interval "^3.0.0" + postgres-range "^1.1.1" + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" @@ -4307,10 +4897,10 @@ pino@^8.5.0: sonic-boom "^3.1.0" thread-stream "^2.0.0" -piscina@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/piscina/-/piscina-4.4.0.tgz#e3af8e5721d8fad08c6ccaf8a64f9f42279efbb5" - integrity sha512-+AQduEJefrOApE4bV7KRmp3N2JnnyErlVqq4P/jmko4FPz9Z877BCccl/iB3FdrWSUkvbGV9Kan/KllJgat3Vg== +piscina@^4.6.1: + version "4.6.1" + resolved "https://registry.yarnpkg.com/piscina/-/piscina-4.6.1.tgz#4de673b0ff84bf641b31b07b3348669383b51c9a" + integrity sha512-z30AwWGtQE+Apr+2WBZensP2lIvwoaMcOPkQlIEmSGMJNUvaYACylPYrQM6wSdUNJlnDVMSpLv7xTMJqlVshOA== optionalDependencies: nice-napi "^1.0.2" @@ -4345,11 +4935,6 @@ pngjs@^6.0.0: resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-6.0.0.tgz#ca9e5d2aa48db0228a52c419c3308e87720da821" integrity sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg== -possible-typed-array-names@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" - integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== - postcss-values-parser@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-6.0.2.tgz#636edc5b86c953896f1bb0d7a7a6615df00fb76f" @@ -4368,6 +4953,55 @@ postcss@^8.4.23, postcss@^8.4.38: picocolors "^1.0.0" source-map-js "^1.2.0" +postgres-array@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" + integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== + +postgres-array@~3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-3.0.2.tgz#68d6182cb0f7f152a7e60dc6a6889ed74b0a5f98" + integrity sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog== + +postgres-bytea@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" + integrity sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w== + +postgres-bytea@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-3.0.0.tgz#9048dc461ac7ba70a6a42d109221619ecd1cb089" + integrity sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw== + dependencies: + obuf "~1.1.2" + +postgres-date@~1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.7.tgz#51bc086006005e5061c591cee727f2531bf641a8" + integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q== + +postgres-date@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-2.1.0.tgz#b85d3c1fb6fb3c6c8db1e9942a13a3bf625189d0" + integrity sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA== + +postgres-interval@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695" + integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ== + dependencies: + xtend "^4.0.0" + +postgres-interval@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-3.0.0.tgz#baf7a8b3ebab19b7f38f07566c7aab0962f0c86a" + integrity sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw== + +postgres-range@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/postgres-range/-/postgres-range-1.1.4.tgz#a59c5f9520909bcec5e63e8cf913a92e4c952863" + integrity sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w== + precinct@^11.0.5: version "11.0.5" resolved "https://registry.yarnpkg.com/precinct/-/precinct-11.0.5.tgz#3e15b3486670806f18addb54b8533e23596399ff" @@ -4439,11 +5073,11 @@ printable-characters@^1.0.42: integrity sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ== prisma@^5.13.0: - version "5.13.0" - resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.13.0.tgz#1f06e20ccfb6038ad68869e6eacd3b346f9d0851" - integrity sha512-kGtcJaElNRAdAGsCNykFSZ7dBKpL14Cbs+VaQ8cECxQlRPDjBlMHNFYeYt0SKovAVy2Y65JXQwB3A5+zIQwnTg== + version "5.16.1" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.16.1.tgz#6dfd1e27e6534741326f4a231f04c16b3fbb7ba9" + integrity sha512-Z1Uqodk44diztImxALgJJfNl2Uisl9xDRvqybMKEBYJLNKNhDfAHf+ZIJbZyYiBhLMbKU9cYGdDVG5IIXEnL2Q== dependencies: - "@prisma/engines" "5.13.0" + "@prisma/engines" "5.16.1" process-warning@^2.0.0: version "2.1.0" @@ -4575,16 +5209,6 @@ regexp.prototype.flags@^1.4.3: define-properties "^1.1.3" functions-have-names "^1.2.2" -regexp.prototype.flags@^1.5.1: - version "1.5.2" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" - integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== - dependencies: - call-bind "^1.0.6" - define-properties "^1.2.1" - es-errors "^1.3.0" - set-function-name "^2.0.1" - regexpp@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" @@ -4600,6 +5224,15 @@ require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +require-in-the-middle@^7.1.1: + version "7.3.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.3.0.tgz#ce64a1083647dc07b3273b348357efac8a9945c9" + integrity sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw== + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.22.1" + requirejs-config-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz#4244da5dd1f59874038cc1091d078d620abb6ebc" @@ -4683,10 +5316,10 @@ rimraf@^3.0.2: dependencies: glob "^7.1.3" -rimraf@^5.0.5: - version "5.0.5" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.5.tgz#9be65d2d6e683447d2e9013da2bf451139a61ccf" - integrity sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A== +rimraf@^5.0.7: + version "5.0.7" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.7.tgz#27bddf202e7d89cb2e0381656380d1734a854a74" + integrity sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg== dependencies: glob "^10.3.7" @@ -4721,7 +5354,7 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rxjs@^7.0.0: +rxjs@^7.8.1: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== @@ -4800,6 +5433,11 @@ semver@^7.3.7: dependencies: lru-cache "^6.0.0" +semver@^7.5.2: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + semver@^7.5.3: version "7.6.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" @@ -4812,28 +5450,6 @@ set-cookie-parser@^2.4.1: resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.4.8.tgz#d0da0ed388bc8f24e706a391f9c9e252a13c58b2" integrity sha512-edRH8mBKEWNVIVMKejNnuJxleqYE/ZSdcT8/Nem9/mmosx12pctd80s2Oy00KNZzrogMZS5mauK2/ymL1bvlvg== -set-function-length@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.1.tgz#47cc5945f2c771e2cf261c6737cf9684a2a5e425" - integrity sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g== - dependencies: - define-data-property "^1.1.2" - es-errors "^1.3.0" - function-bind "^1.1.2" - get-intrinsic "^1.2.3" - gopd "^1.0.1" - has-property-descriptors "^1.0.1" - -set-function-name@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" - integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== - dependencies: - define-data-property "^1.1.4" - es-errors "^1.3.0" - functions-have-names "^1.2.3" - has-property-descriptors "^1.0.2" - setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -4851,11 +5467,16 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@^1.7.3: +shell-quote@^1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== +shimmer@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" + integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -4914,15 +5535,15 @@ source-map-js@^1.2.0: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== -source-map@^0.6.1, source-map@~0.6.1: +source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -spawn-command@^0.0.2-1: - version "0.0.2-1" - resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" - integrity sha512-n98l9E2RMSJ9ON1AKisHzz7V42VDiBQGY6PB1BwRglz99wpVsSuGzQ+jOi6lFXBGVTCrRpltvjm+/XA+tpeJrg== +spawn-command@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e" + integrity sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ== spdx-correct@^3.0.0: version "3.1.1" @@ -4982,13 +5603,6 @@ std-env@^3.5.0: resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2" integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg== -stop-iteration-iterator@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" - integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ== - dependencies: - internal-slot "^1.0.4" - stream-to-array@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353" @@ -5133,7 +5747,7 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -supports-color@^8.1.0: +supports-color@^8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== @@ -5274,13 +5888,13 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" -tsx@^4.9.0: - version "4.9.0" - resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.9.0.tgz#08d7ea58df19d87416d26cce8caf93d0c3619852" - integrity sha512-UY0UUhDPL6MkqkZU4xTEjEBOLfV+RIt4xeeJ1qwK73xai4/zveG+X6+tieILa7rjtegUW2LE4p7fw7gAoLuytA== +tsx@^4.16.0: + version "4.16.0" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.16.0.tgz#913dd96f191b76f07a8744201d8c15d510aa1352" + integrity sha512-MPgN+CuY+4iKxGoJNPv+1pyo5YWZAQ5XfsyobUG+zoKG7IkvCPLZDEyoIb8yLS2FcWci1nlxAqmvPlFWD5AFiQ== dependencies: - esbuild "~0.20.2" - get-tsconfig "^4.7.3" + esbuild "~0.21.5" + get-tsconfig "^4.7.5" optionalDependencies: fsevents "~2.3.3" @@ -5409,15 +6023,6 @@ uuid@8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -v8-to-istanbul@^9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad" - integrity sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA== - dependencies: - "@jridgewell/trace-mapping" "^0.3.12" - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^2.0.0" - validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -5515,27 +6120,6 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" -which-collection@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" - integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== - dependencies: - is-map "^2.0.1" - is-set "^2.0.1" - is-weakmap "^2.0.1" - is-weakset "^2.0.1" - -which-typed-array@^1.1.13: - version "1.1.14" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.14.tgz#1f78a111aee1e131ca66164d8bdc3ab062c95a06" - integrity sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg== - dependencies: - available-typed-arrays "^1.0.6" - call-bind "^1.0.5" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.1" - which-typed-array@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" @@ -5610,6 +6194,11 @@ ws@^8.14.2: resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.0.tgz#d145d18eca2ed25aaf791a183903f7be5e295fea" integrity sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow== +xtend@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" @@ -5625,7 +6214,7 @@ yargs-parser@^21.1.1: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== -yargs@^17.3.1: +yargs@^17.7.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== @@ -5655,7 +6244,7 @@ zlib-sync@^0.1.9: dependencies: nan "^2.18.0" -zod@^3.23.6: - version "3.23.6" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.6.tgz#c08a977e2255dab1fdba933651584a05fcbf19e1" - integrity sha512-RTHJlZhsRbuA8Hmp/iNL7jnfc4nZishjsanDAfEY1QpDQZCahUp3xDzl+zfweE9BklxMUcgBgS1b7Lvie/ZVwA== +zod@^3.23.8: + version "3.23.8" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" + integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== From 0fa9b3300c049a629cb5ea4e69a0825616c9c36f Mon Sep 17 00:00:00 2001 From: nwjgit <69014816+nwjgit@users.noreply.github.com> Date: Tue, 2 Jul 2024 10:03:49 -0500 Subject: [PATCH 027/249] Fix baxtorian Bathhouses loot image (#5932) --- src/lib/baxtorianBathhouses.ts | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/lib/baxtorianBathhouses.ts b/src/lib/baxtorianBathhouses.ts index a7876c0576d..02f65938798 100644 --- a/src/lib/baxtorianBathhouses.ts +++ b/src/lib/baxtorianBathhouses.ts @@ -17,6 +17,7 @@ import addSubTaskToActivityTask from './util/addSubTaskToActivityTask'; import { calcMaxTripLength } from './util/calcMaxTripLength'; import getOSItem from './util/getOSItem'; import { handleTripFinish } from './util/handleTripFinish'; +import { makeBankImage } from './util/makeBankImage'; import resolveItems, { resolveOSItems } from './util/resolveItems'; import { updateBankSetting } from './util/updateBankSetting'; @@ -358,7 +359,7 @@ export async function baxtorianBathhousesStartCommand({ if (!user.owns(cost)) { return `You don't have enough supplies to do a trip, for ${quantity}x ${bathHouseTier.name} baths, you need: ${cost}.`; } - updateBankSetting('bb_cost', cost); + await updateBankSetting('bb_cost', cost); await user.removeItemsFromBank(cost); await addSubTaskToActivityTask({ @@ -473,7 +474,12 @@ export async function baxtorianBathhousesActivity(data: BathhouseTaskOptions) { } } - await user.addItemsToBank({ items: loot, collectionLog: true }); + const { previousCL, itemsAdded } = await transactItems({ + userID: user.id, + collectionLog: true, + itemsToAdd: loot + }); + let xpStr = await user.addXP({ skillName: SkillsEnum.Herblore, amount: herbXP, duration }); xpStr += '\n'; xpStr += await user.addXP({ @@ -483,15 +489,16 @@ export async function baxtorianBathhousesActivity(data: BathhouseTaskOptions) { }); let uniqSpecies = uniqueArr(speciesServed); - updateBankSetting('bb_loot', loot); + await updateBankSetting('bb_loot', loot); - const bankImage = await bankImageGenerator.generateBankImage({ - bank: loot, + const bankImage = await makeBankImage({ + bank: itemsAdded, + title: 'Baxtorian Bathhouses Loot', user, - title: 'Baxtorian Bathhouses Loot' + previousCL }); - handleTripFinish( + return handleTripFinish( user, channelID, `${userMention(userID)}, ${user.minionName} finished running ${quantity}x ${tier.name} baths for ${ @@ -502,7 +509,7 @@ export async function baxtorianBathhousesActivity(data: BathhouseTaskOptions) { : '' } ${xpStr}`, - bankImage.image, + bankImage.file.attachment, data, loot ); From 00546e5bce5bd93cc774a1e553f514c5a2dc9263 Mon Sep 17 00:00:00 2001 From: nwjgit <69014816+nwjgit@users.noreply.github.com> Date: Tue, 2 Jul 2024 10:04:48 -0500 Subject: [PATCH 028/249] Fix dye swap creatables (#5903) --- .../guardiansOfTheRiftCreatables.ts | 24 ++++++++--------- src/lib/data/creatables/leagueCreatables.ts | 2 +- .../unit/snapshots/banksnapshots.test.ts.snap | 26 +++++++++---------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/lib/data/creatables/guardiansOfTheRiftCreatables.ts b/src/lib/data/creatables/guardiansOfTheRiftCreatables.ts index 4bd29d81f34..8e3563d3fc4 100644 --- a/src/lib/data/creatables/guardiansOfTheRiftCreatables.ts +++ b/src/lib/data/creatables/guardiansOfTheRiftCreatables.ts @@ -108,38 +108,38 @@ export const guardiansOfTheRiftCreatables: Createable[] = [ // Dye swaps { name: 'Abyssal green dye (using Abyssal blue dye)', - inputItems: new Bank({ 'Abyssal green dye': 1 }), - outputItems: new Bank({ 'Abyssal blue dye': 1 }), + inputItems: new Bank({ 'Abyssal blue dye': 1 }), + outputItems: new Bank({ 'Abyssal green dye': 1 }), noCl: true }, { name: 'Abyssal green dye (using Abyssal red dye)', - inputItems: new Bank({ 'Abyssal green dye': 1 }), - outputItems: new Bank({ 'Abyssal red dye': 1 }), + inputItems: new Bank({ 'Abyssal red dye': 1 }), + outputItems: new Bank({ 'Abyssal green dye': 1 }), noCl: true }, { name: 'Abyssal blue dye (using Abyssal green dye)', - inputItems: new Bank({ 'Abyssal blue dye': 1 }), - outputItems: new Bank({ 'Abyssal green dye': 1 }), + inputItems: new Bank({ 'Abyssal green dye': 1 }), + outputItems: new Bank({ 'Abyssal blue dye': 1 }), noCl: true }, { name: 'Abyssal blue dye (using Abyssal red dye)', - inputItems: new Bank({ 'Abyssal blue dye': 1 }), - outputItems: new Bank({ 'Abyssal red dye': 1 }), + inputItems: new Bank({ 'Abyssal red dye': 1 }), + outputItems: new Bank({ 'Abyssal blue dye': 1 }), noCl: true }, { name: 'Abyssal red dye (using Abyssal green dye)', - inputItems: new Bank({ 'Abyssal red dye': 1 }), - outputItems: new Bank({ 'Abyssal green dye': 1 }), + inputItems: new Bank({ 'Abyssal green dye': 1 }), + outputItems: new Bank({ 'Abyssal red dye': 1 }), noCl: true }, { name: 'Abyssal red dye (using Abyssal blue dye)', - inputItems: new Bank({ 'Abyssal red dye': 1 }), - outputItems: new Bank({ 'Abyssal blue dye': 1 }), + inputItems: new Bank({ 'Abyssal blue dye': 1 }), + outputItems: new Bank({ 'Abyssal red dye': 1 }), noCl: true } ]; diff --git a/src/lib/data/creatables/leagueCreatables.ts b/src/lib/data/creatables/leagueCreatables.ts index ba2100a9bb7..fa842864141 100644 --- a/src/lib/data/creatables/leagueCreatables.ts +++ b/src/lib/data/creatables/leagueCreatables.ts @@ -271,7 +271,7 @@ const voidOrnaments: Createable[] = [ outputItems: new Bank().add('Elite void robe (or)') }, { - name: 'Revert Elite void robe (or))', + name: 'Revert Elite void robe (or)', inputItems: new Bank({ 'Elite void robe (or)': 1 }), diff --git a/tests/unit/snapshots/banksnapshots.test.ts.snap b/tests/unit/snapshots/banksnapshots.test.ts.snap index ac26ef56ed9..eea6e871b5a 100644 --- a/tests/unit/snapshots/banksnapshots.test.ts.snap +++ b/tests/unit/snapshots/banksnapshots.test.ts.snap @@ -21527,7 +21527,7 @@ exports[`OSB Creatables 1`] = ` }, "frozen": false, }, - "name": "Revert Elite void robe (or))", + "name": "Revert Elite void robe (or)", "noCl": true, "outputItems": Bank { "bank": { @@ -22797,7 +22797,7 @@ exports[`OSB Creatables 1`] = ` "cantHaveItems": undefined, "inputItems": Bank { "bank": { - "26807": 1, + "26809": 1, }, "frozen": false, }, @@ -22805,7 +22805,7 @@ exports[`OSB Creatables 1`] = ` "noCl": true, "outputItems": Bank { "bank": { - "26809": 1, + "26807": 1, }, "frozen": false, }, @@ -22814,7 +22814,7 @@ exports[`OSB Creatables 1`] = ` "cantHaveItems": undefined, "inputItems": Bank { "bank": { - "26807": 1, + "26811": 1, }, "frozen": false, }, @@ -22822,7 +22822,7 @@ exports[`OSB Creatables 1`] = ` "noCl": true, "outputItems": Bank { "bank": { - "26811": 1, + "26807": 1, }, "frozen": false, }, @@ -22831,7 +22831,7 @@ exports[`OSB Creatables 1`] = ` "cantHaveItems": undefined, "inputItems": Bank { "bank": { - "26809": 1, + "26807": 1, }, "frozen": false, }, @@ -22839,7 +22839,7 @@ exports[`OSB Creatables 1`] = ` "noCl": true, "outputItems": Bank { "bank": { - "26807": 1, + "26809": 1, }, "frozen": false, }, @@ -22848,7 +22848,7 @@ exports[`OSB Creatables 1`] = ` "cantHaveItems": undefined, "inputItems": Bank { "bank": { - "26809": 1, + "26811": 1, }, "frozen": false, }, @@ -22856,7 +22856,7 @@ exports[`OSB Creatables 1`] = ` "noCl": true, "outputItems": Bank { "bank": { - "26811": 1, + "26809": 1, }, "frozen": false, }, @@ -22865,7 +22865,7 @@ exports[`OSB Creatables 1`] = ` "cantHaveItems": undefined, "inputItems": Bank { "bank": { - "26811": 1, + "26807": 1, }, "frozen": false, }, @@ -22873,7 +22873,7 @@ exports[`OSB Creatables 1`] = ` "noCl": true, "outputItems": Bank { "bank": { - "26807": 1, + "26811": 1, }, "frozen": false, }, @@ -22882,7 +22882,7 @@ exports[`OSB Creatables 1`] = ` "cantHaveItems": undefined, "inputItems": Bank { "bank": { - "26811": 1, + "26809": 1, }, "frozen": false, }, @@ -22890,7 +22890,7 @@ exports[`OSB Creatables 1`] = ` "noCl": true, "outputItems": Bank { "bank": { - "26809": 1, + "26811": 1, }, "frozen": false, }, From c2f4723870004c42d54ed69c9ef1ba4cc73df2be Mon Sep 17 00:00:00 2001 From: TastyPumPum <79149170+TastyPumPum@users.noreply.github.com> Date: Tue, 2 Jul 2024 16:11:57 +0100 Subject: [PATCH 029/249] BSO Wildy Changes (#5926) --- .../killableMonsters/custom/demiBosses.ts | 6 +- .../killableMonsters/krystiliaMonsters.ts | 1 + .../lib/abstracted_commands/minionKill.ts | 55 +++++-------------- src/mahoji/mahojiSettings.ts | 4 +- 4 files changed, 21 insertions(+), 45 deletions(-) diff --git a/src/lib/minions/data/killableMonsters/custom/demiBosses.ts b/src/lib/minions/data/killableMonsters/custom/demiBosses.ts index e8b182432c8..ad5a0a3f033 100644 --- a/src/lib/minions/data/killableMonsters/custom/demiBosses.ts +++ b/src/lib/minions/data/killableMonsters/custom/demiBosses.ts @@ -103,7 +103,8 @@ const Malygos: CustomMonster = { uniques: resolveItems(['Abyssal thread', 'Abyssal cape', 'Ori', 'Dragon hunter lance']), notifyDrops: resolveItems(['Abyssal cape', 'Ori']), baseMonster: Monsters.Vorkath, - customMonsterData: { attributes: [MonsterAttribute.Dragon, MonsterAttribute.Fiery] } + customMonsterData: { attributes: [MonsterAttribute.Dragon, MonsterAttribute.Fiery] }, + canBePked: true }; const Treebeard: CustomMonster = { @@ -139,7 +140,8 @@ const Treebeard: CustomMonster = { resolveNameBank({ 'Axe of the high sungod': 10 }) - ] + ], + canBePked: true }; export const QueenBlackDragon: CustomMonster = { diff --git a/src/lib/minions/data/killableMonsters/krystiliaMonsters.ts b/src/lib/minions/data/killableMonsters/krystiliaMonsters.ts index c1967ce7a28..99fe3e1556b 100644 --- a/src/lib/minions/data/killableMonsters/krystiliaMonsters.ts +++ b/src/lib/minions/data/killableMonsters/krystiliaMonsters.ts @@ -15,6 +15,7 @@ export const krystiliaMonsters: KillableMonster[] = [ difficultyRating: 5, qpRequired: 0, + canCannon: true, pkActivityRating: 1, pkBaseDeathChance: 1, revsWeaponBoost: true, diff --git a/src/mahoji/lib/abstracted_commands/minionKill.ts b/src/mahoji/lib/abstracted_commands/minionKill.ts index 6e365538dc3..660bcb70fea 100644 --- a/src/mahoji/lib/abstracted_commands/minionKill.ts +++ b/src/mahoji/lib/abstracted_commands/minionKill.ts @@ -338,7 +338,7 @@ export async function minionKillCommand( } } - for (const [itemID, boostAmount] of Object.entries(resolveAvailableItemBoosts(user, monster, isInWilderness))) { + for (const [itemID, boostAmount] of Object.entries(resolveAvailableItemBoosts(user, monster))) { timeToFinish *= (100 - boostAmount) / 100; boosts.push(`${boostAmount}% for ${itemNameFromID(parseInt(itemID))}`); } @@ -394,13 +394,8 @@ export async function minionKillCommand( } function applyDragonBoost() { - const hasDragonLance = isInWilderness - ? wildyGear.hasEquipped('Dragon hunter lance') - : user.hasEquippedOrInBank('Dragon hunter lance'); - const hasDragonCrossbow = isInWilderness - ? wildyGear.hasEquipped('Dragon hunter crossbow') - : user.hasEquippedOrInBank('Dragon hunter crossbow'); - + const hasDragonLance = user.hasEquippedOrInBank('Dragon hunter lance'); + const hasDragonCrossbow = user.hasEquippedOrInBank('Dragon hunter crossbow'); if ( (hasDragonLance && !attackStyles.includes(SkillsEnum.Ranged) && !attackStyles.includes(SkillsEnum.Magic)) || (hasDragonCrossbow && attackStyles.includes(SkillsEnum.Ranged)) @@ -414,18 +409,10 @@ export async function minionKillCommand( } function applyBlackMaskBoost() { - const hasInfernalSlayerHelmI = isInWilderness - ? wildyGear.hasEquipped('Infernal slayer helmet(i)') - : user.hasEquippedOrInBank('Infernal slayer helmet(i)'); - const hasInfernalSlayerHelm = isInWilderness - ? wildyGear.hasEquipped('Infernal slayer helmet') - : user.hasEquippedOrInBank('Infernal slayer helmet'); - const hasBlackMask = isInWilderness - ? wildyGear.hasEquipped('Black mask') - : user.hasEquippedOrInBank('Black mask'); - const hasBlackMaskI = isInWilderness - ? wildyGear.hasEquipped('Black mask (i)') - : user.hasEquippedOrInBank('Black mask (i)'); + const hasInfernalSlayerHelmI = user.hasEquippedOrInBank('Infernal slayer helmet(i)'); + const hasInfernalSlayerHelm = user.hasEquippedOrInBank('Infernal slayer helmet'); + const hasBlackMask = user.hasEquippedOrInBank('Black mask'); + const hasBlackMaskI = user.hasEquippedOrInBank('Black mask (i)'); if (attackStyles.includes(SkillsEnum.Ranged) || attackStyles.includes(SkillsEnum.Magic)) { if (hasBlackMaskI) { @@ -451,12 +438,8 @@ export async function minionKillCommand( let salveEnhanced = false; const style = attackStyles[0]; if (style === 'ranged' || style === 'magic') { - salveBoost = isInWilderness - ? wildyGear.hasEquipped('Salve amulet(i)') - : user.hasEquippedOrInBank('Salve amulet (i)'); - salveEnhanced = isInWilderness - ? wildyGear.hasEquipped('Salve amulet(ei)') - : user.hasEquippedOrInBank('Salve amulet (ei)'); + salveBoost = user.hasEquippedOrInBank('Salve amulet (i)'); + salveEnhanced = user.hasEquippedOrInBank('Salve amulet (ei)'); if (salveBoost) { salveAmuletBoost = salveEnhanced ? 20 : oneSixthBoost; salveAmuletBoostMsg = `${salveAmuletBoost}% for Salve amulet${ @@ -464,12 +447,8 @@ export async function minionKillCommand( } on non-melee task`; } } else { - salveBoost = isInWilderness - ? wildyGear.hasEquipped('Salve amulet') - : user.hasEquippedOrInBank('Salve amulet'); - salveEnhanced = isInWilderness - ? wildyGear.hasEquipped('Salve amulet (e)') - : user.hasEquippedOrInBank('Salve amulet (e)'); + salveBoost = user.hasEquippedOrInBank('Salve amulet'); + salveEnhanced = user.hasEquippedOrInBank('Salve amulet (e)'); if (salveBoost) { salveAmuletBoost = salveEnhanced ? 20 : oneSixthBoost; salveAmuletBoostMsg = `${salveAmuletBoost}% for Salve amulet${ @@ -487,11 +466,7 @@ export async function minionKillCommand( let virtusPiecesEquipped = 0; for (const item of resolveItems(['Virtus mask', 'Virtus robe top', 'Virtus robe legs'])) { - if (isInWilderness) { - if (wildyGear.hasEquipped(item)) { - virtusPiecesEquipped += blackMaskBoost !== 0 && itemNameFromID(item) === 'Virtus mask' ? 0 : 1; - } - } else if (user.gear.mage.hasEquipped(item)) { + if (user.gear.mage.hasEquipped(item)) { virtusPiecesEquipped += blackMaskBoost !== 0 && itemNameFromID(item) === 'Virtus mask' ? 0 : 1; } } @@ -763,9 +738,7 @@ export async function minionKillCommand( convertPvmStylesToGearSetup(attackStyles).includes(degItem.attackStyle) && (monster.setupsUsed ? monster.setupsUsed.includes(degItem.attackStyle) : true); - const gearCheck = isInWilderness - ? user.gear.wildy.hasEquipped(degItem.item.id) - : user.gear[degItem.attackStyle].hasEquipped(degItem.item.id); + const gearCheck = user.gear[degItem.attackStyle].hasEquipped(degItem.item.id); if (isUsing && gearCheck) { // We assume they have enough charges, add the boost, and degrade at the end to avoid doing it twice. @@ -1070,7 +1043,7 @@ export async function minionKillCommand( let hasDied: boolean | undefined = undefined; let hasWildySupplies = undefined; - if (isInWilderness) { + if (isInWilderness && ![BSOMonsters.Malygos.id, BSOMonsters.Treebeard.id].includes(monster.id)) { await increaseWildEvasionXp(user, duration); thePkCount = 0; hasDied = false; diff --git a/src/mahoji/mahojiSettings.ts b/src/mahoji/mahojiSettings.ts index 01a32f60f94..a9f1f44ec79 100644 --- a/src/mahoji/mahojiSettings.ts +++ b/src/mahoji/mahojiSettings.ts @@ -310,7 +310,7 @@ export function hasMonsterRequirements(user: MUser, monster: KillableMonster) { return [true]; } -export function resolveAvailableItemBoosts(user: MUser, monster: KillableMonster, isInWilderness: boolean = false) { +export function resolveAvailableItemBoosts(user: MUser, monster: KillableMonster) { const boosts = new Bank(); if (monster.itemInBankBoosts) { for (const boostSet of monster.itemInBankBoosts) { @@ -320,7 +320,7 @@ export function resolveAvailableItemBoosts(user: MUser, monster: KillableMonster // find the highest boost that the player has for (const [itemID, boostAmount] of Object.entries(boostSet)) { const parsedId = parseInt(itemID); - if (isInWilderness ? !user.hasEquipped(parsedId) : !user.hasEquippedOrInBank(parsedId)) { + if (!user.hasEquippedOrInBank(parsedId)) { continue; } if (boostAmount > highestBoostAmount) { From 49e1f42b4007b10bacca395c8861804ea22874f5 Mon Sep 17 00:00:00 2001 From: nwjgit <69014816+nwjgit@users.noreply.github.com> Date: Tue, 2 Jul 2024 10:15:48 -0500 Subject: [PATCH 030/249] Peky boost for forestry (#5904) --- src/mahoji/commands/chop.ts | 7 ++++++- src/tasks/minions/woodcuttingActivity.ts | 9 ++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/mahoji/commands/chop.ts b/src/mahoji/commands/chop.ts index 895654f2f73..e314602c6f1 100644 --- a/src/mahoji/commands/chop.ts +++ b/src/mahoji/commands/chop.ts @@ -164,6 +164,7 @@ export const chopCommand: OSBMahojiCommand = { let wcLvl = skills.woodcutting; const farmingLvl = user.skillsAsLevels.farming; + const pekyBoost = user.usingPet('Peky'); // Ivy, Redwood logs, Logs, Sulliuscep, Farming patches, Woodcutting guild don't spawn forestry events if ( @@ -188,7 +189,11 @@ export const chopCommand: OSBMahojiCommand = { } } } else { - boosts.push('Participating in Forestry events'); + boosts.push( + `Participating in Forestry events${ + pekyBoost ? " (uniques are 5x as common thanks to Peky's help)" : '' + }` + ); } // Default bronze axe, last in the array diff --git a/src/tasks/minions/woodcuttingActivity.ts b/src/tasks/minions/woodcuttingActivity.ts index 5616128fe1e..11f125d0ca0 100644 --- a/src/tasks/minions/woodcuttingActivity.ts +++ b/src/tasks/minions/woodcuttingActivity.ts @@ -27,8 +27,11 @@ async function handleForestry({ user, duration, loot }: { user: MUser; duration: let strForestry = ''; const userWcLevel = user.skillLevel(SkillsEnum.Woodcutting); const chanceWcLevel = Math.min(userWcLevel, 99); - const eggChance = Math.ceil(2700 - ((chanceWcLevel - 1) * (2700 - 1350)) / 98); - const whistleChance = Math.ceil(90 - ((chanceWcLevel - 1) * (90 - 45)) / 98); + + const pekyBoost = user.usingPet('Peky') ? 5 : 1; + const eggChance = Math.ceil((2700 - ((chanceWcLevel - 1) * (2700 - 1350)) / 98) / pekyBoost); + const whistleChance = Math.ceil((90 - ((chanceWcLevel - 1) * (90 - 45)) / 98) / pekyBoost); + const garlandChance = Math.ceil(50 / pekyBoost); perTimeUnitChance(duration, 20, Time.Minute, async () => { const eventIndex = randInt(0, ForestryEvents.length - 1); @@ -98,7 +101,7 @@ async function handleForestry({ user, duration, loot }: { user: MUser; duration: break; case 8: // Enchantment Ritual eventInteraction = randInt(6, 8); // ritual circles - if (roll(50)) { + if (roll(garlandChance)) { loot.add('Petal garland'); } eventCounts[event.id]++; From 11d1487c467160ab3df382ba0478bc4e39c0b015 Mon Sep 17 00:00:00 2001 From: gc <30398469+gc@users.noreply.github.com> Date: Wed, 3 Jul 2024 03:55:40 +1000 Subject: [PATCH 031/249] Ignore cpuprof files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 297cc60a2f8..4fa9144c27f 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ node_modules/ .eslintcache /coverage +*.cpuprofile \ No newline at end of file From e9fd7b708845fe515a7560f22dd6b9a3475e2224 Mon Sep 17 00:00:00 2001 From: GC <30398469+gc@users.noreply.github.com> Date: Wed, 3 Jul 2024 09:48:03 +1000 Subject: [PATCH 032/249] Various things (#5934) --- .eslintignore | 7 - .eslintrc.json | 22 - .github/workflows/unit_tests.yml | 13 +- .gitignore | 4 +- README.md | 12 +- biome.json | 57 + package.json | 35 +- prisma/robochimp.prisma | 10 +- src/config.example.ts | 2 +- src/index.ts | 11 +- src/lib/DynamicButtons.ts | 7 +- src/lib/MUser.ts | 54 +- src/lib/PaginatedMessage.ts | 15 +- src/lib/Task.ts | 22 +- src/lib/addXP.ts | 10 +- src/lib/analytics.ts | 4 +- src/lib/badges.ts | 2 +- src/lib/bankImage.ts | 49 +- src/lib/clues/clueReqs.ts | 4 +- src/lib/clues/clueTiers.ts | 32 +- src/lib/clues/clueUtils.ts | 2 +- src/lib/clues/stashUnits.ts | 4 +- src/lib/collectionLogTask.ts | 39 +- src/lib/colosseum.ts | 15 +- src/lib/combat_achievements/caUtils.ts | 4 +- .../combat_achievements/combatAchievements.ts | 18 +- src/lib/combat_achievements/easy.ts | 2 +- src/lib/combat_achievements/elite.ts | 8 +- src/lib/combat_achievements/grandmaster.ts | 8 +- src/lib/combat_achievements/hard.ts | 6 +- src/lib/combat_achievements/master.ts | 8 +- src/lib/combat_achievements/medium.ts | 2 +- src/lib/constants.ts | 21 +- src/lib/data/Collections.ts | 76 +- src/lib/data/CollectionsExport.ts | 10 +- src/lib/data/OSB.commands.json | 35 +- src/lib/data/buyables/aerialFishBuyables.ts | 2 +- src/lib/data/buyables/buyables.ts | 8 +- src/lib/data/buyables/canifisClothes.ts | 2 +- src/lib/data/buyables/capes.ts | 2 +- src/lib/data/buyables/castleWars.ts | 2 +- src/lib/data/buyables/forestryBuyables.ts | 2 +- src/lib/data/buyables/frem.ts | 2 +- src/lib/data/buyables/gnomeClothes.ts | 2 +- .../buyables/guardiansOfTheRifBuyables.ts | 2 +- .../data/buyables/mairinsMarketBuyables.ts | 2 +- src/lib/data/buyables/mining.ts | 2 +- src/lib/data/buyables/perdu.ts | 2 +- src/lib/data/buyables/runes.ts | 2 +- .../data/buyables/shootingStarsBuyables.ts | 2 +- src/lib/data/buyables/skillCapeBuyables.ts | 2 +- src/lib/data/buyables/slayerBuyables.ts | 2 +- src/lib/data/buyables/titheFarmBuyables.ts | 2 +- src/lib/data/buyables/troubleBrewingShop.ts | 2 +- src/lib/data/cox.ts | 30 +- src/lib/data/creatables/amrod.ts | 2 +- src/lib/data/creatables/armorPacks.ts | 4 +- src/lib/data/creatables/caCreatables.ts | 2 +- src/lib/data/creatables/capes.ts | 2 +- src/lib/data/creatables/dragonfireShields.ts | 2 +- src/lib/data/creatables/dt.ts | 2 +- src/lib/data/creatables/forestryCreatables.ts | 2 +- src/lib/data/creatables/gracefulOutfits.ts | 2 +- .../guardiansOfTheRiftCreatables.ts | 2 +- src/lib/data/creatables/leagueCreatables.ts | 2 +- src/lib/data/creatables/lms.ts | 2 +- src/lib/data/creatables/mysticStaves.ts | 2 +- src/lib/data/creatables/nex.ts | 2 +- src/lib/data/creatables/ornaments.ts | 14 +- src/lib/data/creatables/shadesOfMorton.ts | 2 +- src/lib/data/creatables/slayer.ts | 2 +- src/lib/data/creatables/toa.ts | 2 +- src/lib/data/creatables/tob.ts | 2 +- src/lib/data/creatablesTable.txt | 14 +- src/lib/data/createables.ts | 4 +- src/lib/data/filterables.ts | 8 +- src/lib/data/leaguesBuyables.ts | 2 +- src/lib/data/misc.ts | 2 +- src/lib/data/similarItems.ts | 2 +- src/lib/data/tob.ts | 36 +- src/lib/degradeableItems.ts | 10 +- src/lib/diaries.ts | 5 +- src/lib/economyLogs.ts | 2 +- src/lib/events.ts | 9 +- src/lib/finishables.ts | 12 +- src/lib/geImage.ts | 34 +- src/lib/gear/functions/generateGearImage.ts | 12 +- .../functions/hasWildyHuntGearEquipped.ts | 2 +- src/lib/gear/functions/inverseOfStat.ts | 3 +- src/lib/gear/types.ts | 4 +- src/lib/giantsFoundry.ts | 2 +- src/lib/grandExchange.ts | 40 +- src/lib/growablePets.ts | 4 +- src/lib/handleNewCLItems.ts | 7 +- .../http/routes/webhooks/githubSponsors.ts | 5 +- src/lib/http/routes/webhooks/patreon.ts | 2 +- src/lib/http/types.ts | 4 +- src/lib/http/util.ts | 4 +- src/lib/implings.ts | 4 +- src/lib/lootTrack.ts | 10 +- src/lib/marketPrices.ts | 2 +- src/lib/mastery.ts | 2 +- src/lib/metrics.ts | 25 +- src/lib/minions/data/bankBackgrounds.ts | 2 +- src/lib/minions/data/combatConstants.ts | 18 +- .../data/killableMonsters/bosses/dt.ts | 4 +- .../data/killableMonsters/bosses/gwd.ts | 2 +- .../data/killableMonsters/bosses/misc.ts | 4 +- .../data/killableMonsters/bosses/wildy.ts | 3 +- .../killableMonsters/camdozaalMonsters.ts | 2 +- .../data/killableMonsters/chaeldarMonsters.ts | 2 +- .../data/killableMonsters/creatureCreation.ts | 2 +- .../minions/data/killableMonsters/index.ts | 2 +- .../data/killableMonsters/konarMonsters.ts | 2 +- .../killableMonsters/krystiliaMonsters.ts | 2 +- src/lib/minions/data/killableMonsters/low.ts | 2 +- .../data/killableMonsters/mazchnaMonsters.ts | 2 +- .../data/killableMonsters/nieveMonsters.ts | 2 +- .../data/killableMonsters/reanimated.ts | 2 +- src/lib/minions/data/killableMonsters/revs.ts | 2 +- .../data/killableMonsters/turaelMonsters.ts | 2 +- .../data/killableMonsters/vannakaMonsters.ts | 2 +- src/lib/minions/data/planks.ts | 2 +- src/lib/minions/data/plunder.ts | 4 +- src/lib/minions/data/quests.ts | 6 +- src/lib/minions/data/sepulchre.ts | 4 +- src/lib/minions/data/templeTrekking.ts | 4 +- src/lib/minions/farming/types.ts | 6 +- .../functions/addSkillingClueToLoot.ts | 2 +- src/lib/minions/functions/announceLoot.ts | 10 +- src/lib/minions/functions/autoFarm.ts | 6 +- src/lib/minions/functions/autoFarmFilters.ts | 6 +- src/lib/minions/functions/blowpipeCommand.ts | 12 +- .../minions/functions/calculateMonsterFood.ts | 5 +- src/lib/minions/functions/darkAltarCommand.ts | 4 +- .../minions/functions/decantPotionFromBank.ts | 2 +- .../functions/degradeableItemsCommand.ts | 10 +- .../functions/getUserBestGearFromBank.ts | 21 +- .../minions/functions/getUserFoodFromBank.ts | 2 +- .../functions/hasEnoughFoodForMonster.ts | 2 +- src/lib/minions/functions/index.ts | 22 +- .../functions/isImportantItemForMonster.ts | 2 +- src/lib/minions/functions/lmsSimCommand.ts | 28 +- .../minions/functions/reducedTimeForGroup.ts | 6 +- .../minions/functions/reducedTimeFromKC.ts | 2 +- .../minions/functions/removeFoodFromUser.ts | 4 +- src/lib/minions/functions/trainCommand.ts | 8 +- .../minions/functions/unequipAllCommand.ts | 5 +- src/lib/minions/types.ts | 34 +- src/lib/modals.ts | 2 +- src/lib/musicCape.ts | 6 +- src/lib/openables.ts | 10 +- src/lib/party.ts | 8 +- src/lib/patreon.ts | 12 +- src/lib/perkTiers.ts | 4 +- src/lib/poh/index.ts | 10 +- src/lib/poh/objects/amulets.ts | 2 +- src/lib/poh/objects/dungeon_decorations.ts | 2 +- src/lib/poh/objects/garden_decorations.ts | 2 +- src/lib/poh/objects/guards.ts | 2 +- src/lib/poh/objects/jewellery_boxes.ts | 2 +- src/lib/poh/objects/mounted_capes.ts | 2 +- src/lib/poh/objects/mounted_fish.ts | 2 +- src/lib/poh/objects/mounted_heads.ts | 2 +- src/lib/poh/objects/mounted_items.ts | 2 +- src/lib/poh/objects/pools.ts | 2 +- src/lib/poh/objects/prayer_altars.ts | 2 +- src/lib/poh/objects/prisons.ts | 2 +- src/lib/poh/objects/spellbook_altars.ts | 2 +- src/lib/poh/objects/teleports.ts | 2 +- src/lib/poh/objects/thrones.ts | 2 +- src/lib/poh/objects/torches.ts | 2 +- src/lib/pohImage.ts | 15 +- src/lib/premiumPatronTime.ts | 2 +- src/lib/randomEvents.ts | 2 +- src/lib/roboChimp.ts | 3 +- src/lib/rolesTask.ts | 55 +- src/lib/settings/minigames.ts | 2 +- src/lib/settings/prisma.ts | 13 +- src/lib/settings/settings.ts | 12 +- src/lib/shadesKeys.ts | 4 +- src/lib/simulation/nex.ts | 16 +- src/lib/simulation/tempoross.ts | 2 +- src/lib/simulation/toa.ts | 107 +- src/lib/simulation/tob.ts | 8 +- src/lib/simulation/wintertodt.ts | 8 +- .../functions/calcFarmingContracts.ts | 2 +- src/lib/skilling/functions/calcsFarming.ts | 3 +- src/lib/skilling/functions/calcsHunter.ts | 8 +- .../skilling/functions/calcsRunecrafting.ts | 4 +- .../skilling/functions/determineMiningTime.ts | 4 +- .../functions/determineWoodcuttingTime.ts | 6 +- src/lib/skilling/functions/getFarmingInfo.ts | 9 +- .../skilling/functions/questRequirements.ts | 2 +- src/lib/skilling/skills/agility.ts | 3 +- .../skills/construction/constructables.ts | 14 +- src/lib/skilling/skills/cooking/cooking.ts | 3 +- .../skilling/skills/cooking/leapingFish.ts | 2 +- .../skills/crafting/craftables/birdhouse.ts | 2 +- .../skills/crafting/craftables/built.ts | 2 +- .../skills/crafting/craftables/dragonhide.ts | 2 +- .../skills/crafting/craftables/gems.ts | 2 +- .../crafting/craftables/glassblowing.ts | 2 +- .../skills/crafting/craftables/gold.ts | 2 +- .../skills/crafting/craftables/index.ts | 2 +- .../skills/crafting/craftables/leather.ts | 2 +- .../skills/crafting/craftables/misc.ts | 2 +- .../skills/crafting/craftables/silver.ts | 2 +- .../skills/crafting/craftables/tanning.ts | 2 +- src/lib/skilling/skills/farming/allotments.ts | 2 +- src/lib/skilling/skills/farming/fruitTrees.ts | 2 +- src/lib/skilling/skills/farming/herbPlants.ts | 2 +- src/lib/skilling/skills/farming/hops.ts | 2 +- src/lib/skilling/skills/farming/index.ts | 3 +- .../skilling/skills/farming/specialPlants.ts | 2 +- src/lib/skilling/skills/farming/trees.ts | 2 +- src/lib/skilling/skills/firemaking.ts | 3 +- src/lib/skilling/skills/fishing.ts | 3 +- .../skills/fletching/fletchables/arrows.ts | 2 +- .../skills/fletching/fletchables/bolts.ts | 2 +- .../skills/fletching/fletchables/bows.ts | 2 +- .../skills/fletching/fletchables/crossbows.ts | 2 +- .../skills/fletching/fletchables/darts.ts | 2 +- .../skills/fletching/fletchables/index.ts | 2 +- .../skills/fletching/fletchables/javelins.ts | 2 +- .../skills/fletching/fletchables/shafts.ts | 2 +- .../skills/fletching/fletchables/shields.ts | 2 +- .../skills/fletching/fletchables/slayer.ts | 2 +- .../fletching/fletchables/tippedBolts.ts | 2 +- .../fletchables/tippedDragonBolts.ts | 2 +- .../skills/fletching/fletchables/tips.ts | 2 +- .../skills/herblore/mixables/barbMixes.ts | 2 +- .../skills/herblore/mixables/crush.ts | 2 +- .../skills/herblore/mixables/grimy.ts | 2 +- .../skills/herblore/mixables/index.ts | 2 +- .../skills/herblore/mixables/potions.ts | 2 +- .../skilling/skills/herblore/mixables/tar.ts | 2 +- .../herblore/mixables/unfinishedPotions.ts | 2 +- .../skilling/skills/hunter/aerialFishing.ts | 3 +- .../skills/hunter/creatures/birdSnaring.ts | 3 +- .../skills/hunter/creatures/boxTrapping.ts | 3 +- .../hunter/creatures/butterflyNetting.ts | 3 +- .../hunter/creatures/deadfallTrapping.ts | 3 +- .../skills/hunter/creatures/falconry.ts | 3 +- .../skilling/skills/hunter/creatures/index.ts | 2 +- .../hunter/creatures/magicBoxTrapping.ts | 3 +- .../skills/hunter/creatures/netTrapping.ts | 3 +- .../hunter/creatures/pitfallTrapping.ts | 3 +- .../skills/hunter/creatures/rabbitSnaring.ts | 3 +- .../skills/hunter/creatures/tracking.ts | 3 +- src/lib/skilling/skills/hunter/driftNet.ts | 3 +- src/lib/skilling/skills/index.ts | 3 +- src/lib/skilling/skills/mining.ts | 3 +- src/lib/skilling/skills/prayer.ts | 3 +- .../skilling/skills/smithing/blastables.ts | 2 +- .../skilling/skills/smithing/smeltables.ts | 2 +- .../skills/smithing/smithables/adamant.ts | 2 +- .../skills/smithing/smithables/bronze.ts | 2 +- .../skills/smithing/smithables/gold.ts | 2 +- .../skills/smithing/smithables/iron.ts | 2 +- .../skills/smithing/smithables/mithril.ts | 2 +- .../skills/smithing/smithables/rune.ts | 2 +- .../skills/smithing/smithables/steel.ts | 2 +- .../skills/woodcutting/woodcutting.ts | 3 +- src/lib/skilling/types.ts | 14 +- src/lib/slayer/constants.ts | 8 +- src/lib/slayer/slayerMasters.ts | 2 +- src/lib/slayer/slayerShop.ts | 2 +- src/lib/slayer/slayerUnlocks.ts | 101 +- src/lib/slayer/slayerUtil.ts | 52 +- src/lib/slayer/tasks/bossTasks.ts | 2 +- src/lib/slayer/tasks/chaeldarTasks.ts | 2 +- src/lib/slayer/tasks/duradelTasks.ts | 4 +- src/lib/slayer/tasks/index.ts | 2 +- src/lib/slayer/tasks/konarTasks.ts | 4 +- src/lib/slayer/tasks/krystiliaTasks.ts | 2 +- src/lib/slayer/tasks/mazchnaTasks.ts | 4 +- src/lib/slayer/tasks/nieveTasks.ts | 4 +- src/lib/slayer/tasks/turaelTasks.ts | 2 +- src/lib/slayer/tasks/vannakaTasks.ts | 4 +- src/lib/slayer/types.ts | 8 +- src/lib/sorts.ts | 2 +- src/lib/structures/Bank.ts | 9 +- src/lib/structures/Gear.ts | 17 +- src/lib/structures/GeneralBank.ts | 18 +- src/lib/structures/LastManStandingUsage.ts | 3 +- src/lib/structures/MUserStats.ts | 4 +- src/lib/structures/OldSchoolBotClient.ts | 13 +- src/lib/structures/PercentCounter.ts | 2 +- src/lib/structures/Requirements.ts | 30 +- src/lib/tableBank.ts | 2 +- src/lib/tickers.ts | 31 +- src/lib/types/index.ts | 2 +- src/lib/types/minions.ts | 6 +- src/lib/util.ts | 37 +- src/lib/util/activityInArea.ts | 8 +- src/lib/util/addSubTaskToActivityTask.ts | 18 +- src/lib/util/ashSanctifier.ts | 4 +- src/lib/util/cachedUserIDs.ts | 38 +- src/lib/util/calcConBonusXP.ts | 2 +- src/lib/util/calcDropRatesFromBank.ts | 6 +- src/lib/util/calcMassDurationQuantity.ts | 2 +- src/lib/util/calcMaxTripLength.ts | 4 +- src/lib/util/calcWildyPkChance.ts | 20 +- .../calculateGearLostOnDeathWilderness.ts | 62 +- src/lib/util/canvasUtil.ts | 14 +- src/lib/util/chart.ts | 2 +- src/lib/util/clLeaderboard.ts | 2 +- src/lib/util/clientSettings.ts | 2 +- src/lib/util/commandUsage.ts | 5 +- src/lib/util/equipMulti.ts | 12 +- src/lib/util/farmingHelpers.ts | 6 +- src/lib/util/findMonster.ts | 2 +- src/lib/util/getKCByName.ts | 4 +- src/lib/util/getOSItem.ts | 4 +- src/lib/util/giveaway.ts | 9 +- src/lib/util/globalInteractions.ts | 11 +- src/lib/util/handleMahojiConfirmation.ts | 16 +- src/lib/util/handleTripFinish.ts | 8 +- src/lib/util/interactionHelpers.ts | 7 +- src/lib/util/interactionReply.ts | 4 +- src/lib/util/itemIsTradeable.ts | 3 +- src/lib/util/linkedAccountsUtil.ts | 4 +- src/lib/util/logError.ts | 2 +- src/lib/util/makeBankImage.ts | 8 +- src/lib/util/migrateUser.ts | 7 +- src/lib/util/minionStatsEmbed.ts | 12 +- src/lib/util/minionStatus.ts | 78 +- src/lib/util/minionUtils.ts | 9 +- src/lib/util/parseStringBank.ts | 30 +- src/lib/util/repeatStoredTrip.ts | 21 +- src/lib/util/smallUtils.ts | 25 +- src/lib/util/statsEmbed.ts | 4 +- src/lib/util/taskGroupFromActivity.ts | 2 +- src/lib/util/transactItemsFromBank.ts | 8 +- src/lib/util/updateBankSetting.ts | 2 +- src/lib/util/userEvents.ts | 3 +- src/lib/util/userQueues.ts | 4 +- src/lib/util/webhook.ts | 25 +- src/lib/workers/casket.worker.ts | 4 +- src/lib/workers/finish.worker.ts | 4 +- src/lib/workers/index.ts | 4 +- src/lib/workers/kill.worker.ts | 14 +- src/mahoji/commands/activities.ts | 15 +- src/mahoji/commands/admin.ts | 75 +- src/mahoji/commands/ask.ts | 5 +- src/mahoji/commands/bank.ts | 21 +- src/mahoji/commands/bingo.ts | 44 +- src/mahoji/commands/bossrecords.ts | 12 +- src/mahoji/commands/botleagues.ts | 9 +- src/mahoji/commands/bs.ts | 5 +- src/mahoji/commands/build.ts | 15 +- src/mahoji/commands/buy.ts | 21 +- src/mahoji/commands/ca.ts | 19 +- src/mahoji/commands/casket.ts | 5 +- src/mahoji/commands/choose.ts | 5 +- src/mahoji/commands/chop.ts | 12 +- src/mahoji/commands/cl.ts | 30 +- src/mahoji/commands/claim.ts | 7 +- src/mahoji/commands/clue.ts | 16 +- src/mahoji/commands/clues.ts | 5 +- src/mahoji/commands/config.ts | 53 +- src/mahoji/commands/cook.ts | 7 +- src/mahoji/commands/craft.ts | 7 +- src/mahoji/commands/create.ts | 15 +- src/mahoji/commands/data.ts | 5 +- src/mahoji/commands/drop.ts | 7 +- src/mahoji/commands/drycalc.ts | 5 +- src/mahoji/commands/fake.ts | 8 +- src/mahoji/commands/fakepm.ts | 5 +- src/mahoji/commands/farming.ts | 17 +- src/mahoji/commands/finish.ts | 5 +- src/mahoji/commands/fish.ts | 11 +- src/mahoji/commands/fletch.ts | 11 +- src/mahoji/commands/gamble.ts | 9 +- src/mahoji/commands/ge.ts | 26 +- src/mahoji/commands/gear.ts | 8 +- src/mahoji/commands/gearpresets.ts | 25 +- src/mahoji/commands/gift.ts | 9 +- src/mahoji/commands/giveaway.ts | 13 +- src/mahoji/commands/gp.ts | 4 +- src/mahoji/commands/help.ts | 2 +- src/mahoji/commands/hunt.ts | 21 +- src/mahoji/commands/invite.ts | 2 +- src/mahoji/commands/k.ts | 8 +- src/mahoji/commands/kc.ts | 7 +- src/mahoji/commands/kill.ts | 5 +- src/mahoji/commands/laps.ts | 9 +- src/mahoji/commands/leaderboard.ts | 42 +- src/mahoji/commands/leagues.ts | 5 +- src/mahoji/commands/light.ts | 7 +- src/mahoji/commands/loot.ts | 5 +- src/mahoji/commands/lvl.ts | 7 +- src/mahoji/commands/m.ts | 4 +- src/mahoji/commands/mass.ts | 17 +- src/mahoji/commands/mine.ts | 13 +- src/mahoji/commands/minigames.ts | 40 +- src/mahoji/commands/minion.ts | 15 +- src/mahoji/commands/mix.ts | 7 +- src/mahoji/commands/offer.ts | 19 +- src/mahoji/commands/open.ts | 9 +- src/mahoji/commands/patreon.ts | 2 +- src/mahoji/commands/pay.ts | 7 +- src/mahoji/commands/poh.ts | 7 +- src/mahoji/commands/poll.ts | 5 +- src/mahoji/commands/price.ts | 4 +- src/mahoji/commands/raid.ts | 15 +- src/mahoji/commands/redeem.ts | 7 +- src/mahoji/commands/roll.ts | 5 +- src/mahoji/commands/rp.ts | 31 +- src/mahoji/commands/runecraft.ts | 9 +- src/mahoji/commands/sacrifice.ts | 9 +- src/mahoji/commands/sell.ts | 15 +- src/mahoji/commands/simulate.ts | 9 +- src/mahoji/commands/slayer.ts | 27 +- src/mahoji/commands/smelt.ts | 9 +- src/mahoji/commands/smith.ts | 13 +- src/mahoji/commands/stats.ts | 8 +- src/mahoji/commands/steal.ts | 12 +- src/mahoji/commands/testpotato.ts | 43 +- src/mahoji/commands/tokkulshop.ts | 21 +- src/mahoji/commands/tools.ts | 76 +- src/mahoji/commands/trade.ts | 9 +- src/mahoji/commands/trivia.ts | 9 +- src/mahoji/commands/use.ts | 4 +- src/mahoji/commands/wiki.ts | 5 +- src/mahoji/commands/xp.ts | 7 +- src/mahoji/guildSettings.ts | 4 +- .../achievementDiaryCommand.ts | 5 +- .../aerialFishingCommand.ts | 2 +- .../agilityArenaCommand.ts | 8 +- .../lib/abstracted_commands/alchCommand.ts | 6 +- .../abstracted_commands/autoSlayCommand.ts | 37 +- .../lib/abstracted_commands/bankBgCommand.ts | 4 +- .../lib/abstracted_commands/barbAssault.ts | 34 +- .../abstracted_commands/birdhousesCommand.ts | 14 +- .../lib/abstracted_commands/buryCommand.ts | 2 +- .../lib/abstracted_commands/butlerCommand.ts | 14 +- .../buyFossilIslandNotes.ts | 4 +- .../abstracted_commands/camdozaalCommand.ts | 8 +- .../abstracted_commands/cancelTaskCommand.ts | 4 +- .../lib/abstracted_commands/capegamble.ts | 4 +- .../lib/abstracted_commands/castCommand.ts | 4 +- .../chargeGloriesCommand.ts | 4 +- .../chargeWealthCommand.ts | 4 +- .../abstracted_commands/chompyHuntCommand.ts | 6 +- .../lib/abstracted_commands/collectCommand.ts | 18 +- .../lib/abstracted_commands/coxCommand.ts | 16 +- .../lib/abstracted_commands/crackerCommand.ts | 2 +- .../cutLeapingFishCommand.ts | 6 +- .../lib/abstracted_commands/dailyCommand.ts | 12 +- .../lib/abstracted_commands/diceCommand.ts | 4 +- .../abstracted_commands/driftNetCommand.ts | 9 +- .../lib/abstracted_commands/duelCommand.ts | 9 +- .../lib/abstracted_commands/enchantCommand.ts | 4 +- .../lib/abstracted_commands/farmingCommand.ts | 13 +- .../farmingContractCommand.ts | 8 +- .../abstracted_commands/fightCavesCommand.ts | 15 +- .../lib/abstracted_commands/fishingTrawler.ts | 2 +- .../abstracted_commands/gauntletCommand.ts | 10 +- .../lib/abstracted_commands/gearCommands.ts | 15 +- .../giantsFoundryCommand.ts | 16 +- .../gnomeRestaurantCommand.ts | 4 +- .../guardiansOfTheRiftCommand.ts | 8 +- .../lib/abstracted_commands/hotColdCommand.ts | 9 +- .../lib/abstracted_commands/infernoCommand.ts | 44 +- .../lib/abstracted_commands/ironmanCommand.ts | 6 +- .../lib/abstracted_commands/lampCommand.ts | 6 +- .../lib/abstracted_commands/lmsCommand.ts | 2 +- .../abstracted_commands/luckyPickCommand.ts | 42 +- .../mageTrainingArenaCommand.ts | 2 +- .../mahoganyHomesCommand.ts | 10 +- .../abstracted_commands/minionBuyCommand.ts | 2 +- .../lib/abstracted_commands/minionKill.ts | 90 +- .../minionStatusCommand.ts | 3 +- .../motherlodeMineCommand.ts | 4 +- .../lib/abstracted_commands/nexCommand.ts | 7 +- .../abstracted_commands/nightmareCommand.ts | 26 +- .../nightmareZoneCommand.ts | 16 +- .../lib/abstracted_commands/openCommand.ts | 13 +- .../abstracted_commands/pestControlCommand.ts | 24 +- .../lib/abstracted_commands/pohCommand.ts | 4 +- .../abstracted_commands/puroPuroCommand.ts | 6 +- .../pyramidPlunderCommand.ts | 4 +- .../lib/abstracted_commands/questCommand.ts | 4 +- .../abstracted_commands/roguesDenCommand.ts | 2 +- .../lib/abstracted_commands/sawmillCommand.ts | 12 +- .../lib/abstracted_commands/scatterCommand.ts | 2 +- .../abstracted_commands/sepulchreCommand.ts | 4 +- .../shadesOfMortonCommand.ts | 10 +- .../shootingStarsCommand.ts | 15 +- .../abstracted_commands/slayerShopCommand.ts | 8 +- .../abstracted_commands/slayerTaskCommand.ts | 66 +- .../lib/abstracted_commands/slotsCommand.ts | 19 +- .../abstracted_commands/soulWarsCommand.ts | 4 +- .../abstracted_commands/stashUnitsCommand.ts | 9 +- .../lib/abstracted_commands/statCommand.ts | 105 +- .../tearsOfGuthixCommand.ts | 2 +- .../abstracted_commands/temporossCommand.ts | 4 +- .../tiaraRunecraftCommand.ts | 2 +- .../abstracted_commands/titheFarmCommand.ts | 10 +- .../lib/abstracted_commands/tobCommand.ts | 24 +- .../lib/abstracted_commands/trekCommand.ts | 30 +- .../troubleBrewingCommand.ts | 4 +- .../abstracted_commands/underwaterCommand.ts | 11 +- .../lib/abstracted_commands/useCommand.ts | 2 +- .../volcanicMineCommand.ts | 12 +- .../warriorsGuildCommand.ts | 4 +- .../abstracted_commands/wintertodtCommand.ts | 6 +- .../lib/abstracted_commands/zalcanoCommand.ts | 4 +- src/mahoji/lib/bingo/BingoManager.ts | 11 +- src/mahoji/lib/bingo/bingoUtil.ts | 2 +- src/mahoji/lib/bingo/globalTiles.ts | 4 +- src/mahoji/lib/events.ts | 10 +- src/mahoji/lib/inhibitors.ts | 16 +- src/mahoji/lib/mahojiCommandOptions.ts | 11 +- src/mahoji/lib/postCommand.ts | 4 +- src/mahoji/lib/preCommand.ts | 11 +- src/mahoji/lib/util.ts | 8 +- src/mahoji/mahojiSettings.ts | 14 +- src/scripts/build.ts | 147 ++ src/scripts/integration-tests.ts | 7 +- src/scripts/remove-item.ts | 8 +- .../{postbuild.ts => renderCommandsFile.ts} | 13 +- ...tables-file.ts => renderCreatablesFile.ts} | 11 +- .../HunterActivity/aerialFishingActivity.ts | 10 +- .../HunterActivity/birdhouseActivity.ts | 8 +- .../HunterActivity/driftNetActivity.ts | 6 +- .../minions/HunterActivity/hunterActivity.ts | 14 +- .../PrayerActivity/offeringActivity.ts | 2 +- .../PrayerActivity/scatteringActivity.ts | 4 +- src/tasks/minions/agilityActivity.ts | 8 +- src/tasks/minions/alchingActivity.ts | 6 +- src/tasks/minions/butlerActivity.ts | 4 +- .../camdozaalFishingActivity.ts | 10 +- .../camdozaalMiningActivity.ts | 10 +- .../camdozaalSmithingActivity.ts | 8 +- src/tasks/minions/castingActivity.ts | 6 +- src/tasks/minions/clueActivity.ts | 2 +- src/tasks/minions/collectingActivity.ts | 4 +- src/tasks/minions/colosseumActivity.ts | 7 +- src/tasks/minions/combatRingActivity.ts | 2 +- src/tasks/minions/constructionActivity.ts | 2 +- src/tasks/minions/cookingActivity.ts | 2 +- src/tasks/minions/craftingActivity.ts | 6 +- src/tasks/minions/cutLeapingFishActivity.ts | 8 +- src/tasks/minions/darkAltarActivity.ts | 4 +- src/tasks/minions/enchantingActivity.ts | 6 +- src/tasks/minions/farmingActivity.ts | 8 +- src/tasks/minions/firemakingActivity.ts | 6 +- src/tasks/minions/fishingActivity.ts | 16 +- src/tasks/minions/fletchingActivity.ts | 6 +- src/tasks/minions/gloryChargingActivity.ts | 4 +- src/tasks/minions/groupMonsterActivity.ts | 8 +- src/tasks/minions/herbloreActivity.ts | 10 +- src/tasks/minions/mageArena2Activity.ts | 4 +- src/tasks/minions/mageArenaActivity.ts | 4 +- .../minions/minigames/agilityArenaActivity.ts | 8 +- .../minigames/barbarianAssaultActivity.ts | 4 +- .../minions/minigames/castleWarsActivity.ts | 2 +- .../minigames/championsChallengeActivity.ts | 2 +- .../minions/minigames/chompyHuntActivity.ts | 4 +- .../minions/minigames/fightCavesActivity.ts | 12 +- .../minions/minigames/gauntletActivity.ts | 5 +- .../minigames/giantsFoundryActivity.ts | 14 +- .../minigames/gnomeRestaurantActivity.ts | 4 +- .../minigames/guardiansOfTheRiftActivity.ts | 10 +- .../minions/minigames/infernoActivity.ts | 14 +- src/tasks/minions/minigames/lmsActivity.ts | 8 +- .../minigames/mageTrainingArenaActivity.ts | 8 +- .../minigames/mahoganyHomesActivity.ts | 2 +- .../minions/minigames/nightmareActivity.ts | 4 +- .../minigames/nightmareZoneActivity.ts | 6 +- .../minions/minigames/pestControlActivity.ts | 8 +- .../minions/minigames/plunderActivity.ts | 2 +- .../minions/minigames/puroPuroActivity.ts | 10 +- src/tasks/minions/minigames/raidsActivity.ts | 8 +- .../minigames/roguesDenMazeActivity.ts | 2 +- .../minions/minigames/sepulchreActivity.ts | 8 +- .../minigames/shadesOfMortonActivity.ts | 12 +- .../minions/minigames/soulWarsActivity.ts | 2 +- .../minigames/tearsOfGuthixActivity.ts | 6 +- .../minigames/templeTrekkingActivity.ts | 14 +- .../minions/minigames/temporossActivity.ts | 6 +- .../minions/minigames/titheFarmActivity.ts | 2 +- src/tasks/minions/minigames/toaActivity.ts | 14 +- src/tasks/minions/minigames/tobActivity.ts | 8 +- .../minions/minigames/trawlerActivity.ts | 2 +- .../minigames/troubleBrewingActivity.ts | 6 +- .../warriorsGuild/animatedArmourActivity.ts | 4 +- .../warriorsGuild/cyclopsActivity.ts | 6 +- .../minions/minigames/wintertodtActivity.ts | 8 +- .../minions/minigames/zalcanoActivity.ts | 2 +- src/tasks/minions/miningActivity.ts | 10 +- src/tasks/minions/monsterActivity.ts | 39 +- src/tasks/minions/motherlodeMineActivity.ts | 8 +- src/tasks/minions/nexActivity.ts | 9 +- src/tasks/minions/pickpocketActivity.ts | 11 +- src/tasks/minions/smithingActivity.ts | 2 +- .../minions/strongholdOfSecurityActivity.ts | 2 +- src/tasks/minions/tiaraRunecraftActivity.ts | 4 +- src/tasks/minions/tokkulShopActivity.ts | 2 +- src/tasks/minions/underwaterActivity.ts | 6 +- src/tasks/minions/volcanicMineActivity.ts | 4 +- src/tasks/minions/wealthChargingActivity.ts | 4 +- src/tasks/minions/woodcuttingActivity.ts | 50 +- tests/integration/MUser.test.ts | 8 +- tests/integration/allCommandsBase.test.ts | 16 +- tests/integration/commands/ironman.test.ts | 2 +- tests/integration/commands/sacrifice.test.ts | 7 +- tests/integration/gearFixing.test.ts | 4 +- tests/integration/grandExchange.test.ts | 16 +- tests/integration/migrateUser.test.ts | 34 +- tests/integration/misc.test.ts | 2 +- tests/integration/paymentConflict.test.ts | 5 +- tests/integration/rolesTask.test.ts | 3 +- tests/integration/tradeTransaction.test.ts | 56 +- tests/integration/trading.test.ts | 3 +- tests/integration/util.ts | 12 +- tests/unit/Gear.test.ts | 4 +- tests/unit/GeneralBank.test.ts | 8 +- tests/unit/bankBackgrounds.test.ts | 2 +- tests/unit/banksnapshots.test.ts | 6 +- tests/unit/calcPOHBoosts.test.ts | 2 +- tests/unit/circular.test.ts | 19 +- tests/unit/getUsersPerkTier.test.ts | 2 +- tests/unit/http/http.test.ts | 2 +- tests/unit/http/patreon.test.ts | 2 +- tests/unit/images.test.ts | 2 +- tests/unit/parseStringBank.test.ts | 7 +- tests/unit/sanity.test.ts | 4 +- tests/unit/setup.ts | 1 - tests/unit/similarItems.test.ts | 8 +- tests/unit/skillsMeetRequirements.test.ts | 4 +- tests/unit/slayer.test.ts | 18 +- .../snapshots/slayerUnlocks.snapshot.json | 53 + .../unit/util.getUserBestGearFromBank.test.ts | 4 +- tests/unit/util.test.ts | 10 +- tests/unit/utils.ts | 11 +- vitest.integration.config.mts | 32 +- vitest.unit.config.mts | 36 +- yarn.lock | 2213 ++--------------- 642 files changed, 3417 insertions(+), 5072 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc.json create mode 100644 biome.json create mode 100644 src/scripts/build.ts rename src/scripts/{postbuild.ts => renderCommandsFile.ts} (87%) rename src/scripts/{render-creatables-file.ts => renderCreatablesFile.ts} (73%) create mode 100644 tests/unit/snapshots/slayerUnlocks.snapshot.json diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 443036b3cfa..00000000000 --- a/.eslintignore +++ /dev/null @@ -1,7 +0,0 @@ -node_modules/ -dist/ -*.js -icon_cache/ -licenses/ -coverage/ -.github/ \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index ae0cfc51597..00000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "extends": ["@oldschoolgg"], - "rules": { - "@typescript-eslint/naming-convention": 0, - "@typescript-eslint/no-throw-literal": 0, - "@typescript-eslint/default-param-last": 0, - "no-return-await": "error", - "import/no-cycle": "error", - "@typescript-eslint/strict-boolean-expressions": "warn", - "@typescript-eslint/no-misused-promises": [ - "error", - { - "checksVoidReturn": false - } - ], - "@typescript-eslint/no-base-to-string": "error", - "eqeqeq": "error", - "no-shadow": "warn", - "@typescript-eslint/no-floating-promises": "warn", - "@typescript-eslint/ban-ts-comment": "off" - } -} diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 2f50f426678..877659f586a 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -39,18 +39,7 @@ jobs: popd - name: Copy env run: cp .env.test .env - - name: Generate Prisma Client - run: yarn gen - - name: Run ESLint on changed files - uses: tj-actions/eslint-changed-files@v21 - with: - skip_annotations: true - config_path: ".eslintrc.json" - ignore_path: ".eslintignore" - file_extensions: | - **/*.ts - **/*.tsx - name: Build - run: yarn build + run: yarn build --clean - name: Test run: yarn test:unit diff --git a/.gitignore b/.gitignore index 4fa9144c27f..74c797c22dd 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,6 @@ node_modules/ .eslintcache /coverage -*.cpuprofile \ No newline at end of file +*.cpuprofile + +cache.json \ No newline at end of file diff --git a/README.md b/README.md index 5ab549e99a3..a6247184e2c 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,6 @@ To suggest a new feature, [click here](https://github.com/oldschoolgg/oldschoolb Anyone is free to create PR's with improvements and additions to Old School Bot. -Please lint your code with the projects' [ESLint](https://eslint.org/) config. - Contributors are listed in this file, and given a Contributor role in the support server. If you have more questions, there are lots of helpful Contributors in the `#developers` channel on the Discord server. ### Setting up the bot to run locally for contributing @@ -93,3 +91,13 @@ You can also ask Magna to invite your Bot with your invite link above if you so ## Self Hosting Self hosting is not supported. + +## Notes + +### Profiling tests + +- node --cpu-prof --cpu-prof-dir=./profiling ./node_modules/vitest/vitest.mjs run --coverage --config vitest.unit.config.mts parseStringBank + +## Module graph + +- yarn build && npx madge --image graph2.svg ./dist/index.js diff --git a/biome.json b/biome.json new file mode 100644 index 00000000000..8b46743f065 --- /dev/null +++ b/biome.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", + "organizeImports": { + "enabled": true + }, + "files": { + "maxSize": 10000000, + "ignore": ["node_modules", "dist", "coverage", "profiling", "logs", "icon_cache"], + "include": ["**/*.ts", "**/*.mts", "**/*.json"] + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "a11y": { + "useAltText": "off", + "useKeyWithClickEvents": "off" + }, + "suspicious": { + "noExplicitAny": "warn", + "noAssignInExpressions": "warn", + "noAsyncPromiseExecutor": "off" + }, + "style": { + "noNonNullAssertion": "off", + "noParameterAssign": "off", + "useExponentiationOperator": "off", + "noUselessElse": "off" + }, + "correctness": { + "useExhaustiveDependencies": "warn", + "noUnnecessaryContinue": "warn", + "noConstructorReturn": "off" + }, + "complexity": { + "noExtraBooleanCast": "warn", + "noBannedTypes": "warn", + "noForEach": "warn" + } + } + }, + "formatter": { + "enabled": true, + "formatWithErrors": true, + "indentStyle": "tab", + "indentWidth": 4, + "lineWidth": 120, + "lineEnding": "lf" + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "trailingCommas": "none", + "arrowParentheses": "asNeeded" + } + } +} diff --git a/package.json b/package.json index 25d883eaeae..ffea0cecb98 100644 --- a/package.json +++ b/package.json @@ -1,22 +1,19 @@ { "scripts": { - "gen": "concurrently \"prisma generate --no-hints\" \"prisma generate --no-hints --schema prisma/robochimp.prisma\" && echo \"Generated Prisma Client\"", - "prettify": "prettier --use-tabs ./**/*.{js,md,json,yml} --write", - "lint": "concurrently \"yarn prettify\" \"eslint --quiet *.{ts,mts} \"{src,tests}/**/*.ts\" --fix\"", + "gen": "concurrently --raw \"prisma generate --no-hints\" \"prisma generate --no-hints --schema prisma/robochimp.prisma\" && echo \"Generated Prisma Client\"", + "prettify": "prettier --use-tabs ./**/*.{md,yml} --write", + "lint": "concurrently --timings --raw --kill-others-on-fail \"biome check --write --unsafe --diagnostic-level=error\" \"yarn prettify\"", "build:tsc": "tsc -p src", - "build": "concurrently \"yarn wipedist\" \"yarn gen\" && yarn pre:build && yarn build:tsc && yarn post:build && echo \"Finished Building\"", + "build": "tsx ./src/scripts/build.ts", "wipedist": "rimraf \"dist/\"", "start": "yarn build && concurrently \"tsc -w -p src\" \"node --enable-source-maps dist/\"", - "test": "concurrently \"tsc -p src\" \"yarn test:lint\" \"yarn test:unit\"", - "test:lint": "eslint --quiet *.{mts,ts} \"{src,tests}/**/*.ts\"", + "test": "concurrently --timings --raw --kill-others-on-fail \"tsc -p src\" \"yarn test:lint\" \"yarn test:unit\"", + "test:lint": "biome check --diagnostic-level=error", "test:unit": "vitest run --coverage --config vitest.unit.config.mts", "dev": "yarn wipedist && tsc -w -p src", "test:watch": "vitest --config vitest.unit.config.mts --coverage", "test:integration": "tsx ./src/scripts/integration-tests.ts", - "pre:build": "tsx ./src/scripts/render-creatables-file.ts", - "post:build": "tsx ./src/scripts/postbuild.ts", - "build:esbuild": "esbuild --bundle src/index.ts --outdir=dist --platform=node --loader:.node=file --external:canvas", - "run:node": "node --enable-source-maps dist/index.js" + "build:esbuild": "esbuild --bundle src/index.ts --outdir=dist --platform=node --loader:.node=file --external:canvas" }, "dependencies": { "@fastify/cors": "^8.2.0", @@ -36,6 +33,7 @@ "chart.js": "^3.7.0", "chartjs-node-canvas": "github:gc/ChartjsNodeCanvas#a598b6dd27c44351f235bca07ca4ee660121f289", "chartjs-plugin-datalabels": "^2.0.0", + "decimal.js": "^10.4.3", "deepmerge": "^4.3.1", "discord.js": "14.14.1", "dotenv": "^16.4.5", @@ -46,7 +44,6 @@ "lodash": "^4.17.21", "lru-cache": "^10.3.0", "mahoji": "^0.0.7", - "mathjs": "^13.0.0", "murmurhash": "^2.0.1", "node-cron": "^3.0.3", "node-fetch": "^2.6.7", @@ -60,31 +57,23 @@ "zod": "^3.23.8" }, "devDependencies": { - "@oldschoolgg/eslint-config": "^2.0.13", + "@biomejs/biome": "^1.8.3", "@oldschoolgg/ts-config": "^0.0.1", "@types/jest-image-snapshot": "^6.1.0", "@types/lodash": "^4.14.195", - "@types/madge": "^5.0.0", "@types/mitm": "^1.3.8", "@types/node": "^14.18.12", "@types/node-cron": "^3.0.7", "@types/node-fetch": "^2.6.1", - "@typescript-eslint/eslint-plugin": "^5.41.0", - "@typescript-eslint/parser": "^5.41.0", "@vitest/coverage-v8": "^1.6.0", "concurrently": "^8.2.2", "dotenv-cli": "^7.4.2", + "dpdm": "^3.14.0", "esbuild": "^0.22.0", - "eslint": "^8.36.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-prettier": "^4.2.1", - "eslint-plugin-simple-import-sort": "^8.0.0", - "eslint-plugin-unicorn": "^44.0.2", + "fast-glob": "^3.3.2", "jest-image-snapshot": "^6.2.0", - "madge": "^7.0.0", "mitm": "^1.7.2", - "prettier": "^2.7.1", + "prettier": "^3.3.2", "prisma": "^5.13.0", "rimraf": "^5.0.7", "tsx": "^4.16.0", diff --git a/prisma/robochimp.prisma b/prisma/robochimp.prisma index e69c51ed5ae..f3e6cabce01 100644 --- a/prisma/robochimp.prisma +++ b/prisma/robochimp.prisma @@ -32,11 +32,11 @@ model BlacklistedEntity { } model User { - id BigInt @id @unique - bits Int[] - github_id Int? - patreon_id String? - migrated_user_id BigInt? + id BigInt @id @unique + bits Int[] + github_id Int? + patreon_id String? + migrated_user_id BigInt? leagues_completed_tasks_ids Int[] leagues_points_balance_osb Int @default(0) diff --git a/src/config.example.ts b/src/config.example.ts index 33deae92518..49dd199eea3 100644 --- a/src/config.example.ts +++ b/src/config.example.ts @@ -1,4 +1,4 @@ -import { IDiscordSettings } from './lib/types'; +import type { IDiscordSettings } from './lib/types'; export const botToken = ''; export const production = false; diff --git a/src/index.ts b/src/index.ts index e211401a80c..96325d11ab7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,24 +6,25 @@ import './lib/data/trophies'; import './lib/itemMods'; import './lib/geImage'; +import { join } from 'node:path'; import * as Sentry from '@sentry/node'; import { Chart } from 'chart.js'; import ChartDataLabels from 'chartjs-plugin-datalabels'; -import { GatewayIntentBits, Options, Partials, TextChannel } from 'discord.js'; +import type { TextChannel } from 'discord.js'; +import { GatewayIntentBits, Options, Partials } from 'discord.js'; import { isObject } from 'e'; import { MahojiClient } from 'mahoji'; -import { join } from 'path'; -import { botToken, DEV_SERVER_ID, SENTRY_DSN, SupportServer } from './config'; +import { DEV_SERVER_ID, SENTRY_DSN, SupportServer, botToken } from './config'; +import { syncActivityCache } from './lib/Task'; import { BLACKLISTED_GUILDS, BLACKLISTED_USERS } from './lib/blacklists'; -import { Channel, Events, globalConfig, META_CONSTANTS } from './lib/constants'; +import { Channel, Events, META_CONSTANTS, globalConfig } from './lib/constants'; import { economyLog } from './lib/economyLogs'; import { onMessage } from './lib/events'; import { makeServer } from './lib/http'; import { modalInteractionHook } from './lib/modals'; import { runStartupScripts } from './lib/startupScripts'; import { OldSchoolBotClient } from './lib/structures/OldSchoolBotClient'; -import { syncActivityCache } from './lib/Task'; import { assert, runTimedLoggedFn } from './lib/util'; import { CACHED_ACTIVE_USER_IDS, syncActiveUserIDs } from './lib/util/cachedUserIDs'; import { interactionHook } from './lib/util/globalInteractions'; diff --git a/src/lib/DynamicButtons.ts b/src/lib/DynamicButtons.ts index ddf8d714e6c..d2ebaa8e45e 100644 --- a/src/lib/DynamicButtons.ts +++ b/src/lib/DynamicButtons.ts @@ -1,7 +1,5 @@ -import { +import type { BaseMessageOptions, - ButtonBuilder, - ButtonStyle, DMChannel, Message, MessageComponentInteraction, @@ -9,7 +7,8 @@ import { TextChannel, ThreadChannel } from 'discord.js'; -import { noOp, Time } from 'e'; +import { ButtonBuilder, ButtonStyle } from 'discord.js'; +import { Time, noOp } from 'e'; import murmurhash from 'murmurhash'; import { BLACKLISTED_USERS } from './blacklists'; diff --git a/src/lib/MUser.ts b/src/lib/MUser.ts index 85dc4bf603a..b28502d154e 100644 --- a/src/lib/MUser.ts +++ b/src/lib/MUser.ts @@ -1,43 +1,46 @@ import { mentionCommand } from '@oldschoolgg/toolkit'; import { UserError } from '@oldschoolgg/toolkit/dist/lib/UserError'; -import { GearSetupType, Prisma, User, UserStats, xp_gains_skill_enum } from '@prisma/client'; +import type { GearSetupType, Prisma, User, UserStats, xp_gains_skill_enum } from '@prisma/client'; import { userMention } from 'discord.js'; import { calcWhatPercent, objectEntries, percentChance, sumArr, uniqueArr } from 'e'; import { Bank } from 'oldschooljs'; -import { EquipmentSlot, Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; +import { EquipmentSlot } from 'oldschooljs/dist/meta/types'; import { timePerAlch } from '../mahoji/lib/abstracted_commands/alchCommand'; import { userStatsUpdate } from '../mahoji/mahojiSettings'; import { addXP } from './addXP'; import { userIsBusy } from './busyCounterCache'; import { ClueTiers } from './clues/clueTiers'; -import { CATier, CombatAchievements } from './combat_achievements/combatAchievements'; -import { badges, BitField, Emoji, projectiles, usernameCache } from './constants'; +import type { CATier } from './combat_achievements/combatAchievements'; +import { CombatAchievements } from './combat_achievements/combatAchievements'; +import { BitField, Emoji, badges, projectiles, usernameCache } from './constants'; import { bossCLItems } from './data/Collections'; import { allPetIDs } from './data/CollectionsExport'; import { getSimilarItems } from './data/similarItems'; import { degradeableItems } from './degradeableItems'; -import { GearSetup, UserFullGearSetup } from './gear/types'; +import type { GearSetup, UserFullGearSetup } from './gear/types'; import { handleNewCLItems } from './handleNewCLItems'; import { marketPriceOfBank } from './marketPrices'; import backgroundImages from './minions/data/bankBackgrounds'; -import { CombatOptionsEnum } from './minions/data/combatConstants'; +import type { CombatOptionsEnum } from './minions/data/combatConstants'; import { defaultFarmingContract } from './minions/farming'; -import { FarmingContract } from './minions/farming/types'; -import { AttackStyles } from './minions/functions'; +import type { FarmingContract } from './minions/farming/types'; +import type { AttackStyles } from './minions/functions'; import { blowpipeDarts, validateBlowpipeData } from './minions/functions/blowpipeCommand'; -import { AddXpParams, BlowpipeData, ClueBank } from './minions/types'; +import type { AddXpParams, BlowpipeData, ClueBank } from './minions/types'; import { getUsersPerkTier, syncPerkTierOfUser } from './perkTiers'; import { roboChimpUserFetch } from './roboChimp'; -import { getMinigameEntity, Minigames, MinigameScore } from './settings/minigames'; +import type { MinigameScore } from './settings/minigames'; +import { Minigames, getMinigameEntity } from './settings/minigames'; import { prisma } from './settings/prisma'; import { getFarmingInfoFromUser } from './skilling/functions/getFarmingInfo'; import Farming from './skilling/skills/farming'; import { SkillsEnum } from './skilling/types'; -import { BankSortMethod } from './sorts'; -import { ChargeBank } from './structures/Bank'; -import { defaultGear, Gear } from './structures/Gear'; -import { ItemBank, Skills } from './types'; +import type { BankSortMethod } from './sorts'; +import type { ChargeBank } from './structures/Bank'; +import { Gear, defaultGear } from './structures/Gear'; +import type { ItemBank, Skills } from './types'; import { addItemToBank, convertXPtoLVL, itemNameFromID } from './util'; import { determineRunes } from './util/determineRunes'; import { getKCByName } from './util/getKCByName'; @@ -46,7 +49,7 @@ import { logError } from './util/logError'; import { minionIsBusy } from './util/minionIsBusy'; import { minionName } from './util/minionUtils'; import resolveItems from './util/resolveItems'; -import { TransactItemsArgs } from './util/transactItemsFromBank'; +import type { TransactItemsArgs } from './util/transactItemsFromBank'; export async function mahojiUserSettingsUpdate(user: string | bigint, data: Prisma.UserUncheckedUpdateInput) { try { @@ -272,8 +275,7 @@ export class MUserClass { } async calcActualClues() { - const result: { id: number; qty: number }[] = - await prisma.$queryRawUnsafe(`SELECT (data->>'clueID')::int AS id, SUM((data->>'quantity')::int)::int AS qty + const result: { id: number; qty: number }[] = await prisma.$queryRawUnsafe(`SELECT (data->>'clueID')::int AS id, SUM((data->>'quantity')::int)::int AS qty FROM activity WHERE type = 'ClueCompletion' AND user_id = '${this.id}'::bigint @@ -430,7 +432,7 @@ GROUP BY data->>'clueID';`); return false; } } - return type === 'one' ? false : true; + return type !== 'one'; } getSkills(levels: boolean) { @@ -579,8 +581,8 @@ Charge your items using ${mentionCommand(globalClient, 'minion', 'charge')}.` const ammo = newRangeGear.ammo?.quantity; const projectileCategory = Object.values(projectiles).find(i => i.items.includes(equippedAmmo)); - if (hasAvas && projectileCategory!.savedByAvas) { - let ammoCopy = ammoRemove[1]; + if (hasAvas && projectileCategory?.savedByAvas) { + const ammoCopy = ammoRemove[1]; for (let i = 0; i < ammoCopy; i++) { if (percentChance(80)) { ammoRemove[1]--; @@ -591,10 +593,10 @@ Charge your items using ${mentionCommand(globalClient, 'minion', 'charge')}.` if (!ammo || ammo < ammoRemove[1]) throw new UserError( `Not enough ${ammoRemove[0].name} equipped in ${gearKey} gear, you need ${ - ammoRemove![1] + ammoRemove?.[1] } but you have only ${ammo}.` ); - newRangeGear.ammo!.quantity -= ammoRemove![1]; + newRangeGear.ammo!.quantity -= ammoRemove?.[1]; if (newRangeGear.ammo!.quantity <= 0) newRangeGear.ammo = null; const updateKey = options?.wildy ? 'gear_wildy' : 'gear_range'; updates[updateKey] = newRangeGear as any as Prisma.InputJsonObject; @@ -602,7 +604,7 @@ Charge your items using ${mentionCommand(globalClient, 'minion', 'charge')}.` if (dart) { if (hasAvas) { - let copyDarts = dart![1]; + const copyDarts = dart?.[1]; for (let i = 0; i < copyDarts; i++) { if (percentChance(80)) { realCost.remove(dart[0].id, 1); @@ -631,7 +633,7 @@ Charge your items using ${mentionCommand(globalClient, 'minion', 'charge')}.` ); } const bpData = { ...this.blowpipe }; - bpData.dartQuantity -= dart![1]; + bpData.dartQuantity -= dart?.[1]; bpData.scales -= scales; validateBlowpipeData(bpData); updates.blowpipe = bpData; @@ -765,7 +767,7 @@ Charge your items using ${mentionCommand(globalClient, 'minion', 'charge')}.` return this.caPoints() >= CombatAchievements[tier].rewardThreshold; } - buildTertiaryItemChanges(hasRingOfWealthI: boolean = false, inWildy: boolean = false, onTask: boolean = false) { + buildTertiaryItemChanges(hasRingOfWealthI = false, inWildy = false, onTask = false) { const changes = new Map(); const tiers = Object.keys(CombatAchievements) as Array; @@ -853,7 +855,7 @@ Charge your items using ${mentionCommand(globalClient, 'minion', 'charge')}.` } async validateEquippedGear() { - let itemsUnequippedAndRefunded = new Bank(); + const itemsUnequippedAndRefunded = new Bank(); for (const [gearSetupName, gearSetup] of Object.entries(this.gear) as [GearSetupType, GearSetup][]) { if (gearSetup['2h'] !== null) { if (gearSetup.weapon?.item) { diff --git a/src/lib/PaginatedMessage.ts b/src/lib/PaginatedMessage.ts index 848859469d0..678cbf969d6 100644 --- a/src/lib/PaginatedMessage.ts +++ b/src/lib/PaginatedMessage.ts @@ -1,16 +1,9 @@ import { UserError } from '@oldschoolgg/toolkit/dist/lib/UserError'; -import { - ActionRowBuilder, - BaseMessageOptions, - ButtonBuilder, - ButtonStyle, - ComponentType, - MessageEditOptions, - TextChannel -} from 'discord.js'; +import type { BaseMessageOptions, ComponentType, MessageEditOptions, TextChannel } from 'discord.js'; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; import { Time } from 'e'; -import { PaginatedMessagePage } from './util'; +import type { PaginatedMessagePage } from './util'; import { logError } from './util/logError'; const controlButtons: { @@ -97,7 +90,7 @@ export class PaginatedMessage { .setEmoji(i.emoji) ) ) - ] + ] }; } catch (err) { if (typeof err === 'string') return err; diff --git a/src/lib/Task.ts b/src/lib/Task.ts index 11f2692f2e6..be9b963a7a8 100644 --- a/src/lib/Task.ts +++ b/src/lib/Task.ts @@ -1,7 +1,16 @@ -import { Activity, activity_type_enum } from '@prisma/client'; -import { z, ZodSchema } from 'zod'; +import type { Activity } from '@prisma/client'; +import { activity_type_enum } from '@prisma/client'; +import type { ZodSchema } from 'zod'; +import { z } from 'zod'; import { production } from '../config'; +import { aerialFishingTask } from '../tasks/minions/HunterActivity/aerialFishingActivity'; +import { birdHouseTask } from '../tasks/minions/HunterActivity/birdhouseActivity'; +import { driftNetTask } from '../tasks/minions/HunterActivity/driftNetActivity'; +import { hunterTask } from '../tasks/minions/HunterActivity/hunterActivity'; +import { buryingTask } from '../tasks/minions/PrayerActivity/buryingActivity'; +import { offeringTask } from '../tasks/minions/PrayerActivity/offeringActivity'; +import { scatteringTask } from '../tasks/minions/PrayerActivity/scatteringActivity'; import { agilityTask } from '../tasks/minions/agilityActivity'; import { alchingTask } from '../tasks/minions/alchingActivity'; import { butlerTask } from '../tasks/minions/butlerActivity'; @@ -26,10 +35,6 @@ import { fletchingTask } from '../tasks/minions/fletchingActivity'; import { gloryChargingTask } from '../tasks/minions/gloryChargingActivity'; import { groupoMonsterTask } from '../tasks/minions/groupMonsterActivity'; import { herbloreTask } from '../tasks/minions/herbloreActivity'; -import { aerialFishingTask } from '../tasks/minions/HunterActivity/aerialFishingActivity'; -import { birdHouseTask } from '../tasks/minions/HunterActivity/birdhouseActivity'; -import { driftNetTask } from '../tasks/minions/HunterActivity/driftNetActivity'; -import { hunterTask } from '../tasks/minions/HunterActivity/hunterActivity'; import { mageArenaTwoTask } from '../tasks/minions/mageArena2Activity'; import { mageArenaTask } from '../tasks/minions/mageArenaActivity'; import { agilityArenaTask } from '../tasks/minions/minigames/agilityArenaActivity'; @@ -70,9 +75,6 @@ import { monsterTask } from '../tasks/minions/monsterActivity'; import { motherlodeMiningTask } from '../tasks/minions/motherlodeMineActivity'; import { nexTask } from '../tasks/minions/nexActivity'; import { pickpocketTask } from '../tasks/minions/pickpocketActivity'; -import { buryingTask } from '../tasks/minions/PrayerActivity/buryingActivity'; -import { offeringTask } from '../tasks/minions/PrayerActivity/offeringActivity'; -import { scatteringTask } from '../tasks/minions/PrayerActivity/scatteringActivity'; import { questingTask } from '../tasks/minions/questingActivity'; import { runecraftTask } from '../tasks/minions/runecraftActivity'; import { sawmillTask } from '../tasks/minions/sawmillActivity'; @@ -195,7 +197,7 @@ export async function processPendingActivities() { finish_date: production ? { lt: new Date() - } + } : undefined } }); diff --git a/src/lib/addXP.ts b/src/lib/addXP.ts index e6cec718fa1..4a2d375e196 100644 --- a/src/lib/addXP.ts +++ b/src/lib/addXP.ts @@ -1,13 +1,13 @@ import { formatOrdinal, toTitleCase } from '@oldschoolgg/toolkit'; import { UserEventType } from '@prisma/client'; import { bold } from 'discord.js'; -import { noOp, Time } from 'e'; +import { Time, noOp } from 'e'; import { convertXPtoLVL, toKMB } from 'oldschooljs/dist/util/util'; import { MAXING_MESSAGE, SupportServer } from '../config'; import { Events, LEVEL_99_XP, MAX_TOTAL_LEVEL, MAX_XP } from './constants'; import { skillEmoji } from './data/emojis'; -import { AddXpParams } from './minions/types'; +import type { AddXpParams } from './minions/types'; import { prisma } from './settings/prisma'; import Skills from './skilling/skills'; import { insertUserEvent } from './util/userEvents'; @@ -25,7 +25,7 @@ async function howManyMaxed() { (await Promise.all([prisma.$queryRawUnsafe(makeQuery(false)), prisma.$queryRawUnsafe(makeQuery(true))])) as any ) .map((i: any) => i[0].count) - .map((i: any) => parseInt(i)); + .map((i: any) => Number.parseInt(i)); return { normies, @@ -124,7 +124,7 @@ export async function addXP(user: MUser, params: AddXpParams): Promise { let str = `${skill.emoji} **${user.badgedUsername}'s** minion, ${ user.minionName }, just achieved level 99 in ${skillNameCased}! They are the ${formatOrdinal( - parseInt(usersWith.count) + 1 + Number.parseInt(usersWith.count) + 1 )} to get 99 ${skillNameCased}.`; if (user.isIronman) { @@ -135,7 +135,7 @@ export async function addXP(user: MUser, params: AddXpParams): Promise { >( `SELECT COUNT(*)::int FROM users WHERE "minion.ironman" = true AND "skills.${params.skillName}" >= ${LEVEL_99_XP};` ); - str += ` They are the ${formatOrdinal(parseInt(ironmenWith.count) + 1)} Ironman to get 99.`; + str += ` They are the ${formatOrdinal(Number.parseInt(ironmenWith.count) + 1)} Ironman to get 99.`; } globalClient.emit(Events.ServerNotification, str); } diff --git a/src/lib/analytics.ts b/src/lib/analytics.ts index 629e2a067de..a35f5c69563 100644 --- a/src/lib/analytics.ts +++ b/src/lib/analytics.ts @@ -1,6 +1,6 @@ import { ActivityGroup, globalConfig } from '../lib/constants'; import { prisma } from '../lib/settings/prisma'; -import { GroupMonsterActivityTaskOptions } from '../lib/types/minions'; +import type { GroupMonsterActivityTaskOptions } from '../lib/types/minions'; import { taskGroupFromActivity } from '../lib/util/taskGroupFromActivity'; async function calculateMinionTaskCounts() { @@ -45,7 +45,7 @@ export async function analyticsTick() { 'SELECT SUM("GP") AS count FROM users;' ].map(query => prisma.$queryRawUnsafe(query)) ) - ).map((result: any) => parseInt(result[0].count)) as number[]; + ).map((result: any) => Number.parseInt(result[0].count)) as number[]; const taskCounts = await calculateMinionTaskCounts(); const currentClientSettings = await await prisma.clientStorage.findFirst({ diff --git a/src/lib/badges.ts b/src/lib/badges.ts index c7fdc9cba61..15e7766b59b 100644 --- a/src/lib/badges.ts +++ b/src/lib/badges.ts @@ -1,4 +1,4 @@ -import { badges, BadgesEnum } from '../lib/constants'; +import { BadgesEnum, badges } from '../lib/constants'; import { prisma } from '../lib/settings/prisma'; export async function cacheBadges() { diff --git a/src/lib/bankImage.ts b/src/lib/bankImage.ts index 0afc8137c54..fa67b914550 100644 --- a/src/lib/bankImage.ts +++ b/src/lib/bankImage.ts @@ -1,23 +1,25 @@ -import { Canvas, GlobalFonts, Image, loadImage, SKRSContext2D } from '@napi-rs/canvas'; +import { existsSync } from 'node:fs'; +import * as fs from 'node:fs/promises'; +import * as path from 'node:path'; +import type { SKRSContext2D } from '@napi-rs/canvas'; +import { Canvas, GlobalFonts, Image, loadImage } from '@napi-rs/canvas'; import { cleanString, formatItemStackQuantity, generateHexColorForCashStack } from '@oldschoolgg/toolkit'; import { UserError } from '@oldschoolgg/toolkit/dist/lib/UserError'; import { AttachmentBuilder } from 'discord.js'; import { chunk, randInt, sumArr } from 'e'; -import { existsSync } from 'fs'; -import * as fs from 'fs/promises'; import fetch from 'node-fetch'; import { Bank } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; import { toKMB } from 'oldschooljs/dist/util/util'; -import * as path from 'path'; -import { BitField, BOT_TYPE, ItemIconPacks, PerkTier, toaPurpleItems } from '../lib/constants'; +import { BOT_TYPE, BitField, ItemIconPacks, PerkTier, toaPurpleItems } from '../lib/constants'; import { allCLItems } from '../lib/data/Collections'; import { filterableTypes } from '../lib/data/filterables'; import backgroundImages from '../lib/minions/data/bankBackgrounds'; -import { BankBackground, FlagMap, Flags } from '../lib/minions/types'; -import { BankSortMethod, BankSortMethods, sorts } from '../lib/sorts'; -import { ItemBank } from '../lib/types'; +import type { BankBackground, FlagMap, Flags } from '../lib/minions/types'; +import type { BankSortMethod } from '../lib/sorts'; +import { BankSortMethods, sorts } from '../lib/sorts'; +import type { ItemBank } from '../lib/types'; import { drawImageWithOutline, fillTextXTimesInCtx, getClippedRegionImage } from '../lib/util/canvasUtil'; import itemID from '../lib/util/itemID'; import { logError } from '../lib/util/logError'; @@ -243,7 +245,7 @@ function drawTitle(ctx: SKRSContext2D, title: string, canvas: Canvas) { // Draw Bank Title ctx.font = '16px RuneScape Bold 12'; const titleWidthPx = ctx.measureText(title); - let titleX = Math.floor(floor(canvas.width / 2) - titleWidthPx.width / 2); + const titleX = Math.floor(floor(canvas.width / 2) - titleWidthPx.width / 2); ctx.fillStyle = '#000000'; fillTextXTimesInCtx(ctx, title, titleX + 1, 22); @@ -292,8 +294,8 @@ export class BankImageTask { const basePath = './src/lib/resources/images/bank_backgrounds/spritesheet/'; const files = await fs.readdir(basePath); for (const file of files) { - const bgName: BGSpriteName = file.split('\\').pop()!.split('/').pop()!.split('.').shift()! as BGSpriteName; - let d = await loadImage(await fs.readFile(basePath + file)); + const bgName: BGSpriteName = file.split('\\').pop()?.split('/').pop()?.split('.').shift()! as BGSpriteName; + const d = await loadImage(await fs.readFile(basePath + file)); this._bgSpriteData = d; this.bgSpriteList[bgName] = { name: bgName, @@ -358,7 +360,7 @@ export class BankImageTask { // For each one, set a cache value that it exists. for (const fileName of filesInDir) { - this.itemIconsList.add(parseInt(path.parse(fileName).name)); + this.itemIconsList.add(Number.parseInt(path.parse(fileName).name)); } for (const pack of ItemIconPacks) { @@ -367,7 +369,7 @@ export class BankImageTask { for (const dir of directories) { const filesInThisDir = await fs.readdir(`./src/lib/resources/images/icon_packs/${pack.id}_${dir}`); for (const fileName of filesInThisDir) { - const themedItemID = parseInt(path.parse(fileName).name); + const themedItemID = Number.parseInt(path.parse(fileName).name); const image = await loadImage( `./src/lib/resources/images/icon_packs/${pack.id}_${dir}/${fileName}` ); @@ -482,13 +484,12 @@ export class BankImageTask { } } - getBgAndSprite(bankBgId: number = 1, user?: MUser) { - let background = this.backgroundImages.find(i => i.id === bankBgId)!; + getBgAndSprite(bankBgId = 1, user?: MUser) { + const background = this.backgroundImages.find(i => i.id === bankBgId)!; const currentContract = user?.farmingContract(); const isFarmingContractReadyToHarvest = Boolean( - currentContract && - currentContract.contract.hasContract && + currentContract?.contract.hasContract && currentContract.matchingPlantedCrop && currentContract.matchingPlantedCrop.ready ); @@ -540,7 +541,7 @@ export class BankImageTask { // Adds distance from side // 36 + 21 is the itemLength + the space between each item xLoc = 2 + 6 + (compact ? 9 : 20) + (i % itemsPerRow) * itemWidthSize; - let [item, quantity] = items[i]; + const [item, quantity] = items[i]; const itemImage = await this.getItemImage(item.id, user); const itemHeight = compact ? itemImage.height / 1 : itemImage.height; const itemWidth = compact ? itemImage.width / 1 : itemImage.width; @@ -593,7 +594,7 @@ export class BankImageTask { } if (bottomItemText) { - let text = + const text = typeof bottomItemText === 'number' ? toKMB(bottomItemText) : bottomItemText.toString().slice(0, 8); ctx.fillStyle = 'black'; fillTextXTimesInCtx(ctx, text, floor(xLoc), yLoc + distanceFromTop); @@ -690,7 +691,7 @@ export class BankImageTask { // Paging if (typeof page === 'number' && !isShowingFullBankImage) { - let pageLoot = chunked[page]; + const pageLoot = chunked[page]; if (!pageLoot) throw new UserError('You have no items on this page.'); items = pageLoot; } @@ -708,7 +709,7 @@ export class BankImageTask { itemSize * 1.5 ) - 2; - let { + const { sprite: bgSprite, uniqueSprite: hasBgSprite, background: bgImage, @@ -722,7 +723,7 @@ export class BankImageTask { currentCL !== undefined && bank.items().some(([item]) => !currentCL.has(item.id) && allCLItems.includes(item.id)); - let actualBackground = isPurple && bgImage.hasPurple ? bgImage.purpleImage! : backgroundImage; + const actualBackground = isPurple && bgImage.hasPurple ? bgImage.purpleImage! : backgroundImage; const hexColor = user?.user.bank_bg_hex; @@ -927,7 +928,7 @@ export async function drawChestLootImage(options: { name: fileName }); } - let spaceBetweenImages = 15; + const spaceBetweenImages = 15; const combinedCanvas = new Canvas( canvases[0].width * canvases.length + spaceBetweenImages * canvases.length, canvases[0].height diff --git a/src/lib/clues/clueReqs.ts b/src/lib/clues/clueReqs.ts index 94e49e244be..3523a3f9501 100644 --- a/src/lib/clues/clueReqs.ts +++ b/src/lib/clues/clueReqs.ts @@ -1,7 +1,7 @@ -import { Bank } from 'oldschooljs'; +import type { Bank } from 'oldschooljs'; import type { Skills } from '../types'; -import { ClueTier } from './clueTiers'; +import type { ClueTier } from './clueTiers'; import { allStashUnitTiers } from './stashUnits'; export interface ClueReq { diff --git a/src/lib/clues/clueTiers.ts b/src/lib/clues/clueTiers.ts index 892718d0ed1..f2699f51255 100644 --- a/src/lib/clues/clueTiers.ts +++ b/src/lib/clues/clueTiers.ts @@ -1,24 +1,24 @@ import { Time } from 'e'; import { Clues } from 'oldschooljs'; -import { BeginnerCasket, BeginnerClueTable } from 'oldschooljs/dist/simulation/clues/Beginner'; -import { EasyCasket, EasyClueTable } from 'oldschooljs/dist/simulation/clues/Easy'; -import { EliteCasket, EliteClueTable } from 'oldschooljs/dist/simulation/clues/Elite'; -import { HardCasket, HardClueTable } from 'oldschooljs/dist/simulation/clues/Hard'; -import { MasterCasket, MasterClueTable } from 'oldschooljs/dist/simulation/clues/Master'; -import { MediumCasket, MediumClueTable } from 'oldschooljs/dist/simulation/clues/Medium'; +import type { BeginnerCasket } from 'oldschooljs/dist/simulation/clues/Beginner'; +import { BeginnerClueTable } from 'oldschooljs/dist/simulation/clues/Beginner'; +import type { EasyCasket } from 'oldschooljs/dist/simulation/clues/Easy'; +import { EasyClueTable } from 'oldschooljs/dist/simulation/clues/Easy'; +import type { EliteCasket } from 'oldschooljs/dist/simulation/clues/Elite'; +import { EliteClueTable } from 'oldschooljs/dist/simulation/clues/Elite'; +import type { HardCasket } from 'oldschooljs/dist/simulation/clues/Hard'; +import { HardClueTable } from 'oldschooljs/dist/simulation/clues/Hard'; +import type { MasterCasket } from 'oldschooljs/dist/simulation/clues/Master'; +import { MasterClueTable } from 'oldschooljs/dist/simulation/clues/Master'; +import type { MediumCasket } from 'oldschooljs/dist/simulation/clues/Medium'; +import { MediumClueTable } from 'oldschooljs/dist/simulation/clues/Medium'; import itemID from '../util/itemID'; import resolveItems from '../util/resolveItems'; -import { beginnerReqs, ClueReqs } from './clueReqs'; -import { - beginnerStashes, - easyStashes, - eliteStashes, - hardStashes, - masterStashes, - mediumStashes, - StashUnitTier -} from './stashUnits'; +import type { ClueReqs } from './clueReqs'; +import { beginnerReqs } from './clueReqs'; +import type { StashUnitTier } from './stashUnits'; +import { beginnerStashes, easyStashes, eliteStashes, hardStashes, masterStashes, mediumStashes } from './stashUnits'; const { Beginner, Easy, Medium, Hard, Elite, Master } = Clues; diff --git a/src/lib/clues/clueUtils.ts b/src/lib/clues/clueUtils.ts index 2a19770411a..3d9ea5b4b39 100644 --- a/src/lib/clues/clueUtils.ts +++ b/src/lib/clues/clueUtils.ts @@ -1,5 +1,5 @@ import { ButtonBuilder, ButtonStyle } from 'discord.js'; -import { Bank } from 'oldschooljs'; +import type { Bank } from 'oldschooljs'; import { BitField } from '../constants'; import { ClueTiers } from './clueTiers'; diff --git a/src/lib/clues/stashUnits.ts b/src/lib/clues/stashUnits.ts index f092bd418c0..e5cf311236c 100644 --- a/src/lib/clues/stashUnits.ts +++ b/src/lib/clues/stashUnits.ts @@ -14,7 +14,7 @@ import { } from '../data/CollectionsExport'; import { allTeamCapes } from '../data/misc'; import resolveItems, { deepResolveItems } from '../util/resolveItems'; -import { ClueTier } from './clueTiers'; +import type { ClueTier } from './clueTiers'; export interface IStashUnit { id: number; @@ -612,4 +612,4 @@ export const allStashUnitTiers = [ masterStashes ]; -export const allStashUnitsFlat = allStashUnitTiers.map(i => i.units).flat(); +export const allStashUnitsFlat = allStashUnitTiers.flatMap(i => i.units); diff --git a/src/lib/collectionLogTask.ts b/src/lib/collectionLogTask.ts index 5f05190bf43..44ce4d38a53 100644 --- a/src/lib/collectionLogTask.ts +++ b/src/lib/collectionLogTask.ts @@ -1,14 +1,17 @@ -import { Canvas, SKRSContext2D } from '@napi-rs/canvas'; +import type { SKRSContext2D } from '@napi-rs/canvas'; +import { Canvas } from '@napi-rs/canvas'; import { formatItemStackQuantity, generateHexColorForCashStack } from '@oldschoolgg/toolkit'; import { calcWhatPercent, objectEntries } from 'e'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; -import { Bank, Util } from 'oldschooljs'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { Bank } from 'oldschooljs'; +import { Util } from 'oldschooljs'; -import { allCollectionLogs, getCollection, getTotalCl, UserStatsDataNeededForCL } from '../lib/data/Collections'; -import { IToReturnCollection } from '../lib/data/CollectionsExport'; +import type { UserStatsDataNeededForCL } from '../lib/data/Collections'; +import { allCollectionLogs, getCollection, getTotalCl } from '../lib/data/Collections'; +import type { IToReturnCollection } from '../lib/data/CollectionsExport'; import { fillTextXTimesInCtx, getClippedRegion, measureTextWidth } from '../lib/util/canvasUtil'; import getOSItem from '../lib/util/getOSItem'; -import { IBgSprite } from './bankImage'; +import type { IBgSprite } from './bankImage'; export const collectionLogTypes = [ { name: 'collection', description: 'Normal Collection Log' }, @@ -29,15 +32,7 @@ class CollectionLogTask { return bankImageGenerator.drawBorder(ctx, sprite); } - drawSquare( - ctx: SKRSContext2D, - x: number, - y: number, - w: number, - h: number, - pixelSize: number = 1, - hollow: boolean = true - ) { + drawSquare(ctx: SKRSContext2D, x: number, y: number, w: number, h: number, pixelSize = 1, hollow = true) { ctx.save(); if (hollow) { ctx.translate(0.5, 0.5); @@ -104,7 +99,7 @@ class CollectionLogTask { user: MUser; collection: string; type: CollectionLogType; - flags: { [key: string]: string | number }; + flags: { [key: string]: string | number | undefined }; stats: UserStatsDataNeededForCL | null; collectionLog?: IToReturnCollection; }): Promise { @@ -113,7 +108,7 @@ class CollectionLogTask { if (options.flags.temp) { options.type = 'temp'; } - let { collection, type, user, flags } = options; + const { collection, type, user, flags } = options; let collectionLog: IToReturnCollection | undefined | false = undefined; @@ -144,7 +139,7 @@ class CollectionLogTask { attachment: Buffer.from( collectionLog.collection .map(i => { - let _i = getOSItem(i); + const _i = getOSItem(i); const _q = (collectionLog as IToReturnCollection).userItems.amount(_i.id); if (_q === 0 && !flags.missing) return undefined; return `${flags.nq || flags.missing ? '' : `${_q}x `}${_i.name}`; @@ -160,7 +155,7 @@ class CollectionLogTask { // Disable tall flag when not showing left list if (flags.nl && flags.tall) { - delete flags.tall; + flags.tall = undefined; } const userCollectionBank = collectionLog.userItems; @@ -360,7 +355,7 @@ class CollectionLogTask { ctx.fillStyle = '#FF981F'; this.drawText(ctx, (drawnSoFar = collectionLog.isActivity ? 'Completions: ' : 'Kills: '), 0, 25); let pixelLevel = 25; - for (let [type, value] of objectEntries(collectionLog.completions)) { + for (const [type, value] of objectEntries(collectionLog.completions)) { if ( measureTextWidth(ctx, drawnSoFar) + measureTextWidth(ctx, ` / ${type}: `) + @@ -399,12 +394,12 @@ class CollectionLogTask { ctx.save(); ctx.font = '16px OSRSFontCompact'; ctx.fillStyle = generateHexColorForCashStack(totalPrice); - let value = Util.toKMB(totalPrice); + const value = Util.toKMB(totalPrice); this.drawText(ctx, value, ctx.canvas.width - 15 - ctx.measureText(value).width, 75 + 25); ctx.restore(); if (leftListCanvas && !fullSize) { - if (!Boolean(flags.tall)) { + if (!flags.tall) { let selectedPos = 8; const listItemSize = 15; for (const name of Object.keys(collectionLog.leftList!)) { diff --git a/src/lib/colosseum.ts b/src/lib/colosseum.ts index b708051cf77..4fc3cf719e9 100644 --- a/src/lib/colosseum.ts +++ b/src/lib/colosseum.ts @@ -1,6 +1,7 @@ import { mentionCommand } from '@oldschoolgg/toolkit'; import { UserError } from '@oldschoolgg/toolkit/dist/lib/UserError'; import { + Time, calcPercentOfNum, calcWhatPercent, clamp, @@ -10,21 +11,21 @@ import { percentChance, randInt, reduceNumByPercent, - sumArr, - Time + sumArr } from 'e'; import { Bank, LootTable } from 'oldschooljs'; -import { EquipmentSlot } from 'oldschooljs/dist/meta/types'; +import type { EquipmentSlot } from 'oldschooljs/dist/meta/types'; import { userStatsBankUpdate } from '../mahoji/mahojiSettings'; import { degradeChargeBank } from './degradeableItems'; -import { GearSetupType } from './gear/types'; +import type { GearSetupType } from './gear/types'; import { trackLoot } from './lootTrack'; import { QuestID } from './minions/data/quests'; import { ChargeBank } from './structures/Bank'; -import { GeneralBank, GeneralBankType } from './structures/GeneralBank'; -import { ItemBank, Skills } from './types'; -import { ColoTaskOptions } from './types/minions'; +import type { GeneralBankType } from './structures/GeneralBank'; +import { GeneralBank } from './structures/GeneralBank'; +import type { ItemBank, Skills } from './types'; +import type { ColoTaskOptions } from './types/minions'; import addSubTaskToActivityTask from './util/addSubTaskToActivityTask'; import resolveItems from './util/resolveItems'; import { exponentialPercentScale, formatDuration, formatSkillRequirements, itemNameFromID } from './util/smallUtils'; diff --git a/src/lib/combat_achievements/caUtils.ts b/src/lib/combat_achievements/caUtils.ts index f5092311fb7..a0f68d91918 100644 --- a/src/lib/combat_achievements/caUtils.ts +++ b/src/lib/combat_achievements/caUtils.ts @@ -1,6 +1,6 @@ -import { CAViewType } from '../../mahoji/commands/ca'; +import type { CAViewType } from '../../mahoji/commands/ca'; import type { ActivityTaskData, MonsterActivityTaskOptions } from '../types/minions'; -import { CombatAchievement } from './combatAchievements'; +import type { CombatAchievement } from './combatAchievements'; export function isCertainMonsterTrip(monsterID: number) { return (data: ActivityTaskData) => diff --git a/src/lib/combat_achievements/combatAchievements.ts b/src/lib/combat_achievements/combatAchievements.ts index 4a6574c39c7..cb34e45c401 100644 --- a/src/lib/combat_achievements/combatAchievements.ts +++ b/src/lib/combat_achievements/combatAchievements.ts @@ -1,12 +1,12 @@ -import { activity_type_enum } from '@prisma/client'; +import type { activity_type_enum } from '@prisma/client'; import { deepClone, notEmpty, roll, sumArr } from 'e'; -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; -import { Requirements } from '../structures/Requirements'; -import { ActivityTaskData, TOAOptions } from '../types/minions'; +import type { Requirements } from '../structures/Requirements'; +import type { ActivityTaskData, TOAOptions } from '../types/minions'; import { assert } from '../util'; import getOSItem from '../util/getOSItem'; -import { TripFinishEffect } from '../util/handleTripFinish'; +import type { TripFinishEffect } from '../util/handleTripFinish'; import { easyCombatAchievements } from './easy'; import { eliteCombatAchievements } from './elite'; import { grandmasterCombatAchievements } from './grandmaster'; @@ -152,12 +152,12 @@ for (const [, val] of entries) { assert(val.tasks.length === val.length, `${val.name} has ${val.tasks.length} tasks, but length is ${val.length}`); } -export const allCombatAchievementTasks = entries.map(i => i[1].tasks).flat(); +export const allCombatAchievementTasks = entries.flatMap(i => i[1].tasks); -const allCATaskIDs = entries.map(i => i[1].tasks.map(t => t.id)).flat(); +const allCATaskIDs = entries.flatMap(i => i[1].tasks.map(t => t.id)); assert(allCATaskIDs.length === new Set(allCATaskIDs).size); assert(sumArr(Object.values(CombatAchievements).map(i => i.length)) === allCATaskIDs.length); -const indexesWithRng = entries.map(i => i[1].tasks.filter(t => 'rng' in t)).flat(); +const indexesWithRng = entries.flatMap(i => i[1].tasks.filter(t => 'rng' in t)); export const combatAchievementTripEffect: TripFinishEffect['fn'] = async ({ data, messages }) => { const dataCopy = deepClone(data); @@ -169,7 +169,7 @@ export const combatAchievementTripEffect: TripFinishEffect['fn'] = async ({ data } if (!('quantity' in dataCopy)) return; let quantity = Number(dataCopy.quantity); - if (isNaN(quantity)) return; + if (Number.isNaN(quantity)) return; if (data.type === 'TombsOfAmascut') { const wipedRoom = (data as TOAOptions).wipedRoom ?? 0; diff --git a/src/lib/combat_achievements/easy.ts b/src/lib/combat_achievements/easy.ts index 2b5cd47f667..031e85d073f 100644 --- a/src/lib/combat_achievements/easy.ts +++ b/src/lib/combat_achievements/easy.ts @@ -6,7 +6,7 @@ import { Requirements } from '../structures/Requirements'; import getOSItem from '../util/getOSItem'; import resolveItems from '../util/resolveItems'; import { isCertainMonsterTrip } from './caUtils'; -import { type CombatAchievement } from './combatAchievements'; +import type { CombatAchievement } from './combatAchievements'; export const easyCombatAchievements: CombatAchievement[] = [ { diff --git a/src/lib/combat_achievements/elite.ts b/src/lib/combat_achievements/elite.ts index ac167d2a044..084a3857691 100644 --- a/src/lib/combat_achievements/elite.ts +++ b/src/lib/combat_achievements/elite.ts @@ -1,19 +1,19 @@ import { Monsters } from 'oldschooljs'; import { - demonBaneWeapons, MIMIC_MONSTER_ID, NEX_ID, NIGHTMARE_ID, PHOSANI_NIGHTMARE_ID, - ZALCANO_ID + ZALCANO_ID, + demonBaneWeapons } from '../constants'; import { anyoneDiedInTOARaid } from '../simulation/toa'; import { SkillsEnum } from '../skilling/types'; import { Requirements } from '../structures/Requirements'; -import { ActivityTaskData, GauntletOptions, NightmareActivityTaskOptions, TOAOptions } from '../types/minions'; +import type { ActivityTaskData, GauntletOptions, NightmareActivityTaskOptions, TOAOptions } from '../types/minions'; import { isCertainMonsterTrip } from './caUtils'; -import { type CombatAchievement } from './combatAchievements'; +import type { CombatAchievement } from './combatAchievements'; export const eliteCombatAchievements: CombatAchievement[] = [ { diff --git a/src/lib/combat_achievements/grandmaster.ts b/src/lib/combat_achievements/grandmaster.ts index 35e60e959ed..8736e995974 100644 --- a/src/lib/combat_achievements/grandmaster.ts +++ b/src/lib/combat_achievements/grandmaster.ts @@ -4,17 +4,17 @@ import { Monsters } from 'oldschooljs'; import { PHOSANI_NIGHTMARE_ID } from '../constants'; import { anyoneDiedInTOARaid } from '../simulation/toa'; import { Requirements } from '../structures/Requirements'; -import { +import type { ActivityTaskData, GauntletOptions, NexTaskOptions, NightmareActivityTaskOptions, RaidsOptions, - TheatreOfBloodTaskOptions, - TOAOptions + TOAOptions, + TheatreOfBloodTaskOptions } from '../types/minions'; import { isCertainMonsterTrip } from './caUtils'; -import { type CombatAchievement } from './combatAchievements'; +import type { CombatAchievement } from './combatAchievements'; export const grandmasterCombatAchievements: CombatAchievement[] = [ { diff --git a/src/lib/combat_achievements/hard.ts b/src/lib/combat_achievements/hard.ts index b5d51bfb57c..82f33c30776 100644 --- a/src/lib/combat_achievements/hard.ts +++ b/src/lib/combat_achievements/hard.ts @@ -1,11 +1,11 @@ import { Monsters } from 'oldschooljs'; -import { demonBaneWeapons, NIGHTMARE_ID } from '../constants'; +import { NIGHTMARE_ID, demonBaneWeapons } from '../constants'; import { anglerOutfit } from '../data/CollectionsExport'; import { Requirements } from '../structures/Requirements'; -import { TOAOptions } from '../types/minions'; +import type { TOAOptions } from '../types/minions'; import { isCertainMonsterTrip } from './caUtils'; -import { type CombatAchievement } from './combatAchievements'; +import type { CombatAchievement } from './combatAchievements'; export const hardCombatAchievements: CombatAchievement[] = [ { diff --git a/src/lib/combat_achievements/master.ts b/src/lib/combat_achievements/master.ts index 1f43f3e5b31..0ee97315f6d 100644 --- a/src/lib/combat_achievements/master.ts +++ b/src/lib/combat_achievements/master.ts @@ -4,18 +4,18 @@ import { Monsters } from 'oldschooljs'; import { NEX_ID, NIGHTMARE_ID, PHOSANI_NIGHTMARE_ID } from '../constants'; import { anyoneDiedInTOARaid } from '../simulation/toa'; import { Requirements } from '../structures/Requirements'; -import { +import type { ActivityTaskData, GauntletOptions, MonsterActivityTaskOptions, NightmareActivityTaskOptions, RaidsOptions, - TheatreOfBloodTaskOptions, - TOAOptions + TOAOptions, + TheatreOfBloodTaskOptions } from '../types/minions'; import resolveItems from '../util/resolveItems'; import { isCertainMonsterTrip } from './caUtils'; -import { type CombatAchievement } from './combatAchievements'; +import type { CombatAchievement } from './combatAchievements'; export const masterCombatAchievements: CombatAchievement[] = [ { diff --git a/src/lib/combat_achievements/medium.ts b/src/lib/combat_achievements/medium.ts index cbbc56bc37c..420f3c1b43e 100644 --- a/src/lib/combat_achievements/medium.ts +++ b/src/lib/combat_achievements/medium.ts @@ -3,7 +3,7 @@ import { Monsters } from 'oldschooljs'; import { SkillsEnum } from '../skilling/types'; import { Requirements } from '../structures/Requirements'; import { isCertainMonsterTrip } from './caUtils'; -import { type CombatAchievement } from './combatAchievements'; +import type { CombatAchievement } from './combatAchievements'; export const mediumCombatAchievements: CombatAchievement[] = [ { diff --git a/src/lib/constants.ts b/src/lib/constants.ts index d71edf2fa23..2c8e59c93e9 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -1,11 +1,12 @@ import path from 'node:path'; -import { Image } from '@napi-rs/canvas'; +import { execSync } from 'node:child_process'; +import type { Image } from '@napi-rs/canvas'; import { SimpleTable, StoreBitfield } from '@oldschoolgg/toolkit'; -import { execSync } from 'child_process'; -import { APIButtonComponent, ButtonBuilder, ButtonStyle, ComponentType } from 'discord.js'; +import type { APIButtonComponent } from 'discord.js'; +import { ButtonBuilder, ButtonStyle, ComponentType } from 'discord.js'; import * as dotenv from 'dotenv'; -import { CommandOptions } from 'mahoji/dist/lib/types'; +import type { CommandOptions } from 'mahoji/dist/lib/types'; import { z } from 'zod'; import { DISCORD_SETTINGS, production } from '../config'; @@ -42,8 +43,8 @@ export const Channel = { ? '346304390858145792' : '1154056119019393035' : production - ? '792691343284764693' - : '1154056119019393035' + ? '792691343284764693' + : '1154056119019393035' }; export const Roles = { @@ -69,7 +70,7 @@ export const Roles = { TopGlobalCL: '1072426869028294747' }; -export const enum Emoji { +export enum Emoji { MoneyBag = '<:MoneyBag:493286312854683654>', OSBot = '<:OSBot:601768469905801226>', Joy = '😂', @@ -178,7 +179,7 @@ export enum ActivityGroup { Minigame = 'Minigame' } -export const enum Events { +export enum Events { Error = 'error', Log = 'log', Verbose = 'verbose', @@ -191,7 +192,7 @@ export const enum Events { export const COINS_ID = 995; -export const enum PerkTier { +export enum PerkTier { /** * Boosters */ @@ -366,7 +367,7 @@ export const BitFieldData: Record = { } } as const; -export const enum PatronTierID { +export enum PatronTierID { One = '4608201', Two = '4608226', Three = '4720356', diff --git a/src/lib/data/Collections.ts b/src/lib/data/Collections.ts index 64353681b15..24fe84a9cb5 100644 --- a/src/lib/data/Collections.ts +++ b/src/lib/data/Collections.ts @@ -1,11 +1,12 @@ import { AttachmentBuilder } from 'discord.js'; import { calcWhatPercent, isObject, notEmpty, removeFromArr, sumArr, uniqueArr } from 'e'; import { Bank, Clues, Monsters } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; import { ChambersOfXeric } from 'oldschooljs/dist/simulation/misc/ChambersOfXeric'; -import Monster from 'oldschooljs/dist/structures/Monster'; +import type Monster from 'oldschooljs/dist/structures/Monster'; -import { ClueTier, ClueTiers } from '../clues/clueTiers'; +import type { ClueTier } from '../clues/clueTiers'; +import { ClueTiers } from '../clues/clueTiers'; import { NEX_ID, PHOSANI_NIGHTMARE_ID, ZALCANO_ID } from '../constants'; import killableMonsters, { effectiveMonsters, NightmareMonster } from '../minions/data/killableMonsters'; import { sepulchreFloors } from '../minions/data/sepulchre'; @@ -19,12 +20,14 @@ import type { MinigameName } from '../settings/minigames'; import { NexNonUniqueTable, NexUniqueTable } from '../simulation/misc'; import { allFarmingItems } from '../skilling/skills/farming'; import { SkillsEnum } from '../skilling/types'; -import { MUserStats } from '../structures/MUserStats'; +import type { MUserStats } from '../structures/MUserStats'; import type { ItemBank } from '../types'; import { fetchStatsForCL, stringMatches } from '../util'; import resolveItems from '../util/resolveItems'; import { makeTable, shuffleRandom } from '../util/smallUtils'; +import type { FormatProgressFunction, ICollection, ILeftListStatus, IToReturnCollection } from './CollectionsExport'; import { + NexCL, abyssalSireCL, aerialFishingCL, alchemicalHydraCL, @@ -67,7 +70,6 @@ import { fightCavesCL, fishingTrawlerCL, forestryCL, - FormatProgressFunction, fossilIslandNotesCL, generalGraardorCL, giantMoleCL, @@ -79,10 +81,7 @@ import { guardiansOfTheRiftCL, hallowedSepulchreCL, hesporiCL, - ICollection, - ILeftListStatus, implingsCL, - IToReturnCollection, kalphiteQueenCL, kingBlackDragonCL, krakenCL, @@ -95,7 +94,6 @@ import { monkeyBackpacksCL, motherlodeMineCL, muspahCL, - NexCL, oborCL, pestControlCL, questCL, @@ -114,13 +112,13 @@ import { spiritAnglerOutfit, templeTrekkingCL, temporossCL, - theatreOfBLoodCL, theGauntletCL, theInfernoCL, theLeviathanCL, theNightmareCL, - thermonuclearSmokeDevilCL, theWhispererCL, + theatreOfBLoodCL, + thermonuclearSmokeDevilCL, titheFarmCL, toaCL, troubleBrewingCL, @@ -1106,9 +1104,9 @@ export const allCollectionLogs: ICollection = { }, Creatables: { counts: false, - items: Createables.filter(i => i.noCl !== true) - .map(i => new Bank(i.outputItems).items().map(i => i[0].id)) - .flat() + items: Createables.filter(i => i.noCl !== true).flatMap(i => + new Bank(i.outputItems).items().map(i => i[0].id) + ) }, Leagues: { counts: false, @@ -1173,7 +1171,7 @@ export const allDroppedItems = uniqueArr([ ) .flat(100), ...Object.values(Monsters) - .map(m => (m && m.allItems ? m.allItems : [])) + .map(m => (m?.allItems ? m.allItems : [])) .flat(100) ]); @@ -1221,13 +1219,8 @@ export function calcCLDetails(user: MUser | Bank) { } // Get the left list to be added to the cls -function getLeftList( - userBank: Bank, - checkCategory: string, - allItems: boolean = false, - removeCoins = false -): ILeftListStatus { - let leftList: ILeftListStatus = {}; +function getLeftList(userBank: Bank, checkCategory: string, allItems = false, removeCoins = false): ILeftListStatus { + const leftList: ILeftListStatus = {}; for (const [category, entries] of Object.entries(allCollectionLogs)) { if (category === checkCategory) { // Sort list by alphabetical order @@ -1332,12 +1325,9 @@ export function getCollectionItems( } let _items: number[] = []; - let _clName: string = ''; + let _clName = ''; loop: for (const [category, entries] of Object.entries(allCollectionLogs)) { - if ( - stringMatches(category, collection) || - (entries.alias && entries.alias.some(a => stringMatches(a, collection))) - ) { + if (stringMatches(category, collection) || entries.alias?.some(a => stringMatches(a, collection))) { _clName = category; _items = uniqueArr( Object.values(entries.activities) @@ -1348,10 +1338,7 @@ export function getCollectionItems( break; } for (const [activityName, attributes] of Object.entries(entries.activities)) { - if ( - stringMatches(activityName, collection) || - (attributes.alias && attributes.alias.find(a => stringMatches(a, collection))) - ) { + if (stringMatches(activityName, collection) || attributes.alias?.find(a => stringMatches(a, collection))) { _clName = activityName; _items = [ ...new Set([...attributes.items, ...(allItems && attributes.allItems ? attributes.allItems : [])]) @@ -1367,7 +1354,7 @@ export function getCollectionItems( ); if (_monster) { _clName = _monster.name; - _items = uniqueArr(Monsters.get(_monster!.id)!.allItems); + _items = uniqueArr(Monsters.get(_monster?.id)!.allItems); } } if (removeCoins && _items.includes(995)) _items = removeFromArr(_items, 995); @@ -1390,7 +1377,7 @@ for (const mon of killableMonsters) allClNames.push(mon.name); export async function getCollection(options: { user: MUser; search: string; - flags: { [key: string]: string | number }; + flags: { [key: string]: string | number | undefined }; logType?: 'collection' | 'sacrifice' | 'bank' | 'temp'; }): Promise { let { user, search, flags, logType } = options; @@ -1407,7 +1394,7 @@ export async function getCollection(options: { flags.missing = 'missing'; } - if (Boolean(flags.missing)) { + if (flags.missing) { clItems = clItems.filter(i => !userCheckBank.has(i)); } @@ -1437,7 +1424,7 @@ export async function getCollection(options: { } for (const [category, entries] of Object.entries(allCollectionLogs)) { - if (stringMatches(category, search) || (entries.alias && entries.alias.some(a => stringMatches(a, search)))) { + if (stringMatches(category, search) || entries.alias?.some(a => stringMatches(a, search))) { return { category, name: category, @@ -1450,10 +1437,7 @@ export async function getCollection(options: { }; } for (const [activityName, attributes] of Object.entries(entries.activities)) { - if ( - stringMatches(activityName, search) || - (attributes.alias && attributes.alias.find(a => stringMatches(a, search))) - ) { + if (stringMatches(activityName, search) || attributes.alias?.find(a => stringMatches(a, search))) { let userKC: Record | undefined = { Default: 0 }; // Defaults to the activity name @@ -1519,9 +1503,9 @@ export async function getCollection(options: { return false; } -export const allCollectionLogsFlat = Object.values(allCollectionLogs) - .map(i => Object.entries(i.activities).map(entry => ({ ...entry[1], name: entry[0] }))) - .flat(); +export const allCollectionLogsFlat = Object.values(allCollectionLogs).flatMap(i => + Object.entries(i.activities).map(entry => ({ ...entry[1], name: entry[0] })) +); export function isCLItem(item: Item | number | [Item, number]): boolean { if (Array.isArray(item)) return isCLItem(item[0]); @@ -1529,8 +1513,6 @@ export function isCLItem(item: Item | number | [Item, number]): boolean { } export const bossCLItems = Object.values({ - ...allCollectionLogs['Bosses'].activities, - ...allCollectionLogs['Raids'].activities -}) - .map(i => i.items) - .flat(); + ...allCollectionLogs.Bosses.activities, + ...allCollectionLogs.Raids.activities +}).flatMap(i => i.items); diff --git a/src/lib/data/CollectionsExport.ts b/src/lib/data/CollectionsExport.ts index a383c195259..201f72f0bd1 100644 --- a/src/lib/data/CollectionsExport.ts +++ b/src/lib/data/CollectionsExport.ts @@ -1,14 +1,14 @@ -import { Minigame } from '@prisma/client'; +import type { Minigame } from '@prisma/client'; import { objectEntries } from 'e'; import { Bank } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; import { growablePets } from '../growablePets'; import { implings } from '../implings'; -import { MinigameScore } from '../settings/minigames'; +import type { MinigameScore } from '../settings/minigames'; import getOSItem from '../util/getOSItem'; import resolveItems from '../util/resolveItems'; -import { UserStatsDataNeededForCL } from './Collections'; +import type { UserStatsDataNeededForCL } from './Collections'; import { gracefulCapes, gracefulFeet, @@ -2248,7 +2248,7 @@ export const allPetIDs = [ ...allPetsCL, ...chambersOfXericMetamorphPets, ...tobMetamorphPets, - ...growablePets.map(petSeries => petSeries.stages).flat(1), + ...growablePets.flatMap(petSeries => petSeries.stages), ...metamorphPets, ...toaMetamorphPets ]; diff --git a/src/lib/data/OSB.commands.json b/src/lib/data/OSB.commands.json index 6e3a3af50a3..b44b4e9d65e 100644 --- a/src/lib/data/OSB.commands.json +++ b/src/lib/data/OSB.commands.json @@ -39,15 +39,7 @@ { "name": "bingo", "desc": "Bingo!", - "subOptions": [ - "make_team", - "leave_team", - "view", - "leaderboard", - "create_bingo", - "manage_bingo", - "items" - ] + "subOptions": ["make_team", "leave_team", "view", "leaderboard", "create_bingo", "manage_bingo", "items"] }, { "name": "bossrecords", @@ -205,28 +197,12 @@ { "name": "gamble", "desc": "Partake in various gambling activities.", - "subOptions": [ - "item", - "dice", - "duel", - "lucky_pick", - "slots", - "hot_cold", - "give_random_item" - ] + "subOptions": ["item", "dice", "duel", "lucky_pick", "slots", "hot_cold", "give_random_item"] }, { "name": "ge", "desc": "Exchange grandly with other players on the bot!", - "subOptions": [ - "buy", - "sell", - "my_listings", - "cancel", - "stats", - "price", - "view" - ] + "subOptions": ["buy", "sell", "my_listings", "cancel", "stats", "price", "view"] }, { "name": "gear", @@ -533,10 +509,7 @@ { "name": "tokkulshop", "desc": "Buy or sell items from the Tzhaar shops.", - "examples": [ - "/tokkulshop buy Obsidian platebody", - "/tokkulshop sell Chaos rune 5000" - ], + "examples": ["/tokkulshop buy Obsidian platebody", "/tokkulshop sell Chaos rune 5000"], "flags": ["minion"], "subOptions": ["buy", "sell"] }, diff --git a/src/lib/data/buyables/aerialFishBuyables.ts b/src/lib/data/buyables/aerialFishBuyables.ts index a92948164c5..ee1751df4b0 100644 --- a/src/lib/data/buyables/aerialFishBuyables.ts +++ b/src/lib/data/buyables/aerialFishBuyables.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Buyable } from './buyables'; +import type { Buyable } from './buyables'; export const aerialFishBuyables: Buyable[] = [ { diff --git a/src/lib/data/buyables/buyables.ts b/src/lib/data/buyables/buyables.ts index 8a6dfd6fee3..5b228c3e729 100644 --- a/src/lib/data/buyables/buyables.ts +++ b/src/lib/data/buyables/buyables.ts @@ -3,10 +3,10 @@ import { Bank } from 'oldschooljs'; import { chompyHats } from '../../constants'; import { CombatCannonItemBank } from '../../minions/data/combatConstants'; import { QuestID } from '../../minions/data/quests'; -import { MinigameName } from '../../settings/settings'; +import type { MinigameName } from '../../settings/settings'; import { soteSkillRequirements } from '../../skilling/functions/questRequirements'; -import { MUserStats } from '../../structures/MUserStats'; -import { Skills } from '../../types'; +import type { MUserStats } from '../../structures/MUserStats'; +import type { Skills } from '../../types'; import { allTeamCapes } from '../misc'; import { aerialFishBuyables } from './aerialFishBuyables'; import { canifisClothes } from './canifisClothes'; @@ -469,7 +469,7 @@ const questBuyables: Buyable[] = [ { name: 'Monkey', outputItems: new Bank({ - 19_556: 1 + 19556: 1 }), gpCost: 1_000_000, qpRequired: 182 diff --git a/src/lib/data/buyables/canifisClothes.ts b/src/lib/data/buyables/canifisClothes.ts index c26c71bf01f..9cbe9d7ac89 100644 --- a/src/lib/data/buyables/canifisClothes.ts +++ b/src/lib/data/buyables/canifisClothes.ts @@ -1,4 +1,4 @@ -import { Buyable } from './buyables'; +import type { Buyable } from './buyables'; export const canifisClothes: Buyable[] = [ { diff --git a/src/lib/data/buyables/capes.ts b/src/lib/data/buyables/capes.ts index b9153750b12..480434fd71a 100644 --- a/src/lib/data/buyables/capes.ts +++ b/src/lib/data/buyables/capes.ts @@ -3,7 +3,7 @@ import { Bank } from 'oldschooljs'; import { diaries, userhasDiaryTier } from '../../diaries'; import { MAX_QP } from '../../minions/data/quests'; import { musicCapeRequirements } from '../../musicCape'; -import { Buyable } from './buyables'; +import type { Buyable } from './buyables'; export const capeBuyables: Buyable[] = [ { diff --git a/src/lib/data/buyables/castleWars.ts b/src/lib/data/buyables/castleWars.ts index bf416158d2c..f15dbeb78ab 100644 --- a/src/lib/data/buyables/castleWars.ts +++ b/src/lib/data/buyables/castleWars.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Buyable } from './buyables'; +import type { Buyable } from './buyables'; const items = [ ['Red decorative full helm', 5], diff --git a/src/lib/data/buyables/forestryBuyables.ts b/src/lib/data/buyables/forestryBuyables.ts index 31f5cd49833..5522eb2460d 100644 --- a/src/lib/data/buyables/forestryBuyables.ts +++ b/src/lib/data/buyables/forestryBuyables.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Buyable } from './buyables'; +import type { Buyable } from './buyables'; export const forestryBuyables: Buyable[] = [ { diff --git a/src/lib/data/buyables/frem.ts b/src/lib/data/buyables/frem.ts index fac910330c7..be0d01574be 100644 --- a/src/lib/data/buyables/frem.ts +++ b/src/lib/data/buyables/frem.ts @@ -1,4 +1,4 @@ -import { Buyable } from './buyables'; +import type { Buyable } from './buyables'; export const fremennikClothes: Buyable[] = [ { diff --git a/src/lib/data/buyables/gnomeClothes.ts b/src/lib/data/buyables/gnomeClothes.ts index c79d04db476..71407041932 100644 --- a/src/lib/data/buyables/gnomeClothes.ts +++ b/src/lib/data/buyables/gnomeClothes.ts @@ -1,4 +1,4 @@ -import { Buyable } from './buyables'; +import type { Buyable } from './buyables'; export const gnomeClothes: Buyable[] = [ { diff --git a/src/lib/data/buyables/guardiansOfTheRifBuyables.ts b/src/lib/data/buyables/guardiansOfTheRifBuyables.ts index 34136adbd9a..26b49f906d3 100644 --- a/src/lib/data/buyables/guardiansOfTheRifBuyables.ts +++ b/src/lib/data/buyables/guardiansOfTheRifBuyables.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Buyable } from './buyables'; +import type { Buyable } from './buyables'; export const guardiansOfTheRiftBuyables: Buyable[] = [ { diff --git a/src/lib/data/buyables/mairinsMarketBuyables.ts b/src/lib/data/buyables/mairinsMarketBuyables.ts index 218ffcfa5d2..f16079085dc 100644 --- a/src/lib/data/buyables/mairinsMarketBuyables.ts +++ b/src/lib/data/buyables/mairinsMarketBuyables.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Buyable } from './buyables'; +import type { Buyable } from './buyables'; export const mairinsMarketBuyables: Buyable[] = [ { diff --git a/src/lib/data/buyables/mining.ts b/src/lib/data/buyables/mining.ts index 2359b3ca603..0de8307b0af 100644 --- a/src/lib/data/buyables/mining.ts +++ b/src/lib/data/buyables/mining.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Buyable } from './buyables'; +import type { Buyable } from './buyables'; export const miningBuyables: Buyable[] = [ { diff --git a/src/lib/data/buyables/perdu.ts b/src/lib/data/buyables/perdu.ts index 43a21af8251..b7b304e4680 100644 --- a/src/lib/data/buyables/perdu.ts +++ b/src/lib/data/buyables/perdu.ts @@ -2,7 +2,7 @@ import { Bank } from 'oldschooljs'; import resolveItems from '../../util/resolveItems'; import { diariesCL } from '../CollectionsExport'; -import { Buyable } from './buyables'; +import type { Buyable } from './buyables'; export const perduBuyables: Buyable[] = diariesCL.map(itemName => ({ name: itemName, diff --git a/src/lib/data/buyables/runes.ts b/src/lib/data/buyables/runes.ts index 328bf40af0d..507654200c6 100644 --- a/src/lib/data/buyables/runes.ts +++ b/src/lib/data/buyables/runes.ts @@ -1,4 +1,4 @@ -import { Buyable } from './buyables'; +import type { Buyable } from './buyables'; export const runeBuyables: Buyable[] = [ { diff --git a/src/lib/data/buyables/shootingStarsBuyables.ts b/src/lib/data/buyables/shootingStarsBuyables.ts index ad01c2c4e75..3edd6df8f1e 100644 --- a/src/lib/data/buyables/shootingStarsBuyables.ts +++ b/src/lib/data/buyables/shootingStarsBuyables.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Buyable } from './buyables'; +import type { Buyable } from './buyables'; export const shootingStarsBuyables: Buyable[] = [ { diff --git a/src/lib/data/buyables/skillCapeBuyables.ts b/src/lib/data/buyables/skillCapeBuyables.ts index 7d8637f0ea2..d6061f7a9f4 100644 --- a/src/lib/data/buyables/skillCapeBuyables.ts +++ b/src/lib/data/buyables/skillCapeBuyables.ts @@ -2,7 +2,7 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; import { Bank } from 'oldschooljs'; import Skillcapes from '../../skilling/skillcapes'; -import { Buyable } from './buyables'; +import type { Buyable } from './buyables'; export const skillCapeBuyables: Buyable[] = []; diff --git a/src/lib/data/buyables/slayerBuyables.ts b/src/lib/data/buyables/slayerBuyables.ts index fec3b7e8f2a..5fbc8e05546 100644 --- a/src/lib/data/buyables/slayerBuyables.ts +++ b/src/lib/data/buyables/slayerBuyables.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Buyable } from './buyables'; +import type { Buyable } from './buyables'; // Most prices >=10k are x10, < 10k = x100 export const slayerBuyables: Buyable[] = [ diff --git a/src/lib/data/buyables/titheFarmBuyables.ts b/src/lib/data/buyables/titheFarmBuyables.ts index ec211ea3bb9..37cc665a5ce 100644 --- a/src/lib/data/buyables/titheFarmBuyables.ts +++ b/src/lib/data/buyables/titheFarmBuyables.ts @@ -1,4 +1,4 @@ -import { ItemBank } from '../../types'; +import type { ItemBank } from '../../types'; import itemID from '../../util/itemID'; interface TitheFarmBuyable { diff --git a/src/lib/data/buyables/troubleBrewingShop.ts b/src/lib/data/buyables/troubleBrewingShop.ts index 5bc0f4e0bcb..e1f18573ee3 100644 --- a/src/lib/data/buyables/troubleBrewingShop.ts +++ b/src/lib/data/buyables/troubleBrewingShop.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Buyable } from './buyables'; +import type { Buyable } from './buyables'; export const troubleBrewingBuyables: Buyable[] = [ { diff --git a/src/lib/data/cox.ts b/src/lib/data/cox.ts index 56dbe93fb82..0f1625223a6 100644 --- a/src/lib/data/cox.ts +++ b/src/lib/data/cox.ts @@ -1,23 +1,23 @@ import { + Time, calcPercentOfNum, calcWhatPercent, increaseNumByPercent, percentChance, randInt, reduceNumByPercent, - shuffleArr, - Time + shuffleArr } from 'e'; import { Bank } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; -import { ChambersOfXericOptions } from 'oldschooljs/dist/simulation/misc/ChambersOfXeric'; +import type { Item } from 'oldschooljs/dist/meta/types'; +import type { ChambersOfXericOptions } from 'oldschooljs/dist/simulation/misc/ChambersOfXeric'; import { checkUserCanUseDegradeableItem } from '../degradeableItems'; -import { GearStats } from '../gear/types'; +import type { GearStats } from '../gear/types'; import { getMinigameScore } from '../settings/minigames'; import { SkillsEnum } from '../skilling/types'; -import { constructGearSetup, Gear } from '../structures/Gear'; -import { Skills } from '../types'; +import { Gear, constructGearSetup } from '../structures/Gear'; +import type { Skills } from '../types'; import { randomVariation } from '../util'; import getOSItem from '../util/getOSItem'; import { logError } from '../util/logError'; @@ -55,7 +55,7 @@ export async function createTeam( users: MUser[], cm: boolean ): Promise> { - let res = []; + const res = []; const isSolo = users.length === 1; for (const u of users) { @@ -146,14 +146,8 @@ function calcSetupPercent( } // For melee compare the highest melee attack stat of max setup with the highest melee attack stat of the user if (melee) { - let maxMeleeStat = Math.max( - maxStats['attack_stab'], - Math.max(maxStats['attack_slash'], maxStats['attack_crush']) - ); - let userMeleeStat = Math.max( - userStats['attack_stab'], - Math.max(userStats['attack_slash'], userStats['attack_crush']) - ); + const maxMeleeStat = Math.max(maxStats.attack_stab, Math.max(maxStats.attack_slash, maxStats.attack_crush)); + const userMeleeStat = Math.max(userStats.attack_stab, Math.max(userStats.attack_slash, userStats.attack_crush)); totalPercent += Math.min(100, calcWhatPercent(userMeleeStat, maxMeleeStat)); numKeys++; } @@ -243,7 +237,7 @@ export const minimumCoxSuppliesNeeded = new Bank({ 'Super restore(4)': 5 }); -export async function checkCoxTeam(users: MUser[], cm: boolean, quantity: number = 1): Promise { +export async function checkCoxTeam(users: MUser[], cm: boolean, quantity = 1): Promise { const hasHerbalist = users.some(u => u.skillLevel(SkillsEnum.Herblore) >= 78); if (!hasHerbalist) { return 'nobody with atleast level 78 Herblore'; @@ -474,7 +468,7 @@ export async function calcCoxDuration( let totalReduction = 0; - let reductions: Record = {}; + const reductions: Record = {}; // Track degradeable items: const degradeableItems: { item: Item; user: MUser; chargesToDegrade: number }[] = []; diff --git a/src/lib/data/creatables/amrod.ts b/src/lib/data/creatables/amrod.ts index 41416972c7f..398216c7314 100644 --- a/src/lib/data/creatables/amrod.ts +++ b/src/lib/data/creatables/amrod.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; export const amrodCreatables: Createable[] = [ { diff --git a/src/lib/data/creatables/armorPacks.ts b/src/lib/data/creatables/armorPacks.ts index 3e9db8425fe..8f61e7b2a6d 100644 --- a/src/lib/data/creatables/armorPacks.ts +++ b/src/lib/data/creatables/armorPacks.ts @@ -1,9 +1,9 @@ import { Bank } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; import getOSItem from '../../util/getOSItem'; import itemID from '../../util/itemID'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; export const armorAndItemPacks: Createable[] = [ // Melee armour sets diff --git a/src/lib/data/creatables/caCreatables.ts b/src/lib/data/creatables/caCreatables.ts index 4e9aacdcbed..2e01875aac5 100644 --- a/src/lib/data/creatables/caCreatables.ts +++ b/src/lib/data/creatables/caCreatables.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; export const caCreatables: Createable[] = [ { diff --git a/src/lib/data/creatables/capes.ts b/src/lib/data/creatables/capes.ts index eeee861dea3..b893abf694f 100644 --- a/src/lib/data/creatables/capes.ts +++ b/src/lib/data/creatables/capes.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; export const capeCreatables: Createable[] = [ { diff --git a/src/lib/data/creatables/dragonfireShields.ts b/src/lib/data/creatables/dragonfireShields.ts index 64c7de6ce37..3535c014d1b 100644 --- a/src/lib/data/creatables/dragonfireShields.ts +++ b/src/lib/data/creatables/dragonfireShields.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../util/itemID'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; export const dragonFireShieldCreatables: Createable[] = [ // Uncharged diff --git a/src/lib/data/creatables/dt.ts b/src/lib/data/creatables/dt.ts index 6a79aead27c..3894f8e2800 100644 --- a/src/lib/data/creatables/dt.ts +++ b/src/lib/data/creatables/dt.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; const bellatorRing: Createable[] = [ { diff --git a/src/lib/data/creatables/forestryCreatables.ts b/src/lib/data/creatables/forestryCreatables.ts index 7af87edf28f..1ae8457ac3f 100644 --- a/src/lib/data/creatables/forestryCreatables.ts +++ b/src/lib/data/creatables/forestryCreatables.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; export const forestryCreatables: Createable[] = [ { diff --git a/src/lib/data/creatables/gracefulOutfits.ts b/src/lib/data/creatables/gracefulOutfits.ts index 2bd4f638749..38689d9c6ea 100644 --- a/src/lib/data/creatables/gracefulOutfits.ts +++ b/src/lib/data/creatables/gracefulOutfits.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../util/itemID'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; export const gracefulOutfitCreatables: Createable[] = [ // Normal diff --git a/src/lib/data/creatables/guardiansOfTheRiftCreatables.ts b/src/lib/data/creatables/guardiansOfTheRiftCreatables.ts index 8e3563d3fc4..dee501e7522 100644 --- a/src/lib/data/creatables/guardiansOfTheRiftCreatables.ts +++ b/src/lib/data/creatables/guardiansOfTheRiftCreatables.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; export const guardiansOfTheRiftCreatables: Createable[] = [ // Red Recolours diff --git a/src/lib/data/creatables/leagueCreatables.ts b/src/lib/data/creatables/leagueCreatables.ts index fa842864141..3160186b567 100644 --- a/src/lib/data/creatables/leagueCreatables.ts +++ b/src/lib/data/creatables/leagueCreatables.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; const toolCreatables: Createable[] = [ { diff --git a/src/lib/data/creatables/lms.ts b/src/lib/data/creatables/lms.ts index 3a92d98d55c..2cfbe06ac0b 100644 --- a/src/lib/data/creatables/lms.ts +++ b/src/lib/data/creatables/lms.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; export const lmsCreatables: Createable[] = [ // Granite mauls diff --git a/src/lib/data/creatables/mysticStaves.ts b/src/lib/data/creatables/mysticStaves.ts index 1079cad80cb..71d791c337d 100644 --- a/src/lib/data/creatables/mysticStaves.ts +++ b/src/lib/data/creatables/mysticStaves.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; export const mysticStavesCreatables: Createable[] = [ { diff --git a/src/lib/data/creatables/nex.ts b/src/lib/data/creatables/nex.ts index 3af3a56ca93..d4bd55f4bcb 100644 --- a/src/lib/data/creatables/nex.ts +++ b/src/lib/data/creatables/nex.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; export const nexCreatables: Createable[] = [ { diff --git a/src/lib/data/creatables/ornaments.ts b/src/lib/data/creatables/ornaments.ts index 8d381665b26..970c0c713fb 100644 --- a/src/lib/data/creatables/ornaments.ts +++ b/src/lib/data/creatables/ornaments.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; export const ornamentKits: Createable[] = [ { @@ -329,13 +329,13 @@ export const ornamentKits: Createable[] = [ 'Rune scimitar ornament kit (guthix)': 1 }), outputItems: new Bank({ - 23_330: 1 + 23330: 1 }) }, { name: 'Revert rune scimitar (guthix)', inputItems: new Bank({ - 23_330: 1 + 23330: 1 }), outputItems: new Bank({ 'Rune scimitar': 1, @@ -350,13 +350,13 @@ export const ornamentKits: Createable[] = [ 'Rune scimitar ornament kit (saradomin)': 1 }), outputItems: new Bank({ - 23_332: 1 + 23332: 1 }) }, { name: 'Revert rune scimitar (saradomin)', inputItems: new Bank({ - 23_332: 1 + 23332: 1 }), outputItems: new Bank({ 'Rune scimitar': 1, @@ -371,13 +371,13 @@ export const ornamentKits: Createable[] = [ 'Rune scimitar ornament kit (zamorak)': 1 }), outputItems: new Bank({ - 23_334: 1 + 23334: 1 }) }, { name: 'Revert rune scimitar (zamorak)', inputItems: new Bank({ - 23_334: 1 + 23334: 1 }), outputItems: new Bank({ 'Rune scimitar': 1, diff --git a/src/lib/data/creatables/shadesOfMorton.ts b/src/lib/data/creatables/shadesOfMorton.ts index ed3cded2317..0b8a0d9c322 100644 --- a/src/lib/data/creatables/shadesOfMorton.ts +++ b/src/lib/data/creatables/shadesOfMorton.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; export const shadesOfMortonCreatables: Createable[] = [ { diff --git a/src/lib/data/creatables/slayer.ts b/src/lib/data/creatables/slayer.ts index 6c4dcb6d6c7..ac65c98a2ef 100644 --- a/src/lib/data/creatables/slayer.ts +++ b/src/lib/data/creatables/slayer.ts @@ -2,7 +2,7 @@ import { Bank } from 'oldschooljs'; import { toKMB } from 'oldschooljs/dist/util/util'; import { SlayerTaskUnlocksEnum } from '../../slayer/slayerUnlocks'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; export const slayerCreatables: Createable[] = [ { diff --git a/src/lib/data/creatables/toa.ts b/src/lib/data/creatables/toa.ts index 22e62af64c2..5b200810ba2 100644 --- a/src/lib/data/creatables/toa.ts +++ b/src/lib/data/creatables/toa.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import getOSItem from '../../util/getOSItem'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; export const toaCreatables: Createable[] = [ { diff --git a/src/lib/data/creatables/tob.ts b/src/lib/data/creatables/tob.ts index 5c00c0114ed..7ce13760011 100644 --- a/src/lib/data/creatables/tob.ts +++ b/src/lib/data/creatables/tob.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Createable } from '../createables'; +import type { Createable } from '../createables'; export const tobCreatables: Createable[] = [ { diff --git a/src/lib/data/creatablesTable.txt b/src/lib/data/creatablesTable.txt index 64bbc56712b..3d64412ce80 100644 --- a/src/lib/data/creatablesTable.txt +++ b/src/lib/data/creatablesTable.txt @@ -780,7 +780,7 @@ | Rune crossbow (or) | 1x Rune crossbow, 1x Shattered relics variety ornament kit | 1x Rune crossbow (or) | 0 | | Revert Rune crossbow (or) | 1x Rune crossbow (or) | 1x Rune crossbow, 1x Shattered relics variety ornament kit | 0 | | Elite void robe (or) | 1x Elite void robe, 1x Shattered relics void ornament kit | 1x Elite void robe (or) | 0 | -| Revert Elite void robe (or)) | 1x Elite void robe (or) | 1x Elite void robe, 1x Shattered relics void ornament kit | 0 | +| Revert Elite void robe (or) | 1x Elite void robe (or) | 1x Elite void robe, 1x Shattered relics void ornament kit | 0 | | Elite void top (or) | 1x Elite void top, 1x Shattered relics void ornament kit | 1x Elite void top (or) | 0 | | Revert Elite void top (or)) | 1x Elite void top (or) | 1x Elite void top, 1x Shattered relics void ornament kit | 0 | | Void knight gloves (or) | 1x Void knight gloves, 1x Shattered relics void ornament kit | 1x Void knight gloves (or) | 0 | @@ -852,12 +852,12 @@ | Revert Robe top of the eye (blue) | 1x Robe top of the eye (blue) | 1x Robe top of the eye | 0 | | Robe bottoms of the eye (blue) | 1x Abyssal blue dye, 1x Robe bottoms of the eye | 1x Robe bottoms of the eye (blue) | 0 | | Revert Robe bottoms of the eye (blue) | 1x Robe bottoms of the eye (blue) | 1x Robe bottoms of the eye | 0 | -| Abyssal green dye (using Abyssal blue dye) | 1x Abyssal green dye | 1x Abyssal blue dye | 0 | -| Abyssal green dye (using Abyssal red dye) | 1x Abyssal green dye | 1x Abyssal red dye | 0 | -| Abyssal blue dye (using Abyssal green dye) | 1x Abyssal blue dye | 1x Abyssal green dye | 0 | -| Abyssal blue dye (using Abyssal red dye) | 1x Abyssal blue dye | 1x Abyssal red dye | 0 | -| Abyssal red dye (using Abyssal green dye) | 1x Abyssal red dye | 1x Abyssal green dye | 0 | -| Abyssal red dye (using Abyssal blue dye) | 1x Abyssal red dye | 1x Abyssal blue dye | 0 | +| Abyssal green dye (using Abyssal blue dye) | 1x Abyssal blue dye | 1x Abyssal green dye | 0 | +| Abyssal green dye (using Abyssal red dye) | 1x Abyssal red dye | 1x Abyssal green dye | 0 | +| Abyssal blue dye (using Abyssal green dye) | 1x Abyssal green dye | 1x Abyssal blue dye | 0 | +| Abyssal blue dye (using Abyssal red dye) | 1x Abyssal red dye | 1x Abyssal blue dye | 0 | +| Abyssal red dye (using Abyssal green dye) | 1x Abyssal green dye | 1x Abyssal red dye | 0 | +| Abyssal red dye (using Abyssal blue dye) | 1x Abyssal blue dye | 1x Abyssal red dye | 0 | | Bronze coffin | 1x Bronze locks, 1x Broken coffin | 1x Bronze coffin | 0 | | Steel coffin | 1x Steel locks, 1x Bronze coffin | 1x Steel coffin | 0 | | Black coffin | 1x Black locks, 1x Steel coffin | 1x Black coffin | 0 | diff --git a/src/lib/data/createables.ts b/src/lib/data/createables.ts index 4557444d6d3..32c38df2433 100644 --- a/src/lib/data/createables.ts +++ b/src/lib/data/createables.ts @@ -2,8 +2,8 @@ import { Bank } from 'oldschooljs'; import { BitField } from '../constants'; import { blisterwoodRequirements, ivandisRequirements } from '../minions/data/templeTrekking'; -import { SlayerTaskUnlocksEnum } from '../slayer/slayerUnlocks'; -import { ItemBank, Skills } from '../types'; +import type { SlayerTaskUnlocksEnum } from '../slayer/slayerUnlocks'; +import type { ItemBank, Skills } from '../types'; import getOSItem from '../util/getOSItem'; import itemID from '../util/itemID'; import { itemNameFromID } from '../util/smallUtils'; diff --git a/src/lib/data/filterables.ts b/src/lib/data/filterables.ts index 0405181681c..2855ed5cefd 100644 --- a/src/lib/data/filterables.ts +++ b/src/lib/data/filterables.ts @@ -216,7 +216,7 @@ const gems = resolveItems([ 'Zenyte shard' ]); -const craftingItems = Craftables.flatMap(item => Object.keys(item.inputItems.bank).map(key => parseInt(key))); +const craftingItems = Craftables.flatMap(item => Object.keys(item.inputItems.bank).map(key => Number.parseInt(key))); const craftingItemsSet = [...new Set(craftingItems)]; @@ -333,7 +333,7 @@ const seeds = resolveItems([ const allPotions = Potions.flatMap(potion => potion.items); const potions = [...new Set(allPotions)]; -const grimyHerbs = Grimy.flatMap(grimy => Object.keys(grimy.inputItems.bank).map(key => parseInt(key))); +const grimyHerbs = Grimy.flatMap(grimy => Object.keys(grimy.inputItems.bank).map(key => Number.parseInt(key))); const cleanHerbs = Grimy.flatMap(clean => clean.item.id); const herbs = [...new Set(grimyHerbs), ...new Set(cleanHerbs)]; @@ -341,7 +341,7 @@ const unfPots = unfinishedPotions.flatMap(unf => unf.item.id); const unfPotions = resolveItems(['Vial of water', ...new Set(unfPots)]); const allSecondaries = PotionsMixable.flatMap(item => - Object.keys(item.inputItems.bank).map(key => parseInt(key)) + Object.keys(item.inputItems.bank).map(key => Number.parseInt(key)) ).filter(item => !potions.includes(item) && !unfPotions.includes(item) && !herbs.includes(item)); const secondaries = [...new Set(allSecondaries)]; @@ -375,7 +375,7 @@ const bones = resolveItems([ 'Zogre bones' ]); -const fletchingItems = Fletchables.flatMap(item => Object.keys(item.inputItems.bank).map(key => parseInt(key))); +const fletchingItems = Fletchables.flatMap(item => Object.keys(item.inputItems.bank).map(key => Number.parseInt(key))); const fletchingItemsSet = [...new Set(fletchingItems)]; diff --git a/src/lib/data/leaguesBuyables.ts b/src/lib/data/leaguesBuyables.ts index caa9b8b9f0f..916618a6d5f 100644 --- a/src/lib/data/leaguesBuyables.ts +++ b/src/lib/data/leaguesBuyables.ts @@ -1,4 +1,4 @@ -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; import getOSItem from '../util/getOSItem'; diff --git a/src/lib/data/misc.ts b/src/lib/data/misc.ts index 83bc4989480..65579392fc8 100644 --- a/src/lib/data/misc.ts +++ b/src/lib/data/misc.ts @@ -1,4 +1,4 @@ -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; import getOSItem from '../util/getOSItem'; diff --git a/src/lib/data/similarItems.ts b/src/lib/data/similarItems.ts index 0176dedd83e..c4789c5f566 100644 --- a/src/lib/data/similarItems.ts +++ b/src/lib/data/similarItems.ts @@ -392,7 +392,7 @@ for (const [baseItem, similarItems] of source) { if (!inverseSimilarItems.get(item)) { inverseSimilarItems.set(item, new Set()); } - inverseSimilarItems.get(item)!.add(itemID(baseItem)); + inverseSimilarItems.get(item)?.add(itemID(baseItem)); } } diff --git a/src/lib/data/tob.ts b/src/lib/data/tob.ts index 46fbdf1031b..409aa7cbeea 100644 --- a/src/lib/data/tob.ts +++ b/src/lib/data/tob.ts @@ -1,11 +1,11 @@ -import { calcPercentOfNum, calcWhatPercent, randFloat, randInt, reduceNumByPercent, round, Time } from 'e'; +import { Time, calcPercentOfNum, calcWhatPercent, randFloat, randInt, reduceNumByPercent, round } from 'e'; import { Bank } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; import { randomVariation } from 'oldschooljs/dist/util'; import type { GearStats } from '../gear/types'; import { blowpipeDarts } from '../minions/functions/blowpipeCommand'; -import { constructGearSetup, Gear } from '../structures/Gear'; +import { Gear, constructGearSetup } from '../structures/Gear'; import getOSItem from '../util/getOSItem'; import { logError } from '../util/logError'; import resolveItems from '../util/resolveItems'; @@ -71,10 +71,10 @@ export function calculateTOBDeaths( total: number; } ): TOBDeaths { - let deaths: number[] = []; - let wipeDeaths: number[] = []; - let realDeathChances: { name: string; deathChance: number }[] = []; - let wipeDeathChances: { name: string; deathChance: number }[] = []; + const deaths: number[] = []; + const wipeDeaths: number[] = []; + const realDeathChances: { name: string; deathChance: number }[] = []; + const wipeDeathChances: { name: string; deathChance: number }[] = []; // These numbers are for proficiency. After this point, the odds of a wipe are the same, but deaths are reduced. const minionProfiencyKC = isHardMode ? 75 : 50; @@ -170,14 +170,8 @@ function calcSetupPercent( } // For melee compare the highest melee attack stat of max setup with the highest melee attack stat of the user if (melee) { - let maxMeleeStat = Math.max( - maxStats['attack_stab'], - Math.max(maxStats['attack_slash'], maxStats['attack_crush']) - ); - let userMeleeStat = Math.max( - userStats['attack_stab'], - Math.max(userStats['attack_slash'], userStats['attack_crush']) - ); + const maxMeleeStat = Math.max(maxStats.attack_stab, Math.max(maxStats.attack_slash, maxStats.attack_crush)); + const userMeleeStat = Math.max(userStats.attack_stab, Math.max(userStats.attack_slash, userStats.attack_crush)); totalPercent += Math.min(100, calcWhatPercent(userMeleeStat, maxMeleeStat)); numKeys++; } @@ -385,9 +379,9 @@ interface TobTeam { export function calcTOBBaseDuration({ team, hardMode }: { team: TobTeam[]; hardMode: boolean }) { const teamSize = team.length; - let individualReductions = []; + const individualReductions = []; - let reductions: Record = {}; + const reductions: Record = {}; for (const u of team) { let userPercentChange = 0; @@ -448,7 +442,7 @@ export function calcTOBBaseDuration({ team, hardMode }: { team: TobTeam[]; hardM userPercentChange = reduceNumByPercent(userPercentChange, 10); } - let reduction = round(userPercentChange / teamSize, 1); + const reduction = round(userPercentChange / teamSize, 1); individualReductions.push(userPercentChange); reductions[u.user.id] = reduction; @@ -499,7 +493,7 @@ export function createTOBRaid({ hardMode: boolean; disableVariation?: true; }): { duration: number; parsedTeam: ParsedTeamMember[]; wipedRoom: TOBRoom | null; deathDuration: number | null } { - let parsedTeam: ParsedTeamMember[] = []; + const parsedTeam: ParsedTeamMember[] = []; for (const u of team) { const gearPercents = calculateTOBUserGearPercents(u.user); @@ -514,12 +508,12 @@ export function createTOBRaid({ }); } - let duration = Math.floor(randomVariation(baseDuration, 5)); + const duration = Math.floor(randomVariation(baseDuration, 5)); let wipedRoom: TOBRoom | null = null; let deathDuration: number | null = 0; for (let i = 0; i < TOBRooms.length; i++) { - let room = TOBRooms[i]; + const room = TOBRooms[i]; if (parsedTeam.every(member => member.wipeDeaths.includes(i))) { wipedRoom = room; diff --git a/src/lib/degradeableItems.ts b/src/lib/degradeableItems.ts index 49693387702..47807b39d83 100644 --- a/src/lib/degradeableItems.ts +++ b/src/lib/degradeableItems.ts @@ -1,11 +1,11 @@ import { percentChance } from 'e'; import { Bank } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; -import Monster from 'oldschooljs/dist/structures/Monster'; +import type { Item } from 'oldschooljs/dist/meta/types'; +import type Monster from 'oldschooljs/dist/structures/Monster'; -import { GearSetupType } from './gear/types'; -import { KillableMonster } from './minions/types'; -import { ChargeBank } from './structures/Bank'; +import type { GearSetupType } from './gear/types'; +import type { KillableMonster } from './minions/types'; +import type { ChargeBank } from './structures/Bank'; import { assert } from './util'; import getOSItem from './util/getOSItem'; import itemID from './util/itemID'; diff --git a/src/lib/diaries.ts b/src/lib/diaries.ts index 8e717f234a8..dbe08dfc82a 100644 --- a/src/lib/diaries.ts +++ b/src/lib/diaries.ts @@ -2,7 +2,8 @@ import { objectEntries } from 'e'; import { Monsters } from 'oldschooljs'; import { MAX_QP } from './minions/data/quests'; -import { DiaryID, DiaryTier, DiaryTierName } from './minions/types'; +import type { DiaryTier, DiaryTierName } from './minions/types'; +import { DiaryID } from './minions/types'; import type { MinigameScore } from './settings/minigames'; import Skillcapes from './skilling/skillcapes'; import { courses } from './skilling/skills/agility'; @@ -32,7 +33,7 @@ export function userhasDiaryTierSync( let canDo = true; const reasons: string[] = []; if (!hasReqs) { - let failSkills: Skills = {}; + const failSkills: Skills = {}; for (const skill of objectEntries(tier.skillReqs)) { if (skills[skill[0]] < skill[1]!) failSkills[skill[0]] = skill[1]!; canDo = false; diff --git a/src/lib/economyLogs.ts b/src/lib/economyLogs.ts index d42331c245c..b364fc0d217 100644 --- a/src/lib/economyLogs.ts +++ b/src/lib/economyLogs.ts @@ -3,7 +3,7 @@ import { sendToChannelID } from './util/webhook'; let economyLogBuffer: string[] = []; -export async function economyLog(message: string, flush: boolean = false) { +export async function economyLog(message: string, flush = false) { economyLogBuffer.push(message); if ((flush && economyLogBuffer.length !== 1) || economyLogBuffer.length === 10) { await sendToChannelID(Channel.EconomyLogs, { diff --git a/src/lib/events.ts b/src/lib/events.ts index 526af76b5ce..bdddc4f4c71 100644 --- a/src/lib/events.ts +++ b/src/lib/events.ts @@ -1,17 +1,18 @@ import { mentionCommand } from '@oldschoolgg/toolkit'; import { UserError } from '@oldschoolgg/toolkit/dist/lib/UserError'; -import { BaseMessageOptions, bold, ButtonBuilder, ButtonStyle, EmbedBuilder, Message, TextChannel } from 'discord.js'; -import { isFunction, roll, Time } from 'e'; +import type { BaseMessageOptions, Message, TextChannel } from 'discord.js'; +import { ButtonBuilder, ButtonStyle, EmbedBuilder, bold } from 'discord.js'; +import { Time, isFunction, roll } from 'e'; import { LRUCache } from 'lru-cache'; import { Items } from 'oldschooljs'; -import { production, SupportServer } from '../config'; +import { SupportServer, production } from '../config'; import { untrustedGuildSettingsCache } from '../mahoji/guildSettings'; import { minionStatusCommand } from '../mahoji/lib/abstracted_commands/minionStatusCommand'; import { BitField, Channel, Emoji, globalConfig } from './constants'; import pets from './data/pets'; import { prisma } from './settings/prisma'; -import { ItemBank } from './types'; +import type { ItemBank } from './types'; import { channelIsSendable, formatDuration, makeComponents, toKMB } from './util'; import { logError } from './util/logError'; import { makeBankImage } from './util/makeBankImage'; diff --git a/src/lib/finishables.ts b/src/lib/finishables.ts index 275bd3cf72b..6baac2786da 100644 --- a/src/lib/finishables.ts +++ b/src/lib/finishables.ts @@ -12,6 +12,7 @@ import { EliteMimicTable, MasterMimicTable } from 'oldschooljs/dist/simulation/m import { allCollectionLogsFlat } from './data/Collections'; import { + NexCL, chambersOfXericCL, chambersOfXericNormalCL, cluesBeginnerCL, @@ -21,13 +22,12 @@ import { cluesMasterCL, cluesMediumCL, evilChickenOutfit, - NexCL, temporossCL, - theatreOfBLoodCL, - theatreOfBLoodNormalCL, theGauntletCL, theNightmareCL, theNightmareNormalCL, + theatreOfBLoodCL, + theatreOfBLoodNormalCL, wintertodtCL } from './data/CollectionsExport'; import pets from './data/pets'; @@ -287,14 +287,14 @@ for (const mon of monsterPairedCLs) { cl: mon.cl, kill: ({ accumulatedLoot }) => { const cost = new Bank(); - if (killableMonster && killableMonster.healAmountNeeded) { + if (killableMonster?.healAmountNeeded) { cost.add('Swordfish', Math.ceil(killableMonster.healAmountNeeded / 14)); } if (killableMonster?.itemCost) { cost.add(calculateTripConsumableCost(killableMonster.itemCost, 1, killableMonster.timeToFinish)); } - let loot = mon.mon.kill(1, {}); - if (killableMonster && killableMonster.specialLoot) { + const loot = mon.mon.kill(1, {}); + if (killableMonster?.specialLoot) { killableMonster.specialLoot({ ownedItems: accumulatedLoot, loot, quantity: 1 }); } return { loot, cost }; diff --git a/src/lib/geImage.ts b/src/lib/geImage.ts index 01eca729746..3e83d4c8bf9 100644 --- a/src/lib/geImage.ts +++ b/src/lib/geImage.ts @@ -1,9 +1,10 @@ -import { Canvas, Image, loadImage, SKRSContext2D } from '@napi-rs/canvas'; +import * as fs from 'node:fs/promises'; +import type { Image, SKRSContext2D } from '@napi-rs/canvas'; +import { Canvas, loadImage } from '@napi-rs/canvas'; import { formatItemStackQuantity, generateHexColorForCashStack, toTitleCase } from '@oldschoolgg/toolkit'; -import { GEListing, GETransaction } from '@prisma/client'; -import * as fs from 'fs/promises'; +import type { GEListing, GETransaction } from '@prisma/client'; -import { GEListingWithTransactions } from './../mahoji/commands/ge'; +import type { GEListingWithTransactions } from './../mahoji/commands/ge'; import { GrandExchange } from './grandExchange'; import { fillTextXTimesInCtx } from './util/canvasUtil'; import getOSItem from './util/getOSItem'; @@ -12,7 +13,7 @@ function drawTitle(ctx: SKRSContext2D, title: string, canvas: Canvas) { // Draw Page Title ctx.font = '16px RuneScape Bold 12'; const titleWidthPx = ctx.measureText(title); - let titleX = Math.floor(Math.floor(canvas.width) * 0.95 - titleWidthPx.width); + const titleX = Math.floor(Math.floor(canvas.width) * 0.95 - titleWidthPx.width); ctx.fillStyle = '#000000'; fillTextXTimesInCtx(ctx, title, titleX + 1, 22); @@ -57,14 +58,7 @@ class GeImageTask { ); } - drawText( - ctx: SKRSContext2D, - text: string, - x: number, - y: number, - maxWidth: number | undefined = undefined, - lineHeight: number - ) { + drawText(ctx: SKRSContext2D, text: string, x: number, y: number, maxWidth: number | undefined, lineHeight: number) { // If max width is set, we have to line break the text const textLines = []; const measuredText = ctx.measureText(text); @@ -93,7 +87,7 @@ class GeImageTask { async getSlotImage( ctx: SKRSContext2D, slot: number, - locked: boolean = false, + locked: boolean, listing: GEListingWithTransactions | undefined ) { const slotImage = listing ? this.geSlotActive! : locked ? this.geSlotLocked! : this.geSlotOpen!; @@ -102,7 +96,7 @@ class GeImageTask { // Draw Bank Title ctx.textAlign = 'center'; ctx.font = '16px RuneScape Bold 12'; - let type = listing ? ` - ${toTitleCase(listing.type.toString())}` : ' - Empty'; + const type = listing ? ` - ${toTitleCase(listing.type.toString())}` : ' - Empty'; this.drawText( ctx, locked ? 'Locked' : `Slot ${slot}${type}`, @@ -124,10 +118,10 @@ class GeImageTask { ctx.translate(8, 34); ctx.drawImage( itemImage, - Math.floor((32 - itemImage!.width) / 2) + 2, - Math.floor((32 - itemImage!.height) / 2), - itemImage!.width, - itemImage!.height + Math.floor((32 - itemImage?.width) / 2) + 2, + Math.floor((32 - itemImage?.height) / 2), + itemImage?.width, + itemImage?.height ); if (listing.total_quantity > 1) { const formattedQuantity = formatItemStackQuantity(listing.total_quantity); @@ -225,7 +219,7 @@ class GeImageTask { } ctx.save(); ctx.translate(x * (this.geSlotOpen!.width + 2), y); - await this.getSlotImage(ctx, i + 1, i >= slots ? true : false, listing); + await this.getSlotImage(ctx, i + 1, i >= slots, listing); ctx.restore(); x++; if (i > (page - 1) * chunkSize + 8) break; diff --git a/src/lib/gear/functions/generateGearImage.ts b/src/lib/gear/functions/generateGearImage.ts index b5a0e7f6ab4..c15b7ae456b 100644 --- a/src/lib/gear/functions/generateGearImage.ts +++ b/src/lib/gear/functions/generateGearImage.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/restrict-plus-operands */ import { Canvas } from '@napi-rs/canvas'; import { toTitleCase } from '@oldschoolgg/toolkit'; import { EquipmentSlot } from 'oldschooljs/dist/meta/types'; @@ -10,7 +9,8 @@ import { fillTextXTimesInCtx, loadAndCacheLocalImage } from '../../util/canvasUtil'; -import { GearSetup, GearSetupType, GearSetupTypes } from '../types'; +import type { GearSetup, GearSetupType } from '../types'; +import { GearSetupTypes } from '../types'; /** * The default gear in a gear setup, when nothing is equipped. @@ -62,8 +62,8 @@ function drawText(canvas: Canvas, text: string, x: number, y: number, maxStat = i === 0 ? x - (ctx.textAlign === 'end' ? ctx.measureText(texts[i + 1]).width - 3 : 0) : ctx.textAlign === 'end' - ? x - : ctx.measureText(texts[i - 1]).width + x + 3, + ? x + : ctx.measureText(texts[i - 1]).width + x + 3, y ); } @@ -82,7 +82,7 @@ export async function generateGearImage( debugLog('Generating gear image', { user_id: user.id }); const bankBg = user.user.bankBackground ?? 1; - let { sprite, uniqueSprite, background: userBgImage } = bankImageGenerator.getBgAndSprite(bankBg, user); + const { sprite, uniqueSprite, background: userBgImage } = bankImageGenerator.getBgAndSprite(bankBg, user); const hexColor = user.user.bank_bg_hex; @@ -239,7 +239,7 @@ export async function generateGearImage( } export async function generateAllGearImage(user: MUser) { - let { + const { sprite: bgSprite, uniqueSprite: hasBgSprite, background: userBg diff --git a/src/lib/gear/functions/hasWildyHuntGearEquipped.ts b/src/lib/gear/functions/hasWildyHuntGearEquipped.ts index de718cc79eb..d3445c571e5 100644 --- a/src/lib/gear/functions/hasWildyHuntGearEquipped.ts +++ b/src/lib/gear/functions/hasWildyHuntGearEquipped.ts @@ -1,5 +1,5 @@ import getOSItem from '../../util/getOSItem'; -import { GearSetup } from '../types'; +import type { GearSetup } from '../types'; export function hasWildyHuntGearEquipped(setup: GearSetup): [boolean, string, number] { const userBodyID = setup.body?.item; diff --git a/src/lib/gear/functions/inverseOfStat.ts b/src/lib/gear/functions/inverseOfStat.ts index 21704afffcc..0362bb70167 100644 --- a/src/lib/gear/functions/inverseOfStat.ts +++ b/src/lib/gear/functions/inverseOfStat.ts @@ -1,4 +1,5 @@ -import { DefenceGearStat, GearStat, OffenceGearStat } from '../types'; +import type { DefenceGearStat, OffenceGearStat } from '../types'; +import { GearStat } from '../types'; const defenceMap: { [key in DefenceGearStat]: OffenceGearStat } = { [GearStat.DefenceSlash]: GearStat.AttackSlash, diff --git a/src/lib/gear/types.ts b/src/lib/gear/types.ts index 07a0d69a3bc..31f0fac9895 100644 --- a/src/lib/gear/types.ts +++ b/src/lib/gear/types.ts @@ -1,6 +1,6 @@ -import { EquipmentSlot } from 'oldschooljs/dist/meta/types'; +import type { EquipmentSlot } from 'oldschooljs/dist/meta/types'; -import { Gear } from '../structures/Gear'; +import type { Gear } from '../structures/Gear'; export type UserFullGearSetup = { [key in GearSetupType]: Gear; diff --git a/src/lib/giantsFoundry.ts b/src/lib/giantsFoundry.ts index 941a3fa9767..b36feb30e57 100644 --- a/src/lib/giantsFoundry.ts +++ b/src/lib/giantsFoundry.ts @@ -55,7 +55,7 @@ export function decodeGiantWeapons(weaponID: string) { const weaponIDs = weaponID.split('-'); assert(weaponIDs.length === 3); const [tipMould, bladeMould, forteMould] = weaponIDs; - return [parseInt(tipMould), parseInt(bladeMould), parseInt(forteMould)]; + return [Number.parseInt(tipMould), Number.parseInt(bladeMould), Number.parseInt(forteMould)]; } // weaponID encoded as 10-4-3 diff --git a/src/lib/grandExchange.ts b/src/lib/grandExchange.ts index ab3f7441be5..240cffe3872 100644 --- a/src/lib/grandExchange.ts +++ b/src/lib/grandExchange.ts @@ -1,16 +1,18 @@ -import { GEListing, GEListingType, GETransaction } from '@prisma/client'; +import type { GEListing, GETransaction } from '@prisma/client'; +import { GEListingType } from '@prisma/client'; import { Stopwatch } from '@sapphire/stopwatch'; -import { bold, ButtonBuilder, ButtonStyle, userMention } from 'discord.js'; -import { calcPercentOfNum, clamp, noOp, sumArr, Time } from 'e'; +import { ButtonBuilder, ButtonStyle, bold, userMention } from 'discord.js'; +import { Time, calcPercentOfNum, clamp, noOp, sumArr } from 'e'; import { Bank } from 'oldschooljs'; -import { Item, ItemBank } from 'oldschooljs/dist/meta/types'; +import type { Item, ItemBank } from 'oldschooljs/dist/meta/types'; import PQueue from 'p-queue'; import { ADMIN_IDS, OWNER_IDS, production } from '../config'; import { BLACKLISTED_USERS } from './blacklists'; -import { BitField, globalConfig, ONE_TRILLION, PerkTier } from './constants'; +import { BitField, ONE_TRILLION, PerkTier, globalConfig } from './constants'; import { marketPricemap } from './marketPrices'; -import { RobochimpUser, roboChimpUserFetch } from './roboChimp'; +import type { RobochimpUser } from './roboChimp'; +import { roboChimpUserFetch } from './roboChimp'; import { prisma } from './settings/prisma'; import { fetchTableBank, makeTransactFromTableBankQueries } from './tableBank'; import { assert, generateGrandExchangeID, getInterval, itemNameFromID, makeComponents, toKMB } from './util'; @@ -28,7 +30,7 @@ interface CreateListingArgs { } function validateNumber(num: number) { - if (num < 0 || isNaN(num) || !Number.isInteger(num) || num >= Number.MAX_SAFE_INTEGER) { + if (num < 0 || Number.isNaN(num) || !Number.isInteger(num) || num >= Number.MAX_SAFE_INTEGER) { throw new Error(`Invalid number: ${num}.`); } } @@ -286,12 +288,24 @@ class GrandExchangeSingleton { return { error: 'Invalid item.' }; } - if (!price || price <= 0 || isNaN(price) || !Number.isInteger(price) || price > this.config.maxPricePerItem) { + if ( + !price || + price <= 0 || + Number.isNaN(price) || + !Number.isInteger(price) || + price > this.config.maxPricePerItem + ) { return { error: `Invalid price, the price must be a number between 1 and ${toKMB(this.config.maxPricePerItem)}.` }; } - if (!quantity || quantity <= 0 || isNaN(quantity) || !Number.isInteger(quantity) || quantity > 5_000_000) { + if ( + !quantity || + quantity <= 0 || + Number.isNaN(quantity) || + !Number.isInteger(quantity) || + quantity > 5_000_000 + ) { return { error: 'Invalid quantity, the quantity must be a number between 1 and 5m.' }; } @@ -343,8 +357,8 @@ ${type} ${toKMB(quantity)} ${item.name} for ${toKMB(price)} each, for a total of type === 'Buy' ? '' : applicableTax.taxedAmount > 0 - ? ` At this price, you will receive ${toKMB(totalAfterTax)} after taxes.` - : ' No tax will be charged on these items.' + ? ` At this price, you will receive ${toKMB(totalAfterTax)} after taxes.` + : ' No tax will be charged on these items.' }`; const guidePrice = marketPricemap.get(item.id); @@ -405,7 +419,7 @@ ${type} ${toKMB(quantity)} ${item.name} for ${toKMB(price)} each, for a total of sellerListing: GEListing, remainingItemsInBuyLimit: number ) { - let logContext: Record = { + const logContext: Record = { buyerListingID: buyerListing.id.toString(), sellerListingID: sellerListing.id.toString(), type: 'GE_TRANSACTION' @@ -441,7 +455,7 @@ ${type} ${toKMB(quantity)} ${item.name} for ${toKMB(price)} each, for a total of } let priceWinner: 'buyer' | 'seller' = 'buyer'; - let pricePerItemBeforeTax: number = -1; + let pricePerItemBeforeTax = -1; if (buyerListing.created_at < sellerListing.created_at) { pricePerItemBeforeTax = Number(buyerListing.asking_price_per_item); priceWinner = 'buyer'; diff --git a/src/lib/growablePets.ts b/src/lib/growablePets.ts index 57e1491ab4b..fefa91de37b 100644 --- a/src/lib/growablePets.ts +++ b/src/lib/growablePets.ts @@ -1,7 +1,7 @@ -import { randFloat, Time } from 'e'; +import { Time, randFloat } from 'e'; import { Bank } from 'oldschooljs'; -import { ActivityTaskOptions } from './types/minions'; +import type { ActivityTaskOptions } from './types/minions'; import getOSItem from './util/getOSItem'; import resolveItems from './util/resolveItems'; diff --git a/src/lib/handleNewCLItems.ts b/src/lib/handleNewCLItems.ts index 98f4836e4f6..dc70caa5899 100644 --- a/src/lib/handleNewCLItems.ts +++ b/src/lib/handleNewCLItems.ts @@ -1,7 +1,8 @@ import { formatOrdinal, roboChimpCLRankQuery } from '@oldschoolgg/toolkit'; -import { Prisma, UserEventType } from '@prisma/client'; +import type { Prisma } from '@prisma/client'; +import { UserEventType } from '@prisma/client'; import { roll, sumArr } from 'e'; -import { Bank } from 'oldschooljs'; +import type { Bank } from 'oldschooljs'; import { Events } from './constants'; import { allCLItems, allCollectionLogsFlat, calcCLDetails } from './data/Collections'; @@ -124,7 +125,7 @@ export async function handleNewCLItems({ user, minigames: await user.fetchMinigames(), stats: await fetchStatsForCL(user) - })}!` + })}!` : ''; const nthUser = ( diff --git a/src/lib/http/routes/webhooks/githubSponsors.ts b/src/lib/http/routes/webhooks/githubSponsors.ts index 5fb5dad4c8c..94b4912cb73 100644 --- a/src/lib/http/routes/webhooks/githubSponsors.ts +++ b/src/lib/http/routes/webhooks/githubSponsors.ts @@ -2,8 +2,8 @@ import { Channel } from '../../../constants'; import { patreonTask } from '../../../patreon'; import { syncLinkedAccounts } from '../../../util/linkedAccountsUtil'; import { sendToChannelID } from '../../../util/webhook'; -import { GithubSponsorsWebhookData } from '../../githubApiTypes'; -import { FastifyServer } from '../../types'; +import type { GithubSponsorsWebhookData } from '../../githubApiTypes'; +import type { FastifyServer } from '../../types'; import { getUserIdFromGithubID, parseStrToTier, verifyGithubSecret } from '../../util'; const githubSponsors = (server: FastifyServer) => @@ -17,7 +17,6 @@ const githubSponsors = (server: FastifyServer) => } const data = request.body as GithubSponsorsWebhookData; const userID = await getUserIdFromGithubID(data.sender.id.toString()); - // eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check switch (data.action) { case 'created': { const tier = parseStrToTier(data.sponsorship.tier.name); diff --git a/src/lib/http/routes/webhooks/patreon.ts b/src/lib/http/routes/webhooks/patreon.ts index ff2f38c0def..6aa7c0b61fd 100644 --- a/src/lib/http/routes/webhooks/patreon.ts +++ b/src/lib/http/routes/webhooks/patreon.ts @@ -1,5 +1,5 @@ import { patreonTask } from '../../../patreon'; -import { FastifyServer } from '../../types'; +import type { FastifyServer } from '../../types'; import { verifyPatreonSecret } from '../../util'; export const patreonRoute = (server: FastifyServer) => diff --git a/src/lib/http/types.ts b/src/lib/http/types.ts index 7937b8f7e3e..e7b88a21ecf 100644 --- a/src/lib/http/types.ts +++ b/src/lib/http/types.ts @@ -1,5 +1,5 @@ -import { FastifyInstance, FastifyLoggerInstance } from 'fastify'; -import { IncomingMessage, Server, ServerResponse } from 'http'; +import type { IncomingMessage, Server, ServerResponse } from 'node:http'; +import type { FastifyInstance, FastifyLoggerInstance } from 'fastify'; interface UserAuth { token: string; diff --git a/src/lib/http/util.ts b/src/lib/http/util.ts index 2c2910adc05..3874e0e8864 100644 --- a/src/lib/http/util.ts +++ b/src/lib/http/util.ts @@ -1,8 +1,8 @@ +import { createHmac } from 'node:crypto'; import { graphql } from '@octokit/graphql'; -import { createHmac } from 'crypto'; import { CLIENT_SECRET, GITHUB_TOKEN } from '../../config'; -import { globalConfig, PerkTier } from '../constants'; +import { PerkTier, globalConfig } from '../constants'; export function rateLimit(max: number, timeWindow: string) { return { diff --git a/src/lib/implings.ts b/src/lib/implings.ts index 021fef3b80d..9418b1bb0f5 100644 --- a/src/lib/implings.ts +++ b/src/lib/implings.ts @@ -2,7 +2,7 @@ import { activity_type_enum } from '@prisma/client'; import { Time } from 'e'; import { Bank, LootTable, Openables } from 'oldschooljs'; -import { ActivityTaskData } from './types/minions'; +import type { ActivityTaskData } from './types/minions'; import activityInArea, { WorldLocations } from './util/activityInArea'; const { @@ -128,7 +128,7 @@ export function handlePassiveImplings(user: MUser, data: ActivityTaskData) { const skills = user.skillsAsLevels; const level = skills.hunter; - let bank = new Bank(); + const bank = new Bank(); const missed = new Bank(); const impTable = implingTableByWorldLocation[activityInArea(user, data)]; diff --git a/src/lib/lootTrack.ts b/src/lib/lootTrack.ts index 772f0e93545..498b0dab001 100644 --- a/src/lib/lootTrack.ts +++ b/src/lib/lootTrack.ts @@ -1,9 +1,9 @@ -import { loot_track_type, LootTrack } from '@prisma/client'; +import type { LootTrack, loot_track_type } from '@prisma/client'; import { Time } from 'e'; import { Bank } from 'oldschooljs'; import { prisma } from './settings/prisma'; -import { ItemBank } from './types'; +import type { ItemBank } from './types'; import { cleanString, formatDuration } from './util'; import { makeBankImage } from './util/makeBankImage'; @@ -48,7 +48,7 @@ async function trackIndividualsLoot({ type: loot_track_type; }) { // Find the existing loot track - let current = await prisma.lootTrack.findFirst({ + const current = await prisma.lootTrack.findFirst({ where: { key, user_id: userID, @@ -79,13 +79,13 @@ async function trackIndividualsLoot({ data.changeType === 'loot' ? { increment: duration - } + } : undefined, total_kc: data.changeType === 'loot' ? { increment: data.kc - } + } : undefined, [data.changeType]: new Bank(current?.[data.changeType] as ItemBank | undefined).add(bankToAdd).bank, user_id: userID diff --git a/src/lib/marketPrices.ts b/src/lib/marketPrices.ts index 7f9d378c5ba..bd862712457 100644 --- a/src/lib/marketPrices.ts +++ b/src/lib/marketPrices.ts @@ -1,6 +1,6 @@ import { notEmpty } from 'e'; import _ from 'lodash'; -import { Bank } from 'oldschooljs'; +import type { Bank } from 'oldschooljs'; import * as ss from 'simple-statistics'; import { prisma } from './settings/prisma'; diff --git a/src/lib/mastery.ts b/src/lib/mastery.ts index a7fbd581199..1d1764d2967 100644 --- a/src/lib/mastery.ts +++ b/src/lib/mastery.ts @@ -6,7 +6,7 @@ import { MAX_XP } from './constants'; import { getTotalCl } from './data/Collections'; import { MAX_QP } from './minions/data/quests'; import { SkillsEnum } from './skilling/types'; -import { MUserStats } from './structures/MUserStats'; +import type { MUserStats } from './structures/MUserStats'; export async function calculateMastery(user: MUser, stats: MUserStats) { const [totalClItems, clItems] = getTotalCl(user, 'collection', stats); diff --git a/src/lib/metrics.ts b/src/lib/metrics.ts index ecca295c99b..091f55bf156 100644 --- a/src/lib/metrics.ts +++ b/src/lib/metrics.ts @@ -1,9 +1,10 @@ +import { writeFile } from 'node:fs/promises'; +import type { CpuInfo } from 'node:os'; +import os from 'node:os'; +import { monitorEventLoopDelay } from 'node:perf_hooks'; import { miniID } from '@oldschoolgg/toolkit'; -import { Prisma } from '@prisma/client'; +import type { Prisma } from '@prisma/client'; import { Time } from 'e'; -import { writeFile } from 'fs/promises'; -import os, { CpuInfo } from 'os'; -import { monitorEventLoopDelay } from 'perf_hooks'; import { prisma } from './settings/prisma'; import { LOG_FILE_NAME, sonicBoom } from './util/logger'; @@ -36,18 +37,18 @@ function totalCpusTime(cpus: CpuInfo[]) { let cpus = os.cpus(); let startUsage = process.cpuUsage(); function getCPUMetrics() { - let newCpus = os.cpus(); - let newStartUsage = process.cpuUsage(); + const newCpus = os.cpus(); + const newStartUsage = process.cpuUsage(); - let elapCpuTimeMs = totalCpusTime(newCpus) - totalCpusTime(cpus); - let elapUsage = process.cpuUsage(startUsage); + const elapCpuTimeMs = totalCpusTime(newCpus) - totalCpusTime(cpus); + const elapUsage = process.cpuUsage(startUsage); cpus = newCpus; startUsage = newStartUsage; - let cpuUser = elapUsage.user / 1000; // microseconds to milliseconds - let cpuSystem = elapUsage.system / 1000; - let cpuPercent = (100 * (cpuUser + cpuSystem)) / elapCpuTimeMs; + const cpuUser = elapUsage.user / 1000; // microseconds to milliseconds + const cpuSystem = elapUsage.system / 1000; + const cpuPercent = (100 * (cpuUser + cpuSystem)) / elapCpuTimeMs; return { cpuUser, @@ -62,7 +63,7 @@ export async function collectMetrics() { [...prismaMetrics.counters, ...prismaMetrics.gauges, ...prismaMetrics.histograms].map(i => [i.key, i.value]) ); - let metrics: Omit = { + const metrics: Omit = { eventLoopDelayMin: h.min * 1e-6, eventLoopDelayMax: h.max * 1e-6, eventLoopDelayMean: h.mean * 1e-6, diff --git a/src/lib/minions/data/bankBackgrounds.ts b/src/lib/minions/data/bankBackgrounds.ts index e87cfffb866..0145d41b2e0 100644 --- a/src/lib/minions/data/bankBackgrounds.ts +++ b/src/lib/minions/data/bankBackgrounds.ts @@ -2,7 +2,7 @@ import { StoreBitfield } from '@oldschoolgg/toolkit'; import { Bank } from 'oldschooljs'; import { BitField, PerkTier } from '../../constants'; -import { BankBackground } from '../types'; +import type { BankBackground } from '../types'; const backgroundImages: BankBackground[] = [ { diff --git a/src/lib/minions/data/combatConstants.ts b/src/lib/minions/data/combatConstants.ts index 2bd5b63ae2d..03fb3bd72ff 100644 --- a/src/lib/minions/data/combatConstants.ts +++ b/src/lib/minions/data/combatConstants.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { Consumable } from '../types'; +import type { Consumable } from '../types'; // Configure boost percents export const boostCannon = 30; @@ -20,16 +20,16 @@ export interface CombatOptionsDesc { aliases: string[]; } export enum CombatOptionsEnum { - NullOption, - AlwaysCannon, - AlwaysIceBurst, - AlwaysIceBarrage + NullOption = 0, + AlwaysCannon = 1, + AlwaysIceBurst = 2, + AlwaysIceBarrage = 3 } export enum SlayerActivityConstants { - None, - IceBarrage, - IceBurst, - Cannon + None = 0, + IceBarrage = 1, + IceBurst = 2, + Cannon = 3 } export const CombatCannonItemBank = new Bank({ 'Cannon barrels': 1, diff --git a/src/lib/minions/data/killableMonsters/bosses/dt.ts b/src/lib/minions/data/killableMonsters/bosses/dt.ts index cf4e032c713..b5b897b961c 100644 --- a/src/lib/minions/data/killableMonsters/bosses/dt.ts +++ b/src/lib/minions/data/killableMonsters/bosses/dt.ts @@ -1,4 +1,4 @@ -import { roll, Time } from 'e'; +import { Time, roll } from 'e'; import { Bank, Monsters } from 'oldschooljs'; import { dukeSucellusCL, theLeviathanCL, theWhispererCL, vardorvisCL } from '../../../../data/CollectionsExport'; @@ -6,7 +6,7 @@ import { GearStat } from '../../../../gear/types'; import { SkillsEnum } from '../../../../skilling/types'; import itemID from '../../../../util/itemID'; import resolveItems, { deepResolveItems } from '../../../../util/resolveItems'; -import { KillableMonster } from '../../../types'; +import type { KillableMonster } from '../../../types'; import { QuestID } from '../../quests'; const awakenedDeathProps = { diff --git a/src/lib/minions/data/killableMonsters/bosses/gwd.ts b/src/lib/minions/data/killableMonsters/bosses/gwd.ts index ad3ea47e6b4..309d714cf31 100644 --- a/src/lib/minions/data/killableMonsters/bosses/gwd.ts +++ b/src/lib/minions/data/killableMonsters/bosses/gwd.ts @@ -11,7 +11,7 @@ import { GearStat } from '../../../../gear/types'; import { SkillsEnum } from '../../../../skilling/types'; import itemID from '../../../../util/itemID'; import resolveItems, { deepResolveItems } from '../../../../util/resolveItems'; -import { KillableMonster } from '../../../types'; +import type { KillableMonster } from '../../../types'; const killableBosses: KillableMonster[] = [ { diff --git a/src/lib/minions/data/killableMonsters/bosses/misc.ts b/src/lib/minions/data/killableMonsters/bosses/misc.ts index d211c2ba0c2..6009579ae5d 100644 --- a/src/lib/minions/data/killableMonsters/bosses/misc.ts +++ b/src/lib/minions/data/killableMonsters/bosses/misc.ts @@ -1,4 +1,4 @@ -import { roll, Time } from 'e'; +import { Time, roll } from 'e'; import { Bank, Monsters } from 'oldschooljs'; import { corporealBeastCL, muspahCL } from '../../../../data/CollectionsExport'; @@ -6,7 +6,7 @@ import { GearStat } from '../../../../gear/types'; import { SkillsEnum } from '../../../../skilling/types'; import itemID from '../../../../util/itemID'; import resolveItems, { deepResolveItems } from '../../../../util/resolveItems'; -import { KillableMonster } from '../../../types'; +import type { KillableMonster } from '../../../types'; const killableBosses: KillableMonster[] = [ { diff --git a/src/lib/minions/data/killableMonsters/bosses/wildy.ts b/src/lib/minions/data/killableMonsters/bosses/wildy.ts index ad7ff30ff1d..bb76179a06a 100644 --- a/src/lib/minions/data/killableMonsters/bosses/wildy.ts +++ b/src/lib/minions/data/killableMonsters/bosses/wildy.ts @@ -5,7 +5,8 @@ import { GearStat } from '../../../../gear/types'; import { SkillsEnum } from '../../../../skilling/types'; import itemID from '../../../../util/itemID'; import resolveItems, { deepResolveItems } from '../../../../util/resolveItems'; -import { DiaryID, KillableMonster } from '../../../types'; +import type { KillableMonster } from '../../../types'; +import { DiaryID } from '../../../types'; export const wildyKillableMonsters: KillableMonster[] = [ { diff --git a/src/lib/minions/data/killableMonsters/camdozaalMonsters.ts b/src/lib/minions/data/killableMonsters/camdozaalMonsters.ts index 5aba8279dbb..e0498fd9b57 100644 --- a/src/lib/minions/data/killableMonsters/camdozaalMonsters.ts +++ b/src/lib/minions/data/killableMonsters/camdozaalMonsters.ts @@ -1,7 +1,7 @@ import { Time } from 'e'; import { Monsters } from 'oldschooljs'; -import { KillableMonster } from '../../types'; +import type { KillableMonster } from '../../types'; export const camdozaalMonsters: KillableMonster[] = [ { diff --git a/src/lib/minions/data/killableMonsters/chaeldarMonsters.ts b/src/lib/minions/data/killableMonsters/chaeldarMonsters.ts index ca14b5dd277..decd4e95e9e 100644 --- a/src/lib/minions/data/killableMonsters/chaeldarMonsters.ts +++ b/src/lib/minions/data/killableMonsters/chaeldarMonsters.ts @@ -5,7 +5,7 @@ import { itemID } from 'oldschooljs/dist/util'; import { GearStat } from '../../../gear/types'; import { SkillsEnum } from '../../../skilling/types'; import resolveItems, { deepResolveItems } from '../../../util/resolveItems'; -import { KillableMonster } from '../../types'; +import type { KillableMonster } from '../../types'; export const chaeldarMonsters: KillableMonster[] = [ { diff --git a/src/lib/minions/data/killableMonsters/creatureCreation.ts b/src/lib/minions/data/killableMonsters/creatureCreation.ts index 9933d953955..2328356be2d 100644 --- a/src/lib/minions/data/killableMonsters/creatureCreation.ts +++ b/src/lib/minions/data/killableMonsters/creatureCreation.ts @@ -4,7 +4,7 @@ import { SkillsEnum } from 'oldschooljs/dist/constants'; import { GearStat } from '../../../gear/types'; import itemID from '../../../util/itemID'; -import { KillableMonster } from '../../types'; +import type { KillableMonster } from '../../types'; export const creatureCreationCreatures: KillableMonster[] = []; const creatures = [ diff --git a/src/lib/minions/data/killableMonsters/index.ts b/src/lib/minions/data/killableMonsters/index.ts index 7d0aa5f0536..fa1954cc26b 100644 --- a/src/lib/minions/data/killableMonsters/index.ts +++ b/src/lib/minions/data/killableMonsters/index.ts @@ -6,7 +6,7 @@ import { GearStat } from '../../../gear/types'; import { SkillsEnum } from '../../../skilling/types'; import itemID from '../../../util/itemID'; import resolveItems, { deepResolveItems } from '../../../util/resolveItems'; -import { KillableMonster } from '../../types'; +import type { KillableMonster } from '../../types'; import { NIGHTMARES_HP } from './../../../constants'; import bosses from './bosses'; import { camdozaalMonsters } from './camdozaalMonsters'; diff --git a/src/lib/minions/data/killableMonsters/konarMonsters.ts b/src/lib/minions/data/killableMonsters/konarMonsters.ts index aafb9832bf0..03fa12e6c96 100644 --- a/src/lib/minions/data/killableMonsters/konarMonsters.ts +++ b/src/lib/minions/data/killableMonsters/konarMonsters.ts @@ -4,7 +4,7 @@ import { itemID } from 'oldschooljs/dist/util'; import { GearStat } from '../../../gear/types'; import resolveItems from '../../../util/resolveItems'; -import { KillableMonster } from '../../types'; +import type { KillableMonster } from '../../types'; export const konarMonsters: KillableMonster[] = [ { diff --git a/src/lib/minions/data/killableMonsters/krystiliaMonsters.ts b/src/lib/minions/data/killableMonsters/krystiliaMonsters.ts index 762eb3a5917..8657aa132a0 100644 --- a/src/lib/minions/data/killableMonsters/krystiliaMonsters.ts +++ b/src/lib/minions/data/killableMonsters/krystiliaMonsters.ts @@ -2,7 +2,7 @@ import { Time } from 'e'; import { Monsters } from 'oldschooljs'; import resolveItems, { deepResolveItems } from '../../../util/resolveItems'; -import { KillableMonster } from '../../types'; +import type { KillableMonster } from '../../types'; export const krystiliaMonsters: KillableMonster[] = [ { diff --git a/src/lib/minions/data/killableMonsters/low.ts b/src/lib/minions/data/killableMonsters/low.ts index 48ea7da3d41..fe270edfc27 100644 --- a/src/lib/minions/data/killableMonsters/low.ts +++ b/src/lib/minions/data/killableMonsters/low.ts @@ -5,7 +5,7 @@ import { SkillsEnum } from 'oldschooljs/dist/constants'; import { GearStat } from '../../../gear/types'; import itemID from '../../../util/itemID'; import resolveItems from '../../../util/resolveItems'; -import { KillableMonster } from '../../types'; +import type { KillableMonster } from '../../types'; const killableMonsters: KillableMonster[] = [ { diff --git a/src/lib/minions/data/killableMonsters/mazchnaMonsters.ts b/src/lib/minions/data/killableMonsters/mazchnaMonsters.ts index b68e6ebc1b9..18e68b5ada7 100644 --- a/src/lib/minions/data/killableMonsters/mazchnaMonsters.ts +++ b/src/lib/minions/data/killableMonsters/mazchnaMonsters.ts @@ -3,7 +3,7 @@ import { Bank, Monsters } from 'oldschooljs'; import { GearStat } from '../../../gear/types'; import itemID from '../../../util/itemID'; -import { KillableMonster } from '../../types'; +import type { KillableMonster } from '../../types'; export const mazchnaMonsters: KillableMonster[] = [ { diff --git a/src/lib/minions/data/killableMonsters/nieveMonsters.ts b/src/lib/minions/data/killableMonsters/nieveMonsters.ts index 4d66b249b76..4d1aefeb254 100644 --- a/src/lib/minions/data/killableMonsters/nieveMonsters.ts +++ b/src/lib/minions/data/killableMonsters/nieveMonsters.ts @@ -4,7 +4,7 @@ import { itemID } from 'oldschooljs/dist/util'; import { GearStat } from '../../../gear/types'; import resolveItems, { deepResolveItems } from '../../../util/resolveItems'; -import { KillableMonster } from '../../types'; +import type { KillableMonster } from '../../types'; export const nieveMonsters: KillableMonster[] = [ { diff --git a/src/lib/minions/data/killableMonsters/reanimated.ts b/src/lib/minions/data/killableMonsters/reanimated.ts index b95dd92eaaa..e92845dccc0 100644 --- a/src/lib/minions/data/killableMonsters/reanimated.ts +++ b/src/lib/minions/data/killableMonsters/reanimated.ts @@ -3,7 +3,7 @@ import { Bank, Monsters } from 'oldschooljs'; import { GearStat } from '../../../gear/types'; import { SkillsEnum } from '../../../skilling/types'; -import { KillableMonster } from '../../types'; +import type { KillableMonster } from '../../types'; const renanimatedMonstersRaw = [ { diff --git a/src/lib/minions/data/killableMonsters/revs.ts b/src/lib/minions/data/killableMonsters/revs.ts index a95a3cf52e3..f0beada3a08 100644 --- a/src/lib/minions/data/killableMonsters/revs.ts +++ b/src/lib/minions/data/killableMonsters/revs.ts @@ -1,7 +1,7 @@ import { Time } from 'e'; import { Bank, Monsters } from 'oldschooljs'; -import { KillableMonster } from '../../types'; +import type { KillableMonster } from '../../types'; export const revenantMonsters: KillableMonster[] = [ { diff --git a/src/lib/minions/data/killableMonsters/turaelMonsters.ts b/src/lib/minions/data/killableMonsters/turaelMonsters.ts index eec8bc47628..190b054ca92 100644 --- a/src/lib/minions/data/killableMonsters/turaelMonsters.ts +++ b/src/lib/minions/data/killableMonsters/turaelMonsters.ts @@ -5,7 +5,7 @@ import { itemID } from 'oldschooljs/dist/util'; import { GearStat } from '../../../gear/types'; import { SkillsEnum } from '../../../skilling/types'; import { deepResolveItems } from '../../../util/resolveItems'; -import { KillableMonster } from '../../types'; +import type { KillableMonster } from '../../types'; export const turaelMonsters: KillableMonster[] = [ { diff --git a/src/lib/minions/data/killableMonsters/vannakaMonsters.ts b/src/lib/minions/data/killableMonsters/vannakaMonsters.ts index fe867c24e0c..ab4f010fafa 100644 --- a/src/lib/minions/data/killableMonsters/vannakaMonsters.ts +++ b/src/lib/minions/data/killableMonsters/vannakaMonsters.ts @@ -4,7 +4,7 @@ import { itemID } from 'oldschooljs/dist/util'; import { GearStat } from '../../../gear/types'; import resolveItems, { deepResolveItems } from '../../../util/resolveItems'; -import { KillableMonster } from '../../types'; +import type { KillableMonster } from '../../types'; export const vannakaMonsters: KillableMonster[] = [ { diff --git a/src/lib/minions/data/planks.ts b/src/lib/minions/data/planks.ts index df7329a2f64..df3c735d2c8 100644 --- a/src/lib/minions/data/planks.ts +++ b/src/lib/minions/data/planks.ts @@ -1,4 +1,4 @@ -import { Plankable } from '../../skilling/types'; +import type { Plankable } from '../../skilling/types'; import itemID from '../../util/itemID'; export const Planks: Plankable[] = [ diff --git a/src/lib/minions/data/plunder.ts b/src/lib/minions/data/plunder.ts index cb9529722d7..cc0b9480cac 100644 --- a/src/lib/minions/data/plunder.ts +++ b/src/lib/minions/data/plunder.ts @@ -3,9 +3,9 @@ import { Bank } from 'oldschooljs'; import LootTable from 'oldschooljs/dist/structures/LootTable'; import { SkillsEnum } from '../../skilling/types'; -import { ItemBank } from '../../types'; +import type { ItemBank } from '../../types'; import { roll, skillingPetDropRate } from '../../util'; -import { MUserClass } from './../../MUser'; +import type { MUserClass } from './../../MUser'; const Room1Table = new LootTable().add('Ivory Comb', 1, 3).add('Pottery scarab').add('Pottery statuette'); diff --git a/src/lib/minions/data/quests.ts b/src/lib/minions/data/quests.ts index f6e8c2641fb..70451b6ac96 100644 --- a/src/lib/minions/data/quests.ts +++ b/src/lib/minions/data/quests.ts @@ -1,7 +1,7 @@ -import { sumArr, Time } from 'e'; +import { Time, sumArr } from 'e'; import { Bank } from 'oldschooljs'; -import { Skills } from '../../types'; +import type { Skills } from '../../types'; interface Quest { id: QuestID; @@ -190,7 +190,7 @@ export const quests: Quest[] = [ herblore: 500 }, calcTime: () => { - let duration = Time.Minute * 6; + const duration = Time.Minute * 6; return duration; } }, diff --git a/src/lib/minions/data/sepulchre.ts b/src/lib/minions/data/sepulchre.ts index 1a28fa85605..3e823fc4a7f 100644 --- a/src/lib/minions/data/sepulchre.ts +++ b/src/lib/minions/data/sepulchre.ts @@ -1,8 +1,8 @@ -import { randInt, roll, Time } from 'e'; +import { Time, randInt, roll } from 'e'; import { Bank } from 'oldschooljs'; import LootTable from 'oldschooljs/dist/structures/LootTable'; -import { ItemBank } from '../../types'; +import type { ItemBank } from '../../types'; import resolveItems from '../../util/resolveItems'; const LowTierCoffin = new LootTable() diff --git a/src/lib/minions/data/templeTrekking.ts b/src/lib/minions/data/templeTrekking.ts index 89542e5a654..f18c002e1a2 100644 --- a/src/lib/minions/data/templeTrekking.ts +++ b/src/lib/minions/data/templeTrekking.ts @@ -3,8 +3,8 @@ import { Bank } from 'oldschooljs'; import LootTable from 'oldschooljs/dist/structures/LootTable'; import { GearStat } from '../../gear/types'; -import { Skills } from '../../types'; -import { GearRequirements } from '../types'; +import type { Skills } from '../../types'; +import type { GearRequirements } from '../types'; interface TrekDifficulty { difficulty: string; diff --git a/src/lib/minions/farming/types.ts b/src/lib/minions/farming/types.ts index 778a9072e0d..e777731a63d 100644 --- a/src/lib/minions/farming/types.ts +++ b/src/lib/minions/farming/types.ts @@ -1,7 +1,7 @@ -import { CropUpgradeType } from '@prisma/client'; +import type { CropUpgradeType } from '@prisma/client'; -import { Plant } from '../../skilling/types'; -import { FarmingPatchName } from '../../util/farmingHelpers'; +import type { Plant } from '../../skilling/types'; +import type { FarmingPatchName } from '../../util/farmingHelpers'; export interface IPatchData { lastPlanted: string | null; diff --git a/src/lib/minions/functions/addSkillingClueToLoot.ts b/src/lib/minions/functions/addSkillingClueToLoot.ts index 9ef16224692..a6054222864 100644 --- a/src/lib/minions/functions/addSkillingClueToLoot.ts +++ b/src/lib/minions/functions/addSkillingClueToLoot.ts @@ -1,5 +1,5 @@ import { percentChance, sumArr } from 'e'; -import { Bank } from 'oldschooljs'; +import type { Bank } from 'oldschooljs'; import { BOT_TYPE } from '../../constants'; import { diff --git a/src/lib/minions/functions/announceLoot.ts b/src/lib/minions/functions/announceLoot.ts index eed78964c7b..0c5f817cb97 100644 --- a/src/lib/minions/functions/announceLoot.ts +++ b/src/lib/minions/functions/announceLoot.ts @@ -1,7 +1,7 @@ -import { Bank } from 'oldschooljs'; +import type { Bank } from 'oldschooljs'; import { Events } from '../../constants'; -import { ArrayItemsResolved } from '../../types'; +import type { ArrayItemsResolved } from '../../types'; import { minionName } from '../../util/minionUtils'; import { effectiveMonsters } from '../data/killableMonsters'; @@ -19,7 +19,7 @@ export default async function announceLoot({ team?: { leader: MUser; lootRecipient: MUser; size: number }; }) { if (!_notifyDrops) return; - const notifyDrops = _notifyDrops.flat(Infinity); + const notifyDrops = _notifyDrops.flat(Number.POSITIVE_INFINITY); const kc = await user.getKC(monsterID); const itemsToAnnounce = loot.clone().filter(i => notifyDrops.includes(i.id)); if (itemsToAnnounce.length > 0) { @@ -27,13 +27,13 @@ export default async function announceLoot({ if (team && team.size > 1) { notif = `In ${team.leader.badgedUsername}'s party of ${team.size} minions killing ${ - effectiveMonsters.find(m => m.id === monsterID)!.name + effectiveMonsters.find(m => m.id === monsterID)?.name }, **${team.lootRecipient.badgedUsername}** just received **${itemsToAnnounce}**!`; } else { notif = `**${user.badgedUsername}'s** minion, ${minionName( user )}, just received **${itemsToAnnounce}**, their ${ - effectiveMonsters.find(m => m.id === monsterID)!.name + effectiveMonsters.find(m => m.id === monsterID)?.name } KC is ${kc.toLocaleString()}!`; } diff --git a/src/lib/minions/functions/autoFarm.ts b/src/lib/minions/functions/autoFarm.ts index 85c393cd8a1..fe5bac9c328 100644 --- a/src/lib/minions/functions/autoFarm.ts +++ b/src/lib/minions/functions/autoFarm.ts @@ -3,8 +3,8 @@ import { SkillsEnum } from 'oldschooljs/dist/constants'; import { farmingPlantCommand } from '../../../mahoji/lib/abstracted_commands/farmingCommand'; import { plants } from '../../skilling/skills/farming'; -import { IPatchDataDetailed } from '../farming/types'; -import { Plant } from './../../skilling/types'; +import type { IPatchDataDetailed } from '../farming/types'; +import type { Plant } from './../../skilling/types'; import { allFarm, replant } from './autoFarmFilters'; export async function autoFarm(user: MUser, patchesDetailed: IPatchDataDetailed[], channelID: string) { @@ -41,7 +41,7 @@ export async function autoFarm(user: MUser, patchesDetailed: IPatchDataDetailed[ .sort((a, b) => b.level - a.level); if (autoFarmFilter === AutoFarmFilterEnum.AllFarm) { - canHarvest = elligible.find(p => patchesDetailed.find(_p => _p.patchName === p.seedType)!.ready); + canHarvest = elligible.find(p => patchesDetailed.find(_p => _p.patchName === p.seedType)?.ready); errorString = "There's no Farming crops that you have the requirements to plant, and nothing to harvest."; } if (autoFarmFilter === AutoFarmFilterEnum.Replant) { diff --git a/src/lib/minions/functions/autoFarmFilters.ts b/src/lib/minions/functions/autoFarmFilters.ts index 267e582e69e..46ceef3ecae 100644 --- a/src/lib/minions/functions/autoFarmFilters.ts +++ b/src/lib/minions/functions/autoFarmFilters.ts @@ -1,9 +1,9 @@ import { Bank } from 'oldschooljs'; -import { MUserClass } from '../../MUser'; +import type { MUserClass } from '../../MUser'; import { calcNumOfPatches } from '../../skilling/functions/calcsFarming'; -import { Plant } from '../../skilling/types'; -import { IPatchDataDetailed } from '../farming/types'; +import type { Plant } from '../../skilling/types'; +import type { IPatchDataDetailed } from '../farming/types'; export function replant( p: Plant, diff --git a/src/lib/minions/functions/blowpipeCommand.ts b/src/lib/minions/functions/blowpipeCommand.ts index c7c1fab3642..140608de2bc 100644 --- a/src/lib/minions/functions/blowpipeCommand.ts +++ b/src/lib/minions/functions/blowpipeCommand.ts @@ -1,8 +1,8 @@ -import { Prisma } from '@prisma/client'; +import type { Prisma } from '@prisma/client'; import { Bank } from 'oldschooljs'; import getOSItem, { getItem } from '../../util/getOSItem'; -import { BlowpipeData } from '../types'; +import type { BlowpipeData } from '../types'; const defaultBlowpipe: BlowpipeData = { scales: 0, @@ -66,7 +66,7 @@ Zulrah's scales: ${rawBlowpipeData.scales.toLocaleString()}x const item = rawBlowpipeData.dartID ? getOSItem(rawBlowpipeData.dartID) : null; if (item) { - str += `${item.name}'s: ${rawBlowpipeData.dartQuantity!.toLocaleString()}x`; + str += `${item.name}'s: ${rawBlowpipeData.dartQuantity?.toLocaleString()}x`; } return str; @@ -94,7 +94,7 @@ async function addCommand(user: MUser, itemName: string, quantity = 1) { const userBank = user.bank; - let itemsToRemove = new Bank(); + const itemsToRemove = new Bank(); if (!blowpipeDarts.includes(item) && item !== getOSItem("Zulrah's scales")) { return "You can only charge your blowpipe with darts and Zulrah's scales."; } @@ -114,7 +114,7 @@ async function addCommand(user: MUser, itemName: string, quantity = 1) { }'s in your Blowpipe, do \`/minion blowpipe remove_darts:true\` to remove them first.`; } - let currentData: BlowpipeData = { ...rawBlowpipeData }; + const currentData: BlowpipeData = { ...rawBlowpipeData }; validateBlowpipeData(currentData); currentData.scales += itemsToRemove.amount("Zulrah's scales"); @@ -172,7 +172,7 @@ async function unchargeCommand(user: MUser) { } const rawBlowpipeData = { ...user.blowpipe }; - let returnedBank = new Bank(); + const returnedBank = new Bank(); if (rawBlowpipeData.scales) { returnedBank.add("Zulrah's scales", rawBlowpipeData.scales); } diff --git a/src/lib/minions/functions/calculateMonsterFood.ts b/src/lib/minions/functions/calculateMonsterFood.ts index 2262b5e0f57..7691b87d998 100644 --- a/src/lib/minions/functions/calculateMonsterFood.ts +++ b/src/lib/minions/functions/calculateMonsterFood.ts @@ -1,10 +1,11 @@ import { calcWhatPercent, reduceNumByPercent } from 'e'; import { inverseOfOffenceStat } from '../../gear/functions/inverseOfStat'; -import { GearSetupType, GearStat } from '../../gear/types'; +import type { GearSetupType } from '../../gear/types'; +import { GearStat } from '../../gear/types'; import { maxDefenceStats, maxOffenceStats } from '../../structures/Gear'; import { readableStatName } from '../../util/smallUtils'; -import { KillableMonster } from '../types'; +import type { KillableMonster } from '../types'; const { floor, max } = Math; diff --git a/src/lib/minions/functions/darkAltarCommand.ts b/src/lib/minions/functions/darkAltarCommand.ts index 355e1dfd3fc..8b522fda894 100644 --- a/src/lib/minions/functions/darkAltarCommand.ts +++ b/src/lib/minions/functions/darkAltarCommand.ts @@ -1,9 +1,9 @@ -import { increaseNumByPercent, reduceNumByPercent, Time } from 'e'; +import { Time, increaseNumByPercent, reduceNumByPercent } from 'e'; import { SkillsEnum } from 'oldschooljs/dist/constants'; import { userHasGracefulEquipped } from '../../../mahoji/mahojiSettings'; import { KourendKebosDiary, userhasDiaryTier } from '../../diaries'; -import { DarkAltarOptions } from '../../types/minions'; +import type { DarkAltarOptions } from '../../types/minions'; import { formatDuration, hasSkillReqs } from '../../util'; import addSubTaskToActivityTask from '../../util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../util/calcMaxTripLength'; diff --git a/src/lib/minions/functions/decantPotionFromBank.ts b/src/lib/minions/functions/decantPotionFromBank.ts index b0fca1dcce2..bb2cf02dcc1 100644 --- a/src/lib/minions/functions/decantPotionFromBank.ts +++ b/src/lib/minions/functions/decantPotionFromBank.ts @@ -31,7 +31,7 @@ export default function decantPotionFromBank( for (let i = 0; i < potionToDecant.items.length; i++) { if (i === dose - 1) continue; - let qty = userBank.amount(potionToDecant.items[i]); + const qty = userBank.amount(potionToDecant.items[i]); if (qty > 0) { potionsToRemove.add(potionToDecant.items[i], qty); sumOfPots += qty; diff --git a/src/lib/minions/functions/degradeableItemsCommand.ts b/src/lib/minions/functions/degradeableItemsCommand.ts index ef6bcfdf460..e1a23eb2c6f 100644 --- a/src/lib/minions/functions/degradeableItemsCommand.ts +++ b/src/lib/minions/functions/degradeableItemsCommand.ts @@ -1,5 +1,5 @@ -import { ChatInputCommandInteraction } from 'discord.js'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { ChatInputCommandInteraction } from 'discord.js'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { Bank } from 'oldschooljs'; import { mahojiParseNumber } from '../../../mahoji/mahojiSettings'; @@ -36,7 +36,7 @@ ${degradeableItems const needConvert = item.convertOnCharge && item.unchargedItem; if (needConvert && !user.hasEquippedOrInBank(item.item.id) && !user.owns(item.unchargedItem!.id)) { - return `You don't own a ${item.item.name} or ${item.unchargedItem!.name}.`; + return `You don't own a ${item.item.name} or ${item.unchargedItem?.name}.`; } await handleMahojiConfirmation( @@ -48,13 +48,13 @@ ${degradeableItems if (needConvert && !user.hasEquippedOrInBank(item.item.id)) { if (!user.owns(item.unchargedItem!.id)) { - return `Your ${item.unchargedItem!.name} disappeared and cannot be charged`; + return `Your ${item.unchargedItem?.name} disappeared and cannot be charged`; } await user.transactItems({ filterLoot: false, collectionLog: true, itemsToAdd: new Bank().add(item.item.id), - itemsToRemove: new Bank().add(item.unchargedItem!.id).add(cost) + itemsToRemove: new Bank().add(item.unchargedItem?.id).add(cost) }); } else { await transactItems({ userID: user.id, itemsToRemove: cost }); diff --git a/src/lib/minions/functions/getUserBestGearFromBank.ts b/src/lib/minions/functions/getUserBestGearFromBank.ts index 50480b78eb4..6f90667b4ca 100644 --- a/src/lib/minions/functions/getUserBestGearFromBank.ts +++ b/src/lib/minions/functions/getUserBestGearFromBank.ts @@ -1,15 +1,16 @@ import { Bank } from 'oldschooljs'; -import { EquipmentSlot, Item } from 'oldschooljs/dist/meta/types'; +import type { EquipmentSlot, Item } from 'oldschooljs/dist/meta/types'; -import { GearSetupType, GearStat } from '../../gear/types'; -import { Gear } from '../../structures/Gear'; -import { Skills } from '../../types'; +import type { GearSetupType } from '../../gear/types'; +import { GearStat } from '../../gear/types'; +import type { Gear } from '../../structures/Gear'; +import type { Skills } from '../../types'; import { assert, skillsMeetRequirements } from '../../util'; import getOSItem from '../../util/getOSItem'; function getItemScore(item: Item) { return Object.values(item.equipment!).reduce( - (a, b) => (!isNaN(Number(a)) ? Number(a) : 0) + (!isNaN(Number(b)) ? Number(b) : 0), + (a, b) => (!Number.isNaN(Number(a)) ? Number(a) : 0) + (!Number.isNaN(Number(b)) ? Number(b) : 0), 0 ); } @@ -23,8 +24,8 @@ export default function getUserBestGearFromBank( extra: string | null = null ) { assert(Object.values(GearStat).includes(gearStat as any)); - let toRemoveFromGear: Bank = new Bank(); - let toRemoveFromBank: Bank = new Bank(); + const toRemoveFromGear: Bank = new Bank(); + const toRemoveFromBank: Bank = new Bank(); const gearToEquip = { ...userGear.raw() }; let score2h = 0; @@ -133,16 +134,16 @@ export default function getUserBestGearFromBank( // Removes weapon/shield or 2h, depending on what has the highest stats if ((!gearStatExtra && scoreWs > score2h) || (gearStatExtra && scoreWsExtra > score2hExtra)) { if (gearToEquip['2h']) { - toRemoveFromBank.remove(gearToEquip['2h']!.item, gearToEquip['2h']!.quantity); + toRemoveFromBank.remove(gearToEquip['2h']?.item, gearToEquip['2h']?.quantity); gearToEquip['2h'] = null; } } else { if (gearToEquip.weapon) { - toRemoveFromBank.remove(gearToEquip.weapon!.item, gearToEquip.weapon!.quantity); + toRemoveFromBank.remove(gearToEquip.weapon?.item, gearToEquip.weapon?.quantity); gearToEquip.weapon = null; } if (gearToEquip.shield) { - toRemoveFromBank.remove(gearToEquip.shield!.item, gearToEquip.shield!.quantity); + toRemoveFromBank.remove(gearToEquip.shield?.item, gearToEquip.shield?.quantity); gearToEquip.shield = null; } } diff --git a/src/lib/minions/functions/getUserFoodFromBank.ts b/src/lib/minions/functions/getUserFoodFromBank.ts index 1b60f40adc7..88770e91f4d 100644 --- a/src/lib/minions/functions/getUserFoodFromBank.ts +++ b/src/lib/minions/functions/getUserFoodFromBank.ts @@ -27,7 +27,7 @@ export default function getUserFoodFromBank({ let userBank = user.bank; if (unavailableBank) userBank = userBank.clone().remove(unavailableBank); let totalHealingCalc = totalHealingNeeded; - let foodToRemove = new Bank(); + const foodToRemove = new Bank(); let sorted = [...Eatables.filter(e => (isWilderness ? true : !e.wildyOnly))] .sort((i, j) => (getRealHealAmount(user, i.healAmount) > getRealHealAmount(user, j.healAmount) ? 1 : -1)) diff --git a/src/lib/minions/functions/hasEnoughFoodForMonster.ts b/src/lib/minions/functions/hasEnoughFoodForMonster.ts index c5e770f2c53..f5bf2567141 100644 --- a/src/lib/minions/functions/hasEnoughFoodForMonster.ts +++ b/src/lib/minions/functions/hasEnoughFoodForMonster.ts @@ -1,4 +1,4 @@ -import { KillableMonster } from '../types'; +import type { KillableMonster } from '../types'; import calculateMonsterFood from './calculateMonsterFood'; import getUserFoodFromBank from './getUserFoodFromBank'; diff --git a/src/lib/minions/functions/index.ts b/src/lib/minions/functions/index.ts index d735c098d73..15bf7684a9f 100644 --- a/src/lib/minions/functions/index.ts +++ b/src/lib/minions/functions/index.ts @@ -1,13 +1,13 @@ -import { User } from '@prisma/client'; +import type { User } from '@prisma/client'; import { Monsters } from 'oldschooljs'; -import Monster from 'oldschooljs/dist/structures/Monster'; +import type Monster from 'oldschooljs/dist/structures/Monster'; import { NIGHTMARES_HP } from '../../constants'; import { SkillsEnum } from '../../skilling/types'; import { randomVariation } from '../../util'; import { xpCannonVaryPercent, xpPercentToCannon, xpPercentToCannonM } from '../data/combatConstants'; import killableMonsters from '../data/killableMonsters'; -import { AddMonsterXpParams, KillableMonster, ResolveAttackStylesParams } from '../types'; +import type { AddMonsterXpParams, KillableMonster, ResolveAttackStylesParams } from '../types'; export { default as calculateMonsterFood } from './calculateMonsterFood'; export { default as reducedTimeForGroup } from './reducedTimeForGroup'; @@ -75,8 +75,8 @@ export async function addMonsterXP(user: MUser, params: AddMonsterXpParams) { const cannonQty = params.cannonMulti ? randomVariation(Math.floor((xpPercentToCannonM / 100) * params.quantity), xpCannonVaryPercent) : params.usingCannon - ? randomVariation(Math.floor((xpPercentToCannon / 100) * params.quantity), xpCannonVaryPercent) - : 0; + ? randomVariation(Math.floor((xpPercentToCannon / 100) * params.quantity), xpCannonVaryPercent) + : 0; // Remove superiors from the regular count to be added separately. let normalQty = 0; @@ -95,27 +95,27 @@ export async function addMonsterXP(user: MUser, params: AddMonsterXpParams) { } // Calculate regular monster XP - if (monster && monster.customMonsterHP) { + if (monster?.customMonsterHP) { hp = monster.customMonsterHP; } else if (osjsMon?.data?.hitpoints) { hp = osjsMon.data.hitpoints; } - if (monster && monster.combatXpMultiplier) { + if (monster?.combatXpMultiplier) { xpMultiplier = monster.combatXpMultiplier; } // Calculate superior XP: let superiorSlayXp = 0; let superiorXp = 0; - if (superiorQty && osjsSuperior!.data!.hitpoints) { - superiorXp = 4 * superiorQty * osjsSuperior!.data!.hitpoints; - superiorSlayXp = superiorQty * osjsSuperior!.data!.slayerXP; + if (superiorQty && osjsSuperior?.data?.hitpoints) { + superiorXp = 4 * superiorQty * osjsSuperior?.data?.hitpoints; + superiorSlayXp = superiorQty * osjsSuperior?.data?.slayerXP; } const totalXP = hp * 4 * normalQty * xpMultiplier + superiorXp; const xpPerSkill = totalXP / attackStyles.length; - let res: string[] = []; + const res: string[] = []; for (const style of attackStyles) { res.push( diff --git a/src/lib/minions/functions/isImportantItemForMonster.ts b/src/lib/minions/functions/isImportantItemForMonster.ts index 418f3fd5d25..9478a0aae0d 100644 --- a/src/lib/minions/functions/isImportantItemForMonster.ts +++ b/src/lib/minions/functions/isImportantItemForMonster.ts @@ -1,4 +1,4 @@ -import { KillableMonster } from '../types'; +import type { KillableMonster } from '../types'; export default function isImportantItemForMonster(itemID: number, monster: KillableMonster) { if (!monster.uniques) return false; diff --git a/src/lib/minions/functions/lmsSimCommand.ts b/src/lib/minions/functions/lmsSimCommand.ts index 68f67f8bbb1..298087883db 100644 --- a/src/lib/minions/functions/lmsSimCommand.ts +++ b/src/lib/minions/functions/lmsSimCommand.ts @@ -1,7 +1,9 @@ -import { Channel, Message, TextChannel } from 'discord.js'; +import type { Channel, Message } from 'discord.js'; +import { TextChannel } from 'discord.js'; import { chunk, sleep } from 'e'; -import LastManStandingUsage, { LMS_FINAL, LMS_PREP, LMS_ROUND } from '../../structures/LastManStandingUsage'; +import type LastManStandingUsage from '../../structures/LastManStandingUsage'; +import { LMS_FINAL, LMS_PREP, LMS_ROUND } from '../../structures/LastManStandingUsage'; import { channelIsSendable, cleanMentions } from '../../util'; const playing = new Set(); @@ -10,15 +12,15 @@ function calculateMaxDeaths(game: LastManStandingGame) { return game.prep // have 0 deaths during the preparation phase ? 0 : // For 16 people, 5 die, 36 -> 7, and so on keeps the game interesting. - game.contestants.size >= 16 - ? Math.ceil(Math.sqrt(game.contestants.size) + 1) - : // If there are more than 7 contestants, proceed to kill them in 4s. - game.contestants.size > 7 - ? 4 - : // If there are more than 3 contestants, eliminate 2, else 1 (3 -> 2, 2 -> 1) - game.contestants.size > 3 - ? 2 - : 1; + game.contestants.size >= 16 + ? Math.ceil(Math.sqrt(game.contestants.size) + 1) + : // If there are more than 7 contestants, proceed to kill them in 4s. + game.contestants.size > 7 + ? 4 + : // If there are more than 3 contestants, eliminate 2, else 1 (3 -> 2, 2 -> 1) + game.contestants.size > 3 + ? 2 + : 1; } function shuffle(contestants: string[]) { @@ -71,7 +73,7 @@ function buildTexts(game: LastManStandingGame, results: string[], deaths: string deaths.length > 0 ? `${`**${deaths.length} new gravestone${ deaths.length === 1 ? ' litters' : 's litter' - } the battlefield.**`}\n\n${deaths.map(d => `- ${d}`).join('\n')}` + } the battlefield.**`}\n\n${deaths.map(d => `- ${d}`).join('\n')}` : ''; const panels = chunk(results, 5); @@ -161,7 +163,7 @@ export async function lmsSimCommand(channel: Channel | undefined, names?: string if (!channelIsSendable(channel)) return; gameMessage = await channel.send(text); - await sleep(Math.max(gameMessage!.content.length / 20, 7) * 700); + await sleep(Math.max(gameMessage?.content.length / 20, 7) * 700); // Delete the previous message, and if stopped, send stop. gameMessage?.delete(); diff --git a/src/lib/minions/functions/reducedTimeForGroup.ts b/src/lib/minions/functions/reducedTimeForGroup.ts index 19a4275a74e..1f159661da7 100644 --- a/src/lib/minions/functions/reducedTimeForGroup.ts +++ b/src/lib/minions/functions/reducedTimeForGroup.ts @@ -1,7 +1,7 @@ import { resolveAvailableItemBoosts } from '../../../mahoji/mahojiSettings'; import { calcPOHBoosts } from '../../poh'; import { prisma } from '../../settings/prisma'; -import { KillableMonster } from '../types'; +import type { KillableMonster } from '../types'; import reducedTimeFromKC from './reducedTimeFromKC'; export default async function reducedTimeForGroup( @@ -9,7 +9,7 @@ export default async function reducedTimeForGroup( monster: KillableMonster ): Promise<[number, string[]]> { let reductionMultiplier = 0; - let messages = []; + const messages = []; if (monster.name === 'Corporeal Beast') { for (let i = 0; i < users.length; i++) { @@ -33,7 +33,7 @@ export default async function reducedTimeForGroup( userItemBoost += boostAmount; } // 1 per user, i/15 for incentive to group (more people compounding i bonus), then add the users kc and item boost percent - let multiplier = 1 + i / 15 + userKcReduction / 100 + userItemBoost / 100; + const multiplier = 1 + i / 15 + userKcReduction / 100 + userItemBoost / 100; reductionMultiplier += multiplier; messages.push(`${multiplier.toFixed(2)}x bonus from ${user.usernameOrMention}`); } diff --git a/src/lib/minions/functions/reducedTimeFromKC.ts b/src/lib/minions/functions/reducedTimeFromKC.ts index eb772908eb5..cb055d193c3 100644 --- a/src/lib/minions/functions/reducedTimeFromKC.ts +++ b/src/lib/minions/functions/reducedTimeFromKC.ts @@ -1,6 +1,6 @@ import { Time } from 'e'; -import { KillableMonster } from '../types'; +import type { KillableMonster } from '../types'; const FIVE_HOURS = Time.Hour * 5; diff --git a/src/lib/minions/functions/removeFoodFromUser.ts b/src/lib/minions/functions/removeFoodFromUser.ts index a5e7c624e71..a9174c2e4a1 100644 --- a/src/lib/minions/functions/removeFoodFromUser.ts +++ b/src/lib/minions/functions/removeFoodFromUser.ts @@ -1,11 +1,11 @@ import { UserError } from '@oldschoolgg/toolkit/dist/lib/UserError'; import { objectEntries, reduceNumByPercent } from 'e'; -import { Bank } from 'oldschooljs'; +import type { Bank } from 'oldschooljs'; import { itemID } from 'oldschooljs/dist/util'; import { Emoji } from '../../constants'; import { Eatables } from '../../data/eatables'; -import { GearSetupType } from '../../gear/types'; +import type { GearSetupType } from '../../gear/types'; import { updateBankSetting } from '../../util/updateBankSetting'; import getUserFoodFromBank from './getUserFoodFromBank'; diff --git a/src/lib/minions/functions/trainCommand.ts b/src/lib/minions/functions/trainCommand.ts index 3003ab33077..3991b0a67ed 100644 --- a/src/lib/minions/functions/trainCommand.ts +++ b/src/lib/minions/functions/trainCommand.ts @@ -2,7 +2,7 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; import { uniqueArr } from 'e'; import { SkillsEnum } from 'oldschooljs/dist/constants'; -import { AttackStyles } from '.'; +import type { AttackStyles } from '.'; const validStyles: AttackStyles[] = [ SkillsEnum.Attack, @@ -29,7 +29,7 @@ export const allPossibleStyles: string[] = uniqueArr([ ...validStyles, ...validStyles .map(i => { - let styles = []; + const styles = []; for (const style of validStyles) { if (style === i) continue; if (invalidCombinations.some(t => t.includes(i) && t.includes(style))) continue; @@ -61,8 +61,8 @@ export async function trainCommand(user: MUser, _styles: string | undefined) { _styles === 'shared' ? [SkillsEnum.Attack, SkillsEnum.Strength, SkillsEnum.Defence] : isValidAttackStyle(_styles) - ? [_styles] - : parsed.filter(isValidAttackStyle); + ? [_styles] + : parsed.filter(isValidAttackStyle); for (const comb of invalidCombinations) { if (comb.every(i => styles.includes(i))) { diff --git a/src/lib/minions/functions/unequipAllCommand.ts b/src/lib/minions/functions/unequipAllCommand.ts index 393aa3fea0a..53a49b87f02 100644 --- a/src/lib/minions/functions/unequipAllCommand.ts +++ b/src/lib/minions/functions/unequipAllCommand.ts @@ -1,7 +1,8 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; import { Bank } from 'oldschooljs'; -import { GearSetupType, GearSetupTypes } from '../../gear/types'; +import type { GearSetupType } from '../../gear/types'; +import { GearSetupTypes } from '../../gear/types'; import { defaultGear } from '../../structures/Gear'; export async function unEquipAllCommand( @@ -18,7 +19,7 @@ export async function unEquipAllCommand( } const currentEquippedGear = user.gear[gearType]; - let refund = new Bank(); + const refund = new Bank(); for (const val of Object.values(currentEquippedGear.raw())) { if (!val) continue; refund.add(val.item, val.quantity); diff --git a/src/lib/minions/types.ts b/src/lib/minions/types.ts index d51658f0b71..e9f3e189b7a 100644 --- a/src/lib/minions/types.ts +++ b/src/lib/minions/types.ts @@ -1,22 +1,22 @@ -import { Image } from '@napi-rs/canvas'; -import { StoreBitfield } from '@oldschoolgg/toolkit'; -import { XpGainSource } from '@prisma/client'; -import { Bank, MonsterKillOptions } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; -import SimpleMonster from 'oldschooljs/dist/structures/SimpleMonster'; +import type { Image } from '@napi-rs/canvas'; +import type { StoreBitfield } from '@oldschoolgg/toolkit'; +import type { XpGainSource } from '@prisma/client'; +import type { Bank, MonsterKillOptions } from 'oldschooljs'; +import type { Item } from 'oldschooljs/dist/meta/types'; +import type SimpleMonster from 'oldschooljs/dist/structures/SimpleMonster'; -import { ClueTier } from '../clues/clueTiers'; -import { BitField, PerkTier } from '../constants'; -import { GearSetupType, GearStat, OffenceGearStat } from '../gear/types'; -import { POHBoosts } from '../poh'; -import { MinigameName } from '../settings/minigames'; -import { LevelRequirements, SkillsEnum } from '../skilling/types'; +import type { ClueTier } from '../clues/clueTiers'; +import type { BitField, PerkTier } from '../constants'; +import type { GearSetupType, GearStat, OffenceGearStat } from '../gear/types'; +import type { POHBoosts } from '../poh'; +import type { MinigameName } from '../settings/minigames'; +import type { LevelRequirements, SkillsEnum } from '../skilling/types'; import type { MUserStats } from '../structures/MUserStats'; -import { ArrayItemsResolved, ItemBank, Skills } from '../types'; -import { MonsterActivityTaskOptions } from '../types/minions'; -import { calculateSimpleMonsterDeathChance } from '../util'; -import { QuestID } from './data/quests'; -import { AttackStyles } from './functions'; +import type { ArrayItemsResolved, ItemBank, Skills } from '../types'; +import type { MonsterActivityTaskOptions } from '../types/minions'; +import type { calculateSimpleMonsterDeathChance } from '../util'; +import type { QuestID } from './data/quests'; +import type { AttackStyles } from './functions'; export type BankBackground = { image: Image | null; diff --git a/src/lib/modals.ts b/src/lib/modals.ts index 5de83a38d60..556f02914be 100644 --- a/src/lib/modals.ts +++ b/src/lib/modals.ts @@ -1,4 +1,4 @@ -import { ModalSubmitInteraction } from 'discord.js'; +import type { ModalSubmitInteraction } from 'discord.js'; import { assert } from './util'; diff --git a/src/lib/musicCape.ts b/src/lib/musicCape.ts index 9f67f372b64..ebcbe4d595b 100644 --- a/src/lib/musicCape.ts +++ b/src/lib/musicCape.ts @@ -6,9 +6,11 @@ import { getPOH } from '../mahoji/lib/abstracted_commands/pohCommand'; import { MIMIC_MONSTER_ID, NEX_ID, ZALCANO_ID } from './constants'; import { championScrolls } from './data/CollectionsExport'; import { RandomEvents } from './randomEvents'; -import { MinigameName, Minigames } from './settings/minigames'; +import type { MinigameName } from './settings/minigames'; +import { Minigames } from './settings/minigames'; import { getUsersActivityCounts, prisma } from './settings/prisma'; -import { RequirementFailure, Requirements } from './structures/Requirements'; +import type { RequirementFailure } from './structures/Requirements'; +import { Requirements } from './structures/Requirements'; import { itemNameFromID } from './util'; import resolveItems from './util/resolveItems'; diff --git a/src/lib/openables.ts b/src/lib/openables.ts index e4b93fec3dc..15bc8bac593 100644 --- a/src/lib/openables.ts +++ b/src/lib/openables.ts @@ -1,7 +1,7 @@ import { formatOrdinal } from '@oldschoolgg/toolkit'; import { Bank, LootTable, Openables } from 'oldschooljs'; import { SkillsEnum } from 'oldschooljs/dist/constants'; -import { Item, OpenableOpenOptions } from 'oldschooljs/dist/meta/types'; +import type { Item, OpenableOpenOptions } from 'oldschooljs/dist/meta/types'; import { Mimic } from 'oldschooljs/dist/simulation/misc'; import BrimstoneChest, { BrimstoneChestOpenable } from 'oldschooljs/dist/simulation/openables/BrimstoneChest'; import { HallowedSackTable } from 'oldschooljs/dist/simulation/openables/HallowedSack'; @@ -12,7 +12,7 @@ import { ClueTiers } from './clues/clueTiers'; import { Emoji, Events, MIMIC_MONSTER_ID } from './constants'; import { cluesRaresCL } from './data/CollectionsExport'; import { defaultFarmingContract } from './minions/farming'; -import { FarmingContract } from './minions/farming/types'; +import type { FarmingContract } from './minions/farming/types'; import { shadeChestOpenables } from './shadesKeys'; import { nestTable } from './simulation/birdsNest'; import { @@ -23,7 +23,7 @@ import { SpoilsOfWarTable } from './simulation/misc'; import { openSeedPack } from './skilling/functions/calcFarmingContracts'; -import { ItemBank } from './types'; +import type { ItemBank } from './types'; import { itemID, roll } from './util'; import getOSItem from './util/getOSItem'; import resolveItems from './util/resolveItems'; @@ -97,7 +97,7 @@ for (const clueTier of ClueTiers) { aliases: [clueTier.name.toLowerCase()], output: async ({ quantity, user, self }) => { const clueTier = ClueTiers.find(c => c.id === self.id)!; - let loot = new Bank(clueTier.table.open(quantity)); + const loot = new Bank(clueTier.table.open(quantity)); let mimicNumber = 0; if (clueTier.mimicChance) { for (let i = 0; i < quantity; i++) { @@ -134,7 +134,7 @@ for (const clueTier of ClueTiers) { // and send a notification if they got one. const announcedLoot = loot.filter(i => clueItemsToNotifyOf.includes(i.id), false); if (gotMilestoneReward) { - announcedLoot.add(clueTier.milestoneReward!.itemReward); + announcedLoot.add(clueTier.milestoneReward?.itemReward); } if (announcedLoot.length > 0) { globalClient.emit( diff --git a/src/lib/party.ts b/src/lib/party.ts index a685ffcae01..e44efb1f4e8 100644 --- a/src/lib/party.ts +++ b/src/lib/party.ts @@ -1,12 +1,12 @@ -/* eslint-disable prefer-promise-reject-errors */ import { UserError } from '@oldschoolgg/toolkit/dist/lib/UserError'; -import { ButtonBuilder, ButtonStyle, ComponentType, InteractionCollector, TextChannel, userMention } from 'discord.js'; -import { debounce, noOp, Time } from 'e'; +import type { TextChannel } from 'discord.js'; +import { ButtonBuilder, ButtonStyle, ComponentType, InteractionCollector, userMention } from 'discord.js'; +import { Time, debounce, noOp } from 'e'; import { production } from '../config'; import { BLACKLISTED_USERS } from './blacklists'; import { SILENT_ERROR, usernameCache } from './constants'; -import { MakePartyOptions } from './types'; +import type { MakePartyOptions } from './types'; import { makeComponents } from './util'; import { CACHED_ACTIVE_USER_IDS } from './util/cachedUserIDs'; diff --git a/src/lib/patreon.ts b/src/lib/patreon.ts index 7fe094b0c77..5338d311b54 100644 --- a/src/lib/patreon.ts +++ b/src/lib/patreon.ts @@ -2,14 +2,14 @@ import { Time } from 'e'; import fetch from 'node-fetch'; import { production } from '../config'; +import { mahojiUserSettingsUpdate } from './MUser'; import { cacheBadges } from './badges'; -import { BadgesEnum, BitField, Channel, globalConfig, PatronTierID, PerkTier } from './constants'; +import { BadgesEnum, BitField, Channel, PatronTierID, PerkTier, globalConfig } from './constants'; import { fetchSponsors, getUserIdFromGithubID } from './http/util'; -import { mahojiUserSettingsUpdate } from './MUser'; import { getUsersPerkTier } from './perkTiers'; import { roboChimpUserFetch } from './roboChimp'; import { prisma } from './settings/prisma'; -import { Patron } from './types'; +import type { Patron } from './types'; import { logError } from './util/logError'; import { sendToChannelID } from './util/webhook'; @@ -144,7 +144,7 @@ class PatreonTask { const userBitfield = user.bitfield; try { - let newField = [ + const newField = [ ...userBitfield.filter(number => !tiers.map(t => t[1]).includes(number)), bitFieldFromPerkTier(perkTier) ]; @@ -179,13 +179,13 @@ class PatreonTask { } async syncGithub() { - let messages = []; + const messages = []; const sponsors = await fetchSponsors(); for (const sponsor of sponsors) { if (!sponsor.tier) continue; const userID = await getUserIdFromGithubID(sponsor.githubID); if (!userID) continue; - let res = await this.validatePerks(userID, sponsor.tier); + const res = await this.validatePerks(userID, sponsor.tier); if (res) { messages.push(res); } diff --git a/src/lib/perkTiers.ts b/src/lib/perkTiers.ts index 39dbfc82b9d..3536b68bf0d 100644 --- a/src/lib/perkTiers.ts +++ b/src/lib/perkTiers.ts @@ -1,4 +1,4 @@ -import { User } from '@prisma/client'; +import type { User } from '@prisma/client'; import { notEmpty } from 'e'; import { SupportServer } from '../config'; @@ -46,7 +46,7 @@ export function getUsersPerkTier( } if (noCheckOtherAccounts !== true && userOrBitfield instanceof GlobalMUserClass) { - let main = userOrBitfield.user.main_account; + const main = userOrBitfield.user.main_account; const allAccounts: string[] = [...userOrBitfield.user.ironman_alts, userOrBitfield.id]; if (main) { allAccounts.push(main); diff --git a/src/lib/poh/index.ts b/src/lib/poh/index.ts index 91fa7b5f981..a2243181cf6 100644 --- a/src/lib/poh/index.ts +++ b/src/lib/poh/index.ts @@ -1,7 +1,7 @@ import { objectEntries } from 'e'; import { Bank } from 'oldschooljs'; -import { LevelRequirements } from '../skilling/types'; +import type { LevelRequirements } from '../skilling/types'; import { Amulets } from './objects/amulets'; import { DungeonDecorations } from './objects/dungeon_decorations'; import { GardenDecorations } from './objects/garden_decorations'; @@ -18,7 +18,7 @@ import { SpellbookAltars } from './objects/spellbook_altars'; import { Teleports } from './objects/teleports'; import { Thrones } from './objects/thrones'; import { Torches } from './objects/torches'; -import { PlayerOwnedHouse } from '.prisma/client'; +import type { PlayerOwnedHouse } from '.prisma/client'; export interface PoH { background: 1; @@ -126,11 +126,11 @@ export const GroupedPohObjects = { GardenDecorations }; -export const PoHObjects = Object.values(GroupedPohObjects).flat(Infinity) as PoHObject[]; +export const PoHObjects = Object.values(GroupedPohObjects).flat(Number.POSITIVE_INFINITY) as PoHObject[]; export const getPOHObject = (idOrName: number | string) => { const key = typeof idOrName === 'string' ? 'name' : 'id'; - let obj = PoHObjects.find(i => i[key] === idOrName); + const obj = PoHObjects.find(i => i[key] === idOrName); if (!obj) throw new Error(`POH Object with id/name ${idOrName} doesn't exist.`); return obj; }; @@ -139,7 +139,7 @@ export type POHBoosts = Partial>>; export function calcPOHBoosts(poh: PlayerOwnedHouse, boosts: POHBoosts): [number, string[]] { let boost = 0; - let messages = []; + const messages = []; for (const [slot, objBoosts] of objectEntries(boosts)) { if (objBoosts === undefined) continue; for (const [name, boostPercent] of objectEntries(objBoosts)) { diff --git a/src/lib/poh/objects/amulets.ts b/src/lib/poh/objects/amulets.ts index 286d6d02806..84d433f07f4 100644 --- a/src/lib/poh/objects/amulets.ts +++ b/src/lib/poh/objects/amulets.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { PoHObject } from '..'; +import type { PoHObject } from '..'; export const Amulets: PoHObject[] = [ { diff --git a/src/lib/poh/objects/dungeon_decorations.ts b/src/lib/poh/objects/dungeon_decorations.ts index 429de1e37f6..16e9a223f8e 100644 --- a/src/lib/poh/objects/dungeon_decorations.ts +++ b/src/lib/poh/objects/dungeon_decorations.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { PoHObject } from '..'; +import type { PoHObject } from '..'; export const DungeonDecorations: PoHObject[] = [ { diff --git a/src/lib/poh/objects/garden_decorations.ts b/src/lib/poh/objects/garden_decorations.ts index 49d527a7efa..1ef3aa6e243 100644 --- a/src/lib/poh/objects/garden_decorations.ts +++ b/src/lib/poh/objects/garden_decorations.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { PoHObject } from '..'; +import type { PoHObject } from '..'; const cost = new Bank().add('Magic stone', 2); diff --git a/src/lib/poh/objects/guards.ts b/src/lib/poh/objects/guards.ts index 2359e9098e9..a5069759839 100644 --- a/src/lib/poh/objects/guards.ts +++ b/src/lib/poh/objects/guards.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { PoHObject } from '..'; +import type { PoHObject } from '..'; export const Guards: PoHObject[] = [ { diff --git a/src/lib/poh/objects/jewellery_boxes.ts b/src/lib/poh/objects/jewellery_boxes.ts index e50f979c43a..e43a655de85 100644 --- a/src/lib/poh/objects/jewellery_boxes.ts +++ b/src/lib/poh/objects/jewellery_boxes.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { PoHObject } from '..'; +import type { PoHObject } from '..'; export const JewelleryBoxes: PoHObject[] = [ { diff --git a/src/lib/poh/objects/mounted_capes.ts b/src/lib/poh/objects/mounted_capes.ts index 3ab9aad3fe8..30af8c36eba 100644 --- a/src/lib/poh/objects/mounted_capes.ts +++ b/src/lib/poh/objects/mounted_capes.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { PoHObject } from '..'; +import type { PoHObject } from '..'; const baseBank = () => new Bank().add('Marble block').add('Gold leaf'); diff --git a/src/lib/poh/objects/mounted_fish.ts b/src/lib/poh/objects/mounted_fish.ts index 0e13de32ea2..c199e38fbc1 100644 --- a/src/lib/poh/objects/mounted_fish.ts +++ b/src/lib/poh/objects/mounted_fish.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { PoHObject } from '..'; +import type { PoHObject } from '..'; export const MountedFish: PoHObject[] = [ { diff --git a/src/lib/poh/objects/mounted_heads.ts b/src/lib/poh/objects/mounted_heads.ts index 7bdcb20dcc3..aa4033aaeb6 100644 --- a/src/lib/poh/objects/mounted_heads.ts +++ b/src/lib/poh/objects/mounted_heads.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { PoHObject } from '..'; +import type { PoHObject } from '..'; const baseBank = () => new Bank().add('Marble block').add('Gold leaf'); diff --git a/src/lib/poh/objects/mounted_items.ts b/src/lib/poh/objects/mounted_items.ts index 3734da11f3e..c54992e787d 100644 --- a/src/lib/poh/objects/mounted_items.ts +++ b/src/lib/poh/objects/mounted_items.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { PoHObject } from '..'; +import type { PoHObject } from '..'; export const MountedItems: PoHObject[] = [ { diff --git a/src/lib/poh/objects/pools.ts b/src/lib/poh/objects/pools.ts index 2987838d3a2..5b59c740f5b 100644 --- a/src/lib/poh/objects/pools.ts +++ b/src/lib/poh/objects/pools.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { PoHObject } from '..'; +import type { PoHObject } from '..'; export const Pools: PoHObject[] = [ { diff --git a/src/lib/poh/objects/prayer_altars.ts b/src/lib/poh/objects/prayer_altars.ts index 822fa8323e6..d5341292651 100644 --- a/src/lib/poh/objects/prayer_altars.ts +++ b/src/lib/poh/objects/prayer_altars.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { PoHObject } from '..'; +import type { PoHObject } from '..'; export const PrayerAltars: PoHObject[] = [ { diff --git a/src/lib/poh/objects/prisons.ts b/src/lib/poh/objects/prisons.ts index cc081fd8063..dd0b0bb48a7 100644 --- a/src/lib/poh/objects/prisons.ts +++ b/src/lib/poh/objects/prisons.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { PoHObject } from '..'; +import type { PoHObject } from '..'; export const Prisons: PoHObject[] = [ { diff --git a/src/lib/poh/objects/spellbook_altars.ts b/src/lib/poh/objects/spellbook_altars.ts index 2615b321d36..f9828ae7830 100644 --- a/src/lib/poh/objects/spellbook_altars.ts +++ b/src/lib/poh/objects/spellbook_altars.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { PoHObject } from '..'; +import type { PoHObject } from '..'; export const SpellbookAltars: PoHObject[] = [ { diff --git a/src/lib/poh/objects/teleports.ts b/src/lib/poh/objects/teleports.ts index 7b5b45c76e6..c8deb952b83 100644 --- a/src/lib/poh/objects/teleports.ts +++ b/src/lib/poh/objects/teleports.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { PoHObject } from '..'; +import type { PoHObject } from '..'; export const Teleports: PoHObject[] = [ { diff --git a/src/lib/poh/objects/thrones.ts b/src/lib/poh/objects/thrones.ts index 7fd6ddbdac3..9bf808fa9bf 100644 --- a/src/lib/poh/objects/thrones.ts +++ b/src/lib/poh/objects/thrones.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { PoHObject } from '..'; +import type { PoHObject } from '..'; export const Thrones: PoHObject[] = [ { diff --git a/src/lib/poh/objects/torches.ts b/src/lib/poh/objects/torches.ts index 6e9a37ed657..3e612d6490b 100644 --- a/src/lib/poh/objects/torches.ts +++ b/src/lib/poh/objects/torches.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { PoHObject } from '..'; +import type { PoHObject } from '..'; export const Torches: PoHObject[] = [ { diff --git a/src/lib/pohImage.ts b/src/lib/pohImage.ts index a9644146f3e..1df18f67104 100644 --- a/src/lib/pohImage.ts +++ b/src/lib/pohImage.ts @@ -1,12 +1,13 @@ -import { Canvas, Image, SKRSContext2D } from '@napi-rs/canvas'; +import * as fs from 'node:fs'; +import path from 'node:path'; +import type { Image, SKRSContext2D } from '@napi-rs/canvas'; +import { Canvas } from '@napi-rs/canvas'; import { objectEntries, randInt } from 'e'; -import * as fs from 'fs'; -import path from 'path'; import { DUNGEON_FLOOR_Y, GROUND_FLOOR_Y, HOUSE_WIDTH, Placeholders, TOP_FLOOR_Y } from './poh'; import { canvasImageFromBuffer, loadAndCacheLocalImage } from './util/canvasUtil'; import { getActivityOfUser } from './util/minionIsBusy'; -import { PlayerOwnedHouse } from '.prisma/client'; +import type { PlayerOwnedHouse } from '.prisma/client'; const CONSTRUCTION_IMG_DIR = './src/lib/poh/images'; const FOLDERS = [ @@ -33,7 +34,7 @@ class PoHImage { public imageCache: Map = new Map(); public bgImages: Image[] = []; initPromise: Promise | null = this.init(); - initFinished: boolean = false; + initFinished = false; async init() { this.bgImages.push(await loadAndCacheLocalImage('./src/lib/poh/images/bg_1.jpg')); @@ -42,7 +43,7 @@ class PoHImage { const currentPath = path.join(CONSTRUCTION_IMG_DIR, folder); const filesInDir = await fs.promises.readdir(currentPath); for (const fileName of filesInDir) { - const id = parseInt(path.parse(fileName).name); + const id = Number.parseInt(path.parse(fileName).name); const imageBuffer = await fs.promises.readFile(path.join(currentPath, `${id}.png`)); const image = await canvasImageFromBuffer(imageBuffer); @@ -93,7 +94,7 @@ class PoHImage { const [placeholder, coordArr] = objects; for (const obj of coordArr) { const [x, y] = obj; - let id = poh[key] ?? placeholder; + const id = poh[key] ?? placeholder; const isMountedItem = key === 'mounted_item' && id !== 1111; if (isMountedItem) { const hasCustomItem = id !== 1112; diff --git a/src/lib/premiumPatronTime.ts b/src/lib/premiumPatronTime.ts index 26f468db001..fa83b534489 100644 --- a/src/lib/premiumPatronTime.ts +++ b/src/lib/premiumPatronTime.ts @@ -1,4 +1,4 @@ -import { ChatInputCommandInteraction } from 'discord.js'; +import type { ChatInputCommandInteraction } from 'discord.js'; import { Time } from 'e'; import { handleMahojiConfirmation } from './util/handleMahojiConfirmation'; diff --git a/src/lib/randomEvents.ts b/src/lib/randomEvents.ts index 8f138c3222f..c1761b570f6 100644 --- a/src/lib/randomEvents.ts +++ b/src/lib/randomEvents.ts @@ -1,5 +1,5 @@ import { activity_type_enum } from '@prisma/client'; -import { randArrItem, roll, Time } from 'e'; +import { Time, randArrItem, roll } from 'e'; import { LRUCache } from 'lru-cache'; import { Bank } from 'oldschooljs'; import LootTable from 'oldschooljs/dist/structures/LootTable'; diff --git a/src/lib/roboChimp.ts b/src/lib/roboChimp.ts index bc85b0b75be..b37a3703b05 100644 --- a/src/lib/roboChimp.ts +++ b/src/lib/roboChimp.ts @@ -1,5 +1,6 @@ import { formatOrdinal } from '@oldschoolgg/toolkit'; -import { PrismaClient, TriviaQuestion, User } from '@prisma/robochimp'; +import type { TriviaQuestion, User } from '@prisma/robochimp'; +import { PrismaClient } from '@prisma/robochimp'; import { calcWhatPercent, round, sumArr } from 'e'; import deepEqual from 'fast-deep-equal'; diff --git a/src/lib/rolesTask.ts b/src/lib/rolesTask.ts index 7008b9756ab..a28839fe1b4 100644 --- a/src/lib/rolesTask.ts +++ b/src/lib/rolesTask.ts @@ -1,7 +1,7 @@ import { Prisma } from '@prisma/client'; import { noOp, notEmpty } from 'e'; -import { production, SupportServer } from '../config'; +import { SupportServer, production } from '../config'; import { ClueTiers } from '../lib/clues/clueTiers'; import { Roles, usernameCache } from '../lib/constants'; import { getCollectionItems } from '../lib/data/Collections'; @@ -11,7 +11,7 @@ import Skills from '../lib/skilling/skills'; import { convertXPtoLVL } from '../lib/util'; import { logError } from '../lib/util/logError'; import { TeamLoot } from './simulation/TeamLoot'; -import { ItemBank } from './types'; +import type { ItemBank } from './types'; function addToUserMap(userMap: Record, id: string, reason: string) { if (!userMap[id]) userMap[id] = []; @@ -61,15 +61,15 @@ async function addRoles({ if (process.env.TEST) return ''; const g = globalClient.guilds.cache.get(SupportServer); if (!g) throw new Error('No support guild'); - let added: string[] = []; - let removed: string[] = []; - let _role = await g.roles.fetch(role).catch(noOp); + const added: string[] = []; + const removed: string[] = []; + const _role = await g.roles.fetch(role).catch(noOp); if (!_role) return `\nCould not check ${role} role`; for (const u of users.filter(notEmpty)) { await g.members.fetch(u).catch(noOp); } const roleName = _role.name!; - let noChangeUserDescriptions: string[] = []; + const noChangeUserDescriptions: string[] = []; for (const mem of g.members.cache.values()) { const mUser = await mUserFetch(mem.user.id); if (mem.roles.cache.has(role) && !users.includes(mem.user.id)) { @@ -108,9 +108,9 @@ async function addRoles({ str += `\nRemoved from: ${removed.join(', ')}.`; } if (userMap) { - let userArr = []; + const userArr = []; for (const [id, arr] of Object.entries(userMap)) { - let username = usernameCache.get(id) ?? 'Unknown'; + const username = usernameCache.get(id) ?? 'Unknown'; userArr.push(`${username}(${arr.join(', ')})`); } str += `\n${userArr.join(',')}`; @@ -126,8 +126,7 @@ async function addRoles({ export async function runRolesTask() { const skillVals = Object.values(Skills); - let results: string[] = []; - // eslint-disable-next-line @typescript-eslint/unbound-method + const results: string[] = []; const q = async (str: string) => { const result = await prisma.$queryRawUnsafe(str).catch(err => { logError(`This query failed: ${str}`, err); @@ -231,7 +230,7 @@ SELECT id, (cardinality(u.cl_keys) - u.inverse_length) as qty .catch(handleErr) ]); - let result = []; + const result = []; const userID = users[0]?.id; const ironmanID = ironUsers[0]?.id; @@ -272,7 +271,7 @@ SELECT id, (cardinality(u.cl_keys) - u.inverse_length) as qty // Top sacrificers async function topSacrificers() { const userMap = {}; - let topSacrificers: string[] = []; + const topSacrificers: string[] = []; const mostValue = await q('SELECT id FROM users ORDER BY "sacrificedValue" DESC LIMIT 3;'); for (let i = 0; i < 3; i++) { if (mostValue[i] !== undefined) { @@ -307,7 +306,7 @@ ORDER BY u.sacbanklength DESC LIMIT 1;`); // Top minigamers async function topMinigamers() { - let topMinigamers = ( + const topMinigamers = ( await Promise.all( minigames.map(m => q( @@ -318,9 +317,9 @@ LIMIT 1;` ) ) ) - ).map((i: any) => [i[0].user_id, Minigames.find(m => m.column === i[0].m)!.name]); + ).map((i: any) => [i[0].user_id, Minigames.find(m => m.column === i[0].m)?.name]); - let userMap = {}; + const userMap = {}; for (const [id, m] of topMinigamers) { addToUserMap(userMap, id, `Rank 1 ${m}`); } @@ -337,7 +336,7 @@ LIMIT 1;` // Top clue hunters async function topClueHunters() { - let topClueHunters = ( + const topClueHunters = ( await Promise.all( ClueTiers.map(t => q( @@ -354,7 +353,7 @@ LIMIT 1;` .filter((i: any) => Boolean(i[0]?.id)) .map((i: any) => [i[0]?.id, i[0]?.n]); - let userMap = {}; + const userMap = {}; for (const [id, n] of topClueHunters) { addToUserMap(userMap, id, `Rank 1 ${n} Clues`); @@ -395,8 +394,8 @@ FROM user_stats ORDER BY "tithe_farms_completed" DESC LIMIT 2;` ]; - let res = (await Promise.all(queries.map(q))).map((i: any) => [i[0]?.id, i[0]?.desc]); - let userMap = {}; + const res = (await Promise.all(queries.map(q))).map((i: any) => [i[0]?.id, i[0]?.desc]); + const userMap = {}; for (const [id, desc] of res) { addToUserMap(userMap, id, desc); } @@ -413,7 +412,7 @@ LIMIT 2;` // Top slayers async function slayer() { - let topSlayers = ( + const topSlayers = ( await Promise.all( [mostSlayerPointsQuery, longerSlayerTaskStreakQuery, mostSlayerTasksDoneQuery].map(query => q(query)) ) @@ -421,7 +420,7 @@ LIMIT 2;` .filter((i: any) => Boolean(i[0]?.id)) .map((i: any) => [i[0]?.id, i[0]?.desc]); - let userMap = {}; + const userMap = {}; for (const [id, desc] of topSlayers) { addToUserMap(userMap, id, desc); } @@ -445,9 +444,7 @@ LIMIT 2;` '982989775399174184', '346304390858145792' ]; - const res = await prisma.$queryRaw< - { user_id: string; qty: number }[] - >`SELECT user_id, COUNT(user_id)::int AS qty + const res = await prisma.$queryRaw<{ user_id: string; qty: number }[]>`SELECT user_id, COUNT(user_id)::int AS qty FROM giveaway WHERE channel_id IN (${Prisma.join(GIVEAWAY_CHANNELS)}) AND user_id NOT IN ('157797566833098752') @@ -472,7 +469,7 @@ LIMIT 50;`; giveawayBank.add(giveaway.user_id, giveaway.loot as ItemBank); } - let userMap = {}; + const userMap = {}; const [[highestID, loot]] = giveawayBank.entries().sort((a, b) => b[1].value() - a[1].value()); addToUserMap(userMap, highestID, `Most Value Given Away (${loot.value()})`); @@ -488,9 +485,7 @@ LIMIT 50;`; // Global CL % async function globalCL() { - const result = await roboChimpClient.$queryRaw< - { id: string; total_cl_percent: number }[] - >`SELECT ((osb_cl_percent + bso_cl_percent) / 2) AS total_cl_percent, id::text AS id + const result = await roboChimpClient.$queryRaw<{ id: string; total_cl_percent: number }[]>`SELECT ((osb_cl_percent + bso_cl_percent) / 2) AS total_cl_percent, id::text AS id FROM public.user WHERE osb_cl_percent IS NOT NULL AND bso_cl_percent IS NOT NULL ORDER BY total_cl_percent DESC @@ -517,7 +512,7 @@ LIMIT 10;`; ['Global CL', globalCL] ] as const; - let failed: string[] = []; + const failed: string[] = []; await Promise.all( tup.map(async ([name, fn]) => { try { @@ -532,7 +527,7 @@ LIMIT 10;`; }) ); - let res = `**Roles** + const res = `**Roles** ${results.join('\n')} ${failed.length > 0 ? `Failed: ${failed.join(', ')}` : ''}`; diff --git a/src/lib/settings/minigames.ts b/src/lib/settings/minigames.ts index f3620046c8d..0d313b92d4c 100644 --- a/src/lib/settings/minigames.ts +++ b/src/lib/settings/minigames.ts @@ -1,4 +1,4 @@ -import { Minigame } from '@prisma/client'; +import type { Minigame } from '@prisma/client'; import { prisma } from './prisma'; diff --git a/src/lib/settings/prisma.ts b/src/lib/settings/prisma.ts index d8ea48c39ab..e62203a12b3 100644 --- a/src/lib/settings/prisma.ts +++ b/src/lib/settings/prisma.ts @@ -1,9 +1,10 @@ import { isMainThread } from 'node:worker_threads'; -import { Activity, activity_type_enum, Prisma, PrismaClient } from '@prisma/client'; +import type { Activity, Prisma } from '@prisma/client'; +import { PrismaClient, activity_type_enum } from '@prisma/client'; import { production } from '../../config'; -import { ActivityTaskData } from '../types/minions'; +import type { ActivityTaskData } from '../types/minions'; import { sqlLog } from '../util/logger'; declare global { @@ -34,7 +35,7 @@ function makePrismaClient(): PrismaClient { export const prisma = global.prisma || makePrismaClient(); global.prisma = prisma; -export let queryCountStore = { value: 0 }; +export const queryCountStore = { value: 0 }; if (isMainThread) { // @ts-ignore ignore @@ -68,8 +69,8 @@ export async function countUsersWithItemInCl(itemID: number, ironmenOnly: boolea WHERE ("collectionLogBank"->>'${itemID}') IS NOT NULL AND ("collectionLogBank"->>'${itemID}')::int >= 1 ${ironmenOnly ? 'AND "minion.ironman" = true' : ''};`; - const result = parseInt(((await prisma.$queryRawUnsafe(query)) as any)[0].count); - if (isNaN(result)) { + const result = Number.parseInt(((await prisma.$queryRawUnsafe(query)) as any)[0].count); + if (Number.isNaN(result)) { throw new Error(`countUsersWithItemInCl produced invalid number '${result}' for ${itemID}`); } return result; @@ -81,7 +82,7 @@ FROM activity WHERE user_id = ${BigInt(user.id)} GROUP BY type;`; - let result: Record = {} as Record; + const result: Record = {} as Record; for (const type of Object.values(activity_type_enum)) { result[type] = 0; } diff --git a/src/lib/settings/settings.ts b/src/lib/settings/settings.ts index 03a7e071b51..ee75e0c82a4 100644 --- a/src/lib/settings/settings.ts +++ b/src/lib/settings/settings.ts @@ -1,13 +1,13 @@ -import { Activity, NewUser, Prisma } from '@prisma/client'; -import { +import type { Activity, NewUser, Prisma } from '@prisma/client'; +import type { APIInteractionGuildMember, ButtonInteraction, ChatInputCommandInteraction, GuildMember, User } from 'discord.js'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; -import { CommandOptions } from 'mahoji/dist/lib/types'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { CommandOptions } from 'mahoji/dist/lib/types'; import { postCommand } from '../../mahoji/lib/postCommand'; import { preCommand } from '../../mahoji/lib/preCommand'; @@ -113,7 +113,7 @@ export async function runCommand({ if (!mahojiCommand) throw new Error('No command found'); const abstractCommand = convertMahojiCommandToAbstractCommand(mahojiCommand); - let error: Error | null = null; + const error: Error | null = null; let inhibited = false; try { const inhibitedReason = await preCommand({ @@ -134,7 +134,7 @@ export async function runCommand({ content: typeof inhibitedReason.reason! === 'string' ? inhibitedReason.reason - : inhibitedReason.reason!.content!, + : inhibitedReason.reason?.content!, ephemeral: true }); return null; diff --git a/src/lib/shadesKeys.ts b/src/lib/shadesKeys.ts index c0efc720278..6526334cda3 100644 --- a/src/lib/shadesKeys.ts +++ b/src/lib/shadesKeys.ts @@ -1,8 +1,8 @@ import { roll } from 'e'; import { Bank, LootTable } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; -import { UnifiedOpenable } from './openables'; +import type { UnifiedOpenable } from './openables'; import getOSItem from './util/getOSItem'; import resolveItems from './util/resolveItems'; diff --git a/src/lib/simulation/nex.ts b/src/lib/simulation/nex.ts index 3d234be9ae2..c62badb093b 100644 --- a/src/lib/simulation/nex.ts +++ b/src/lib/simulation/nex.ts @@ -1,5 +1,6 @@ import { userMention } from 'discord.js'; import { + Time, calcWhatPercent, clamp, increaseNumByPercent, @@ -8,8 +9,7 @@ import { randInt, reduceNumByPercent, roll, - sumArr, - Time + sumArr } from 'e'; import { Bank } from 'oldschooljs'; import { randomVariation } from 'oldschooljs/dist/util/util'; @@ -21,8 +21,8 @@ import itemID from '../util/itemID'; import { arrows, bolts, bows, crossbows } from '../util/minionUtils'; import resolveItems from '../util/resolveItems'; import { exponentialPercentScale, formatDuration, formatSkillRequirements, itemNameFromID } from '../util/smallUtils'; -import { NexNonUniqueTable, NexUniqueTable } from './misc'; import { TeamLoot } from './TeamLoot'; +import { NexNonUniqueTable, NexUniqueTable } from './misc'; const minStats: Skills = { defence: 90, @@ -31,7 +31,7 @@ const minStats: Skills = { }; export function nexGearStats(user: MUser) { - let { gear } = user; + const { gear } = user; const offence = calcWhatPercent(gear.range.stats.attack_ranged, 252); const defence = calcWhatPercent(gear.range.stats.defence_magic, 150); return { @@ -177,12 +177,12 @@ export function handleNexKills({ quantity, team }: NexContext) { export async function calculateNexDetails({ team }: { team: MUser[] }) { let maxTripLength = Math.max(...team.map(u => calcMaxTripLength(u))); let lengthPerKill = Time.Minute * 35; - let resultTeam: TeamMember[] = []; + const resultTeam: TeamMember[] = []; for (const member of team) { let { offence, defence, rangeGear } = nexGearStats(member); let deathChance = 100; - let nexKC = await member.getKC(NEX_ID); + const nexKC = await member.getKC(NEX_ID); const kcLearningCap = 500; const kcPercent = clamp(calcWhatPercent(nexKC, kcLearningCap), 0, 100); const messages: string[] = []; @@ -202,7 +202,7 @@ export async function calculateNexDetails({ team }: { team: MUser[] }) { const hasRigour = member.bitfield.includes(BitField.HasDexScroll); if (hasRigour) offence += 5; - let offensivePercents = [offence, clamp(calcWhatPercent(nexKC, 100), 0, 100)]; + const offensivePercents = [offence, clamp(calcWhatPercent(nexKC, 100), 0, 100)]; const totalOffensivePecent = sumArr(offensivePercents) / offensivePercents.length; const contribution = totalOffensivePecent; @@ -255,7 +255,7 @@ export async function calculateNexDetails({ team }: { team: MUser[] }) { lengthPerKill = clamp(lengthPerKill, Time.Minute * 6, Time.Hour); - let quantity = Math.floor(maxTripLength / lengthPerKill); + const quantity = Math.floor(maxTripLength / lengthPerKill); let wipedKill: number | null = null; diff --git a/src/lib/simulation/tempoross.ts b/src/lib/simulation/tempoross.ts index 9b281fb6f51..3b3f162e1d4 100644 --- a/src/lib/simulation/tempoross.ts +++ b/src/lib/simulation/tempoross.ts @@ -129,7 +129,7 @@ const fishTables = [ export function getTemporossLoot(quantity: number, fishingLevel: number, userBank: Bank) { const loot = new Bank(); - let lootTable = new LootTable() + const lootTable = new LootTable() .add('Spirit flakes', [32, 64], 2000) .add(PoolCasketTable, 1, 400) .add('Plank', [20, 30], 350) diff --git a/src/lib/simulation/toa.ts b/src/lib/simulation/toa.ts index c6dbd2d0716..874b048c285 100644 --- a/src/lib/simulation/toa.ts +++ b/src/lib/simulation/toa.ts @@ -1,7 +1,9 @@ -import { mentionCommand, SimpleTable } from '@oldschoolgg/toolkit'; -import { Minigame, XpGainSource } from '@prisma/client'; +import { SimpleTable, mentionCommand } from '@oldschoolgg/toolkit'; +import type { Minigame } from '@prisma/client'; +import { XpGainSource } from '@prisma/client'; import { bold } from 'discord.js'; import { + Time, calcPercentOfNum, calcWhatPercent, clamp, @@ -15,24 +17,23 @@ import { round, scaleNumber, sumArr, - Time, uniqueArr } from 'e'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { Bank, LootTable } from 'oldschooljs'; import { mahojiParseNumber, userStatsBankUpdate } from '../../mahoji/mahojiSettings'; import { Emoji } from '../constants'; import { getSimilarItems } from '../data/similarItems'; import { degradeItem } from '../degradeableItems'; -import { GearStats, UserFullGearSetup } from '../gear/types'; +import type { GearStats, UserFullGearSetup } from '../gear/types'; import { trackLoot } from '../lootTrack'; import { setupParty } from '../party'; import { getMinigameScore } from '../settings/minigames'; import { SkillsEnum } from '../skilling/types'; -import { constructGearSetup, Gear } from '../structures/Gear'; -import { MakePartyOptions, Skills } from '../types'; -import { TOAOptions } from '../types/minions'; +import { Gear, constructGearSetup } from '../structures/Gear'; +import type { MakePartyOptions, Skills } from '../types'; +import type { TOAOptions } from '../types/minions'; import { assert, channelIsSendable, @@ -421,7 +422,7 @@ export function calculateXPFromRaid({ let totalCombatXP = 0; for (const style of meleeStyles) { - let amount = calcPercentOfNum(percentOfRaid, xpPerStyle); + const amount = calcPercentOfNum(percentOfRaid, xpPerStyle); totalCombatXP += amount; promises.push( user.addXP({ @@ -490,7 +491,7 @@ const untradeables = [ ]; function untradeableRoll(kc: number, cl: Bank) { - let loot = new Bank(); + const loot = new Bank(); for (const { item, dropRate } of untradeables) { let rolls = 1; if (!cl.has(item.id) && kc > 5) { @@ -506,7 +507,7 @@ function untradeableRoll(kc: number, cl: Bank) { return loot; } -let TOAUniqueTable = new LootTable() +const TOAUniqueTable = new LootTable() .add('Lightbearer', 1, 7) .add("Osmumten's fang", 1, 7) .add("Elidinis' ward", 1, 3) @@ -572,7 +573,7 @@ const nonUniqueTable = [ function nonUniqueLoot({ points }: { points: number }) { assert(typeof points === 'number', `Points must be a number, received ${typeof points} ${points}.`); assert(points >= 1 && points <= 64_000, `Points (${points.toLocaleString()}) must be between 1-64,000`); - let loot = new Bank(); + const loot = new Bank(); for (let i = 0; i < 3; i++) { const [item, divisor] = randArrItem(nonUniqueTable); @@ -611,14 +612,14 @@ export function calcTOALoot({ users, raidLevel }: { users: TOALootUser[]; raidLe const totalTeamPoints = sumArr(users.map(i => i.points)); // The number of raid levels less than or equal to 400 - let x = Math.min(raidLevel, 400); + const x = Math.min(raidLevel, 400); // The number of raid levels from 400 to 550 - let y = Math.min(150, raidLevel - 400); + const y = Math.min(150, raidLevel - 400); // prettier-ignore - let pointsForOnePercentUniqueChance = 10_500 - 20 * (x + (y / 3)); - let chanceOfUnique = Math.min(totalTeamPoints / pointsForOnePercentUniqueChance, 55); - let didGetUnique = percentChance(chanceOfUnique); + const pointsForOnePercentUniqueChance = 10_500 - 20 * (x + y / 3); + const chanceOfUnique = Math.min(totalTeamPoints / pointsForOnePercentUniqueChance, 55); + const didGetUnique = percentChance(chanceOfUnique); const uniqueRecipient = didGetUnique ? uniqueDeciderTable.roll() : null; const messages: string[] = [ @@ -639,9 +640,9 @@ export function calcTOALoot({ users, raidLevel }: { users: TOALootUser[]; raidLe } loot.add(user.id, untradeableRoll(user.kc, user.cl)); - let pointsForOnePercentPetChance = 350_000 - 700 * (x + y / 3); - let chanceOfPet = Math.min(user.points / pointsForOnePercentPetChance, 55); - let didGetPet = percentChance(chanceOfPet); + const pointsForOnePercentPetChance = 350_000 - 700 * (x + y / 3); + const chanceOfPet = Math.min(user.points / pointsForOnePercentPetChance, 55); + const didGetPet = percentChance(chanceOfPet); if (didGetPet) { loot.add(user.id, "Tumeken's guardian"); } @@ -773,7 +774,7 @@ function calcDeathChance(totalAttempts: number, raidLevel: RaidLevel, tobAndCoxK } const match = map.find(i => totalAttempts <= i.attemptsMax)! ?? map[map.length - 1]; - const nextMatch = map[map.indexOf(match) + 1] ?? { attemptsMax: Infinity, increase: 0 }; + const nextMatch = map[map.indexOf(match) + 1] ?? { attemptsMax: Number.POSITIVE_INFINITY, increase: 0 }; const increase = scaleNumber( totalAttempts, @@ -810,7 +811,7 @@ function calculateTotalEffectiveness({ skillsAsLevels: Skills; randomNess: boolean; }) { - let percents = []; + const percents = []; percents.push(clamp(calcWhatPercent(totalKC, 20), 0, 100)); percents.push(clamp(calcWhatPercent(totalAttempts, 20), 0, 100)); @@ -829,7 +830,7 @@ function calculateTotalEffectiveness({ } function calculateAdditionalDeathChance(raidLevel: RaidLevel, attempts: number) { - const minDeathChance = mileStoneBaseDeathChances.find(i => i.level === raidLevel)!.minChance; + const minDeathChance = mileStoneBaseDeathChances.find(i => i.level === raidLevel)?.minChance; if (!minDeathChance) return 0; let divisor = 1; for (const number of [50, 100, 200, 300]) { @@ -848,8 +849,8 @@ function calculatePointsAndDeaths( coxAndTobKC: number, teamSize: number ) { - let deaths: number[] = []; - let deathChance = calcDeathChance(totalAttempts, raidLevel, coxAndTobKC); + const deaths: number[] = []; + const deathChance = calcDeathChance(totalAttempts, raidLevel, coxAndTobKC); const harshEffectivenessScale = exponentialPercentScale(effectiveness, 0.05); let points = estimatePoints(raidLevel, teamSize) / teamSize; @@ -893,14 +894,8 @@ function calcSetupPercent( } // For melee compare the highest melee attack stat of max setup with the highest melee attack stat of the user if (melee) { - let maxMeleeStat = Math.max( - maxStats['attack_stab'], - Math.max(maxStats['attack_slash'], maxStats['attack_crush']) - ); - let userMeleeStat = Math.max( - userStats['attack_stab'], - Math.max(userStats['attack_slash'], userStats['attack_crush']) - ); + const maxMeleeStat = Math.max(maxStats.attack_stab, Math.max(maxStats.attack_slash, maxStats.attack_crush)); + const userMeleeStat = Math.max(userStats.attack_stab, Math.max(userStats.attack_slash, userStats.attack_crush)); totalPercent += Math.min(100, calcWhatPercent(userMeleeStat, maxMeleeStat)); numKeys++; } @@ -983,7 +978,7 @@ async function calcTOAInput({ } // Between 8-1 brews - let brewsNeeded = Math.max(1, 9 - Math.max(1, Math.ceil((kc + 1) / 12))); + const brewsNeeded = Math.max(1, 9 - Math.max(1, Math.ceil((kc + 1) / 12))); let restoresNeeded = Math.max(2, Math.floor(brewsNeeded / 3)); if (kc < 2) restoresNeeded += 3; else if (kc < 5) restoresNeeded += 2; @@ -996,7 +991,7 @@ async function calcTOAInput({ throw new Error(`${user.logName} had no range weapon for TOA`); } if (rangeWeapon.id !== itemID('Bow of faerdhinen (c)')) { - cost.add(user.gear.range.ammo!.item, BOW_ARROWS_NEEDED * quantity); + cost.add(user.gear.range.ammo?.item, BOW_ARROWS_NEEDED * quantity); } if (user.gear.melee.hasEquipped('Amulet of blood fury')) { cost.remove('Saradomin brew(4)', quantity); @@ -1092,9 +1087,7 @@ export async function checkTOATeam(users: MUser[], raidLevel: number, quantity: Time.Hour, quantity ); - if (!checkResult[0]) { - continue; - } else { + if (checkResult[1]) { return checkResult[1]; } } @@ -1130,7 +1123,7 @@ export async function toaStartCommand( return "Your minion is busy, so you can't start a raid."; } - let maxSize = mahojiParseNumber({ input: teamSize, min: 2, max: 8 }) ?? 8; + const maxSize = mahojiParseNumber({ input: teamSize, min: 2, max: 8 }) ?? 8; const partyOptions: MakePartyOptions = { leader: user, @@ -1271,7 +1264,7 @@ export async function toaStartCommand( })) }); - let userArr: [string, number, number[]][][] = []; + const userArr: [string, number, number[]][][] = []; for (let i = 0; i < quantity; i++) { const thisQtyArr: [string, number, number[]][] = []; for (const user of toaSimResults[i].parsedTeam) { @@ -1318,7 +1311,7 @@ const totalSpeedReductions = sumArr(miscBoosts.map(i => i[1])) + 15; // Os fang; -let baseTOADurations: Record = { +const baseTOADurations: Record = { 1: Time.Minute * 50, 100: Time.Minute * 50, 150: Time.Minute * 60, @@ -1355,11 +1348,11 @@ export function createTOATeam({ disableVariation?: true; quantity: number; }) { - let arr = []; + const arr = []; const messages: string[] = []; for (const { user, toaAttempts, minigameScores } of team) { - let gearStats = calculateUserGearPercents(user.gear, raidLevel); + const gearStats = calculateUserGearPercents(user.gear, raidLevel); const totalAttempts = toaAttempts; const totalKC = minigameScores.tombs_of_amascut; const effectiveness = calculateTotalEffectiveness({ @@ -1393,8 +1386,8 @@ export function createTOATeam({ const teamSize = team.length; const maxScaling = 350; assert(teamSize >= 1 && teamSize < 9, 'TOA team must be 1-8 users'); - let individualReductions: { id: string; reduction: number }[] = []; - let reductions: Record = {}; + const individualReductions: { id: string; reduction: number }[] = []; + const reductions: Record = {}; const results: { duration: number; @@ -1406,7 +1399,7 @@ export function createTOATeam({ messages: string[]; }[] = []; - let parsedTeam = []; + const parsedTeam = []; for (const u of arr) { let userPercentChange = 0; @@ -1418,16 +1411,16 @@ export function createTOATeam({ // Reduce time for KC const kcPercent = clamp(calcWhatPercent(u.totalAttempts, maxScaling), 1, 100); - let kcPercentBoost = calcPerc(kcPercent, speedReductionForKC); + const kcPercentBoost = calcPerc(kcPercent, speedReductionForKC); userPercentChange += kcPercentBoost; let weaponBoosts: string[] = ["Osmumten's fang", 'Ghrazi rapier']; - let boostAmounts = [15, 6] as const; + const boostAmounts = [15, 6] as const; // If the raid level is less than 300, Ghrazi rapier is the BIS instead. if (raidLevel < 300) weaponBoosts = [weaponBoosts[1], weaponBoosts[0]]; for (let i = 0; i < weaponBoosts.length; i++) { - let amount = boostAmounts[i]; + const amount = boostAmounts[i]; if (u.gear.melee.hasEquipped(weaponBoosts[i])) { userPercentChange += amount; break; @@ -1448,7 +1441,7 @@ export function createTOATeam({ } userPercentChange = calcWhatPercent(userPercentChange, totalSpeedReductions); - let reduction = round(userPercentChange / teamSize, 1); + const reduction = round(userPercentChange / teamSize, 1); individualReductions.push({ id: u.id, reduction: userPercentChange }); reductions[u.user.id] = reduction; @@ -1477,7 +1470,7 @@ export function createTOATeam({ totalReduction = round(totalReduction / (teamSize - 1), 2); messages.push( `${ - arr.find(i => i.id === worstTeamMember.id)!.user.badgedUsername + arr.find(i => i.id === worstTeamMember.id)?.user.badgedUsername } is being carried by the rest of the team so they don't affect the raid time!` ); } else { @@ -1508,7 +1501,7 @@ export function createTOATeam({ } for (let i = 0; i < TOARooms.length; i++) { - let room = TOARooms[i]; + const room = TOARooms[i]; if (usersWithPointsAndDeaths.every(member => member.deaths.includes(i))) { wipedRoom = room.id; @@ -1608,7 +1601,7 @@ export async function toaHelpCommand(user: MUser, channelID: string) { totalUniques += user.cl.amount(item); } - let str = `**Tombs of Amascut** + const str = `**Tombs of Amascut** **Attempts:** ${stats.toa_attempts} **Entry Mode:** ${entryKC} KC @@ -1618,18 +1611,18 @@ export async function toaHelpCommand(user: MUser, channelID: string) { totalUniques > 0 ? `(1 unique per ${Math.floor( stats.total_toa_points / totalUniques - ).toLocaleString()} pts, one unique every ${Math.floor( + ).toLocaleString()} pts, one unique every ${Math.floor( totalKC / totalUniques - )} raids, one unique every ${formatDuration( + )} raids, one unique every ${formatDuration( (stats.total_toa_duration_minutes * Time.Minute) / totalUniques - )})` + )})` : '' } **Requirements** ${toaRequirements .map(i => { - let res = i.doesMeet({ user, gearStats, quantity: 1, allItemsOwned: user.allItemsOwned }); + const res = i.doesMeet({ user, gearStats, quantity: 1, allItemsOwned: user.allItemsOwned }); if (typeof res === 'string') { return `- ❌ ${bold(i.name)} ${res}`; } diff --git a/src/lib/simulation/tob.ts b/src/lib/simulation/tob.ts index 3148333da4f..cd0903b2989 100644 --- a/src/lib/simulation/tob.ts +++ b/src/lib/simulation/tob.ts @@ -1,8 +1,8 @@ import { SimpleTable } from '@oldschoolgg/toolkit'; import { percentChance, roll, sumArr } from 'e'; import { Bank, LootTable } from 'oldschooljs'; -import { LootBank } from 'oldschooljs/dist/meta/types'; -import { convertLootBanksToItemBanks, JSONClone } from 'oldschooljs/dist/util'; +import type { LootBank } from 'oldschooljs/dist/meta/types'; +import { JSONClone, convertLootBanksToItemBanks } from 'oldschooljs/dist/util'; import { TOBRooms } from '../data/tob'; import { assert } from '../util/logError'; @@ -101,7 +101,7 @@ export class TheatreOfBloodClass { if (isHardMode) { // Add 15% extra regular loot for hard mode: for (const [itemID] of Object.entries(loot.bank)) { - loot.bank[parseInt(itemID)] = Math.ceil(loot.bank[parseInt(itemID)] * 1.15); + loot.bank[Number.parseInt(itemID)] = Math.ceil(loot.bank[Number.parseInt(itemID)] * 1.15); } // Add HM Tertiary drops: dust / kits loot.add(HardModeExtraTable.roll()); @@ -151,7 +151,7 @@ export class TheatreOfBloodClass { const totalDeaths = sumArr(parsedTeam.map(i => i.numDeaths)); - let percentBaseChanceOfUnique = (options.hardMode ? 13 : 11) * (teamPoints / maxPointsTeamCanGet); + const percentBaseChanceOfUnique = (options.hardMode ? 13 : 11) * (teamPoints / maxPointsTeamCanGet); const purpleReceived = percentChance(percentBaseChanceOfUnique); const purpleRecipient = purpleReceived ? this.uniqueDecide(parsedTeam) : null; diff --git a/src/lib/simulation/wintertodt.ts b/src/lib/simulation/wintertodt.ts index 2e6f7671fd5..24cf7f0da6c 100644 --- a/src/lib/simulation/wintertodt.ts +++ b/src/lib/simulation/wintertodt.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ import { SimpleTable } from '@oldschoolgg/toolkit'; import { calcPercentOfNum, randInt, roll } from 'e'; import { Bank } from 'oldschooljs'; @@ -6,8 +5,9 @@ import LootTable from 'oldschooljs/dist/structures/LootTable'; import { convertXPtoLVL } from 'oldschooljs/dist/util/util'; import { MAX_XP } from '../constants'; -import { LevelRequirements, SkillsEnum } from '../skilling/types'; -import { ItemBank } from '../types'; +import type { LevelRequirements } from '../skilling/types'; +import { SkillsEnum } from '../skilling/types'; +import type { ItemBank } from '../types'; import itemID from '../util/itemID'; import resolveItems from '../util/resolveItems'; import { normal } from '../util/smallUtils'; @@ -258,7 +258,7 @@ export class WintertodtCrateClass { for (let i = 0; i < rolls; i++) { const rolledUnique = this.rollUnique(new Bank().add(itemsOwned).add(loot), firemakingXP); - if (rolledUnique instanceof Array) { + if (Array.isArray(rolledUnique)) { const [itemID, qty] = rolledUnique; loot.add(itemID, qty); continue; diff --git a/src/lib/skilling/functions/calcFarmingContracts.ts b/src/lib/skilling/functions/calcFarmingContracts.ts index 6a8439e3c99..a96533f14c9 100644 --- a/src/lib/skilling/functions/calcFarmingContracts.ts +++ b/src/lib/skilling/functions/calcFarmingContracts.ts @@ -3,7 +3,7 @@ import { randArrItem, randInt, roll } from 'e'; import { Bank, LootTable } from 'oldschooljs'; import { HighSeedPackTable, LowSeedPackTable, MediumSeedPackTable } from '../../data/seedPackTables'; -import { PlantTier } from '../../minions/farming/types'; +import type { PlantTier } from '../../minions/farming/types'; export function openSeedPack(seedTier: number): Bank { const loot = new Bank(); diff --git a/src/lib/skilling/functions/calcsFarming.ts b/src/lib/skilling/functions/calcsFarming.ts index 9a5fea0bb34..170e12dd983 100644 --- a/src/lib/skilling/functions/calcsFarming.ts +++ b/src/lib/skilling/functions/calcsFarming.ts @@ -1,6 +1,7 @@ import { randInt } from 'e'; -import { Plant, SkillsEnum } from '../types'; +import type { Plant } from '../types'; +import { SkillsEnum } from '../types'; export function calcNumOfPatches(plant: Plant, user: MUser, qp: number): [number] { let numOfPatches = plant.defaultNumOfPatches; diff --git a/src/lib/skilling/functions/calcsHunter.ts b/src/lib/skilling/functions/calcsHunter.ts index a1bc41fc866..768e974a96d 100644 --- a/src/lib/skilling/functions/calcsHunter.ts +++ b/src/lib/skilling/functions/calcsHunter.ts @@ -2,7 +2,7 @@ import { Time } from 'e'; import LootTable from 'oldschooljs/dist/structures/LootTable'; import { percentChance } from '../../util'; -import { Creature } from '../types'; +import type { Creature } from '../types'; export function calcLootXPHunting( currentLevel: number, @@ -30,7 +30,7 @@ export function calcLootXPHunting( const experienceFactor = experienceScore / (Time.Hour / timeInSeconds); const maxPercentIncrease = 10; - let percentIncrease = Math.min(Math.floor(experienceFactor), maxPercentIncrease); + const percentIncrease = Math.min(Math.floor(experienceFactor), maxPercentIncrease); chanceOfSuccess += chanceOfSuccess * (percentIncrease / 100); } @@ -48,10 +48,10 @@ export function calcLootXPHunting( } export function generateHerbiTable(currentHerbLvl: number, gotMagicSec: boolean): LootTable { - let herbiTable = new LootTable(); + const herbiTable = new LootTable(); if (currentHerbLvl < 31) return herbiTable; herbiTable.tertiary(6500, 'Herbi'); - let baseYield = gotMagicSec ? 2 : 1; + const baseYield = gotMagicSec ? 2 : 1; herbiTable .add('Grimy guam leaf', [baseYield, 4]) .add('Grimy irit leaf', [baseYield, 4]) diff --git a/src/lib/skilling/functions/calcsRunecrafting.ts b/src/lib/skilling/functions/calcsRunecrafting.ts index 3c820ec19e7..ff4808461a7 100644 --- a/src/lib/skilling/functions/calcsRunecrafting.ts +++ b/src/lib/skilling/functions/calcsRunecrafting.ts @@ -30,7 +30,7 @@ export function raimentBonus(user: MUser, quantity: number): number { let bonusQuantity = 0; if ( user.gear.skilling.hasEquipped( - Object.keys(Runecraft.raimentsOfTheEyeItems).map(i => parseInt(i)), + Object.keys(Runecraft.raimentsOfTheEyeItems).map(i => Number.parseInt(i)), true ) ) { @@ -39,7 +39,7 @@ export function raimentBonus(user: MUser, quantity: number): number { } else { // For each Raiments of the Eye item, check if they have it, give its' quantity boost if so (NO bonus XP). for (const [itemID, bonus] of Object.entries(Runecraft.raimentsOfTheEyeItems)) { - if (user.gear.skilling.hasEquipped([parseInt(itemID)], false)) { + if (user.gear.skilling.hasEquipped([Number.parseInt(itemID)], false)) { const amountToAdd = Math.floor(quantity * (bonus / 100)); bonusQuantity += amountToAdd; } diff --git a/src/lib/skilling/functions/determineMiningTime.ts b/src/lib/skilling/functions/determineMiningTime.ts index b65ae8e7eb3..b60c1eb4fca 100644 --- a/src/lib/skilling/functions/determineMiningTime.ts +++ b/src/lib/skilling/functions/determineMiningTime.ts @@ -1,7 +1,7 @@ -import { percentChance, Time } from 'e'; +import { Time, percentChance } from 'e'; import { calcMaxTripLength } from '../../util/calcMaxTripLength'; -import { Ore } from './../types'; +import type { Ore } from './../types'; interface MiningTimeOptions { quantity: number | undefined; diff --git a/src/lib/skilling/functions/determineWoodcuttingTime.ts b/src/lib/skilling/functions/determineWoodcuttingTime.ts index ec1b4e356fa..b94cee7444c 100644 --- a/src/lib/skilling/functions/determineWoodcuttingTime.ts +++ b/src/lib/skilling/functions/determineWoodcuttingTime.ts @@ -1,9 +1,9 @@ -import { percentChance, Time } from 'e'; +import { Time, percentChance } from 'e'; import { calcMaxTripLength } from '../../util/calcMaxTripLength'; import resolveItems from '../../util/resolveItems'; -import { MUserClass } from './../../MUser'; -import { Log } from './../types'; +import type { MUserClass } from './../../MUser'; +import type { Log } from './../types'; interface WoodcuttingTimeOptions { quantity: number | undefined; diff --git a/src/lib/skilling/functions/getFarmingInfo.ts b/src/lib/skilling/functions/getFarmingInfo.ts index 20125b37308..2440ff648d1 100644 --- a/src/lib/skilling/functions/getFarmingInfo.ts +++ b/src/lib/skilling/functions/getFarmingInfo.ts @@ -1,12 +1,13 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; -import { User } from '@prisma/client'; +import type { User } from '@prisma/client'; import { Time } from 'e'; import { mahojiUsersSettingsFetch } from '../../../mahoji/mahojiSettings'; import { defaultPatches } from '../../minions/farming'; -import { IPatchData, IPatchDataDetailed } from '../../minions/farming/types'; +import type { IPatchData, IPatchDataDetailed } from '../../minions/farming/types'; import { assert, formatDuration } from '../../util'; -import { farmingKeys, FarmingPatchName, farmingPatchNames, findPlant } from '../../util/farmingHelpers'; +import type { FarmingPatchName } from '../../util/farmingHelpers'; +import { farmingKeys, farmingPatchNames, findPlant } from '../../util/farmingHelpers'; export function getFarmingInfoFromUser(user: User) { const patches: Record = {} as Record; @@ -62,7 +63,7 @@ export function getFarmingInfoFromUser(user: User) { } export async function getFarmingInfo(userID: string) { - let keys: Partial> = {}; + const keys: Partial> = {}; for (const key of farmingKeys) keys[key] = true; const userData = await mahojiUsersSettingsFetch(userID, keys); return getFarmingInfoFromUser(userData as User); diff --git a/src/lib/skilling/functions/questRequirements.ts b/src/lib/skilling/functions/questRequirements.ts index 7f78d0b08d2..1b571e30288 100644 --- a/src/lib/skilling/functions/questRequirements.ts +++ b/src/lib/skilling/functions/questRequirements.ts @@ -1,4 +1,4 @@ -import { Skills } from '../../types'; +import type { Skills } from '../../types'; export const sinsOfTheFatherSkillRequirements: Skills = { woodcutting: 62, diff --git a/src/lib/skilling/skills/agility.ts b/src/lib/skilling/skills/agility.ts index e4f45658b4f..c1d08216ad7 100644 --- a/src/lib/skilling/skills/agility.ts +++ b/src/lib/skilling/skills/agility.ts @@ -1,5 +1,6 @@ import { Emoji } from '../../constants'; -import { Course, SkillsEnum } from '../types'; +import type { Course } from '../types'; +import { SkillsEnum } from '../types'; export const courses: Course[] = [ { diff --git a/src/lib/skilling/skills/construction/constructables.ts b/src/lib/skilling/skills/construction/constructables.ts index a0e70e7dcae..66980848cc6 100644 --- a/src/lib/skilling/skills/construction/constructables.ts +++ b/src/lib/skilling/skills/construction/constructables.ts @@ -3,19 +3,19 @@ import { itemID } from 'oldschooljs/dist/util'; interface Constructable { id: number; name: string; - input: [Plank, number]; + input: [number, number]; xp: number; level: number; nails?: number; ticks: number; } -export enum Plank { - Plank = itemID('Plank'), - OakPlank = itemID('Oak plank'), - TeakPlank = itemID('Teak plank'), - MahoganyPlank = itemID('Mahogany plank') -} +export const Plank = { + Plank: itemID('Plank'), + OakPlank: itemID('Oak plank'), + TeakPlank: itemID('Teak plank'), + MahoganyPlank: itemID('Mahogany plank') +} as const; const Constructables: Constructable[] = [ { diff --git a/src/lib/skilling/skills/cooking/cooking.ts b/src/lib/skilling/skills/cooking/cooking.ts index 8e5fe0333a9..95c634278bb 100644 --- a/src/lib/skilling/skills/cooking/cooking.ts +++ b/src/lib/skilling/skills/cooking/cooking.ts @@ -1,6 +1,7 @@ import { Emoji } from '../../../constants'; import itemID from '../../../util/itemID'; -import { Cookable, SkillsEnum } from '../../types'; +import type { Cookable } from '../../types'; +import { SkillsEnum } from '../../types'; export const Cookables: Cookable[] = [ { diff --git a/src/lib/skilling/skills/cooking/leapingFish.ts b/src/lib/skilling/skills/cooking/leapingFish.ts index 1cc67c89d2b..8053ee9e742 100644 --- a/src/lib/skilling/skills/cooking/leapingFish.ts +++ b/src/lib/skilling/skills/cooking/leapingFish.ts @@ -1,5 +1,5 @@ import getOSItem from '../../../util/getOSItem'; -import { CutLeapingFish } from '../../types'; +import type { CutLeapingFish } from '../../types'; const LeapingFish: CutLeapingFish[] = [ { diff --git a/src/lib/skilling/skills/crafting/craftables/birdhouse.ts b/src/lib/skilling/skills/crafting/craftables/birdhouse.ts index cb5f502fb08..69589e0c683 100644 --- a/src/lib/skilling/skills/crafting/craftables/birdhouse.ts +++ b/src/lib/skilling/skills/crafting/craftables/birdhouse.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Craftable } from '../../../types'; +import type { Craftable } from '../../../types'; const Birdhouse: Craftable[] = [ { diff --git a/src/lib/skilling/skills/crafting/craftables/built.ts b/src/lib/skilling/skills/crafting/craftables/built.ts index 3d310ee617d..0ebc0e00ad6 100644 --- a/src/lib/skilling/skills/crafting/craftables/built.ts +++ b/src/lib/skilling/skills/crafting/craftables/built.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Craftable } from '../../../types'; +import type { Craftable } from '../../../types'; const Built: Craftable[] = [ { diff --git a/src/lib/skilling/skills/crafting/craftables/dragonhide.ts b/src/lib/skilling/skills/crafting/craftables/dragonhide.ts index 8c3baa40602..345a2dd933b 100644 --- a/src/lib/skilling/skills/crafting/craftables/dragonhide.ts +++ b/src/lib/skilling/skills/crafting/craftables/dragonhide.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Craftable } from '../../../types'; +import type { Craftable } from '../../../types'; const Dragonhide: Craftable[] = [ { diff --git a/src/lib/skilling/skills/crafting/craftables/gems.ts b/src/lib/skilling/skills/crafting/craftables/gems.ts index 6914aa91b48..6e101a43a6f 100644 --- a/src/lib/skilling/skills/crafting/craftables/gems.ts +++ b/src/lib/skilling/skills/crafting/craftables/gems.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Craftable } from '../../../types'; +import type { Craftable } from '../../../types'; const Gems: Craftable[] = [ { diff --git a/src/lib/skilling/skills/crafting/craftables/glassblowing.ts b/src/lib/skilling/skills/crafting/craftables/glassblowing.ts index a6d7aa9c2bf..7b469fb2384 100644 --- a/src/lib/skilling/skills/crafting/craftables/glassblowing.ts +++ b/src/lib/skilling/skills/crafting/craftables/glassblowing.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Craftable } from '../../../types'; +import type { Craftable } from '../../../types'; const Glassblowing: Craftable[] = [ { diff --git a/src/lib/skilling/skills/crafting/craftables/gold.ts b/src/lib/skilling/skills/crafting/craftables/gold.ts index 1a1bbae6729..feb84009d67 100644 --- a/src/lib/skilling/skills/crafting/craftables/gold.ts +++ b/src/lib/skilling/skills/crafting/craftables/gold.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Craftable } from '../../../types'; +import type { Craftable } from '../../../types'; const Gold: Craftable[] = [ { diff --git a/src/lib/skilling/skills/crafting/craftables/index.ts b/src/lib/skilling/skills/crafting/craftables/index.ts index ede6c1c1b71..6e9d50d1b6f 100644 --- a/src/lib/skilling/skills/crafting/craftables/index.ts +++ b/src/lib/skilling/skills/crafting/craftables/index.ts @@ -1,4 +1,4 @@ -import { Craftable } from '../../../types'; +import type { Craftable } from '../../../types'; import Birdhouse from './birdhouse'; import Built from './built'; import Dragonhide from './dragonhide'; diff --git a/src/lib/skilling/skills/crafting/craftables/leather.ts b/src/lib/skilling/skills/crafting/craftables/leather.ts index 93e8bce071f..6146b4861ac 100644 --- a/src/lib/skilling/skills/crafting/craftables/leather.ts +++ b/src/lib/skilling/skills/crafting/craftables/leather.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Craftable } from '../../../types'; +import type { Craftable } from '../../../types'; const Leather: Craftable[] = [ { diff --git a/src/lib/skilling/skills/crafting/craftables/misc.ts b/src/lib/skilling/skills/crafting/craftables/misc.ts index 721f105c03e..90265ac214d 100644 --- a/src/lib/skilling/skills/crafting/craftables/misc.ts +++ b/src/lib/skilling/skills/crafting/craftables/misc.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Craftable } from '../../../types'; +import type { Craftable } from '../../../types'; const Misc: Craftable[] = [ { diff --git a/src/lib/skilling/skills/crafting/craftables/silver.ts b/src/lib/skilling/skills/crafting/craftables/silver.ts index bd370cba987..a7e6321cc9f 100644 --- a/src/lib/skilling/skills/crafting/craftables/silver.ts +++ b/src/lib/skilling/skills/crafting/craftables/silver.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Craftable } from '../../../types'; +import type { Craftable } from '../../../types'; const Silver: Craftable[] = [ { diff --git a/src/lib/skilling/skills/crafting/craftables/tanning.ts b/src/lib/skilling/skills/crafting/craftables/tanning.ts index 54511dc31de..f6bd96753a9 100644 --- a/src/lib/skilling/skills/crafting/craftables/tanning.ts +++ b/src/lib/skilling/skills/crafting/craftables/tanning.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Craftable } from '../../../types'; +import type { Craftable } from '../../../types'; const Tanning: Craftable[] = [ { diff --git a/src/lib/skilling/skills/farming/allotments.ts b/src/lib/skilling/skills/farming/allotments.ts index b35c2a316fd..3e1bb472fba 100644 --- a/src/lib/skilling/skills/farming/allotments.ts +++ b/src/lib/skilling/skills/farming/allotments.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../util/itemID'; -import { Plant } from '../../types'; +import type { Plant } from '../../types'; const allotmentPlants: Plant[] = [ { diff --git a/src/lib/skilling/skills/farming/fruitTrees.ts b/src/lib/skilling/skills/farming/fruitTrees.ts index f23fb4d1ec9..b1acd498758 100644 --- a/src/lib/skilling/skills/farming/fruitTrees.ts +++ b/src/lib/skilling/skills/farming/fruitTrees.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../util/itemID'; -import { Plant } from '../../types'; +import type { Plant } from '../../types'; const fruitTrees: Plant[] = [ { diff --git a/src/lib/skilling/skills/farming/herbPlants.ts b/src/lib/skilling/skills/farming/herbPlants.ts index 56b7e61f74b..0443d6f3134 100644 --- a/src/lib/skilling/skills/farming/herbPlants.ts +++ b/src/lib/skilling/skills/farming/herbPlants.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../util/itemID'; -import { Plant } from '../../types'; +import type { Plant } from '../../types'; const herbPlants: Plant[] = [ { diff --git a/src/lib/skilling/skills/farming/hops.ts b/src/lib/skilling/skills/farming/hops.ts index fbac0155745..4bc64786b47 100644 --- a/src/lib/skilling/skills/farming/hops.ts +++ b/src/lib/skilling/skills/farming/hops.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../util/itemID'; -import { Plant } from '../../types'; +import type { Plant } from '../../types'; const hopsPlants: Plant[] = [ { diff --git a/src/lib/skilling/skills/farming/index.ts b/src/lib/skilling/skills/farming/index.ts index 8710e6f6141..8e2b83d63cf 100644 --- a/src/lib/skilling/skills/farming/index.ts +++ b/src/lib/skilling/skills/farming/index.ts @@ -3,7 +3,8 @@ import { CropUpgradeType } from '@prisma/client'; import { Emoji } from '../../../constants'; import getOSItem from '../../../util/getOSItem'; import itemID from '../../../util/itemID'; -import { Plant, SkillsEnum } from '../../types'; +import type { Plant } from '../../types'; +import { SkillsEnum } from '../../types'; import allotmentPlants from './allotments'; import fruitTrees from './fruitTrees'; import herbPlants from './herbPlants'; diff --git a/src/lib/skilling/skills/farming/specialPlants.ts b/src/lib/skilling/skills/farming/specialPlants.ts index f75856b49a0..e902f2f4b23 100644 --- a/src/lib/skilling/skills/farming/specialPlants.ts +++ b/src/lib/skilling/skills/farming/specialPlants.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../util/itemID'; -import { Plant } from '../../types'; +import type { Plant } from '../../types'; const specialPlants: Plant[] = [ { diff --git a/src/lib/skilling/skills/farming/trees.ts b/src/lib/skilling/skills/farming/trees.ts index fb193499dda..7eef775ffa7 100644 --- a/src/lib/skilling/skills/farming/trees.ts +++ b/src/lib/skilling/skills/farming/trees.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../util/itemID'; -import { Plant } from '../../types'; +import type { Plant } from '../../types'; const trees: Plant[] = [ { diff --git a/src/lib/skilling/skills/firemaking.ts b/src/lib/skilling/skills/firemaking.ts index 8b08bc7a7e4..1d265511618 100644 --- a/src/lib/skilling/skills/firemaking.ts +++ b/src/lib/skilling/skills/firemaking.ts @@ -1,6 +1,7 @@ import { Emoji } from '../../constants'; import itemID from '../../util/itemID'; -import { Burnable, SkillsEnum } from '../types'; +import type { Burnable } from '../types'; +import { SkillsEnum } from '../types'; const burnables: Burnable[] = [ { diff --git a/src/lib/skilling/skills/fishing.ts b/src/lib/skilling/skills/fishing.ts index 01fadc19616..859c2f636d4 100644 --- a/src/lib/skilling/skills/fishing.ts +++ b/src/lib/skilling/skills/fishing.ts @@ -1,6 +1,7 @@ import { Emoji } from '../../constants'; import itemID from '../../util/itemID'; -import { Fish, SkillsEnum } from '../types'; +import type { Fish } from '../types'; +import { SkillsEnum } from '../types'; const fishes: Fish[] = [ { diff --git a/src/lib/skilling/skills/fletching/fletchables/arrows.ts b/src/lib/skilling/skills/fletching/fletchables/arrows.ts index 38de9a53135..82d075c5550 100644 --- a/src/lib/skilling/skills/fletching/fletchables/arrows.ts +++ b/src/lib/skilling/skills/fletching/fletchables/arrows.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Fletchable } from '../../../types'; +import type { Fletchable } from '../../../types'; const Arrows: Fletchable[] = [ { diff --git a/src/lib/skilling/skills/fletching/fletchables/bolts.ts b/src/lib/skilling/skills/fletching/fletchables/bolts.ts index ea50f81a7a7..ef305e1f22d 100644 --- a/src/lib/skilling/skills/fletching/fletchables/bolts.ts +++ b/src/lib/skilling/skills/fletching/fletchables/bolts.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Fletchable } from '../../../types'; +import type { Fletchable } from '../../../types'; const Bolts: Fletchable[] = [ { diff --git a/src/lib/skilling/skills/fletching/fletchables/bows.ts b/src/lib/skilling/skills/fletching/fletchables/bows.ts index f0f49c7ca70..5f68f29adfc 100644 --- a/src/lib/skilling/skills/fletching/fletchables/bows.ts +++ b/src/lib/skilling/skills/fletching/fletchables/bows.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Fletchable } from '../../../types'; +import type { Fletchable } from '../../../types'; const Bows: Fletchable[] = [ { diff --git a/src/lib/skilling/skills/fletching/fletchables/crossbows.ts b/src/lib/skilling/skills/fletching/fletchables/crossbows.ts index b9015f22490..f8997e28562 100644 --- a/src/lib/skilling/skills/fletching/fletchables/crossbows.ts +++ b/src/lib/skilling/skills/fletching/fletchables/crossbows.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Fletchable } from '../../../types'; +import type { Fletchable } from '../../../types'; const Crossbows: Fletchable[] = [ // stocks diff --git a/src/lib/skilling/skills/fletching/fletchables/darts.ts b/src/lib/skilling/skills/fletching/fletchables/darts.ts index b7e984843bb..acdf70e6844 100644 --- a/src/lib/skilling/skills/fletching/fletchables/darts.ts +++ b/src/lib/skilling/skills/fletching/fletchables/darts.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Fletchable } from '../../../types'; +import type { Fletchable } from '../../../types'; const Darts: Fletchable[] = [ { diff --git a/src/lib/skilling/skills/fletching/fletchables/index.ts b/src/lib/skilling/skills/fletching/fletchables/index.ts index b3fda759702..bad94449646 100644 --- a/src/lib/skilling/skills/fletching/fletchables/index.ts +++ b/src/lib/skilling/skills/fletching/fletchables/index.ts @@ -1,4 +1,4 @@ -import { Fletchable } from '../../../types'; +import type { Fletchable } from '../../../types'; import Arrows from './arrows'; import Bolts from './bolts'; import Bows from './bows'; diff --git a/src/lib/skilling/skills/fletching/fletchables/javelins.ts b/src/lib/skilling/skills/fletching/fletchables/javelins.ts index 38b30505566..8fc9448bb17 100644 --- a/src/lib/skilling/skills/fletching/fletchables/javelins.ts +++ b/src/lib/skilling/skills/fletching/fletchables/javelins.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Fletchable } from '../../../types'; +import type { Fletchable } from '../../../types'; const Javelins: Fletchable[] = [ { diff --git a/src/lib/skilling/skills/fletching/fletchables/shafts.ts b/src/lib/skilling/skills/fletching/fletchables/shafts.ts index 2a5aacf7f71..ea1359d7f89 100644 --- a/src/lib/skilling/skills/fletching/fletchables/shafts.ts +++ b/src/lib/skilling/skills/fletching/fletchables/shafts.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Fletchable } from '../../../types'; +import type { Fletchable } from '../../../types'; const Shafts: Fletchable[] = [ { diff --git a/src/lib/skilling/skills/fletching/fletchables/shields.ts b/src/lib/skilling/skills/fletching/fletchables/shields.ts index 451faad272c..91a5903cf86 100644 --- a/src/lib/skilling/skills/fletching/fletchables/shields.ts +++ b/src/lib/skilling/skills/fletching/fletchables/shields.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Fletchable } from '../../../types'; +import type { Fletchable } from '../../../types'; const Shields: Fletchable[] = [ { diff --git a/src/lib/skilling/skills/fletching/fletchables/slayer.ts b/src/lib/skilling/skills/fletching/fletchables/slayer.ts index 6490026b32f..f97f0285ed9 100644 --- a/src/lib/skilling/skills/fletching/fletchables/slayer.ts +++ b/src/lib/skilling/skills/fletching/fletchables/slayer.ts @@ -2,7 +2,7 @@ import { Bank } from 'oldschooljs'; import { SlayerTaskUnlocksEnum } from '../../../../slayer/slayerUnlocks'; import itemID from '../../../../util/itemID'; -import { Fletchable } from '../../../types'; +import type { Fletchable } from '../../../types'; const Slayer: Fletchable[] = [ { diff --git a/src/lib/skilling/skills/fletching/fletchables/tippedBolts.ts b/src/lib/skilling/skills/fletching/fletchables/tippedBolts.ts index 21b26db611f..53441c42fb4 100644 --- a/src/lib/skilling/skills/fletching/fletchables/tippedBolts.ts +++ b/src/lib/skilling/skills/fletching/fletchables/tippedBolts.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Fletchable } from '../../../types'; +import type { Fletchable } from '../../../types'; const TippedBolts: Fletchable[] = [ { diff --git a/src/lib/skilling/skills/fletching/fletchables/tippedDragonBolts.ts b/src/lib/skilling/skills/fletching/fletchables/tippedDragonBolts.ts index 2a39f95991c..44ad4546665 100644 --- a/src/lib/skilling/skills/fletching/fletchables/tippedDragonBolts.ts +++ b/src/lib/skilling/skills/fletching/fletchables/tippedDragonBolts.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Fletchable } from '../../../types'; +import type { Fletchable } from '../../../types'; const TippedDragonBolts: Fletchable[] = [ { diff --git a/src/lib/skilling/skills/fletching/fletchables/tips.ts b/src/lib/skilling/skills/fletching/fletchables/tips.ts index 8742a7a791a..54d991b13e8 100644 --- a/src/lib/skilling/skills/fletching/fletchables/tips.ts +++ b/src/lib/skilling/skills/fletching/fletchables/tips.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import itemID from '../../../../util/itemID'; -import { Fletchable } from '../../../types'; +import type { Fletchable } from '../../../types'; const Tips: Fletchable[] = [ { diff --git a/src/lib/skilling/skills/herblore/mixables/barbMixes.ts b/src/lib/skilling/skills/herblore/mixables/barbMixes.ts index a25777d7c50..6437dd144ed 100644 --- a/src/lib/skilling/skills/herblore/mixables/barbMixes.ts +++ b/src/lib/skilling/skills/herblore/mixables/barbMixes.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import getOSItem from '../../../../util/getOSItem'; -import { Mixable } from '../../../types'; +import type { Mixable } from '../../../types'; export const barbMixes: Mixable[] = [ { diff --git a/src/lib/skilling/skills/herblore/mixables/crush.ts b/src/lib/skilling/skills/herblore/mixables/crush.ts index 8315c880399..48349c88ee1 100644 --- a/src/lib/skilling/skills/herblore/mixables/crush.ts +++ b/src/lib/skilling/skills/herblore/mixables/crush.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import getOSItem from '../../../../util/getOSItem'; -import { Mixable } from '../../../types'; +import type { Mixable } from '../../../types'; const Crush: Mixable[] = [ { diff --git a/src/lib/skilling/skills/herblore/mixables/grimy.ts b/src/lib/skilling/skills/herblore/mixables/grimy.ts index 8c8c6e3775a..5a7c0971bd7 100644 --- a/src/lib/skilling/skills/herblore/mixables/grimy.ts +++ b/src/lib/skilling/skills/herblore/mixables/grimy.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import getOSItem from '../../../../util/getOSItem'; -import { Mixable } from '../../../types'; +import type { Mixable } from '../../../types'; const Grimy: Mixable[] = [ { diff --git a/src/lib/skilling/skills/herblore/mixables/index.ts b/src/lib/skilling/skills/herblore/mixables/index.ts index 6db6b028e27..dead8f1af1d 100644 --- a/src/lib/skilling/skills/herblore/mixables/index.ts +++ b/src/lib/skilling/skills/herblore/mixables/index.ts @@ -1,4 +1,4 @@ -import { Mixable } from '../../../types'; +import type { Mixable } from '../../../types'; import { barbMixes } from './barbMixes'; import Crush from './crush'; import Grimy from './grimy'; diff --git a/src/lib/skilling/skills/herblore/mixables/potions.ts b/src/lib/skilling/skills/herblore/mixables/potions.ts index 9447173b793..cca1dfcba35 100644 --- a/src/lib/skilling/skills/herblore/mixables/potions.ts +++ b/src/lib/skilling/skills/herblore/mixables/potions.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import getOSItem from '../../../../util/getOSItem'; -import { Mixable } from '../../../types'; +import type { Mixable } from '../../../types'; const Potions: Mixable[] = [ { diff --git a/src/lib/skilling/skills/herblore/mixables/tar.ts b/src/lib/skilling/skills/herblore/mixables/tar.ts index 299c43786ad..c662cb92819 100644 --- a/src/lib/skilling/skills/herblore/mixables/tar.ts +++ b/src/lib/skilling/skills/herblore/mixables/tar.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import getOSItem from '../../../../util/getOSItem'; -import { Mixable } from '../../../types'; +import type { Mixable } from '../../../types'; const Tar: Mixable[] = [ { diff --git a/src/lib/skilling/skills/herblore/mixables/unfinishedPotions.ts b/src/lib/skilling/skills/herblore/mixables/unfinishedPotions.ts index 91b448ca057..cd587e02691 100644 --- a/src/lib/skilling/skills/herblore/mixables/unfinishedPotions.ts +++ b/src/lib/skilling/skills/herblore/mixables/unfinishedPotions.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import getOSItem from '../../../../util/getOSItem'; -import { Mixable } from '../../../types'; +import type { Mixable } from '../../../types'; const unfinishedPotions: Mixable[] = [ { diff --git a/src/lib/skilling/skills/hunter/aerialFishing.ts b/src/lib/skilling/skills/hunter/aerialFishing.ts index cbeaae8fedb..702baa49a93 100644 --- a/src/lib/skilling/skills/hunter/aerialFishing.ts +++ b/src/lib/skilling/skills/hunter/aerialFishing.ts @@ -1,6 +1,7 @@ import LootTable from 'oldschooljs/dist/structures/LootTable'; -import { Creature, HunterTechniqueEnum } from '../../types'; +import type { Creature } from '../../types'; +import { HunterTechniqueEnum } from '../../types'; const aerialFishingCreatures: Creature[] = [ { diff --git a/src/lib/skilling/skills/hunter/creatures/birdSnaring.ts b/src/lib/skilling/skills/hunter/creatures/birdSnaring.ts index c2468c41340..5abb41dd8b8 100644 --- a/src/lib/skilling/skills/hunter/creatures/birdSnaring.ts +++ b/src/lib/skilling/skills/hunter/creatures/birdSnaring.ts @@ -1,6 +1,7 @@ import LootTable from 'oldschooljs/dist/structures/LootTable'; -import { Creature, HunterTechniqueEnum } from '../../../types'; +import type { Creature } from '../../../types'; +import { HunterTechniqueEnum } from '../../../types'; const birdSnaringCreatures: Creature[] = [ { diff --git a/src/lib/skilling/skills/hunter/creatures/boxTrapping.ts b/src/lib/skilling/skills/hunter/creatures/boxTrapping.ts index 266575838b4..fb3e9280cca 100644 --- a/src/lib/skilling/skills/hunter/creatures/boxTrapping.ts +++ b/src/lib/skilling/skills/hunter/creatures/boxTrapping.ts @@ -1,6 +1,7 @@ import LootTable from 'oldschooljs/dist/structures/LootTable'; -import { Creature, HunterTechniqueEnum } from '../../../types'; +import type { Creature } from '../../../types'; +import { HunterTechniqueEnum } from '../../../types'; const boxTrappingCreatures: Creature[] = [ { diff --git a/src/lib/skilling/skills/hunter/creatures/butterflyNetting.ts b/src/lib/skilling/skills/hunter/creatures/butterflyNetting.ts index 722e3afde5f..03cf5a8967b 100644 --- a/src/lib/skilling/skills/hunter/creatures/butterflyNetting.ts +++ b/src/lib/skilling/skills/hunter/creatures/butterflyNetting.ts @@ -1,7 +1,8 @@ import { Bank } from 'oldschooljs'; import LootTable from 'oldschooljs/dist/structures/LootTable'; -import { Creature, HunterTechniqueEnum } from '../../../types'; +import type { Creature } from '../../../types'; +import { HunterTechniqueEnum } from '../../../types'; const butterflyNettingCreatures: Creature[] = [ { diff --git a/src/lib/skilling/skills/hunter/creatures/deadfallTrapping.ts b/src/lib/skilling/skills/hunter/creatures/deadfallTrapping.ts index 66540496e8a..afc390ec5a2 100644 --- a/src/lib/skilling/skills/hunter/creatures/deadfallTrapping.ts +++ b/src/lib/skilling/skills/hunter/creatures/deadfallTrapping.ts @@ -1,7 +1,8 @@ import { Bank } from 'oldschooljs'; import LootTable from 'oldschooljs/dist/structures/LootTable'; -import { Creature, HunterTechniqueEnum } from '../../../types'; +import type { Creature } from '../../../types'; +import { HunterTechniqueEnum } from '../../../types'; const deadfallTrappingCreatures: Creature[] = [ { diff --git a/src/lib/skilling/skills/hunter/creatures/falconry.ts b/src/lib/skilling/skills/hunter/creatures/falconry.ts index 911a51ed0c7..b539454b902 100644 --- a/src/lib/skilling/skills/hunter/creatures/falconry.ts +++ b/src/lib/skilling/skills/hunter/creatures/falconry.ts @@ -1,6 +1,7 @@ import LootTable from 'oldschooljs/dist/structures/LootTable'; -import { Creature, HunterTechniqueEnum } from '../../../types'; +import type { Creature } from '../../../types'; +import { HunterTechniqueEnum } from '../../../types'; const falconryCreatures: Creature[] = [ { diff --git a/src/lib/skilling/skills/hunter/creatures/index.ts b/src/lib/skilling/skills/hunter/creatures/index.ts index 1427f9448f6..8454c762b5b 100644 --- a/src/lib/skilling/skills/hunter/creatures/index.ts +++ b/src/lib/skilling/skills/hunter/creatures/index.ts @@ -1,4 +1,4 @@ -import { Creature } from '../../../types'; +import type { Creature } from '../../../types'; import birdSnaringCreatures from './birdSnaring'; import boxTrappingCreatures from './boxTrapping'; import butterflyNettingCreatures from './butterflyNetting'; diff --git a/src/lib/skilling/skills/hunter/creatures/magicBoxTrapping.ts b/src/lib/skilling/skills/hunter/creatures/magicBoxTrapping.ts index fed6d5aaf03..5c6ba991581 100644 --- a/src/lib/skilling/skills/hunter/creatures/magicBoxTrapping.ts +++ b/src/lib/skilling/skills/hunter/creatures/magicBoxTrapping.ts @@ -1,7 +1,8 @@ import { Bank } from 'oldschooljs'; import LootTable from 'oldschooljs/dist/structures/LootTable'; -import { Creature, HunterTechniqueEnum } from '../../../types'; +import type { Creature } from '../../../types'; +import { HunterTechniqueEnum } from '../../../types'; const magicBoxTrappingCreatures: Creature[] = [ { diff --git a/src/lib/skilling/skills/hunter/creatures/netTrapping.ts b/src/lib/skilling/skills/hunter/creatures/netTrapping.ts index d255b77d58d..923a2493517 100644 --- a/src/lib/skilling/skills/hunter/creatures/netTrapping.ts +++ b/src/lib/skilling/skills/hunter/creatures/netTrapping.ts @@ -1,6 +1,7 @@ import LootTable from 'oldschooljs/dist/structures/LootTable'; -import { Creature, HunterTechniqueEnum } from '../../../types'; +import type { Creature } from '../../../types'; +import { HunterTechniqueEnum } from '../../../types'; const netTrappingCreatures: Creature[] = [ { diff --git a/src/lib/skilling/skills/hunter/creatures/pitfallTrapping.ts b/src/lib/skilling/skills/hunter/creatures/pitfallTrapping.ts index dca987aa457..78202bdf99d 100644 --- a/src/lib/skilling/skills/hunter/creatures/pitfallTrapping.ts +++ b/src/lib/skilling/skills/hunter/creatures/pitfallTrapping.ts @@ -1,6 +1,7 @@ import LootTable from 'oldschooljs/dist/structures/LootTable'; -import { Creature, HunterTechniqueEnum } from '../../../types'; +import type { Creature } from '../../../types'; +import { HunterTechniqueEnum } from '../../../types'; const pitfallTrappingCreatures: Creature[] = [ { diff --git a/src/lib/skilling/skills/hunter/creatures/rabbitSnaring.ts b/src/lib/skilling/skills/hunter/creatures/rabbitSnaring.ts index 3500cfdc0be..a1f0840ac5d 100644 --- a/src/lib/skilling/skills/hunter/creatures/rabbitSnaring.ts +++ b/src/lib/skilling/skills/hunter/creatures/rabbitSnaring.ts @@ -1,7 +1,8 @@ import { Bank } from 'oldschooljs'; import LootTable from 'oldschooljs/dist/structures/LootTable'; -import { Creature, HunterTechniqueEnum } from '../../../types'; +import type { Creature } from '../../../types'; +import { HunterTechniqueEnum } from '../../../types'; const rabbitSnaringCreatures: Creature[] = [ { diff --git a/src/lib/skilling/skills/hunter/creatures/tracking.ts b/src/lib/skilling/skills/hunter/creatures/tracking.ts index 8eac1ff2b17..fd1067f2f3c 100644 --- a/src/lib/skilling/skills/hunter/creatures/tracking.ts +++ b/src/lib/skilling/skills/hunter/creatures/tracking.ts @@ -1,6 +1,7 @@ import LootTable from 'oldschooljs/dist/structures/LootTable'; -import { Creature, HunterTechniqueEnum } from '../../../types'; +import type { Creature } from '../../../types'; +import { HunterTechniqueEnum } from '../../../types'; const trackingCreatures: Creature[] = [ { diff --git a/src/lib/skilling/skills/hunter/driftNet.ts b/src/lib/skilling/skills/hunter/driftNet.ts index d6d2cfcc4f3..80afdc867e0 100644 --- a/src/lib/skilling/skills/hunter/driftNet.ts +++ b/src/lib/skilling/skills/hunter/driftNet.ts @@ -1,6 +1,7 @@ import LootTable from 'oldschooljs/dist/structures/LootTable'; -import { Creature, HunterTechniqueEnum } from '../../types'; +import type { Creature } from '../../types'; +import { HunterTechniqueEnum } from '../../types'; const driftNetCreatures: Creature[] = [ { diff --git a/src/lib/skilling/skills/index.ts b/src/lib/skilling/skills/index.ts index d6cbb3bf9ad..2596517723b 100644 --- a/src/lib/skilling/skills/index.ts +++ b/src/lib/skilling/skills/index.ts @@ -1,5 +1,6 @@ import { Emoji } from '../../constants'; -import { Skill, SkillsEnum } from '../types'; +import type { Skill } from '../types'; +import { SkillsEnum } from '../types'; import Agility from './agility'; import Construction from './construction'; import Cooking from './cooking/cooking'; diff --git a/src/lib/skilling/skills/mining.ts b/src/lib/skilling/skills/mining.ts index b7a86ba017c..e08fa1af4a8 100644 --- a/src/lib/skilling/skills/mining.ts +++ b/src/lib/skilling/skills/mining.ts @@ -2,7 +2,8 @@ import LootTable from 'oldschooljs/dist/structures/LootTable'; import { Emoji } from '../../constants'; import itemID from '../../util/itemID'; -import { Ore, SkillsEnum } from '../types'; +import type { Ore } from '../types'; +import { SkillsEnum } from '../types'; const GemRockTable = new LootTable() .add('Uncut opal', 1, 60) diff --git a/src/lib/skilling/skills/prayer.ts b/src/lib/skilling/skills/prayer.ts index 62b4d565a8d..df4340cf9e0 100644 --- a/src/lib/skilling/skills/prayer.ts +++ b/src/lib/skilling/skills/prayer.ts @@ -1,6 +1,7 @@ import { Emoji } from '../../constants'; import itemID from '../../util/itemID'; -import { Ash, Bone, SkillsEnum } from '../types'; +import type { Ash, Bone } from '../types'; +import { SkillsEnum } from '../types'; const bones: Bone[] = [ { diff --git a/src/lib/skilling/skills/smithing/blastables.ts b/src/lib/skilling/skills/smithing/blastables.ts index 6c88afafb97..c7570433d11 100644 --- a/src/lib/skilling/skills/smithing/blastables.ts +++ b/src/lib/skilling/skills/smithing/blastables.ts @@ -2,7 +2,7 @@ import { Time } from 'e'; import { Bank } from 'oldschooljs'; import itemID from '../../../util/itemID'; -import { BlastableBar } from '../../types'; +import type { BlastableBar } from '../../types'; const BlastableBars: BlastableBar[] = [ { diff --git a/src/lib/skilling/skills/smithing/smeltables.ts b/src/lib/skilling/skills/smithing/smeltables.ts index 9c6042a32a8..99cfaa00f6f 100644 --- a/src/lib/skilling/skills/smithing/smeltables.ts +++ b/src/lib/skilling/skills/smithing/smeltables.ts @@ -2,7 +2,7 @@ import { Time } from 'e'; import { Bank } from 'oldschooljs'; import itemID from '../../../util/itemID'; -import { Bar } from '../../types'; +import type { Bar } from '../../types'; const Bars: Bar[] = [ { diff --git a/src/lib/skilling/skills/smithing/smithables/adamant.ts b/src/lib/skilling/skills/smithing/smithables/adamant.ts index 0d33acba648..354b0e1e4fe 100644 --- a/src/lib/skilling/skills/smithing/smithables/adamant.ts +++ b/src/lib/skilling/skills/smithing/smithables/adamant.ts @@ -1,7 +1,7 @@ import { Time } from 'e'; import itemID from '../../../../util/itemID'; -import { SmithedItem } from '../../../types'; +import type { SmithedItem } from '../../../types'; const Adamant: SmithedItem[] = [ { diff --git a/src/lib/skilling/skills/smithing/smithables/bronze.ts b/src/lib/skilling/skills/smithing/smithables/bronze.ts index 214fa97e578..ef24d81b756 100644 --- a/src/lib/skilling/skills/smithing/smithables/bronze.ts +++ b/src/lib/skilling/skills/smithing/smithables/bronze.ts @@ -1,7 +1,7 @@ import { Time } from 'e'; import itemID from '../../../../util/itemID'; -import { SmithedItem } from '../../../types'; +import type { SmithedItem } from '../../../types'; const Bronze: SmithedItem[] = [ { diff --git a/src/lib/skilling/skills/smithing/smithables/gold.ts b/src/lib/skilling/skills/smithing/smithables/gold.ts index 71853cc9071..a4c952fd299 100644 --- a/src/lib/skilling/skills/smithing/smithables/gold.ts +++ b/src/lib/skilling/skills/smithing/smithables/gold.ts @@ -1,7 +1,7 @@ import { Time } from 'e'; import itemID from '../../../../util/itemID'; -import { SmithedItem } from '../../../types'; +import type { SmithedItem } from '../../../types'; const Gold: SmithedItem[] = [ { diff --git a/src/lib/skilling/skills/smithing/smithables/iron.ts b/src/lib/skilling/skills/smithing/smithables/iron.ts index ef01aa7fc42..3813741c0b9 100644 --- a/src/lib/skilling/skills/smithing/smithables/iron.ts +++ b/src/lib/skilling/skills/smithing/smithables/iron.ts @@ -1,7 +1,7 @@ import { Time } from 'e'; import itemID from '../../../../util/itemID'; -import { SmithedItem } from '../../../types'; +import type { SmithedItem } from '../../../types'; const Iron: SmithedItem[] = [ { diff --git a/src/lib/skilling/skills/smithing/smithables/mithril.ts b/src/lib/skilling/skills/smithing/smithables/mithril.ts index 63cde7f64d9..b20cfe69bf2 100644 --- a/src/lib/skilling/skills/smithing/smithables/mithril.ts +++ b/src/lib/skilling/skills/smithing/smithables/mithril.ts @@ -1,7 +1,7 @@ import { Time } from 'e'; import itemID from '../../../../util/itemID'; -import { SmithedItem } from '../../../types'; +import type { SmithedItem } from '../../../types'; const Mithril: SmithedItem[] = [ { diff --git a/src/lib/skilling/skills/smithing/smithables/rune.ts b/src/lib/skilling/skills/smithing/smithables/rune.ts index 695e62c3260..139adbed85b 100644 --- a/src/lib/skilling/skills/smithing/smithables/rune.ts +++ b/src/lib/skilling/skills/smithing/smithables/rune.ts @@ -1,7 +1,7 @@ import { Time } from 'e'; import itemID from '../../../../util/itemID'; -import { SmithedItem } from '../../../types'; +import type { SmithedItem } from '../../../types'; const Rune: SmithedItem[] = [ { diff --git a/src/lib/skilling/skills/smithing/smithables/steel.ts b/src/lib/skilling/skills/smithing/smithables/steel.ts index 64d2e39bbbc..9b1accb4df8 100644 --- a/src/lib/skilling/skills/smithing/smithables/steel.ts +++ b/src/lib/skilling/skills/smithing/smithables/steel.ts @@ -1,7 +1,7 @@ import { Time } from 'e'; import itemID from '../../../../util/itemID'; -import { SmithedItem } from '../../../types'; +import type { SmithedItem } from '../../../types'; const Steel: SmithedItem[] = [ { diff --git a/src/lib/skilling/skills/woodcutting/woodcutting.ts b/src/lib/skilling/skills/woodcutting/woodcutting.ts index 43aee00b7d5..112e38bb483 100644 --- a/src/lib/skilling/skills/woodcutting/woodcutting.ts +++ b/src/lib/skilling/skills/woodcutting/woodcutting.ts @@ -2,7 +2,8 @@ import { LootTable } from 'oldschooljs'; import { Emoji } from '../../../constants'; import itemID from '../../../util/itemID'; -import { Log, SkillsEnum } from '../../types'; +import type { Log } from '../../types'; +import { SkillsEnum } from '../../types'; const sulliuscepTable = new LootTable() .add('Numulite', [4, 8], 34) diff --git a/src/lib/skilling/types.ts b/src/lib/skilling/types.ts index 2060adeeb07..5d08411b7b6 100644 --- a/src/lib/skilling/types.ts +++ b/src/lib/skilling/types.ts @@ -1,11 +1,11 @@ -import { Bank } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; -import LootTable from 'oldschooljs/dist/structures/LootTable'; +import type { Bank } from 'oldschooljs'; +import type { Item } from 'oldschooljs/dist/meta/types'; +import type LootTable from 'oldschooljs/dist/structures/LootTable'; -import { Emoji } from '../constants'; -import { SlayerTaskUnlocksEnum } from '../slayer/slayerUnlocks'; -import { ItemBank } from '../types'; -import { FarmingPatchName } from '../util/farmingHelpers'; +import type { Emoji } from '../constants'; +import type { SlayerTaskUnlocksEnum } from '../slayer/slayerUnlocks'; +import type { ItemBank } from '../types'; +import type { FarmingPatchName } from '../util/farmingHelpers'; export enum SkillsEnum { Agility = 'agility', diff --git a/src/lib/slayer/constants.ts b/src/lib/slayer/constants.ts index c3b327a990f..2f75daae39f 100644 --- a/src/lib/slayer/constants.ts +++ b/src/lib/slayer/constants.ts @@ -1,8 +1,8 @@ export enum AutoslayOptionsEnum { - Default, - HighestUnlocked, - MaxEfficiency, - LowestCombat + Default = 0, + HighestUnlocked = 1, + MaxEfficiency = 2, + LowestCombat = 3 } export const slayerMasterChoices = [ diff --git a/src/lib/slayer/slayerMasters.ts b/src/lib/slayer/slayerMasters.ts index 058196d72ec..3e87831f050 100644 --- a/src/lib/slayer/slayerMasters.ts +++ b/src/lib/slayer/slayerMasters.ts @@ -8,7 +8,7 @@ import { mazchnaTasks } from './tasks/mazchnaTasks'; import { nieveTasks } from './tasks/nieveTasks'; import { turaelTasks } from './tasks/turaelTasks'; import { vannakaTasks } from './tasks/vannakaTasks'; -import { SlayerMaster } from './types'; +import type { SlayerMaster } from './types'; export const slayerMasters: SlayerMaster[] = [ { diff --git a/src/lib/slayer/slayerShop.ts b/src/lib/slayer/slayerShop.ts index 5784e64b565..67ff5d6d4bc 100644 --- a/src/lib/slayer/slayerShop.ts +++ b/src/lib/slayer/slayerShop.ts @@ -1,5 +1,5 @@ import getOSItem from '../util/getOSItem'; -import { SlayerShopItem } from './types'; +import type { SlayerShopItem } from './types'; export const slayerShopBuy: readonly SlayerShopItem[] = [ { diff --git a/src/lib/slayer/slayerUnlocks.ts b/src/lib/slayer/slayerUnlocks.ts index a915056f427..0a02009a5f6 100644 --- a/src/lib/slayer/slayerUnlocks.ts +++ b/src/lib/slayer/slayerUnlocks.ts @@ -18,59 +18,60 @@ export interface SlayerTaskUnlocks { export enum SlayerTaskUnlocksEnum { // Unlockables MalevolentMasquerade = 2, - RingBling, - SeeingRed, - IHopeYouMithMe, - WatchTheBirdie, - HotStuff, - ReptileGotRipped, - LikeABoss, - BiggerAndBadder, - KingBlackBonnet, - KalphiteKhat, - UnholyHelmet, - DarkMantle, - UndeadHead, - UseMoreHead, - TwistedVision, - StopTheWyvern, - Basilocked, - ActualVampyreSlayer, - IWildyMoreSlayer, + RingBling = 3, + SeeingRed = 4, + IHopeYouMithMe = 5, + WatchTheBirdie = 6, + HotStuff = 7, + ReptileGotRipped = 8, + LikeABoss = 9, + BiggerAndBadder = 10, + KingBlackBonnet = 11, + KalphiteKhat = 12, + UnholyHelmet = 13, + DarkMantle = 14, + UndeadHead = 15, + UseMoreHead = 16, + TwistedVision = 17, + StopTheWyvern = 18, + Basilocked = 19, + ActualVampyreSlayer = 20, // Extension Unlocks - NeedMoreDarkness, - AnkouVeryMuch, - SuqANotherOne, - FireAndDarkness, - PedalToTheMetals, - IReallyMithYou, - AdamindSomeMore, - RUUUUUNE, - SpiritualFervour, - BirdsOfAFeather, - GreaterChallenge, - ItsDarkInHere, - BleedMeDry, - SmellYaLater, - Horrorific, - ToDustYouShallReturn, - WyverNotherOne, - GetSmashed, - NechsPlease, - AugmentMyAbbies, - KrackOn, - GetScabarightOnIt, - WyverNotherTwo, - Basilonger, - MoreAtStake, - Revenenenenenants, + NeedMoreDarkness = 21, + AnkouVeryMuch = 22, + SuqANotherOne = 23, + FireAndDarkness = 24, + PedalToTheMetals = 25, + IReallyMithYou = 26, + AdamindSomeMore = 27, + RUUUUUNE = 28, + SpiritualFervour = 29, + BirdsOfAFeather = 30, + GreaterChallenge = 31, + ItsDarkInHere = 32, + BleedMeDry = 33, + SmellYaLater = 34, + Horrorific = 35, + ToDustYouShallReturn = 36, + WyverNotherOne = 37, + GetSmashed = 38, + NechsPlease = 39, + AugmentMyAbbies = 40, + KrackOn = 41, + GetScabarightOnIt = 42, + WyverNotherTwo = 43, + Basilonger = 44, + MoreAtStake = 45, // Item Purchases: - SlayerRing, - HerbSack, - RunePouch, - DoubleTrouble, - BroaderFletching + SlayerRing = 46, + HerbSack = 47, + RunePouch = 48, + DoubleTrouble = 49, + BroaderFletching = 50, + IWildyMoreSlayer = 200, + Revenenenenenants = 201 } + export const SlayerRewardsShop: SlayerTaskUnlocks[] = [ { id: SlayerTaskUnlocksEnum.MalevolentMasquerade, diff --git a/src/lib/slayer/slayerUtil.ts b/src/lib/slayer/slayerUtil.ts index fb24c0c3f5a..0117d7bcaa9 100644 --- a/src/lib/slayer/slayerUtil.ts +++ b/src/lib/slayer/slayerUtil.ts @@ -1,12 +1,12 @@ import { notEmpty, objectKeys, randFloat, randInt } from 'e'; -import { Bank, Monsters, MonsterSlayerMaster } from 'oldschooljs'; -import Monster from 'oldschooljs/dist/structures/Monster'; +import { Bank, MonsterSlayerMaster, Monsters } from 'oldschooljs'; +import type Monster from 'oldschooljs/dist/structures/Monster'; import { KourendKebosDiary, LumbridgeDraynorDiary, userhasDiaryTier } from '../../lib/diaries'; import { CombatAchievements } from '../combat_achievements/combatAchievements'; -import { PvMMethod } from '../constants'; +import type { PvMMethod } from '../constants'; import { CombatOptionsEnum } from '../minions/data/combatConstants'; -import { KillableMonster } from '../minions/types'; +import type { KillableMonster } from '../minions/types'; import { prisma } from '../settings/prisma'; import { getNewUser } from '../settings/settings'; import { SkillsEnum } from '../skilling/types'; @@ -18,17 +18,17 @@ import { autoslayModes } from './constants'; import { slayerMasters } from './slayerMasters'; import { SlayerRewardsShop, SlayerTaskUnlocksEnum } from './slayerUnlocks'; import { bossTasks, wildernessBossTasks } from './tasks/bossTasks'; -import { AssignableSlayerTask, SlayerMaster } from './types'; +import type { AssignableSlayerTask, SlayerMaster } from './types'; export enum SlayerMasterEnum { - Reserved, - Turael, - Mazchna, - Vannaka, - Chaeldar, - Konar, - Nieve, - Duradel + Reserved = 0, + Turael = 1, + Mazchna = 2, + Vannaka = 3, + Chaeldar = 4, + Konar = 5, + Nieve = 6, + Duradel = 7 } export interface DetermineBoostParams { @@ -55,12 +55,12 @@ export function determineBoostChoice(params: DetermineBoostParams) { boostChoice = 'cannon'; } else if ( params.cbOpts.includes(CombatOptionsEnum.AlwaysIceBarrage) && - (params.monster!.canBarrage || params.wildyJelly) + (params.monster?.canBarrage || params.wildyJelly) ) { boostChoice = 'barrage'; } else if ( params.cbOpts.includes(CombatOptionsEnum.AlwaysIceBurst) && - (params.monster!.canBarrage || params.wildyJelly) + (params.monster?.canBarrage || params.wildyJelly) ) { boostChoice = 'burst'; } else if (params.cbOpts.includes(CombatOptionsEnum.AlwaysCannon)) { @@ -116,7 +116,7 @@ export function weightedPick(filteredTasks: AssignableSlayerTask[]) { } } - let task = filteredTasks[result]; + const task = filteredTasks[result]; return task; } @@ -129,12 +129,7 @@ export function userCanUseMaster(user: MUser, master: SlayerMaster) { ); } -export function userCanUseTask( - user: MUser, - task: AssignableSlayerTask, - master: SlayerMaster, - allowBossTasks: boolean = false -) { +export function userCanUseTask(user: MUser, task: AssignableSlayerTask, master: SlayerMaster, allowBossTasks = false) { if (task.isBoss && !allowBossTasks) return false; if (task.dontAssign) return false; const myLastTask = user.user.slayer_last_task; @@ -219,7 +214,7 @@ export async function assignNewSlayerTask(_user: MUser, master: SlayerMaster) { const newUser = await getNewUser(_user.id); - let maxQuantity = assignedTask!.amount[1]; + let maxQuantity = assignedTask?.amount[1]; if (bossTask && _user.user.slayer_unlocks.includes(SlayerTaskUnlocksEnum.LikeABoss)) { for (const tier of objectKeys(CombatAchievements)) { if (_user.hasCompletedCATier(tier)) { @@ -228,19 +223,19 @@ export async function assignNewSlayerTask(_user: MUser, master: SlayerMaster) { } } - const quantity = randInt(assignedTask!.amount[0], maxQuantity); + const quantity = randInt(assignedTask?.amount[0], maxQuantity); const currentTask = await prisma.slayerTask.create({ data: { user_id: newUser.id, quantity, quantity_remaining: quantity, slayer_master_id: master.id, - monster_id: assignedTask!.monster.id, + monster_id: assignedTask?.monster.id, skipped: false } }); await _user.update({ - slayer_last_task: assignedTask!.monster.id + slayer_last_task: assignedTask?.monster.id }); return { currentTask, assignedTask }; @@ -383,7 +378,7 @@ export function getSlayerMasterOSJSbyID(slayerMasterID: number) { export function getSlayerReward(id: SlayerTaskUnlocksEnum): string { const { name } = SlayerRewardsShop.find(srs => { - return srs!.id === id; + return srs?.id === id; })!; return name; } @@ -495,8 +490,7 @@ export function filterLootReplace(myBank: Bank, myLoot: Bank) { } export async function getSlayerTaskStats(userID: string) { - const result: { monster_id: number; total_quantity: number; qty: number }[] = - await prisma.$queryRaw`SELECT monster_id, SUM(quantity)::int AS total_quantity, COUNT(monster_id)::int AS qty + const result: { monster_id: number; total_quantity: number; qty: number }[] = await prisma.$queryRaw`SELECT monster_id, SUM(quantity)::int AS total_quantity, COUNT(monster_id)::int AS qty FROM slayer_tasks WHERE user_id = ${userID} AND quantity_remaining = 0 diff --git a/src/lib/slayer/tasks/bossTasks.ts b/src/lib/slayer/tasks/bossTasks.ts index bfaab90bdd8..cbab3641485 100644 --- a/src/lib/slayer/tasks/bossTasks.ts +++ b/src/lib/slayer/tasks/bossTasks.ts @@ -1,6 +1,6 @@ import { Monsters } from 'oldschooljs'; -import { AssignableSlayerTask } from '../types'; +import type { AssignableSlayerTask } from '../types'; export const bossTasks: AssignableSlayerTask[] = [ { diff --git a/src/lib/slayer/tasks/chaeldarTasks.ts b/src/lib/slayer/tasks/chaeldarTasks.ts index 20d6902053e..bb7b1ca9dce 100644 --- a/src/lib/slayer/tasks/chaeldarTasks.ts +++ b/src/lib/slayer/tasks/chaeldarTasks.ts @@ -1,6 +1,6 @@ import { Monsters } from 'oldschooljs'; -import { AssignableSlayerTask } from '../types'; +import type { AssignableSlayerTask } from '../types'; import { bossTasks } from './bossTasks'; export const chaeldarTasks: AssignableSlayerTask[] = [ diff --git a/src/lib/slayer/tasks/duradelTasks.ts b/src/lib/slayer/tasks/duradelTasks.ts index 14ee238c98a..8b1ae3a0abe 100644 --- a/src/lib/slayer/tasks/duradelTasks.ts +++ b/src/lib/slayer/tasks/duradelTasks.ts @@ -2,7 +2,7 @@ import { Monsters } from 'oldschooljs'; import killableMonsters from '../../minions/data/killableMonsters'; import { SlayerTaskUnlocksEnum } from '../slayerUnlocks'; -import { AssignableSlayerTask } from '../types'; +import type { AssignableSlayerTask } from '../types'; import { bossTasks } from './bossTasks'; export const duradelTasks: AssignableSlayerTask[] = [ @@ -396,7 +396,7 @@ export const duradelTasks: AssignableSlayerTask[] = [ amount: [10, 20], weight: 7, monsters: [Monsters.SteelDragon.id], - levelRequirements: killableMonsters.find(k => k.id === Monsters.SteelDragon.id)!.levelRequirements, + levelRequirements: killableMonsters.find(k => k.id === Monsters.SteelDragon.id)?.levelRequirements, extendedAmount: [40, 60], extendedUnlockId: SlayerTaskUnlocksEnum.PedalToTheMetals, combatLevel: 85, diff --git a/src/lib/slayer/tasks/index.ts b/src/lib/slayer/tasks/index.ts index b33c3dccc07..2ebfd8862ea 100644 --- a/src/lib/slayer/tasks/index.ts +++ b/src/lib/slayer/tasks/index.ts @@ -1,4 +1,4 @@ -import { AssignableSlayerTask } from '../types'; +import type { AssignableSlayerTask } from '../types'; import { bossTasks } from './bossTasks'; import { chaeldarTasks } from './chaeldarTasks'; import { duradelTasks } from './duradelTasks'; diff --git a/src/lib/slayer/tasks/konarTasks.ts b/src/lib/slayer/tasks/konarTasks.ts index da41593d7b1..aa00ae46a8d 100644 --- a/src/lib/slayer/tasks/konarTasks.ts +++ b/src/lib/slayer/tasks/konarTasks.ts @@ -2,7 +2,7 @@ import { Monsters } from 'oldschooljs'; import killableMonsters from '../../minions/data/killableMonsters'; import { SlayerTaskUnlocksEnum } from '../slayerUnlocks'; -import { AssignableSlayerTask } from '../types'; +import type { AssignableSlayerTask } from '../types'; import { bossTasks } from './bossTasks'; export const konarTasks: AssignableSlayerTask[] = [ @@ -383,7 +383,7 @@ export const konarTasks: AssignableSlayerTask[] = [ amount: [30, 50], weight: 5, monsters: [Monsters.SteelDragon.id], - levelRequirements: killableMonsters.find(k => k.id === Monsters.SteelDragon.id)!.levelRequirements, + levelRequirements: killableMonsters.find(k => k.id === Monsters.SteelDragon.id)?.levelRequirements, extendedAmount: [40, 60], extendedUnlockId: SlayerTaskUnlocksEnum.PedalToTheMetals, combatLevel: 85, diff --git a/src/lib/slayer/tasks/krystiliaTasks.ts b/src/lib/slayer/tasks/krystiliaTasks.ts index ffc05fecaa9..40b5d560718 100644 --- a/src/lib/slayer/tasks/krystiliaTasks.ts +++ b/src/lib/slayer/tasks/krystiliaTasks.ts @@ -1,7 +1,7 @@ import { Monsters } from 'oldschooljs'; import { SlayerTaskUnlocksEnum } from '../slayerUnlocks'; -import { AssignableSlayerTask } from '../types'; +import type { AssignableSlayerTask } from '../types'; import { wildernessBossTasks } from './bossTasks'; export const krystiliaTasks: AssignableSlayerTask[] = [ diff --git a/src/lib/slayer/tasks/mazchnaTasks.ts b/src/lib/slayer/tasks/mazchnaTasks.ts index 2eb078c7c3a..1aa031a2ce9 100644 --- a/src/lib/slayer/tasks/mazchnaTasks.ts +++ b/src/lib/slayer/tasks/mazchnaTasks.ts @@ -1,7 +1,7 @@ import { Monsters } from 'oldschooljs'; import killableMonsters from '../../minions/data/killableMonsters'; -import { AssignableSlayerTask } from '../types'; +import type { AssignableSlayerTask } from '../types'; export const mazchnaTasks: AssignableSlayerTask[] = [ { @@ -101,7 +101,7 @@ export const mazchnaTasks: AssignableSlayerTask[] = [ amount: [40, 70], weight: 6, monsters: [Monsters.EarthWarrior.id], - levelRequirements: killableMonsters.find(k => k.id === Monsters.EarthWarrior.id)!.levelRequirements, + levelRequirements: killableMonsters.find(k => k.id === Monsters.EarthWarrior.id)?.levelRequirements, combatLevel: 35, unlocked: true }, diff --git a/src/lib/slayer/tasks/nieveTasks.ts b/src/lib/slayer/tasks/nieveTasks.ts index 2e59ea66154..13c8cbd3767 100644 --- a/src/lib/slayer/tasks/nieveTasks.ts +++ b/src/lib/slayer/tasks/nieveTasks.ts @@ -2,7 +2,7 @@ import { Monsters } from 'oldschooljs'; import killableMonsters from '../../minions/data/killableMonsters'; import { SlayerTaskUnlocksEnum } from '../slayerUnlocks'; -import { AssignableSlayerTask } from '../types'; +import type { AssignableSlayerTask } from '../types'; import { bossTasks } from './bossTasks'; export const nieveTasks: AssignableSlayerTask[] = [ @@ -372,7 +372,7 @@ export const nieveTasks: AssignableSlayerTask[] = [ amount: [30, 60], weight: 5, monsters: [Monsters.SteelDragon.id], - levelRequirements: killableMonsters.find(k => k.id === Monsters.SteelDragon.id)!.levelRequirements, + levelRequirements: killableMonsters.find(k => k.id === Monsters.SteelDragon.id)?.levelRequirements, combatLevel: 85, questPoints: 34, unlocked: true diff --git a/src/lib/slayer/tasks/turaelTasks.ts b/src/lib/slayer/tasks/turaelTasks.ts index 06d5fc171d5..0763e64044c 100644 --- a/src/lib/slayer/tasks/turaelTasks.ts +++ b/src/lib/slayer/tasks/turaelTasks.ts @@ -1,6 +1,6 @@ import { Monsters } from 'oldschooljs'; -import { AssignableSlayerTask } from '../types'; +import type { AssignableSlayerTask } from '../types'; export const turaelTasks: AssignableSlayerTask[] = [ { diff --git a/src/lib/slayer/tasks/vannakaTasks.ts b/src/lib/slayer/tasks/vannakaTasks.ts index 8680fadf01e..cdd3d846b49 100644 --- a/src/lib/slayer/tasks/vannakaTasks.ts +++ b/src/lib/slayer/tasks/vannakaTasks.ts @@ -1,7 +1,7 @@ import { Monsters } from 'oldschooljs'; import killableMonsters from '../../minions/data/killableMonsters'; -import { AssignableSlayerTask } from '../types'; +import type { AssignableSlayerTask } from '../types'; export const vannakaTasks: AssignableSlayerTask[] = [ { @@ -176,7 +176,7 @@ export const vannakaTasks: AssignableSlayerTask[] = [ amount: [40, 80], weight: 6, monsters: [Monsters.EarthWarrior.id], - levelRequirements: killableMonsters.find(k => k.id === Monsters.EarthWarrior.id)!.levelRequirements, + levelRequirements: killableMonsters.find(k => k.id === Monsters.EarthWarrior.id)?.levelRequirements, combatLevel: 35, unlocked: true }, diff --git a/src/lib/slayer/types.ts b/src/lib/slayer/types.ts index 423d4f75e35..84d7817d05a 100644 --- a/src/lib/slayer/types.ts +++ b/src/lib/slayer/types.ts @@ -1,8 +1,8 @@ -import { MonsterSlayerMaster } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; -import Monster from 'oldschooljs/dist/structures/Monster'; +import type { MonsterSlayerMaster } from 'oldschooljs'; +import type { Item } from 'oldschooljs/dist/meta/types'; +import type Monster from 'oldschooljs/dist/structures/Monster'; -import { LevelRequirements } from '../skilling/types'; +import type { LevelRequirements } from '../skilling/types'; export interface AssignableSlayerTask { monster: Monster; diff --git a/src/lib/sorts.ts b/src/lib/sorts.ts index 8f7d7676dda..a214ce510be 100644 --- a/src/lib/sorts.ts +++ b/src/lib/sorts.ts @@ -1,4 +1,4 @@ -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; import { marketPriceOrBotPrice } from './marketPrices'; diff --git a/src/lib/structures/Bank.ts b/src/lib/structures/Bank.ts index 501ae9cc36f..639106a5164 100644 --- a/src/lib/structures/Bank.ts +++ b/src/lib/structures/Bank.ts @@ -1,6 +1,9 @@ -import { DegradeableItem, degradeableItems } from '../degradeableItems'; -import { SkillNameType, SkillsArray } from '../skilling/types'; -import { GeneralBank, GeneralBankType } from './GeneralBank'; +import type { DegradeableItem } from '../degradeableItems'; +import { degradeableItems } from '../degradeableItems'; +import type { SkillNameType } from '../skilling/types'; +import { SkillsArray } from '../skilling/types'; +import type { GeneralBankType } from './GeneralBank'; +import { GeneralBank } from './GeneralBank'; export class ChargeBank extends GeneralBank { constructor(initialBank?: GeneralBankType) { diff --git a/src/lib/structures/Gear.ts b/src/lib/structures/Gear.ts index ef99260ddd3..44094bf5754 100644 --- a/src/lib/structures/Gear.ts +++ b/src/lib/structures/Gear.ts @@ -1,20 +1,21 @@ -import { GearPreset } from '@prisma/client'; +import type { GearPreset } from '@prisma/client'; import { notEmpty, objectKeys, uniqueArr } from 'e'; import { Bank } from 'oldschooljs'; -import { EquipmentSlot, Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; +import { EquipmentSlot } from 'oldschooljs/dist/meta/types'; import { getSimilarItems, inverseSimilarItems } from '../data/similarItems'; -import { +import type { DefenceGearStat, GearSetup, GearSetupType, GearSlotItem, - GearStat, GearStats, OffenceGearStat, OtherGearStat } from '../gear/types'; -import { GearRequirement } from '../minions/types'; +import { GearStat } from '../gear/types'; +import type { GearRequirement } from '../minions/types'; import { assert } from '../util'; import getOSItem from '../util/getOSItem'; import itemID from '../util/itemID'; @@ -74,8 +75,8 @@ export function filterGearSetup(gear: undefined | null | GearSetup | PartialGear const filteredGear = !gear ? undefined : typeof gear.ammo === 'undefined' || typeof gear.ammo === 'string' - ? constructGearSetup(gear as PartialGearSetup) - : (gear as GearSetup); + ? constructGearSetup(gear as PartialGearSetup) + : (gear as GearSetup); return filteredGear; } export const globalPresets: (GearPreset & { defaultSetup: GearSetupType })[] = [ @@ -519,7 +520,7 @@ export class Gear { if (allItems.length === 0) { return 'No items'; } - let items = []; + const items = []; for (const item of allItems) { items.push(getOSItem(item).name); } diff --git a/src/lib/structures/GeneralBank.ts b/src/lib/structures/GeneralBank.ts index a45b12add61..2b4f551931d 100644 --- a/src/lib/structures/GeneralBank.ts +++ b/src/lib/structures/GeneralBank.ts @@ -1,9 +1,7 @@ -import { all, create } from 'mathjs'; +import Decimal from 'decimal.js'; import { assert } from '../util'; -const mathjs = create(all); - export type GeneralBankType = Record; interface GeneralBankValueSchema { @@ -12,9 +10,7 @@ interface GeneralBankValueSchema { floats: boolean; } -interface BankValidator { - (key: T, value: number, bank: GeneralBankType): void; -} +type BankValidator = (key: T, value: number, bank: GeneralBankType) => void; export class GeneralBank { private bank: GeneralBankType; @@ -62,7 +58,7 @@ export class GeneralBank { const value = this.bank[key]; if (this.allowedKeys) { if (typeof Array.from(this.allowedKeys.values())[0] === 'number') { - key = parseInt(key as string) as T; + key = Number.parseInt(key as string) as T; } if (!this.allowedKeys.has(key)) { throw new Error( @@ -107,7 +103,7 @@ export class GeneralBank { private addItem(key: T, quantity: number): this { assert(quantity >= 0, 'Quantity must be non-negative.'); - const newValue = mathjs.add(this.amount(key), quantity); + const newValue = Decimal.add(this.amount(key), quantity).toNumber(); if (newValue > this.valueSchema.max) { throw new Error(`Value for ${key} exceeds the maximum of ${this.valueSchema.max}.`); } @@ -122,7 +118,7 @@ export class GeneralBank { if (currentAmount < quantity) { throw new Error(`Not enough ${key} to remove.`); } - const newValue = mathjs.subtract(currentAmount, quantity); + const newValue = Decimal.sub(currentAmount, quantity).toNumber(); this.bank[key] = newValue; if (newValue === 0) { delete this.bank[key]; @@ -131,7 +127,7 @@ export class GeneralBank { return this; } - add(keyOrBank: T | GeneralBank, quantity: number = 1): this { + add(keyOrBank: T | GeneralBank, quantity = 1): this { if (keyOrBank instanceof GeneralBank) { for (const [key, qty] of keyOrBank.entries()) { this.addItem(key, qty); @@ -142,7 +138,7 @@ export class GeneralBank { return this; } - remove(keyOrBank: T | GeneralBank, quantity: number = 1): this { + remove(keyOrBank: T | GeneralBank, quantity = 1): this { if (keyOrBank instanceof GeneralBank) { for (const [key, qty] of Object.entries(keyOrBank.bank) as [T, number][]) { this.removeItem(key as T, qty); diff --git a/src/lib/structures/LastManStandingUsage.ts b/src/lib/structures/LastManStandingUsage.ts index 134da10b55a..41330ba4f7f 100644 --- a/src/lib/structures/LastManStandingUsage.ts +++ b/src/lib/structures/LastManStandingUsage.ts @@ -14,7 +14,8 @@ export default class LastManStandingUsage { public parse(usage: string): void { let current = ''; - for (let i = 0, char; i < usage.length; i++) { + let char: string | undefined = undefined; + for (let i = 0; i < usage.length; i++) { char = usage.charAt(i); if (char === '{') { // If there was text, push buffer diff --git a/src/lib/structures/MUserStats.ts b/src/lib/structures/MUserStats.ts index c0dfcc6e3e1..fd1b96d6b98 100644 --- a/src/lib/structures/MUserStats.ts +++ b/src/lib/structures/MUserStats.ts @@ -1,4 +1,4 @@ -import { UserStats } from '@prisma/client'; +import type { UserStats } from '@prisma/client'; import { Bank } from 'oldschooljs'; import { ClueTiers } from '../clues/clueTiers'; @@ -42,7 +42,7 @@ export class MUserStats { for (const tier of ClueTiers) clueCounts[tier.name] = 0; for (const [key, val] of Object.entries(this.userStats.openable_scores as ItemBank)) { - const clueTier = ClueTiers.find(i => i.id === parseInt(key)); + const clueTier = ClueTiers.find(i => i.id === Number.parseInt(key)); if (!clueTier) continue; clueCounts[clueTier.name] += val; } diff --git a/src/lib/structures/OldSchoolBotClient.ts b/src/lib/structures/OldSchoolBotClient.ts index 9fc3db3a39b..ab7522e80ae 100644 --- a/src/lib/structures/OldSchoolBotClient.ts +++ b/src/lib/structures/OldSchoolBotClient.ts @@ -1,9 +1,10 @@ -import { Client, ClientOptions, User } from 'discord.js'; -import { FastifyInstance } from 'fastify'; -import { MahojiClient } from 'mahoji'; +import type { User } from 'discord.js'; +import { Client } from 'discord.js'; +import type { FastifyInstance } from 'fastify'; +import type { MahojiClient } from 'mahoji'; import { production } from '../../config'; -import { Peak } from '../tickers'; +import type { Peak } from '../tickers'; if (typeof production !== 'boolean') { throw new Error('Must provide production boolean.'); @@ -19,10 +20,6 @@ export class OldSchoolBotClient extends Client { _peakIntervalCache!: Peak[]; fastifyServer!: FastifyInstance; - public constructor(clientOptions: ClientOptions) { - super(clientOptions); - } - async fetchUser(id: string | bigint): Promise { const user = await this.users.fetch(typeof id === 'string' ? id : id.toString()); return user; diff --git a/src/lib/structures/PercentCounter.ts b/src/lib/structures/PercentCounter.ts index 220ea69208c..82e0b6b4942 100644 --- a/src/lib/structures/PercentCounter.ts +++ b/src/lib/structures/PercentCounter.ts @@ -12,7 +12,7 @@ export class PercentCounter { } add(isApplying: boolean, percent: number, message: string) { - let change = this.value * (percent / 100); + const change = this.value * (percent / 100); const formattedChange = this.type === 'time' ? formatDuration(change, true) : `${change.toFixed(2)}%`; this[isApplying ? 'messages' : 'missed'].push( diff --git a/src/lib/structures/Requirements.ts b/src/lib/structures/Requirements.ts index 74321e2f9d2..180cb622411 100644 --- a/src/lib/structures/Requirements.ts +++ b/src/lib/structures/Requirements.ts @@ -1,17 +1,19 @@ -import { Minigame } from '@prisma/client'; +import type { Minigame } from '@prisma/client'; import { calcWhatPercent, objectEntries } from 'e'; -import { Bank } from 'oldschooljs'; +import type { Bank } from 'oldschooljs'; -import { getParsedStashUnits, ParsedUnit } from '../../mahoji/lib/abstracted_commands/stashUnitsCommand'; -import { ClueTier } from '../clues/clueTiers'; -import { BitField, BitFieldData, BOT_TYPE } from '../constants'; +import type { ParsedUnit } from '../../mahoji/lib/abstracted_commands/stashUnitsCommand'; +import { getParsedStashUnits } from '../../mahoji/lib/abstracted_commands/stashUnitsCommand'; +import type { ClueTier } from '../clues/clueTiers'; +import type { BitField } from '../constants'; +import { BOT_TYPE, BitFieldData } from '../constants'; import { diaries, userhasDiaryIDTier } from '../diaries'; import { effectiveMonsters } from '../minions/data/killableMonsters'; -import { ClueBank, DiaryID, DiaryTierName } from '../minions/types'; +import type { ClueBank, DiaryID, DiaryTierName } from '../minions/types'; import type { RobochimpUser } from '../roboChimp'; -import { MinigameName } from '../settings/minigames'; +import type { MinigameName } from '../settings/minigames'; import Agility from '../skilling/skills/agility'; -import { Skills } from '../types'; +import type { Skills } from '../types'; import { itemNameFromID } from '../util'; import { MUserStats } from './MUserStats'; @@ -87,7 +89,7 @@ export class Requirements { if ('kcRequirement' in req) { requirementParts.push( `Kill Count Requirement: ${Object.entries(req.kcRequirement) - .map(([k, v]) => `${v}x ${effectiveMonsters.find(i => i.id === Number(k))!.name}`) + .map(([k, v]) => `${v}x ${effectiveMonsters.find(i => i.id === Number(k))?.name}`) .join(', ')}.` ); } @@ -99,7 +101,7 @@ export class Requirements { if ('lapsRequirement' in req) { requirementParts.push( `Agility Course Laps Requirements: ${Object.entries(req.lapsRequirement) - .map(([k, v]) => `${v}x laps of ${Agility.Courses.find(i => i.id === Number(k))!.name}`) + .map(([k, v]) => `${v}x laps of ${Agility.Courses.find(i => i.id === Number(k))?.name}`) .join(', ')}.` ); } @@ -123,7 +125,7 @@ export class Requirements { if ('diaryRequirement' in req) { requirementParts.push( `Achievement Diary Requirement: ${req.diaryRequirement - .map(i => `${i[1]} ${diaries.find(d => d.id === i[0])!.name}`) + .map(i => `${i[1]} ${diaries.find(d => d.id === i[0])?.name}`) .join(', ')}` ); } @@ -211,7 +213,7 @@ export class Requirements { for (const [id, amount] of Object.entries(requirement.kcRequirement)) { if (!kcs[id] || kcs[id] < amount) { missingMonsterNames.push( - `${amount}x ${effectiveMonsters.find(m => m.id === parseInt(id))?.name ?? id}` + `${amount}x ${effectiveMonsters.find(m => m.id === Number.parseInt(id))?.name ?? id}` ); } } @@ -236,7 +238,7 @@ export class Requirements { if (!laps[id] || laps[id] < amount) { results.push({ reason: `You need ${amount}x laps in the ${ - Agility.Courses.find(i => i.id.toString() === id)!.name + Agility.Courses.find(i => i.id.toString() === id)?.name } agility course.` }); } @@ -341,7 +343,7 @@ export class Requirements { })); const results = await Promise.all(requirementResults); - const flatReasons = results.map(r => r.result).flat(); + const flatReasons = results.flatMap(r => r.result); const totalRequirements = this.requirements.length; const metRequirements = results.filter(i => i.result.length === 0).length; diff --git a/src/lib/tableBank.ts b/src/lib/tableBank.ts index c4c345251a3..db7def8af9b 100644 --- a/src/lib/tableBank.ts +++ b/src/lib/tableBank.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import { prisma } from './settings/prisma'; -import { ItemBank } from './types'; +import type { ItemBank } from './types'; import { validateBankAndThrow } from './util'; export function makeTransactFromTableBankQueries({ diff --git a/src/lib/tickers.ts b/src/lib/tickers.ts index 139764421b3..ab3f3d02c28 100644 --- a/src/lib/tickers.ts +++ b/src/lib/tickers.ts @@ -1,18 +1,19 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, TextChannel } from 'discord.js'; -import { noOp, randInt, removeFromArr, shuffleArr, Time } from 'e'; +import type { TextChannel } from 'discord.js'; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder } from 'discord.js'; +import { Time, noOp, randInt, removeFromArr, shuffleArr } from 'e'; import { production } from '../config'; import { userStatsUpdate } from '../mahoji/mahojiSettings'; -import { BitField, Channel, informationalButtons, PeakTier } from './constants'; +import { mahojiUserSettingsUpdate } from './MUser'; +import { processPendingActivities } from './Task'; +import { BitField, Channel, PeakTier, informationalButtons } from './constants'; import { GrandExchange } from './grandExchange'; import { cacheGEPrices } from './marketPrices'; import { collectMetrics } from './metrics'; -import { mahojiUserSettingsUpdate } from './MUser'; import { prisma, queryCountStore } from './settings/prisma'; import { runCommand } from './settings/settings'; import { getFarmingInfo } from './skilling/functions/getFarmingInfo'; import Farming from './skilling/skills/farming'; -import { processPendingActivities } from './Task'; import { awaitMessageComponentInteraction, getSupportGuild, makeComponents, stringMatches } from './util'; import { farmingPatchNames, getFarmingKeyFromName } from './util/farmingHelpers'; import { handleGiveawayCompletion } from './util/giveaway'; @@ -91,14 +92,14 @@ export const tickers: { name: string; interval: number; timer: NodeJS.Timeout | timer: null, interval: Time.Minute, cb: async () => { - let storedCount = queryCountStore.value; + const storedCount = queryCountStore.value; queryCountStore.value = 0; const data = { timestamp: Math.floor(Date.now() / 1000), ...(await collectMetrics()), qps: storedCount / 60 }; - if (isNaN(data.eventLoopDelayMean)) { + if (Number.isNaN(data.eventLoopDelayMean)) { data.eventLoopDelayMean = 0; } await prisma.metric.create({ @@ -133,7 +134,7 @@ WHERE bitfield && '{2,3,4,5,6,7,8,12,21,24}'::int[] AND user_stats."last_daily_t .setEmoji('493286312854683654') .setStyle(ButtonStyle.Secondary); const components = [dailyDMButton]; - let str = 'Your daily is ready!'; + const str = 'Your daily is ready!'; for (const row of result.values()) { if (!production) continue; @@ -162,7 +163,7 @@ WHERE bitfield && '{2,3,4,5,6,7,8,12,21,24}'::int[] AND user_stats."last_daily_t // Divide the current day into interverals for (let i = 0; i <= 10; i++) { - let randomedTime = randInt(1, 2); + const randomedTime = randInt(1, 2); const [peakTier] = shuffleArr(peakTiers); const peak: Peak = { startTime: randomedTime, @@ -185,7 +186,7 @@ WHERE bitfield && '{2,3,4,5,6,7,8,12,21,24}'::int[] AND user_stats."last_daily_t let currentTime = new Date().getTime(); - for (let peak of peakInterval) { + for (const peak of peakInterval) { peak.startTime = currentTime; currentTime += peak.finishTime * Time.Hour; peak.finishTime = currentTime; @@ -200,7 +201,7 @@ WHERE bitfield && '{2,3,4,5,6,7,8,12,21,24}'::int[] AND user_stats."last_daily_t timer: null, cb: async () => { if (!production) return; - let basePlantTime = 1_626_556_507_451; + const basePlantTime = 1_626_556_507_451; const now = Date.now(); const users = await prisma.user.findMany({ where: { @@ -231,11 +232,11 @@ WHERE bitfield && '{2,3,4,5,6,7,8,12,21,24}'::int[] AND user_stats."last_daily_t const storeHarvestablePlant = patch.lastPlanted; const planted = storeHarvestablePlant ? Farming.Plants.find(plants => stringMatches(plants.name, storeHarvestablePlant)) ?? - Farming.Plants.find( + Farming.Plants.find( plants => stringMatches(plants.name, storeHarvestablePlant) || stringMatches(plants.name.split(' ')[0], storeHarvestablePlant) - ) + ) : null; const difference = now - patch.plantTime; if (!planted) continue; @@ -323,7 +324,7 @@ WHERE bitfield && '{2,3,4,5,6,7,8,12,21,24}'::int[] AND user_stats."last_daily_t const channel = guild?.channels.cache.get(Channel.HelpAndSupport) as TextChannel | undefined; if (!channel) return; const messages = await channel.messages.fetch({ limit: 5 }); - if (messages.some(m => m.author.id === globalClient.user!.id)) return; + if (messages.some(m => m.author.id === globalClient.user?.id)) return; if (lastMessageID) { const message = await channel.messages.fetch(lastMessageID).catch(noOp); if (message) { @@ -347,7 +348,7 @@ WHERE bitfield && '{2,3,4,5,6,7,8,12,21,24}'::int[] AND user_stats."last_daily_t const channel = guild?.channels.cache.get(Channel.GrandExchange) as TextChannel | undefined; if (!channel) return; const messages = await channel.messages.fetch({ limit: 5 }); - if (messages.some(m => m.author.id === globalClient.user!.id)) return; + if (messages.some(m => m.author.id === globalClient.user?.id)) return; if (lastMessageGEID) { const message = await channel.messages.fetch(lastMessageGEID).catch(noOp); if (message) { diff --git a/src/lib/types/index.ts b/src/lib/types/index.ts index 7a56ce9325f..700246cdccd 100644 --- a/src/lib/types/index.ts +++ b/src/lib/types/index.ts @@ -1,4 +1,4 @@ -import { SkillsEnum } from '../skilling/types'; +import type { SkillsEnum } from '../skilling/types'; export interface ItemBank { [key: string]: number; diff --git a/src/lib/types/minions.ts b/src/lib/types/minions.ts index a28ba90dcdb..c6712b59141 100644 --- a/src/lib/types/minions.ts +++ b/src/lib/types/minions.ts @@ -1,12 +1,12 @@ import type { CropUpgradeType } from '@prisma/client'; -import { NMZStrategy, TwitcherGloves, UnderwaterAgilityThievingTrainingSkill } from '../constants'; +import type { ItemBank } from '.'; +import type { NMZStrategy, TwitcherGloves, UnderwaterAgilityThievingTrainingSkill } from '../constants'; import type { IPatchData } from '../minions/farming/types'; import type { MinigameName } from '../settings/minigames'; -import { RaidLevel } from '../simulation/toa'; +import type { RaidLevel } from '../simulation/toa'; import type { Peak } from '../tickers'; import type { BirdhouseData } from './../skilling/skills/hunter/defaultBirdHouseTrap'; -import type { ItemBank } from '.'; export interface ActivityTaskOptions { userID: string; diff --git a/src/lib/util.ts b/src/lib/util.ts index 40cc56b3aa7..f9467ab4fc1 100644 --- a/src/lib/util.ts +++ b/src/lib/util.ts @@ -1,43 +1,42 @@ import { gzip } from 'node:zlib'; +import { createHash } from 'node:crypto'; import { stripEmojis } from '@oldschoolgg/toolkit'; import { Stopwatch } from '@sapphire/stopwatch'; -import { createHash } from 'crypto'; -import { +import type { BaseMessageOptions, ButtonBuilder, ButtonInteraction, CacheType, Collection, CollectorFilter, - ComponentType, - escapeMarkdown, Guild, InteractionReplyOptions, - InteractionType, Message, MessageEditOptions, SelectMenuInteraction, TextChannel } from 'discord.js'; -import { chunk, notEmpty, objectEntries, Time } from 'e'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import { ComponentType, InteractionType, escapeMarkdown } from 'discord.js'; +import { Time, chunk, notEmpty, objectEntries } from 'e'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import murmurHash from 'murmurhash'; import { Bank } from 'oldschooljs'; import { bool, integer, nodeCrypto, real } from 'random-js'; import { ADMIN_IDS, OWNER_IDS, SupportServer } from '../config'; +import type { MUserClass } from './MUser'; +import { PaginatedMessage } from './PaginatedMessage'; import { ClueTiers } from './clues/clueTiers'; -import { badgesCache, BitField, projectiles, usernameCache } from './constants'; -import { UserStatsDataNeededForCL } from './data/Collections'; +import { BitField, badgesCache, projectiles, usernameCache } from './constants'; +import type { UserStatsDataNeededForCL } from './data/Collections'; import { getSimilarItems } from './data/similarItems'; -import { DefenceGearStat, GearSetupType, GearSetupTypes, GearStat, OffenceGearStat } from './gear/types'; +import type { DefenceGearStat, GearSetupType, OffenceGearStat } from './gear/types'; +import { GearSetupTypes, GearStat } from './gear/types'; import type { Consumable } from './minions/types'; -import { MUserClass } from './MUser'; -import { PaginatedMessage } from './PaginatedMessage'; import type { POHBoosts } from './poh'; import { SkillsEnum } from './skilling/types'; -import { Gear } from './structures/Gear'; +import type { Gear } from './structures/Gear'; import { MUserStats } from './structures/MUserStats'; import type { ItemBank, Skills } from './types'; import type { @@ -55,7 +54,6 @@ export * from 'oldschooljs/dist/util/index'; const zeroWidthSpace = '\u200b'; // @ts-ignore ignore -// eslint-disable-next-line no-extend-native, func-names BigInt.prototype.toJSON = function () { return this.toString(); }; @@ -247,7 +245,7 @@ export function formatPohBoosts(boosts: POHBoosts) { return slotStr.join(', '); } -function gaussianRand(rolls: number = 3) { +function gaussianRand(rolls = 3) { let rand = 0; for (let i = 0; i < rolls; i += 1) { rand += Math.random(); @@ -360,7 +358,7 @@ export function validateBankAndThrow(bank: Bank) { } export function convertBankToPerHourStats(bank: Bank, time: number) { - let result = []; + const result = []; for (const [item, qty] of bank.items()) { result.push(`${(qty / (time / Time.Hour)).toFixed(1)}/hr ${item.name}`); } @@ -434,7 +432,7 @@ export function getBadges(user: MUser | string | bigint) { return user.badgeString; } -export function getUsername(id: string | bigint, withBadges: boolean = true) { +export function getUsername(id: string | bigint, withBadges = true) { let username = usernameCache.get(id.toString()) ?? 'Unknown'; if (withBadges) username = `${getBadges(id)} ${username}`; return username; @@ -540,10 +538,7 @@ export function checkRangeGearWeapon(gear: Gear) { if (!ammo) return 'You have no ammo equipped.'; const projectileCategory = objectEntries(projectiles).find(i => - i[1].weapons - .map(w => getSimilarItems(w)) - .flat() - .includes(weapon.id) + i[1].weapons.flatMap(w => getSimilarItems(w)).includes(weapon.id) ); if (!projectileCategory) return 'You have an invalid range weapon.'; if (!projectileCategory[1].items.includes(ammo.item)) { diff --git a/src/lib/util/activityInArea.ts b/src/lib/util/activityInArea.ts index a22948ce69b..a33e4b7406b 100644 --- a/src/lib/util/activityInArea.ts +++ b/src/lib/util/activityInArea.ts @@ -1,7 +1,7 @@ import { Monsters } from 'oldschooljs'; import { soteSkillRequirements } from '../skilling/functions/questRequirements'; -import { +import type { ActivityTaskData, AgilityActivityTaskOptions, HunterActivityTaskOptions, @@ -11,9 +11,9 @@ import { } from '../types/minions'; import resolveItems from './resolveItems'; -export const enum WorldLocations { - Priffdinas, - World +export enum WorldLocations { + Priffdinas = 0, + World = 1 } const WorldLocationsChecker = [ diff --git a/src/lib/util/addSubTaskToActivityTask.ts b/src/lib/util/addSubTaskToActivityTask.ts index 90582eecefc..1e2f9a752ab 100644 --- a/src/lib/util/addSubTaskToActivityTask.ts +++ b/src/lib/util/addSubTaskToActivityTask.ts @@ -2,7 +2,7 @@ import { UserError } from '@oldschoolgg/toolkit/dist/lib/UserError'; import { prisma } from '../settings/prisma'; import { activitySync } from '../settings/settings'; -import { ActivityTaskData, ActivityTaskOptions } from '../types/minions'; +import type { ActivityTaskData, ActivityTaskOptions } from '../types/minions'; import { isGroupActivity } from '../util'; import { logError } from './logError'; import { getActivityOfUser } from './minionIsBusy'; @@ -17,7 +17,7 @@ export default async function addSubTaskToActivityTask = { ...taskToAdd } as Partial; - delete __newData.type; - delete __newData.userID; - delete __newData.id; - delete __newData.channelID; - delete __newData.duration; + const __newData: Partial = { ...taskToAdd } as Partial; + __newData.type = undefined; + __newData.userID = undefined; + __newData.id = undefined; + __newData.channelID = undefined; + __newData.duration = undefined; - let newData: Omit = { + const newData: Omit = { ...__newData }; diff --git a/src/lib/util/ashSanctifier.ts b/src/lib/util/ashSanctifier.ts index 59083abb69e..5735784bf3c 100644 --- a/src/lib/util/ashSanctifier.ts +++ b/src/lib/util/ashSanctifier.ts @@ -1,4 +1,4 @@ -import { Bank } from 'oldschooljs'; +import type { Bank } from 'oldschooljs'; import { SkillsEnum } from 'oldschooljs/dist/constants'; import { userStatsUpdate } from '../../mahoji/mahojiSettings'; @@ -15,7 +15,7 @@ export async function ashSanctifierEffect(user: MUser, loot: Bank, duration: num const [hasEliteDiary] = await userhasDiaryTier(user, KourendKebosDiary.elite); const ashXpModifider = hasEliteDiary ? 1 : 0.5; - let startingAshSanctifierCharges = await checkDegradeableItemCharges({ + const startingAshSanctifierCharges = await checkDegradeableItemCharges({ item: getOSItem('Ash sanctifier'), user }); diff --git a/src/lib/util/cachedUserIDs.ts b/src/lib/util/cachedUserIDs.ts index 20e025620aa..2483b12071c 100644 --- a/src/lib/util/cachedUserIDs.ts +++ b/src/lib/util/cachedUserIDs.ts @@ -30,14 +30,14 @@ WHERE main_account IS NOT NULL } export function memoryAnalysis() { - let guilds = globalClient.guilds.cache.size; + const guilds = globalClient.guilds.cache.size; let emojis = 0; - let channels = globalClient.channels.cache.size; - let voiceChannels = 0; - let guildTextChannels = 0; + const channels = globalClient.channels.cache.size; + const voiceChannels = 0; + const guildTextChannels = 0; let roles = 0; let members = 0; - let channelCounter: Record = {} as any; + const channelCounter: Record = {} as any; let messages = 0; let voiceStates = 0; let commands = 0; @@ -98,7 +98,7 @@ export const emojiServers = new Set([ export function cacheCleanup() { if (!globalClient.isReady()) return; - let stopwatch = new Stopwatch(); + const stopwatch = new Stopwatch(); stopwatch.start(); debugLog('Cache Cleanup Start', { type: 'CACHE_CLEANUP' @@ -110,15 +110,15 @@ export function cacheCleanup() { globalClient.channels.cache.delete(channel.id); } // @ts-ignore ignore - delete channel.topic; + channel.topic = undefined; // @ts-ignore ignore - delete channel.rateLimitPerUser; + channel.rateLimitPerUser = undefined; // @ts-ignore ignore - delete channel.nsfw; + channel.nsfw = undefined; // @ts-ignore ignore - delete channel.parentId; + channel.parentId = undefined; // @ts-ignore ignore - delete channel.name; + channel.name = undefined; // @ts-ignore ignore channel.lastMessageId = null; // @ts-ignore ignore @@ -151,21 +151,21 @@ export function cacheCleanup() { } for (const role of guild.roles.cache.values()) { // @ts-ignore ignore - delete role.managed; + role.managed = undefined; // @ts-ignore ignore - delete role.name; + role.name = undefined; // @ts-ignore ignore - delete role.tags; + role.tags = undefined; // @ts-ignore ignore - delete role.icon; + role.icon = undefined; // @ts-ignore ignore - delete role.unicodeEmoji; + role.unicodeEmoji = undefined; // @ts-ignore ignore - delete role.rawPosition; + role.rawPosition = undefined; // @ts-ignore ignore - delete role.color; + role.color = undefined; // @ts-ignore ignore - delete role.hoist; + role.hoist = undefined; } } }); diff --git a/src/lib/util/calcConBonusXP.ts b/src/lib/util/calcConBonusXP.ts index a6cb9bc0309..413c28c4776 100644 --- a/src/lib/util/calcConBonusXP.ts +++ b/src/lib/util/calcConBonusXP.ts @@ -1,6 +1,6 @@ import { round } from 'e'; -import { GearSetup } from '../gear/types'; +import type { GearSetup } from '../gear/types'; import itemID from './itemID'; export function calcConBonusXP(setup: GearSetup): number { diff --git a/src/lib/util/calcDropRatesFromBank.ts b/src/lib/util/calcDropRatesFromBank.ts index a437635d71f..12f927bb294 100644 --- a/src/lib/util/calcDropRatesFromBank.ts +++ b/src/lib/util/calcDropRatesFromBank.ts @@ -1,9 +1,9 @@ import { bold } from 'discord.js'; import { calcWhatPercent } from 'e'; -import { Bank } from 'oldschooljs'; +import type { Bank } from 'oldschooljs'; export function calcDropRatesFromBank(bank: Bank, iterations: number, uniques: number[]) { - let result = []; + const result = []; let uniquesReceived = 0; for (const [item, qty] of bank.items().sort((a, b) => a[1] - b[1])) { if (uniques.includes(item.id)) { @@ -25,7 +25,7 @@ export function calcDropRatesFromBank(bank: Bank, iterations: number, uniques: n } export function calcDropRatesFromBankWithoutUniques(bank: Bank, iterations: number) { - let results = []; + const results = []; for (const [item, qty] of bank.items().sort((a, b) => a[1] - b[1])) { const rate = Math.round(iterations / qty); if (rate < 2) continue; diff --git a/src/lib/util/calcMassDurationQuantity.ts b/src/lib/util/calcMassDurationQuantity.ts index 7c0943e4a48..47627131829 100644 --- a/src/lib/util/calcMassDurationQuantity.ts +++ b/src/lib/util/calcMassDurationQuantity.ts @@ -1,5 +1,5 @@ import { reducedTimeForGroup } from '../minions/functions'; -import { KillableMonster } from '../minions/types'; +import type { KillableMonster } from '../minions/types'; import { calcMaxTripLength } from './calcMaxTripLength'; export default async function calcDurQty( diff --git a/src/lib/util/calcMaxTripLength.ts b/src/lib/util/calcMaxTripLength.ts index b646eb153e5..03cde74c37b 100644 --- a/src/lib/util/calcMaxTripLength.ts +++ b/src/lib/util/calcMaxTripLength.ts @@ -1,5 +1,5 @@ -import { activity_type_enum } from '@prisma/client'; -import { calcPercentOfNum, calcWhatPercent, Time } from 'e'; +import type { activity_type_enum } from '@prisma/client'; +import { Time, calcPercentOfNum, calcWhatPercent } from 'e'; import { PerkTier } from '../constants'; import { SkillsEnum } from '../skilling/types'; diff --git a/src/lib/util/calcWildyPkChance.ts b/src/lib/util/calcWildyPkChance.ts index a8b6d4ad9c8..80bf5e76ad5 100644 --- a/src/lib/util/calcWildyPkChance.ts +++ b/src/lib/util/calcWildyPkChance.ts @@ -1,12 +1,12 @@ -import { calcPercentOfNum, calcWhatPercent, reduceNumByPercent, Time } from 'e'; +import { Time, calcPercentOfNum, calcWhatPercent, reduceNumByPercent } from 'e'; import { randomVariation } from 'oldschooljs/dist/util'; import { userStatsUpdate } from '../../mahoji/mahojiSettings'; import { PeakTier } from '../constants'; -import { KillableMonster } from '../minions/types'; +import type { KillableMonster } from '../minions/types'; import { SkillsEnum } from '../skilling/types'; import { maxDefenceStats } from '../structures/Gear'; -import { Peak } from '../tickers'; +import type { Peak } from '../tickers'; import { percentChance } from '../util'; const peakFactor = [ @@ -50,7 +50,7 @@ export async function calcWildyPKChance( ): Promise<[number, boolean, string]> { // Chance per minute, Difficulty from 1 to 10, and factor a million difference, High peak 5x as likley encounter, Medium peak 1x, Low peak 5x as unlikley const peakInfluence = peakFactor.find(_peaktier => _peaktier.peakTier === peak?.peakTier)?.factor ?? 1; - let pkChance = (1 / (7_000_000 / (Math.pow(monster.pkActivityRating ?? 1, 6) * peakInfluence))) * 100; + const pkChance = (1 / (7_000_000 / (Math.pow(monster.pkActivityRating ?? 1, 6) * peakInfluence))) * 100; let chanceString = `**PK Chance:** ${pkChance.toFixed(2)}% per min (${peak.peakTier} peak time)`; const evasionReduction = await getWildEvasionPercent(user); @@ -78,21 +78,19 @@ export async function calcWildyPKChance( // Weighted Melee gear percent protection 60% slash, 30% crush, 10% stab const defensiveMeleeGearPercent = - (Math.max(0, calcWhatPercent(user.gear.wildy.getStats().defence_slash, maxDefenceStats['defence_slash'])) * 60 + - Math.max(0, calcWhatPercent(user.gear.wildy.getStats().defence_crush, maxDefenceStats['defence_crush'])) * - 30 + - Math.max(0, calcWhatPercent(user.gear.wildy.getStats().defence_stab, maxDefenceStats['defence_stab'])) * - 10) / + (Math.max(0, calcWhatPercent(user.gear.wildy.getStats().defence_slash, maxDefenceStats.defence_slash)) * 60 + + Math.max(0, calcWhatPercent(user.gear.wildy.getStats().defence_crush, maxDefenceStats.defence_crush)) * 30 + + Math.max(0, calcWhatPercent(user.gear.wildy.getStats().defence_stab, maxDefenceStats.defence_stab)) * 10) / 100; const defensiveRangeGearPercent = Math.max( 0, - calcWhatPercent(user.gear.wildy.getStats().defence_ranged, maxDefenceStats['defence_ranged']) + calcWhatPercent(user.gear.wildy.getStats().defence_ranged, maxDefenceStats.defence_ranged) ); const defensiveMageGearPercent = Math.max( 0, - calcWhatPercent(user.gear.wildy.getStats().defence_magic, maxDefenceStats['defence_magic']) + calcWhatPercent(user.gear.wildy.getStats().defence_magic, maxDefenceStats.defence_magic) ); // Weighted attack style percent, 60% magic, 40% ranged, 20% melee diff --git a/src/lib/util/calculateGearLostOnDeathWilderness.ts b/src/lib/util/calculateGearLostOnDeathWilderness.ts index e3173021a70..685a179ebc0 100644 --- a/src/lib/util/calculateGearLostOnDeathWilderness.ts +++ b/src/lib/util/calculateGearLostOnDeathWilderness.ts @@ -1,8 +1,8 @@ import { deepClone, objectEntries } from 'e'; import { Bank } from 'oldschooljs'; -import { EquipmentSlot, Item } from 'oldschooljs/dist/meta/types'; +import type { EquipmentSlot, Item } from 'oldschooljs/dist/meta/types'; -import { GearSetup } from '../gear/types'; +import type { GearSetup } from '../gear/types'; import getOSItem from './getOSItem'; import itemID from './itemID'; import resolveItems from './resolveItems'; @@ -14,9 +14,9 @@ export const gearSwap: IGearSwap = { [itemID("Craw's bow")]: [itemID("Craw's bow (u)")], [itemID("Thammaron's sceptre")]: [itemID("Thammaron's sceptre (u)")], [itemID("Viggora's chainmace")]: [itemID("Viggora's chainmace (u)")], - 23_330: [itemID('Rune scimitar ornament kit (guthix)'), itemID('Rune scimitar')], - 23_332: [itemID('Rune scimitar ornament kit (saradomin)'), itemID('Rune scimitar')], - 23_334: [itemID('Rune scimitar ornament kit (zamorak)'), itemID('Rune scimitar')], + 23330: [itemID('Rune scimitar ornament kit (guthix)'), itemID('Rune scimitar')], + 23332: [itemID('Rune scimitar ornament kit (saradomin)'), itemID('Rune scimitar')], + 23334: [itemID('Rune scimitar ornament kit (zamorak)'), itemID('Rune scimitar')], [itemID('Rune defender (t)')]: [itemID('Rune defender'), itemID('Rune defender ornament kit')], [itemID('Dragon full helm (g)')]: [itemID('Dragon full helm'), itemID('Dragon full helm ornament kit')], [itemID('Dragon chainbody (g)')]: [itemID('Dragon chainbody'), itemID('Dragon chainbody ornament kit')], @@ -55,38 +55,38 @@ export const gearSwap: IGearSwap = { [itemID('Holy sanguinesti staff')]: [itemID('Holy ornament kit'), itemID('Sanguinesti staff')], [itemID('Holy scythe of vitur')]: [itemID('Holy ornament kit'), itemID('Scythe of vitur')], [itemID('Sanguine scythe of vitur')]: [itemID('Sanguine ornament kit'), itemID('Scythe of vitur')], - 24_743: [itemID('Graceful hood'), itemID('Dark dye')], - 24_749: [itemID('Graceful top'), itemID('Dark dye')], - 24_752: [itemID('Graceful legs'), itemID('Dark dye')], - 24_755: [itemID('Graceful gloves'), itemID('Dark dye')], - 24_758: [itemID('Graceful boots'), itemID('Dark dye')], - 24_746: [itemID('Graceful cape'), itemID('Dark dye')], - 25_069: [itemID('Graceful hood'), itemID('Trailblazer graceful ornament kit')], - 25_075: [itemID('Graceful top'), itemID('Trailblazer graceful ornament kit')], - 25_078: [itemID('Graceful legs'), itemID('Trailblazer graceful ornament kit')], - 25_081: [itemID('Graceful gloves'), itemID('Trailblazer graceful ornament kit')], - 25_084: [itemID('Graceful boots'), itemID('Trailblazer graceful ornament kit')], - 25_072: [itemID('Graceful cape'), itemID('Trailblazer graceful ornament kit')], + 24743: [itemID('Graceful hood'), itemID('Dark dye')], + 24749: [itemID('Graceful top'), itemID('Dark dye')], + 24752: [itemID('Graceful legs'), itemID('Dark dye')], + 24755: [itemID('Graceful gloves'), itemID('Dark dye')], + 24758: [itemID('Graceful boots'), itemID('Dark dye')], + 24746: [itemID('Graceful cape'), itemID('Dark dye')], + 25069: [itemID('Graceful hood'), itemID('Trailblazer graceful ornament kit')], + 25075: [itemID('Graceful top'), itemID('Trailblazer graceful ornament kit')], + 25078: [itemID('Graceful legs'), itemID('Trailblazer graceful ornament kit')], + 25081: [itemID('Graceful gloves'), itemID('Trailblazer graceful ornament kit')], + 25084: [itemID('Graceful boots'), itemID('Trailblazer graceful ornament kit')], + 25072: [itemID('Graceful cape'), itemID('Trailblazer graceful ornament kit')], [itemID('Dragon axe (or)')]: [itemID('Dragon axe'), itemID('Trailblazer tool ornament kit')], [itemID('Infernal axe (or)')]: [itemID('Infernal axe'), itemID('Trailblazer tool ornament kit')], [itemID('Dragon harpoon (or)')]: [itemID('Dragon harpoon'), itemID('Trailblazer tool ornament kit')], [itemID('Infernal harpoon (or)')]: [itemID('Infernal harpoon'), itemID('Trailblazer tool ornament kit')], - 25_376: [itemID('Dragon pickaxe'), itemID('Trailblazer tool ornament kit')], + 25376: [itemID('Dragon pickaxe'), itemID('Trailblazer tool ornament kit')], [itemID('Infernal pickaxe (or)')]: [itemID('Infernal pickaxe'), itemID('Trailblazer tool ornament kit')], [itemID('Dragon pickaxe (or)')]: [itemID('Dragon pickaxe'), itemID('Zalcano shard')], - 12_797: [itemID('Dragon pickaxe'), itemID('Dragon pickaxe upgrade kit')], - 12_795: [itemID('Steam battlestaff'), itemID('Steam staff upgrade kit')], - 21_198: [itemID('Lava battlestaff'), itemID('Lava staff upgrade kit')], - 12_807: [itemID('Odium ward'), itemID('Ward upgrade kit')], - 12_806: [itemID('Malediction ward'), itemID('Ward upgrade kit')], - 12_765: [itemID('Dark bow'), itemID('Green dark bow paint')], - 12_766: [itemID('Dark bow'), itemID('Blue dark bow paint')], - 12_767: [itemID('Dark bow'), itemID('Yellow dark bow paint')], - 12_768: [itemID('Dark bow'), itemID('White dark bow paint')], + 12797: [itemID('Dragon pickaxe'), itemID('Dragon pickaxe upgrade kit')], + 12795: [itemID('Steam battlestaff'), itemID('Steam staff upgrade kit')], + 21198: [itemID('Lava battlestaff'), itemID('Lava staff upgrade kit')], + 12807: [itemID('Odium ward'), itemID('Ward upgrade kit')], + 12806: [itemID('Malediction ward'), itemID('Ward upgrade kit')], + 12765: [itemID('Dark bow'), itemID('Green dark bow paint')], + 12766: [itemID('Dark bow'), itemID('Blue dark bow paint')], + 12767: [itemID('Dark bow'), itemID('Yellow dark bow paint')], + 12768: [itemID('Dark bow'), itemID('White dark bow paint')], [itemID('Volcanic abyssal whip')]: [itemID('Abyssal whip'), itemID('Volcanic whip mix')], [itemID('Frozen abyssal whip')]: [itemID('Abyssal whip'), itemID('Frozen whip mix')], - 12_848: [itemID('Granite maul'), itemID('Granite clamp')], - 24_227: [24_225, itemID('Granite clamp')], + 12848: [itemID('Granite maul'), itemID('Granite clamp')], + 24227: [24_225, itemID('Granite clamp')], [itemID('Abyssal tentacle')]: [itemID('Abyssal whip'), itemID('Kraken tentacle')], [itemID("Thammaron's sceptre (a)")]: [itemID("Thammaron's sceptre (au)")], [itemID('Webweaver bow')]: [itemID('Webweaver bow (u)')], @@ -180,8 +180,8 @@ export default function calculateGearLostOnDeathWilderness( >{} ) { // 1 - Duplicate user gear - let userGear = { ...deepClone(options.gear) }; - let removableItems: { slot: EquipmentSlot; sorter: number; originalItem: Item }[] = []; + const userGear = { ...deepClone(options.gear) }; + const removableItems: { slot: EquipmentSlot; sorter: number; originalItem: Item }[] = []; // 2 - Swap user gear to the correct gear for death calculations for (const [_slot, _data] of objectEntries(userGear)) { diff --git a/src/lib/util/canvasUtil.ts b/src/lib/util/canvasUtil.ts index 0dc53ac9b90..ce7066ead49 100644 --- a/src/lib/util/canvasUtil.ts +++ b/src/lib/util/canvasUtil.ts @@ -1,4 +1,5 @@ -import { Canvas, Image, loadImage, SKRSContext2D } from '@napi-rs/canvas'; +import type { Image, SKRSContext2D } from '@napi-rs/canvas'; +import { Canvas, loadImage } from '@napi-rs/canvas'; import { formatItemStackQuantity, generateHexColorForCashStack } from '@oldschoolgg/toolkit'; import { assert } from '../util'; @@ -43,8 +44,8 @@ export function drawImageWithOutline( dw: number, dh: number, outlineColor: string, - outlineWidth: number = 1, - alpha: number = 0.5 + outlineWidth = 1, + alpha = 0.5 ): void { const dArr = [-1, -1, 0, -1, 1, -1, -1, 0, 1, 0, -1, 1, 0, 1, 1, 1]; const purplecanvas = new Canvas(image.width + (outlineWidth + 2), image.height + (outlineWidth + 2)); @@ -64,11 +65,10 @@ function printMultilineText(ctx: SKRSContext2D, text: string, x: number, y: numb let linePositionY = y; for (const line of lines) { - let lineMeasured = ctx.measureText(line); - let thisX = Math.floor(x - lineMeasured.width / 2); + const lineMeasured = ctx.measureText(line); + const thisX = Math.floor(x - lineMeasured.width / 2); ctx.fillText(line, thisX, Math.floor(linePositionY)); - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands - let height: number = lineMeasured.actualBoundingBoxAscent + lineMeasured.actualBoundingBoxDescent; + const height: number = lineMeasured.actualBoundingBoxAscent + lineMeasured.actualBoundingBoxDescent; linePositionY += height + 1; } } diff --git a/src/lib/util/chart.ts b/src/lib/util/chart.ts index a7c251bc484..55308e9c620 100644 --- a/src/lib/util/chart.ts +++ b/src/lib/util/chart.ts @@ -92,7 +92,7 @@ export async function barChart( title: string, format: (value: any) => string, values: [string, number, string?][], - useRelativeColors: boolean = false + useRelativeColors = false ) { const positiveValues = values.map(i => i[1]).filter(v => v > 0); const negativeValues = values.map(i => i[1]).filter(v => v < 0); diff --git a/src/lib/util/clLeaderboard.ts b/src/lib/util/clLeaderboard.ts index 14229860d9a..6538a16690f 100644 --- a/src/lib/util/clLeaderboard.ts +++ b/src/lib/util/clLeaderboard.ts @@ -1,4 +1,4 @@ -import { UserEvent } from '@prisma/client'; +import type { UserEvent } from '@prisma/client'; import { prisma } from '../settings/prisma'; import { userEventsToMap } from './userEvents'; diff --git a/src/lib/util/clientSettings.ts b/src/lib/util/clientSettings.ts index 857a074834c..3732e60df76 100644 --- a/src/lib/util/clientSettings.ts +++ b/src/lib/util/clientSettings.ts @@ -1,4 +1,4 @@ -import { ClientStorage, Prisma } from '@prisma/client'; +import type { ClientStorage, Prisma } from '@prisma/client'; import { globalConfig } from '../constants'; import { prisma } from '../settings/prisma'; diff --git a/src/lib/util/commandUsage.ts b/src/lib/util/commandUsage.ts index e93e5ae221b..4d41bb4fedb 100644 --- a/src/lib/util/commandUsage.ts +++ b/src/lib/util/commandUsage.ts @@ -1,5 +1,6 @@ -import { command_usage_status, Prisma } from '@prisma/client'; -import { CommandOptions } from 'mahoji/dist/lib/types'; +import type { Prisma } from '@prisma/client'; +import { command_usage_status } from '@prisma/client'; +import type { CommandOptions } from 'mahoji/dist/lib/types'; import { getCommandArgs } from '../../mahoji/lib/util'; diff --git a/src/lib/util/equipMulti.ts b/src/lib/util/equipMulti.ts index dba19bc0e50..844a1fed17d 100644 --- a/src/lib/util/equipMulti.ts +++ b/src/lib/util/equipMulti.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import { EquipmentSlot } from 'oldschooljs/dist/meta/types'; -import { GearSetup } from '../gear/types'; +import type { GearSetup } from '../gear/types'; import { isValidGearSetup, skillsMeetRequirements } from '../util'; import { parseStringBank } from './parseStringBank'; @@ -56,22 +56,22 @@ export function gearEquipMultiImpl( for (const [item, qty] of equipBank.items()) { const { slot } = item.equipment!; if (slot === EquipmentSlot.TwoHanded && equippedGear[EquipmentSlot.Shield]) { - unequipBank.add(equippedGear[EquipmentSlot.Shield]!.item, equippedGear[EquipmentSlot.Shield]!.quantity); + unequipBank.add(equippedGear[EquipmentSlot.Shield]?.item, equippedGear[EquipmentSlot.Shield]?.quantity); equippedGear[EquipmentSlot.Shield] = null; } if (slot === EquipmentSlot.TwoHanded && equippedGear[EquipmentSlot.Weapon]) { - unequipBank.add(equippedGear[EquipmentSlot.Weapon]!.item, equippedGear[EquipmentSlot.Weapon]!.quantity); + unequipBank.add(equippedGear[EquipmentSlot.Weapon]?.item, equippedGear[EquipmentSlot.Weapon]?.quantity); equippedGear[EquipmentSlot.Weapon] = null; } if (equippedGear[EquipmentSlot.TwoHanded] && (slot === EquipmentSlot.Shield || slot === EquipmentSlot.Weapon)) { unequipBank.add( - equippedGear[EquipmentSlot.TwoHanded]!.item, - equippedGear[EquipmentSlot.TwoHanded]!.quantity + equippedGear[EquipmentSlot.TwoHanded]?.item, + equippedGear[EquipmentSlot.TwoHanded]?.quantity ); equippedGear[EquipmentSlot.TwoHanded] = null; } if (equippedGear[slot]) { - unequipBank.add(equippedGear[slot]!.item, equippedGear[slot]!.quantity); + unequipBank.add(equippedGear[slot]?.item, equippedGear[slot]?.quantity); } equippedGear[slot] = { item: item.id, quantity: qty }; } diff --git a/src/lib/util/farmingHelpers.ts b/src/lib/util/farmingHelpers.ts index a9fa6953420..c0f70615759 100644 --- a/src/lib/util/farmingHelpers.ts +++ b/src/lib/util/farmingHelpers.ts @@ -1,8 +1,8 @@ -import { FarmedCrop, User } from '@prisma/client'; -import { BaseMessageOptions, ButtonBuilder } from 'discord.js'; +import type { FarmedCrop, User } from '@prisma/client'; +import type { BaseMessageOptions, ButtonBuilder } from 'discord.js'; import { Emoji } from '../constants'; -import { IPatchData, IPatchDataDetailed } from '../minions/farming/types'; +import type { IPatchData, IPatchDataDetailed } from '../minions/farming/types'; import Farming from '../skilling/skills/farming'; import { dateFm, makeAutoFarmButton, makeComponents, stringMatches } from '../util'; diff --git a/src/lib/util/findMonster.ts b/src/lib/util/findMonster.ts index 5088158d9ab..cc6f4ba2e09 100644 --- a/src/lib/util/findMonster.ts +++ b/src/lib/util/findMonster.ts @@ -1,5 +1,5 @@ import killableMonsters from '../minions/data/killableMonsters'; -import { KillableMonster } from '../minions/types'; +import type { KillableMonster } from '../minions/types'; import { stringMatches } from '../util'; export default function findMonster(str = ''): KillableMonster | undefined { diff --git a/src/lib/util/getKCByName.ts b/src/lib/util/getKCByName.ts index 7a744f63c55..76cf7d9cf78 100644 --- a/src/lib/util/getKCByName.ts +++ b/src/lib/util/getKCByName.ts @@ -1,8 +1,8 @@ import { stringMatches } from '@oldschoolgg/toolkit'; -import Monster from 'oldschooljs/dist/structures/Monster'; +import type Monster from 'oldschooljs/dist/structures/Monster'; import { effectiveMonsters } from '../minions/data/killableMonsters'; -import { getMinigameScore, Minigames } from '../settings/minigames'; +import { Minigames, getMinigameScore } from '../settings/minigames'; import creatures from '../skilling/skills/hunter/creatures'; export async function getKCByName(user: MUser, kcName: string): Promise<[string, number] | [null, 0]> { diff --git a/src/lib/util/getOSItem.ts b/src/lib/util/getOSItem.ts index ac910a1e1e1..4b865fe9514 100644 --- a/src/lib/util/getOSItem.ts +++ b/src/lib/util/getOSItem.ts @@ -2,7 +2,7 @@ import '../data/trophies'; import { UserError } from '@oldschoolgg/toolkit/dist/lib/UserError'; import { Items } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; import { production } from '../../config'; @@ -22,7 +22,7 @@ export default function getOSItem(itemName: string | number): Item { identifier = itemName; } else { const parsed = Number(itemName); - identifier = isNaN(parsed) ? cleanItemName(itemName) : parsed; + identifier = Number.isNaN(parsed) ? cleanItemName(itemName) : parsed; } const osItem = Items.get(identifier) as Item | undefined; diff --git a/src/lib/util/giveaway.ts b/src/lib/util/giveaway.ts index e26a8bcbd55..8e44499264f 100644 --- a/src/lib/util/giveaway.ts +++ b/src/lib/util/giveaway.ts @@ -1,14 +1,15 @@ -import { MessageEditOptions, time, userMention } from 'discord.js'; -import { debounce, noOp, randArrItem, Time } from 'e'; +import type { MessageEditOptions } from 'discord.js'; +import { time, userMention } from 'discord.js'; +import { Time, debounce, noOp, randArrItem } from 'e'; import { Bank } from 'oldschooljs'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; import { Events } from '../constants'; import { prisma } from '../settings/prisma'; import { channelIsSendable } from '../util'; import { logError } from './logError'; import { sendToChannelID } from './webhook'; -import { Giveaway } from '.prisma/client'; +import type { Giveaway } from '.prisma/client'; async function refundGiveaway(creator: MUser, loot: Bank) { await transactItems({ diff --git a/src/lib/util/globalInteractions.ts b/src/lib/util/globalInteractions.ts index 0a7883527e7..eb9283f9172 100644 --- a/src/lib/util/globalInteractions.ts +++ b/src/lib/util/globalInteractions.ts @@ -1,18 +1,19 @@ import { mentionCommand } from '@oldschoolgg/toolkit'; -import { ButtonBuilder, ButtonInteraction, ButtonStyle, Interaction } from 'discord.js'; -import { removeFromArr, Time, uniqueArr } from 'e'; +import type { ButtonInteraction, Interaction } from 'discord.js'; +import { ButtonBuilder, ButtonStyle } from 'discord.js'; +import { Time, removeFromArr, uniqueArr } from 'e'; import { Bank } from 'oldschooljs'; +import { Cooldowns } from '../../mahoji/lib/Cooldowns'; import { cancelGEListingCommand } from '../../mahoji/lib/abstracted_commands/cancelGEListingCommand'; import { autoContract } from '../../mahoji/lib/abstracted_commands/farmingContractCommand'; import { shootingStarsCommand, starCache } from '../../mahoji/lib/abstracted_commands/shootingStarsCommand'; -import { Cooldowns } from '../../mahoji/lib/Cooldowns'; -import { ClueTier } from '../clues/clueTiers'; +import type { ClueTier } from '../clues/clueTiers'; import { BitField, PerkTier } from '../constants'; import { prisma } from '../settings/prisma'; import { runCommand } from '../settings/settings'; import { toaHelpCommand } from '../simulation/toa'; -import { ItemBank } from '../types'; +import type { ItemBank } from '../types'; import { formatDuration, stringMatches } from '../util'; import { updateGiveawayMessage } from './giveaway'; import { deferInteraction, interactionReply } from './interactionReply'; diff --git a/src/lib/util/handleMahojiConfirmation.ts b/src/lib/util/handleMahojiConfirmation.ts index 17c3c8bc065..dca8978ce53 100644 --- a/src/lib/util/handleMahojiConfirmation.ts +++ b/src/lib/util/handleMahojiConfirmation.ts @@ -1,15 +1,7 @@ import { channelIsSendable } from '@oldschoolgg/toolkit'; -import { - ActionRowBuilder, - ButtonBuilder, - ButtonInteraction, - ButtonStyle, - ChatInputCommandInteraction, - ComponentType, - InteractionResponseType, - Routes -} from 'discord.js'; -import { noOp, Time } from 'e'; +import type { ButtonInteraction, ChatInputCommandInteraction, ComponentType } from 'discord.js'; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, InteractionResponseType, Routes } from 'discord.js'; +import { Time, noOp } from 'e'; import { SILENT_ERROR } from '../constants'; import { deferInteraction, interactionReply } from './interactionReply'; @@ -32,7 +24,7 @@ export async function handleMahojiConfirmation( await deferInteraction(interaction); const users = _users ?? [interaction.user.id]; - let confirmed: string[] = []; + const confirmed: string[] = []; const isConfirmed = () => confirmed.length === users.length; const confirmMessage = await channel.send({ content: str, diff --git a/src/lib/util/handleTripFinish.ts b/src/lib/util/handleTripFinish.ts index a1290432b5c..3c2932da897 100644 --- a/src/lib/util/handleTripFinish.ts +++ b/src/lib/util/handleTripFinish.ts @@ -1,6 +1,6 @@ -import { activity_type_enum } from '@prisma/client'; -import { AttachmentBuilder, ButtonBuilder, MessageCollector, MessageCreateOptions } from 'discord.js'; -import { Bank } from 'oldschooljs'; +import type { activity_type_enum } from '@prisma/client'; +import type { AttachmentBuilder, ButtonBuilder, MessageCollector, MessageCreateOptions } from 'discord.js'; +import type { Bank } from 'oldschooljs'; import { calculateBirdhouseDetails } from '../../mahoji/lib/abstracted_commands/birdhousesCommand'; import { canRunAutoContract } from '../../mahoji/lib/abstracted_commands/farmingContractCommand'; @@ -14,7 +14,7 @@ import { handleGrowablePetGrowth } from '../growablePets'; import { handlePassiveImplings } from '../implings'; import { triggerRandomEvent } from '../randomEvents'; import { getUsersCurrentSlayerInfo } from '../slayer/slayerUtil'; -import { ActivityTaskData } from '../types/minions'; +import type { ActivityTaskData } from '../types/minions'; import { channelIsSendable, makeComponents } from '../util'; import { makeAutoContractButton, diff --git a/src/lib/util/interactionHelpers.ts b/src/lib/util/interactionHelpers.ts index f9c899aa055..e69f46b05ba 100644 --- a/src/lib/util/interactionHelpers.ts +++ b/src/lib/util/interactionHelpers.ts @@ -1,6 +1,7 @@ -import { createHash } from 'crypto'; -import { ButtonBuilder, ButtonStyle, ChatInputCommandInteraction, ComponentType } from 'discord.js'; -import { chunk, randInt, Time } from 'e'; +import { createHash } from 'node:crypto'; +import type { ChatInputCommandInteraction } from 'discord.js'; +import { ButtonBuilder, ButtonStyle, ComponentType } from 'discord.js'; +import { Time, chunk, randInt } from 'e'; import { deferInteraction, interactionReply } from './interactionReply'; diff --git a/src/lib/util/interactionReply.ts b/src/lib/util/interactionReply.ts index f32c6a551de..076823bea10 100644 --- a/src/lib/util/interactionReply.ts +++ b/src/lib/util/interactionReply.ts @@ -1,14 +1,14 @@ import { UserError } from '@oldschoolgg/toolkit/dist/lib/UserError'; -import { +import type { ButtonInteraction, ChatInputCommandInteraction, - DiscordAPIError, Interaction, InteractionReplyOptions, InteractionResponse, Message, RepliableInteraction } from 'discord.js'; +import { DiscordAPIError } from 'discord.js'; import { SILENT_ERROR } from '../constants'; import { logErrorForInteraction } from './logError'; diff --git a/src/lib/util/itemIsTradeable.ts b/src/lib/util/itemIsTradeable.ts index 6e92c67ef52..28e64509f83 100644 --- a/src/lib/util/itemIsTradeable.ts +++ b/src/lib/util/itemIsTradeable.ts @@ -11,8 +11,7 @@ const specialUntradeables = resolveItems([ ...leaguesCreatables .filter(i => !i.noCl) .map(i => new Bank(i.outputItems)) - .map(i => i.items().map(i => i[0].id)) - .flat(), + .flatMap(i => i.items().map(i => i[0].id)), ...leagueBuyables.map(i => i.item.id) ]); diff --git a/src/lib/util/linkedAccountsUtil.ts b/src/lib/util/linkedAccountsUtil.ts index 3d3fb9f5ac9..f5b80705666 100644 --- a/src/lib/util/linkedAccountsUtil.ts +++ b/src/lib/util/linkedAccountsUtil.ts @@ -1,11 +1,11 @@ -import { User } from '@prisma/client'; +import type { User } from '@prisma/client'; import { mahojiUsersSettingsFetch } from '../../mahoji/mahojiSettings'; import { MUserClass } from '../MUser'; import { prisma } from '../settings/prisma'; async function syncLinkedAccountPerks(user: MUser) { - let main = user.user.main_account; + const main = user.user.main_account; const allAccounts: string[] = [...user.user.ironman_alts]; if (main) { allAccounts.push(main); diff --git a/src/lib/util/logError.ts b/src/lib/util/logError.ts index 83327f7f1c5..f0235819dda 100644 --- a/src/lib/util/logError.ts +++ b/src/lib/util/logError.ts @@ -1,5 +1,5 @@ import { captureException } from '@sentry/node'; -import { Interaction } from 'discord.js'; +import type { Interaction } from 'discord.js'; import { convertAPIOptionsToCommandOptions } from 'mahoji/dist/lib/util'; import { production } from '../../config'; diff --git a/src/lib/util/makeBankImage.ts b/src/lib/util/makeBankImage.ts index eed2a272f6d..45b73e01cf0 100644 --- a/src/lib/util/makeBankImage.ts +++ b/src/lib/util/makeBankImage.ts @@ -1,7 +1,7 @@ -import { Bank } from 'oldschooljs'; +import type { Bank } from 'oldschooljs'; -import { BankFlag } from '../bankImage'; -import { Flags } from '../minions/types'; +import type { BankFlag } from '../bankImage'; +import type { Flags } from '../minions/types'; interface MakeBankImageOptions { bank: Bank; @@ -25,7 +25,7 @@ export async function makeBankImage({ flags = {}, mahojiFlags = [] }: MakeBankImageOptions) { - let realFlags: Flags = { ...flags, background: background ?? 1, nocache: 1 }; + const realFlags: Flags = { ...flags, background: background ?? 1, nocache: 1 }; if (showNewCL || previousCL !== undefined) realFlags.showNewCL = 1; const { image, isTransparent } = await bankImageGenerator.generateBankImage({ bank, diff --git a/src/lib/util/migrateUser.ts b/src/lib/util/migrateUser.ts index dbdeea7f124..796d31c3ba6 100644 --- a/src/lib/util/migrateUser.ts +++ b/src/lib/util/migrateUser.ts @@ -15,7 +15,7 @@ export async function migrateUser(_source: string | MUser, _dest: string | MUser // First check for + cancel active GE Listings: await Promise.all([cancelUsersListings(sourceUser), cancelUsersListings(destUser)]); - let transactions = []; + const transactions = []; transactions.push(prisma.$executeRaw`SET CONSTRAINTS ALL DEFERRED`); // Delete Queries @@ -190,7 +190,10 @@ export async function migrateUser(_source: string | MUser, _dest: string | MUser const robochimpTx = []; robochimpTx.push(roboChimpClient.user.deleteMany({ where: { id: BigInt(destUser.id) } })); robochimpTx.push( - roboChimpClient.user.updateMany({ where: { id: BigInt(sourceUser.id) }, data: { id: BigInt(destUser.id) } }) + roboChimpClient.user.updateMany({ + where: { id: BigInt(sourceUser.id) }, + data: { id: BigInt(destUser.id) } + }) ); // Set the migrated_user_id value to prevent duplicate robochimp migrations. robochimpTx.push( diff --git a/src/lib/util/minionStatsEmbed.ts b/src/lib/util/minionStatsEmbed.ts index e7d282627a4..88304abc780 100644 --- a/src/lib/util/minionStatsEmbed.ts +++ b/src/lib/util/minionStatsEmbed.ts @@ -2,7 +2,7 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; import { EmbedBuilder } from 'discord.js'; import { shuffleArr, sumArr } from 'e'; import { Bank } from 'oldschooljs'; -import { SkillsScore } from 'oldschooljs/dist/meta/types'; +import type { SkillsScore } from 'oldschooljs/dist/meta/types'; import { convertXPtoLVL, toKMB } from 'oldschooljs/dist/util'; import { ClueTiers } from '../clues/clueTiers'; @@ -13,7 +13,7 @@ import { skillEmoji } from '../data/emojis'; import { effectiveMonsters } from '../minions/data/killableMonsters'; import { courses } from '../skilling/skills/agility'; import creatures from '../skilling/skills/hunter/creatures'; -import { ItemBank, Skills } from '../types'; +import type { ItemBank, Skills } from '../types'; import { logError } from './logError'; export async function minionStatsEmbed(user: MUser): Promise { @@ -103,7 +103,7 @@ export async function minionStatsEmbed(user: MUser): Promise { name: '<:Clue_scroll:365003979840552960> Clue Scores', value: clueEntries .map(([id, qty]) => { - const clueTier = ClueTiers.find(t => t.id === parseInt(id)); + const clueTier = ClueTiers.find(t => t.id === Number.parseInt(id)); if (!clueTier) { logError(`No clueTier: ${id}`); return; @@ -144,14 +144,14 @@ export async function minionStatsEmbed(user: MUser): Promise { const lapCounts = Object.entries(userStats.laps_scores as ItemBank).sort((a, b) => a[1] - b[1]); if (lapCounts.length > 0) { const [id, score] = lapCounts[0]; - const res = courses.find(c => c.id === parseInt(id))!; + const res = courses.find(c => c.id === Number.parseInt(id))!; otherStats.push([`${res.name} Laps`, score]); } const monsterScores = Object.entries(userStats.monster_scores as ItemBank).sort((a, b) => a[1] - b[1]); if (monsterScores.length > 0) { const [id, score] = monsterScores[0]; - const res = effectiveMonsters.find(c => c.id === parseInt(id))!; + const res = effectiveMonsters.find(c => c.id === Number.parseInt(id))!; if (!res) { logError(`No monster found with id ${id} for stats embed`); } else { @@ -162,7 +162,7 @@ export async function minionStatsEmbed(user: MUser): Promise { const hunterScores = Object.entries(userStats.creature_scores as ItemBank).sort((a, b) => a[1] - b[1]); if (hunterScores.length > 0) { const [id, score] = hunterScores[0]; - const res = creatures.find(c => c.id === parseInt(id))!; + const res = creatures.find(c => c.id === Number.parseInt(id))!; if (res) { otherStats.push([`${res.name}'s Caught`, score]); } diff --git a/src/lib/util/minionStatus.ts b/src/lib/util/minionStatus.ts index 75d92bb327a..431f6e8c1aa 100644 --- a/src/lib/util/minionStatus.ts +++ b/src/lib/util/minionStatus.ts @@ -27,7 +27,7 @@ import Runecraft from '../skilling/skills/runecraft'; import Smithing from '../skilling/skills/smithing'; import { stealables } from '../skilling/skills/thieving/stealables'; import Woodcutting from '../skilling/skills/woodcutting/woodcutting'; -import { +import type { ActivityTaskOptionsWithQuantity, AgilityActivityTaskOptions, AlchingActivityTaskOptions, @@ -72,9 +72,9 @@ import { SmeltingActivityTaskOptions, SmithingActivityTaskOptions, SpecificQuestOptions, + TOAOptions, TheatreOfBloodTaskOptions, TiaraRunecraftActivityTaskOptions, - TOAOptions, WoodcuttingActivityTaskOptions, ZalcanoActivityTaskOptions } from '../types/minions'; @@ -96,14 +96,14 @@ export function minionStatus(user: MUser) { const data = currentTask as MonsterActivityTaskOptions; const monster = killableMonsters.find(mon => mon.id === data.monsterID); - return `${name} is currently killing ${data.quantity}x ${monster!.name}. ${formattedDuration}`; + return `${name} is currently killing ${data.quantity}x ${monster?.name}. ${formattedDuration}`; } case 'GroupMonsterKilling': { const data = currentTask as GroupMonsterActivityTaskOptions; const monster = killableMonsters.find(mon => mon.id === data.monsterID); - return `${name} is currently killing ${data.quantity}x ${monster!.name} with a party of ${ + return `${name} is currently killing ${data.quantity}x ${monster?.name} with a party of ${ data.users.length }. ${formattedDuration}`; } @@ -113,14 +113,14 @@ export function minionStatus(user: MUser) { const clueTier = ClueTiers.find(tier => tier.id === data.clueID); - return `${name} is currently completing ${data.quantity}x ${clueTier!.name} clues. ${formattedDuration}`; + return `${name} is currently completing ${data.quantity}x ${clueTier?.name} clues. ${formattedDuration}`; } case 'Crafting': { const data = currentTask as CraftingActivityTaskOptions; const craftable = Crafting.Craftables.find(item => item.id === data.craftableID); - return `${name} is currently crafting ${data.quantity}x ${craftable!.name}. ${formattedDuration} Your ${ + return `${name} is currently crafting ${data.quantity}x ${craftable?.name}. ${formattedDuration} Your ${ Emoji.Crafting } Crafting level is ${user.skillLevel(SkillsEnum.Crafting)}`; } @@ -130,7 +130,7 @@ export function minionStatus(user: MUser) { const course = Agility.Courses.find(course => course.name === data.courseID); - return `${name} is currently running ${data.quantity}x ${course!.name} laps. ${formattedDuration} Your ${ + return `${name} is currently running ${data.quantity}x ${course?.name} laps. ${formattedDuration} Your ${ Emoji.Agility } Agility level is ${user.skillLevel(SkillsEnum.Agility)}`; } @@ -140,7 +140,7 @@ export function minionStatus(user: MUser) { const cookable = Cooking.Cookables.find(cookable => cookable.id === data.cookableID); - return `${name} is currently cooking ${data.quantity}x ${cookable!.name}. ${formattedDuration} Your ${ + return `${name} is currently cooking ${data.quantity}x ${cookable?.name}. ${formattedDuration} Your ${ Emoji.Cooking } Cooking level is ${user.skillLevel(SkillsEnum.Cooking)}`; } @@ -150,7 +150,7 @@ export function minionStatus(user: MUser) { const fish = Fishing.Fishes.find(fish => fish.id === data.fishID); - return `${name} is currently fishing ${data.quantity}x ${fish!.name}. ${formattedDuration} Your ${ + return `${name} is currently fishing ${data.quantity}x ${fish?.name}. ${formattedDuration} Your ${ Emoji.Fishing } Fishing level is ${user.skillLevel(SkillsEnum.Fishing)}`; } @@ -160,14 +160,14 @@ export function minionStatus(user: MUser) { const ore = Mining.Ores.find(ore => ore.id === data.oreID); - return `${name} is currently mining ${ore!.name}. ${ + return `${name} is currently mining ${ore?.name}. ${ data.fakeDurationMax === data.fakeDurationMin ? formattedDuration : `approximately ${formatDuration( randomVariation(reduceNumByPercent(durationRemaining, 25), 20) - )} **to** ${formatDuration( + )} **to** ${formatDuration( randomVariation(increaseNumByPercent(durationRemaining, 25), 20) - )} remaining.` + )} remaining.` } Your ${Emoji.Mining} Mining level is ${user.skillLevel(SkillsEnum.Mining)}`; } @@ -179,9 +179,9 @@ export function minionStatus(user: MUser) { ? formattedDuration : `approximately ${formatDuration( randomVariation(reduceNumByPercent(durationRemaining, 25), 20) - )} **to** ${formatDuration( + )} **to** ${formatDuration( randomVariation(increaseNumByPercent(durationRemaining, 25), 20) - )} remaining.` + )} remaining.` } Your ${Emoji.Mining} Mining level is ${user.skillLevel(SkillsEnum.Mining)}`; } @@ -190,7 +190,7 @@ export function minionStatus(user: MUser) { const bar = Smithing.Bars.find(bar => bar.id === data.barID); - return `${name} is currently smelting ${data.quantity}x ${bar!.name}. ${formattedDuration} Your ${ + return `${name} is currently smelting ${data.quantity}x ${bar?.name}. ${formattedDuration} Your ${ Emoji.Smithing } Smithing level is ${user.skillLevel(SkillsEnum.Smithing)}`; } @@ -200,7 +200,7 @@ export function minionStatus(user: MUser) { const SmithableItem = Smithing.SmithableItems.find(item => item.id === data.smithedBarID); - return `${name} is currently smithing ${data.quantity}x ${SmithableItem!.name}. ${formattedDuration} Your ${ + return `${name} is currently smithing ${data.quantity}x ${SmithableItem?.name}. ${formattedDuration} Your ${ Emoji.Smithing } Smithing level is ${user.skillLevel(SkillsEnum.Smithing)}`; } @@ -210,7 +210,7 @@ export function minionStatus(user: MUser) { const bones = Prayer.Bones.find(bones => bones.inputId === data.boneID); - return `${name} is currently offering ${data.quantity}x ${bones!.name}. ${formattedDuration} Your ${ + return `${name} is currently offering ${data.quantity}x ${bones?.name}. ${formattedDuration} Your ${ Emoji.Prayer } Prayer level is ${user.skillLevel(SkillsEnum.Prayer)}`; } @@ -220,7 +220,7 @@ export function minionStatus(user: MUser) { const bones = Prayer.Bones.find(bones => bones.inputId === data.boneID); - return `${name} is currently burying ${data.quantity}x ${bones!.name}. ${formattedDuration} Your ${ + return `${name} is currently burying ${data.quantity}x ${bones?.name}. ${formattedDuration} Your ${ Emoji.Prayer } Prayer level is ${user.skillLevel(SkillsEnum.Prayer)}`; } @@ -230,7 +230,7 @@ export function minionStatus(user: MUser) { const ashes = Prayer.Ashes.find(ashes => ashes.inputId === data.ashID); - return `${name} is currently scattering ${data.quantity}x ${ashes!.name}. ${formattedDuration} Your ${ + return `${name} is currently scattering ${data.quantity}x ${ashes?.name}. ${formattedDuration} Your ${ Emoji.Prayer } Prayer level is ${user.skillLevel(SkillsEnum.Prayer)}`; } @@ -240,7 +240,7 @@ export function minionStatus(user: MUser) { const burn = Firemaking.Burnables.find(burn => burn.inputLogs === data.burnableID); - return `${name} is currently lighting ${data.quantity}x ${burn!.name}. ${formattedDuration} Your ${ + return `${name} is currently lighting ${data.quantity}x ${burn?.name}. ${formattedDuration} Your ${ Emoji.Firemaking } Firemaking level is ${user.skillLevel(SkillsEnum.Firemaking)}`; } @@ -254,14 +254,14 @@ export function minionStatus(user: MUser) { const log = Woodcutting.Logs.find(log => log.id === data.logID); - return `${name} is currently chopping ${log!.name}. ${ + return `${name} is currently chopping ${log?.name}. ${ data.fakeDurationMax === data.fakeDurationMin ? formattedDuration : `approximately ${formatDuration( randomVariation(reduceNumByPercent(durationRemaining, 25), 20) - )} **to** ${formatDuration( + )} **to** ${formatDuration( randomVariation(increaseNumByPercent(durationRemaining, 25), 20) - )} remaining.` + )} remaining.` } Your ${Emoji.Woodcutting} Woodcutting level is ${user.skillLevel(SkillsEnum.Woodcutting)}`; } case 'Runecraft': { @@ -270,7 +270,7 @@ export function minionStatus(user: MUser) { const rune = Runecraft.Runes.find(_rune => _rune.id === data.runeID); return `${name} is currently turning ${data.essenceQuantity}x Essence into ${ - rune!.name + rune?.name }. ${formattedDuration} Your ${Emoji.Runecraft} Runecraft level is ${user.skillLevel( SkillsEnum.Runecraft )}`; @@ -280,7 +280,7 @@ export function minionStatus(user: MUser) { const data = currentTask as TiaraRunecraftActivityTaskOptions; const tiara = Runecraft.Tiaras.find(_tiara => _tiara.id === data.tiaraID); - return `${name} is currently crafting ${data.tiaraQuantity} ${tiara!.name}. ${formattedDuration} Your ${ + return `${name} is currently crafting ${data.tiaraQuantity} ${tiara?.name}. ${formattedDuration} Your ${ Emoji.Runecraft } Runecraft level is ${user.skillLevel(SkillsEnum.Runecraft)}`; } @@ -309,7 +309,7 @@ export function minionStatus(user: MUser) { const data = currentTask as HerbloreActivityTaskOptions; const mixable = Herblore.Mixables.find(i => i.item.id === data.mixableID); - return `${name} is currently mixing ${data.quantity}x ${mixable!.item.name}. ${formattedDuration} Your ${ + return `${name} is currently mixing ${data.quantity}x ${mixable?.item.name}. ${formattedDuration} Your ${ Emoji.Herblore } Herblore level is ${user.skillLevel(SkillsEnum.Herblore)}`; } @@ -318,7 +318,7 @@ export function minionStatus(user: MUser) { const barbarianFish = LeapingFish.find(item => item.item.id === data.id); return `${name} is currently cutting ${data.quantity}x ${ - barbarianFish!.item.name + barbarianFish?.item.name }. ${formattedDuration} Your ${Emoji.Cooking} Cooking level is ${user.skillLevel(SkillsEnum.Cooking)}`; } case 'Wintertodt': { @@ -341,16 +341,16 @@ export function minionStatus(user: MUser) { const plants = Farming.Plants.find(plants => plants.name === data.plantsName); - return `${name} is currently farming ${data.quantity}x ${plants!.name}. ${formattedDuration} Your ${ + return `${name} is currently farming ${data.quantity}x ${plants?.name}. ${formattedDuration} Your ${ Emoji.Farming } Farming level is ${user.skillLevel(SkillsEnum.Farming)}.`; } case 'Sawmill': { const data = currentTask as SawmillActivityTaskOptions; - const plank = Planks.find(_plank => _plank.outputItem === data.plankID); + const plank = Planks.find(_plank => _plank.outputItem === data.plankID)!; return `${name} is currently creating ${data.plankQuantity}x ${itemNameFromID( - plank!.outputItem + plank.outputItem )}s. ${formattedDuration}`; } @@ -406,8 +406,8 @@ export function minionStatus(user: MUser) { case 'Pickpocket': { const data = currentTask as PickpocketActivityTaskOptions; const obj = stealables.find(_obj => _obj.id === data.monsterID); - return `${name} is currently ${obj!.type === 'pickpockable' ? 'pickpocketing' : 'stealing'} from ${ - obj!.name + return `${name} is currently ${obj?.type === 'pickpockable' ? 'pickpocketing' : 'stealing'} from ${ + obj?.name } ${data.quantity}x times. ${formattedDuration}`; } @@ -433,9 +433,9 @@ export function minionStatus(user: MUser) { stringMatches(alias, data.creatureName) || stringMatches(alias.split(' ')[0], data.creatureName) ) ); - let crystalImpling = creature?.name === 'Crystal impling'; + const crystalImpling = creature?.name === 'Crystal impling'; return `${name} is currently hunting ${ - crystalImpling ? creature!.name : `${data.quantity}x ${creature!.name}` + crystalImpling ? creature?.name : `${data.quantity}x ${creature?.name}` }. ${formattedDuration}`; } @@ -460,9 +460,9 @@ export function minionStatus(user: MUser) { case 'Butler': { const data = currentTask as ButlerActivityTaskOptions; - const plank = Planks.find(_plank => _plank.outputItem === data.plankID); + const plank = Planks.find(_plank => _plank.outputItem === data.plankID)!; return `${name} is currently creating ${data.plankQuantity}x ${itemNameFromID( - plank!.outputItem + plank.outputItem )}s. ${formattedDuration}`; } @@ -473,13 +473,13 @@ export function minionStatus(user: MUser) { case 'Enchanting': { const data = currentTask as EnchantingActivityTaskOptions; const enchantable = Enchantables.find(i => i.id === data.itemID); - return `${name} is currently enchanting ${data.quantity}x ${enchantable!.name}. ${formattedDuration}`; + return `${name} is currently enchanting ${data.quantity}x ${enchantable?.name}. ${formattedDuration}`; } case 'Casting': { const data = currentTask as CastingActivityTaskOptions; const spell = Castables.find(i => i.id === data.spellID); - return `${name} is currently casting ${data.quantity}x ${spell!.name}. ${formattedDuration}`; + return `${name} is currently casting ${data.quantity}x ${spell?.name}. ${formattedDuration}`; } case 'GloryCharging': { @@ -679,7 +679,7 @@ export function minionStatus(user: MUser) { case 'SpecificQuest': { const data = currentTask as SpecificQuestOptions; return `${name} is currently doing the ${ - quests.find(i => i.id === data.questID)!.name + quests.find(i => i.id === data.questID)?.name }! The trip should take ${formatDuration(durationRemaining)}.`; } case 'Colosseum': { diff --git a/src/lib/util/minionUtils.ts b/src/lib/util/minionUtils.ts index 20b966943bf..8a3f4d24a1f 100644 --- a/src/lib/util/minionUtils.ts +++ b/src/lib/util/minionUtils.ts @@ -1,11 +1,12 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; -import { BaseMessageOptions, escapeMarkdown, time } from 'discord.js'; +import type { BaseMessageOptions } from 'discord.js'; +import { escapeMarkdown, time } from 'discord.js'; import { Time } from 'e'; import { convertXPtoLVL } from 'oldschooljs/dist/util/util'; import { Emoji } from '../constants'; -import { SkillsEnum } from '../skilling/types'; -import { Peak } from './../tickers'; +import type { SkillsEnum } from '../skilling/types'; +import type { Peak } from './../tickers'; import resolveItems from './resolveItems'; export function skillLevel(user: MUser, skill: SkillsEnum) { @@ -81,7 +82,7 @@ export function minionName(user: MUser) { const prefix = isIronman ? Emoji.Ironman : ''; icon ??= Emoji.Minion; - let strPrefix = prefix ? `${prefix} ` : ''; + const strPrefix = prefix ? `${prefix} ` : ''; return name ? `${strPrefix}${icon} **${escapeMarkdown(name)}**` : `${strPrefix}${icon} Your minion`; } diff --git a/src/lib/util/parseStringBank.ts b/src/lib/util/parseStringBank.ts index a15015d459b..e15002bcc9d 100644 --- a/src/lib/util/parseStringBank.ts +++ b/src/lib/util/parseStringBank.ts @@ -1,7 +1,7 @@ import { evalMathExpression } from '@oldschoolgg/toolkit/dist/util/expressionParser'; import { notEmpty } from 'e'; import { Bank, Items } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; import { itemNameMap } from 'oldschooljs/dist/structures/Items'; import { ONE_TRILLION } from '../constants'; @@ -19,27 +19,27 @@ export function parseQuantityAndItem(str = '', inputBank?: Bank): [Item[], numbe const split = str.split(' '); // If we're passed 2 numbers in a row, e.g. '1 1 coal', remove that number and recurse back. - if (!isNaN(Number(split[1])) && split.length > 2) { + if (!Number.isNaN(Number(split[1])) && split.length > 2) { split.splice(1, 1); return parseQuantityAndItem(split.join(' ')); } let [potentialQty, ...potentialName] = split.length === 1 ? ['', [split[0]]] : split; - let lazyItemGet = Items.get(potentialName.join(' ')) ?? Items.get(Number(potentialName.join(' '))); + const lazyItemGet = Items.get(potentialName.join(' ')) ?? Items.get(Number(potentialName.join(' '))); if (str.includes('#') && lazyItemGet && inputBank) { potentialQty = potentialQty.replace('#', inputBank.amount(lazyItemGet.id).toString()); } let parsedQty: number | null = evalMathExpression(potentialQty.replace('x', '')); - if (parsedQty !== null && isNaN(parsedQty)) parsedQty = null; + if (parsedQty !== null && Number.isNaN(parsedQty)) parsedQty = null; const parsedName = parsedQty === null ? str : potentialName.join(' '); let osItems: Item[] = []; const nameAsInt = Number(parsedName); - if (!isNaN(nameAsInt)) { + if (!Number.isNaN(nameAsInt)) { const item = Items.get(nameAsInt); if (item) osItems.push(item); } else { @@ -51,7 +51,7 @@ export function parseQuantityAndItem(str = '', inputBank?: Bank): [Item[], numbe } if (osItems.length === 0) return []; - let quantity = floor(min(ONE_TRILLION, max(0, parsedQty ?? 0))); + const quantity = floor(min(ONE_TRILLION, max(0, parsedQty ?? 0))); return [osItems, quantity]; } @@ -63,10 +63,10 @@ export function parseStringBank(str = '', inputBank?: Bank, noDuplicateItems?: t .split(',') .filter(i => notEmpty(i) && i !== ''); if (split.length === 0) return []; - let items: [Item, number | undefined][] = []; + const items: [Item, number | undefined][] = []; const currentIDs = new Set(); for (let i = 0; i < split.length; i++) { - let [resItems, quantity] = parseQuantityAndItem(split[i], inputBank); + const [resItems, quantity] = parseQuantityAndItem(split[i], inputBank); if (resItems !== undefined) { for (const item of noDuplicateItems ? resItems.slice(0, 1) : resItems) { if (currentIDs.has(item.id)) continue; @@ -92,7 +92,7 @@ export function parseBankFromFlags({ user?: MUser; }): Bank { const newBank = new Bank(); - const maxQuantity = Number(flags.qty) || Infinity; + const maxQuantity = Number(flags.qty) || Number.POSITIVE_INFINITY; // Add filterables const flagsKeys = Object.keys(flags); @@ -148,7 +148,7 @@ export function parseBank({ noDuplicateItems = undefined }: ParseBankOptions): Bank { if (inputStr) { - let _bank = new Bank(); + const _bank = new Bank(); const strItems = parseStringBank(inputStr, inputBank, noDuplicateItems); for (const [item, quantity] of strItems) { if (maxSize && _bank.length >= maxSize) break; @@ -157,8 +157,8 @@ export function parseBank({ !quantity ? inputBank?.amount(item.id) : inputBank === undefined - ? quantity - : Math.max(0, Math.min(quantity, inputBank.amount(item.id) ?? 1)) + ? quantity + : Math.max(0, Math.min(quantity, inputBank.amount(item.id) ?? 1)) ); } return _bank; @@ -178,7 +178,7 @@ export function parseBank({ } function truncateBankToSize(bank: Bank, size: number) { - let newBank = new Bank(); + const newBank = new Bank(); for (const [item, qty] of bank.items()) { if (newBank.length === size) break; @@ -207,12 +207,12 @@ export function parseInputCostBank({ } const baseBank = parseBankFromFlags({ bank: usersBank, flags, excludeItems, user }); - const stringInputBank = Boolean(inputStr) ? parseStringBank(inputStr, baseBank, true) : []; + const stringInputBank = inputStr ? parseStringBank(inputStr, baseBank, true) : []; const bank = new Bank(); for (const [item, qty] of stringInputBank) { const amountOwned = baseBank.amount(item.id); - const maxQuantity = Number(flags.qty) || Infinity; + const maxQuantity = Number(flags.qty) || Number.POSITIVE_INFINITY; bank.add(item.id, Math.min(maxQuantity, amountOwned, qty || amountOwned)); } diff --git a/src/lib/util/repeatStoredTrip.ts b/src/lib/util/repeatStoredTrip.ts index 70074f213a5..15bd678cb50 100644 --- a/src/lib/util/repeatStoredTrip.ts +++ b/src/lib/util/repeatStoredTrip.ts @@ -1,14 +1,16 @@ -import { Activity, activity_type_enum, Prisma } from '@prisma/client'; -import { ButtonBuilder, ButtonInteraction, ButtonStyle } from 'discord.js'; +import type { Activity, Prisma } from '@prisma/client'; +import { activity_type_enum } from '@prisma/client'; +import type { ButtonInteraction } from 'discord.js'; +import { ButtonBuilder, ButtonStyle } from 'discord.js'; import { Time } from 'e'; import { autocompleteMonsters } from '../../mahoji/commands/k'; -import { PvMMethod } from '../constants'; +import type { PvMMethod } from '../constants'; import { SlayerActivityConstants } from '../minions/data/combatConstants'; import { darkAltarRunes } from '../minions/functions/darkAltarCommand'; import { convertStoredActivityToFlatActivity, prisma } from '../settings/prisma'; import { runCommand } from '../settings/settings'; -import { +import type { ActivityTaskOptionsWithQuantity, AgilityActivityTaskOptions, AlchingActivityTaskOptions, @@ -50,15 +52,15 @@ import { ShadesOfMortonOptions, SmeltingActivityTaskOptions, SmithingActivityTaskOptions, + TOAOptions, TempleTrekkingActivityTaskOptions, TheatreOfBloodTaskOptions, TiaraRunecraftActivityTaskOptions, - TOAOptions, WoodcuttingActivityTaskOptions } from '../types/minions'; import { itemNameFromID } from '../util'; import { giantsFoundryAlloys } from './../../mahoji/lib/abstracted_commands/giantsFoundryCommand'; -import { NightmareZoneActivityTaskOptions, UnderwaterAgilityThievingTaskOptions } from './../types/minions'; +import type { NightmareZoneActivityTaskOptions, UnderwaterAgilityThievingTaskOptions } from './../types/minions'; import getOSItem from './getOSItem'; import { deferInteraction } from './interactionReply'; @@ -237,7 +239,10 @@ export const tripHandlers = { }, [activity_type_enum.Cooking]: { commandName: 'cook', - args: (data: CookingActivityTaskOptions) => ({ name: itemNameFromID(data.cookableID), quantity: data.quantity }) + args: (data: CookingActivityTaskOptions) => ({ + name: itemNameFromID(data.cookableID), + quantity: data.quantity + }) }, [activity_type_enum.Crafting]: { commandName: 'craft', @@ -284,7 +289,7 @@ export const tripHandlers = { data.autoFarmed ? { auto_farm: {} - } + } : {} }, [activity_type_enum.FightCaves]: { diff --git a/src/lib/util/smallUtils.ts b/src/lib/util/smallUtils.ts index ae770efc215..339820a49db 100644 --- a/src/lib/util/smallUtils.ts +++ b/src/lib/util/smallUtils.ts @@ -5,11 +5,12 @@ import { miniID, toTitleCase } from '@oldschoolgg/toolkit'; import type { Prisma } from '@prisma/client'; import { AlignmentEnum, AsciiTable3 } from 'ascii-table3'; import deepmerge from 'deepmerge'; -import { ButtonBuilder, ButtonStyle, InteractionReplyOptions, time } from 'discord.js'; -import { clamp, objectEntries, roll, Time } from 'e'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { InteractionReplyOptions } from 'discord.js'; +import { ButtonBuilder, ButtonStyle, time } from 'discord.js'; +import { Time, clamp, objectEntries, roll } from 'e'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { Bank, Items } from 'oldschooljs'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; import { MersenneTwister19937, shuffle } from 'random-js'; import { skillEmoji } from '../data/emojis'; @@ -40,7 +41,7 @@ export function formatItemBoosts(items: ItemBank[]) { const bonusStr = []; for (const [itemID, boostAmount] of itemEntries) { - bonusStr.push(`${boostAmount}% for ${itemNameFromID(parseInt(itemID))}`); + bonusStr.push(`${boostAmount}% for ${itemNameFromID(Number.parseInt(itemID))}`); } if (multiple) { @@ -70,7 +71,7 @@ export function formatDuration(ms: number, short = false) { m: Math.floor(ms / 60_000) % 60, s: Math.floor(ms / 1000) % 60 }; - let nums = Object.entries(short ? shortTime : time).filter(val => val[1] !== 0); + const nums = Object.entries(short ? shortTime : time).filter(val => val[1] !== 0); if (nums.length === 0) return '1 second'; return nums .map(([key, val]) => `${val}${short ? '' : ' '}${key}${val === 1 || short ? '' : 's'}`) @@ -78,7 +79,7 @@ export function formatDuration(ms: number, short = false) { } export function formatSkillRequirements(reqs: Record, emojis = true) { - let arr = []; + const arr = []; for (const [name, num] of objectEntries(reqs)) { arr.push(`${emojis ? ` ${(skillEmoji as any)[name]} ` : ''}**${num}** ${toTitleCase(name)}`); } @@ -124,7 +125,7 @@ export function shuffleRandom(input: number, arr: readonly T[]): T[] { } export function averageBank(bank: Bank, kc: number) { - let newBank = new Bank(); + const newBank = new Bank(); for (const [item, qty] of bank.items()) { newBank.add(item.id, Math.floor(qty / kc)); } @@ -243,10 +244,10 @@ export function calculateSimpleMonsterDeathChance({ }): number { if (!currentKC) currentKC = 1; currentKC = Math.max(1, currentKC); - let baseDeathChance = Math.min(highestDeathChance, (100 * hardness) / steepness); + const baseDeathChance = Math.min(highestDeathChance, (100 * hardness) / steepness); const maxScalingKC = 5 + (75 * hardness) / steepness; - let reductionFactor = Math.min(1, currentKC / maxScalingKC); - let deathChance = baseDeathChance - reductionFactor * (baseDeathChance - lowestDeathChance); + const reductionFactor = Math.min(1, currentKC / maxScalingKC); + const deathChance = baseDeathChance - reductionFactor * (baseDeathChance - lowestDeathChance); return clamp(deathChance, lowestDeathChance, highestDeathChance); } @@ -334,7 +335,7 @@ export function containsBlacklistedWord(str: string): boolean { return false; } -export function ellipsize(str: string, maxLen: number = 2000) { +export function ellipsize(str: string, maxLen = 2000) { if (str.length > maxLen) { return `${str.substring(0, maxLen - 3)}...`; } diff --git a/src/lib/util/statsEmbed.ts b/src/lib/util/statsEmbed.ts index d61269fae43..1acbdc181f8 100644 --- a/src/lib/util/statsEmbed.ts +++ b/src/lib/util/statsEmbed.ts @@ -1,7 +1,7 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; import { EmbedBuilder } from 'discord.js'; -import { Player } from 'oldschooljs'; -import { CluesScore, SkillScore, SkillsScore } from 'oldschooljs/dist/meta/types'; +import type { Player } from 'oldschooljs'; +import type { CluesScore, SkillScore, SkillsScore } from 'oldschooljs/dist/meta/types'; import { skillEmoji } from '../data/emojis'; diff --git a/src/lib/util/taskGroupFromActivity.ts b/src/lib/util/taskGroupFromActivity.ts index 3ae404704c4..ad3ef805a0d 100644 --- a/src/lib/util/taskGroupFromActivity.ts +++ b/src/lib/util/taskGroupFromActivity.ts @@ -1,5 +1,5 @@ import { ActivityGroup } from '../constants'; -import { activity_type_enum } from '.prisma/client'; +import type { activity_type_enum } from '.prisma/client'; export function taskGroupFromActivity(type: activity_type_enum): ActivityGroup { switch (type) { diff --git a/src/lib/util/transactItemsFromBank.ts b/src/lib/util/transactItemsFromBank.ts index 95eb69aea66..d2d39f40382 100644 --- a/src/lib/util/transactItemsFromBank.ts +++ b/src/lib/util/transactItemsFromBank.ts @@ -1,12 +1,12 @@ -import { Prisma } from '@prisma/client'; +import type { Prisma } from '@prisma/client'; import { Bank } from 'oldschooljs'; import { findBingosWithUserParticipating } from '../../mahoji/lib/bingo/BingoManager'; +import { mahojiUserSettingsUpdate } from '../MUser'; import { deduplicateClueScrolls } from '../clues/clueUtils'; import { handleNewCLItems } from '../handleNewCLItems'; -import { mahojiUserSettingsUpdate } from '../MUser'; import { filterLootReplace } from '../slayer/slayerUtil'; -import { ItemBank } from '../types'; +import type { ItemBank } from '../types'; import { logError } from './logError'; import { userQueueFn } from './userQueues'; @@ -40,7 +40,7 @@ export async function transactItemsFromBank({ ...options }: TransactItemsArgs) { let itemsToAdd = options.itemsToAdd ? options.itemsToAdd.clone() : undefined; - let itemsToRemove = options.itemsToRemove ? options.itemsToRemove.clone() : undefined; + const itemsToRemove = options.itemsToRemove ? options.itemsToRemove.clone() : undefined; return userQueueFn(userID, async () => { const settings = await mUserFetch(userID); diff --git a/src/lib/util/updateBankSetting.ts b/src/lib/util/updateBankSetting.ts index c95482c4ce7..fd62cdc9be2 100644 --- a/src/lib/util/updateBankSetting.ts +++ b/src/lib/util/updateBankSetting.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { ItemBank } from '../types'; +import type { ItemBank } from '../types'; import { mahojiClientSettingsFetch, mahojiClientSettingsUpdate } from './clientSettings'; type ClientBankKey = diff --git a/src/lib/util/userEvents.ts b/src/lib/util/userEvents.ts index 95f3660146e..33b03232b9e 100644 --- a/src/lib/util/userEvents.ts +++ b/src/lib/util/userEvents.ts @@ -1,4 +1,5 @@ -import { Prisma, UserEvent, UserEventType, xp_gains_skill_enum } from '@prisma/client'; +import type { Prisma, UserEvent, xp_gains_skill_enum } from '@prisma/client'; +import { UserEventType } from '@prisma/client'; import { MAX_LEVEL, MAX_TOTAL_LEVEL } from '../constants'; import { allCollectionLogsFlat } from '../data/Collections'; diff --git a/src/lib/util/userQueues.ts b/src/lib/util/userQueues.ts index d3b818a04f5..4ff5563283c 100644 --- a/src/lib/util/userQueues.ts +++ b/src/lib/util/userQueues.ts @@ -2,9 +2,9 @@ import PromiseQueue from 'p-queue'; export const userQueues: Map = new Map(); export function getUserUpdateQueue(userID: string) { - let currentQueue = userQueues.get(userID); + const currentQueue = userQueues.get(userID); if (!currentQueue) { - let queue = new PromiseQueue({ concurrency: 1 }); + const queue = new PromiseQueue({ concurrency: 1 }); userQueues.set(userID, queue); return queue; } diff --git a/src/lib/util/webhook.ts b/src/lib/util/webhook.ts index d0f2572fbc3..a7ee5960232 100644 --- a/src/lib/util/webhook.ts +++ b/src/lib/util/webhook.ts @@ -1,13 +1,6 @@ import { splitMessage } from '@oldschoolgg/toolkit'; -import { - AttachmentBuilder, - BaseMessageOptions, - EmbedBuilder, - Message, - PartialGroupDMChannel, - PermissionsBitField, - WebhookClient -} from 'discord.js'; +import type { AttachmentBuilder, BaseMessageOptions, EmbedBuilder, Message } from 'discord.js'; +import { PartialGroupDMChannel, PermissionsBitField, WebhookClient } from 'discord.js'; import PQueue from 'p-queue'; import { production } from '../../config'; @@ -31,8 +24,8 @@ export async function resolveChannel(channelID: string): Promise => { const clueTier = ClueTiers.find(tier => tier.id === clueTierID)!; - let loot = clueTier.table.open(quantity); + const loot = clueTier.table.open(quantity); let mimicNumber = 0; if (clueTier.mimicChance) { for (let i = 0; i < quantity; i++) { diff --git a/src/lib/workers/finish.worker.ts b/src/lib/workers/finish.worker.ts index f656929e7d2..c70221264b8 100644 --- a/src/lib/workers/finish.worker.ts +++ b/src/lib/workers/finish.worker.ts @@ -3,8 +3,8 @@ import '../data/itemAliases'; import { removeFromArr } from 'e'; import { Bank } from 'oldschooljs'; -import getOSItem from '../util/getOSItem'; import type { FinishWorkerArgs, FinishWorkerReturn } from '.'; +import getOSItem from '../util/getOSItem'; if (global.prisma) { throw new Error('Prisma is loaded in the finish worker!'); @@ -20,7 +20,7 @@ export default async ({ name, tertiaries }: FinishWorkerArgs): FinishWorkerRetur } } const cost = new Bank(); - let loot = new Bank(); + const loot = new Bank(); const kcBank = new Bank(); let kc = 0; const maxAttempts = val.maxAttempts ?? 100_000; diff --git a/src/lib/workers/index.ts b/src/lib/workers/index.ts index 2ff2b4d969e..f97450589cc 100644 --- a/src/lib/workers/index.ts +++ b/src/lib/workers/index.ts @@ -1,10 +1,10 @@ import { resolve } from 'node:path'; -import { Bank } from 'oldschooljs'; +import type { Bank } from 'oldschooljs'; import Piscina from 'piscina'; import { production } from '../../config'; -import { ItemBank } from '../types'; +import type { ItemBank } from '../types'; export interface CasketWorkerArgs { clueTierID: number; diff --git a/src/lib/workers/kill.worker.ts b/src/lib/workers/kill.worker.ts index 02d6a01f659..cfa6d7d395e 100644 --- a/src/lib/workers/kill.worker.ts +++ b/src/lib/workers/kill.worker.ts @@ -3,12 +3,12 @@ import '../data/itemAliases'; import { stringMatches } from '@oldschoolgg/toolkit'; import { Bank, Misc, Monsters } from 'oldschooljs'; +import type { KillWorkerArgs, KillWorkerReturn } from '.'; import killableMonsters from '../minions/data/killableMonsters'; import { handleNexKills } from '../simulation/nex'; import { simulatedKillables } from '../simulation/simulatedKillables'; import { calcDropRatesFromBank } from '../util/calcDropRatesFromBank'; import resolveItems from '../util/resolveItems'; -import type { KillWorkerArgs, KillWorkerReturn } from '.'; if (global.prisma) { throw new Error('Prisma is loaded in the kill worker!'); @@ -27,9 +27,7 @@ export default async ({ if (osjsMonster) { if (quantity > limit) { return { - error: - `The quantity you gave exceeds your limit of ${limit.toLocaleString()}! ` + - '*You can increase your limit by up to 1 million by becoming a patron at ' + error: `The quantity you gave exceeds your limit of ${limit.toLocaleString()}! *You can increase your limit by up to 1 million by becoming a patron at ` }; } @@ -44,7 +42,7 @@ export default async ({ }; const killableMonster = killableMonsters.find(mon => mon.id === osjsMonster.id); - if (killableMonster && killableMonster.specialLoot) { + if (killableMonster?.specialLoot) { killableMonster.specialLoot({ ownedItems: result.bank, loot: result.bank, quantity }); } @@ -55,9 +53,7 @@ export default async ({ if (simulatedKillable) { if (quantity > limit) { return { - error: - `The quantity you gave exceeds your limit of ${limit.toLocaleString()}! ` + - '*You can increase your limit by up to 1 million by becoming a patron at ' + error: `The quantity you gave exceeds your limit of ${limit.toLocaleString()}! *You can increase your limit by up to 1 million by becoming a patron at ` }; } @@ -65,7 +61,7 @@ export default async ({ } if (['nightmare', 'the nightmare'].some(alias => stringMatches(alias, bossName))) { - let bank = new Bank(); + const bank = new Bank(); if (quantity > 10_000) { return { error: 'I can only kill a maximum of 10k nightmares a time!' }; } diff --git a/src/mahoji/commands/activities.ts b/src/mahoji/commands/activities.ts index 2d0bb2c30c6..ae9f1aedbfe 100644 --- a/src/mahoji/commands/activities.ts +++ b/src/mahoji/commands/activities.ts @@ -1,10 +1,9 @@ -import { User } from 'discord.js'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { User } from 'discord.js'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; -import { - UNDERWATER_AGILITY_THIEVING_TRAINING_SKILL, - UnderwaterAgilityThievingTrainingSkill -} from '../../lib/constants'; +import type { UnderwaterAgilityThievingTrainingSkill } from '../../lib/constants'; +import { UNDERWATER_AGILITY_THIEVING_TRAINING_SKILL } from '../../lib/constants'; import { Planks } from '../../lib/minions/data/planks'; import Potions from '../../lib/minions/data/potions'; import { quests } from '../../lib/minions/data/quests'; @@ -23,7 +22,7 @@ import { championsChallengeCommand } from '../lib/abstracted_commands/championsC import { chargeGloriesCommand } from '../lib/abstracted_commands/chargeGloriesCommand'; import { chargeWealthCommand } from '../lib/abstracted_commands/chargeWealthCommand'; import { chompyHuntClaimCommand, chompyHuntCommand } from '../lib/abstracted_commands/chompyHuntCommand'; -import { collectables, collectCommand } from '../lib/abstracted_commands/collectCommand'; +import { collectCommand, collectables } from '../lib/abstracted_commands/collectCommand'; import { decantCommand } from '../lib/abstracted_commands/decantCommand'; import { driftNetCommand } from '../lib/abstracted_commands/driftNetCommand'; import { enchantCommand } from '../lib/abstracted_commands/enchantCommand'; @@ -37,7 +36,7 @@ import { scatterCommand } from '../lib/abstracted_commands/scatterCommand'; import { underwaterAgilityThievingCommand } from '../lib/abstracted_commands/underwaterCommand'; import { warriorsGuildCommand } from '../lib/abstracted_commands/warriorsGuildCommand'; import { ownedItemOption } from '../lib/mahojiCommandOptions'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const activitiesCommand: OSBMahojiCommand = { name: 'activities', diff --git a/src/mahoji/commands/admin.ts b/src/mahoji/commands/admin.ts index a7268472303..36a3d604330 100644 --- a/src/mahoji/commands/admin.ts +++ b/src/mahoji/commands/admin.ts @@ -1,37 +1,39 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ import { execSync } from 'node:child_process'; import { inspect } from 'node:util'; -import { ClientStorage, economy_transaction_type } from '@prisma/client'; +import type { ClientStorage } from '@prisma/client'; +import { economy_transaction_type } from '@prisma/client'; import { Stopwatch } from '@sapphire/stopwatch'; import { isThenable } from '@sentry/utils'; -import { AttachmentBuilder, codeBlock, escapeCodeBlock, InteractionReplyOptions } from 'discord.js'; -import { calcWhatPercent, noOp, notEmpty, randArrItem, sleep, Time, uniqueArr } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; -import { MahojiUserOption } from 'mahoji/dist/lib/types'; +import type { InteractionReplyOptions } from 'discord.js'; +import { AttachmentBuilder, codeBlock, escapeCodeBlock } from 'discord.js'; +import { Time, calcWhatPercent, noOp, notEmpty, randArrItem, sleep, uniqueArr } from 'e'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { MahojiUserOption } from 'mahoji/dist/lib/types'; import { bulkUpdateCommands } from 'mahoji/dist/lib/util'; import { Bank } from 'oldschooljs'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; -import { ADMIN_IDS, OWNER_IDS, production, SupportServer } from '../../config'; +import { ADMIN_IDS, OWNER_IDS, SupportServer, production } from '../../config'; +import { mahojiUserSettingsUpdate } from '../../lib/MUser'; import { BLACKLISTED_GUILDS, BLACKLISTED_USERS, syncBlacklists } from '../../lib/blacklists'; import { - badges, + BOT_TYPE, BadgesEnum, BitField, BitFieldData, - BOT_TYPE, Channel, DISABLED_COMMANDS, - globalConfig, - META_CONSTANTS + META_CONSTANTS, + badges, + globalConfig } from '../../lib/constants'; import { economyLog } from '../../lib/economyLogs'; import { generateGearImage } from '../../lib/gear/functions/generateGearImage'; -import { GearSetup } from '../../lib/gear/types'; +import type { GearSetup } from '../../lib/gear/types'; import { GrandExchange } from '../../lib/grandExchange'; -import { mahojiUserSettingsUpdate } from '../../lib/MUser'; import { patreonTask } from '../../lib/patreon'; import { runRolesTask } from '../../lib/rolesTask'; import { countUsersWithItemInCl, prisma } from '../../lib/settings/prisma'; @@ -61,7 +63,8 @@ import { sendToChannelID } from '../../lib/util/webhook'; import { Cooldowns } from '../lib/Cooldowns'; import { syncCustomPrices } from '../lib/events'; import { itemOption } from '../lib/mahojiCommandOptions'; -import { allAbstractCommands, OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; +import { allAbstractCommands } from '../lib/util'; import { mahojiUsersSettingsFetch } from '../mahojiSettings'; export const gifs = [ @@ -78,9 +81,8 @@ async function unsafeEval({ userID, code }: { userID: string; code: string }) { let asyncTime = '?'; let result = null; let thenable = false; - // eslint-disable-next-line @typescript-eslint/init-declarations try { - // eslint-disable-next-line no-eval + // biome-ignore lint/security/noGlobalEval: result = eval(code); syncTime = stopwatch.toString(); if (isThenable(result)) { @@ -92,7 +94,7 @@ async function unsafeEval({ userID, code }: { userID: string; code: string }) { } catch (error: any) { if (!syncTime) syncTime = stopwatch.toString(); if (thenable && !asyncTime) asyncTime = stopwatch.toString(); - if (error && error.stack) logError(error); + if (error?.stack) logError(error); result = error; } @@ -127,9 +129,7 @@ async function unsafeEval({ userID, code }: { userID: string; code: string }) { } async function allEquippedPets() { - const pets = await prisma.$queryRawUnsafe< - { pet: number; qty: number }[] - >(`SELECT "minion.equippedPet" AS pet, COUNT("minion.equippedPet")::int AS qty + const pets = await prisma.$queryRawUnsafe<{ pet: number; qty: number }[]>(`SELECT "minion.equippedPet" AS pet, COUNT("minion.equippedPet")::int AS qty FROM users WHERE "minion.equippedPet" IS NOT NULL GROUP BY "minion.equippedPet" @@ -174,11 +174,11 @@ async function getAllTradedItems(giveUniques = false) { } }); - let total = new Bank(); + const total = new Bank(); if (giveUniques) { for (const trans of economyTrans) { - let bank = new Bank().add(trans.items_received as ItemBank).add(trans.items_sent as ItemBank); + const bank = new Bank().add(trans.items_received as ItemBank).add(trans.items_sent as ItemBank); for (const item of bank.items()) { total.add(item[0].id); @@ -228,10 +228,9 @@ AND ("gear.melee" IS NOT NULL OR const bank = new Bank(); for (const user of res) { for (const gear of Object.values(user) - .map(i => (i === null ? [] : Object.values(i))) - .flat() + .flatMap(i => (i === null ? [] : Object.values(i))) .filter(notEmpty)) { - let item = getItem(gear.item); + const item = getItem(gear.item); if (item) { bank.add(gear.item, gear.quantity); } @@ -282,9 +281,7 @@ AND ("gear.melee" IS NOT NULL OR name: 'Economy Bank', run: async () => { const [blowpipeRes, totalGP, result] = await prisma.$transaction([ - prisma.$queryRawUnsafe< - { scales: number; dart: number; qty: number }[] - >(`SELECT (blowpipe->>'scales')::int AS scales, (blowpipe->>'dartID')::int AS dart, (blowpipe->>'dartQuantity')::int AS qty + prisma.$queryRawUnsafe<{ scales: number; dart: number; qty: number }[]>(`SELECT (blowpipe->>'scales')::int AS scales, (blowpipe->>'dartID')::int AS dart, (blowpipe->>'dartQuantity')::int AS qty FROM users WHERE blowpipe iS NOT NULL and (blowpipe->>'dartQuantity')::int != 0;`), prisma.$queryRawUnsafe<{ sum: number }[]>('SELECT SUM("GP") FROM users;'), @@ -312,7 +309,9 @@ WHERE blowpipe iS NOT NULL and (blowpipe->>'dartQuantity')::int != 0;`), return { files: [ (await makeBankImage({ bank: economyBank })).file, - new AttachmentBuilder(Buffer.from(JSON.stringify(economyBank.bank, null, 4)), { name: 'bank.json' }) + new AttachmentBuilder(Buffer.from(JSON.stringify(economyBank.bank, null, 4)), { + name: 'bank.json' + }) ] }; } @@ -437,18 +436,14 @@ LIMIT { name: 'Sell GP Sources', run: async () => { - const result = await prisma.$queryRawUnsafe< - { item_id: number; gp: number }[] - >(`select item_id, sum(gp_received) as gp + const result = await prisma.$queryRawUnsafe<{ item_id: number; gp: number }[]>(`select item_id, sum(gp_received) as gp from bot_item_sell group by item_id order by gp desc limit 80; `); - const totalGPGivenOut = await prisma.$queryRawUnsafe< - { total_gp_given_out: number }[] - >(`select sum(gp_received) as total_gp_given_out + const totalGPGivenOut = await prisma.$queryRawUnsafe<{ total_gp_given_out: number }[]>(`select sum(gp_received) as total_gp_given_out from bot_item_sell;`); return { @@ -967,7 +962,7 @@ export const adminCommand: OSBMahojiCommand = { .map(entry => `**${entry[0]}:** ${entry[1]?.name}`) .join('\n'); } - const bit = parseInt(bitEntry[0]); + const bit = Number.parseInt(bitEntry[0]); if ( !bit || @@ -1016,7 +1011,7 @@ ${META_CONSTANTS.RENDERED_STR}` } if (options.shut_down) { globalClient.isShuttingDown = true; - let timer = production ? Time.Second * 30 : Time.Second * 5; + const timer = production ? Time.Second * 30 : Time.Second * 5; await interactionReply(interaction, { content: `Shutting down in ${dateFm(new Date(Date.now() + timer))}.` }); @@ -1207,7 +1202,7 @@ There are ${await countUsersWithItemInCl(item.id, isIron)} ${isIron ? 'ironmen' const marketValueLoot = Math.round(loot.value()); const ratio = marketValueLoot / marketValueCost; - if (!marketValueCost || !marketValueLoot || ratio === Infinity) continue; + if (!marketValueCost || !marketValueLoot || ratio === Number.POSITIVE_INFINITY) continue; str += `${[ res.id, diff --git a/src/mahoji/commands/ask.ts b/src/mahoji/commands/ask.ts index 6bb25be7c67..ac0c99a4a41 100644 --- a/src/mahoji/commands/ask.ts +++ b/src/mahoji/commands/ask.ts @@ -1,7 +1,8 @@ import { randArrItem } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const askCommand: OSBMahojiCommand = { name: 'ask', diff --git a/src/mahoji/commands/bank.ts b/src/mahoji/commands/bank.ts index 917c3055655..4cdb8ecf6de 100644 --- a/src/mahoji/commands/bank.ts +++ b/src/mahoji/commands/bank.ts @@ -1,19 +1,22 @@ -import { codeBlock, EmbedBuilder } from 'discord.js'; +import { EmbedBuilder, codeBlock } from 'discord.js'; import { chunk } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; -import { Bank } from 'oldschooljs'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; +import type { Bank } from 'oldschooljs'; -import { BankFlag, bankFlags } from '../../lib/bankImage'; -import { Emoji, PerkTier } from '../../lib/constants'; -import { Flags } from '../../lib/minions/types'; import { PaginatedMessage } from '../../lib/PaginatedMessage'; -import { BankSortMethod, BankSortMethods } from '../../lib/sorts'; +import type { BankFlag } from '../../lib/bankImage'; +import { bankFlags } from '../../lib/bankImage'; +import { Emoji, PerkTier } from '../../lib/constants'; +import type { Flags } from '../../lib/minions/types'; +import type { BankSortMethod } from '../../lib/sorts'; +import { BankSortMethods } from '../../lib/sorts'; import { channelIsSendable, makePaginatedMessage } from '../../lib/util'; import { deferInteraction } from '../../lib/util/interactionReply'; import { makeBankImage } from '../../lib/util/makeBankImage'; import { parseBank } from '../../lib/util/parseStringBank'; import { filterOption, itemOption } from '../lib/mahojiCommandOptions'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; const bankFormats = ['json', 'text_paged', 'text_full'] as const; const bankItemsPerPage = 10; @@ -191,7 +194,7 @@ export const bankCommand: OSBMahojiCommand = { return `${codeBlock('json', json)}`; } - let flags: Flags = { + const flags: Flags = { page: options.page - 1 }; if (options.sort) flags.sort = options.sort; diff --git a/src/mahoji/commands/bingo.ts b/src/mahoji/commands/bingo.ts index 9dd60dbe4e4..e36b76438a0 100644 --- a/src/mahoji/commands/bingo.ts +++ b/src/mahoji/commands/bingo.ts @@ -1,12 +1,14 @@ import { formatOrdinal, mentionCommand, stringMatches, truncateString } from '@oldschoolgg/toolkit'; -import { Prisma } from '@prisma/client'; -import { bold, ChatInputCommandInteraction, User, userMention } from 'discord.js'; -import { chunk, noOp, notEmpty, Time, uniqueArr } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; -import { MahojiUserOption } from 'mahoji/dist/lib/types'; +import type { Prisma } from '@prisma/client'; +import type { ChatInputCommandInteraction, User } from 'discord.js'; +import { bold, userMention } from 'discord.js'; +import { Time, chunk, noOp, notEmpty, uniqueArr } from 'e'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { MahojiUserOption } from 'mahoji/dist/lib/types'; import { Bank } from 'oldschooljs'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; import { production } from '../../config'; import { BLACKLISTED_USERS } from '../../lib/blacklists'; @@ -18,9 +20,10 @@ import { getItem } from '../../lib/util/getOSItem'; import { handleMahojiConfirmation } from '../../lib/util/handleMahojiConfirmation'; import { parseBank } from '../../lib/util/parseStringBank'; import { BingoManager, BingoTrophies } from '../lib/bingo/BingoManager'; -import { generateTileName, getAllTileItems, isGlobalTile, StoredBingoTile } from '../lib/bingo/bingoUtil'; +import type { StoredBingoTile } from '../lib/bingo/bingoUtil'; +import { generateTileName, getAllTileItems, isGlobalTile } from '../lib/bingo/bingoUtil'; import { globalBingoTiles } from '../lib/bingo/globalTiles'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { doMenu, getPos } from './leaderboard'; const bingoAutocomplete = async (value: string, user: User) => { @@ -244,10 +247,10 @@ async function getBingoFromUserInput(input: string) { const where = Number.isNaN(Number(input)) ? { title: input - } + } : { id: Number(input) - }; + }; const bingo = await prisma.bingo.findFirst({ where }); @@ -495,8 +498,7 @@ export const bingoCommand: OSBMahojiCommand = { } }); return bingos - .map(b => new BingoManager(b).bingoTiles) - .flat() + .flatMap(b => new BingoManager(b).bingoTiles) .filter(b => b.name.toLowerCase().includes(value.toLowerCase())) .map(b => ({ name: truncateString(b.name, 100), @@ -589,7 +591,7 @@ export const bingoCommand: OSBMahojiCommand = { if (options.items) { const bingoID = Number(options.items.bingo); - if (isNaN(bingoID)) { + if (Number.isNaN(bingoID)) { return 'Invalid bingo.'; } const bingoParticipant = await prisma.bingoParticipant.findFirst({ @@ -827,7 +829,7 @@ The creator of the bingo (${userMention( if (bingo.isActive()) { return "You can't add tiles to a bingo after it has started."; } - const globalTile = globalBingoTiles.find(t => stringMatches(t.id, options.manage_bingo!.add_tile)); + const globalTile = globalBingoTiles.find(t => stringMatches(t.id, options.manage_bingo?.add_tile)); let tileToAdd: StoredBingoTile | null = null; if (globalTile) { tileToAdd = { global: globalTile.id }; @@ -858,7 +860,7 @@ Example: \`add_tile:Coal|Trout|Egg\` is a tile where you have to receive a coal return "You can't remove tiles to a bingo after it has started."; } let newTiles = [...bingo.rawBingoTiles]; - const globalTile = globalBingoTiles.find(t => stringMatches(t.id, options.manage_bingo!.remove_tile)); + const globalTile = globalBingoTiles.find(t => stringMatches(t.id, options.manage_bingo?.remove_tile)); let tileName = ''; if (globalTile) { newTiles = newTiles.filter( @@ -867,11 +869,11 @@ Example: \`add_tile:Coal|Trout|Egg\` is a tile where you have to receive a coal tileName = generateTileName(globalTile); } else { const tileToRemove = newTiles.find( - t => md5sum(generateTileName(t)) === options.manage_bingo!.remove_tile + t => md5sum(generateTileName(t)) === options.manage_bingo?.remove_tile ); if (tileToRemove) { newTiles = newTiles.filter( - t => md5sum(generateTileName(t)) !== options.manage_bingo!.remove_tile! + t => md5sum(generateTileName(t)) !== options.manage_bingo?.remove_tile! ); tileName = generateTileName(tileToRemove); } @@ -899,7 +901,7 @@ Example: \`add_tile:Coal|Trout|Egg\` is a tile where you have to receive a coal if (options.manage_bingo.add_extra_gp) { const amount = Number(options.manage_bingo.add_extra_gp); - if (isNaN(amount) || amount < 1) { + if (Number.isNaN(amount) || amount < 1) { return 'Invalid amount.'; } @@ -1003,9 +1005,7 @@ ${yourTeam.bingoTableStr}` } } - let str = `**${bingo.title}** ${teams.length} teams, ${toKMB( - await bingo.countTotalGPInPrizePool() - )} GP Prize Pool + const str = `**${bingo.title}** ${teams.length} teams, ${toKMB(await bingo.countTotalGPInPrizePool())} GP Prize Pool **Start:** ${dateFm(bingo.startDate)} **Finish:** ${dateFm(bingo.endDate)} ${progressString} diff --git a/src/mahoji/commands/bossrecords.ts b/src/mahoji/commands/bossrecords.ts index ca41241f3b5..5dfdfbba826 100644 --- a/src/mahoji/commands/bossrecords.ts +++ b/src/mahoji/commands/bossrecords.ts @@ -1,15 +1,17 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; -import { EmbedBuilder, MessageEditOptions } from 'discord.js'; +import type { MessageEditOptions } from 'discord.js'; +import { EmbedBuilder } from 'discord.js'; import { chunk } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Hiscores } from 'oldschooljs'; import { bossNameMap } from 'oldschooljs/dist/constants'; -import { BossRecords } from 'oldschooljs/dist/meta/types'; +import type { BossRecords } from 'oldschooljs/dist/meta/types'; import pets from '../../lib/data/pets'; import { channelIsSendable, makePaginatedMessage } from '../../lib/util'; import { deferInteraction } from '../../lib/util/interactionReply'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; // Emojis for bosses with no pets const miscEmojis = { @@ -29,7 +31,7 @@ function getEmojiForBoss(key: MiscEmojisKeys | string) { return miscEmojis[key as MiscEmojisKeys]; } - const pet = pets.find(_pet => _pet.bossKeys && _pet.bossKeys.includes(key)); + const pet = pets.find(_pet => _pet.bossKeys?.includes(key)); if (pet) return pet.emoji; } diff --git a/src/mahoji/commands/botleagues.ts b/src/mahoji/commands/botleagues.ts index 5a0f6e09c29..bcc19d36c26 100644 --- a/src/mahoji/commands/botleagues.ts +++ b/src/mahoji/commands/botleagues.ts @@ -1,6 +1,7 @@ import { stringMatches } from '@oldschoolgg/toolkit'; import { chunk } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; import { leagueBuyables } from '../../lib/data/leaguesBuyables'; @@ -8,7 +9,7 @@ import { roboChimpUserFetch } from '../../lib/roboChimp'; import { getUsername } from '../../lib/util'; import getOSItem from '../../lib/util/getOSItem'; import { deferInteraction } from '../../lib/util/interactionReply'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { doMenu } from './leaderboard'; const leaguesTrophiesBuyables = [ @@ -118,14 +119,14 @@ ${leaguesTrophiesBuyables } if (options.buy_reward) { - let quantity = 1; + const quantity = 1; const pointsCostMultiplier = 150; const item = leagueBuyables.find(i => stringMatches(i.item.name, options.buy_reward?.item)); if (!item) return "That's not a valid item."; - let baseCost = item.price * pointsCostMultiplier; + const baseCost = item.price * pointsCostMultiplier; const cost = quantity * baseCost; if (roboChimpUser.leagues_points_balance_osb < cost) { return `You don't have enough League Points to purchase this. You need ${cost}, but you have ${roboChimpUser.leagues_points_balance_osb}.`; diff --git a/src/mahoji/commands/bs.ts b/src/mahoji/commands/bs.ts index 548ae45b1f2..d5dd983220f 100644 --- a/src/mahoji/commands/bs.ts +++ b/src/mahoji/commands/bs.ts @@ -1,6 +1,7 @@ -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { bankCommand } from './bank'; const bankFormats = ['json', 'text_paged', 'text_full'] as const; diff --git a/src/mahoji/commands/build.ts b/src/mahoji/commands/build.ts index 3e9703bdb0b..71fbc53eea7 100644 --- a/src/mahoji/commands/build.ts +++ b/src/mahoji/commands/build.ts @@ -1,17 +1,18 @@ import { stringMatches } from '@oldschoolgg/toolkit'; -import { User } from 'discord.js'; -import { round, Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { User } from 'discord.js'; +import { Time, round } from 'e'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; import Constructables from '../../lib/skilling/skills/construction/constructables'; -import { Skills } from '../../lib/types'; -import { ConstructionActivityTaskOptions } from '../../lib/types/minions'; +import type { Skills } from '../../lib/types'; +import type { ConstructionActivityTaskOptions } from '../../lib/types/minions'; import { formatDuration, hasSkillReqs } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../lib/util/calcMaxTripLength'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; const ds2Requirements: Skills = { magic: 75, @@ -96,7 +97,7 @@ export const buildCommand: OSBMahojiCommand = { } } - let timeToBuildSingleObject = object.ticks * 300; + const timeToBuildSingleObject = object.ticks * 300; const [plank, planksQtyCost] = object.input; diff --git a/src/mahoji/commands/buy.ts b/src/mahoji/commands/buy.ts index c92e1971656..66cda556c1e 100644 --- a/src/mahoji/commands/buy.ts +++ b/src/mahoji/commands/buy.ts @@ -1,11 +1,12 @@ import { bold } from 'discord.js'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; import Buyables from '../../lib/data/buyables/buyables'; import { quests } from '../../lib/minions/data/quests'; -import { getMinigameScore, Minigames } from '../../lib/settings/minigames'; +import { Minigames, getMinigameScore } from '../../lib/settings/minigames'; import { prisma } from '../../lib/settings/prisma'; import { MUserStats } from '../../lib/structures/MUserStats'; import { formatSkillRequirements, itemNameFromID, stringMatches } from '../../lib/util'; @@ -14,7 +15,7 @@ import { deferInteraction } from '../../lib/util/interactionReply'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; import { buyFossilIslandNotes } from '../lib/abstracted_commands/buyFossilIslandNotes'; import { buyKitten } from '../lib/abstracted_commands/buyKitten'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { mahojiParseNumber, multipleUserStatsBankUpdate } from '../mahojiSettings'; const allBuyablesAutocomplete = [...Buyables, { name: 'Kitten' }, { name: 'Fossil Island Notes' }]; @@ -53,9 +54,7 @@ export const buyCommand: OSBMahojiCommand = { } const buyable = Buyables.find( - item => - stringMatches(name, item.name) || - (item.aliases && item.aliases.some(alias => stringMatches(alias, name))) + item => stringMatches(name, item.name) || item.aliases?.some(alias => stringMatches(alias, name)) ); if (!buyable) return "That's not a valid item you can buy."; @@ -110,7 +109,7 @@ export const buyCommand: OSBMahojiCommand = { } if (kc < req) { return `You need ${req} KC in ${ - Minigames.find(i => i.column === key)!.name + Minigames.find(i => i.column === key)?.name } to buy this, you only have ${kc} KC.`; } } @@ -126,12 +125,12 @@ export const buyCommand: OSBMahojiCommand = { return `You don't have the required items to purchase this. You need: ${totalCost}.`; } - let singleOutput: Bank = + const singleOutput: Bank = buyable.outputItems === undefined ? new Bank().add(buyable.name) : buyable.outputItems instanceof Bank - ? buyable.outputItems - : buyable.outputItems(user); + ? buyable.outputItems + : buyable.outputItems(user); const outItems = singleOutput.clone().multiply(quantity); diff --git a/src/mahoji/commands/ca.ts b/src/mahoji/commands/ca.ts index eb1c298a1c4..c0b9439b30b 100644 --- a/src/mahoji/commands/ca.ts +++ b/src/mahoji/commands/ca.ts @@ -1,19 +1,20 @@ import { mentionCommand } from '@oldschoolgg/toolkit'; import { calcWhatPercent, objectEntries } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; import { buildCombatAchievementsResult } from '../../lib/combat_achievements/caUtils'; +import type { CombatAchievement } from '../../lib/combat_achievements/combatAchievements'; import { + CombatAchievements, allCAMonsterNames, allCombatAchievementTasks, caToPlayerString, - CombatAchievement, - CombatAchievements, nextCATier } from '../../lib/combat_achievements/combatAchievements'; import { deferInteraction } from '../../lib/util/interactionReply'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; const viewTypes = ['all', 'incomplete', 'complete'] as const; @@ -94,9 +95,7 @@ export const caCommand: OSBMahojiCommand = { const completedTasks: CombatAchievement[] = []; for (const task of tasksToCheck) { - if ('rng' in task) { - continue; - } else if ('requirements' in task) { + if ('requirements' in task) { const { hasAll } = await task.requirements.check(user); if (hasAll) { completedTasks.push(task); @@ -135,12 +134,12 @@ export const caCommand: OSBMahojiCommand = { } if (options.view) { - let selectedMonster = options.view.name; - let tasksView: CAViewType = options.view.type !== undefined ? options.view.type : 'all'; + const selectedMonster = options.view.name; + const tasksView: CAViewType = options.view.type !== undefined ? options.view.type : 'all'; if (selectedMonster) { const tasksForSelectedMonster = allCombatAchievementTasks.filter( - task => task.monster.toLowerCase() === selectedMonster!.toLowerCase() + task => task.monster.toLowerCase() === selectedMonster?.toLowerCase() ); if (tasksForSelectedMonster.length === 0) diff --git a/src/mahoji/commands/casket.ts b/src/mahoji/commands/casket.ts index ff9b12346a5..9bf4ac41c8d 100644 --- a/src/mahoji/commands/casket.ts +++ b/src/mahoji/commands/casket.ts @@ -1,4 +1,5 @@ -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; import { ClueTiers } from '../../lib/clues/clueTiers'; @@ -6,7 +7,7 @@ import { PerkTier } from '../../lib/constants'; import { deferInteraction } from '../../lib/util/interactionReply'; import { makeBankImage } from '../../lib/util/makeBankImage'; import { Workers } from '../../lib/workers'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; function determineLimit(user: MUser) { const perkTier = user.perkTier(); diff --git a/src/mahoji/commands/choose.ts b/src/mahoji/commands/choose.ts index dc7607bf0d9..7b77aac6200 100644 --- a/src/mahoji/commands/choose.ts +++ b/src/mahoji/commands/choose.ts @@ -1,8 +1,9 @@ import { inlineCode } from 'discord.js'; import { randArrItem } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const chooseCommand: OSBMahojiCommand = { name: 'choose', diff --git a/src/mahoji/commands/chop.ts b/src/mahoji/commands/chop.ts index 0c49f5d8071..9fca9050491 100644 --- a/src/mahoji/commands/chop.ts +++ b/src/mahoji/commands/chop.ts @@ -1,16 +1,18 @@ import { increaseNumByPercent, reduceNumByPercent } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; -import { TwitcherGloves, TWITCHERS_GLOVES } from '../../lib/constants'; +import type { TwitcherGloves } from '../../lib/constants'; +import { TWITCHERS_GLOVES } from '../../lib/constants'; import { determineWoodcuttingTime } from '../../lib/skilling/functions/determineWoodcuttingTime'; import Woodcutting from '../../lib/skilling/skills/woodcutting/woodcutting'; -import { WoodcuttingActivityTaskOptions } from '../../lib/types/minions'; +import type { WoodcuttingActivityTaskOptions } from '../../lib/types/minions'; import { formatDuration, itemNameFromID, randomVariation, stringMatches } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import itemID from '../../lib/util/itemID'; import { minionName } from '../../lib/util/minionUtils'; import resolveItems from '../../lib/util/resolveItems'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const axes = [ { @@ -209,7 +211,7 @@ export const chopCommand: OSBMahojiCommand = { } // Calculate the time it takes to chop specific quantity or as many as possible - let [timeToChop, newQuantity] = determineWoodcuttingTime({ + const [timeToChop, newQuantity] = determineWoodcuttingTime({ quantity, user, log, diff --git a/src/mahoji/commands/cl.ts b/src/mahoji/commands/cl.ts index b24fefd4965..394a25d20ff 100644 --- a/src/mahoji/commands/cl.ts +++ b/src/mahoji/commands/cl.ts @@ -1,15 +1,12 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; -import { - clImageGenerator, - CollectionLogFlags, - CollectionLogType, - collectionLogTypes -} from '../../lib/collectionLogTask'; +import type { CollectionLogType } from '../../lib/collectionLogTask'; +import { CollectionLogFlags, clImageGenerator, collectionLogTypes } from '../../lib/collectionLogTask'; import { allCollectionLogs } from '../../lib/data/Collections'; import { fetchStatsForCL } from '../../lib/util'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const collectionLogCommand: OSBMahojiCommand = { name: 'cl', @@ -47,21 +44,30 @@ export const collectionLogCommand: OSBMahojiCommand = { name: 'type', description: 'The type of log you want to see.', required: false, - choices: collectionLogTypes.map(i => ({ name: `${toTitleCase(i.name)} (${i.description})`, value: i.name })) + choices: collectionLogTypes.map(i => ({ + name: `${toTitleCase(i.name)} (${i.description})`, + value: i.name + })) }, { type: ApplicationCommandOptionType.String, name: 'flag', description: 'The flag you want to pass.', required: false, - choices: CollectionLogFlags.map(i => ({ name: `${toTitleCase(i.name)} (${i.description})`, value: i.name })) + choices: CollectionLogFlags.map(i => ({ + name: `${toTitleCase(i.name)} (${i.description})`, + value: i.name + })) }, { type: ApplicationCommandOptionType.String, name: 'flag_extra', description: 'An additional flag you want to pass.', required: false, - choices: CollectionLogFlags.map(i => ({ name: `${toTitleCase(i.name)} (${i.description})`, value: i.name })) + choices: CollectionLogFlags.map(i => ({ + name: `${toTitleCase(i.name)} (${i.description})`, + value: i.name + })) }, { type: ApplicationCommandOptionType.Boolean, @@ -81,7 +87,7 @@ export const collectionLogCommand: OSBMahojiCommand = { all?: boolean; }>) => { const user = await mUserFetch(userID); - let flags: Record = {}; + const flags: Record = {}; if (options.flag) flags[options.flag] = options.flag; if (options.flag_extra) flags[options.flag_extra] = options.flag_extra; if (options.all) flags.all = 'all'; diff --git a/src/mahoji/commands/claim.ts b/src/mahoji/commands/claim.ts index b34a0311bc8..0ec0c9df8b8 100644 --- a/src/mahoji/commands/claim.ts +++ b/src/mahoji/commands/claim.ts @@ -1,14 +1,15 @@ -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; -import { BitField, BSO_MAX_TOTAL_LEVEL, Channel } from '../../lib/constants'; +import { BSO_MAX_TOTAL_LEVEL, BitField, Channel } from '../../lib/constants'; import { getReclaimableItemsOfUser } from '../../lib/reclaimableItems'; import { roboChimpUserFetch } from '../../lib/roboChimp'; import { prisma } from '../../lib/settings/prisma'; import { dateFm, stringMatches } from '../../lib/util'; import getOSItem from '../../lib/util/getOSItem'; import { sendToChannelID } from '../../lib/util/webhook'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; const claimables = [ { diff --git a/src/mahoji/commands/clue.ts b/src/mahoji/commands/clue.ts index f6198b2d3f2..97922fef167 100644 --- a/src/mahoji/commands/clue.ts +++ b/src/mahoji/commands/clue.ts @@ -1,18 +1,20 @@ -import { notEmpty, randInt, Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import { Time, notEmpty, randInt } from 'e'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; -import { Item, ItemBank } from 'oldschooljs/dist/meta/types'; +import type { Item, ItemBank } from 'oldschooljs/dist/meta/types'; -import { ClueTier, ClueTiers } from '../../lib/clues/clueTiers'; +import type { ClueTier } from '../../lib/clues/clueTiers'; +import { ClueTiers } from '../../lib/clues/clueTiers'; import { allOpenables, getOpenableLoot } from '../../lib/openables'; import { getPOHObject } from '../../lib/poh'; -import { ClueActivityTaskOptions } from '../../lib/types/minions'; +import type { ClueActivityTaskOptions } from '../../lib/types/minions'; import { formatDuration, isWeekend, stringMatches } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../lib/util/calcMaxTripLength'; import getOSItem, { getItem } from '../../lib/util/getOSItem'; import { getPOH } from '../lib/abstracted_commands/pohCommand'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { addToOpenablesScores, getMahojiBank, mahojiUsersSettingsFetch } from '../mahojiSettings'; function reducedClueTime(clueTier: ClueTier, score: number) { @@ -336,7 +338,7 @@ export const clueCommand: OSBMahojiCommand = { const maxCanDo = Math.floor(maxTripLength / timeToFinish); const bankedImplings = user.bank.amount(clueImpling.id); let openedImplings = 0; - let implingLoot = new Bank(); + const implingLoot = new Bank(); while (implingClues + bankedClues < maxCanDo && openedImplings < bankedImplings) { const impLoot = await getOpenableLoot({ openable: implingJarOpenable, user, quantity: 1 }); implingLoot.add(impLoot.bank); diff --git a/src/mahoji/commands/clues.ts b/src/mahoji/commands/clues.ts index 3f6d29bec55..97f71c99c4f 100644 --- a/src/mahoji/commands/clues.ts +++ b/src/mahoji/commands/clues.ts @@ -1,9 +1,10 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; import { EmbedBuilder } from 'discord.js'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Hiscores } from 'oldschooljs'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const cluesCommand: OSBMahojiCommand = { name: 'clues', diff --git a/src/mahoji/commands/config.ts b/src/mahoji/commands/config.ts index bead1d4ee36..4563f3e9866 100644 --- a/src/mahoji/commands/config.ts +++ b/src/mahoji/commands/config.ts @@ -1,20 +1,13 @@ import { hasBanMemberPerms, miniID } from '@oldschoolgg/toolkit'; -import { activity_type_enum } from '@prisma/client'; -import { - bold, - ChatInputCommandInteraction, - EmbedBuilder, - Guild, - HexColorString, - inlineCode, - resolveColor, - User -} from 'discord.js'; -import { clamp, removeFromArr, Time, uniqueArr } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { activity_type_enum } from '@prisma/client'; +import type { ChatInputCommandInteraction, Guild, HexColorString, User } from 'discord.js'; +import { EmbedBuilder, bold, inlineCode, resolveColor } from 'discord.js'; +import { Time, clamp, removeFromArr, uniqueArr } from 'e'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { Bank } from 'oldschooljs'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; import { production } from '../../config'; import { BitField, ItemIconPacks, ParsedCustomEmojiWithGroups, PerkTier } from '../../lib/constants'; @@ -33,7 +26,8 @@ import { makeBankImage } from '../../lib/util/makeBankImage'; import { parseBank } from '../../lib/util/parseStringBank'; import { mahojiGuildSettingsFetch, mahojiGuildSettingsUpdate } from '../guildSettings'; import { itemOption } from '../lib/mahojiCommandOptions'; -import { allAbstractCommands, OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; +import { allAbstractCommands } from '../lib/util'; import { mahojiUsersSettingsFetch, patronMsg } from '../mahojiSettings'; interface UserConfigToggle { @@ -214,7 +208,7 @@ async function favItemConfig( }.`; if (!item) return currentItems; if (itemToAdd) { - let limit = (user.perkTier() + 1) * 100; + const limit = (user.perkTier() + 1) * 100; if (currentFavorites.length >= limit) { return `You can't favorite anymore items, you can favorite a maximum of ${limit}.`; } @@ -270,7 +264,7 @@ async function favAlchConfig( if (!item.highalch) return "That item isn't alchable."; - const action = Boolean(removeItem) ? 'remove' : 'add'; + const action = removeItem ? 'remove' : 'add'; const isAlreadyFav = currentFavorites.includes(item.id); if (action === 'remove') { @@ -531,23 +525,18 @@ async function handleCombatOptions(user: MUser, command: 'add' | 'remove' | 'lis const settings = await mahojiUsersSettingsFetch(user.id, { combat_options: true }); if (!command || (command && command === 'list')) { // List enabled combat options: - const cbOpts = settings.combat_options.map(o => CombatOptionsArray.find(coa => coa!.id === o)!.name); + const cbOpts = settings.combat_options.map(o => CombatOptionsArray.find(coa => coa?.id === o)?.name); return `Your current combat options are:\n${cbOpts.join('\n')}\n\nTry: \`/config user combat_options help\``; } if (command === 'help' || !option || !['add', 'remove'].includes(command)) { - return ( - 'Changes your Combat Options. Usage: `/config user combat_options [add/remove/list] always cannon`' + - `\n\nList of possible options:\n${CombatOptionsArray.map(coa => `**${coa!.name}**: ${coa!.desc}`).join( - '\n' - )}` - ); + return `Changes your Combat Options. Usage: \`/config user combat_options [add/remove/list] always cannon\`\n\nList of possible options:\n${CombatOptionsArray.map( + coa => `**${coa?.name}**: ${coa?.desc}` + ).join('\n')}`; } const newcbopt = CombatOptionsArray.find( - item => - stringMatches(option, item.name) || - (item.aliases && item.aliases.some(alias => stringMatches(alias, option))) + item => stringMatches(option, item.name) || item.aliases?.some(alias => stringMatches(alias, option)) ); if (!newcbopt) return 'Cannot find matching option. Try: `/config user combat_options help`'; @@ -646,7 +635,6 @@ export async function pinTripCommand( if (emoji) { const res = ParsedCustomEmojiWithGroups.exec(emoji); if (!res || !res[3]) return "That's not a valid emoji."; - // eslint-disable-next-line prefer-destructuring emoji = res[3]; const cachedEmoji = globalClient.emojis.cache.get(emoji); @@ -1070,7 +1058,7 @@ export const configCommand: OSBMahojiCommand = { description: 'The trip you want to pin.', required: false, autocomplete: async (_, user) => { - let res = await prisma.$queryRawUnsafe< + const res = await prisma.$queryRawUnsafe< { type: activity_type_enum; data: object; id: number; finish_date: string }[] >(` SELECT DISTINCT ON (activity.type) activity.type, activity.data, activity.id, activity.finish_date @@ -1125,7 +1113,10 @@ LIMIT 20; name: 'name', description: 'The icon pack you want to use.', required: true, - choices: ['Default', ...ItemIconPacks.map(i => i.name)].map(i => ({ name: i, value: i })) + choices: ['Default', ...ItemIconPacks.map(i => i.name)].map(i => ({ + name: i, + value: i + })) } ] } diff --git a/src/mahoji/commands/cook.ts b/src/mahoji/commands/cook.ts index c7afc9a0b70..69a5d28c8d4 100644 --- a/src/mahoji/commands/cook.ts +++ b/src/mahoji/commands/cook.ts @@ -1,16 +1,17 @@ import { Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; import { KourendKebosDiary, userhasDiaryTier } from '../../lib/diaries'; import Cooking, { Cookables } from '../../lib/skilling/skills/cooking/cooking'; import LeapingFish from '../../lib/skilling/skills/cooking/leapingFish'; -import { CookingActivityTaskOptions } from '../../lib/types/minions'; +import type { CookingActivityTaskOptions } from '../../lib/types/minions'; import { formatDuration, itemID, stringMatches } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../lib/util/calcMaxTripLength'; import { cutLeapingFishCommand } from '../lib/abstracted_commands/cutLeapingFishCommand'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const cookCommand: OSBMahojiCommand = { name: 'cook', diff --git a/src/mahoji/commands/craft.ts b/src/mahoji/commands/craft.ts index 77ca3eba5a7..c0b52ac4e1f 100644 --- a/src/mahoji/commands/craft.ts +++ b/src/mahoji/commands/craft.ts @@ -1,16 +1,17 @@ import { stringMatches } from '@oldschoolgg/toolkit'; import { Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { FaladorDiary, userhasDiaryTier } from '../../lib/diaries'; import { Craftables } from '../../lib/skilling/skills/crafting/craftables'; import { SkillsEnum } from '../../lib/skilling/types'; -import { CraftingActivityTaskOptions } from '../../lib/types/minions'; +import type { CraftingActivityTaskOptions } from '../../lib/types/minions'; import { formatDuration } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../lib/util/calcMaxTripLength'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const craftCommand: OSBMahojiCommand = { name: 'craft', diff --git a/src/mahoji/commands/create.ts b/src/mahoji/commands/create.ts index 30b97f4ce6f..86e61c6dda9 100644 --- a/src/mahoji/commands/create.ts +++ b/src/mahoji/commands/create.ts @@ -1,20 +1,21 @@ -import { readFileSync } from 'fs'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import { readFileSync } from 'node:fs'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; import Createables from '../../lib/data/createables'; -import { SkillsEnum } from '../../lib/skilling/types'; -import { SlayerTaskUnlocksEnum } from '../../lib/slayer/slayerUnlocks'; +import type { SkillsEnum } from '../../lib/skilling/types'; +import type { SlayerTaskUnlocksEnum } from '../../lib/slayer/slayerUnlocks'; import { hasSlayerUnlock } from '../../lib/slayer/slayerUtil'; import { stringMatches } from '../../lib/util'; import { handleMahojiConfirmation } from '../../lib/util/handleMahojiConfirmation'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { userStatsBankUpdate } from '../mahojiSettings'; const creatablesTable = readFileSync('./src/lib/data/creatablesTable.txt', 'utf8'); -let content = 'Theses are the items that you can create:'; +const content = 'Theses are the items that you can create:'; const allCreatablesTable = { content, files: [{ attachment: Buffer.from(creatablesTable), name: 'Creatables.txt' }] @@ -92,7 +93,7 @@ export const createCommand: OSBMahojiCommand = { } } if (createableItem.requiredSlayerUnlocks) { - let mySlayerUnlocks = user.user.slayer_unlocks; + const mySlayerUnlocks = user.user.slayer_unlocks; const { success, errors } = hasSlayerUnlock( mySlayerUnlocks as SlayerTaskUnlocksEnum[], diff --git a/src/mahoji/commands/data.ts b/src/mahoji/commands/data.ts index d9253fe85f9..951da008fe9 100644 --- a/src/mahoji/commands/data.ts +++ b/src/mahoji/commands/data.ts @@ -1,8 +1,9 @@ -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { deferInteraction } from '../../lib/util/interactionReply'; import { dataPoints, statsCommand } from '../lib/abstracted_commands/statCommand'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const dataCommand: OSBMahojiCommand = { name: 'data', diff --git a/src/mahoji/commands/drop.ts b/src/mahoji/commands/drop.ts index 38403911285..b9518aa87ad 100644 --- a/src/mahoji/commands/drop.ts +++ b/src/mahoji/commands/drop.ts @@ -1,4 +1,5 @@ -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { ClueTiers } from '../../lib/clues/clueTiers'; import { ellipsize, itemNameFromID, returnStringOrFile } from '../../lib/util'; @@ -6,7 +7,7 @@ import { handleMahojiConfirmation } from '../../lib/util/handleMahojiConfirmatio import { parseBank } from '../../lib/util/parseStringBank'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; import { filterOption } from '../lib/mahojiCommandOptions'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const dropCommand: OSBMahojiCommand = { name: 'drop', @@ -56,7 +57,7 @@ export const dropCommand: OSBMahojiCommand = { } const favs = user.user.favoriteItems; - let itemsToDoubleCheck = [ + const itemsToDoubleCheck = [ ...favs, ...ClueTiers.map(c => [c.id, c.scrollID]), ...user.bank diff --git a/src/mahoji/commands/drycalc.ts b/src/mahoji/commands/drycalc.ts index 3df7d44c802..5a73502c6c4 100644 --- a/src/mahoji/commands/drycalc.ts +++ b/src/mahoji/commands/drycalc.ts @@ -1,7 +1,8 @@ import { round } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const dryCalcCommand: OSBMahojiCommand = { name: 'drycalc', diff --git a/src/mahoji/commands/fake.ts b/src/mahoji/commands/fake.ts index 296a6d1108f..7e5d1aa500b 100644 --- a/src/mahoji/commands/fake.ts +++ b/src/mahoji/commands/fake.ts @@ -1,9 +1,11 @@ -import { Canvas, SKRSContext2D } from '@napi-rs/canvas'; +import type { SKRSContext2D } from '@napi-rs/canvas'; +import { Canvas } from '@napi-rs/canvas'; import { randInt } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { loadAndCacheLocalImage, measureTextWidth } from '../../lib/util/canvasUtil'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; const bg = loadAndCacheLocalImage('./src/lib/resources/images/tob-bg.png'); diff --git a/src/mahoji/commands/fakepm.ts b/src/mahoji/commands/fakepm.ts index 70b89a4a6cd..9b6f2b7d6ae 100644 --- a/src/mahoji/commands/fakepm.ts +++ b/src/mahoji/commands/fakepm.ts @@ -1,8 +1,9 @@ import { Canvas } from '@napi-rs/canvas'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { loadAndCacheLocalImage } from '../../lib/util/canvasUtil'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; const bg = loadAndCacheLocalImage('./src/lib/resources/images/pm-bg.png'); diff --git a/src/mahoji/commands/farming.ts b/src/mahoji/commands/farming.ts index eb4468e557d..76e379b6b21 100644 --- a/src/mahoji/commands/farming.ts +++ b/src/mahoji/commands/farming.ts @@ -1,10 +1,13 @@ -import { AutoFarmFilterEnum, CropUpgradeType } from '@prisma/client'; -import { User } from 'discord.js'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CropUpgradeType } from '@prisma/client'; +import { AutoFarmFilterEnum } from '@prisma/client'; +import type { User } from 'discord.js'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import TitheFarmBuyables from '../../lib/data/buyables/titheFarmBuyables'; import { superCompostables } from '../../lib/data/filterables'; -import { ContractOption, ContractOptions } from '../../lib/minions/farming/types'; +import type { ContractOption } from '../../lib/minions/farming/types'; +import { ContractOptions } from '../../lib/minions/farming/types'; import { autoFarm } from '../../lib/minions/functions/autoFarm'; import { getFarmingInfo } from '../../lib/skilling/functions/getFarmingInfo'; import Farming, { CompostTiers } from '../../lib/skilling/skills/farming'; @@ -14,7 +17,7 @@ import { deferInteraction } from '../../lib/util/interactionReply'; import { compostBinCommand, farmingPlantCommand, harvestCommand } from '../lib/abstracted_commands/farmingCommand'; import { farmingContractCommand } from '../lib/abstracted_commands/farmingContractCommand'; import { titheFarmCommand, titheFarmShopCommand } from '../lib/abstracted_commands/titheFarmCommand'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; const autoFarmFilterTexts: Record = { AllFarm: 'All crops will be farmed with the highest available seed', @@ -214,7 +217,7 @@ export const farmingCommand: OSBMahojiCommand = { return `'Always pay farmers' is now ${!isEnabled ? 'enabled' : 'disabled'}.`; } if (options.default_compost) { - const tier = CompostTiers.find(i => stringMatches(i.name, options.default_compost!.compost)); + const tier = CompostTiers.find(i => stringMatches(i.name, options.default_compost?.compost)); if (!tier) return 'Invalid tier.'; await klasaUser.update({ minion_defaultCompostToUse: tier.name @@ -223,7 +226,7 @@ export const farmingCommand: OSBMahojiCommand = { } if (options.auto_farm_filter) { const autoFarmFilterString = Object.values(AutoFarmFilterEnum).find( - i => i === options.auto_farm_filter!.auto_farm_filter_data + i => i === options.auto_farm_filter?.auto_farm_filter_data ); if (!autoFarmFilterString) return 'Invalid auto farm filter.'; const autoFarmFilter = autoFarmFilterString as AutoFarmFilterEnum; diff --git a/src/mahoji/commands/finish.ts b/src/mahoji/commands/finish.ts index c870a5aea35..98b771dc80b 100644 --- a/src/mahoji/commands/finish.ts +++ b/src/mahoji/commands/finish.ts @@ -1,6 +1,7 @@ import { AttachmentBuilder } from 'discord.js'; import { notEmpty } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; import { finishables } from '../../lib/finishables'; @@ -9,7 +10,7 @@ import { stringMatches } from '../../lib/util'; import { deferInteraction } from '../../lib/util/interactionReply'; import { makeBankImage } from '../../lib/util/makeBankImage'; import { Workers } from '../../lib/workers'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const finishCommand: OSBMahojiCommand = { name: 'finish', diff --git a/src/mahoji/commands/fish.ts b/src/mahoji/commands/fish.ts index cfcc40caa18..3e0f101e1be 100644 --- a/src/mahoji/commands/fish.ts +++ b/src/mahoji/commands/fish.ts @@ -1,16 +1,17 @@ import { stringMatches } from '@oldschoolgg/toolkit'; -import { calcPercentOfNum, randInt, Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import { Time, calcPercentOfNum, randInt } from 'e'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; import TzTokJad from 'oldschooljs/dist/simulation/monsters/special/TzTokJad'; import Fishing from '../../lib/skilling/skills/fishing'; import { SkillsEnum } from '../../lib/skilling/types'; -import { FishingActivityTaskOptions } from '../../lib/types/minions'; +import type { FishingActivityTaskOptions } from '../../lib/types/minions'; import { formatDuration, itemID, itemNameFromID } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../lib/util/calcMaxTripLength'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const fishCommand: OSBMahojiCommand = { name: 'fish', @@ -76,7 +77,7 @@ export const fishCommand: OSBMahojiCommand = { return 'You are not worthy JalYt. Before you can fish Infernal Eels, you need to have defeated the mighty TzTok-Jad!'; } } - const anglerOutfit = Object.keys(Fishing.anglerItems).map(i => itemNameFromID(parseInt(i))); + const anglerOutfit = Object.keys(Fishing.anglerItems).map(i => itemNameFromID(Number.parseInt(i))); if (fish.name === 'Minnow' && anglerOutfit.some(test => !user.hasEquippedOrInBank(test!))) { return 'You need to own the Angler Outfit to fish for Minnows.'; } diff --git a/src/mahoji/commands/fletch.ts b/src/mahoji/commands/fletch.ts index 49d56057ddf..02b1450fff2 100644 --- a/src/mahoji/commands/fletch.ts +++ b/src/mahoji/commands/fletch.ts @@ -1,16 +1,17 @@ import { stringMatches } from '@oldschoolgg/toolkit'; import { Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import Fletching from '../../lib/skilling/skills/fletching'; import { Fletchables } from '../../lib/skilling/skills/fletching/fletchables'; -import { SlayerTaskUnlocksEnum } from '../../lib/slayer/slayerUnlocks'; +import type { SlayerTaskUnlocksEnum } from '../../lib/slayer/slayerUnlocks'; import { hasSlayerUnlock } from '../../lib/slayer/slayerUtil'; -import { FletchingActivityTaskOptions } from '../../lib/types/minions'; +import type { FletchingActivityTaskOptions } from '../../lib/types/minions'; import { formatDuration } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../lib/util/calcMaxTripLength'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const fletchCommand: OSBMahojiCommand = { name: 'fletch', @@ -58,7 +59,7 @@ export const fletchCommand: OSBMahojiCommand = { } if (fletchable.requiredSlayerUnlocks) { - let mySlayerUnlocks = user.user.slayer_unlocks; + const mySlayerUnlocks = user.user.slayer_unlocks; const { success, errors } = hasSlayerUnlock( mySlayerUnlocks as SlayerTaskUnlocksEnum[], diff --git a/src/mahoji/commands/gamble.ts b/src/mahoji/commands/gamble.ts index 2dec4e4bca3..d98997e1794 100644 --- a/src/mahoji/commands/gamble.ts +++ b/src/mahoji/commands/gamble.ts @@ -1,6 +1,7 @@ import { randArrItem } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; -import { MahojiUserOption } from 'mahoji/dist/lib/types'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; +import type { MahojiUserOption } from 'mahoji/dist/lib/types'; import { Bank } from 'oldschooljs'; import { BitField } from '../../lib/constants'; @@ -13,7 +14,7 @@ import { duelCommand } from '../lib/abstracted_commands/duelCommand'; import { hotColdCommand } from '../lib/abstracted_commands/hotColdCommand'; import { luckyPickCommand } from '../lib/abstracted_commands/luckyPickCommand'; import { slotsCommand } from '../lib/abstracted_commands/slotsCommand'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const gambleCommand: OSBMahojiCommand = { name: 'gamble', @@ -268,7 +269,7 @@ export const gambleCommand: OSBMahojiCommand = { type: 'gri' } }); - let debug = new Bank(); + const debug = new Bank(); for (const t of bank) debug.add(t[0].id); return `You gave ${qty.toLocaleString()}x ${item.name} to ${recipientuser.usernameOrMention}.`; diff --git a/src/mahoji/commands/ge.ts b/src/mahoji/commands/ge.ts index 4467b00f3fd..6ea6c32a5c6 100644 --- a/src/mahoji/commands/ge.ts +++ b/src/mahoji/commands/ge.ts @@ -1,13 +1,13 @@ import { getItem } from '@oldschoolgg/toolkit'; import { evalMathExpression } from '@oldschoolgg/toolkit/dist/util/expressionParser'; -import { GEListing, GETransaction } from '@prisma/client'; +import type { GEListing, GETransaction } from '@prisma/client'; import { ApplicationCommandOptionType } from 'discord.js'; import { sumArr, uniqueArr } from 'e'; -import { CommandRunOptions } from 'mahoji'; -import { CommandOption } from 'mahoji/dist/lib/types'; +import type { CommandRunOptions } from 'mahoji'; +import type { CommandOption } from 'mahoji/dist/lib/types'; import { PerkTier } from '../../lib/constants'; -import { createGECancelButton, GrandExchange } from '../../lib/grandExchange'; +import { GrandExchange, createGECancelButton } from '../../lib/grandExchange'; import { marketPricemap } from '../../lib/marketPrices'; import { prisma } from '../../lib/settings/prisma'; import { formatDuration, itemNameFromID, makeComponents, returnStringOrFile, toKMB } from '../../lib/util'; @@ -18,7 +18,7 @@ import { deferInteraction } from '../../lib/util/interactionReply'; import itemIsTradeable from '../../lib/util/itemIsTradeable'; import { cancelGEListingCommand } from '../lib/abstracted_commands/cancelGEListingCommand'; import { itemOption, tradeableItemArr } from '../lib/mahojiCommandOptions'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export type GEListingWithTransactions = GEListing & { buyTransactions: GETransaction[]; @@ -116,7 +116,7 @@ export const geCommand: OSBMahojiCommand = { value: itemID.toString() })); } - let res = tradeableItemArr.filter(i => i.key.includes(value.toLowerCase())); + const res = tradeableItemArr.filter(i => i.key.includes(value.toLowerCase())); return res.map(i => ({ name: `${i.name}`, value: i.id.toString() })); } }, @@ -205,7 +205,7 @@ export const geCommand: OSBMahojiCommand = { const listings = Array.from(marketPricemap.values()); return listings .filter(i => - !input ? true : itemNameFromID(i.itemID)!.toLowerCase().includes(input.toLowerCase()) + !input ? true : itemNameFromID(i.itemID)?.toLowerCase().includes(input.toLowerCase()) ) .map(l => ({ name: `${itemNameFromID(l.itemID)!}`, @@ -311,9 +311,7 @@ The next buy limit reset is at: ${GrandExchange.getInterval().nextResetStr}, it **G.E Slots You Can Use:** ${slots} **Taxes you have paid:** ${(totalGPYourSales._sum.total_tax_paid ?? 0).toLocaleString()} GP -**Total Tax Paid on your sales AND purchases:** ${( - totalGPYourTransactions._sum.total_tax_paid ?? 0 - ).toLocaleString()} GP`; +**Total Tax Paid on your sales AND purchases:** ${(totalGPYourTransactions._sum.total_tax_paid ?? 0).toLocaleString()} GP`; } if (options.my_listings) { @@ -458,7 +456,7 @@ ORDER BY itemName: (options.buy?.item ?? options.sell?.item)!, price: parseNumber((options.buy?.price ?? options.sell?.price)!), quantity: parseNumber((options.buy?.quantity ?? options.sell?.quantity)!), - type: Boolean(options.buy) ? 'Buy' : 'Sell' + type: options.buy ? 'Buy' : 'Sell' }); if ('error' in result) return result.error; @@ -473,9 +471,9 @@ ORDER BY if (options.buy) { const result = await GrandExchange.createListing({ user, - itemName: options.buy!.item, - price: parseNumber(options.buy!.price), - quantity: parseNumber(options.buy!.quantity), + itemName: options.buy?.item, + price: parseNumber(options.buy?.price), + quantity: parseNumber(options.buy?.quantity), type: 'Buy' }); diff --git a/src/mahoji/commands/gear.ts b/src/mahoji/commands/gear.ts index c0089fbf401..ea9756821f6 100644 --- a/src/mahoji/commands/gear.ts +++ b/src/mahoji/commands/gear.ts @@ -1,9 +1,11 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { gearValidationChecks } from '../../lib/constants'; import { allPetIDs } from '../../lib/data/CollectionsExport'; -import { GearSetupType, GearSetupTypes, GearStat } from '../../lib/gear/types'; +import type { GearSetupType } from '../../lib/gear/types'; +import { GearSetupTypes, GearStat } from '../../lib/gear/types'; import { equipPet } from '../../lib/minions/functions/equipPet'; import { unequipPet } from '../../lib/minions/functions/unequipPet'; import { itemNameFromID } from '../../lib/util'; @@ -15,7 +17,7 @@ import { gearViewCommand } from '../lib/abstracted_commands/gearCommands'; import { equippedItemOption, gearPresetOption, gearSetupOption, ownedItemOption } from '../lib/mahojiCommandOptions'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { getMahojiBank, mahojiUsersSettingsFetch } from '../mahojiSettings'; export const gearCommand: OSBMahojiCommand = { diff --git a/src/mahoji/commands/gearpresets.ts b/src/mahoji/commands/gearpresets.ts index 7e412741565..a6dd038a3b8 100644 --- a/src/mahoji/commands/gearpresets.ts +++ b/src/mahoji/commands/gearpresets.ts @@ -1,20 +1,22 @@ -import { GearPreset } from '@prisma/client'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; -import { CommandOption } from 'mahoji/dist/lib/types'; +import type { GearPreset } from '@prisma/client'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; +import type { CommandOption } from 'mahoji/dist/lib/types'; import { EquipmentSlot } from 'oldschooljs/dist/meta/types'; import { production } from '../../config'; import { ParsedCustomEmojiWithGroups } from '../../lib/constants'; import { generateGearImage } from '../../lib/gear/functions/generateGearImage'; -import { GearSetup, GearSetupType, GearSetupTypes } from '../../lib/gear/types'; +import type { GearSetup, GearSetupType } from '../../lib/gear/types'; +import { GearSetupTypes } from '../../lib/gear/types'; import { prisma } from '../../lib/settings/prisma'; -import { defaultGear, Gear, globalPresets } from '../../lib/structures/Gear'; +import { Gear, defaultGear, globalPresets } from '../../lib/structures/Gear'; import { cleanString, isValidGearSetup, isValidNickname, stringMatches } from '../../lib/util'; import { emojiServers } from '../../lib/util/cachedUserIDs'; import { getItem } from '../../lib/util/getOSItem'; import { gearEquipCommand } from '../lib/abstracted_commands/gearCommands'; import { allEquippableItems, gearPresetOption, gearSetupOption } from '../lib/mahojiCommandOptions'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; function maxPresets(user: MUser) { return user.perkTier() * 2 + 4; @@ -23,8 +25,8 @@ function maxPresets(user: MUser) { type InputGear = Partial>; type ParsedInputGear = Partial>; function parseInputGear(inputGear: InputGear) { - let gear: ParsedInputGear = {}; - let remove: EquipmentSlot[] = []; + const gear: ParsedInputGear = {}; + const remove: EquipmentSlot[] = []; for (const [key, val] of Object.entries(inputGear)) { if (val?.toLowerCase() === 'none') { remove.push(key as EquipmentSlot); @@ -63,13 +65,15 @@ export function gearPresetToGear(preset: GearPreset): GearSetup { export async function createOrEditGearSetup( user: MUser, setupToCopy: GearSetupType | undefined, - name = '', + name: string, isUpdating: boolean, gearInput: InputGear, emoji: string | undefined, pinned_setup: GearSetupType | 'reset' | undefined ) { - name = cleanString(name).toLowerCase(); + if (name) { + name = cleanString(name).toLowerCase(); + } if (name.length > 24) return 'Gear preset names must be less than 25 characters long.'; if (!name) return "You didn't supply a name."; if (!isUpdating && !isValidNickname(name)) { @@ -109,7 +113,6 @@ export async function createOrEditGearSetup( if (emoji) { const res = ParsedCustomEmojiWithGroups.exec(emoji); if (!res || !res[3]) return "That's not a valid emoji."; - // eslint-disable-next-line prefer-destructuring emoji = res[3]; const cachedEmoji = globalClient.emojis.cache.get(emoji); diff --git a/src/mahoji/commands/gift.ts b/src/mahoji/commands/gift.ts index 9263a397a2b..c4f92a47d6d 100644 --- a/src/mahoji/commands/gift.ts +++ b/src/mahoji/commands/gift.ts @@ -1,19 +1,20 @@ import { mentionCommand, miniID, truncateString } from '@oldschoolgg/toolkit'; import { GiftBoxStatus } from '@prisma/client'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; -import { MahojiUserOption } from 'mahoji/dist/lib/types'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; +import type { MahojiUserOption } from 'mahoji/dist/lib/types'; import { Bank } from 'oldschooljs'; import { BLACKLISTED_USERS } from '../../lib/blacklists'; import { BOT_TYPE } from '../../lib/constants'; import { prisma } from '../../lib/settings/prisma'; -import { ItemBank } from '../../lib/types'; +import type { ItemBank } from '../../lib/types'; import { containsBlacklistedWord, isValidNickname } from '../../lib/util'; import { handleMahojiConfirmation } from '../../lib/util/handleMahojiConfirmation'; import itemIsTradeable from '../../lib/util/itemIsTradeable'; import { makeBankImage } from '../../lib/util/makeBankImage'; import { parseBank } from '../../lib/util/parseStringBank'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const giftCommand: OSBMahojiCommand = { name: 'gift', diff --git a/src/mahoji/commands/giveaway.ts b/src/mahoji/commands/giveaway.ts index 45426451e3f..faccd9ca445 100644 --- a/src/mahoji/commands/giveaway.ts +++ b/src/mahoji/commands/giveaway.ts @@ -1,9 +1,9 @@ -import { Giveaway } from '@prisma/client'; +import type { Giveaway } from '@prisma/client'; import { Duration } from '@sapphire/time-utilities'; +import type { BaseMessageOptions } from 'discord.js'; import { ActionRowBuilder, AttachmentBuilder, - BaseMessageOptions, ButtonBuilder, ButtonStyle, ChannelType, @@ -11,10 +11,11 @@ import { messageLink, time } from 'discord.js'; -import { randInt, Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import { Time, randInt } from 'e'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; import { Emoji, patronFeatures } from '../../lib/constants'; import { marketPriceOfBank } from '../../lib/marketPrices'; @@ -27,7 +28,7 @@ import { logError } from '../../lib/util/logError'; import { makeBankImage } from '../../lib/util/makeBankImage'; import { parseBank } from '../../lib/util/parseStringBank'; import { filterOption } from '../lib/mahojiCommandOptions'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { addToGPTaxBalance } from '../mahojiSettings'; function makeGiveawayButtons(giveawayID: number): BaseMessageOptions['components'] { diff --git a/src/mahoji/commands/gp.ts b/src/mahoji/commands/gp.ts index 04749ed790c..2a84641bcfe 100644 --- a/src/mahoji/commands/gp.ts +++ b/src/mahoji/commands/gp.ts @@ -1,8 +1,8 @@ -import { CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; import { toKMB } from 'oldschooljs/dist/util'; import { Emoji } from '../../lib/constants'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { mahojiUsersSettingsFetch } from '../mahojiSettings'; export const gpCommand: OSBMahojiCommand = { diff --git a/src/mahoji/commands/help.ts b/src/mahoji/commands/help.ts index 91c3cb81ffd..b11d54c7db1 100644 --- a/src/mahoji/commands/help.ts +++ b/src/mahoji/commands/help.ts @@ -1,7 +1,7 @@ import { ButtonStyle, ComponentType } from 'discord.js'; import { mahojiInformationalButtons } from '../../lib/constants'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const helpCommand: OSBMahojiCommand = { name: 'help', diff --git a/src/mahoji/commands/hunt.ts b/src/mahoji/commands/hunt.ts index 497ef474d33..79b633929ac 100644 --- a/src/mahoji/commands/hunt.ts +++ b/src/mahoji/commands/hunt.ts @@ -1,6 +1,7 @@ import { stringMatches } from '@oldschoolgg/toolkit'; import { Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; import { HERBIBOAR_ID, RAZOR_KEBBIT_ID } from '../../lib/constants'; @@ -10,13 +11,13 @@ import { soteSkillRequirements } from '../../lib/skilling/functions/questRequire import creatures from '../../lib/skilling/skills/hunter/creatures'; import Hunter from '../../lib/skilling/skills/hunter/hunter'; import { HunterTechniqueEnum, SkillsEnum } from '../../lib/skilling/types'; -import { Peak } from '../../lib/tickers'; -import { HunterActivityTaskOptions } from '../../lib/types/minions'; +import type { Peak } from '../../lib/tickers'; +import type { HunterActivityTaskOptions } from '../../lib/types/minions'; import { formatDuration, hasSkillReqs, itemID } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../lib/util/calcMaxTripLength'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { userHasGracefulEquipped } from '../mahojiSettings'; export const huntCommand: OSBMahojiCommand = { @@ -72,14 +73,14 @@ export const huntCommand: OSBMahojiCommand = { const userQP = user.QP; const boosts = []; let traps = 1; - let usingHuntPotion = Boolean(options.hunter_potion); + const usingHuntPotion = Boolean(options.hunter_potion); let wildyScore = 0; if (options.stamina_potions === undefined) { options.stamina_potions = true; } - let usingStaminaPotion = Boolean(options.stamina_potions); + const usingStaminaPotion = Boolean(options.stamina_potions); const creature = Hunter.Creatures.find(creature => creature.aliases.some( @@ -119,7 +120,7 @@ export const huntCommand: OSBMahojiCommand = { } } - let crystalImpling = creature.name === 'Crystal impling'; + const crystalImpling = creature.name === 'Crystal impling'; if (crystalImpling) { const [hasReqs, reason] = hasSkillReqs(user, soteSkillRequirements); @@ -193,7 +194,7 @@ export const huntCommand: OSBMahojiCommand = { )}.`; } - let removeBank = new Bank(); + const removeBank = new Bank(); if (creature.itemsConsumed) { for (const [item, qty] of creature.itemsConsumed.items()) { @@ -212,7 +213,7 @@ export const huntCommand: OSBMahojiCommand = { // If creatures Herbiboar or Razor-backed kebbit use Stamina potion(4) if (usingStaminaPotion) { if (creature.id === HERBIBOAR_ID || creature.id === RAZOR_KEBBIT_ID || crystalImpling) { - let staminaPotionQuantity = + const staminaPotionQuantity = creature.id === HERBIBOAR_ID || crystalImpling ? Math.round(duration / (9 * Time.Minute)) : Math.round(duration / (18 * Time.Minute)); @@ -250,7 +251,7 @@ export const huntCommand: OSBMahojiCommand = { } } wildyStr = `You are hunting ${creature.name} in the Wilderness during ${ - wildyPeak!.peakTier + wildyPeak?.peakTier } peak time and potentially risking your equipped body and legs in the wildy setup with a score ${wildyScore} and also risking Saradomin brews and Super restore potions.`; } diff --git a/src/mahoji/commands/invite.ts b/src/mahoji/commands/invite.ts index b7f2fee88c7..c77fd8193e0 100644 --- a/src/mahoji/commands/invite.ts +++ b/src/mahoji/commands/invite.ts @@ -1,4 +1,4 @@ -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const inviteCommand: OSBMahojiCommand = { name: 'invite', diff --git a/src/mahoji/commands/k.ts b/src/mahoji/commands/k.ts index 163be1a7f39..34cda9c054e 100644 --- a/src/mahoji/commands/k.ts +++ b/src/mahoji/commands/k.ts @@ -1,11 +1,13 @@ -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; -import { NEX_ID, PVM_METHODS, PvMMethod, ZALCANO_ID } from '../../lib/constants'; +import type { PvMMethod } from '../../lib/constants'; +import { NEX_ID, PVM_METHODS, ZALCANO_ID } from '../../lib/constants'; import killableMonsters from '../../lib/minions/data/killableMonsters'; import { prisma } from '../../lib/settings/prisma'; import { returnStringOrFile } from '../../lib/util/smallUtils'; import { minionKillCommand, monsterInfo } from '../lib/abstracted_commands/minionKill'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const autocompleteMonsters = [ ...killableMonsters, diff --git a/src/mahoji/commands/kc.ts b/src/mahoji/commands/kc.ts index 7cdf03e50c5..83bd2d35bd6 100644 --- a/src/mahoji/commands/kc.ts +++ b/src/mahoji/commands/kc.ts @@ -1,10 +1,11 @@ import { stringMatches, toTitleCase } from '@oldschoolgg/toolkit'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Hiscores } from 'oldschooljs'; import { bossNameMap, mappedBossNames } from 'oldschooljs/dist/constants'; -import { BossRecords } from 'oldschooljs/dist/meta/types'; +import type { BossRecords } from 'oldschooljs/dist/meta/types'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const kcCommand: OSBMahojiCommand = { name: 'kc', diff --git a/src/mahoji/commands/kill.ts b/src/mahoji/commands/kill.ts index 1e17e3d8d1e..f6a7448acb8 100644 --- a/src/mahoji/commands/kill.ts +++ b/src/mahoji/commands/kill.ts @@ -1,5 +1,6 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank, Monsters } from 'oldschooljs'; import { PerkTier } from '../../lib/constants'; @@ -7,7 +8,7 @@ import { simulatedKillables } from '../../lib/simulation/simulatedKillables'; import { deferInteraction } from '../../lib/util/interactionReply'; import { makeBankImage } from '../../lib/util/makeBankImage'; import { Workers } from '../../lib/workers'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export function determineKillLimit(user: MUser) { const perkTier = user.perkTier(); diff --git a/src/mahoji/commands/laps.ts b/src/mahoji/commands/laps.ts index 01495dc3c96..37d8e54c255 100644 --- a/src/mahoji/commands/laps.ts +++ b/src/mahoji/commands/laps.ts @@ -1,15 +1,16 @@ import { Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; import { courses } from '../../lib/skilling/skills/agility'; import { SkillsEnum } from '../../lib/skilling/types'; -import { AgilityActivityTaskOptions } from '../../lib/types/minions'; +import type { AgilityActivityTaskOptions } from '../../lib/types/minions'; import { formatDuration, stringMatches } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../lib/util/calcMaxTripLength'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; const unlimitedFireRuneProviders = [ 'Staff of fire', @@ -175,7 +176,7 @@ export const lapsCommand: OSBMahojiCommand = { : { itemID: alchResult.itemToAlch.id, quantity: alchResult.maxCasts - } + } }); return response; diff --git a/src/mahoji/commands/leaderboard.ts b/src/mahoji/commands/leaderboard.ts index 75f0f7b5c32..6d9e77fbff2 100644 --- a/src/mahoji/commands/leaderboard.ts +++ b/src/mahoji/commands/leaderboard.ts @@ -1,12 +1,14 @@ -/* eslint-disable @typescript-eslint/no-floating-promises */ import { toTitleCase } from '@oldschoolgg/toolkit'; -import { Prisma } from '@prisma/client'; -import { ChatInputCommandInteraction, EmbedBuilder } from 'discord.js'; -import { calcWhatPercent, chunk, objectValues, Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; - -import { ClueTier, ClueTiers } from '../../lib/clues/clueTiers'; -import { badges, badgesCache, Emoji, masteryKey, usernameCache } from '../../lib/constants'; +import type { Prisma } from '@prisma/client'; +import type { ChatInputCommandInteraction } from 'discord.js'; +import { EmbedBuilder } from 'discord.js'; +import { Time, calcWhatPercent, chunk, objectValues } from 'e'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; + +import type { ClueTier } from '../../lib/clues/clueTiers'; +import { ClueTiers } from '../../lib/clues/clueTiers'; +import { Emoji, badges, badgesCache, masteryKey, usernameCache } from '../../lib/constants'; import { allClNames, getCollectionItems } from '../../lib/data/Collections'; import { effectiveMonsters } from '../../lib/minions/data/killableMonsters'; import { allOpenables } from '../../lib/openables'; @@ -29,7 +31,7 @@ import { fetchCLLeaderboard } from '../../lib/util/clLeaderboard'; import { deferInteraction } from '../../lib/util/interactionReply'; import { userEventsToMap } from '../../lib/util/userEvents'; import { sendToChannelID } from '../../lib/util/webhook'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; const LB_PAGE_SIZE = 10; @@ -75,7 +77,7 @@ async function kcLb( ) { const monster = effectiveMonsters.find(mon => [mon.name, ...mon.aliases].some(alias => stringMatches(alias, name))); if (!monster) return "That's not a valid monster!"; - let list = await prisma.$queryRawUnsafe<{ id: string; kc: number }[]>( + const list = await prisma.$queryRawUnsafe<{ id: string; kc: number }[]>( `SELECT user_id::text AS id, CAST("monster_scores"->>'${monster.id}' AS INTEGER) as kc FROM user_stats ${ironmanOnly ? 'INNER JOIN "users" on "users"."id" = "user_stats"."user_id"::text' : ''} @@ -106,7 +108,7 @@ async function farmingContractLb( channelID: string, ironmanOnly: boolean ) { - let list = await prisma.$queryRawUnsafe<{ id: string; count: number }[]>( + const list = await prisma.$queryRawUnsafe<{ id: string; count: number }[]>( `SELECT id, CAST("minion.farmingContract"->>'contractsCompleted' AS INTEGER) as count FROM users WHERE "minion.farmingContract" is not null and CAST ("minion.farmingContract"->>'contractsCompleted' AS INTEGER) >= 1 @@ -166,7 +168,7 @@ async function sacrificeLb( ORDER BY "sacrificedValue" DESC LIMIT 2000;` ) - ).map((res: any) => ({ ...res, amount: parseInt(res.sacrificedValue) })); + ).map((res: any) => ({ ...res, amount: Number.parseInt(res.sacrificedValue) })); doMenu( interaction, @@ -382,7 +384,7 @@ async function openLb( ? undefined : allOpenables.find( item => stringMatches(item.name, name) || item.name.toLowerCase().includes(name.toLowerCase()) - ); + ); if (openable) { entityID = openable.id; key = 'openable_scores'; @@ -393,7 +395,7 @@ async function openLb( return `That's not a valid openable item! You can check: ${allOpenables.map(i => i.name).join(', ')}.`; } - let list = await prisma.$queryRawUnsafe<{ id: string; qty: number }[]>( + const list = await prisma.$queryRawUnsafe<{ id: string; qty: number }[]>( `SELECT user_id::text AS id, ("${key}"->>'${entityID}')::int as qty FROM user_stats ${ironmanOnly ? 'INNER JOIN users ON users.id::bigint = user_stats.user_id' : ''} WHERE ("${key}"->>'${entityID}')::int > 3 @@ -602,7 +604,7 @@ async function skillsLb( ), `${skill ? toTitleCase(skill.id) : 'Overall'} Leaderboard` ); - return lbMsg(`Overall ${skill!.name} ${type}`); + return lbMsg(`Overall ${skill?.name} ${type}`); } async function cluesLb( @@ -677,7 +679,7 @@ export async function cacheUsernames() { } }); - let orConditions: Prisma.UserWhereInput[] = []; + const orConditions: Prisma.UserWhereInput[] = []; for (const skill of objectValues(SkillsEnum)) { orConditions.push({ [`skills_${skill}`]: { @@ -805,9 +807,7 @@ LIMIT 10; return lbMsg('Global Mastery Leaderboard'); } - const result = await roboChimpClient.$queryRaw< - { id: string; total_cl_percent: number }[] - >`SELECT ((osb_cl_percent + bso_cl_percent) / 2) AS total_cl_percent, id::text AS id + const result = await roboChimpClient.$queryRaw<{ id: string; total_cl_percent: number }[]>`SELECT ((osb_cl_percent + bso_cl_percent) / 2) AS total_cl_percent, id::text AS id FROM public.user WHERE osb_cl_percent IS NOT NULL AND bso_cl_percent IS NOT NULL ORDER BY total_cl_percent DESC @@ -965,7 +965,7 @@ async function masteryLb(interaction: ChatInputCommandInteraction, user: MUser, subList .map( (lUser, j) => - `${getPos(i, j)}**${getUsername(lUser.id)}:** ${lUser[masteryKey]!.toFixed(3)}% mastery` + `${getPos(i, j)}**${getUsername(lUser.id)}:** ${lUser[masteryKey]?.toFixed(3)}% mastery` ) .join('\n') ), @@ -1139,7 +1139,7 @@ export const leaderboardCommand: OSBMahojiCommand = { ? true : [i.name, ...i.aliases].some(str => str.toLowerCase().includes(value.toLowerCase()) - ) + ) ) .map(i => ({ name: i.name, value: i.name })); } diff --git a/src/mahoji/commands/leagues.ts b/src/mahoji/commands/leagues.ts index ab722a60ba1..5a7b88df5b4 100644 --- a/src/mahoji/commands/leagues.ts +++ b/src/mahoji/commands/leagues.ts @@ -1,10 +1,11 @@ -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import fetch from 'node-fetch'; import { Hiscores } from 'oldschooljs'; import leaguesJson from '../../lib/leagues.json'; import { statsEmbed } from '../../lib/util/statsEmbed'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; interface LeaguesData { [key: string]: { diff --git a/src/mahoji/commands/light.ts b/src/mahoji/commands/light.ts index 2d3a78a47fb..bf2c5852769 100644 --- a/src/mahoji/commands/light.ts +++ b/src/mahoji/commands/light.ts @@ -1,15 +1,16 @@ import { stringMatches } from '@oldschoolgg/toolkit'; import { Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; import Firemaking from '../../lib/skilling/skills/firemaking'; import { SkillsEnum } from '../../lib/skilling/types'; -import { FiremakingActivityTaskOptions } from '../../lib/types/minions'; +import type { FiremakingActivityTaskOptions } from '../../lib/types/minions'; import { formatDuration } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../lib/util/calcMaxTripLength'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const lightCommand: OSBMahojiCommand = { name: 'light', diff --git a/src/mahoji/commands/loot.ts b/src/mahoji/commands/loot.ts index c530e793ed2..93d1334fd21 100644 --- a/src/mahoji/commands/loot.ts +++ b/src/mahoji/commands/loot.ts @@ -1,10 +1,11 @@ -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { PerkTier } from '../../lib/constants'; import { getAllTrackedLootForUser, getDetailsOfSingleTrackedLoot } from '../../lib/lootTrack'; import { prisma } from '../../lib/settings/prisma'; import { handleMahojiConfirmation } from '../../lib/util/handleMahojiConfirmation'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const lootCommand: OSBMahojiCommand = { name: 'loot', diff --git a/src/mahoji/commands/lvl.ts b/src/mahoji/commands/lvl.ts index 5426fbbd970..4bfe83800b3 100644 --- a/src/mahoji/commands/lvl.ts +++ b/src/mahoji/commands/lvl.ts @@ -1,11 +1,12 @@ -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Hiscores } from 'oldschooljs'; -import { SkillsEnum } from 'oldschooljs/dist/constants'; +import type { SkillsEnum } from 'oldschooljs/dist/constants'; import { convertLVLtoXP, convertXPtoLVL } from 'oldschooljs/dist/util'; import { MAX_XP } from '../../lib/constants'; import { skillOption } from '../lib/mahojiCommandOptions'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; const xpLeft = (xp: number) => { const level = convertXPtoLVL(xp); diff --git a/src/mahoji/commands/m.ts b/src/mahoji/commands/m.ts index db7001328e4..6f382872c61 100644 --- a/src/mahoji/commands/m.ts +++ b/src/mahoji/commands/m.ts @@ -1,7 +1,7 @@ -import { CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; import { minionStatusCommand } from '../lib/abstracted_commands/minionStatusCommand'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const mCommand: OSBMahojiCommand = { name: 'm', diff --git a/src/mahoji/commands/mass.ts b/src/mahoji/commands/mass.ts index e33586927fc..4d921dbbebe 100644 --- a/src/mahoji/commands/mass.ts +++ b/src/mahoji/commands/mass.ts @@ -1,20 +1,21 @@ -import { TextChannel } from 'discord.js'; -import { objectKeys, Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { TextChannel } from 'discord.js'; +import { Time, objectKeys } from 'e'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import killableMonsters from '../../lib/minions/data/killableMonsters'; import calculateMonsterFood from '../../lib/minions/functions/calculateMonsterFood'; import hasEnoughFoodForMonster from '../../lib/minions/functions/hasEnoughFoodForMonster'; import removeFoodFromUser from '../../lib/minions/functions/removeFoodFromUser'; -import { KillableMonster } from '../../lib/minions/types'; +import type { KillableMonster } from '../../lib/minions/types'; import { setupParty } from '../../lib/party'; -import { GroupMonsterActivityTaskOptions } from '../../lib/types/minions'; +import type { GroupMonsterActivityTaskOptions } from '../../lib/types/minions'; import { channelIsSendable, formatDuration } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import calcDurQty from '../../lib/util/calcMassDurationQuantity'; import findMonster from '../../lib/util/findMonster'; import { deferInteraction } from '../../lib/util/interactionReply'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { hasMonsterRequirements } from '../mahojiSettings'; function checkReqs(users: MUser[], monster: KillableMonster, quantity: number) { @@ -40,7 +41,7 @@ function checkReqs(users: MUser[], monster: KillableMonster, quantity: number) { if (1 > 2 && !hasEnoughFoodForMonster(monster, user, quantity, users.length)) { return `${ users.length === 1 ? "You don't" : `${user.usernameOrMention} doesn't` - } have enough food. You need at least ${monster!.healAmountNeeded! * quantity} HP in food to ${ + } have enough food. You need at least ${monster.healAmountNeeded! * quantity} HP in food to ${ users.length === 1 ? 'start the mass' : 'enter the mass' }.`; } @@ -130,7 +131,7 @@ export const massCommand: OSBMahojiCommand = { ephemeral: true }; } - let unchangedUsers = [...users]; + const unchangedUsers = [...users]; users = users.filter(i => !i.minionIsBusy); const usersKickedForBusy = unchangedUsers.filter(i => !users.includes(i)); diff --git a/src/mahoji/commands/mine.ts b/src/mahoji/commands/mine.ts index 1df0efa5bf3..3913cbf2c32 100644 --- a/src/mahoji/commands/mine.ts +++ b/src/mahoji/commands/mine.ts @@ -1,18 +1,19 @@ import { stringMatches } from '@oldschoolgg/toolkit'; import { increaseNumByPercent, reduceNumByPercent } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { determineMiningTime } from '../../lib/skilling/functions/determineMiningTime'; import { miningCapeOreEffect, miningGloves, pickaxes, varrockArmours } from '../../lib/skilling/functions/miningBoosts'; import { sinsOfTheFatherSkillRequirements } from '../../lib/skilling/functions/questRequirements'; import Mining from '../../lib/skilling/skills/mining'; -import { MiningActivityTaskOptions } from '../../lib/types/minions'; +import type { MiningActivityTaskOptions } from '../../lib/types/minions'; import { formatDuration, formatSkillRequirements, itemNameFromID, randomVariation } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import itemID from '../../lib/util/itemID'; import { minionName } from '../../lib/util/minionUtils'; import { motherlodeMineCommand } from '../lib/abstracted_commands/motherlodeMineCommand'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const mineCommand: OSBMahojiCommand = { name: 'mine', @@ -61,7 +62,7 @@ export const mineCommand: OSBMahojiCommand = { const motherlodeMine = stringMatches(Mining.MotherlodeMine.name, options.name) || - Mining.MotherlodeMine.aliases!.some(a => stringMatches(a, options.name)); + Mining.MotherlodeMine.aliases?.some(a => stringMatches(a, options.name)); if (motherlodeMine) { return motherlodeMineCommand({ user, channelID, quantity }); @@ -71,7 +72,7 @@ export const mineCommand: OSBMahojiCommand = { ore => stringMatches(ore.id, options.name) || stringMatches(ore.name, options.name) || - (ore.aliases && ore.aliases.some(a => stringMatches(a, options.name))) + ore.aliases?.some(a => stringMatches(a, options.name)) ); if (!ore) { return `Thats not a valid ore to mine. Valid ores are ${Mining.Ores.map(ore => ore.name).join(', ')}, or ${ @@ -163,7 +164,7 @@ export const mineCommand: OSBMahojiCommand = { boosts.push('**Powermining**'); } // Calculate the time it takes to mine specific quantity or as many as possible - let [timeToMine, newQuantity] = determineMiningTime({ + const [timeToMine, newQuantity] = determineMiningTime({ quantity, user, ore, diff --git a/src/mahoji/commands/minigames.ts b/src/mahoji/commands/minigames.ts index 8abf2732bbd..34dd86cec02 100644 --- a/src/mahoji/commands/minigames.ts +++ b/src/mahoji/commands/minigames.ts @@ -1,22 +1,23 @@ -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; -import TrekShopItems from '../../lib/data/buyables/trekBuyables'; import { LMSBuyables } from '../../lib/data/CollectionsExport'; +import TrekShopItems from '../../lib/data/buyables/trekBuyables'; import { - agilityArenaBuyables, agilityArenaBuyCommand, + agilityArenaBuyables, agilityArenaCommand, agilityArenaRecolorCommand, agilityArenaXPCommand } from '../lib/abstracted_commands/agilityArenaCommand'; import { + BarbBuyables, + GambleTiers, barbAssaultBuyCommand, barbAssaultGambleCommand, barbAssaultLevelCommand, barbAssaultStartCommand, - barbAssaultStatsCommand, - BarbBuyables, - GambleTiers + barbAssaultStatsCommand } from '../lib/abstracted_commands/barbAssault'; import { castleWarsStartCommand, castleWarsStatsCommand } from '../lib/abstracted_commands/castleWarsCommand'; import { fishingTrawlerCommand } from '../lib/abstracted_commands/fishingTrawler'; @@ -32,16 +33,16 @@ import { lmsCommand } from '../lib/abstracted_commands/lmsCommand'; import { mageArena2Command } from '../lib/abstracted_commands/mageArena2Command'; import { mageArenaCommand } from '../lib/abstracted_commands/mageArenaCommand'; import { - mageTrainingArenaBuyables, mageTrainingArenaBuyCommand, + mageTrainingArenaBuyables, mageTrainingArenaPointsCommand, mageTrainingArenaStartCommand } from '../lib/abstracted_commands/mageTrainingArenaCommand'; import { contractTiers, mahoganyHomesBuildCommand, - mahoganyHomesBuyables, - mahoganyHomesBuyCommand + mahoganyHomesBuyCommand, + mahoganyHomesBuyables } from '../lib/abstracted_commands/mahoganyHomesCommand'; import { nightmareZoneShopCommand, @@ -49,8 +50,8 @@ import { nightmareZoneStatsCommand } from '../lib/abstracted_commands/nightmareZoneCommand'; import { - pestControlBuyables, pestControlBuyCommand, + pestControlBuyables, pestControlStartCommand, pestControlStatsCommand, pestControlXPCommand @@ -60,10 +61,10 @@ import { roguesDenCommand } from '../lib/abstracted_commands/roguesDenCommand'; import { sepulchreCommand } from '../lib/abstracted_commands/sepulchreCommand'; import { shades, shadesLogs, shadesOfMortonStartCommand } from '../lib/abstracted_commands/shadesOfMortonCommand'; import { - soulWarsBuyables, soulWarsBuyCommand, - soulWarsImbueables, + soulWarsBuyables, soulWarsImbueCommand, + soulWarsImbueables, soulWarsStartCommand, soulWarsTokensCommand } from '../lib/abstracted_commands/soulWarsCommand'; @@ -71,18 +72,19 @@ import { tearsOfGuthixCommand } from '../lib/abstracted_commands/tearsOfGuthixCo import { trekCommand, trekShop } from '../lib/abstracted_commands/trekCommand'; import { troubleBrewingStartCommand } from '../lib/abstracted_commands/troubleBrewingCommand'; import { - volcanicMineCommand, VolcanicMineShop, + volcanicMineCommand, volcanicMineShopCommand, volcanicMineStatsCommand } from '../lib/abstracted_commands/volcanicMineCommand'; -import { OSBMahojiCommand } from '../lib/util'; -import { NMZ_STRATEGY, NMZStrategy } from './../../lib/constants'; +import type { OSBMahojiCommand } from '../lib/util'; +import type { NMZStrategy } from './../../lib/constants'; +import { NMZ_STRATEGY } from './../../lib/constants'; import { giantsFoundryAlloys, giantsFoundryBuyables } from './../lib/abstracted_commands/giantsFoundryCommand'; import { nightmareZoneBuyables, - nightmareZoneImbueables, - nightmareZoneImbueCommand + nightmareZoneImbueCommand, + nightmareZoneImbueables } from './../lib/abstracted_commands/nightmareZoneCommand'; export const minigamesCommand: OSBMahojiCommand = { @@ -1219,11 +1221,11 @@ export const minigamesCommand: OSBMahojiCommand = { */ if (options.temple_trek) { if (options.temple_trek.buy) { - let { reward, difficulty, quantity } = options.temple_trek.buy!; + const { reward, difficulty, quantity } = options.temple_trek.buy!; return trekShop(user, reward, difficulty, quantity, interaction); } if (options.temple_trek.start) { - let { difficulty, quantity } = options.temple_trek.start!; + const { difficulty, quantity } = options.temple_trek.start!; return trekCommand(user, channelID, difficulty, quantity); } } diff --git a/src/mahoji/commands/minion.ts b/src/mahoji/commands/minion.ts index 7fff3b4914e..6d07fdc3709 100644 --- a/src/mahoji/commands/minion.ts +++ b/src/mahoji/commands/minion.ts @@ -1,24 +1,25 @@ import { formatOrdinal, roboChimpCLRankQuery } from '@oldschoolgg/toolkit'; import { bold } from 'discord.js'; import { notEmpty, randArrItem } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; -import { MahojiUserOption } from 'mahoji/dist/lib/types'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; +import type { MahojiUserOption } from 'mahoji/dist/lib/types'; import { BLACKLISTED_USERS } from '../../lib/blacklists'; import { - badges, BitField, BitFieldData, FormattedCustomEmoji, MAX_LEVEL, - minionActivityCache, - PerkTier + PerkTier, + badges, + minionActivityCache } from '../../lib/constants'; import { degradeableItems } from '../../lib/degradeableItems'; import { diaries } from '../../lib/diaries'; import { calculateMastery } from '../../lib/mastery'; import { effectiveMonsters } from '../../lib/minions/data/killableMonsters'; -import { AttackStyles } from '../../lib/minions/functions'; +import type { AttackStyles } from '../../lib/minions/functions'; import { blowpipeCommand, blowpipeDarts } from '../../lib/minions/functions/blowpipeCommand'; import { degradeableItemsCommand } from '../../lib/minions/functions/degradeableItemsCommand'; import { allPossibleStyles, trainCommand } from '../../lib/minions/functions/trainCommand'; @@ -46,7 +47,7 @@ import { Lampables, lampCommand } from '../lib/abstracted_commands/lampCommand'; import { minionBuyCommand } from '../lib/abstracted_commands/minionBuyCommand'; import { minionStatusCommand } from '../lib/abstracted_commands/minionStatusCommand'; import { skillOption } from '../lib/mahojiCommandOptions'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { patronMsg } from '../mahojiSettings'; const patMessages = [ diff --git a/src/mahoji/commands/mix.ts b/src/mahoji/commands/mix.ts index a0169183bed..1b5d06f4817 100644 --- a/src/mahoji/commands/mix.ts +++ b/src/mahoji/commands/mix.ts @@ -1,16 +1,17 @@ import { stringMatches } from '@oldschoolgg/toolkit'; import { Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; import Herblore from '../../lib/skilling/skills/herblore/herblore'; import { SkillsEnum } from '../../lib/skilling/types'; -import { HerbloreActivityTaskOptions } from '../../lib/types/minions'; +import type { HerbloreActivityTaskOptions } from '../../lib/types/minions'; import { formatDuration } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../lib/util/calcMaxTripLength'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const mixCommand: OSBMahojiCommand = { name: 'mix', diff --git a/src/mahoji/commands/offer.ts b/src/mahoji/commands/offer.ts index ba80ba4eeed..4345d631cec 100644 --- a/src/mahoji/commands/offer.ts +++ b/src/mahoji/commands/offer.ts @@ -1,7 +1,8 @@ import { formatOrdinal, stringMatches } from '@oldschoolgg/toolkit'; -import { User } from 'discord.js'; -import { randArrItem, randInt, roll, Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { User } from 'discord.js'; +import { Time, randArrItem, randInt, roll } from 'e'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; import { Events } from '../../lib/constants'; @@ -10,7 +11,7 @@ import { Offerables } from '../../lib/data/offerData'; import { birdsNestID, treeSeedsNest } from '../../lib/simulation/birdsNest'; import Prayer from '../../lib/skilling/skills/prayer'; import { SkillsEnum } from '../../lib/skilling/types'; -import { OfferingActivityTaskOptions } from '../../lib/types/minions'; +import type { OfferingActivityTaskOptions } from '../../lib/types/minions'; import { formatDuration } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../lib/util/calcMaxTripLength'; @@ -18,7 +19,7 @@ import getOSItem from '../../lib/util/getOSItem'; import { deferInteraction } from '../../lib/util/interactionReply'; import { makeBankImage } from '../../lib/util/makeBankImage'; import resolveItems from '../../lib/util/resolveItems'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { userStatsBankUpdate, userStatsUpdate } from '../mahojiSettings'; const specialBones = [ @@ -103,7 +104,7 @@ export const offerCommand: OSBMahojiCommand = { const whichOfferable = Offerables.find( item => stringMatches(options.name, item.name) || - (item.aliases && item.aliases.some(alias => stringMatches(alias, options.name))) + item.aliases?.some(alias => stringMatches(alias, options.name)) ); if (whichOfferable) { const offerableOwned = userBank.amount(whichOfferable.itemID); @@ -114,7 +115,7 @@ export const offerCommand: OSBMahojiCommand = { if (quantity > offerableOwned) { return `You don't have ${quantity} ${whichOfferable.name} to offer the ${whichOfferable.offerWhere}. You have ${offerableOwned}.`; } - let loot = new Bank().add(whichOfferable.table.roll(quantity)); + const loot = new Bank().add(whichOfferable.table.roll(quantity)); const { previousCL, itemsAdded } = await user.transactItems({ collectionLog: true, @@ -132,7 +133,7 @@ export const offerCommand: OSBMahojiCommand = { { slayer_chewed_offered: true, slayer_unsired_offered: true } ); // Notify uniques if (whichOfferable.uniques) { - let current = newStats[whichOfferable.economyCounter]; + const current = newStats[whichOfferable.economyCounter]; notifyUniques( user, whichOfferable.name, @@ -166,7 +167,7 @@ export const offerCommand: OSBMahojiCommand = { const cost = new Bank().add(egg.id, quantity); if (!user.owns(cost)) return "You don't own enough of these eggs."; - let loot = new Bank(); + const loot = new Bank(); for (let i = 0; i < quantity; i++) { if (roll(300)) { loot.add(randArrItem(evilChickenOutfit)); diff --git a/src/mahoji/commands/open.ts b/src/mahoji/commands/open.ts index 8678d5464d0..fd2a5dd7bed 100644 --- a/src/mahoji/commands/open.ts +++ b/src/mahoji/commands/open.ts @@ -1,14 +1,15 @@ import { truncateString } from '@oldschoolgg/toolkit'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { allOpenables, allOpenablesIDs } from '../../lib/openables'; import { deferInteraction } from '../../lib/util/interactionReply'; import { + OpenUntilItems, abstractedOpenCommand, - abstractedOpenUntilCommand, - OpenUntilItems + abstractedOpenUntilCommand } from '../lib/abstracted_commands/openCommand'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const openCommand: OSBMahojiCommand = { name: 'open', diff --git a/src/mahoji/commands/patreon.ts b/src/mahoji/commands/patreon.ts index 31efc1c443b..686f93245d9 100644 --- a/src/mahoji/commands/patreon.ts +++ b/src/mahoji/commands/patreon.ts @@ -1,5 +1,5 @@ import { Emoji } from '../../lib/constants'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const patreonCommand: OSBMahojiCommand = { name: 'patreon', diff --git a/src/mahoji/commands/pay.ts b/src/mahoji/commands/pay.ts index 3a96f9411d2..43b469167f5 100644 --- a/src/mahoji/commands/pay.ts +++ b/src/mahoji/commands/pay.ts @@ -1,5 +1,6 @@ -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; -import { MahojiUserOption } from 'mahoji/dist/lib/types'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; +import type { MahojiUserOption } from 'mahoji/dist/lib/types'; import { Bank } from 'oldschooljs'; import { BLACKLISTED_USERS } from '../../lib/blacklists'; @@ -9,7 +10,7 @@ import { toKMB } from '../../lib/util'; import { handleMahojiConfirmation } from '../../lib/util/handleMahojiConfirmation'; import { deferInteraction } from '../../lib/util/interactionReply'; import { tradePlayerItems } from '../../lib/util/tradePlayerItems'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { addToGPTaxBalance, mahojiParseNumber } from '../mahojiSettings'; export const payCommand: OSBMahojiCommand = { diff --git a/src/mahoji/commands/poh.ts b/src/mahoji/commands/poh.ts index da89a6d96a4..8ddeac99b20 100644 --- a/src/mahoji/commands/poh.ts +++ b/src/mahoji/commands/poh.ts @@ -1,5 +1,6 @@ -import { User } from 'discord.js'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { User } from 'discord.js'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { PoHObjects } from '../../lib/poh'; import { minionIsBusy } from '../../lib/util/minionIsBusy'; @@ -14,7 +15,7 @@ import { pohWallkits } from '../lib/abstracted_commands/pohCommand'; import { ownedItemOption } from '../lib/mahojiCommandOptions'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const pohCommand: OSBMahojiCommand = { name: 'poh', diff --git a/src/mahoji/commands/poll.ts b/src/mahoji/commands/poll.ts index 8fa5cee9c95..5da45d996f6 100644 --- a/src/mahoji/commands/poll.ts +++ b/src/mahoji/commands/poll.ts @@ -1,8 +1,9 @@ -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { channelIsSendable } from '../../lib/util'; import { deferInteraction } from '../../lib/util/interactionReply'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const pollCommand: OSBMahojiCommand = { name: 'poll', diff --git a/src/mahoji/commands/price.ts b/src/mahoji/commands/price.ts index f9ea4a8652b..895c01b5c10 100644 --- a/src/mahoji/commands/price.ts +++ b/src/mahoji/commands/price.ts @@ -1,10 +1,10 @@ import { EmbedBuilder } from 'discord.js'; -import { CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; import { toKMB } from 'oldschooljs/dist/util'; import { getItem } from '../../lib/util/getOSItem'; import { itemOption } from '../lib/mahojiCommandOptions'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { sellPriceOfItem } from './sell'; export const priceCommand: OSBMahojiCommand = { diff --git a/src/mahoji/commands/raid.ts b/src/mahoji/commands/raid.ts index 8b088d65af1..437af01d04b 100644 --- a/src/mahoji/commands/raid.ts +++ b/src/mahoji/commands/raid.ts @@ -1,11 +1,13 @@ -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; -import { mileStoneBaseDeathChances, RaidLevel, toaHelpCommand, toaStartCommand } from '../../lib/simulation/toa'; +import type { RaidLevel } from '../../lib/simulation/toa'; +import { mileStoneBaseDeathChances, toaHelpCommand, toaStartCommand } from '../../lib/simulation/toa'; import { deferInteraction } from '../../lib/util/interactionReply'; import { minionIsBusy } from '../../lib/util/minionIsBusy'; import { coxCommand, coxStatsCommand } from '../lib/abstracted_commands/coxCommand'; import { tobCheckCommand, tobStartCommand, tobStatsCommand } from '../lib/abstracted_commands/tobCommand'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const raidCommand: OSBMahojiCommand = { name: 'raid', @@ -133,7 +135,10 @@ export const raidCommand: OSBMahojiCommand = { name: 'raid_level', description: 'Choose the raid level you want to do (1-600).', required: true, - choices: mileStoneBaseDeathChances.map(i => ({ name: i.level.toString(), value: i.level })) + choices: mileStoneBaseDeathChances.map(i => ({ + name: i.level.toString(), + value: i.level + })) }, { type: ApplicationCommandOptionType.Boolean, @@ -197,7 +202,7 @@ export const raidCommand: OSBMahojiCommand = { if (minionIsBusy(user.id)) return "Your minion is busy, you can't do this."; - if (cox && cox.start) { + if (cox?.start) { return coxCommand( channelID, user, diff --git a/src/mahoji/commands/redeem.ts b/src/mahoji/commands/redeem.ts index c4f858bd592..3a6f4c50ac6 100644 --- a/src/mahoji/commands/redeem.ts +++ b/src/mahoji/commands/redeem.ts @@ -1,11 +1,12 @@ import { ProductID, products } from '@oldschoolgg/toolkit'; import { bold } from 'discord.js'; import { notEmpty } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { BOT_TYPE } from '../../lib/constants'; import { roboChimpSyncData } from '../../lib/roboChimp'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const redeemCommand: OSBMahojiCommand = { name: 'redeem', @@ -78,7 +79,7 @@ export const redeemCommand: OSBMahojiCommand = { push: product.bit } } - }) + }) : undefined ].filter(notEmpty) ); diff --git a/src/mahoji/commands/roll.ts b/src/mahoji/commands/roll.ts index 7842d330b13..511db376b7f 100644 --- a/src/mahoji/commands/roll.ts +++ b/src/mahoji/commands/roll.ts @@ -1,7 +1,8 @@ -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { cryptoRand } from '../../lib/util'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const rollCommand: OSBMahojiCommand = { name: 'roll', diff --git a/src/mahoji/commands/rp.ts b/src/mahoji/commands/rp.ts index 8eefaf486ff..e562738ec66 100644 --- a/src/mahoji/commands/rp.ts +++ b/src/mahoji/commands/rp.ts @@ -2,29 +2,30 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; import { UserEventType, xp_gains_skill_enum } from '@prisma/client'; import { DiscordSnowflake } from '@sapphire/snowflake'; import { Duration } from '@sapphire/time-utilities'; -import { codeBlock, SnowflakeUtil } from 'discord.js'; -import { randArrItem, sumArr, Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; -import { MahojiUserOption } from 'mahoji/dist/lib/types'; +import { SnowflakeUtil, codeBlock } from 'discord.js'; +import { Time, randArrItem, sumArr } from 'e'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; +import type { MahojiUserOption } from 'mahoji/dist/lib/types'; import { Bank } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; -import { ADMIN_IDS, OWNER_IDS, production, SupportServer } from '../../config'; +import { ADMIN_IDS, OWNER_IDS, SupportServer, production } from '../../config'; +import { mahojiUserSettingsUpdate } from '../../lib/MUser'; import { analyticsTick } from '../../lib/analytics'; import { BitField, Channel } from '../../lib/constants'; import { allCollectionLogsFlat } from '../../lib/data/Collections'; -import { GearSetupType } from '../../lib/gear/types'; +import type { GearSetupType } from '../../lib/gear/types'; import { GrandExchange } from '../../lib/grandExchange'; import { marketPricemap } from '../../lib/marketPrices'; import { unEquipAllCommand } from '../../lib/minions/functions/unequipAllCommand'; import { unequipPet } from '../../lib/minions/functions/unequipPet'; -import { mahojiUserSettingsUpdate } from '../../lib/MUser'; import { patreonTask } from '../../lib/patreon'; import { allPerkBitfields } from '../../lib/perkTiers'; import { premiumPatronTime } from '../../lib/premiumPatronTime'; import { prisma } from '../../lib/settings/prisma'; import { TeamLoot } from '../../lib/simulation/TeamLoot'; -import { ItemBank } from '../../lib/types'; +import type { ItemBank } from '../../lib/types'; import { dateFm, isValidDiscordSnowflake, returnStringOrFile } from '../../lib/util'; import getOSItem from '../../lib/util/getOSItem'; import { handleMahojiConfirmation } from '../../lib/util/handleMahojiConfirmation'; @@ -38,7 +39,7 @@ import { insertUserEvent } from '../../lib/util/userEvents'; import { sendToChannelID } from '../../lib/util/webhook'; import { cancelUsersListings } from '../lib/abstracted_commands/cancelGEListingCommand'; import { gearSetupOption } from '../lib/mahojiCommandOptions'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { mahojiUsersSettingsFetch } from '../mahojiSettings'; import { gifs } from './admin'; import { getUserInfo } from './minion'; @@ -587,9 +588,7 @@ Date: ${dateFm(date)}`; return 'Done.'; } if (options.action?.view_all_items) { - const result = await prisma.$queryRawUnsafe< - { item_id: number }[] - >(`SELECT DISTINCT json_object_keys(bank)::int AS item_id + const result = await prisma.$queryRawUnsafe<{ item_id: number }[]>(`SELECT DISTINCT json_object_keys(bank)::int AS item_id FROM users UNION SELECT DISTINCT jsonb_object_keys("collectionLogBank")::int AS item_id @@ -665,8 +664,8 @@ ORDER BY item_id ASC;`); const gearSlot = opts.all ? 'all' : opts.gear_setup && allGearSlots.includes(opts.gear_setup) - ? opts.gear_setup - : undefined; + ? opts.gear_setup + : undefined; if (gearSlot === undefined) { return 'No gear slot specified.'; } @@ -703,7 +702,7 @@ ORDER BY item_id ASC;`); const items = new Bank(); if (options.player.steal_items.item_filter) { - const filter = itemFilters.find(i => i.name === options.player!.steal_items!.item_filter); + const filter = itemFilters.find(i => i.name === options.player?.steal_items?.item_filter); if (!filter) return 'Invalid item filter.'; for (const [item, qty] of userToStealFrom.bank.items()) { if (filter.filter(item)) { diff --git a/src/mahoji/commands/runecraft.ts b/src/mahoji/commands/runecraft.ts index 968d74d58f9..3a8da8ba8a4 100644 --- a/src/mahoji/commands/runecraft.ts +++ b/src/mahoji/commands/runecraft.ts @@ -1,20 +1,21 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; import { Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; import { SkillsEnum } from 'oldschooljs/dist/constants'; import { darkAltarCommand } from '../../lib/minions/functions/darkAltarCommand'; import { sinsOfTheFatherSkillRequirements } from '../../lib/skilling/functions/questRequirements'; import Runecraft from '../../lib/skilling/skills/runecraft'; -import { RunecraftActivityTaskOptions } from '../../lib/types/minions'; +import type { RunecraftActivityTaskOptions } from '../../lib/types/minions'; import { formatDuration, formatSkillRequirements, itemID, stringMatches } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../lib/util/calcMaxTripLength'; import { determineRunes } from '../../lib/util/determineRunes'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; import { tiaraRunecraftCommand } from '../lib/abstracted_commands/tiaraRunecraftCommand'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { calcMaxRCQuantity, userHasGracefulEquipped } from '../mahojiSettings'; export const runecraftCommand: OSBMahojiCommand = { @@ -202,7 +203,7 @@ export const runecraftCommand: OSBMahojiCommand = { let imbueCasts = 0; let teleportReduction = 1; - let removeTalismanAndOrRunes = new Bank(); + const removeTalismanAndOrRunes = new Bank(); let hasRingOfTheElements = false; if (runeObj.inputTalisman) { const tomeOfFire = user.hasEquipped(['Tome of fire', 'Tome of fire (empty)']) ? 0 : 7; diff --git a/src/mahoji/commands/sacrifice.ts b/src/mahoji/commands/sacrifice.ts index d4d7d9790fe..2a57b413cf0 100644 --- a/src/mahoji/commands/sacrifice.ts +++ b/src/mahoji/commands/sacrifice.ts @@ -1,11 +1,12 @@ -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; import { Emoji, Events } from '../../lib/constants'; import { cats } from '../../lib/growablePets'; import minionIcons from '../../lib/minions/data/minionIcons'; -import { ItemBank } from '../../lib/types'; +import type { ItemBank } from '../../lib/types'; import { toKMB } from '../../lib/util'; import { handleMahojiConfirmation } from '../../lib/util/handleMahojiConfirmation'; import { deferInteraction } from '../../lib/util/interactionReply'; @@ -13,7 +14,7 @@ import { parseBank } from '../../lib/util/parseStringBank'; import resolveItems from '../../lib/util/resolveItems'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; import { filterOption } from '../lib/mahojiCommandOptions'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { userStatsBankUpdate } from '../mahojiSettings'; import { sellPriceOfItem } from './sell'; diff --git a/src/mahoji/commands/sell.ts b/src/mahoji/commands/sell.ts index 2639835bbf7..48337e3034b 100644 --- a/src/mahoji/commands/sell.ts +++ b/src/mahoji/commands/sell.ts @@ -1,8 +1,9 @@ -import { Prisma } from '@prisma/client'; +import type { Prisma } from '@prisma/client'; import { clamp, reduceNumByPercent } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; -import { Item, ItemBank } from 'oldschooljs/dist/meta/types'; +import type { Item, ItemBank } from 'oldschooljs/dist/meta/types'; import { MAX_INT_JAVA } from '../../lib/constants'; import { prisma } from '../../lib/settings/prisma'; @@ -12,7 +13,7 @@ import { handleMahojiConfirmation } from '../../lib/util/handleMahojiConfirmatio import { parseBank } from '../../lib/util/parseStringBank'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; import { filterOption } from '../lib/mahojiCommandOptions'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { updateClientGPTrackSetting, userStatsUpdate } from '../mahojiSettings'; /** @@ -41,11 +42,11 @@ const specialSoldItems = new Map([ export const CUSTOM_PRICE_CACHE = new Map(); export function sellPriceOfItem(item: Item, taxRate = 20): { price: number; basePrice: number } { - let cachePrice = CUSTOM_PRICE_CACHE.get(item.id); + const cachePrice = CUSTOM_PRICE_CACHE.get(item.id); if (!cachePrice && (item.price === undefined || !item.tradeable)) { return { price: 0, basePrice: 0 }; } - let basePrice = cachePrice ?? item.price; + const basePrice = cachePrice ?? item.price; let price = basePrice; price = reduceNumByPercent(price, taxRate); price = clamp(price, 0, MAX_INT_JAVA); @@ -54,7 +55,7 @@ export function sellPriceOfItem(item: Item, taxRate = 20): { price: number; base export function sellStorePriceOfItem(item: Item, qty: number): { price: number; basePrice: number } { if (!item.cost || !item.lowalch) return { price: 0, basePrice: 0 }; - let basePrice = item.cost; + const basePrice = item.cost; // Sell price decline with stock by 3% until 10% of item value and is always low alch price when stock is 0. const percentageFirstEleven = (0.4 - 0.015 * Math.min(qty - 1, 10)) * Math.min(qty, 11); let price = ((percentageFirstEleven + Math.max(qty - 11, 0) * 0.1) * item.cost) / qty; diff --git a/src/mahoji/commands/simulate.ts b/src/mahoji/commands/simulate.ts index 752f0f913be..e0bb5901cf4 100644 --- a/src/mahoji/commands/simulate.ts +++ b/src/mahoji/commands/simulate.ts @@ -1,6 +1,7 @@ import { randInt, roll } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { Bank } from 'oldschooljs'; import { ChambersOfXeric } from 'oldschooljs/dist/simulation/misc'; import { toKMB } from 'oldschooljs/dist/util'; @@ -11,7 +12,7 @@ import pets from '../../lib/data/pets'; import { assert, averageBank, formatDuration } from '../../lib/util'; import { deferInteraction } from '../../lib/util/interactionReply'; import { makeBankImage } from '../../lib/util/makeBankImage'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export function determineCoxLimit(user: MUser) { const perkTier = user.perkTier(); @@ -106,7 +107,7 @@ async function coxCommand(user: MUser, quantity: number, cm = false, points = 25 team.push({ id: `${randInt(1, 10_000_000)}`, personalPoints: points }); } - let loot = new Bank(); + const loot = new Bank(); for (let i = 0; i < quantity; i++) { const singleRaidLoot = ChambersOfXeric.complete({ team, diff --git a/src/mahoji/commands/slayer.ts b/src/mahoji/commands/slayer.ts index 120db93f6fe..999e931a0c4 100644 --- a/src/mahoji/commands/slayer.ts +++ b/src/mahoji/commands/slayer.ts @@ -1,5 +1,6 @@ -import { User } from 'discord.js'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { User } from 'discord.js'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Monsters } from 'oldschooljs'; import { autoslayChoices, slayerMasterChoices } from '../../lib/slayer/constants'; @@ -18,7 +19,7 @@ import { slayerStatusCommand, slayerUnblockCommand } from '../lib/abstracted_commands/slayerTaskCommand'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { mahojiUsersSettingsFetch } from '../mahojiSettings'; export const slayerCommand: OSBMahojiCommand = { @@ -115,9 +116,9 @@ export const slayerCommand: OSBMahojiCommand = { !value ? true : r.name.toLowerCase().includes(value) || - r.aliases?.some(alias => + r.aliases?.some(alias => alias.toLowerCase().includes(value.toLowerCase()) - ) + ) ) .map(m => { return { name: m.name, value: m.name }; @@ -142,13 +143,13 @@ export const slayerCommand: OSBMahojiCommand = { if (blockList.slayer_blocked_ids.length === 0) { return [{ name: "You don't have any monsters blocked", value: '' }]; } - const blockedMonsters = blockList.slayer_blocked_ids.map(mId => - Monsters.find(m => m.id === mId) + const blockedMonsters = blockList.slayer_blocked_ids.map( + mId => Monsters.find(m => m.id === mId)! ); return blockedMonsters - .filter(m => (!value ? true : m!.name.toLowerCase().includes(value.toLowerCase()))) + .filter(m => (!value ? true : m?.name.toLowerCase().includes(value.toLowerCase()))) .map(m => { - return { name: m!.name, value: m!.name }; + return { name: m?.name, value: m?.name }; }); } } @@ -172,9 +173,9 @@ export const slayerCommand: OSBMahojiCommand = { (!value ? true : r.name.toLowerCase().includes(value) || - r.aliases?.some(alias => + r.aliases?.some(alias => alias.toLowerCase().includes(value.toLowerCase()) - )) + )) ).map(m => { return { name: m.name, value: m.name }; }); @@ -232,9 +233,9 @@ export const slayerCommand: OSBMahojiCommand = { (!value ? true : r.name.toLowerCase().includes(value) || - r.aliases?.some(alias => + r.aliases?.some(alias => alias.toLowerCase().includes(value.toLowerCase()) - )) + )) ).map(m => { return { name: m.name, value: m.name }; }); diff --git a/src/mahoji/commands/smelt.ts b/src/mahoji/commands/smelt.ts index 8e291df2e68..0ecbfbe9c82 100644 --- a/src/mahoji/commands/smelt.ts +++ b/src/mahoji/commands/smelt.ts @@ -1,16 +1,17 @@ import { Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; import Smithing from '../../lib/skilling/skills/smithing'; import { SkillsEnum } from '../../lib/skilling/types'; -import { SmeltingActivityTaskOptions } from '../../lib/types/minions'; +import type { SmeltingActivityTaskOptions } from '../../lib/types/minions'; import { formatDuration, formatSkillRequirements, itemID, stringMatches } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../lib/util/calcMaxTripLength'; import resolveItems from '../../lib/util/resolveItems'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { userHasGracefulEquipped } from '../mahojiSettings'; export const smeltingCommand: OSBMahojiCommand = { @@ -61,7 +62,7 @@ export const smeltingCommand: OSBMahojiCommand = { const bar = blast_furnace ? Smithing.BlastableBars.find( bar => stringMatches(bar.name, name) || stringMatches(bar.name.split(' ')[0], name) - ) + ) : Smithing.Bars.find(bar => stringMatches(bar.name, name) || stringMatches(bar.name.split(' ')[0], name)); if (!bar) { diff --git a/src/mahoji/commands/smith.ts b/src/mahoji/commands/smith.ts index 6ae07492b29..2a187fd4ca8 100644 --- a/src/mahoji/commands/smith.ts +++ b/src/mahoji/commands/smith.ts @@ -1,18 +1,19 @@ import { Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank } from 'oldschooljs'; import { KaramjaDiary, userhasDiaryTier } from '../../lib/diaries'; import Smithing from '../../lib/skilling/skills/smithing'; import smithables from '../../lib/skilling/skills/smithing/smithables'; import { SkillsEnum } from '../../lib/skilling/types'; -import { SmithingActivityTaskOptions } from '../../lib/types/minions'; +import type { SmithingActivityTaskOptions } from '../../lib/types/minions'; import { formatDuration, stringMatches } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../lib/util/calcMaxTripLength'; import { pluraliseItemName } from '../../lib/util/smallUtils'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const smithCommand: OSBMahojiCommand = { name: 'smith', @@ -69,7 +70,7 @@ export const smithCommand: OSBMahojiCommand = { let setBonus = 0; if ( user.gear.skilling.hasEquipped( - Object.keys(Smithing.smithsUniformItems).map(i => parseInt(i)), + Object.keys(Smithing.smithsUniformItems).map(i => Number.parseInt(i)), true ) ) { @@ -77,7 +78,7 @@ export const smithCommand: OSBMahojiCommand = { } else { // For each Smiths' Uniform item, check if they have it, give % chance to save 1 tick each item for (const [itemID, bonus] of Object.entries(Smithing.smithsUniformItems)) { - if (user.gear.skilling.hasEquipped([parseInt(itemID)], false)) { + if (user.gear.skilling.hasEquipped([Number.parseInt(itemID)], false)) { setBonus += bonus; } } @@ -99,7 +100,7 @@ export const smithCommand: OSBMahojiCommand = { } // Time to smith an item, add on quarter of a second to account for banking/etc. - let timeToSmithSingleBar = timeToUse + Time.Second / 4 - (Time.Second * 0.6 * setBonus) / 100; + const timeToSmithSingleBar = timeToUse + Time.Second / 4 - (Time.Second * 0.6 * setBonus) / 100; let maxTripLength = calcMaxTripLength(user, 'Smithing'); diff --git a/src/mahoji/commands/stats.ts b/src/mahoji/commands/stats.ts index 590a10114d9..479f273916b 100644 --- a/src/mahoji/commands/stats.ts +++ b/src/mahoji/commands/stats.ts @@ -1,10 +1,12 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Hiscores } from 'oldschooljs'; -import { ACCOUNT_TYPES, hiscoreURLs } from 'oldschooljs/dist/constants'; +import type { hiscoreURLs } from 'oldschooljs/dist/constants'; +import { ACCOUNT_TYPES } from 'oldschooljs/dist/constants'; import { statsEmbed } from '../../lib/util/statsEmbed'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; const accountTypeOptions = ACCOUNT_TYPES.map(val => { let name: string = val; diff --git a/src/mahoji/commands/steal.ts b/src/mahoji/commands/steal.ts index 7af63b93b3f..06e2db7c28b 100644 --- a/src/mahoji/commands/steal.ts +++ b/src/mahoji/commands/steal.ts @@ -1,20 +1,22 @@ import { stringMatches } from '@oldschoolgg/toolkit'; -import { User } from 'discord.js'; +import type { User } from 'discord.js'; import { randInt } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { ArdougneDiary, userhasDiaryTier } from '../../lib/diaries'; import removeFoodFromUser from '../../lib/minions/functions/removeFoodFromUser'; -import { Stealable, stealables } from '../../lib/skilling/skills/thieving/stealables'; +import type { Stealable } from '../../lib/skilling/skills/thieving/stealables'; +import { stealables } from '../../lib/skilling/skills/thieving/stealables'; import { SkillsEnum } from '../../lib/skilling/types'; -import { PickpocketActivityTaskOptions } from '../../lib/types/minions'; +import type { PickpocketActivityTaskOptions } from '../../lib/types/minions'; import { formatDuration } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../lib/util/calcMaxTripLength'; import { logError } from '../../lib/util/logError'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; import { calcLootXPPickpocketing } from '../../tasks/minions/pickpocketActivity'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { rogueOutfitPercentBonus } from '../mahojiSettings'; export const stealCommand: OSBMahojiCommand = { diff --git a/src/mahoji/commands/testpotato.ts b/src/mahoji/commands/testpotato.ts index c5adda7e57e..55f45858294 100644 --- a/src/mahoji/commands/testpotato.ts +++ b/src/mahoji/commands/testpotato.ts @@ -1,22 +1,25 @@ import { mentionCommand } from '@oldschoolgg/toolkit'; -import { Prisma, xp_gains_skill_enum } from '@prisma/client'; -import { User } from 'discord.js'; -import { noOp, Time, uniqueArr } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { Prisma } from '@prisma/client'; +import { xp_gains_skill_enum } from '@prisma/client'; +import type { User } from 'discord.js'; +import { Time, noOp, uniqueArr } from 'e'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank, Items } from 'oldschooljs'; import { convertLVLtoXP, itemID, toKMB } from 'oldschooljs/dist/util'; import { production } from '../../config'; -import { allStashUnitsFlat, allStashUnitTiers } from '../../lib/clues/stashUnits'; +import { mahojiUserSettingsUpdate } from '../../lib/MUser'; +import { allStashUnitTiers, allStashUnitsFlat } from '../../lib/clues/stashUnits'; import { CombatAchievements } from '../../lib/combat_achievements/combatAchievements'; -import { BitField, MAX_INT_JAVA } from '../../lib/constants'; +import type { BitField } from '../../lib/constants'; +import { MAX_INT_JAVA } from '../../lib/constants'; import { leaguesCreatables } from '../../lib/data/creatables/leagueCreatables'; import { Eatables } from '../../lib/data/eatables'; import { TOBMaxMageGear, TOBMaxMeleeGear, TOBMaxRangeGear } from '../../lib/data/tob'; import killableMonsters, { effectiveMonsters } from '../../lib/minions/data/killableMonsters'; import potions from '../../lib/minions/data/potions'; import { MAX_QP } from '../../lib/minions/data/quests'; -import { mahojiUserSettingsUpdate } from '../../lib/MUser'; import { allOpenables } from '../../lib/openables'; import { tiers } from '../../lib/patreon'; import { Minigames } from '../../lib/settings/minigames'; @@ -31,12 +34,8 @@ import { allSlayerMonsters } from '../../lib/slayer/tasks'; import { Gear } from '../../lib/structures/Gear'; import { stringMatches } from '../../lib/util'; import { calcDropRatesFromBankWithoutUniques } from '../../lib/util/calcDropRatesFromBank'; -import { - FarmingPatchName, - farmingPatchNames, - getFarmingKeyFromName, - userGrowingProgressStr -} from '../../lib/util/farmingHelpers'; +import type { FarmingPatchName } from '../../lib/util/farmingHelpers'; +import { farmingPatchNames, getFarmingKeyFromName, userGrowingProgressStr } from '../../lib/util/farmingHelpers'; import getOSItem from '../../lib/util/getOSItem'; import { logError } from '../../lib/util/logError'; import { parseStringBank } from '../../lib/util/parseStringBank'; @@ -45,12 +44,12 @@ import { userEventToStr } from '../../lib/util/userEvents'; import { getPOH } from '../lib/abstracted_commands/pohCommand'; import { allUsableItems } from '../lib/abstracted_commands/useCommand'; import { BingoManager } from '../lib/bingo/BingoManager'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { userStatsUpdate } from '../mahojiSettings'; import { fetchBingosThatUserIsInvolvedIn } from './bingo'; export async function giveMaxStats(user: MUser) { - let updates: Prisma.UserUpdateArgs['data'] = {}; + const updates: Prisma.UserUpdateArgs['data'] = {}; for (const skill of Object.values(xp_gains_skill_enum)) { updates[`skills_${skill}`] = convertLVLtoXP(99); } @@ -663,7 +662,7 @@ export const testPotatoCommand: OSBMahojiCommand | null = production if (options.check) { if (options.check.monster_droprates) { const monster = killableMonsters.find(m => - stringMatches(m.name, options.check!.monster_droprates) + stringMatches(m.name, options.check?.monster_droprates) ); if (!monster) return 'Invalid monster'; const qty = 1_000_000; @@ -695,9 +694,9 @@ ${droprates.join('\n')}`), } if (options.set.all_ca_tasks) { await user.update({ - completed_ca_task_ids: Object.values(CombatAchievements) - .map(i => i.tasks.map(t => t.id)) - .flat() + completed_ca_task_ids: Object.values(CombatAchievements).flatMap(i => + i.tasks.map(t => t.id) + ) }); return 'Finished all CA tasks.'; } @@ -710,7 +709,7 @@ ${droprates.join('\n')}`), return `You now ${!current ? 'ARE' : 'ARE NOT'} an ironman.`; } if (options.wipe) { - let { thing } = options.wipe; + const { thing } = options.wipe; if (thing === 'kc') { await userStatsUpdate(user.id, { monster_scores: {} @@ -945,7 +944,7 @@ ${droprates.join('\n')}`), // Set quantity to 50 if user doesn't assign a quantity const quantity = options.setslayertask?.quantity ?? 50; - const assignedTask = selectedMaster!.tasks.find(m => m.monster.id === selectedMonster?.id)!; + const assignedTask = selectedMaster?.tasks.find(m => m.monster.id === selectedMonster?.id)!; if (!selectedMaster) return 'Invalid slayer master.'; if (!selectedMonster) return 'Invalid monster.'; @@ -988,4 +987,4 @@ ${droprates.join('\n')}`), return 'Nothin!'; } - }; + }; diff --git a/src/mahoji/commands/tokkulshop.ts b/src/mahoji/commands/tokkulshop.ts index be1478d6687..8b0ac02d286 100644 --- a/src/mahoji/commands/tokkulshop.ts +++ b/src/mahoji/commands/tokkulshop.ts @@ -1,17 +1,18 @@ import { activity_type_enum } from '@prisma/client'; import { Time } from 'e'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Bank, Monsters } from 'oldschooljs'; import TokkulShopItems from '../../lib/data/buyables/tokkulBuyables'; import { KaramjaDiary, userhasDiaryTier } from '../../lib/diaries'; -import { TokkulShopOptions } from '../../lib/types/minions'; +import type { TokkulShopOptions } from '../../lib/types/minions'; import { formatDuration, stringMatches } from '../../lib/util'; import addSubTaskToActivityTask from '../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../lib/util/calcMaxTripLength'; import { handleMahojiConfirmation } from '../../lib/util/handleMahojiConfirmation'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; const { TzTokJad } = Monsters; @@ -100,8 +101,8 @@ export const tksCommand: OSBMahojiCommand = { // Get user Karamja Diary completion status, item being bought, jad kc, and ironman status const [hasKaramjaDiary] = await userhasDiaryTier(user, KaramjaDiary.easy); const item = TokkulShopItems.find(i => stringMatches(i.name, options.buy?.name ?? options.sell?.name ?? '')); - const hasKilledJad: boolean = (await user.getKC(TzTokJad.id)) >= 1 ? true : false; - const isIronman = user.user.minion_ironman ? true : false; + const hasKilledJad: boolean = (await user.getKC(TzTokJad.id)) >= 1; + const isIronman = !!user.user.minion_ironman; // If the user is buying an invalid item if (!item) return "That's not a valid item."; @@ -114,7 +115,7 @@ export const tksCommand: OSBMahojiCommand = { // User bank, maxTripLength, quantity given const { bank } = user; const maxTripLength = calcMaxTripLength(user, activity_type_enum.TokkulShop); - let quantity = options.buy?.quantity ?? options.sell?.quantity ?? 1; + const quantity = options.buy?.quantity ?? options.sell?.quantity ?? 1; const cost = new Bank(); const loot = new Bank(); @@ -133,7 +134,7 @@ export const tksCommand: OSBMahojiCommand = { } // Calculate the amount of trips needed to buy the quantity given - let amountOfRestocksNeededToBuy = shopStock ? Math.ceil(quantity / shopStock) : null; + const amountOfRestocksNeededToBuy = shopStock ? Math.ceil(quantity / shopStock) : null; // Calculate trip duration const duration = amountOfRestocksNeededToBuy @@ -156,10 +157,10 @@ export const tksCommand: OSBMahojiCommand = { // If the user doesn't have the items or tokkul if (!bank.has(cost)) return `You don't own ${cost}.`; - const action = Boolean(options.buy) ? 'buy' : 'sell'; + const action = options.buy ? 'buy' : 'sell'; // Calculate the max amount of items the user can buy or sell - let maxCanTransact = shopStock ? (maxTripLength / Time.Minute) * shopStock : (maxTripLength / 1000) * 50; + const maxCanTransact = shopStock ? (maxTripLength / Time.Minute) * shopStock : (maxTripLength / 1000) * 50; // If the duration of the trip is longer than the users max allowed trip, give the reason why and the max they can buy or sell if (duration > maxTripLength) { @@ -169,7 +170,7 @@ export const tksCommand: OSBMahojiCommand = { maxCanTransact ? `The max ${item.name.toLowerCase()}s you can ${ action === 'buy' ? 'buy' : 'sell' - } is ${maxCanTransact}` + } is ${maxCanTransact}` : '' }`; } diff --git a/src/mahoji/commands/tools.ts b/src/mahoji/commands/tools.ts index ebce95e1fe7..6c399bf28c9 100644 --- a/src/mahoji/commands/tools.ts +++ b/src/mahoji/commands/tools.ts @@ -1,9 +1,10 @@ -import { Activity, User } from '@prisma/client'; +import type { Activity, User } from '@prisma/client'; import { ChannelType, EmbedBuilder } from 'discord.js'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { Bank } from 'oldschooljs'; -import { Item, ItemBank } from 'oldschooljs/dist/meta/types'; +import type { Item, ItemBank } from 'oldschooljs/dist/meta/types'; import { ToBUniqueTable } from 'oldschooljs/dist/simulation/misc/TheatreOfBlood'; import { ClueTiers } from '../../lib/clues/clueTiers'; @@ -20,7 +21,8 @@ import { } from '../../lib/data/CollectionsExport'; import pets from '../../lib/data/pets'; import killableMonsters, { effectiveMonsters, NightmareMonster } from '../../lib/minions/data/killableMonsters'; -import { MinigameName, Minigames } from '../../lib/settings/minigames'; +import type { MinigameName } from '../../lib/settings/minigames'; +import { Minigames } from '../../lib/settings/minigames'; import { convertStoredActivityToFlatActivity, prisma } from '../../lib/settings/prisma'; import Skills from '../../lib/skilling/skills'; import { @@ -47,7 +49,7 @@ import { stashUnitViewCommand } from '../lib/abstracted_commands/stashUnitsCommand'; import { itemOption, monsterOption, skillOption } from '../lib/mahojiCommandOptions'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { patronMsg } from '../mahojiSettings'; const INTERVAL_DAY = 'day'; @@ -63,9 +65,7 @@ const whereInMassClause = (id: string) => `OR (group_activity = true AND data::jsonb ? 'users' AND data->>'users'::text LIKE '%${id}%')`; async function activityExport(user: User): CommandResponse { - const allActivities = await prisma.$queryRawUnsafe< - Activity[] - >(`SELECT floor(date_part('epoch', start_date)) AS start_date, floor(date_part('epoch', finish_date)) AS finish_date, duration, type, data + const allActivities = await prisma.$queryRawUnsafe(`SELECT floor(date_part('epoch', start_date)) AS start_date, floor(date_part('epoch', finish_date)) AS finish_date, duration, type, data FROM activity WHERE user_id = '${user.id}' OR (group_activity = true AND data::jsonb ? 'users' AND data->>'users'::text LIKE '%${user.id}%');`); @@ -219,7 +219,7 @@ async function executeXPGainsQuery( } async function xpGains(interval: string, skill?: string, ironmanOnly?: boolean) { - let intervalValue: string = ''; + let intervalValue = ''; switch (interval.toLowerCase()) { case INTERVAL_DAY: @@ -261,7 +261,7 @@ async function xpGains(interval: string, skill?: string, ironmanOnly?: boolean) } async function kcGains(interval: string, monsterName: string, ironmanOnly?: boolean): CommandResponse { - let intervalValue: string = ''; + let intervalValue = ''; switch (interval.toLowerCase()) { case INTERVAL_DAY: @@ -313,9 +313,9 @@ async function kcGains(interval: string, monsterName: string, ironmanOnly?: bool return { embeds: [embed] }; } -const clueItemsOnlyDroppedInOneTier = ClueTiers.map(i => +const clueItemsOnlyDroppedInOneTier = ClueTiers.flatMap(i => i.table.allItems.filter(itemID => ClueTiers.filter(i => i.table.allItems.includes(itemID)).length === 1) -).flat(); +); interface DrystreakMinigame { name: string; @@ -392,9 +392,7 @@ export const dryStreakEntities: DrystreakEntity[] = [ 'Olmlet' ]), run: async ({ item, ironmanOnly }) => { - const result = await prisma.$queryRawUnsafe< - { id: string; points: number; raids_total_kc: number }[] - >(`SELECT "users"."id", "user_stats".total_cox_points AS points, "minigames"."raids" + "minigames"."raids_challenge_mode" AS raids_total_kc + const result = await prisma.$queryRawUnsafe<{ id: string; points: number; raids_total_kc: number }[]>(`SELECT "users"."id", "user_stats".total_cox_points AS points, "minigames"."raids" + "minigames"."raids_challenge_mode" AS raids_total_kc FROM user_stats INNER JOIN "users" on "users"."id" = "user_stats"."user_id"::text INNER JOIN "minigames" on "minigames"."user_id" = "user_stats"."user_id"::text @@ -457,9 +455,7 @@ LIMIT 10;`); name: 'Guardians of the Rift', items: guardiansOfTheRiftCL, run: async ({ item, ironmanOnly }) => { - const result = await prisma.$queryRawUnsafe< - { id: string; val: number }[] - >(`SELECT users.id, gotr_rift_searches AS val + const result = await prisma.$queryRawUnsafe<{ id: string; val: number }[]>(`SELECT users.id, gotr_rift_searches AS val FROM users INNER JOIN "user_stats" "userstats" on "userstats"."user_id"::text = "users"."id" WHERE "collectionLogBank"->>'${item.id}' IS NULL @@ -554,9 +550,7 @@ LIMIT 10;`); name: 'Superior Slayer Creatures', items: resolveItems(['Imbued heart', 'Eternal gem']), run: async ({ ironmanOnly, item }) => { - const result = await prisma.$queryRawUnsafe< - { id: string; slayer_superior_count: number }[] - >(`SELECT id, slayer_superior_count + const result = await prisma.$queryRawUnsafe<{ id: string; slayer_superior_count: number }[]>(`SELECT id, slayer_superior_count FROM users INNER JOIN "user_stats" ON "user_stats"."user_id"::text = "users"."id" WHERE "collectionLogBank"->>'${item.id}' IS NULL @@ -629,17 +623,18 @@ async function dryStreakCommand(monsterName: string, itemName: string, ironmanOn ORDER BY ("${key}"->>'${id}')::int DESC LIMIT 10;`; - const result = await prisma.$queryRawUnsafe< - { - id: string; - KC: string; - }[] - >(query); + const result = + await prisma.$queryRawUnsafe< + { + id: string; + KC: string; + }[] + >(query); if (result.length === 0) return 'No results found.'; return `**Dry Streaks for ${item.name} from ${mon.name}:**\n${result - .map(({ id, KC }) => `${getUsername(id) as string}: ${parseInt(KC).toLocaleString()}`) + .map(({ id, KC }) => `${getUsername(id) as string}: ${Number.parseInt(KC).toLocaleString()}`) .join('\n')}`; } @@ -649,8 +644,8 @@ async function mostDrops(user: MUser, itemName: string, filter: string) { filter === 'Irons Only' ? 'AND "minion.ironman" = true' : filter === 'Mains Only' - ? 'AND "minion.ironman" = false' - : ''; + ? 'AND "minion.ironman" = false' + : ''; if (!item) return "That's not a valid item."; if (!allDroppedItems.includes(item.id) && !user.bitfield.includes(BitField.isModerator)) { return "You can't check this item, because it's not on any collection log."; @@ -658,19 +653,20 @@ async function mostDrops(user: MUser, itemName: string, filter: string) { const query = `SELECT "id", "collectionLogBank"->>'${item.id}' AS "qty" FROM users WHERE "collectionLogBank"->>'${item.id}' IS NOT NULL ${ironmanPart} ORDER BY ("collectionLogBank"->>'${item.id}')::int DESC LIMIT 10;`; - const result = await prisma.$queryRawUnsafe< - { - id: string; - qty: string; - }[] - >(query); + const result = + await prisma.$queryRawUnsafe< + { + id: string; + qty: string; + }[] + >(query); if (result.length === 0) return 'No results found.'; return `**Most '${item.name}' received:**\n${result .map( ({ id, qty }) => - `${result.length < 10 ? '(Anonymous)' : getUsername(id)}: ${parseInt(qty).toLocaleString()}` + `${result.length < 10 ? '(Anonymous)' : getUsername(id)}: ${Number.parseInt(qty).toLocaleString()}` ) .join('\n')}`; } @@ -1102,7 +1098,7 @@ export const toolsCommand: OSBMahojiCommand = { } if (options.user) { if (options.user.mypets) { - let b = new Bank(); + const b = new Bank(); for (const [pet, qty] of Object.entries(mahojiUser.user.pets as ItemBank)) { const petObj = pets.find(i => i.id === Number(pet)); if (!petObj) continue; @@ -1149,7 +1145,7 @@ export const toolsCommand: OSBMahojiCommand = { }); return `You can view your temporary CL using, for example, \`/cl name:PvM type:Temp\`. You last reset your temporary CL: ${ - Boolean(lastReset?.last_temp_cl_reset) + lastReset?.last_temp_cl_reset ? `` : 'Never' }`; diff --git a/src/mahoji/commands/trade.ts b/src/mahoji/commands/trade.ts index ff2f5319917..4296fa4c3f1 100644 --- a/src/mahoji/commands/trade.ts +++ b/src/mahoji/commands/trade.ts @@ -1,6 +1,7 @@ import { discrimName, mentionCommand, truncateString } from '@oldschoolgg/toolkit'; -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; -import { MahojiUserOption } from 'mahoji/dist/lib/types'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; +import type { MahojiUserOption } from 'mahoji/dist/lib/types'; import { Bank } from 'oldschooljs'; import { BLACKLISTED_USERS } from '../../lib/blacklists'; @@ -12,7 +13,7 @@ import itemIsTradeable from '../../lib/util/itemIsTradeable'; import { parseBank } from '../../lib/util/parseStringBank'; import { tradePlayerItems } from '../../lib/util/tradePlayerItems'; import { filterOption } from '../lib/mahojiCommandOptions'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; import { addToGPTaxBalance, mahojiParseNumber } from '../mahojiSettings'; export const tradeCommand: OSBMahojiCommand = { @@ -92,7 +93,7 @@ export const tradeCommand: OSBMahojiCommand = { filters: [options.filter], search: options.search, noDuplicateItems: true - }).filter(i => itemIsTradeable(i.id, true)); + }).filter(i => itemIsTradeable(i.id, true)); const itemsReceived = parseBank({ inputStr: options.receive, maxSize: 70, diff --git a/src/mahoji/commands/trivia.ts b/src/mahoji/commands/trivia.ts index a1ed5a8cbbd..6b1e14a9bce 100644 --- a/src/mahoji/commands/trivia.ts +++ b/src/mahoji/commands/trivia.ts @@ -1,12 +1,13 @@ -import { TextChannel, userMention } from 'discord.js'; +import type { TextChannel } from 'discord.js'; +import { userMention } from 'discord.js'; import { shuffleArr, uniqueArr } from 'e'; import { ApplicationCommandOptionType } from 'mahoji'; -import { CommandRunOptions, MahojiUserOption } from 'mahoji/dist/lib/types'; +import type { CommandRunOptions, MahojiUserOption } from 'mahoji/dist/lib/types'; import { DynamicButtons } from '../../lib/DynamicButtons'; import { getRandomTriviaQuestions } from '../../lib/roboChimp'; import { deferInteraction } from '../../lib/util/interactionReply'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const triviaCommand: OSBMahojiCommand = { name: 'trivia', @@ -46,7 +47,7 @@ export const triviaCommand: OSBMahojiCommand = { buttons.add({ name: q, fn: ({ interaction }) => { - if (question.answers.includes(q) ? true : false) { + if (question.answers.includes(q)) { correctUser = interaction.user.id; } }, diff --git a/src/mahoji/commands/use.ts b/src/mahoji/commands/use.ts index 0d159a4aa3a..0164c9cdf4b 100644 --- a/src/mahoji/commands/use.ts +++ b/src/mahoji/commands/use.ts @@ -1,8 +1,8 @@ -import { CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; import { allUsableItems, useCommand } from '../lib/abstracted_commands/useCommand'; import { ownedItemOption } from '../lib/mahojiCommandOptions'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const mahojiUseCommand: OSBMahojiCommand = { name: 'use', diff --git a/src/mahoji/commands/wiki.ts b/src/mahoji/commands/wiki.ts index 6c02afac87a..6fa1b1cff6f 100644 --- a/src/mahoji/commands/wiki.ts +++ b/src/mahoji/commands/wiki.ts @@ -1,7 +1,8 @@ -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import fetch from 'node-fetch'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const wikiCommand: OSBMahojiCommand = { name: 'wiki', diff --git a/src/mahoji/commands/xp.ts b/src/mahoji/commands/xp.ts index a74d700bee2..f463281911e 100644 --- a/src/mahoji/commands/xp.ts +++ b/src/mahoji/commands/xp.ts @@ -1,9 +1,10 @@ -import { ApplicationCommandOptionType, CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; +import { ApplicationCommandOptionType } from 'mahoji'; import { Hiscores } from 'oldschooljs'; -import { SkillsScore } from 'oldschooljs/dist/meta/types'; +import type { SkillsScore } from 'oldschooljs/dist/meta/types'; import { statsEmbed } from '../../lib/util/statsEmbed'; -import { OSBMahojiCommand } from '../lib/util'; +import type { OSBMahojiCommand } from '../lib/util'; export const xpCommand: OSBMahojiCommand = { name: 'xp', diff --git a/src/mahoji/guildSettings.ts b/src/mahoji/guildSettings.ts index a4207a55a14..594b85ac543 100644 --- a/src/mahoji/guildSettings.ts +++ b/src/mahoji/guildSettings.ts @@ -1,5 +1,5 @@ -import { Guild, Prisma } from '@prisma/client'; -import { Guild as DJSGuild } from 'discord.js'; +import type { Guild, Prisma } from '@prisma/client'; +import type { Guild as DJSGuild } from 'discord.js'; import { LRUCache } from 'lru-cache'; import { prisma } from '../lib/settings/prisma'; diff --git a/src/mahoji/lib/abstracted_commands/achievementDiaryCommand.ts b/src/mahoji/lib/abstracted_commands/achievementDiaryCommand.ts index 7337820b5e4..96063494a53 100644 --- a/src/mahoji/lib/abstracted_commands/achievementDiaryCommand.ts +++ b/src/mahoji/lib/abstracted_commands/achievementDiaryCommand.ts @@ -4,8 +4,9 @@ import { calcWhatPercent } from 'e'; import { Bank, Monsters } from 'oldschooljs'; import { diaries, userhasDiaryTier, userhasDiaryTierSync } from '../../../lib/diaries'; -import { DiaryTier } from '../../../lib/minions/types'; -import { Minigames, MinigameScore } from '../../../lib/settings/minigames'; +import type { DiaryTier } from '../../../lib/minions/types'; +import type { MinigameScore } from '../../../lib/settings/minigames'; +import { Minigames } from '../../../lib/settings/minigames'; import { MUserStats } from '../../../lib/structures/MUserStats'; import { formatSkillRequirements, itemNameFromID, stringMatches } from '../../../lib/util'; diff --git a/src/mahoji/lib/abstracted_commands/aerialFishingCommand.ts b/src/mahoji/lib/abstracted_commands/aerialFishingCommand.ts index 1b4d82bdc36..d8e69518b24 100644 --- a/src/mahoji/lib/abstracted_commands/aerialFishingCommand.ts +++ b/src/mahoji/lib/abstracted_commands/aerialFishingCommand.ts @@ -1,7 +1,7 @@ import { Time } from 'e'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; import { formatDuration, randomVariation } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; diff --git a/src/mahoji/lib/abstracted_commands/agilityArenaCommand.ts b/src/mahoji/lib/abstracted_commands/agilityArenaCommand.ts index 548a1cbd6b9..dc542ad2729 100644 --- a/src/mahoji/lib/abstracted_commands/agilityArenaCommand.ts +++ b/src/mahoji/lib/abstracted_commands/agilityArenaCommand.ts @@ -1,4 +1,4 @@ -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { Bank } from 'oldschooljs'; import { KaramjaDiary, userhasDiaryTier } from '../../../lib/diaries'; @@ -137,7 +137,7 @@ export async function agilityArenaBuyCommand(user: MUser, input: string, qty = 1 export async function agilityArenaRecolorCommand(user: MUser) { const { bank } = user; - let ticketCost = 250; + const ticketCost = 250; if (!bank.has(plainGraceful)) { return mahojiChatHead({ content: "Ye don't have a full set of Graceful in your bank for me to recolor!", @@ -180,9 +180,7 @@ export async function agilityArenaXPCommand(user: MUser, qty: number): CommandRe } const [hasKaramjaMed] = await userhasDiaryTier(user, KaramjaDiary.medium); const xpToGive = determineXPFromTickets(qty, user, hasKaramjaMed); - let str = `Redeemed ${qty}x Agility arena tickets for ${xpToGive.toLocaleString()} Agility XP. (${( - xpToGive / qty - ).toFixed(2)} ea)`; + let str = `Redeemed ${qty}x Agility arena tickets for ${xpToGive.toLocaleString()} Agility XP. (${(xpToGive / qty).toFixed(2)} ea)`; await transactItems({ userID: user.id, itemsToRemove: new Bank().add('Agility arena ticket', qty) }); await user.addXP({ skillName: SkillsEnum.Agility, diff --git a/src/mahoji/lib/abstracted_commands/alchCommand.ts b/src/mahoji/lib/abstracted_commands/alchCommand.ts index ec1bc9aae97..01034486070 100644 --- a/src/mahoji/lib/abstracted_commands/alchCommand.ts +++ b/src/mahoji/lib/abstracted_commands/alchCommand.ts @@ -1,8 +1,8 @@ -import { ChatInputCommandInteraction } from 'discord.js'; -import { clamp, Time } from 'e'; +import type { ChatInputCommandInteraction } from 'discord.js'; +import { Time, clamp } from 'e'; import { Bank } from 'oldschooljs'; import { SkillsEnum } from 'oldschooljs/dist/constants'; -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; import type { AlchingActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, toKMB } from '../../../lib/util'; diff --git a/src/mahoji/lib/abstracted_commands/autoSlayCommand.ts b/src/mahoji/lib/abstracted_commands/autoSlayCommand.ts index fbb695f06a6..5aa82d66114 100644 --- a/src/mahoji/lib/abstracted_commands/autoSlayCommand.ts +++ b/src/mahoji/lib/abstracted_commands/autoSlayCommand.ts @@ -1,13 +1,13 @@ import { isGuildChannel } from '@oldschoolgg/toolkit'; -import { ChatInputCommandInteraction } from 'discord.js'; -import { CommandOptions } from 'mahoji/dist/lib/types'; +import type { ChatInputCommandInteraction } from 'discord.js'; +import type { CommandOptions } from 'mahoji/dist/lib/types'; import { Monsters } from 'oldschooljs'; -import { PvMMethod } from '../../../lib/constants'; +import type { PvMMethod } from '../../../lib/constants'; import killableMonsters from '../../../lib/minions/data/killableMonsters'; import { runCommand } from '../../../lib/settings/settings'; -import { autoslayModes, AutoslayOptionsEnum } from '../../../lib/slayer/constants'; -import { getCommonTaskName, getUsersCurrentSlayerInfo, SlayerMasterEnum } from '../../../lib/slayer/slayerUtil'; +import { AutoslayOptionsEnum, autoslayModes } from '../../../lib/slayer/constants'; +import { SlayerMasterEnum, getCommonTaskName, getUsersCurrentSlayerInfo } from '../../../lib/slayer/slayerUtil'; import { hasSkillReqs, stringMatches } from '../../../lib/util'; import { interactionReply } from '../../../lib/util/interactionReply'; import { slayerNewTaskCommand } from './slayerTaskCommand'; @@ -270,7 +270,10 @@ export async function autoSlayCommand({ let currentLow = Number.POSITIVE_INFINITY; let currentMonID: number | null = null; - for (const monsterID of usersTask.assignedTask!.monsters) { + if (!usersTask.assignedTask) { + throw new Error('User had no assignedTask?'); + } + for (const monsterID of usersTask.assignedTask.monsters) { const osjsM = Monsters.get(monsterID); if (osjsM && osjsM.data.combatLevel < currentLow) { currentLow = osjsM.data.combatLevel; @@ -292,8 +295,8 @@ export async function autoSlayCommand({ } if (method === 'ehp') { const ehpMonster = AutoSlayMaxEfficiencyTable.find(e => { - const masterMatch = !e.slayerMasters || e.slayerMasters.includes(usersTask.currentTask!.slayer_master_id); - return masterMatch && e.monsterID === usersTask.assignedTask!.monster.id; + const masterMatch = !e.slayerMasters || e.slayerMasters.includes(usersTask.currentTask?.slayer_master_id); + return masterMatch && e.monsterID === usersTask.assignedTask?.monster.id; }); const ehpKillable = killableMonsters.find(m => m.id === ehpMonster?.efficientMonster); @@ -301,15 +304,15 @@ export async function autoSlayCommand({ // If we don't have the requirements for the efficient monster, revert to default monster if ( (ehpKillable?.levelRequirements !== undefined && !hasSkillReqs(user, ehpKillable.levelRequirements)[0]) || - (usersTask.currentTask!.slayer_master_id === 8 && + (usersTask.currentTask?.slayer_master_id === 8 && [Monsters.Jelly.id, Monsters.Bloodveld.id, Monsters.BlackDragon.id].includes( - usersTask.assignedTask!.monster.id + usersTask.assignedTask?.monster.id )) ) { runCommand({ commandName: 'k', args: { - name: usersTask.assignedTask!.monster.name + name: usersTask.assignedTask?.monster.name }, bypassInhibitors: true, ...cmdRunOptions @@ -317,8 +320,8 @@ export async function autoSlayCommand({ return; } - if (ehpMonster && ehpMonster.efficientName) { - let args: CommandOptions = { + if (ehpMonster?.efficientName) { + const args: CommandOptions = { name: ehpMonster.efficientName }; if (ehpMonster.efficientMethod) { @@ -335,7 +338,7 @@ export async function autoSlayCommand({ runCommand({ commandName: 'k', args: { - name: usersTask.assignedTask!.monster.name + name: usersTask.assignedTask?.monster.name }, bypassInhibitors: true, ...cmdRunOptions @@ -345,7 +348,7 @@ export async function autoSlayCommand({ if (method === 'boss') { // This code handles the 'highest/boss' setting of autoslay. const myQPs = await user.QP; - let commonName = getCommonTaskName(usersTask.assignedTask!.monster); + const commonName = getCommonTaskName(usersTask.assignedTask!.monster); if (commonName === 'TzHaar') { runCommand({ commandName: 'activities', @@ -357,7 +360,7 @@ export async function autoSlayCommand({ } const allMonsters = killableMonsters.filter(m => { - return usersTask.assignedTask!.monsters.includes(m.id); + return usersTask.assignedTask?.monsters.includes(m.id); }); if (allMonsters.length === 0) return 'Please report this error. No monster variations found.'; let maxDiff = 0; @@ -392,7 +395,7 @@ export async function autoSlayCommand({ } await runCommand({ commandName: 'k', - args: { name: usersTask.assignedTask!.monster.name }, + args: { name: usersTask.assignedTask?.monster.name }, bypassInhibitors: true, ...cmdRunOptions }); diff --git a/src/mahoji/lib/abstracted_commands/bankBgCommand.ts b/src/mahoji/lib/abstracted_commands/bankBgCommand.ts index f00324ef42e..4c6838cc262 100644 --- a/src/mahoji/lib/abstracted_commands/bankBgCommand.ts +++ b/src/mahoji/lib/abstracted_commands/bankBgCommand.ts @@ -1,4 +1,4 @@ -import { ChatInputCommandInteraction } from 'discord.js'; +import type { ChatInputCommandInteraction } from 'discord.js'; import { Bank } from 'oldschooljs'; import { BitField } from '../../../lib/constants'; @@ -84,7 +84,7 @@ export async function bankBgCommand(interaction: ChatInputCommandInteraction, us /** * If this bank image has a gp or item cost, confirm and charge. */ - let economyCost = new Bank(); + const economyCost = new Bank(); if (selectedImage.gpCost || selectedImage.itemCost) { const userBank = user.bank; diff --git a/src/mahoji/lib/abstracted_commands/barbAssault.ts b/src/mahoji/lib/abstracted_commands/barbAssault.ts index a3eed89c165..c9f56d339be 100644 --- a/src/mahoji/lib/abstracted_commands/barbAssault.ts +++ b/src/mahoji/lib/abstracted_commands/barbAssault.ts @@ -1,7 +1,7 @@ import { formatOrdinal } from '@oldschoolgg/toolkit'; -import { ButtonBuilder, ChatInputCommandInteraction } from 'discord.js'; -import { calcWhatPercent, clamp, reduceNumByPercent, roll, round, Time } from 'e'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { ButtonBuilder, ChatInputCommandInteraction } from 'discord.js'; +import { Time, calcWhatPercent, clamp, reduceNumByPercent, roll, round } from 'e'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { Bank } from 'oldschooljs'; import { buildClueButtons } from '../../../lib/clues/clueUtils'; @@ -143,15 +143,11 @@ export async function barbAssaultBuyCommand( const stats = await user.fetchStats({ honour_points: true }); const balance = stats.honour_points; if (balance < cost * quantity) { - return `You don't have enough Honour Points to buy ${quantity.toLocaleString()}x ${item.name}. You need ${( - cost * quantity - ).toLocaleString()}, but you have only ${balance.toLocaleString()}.`; + return `You don't have enough Honour Points to buy ${quantity.toLocaleString()}x ${item.name}. You need ${(cost * quantity).toLocaleString()}, but you have only ${balance.toLocaleString()}.`; } await handleMahojiConfirmation( interaction, - `Are you sure you want to buy ${quantity.toLocaleString()}x ${item.name}, for ${( - cost * quantity - ).toLocaleString()} honour points?` + `Are you sure you want to buy ${quantity.toLocaleString()}x ${item.name}, for ${(cost * quantity).toLocaleString()} honour points?` ); await userStatsUpdate( user.id, @@ -165,9 +161,7 @@ export async function barbAssaultBuyCommand( await user.addItemsToBank({ items: new Bank().add(item.id, quantity), collectionLog: true }); - return `Successfully purchased ${quantity.toLocaleString()}x ${item.name} for ${( - cost * quantity - ).toLocaleString()} Honour Points.`; + return `Successfully purchased ${quantity.toLocaleString()}x ${item.name} for ${(cost * quantity).toLocaleString()} Honour Points.`; } export async function barbAssaultGambleCommand( @@ -183,15 +177,11 @@ export async function barbAssaultGambleCommand( const { honour_points: balance } = await user.fetchStats({ honour_points: true }); const { cost, name, table } = buyable; if (balance < cost * quantity) { - return `You don't have enough Honour Points to do ${quantity.toLocaleString()}x ${name} gamble. You need ${( - cost * quantity - ).toLocaleString()}, but you have only ${balance.toLocaleString()}.`; + return `You don't have enough Honour Points to do ${quantity.toLocaleString()}x ${name} gamble. You need ${(cost * quantity).toLocaleString()}, but you have only ${balance.toLocaleString()}.`; } await handleMahojiConfirmation( interaction, - `Are you sure you want to do ${quantity.toLocaleString()}x ${name} gamble, using ${( - cost * quantity - ).toLocaleString()} honour points?` + `Are you sure you want to do ${quantity.toLocaleString()}x ${name} gamble, using ${(cost * quantity).toLocaleString()} honour points?` ); const newStats = await userStatsUpdate( user.id, @@ -203,7 +193,7 @@ export async function barbAssaultGambleCommand( name === 'High' ? { increment: quantity - } + } : undefined }, { @@ -229,10 +219,8 @@ export async function barbAssaultGambleCommand( const perkTier = user.perkTier(); const components: ButtonBuilder[] = buildClueButtons(loot, perkTier, user); - let response: Awaited = { - content: `You spent ${( - cost * quantity - ).toLocaleString()} Honour Points for ${quantity.toLocaleString()}x ${name} Gamble, and received...`, + const response: Awaited = { + content: `You spent ${(cost * quantity).toLocaleString()} Honour Points for ${quantity.toLocaleString()}x ${name} Gamble, and received...`, files: [ ( await makeBankImage({ diff --git a/src/mahoji/lib/abstracted_commands/birdhousesCommand.ts b/src/mahoji/lib/abstracted_commands/birdhousesCommand.ts index 52f9cea436e..fb1dd671edf 100644 --- a/src/mahoji/lib/abstracted_commands/birdhousesCommand.ts +++ b/src/mahoji/lib/abstracted_commands/birdhousesCommand.ts @@ -1,10 +1,12 @@ -import { User } from '@prisma/client'; +import type { User } from '@prisma/client'; import { time } from 'discord.js'; import { Bank } from 'oldschooljs'; -import birdhouses, { Birdhouse, birdhouseSeeds } from '../../../lib/skilling/skills/hunter/birdHouseTrapping'; -import defaultBirdhouseTrap, { BirdhouseData } from '../../../lib/skilling/skills/hunter/defaultBirdHouseTrap'; -import { BirdhouseActivityTaskOptions } from '../../../lib/types/minions'; +import type { Birdhouse } from '../../../lib/skilling/skills/hunter/birdHouseTrapping'; +import birdhouses, { birdhouseSeeds } from '../../../lib/skilling/skills/hunter/birdHouseTrapping'; +import type { BirdhouseData } from '../../../lib/skilling/skills/hunter/defaultBirdHouseTrap'; +import defaultBirdhouseTrap from '../../../lib/skilling/skills/hunter/defaultBirdHouseTrap'; +import type { BirdhouseActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, stringMatches } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { updateBankSetting } from '../../../lib/util/updateBankSetting'; @@ -75,7 +77,7 @@ export async function birdhouseHarvestCommand(user: MUser, channelID: string, in stringMatches(alias, inputBirdhouseName) || stringMatches(alias.split(' ')[0], inputBirdhouseName) ) - ) + ) : undefined; if (!birdhouseToPlant && existingBirdhouse.birdHouse) birdhouseToPlant = existingBirdhouse.birdHouse; @@ -102,7 +104,7 @@ export async function birdhouseHarvestCommand(user: MUser, channelID: string, in duration *= 0.9; } let gotCraft = false; - let removeBank = new Bank(); + const removeBank = new Bank(); const premadeBankCost = birdhouseToPlant.houseItemReq.clone().multiply(4); if (user.owns(premadeBankCost)) { removeBank.add(premadeBankCost); diff --git a/src/mahoji/lib/abstracted_commands/buryCommand.ts b/src/mahoji/lib/abstracted_commands/buryCommand.ts index c0a4455d364..4cd8be7c572 100644 --- a/src/mahoji/lib/abstracted_commands/buryCommand.ts +++ b/src/mahoji/lib/abstracted_commands/buryCommand.ts @@ -3,7 +3,7 @@ import { Bank } from 'oldschooljs'; import Prayer from '../../../lib/skilling/skills/prayer'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { BuryingActivityTaskOptions } from '../../../lib/types/minions'; +import type { BuryingActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, stringMatches } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; diff --git a/src/mahoji/lib/abstracted_commands/butlerCommand.ts b/src/mahoji/lib/abstracted_commands/butlerCommand.ts index 3526f05b4fc..a989347349e 100644 --- a/src/mahoji/lib/abstracted_commands/butlerCommand.ts +++ b/src/mahoji/lib/abstracted_commands/butlerCommand.ts @@ -1,9 +1,9 @@ -import { clamp, Time } from 'e'; +import { Time, clamp } from 'e'; import { Bank } from 'oldschooljs'; import { Planks } from '../../../lib/minions/data/planks'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { ButlerActivityTaskOptions } from '../../../lib/types/minions'; +import type { ButlerActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, itemNameFromID, stringMatches, toKMB } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -48,7 +48,7 @@ export async function butlerCommand(user: MUser, plankName: string, quantity: nu return 'You need level 50 Construction to use the demon butler.'; } - let timePerPlank = (Time.Second * 15) / 26; + const timePerPlank = (Time.Second * 15) / 26; const maxTripLength = calcMaxTripLength(user, 'Butler'); @@ -67,9 +67,9 @@ export async function butlerCommand(user: MUser, plankName: string, quantity: nu } const { GP } = user; - let cost = plank!.gpCost * quantity; + let cost = plank?.gpCost * quantity; - let inventories = Math.ceil(quantity / 26); + const inventories = Math.ceil(quantity / 26); cost += 1250 * inventories; @@ -130,7 +130,7 @@ export async function butlerCommand(user: MUser, plankName: string, quantity: nu )}.`; } - const costBank = new Bank(consumedItems).add('Coins', cost).add(plank!.inputItem, quantity); + const costBank = new Bank(consumedItems).add('Coins', cost).add(plank?.inputItem, quantity); await user.removeItemsFromBank(costBank); await updateBankSetting('construction_cost_bank', new Bank().add('Coins', cost)); @@ -138,7 +138,7 @@ export async function butlerCommand(user: MUser, plankName: string, quantity: nu await addSubTaskToActivityTask({ type: 'Butler', duration, - plankID: plank!.outputItem, + plankID: plank?.outputItem, plankQuantity: quantity, userID: user.id, channelID: channelID.toString() diff --git a/src/mahoji/lib/abstracted_commands/buyFossilIslandNotes.ts b/src/mahoji/lib/abstracted_commands/buyFossilIslandNotes.ts index 04de7549175..cb8a978eb62 100644 --- a/src/mahoji/lib/abstracted_commands/buyFossilIslandNotes.ts +++ b/src/mahoji/lib/abstracted_commands/buyFossilIslandNotes.ts @@ -1,4 +1,4 @@ -import { ChatInputCommandInteraction } from 'discord.js'; +import type { ChatInputCommandInteraction } from 'discord.js'; import { randArrItem } from 'e'; import { Bank } from 'oldschooljs'; @@ -24,7 +24,7 @@ export async function buyFossilIslandNotes(user: MUser, interaction: ChatInputCo ); const tempClWithNewUniques = user.cl ? user.cl.clone() : new Bank(); - let loot = new Bank(); + const loot = new Bank(); for (let i = 0; i < quantity; i++) { const filteredPages = fossilIslandNotesCL.filter(page => !tempClWithNewUniques.has(page)); const outPage = diff --git a/src/mahoji/lib/abstracted_commands/camdozaalCommand.ts b/src/mahoji/lib/abstracted_commands/camdozaalCommand.ts index b61f0055fea..5d6a720a47d 100644 --- a/src/mahoji/lib/abstracted_commands/camdozaalCommand.ts +++ b/src/mahoji/lib/abstracted_commands/camdozaalCommand.ts @@ -1,4 +1,4 @@ -import { increaseNumByPercent, reduceNumByPercent, Time } from 'e'; +import { Time, increaseNumByPercent, reduceNumByPercent } from 'e'; import { Bank } from 'oldschooljs'; import { SkillsEnum } from 'oldschooljs/dist/constants'; @@ -6,7 +6,7 @@ import { determineMiningTime } from '../../../lib/skilling/functions/determineMi import { pickaxes } from '../../../lib/skilling/functions/miningBoosts'; import Fishing from '../../../lib/skilling/skills/fishing'; import Mining from '../../../lib/skilling/skills/mining'; -import { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; import { formatDuration, itemNameFromID, randomVariation } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -45,7 +45,7 @@ async function miningCommand(user: MUser, channelID: string, quantity: number | const barroniteRocks = Mining.CamdozaalMine; // Calculate the time it takes to mine specific quantity or as many as possible - let [duration, newQuantity] = determineMiningTime({ + const [duration, newQuantity] = determineMiningTime({ quantity, user, ore: barroniteRocks, @@ -137,7 +137,7 @@ async function fishingCommand(user: MUser, channelID: string, quantity: number | const maxTripLength = calcMaxTripLength(user, 'CamdozaalFishing'); const camdozaalfish = Fishing.camdozaalFishes.find(_fish => _fish.name === 'Raw guppy')!; - let timePerFish = camdozaalfish.timePerFish * Time.Second; + const timePerFish = camdozaalfish.timePerFish * Time.Second; if (!quantity) { quantity = Math.floor(maxTripLength / timePerFish); diff --git a/src/mahoji/lib/abstracted_commands/cancelTaskCommand.ts b/src/mahoji/lib/abstracted_commands/cancelTaskCommand.ts index 75a3c3beae9..4539fde6250 100644 --- a/src/mahoji/lib/abstracted_commands/cancelTaskCommand.ts +++ b/src/mahoji/lib/abstracted_commands/cancelTaskCommand.ts @@ -1,7 +1,7 @@ -import { ChatInputCommandInteraction } from 'discord.js'; +import type { ChatInputCommandInteraction } from 'discord.js'; import { cancelTask } from '../../../lib/settings/settings'; -import { NexTaskOptions, RaidsOptions } from '../../../lib/types/minions'; +import type { NexTaskOptions, RaidsOptions } from '../../../lib/types/minions'; import { handleMahojiConfirmation } from '../../../lib/util/handleMahojiConfirmation'; import { getActivityOfUser } from '../../../lib/util/minionIsBusy'; diff --git a/src/mahoji/lib/abstracted_commands/capegamble.ts b/src/mahoji/lib/abstracted_commands/capegamble.ts index 62cada7739f..bb7bbc509da 100644 --- a/src/mahoji/lib/abstracted_commands/capegamble.ts +++ b/src/mahoji/lib/abstracted_commands/capegamble.ts @@ -1,5 +1,5 @@ import { formatOrdinal } from '@oldschoolgg/toolkit'; -import { ChatInputCommandInteraction } from 'discord.js'; +import type { ChatInputCommandInteraction } from 'discord.js'; import { Bank } from 'oldschooljs'; import { Events } from '../../../lib/constants'; @@ -68,7 +68,7 @@ export async function capeGambleCommand( user: MUser, type: string, interaction: ChatInputCommandInteraction, - autoconfirm: boolean = false + autoconfirm = false ) { const src = itemGambles.find(i => i.type === type); if (!src) return 'Invalid type. You can only gamble fire capes, infernal capes, or quivers.'; diff --git a/src/mahoji/lib/abstracted_commands/castCommand.ts b/src/mahoji/lib/abstracted_commands/castCommand.ts index 1fe1bc1eabe..2466abe113c 100644 --- a/src/mahoji/lib/abstracted_commands/castCommand.ts +++ b/src/mahoji/lib/abstracted_commands/castCommand.ts @@ -1,8 +1,8 @@ -import { reduceNumByPercent, Time } from 'e'; +import { Time, reduceNumByPercent } from 'e'; import { SkillsEnum } from 'oldschooljs/dist/constants'; import { Castables } from '../../../lib/skilling/skills/magic/castables'; -import { CastingActivityTaskOptions } from '../../../lib/types/minions'; +import type { CastingActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, stringMatches } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; diff --git a/src/mahoji/lib/abstracted_commands/chargeGloriesCommand.ts b/src/mahoji/lib/abstracted_commands/chargeGloriesCommand.ts index 303551cf9a6..43cfad92b52 100644 --- a/src/mahoji/lib/abstracted_commands/chargeGloriesCommand.ts +++ b/src/mahoji/lib/abstracted_commands/chargeGloriesCommand.ts @@ -1,8 +1,8 @@ import { Time } from 'e'; import { Bank } from 'oldschooljs'; -import { userhasDiaryTier, WildernessDiary } from '../../../lib/diaries'; -import { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; +import { WildernessDiary, userhasDiaryTier } from '../../../lib/diaries'; +import type { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; import { formatDuration } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; diff --git a/src/mahoji/lib/abstracted_commands/chargeWealthCommand.ts b/src/mahoji/lib/abstracted_commands/chargeWealthCommand.ts index 24ac73bc63b..25508bfde2a 100644 --- a/src/mahoji/lib/abstracted_commands/chargeWealthCommand.ts +++ b/src/mahoji/lib/abstracted_commands/chargeWealthCommand.ts @@ -1,8 +1,8 @@ import { Time } from 'e'; import { Bank } from 'oldschooljs'; -import { userhasDiaryTier, WildernessDiary } from '../../../lib/diaries'; -import { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; +import { WildernessDiary, userhasDiaryTier } from '../../../lib/diaries'; +import type { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; import { formatDuration } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; diff --git a/src/mahoji/lib/abstracted_commands/chompyHuntCommand.ts b/src/mahoji/lib/abstracted_commands/chompyHuntCommand.ts index 0093a48bb60..cab490ab79d 100644 --- a/src/mahoji/lib/abstracted_commands/chompyHuntCommand.ts +++ b/src/mahoji/lib/abstracted_commands/chompyHuntCommand.ts @@ -1,8 +1,8 @@ -import { percentChance, reduceNumByPercent, Time } from 'e'; +import { Time, percentChance, reduceNumByPercent } from 'e'; import { Bank } from 'oldschooljs'; import { chompyHats } from '../../../lib/constants'; -import { userhasDiaryTier, WesternProv } from '../../../lib/diaries'; +import { WesternProv, userhasDiaryTier } from '../../../lib/diaries'; import { getMinigameScore } from '../../../lib/settings/minigames'; import type { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; import { formatDuration } from '../../../lib/util'; @@ -50,7 +50,7 @@ export async function chompyHuntCommand(user: MUser, channelID: string) { const tripLength = calcMaxTripLength(user, 'BigChompyBirdHunting'); - let boosts = []; + const boosts = []; let quantity = Math.floor((baseChompyPerHour / Time.Hour) * tripLength); for (const [diary, boost] of diaryBoosts) { const [hasDiary] = await userhasDiaryTier(user, diary); diff --git a/src/mahoji/lib/abstracted_commands/collectCommand.ts b/src/mahoji/lib/abstracted_commands/collectCommand.ts index 91fea4da964..3d4bf50a02d 100644 --- a/src/mahoji/lib/abstracted_commands/collectCommand.ts +++ b/src/mahoji/lib/abstracted_commands/collectCommand.ts @@ -1,11 +1,11 @@ import { Time } from 'e'; import { Bank } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; -import { userhasDiaryTier, WildernessDiary } from '../../../lib/diaries'; -import { SkillsEnum } from '../../../lib/skilling/types'; -import { Skills } from '../../../lib/types'; -import { CollectingOptions } from '../../../lib/types/minions'; +import { WildernessDiary, userhasDiaryTier } from '../../../lib/diaries'; +import type { SkillsEnum } from '../../../lib/skilling/types'; +import type { Skills } from '../../../lib/types'; +import type { CollectingOptions } from '../../../lib/types/minions'; import { formatDuration, stringMatches } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -210,11 +210,9 @@ export async function collectCommand( let cost: Bank = new Bank(); if (collectable.itemCost) { - { - cost = collectable.itemCost.clone().multiply(quantity); - if (cost.has('Ring of dueling(8)') && hasJewelleryBox) - cost.remove('Ring of dueling(8)', cost.amount('Ring of dueling(8)')); - } + cost = collectable.itemCost.clone().multiply(quantity); + if (cost.has('Ring of dueling(8)') && hasJewelleryBox) + cost.remove('Ring of dueling(8)', cost.amount('Ring of dueling(8)')); if (cost.has('Stamina potion(4)') && no_stams) { // 50% longer trip time for not using stamina potion (4) duration *= 1.5; diff --git a/src/mahoji/lib/abstracted_commands/coxCommand.ts b/src/mahoji/lib/abstracted_commands/coxCommand.ts index a2e44968399..366ed284e0c 100644 --- a/src/mahoji/lib/abstracted_commands/coxCommand.ts +++ b/src/mahoji/lib/abstracted_commands/coxCommand.ts @@ -15,8 +15,8 @@ import { degradeItem } from '../../../lib/degradeableItems'; import { trackLoot } from '../../../lib/lootTrack'; import { setupParty } from '../../../lib/party'; import { getMinigameScore } from '../../../lib/settings/minigames'; -import { MakePartyOptions } from '../../../lib/types'; -import { RaidsOptions } from '../../../lib/types/minions'; +import type { MakePartyOptions } from '../../../lib/types'; +import type { RaidsOptions } from '../../../lib/types/minions'; import { channelIsSendable, formatDuration, randomVariation } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -62,9 +62,7 @@ export async function coxStatsCommand(user: MUser) { )}%, Team: ${Emoji.Skull} ${(await createTeam(Array(2).fill(user), false))[0].deathChance.toFixed(1)}% ${ Emoji.CombatSword } ${calcWhatPercent(normalTeam.reductions[user.id], normalTeam.maxUserReduction).toFixed(1)}%) -**Challenge Mode:** ${minigameScores.raids_challenge_mode} KC (Solo: ${Emoji.Skull} ${( - await createTeam([user], true) - )[0].deathChance.toFixed(1)}% ${Emoji.CombatSword} ${calcWhatPercent( +**Challenge Mode:** ${minigameScores.raids_challenge_mode} KC (Solo: ${Emoji.Skull} ${(await createTeam([user], true))[0].deathChance.toFixed(1)}% ${Emoji.CombatSword} ${calcWhatPercent( cmSolo.reductions[user.id], cmSolo.maxUserReduction ).toFixed(1)}%, Team: ${Emoji.Skull} ${(await createTeam(Array(2).fill(user), true))[0].deathChance.toFixed(1)}% ${ @@ -104,7 +102,7 @@ export async function coxCommand( return "Your minion is busy, so you can't start a raid."; } - let maxSize = mahojiParseNumber({ input: maxSizeInput, min: 2, max: 15 }) ?? 15; + const maxSize = mahojiParseNumber({ input: maxSizeInput, min: 2, max: 15 }) ?? 15; const partyOptions: MakePartyOptions = { leader: user, @@ -178,7 +176,7 @@ export async function coxCommand( } // This gives a normal duration distribution. Better than (raidDuration * quantity) +/- 5% - let duration = sumArr( + const duration = sumArr( Array(quantity) .fill(raidDuration) .map(d => randomVariation(d, 5)) @@ -237,12 +235,12 @@ export async function coxCommand( let str = isSolo ? `${user.minionName} is now doing ${quantity > 1 ? quantity : 'a'} Chambers of Xeric raid${ quantity > 1 ? 's' : '' - }. The total trip will take ${formatDuration(duration)}.` + }. The total trip will take ${formatDuration(duration)}.` : `${partyOptions.leader.usernameOrMention}'s party (${users .map(u => u.usernameOrMention) .join(', ')}) is now off to do ${quantity > 1 ? quantity : 'a'} Chambers of Xeric raid${ quantity > 1 ? 's' : '' - } - the total trip will take ${formatDuration(duration)}.`; + } - the total trip will take ${formatDuration(duration)}.`; str += ` \n\n${debugStr}`; diff --git a/src/mahoji/lib/abstracted_commands/crackerCommand.ts b/src/mahoji/lib/abstracted_commands/crackerCommand.ts index 8a40165659f..31c0b41ee3c 100644 --- a/src/mahoji/lib/abstracted_commands/crackerCommand.ts +++ b/src/mahoji/lib/abstracted_commands/crackerCommand.ts @@ -1,4 +1,4 @@ -import { ChatInputCommandInteraction, User } from 'discord.js'; +import type { ChatInputCommandInteraction, User } from 'discord.js'; import { shuffleArr } from 'e'; import { Bank, LootTable } from 'oldschooljs'; diff --git a/src/mahoji/lib/abstracted_commands/cutLeapingFishCommand.ts b/src/mahoji/lib/abstracted_commands/cutLeapingFishCommand.ts index caad11a58b5..0d2a0f0d586 100644 --- a/src/mahoji/lib/abstracted_commands/cutLeapingFishCommand.ts +++ b/src/mahoji/lib/abstracted_commands/cutLeapingFishCommand.ts @@ -2,7 +2,7 @@ import { Time } from 'e'; import { Bank } from 'oldschooljs'; import LeapingFish from '../../../lib/skilling/skills/cooking/leapingFish'; -import { CutLeapingFishActivityTaskOptions } from '../../../lib/types/minions'; +import type { CutLeapingFishActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, stringMatches } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -27,9 +27,9 @@ export async function cutLeapingFishCommand({ if (!barbarianFish) return 'That is not a valid fish to cut.'; - let requiredItems = barbarianFish.item.name; + const requiredItems = barbarianFish.item.name; - let timeToCutSingleItem = barbarianFish.tickRate * Time.Second * 0.6; + const timeToCutSingleItem = barbarianFish.tickRate * Time.Second * 0.6; const maxTripLength = calcMaxTripLength(user, 'Cooking'); diff --git a/src/mahoji/lib/abstracted_commands/dailyCommand.ts b/src/mahoji/lib/abstracted_commands/dailyCommand.ts index 8c43c953a32..b0c9bd13fda 100644 --- a/src/mahoji/lib/abstracted_commands/dailyCommand.ts +++ b/src/mahoji/lib/abstracted_commands/dailyCommand.ts @@ -1,13 +1,13 @@ -import { ChatInputCommandInteraction, TextChannel } from 'discord.js'; -import { roll, shuffleArr, Time, uniqueArr } from 'e'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { ChatInputCommandInteraction, TextChannel } from 'discord.js'; +import { Time, roll, shuffleArr, uniqueArr } from 'e'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { Bank } from 'oldschooljs'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; import { SupportServer } from '../../../config'; +import { DynamicButtons } from '../../../lib/DynamicButtons'; import { COINS_ID, Emoji } from '../../../lib/constants'; import pets from '../../../lib/data/pets'; -import { DynamicButtons } from '../../../lib/DynamicButtons'; import { getRandomTriviaQuestions } from '../../../lib/roboChimp'; import dailyRoll from '../../../lib/simulation/dailyTable'; import { channelIsSendable, formatDuration, isWeekend } from '../../../lib/util'; @@ -155,7 +155,7 @@ export async function dailyCommand( buttons.add({ name: answer, fn: ({ interaction }) => { - if (question.answers.includes(answer) ? true : false) { + if (question.answers.includes(answer)) { correctUser = interaction.user.id; } }, diff --git a/src/mahoji/lib/abstracted_commands/diceCommand.ts b/src/mahoji/lib/abstracted_commands/diceCommand.ts index cac29c5e7fb..b53ebbbe981 100644 --- a/src/mahoji/lib/abstracted_commands/diceCommand.ts +++ b/src/mahoji/lib/abstracted_commands/diceCommand.ts @@ -1,4 +1,4 @@ -import { ChatInputCommandInteraction } from 'discord.js'; +import type { ChatInputCommandInteraction } from 'discord.js'; import { Bank, Util } from 'oldschooljs'; import { cryptoRand } from '../../../lib/util'; @@ -35,7 +35,7 @@ export async function diceCommand(user: MUser, interaction: ChatInputCommandInte const gp = user.GP; if (amount > gp) return "You don't have enough GP."; const won = roll >= 55; - let amountToAdd = won ? amount : -amount; + const amountToAdd = won ? amount : -amount; await updateClientGPTrackSetting('gp_dice', amountToAdd); await updateGPTrackSetting('gp_dice', amountToAdd, user); diff --git a/src/mahoji/lib/abstracted_commands/driftNetCommand.ts b/src/mahoji/lib/abstracted_commands/driftNetCommand.ts index c647ff06dc0..b1c34bdc73f 100644 --- a/src/mahoji/lib/abstracted_commands/driftNetCommand.ts +++ b/src/mahoji/lib/abstracted_commands/driftNetCommand.ts @@ -1,8 +1,8 @@ -import { randFloat, reduceNumByPercent, Time } from 'e'; +import { Time, randFloat, reduceNumByPercent } from 'e'; import { Bank } from 'oldschooljs'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; import { formatDuration } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -32,9 +32,10 @@ export async function driftNetCommand( return 'You need a trident equipped to do Drift net fishing. Example of tridents are Merfolk trident and Uncharged trident.'; } - if (minutes < 1 || !Number.isInteger(minutes) || isNaN(minutes)) return 'Please specify a valid number of minutes.'; + if (minutes < 1 || !Number.isInteger(minutes) || Number.isNaN(minutes)) + return 'Please specify a valid number of minutes.'; - let tripLength = Time.Minute * minutes; + const tripLength = Time.Minute * minutes; if (tripLength > maxTripLength) { return `${user.minionName} can't go on trips longer than ${formatDuration( diff --git a/src/mahoji/lib/abstracted_commands/duelCommand.ts b/src/mahoji/lib/abstracted_commands/duelCommand.ts index 2f1cff14193..ec332674a3c 100644 --- a/src/mahoji/lib/abstracted_commands/duelCommand.ts +++ b/src/mahoji/lib/abstracted_commands/duelCommand.ts @@ -1,11 +1,12 @@ -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChatInputCommandInteraction } from 'discord.js'; -import { noOp, sleep, Time } from 'e'; -import { MahojiUserOption } from 'mahoji/dist/lib/types'; +import type { ChatInputCommandInteraction } from 'discord.js'; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; +import { Time, noOp, sleep } from 'e'; +import type { MahojiUserOption } from 'mahoji/dist/lib/types'; import { Bank, Util } from 'oldschooljs'; +import { MUserClass } from '../../../lib/MUser'; import { BLACKLISTED_USERS } from '../../../lib/blacklists'; import { Emoji, Events } from '../../../lib/constants'; -import { MUserClass } from '../../../lib/MUser'; import { prisma } from '../../../lib/settings/prisma'; import { awaitMessageComponentInteraction, channelIsSendable } from '../../../lib/util'; import { deferInteraction } from '../../../lib/util/interactionReply'; diff --git a/src/mahoji/lib/abstracted_commands/enchantCommand.ts b/src/mahoji/lib/abstracted_commands/enchantCommand.ts index c2f7315059e..a269deba860 100644 --- a/src/mahoji/lib/abstracted_commands/enchantCommand.ts +++ b/src/mahoji/lib/abstracted_commands/enchantCommand.ts @@ -2,7 +2,7 @@ import { Time } from 'e'; import { Enchantables } from '../../../lib/skilling/skills/magic/enchantables'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { EnchantingActivityTaskOptions } from '../../../lib/types/minions'; +import type { EnchantingActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, itemNameFromID, stringMatches } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -29,7 +29,7 @@ export async function enchantCommand(user: MUser, channelID: string, name: strin const maxTripLength = calcMaxTripLength(user, 'Enchanting'); - let timeToEnchantTen = 3 * Time.Second * 0.6 + Time.Second / 4; + const timeToEnchantTen = 3 * Time.Second * 0.6 + Time.Second / 4; if (!quantity) { quantity = Math.floor(maxTripLength / timeToEnchantTen); diff --git a/src/mahoji/lib/abstracted_commands/farmingCommand.ts b/src/mahoji/lib/abstracted_commands/farmingCommand.ts index 5d13e4769c6..f2e3647092c 100644 --- a/src/mahoji/lib/abstracted_commands/farmingCommand.ts +++ b/src/mahoji/lib/abstracted_commands/farmingCommand.ts @@ -1,5 +1,5 @@ -import { CropUpgradeType } from '@prisma/client'; -import { ChatInputCommandInteraction } from 'discord.js'; +import type { CropUpgradeType } from '@prisma/client'; +import type { ChatInputCommandInteraction } from 'discord.js'; import { Time } from 'e'; import { Bank } from 'oldschooljs'; @@ -9,8 +9,9 @@ import { prisma } from '../../../lib/settings/prisma'; import { calcNumOfPatches } from '../../../lib/skilling/functions/calcsFarming'; import { getFarmingInfo } from '../../../lib/skilling/functions/getFarmingInfo'; import Farming from '../../../lib/skilling/skills/farming'; -import { Plant, SkillsEnum } from '../../../lib/skilling/types'; -import { FarmingActivityTaskOptions } from '../../../lib/types/minions'; +import type { Plant } from '../../../lib/skilling/types'; +import { SkillsEnum } from '../../../lib/skilling/types'; +import type { FarmingActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, stringMatches } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -158,7 +159,7 @@ export async function farmingPlantCommand({ )}. *Make sure you are not attempting to farm 0 crops.*`; } - let wantsToPay = (pay || alwaysPay) && plant.canPayFarmer; + const wantsToPay = (pay || alwaysPay) && plant.canPayFarmer; if (user.skillLevel(SkillsEnum.Farming) < plant.level) { return `${user.minionName} needs ${plant.level} Farming to plant ${plant.name}.`; @@ -198,7 +199,7 @@ export async function farmingPlantCommand({ return `There are not enough ${plant.seedType} patches to plant that many. The max amount of patches to plant in is ${numOfPatches}.`; } - let duration: number = 0; + let duration = 0; if (patchType.patchPlanted) { duration = patchType.lastQuantity * (timePerPatchTravel + timePerPatchPlant + timePerPatchHarvest); if (quantity > patchType.lastQuantity) { diff --git a/src/mahoji/lib/abstracted_commands/farmingContractCommand.ts b/src/mahoji/lib/abstracted_commands/farmingContractCommand.ts index e2b4f7834b7..0426f782553 100644 --- a/src/mahoji/lib/abstracted_commands/farmingContractCommand.ts +++ b/src/mahoji/lib/abstracted_commands/farmingContractCommand.ts @@ -1,7 +1,11 @@ -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { defaultFarmingContract } from '../../../lib/minions/farming'; -import { ContractOption, FarmingContract, FarmingContractDifficultyLevel } from '../../../lib/minions/farming/types'; +import type { + ContractOption, + FarmingContract, + FarmingContractDifficultyLevel +} from '../../../lib/minions/farming/types'; import { getPlantToGrow } from '../../../lib/skilling/functions/calcFarmingContracts'; import { getFarmingInfo } from '../../../lib/skilling/functions/getFarmingInfo'; import { plants } from '../../../lib/skilling/skills/farming'; diff --git a/src/mahoji/lib/abstracted_commands/fightCavesCommand.ts b/src/mahoji/lib/abstracted_commands/fightCavesCommand.ts index 784f7ff2540..0443b7d7101 100644 --- a/src/mahoji/lib/abstracted_commands/fightCavesCommand.ts +++ b/src/mahoji/lib/abstracted_commands/fightCavesCommand.ts @@ -1,12 +1,12 @@ -import { calcWhatPercent, percentChance, randInt, reduceNumByPercent, Time } from 'e'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import { Time, calcWhatPercent, percentChance, randInt, reduceNumByPercent } from 'e'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { Bank, Monsters } from 'oldschooljs'; import TzTokJad from 'oldschooljs/dist/simulation/monsters/special/TzTokJad'; import { itemID } from 'oldschooljs/dist/util'; import { getMinigameScore } from '../../../lib/settings/minigames'; import { getUsersCurrentSlayerInfo } from '../../../lib/slayer/slayerUtil'; -import { FightCavesActivityTaskOptions } from '../../../lib/types/minions'; +import type { FightCavesActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { newChatHeadImage } from '../../../lib/util/chatHeadImage'; @@ -99,7 +99,10 @@ export async function fightCavesCommand(user: MUser, channelID: string): Command if (gearFailure) { return { files: [ - { attachment: await newChatHeadImage({ content: gearFailure, head: 'mejJal' }), name: 'fightcaves.jpg' } + { + attachment: await newChatHeadImage({ content: gearFailure, head: 'mejJal' }), + name: 'fightcaves.jpg' + } ] }; } @@ -126,8 +129,8 @@ export async function fightCavesCommand(user: MUser, channelID: string): Command const isOnTask = usersTask.currentTask !== null && usersTask.currentTask !== undefined && - usersTask.currentTask!.monster_id === Monsters.TzHaarKet.id && - usersTask.currentTask!.quantity_remaining === usersTask.currentTask!.quantity; + usersTask.currentTask?.monster_id === Monsters.TzHaarKet.id && + usersTask.currentTask?.quantity_remaining === usersTask.currentTask?.quantity; // 15% boost for on task if (isOnTask && user.hasEquippedOrInBank('Black mask (i)')) { diff --git a/src/mahoji/lib/abstracted_commands/fishingTrawler.ts b/src/mahoji/lib/abstracted_commands/fishingTrawler.ts index 2e52cf789ec..151b9f25f55 100644 --- a/src/mahoji/lib/abstracted_commands/fishingTrawler.ts +++ b/src/mahoji/lib/abstracted_commands/fishingTrawler.ts @@ -1,4 +1,4 @@ -import { calcWhatPercent, reduceNumByPercent, Time } from 'e'; +import { Time, calcWhatPercent, reduceNumByPercent } from 'e'; import { getMinigameScore } from '../../../lib/settings/minigames'; import type { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; diff --git a/src/mahoji/lib/abstracted_commands/gauntletCommand.ts b/src/mahoji/lib/abstracted_commands/gauntletCommand.ts index f506714547e..086ba8d13f2 100644 --- a/src/mahoji/lib/abstracted_commands/gauntletCommand.ts +++ b/src/mahoji/lib/abstracted_commands/gauntletCommand.ts @@ -1,10 +1,10 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; -import { calcWhatPercent, reduceNumByPercent, Time } from 'e'; +import { Time, calcWhatPercent, reduceNumByPercent } from 'e'; import { BitField } from '../../../lib/constants'; import { getMinigameScore } from '../../../lib/settings/minigames'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { GauntletOptions } from '../../../lib/types/minions'; +import type { GauntletOptions } from '../../../lib/types/minions'; import { formatDuration, formatSkillRequirements, randomVariation } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -115,7 +115,7 @@ export async function gauntletCommand(user: MUser, channelID: string, type: 'cor } // Add a 5% variance to account for randomness of gauntlet - let gauntletLength = randomVariation(baseLength, 5); + const gauntletLength = randomVariation(baseLength, 5); const maxTripLength = calcMaxTripLength(user, 'Gauntlet'); @@ -141,9 +141,7 @@ export async function gauntletCommand(user: MUser, channelID: string, type: 'cor const boostsStr = boosts.length > 0 ? `**Boosts:** ${boosts.join(', ')}` : ''; - return `${user.minionName} is now doing ${quantity}x ${readableName}. The trip will take ${formatDuration( - duration - )}. + return `${user.minionName} is now doing ${quantity}x ${readableName}. The trip will take ${formatDuration(duration)}. ${boostsStr} `; } diff --git a/src/mahoji/lib/abstracted_commands/gearCommands.ts b/src/mahoji/lib/abstracted_commands/gearCommands.ts index c910a2b46e2..7b54a3943b0 100644 --- a/src/mahoji/lib/abstracted_commands/gearCommands.ts +++ b/src/mahoji/lib/abstracted_commands/gearCommands.ts @@ -1,17 +1,18 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; -import { GearPreset } from '@prisma/client'; -import { ChatInputCommandInteraction } from 'discord.js'; +import type { GearPreset } from '@prisma/client'; +import type { ChatInputCommandInteraction } from 'discord.js'; import { objectValues } from 'e'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { Bank } from 'oldschooljs'; import { MAX_INT_JAVA, PATRON_ONLY_GEAR_SETUP, PerkTier } from '../../../lib/constants'; import { generateAllGearImage, generateGearImage } from '../../../lib/gear/functions/generateGearImage'; -import { GearSetup, GearSetupType, GearStat } from '../../../lib/gear/types'; +import type { GearSetup, GearSetupType } from '../../../lib/gear/types'; +import { GearStat } from '../../../lib/gear/types'; import getUserBestGearFromBank from '../../../lib/minions/functions/getUserBestGearFromBank'; import { unEquipAllCommand } from '../../../lib/minions/functions/unequipAllCommand'; import { prisma } from '../../../lib/settings/prisma'; -import { defaultGear, Gear, globalPresets } from '../../../lib/structures/Gear'; +import { Gear, defaultGear, globalPresets } from '../../../lib/structures/Gear'; import { assert, formatSkillRequirements, isValidGearSetup, stringMatches } from '../../../lib/util'; import calculateGearLostOnDeathWilderness from '../../../lib/util/calculateGearLostOnDeathWilderness'; import { gearEquipMultiImpl } from '../../../lib/util/equipMulti'; @@ -335,7 +336,7 @@ export async function gearStatsCommand(user: MUser, input: string): CommandRespo const gear = { ...defaultGear }; for (const name of input.split(',')) { const item = getItem(name); - if (item && item.equipment) { + if (item?.equipment) { gear[item.equipment.slot] = { item: item.id, quantity: 1 }; } } @@ -353,7 +354,7 @@ export async function gearViewCommand(user: MUser, input: string, text: boolean) .join('\n') ), name: 'gear.txt' - } + } : { attachment: await generateAllGearImage(user), name: 'osbot.png' }; return { content: 'Here are all your gear setups', diff --git a/src/mahoji/lib/abstracted_commands/giantsFoundryCommand.ts b/src/mahoji/lib/abstracted_commands/giantsFoundryCommand.ts index ff446c18770..4706d86de3d 100644 --- a/src/mahoji/lib/abstracted_commands/giantsFoundryCommand.ts +++ b/src/mahoji/lib/abstracted_commands/giantsFoundryCommand.ts @@ -1,5 +1,5 @@ -import { ChatInputCommandInteraction } from 'discord.js'; -import { calcWhatPercent, Time } from 'e'; +import type { ChatInputCommandInteraction } from 'discord.js'; +import { Time, calcWhatPercent } from 'e'; import { Bank } from 'oldschooljs'; import { TOTAL_GIANT_WEAPONS } from '../../../lib/giantsFoundry'; @@ -7,14 +7,14 @@ import { trackLoot } from '../../../lib/lootTrack'; import { getMinigameEntity } from '../../../lib/settings/minigames'; import Smithing from '../../../lib/skilling/skills/smithing'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { GiantsFoundryActivityTaskOptions } from '../../../lib/types/minions'; +import type { GiantsFoundryActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, stringMatches } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; import { handleMahojiConfirmation } from '../../../lib/util/handleMahojiConfirmation'; import { updateBankSetting } from '../../../lib/util/updateBankSetting'; import { userStatsBankUpdate, userStatsUpdate } from '../../mahojiSettings'; -import { GiantsFoundryBank } from './../../../lib/giantsFoundry'; +import type { GiantsFoundryBank } from './../../../lib/giantsFoundry'; export const giantsFoundryAlloys = [ { @@ -179,7 +179,7 @@ export async function giantsFoundryStartCommand( let setBonus = 0; if ( user.gear.skilling.hasEquipped( - Object.keys(Smithing.smithsUniformItems).map(i => parseInt(i)), + Object.keys(Smithing.smithsUniformItems).map(i => Number.parseInt(i)), true ) ) { @@ -187,7 +187,7 @@ export async function giantsFoundryStartCommand( } else { // For each Smiths' Uniform item, check if they have it, give its' set boost and covert to 15% speed bonus later. for (const [itemID] of Object.entries(Smithing.smithsUniformItems)) { - if (user.gear.skilling.hasEquipped([parseInt(itemID)], false)) { + if (user.gear.skilling.hasEquipped([Number.parseInt(itemID)], false)) { setBonus += 3; } } @@ -300,7 +300,5 @@ export async function giantsFoundryShopCommand( { foundry_reputation: true } ); - return `You successfully bought **${quantity.toLocaleString()}x ${shopItem.name}** for ${( - shopItem.cost * quantity - ).toLocaleString()} Foundry Reputation.\nYou now have ${newRep} Foundry Reputation left.`; + return `You successfully bought **${quantity.toLocaleString()}x ${shopItem.name}** for ${(shopItem.cost * quantity).toLocaleString()} Foundry Reputation.\nYou now have ${newRep} Foundry Reputation left.`; } diff --git a/src/mahoji/lib/abstracted_commands/gnomeRestaurantCommand.ts b/src/mahoji/lib/abstracted_commands/gnomeRestaurantCommand.ts index a2f57ff17fe..369f9f0a565 100644 --- a/src/mahoji/lib/abstracted_commands/gnomeRestaurantCommand.ts +++ b/src/mahoji/lib/abstracted_commands/gnomeRestaurantCommand.ts @@ -1,10 +1,10 @@ -import { calcWhatPercent, randInt, reduceNumByPercent, Time } from 'e'; +import { Time, calcWhatPercent, randInt, reduceNumByPercent } from 'e'; import { Bank } from 'oldschooljs'; import { SkillsEnum } from 'oldschooljs/dist/constants'; import { getPOHObject } from '../../../lib/poh'; import { getMinigameScore } from '../../../lib/settings/minigames'; -import { GnomeRestaurantActivityTaskOptions } from '../../../lib/types/minions'; +import type { GnomeRestaurantActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, randomVariation } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; diff --git a/src/mahoji/lib/abstracted_commands/guardiansOfTheRiftCommand.ts b/src/mahoji/lib/abstracted_commands/guardiansOfTheRiftCommand.ts index d6447979358..eecc0116ce5 100644 --- a/src/mahoji/lib/abstracted_commands/guardiansOfTheRiftCommand.ts +++ b/src/mahoji/lib/abstracted_commands/guardiansOfTheRiftCommand.ts @@ -5,7 +5,7 @@ import { trackLoot } from '../../../lib/lootTrack'; import { pickaxes, varrockArmours } from '../../../lib/skilling/functions/miningBoosts'; import Runecraft from '../../../lib/skilling/skills/runecraft'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { GuardiansOfTheRiftActivityTaskOptions } from '../../../lib/types/minions'; +import type { GuardiansOfTheRiftActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, itemID, itemNameFromID, randomVariation } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -23,8 +23,8 @@ export async function guardiansOfTheRiftStartCommand( return 'You need 27 Runecraft to access the Temple of the Eye.'; } - let timePerGame = Time.Minute * 10; - let maxTripLength = calcMaxTripLength(user, 'GuardiansOfTheRift'); + const timePerGame = Time.Minute * 10; + const maxTripLength = calcMaxTripLength(user, 'GuardiansOfTheRift'); const quantity = Math.floor(maxTripLength / timePerGame); const duration = quantity * timePerGame; // Being reduced with ticks @@ -133,7 +133,7 @@ export async function guardiansOfTheRiftStartCommand( rolls += 1; } - let removeRunesAndNecks = new Bank(); + const removeRunesAndNecks = new Bank(); if (combinationRunes) { const tomeOfFire = user.hasEquipped(['Tome of fire', 'Tome of fire (empty)']) ? 0 : 7; const tomeOfWater = user.hasEquipped(['Tome of water', 'Tome of water (empty)']) ? 0 : 7; diff --git a/src/mahoji/lib/abstracted_commands/hotColdCommand.ts b/src/mahoji/lib/abstracted_commands/hotColdCommand.ts index 453fd22c0c4..a063e144c94 100644 --- a/src/mahoji/lib/abstracted_commands/hotColdCommand.ts +++ b/src/mahoji/lib/abstracted_commands/hotColdCommand.ts @@ -1,5 +1,6 @@ -import { ChatInputCommandInteraction, EmbedBuilder } from 'discord.js'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { ChatInputCommandInteraction } from 'discord.js'; +import { EmbedBuilder } from 'discord.js'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { LootTable } from 'oldschooljs'; import { toKMB } from 'oldschooljs/dist/util'; @@ -40,8 +41,8 @@ export async function hotColdCommand( if (!amount || !choice || !['hot', 'cold'].includes(choice) || !Number.isInteger(amount)) return explanation; if (amount < 10_000_000 || amount > 500_000_000) return 'You must gamble between 10m and 500m.'; if (user.GP < amount) return "You can't afford to gamble that much."; - let flowerLoot = flowerTable.roll(); - let flower = flowerLoot.items()[0][0]; + const flowerLoot = flowerTable.roll(); + const flower = flowerLoot.items()[0][0]; await handleMahojiConfirmation( interaction, diff --git a/src/mahoji/lib/abstracted_commands/infernoCommand.ts b/src/mahoji/lib/abstracted_commands/infernoCommand.ts index bef958eb09e..bfb30672398 100644 --- a/src/mahoji/lib/abstracted_commands/infernoCommand.ts +++ b/src/mahoji/lib/abstracted_commands/infernoCommand.ts @@ -1,19 +1,20 @@ -import { calcPercentOfNum, percentChance, randInt, roll, sumArr, Time } from 'e'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import { Time, calcPercentOfNum, percentChance, randInt, roll, sumArr } from 'e'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { Bank, Monsters } from 'oldschooljs'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; import { itemID } from 'oldschooljs/dist/util'; -import { BitField, Emoji, projectiles, ProjectileType } from '../../../lib/constants'; +import type { ProjectileType } from '../../../lib/constants'; +import { BitField, Emoji, projectiles } from '../../../lib/constants'; import { getSimilarItems } from '../../../lib/data/similarItems'; import { blowpipeDarts } from '../../../lib/minions/functions/blowpipeCommand'; -import { BlowpipeData } from '../../../lib/minions/types'; +import type { BlowpipeData } from '../../../lib/minions/types'; import { getMinigameScore } from '../../../lib/settings/minigames'; import { prisma } from '../../../lib/settings/prisma'; import { getUsersCurrentSlayerInfo } from '../../../lib/slayer/slayerUtil'; import { PercentCounter } from '../../../lib/structures/PercentCounter'; -import { Skills } from '../../../lib/types'; -import { InfernoOptions } from '../../../lib/types/minions'; +import type { Skills } from '../../../lib/types'; +import type { InfernoOptions } from '../../../lib/types/minions'; import { formatDuration, hasSkillReqs, itemNameFromID, randomVariation } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { newChatHeadImage } from '../../../lib/util/chatHeadImage'; @@ -267,26 +268,19 @@ async function infernoRun({ const weapon = setup.equippedWeapon(); const validWeapons = Object.keys(weapons) .map(itemID) - .map(id => getSimilarItems(id)) - .flat(); + .flatMap(id => getSimilarItems(id)); if (!weapon || !validWeapons.includes(weapon.id)) { return `You need one of these weapons in your ${name} setup: ${Object.keys(weapons).join(', ')}.`; } } - zukDeathChance.add( - getSimilarItems(itemID('Armadyl crossbow')).includes(rangeGear.equippedWeapon()!.id), - 7.5, - 'Zuk with ACB' - ); - duration.add(getSimilarItems(itemID('Armadyl crossbow')).includes(rangeGear.equippedWeapon()!.id), 4.5, 'ACB'); + const rangeGearWeapon = rangeGear.equippedWeapon()!; - zukDeathChance.add( - getSimilarItems(itemID('Twisted bow')).includes(rangeGear.equippedWeapon()!.id), - 1.5, - 'Zuk with TBow' - ); - duration.add(getSimilarItems(itemID('Twisted bow')).includes(rangeGear.equippedWeapon()!.id), -7.5, 'TBow'); + zukDeathChance.add(getSimilarItems(itemID('Armadyl crossbow')).includes(rangeGearWeapon.id), 7.5, 'Zuk with ACB'); + duration.add(getSimilarItems(itemID('Armadyl crossbow')).includes(rangeGearWeapon.id), 4.5, 'ACB'); + + zukDeathChance.add(getSimilarItems(itemID('Twisted bow')).includes(rangeGearWeapon.id), 1.5, 'Zuk with TBow'); + duration.add(getSimilarItems(itemID('Twisted bow')).includes(rangeGearWeapon.id), -7.5, 'TBow'); /** * @@ -304,9 +298,9 @@ async function infernoRun({ const isOnTask = usersTask.currentTask !== null && usersTask.currentTask !== undefined && - usersTask.currentTask!.monster_id === Monsters.TzHaarKet.id && + usersTask.currentTask?.monster_id === Monsters.TzHaarKet.id && score > 0 && - usersTask.currentTask!.quantity_remaining === usersTask.currentTask!.quantity; + usersTask.currentTask?.quantity_remaining === usersTask.currentTask?.quantity; duration.add(isOnTask && user.hasEquippedOrInBank('Black mask (i)'), -9, `${Emoji.Slayer} Slayer Task`); @@ -331,11 +325,11 @@ async function infernoRun({ if (!projectile) { return 'You have no projectiles equipped in your range setup.'; } - const projectileType: ProjectileType = rangeGear.equippedWeapon()!.name === 'Twisted bow' ? 'arrow' : 'bolt'; + const projectileType: ProjectileType = rangeGear.equippedWeapon()?.name === 'Twisted bow' ? 'arrow' : 'bolt'; const projectilesForTheirType = projectiles[projectileType].items; if (!projectilesForTheirType.includes(projectile.item)) { return `You're using incorrect projectiles, you're using a ${ - rangeGear.equippedWeapon()!.name + rangeGear.equippedWeapon()?.name }, which uses ${projectileType}s, so you should be using one of these: ${projectilesForTheirType .map(itemNameFromID) .join(', ')}.`; diff --git a/src/mahoji/lib/abstracted_commands/ironmanCommand.ts b/src/mahoji/lib/abstracted_commands/ironmanCommand.ts index 8e04cae5547..4e20068da8c 100644 --- a/src/mahoji/lib/abstracted_commands/ironmanCommand.ts +++ b/src/mahoji/lib/abstracted_commands/ironmanCommand.ts @@ -1,7 +1,7 @@ import { mentionCommand } from '@oldschoolgg/toolkit'; -import { Prisma } from '@prisma/client'; -import { ChatInputCommandInteraction } from 'discord.js'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { Prisma } from '@prisma/client'; +import type { ChatInputCommandInteraction } from 'discord.js'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; import { BitField } from '../../../lib/constants'; import { GrandExchange } from '../../../lib/grandExchange'; diff --git a/src/mahoji/lib/abstracted_commands/lampCommand.ts b/src/mahoji/lib/abstracted_commands/lampCommand.ts index f504ab57c5f..1474d3de644 100644 --- a/src/mahoji/lib/abstracted_commands/lampCommand.ts +++ b/src/mahoji/lib/abstracted_commands/lampCommand.ts @@ -1,9 +1,9 @@ import { clamp, objectValues } from 'e'; import { Bank } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { Skills } from '../../../lib/types'; +import type { Skills } from '../../../lib/types'; import { assert, isValidSkill, itemID } from '../../../lib/util'; import { getItem } from '../../../lib/util/getOSItem'; import resolveItems from '../../../lib/util/resolveItems'; @@ -268,7 +268,7 @@ export async function lampCommand(user: MUser, itemToUse: string, skill: string, ]!}** in ${skill} to receive it.`; } - let amount = skillsToReceive[skill]!; + const amount = skillsToReceive[skill]!; assert(typeof amount === 'number' && amount > 0); await user.removeItemsFromBank(toRemoveFromBank); diff --git a/src/mahoji/lib/abstracted_commands/lmsCommand.ts b/src/mahoji/lib/abstracted_commands/lmsCommand.ts index 1b29cd91ae3..e7460c0fb4d 100644 --- a/src/mahoji/lib/abstracted_commands/lmsCommand.ts +++ b/src/mahoji/lib/abstracted_commands/lmsCommand.ts @@ -1,4 +1,4 @@ -import { ChatInputCommandInteraction } from 'discord.js'; +import type { ChatInputCommandInteraction } from 'discord.js'; import { Time } from 'e'; import { Bank } from 'oldschooljs'; diff --git a/src/mahoji/lib/abstracted_commands/luckyPickCommand.ts b/src/mahoji/lib/abstracted_commands/luckyPickCommand.ts index 23754c5007a..6905dfb1027 100644 --- a/src/mahoji/lib/abstracted_commands/luckyPickCommand.ts +++ b/src/mahoji/lib/abstracted_commands/luckyPickCommand.ts @@ -1,12 +1,6 @@ -import { - ActionRowBuilder, - BaseMessageOptions, - ButtonBuilder, - ButtonStyle, - ChatInputCommandInteraction, - MessageComponentInteraction -} from 'discord.js'; -import { chunk, noOp, roll, shuffleArr, Time } from 'e'; +import type { BaseMessageOptions, ChatInputCommandInteraction, MessageComponentInteraction } from 'discord.js'; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; +import { Time, chunk, noOp, roll, shuffleArr } from 'e'; import { Bank } from 'oldschooljs'; import { toKMB } from 'oldschooljs/dist/util'; @@ -62,10 +56,26 @@ export async function luckyPickCommand( function getButtons(): ButtonInstance[] { // prettier-ignore - let buttonsToShow = ['0', '0', '0', - '2x', '1.5x', '0', '1.5x', '0', - '1.5x', '1.5x', '2x', '0', '3x', - '2x', '0', '0', '2x', '0']; + const buttonsToShow = [ + '0', + '0', + '0', + '2x', + '1.5x', + '0', + '1.5x', + '0', + '1.5x', + '1.5x', + '2x', + '0', + '3x', + '2x', + '0', + '0', + '2x', + '0' + ]; buttonsToShow.push(roll(10) ? '10x' : '0'); buttonsToShow.push(roll(10) ? '5x' : '0'); @@ -96,11 +106,11 @@ export async function luckyPickCommand( await user.removeItemsFromBank(new Bank().add('Coins', amount)); const buttonsToShow = getButtons(); function getCurrentButtons({ showTrueNames }: { showTrueNames: boolean }): BaseMessageOptions['components'] { - let chunkedButtons = chunk(buttonsToShow, 5); + const chunkedButtons = chunk(buttonsToShow, 5); return chunkedButtons.map(c => new ActionRowBuilder().addComponents( c.map(b => { - let button = new ButtonBuilder() + const button = new ButtonBuilder() .setCustomId(b.id.toString()) .setStyle( @@ -140,7 +150,7 @@ export async function luckyPickCommand( button: ButtonInstance; interaction: MessageComponentInteraction; }) => { - let amountReceived = Math.floor(button.mod(amount)); + const amountReceived = Math.floor(button.mod(amount)); await user.addItemsToBank({ items: new Bank().add('Coins', amountReceived) }); await updateClientGPTrackSetting('gp_luckypick', amountReceived - amount); await updateGPTrackSetting('gp_luckypick', amountReceived - amount, user); diff --git a/src/mahoji/lib/abstracted_commands/mageTrainingArenaCommand.ts b/src/mahoji/lib/abstracted_commands/mageTrainingArenaCommand.ts index eedd85797bb..02e0fe3257a 100644 --- a/src/mahoji/lib/abstracted_commands/mageTrainingArenaCommand.ts +++ b/src/mahoji/lib/abstracted_commands/mageTrainingArenaCommand.ts @@ -1,5 +1,5 @@ import { Time } from 'e'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { Bank, LootTable } from 'oldschooljs'; import { prisma } from '../../../lib/settings/prisma'; diff --git a/src/mahoji/lib/abstracted_commands/mahoganyHomesCommand.ts b/src/mahoji/lib/abstracted_commands/mahoganyHomesCommand.ts index 1de253c36d4..b79d1191d52 100644 --- a/src/mahoji/lib/abstracted_commands/mahoganyHomesCommand.ts +++ b/src/mahoji/lib/abstracted_commands/mahoganyHomesCommand.ts @@ -1,11 +1,11 @@ -import { calcPercentOfNum, calcWhatPercent, randArrItem, randInt, roll, Time } from 'e'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import { Time, calcPercentOfNum, calcWhatPercent, randArrItem, randInt, roll } from 'e'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { Bank } from 'oldschooljs'; import { getMinigameScore } from '../../../lib/settings/minigames'; import { Plank } from '../../../lib/skilling/skills/construction/constructables'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { MahoganyHomesActivityTaskOptions } from '../../../lib/types/minions'; +import type { MahoganyHomesActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, stringMatches } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -16,7 +16,7 @@ interface IContract { name: string; tier: number; level: number; - plank: Plank; + plank: number; xp: number; points: number; plankXP: number[]; @@ -75,7 +75,7 @@ function calcTrip( const lenPerQty = maxLen / qtyPerMaxLen; const qty = Math.floor(maxLen / lenPerQty); - let itemsNeeded = new Bank(); + const itemsNeeded = new Bank(); let xp = 0; for (let i = 0; i < qty; i++) { diff --git a/src/mahoji/lib/abstracted_commands/minionBuyCommand.ts b/src/mahoji/lib/abstracted_commands/minionBuyCommand.ts index 3b93d7884cb..d31ebb76bd6 100644 --- a/src/mahoji/lib/abstracted_commands/minionBuyCommand.ts +++ b/src/mahoji/lib/abstracted_commands/minionBuyCommand.ts @@ -1,5 +1,5 @@ import { ComponentType } from 'discord.js'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { mahojiInformationalButtons } from '../../../lib/constants'; import { clArrayUpdate } from '../../../lib/handleNewCLItems'; diff --git a/src/mahoji/lib/abstracted_commands/minionKill.ts b/src/mahoji/lib/abstracted_commands/minionKill.ts index 9b216931e3c..984e7d08524 100644 --- a/src/mahoji/lib/abstracted_commands/minionKill.ts +++ b/src/mahoji/lib/abstracted_commands/minionKill.ts @@ -1,6 +1,8 @@ -import { ChartConfiguration } from 'chart.js'; -import { bold, ChatInputCommandInteraction, InteractionReplyOptions } from 'discord.js'; +import type { ChartConfiguration } from 'chart.js'; +import type { ChatInputCommandInteraction, InteractionReplyOptions } from 'discord.js'; +import { bold } from 'discord.js'; import { + Time, calcPercentOfNum, calcWhatPercent, increaseNumByPercent, @@ -8,7 +10,6 @@ import { reduceNumByPercent, round, sumArr, - Time, uniqueArr } from 'e'; import { Bank, Monsters } from 'oldschooljs'; @@ -16,14 +17,17 @@ import { MonsterAttribute } from 'oldschooljs/dist/meta/monsterData'; import { itemID } from 'oldschooljs/dist/util'; import { colosseumCommand } from '../../../lib/colosseum'; -import { BitField, PeakTier, PvMMethod } from '../../../lib/constants'; +import type { PvMMethod } from '../../../lib/constants'; +import { BitField, PeakTier } from '../../../lib/constants'; import { Eatables } from '../../../lib/data/eatables'; import { getSimilarItems } from '../../../lib/data/similarItems'; -import { checkUserCanUseDegradeableItem, degradeablePvmBoostItems, degradeItem } from '../../../lib/degradeableItems'; +import { checkUserCanUseDegradeableItem, degradeItem, degradeablePvmBoostItems } from '../../../lib/degradeableItems'; import { userhasDiaryIDTier } from '../../../lib/diaries'; -import { GearSetupType } from '../../../lib/gear/types'; +import type { GearSetupType } from '../../../lib/gear/types'; import { trackLoot } from '../../../lib/lootTrack'; +import type { CombatOptionsEnum } from '../../../lib/minions/data/combatConstants'; import { + SlayerActivityConstants, boostCannon, boostCannonMulti, boostIceBarrage, @@ -31,29 +35,23 @@ import { cannonBanks, cannonMultiConsumables, cannonSingleConsumables, - CombatOptionsEnum, iceBarrageConsumables, - iceBurstConsumables, - SlayerActivityConstants + iceBurstConsumables } from '../../../lib/minions/data/combatConstants'; import { revenantMonsters } from '../../../lib/minions/data/killableMonsters/revs'; import { quests } from '../../../lib/minions/data/quests'; -import { - AttackStyles, - calculateMonsterFood, - convertAttackStylesToSetup, - resolveAttackStyles -} from '../../../lib/minions/functions'; +import type { AttackStyles } from '../../../lib/minions/functions'; +import { calculateMonsterFood, convertAttackStylesToSetup, resolveAttackStyles } from '../../../lib/minions/functions'; import reducedTimeFromKC from '../../../lib/minions/functions/reducedTimeFromKC'; import removeFoodFromUser from '../../../lib/minions/functions/removeFoodFromUser'; -import { Consumable } from '../../../lib/minions/types'; +import type { Consumable } from '../../../lib/minions/types'; import { calcPOHBoosts } from '../../../lib/poh'; import { SkillsEnum } from '../../../lib/skilling/types'; import { SlayerTaskUnlocksEnum } from '../../../lib/slayer/slayerUnlocks'; import { determineBoostChoice, getUsersCurrentSlayerInfo } from '../../../lib/slayer/slayerUtil'; import { maxOffenceStats } from '../../../lib/structures/Gear'; -import { Peak } from '../../../lib/tickers'; -import { MonsterActivityTaskOptions } from '../../../lib/types/minions'; +import type { Peak } from '../../../lib/tickers'; +import type { MonsterActivityTaskOptions } from '../../../lib/types/minions'; import { calculateSimpleMonsterDeathChance, calculateTripConsumableCost, @@ -153,7 +151,7 @@ export async function minionKillCommand( const key = ({ melee: 'attack_crush', mage: 'attack_magic', range: 'attack_ranged' } as const)[style]; const boosts = []; - let messages: string[] = []; + const messages: string[] = []; if (!name) return invalidMonsterMsg; @@ -261,7 +259,7 @@ export async function minionKillCommand( for (const [itemID, boostAmount] of Object.entries(resolveAvailableItemBoosts(user, monster, isInWilderness))) { timeToFinish *= (100 - boostAmount) / 100; - boosts.push(`${boostAmount}% for ${itemNameFromID(parseInt(itemID))}`); + boosts.push(`${boostAmount}% for ${itemNameFromID(Number.parseInt(itemID))}`); } const monsterHP = osjsMon?.data.hitpoints ?? 100; @@ -395,8 +393,8 @@ export async function minionKillCommand( virtusPiecesEquipped > 1 ? ` with ${virtusPiecesEquipped} Virtus pieces` : virtusPiecesEquipped === 1 - ? ` with ${virtusPiecesEquipped} Virtus piece` - : ''; + ? ` with ${virtusPiecesEquipped} Virtus piece` + : ''; } if (isDragon && monster.name.toLowerCase() !== 'vorkath') { @@ -485,21 +483,21 @@ export async function minionKillCommand( } } - if ((method === 'burst' || method === 'barrage') && !monster!.canBarrage) { + if ((method === 'burst' || method === 'barrage') && !monster?.canBarrage) { if (jelly) { if (!isInWilderness) { return `${monster.name} can only be barraged or burst in the wilderness.`; } - } else return `${monster!.name} cannot be barraged or burst.`; + } else return `${monster?.name} cannot be barraged or burst.`; } if (!usingCannon) { - if (method === 'cannon' && !monster!.canCannon) { - return `${monster!.name} cannot be killed with a cannon.`; + if (method === 'cannon' && !monster?.canCannon) { + return `${monster?.name} cannot be killed with a cannon.`; } } - if (boostChoice === 'barrage' && attackStyles.includes(SkillsEnum.Magic) && (monster!.canBarrage || wildyJelly)) { + if (boostChoice === 'barrage' && attackStyles.includes(SkillsEnum.Magic) && (monster?.canBarrage || wildyJelly)) { consumableCosts.push(iceBarrageConsumables); calculateVirtusBoost(); timeToFinish = reduceNumByPercent(timeToFinish, boostIceBarrage + virtusBoost); @@ -508,30 +506,30 @@ export async function minionKillCommand( } else if ( boostChoice === 'burst' && attackStyles.includes(SkillsEnum.Magic) && - (monster!.canBarrage || wildyJelly) + (monster?.canBarrage || wildyJelly) ) { consumableCosts.push(iceBurstConsumables); calculateVirtusBoost(); timeToFinish = reduceNumByPercent(timeToFinish, boostIceBurst + virtusBoost); boosts.push(`${boostIceBurst + virtusBoost}% for Ice Burst${virtusBoostMsg}`); burstOrBarrage = SlayerActivityConstants.IceBurst; - } else if ((boostChoice === 'cannon' && hasCannon && monster!.cannonMulti) || cannonMulti) { + } else if ((boostChoice === 'cannon' && hasCannon && monster?.cannonMulti) || cannonMulti) { usingCannon = true; cannonMulti = true; consumableCosts.push(cannonMultiConsumables); timeToFinish = reduceNumByPercent(timeToFinish, boostCannonMulti); boosts.push(`${boostCannonMulti}% for Cannon in multi`); - } else if ((boostChoice === 'cannon' && hasCannon && monster!.canCannon) || usingCannon) { + } else if ((boostChoice === 'cannon' && hasCannon && monster?.canCannon) || usingCannon) { usingCannon = true; consumableCosts.push(cannonSingleConsumables); timeToFinish = reduceNumByPercent(timeToFinish, boostCannon); boosts.push(`${boostCannon}% for Cannon in singles`); - } else if (method === 'chinning' && attackStyles.includes(SkillsEnum.Ranged) && monster!.canChinning) { + } else if (method === 'chinning' && attackStyles.includes(SkillsEnum.Ranged) && monster?.canChinning) { chinning = true; // Check what Chinchompa to use const chinchompas = ['Black chinchompa', 'Red chinchompa', 'Chinchompa']; let chinchompa = 'Black chinchompa'; - for (let chin of chinchompas) { + for (const chin of chinchompas) { if (user.owns(chin) && user.bank.amount(chin) > 5000) { chinchompa = chin; break; @@ -616,13 +614,13 @@ export async function minionKillCommand( } } if (isOnTask) { - let effectiveQtyRemaining = usersTask.currentTask!.quantity_remaining; + let effectiveQtyRemaining = usersTask.currentTask?.quantity_remaining; if ( monster.id === Monsters.KrilTsutsaroth.id && - usersTask.currentTask!.monster_id !== Monsters.KrilTsutsaroth.id + usersTask.currentTask?.monster_id !== Monsters.KrilTsutsaroth.id ) { effectiveQtyRemaining = Math.ceil(effectiveQtyRemaining / 2); - } else if (monster.id === Monsters.Kreearra.id && usersTask.currentTask!.monster_id !== Monsters.Kreearra.id) { + } else if (monster.id === Monsters.Kreearra.id && usersTask.currentTask?.monster_id !== Monsters.Kreearra.id) { effectiveQtyRemaining = Math.ceil(effectiveQtyRemaining / 4); } else if ( monster.id === Monsters.GrotesqueGuardians.id && @@ -858,7 +856,7 @@ export async function minionKillCommand( } // Check food - let foodStr: string = ''; + let foodStr = ''; // Find best eatable boost and add 1% extra const noFoodBoost = Math.floor(Math.max(...Eatables.map(eatable => eatable.pvmBoost ?? 0)) + 1); if (monster.healAmountNeeded && monster.attackStyleToUse && monster.attackStylesUsed) { @@ -928,7 +926,7 @@ export async function minionKillCommand( // Remove items after food calc to prevent losing items if the user doesn't have the right amount of food. Example: Mossy key if (lootToRemove.length > 0) { updateBankSetting('economyStats_PVMCost', lootToRemove); - await user.specialRemoveItems(lootToRemove, { wildy: isInWilderness ? true : false }); + await user.specialRemoveItems(lootToRemove, { wildy: !!isInWilderness }); totalCost.add(lootToRemove); } @@ -1015,7 +1013,7 @@ export async function monsterInfo(user: MUser, name: string): Promise 0) { timeToFinish = reduceNumByPercent(timeToFinish, boostPercent); - let boostString = messages.join(' ').replace(RegExp('[0-9]{2}% for '), ''); + const boostString = messages.join(' ').replace(/[0-9]{2}% for /, ''); ownedBoostItems.push(`${boostString}`); totalItemBoost += boostPercent; } @@ -1087,7 +1085,7 @@ export async function monsterInfo(user: MUser, name: string): Promise 0) { itemRequirements.push(`**Items Required:** ${formatItemReqs(monster.itemsRequired)}\n`); } @@ -1098,8 +1096,8 @@ export async function monsterInfo(user: MUser, name: string): Promise 0) { itemRequirements.push( `**Healing Required:** ${gearReductions}\nYou require ${ @@ -1111,7 +1109,7 @@ export async function monsterInfo(user: MUser, name: string): Promise u.usernameOrMention) .join(', ')}) is now off to kill ${details.quantity}x Nex! (${calcPerHour( details.quantity, diff --git a/src/mahoji/lib/abstracted_commands/nightmareCommand.ts b/src/mahoji/lib/abstracted_commands/nightmareCommand.ts index 24dc4b8d5e2..bdad1659480 100644 --- a/src/mahoji/lib/abstracted_commands/nightmareCommand.ts +++ b/src/mahoji/lib/abstracted_commands/nightmareCommand.ts @@ -1,5 +1,5 @@ import { mentionCommand } from '@oldschoolgg/toolkit'; -import { reduceNumByPercent, Time } from 'e'; +import { Time, reduceNumByPercent } from 'e'; import { Bank } from 'oldschooljs'; import { BitField, PHOSANI_NIGHTMARE_ID, ZAM_HASTA_CRUSH } from '../../../lib/constants'; @@ -8,9 +8,9 @@ import { trackLoot } from '../../../lib/lootTrack'; import { NightmareMonster } from '../../../lib/minions/data/killableMonsters'; import { calculateMonsterFood } from '../../../lib/minions/functions'; import removeFoodFromUser from '../../../lib/minions/functions/removeFoodFromUser'; -import { KillableMonster } from '../../../lib/minions/types'; +import type { KillableMonster } from '../../../lib/minions/types'; import { Gear } from '../../../lib/structures/Gear'; -import { NightmareActivityTaskOptions } from '../../../lib/types/minions'; +import type { NightmareActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, hasSkillReqs } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import calcDurQty from '../../../lib/util/calcMassDurationQuantity'; @@ -129,8 +129,8 @@ function perUserCost(user: MUser, quantity: number, isPhosani: boolean, hasShado } export async function nightmareCommand(user: MUser, channelID: string, name: string, qty: number | undefined) { - const hasShadow = user.gear.mage.hasEquipped("Tumeken's shadow") ? true : false; - const hasSang = user.gear.mage.hasEquipped('Sanguinesti staff') ? true : false; + const hasShadow = !!user.gear.mage.hasEquipped("Tumeken's shadow"); + const hasSang = !!user.gear.mage.hasEquipped('Sanguinesti staff'); name = name.toLowerCase(); let isPhosani = false; let type: 'solo' | 'mass' = 'solo'; @@ -228,7 +228,7 @@ export async function nightmareCommand(user: MUser, channelID: string, name: str } } - let durQtyRes = await calcDurQty( + const durQtyRes = await calcDurQty( users, { ...NightmareMonster, timeToFinish: effectiveTime }, qty, @@ -236,7 +236,7 @@ export async function nightmareCommand(user: MUser, channelID: string, name: str Time.Minute * 30 ); if (typeof durQtyRes === 'string') return durQtyRes; - let [quantity, duration, perKillTime] = durQtyRes; + const [quantity, duration, perKillTime] = durQtyRes; const totalCost = new Bank(); let soloFoodUsage: Bank | null = null; @@ -244,7 +244,7 @@ export async function nightmareCommand(user: MUser, channelID: string, name: str const cost = perUserCost(user, quantity, isPhosani, hasShadow, hasSang); if (typeof cost === 'string') return cost; - let healingMod = isPhosani ? 1.5 : 1; + const healingMod = isPhosani ? 1.5 : 1; try { const { foodRemoved } = await removeFoodFromUser({ user, @@ -313,19 +313,19 @@ export async function nightmareCommand(user: MUser, channelID: string, name: str ${soloBoosts.length > 0 ? `**Boosts:** ${soloBoosts.join(', ')}` : ''}` : `${user.usernameOrMention}'s party of ${ users.length - } is now off to kill ${quantity}x Nightmare. Each kill takes ${formatDuration( + } is now off to kill ${quantity}x Nightmare. Each kill takes ${formatDuration( perKillTime - )} instead of ${formatDuration( + )} instead of ${formatDuration( NightmareMonster.timeToFinish - )} - the total trip will take ${formatDuration(duration)}.`; + )} - the total trip will take ${formatDuration(duration)}.`; str += `\nRemoved ${soloFoodUsage} from your bank.${ isPhosani ? hasShadow ? ` Your minion is using ${shadowChargesPerKc * quantity} Tumeken's shadow charges. ` : hasSang - ? ` Your minion is using ${sangChargesPerKc * quantity} Sanguinesti staff charges. ` - : '' + ? ` Your minion is using ${sangChargesPerKc * quantity} Sanguinesti staff charges. ` + : '' : '' }`; diff --git a/src/mahoji/lib/abstracted_commands/nightmareZoneCommand.ts b/src/mahoji/lib/abstracted_commands/nightmareZoneCommand.ts index f62c6e89487..f8861fbd0c4 100644 --- a/src/mahoji/lib/abstracted_commands/nightmareZoneCommand.ts +++ b/src/mahoji/lib/abstracted_commands/nightmareZoneCommand.ts @@ -1,21 +1,21 @@ -import { ChatInputCommandInteraction } from 'discord.js'; -import { calcWhatPercent, reduceNumByPercent, round, sumArr, Time } from 'e'; +import type { ChatInputCommandInteraction } from 'discord.js'; +import { Time, calcWhatPercent, reduceNumByPercent, round, sumArr } from 'e'; import { Bank } from 'oldschooljs'; -import { NMZStrategy } from '../../../lib/constants'; +import type { NMZStrategy } from '../../../lib/constants'; import { trackLoot } from '../../../lib/lootTrack'; import { MAX_QP } from '../../../lib/minions/data/quests'; import { resolveAttackStyles } from '../../../lib/minions/functions'; import { getMinigameEntity } from '../../../lib/settings/minigames'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { Skills } from '../../../lib/types'; +import type { Skills } from '../../../lib/types'; import { formatDuration, hasSkillReqs, stringMatches } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; import getOSItem from '../../../lib/util/getOSItem'; import { handleMahojiConfirmation } from '../../../lib/util/handleMahojiConfirmation'; import { updateBankSetting } from '../../../lib/util/updateBankSetting'; -import { NightmareZoneActivityTaskOptions } from './../../../lib/types/minions'; +import type { NightmareZoneActivityTaskOptions } from './../../../lib/types/minions'; const itemBoosts = [ // Special weapons @@ -426,7 +426,7 @@ export async function nightmareZoneShopCommand( .join(', ')}`; } - let costPerItem = shopItem.cost; + const costPerItem = shopItem.cost; const cost = quantity * costPerItem; if (cost > currentUserPoints) { return `You don't have enough Nightmare Zone points to buy ${quantity.toLocaleString()}x ${ @@ -456,9 +456,7 @@ export async function nightmareZoneShopCommand( } }); - return `You successfully bought **${quantity.toLocaleString()}x ${shopItem.name}** for ${( - costPerItem * quantity - ).toLocaleString()} Nightmare Zone points.\nYou now have ${currentUserPoints - cost} Nightmare Zone points left.`; + return `You successfully bought **${quantity.toLocaleString()}x ${shopItem.name}** for ${(costPerItem * quantity).toLocaleString()} Nightmare Zone points.\nYou now have ${currentUserPoints - cost} Nightmare Zone points left.`; } export async function nightmareZoneImbueCommand(user: MUser, input = '') { diff --git a/src/mahoji/lib/abstracted_commands/openCommand.ts b/src/mahoji/lib/abstracted_commands/openCommand.ts index 41e25d1acf9..34c004de1a4 100644 --- a/src/mahoji/lib/abstracted_commands/openCommand.ts +++ b/src/mahoji/lib/abstracted_commands/openCommand.ts @@ -1,12 +1,13 @@ import { stringMatches } from '@oldschoolgg/toolkit'; -import { ButtonBuilder, ChatInputCommandInteraction } from 'discord.js'; +import type { ButtonBuilder, ChatInputCommandInteraction } from 'discord.js'; import { notEmpty, uniqueArr } from 'e'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { Bank } from 'oldschooljs'; import { buildClueButtons } from '../../../lib/clues/clueUtils'; import { BitField, PerkTier } from '../../../lib/constants'; -import { allOpenables, getOpenableLoot, UnifiedOpenable } from '../../../lib/openables'; +import type { UnifiedOpenable } from '../../../lib/openables'; +import { allOpenables, getOpenableLoot } from '../../../lib/openables'; import { makeComponents } from '../../../lib/util'; import getOSItem, { getItem } from '../../../lib/util/getOSItem'; import { handleMahojiConfirmation } from '../../../lib/util/handleMahojiConfirmation'; @@ -57,7 +58,7 @@ export async function abstractedOpenUntilCommand( const cost = new Bank(); const loot = new Bank(); let amountOpened = 0; - let max = Math.min(100, amountOfThisOpenableOwned); + const max = Math.min(100, amountOfThisOpenableOwned); for (let i = 0; i < max; i++) { cost.add(openable.openedItem.id); const thisLoot = await getOpenableLoot({ openable, quantity: 1, user }); @@ -131,14 +132,14 @@ async function finalizeOpening({ const perkTier = user.perkTier(); const components: ButtonBuilder[] = buildClueButtons(loot, perkTier, user); - let response: Awaited = { + const response: Awaited = { files: [image.file], content: `You have now opened a total of ${openedStr} ${messages.join(', ')}`, components: components.length > 0 ? makeComponents(components) : undefined }; if (response.content!.length > 1900) { - response.files!.push({ name: 'response.txt', attachment: Buffer.from(response.content!) }); + response.files?.push({ name: 'response.txt', attachment: Buffer.from(response.content!) }); response.content = 'Due to opening so many things at once, you will have to download the attached text file to read the response.'; } diff --git a/src/mahoji/lib/abstracted_commands/pestControlCommand.ts b/src/mahoji/lib/abstracted_commands/pestControlCommand.ts index bb59b5b5f66..6541f70d32f 100644 --- a/src/mahoji/lib/abstracted_commands/pestControlCommand.ts +++ b/src/mahoji/lib/abstracted_commands/pestControlCommand.ts @@ -1,11 +1,11 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; -import { ChatInputCommandInteraction } from 'discord.js'; -import { reduceNumByPercent, Time } from 'e'; +import type { ChatInputCommandInteraction } from 'discord.js'; +import { Time, reduceNumByPercent } from 'e'; import { Bank } from 'oldschooljs'; -import { userhasDiaryTier, WesternProv } from '../../../lib/diaries'; +import { WesternProv, userhasDiaryTier } from '../../../lib/diaries'; import { getMinigameScore } from '../../../lib/settings/settings'; -import { SkillsEnum } from '../../../lib/skilling/types'; +import type { SkillsEnum } from '../../../lib/skilling/types'; import type { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; import { formatDuration, hasSkillReqs, stringMatches } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; @@ -15,7 +15,7 @@ import { handleMahojiConfirmation } from '../../../lib/util/handleMahojiConfirma import { minionIsBusy } from '../../../lib/util/minionIsBusy'; import { userStatsUpdate } from '../../mahojiSettings'; -let itemBoosts = [ +const itemBoosts = [ [['Abyssal whip', 'Abyssal tentacle'].map(getOSItem), 12], [['Barrows gloves', 'Ferocious gloves'].map(getOSItem), 4], [['Amulet of fury', 'Amulet of torture', 'Amulet of fury (or)', 'Amulet of torture (or)'].map(getOSItem), 5], @@ -25,7 +25,7 @@ let itemBoosts = [ export function getBoatType(user: MUser, cbLevel: number) { let type: 'veteran' | 'intermediate' | 'novice' = 'intermediate'; - let pointsPerGame: number = 1; + let pointsPerGame = 1; if (cbLevel >= 100) { type = 'veteran'; @@ -58,7 +58,7 @@ export function getBoatType(user: MUser, cbLevel: number) { }; } -let baseStats = { +const baseStats = { attack: 42, strength: 42, defence: 42, @@ -123,7 +123,7 @@ export const pestControlBuyables = [ } ]; -let xpMultiplier = { +const xpMultiplier = { prayer: 18, magic: 32, ranged: 32, @@ -148,7 +148,7 @@ export async function pestControlBuyCommand(user: MUser, input: string) { return `You don't have enough Void knight commendation points to buy the ${item.name}. You need ${cost}, but you have only ${balance}.`; } - let [hasReqs, str] = hasSkillReqs(user, buyable.requiredStats); + const [hasReqs, str] = hasSkillReqs(user, buyable.requiredStats); if (!hasReqs) { return `You need ${str} to buy this item.`; } @@ -189,7 +189,7 @@ export async function pestControlStartCommand(user: MUser, channelID: string) { const maxLength = calcMaxTripLength(user, 'PestControl'); const gear = user.gear.melee; - let boosts = []; + const boosts = []; for (const [items, percent] of itemBoosts) { for (const item of items) { if (gear.hasEquipped(item.name)) { @@ -202,7 +202,7 @@ export async function pestControlStartCommand(user: MUser, channelID: string) { const quantity = Math.floor(maxLength / gameLength); - let duration = quantity * gameLength; + const duration = quantity * gameLength; await addSubTaskToActivityTask({ userID: user.id, @@ -213,7 +213,7 @@ export async function pestControlStartCommand(user: MUser, channelID: string) { minigameID: 'pest_control' }); - let { boatType } = getBoatType(user, user.combatLevel); + const { boatType } = getBoatType(user, user.combatLevel); let str = `${ user.minionName diff --git a/src/mahoji/lib/abstracted_commands/pohCommand.ts b/src/mahoji/lib/abstracted_commands/pohCommand.ts index 24b479eaf0b..a42cdcf1735 100644 --- a/src/mahoji/lib/abstracted_commands/pohCommand.ts +++ b/src/mahoji/lib/abstracted_commands/pohCommand.ts @@ -1,9 +1,9 @@ import { stringMatches } from '@oldschoolgg/toolkit'; -import { ChatInputCommandInteraction } from 'discord.js'; +import type { ChatInputCommandInteraction } from 'discord.js'; import { Bank } from 'oldschooljs'; import { BitField } from '../../../lib/constants'; -import { getPOHObject, GroupedPohObjects, itemsNotRefundable, PoHObjects } from '../../../lib/poh'; +import { GroupedPohObjects, PoHObjects, getPOHObject, itemsNotRefundable } from '../../../lib/poh'; import { pohImageGenerator } from '../../../lib/pohImage'; import { prisma } from '../../../lib/settings/prisma'; import { SkillsEnum } from '../../../lib/skilling/types'; diff --git a/src/mahoji/lib/abstracted_commands/puroPuroCommand.ts b/src/mahoji/lib/abstracted_commands/puroPuroCommand.ts index 4c5b8928893..0ed48078596 100644 --- a/src/mahoji/lib/abstracted_commands/puroPuroCommand.ts +++ b/src/mahoji/lib/abstracted_commands/puroPuroCommand.ts @@ -1,8 +1,8 @@ import { Time } from 'e'; -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; -import { Skills } from '../../../lib/types'; -import { PuroPuroActivityTaskOptions } from '../../../lib/types/minions'; +import type { Skills } from '../../../lib/types'; +import type { PuroPuroActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, hasSkillReqs, itemID, stringMatches } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; diff --git a/src/mahoji/lib/abstracted_commands/pyramidPlunderCommand.ts b/src/mahoji/lib/abstracted_commands/pyramidPlunderCommand.ts index 832ca8a5712..90812094bc0 100644 --- a/src/mahoji/lib/abstracted_commands/pyramidPlunderCommand.ts +++ b/src/mahoji/lib/abstracted_commands/pyramidPlunderCommand.ts @@ -1,8 +1,8 @@ -import { reduceNumByPercent, Time } from 'e'; +import { Time, reduceNumByPercent } from 'e'; import { plunderBoosts, plunderRooms } from '../../../lib/minions/data/plunder'; import { getMinigameScore } from '../../../lib/settings/minigames'; -import { PlunderActivityTaskOptions } from '../../../lib/types/minions'; +import type { PlunderActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; diff --git a/src/mahoji/lib/abstracted_commands/questCommand.ts b/src/mahoji/lib/abstracted_commands/questCommand.ts index 5a0db6daf1a..939eb683e7a 100644 --- a/src/mahoji/lib/abstracted_commands/questCommand.ts +++ b/src/mahoji/lib/abstracted_commands/questCommand.ts @@ -1,7 +1,7 @@ -import { sumArr, Time } from 'e'; +import { Time, sumArr } from 'e'; import { MAX_GLOBAL_QP, MAX_QP, quests } from '../../../lib/minions/data/quests'; -import { ActivityTaskOptionsWithNoChanges, SpecificQuestOptions } from '../../../lib/types/minions'; +import type { ActivityTaskOptionsWithNoChanges, SpecificQuestOptions } from '../../../lib/types/minions'; import { formatDuration, hasSkillReqs } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { minionIsBusy } from '../../../lib/util/minionIsBusy'; diff --git a/src/mahoji/lib/abstracted_commands/roguesDenCommand.ts b/src/mahoji/lib/abstracted_commands/roguesDenCommand.ts index 94a691fe7f9..1747a7464e6 100644 --- a/src/mahoji/lib/abstracted_commands/roguesDenCommand.ts +++ b/src/mahoji/lib/abstracted_commands/roguesDenCommand.ts @@ -1,4 +1,4 @@ -import { reduceNumByPercent, Time } from 'e'; +import { Time, reduceNumByPercent } from 'e'; import { Bank } from 'oldschooljs'; import { SkillsEnum } from '../../../lib/skilling/types'; diff --git a/src/mahoji/lib/abstracted_commands/sawmillCommand.ts b/src/mahoji/lib/abstracted_commands/sawmillCommand.ts index 6fbd0406ca1..01c3ec73971 100644 --- a/src/mahoji/lib/abstracted_commands/sawmillCommand.ts +++ b/src/mahoji/lib/abstracted_commands/sawmillCommand.ts @@ -1,8 +1,8 @@ -import { clamp, Time } from 'e'; +import { Time, clamp } from 'e'; import { Bank } from 'oldschooljs'; import { Planks } from '../../../lib/minions/data/planks'; -import { SawmillActivityTaskOptions } from '../../../lib/types/minions'; +import type { SawmillActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, itemNameFromID, stringMatches, toKMB } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -56,7 +56,7 @@ export async function sawmillCommand( } const { GP } = user; - let cost = plank!.gpCost * quantity; + const cost = plank?.gpCost * quantity; if (GP < cost) { return `You need ${toKMB(cost)} GP to create ${quantity} planks.`; @@ -72,15 +72,15 @@ export async function sawmillCommand( )}.`; } - const costBank = new Bank().add('Coins', plank!.gpCost * quantity).add(plank!.inputItem, quantity); + const costBank = new Bank().add('Coins', plank?.gpCost * quantity).add(plank?.inputItem, quantity); await transactItems({ userID: user.id, itemsToRemove: costBank }); - await updateBankSetting('construction_cost_bank', new Bank().add('Coins', plank!.gpCost * quantity)); + await updateBankSetting('construction_cost_bank', new Bank().add('Coins', plank?.gpCost * quantity)); await addSubTaskToActivityTask({ type: 'Sawmill', duration, - plankID: plank!.outputItem, + plankID: plank?.outputItem, plankQuantity: quantity, userID: user.id, channelID: channelID.toString() diff --git a/src/mahoji/lib/abstracted_commands/scatterCommand.ts b/src/mahoji/lib/abstracted_commands/scatterCommand.ts index 839668468fa..ea78cf49cf8 100644 --- a/src/mahoji/lib/abstracted_commands/scatterCommand.ts +++ b/src/mahoji/lib/abstracted_commands/scatterCommand.ts @@ -3,7 +3,7 @@ import { Bank } from 'oldschooljs'; import Prayer from '../../../lib/skilling/skills/prayer'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { ScatteringActivityTaskOptions } from '../../../lib/types/minions'; +import type { ScatteringActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, stringMatches } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; diff --git a/src/mahoji/lib/abstracted_commands/sepulchreCommand.ts b/src/mahoji/lib/abstracted_commands/sepulchreCommand.ts index 8d558bf4b8b..54ae03d0a48 100644 --- a/src/mahoji/lib/abstracted_commands/sepulchreCommand.ts +++ b/src/mahoji/lib/abstracted_commands/sepulchreCommand.ts @@ -1,8 +1,8 @@ -import { reduceNumByPercent, sumArr, Time } from 'e'; +import { Time, reduceNumByPercent, sumArr } from 'e'; import { sepulchreBoosts, sepulchreFloors } from '../../../lib/minions/data/sepulchre'; import { getMinigameScore } from '../../../lib/settings/minigames'; -import { SepulchreActivityTaskOptions } from '../../../lib/types/minions'; +import type { SepulchreActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; diff --git a/src/mahoji/lib/abstracted_commands/shadesOfMortonCommand.ts b/src/mahoji/lib/abstracted_commands/shadesOfMortonCommand.ts index d010cc8ba6a..eb8c35e86e9 100644 --- a/src/mahoji/lib/abstracted_commands/shadesOfMortonCommand.ts +++ b/src/mahoji/lib/abstracted_commands/shadesOfMortonCommand.ts @@ -1,8 +1,8 @@ import { Time } from 'e'; import { Bank } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; -import { ShadesOfMortonOptions } from '../../../lib/types/minions'; +import type { ShadesOfMortonOptions } from '../../../lib/types/minions'; import { formatDuration, itemNameFromID } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -255,12 +255,12 @@ export const shadesLogs: ShadesLog[] = [ const coffins = ['Bronze coffin', 'Steel coffin', 'Black coffin', 'Silver coffin', 'Gold coffin']; export async function shadesOfMortonStartCommand(user: MUser, channelID: string, logStr: string, shadeStr: string) { - let messages: string[] = []; + const messages: string[] = []; let totalTime = calcMaxTripLength(user, 'ShadesOfMorton'); for (let i = coffins.length - 1; i >= 0; i--) { const coffin = coffins[i]; if (user.hasEquipped(coffin)) { - let bonusTime = i * Time.Minute; + const bonusTime = i * Time.Minute; if (bonusTime) { totalTime += bonusTime; messages.push(`${formatDuration(bonusTime)} bonus max trip length for ${itemNameFromID(coffin)}`); @@ -287,7 +287,7 @@ export async function shadesOfMortonStartCommand(user: MUser, channelID: string, const quantity = Math.min(logsOwned, shadesOwned, Math.floor(totalTime / timePerLog)); const duration = quantity * timePerLog; - let prayerXP = log.prayerXP[shade.shadeName]; + const prayerXP = log.prayerXP[shade.shadeName]; if (!prayerXP) { return `You can't use ${log.normalLog.name} with ${shade.item.name}.`; } diff --git a/src/mahoji/lib/abstracted_commands/shootingStarsCommand.ts b/src/mahoji/lib/abstracted_commands/shootingStarsCommand.ts index ef3bb7e0366..00abaca04b4 100644 --- a/src/mahoji/lib/abstracted_commands/shootingStarsCommand.ts +++ b/src/mahoji/lib/abstracted_commands/shootingStarsCommand.ts @@ -1,19 +1,20 @@ import { SimpleTable } from '@oldschoolgg/toolkit'; -import { activity_type_enum } from '@prisma/client'; +import type { activity_type_enum } from '@prisma/client'; import { ButtonBuilder, ButtonStyle } from 'discord.js'; -import { percentChance, randInt, roll, Time } from 'e'; +import { Time, percentChance, randInt, roll } from 'e'; import { Bank } from 'oldschooljs'; import addSkillingClueToLoot from '../../../lib/minions/functions/addSkillingClueToLoot'; import { determineMiningTime } from '../../../lib/skilling/functions/determineMiningTime'; import { pickaxes } from '../../../lib/skilling/functions/miningBoosts'; -import { Ore, SkillsEnum } from '../../../lib/skilling/types'; -import { ActivityTaskData, ShootingStarsOptions } from '../../../lib/types/minions'; +import type { Ore } from '../../../lib/skilling/types'; +import { SkillsEnum } from '../../../lib/skilling/types'; +import type { ActivityTaskData, ShootingStarsOptions } from '../../../lib/types/minions'; import { formatDuration, itemNameFromID } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength, patronMaxTripBonus } from '../../../lib/util/calcMaxTripLength'; import { minionName } from '../../../lib/util/minionUtils'; -import { MUserClass } from './../../../lib/MUser'; +import type { MUserClass } from './../../../lib/MUser'; interface Star extends Ore { size: number; @@ -202,8 +203,8 @@ export async function shootingStarsCommand(channelID: string, user: MUserClass, let duration = 0; let dustReceived = 0; let totalXp = 0; - for (let star of stars) { - let [timeToMine, newQuantity] = determineMiningTime({ + for (const star of stars) { + const [timeToMine, newQuantity] = determineMiningTime({ quantity: Math.round(star.dustAvailable / usersWith), user, ore: star, diff --git a/src/mahoji/lib/abstracted_commands/slayerShopCommand.ts b/src/mahoji/lib/abstracted_commands/slayerShopCommand.ts index b65cbecf976..c97b03ce63c 100644 --- a/src/mahoji/lib/abstracted_commands/slayerShopCommand.ts +++ b/src/mahoji/lib/abstracted_commands/slayerShopCommand.ts @@ -1,4 +1,4 @@ -import { ChatInputCommandInteraction } from 'discord.js'; +import type { ChatInputCommandInteraction } from 'discord.js'; import { removeFromArr } from 'e'; import { Bank } from 'oldschooljs'; @@ -109,11 +109,7 @@ export function slayerShopListMyUnlocks(mahojiUser: MUser) { const myUnlocks = SlayerRewardsShop.filter(srs => mahojiUser.user.slayer_unlocks.includes(srs.id)); const unlocksStr = myUnlocks.map(unlock => unlock.name).join('\n'); - const content = - `Current points: ${mahojiUser.user.slayer_points}\n**You currently have the following ` + - `rewards unlocked:**\n${unlocksStr}\n\n` + - 'Usage:\n`/slayer rewards [unlock|buy|disable] Reward`\nExample:' + - '\n`/slayer rewards unlock unlockable:Malevolent Masquerade`'; + const content = `Current points: ${mahojiUser.user.slayer_points}\n**You currently have the following rewards unlocked:**\n${unlocksStr}\n\nUsage:\n\`/slayer rewards [unlock|buy|disable] Reward\`\nExample:\n\`/slayer rewards unlock unlockable:Malevolent Masquerade\``; if (content.length > 2000) { return { content: 'Your currently unlocked Slayer rewards', diff --git a/src/mahoji/lib/abstracted_commands/slayerTaskCommand.ts b/src/mahoji/lib/abstracted_commands/slayerTaskCommand.ts index 6d891682a59..d82221dec83 100644 --- a/src/mahoji/lib/abstracted_commands/slayerTaskCommand.ts +++ b/src/mahoji/lib/abstracted_commands/slayerTaskCommand.ts @@ -1,6 +1,7 @@ import { stringMatches } from '@oldschoolgg/toolkit'; -import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChatInputCommandInteraction } from 'discord.js'; -import { notEmpty, randInt, removeFromArr, Time } from 'e'; +import type { ChatInputCommandInteraction } from 'discord.js'; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; +import { Time, notEmpty, randInt, removeFromArr } from 'e'; import { Monsters } from 'oldschooljs'; import killableMonsters from '../../../lib/minions/data/killableMonsters'; @@ -15,7 +16,7 @@ import { getUsersCurrentSlayerInfo, userCanUseMaster } from '../../../lib/slayer/slayerUtil'; -import { AssignableSlayerTask } from '../../../lib/slayer/types'; +import type { AssignableSlayerTask } from '../../../lib/slayer/types'; import { awaitMessageComponentInteraction, channelIsSendable } from '../../../lib/util'; import { handleMahojiConfirmation } from '../../../lib/util/handleMahojiConfirmation'; import { interactionReply } from '../../../lib/util/interactionReply'; @@ -70,14 +71,14 @@ function getAlternateMonsterList(assignedTask: AssignableSlayerTask | null) { const altMobs = assignedTask.monsters; const alternateMonsters = killableMonsters .filter(m => { - return altMobs.includes(m.id) && m!.id !== assignedTask.monster.id; + return altMobs.includes(m.id) && m.id !== assignedTask.monster.id; }) .map(m => { - return m!.name; + return m?.name; }); - const cname = getCommonTaskName(assignedTask!.monster); - if (cname !== assignedTask!.monster.name && cname.substr(0, cname.length - 1) !== assignedTask!.monster.name) { - alternateMonsters.unshift(assignedTask!.monster.name); + const cname = getCommonTaskName(assignedTask?.monster); + if (cname !== assignedTask?.monster.name && cname.substr(0, cname.length - 1) !== assignedTask?.monster.name) { + alternateMonsters.unshift(assignedTask?.monster.name); } return alternateMonsters.length > 0 ? ` (**Alternate Monsters**: ${alternateMonsters.join(', ')})` : ''; @@ -105,11 +106,11 @@ export async function slayerStatusCommand(mahojiUser: MUser) { return ( `${ currentTask - ? `\nYour current task from ${slayerMaster!.name} is to kill **${getCommonTaskName( - assignedTask!.monster - )}**${getAlternateMonsterList( + ? `\nYour current task from ${slayerMaster.name} is to kill **${getCommonTaskName( + assignedTask.monster + )}**${getAlternateMonsterList( assignedTask - )}. You have ${currentTask.quantity_remaining.toLocaleString()} kills remaining.` + )}. You have ${currentTask.quantity_remaining.toLocaleString()} kills remaining.` : '' }` + `\nYou have ${slayerPoints.toLocaleString()} slayer points, and have completed ${ @@ -256,15 +257,15 @@ export async function slayerNewTaskCommand({ slayerMasterOverride && has99SlayerCape ? slayerMasters.find(m => m.aliases.some(alias => stringMatches(alias, slayerMasterOverride))) ?? null : slayerMasterOverride - ? slayerMasters - .filter(m => userCanUseMaster(user, m)) - .find(m => m.aliases.some(alias => stringMatches(alias, slayerMasterOverride))) ?? null - : rememberedSlayerMaster - ? slayerMasters - .filter(m => userCanUseMaster(user, m)) - .find(m => m.aliases.some(alias => stringMatches(alias, rememberedSlayerMaster))) ?? - proposedDefaultMaster - : proposedDefaultMaster; + ? slayerMasters + .filter(m => userCanUseMaster(user, m)) + .find(m => m.aliases.some(alias => stringMatches(alias, slayerMasterOverride))) ?? null + : rememberedSlayerMaster + ? slayerMasters + .filter(m => userCanUseMaster(user, m)) + .find(m => m.aliases.some(alias => stringMatches(alias, rememberedSlayerMaster))) ?? + proposedDefaultMaster + : proposedDefaultMaster; // Contains (if matched) the requested Slayer Master regardless of requirements. const matchedSlayerMaster = slayerMasterOverride @@ -272,7 +273,7 @@ export async function slayerNewTaskCommand({ m => stringMatches(m.name, slayerMasterOverride) || m.aliases.some(alias => stringMatches(alias, slayerMasterOverride)) - ) ?? null + ) ?? null : null; // Special handling for Turael skip @@ -300,7 +301,7 @@ export async function slayerNewTaskCommand({ await userStatsUpdate(user.id, { [taskStreakKey]: 0 }, {}); const newSlayerTask = await assignNewSlayerTask(user, slayerMaster); - let commonName = getCommonTaskName(newSlayerTask.assignedTask!.monster); + const commonName = getCommonTaskName(newSlayerTask.assignedTask.monster); const returnMessage = `Your task has been skipped.\n\n ${slayerMaster.name}` + ` has assigned you to kill ${newSlayerTask.currentTask.quantity}x ${commonName}${getAlternateMonsterList( @@ -319,13 +320,13 @@ export async function slayerNewTaskCommand({ // Store favorite slayer master if requested: if (saveDefaultSlayerMaster && slayerMaster) { await user.update({ slayer_remember_master: slayerMaster.name }); - resultMessage = `**Saved ${slayerMaster!.name} as default slayer master.**\n\n`; + resultMessage = `**Saved ${slayerMaster?.name} as default slayer master.**\n\n`; } if (currentTask || !slayerMaster) { let warningInfo = ''; if (slayerMasterOverride && !slayerMaster && matchedSlayerMaster) { - let aRequirements: string[] = []; + const aRequirements: string[] = []; if (matchedSlayerMaster.slayerLvl) aRequirements.push(`Slayer Level: ${matchedSlayerMaster.slayerLvl}`); if (matchedSlayerMaster.combatLvl) aRequirements.push(`Combat Level: ${matchedSlayerMaster.combatLvl}`); if (matchedSlayerMaster.questPoints) aRequirements.push(`Quest points: ${matchedSlayerMaster.questPoints}`); @@ -333,10 +334,9 @@ export async function slayerNewTaskCommand({ if (aRequirements.length > 0) warningInfo += `**Requires**:\n${aRequirements.join('\n')}\n\n`; } - let baseInfo = currentTask + const baseInfo = currentTask ? await slayerStatusCommand(user) - : 'You have no task at the moment, you can get a task using `/slayer task master:Turael`' + - `All slayer Masters: ${slayerMasters.map(i => i.name).join(', ')}`; + : `You have no task at the moment, you can get a task using \`/slayer task master:Turael\`All slayer Masters: ${slayerMasters.map(i => i.name).join(', ')}`; resultMessage += `${warningInfo}${baseInfo}`; if (currentTask && !warningInfo) { @@ -352,9 +352,7 @@ export async function slayerNewTaskCommand({ const newSlayerTask = await assignNewSlayerTask(user, slayerMaster); const myUnlocks = user.user.slayer_unlocks ?? []; - const extendReward = SlayerRewardsShop.find( - srs => srs.extendID && srs.extendID.includes(newSlayerTask.currentTask.monster_id) - ); + const extendReward = SlayerRewardsShop.find(srs => srs.extendID?.includes(newSlayerTask.currentTask.monster_id)); if (extendReward && myUnlocks.includes(extendReward.id)) { const quantity = newSlayerTask.assignedTask.extendedAmount ? randInt(newSlayerTask.assignedTask.extendedAmount[0], newSlayerTask.assignedTask.extendedAmount[1]) @@ -371,7 +369,7 @@ export async function slayerNewTaskCommand({ }); } - let commonName = getCommonTaskName(newSlayerTask.assignedTask!.monster); + let commonName = getCommonTaskName(newSlayerTask.assignedTask.monster); if (commonName === 'TzHaar') { resultMessage += 'Ah... Tzhaar... '; commonName += @@ -427,9 +425,7 @@ export async function slayerSkipTaskCommand({ if (block && myBlockList.length >= maxBlocks) { interactionReply( interaction, - `You cannot have more than ${maxBlocks} slayer blocks!\n\nUse:\n` + - '`/slayer rewards unblock assignment:kalphite`\n to remove a blocked monster.\n' + - '`/slayer manage command:list_blocks` for your list of blocked monsters.' + `You cannot have more than ${maxBlocks} slayer blocks!\n\nUse:\n\`/slayer rewards unblock assignment:kalphite\`\n to remove a blocked monster.\n\`/slayer manage command:list_blocks\` for your list of blocked monsters.` ); return; } diff --git a/src/mahoji/lib/abstracted_commands/slotsCommand.ts b/src/mahoji/lib/abstracted_commands/slotsCommand.ts index 9b78dab4f4a..e98ad832187 100644 --- a/src/mahoji/lib/abstracted_commands/slotsCommand.ts +++ b/src/mahoji/lib/abstracted_commands/slotsCommand.ts @@ -1,13 +1,8 @@ import { SimpleTable } from '@oldschoolgg/toolkit'; -import { - ActionRowBuilder, - BaseMessageOptions, - ButtonBuilder, - ButtonStyle, - ChatInputCommandInteraction -} from 'discord.js'; +import type { BaseMessageOptions, ChatInputCommandInteraction } from 'discord.js'; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js'; import { chunk, noOp, randInt, shuffleArr, sleep } from 'e'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { Bank } from 'oldschooljs'; import { toKMB } from 'oldschooljs/dist/util'; @@ -81,7 +76,7 @@ function determineWinnings(bet: number, buttons: ButtonInstance[]) { const winningRow = chunk(buttons, 3) .filter(row => row.every(b => b.name === row[0].name)) .sort((a, b) => b[0].mod(bet) - a[0].mod(bet))[0]; - let amountReceived = winningRow ? winningRow[0].mod(bet) : 0; + const amountReceived = winningRow ? winningRow[0].mod(bet) : 0; return { amountReceived, winningRow @@ -125,7 +120,7 @@ ${buttonsData.map(b => `${b.name}: ${b.mod(1)}x`).join('\n')}`; await user.removeItemsFromBank(new Bank().add('Coins', amount)); const buttonsToShow = getButtons(); - let chunkedButtons = chunk(buttonsToShow, 3); + const chunkedButtons = chunk(buttonsToShow, 3); const { winningRow, amountReceived } = determineWinnings(amount, buttonsToShow); @@ -141,8 +136,8 @@ ${buttonsData.map(b => `${b.name}: ${b.mod(1)}x`).join('\n')}`; !shouldShowThisButton ? ButtonStyle.Secondary : isWinning - ? ButtonStyle.Success - : ButtonStyle.Secondary + ? ButtonStyle.Success + : ButtonStyle.Secondary ) .setEmoji(shouldShowThisButton ? b.emoji : '❓'); }) diff --git a/src/mahoji/lib/abstracted_commands/soulWarsCommand.ts b/src/mahoji/lib/abstracted_commands/soulWarsCommand.ts index a6326c5a8dd..b231202ea34 100644 --- a/src/mahoji/lib/abstracted_commands/soulWarsCommand.ts +++ b/src/mahoji/lib/abstracted_commands/soulWarsCommand.ts @@ -1,4 +1,4 @@ -import { User } from '@prisma/client'; +import type { User } from '@prisma/client'; import { Time } from 'e'; import { Bank } from 'oldschooljs'; @@ -164,7 +164,7 @@ export async function soulWarsBuyCommand(user: MUser, input = '', quantity?: num if (!quantity) { quantity = 1; } - if (!Number.isNaN(parseInt(possibleItemName[0]))) { + if (!Number.isNaN(Number.parseInt(possibleItemName[0]))) { quantity = Number(possibleItemName.shift()); } diff --git a/src/mahoji/lib/abstracted_commands/stashUnitsCommand.ts b/src/mahoji/lib/abstracted_commands/stashUnitsCommand.ts index 4f6e6274cb3..e6287340686 100644 --- a/src/mahoji/lib/abstracted_commands/stashUnitsCommand.ts +++ b/src/mahoji/lib/abstracted_commands/stashUnitsCommand.ts @@ -1,10 +1,11 @@ import { stringMatches } from '@oldschoolgg/toolkit'; -import { StashUnit, User } from '@prisma/client'; +import type { StashUnit, User } from '@prisma/client'; import { partition } from 'e'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import Bank from 'oldschooljs/dist/structures/Bank'; -import { allStashUnitsFlat, allStashUnitTiers, IStashUnit, StashUnitTier } from '../../../lib/clues/stashUnits'; +import type { IStashUnit, StashUnitTier } from '../../../lib/clues/stashUnits'; +import { allStashUnitTiers, allStashUnitsFlat } from '../../../lib/clues/stashUnits'; import { prisma } from '../../../lib/settings/prisma'; import { assert } from '../../../lib/util'; import { makeBankImage } from '../../../lib/util/makeBankImage'; @@ -30,7 +31,7 @@ export async function getParsedStashUnits(userID: string): Promise return slot.some(i => builtUnit.items_contained.includes(i)); } return builtUnit.items_contained.includes(slot); - }) + }) : false, builtUnit, tier diff --git a/src/mahoji/lib/abstracted_commands/statCommand.ts b/src/mahoji/lib/abstracted_commands/statCommand.ts index 96e85dc4525..9b52fd2e396 100644 --- a/src/mahoji/lib/abstracted_commands/statCommand.ts +++ b/src/mahoji/lib/abstracted_commands/statCommand.ts @@ -1,9 +1,9 @@ -import { activity_type_enum, UserStats } from '@prisma/client'; -import { sumArr, Time } from 'e'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { UserStats, activity_type_enum } from '@prisma/client'; +import { Time, sumArr } from 'e'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import { Bank, Monsters } from 'oldschooljs'; import { SkillsEnum } from 'oldschooljs/dist/constants'; -import { ItemBank, SkillsScore } from 'oldschooljs/dist/meta/types'; +import type { ItemBank, SkillsScore } from 'oldschooljs/dist/meta/types'; import { TOBRooms } from 'oldschooljs/dist/simulation/misc/TheatreOfBlood'; import { toKMB } from 'oldschooljs/dist/util'; @@ -22,8 +22,8 @@ import { Castables } from '../../../lib/skilling/skills/magic/castables'; import { ForestryEvents } from '../../../lib/skilling/skills/woodcutting/forestry'; import { getSlayerTaskStats } from '../../../lib/slayer/slayerUtil'; import { sorts } from '../../../lib/sorts'; -import { InfernoOptions } from '../../../lib/types/minions'; -import { formatDuration, getUsername, sanitizeBank, SQL_sumOfAllCLItems, stringMatches } from '../../../lib/util'; +import type { InfernoOptions } from '../../../lib/types/minions'; +import { SQL_sumOfAllCLItems, formatDuration, getUsername, sanitizeBank, stringMatches } from '../../../lib/util'; import { barChart, lineChart, pieChart } from '../../../lib/util/chart'; import { getItem } from '../../../lib/util/getOSItem'; import { makeBankImage } from '../../../lib/util/makeBankImage'; @@ -118,8 +118,7 @@ WHERE } export async function personalConstructionStats(user: MUser) { - const result: { id: number; qty: number }[] = - await prisma.$queryRawUnsafe(`SELECT (data->>'objectID')::int AS id, SUM((data->>'quantity')::int)::int AS qty + const result: { id: number; qty: number }[] = await prisma.$queryRawUnsafe(`SELECT (data->>'objectID')::int AS id, SUM((data->>'quantity')::int)::int AS qty FROM activity WHERE type = 'Construction' AND user_id = '${user.id}'::bigint @@ -136,8 +135,7 @@ GROUP BY data->>'objectID';`); } export async function personalFiremakingStats(user: MUser) { - const result: { id: number; qty: number }[] = - await prisma.$queryRawUnsafe(`SELECT (data->>'burnableID')::int AS id, SUM((data->>'quantity')::int)::int AS qty + const result: { id: number; qty: number }[] = await prisma.$queryRawUnsafe(`SELECT (data->>'burnableID')::int AS id, SUM((data->>'quantity')::int)::int AS qty FROM activity WHERE type = 'Firemaking' AND user_id = '${user.id}'::bigint @@ -154,8 +152,7 @@ GROUP BY data->>'burnableID';`); } export async function personalWoodcuttingStats(user: MUser) { - const result: { id: number; qty: number }[] = - await prisma.$queryRawUnsafe(`SELECT (data->>'logID')::int AS id, SUM((data->>'quantity')::int)::int AS qty + const result: { id: number; qty: number }[] = await prisma.$queryRawUnsafe(`SELECT (data->>'logID')::int AS id, SUM((data->>'quantity')::int)::int AS qty FROM activity WHERE type = 'Woodcutting' AND user_id = '${user.id}'::bigint @@ -172,8 +169,7 @@ GROUP BY data->>'logID';`); } export async function personalMiningStats(user: MUser) { - const result: { id: number; qty: number }[] = - await prisma.$queryRawUnsafe(`SELECT (data->>'oreID')::int AS id, SUM((data->>'quantity')::int)::int AS qty + const result: { id: number; qty: number }[] = await prisma.$queryRawUnsafe(`SELECT (data->>'oreID')::int AS id, SUM((data->>'quantity')::int)::int AS qty FROM activity WHERE type = 'Mining' AND user_id = '${user.id}'::bigint @@ -190,8 +186,7 @@ GROUP BY data->>'oreID';`); } export async function personalHerbloreStats(user: MUser, stats: UserStats) { - const result: { id: number; qty: number }[] = - await prisma.$queryRawUnsafe(`SELECT (data->>'mixableID')::int AS id, SUM((data->>'quantity')::int)::int AS qty + const result: { id: number; qty: number }[] = await prisma.$queryRawUnsafe(`SELECT (data->>'mixableID')::int AS id, SUM((data->>'quantity')::int)::int AS qty FROM activity WHERE type = 'Herblore' AND user_id = '${user.id}'::bigint @@ -208,16 +203,14 @@ GROUP BY data->>'mixableID';`); return items; } export async function personalAlchingStats(user: MUser, includeAgilityAlching = true) { - const result: { id: number; qty: number }[] = - await prisma.$queryRawUnsafe(`SELECT (data->>'itemID')::int AS id, SUM((data->>'quantity')::int)::int AS qty + const result: { id: number; qty: number }[] = await prisma.$queryRawUnsafe(`SELECT (data->>'itemID')::int AS id, SUM((data->>'quantity')::int)::int AS qty FROM activity WHERE type = 'Alching' AND user_id = '${user.id}'::bigint AND data->>'itemID' IS NOT NULL AND completed = true GROUP BY data->>'itemID';`); - const agilityAlchRes: { id: number; qty: number }[] = - await prisma.$queryRawUnsafe(`SELECT (((data->>'alch')::json)->>'itemID')::int AS id, SUM((((data->>'alch')::json)->>'quantity')::int)::int AS qty + const agilityAlchRes: { id: number; qty: number }[] = await prisma.$queryRawUnsafe(`SELECT (((data->>'alch')::json)->>'itemID')::int AS id, SUM((((data->>'alch')::json)->>'quantity')::int)::int AS qty FROM activity WHERE type = 'Agility' AND user_id = '${user.id}'::bigint @@ -234,8 +227,7 @@ GROUP BY ((data->>'alch')::json)->>'itemID';`); return items; } export async function personalSmithingStats(user: MUser) { - const result: { id: number; qty: number }[] = - await prisma.$queryRawUnsafe(`SELECT (data->>'smithedBarID')::int AS id, SUM((data->>'quantity')::int)::int AS qty + const result: { id: number; qty: number }[] = await prisma.$queryRawUnsafe(`SELECT (data->>'smithedBarID')::int AS id, SUM((data->>'quantity')::int)::int AS qty FROM activity WHERE type = 'Smithing' AND user_id = '${user.id}'::bigint @@ -251,8 +243,7 @@ GROUP BY data->>'smithedBarID';`); return items; } export async function personalSmeltingStats(user: MUser) { - const result: { id: number; qty: number }[] = - await prisma.$queryRawUnsafe(`SELECT (data->>'barID')::int AS id, SUM((data->>'quantity')::int)::int AS qty + const result: { id: number; qty: number }[] = await prisma.$queryRawUnsafe(`SELECT (data->>'barID')::int AS id, SUM((data->>'quantity')::int)::int AS qty FROM activity WHERE type = 'Smelting' AND user_id = '${user.id}'::bigint @@ -268,8 +259,7 @@ GROUP BY data->>'barID';`); return items; } export async function personalSpellCastStats(user: MUser) { - const result: { id: number; qty: number }[] = - await prisma.$queryRawUnsafe(`SELECT (data->>'spellID')::int AS id, SUM((data->>'quantity')::int)::int AS qty + const result: { id: number; qty: number }[] = await prisma.$queryRawUnsafe(`SELECT (data->>'spellID')::int AS id, SUM((data->>'quantity')::int)::int AS qty FROM activity WHERE type = 'Casting' AND user_id = '${user.id}'::bigint @@ -279,15 +269,14 @@ GROUP BY data->>'spellID';`); return result.map(i => ({ castable: Castables.find(t => t.id === i.id)!, id: i.id, qty: i.qty })); } export async function personalCollectingStats(user: MUser) { - const result: { id: number; qty: number }[] = - await prisma.$queryRawUnsafe(`SELECT (data->>'collectableID')::int AS id, SUM((data->>'quantity')::int)::int AS qty + const result: { id: number; qty: number }[] = await prisma.$queryRawUnsafe(`SELECT (data->>'collectableID')::int AS id, SUM((data->>'quantity')::int)::int AS qty FROM activity WHERE type = 'Collecting' AND user_id = '${user.id}'::bigint AND data->>'collectableID' IS NOT NULL AND completed = true GROUP BY data->>'collectableID';`); - let bank = new Bank(); + const bank = new Bank(); for (const { id, qty } of result) { const col = collectables.find(t => t.item.id === id); if (!col) continue; @@ -323,8 +312,7 @@ export const dataPoints: readonly DataPiece[] = [ name: 'Personal Activity Types', perkTierNeeded: PerkTier.Four, run: async (user: MUser) => { - const result: { type: activity_type_enum; qty: number }[] = - await prisma.$queryRawUnsafe(`SELECT type, count(type)::int AS qty + const result: { type: activity_type_enum; qty: number }[] = await prisma.$queryRawUnsafe(`SELECT type, count(type)::int AS qty FROM activity WHERE completed = true AND user_id = ${BigInt(user.id)} @@ -357,8 +345,7 @@ GROUP BY type;`); name: 'Personal Monster KC', perkTierNeeded: PerkTier.Four, run: async (user: MUser) => { - const result: { id: number; kc: number }[] = - await prisma.$queryRawUnsafe(`SELECT (data->>'monsterID')::int as id, SUM((data->>'quantity')::int)::int AS kc + const result: { id: number; kc: number }[] = await prisma.$queryRawUnsafe(`SELECT (data->>'monsterID')::int as id, SUM((data->>'quantity')::int)::int AS kc FROM activity WHERE completed = true AND user_id = ${BigInt(user.id)} @@ -384,7 +371,7 @@ GROUP BY data->>'monsterID';`); .slice(0, 15) .map(i => [i[0].name, i[0].price * i[1]]); const everythingElse = items.slice(20, items.length); - let everythingElseBank = new Bank(); + const everythingElseBank = new Bank(); for (const i of everythingElse) everythingElseBank.add(i[0].name, i[1]); dataPoints.push(['Everything else', everythingElseBank.value()]); const buffer = await barChart( @@ -418,8 +405,7 @@ GROUP BY data->>'monsterID';`); name: 'Global Inferno Death Times', perkTierNeeded: PerkTier.Four, run: async () => { - const result: { mins: number; count: number }[] = - await prisma.$queryRaw`SELECT mins, COUNT(mins)::int FROM (SELECT ((data->>'deathTime')::int / 1000 / 60) as mins + const result: { mins: number; count: number }[] = await prisma.$queryRaw`SELECT mins, COUNT(mins)::int FROM (SELECT ((data->>'deathTime')::int / 1000 / 60) as mins FROM activity WHERE type = 'Inferno' AND completed = true @@ -438,8 +424,7 @@ GROUP BY mins;`; name: 'Personal Inferno Death Times', perkTierNeeded: PerkTier.Four, run: async (user: MUser) => { - const result: { mins: number; count: number }[] = - await prisma.$queryRawUnsafe(`SELECT mins, COUNT(mins)::int FROM (SELECT ((data->>'deathTime')::int / 1000 / 60) as mins + const result: { mins: number; count: number }[] = await prisma.$queryRawUnsafe(`SELECT mins, COUNT(mins)::int FROM (SELECT ((data->>'deathTime')::int / 1000 / 60) as mins FROM activity WHERE type = 'Inferno' AND user_id = ${BigInt(user.id)} @@ -470,7 +455,7 @@ GROUP BY mins;`); }); let completedAt = null; let postFirstCapeCompletions = 0; - let totalCost = new Bank(); + const totalCost = new Bank(); for (let i = 0; i < activities.length; i++) { const data = activities[i].data as unknown as InfernoOptions; if (completedAt === null && !data.deathTime) { @@ -494,8 +479,7 @@ GROUP BY mins;`); name: 'Personal TOB Wipes', perkTierNeeded: PerkTier.Four, run: async (user: MUser) => { - const result: { wiped_room: number; count: number }[] = - await prisma.$queryRawUnsafe(`SELECT (data->>'wipedRoom')::int AS wiped_room, COUNT(data->>'wipedRoom')::int + const result: { wiped_room: number; count: number }[] = await prisma.$queryRawUnsafe(`SELECT (data->>'wipedRoom')::int AS wiped_room, COUNT(data->>'wipedRoom')::int FROM activity WHERE type = 'TheatreOfBlood' AND completed = true @@ -518,8 +502,7 @@ GROUP BY 1;`); name: 'Global TOB Wipes', perkTierNeeded: PerkTier.Four, run: async () => { - const result: { wiped_room: number; count: number }[] = - await prisma.$queryRaw`SELECT (data->>'wipedRoom')::int AS wiped_room, COUNT(data->>'wipedRoom')::int + const result: { wiped_room: number; count: number }[] = await prisma.$queryRaw`SELECT (data->>'wipedRoom')::int AS wiped_room, COUNT(data->>'wipedRoom')::int FROM activity WHERE type = 'TheatreOfBlood' AND completed = true @@ -561,8 +544,7 @@ WHERE "skills.${skillName}" = 200000000::int;`) as Promise<{ qty: number; skill_ name: 'Personal Farmed Crops', perkTierNeeded: PerkTier.Four, run: async (user: MUser) => { - const result: { plant: string; qty: number }[] = - await prisma.$queryRawUnsafe(`SELECT data->>'plantsName' as plant, COUNT(data->>'plantsName')::int AS qty + const result: { plant: string; qty: number }[] = await prisma.$queryRawUnsafe(`SELECT data->>'plantsName' as plant, COUNT(data->>'plantsName')::int AS qty FROM activity WHERE type = 'Farming' AND data->>'plantsName' IS NOT NULL @@ -581,8 +563,7 @@ GROUP BY data->>'plantsName'`); name: 'Global Farmed Crops', perkTierNeeded: PerkTier.Four, run: async () => { - const result: { plant: string; qty: number }[] = - await prisma.$queryRaw`SELECT data->>'plantsName' as plant, COUNT(data->>'plantsName')::int AS qty + const result: { plant: string; qty: number }[] = await prisma.$queryRaw`SELECT data->>'plantsName' as plant, COUNT(data->>'plantsName')::int AS qty FROM activity WHERE type = 'Farming' AND data->>'plantsName' IS NOT NULL @@ -796,7 +777,7 @@ ${result const result = await prisma.$queryRawUnsafe( 'SELECT COUNT(*)::int FROM users WHERE "minion.ironman" = true;' ); - return `There are ${parseInt(result[0].count).toLocaleString()} ironman minions!`; + return `There are ${Number.parseInt(result[0].count).toLocaleString()} ironman minions!`; } }, { @@ -823,7 +804,7 @@ GROUP BY "bankBackground";`); return result .map( (res: any) => - `**${getBankBgById(res.bankBackground).name}:** ${parseInt(res.count).toLocaleString()}` + `**${getBankBgById(res.bankBackground).name}:** ${Number.parseInt(res.count).toLocaleString()}` ) .join('\n'); } @@ -833,7 +814,7 @@ GROUP BY "bankBackground";`); perkTierNeeded: PerkTier.Four, run: async () => { const result = await prisma.$queryRawUnsafe('SELECT SUM ("sacrificedValue") AS total FROM users;'); - return `There has been ${parseInt(result[0].total).toLocaleString()} GP worth of items sacrificed!`; + return `There has been ${Number.parseInt(result[0].total).toLocaleString()} GP worth of items sacrificed!`; } }, { @@ -859,7 +840,7 @@ GROUP BY "bankBackground";`); str += Object.entries(totalBank) .sort(([, qty1], [, qty2]) => qty2 - qty1) .map(([monID, qty]) => { - return `${Monsters.get(parseInt(monID))?.name}: ${qty.toLocaleString()}`; + return `${Monsters.get(Number.parseInt(monID))?.name}: ${qty.toLocaleString()}`; }) .join('\n'); @@ -889,7 +870,7 @@ GROUP BY "bankBackground";`); return Object.entries(totalBank) .map( ([clueID, qty]) => - `**${ClueTiers.find(t => t.id === parseInt(clueID))?.name}:** ${qty.toLocaleString()}` + `**${ClueTiers.find(t => t.id === Number.parseInt(clueID))?.name}:** ${qty.toLocaleString()}` ) .join('\n'); } @@ -903,8 +884,8 @@ GROUP BY "bankBackground";`); let res = `${Emoji.Casket} **${user.minionName}'s Clue Scores:**\n\n`; for (const [clueID, clueScore] of Object.entries(clueScores.bank)) { - const clue = ClueTiers.find(c => c.id === parseInt(clueID)); - res += `**${clue!.name}**: ${clueScore.toLocaleString()}\n`; + const clue = ClueTiers.find(c => c.id === Number.parseInt(clueID)); + res += `**${clue?.name}**: ${clueScore.toLocaleString()}\n`; } return res; } @@ -920,13 +901,13 @@ GROUP BY "bankBackground";`); name: 'Personal Agility Stats', perkTierNeeded: null, run: async (user, stats) => { - const entries = Object.entries(stats.laps_scores as ItemBank).map(arr => [parseInt(arr[0]), arr[1]]); + const entries = Object.entries(stats.laps_scores as ItemBank).map(arr => [Number.parseInt(arr[0]), arr[1]]); const sepulchreCount = await getMinigameScore(user.id, 'sepulchre'); if (sepulchreCount === 0 && entries.length === 0) { return "You haven't done any laps yet! Sad."; } const data = `${entries - .map(([id, qty]) => `**${Agility.Courses.find(c => c.id === id)!.name}:** ${qty}`) + .map(([id, qty]) => `**${Agility.Courses.find(c => c.id === id)?.name}:** ${qty}`) .join('\n')}\n**Hallowed Sepulchre:** ${sepulchreCount}`; return data; } @@ -1043,7 +1024,7 @@ GROUP BY "bankBackground";`); items_sent: true } }); - let items = new Bank(); + const items = new Bank(); for (const g of giveaways) { items.add(g.items_sent as ItemBank); } @@ -1064,7 +1045,7 @@ GROUP BY "bankBackground";`); items_sent: true } }); - let items = new Bank(); + const items = new Bank(); for (const g of giveaways) { items.add(g.items_sent as ItemBank); } @@ -1193,9 +1174,7 @@ LIMIT 5;` ${luckiest .map( i => - `${getUsername(i.id)}: ${i.points_per_item.toLocaleString()} points per item / 1 in ${( - i.raids_total_kc / i.total_cox_items - ).toFixed(1)} raids` + `${getUsername(i.id)}: ${i.points_per_item.toLocaleString()} points per item / 1 in ${(i.raids_total_kc / i.total_cox_items).toFixed(1)} raids` ) .join('\n')} @@ -1203,9 +1182,7 @@ ${luckiest ${unluckiest .map( i => - `${getUsername(i.id)}: ${i.points_per_item.toLocaleString()} points per item / 1 in ${( - i.raids_total_kc / i.total_cox_items - ).toFixed(1)} raids` + `${getUsername(i.id)}: ${i.points_per_item.toLocaleString()} points per item / 1 in ${(i.raids_total_kc / i.total_cox_items).toFixed(1)} raids` ) .join('\n')}`; return { diff --git a/src/mahoji/lib/abstracted_commands/tearsOfGuthixCommand.ts b/src/mahoji/lib/abstracted_commands/tearsOfGuthixCommand.ts index ebc7f1c4e84..e99650c426e 100644 --- a/src/mahoji/lib/abstracted_commands/tearsOfGuthixCommand.ts +++ b/src/mahoji/lib/abstracted_commands/tearsOfGuthixCommand.ts @@ -1,4 +1,4 @@ -import { notEmpty, objectEntries, Time } from 'e'; +import { Time, notEmpty, objectEntries } from 'e'; import { Emoji } from '../../../lib/constants'; import { SkillsEnum } from '../../../lib/skilling/types'; diff --git a/src/mahoji/lib/abstracted_commands/temporossCommand.ts b/src/mahoji/lib/abstracted_commands/temporossCommand.ts index ef1767e59ac..27ade97a8db 100644 --- a/src/mahoji/lib/abstracted_commands/temporossCommand.ts +++ b/src/mahoji/lib/abstracted_commands/temporossCommand.ts @@ -1,7 +1,7 @@ -import { calcWhatPercent, reduceNumByPercent, Time } from 'e'; +import { Time, calcWhatPercent, reduceNumByPercent } from 'e'; import { getMinigameScore } from '../../../lib/settings/minigames'; -import { TemporossActivityTaskOptions } from '../../../lib/types/minions'; +import type { TemporossActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; diff --git a/src/mahoji/lib/abstracted_commands/tiaraRunecraftCommand.ts b/src/mahoji/lib/abstracted_commands/tiaraRunecraftCommand.ts index 05ec26af51f..f71b59072ac 100644 --- a/src/mahoji/lib/abstracted_commands/tiaraRunecraftCommand.ts +++ b/src/mahoji/lib/abstracted_commands/tiaraRunecraftCommand.ts @@ -3,7 +3,7 @@ import { Bank } from 'oldschooljs'; import { SkillsEnum } from 'oldschooljs/dist/constants'; import Runecraft from '../../../lib/skilling/skills/runecraft'; -import { TiaraRunecraftActivityTaskOptions } from '../../../lib/types/minions'; +import type { TiaraRunecraftActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, stringMatches } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; diff --git a/src/mahoji/lib/abstracted_commands/titheFarmCommand.ts b/src/mahoji/lib/abstracted_commands/titheFarmCommand.ts index aa4a71b8185..0b54b08c3a7 100644 --- a/src/mahoji/lib/abstracted_commands/titheFarmCommand.ts +++ b/src/mahoji/lib/abstracted_commands/titheFarmCommand.ts @@ -1,10 +1,10 @@ -import { ChatInputCommandInteraction } from 'discord.js'; +import type { ChatInputCommandInteraction } from 'discord.js'; import { Time } from 'e'; import { Bank } from 'oldschooljs'; import { Emoji } from '../../../lib/constants'; import TitheFarmBuyables from '../../../lib/data/buyables/titheFarmBuyables'; -import { TitheFarmActivityTaskOptions } from '../../../lib/types/minions'; +import type { TitheFarmActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, stringMatches } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { handleMahojiConfirmation } from '../../../lib/util/handleMahojiConfirmation'; @@ -68,9 +68,7 @@ export async function titheFarmShopCommand( _quantity?: number ) { const buyable = TitheFarmBuyables.find( - item => - stringMatches(buyableName, item.name) || - (item.aliases && item.aliases.some(alias => stringMatches(alias, buyableName))) + item => stringMatches(buyableName, item.name) || item.aliases?.some(alias => stringMatches(alias, buyableName)) ); if (!buyable) { @@ -90,7 +88,7 @@ export async function titheFarmShopCommand( return `You need ${cost} Tithe Farm points to make this purchase.`; } - let purchaseMsg = `${loot} for ${cost} Tithe Farm points`; + const purchaseMsg = `${loot} for ${cost} Tithe Farm points`; await handleMahojiConfirmation(interaction, `${user}, please confirm that you want to purchase ${purchaseMsg}.`); await userStatsUpdate( diff --git a/src/mahoji/lib/abstracted_commands/tobCommand.ts b/src/mahoji/lib/abstracted_commands/tobCommand.ts index 80c508c3bd4..73d96f59740 100644 --- a/src/mahoji/lib/abstracted_commands/tobCommand.ts +++ b/src/mahoji/lib/abstracted_commands/tobCommand.ts @@ -6,13 +6,13 @@ import { randomVariation } from 'oldschooljs/dist/util'; import { Emoji } from '../../../lib/constants'; import { getSimilarItems } from '../../../lib/data/similarItems'; import { + TENTACLE_CHARGES_PER_RAID, baseTOBUniques, calcTOBBaseDuration, calculateTOBDeaths, calculateTOBUserGearPercents, createTOBRaid, - minimumTOBSuppliesNeeded, - TENTACLE_CHARGES_PER_RAID + minimumTOBSuppliesNeeded } from '../../../lib/data/tob'; import { checkUserCanUseDegradeableItem, degradeItem } from '../../../lib/degradeableItems'; import { trackLoot } from '../../../lib/lootTrack'; @@ -20,8 +20,8 @@ import { blowpipeDarts } from '../../../lib/minions/functions/blowpipeCommand'; import getUserFoodFromBank from '../../../lib/minions/functions/getUserFoodFromBank'; import { setupParty } from '../../../lib/party'; import { getMinigameScore } from '../../../lib/settings/minigames'; -import { MakePartyOptions } from '../../../lib/types'; -import { TheatreOfBloodTaskOptions } from '../../../lib/types/minions'; +import type { MakePartyOptions } from '../../../lib/types'; +import type { TheatreOfBloodTaskOptions } from '../../../lib/types/minions'; import { channelIsSendable, formatDuration, formatSkillRequirements, skillsMeetRequirements } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -48,7 +48,7 @@ export async function calcTOBInput(u: MUser) { items.add('Super combat potion(4)', 1); items.add('Ranging potion(4)', 1); - let brewsNeeded = Math.max(1, 6 - Math.max(1, Math.ceil((kc + 1) / 10))); + const brewsNeeded = Math.max(1, 6 - Math.max(1, Math.ceil((kc + 1) / 10))); const restoresNeeded = Math.max(2, Math.floor(brewsNeeded / 3)); let healingNeeded = 60; @@ -79,7 +79,7 @@ export async function checkTOBUser( user: MUser, isHardMode: boolean, teamSize?: number, - quantity: number = 1 + quantity = 1 ): Promise<[false] | [true, string]> { if (!user.user.minion_hasBought) { return [true, `${user.usernameOrMention} doesn't have a minion`]; @@ -237,7 +237,7 @@ export async function checkTOBTeam( users: MUser[], isHardMode: boolean, solo: boolean, - quantity: number = 1 + quantity = 1 ): Promise { const userWithoutSupplies = users.find(u => !u.bank.has(minimumTOBSuppliesNeeded)); if (userWithoutSupplies) { @@ -250,9 +250,7 @@ export async function checkTOBTeam( for (const user of users) { if (user.minionIsBusy) return `${user.usernameOrMention}'s minion is busy.`; const checkResult = await checkTOBUser(user, isHardMode, users.length, quantity); - if (!checkResult[0]) { - continue; - } else { + if (checkResult[1]) { return checkResult[1]; } } @@ -319,7 +317,7 @@ export async function tobStartCommand( return "Your minion is busy, so you can't start a raid."; } - let maxSize = mahojiParseNumber({ input: maxSizeInput, min: 2, max: 5 }) ?? 5; + const maxSize = mahojiParseNumber({ input: maxSizeInput, min: 2, max: 5 }) ?? 5; const partyOptions: MakePartyOptions = { leader: user, @@ -391,7 +389,7 @@ export async function tobStartCommand( let totalDuration = 0; let totalFakeDuration = 0; - let deaths: number[][][] = []; + const deaths: number[][][] = []; const wipedRooms: (number | null)[] = []; for (let i = 0; i < qty; i++) { @@ -430,7 +428,7 @@ export async function tobStartCommand( .clone() .add('Coins', 100_000) .add(blowpipeData.dartID!, Math.floor(Math.min(blowpipeData.dartQuantity, 156))) - .add(u.gear.range.ammo!.item, 100) + .add(u.gear.range.ammo?.item, 100) .multiply(qty) ); await userStatsBankUpdate(u.id, 'tob_cost', realCost); diff --git a/src/mahoji/lib/abstracted_commands/trekCommand.ts b/src/mahoji/lib/abstracted_commands/trekCommand.ts index ce623cfd861..428a7a5aa65 100644 --- a/src/mahoji/lib/abstracted_commands/trekCommand.ts +++ b/src/mahoji/lib/abstracted_commands/trekCommand.ts @@ -1,4 +1,4 @@ -import { ChatInputCommandInteraction } from 'discord.js'; +import type { ChatInputCommandInteraction } from 'discord.js'; import { objectEntries, randInt, reduceNumByPercent } from 'e'; import { Bank } from 'oldschooljs'; @@ -6,10 +6,10 @@ import TrekShopItems, { TrekExperience } from '../../../lib/data/buyables/trekBu import { MorytaniaDiary, userhasDiaryTier } from '../../../lib/diaries'; import { GearStat } from '../../../lib/gear/types'; import { difficulties, rewardTokens, trekBankBoosts } from '../../../lib/minions/data/templeTrekking'; -import { AddXpParams, GearRequirement } from '../../../lib/minions/types'; +import type { AddXpParams, GearRequirement } from '../../../lib/minions/types'; import { getMinigameScore } from '../../../lib/settings/minigames'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { TempleTrekkingActivityTaskOptions } from '../../../lib/types/minions'; +import type { TempleTrekkingActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, percentChance, readableStatName, stringMatches } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -41,9 +41,9 @@ export async function trekCommand(user: MUser, channelID: string, difficulty: st ); if (setup === 'melee') { - if (maxMeleeStat[0] !== GearStat.AttackCrush) delete newRequirements.attack_crush; - if (maxMeleeStat[0] !== GearStat.AttackSlash) delete newRequirements.attack_slash; - if (maxMeleeStat[0] !== GearStat.AttackStab) delete newRequirements.attack_stab; + if (maxMeleeStat[0] !== GearStat.AttackCrush) newRequirements.attack_crush = undefined; + if (maxMeleeStat[0] !== GearStat.AttackSlash) newRequirements.attack_slash = undefined; + if (maxMeleeStat[0] !== GearStat.AttackStab) newRequirements.attack_stab = undefined; } else { newRequirements = requirements; } @@ -53,7 +53,7 @@ export async function trekCommand(user: MUser, channelID: string, difficulty: st return `You don't have the requirements to do ${tier.difficulty} treks! Your ${readableStatName( unmetKey! )} stat in your ${setup} setup is ${has}, but you need atleast ${ - tier.minimumGearRequirements[setup]![unmetKey!] + tier.minimumGearRequirements[setup]?.[unmetKey!] }.`; } } @@ -150,9 +150,7 @@ export async function trekShop( ) { const userBank = user.bank; const specifiedItem = TrekShopItems.find( - item => - stringMatches(reward, item.name) || - (item.aliases && item.aliases.some(alias => stringMatches(alias, reward))) + item => stringMatches(reward, item.name) || item.aliases?.some(alias => stringMatches(alias, reward)) ); if (!specifiedItem) { @@ -161,22 +159,22 @@ export async function trekShop( }).join(', ')}.`; } - let inbankquantity = + const inbankquantity = difficulty === 'Easy' ? userBank.amount(rewardTokens.easy) : difficulty === 'Medium' - ? userBank.amount(rewardTokens.medium) - : userBank.amount(rewardTokens.hard); + ? userBank.amount(rewardTokens.medium) + : userBank.amount(rewardTokens.hard); if (quantity === undefined) { quantity = inbankquantity; } if (quantity > inbankquantity || quantity === 0) { return "You don't have enough reward tokens for that."; } - let outItems = new Bank(); + const outItems = new Bank(); - let inItems = new Bank(); - let outXP: AddXpParams[] = [ + const inItems = new Bank(); + const outXP: AddXpParams[] = [ { skillName: SkillsEnum.Agility, amount: 0, diff --git a/src/mahoji/lib/abstracted_commands/troubleBrewingCommand.ts b/src/mahoji/lib/abstracted_commands/troubleBrewingCommand.ts index dad7f59c2c0..0d954cf1caa 100644 --- a/src/mahoji/lib/abstracted_commands/troubleBrewingCommand.ts +++ b/src/mahoji/lib/abstracted_commands/troubleBrewingCommand.ts @@ -6,8 +6,8 @@ import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; export async function troubleBrewingStartCommand(user: MUser, channelID: string) { - let timePerGame = Time.Minute * 20; - let maxTripLength = calcMaxTripLength(user, 'TroubleBrewing'); + const timePerGame = Time.Minute * 20; + const maxTripLength = calcMaxTripLength(user, 'TroubleBrewing'); const quantity = Math.floor(maxTripLength / timePerGame); const duration = quantity * timePerGame; diff --git a/src/mahoji/lib/abstracted_commands/underwaterCommand.ts b/src/mahoji/lib/abstracted_commands/underwaterCommand.ts index 476d521271e..98222dbee54 100644 --- a/src/mahoji/lib/abstracted_commands/underwaterCommand.ts +++ b/src/mahoji/lib/abstracted_commands/underwaterCommand.ts @@ -1,11 +1,11 @@ -import { randFloat, reduceNumByPercent, Time } from 'e'; +import { Time, randFloat, reduceNumByPercent } from 'e'; import { Bank } from 'oldschooljs'; -import { UnderwaterAgilityThievingTrainingSkill } from '../../../lib/constants'; +import type { UnderwaterAgilityThievingTrainingSkill } from '../../../lib/constants'; import { formatDuration } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; -import { UnderwaterAgilityThievingTaskOptions } from './../../../lib/types/minions'; +import type { UnderwaterAgilityThievingTaskOptions } from './../../../lib/types/minions'; export async function underwaterAgilityThievingCommand( channelID: string, @@ -25,9 +25,10 @@ export async function underwaterAgilityThievingCommand( return 'You need Graceful top, legs and gloves to do Underwater Agility and Thieving.'; } - if (minutes < 1 || !Number.isInteger(minutes) || isNaN(minutes)) return 'Please specify a valid number of minutes.'; + if (minutes < 1 || !Number.isInteger(minutes) || Number.isNaN(minutes)) + return 'Please specify a valid number of minutes.'; - let tripLength = Time.Minute * minutes; + const tripLength = Time.Minute * minutes; if (tripLength > maxTripLength) { return `${user.minionName} can't go on trips longer than ${formatDuration( diff --git a/src/mahoji/lib/abstracted_commands/useCommand.ts b/src/mahoji/lib/abstracted_commands/useCommand.ts index 8cc1c9e35d6..d0ecbb56376 100644 --- a/src/mahoji/lib/abstracted_commands/useCommand.ts +++ b/src/mahoji/lib/abstracted_commands/useCommand.ts @@ -1,6 +1,6 @@ import { notEmpty } from 'e'; import { Bank } from 'oldschooljs'; -import { Item } from 'oldschooljs/dist/meta/types'; +import type { Item } from 'oldschooljs/dist/meta/types'; import { BitField } from '../../../lib/constants'; import { assert } from '../../../lib/util'; diff --git a/src/mahoji/lib/abstracted_commands/volcanicMineCommand.ts b/src/mahoji/lib/abstracted_commands/volcanicMineCommand.ts index 7176aba7412..4e450036b71 100644 --- a/src/mahoji/lib/abstracted_commands/volcanicMineCommand.ts +++ b/src/mahoji/lib/abstracted_commands/volcanicMineCommand.ts @@ -1,10 +1,10 @@ -import { ChatInputCommandInteraction } from 'discord.js'; -import { objectEntries, Time } from 'e'; +import type { ChatInputCommandInteraction } from 'discord.js'; +import { Time, objectEntries } from 'e'; import { Bank } from 'oldschooljs'; import { getMinigameScore } from '../../../lib/settings/minigames'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; import { formatDuration, formatSkillRequirements, hasSkillReqs, stringMatches } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -159,7 +159,7 @@ export async function volcanicMineCommand(user: MUser, channelID: string, gameQu await transactItems({ userID: user.id, itemsToRemove: suppliesUsage }); - let duration = VolcanicMineGameTime * gameQuantity; + const duration = VolcanicMineGameTime * gameQuantity; const str = `${ user.minionName @@ -222,9 +222,7 @@ export async function volcanicMineShopCommand( } }); - return `You sucessfully bought **${quantity.toLocaleString()}x ${shopItem.name}** for ${( - shopItem.cost * quantity - ).toLocaleString()} Volcanic Mine points.${ + return `You sucessfully bought **${quantity.toLocaleString()}x ${shopItem.name}** for ${(shopItem.cost * quantity).toLocaleString()} Volcanic Mine points.${ shopItem.clOnly ? `\n${quantity > 1 ? 'These items were' : 'This item was'} directly added to your collection log.` : '' diff --git a/src/mahoji/lib/abstracted_commands/warriorsGuildCommand.ts b/src/mahoji/lib/abstracted_commands/warriorsGuildCommand.ts index 7612b07fc38..37242b6ba87 100644 --- a/src/mahoji/lib/abstracted_commands/warriorsGuildCommand.ts +++ b/src/mahoji/lib/abstracted_commands/warriorsGuildCommand.ts @@ -1,7 +1,7 @@ import { Time } from 'e'; import { Bank } from 'oldschooljs'; -import { ActivityTaskOptionsWithQuantity, AnimatedArmourActivityTaskOptions } from '../../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity, AnimatedArmourActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -120,7 +120,7 @@ async function cyclopsCommand(user: MUser, channelID: string, quantity: number | type: 'Cyclops' }); - let response = `${user.minionName} is now off to kill ${quantity}x Cyclops, it'll take around ${formatDuration( + const response = `${user.minionName} is now off to kill ${quantity}x Cyclops, it'll take around ${formatDuration( duration )} to finish. ${ hasAttackCape diff --git a/src/mahoji/lib/abstracted_commands/wintertodtCommand.ts b/src/mahoji/lib/abstracted_commands/wintertodtCommand.ts index 4ccd99cc556..523ed0c86d5 100644 --- a/src/mahoji/lib/abstracted_commands/wintertodtCommand.ts +++ b/src/mahoji/lib/abstracted_commands/wintertodtCommand.ts @@ -1,11 +1,11 @@ -import { calcWhatPercent, reduceNumByPercent, Time } from 'e'; +import { Time, calcWhatPercent, reduceNumByPercent } from 'e'; import { Bank } from 'oldschooljs'; import { SkillsEnum } from 'oldschooljs/dist/constants'; import { Eatables } from '../../../lib/data/eatables'; import { warmGear } from '../../../lib/data/filterables'; import { trackLoot } from '../../../lib/lootTrack'; -import { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; +import type { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; import { formatDuration } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; @@ -59,7 +59,7 @@ export async function wintertodtCommand(user: MUser, channelID: string) { continue; } - let foodStr: string = `**Food:** ${healAmountNeeded} HP/kill`; + let foodStr = `**Food:** ${healAmountNeeded} HP/kill`; if (healAmountNeeded !== baseHealAmountNeeded) { foodStr += `. Reduced from ${baseHealAmountNeeded}, -${calcWhatPercent( diff --git a/src/mahoji/lib/abstracted_commands/zalcanoCommand.ts b/src/mahoji/lib/abstracted_commands/zalcanoCommand.ts index f4b54bae882..b524f482ec6 100644 --- a/src/mahoji/lib/abstracted_commands/zalcanoCommand.ts +++ b/src/mahoji/lib/abstracted_commands/zalcanoCommand.ts @@ -1,9 +1,9 @@ -import { calcWhatPercent, percentChance, reduceNumByPercent, Time } from 'e'; +import { Time, calcWhatPercent, percentChance, reduceNumByPercent } from 'e'; import { ZALCANO_ID } from '../../../lib/constants'; import removeFoodFromUser from '../../../lib/minions/functions/removeFoodFromUser'; import { soteSkillRequirements } from '../../../lib/skilling/functions/questRequirements'; -import { ZalcanoActivityTaskOptions } from '../../../lib/types/minions'; +import type { ZalcanoActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, hasSkillReqs } from '../../../lib/util'; import addSubTaskToActivityTask from '../../../lib/util/addSubTaskToActivityTask'; import { calcMaxTripLength } from '../../../lib/util/calcMaxTripLength'; diff --git a/src/mahoji/lib/bingo/BingoManager.ts b/src/mahoji/lib/bingo/BingoManager.ts index f846a0869db..a1276545079 100644 --- a/src/mahoji/lib/bingo/BingoManager.ts +++ b/src/mahoji/lib/bingo/BingoManager.ts @@ -1,6 +1,6 @@ -import { type Bingo, Prisma } from '@prisma/client'; +import type { Bingo, Prisma } from '@prisma/client'; import { ButtonBuilder, ButtonStyle, userMention } from 'discord.js'; -import { chunk, noOp, Time } from 'e'; +import { Time, chunk, noOp } from 'e'; import groupBy from 'lodash/groupBy'; import { Bank } from 'oldschooljs'; import { toKMB } from 'oldschooljs/dist/util'; @@ -8,11 +8,12 @@ import * as ss from 'simple-statistics'; import { Emoji } from '../../../lib/constants'; import { prisma } from '../../../lib/settings/prisma'; -import { ItemBank } from '../../../lib/types'; +import type { ItemBank } from '../../../lib/types'; import getOSItem from '../../../lib/util/getOSItem'; import { addBanks } from '../../../lib/util/smallUtils'; import { sendToChannelID } from '../../../lib/util/webhook'; -import { generateTileName, isGlobalTile, rowsForSquare, StoredBingoTile, UniversalBingoTile } from './bingoUtil'; +import type { StoredBingoTile, UniversalBingoTile } from './bingoUtil'; +import { generateTileName, isGlobalTile, rowsForSquare } from './bingoUtil'; import { globalBingoTiles } from './globalTiles'; export const BingoTrophies = [ @@ -259,7 +260,7 @@ export class BingoManager { team.tilesCompletedCount >= t.guaranteedAt || 100 - t.percentile <= ss.quantileRank(tilesCompletedCounts, team.tilesCompletedCount) * 100 - )[0] ?? null + )[0] ?? null : null, rank: index + 1 })) diff --git a/src/mahoji/lib/bingo/bingoUtil.ts b/src/mahoji/lib/bingo/bingoUtil.ts index 1ebdf21db24..59eaf0b3bb3 100644 --- a/src/mahoji/lib/bingo/bingoUtil.ts +++ b/src/mahoji/lib/bingo/bingoUtil.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { ItemBank } from '../../../lib/types'; +import type { ItemBank } from '../../../lib/types'; import { getItem } from '../../../lib/util/getOSItem'; import { globalBingoTiles } from './globalTiles'; diff --git a/src/mahoji/lib/bingo/globalTiles.ts b/src/mahoji/lib/bingo/globalTiles.ts index 4f4786855a5..72034fe43b9 100644 --- a/src/mahoji/lib/bingo/globalTiles.ts +++ b/src/mahoji/lib/bingo/globalTiles.ts @@ -1,8 +1,8 @@ -import { Bank } from 'oldschooljs'; +import type { Bank } from 'oldschooljs'; import { championScrolls, skillingPetsCL } from '../../../lib/data/CollectionsExport'; import resolveItems from '../../../lib/util/resolveItems'; -import { GlobalBingoTile } from './bingoUtil'; +import type { GlobalBingoTile } from './bingoUtil'; const otherSpiritShieldParts = resolveItems(['Blessed spirit shield', 'Holy elixir', 'Spirit shield']); const allSpiritShieldSets = [ diff --git a/src/mahoji/lib/events.ts b/src/mahoji/lib/events.ts index 9b2b59d6896..66a6ea061b8 100644 --- a/src/mahoji/lib/events.ts +++ b/src/mahoji/lib/events.ts @@ -1,10 +1,10 @@ import { bulkUpdateCommands } from 'mahoji/dist/lib/util'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; import { DEV_SERVER_ID, production } from '../../config'; import { cacheBadges } from '../../lib/badges'; import { syncBlacklists } from '../../lib/blacklists'; -import { Channel, DISABLED_COMMANDS, globalConfig, META_CONSTANTS } from '../../lib/constants'; +import { Channel, DISABLED_COMMANDS, META_CONSTANTS, globalConfig } from '../../lib/constants'; import { initCrons } from '../../lib/crons'; import { GrandExchange } from '../../lib/grandExchange'; import { prisma } from '../../lib/settings/prisma'; @@ -39,8 +39,10 @@ export async function onStartup() { update: {} }); - for (const command of disabledCommands!.disabled_commands) { - DISABLED_COMMANDS.add(command); + if (disabledCommands.disabled_commands) { + for (const command of disabledCommands.disabled_commands) { + DISABLED_COMMANDS.add(command); + } } // Sync blacklists diff --git a/src/mahoji/lib/inhibitors.ts b/src/mahoji/lib/inhibitors.ts index a3d6f3e2623..0a5f70f33ee 100644 --- a/src/mahoji/lib/inhibitors.ts +++ b/src/mahoji/lib/inhibitors.ts @@ -1,19 +1,11 @@ -import { - ComponentType, - DMChannel, - Guild, - GuildMember, - InteractionReplyOptions, - PermissionsBitField, - TextChannel, - User -} from 'discord.js'; +import type { DMChannel, Guild, GuildMember, InteractionReplyOptions, TextChannel, User } from 'discord.js'; +import { ComponentType, PermissionsBitField } from 'discord.js'; import { OWNER_IDS, SupportServer } from '../../config'; import { BLACKLISTED_GUILDS, BLACKLISTED_USERS } from '../../lib/blacklists'; -import { BadgesEnum, BitField, Channel, DISABLED_COMMANDS, minionBuyButton, PerkTier } from '../../lib/constants'; +import { BadgesEnum, BitField, Channel, DISABLED_COMMANDS, PerkTier, minionBuyButton } from '../../lib/constants'; import { perkTierCache, syncPerkTierOfUser } from '../../lib/perkTiers'; -import { CategoryFlag } from '../../lib/types'; +import type { CategoryFlag } from '../../lib/types'; import { formatDuration } from '../../lib/util'; import { minionIsBusy } from '../../lib/util/minionIsBusy'; import { mahojiGuildSettingsFetch, untrustedGuildSettingsCache } from '../guildSettings'; diff --git a/src/mahoji/lib/mahojiCommandOptions.ts b/src/mahoji/lib/mahojiCommandOptions.ts index 65cb33d51af..a6030a08e52 100644 --- a/src/mahoji/lib/mahojiCommandOptions.ts +++ b/src/mahoji/lib/mahojiCommandOptions.ts @@ -1,9 +1,10 @@ import { toTitleCase } from '@oldschoolgg/toolkit'; -import { APIApplicationCommandOptionChoice, ApplicationCommandOptionType } from 'discord.js'; +import type { APIApplicationCommandOptionChoice } from 'discord.js'; +import { ApplicationCommandOptionType } from 'discord.js'; import { uniqueArr } from 'e'; -import { CommandOption } from 'mahoji/dist/lib/types'; +import type { CommandOption } from 'mahoji/dist/lib/types'; import { Bank, Items } from 'oldschooljs'; -import { Item, ItemBank } from 'oldschooljs/dist/meta/types'; +import type { Item, ItemBank } from 'oldschooljs/dist/meta/types'; import { baseFilters, filterableTypes } from '../../lib/data/filterables'; import { GearSetupTypes } from '../../lib/gear/types'; @@ -20,7 +21,7 @@ export const filterOption: CommandOption = { description: 'The filter you want to use.', required: false, autocomplete: async (value: string) => { - let res = !value + const res = !value ? filterableTypes : [...filterableTypes].filter(filter => filter.name.toLowerCase().includes(value.toLowerCase())); return [...res] @@ -94,7 +95,7 @@ export const equippedItemOption = (): CommandOption => ({ autocomplete: async (value, user) => { const mUser = await mUserFetch(user.id); - let results: APIApplicationCommandOptionChoice[] = []; + const results: APIApplicationCommandOptionChoice[] = []; const entries: [string, Item[]][] = Object.entries(mUser.gear).map(entry => [ entry[0], entry[1].allItems(false).map(getOSItem) diff --git a/src/mahoji/lib/postCommand.ts b/src/mahoji/lib/postCommand.ts index e035601aaf0..5df87c2ef58 100644 --- a/src/mahoji/lib/postCommand.ts +++ b/src/mahoji/lib/postCommand.ts @@ -1,11 +1,11 @@ -import { CommandOptions } from 'mahoji/dist/lib/types'; +import type { CommandOptions } from 'mahoji/dist/lib/types'; import { modifyBusyCounter } from '../../lib/busyCounterCache'; import { busyImmuneCommands, shouldTrackCommand } from '../../lib/constants'; import { prisma } from '../../lib/settings/prisma'; import { makeCommandUsage } from '../../lib/util/commandUsage'; import { logError } from '../../lib/util/logError'; -import { AbstractCommand } from './inhibitors'; +import type { AbstractCommand } from './inhibitors'; export async function postCommand({ abstractCommand, diff --git a/src/mahoji/lib/preCommand.ts b/src/mahoji/lib/preCommand.ts index 766c44151c0..22cfa45a20b 100644 --- a/src/mahoji/lib/preCommand.ts +++ b/src/mahoji/lib/preCommand.ts @@ -1,12 +1,13 @@ -import { InteractionReplyOptions, TextChannel, User } from 'discord.js'; -import { CommandOptions } from 'mahoji/dist/lib/types'; +import type { InteractionReplyOptions, TextChannel, User } from 'discord.js'; +import type { CommandOptions } from 'mahoji/dist/lib/types'; import { modifyBusyCounter, userIsBusy } from '../../lib/busyCounterCache'; -import { badges, badgesCache, busyImmuneCommands, Emoji, usernameCache } from '../../lib/constants'; +import { Emoji, badges, badgesCache, busyImmuneCommands, usernameCache } from '../../lib/constants'; import { prisma } from '../../lib/settings/prisma'; import { removeMarkdownEmojis, stripEmojis } from '../../lib/util'; import { CACHED_ACTIVE_USER_IDS } from '../../lib/util/cachedUserIDs'; -import { AbstractCommand, runInhibitors } from './inhibitors'; +import type { AbstractCommand } from './inhibitors'; +import { runInhibitors } from './inhibitors'; function cleanUsername(username: string) { return removeMarkdownEmojis(username).substring(0, 32); @@ -30,7 +31,7 @@ export async function syncNewUserUsername(user: MUser, username: string) { } }); } - let name = stripEmojis(username); + const name = stripEmojis(username); usernameCache.set(user.id, name); const rawBadges = user.user.badges.map(num => badges[num]); if (user.isIronman) { diff --git a/src/mahoji/lib/util.ts b/src/mahoji/lib/util.ts index 9b2cd3bbfba..1a6fac694bb 100644 --- a/src/mahoji/lib/util.ts +++ b/src/mahoji/lib/util.ts @@ -1,7 +1,7 @@ -import { Prisma } from '@prisma/client'; +import type { Prisma } from '@prisma/client'; import { isObject } from 'e'; -import { ICommand, MahojiClient } from 'mahoji'; -import { CommandOptions, MahojiUserOption } from 'mahoji/dist/lib/types'; +import type { ICommand, MahojiClient } from 'mahoji'; +import type { CommandOptions, MahojiUserOption } from 'mahoji/dist/lib/types'; import type { AbstractCommand, AbstractCommandAttributes } from './inhibitors'; @@ -24,7 +24,7 @@ interface CompressedArg { [key: string]: string | number | boolean | null | undefined | CompressedArg; } function compressMahojiArgs(options: CommandOptions) { - let newOptions: CompressedArg = {}; + const newOptions: CompressedArg = {}; for (const [key, val] of Object.entries(options) as [ keyof CommandOptions, CommandOptions[keyof CommandOptions] diff --git a/src/mahoji/mahojiSettings.ts b/src/mahoji/mahojiSettings.ts index 8b8892da5f3..c9516339243 100644 --- a/src/mahoji/mahojiSettings.ts +++ b/src/mahoji/mahojiSettings.ts @@ -3,11 +3,11 @@ import type { Prisma, User, UserStats } from '@prisma/client'; import { isFunction, objectEntries, round } from 'e'; import { Bank } from 'oldschooljs'; +import type { SelectedUserStats } from '../lib/MUser'; import { globalConfig } from '../lib/constants'; import type { KillableMonster } from '../lib/minions/types'; -import type { SelectedUserStats } from '../lib/MUser'; import { prisma } from '../lib/settings/prisma'; -import { Rune } from '../lib/skilling/skills/runecraft'; +import type { Rune } from '../lib/skilling/skills/runecraft'; import { hasGracefulEquipped } from '../lib/structures/Gear'; import type { ItemBank } from '../lib/types'; import { anglerBoosts, formatItemReqs, hasSkillReqs, itemNameFromID, readableStatName } from '../lib/util'; @@ -129,9 +129,9 @@ export async function multipleUserStatsBankUpdate(userID: string, updates: Parti await userStatsUpdate( userID, u => { - let updateObj: Prisma.UserStatsUpdateInput = {}; + const updateObj: Prisma.UserStatsUpdateInput = {}; for (const [key, bank] of objectEntries(updates)) { - updateObj[key] = bank!.clone().add(u[key] as ItemBank).bank; + updateObj[key] = bank?.clone().add(u[key] as ItemBank).bank; } return updateObj; }, @@ -258,7 +258,7 @@ export function hasMonsterRequirements(user: MUser, monster: KillableMonster) { `You don't have the requirements to kill ${monster.name}! Your ${readableStatName( unmetKey! )} stat in your ${setup} setup is ${has}, but you need atleast ${ - monster.minimumGearRequirements[setup]![unmetKey!] + monster.minimumGearRequirements[setup]?.[unmetKey!] }.` ]; } @@ -269,7 +269,7 @@ export function hasMonsterRequirements(user: MUser, monster: KillableMonster) { return [true]; } -export function resolveAvailableItemBoosts(user: MUser, monster: KillableMonster, isInWilderness: boolean = false) { +export function resolveAvailableItemBoosts(user: MUser, monster: KillableMonster, isInWilderness = false) { const boosts = new Bank(); if (monster.itemInBankBoosts) { for (const boostSet of monster.itemInBankBoosts) { @@ -278,7 +278,7 @@ export function resolveAvailableItemBoosts(user: MUser, monster: KillableMonster // find the highest boost that the player has for (const [itemID, boostAmount] of Object.entries(boostSet)) { - const parsedId = parseInt(itemID); + const parsedId = Number.parseInt(itemID); if (isInWilderness ? !user.hasEquipped(parsedId) : !user.hasEquippedOrInBank(parsedId)) { continue; } diff --git a/src/scripts/build.ts b/src/scripts/build.ts new file mode 100644 index 00000000000..9930bcd6e2a --- /dev/null +++ b/src/scripts/build.ts @@ -0,0 +1,147 @@ +import { exec as execNonPromise } from 'node:child_process'; +import { createHash } from 'node:crypto'; +import { existsSync, readFileSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { promisify } from 'node:util'; +import { Stopwatch } from '@sapphire/stopwatch'; +import fg from 'fast-glob'; +import { BOT_TYPE } from '../lib/constants'; + +const args = process.argv.slice(2); + +const hasArg = (arg: string) => args.includes(arg); + +const forceRebuild = hasArg('--clean'); + +async function runTimedLoggedFn(name: string, fn: () => Promise) { + const stopwatch = new Stopwatch(); + stopwatch.start(); + await fn(); + stopwatch.stop(); + console.log(`Finished ${name} in ${stopwatch.toString()}`); +} + +const rawExecAsync = promisify(execNonPromise); + +async function execAsync(command: string) { + try { + console.log(' Running command:', command); + const result = await rawExecAsync(command); + if (result.stderr) { + console.error(result.stderr); + } + } catch (err) { + console.error(err); + } +} + +if (!existsSync('./cache.json')) { + writeFileSync('./cache.json', JSON.stringify({}, null, 2)); +} +const currentCache = JSON.parse(readFileSync('./cache.json', 'utf-8')); + +function doHash(string: string | Buffer) { + return createHash('sha256').update(string).digest('hex'); +} + +function getFileHash(filePath: string): string { + return doHash(readFileSync(filePath)); +} + +function getCacheHash(cachePath: string, key: string): string | null { + if (!existsSync(cachePath)) return null; + const cache = JSON.parse(readFileSync(cachePath, 'utf-8')); + return cache[key] || null; +} + +function setCacheValue(key: string, value: string | number) { + const cache = JSON.parse(readFileSync(cacheFilePath, 'utf-8')); + cache[key] = value; + writeFileSync(cacheFilePath, JSON.stringify(cache, null, 2)); +} + +function shouldGeneratePrismaClient( + schemaPath: string, + cachePath: string, + cacheKey: string, + clientPath: string +): boolean { + if (!existsSync(clientPath)) return true; + const currentHash = getFileHash(schemaPath); + const cachedHash = getCacheHash(cachePath, cacheKey); + if (currentHash !== cachedHash) { + setCacheValue(cacheKey, currentHash); + return true; + } + return false; +} + +const cacheFilePath = './cache.json'; + +async function handlePrismaClientGeneration() { + const prismaSchemaPaths = [ + { schema: 'prisma/robochimp.prisma', client: 'node_modules/@prisma/client', key: 'robochimpPrismaSchemaHash' }, + { schema: 'prisma/schema.prisma', client: 'node_modules/@prisma/robochimp', key: `${BOT_TYPE}SchemaHash` } + ]; + + let shouldRunGen = false; + for (const { schema, client, key } of prismaSchemaPaths) { + if (shouldGeneratePrismaClient(schema, cacheFilePath, key, client)) { + shouldRunGen = true; + break; + } + } + + if (shouldRunGen || forceRebuild) { + await execAsync('yarn gen'); + } +} + +async function checkForWipingDistFolder() { + const allTypescriptFiles = await fg('**/**/*.ts', { cwd: path.join('src'), onlyFiles: true }); + allTypescriptFiles.sort(); + const hash = doHash(allTypescriptFiles.join('\n')); + if (currentCache.typescriptFilesHash !== hash || forceRebuild) { + console.log(' Removing dist folder'); + await execAsync('yarn wipedist'); + setCacheValue('typescriptFilesHash', hash); + } +} + +async function handleTypescriptCompilation() { + await execAsync('yarn build:tsc'); +} + +async function handleCreatables() { + const allCreatablesFiles = await fg(['./src/lib/data/creatables/*.ts', './src/lib/data/creatables.ts'], { + cwd: path.join('src'), + onlyFiles: true + }); + const hash = doHash(allCreatablesFiles.join('\n')); + if (currentCache.creatablesHash !== hash || forceRebuild) { + console.log(' Rebuilding creatables.txt file'); + const { renderCreatablesFile } = await import('./renderCreatablesFile'); + renderCreatablesFile(); + setCacheValue('creatablesHash', hash); + } +} + +async function handleCommandsJSON() { + const currentFileHash = getFileHash(`./src/lib/data/${BOT_TYPE}.commands.json`); + if (currentCache.commandsHash !== currentFileHash || forceRebuild) { + console.log(' Updating commands json file'); + const { commandsFile } = await import('./renderCommandsFile'); + await commandsFile(); + setCacheValue('commandsHash', currentFileHash); + } +} + +async function main() { + await runTimedLoggedFn('Prisma Client / Wipe Dist', () => + Promise.all([handlePrismaClientGeneration(), checkForWipingDistFolder()]) + ); + await runTimedLoggedFn('Typescript Compilation', handleTypescriptCompilation); + await runTimedLoggedFn('Post Build', () => Promise.all([handleCreatables(), handleCommandsJSON()])); +} + +runTimedLoggedFn('Build', main); diff --git a/src/scripts/integration-tests.ts b/src/scripts/integration-tests.ts index 93b0fb6545e..2ffbc33cc58 100644 --- a/src/scripts/integration-tests.ts +++ b/src/scripts/integration-tests.ts @@ -1,4 +1,4 @@ -import { execSync } from 'child_process'; +import { execSync } from 'node:child_process'; import { sleep } from 'e'; async function main() { @@ -13,14 +13,13 @@ async function main() { execSync('dotenv -e .env.test -- prisma db push --schema="./prisma/robochimp.prisma"', { stdio: 'inherit' }); console.log('Building...'); - execSync('yarn pre:build', { stdio: 'inherit' }); execSync('yarn build:esbuild', { stdio: 'inherit' }); console.log('Starting tests...'); - let runs = 1; + const runs = 1; for (let i = 0; i < runs; i++) { console.log(`Starting run ${i + 1}/${runs}`); - execSync('vitest run --config vitest.integration.config.mts sacrifice', { + execSync('vitest run --config vitest.integration.config.mts', { stdio: 'inherit', encoding: 'utf-8' }); diff --git a/src/scripts/remove-item.ts b/src/scripts/remove-item.ts index 9901f6b735c..96c4cdf380f 100644 --- a/src/scripts/remove-item.ts +++ b/src/scripts/remove-item.ts @@ -1,7 +1,7 @@ import '../lib/data/itemAliases'; import '../lib/itemMods'; -import { writeFileSync } from 'fs'; +import { writeFileSync } from 'node:fs'; import { EquipmentSlot } from 'oldschooljs/dist/meta/types'; import { GearSetupTypes } from '../lib/gear/types'; @@ -29,14 +29,12 @@ FINAL_QUERY += `\n${extraFunctions}\n\n`; FINAL_QUERY += '\nBEGIN;\n'; -FINAL_QUERY += GearSetupTypes.map(gearType => +FINAL_QUERY += GearSetupTypes.flatMap(gearType => Object.values(EquipmentSlot).map( slot => `UPDATE users SET "gear.${gearType}" = jsonb_set("gear.${gearType}"::jsonb, ARRAY['${slot}'], 'null'::jsonb) WHERE "gear.${gearType}"->'${slot}'->>'item' = ANY(${arrayToRemove});` ) -) - .flat() - .join('\n'); +).join('\n'); const removeFromBankQuery = (column: string) => `"${column}" = "${column}"::jsonb - ${arrayToRemove}`; const removeFromArrayQuery = (column: string) => diff --git a/src/scripts/postbuild.ts b/src/scripts/renderCommandsFile.ts similarity index 87% rename from src/scripts/postbuild.ts rename to src/scripts/renderCommandsFile.ts index b1221ebdf6c..25671fa390d 100644 --- a/src/scripts/postbuild.ts +++ b/src/scripts/renderCommandsFile.ts @@ -3,12 +3,13 @@ import './no-prisma'; import { execSync } from 'node:child_process'; import { join } from 'node:path'; +import { writeFileSync } from 'node:fs'; import { stringMatches } from '@oldschoolgg/toolkit'; import { ApplicationCommandOptionType } from 'discord.js'; -import { writeFileSync } from 'fs'; import { MahojiClient } from 'mahoji'; import { BOT_TYPE } from '../lib/constants'; + import type { AbstractCommand } from '../mahoji/lib/inhibitors'; import { allAbstractCommands } from '../mahoji/lib/util'; @@ -55,11 +56,9 @@ async function renderCommands() { .sort((a, b) => a.name.localeCompare(b.name)); } -export async function main() { - console.log('Postbuild'); +export async function commandsFile() { const commands = await renderCommands(); - writeFileSync(`./src/lib/data/${BOT_TYPE.toLowerCase()}.commands.json`, JSON.stringify(commands, null, 4)); - execSync('yarn prettify'); + const path = `./src/lib/data/${BOT_TYPE.toLowerCase()}.commands.json`; + writeFileSync(path, JSON.stringify(commands, null, 4)); + execSync('yarn lint'); } - -main().then(() => null); diff --git a/src/scripts/render-creatables-file.ts b/src/scripts/renderCreatablesFile.ts similarity index 73% rename from src/scripts/render-creatables-file.ts rename to src/scripts/renderCreatablesFile.ts index 9253fd38337..b29a8f90dff 100644 --- a/src/scripts/render-creatables-file.ts +++ b/src/scripts/renderCreatablesFile.ts @@ -1,14 +1,9 @@ -import './no-prisma'; -import '../lib/data/itemAliases'; - -import { writeFileSync } from 'fs'; +import { writeFileSync } from 'node:fs'; import { Bank } from 'oldschooljs'; - import Createables from '../lib/data/createables'; import { makeTable } from '../lib/util/smallUtils'; -export function main() { - console.log('Prebuild'); +export function renderCreatablesFile() { const headers = ['Item name', 'Input Items', 'Output Items', 'GP Cost']; const rows = Createables.map(i => { return [i.name, `${new Bank(i.inputItems)}`, `${new Bank(i.outputItems)}`, `${i.GPCost ?? 0}`]; @@ -16,5 +11,3 @@ export function main() { writeFileSync('./src/lib/data/creatablesTable.txt', makeTable(headers, rows)); } - -main(); diff --git a/src/tasks/minions/HunterActivity/aerialFishingActivity.ts b/src/tasks/minions/HunterActivity/aerialFishingActivity.ts index b6d324dc88a..2f1866a8afc 100644 --- a/src/tasks/minions/HunterActivity/aerialFishingActivity.ts +++ b/src/tasks/minions/HunterActivity/aerialFishingActivity.ts @@ -6,7 +6,7 @@ import addSkillingClueToLoot from '../../../lib/minions/functions/addSkillingClu import Fishing from '../../../lib/skilling/skills/fishing'; import aerialFishingCreatures from '../../../lib/skilling/skills/hunter/aerialFishing'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; import { roll, skillingPetDropRate } from '../../../lib/util'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { anglerBoostPercent } from '../../../mahoji/mahojiSettings'; @@ -14,7 +14,7 @@ import { anglerBoostPercent } from '../../../mahoji/mahojiSettings'; export const aerialFishingTask: MinionTask = { type: 'AerialFishing', async run(data: ActivityTaskOptionsWithQuantity) { - let { quantity, userID, channelID } = data; + const { quantity, userID, channelID } = data; const user = await mUserFetch(userID); const currentHuntLevel = user.skillLevel(SkillsEnum.Hunter); const currentFishLevel = user.skillLevel(SkillsEnum.Fishing); @@ -39,7 +39,7 @@ export const aerialFishingTask: MinionTask = { if (roll(100 - ((maxRoll - 40) * 25) / 59)) { molchPearls++; } - let currentRoll = randInt(0, maxRoll); + const currentRoll = randInt(0, maxRoll); loot.add(bluegill.table.roll()); if ( @@ -83,7 +83,7 @@ export const aerialFishingTask: MinionTask = { // If they have the entire angler outfit, give an extra 2.5% xp bonus if ( user.gear.skilling.hasEquipped( - Object.keys(Fishing.anglerItems).map(i => parseInt(i)), + Object.keys(Fishing.anglerItems).map(i => Number.parseInt(i)), true ) ) { @@ -93,7 +93,7 @@ export const aerialFishingTask: MinionTask = { } else { // For each angler item, check if they have it, give its' XP boost if so. for (const [itemID, bonus] of Object.entries(Fishing.anglerItems)) { - if (user.hasEquipped(parseInt(itemID))) { + if (user.hasEquipped(Number.parseInt(itemID))) { const amountToAdd = Math.floor(fishXpReceived * (bonus / 100)); fishXpReceived += amountToAdd; bonusXP += amountToAdd; diff --git a/src/tasks/minions/HunterActivity/birdhouseActivity.ts b/src/tasks/minions/HunterActivity/birdhouseActivity.ts index 35cf3da9341..ffcd4c68a74 100644 --- a/src/tasks/minions/HunterActivity/birdhouseActivity.ts +++ b/src/tasks/minions/HunterActivity/birdhouseActivity.ts @@ -1,11 +1,11 @@ -import { Prisma } from '@prisma/client'; +import type { Prisma } from '@prisma/client'; import { randFloat, roll } from 'e'; import { Bank } from 'oldschooljs'; import birdhouses from '../../../lib/skilling/skills/hunter/birdHouseTrapping'; -import { BirdhouseData } from '../../../lib/skilling/skills/hunter/defaultBirdHouseTrap'; +import type { BirdhouseData } from '../../../lib/skilling/skills/hunter/defaultBirdHouseTrap'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { BirdhouseActivityTaskOptions } from '../../../lib/types/minions'; +import type { BirdhouseActivityTaskOptions } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import itemID from '../../../lib/util/itemID'; import { sendToChannelID } from '../../../lib/util/webhook'; @@ -25,7 +25,7 @@ export const birdHouseTask: MinionTask = { const user = await mUserFetch(userID); let hunterXP = 0; let craftingXP = 0; - let strungRabbitFoot = user.hasEquipped('Strung rabbit foot'); + const strungRabbitFoot = user.hasEquipped('Strung rabbit foot'); const loot = new Bank(); const birdhouse = birdhouses.find(_birdhouse => _birdhouse.name === birdhouseName); diff --git a/src/tasks/minions/HunterActivity/driftNetActivity.ts b/src/tasks/minions/HunterActivity/driftNetActivity.ts index 9c20dca1647..dde3a8c0a79 100644 --- a/src/tasks/minions/HunterActivity/driftNetActivity.ts +++ b/src/tasks/minions/HunterActivity/driftNetActivity.ts @@ -2,7 +2,7 @@ import { Bank } from 'oldschooljs'; import driftNetCreatures from '../../../lib/skilling/skills/hunter/driftNet'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; // Bonus loot from higher fishing level @@ -32,7 +32,7 @@ const fishBonusLoot = [ export const driftNetTask: MinionTask = { type: 'DriftNet', async run(data: ActivityTaskOptionsWithQuantity) { - let { quantity, userID, channelID, duration } = data; + const { quantity, userID, channelID, duration } = data; const user = await mUserFetch(userID); const currentHuntLevel = user.skillLevel(SkillsEnum.Hunter); const currentFishLevel = user.skillLevel(SkillsEnum.Fishing); @@ -40,7 +40,7 @@ export const driftNetTask: MinionTask = { // Current fishable creatures const fishShoal = driftNetCreatures.find(_fish => _fish.name === 'Fish shoal')!; - let fishShoalCaught = quantity; + const fishShoalCaught = quantity; // Build up loot table based on fishing level const fishTable = fishShoal.table.clone(); diff --git a/src/tasks/minions/HunterActivity/hunterActivity.ts b/src/tasks/minions/HunterActivity/hunterActivity.ts index 12351538e07..914d29dfdd8 100644 --- a/src/tasks/minions/HunterActivity/hunterActivity.ts +++ b/src/tasks/minions/HunterActivity/hunterActivity.ts @@ -1,5 +1,5 @@ -import { Prisma } from '@prisma/client'; -import { randInt, Time } from 'e'; +import type { Prisma } from '@prisma/client'; +import { Time, randInt } from 'e'; import { Bank } from 'oldschooljs'; import { EquipmentSlot } from 'oldschooljs/dist/meta/types'; @@ -9,7 +9,7 @@ import { trackLoot } from '../../../lib/lootTrack'; import { calcLootXPHunting, generateHerbiTable } from '../../../lib/skilling/functions/calcsHunter'; import Hunter from '../../../lib/skilling/skills/hunter/hunter'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { HunterActivityTaskOptions } from '../../../lib/types/minions'; +import type { HunterActivityTaskOptions } from '../../../lib/types/minions'; import { roll, skillingPetDropRate, stringMatches } from '../../../lib/util'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import itemID from '../../../lib/util/itemID'; @@ -55,7 +55,7 @@ export const hunterTask: MinionTask = { if (!creature) return; - let crystalImpling = creature.name === 'Crystal impling'; + const crystalImpling = creature.name === 'Crystal impling'; let graceful = false; if (userHasGracefulEquipped(user)) { @@ -114,8 +114,8 @@ export const hunterTask: MinionTask = { } if (gotPked && !died) { if (userBank.amount('Saradomin brew(4)') >= 10 && userBank.amount('Super restore(4)') >= 5) { - let lostBrew = randInt(1, 10); - let lostRestore = randInt(1, 5); + const lostBrew = randInt(1, 10); + const lostRestore = randInt(1, 5); const cost = new Bank().add('Saradomin brew(4)', lostBrew).add('Super restore(4)', lostRestore); await transactItems({ userID: user.id, itemsToRemove: cost }); @@ -181,7 +181,7 @@ export const hunterTask: MinionTask = { ? '.' : `${quantity}x times, due to clever creatures you missed out on ${ quantity - successfulQuantity - }x catches. ` + }x catches. ` }${xpStr}\n\nYou received: ${loot}.${magicSecStr.length > 1 ? magicSecStr : ''}`; if (gotPked && !died) { diff --git a/src/tasks/minions/PrayerActivity/offeringActivity.ts b/src/tasks/minions/PrayerActivity/offeringActivity.ts index a091a9ee3d2..9f9ec163f03 100644 --- a/src/tasks/minions/PrayerActivity/offeringActivity.ts +++ b/src/tasks/minions/PrayerActivity/offeringActivity.ts @@ -3,7 +3,7 @@ import { percentChance, randInt } from 'e'; import { zealOutfit } from '../../../lib/shadesKeys'; import Prayer from '../../../lib/skilling/skills/prayer'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { OfferingActivityTaskOptions } from '../../../lib/types/minions'; +import type { OfferingActivityTaskOptions } from '../../../lib/types/minions'; import { roll } from '../../../lib/util'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; diff --git a/src/tasks/minions/PrayerActivity/scatteringActivity.ts b/src/tasks/minions/PrayerActivity/scatteringActivity.ts index 2483a028156..3a3ab92b9d9 100644 --- a/src/tasks/minions/PrayerActivity/scatteringActivity.ts +++ b/src/tasks/minions/PrayerActivity/scatteringActivity.ts @@ -2,7 +2,7 @@ import { Bank } from 'oldschooljs'; import Prayer from '../../../lib/skilling/skills/prayer'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { ScatteringActivityTaskOptions } from '../../../lib/types/minions'; +import type { ScatteringActivityTaskOptions } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { userStatsBankUpdate } from '../../../mahoji/mahojiSettings'; @@ -22,7 +22,7 @@ export const scatteringTask: MinionTask = { duration }); - let str = `${user}, ${user.minionName} finished scattering ${quantity}x ${ash.name}. ${xpRes}`; + const str = `${user}, ${user.minionName} finished scattering ${quantity}x ${ash.name}. ${xpRes}`; await userStatsBankUpdate(user.id, 'scattered_ashes_bank', new Bank().add(ash.inputId, quantity)); diff --git a/src/tasks/minions/agilityActivity.ts b/src/tasks/minions/agilityActivity.ts index 44504d3a7f2..fbf394197d5 100644 --- a/src/tasks/minions/agilityActivity.ts +++ b/src/tasks/minions/agilityActivity.ts @@ -1,12 +1,12 @@ -import { increaseNumByPercent, randInt, roll, Time } from 'e'; +import { Time, increaseNumByPercent, randInt, roll } from 'e'; import { Bank } from 'oldschooljs'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; import { Emoji, Events } from '../../lib/constants'; import { ArdougneDiary, userhasDiaryTier } from '../../lib/diaries'; import Agility from '../../lib/skilling/skills/agility'; import { SkillsEnum } from '../../lib/skilling/types'; -import { AgilityActivityTaskOptions } from '../../lib/types/minions'; +import type { AgilityActivityTaskOptions } from '../../lib/types/minions'; import { addItemToBank, skillingPetDropRate } from '../../lib/util'; import getOSItem from '../../lib/util/getOSItem'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; @@ -24,7 +24,7 @@ function chanceOfFailingAgilityPyramid(user: MUser) { export const agilityTask: MinionTask = { type: 'Agility', async run(data: AgilityActivityTaskOptions) { - let { courseID, quantity, userID, channelID, duration, alch } = data; + const { courseID, quantity, userID, channelID, duration, alch } = data; const user = await mUserFetch(userID); const currentLevel = user.skillLevel(SkillsEnum.Agility); diff --git a/src/tasks/minions/alchingActivity.ts b/src/tasks/minions/alchingActivity.ts index b324277df22..6018af21756 100644 --- a/src/tasks/minions/alchingActivity.ts +++ b/src/tasks/minions/alchingActivity.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import { SkillsEnum } from '../../lib/skilling/types'; -import { AlchingActivityTaskOptions } from '../../lib/types/minions'; +import type { AlchingActivityTaskOptions } from '../../lib/types/minions'; import { roll } from '../../lib/util'; import getOSItem from '../../lib/util/getOSItem'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; @@ -13,7 +13,7 @@ const bryophytasStaffId = itemID("Bryophyta's staff"); export const alchingTask: MinionTask = { type: 'Alching', async run(data: AlchingActivityTaskOptions) { - let { itemID, quantity, channelID, alchValue, userID, duration } = data; + const { itemID, quantity, channelID, alchValue, userID, duration } = data; const user = await mUserFetch(userID); const loot = new Bank({ Coins: alchValue }); @@ -46,7 +46,7 @@ export const alchingTask: MinionTask = { }); const saved = savedRunes > 0 ? `Your Bryophyta's staff saved you ${savedRunes} Nature runes.` : ''; - let responses = [ + const responses = [ `${user}, ${user.minionName} has finished alching ${quantity}x ${item.name}! ${loot} has been added to your bank. ${xpRes}. ${saved}` ].join('\n'); diff --git a/src/tasks/minions/butlerActivity.ts b/src/tasks/minions/butlerActivity.ts index 6072d9cbc0f..cfcd2eed75f 100644 --- a/src/tasks/minions/butlerActivity.ts +++ b/src/tasks/minions/butlerActivity.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { ButlerActivityTaskOptions } from '../../lib/types/minions'; +import type { ButlerActivityTaskOptions } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; export const butlerTask: MinionTask = { @@ -13,7 +13,7 @@ export const butlerTask: MinionTask = { [plankID]: plankQuantity }); - let str = `${user}, ${user.minionName} finished creating planks, you received ${loot}.`; + const str = `${user}, ${user.minionName} finished creating planks, you received ${loot}.`; await user.addItemsToBank({ items: loot, collectionLog: true }); diff --git a/src/tasks/minions/camdozaalActivity/camdozaalFishingActivity.ts b/src/tasks/minions/camdozaalActivity/camdozaalFishingActivity.ts index 3109513bf23..1bd83f3a10f 100644 --- a/src/tasks/minions/camdozaalActivity/camdozaalFishingActivity.ts +++ b/src/tasks/minions/camdozaalActivity/camdozaalFishingActivity.ts @@ -4,7 +4,7 @@ import { Emoji, Events } from '../../../lib/constants'; import addSkillingClueToLoot from '../../../lib/minions/functions/addSkillingClueToLoot'; import Fishing from '../../../lib/skilling/skills/fishing'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; import { roll, skillingPetDropRate } from '../../../lib/util'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { makeBankImage } from '../../../lib/util/makeBankImage'; @@ -12,7 +12,7 @@ import { makeBankImage } from '../../../lib/util/makeBankImage'; export const camdozaalFishingTask: MinionTask = { type: 'CamdozaalFishing', async run(data: ActivityTaskOptionsWithQuantity) { - let { userID, channelID, quantity, duration } = data; + const { userID, channelID, quantity, duration } = data; const user = await mUserFetch(userID); const currentFishLevel = user.skillLevel(SkillsEnum.Fishing); @@ -75,7 +75,7 @@ export const camdozaalFishingTask: MinionTask = { // If user has the entire angler outfit, give an extra 2.5% xp bonus if ( user.gear.skilling.hasEquipped( - Object.keys(Fishing.anglerItems).map(i => parseInt(i)), + Object.keys(Fishing.anglerItems).map(i => Number.parseInt(i)), true ) ) { @@ -85,7 +85,7 @@ export const camdozaalFishingTask: MinionTask = { } else { // For each angler item, check if they have it, give its' XP boost for (const [itemID, bonus] of Object.entries(Fishing.anglerItems)) { - if (user.hasEquipped(parseInt(itemID))) { + if (user.hasEquipped(Number.parseInt(itemID))) { const amountToAdd = Math.floor(fishingXpReceived * (bonus / 100)); fishingXpReceived += amountToAdd; bonusXP += amountToAdd; @@ -108,7 +108,7 @@ export const camdozaalFishingTask: MinionTask = { } // Add clue scrolls - let clueScrollChance = guppy.clueScrollChance!; + const clueScrollChance = guppy.clueScrollChance!; addSkillingClueToLoot(user, SkillsEnum.Fishing, quantity, clueScrollChance, loot); // Heron Pet roll diff --git a/src/tasks/minions/camdozaalActivity/camdozaalMiningActivity.ts b/src/tasks/minions/camdozaalActivity/camdozaalMiningActivity.ts index 8645269db37..4eab9634d7c 100644 --- a/src/tasks/minions/camdozaalActivity/camdozaalMiningActivity.ts +++ b/src/tasks/minions/camdozaalActivity/camdozaalMiningActivity.ts @@ -4,7 +4,7 @@ import { Emoji, Events } from '../../../lib/constants'; import addSkillingClueToLoot from '../../../lib/minions/functions/addSkillingClueToLoot'; import Mining from '../../../lib/skilling/skills/mining'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; import { roll, skillingPetDropRate } from '../../../lib/util'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { makeBankImage } from '../../../lib/util/makeBankImage'; @@ -12,7 +12,7 @@ import { makeBankImage } from '../../../lib/util/makeBankImage'; export const camdozaalMiningTask: MinionTask = { type: 'CamdozaalMining', async run(data: ActivityTaskOptionsWithQuantity) { - let { quantity, userID, channelID, duration } = data; + const { quantity, userID, channelID, duration } = data; const user = await mUserFetch(userID); const camdozaalMine = Mining.CamdozaalMine; const currentLevel = user.skillLevel(SkillsEnum.Mining); @@ -62,7 +62,7 @@ export const camdozaalMiningTask: MinionTask = { // If user has the entire prospector outfit, give an extra 2.5% xp bonus if ( user.gear.skilling.hasEquipped( - Object.keys(Mining.prospectorItems).map(i => parseInt(i)), + Object.keys(Mining.prospectorItems).map(i => Number.parseInt(i)), true ) ) { @@ -72,7 +72,7 @@ export const camdozaalMiningTask: MinionTask = { } else { // For each prospector item, check if they have it, give its' XP boost for (const [itemID, bonus] of Object.entries(Mining.prospectorItems)) { - if (user.hasEquipped(parseInt(itemID))) { + if (user.hasEquipped(Number.parseInt(itemID))) { const amountToAdd = Math.floor(miningXpReceived * (bonus / 100)); miningXpReceived += amountToAdd; bonusXP += amountToAdd; @@ -95,7 +95,7 @@ export const camdozaalMiningTask: MinionTask = { } // Add clue scrolls - let clueScrollChance = Mining.CamdozaalMine.clueScrollChance!; + const clueScrollChance = Mining.CamdozaalMine.clueScrollChance!; addSkillingClueToLoot(user, SkillsEnum.Fishing, quantity, clueScrollChance, loot); // Rock golem roll diff --git a/src/tasks/minions/camdozaalActivity/camdozaalSmithingActivity.ts b/src/tasks/minions/camdozaalActivity/camdozaalSmithingActivity.ts index 0916cd488a1..9b8d53faf24 100644 --- a/src/tasks/minions/camdozaalActivity/camdozaalSmithingActivity.ts +++ b/src/tasks/minions/camdozaalActivity/camdozaalSmithingActivity.ts @@ -1,7 +1,7 @@ import { Bank, LootTable } from 'oldschooljs'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { makeBankImage } from '../../../lib/util/makeBankImage'; @@ -20,7 +20,7 @@ const barroniteDepositLootTable = new LootTable() export const camdozaalSmithingTask: MinionTask = { type: 'CamdozaalSmithing', async run(data: ActivityTaskOptionsWithQuantity) { - let { quantity, userID, channelID, duration } = data; + const { quantity, userID, channelID, duration } = data; const user = await mUserFetch(userID); // Count loot received during trip @@ -30,7 +30,7 @@ export const camdozaalSmithingTask: MinionTask = { } // Add up the xp from the trip - let smithingXpReceived = quantity * 30; + const smithingXpReceived = quantity * 30; // Add xp to user const xpRes = await user.addXP({ @@ -41,7 +41,7 @@ export const camdozaalSmithingTask: MinionTask = { }); // Trip finish message - let str = `${user}, ${user.minionName} finished smithing in Camdozaal! ${xpRes}`; + const str = `${user}, ${user.minionName} finished smithing in Camdozaal! ${xpRes}`; // Give the user the items from the trip const { previousCL, itemsAdded } = await transactItems({ diff --git a/src/tasks/minions/castingActivity.ts b/src/tasks/minions/castingActivity.ts index 7cee3071d75..49a0eba7477 100644 --- a/src/tasks/minions/castingActivity.ts +++ b/src/tasks/minions/castingActivity.ts @@ -1,12 +1,12 @@ import { Castables } from '../../lib/skilling/skills/magic/castables'; import { SkillsEnum } from '../../lib/skilling/types'; -import { CastingActivityTaskOptions } from '../../lib/types/minions'; +import type { CastingActivityTaskOptions } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; export const castingTask: MinionTask = { type: 'Casting', async run(data: CastingActivityTaskOptions) { - let { spellID, quantity, userID, channelID, duration } = data; + const { spellID, quantity, userID, channelID, duration } = data; const user = await mUserFetch(userID); const spell = Castables.find(i => i.id === spellID)!; @@ -51,7 +51,7 @@ export const castingTask: MinionTask = { }); } - let str = `${user}, ${user.minionName} finished casting ${quantity}x ${spell.name}, you received ${ + const str = `${user}, ${user.minionName} finished casting ${quantity}x ${spell.name}, you received ${ loot ?? 'no items' }. ${xpRes} ${craftXpRes}${prayerXpRes}`; diff --git a/src/tasks/minions/clueActivity.ts b/src/tasks/minions/clueActivity.ts index 05a3552721d..1a24d9f269b 100644 --- a/src/tasks/minions/clueActivity.ts +++ b/src/tasks/minions/clueActivity.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import { ClueTiers } from '../../lib/clues/clueTiers'; -import { ClueActivityTaskOptions } from '../../lib/types/minions'; +import type { ClueActivityTaskOptions } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; export const clueTask: MinionTask = { diff --git a/src/tasks/minions/collectingActivity.ts b/src/tasks/minions/collectingActivity.ts index 1678bbf8ed1..b4b7d658650 100644 --- a/src/tasks/minions/collectingActivity.ts +++ b/src/tasks/minions/collectingActivity.ts @@ -2,7 +2,7 @@ import { Time } from 'e'; import { Bank } from 'oldschooljs'; import { MorytaniaDiary, userhasDiaryTier } from '../../lib/diaries'; -import { CollectingOptions } from '../../lib/types/minions'; +import type { CollectingOptions } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; import { collectables } from '../../mahoji/lib/abstracted_commands/collectCommand'; @@ -10,7 +10,7 @@ import { collectables } from '../../mahoji/lib/abstracted_commands/collectComman export const collectingTask: MinionTask = { type: 'Collecting', async run(data: CollectingOptions) { - let { collectableID, quantity, userID, channelID, duration } = data; + const { collectableID, quantity, userID, channelID, duration } = data; const user = await mUserFetch(userID); const collectable = collectables.find(c => c.item.id === collectableID)!; diff --git a/src/tasks/minions/colosseumActivity.ts b/src/tasks/minions/colosseumActivity.ts index 30fb15b9f35..32aa1aecfc5 100644 --- a/src/tasks/minions/colosseumActivity.ts +++ b/src/tasks/minions/colosseumActivity.ts @@ -1,11 +1,11 @@ import { randArrItem } from 'e'; import { Bank } from 'oldschooljs'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; import { ColosseumWaveBank, colosseumWaves } from '../../lib/colosseum'; import { trackLoot } from '../../lib/lootTrack'; import { incrementMinigameScore } from '../../lib/settings/minigames'; -import { ColoTaskOptions } from '../../lib/types/minions'; +import type { ColoTaskOptions } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; import { makeBankImage } from '../../lib/util/makeBankImage'; import resolveItems from '../../lib/util/resolveItems'; @@ -25,7 +25,8 @@ export const colosseumTask: MinionTask = { newKCs.add(i + 1); } const stats = await user.fetchStats({ colo_kc_bank: true, colo_max_glory: true }); - for (const [key, value] of Object.entries(stats.colo_kc_bank as ItemBank)) newKCs.add(parseInt(key), value); + for (const [key, value] of Object.entries(stats.colo_kc_bank as ItemBank)) + newKCs.add(Number.parseInt(key), value); await userStatsUpdate(user.id, { colo_kc_bank: newKCs._bank }); const newKCsStr = `${newKCs .entries() diff --git a/src/tasks/minions/combatRingActivity.ts b/src/tasks/minions/combatRingActivity.ts index 2e202970f82..737d82c8168 100644 --- a/src/tasks/minions/combatRingActivity.ts +++ b/src/tasks/minions/combatRingActivity.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { ActivityTaskOptionsWithNoChanges } from '../../lib/types/minions'; +import type { ActivityTaskOptionsWithNoChanges } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; export const combatRingTask: MinionTask = { diff --git a/src/tasks/minions/constructionActivity.ts b/src/tasks/minions/constructionActivity.ts index 836a22bab4b..818c88eabfc 100644 --- a/src/tasks/minions/constructionActivity.ts +++ b/src/tasks/minions/constructionActivity.ts @@ -2,7 +2,7 @@ import { calcPercentOfNum } from 'e'; import Constructables from '../../lib/skilling/skills/construction/constructables'; import { SkillsEnum } from '../../lib/skilling/types'; -import { ConstructionActivityTaskOptions } from '../../lib/types/minions'; +import type { ConstructionActivityTaskOptions } from '../../lib/types/minions'; import { calcConBonusXP } from '../../lib/util/calcConBonusXP'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; diff --git a/src/tasks/minions/cookingActivity.ts b/src/tasks/minions/cookingActivity.ts index f6c9bc4b4b5..f24535659e1 100644 --- a/src/tasks/minions/cookingActivity.ts +++ b/src/tasks/minions/cookingActivity.ts @@ -4,7 +4,7 @@ import { KourendKebosDiary, userhasDiaryTier } from '../../lib/diaries'; import calcBurntCookables from '../../lib/skilling/functions/calcBurntCookables'; import Cooking from '../../lib/skilling/skills/cooking/cooking'; import { SkillsEnum } from '../../lib/skilling/types'; -import { CookingActivityTaskOptions } from '../../lib/types/minions'; +import type { CookingActivityTaskOptions } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; export const cookingTask: MinionTask = { diff --git a/src/tasks/minions/craftingActivity.ts b/src/tasks/minions/craftingActivity.ts index 8451ab8e2d3..1515521e77f 100644 --- a/src/tasks/minions/craftingActivity.ts +++ b/src/tasks/minions/craftingActivity.ts @@ -2,7 +2,7 @@ import { Bank } from 'oldschooljs'; import { Craftables } from '../../lib/skilling/skills/crafting/craftables'; import { SkillsEnum } from '../../lib/skilling/types'; -import { CraftingActivityTaskOptions } from '../../lib/types/minions'; +import type { CraftingActivityTaskOptions } from '../../lib/types/minions'; import { randFloat } from '../../lib/util'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; @@ -19,7 +19,7 @@ export const craftingTask: MinionTask = { if (item.outputMultiple) { sets = ' sets of'; } - let quantityToGive = item.outputMultiple ? quantity * item.outputMultiple : quantity; + const quantityToGive = item.outputMultiple ? quantity * item.outputMultiple : quantity; const loot = new Bank(); let crushed = 0; @@ -37,7 +37,7 @@ export const craftingTask: MinionTask = { const xpRes = await user.addXP({ skillName: SkillsEnum.Crafting, amount: xpReceived, duration }); - let str = `${user}, ${user.minionName} finished crafting ${quantity}${sets} ${item.name}, and received ${loot}. ${xpRes}`; + const str = `${user}, ${user.minionName} finished crafting ${quantity}${sets} ${item.name}, and received ${loot}. ${xpRes}`; await transactItems({ userID: user.id, diff --git a/src/tasks/minions/cutLeapingFishActivity.ts b/src/tasks/minions/cutLeapingFishActivity.ts index 8af6667aa07..c1309df9e57 100644 --- a/src/tasks/minions/cutLeapingFishActivity.ts +++ b/src/tasks/minions/cutLeapingFishActivity.ts @@ -3,13 +3,13 @@ import { Bank } from 'oldschooljs'; import LeapingFish from '../../lib/skilling/skills/cooking/leapingFish'; import { SkillsEnum } from '../../lib/skilling/types'; -import { CutLeapingFishActivityTaskOptions } from '../../lib/types/minions'; +import type { CutLeapingFishActivityTaskOptions } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; export const cutLeapingFishTask: MinionTask = { type: 'CutLeapingFish', async run(data: CutLeapingFishActivityTaskOptions) { - let { fishID, userID, channelID, quantity, duration } = data; + const { fishID, userID, channelID, quantity, duration } = data; const user = await mUserFetch(userID); const barbarianFish = LeapingFish.find(LeapingFish => LeapingFish.item.id === fishID)!; @@ -59,7 +59,7 @@ export const cutLeapingFishTask: MinionTask = { } } - let loot = new Bank(); + const loot = new Bank(); loot.add('Roe', roeCreated); loot.add('Caviar', caviarCreated); @@ -75,7 +75,7 @@ export const cutLeapingFishTask: MinionTask = { duration }); - let str = `${user}, ${user.minionName} finished cutting ${quantity}x ${barbarianFish.item.name}. ${xpRes}\n\n You received: ${loot}.`; + const str = `${user}, ${user.minionName} finished cutting ${quantity}x ${barbarianFish.item.name}. ${xpRes}\n\n You received: ${loot}.`; await transactItems({ userID: user.id, diff --git a/src/tasks/minions/darkAltarActivity.ts b/src/tasks/minions/darkAltarActivity.ts index aa6f577ffe3..a1fee6d7680 100644 --- a/src/tasks/minions/darkAltarActivity.ts +++ b/src/tasks/minions/darkAltarActivity.ts @@ -5,7 +5,7 @@ import { Events } from '../../lib/constants'; import { darkAltarRunes } from '../../lib/minions/functions/darkAltarCommand'; import { bloodEssence, raimentBonus } from '../../lib/skilling/functions/calcsRunecrafting'; import { SkillsEnum } from '../../lib/skilling/types'; -import { DarkAltarOptions } from '../../lib/types/minions'; +import type { DarkAltarOptions } from '../../lib/types/minions'; import { skillingPetDropRate } from '../../lib/util'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; @@ -54,7 +54,7 @@ export const darkAltarTask: MinionTask = { runeQuantity += bonusBlood; } - let loot = new Bank().add(runeData.item.id, runeQuantity); + const loot = new Bank().add(runeData.item.id, runeQuantity); const { petDropRate } = skillingPetDropRate(user, SkillsEnum.Runecraft, runeData.petChance); for (let i = 0; i < quantity; i++) { if (roll(petDropRate)) { diff --git a/src/tasks/minions/enchantingActivity.ts b/src/tasks/minions/enchantingActivity.ts index 185a3d9c5aa..8f997bef2f4 100644 --- a/src/tasks/minions/enchantingActivity.ts +++ b/src/tasks/minions/enchantingActivity.ts @@ -1,12 +1,12 @@ import { Enchantables } from '../../lib/skilling/skills/magic/enchantables'; import { SkillsEnum } from '../../lib/skilling/types'; -import { EnchantingActivityTaskOptions } from '../../lib/types/minions'; +import type { EnchantingActivityTaskOptions } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; export const enchantingTask: MinionTask = { type: 'Enchanting', async run(data: EnchantingActivityTaskOptions) { - let { itemID, quantity, userID, channelID, duration } = data; + const { itemID, quantity, userID, channelID, duration } = data; const user = await mUserFetch(userID); const enchantable = Enchantables.find(fletchable => fletchable.id === itemID)!; @@ -25,7 +25,7 @@ export const enchantingTask: MinionTask = { itemsToAdd: loot }); - let str = `${user}, ${user.minionName} finished enchanting ${quantity}x ${enchantable.name}, you received ${loot}. ${xpRes}`; + const str = `${user}, ${user.minionName} finished enchanting ${quantity}x ${enchantable.name}, you received ${loot}. ${xpRes}`; handleTripFinish(user, channelID, str, undefined, data, loot); } diff --git a/src/tasks/minions/farmingActivity.ts b/src/tasks/minions/farmingActivity.ts index f638a9d50b1..b5330c88820 100644 --- a/src/tasks/minions/farmingActivity.ts +++ b/src/tasks/minions/farmingActivity.ts @@ -3,13 +3,13 @@ import { Bank, Monsters } from 'oldschooljs'; import { combatAchievementTripEffect } from '../../lib/combat_achievements/combatAchievements'; import { BitField, Emoji, Events } from '../../lib/constants'; -import { PatchTypes } from '../../lib/minions/farming'; -import { FarmingContract } from '../../lib/minions/farming/types'; +import type { PatchTypes } from '../../lib/minions/farming'; +import type { FarmingContract } from '../../lib/minions/farming/types'; import { prisma } from '../../lib/settings/prisma'; import { calcVariableYield } from '../../lib/skilling/functions/calcsFarming'; import Farming from '../../lib/skilling/skills/farming'; import { SkillsEnum } from '../../lib/skilling/types'; -import { FarmingActivityTaskOptions, MonsterActivityTaskOptions } from '../../lib/types/minions'; +import type { FarmingActivityTaskOptions, MonsterActivityTaskOptions } from '../../lib/types/minions'; import { assert, roll, skillingPetDropRate } from '../../lib/util'; import chatHeadImage from '../../lib/util/chatHeadImage'; import { getFarmingKeyFromName } from '../../lib/util/farmingHelpers'; @@ -465,7 +465,7 @@ export const farmingTask: MinionTask = { contractsCompleted + 1 } farming contracts.`, head: 'jane' - }) + }) : undefined, data, loot diff --git a/src/tasks/minions/firemakingActivity.ts b/src/tasks/minions/firemakingActivity.ts index 2a9a531a58c..0bc75d52143 100644 --- a/src/tasks/minions/firemakingActivity.ts +++ b/src/tasks/minions/firemakingActivity.ts @@ -1,6 +1,6 @@ import Firemaking from '../../lib/skilling/skills/firemaking'; import { SkillsEnum } from '../../lib/skilling/types'; -import { FiremakingActivityTaskOptions } from '../../lib/types/minions'; +import type { FiremakingActivityTaskOptions } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; export const firemakingTask: MinionTask = { @@ -17,7 +17,7 @@ export const firemakingTask: MinionTask = { // If they have the entire pyromancer outfit, give an extra 0.5% xp bonus if ( user.gear.skilling.hasEquipped( - Object.keys(Firemaking.pyromancerItems).map(i => parseInt(i)), + Object.keys(Firemaking.pyromancerItems).map(i => Number.parseInt(i)), true ) ) { @@ -27,7 +27,7 @@ export const firemakingTask: MinionTask = { } else { // For each pyromancer item, check if they have it, give its' XP boost if so. for (const [itemID, bonus] of Object.entries(Firemaking.pyromancerItems)) { - if (user.hasEquipped(parseInt(itemID))) { + if (user.hasEquipped(Number.parseInt(itemID))) { const amountToAdd = Math.floor(xpReceived * (bonus / 100)); xpReceived += amountToAdd; bonusXP += amountToAdd; diff --git a/src/tasks/minions/fishingActivity.ts b/src/tasks/minions/fishingActivity.ts index b81cf7cf426..1eb322259ee 100644 --- a/src/tasks/minions/fishingActivity.ts +++ b/src/tasks/minions/fishingActivity.ts @@ -6,7 +6,7 @@ import { Emoji, Events } from '../../lib/constants'; import addSkillingClueToLoot from '../../lib/minions/functions/addSkillingClueToLoot'; import Fishing from '../../lib/skilling/skills/fishing'; import { SkillsEnum } from '../../lib/skilling/types'; -import { FishingActivityTaskOptions } from '../../lib/types/minions'; +import type { FishingActivityTaskOptions } from '../../lib/types/minions'; import { roll, skillingPetDropRate } from '../../lib/util'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; import itemID from '../../lib/util/itemID'; @@ -40,7 +40,7 @@ export const fishingTask: MinionTask = { quantity: z.number().min(1) }), async run(data: FishingActivityTaskOptions) { - let { fishID, quantity, userID, channelID, duration } = data; + const { fishID, quantity, userID, channelID, duration } = data; const user = await mUserFetch(userID); const currentLevel = user.skillLevel(SkillsEnum.Fishing); const { blessingEquipped, blessingChance } = radasBlessing(user); @@ -96,7 +96,7 @@ export const fishingTask: MinionTask = { // If they have the entire angler outfit, give an extra 0.5% xp bonus if ( user.gear.skilling.hasEquipped( - Object.keys(Fishing.anglerItems).map(i => parseInt(i)), + Object.keys(Fishing.anglerItems).map(i => Number.parseInt(i)), true ) ) { @@ -106,7 +106,7 @@ export const fishingTask: MinionTask = { } else { // For each angler item, check if they have it, give its' XP boost if so. for (const [itemID, bonus] of Object.entries(Fishing.anglerItems)) { - if (user.hasEquipped(parseInt(itemID))) { + if (user.hasEquipped(Number.parseInt(itemID))) { const amountToAdd = Math.floor(xpReceived * (bonus / 100)); xpReceived += amountToAdd; bonusXP += amountToAdd; @@ -125,7 +125,7 @@ export const fishingTask: MinionTask = { skillName: SkillsEnum.Agility, amount: agilityXpReceived, duration - }) + }) : ''; xpRes += strengthXpReceived > 0 @@ -133,7 +133,7 @@ export const fishingTask: MinionTask = { skillName: SkillsEnum.Strength, amount: strengthXpReceived, duration - }) + }) : ''; let str = `${user}, ${user.minionName} finished fishing ${quantity} ${fish.name}. ${xpRes}`; @@ -142,7 +142,7 @@ export const fishingTask: MinionTask = { const baseKarambwanji = 1 + Math.floor(user.skillLevel(SkillsEnum.Fishing) / 5); let baseMinnow = [10, 10]; for (const [level, quantities] of Object.entries(minnowQuantity).reverse()) { - if (user.skillLevel(SkillsEnum.Fishing) >= parseInt(level)) { + if (user.skillLevel(SkillsEnum.Fishing) >= Number.parseInt(level)) { baseMinnow = quantities; break; } @@ -162,7 +162,7 @@ export const fishingTask: MinionTask = { } } - let loot = new Bank({ + const loot = new Bank({ [fish.id]: lootQuantity }); diff --git a/src/tasks/minions/fletchingActivity.ts b/src/tasks/minions/fletchingActivity.ts index 517385bbe69..6c469473906 100644 --- a/src/tasks/minions/fletchingActivity.ts +++ b/src/tasks/minions/fletchingActivity.ts @@ -2,13 +2,13 @@ import { Bank } from 'oldschooljs'; import Fletching from '../../lib/skilling/skills/fletching'; import { SkillsEnum } from '../../lib/skilling/types'; -import { FletchingActivityTaskOptions } from '../../lib/types/minions'; +import type { FletchingActivityTaskOptions } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; export const fletchingTask: MinionTask = { type: 'Fletching', async run(data: FletchingActivityTaskOptions) { - let { fletchableName, quantity, userID, channelID, duration } = data; + const { fletchableName, quantity, userID, channelID, duration } = data; const user = await mUserFetch(userID); const fletchableItem = Fletching.Fletchables.find(fletchable => fletchable.name === fletchableName)!; @@ -35,7 +35,7 @@ export const fletchingTask: MinionTask = { if (fletchableItem.outputMultiple) { sets = ' sets of'; } - let quantityToGive = fletchableItem.outputMultiple ? quantity * fletchableItem.outputMultiple : quantity; + const quantityToGive = fletchableItem.outputMultiple ? quantity * fletchableItem.outputMultiple : quantity; const loot = new Bank({ [fletchableItem.id]: quantityToGive }); await transactItems({ diff --git a/src/tasks/minions/gloryChargingActivity.ts b/src/tasks/minions/gloryChargingActivity.ts index 88aff026436..e0d62ef0b84 100644 --- a/src/tasks/minions/gloryChargingActivity.ts +++ b/src/tasks/minions/gloryChargingActivity.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import { Events } from '../../lib/constants'; -import { ActivityTaskOptionsWithQuantity } from '../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity } from '../../lib/types/minions'; import { roll } from '../../lib/util'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; import { gloriesInventorySize } from '../../mahoji/lib/abstracted_commands/chargeGloriesCommand'; @@ -12,7 +12,7 @@ export const gloryChargingTask: MinionTask = { const { quantity, userID, channelID } = data; const user = await mUserFetch(userID); let deaths = 0; - let loot = new Bank(); + const loot = new Bank(); for (let i = 0; i < quantity; i++) { if (roll(99)) { deaths++; diff --git a/src/tasks/minions/groupMonsterActivity.ts b/src/tasks/minions/groupMonsterActivity.ts index b0ab3ef7b5d..9271e908083 100644 --- a/src/tasks/minions/groupMonsterActivity.ts +++ b/src/tasks/minions/groupMonsterActivity.ts @@ -6,7 +6,7 @@ import killableMonsters from '../../lib/minions/data/killableMonsters'; import { addMonsterXP } from '../../lib/minions/functions'; import announceLoot from '../../lib/minions/functions/announceLoot'; import isImportantItemForMonster from '../../lib/minions/functions/isImportantItemForMonster'; -import { GroupMonsterActivityTaskOptions } from '../../lib/types/minions'; +import type { GroupMonsterActivityTaskOptions } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; export const groupoMonsterTask: MinionTask = { @@ -23,7 +23,7 @@ export const groupoMonsterTask: MinionTask = { const userWhoGetsLoot = randArrItem(users); const currentLoot = teamsLoot[userWhoGetsLoot]; teamsLoot[userWhoGetsLoot] = loot.add(currentLoot); - kcAmounts[userWhoGetsLoot] = Boolean(kcAmounts[userWhoGetsLoot]) ? ++kcAmounts[userWhoGetsLoot] : 1; + kcAmounts[userWhoGetsLoot] = kcAmounts[userWhoGetsLoot] ? ++kcAmounts[userWhoGetsLoot] : 1; } const leaderUser = await mUserFetch(leader); @@ -49,7 +49,9 @@ export const groupoMonsterTask: MinionTask = { }); const kcToAdd = kcAmounts[user.id]; if (kcToAdd) await user.incrementKC(monsterID, kcToAdd); - const purple = Object.keys(loot).some(itemID => isImportantItemForMonster(parseInt(itemID), monster)); + const purple = Object.keys(loot).some(itemID => + isImportantItemForMonster(Number.parseInt(itemID), monster) + ); resultStr += `${purple ? Emoji.Purple : ''} **${user} received:** ||${loot}||\n`; diff --git a/src/tasks/minions/herbloreActivity.ts b/src/tasks/minions/herbloreActivity.ts index ce17aa63cec..eda1d21e6bf 100644 --- a/src/tasks/minions/herbloreActivity.ts +++ b/src/tasks/minions/herbloreActivity.ts @@ -1,10 +1,10 @@ import { randInt } from 'e'; import { Bank } from 'oldschooljs'; -import { userhasDiaryTier, WildernessDiary } from '../../lib/diaries'; +import { WildernessDiary, userhasDiaryTier } from '../../lib/diaries'; import Herblore from '../../lib/skilling/skills/herblore/herblore'; import { SkillsEnum } from '../../lib/skilling/types'; -import { HerbloreActivityTaskOptions } from '../../lib/types/minions'; +import type { HerbloreActivityTaskOptions } from '../../lib/types/minions'; import { percentChance } from '../../lib/util'; import getOSItem from '../../lib/util/getOSItem'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; @@ -24,11 +24,11 @@ export const herbloreTask: MinionTask = { const currentHerbLevel = user.skillLevel(SkillsEnum.Herblore); let scales = 0; // Having 99 herblore gives a 98% chance to recieve the max amount of shards - let maxShardChance = currentHerbLevel >= 99 ? 98 : 0; + const maxShardChance = currentHerbLevel >= 99 ? 98 : 0; // Completion of hard wilderness diary gives 50% more lava scale shards per lava scale, rounded down - let diaryMultiplier = hasWildyDiary ? 1.5 : 1; + const diaryMultiplier = hasWildyDiary ? 1.5 : 1; - if (Boolean(wesley)) { + if (wesley) { // Wesley always turns Lava scales into 3 lava scale shards scales = quantity * 3; } else { diff --git a/src/tasks/minions/mageArena2Activity.ts b/src/tasks/minions/mageArena2Activity.ts index e5cd5138c52..4a348b5e13d 100644 --- a/src/tasks/minions/mageArena2Activity.ts +++ b/src/tasks/minions/mageArena2Activity.ts @@ -1,13 +1,13 @@ import { percentChance, randArrItem } from 'e'; import { Bank } from 'oldschooljs'; -import { ActivityTaskOptionsWithNoChanges } from '../../lib/types/minions'; +import type { ActivityTaskOptionsWithNoChanges } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; export const mageArenaTwoTask: MinionTask = { type: 'MageArena2', async run(data: ActivityTaskOptionsWithNoChanges) { - let { userID, channelID } = data; + const { userID, channelID } = data; const user = await mUserFetch(userID); let str = ''; diff --git a/src/tasks/minions/mageArenaActivity.ts b/src/tasks/minions/mageArenaActivity.ts index 8b8e3aeea60..42924010c72 100644 --- a/src/tasks/minions/mageArenaActivity.ts +++ b/src/tasks/minions/mageArenaActivity.ts @@ -1,12 +1,12 @@ import { Bank } from 'oldschooljs'; -import { ActivityTaskOptionsWithNoChanges } from '../../lib/types/minions'; +import type { ActivityTaskOptionsWithNoChanges } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; export const mageArenaTask: MinionTask = { type: 'MageArena', async run(data: ActivityTaskOptionsWithNoChanges) { - let { userID, channelID } = data; + const { userID, channelID } = data; const user = await mUserFetch(userID); const loot = new Bank().add('Saradomin cape').add('Zamorak cape').add('Guthix cape'); await transactItems({ diff --git a/src/tasks/minions/minigames/agilityArenaActivity.ts b/src/tasks/minions/minigames/agilityArenaActivity.ts index 5718840ef20..ebb1514232a 100644 --- a/src/tasks/minions/minigames/agilityArenaActivity.ts +++ b/src/tasks/minions/minigames/agilityArenaActivity.ts @@ -1,11 +1,11 @@ -import { calcWhatPercent, reduceNumByPercent, Time } from 'e'; +import { Time, calcWhatPercent, reduceNumByPercent } from 'e'; import { Bank } from 'oldschooljs'; import { Emoji, Events } from '../../../lib/constants'; import { KaramjaDiary, userhasDiaryTier } from '../../../lib/diaries'; import { incrementMinigameScore } from '../../../lib/settings/settings'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; import { formatDuration, randomVariation, roll, skillingPetDropRate } from '../../../lib/util'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { determineXPFromTickets } from '../../../mahoji/lib/abstracted_commands/agilityArenaCommand'; @@ -18,7 +18,7 @@ export const agilityArenaTask: MinionTask = { const currentLevel = user.skillLevel(SkillsEnum.Agility); // You get 1 ticket per minute at best without diary - let timePerTicket = Time.Minute; + const timePerTicket = Time.Minute; let ticketsReceived = Math.floor(duration / timePerTicket); // Approximately 25k xp/hr (416xp per min) from the obstacles @@ -63,7 +63,7 @@ export const agilityArenaTask: MinionTask = { str += `\nYou received ${bonusTickets} bonus tickets for the Karamja Elite Diary.`; } - let xpFromTickets = determineXPFromTickets(ticketsReceived, user, hasKaramjaElite); + const xpFromTickets = determineXPFromTickets(ticketsReceived, user, hasKaramjaElite); const xpFromTrip = xpFromTickets + agilityXP; str += `\n${( (xpFromTrip / (duration / Time.Minute)) * diff --git a/src/tasks/minions/minigames/barbarianAssaultActivity.ts b/src/tasks/minions/minigames/barbarianAssaultActivity.ts index de92b4f7bb5..2b0b6707cf3 100644 --- a/src/tasks/minions/minigames/barbarianAssaultActivity.ts +++ b/src/tasks/minions/minigames/barbarianAssaultActivity.ts @@ -2,7 +2,7 @@ import { calcPercentOfNum, calcWhatPercent, randInt } from 'e'; import { KandarinDiary, userhasDiaryTier } from '../../../lib/diaries'; import { incrementMinigameScore } from '../../../lib/settings/settings'; -import { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; +import type { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { userStatsUpdate } from '../../../mahoji/mahojiSettings'; @@ -28,7 +28,7 @@ export const barbAssaultTask: MinionTask = { pts *= 1.1; resultStr += `${user.usernameOrMention} received 10% extra pts for Kandarin Hard diary. `; } - let totalPoints = Math.floor(pts * quantity); + const totalPoints = Math.floor(pts * quantity); await incrementMinigameScore(user.id, 'barb_assault', quantity); await userStatsUpdate( diff --git a/src/tasks/minions/minigames/castleWarsActivity.ts b/src/tasks/minions/minigames/castleWarsActivity.ts index 9a79c3365eb..3b67d719934 100644 --- a/src/tasks/minions/minigames/castleWarsActivity.ts +++ b/src/tasks/minions/minigames/castleWarsActivity.ts @@ -2,7 +2,7 @@ import { SimpleTable } from '@oldschoolgg/toolkit'; import { Bank } from 'oldschooljs'; import { incrementMinigameScore } from '../../../lib/settings/settings'; -import { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; +import type { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; const ticketTable = new SimpleTable().add(1, 4).add(2, 4).add(3, 1); diff --git a/src/tasks/minions/minigames/championsChallengeActivity.ts b/src/tasks/minions/minigames/championsChallengeActivity.ts index 163494c706e..66829140f78 100644 --- a/src/tasks/minions/minigames/championsChallengeActivity.ts +++ b/src/tasks/minions/minigames/championsChallengeActivity.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import { incrementMinigameScore } from '../../../lib/settings/settings'; -import { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; +import type { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; export const championsChallengeTask: MinionTask = { diff --git a/src/tasks/minions/minigames/chompyHuntActivity.ts b/src/tasks/minions/minigames/chompyHuntActivity.ts index 18f9d216fe3..7df8ad39123 100644 --- a/src/tasks/minions/minigames/chompyHuntActivity.ts +++ b/src/tasks/minions/minigames/chompyHuntActivity.ts @@ -2,9 +2,9 @@ import { roll } from 'e'; import { Bank } from 'oldschooljs'; import { chompyHats } from '../../../lib/constants'; -import { userhasDiaryTier, WesternProv } from '../../../lib/diaries'; +import { WesternProv, userhasDiaryTier } from '../../../lib/diaries'; import { getMinigameEntity, incrementMinigameScore } from '../../../lib/settings/settings'; -import { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; +import type { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; export const chompHuntTask: MinionTask = { diff --git a/src/tasks/minions/minigames/fightCavesActivity.ts b/src/tasks/minions/minigames/fightCavesActivity.ts index 04abe6e2f0c..fb918924c99 100644 --- a/src/tasks/minions/minigames/fightCavesActivity.ts +++ b/src/tasks/minions/minigames/fightCavesActivity.ts @@ -6,7 +6,7 @@ import { Emoji, Events } from '../../../lib/constants'; import { prisma } from '../../../lib/settings/prisma'; import { SkillsEnum } from '../../../lib/skilling/types'; import { calculateSlayerPoints, getUsersCurrentSlayerInfo } from '../../../lib/slayer/slayerUtil'; -import { FightCavesActivityTaskOptions } from '../../../lib/types/minions'; +import type { FightCavesActivityTaskOptions } from '../../../lib/types/minions'; import { formatDuration, percentChance } from '../../../lib/util'; import chatHeadImage from '../../../lib/util/chatHeadImage'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; @@ -42,8 +42,8 @@ export const fightCavesTask: MinionTask = { const isOnTask = usersTask.currentTask !== null && usersTask.currentTask !== undefined && - usersTask.currentTask!.monster_id === Monsters.TzHaarKet.id && - usersTask.currentTask!.quantity_remaining === usersTask.currentTask!.quantity; + usersTask.currentTask?.monster_id === Monsters.TzHaarKet.id && + usersTask.currentTask?.quantity_remaining === usersTask.currentTask?.quantity; if (preJadDeathTime) { let slayerMsg = ''; @@ -52,7 +52,7 @@ export const fightCavesTask: MinionTask = { await prisma.slayerTask.update({ where: { - id: usersTask.currentTask!.id + id: usersTask.currentTask?.id }, data: { quantity_remaining: 0, @@ -103,7 +103,7 @@ export const fightCavesTask: MinionTask = { await prisma.slayerTask.update({ where: { - id: usersTask.currentTask!.id + id: usersTask.currentTask?.id }, data: { quantity_remaining: 0, @@ -179,7 +179,7 @@ export const fightCavesTask: MinionTask = { await prisma.slayerTask.update({ where: { - id: usersTask.currentTask!.id + id: usersTask.currentTask?.id }, data: { quantity_remaining: 0 diff --git a/src/tasks/minions/minigames/gauntletActivity.ts b/src/tasks/minions/minigames/gauntletActivity.ts index 38a752d6a56..b6c3815f175 100644 --- a/src/tasks/minions/minigames/gauntletActivity.ts +++ b/src/tasks/minions/minigames/gauntletActivity.ts @@ -3,9 +3,10 @@ import { calcWhatPercent, percentChance } from 'e'; import { Bank } from 'oldschooljs'; import { Events } from '../../../lib/constants'; -import { getMinigameScore, incrementMinigameScore, MinigameName } from '../../../lib/settings/settings'; +import type { MinigameName } from '../../../lib/settings/settings'; +import { getMinigameScore, incrementMinigameScore } from '../../../lib/settings/settings'; import { gauntlet } from '../../../lib/simulation/gauntlet'; -import { GauntletOptions } from '../../../lib/types/minions'; +import type { GauntletOptions } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { makeBankImage } from '../../../lib/util/makeBankImage'; import { updateBankSetting } from '../../../lib/util/updateBankSetting'; diff --git a/src/tasks/minions/minigames/giantsFoundryActivity.ts b/src/tasks/minions/minigames/giantsFoundryActivity.ts index 635730f967e..1b9facbf8f3 100644 --- a/src/tasks/minions/minigames/giantsFoundryActivity.ts +++ b/src/tasks/minions/minigames/giantsFoundryActivity.ts @@ -1,16 +1,12 @@ import { deepClone } from 'e'; import { Bank } from 'oldschooljs'; -import { - encodeGiantWeapons, - generateRandomGiantWeapon, - GiantsFoundryBank, - giantWeaponName -} from '../../../lib/giantsFoundry'; +import type { GiantsFoundryBank } from '../../../lib/giantsFoundry'; +import { encodeGiantWeapons, generateRandomGiantWeapon, giantWeaponName } from '../../../lib/giantsFoundry'; import { trackLoot } from '../../../lib/lootTrack'; import { incrementMinigameScore } from '../../../lib/settings/settings'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { GiantsFoundryActivityTaskOptions } from '../../../lib/types/minions'; +import type { GiantsFoundryActivityTaskOptions } from '../../../lib/types/minions'; import { randomVariation } from '../../../lib/util'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { updateBankSetting } from '../../../lib/util/updateBankSetting'; @@ -37,7 +33,7 @@ export const giantsFoundryTask: MinionTask = { const newWeapons = deepClone(currentStats.gf_weapons_made) as GiantsFoundryBank; for (let i = 0; i < quantity; i++) { - let quality = Math.min(Math.floor(randomVariation(metalScore - 5 + avgMouldBonus, 10)), 199); + const quality = Math.min(Math.floor(randomVariation(metalScore - 5 + avgMouldBonus, 10)), 199); xpReceived += (Math.pow(quality, 2) / 73 + 1.5 * quality + 1) * 30; reputationReceived += quality; @@ -75,7 +71,7 @@ export const giantsFoundryTask: MinionTask = { const loot = new Bank().add('Coins', 2 * xpReceived); - let str = `${user}, ${ + const str = `${user}, ${ user.minionName } finished creating ${quantity}x giant weapons in the Giants' Foundry minigame. ${ boosts.length > 0 ? `**Boosts:** ${boosts.join(', ')}.` : '' diff --git a/src/tasks/minions/minigames/gnomeRestaurantActivity.ts b/src/tasks/minions/minigames/gnomeRestaurantActivity.ts index f5b610cd11f..d1a63281b48 100644 --- a/src/tasks/minions/minigames/gnomeRestaurantActivity.ts +++ b/src/tasks/minions/minigames/gnomeRestaurantActivity.ts @@ -3,7 +3,7 @@ import LootTable from 'oldschooljs/dist/structures/LootTable'; import { incrementMinigameScore } from '../../../lib/settings/settings'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { GnomeRestaurantActivityTaskOptions } from '../../../lib/types/minions'; +import type { GnomeRestaurantActivityTaskOptions } from '../../../lib/types/minions'; import { roll } from '../../../lib/util'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { updateBankSetting } from '../../../lib/util/updateBankSetting'; @@ -89,7 +89,7 @@ export const gnomeResTask: MinionTask = { duration }); - let str = `<@${userID}>, ${user.minionName} finished completing ${quantity}x Gnome Restaurant deliveries. You received **${loot}**. ${xpRes}`; + const str = `<@${userID}>, ${user.minionName} finished completing ${quantity}x Gnome Restaurant deliveries. You received **${loot}**. ${xpRes}`; updateBankSetting('gnome_res_loot', loot); diff --git a/src/tasks/minions/minigames/guardiansOfTheRiftActivity.ts b/src/tasks/minions/minigames/guardiansOfTheRiftActivity.ts index c269d374bde..59f426cf044 100644 --- a/src/tasks/minions/minigames/guardiansOfTheRiftActivity.ts +++ b/src/tasks/minions/minigames/guardiansOfTheRiftActivity.ts @@ -14,7 +14,7 @@ import { makeBankImage } from '../../../lib/util/makeBankImage'; import { updateBankSetting } from '../../../lib/util/updateBankSetting'; import { calcMaxRCQuantity, userStatsUpdate } from '../../../mahoji/mahojiSettings'; import { rewardsGuardianTable } from './../../../lib/simulation/rewardsGuardian'; -import { GuardiansOfTheRiftActivityTaskOptions } from './../../../lib/types/minions'; +import type { GuardiansOfTheRiftActivityTaskOptions } from './../../../lib/types/minions'; const catalyticRunesArray: string[] = [ 'Mind rune', @@ -74,7 +74,7 @@ export const guardiansOfTheRiftTask: MinionTask = { }) ]); - let runesLoot = new Bank(); + const runesLoot = new Bank(); let inventorySize = 28; const { bank } = user; // For each pouch the user has, increase their inventory size. @@ -88,7 +88,7 @@ export const guardiansOfTheRiftTask: MinionTask = { let setBonus = 1; if ( user.gear.skilling.hasEquipped( - Object.keys(Runecraft.raimentsOfTheEyeItems).map(i => parseInt(i)), + Object.keys(Runecraft.raimentsOfTheEyeItems).map(i => Number.parseInt(i)), true ) ) { @@ -96,7 +96,7 @@ export const guardiansOfTheRiftTask: MinionTask = { } else { // For each Raiments of the Eye item, check if they have it, give its' quantity boost if so (NO bonus XP). for (const [itemID, bonus] of Object.entries(Runecraft.raimentsOfTheEyeItems)) { - if (user.gear.skilling.hasEquipped([parseInt(itemID)], false)) { + if (user.gear.skilling.hasEquipped([Number.parseInt(itemID)], false)) { setBonus += bonus / 100; } } @@ -124,7 +124,7 @@ export const guardiansOfTheRiftTask: MinionTask = { runesLoot.add(rune, Math.floor(quantityPerEssence * inventorySize * setBonus)); } - let rewardsGuardianLoot = new Bank(); + const rewardsGuardianLoot = new Bank(); let rewardsQty = 0; for (let i = 0; i < quantity; i++) { rewardsQty += randInt(rolls - 1, rolls); diff --git a/src/tasks/minions/minigames/infernoActivity.ts b/src/tasks/minions/minigames/infernoActivity.ts index 8dc076fcec0..d1c709cf008 100644 --- a/src/tasks/minions/minigames/infernoActivity.ts +++ b/src/tasks/minions/minigames/infernoActivity.ts @@ -1,7 +1,7 @@ import { formatOrdinal } from '@oldschoolgg/toolkit'; import { calcPercentOfNum, calcWhatPercent } from 'e'; import { Bank, Monsters } from 'oldschooljs'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; import { Events } from '../../../lib/constants'; import { diariesObject, userhasDiaryTier } from '../../../lib/diaries'; @@ -9,7 +9,7 @@ import { countUsersWithItemInCl, prisma } from '../../../lib/settings/prisma'; import { getMinigameScore, incrementMinigameScore } from '../../../lib/settings/settings'; import { SkillsEnum } from '../../../lib/skilling/types'; import { calculateSlayerPoints, getUsersCurrentSlayerInfo } from '../../../lib/slayer/slayerUtil'; -import { InfernoOptions } from '../../../lib/types/minions'; +import type { InfernoOptions } from '../../../lib/types/minions'; import { formatDuration } from '../../../lib/util'; import chatHeadImage from '../../../lib/util/chatHeadImage'; import { mahojiClientSettingsFetch, mahojiClientSettingsUpdate } from '../../../lib/util/clientSettings'; @@ -28,9 +28,9 @@ export const infernoTask: MinionTask = { const isOnTask = usersTask.currentTask !== null && usersTask.currentTask !== undefined && - usersTask.currentTask!.monster_id === Monsters.TzHaarKet.id && + usersTask.currentTask?.monster_id === Monsters.TzHaarKet.id && score > 0 && - usersTask.currentTask!.quantity_remaining === usersTask.currentTask!.quantity; + usersTask.currentTask?.quantity_remaining === usersTask.currentTask?.quantity; const unusedItems = new Bank(); const cost = new Bank(data.cost); @@ -101,7 +101,7 @@ export const infernoTask: MinionTask = { await prisma.slayerTask.update({ where: { - id: usersTask.currentTask!.id + id: usersTask.currentTask?.id }, data: { quantity_remaining: 0, @@ -132,7 +132,7 @@ export const infernoTask: MinionTask = { await prisma.slayerTask.update({ where: { - id: usersTask.currentTask!.id + id: usersTask.currentTask?.id }, data: { quantity_remaining: 0, @@ -206,7 +206,7 @@ You made it through ${percentMadeItThrough.toFixed(2)}% of the Inferno${ unusedItems.length ? `, you didn't use ${percSuppliesRefunded.toFixed( 2 - )}% of your supplies, ${unusedItems} was returned to your bank` + )}% of your supplies, ${unusedItems} was returned to your bank` : '.' } `, diff --git a/src/tasks/minions/minigames/lmsActivity.ts b/src/tasks/minions/minigames/lmsActivity.ts index e8e4e768213..0c0d0ca92c1 100644 --- a/src/tasks/minions/minigames/lmsActivity.ts +++ b/src/tasks/minions/minigames/lmsActivity.ts @@ -1,10 +1,10 @@ -import { formatOrdinal, SimpleTable } from '@oldschoolgg/toolkit'; +import { SimpleTable, formatOrdinal } from '@oldschoolgg/toolkit'; import { clamp, percentChance, sumArr } from 'e'; import { Emoji } from '../../../lib/constants'; import { prisma } from '../../../lib/settings/prisma'; import { incrementMinigameScore } from '../../../lib/settings/settings'; -import { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; +import type { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; import { calcPerHour, gaussianRandom } from '../../../lib/util'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; @@ -76,9 +76,9 @@ export function calculateResultOfLMSGames(qty: number, lmsStats: Awaited 0) { diff --git a/src/tasks/minions/minigames/plunderActivity.ts b/src/tasks/minions/minigames/plunderActivity.ts index a17154cee95..7023f70e722 100644 --- a/src/tasks/minions/minigames/plunderActivity.ts +++ b/src/tasks/minions/minigames/plunderActivity.ts @@ -6,7 +6,7 @@ import { incrementMinigameScore } from '../../../lib/settings/settings'; import { SkillsEnum } from '../../../lib/skilling/types'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { makeBankImage } from '../../../lib/util/makeBankImage'; -import { PlunderActivityTaskOptions } from './../../../lib/types/minions'; +import type { PlunderActivityTaskOptions } from './../../../lib/types/minions'; export const plunderTask: MinionTask = { type: 'Plunder', diff --git a/src/tasks/minions/minigames/puroPuroActivity.ts b/src/tasks/minions/minigames/puroPuroActivity.ts index 22f27179bb3..7ecf566691e 100644 --- a/src/tasks/minions/minigames/puroPuroActivity.ts +++ b/src/tasks/minions/minigames/puroPuroActivity.ts @@ -1,16 +1,16 @@ -import { randInt, reduceNumByPercent, roll, Time } from 'e'; +import { Time, randInt, reduceNumByPercent, roll } from 'e'; import { Bank } from 'oldschooljs'; import { SkillsEnum } from 'oldschooljs/dist/constants'; import { implings, puroImpHighTierTable, - puroImplings, puroImpNormalTable, - puroImpSpellTable + puroImpSpellTable, + puroImplings } from '../../../lib/implings'; import { incrementMinigameScore } from '../../../lib/settings/minigames'; -import { PuroPuroActivityTaskOptions } from '../../../lib/types/minions'; +import type { PuroPuroActivityTaskOptions } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import itemID from '../../../lib/util/itemID'; import { userHasGracefulEquipped, userStatsBankUpdate } from '../../../mahoji/mahojiSettings'; @@ -35,7 +35,7 @@ export const puroPuroTask: MinionTask = { const missed = new Bank(); const itemCost = new Bank(); let hunterXP = 0; - let hunterLevel = user.skillLevel(SkillsEnum.Hunter); + const hunterLevel = user.skillLevel(SkillsEnum.Hunter); const allImpQty = hunt(minutes, user, 1, 3); const highTierImpQty = hunt(minutes, user, 0.75, 1) * (darkLure ? 1.2 : 1); const singleImpQty = hunt(minutes, user, 5, 6); diff --git a/src/tasks/minions/minigames/raidsActivity.ts b/src/tasks/minions/minigames/raidsActivity.ts index 3b3d9b0b904..20ea72e2acc 100644 --- a/src/tasks/minions/minigames/raidsActivity.ts +++ b/src/tasks/minions/minigames/raidsActivity.ts @@ -11,7 +11,7 @@ import { createTeam } from '../../../lib/data/cox'; import { trackLoot } from '../../../lib/lootTrack'; import { resolveAttackStyles } from '../../../lib/minions/functions'; import { getMinigameScore, incrementMinigameScore } from '../../../lib/settings/settings'; -import { RaidsOptions } from '../../../lib/types/minions'; +import type { RaidsOptions } from '../../../lib/types/minions'; import { randomVariation, roll } from '../../../lib/util'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import resolveItems from '../../../lib/util/resolveItems'; @@ -149,7 +149,7 @@ export const raidsTask: MinionTask = { } finished. The total amount of points your team got is ${totalPoints.toLocaleString()}.\n`; await Promise.all(allUsers.map(u => incrementMinigameScore(u.id, minigameID, quantity))); - for (let [userID, userData] of raidResults) { + for (const [userID, userData] of raidResults) { const { personalPoints, deaths, deathChance, loot, mUser: user } = userData; if (!user) continue; @@ -230,7 +230,7 @@ export const raidsTask: MinionTask = { } ], type: 'Chambers of Xerician' - }) + }) : undefined, data, totalLoot @@ -250,7 +250,7 @@ export const raidsTask: MinionTask = { customTexts: [] })), type: 'Chambers of Xerician' - }) + }) : undefined, data, null diff --git a/src/tasks/minions/minigames/roguesDenMazeActivity.ts b/src/tasks/minions/minigames/roguesDenMazeActivity.ts index 4d50986e884..d42484ac2e6 100644 --- a/src/tasks/minions/minigames/roguesDenMazeActivity.ts +++ b/src/tasks/minions/minigames/roguesDenMazeActivity.ts @@ -3,7 +3,7 @@ import { Bank } from 'oldschooljs'; import { roguesDenOutfit } from '../../../lib/data/CollectionsExport'; import { incrementMinigameScore } from '../../../lib/settings/settings'; -import { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { makeBankImage } from '../../../lib/util/makeBankImage'; diff --git a/src/tasks/minions/minigames/sepulchreActivity.ts b/src/tasks/minions/minigames/sepulchreActivity.ts index 3511d3690bc..498934d1e58 100644 --- a/src/tasks/minions/minigames/sepulchreActivity.ts +++ b/src/tasks/minions/minigames/sepulchreActivity.ts @@ -5,7 +5,7 @@ import { trackLoot } from '../../../lib/lootTrack'; import { openCoffin, sepulchreFloors } from '../../../lib/minions/data/sepulchre'; import { incrementMinigameScore } from '../../../lib/settings/settings'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { SepulchreActivityTaskOptions } from '../../../lib/types/minions'; +import type { SepulchreActivityTaskOptions } from '../../../lib/types/minions'; import { roll } from '../../../lib/util'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { makeBankImage } from '../../../lib/util/makeBankImage'; @@ -49,13 +49,13 @@ export const sepulchreTask: MinionTask = { itemsToAdd: loot }); - let xpRes = await user.addXP({ + const xpRes = await user.addXP({ skillName: SkillsEnum.Agility, amount: agilityXP, duration }); - let thievingXpRes = await user.addXP({ + const thievingXpRes = await user.addXP({ skillName: SkillsEnum.Thieving, amount: thievingXP, duration @@ -77,7 +77,7 @@ export const sepulchreTask: MinionTask = { ] }); - let str = `${user}, ${user.minionName} finished doing the Hallowed Sepulchre ${quantity}x times (floor ${ + const str = `${user}, ${user.minionName} finished doing the Hallowed Sepulchre ${quantity}x times (floor ${ floors[0] }-${floors[floors.length - 1]}), and opened ${numCoffinsOpened}x coffins.\n\n${xpRes}\n${thievingXpRes}`; diff --git a/src/tasks/minions/minigames/shadesOfMortonActivity.ts b/src/tasks/minions/minigames/shadesOfMortonActivity.ts index 75b94d71f59..62d9a4064ad 100644 --- a/src/tasks/minions/minigames/shadesOfMortonActivity.ts +++ b/src/tasks/minions/minigames/shadesOfMortonActivity.ts @@ -4,7 +4,7 @@ import { Bank, LootTable } from 'oldschooljs'; import { MorytaniaDiary, userhasDiaryTier } from '../../../lib/diaries'; import { incrementMinigameScore } from '../../../lib/settings/minigames'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { ShadesOfMortonOptions } from '../../../lib/types/minions'; +import type { ShadesOfMortonOptions } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { shades, shadesLogs } from '../../../mahoji/lib/abstracted_commands/shadesOfMortonCommand'; @@ -19,17 +19,17 @@ export const shadesOfMortonTask: MinionTask = { const log = shadesLogs.find(i => i.normalLog.id === logID)!; const shade = shades.find(i => i.shadeName === shadeID)!; - let loot = new Bank(); + const loot = new Bank(); const multiplier = 100; - let table = new LootTable().add('Coins', 0.21 * multiplier); + const table = new LootTable().add('Coins', 0.21 * multiplier); if (shade.lowMetalKeys) { - let subTable = new LootTable(); + const subTable = new LootTable(); for (const key of shade.lowMetalKeys.items) subTable.add(key); table.add(subTable, 1, shade.lowMetalKeys.fraction * multiplier); } if (shade.highMetalKeys) { - let subTable = new LootTable(); + const subTable = new LootTable(); for (const key of shade.highMetalKeys.items) subTable.add(key); table.add(subTable, 1, shade.highMetalKeys.fraction * multiplier); } @@ -38,7 +38,7 @@ export const shadesOfMortonTask: MinionTask = { loot.add(table.roll()); } - let messages: string[] = []; + const messages: string[] = []; const { itemsAdded } = await transactItems({ userID: user.id, collectionLog: true, itemsToAdd: loot }); diff --git a/src/tasks/minions/minigames/soulWarsActivity.ts b/src/tasks/minions/minigames/soulWarsActivity.ts index b1c67dbac29..93135c48291 100644 --- a/src/tasks/minions/minigames/soulWarsActivity.ts +++ b/src/tasks/minions/minigames/soulWarsActivity.ts @@ -1,7 +1,7 @@ import { increaseNumByPercent, reduceNumByPercent } from 'e'; import { incrementMinigameScore } from '../../../lib/settings/settings'; -import { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; +import type { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; import { roll } from '../../../lib/util'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; diff --git a/src/tasks/minions/minigames/tearsOfGuthixActivity.ts b/src/tasks/minions/minigames/tearsOfGuthixActivity.ts index f605aa0193a..001e146c6eb 100644 --- a/src/tasks/minions/minigames/tearsOfGuthixActivity.ts +++ b/src/tasks/minions/minigames/tearsOfGuthixActivity.ts @@ -2,8 +2,8 @@ import { increaseNumByPercent, randInt } from 'e'; import { LumbridgeDraynorDiary, userhasDiaryTier } from '../../../lib/diaries'; import { incrementMinigameScore } from '../../../lib/settings/settings'; -import { SkillsEnum } from '../../../lib/skilling/types'; -import { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; +import type { SkillsEnum } from '../../../lib/skilling/types'; +import type { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { userStatsUpdate } from '../../../mahoji/mahojiSettings'; @@ -58,7 +58,7 @@ export const togTask: MinionTask = { const xpStr = await user.addXP({ skillName: lowestSkill, amount: xpToGive, duration, source: 'TearsOfGuthix' }); - let output = `${user}, ${ + const output = `${user}, ${ user.minionName } finished telling Juna a story and drinking from the Tears of Guthix and collected ${tears} tears.\nLowest XP skill is ${lowestSkill}.\n${xpStr.toLocaleString()}.${ hasDiary ? '\n10% XP bonus for Lumbridge & Draynor Hard diary.' : '' diff --git a/src/tasks/minions/minigames/templeTrekkingActivity.ts b/src/tasks/minions/minigames/templeTrekkingActivity.ts index b9bd68476a3..191a45a7258 100644 --- a/src/tasks/minions/minigames/templeTrekkingActivity.ts +++ b/src/tasks/minions/minigames/templeTrekkingActivity.ts @@ -9,7 +9,7 @@ import { rewardTokens } from '../../../lib/minions/data/templeTrekking'; import { incrementMinigameScore } from '../../../lib/settings/settings'; -import { TempleTrekkingActivityTaskOptions } from '../../../lib/types/minions'; +import type { TempleTrekkingActivityTaskOptions } from '../../../lib/types/minions'; import { percentChance, stringMatches } from '../../../lib/util'; import getOSItem from '../../../lib/util/getOSItem'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; @@ -44,21 +44,21 @@ export const templeTrekkingTask: MinionTask = { const user = await mUserFetch(userID); await incrementMinigameScore(user.id, 'temple_trekking', quantity); const userBank = user.bank.clone(); - let loot = new Bank(); + const loot = new Bank(); const rewardToken = stringMatches(difficulty, 'hard') ? getOSItem(rewardTokens.hard) : stringMatches(difficulty, 'medium') - ? getOSItem(rewardTokens.medium) - : getOSItem(rewardTokens.easy); + ? getOSItem(rewardTokens.medium) + : getOSItem(rewardTokens.easy); let totalEncounters = 0; for (let trip = 0; trip < quantity; trip++) { const encounters = stringMatches(difficulty, 'hard') ? randInt(0, 7) : stringMatches(difficulty, 'medium') - ? randInt(0, 4) - : randInt(0, 5); + ? randInt(0, 4) + : randInt(0, 5); for (let i = 0; i < encounters; i++) { // 2 out of 12 encounters drop loot, 16% @@ -89,7 +89,7 @@ export const templeTrekkingTask: MinionTask = { itemsToAdd: loot }); - let str = `${user}, ${user.minionName} finished Temple Trekking ${quantity}x times. ${totalEncounters}x encounters were defeated.`; + const str = `${user}, ${user.minionName} finished Temple Trekking ${quantity}x times. ${totalEncounters}x encounters were defeated.`; const image = await makeBankImage({ bank: itemsAdded, diff --git a/src/tasks/minions/minigames/temporossActivity.ts b/src/tasks/minions/minigames/temporossActivity.ts index b9e9cd7e6fa..997389a3be3 100644 --- a/src/tasks/minions/minigames/temporossActivity.ts +++ b/src/tasks/minions/minigames/temporossActivity.ts @@ -6,7 +6,7 @@ import { getMinigameEntity, incrementMinigameScore } from '../../../lib/settings import { getTemporossLoot } from '../../../lib/simulation/tempoross'; import Fishing from '../../../lib/skilling/skills/fishing'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { TemporossActivityTaskOptions } from '../../../lib/types/minions'; +import type { TemporossActivityTaskOptions } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { makeBankImage } from '../../../lib/util/makeBankImage'; @@ -43,7 +43,7 @@ export const temporossTask: MinionTask = { // If they have the entire angler outfit, give an extra 0.5% xp bonus if ( user.gear.skilling.hasEquipped( - Object.keys(Fishing.anglerItems).map(i => parseInt(i)), + Object.keys(Fishing.anglerItems).map(i => Number.parseInt(i)), true ) ) { @@ -53,7 +53,7 @@ export const temporossTask: MinionTask = { } else { // For each angler item, check if they have it, give its' XP boost if so. for (const [itemID, bonus] of Object.entries(Fishing.anglerItems)) { - if (user.hasEquipped(parseInt(itemID))) { + if (user.hasEquipped(Number.parseInt(itemID))) { const amountToAdd = Math.floor(fXPtoGive * (bonus / 100)); fXPtoGive += amountToAdd; fBonusXP += amountToAdd; diff --git a/src/tasks/minions/minigames/titheFarmActivity.ts b/src/tasks/minions/minigames/titheFarmActivity.ts index 144c5cfb15c..6dd3002efea 100644 --- a/src/tasks/minions/minigames/titheFarmActivity.ts +++ b/src/tasks/minions/minigames/titheFarmActivity.ts @@ -2,7 +2,7 @@ import { Bank } from 'oldschooljs'; import { Emoji, Events } from '../../../lib/constants'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { TitheFarmActivityTaskOptions } from '../../../lib/types/minions'; +import type { TitheFarmActivityTaskOptions } from '../../../lib/types/minions'; import { roll, skillingPetDropRate } from '../../../lib/util'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { userStatsUpdate } from '../../../mahoji/mahojiSettings'; diff --git a/src/tasks/minions/minigames/toaActivity.ts b/src/tasks/minions/minigames/toaActivity.ts index def40dd5e08..72ede2bb06d 100644 --- a/src/tasks/minions/minigames/toaActivity.ts +++ b/src/tasks/minions/minigames/toaActivity.ts @@ -1,8 +1,8 @@ import { formatOrdinal } from '@oldschoolgg/toolkit'; import { bold } from 'discord.js'; -import { isObject, Time, uniqueArr } from 'e'; +import { Time, isObject, uniqueArr } from 'e'; import { Bank } from 'oldschooljs'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; import { drawChestLootImage } from '../../../lib/bankImage'; import { Emoji, Events, toaPurpleItems } from '../../../lib/constants'; @@ -17,7 +17,7 @@ import { toaOrnamentKits, toaPetTransmogItems } from '../../../lib/simulation/toa'; -import { TOAOptions } from '../../../lib/types/minions'; +import type { TOAOptions } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { assert } from '../../../lib/util/logError'; import resolveItems from '../../../lib/util/resolveItems'; @@ -130,12 +130,12 @@ export const toaTask: MinionTask = { let resultMessage = isSolo ? `${leaderSoloUser}, your minion finished ${quantity === 1 ? 'a' : `${quantity}x`} Tombs of Amascut raid${ quantity > 1 ? 's' : '' - }! Your KC is now ${minigameIncrementResult[0].newScore}.\n` + }! Your KC is now ${minigameIncrementResult[0].newScore}.\n` : `<@${leader}> Your Raid${quantity > 1 ? 's have' : ' has'} finished.\n`; const shouldShowImage = allUsers.length <= 3 && totalLoot.entries().every(i => i[1].length <= 6); - for (let [userID, userData] of raidResults.entries()) { + for (const [userID, userData] of raidResults.entries()) { const { points, deaths, mUser: user } = userData; await userStatsUpdate( user.id, @@ -266,7 +266,7 @@ export const toaTask: MinionTask = { } ], type: 'Tombs of Amascut' - }) + }) : undefined, data, itemsAddedTeamLoot.totalLoot() @@ -286,7 +286,7 @@ export const toaTask: MinionTask = { customTexts: makeCustomTexts(u.id) })), type: 'Tombs of Amascut' - }) + }) : undefined, data, null diff --git a/src/tasks/minions/minigames/tobActivity.ts b/src/tasks/minions/minigames/tobActivity.ts index 8c080390c30..cd5a478d3e6 100644 --- a/src/tasks/minions/minigames/tobActivity.ts +++ b/src/tasks/minions/minigames/tobActivity.ts @@ -12,7 +12,7 @@ import { getMinigameScore, incrementMinigameScore } from '../../../lib/settings/ import { TeamLoot } from '../../../lib/simulation/TeamLoot'; import { TheatreOfBlood } from '../../../lib/simulation/tob'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { TheatreOfBloodTaskOptions } from '../../../lib/types/minions'; +import type { TheatreOfBloodTaskOptions } from '../../../lib/types/minions'; import { convertPercentChance } from '../../../lib/util'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { updateBankSetting } from '../../../lib/util/updateBankSetting'; @@ -120,7 +120,7 @@ export const tobTask: MinionTask = { }) ); - for (let [userID, _userLoot] of Object.entries(result.loot)) { + for (const [userID, _userLoot] of Object.entries(result.loot)) { if (data.solo && userID !== leader) continue; const user = allUsers.find(i => i.id === userID); if (!user) continue; @@ -247,7 +247,7 @@ export const tobTask: MinionTask = { } ], type: 'Theatre of Blood' - }) + }) : undefined, data, totalLoot @@ -267,7 +267,7 @@ export const tobTask: MinionTask = { customTexts: [] })), type: 'Theatre of Blood' - }) + }) : undefined, data, null diff --git a/src/tasks/minions/minigames/trawlerActivity.ts b/src/tasks/minions/minigames/trawlerActivity.ts index 944cab72408..fdf483e40f7 100644 --- a/src/tasks/minions/minigames/trawlerActivity.ts +++ b/src/tasks/minions/minigames/trawlerActivity.ts @@ -5,7 +5,7 @@ import { ArdougneDiary, userhasDiaryTier } from '../../../lib/diaries'; import { incrementMinigameScore } from '../../../lib/settings/settings'; import { fishingTrawlerLoot } from '../../../lib/simulation/fishingTrawler'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { makeBankImage } from '../../../lib/util/makeBankImage'; import { anglerBoostPercent } from '../../../mahoji/mahojiSettings'; diff --git a/src/tasks/minions/minigames/troubleBrewingActivity.ts b/src/tasks/minions/minigames/troubleBrewingActivity.ts index ce15852021a..fd02fe68433 100644 --- a/src/tasks/minions/minigames/troubleBrewingActivity.ts +++ b/src/tasks/minions/minigames/troubleBrewingActivity.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import { incrementMinigameScore } from '../../../lib/settings/minigames'; -import { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; +import type { MinigameActivityTaskOptionsWithNoChanges } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; export const brewingTask: MinionTask = { @@ -10,7 +10,7 @@ export const brewingTask: MinionTask = { const { channelID, quantity, userID } = data; const user = await mUserFetch(userID); await incrementMinigameScore(user.id, 'trouble_brewing', quantity); - let loot = new Bank().add('Pieces of eight', quantity * 100); + const loot = new Bank().add('Pieces of eight', quantity * 100); await transactItems({ userID: user.id, @@ -18,7 +18,7 @@ export const brewingTask: MinionTask = { itemsToAdd: loot }); - let str = `${user}, ${user.minionName} finished doing ${quantity}x games of Trouble Brewing, you received: ${loot}.`; + const str = `${user}, ${user.minionName} finished doing ${quantity}x games of Trouble Brewing, you received: ${loot}.`; handleTripFinish(user, channelID, str, undefined, data, null); } diff --git a/src/tasks/minions/minigames/warriorsGuild/animatedArmourActivity.ts b/src/tasks/minions/minigames/warriorsGuild/animatedArmourActivity.ts index db34d2c95fc..e5f75ab9534 100644 --- a/src/tasks/minions/minigames/warriorsGuild/animatedArmourActivity.ts +++ b/src/tasks/minions/minigames/warriorsGuild/animatedArmourActivity.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import { Emoji } from '../../../../lib/constants'; -import { AnimatedArmourActivityTaskOptions } from '../../../../lib/types/minions'; +import type { AnimatedArmourActivityTaskOptions } from '../../../../lib/types/minions'; import { handleTripFinish } from '../../../../lib/util/handleTripFinish'; import { Armours } from '../../../../mahoji/lib/abstracted_commands/warriorsGuildCommand'; @@ -12,7 +12,7 @@ export const animatedArmorTask: MinionTask = { const user = await mUserFetch(userID); const armour = Armours.find(armour => armour.name === armourID)!; - let baseQuantity = quantity * armour.tokens; + const baseQuantity = quantity * armour.tokens; const loot = new Bank().add('Warrior guild token', baseQuantity); const messages: string[] = []; diff --git a/src/tasks/minions/minigames/warriorsGuild/cyclopsActivity.ts b/src/tasks/minions/minigames/warriorsGuild/cyclopsActivity.ts index 7a9f05f2323..f1f2d77ca0e 100644 --- a/src/tasks/minions/minigames/warriorsGuild/cyclopsActivity.ts +++ b/src/tasks/minions/minigames/warriorsGuild/cyclopsActivity.ts @@ -1,7 +1,7 @@ import { Bank } from 'oldschooljs'; import { CyclopsTable } from '../../../../lib/simulation/cyclops'; -import { ActivityTaskOptionsWithQuantity } from '../../../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity } from '../../../../lib/types/minions'; import { roll } from '../../../../lib/util'; import { handleTripFinish } from '../../../../lib/util/handleTripFinish'; import itemID from '../../../../lib/util/itemID'; @@ -51,7 +51,7 @@ export const cyclopsTask: MinionTask = { const user = await mUserFetch(userID); const userBank = user.bank; - let loot = new Bank(); + const loot = new Bank(); for (let i = 0; i < quantity; i++) { const highestDefenderOwned = defenders.find( @@ -77,7 +77,7 @@ export const cyclopsTask: MinionTask = { }); const { newKC } = await user.incrementKC(cyclopsID, quantity); - let str = `${user}, ${user.minionName} finished killing ${quantity} Cyclops. Your Cyclops KC is now ${newKC}.`; + const str = `${user}, ${user.minionName} finished killing ${quantity} Cyclops. Your Cyclops KC is now ${newKC}.`; const image = await makeBankImage({ bank: itemsAdded, diff --git a/src/tasks/minions/minigames/wintertodtActivity.ts b/src/tasks/minions/minigames/wintertodtActivity.ts index d6e9768c2db..fd2b757961e 100644 --- a/src/tasks/minions/minigames/wintertodtActivity.ts +++ b/src/tasks/minions/minigames/wintertodtActivity.ts @@ -7,7 +7,7 @@ import { getMinigameScore, incrementMinigameScore } from '../../../lib/settings/ import { WintertodtCrate } from '../../../lib/simulation/wintertodt'; import Firemaking from '../../../lib/skilling/skills/firemaking'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity } from '../../../lib/types/minions'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { makeBankImage } from '../../../lib/util/makeBankImage'; import { updateBankSetting } from '../../../lib/util/updateBankSetting'; @@ -18,7 +18,7 @@ export const wintertodtTask: MinionTask = { const { userID, channelID, quantity } = data; const user = await mUserFetch(userID); - let loot = new Bank(); + const loot = new Bank(); let totalPoints = 0; @@ -74,7 +74,7 @@ export const wintertodtTask: MinionTask = { // If they have the entire pyromancer outfit, give an extra 0.5% xp bonus if ( user.gear.skilling.hasEquipped( - Object.keys(Firemaking.pyromancerItems).map(i => parseInt(i)), + Object.keys(Firemaking.pyromancerItems).map(i => Number.parseInt(i)), true ) ) { @@ -84,7 +84,7 @@ export const wintertodtTask: MinionTask = { } else { // For each pyromancer item, check if they have it, give its' XP boost if so. for (const [itemID, bonus] of Object.entries(Firemaking.pyromancerItems)) { - if (user.hasEquipped(parseInt(itemID))) { + if (user.hasEquipped(Number.parseInt(itemID))) { const amountToAdd = Math.floor(fmXpToGive * (bonus / 100)); fmXpToGive += amountToAdd; fmBonusXP += amountToAdd; diff --git a/src/tasks/minions/minigames/zalcanoActivity.ts b/src/tasks/minions/minigames/zalcanoActivity.ts index 4b111f52b95..24c7c26ac92 100644 --- a/src/tasks/minions/minigames/zalcanoActivity.ts +++ b/src/tasks/minions/minigames/zalcanoActivity.ts @@ -3,7 +3,7 @@ import { Bank, Misc } from 'oldschooljs'; import { Events, ZALCANO_ID } from '../../../lib/constants'; import { SkillsEnum } from '../../../lib/skilling/types'; -import { ZalcanoActivityTaskOptions } from '../../../lib/types/minions'; +import type { ZalcanoActivityTaskOptions } from '../../../lib/types/minions'; import { ashSanctifierEffect } from '../../../lib/util/ashSanctifier'; import { handleTripFinish } from '../../../lib/util/handleTripFinish'; import { makeBankImage } from '../../../lib/util/makeBankImage'; diff --git a/src/tasks/minions/miningActivity.ts b/src/tasks/minions/miningActivity.ts index a3dec789849..141ac8fbc69 100644 --- a/src/tasks/minions/miningActivity.ts +++ b/src/tasks/minions/miningActivity.ts @@ -1,11 +1,11 @@ -import { randInt, roll, Time } from 'e'; +import { Time, randInt, roll } from 'e'; import { Bank } from 'oldschooljs'; import { Emoji, Events } from '../../lib/constants'; import addSkillingClueToLoot from '../../lib/minions/functions/addSkillingClueToLoot'; import Mining from '../../lib/skilling/skills/mining'; import { SkillsEnum } from '../../lib/skilling/types'; -import { MiningActivityTaskOptions } from '../../lib/types/minions'; +import type { MiningActivityTaskOptions } from '../../lib/types/minions'; import { skillingPetDropRate } from '../../lib/util'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; @@ -13,7 +13,7 @@ export const miningTask: MinionTask = { type: 'Mining', async run(data: MiningActivityTaskOptions) { const { oreID, userID, channelID, duration, powermine } = data; - let { quantity } = data; + const { quantity } = data; const user = await mUserFetch(userID); const ore = Mining.Ores.find(ore => ore.id === oreID)!; @@ -23,7 +23,7 @@ export const miningTask: MinionTask = { // If they have the entire prospector outfit, give an extra 0.5% xp bonus if ( user.gear.skilling.hasEquipped( - Object.keys(Mining.prospectorItems).map(i => parseInt(i)), + Object.keys(Mining.prospectorItems).map(i => Number.parseInt(i)), true ) ) { @@ -33,7 +33,7 @@ export const miningTask: MinionTask = { } else { // For each prospector item, check if they have it, give its' XP boost if so. for (const [itemID, bonus] of Object.entries(Mining.prospectorItems)) { - if (user.hasEquipped(parseInt(itemID))) { + if (user.hasEquipped(Number.parseInt(itemID))) { const amountToAdd = Math.floor(xpReceived * (bonus / 100)); xpReceived += amountToAdd; bonusXP += amountToAdd; diff --git a/src/tasks/minions/monsterActivity.ts b/src/tasks/minions/monsterActivity.ts index ae8f9fded9c..4006274e767 100644 --- a/src/tasks/minions/monsterActivity.ts +++ b/src/tasks/minions/monsterActivity.ts @@ -1,6 +1,7 @@ -import { Prisma } from '@prisma/client'; -import { deepClone, percentChance, Time } from 'e'; -import { Bank, MonsterKillOptions, Monsters } from 'oldschooljs'; +import type { Prisma } from '@prisma/client'; +import { Time, deepClone, percentChance } from 'e'; +import type { MonsterKillOptions } from 'oldschooljs'; +import { Bank, Monsters } from 'oldschooljs'; import { Emoji } from '../../lib/constants'; import { KourendKebosDiary, userhasDiaryTier } from '../../lib/diaries'; @@ -12,7 +13,7 @@ import { prisma } from '../../lib/settings/prisma'; import { SkillsEnum } from '../../lib/skilling/types'; import { SlayerTaskUnlocksEnum } from '../../lib/slayer/slayerUnlocks'; import { calculateSlayerPoints, isOnSlayerTask } from '../../lib/slayer/slayerUtil'; -import { MonsterActivityTaskOptions } from '../../lib/types/minions'; +import type { MonsterActivityTaskOptions } from '../../lib/types/minions'; import { calculateSimpleMonsterDeathChance, hasSkillReqs, roll } from '../../lib/util'; import { ashSanctifierEffect } from '../../lib/util/ashSanctifier'; import calculateGearLostOnDeathWilderness from '../../lib/util/calculateGearLostOnDeathWilderness'; @@ -106,7 +107,7 @@ export const monsterTask: MinionTask = { gear: userGear, smited: hasPrayerLevel && !protectItem, protectItem: hasPrayerLevel, - after20wilderness: monster.pkBaseDeathChance && monster.pkBaseDeathChance >= 5 ? true : false, + after20wilderness: !!(monster.pkBaseDeathChance && monster.pkBaseDeathChance >= 5), skulled }); @@ -202,7 +203,7 @@ export const monsterTask: MinionTask = { const superiorTable = isOnTaskResult.hasSuperiorsUnlocked && monster.superior ? monster.superior : undefined; const isInCatacombs = (!usingCannon ? monster.existsInCatacombs ?? undefined : undefined) && !isInWilderness; - let hasRingOfWealthI = user.gear.wildy.hasEquipped('Ring of wealth (i)') && isInWilderness; + const hasRingOfWealthI = user.gear.wildy.hasEquipped('Ring of wealth (i)') && isInWilderness; if (hasRingOfWealthI) { messages.push('\nYour clue scroll chance is doubled due to wearing a Ring of Wealth (i).'); } @@ -252,7 +253,7 @@ export const monsterTask: MinionTask = { } if (newSuperiorCount) { // Superior loot and totems if in catacombs - loot.add(superiorTable!.kill(newSuperiorCount)); + loot.add(superiorTable?.kill(newSuperiorCount)); if (isInCatacombs) loot.add('Dark totem base', newSuperiorCount); if (isInWilderness) loot.add("Larran's key", newSuperiorCount); } @@ -323,17 +324,17 @@ export const monsterTask: MinionTask = { const { quantitySlayed } = isOnTaskResult; const effectiveSlayed = monsterID === Monsters.KrilTsutsaroth.id && - isOnTaskResult.currentTask!.monster_id !== Monsters.KrilTsutsaroth.id + isOnTaskResult.currentTask?.monster_id !== Monsters.KrilTsutsaroth.id ? quantitySlayed! * 2 : monsterID === Monsters.Kreearra.id && - isOnTaskResult.currentTask.monster_id !== Monsters.Kreearra.id - ? quantitySlayed * 4 - : monsterID === Monsters.GrotesqueGuardians.id && - user.user.slayer_unlocks.includes(SlayerTaskUnlocksEnum.DoubleTrouble) - ? quantitySlayed * 2 - : quantitySlayed; - - const quantityLeft = Math.max(0, isOnTaskResult.currentTask!.quantity_remaining - effectiveSlayed); + isOnTaskResult.currentTask.monster_id !== Monsters.Kreearra.id + ? quantitySlayed * 4 + : monsterID === Monsters.GrotesqueGuardians.id && + user.user.slayer_unlocks.includes(SlayerTaskUnlocksEnum.DoubleTrouble) + ? quantitySlayed * 2 + : quantitySlayed; + + const quantityLeft = Math.max(0, isOnTaskResult.currentTask?.quantity_remaining - effectiveSlayed); const isUsingKrystilia = isOnTaskResult.slayerMaster.id === 8; thisTripFinishesTask = quantityLeft === 0; @@ -383,12 +384,12 @@ export const monsterTask: MinionTask = { } } else { str += `\nYou killed ${effectiveSlayed}x of your ${ - isOnTaskResult.currentTask!.quantity_remaining + isOnTaskResult.currentTask?.quantity_remaining } remaining kills, you now have ${quantityLeft} kills remaining.`; } await prisma.slayerTask.update({ where: { - id: isOnTaskResult.currentTask!.id + id: isOnTaskResult.currentTask?.id }, data: { quantity_remaining: quantityLeft @@ -439,7 +440,7 @@ export const monsterTask: MinionTask = { title: `Loot From ${quantity} ${monster.name}:`, user, previousCL - }); + }); return handleTripFinish(user, channelID, str, image?.file.attachment, data, itemsAdded, messages); } diff --git a/src/tasks/minions/motherlodeMineActivity.ts b/src/tasks/minions/motherlodeMineActivity.ts index 82beba490b5..e3e2b116cca 100644 --- a/src/tasks/minions/motherlodeMineActivity.ts +++ b/src/tasks/minions/motherlodeMineActivity.ts @@ -5,7 +5,7 @@ import { Emoji, Events } from '../../lib/constants'; import { FaladorDiary, userhasDiaryTier } from '../../lib/diaries'; import Mining from '../../lib/skilling/skills/mining'; import { SkillsEnum } from '../../lib/skilling/types'; -import { MotherlodeMiningActivityTaskOptions } from '../../lib/types/minions'; +import type { MotherlodeMiningActivityTaskOptions } from '../../lib/types/minions'; import { skillingPetDropRate } from '../../lib/util'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; @@ -13,7 +13,7 @@ export const motherlodeMiningTask: MinionTask = { type: 'MotherlodeMining', async run(data: MotherlodeMiningActivityTaskOptions) { const { userID, channelID, duration } = data; - let { quantity } = data; + const { quantity } = data; const user = await mUserFetch(userID); const motherlode = Mining.MotherlodeMine; @@ -24,7 +24,7 @@ export const motherlodeMiningTask: MinionTask = { // If they have the entire prospector outfit, give an extra 0.5% xp bonus if ( user.gear.skilling.hasEquipped( - Object.keys(Mining.prospectorItems).map(i => parseInt(i)), + Object.keys(Mining.prospectorItems).map(i => Number.parseInt(i)), true ) ) { @@ -34,7 +34,7 @@ export const motherlodeMiningTask: MinionTask = { } else { // For each prospector item, check if they have it, give its' XP boost if so. for (const [itemID, bonus] of Object.entries(Mining.prospectorItems)) { - if (user.hasEquipped(parseInt(itemID))) { + if (user.hasEquipped(Number.parseInt(itemID))) { const amountToAdd = Math.floor(xpReceived * (bonus / 100)); xpReceived += amountToAdd; bonusXP += amountToAdd; diff --git a/src/tasks/minions/nexActivity.ts b/src/tasks/minions/nexActivity.ts index c211a340f55..9a2cdb2a8cb 100644 --- a/src/tasks/minions/nexActivity.ts +++ b/src/tasks/minions/nexActivity.ts @@ -3,8 +3,9 @@ import { userMention } from 'discord.js'; import { NEX_ID } from '../../lib/constants'; import { trackLoot } from '../../lib/lootTrack'; -import { handleNexKills, NexContext } from '../../lib/simulation/nex'; -import { NexTaskOptions } from '../../lib/types/minions'; +import type { NexContext } from '../../lib/simulation/nex'; +import { handleNexKills } from '../../lib/simulation/nex'; +import type { NexTaskOptions } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; import { makeBankImage } from '../../lib/util/makeBankImage'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; @@ -81,7 +82,7 @@ export const nexTask: MinionTask = { ? `${allMention} your minion${users.length === 1 ? '' : 's'} died in all kill attempts.` : `${allMention} Your team finished killing ${quantity}x Nex.${ wipedKill ? ` Your team wiped on the ${formatOrdinal(wipedKill)} kill.` : '' - } + } ${loot.formatLoot()}` }, @@ -93,7 +94,7 @@ ${loot.formatLoot()}` user: allMUsers[0], previousCL: undefined }) - ).file.attachment + ).file.attachment : undefined, data, loot.totalLoot() diff --git a/src/tasks/minions/pickpocketActivity.ts b/src/tasks/minions/pickpocketActivity.ts index bdd5f3b72d7..4c0bc35d015 100644 --- a/src/tasks/minions/pickpocketActivity.ts +++ b/src/tasks/minions/pickpocketActivity.ts @@ -2,9 +2,10 @@ import { percentChance, randInt, roll } from 'e'; import { Bank } from 'oldschooljs'; import { Events } from '../../lib/constants'; -import { Stealable, stealables } from '../../lib/skilling/skills/thieving/stealables'; +import type { Stealable } from '../../lib/skilling/skills/thieving/stealables'; +import { stealables } from '../../lib/skilling/skills/thieving/stealables'; import { SkillsEnum } from '../../lib/skilling/types'; -import { PickpocketActivityTaskOptions } from '../../lib/types/minions'; +import type { PickpocketActivityTaskOptions } from '../../lib/types/minions'; import { skillingPetDropRate } from '../../lib/util'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; import { makeBankImage } from '../../lib/util/makeBankImage'; @@ -27,7 +28,7 @@ export function calcLootXPPickpocketing( const diary = hasDiary && npc.customTickRate === undefined ? 1.1 : 1; const thievCape = hasThievingCape && npc.customTickRate === undefined ? 1.1 : 1; - let chanceOfSuccess = (npc.slope! * currentLevel + npc.intercept!) * diary * thievCape; + const chanceOfSuccess = (npc.slope! * currentLevel + npc.intercept!) * diary * thievCape; for (let i = 0; i < quantity; i++) { if (!percentChance(chanceOfSuccess)) { @@ -113,7 +114,7 @@ export const pickpocketTask: MinionTask = { ? '' : `${ 100 - obj.lootPercent! - }% of the loot was dropped in favour of enhancing amount of stalls stolen from.` + }% of the loot was dropped in favour of enhancing amount of stalls stolen from.` }`; if (rogueOutfitBoostActivated) { @@ -140,7 +141,7 @@ export const pickpocketTask: MinionTask = { title: `Loot From ${successfulQuantity} ${obj.name}:`, user, previousCL - }); + }); handleTripFinish(user, channelID, str, image?.file.attachment, data, loot); } diff --git a/src/tasks/minions/smithingActivity.ts b/src/tasks/minions/smithingActivity.ts index d416d329c0b..7b75e41da7a 100644 --- a/src/tasks/minions/smithingActivity.ts +++ b/src/tasks/minions/smithingActivity.ts @@ -24,7 +24,7 @@ export const smithingTask: MinionTask = { [smithedItem.id]: quantity * smithedItem.outputMultiple }); - let str = `${user}, ${user.minionName} finished smithing, you received ${loot}. ${xpRes}`; + const str = `${user}, ${user.minionName} finished smithing, you received ${loot}. ${xpRes}`; await transactItems({ userID: user.id, diff --git a/src/tasks/minions/strongholdOfSecurityActivity.ts b/src/tasks/minions/strongholdOfSecurityActivity.ts index 86bb6551a2b..70d2e30d6ba 100644 --- a/src/tasks/minions/strongholdOfSecurityActivity.ts +++ b/src/tasks/minions/strongholdOfSecurityActivity.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { ActivityTaskOptionsWithNoChanges } from '../../lib/types/minions'; +import type { ActivityTaskOptionsWithNoChanges } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; export const strongholdTask: MinionTask = { diff --git a/src/tasks/minions/tiaraRunecraftActivity.ts b/src/tasks/minions/tiaraRunecraftActivity.ts index 26f9fba1864..4851080ecd2 100644 --- a/src/tasks/minions/tiaraRunecraftActivity.ts +++ b/src/tasks/minions/tiaraRunecraftActivity.ts @@ -2,7 +2,7 @@ import { Bank } from 'oldschooljs'; import Runecraft from '../../lib/skilling/skills/runecraft'; import { SkillsEnum } from '../../lib/skilling/types'; -import { TiaraRunecraftActivityTaskOptions } from '../../lib/types/minions'; +import type { TiaraRunecraftActivityTaskOptions } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; export const tiaraRunecraftTask: MinionTask = { @@ -14,7 +14,7 @@ export const tiaraRunecraftTask: MinionTask = { const tiara = Runecraft.Tiaras.find(_tiara => _tiara.id === tiaraID)!; const xpReceived = tiaraQuantity * tiara.xp; - let xpRes = `\n${await user.addXP({ + const xpRes = `\n${await user.addXP({ skillName: SkillsEnum.Runecraft, amount: xpReceived, duration diff --git a/src/tasks/minions/tokkulShopActivity.ts b/src/tasks/minions/tokkulShopActivity.ts index d9e19207d5b..72b71438693 100644 --- a/src/tasks/minions/tokkulShopActivity.ts +++ b/src/tasks/minions/tokkulShopActivity.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { TokkulShopOptions } from '../../lib/types/minions'; +import type { TokkulShopOptions } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; import { updateBankSetting } from '../../lib/util/updateBankSetting'; diff --git a/src/tasks/minions/underwaterActivity.ts b/src/tasks/minions/underwaterActivity.ts index 6f5e5f583b6..e898ad5c156 100644 --- a/src/tasks/minions/underwaterActivity.ts +++ b/src/tasks/minions/underwaterActivity.ts @@ -2,7 +2,7 @@ import { percentChance } from 'e'; import { Bank, LootTable } from 'oldschooljs'; import { SkillsEnum } from '../../lib/skilling/types'; -import { UnderwaterAgilityThievingTaskOptions } from '../../lib/types/minions'; +import type { UnderwaterAgilityThievingTaskOptions } from '../../lib/types/minions'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; // Bonus loot from clams and chests, TODO: check wiki in future for more accurate rates @@ -16,14 +16,14 @@ const clamChestTable = new LootTable() export const underwaterAgilityThievingTask: MinionTask = { type: 'UnderwaterAgilityThieving', async run(data: UnderwaterAgilityThievingTaskOptions) { - let { quantity, userID, channelID, duration, trainingSkill } = data; + const { quantity, userID, channelID, duration, trainingSkill } = data; const user = await mUserFetch(userID); const currentThievingLevel = user.skillLevel(SkillsEnum.Thieving); const currentAgilityLevel = user.skillLevel(SkillsEnum.Agility); let successful = 0; // Search clam/chest until it becomes inactive chance - let chanceOfSuccess = 0.043_88 * currentThievingLevel + 11.68; + const chanceOfSuccess = 0.043_88 * currentThievingLevel + 11.68; for (let i = 0; i < quantity; i++) { while (percentChance(chanceOfSuccess)) { diff --git a/src/tasks/minions/volcanicMineActivity.ts b/src/tasks/minions/volcanicMineActivity.ts index 4784f4196c5..6695e127d54 100644 --- a/src/tasks/minions/volcanicMineActivity.ts +++ b/src/tasks/minions/volcanicMineActivity.ts @@ -1,10 +1,10 @@ -import { randFloat, randInt, roll, Time } from 'e'; +import { Time, randFloat, randInt, roll } from 'e'; import { Bank, LootTable } from 'oldschooljs'; import { Emoji, Events } from '../../lib/constants'; import { incrementMinigameScore } from '../../lib/settings/settings'; import { SkillsEnum } from '../../lib/skilling/types'; -import { ActivityTaskOptionsWithQuantity } from '../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity } from '../../lib/types/minions'; import { skillingPetDropRate } from '../../lib/util'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; import { VolcanicMineGameTime } from '../../mahoji/lib/abstracted_commands/volcanicMineCommand'; diff --git a/src/tasks/minions/wealthChargingActivity.ts b/src/tasks/minions/wealthChargingActivity.ts index 822fb410b71..e176e92ba8c 100644 --- a/src/tasks/minions/wealthChargingActivity.ts +++ b/src/tasks/minions/wealthChargingActivity.ts @@ -1,6 +1,6 @@ import { Bank } from 'oldschooljs'; -import { ActivityTaskOptionsWithQuantity } from '../../lib/types/minions'; +import type { ActivityTaskOptionsWithQuantity } from '../../lib/types/minions'; import { roll } from '../../lib/util'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; import { wealthInventorySize } from '../../mahoji/lib/abstracted_commands/chargeWealthCommand'; @@ -11,7 +11,7 @@ export const wealthChargeTask: MinionTask = { const { quantity, userID, channelID } = data; const user = await mUserFetch(userID); let deaths = 0; - let loot = new Bank(); + const loot = new Bank(); for (let i = 0; i < quantity; i++) { if (roll(9)) { deaths++; diff --git a/src/tasks/minions/woodcuttingActivity.ts b/src/tasks/minions/woodcuttingActivity.ts index 167694372d0..3a7314ae46d 100644 --- a/src/tasks/minions/woodcuttingActivity.ts +++ b/src/tasks/minions/woodcuttingActivity.ts @@ -1,7 +1,8 @@ -import { percentChance, randInt, Time } from 'e'; +import { Time, objectEntries, percentChance, randInt } from 'e'; import { Bank } from 'oldschooljs'; -import { Emoji, Events, TwitcherGloves } from '../../lib/constants'; +import type { TwitcherGloves } from '../../lib/constants'; +import { Emoji, Events } from '../../lib/constants'; import { MediumSeedPackTable } from '../../lib/data/seedPackTables'; import addSkillingClueToLoot from '../../lib/minions/functions/addSkillingClueToLoot'; import { eggNest } from '../../lib/simulation/birdsNest'; @@ -9,15 +10,15 @@ import { soteSkillRequirements } from '../../lib/skilling/functions/questRequire import { ForestryEvents, LeafTable } from '../../lib/skilling/skills/woodcutting/forestry'; import Woodcutting from '../../lib/skilling/skills/woodcutting/woodcutting'; import { SkillsEnum } from '../../lib/skilling/types'; -import { WoodcuttingActivityTaskOptions } from '../../lib/types/minions'; +import type { WoodcuttingActivityTaskOptions } from '../../lib/types/minions'; import { perTimeUnitChance, roll, skillingPetDropRate } from '../../lib/util'; import { handleTripFinish } from '../../lib/util/handleTripFinish'; import resolveItems from '../../lib/util/resolveItems'; import { userStatsBankUpdate } from '../../mahoji/mahojiSettings'; async function handleForestry({ user, duration, loot }: { user: MUser; duration: number; loot: Bank }) { - let eventCounts: { [key: number]: number } = {}; - let eventXP = {} as { [key in SkillsEnum]: number }; + const eventCounts: { [key: number]: number } = {}; + const eventXP = {} as { [key in SkillsEnum]: number }; ForestryEvents.forEach(event => { eventCounts[event.id] = 0; eventXP[event.uniqueXP] = 0; @@ -123,25 +124,20 @@ async function handleForestry({ user, duration, loot }: { user: MUser; duration: }); let totalEvents = 0; - for (const event in eventCounts) { - if (eventCounts.hasOwnProperty(event)) { - const count = eventCounts[event]; + for (const [event, count] of objectEntries(eventCounts)) { + if (event && count && count > 0) { totalEvents += count; - await userStatsBankUpdate( - user.id, - 'forestry_event_completions_bank', - new Bank().add(parseInt(event), count) - ); + await userStatsBankUpdate(user.id, 'forestry_event_completions_bank', new Bank().add(Number(event), count)); } } // Give user xp from events let xpRes = ''; - for (const skill in eventXP) { - if (eventXP.hasOwnProperty(skill)) { + for (const [key, val] of objectEntries(eventXP)) { + if (key && val && val > 0) { xpRes += await user.addXP({ - skillName: skill as SkillsEnum, - amount: Math.ceil(eventXP[skill as SkillsEnum]), + skillName: key, + amount: Math.ceil(val), minimal: true, source: 'ForestryEvents' }); @@ -151,8 +147,8 @@ async function handleForestry({ user, duration, loot }: { user: MUser; duration: // Generate forestry message const completedEvents = Object.entries(eventCounts) .map(([eventId, count]) => { - const event = ForestryEvents.find(e => e.id === parseInt(eventId)); - return count > 0 ? `${count} ${event!.name}` : null; + const event = ForestryEvents.find(e => e.id === Number.parseInt(eventId)); + return count > 0 ? `${count} ${event?.name}` : null; }) .filter(Boolean) .join(' & '); @@ -191,16 +187,16 @@ export const woodcuttingTask: MinionTask = { user.bank.has(['Woodcutting cape', 'Cape pouch']) && userWcLevel >= 99); - let strungRabbitFoot = user.hasEquipped('Strung rabbit foot'); - let twitchersEquipped = user.hasEquipped("twitcher's gloves"); + const strungRabbitFoot = user.hasEquipped('Strung rabbit foot'); + const twitchersEquipped = user.hasEquipped("twitcher's gloves"); let twitcherSetting: TwitcherGloves | undefined = 'egg'; let xpReceived = quantity * log.xp; let bonusXP = 0; let rationUsed = 0; let lostLogs = 0; - let loot = new Bank(); - let itemsToRemove = new Bank(); - let priffUnlocked = user.hasSkillReqs(soteSkillRequirements) && user.QP >= 150; + const loot = new Bank(); + const itemsToRemove = new Bank(); + const priffUnlocked = user.hasSkillReqs(soteSkillRequirements) && user.QP >= 150; // Felling axe +10% xp bonus & 20% logs lost if (user.gear.skilling.hasEquipped('Bronze felling axe')) { @@ -219,7 +215,7 @@ export const woodcuttingTask: MinionTask = { // If they have the entire lumberjack outfit, give an extra 0.5% xp bonus if ( user.gear.skilling.hasEquipped( - Object.keys(Woodcutting.lumberjackItems).map(i => parseInt(i)), + Object.keys(Woodcutting.lumberjackItems).map(i => Number.parseInt(i)), true ) ) { @@ -229,7 +225,7 @@ export const woodcuttingTask: MinionTask = { } else { // For each lumberjack item, check if they have it, give its XP boost if so for (const [itemID, bonus] of Object.entries(Woodcutting.lumberjackItems)) { - if (user.gear.skilling.hasEquipped([parseInt(itemID)], false)) { + if (user.gear.skilling.hasEquipped([Number.parseInt(itemID)], false)) { const amountToAdd = Math.floor(xpReceived * (bonus / 100)); xpReceived += amountToAdd; bonusXP += amountToAdd; @@ -343,7 +339,7 @@ export const woodcuttingTask: MinionTask = { lostLogs > 0 && !powerchopping ? `You lost ${ log.lootTable ? `${lostLogs}x ${log.name} loot rolls` : `${lostLogs}x ${log.name}` - } due to using a felling axe.` + } due to using a felling axe.` : '' }`; diff --git a/tests/integration/MUser.test.ts b/tests/integration/MUser.test.ts index 3f50337d1ad..937cdd2abb5 100644 --- a/tests/integration/MUser.test.ts +++ b/tests/integration/MUser.test.ts @@ -1,7 +1,7 @@ import { activity_type_enum } from '@prisma/client'; -import { objectEntries, randArrItem, randInt, Time } from 'e'; +import { Time, objectEntries, randArrItem, randInt } from 'e'; import { Bank } from 'oldschooljs'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; import { convertLVLtoXP } from 'oldschooljs/dist/util'; import { describe, expect, test } from 'vitest'; @@ -110,11 +110,11 @@ describe('MUser', () => { test('skillsAsLevels/skillsAsXP', async () => { const user = await createTestUser(); for (const [key, val] of objectEntries(user.skillsAsLevels)) { - let expectedVal = key === 'hitpoints' ? 10 : 1; + const expectedVal = key === 'hitpoints' ? 10 : 1; expect(val).toEqual(expectedVal); } for (const [key, val] of objectEntries(user.skillsAsXP)) { - let expectedVal = key === 'hitpoints' ? convertLVLtoXP(10) : convertLVLtoXP(1); + const expectedVal = key === 'hitpoints' ? convertLVLtoXP(10) : convertLVLtoXP(1); expect(val).toEqual(expectedVal); } await user.addXP({ skillName: SkillsEnum.Agility, amount: convertLVLtoXP(50) }); diff --git a/tests/integration/allCommandsBase.test.ts b/tests/integration/allCommandsBase.test.ts index 3095093cbd5..ee64e77da17 100644 --- a/tests/integration/allCommandsBase.test.ts +++ b/tests/integration/allCommandsBase.test.ts @@ -1,9 +1,9 @@ import { join } from 'node:path'; import { ApplicationCommandOptionType } from 'discord.js'; -import { randArrItem, randInt, shuffleArr, Time } from 'e'; +import { Time, randArrItem, randInt, shuffleArr } from 'e'; import { Store } from 'mahoji/dist/lib/structures/Store'; -import { CommandOption } from 'mahoji/dist/lib/types'; +import type { CommandOption } from 'mahoji/dist/lib/types'; import { isValidCommand } from 'mahoji/dist/lib/util'; import { Bank, Items } from 'oldschooljs'; import { expect, test, vi } from 'vitest'; @@ -39,7 +39,6 @@ import { fishCommand } from '../../src/mahoji/commands/fish'; import { fletchCommand } from '../../src/mahoji/commands/fletch'; import { gambleCommand } from '../../src/mahoji/commands/gamble'; import { gearCommand } from '../../src/mahoji/commands/gear'; -import { gearPresetsCommand } from '../../src/mahoji/commands/gearpresets'; import { giftCommand } from '../../src/mahoji/commands/gift'; import { giveawayCommand } from '../../src/mahoji/commands/giveaway'; import { gpCommand } from '../../src/mahoji/commands/gp'; @@ -82,11 +81,12 @@ import { tradeCommand } from '../../src/mahoji/commands/trade'; import { triviaCommand } from '../../src/mahoji/commands/trivia'; import { mahojiUseCommand } from '../../src/mahoji/commands/use'; import { randomMock } from './setup'; -import { createTestUser, mockClient, TestUser } from './util'; +import type { TestUser } from './util'; +import { createTestUser, mockClient } from './util'; type CommandInput = Record; async function generateCommandInputs(user: TestUser, options: readonly CommandOption[]): Promise { - let results: CommandInput[] = []; + const results: CommandInput[] = []; const allPossibleOptions: Record = {}; for (const option of options) { @@ -150,7 +150,7 @@ async function generateCommandInputs(user: TestUser, options: readonly CommandOp const sorted = Object.values(allPossibleOptions).sort((a, b) => b.length - a.length); const longestOptions = sorted[0]?.length; for (let i = 0; i < longestOptions; i++) { - let obj: Record = {}; + const obj: Record = {}; for (const [key, val] of Object.entries(allPossibleOptions)) { obj[key] = val[i] ?? randArrItem(val); } @@ -202,7 +202,8 @@ test( 'trivia', 'ge', 'rp', - 'cl' + 'cl', + 'gearpresets' ]; const cmds = [ adminCommand, @@ -235,7 +236,6 @@ test( fletchCommand, gambleCommand, gearCommand, - gearPresetsCommand, giveawayCommand, helpCommand, huntCommand, diff --git a/tests/integration/commands/ironman.test.ts b/tests/integration/commands/ironman.test.ts index 4ebac861fa0..f2c440c564f 100644 --- a/tests/integration/commands/ironman.test.ts +++ b/tests/integration/commands/ironman.test.ts @@ -1,5 +1,5 @@ import { miniID } from '@oldschoolgg/toolkit'; -import { Prisma } from '@prisma/client'; +import type { Prisma } from '@prisma/client'; import { Time } from 'e'; import { Bank } from 'oldschooljs'; import { describe, expect, test } from 'vitest'; diff --git a/tests/integration/commands/sacrifice.test.ts b/tests/integration/commands/sacrifice.test.ts index f32ac4464a2..2ac09a49c75 100644 --- a/tests/integration/commands/sacrifice.test.ts +++ b/tests/integration/commands/sacrifice.test.ts @@ -1,5 +1,5 @@ import { Bank } from 'oldschooljs'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; import { describe, expect, test } from 'vitest'; import { Emoji } from '../../../src/lib/constants'; @@ -16,10 +16,7 @@ describe('Sacrifice Command', async () => { await user.runCommand(sacrificeCommand, { items: '1 trout, 10 coal' }); const result = await user.runCommand(sacrificeCommand, {}); expect(result).toEqual( - `${Emoji.Incinerator} **Your Sacrifice Stats** ${Emoji.Incinerator}\n\n` + - `**Current Minion Icon:** ${Emoji.Minion}\n` + - '**Sacrificed Value:** 1,590 GP\n' + - '**Unique Items Sacrificed:** 2 items' + `${Emoji.Incinerator} **Your Sacrifice Stats** ${Emoji.Incinerator}\n\n**Current Minion Icon:** ${Emoji.Minion}\n**Sacrificed Value:** 1,590 GP\n**Unique Items Sacrificed:** 2 items` ); }); diff --git a/tests/integration/gearFixing.test.ts b/tests/integration/gearFixing.test.ts index 9534ef0a4b2..cea0975c4a4 100644 --- a/tests/integration/gearFixing.test.ts +++ b/tests/integration/gearFixing.test.ts @@ -1,8 +1,8 @@ -import deepEqual from 'deep-equal'; +import deepEqual from 'fast-deep-equal'; import { Bank } from 'oldschooljs'; import { describe, test } from 'vitest'; -import { defaultGear, Gear } from '../../src/lib/structures/Gear'; +import { Gear, defaultGear } from '../../src/lib/structures/Gear'; import { assert } from '../../src/lib/util'; import itemID from '../../src/lib/util/itemID'; import { createTestUser, mockClient } from './util'; diff --git a/tests/integration/grandExchange.test.ts b/tests/integration/grandExchange.test.ts index 243f4a74496..cea422a8e41 100644 --- a/tests/integration/grandExchange.test.ts +++ b/tests/integration/grandExchange.test.ts @@ -1,4 +1,4 @@ -import { calcPercentOfNum, randArrItem, randInt, shuffleArr, Time } from 'e'; +import { Time, calcPercentOfNum, randArrItem, randInt, shuffleArr } from 'e'; import { Bank } from 'oldschooljs'; import PQueue from 'p-queue'; import { describe, expect, test } from 'vitest'; @@ -9,7 +9,8 @@ import { assert } from '../../src/lib/util'; import resolveItems from '../../src/lib/util/resolveItems'; import { geCommand } from '../../src/mahoji/commands/ge'; import { cancelUsersListings } from '../../src/mahoji/lib/abstracted_commands/cancelGEListingCommand'; -import { createTestUser, mockClient, TestUser } from './util'; +import type { TestUser } from './util'; +import { createTestUser, mockClient } from './util'; const quantities = [-1, 0, 100_000_000_000_000_000, 1, 2, 38, 1_000_000_000_000, 500, '5*5']; const prices = [ @@ -38,7 +39,7 @@ const sampleBank = new Bank() describe('Grand Exchange', async () => { const itemPool = resolveItems(['Egg', 'Trout', 'Coal']); - GrandExchange.calculateSlotsOfUser = async () => ({ slots: 500 } as any); + GrandExchange.calculateSlotsOfUser = async () => ({ slots: 500 }) as any; await mockClient(); async function waitForGEToBeEmpty() { @@ -49,6 +50,7 @@ describe('Grand Exchange', async () => { test( 'Fuzz', async () => { + // biome-ignore lint/suspicious/noSelfCompare: assert(randInt(1, 100_000) !== randInt(1, 100_000)); await GrandExchange.totalReset(); @@ -56,10 +58,10 @@ describe('Grand Exchange', async () => { const currentOwnedBank = await GrandExchange.fetchOwnedBank(); expect(currentOwnedBank.toString()).toEqual(new Bank().toString()); - let amountOfUsers = randInt(100, 200); + const amountOfUsers = randInt(100, 200); const totalExpectedBank = sampleBank.clone().multiply(amountOfUsers); - let users: TestUser[] = []; + const users: TestUser[] = []; for (let i = 0; i < amountOfUsers; i++) { const user = await createTestUser(); @@ -73,8 +75,8 @@ describe('Grand Exchange', async () => { for (const user of shuffleArr(users)) { commandPromises.add(async () => { const method = randArrItem(['buy', 'sell']); - let quantity = randArrItem(quantities); - let price = randArrItem(prices); + const quantity = randArrItem(quantities); + const price = randArrItem(prices); for (const item of itemPool) { await user.runCommand(geCommand, { [method]: { diff --git a/tests/integration/migrateUser.test.ts b/tests/integration/migrateUser.test.ts index dc56788b15b..4387b460bbf 100644 --- a/tests/integration/migrateUser.test.ts +++ b/tests/integration/migrateUser.test.ts @@ -1,6 +1,5 @@ -import { +import type { Activity, - activity_type_enum, Bingo, BingoParticipant, BuyCommandTransaction, @@ -19,22 +18,24 @@ import { ReclaimableItem, SlayerTask, UserStats, - XPGain + XPGain, + activity_type_enum } from '@prisma/client'; -import { deepClone, randArrItem, randInt, shuffleArr, sumArr, Time } from 'e'; +import { Time, deepClone, randArrItem, randInt, shuffleArr, sumArr } from 'e'; import { Bank } from 'oldschooljs'; -import { ItemBank } from 'oldschooljs/dist/meta/types'; +import type { ItemBank } from 'oldschooljs/dist/meta/types'; import { describe, expect, test, vi } from 'vitest'; +import { processPendingActivities } from '../../src/lib/Task'; import { BitField } from '../../src/lib/constants'; -import { GearSetupType, UserFullGearSetup } from '../../src/lib/gear/types'; +import type { GearSetupType, UserFullGearSetup } from '../../src/lib/gear/types'; import { trackLoot } from '../../src/lib/lootTrack'; -import { incrementMinigameScore, MinigameName } from '../../src/lib/settings/minigames'; -import { SkillsEnum } from '../../src/lib/skilling/types'; +import type { MinigameName } from '../../src/lib/settings/minigames'; +import { incrementMinigameScore } from '../../src/lib/settings/minigames'; +import type { SkillsEnum } from '../../src/lib/skilling/types'; import { slayerMasters } from '../../src/lib/slayer/slayerMasters'; import { assignNewSlayerTask } from '../../src/lib/slayer/slayerUtil'; -import { processPendingActivities } from '../../src/lib/Task'; -import { Skills } from '../../src/lib/types'; +import type { Skills } from '../../src/lib/types'; import { isGroupActivity } from '../../src/lib/util'; import { gearEquipMultiImpl } from '../../src/lib/util/equipMulti'; import { findPlant } from '../../src/lib/util/farmingHelpers'; @@ -53,11 +54,12 @@ import { stashUnitFillAllCommand } from '../../src/mahoji/lib/abstracted_commands/stashUnitsCommand'; import { syncNewUserUsername } from '../../src/mahoji/lib/preCommand'; -import { OSBMahojiCommand } from '../../src/mahoji/lib/util'; +import type { OSBMahojiCommand } from '../../src/mahoji/lib/util'; import { updateClientGPTrackSetting, userStatsUpdate } from '../../src/mahoji/mahojiSettings'; import { calculateResultOfLMSGames, getUsersLMSStats } from '../../src/tasks/minions/minigames/lmsActivity'; -import { createTestUser, mockClient, mockedId, TestUser } from './util'; -import { BotItemSell, GEListing, StashUnit } from '.prisma/client'; +import type { TestUser } from './util'; +import { createTestUser, mockClient, mockedId } from './util'; +import type { BotItemSell, GEListing, StashUnit } from '.prisma/client'; interface TestCommand { name: string; @@ -67,7 +69,7 @@ interface TestCommand { } class UserData { // Class Data - private loaded: boolean = false; + private loaded = false; private mUser: MUser | null = null; // Robochimp: @@ -259,7 +261,7 @@ class UserData { this.loaded = true; } - equals(target: UserData, ignoreRoboChimp: boolean = false): { result: boolean; errors: string[] } { + equals(target: UserData, ignoreRoboChimp = false): { result: boolean; errors: string[] } { const errors: string[] = []; if (!this.loaded || !target.loaded) { errors.push('Both UserData object must be loaded. Try .sync()'); @@ -1114,7 +1116,7 @@ async function runAllTestCommandsOnUser(user: TestUser) { return user; } -async function runRandomTestCommandsOnUser(user: TestUser, numCommands: number = 6, forceRoboChimp: boolean = false) { +async function runRandomTestCommandsOnUser(user: TestUser, numCommands = 6, forceRoboChimp = false) { const commandHistory: string[] = []; const priorityCommands = allTableCommands.filter(c => c.priority); const otherCommands = allTableCommands.filter(c => !c.priority); diff --git a/tests/integration/misc.test.ts b/tests/integration/misc.test.ts index b1aaee16757..2d113c1b154 100644 --- a/tests/integration/misc.test.ts +++ b/tests/integration/misc.test.ts @@ -1,4 +1,4 @@ -import { UserEvent } from '@prisma/client'; +import type { UserEvent } from '@prisma/client'; import { randArrItem } from 'e'; import { describe, expect, test } from 'vitest'; diff --git a/tests/integration/paymentConflict.test.ts b/tests/integration/paymentConflict.test.ts index d44d5ba93e4..e3521972bbe 100644 --- a/tests/integration/paymentConflict.test.ts +++ b/tests/integration/paymentConflict.test.ts @@ -1,9 +1,10 @@ -import { randArrItem, randInt, roll, Time } from 'e'; +import { Time, randArrItem, randInt, roll } from 'e'; import { Bank } from 'oldschooljs'; import { describe, expect, test } from 'vitest'; import { payCommand } from '../../src/mahoji/commands/pay'; -import { createTestUser, mockClient, TestUser } from './util'; +import type { TestUser } from './util'; +import { createTestUser, mockClient } from './util'; describe('Payment conflicts', async () => { const payerCount = 50; diff --git a/tests/integration/rolesTask.test.ts b/tests/integration/rolesTask.test.ts index 5ba57e19b23..abbdcc58dc1 100644 --- a/tests/integration/rolesTask.test.ts +++ b/tests/integration/rolesTask.test.ts @@ -3,7 +3,8 @@ import { Bank } from 'oldschooljs'; import { describe, expect, test } from 'vitest'; import { runRolesTask } from '../../src/lib/rolesTask'; -import { MinigameName, Minigames } from '../../src/lib/settings/minigames'; +import type { MinigameName } from '../../src/lib/settings/minigames'; +import { Minigames } from '../../src/lib/settings/minigames'; import { cryptoRand } from '../../src/lib/util'; import { userStatsBankUpdate } from '../../src/mahoji/mahojiSettings'; import { createTestUser, mockedId } from './util'; diff --git a/tests/integration/tradeTransaction.test.ts b/tests/integration/tradeTransaction.test.ts index deeb8c24672..b684d57c154 100644 --- a/tests/integration/tradeTransaction.test.ts +++ b/tests/integration/tradeTransaction.test.ts @@ -1,4 +1,4 @@ -import { Prisma } from '@prisma/client'; +import type { Prisma } from '@prisma/client'; import { Bank } from 'oldschooljs'; import { describe, expect, test } from 'vitest'; @@ -6,10 +6,11 @@ import { tradePlayerItems } from '../../src/lib/util/tradePlayerItems'; import { mockedId } from './util'; describe('Transactionalized Trade Test', async () => { - async function createUserWithBank(bank: Bank, userData: Partial = {}) { + async function createUserWithBank(_bank: Bank, userData: Partial = {}) { const userId = mockedId(); + const bank = _bank.clone(); const GP = bank.amount('Coins'); - delete bank.bank[995]; + bank.remove('Coins', GP); await global.prisma!.user.create({ data: { id: userId, GP, bank: bank.bank, ...userData } @@ -105,46 +106,71 @@ describe('Transactionalized Trade Test', async () => { }); test('Test not enough GP trade...', async () => { - const cyrStartingBank = new Bank().add('Coins', 1_000_000).add('Twisted bow', 2).add('Dragon arrow', 1000); + const cyrStartingBank = new Bank() + .add('Coins', 1_000_000) + .add('Twisted bow', 2) + .add('Dragon arrow', 1000) + .freeze(); const cyr = await createUserWithBank(cyrStartingBank); - const magnaStartingBank = new Bank().add('Coins', 20_000_000).add('Cannonball', 10_000).add('Feather', 500); + const magnaStartingBank = new Bank() + .add('Coins', 20_000_000) + .add('Cannonball', 10_000) + .add('Feather', 500) + .freeze(); const magna = await createUserWithBank(magnaStartingBank); const uCyr = await mUserFetch(cyr); const uMagna = await mUserFetch(magna); - const tradeFromCyr = new Bank().add('Coins', 2_000_000).add('Twisted bow', 1); - const tradeFromMagna = new Bank().add('Coins', 2_000_000).add('Feather', 500).add('Cannonball', 2000); + expect(uCyr.GP).toBe(1_000_000); + expect(uMagna.GP).toBe(20_000_000); + + const tradeFromCyr = new Bank().add('Coins', 2_000_000).add('Twisted bow', 1).freeze(); + const tradeFromMagna = new Bank().add('Coins', 2_000_000).add('Feather', 500).add('Cannonball', 2000).freeze(); const result = await tradePlayerItems(uCyr, uMagna, tradeFromCyr, tradeFromMagna); const expectedResult = { success: false, message: `<@${cyr}> doesn't own all items.` }; + expect(uCyr.GP).toBe(1_000_000); + expect(uMagna.GP).toBe(20_000_000); + + await uCyr.sync(); expect(result).toMatchObject(expectedResult); - expect(uCyr.bank.equals(cyrStartingBank)).toBe(true); - expect(uMagna.bank.equals(magnaStartingBank)).toBe(true); + console.log(cyrStartingBank.bank); + expect(uCyr.bankWithGP.toString()).toEqual(cyrStartingBank.toString()); + expect(uCyr.bankWithGP.equals(cyrStartingBank)).toBe(true); + expect(uMagna.bankWithGP.equals(magnaStartingBank)).toBe(true); }); test('Test not enough items trade...', async () => { - const cyrStartingBank = new Bank().add('Coins', 1_000_000).add('Twisted bow', 2).add('Dragon arrow', 1000); + const cyrStartingBank = new Bank() + .add('Coins', 1_000_000) + .add('Twisted bow', 2) + .add('Dragon arrow', 1000) + .freeze(); const cyr = await createUserWithBank(cyrStartingBank); - const magnaStartingBank = new Bank().add('Coins', 20_000_000).add('Cannonball', 10_000).add('Feather', 500); + const magnaStartingBank = new Bank() + .add('Coins', 20_000_000) + .add('Cannonball', 10_000) + .add('Feather', 500) + .freeze(); const magna = await createUserWithBank(magnaStartingBank); const uCyr = await mUserFetch(cyr); const uMagna = await mUserFetch(magna); - const tradeFromCyr = new Bank().add('Coins', 1_000_000).add('Twisted bow', 1); - const tradeFromMagna = new Bank().add('Coins', 2_000_000).add('Feather', 5000).add('Cannonball', 2000); + const tradeFromCyr = new Bank().add('Coins', 1_000_000).add('Twisted bow', 1).freeze(); + const tradeFromMagna = new Bank().add('Coins', 2_000_000).add('Feather', 5000).add('Cannonball', 2000).freeze(); const result = await tradePlayerItems(uCyr, uMagna, tradeFromCyr, tradeFromMagna); const expectedResult = { success: false, message: `<@${magna}> doesn't own all items.` }; expect(result).toMatchObject(expectedResult); - expect(uCyr.bank.equals(cyrStartingBank)).toBe(true); - expect(uMagna.bank.equals(magnaStartingBank)).toBe(true); + expect(uCyr.bankWithGP.equals(cyrStartingBank)).toBe(true); + expect(uMagna.bankWithGP.equals(magnaStartingBank)).toBe(true); }); }); diff --git a/tests/integration/trading.test.ts b/tests/integration/trading.test.ts index 31b9d56d2b6..beadfc25cf2 100644 --- a/tests/integration/trading.test.ts +++ b/tests/integration/trading.test.ts @@ -3,7 +3,8 @@ import { Bank } from 'oldschooljs'; import { expect, test } from 'vitest'; import { tradeCommand } from '../../src/mahoji/commands/trade'; -import { createTestUser, mockClient, TestUser } from './util'; +import type { TestUser } from './util'; +import { createTestUser, mockClient } from './util'; test('Trade consistency', async () => { await mockClient(); diff --git a/tests/integration/util.ts b/tests/integration/util.ts index d28df44a5fb..55b18d18b8e 100644 --- a/tests/integration/util.ts +++ b/tests/integration/util.ts @@ -1,17 +1,17 @@ -import { Prisma } from '@prisma/client'; +import type { Prisma } from '@prisma/client'; import { randInt, shuffleArr, uniqueArr } from 'e'; -import { CommandRunOptions } from 'mahoji'; +import type { CommandRunOptions } from 'mahoji'; import { Bank } from 'oldschooljs'; -import { globalConfig } from '../../src/lib/constants'; import { MUserClass } from '../../src/lib/MUser'; import { processPendingActivities } from '../../src/lib/Task'; -import { ItemBank } from '../../src/lib/types'; +import { globalConfig } from '../../src/lib/constants'; +import type { ItemBank } from '../../src/lib/types'; import { cryptoRand } from '../../src/lib/util'; import { giveMaxStats } from '../../src/mahoji/commands/testpotato'; import { ironmanCommand } from '../../src/mahoji/lib/abstracted_commands/ironmanCommand'; -import { OSBMahojiCommand } from '../../src/mahoji/lib/util'; -import { ClientStorage, User, UserStats } from '.prisma/client'; +import type { OSBMahojiCommand } from '../../src/mahoji/lib/util'; +import type { ClientStorage, User, UserStats } from '.prisma/client'; export const commandRunOptions = (userID: string): Omit => ({ userID, diff --git a/tests/unit/Gear.test.ts b/tests/unit/Gear.test.ts index d7b08e19705..f7831f6dc6c 100644 --- a/tests/unit/Gear.test.ts +++ b/tests/unit/Gear.test.ts @@ -1,10 +1,10 @@ -import { GearPreset } from '@prisma/client'; +import type { GearPreset } from '@prisma/client'; import { Bank } from 'oldschooljs'; import { itemID } from 'oldschooljs/dist/util'; import { describe, expect, it, test } from 'vitest'; import { GearStat } from '../../src/lib/gear/types'; -import { constructGearSetup, Gear } from '../../src/lib/structures/Gear'; +import { Gear, constructGearSetup } from '../../src/lib/structures/Gear'; import { itemNameFromID } from '../../src/lib/util'; import getOSItem from '../../src/lib/util/getOSItem'; diff --git a/tests/unit/GeneralBank.test.ts b/tests/unit/GeneralBank.test.ts index 7a075784efa..79334cd3981 100644 --- a/tests/unit/GeneralBank.test.ts +++ b/tests/unit/GeneralBank.test.ts @@ -1,11 +1,10 @@ +import Decimal from 'decimal.js'; import { beforeEach, describe, expect, it, test } from 'vitest'; import { GeneralBank } from '../../src/lib/structures/GeneralBank'; describe('GeneralBank', () => { - // eslint-disable-next-line @typescript-eslint/init-declarations let bank: GeneralBank; - // eslint-disable-next-line @typescript-eslint/init-declarations let validator: (key: string, value: number, bank: Record) => void; beforeEach(() => { @@ -61,7 +60,6 @@ describe('GeneralBank', () => { }); describe('GeneralBank 2', () => { - // eslint-disable-next-line @typescript-eslint/init-declarations let bank: GeneralBank; beforeEach(() => { @@ -121,7 +119,6 @@ describe('GeneralBank 2', () => { }); describe('Bank with allowedKeys', () => { - // eslint-disable-next-line @typescript-eslint/init-declarations let bank: GeneralBank; beforeEach(() => { @@ -183,4 +180,7 @@ test('Float Banks', () => { ['a', 1.15], ['b', 1_222_222.100_150_01] ]); + expect(Decimal.add(0.000_000_000_001, 0.000_000_000_001).toNumber()).toEqual(0.000_000_000_002); + expect(Decimal.add(1, 0.1).toNumber()).toEqual(1.1); + expect(Decimal.add(1, 1).toNumber()).toEqual(2); }); diff --git a/tests/unit/bankBackgrounds.test.ts b/tests/unit/bankBackgrounds.test.ts index 9eb537e88cc..1745daf451f 100644 --- a/tests/unit/bankBackgrounds.test.ts +++ b/tests/unit/bankBackgrounds.test.ts @@ -1,4 +1,4 @@ -import fs from 'fs'; +import fs from 'node:fs'; import { describe, expect, test } from 'vitest'; import backgroundImages from '../../src/lib/minions/data/bankBackgrounds'; diff --git a/tests/unit/banksnapshots.test.ts b/tests/unit/banksnapshots.test.ts index 92232acef26..1de3a987285 100644 --- a/tests/unit/banksnapshots.test.ts +++ b/tests/unit/banksnapshots.test.ts @@ -11,7 +11,7 @@ it(`${BOT_TYPE} Creatables`, () => { ...i, inputItems: new Bank(i.inputItems), outputItems: new Bank(i.outputItems), - cantHaveItems: Boolean(i.cantHaveItems) ? new Bank(i.cantHaveItems) : undefined + cantHaveItems: i.cantHaveItems ? new Bank(i.cantHaveItems) : undefined })); expect(result).toMatchSnapshot(); }); @@ -23,8 +23,8 @@ it(`${BOT_TYPE} Buyables`, () => { outputItems: !i.outputItems ? undefined : i.outputItems instanceof Bank - ? i.outputItems - : i.outputItems(mockMUser()) + ? i.outputItems + : i.outputItems(mockMUser()) })); expect(result).toMatchSnapshot(); }); diff --git a/tests/unit/calcPOHBoosts.test.ts b/tests/unit/calcPOHBoosts.test.ts index a54c5320bf6..6cd5262acfa 100644 --- a/tests/unit/calcPOHBoosts.test.ts +++ b/tests/unit/calcPOHBoosts.test.ts @@ -1,4 +1,4 @@ -import { PlayerOwnedHouse } from '@prisma/client'; +import type { PlayerOwnedHouse } from '@prisma/client'; import { describe, expect, test } from 'vitest'; import { calcPOHBoosts, getPOHObject } from '../../src/lib/poh'; diff --git a/tests/unit/circular.test.ts b/tests/unit/circular.test.ts index 10f575f9d8b..80e5fb25f87 100644 --- a/tests/unit/circular.test.ts +++ b/tests/unit/circular.test.ts @@ -1,14 +1,9 @@ -import { Time } from 'e'; -import madge from 'madge'; -import { describe, expect, test } from 'vitest'; +import { parseCircular, parseDependencyTree } from 'dpdm'; +import { expect, test } from 'vitest'; -describe('Circular Dependencies', () => { - test( - 'Circular Dependencies', - async () => { - const res = Object.values(await madge('./dist/index.js').then(res => res.circularGraph())).flat(2); - expect(res).toEqual([]); - }, - { timeout: Time.Minute } - ); +test('Circular Dependencies', async () => { + const tree = await parseDependencyTree('./dist/index.js', {}); + + const circulars = parseCircular(tree); + expect(circulars).toEqual([]); }); diff --git a/tests/unit/getUsersPerkTier.test.ts b/tests/unit/getUsersPerkTier.test.ts index 68f92ebf4aa..220df522cc3 100644 --- a/tests/unit/getUsersPerkTier.test.ts +++ b/tests/unit/getUsersPerkTier.test.ts @@ -1,8 +1,8 @@ import { Time } from 'e'; import { describe, expect, test } from 'vitest'; -import { BitField, PerkTier } from '../../src/lib/constants'; import { MUserClass } from '../../src/lib/MUser'; +import { BitField, PerkTier } from '../../src/lib/constants'; import { getUsersPerkTier } from '../../src/lib/perkTiers'; import { mockMUser } from './utils'; diff --git a/tests/unit/http/http.test.ts b/tests/unit/http/http.test.ts index 1dfde566176..a9ae2ebc5fb 100644 --- a/tests/unit/http/http.test.ts +++ b/tests/unit/http/http.test.ts @@ -1,7 +1,7 @@ import { afterAll, beforeAll, expect, test } from 'vitest'; import { makeServer } from '../../../src/lib/http'; -import { FastifyServer } from '../../../src/lib/http/types'; +import type { FastifyServer } from '../../../src/lib/http/types'; let app: FastifyServer = null!; beforeAll(async () => { diff --git a/tests/unit/http/patreon.test.ts b/tests/unit/http/patreon.test.ts index c9b5cf728c2..9efca54f782 100644 --- a/tests/unit/http/patreon.test.ts +++ b/tests/unit/http/patreon.test.ts @@ -3,7 +3,7 @@ import { createHmac } from 'node:crypto'; import { afterAll, beforeAll, expect, test } from 'vitest'; import { makeServer } from '../../../src/lib/http'; -import { FastifyServer } from '../../../src/lib/http/types'; +import type { FastifyServer } from '../../../src/lib/http/types'; import { mockPatreonWebhookSecret } from '../setup'; let app: FastifyServer = null!; diff --git a/tests/unit/images.test.ts b/tests/unit/images.test.ts index 63e6cafaa36..2cce18d2942 100644 --- a/tests/unit/images.test.ts +++ b/tests/unit/images.test.ts @@ -53,7 +53,7 @@ describe('Images', () => { }); test('Bank Image', async () => { - let bank = new Bank(); + const bank = new Bank(); for (const item of [...Monsters.Cow.allItems]) { bank.add(item); } diff --git a/tests/unit/parseStringBank.test.ts b/tests/unit/parseStringBank.test.ts index 512f414b2e4..13c5abb9145 100644 --- a/tests/unit/parseStringBank.test.ts +++ b/tests/unit/parseStringBank.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ import { randInt } from 'e'; import { Bank, Items } from 'oldschooljs'; import { describe, expect, test } from 'vitest'; @@ -30,12 +29,14 @@ describe('Bank Parsers', () => { expect(expected).toEqual(expect.arrayContaining(output)); expect(output.length).toEqual(expected.length); for (let i = 0; i < output.length; i++) { - let [resItem, resQty] = output[i]; - let [expItem, expQty] = expected[i]; + const [resItem, resQty] = output[i]; + const [expItem, expQty] = expected[i]; expect(resItem).toEqual(expItem); expect(resQty).toEqual(expQty); } + }); + test('parseStringBank2', async () => { expect(psb('')).toEqual([]); expect(psb(' ')).toEqual([]); expect(psb(', ')).toEqual([]); diff --git a/tests/unit/sanity.test.ts b/tests/unit/sanity.test.ts index 200811a56bc..826a3c22277 100644 --- a/tests/unit/sanity.test.ts +++ b/tests/unit/sanity.test.ts @@ -35,7 +35,7 @@ describe('Sanity', () => { expect(itemID('Reward casket (master)')).toEqual(19_836); }); test('openables', () => { - let ids = new Set(); + const ids = new Set(); for (const openable of allOpenables) { if (getOSItem(openable.id) !== openable.openedItem) { throw new Error(`${openable.name} doesnt match`); @@ -48,7 +48,7 @@ describe('Sanity', () => { }); test('exponentialPercentScale', () => { for (let i = 0; i < 100; i++) { - let num = exponentialPercentScale(i); + const num = exponentialPercentScale(i); expect(num > 0 && num <= 100).toBeTruthy(); } expect(exponentialPercentScale(100)).toEqual(100); diff --git a/tests/unit/setup.ts b/tests/unit/setup.ts index 02314a59bc9..7114e976933 100644 --- a/tests/unit/setup.ts +++ b/tests/unit/setup.ts @@ -1,5 +1,4 @@ import '../globalSetup'; - import { vi } from 'vitest'; import { globalConfig } from '../../src/lib/constants'; diff --git a/tests/unit/similarItems.test.ts b/tests/unit/similarItems.test.ts index 6c658a36ca4..3a021c5e33f 100644 --- a/tests/unit/similarItems.test.ts +++ b/tests/unit/similarItems.test.ts @@ -153,13 +153,13 @@ describe('Gear', () => { expect(getSimilarItems(itemID('Infernal max cape'))).toEqual([itemID('Infernal max cape')]); test('toa', () => { - let testGear = new Gear({ cape: 'Masori assembler max cape' }); + const testGear = new Gear({ cape: 'Masori assembler max cape' }); expect(testGear.hasEquipped("Ava's assembler")).toEqual(true); - let testGear2 = new Gear({ weapon: "Osmumten's fang (or)" }); + const testGear2 = new Gear({ weapon: "Osmumten's fang (or)" }); expect(testGear2.hasEquipped("Osmumten's fang")).toEqual(true); - let testGear3 = new Gear({ cape: 'Masori assembler' }); + const testGear3 = new Gear({ cape: 'Masori assembler' }); expect(testGear3.hasEquipped("Ava's assembler")).toEqual(true); - let testGear4 = new Gear({ cape: "Elidinis' ward (or)" }); + const testGear4 = new Gear({ cape: "Elidinis' ward (or)" }); expect(testGear4.hasEquipped("Elidinis' ward (f)")).toEqual(true); }); diff --git a/tests/unit/skillsMeetRequirements.test.ts b/tests/unit/skillsMeetRequirements.test.ts index 6509a611a81..f6cb50712cd 100644 --- a/tests/unit/skillsMeetRequirements.test.ts +++ b/tests/unit/skillsMeetRequirements.test.ts @@ -1,11 +1,11 @@ import { objectEntries } from 'e'; import { describe, expect, test } from 'vitest'; -import { Skills } from '../../src/lib/skilling/skills'; +import type { Skills } from '../../src/lib/skilling/skills'; import { convertLVLtoXP, skillsMeetRequirements } from '../../src/lib/util'; function convert(bank: Record) { - let newObj: Record = {}; + const newObj: Record = {}; for (const [key, val] of objectEntries(bank)) { newObj[key] = convertLVLtoXP(val); } diff --git a/tests/unit/slayer.test.ts b/tests/unit/slayer.test.ts index 8a7e7f7232c..72899cd489a 100644 --- a/tests/unit/slayer.test.ts +++ b/tests/unit/slayer.test.ts @@ -1,11 +1,25 @@ +import { writeFileSync } from 'node:fs'; + +import { objectEntries } from 'e'; import { describe, expect, test } from 'vitest'; +import { SlayerTaskUnlocksEnum } from '../../src/lib/slayer/slayerUnlocks'; import { allSlayerTasks } from '../../src/lib/slayer/tasks'; -describe('slayer.test', () => { - test('tasks', () => { +describe('Slayer', () => { + test('All slayer task monster lists should contain their main monster id', () => { for (const task of allSlayerTasks) { expect(task.monsters).toContain(task.monster.id); } }); + + test('Snapshot the values of the slayer unlocks enum', () => { + const copy = { ...SlayerTaskUnlocksEnum }; + for (const [key, value] of objectEntries(copy)) { + if (typeof value === 'string') { + delete copy[key]; + } + } + writeFileSync('./tests/unit/snapshots/slayerUnlocks.snapshot.json', `${JSON.stringify(copy, null, ' ')}\n`); + }); }); diff --git a/tests/unit/snapshots/slayerUnlocks.snapshot.json b/tests/unit/snapshots/slayerUnlocks.snapshot.json new file mode 100644 index 00000000000..213ffed7e98 --- /dev/null +++ b/tests/unit/snapshots/slayerUnlocks.snapshot.json @@ -0,0 +1,53 @@ +{ + "MalevolentMasquerade": 2, + "RingBling": 3, + "SeeingRed": 4, + "IHopeYouMithMe": 5, + "WatchTheBirdie": 6, + "HotStuff": 7, + "ReptileGotRipped": 8, + "LikeABoss": 9, + "BiggerAndBadder": 10, + "KingBlackBonnet": 11, + "KalphiteKhat": 12, + "UnholyHelmet": 13, + "DarkMantle": 14, + "UndeadHead": 15, + "UseMoreHead": 16, + "TwistedVision": 17, + "StopTheWyvern": 18, + "Basilocked": 19, + "ActualVampyreSlayer": 20, + "NeedMoreDarkness": 21, + "AnkouVeryMuch": 22, + "SuqANotherOne": 23, + "FireAndDarkness": 24, + "PedalToTheMetals": 25, + "IReallyMithYou": 26, + "AdamindSomeMore": 27, + "RUUUUUNE": 28, + "SpiritualFervour": 29, + "BirdsOfAFeather": 30, + "GreaterChallenge": 31, + "ItsDarkInHere": 32, + "BleedMeDry": 33, + "SmellYaLater": 34, + "Horrorific": 35, + "ToDustYouShallReturn": 36, + "WyverNotherOne": 37, + "GetSmashed": 38, + "NechsPlease": 39, + "AugmentMyAbbies": 40, + "KrackOn": 41, + "GetScabarightOnIt": 42, + "WyverNotherTwo": 43, + "Basilonger": 44, + "MoreAtStake": 45, + "SlayerRing": 46, + "HerbSack": 47, + "RunePouch": 48, + "DoubleTrouble": 49, + "BroaderFletching": 50, + "IWildyMoreSlayer": 200, + "Revenenenenenants": 201 +} diff --git a/tests/unit/util.getUserBestGearFromBank.test.ts b/tests/unit/util.getUserBestGearFromBank.test.ts index 0c1b86cb755..03a1cf254e7 100644 --- a/tests/unit/util.getUserBestGearFromBank.test.ts +++ b/tests/unit/util.getUserBestGearFromBank.test.ts @@ -2,10 +2,10 @@ import { Bank } from 'oldschooljs'; import { convertLVLtoXP } from 'oldschooljs/dist/util'; import { describe, expect, test } from 'vitest'; -import { GearSetup, GearStat } from '../../src/lib/gear/types'; +import { type GearSetup, GearStat } from '../../src/lib/gear/types'; import getUserBestGearFromBank from '../../src/lib/minions/functions/getUserBestGearFromBank'; import { Gear } from '../../src/lib/structures/Gear'; -import { Skills } from '../../src/lib/types'; +import type { Skills } from '../../src/lib/types'; import itemID from '../../src/lib/util/itemID'; const userBank = new Bank({ diff --git a/tests/unit/util.test.ts b/tests/unit/util.test.ts index 84faaba350d..7085b0e88d2 100644 --- a/tests/unit/util.test.ts +++ b/tests/unit/util.test.ts @@ -32,7 +32,7 @@ describe('util', () => { ({ bank: b, skillLevel: () => 99 - } as any as MUser); + }) as any as MUser; expect( getUserFoodFromBank({ user: fakeUser(new Bank().add('Shark')), totalHealingNeeded: 500, favoriteFood: [] }) ).toStrictEqual(false); @@ -68,7 +68,7 @@ describe('util', () => { }); test('sanitizeBank', () => { - let buggyBank = new Bank(); + const buggyBank = new Bank(); buggyBank.bank[1] = -1; buggyBank.bank[2] = 0; sanitizeBank(buggyBank); @@ -82,7 +82,7 @@ describe('util', () => { test('sellPriceOfItem', () => { const item = getOSItem('Dragon pickaxe'); const { price } = item; - let expected = reduceNumByPercent(price, 20); + const expected = reduceNumByPercent(price, 20); expect(sellPriceOfItem(item)).toEqual({ price: expected, basePrice: price }); expect(sellPriceOfItem(getOSItem('Yellow square'))).toEqual({ price: 0, basePrice: 0 }); }); @@ -91,9 +91,9 @@ describe('util', () => { const item = getOSItem('Dragon pickaxe'); const { cost } = item; - let expectedOneQty = + const expectedOneQty = (((0.4 - 0.015 * Math.min(1 - 1, 10)) * Math.min(1, 11) + Math.max(1 - 11, 0) * 0.1) * cost) / 1; - let expectedTwentytwoQty = + const expectedTwentytwoQty = (((0.4 - 0.015 * Math.min(22 - 1, 10)) * Math.min(22, 11) + Math.max(22 - 11, 0) * 0.1) * cost) / 22; expect(sellStorePriceOfItem(item, 1)).toEqual({ price: expectedOneQty, basePrice: cost }); expect(sellStorePriceOfItem(item, 22)).toEqual({ price: expectedTwentytwoQty, basePrice: cost }); diff --git a/tests/unit/utils.ts b/tests/unit/utils.ts index 26c1522ce84..0bf06d8a3e6 100644 --- a/tests/unit/utils.ts +++ b/tests/unit/utils.ts @@ -1,14 +1,15 @@ -import { Prisma, User } from '@prisma/client'; -import { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; +import type { Prisma, User } from '@prisma/client'; +import type { CommandResponse } from 'mahoji/dist/lib/structures/ICommand'; import murmurhash from 'murmurhash'; import { Bank } from 'oldschooljs'; import { convertLVLtoXP } from 'oldschooljs/dist/util'; import { expect } from 'vitest'; -import { BitField } from '../../src/lib/constants'; -import type { GearSetup } from '../../src/lib/gear/types'; import { MUserClass } from '../../src/lib/MUser'; -import { filterGearSetup, Gear, PartialGearSetup } from '../../src/lib/structures/Gear'; +import type { BitField } from '../../src/lib/constants'; +import type { GearSetup } from '../../src/lib/gear/types'; +import type { PartialGearSetup } from '../../src/lib/structures/Gear'; +import { Gear, filterGearSetup } from '../../src/lib/structures/Gear'; import type { OSBMahojiCommand } from '../../src/mahoji/lib/util'; interface MockUserArgs { diff --git a/vitest.integration.config.mts b/vitest.integration.config.mts index 1a349326db2..d8ef700294a 100644 --- a/vitest.integration.config.mts +++ b/vitest.integration.config.mts @@ -1,20 +1,20 @@ import { defineConfig } from 'vitest/config'; export default defineConfig({ - test: { - name: 'Old School Bot - Integration', - include: ['tests/integration/**/*.test.ts'], - setupFiles: 'tests/integration/setup.ts', - coverage: { - provider: 'v8', - reporter: 'text', - include: ['src/lib/MUser.ts'] - }, - testTimeout: 30_000, - bail: 1, - maxConcurrency: 5, - maxWorkers: 5, - minWorkers: 1, - pool: 'forks' - } + test: { + name: 'Old School Bot - Integration', + include: ['tests/integration/**/*.test.ts'], + setupFiles: 'tests/integration/setup.ts', + coverage: { + provider: 'v8', + reporter: 'text', + include: ['src/lib/MUser.ts'] + }, + testTimeout: 30_000, + bail: 1, + maxConcurrency: 5, + maxWorkers: 5, + minWorkers: 1, + pool: 'forks' + } }); diff --git a/vitest.unit.config.mts b/vitest.unit.config.mts index 9743145a3d3..a5315297c70 100644 --- a/vitest.unit.config.mts +++ b/vitest.unit.config.mts @@ -3,19 +3,25 @@ import { basename, dirname, join } from 'node:path'; import { defineConfig } from 'vitest/config'; export default defineConfig({ - test: { - name: 'Old School Bot - Unit', - include: ['tests/unit/**/*.test.ts'], - coverage: { - provider: 'v8', - reporter: 'html', - include: ['src/lib/structures/Gear.ts', 'src/lib/util/parseStringBank.ts', 'src/lib/util/equipMulti.ts'] - }, - setupFiles: 'tests/unit/setup.ts', - resolveSnapshotPath: (testPath, extension) => - join(join(dirname(testPath), 'snapshots'), `${basename(testPath)}${extension}`), - maxWorkers: 10, - minWorkers: 1, - slowTestThreshold: 0 - } + test: { + name: 'Old School Bot - Unit', + include: ['tests/unit/**/*.test.ts'], + coverage: { + provider: 'v8', + reporter: 'html', + include: ['src/lib/structures/Gear.ts', 'src/lib/util/parseStringBank.ts', 'src/lib/util/equipMulti.ts'] + }, + setupFiles: 'tests/unit/setup.ts', + resolveSnapshotPath: (testPath, extension) => + join(join(dirname(testPath), 'snapshots'), `${basename(testPath)}${extension}`), + slowTestThreshold: 0, + isolate: false, + poolOptions: { + threads: { + minThreads: 10, + maxThreads: 20, + singleThread: true + } + } + } }); diff --git a/yarn.lock b/yarn.lock index d56b43a4095..b3e4066630a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,13 +10,6 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@babel/code-frame@^7.0.0": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" - integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== - dependencies: - "@babel/highlight" "^7.14.5" - "@babel/code-frame@^7.12.13": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" @@ -29,11 +22,6 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== - "@babel/helper-validator-identifier@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" @@ -44,15 +32,6 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== -"@babel/highlight@^7.14.5": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" - js-tokens "^4.0.0" - "@babel/highlight@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" @@ -62,11 +41,6 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.21.8": - version "7.24.5" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.5.tgz#4a4d5ab4315579e5398a82dcf636ca80c3392790" - integrity sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg== - "@babel/parser@^7.23.6": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b" @@ -79,13 +53,6 @@ dependencies: regenerator-runtime "^0.14.0" -"@babel/runtime@^7.24.6": - version "7.24.7" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.7.tgz#f4f0d5530e8dbdf59b3451b9b3e594b6ba082e12" - integrity sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw== - dependencies: - regenerator-runtime "^0.14.0" - "@babel/types@^7.23.6": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.9.tgz#1dd7b59a9a2b5c87f8b41e52770b5ecbf492e002" @@ -100,13 +67,59 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@dependents/detective-less@^4.1.0": - version "4.1.0" - resolved "https://registry.yarnpkg.com/@dependents/detective-less/-/detective-less-4.1.0.tgz#4a979ee7a6a79eb33602862d6a1263e30f98002e" - integrity sha512-KrkT6qO5NxqNfy68sBl6CTSoJ4SNDIS5iQArkibhlbGU4LaDukZ3q2HIkh8aUKDio6o4itU4xDR7t82Y2eP1Bg== - dependencies: - gonzales-pe "^4.3.0" - node-source-walk "^6.0.1" +"@biomejs/biome@^1.8.3": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-1.8.3.tgz#3b5eecea90d973f71618aae3e6e8be4d2ca23e42" + integrity sha512-/uUV3MV+vyAczO+vKrPdOW0Iaet7UnJMU4bNMinggGJTAnBPjCoLEYcyYtYHNnUNYlv4xZMH6hVIQCAozq8d5w== + optionalDependencies: + "@biomejs/cli-darwin-arm64" "1.8.3" + "@biomejs/cli-darwin-x64" "1.8.3" + "@biomejs/cli-linux-arm64" "1.8.3" + "@biomejs/cli-linux-arm64-musl" "1.8.3" + "@biomejs/cli-linux-x64" "1.8.3" + "@biomejs/cli-linux-x64-musl" "1.8.3" + "@biomejs/cli-win32-arm64" "1.8.3" + "@biomejs/cli-win32-x64" "1.8.3" + +"@biomejs/cli-darwin-arm64@1.8.3": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.8.3.tgz#be2bfdd445cd2d3cb0ff41a96a72ec761753997c" + integrity sha512-9DYOjclFpKrH/m1Oz75SSExR8VKvNSSsLnVIqdnKexj6NwmiMlKk94Wa1kZEdv6MCOHGHgyyoV57Cw8WzL5n3A== + +"@biomejs/cli-darwin-x64@1.8.3": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.8.3.tgz#47d408edd9f5c04069fbcf8610bacf1db8c6c0d9" + integrity sha512-UeW44L/AtbmOF7KXLCoM+9PSgPo0IDcyEUfIoOXYeANaNXXf9mLUwV1GeF2OWjyic5zj6CnAJ9uzk2LT3v/wAw== + +"@biomejs/cli-linux-arm64-musl@1.8.3": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.8.3.tgz#44df284383d57cf4f28daeedd080dad7be05df78" + integrity sha512-9yjUfOFN7wrYsXt/T/gEWfvVxKlnh3yBpnScw98IF+oOeCYb5/b/+K7YNqKROV2i1DlMjg9g/EcN9wvj+NkMuQ== + +"@biomejs/cli-linux-arm64@1.8.3": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.8.3.tgz#6a6b1da1dfce0294a028cbb5d6c40d73691dd713" + integrity sha512-fed2ji8s+I/m8upWpTJGanqiJ0rnlHOK3DdxsyVLZQ8ClY6qLuPc9uehCREBifRJLl/iJyQpHIRufLDeotsPtw== + +"@biomejs/cli-linux-x64-musl@1.8.3": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.8.3.tgz#ceef30a8ee1a00d4ad31e32dd31ba2a661f2719d" + integrity sha512-UHrGJX7PrKMKzPGoEsooKC9jXJMa28TUSMjcIlbDnIO4EAavCoVmNQaIuUSH0Ls2mpGMwUIf+aZJv657zfWWjA== + +"@biomejs/cli-linux-x64@1.8.3": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-1.8.3.tgz#665df74d19fb8f83001a9d80824d3a1723e2123f" + integrity sha512-I8G2QmuE1teISyT8ie1HXsjFRz9L1m5n83U1O6m30Kw+kPMPSKjag6QGUn+sXT8V+XWIZxFFBoTDEDZW2KPDDw== + +"@biomejs/cli-win32-arm64@1.8.3": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.8.3.tgz#0fb6f58990f4de0331a6ed22c47c66f5a89133cc" + integrity sha512-J+Hu9WvrBevfy06eU1Na0lpc7uR9tibm9maHynLIoAjLZpQU3IW+OKHUtyL8p6/3pT2Ju5t5emReeIS2SAxhkQ== + +"@biomejs/cli-win32-x64@1.8.3": + version "1.8.3" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.8.3.tgz#6a9dc5a4e13357277da43c015cd5cdc374035448" + integrity sha512-/PJ59vA1pnQeKahemaQf4Nyj7IKUvGQSc3Ze1uIGi+Wvr1xF7rGobSrAAG01T/gUDG21vkDsZYM03NAmPiVkqg== "@discordjs/builders@^1.7.0": version "1.7.0" @@ -523,53 +536,6 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.22.0.tgz#4b83e9449a205e7d94d5368035450fc1680fe525" integrity sha512-4bDHJrk2WHBXJPhy1y80X7/5b5iZTZP3LGcKIlAP1J+KqZ4zQAPMLEzftGyjjfcKbA4JDlPt/+2R/F1ZTeRgrw== -"@eslint-community/eslint-utils@^4.2.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.3.0.tgz#a556790523a351b4e47e9d385f47265eaaf9780a" - integrity sha512-v3oplH6FYCULtFuCeqyuTd9D2WKO937Dxdq+GmHOLL72TTRriLxz2VLlNfkZRsvj6PKnOPAtuT6dwrs/pA5DvA== - dependencies: - eslint-visitor-keys "^3.3.0" - -"@eslint-community/regexpp@^4.6.1": - version "4.10.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" - integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== - -"@eslint/eslintrc@^1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.1.tgz#af58772019a2d271b7e2d4c23ff4ddcba3ccfb3e" - integrity sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA== - dependencies: - ajv "^6.12.4" - debug "^4.3.2" - espree "^9.4.0" - globals "^13.19.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.1.0" - minimatch "^3.1.2" - strip-json-comments "^3.1.1" - -"@eslint/eslintrc@^2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" - integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== - dependencies: - ajv "^6.12.4" - debug "^4.3.2" - espree "^9.6.0" - globals "^13.19.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.1.0" - minimatch "^3.1.2" - strip-json-comments "^3.1.1" - -"@eslint/js@8.57.0": - version "8.57.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" - integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== - "@fastify/ajv-compiler@^3.5.0": version "3.5.0" resolved "https://registry.yarnpkg.com/@fastify/ajv-compiler/-/ajv-compiler-3.5.0.tgz#459bff00fefbf86c96ec30e62e933d2379e46670" @@ -639,39 +605,6 @@ type-is "^1.6.18" vary "^1.1.2" -"@humanwhocodes/config-array@^0.11.14": - version "0.11.14" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" - integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== - dependencies: - "@humanwhocodes/object-schema" "^2.0.2" - debug "^4.3.1" - minimatch "^3.0.5" - -"@humanwhocodes/config-array@^0.11.8": - version "0.11.8" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" - integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== - dependencies: - "@humanwhocodes/object-schema" "^1.2.1" - debug "^4.1.1" - minimatch "^3.0.5" - -"@humanwhocodes/module-importer@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" - integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== - -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== - -"@humanwhocodes/object-schema@^2.0.2": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" - integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== - "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -845,7 +778,7 @@ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== -"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": +"@nodelib/fs.walk@^1.2.3": version "1.2.8" resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== @@ -904,21 +837,6 @@ dependencies: "@octokit/openapi-types" "^8.3.0" -"@oldschoolgg/eslint-config@^2.0.13": - version "2.0.13" - resolved "https://registry.yarnpkg.com/@oldschoolgg/eslint-config/-/eslint-config-2.0.13.tgz#a203aa964dde613053b4be249c51479da86134b1" - integrity sha512-DJb9d4CAQF8rYQA1+SoJK3/sngM62Z6SO6gW4K2QCIJ8Hj6QFwVCUD7CXJkkAggxl9ua0uzBqODD7OVhJ9PgUw== - dependencies: - "@typescript-eslint/eslint-plugin" "^5.41.0" - "@typescript-eslint/parser" "^5.41.0" - eslint "^8.33.0" - eslint-config-prettier "^8.5.0" - eslint-plugin-import "^2.26.0" - eslint-plugin-prettier "^4.2.1" - eslint-plugin-simple-import-sort "^8.0.0" - eslint-plugin-unicorn "^44.0.2" - prettier "^2.7.1" - "@oldschoolgg/toolkit@^0.0.24": version "0.0.24" resolved "https://registry.yarnpkg.com/@oldschoolgg/toolkit/-/toolkit-0.0.24.tgz#86867f126565d278a209d0e95b3cd9ae527b39ac" @@ -1518,16 +1436,6 @@ expect "^29.0.0" pretty-format "^29.0.0" -"@types/json-schema@^7.0.9": - version "7.0.9" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" - integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== - -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= - "@types/keygrip@*": version "1.0.6" resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.6.tgz#1749535181a2a9b02ac04a797550a8787345b740" @@ -1580,13 +1488,6 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.195.tgz#bafc975b252eb6cea78882ce8a7b6bf22a6de632" integrity sha512-Hwx9EUgdwf2GLarOjQp5ZH8ZmblzcbTBC2wtQWNKARBSxM9ezRIAUpeDTgoQRAFB0+8CNWXVA9+MaSOzOF3nPg== -"@types/madge@^5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@types/madge/-/madge-5.0.0.tgz#5b77c542cb547157b73c7d3c01c82ba81fdec5ca" - integrity sha512-Son5Z121knxCXlQM3Q0ivh0OP8Fix4ztGl0VfA9JybQMPQprc2K4jtTaRc3IhGyBy6ku5cWKJxEuj8zePiZbBQ== - dependencies: - "@types/node" "*" - "@types/mime@^1": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" @@ -1629,11 +1530,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.63.tgz#1788fa8da838dbb5f9ea994b834278205db6ca2b" integrity sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ== -"@types/normalize-package-data@^2.4.0": - version "2.4.1" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" - integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== - "@types/pg-pool@2.0.4": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/pg-pool/-/pg-pool-2.0.4.tgz#b5c60f678094ff3acf3442628a7f708928fcf263" @@ -1676,11 +1572,6 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/semver@^7.3.12": - version "7.3.13" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" - integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== - "@types/send@*": version "0.17.4" resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" @@ -1734,121 +1625,6 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^5.41.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.50.0.tgz#fb48c31cadc853ffc1dc35373f56b5e2a8908fe9" - integrity sha512-vwksQWSFZiUhgq3Kv7o1Jcj0DUNylwnIlGvKvLLYsq8pAWha6/WCnXUeaSoNNha/K7QSf2+jvmkxggC1u3pIwQ== - dependencies: - "@typescript-eslint/scope-manager" "5.50.0" - "@typescript-eslint/type-utils" "5.50.0" - "@typescript-eslint/utils" "5.50.0" - debug "^4.3.4" - grapheme-splitter "^1.0.4" - ignore "^5.2.0" - natural-compare-lite "^1.4.0" - regexpp "^3.2.0" - semver "^7.3.7" - tsutils "^3.21.0" - -"@typescript-eslint/parser@^5.41.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.50.0.tgz#a33f44b2cc83d1b7176ec854fbecd55605b0b032" - integrity sha512-KCcSyNaogUDftK2G9RXfQyOCt51uB5yqC6pkUYqhYh8Kgt+DwR5M0EwEAxGPy/+DH6hnmKeGsNhiZRQxjH71uQ== - dependencies: - "@typescript-eslint/scope-manager" "5.50.0" - "@typescript-eslint/types" "5.50.0" - "@typescript-eslint/typescript-estree" "5.50.0" - debug "^4.3.4" - -"@typescript-eslint/scope-manager@5.50.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.50.0.tgz#90b8a3b337ad2c52bbfe4eac38f9164614e40584" - integrity sha512-rt03kaX+iZrhssaT974BCmoUikYtZI24Vp/kwTSy841XhiYShlqoshRFDvN1FKKvU2S3gK+kcBW1EA7kNUrogg== - dependencies: - "@typescript-eslint/types" "5.50.0" - "@typescript-eslint/visitor-keys" "5.50.0" - -"@typescript-eslint/type-utils@5.50.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.50.0.tgz#509d5cc9728d520008f7157b116a42c5460e7341" - integrity sha512-dcnXfZ6OGrNCO7E5UY/i0ktHb7Yx1fV6fnQGGrlnfDhilcs6n19eIRcvLBqx6OQkrPaFlDPk3OJ0WlzQfrV0bQ== - dependencies: - "@typescript-eslint/typescript-estree" "5.50.0" - "@typescript-eslint/utils" "5.50.0" - debug "^4.3.4" - tsutils "^3.21.0" - -"@typescript-eslint/types@5.50.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.50.0.tgz#c461d3671a6bec6c2f41f38ed60bd87aa8a30093" - integrity sha512-atruOuJpir4OtyNdKahiHZobPKFvZnBnfDiyEaBf6d9vy9visE7gDjlmhl+y29uxZ2ZDgvXijcungGFjGGex7w== - -"@typescript-eslint/types@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f" - integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ== - -"@typescript-eslint/typescript-estree@5.50.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.50.0.tgz#0b9b82975bdfa40db9a81fdabc7f93396867ea97" - integrity sha512-Gq4zapso+OtIZlv8YNAStFtT6d05zyVCK7Fx3h5inlLBx2hWuc/0465C2mg/EQDDU2LKe52+/jN4f0g9bd+kow== - dependencies: - "@typescript-eslint/types" "5.50.0" - "@typescript-eslint/visitor-keys" "5.50.0" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" - -"@typescript-eslint/typescript-estree@^5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz#7d17794b77fabcac615d6a48fb143330d962eb9b" - integrity sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA== - dependencies: - "@typescript-eslint/types" "5.62.0" - "@typescript-eslint/visitor-keys" "5.62.0" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" - -"@typescript-eslint/utils@5.50.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.50.0.tgz#807105f5ffb860644d30d201eefad7017b020816" - integrity sha512-v/AnUFImmh8G4PH0NDkf6wA8hujNNcrwtecqW4vtQ1UOSNBaZl49zP1SHoZ/06e+UiwzHpgb5zP5+hwlYYWYAw== - dependencies: - "@types/json-schema" "^7.0.9" - "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.50.0" - "@typescript-eslint/types" "5.50.0" - "@typescript-eslint/typescript-estree" "5.50.0" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" - semver "^7.3.7" - -"@typescript-eslint/visitor-keys@5.50.0": - version "5.50.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.50.0.tgz#b752ffc143841f3d7bc57d6dd01ac5c40f8c4903" - integrity sha512-cdMeD9HGu6EXIeGOh2yVW6oGf9wq8asBgZx7nsR/D36gTfQ0odE5kcRYe5M81vjEFAcPeugXrHg78Imu55F6gg== - dependencies: - "@typescript-eslint/types" "5.50.0" - eslint-visitor-keys "^3.3.0" - -"@typescript-eslint/visitor-keys@5.62.0": - version "5.62.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e" - integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw== - dependencies: - "@typescript-eslint/types" "5.62.0" - eslint-visitor-keys "^3.3.0" - -"@ungap/structured-clone@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" - integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== - "@vitest/coverage-v8@^1.6.0": version "1.6.0" resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-1.6.0.tgz#2f54ccf4c2d9f23a71294aba7f95b3d2e27d14e7" @@ -1939,22 +1715,17 @@ acorn-import-attributes@^1.9.5: resolved "https://registry.yarnpkg.com/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz#7eb1557b1ba05ef18b5ed0ec67591bfab04688ef" integrity sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ== -acorn-jsx@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - acorn-walk@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== -acorn@^8.11.3, acorn@^8.9.0: +acorn@^8.11.3: version "8.11.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== -acorn@^8.8.0, acorn@^8.8.2: +acorn@^8.8.2: version "8.8.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== @@ -1966,16 +1737,6 @@ ajv-formats@^2.1.1: dependencies: ajv "^8.0.0" -ajv@^6.10.0, ajv@^6.12.4: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - ajv@^8.0.0, ajv@^8.10.0, ajv@^8.11.0: version "8.12.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" @@ -2020,62 +1781,11 @@ ansi-styles@^6.1.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== -any-promise@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" - integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== - -app-module-path@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/app-module-path/-/app-module-path-2.2.0.tgz#641aa55dfb7d6a6f0a8141c4b9c0aa50b6c24dd5" - integrity sha512-gkco+qxENJV+8vFcDiiFhuoSvRXb2a/QPqpSoWhVz829VNJfOTnELbBmPmNKFxf3xdNnw4DWCkzkDaavcX/1YQ== - archy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -array-includes@^3.1.6: - version "3.1.6" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" - integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - get-intrinsic "^1.1.3" - is-string "^1.0.7" - -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - -array.prototype.flat@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" - integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - es-shim-unscopables "^1.0.0" - -array.prototype.flatmap@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" - integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - es-shim-unscopables "^1.0.0" - ascii-table3@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/ascii-table3/-/ascii-table3-0.9.0.tgz#fed1f8c735bd056f1d67b4dff482a72b0858ebad" @@ -2088,11 +1798,6 @@ assertion-error@^1.1.0: resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== -ast-module-types@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ast-module-types/-/ast-module-types-5.0.0.tgz#32b2b05c56067ff38e95df66f11d6afd6c9ba16b" - integrity sha512-JvqziE0Wc0rXQfma0HZC/aY7URXHFuZV84fJRtP8u+lhp0JYCNd5wJzVXP45t0PH0Mej3ynlzvdyITYIu0G4LQ== - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -2103,11 +1808,6 @@ atomic-sleep@^1.0.0: resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== -available-typed-arrays@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" - integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== - avvio@^8.2.0: version "8.2.1" resolved "https://registry.yarnpkg.com/avvio/-/avvio-8.2.1.tgz#b5a482729847abb84d5aadce06511c04a0a62f82" @@ -2181,11 +1881,6 @@ bufferutil@^4.0.8: dependencies: node-gyp-build "^4.3.0" -builtin-modules@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" - integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== - bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" @@ -2196,19 +1891,6 @@ cac@^6.7.14: resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - chai@^4.3.10: version "4.4.1" resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1" @@ -2267,23 +1949,11 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== -ci-info@^3.4.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.1.tgz#708a6cdae38915d597afdf3b145f2f8e1ff55f3f" - integrity sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w== - cjs-module-lexer@^1.2.2: version "1.3.1" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz#c485341ae8fd999ca4ee5af2d7a1c9ae01e0099c" integrity sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q== -clean-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clean-regexp/-/clean-regexp-1.0.0.tgz#8df7c7aae51fd36874e8f8d05b9180bc11a3fed7" - integrity sha1-jffHquUf02h06PjQW5GAvBGj/tc= - dependencies: - escape-string-regexp "^1.0.5" - cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -2329,7 +1999,7 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= -color-name@^1.1.4, color-name@~1.1.4: +color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== @@ -2341,26 +2011,6 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -commander@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" - integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== - -commander@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" - integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== - -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== - -complex.js@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/complex.js/-/complex.js-2.1.1.tgz#0675dac8e464ec431fb2ab7d30f41d889fb25c31" - integrity sha512-8njCHOTtFFLtegk6zQo0kkVX1rngygb/KQI6z1qZxlFI3scluC+LVTCFbrkWjBv4vvLlbQ9t88IPMC6k95VTTg== - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -2386,7 +2036,7 @@ cookie@^0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -2402,14 +2052,7 @@ date-fns@^2.30.0: dependencies: "@babel/runtime" "^7.21.0" -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^4.0.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: +debug@^4.0.0, debug@^4.1.1, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -2428,16 +2071,6 @@ deep-eql@^4.1.3: dependencies: type-detect "^4.0.0" -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - -deep-is@^0.1.3, deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= - deepmerge@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" @@ -2450,14 +2083,6 @@ defaults@^1.0.3: dependencies: clone "^1.0.2" -define-properties@^1.1.3, define-properties@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" - integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== - dependencies: - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -2468,86 +2093,11 @@ depd@2.0.0: resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -dependency-tree@^10.0.9: - version "10.0.9" - resolved "https://registry.yarnpkg.com/dependency-tree/-/dependency-tree-10.0.9.tgz#0c6c0dbeb0c5ec2cf83bf755f30e9cb12e7b4ac7" - integrity sha512-dwc59FRIsht+HfnTVM0BCjJaEWxdq2YAvEDy4/Hn6CwS3CBWMtFnL3aZGAkQn3XCYxk/YcTDE4jX2Q7bFTwCjA== - dependencies: - commander "^10.0.1" - filing-cabinet "^4.1.6" - precinct "^11.0.5" - typescript "^5.0.4" - deprecation@^2.0.0: version "2.3.1" resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== -detective-amd@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/detective-amd/-/detective-amd-5.0.2.tgz#579900f301c160efe037a6377ec7e937434b2793" - integrity sha512-XFd/VEQ76HSpym80zxM68ieB77unNuoMwopU2TFT/ErUk5n4KvUTwW4beafAVUugrjV48l4BmmR0rh2MglBaiA== - dependencies: - ast-module-types "^5.0.0" - escodegen "^2.0.0" - get-amd-module-type "^5.0.1" - node-source-walk "^6.0.1" - -detective-cjs@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/detective-cjs/-/detective-cjs-5.0.1.tgz#836ad51c6de4863efc7c419ec243694f760ff8b2" - integrity sha512-6nTvAZtpomyz/2pmEmGX1sXNjaqgMplhQkskq2MLrar0ZAIkHMrDhLXkRiK2mvbu9wSWr0V5/IfiTrZqAQMrmQ== - dependencies: - ast-module-types "^5.0.0" - node-source-walk "^6.0.0" - -detective-es6@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/detective-es6/-/detective-es6-4.0.1.tgz#38d5d49a6d966e992ef8f2d9bffcfe861a58a88a" - integrity sha512-k3Z5tB4LQ8UVHkuMrFOlvb3GgFWdJ9NqAa2YLUU/jTaWJIm+JJnEh4PsMc+6dfT223Y8ACKOaC0qcj7diIhBKw== - dependencies: - node-source-walk "^6.0.1" - -detective-postcss@^6.1.3: - version "6.1.3" - resolved "https://registry.yarnpkg.com/detective-postcss/-/detective-postcss-6.1.3.tgz#51a2d4419327ad85d0af071c7054c79fafca7e73" - integrity sha512-7BRVvE5pPEvk2ukUWNQ+H2XOq43xENWbH0LcdCE14mwgTBEAMoAx+Fc1rdp76SmyZ4Sp48HlV7VedUnP6GA1Tw== - dependencies: - is-url "^1.2.4" - postcss "^8.4.23" - postcss-values-parser "^6.0.2" - -detective-sass@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/detective-sass/-/detective-sass-5.0.3.tgz#63e54bc9b32f4bdbd9d5002308f9592a3d3a508f" - integrity sha512-YsYT2WuA8YIafp2RVF5CEfGhhyIVdPzlwQgxSjK+TUm3JoHP+Tcorbk3SfG0cNZ7D7+cYWa0ZBcvOaR0O8+LlA== - dependencies: - gonzales-pe "^4.3.0" - node-source-walk "^6.0.1" - -detective-scss@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/detective-scss/-/detective-scss-4.0.3.tgz#79758baa0158f72bfc4481eb7e21cc3b5f1ea6eb" - integrity sha512-VYI6cHcD0fLokwqqPFFtDQhhSnlFWvU614J42eY6G0s8c+MBhi9QAWycLwIOGxlmD8I/XvGSOUV1kIDhJ70ZPg== - dependencies: - gonzales-pe "^4.3.0" - node-source-walk "^6.0.1" - -detective-stylus@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/detective-stylus/-/detective-stylus-4.0.0.tgz#ce97b6499becdc291de7b3c11df8c352c1eee46e" - integrity sha512-TfPotjhszKLgFBzBhTOxNHDsutIxx9GTWjrL5Wh7Qx/ydxKhwUrlSFeLIn+ZaHPF+h0siVBkAQSuy6CADyTxgQ== - -detective-typescript@^11.1.0: - version "11.2.0" - resolved "https://registry.yarnpkg.com/detective-typescript/-/detective-typescript-11.2.0.tgz#5b1450b518cb84b6cfb98ea72d5edd9660668e1b" - integrity sha512-ARFxjzizOhPqs1fYC/2NMC3N4jrQ6HvVflnXBTRqNEqJuXwyKLRr9CrJwkRcV/SnZt1sNXgsF6FPm0x57Tq0rw== - dependencies: - "@typescript-eslint/typescript-estree" "^5.62.0" - ast-module-types "^5.0.0" - node-source-walk "^6.0.2" - typescript "^5.4.4" - diff-sequences@^29.4.3: version "29.4.3" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" @@ -2558,13 +2108,6 @@ diff-sequences@^29.6.3: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - discord-api-types@0.37.61: version "0.37.61" resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.61.tgz#9dd8e58c624237e6f1b23be2d29579af268b8c5b" @@ -2590,20 +2133,6 @@ discord.js@14.14.1: undici "5.27.2" ws "8.14.2" -doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== - dependencies: - esutils "^2.0.2" - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - dotenv-cli@^7.4.2: version "7.4.2" resolved "https://registry.yarnpkg.com/dotenv-cli/-/dotenv-cli-7.4.2.tgz#c158a818de08e1fbc51d310f628cbace9075b734" @@ -2624,6 +2153,19 @@ dotenv@^16.3.0, dotenv@^16.4.5: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.5.tgz#cdd3b3b604cb327e286b4762e13502f717cb099f" integrity sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg== +dpdm@^3.14.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/dpdm/-/dpdm-3.14.0.tgz#12a60a2d88b23981c91239b86e7462a5c203e5e9" + integrity sha512-YJzsFSyEtj88q5eTELg3UWU7TVZkG1dpbF4JDQ3t1b07xuzXmdoGeSz9TKOke1mUuOpWlk4q+pBh+aHzD6GBTg== + dependencies: + chalk "^4.1.2" + fs-extra "^11.1.1" + glob "^10.3.4" + ora "^5.4.1" + tslib "^2.6.2" + typescript "^5.2.2" + yargs "^17.7.2" + e@^0.2.3, e@^0.2.32: version "0.2.32" resolved "https://registry.yarnpkg.com/e/-/e-0.2.32.tgz#b15d9060c5b499b18b9631bd04fe6f34045fd64d" @@ -2654,85 +2196,6 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== -enhanced-resolve@^5.14.1: - version "5.16.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz#65ec88778083056cb32487faa9aef82ed0864787" - integrity sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" - -error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -es-abstract@^1.19.0, es-abstract@^1.20.4: - version "1.21.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.1.tgz#e6105a099967c08377830a0c9cb589d570dd86c6" - integrity sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - es-set-tostringtag "^2.0.1" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.1.3" - get-symbol-description "^1.0.0" - globalthis "^1.0.3" - gopd "^1.0.1" - has "^1.0.3" - has-property-descriptors "^1.0.0" - has-proto "^1.0.1" - has-symbols "^1.0.3" - internal-slot "^1.0.4" - is-array-buffer "^3.0.1" - is-callable "^1.2.7" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-typed-array "^1.1.10" - is-weakref "^1.0.2" - object-inspect "^1.12.2" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" - safe-regex-test "^1.0.0" - string.prototype.trimend "^1.0.6" - string.prototype.trimstart "^1.0.6" - typed-array-length "^1.0.4" - unbox-primitive "^1.0.2" - which-typed-array "^1.1.9" - -es-set-tostringtag@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" - integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== - dependencies: - get-intrinsic "^1.1.3" - has "^1.0.3" - has-tostringtag "^1.0.0" - -es-shim-unscopables@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" - integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== - dependencies: - has "^1.0.3" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - esbuild@^0.20.1: version "0.20.2" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.20.2.tgz#9d6b2386561766ee6b5a55196c6d766d28c87ea1" @@ -2826,11 +2289,6 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -escape-latex@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/escape-latex/-/escape-latex-1.2.0.tgz#07c03818cf7dac250cce517f4fda1b001ef2bca1" - integrity sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw== - escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -2841,286 +2299,6 @@ escape-string-regexp@^2.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -escodegen@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" - integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== - dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -eslint-config-prettier@^8.5.0: - version "8.6.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.6.0.tgz#dec1d29ab728f4fa63061774e1672ac4e363d207" - integrity sha512-bAF0eLpLVqP5oEVUFKpMA+NnRFICwn9X8B5jrR9FcqnYBuPbqWEjTEspPWMj5ye6czoSLDweCzSo3Ko7gGrZaA== - -eslint-import-resolver-node@^0.3.7: - version "0.3.7" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" - integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== - dependencies: - debug "^3.2.7" - is-core-module "^2.11.0" - resolve "^1.22.1" - -eslint-module-utils@^2.7.4: - version "2.7.4" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" - integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== - dependencies: - debug "^3.2.7" - -eslint-plugin-import@^2.26.0: - version "2.27.5" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" - integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== - dependencies: - array-includes "^3.1.6" - array.prototype.flat "^1.3.1" - array.prototype.flatmap "^1.3.1" - debug "^3.2.7" - doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.7" - eslint-module-utils "^2.7.4" - has "^1.0.3" - is-core-module "^2.11.0" - is-glob "^4.0.3" - minimatch "^3.1.2" - object.values "^1.1.6" - resolve "^1.22.1" - semver "^6.3.0" - tsconfig-paths "^3.14.1" - -eslint-plugin-prettier@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" - integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== - dependencies: - prettier-linter-helpers "^1.0.0" - -eslint-plugin-simple-import-sort@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-8.0.0.tgz#9d9a2372b0606e999ea841b10458a370a6ccc160" - integrity sha512-bXgJQ+lqhtQBCuWY/FUWdB27j4+lqcvXv5rUARkzbeWLwea+S5eBZEQrhnO+WgX3ZoJHVj0cn943iyXwByHHQw== - -eslint-plugin-unicorn@^44.0.2: - version "44.0.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-44.0.2.tgz#6324a001c0a5e2ac00fb51b30db27d14c6c36ab3" - integrity sha512-GLIDX1wmeEqpGaKcnMcqRvMVsoabeF0Ton0EX4Th5u6Kmf7RM9WBl705AXFEsns56ESkEs0uyelLuUTvz9Tr0w== - dependencies: - "@babel/helper-validator-identifier" "^7.19.1" - ci-info "^3.4.0" - clean-regexp "^1.0.0" - eslint-utils "^3.0.0" - esquery "^1.4.0" - indent-string "^4.0.0" - is-builtin-module "^3.2.0" - lodash "^4.17.21" - pluralize "^8.0.0" - read-pkg-up "^7.0.1" - regexp-tree "^0.1.24" - safe-regex "^2.1.1" - semver "^7.3.7" - strip-indent "^3.0.0" - -eslint-scope@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" - integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== - dependencies: - esrecurse "^4.3.0" - estraverse "^4.1.1" - -eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== - dependencies: - esrecurse "^4.3.0" - estraverse "^5.2.0" - -eslint-scope@^7.2.2: - version "7.2.2" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" - integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== - dependencies: - esrecurse "^4.3.0" - estraverse "^5.2.0" - -eslint-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== - dependencies: - eslint-visitor-keys "^2.0.0" - -eslint-visitor-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== - -eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== - -eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: - version "3.4.3" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" - integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== - -eslint@^8.33.0: - version "8.33.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.33.0.tgz#02f110f32998cb598c6461f24f4d306e41ca33d7" - integrity sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA== - dependencies: - "@eslint/eslintrc" "^1.4.1" - "@humanwhocodes/config-array" "^0.11.8" - "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.3.2" - doctrine "^3.0.0" - escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-utils "^3.0.0" - eslint-visitor-keys "^3.3.0" - espree "^9.4.0" - esquery "^1.4.0" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - find-up "^5.0.0" - glob-parent "^6.0.2" - globals "^13.19.0" - grapheme-splitter "^1.0.4" - ignore "^5.2.0" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-sdsl "^4.1.4" - js-yaml "^4.1.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" - natural-compare "^1.4.0" - optionator "^0.9.1" - regexpp "^3.2.0" - strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" - text-table "^0.2.0" - -eslint@^8.36.0: - version "8.57.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" - integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== - dependencies: - "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.6.1" - "@eslint/eslintrc" "^2.1.4" - "@eslint/js" "8.57.0" - "@humanwhocodes/config-array" "^0.11.14" - "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - "@ungap/structured-clone" "^1.2.0" - ajv "^6.12.4" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.3.2" - doctrine "^3.0.0" - escape-string-regexp "^4.0.0" - eslint-scope "^7.2.2" - eslint-visitor-keys "^3.4.3" - espree "^9.6.1" - esquery "^1.4.2" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - find-up "^5.0.0" - glob-parent "^6.0.2" - globals "^13.19.0" - graphemer "^1.4.0" - ignore "^5.2.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-yaml "^4.1.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" - natural-compare "^1.4.0" - optionator "^0.9.3" - strip-ansi "^6.0.1" - text-table "^0.2.0" - -espree@^9.4.0: - version "9.4.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.1.tgz#51d6092615567a2c2cff7833445e37c28c0065bd" - integrity sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg== - dependencies: - acorn "^8.8.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.3.0" - -espree@^9.6.0, espree@^9.6.1: - version "9.6.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" - integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== - dependencies: - acorn "^8.9.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.4.1" - -esprima@^4.0.0, esprima@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== - dependencies: - estraverse "^5.1.0" - -esquery@^1.4.2: - version "1.5.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" - integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.1.0, estraverse@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== - estree-walker@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" @@ -3128,11 +2306,6 @@ estree-walker@^3.0.3: dependencies: "@types/estree" "^1.0.0" -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - event-target-shim@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" @@ -3190,15 +2363,10 @@ fast-deep-equal@3.1.3, fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-diff@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" - integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== - -fast-glob@^3.2.9: - version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" - integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== +fast-glob@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -3206,11 +2374,6 @@ fast-glob@^3.2.9: merge2 "^1.3.0" micromatch "^4.0.4" -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - fast-json-stringify@^5.0.0: version "5.6.2" resolved "https://registry.yarnpkg.com/fast-json-stringify/-/fast-json-stringify-5.6.2.tgz#1ea6c2b8d0f27f297f682d1af039398a603af507" @@ -3223,11 +2386,6 @@ fast-json-stringify@^5.0.0: fast-uri "^2.1.0" rfdc "^1.2.0" -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= - fast-querystring@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/fast-querystring/-/fast-querystring-1.1.1.tgz#f4c56ef56b1a954880cfd8c01b83f9e1a3d3fda2" @@ -3280,38 +2438,20 @@ fastify@^4.14.1: semver "^7.3.7" tiny-lru "^10.0.0" -fastq@^1.6.0, fastq@^1.6.1: +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + +fastq@^1.6.1: version "1.11.1" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.1.tgz#5d8175aae17db61947f8b162cfc7f63264d22807" integrity sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw== dependencies: reusify "^1.0.4" -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== - dependencies: - flat-cache "^3.0.4" - -filing-cabinet@^4.1.6: - version "4.2.0" - resolved "https://registry.yarnpkg.com/filing-cabinet/-/filing-cabinet-4.2.0.tgz#bd81241edce6e0c051882bef7b69ffa4c017baf9" - integrity sha512-YZ21ryzRcyqxpyKggdYSoXx//d3sCJzM3lsYoaeg/FyXdADGJrUl+BW1KIglaVLJN5BBcMtWylkygY8zBp2MrQ== - dependencies: - app-module-path "^2.2.0" - commander "^10.0.1" - enhanced-resolve "^5.14.1" - is-relative-path "^1.0.2" - module-definition "^5.0.1" - module-lookup-amd "^8.0.5" - resolve "^1.22.3" - resolve-dependency-path "^3.0.2" - sass-lookup "^5.0.1" - stylus-lookup "^5.0.1" - tsconfig-paths "^4.2.0" - typescript "^5.0.4" - fill-range@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" @@ -3328,42 +2468,6 @@ find-my-way@^7.3.0: fast-querystring "^1.0.0" safe-regex2 "^2.0.0" -find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== - dependencies: - flatted "^3.1.0" - rimraf "^3.0.2" - -flatted@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.1.tgz#bbef080d95fca6709362c73044a1634f7c6e7d05" - integrity sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg== - -for-each@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" - integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== - dependencies: - is-callable "^1.1.3" - foreground-child@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" @@ -3386,10 +2490,14 @@ forwarded@0.2.0, forwarded@^0.2.0: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== -fraction.js@^4.3.7: - version "4.3.7" - resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" - integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== +fs-extra@^11.1.1: + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" fs.realpath@^1.0.0: version "1.0.0" @@ -3411,34 +2519,6 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -function-bind@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" - integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== - -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" - -functions-have-names@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" - integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== - -get-amd-module-type@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/get-amd-module-type/-/get-amd-module-type-5.0.1.tgz#bef38ea3674e1aa1bda9c59c8b0da598582f73f2" - integrity sha512-jb65zDeHyDjFR1loOVk0HQGM5WNwoGB8aLWy3LKCieMKol0/ProHkhO2X1JxojuN10vbz1qNn09MJ7tNp7qMzw== - dependencies: - ast-module-types "^5.0.0" - node-source-walk "^6.0.1" - get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -3449,20 +2529,6 @@ get-func-name@^2.0.0, get-func-name@^2.0.1, get-func-name@^2.0.2: resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" - integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" - -get-own-enumerable-property-symbols@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" - integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== - get-stdin@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" @@ -3473,14 +2539,6 @@ get-stream@^8.0.1: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-8.0.1.tgz#def9dfd71742cd7754a7761ed43749a27d02eca2" integrity sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA== -get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - get-tsconfig@^4.7.5: version "4.7.5" resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.7.5.tgz#5e012498579e9a6947511ed0cd403272c7acbbaf" @@ -3495,12 +2553,17 @@ glob-parent@^5.1.2: dependencies: is-glob "^4.0.1" -glob-parent@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" - integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== +glob@^10.3.4: + version "10.4.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.2.tgz#bed6b95dade5c1f80b4434daced233aee76160e5" + integrity sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w== dependencies: - is-glob "^4.0.3" + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" glob@^10.3.7: version "10.3.12" @@ -3513,7 +2576,7 @@ glob@^10.3.7: minipass "^7.0.4" path-scurry "^1.10.2" -glob@^7.1.3, glob@^7.1.4, glob@^7.2.3: +glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -3525,76 +2588,16 @@ glob@^7.1.3, glob@^7.1.4, glob@^7.2.3: once "^1.3.0" path-is-absolute "^1.0.0" -globals@^13.19.0: - version "13.20.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" - integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== - dependencies: - type-fest "^0.20.2" - -globalthis@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" - integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== - dependencies: - define-properties "^1.1.3" - -globby@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" - glur@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/glur/-/glur-1.1.2.tgz#f20ea36db103bfc292343921f1f91e83c3467689" integrity sha512-l+8esYHTKOx2G/Aao4lEQ0bnHWg4fWtJbVoZZT9Knxi01pB8C80BR85nONLFwkkQoFRCmXY+BUcGZN3yZ2QsRA== -gonzales-pe@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3" - integrity sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ== - dependencies: - minimist "^1.2.5" - -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== - dependencies: - get-intrinsic "^1.1.3" - -graceful-fs@^4.2.4: - version "4.2.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== - -graceful-fs@^4.2.9: +graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -grapheme-splitter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== - -graphemer@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" - integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== - -has-bigints@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" - integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== - has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -3605,30 +2608,6 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== - dependencies: - get-intrinsic "^1.1.1" - -has-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" - integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== - -has-symbols@^1.0.2, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== - dependencies: - has-symbols "^1.0.2" - has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -3636,23 +2615,11 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -hasown@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.1.tgz#26f48f039de2c0f8d3356c223fb8d50253519faa" - integrity sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA== - dependencies: - function-bind "^1.1.2" - helmet@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/helmet/-/helmet-6.0.1.tgz#52ec353638b2e87f14fe079d142b368ac11e79a4" integrity sha512-8wo+VdQhTMVBMCITYZaGTbE4lvlthelPYSvoyNvk4RECTmrVjMerp9RfUOQXZWLvCcAn1pKj7ZRxK4lI9Alrcw== -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -3686,19 +2653,6 @@ ieee754@^1.1.13, ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^5.2.0: - version "5.2.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" - integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== - -import-fresh@^3.0.0, import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - import-in-the-middle@1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz#2a266676e3495e72c04bbaa5ec14756ba168391b" @@ -3719,16 +2673,6 @@ import-in-the-middle@^1.8.1: cjs-module-lexer "^1.2.2" module-details-from-path "^1.0.3" -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= - -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -3742,93 +2686,29 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@~1.3.0: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -internal-slot@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3" - integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ== - dependencies: - get-intrinsic "^1.1.3" - has "^1.0.3" - side-channel "^1.0.4" - ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -is-array-buffer@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.1.tgz#deb1db4fcae48308d54ef2442706c0393997052a" - integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" - is-typed-array "^1.1.10" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= - -is-bigint@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.2.tgz#ffb381442503235ad245ea89e45b3dbff040ee5a" - integrity sha512-0JV5+SOCQkIdzjBK9buARcV804Ddu7A0Qet6sHi3FimE9ne6m4BGQZfRn+NZiXbBk4F4XmHfDZIipLj9pX8dSA== - -is-boolean-object@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.1.tgz#3c0878f035cb821228d350d2e1e36719716a3de8" - integrity sha512-bXdQWkECBUIAcCkeH1unwJLIpZYaa5VvuygSyS/c2lf719mTKZDU5UdDRlpd01UjADgmW8RfqaP+mRaVPdr/Ng== - dependencies: - call-bind "^1.0.2" - -is-builtin-module@^3.2.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" - integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== - dependencies: - builtin-modules "^3.3.0" - -is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" - integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== - -is-core-module@^2.11.0, is-core-module@^2.9.0: +is-core-module@^2.9.0: version "2.11.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== dependencies: has "^1.0.3" -is-core-module@^2.13.0: - version "2.13.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" - integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== - dependencies: - hasown "^2.0.0" - -is-date-object@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5" - integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A== - is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: +is-glob@^4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -3840,113 +2720,26 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== -is-negative-zero@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" - integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== - -is-number-object@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb" - integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw== - is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" - integrity sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg== - -is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - is-plain-object@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== -is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" - integrity sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA== - -is-relative-path@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-relative-path/-/is-relative-path-1.0.2.tgz#091b46a0d67c1ed0fe85f1f8cfdde006bb251d46" - integrity sha512-i1h+y50g+0hRbBD+dbnInl3JlJ702aar58snAeX+MxBAPvzXGej7sYoPMhlnykabt0ZzCJNBEyzMlekuQZN7fA== - -is-shared-array-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" - integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== - dependencies: - call-bind "^1.0.2" - is-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac" integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== -is-string@^1.0.5, is-string@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== - dependencies: - has-tostringtag "^1.0.0" - -is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== - dependencies: - has-symbols "^1.0.2" - -is-typed-array@^1.1.10, is-typed-array@^1.1.9: - version "1.1.10" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" - integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" - is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== -is-url-superb@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-url-superb/-/is-url-superb-4.0.0.tgz#b54d1d2499bb16792748ac967aa3ecb41a33a8c2" - integrity sha512-GI+WjezhPPcbM+tqE9LnmsY5qqjwHzTvjJ36wxYX5ujNXefSUJ/T17r5bqDV8yLhcgB59KTPNOc9O9cmHTPWsA== - -is-url@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" - integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== - -is-weakref@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" - integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== - dependencies: - call-bind "^1.0.2" - isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -4006,10 +2799,14 @@ jackspeak@^2.3.6: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" -javascript-natural-sort@^0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59" - integrity sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== +jackspeak@^3.1.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.0.tgz#a75763ff36ad778ede6a156d8ee8b124de445b4a" + integrity sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" jest-diff@^29.6.2: version "29.6.2" @@ -4077,11 +2874,6 @@ jest-util@^29.6.2: graceful-fs "^4.2.9" picomatch "^2.2.3" -js-sdsl@^4.1.4: - version "4.3.0" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711" - integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== - js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -4092,65 +2884,24 @@ js-tokens@^8.0.2: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-8.0.3.tgz#1c407ec905643603b38b6be6977300406ec48775" integrity sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw== -js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - json-schema-traverse@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= - -json5@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" - integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== - dependencies: - minimist "^1.2.0" - -json5@^2.2.2: - version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - jsonc-parser@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== -levn@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" light-my-request@^5.6.1: version "5.9.1" @@ -4161,11 +2912,6 @@ light-my-request@^5.6.1: process-warning "^2.0.0" set-cookie-parser "^2.4.1" -lines-and-columns@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" - integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= - local-pkg@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.5.0.tgz#093d25a346bae59a99f80e75f6e9d36d7e8c925c" @@ -4174,20 +2920,6 @@ local-pkg@^0.5.0: mlly "^1.4.2" pkg-types "^1.0.3" -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -4242,25 +2974,6 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -madge@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/madge/-/madge-7.0.0.tgz#64b1762033b0f969caa7e5853004b6850e8430bb" - integrity sha512-x9eHkBWoCJ2B8yGesWf8LRucarkbH5P3lazqgvmxe4xn5U2Meyfu906iG9mBB1RnY/f4D+gtELWdiz1k6+jAZA== - dependencies: - chalk "^4.1.2" - commander "^7.2.0" - commondir "^1.0.1" - debug "^4.3.4" - dependency-tree "^10.0.9" - ora "^5.4.1" - pluralize "^8.0.0" - precinct "^11.0.5" - pretty-ms "^7.0.1" - rc "^1.2.8" - stream-to-array "^2.3.0" - ts-graphviz "^1.8.1" - walkdir "^0.4.1" - magic-bytes.js@^1.5.0: version "1.10.0" resolved "https://registry.yarnpkg.com/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz#c41cf4bc2f802992b05e64962411c9dd44fdef92" @@ -4306,21 +3019,6 @@ math-expression-evaluator@^1.3.14: resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.3.14.tgz#0ebeaccf65fea0f6f5a626f88df41814e5fcd9bf" integrity sha512-M6AMrvq9bO8uL42KvQHPA2/SbAobA0R7gviUmPrcTcGfdwpaLitz4q2Euzx2lP9Oy88vxK3HOrsISgSwKsYS4A== -mathjs@^13.0.0: - version "13.0.0" - resolved "https://registry.yarnpkg.com/mathjs/-/mathjs-13.0.0.tgz#29027756f92b7eae6ea9c22bbbdf3a50e66ef917" - integrity sha512-Jy9/01M5lTRLxlkxnvPmvWq6EFwzq8guIspeQ9p66AY+8Pih3Jf8Us5fSZ9kC8gl7iRNHUQ+SJpitX41aa6agw== - dependencies: - "@babel/runtime" "^7.24.6" - complex.js "^2.1.1" - decimal.js "^10.4.3" - escape-latex "^1.2.0" - fraction.js "^4.3.7" - javascript-natural-sort "^0.7.1" - seedrandom "^3.0.5" - tiny-emitter "^2.1.0" - typed-function "^4.1.1" - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -4331,7 +3029,7 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -merge2@^1.3.0, merge2@^1.4.1: +merge2@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== @@ -4366,12 +3064,7 @@ mimic-fn@^4.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc" integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== -min-indent@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" - integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== - -minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: +minimatch@^3.0.4, minimatch@^3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -4385,7 +3078,14 @@ minimatch@^9.0.1: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.6: version "1.2.7" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== @@ -4400,6 +3100,11 @@ minipass@^7.0.4: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.0.tgz#b545f84af94e567386770159302ca113469c80b8" integrity sha512-oGZRv2OT1lO2UF1zUcwdTb3wqUwI0kBGTgt/T7OdSj6M6N5m3o5uPf0AIW6lVxGGoiWUR7e2AwTE+xiwK8WQig== +minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + mitm@^1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/mitm/-/mitm-1.7.2.tgz#d079c44c763a333b15a0f7bfd02446fb8dbbe8e8" @@ -4435,35 +3140,17 @@ mnemonist@0.39.5: dependencies: obliterator "^2.0.1" -module-definition@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/module-definition/-/module-definition-5.0.1.tgz#62d1194e5d5ea6176b7dc7730f818f466aefa32f" - integrity sha512-kvw3B4G19IXk+BOXnYq/D/VeO9qfHaapMeuS7w7sNUqmGaA6hywdFHMi+VWeR9wUScXM7XjoryTffCZ5B0/8IA== - dependencies: - ast-module-types "^5.0.0" - node-source-walk "^6.0.1" - module-details-from-path@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== -module-lookup-amd@^8.0.5: - version "8.0.5" - resolved "https://registry.yarnpkg.com/module-lookup-amd/-/module-lookup-amd-8.0.5.tgz#aaeea41979105b49339380ca3f7d573db78c32a5" - integrity sha512-vc3rYLjDo5Frjox8NZpiyLXsNWJ5BWshztc/5KSOMzpg9k5cHH652YsJ7VKKmtM4SvaxuE9RkrYGhiSjH3Ehow== - dependencies: - commander "^10.0.1" - glob "^7.2.3" - requirejs "^2.3.6" - requirejs-config-file "^4.0.0" - ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.1.1, ms@^2.1.3: +ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -4483,16 +3170,6 @@ nanoid@^3.3.7: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== -natural-compare-lite@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" - integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= - nice-napi@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/nice-napi/-/nice-napi-1.0.2.tgz#dc0ab5a1eac20ce548802fc5686eaa6bc654927b" @@ -4525,23 +3202,6 @@ node-gyp-build@^4.2.2, node-gyp-build@^4.3.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== -node-source-walk@^6.0.0, node-source-walk@^6.0.1, node-source-walk@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/node-source-walk/-/node-source-walk-6.0.2.tgz#ba81bc4bc0f6f05559b084bea10be84c3f87f211" - integrity sha512-jn9vOIK/nfqoFCcpK89/VCVaLg1IHE6UVfDOzvqmANaJ/rWCTEdH8RZ1V278nv2jr36BJdyQXIAavBLXpzdlag== - dependencies: - "@babel/parser" "^7.21.8" - -normalize-package-data@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - npm-run-path@^5.1.0: version "5.3.0" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.3.0.tgz#e23353d0ebb9317f174e93417e4a4d82d0249e9f" @@ -4549,35 +3209,6 @@ npm-run-path@^5.1.0: dependencies: path-key "^4.0.0" -object-inspect@^1.12.2, object-inspect@^1.9.0: - version "1.12.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" - integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@^4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" - integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - has-symbols "^1.0.3" - object-keys "^1.1.1" - -object.values@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" - integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - obliterator@^2.0.1: version "2.0.4" resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" @@ -4632,42 +3263,6 @@ opentelemetry-instrumentation-fetch-node@1.2.0: "@opentelemetry/instrumentation" "^0.43.0" "@opentelemetry/semantic-conventions" "^1.17.0" -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== - dependencies: - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - word-wrap "^1.2.3" - -optionator@^0.9.3: - version "0.9.4" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" - integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== - dependencies: - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - word-wrap "^1.2.5" - ora@^5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" @@ -4688,20 +3283,6 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== -p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - p-limit@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-5.0.0.tgz#6946d5b7140b649b7a33a027d89b4c625b3a5985" @@ -4709,20 +3290,6 @@ p-limit@^5.0.0: dependencies: yocto-queue "^1.0.0" -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - p-queue@^6.6.2: version "6.6.2" resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" @@ -4738,37 +3305,10 @@ p-timeout@^3.2.0: dependencies: p-finally "^1.0.0" -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -parse-json@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== - dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" - -parse-ms@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" - integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== +package-json-from-dist@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00" + integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw== path-is-absolute@^1.0.0: version "1.0.1" @@ -4798,10 +3338,13 @@ path-scurry@^1.10.2: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" pathe@^1.1.0: version "1.1.0" @@ -4920,11 +3463,6 @@ pkg-types@^1.0.3: mlly "^1.2.0" pathe "^1.1.0" -pluralize@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" - integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== - pngjs@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.4.0.tgz#99ca7d725965fb655814eaf65f38f12bbdbf555f" @@ -4935,16 +3473,7 @@ pngjs@^6.0.0: resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-6.0.0.tgz#ca9e5d2aa48db0228a52c419c3308e87720da821" integrity sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg== -postcss-values-parser@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-6.0.2.tgz#636edc5b86c953896f1bb0d7a7a6615df00fb76f" - integrity sha512-YLJpK0N1brcNJrs9WatuJFtHaV9q5aAOj+S4DI5S7jgHlRfm0PIbDCAFRYMQD5SHq7Fy6xsDhyutgS0QOAs0qw== - dependencies: - color-name "^1.1.4" - is-url-superb "^4.0.0" - quote-unquote "^1.0.0" - -postcss@^8.4.23, postcss@^8.4.38: +postcss@^8.4.38: version "8.4.38" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e" integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A== @@ -5002,45 +3531,10 @@ postgres-range@^1.1.1: resolved "https://registry.yarnpkg.com/postgres-range/-/postgres-range-1.1.4.tgz#a59c5f9520909bcec5e63e8cf913a92e4c952863" integrity sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w== -precinct@^11.0.5: - version "11.0.5" - resolved "https://registry.yarnpkg.com/precinct/-/precinct-11.0.5.tgz#3e15b3486670806f18addb54b8533e23596399ff" - integrity sha512-oHSWLC8cL/0znFhvln26D14KfCQFFn4KOLSw6hmLhd+LQ2SKt9Ljm89but76Pc7flM9Ty1TnXyrA2u16MfRV3w== - dependencies: - "@dependents/detective-less" "^4.1.0" - commander "^10.0.1" - detective-amd "^5.0.2" - detective-cjs "^5.0.1" - detective-es6 "^4.0.1" - detective-postcss "^6.1.3" - detective-sass "^5.0.3" - detective-scss "^4.0.3" - detective-stylus "^4.0.0" - detective-typescript "^11.1.0" - module-definition "^5.0.1" - node-source-walk "^6.0.2" - -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - -prettier-linter-helpers@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" - integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== - dependencies: - fast-diff "^1.1.2" - -prettier@^2.7.1: - version "2.8.3" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.3.tgz#ab697b1d3dd46fb4626fbe2f543afe0cc98d8632" - integrity sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw== +prettier@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.2.tgz#03ff86dc7c835f2d2559ee76876a3914cec4a90a" + integrity sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA== pretty-format@^29.0.0, pretty-format@^29.6.2: version "29.6.2" @@ -5060,13 +3554,6 @@ pretty-format@^29.7.0: ansi-styles "^5.0.0" react-is "^18.0.0" -pretty-ms@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" - integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== - dependencies: - parse-ms "^2.1.0" - printable-characters@^1.0.42: version "1.0.42" resolved "https://registry.yarnpkg.com/printable-characters/-/printable-characters-1.0.42.tgz#3f18e977a9bd8eb37fcc4ff5659d7be90868b3d8" @@ -5112,11 +3599,6 @@ quick-format-unescaped@^4.0.3: resolved "https://registry.yarnpkg.com/quick-format-unescaped/-/quick-format-unescaped-4.0.3.tgz#6d6b66b8207aa2b35eef12be1421bb24c428f652" integrity sha512-MaL/oqh02mhEo5m5J2rwsVL23Iw2PEaGVHgT2vFt8AAsr0lfvQA5dpXo9TPu0rz7tSBdUPgkbam0j/fj5ZM8yg== -quote-unquote@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/quote-unquote/-/quote-unquote-1.0.0.tgz#67a9a77148effeaf81a4d428404a710baaac8a0b" - integrity sha512-twwRO/ilhlG/FIgYeKGFqyHhoEhqgnKVkcmqMKi2r524gz3ZbDTcyFt38E9xjJI2vT+KbRNHVbnJ/e0I25Azwg== - random-js@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/random-js/-/random-js-2.1.0.tgz#0c03238b0a4f701f7a4c7303036ade3083d8ee14" @@ -5132,40 +3614,11 @@ raw-body@^2.5.1: iconv-lite "0.4.24" unpipe "1.0.0" -rc@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - react-is@^18.0.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -read-pkg-up@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" - integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== - dependencies: - find-up "^4.1.0" - read-pkg "^5.2.0" - type-fest "^0.8.1" - -read-pkg@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" - integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== - dependencies: - "@types/normalize-package-data" "^2.4.0" - normalize-package-data "^2.5.0" - parse-json "^5.0.0" - type-fest "^0.6.0" - readable-stream@^3.4.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" @@ -5195,25 +3648,6 @@ regenerator-runtime@^0.14.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== -regexp-tree@^0.1.24, regexp-tree@~0.1.1: - version "0.1.24" - resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.24.tgz#3d6fa238450a4d66e5bc9c4c14bb720e2196829d" - integrity sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw== - -regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" - -regexpp@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -5233,35 +3667,12 @@ require-in-the-middle@^7.1.1: module-details-from-path "^1.0.3" resolve "^1.22.1" -requirejs-config-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/requirejs-config-file/-/requirejs-config-file-4.0.0.tgz#4244da5dd1f59874038cc1091d078d620abb6ebc" - integrity sha512-jnIre8cbWOyvr8a5F2KuqBnY+SDA4NXr/hzEZJG79Mxm2WiFQz2dzhC8ibtPJS7zkmBEl1mxSwp5HhC1W4qpxw== - dependencies: - esprima "^4.0.0" - stringify-object "^3.2.1" - -requirejs@^2.3.6: - version "2.3.6" - resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.6.tgz#e5093d9601c2829251258c0b9445d4d19fa9e7c9" - integrity sha512-ipEzlWQe6RK3jkzikgCupiTbTvm4S0/CAU5GlgptkN5SO6F3u0UD0K18wy6ErDqiCyP4J4YYe1HuAShvsxePLg== - -resolve-dependency-path@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/resolve-dependency-path/-/resolve-dependency-path-3.0.2.tgz#012816717bcbe8b846835da11af9d2beb5acef50" - integrity sha512-Tz7zfjhLfsvR39ADOSk9us4421J/1ztVBo4rWUkF38hgHK5m0OCZ3NxFVpqHRkjctnwVa15igEUHFJp8MCS7vA== - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - resolve-pkg-maps@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== -resolve@^1.10.0, resolve@^1.22.1: +resolve@^1.22.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -5270,15 +3681,6 @@ resolve@^1.10.0, resolve@^1.22.1: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^1.22.3: - version "1.22.8" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" - integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== - dependencies: - is-core-module "^2.13.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" @@ -5309,13 +3711,6 @@ rimraf@^2.6.2: dependencies: glob "^7.1.3" -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - rimraf@^5.0.7: version "5.0.7" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.7.tgz#27bddf202e7d89cb2e0381656380d1734a854a74" @@ -5366,15 +3761,6 @@ safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-regex-test@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" - integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" - is-regex "^1.1.4" - safe-regex2@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/safe-regex2/-/safe-regex2-2.0.0.tgz#b287524c397c7a2994470367e0185e1916b1f5b9" @@ -5382,13 +3768,6 @@ safe-regex2@^2.0.0: dependencies: ret "~0.2.0" -safe-regex@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-2.1.1.tgz#f7128f00d056e2fe5c11e81a1324dd974aadced2" - integrity sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A== - dependencies: - regexp-tree "~0.1.1" - safe-stable-stringify@^2.3.1: version "2.4.1" resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.1.tgz#34694bd8a30575b7f94792aa51527551bd733d61" @@ -5399,29 +3778,17 @@ safe-stable-stringify@^2.3.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sass-lookup@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/sass-lookup/-/sass-lookup-5.0.1.tgz#1f01d7ff21e09d8c9dcf8d05b3fca28f2f96e6ed" - integrity sha512-t0X5PaizPc2H4+rCwszAqHZRtr4bugo4pgiCvrBFvIX0XFxnr29g77LJcpyj9A0DcKf7gXMLcgvRjsonYI6x4g== - dependencies: - commander "^10.0.1" - secure-json-parse@^2.4.0, secure-json-parse@^2.5.0: version "2.7.0" resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862" integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw== -seedrandom@^3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7" - integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg== - -"semver@2 || 3 || 4 || 5", "semver@>= 5 < 6": +"semver@>= 5 < 6": version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.0.0, semver@^6.3.0: +semver@^6.0.0: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== @@ -5477,15 +3844,6 @@ shimmer@^1.2.1: resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - siginfo@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" @@ -5535,42 +3893,11 @@ source-map-js@^1.2.0: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== -source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - spawn-command@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2.tgz#9544e1a43ca045f8531aac1a48cb29bdae62338e" integrity sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ== -spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.9" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.9.tgz#8a595135def9592bda69709474f1cbeea7c2467f" - integrity sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ== - split2@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/split2/-/split2-4.1.0.tgz#101907a24370f85bb782f08adaabe4e281ecf809" @@ -5603,13 +3930,6 @@ std-env@^3.5.0: resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.7.0.tgz#c9f7386ced6ecf13360b6c6c55b8aaa4ef7481d2" integrity sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg== -stream-to-array@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353" - integrity sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA== - dependencies: - any-promise "^1.1.0" - "string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -5637,24 +3957,6 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" -string.prototype.trimend@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" - integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - -string.prototype.trimstart@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" - integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -5662,15 +3964,6 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -stringify-object@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" - integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== - dependencies: - get-own-enumerable-property-symbols "^3.0.0" - is-obj "^1.0.1" - is-regexp "^1.0.0" - "strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -5692,33 +3985,11 @@ strip-ansi@^7.0.1: dependencies: ansi-regex "^6.0.1" -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= - strip-final-newline@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd" integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== -strip-indent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" - integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== - dependencies: - min-indent "^1.0.0" - -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== - strip-literal@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-literal/-/strip-literal-2.0.0.tgz#5d063580933e4e03ebb669b12db64d2200687527" @@ -5726,13 +3997,6 @@ strip-literal@^2.0.0: dependencies: js-tokens "^8.0.2" -stylus-lookup@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/stylus-lookup/-/stylus-lookup-5.0.1.tgz#3c4d116c3b1e8e1a8169c0d9cd20e608595560f4" - integrity sha512-tLtJEd5AGvnVy4f9UHQMw4bkJJtaAcmo54N+ovQBjDY3DuWyK9Eltxzr5+KG0q4ew6v2EHyuWWNnHeiw/Eo7rQ== - dependencies: - commander "^10.0.1" - supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -5759,11 +4023,6 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -tapable@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" - integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== - test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -5773,11 +4032,6 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= - thread-stream@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-2.2.0.tgz#310c03a253f729094ce5d4638ef5186dfa80a9e8" @@ -5785,11 +4039,6 @@ thread-stream@^2.0.0: dependencies: real-require "^0.2.0" -tiny-emitter@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" - integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== - tiny-lru@^10.0.0: version "10.0.1" resolved "https://registry.yarnpkg.com/tiny-lru/-/tiny-lru-10.0.1.tgz#aaf5d22207e641ed1b176ac2e616d6cc2fc9ef66" @@ -5837,57 +4086,21 @@ tree-kill@^1.2.2: resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== -ts-graphviz@^1.8.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/ts-graphviz/-/ts-graphviz-1.8.2.tgz#6c4768d05f8a36e37abe34855ffe89a4c4bd96cc" - integrity sha512-5YhbFoHmjxa7pgQLkB07MtGnGJ/yhvjmc9uhsnDBEICME6gkPf83SBwLDQqGDoCa3XzUMWLk1AU2Wn1u1naDtA== - ts-mixer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.3.tgz#69bd50f406ff39daa369885b16c77a6194c7cae6" integrity sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ== -tsconfig-paths@^3.14.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== - dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.1" - minimist "^1.2.6" - strip-bom "^3.0.0" - -tsconfig-paths@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" - integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== - dependencies: - json5 "^2.2.2" - minimist "^1.2.6" - strip-bom "^3.0.0" - tslib@2.6.2, tslib@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== -tslib@^1.8.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - tslib@^2.1.0, tslib@^2.3.1: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== -tsutils@^3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" - tsx@^4.16.0: version "4.16.0" resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.16.0.tgz#913dd96f191b76f07a8744201d8c15d510aa1352" @@ -5898,40 +4111,11 @@ tsx@^4.16.0: optionalDependencies: fsevents "~2.3.3" -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - type-detect@^4.0.0, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - -type-fest@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" - integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - type-is@^1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -5940,29 +4124,15 @@ type-is@^1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -typed-array-length@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" - integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== - dependencies: - call-bind "^1.0.2" - for-each "^0.3.3" - is-typed-array "^1.1.9" - -typed-function@^4.1.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/typed-function/-/typed-function-4.2.1.tgz#19aa51847aa2dea9ef5e7fb7641c060179a74426" - integrity sha512-EGjWssW7Tsk4DGfE+5yluuljS1OGYWiI1J6e8puZz9nTMM51Oug8CD5Zo4gWMsOhq5BI+1bF+rWTm4Vbj3ivRA== - typescript@5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.2.tgz#891e1a90c5189d8506af64b9ef929fca99ba1ee5" integrity sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw== -typescript@^5.0.4, typescript@^5.4.4: - version "5.4.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" - integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== +typescript@^5.2.2: + version "5.5.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.3.tgz#e1b0a3c394190838a0b168e771b0ad56a0af0faa" + integrity sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ== ufo@^1.1.2: version "1.1.2" @@ -5974,16 +4144,6 @@ ufo@^1.3.2: resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.4.0.tgz#39845b31be81b4f319ab1d99fd20c56cac528d32" integrity sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ== -unbox-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" - integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== - dependencies: - call-bind "^1.0.2" - has-bigints "^1.0.2" - has-symbols "^1.0.3" - which-boxed-primitive "^1.0.2" - "underscore@>= 1.1.6 < 1.14": version "1.13.6" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441" @@ -6001,6 +4161,11 @@ universal-user-agent@^6.0.0: resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== +universalify@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d" + integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== + unpipe@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -6023,14 +4188,6 @@ uuid@8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - vary@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -6084,11 +4241,6 @@ vitest@^1.6.0: vite-node "1.6.0" why-is-node-running "^2.2.2" -walkdir@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39" - integrity sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ== - wcwidth@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" @@ -6109,29 +4261,6 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -which-boxed-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== - dependencies: - is-bigint "^1.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" - is-symbol "^1.0.3" - -which-typed-array@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" - integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" - is-typed-array "^1.1.10" - which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -6147,11 +4276,6 @@ why-is-node-running@^2.2.2: siginfo "^2.0.0" stackback "0.0.2" -word-wrap@^1.2.3, word-wrap@^1.2.5, word-wrap@~1.2.3: - version "1.2.5" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" - integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== - "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -6227,11 +4351,6 @@ yargs@^17.7.2: y18n "^5.0.5" yargs-parser "^21.1.1" -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - yocto-queue@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251" From db87f7cc5e6de5ca32ffc317f026e2e862484749 Mon Sep 17 00:00:00 2001 From: GC <30398469+gc@users.noreply.github.com> Date: Wed, 3 Jul 2024 13:30:52 +1000 Subject: [PATCH 033/249] Integration test improvements (#5935) --- package.json | 13 +- src/index.ts | 9 +- src/lib/MUser.ts | 14 +- src/lib/bankImage.ts | 10 +- src/lib/geImage.ts | 10 +- src/lib/grandExchange.ts | 5 - src/lib/roboChimp.ts | 9 +- src/lib/settings/prisma.ts | 10 +- src/lib/util/logger.ts | 9 +- src/lib/util/transactItemsFromBank.ts | 10 +- src/scripts/integration-tests.ts | 10 +- tests/integration/allCommandsBase.test.ts | 33 +- tests/integration/commands/open.test.ts | 16 +- tests/integration/grandExchange.test.ts | 77 ++- tests/integration/migrateUser.test.ts | 340 ++++++----- tests/integration/setup.ts | 14 - tests/integration/tradeTransaction.test.ts | 5 - tests/integration/trading.test.ts | 7 +- tests/unit/images.test.ts | 43 +- tests/unit/parseStringBank.test.ts | 26 +- tests/unit/snapshots/bank.OSB.png | Bin 0 -> 16248 bytes tests/unit/snapshots/chatHead.OSB.png | Bin 0 -> 62759 bytes tests/unit/snapshots/cl.OSB.png | Bin 0 -> 49082 bytes tests/unit/snapshots/cox.OSB.png | Bin 0 -> 38666 bytes ...mages-test-ts-images-bank-image-1-snap.png | Bin 15703 -> 0 bytes ...ages-test-ts-images-chart-image-1-snap.png | Bin 21256 -> 0 bytes ...mages-test-ts-images-chat-heads-1-snap.png | Bin 63003 -> 0 bytes ...s-test-ts-images-collection-log-1-snap.png | Bin 48541 -> 0 bytes ...images-test-ts-images-cox-image-1-snap.png | Bin 39175 -> 0 bytes ...images-test-ts-images-poh-image-1-snap.png | Bin 290677 -> 0 bytes ...images-test-ts-images-toa-image-1-snap.png | Bin 25557 -> 0 bytes tests/unit/snapshots/poh.OSB.png | Bin 0 -> 289832 bytes tests/unit/snapshots/toa.OSB.png | Bin 0 -> 25449 bytes vitest.integration.config.mts | 12 +- yarn.lock | 535 +----------------- 35 files changed, 304 insertions(+), 913 deletions(-) create mode 100644 tests/unit/snapshots/bank.OSB.png create mode 100644 tests/unit/snapshots/chatHead.OSB.png create mode 100644 tests/unit/snapshots/cl.OSB.png create mode 100644 tests/unit/snapshots/cox.OSB.png delete mode 100644 tests/unit/snapshots/images-test-ts-tests-unit-images-test-ts-images-bank-image-1-snap.png delete mode 100644 tests/unit/snapshots/images-test-ts-tests-unit-images-test-ts-images-chart-image-1-snap.png delete mode 100644 tests/unit/snapshots/images-test-ts-tests-unit-images-test-ts-images-chat-heads-1-snap.png delete mode 100644 tests/unit/snapshots/images-test-ts-tests-unit-images-test-ts-images-collection-log-1-snap.png delete mode 100644 tests/unit/snapshots/images-test-ts-tests-unit-images-test-ts-images-cox-image-1-snap.png delete mode 100644 tests/unit/snapshots/images-test-ts-tests-unit-images-test-ts-images-poh-image-1-snap.png delete mode 100644 tests/unit/snapshots/images-test-ts-tests-unit-images-test-ts-images-toa-image-1-snap.png create mode 100644 tests/unit/snapshots/poh.OSB.png create mode 100644 tests/unit/snapshots/toa.OSB.png diff --git a/package.json b/package.json index ffea0cecb98..58234425001 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,7 @@ "test:unit": "vitest run --coverage --config vitest.unit.config.mts", "dev": "yarn wipedist && tsc -w -p src", "test:watch": "vitest --config vitest.unit.config.mts --coverage", - "test:integration": "tsx ./src/scripts/integration-tests.ts", - "build:esbuild": "esbuild --bundle src/index.ts --outdir=dist --platform=node --loader:.node=file --external:canvas" + "test:integration": "tsx ./src/scripts/integration-tests.ts" }, "dependencies": { "@fastify/cors": "^8.2.0", @@ -59,25 +58,19 @@ "devDependencies": { "@biomejs/biome": "^1.8.3", "@oldschoolgg/ts-config": "^0.0.1", - "@types/jest-image-snapshot": "^6.1.0", "@types/lodash": "^4.14.195", - "@types/mitm": "^1.3.8", - "@types/node": "^14.18.12", + "@types/node": "^20.14.9", "@types/node-cron": "^3.0.7", "@types/node-fetch": "^2.6.1", "@vitest/coverage-v8": "^1.6.0", "concurrently": "^8.2.2", - "dotenv-cli": "^7.4.2", "dpdm": "^3.14.0", - "esbuild": "^0.22.0", "fast-glob": "^3.3.2", - "jest-image-snapshot": "^6.2.0", - "mitm": "^1.7.2", "prettier": "^3.3.2", "prisma": "^5.13.0", "rimraf": "^5.0.7", "tsx": "^4.16.0", - "typescript": "5.0.2", + "typescript": "^5.5.3", "vitest": "^1.6.0" }, "engines": { diff --git a/src/index.ts b/src/index.ts index 96325d11ab7..8f9d93876a9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -127,14 +127,7 @@ export const mahojiClient = new MahojiClient({ }); declare global { - const globalClient: OldSchoolBotClient; -} -declare global { - namespace NodeJS { - interface Global { - globalClient: OldSchoolBotClient; - } - } + var globalClient: OldSchoolBotClient; } client.mahojiClient = mahojiClient; diff --git a/src/lib/MUser.ts b/src/lib/MUser.ts index b28502d154e..6cc93057e41 100644 --- a/src/lib/MUser.ts +++ b/src/lib/MUser.ts @@ -947,6 +947,8 @@ Charge your items using ${mentionCommand(globalClient, 'minion', 'charge')}.` } declare global { export type MUser = MUserClass; + var mUserFetch: typeof srcMUserFetch; + var GlobalMUserClass: typeof MUserClass; } export async function srcMUserFetch(userID: string) { @@ -962,17 +964,5 @@ export async function srcMUserFetch(userID: string) { return new MUserClass(user); } -declare global { - const mUserFetch: typeof srcMUserFetch; - const GlobalMUserClass: typeof MUserClass; -} -declare global { - namespace NodeJS { - interface Global { - mUserFetch: typeof srcMUserFetch; - GlobalMUserClass: typeof MUserClass; - } - } -} global.mUserFetch = srcMUserFetch; global.GlobalMUserClass = MUserClass; diff --git a/src/lib/bankImage.ts b/src/lib/bankImage.ts index fa67b914550..3c21b414b98 100644 --- a/src/lib/bankImage.ts +++ b/src/lib/bankImage.ts @@ -944,15 +944,9 @@ export async function drawChestLootImage(options: { } declare global { - const bankImageGenerator: BankImageTask; -} -declare global { - namespace NodeJS { - interface Global { - bankImageGenerator: BankImageTask; - } - } + var bankImageGenerator: BankImageTask; } + export const bankImageTask = new BankImageTask(); global.bankImageGenerator = bankImageTask; bankImageGenerator.init(); diff --git a/src/lib/geImage.ts b/src/lib/geImage.ts index 3e83d4c8bf9..6678754ee09 100644 --- a/src/lib/geImage.ts +++ b/src/lib/geImage.ts @@ -231,15 +231,9 @@ class GeImageTask { } declare global { - const geImageGenerator: GeImageTask; -} -declare global { - namespace NodeJS { - interface Global { - geImageGenerator: GeImageTask; - } - } + var geImageGenerator: GeImageTask; } + global.geImageGenerator = new GeImageTask(); geImageGenerator.init(); diff --git a/src/lib/grandExchange.ts b/src/lib/grandExchange.ts index 240cffe3872..5146228c64d 100644 --- a/src/lib/grandExchange.ts +++ b/src/lib/grandExchange.ts @@ -1,6 +1,5 @@ import type { GEListing, GETransaction } from '@prisma/client'; import { GEListingType } from '@prisma/client'; -import { Stopwatch } from '@sapphire/stopwatch'; import { ButtonBuilder, ButtonStyle, bold, userMention } from 'discord.js'; import { Time, calcPercentOfNum, clamp, noOp, sumArr } from 'e'; import { Bank } from 'oldschooljs'; @@ -846,8 +845,6 @@ Difference: ${shouldHave.difference(currentBank)}`); private async _tick() { if (!this.ready) return; if (this.locked) return; - const stopwatch = new Stopwatch(); - stopwatch.start(); const { buyListings: _buyListings, sellListings: _sellListings } = await this.fetchActiveListings(); // Filter out listings from Blacklisted users: @@ -895,8 +892,6 @@ Difference: ${shouldHave.difference(currentBank)}`); // Process only one transaction per tick break; } - - stopwatch.stop(); } async totalReset() { diff --git a/src/lib/roboChimp.ts b/src/lib/roboChimp.ts index b37a3703b05..2c8deb49354 100644 --- a/src/lib/roboChimp.ts +++ b/src/lib/roboChimp.ts @@ -10,14 +10,7 @@ import { calculateMastery } from './mastery'; import { MUserStats } from './structures/MUserStats'; declare global { - const roboChimpClient: PrismaClient; -} -declare global { - namespace NodeJS { - interface Global { - roboChimpClient: PrismaClient; - } - } + var roboChimpClient: PrismaClient; } export type RobochimpUser = User; diff --git a/src/lib/settings/prisma.ts b/src/lib/settings/prisma.ts index e62203a12b3..a1902835614 100644 --- a/src/lib/settings/prisma.ts +++ b/src/lib/settings/prisma.ts @@ -8,17 +8,12 @@ import type { ActivityTaskData } from '../types/minions'; import { sqlLog } from '../util/logger'; declare global { - namespace NodeJS { - interface Global { - prisma: PrismaClient | undefined; - } - } + var prisma: PrismaClient | undefined; } function makePrismaClient(): PrismaClient { - if (!isMainThread && !process.env.TEST) return null as any; if (!production && !process.env.TEST) console.log('Making prisma client...'); - if (!isMainThread) { + if (!isMainThread && !process.env.TEST) { throw new Error('Prisma client should only be created on the main thread.'); } @@ -32,6 +27,7 @@ function makePrismaClient(): PrismaClient { }); } +// biome-ignore lint/suspicious/noRedeclare: export const prisma = global.prisma || makePrismaClient(); global.prisma = prisma; diff --git a/src/lib/util/logger.ts b/src/lib/util/logger.ts index e7b3d91dfa5..415e9bb9205 100644 --- a/src/lib/util/logger.ts +++ b/src/lib/util/logger.ts @@ -37,14 +37,7 @@ function _debugLog(str: string, context: LogContext = {}) { sonicBoom.write(`${JSON.stringify(o)}\n`); } declare global { - const debugLog: typeof _debugLog; -} -declare global { - namespace NodeJS { - interface Global { - debugLog: typeof _debugLog; - } - } + var debugLog: typeof _debugLog; } global.debugLog = _debugLog; diff --git a/src/lib/util/transactItemsFromBank.ts b/src/lib/util/transactItemsFromBank.ts index d2d39f40382..4b18449d743 100644 --- a/src/lib/util/transactItemsFromBank.ts +++ b/src/lib/util/transactItemsFromBank.ts @@ -22,15 +22,9 @@ export interface TransactItemsArgs { } declare global { - const transactItems: typeof transactItemsFromBank; -} -declare global { - namespace NodeJS { - interface Global { - transactItems: typeof transactItemsFromBank; - } - } + var transactItems: typeof transactItemsFromBank; } + global.transactItems = transactItemsFromBank; export async function transactItemsFromBank({ userID, diff --git a/src/scripts/integration-tests.ts b/src/scripts/integration-tests.ts index 2ffbc33cc58..20ee1208c9c 100644 --- a/src/scripts/integration-tests.ts +++ b/src/scripts/integration-tests.ts @@ -1,4 +1,6 @@ import { execSync } from 'node:child_process'; +import path from 'node:path'; +import { config } from 'dotenv'; import { sleep } from 'e'; async function main() { @@ -9,11 +11,13 @@ async function main() { await sleep(2000); console.log('Getting ready...'); - execSync('dotenv -e .env.test -- prisma db push --schema="./prisma/schema.prisma"', { stdio: 'inherit' }); - execSync('dotenv -e .env.test -- prisma db push --schema="./prisma/robochimp.prisma"', { stdio: 'inherit' }); + const env = { ...process.env, ...config({ path: path.resolve('.env.test') }).parsed }; + + execSync('npx prisma db push --schema="./prisma/schema.prisma"', { stdio: 'inherit', env }); + execSync('npx prisma db push --schema="./prisma/robochimp.prisma"', { stdio: 'inherit', env }); console.log('Building...'); - execSync('yarn build:esbuild', { stdio: 'inherit' }); + execSync('yarn build', { stdio: 'inherit' }); console.log('Starting tests...'); const runs = 1; diff --git a/tests/integration/allCommandsBase.test.ts b/tests/integration/allCommandsBase.test.ts index ee64e77da17..284d263ccf2 100644 --- a/tests/integration/allCommandsBase.test.ts +++ b/tests/integration/allCommandsBase.test.ts @@ -176,7 +176,7 @@ test( await maxUser.update({ bitfield: [BitField.isModerator] }); const store = new Store({ name: 'commands', dirs: [join('dist', 'mahoji')], checker: isValidCommand }); await store.load(); - const currentClientSettings = await mahojiClientSettingsFetch({ construction_cost_bank: true }); + await mahojiClientSettingsFetch({ construction_cost_bank: true }); await prisma.activity.deleteMany({ where: { user_id: BigInt(maxUser.id) @@ -289,30 +289,35 @@ test( ['minion', 'bankbg'] ]; + const promises = []; + for (const command of cmds) { if (ignoredCommands.includes(command.name)) continue; - const options = await generateCommandInputs(maxUser, command.options!); + + const options = shuffleArr(await generateCommandInputs(maxUser, command.options!)).slice(0, 5); outer: for (const option of options) { for (const [parent, sub, subCommand] of ignoredSubCommands) { if (command.name === parent && option[sub] && (subCommand ? option[sub][subCommand] : true)) { continue outer; } } - try { - const res = await maxUser.runCommand(command, option); - minionActivityCache.clear(); - // console.log(`Running command ${command.name} - // Options: ${JSON.stringify(option)} - // Result: ${JSON.stringify(res).slice(0, 100)}`); - } catch (err) { - console.error( - `Failed to run command ${command.name} with options ${JSON.stringify(option)}: ${err}` - ); - throw err; - } + + promises.push(async () => { + try { + await maxUser.runCommand(command, option); + minionActivityCache.clear(); + } catch (err) { + console.error( + `Failed to run command ${command.name} with options ${JSON.stringify(option)}: ${err}` + ); + throw err; + } + }); } } + await Promise.all(promises); + await client.processActivities(); }, { diff --git a/tests/integration/commands/open.test.ts b/tests/integration/commands/open.test.ts index 0462f07c7ff..02a5ac3c673 100644 --- a/tests/integration/commands/open.test.ts +++ b/tests/integration/commands/open.test.ts @@ -1,5 +1,5 @@ import { Bank } from 'oldschooljs'; -import { beforeEach, describe, expect, test } from 'vitest'; +import { describe, expect, test } from 'vitest'; import { openCommand } from '../../../src/mahoji/commands/open'; import { randomMock } from '../setup'; @@ -7,15 +7,10 @@ import { createTestUser, mockClient } from '../util'; describe('Open Command', async () => { await mockClient(); - const user = await createTestUser(); - - beforeEach(async () => { + test.concurrent('Open with no quantity', async () => { randomMock(); - await user.reset(); + const user = await createTestUser(); await user.addItemsToBank({ items: new Bank().add('Reward casket (beginner)', 100) }); - }); - - test('Open with no quantity', async () => { const result = await user.runCommand(openCommand, { name: 'reward casket (beginner)' }); expect(result).toMatchObject({ content: `You have now opened a total of 1x Reward casket (beginner) @@ -31,7 +26,10 @@ describe('Open Command', async () => { await user.clMatch(new Bank().add('Fire rune', 34)); }); - test('Open with quantity', async () => { + test.concurrent('Open with quantity', async () => { + randomMock(); + const user = await createTestUser(); + await user.addItemsToBank({ items: new Bank().add('Reward casket (beginner)', 100) }); await user.runCommand(openCommand, { name: 'reward casket (beginner)', quantity: 10 }); await user.bankAmountMatch('Reward casket (beginner)', 90); await user.openedBankMatch(new Bank().add('Reward casket (beginner)', 10)); diff --git a/tests/integration/grandExchange.test.ts b/tests/integration/grandExchange.test.ts index cea422a8e41..849f452ed5e 100644 --- a/tests/integration/grandExchange.test.ts +++ b/tests/integration/grandExchange.test.ts @@ -5,6 +5,7 @@ import { describe, expect, test } from 'vitest'; import { usernameCache } from '../../src/lib/constants'; import { GrandExchange } from '../../src/lib/grandExchange'; +import { prisma } from '../../src/lib/settings/prisma'; import { assert } from '../../src/lib/util'; import resolveItems from '../../src/lib/util/resolveItems'; import { geCommand } from '../../src/mahoji/commands/ge'; @@ -12,23 +13,11 @@ import { cancelUsersListings } from '../../src/mahoji/lib/abstracted_commands/ca import type { TestUser } from './util'; import { createTestUser, mockClient } from './util'; -const quantities = [-1, 0, 100_000_000_000_000_000, 1, 2, 38, 1_000_000_000_000, 500, '5*5']; -const prices = [ - -1, - 0, - 100_000_000_000_000_000, - 1, - 2, - 1_000_000_000_000, - 99, - 100, - 101, - 1005, - 4005, - 5005, - 100_000, - '5*9999999' -]; +const TICKS_TO_RUN = 100; +const AMOUNT_USERS = 20; + +const quantities = [1, 2, 38, 500, '5*5']; +const prices = [1, 30, 33, 55]; const sampleBank = new Bank() .add('Coins', 1_000_000_000) @@ -58,33 +47,39 @@ describe('Grand Exchange', async () => { const currentOwnedBank = await GrandExchange.fetchOwnedBank(); expect(currentOwnedBank.toString()).toEqual(new Bank().toString()); - const amountOfUsers = randInt(100, 200); - - const totalExpectedBank = sampleBank.clone().multiply(amountOfUsers); - const users: TestUser[] = []; - for (let i = 0; i < amountOfUsers; i++) { - const user = await createTestUser(); - await user.addItemsToBank({ items: sampleBank }); - users.push(user); + const totalExpectedBank = sampleBank.clone().multiply(AMOUNT_USERS); + let users: TestUser[] = []; + + for (let i = 0; i < AMOUNT_USERS; i++) { + users.push( + (async () => { + const user = await createTestUser(); + await user.addItemsToBank({ items: sampleBank }); + return user; + })() as any + ); } - console.log(`Finished initializing ${amountOfUsers} users`); + users = await Promise.all(users); + console.log(`Finished initializing ${AMOUNT_USERS} users`); // Run a bunch of commands to buy/sell const commandPromises = new PQueue({ concurrency: 20 }); for (const user of shuffleArr(users)) { commandPromises.add(async () => { - const method = randArrItem(['buy', 'sell']); - const quantity = randArrItem(quantities); - const price = randArrItem(prices); - for (const item of itemPool) { - await user.runCommand(geCommand, { - [method]: { - item, - quantity, - price - } - }); + for (let i = 0; i < 5; i++) { + const method = randArrItem(['buy', 'sell']); + const quantity = randArrItem(quantities); + const price = randArrItem(prices); + for (const item of itemPool) { + await user.runCommand(geCommand, { + [method]: { + item, + quantity, + price + } + }); + } } }); } @@ -93,13 +88,13 @@ describe('Grand Exchange', async () => { console.log('Finished running all commands'); // Tick the g.e to make some transactions - for (let i = 0; i < 100; i++) { + for (let i = 0; i < TICKS_TO_RUN; i++) { await GrandExchange.tick(); - await waitForGEToBeEmpty(); await GrandExchange.extensiveVerification(); } await waitForGEToBeEmpty(); - console.log('Finished ticking 100 times'); + const count = await prisma.gETransaction.count(); + console.log(`Finished ticking ${TICKS_TO_RUN} times, made ${count} transactions`); // Cancel all remaining listings const cancelPromises = []; @@ -130,8 +125,6 @@ describe('Grand Exchange', async () => { const data = await GrandExchange.fetchData(); expect(data.isLocked).toEqual(false); - expect(data.taxBank, '1MS').toBeGreaterThan(0); - expect(data.totalTax, 'L1M').toBeGreaterThan(0); const totalTaxed = await global.prisma!.gETransaction.aggregate({ _sum: { diff --git a/tests/integration/migrateUser.test.ts b/tests/integration/migrateUser.test.ts index 4387b460bbf..ebc0eadeeff 100644 --- a/tests/integration/migrateUser.test.ts +++ b/tests/integration/migrateUser.test.ts @@ -24,7 +24,7 @@ import type { import { Time, deepClone, randArrItem, randInt, shuffleArr, sumArr } from 'e'; import { Bank } from 'oldschooljs'; import type { ItemBank } from 'oldschooljs/dist/meta/types'; -import { describe, expect, test, vi } from 'vitest'; +import { expect, test, vi } from 'vitest'; import { processPendingActivities } from '../../src/lib/Task'; import { BitField } from '../../src/lib/constants'; @@ -1190,226 +1190,212 @@ async function buildBaseUser(userId: string) { const user = await createTestUser(startBank, userData); return user; } -describe('migrate user test', async () => { - await mockClient(); - vi.doMock('../../src/lib/util', async () => { - const actual: any = await vi.importActual('../../src/lib/util'); - return { - ...actual, - channelIsSendable: () => false - }; - }); - - const logResult = ( - result: { result: boolean; errors: string[] }, - sourceData: UserData, - newData: UserData, - srcHistory?: string[], - dstHistory?: string[] - ) => { - if (!result.result) { - if (srcHistory) { - console.log(`Source Command History: ${sourceData.id}`); - console.log(srcHistory); - } - if (dstHistory) { - console.log(`Target Command History: ${newData.id}`); - console.log(dstHistory); - } - console.log(`source: ${sourceData.id} dest: ${newData.id}`); - console.log(result.errors); - console.log(JSON.stringify(sourceData)); - console.log(JSON.stringify(newData)); - } +await mockClient(); +vi.doMock('../../src/lib/util', async () => { + const actual: any = await vi.importActual('../../src/lib/util'); + return { + ...actual, + channelIsSendable: () => false }; +}); - test('test preventing a double (clobber) robochimp migration (two bot-migration)', async () => { - const sourceUserId = mockedId(); - const destUserId = mockedId(); - - // Create source user, and populate data: - const sourceUser = await buildBaseUser(sourceUserId); - const srcHistory = await runRandomTestCommandsOnUser(sourceUser, 5, true); - - const sourceData = new UserData(sourceUser); - await sourceData.sync(); - - const migrateResult = await migrateUser(sourceUser.id, destUserId); - expect(migrateResult).toEqual(true); - - const destData = new UserData(destUserId); - await destData.sync(); - - const compareResult = sourceData.equals(destData); - logResult(compareResult, sourceData, destData, srcHistory, []); - expect(compareResult.result).toBe(true); - - // Now the actual test, everything above has to happen first... - await runAllTestCommandsOnUser(sourceUser); +const logResult = ( + result: { result: boolean; errors: string[] }, + sourceData: UserData, + newData: UserData, + srcHistory?: string[], + dstHistory?: string[] +) => { + if (!result.result) { + if (srcHistory) { + console.log(`Source Command History: ${sourceData.id}`); + console.log(srcHistory); + } + if (dstHistory) { + console.log(`Target Command History: ${newData.id}`); + console.log(dstHistory); + } + console.log(`source: ${sourceData.id} dest: ${newData.id}`); + console.log(result.errors); + console.log(JSON.stringify(sourceData)); + console.log(JSON.stringify(newData)); + } +}; - const newSourceData = new UserData(sourceUser); - await newSourceData.sync(); +test.concurrent('test preventing a double (clobber) robochimp migration (two bot-migration)', async () => { + const sourceUserId = mockedId(); + const destUserId = mockedId(); - const secondMigrateResult = await migrateUser(sourceUser.id, destUserId); - expect(secondMigrateResult).toEqual(true); + // Create source user, and populate data: + const sourceUser = await buildBaseUser(sourceUserId); + const srcHistory = await runRandomTestCommandsOnUser(sourceUser, 5, true); - const newDestData = new UserData(destUserId); - await newDestData.sync(); + const sourceData = new UserData(sourceUser); + await sourceData.sync(); - const newCompareResult = sourceData.equals(destData); - logResult(newCompareResult, newSourceData, newDestData); - expect(newCompareResult.result).toBe(true); + const migrateResult = await migrateUser(sourceUser.id, destUserId); + expect(migrateResult).toEqual(true); - expect(newDestData.githubId).toEqual(sourceData.githubId); - expect(newDestData.githubId).toEqual(destData.githubId); + const destData = new UserData(destUserId); + await destData.sync(); - // Make sure the 2nd transfer didn't overwrite robochimp: - expect(newDestData.githubId !== newSourceData.githubId).toBeTruthy(); + const compareResult = sourceData.equals(destData); + logResult(compareResult, sourceData, destData, srcHistory, []); + expect(compareResult.result).toBe(true); - // Verify migrated id is correct - expect(newDestData.migratedUserId).toEqual(BigInt(sourceData.id)); - }); - test('test migrating existing user to target with no records', async () => { - const sourceUser = await buildBaseUser(mockedId()); - await runAllTestCommandsOnUser(sourceUser); + // Now the actual test, everything above has to happen first... + await runAllTestCommandsOnUser(sourceUser); - const destUserId = mockedId(); + const newSourceData = new UserData(sourceUser); + await newSourceData.sync(); - const sourceData = new UserData(sourceUser); - await sourceData.sync(); + const secondMigrateResult = await migrateUser(sourceUser.id, destUserId); + expect(secondMigrateResult).toEqual(true); - const migrateResult = await migrateUser(sourceUser.id, destUserId); - expect(migrateResult).toEqual(true); + const newDestData = new UserData(destUserId); + await newDestData.sync(); - const newData = new UserData(destUserId); - await newData.sync(); + const newCompareResult = sourceData.equals(destData); + logResult(newCompareResult, newSourceData, newDestData); + expect(newCompareResult.result).toBe(true); - const compareResult = sourceData.equals(newData); - logResult(compareResult, sourceData, newData); + expect(newDestData.githubId).toEqual(sourceData.githubId); + expect(newDestData.githubId).toEqual(destData.githubId); - expect(compareResult.result).toBe(true); - }); + // Make sure the 2nd transfer didn't overwrite robochimp: + expect(newDestData.githubId !== newSourceData.githubId).toBeTruthy(); - test('test migrating full user on top of full profile', async () => { - const sourceUser = await buildBaseUser(mockedId()); - const destUser = await buildBaseUser(mockedId()); - await runAllTestCommandsOnUser(sourceUser); - await runAllTestCommandsOnUser(destUser); + // Verify migrated id is correct + expect(newDestData.migratedUserId).toEqual(BigInt(sourceData.id)); +}); +test.concurrent('test migrating existing user to target with no records', async () => { + const sourceUser = await buildBaseUser(mockedId()); + await runAllTestCommandsOnUser(sourceUser); - const sourceData = new UserData(sourceUser); - await sourceData.sync(); + const destUserId = mockedId(); - const migrateResult = await migrateUser(sourceUser.id, destUser.id); - expect(migrateResult).toEqual(true); + const sourceData = new UserData(sourceUser); + await sourceData.sync(); - const newData = new UserData(destUser.id); - await newData.sync(); - const compareResult = sourceData.equals(newData); - logResult(compareResult, sourceData, newData); + const migrateResult = await migrateUser(sourceUser.id, destUserId); + expect(migrateResult).toEqual(true); - expect(compareResult.result).toBe(true); + const newData = new UserData(destUserId); + await newData.sync(); - if (newData.poh) newData.poh.spellbook_altar = 33; - if (newData.userStats) newData.userStats.sacrificed_bank = new Bank().add('Cannonball').bank; - newData.skillsAsLevels!.cooking = 1_000_000; - newData.bingos = []; - newData.botItemSell = []; - if (newData.gear?.melee) newData.gear.melee.weapon = null; + const compareResult = sourceData.equals(newData); + logResult(compareResult, sourceData, newData); - const badResult = sourceData.equals(newData); - expect(badResult.result).toBe(false); + expect(compareResult.result).toBe(true); +}); - const expectedBadResult = [ - `Failed comparing ${sourceUser.id} vs ${destUser.id}:`, - "melee gear doesn't match", - "cooking level doesn't match. 1 vs 1000000", - "POH Object doesn't match: null !== 33", - 'User Stats doesn\'t match: {} !== {"2":1}', - 'Wrong number of BotItemSell rows. 1 vs 0' - ]; - expect(badResult.errors).toEqual(expectedBadResult); - }); +test.concurrent('test migrating full user on top of full profile', async () => { + const sourceUser = await buildBaseUser(mockedId()); + const destUser = await buildBaseUser(mockedId()); + await runAllTestCommandsOnUser(sourceUser); + await runAllTestCommandsOnUser(destUser); + + const sourceData = new UserData(sourceUser); + await sourceData.sync(); + + const migrateResult = await migrateUser(sourceUser.id, destUser.id); + expect(migrateResult).toEqual(true); + + const newData = new UserData(destUser.id); + await newData.sync(); + const compareResult = sourceData.equals(newData); + logResult(compareResult, sourceData, newData); + + expect(compareResult.result).toBe(true); + + if (newData.poh) newData.poh.spellbook_altar = 33; + if (newData.userStats) newData.userStats.sacrificed_bank = new Bank().add('Cannonball').bank; + newData.skillsAsLevels!.cooking = 1_000_000; + newData.bingos = []; + newData.botItemSell = []; + if (newData.gear?.melee) newData.gear.melee.weapon = null; + + const badResult = sourceData.equals(newData); + expect(badResult.result).toBe(false); + + const expectedBadResult = [ + `Failed comparing ${sourceUser.id} vs ${destUser.id}:`, + "melee gear doesn't match", + "cooking level doesn't match. 1 vs 1000000", + "POH Object doesn't match: null !== 33", + 'User Stats doesn\'t match: {} !== {"2":1}', + 'Wrong number of BotItemSell rows. 1 vs 0' + ]; + expect(badResult.errors).toEqual(expectedBadResult); +}); - test( - 'test migrating random user on top of empty profile', - async () => { - const sourceUser = await buildBaseUser(mockedId()); - const destUserId = mockedId(); +test.concurrent('test migrating random user on top of empty profile', async () => { + const sourceUser = await buildBaseUser(mockedId()); + const destUserId = mockedId(); - const sourceRolls = randInt(6, 11); - const cmdHistory = await runRandomTestCommandsOnUser(sourceUser, sourceRolls); + const sourceRolls = randInt(6, 11); + const cmdHistory = await runRandomTestCommandsOnUser(sourceUser, sourceRolls); - const sourceData = new UserData(sourceUser); - await sourceData.sync(); + const sourceData = new UserData(sourceUser); + await sourceData.sync(); - const result = await migrateUser(sourceUser, destUserId); + const result = await migrateUser(sourceUser, destUserId); - if (result !== true) throw new Error(`${sourceUser.id} - ${result}`); - expect(result).toEqual(true); + if (result !== true) throw new Error(`${sourceUser.id} - ${result}`); + expect(result).toEqual(true); - const newData = new UserData(destUserId); - await newData.sync(); + const newData = new UserData(destUserId); + await newData.sync(); - const compareResult = sourceData.equals(newData); - logResult(compareResult, sourceData, newData, cmdHistory, []); + const compareResult = sourceData.equals(newData); + logResult(compareResult, sourceData, newData, cmdHistory, []); - expect(compareResult.result).toBe(true); - }, - { repeats: 1 } - ); + expect(compareResult.result).toBe(true); +}); - test( - 'test migrating random user on top of random profile', - async () => { - const sourceUser = await buildBaseUser(mockedId()); - const destUser = await buildBaseUser(mockedId()); +test.concurrent('test migrating random user on top of random profile', async () => { + const sourceUser = await buildBaseUser(mockedId()); + const destUser = await buildBaseUser(mockedId()); - const sourceRolls = randInt(5, 12); - const destRolls = randInt(5, 12); + const sourceRolls = randInt(5, 12); + const destRolls = randInt(5, 12); - const srcHistory = await runRandomTestCommandsOnUser(sourceUser, sourceRolls); - const dstHistory = await runRandomTestCommandsOnUser(destUser, destRolls); + const srcHistory = await runRandomTestCommandsOnUser(sourceUser, sourceRolls); + const dstHistory = await runRandomTestCommandsOnUser(destUser, destRolls); - const sourceData = new UserData(sourceUser); - await sourceData.sync(); + const sourceData = new UserData(sourceUser); + await sourceData.sync(); - const result = await migrateUser(sourceUser, destUser); - expect(result).toEqual(true); + const result = await migrateUser(sourceUser, destUser); + expect(result).toEqual(true); - const newData = new UserData(destUser); - await newData.sync(); + const newData = new UserData(destUser); + await newData.sync(); - const compareResult = sourceData.equals(newData); - logResult(compareResult, sourceData, newData, srcHistory, dstHistory); + const compareResult = sourceData.equals(newData); + logResult(compareResult, sourceData, newData, srcHistory, dstHistory); - expect(compareResult.result).toBe(true); - }, - { repeats: 1 } - ); + expect(compareResult.result).toBe(true); +}); - test( - 'test migrating random user on top of full profile', - async () => { - const sourceUser = await buildBaseUser(mockedId()); - const destUser = await buildBaseUser(mockedId()); +test.concurrent('test migrating random user on top of full profile', async () => { + const sourceUser = await buildBaseUser(mockedId()); + const destUser = await buildBaseUser(mockedId()); - const cmdHistory = await runRandomTestCommandsOnUser(sourceUser); - await runAllTestCommandsOnUser(destUser); + const cmdHistory = await runRandomTestCommandsOnUser(sourceUser); + await runAllTestCommandsOnUser(destUser); - const sourceData = new UserData(sourceUser); - await sourceData.sync(); + const sourceData = new UserData(sourceUser); + await sourceData.sync(); - const result = await migrateUser(sourceUser, destUser); - expect(result).toEqual(true); + const result = await migrateUser(sourceUser, destUser); + expect(result).toEqual(true); - const newData = new UserData(destUser); - await newData.sync(); + const newData = new UserData(destUser); + await newData.sync(); - const compareResult = sourceData.equals(newData); - logResult(compareResult, sourceData, newData, cmdHistory, []); + const compareResult = sourceData.equals(newData); + logResult(compareResult, sourceData, newData, cmdHistory, []); - expect(compareResult.result).toBe(true); - }, - { repeats: 1 } - ); + expect(compareResult.result).toBe(true); }); diff --git a/tests/integration/setup.ts b/tests/integration/setup.ts index 982263ac04c..f080eafef8b 100644 --- a/tests/integration/setup.ts +++ b/tests/integration/setup.ts @@ -3,7 +3,6 @@ import '../globalSetup'; import { Image } from '@napi-rs/canvas'; import { noOp } from 'e'; -import mitm from 'mitm'; import { afterEach, beforeEach, vi } from 'vitest'; import { BankImageTask, bankImageTask } from '../../src/lib/bankImage'; @@ -70,16 +69,3 @@ async function init() { } init(); - -function setupRequestLogging() { - const mitmInstance = mitm(); - - mitmInstance.on('connect', (socket, opts) => { - if (opts?.host) { - // throw new Error(`Sending request to ${opts.host}`); - socket.bypass(); - } - }); -} - -setupRequestLogging(); diff --git a/tests/integration/tradeTransaction.test.ts b/tests/integration/tradeTransaction.test.ts index b684d57c154..0c478194479 100644 --- a/tests/integration/tradeTransaction.test.ts +++ b/tests/integration/tradeTransaction.test.ts @@ -133,12 +133,7 @@ describe('Transactionalized Trade Test', async () => { const expectedResult = { success: false, message: `<@${cyr}> doesn't own all items.` }; - expect(uCyr.GP).toBe(1_000_000); - expect(uMagna.GP).toBe(20_000_000); - - await uCyr.sync(); expect(result).toMatchObject(expectedResult); - console.log(cyrStartingBank.bank); expect(uCyr.bankWithGP.toString()).toEqual(cyrStartingBank.toString()); expect(uCyr.bankWithGP.equals(cyrStartingBank)).toBe(true); expect(uMagna.bankWithGP.equals(magnaStartingBank)).toBe(true); diff --git a/tests/integration/trading.test.ts b/tests/integration/trading.test.ts index beadfc25cf2..1cb9065481b 100644 --- a/tests/integration/trading.test.ts +++ b/tests/integration/trading.test.ts @@ -10,12 +10,13 @@ test('Trade consistency', async () => { await mockClient(); const bank = new Bank().add('Coins', 1000).add('Egg', 1000).add('Coal', 1000).add('Trout', 1000).freeze(); - const NUMBER_OF_USERS = 50; + const NUMBER_OF_USERS = 20; - const users: TestUser[] = []; + let users: TestUser[] = []; for (let i = 0; i < NUMBER_OF_USERS; i++) { - users.push(await createTestUser(bank)); + users.push(createTestUser(bank) as any); } + users = await Promise.all(users); function checkMatch() { const expectedBank = bank.clone().multiply(NUMBER_OF_USERS); diff --git a/tests/unit/images.test.ts b/tests/unit/images.test.ts index 2cce18d2942..3bdb23053d8 100644 --- a/tests/unit/images.test.ts +++ b/tests/unit/images.test.ts @@ -1,39 +1,26 @@ -import { configureToMatchImageSnapshot } from 'jest-image-snapshot'; +import { writeFile } from 'node:fs/promises'; import { Bank, Monsters } from 'oldschooljs'; -import { describe, expect, test } from 'vitest'; +import { describe, test } from 'vitest'; import { drawChestLootImage } from '../../src/lib/bankImage'; import { clImageGenerator } from '../../src/lib/collectionLogTask'; +import { BOT_TYPE } from '../../src/lib/constants'; import { pohImageGenerator } from '../../src/lib/pohImage'; import { mahojiChatHead } from '../../src/lib/util/chatHeadImage'; import { makeBankImage } from '../../src/lib/util/makeBankImage'; import { mockMUser } from './utils'; -declare module 'vitest' { - interface Assertion { - toMatchImageSnapshot(): T; - } -} - -const toMatchImageSnapshotPlugin = configureToMatchImageSnapshot({ - customSnapshotsDir: './tests/unit/snapshots', - noColors: true, - failureThreshold: 5, - failureThresholdType: 'percent' -}); -expect.extend({ toMatchImageSnapshot: toMatchImageSnapshotPlugin }); - describe('Images', () => { - test('Chat Heads', async () => { + test.concurrent('Chat Heads', async () => { const result = await mahojiChatHead({ content: 'Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test', head: 'santa' }); - expect(result.files[0].attachment).toMatchImageSnapshot(); + await writeFile(`tests/unit/snapshots/chatHead.${BOT_TYPE}.png`, result.files[0].attachment); }); - test('Collection Log', async () => { + test.concurrent('Collection Log', async () => { const result: any = await clImageGenerator.generateLogImage({ user: mockMUser({ cl: new Bank().add('Harmonised orb') }), collection: 'nightmare', @@ -49,10 +36,10 @@ describe('Images', () => { gotrRiftSearches: 1 } }); - expect(result.files[0].attachment).toMatchImageSnapshot(); + await writeFile(`tests/unit/snapshots/cl.${BOT_TYPE}.png`, result.files[0].attachment); }); - test('Bank Image', async () => { + test.concurrent('Bank Image', async () => { const bank = new Bank(); for (const item of [...Monsters.Cow.allItems]) { bank.add(item); @@ -63,10 +50,10 @@ describe('Images', () => { bank, title: 'Test Image' }); - expect(result.file.attachment).toMatchImageSnapshot(); + await writeFile(`tests/unit/snapshots/bank.${BOT_TYPE}.png`, result.file.attachment); }); - test('POH Image', async () => { + test.concurrent('POH Image', async () => { const result = await pohImageGenerator.run({ prayer_altar: 13_197, throne: 13_667, @@ -74,7 +61,7 @@ describe('Images', () => { mounted_cape: 29_210, background_id: 1 } as any); - expect(result).toMatchImageSnapshot(); + await writeFile(`tests/unit/snapshots/poh.${BOT_TYPE}.png`, result); }); // test('Chart Image', async () => { @@ -85,7 +72,7 @@ describe('Images', () => { // expect(result).toMatchImageSnapshot(); // }); - test('TOA Image', async () => { + test.concurrent('TOA Image', async () => { const image = await drawChestLootImage({ entries: [ { @@ -103,10 +90,10 @@ describe('Images', () => { ], type: 'Tombs of Amascut' }); - expect(image.attachment as Buffer).toMatchImageSnapshot(); + await writeFile(`tests/unit/snapshots/toa.${BOT_TYPE}.png`, image.attachment); }); - test('COX Image', async () => { + test.concurrent('COX Image', async () => { const image = await drawChestLootImage({ entries: [ { @@ -124,6 +111,6 @@ describe('Images', () => { ], type: 'Chambers of Xerician' }); - expect(image.attachment as Buffer).toMatchImageSnapshot(); + await writeFile(`tests/unit/snapshots/cox.${BOT_TYPE}.png`, image.attachment); }); }); diff --git a/tests/unit/parseStringBank.test.ts b/tests/unit/parseStringBank.test.ts index 13c5abb9145..8123f195715 100644 --- a/tests/unit/parseStringBank.test.ts +++ b/tests/unit/parseStringBank.test.ts @@ -16,7 +16,7 @@ const get = getOSItem; const pQI = parseQuantityAndItem; describe('Bank Parsers', () => { - test('parseStringBank', async () => { + test.concurrent('parseStringBank', async () => { const output = psb(` 1x twisted bow, coal, 5k egg, 1b trout, 5 ${itemID('Feather')} `); const expected = [ [get('Twisted bow'), 1], @@ -36,7 +36,7 @@ describe('Bank Parsers', () => { } }); - test('parseStringBank2', async () => { + test.concurrent('parseStringBank2', async () => { expect(psb('')).toEqual([]); expect(psb(' ')).toEqual([]); expect(psb(', ')).toEqual([]); @@ -70,7 +70,7 @@ describe('Bank Parsers', () => { ]); }); - test('parseBank - flags', async () => { + test.concurrent('parseBank - flags', async () => { const bank = new Bank().add('Steel arrow').add('Bones').add('Coal').add('Clue scroll (easy)'); const res = parseBank({ inputBank: bank, @@ -91,7 +91,7 @@ describe('Bank Parsers', () => { expect(res3.length).toEqual(1); }); - test('parseBank - filters', async () => { + test.concurrent('parseBank - filters', async () => { const bank = new Bank().add('Steel arrow').add('Bones').add('Coal').add('Clue scroll (easy)'); const res = parseBank({ inputBank: bank, @@ -101,7 +101,7 @@ describe('Bank Parsers', () => { expect(res.amount('Clue scroll (easy)')).toEqual(1); }); - test('parseBank - search', async () => { + test.concurrent('parseBank - search', async () => { const bank = new Bank() .add('Steel arrow') .add('Bones') @@ -120,7 +120,7 @@ describe('Bank Parsers', () => { expect(res.amount('Rune arrow')).toEqual(1); }); - test('parseBank - inputStr', async () => { + test.concurrent('parseBank - inputStr', async () => { const bank = new Bank() .add('Steel arrow') .add('Bones', 2) @@ -147,7 +147,7 @@ describe('Bank Parsers', () => { expect(res2.amount('Bones')).toEqual(2); }); - test('parseBank - other', async () => { + test.concurrent('parseBank - other', async () => { const bank = new Bank() .add('Steel arrow') .add('Bones', 2) @@ -165,7 +165,7 @@ describe('Bank Parsers', () => { expect(res.amount('Coal')).toEqual(6); }); - test('parseBank - same item names', async () => { + test.concurrent('parseBank - same item names', async () => { const bank = new Bank().add(22_002); const res = parseBank({ inputBank: bank, @@ -176,7 +176,7 @@ describe('Bank Parsers', () => { expect(res.amount(22_002)).toEqual(1); }); - test('parseBank - extra number', async () => { + test.concurrent('parseBank - extra number', async () => { const bank = new Bank().add('Coal', 5).add('3rd age platebody', 100).add('Egg', 3); const res = parseBank({ inputBank: bank, @@ -192,7 +192,7 @@ describe('Bank Parsers', () => { expect(other.amount('Egg')).toEqual(3); }); - test('parseBank - look for nonexistent items', async () => { + test.concurrent('parseBank - look for nonexistent items', async () => { const bank = new Bank().add('Steel arrow').add('Bones').add('Coal', 500).add('Clue scroll (easy)'); expect(parseBank({ inputBank: bank, inputStr: '1 Portrait' }).toString()).toEqual('No items'); expect(parseBank({ inputBank: bank, inputStr: '1 666' }).toString()).toEqual('No items'); @@ -200,7 +200,7 @@ describe('Bank Parsers', () => { expect(parseBank({ inputBank: bank, inputStr: '0 cOaL' }).toString()).toEqual('500x Coal'); }); - test('parseBank - check item aliases', async () => { + test.concurrent('parseBank - check item aliases', async () => { const bank = new Bank().add('Arceuus graceful top', 30).add('Bones'); expect(parseBank({ inputBank: bank, inputStr: 'pUrPle gRaceful top' }).toString()).toEqual( '30x Arceuus graceful top' @@ -219,7 +219,7 @@ describe('Bank Parsers', () => { ); }); - test('parseQuantityAndItem', () => { + test.concurrent('parseQuantityAndItem', () => { expect(pQI('')).toEqual([]); expect(pQI(' ,,, ')).toEqual([]); expect(pQI('1.5k twisted bow')).toEqual([[get('Twisted bow')], 1500]); @@ -260,7 +260,7 @@ describe('Bank Parsers', () => { expect(pQI('1.5k*10 twisted bow', testBank)).toEqual([[get('Twisted bow')], 10_000 * 1.5]); }); - test('parseInputCostBank', () => { + test.concurrent('parseInputCostBank', () => { const usersBank = new Bank() .add('Coal', 100) .add('Egg', 3) diff --git a/tests/unit/snapshots/bank.OSB.png b/tests/unit/snapshots/bank.OSB.png new file mode 100644 index 0000000000000000000000000000000000000000..83bbe180cf6ff83e0ecdc8ab6e3403f3648cc35a GIT binary patch literal 16248 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 literal 0 HcmV?d00001 diff --git a/tests/unit/snapshots/chatHead.OSB.png b/tests/unit/snapshots/chatHead.OSB.png new file mode 100644 index 0000000000000000000000000000000000000000..38567d0861b5224ce54ba50006fbf6572585a287 GIT binary patch literal 62759 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

LhRaD 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 q*LQypKJ)26gf! literal 0 HcmV?d00001 diff --git a/tests/unit/snapshots/cl.OSB.png b/tests/unit/snapshots/cl.OSB.png new file mode 100644 index 0000000000000000000000000000000000000000..08fb648d09d0857ee196eaf2bd4402e7b2fb8779 GIT binary patch literal 49082 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 y=Hm~2h9g8xoXHt0BR?Rjv#+v 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 Qjt0F_Qh!mcU>W}Z0Q5VF@Bjb+ diff --git a/tests/unit/snapshots/images-test-ts-tests-unit-images-test-ts-images-chart-image-1-snap.png b/tests/unit/snapshots/images-test-ts-tests-unit-images-test-ts-images-chart-image-1-snap.png deleted file mode 100644 index 10bfb5ae32fc56cff5945575e0f5a5feee9aeb1d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21256 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 JtaD0e0syC*3K{?a diff --git a/tests/unit/snapshots/images-test-ts-tests-unit-images-test-ts-images-chat-heads-1-snap.png b/tests/unit/snapshots/images-test-ts-tests-unit-images-test-ts-images-chat-heads-1-snap.png deleted file mode 100644 index 36fdf927b56269bfbfa4cd8802618310100e2bb1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 63003 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 s@xSZ4zXzZC@~ diff --git a/tests/unit/snapshots/images-test-ts-tests-unit-images-test-ts-images-collection-log-1-snap.png b/tests/unit/snapshots/images-test-ts-tests-unit-images-test-ts-images-collection-log-1-snap.png deleted file mode 100644 index 7ad554eedee9ca28c715c8b654d31fd74454cc69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48541 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 diff --git a/tests/unit/snapshots/images-test-ts-tests-unit-images-test-ts-images-cox-image-1-snap.png b/tests/unit/snapshots/images-test-ts-tests-unit-images-test-ts-images-cox-image-1-snap.png deleted file mode 100644 index dd11a384998345391f319391447966256eac280f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39175 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ⅆ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