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 { AppController } from './app.controller';
import { AppService } from './app.service'; import { AppService } from './app.service';
import { config } from './config'; import { config } from './config';
...@@ -51,6 +51,7 @@ import { EloService } from './elo/elo.service'; ...@@ -51,6 +51,7 @@ import { EloService } from './elo/elo.service';
import { CardInfoService } from './card-info/card-info.service'; import { CardInfoService } from './card-info/card-info.service';
import { ServeStaticModule } from '@nestjs/serve-static'; import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path'; import { join } from 'path';
import { AthleticCheckerService } from './athletic-checker/athletic-checker.service';
const ygoproEntities = [YGOProDatabaseDatas, YGOProDatabaseTexts]; const ygoproEntities = [YGOProDatabaseDatas, YGOProDatabaseTexts];
const mycardEntities = [ const mycardEntities = [
...@@ -98,6 +99,7 @@ const mycardEntities = [ ...@@ -98,6 +99,7 @@ const mycardEntities = [
@Module({ @Module({
imports: [ imports: [
HttpModule,
ServeStaticModule.forRoot({ ServeStaticModule.forRoot({
rootPath: join(__dirname, '..', 'upload'), rootPath: join(__dirname, '..', 'upload'),
serveRoot: '/api/download', serveRoot: '/api/download',
...@@ -135,6 +137,7 @@ const mycardEntities = [ ...@@ -135,6 +137,7 @@ const mycardEntities = [
HttpResponseService, HttpResponseService,
EloService, EloService,
CardInfoService, CardInfoService,
AthleticCheckerService,
], ],
}) })
export class AppModule {} export class AppModule {}
...@@ -9,6 +9,7 @@ import { ...@@ -9,6 +9,7 @@ import {
Like, Like,
MoreThanOrEqual, MoreThanOrEqual,
SelectQueryBuilder, SelectQueryBuilder,
UpdateResult,
} from 'typeorm'; } from 'typeorm';
import { UserInfo } from './entities/mycard/UserInfo'; import { UserInfo } from './entities/mycard/UserInfo';
import Filter from 'bad-words-chinese'; import Filter from 'bad-words-chinese';
...@@ -35,6 +36,7 @@ import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity ...@@ -35,6 +36,7 @@ import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity
import { DeckInfoOrHistory } from './entities/mycard/DeckInfoOrHistory'; import { DeckInfoOrHistory } from './entities/mycard/DeckInfoOrHistory';
import { EloService } from './elo/elo.service'; import { EloService } from './elo/elo.service';
import { CardInfoService } from './card-info/card-info.service'; import { CardInfoService } from './card-info/card-info.service';
import { AthleticCheckerService } from './athletic-checker/athletic-checker.service';
const attrOffset = 1010; const attrOffset = 1010;
const raceOffset = 1020; const raceOffset = 1020;
...@@ -118,6 +120,7 @@ export class AppService { ...@@ -118,6 +120,7 @@ export class AppService {
private log: AppLogger, private log: AppLogger,
private eloService: EloService, private eloService: EloService,
private cardInfoService: CardInfoService, private cardInfoService: CardInfoService,
private athleticCheckerService: AthleticCheckerService,
) { ) {
this.log.setContext('ygopro-arena-revive'); this.log.setContext('ygopro-arena-revive');
this.chineseDirtyFilter = new Filter({ this.chineseDirtyFilter = new Filter({
...@@ -852,21 +855,53 @@ export class AppService { ...@@ -852,21 +855,53 @@ export class AppService {
} }
}); });
} else { } 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( const expResult = this.eloService.getExpScore(
userA.exp, userA.exp,
userB.exp, userB.exp,
userscoreA, userscoreA,
userscoreB, userscoreB,
); );
let noRecord = false;
if (userscoreA > userscoreB) { if (userscoreA > userscoreB) {
paramA['entertain_win'] = 1;
paramB['entertain_lose'] = 1;
winner = usernameA; 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) { if (userscoreA < userscoreB) {
paramA['entertain_lose'] = 1;
paramB['entertain_win'] = 1;
winner = usernameB; 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) { if (userscoreA === userscoreB) {
paramA['entertain_draw'] = 1; paramA['entertain_draw'] = 1;
...@@ -895,8 +930,10 @@ export class AppService { ...@@ -895,8 +930,10 @@ export class AppService {
await this.transaction(this.mcdb, async (db) => { await this.transaction(this.mcdb, async (db) => {
const repo = db.getRepository(BattleHistory); const repo = db.getRepository(BattleHistory);
try { try {
if (!noRecord) {
await repo.save(battleHistory);
}
await Promise.all([ await Promise.all([
repo.save(battleHistory),
db db
.createQueryBuilder() .createQueryBuilder()
.update(UserInfo) .update(UserInfo)
...@@ -1547,18 +1584,18 @@ export class AppService { ...@@ -1547,18 +1584,18 @@ export class AppService {
.from(UserInfo, 'tg') .from(UserInfo, 'tg')
.addFrom(UserInfo, 'rk') .addFrom(UserInfo, 'rk')
.where('tg.username = :username', { username }) .where('tg.username = :username', { username })
.andWhere('rk.pt >= tg.pt') .andWhere('rk.pt > tg.pt')
.getRawOne(); .getRawOne();
resultData.arena_rank = parseInt(arenaRank); resultData.arena_rank = parseInt(arenaRank) + 1;
const { expRank } = await this.mcdb.manager const { expRank } = await this.mcdb.manager
.createQueryBuilder() .createQueryBuilder()
.select('count(*)', 'expRank') .select('count(*)', 'expRank')
.from(UserInfo, 'tg') .from(UserInfo, 'tg')
.addFrom(UserInfo, 'rk') .addFrom(UserInfo, 'rk')
.where('tg.username = :username', { username }) .where('tg.username = :username', { username })
.andWhere('rk.exp >= tg.exp') .andWhere('rk.exp > tg.exp')
.getRawOne(); .getRawOne();
resultData.exp_rank = parseInt(expRank); resultData.exp_rank = parseInt(expRank) + 1;
return resultData; return resultData;
} }
async updateAds(body: any) { 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 { ...@@ -10,6 +10,17 @@ export interface Config {
enableSchedule: boolean; 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 = { export const config: Config = {
accessKey: process.env.ARENA_ACCESS_KEY, accessKey: process.env.ARENA_ACCESS_KEY,
host: process.env.PGHOST, 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