WIP, trade listing

This commit is contained in:
Alexander Drozdov
2020-01-07 17:29:31 +02:00
parent 3871dccad2
commit da0df1ce97
9 changed files with 296 additions and 21 deletions
+1
View File
@@ -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",
+5 -1
View File
@@ -23,9 +23,13 @@ export default {
</script>
<style lang="postcss">
@import url('https://use.fontawesome.com/releases/v5.2.0/css/all.css');
@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css');
@import url('https://web.poecdn.com/css/font.css');
@tailwind base;
@tailwind components;
@tailwind utilities;
.table-stripped tbody tr:nth-child(odd) {
background: theme('colors.gray.900');
}
</style>
+6 -2
View File
@@ -1,5 +1,5 @@
<template>
<div class="p-2 border-b border-gray-700" v-if="price">
<div class="p-4 border-b border-gray-700" v-if="price">
<div class="bg-gray-900 mb-2 p-1 leading-none">{{ item.name }}</div>
<rare-item v-if="item.rarity === 'Rare' && item.computed.type !== 'Map'" :item="item" />
<div v-else class="flex items-center pb-4">
@@ -31,6 +31,9 @@
}]" padding="2" />
</div>
</div>
<div>
<trade-listing :item="item" />
</div>
</div>
</template>
@@ -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
},
+1 -1
View File
@@ -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'
+16 -10
View File
@@ -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)
+80
View File
@@ -0,0 +1,80 @@
<template>
<div v-if="loading">
<i class="fas fa-exclamation-circle pr-1 text-gray-600"></i>
<span>Requesting search results...</span>
</div>
<table v-else-if="results" class="table-stripped w-full">
<thead>
<tr class="text-left">
<th class="px-2">Price</th>
<th v-if="item.rarity === 'Gem'" class="px-2">Level</th>
<th v-if="item.rarity === 'Gem'" class="px-2">Quality</th>
<th class="px-2"></th>
<th class="px-2 w-full">Listed</th>
</tr>
</thead>
<tbody>
<tr v-for="result in results" :key="result.id">
<td class="px-2 whitespace-no-wrap">{{ result.priceAmount }} {{ result.priceCurrency }}</td>
<td v-if="item.rarity === 'Gem'" class="px-2 whitespace-no-wrap">{{ result.level }}</td>
<td v-if="item.rarity === 'Gem'" class="px-2 whitespace-no-wrap text-blue-400 text-right">{{ result.quality }}</td>
<td class="px-2 whitespace-no-wrap text-red-500"><span v-if="result.corrupted">Corrupted</span></td>
<td class="font-sans text-xs px-2">{{ getRelativeTime(result.listedAt) }}</td>
</tr>
</tbody>
</table>
<div v-else-if="error">
<i class="fas fa-exclamation-circle pr-1 text-red-600"></i>
<span>{{ error }}</span>
</div>
</template>
<script>
import { DateTime } from 'luxon'
import { requestTradeResultList, requestResults } from './pathofexile-trade'
export default {
props: {
item: {
type: Object,
required: true
}
},
data () {
return {
loading: false,
error: null,
results: null
}
},
watch: {
item: {
immediate: true,
async handler (item) {
try {
// @TODO https://www.pathofexile.com/forum/view-thread/2079853#p15244273
this.loading = true
this.results = []
const list = await requestTradeResultList(item)
this.results = await requestResults(list.id, list.result.slice(0, 10))
this.loading = false
if (list.total > 10) {
this.results.push(...await requestResults(list.id, list.result.slice(10, 20)))
}
} catch (err) {
this.error = err.message
} finally {
this.loading = false
}
}
}
},
methods: {
getRelativeTime (iso) {
return DateTime.fromISO(iso).toRelative()
}
}
}
</script>
+172
View File
@@ -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<PricingResult[]> {
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
})
}
+2 -6
View File
@@ -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<RareItemPrice | null> {
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'),
+13 -1
View File
@@ -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"