diff --git a/package.json b/package.json index 30b777bf..24191d10 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "dependencies": { "core-js": "^2.6.5", "iohook": "^0.6.2", + "luxon": "^1.21.3", "node-window-manager": "^2.0.1", "robotjs": "^0.6.0", "tailwindcss": "^1.1.4", diff --git a/src/App.vue b/src/App.vue index 8b9f59ff..cb7fc02a 100644 --- a/src/App.vue +++ b/src/App.vue @@ -23,9 +23,13 @@ export default { diff --git a/src/components/CheckedItem.vue b/src/components/CheckedItem.vue index cac18ade..0cf30167 100644 --- a/src/components/CheckedItem.vue +++ b/src/components/CheckedItem.vue @@ -1,5 +1,5 @@ - + {{ item.name }} @@ -31,6 +31,9 @@ }]" padding="2" /> + + + @@ -38,10 +41,11 @@ import { Parser, parseClipboard } from './Parser' import { Prices } from './Prices' import RareItem from './RareItem' +import TradeListing from './TradeListing' export default { name: 'CheckedItem', - components: { RareItem }, + components: { RareItem, TradeListing }, props: { clipboard: String }, diff --git a/src/components/Parser.ts b/src/components/Parser.ts index 9d77dd85..d82bbb62 100644 --- a/src/components/Parser.ts +++ b/src/components/Parser.ts @@ -40,7 +40,7 @@ export interface ParsedItem { const LATEST_MAP_VARIANT = 'Metamorph' -enum WellKnownType { +export enum WellKnownType { Map = 'Map', UniqueFlask = 'Unique Flask', MetamorphPart = 'Metamorph Part' diff --git a/src/components/RareItem.vue b/src/components/RareItem.vue index 34a13634..1ff4ab55 100644 --- a/src/components/RareItem.vue +++ b/src/components/RareItem.vue @@ -56,16 +56,6 @@ export default { required: true } }, - async created () { - try { - this.loading = true - this.price = await requestPoeprices(this.item) - } catch (err) { - this.error = err.message - } finally { - this.loading = false - } - }, data () { return { price: null, @@ -73,6 +63,22 @@ export default { loading: false } }, + watch: { + item: { + immediate: true, + async handler (item) { + try { + this.loading = true + this.price = null + this.price = await requestPoeprices(this.item) + } catch (err) { + this.error = err.message + } finally { + this.loading = false + } + } + } + }, computed: { currency () { return Prices.findByDetailsId(this.price.currency) diff --git a/src/components/TradeListing.vue b/src/components/TradeListing.vue new file mode 100644 index 00000000..aa8865c4 --- /dev/null +++ b/src/components/TradeListing.vue @@ -0,0 +1,80 @@ + + + + Requesting search results... + + + + + Price + Level + Quality + + Listed + + + + + {{ result.priceAmount }} {{ result.priceCurrency }} + {{ result.level }} + {{ result.quality }} + Corrupted + {{ getRelativeTime(result.listedAt) }} + + + + + + {{ error }} + + + + diff --git a/src/components/pathofexile-trade.ts b/src/components/pathofexile-trade.ts new file mode 100644 index 00000000..d9bd0ba4 --- /dev/null +++ b/src/components/pathofexile-trade.ts @@ -0,0 +1,172 @@ +import { ParsedItem } from './Parser' +import { ItemRarity } from './parser-constants' +import { Leagues } from './Leagues' + +const TradeOpts = { + GemLevel: 16, + GemLevelRange: 0, + GemQualityRange: 0 +} + +const EXACT_GEM_LEVEL = ['Empower Support', 'Enlighten Support', 'Enhance Support'] + +interface TradeRequest { /* eslint-disable camelcase */ + query: { + status: { option: 'online' } + name?: string + type?: string + filters: { + socket_filters: { + filters: { + links: { + min?: number + max?: number + } + } + } + misc_filters: { + filters: { + quality: { + min?: number + max?: number + } + gem_level: { + min?: number + max?: number + } + } + } + } + } + sort: { + price: 'asc' + } +} + +interface SearchResult { + id: string + result: string[] + total: number +} + +interface FetchResult { + id: string + item: { + corrupted?: boolean + properties?: Array<{ + name: 'Quality' | 'Level' + values: [[string, number]] + }> + } + listing: { + indexed: string + price: { + amount: number + currency: string + type: '~price' + } + } +} + +interface PricingResult { + id: string + corrupted?: boolean + quality?: string + level?: string + listedAt: string + priceAmount: number + priceCurrency: string +} + +export async function requestTradeResultList (item: ParsedItem) { + const body: TradeRequest = { + query: { + status: { option: 'online' }, + filters: { + socket_filters: { + filters: { + links: {} + } + }, + misc_filters: { + filters: { + quality: {}, + gem_level: {} + } + } + } + }, + sort: { + price: 'asc' + } + } + const { query } = body + + if (item.rarity === ItemRarity.Gem) { + query.type = item.name + + if (TradeOpts.GemQualityRange > 0) { + query.filters.misc_filters.filters.quality.min = (item.quality || 0) - TradeOpts.GemQualityRange + query.filters.misc_filters.filters.quality.max = (item.quality || 0) + TradeOpts.GemQualityRange + } else { + query.filters.misc_filters.filters.quality.min = item.quality + } + + if (EXACT_GEM_LEVEL.includes(item.name)) { + query.filters.misc_filters.filters.gem_level.min = item.gemLevel + query.filters.misc_filters.filters.gem_level.max = item.gemLevel + } else if (item.gemLevel! >= TradeOpts.GemLevel) { + if (TradeOpts.GemLevelRange > 0) { + query.filters.misc_filters.filters.gem_level.min = item.gemLevel! - TradeOpts.GemLevelRange + query.filters.misc_filters.filters.gem_level.max = item.gemLevel! + TradeOpts.GemLevelRange + } else { + query.filters.misc_filters.filters.gem_level.min = item.gemLevel! + } + } + } else if (item.rarity === ItemRarity.DivinationCard) { + query.type = item.name + } else if (item.rarity === ItemRarity.Unique) { + query.name = item.name + query.type = item.baseType + } else { + // TODO + if (item.rarity !== ItemRarity.Rare && item.rarity !== ItemRarity.Magic) { + query.type = item.name + } + } + + if (item.linkedSockets) { + query.filters.socket_filters.filters.links.min = item.linkedSockets + } + + // console.log(body) + + const response = await fetch(`https://www.pathofexile.com/api/trade/search/${Leagues.selected}`, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify(body) + }) + const data: SearchResult = await response.json() + + return data +} + +export async function requestResults (queryId: string, resultIds: string[]): Promise { + const response = await fetch(`https://www.pathofexile.com/api/trade/fetch/${resultIds.join(',')}?query=${queryId}`) + const data: { result: FetchResult[] } = await response.json() + + return data.result.map(result => { + return { + id: result.id, + corrupted: result.item.corrupted, + quality: result.item.properties?.find(prop => prop.name === 'Quality')?.values[0][0], + level: result.item.properties?.find(prop => prop.name === 'Level')?.values[0][0], + listedAt: result.listing.indexed, + priceAmount: result.listing.price.amount, + priceCurrency: result.listing.price.currency + } as PricingResult + }) +} diff --git a/src/components/poeprices.ts b/src/components/poeprices.ts index 3aaaf35b..7e96c296 100644 --- a/src/components/poeprices.ts +++ b/src/components/poeprices.ts @@ -1,9 +1,7 @@ import { stringify } from 'querystring' -import { ParsedItem, ItemRarity } from './Parser' +import { ParsedItem, ItemRarity, WellKnownType } from './Parser' import { Leagues } from './Leagues' -let MAX_1 = false - /* eslint-disable camelcase */ interface PoepricesApiResponse { currency: 'chaos' | 'exalted' @@ -29,9 +27,7 @@ interface RareItemPrice { } export async function requestPoeprices (item: ParsedItem): Promise { - console.assert(item.rarity === ItemRarity.Rare) - if (MAX_1) return null - // MAX_1 = true + console.assert(item.rarity === ItemRarity.Rare && item.computed.type !== WellKnownType.Map) const query = stringify({ i: Buffer.from(item.rawText).toString('base64'), diff --git a/yarn.lock b/yarn.lock index a0793ba6..1d641e68 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6099,6 +6099,11 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +luxon@^1.21.3: + version "1.21.3" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.21.3.tgz#f1d5c2a7e855d039836cf4954f883ecac8fc4727" + integrity sha512-lLRwNcNnkZLuv13A1FUuZRZmTWF7ro2ricYvb0L9cvBYHPvZhQdKwrYnZzi103D2XKmlVmxWpdn2wfIiOt2YEw== + make-dir@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" @@ -8322,13 +8327,20 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.10.0, resolve@^1.12.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.5.0, resolve@^1.8.1: +resolve@^1.10.0, resolve@^1.12.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.1.tgz#9e018c540fcf0c427d678b9931cbf45e984bcaff" integrity sha512-fn5Wobh4cxbLzuHaE+nphztHy43/b++4M6SsGFC2gB8uYwf0C8LcarfCz1un7UTW8OFQg9iNjZ4xpcFVGebDPg== dependencies: path-parse "^1.0.6" +resolve@^1.5.0: + version "1.14.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2" + integrity sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ== + dependencies: + path-parse "^1.0.6" + responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"