@ianlucas/cs2-lib-inspect 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/License.txt ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 candyboyz
4
+ Copyright (c) 2024-present Ian Lucas
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # cs2-lib-inspect
2
+
3
+ > A utility for creating inspect links for CS2 items
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @ianlucas/cs2-lib-inspect
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { CS_Economy } from "@ianlucas/cs2-lib";
15
+ import { CS_generateInspectLink } from "@ianlucas/cs2-lib-inspect";
16
+
17
+ const link = CS_generateInspectLink(CS_Economy.getById(307));
18
+ ```
@@ -0,0 +1,560 @@
1
+ // src/index.ts
2
+ import {
3
+ CS_Economy,
4
+ CS_MIN_SEED,
5
+ CS_MIN_STICKER_WEAR,
6
+ CS_MIN_WEAR,
7
+ CS_NONE,
8
+ CS_RARITY_ANCIENT_COLOR,
9
+ CS_RARITY_COMMON_COLOR,
10
+ CS_RARITY_IMMORTAL_COLOR,
11
+ CS_RARITY_LEGENDARY_COLOR,
12
+ CS_RARITY_MYTHICAL_COLOR,
13
+ CS_RARITY_RARE_COLOR,
14
+ CS_RARITY_UNCOMMON_COLOR
15
+ } from "@ianlucas/cs2-lib";
16
+ import { Buffer } from "buffer";
17
+ import CRC32 from "crc-32";
18
+
19
+ // src/econ.ts
20
+ import { WireType } from "@protobuf-ts/runtime";
21
+ import { UnknownFieldHandler } from "@protobuf-ts/runtime";
22
+ import { reflectionMergePartial } from "@protobuf-ts/runtime";
23
+ import { MessageType } from "@protobuf-ts/runtime";
24
+ var CEconItemPreviewDataBlock$Type = class extends MessageType {
25
+ constructor() {
26
+ super("CEconItemPreviewDataBlock", [
27
+ {
28
+ no: 1,
29
+ name: "accountid",
30
+ kind: "scalar",
31
+ opt: true,
32
+ T: 13
33
+ },
34
+ {
35
+ no: 2,
36
+ name: "itemid",
37
+ kind: "scalar",
38
+ opt: true,
39
+ T: 4,
40
+ L: 0
41
+ },
42
+ {
43
+ no: 3,
44
+ name: "defindex",
45
+ kind: "scalar",
46
+ opt: true,
47
+ T: 13
48
+ },
49
+ {
50
+ no: 4,
51
+ name: "paintindex",
52
+ kind: "scalar",
53
+ opt: true,
54
+ T: 13
55
+ },
56
+ {
57
+ no: 5,
58
+ name: "rarity",
59
+ kind: "scalar",
60
+ opt: true,
61
+ T: 13
62
+ },
63
+ {
64
+ no: 6,
65
+ name: "quality",
66
+ kind: "scalar",
67
+ opt: true,
68
+ T: 13
69
+ },
70
+ {
71
+ no: 7,
72
+ name: "paintwear",
73
+ kind: "scalar",
74
+ opt: true,
75
+ T: 13
76
+ },
77
+ {
78
+ no: 8,
79
+ name: "paintseed",
80
+ kind: "scalar",
81
+ opt: true,
82
+ T: 13
83
+ },
84
+ {
85
+ no: 9,
86
+ name: "killeaterscoretype",
87
+ kind: "scalar",
88
+ opt: true,
89
+ T: 13
90
+ },
91
+ {
92
+ no: 10,
93
+ name: "killeatervalue",
94
+ kind: "scalar",
95
+ opt: true,
96
+ T: 13
97
+ },
98
+ {
99
+ no: 11,
100
+ name: "customname",
101
+ kind: "scalar",
102
+ opt: true,
103
+ T: 9
104
+ },
105
+ {
106
+ no: 12,
107
+ name: "stickers",
108
+ kind: "message",
109
+ repeat: 1,
110
+ T: () => CEconItemPreviewDataBlock_Sticker
111
+ },
112
+ {
113
+ no: 13,
114
+ name: "inventory",
115
+ kind: "scalar",
116
+ opt: true,
117
+ T: 13
118
+ },
119
+ {
120
+ no: 14,
121
+ name: "origin",
122
+ kind: "scalar",
123
+ opt: true,
124
+ T: 13
125
+ },
126
+ {
127
+ no: 15,
128
+ name: "questid",
129
+ kind: "scalar",
130
+ opt: true,
131
+ T: 13
132
+ },
133
+ {
134
+ no: 16,
135
+ name: "dropreason",
136
+ kind: "scalar",
137
+ opt: true,
138
+ T: 13
139
+ },
140
+ {
141
+ no: 17,
142
+ name: "musicindex",
143
+ kind: "scalar",
144
+ opt: true,
145
+ T: 13
146
+ },
147
+ {
148
+ no: 18,
149
+ name: "entindex",
150
+ kind: "scalar",
151
+ opt: true,
152
+ T: 5
153
+ }
154
+ ]);
155
+ }
156
+ create(value) {
157
+ const message = globalThis.Object.create(this.messagePrototype);
158
+ message.stickers = [];
159
+ if (value !== void 0)
160
+ reflectionMergePartial(this, message, value);
161
+ return message;
162
+ }
163
+ internalBinaryRead(reader, length, options, target) {
164
+ let message = target ?? this.create(), end = reader.pos + length;
165
+ while (reader.pos < end) {
166
+ let [fieldNo, wireType] = reader.tag();
167
+ switch (fieldNo) {
168
+ case /* optional uint32 accountid */
169
+ 1:
170
+ message.accountid = reader.uint32();
171
+ break;
172
+ case /* optional uint64 itemid */
173
+ 2:
174
+ message.itemid = reader.uint64().toBigInt();
175
+ break;
176
+ case /* optional uint32 defindex */
177
+ 3:
178
+ message.defindex = reader.uint32();
179
+ break;
180
+ case /* optional uint32 paintindex */
181
+ 4:
182
+ message.paintindex = reader.uint32();
183
+ break;
184
+ case /* optional uint32 rarity */
185
+ 5:
186
+ message.rarity = reader.uint32();
187
+ break;
188
+ case /* optional uint32 quality */
189
+ 6:
190
+ message.quality = reader.uint32();
191
+ break;
192
+ case /* optional uint32 paintwear */
193
+ 7:
194
+ message.paintwear = reader.uint32();
195
+ break;
196
+ case /* optional uint32 paintseed */
197
+ 8:
198
+ message.paintseed = reader.uint32();
199
+ break;
200
+ case /* optional uint32 killeaterscoretype */
201
+ 9:
202
+ message.killeaterscoretype = reader.uint32();
203
+ break;
204
+ case /* optional uint32 killeatervalue */
205
+ 10:
206
+ message.killeatervalue = reader.uint32();
207
+ break;
208
+ case /* optional string customname */
209
+ 11:
210
+ message.customname = reader.string();
211
+ break;
212
+ case /* repeated CEconItemPreviewDataBlock.Sticker stickers */
213
+ 12:
214
+ message.stickers.push(
215
+ CEconItemPreviewDataBlock_Sticker.internalBinaryRead(
216
+ reader,
217
+ reader.uint32(),
218
+ options
219
+ )
220
+ );
221
+ break;
222
+ case /* optional uint32 inventory */
223
+ 13:
224
+ message.inventory = reader.uint32();
225
+ break;
226
+ case /* optional uint32 origin */
227
+ 14:
228
+ message.origin = reader.uint32();
229
+ break;
230
+ case /* optional uint32 questid */
231
+ 15:
232
+ message.questid = reader.uint32();
233
+ break;
234
+ case /* optional uint32 dropreason */
235
+ 16:
236
+ message.dropreason = reader.uint32();
237
+ break;
238
+ case /* optional uint32 musicindex */
239
+ 17:
240
+ message.musicindex = reader.uint32();
241
+ break;
242
+ case /* optional int32 entindex */
243
+ 18:
244
+ message.entindex = reader.int32();
245
+ break;
246
+ default:
247
+ let u = options.readUnknownField;
248
+ if (u === "throw")
249
+ throw new globalThis.Error(
250
+ `Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`
251
+ );
252
+ let d = reader.skip(wireType);
253
+ if (u !== false)
254
+ (u === true ? UnknownFieldHandler.onRead : u)(
255
+ this.typeName,
256
+ message,
257
+ fieldNo,
258
+ wireType,
259
+ d
260
+ );
261
+ }
262
+ }
263
+ return message;
264
+ }
265
+ internalBinaryWrite(message, writer, options) {
266
+ if (message.accountid !== void 0)
267
+ writer.tag(1, WireType.Varint).uint32(message.accountid);
268
+ if (message.itemid !== void 0)
269
+ writer.tag(2, WireType.Varint).uint64(message.itemid);
270
+ if (message.defindex !== void 0)
271
+ writer.tag(3, WireType.Varint).uint32(message.defindex);
272
+ if (message.paintindex !== void 0)
273
+ writer.tag(4, WireType.Varint).uint32(message.paintindex);
274
+ if (message.rarity !== void 0)
275
+ writer.tag(5, WireType.Varint).uint32(message.rarity);
276
+ if (message.quality !== void 0)
277
+ writer.tag(6, WireType.Varint).uint32(message.quality);
278
+ if (message.paintwear !== void 0)
279
+ writer.tag(7, WireType.Varint).uint32(message.paintwear);
280
+ if (message.paintseed !== void 0)
281
+ writer.tag(8, WireType.Varint).uint32(message.paintseed);
282
+ if (message.killeaterscoretype !== void 0)
283
+ writer.tag(9, WireType.Varint).uint32(message.killeaterscoretype);
284
+ if (message.killeatervalue !== void 0)
285
+ writer.tag(10, WireType.Varint).uint32(message.killeatervalue);
286
+ if (message.customname !== void 0)
287
+ writer.tag(11, WireType.LengthDelimited).string(message.customname);
288
+ for (let i = 0; i < message.stickers.length; i++)
289
+ CEconItemPreviewDataBlock_Sticker.internalBinaryWrite(
290
+ message.stickers[i],
291
+ writer.tag(12, WireType.LengthDelimited).fork(),
292
+ options
293
+ ).join();
294
+ if (message.inventory !== void 0)
295
+ writer.tag(13, WireType.Varint).uint32(message.inventory);
296
+ if (message.origin !== void 0)
297
+ writer.tag(14, WireType.Varint).uint32(message.origin);
298
+ if (message.questid !== void 0)
299
+ writer.tag(15, WireType.Varint).uint32(message.questid);
300
+ if (message.dropreason !== void 0)
301
+ writer.tag(16, WireType.Varint).uint32(message.dropreason);
302
+ if (message.musicindex !== void 0)
303
+ writer.tag(17, WireType.Varint).uint32(message.musicindex);
304
+ if (message.entindex !== void 0)
305
+ writer.tag(18, WireType.Varint).int32(message.entindex);
306
+ let u = options.writeUnknownFields;
307
+ if (u !== false)
308
+ (u == true ? UnknownFieldHandler.onWrite : u)(
309
+ this.typeName,
310
+ message,
311
+ writer
312
+ );
313
+ return writer;
314
+ }
315
+ };
316
+ var CEconItemPreviewDataBlock = new CEconItemPreviewDataBlock$Type();
317
+ var CEconItemPreviewDataBlock_Sticker$Type = class extends MessageType {
318
+ constructor() {
319
+ super("CEconItemPreviewDataBlock.Sticker", [
320
+ {
321
+ no: 1,
322
+ name: "slot",
323
+ kind: "scalar",
324
+ opt: true,
325
+ T: 13
326
+ },
327
+ {
328
+ no: 2,
329
+ name: "sticker_id",
330
+ kind: "scalar",
331
+ opt: true,
332
+ T: 13
333
+ },
334
+ {
335
+ no: 3,
336
+ name: "wear",
337
+ kind: "scalar",
338
+ opt: true,
339
+ T: 2
340
+ },
341
+ {
342
+ no: 4,
343
+ name: "scale",
344
+ kind: "scalar",
345
+ opt: true,
346
+ T: 2
347
+ },
348
+ {
349
+ no: 5,
350
+ name: "rotation",
351
+ kind: "scalar",
352
+ opt: true,
353
+ T: 2
354
+ },
355
+ {
356
+ no: 6,
357
+ name: "tint_id",
358
+ kind: "scalar",
359
+ opt: true,
360
+ T: 13
361
+ },
362
+ {
363
+ no: 7,
364
+ name: "offset_x",
365
+ kind: "scalar",
366
+ opt: true,
367
+ T: 13
368
+ },
369
+ {
370
+ no: 8,
371
+ name: "offset_y",
372
+ kind: "scalar",
373
+ opt: true,
374
+ T: 13
375
+ }
376
+ ]);
377
+ }
378
+ create(value) {
379
+ const message = globalThis.Object.create(this.messagePrototype);
380
+ if (value !== void 0)
381
+ reflectionMergePartial(
382
+ this,
383
+ message,
384
+ value
385
+ );
386
+ return message;
387
+ }
388
+ internalBinaryRead(reader, length, options, target) {
389
+ let message = target ?? this.create(), end = reader.pos + length;
390
+ while (reader.pos < end) {
391
+ let [fieldNo, wireType] = reader.tag();
392
+ switch (fieldNo) {
393
+ case /* optional uint32 slot */
394
+ 1:
395
+ message.slot = reader.uint32();
396
+ break;
397
+ case /* optional uint32 sticker_id */
398
+ 2:
399
+ message.stickerId = reader.uint32();
400
+ break;
401
+ case /* optional float wear */
402
+ 3:
403
+ message.wear = reader.float();
404
+ break;
405
+ case /* optional float scale */
406
+ 4:
407
+ message.scale = reader.float();
408
+ break;
409
+ case /* optional float rotation */
410
+ 5:
411
+ message.rotation = reader.float();
412
+ break;
413
+ case /* optional uint32 tint_id */
414
+ 6:
415
+ message.tintId = reader.uint32();
416
+ break;
417
+ case /* optional uint32 offset_x */
418
+ 7:
419
+ message.offsetX = reader.uint32();
420
+ break;
421
+ case /* optional uint32 offset_y */
422
+ 8:
423
+ message.offsetY = reader.uint32();
424
+ break;
425
+ default:
426
+ let u = options.readUnknownField;
427
+ if (u === "throw")
428
+ throw new globalThis.Error(
429
+ `Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`
430
+ );
431
+ let d = reader.skip(wireType);
432
+ if (u !== false)
433
+ (u === true ? UnknownFieldHandler.onRead : u)(
434
+ this.typeName,
435
+ message,
436
+ fieldNo,
437
+ wireType,
438
+ d
439
+ );
440
+ }
441
+ }
442
+ return message;
443
+ }
444
+ internalBinaryWrite(message, writer, options) {
445
+ if (message.slot !== void 0)
446
+ writer.tag(1, WireType.Varint).uint32(message.slot);
447
+ if (message.stickerId !== void 0)
448
+ writer.tag(2, WireType.Varint).uint32(message.stickerId);
449
+ if (message.wear !== void 0)
450
+ writer.tag(3, WireType.Bit32).float(message.wear);
451
+ if (message.scale !== void 0)
452
+ writer.tag(4, WireType.Bit32).float(message.scale);
453
+ if (message.rotation !== void 0)
454
+ writer.tag(5, WireType.Bit32).float(message.rotation);
455
+ if (message.tintId !== void 0)
456
+ writer.tag(6, WireType.Varint).uint32(message.tintId);
457
+ if (message.offsetX !== void 0)
458
+ writer.tag(7, WireType.Varint).uint32(message.offsetX);
459
+ if (message.offsetY !== void 0)
460
+ writer.tag(8, WireType.Varint).uint32(message.offsetY);
461
+ let u = options.writeUnknownFields;
462
+ if (u !== false)
463
+ (u == true ? UnknownFieldHandler.onWrite : u)(
464
+ this.typeName,
465
+ message,
466
+ writer
467
+ );
468
+ return writer;
469
+ }
470
+ };
471
+ var CEconItemPreviewDataBlock_Sticker = new CEconItemPreviewDataBlock_Sticker$Type();
472
+
473
+ // src/index.ts
474
+ var CS_RARITY_INT = {
475
+ [CS_RARITY_COMMON_COLOR]: 1,
476
+ [CS_RARITY_UNCOMMON_COLOR]: 2,
477
+ [CS_RARITY_RARE_COLOR]: 3,
478
+ [CS_RARITY_MYTHICAL_COLOR]: 4,
479
+ [CS_RARITY_LEGENDARY_COLOR]: 5,
480
+ [CS_RARITY_ANCIENT_COLOR]: 6,
481
+ [CS_RARITY_IMMORTAL_COLOR]: 7
482
+ };
483
+ var MISSING_ITEM_DEF = {
484
+ graffiti: 1348,
485
+ patch: 4609,
486
+ sticker: 1209,
487
+ musickit: 1314
488
+ };
489
+ var CS_PREVIEW_URL = "steam://rungame/730/76561202255233023/+csgo_econ_action_preview%20";
490
+ function floatToBytes(floatValue) {
491
+ const floatArray = new Float32Array(1);
492
+ floatArray[0] = floatValue;
493
+ const byteArray = new Uint32Array(floatArray.buffer);
494
+ return byteArray[0];
495
+ }
496
+ function getItemAttributes(item) {
497
+ const { def, index, rarity, type, tint, wearmin } = item;
498
+ const hasStickerObject = MISSING_ITEM_DEF[type] !== void 0;
499
+ const hasPaintIndex = !hasStickerObject && type !== "musickit";
500
+ return {
501
+ defindex: MISSING_ITEM_DEF[type] ?? def,
502
+ musicindex: type === "musickit" ? index : void 0,
503
+ paintindex: hasPaintIndex ? index : void 0,
504
+ paintseed: CS_Economy.hasSeed(item) ? CS_MIN_SEED : void 0,
505
+ paintwear: CS_Economy.hasWear(item) ? wearmin ?? CS_MIN_WEAR : void 0,
506
+ rarity: CS_RARITY_INT[rarity] ?? 0,
507
+ stickers: hasStickerObject ? [
508
+ {
509
+ tintId: tint,
510
+ stickerId: index,
511
+ slot: 0
512
+ }
513
+ ] : []
514
+ };
515
+ }
516
+ function getBaseInventoryItemAttributes({
517
+ id,
518
+ nametag,
519
+ seed,
520
+ stattrak,
521
+ stickers,
522
+ stickerswear,
523
+ wear
524
+ }) {
525
+ const item = CS_Economy.getById(id);
526
+ const baseAttributes = getItemAttributes(item);
527
+ return {
528
+ ...baseAttributes,
529
+ customname: nametag,
530
+ killeaterscoretype: stattrak !== void 0 ? 0 : void 0,
531
+ killeatervalue: stattrak,
532
+ paintseed: CS_Economy.hasSeed(item) ? seed ?? CS_MIN_SEED : void 0,
533
+ paintwear: CS_Economy.hasWear(item) ? floatToBytes(wear ?? item.wearmin ?? CS_MIN_WEAR) : void 0,
534
+ stickers: stickers !== void 0 ? stickers.map((sticker, slot) => ({
535
+ slot,
536
+ stickerId: CS_Economy.getById(sticker).index,
537
+ wear: stickerswear?.[slot] ?? CS_MIN_STICKER_WEAR
538
+ })).filter(({ stickerId }) => stickerId !== CS_NONE) : baseAttributes.stickers
539
+ };
540
+ }
541
+ function generateHex(attributes) {
542
+ console.log(attributes);
543
+ const payload = Buffer.concat([Uint8Array.from([0]), CEconItemPreviewDataBlock.toBinary(attributes)]);
544
+ const crc = CRC32.buf(payload);
545
+ const xCrc = crc & 65535 ^ CEconItemPreviewDataBlock.toBinary(attributes).byteLength * crc;
546
+ const crcBuffer = Buffer.alloc(4);
547
+ crcBuffer.writeUInt32BE((xCrc & 4294967295) >>> 0, 0);
548
+ const buffer = Buffer.concat([payload, crcBuffer]);
549
+ return buffer.toString("hex").toUpperCase();
550
+ }
551
+ function CS_generateInspectLink(item) {
552
+ const econ = {
553
+ ...typeof item.uid === void 0 ? getItemAttributes(item) : getBaseInventoryItemAttributes(item)
554
+ };
555
+ return `${CS_PREVIEW_URL}${generateHex(econ)}`;
556
+ }
557
+ export {
558
+ CS_PREVIEW_URL,
559
+ CS_generateInspectLink
560
+ };
@@ -0,0 +1,6 @@
1
+ import { CS_Item, CS_BaseInventoryItem, CS_InventoryItem } from '@ianlucas/cs2-lib';
2
+
3
+ declare const CS_PREVIEW_URL = "steam://rungame/730/76561202255233023/+csgo_econ_action_preview%20";
4
+ declare function CS_generateInspectLink(item: CS_Item | CS_BaseInventoryItem | CS_InventoryItem): string;
5
+
6
+ export { CS_PREVIEW_URL, CS_generateInspectLink };
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@ianlucas/cs2-lib-inspect",
3
+ "version": "1.0.0",
4
+ "description": "A utility for creating inspect links for CS2 items",
5
+ "license": "MIT",
6
+ "repository": "ianlucas/cs2-lib-inspect",
7
+ "main": "dist/esm/index.js",
8
+ "types": "dist/index.d.mts",
9
+ "files": [
10
+ "dist/**/*"
11
+ ],
12
+ "scripts": {
13
+ "format": "prettier . --write",
14
+ "prepack": "tsup --legacy-output",
15
+ "protoc": "npx protoc --ts_out src --proto_path protos protos/econ.proto",
16
+ "test": "jest"
17
+ },
18
+ "dependencies": {
19
+ "@protobuf-ts/runtime": "^2.9.4",
20
+ "buffer": "^6.0.3",
21
+ "crc-32": "^1.2.2"
22
+ },
23
+ "peerDependencies": {
24
+ "@ianlucas/cs2-lib": "^4.5.0"
25
+ },
26
+ "devDependencies": {
27
+ "@protobuf-ts/plugin": "^2.9.3",
28
+ "prettier": "^3.2.5",
29
+ "tsup": "^8.0.1",
30
+ "typescript": "^5.3.3"
31
+ }
32
+ }