Add error handling, fix authentication and fix online time display

This commit is contained in:
2020-06-01 13:32:45 +02:00
parent 5925872ac8
commit 66898bfab6
3 changed files with 94 additions and 56 deletions

View File

@@ -24,6 +24,7 @@
"@nestjs/common": "^7.0.0", "@nestjs/common": "^7.0.0",
"@nestjs/core": "^7.0.0", "@nestjs/core": "^7.0.0",
"@nestjs/platform-express": "^7.0.0", "@nestjs/platform-express": "^7.0.0",
"node-fetch": "^2.6.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rxjs": "^6.5.4" "rxjs": "^6.5.4"

View File

@@ -15,7 +15,7 @@ export class AppController {
return this.appService.getHello(); return this.appService.getHello();
} }
@Get() @Get('/stats')
async getStats(): Promise<TableEntry[]> { async getStats(): Promise<TableEntry[]> {
return await this.sinusBotService.fetchStats(); return await this.sinusBotService.fetchStats();
} }

View File

@@ -1,29 +1,45 @@
import { TableEntry } from "../models/TableEntry"; import { TableEntry } from "../models/TableEntry";
import { TunakillUser } from "../models/TunakillUser"; import { TunakillUser } from "../models/TunakillUser";
import { Injectable } from "@nestjs/common"; import {HttpException, HttpStatus, Injectable} from "@nestjs/common";
import {TunakillLogin} from "../models/TunakillLogin"; import {TunakillLogin} from "../models/TunakillLogin";
import fetch from 'node-fetch';
@Injectable() @Injectable()
export class SinusBotService { export class SinusBotService {
private host = process.env.HOST private host = process.env.HOST
private botId = process.env.SINUSBOT_BOTID; private botInfo = {
id: process.env.SINUSBOT_BOTID,
instanceId: null
}
private tunaKillURL = `${this.host}/api/v1/bot/i/${this.botId}/event/tunakill_rank_all_user`; private tunaKillURL = `${this.host}/api/v1/bot/i/${this.botInfo.instanceId}/event/tunakill_rank_all_user`;
private loginURL = `${this.host}/api/v1/bot/login`; private loginURL = `${this.host}/api/v1/bot/login`;
private credentials = { private credentials = {
username: process.env.SINUSBOT_USER, username: process.env.SINUSBOT_USER,
password: process.env.SINUSBOT_PASSWORD password: process.env.SINUSBOT_PASSWORD
} }
private bearer: string; private bearer?: string;
public async fetchStats(): Promise<TableEntry[] | undefined> { public async fetchStats(): Promise<TableEntry[]> {
if (this.bearer === null) { if (this.bearer == null) {
this.bearer = await this.login().catch(error => error.message); console.log(`Hey! I'm trying to get my Bearer token!`);
await this.login()
.then(res => this.logResponse(res))
.then(token => this.bearer = token)
.catch(error => {
throw this.createHttpException(HttpStatus.UNAUTHORIZED, error.message);
});
console.log(`Seems like I have my Bearer token! It's called: ${this.bearer}`);
} }
return await fetch(this.tunaKillURL) console.log(`I try to fetch user data now!`)
.then(res => res.json()) return await fetch(this.tunaKillURL, this.requestConfig(null, this.bearer))
.then(data => this.consumeTunakillResponse(data)); .then(res => this.checkStatus(res))
.then(data => data.json())
.then(data => this.consumeTunakillResponse(data))
.catch(error => {
throw this.createHttpException(HttpStatus.UNAUTHORIZED, error.message);
});
} }
/** /**
@@ -31,33 +47,48 @@ export class SinusBotService {
*/ */
private async login(): Promise<string> { private async login(): Promise<string> {
const body: TunakillLogin = { const body: TunakillLogin = {
botId: this.botId, username: this.credentials.username,
username: this.credentials.password, password: this.credentials.password,
password: this.credentials.username, botId: this.botInfo.id
} }
console.log(`> I try to login now! My credentials are: ${JSON.stringify(body)}`)
return await fetch(this.loginURL, this.requestConfig(JSON.stringify(body))) return await fetch(this.loginURL, this.requestConfig(JSON.stringify(body)))
.then(res => { .then(res => this.checkStatus(res))
if (res.ok && res.body !== null) { .then(data => data.json())
return res.json(); .then(data => data.token);
} else {
throw new Error('Response is invalid.');
}
})
.then(data => {
if (data.token !== null) {
return data.token;
} else {
throw new Error('Token in response does not exist.');
}
});
} }
requestConfig = (body?: string, bearerToken?: string): RequestInit => { private consumeTunakillResponse(data: any): TableEntry[] {
if (!(data !== null || data[0] !== null || data[0].data !== null)) {
throw Error('Response from SinusBot does not have any data to parse.')
}
const response = data[0].data;
if (!(response.length > 0)) {
throw Error('User list is empty.')
}
return response
// TODO: Remove hardcoded username filter for bots.
.filter((user: TunakillUser) => user.name !== "Server Query Admin" && user.name !== "DJ Inshalla")
.filter((user: TunakillUser) => user.time != null)
.map((user: TunakillUser) => {
return {
name: user.name,
rawTime: user.time,
onlineTime: this.humanizeTime(user.time)
};
})
.sort((a: any, b: any) => this.sortByDescendingTime(a.rawTime, b.rawTime));
}
private requestConfig = (body?: string, bearerToken?: string, requestType: string = 'POST'): RequestInit => {
return { return {
method: 'POST', method: requestType,
body: body, body: body,
headers: { headers: {
'Accept': '*/*',
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'User-Agent': 'HumeniusTSRankingBackend/0.0.1', 'User-Agent': 'HumeniusTSRankingBackend/0.0.1',
'Authorization': `Bearer ${bearerToken}` 'Authorization': `Bearer ${bearerToken}`
@@ -65,37 +96,43 @@ export class SinusBotService {
}; };
} }
private consumeTunakillResponse(data: any): TableEntry[] | undefined { private createHttpException = (statusCode: HttpStatus, message?: string): HttpException => {
if (!(data !== null || data[0] !== null || data[0].data !== null)) { return new HttpException({
return undefined; status: statusCode,
error: message
}, statusCode);
}
private checkStatus = response => {
if (!response.ok) {
throw Error(response.statusText);
}
return response;
}
private sortByDescendingTime = (a: number, b: number) => {
if (a < b) {
return 1;
} }
const response = data[0].data; if (a > b) {
if (!(response.length > 0)) { return -1;
return undefined;
} }
return response return 0;
.map((user: TunakillUser) => { }
if (user.name === "Server Query Admin") { return null; }
return {
name: user.name,
rawTime: user.time,
onlineTime: new Date(user.time * 1000)
.toISOString()
.substr(11, 8)
};
})
.sort((a: any, b: any) => {
if (a.rawTime < b.rawTime) {
return -1;
}
if (a.rawTime > b.rawTime) { private humanizeTime = (seconds: number) => {
return 1; const d = Math.floor(seconds / (3600 * 24));
} const h = Math.floor(seconds % (3600 * 24) / 3600);
const m = Math.floor(seconds % 3600 / 60);
const s = Math.floor(seconds % 60);
return 0; return `${d}d ${h}h ${m}m ${s}s`;
}); }
private logResponse(res: any) {
console.log(res);
return res;
} }
} }