mirror of
https://github.com/vkaelin/LeagueStats.git
synced 2026-03-25 12:57:28 +00:00
feat: add Jax Service back
This commit is contained in:
parent
753bda3418
commit
63b42f7c86
15 changed files with 2535 additions and 21 deletions
21
server-v2/app/Services/Jax/JaxConfig.ts
Normal file
21
server-v2/app/Services/Jax/JaxConfig.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
import Env from '@ioc:Adonis/Core/Env'
|
||||||
|
|
||||||
|
export interface JaxConfig {
|
||||||
|
key: string
|
||||||
|
region: string
|
||||||
|
requestOptions: JaxConfigRequestOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface JaxConfigRequestOptions {
|
||||||
|
retriesBeforeAbort: number
|
||||||
|
delayBeforeRetry: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export const JAX_CONFIG: JaxConfig = {
|
||||||
|
key: Env.get('API_KEY') as string,
|
||||||
|
region: 'euw1',
|
||||||
|
requestOptions: {
|
||||||
|
retriesBeforeAbort: 3,
|
||||||
|
delayBeforeRetry: 1000,
|
||||||
|
},
|
||||||
|
}
|
||||||
4
server-v2/app/Services/Jax/index.ts
Normal file
4
server-v2/app/Services/Jax/index.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
import Jax from './src/Jax'
|
||||||
|
import { JAX_CONFIG } from './JaxConfig'
|
||||||
|
|
||||||
|
export = new Jax(JAX_CONFIG)
|
||||||
53
server-v2/app/Services/Jax/src/CDragonRequest.ts
Normal file
53
server-v2/app/Services/Jax/src/CDragonRequest.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { promisify } from 'util'
|
||||||
|
import got from 'got'
|
||||||
|
import Logger from '@ioc:Adonis/Core/Logger'
|
||||||
|
import Redis from '@ioc:Adonis/Addons/Redis'
|
||||||
|
import { JaxConfig } from '../JaxConfig'
|
||||||
|
|
||||||
|
export default class CDragonRequest {
|
||||||
|
private config: JaxConfig
|
||||||
|
private endpoint: string
|
||||||
|
private cacheTime: number
|
||||||
|
private retries: number
|
||||||
|
private sleep: { (ms: number): Promise<void>; <T>(ms: number, value: T): Promise<T> }
|
||||||
|
|
||||||
|
constructor(config: JaxConfig, endpoint: string, cacheTime: number) {
|
||||||
|
this.config = config
|
||||||
|
this.endpoint = endpoint
|
||||||
|
this.cacheTime = cacheTime
|
||||||
|
this.retries = config.requestOptions.retriesBeforeAbort
|
||||||
|
|
||||||
|
this.sleep = promisify(setTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/items.json
|
||||||
|
// https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/perks.json
|
||||||
|
// https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/perkstyles.json
|
||||||
|
// https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champion-summary.json
|
||||||
|
// https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/summoner-spells.json
|
||||||
|
|
||||||
|
public async execute() {
|
||||||
|
const url = `https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/${this.endpoint}`
|
||||||
|
|
||||||
|
const requestCached = await Redis.get(url)
|
||||||
|
if (requestCached) {
|
||||||
|
return JSON.parse(requestCached)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await got(url)
|
||||||
|
|
||||||
|
await Redis.set(url, response.body, 'EX', this.cacheTime)
|
||||||
|
return JSON.parse(response.body)
|
||||||
|
} catch (error) {
|
||||||
|
this.retries--
|
||||||
|
|
||||||
|
Logger.error('CDragon Error : ', error)
|
||||||
|
|
||||||
|
if (this.retries > 0) {
|
||||||
|
await this.sleep(this.config.requestOptions.delayBeforeRetry)
|
||||||
|
return this.execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
104
server-v2/app/Services/Jax/src/Endpoints/CDragonEndpoint.ts
Normal file
104
server-v2/app/Services/Jax/src/Endpoints/CDragonEndpoint.ts
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
import { JaxConfig } from '../../JaxConfig'
|
||||||
|
import CDragonRequest from '../CDragonRequest'
|
||||||
|
|
||||||
|
export interface ChampionDTO {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
alias: string
|
||||||
|
squarePortraitPath: string
|
||||||
|
roles: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ItemDTO {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
active: boolean
|
||||||
|
inStore: boolean
|
||||||
|
from: number[]
|
||||||
|
to: number[]
|
||||||
|
categories: string[]
|
||||||
|
mapStringIdInclusions: string[]
|
||||||
|
maxStacks: number
|
||||||
|
modeNameInclusions: string[]
|
||||||
|
requiredChampion: string
|
||||||
|
requiredAlly: string
|
||||||
|
requiredBuffCurrencyName: string
|
||||||
|
requiredBuffCurrencyCost: number
|
||||||
|
specialRecipe: number
|
||||||
|
isEnchantment: boolean
|
||||||
|
price: number
|
||||||
|
priceTotal: number
|
||||||
|
iconPath: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PerkDTO {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
majorChangePatchVersion: string
|
||||||
|
tooltip: string
|
||||||
|
shortDesc: string
|
||||||
|
longDesc: string
|
||||||
|
iconPath: string
|
||||||
|
endOfGameStatDescs: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PerkStyleResponse {
|
||||||
|
schemaVersion: string
|
||||||
|
styles: PerkStyleDTO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PerkStyleDTO {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
tooltip: string
|
||||||
|
iconPath: string
|
||||||
|
assetMap: { [key: string]: string }
|
||||||
|
isAdvanced: boolean
|
||||||
|
allowedSubStyles: number[]
|
||||||
|
subStyleBonus: { styleId: number; perkId: number }[]
|
||||||
|
slots: { type: string; slotLabel: string; perks: number[] }[]
|
||||||
|
defaultPageName: string
|
||||||
|
defaultSubStyle: number
|
||||||
|
defaultPerks: number[]
|
||||||
|
defaultPerksWhenSplashed: number[]
|
||||||
|
defaultStatModsPerSubStyle: { id: string; perks: number[] }[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SummonerSpellDTO {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
summonerLevel: number
|
||||||
|
cooldown: number
|
||||||
|
gameModes: string[]
|
||||||
|
iconPath: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class CDragonEndpoint {
|
||||||
|
private config: JaxConfig
|
||||||
|
|
||||||
|
constructor(config: JaxConfig) {
|
||||||
|
this.config = config
|
||||||
|
}
|
||||||
|
|
||||||
|
public async champions(): Promise<ChampionDTO[]> {
|
||||||
|
return new CDragonRequest(this.config, 'champion-summary.json', 36000).execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async items(): Promise<ItemDTO[]> {
|
||||||
|
return new CDragonRequest(this.config, 'items.json', 36000).execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async perks(): Promise<PerkDTO[]> {
|
||||||
|
return new CDragonRequest(this.config, 'perks.json', 36000).execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async perkstyles(): Promise<PerkStyleResponse> {
|
||||||
|
return new CDragonRequest(this.config, 'perkstyles.json', 36000).execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
public async summonerSpells(): Promise<SummonerSpellDTO[]> {
|
||||||
|
return new CDragonRequest(this.config, 'summoner-spells.json', 36000).execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
48
server-v2/app/Services/Jax/src/Endpoints/LeagueEndpoint.ts
Normal file
48
server-v2/app/Services/Jax/src/Endpoints/LeagueEndpoint.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
// import { RiotRateLimiter } from '@fightmegg/riot-rate-limiter'
|
||||||
|
import RiotRateLimiter from 'riot-ratelimiter'
|
||||||
|
import { JaxConfig } from '../../JaxConfig'
|
||||||
|
import JaxRequest from '../JaxRequest'
|
||||||
|
|
||||||
|
export interface LeagueEntryDTO {
|
||||||
|
leagueId: string
|
||||||
|
queueType: string
|
||||||
|
tier: string
|
||||||
|
rank: string
|
||||||
|
summonerId: string
|
||||||
|
summonerName: string
|
||||||
|
leaguePoints: number
|
||||||
|
wins: number
|
||||||
|
losses: number
|
||||||
|
veteran: boolean
|
||||||
|
inactive: boolean
|
||||||
|
freshBlood: boolean
|
||||||
|
hotStreak: boolean
|
||||||
|
miniSeries?: MiniSeriesDTO
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MiniSeriesDTO {
|
||||||
|
losses: number
|
||||||
|
progress: string
|
||||||
|
target: number
|
||||||
|
wins: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class LeagueEndpoint {
|
||||||
|
private config: JaxConfig
|
||||||
|
private limiter: RiotRateLimiter
|
||||||
|
|
||||||
|
constructor(config: JaxConfig, limiter: RiotRateLimiter) {
|
||||||
|
this.config = config
|
||||||
|
this.limiter = limiter
|
||||||
|
}
|
||||||
|
|
||||||
|
public summonerID(summonerID: string, region: string): Promise<LeagueEntryDTO[]> {
|
||||||
|
return new JaxRequest(
|
||||||
|
region,
|
||||||
|
this.config,
|
||||||
|
`league/v4/entries/by-summoner/${summonerID}`,
|
||||||
|
this.limiter,
|
||||||
|
300
|
||||||
|
).execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
218
server-v2/app/Services/Jax/src/Endpoints/MatchEndpoint.ts
Normal file
218
server-v2/app/Services/Jax/src/Endpoints/MatchEndpoint.ts
Normal file
|
|
@ -0,0 +1,218 @@
|
||||||
|
// import { RiotRateLimiter } from '@fightmegg/riot-rate-limiter'
|
||||||
|
import { getRiotRegion } from 'App/helpers'
|
||||||
|
import RiotRateLimiter from 'riot-ratelimiter'
|
||||||
|
import { JaxConfig } from '../../JaxConfig'
|
||||||
|
import JaxRequest from '../JaxRequest'
|
||||||
|
|
||||||
|
export interface MatchDto {
|
||||||
|
metadata: MetadataDto
|
||||||
|
info: InfoDto
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MetadataDto {
|
||||||
|
dataVersion: string
|
||||||
|
matchId: string
|
||||||
|
participants: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InfoDto {
|
||||||
|
gameCreation: number
|
||||||
|
gameDuration: number
|
||||||
|
gameId: number
|
||||||
|
gameMode: string
|
||||||
|
gameName: string
|
||||||
|
gameStartTimestamp: number
|
||||||
|
gameType: string
|
||||||
|
gameVersion: string
|
||||||
|
mapId: number
|
||||||
|
participants: ParticipantDto[]
|
||||||
|
platformId: string
|
||||||
|
queueId: number
|
||||||
|
teams: TeamDto[]
|
||||||
|
tournamentCode?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ParticipantDto {
|
||||||
|
assists: number
|
||||||
|
baronKills: number
|
||||||
|
bountyLevel: number
|
||||||
|
champExperience: number
|
||||||
|
champLevel: number
|
||||||
|
championId: number
|
||||||
|
championName: string
|
||||||
|
championTransform: ChampionTransformDto
|
||||||
|
consumablesPurchased: number
|
||||||
|
damageDealtToObjectives: number
|
||||||
|
damageDealtToTurrets: number
|
||||||
|
damageSelfMitigated: number
|
||||||
|
deaths: number
|
||||||
|
detectorWardsPlaced: number
|
||||||
|
doubleKills: number
|
||||||
|
dragonKills: number
|
||||||
|
firstBloodAssist: boolean
|
||||||
|
firstBloodKill: boolean
|
||||||
|
firstTowerAssist: boolean
|
||||||
|
firstTowerKill: boolean
|
||||||
|
gameEndedInEarlySurrender: boolean
|
||||||
|
gameEndedInSurrender: boolean
|
||||||
|
goldEarned: number
|
||||||
|
goldSpent: number
|
||||||
|
individualPosition: 'Invalid' | TeamPositionDto // TODO
|
||||||
|
inhibitorKills: number
|
||||||
|
item0: number
|
||||||
|
item1: number
|
||||||
|
item2: number
|
||||||
|
item3: number
|
||||||
|
item4: number
|
||||||
|
item5: number
|
||||||
|
item6: number
|
||||||
|
itemsPurchased: number
|
||||||
|
killingSprees: number
|
||||||
|
kills: number
|
||||||
|
lane: LaneDto // TODO
|
||||||
|
largestCriticalStrike: number
|
||||||
|
largestKillingSpree: number
|
||||||
|
largestMultiKill: number
|
||||||
|
longestTimeSpentLiving: number
|
||||||
|
magicDamageDealt: number
|
||||||
|
magicDamageDealtToChampions: number
|
||||||
|
magicDamageTaken: number
|
||||||
|
neutralMinionsKilled: number
|
||||||
|
nexusKills: number
|
||||||
|
objectivesStolen: number
|
||||||
|
objectivesStolenAssists: number
|
||||||
|
participantId: number
|
||||||
|
pentaKills: number
|
||||||
|
perks: PerksDto
|
||||||
|
physicalDamageDealt: number
|
||||||
|
physicalDamageDealtToChampions: number
|
||||||
|
physicalDamageTaken: number
|
||||||
|
profileIcon: number
|
||||||
|
puuid: string
|
||||||
|
quadraKills: number
|
||||||
|
riotIdName: string
|
||||||
|
riotIdTagline: string
|
||||||
|
role: RoleDto // TODO
|
||||||
|
sightWardsBoughtInGame: number
|
||||||
|
spell1Casts: number
|
||||||
|
spell2Casts: number
|
||||||
|
spell3Casts: number
|
||||||
|
spell4Casts: number
|
||||||
|
summoner1Casts: number
|
||||||
|
summoner1Id: number
|
||||||
|
summoner2Casts: number
|
||||||
|
summoner2Id: number
|
||||||
|
summonerId: string
|
||||||
|
summonerLevel: number
|
||||||
|
summonerName: string
|
||||||
|
teamEarlySurrendered: boolean
|
||||||
|
teamId: number
|
||||||
|
teamPosition: TeamPositionDto // TODO
|
||||||
|
timeCCingOthers: number
|
||||||
|
timePlayed: number
|
||||||
|
totalDamageDealt: number
|
||||||
|
totalDamageDealtToChampions: number
|
||||||
|
totalDamageShieldedOnTeammates: number
|
||||||
|
totalDamageTaken: number
|
||||||
|
totalHeal: number
|
||||||
|
totalHealsOnTeammates: number
|
||||||
|
totalMinionsKilled: number
|
||||||
|
totalTimeCCDealt: number
|
||||||
|
totalTimeSpentDead: number
|
||||||
|
totalUnitsHealed: number
|
||||||
|
tripleKills: number
|
||||||
|
trueDamageDealt: number
|
||||||
|
trueDamageDealtToChampions: number
|
||||||
|
trueDamageTaken: number
|
||||||
|
turretKills: number
|
||||||
|
unrealKills: number
|
||||||
|
visionScore: number
|
||||||
|
visionWardsBoughtInGame: number
|
||||||
|
wardsKilled: number
|
||||||
|
wardsPlaced: number
|
||||||
|
win: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ChampionTransformDto {
|
||||||
|
None,
|
||||||
|
Slayer,
|
||||||
|
Assasin,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LaneDto = 'TOP' | 'JUNGLE' | 'MIDDLE' | 'BOTTOM'
|
||||||
|
|
||||||
|
export interface PerksDto {
|
||||||
|
statPerks: PerkStatsDto
|
||||||
|
styles: PerkStyleDto[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PerkStatsDto {
|
||||||
|
defense: number
|
||||||
|
flex: number
|
||||||
|
offense: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PerkStyleDto {
|
||||||
|
description: 'primaryStyle' | 'subStyle'
|
||||||
|
selections: PerkStyleSelectionDto[]
|
||||||
|
style: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PerkStyleSelectionDto {
|
||||||
|
perk: number
|
||||||
|
var1: number
|
||||||
|
var2: number
|
||||||
|
var3: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RoleDto = 'NONE' | 'DUO' | 'SOLO' | 'CARRY' | 'SUPPORT'
|
||||||
|
|
||||||
|
export type TeamPositionDto = 'TOP' | 'JUNGLE' | 'MIDDLE' | 'BOTTOM' | 'UTILITY'
|
||||||
|
|
||||||
|
export interface TeamDto {
|
||||||
|
bans: BanDto[]
|
||||||
|
objectives: ObjectivesDto
|
||||||
|
teamId: number
|
||||||
|
win: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BanDto {
|
||||||
|
championId: number
|
||||||
|
pickTurn: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ObjectivesDto {
|
||||||
|
baron: ObjectiveDto
|
||||||
|
champion: ObjectiveDto
|
||||||
|
dragon: ObjectiveDto
|
||||||
|
inhibitor: ObjectiveDto
|
||||||
|
riftHerald: ObjectiveDto
|
||||||
|
tower: ObjectiveDto
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ObjectiveDto {
|
||||||
|
first: boolean
|
||||||
|
kills: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class MatchEndpoint {
|
||||||
|
private config: JaxConfig
|
||||||
|
private limiter: RiotRateLimiter
|
||||||
|
|
||||||
|
constructor(config: JaxConfig, limiter: RiotRateLimiter) {
|
||||||
|
this.config = config
|
||||||
|
this.limiter = limiter
|
||||||
|
|
||||||
|
this.get = this.get.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(matchID: string, region: string): Promise<MatchDto> {
|
||||||
|
return new JaxRequest(
|
||||||
|
getRiotRegion(region),
|
||||||
|
this.config,
|
||||||
|
`match/v5/matches/${matchID}`,
|
||||||
|
this.limiter,
|
||||||
|
1500
|
||||||
|
).execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
// import { RiotRateLimiter } from '@fightmegg/riot-rate-limiter'
|
||||||
|
import { getRiotRegion } from 'App/helpers'
|
||||||
|
import RiotRateLimiter from 'riot-ratelimiter'
|
||||||
|
import { JaxConfig } from '../../JaxConfig'
|
||||||
|
import JaxRequest from '../JaxRequest'
|
||||||
|
|
||||||
|
// export interface MatchlistDto {
|
||||||
|
// startIndex: number,
|
||||||
|
// totalGames: number,
|
||||||
|
// endIndex: number,
|
||||||
|
// matches: MatchReferenceDto[]
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export interface MatchReferenceDto {
|
||||||
|
// gameId: number,
|
||||||
|
// role: string,
|
||||||
|
// season: number,
|
||||||
|
// platformId: string,
|
||||||
|
// champion: number,
|
||||||
|
// queue: number,
|
||||||
|
// lane: string,
|
||||||
|
// timestamp: number,
|
||||||
|
// seasonMatch?: number
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* ===============================================
|
||||||
|
* V5
|
||||||
|
* ===============================================
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type MatchlistDto = string[]
|
||||||
|
|
||||||
|
export default class MatchlistEndpoint {
|
||||||
|
private config: JaxConfig
|
||||||
|
private limiter: RiotRateLimiter
|
||||||
|
|
||||||
|
constructor(config: JaxConfig, limiter: RiotRateLimiter) {
|
||||||
|
this.config = config
|
||||||
|
this.limiter = limiter
|
||||||
|
}
|
||||||
|
|
||||||
|
public puuid(puuid: string, region: string, beginIndex = 0, count = 100): Promise<MatchlistDto> {
|
||||||
|
return new JaxRequest(
|
||||||
|
getRiotRegion(region),
|
||||||
|
this.config,
|
||||||
|
`match/v5/matches/by-puuid/${puuid}/ids?start=${beginIndex}&count=${count}`,
|
||||||
|
this.limiter,
|
||||||
|
0
|
||||||
|
).execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
// import { RiotRateLimiter } from '@fightmegg/riot-rate-limiter'
|
||||||
|
import RiotRateLimiter from 'riot-ratelimiter'
|
||||||
|
import { LeagueEntriesByQueue } from 'App/Services/SummonerService'
|
||||||
|
import { JaxConfig } from '../../JaxConfig'
|
||||||
|
import JaxRequest from '../JaxRequest'
|
||||||
|
|
||||||
|
export interface CurrentGameInfo {
|
||||||
|
gameId: number
|
||||||
|
gameType: string
|
||||||
|
gameStartTime: number
|
||||||
|
mapId: number
|
||||||
|
gameLength: number
|
||||||
|
platformId: string
|
||||||
|
gameMode: string
|
||||||
|
bannedChampions: BannedChampion[]
|
||||||
|
gameQueueConfigId: number
|
||||||
|
observers: Observer
|
||||||
|
participants: CurrentGameParticipant[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BannedChampion {
|
||||||
|
pickTurn: number
|
||||||
|
championId: number
|
||||||
|
teamId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Observer {
|
||||||
|
encryptionKey: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CurrentGameParticipant {
|
||||||
|
championId: number
|
||||||
|
perks: Perks
|
||||||
|
profileIconId: number
|
||||||
|
bot: boolean
|
||||||
|
teamId: number
|
||||||
|
summonerName: string
|
||||||
|
summonerId: string
|
||||||
|
spell1Id: number
|
||||||
|
spell2Id: number
|
||||||
|
gameCustomizationObjects: GameCustomizationObject[]
|
||||||
|
// Custom types from here
|
||||||
|
role?: string
|
||||||
|
runes?: { primaryRune: string; secondaryRune: string } | {}
|
||||||
|
level?: number
|
||||||
|
rank?: LeagueEntriesByQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Perks {
|
||||||
|
perkIds: number[]
|
||||||
|
perkStyle: number
|
||||||
|
perkSubStyle: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GameCustomizationObject {
|
||||||
|
category: string
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SpectatorEndpoint {
|
||||||
|
private config: JaxConfig
|
||||||
|
private limiter: RiotRateLimiter
|
||||||
|
|
||||||
|
constructor(config: JaxConfig, limiter: RiotRateLimiter) {
|
||||||
|
this.config = config
|
||||||
|
this.limiter = limiter
|
||||||
|
}
|
||||||
|
|
||||||
|
public summonerID(summonerID: string, region: string) {
|
||||||
|
return new JaxRequest(
|
||||||
|
region,
|
||||||
|
this.config,
|
||||||
|
`spectator/v4/active-games/by-summoner/${summonerID}`,
|
||||||
|
this.limiter,
|
||||||
|
0
|
||||||
|
).execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
45
server-v2/app/Services/Jax/src/Endpoints/SummonerEndpoint.ts
Normal file
45
server-v2/app/Services/Jax/src/Endpoints/SummonerEndpoint.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
// import { RiotRateLimiter } from '@fightmegg/riot-rate-limiter'
|
||||||
|
import RiotRateLimiter from 'riot-ratelimiter'
|
||||||
|
import { JaxConfig } from '../../JaxConfig'
|
||||||
|
import JaxRequest from '../JaxRequest'
|
||||||
|
|
||||||
|
export interface SummonerDTO {
|
||||||
|
accountId: string
|
||||||
|
profileIconId: number
|
||||||
|
revisionDate: number
|
||||||
|
name: string
|
||||||
|
id: string
|
||||||
|
puuid: string
|
||||||
|
summonerLevel: number
|
||||||
|
region?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SummonerEndpoint {
|
||||||
|
private config: JaxConfig
|
||||||
|
private limiter: RiotRateLimiter
|
||||||
|
|
||||||
|
constructor(config: JaxConfig, limiter: RiotRateLimiter) {
|
||||||
|
this.config = config
|
||||||
|
this.limiter = limiter
|
||||||
|
}
|
||||||
|
|
||||||
|
public summonerId(summonerId: string, region: string): Promise<SummonerDTO> {
|
||||||
|
return new JaxRequest(
|
||||||
|
region,
|
||||||
|
this.config,
|
||||||
|
`summoner/v4/summoners/${summonerId}`,
|
||||||
|
this.limiter,
|
||||||
|
36000
|
||||||
|
).execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
public summonerName(summonerName: string, region: string): Promise<SummonerDTO> {
|
||||||
|
return new JaxRequest(
|
||||||
|
region,
|
||||||
|
this.config,
|
||||||
|
`summoner/v4/summoners/by-name/${encodeURI(summonerName)}`,
|
||||||
|
this.limiter,
|
||||||
|
36000
|
||||||
|
).execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
42
server-v2/app/Services/Jax/src/Jax.ts
Normal file
42
server-v2/app/Services/Jax/src/Jax.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import LeagueEndpoint from './Endpoints/LeagueEndpoint'
|
||||||
|
import MatchEndpoint from './Endpoints/MatchEndpoint'
|
||||||
|
import MatchlistEndpoint from './Endpoints/MatchlistEndpoint'
|
||||||
|
import SummonerEndpoint from './Endpoints/SummonerEndpoint'
|
||||||
|
import SpectatorEndpoint from './Endpoints/SpectatorEndpoint'
|
||||||
|
import CDragonEndpoint from './Endpoints/CDragonEndpoint'
|
||||||
|
import { JaxConfig } from '../JaxConfig'
|
||||||
|
// import { RiotRateLimiter } from '@fightmegg/riot-rate-limiter'
|
||||||
|
import RiotRateLimiter from 'riot-ratelimiter'
|
||||||
|
import { STRATEGY } from 'riot-ratelimiter/dist/RateLimiter'
|
||||||
|
|
||||||
|
export default class Jax {
|
||||||
|
public key: string
|
||||||
|
public limiter: RiotRateLimiter
|
||||||
|
public config: JaxConfig
|
||||||
|
public League: LeagueEndpoint
|
||||||
|
public Match: MatchEndpoint
|
||||||
|
public Matchlist: MatchlistEndpoint
|
||||||
|
public Spectator: SpectatorEndpoint
|
||||||
|
public Summoner: SummonerEndpoint
|
||||||
|
public CDragon: CDragonEndpoint
|
||||||
|
|
||||||
|
constructor(config: JaxConfig) {
|
||||||
|
this.key = config.key
|
||||||
|
// this.limiter = new RiotRateLimiter({
|
||||||
|
// debug: true,
|
||||||
|
// retryCount: 0,
|
||||||
|
// })
|
||||||
|
this.limiter = new RiotRateLimiter({
|
||||||
|
debug: false,
|
||||||
|
strategy: STRATEGY.SPREAD,
|
||||||
|
})
|
||||||
|
this.config = config
|
||||||
|
|
||||||
|
this.League = new LeagueEndpoint(this.config, this.limiter)
|
||||||
|
this.Match = new MatchEndpoint(this.config, this.limiter)
|
||||||
|
this.Matchlist = new MatchlistEndpoint(this.config, this.limiter)
|
||||||
|
this.Spectator = new SpectatorEndpoint(this.config, this.limiter)
|
||||||
|
this.Summoner = new SummonerEndpoint(this.config, this.limiter)
|
||||||
|
this.CDragon = new CDragonEndpoint(this.config)
|
||||||
|
}
|
||||||
|
}
|
||||||
84
server-v2/app/Services/Jax/src/JaxRequest.ts
Normal file
84
server-v2/app/Services/Jax/src/JaxRequest.ts
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { promisify } from 'util'
|
||||||
|
import { JaxConfig } from '../JaxConfig'
|
||||||
|
import Logger from '@ioc:Adonis/Core/Logger'
|
||||||
|
import Redis from '@ioc:Adonis/Addons/Redis'
|
||||||
|
import RiotRateLimiter from 'riot-ratelimiter'
|
||||||
|
// import { RiotRateLimiter } from '@fightmegg/riot-rate-limiter'
|
||||||
|
|
||||||
|
export default class JaxRequest {
|
||||||
|
private region: string
|
||||||
|
private config: JaxConfig
|
||||||
|
private endpoint: string
|
||||||
|
private limiter: RiotRateLimiter
|
||||||
|
private cacheTime: number
|
||||||
|
private retries: number
|
||||||
|
private sleep: { (ms: number): Promise<void>; <T>(ms: number, value: T): Promise<T> }
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
region: string,
|
||||||
|
config: JaxConfig,
|
||||||
|
endpoint: string,
|
||||||
|
limiter: RiotRateLimiter,
|
||||||
|
cacheTime: number
|
||||||
|
) {
|
||||||
|
this.region = region
|
||||||
|
this.config = config
|
||||||
|
this.endpoint = endpoint
|
||||||
|
this.limiter = limiter
|
||||||
|
this.cacheTime = cacheTime
|
||||||
|
this.retries = config.requestOptions.retriesBeforeAbort
|
||||||
|
|
||||||
|
this.sleep = promisify(setTimeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
public async execute() {
|
||||||
|
const url = `https://${this.region}.api.riotgames.com/lol/${this.endpoint}`
|
||||||
|
|
||||||
|
// Redis cache
|
||||||
|
if (this.cacheTime > 0) {
|
||||||
|
const requestCached = await Redis.get(url)
|
||||||
|
if (requestCached) {
|
||||||
|
return JSON.parse(requestCached)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resp: any = await this.limiter.executing({
|
||||||
|
url,
|
||||||
|
token: this.config.key,
|
||||||
|
resolveWithFullResponse: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (this.cacheTime > 0) {
|
||||||
|
await Redis.setex(url, this.cacheTime, resp)
|
||||||
|
}
|
||||||
|
return JSON.parse(resp)
|
||||||
|
} catch ({ statusCode, ...rest }) {
|
||||||
|
this.retries--
|
||||||
|
|
||||||
|
if (statusCode !== 500 && statusCode !== 503 && statusCode !== 504) {
|
||||||
|
//
|
||||||
|
// Don't log 404 when summoner isn't playing or the summoner doesn't exist
|
||||||
|
// Or if summoner has no MatchList
|
||||||
|
if (
|
||||||
|
!this.endpoint.includes('spectator/v4/active-games/by-summoner') &&
|
||||||
|
!this.endpoint.includes('summoner/v4/summoners/by-name') &&
|
||||||
|
!this.endpoint.includes('match/v4/matchlists/by-account')
|
||||||
|
) {
|
||||||
|
Logger.error(`JaxRequest Error ${statusCode}: `, rest)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('====================================')
|
||||||
|
console.log(statusCode)
|
||||||
|
console.log('====================================')
|
||||||
|
|
||||||
|
if (this.retries > 0) {
|
||||||
|
await this.sleep(this.config.requestOptions.delayBeforeRetry)
|
||||||
|
return this.execute()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
91
server-v2/app/Services/SummonerService.ts
Normal file
91
server-v2/app/Services/SummonerService.ts
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
import Jax from './Jax'
|
||||||
|
import { SummonerDTO } from 'App/Services/Jax/src/Endpoints/SummonerEndpoint'
|
||||||
|
import { LeagueEntryDTO } from './Jax/src/Endpoints/LeagueEndpoint'
|
||||||
|
import Summoner from 'App/Models/Summoner'
|
||||||
|
|
||||||
|
export interface LeagueEntriesByQueue {
|
||||||
|
soloQ?: LeagueEntryByQueue
|
||||||
|
flex5v5?: LeagueEntryByQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LeagueEntryByQueue extends LeagueEntryDTO {
|
||||||
|
fullRank: string
|
||||||
|
winrate: string
|
||||||
|
shortName: string | number
|
||||||
|
}
|
||||||
|
|
||||||
|
class SummonerService {
|
||||||
|
private uniqueLeagues = ['CHALLENGER', 'GRANDMASTER', 'MASTER']
|
||||||
|
private leaguesNumbers = { I: 1, II: 2, III: 3, IV: 4 }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to transform League Data from the Riot API
|
||||||
|
* @param league raw data of the league from Riot API
|
||||||
|
*/
|
||||||
|
private getleagueData(league?: LeagueEntryDTO): LeagueEntryByQueue | null {
|
||||||
|
if (!league) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const fullRank = this.uniqueLeagues.includes(league.tier)
|
||||||
|
? league.tier
|
||||||
|
: `${league.tier} ${league.rank}`
|
||||||
|
const winrate = +((league.wins * 100) / (league.wins + league.losses)).toFixed(1) + '%'
|
||||||
|
const shortName = this.uniqueLeagues.includes(league.tier)
|
||||||
|
? league.leaguePoints
|
||||||
|
: league.tier[0] + this.leaguesNumbers[league.rank]
|
||||||
|
|
||||||
|
return {
|
||||||
|
...league,
|
||||||
|
fullRank,
|
||||||
|
winrate,
|
||||||
|
shortName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get account infos for a searched summoner name
|
||||||
|
* @param summonerName
|
||||||
|
* @param region
|
||||||
|
*/
|
||||||
|
public async getAccount(summonerName: string, region: string) {
|
||||||
|
const name = summonerName.toLowerCase()
|
||||||
|
const account = await Jax.Summoner.summonerName(name, region)
|
||||||
|
return account
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the full list of old and actual summoner names
|
||||||
|
* @param account of the summoner
|
||||||
|
* @param summonerDB summoner in the database
|
||||||
|
*/
|
||||||
|
public async getAllSummonerNames(account: SummonerDTO, summonerDB: Summoner) {
|
||||||
|
if (!summonerDB.names.find((n) => n.name === account.name)) {
|
||||||
|
await summonerDB.related('names').create({
|
||||||
|
name: account.name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return summonerDB.names
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get ranked data for a specific Summoner
|
||||||
|
* @param account
|
||||||
|
* @param region
|
||||||
|
*/
|
||||||
|
public async getRanked(account: SummonerDTO, region: string): Promise<LeagueEntriesByQueue> {
|
||||||
|
const ranked = await Jax.League.summonerID(account.id, region)
|
||||||
|
const result: LeagueEntriesByQueue = {}
|
||||||
|
|
||||||
|
if (ranked && ranked.length) {
|
||||||
|
result.soloQ =
|
||||||
|
this.getleagueData(ranked.find((e) => e.queueType === 'RANKED_SOLO_5x5')) || undefined
|
||||||
|
result.flex5v5 =
|
||||||
|
this.getleagueData(ranked.find((e) => e.queueType === 'RANKED_FLEX_SR')) || undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new SummonerService()
|
||||||
110
server-v2/app/helpers.ts
Normal file
110
server-v2/app/helpers.ts
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
import MatchPlayer from './Models/MatchPlayer'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All League of Legends regions used in Riot API
|
||||||
|
*/
|
||||||
|
export enum LeagueRegion {
|
||||||
|
BRAZIL = 'br1',
|
||||||
|
EUROPE_NORTHEAST = 'eun1',
|
||||||
|
EUROPE_WEST = 'euw1',
|
||||||
|
KOREA = 'kr',
|
||||||
|
LATIN_AMERICA_NORTH = 'la1',
|
||||||
|
LATIN_AMERICA_SOUTH = 'la2',
|
||||||
|
NORTH_AMERICA = 'na1',
|
||||||
|
OCEANIA = 'oc1',
|
||||||
|
RUSSIA = 'ru',
|
||||||
|
TURKEY = 'tr1',
|
||||||
|
JAPAN = 'jp1',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New regions used in Riot API >= v5
|
||||||
|
*/
|
||||||
|
export enum RiotRegion {
|
||||||
|
AMERICAS = 'americas',
|
||||||
|
ASIA = 'asia',
|
||||||
|
EUROPE = 'europe',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map old Riot API regions to new ones
|
||||||
|
* @param region : old region
|
||||||
|
* @returns new region name
|
||||||
|
*/
|
||||||
|
export function getRiotRegion(region: string): RiotRegion {
|
||||||
|
switch (
|
||||||
|
region as LeagueRegion // TODO: remove cast when region is typed to "Region" everywhere instead of string
|
||||||
|
) {
|
||||||
|
case LeagueRegion.NORTH_AMERICA:
|
||||||
|
case LeagueRegion.BRAZIL:
|
||||||
|
case LeagueRegion.LATIN_AMERICA_NORTH:
|
||||||
|
case LeagueRegion.LATIN_AMERICA_SOUTH:
|
||||||
|
case LeagueRegion.OCEANIA:
|
||||||
|
return RiotRegion.AMERICAS
|
||||||
|
case LeagueRegion.KOREA:
|
||||||
|
case LeagueRegion.JAPAN:
|
||||||
|
return RiotRegion.ASIA
|
||||||
|
case LeagueRegion.EUROPE_NORTHEAST:
|
||||||
|
case LeagueRegion.EUROPE_WEST:
|
||||||
|
case LeagueRegion.TURKEY:
|
||||||
|
case LeagueRegion.RUSSIA:
|
||||||
|
return RiotRegion.EUROPE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* League of Legends queues with defined role for each summoner
|
||||||
|
*/
|
||||||
|
export const queuesWithRole = [
|
||||||
|
0, // Custom
|
||||||
|
400, // Draft
|
||||||
|
420, // Solo/Duo
|
||||||
|
430, // Blind,
|
||||||
|
440, // Flex
|
||||||
|
700, // Clash
|
||||||
|
]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* League of Legends seasons timestamps
|
||||||
|
*/
|
||||||
|
export const seasons = {
|
||||||
|
0: 9,
|
||||||
|
1578628800000: 10,
|
||||||
|
1604970061000: 10.5, // Preseason 11
|
||||||
|
1610078400000: 11,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* League of Legends all support item ids
|
||||||
|
*/
|
||||||
|
export const supportItems = [3850, 3851, 3853, 3854, 3855, 3857, 3858, 3859, 3860, 3862, 3863, 3864]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get season number for a match
|
||||||
|
* @param timestamp
|
||||||
|
*/
|
||||||
|
export function getSeasonNumber(timestamp: number): number {
|
||||||
|
const arrSeasons = Object.keys(seasons).map((k) => Number(k))
|
||||||
|
arrSeasons.push(timestamp)
|
||||||
|
arrSeasons.sort()
|
||||||
|
const indexSeason = arrSeasons.indexOf(timestamp) - 1
|
||||||
|
return seasons[arrSeasons[indexSeason]]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return current League of Legends season number
|
||||||
|
*/
|
||||||
|
export function getCurrentSeason(): number {
|
||||||
|
const lastTimestamp = Object.keys(seasons).pop()!
|
||||||
|
return seasons[lastTimestamp]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort array of Players by roles according to a specific order
|
||||||
|
* @param a first player
|
||||||
|
* @param b second player
|
||||||
|
*/
|
||||||
|
export function sortTeamByRole(a: MatchPlayer, b: MatchPlayer) {
|
||||||
|
const sortingArr = ['TOP', 'JUNGLE', 'MIDDLE', 'BOTTOM', 'SUPPORT']
|
||||||
|
return sortingArr.indexOf(a.teamPosition) - sortingArr.indexOf(b.teamPosition)
|
||||||
|
}
|
||||||
1601
server-v2/package-lock.json
generated
1601
server-v2/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -27,10 +27,13 @@
|
||||||
"@adonisjs/lucid": "^16.0.2",
|
"@adonisjs/lucid": "^16.0.2",
|
||||||
"@adonisjs/redis": "^7.0.9",
|
"@adonisjs/redis": "^7.0.9",
|
||||||
"@adonisjs/repl": "^3.1.6",
|
"@adonisjs/repl": "^3.1.6",
|
||||||
|
"got": "^11.8.2",
|
||||||
"luxon": "^2.0.2",
|
"luxon": "^2.0.2",
|
||||||
"pg": "^8.7.1",
|
"pg": "^8.7.1",
|
||||||
"proxy-addr": "^2.0.7",
|
"proxy-addr": "^2.0.7",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
|
"request": "^2.88.2",
|
||||||
|
"riot-ratelimiter": "^0.1.5",
|
||||||
"source-map-support": "^0.5.20"
|
"source-map-support": "^0.5.20"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue