@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 ADDED
@@ -0,0 +1,3 @@
1
+ sku playground: https://juice789.github.io/tf2items/
2
+
3
+ todo readme
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
+ }
@@ -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 }
@@ -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 }