You've already forked tf2wikipricing
205 lines
6.8 KiB
TypeScript
205 lines
6.8 KiB
TypeScript
import { getStorageValue, setStorageValue } from './storage'
|
|
import { logDebug, log, logError } from './utils/log'
|
|
import './config'
|
|
declare function GM_fetch(input: string | URL | globalThis.Request, init?: RequestInit): Promise<Response>
|
|
import './GM_fetch'
|
|
import { storage_version, storage_schema, storage_lastUpdateTime } from './config'
|
|
import Australiums from '../resources/australiums.json'
|
|
const semver = require('semver')
|
|
|
|
export function checkAustraliumVariant(defindex: number): boolean {
|
|
return Object.prototype.hasOwnProperty.call(Australiums, defindex.toString());
|
|
}
|
|
|
|
export declare const __VERSION__: string;
|
|
|
|
function isDateAfterOneDay(date1: Date, date2: Date): boolean {
|
|
const diff = date2.getTime() - date1.getTime();
|
|
const diffDays = Math.round(diff / (1000 * 3600 * 24));
|
|
return diffDays > 1;
|
|
}
|
|
|
|
export enum ItemSlot {
|
|
Primary = "primary",
|
|
Secondary = "secondary",
|
|
Melee = "melee",
|
|
PDA = "pda",
|
|
PDA2 = "pda2",
|
|
Building = "building",
|
|
Misc = "misc",
|
|
Special = "special",
|
|
Taunt = "taunt",
|
|
Tool = "tool",
|
|
}
|
|
|
|
export class ItemSchema {
|
|
[key: string]: {
|
|
name: string,
|
|
slot: ItemSlot,
|
|
tradable: Boolean,
|
|
hasAustraliumVariant: Boolean,
|
|
festiveVariant: number | null
|
|
botkillerVariants: Array<number> | null
|
|
canKillstreakify: Boolean
|
|
};
|
|
}
|
|
|
|
export function getItemIndexByName(schema: ItemSchema, name: string, excludeStock: Boolean = true, excludeDecorated: Boolean = true) {
|
|
for (const [defindex, value] of Object.entries(schema)) {
|
|
if (value['name'] == name) {
|
|
const index = parseInt(defindex)
|
|
if(excludeStock && index <= 30) continue
|
|
if(excludeDecorated && (index >= 15000 && index < 16000)) continue
|
|
return index
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
export function getTradableStatusByDefindex(schema: ItemSchema, defindex: number) {
|
|
return schema[defindex.toString()].tradable
|
|
}
|
|
|
|
export function getTradableStatusByName(schema: ItemSchema, name: string, excludeStock: Boolean = true, excludeDecorated = true,) {
|
|
for (const [defindex, value] of Object.entries(schema)) {
|
|
if (value['name'] == name) {
|
|
const index = parseInt(defindex)
|
|
if(excludeStock && index <= 30) continue
|
|
if(excludeDecorated && (index >= 15000 && index < 16000)) continue
|
|
return value.tradable
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
export function linkBotkillerVariants(items: Array<{item_class: string, defindex: number, item_name: string, item_type_name: string}>, schema: ItemSchema): void {
|
|
if(!schema) return
|
|
items.filter(item =>
|
|
item.item_class != null &&
|
|
item.item_class.startsWith('tf_weapon') &&
|
|
item.item_name.includes('Botkiller')
|
|
).forEach(botkiller => {
|
|
const originalName = botkiller.item_type_name
|
|
const original = items.find(item => item.item_name === originalName && item.defindex > 30 && (item.defindex < 15000 || item.defindex >= 16000));
|
|
if (original) {
|
|
if(schema[original.defindex]) {
|
|
if(schema[original.defindex].botkillerVariants == null) {
|
|
// init array
|
|
schema[original.defindex].botkillerVariants = new Array<number>()
|
|
}
|
|
schema[original.defindex].botkillerVariants.push(botkiller.defindex)
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
export async function wipeSchema(): Promise<void> {
|
|
await setStorageValue(storage_version, __VERSION__)
|
|
await setStorageValue(storage_schema, null)
|
|
await setStorageValue(storage_lastUpdateTime, new Date().toISOString())
|
|
logDebug(`Schema wiped`)
|
|
}
|
|
|
|
export async function prepareSchema(): Promise<ItemSchema> {
|
|
let needsUpdate: boolean = false
|
|
let itemSchema: ItemSchema | null = null
|
|
|
|
const storedVersion: string | null = await getStorageValue(storage_version, null)
|
|
if(!storedVersion || !semver.valid(storedVersion)) {
|
|
log(`Cache is from an unknown version of the extension. Updating for version ${__VERSION__}`);
|
|
needsUpdate = true
|
|
} else if(semver.valid(storedVersion) && semver.lt(storedVersion, __VERSION__)) {
|
|
log(`Cache is from a previous version (${storedVersion}) of the extension. Updating for version ${__VERSION__}`);
|
|
needsUpdate = true
|
|
} else {
|
|
log(`Cache is from current version (${storedVersion}) of the extension.`);
|
|
itemSchema = await getStorageValue(storage_schema, null);
|
|
}
|
|
|
|
const update = await getStorageValue(storage_lastUpdateTime, null)
|
|
if (update) {
|
|
const lastUpdateTime = new Date(update);
|
|
log(`Item schema updated at ${lastUpdateTime}`);
|
|
if (itemSchema == null || Object.keys(itemSchema).length === 0 || isDateAfterOneDay(lastUpdateTime, new Date())) {
|
|
needsUpdate = true
|
|
}
|
|
}
|
|
|
|
if(needsUpdate) {
|
|
log("Item Schema out of Date. Rebuilding...");
|
|
const url = "https://raw.githubusercontent.com/danocmx/node-tf2-static-schema/master/static/items.json"
|
|
const response = await GM_fetch(url);
|
|
if (response.ok) {
|
|
await setStorageValue(storage_lastUpdateTime, new Date().getTime());
|
|
|
|
const cacheItems = {}
|
|
|
|
const responseItems: any[] = await response.json()
|
|
// We want to keep the keys `defindex`, `item_name`, and `attributes`
|
|
responseItems.forEach((item: any) => {
|
|
const defindex: number = item['defindex']
|
|
|
|
let tradable: boolean = true
|
|
try {
|
|
if(item['attributes'] != null) {
|
|
if(item['attributes'].find((attribute: {}) => (attribute as any)['class'] == "cannot_trade")) {
|
|
tradable = false
|
|
}
|
|
}
|
|
} catch(error) {
|
|
logError(error)
|
|
log(item)
|
|
}
|
|
|
|
let canKillstreakify: boolean = false
|
|
try {
|
|
if(item['capabilities'] != null) {
|
|
if(item['capabilities']['can_killstreakify'] != null && item['capabilities']['can_killstreakify'] == true) {
|
|
canKillstreakify = true
|
|
}
|
|
}
|
|
} catch(error) {
|
|
logError(error)
|
|
log(item)
|
|
}
|
|
|
|
(cacheItems as any)[defindex.toString()] = {
|
|
"name": item['item_name'],
|
|
"slot": item['item_slot'],
|
|
"tradable": tradable,
|
|
"canKillstreakify": canKillstreakify,
|
|
"hasAustraliumVariant": checkAustraliumVariant(defindex)
|
|
}
|
|
});
|
|
|
|
linkFestiveVariants(responseItems, cacheItems)
|
|
linkBotkillerVariants(responseItems, cacheItems)
|
|
|
|
await setStorageValue(storage_schema, (cacheItems));
|
|
itemSchema = cacheItems
|
|
await setStorageValue(storage_version, __VERSION__);
|
|
logDebug(`Item schema updated at ${new Date()}`)
|
|
} else {
|
|
logError("Could not fetch item schema.");
|
|
}
|
|
}
|
|
return itemSchema
|
|
} |