diff --git a/__tests__/schema.test.ts b/__tests__/schema.test.ts index 34fb440..d5b9853 100644 --- a/__tests__/schema.test.ts +++ b/__tests__/schema.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test, mock } from "bun:test"; -import { ItemSchema, ItemSlot, getItemIndexByName, getTradableStatusByDefindex, getTradableStatusByName, prepareSchema } from '../src/content/schemaService' +import { ItemSchema, ItemSlot, getItemIndexByName, getTradableStatusByDefindex, getTradableStatusByName, linkFestiveVariants, prepareSchema } from '../src/content/schemaService' // Mock the storage and log functions mock.module('../src/content/storage', () => ({ @@ -19,35 +19,48 @@ const mockSchema: ItemSchema = { slot: ItemSlot.Primary, tradable: false, hasAustraliumVariant: false, - canKillstreakify: true + canKillstreakify: true, + festiveVariant: null }, '208': { name: 'Flame Thrower', slot: ItemSlot.Primary, tradable: true, hasAustraliumVariant: true, - canKillstreakify: true + canKillstreakify: true, + festiveVariant: 659 + }, + '659': { + name: 'Festive Flame Thrower', + slot: ItemSlot.Primary, + tradable: true, + hasAustraliumVariant: false, + canKillstreakify: true, + festiveVariant: null }, '5021': { name: 'Mann Co. Supply Crate Key', slot: ItemSlot.Tool, tradable: true, hasAustraliumVariant: false, - canKillstreakify: false + canKillstreakify: false, + festiveVariant: null }, '15141': { name: 'Flame Thrower', slot: ItemSlot.Primary, tradable: true, hasAustraliumVariant: false, - canKillstreakify: true + canKillstreakify: true, + festiveVariant: null }, '69420': { name: 'Non-Tradable Item', slot: ItemSlot.Misc, tradable: false, hasAustraliumVariant: false, - canKillstreakify: false + canKillstreakify: false, + festiveVariant: null } } @@ -117,4 +130,75 @@ describe('Schema Service', () => { } }); }); + + describe('linkFestiveVariants', () => { + test('should link festive variants to their original counterparts', () => { + const testSchema = { + '21': { // Stock (should be ignored) + name: 'Flame Thrower', + slot: ItemSlot.Primary, + tradable: false, + hasAustraliumVariant: false, + canKillstreakify: false, + festiveVariant: null + }, + '208': { // Original Flame Thrower + name: 'Flame Thrower', + slot: ItemSlot.Primary, + tradable: true, + hasAustraliumVariant: true, + canKillstreakify: true, + festiveVariant: null + }, + '659': { // Festive Flame Thrower (should be detected) + name: 'Festive Flame Thrower', + slot: ItemSlot.Primary, + tradable: true, + hasAustraliumVariant: false, + canKillstreakify: true, + festiveVariant: null + }, + '15141': { // Decorated (should be ignored) + name: 'Flame Thrower', + slot: ItemSlot.Primary, + tradable: true, + hasAustraliumVariant: false, + canKillstreakify: true, + festiveVariant: null + } + }; + const mockResponseItems = [ + { item_class: 'tf_weapon_flamethrower', defindex: 21, item_name: 'Flame Thrower' }, // Incorrect; stock + { item_class: 'tf_weapon_flamethrower', defindex: 208, item_name: 'Flame Thrower' }, // Original + { item_class: 'tf_weapon_flamethrower', defindex: 659, item_name: 'Festive Flame Thrower' }, // Festive + { item_class: 'tf_weapon_flamethrower', defindex: 15141, item_name: 'Flame Thrower' }, // Incorrect; decorated + ]; + linkFestiveVariants(mockResponseItems, testSchema) + expect(testSchema['21'].festiveVariant).toBeNull() + expect(testSchema['208'].festiveVariant).toBe(659) + expect(testSchema['659'].festiveVariant).toBeNull(); + expect(testSchema['15141'].festiveVariant).toBeNull() + }) + + test('should not link if no festive variant exists', () => { + const testSchema = { + '163': { + name: 'Crit-a-Cola', + slot: ItemSlot.Secondary, + tradable: true, + hasAustraliumVariant: false, + canKillstreakify: false, + festiveVariant: null + } + }; + + const mockResponseItems = [ + { item_class: 'tf_weapon_lunchbox_drink', defindex: 163, item_name: 'Crit-a-Cola' } + ]; + + linkFestiveVariants(mockResponseItems, testSchema); + + expect(testSchema['163'].festiveVariant).toBeNull(); + }); + }); }) \ No newline at end of file diff --git a/src/content/content.ts b/src/content/content.ts index c41d706..088fea0 100644 --- a/src/content/content.ts +++ b/src/content/content.ts @@ -237,6 +237,42 @@ async function inject() { })) } + // Check item schema for Festive variant of current defindex + if(itemSchema[itemIndex].festiveVariant != null) { + promises.push(new Promise(async (resolve) => { + logDebug(`Fetching price for Festive ${itemName}`) + var data: ItemPriceData | null + try { + data = await fetchPrice(token, `${itemSchema[itemIndex].festiveVariant};6`, currentTime); + updateTime = new Date(data.update) + } catch { + log(`Festive ${itemName} is unpriced or unavailable, skipping...`) + } + + const priceRow = createPriceRow($T("Festive"), data, keyPrice, locale, "https://wiki.teamfortress.com/wiki/Festive_weapons") + + priceRows.push({quality: 97, row: priceRow}) + resolve() + return + })) + promises.push(new Promise(async (resolve) => { + logDebug(`Fetching price for Strange Festive ${itemName}`) + var data: ItemPriceData | null + try { + data = await fetchPrice(token, `${itemSchema[itemIndex].festiveVariant};11`, currentTime); + updateTime = new Date(data.update) + } catch { + log(`Strange Festive ${itemName} is unpriced or unavailable, skipping...`) + } + + const priceRow = createPriceRow($T("Strange Festive"), data, keyPrice, locale, "https://wiki.teamfortress.com/wiki/Festive_weapons") + + priceRows.push({quality: 98, row: priceRow}) + resolve() + return + })) + } + Promise.all(promises).then(() => { priceRows.sort((a, b) => { // Sort 6 first always, then numerically diff --git a/src/content/schemaService.ts b/src/content/schemaService.ts index e86a2f0..3521169 100644 --- a/src/content/schemaService.ts +++ b/src/content/schemaService.ts @@ -38,6 +38,7 @@ export class ItemSchema { slot: ItemSlot, tradable: Boolean, hasAustraliumVariant: Boolean, + festiveVariant: number | null canKillstreakify: Boolean }; } @@ -70,6 +71,25 @@ export function getTradableStatusByName(schema: ItemSchema, name: string, exclud return true } +export function linkFestiveVariants(items: Array<{item_class: string, defindex: number, item_name: string}>, schema: ItemSchema): void { + if(!schema) return + items.filter(item => + item.item_class != null && + item.item_class.startsWith('tf_weapon') && + item.item_name.startsWith('Festive ') + ).forEach(festive => { + const originalName = festive.item_name.slice(8); // "Festive " is 8 chars + const original = items.find(item => item.item_name === originalName && item.defindex > 30 && (item.defindex < 15000 || item.defindex >= 16000)); + + if (original) { + if(schema[original.defindex]) { + schema[original.defindex].festiveVariant = festive.defindex; + console.log(`original:${original.defindex},festive:${festive.defindex}`); + } + } + }); +} + export async function wipeSchema(): Promise { await setStorageValue(storage_version, __VERSION__) await setStorageValue(storage_schema, null) @@ -149,6 +169,8 @@ export async function prepareSchema(): Promise { } }); + linkFestiveVariants(responseItems, cacheItems) + await setStorageValue(storage_schema, (cacheItems)); itemSchema = cacheItems await setStorageValue(storage_version, __VERSION__);