Add error handling, fix authentication and fix online time display
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user