diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index aa786e34..23d77e1b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,8 +20,8 @@ jobs: working-directory: ./renderer - run: npm run make-index-files working-directory: ./renderer - # - run: npm run lint - # working-directory: ./renderer + - run: npm run lint + working-directory: ./renderer - run: npm run build working-directory: ./renderer - uses: actions/upload-artifact@v4 diff --git a/main/src/proxy.ts b/main/src/proxy.ts index 7b78320d..de81502a 100644 --- a/main/src/proxy.ts +++ b/main/src/proxy.ts @@ -28,6 +28,7 @@ export class HttpProxy { delete req.headers[key] } } + const url = req.url.slice('/proxy/'.length); const proxyReq = net.request({ url: 'https://' + req.url.slice('/proxy/'.length), @@ -47,6 +48,7 @@ export class HttpProxy { }) proxyReq.addListener('error', (err) => { logger.write(`error [cors-proxy] ${err.message} (${host})`) + logger.write(`[DEBUG] error [cors-proxy] ${url}`) res.destroy(err) }) req.pipe(proxyReq as unknown as NodeJS.WritableStream) diff --git a/renderer/.eslintrc.js b/renderer/.eslintrc.js index 70290e65..a04cb6ed 100644 --- a/renderer/.eslintrc.js +++ b/renderer/.eslintrc.js @@ -1,60 +1,56 @@ module.exports = { - root: true, + root: true, + env: { + node: true + }, + plugins: [ + '@typescript-eslint', + // 'only-warn' + ], + extends: [ + 'plugin:vue/base', + 'standard-with-typescript', + ], + rules: { + 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', + 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', + 'quote-props': ['error', 'consistent-as-needed'], + 'no-labels': ['error', { allowLoop: true }], + 'multiline-ternary': 'off', + 'no-unused-vars': 'off', + 'no-undef': 'off', + '@typescript-eslint/no-unused-vars': ['error'], + '@typescript-eslint/strict-boolean-expressions': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/restrict-template-expressions': 'off', + '@typescript-eslint/prefer-nullish-coalescing': 'off', + '@typescript-eslint/prefer-optional-chain': 'off', + '@typescript-eslint/prefer-readonly': 'off', + '@typescript-eslint/no-floating-promises': 'off', + '@typescript-eslint/no-misused-promises': 'off', + '@typescript-eslint/prefer-reduce-type-parameter': 'off', + '@typescript-eslint/no-invalid-void-type': 'off', + '@typescript-eslint/consistent-indexed-object-style': 'off', + 'import/first': 'off', + 'import/no-duplicates': 'off', + 'func-call-spacing': 'off', + // TODO: refactor IPC and enable + '@typescript-eslint/consistent-type-assertions': 'off', + }, + overrides: [{ + files: ['src/main/**/*'], env: { - node: true - }, - plugins: [ - '@typescript-eslint', - 'prettier' - // 'only-warn' - ], - extends: [ - 'plugin:vue/base', - 'standard-with-typescript', - 'plugin:prettier/recommended', - 'eslint-plugin-prettier/recommended', - ], - rules: { - 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', - 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', - 'quote-props': ['error', 'consistent-as-needed'], - 'no-labels': ['error', { allowLoop: true }], - 'multiline-ternary': 'off', - 'no-unused-vars': 'off', - 'no-undef': 'off', - '@typescript-eslint/no-unused-vars': ['error'], - '@typescript-eslint/strict-boolean-expressions': 'off', - '@typescript-eslint/no-non-null-assertion': 'off', - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/restrict-template-expressions': 'off', - '@typescript-eslint/prefer-nullish-coalescing': 'off', - '@typescript-eslint/prefer-optional-chain': 'off', - '@typescript-eslint/prefer-readonly': 'off', - '@typescript-eslint/no-floating-promises': 'off', - '@typescript-eslint/no-misused-promises': 'off', - '@typescript-eslint/prefer-reduce-type-parameter': 'off', - '@typescript-eslint/no-invalid-void-type': 'off', - '@typescript-eslint/consistent-indexed-object-style': 'off', - 'import/first': 'off', - 'import/no-duplicates': 'off', - 'func-call-spacing': 'off', - // TODO: refactor IPC and enable - '@typescript-eslint/consistent-type-assertions': 'off', - "indent": ["error", "tab"] - }, - overrides: [{ - files: ['src/main/**/*'], - env: { - node: true - } - }, { - files: ['*.ts'], - parserOptions: { - project: './tsconfig.json' - } - }], - parserOptions: { - parser: '@typescript-eslint/parser', - extraFileExtensions: ['.vue'] + node: true } + }, { + files: ['*.ts'], + parserOptions: { + project: './tsconfig.json' + } + }], + parserOptions: { + parser: '@typescript-eslint/parser', + extraFileExtensions: ['.vue'] + } } diff --git a/renderer/.prettierrc b/renderer/.prettierrc deleted file mode 100644 index 404c7714..00000000 --- a/renderer/.prettierrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "singleQuote": true, - "endOfLine": "lf", - "tabWidth": 2, - "useTabs": true, - "trailingComma": "es5" -} \ No newline at end of file diff --git a/renderer/package.json b/renderer/package.json index 8f87fc9f..28e158f8 100644 --- a/renderer/package.json +++ b/renderer/package.json @@ -5,6 +5,7 @@ "scripts": { "dev": "vite", "lint": "eslint --ext .ts,.vue src", + "lint-fix": "eslint --ext .ts,.vue src --fix", "build": "vue-tsc --noEmit && vite build", "make-index-files": "node src/assets/make-index-files.mjs" }, @@ -34,10 +35,7 @@ "@types/object-hash": "^3.0.0", "@vitejs/plugin-vue": "^4.0.0", "autoprefixer": "^10.0.2", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-prettier": "^5.2.1", "postcss": "^8.2.14", - "prettier": "3.4.2", "typescript": "5.6.x", "vite": "^5.0.0", "vue-tsc": "^2.0.0" diff --git a/renderer/src/parser/Parser.ts b/renderer/src/parser/Parser.ts index 06ad27a1..150202c7 100644 --- a/renderer/src/parser/Parser.ts +++ b/renderer/src/parser/Parser.ts @@ -299,8 +299,8 @@ function parseNamePlate (section: string[]) { const item: ParserState = { rarity: undefined, category: undefined, - name: name, - baseType: baseType, + name, + baseType, isUnidentified: false, isCorrupted: false, newMods: [], diff --git a/renderer/src/parser/meta.ts b/renderer/src/parser/meta.ts index c7000eeb..ed2714d4 100644 --- a/renderer/src/parser/meta.ts +++ b/renderer/src/parser/meta.ts @@ -1,97 +1,97 @@ export enum ItemCategory { - Map = 'Map', - CapturedBeast = 'Captured Beast', - MetamorphSample = 'Metamorph Sample', - Helmet = 'Helmet', - BodyArmour = 'Body Armour', - Gloves = 'Gloves', - Boots = 'Boots', - Shield = 'Shield', - Amulet = 'Amulet', - Belt = 'Belt', - Ring = 'Ring', - Flask = 'Flask', - AbyssJewel = 'Abyss Jewel', - Jewel = 'Jewel', - Quiver = 'Quiver', - Claw = 'Claw', - Bow = 'Bow', - Sceptre = 'Sceptre', - Wand = 'Wand', - FishingRod = 'Fishing Rod', - Staff = 'Staff', - Warstaff = 'Warstaff', - Dagger = 'Dagger', - RuneDagger = 'Rune Dagger', - OneHandedAxe = 'One-Handed Axe', - TwoHandedAxe = 'Two-Handed Axe', - OneHandedMace = 'One-Handed Mace', - TwoHandedMace = 'Two-Handed Mace', - OneHandedSword = 'One-Handed Sword', - TwoHandedSword = 'Two-Handed Sword', - ClusterJewel = 'Cluster Jewel', - HeistBlueprint = 'Heist Blueprint', - HeistContract = 'Heist Contract', - HeistTool = 'Heist Tool', - HeistBrooch = 'Heist Brooch', - HeistGear = 'Heist Gear', - HeistCloak = 'Heist Cloak', - Trinket = 'Trinket', - Invitation = 'Invitation', - Gem = 'Gem', - Currency = 'Currency', - DivinationCard = 'Divination Card', - Voidstone = 'Voidstone', - Sentinel = 'Sentinel', - MemoryLine = 'Memory Line', - SanctumRelic = 'Sanctum Relic', - Tincture = 'Tincture', - Charm = 'Charm', - Crossbow = 'Crossbow', + Map = 'Map', + CapturedBeast = 'Captured Beast', + MetamorphSample = 'Metamorph Sample', + Helmet = 'Helmet', + BodyArmour = 'Body Armour', + Gloves = 'Gloves', + Boots = 'Boots', + Shield = 'Shield', + Amulet = 'Amulet', + Belt = 'Belt', + Ring = 'Ring', + Flask = 'Flask', + AbyssJewel = 'Abyss Jewel', + Jewel = 'Jewel', + Quiver = 'Quiver', + Claw = 'Claw', + Bow = 'Bow', + Sceptre = 'Sceptre', + Wand = 'Wand', + FishingRod = 'Fishing Rod', + Staff = 'Staff', + Warstaff = 'Warstaff', + Dagger = 'Dagger', + RuneDagger = 'Rune Dagger', + OneHandedAxe = 'One-Handed Axe', + TwoHandedAxe = 'Two-Handed Axe', + OneHandedMace = 'One-Handed Mace', + TwoHandedMace = 'Two-Handed Mace', + OneHandedSword = 'One-Handed Sword', + TwoHandedSword = 'Two-Handed Sword', + ClusterJewel = 'Cluster Jewel', + HeistBlueprint = 'Heist Blueprint', + HeistContract = 'Heist Contract', + HeistTool = 'Heist Tool', + HeistBrooch = 'Heist Brooch', + HeistGear = 'Heist Gear', + HeistCloak = 'Heist Cloak', + Trinket = 'Trinket', + Invitation = 'Invitation', + Gem = 'Gem', + Currency = 'Currency', + DivinationCard = 'Divination Card', + Voidstone = 'Voidstone', + Sentinel = 'Sentinel', + MemoryLine = 'Memory Line', + SanctumRelic = 'Sanctum Relic', + Tincture = 'Tincture', + Charm = 'Charm', + Crossbow = 'Crossbow', } export const WEAPON_ONE_HANDED_MELEE = new Set([ - ItemCategory.OneHandedAxe, - ItemCategory.OneHandedMace, - ItemCategory.OneHandedSword, - ItemCategory.Claw, - ItemCategory.Dagger, - ItemCategory.RuneDagger, - ItemCategory.Sceptre, -]); + ItemCategory.OneHandedAxe, + ItemCategory.OneHandedMace, + ItemCategory.OneHandedSword, + ItemCategory.Claw, + ItemCategory.Dagger, + ItemCategory.RuneDagger, + ItemCategory.Sceptre +]) export const WEAPON_ONE_HANDED = new Set([ - ItemCategory.Wand, - ...WEAPON_ONE_HANDED_MELEE, -]); + ItemCategory.Wand, + ...WEAPON_ONE_HANDED_MELEE +]) export const WEAPONE_TWO_HANDED_MELEE = new Set([ - ItemCategory.TwoHandedAxe, - ItemCategory.TwoHandedMace, - ItemCategory.TwoHandedSword, - ItemCategory.Staff, - ItemCategory.Warstaff, -]); + ItemCategory.TwoHandedAxe, + ItemCategory.TwoHandedMace, + ItemCategory.TwoHandedSword, + ItemCategory.Staff, + ItemCategory.Warstaff +]) export const WEAPON = new Set([ - ItemCategory.FishingRod, - ItemCategory.Bow, - ...WEAPON_ONE_HANDED, - ...WEAPONE_TWO_HANDED_MELEE, -]); + ItemCategory.FishingRod, + ItemCategory.Bow, + ...WEAPON_ONE_HANDED, + ...WEAPONE_TWO_HANDED_MELEE +]) export const ARMOUR = new Set([ - ItemCategory.BodyArmour, - ItemCategory.Boots, - ItemCategory.Gloves, - ItemCategory.Helmet, - ItemCategory.Shield, -]); + ItemCategory.BodyArmour, + ItemCategory.Boots, + ItemCategory.Gloves, + ItemCategory.Helmet, + ItemCategory.Shield +]) export const ACCESSORY = new Set([ - ItemCategory.Amulet, - ItemCategory.Belt, - ItemCategory.Ring, - ItemCategory.Trinket, - // ItemCategory.Quiver -]); + ItemCategory.Amulet, + ItemCategory.Belt, + ItemCategory.Ring, + ItemCategory.Trinket + // ItemCategory.Quiver +]) diff --git a/renderer/src/parser/modifiers.ts b/renderer/src/parser/modifiers.ts index 083e570b..99b696f0 100644 --- a/renderer/src/parser/modifiers.ts +++ b/renderer/src/parser/modifiers.ts @@ -109,7 +109,7 @@ export function translateStatWithRoll ( calc.sources.some(s => s.stat.stat.ref === calc.stat.ref && s.stat.roll!.dp) : undefined - return { string: translation.string, negate: translation.negate || false, dp: dp } + return { string: translation.string, negate: translation.negate || false, dp } } export enum ModifierType { diff --git a/renderer/src/web/Config.ts b/renderer/src/web/Config.ts index b4303cfd..5b1f888f 100644 --- a/renderer/src/web/Config.ts +++ b/renderer/src/web/Config.ts @@ -1,731 +1,731 @@ -import { reactive as deepReactive, shallowRef, toRaw } from 'vue'; -import isDeepEqual from 'fast-deep-equal'; -import { Host } from '@/web/background/IPC'; -import { HostConfig, ShortcutAction } from '@ipc/types'; -import type * as widget from './overlay/widgets'; -import type { StashSearchWidget } from './stash-search/widget'; -import type { ItemCheckWidget } from './item-check/widget'; -import type { ItemSearchWidget } from './item-search/widget'; +import { reactive as deepReactive, shallowRef, toRaw } from 'vue' +import isDeepEqual from 'fast-deep-equal' +import { Host } from '@/web/background/IPC' +import { HostConfig, ShortcutAction } from '@ipc/types' +import type * as widget from './overlay/widgets' +import type { StashSearchWidget } from './stash-search/widget' +import type { ItemCheckWidget } from './item-check/widget' +import type { ItemSearchWidget } from './item-search/widget' -const _config = shallowRef(null); -let _lastSavedConfig: Config | null = null; +const _config = shallowRef(null) +let _lastSavedConfig: Config | null = null -export function AppConfig(): Config; -export function AppConfig(type: string): T | undefined; -export function AppConfig(type?: string) { - if (!type) { - return _config.value!; - } else { - return _config.value!.widgets.find((w) => w.wmType === type); - } +export function AppConfig (): Config +export function AppConfig (type: string): T | undefined +export function AppConfig (type?: string) { + if (!type) { + return _config.value! + } else { + return _config.value!.widgets.find((w) => w.wmType === type) + } } -export function updateConfig(updates: Config) { - _config.value = deepReactive(JSON.parse(JSON.stringify(updates))); - document.documentElement.style.fontSize = `${_config.value!.fontSize}px`; +export function updateConfig (updates: Config) { + _config.value = deepReactive(JSON.parse(JSON.stringify(updates))) + document.documentElement.style.fontSize = `${_config.value!.fontSize}px` } -export function saveConfig(opts?: { isTemporary: boolean }) { - const rawConfig = toRaw(_config.value!); - if ( - rawConfig.widgets.some( - (w) => w.wmZorder === 'exclusive' && w.wmWants === 'show' - ) - ) { - return; - } +export function saveConfig (opts?: { isTemporary: boolean }) { + const rawConfig = toRaw(_config.value!) + if ( + rawConfig.widgets.some( + (w) => w.wmZorder === 'exclusive' && w.wmWants === 'show' + ) + ) { + return + } - if (!isDeepEqual(rawConfig, _lastSavedConfig)) { - _lastSavedConfig = JSON.parse(JSON.stringify(rawConfig)); - Host.sendEvent({ - name: 'CLIENT->MAIN::save-config', - payload: { - contents: JSON.stringify(AppConfig()), - isTemporary: opts?.isTemporary ?? false, - }, - }); - } + if (!isDeepEqual(rawConfig, _lastSavedConfig)) { + _lastSavedConfig = JSON.parse(JSON.stringify(rawConfig)) + Host.sendEvent({ + name: 'CLIENT->MAIN::save-config', + payload: { + contents: JSON.stringify(AppConfig()), + isTemporary: opts?.isTemporary ?? false + } + }) + } } -export function pushHostConfig() { - Host.sendEvent({ - name: 'CLIENT->MAIN::update-host-config', - payload: getConfigForHost(), - }); +export function pushHostConfig () { + Host.sendEvent({ + name: 'CLIENT->MAIN::update-host-config', + payload: getConfigForHost() + }) } -export async function initConfig() { - Host.onEvent('MAIN->CLIENT::config-changed', (e) => { - _lastSavedConfig = JSON.parse(e.contents); // should be a deep copy - updateConfig(JSON.parse(e.contents)); - }); +export async function initConfig () { + Host.onEvent('MAIN->CLIENT::config-changed', (e) => { + _lastSavedConfig = JSON.parse(e.contents) // should be a deep copy + updateConfig(JSON.parse(e.contents)) + }) - const contents = await Host.getConfig(); - if (!contents) { - updateConfig(defaultConfig()); - return; - } + const contents = await Host.getConfig() + if (!contents) { + updateConfig(defaultConfig()) + return + } - let config: Config; - try { - config = JSON.parse(contents); - } catch { - updateConfig(defaultConfig()); - saveConfig({ isTemporary: true }); - return; + let config: Config + try { + config = JSON.parse(contents) + } catch { + updateConfig(defaultConfig()) + saveConfig({ isTemporary: true }) + return - // TODO - // dialog.showErrorBox( - // 'Awakened PoE2 Trade2 - Incompatible configuration', - // // ---------------------- - // 'You are trying to use an older version of Awakened PoE2 Trade2 with a newer incompatible configuration file.\n' + - // 'You need to install the latest version to continue using it.' - // ) - } + // TODO + // dialog.showErrorBox( + // 'Awakened PoE2 Trade2 - Incompatible configuration', + // // ---------------------- + // 'You are trying to use an older version of Awakened PoE2 Trade2 with a newer incompatible configuration file.\n' + + // 'You need to install the latest version to continue using it.' + // ) + } - updateConfig(upgradeConfig(config)); + updateConfig(upgradeConfig(config)) } -export function poeWebApi() { - const { language, realm } = AppConfig(); - switch (language) { - case 'en': - return 'www.pathofexile.com'; - case 'ru': - return 'ru.pathofexile.com'; - case 'cmn-Hant': - return realm === 'pc-garena' ? 'pathofexile.tw' : 'www.pathofexile.com'; - case 'ko': - return 'poe.game.daum.net'; - } +export function poeWebApi () { + const { language, realm } = AppConfig() + switch (language) { + case 'en': + return 'www.pathofexile.com' + case 'ru': + return 'ru.pathofexile.com' + case 'cmn-Hant': + return realm === 'pc-garena' ? 'pathofexile.tw' : 'www.pathofexile.com' + case 'ko': + return 'poe.game.daum.net' + } } export interface Config { - configVersion: number; - leagueId?: string; - overlayKey: string; - overlayBackground: string; - overlayBackgroundClose: boolean; - restoreClipboard: boolean; - commands: Array<{ - text: string; - hotkey: string | null; - send: boolean; - }>; - clientLog: string | null; - gameConfig: string | null; - windowTitle: string; - logKeys: boolean; - accountName: string; - stashScroll: boolean; - language: 'en' | 'ru' | 'cmn-Hant' | 'ko'; - realm: 'pc-ggg' | 'pc-garena'; - widgets: widget.Widget[]; - fontSize: number; - showAttachNotification: boolean; + configVersion: number + leagueId?: string + overlayKey: string + overlayBackground: string + overlayBackgroundClose: boolean + restoreClipboard: boolean + commands: Array<{ + text: string + hotkey: string | null + send: boolean + }> + clientLog: string | null + gameConfig: string | null + windowTitle: string + logKeys: boolean + accountName: string + stashScroll: boolean + language: 'en' | 'ru' | 'cmn-Hant' | 'ko' + realm: 'pc-ggg' | 'pc-garena' + widgets: widget.Widget[] + fontSize: number + showAttachNotification: boolean } export const defaultConfig = (): Config => ({ - configVersion: 16, - overlayKey: 'Shift + Space', - overlayBackground: 'rgba(129, 139, 149, 0.15)', - overlayBackgroundClose: true, - restoreClipboard: false, - showAttachNotification: true, - commands: [ - { - text: '/hideout', - hotkey: 'F5', - send: true, - }, - { - text: '/exit', - hotkey: 'F9', - send: true, - }, - { - text: '@last ty', - hotkey: null, - send: true, - }, - { - text: '/invite @last', - hotkey: null, - send: true, - }, - { - text: '/tradewith @last', - hotkey: null, - send: true, - }, - { - text: '/hideout @last', - hotkey: null, - send: true, - }, - ], - clientLog: null, - gameConfig: null, - windowTitle: 'Path of Exile 2', - logKeys: false, - accountName: '', - stashScroll: true, - language: 'en', - realm: 'pc-ggg', - fontSize: 16, - widgets: [ - // --- REQUIRED --- - { - wmId: 1, - wmType: 'menu', - wmTitle: '', - wmWants: 'show', - wmZorder: 1, - wmFlags: ['invisible-on-blur', 'skip-menu'], - anchor: { - pos: 'tl', - x: 5, - y: 5, - }, - alwaysShow: false, - } as widget.WidgetMenu, - { - wmId: 2, - wmType: 'price-check', - wmTitle: '', - wmWants: 'hide', - wmZorder: 'exclusive', - wmFlags: ['hide-on-blur', 'skip-menu'], - showRateLimitState: false, - apiLatencySeconds: 2, - collapseListings: 'api', - smartInitialSearch: true, - lockedInitialSearch: true, - activateStockFilter: false, - builtinBrowser: false, - hotkey: 'D', - hotkeyHold: 'Ctrl', - hotkeyLocked: 'Ctrl + Alt + D', - showSeller: false, - searchStatRange: 10, - showCursor: true, - requestPricePrediction: false, - rememberCurrency: false, - } as widget.PriceCheckWidget, - { - wmId: 3, - wmType: 'item-check', - wmTitle: '', - wmWants: 'hide', - wmZorder: 'exclusive', - wmFlags: ['hide-on-blur', 'skip-menu'], - hotkey: null, - wikiKey: null, - poedbKey: null, - craftOfExileKey: null, - stashSearchKey: null, - maps: { - profile: 1, - showNewStats: false, - selectedStats: [ - { - matcher: '#% maximum Player Resistances', - decision: 'w--', - }, - { - matcher: 'Monsters reflect #% of Physical Damage', - decision: 'd--', - }, - { - matcher: 'Monsters reflect #% of Elemental Damage', - decision: 'd--', - }, - { - matcher: 'Area contains two Unique Bosses', - decision: 'g--', - }, - ], - }, - } as ItemCheckWidget, - { - wmId: 4, - wmType: 'delve-grid', - wmTitle: '', - wmWants: 'hide', - wmZorder: 4, - wmFlags: ['hide-on-focus', 'skip-menu'], - toggleKey: null, - } as widget.DelveGridWidget, - { - wmId: 5, - wmType: 'settings', - wmTitle: '', - wmWants: 'hide', - wmZorder: 'exclusive', - wmFlags: ['invisible-on-blur', 'ignore-ui-visibility'], - }, - { - wmId: 6, - wmType: 'item-search', - wmTitle: '', - wmWants: 'hide', - wmZorder: 6, - wmFlags: ['invisible-on-blur'], - anchor: { - pos: 'tl', - x: 10, - y: 20, - }, - } as ItemSearchWidget, - // --- DEFAULT --- - { - wmId: 101, - wmType: 'stash-search', - wmTitle: 'Map rolling', - wmWants: 'hide', - wmZorder: 101, - wmFlags: ['invisible-on-blur'], - anchor: { - pos: 'tl', - x: 35, - y: 46, - }, - entries: [ - { id: 1, name: '', text: '"Pack Size: +3"', hotkey: null }, - { id: 2, name: '', text: 'Reflect', hotkey: null }, - { id: 3, name: '', text: '"Cannot Leech Life"', hotkey: null }, - { id: 4, name: '', text: '"Cannot Leech Mana"', hotkey: null }, - ], - } as StashSearchWidget, - { - wmId: 102, - wmType: 'stash-search', - wmTitle: 'Dump sorting', - wmWants: 'hide', - wmZorder: 102, - wmFlags: ['invisible-on-blur'], - anchor: { - pos: 'tl', - x: 34, - y: 56, - }, - entries: [ - { id: 1, name: '', text: 'Currency', hotkey: null }, - { id: 2, name: '', text: '"Divination Card"', hotkey: null }, - { id: 3, name: '', text: 'Fossil', hotkey: null }, - { id: 4, name: '', text: '"Map Tier"', hotkey: null }, - { - id: 5, - name: '', - text: '"Map Device" "Rarity: Normal"', - hotkey: null, - }, - { id: 6, name: '', text: 'Tane Laboratory', hotkey: null }, - ], - } as StashSearchWidget, - { - wmId: 103, - wmType: 'image-strip', - wmTitle: 'Cheat sheets', - wmWants: 'hide', - wmZorder: 103, - wmFlags: ['invisible-on-blur'], - anchor: { - pos: 'tc', - x: 50, - y: 10, - }, - images: [{ id: 1, url: 'syndicate.jpg' }], - } as widget.ImageStripWidget, - ], -}); + configVersion: 16, + overlayKey: 'Shift + Space', + overlayBackground: 'rgba(129, 139, 149, 0.15)', + overlayBackgroundClose: true, + restoreClipboard: false, + showAttachNotification: true, + commands: [ + { + text: '/hideout', + hotkey: 'F5', + send: true + }, + { + text: '/exit', + hotkey: 'F9', + send: true + }, + { + text: '@last ty', + hotkey: null, + send: true + }, + { + text: '/invite @last', + hotkey: null, + send: true + }, + { + text: '/tradewith @last', + hotkey: null, + send: true + }, + { + text: '/hideout @last', + hotkey: null, + send: true + } + ], + clientLog: null, + gameConfig: null, + windowTitle: 'Path of Exile 2', + logKeys: false, + accountName: '', + stashScroll: true, + language: 'en', + realm: 'pc-ggg', + fontSize: 16, + widgets: [ + // --- REQUIRED --- + { + wmId: 1, + wmType: 'menu', + wmTitle: '', + wmWants: 'show', + wmZorder: 1, + wmFlags: ['invisible-on-blur', 'skip-menu'], + anchor: { + pos: 'tl', + x: 5, + y: 5 + }, + alwaysShow: false + } as widget.WidgetMenu, + { + wmId: 2, + wmType: 'price-check', + wmTitle: '', + wmWants: 'hide', + wmZorder: 'exclusive', + wmFlags: ['hide-on-blur', 'skip-menu'], + showRateLimitState: false, + apiLatencySeconds: 2, + collapseListings: 'api', + smartInitialSearch: true, + lockedInitialSearch: true, + activateStockFilter: false, + builtinBrowser: false, + hotkey: 'D', + hotkeyHold: 'Ctrl', + hotkeyLocked: 'Ctrl + Alt + D', + showSeller: false, + searchStatRange: 10, + showCursor: true, + requestPricePrediction: false, + rememberCurrency: false + } as widget.PriceCheckWidget, + { + wmId: 3, + wmType: 'item-check', + wmTitle: '', + wmWants: 'hide', + wmZorder: 'exclusive', + wmFlags: ['hide-on-blur', 'skip-menu'], + hotkey: null, + wikiKey: null, + poedbKey: null, + craftOfExileKey: null, + stashSearchKey: null, + maps: { + profile: 1, + showNewStats: false, + selectedStats: [ + { + matcher: '#% maximum Player Resistances', + decision: 'w--' + }, + { + matcher: 'Monsters reflect #% of Physical Damage', + decision: 'd--' + }, + { + matcher: 'Monsters reflect #% of Elemental Damage', + decision: 'd--' + }, + { + matcher: 'Area contains two Unique Bosses', + decision: 'g--' + } + ] + } + } as ItemCheckWidget, + { + wmId: 4, + wmType: 'delve-grid', + wmTitle: '', + wmWants: 'hide', + wmZorder: 4, + wmFlags: ['hide-on-focus', 'skip-menu'], + toggleKey: null + } as widget.DelveGridWidget, + { + wmId: 5, + wmType: 'settings', + wmTitle: '', + wmWants: 'hide', + wmZorder: 'exclusive', + wmFlags: ['invisible-on-blur', 'ignore-ui-visibility'] + }, + { + wmId: 6, + wmType: 'item-search', + wmTitle: '', + wmWants: 'hide', + wmZorder: 6, + wmFlags: ['invisible-on-blur'], + anchor: { + pos: 'tl', + x: 10, + y: 20 + } + } as ItemSearchWidget, + // --- DEFAULT --- + { + wmId: 101, + wmType: 'stash-search', + wmTitle: 'Map rolling', + wmWants: 'hide', + wmZorder: 101, + wmFlags: ['invisible-on-blur'], + anchor: { + pos: 'tl', + x: 35, + y: 46 + }, + entries: [ + { id: 1, name: '', text: '"Pack Size: +3"', hotkey: null }, + { id: 2, name: '', text: 'Reflect', hotkey: null }, + { id: 3, name: '', text: '"Cannot Leech Life"', hotkey: null }, + { id: 4, name: '', text: '"Cannot Leech Mana"', hotkey: null } + ] + } as StashSearchWidget, + { + wmId: 102, + wmType: 'stash-search', + wmTitle: 'Dump sorting', + wmWants: 'hide', + wmZorder: 102, + wmFlags: ['invisible-on-blur'], + anchor: { + pos: 'tl', + x: 34, + y: 56 + }, + entries: [ + { id: 1, name: '', text: 'Currency', hotkey: null }, + { id: 2, name: '', text: '"Divination Card"', hotkey: null }, + { id: 3, name: '', text: 'Fossil', hotkey: null }, + { id: 4, name: '', text: '"Map Tier"', hotkey: null }, + { + id: 5, + name: '', + text: '"Map Device" "Rarity: Normal"', + hotkey: null + }, + { id: 6, name: '', text: 'Tane Laboratory', hotkey: null } + ] + } as StashSearchWidget, + { + wmId: 103, + wmType: 'image-strip', + wmTitle: 'Cheat sheets', + wmWants: 'hide', + wmZorder: 103, + wmFlags: ['invisible-on-blur'], + anchor: { + pos: 'tc', + x: 50, + y: 10 + }, + images: [{ id: 1, url: 'syndicate.jpg' }] + } as widget.ImageStripWidget + ] +}) -function upgradeConfig(_config: Config): Config { - const config = _config as Omit & { - widgets: Array>; - }; +function upgradeConfig (_config: Config): Config { + const config = _config as Omit & { + widgets: Array> + } - if (config.configVersion < 3) { - config.widgets.push({ - ...defaultConfig().widgets.find((w) => w.wmType === 'image-strip')!, - wmId: Math.max(0, ...config.widgets.map((_) => _.wmId)) + 1, - wmZorder: null, - }); + if (config.configVersion < 3) { + config.widgets.push({ + ...defaultConfig().widgets.find((w) => w.wmType === 'image-strip')!, + wmId: Math.max(0, ...config.widgets.map((_) => _.wmId)) + 1, + wmZorder: null + }) - config.widgets.push({ - ...defaultConfig().widgets.find((w) => w.wmType === 'delve-grid')!, - wmId: Math.max(0, ...config.widgets.map((_) => _.wmId)) + 1, - wmZorder: null, - }); + config.widgets.push({ + ...defaultConfig().widgets.find((w) => w.wmType === 'delve-grid')!, + wmId: Math.max(0, ...config.widgets.map((_) => _.wmId)) + 1, + wmZorder: null + }) - config.widgets.find((w) => w.wmType === 'menu')!.alwaysShow = false; + config.widgets.find((w) => w.wmType === 'menu')!.alwaysShow = false - config.configVersion = 3; - } + config.configVersion = 3 + } - if (config.configVersion < 4) { - config.widgets.find( - (w) => w.wmType === 'price-check' - )!.chaosPriceThreshold = 0.05; + if (config.configVersion < 4) { + config.widgets.find( + (w) => w.wmType === 'price-check' + )!.chaosPriceThreshold = 0.05 - const mapCheck = config.widgets.find((w) => w.wmType === 'map-check')!; - (mapCheck as any).selectedStats.forEach((e: any) => { - e.matcher = e.matchRef; - e.matchRef = undefined; - }); + const mapCheck = config.widgets.find((w) => w.wmType === 'map-check')!; + (mapCheck as any).selectedStats.forEach((e: any) => { + e.matcher = e.matchRef + e.matchRef = undefined + }) - { - const widgets = config.widgets.filter((w) => w.wmType === 'image-strip')!; - widgets.forEach((imgStrip: any) => { - imgStrip.images.forEach((e: any, idx: number) => { - e.id = idx; - }); - }); - } + { + const widgets = config.widgets.filter((w) => w.wmType === 'image-strip')! + widgets.forEach((imgStrip: any) => { + imgStrip.images.forEach((e: any, idx: number) => { + e.id = idx + }) + }) + } - config.configVersion = 4; - } + config.configVersion = 4 + } - if (config.configVersion < 5) { - config.commands.forEach((cmd) => { - cmd.send = true; - }); + if (config.configVersion < 5) { + config.commands.forEach((cmd) => { + cmd.send = true + }) - config.configVersion = 5; - } + config.configVersion = 5 + } - if (config.configVersion < 6) { - config.widgets.find((w) => w.wmType === 'price-check')!.showRateLimitState = - (config as any).logLevel === 'debug'; - config.widgets.find((w) => w.wmType === 'price-check')!.apiLatencySeconds = - 2; + if (config.configVersion < 6) { + config.widgets.find((w) => w.wmType === 'price-check')!.showRateLimitState = + (config as any).logLevel === 'debug' + config.widgets.find((w) => w.wmType === 'price-check')!.apiLatencySeconds = + 2 - config.configVersion = 6; - } + config.configVersion = 6 + } - if (config.configVersion < 7) { - const mapCheck = config.widgets.find((w) => w.wmType === 'map-check')!; - mapCheck.wmType = 'item-check'; - mapCheck.maps = { selectedStats: mapCheck.selectedStats }; - mapCheck.selectedStats = undefined; - (config as any).itemCheckKey = (config as any).mapCheckKey || null; - (config as any).mapCheckKey = undefined; + if (config.configVersion < 7) { + const mapCheck = config.widgets.find((w) => w.wmType === 'map-check')! + mapCheck.wmType = 'item-check' + mapCheck.maps = { selectedStats: mapCheck.selectedStats } + mapCheck.selectedStats = undefined; + (config as any).itemCheckKey = (config as any).mapCheckKey || null; + (config as any).mapCheckKey = undefined - config.configVersion = 7; - } + config.configVersion = 7 + } - if (config.configVersion < 8) { - const itemCheck = config.widgets.find((w) => w.wmType === 'item-check')!; - (itemCheck as ItemCheckWidget).maps.showNewStats = false; - itemCheck.maps.selectedStats = ( - itemCheck as ItemCheckWidget - ).maps.selectedStats.map((entry) => ({ - matcher: entry.matcher, - decision: (entry as any).valueDanger - ? 'danger' - : (entry as any).valueWarning - ? 'warning' - : (entry as any).valueDesirable - ? 'desirable' - : 'seen', - })); + if (config.configVersion < 8) { + const itemCheck = config.widgets.find((w) => w.wmType === 'item-check')!; + (itemCheck as ItemCheckWidget).maps.showNewStats = false + itemCheck.maps.selectedStats = ( + itemCheck as ItemCheckWidget + ).maps.selectedStats.map((entry) => ({ + matcher: entry.matcher, + decision: (entry as any).valueDanger + ? 'danger' + : (entry as any).valueWarning + ? 'warning' + : (entry as any).valueDesirable + ? 'desirable' + : 'seen' + })) - config.configVersion = 8; - } + config.configVersion = 8 + } - if (config.configVersion < 9) { - config.widgets.find((w) => w.wmType === 'price-check')!.collapseListings = - 'api'; + if (config.configVersion < 9) { + config.widgets.find((w) => w.wmType === 'price-check')!.collapseListings = + 'api' - config.widgets.find((w) => w.wmType === 'price-check')!.smartInitialSearch = - true; - config.widgets.find( - (w) => w.wmType === 'price-check' - )!.lockedInitialSearch = true; + config.widgets.find((w) => w.wmType === 'price-check')!.smartInitialSearch = + true + config.widgets.find( + (w) => w.wmType === 'price-check' + )!.lockedInitialSearch = true - config.widgets.find( - (w) => w.wmType === 'price-check' - )!.activateStockFilter = false; + config.widgets.find( + (w) => w.wmType === 'price-check' + )!.activateStockFilter = false - config.configVersion = 9; - } + config.configVersion = 9 + } - if (config.configVersion < 10) { - config.widgets.push({ - ...defaultConfig().widgets.find((w) => w.wmType === 'settings')!, - wmId: Math.max(0, ...config.widgets.map((_) => _.wmId)) + 1, - }); + if (config.configVersion < 10) { + config.widgets.push({ + ...defaultConfig().widgets.find((w) => w.wmType === 'settings')!, + wmId: Math.max(0, ...config.widgets.map((_) => _.wmId)) + 1 + }) - const priceCheck = config.widgets.find((w) => w.wmType === 'price-check')!; - priceCheck.hotkey = (config as any).priceCheckKey; - priceCheck.hotkeyHold = (config as any).priceCheckKeyHold; - priceCheck.hotkeyLocked = (config as any).priceCheckLocked; - priceCheck.showSeller = (config as any).showSeller; - priceCheck.searchStatRange = (config as any).searchStatRange; - priceCheck.showCursor = (config as any).priceCheckShowCursor; + const priceCheck = config.widgets.find((w) => w.wmType === 'price-check')! + priceCheck.hotkey = (config as any).priceCheckKey + priceCheck.hotkeyHold = (config as any).priceCheckKeyHold + priceCheck.hotkeyLocked = (config as any).priceCheckLocked + priceCheck.showSeller = (config as any).showSeller + priceCheck.searchStatRange = (config as any).searchStatRange + priceCheck.showCursor = (config as any).priceCheckShowCursor - if (priceCheck.chaosPriceThreshold === 0.05) { - priceCheck.chaosPriceThreshold = 0; - } + if (priceCheck.chaosPriceThreshold === 0.05) { + priceCheck.chaosPriceThreshold = 0 + } - config.configVersion = 10; - } + config.configVersion = 10 + } - if (config.configVersion < 11) { - config.widgets.find( - (w) => w.wmType === 'price-check' - )!.requestPricePrediction = false; + if (config.configVersion < 11) { + config.widgets.find( + (w) => w.wmType === 'price-check' + )!.requestPricePrediction = false - config.configVersion = 11; - } + config.configVersion = 11 + } - if (config.configVersion < 12) { - const afterSettings = config.widgets.findIndex( - (w) => w.wmType === 'settings' - ); - config.widgets.splice(afterSettings + 1, 0, { - ...defaultConfig().widgets.find((w) => w.wmType === 'item-search')!, - wmWants: 'show', - wmId: Math.max(0, ...config.widgets.map((_) => _.wmId)) + 1, - }); + if (config.configVersion < 12) { + const afterSettings = config.widgets.findIndex( + (w) => w.wmType === 'settings' + ) + config.widgets.splice(afterSettings + 1, 0, { + ...defaultConfig().widgets.find((w) => w.wmType === 'item-search')!, + wmWants: 'show', + wmId: Math.max(0, ...config.widgets.map((_) => _.wmId)) + 1 + }) - config.realm = 'pc-ggg'; - if (config.language === ('zh_TW' as string)) { - config.language = 'cmn-Hant'; - } + config.realm = 'pc-ggg' + if (config.language === ('zh_TW' as string)) { + config.language = 'cmn-Hant' + } - config.configVersion = 12; - } + config.configVersion = 12 + } - if (config.configVersion < 13) { - config.showAttachNotification = true; + if (config.configVersion < 13) { + config.showAttachNotification = true - config.configVersion = 13; - } + config.configVersion = 13 + } - if (config.configVersion < 14) { - const imgWidgets = config.widgets.filter( - (w) => w.wmType === 'image-strip' - ) as widget.ImageStripWidget[]; - imgWidgets.forEach((imgStrip) => { - imgStrip.images.forEach((e) => { - e.url = e.url.startsWith('app-file://') - ? e.url.slice('app-file://'.length) - : e.url; - }); - }); + if (config.configVersion < 14) { + const imgWidgets = config.widgets.filter( + (w) => w.wmType === 'image-strip' + ) as widget.ImageStripWidget[] + imgWidgets.forEach((imgStrip) => { + imgStrip.images.forEach((e) => { + e.url = e.url.startsWith('app-file://') + ? e.url.slice('app-file://'.length) + : e.url + }) + }) - const itemCheck = config.widgets.find( - (w) => w.wmType === 'item-check' - ) as ItemCheckWidget; - itemCheck.wikiKey = (config as any).wikiKey; - itemCheck.poedbKey = null; - itemCheck.craftOfExileKey = (config as any).craftOfExileKey; - itemCheck.stashSearchKey = null; + const itemCheck = config.widgets.find( + (w) => w.wmType === 'item-check' + ) as ItemCheckWidget + itemCheck.wikiKey = (config as any).wikiKey + itemCheck.poedbKey = null + itemCheck.craftOfExileKey = (config as any).craftOfExileKey + itemCheck.stashSearchKey = null - config.configVersion = 14; - } + config.configVersion = 14 + } - if (config.configVersion < 15) { - const priceCheck = config.widgets.find( - (w) => w.wmType === 'price-check' - ) as widget.PriceCheckWidget; - priceCheck.builtinBrowser = false; + if (config.configVersion < 15) { + const priceCheck = config.widgets.find( + (w) => w.wmType === 'price-check' + ) as widget.PriceCheckWidget + priceCheck.builtinBrowser = false - const itemSearch = config.widgets.find( - (w) => w.wmType === 'item-search' - ) as ItemSearchWidget; - itemSearch.ocrGemsKey = null; + const itemSearch = config.widgets.find( + (w) => w.wmType === 'item-search' + ) as ItemSearchWidget + itemSearch.ocrGemsKey = null - const itemCheck = config.widgets.find( - (w) => w.wmType === 'item-check' - ) as ItemCheckWidget; - itemCheck.maps.profile = 1; - for (const stat of itemCheck.maps.selectedStats) { - const p1decision = - stat.decision === 'danger' - ? 'd' - : stat.decision === 'warning' - ? 'w' - : stat.decision === 'desirable' - ? 'g' - : 's'; + const itemCheck = config.widgets.find( + (w) => w.wmType === 'item-check' + ) as ItemCheckWidget + itemCheck.maps.profile = 1 + for (const stat of itemCheck.maps.selectedStats) { + const p1decision = + stat.decision === 'danger' + ? 'd' + : stat.decision === 'warning' + ? 'w' + : stat.decision === 'desirable' + ? 'g' + : 's' - stat.decision = `${p1decision}--`; - } + stat.decision = `${p1decision}--` + } - config.configVersion = 15; - } + config.configVersion = 15 + } - if (config.configVersion < 16) { - const delve = config.widgets.find( - (w) => w.wmType === 'delve-grid' - ) as widget.DelveGridWidget; - delve.toggleKey = (config as any).delveGridKey; + if (config.configVersion < 16) { + const delve = config.widgets.find( + (w) => w.wmType === 'delve-grid' + ) as widget.DelveGridWidget + delve.toggleKey = (config as any).delveGridKey - const itemCheck = config.widgets.find( - (w) => w.wmType === 'item-check' - ) as ItemCheckWidget; - itemCheck.hotkey = (config as any).itemCheckKey; + const itemCheck = config.widgets.find( + (w) => w.wmType === 'item-check' + ) as ItemCheckWidget + itemCheck.hotkey = (config as any).itemCheckKey - if (itemCheck.maps.profile === undefined) { - itemCheck.maps.profile = 1; - itemCheck.maps.selectedStats = []; - } + if (itemCheck.maps.profile === undefined) { + itemCheck.maps.profile = 1 + itemCheck.maps.selectedStats = [] + } - config.configVersion = 16; - } + config.configVersion = 16 + } - if (config.logKeys === undefined) { - config.logKeys = false; - } + if (config.logKeys === undefined) { + config.logKeys = false + } - const priceCheck = config.widgets.find( - (w) => w.wmType === 'price-check' - ) as widget.PriceCheckWidget; - if (priceCheck.rememberCurrency === undefined) { - priceCheck.rememberCurrency = false; - } + const priceCheck = config.widgets.find( + (w) => w.wmType === 'price-check' + ) as widget.PriceCheckWidget + if (priceCheck.rememberCurrency === undefined) { + priceCheck.rememberCurrency = false + } - for (const widget of config.widgets) { - if (widget.wmType === 'stash-search') { - (widget as StashSearchWidget).enableHotkeys ??= true; - } - } + for (const widget of config.widgets) { + if (widget.wmType === 'stash-search') { + (widget as StashSearchWidget).enableHotkeys ??= true + } + } - return config as unknown as Config; + return config as unknown as Config } -function getConfigForHost(): HostConfig { - const actions: ShortcutAction[] = []; +function getConfigForHost (): HostConfig { + const actions: ShortcutAction[] = [] - const config = AppConfig(); - const priceCheck = AppConfig('price-check') as widget.PriceCheckWidget; - if (priceCheck.hotkey) { - actions.push({ - shortcut: `${priceCheck.hotkeyHold} + ${priceCheck.hotkey}`, - action: { type: 'copy-item', target: 'price-check', focusOverlay: false }, - keepModKeys: true, - }); - } - if (priceCheck.hotkeyLocked) { - actions.push({ - shortcut: priceCheck.hotkeyLocked, - action: { type: 'copy-item', target: 'price-check', focusOverlay: true }, - }); - } - actions.push({ - shortcut: config.overlayKey, - action: { type: 'toggle-overlay' }, - keepModKeys: true, - }); - const itemCheck = AppConfig('item-check') as ItemCheckWidget; - if (itemCheck.wikiKey) { - actions.push({ - shortcut: itemCheck.wikiKey, - action: { type: 'copy-item', target: 'open-wiki' }, - }); - } - if (itemCheck.craftOfExileKey) { - actions.push({ - shortcut: itemCheck.craftOfExileKey, - action: { type: 'copy-item', target: 'open-craft-of-exile' }, - }); - } - if (itemCheck.poedbKey) { - actions.push({ - shortcut: itemCheck.poedbKey, - action: { type: 'copy-item', target: 'open-poedb' }, - }); - } - if (itemCheck.stashSearchKey) { - actions.push({ - shortcut: itemCheck.stashSearchKey, - action: { type: 'copy-item', target: 'search-similar' }, - }); - } - if (itemCheck.hotkey) { - actions.push({ - shortcut: itemCheck.hotkey, - action: { type: 'copy-item', target: 'item-check', focusOverlay: true }, - }); - } - const delveGrid = AppConfig('delve-grid') as widget.DelveGridWidget; - if (delveGrid.toggleKey) { - actions.push({ - shortcut: delveGrid.toggleKey, - action: { type: 'trigger-event', target: 'delve-grid' }, - keepModKeys: true, - }); - } - for (const command of config.commands) { - if (command.hotkey) { - actions.push({ - shortcut: command.hotkey, - action: { - type: 'paste-in-chat', - text: command.text, - send: command.send, - }, - }); - } - } - for (const widget of config.widgets) { - if (widget.wmType === 'stash-search') { - const stashSearch = widget as StashSearchWidget; - if (!stashSearch.enableHotkeys) continue; + const config = AppConfig() + const priceCheck = AppConfig('price-check') as widget.PriceCheckWidget + if (priceCheck.hotkey) { + actions.push({ + shortcut: `${priceCheck.hotkeyHold} + ${priceCheck.hotkey}`, + action: { type: 'copy-item', target: 'price-check', focusOverlay: false }, + keepModKeys: true + }) + } + if (priceCheck.hotkeyLocked) { + actions.push({ + shortcut: priceCheck.hotkeyLocked, + action: { type: 'copy-item', target: 'price-check', focusOverlay: true } + }) + } + actions.push({ + shortcut: config.overlayKey, + action: { type: 'toggle-overlay' }, + keepModKeys: true + }) + const itemCheck = AppConfig('item-check') as ItemCheckWidget + if (itemCheck.wikiKey) { + actions.push({ + shortcut: itemCheck.wikiKey, + action: { type: 'copy-item', target: 'open-wiki' } + }) + } + if (itemCheck.craftOfExileKey) { + actions.push({ + shortcut: itemCheck.craftOfExileKey, + action: { type: 'copy-item', target: 'open-craft-of-exile' } + }) + } + if (itemCheck.poedbKey) { + actions.push({ + shortcut: itemCheck.poedbKey, + action: { type: 'copy-item', target: 'open-poedb' } + }) + } + if (itemCheck.stashSearchKey) { + actions.push({ + shortcut: itemCheck.stashSearchKey, + action: { type: 'copy-item', target: 'search-similar' } + }) + } + if (itemCheck.hotkey) { + actions.push({ + shortcut: itemCheck.hotkey, + action: { type: 'copy-item', target: 'item-check', focusOverlay: true } + }) + } + const delveGrid = AppConfig('delve-grid') as widget.DelveGridWidget + if (delveGrid.toggleKey) { + actions.push({ + shortcut: delveGrid.toggleKey, + action: { type: 'trigger-event', target: 'delve-grid' }, + keepModKeys: true + }) + } + for (const command of config.commands) { + if (command.hotkey) { + actions.push({ + shortcut: command.hotkey, + action: { + type: 'paste-in-chat', + text: command.text, + send: command.send + } + }) + } + } + for (const widget of config.widgets) { + if (widget.wmType === 'stash-search') { + const stashSearch = widget as StashSearchWidget + if (!stashSearch.enableHotkeys) continue - for (const entry of stashSearch.entries) { - if (entry.hotkey) { - actions.push({ - shortcut: entry.hotkey, - action: { type: 'stash-search', text: entry.text }, - }); - } - } - } else if (widget.wmType === 'timer') { - const stopwatch = widget as widget.StopwatchWidget; - if (stopwatch.toggleKey) { - actions.push({ - shortcut: stopwatch.toggleKey, - keepModKeys: true, - action: { - type: 'trigger-event', - target: `stopwatch-start-stop:${widget.wmId}`, - }, - }); - } - if (stopwatch.resetKey) { - actions.push({ - shortcut: stopwatch.resetKey, - keepModKeys: true, - action: { - type: 'trigger-event', - target: `stopwatch-reset:${widget.wmId}`, - }, - }); - } - } else if (widget.wmType === 'item-search') { - const itemSearch = widget as ItemSearchWidget; - if (itemSearch.ocrGemsKey) { - actions.push({ - shortcut: itemSearch.ocrGemsKey, - keepModKeys: true, - action: { type: 'ocr-text', target: 'heist-gems' }, - }); - } - } - } + for (const entry of stashSearch.entries) { + if (entry.hotkey) { + actions.push({ + shortcut: entry.hotkey, + action: { type: 'stash-search', text: entry.text } + }) + } + } + } else if (widget.wmType === 'timer') { + const stopwatch = widget as widget.StopwatchWidget + if (stopwatch.toggleKey) { + actions.push({ + shortcut: stopwatch.toggleKey, + keepModKeys: true, + action: { + type: 'trigger-event', + target: `stopwatch-start-stop:${widget.wmId}` + } + }) + } + if (stopwatch.resetKey) { + actions.push({ + shortcut: stopwatch.resetKey, + keepModKeys: true, + action: { + type: 'trigger-event', + target: `stopwatch-reset:${widget.wmId}` + } + }) + } + } else if (widget.wmType === 'item-search') { + const itemSearch = widget as ItemSearchWidget + if (itemSearch.ocrGemsKey) { + actions.push({ + shortcut: itemSearch.ocrGemsKey, + keepModKeys: true, + action: { type: 'ocr-text', target: 'heist-gems' } + }) + } + } + } - return { - shortcuts: actions, - restoreClipboard: config.restoreClipboard, - clientLog: config.clientLog, - gameConfig: config.gameConfig, - stashScroll: config.stashScroll, - overlayKey: config.overlayKey, - logKeys: config.logKeys, - windowTitle: config.windowTitle, - language: config.language, - }; + return { + shortcuts: actions, + restoreClipboard: config.restoreClipboard, + clientLog: config.clientLog, + gameConfig: config.gameConfig, + stashScroll: config.stashScroll, + overlayKey: config.overlayKey, + logKeys: config.logKeys, + windowTitle: config.windowTitle, + language: config.language + } } diff --git a/renderer/src/web/background/Leagues.ts b/renderer/src/web/background/Leagues.ts index 7c1985a7..d1271dd3 100644 --- a/renderer/src/web/background/Leagues.ts +++ b/renderer/src/web/background/Leagues.ts @@ -1,118 +1,126 @@ -import { computed, shallowRef, readonly } from 'vue'; -import { createGlobalState } from '@vueuse/core'; -import { AppConfig, poeWebApi } from '@/web/Config'; -import { Host } from './IPC'; +import { computed, shallowRef, readonly } from 'vue' +import { createGlobalState } from '@vueuse/core' +import { AppConfig } from '@/web/Config' // pc-ggg, pc-garena // const PERMANENT_SC = ['Standard', '標準模式'] -const PERMANENT_HC = ['Hardcore', '專家模式']; +const PERMANENT_HC = ['Hardcore', '專家模式'] interface ApiLeague { - id: string; - event?: boolean; - rules: Array<{ id: string }>; + id: string + event?: boolean + rules: Array<{ id: string }> } -const DEFAULT_POE2_LEAGUES: ApiLeague[] = [ - { id: 'Standard', rules: [] }, - { - id: 'Hardcore', - rules: [ - { - id: 'Hardcore', - }, - ], - }, -]; +/* +[4:51:57 PM] error [cors-proxy] net::ERR_BLOCKED_BY_CLIENT (www.pathofexile.com) +[4:51:57 PM] error [cors-proxy] net::ERR_BLOCKED_BY_CLIENT (www.pathofexile.com) +[4:51:57 PM] error [cors-proxy] net::ERR_BLOCKED_BY_CLIENT (www.pathofexile.com) +[4:51:57 PM] error [Shortcuts] Hotkey "Home" reserved by the game will not be registered. +[4:51:57 PM] error [GameConfig] Failed to find game configuration file in the default location. +[4:51:58 PM] error [cors-proxy] net::ERR_BLOCKED_BY_CLIENT (www.pathofexile.com) +*/ interface League { - id: string; - isPopular: boolean; + id: string + isPopular: boolean } export const useLeagues = createGlobalState(() => { - const isLoading = shallowRef(false); - const error = shallowRef(null); - const tradeLeagues = shallowRef([]); + const isLoading = shallowRef(false) + const error = shallowRef(null) + const tradeLeagues = shallowRef([]) - const selectedId = computed({ - get() { - return tradeLeagues.value.length ? AppConfig().leagueId : undefined; - }, - set(id) { - AppConfig().leagueId = id; - }, - }); + const DEFAULT_POE2_LEAGUES: ApiLeague[] = [ + { id: 'Standard', rules: [] }, + { + id: 'Hardcore', + rules: [ + { + id: 'Hardcore' + } + ] + } + ] - const selected = computed(() => { - const { leagueId } = AppConfig(); - if (!tradeLeagues.value || !leagueId) return undefined; - const listed = tradeLeagues.value.find((league) => league.id === leagueId); - return { - id: leagueId, - realm: AppConfig().realm, - isPopular: !isPrivateLeague(leagueId) && Boolean(listed?.isPopular), - }; - }); + const selectedId = computed({ + get () { + return tradeLeagues.value.length ? AppConfig().leagueId : undefined + }, + set (id) { + AppConfig().leagueId = id + } + }) - async function load() { - isLoading.value = true; - error.value = null; + const selected = computed(() => { + const { leagueId } = AppConfig() + if (!tradeLeagues.value || !leagueId) return undefined + const listed = tradeLeagues.value.find((league) => league.id === leagueId) + return { + id: leagueId, + realm: AppConfig().realm, + isPopular: !isPrivateLeague(leagueId) && Boolean(listed?.isPopular) + } + }) - try { - const response = await Host.proxy( - `${poeWebApi()}/api/leagues?type=main&realm=pc` - ); - if (!response.ok) - throw new Error(JSON.stringify(Object.fromEntries(response.headers))); - // const leagues: ApiLeague[] = await response.json(); - const leagues: ApiLeague[] = DEFAULT_POE2_LEAGUES; - tradeLeagues.value = leagues - .filter( - (league) => - !PERMANENT_HC.includes(league.id) && - !league.rules.some( - (rule) => - rule.id === 'NoParties' || - (rule.id === 'HardMode' && !league.event) - ) - ) - .map((league) => { - return { id: league.id, isPopular: true }; - }); + async function load () { + isLoading.value = true + error.value = null - const leagueIsAlive = tradeLeagues.value.some( - (league) => league.id === selectedId.value - ); - if (!leagueIsAlive && !isPrivateLeague(selectedId.value ?? '')) { - if (tradeLeagues.value.length > 1) { - const TMP_CHALLENGE = 1; - selectedId.value = tradeLeagues.value[TMP_CHALLENGE].id; - } else { - const STANDARD = 0; - selectedId.value = tradeLeagues.value[STANDARD].id; - } - } - } catch (e) { - error.value = (e as Error).message; - } finally { - isLoading.value = false; - } - } + try { + // const response = await Host.proxy( + // `${poeWebApi()}/api/leagues?type=main&realm=pc` + // ); + // if (!response.ok) + // throw new Error(JSON.stringify(Object.fromEntries(response.headers))); + // const leagues: ApiLeague[] = await response.json(); + const leagues: ApiLeague[] = DEFAULT_POE2_LEAGUES + tradeLeagues.value = leagues + .filter( + (league) => + !PERMANENT_HC.includes(league.id) && + !league.rules.some( + (rule) => + rule.id === 'NoParties' || + (rule.id === 'HardMode' && !league.event) + ) + ) + .map((league) => { + return { id: league.id, isPopular: true } + }) - return { - isLoading, - error, - selectedId, - selected, - list: readonly(tradeLeagues), - load, - }; -}); + const leagueIsAlive = tradeLeagues.value.some( + (league) => league.id === selectedId.value + ) + if (!leagueIsAlive && !isPrivateLeague(selectedId.value ?? '')) { + if (tradeLeagues.value.length > 1) { + const TMP_CHALLENGE = 1 + selectedId.value = tradeLeagues.value[TMP_CHALLENGE].id + } else { + const STANDARD = 0 + selectedId.value = tradeLeagues.value[STANDARD].id + } + } + } catch (e) { + error.value = (e as Error).message + } finally { + isLoading.value = false + } + } -function isPrivateLeague(id: string) { - if (id.includes('Ruthless')) { - return true; - } - return /\(PL\d+\)$/.test(id); + return { + isLoading, + error, + selectedId, + selected, + list: readonly(tradeLeagues), + load + } +}) + +function isPrivateLeague (id: string) { + if (id.includes('Ruthless')) { + return true + } + return /\(PL\d+\)$/.test(id) } diff --git a/renderer/src/web/item-check/WidgetItemCheck.vue b/renderer/src/web/item-check/WidgetItemCheck.vue index e99a28eb..cdf089b7 100644 --- a/renderer/src/web/item-check/WidgetItemCheck.vue +++ b/renderer/src/web/item-check/WidgetItemCheck.vue @@ -19,7 +19,7 @@ import type { ItemCheckWidget } from './widget.js' import Widget from '../overlay/Widget.vue' import MapCheck from '../map-check/MapCheck.vue' import ItemInfo from './ItemInfo.vue' -import ConversionWarningBanner from "../conversion-warn-banner/ConversionWarningBanner.vue"; +import ConversionWarningBanner from '../conversion-warn-banner/ConversionWarningBanner.vue' const props = defineProps<{ config: ItemCheckWidget diff --git a/renderer/src/web/item-check/hotkeyable-actions.ts b/renderer/src/web/item-check/hotkeyable-actions.ts index ae0079e4..aac91e5e 100644 --- a/renderer/src/web/item-check/hotkeyable-actions.ts +++ b/renderer/src/web/item-check/hotkeyable-actions.ts @@ -1,54 +1,53 @@ -import { Host } from '@/web/background/IPC'; -import { AppConfig } from '@/web/Config'; -import { ParsedItem, parseClipboard } from '@/parser'; +import { Host } from '@/web/background/IPC' +import { AppConfig } from '@/web/Config' +import { ParsedItem, parseClipboard } from '@/parser' -const POEDB_LANGS = { en: 'us', ru: 'ru', 'cmn-Hant': 'tw', ko: 'kr' }; +const POEDB_LANGS = { 'en': 'us', 'ru': 'ru', 'cmn-Hant': 'tw', 'ko': 'kr' } -export function registerActions() { - Host.onEvent('MAIN->CLIENT::item-text', (e) => { - if ( - ![ - 'open-wiki', - 'open-craft-of-exile', - 'open-poedb', - 'search-similar', - ].includes(e.target) - ) - return; - const parsed = parseClipboard(e.clipboard); - if (!parsed.isOk()) return; +export function registerActions () { + Host.onEvent('MAIN->CLIENT::item-text', (e) => { + if ( + ![ + 'open-wiki', + 'open-craft-of-exile', + 'open-poedb', + 'search-similar' + ].includes(e.target) + ) { return } + const parsed = parseClipboard(e.clipboard) + if (!parsed.isOk()) return - if (e.target === 'open-wiki') { - openWiki(parsed.value); - } else if (e.target === 'open-craft-of-exile') { - openCoE(parsed.value); - } else if (e.target === 'open-poedb') { - openPoedb(parsed.value); - } else if (e.target === 'search-similar') { - findSimilarItems(parsed.value); - } - }); + if (e.target === 'open-wiki') { + openWiki(parsed.value) + } else if (e.target === 'open-craft-of-exile') { + openCoE(parsed.value) + } else if (e.target === 'open-poedb') { + openPoedb(parsed.value) + } else if (e.target === 'search-similar') { + findSimilarItems(parsed.value) + } + }) } -export function openWiki(item: ParsedItem) { - window.open(`https://www.poe2wiki.net/wiki/${item.info.refName}`); +export function openWiki (item: ParsedItem) { + window.open(`https://www.poe2wiki.net/wiki/${item.info.refName}`) } -export function openPoedb(item: ParsedItem) { - window.open( - `https://poe2db.tw/${POEDB_LANGS[AppConfig().language]}/search?q=${item.info.refName}` - ); +export function openPoedb (item: ParsedItem) { + window.open( + `https://poe2db.tw/${POEDB_LANGS[AppConfig().language]}/search?q=${item.info.refName}` + ) } -export function openCoE(item: ParsedItem) { - const encodedClipboard = encodeURIComponent(item.rawText); - window.open( - `https://craftofexile.com/?game=poe2&eimport=${encodedClipboard}` - ); +export function openCoE (item: ParsedItem) { + const encodedClipboard = encodeURIComponent(item.rawText) + window.open( + `https://craftofexile.com/?game=poe2&eimport=${encodedClipboard}` + ) } -export function findSimilarItems(item: ParsedItem) { - const text = JSON.stringify(item.info.name); - Host.sendEvent({ - name: 'CLIENT->MAIN::user-action', - payload: { action: 'stash-search', text }, - }); +export function findSimilarItems (item: ParsedItem) { + const text = JSON.stringify(item.info.name) + Host.sendEvent({ + name: 'CLIENT->MAIN::user-action', + payload: { action: 'stash-search', text } + }) } diff --git a/renderer/src/web/overlay/WidgetMenu.vue b/renderer/src/web/overlay/WidgetMenu.vue index 0ff1db8c..9608bc34 100644 --- a/renderer/src/web/overlay/WidgetMenu.vue +++ b/renderer/src/web/overlay/WidgetMenu.vue @@ -61,7 +61,7 @@ export default defineComponent({ required: true } }, - setup(props) { + setup (props) { const wm = inject('wm')! const widgets = computed(() => { @@ -79,17 +79,17 @@ export default defineComponent({ return { t, widgets, - createOfType(type: string) { + createOfType (type: string) { wm.create(type) }, - toggle(widget: IWidget) { + toggle (widget: IWidget) { if (widget.wmWants === 'hide') { wm.show(widget.wmId) } else { wm.hide(widget.wmId) } }, - handleItemPaste(e: Event) { + handleItemPaste (e: Event) { const target = e.target as HTMLInputElement const inputRect = target.getBoundingClientRect() Host.selfDispatch({ diff --git a/renderer/src/web/price-check/CheckedItem.vue b/renderer/src/web/price-check/CheckedItem.vue index 0f23abd0..c60d6454 100644 --- a/renderer/src/web/price-check/CheckedItem.vue +++ b/renderer/src/web/price-check/CheckedItem.vue @@ -71,7 +71,7 @@ export default defineComponent({ required: true } }, - setup(props) { + setup (props) { const widget = computed(() => AppConfig('price-check')!) const leagues = useLeagues() @@ -177,7 +177,7 @@ export default defineComponent({ props.item.info.unique == null) }) - function handleSearchMouseenter(e: MouseEvent) { + function handleSearchMouseenter (e: MouseEvent) { if ((filtersComponent.value.$el as HTMLElement).contains(e.relatedTarget as HTMLElement)) { doSearch.value = true @@ -216,10 +216,10 @@ export default defineComponent({ showSupportLinks, presets: computed(() => presets.value.presets.map(preset => ({ id: preset.id, active: (preset.id === presets.value.active) }))), - selectPreset(id: string) { + selectPreset (id: string) { presets.value.active = id }, - makeTradeLink() { + makeTradeLink () { return `https://${getTradeEndpoint()}/trade2/search/poe2/${itemFilters.value.trade.league}?q=${JSON.stringify(createTradeRequest(itemFilters.value, itemStats.value, props.item))}` } } diff --git a/renderer/src/web/price-check/PriceCheckWindow.vue b/renderer/src/web/price-check/PriceCheckWindow.vue index 679beecd..a76fc18e 100644 --- a/renderer/src/web/price-check/PriceCheckWindow.vue +++ b/renderer/src/web/price-check/PriceCheckWindow.vue @@ -79,7 +79,7 @@ import CheckPositionCircle from './CheckPositionCircle.vue' import AppTitleBar from '@/web/ui/AppTitlebar.vue' import ItemQuickPrice from '@/web/ui/ItemQuickPrice.vue' import { PriceCheckWidget, WidgetManager } from '../overlay/interfaces' -import ConversionWarningBanner from "../conversion-warn-banner/ConversionWarningBanner.vue"; +import ConversionWarningBanner from '../conversion-warn-banner/ConversionWarningBanner.vue' type ParseError = { name: string; message: string; rawText: ParsedItem['rawText'] } @@ -103,7 +103,7 @@ export default defineComponent({ required: true } }, - setup(props) { + setup (props) { const wm = inject('wm')! const { xchgRate, initialLoading: xchgRateLoading, queuePricesFetch } = usePoeninja() @@ -163,7 +163,7 @@ export default defineComponent({ } }) - function handleIdentification(identified: ParsedItem) { + function handleIdentification (identified: ParsedItem) { item.value = ok(identified) } @@ -205,7 +205,7 @@ export default defineComponent({ } }) - function closePriceCheck() { + function closePriceCheck () { if (isBrowserShown.value || !Host.isElectron) { wm.hide(props.config.wmId) } else { @@ -213,7 +213,7 @@ export default defineComponent({ } } - function openLeagueSelection() { + function openLeagueSelection () { const settings = wm.widgets.value.find(w => w.wmType === 'settings')! wm.setFlag(settings.wmId, `settings:widget:${props.config.wmId}`, true) wm.show(settings.wmId) @@ -221,14 +221,14 @@ export default defineComponent({ const iframeEl = shallowRef(null) - function showBrowser(url: string) { + function showBrowser (url: string) { wm.setFlag(props.config.wmId, 'has-browser', true) nextTick(() => { iframeEl.value!.src = url }) } - function closeBrowser() { + function closeBrowser () { wm.setFlag(props.config.wmId, 'has-browser', false) } diff --git a/renderer/src/web/price-check/filters/SourceInfo.vue b/renderer/src/web/price-check/filters/SourceInfo.vue index 02e848b9..75571a28 100644 --- a/renderer/src/web/price-check/filters/SourceInfo.vue +++ b/renderer/src/web/price-check/filters/SourceInfo.vue @@ -63,7 +63,7 @@ const stats = computed(() => { if (!parsed.roll) { return { text: parsed.translation.string, - contribution: contribution, + contribution, contributes: true } } diff --git a/renderer/src/web/price-check/filters/create-item-filters.ts b/renderer/src/web/price-check/filters/create-item-filters.ts index 84978955..e37dc30f 100644 --- a/renderer/src/web/price-check/filters/create-item-filters.ts +++ b/renderer/src/web/price-check/filters/create-item-filters.ts @@ -176,7 +176,7 @@ export function createFilters ( } filters.searchRelaxed = { category: item.category, - disabled: disabled + disabled } } } diff --git a/renderer/src/web/price-check/filters/create-stat-filters.ts b/renderer/src/web/price-check/filters/create-stat-filters.ts index 3fb18de4..49498e49 100644 --- a/renderer/src/web/price-check/filters/create-stat-filters.ts +++ b/renderer/src/web/price-check/filters/create-stat-filters.ts @@ -177,7 +177,7 @@ export function calculatedStatToFilter ( ? FilterTag.Enchant : FilterTag.Variant, oils: decodeOils(calc), - sources: sources, + sources, option: { value: sources[0].contributes!.value }, @@ -197,7 +197,7 @@ export function calculatedStatToFilter ( text: translation.string, tag: (type as unknown) as FilterTag, oils: decodeOils(calc), - sources: sources, + sources, roll: undefined, disabled: true } @@ -288,7 +288,7 @@ export function calculatedStatToFilter ( bounds: (item.rarity === ItemRarity.Unique && roll.min !== roll.max && calc.stat.better !== StatBetter.NotComparable) ? filterBounds : undefined, - dp: dp, + dp, isNegated: false, tradeInvert: calc.stat.trade.inverted } @@ -427,10 +427,10 @@ function applyClusterJewelRules (filters: StatFilter[]) { // 4 is [_, 5] if (filter.roll!.value === 4) { filter.roll!.max = 5 - // 5 is [5, 5] + // 5 is [5, 5] } else if (filter.roll!.value === 5) { filter.roll!.min = filter.roll!.default.min - // 3, 6, 10, 11, 12 are [n, _] + // 3, 6, 10, 11, 12 are [n, _] } else if ( filter.roll!.value === 3 || filter.roll!.value === 6 || diff --git a/renderer/src/web/price-check/filters/pseudo/index.ts b/renderer/src/web/price-check/filters/pseudo/index.ts index 8cccb91c..7f394560 100644 --- a/renderer/src/web/price-check/filters/pseudo/index.ts +++ b/renderer/src/web/price-check/filters/pseudo/index.ts @@ -335,7 +335,7 @@ export function filterPseudo (ctx: FiltersCreationContext) { const filter = calculatedStatToFilter({ stat: STAT_BY_REF(rule.pseudo)!, type: ModifierType.Pseudo, - sources: sources + sources }, ctx.searchInRange, ctx.item) filter.disabled = rule.disabled ?? true diff --git a/renderer/src/web/price-check/filters/pseudo/item-property.ts b/renderer/src/web/price-check/filters/pseudo/item-property.ts index 97e082a4..c0350efa 100644 --- a/renderer/src/web/price-check/filters/pseudo/item-property.ts +++ b/renderer/src/web/price-check/filters/pseudo/item-property.ts @@ -257,7 +257,7 @@ function propToFilter (opts: { better: StatBetter.PositiveRoll } const filter = calculatedStatToFilter({ - stat: stat, + stat, type: ModifierType.Pseudo, sources: [{ modifier: { @@ -265,7 +265,7 @@ function propToFilter (opts: { stats: [] }, stat: { - stat: stat, + stat, translation: stat.matchers[0], roll: { dp: opts.dp ?? false, diff --git a/renderer/src/web/price-check/price-prediction/poeprices.ts b/renderer/src/web/price-check/price-prediction/poeprices.ts index 2c15c927..534333c9 100644 --- a/renderer/src/web/price-check/price-prediction/poeprices.ts +++ b/renderer/src/web/price-check/price-prediction/poeprices.ts @@ -1,144 +1,144 @@ -import { ParsedItem } from '@/parser'; -import { useLeagues } from '@/web/background/Leagues'; -import { Host } from '@/web/background/IPC'; -import { Cache } from '../trade/Cache'; -import { usePoeninja } from '@/web/background/Prices'; +import { ParsedItem } from '@/parser' +import { useLeagues } from '@/web/background/Leagues' +import { Host } from '@/web/background/IPC' +import { Cache } from '../trade/Cache' +import { usePoeninja } from '@/web/background/Prices' -const cache = new Cache(); +const cache = new Cache() interface PoepricesApiResponse { - /* eslint-disable camelcase */ currency: 'chaos' | 'divine' | 'exalt'; - error: number; - error_msg: string; - warning_msg: string; - max: number; - min: number; - pred_confidence_score: number; - pred_explanation: Array<[string, number]>; + /* eslint-disable camelcase */ currency: 'chaos' | 'divine' | 'exalt' + error: number + error_msg: string + warning_msg: string + max: number + min: number + pred_confidence_score: number + pred_explanation: Array<[string, number]> } export interface RareItemPrice { - max: number; - min: number; - confidence: number; - currency: 'chaos' | 'div'; - explanation: Array<{ - name: string; - contrib: number; - }>; + max: number + min: number + confidence: number + currency: 'chaos' | 'div' + explanation: Array<{ + name: string + contrib: number + }> } -export async function requestPoeprices( - item: ParsedItem +export async function requestPoeprices ( + item: ParsedItem ): Promise { - const query = querystring({ - i: utf8ToBase64(transformItemText(item.rawText)), - l: useLeagues().selectedId.value, - s: 'awakened-poe-trade', // might be required name here - }); + const query = querystring({ + i: utf8ToBase64(transformItemText(item.rawText)), + l: useLeagues().selectedId.value, + s: 'awakened-poe-trade' // might be required name here + }) - let data = cache.get(query); - if (!data) { - const response = await Host.proxy(`www.poeprices.info/api?${query}`); - try { - data = (await response.json()) as PoepricesApiResponse; - } catch (e) { - throw new Error( - `${response.status}, poeprices.info API is under load or down.` - ); - } + let data = cache.get(query) + if (!data) { + const response = await Host.proxy(`www.poeprices.info/api?${query}`) + try { + data = (await response.json()) as PoepricesApiResponse + } catch (e) { + throw new Error( + `${response.status}, poeprices.info API is under load or down.` + ) + } - if (data.error !== 0) { - throw new Error(data.error_msg); - } + if (data.error !== 0) { + throw new Error(data.error_msg) + } - cache.set(query, data, 300); - } + cache.set(query, data, 300) + } - if (data.currency === 'exalt') { - const { findPriceByQuery, autoCurrency } = usePoeninja(); - const xchgExalted = findPriceByQuery({ - ns: 'ITEM', - name: 'Exalted Orb', - variant: undefined, - }); - if (!xchgExalted) { - throw new Error('poeprices.info gave the price in Exalted Orbs.'); - } - const converted = autoCurrency([ - data.min * xchgExalted.chaos, - data.max * xchgExalted.chaos, - ]); - data.min = converted.min; - data.max = converted.max; - data.currency = converted.currency === 'div' ? 'divine' : 'chaos'; - } else if (data.currency !== 'divine' && data.currency !== 'chaos') { - throw new Error('poeprices.info gave the price in unknown currency.'); - } + if (data.currency === 'exalt') { + const { findPriceByQuery, autoCurrency } = usePoeninja() + const xchgExalted = findPriceByQuery({ + ns: 'ITEM', + name: 'Exalted Orb', + variant: undefined + }) + if (!xchgExalted) { + throw new Error('poeprices.info gave the price in Exalted Orbs.') + } + const converted = autoCurrency([ + data.min * xchgExalted.chaos, + data.max * xchgExalted.chaos + ]) + data.min = converted.min + data.max = converted.max + data.currency = converted.currency === 'div' ? 'divine' : 'chaos' + } else if (data.currency !== 'divine' && data.currency !== 'chaos') { + throw new Error('poeprices.info gave the price in unknown currency.') + } - return { - currency: data.currency === 'divine' ? 'div' : 'chaos', - min: data.min, - max: data.max, - confidence: Math.round(data.pred_confidence_score), - explanation: data.pred_explanation.map((expl) => ({ - name: expl[0], - contrib: Math.round(expl[1] * 100), - })), - }; + return { + currency: data.currency === 'divine' ? 'div' : 'chaos', + min: data.min, + max: data.max, + confidence: Math.round(data.pred_confidence_score), + explanation: data.pred_explanation.map((expl) => ({ + name: expl[0], + contrib: Math.round(expl[1] * 100) + })) + } } -export function getExternalLink(item: ParsedItem): string { - const query = querystring({ - i: utf8ToBase64(transformItemText(item.rawText)), - l: useLeagues().selectedId.value, - s: 'awakened-poe-trade', - w: 1, - }); - return `https://www.poeprices.info/api?${query}`; +export function getExternalLink (item: ParsedItem): string { + const query = querystring({ + i: utf8ToBase64(transformItemText(item.rawText)), + l: useLeagues().selectedId.value, + s: 'awakened-poe-trade', + w: 1 + }) + return `https://www.poeprices.info/api?${query}` } -export async function sendFeedback( - feedback: { text: string; option: 'fair' | 'low' | 'high' }, - prediction: Pick, - item: ParsedItem +export async function sendFeedback ( + feedback: { text: string, option: 'fair' | 'low' | 'high' }, + prediction: Pick, + item: ParsedItem ): Promise { - const body = new FormData(); - body.append('selector', feedback.option); - body.append('feedbacktxt', feedback.text); - body.append('qitem_txt', utf8ToBase64(transformItemText(item.rawText))); - body.append('source', 'awakened-poe-trade'); - body.append('min', String(prediction.min)); - body.append('max', String(prediction.max)); - body.append('currency', prediction.currency); - body.append('league', useLeagues().selectedId.value!); - // body.append('debug', String(1)) + const body = new FormData() + body.append('selector', feedback.option) + body.append('feedbacktxt', feedback.text) + body.append('qitem_txt', utf8ToBase64(transformItemText(item.rawText))) + body.append('source', 'awakened-poe-trade') + body.append('min', String(prediction.min)) + body.append('max', String(prediction.max)) + body.append('currency', prediction.currency) + body.append('league', useLeagues().selectedId.value!) + // body.append('debug', String(1)) - const response = await Host.proxy('www.poeprices.info/send_feedback', { - method: 'POST', - body, - }); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const text = await response.text(); - // console.assert(text === `"${feedback.option}"`) + const response = await Host.proxy('www.poeprices.info/send_feedback', { + method: 'POST', + body + }) + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const text = await response.text() + // console.assert(text === `"${feedback.option}"`) } -function utf8ToBase64(value: string) { - return btoa(unescape(encodeURIComponent(value))); +function utf8ToBase64 (value: string) { + return btoa(unescape(encodeURIComponent(value))) } -function querystring(q: Record) { - return Object.entries(q) - .map((pair) => pair.map(encodeURIComponent).join('=')) - .join('&'); +function querystring (q: Record) { + return Object.entries(q) + .map((pair) => pair.map(encodeURIComponent).join('=')) + .join('&') } /** * @deprecated TODO blocked by poeprices.info not supporting advanced text */ -function transformItemText(rawText: string) { - // this may not account for all cases - return rawText - .replace(/(?<=\d)(\([^)]+\))/gm, '') - .replace(/^\{.+\}$\n/gm, ''); +function transformItemText (rawText: string) { + // this may not account for all cases + return rawText + .replace(/(?<=\d)(\([^)]+\))/gm, '') + .replace(/^\{.+\}$\n/gm, '') } diff --git a/renderer/src/web/price-check/trade/TradeBulk.vue b/renderer/src/web/price-check/trade/TradeBulk.vue index e7ccfd65..47b921b3 100644 --- a/renderer/src/web/price-check/trade/TradeBulk.vue +++ b/renderer/src/web/price-check/trade/TradeBulk.vue @@ -111,7 +111,7 @@ import TradeLinks from './TradeLinks.vue' const slowdown = artificialSlowdown(900) -function useBulkApi() { +function useBulkApi () { type BulkSearchExtended = Record<'xchgChaos' | 'xchgStable', { listed: Ref listedLazy: ComputedRef @@ -121,7 +121,7 @@ function useBulkApi() { const error = shallowRef(null) const result = shallowRef(null) - async function search(item: ParsedItem, filters: ItemFilters) { + async function search (item: ParsedItem, filters: ItemFilters) { try { searchId += 1 error.value = null @@ -133,8 +133,8 @@ function useBulkApi() { const have = (item.info.refName === 'Chaos Orb') ? ['divine'] : (item.info.refName === 'Divine Orb') - ? ['chaos'] - : ['divine', 'chaos'] + ? ['chaos'] + : ['divine', 'chaos'] const optimisticSearch = await execBulkSearch( item, filters, have, { accountName: AppConfig().accountName }) @@ -149,7 +149,7 @@ function useBulkApi() { } } - function getResultsByHave( + function getResultsByHave ( item: ParsedItem, filters: ItemFilters, preloaded: Array, @@ -203,7 +203,7 @@ export default defineComponent({ required: true } }, - setup(props) { + setup (props) { const widget = computed(() => AppConfig('price-check')!) const { error, result, search } = useBulkApi() @@ -236,7 +236,7 @@ export default defineComponent({ } }) - function makeTradeLink(_have?: string[]) { + function makeTradeLink (_have?: string[]) { const have = _have ?? ((selectedCurr.value === 'xchgStable') ? ['divine'] : ['chaos']) const httpPostBody = createTradeRequest(props.filters, props.item, have) const httpGetQuery = { exchange: httpPostBody.query } @@ -254,7 +254,7 @@ export default defineComponent({ execSearch: () => { search(props.item, props.filters) }, showSeller: computed(() => widget.value.showSeller), makeTradeLink, - openTradeLink() { + openTradeLink () { showBrowser(makeTradeLink(['mirror'])) } } diff --git a/renderer/src/web/price-check/trade/TradeListing.vue b/renderer/src/web/price-check/trade/TradeListing.vue index d3a4d585..5595ba69 100644 --- a/renderer/src/web/price-check/trade/TradeListing.vue +++ b/renderer/src/web/price-check/trade/TradeListing.vue @@ -105,7 +105,7 @@ const API_FETCH_LIMIT = 100 const MIN_NOT_GROUPED = 7 const MIN_GROUPED = 10 -function useTradeApi() { +function useTradeApi () { let searchId = 0 const error = shallowRef(null) const searchResult = shallowRef(null) @@ -143,7 +143,7 @@ function useTradeApi() { return out }) - async function search(filters: ItemFilters, stats: StatFilter[], item: ParsedItem) { + async function search (filters: ItemFilters, stats: StatFilter[], item: ParsedItem) { try { searchId += 1 error.value = null @@ -174,7 +174,7 @@ function useTradeApi() { } let fetched = 20 - async function fetchMore(): Promise { + async function fetchMore (): Promise { if (_searchId !== searchId) return const totalGrouped = groupedResults.value.length const totalNotGrouped = groupedResults.value.reduce((len, res) => @@ -215,7 +215,7 @@ export default defineComponent({ required: true } }, - setup(props) { + setup (props) { const widget = computed(() => AppConfig('price-check')!) watch(() => props.item, (item) => { @@ -226,7 +226,7 @@ export default defineComponent({ const showBrowser = inject<(url: string) => void>('builtin-browser')! - function makeTradeLink() { + function makeTradeLink () { return (searchResult.value) ? `https://${getTradeEndpoint()}/trade2/search/poe2/${props.filters.trade.league}/${searchResult.value.id}` : `https://${getTradeEndpoint()}/trade2/search/poe2/${props.filters.trade.league}?q=${JSON.stringify(createTradeRequest(props.filters, props.stats, props.item))}` @@ -253,7 +253,7 @@ export default defineComponent({ error, showSeller: computed(() => widget.value.showSeller), makeTradeLink, - openTradeLink() { + openTradeLink () { showBrowser(makeTradeLink()) } } diff --git a/renderer/src/web/price-check/trade/pathofexile-bulk.ts b/renderer/src/web/price-check/trade/pathofexile-bulk.ts index 985bd7ab..12349190 100644 --- a/renderer/src/web/price-check/trade/pathofexile-bulk.ts +++ b/renderer/src/web/price-check/trade/pathofexile-bulk.ts @@ -1,225 +1,225 @@ -import { DateTime } from 'luxon'; -import { Host } from '@/web/background/IPC'; +import { DateTime } from 'luxon' +import { Host } from '@/web/background/IPC' import { - TradeResponse, - Account, - getTradeEndpoint, - RATE_LIMIT_RULES, - adjustRateLimits, - tradeTag, - preventQueueCreation, -} from './common'; -import { RateLimiter } from './RateLimiter'; -import { ItemFilters } from '../filters/interfaces'; -import { ParsedItem } from '@/parser'; -import { Cache } from './Cache'; + TradeResponse, + Account, + getTradeEndpoint, + RATE_LIMIT_RULES, + adjustRateLimits, + tradeTag, + preventQueueCreation +} from './common' +import { RateLimiter } from './RateLimiter' +import { ItemFilters } from '../filters/interfaces' +import { ParsedItem } from '@/parser' +import { Cache } from './Cache' interface TradeRequest { - /* eslint-disable camelcase */ engine: 'new'; - query: { - status: { option: 'online' | 'onlineleague' | 'any' }; - have: string[]; - want: string[]; - minimum?: number; - fulfillable?: null; - }; - sort: { have: 'asc' }; + /* eslint-disable camelcase */ engine: 'new' + query: { + status: { option: 'online' | 'onlineleague' | 'any' } + have: string[] + want: string[] + minimum?: number + fulfillable?: null + } + sort: { have: 'asc' } } interface SearchResult { - id: string; - result: Record; - total: number; + id: string + result: Record + total: number } interface FetchResult { - id: string; - listing: { - indexed: string; - offers: Array<{ - exchange: { - currency: string; - amount: number; - }; - item: { - amount: number; - stock: number; - }; - }>; - account: Account; - }; + id: string + listing: { + indexed: string + offers: Array<{ + exchange: { + currency: string + amount: number + } + item: { + amount: number + stock: number + } + }> + account: Account + } } export interface PricingResult { - id: string; - relativeDate: string; - exchangeAmount: number; - itemAmount: number; - stock: number; - accountStatus: 'offline' | 'online' | 'afk'; - isMine: boolean; - accountName: string; - ign: string; + id: string + relativeDate: string + exchangeAmount: number + itemAmount: number + stock: number + accountStatus: 'offline' | 'online' | 'afk' + isMine: boolean + accountName: string + ign: string } -const cache = new Cache(); +const cache = new Cache() -async function requestTradeResultList( - body: TradeRequest, - leagueId: string +async function requestTradeResultList ( + body: TradeRequest, + leagueId: string ): Promise { - let data = cache.get([body, leagueId]); + let data = cache.get([body, leagueId]) - if (!data) { - preventQueueCreation([{ count: 1, limiters: RATE_LIMIT_RULES.EXCHANGE }]); + if (!data) { + preventQueueCreation([{ count: 1, limiters: RATE_LIMIT_RULES.EXCHANGE }]) - await RateLimiter.waitMulti(RATE_LIMIT_RULES.EXCHANGE); + await RateLimiter.waitMulti(RATE_LIMIT_RULES.EXCHANGE) - const response = await Host.proxy( - `${getTradeEndpoint()}/api/trade2/exchange/${leagueId}`, - { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify(body), - } - ); - adjustRateLimits(RATE_LIMIT_RULES.EXCHANGE, response.headers); + const response = await Host.proxy( + `${getTradeEndpoint()}/api/trade2/exchange/${leagueId}`, + { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify(body) + } + ) + adjustRateLimits(RATE_LIMIT_RULES.EXCHANGE, response.headers) - const _data = (await response.json()) as TradeResponse; - if (_data.error) { - throw new Error(_data.error.message); - } else { - data = _data; - } + const _data = (await response.json()) as TradeResponse + if (_data.error) { + throw new Error(_data.error.message) + } else { + data = _data + } - cache.set( - [body, leagueId], - data, - Cache.deriveTtl(...RATE_LIMIT_RULES.EXCHANGE) - ); - } + cache.set( + [body, leagueId], + data, + Cache.deriveTtl(...RATE_LIMIT_RULES.EXCHANGE) + ) + } - return data; + return data } -function toPricingResult( - result: FetchResult, - opts: { accountName: string }, - offer: number +function toPricingResult ( + result: FetchResult, + opts: { accountName: string }, + offer: number ): PricingResult { - return { - id: result.id, - relativeDate: - DateTime.fromISO(result.listing.indexed).toRelative({ style: 'short' }) ?? - '', - exchangeAmount: result.listing.offers[offer].exchange.amount, - itemAmount: result.listing.offers[offer].item.amount, - stock: result.listing.offers[offer].item.stock, - isMine: result.listing.account.name === opts.accountName, - ign: result.listing.account.lastCharacterName, - accountName: result.listing.account.name, - accountStatus: result.listing.account.online - ? result.listing.account.online.status === 'afk' - ? 'afk' - : 'online' - : 'offline', - }; + return { + id: result.id, + relativeDate: + DateTime.fromISO(result.listing.indexed).toRelative({ style: 'short' }) ?? + '', + exchangeAmount: result.listing.offers[offer].exchange.amount, + itemAmount: result.listing.offers[offer].item.amount, + stock: result.listing.offers[offer].item.stock, + isMine: result.listing.account.name === opts.accountName, + ign: result.listing.account.lastCharacterName, + accountName: result.listing.account.name, + accountStatus: result.listing.account.online + ? result.listing.account.online.status === 'afk' + ? 'afk' + : 'online' + : 'offline' + } } export interface BulkSearch { - queryId: string; - haveTag: string; - total: number; - listed: PricingResult[]; + queryId: string + haveTag: string + total: number + listed: PricingResult[] } -export function createTradeRequest( - filters: ItemFilters, - item: ParsedItem, - have: string[] +export function createTradeRequest ( + filters: ItemFilters, + item: ParsedItem, + have: string[] ): TradeRequest { - return { - engine: 'new', - query: { - have: have, - want: [tradeTag(item)!], - status: { - option: filters.trade.offline - ? 'any' - : filters.trade.onlineInLeague - ? 'onlineleague' - : 'online', - }, - minimum: - filters.stackSize && !filters.stackSize.disabled - ? filters.stackSize.value - : undefined, - // fulfillable: null - }, - sort: { have: 'asc' }, - }; + return { + engine: 'new', + query: { + have, + want: [tradeTag(item)!], + status: { + option: filters.trade.offline + ? 'any' + : filters.trade.onlineInLeague + ? 'onlineleague' + : 'online' + }, + minimum: + filters.stackSize && !filters.stackSize.disabled + ? filters.stackSize.value + : undefined + // fulfillable: null + }, + sort: { have: 'asc' } + } } -const SHOW_RESULTS = 20; -const API_FETCH_LIMIT = 100; +const SHOW_RESULTS = 20 +const API_FETCH_LIMIT = 100 -export async function execBulkSearch( - item: ParsedItem, - filters: ItemFilters, - have: string[], - opts: { accountName: string } +export async function execBulkSearch ( + item: ParsedItem, + filters: ItemFilters, + have: string[], + opts: { accountName: string } ): Promise> { - const query = await requestTradeResultList( - createTradeRequest(filters, item, have), - filters.trade.league - ); + const query = await requestTradeResultList( + createTradeRequest(filters, item, have), + filters.trade.league + ) - const offer = 0; - const results = Object.values(query.result).filter( - (result) => result.listing.offers.length === 1 - ); + const offer = 0 + const results = Object.values(query.result).filter( + (result) => result.listing.offers.length === 1 + ) - const resultByHave = have.map((tradeTag) => { - const resultsTag = results.filter( - (result) => result.listing.offers[offer].exchange.currency === tradeTag - ); + const resultByHave = have.map((tradeTag) => { + const resultsTag = results.filter( + (result) => result.listing.offers[offer].exchange.currency === tradeTag + ) - const loadedOnDemand = - tradeTag === 'chaos' && - resultsTag.length < SHOW_RESULTS && - query.total > API_FETCH_LIMIT; - if (loadedOnDemand) return null; + const loadedOnDemand = + tradeTag === 'chaos' && + resultsTag.length < SHOW_RESULTS && + query.total > API_FETCH_LIMIT + if (loadedOnDemand) return null - const listed = resultsTag - .sort( - (a, b) => - a.listing.offers[offer].exchange.amount / - a.listing.offers[offer].item.amount - - b.listing.offers[offer].exchange.amount / - b.listing.offers[offer].item.amount - ) - .slice(0, SHOW_RESULTS) - .map((result) => toPricingResult(result, opts, offer)); + const listed = resultsTag + .sort( + (a, b) => + a.listing.offers[offer].exchange.amount / + a.listing.offers[offer].item.amount - + b.listing.offers[offer].exchange.amount / + b.listing.offers[offer].item.amount + ) + .slice(0, SHOW_RESULTS) + .map((result) => toPricingResult(result, opts, offer)) - const chaosIsLoaded = - tradeTag === 'divine' && - resultsTag.length < results.length && - (results.length - resultsTag.length >= SHOW_RESULTS || - query.total <= API_FETCH_LIMIT); + const chaosIsLoaded = + tradeTag === 'divine' && + resultsTag.length < results.length && + (results.length - resultsTag.length >= SHOW_RESULTS || + query.total <= API_FETCH_LIMIT) - return { - queryId: query.id, - haveTag: tradeTag, - // this is a best guess when making request with multiple `have` currencies - total: chaosIsLoaded - ? resultsTag.length - : query.total - (results.length - resultsTag.length), - listed: listed, - }; - }); + return { + queryId: query.id, + haveTag: tradeTag, + // this is a best guess when making request with multiple `have` currencies + total: chaosIsLoaded + ? resultsTag.length + : query.total - (results.length - resultsTag.length), + listed + } + }) - return resultByHave; + return resultByHave } diff --git a/renderer/src/web/price-check/trade/pathofexile-trade.ts b/renderer/src/web/price-check/trade/pathofexile-trade.ts index a279da4e..ff4d1616 100644 --- a/renderer/src/web/price-check/trade/pathofexile-trade.ts +++ b/renderer/src/web/price-check/trade/pathofexile-trade.ts @@ -1,908 +1,908 @@ -import { ItemInfluence, ItemCategory, ParsedItem, ItemRarity } from '@/parser'; +import { ItemInfluence, ItemCategory, ParsedItem, ItemRarity } from '@/parser' import { - ItemFilters, - StatFilter, - INTERNAL_TRADE_IDS, - InternalTradeId, -} from '../filters/interfaces'; -import { setProperty as propSet } from 'dot-prop'; -import { DateTime } from 'luxon'; -import { Host } from '@/web/background/IPC'; + ItemFilters, + StatFilter, + INTERNAL_TRADE_IDS, + InternalTradeId +} from '../filters/interfaces' +import { setProperty as propSet } from 'dot-prop' +import { DateTime } from 'luxon' +import { Host } from '@/web/background/IPC' import { - TradeResponse, - Account, - getTradeEndpoint, - adjustRateLimits, - RATE_LIMIT_RULES, - preventQueueCreation, -} from './common'; -import { STAT_BY_REF } from '@/assets/data'; -import { RateLimiter } from './RateLimiter'; -import { ModifierType } from '@/parser/modifiers'; -import { Cache } from './Cache'; + TradeResponse, + Account, + getTradeEndpoint, + adjustRateLimits, + RATE_LIMIT_RULES, + preventQueueCreation +} from './common' +import { STAT_BY_REF } from '@/assets/data' +import { RateLimiter } from './RateLimiter' +import { ModifierType } from '@/parser/modifiers' +import { Cache } from './Cache' export const CATEGORY_TO_TRADE_ID = new Map([ - [ItemCategory.Map, 'map'], - [ItemCategory.AbyssJewel, 'jewel.abyss'], - [ItemCategory.Amulet, 'accessory.amulet'], - [ItemCategory.Belt, 'accessory.belt'], - [ItemCategory.BodyArmour, 'armour.chest'], - [ItemCategory.Boots, 'armour.boots'], - [ItemCategory.Bow, 'weapon.bow'], - [ItemCategory.Claw, 'weapon.claw'], - [ItemCategory.Dagger, 'weapon.dagger'], - [ItemCategory.FishingRod, 'weapon.rod'], - [ItemCategory.Flask, 'flask'], - [ItemCategory.Gloves, 'armour.gloves'], - [ItemCategory.Helmet, 'armour.helmet'], - [ItemCategory.Jewel, 'jewel'], - [ItemCategory.OneHandedAxe, 'weapon.oneaxe'], - [ItemCategory.OneHandedMace, 'weapon.onemace'], - [ItemCategory.OneHandedSword, 'weapon.onesword'], - [ItemCategory.Quiver, 'armour.quiver'], - [ItemCategory.Ring, 'accessory.ring'], - [ItemCategory.RuneDagger, 'weapon.runedagger'], - [ItemCategory.Sceptre, 'weapon.sceptre'], - [ItemCategory.Shield, 'armour.shield'], - [ItemCategory.Staff, 'weapon.staff'], - [ItemCategory.TwoHandedAxe, 'weapon.twoaxe'], - [ItemCategory.TwoHandedMace, 'weapon.twomace'], - [ItemCategory.TwoHandedSword, 'weapon.twosword'], - [ItemCategory.Wand, 'weapon.wand'], - [ItemCategory.Warstaff, 'weapon.warstaff'], - [ItemCategory.ClusterJewel, 'jewel.cluster'], - [ItemCategory.HeistBlueprint, 'heistmission.blueprint'], - [ItemCategory.HeistContract, 'heistmission.contract'], - [ItemCategory.HeistTool, 'heistequipment.heisttool'], - [ItemCategory.HeistBrooch, 'heistequipment.heistreward'], - [ItemCategory.HeistGear, 'heistequipment.heistweapon'], - [ItemCategory.HeistCloak, 'heistequipment.heistutility'], - [ItemCategory.Trinket, 'accessory.trinket'], - [ItemCategory.SanctumRelic, 'sanctum.relic'], - [ItemCategory.Tincture, 'tincture'], - [ItemCategory.Charm, 'azmeri.charm'], -]); + [ItemCategory.Map, 'map'], + [ItemCategory.AbyssJewel, 'jewel.abyss'], + [ItemCategory.Amulet, 'accessory.amulet'], + [ItemCategory.Belt, 'accessory.belt'], + [ItemCategory.BodyArmour, 'armour.chest'], + [ItemCategory.Boots, 'armour.boots'], + [ItemCategory.Bow, 'weapon.bow'], + [ItemCategory.Claw, 'weapon.claw'], + [ItemCategory.Dagger, 'weapon.dagger'], + [ItemCategory.FishingRod, 'weapon.rod'], + [ItemCategory.Flask, 'flask'], + [ItemCategory.Gloves, 'armour.gloves'], + [ItemCategory.Helmet, 'armour.helmet'], + [ItemCategory.Jewel, 'jewel'], + [ItemCategory.OneHandedAxe, 'weapon.oneaxe'], + [ItemCategory.OneHandedMace, 'weapon.onemace'], + [ItemCategory.OneHandedSword, 'weapon.onesword'], + [ItemCategory.Quiver, 'armour.quiver'], + [ItemCategory.Ring, 'accessory.ring'], + [ItemCategory.RuneDagger, 'weapon.runedagger'], + [ItemCategory.Sceptre, 'weapon.sceptre'], + [ItemCategory.Shield, 'armour.shield'], + [ItemCategory.Staff, 'weapon.staff'], + [ItemCategory.TwoHandedAxe, 'weapon.twoaxe'], + [ItemCategory.TwoHandedMace, 'weapon.twomace'], + [ItemCategory.TwoHandedSword, 'weapon.twosword'], + [ItemCategory.Wand, 'weapon.wand'], + [ItemCategory.Warstaff, 'weapon.warstaff'], + [ItemCategory.ClusterJewel, 'jewel.cluster'], + [ItemCategory.HeistBlueprint, 'heistmission.blueprint'], + [ItemCategory.HeistContract, 'heistmission.contract'], + [ItemCategory.HeistTool, 'heistequipment.heisttool'], + [ItemCategory.HeistBrooch, 'heistequipment.heistreward'], + [ItemCategory.HeistGear, 'heistequipment.heistweapon'], + [ItemCategory.HeistCloak, 'heistequipment.heistutility'], + [ItemCategory.Trinket, 'accessory.trinket'], + [ItemCategory.SanctumRelic, 'sanctum.relic'], + [ItemCategory.Tincture, 'tincture'], + [ItemCategory.Charm, 'azmeri.charm'] +]) const TOTAL_MODS_TEXT = { - CRAFTED_MODIFIERS: [ - '# Crafted Modifiers', - '# Crafted Prefix Modifiers', - '# Crafted Suffix Modifiers', - ], - EMPTY_MODIFIERS: [ - '# Empty Modifiers', - '# Empty Prefix Modifiers', - '# Empty Suffix Modifiers', - ], - TOTAL_MODIFIERS: ['# Modifiers', '# Prefix Modifiers', '# Suffix Modifiers'], -}; + CRAFTED_MODIFIERS: [ + '# Crafted Modifiers', + '# Crafted Prefix Modifiers', + '# Crafted Suffix Modifiers' + ], + EMPTY_MODIFIERS: [ + '# Empty Modifiers', + '# Empty Prefix Modifiers', + '# Empty Suffix Modifiers' + ], + TOTAL_MODIFIERS: ['# Modifiers', '# Prefix Modifiers', '# Suffix Modifiers'] +} const INFLUENCE_PSEUDO_TEXT = { - [ItemInfluence.Shaper]: 'Has Shaper Influence', - [ItemInfluence.Crusader]: 'Has Crusader Influence', - [ItemInfluence.Hunter]: 'Has Hunter Influence', - [ItemInfluence.Elder]: 'Has Elder Influence', - [ItemInfluence.Redeemer]: 'Has Redeemer Influence', - [ItemInfluence.Warlord]: 'Has Warlord Influence', -}; + [ItemInfluence.Shaper]: 'Has Shaper Influence', + [ItemInfluence.Crusader]: 'Has Crusader Influence', + [ItemInfluence.Hunter]: 'Has Hunter Influence', + [ItemInfluence.Elder]: 'Has Elder Influence', + [ItemInfluence.Redeemer]: 'Has Redeemer Influence', + [ItemInfluence.Warlord]: 'Has Warlord Influence' +} interface FilterBoolean { - option?: 'true' | 'false'; + option?: 'true' | 'false' } interface FilterRange { - min?: number; - max?: number; + min?: number + max?: number } interface TradeRequest { - /* eslint-disable camelcase */ - query: { - status: { option: 'online' | 'onlineleague' | 'any' }; - name?: string | { discriminator: string; option: string }; - type?: string | { discriminator: string; option: string }; - stats: Array<{ - type: 'and' | 'if' | 'count' | 'not'; - value?: FilterRange; - filters: Array<{ - id: string; - value?: { - min?: number; - max?: number; - option?: number | string; - }; - disabled?: boolean; - }>; - disabled?: boolean; - }>; - filters: { - type_filters?: { - filters: { - rarity?: { - option?: 'nonunique' | 'uniquefoil'; - }; - category?: { - option?: string; - }; - }; - }; - socket_filters?: { - filters: { - links?: FilterRange; - sockets?: { - w?: number; - }; - }; - }; - misc_filters?: { - filters: { - ilvl?: FilterRange; - quality?: FilterRange; - gem_level?: FilterRange; - corrupted?: FilterBoolean; - fractured_item?: FilterBoolean; - mirrored?: FilterBoolean; - identified?: FilterBoolean; - stack_size?: FilterRange; - }; - }; - armour_filters?: { - filters: { - ar?: FilterRange; - es?: FilterRange; - ev?: FilterRange; - ward?: FilterRange; - block?: FilterRange; - base_defence_percentile?: FilterRange; - }; - }; - weapon_filters?: { - filters: { - dps?: FilterRange; - pdps?: FilterRange; - edps?: FilterRange; - crit?: FilterRange; - aps?: FilterRange; - }; - }; - map_filters?: { - filters: { - map_tier?: FilterRange; - map_blighted?: FilterBoolean; - map_uberblighted?: FilterBoolean; - area_level?: FilterRange; - }; - }; - heist_filters?: { - filters: { - heist_wings?: FilterRange; - heist_agility?: FilterRange; - heist_brute_force?: FilterRange; - heist_counter_thaumaturgy?: FilterRange; - heist_deception?: FilterRange; - heist_demolition?: FilterRange; - heist_engineering?: FilterRange; - heist_lockpicking?: FilterRange; - heist_perception?: FilterRange; - heist_trap_disarmament?: FilterRange; - }; - }; - sentinel_filters?: { - filters: { - sentinel_durability?: FilterRange; - }; - }; - trade_filters?: { - filters: { - collapse?: FilterBoolean; - indexed?: { option?: string }; - price?: FilterRange | { option?: string }; - }; - }; - }; - }; - sort: { - price: 'asc'; - }; + /* eslint-disable camelcase */ + query: { + status: { option: 'online' | 'onlineleague' | 'any' } + name?: string | { discriminator: string, option: string } + type?: string | { discriminator: string, option: string } + stats: Array<{ + type: 'and' | 'if' | 'count' | 'not' + value?: FilterRange + filters: Array<{ + id: string + value?: { + min?: number + max?: number + option?: number | string + } + disabled?: boolean + }> + disabled?: boolean + }> + filters: { + type_filters?: { + filters: { + rarity?: { + option?: 'nonunique' | 'uniquefoil' + } + category?: { + option?: string + } + } + } + socket_filters?: { + filters: { + links?: FilterRange + sockets?: { + w?: number + } + } + } + misc_filters?: { + filters: { + ilvl?: FilterRange + quality?: FilterRange + gem_level?: FilterRange + corrupted?: FilterBoolean + fractured_item?: FilterBoolean + mirrored?: FilterBoolean + identified?: FilterBoolean + stack_size?: FilterRange + } + } + armour_filters?: { + filters: { + ar?: FilterRange + es?: FilterRange + ev?: FilterRange + ward?: FilterRange + block?: FilterRange + base_defence_percentile?: FilterRange + } + } + weapon_filters?: { + filters: { + dps?: FilterRange + pdps?: FilterRange + edps?: FilterRange + crit?: FilterRange + aps?: FilterRange + } + } + map_filters?: { + filters: { + map_tier?: FilterRange + map_blighted?: FilterBoolean + map_uberblighted?: FilterBoolean + area_level?: FilterRange + } + } + heist_filters?: { + filters: { + heist_wings?: FilterRange + heist_agility?: FilterRange + heist_brute_force?: FilterRange + heist_counter_thaumaturgy?: FilterRange + heist_deception?: FilterRange + heist_demolition?: FilterRange + heist_engineering?: FilterRange + heist_lockpicking?: FilterRange + heist_perception?: FilterRange + heist_trap_disarmament?: FilterRange + } + } + sentinel_filters?: { + filters: { + sentinel_durability?: FilterRange + } + } + trade_filters?: { + filters: { + collapse?: FilterBoolean + indexed?: { option?: string } + price?: FilterRange | { option?: string } + } + } + } + } + sort: { + price: 'asc' + } } export interface SearchResult { - id: string; - result: string[]; - total: number; - inexact?: boolean; + id: string + result: string[] + total: number + inexact?: boolean } interface FetchResult { - id: string; - item: { - ilvl?: number; - stackSize?: number; - corrupted?: boolean; - properties?: Array<{ - values: [[string, number]]; - type: - | 78 // Corpse Level (Filled Coffin) - | 30 // Spawns a Level %0 Monster when Harvested - | 6 // Quality - | 5; // Level - }>; - note?: string; - }; - listing: { - indexed: string; - price?: { - amount: number; - currency: string; - type: '~price'; - }; - account: Account; - }; + id: string + item: { + ilvl?: number + stackSize?: number + corrupted?: boolean + properties?: Array<{ + values: [[string, number]] + type: + | 78 // Corpse Level (Filled Coffin) + | 30 // Spawns a Level %0 Monster when Harvested + | 6 // Quality + | 5 // Level + }> + note?: string + } + listing: { + indexed: string + price?: { + amount: number + currency: string + type: '~price' + } + account: Account + } } export interface PricingResult { - id: string; - itemLevel?: string; - stackSize?: number; - corrupted?: boolean; - quality?: string; - level?: string; - relativeDate: string; - priceAmount: number; - priceCurrency: string; - isMine: boolean; - hasNote: boolean; - accountName: string; - accountStatus: 'offline' | 'online' | 'afk'; - ign: string; + id: string + itemLevel?: string + stackSize?: number + corrupted?: boolean + quality?: string + level?: string + relativeDate: string + priceAmount: number + priceCurrency: string + isMine: boolean + hasNote: boolean + accountName: string + accountStatus: 'offline' | 'online' | 'afk' + ign: string } -export function createTradeRequest( - filters: ItemFilters, - stats: StatFilter[], - item: ParsedItem +export function createTradeRequest ( + filters: ItemFilters, + stats: StatFilter[], + item: ParsedItem ) { - const body: TradeRequest = { - query: { - status: { - option: filters.trade.offline - ? 'any' - : filters.trade.onlineInLeague - ? 'onlineleague' - : 'online', - }, - stats: [{ type: 'and', filters: [] }], - filters: {}, - }, - sort: { - price: 'asc', - }, - }; - const { query } = body; + const body: TradeRequest = { + query: { + status: { + option: filters.trade.offline + ? 'any' + : filters.trade.onlineInLeague + ? 'onlineleague' + : 'online' + }, + stats: [{ type: 'and', filters: [] }], + filters: {} + }, + sort: { + price: 'asc' + } + } + const { query } = body - if (filters.trade.currency) { - propSet( - query.filters, - 'trade_filters.filters.price.option', - filters.trade.currency - ); - } + if (filters.trade.currency) { + propSet( + query.filters, + 'trade_filters.filters.price.option', + filters.trade.currency + ) + } - if (filters.trade.collapseListings === 'api') { - propSet( - query.filters, - 'trade_filters.filters.collapse.option', - String(true) - ); - } + if (filters.trade.collapseListings === 'api') { + propSet( + query.filters, + 'trade_filters.filters.collapse.option', + String(true) + ) + } - if (filters.trade.listed) { - propSet( - query.filters, - 'trade_filters.filters.indexed.option', - filters.trade.listed - ); - } + if (filters.trade.listed) { + propSet( + query.filters, + 'trade_filters.filters.indexed.option', + filters.trade.listed + ) + } - const activeSearch = - filters.searchRelaxed && !filters.searchRelaxed.disabled - ? filters.searchRelaxed - : filters.searchExact; + const activeSearch = + filters.searchRelaxed && !filters.searchRelaxed.disabled + ? filters.searchRelaxed + : filters.searchExact - if (activeSearch.nameTrade) { - query.name = nameToQuery(activeSearch.nameTrade, filters); - } else if (activeSearch.name) { - query.name = nameToQuery(activeSearch.name, filters); - } + if (activeSearch.nameTrade) { + query.name = nameToQuery(activeSearch.nameTrade, filters) + } else if (activeSearch.name) { + query.name = nameToQuery(activeSearch.name, filters) + } - if (activeSearch.baseTypeTrade) { - query.type = nameToQuery(activeSearch.baseTypeTrade, filters); - } else if (activeSearch.baseType) { - query.type = nameToQuery(activeSearch.baseType, filters); - } + if (activeSearch.baseTypeTrade) { + query.type = nameToQuery(activeSearch.baseTypeTrade, filters) + } else if (activeSearch.baseType) { + query.type = nameToQuery(activeSearch.baseType, filters) + } - if (filters.foil && !filters.foil.disabled) { - propSet(query.filters, 'type_filters.filters.rarity.option', 'uniquefoil'); - } else if (filters.rarity) { - propSet( - query.filters, - 'type_filters.filters.rarity.option', - filters.rarity.value - ); - } + if (filters.foil && !filters.foil.disabled) { + propSet(query.filters, 'type_filters.filters.rarity.option', 'uniquefoil') + } else if (filters.rarity) { + propSet( + query.filters, + 'type_filters.filters.rarity.option', + filters.rarity.value + ) + } - if (activeSearch.category) { - const id = CATEGORY_TO_TRADE_ID.get(activeSearch.category); - if (id) { - propSet(query.filters, 'type_filters.filters.category.option', id); - } else { - throw new Error(`Invalid category: ${activeSearch.category}`); - } - } + if (activeSearch.category) { + const id = CATEGORY_TO_TRADE_ID.get(activeSearch.category) + if (id) { + propSet(query.filters, 'type_filters.filters.category.option', id) + } else { + throw new Error(`Invalid category: ${activeSearch.category}`) + } + } - if (filters.corrupted?.value === false || filters.corrupted?.exact) { - propSet( - query.filters, - 'misc_filters.filters.corrupted.option', - String(filters.corrupted.value) - ); - } - if (filters.fractured?.value === false) { - propSet( - query.filters, - 'misc_filters.filters.fractured_item.option', - String(false) - ); - } - if (filters.mirrored) { - if (filters.mirrored.disabled) { - propSet( - query.filters, - 'misc_filters.filters.mirrored.option', - String(false) - ); - } - } else if ( - item.rarity === ItemRarity.Normal || - item.rarity === ItemRarity.Magic || - item.rarity === ItemRarity.Rare - ) { - propSet( - query.filters, - 'misc_filters.filters.mirrored.option', - String(false) - ); - } + if (filters.corrupted?.value === false || filters.corrupted?.exact) { + propSet( + query.filters, + 'misc_filters.filters.corrupted.option', + String(filters.corrupted.value) + ) + } + if (filters.fractured?.value === false) { + propSet( + query.filters, + 'misc_filters.filters.fractured_item.option', + String(false) + ) + } + if (filters.mirrored) { + if (filters.mirrored.disabled) { + propSet( + query.filters, + 'misc_filters.filters.mirrored.option', + String(false) + ) + } + } else if ( + item.rarity === ItemRarity.Normal || + item.rarity === ItemRarity.Magic || + item.rarity === ItemRarity.Rare + ) { + propSet( + query.filters, + 'misc_filters.filters.mirrored.option', + String(false) + ) + } - if (filters.gemLevel && !filters.gemLevel.disabled) { - propSet( - query.filters, - 'misc_filters.filters.gem_level.min', - filters.gemLevel.value - ); - } + if (filters.gemLevel && !filters.gemLevel.disabled) { + propSet( + query.filters, + 'misc_filters.filters.gem_level.min', + filters.gemLevel.value + ) + } - if (filters.quality && !filters.quality.disabled) { - propSet( - query.filters, - 'misc_filters.filters.quality.min', - filters.quality.value - ); - } + if (filters.quality && !filters.quality.disabled) { + propSet( + query.filters, + 'misc_filters.filters.quality.min', + filters.quality.value + ) + } - if (filters.itemLevel && !filters.itemLevel.disabled) { - propSet( - query.filters, - 'misc_filters.filters.ilvl.min', - filters.itemLevel.value - ); - if (filters.itemLevel.max) { - propSet( - query.filters, - 'misc_filters.filters.ilvl.max', - filters.itemLevel.max - ); - } - } + if (filters.itemLevel && !filters.itemLevel.disabled) { + propSet( + query.filters, + 'misc_filters.filters.ilvl.min', + filters.itemLevel.value + ) + if (filters.itemLevel.max) { + propSet( + query.filters, + 'misc_filters.filters.ilvl.max', + filters.itemLevel.max + ) + } + } - if (filters.stackSize && !filters.stackSize.disabled) { - propSet( - query.filters, - 'misc_filters.filters.stack_size.min', - filters.stackSize.value - ); - } + if (filters.stackSize && !filters.stackSize.disabled) { + propSet( + query.filters, + 'misc_filters.filters.stack_size.min', + filters.stackSize.value + ) + } - if (filters.linkedSockets && !filters.linkedSockets.disabled) { - propSet( - query.filters, - 'socket_filters.filters.links.min', - filters.linkedSockets.value - ); - } + if (filters.linkedSockets && !filters.linkedSockets.disabled) { + propSet( + query.filters, + 'socket_filters.filters.links.min', + filters.linkedSockets.value + ) + } - if (filters.whiteSockets && !filters.whiteSockets.disabled) { - propSet( - query.filters, - 'socket_filters.filters.sockets.w', - filters.whiteSockets.value - ); - } + if (filters.whiteSockets && !filters.whiteSockets.disabled) { + propSet( + query.filters, + 'socket_filters.filters.sockets.w', + filters.whiteSockets.value + ) + } - if (filters.mapTier && !filters.mapTier.disabled) { - propSet( - query.filters, - 'map_filters.filters.map_tier.min', - filters.mapTier.value - ); - propSet( - query.filters, - 'map_filters.filters.map_tier.max', - filters.mapTier.value - ); - } + if (filters.mapTier && !filters.mapTier.disabled) { + propSet( + query.filters, + 'map_filters.filters.map_tier.min', + filters.mapTier.value + ) + propSet( + query.filters, + 'map_filters.filters.map_tier.max', + filters.mapTier.value + ) + } - if (filters.mapBlighted) { - if (filters.mapBlighted.value === 'Blighted') { - propSet( - query.filters, - 'map_filters.filters.map_blighted.option', - String(true) - ); - } else if (filters.mapBlighted.value === 'Blight-ravaged') { - propSet( - query.filters, - 'map_filters.filters.map_uberblighted.option', - String(true) - ); - } - } + if (filters.mapBlighted) { + if (filters.mapBlighted.value === 'Blighted') { + propSet( + query.filters, + 'map_filters.filters.map_blighted.option', + String(true) + ) + } else if (filters.mapBlighted.value === 'Blight-ravaged') { + propSet( + query.filters, + 'map_filters.filters.map_uberblighted.option', + String(true) + ) + } + } - if (filters.unidentified && !filters.unidentified.disabled) { - propSet( - query.filters, - 'misc_filters.filters.identified.option', - String(false) - ); - } + if (filters.unidentified && !filters.unidentified.disabled) { + propSet( + query.filters, + 'misc_filters.filters.identified.option', + String(false) + ) + } - if (filters.areaLevel && !filters.areaLevel.disabled) { - propSet( - query.filters, - 'map_filters.filters.area_level.min', - filters.areaLevel.value - ); - } + if (filters.areaLevel && !filters.areaLevel.disabled) { + propSet( + query.filters, + 'map_filters.filters.area_level.min', + filters.areaLevel.value + ) + } - if (filters.heistWingsRevealed && !filters.heistWingsRevealed.disabled) { - propSet( - query.filters, - 'heist_filters.filters.heist_wings.min', - filters.heistWingsRevealed.value - ); - } + if (filters.heistWingsRevealed && !filters.heistWingsRevealed.disabled) { + propSet( + query.filters, + 'heist_filters.filters.heist_wings.min', + filters.heistWingsRevealed.value + ) + } - if (filters.sentinelCharge && !filters.sentinelCharge.disabled) { - propSet( - query.filters, - 'sentinel_filters.filters.sentinel_durability.min', - filters.sentinelCharge.value - ); - } + if (filters.sentinelCharge && !filters.sentinelCharge.disabled) { + propSet( + query.filters, + 'sentinel_filters.filters.sentinel_durability.min', + filters.sentinelCharge.value + ) + } - for (const stat of stats) { - if (stat.tradeId[0] === 'item.has_empty_modifier') { - const TARGET_ID = { - CRAFTED_MODIFIERS: STAT_BY_REF( - TOTAL_MODS_TEXT.CRAFTED_MODIFIERS[stat.option!.value] - )!.trade.ids[ModifierType.Pseudo][0], - EMPTY_MODIFIERS: STAT_BY_REF( - TOTAL_MODS_TEXT.EMPTY_MODIFIERS[stat.option!.value] - )!.trade.ids[ModifierType.Pseudo][0], - TOTAL_MODIFIERS: STAT_BY_REF(TOTAL_MODS_TEXT.TOTAL_MODIFIERS[0])!.trade - .ids[ModifierType.Pseudo][0], - }; + for (const stat of stats) { + if (stat.tradeId[0] === 'item.has_empty_modifier') { + const TARGET_ID = { + CRAFTED_MODIFIERS: STAT_BY_REF( + TOTAL_MODS_TEXT.CRAFTED_MODIFIERS[stat.option!.value] + )!.trade.ids[ModifierType.Pseudo][0], + EMPTY_MODIFIERS: STAT_BY_REF( + TOTAL_MODS_TEXT.EMPTY_MODIFIERS[stat.option!.value] + )!.trade.ids[ModifierType.Pseudo][0], + TOTAL_MODIFIERS: STAT_BY_REF(TOTAL_MODS_TEXT.TOTAL_MODIFIERS[0])!.trade + .ids[ModifierType.Pseudo][0] + } - query.stats.push({ - type: 'count', - value: { min: 1, max: 1 }, - disabled: stat.disabled, - filters: [ - { - id: TARGET_ID.EMPTY_MODIFIERS, - value: { min: 1, max: 1 }, - disabled: stat.disabled, - }, - { - id: TARGET_ID.CRAFTED_MODIFIERS, - value: { min: 1, max: undefined }, - disabled: stat.disabled, - }, - ], - }); + query.stats.push({ + type: 'count', + value: { min: 1, max: 1 }, + disabled: stat.disabled, + filters: [ + { + id: TARGET_ID.EMPTY_MODIFIERS, + value: { min: 1, max: 1 }, + disabled: stat.disabled + }, + { + id: TARGET_ID.CRAFTED_MODIFIERS, + value: { min: 1, max: undefined }, + disabled: stat.disabled + } + ] + }) - query.stats.push({ - type: 'count', - value: { min: 1, max: 1 }, - disabled: stat.disabled, - filters: [ - { - id: TARGET_ID.EMPTY_MODIFIERS, - value: { min: 1, max: 1 }, - disabled: stat.disabled, - }, - { - id: TARGET_ID.TOTAL_MODIFIERS, - value: { min: 6, max: undefined }, - disabled: stat.disabled, - }, - ], - }); - } else if ( - // https://github.com/SnosMe/awakened-poe-trade/issues/758 - item.category === ItemCategory.Flask && - stat.statRef === '#% increased Charge Recovery' && - !stats.some((s) => s.statRef === '#% increased effect') - ) { - const reducedEffectId = STAT_BY_REF('#% increased effect')!.trade.ids[ - ModifierType.Explicit - ][0]; - query.stats.push({ - type: 'not', - disabled: stat.disabled, - filters: [{ id: reducedEffectId, disabled: stat.disabled }], - }); - } + query.stats.push({ + type: 'count', + value: { min: 1, max: 1 }, + disabled: stat.disabled, + filters: [ + { + id: TARGET_ID.EMPTY_MODIFIERS, + value: { min: 1, max: 1 }, + disabled: stat.disabled + }, + { + id: TARGET_ID.TOTAL_MODIFIERS, + value: { min: 6, max: undefined }, + disabled: stat.disabled + } + ] + }) + } else if ( + // https://github.com/SnosMe/awakened-poe-trade/issues/758 + item.category === ItemCategory.Flask && + stat.statRef === '#% increased Charge Recovery' && + !stats.some((s) => s.statRef === '#% increased effect') + ) { + const reducedEffectId = STAT_BY_REF('#% increased effect')!.trade.ids[ + ModifierType.Explicit + ][0] + query.stats.push({ + type: 'not', + disabled: stat.disabled, + filters: [{ id: reducedEffectId, disabled: stat.disabled }] + }) + } - if (stat.disabled) continue; + if (stat.disabled) continue - const input = stat.roll!; - switch (stat.tradeId[0] as InternalTradeId) { - case 'item.base_percentile': - propSet( - query.filters, - 'armour_filters.filters.base_defence_percentile.min', - typeof input.min === 'number' ? input.min : undefined - ); - propSet( - query.filters, - 'armour_filters.filters.base_defence_percentile.max', - typeof input.max === 'number' ? input.max : undefined - ); - break; - case 'item.armour': - propSet( - query.filters, - 'armour_filters.filters.ar.min', - typeof input.min === 'number' ? input.min : undefined - ); - propSet( - query.filters, - 'armour_filters.filters.ar.max', - typeof input.max === 'number' ? input.max : undefined - ); - break; - case 'item.evasion_rating': - propSet( - query.filters, - 'armour_filters.filters.ev.min', - typeof input.min === 'number' ? input.min : undefined - ); - propSet( - query.filters, - 'armour_filters.filters.ev.max', - typeof input.max === 'number' ? input.max : undefined - ); - break; - case 'item.energy_shield': - propSet( - query.filters, - 'armour_filters.filters.es.min', - typeof input.min === 'number' ? input.min : undefined - ); - propSet( - query.filters, - 'armour_filters.filters.es.max', - typeof input.max === 'number' ? input.max : undefined - ); - break; - case 'item.ward': - propSet( - query.filters, - 'armour_filters.filters.ward.min', - typeof input.min === 'number' ? input.min : undefined - ); - propSet( - query.filters, - 'armour_filters.filters.ward.max', - typeof input.max === 'number' ? input.max : undefined - ); - break; - case 'item.block': - propSet( - query.filters, - 'armour_filters.filters.block.min', - typeof input.min === 'number' ? input.min : undefined - ); - propSet( - query.filters, - 'armour_filters.filters.block.max', - typeof input.max === 'number' ? input.max : undefined - ); - break; - case 'item.total_dps': - propSet( - query.filters, - 'weapon_filters.filters.dps.min', - typeof input.min === 'number' ? input.min : undefined - ); - propSet( - query.filters, - 'weapon_filters.filters.dps.max', - typeof input.max === 'number' ? input.max : undefined - ); - break; - case 'item.physical_dps': - propSet( - query.filters, - 'weapon_filters.filters.pdps.min', - typeof input.min === 'number' ? input.min : undefined - ); - propSet( - query.filters, - 'weapon_filters.filters.pdps.max', - typeof input.max === 'number' ? input.max : undefined - ); - break; - case 'item.elemental_dps': - propSet( - query.filters, - 'weapon_filters.filters.edps.min', - typeof input.min === 'number' ? input.min : undefined - ); - propSet( - query.filters, - 'weapon_filters.filters.edps.max', - typeof input.max === 'number' ? input.max : undefined - ); - break; - case 'item.crit': - propSet( - query.filters, - 'weapon_filters.filters.crit.min', - typeof input.min === 'number' ? input.min : undefined - ); - propSet( - query.filters, - 'weapon_filters.filters.crit.max', - typeof input.max === 'number' ? input.max : undefined - ); - break; - case 'item.aps': - propSet( - query.filters, - 'weapon_filters.filters.aps.min', - typeof input.min === 'number' ? input.min : undefined - ); - propSet( - query.filters, - 'weapon_filters.filters.aps.max', - typeof input.max === 'number' ? input.max : undefined - ); - break; - } - } + const input = stat.roll! + switch (stat.tradeId[0] as InternalTradeId) { + case 'item.base_percentile': + propSet( + query.filters, + 'armour_filters.filters.base_defence_percentile.min', + typeof input.min === 'number' ? input.min : undefined + ) + propSet( + query.filters, + 'armour_filters.filters.base_defence_percentile.max', + typeof input.max === 'number' ? input.max : undefined + ) + break + case 'item.armour': + propSet( + query.filters, + 'armour_filters.filters.ar.min', + typeof input.min === 'number' ? input.min : undefined + ) + propSet( + query.filters, + 'armour_filters.filters.ar.max', + typeof input.max === 'number' ? input.max : undefined + ) + break + case 'item.evasion_rating': + propSet( + query.filters, + 'armour_filters.filters.ev.min', + typeof input.min === 'number' ? input.min : undefined + ) + propSet( + query.filters, + 'armour_filters.filters.ev.max', + typeof input.max === 'number' ? input.max : undefined + ) + break + case 'item.energy_shield': + propSet( + query.filters, + 'armour_filters.filters.es.min', + typeof input.min === 'number' ? input.min : undefined + ) + propSet( + query.filters, + 'armour_filters.filters.es.max', + typeof input.max === 'number' ? input.max : undefined + ) + break + case 'item.ward': + propSet( + query.filters, + 'armour_filters.filters.ward.min', + typeof input.min === 'number' ? input.min : undefined + ) + propSet( + query.filters, + 'armour_filters.filters.ward.max', + typeof input.max === 'number' ? input.max : undefined + ) + break + case 'item.block': + propSet( + query.filters, + 'armour_filters.filters.block.min', + typeof input.min === 'number' ? input.min : undefined + ) + propSet( + query.filters, + 'armour_filters.filters.block.max', + typeof input.max === 'number' ? input.max : undefined + ) + break + case 'item.total_dps': + propSet( + query.filters, + 'weapon_filters.filters.dps.min', + typeof input.min === 'number' ? input.min : undefined + ) + propSet( + query.filters, + 'weapon_filters.filters.dps.max', + typeof input.max === 'number' ? input.max : undefined + ) + break + case 'item.physical_dps': + propSet( + query.filters, + 'weapon_filters.filters.pdps.min', + typeof input.min === 'number' ? input.min : undefined + ) + propSet( + query.filters, + 'weapon_filters.filters.pdps.max', + typeof input.max === 'number' ? input.max : undefined + ) + break + case 'item.elemental_dps': + propSet( + query.filters, + 'weapon_filters.filters.edps.min', + typeof input.min === 'number' ? input.min : undefined + ) + propSet( + query.filters, + 'weapon_filters.filters.edps.max', + typeof input.max === 'number' ? input.max : undefined + ) + break + case 'item.crit': + propSet( + query.filters, + 'weapon_filters.filters.crit.min', + typeof input.min === 'number' ? input.min : undefined + ) + propSet( + query.filters, + 'weapon_filters.filters.crit.max', + typeof input.max === 'number' ? input.max : undefined + ) + break + case 'item.aps': + propSet( + query.filters, + 'weapon_filters.filters.aps.min', + typeof input.min === 'number' ? input.min : undefined + ) + propSet( + query.filters, + 'weapon_filters.filters.aps.max', + typeof input.max === 'number' ? input.max : undefined + ) + break + } + } - stats = stats.filter( - (stat) => !INTERNAL_TRADE_IDS.includes(stat.tradeId[0] as any) - ); - if (filters.veiled) { - for (const statRef of filters.veiled.statRefs) { - stats.push({ - disabled: filters.veiled.disabled, - statRef: undefined!, - text: undefined!, - tag: undefined!, - sources: undefined!, - tradeId: STAT_BY_REF(statRef)!.trade.ids[ModifierType.Veiled], - }); - } - } + stats = stats.filter( + (stat) => !INTERNAL_TRADE_IDS.includes(stat.tradeId[0] as any) + ) + if (filters.veiled) { + for (const statRef of filters.veiled.statRefs) { + stats.push({ + disabled: filters.veiled.disabled, + statRef: undefined!, + text: undefined!, + tag: undefined!, + sources: undefined!, + tradeId: STAT_BY_REF(statRef)!.trade.ids[ModifierType.Veiled] + }) + } + } - if (filters.influences) { - for (const influence of filters.influences) { - stats.push({ - disabled: influence.disabled, - statRef: undefined!, - text: undefined!, - tag: undefined!, - sources: undefined!, - tradeId: STAT_BY_REF(INFLUENCE_PSEUDO_TEXT[influence.value])!.trade.ids[ - ModifierType.Pseudo - ], - }); - } - } + if (filters.influences) { + for (const influence of filters.influences) { + stats.push({ + disabled: influence.disabled, + statRef: undefined!, + text: undefined!, + tag: undefined!, + sources: undefined!, + tradeId: STAT_BY_REF(INFLUENCE_PSEUDO_TEXT[influence.value])!.trade.ids[ + ModifierType.Pseudo + ] + }) + } + } - const qAnd = query.stats[0]; - for (const stat of stats) { - if (stat.tradeId.length === 1) { - qAnd.filters.push(tradeIdToQuery(stat.tradeId[0], stat)); - } else { - query.stats.push({ - type: 'count', - value: { min: 1 }, - disabled: stat.disabled, - filters: stat.tradeId.map((id) => tradeIdToQuery(id, stat)), - }); - } - } + const qAnd = query.stats[0] + for (const stat of stats) { + if (stat.tradeId.length === 1) { + qAnd.filters.push(tradeIdToQuery(stat.tradeId[0], stat)) + } else { + query.stats.push({ + type: 'count', + value: { min: 1 }, + disabled: stat.disabled, + filters: stat.tradeId.map((id) => tradeIdToQuery(id, stat)) + }) + } + } - return body; + return body } -const cache = new Cache(); +const cache = new Cache() -export async function requestTradeResultList( - body: TradeRequest, - leagueId: string +export async function requestTradeResultList ( + body: TradeRequest, + leagueId: string ): Promise { - let data = cache.get([body, leagueId]); + let data = cache.get([body, leagueId]) - if (!data) { - preventQueueCreation([ - { count: 1, limiters: RATE_LIMIT_RULES.SEARCH }, - { count: 1, limiters: RATE_LIMIT_RULES.FETCH }, - ]); + if (!data) { + preventQueueCreation([ + { count: 1, limiters: RATE_LIMIT_RULES.SEARCH }, + { count: 1, limiters: RATE_LIMIT_RULES.FETCH } + ]) - await RateLimiter.waitMulti(RATE_LIMIT_RULES.SEARCH); + await RateLimiter.waitMulti(RATE_LIMIT_RULES.SEARCH) - const response = await Host.proxy( - `${getTradeEndpoint()}/api/trade2/search/${leagueId}`, - { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify(body), - } - ); - adjustRateLimits(RATE_LIMIT_RULES.SEARCH, response.headers); + const response = await Host.proxy( + `${getTradeEndpoint()}/api/trade2/search/${leagueId}`, + { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify(body) + } + ) + adjustRateLimits(RATE_LIMIT_RULES.SEARCH, response.headers) - const _data = (await response.json()) as TradeResponse; - if (_data.error) { - throw new Error(_data.error.message); - } else { - data = _data; - } + const _data = (await response.json()) as TradeResponse + if (_data.error) { + throw new Error(_data.error.message) + } else { + data = _data + } - cache.set( - [body, leagueId], - data, - Cache.deriveTtl(...RATE_LIMIT_RULES.SEARCH, ...RATE_LIMIT_RULES.FETCH) - ); - } + cache.set( + [body, leagueId], + data, + Cache.deriveTtl(...RATE_LIMIT_RULES.SEARCH, ...RATE_LIMIT_RULES.FETCH) + ) + } - return data; + return data } -export async function requestResults( - queryId: string, - resultIds: string[], - opts: { accountName: string } +export async function requestResults ( + queryId: string, + resultIds: string[], + opts: { accountName: string } ): Promise { - let data = cache.get(resultIds); + let data = cache.get(resultIds) - if (!data) { - await RateLimiter.waitMulti(RATE_LIMIT_RULES.FETCH); + if (!data) { + await RateLimiter.waitMulti(RATE_LIMIT_RULES.FETCH) - const response = await Host.proxy( - `${getTradeEndpoint()}/api/trade2/fetch/${resultIds.join(',')}?query=${queryId}` - ); - adjustRateLimits(RATE_LIMIT_RULES.FETCH, response.headers); + const response = await Host.proxy( + `${getTradeEndpoint()}/api/trade2/fetch/${resultIds.join(',')}?query=${queryId}` + ) + adjustRateLimits(RATE_LIMIT_RULES.FETCH, response.headers) - const _data = (await response.json()) as TradeResponse<{ - result: Array; - }>; - if (_data.error) { - throw new Error(_data.error.message); - } else { - data = _data.result.filter((res) => res != null); - } + const _data = (await response.json()) as TradeResponse<{ + result: Array + }> + if (_data.error) { + throw new Error(_data.error.message) + } else { + data = _data.result.filter((res) => res != null) + } - cache.set( - resultIds, - data, - Cache.deriveTtl(...RATE_LIMIT_RULES.SEARCH, ...RATE_LIMIT_RULES.FETCH) - ); - } + cache.set( + resultIds, + data, + Cache.deriveTtl(...RATE_LIMIT_RULES.SEARCH, ...RATE_LIMIT_RULES.FETCH) + ) + } - return data.map((result) => { - return { - id: result.id, - itemLevel: - result.item.properties?.find((prop) => prop.type === 78) - ?.values[0][0] ?? String(result.item.ilvl), - stackSize: result.item.stackSize, - corrupted: result.item.corrupted, - quality: result.item.properties?.find((prop) => prop.type === 6) - ?.values[0][0], - level: result.item.properties?.find((prop) => prop.type === 5) - ?.values[0][0], - relativeDate: - DateTime.fromISO(result.listing.indexed).toRelative({ - style: 'short', - }) ?? '', - priceAmount: result.listing.price?.amount ?? 0, - priceCurrency: result.listing.price?.currency ?? 'no price', - hasNote: result.item.note != null, - isMine: result.listing.account.name === opts.accountName, - ign: result.listing.account.lastCharacterName, - accountName: result.listing.account.name, - accountStatus: result.listing.account.online - ? result.listing.account.online.status === 'afk' - ? 'afk' - : 'online' - : 'offline', - }; - }); + return data.map((result) => { + return { + id: result.id, + itemLevel: + result.item.properties?.find((prop) => prop.type === 78) + ?.values[0][0] ?? String(result.item.ilvl), + stackSize: result.item.stackSize, + corrupted: result.item.corrupted, + quality: result.item.properties?.find((prop) => prop.type === 6) + ?.values[0][0], + level: result.item.properties?.find((prop) => prop.type === 5) + ?.values[0][0], + relativeDate: + DateTime.fromISO(result.listing.indexed).toRelative({ + style: 'short' + }) ?? '', + priceAmount: result.listing.price?.amount ?? 0, + priceCurrency: result.listing.price?.currency ?? 'no price', + hasNote: result.item.note != null, + isMine: result.listing.account.name === opts.accountName, + ign: result.listing.account.lastCharacterName, + accountName: result.listing.account.name, + accountStatus: result.listing.account.online + ? result.listing.account.online.status === 'afk' + ? 'afk' + : 'online' + : 'offline' + } + }) } -function getMinMax(roll: StatFilter['roll']) { - if (!roll) { - return { min: undefined, max: undefined }; - } +function getMinMax (roll: StatFilter['roll']) { + if (!roll) { + return { min: undefined, max: undefined } + } - const sign = roll.tradeInvert ? -1 : 1; - const a = typeof roll.min === 'number' ? roll.min * sign : undefined; - const b = typeof roll.max === 'number' ? roll.max * sign : undefined; + const sign = roll.tradeInvert ? -1 : 1 + const a = typeof roll.min === 'number' ? roll.min * sign : undefined + const b = typeof roll.max === 'number' ? roll.max * sign : undefined - return !roll.tradeInvert ? { min: a, max: b } : { min: b, max: a }; + return !roll.tradeInvert ? { min: a, max: b } : { min: b, max: a } } -function tradeIdToQuery(id: string, stat: StatFilter) { - // NOTE: if there will be too many overrides in the future, - // consider moving them to stats.ndjson +function tradeIdToQuery (id: string, stat: StatFilter) { + // NOTE: if there will be too many overrides in the future, + // consider moving them to stats.ndjson - let roll = stat.roll; + let roll = stat.roll - // fixes Corrupted Implicit "Bleeding cannot be inflicted on you" - if (id === 'implicit.stat_1901158930') { - if (stat.roll?.value === 100) { - roll = undefined; // stat semantic type is flag - } - // fixes "Cannot be Poisoned" from Essence - } else if (id === 'explicit.stat_3835551335') { - if (stat.roll?.value === 100) { - roll = undefined; // stat semantic type is flag - } - // fixes "Instant Recovery" on Flasks - } else if (id.endsWith('stat_1526933524')) { - if (stat.roll?.value === 100) { - roll = undefined; // stat semantic type is flag - } - // fixes Delve "Reservation Efficiency of Skills" - } else if (id.endsWith('stat_1269219558')) { - roll = { ...roll!, tradeInvert: !roll!.tradeInvert }; - } + // fixes Corrupted Implicit "Bleeding cannot be inflicted on you" + if (id === 'implicit.stat_1901158930') { + if (stat.roll?.value === 100) { + roll = undefined // stat semantic type is flag + } + // fixes "Cannot be Poisoned" from Essence + } else if (id === 'explicit.stat_3835551335') { + if (stat.roll?.value === 100) { + roll = undefined // stat semantic type is flag + } + // fixes "Instant Recovery" on Flasks + } else if (id.endsWith('stat_1526933524')) { + if (stat.roll?.value === 100) { + roll = undefined // stat semantic type is flag + } + // fixes Delve "Reservation Efficiency of Skills" + } else if (id.endsWith('stat_1269219558')) { + roll = { ...roll!, tradeInvert: !roll!.tradeInvert } + } - return { - id, - value: { - ...getMinMax(roll), - option: stat.option != null ? stat.option.value : undefined, - }, - disabled: stat.disabled, - }; + return { + id, + value: { + ...getMinMax(roll), + option: stat.option != null ? stat.option.value : undefined + }, + disabled: stat.disabled + } } -function nameToQuery(name: string, filters: ItemFilters) { - if (!filters.discriminator) { - return name; - } else { - return { - discriminator: filters.discriminator.trade, - option: name, - }; - } +function nameToQuery (name: string, filters: ItemFilters) { + if (!filters.discriminator) { + return name + } else { + return { + discriminator: filters.discriminator.trade, + option: name + } + } } diff --git a/renderer/src/web/price-check/trends/PriceTrend.vue b/renderer/src/web/price-check/trends/PriceTrend.vue index 1240924b..8a92b857 100644 --- a/renderer/src/web/price-check/trends/PriceTrend.vue +++ b/renderer/src/web/price-check/trends/PriceTrend.vue @@ -104,7 +104,7 @@ export default defineComponent({ : autoCurrency(trend.chaos) return { - price: price, + price, change: deltaFromGraph(trend.graph), url: trend.url } diff --git a/renderer/src/web/price-check/unidentified-resolver/UnidentifiedResolver.vue b/renderer/src/web/price-check/unidentified-resolver/UnidentifiedResolver.vue index 90fbd54e..6cc8d474 100644 --- a/renderer/src/web/price-check/unidentified-resolver/UnidentifiedResolver.vue +++ b/renderer/src/web/price-check/unidentified-resolver/UnidentifiedResolver.vue @@ -28,7 +28,7 @@ export default defineComponent({ default: null } }, - setup(props, ctx) { + setup (props, ctx) { const identifiedVariants = computed(() => { const baseType = props.item!.info.refName const possible: BaseType[] = [] @@ -55,10 +55,10 @@ export default defineComponent({ !props.item.info.unique }) - function select(info: BaseType) { + function select (info: BaseType) { const newItem: ParsedItem = { ...props.item!, - info: info + info } ctx.emit('identify', newItem) } diff --git a/renderer/src/web/settings/SettingsWindow.vue b/renderer/src/web/settings/SettingsWindow.vue index 6c0ae0b3..a54dd37f 100644 --- a/renderer/src/web/settings/SettingsWindow.vue +++ b/renderer/src/web/settings/SettingsWindow.vue @@ -69,7 +69,7 @@ import SettingsStopwatch from './stopwatch.vue' import SettingsItemSearch from '../item-search/settings-item-search.vue' import ConversionWarningBanner from '../conversion-warn-banner/ConversionWarningBanner.vue' -function shuffle(array: T[]): T[] { +function shuffle (array: T[]): T[] { let currentIndex = array.length while (currentIndex !== 0) { const randomIndex = Math.floor(Math.random() * currentIndex) @@ -80,7 +80,7 @@ function shuffle(array: T[]): T[] { return array } -function quit() { +function quit () { Host.sendEvent({ name: 'CLIENT->MAIN::user-action', payload: { action: 'quit' } @@ -95,7 +95,7 @@ export default defineComponent({ required: true } }, - setup(props) { + setup (props) { const wm = inject('wm')! const { t } = useI18n() @@ -143,7 +143,7 @@ export default defineComponent({ menuByType(configWidget.value?.wmType) .map(group => group.map(component => ({ name: t(component.name!), - select() { selectedComponent.value = component }, + select () { selectedComponent.value = component }, isSelected: (selectedComponent.value === component), type: 'menu-item' as const }))), @@ -152,14 +152,14 @@ export default defineComponent({ return { t, - save() { + save () { updateConfig(configClone.value!) saveConfig() pushHostConfig() wm.hide(props.config.wmId) }, - cancel() { + cancel () { wm.hide(props.config.wmId) }, quit, @@ -178,13 +178,13 @@ export default defineComponent({ }) }), podiumVisible, - showPodium() { podiumVisible.value = true }, - hidePodium() { podiumVisible.value = false } + showPodium () { podiumVisible.value = true }, + hidePodium () { podiumVisible.value = false } } } }) -function menuByType(type?: string) { +function menuByType (type?: string) { switch (type) { case 'stash-search': return [[SettingsStashSearch]] @@ -206,7 +206,7 @@ function menuByType(type?: string) { } } -function flatJoin(arr: T[][], joinEl: () => J) { +function flatJoin (arr: T[][], joinEl: () => J) { const out: Array = [] for (const nested of arr) { out.push(...nested) diff --git a/renderer/src/web/settings/about.vue b/renderer/src/web/settings/about.vue index 269ac19e..769805ad 100644 --- a/renderer/src/web/settings/about.vue +++ b/renderer/src/web/settings/about.vue @@ -35,32 +35,32 @@ import { useI18n } from 'vue-i18n' import { Host } from '@/web/background/IPC' import { DateTime } from 'luxon' -function checkForUpdates() { +function checkForUpdates () { Host.sendEvent({ name: 'CLIENT->MAIN::user-action', payload: { action: 'check-for-update' } }) } -function openDownloadPage() { +function openDownloadPage () { window.open('https://snosme.github.io/awakened-poe-trade/download') } -function quitAndInstall() { +function quitAndInstall () { Host.sendEvent({ name: 'CLIENT->MAIN::user-action', payload: { action: 'update-and-restart' } }) } -function fmtTime(millis: number) { +function fmtTime (millis: number) { return DateTime.fromMillis(millis).toRelative({ style: 'long' }) ?? 'n/a' } export default defineComponent({ name: 'settings.about', inheritAttrs: false, - setup() { + setup () { const { t } = useI18n() const info = computed(() => {