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`);
+ }
+};