diff --git a/.terserrc.js b/.terserrc.js index 3bc27c1..dd1b704 100644 --- a/.terserrc.js +++ b/.terserrc.js @@ -1,7 +1,7 @@ const preambles = { 'extended-player-profile': `// ==UserScript== // @name Extended player profile -// @version 1.0.2 +// @version 1.1.0 // @description Adds additional info and actions to a player's profile. // @author Dawid Wysokiński - Kichiyaki - contact@dwysokinski.me // @match https://*/game.php?*screen=info_player* diff --git a/src/extended-player-profile.user.ts b/src/extended-player-profile.user.ts index 24cfedd..79e5ff8 100644 --- a/src/extended-player-profile.user.ts +++ b/src/extended-player-profile.user.ts @@ -4,14 +4,16 @@ import { Player, PlayerSnapshot, TWHelpClient } from './lib/twhelp'; import { DialogTable } from './common/dialog-table'; import { InADayClient } from './lib/tw'; import { createTranslationFunc } from './utils'; +import { buildURL } from './lib/twstats'; const t = createTranslationFunc({ pl_PL: { 'Joined at': 'Dołączył o', 'Last activity at': 'Ostatnio aktywny o', - 'Best rank': 'Najlepszy ranking', + 'Best rank': 'Najwyższy ranking', 'Most points': 'Najwięcej punktów', 'Most villages': 'Najwięcej wiosek', + 'Show other servers': 'Pokaż inne serwery', 'Show in a day ranks': 'Pokaż dzienne rankingi', 'Show tribe changes': 'Pokaż zmiany plemion', 'Show history': 'Pokaż historię', @@ -51,6 +53,8 @@ const t = createTranslationFunc({ 'Resources gathered': 'Zebrane surowce', 'Villages conquered': 'Przejęte wioski', Changes: 'Zmiany', + Open: 'Otwarty', + Closed: 'Zamknięty', }, }); @@ -94,6 +98,13 @@ class TWHelpConnector { sort: ['date:desc'], }); } + + playerOtherServers(page: number, limit: number) { + return this.client.playerOtherServers(this.version, this.server, this.id, { + offset: (page - 1) * limit, + limit, + }); + } } class InADayConnector { @@ -108,6 +119,7 @@ class InADayConnector { } enum DialogId { + OTHER_SERVERS = 'kichiyaki_other_servers', IN_A_DAY_RANKS = 'kichiyaki_in_a_day_ranks', TRIBE_CHANGES = 'kichiyaki_tribe_changes', ENNOBLEMENTS = 'kichiyaki_ennoblements', @@ -276,6 +288,10 @@ class UI { } [ + { + name: t('Show other servers'), + handler: this.showOtherServers.bind(this), + }, { name: t('Show in a day ranks'), handler: this.showInADayRanks.bind(this), @@ -304,6 +320,57 @@ class UI { }); } + private async showOtherServers(e: Event) { + e.preventDefault(); + + await new DialogTable( + DialogId.OTHER_SERVERS, + [ + { + header: t('Server'), + accessor: (p) => + `${p.server.key} (${ + p.server.open ? t('Open') : t('Closed') + })`, + }, + { + header: t('Tribe'), + accessor: (p) => { + if (!p.tribe) { + return '-'; + } + const url = buildURL({ + server: p.server.key, + id: p.tribe.id, + entity: 'tribe', + }); + return `${p.tribe.tag}`; + }, + }, + { + header: t('Best rank'), + accessor: (p) => p.bestRank.toString(), + }, + { + header: t('Most points'), + accessor: (p) => p.mostPoints.toLocaleString(), + }, + { + header: t('Most villages'), + accessor: (p) => p.mostVillages.toLocaleString(), + }, + ], + 30, + (page: number, limit: number) => { + return this.twhelpConnector.playerOtherServers(page, limit); + } + ).render(); + } + private async showInADayRanks(e: Event) { e.preventDefault(); diff --git a/src/lib/tw.ts b/src/lib/tw.ts index c74ad2b..105f2f8 100644 --- a/src/lib/tw.ts +++ b/src/lib/tw.ts @@ -2,8 +2,6 @@ import axios, { AxiosInstance } from 'axios'; import random from 'lodash/random'; import { wait } from '../utils'; -const DEFAULT_TIMEOUT = 10000; - type InADayScore = { player: { id: number; @@ -128,9 +126,9 @@ export type InADayPlayer = { export class InADayClient { client: AxiosInstance; - constructor(timeout?: number) { + constructor(timeout: number = 10000) { this.client = axios.create({ - timeout: timeout ?? DEFAULT_TIMEOUT, + timeout, }); } diff --git a/src/lib/twhelp.ts b/src/lib/twhelp.ts index db41dd8..f436335 100644 --- a/src/lib/twhelp.ts +++ b/src/lib/twhelp.ts @@ -1,7 +1,8 @@ -import axios, { AxiosInstance } from 'axios'; - -const DEFAULT_TIMEOUT = 10000; -const X_TOTAL_COUNT_HEADER = 'x-total-count'; +import axios, { + AxiosInstance, + AxiosResponseHeaders, + RawAxiosResponseHeaders, +} from 'axios'; export type Version = { code: string; @@ -10,6 +11,18 @@ export type Version = { timezone: string; }; +export type ServerMeta = { + key: string; + open: boolean; +}; + +export type TribeMeta = { + id: number; + name: string; + tag: string; + profileUrl: string; +}; + export type Player = { id: number; points: number; @@ -31,13 +44,11 @@ export type Player = { mostVillagesAt: string; lastActivityAt: string; createdAt: string; + tribe: TribeMeta | null; }; -export type TribeMeta = { - id: number; - name: string; - tag: string; - profileUrl: string; +export type PlayerWithServer = Player & { + server: ServerMeta; }; export type PlayerMeta = { @@ -112,13 +123,18 @@ export type ListPlayerSnapshotsParams = { sort?: string[]; }; +export type ListPlayerOtherServersParams = { + offset?: number; + limit?: number; +}; + export class TWHelpClient { client: AxiosInstance; - constructor(url: string, timeout?: number) { + constructor(url: string, timeout: number = 10000) { this.client = axios.create({ baseURL: url, - timeout: timeout ?? DEFAULT_TIMEOUT, + timeout, }); } @@ -126,7 +142,7 @@ export class TWHelpClient { const resp = await this.client.get('/api/v1/versions'); return { data: resp.data.data, - total: parseInt(resp.headers[X_TOTAL_COUNT_HEADER] ?? '0'), + total: this.parseTotal(resp.headers), }; } @@ -168,7 +184,7 @@ export class TWHelpClient { ); return { data: resp.data.data, - total: parseInt(resp.headers[X_TOTAL_COUNT_HEADER] ?? '0'), + total: this.parseTotal(resp.headers), }; } @@ -199,7 +215,7 @@ export class TWHelpClient { ); return { data: resp.data.data, - total: parseInt(resp.headers[X_TOTAL_COUNT_HEADER] ?? '0'), + total: this.parseTotal(resp.headers), }; } @@ -230,7 +246,38 @@ export class TWHelpClient { ); return { data: resp.data.data, - total: parseInt(resp.headers[X_TOTAL_COUNT_HEADER] ?? '0'), + total: this.parseTotal(resp.headers), }; } + + public async playerOtherServers( + version: string, + server: string, + id: number, + queryParams?: ListPlayerOtherServersParams + ): Promise> { + const params = new URLSearchParams(); + + if (queryParams?.limit) { + params.set('limit', queryParams.limit.toString()); + } + + if (queryParams?.offset) { + params.set('offset', queryParams.offset.toString()); + } + + const resp = await this.client.get( + `/api/v1/versions/${version}/servers/${server}/players/${id}/other-servers?${params.toString()}` + ); + return { + data: resp.data.data, + total: this.parseTotal(resp.headers), + }; + } + + private parseTotal( + headers: RawAxiosResponseHeaders | AxiosResponseHeaders + ): number { + return parseInt(headers['x-total-count'] ?? '0'); + } } diff --git a/src/lib/twstats.ts b/src/lib/twstats.ts new file mode 100644 index 0000000..e25e2aa --- /dev/null +++ b/src/lib/twstats.ts @@ -0,0 +1,24 @@ +const BASE = 'https://www.twstats.com'; + +export type BuildURLParams = + | { + entity: 'player'; + id: number; + server: string; + } + | { + entity: 'tribe'; + id: number; + server: string; + }; + +export const buildURL = (params: BuildURLParams) => { + switch (params.entity) { + case 'player': + return `${BASE}/in/${params.server}/player/${params.id}`; + case 'tribe': + return `${BASE}/in/${params.server}/tribe/${params.id}`; + default: + throw new Error(`Incorrect params.entity`); + } +};