Commit eb178130 authored by nanahira's avatar nanahira

will not record entertain with competitive deck victory

parent 9b9becee
Pipeline #3427 passed with stages
in 3 minutes and 12 seconds
import { Module } from '@nestjs/common';
import { HttpModule, Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { config } from './config';
......@@ -51,6 +51,7 @@ import { EloService } from './elo/elo.service';
import { CardInfoService } from './card-info/card-info.service';
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';
import { AthleticCheckerService } from './athletic-checker/athletic-checker.service';
const ygoproEntities = [YGOProDatabaseDatas, YGOProDatabaseTexts];
const mycardEntities = [
......@@ -98,6 +99,7 @@ const mycardEntities = [
@Module({
imports: [
HttpModule,
ServeStaticModule.forRoot({
rootPath: join(__dirname, '..', 'upload'),
serveRoot: '/api/download',
......@@ -135,6 +137,7 @@ const mycardEntities = [
HttpResponseService,
EloService,
CardInfoService,
AthleticCheckerService,
],
})
export class AppModule {}
......@@ -9,6 +9,7 @@ import {
Like,
MoreThanOrEqual,
SelectQueryBuilder,
UpdateResult,
} from 'typeorm';
import { UserInfo } from './entities/mycard/UserInfo';
import Filter from 'bad-words-chinese';
......@@ -35,6 +36,7 @@ import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity
import { DeckInfoOrHistory } from './entities/mycard/DeckInfoOrHistory';
import { EloService } from './elo/elo.service';
import { CardInfoService } from './card-info/card-info.service';
import { AthleticCheckerService } from './athletic-checker/athletic-checker.service';
const attrOffset = 1010;
const raceOffset = 1020;
......@@ -118,6 +120,7 @@ export class AppService {
private log: AppLogger,
private eloService: EloService,
private cardInfoService: CardInfoService,
private athleticCheckerService: AthleticCheckerService,
) {
this.log.setContext('ygopro-arena-revive');
this.chineseDirtyFilter = new Filter({
......@@ -852,21 +855,53 @@ export class AppService {
}
});
} else {
const [isAthleticA, isAthleticB] = await Promise.all([
this.athleticCheckerService.checkAthletic(deckA),
this.athleticCheckerService.checkAthletic(deckB),
]);
this.log.error(deckA);
this.log.error(isAthleticA);
let athleticResultOk = true;
for (const result of [isAthleticA, isAthleticB]) {
if (!result.success) {
athleticResultOk = false;
this.log.error(`Failed to get athletic result: ${result.message}`);
}
}
const expResult = this.eloService.getExpScore(
userA.exp,
userB.exp,
userscoreA,
userscoreB,
);
let noRecord = false;
if (userscoreA > userscoreB) {
paramA['entertain_win'] = 1;
paramB['entertain_lose'] = 1;
winner = usernameA;
if (athleticResultOk && isAthleticA.athletic && !isAthleticB.athletic) {
this.log.log(
`Will not record this match because ${usernameA}'s ${deckA} is competitive while ${usernameB}'s ${deckB} isn't.`,
);
noRecord = true;
paramA['entertain_all'] = 0;
paramB['entertain_all'] = 0;
} else {
paramA['entertain_win'] = 1;
paramB['entertain_lose'] = 1;
}
}
if (userscoreA < userscoreB) {
paramA['entertain_lose'] = 1;
paramB['entertain_win'] = 1;
winner = usernameB;
if (athleticResultOk && isAthleticB.athletic && !isAthleticA.athletic) {
this.log.log(
`Will not record this match because ${usernameB}'s ${deckB} is competitive while ${usernameA}'s ${deckA} isn't.`,
);
noRecord = true;
paramA['entertain_all'] = 0;
paramB['entertain_all'] = 0;
} else {
paramA['entertain_lose'] = 1;
paramB['entertain_win'] = 1;
}
}
if (userscoreA === userscoreB) {
paramA['entertain_draw'] = 1;
......@@ -895,8 +930,10 @@ export class AppService {
await this.transaction(this.mcdb, async (db) => {
const repo = db.getRepository(BattleHistory);
try {
if (!noRecord) {
await repo.save(battleHistory);
}
await Promise.all([
repo.save(battleHistory),
db
.createQueryBuilder()
.update(UserInfo)
......@@ -1547,18 +1584,18 @@ export class AppService {
.from(UserInfo, 'tg')
.addFrom(UserInfo, 'rk')
.where('tg.username = :username', { username })
.andWhere('rk.pt >= tg.pt')
.andWhere('rk.pt > tg.pt')
.getRawOne();
resultData.arena_rank = parseInt(arenaRank);
resultData.arena_rank = parseInt(arenaRank) + 1;
const { expRank } = await this.mcdb.manager
.createQueryBuilder()
.select('count(*)', 'expRank')
.from(UserInfo, 'tg')
.addFrom(UserInfo, 'rk')
.where('tg.username = :username', { username })
.andWhere('rk.exp >= tg.exp')
.andWhere('rk.exp > tg.exp')
.getRawOne();
resultData.exp_rank = parseInt(expRank);
resultData.exp_rank = parseInt(expRank) + 1;
return resultData;
}
async updateAds(body: any) {
......
import { Test, TestingModule } from '@nestjs/testing';
import { AthleticCheckerService } from './athletic-checker.service';
describe('AthleticCheckerService', () => {
let service: AthleticCheckerService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [AthleticCheckerService],
}).compile();
service = module.get<AthleticCheckerService>(AthleticCheckerService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
import { HttpService, Injectable } from '@nestjs/common';
import moment, { Moment } from 'moment';
import axios from 'axios';
import { athleticCheckConfig } from '../config';
interface AthleticDecksReturnData {
name: string;
}
interface AthleticCheckReturnMessage {
success: boolean;
athletic?: boolean;
message: string;
}
@Injectable()
export class AthleticCheckerService {
athleticDeckCache: string[];
lastAthleticDeckFetchTime: Moment;
constructor(private readonly httpService: HttpService) {}
private async getAthleticDecks(): Promise<string[]> {
if (
this.athleticDeckCache &&
moment().diff(this.lastAthleticDeckFetchTime, 'seconds') <
athleticCheckConfig.ttl
) {
return this.athleticDeckCache;
}
const { data } = await this.httpService
.get<AthleticDecksReturnData[]>(athleticCheckConfig.rankURL, {
timeout: 10000,
responseType: 'json',
params: athleticCheckConfig.athleticFetchParams,
})
.toPromise();
const athleticDecks = data
.slice(0, athleticCheckConfig.rankCount)
.map((m) => m.name);
this.athleticDeckCache = athleticDecks;
this.lastAthleticDeckFetchTime = moment();
return athleticDecks;
}
async checkAthletic(deckType: string): Promise<AthleticCheckReturnMessage> {
try {
const athleticDecks = await this.getAthleticDecks();
const athletic = athleticDecks.includes(deckType);
return { success: true, athletic, message: null };
} catch (e) {
return { success: false, message: e.toString() };
}
}
}
......@@ -10,6 +10,17 @@ export interface Config {
enableSchedule: boolean;
}
export const athleticCheckConfig = {
rankURL: 'https://api.mycard.moe/ygopro/analytics/deck/type',
identifierURL: 'https://api.mycard.moe/ygopro/identifier/production',
athleticFetchParams: {
type: 'week',
source: 'mycard-athletic',
},
rankCount: 10,
ttl: 600,
};
export const config: Config = {
accessKey: process.env.ARENA_ACCESS_KEY,
host: process.env.PGHOST,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment