| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- import { allItems, _Tiers, Tiers } from "../../Types/Items";
- import { Inject, Injectable } from "../../Injector/ServiceDecorator";
- import { ItemManagerFeatureIfc, ItemManagerIfc } from "./RPCInterface";
- import { FrontworkComponent } from "../../Types/FrontworkComponent";
- import { TableDefinitionExporter } from "../../Types/Interfaces";
- import { TableDefiniton, Item, User, Character, SRToken, SRPriority, Spec, Signup, Raid } from "../../Types/Types";
- import { IAdmin } from "../../Admin/Interface";
- import { IItemManager } from "./Interface";
- import { getLogger } from "log4js";
- import { IUserManager } from "../User/Interface";
- import { ICharacterManager } from "../Character/Interface";
- import { IPubSub } from "../PubSub/Interface";
- import { IRaidManager } from "../Raid/Interface";
-
- const fetch = require('node-fetch')
- const xml2js = require('xml2js');
- const parser = new xml2js.Parser(/* options */);
-
- @Injectable(IItemManager)
- export class ItemManager
- implements FrontworkComponent<ItemManagerIfc, ItemManagerFeatureIfc>, TableDefinitionExporter, IItemManager{
- name = "ItemManager" as "ItemManager";
-
- @Inject(IAdmin)
- private admin: IAdmin
-
- @Inject(IUserManager)
- private userManager: IUserManager
-
- @Inject(ICharacterManager)
- private character: ICharacterManager
-
- @Inject(IPubSub)
- private pubsub: IPubSub<any>
-
- @Inject(IRaidManager)
- private raidManager: IRaidManager
-
- exportRPCs = () => [
- this.getItems,
- this.getItem,
- this.buyToken,
- this.calculatePriorities,
- this.getToken,
- this.getTokens,
- this.getAllPriorities
- ]
-
- exportRPCFeatures = () => [{
- name: 'managePriorities' as 'managePriorities',
- exportRPCs: () => [
- this.setPriority,
- this.deletePriority
- ],
- },{
- name: 'reset' as 'reset',
- exportRPCs: () => [
- this.wipeCurrencyAndItems
- ]
- }]
-
- notifyRaid = async (raid:Raid | {id:number}) => {
- const data = await this.raidManager.getRaidData(<Raid>raid)
- this.pubsub.publish(""+raid.id, data)
- await this.notifyRaids()
- }
-
- notifyRaids = async () => {
- this.pubsub.publish('raids', undefined)
- }
-
- wipeCurrencyAndItems = async () => {
- await Promise.all([
- this.userManager.wipeCurrency(),
- Promise.all(_Tiers.map(tier => this.admin.knex(tier+'tokens').where(true).del()))
- ])
- }
-
- getItems = async () :Promise<Item[]> => await this.admin.knex.select('*').from('items')
-
- getItem = async(name:string):Promise<Item> => {
- return await this.admin.knex('items').select('*').where('itemname','=',name).first()
- }
-
- fetchItem = async (name:string):Promise<Item> => {
- const res = await fetch('https://classic.wowhead.com/item='+name+'&xml');
- const txt = await res.text();
- const r = await parser.parseStringPromise(txt);
-
- try{
- return <Item>{
- itemname: r.wowhead.item[0].name[0],
- iconname: r.wowhead.item[0].icon[0]._,
- url: r.wowhead.item[0].link[0],
- quality: r.wowhead.item[0].quality[0]._,
- }
- }catch (e){
- console.log(name)
- throw e
- }
- }
-
- getTableDefinitions(): TableDefiniton[] {
- return [
- ...['null',..._Tiers].map(tier => {
- return {
- name: tier+'tokens',
- tableBuilder: (table) => {
- table.primary(['characterid', 'itemname'])
- table.integer("characterid")
- table.foreign("characterid").references("id").inTable('characters')
- table.string("itemname")
- table.foreign("itemname").references("itemname").inTable('items')
- table.string("signupid").nullable()
- table.foreign("signupid").references("id").inTable('signups').onDelete('SET NULL')
- table.integer("level").defaultTo(1)
- }
- }
- }),{
- name: 'priorities',
- tableBuilder: (table) => {
- table.increments('id').primary()
- table.string('itemname')
- table.foreign('itemname').references('itemname').inTable('items')
- table.string('race').nullable()
- table.integer('specid').nullable()
- table.foreign('specid').references('id').inTable('specs')
- table.integer('modifier')
- table.string('description').nullable()
- }
- },{
- name: 'items',
- tableBuilder: (table) => {
- table.string('itemname').unique().notNullable().primary()
- table.string('iconname').notNullable()
- table.string('url').notNullable()
- table.string('quality').defaultTo('Epic').notNullable()
- table.boolean('hidden').defaultTo(false).notNullable()
- table.enu('tier', _Tiers).notNullable()
- }
- }]
- }
-
- buyToken = async (usertoken: string, charactername:string, itemname:string, signup: Signup): Promise<(SRToken & Character & Item) | undefined> => {
- const record = this.userManager.getUserRecordByToken(usertoken)
- const character = await this.character.getCharacterByName(charactername)
-
- if(!record || !character || record.user.username !== character.username) return
-
- const item = await this.getItem(itemname)
- if(!item) return
-
- const currency = await this.userManager.getCurrency(record.user, item.tier)
- if(currency < 1) return
-
- const streaks = await this.getTokens(character, [item.tier], false)
- const activeTokens = await this.getTokens(character, [item.tier], true)
-
- await this.userManager.decrementCurrency(record.user, item.tier, 1)
- const modifier = await this.calculatePriorities(itemname, character)
-
- const tx = await this.admin.knex.transaction()
-
- if(streaks.length > 0){
- const myStreak = streaks.find(token => token.itemname === itemname)
- if(myStreak){
- //getLogger('ItemManager').debug('update signupid and increment level')
- await this.admin
- .knex(item.tier+'tokens')
- .transacting(tx)
- .where({
- characterid: character.id,
- itemname: item.itemname
- })
- .update({
- signupid: signup.id,
- level: myStreak.level+1
- })
-
- }
-
- //getLogger('ItemManager').debug('delete streaks')
- await Promise.all(streaks.map(async s => await this.admin
- .knex(item.tier+'tokens')
- .transacting(tx)
- .where({
- itemname: s.itemname,
- characterid: character.id,
- signupid: null
- })
- .del()
- ))
-
- if(myStreak){
- await tx.commit()
- await this.notifyRaid({id: signup.raidid})
-
- return await this.getToken(character, item)
- }
- }
-
- const matchingReserve = activeTokens.find(token => token.itemname === itemname)
- if(matchingReserve){
- //getLogger('ItemManager').debug('upgrade reserve')
- await this.admin
- .knex(item.tier+'tokens')
- .transacting(tx)
- .increment('level')
- .where({
- signupid: signup.id,
- characterid: character.id,
- itemname: item.itemname
- })
- }else{
- //getLogger('ItemManager').debug('new reserve')
- await this.admin
- .knex(item.tier+'tokens')
- .transacting(tx)
- .insert({
- characterid: character.id,
- itemname: item.itemname,
- level: 1+modifier,
- signupid: signup.id
- })
- }
- await tx.commit()
- await this.notifyRaid({id: signup.raidid})
-
- return await this.getToken(character, item)
- }
-
- getAllPriorities = async() :Promise<(SRPriority & Spec & Item)[]> => this.admin
- .knex('priorities as p')
- .join('items as i', 'p.itemname', '=', 'i.itemname')
- .leftJoin('specs as s', 'p.specid', '=', 's.id')
- .select('*')
-
- deletePriority = async(priority:SRPriority) : Promise<void> => {
- await this.admin.knex('priorities')
- .where(priority)
- .del()
- }
-
- setPriority = async(itemname:string, priority: SRPriority) : Promise<void> => {
- const item = await this.getItem(itemname)
-
- await this.admin
- .knex('priorities')
- .insert(<SRPriority>{
- itemname: item.itemname,
- ...priority
- })
- }
-
- getPriorities = async(itemname:string) : Promise<SRPriority[]> => {
- return await this.admin
- .knex('priorities as p')
- .where('p.itemname', '=', itemname)
- .select('*')
- }
-
- calculatePriorities = async (itemname: string, character:Character):Promise<number>=> {
- const rules : SRPriority[] = await this.admin
- .knex('priorities as p')
- .select('*')
- .join('items as i', 'i.itemname', '=', 'p.itemname')
- .where('p.itemname', '=', itemname)
-
- return rules.map(rule => {
- if(rule.specid && rule.race){
- if(rule.specid === character.specid && rule.race === character.race)
- return rule.modifier
- else
- return 0
- }
-
- if(rule.specid){
- if(rule.specid === character.specid)
- return rule.modifier
- else
- return 0
- }
-
- if(rule.race){
- if(rule.race === character.race)
- return rule.modifier
- else
- return 0
- }
- return 0
- }).reduce((prev, curr) => prev+curr, 0)
- }
-
- getToken = async (character:Character, item:Item, valid=true): Promise<(SRToken & Character & Item) | undefined>=> {
- return await this.admin
- .knex(item.tier+'tokens as t')
- .select('*')
- .join('characters as c', 'c.id', '=', 't.characterid')
- .join('items as i', 'i.itemname', '=', 't.itemname')
- .where({
- characterid: character.id,
- "i.itemname": item.itemname
- })
- .andWhere(function(){
- if(valid){
- this.whereNotNull('t.signupid')
- }else{
- this.whereNull('t.signupid')
- }
- })
- .first()
- }
-
- getTokens = async (character:Character, tiers:Tiers[], valid=true) : Promise<(SRToken & Character & Item)[]> => {
- const ret = await Promise.all(tiers.map(async tier => {
- return await this.admin
- .knex(tier+'tokens as t')
- .select('*')
- .join('characters as c', 'c.id', '=', 't.characterid')
- .join('items as i', 'i.itemname', '=', 't.itemname')
- .where({
- characterid: character.id,
- })
- .andWhere(function(){
- if(valid){
- this.whereNotNull('t.signupid')
- }else{
- this.whereNull('t.signupid')
- }
- })
- }))
- return ret.flat()
- }
-
- countItems = async() :Promise<number> => {
- const count = await this.admin
- .knex('items')
- .count('*');
-
- return <number>count[0]['count(*)']
- }
-
- private initialized = false
- initialize = async () => {
- if(this.initialized) return
- this.initialized = true
-
- const itemTiers = allItems
-
- const countCache = await this.countItems()
-
- if(countCache != Object.values(itemTiers).flat().length){
- await Promise.all(
- Object.entries(itemTiers)
- .map((kv) => Promise.all(
- kv[1].map(i => this.fetchItem(i)
- .then(item =>
- this.admin
- .knex('items')
- .insert({
- tier: kv[0],
- ...item
- }).catch(() => {})
- )
- )
- ))
- )
- }
- }
- }
|