@juice789/tf2items 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/api.js +59 -0
- package/app.js +42 -0
- package/blanket.js +119 -0
- package/fetchAppDataInventory.js +54 -0
- package/fetchItemsApi.js +33 -0
- package/fetchItemsGame.js +12 -0
- package/fetchParticleEffects.js +20 -0
- package/fetchTextures.js +21 -0
- package/fetchTfEnglish.js +13 -0
- package/fromEconItem.js +359 -0
- package/fromListingV1.js +374 -0
- package/fromListingV2.js +92 -0
- package/getCollections.js +34 -0
- package/getItems.js +42 -0
- package/options-example.json +3 -0
- package/package.json +37 -0
- package/sagaHelpers.js +9 -0
- package/sagas.js +13 -0
- package/saveSchema.js +42 -0
- package/schema.json +1 -0
- package/schemaHelper.json +958 -0
- package/schemaItems.js +15 -0
- package/sku.js +142 -0
- package/skuBp.js +87 -0
- package/toSearchParams.js +99 -0
- package/todo.txt +24 -0
- package/transformItems.js +106 -0
package/README.md
ADDED
package/api.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const axios = require('axios')
|
|
2
|
+
const { compose, __, map, applyTo, prop } = require('ramda')
|
|
3
|
+
|
|
4
|
+
const myAxios = axios.create()
|
|
5
|
+
|
|
6
|
+
myAxios.interceptors.response.use(prop('data'), Promise.reject.bind(Promise))
|
|
7
|
+
|
|
8
|
+
const api = {}
|
|
9
|
+
|
|
10
|
+
api.getSchemaItems = ({ steamApiKey: key }) => (start) => myAxios({
|
|
11
|
+
method: 'get',
|
|
12
|
+
url: 'https://api.steampowered.com/IEconItems_440/GetSchemaItems/v1/',
|
|
13
|
+
params: {
|
|
14
|
+
key,
|
|
15
|
+
language: 'EN',
|
|
16
|
+
start
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
api.getSchemaOverview = ({ steamApiKey: key }) => () => myAxios({
|
|
21
|
+
method: 'get',
|
|
22
|
+
url: 'https://api.steampowered.com/IEconItems_440/GetSchemaOverview/v1/',
|
|
23
|
+
params: {
|
|
24
|
+
key,
|
|
25
|
+
language: 'EN'
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
api.getItemsGameUrl = ({ steamApiKey: key }) => () => myAxios({
|
|
30
|
+
method: 'get',
|
|
31
|
+
url: 'https://api.steampowered.com/IEconItems_440/GetSchemaURL/v1/',
|
|
32
|
+
params: {
|
|
33
|
+
key
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
api.getItemsGame = () => (url) => myAxios({
|
|
38
|
+
method: 'get',
|
|
39
|
+
url
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
api.fetchTfEnglish = () => () => myAxios({
|
|
43
|
+
method: 'get',
|
|
44
|
+
url: 'https://raw.githubusercontent.com/SteamDatabase/GameTracking-TF2/master/tf/resource/tf_english.txt'
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
api.fetchProtoObjDefs = () => () => myAxios({
|
|
48
|
+
method: 'get',
|
|
49
|
+
url: 'https://raw.githubusercontent.com/SteamDatabase/GameTracking-TF2/master/tf/resource/tf_proto_obj_defs_english.txt'
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
api.getAssetClassInfo = ({ steamApiKey }) => (ids, appId = 440) => myAxios({
|
|
53
|
+
method: 'get',
|
|
54
|
+
url: `https://api.steampowered.com/ISteamEconomy/GetAssetClassInfo/v1/?key=${steamApiKey}&appid=${appId}&language=EN&class_count=${ids.length}${ids.join('')}`
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const createApi = compose(map(__, api), applyTo)
|
|
58
|
+
|
|
59
|
+
module.exports = createApi
|
package/app.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const { runSaga } = require('redux-saga')
|
|
2
|
+
const { map, compose, replace } = require('ramda')
|
|
3
|
+
const { renameKeysWith } = require('ramda-adjunct')
|
|
4
|
+
|
|
5
|
+
const helperObjects = require('./schemaHelper.json')
|
|
6
|
+
const sagaHelpers = require('./sagaHelpers')
|
|
7
|
+
|
|
8
|
+
const items = require('./schemaItems.js')
|
|
9
|
+
const sku = require('./sku.js')
|
|
10
|
+
const skuBp = require('./skuBp.js')
|
|
11
|
+
|
|
12
|
+
const createApi = require('./api.js')
|
|
13
|
+
|
|
14
|
+
const sagas = require('./sagas.js')
|
|
15
|
+
const { saveSchemaSaga } = require('./saveSchema.js')
|
|
16
|
+
|
|
17
|
+
const getInstance = (options) => {
|
|
18
|
+
const api = createApi(options)
|
|
19
|
+
return compose(
|
|
20
|
+
renameKeysWith(replace('Saga', '')),
|
|
21
|
+
map(
|
|
22
|
+
(saga) => (...args) => new Promise((resolve, reject) => {
|
|
23
|
+
runSaga({
|
|
24
|
+
context: { api }
|
|
25
|
+
}, saga, ...args)
|
|
26
|
+
.toPromise()
|
|
27
|
+
.then(resolve, reject)
|
|
28
|
+
})
|
|
29
|
+
),
|
|
30
|
+
)({ ...sagas, saveSchemaSaga })
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
...helperObjects,
|
|
35
|
+
...items,
|
|
36
|
+
...sku,
|
|
37
|
+
...skuBp,
|
|
38
|
+
...sagas,
|
|
39
|
+
...sagaHelpers,
|
|
40
|
+
saveSchemaSaga,
|
|
41
|
+
getInstance
|
|
42
|
+
}
|
package/blanket.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const {
|
|
2
|
+
defaultTo,
|
|
3
|
+
nth,
|
|
4
|
+
compose,
|
|
5
|
+
length,
|
|
6
|
+
prop,
|
|
7
|
+
map,
|
|
8
|
+
uncurryN,
|
|
9
|
+
values,
|
|
10
|
+
when,
|
|
11
|
+
includes,
|
|
12
|
+
__,
|
|
13
|
+
propEq,
|
|
14
|
+
identity,
|
|
15
|
+
filter,
|
|
16
|
+
assoc,
|
|
17
|
+
chain,
|
|
18
|
+
omit,
|
|
19
|
+
pickBy,
|
|
20
|
+
has,
|
|
21
|
+
complement,
|
|
22
|
+
allPass,
|
|
23
|
+
converge,
|
|
24
|
+
concat,
|
|
25
|
+
of,
|
|
26
|
+
unnest,
|
|
27
|
+
pick
|
|
28
|
+
} = require('ramda')
|
|
29
|
+
const { renameKeys } = require('ramda-adjunct')
|
|
30
|
+
|
|
31
|
+
const { safeItems: items } = require('./schemaItems.js')
|
|
32
|
+
const { skuFromItem, itemFromSku } = require('./sku.js')
|
|
33
|
+
|
|
34
|
+
const choose = (n, xs) =>
|
|
35
|
+
n < 1 || n > xs.length
|
|
36
|
+
? []
|
|
37
|
+
: n == 1
|
|
38
|
+
? [...xs.map(x => [x])]
|
|
39
|
+
: [
|
|
40
|
+
...choose(n - 1, xs.slice(1)).map(ys => [xs[0], ...ys]),
|
|
41
|
+
...choose(n, xs.slice(1))
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
const getCombos = uncurryN(3, (min, max, xs) =>
|
|
45
|
+
xs.length == 0 || min > max
|
|
46
|
+
? []
|
|
47
|
+
: [...choose(min, xs), ...getCombos(min + 1, max, xs)]
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
const unboxSkinsRemap = when(
|
|
51
|
+
allPass([
|
|
52
|
+
complement(has)('texture'),//no texture
|
|
53
|
+
compose(//is an unbox skin
|
|
54
|
+
allPass([
|
|
55
|
+
has('texture'),
|
|
56
|
+
complement(propEq)('item_name', 'War Paint')
|
|
57
|
+
]),
|
|
58
|
+
prop(__, items),
|
|
59
|
+
prop('defindex')
|
|
60
|
+
)
|
|
61
|
+
]),
|
|
62
|
+
converge(
|
|
63
|
+
concat,
|
|
64
|
+
[
|
|
65
|
+
of,
|
|
66
|
+
compose(
|
|
67
|
+
of,
|
|
68
|
+
chain(assoc('defindex'), compose(
|
|
69
|
+
prop('defindex'),//change defindex from unbox skin defindex to weapon defindex
|
|
70
|
+
defaultTo({}),
|
|
71
|
+
nth(0),
|
|
72
|
+
values,
|
|
73
|
+
({ item_name }) => pickBy(allPass([
|
|
74
|
+
propEq('item_quality', 6),
|
|
75
|
+
propEq('item_name', item_name)
|
|
76
|
+
]), items),
|
|
77
|
+
prop(__, items),
|
|
78
|
+
prop('defindex')
|
|
79
|
+
))
|
|
80
|
+
)
|
|
81
|
+
]
|
|
82
|
+
)
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
const warPaintRemap = when(
|
|
86
|
+
allPass([
|
|
87
|
+
complement(has)('texture'),
|
|
88
|
+
compose(
|
|
89
|
+
propEq('item_name', 'War Paint'),
|
|
90
|
+
prop(__, items),
|
|
91
|
+
prop('defindex')
|
|
92
|
+
),
|
|
93
|
+
]),
|
|
94
|
+
assoc('defindex', '9536')
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
const remaps = compose(
|
|
98
|
+
unboxSkinsRemap,
|
|
99
|
+
warPaintRemap
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
const propListDefault = ['killstreakTier', 'elevated', 'festivized', 'effect', 'texture', 'wear', 'craft', 'series']
|
|
103
|
+
|
|
104
|
+
const blanketify = uncurryN(3, (propList, skus, sku) => compose(
|
|
105
|
+
map(pick(['sku', 'originalSku'])),
|
|
106
|
+
map(renameKeys({ _sku: 'sku', sku: 'originalSku' })),//reset the sku, save the original sku
|
|
107
|
+
filter(compose(includes(__, skus), prop('_sku'))),//find every new combination in the sku list
|
|
108
|
+
map(chain(assoc('_sku'), skuFromItem)),//save the new sku
|
|
109
|
+
unnest,
|
|
110
|
+
map(remaps),//decode defindices that are not possible anymore
|
|
111
|
+
map(omit(__, itemFromSku(sku))),//create items from the combinations
|
|
112
|
+
concat([[]]),//create the last combination where no prop is removed. If the skus includes the sku we can return it.
|
|
113
|
+
chain(compose(getCombos(0), length), identity),//create every possible combination
|
|
114
|
+
filter(compose(Boolean, prop(__, itemFromSku(sku))))//remove every prop from proplist that is not present in the item
|
|
115
|
+
)(propList || propListDefault))
|
|
116
|
+
|
|
117
|
+
module.exports = {
|
|
118
|
+
blanketify
|
|
119
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const { call, getContext, delay } = require('redux-saga/effects')
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
groupBy, map, compose, toPairs, uniq, pick, values, mergeRight, indexBy, props, mapObjIndexed, when, complement, has, assoc, unnest, uncurryN
|
|
5
|
+
} = require('ramda')
|
|
6
|
+
|
|
7
|
+
const getAssetClassQuery = compose(
|
|
8
|
+
map(([key, { classid, instanceid }]) => `&classid${key}=${classid}&instanceid${key}=${instanceid}`),
|
|
9
|
+
toPairs
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
const getAssetClassIds = compose(
|
|
13
|
+
uniq,
|
|
14
|
+
map(pick(['classid', 'instanceid']))
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
const transformAssetClasses = compose(
|
|
18
|
+
indexBy(props(['classid', 'instanceid'])),
|
|
19
|
+
map(
|
|
20
|
+
when(
|
|
21
|
+
complement(has)('instanceid'),
|
|
22
|
+
assoc('instanceid', '0')
|
|
23
|
+
)
|
|
24
|
+
),
|
|
25
|
+
values
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
const mergeAssetClasses = uncurryN(2, (assetClasses) => compose(
|
|
29
|
+
unnest,
|
|
30
|
+
values,
|
|
31
|
+
mapObjIndexed((v, k) => map(mergeRight(assetClasses[k]), v)),
|
|
32
|
+
groupBy(props(['classid', 'instanceid']))
|
|
33
|
+
))
|
|
34
|
+
|
|
35
|
+
function* fetchAppDataInventory(inventory, d = 1000) {
|
|
36
|
+
|
|
37
|
+
let p = 0, assetClasses = {}
|
|
38
|
+
const { getAssetClassInfo } = yield getContext('api')
|
|
39
|
+
const ids = getAssetClassIds(inventory)
|
|
40
|
+
|
|
41
|
+
while (ids.length > 0 && p < ids.length) {
|
|
42
|
+
yield delay(d)
|
|
43
|
+
const query = getAssetClassQuery(ids.slice(p, p + 100))
|
|
44
|
+
const { result } = yield call(getAssetClassInfo, query)
|
|
45
|
+
assetClasses = mergeRight(assetClasses, transformAssetClasses(result))
|
|
46
|
+
p += 100
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return mergeAssetClasses(assetClasses, inventory)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = {
|
|
53
|
+
fetchAppDataInventory
|
|
54
|
+
}
|
package/fetchItemsApi.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const { call, delay, getContext } = require('redux-saga/effects')
|
|
2
|
+
const { prop, indexBy, pick, map, compose, evolve, replace, when } = require('ramda')
|
|
3
|
+
|
|
4
|
+
const propsToKeep = [
|
|
5
|
+
'image_url'
|
|
6
|
+
]
|
|
7
|
+
|
|
8
|
+
const transformItemsApi = compose(
|
|
9
|
+
map(
|
|
10
|
+
compose(
|
|
11
|
+
pick(propsToKeep),
|
|
12
|
+
when(
|
|
13
|
+
prop('image_url'),
|
|
14
|
+
evolve({ image_url: replace('http://media.steampowered.com/apps/440/icons/', '') })
|
|
15
|
+
)
|
|
16
|
+
)),
|
|
17
|
+
indexBy(prop('defindex'))
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
function* fetchItemsApiSaga() {
|
|
21
|
+
const { getSchemaItems } = yield getContext('api')
|
|
22
|
+
let start = 0, items = []
|
|
23
|
+
do {
|
|
24
|
+
yield delay(1000)
|
|
25
|
+
const response = yield call(getSchemaItems, start)
|
|
26
|
+
items = items.concat(response.result.items)
|
|
27
|
+
start = response.result.next
|
|
28
|
+
} while (start >= 0)
|
|
29
|
+
return transformItemsApi(items)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
module.exports = { fetchItemsApiSaga }
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const { call, getContext } = require('redux-saga/effects')
|
|
2
|
+
const vdf = require('vdf')
|
|
3
|
+
|
|
4
|
+
function* fetchItemsGameSaga() {
|
|
5
|
+
const { getItemsGameUrl, getItemsGame } = yield getContext('api')
|
|
6
|
+
const { result: { items_game_url } } = yield call(getItemsGameUrl)
|
|
7
|
+
const itemsGameVdf = yield call(getItemsGame, items_game_url)
|
|
8
|
+
const { items_game } = vdf.parse(itemsGameVdf)
|
|
9
|
+
return items_game
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = { fetchItemsGameSaga }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const { call, delay, getContext } = require('redux-saga/effects')
|
|
2
|
+
const { prop, indexBy, map, compose, pickBy, complement, includes, path, nth, invert, invertObj } = require('ramda')
|
|
3
|
+
|
|
4
|
+
const transformEffects = compose(
|
|
5
|
+
invertObj,
|
|
6
|
+
map(nth(0)),
|
|
7
|
+
invert,
|
|
8
|
+
pickBy(complement(includes)('Attrib_Particle')),
|
|
9
|
+
map(prop('name')),
|
|
10
|
+
indexBy(prop('id')),
|
|
11
|
+
path(['result', 'attribute_controlled_attached_particles'])
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
function* fetchParticleEffectsSaga() {
|
|
15
|
+
const { getSchemaOverview } = yield getContext('api')
|
|
16
|
+
const schema = yield call(getSchemaOverview)
|
|
17
|
+
return transformEffects(schema)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = { fetchParticleEffectsSaga }
|
package/fetchTextures.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const { call, getContext } = require('redux-saga/effects')
|
|
2
|
+
const vdf = require('vdf')
|
|
3
|
+
|
|
4
|
+
const { path, compose, pickBy, startsWith, reduce, replace, __ } = require('ramda')
|
|
5
|
+
const { renameKeysWith } = require('ramda-adjunct')
|
|
6
|
+
|
|
7
|
+
const sanitizeTexture = reduce((all, curr) => replace(curr, '', all), __, ['9_', '_field { field_number: 2 }'])
|
|
8
|
+
|
|
9
|
+
const transformTextures = compose(
|
|
10
|
+
renameKeysWith(sanitizeTexture),
|
|
11
|
+
pickBy((val, key) => startsWith('9', key) && isNaN(val[0])),
|
|
12
|
+
path(['lang', 'Tokens'])
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
function* fetchTexturesSaga() {
|
|
16
|
+
const { fetchProtoObjDefs } = yield getContext('api')
|
|
17
|
+
const protoObjDefs = yield call(fetchProtoObjDefs)
|
|
18
|
+
return transformTextures(vdf.parse(protoObjDefs))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = { fetchTexturesSaga }
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const { call, getContext } = require('redux-saga/effects')
|
|
2
|
+
const vdf = require('vdf')
|
|
3
|
+
const { toLower } = require('ramda')
|
|
4
|
+
const { renameKeysWith } = require('ramda-adjunct')
|
|
5
|
+
|
|
6
|
+
function* fetchTfEnglishSaga() {
|
|
7
|
+
const { fetchTfEnglish } = yield getContext('api')
|
|
8
|
+
const english = yield call(fetchTfEnglish)
|
|
9
|
+
const englishVdf = vdf.parse(english)
|
|
10
|
+
return renameKeysWith(toLower, englishVdf.lang.Tokens)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports = { fetchTfEnglishSaga }
|