tf2-sku-to-name 1.0.6__py3-none-any.whl → 2.0.0__py3-none-any.whl
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.
- sku/__init__.py +13 -0
- sku/parser.py +531 -104
- sku/schema.py +646 -0
- tf2_sku_to_name-2.0.0.dist-info/METADATA +106 -0
- tf2_sku_to_name-2.0.0.dist-info/RECORD +9 -0
- {tf2_sku_to_name-1.0.6.dist-info → tf2_sku_to_name-2.0.0.dist-info}/WHEEL +1 -1
- sku/data/data.json +0 -70183
- tf2_sku_to_name-1.0.6.dist-info/METADATA +0 -17
- tf2_sku_to_name-1.0.6.dist-info/RECORD +0 -8
- {tf2_sku_to_name-1.0.6.dist-info → tf2_sku_to_name-2.0.0.dist-info/licenses}/LICENSE +0 -0
- {tf2_sku_to_name-1.0.6.dist-info → tf2_sku_to_name-2.0.0.dist-info}/top_level.txt +0 -0
sku/schema.py
ADDED
@@ -0,0 +1,646 @@
|
|
1
|
+
import json
|
2
|
+
import time
|
3
|
+
import requests
|
4
|
+
from typing import Dict, Optional
|
5
|
+
from pathlib import Path
|
6
|
+
|
7
|
+
|
8
|
+
class Schema:
|
9
|
+
PAINT_MAPPINGS = {
|
10
|
+
'(paint: color no. 216-190-216)': 3100495,
|
11
|
+
'(paint: a color similar to slate)': 3100495,
|
12
|
+
'(paint: indubitably green)': 7511618,
|
13
|
+
'(paint: color no. 729e42)': 7511618,
|
14
|
+
'(paint: zepheniah\'s greed)': 4345659,
|
15
|
+
'(paint: color no. 424f3b)': 4345659,
|
16
|
+
'(paint: noble hatter\'s violet)': 5322826,
|
17
|
+
'(paint: color no. 51384a)': 5322826,
|
18
|
+
'(paint: color no. 483838)': 5322826,
|
19
|
+
'(paint: a deep commitment to purple)': 8208497,
|
20
|
+
'(paint: color no. 7d4071)': 8208497,
|
21
|
+
'(paint: mann co. orange)': 12377523,
|
22
|
+
'(paint: color no. cf7336)': 12377523,
|
23
|
+
'(paint: muskelmannbraun)': 10843461,
|
24
|
+
'(paint: color no. a57545)': 10843461,
|
25
|
+
'(paint: peculiarly drab tincture)': 12955537,
|
26
|
+
'(paint: color no. c5af91)': 12955537,
|
27
|
+
'(paint: radigan conagher brown)': 6901050,
|
28
|
+
'(paint: color no. 694d3a)': 6901050,
|
29
|
+
'(paint: ye olde rustic colour)': 8154199,
|
30
|
+
'(paint: color no. 7c6c57)': 8154199,
|
31
|
+
'(paint: australium gold)': 15185211,
|
32
|
+
'(paint: color no. e7b53b)': 15185211,
|
33
|
+
'(paint: aged moustache grey)': 8289918,
|
34
|
+
'(paint: color no. 7e7e7e)': 8289918,
|
35
|
+
'(paint: an extraordinary abundance of tinge)': 15132390,
|
36
|
+
'(paint: color no. e6e6e6)': 15132390,
|
37
|
+
'(paint: a distinctive lack of hue)': 1315860,
|
38
|
+
'(paint: color no. 141414)': 1315860,
|
39
|
+
'(paint: pink as hell)': 16738740,
|
40
|
+
'(paint: color no. ff69b4)': 16738740,
|
41
|
+
'(paint: the bitter taste of defeat and lime)': 3329330,
|
42
|
+
'(paint: color no. 32cd32)': 3329330,
|
43
|
+
'(paint: the color of a gentlemann\'s business pants)': 15787660,
|
44
|
+
'(paint: color no. f0e68c)': 15787660,
|
45
|
+
'(paint: dark salmon injustice)': 15308410,
|
46
|
+
'(paint: color no. e9967a)': 15308410,
|
47
|
+
'(paint: a mann\'s mint)': 12377306,
|
48
|
+
'(paint: color no. bcddb3)': 12377306,
|
49
|
+
'(paint: after eight)': 2960676,
|
50
|
+
'(paint: color no. 2d2d24)': 2960676,
|
51
|
+
'(paint: legacy paint)': 5801378,
|
52
|
+
'(paint: waterlogged lab coat)': 11049612,
|
53
|
+
'(paint: color no. a89a8c)': 11049612,
|
54
|
+
'(paint: balaclavas are forever)': 3874595,
|
55
|
+
'(paint: color no. 3b1f23)': 3874595,
|
56
|
+
'(paint: an air of debonair)': 6637376,
|
57
|
+
'(paint: color no. 654740)': 6637376,
|
58
|
+
'(paint: the value of teamwork)': 8400928,
|
59
|
+
'(paint: color no. 803020)': 8400928,
|
60
|
+
'(paint: cream spirit)': 12807213,
|
61
|
+
'(paint: color no. c36c2d)': 12807213,
|
62
|
+
'(paint: operator\'s overalls)': 4732984,
|
63
|
+
'(paint: color no. 483838)': 4732984,
|
64
|
+
'(paint: drably olive)': 8421376,
|
65
|
+
'(paint: color no. 808000)': 8421376,
|
66
|
+
}
|
67
|
+
|
68
|
+
MUNITION_CRATES = {
|
69
|
+
82: 5734, 83: 5735, 84: 5742, 85: 5752,
|
70
|
+
90: 5781, 91: 5802, 92: 5803, 103: 5859
|
71
|
+
}
|
72
|
+
|
73
|
+
PISTOL_SKINS = {
|
74
|
+
0: 15013, 18: 15018, 35: 15035, 41: 15041, 46: 15046,
|
75
|
+
56: 15056, 61: 15061, 63: 15060, 69: 15100, 70: 15101,
|
76
|
+
74: 15102, 78: 15126, 81: 15148
|
77
|
+
}
|
78
|
+
|
79
|
+
ROCKET_LAUNCHER_SKINS = {
|
80
|
+
1: 15014, 6: 15006, 28: 15028, 43: 15043, 52: 15052,
|
81
|
+
57: 15057, 60: 15081, 69: 15104, 70: 15105, 76: 15129,
|
82
|
+
79: 15130, 80: 15150
|
83
|
+
}
|
84
|
+
|
85
|
+
MEDIGUN_SKINS = {
|
86
|
+
2: 15010, 5: 15008, 25: 15025, 39: 15039, 50: 15050,
|
87
|
+
65: 15078, 72: 15097, 76: 15120, 78: 15121, 79: 15122,
|
88
|
+
81: 15145, 83: 15146
|
89
|
+
}
|
90
|
+
|
91
|
+
REVOLVER_SKINS = {
|
92
|
+
3: 15011, 27: 15027, 42: 15042, 51: 15051, 63: 15064,
|
93
|
+
64: 15062, 65: 15063, 72: 15103, 76: 15127, 77: 15128,
|
94
|
+
81: 15149
|
95
|
+
}
|
96
|
+
|
97
|
+
STICKYBOMB_SKINS = {
|
98
|
+
4: 15012, 8: 15009, 24: 15024, 38: 15038, 45: 15045,
|
99
|
+
48: 15048, 60: 15082, 62: 15083, 63: 15084, 68: 15113,
|
100
|
+
76: 15137, 78: 15138, 81: 15155
|
101
|
+
}
|
102
|
+
|
103
|
+
SNIPER_RIFLE_SKINS = {
|
104
|
+
7: 15007, 14: 15000, 19: 15019, 23: 15023, 33: 15033,
|
105
|
+
59: 15059, 62: 15070, 64: 15071, 65: 15072, 76: 15135,
|
106
|
+
66: 15111, 67: 15112, 78: 15136, 82: 15154
|
107
|
+
}
|
108
|
+
|
109
|
+
FLAME_THROWER_SKINS = {
|
110
|
+
9: 15005, 17: 15017, 30: 15030, 34: 15034, 49: 15049,
|
111
|
+
54: 15054, 60: 15066, 61: 15068, 62: 15067, 66: 15089,
|
112
|
+
67: 15090, 76: 15115, 80: 15141
|
113
|
+
}
|
114
|
+
|
115
|
+
MINIGUN_SKINS = {
|
116
|
+
10: 15004, 20: 15020, 26: 15026, 31: 15031, 40: 15040,
|
117
|
+
55: 15055, 61: 15088, 62: 15087, 63: 15086, 70: 15098,
|
118
|
+
73: 15099, 76: 15123, 77: 15125, 78: 15124, 84: 15147
|
119
|
+
}
|
120
|
+
|
121
|
+
SCATTERGUN_SKINS = {
|
122
|
+
11: 15002, 15: 15015, 21: 15021, 29: 15029, 36: 15036,
|
123
|
+
53: 15053, 61: 15069, 63: 15065, 69: 15106, 72: 15107,
|
124
|
+
74: 15108, 76: 15131, 83: 15157, 85: 15151
|
125
|
+
}
|
126
|
+
|
127
|
+
SHOTGUN_SKINS = {
|
128
|
+
12: 15003, 16: 15016, 44: 15044, 47: 15047, 60: 15085,
|
129
|
+
72: 15109, 76: 15132, 78: 15133, 86: 15152
|
130
|
+
}
|
131
|
+
|
132
|
+
SMG_SKINS = {
|
133
|
+
13: 15001, 22: 15022, 32: 15032, 37: 15037, 58: 15058,
|
134
|
+
65: 15076, 69: 15110, 79: 15134, 81: 15153
|
135
|
+
}
|
136
|
+
|
137
|
+
WRENCH_SKINS = {
|
138
|
+
60: 15074, 61: 15073, 64: 15075, 75: 15114,
|
139
|
+
77: 15140, 78: 15139, 82: 15156
|
140
|
+
}
|
141
|
+
|
142
|
+
GRENADE_LAUNCHER_SKINS = {
|
143
|
+
60: 15077, 63: 15079, 67: 15091, 68: 15092,
|
144
|
+
76: 15116, 77: 15117, 80: 15142, 84: 15158
|
145
|
+
}
|
146
|
+
|
147
|
+
KNIFE_SKINS = {
|
148
|
+
64: 15080, 69: 15094, 70: 15095, 71: 15096,
|
149
|
+
77: 15119, 78: 15118, 81: 15143, 82: 15144
|
150
|
+
}
|
151
|
+
|
152
|
+
EXCLUSIVE_GENUINE = {
|
153
|
+
810: 831, 811: 832, 812: 833, 813: 834, 814: 835,
|
154
|
+
815: 836, 816: 837, 817: 838, 30720: 30740,
|
155
|
+
30721: 30741, 30724: 30739
|
156
|
+
}
|
157
|
+
|
158
|
+
EXCLUSIVE_GENUINE_REVERSED = {
|
159
|
+
831: 810, 832: 811, 833: 812, 834: 813, 835: 814,
|
160
|
+
836: 815, 837: 816, 838: 817, 30740: 30720,
|
161
|
+
30741: 30721, 30739: 30724
|
162
|
+
}
|
163
|
+
|
164
|
+
RETIRED_KEYS = {
|
165
|
+
'5049': {'defindex': 5049, 'name': 'Festive Winter Crate Key'},
|
166
|
+
'5067': {'defindex': 5067, 'name': 'Refreshing Summer Cooler Key'},
|
167
|
+
'5072': {'defindex': 5072, 'name': 'Naughty Winter Crate Key'},
|
168
|
+
'5073': {'defindex': 5073, 'name': 'Nice Winter Crate Key'},
|
169
|
+
'5079': {'defindex': 5079, 'name': 'Scorched Key'},
|
170
|
+
'5081': {'defindex': 5081, 'name': 'Fall Key'},
|
171
|
+
'5628': {'defindex': 5628, 'name': 'Eerie Key'},
|
172
|
+
'5631': {'defindex': 5631, 'name': 'Naughty Winter Crate Key 2012'},
|
173
|
+
'5632': {'defindex': 5632, 'name': 'Nice Winter Crate Key 2012'},
|
174
|
+
'5713': {'defindex': 5713, 'name': 'Spooky Key'},
|
175
|
+
'5716': {'defindex': 5716, 'name': 'Naughty Winter Crate Key 2013'},
|
176
|
+
'5717': {'defindex': 5717, 'name': 'Nice Winter Crate Key 2013'},
|
177
|
+
'5762': {'defindex': 5762, 'name': 'Limited Late Summer Crate Key'},
|
178
|
+
'5791': {'defindex': 5791, 'name': 'Naughty Winter Crate Key 2014'},
|
179
|
+
'5792': {'defindex': 5792, 'name': 'Nice Winter Crate Key 2014'}
|
180
|
+
}
|
181
|
+
|
182
|
+
CACHE_DIR = Path.home() / '.tf2_sku_cache'
|
183
|
+
SCHEMA_CACHE_FILE = CACHE_DIR / 'schema.json'
|
184
|
+
CACHE_DURATION = 24 * 60 * 60 * 30 # 30 days
|
185
|
+
|
186
|
+
def __init__(self, data: Optional[Dict] = None, api_key: Optional[str] = None, use_autobot: bool = True):
|
187
|
+
self.api_key = api_key
|
188
|
+
self.use_autobot = use_autobot
|
189
|
+
|
190
|
+
if data:
|
191
|
+
self.version = data.get('version')
|
192
|
+
self.raw = data.get('raw')
|
193
|
+
self.time = data.get('time', int(time.time() * 1000))
|
194
|
+
else:
|
195
|
+
# Load from cache or fetch new schema
|
196
|
+
if not self._load_from_cache():
|
197
|
+
self._fetch_schema()
|
198
|
+
|
199
|
+
self._set_properties_data()
|
200
|
+
|
201
|
+
def _set_properties_data(self):
|
202
|
+
self.crate_series_list = self._get_crate_series_list()
|
203
|
+
self.qualities = self._get_qualities()
|
204
|
+
self.effects = self._get_particle_effects()
|
205
|
+
self.paintkits = self._get_paint_kits()
|
206
|
+
self.paints = self._get_paints()
|
207
|
+
|
208
|
+
def _load_from_cache(self) -> bool:
|
209
|
+
if not self.SCHEMA_CACHE_FILE.exists():
|
210
|
+
return False
|
211
|
+
|
212
|
+
try:
|
213
|
+
with open(self.SCHEMA_CACHE_FILE, 'r') as f:
|
214
|
+
data = json.load(f)
|
215
|
+
|
216
|
+
if time.time() * 1000 - data.get('time', 0) > self.CACHE_DURATION * 1000:
|
217
|
+
return False
|
218
|
+
|
219
|
+
self.version = data.get('version')
|
220
|
+
self.raw = data.get('raw')
|
221
|
+
self.time = data.get('time')
|
222
|
+
return True
|
223
|
+
except Exception:
|
224
|
+
return False
|
225
|
+
|
226
|
+
def _save_to_cache(self):
|
227
|
+
try:
|
228
|
+
self.CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
229
|
+
with open(self.SCHEMA_CACHE_FILE, 'w') as f:
|
230
|
+
json.dump({
|
231
|
+
'version': self.version,
|
232
|
+
'raw': self.raw,
|
233
|
+
'time': self.time
|
234
|
+
}, f)
|
235
|
+
except Exception:
|
236
|
+
pass # Ignore cache write errors
|
237
|
+
|
238
|
+
def _fetch_schema(self):
|
239
|
+
if self.use_autobot:
|
240
|
+
self._fetch_from_autobot()
|
241
|
+
else:
|
242
|
+
if not self.api_key:
|
243
|
+
raise ValueError("API key required for Steam API schema fetch")
|
244
|
+
self._fetch_from_steam()
|
245
|
+
|
246
|
+
def _fetch_from_autobot(self):
|
247
|
+
try:
|
248
|
+
response = requests.get('https://schema.autobot.tf/schema')
|
249
|
+
response.raise_for_status()
|
250
|
+
data = response.json()
|
251
|
+
|
252
|
+
self.version = data.get('version')
|
253
|
+
self.raw = data.get('raw')
|
254
|
+
self.time = data.get('time', int(time.time() * 1000))
|
255
|
+
|
256
|
+
self._save_to_cache()
|
257
|
+
except Exception as e:
|
258
|
+
raise ValueError(f"Failed to fetch schema from autobot.tf: {e}")
|
259
|
+
|
260
|
+
def _fetch_from_steam(self):
|
261
|
+
raise NotImplementedError("Steam API schema fetching not yet implemented")
|
262
|
+
|
263
|
+
def get_item_by_defindex(self, defindex: int) -> Optional[Dict]:
|
264
|
+
items = self.raw['schema']['items']
|
265
|
+
|
266
|
+
start, end = 0, len(items) - 1
|
267
|
+
while start <= end:
|
268
|
+
mid = (start + end) // 2
|
269
|
+
if items[mid]['defindex'] < defindex:
|
270
|
+
start = mid + 1
|
271
|
+
elif items[mid]['defindex'] > defindex:
|
272
|
+
end = mid - 1
|
273
|
+
else:
|
274
|
+
return items[mid]
|
275
|
+
|
276
|
+
for item in items:
|
277
|
+
if item['defindex'] == defindex:
|
278
|
+
return item
|
279
|
+
|
280
|
+
return None
|
281
|
+
|
282
|
+
def get_item_by_item_name(self, name: str) -> Optional[Dict]:
|
283
|
+
name_lower = name.lower()
|
284
|
+
|
285
|
+
for item in self.raw['schema']['items']:
|
286
|
+
if name_lower == item['item_name'].lower():
|
287
|
+
if item['item_name'] == 'Name Tag' and item['defindex'] == 2093:
|
288
|
+
continue
|
289
|
+
|
290
|
+
if item.get('item_quality', 0) == 0:
|
291
|
+
continue
|
292
|
+
|
293
|
+
return item
|
294
|
+
|
295
|
+
return None
|
296
|
+
|
297
|
+
def get_item_by_item_name_with_the(self, name: str) -> Optional[Dict]:
|
298
|
+
name_lower = name.lower()
|
299
|
+
|
300
|
+
if 'the ' in name_lower:
|
301
|
+
name_lower = name_lower.replace('the ', '').strip()
|
302
|
+
|
303
|
+
for item in self.raw['schema']['items']:
|
304
|
+
item_name = item['item_name'].lower()
|
305
|
+
|
306
|
+
if 'the ' in item_name:
|
307
|
+
item_name = item_name.replace('the ', '').strip()
|
308
|
+
|
309
|
+
if name_lower == item_name:
|
310
|
+
if item['item_name'] == 'Name Tag' and item['defindex'] == 2093:
|
311
|
+
continue
|
312
|
+
|
313
|
+
if item.get('item_quality', 0) == 0:
|
314
|
+
continue
|
315
|
+
|
316
|
+
return item
|
317
|
+
|
318
|
+
return None
|
319
|
+
|
320
|
+
def get_quality_by_id(self, quality_id: int) -> Optional[str]:
|
321
|
+
qualities = self.raw['schema']['qualities']
|
322
|
+
quality_names = self.raw['schema']['qualityNames']
|
323
|
+
|
324
|
+
for name, qid in qualities.items():
|
325
|
+
if qid == quality_id:
|
326
|
+
return quality_names.get(name)
|
327
|
+
|
328
|
+
return None
|
329
|
+
|
330
|
+
def get_quality_id_by_name(self, name: str) -> Optional[int]:
|
331
|
+
qualities = self.raw['schema']['qualities']
|
332
|
+
quality_names = self.raw['schema']['qualityNames']
|
333
|
+
|
334
|
+
name_lower = name.lower()
|
335
|
+
for key, quality_name in quality_names.items():
|
336
|
+
if quality_name.lower() == name_lower:
|
337
|
+
return qualities.get(key)
|
338
|
+
|
339
|
+
return None
|
340
|
+
|
341
|
+
def get_effect_by_id(self, effect_id: int) -> Optional[str]:
|
342
|
+
particles = self.raw['schema']['attribute_controlled_attached_particles']
|
343
|
+
|
344
|
+
start, end = 0, len(particles) - 1
|
345
|
+
while start <= end:
|
346
|
+
mid = (start + end) // 2
|
347
|
+
if particles[mid]['id'] < effect_id:
|
348
|
+
start = mid + 1
|
349
|
+
elif particles[mid]['id'] > effect_id:
|
350
|
+
end = mid - 1
|
351
|
+
else:
|
352
|
+
return particles[mid]['name']
|
353
|
+
|
354
|
+
for particle in particles:
|
355
|
+
if particle['id'] == effect_id:
|
356
|
+
return particle['name']
|
357
|
+
|
358
|
+
return None
|
359
|
+
|
360
|
+
def get_effect_id_by_name(self, name: str) -> Optional[int]:
|
361
|
+
particles = self.raw['schema']['attribute_controlled_attached_particles']
|
362
|
+
name_lower = name.lower()
|
363
|
+
|
364
|
+
for particle in particles:
|
365
|
+
if particle['name'].lower() == name_lower:
|
366
|
+
return particle['id']
|
367
|
+
|
368
|
+
return None
|
369
|
+
|
370
|
+
def get_skin_by_id(self, skin_id: int) -> Optional[str]:
|
371
|
+
paintkits = self.raw['schema'].get('paintkits', {})
|
372
|
+
return paintkits.get(str(skin_id))
|
373
|
+
|
374
|
+
def get_skin_id_by_name(self, name: str) -> Optional[int]:
|
375
|
+
paintkits = self.raw['schema'].get('paintkits', {})
|
376
|
+
name_lower = name.lower()
|
377
|
+
|
378
|
+
for sid, skin_name in paintkits.items():
|
379
|
+
if skin_name.lower() == name_lower:
|
380
|
+
return int(sid)
|
381
|
+
|
382
|
+
return None
|
383
|
+
|
384
|
+
def get_paint_name_by_decimal(self, decimal: int) -> Optional[str]:
|
385
|
+
if decimal == 5801378:
|
386
|
+
return 'Legacy Paint'
|
387
|
+
|
388
|
+
paint_cans = [item for item in self.raw['schema']['items']
|
389
|
+
if 'Paint Can' in item.get('name', '') and item.get('name') != 'Paint Can']
|
390
|
+
|
391
|
+
for paint in paint_cans:
|
392
|
+
if 'attributes' not in paint:
|
393
|
+
continue
|
394
|
+
|
395
|
+
for attr in paint['attributes']:
|
396
|
+
if attr.get('value') == decimal:
|
397
|
+
return paint['item_name']
|
398
|
+
|
399
|
+
return None
|
400
|
+
|
401
|
+
def get_paint_decimal_by_name(self, name: str) -> Optional[int]:
|
402
|
+
if name == 'Legacy Paint':
|
403
|
+
return 5801378
|
404
|
+
|
405
|
+
paint_cans = [item for item in self.raw['schema']['items']
|
406
|
+
if 'Paint Can' in item.get('name', '') and item.get('name') != 'Paint Can']
|
407
|
+
|
408
|
+
name_lower = name.lower()
|
409
|
+
for paint in paint_cans:
|
410
|
+
if paint['item_name'].lower() == name_lower:
|
411
|
+
if 'attributes' in paint and paint['attributes']:
|
412
|
+
return paint['attributes'][0]['value']
|
413
|
+
|
414
|
+
return None
|
415
|
+
|
416
|
+
def _get_crate_series_list(self) -> Dict[int, int]:
|
417
|
+
crate_series = {}
|
418
|
+
|
419
|
+
for item in self.raw['schema']['items']:
|
420
|
+
if 'attributes' in item:
|
421
|
+
for attr in item['attributes']:
|
422
|
+
if attr.get('name') == 'set supply crate series':
|
423
|
+
crate_series[item['defindex']] = attr['value']
|
424
|
+
break
|
425
|
+
|
426
|
+
if 'items_game' in self.raw:
|
427
|
+
items = self.raw['items_game'].get('items', {})
|
428
|
+
for defindex, item_data in items.items():
|
429
|
+
if 'static_attrs' in item_data:
|
430
|
+
series_attr = item_data['static_attrs'].get('set supply crate series')
|
431
|
+
if series_attr:
|
432
|
+
value = series_attr.get('value', series_attr) if isinstance(series_attr, dict) else series_attr
|
433
|
+
crate_series[int(defindex)] = int(value)
|
434
|
+
|
435
|
+
return crate_series
|
436
|
+
|
437
|
+
def _get_qualities(self) -> Dict[str, int]:
|
438
|
+
qualities_raw = self.raw['schema']['qualities']
|
439
|
+
quality_names = self.raw['schema']['qualityNames']
|
440
|
+
|
441
|
+
return {quality_names[key]: value for key, value in qualities_raw.items() if key in quality_names}
|
442
|
+
|
443
|
+
def _get_particle_effects(self) -> Dict[str, int]:
|
444
|
+
effects = {}
|
445
|
+
previous = ''
|
446
|
+
|
447
|
+
for particle in self.raw['schema']['attribute_controlled_attached_particles']:
|
448
|
+
name = particle['name']
|
449
|
+
if name and name != previous:
|
450
|
+
effects[name] = particle['id']
|
451
|
+
|
452
|
+
if name == 'Eerie Orbiting Fire':
|
453
|
+
effects.pop('Orbiting Fire', None)
|
454
|
+
effects['Orbiting Fire'] = 33
|
455
|
+
elif name == 'Nether Trail':
|
456
|
+
effects.pop('Ether Trail', None)
|
457
|
+
effects['Ether Trail'] = 103
|
458
|
+
elif name == 'Refragmenting Reality':
|
459
|
+
effects.pop('Fragmenting Reality', None)
|
460
|
+
effects['Fragmenting Reality'] = 141
|
461
|
+
|
462
|
+
previous = name
|
463
|
+
|
464
|
+
effects.pop('', None)
|
465
|
+
return effects
|
466
|
+
|
467
|
+
def _get_paint_kits(self) -> Dict[str, int]:
|
468
|
+
paintkits = self.raw['schema'].get('paintkits', {})
|
469
|
+
return {name: int(kit_id) for kit_id, name in paintkits.items()}
|
470
|
+
|
471
|
+
def _get_paints(self) -> Dict[str, int]:
|
472
|
+
paints = {}
|
473
|
+
|
474
|
+
paint_cans = [item for item in self.raw['schema']['items']
|
475
|
+
if 'Paint Can' in item.get('name', '') and item.get('name') != 'Paint Can']
|
476
|
+
|
477
|
+
for paint in paint_cans:
|
478
|
+
if 'attributes' in paint and paint['attributes']:
|
479
|
+
paints[paint['item_name']] = paint['attributes'][0]['value']
|
480
|
+
|
481
|
+
paints['Legacy Paint'] = 5801378
|
482
|
+
return paints
|
483
|
+
|
484
|
+
def check_existence(self, item: Dict) -> bool:
|
485
|
+
schema_item = self.get_item_by_defindex(item['defindex'])
|
486
|
+
if not schema_item:
|
487
|
+
return False
|
488
|
+
|
489
|
+
if schema_item.get('item_quality') in [0, 3, 5, 11]:
|
490
|
+
if item.get('quality') != schema_item['item_quality']:
|
491
|
+
return False
|
492
|
+
|
493
|
+
if ((item.get('quality') != 1 and item['defindex'] in self.EXCLUSIVE_GENUINE_REVERSED) or
|
494
|
+
(item.get('quality') == 1 and item['defindex'] in self.EXCLUSIVE_GENUINE)):
|
495
|
+
return False
|
496
|
+
|
497
|
+
if str(item['defindex']) in self.RETIRED_KEYS:
|
498
|
+
if item['defindex'] in [5713, 5716, 5717, 5762]:
|
499
|
+
if item.get('craftable', True):
|
500
|
+
return False
|
501
|
+
elif item['defindex'] not in [5791, 5792]:
|
502
|
+
if not item.get('craftable', True):
|
503
|
+
return False
|
504
|
+
|
505
|
+
if schema_item.get('item_class') == 'supply_crate' and not item.get('crateseries'):
|
506
|
+
if item['defindex'] not in [5739, 5760, 5737, 5738]:
|
507
|
+
return False
|
508
|
+
|
509
|
+
if (item.get('quality', 6) != 6 or item.get('killstreak', 0) != 0 or
|
510
|
+
item.get('australium', False) or item.get('effect') or
|
511
|
+
item.get('festive', False) or item.get('paintkit') or
|
512
|
+
item.get('wear') or item.get('quality2') or
|
513
|
+
item.get('craftnumber') or item.get('target') or
|
514
|
+
item.get('output') or item.get('outputQuality') or
|
515
|
+
item.get('paint')):
|
516
|
+
return False
|
517
|
+
|
518
|
+
if item.get('crateseries'):
|
519
|
+
valid_series = [
|
520
|
+
1, 3, 7, 12, 13, 18, 19, 23, 26, 31, 34, 39, 43, 47, 54, 57, 75,
|
521
|
+
2, 4, 8, 11, 14, 17, 20, 24, 27, 32, 37, 42, 44, 49, 56, 71, 76,
|
522
|
+
5, 9, 10, 15, 16, 21, 25, 28, 29, 33, 38, 41, 45, 55, 59, 77,
|
523
|
+
30, 40, 50, 82, 83, 84, 85, 90, 91, 92, 103
|
524
|
+
]
|
525
|
+
|
526
|
+
if item['crateseries'] not in valid_series:
|
527
|
+
if item['crateseries'] not in self.crate_series_list.values():
|
528
|
+
return False
|
529
|
+
|
530
|
+
if item['crateseries'] != self.crate_series_list.get(item['defindex']):
|
531
|
+
return False
|
532
|
+
else:
|
533
|
+
series = item['crateseries']
|
534
|
+
defindex = item['defindex']
|
535
|
+
|
536
|
+
valid = False
|
537
|
+
if series in [1, 3, 7, 12, 13, 18, 19, 23, 26, 31, 34, 39, 43, 47, 54, 57, 75]:
|
538
|
+
valid = defindex == 5022
|
539
|
+
elif series in [2, 4, 8, 11, 14, 17, 20, 24, 27, 32, 37, 42, 44, 49, 56, 71, 76]:
|
540
|
+
valid = defindex == 5041
|
541
|
+
elif series in [5, 9, 10, 15, 16, 21, 25, 28, 29, 33, 38, 41, 45, 55, 59, 77]:
|
542
|
+
valid = defindex == 5045
|
543
|
+
elif series in [30, 40, 50]:
|
544
|
+
valid = defindex == 5068
|
545
|
+
elif series in self.MUNITION_CRATES:
|
546
|
+
valid = defindex == self.MUNITION_CRATES[series]
|
547
|
+
|
548
|
+
if not valid:
|
549
|
+
return False
|
550
|
+
|
551
|
+
return True
|
552
|
+
|
553
|
+
def get_name(self, item: Dict, proper: bool = True, use_pipe_for_skin: bool = False,
|
554
|
+
scm_format: bool = False) -> Optional[str]:
|
555
|
+
schema_item = self.get_item_by_defindex(item['defindex'])
|
556
|
+
if not schema_item:
|
557
|
+
return None
|
558
|
+
|
559
|
+
name = ''
|
560
|
+
|
561
|
+
if not scm_format and not item.get('tradable', True):
|
562
|
+
name = 'Non-Tradable '
|
563
|
+
|
564
|
+
if not scm_format and not item.get('craftable', True):
|
565
|
+
name += 'Non-Craftable '
|
566
|
+
|
567
|
+
if item.get('quality2'):
|
568
|
+
quality2_name = self.get_quality_by_id(item['quality2'])
|
569
|
+
elevated_suffix = '(e)' if not scm_format and (item.get('wear') or item.get('paintkit')) else ''
|
570
|
+
name += f"{quality2_name}{elevated_suffix} "
|
571
|
+
|
572
|
+
should_add_quality = (
|
573
|
+
(item.get('quality') == 6 and item.get('quality2')) or
|
574
|
+
(item.get('quality') not in [6, 15, 5]) or
|
575
|
+
(item.get('quality') == 5 and not item.get('effect')) or
|
576
|
+
(item.get('quality') == 5 and scm_format) or
|
577
|
+
schema_item.get('item_quality') == 5
|
578
|
+
)
|
579
|
+
|
580
|
+
if should_add_quality:
|
581
|
+
name += f"{self.get_quality_by_id(item.get('quality', 6))} "
|
582
|
+
|
583
|
+
if not scm_format and item.get('effect'):
|
584
|
+
name += f"{self.get_effect_by_id(item['effect'])} "
|
585
|
+
|
586
|
+
if item.get('festive'):
|
587
|
+
name += 'Festivized '
|
588
|
+
|
589
|
+
if item.get('killstreak', 0) > 0:
|
590
|
+
ks_names = ['Killstreak', 'Specialized Killstreak', 'Professional Killstreak']
|
591
|
+
name += f"{ks_names[item['killstreak'] - 1]} "
|
592
|
+
|
593
|
+
if item.get('target'):
|
594
|
+
target_item = self.get_item_by_defindex(item['target'])
|
595
|
+
if target_item:
|
596
|
+
name += f"{target_item['item_name']} "
|
597
|
+
|
598
|
+
if item.get('outputQuality') and item['outputQuality'] != 6:
|
599
|
+
name = f"{self.get_quality_by_id(item['outputQuality'])} {name}"
|
600
|
+
|
601
|
+
if item.get('output'):
|
602
|
+
output_item = self.get_item_by_defindex(item['output'])
|
603
|
+
if output_item:
|
604
|
+
name += f"{output_item['item_name']} "
|
605
|
+
|
606
|
+
if item.get('australium'):
|
607
|
+
name += 'Australium '
|
608
|
+
|
609
|
+
if item.get('paintkit'):
|
610
|
+
skin_name = self.get_skin_by_id(item['paintkit'])
|
611
|
+
if skin_name:
|
612
|
+
separator = ' | ' if use_pipe_for_skin else ' '
|
613
|
+
name += f"{skin_name}{separator}"
|
614
|
+
|
615
|
+
if proper and not name and schema_item.get('proper_name'):
|
616
|
+
name = 'The '
|
617
|
+
|
618
|
+
if str(item['defindex']) in self.RETIRED_KEYS:
|
619
|
+
name += self.RETIRED_KEYS[str(item['defindex'])]['name']
|
620
|
+
else:
|
621
|
+
name += schema_item['item_name']
|
622
|
+
|
623
|
+
if item.get('wear'):
|
624
|
+
wear_names = ['Factory New', 'Minimal Wear', 'Field-Tested', 'Well-Worn', 'Battle Scarred']
|
625
|
+
name += f" ({wear_names[item['wear'] - 1]})"
|
626
|
+
|
627
|
+
if item.get('crateseries'):
|
628
|
+
has_attr = (schema_item.get('attributes') and
|
629
|
+
schema_item['attributes'][0].get('class') == 'supply_crate_series')
|
630
|
+
if scm_format:
|
631
|
+
if has_attr:
|
632
|
+
name += f" Series %23{item['crateseries']}"
|
633
|
+
else:
|
634
|
+
name += f" #{item['crateseries']}"
|
635
|
+
elif item.get('craftnumber'):
|
636
|
+
name += f" #{item['craftnumber']}"
|
637
|
+
|
638
|
+
if not scm_format and item.get('paint'):
|
639
|
+
paint_name = self.get_paint_name_by_decimal(item['paint'])
|
640
|
+
if paint_name:
|
641
|
+
name += f" (Paint: {paint_name})"
|
642
|
+
|
643
|
+
if scm_format and item.get('wear') and item.get('effect') and item.get('quality') == 15:
|
644
|
+
name = f"Unusual {name}"
|
645
|
+
|
646
|
+
return name
|