Compare commits

..

30 Commits

Author SHA1 Message Date
Renovate 04f9873c67 chore(deps): update dependency eslint-config-prettier to v9.1.0
ci/woodpecker/pr/test Pipeline was successful Details
2024-04-05 19:44:37 +00:00
Dawid Wysokiński 99ef0c40de
chore: update README.md
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/tag/docker Pipeline was successful Details
ci/woodpecker/tag/deployment Pipeline was successful Details
2024-04-05 07:41:58 +02:00
Dawid Wysokiński cab4709463
chore(deps): update dependency date-fns to v3
ci/woodpecker/push/test Pipeline was successful Details
2024-04-05 06:38:20 +02:00
Renovate 82b864946b chore(deps): update dependency husky to v9 (#98)
ci/woodpecker/push/test Pipeline was successful Details
Reviewed-on: #98
Co-authored-by: Renovate <renovate@dwysokinski.me>
Co-committed-by: Renovate <renovate@dwysokinski.me>
2024-04-04 03:50:40 +00:00
Dawid Wysokiński b43139bcbb
chore: caddy - enable file listings
ci/woodpecker/push/test Pipeline was successful Details
2024-04-03 07:59:17 +02:00
Dawid Wysokiński 81274707d3
chore: update README.md
ci/woodpecker/push/test Pipeline was successful Details
2024-04-03 07:55:30 +02:00
Dawid Wysokiński f2ed75e4c8
chore: delete manifest.tmpl
ci/woodpecker/push/test Pipeline was successful Details
ci/woodpecker/tag/docker Pipeline was successful Details
ci/woodpecker/tag/deployment Pipeline was successful Details
2024-04-03 07:44:06 +02:00
Dawid Wysokiński dc5f4a03de
chore: add linux/arm64 to docker buildx config
ci/woodpecker/push/test Pipeline was successful Details
2024-04-03 07:41:07 +02:00
Dawid Wysokiński 8496747ac8
chore: update README.md
ci/woodpecker/push/test Pipeline was successful Details
2024-04-03 07:38:16 +02:00
Dawid Wysokiński a637e66258
chore: rename docs folder -> assets
ci/woodpecker/push/test Pipeline was successful Details
2024-04-03 07:32:24 +02:00
Dawid Wysokiński 0fbea77850
feat: specify timeout for api requests
ci/woodpecker/push/test Pipeline was successful Details
2024-04-03 07:29:27 +02:00
Dawid Wysokiński 0d034710b5
refactor: extended tribe profile - use /api/v2
ci/woodpecker/push/test Pipeline was successful Details
2024-04-03 07:18:14 +02:00
Dawid Wysokiński 998deef3aa
refactor: extended player profile - use /api/v2
ci/woodpecker/push/test Pipeline was successful Details
2024-04-03 06:46:29 +02:00
Renovate a740a589be chore(deps): update dependency @types/jquery to v3.5.29 (#82)
ci/woodpecker/push/test Pipeline was successful Details
Reviewed-on: #82
Co-authored-by: Renovate <renovate@dwysokinski.me>
Co-committed-by: Renovate <renovate@dwysokinski.me>
2024-04-03 03:52:57 +00:00
Renovate 8bffd2647b chore(deps): update commitlint monorepo to v19 (major) (#96)
ci/woodpecker/push/test Pipeline was successful Details
Reviewed-on: #96
Co-authored-by: Renovate <renovate@dwysokinski.me>
Co-committed-by: Renovate <renovate@dwysokinski.me>
2024-04-03 03:52:19 +00:00
Dawid Wysokiński af3fffae43
refactor: extended village profile - use /api/v2
ci/woodpecker/push/test Pipeline was successful Details
2024-04-02 08:24:02 +02:00
Dawid Wysokiński a2fb6425a0
refactor: extended map popup - use /api/v2
ci/woodpecker/push/test Pipeline was successful Details
2024-04-02 07:29:59 +02:00
Renovate 93fa246f14 chore(deps): update dependency axios to v1.6.8 (#94)
ci/woodpecker/push/test Pipeline was successful Details
Reviewed-on: #94
Co-authored-by: Renovate <renovate@dwysokinski.me>
Co-committed-by: Renovate <renovate@dwysokinski.me>
2024-04-02 03:42:08 +00:00
Dawid Wysokiński fc9d1be2fe
chore: update README.md [skip ci] 2024-04-01 09:52:38 +02:00
Dawid Wysokiński aaabe449d5
chore: new domain - twhelp.app
ci/woodpecker/push/test Pipeline was successful Details
2024-04-01 09:48:09 +02:00
Dawid Wysokiński ac571326c3
feat: /api/v2 - generate client
ci/woodpecker/push/test Pipeline was successful Details
2024-04-01 09:45:33 +02:00
Renovate cceeb270c4 chore(deps): update commitlint monorepo to v18.6.1 (#92)
ci/woodpecker/push/test Pipeline was successful Details
Reviewed-on: #92
Co-authored-by: Renovate <renovate@dwysokinski.me>
Co-committed-by: Renovate <renovate@dwysokinski.me>
2024-04-01 07:22:44 +00:00
Dawid Wysokiński f94fd27432
chore: update ci/cd pipelines
ci/woodpecker/push/test Pipeline was successful Details
2024-04-01 09:21:56 +02:00
Renovate 1ddcbb66ed chore(deps): update dependency prettier to v3.2.5 (#93)
ci/woodpecker/push/test Pipeline was successful Details
Reviewed-on: #93
Co-authored-by: Renovate <renovate@dwysokinski.me>
Co-committed-by: Renovate <renovate@dwysokinski.me>
2024-04-01 07:17:41 +00:00
Renovate d099601b69 chore(deps): update dependency typescript to v5.4.3 (#88)
ci/woodpecker/push/test Pipeline failed Details
Reviewed-on: #88
Co-authored-by: Renovate <renovate@dwysokinski.me>
Co-committed-by: Renovate <renovate@dwysokinski.me>
2024-04-01 07:17:31 +00:00
Renovate ff364a93ea chore(deps): update node.js to v20 (#79)
ci/woodpecker/push/test Pipeline was successful Details
Reviewed-on: #79
Co-authored-by: Renovate <renovate@dwysokinski.me>
Co-committed-by: Renovate <renovate@dwysokinski.me>
2024-04-01 07:14:37 +00:00
Renovate f38e3d6954 chore(deps): update dependency @types/node to v18.19.14 (#81)
ci/woodpecker/push/test Pipeline was successful Details
Reviewed-on: #81
Co-authored-by: Renovate <renovate@dwysokinski.me>
Co-committed-by: Renovate <renovate@dwysokinski.me>
2024-02-02 06:39:01 +00:00
Renovate 2bfc8b1c84 chore(deps): update dependency axios to v1.6.7 (#91)
ci/woodpecker/push/test Pipeline failed Details
Reviewed-on: #91
Co-authored-by: Renovate <renovate@dwysokinski.me>
Co-committed-by: Renovate <renovate@dwysokinski.me>
2024-02-02 06:38:48 +00:00
Renovate c2fd244639 chore(deps): update caddy docker tag to v2.7.6 (#90)
ci/woodpecker/push/test Pipeline was successful Details
Reviewed-on: #90
Co-authored-by: Renovate <renovate@dwysokinski.me>
Co-committed-by: Renovate <renovate@dwysokinski.me>
2024-01-13 08:48:23 +00:00
Renovate e69cf46abe chore(deps): update commitlint monorepo to v18.4.4 (#86)
ci/woodpecker/push/test Pipeline was successful Details
Reviewed-on: #86
Co-authored-by: Renovate <renovate@dwysokinski.me>
Co-committed-by: Renovate <renovate@dwysokinski.me>
2024-01-13 08:19:14 +00:00
33 changed files with 1315 additions and 1837 deletions

2
.env
View File

@ -1 +1 @@
TWHELP_API_BASE_URL=https://tribalwarshelp.com
TWHELP_API_BASE_URL=https://twhelp.app/api

View File

@ -5,3 +5,4 @@ k8s
.prettierrc.js
commitlint.config.js
postbuild.js
src/lib/twhelp

View File

@ -1,4 +1 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx --no -- commitlint --edit ${1}

View File

@ -1,3 +0,0 @@
module.exports = {
singleQuote: true,
};

4
.prettierrc.json Normal file
View File

@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "es5"
}

View File

@ -4,31 +4,31 @@ when:
steps:
deploy:
image: alpine/k8s:1.26.9
image: alpine/k8s:1.27.11
secrets:
- kubeconfig
commands:
- "mkdir ~/.kube && echo \"$KUBECONFIG\" > ~/.kube/twhelp"
- "cd ./k8s/overlays/prod && kustomize edit set image scripts=gitea.dwysokinski.me/twhelp-packages/scripts:${CI_COMMIT_TAG##v} && cd ../../.."
- kustomize build ./k8s/overlays/prod | kubectl --kubeconfig ~/.kube/twhelp apply -n twhelp -f -
notify:
email:
image: deblan/woodpecker-email
settings:
dsn:
from_secret: email_dsn
from:
from_secret: email_from
from.name: Woodpecker
host:
from_secret: email_host
username:
from_secret: email_username
password:
from_secret: email_password
address:
from_secret: email_from_address
name: Woodpecker
recipients:
- notifications@dwysokinski.me
- notifications@twhelp.app
recipients_only: true
subject: "[deploy - {{ build.status }}] {{ repo.owner }}/{{ repo.name }} ({{ build.branch }} - {{ truncate build.commit 8 }})"
content:
subject:
"[deployment - {{ pipeline.status }}] {{ repo.full_name }} ({{ commit.tag }})"
when:
status: [failure]
status: [success, failure]
depends_on:
- docker

View File

@ -6,7 +6,7 @@ steps:
publish:
image: woodpeckerci/plugin-docker-buildx
settings:
platforms: linux/amd64
platforms: linux/amd64,linux/arm64
repo: gitea.dwysokinski.me/twhelp-packages/scripts
registry: gitea.dwysokinski.me
auto_tag: true
@ -14,20 +14,3 @@ steps:
from_secret: docker_username
password:
from_secret: docker_password
notify:
image: deblan/woodpecker-email
settings:
from:
from_secret: email_from
from.name: Woodpecker
host:
from_secret: email_host
username:
from_secret: email_username
password:
from_secret: email_password
recipients:
- notifications@dwysokinski.me
recipients_only: true
when:
status: [failure]

View File

@ -5,7 +5,7 @@ when:
- ${CI_REPO_DEFAULT_BRANCH}
variables:
- &node_image 'node:18'
- &node_image 'node:20'
steps:
test-build:

View File

@ -1,6 +1,6 @@
:80 {
root * /var/www
file_server
file_server browse
header -Server
header Cache-Control max-age=3600
header *.js Content-type "text/javascript; charset=UTF-8"

View File

@ -1,4 +1,4 @@
FROM node:18.18-alpine as build-deps
FROM --platform=$BUILDPLATFORM node:20.12-alpine as build-deps
WORKDIR /app
COPY package.json yarn.lock ./
@ -8,7 +8,7 @@ COPY . ./
ENV NODE_ENV=production
RUN yarn build
FROM caddy:2.7.5-alpine
FROM caddy:2.7.6-alpine
COPY --from=build-deps /app/dist /var/www
COPY Caddyfile /etc/caddy/Caddyfile

View File

@ -1,6 +1,31 @@
# Tribal Wars - scripts
<div align="center">
<picture>
<img alt="TWHelp logo" src="assets/white_logo_full.png" width="50%">
</picture>
</div>
This repo contains a variety of scripts for [Tribal Wars](https://www.tribalwars.net/en-dk/).
<br>
<h1 align="center"><a href="https://www.tribalwars.net/en-dk/">Tribal Wars</a> scripts to enhance your gameplay</h1>
<div align="center">
[![License](https://img.shields.io/badge/License-MIT-green)](#license)
[![CI/CD](https://woodpecker.dwysokinski.me/api/badges/10/status.svg)](https://woodpecker.dwysokinski.me/repos/10)
</div>
<p align="center">
<a href="https://twhelp.app/api/v2/swagger/">API</a>
·
<a href="https://gitea.dwysokinski.me/twhelp/scripts">Scripts</a>
·
<a href="https://gitea.dwysokinski.me/twhelp/dcbot">Discord Bot</a>
·
<a href="mailto:contact@twhelp.app">Report Bug</a>
·
<a href="mailto:contact@twhelp.app">Request Feature</a>
</p>
## Scripts
@ -8,70 +33,71 @@ This repo contains a variety of scripts for [Tribal Wars](https://www.tribalwars
This script adds additional info and actions on a player overview.
![img.png](docs/extended-player-profile.png)
![img.png](assets/extended-player-profile.png)
#### Installation
[User script](https://scripts.tribalwarshelp.com/extended-player-profile.user.js)
[User script](https://scripts.twhelp.app/extended-player-profile.user.js)
Quick bar:
```javascript
javascript:
$.getScript('https://scripts.tribalwarshelp.com/extended-player-profile.quickbar.js')
$.getScript('https://scripts.twhelp.app/extended-player-profile.quickbar.js?_='+~~(Date.now()/9e6))
```
### Extended tribe profile
This script adds additional info and actions on a tribe overview.
![img.png](docs/extended-tribe-profile.png)
![img.png](assets/extended-tribe-profile.png)
#### Installation
[User script](https://scripts.tribalwarshelp.com/extended-tribe-profile.user.js)
[User script](https://scripts.twhelp.app/extended-tribe-profile.user.js)
Quick bar:
```javascript
javascript:
$.getScript('https://scripts.tribalwarshelp.com/extended-tribe-profile.quickbar.js')
$.getScript('https://scripts.twhelp.app/extended-tribe-profile.quickbar.js?_='+~~(Date.now()/9e6))
```
### Extended map popup
This script extends the map popup with additional info.
![img.png](docs/extended-map-popup.png)
![img.png](assets/extended-map-popup.png)
#### Installation
[User script](https://scripts.tribalwarshelp.com/extended-map-popup.user.js)
[User script](https://scripts.twhelp.app/extended-map-popup.user.js)
Quick bar:
```javascript
javascript:
$.getScript('https://scripts.tribalwarshelp.com/extended-map-popup.quickbar.js')
$.getScript('https://scripts.twhelp.app/extended-map-popup.quickbar.js?_='+~~(Date.now()/9e6))
```
### Extended village profile
This script adds additional info and actions on a village overview.
![img.png](docs/extended-village-profile.png)
![img.png](assets/extended-village-profile.png)
#### Installation
[User script](https://scripts.tribalwarshelp.com/extended-village-profile.user.js)
[User script](https://scripts.twhelp.app/extended-village-profile.user.js)
Quick bar:
```javascript
javascript:
$.getScript('https://scripts.tribalwarshelp.com/extended-village-profile.quickbar.js')
$.getScript('https://scripts.twhelp.app/extended-village-profile.quickbar.js?_='+~~(Date.now()/9e6))
```
## Contributing
If you would like to contribute to the software, please contact me via [email](mailto:contact@twhelp.app).
## License
Distributed under the MIT License. See ``LICENSE`` for more information.
This project is licensed under the MIT license. See the [LICENSE](LICENSE) file for the full license text.
## Contact
Dawid Wysokiński - [contact@dwysokinski.me](mailto:contact@dwysokinski.me)

View File

Before

Width:  |  Height:  |  Size: 204 KiB

After

Width:  |  Height:  |  Size: 204 KiB

View File

Before

Width:  |  Height:  |  Size: 196 KiB

After

Width:  |  Height:  |  Size: 196 KiB

View File

Before

Width:  |  Height:  |  Size: 452 KiB

After

Width:  |  Height:  |  Size: 452 KiB

View File

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

BIN
assets/white_logo_full.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

View File

@ -18,3 +18,13 @@ spec:
name: scripts-caddy-service
port:
number: 80
- host: scripts.twhelp.app
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: scripts-caddy-service
port:
number: 80

View File

@ -1,13 +0,0 @@
image: gitea.dwysokinski.me/twhelp-packages/scripts:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
{{#if build.tags}}
tags:
{{#each build.tags}}
- {{this}}
{{/each}}
{{/if}}
manifests:
-
image: gitea.dwysokinski.me/twhelp-packages/scripts:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
platform:
architecture: amd64
os: linux

View File

@ -3,9 +3,10 @@
"version": "0.1.0",
"license": "MIT",
"scripts": {
"prepare": "husky install",
"build": "rm -rf dist && parcel build ./src/*.user.ts ./src/*.quickbar.ts",
"postbuild": "PUBLIC_URL=https://scripts.tribalwarshelp.com node postbuild.js",
"prepare": "husky",
"generate-client": "openapi --input src/lib/twhelp/openapi3.json --output src/lib/twhelp/generated --name TWHelpClient --useOptions --client axios",
"build": "rm -rf dist && yarn generate-client && parcel build ./src/*.user.ts ./src/*.quickbar.ts",
"postbuild": "PUBLIC_URL=https://scripts.twhelp.app node postbuild.js",
"lint": "eslint src/**/*.ts"
},
"targets": {
@ -20,29 +21,30 @@
"url": "https://dwysokinski.me/"
},
"browserslist": [
"since 2017-06"
"since 2020-01"
],
"devDependencies": {
"@commitlint/cli": "^18.0.0",
"@commitlint/config-conventional": "^18.0.0",
"@parcel/validator-eslint": "^2.9.1",
"@parcel/validator-typescript": "^2.9.1",
"@commitlint/cli": "^19.0.0",
"@commitlint/config-conventional": "^19.0.0",
"@parcel/validator-eslint": "^2.12.0",
"@parcel/validator-typescript": "^2.12.0",
"@types/jquery": "^3.5.14",
"@types/lodash": "^4.14.191",
"@types/node": "^18.11.17",
"@types/node": "^20.0.0",
"@typescript-eslint/eslint-plugin": "^5.50.0",
"@typescript-eslint/parser": "^5.50.0",
"buffer": "^5.5.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "^9.0.0",
"husky": "^8.0.3",
"parcel": "^2.9.1",
"husky": "^9.0.0",
"openapi-typescript-codegen": "^0.28.0",
"parcel": "^2.12.0",
"prettier": "^3.0.0",
"typescript": "^5.0.0"
},
"dependencies": {
"axios": "^1.2.2",
"date-fns": "^2.29.3",
"date-fns": "^3.0.0",
"lodash": "^4.17.21"
}
}

View File

@ -7,9 +7,9 @@ const DIST_DIR = 'dist';
const metadata = {
'extended-player-profile': `// ==UserScript==
// @name Extended player profile
// @version 1.1.6
// @version 1.2.0
// @description Adds additional info and actions on a player overview.
// @author Dawid Wysokiński - Kichiyaki - contact@dwysokinski.me
// @author Dawid Wysokiński - Kichiyaki - contact@twhelp.app
// @match https://*/game.php?*screen=info_player*
// @downloadURL ${process.env.PUBLIC_URL}/extended-player-profile.user.js
// @updateURL ${process.env.PUBLIC_URL}/extended-player-profile.user.js
@ -19,9 +19,9 @@ const metadata = {
// ==/UserScript==`,
'extended-map-popup': `// ==UserScript==
// @name Extended map popup
// @version 1.0.5
// @version 1.1.0
// @description Extends the map popup with additional info.
// @author Dawid Wysokiński - Kichiyaki - contact@dwysokinski.me
// @author Dawid Wysokiński - Kichiyaki - contact@twhelp.app
// @match https://*/game.php?*screen=map*
// @downloadURL ${process.env.PUBLIC_URL}/extended-map-popup.user.js
// @updateURL ${process.env.PUBLIC_URL}/extended-map-popup.user.js
@ -31,9 +31,9 @@ const metadata = {
// ==/UserScript==`,
'extended-village-profile': `// ==UserScript==
// @name Extended village profile
// @version 1.0.4
// @version 1.1.0
// @description Adds additional info and actions on a village overview.
// @author Dawid Wysokiński - Kichiyaki - contact@dwysokinski.me
// @author Dawid Wysokiński - Kichiyaki - contact@twhelp.app
// @match https://*/game.php?*screen=info_village*
// @downloadURL ${process.env.PUBLIC_URL}/extended-village-profile.user.js
// @updateURL ${process.env.PUBLIC_URL}/extended-village-profile.user.js
@ -43,9 +43,9 @@ const metadata = {
// ==/UserScript==`,
'extended-tribe-profile': `// ==UserScript==
// @name Extended tribe profile
// @version 1.0.4
// @version 1.1.0
// @description Adds additional info and actions on a tribe overview.
// @author Dawid Wysokiński - Kichiyaki - contact@dwysokinski.me
// @author Dawid Wysokiński - Kichiyaki - contact@twhelp.app
// @match https://*/game.php?*screen=info_ally*
// @downloadURL ${process.env.PUBLIC_URL}/extended-tribe-profile.user.js
// @updateURL ${process.env.PUBLIC_URL}/extended-tribe-profile.user.js

View File

@ -15,48 +15,54 @@ export type DialogTableColumn<T> = {
accessor: (row: T, index: number, rows: T[]) => string;
};
export type Cursor = {
next?: string;
self?: string;
};
export type LoadDataResult<T> = {
cursor?: Cursor;
data: T[];
total: number;
};
export class DialogTable<T> {
prevPageId: string;
selectId: string;
nextPageId: string;
private readonly prevPageId: string;
private readonly nextPageId: string;
private readonly prevCursors: Cursor[] = [];
constructor(
private readonly id: string,
private readonly columns: DialogTableColumn<T>[],
private readonly limit: number,
private readonly loadData: (
page: number,
cursor: string | undefined,
limit: number
) => Promise<LoadDataResult<T>>
) {
this.prevPageId = `${this.id}_page_prev`;
this.selectId = `${this.id}_page_select`;
this.nextPageId = `${this.id}_page_next`;
}
public async render() {
await this.renderPage(1);
await this.renderPage();
}
private async renderPage(page: number) {
window.Dialog.show(`${this.id}_loading`, `<p>${t('Loading')}...</p>`);
private async renderPage(pageCursor?: string) {
window.Dialog.show(this.id, `<p>${t('Loading')}...</p>`);
try {
const { data, total } = await this.loadData(page, this.limit);
const { data, cursor } = await this.loadData(pageCursor, this.limit);
window.Dialog.show(
this.id,
`
${this.buildPagination(page, total)}
${this.buildPagination(cursor)}
${this.buildTable(data)}
`
);
this.addEventListeners(page);
this.addEventListeners(cursor);
} catch (err) {
console.error(err);
window.Dialog.close();
@ -64,31 +70,17 @@ export class DialogTable<T> {
}
}
private buildPagination(page: number, total: number): string {
const maxPage = Math.ceil(total / this.limit);
const pageOpts = [];
for (let i = 1; i <= (page > maxPage ? page : maxPage); i++) {
pageOpts.push(
`<option${
i === page ? ' disabled selected' : ''
} value="${i}">${i}</option>`
);
}
private buildPagination(cursor?: Cursor): string {
return `
<div style="display: flex; flex-direction: row; align-items: center; justify-content: center; margin-bottom: 10px">
<button title="${t(
'Previous page'
)}" style="margin-right: 5px" class="btn" id="${this.prevPageId}"${
page <= 1 ? ' disabled' : ''
}>&lt;</button>
<select style="margin-right: 5px" id="${this.selectId}">
${pageOpts.join('')}
</select>
this.prevCursors.length === 0 ? ' disabled' : ''
}>&lt;</button>
<button title="${t('Next page')}" class="btn" id="${
this.nextPageId
}"${page >= maxPage ? ' disabled' : ''}>&gt;</button>
this.nextPageId
}"${!cursor?.next ? ' disabled' : ''}>&gt;</button>
</div>
`;
}
@ -116,27 +108,25 @@ export class DialogTable<T> {
`;
}
private addEventListeners(page: number) {
private addEventListeners(cursor?: Cursor) {
document
.querySelector('#' + this.prevPageId)
?.addEventListener('click', () => {
this.renderPage(page - 1);
});
document
.querySelector('#' + this.selectId)
?.addEventListener('change', (e: Event) => {
if (!(e.currentTarget instanceof HTMLSelectElement)) {
const prev = this.prevCursors.pop();
if (!prev) {
return;
}
this.renderPage(parseInt(e.currentTarget.value));
this.renderPage(prev.self);
});
document
.querySelector('#' + this.nextPageId)
?.addEventListener('click', () => {
this.renderPage(page + 1);
if (!cursor) {
return;
}
this.prevCursors.push(cursor);
this.renderPage(cursor.next);
});
}
}

View File

@ -1,13 +1,14 @@
import addSeconds from 'date-fns/addSeconds';
import {
Ennoblement,
ServerConfig,
TWHelpClient,
UnitInfo,
} from './lib/twhelp';
import { addSeconds } from 'date-fns/addSeconds';
import { Cache, InMemoryStorage } from './lib/cache';
import { calcDistance, calcLoyalty } from './lib/tw';
import { createTranslationFunc } from './utils';
import {
TWHelpClient,
Ennoblement,
UnitInfo,
ServerConfig,
AxiosHttpRequestWithTimeout,
} from './lib/twhelp';
declare global {
interface Window {
@ -43,25 +44,41 @@ const t = createTranslationFunc({
class TWHelpConnector {
private static readonly SERVER_CONFIG_CACHE_KEY =
'extended_map_popup_server_config';
private static readonly UNIT_INFO_CACHE_KEY = 'extended_map_popup_unit_info';
'extended_map_popup_server_config_v2';
private static readonly UNIT_INFO_CACHE_KEY =
'extended_map_popup_unit_info_v2';
private static readonly VILLAGE_CACHE_KEY_PREFIX =
'extended_map_popup_village_';
'extended_map_popup_village_v2_';
private readonly localStorageCache = new Cache(localStorage);
private readonly inMemoryCache = new Cache(new InMemoryStorage());
private readonly client: TWHelpClient;
constructor(
private readonly client: TWHelpClient,
readonly baseUrl: string,
private readonly version: string,
private readonly server: string
) {}
) {
this.client = new TWHelpClient(
{
BASE: baseUrl,
},
AxiosHttpRequestWithTimeout
);
}
serverConfig() {
return this.localStorageCache.load(
TWHelpConnector.SERVER_CONFIG_CACHE_KEY,
3600,
() => {
return this.client.serverConfig(this.version, this.server);
async () => {
return (
await this.client.servers.getServerConfig({
serverKey: this.server,
versionCode: this.version,
})
).data;
}
);
}
@ -70,8 +87,13 @@ class TWHelpConnector {
return this.localStorageCache.load(
TWHelpConnector.UNIT_INFO_CACHE_KEY,
3600,
() => {
return this.client.unitInfo(this.version, this.server);
async () => {
return (
await this.client.servers.getUnitInfo({
serverKey: this.server,
versionCode: this.version,
})
).data;
}
);
}
@ -84,15 +106,14 @@ class TWHelpConnector {
}
return this.inMemoryCache.load(key, 86400, async () => {
const ennoblements = await this.client.villageEnnoblements(
this.version,
this.server,
id,
{
limit: 1,
const ennoblements =
await this.client.ennoblements.listVillageEnnoblements({
versionCode: this.version,
serverKey: this.server,
sort: ['createdAt:DESC'],
}
);
villageId: id,
limit: 1,
});
return ennoblements.data.length > 0 ? ennoblements.data[0] : null;
});
}
@ -245,9 +266,9 @@ class Popup {
class ExtendedMapPopup {
connector: TWHelpConnector;
constructor(client: TWHelpClient) {
constructor(baseApiUrl: string) {
this.connector = new TWHelpConnector(
client,
baseApiUrl,
window.game_data.market,
window.game_data.world
);
@ -273,9 +294,7 @@ class ExtendedMapPopup {
return;
}
await new ExtendedMapPopup(
new TWHelpClient(process.env.TWHELP_API_BASE_URL ?? '')
)
await new ExtendedMapPopup(process.env.TWHELP_API_BASE_URL ?? '')
.run()
.catch((err) => {
console.log(err);

View File

@ -1,10 +1,16 @@
// Extended player profile
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';
import {
TWHelpClient,
PlayerSnapshot,
Player,
AxiosHttpRequestWithTimeout,
} from './lib/twhelp';
import { DialogTable } from './common/dialog-table';
import { Cache } from './lib/cache';
const t = createTranslationFunc({
pl_PL: {
@ -59,62 +65,98 @@ const t = createTranslationFunc({
});
class TWHelpConnector {
private readonly client: TWHelpClient;
constructor(
private readonly client: TWHelpClient,
readonly baseUrl: string,
private readonly version: string,
private readonly server: string,
private readonly id: number
) {}
) {
this.client = new TWHelpClient(
{
BASE: baseUrl,
},
AxiosHttpRequestWithTimeout
);
}
player() {
return this.client.player(this.version, this.server, this.id);
async player() {
return (
await this.client.players.getPlayer({
versionCode: this.version,
serverKey: this.server,
playerId: this.id,
})
).data;
}
async latestSnapshot() {
const snapshot = await this.playerSnapshots(1, 1);
const snapshot = await this.playerSnapshots(undefined, 1);
return snapshot.data.length > 0 ? snapshot.data[0] : null;
}
playerTribeChanges(page: number, limit: number) {
return this.client.playerTribeChanges(this.version, this.server, this.id, {
offset: (page - 1) * limit,
playerTribeChanges(cursor: string | undefined, limit: number) {
return this.client.tribeChanges.listPlayerTribeChanges({
versionCode: this.version,
serverKey: this.server,
playerId: this.id,
limit,
sort: ['createdAt:desc', 'id:asc'],
cursor,
sort: ['createdAt:DESC'],
});
}
playerEnnoblements(page: number, limit: number) {
return this.client.playerEnnoblements(this.version, this.server, this.id, {
offset: (page - 1) * limit,
playerEnnoblements(cursor: string | undefined, limit: number) {
return this.client.ennoblements.listPlayerEnnoblements({
versionCode: this.version,
serverKey: this.server,
playerId: this.id,
limit,
sort: ['createdAt:desc'],
cursor,
sort: ['createdAt:DESC'],
});
}
playerSnapshots(page: number, limit: number) {
return this.client.playerSnapshots(this.version, this.server, this.id, {
offset: (page - 1) * limit,
playerSnapshots(cursor: string | undefined, limit: number) {
return this.client.snapshots.listPlayerPlayerSnapshots({
versionCode: this.version,
serverKey: this.server,
playerId: this.id,
limit,
sort: ['date:desc'],
cursor,
sort: ['date:DESC'],
});
}
playerOtherServers(page: number, limit: number) {
return this.client.playerOtherServers(this.version, this.server, this.id, {
offset: (page - 1) * limit,
playerOtherServers(cursor: string | undefined, limit: number) {
return this.client.players.listVersionPlayers({
versionCode: this.version,
limit,
cursor,
id: [this.id],
});
}
}
class InADayConnector {
constructor(
private readonly client: InADayClient,
private readonly name: string
) {}
private static readonly CACHE_KEY_PREFIX =
'extended_player_profile_in_a_day_';
private readonly cache = new Cache(localStorage);
private readonly client = new InADayClient();
constructor(private readonly name: string) {}
player() {
return this.client.player(this.name);
return this.cache.load(
InADayConnector.CACHE_KEY_PREFIX + this.name,
600,
() => {
return this.client.player(this.name);
}
);
}
}
@ -160,20 +202,20 @@ class UI {
<tr>
<td>${t('Best rank')}:</td>
<td>${this.player.bestRank} (${new Date(
this.player.bestRankAt
).toLocaleString()})</td>
this.player.bestRankAt
).toLocaleString()})</td>
</tr>
<tr>
<td>${t('Most points')}:</td>
<td>${this.player.mostPoints.toLocaleString()} (${new Date(
this.player.mostPointsAt
).toLocaleString()})</td>
this.player.mostPointsAt
).toLocaleString()})</td>
</tr>
<tr>
<td>${t('Most villages')}:</td>
<td>${this.player.mostVillages.toLocaleString()} (${new Date(
this.player.mostVillagesAt
).toLocaleString()})</td>
this.player.mostVillagesAt
).toLocaleString()})</td>
</tr>
`
);
@ -204,38 +246,54 @@ class UI {
},
{
header: t('ODA'),
value: this.player.scoreAtt - (this.latestSnapshot?.scoreAtt ?? 0),
value:
this.player.opponentsDefeated.scoreAtt -
(this.latestSnapshot?.opponentsDefeated.scoreAtt ?? 0),
},
{
header: t('ODA - rank'),
value: this.player.rankAtt - (this.latestSnapshot?.rankAtt ?? 0),
value:
this.player.opponentsDefeated.rankAtt -
(this.latestSnapshot?.opponentsDefeated.rankAtt ?? 0),
rank: true,
},
{
header: t('ODD'),
value: this.player.scoreDef - (this.latestSnapshot?.scoreDef ?? 0),
value:
this.player.opponentsDefeated.scoreDef -
(this.latestSnapshot?.opponentsDefeated.scoreDef ?? 0),
},
{
header: t('ODD - rank'),
value: this.player.rankDef - (this.latestSnapshot?.rankDef ?? 0),
value:
this.player.opponentsDefeated.rankDef -
(this.latestSnapshot?.opponentsDefeated.rankDef ?? 0),
rank: true,
},
{
header: t('ODS'),
value: this.player.scoreSup - (this.latestSnapshot?.scoreSup ?? 0),
value:
this.player.opponentsDefeated.scoreSup -
(this.latestSnapshot?.opponentsDefeated.scoreSup ?? 0),
},
{
header: t('ODS - rank'),
value: this.player.rankSup - (this.latestSnapshot?.rankSup ?? 0),
value:
this.player.opponentsDefeated.rankSup -
(this.latestSnapshot?.opponentsDefeated.rankSup ?? 0),
rank: true,
},
{
header: t('OD'),
value: this.player.scoreTotal - (this.latestSnapshot?.scoreTotal ?? 0),
value:
this.player.opponentsDefeated.scoreTotal -
(this.latestSnapshot?.opponentsDefeated.scoreTotal ?? 0),
},
{
header: t('OD - rank'),
value: this.player.rankTotal - (this.latestSnapshot?.rankTotal ?? 0),
value:
this.player.opponentsDefeated.rankTotal -
(this.latestSnapshot?.opponentsDefeated.rankTotal ?? 0),
rank: true,
},
];
@ -364,8 +422,8 @@ class UI {
},
],
30,
(page: number, limit: number) => {
return this.twhelpConnector.playerOtherServers(page, limit);
(cursor: string | undefined, limit: number) => {
return this.twhelpConnector.playerOtherServers(cursor, limit);
}
).render();
}
@ -454,8 +512,8 @@ class UI {
},
],
30,
(page: number, limit: number) => {
return this.twhelpConnector.playerTribeChanges(page, limit);
(cursor: string | undefined, limit: number) => {
return this.twhelpConnector.playerTribeChanges(cursor, limit);
}
).render();
}
@ -473,8 +531,8 @@ class UI {
{
header: t('Tribe'),
accessor: (s) =>
s.tribe
? `<a href="${s.tribe.profileUrl}">${s.tribe.tag}</a>`
s.player.tribe
? `<a href="${s.player.tribe.profileUrl}">${s.player.tribe.tag}</a>`
: '-',
},
{
@ -489,29 +547,29 @@ class UI {
{
header: t('OD'),
accessor: (s) =>
`${s.scoreTotal.toLocaleString()} (<strong>${
s.rankTotal
`${s.opponentsDefeated.scoreTotal.toLocaleString()} (<strong>${
s.opponentsDefeated.rankTotal
}</strong>)`,
},
{
header: t('ODA'),
accessor: (s) =>
`${s.scoreAtt.toLocaleString()} (<strong>${s.rankAtt}</strong>)`,
`${s.opponentsDefeated.scoreAtt.toLocaleString()} (<strong>${s.opponentsDefeated.rankAtt}</strong>)`,
},
{
header: t('ODD'),
accessor: (s) =>
`${s.scoreDef.toLocaleString()} (<strong>${s.rankDef}</strong>)`,
`${s.opponentsDefeated.scoreDef.toLocaleString()} (<strong>${s.opponentsDefeated.rankDef}</strong>)`,
},
{
header: t('ODS'),
accessor: (s) =>
`${s.scoreSup.toLocaleString()} (<strong>${s.rankSup}</strong>)`,
`${s.opponentsDefeated.scoreSup.toLocaleString()} (<strong>${s.opponentsDefeated.rankSup}</strong>)`,
},
],
30,
(page: number, limit: number) => {
return this.twhelpConnector.playerSnapshots(page, limit);
(cursor: string | undefined, limit: number) => {
return this.twhelpConnector.playerSnapshots(cursor, limit);
}
).render();
}
@ -563,8 +621,8 @@ class UI {
},
],
30,
(page: number, limit: number) => {
return this.twhelpConnector.playerEnnoblements(page, limit);
(cursor: string | undefined, limit: number) => {
return this.twhelpConnector.playerEnnoblements(cursor, limit);
}
).render();
}
@ -574,18 +632,15 @@ class ExtendedPlayerProfile {
private readonly twhelpConnector: TWHelpConnector;
private readonly inADayConnector: InADayConnector;
constructor(twhelpClient: TWHelpClient, inADayClient: InADayClient) {
constructor(apiBaseUrl: string) {
this.twhelpConnector = new TWHelpConnector(
twhelpClient,
apiBaseUrl,
window.game_data.market,
window.game_data.world,
this.getPlayerId()
);
this.inADayConnector = new InADayConnector(
inADayClient,
this.getPlayerName()
);
this.inADayConnector = new InADayConnector(this.getPlayerName());
}
async run() {
@ -627,10 +682,7 @@ class ExtendedPlayerProfile {
return;
}
await new ExtendedPlayerProfile(
new TWHelpClient(process.env.TWHELP_API_BASE_URL ?? ''),
new InADayClient()
)
await new ExtendedPlayerProfile(process.env.TWHELP_API_BASE_URL ?? '')
.run()
.catch((err) => {
console.log(err);

View File

@ -1,7 +1,13 @@
import { Player, Tribe, TribeSnapshot, TWHelpClient } from './lib/twhelp';
import {
AxiosHttpRequestWithTimeout,
Player,
Tribe,
TribeSnapshot,
TWHelpClient,
} from './lib/twhelp';
import { createTranslationFunc } from './utils';
import { DialogTable } from './common/dialog-table';
import { buildURL } from './lib/twstats';
import { DialogTable } from './common/dialog-table';
const t = createTranslationFunc({
pl_PL: {
@ -44,47 +50,75 @@ const t = createTranslationFunc({
});
class TWHelpConnector {
private readonly client: TWHelpClient;
constructor(
private readonly client: TWHelpClient,
baseUrl: string,
private readonly version: string,
private readonly server: string,
private readonly id: number
) {}
) {
this.client = new TWHelpClient(
{
BASE: baseUrl,
},
AxiosHttpRequestWithTimeout
);
}
tribe() {
return this.client.tribe(this.version, this.server, this.id);
async tribe() {
return (
await this.client.tribes.getTribe({
versionCode: this.version,
serverKey: this.server,
tribeId: this.id,
})
).data;
}
tribeMembers() {
return this.client.tribeMembers(this.version, this.server, this.id);
return this.client.tribes.listTribeMembers({
versionCode: this.version,
serverKey: this.server,
tribeId: this.id,
});
}
async latestSnapshot() {
const snapshot = await this.tribeSnapshots(1, 1);
const snapshot = await this.tribeSnapshots(undefined, 1);
return snapshot.data.length > 0 ? snapshot.data[0] : null;
}
tribeSnapshots(page: number, limit: number) {
return this.client.tribeSnapshots(this.version, this.server, this.id, {
offset: (page - 1) * limit,
tribeSnapshots(cursor: string | undefined, limit: number) {
return this.client.snapshots.listTribeTribeSnapshots({
versionCode: this.version,
serverKey: this.server,
tribeId: this.id,
cursor,
limit,
sort: ['date:desc'],
sort: ['date:DESC'],
});
}
tribeTribeChanges(page: number, limit: number) {
return this.client.tribeTribeChanges(this.version, this.server, this.id, {
offset: (page - 1) * limit,
memberChanges(cursor: string | undefined, limit: number) {
return this.client.tribeChanges.listTribeMemberChanges({
versionCode: this.version,
serverKey: this.server,
tribeId: this.id,
cursor,
limit,
sort: ['createdAt:desc'],
sort: ['createdAt:DESC'],
});
}
tribeEnnoblements(page: number, limit: number) {
return this.client.tribeEnnoblements(this.version, this.server, this.id, {
offset: (page - 1) * limit,
tribeEnnoblements(cursor: string | undefined, limit: number) {
return this.client.ennoblements.listTribeEnnoblements({
versionCode: this.version,
serverKey: this.server,
tribeId: this.id,
cursor,
limit,
sort: ['createdAt:desc'],
sort: ['createdAt:DESC'],
});
}
}
@ -133,20 +167,20 @@ class UI {
<tr>
<td>${t('Best rank')}:</td>
<td>${this.tribe.bestRank} (${new Date(
this.tribe.bestRankAt
).toLocaleString()})</td>
this.tribe.bestRankAt
).toLocaleString()})</td>
</tr>
<tr>
<td>${t('Most points')}:</td>
<td>${this.tribe.mostPoints.toLocaleString()} (${new Date(
this.tribe.mostPointsAt
).toLocaleString()})</td>
this.tribe.mostPointsAt
).toLocaleString()})</td>
</tr>
<tr>
<td>${t('Most villages')}:</td>
<td>${this.tribe.mostVillages.toLocaleString()} (${new Date(
this.tribe.mostVillagesAt
).toLocaleString()})</td>
this.tribe.mostVillagesAt
).toLocaleString()})</td>
</tr>
`
);
@ -185,29 +219,41 @@ class UI {
},
{
header: t('ODA'),
value: this.tribe.scoreAtt - (this.latestSnapshot?.scoreAtt ?? 0),
value:
this.tribe.opponentsDefeated.scoreAtt -
(this.latestSnapshot?.opponentsDefeated.scoreAtt ?? 0),
},
{
header: t('ODA - rank'),
value: this.tribe.rankAtt - (this.latestSnapshot?.rankAtt ?? 0),
value:
this.tribe.opponentsDefeated.rankAtt -
(this.latestSnapshot?.opponentsDefeated.rankAtt ?? 0),
rank: true,
},
{
header: t('ODD'),
value: this.tribe.scoreDef - (this.latestSnapshot?.scoreDef ?? 0),
value:
this.tribe.opponentsDefeated.scoreDef -
(this.latestSnapshot?.opponentsDefeated.scoreDef ?? 0),
},
{
header: t('ODD - rank'),
value: this.tribe.rankDef - (this.latestSnapshot?.rankDef ?? 0),
value:
this.tribe.opponentsDefeated.rankDef -
(this.latestSnapshot?.opponentsDefeated.rankDef ?? 0),
rank: true,
},
{
header: t('OD'),
value: this.tribe.scoreTotal - (this.latestSnapshot?.scoreTotal ?? 0),
value:
this.tribe.opponentsDefeated.scoreTotal -
(this.latestSnapshot?.opponentsDefeated.scoreTotal ?? 0),
},
{
header: t('OD - rank'),
value: this.tribe.rankTotal - (this.latestSnapshot?.rankTotal ?? 0),
value:
this.tribe.opponentsDefeated.rankTotal -
(this.latestSnapshot?.opponentsDefeated.rankTotal ?? 0),
rank: true,
},
];
@ -231,10 +277,10 @@ class UI {
r.value,
r.rank
)}">${
r.customFormat
? r.customFormat(r.value)
: Math.abs(r.value).toLocaleString()
}</td>
r.customFormat
? r.customFormat(r.value)
: Math.abs(r.value).toLocaleString()
}</td>
</tr>
`
)
@ -323,8 +369,8 @@ class UI {
},
],
30,
(page: number, limit: number) => {
return this.twhelpConnector.tribeTribeChanges(page, limit);
(cursor: string | undefined, limit: number) => {
return this.twhelpConnector.memberChanges(cursor, limit);
}
).render();
}
@ -359,24 +405,24 @@ class UI {
{
header: t('OD'),
accessor: (s) =>
`${s.scoreTotal.toLocaleString()} (<strong>${
s.rankTotal
`${s.opponentsDefeated.scoreTotal.toLocaleString()} (<strong>${
s.opponentsDefeated.rankTotal
}</strong>)`,
},
{
header: t('ODA'),
accessor: (s) =>
`${s.scoreAtt.toLocaleString()} (<strong>${s.rankAtt}</strong>)`,
`${s.opponentsDefeated.scoreAtt.toLocaleString()} (<strong>${s.opponentsDefeated.rankAtt}</strong>)`,
},
{
header: t('ODD'),
accessor: (s) =>
`${s.scoreDef.toLocaleString()} (<strong>${s.rankDef}</strong>)`,
`${s.opponentsDefeated.scoreDef.toLocaleString()} (<strong>${s.opponentsDefeated.rankDef}</strong>)`,
},
],
30,
(page: number, limit: number) => {
return this.twhelpConnector.tribeSnapshots(page, limit);
(cursor: string | undefined, limit: number) => {
return this.twhelpConnector.tribeSnapshots(cursor, limit);
}
).render();
}
@ -428,8 +474,8 @@ class UI {
},
],
30,
(page: number, limit: number) => {
return this.twhelpConnector.tribeEnnoblements(page, limit);
(cursor: string | undefined, limit: number) => {
return this.twhelpConnector.tribeEnnoblements(cursor, limit);
}
).render();
}
@ -477,10 +523,10 @@ class UI {
const member = this.members.find((m) => m.id === id);
[
(member?.rankAtt ?? 0).toString(),
(member?.rankDef ?? 0).toString(),
(member?.rankSup ?? 0).toString(),
(member?.rankTotal ?? 0).toString(),
(member?.opponentsDefeated.rankAtt ?? 0).toString(),
(member?.opponentsDefeated.rankDef ?? 0).toString(),
(member?.opponentsDefeated.rankSup ?? 0).toString(),
(member?.opponentsDefeated.rankTotal ?? 0).toString(),
member?.lastActivityAt
? new Date(member.lastActivityAt).toLocaleString()
: '-',
@ -501,9 +547,9 @@ class UI {
class ExtendedTribeProfile {
private readonly twhelpConnector: TWHelpConnector;
constructor(twhelpClient: TWHelpClient) {
constructor(apiBaseUrl: string) {
this.twhelpConnector = new TWHelpConnector(
twhelpClient,
apiBaseUrl,
window.game_data.market,
window.game_data.world,
this.getTribeId()
@ -543,9 +589,7 @@ class ExtendedTribeProfile {
return;
}
await new ExtendedTribeProfile(
new TWHelpClient(process.env.TWHELP_API_BASE_URL ?? '')
)
await new ExtendedTribeProfile(process.env.TWHELP_API_BASE_URL ?? '')
.run()
.catch((err) => {
console.log(err);

View File

@ -1,8 +1,13 @@
import { Ennoblement, ServerConfig, TWHelpClient } from './lib/twhelp';
import { createTranslationFunc } from './utils';
import { DialogTable } from './common/dialog-table';
import { calcLoyalty } from './lib/tw';
import { Cache } from './lib/cache';
import {
TWHelpClient,
Ennoblement,
ServerConfig,
AxiosHttpRequestWithTimeout,
} from './lib/twhelp';
import { DialogTable } from './common/dialog-table';
const t = createTranslationFunc({
pl_PL: {
@ -22,32 +27,47 @@ const t = createTranslationFunc({
class TWHelpConnector {
private static SERVER_CONFIG_CACHE_KEY =
'extended_village_profile_server_config';
'extended_village_profile_server_config_v2';
private cache = new Cache(localStorage);
private client: TWHelpClient;
constructor(
private readonly client: TWHelpClient,
readonly baseUrl: string,
private readonly version: string,
private readonly server: string,
private readonly id: number
) {}
) {
this.client = new TWHelpClient(
{
BASE: baseUrl,
},
AxiosHttpRequestWithTimeout
);
}
serverConfig() {
return this.cache.load(
TWHelpConnector.SERVER_CONFIG_CACHE_KEY,
3600,
() => {
return this.client.serverConfig(this.version, this.server);
async () => {
return (
await this.client.servers.getServerConfig({
versionCode: this.version,
serverKey: this.server,
})
).data;
}
);
}
async latestEnnoblement() {
const ennoblements = await this.client.villageEnnoblements(
this.version,
this.server,
this.id,
const ennoblements = await this.client.ennoblements.listVillageEnnoblements(
{
versionCode: this.version,
serverKey: this.server,
villageId: this.id,
limit: 1,
sort: ['createdAt:DESC'],
}
@ -55,11 +75,14 @@ class TWHelpConnector {
return ennoblements.data.length > 0 ? ennoblements.data[0] : null;
}
villageEnnoblements(page: number, limit: number) {
return this.client.villageEnnoblements(this.version, this.server, this.id, {
offset: (page - 1) * limit,
limit,
sort: ['createdAt:desc'],
villageEnnoblements(cursor: string | undefined, limit: number) {
return this.client.ennoblements.listVillageEnnoblements({
cursor,
versionCode: this.version,
serverKey: this.server,
villageId: this.id,
limit: limit,
sort: ['createdAt:DESC'],
});
}
}
@ -138,7 +161,7 @@ class UI {
private async showEnnoblements(e: Event) {
e.preventDefault();
await new DialogTable(
await new DialogTable<Ennoblement>(
DialogId.ENNOBLEMENTS,
[
{
@ -182,8 +205,8 @@ class UI {
},
],
30,
(page: number, limit: number) => {
return this.connector.villageEnnoblements(page, limit);
async (cursor, limit) => {
return this.connector.villageEnnoblements(cursor, limit);
}
).render();
}
@ -191,9 +214,9 @@ class UI {
class ExtendedVillageProfile {
connector: TWHelpConnector;
constructor(client: TWHelpClient) {
constructor(apiBaseUrl: string) {
this.connector = new TWHelpConnector(
client,
apiBaseUrl,
window.game_data.market,
window.game_data.world,
this.getVillageId()
@ -226,9 +249,7 @@ class ExtendedVillageProfile {
return;
}
await new ExtendedVillageProfile(
new TWHelpClient(process.env.TWHELP_API_BASE_URL ?? '')
)
await new ExtendedVillageProfile(process.env.TWHELP_API_BASE_URL ?? '')
.run()
.catch((err) => {
console.log(err);

View File

@ -1,5 +1,5 @@
import addSeconds from 'date-fns/addSeconds';
import isAfter from 'date-fns/isAfter';
import { addSeconds } from 'date-fns/addSeconds';
import { isAfter } from 'date-fns/isAfter';
export interface Storage {
getItem(key: string): string | null;

View File

@ -1,4 +1,4 @@
import differenceInMinutes from 'date-fns/differenceInMinutes';
import { differenceInMinutes } from 'date-fns/differenceInMinutes';
import { LOYALTY_AFTER_CONQUER, MAX_LOYALTY } from './constants';
export const calcLoyalty = (

View File

@ -1,424 +0,0 @@
import axios, {
AxiosInstance,
AxiosResponseHeaders,
RawAxiosResponseHeaders,
} from 'axios';
export type ServerMeta = {
key: string;
open: boolean;
};
export type Tribe = {
id: number;
name: string;
tag: string;
numMembers: number;
numVillages: number;
points: number;
allPoints: number;
rank: number;
dominance: number;
rankAtt: number;
scoreAtt: number;
rankDef: number;
scoreDef: number;
rankTotal: number;
scoreTotal: number;
profileUrl: string;
bestRank: number;
bestRankAt: string;
mostPoints: number;
mostPointsAt: string;
mostVillages: number;
mostVillagesAt: string;
createdAt: string;
deletedAt: string | null;
};
export type TribeMeta = {
id: number;
name: string;
tag: string;
profileUrl: string;
};
export type Player = {
id: number;
points: number;
rank: number;
numVillages: number;
scoreAtt: number;
rankAtt: number;
scoreDef: number;
rankDef: number;
scoreSup: number;
rankSup: number;
scoreTotal: number;
rankTotal: number;
bestRank: number;
bestRankAt: string;
mostPoints: number;
mostPointsAt: string;
mostVillages: number;
mostVillagesAt: string;
lastActivityAt: string;
createdAt: string;
tribe: TribeMeta | null;
};
export type PlayerWithServer = Player & {
server: ServerMeta;
};
export type PlayerMeta = {
id: number;
name: string;
profileUrl: string;
tribe: TribeMeta | null;
};
export type VillageMeta = {
id: number;
fullName: string;
profileUrl: string;
x: number;
y: number;
continent: string;
player: PlayerMeta | null;
};
export type TribeChange = {
id: number;
newTribe: TribeMeta | null;
player: PlayerMeta;
createdAt: string;
};
export type Ennoblement = {
id: number;
points: number;
newOwner: PlayerMeta | null;
village: VillageMeta;
createdAt: string;
};
export type TribeSnapshot = {
id: number;
numMembers: number;
numVillages: number;
points: number;
allPoints: number;
rank: number;
dominance: number;
rankAtt: number;
scoreAtt: number;
rankDef: number;
scoreDef: number;
rankTotal: number;
scoreTotal: number;
date: string;
};
export type PlayerSnapshot = {
id: number;
tribe: TribeMeta | null;
points: number;
numVillages: number;
rank: number;
rankAtt: number;
rankDef: number;
rankSup: number;
rankTotal: number;
scoreAtt: number;
scoreDef: number;
scoreSup: number;
scoreTotal: number;
date: string;
};
export type ServerConfig = {
speed: number;
unitSpeed: number;
snob: {
maxDist: number;
};
};
export type Unit = {
speed: number;
};
export type UnitInfo = {
archer: Unit;
axe: Unit;
catapult: Unit;
heavy: Unit;
knight: Unit;
light: Unit;
marcher: Unit;
militia: Unit;
ram: Unit;
snob: Unit;
spear: Unit;
spy: Unit;
sword: Unit;
};
export type ListResult<T> = {
data: T[];
total: number;
};
export type ListTribeChangesQueryParams = {
offset?: number;
limit?: number;
sort?: string[];
};
export type ListEnnoblementsParams = {
offset?: number;
limit?: number;
sort?: string[];
};
export type ListTribeMembers = {
offset?: number;
limit?: number;
sort?: string[];
};
export type ListSnapshotsParams = {
offset?: number;
limit?: number;
sort?: string[];
};
export type ListPlayerOtherServersParams = {
offset?: number;
limit?: number;
};
export class TWHelpClient {
client: AxiosInstance;
constructor(url: string, timeout = 10000) {
this.client = axios.create({
baseURL: url,
timeout,
});
}
public async serverConfig(
version: string,
server: string
): Promise<ServerConfig> {
const resp = await this.client.get(
`/api/v1/versions/${version}/servers/${server}/config`
);
return resp.data.data;
}
public async unitInfo(version: string, server: string): Promise<UnitInfo> {
const resp = await this.client.get(
`/api/v1/versions/${version}/servers/${server}/unit-info`
);
return resp.data.data;
}
public async tribe(
version: string,
server: string,
id: number
): Promise<Tribe> {
const resp = await this.client.get(
`/api/v1/versions/${version}/servers/${server}/tribes/${id}`
);
return resp.data.data;
}
public async tribeSnapshots(
version: string,
server: string,
id: number,
queryParams?: ListSnapshotsParams
): Promise<ListResult<TribeSnapshot>> {
const queryString = queryParams ? this.buildQueryString(queryParams) : '';
const resp = await this.client.get(
`/api/v1/versions/${version}/servers/${server}/tribes/${id}/snapshots?${queryString}`
);
return {
data: resp.data.data,
total: this.parseTotal(resp.headers),
};
}
public async tribeTribeChanges(
version: string,
server: string,
id: number,
queryParams?: ListTribeChangesQueryParams
): Promise<ListResult<TribeChange>> {
const queryString = queryParams ? this.buildQueryString(queryParams) : '';
const resp = await this.client.get(
`/api/v1/versions/${version}/servers/${server}/tribes/${id}/tribe-changes?${queryString}`
);
return {
data: resp.data.data,
total: this.parseTotal(resp.headers),
};
}
public async tribeEnnoblements(
version: string,
server: string,
id: number,
queryParams?: ListEnnoblementsParams
): Promise<ListResult<Ennoblement>> {
const queryString = queryParams ? this.buildQueryString(queryParams) : '';
const resp = await this.client.get(
`/api/v1/versions/${version}/servers/${server}/tribes/${id}/ennoblements?${queryString}`
);
return {
data: resp.data.data,
total: this.parseTotal(resp.headers),
};
}
public async tribeMembers(
version: string,
server: string,
id: number,
queryParams?: ListTribeMembers
): Promise<ListResult<Player>> {
const queryString = queryParams ? this.buildQueryString(queryParams) : '';
const resp = await this.client.get(
`/api/v1/versions/${version}/servers/${server}/tribes/${id}/members?${queryString}`
);
return {
data: resp.data.data,
total: this.parseTotal(resp.headers),
};
}
public async player(
version: string,
server: string,
id: number
): Promise<Player> {
const resp = await this.client.get(
`/api/v1/versions/${version}/servers/${server}/players/${id}`
);
return resp.data.data;
}
public async playerTribeChanges(
version: string,
server: string,
id: number,
queryParams?: ListTribeChangesQueryParams
): Promise<ListResult<TribeChange>> {
const queryString = queryParams ? this.buildQueryString(queryParams) : '';
const resp = await this.client.get(
`/api/v1/versions/${version}/servers/${server}/players/${id}/tribe-changes?${queryString}`
);
return {
data: resp.data.data,
total: this.parseTotal(resp.headers),
};
}
public async playerEnnoblements(
version: string,
server: string,
id: number,
queryParams?: ListEnnoblementsParams
): Promise<ListResult<Ennoblement>> {
const queryString = queryParams ? this.buildQueryString(queryParams) : '';
const resp = await this.client.get(
`/api/v1/versions/${version}/servers/${server}/players/${id}/ennoblements?${queryString}`
);
return {
data: resp.data.data,
total: this.parseTotal(resp.headers),
};
}
public async playerSnapshots(
version: string,
server: string,
id: number,
queryParams?: ListSnapshotsParams
): Promise<ListResult<PlayerSnapshot>> {
const queryString = queryParams ? this.buildQueryString(queryParams) : '';
const resp = await this.client.get(
`/api/v1/versions/${version}/servers/${server}/players/${id}/snapshots?${queryString}`
);
return {
data: resp.data.data,
total: this.parseTotal(resp.headers),
};
}
public async playerOtherServers(
version: string,
server: string,
id: number,
queryParams?: ListPlayerOtherServersParams
): Promise<ListResult<PlayerWithServer>> {
const queryString = queryParams ? this.buildQueryString(queryParams) : '';
const resp = await this.client.get(
`/api/v1/versions/${version}/servers/${server}/players/${id}/other-servers?${queryString}`
);
return {
data: resp.data.data,
total: this.parseTotal(resp.headers),
};
}
public async villageEnnoblements(
version: string,
server: string,
id: number,
queryParams?: ListEnnoblementsParams
): Promise<ListResult<Ennoblement>> {
const queryString = queryParams ? this.buildQueryString(queryParams) : '';
const resp = await this.client.get(
`/api/v1/versions/${version}/servers/${server}/villages/${id}/ennoblements?${queryString}`
);
return {
data: resp.data.data,
total: this.parseTotal(resp.headers),
};
}
private buildQueryString(
queryParams: Record<string, string | boolean | number | Date | string[]>
): string {
const params = new URLSearchParams();
for (const [name, val] of Object.entries(queryParams)) {
if (Array.isArray(val)) {
val.forEach((s) => {
params.append(name, s);
});
continue;
}
if (val instanceof Date) {
params.set(name, val.toISOString());
continue;
}
params.set(name, val.toString());
}
return params.toString();
}
private parseTotal(
headers: RawAxiosResponseHeaders | AxiosResponseHeaders
): number {
return parseInt(headers['x-total-count'] ?? '0');
}
}

1
src/lib/twhelp/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
generated

2
src/lib/twhelp/index.ts Normal file
View File

@ -0,0 +1,2 @@
export * from './generated';
export * from './timeout';

File diff suppressed because one or more lines are too long

20
src/lib/twhelp/timeout.ts Normal file
View File

@ -0,0 +1,20 @@
import axios from 'axios';
import { request as __request } from './generated/core/request';
import { CancelablePromise } from './generated/core/CancelablePromise';
import { BaseHttpRequest } from './generated/core/BaseHttpRequest';
import { ApiRequestOptions } from './generated/core/ApiRequestOptions';
import type { OpenAPIConfig } from './generated/core/OpenAPI';
export class AxiosHttpRequestWithTimeout extends BaseHttpRequest {
axiosInstance = axios.create({
timeout: 5000,
});
constructor(config: OpenAPIConfig) {
super(config);
}
public override request<T>(options: ApiRequestOptions): CancelablePromise<T> {
return __request(this.config, options, this.axiosInstance);
}
}

1947
yarn.lock

File diff suppressed because it is too large Load Diff