Commit 33727617 authored by nanahira's avatar nanahira

Merge branch 'master' of ../srvpro into koishi

parents 9e33544d 599ef254
......@@ -22,7 +22,7 @@ class Challonge {
return this.previous;
}
try {
const { data: { tournament } } = await axios_1.default.get(`https://api.challonge.com/v1/tournaments/${this.config.tournament_id}.json`, {
const { data: { tournament } } = await axios_1.default.get(`${this.config.challonge_url}/v1/tournaments/${this.config.tournament_id}.json`, {
params: {
api_key: this.config.api_key,
include_participants: 1,
......@@ -35,7 +35,7 @@ class Challonge {
return tournament;
}
catch (e) {
this.log.error(`Failed to get tournament ${this.config.tournament_id}`, e);
this.log.error(`Failed to get tournament ${this.config.tournament_id}: ${e}`);
return;
}
}
......@@ -47,7 +47,7 @@ class Challonge {
}
async putScore(matchId, match, retried = 0) {
try {
await axios_1.default.put(`https://api.challonge.com/v1/tournaments/${this.config.tournament_id}/matches/${matchId}.json`, {
await axios_1.default.put(`${this.config.challonge_url}/v1/tournaments/${this.config.tournament_id}/matches/${matchId}.json`, {
api_key: this.config.api_key,
match: match,
});
......@@ -56,7 +56,7 @@ class Challonge {
return true;
}
catch (e) {
this.log.error(`Failed to put score for match ${matchId}`, e);
this.log.error(`Failed to put score for match ${matchId}: ${e}`);
if (retried < 5) {
this.log.info(`Retrying match ${matchId}`);
return this.putScore(matchId, match, retried + 1);
......@@ -67,6 +67,34 @@ class Challonge {
}
}
}
async clearParticipants() {
try {
await axios_1.default.delete(`${this.config.challonge_url}/v1/tournaments/${this.config.tournament_id}/participants/clear.json`, {
params: {
api_key: this.config.api_key
},
validateStatus: () => true,
});
return true;
}
catch (e) {
this.log.error(`Failed to clear participants for tournament ${this.config.tournament_id}: ${e}`);
return false;
}
}
async uploadParticipants(participantNames) {
try {
await axios_1.default.post(`${this.config.challonge_url}/v1/tournaments/${this.config.tournament_id}/participants/bulk_add.json`, {
api_key: this.config.api_key,
participants: participantNames.map(name => ({ name })),
});
return true;
}
catch (e) {
this.log.error(`Failed to upload participants for tournament ${this.config.tournament_id}: ${e}`);
return false;
}
}
}
exports.Challonge = Challonge;
//# sourceMappingURL=challonge.js.map
\ No newline at end of file
......@@ -2,34 +2,15 @@ import axios from 'axios';
import { createLogger } from 'bunyan';
import moment, { Moment } from 'moment';
import PQueue from 'p-queue';
import _ from 'underscore';
export interface Match {
attachment_count?: any;
created_at: string;
group_id?: any;
has_attachment: boolean;
id: number;
identifier: string;
location?: any;
loser_id?: any;
state: 'pending' | 'open' | 'complete'; // pending: 还未开始,open: 进行中,complete: 已结束
player1_id: number;
player1_is_prereq_match_loser: boolean;
player1_prereq_match_id?: any;
player1_votes?: any;
player2_id: number;
player2_is_prereq_match_loser: boolean;
player2_prereq_match_id?: any;
player2_votes?: any;
round: number;
scheduled_time?: any;
started_at: string;
state: string;
tournament_id: number;
underway_at?: any;
updated_at: string;
winner_id?: any;
prerequisite_match_ids_csv: string;
scores_csv: string;
winner_id?: number | 'tie'; // 如果存在,则代表该比赛已经结束
scores_csv?: string; // 比分,2-1 这样的格式,请保证和上传的情况相同
}
export interface MatchWrapper {
......@@ -37,34 +18,9 @@ export interface MatchWrapper {
}
export interface Participant {
active: boolean;
checked_in_at?: any;
created_at: string;
final_rank?: any;
group_id?: any;
icon?: any;
id: number;
invitation_id?: any;
invite_email?: any;
misc?: any;
name: string;
on_waiting_list: boolean;
seed: number;
tournament_id: number;
updated_at: string;
challonge_username?: any;
challonge_email_address_verified?: any;
removable: boolean;
participatable_or_invitation_attached: boolean;
confirm_remove: boolean;
invitation_pending: boolean;
display_name_with_invitation_email_address: string;
email_hash?: any;
username?: any;
attached_participatable_portrait_url?: any;
can_check_in: boolean;
checked_in: boolean;
reactivatable: boolean;
deckbuf?: string;
}
export interface ParticipantWrapper {
......@@ -72,86 +28,27 @@ export interface ParticipantWrapper {
}
export interface Tournament {
accept_attachments: boolean;
allow_participant_match_reporting: boolean;
anonymous_voting: boolean;
category?: any;
check_in_duration?: any;
completed_at?: any;
created_at: string;
created_by_api: boolean;
credit_capped: boolean;
description: string;
game_id: number;
group_stages_enabled: boolean;
hide_forum: boolean;
hide_seeds: boolean;
hold_third_place_match: boolean;
id: number;
max_predictions_per_user: number;
name: string;
notify_users_when_matches_open: boolean;
notify_users_when_the_tournament_ends: boolean;
open_signup: boolean;
participants_count: number;
prediction_method: number;
predictions_opened_at?: any;
private: boolean;
progress_meter: number;
pts_for_bye: string;
pts_for_game_tie: string;
pts_for_game_win: string;
pts_for_match_tie: string;
pts_for_match_win: string;
quick_advance: boolean;
ranked_by: string;
require_score_agreement: boolean;
rr_pts_for_game_tie: string;
rr_pts_for_game_win: string;
rr_pts_for_match_tie: string;
rr_pts_for_match_win: string;
sequential_pairings: boolean;
show_rounds: boolean;
signup_cap?: any;
start_at?: any;
started_at: string;
started_checking_in_at?: any;
state: string;
swiss_rounds: number;
teams: boolean;
tie_breaks: string[];
tournament_type: string;
updated_at: string;
url: string;
description_source: string;
subdomain?: any;
full_challonge_url: string;
live_image_url: string;
sign_up_url?: any;
review_before_finalizing: boolean;
accepting_predictions: boolean;
participants_locked: boolean;
game_name: string;
participants_swappable: boolean;
team_convertable: boolean;
group_stages_were_started: boolean;
participants: ParticipantWrapper[];
matches: MatchWrapper[];
}
// GET /v1/tournaments/${tournament_id}.json?api_key=xxx&include_participants=1&include_matches=1 returns { tournament: Tournament }
export interface TournamentWrapper {
tournament: Tournament;
}
// PUT /v1/tournaments/${tournament_id}/matches/${match_id}.json { api_key: string, match: MatchPost } returns ANY
export interface MatchPost {
scores_csv: string;
winner_id: number;
scores_csv: string; // 比分。2-1 这样的格式。可能有特殊情况,比如 -9-1 或者 1--9,代表有一方掉线,或是加时赛胜利。也就是允许负数(从第一串数字的最后一个 - 区分)
winner_id?: number | 'tie'; // 上传比分的时候这个字段不一定存在。如果不存在的话代表比赛没打完(比如 1-0 就会上传,这时候换 side)
}
export interface ChallongeConfig {
api_key: string;
tournament_id: string;
cache_ttl: number;
challonge_url: string;
}
export class Challonge {
......@@ -169,7 +66,7 @@ export class Challonge {
}
try {
const { data: { tournament } } = await axios.get<TournamentWrapper>(
`https://api.challonge.com/v1/tournaments/${this.config.tournament_id}.json`,
`${this.config.challonge_url}/v1/tournaments/${this.config.tournament_id}.json`,
{
params: {
api_key: this.config.api_key,
......@@ -183,7 +80,7 @@ export class Challonge {
this.previousTime = moment();
return tournament;
} catch (e) {
this.log.error(`Failed to get tournament ${this.config.tournament_id}`, e);
this.log.error(`Failed to get tournament ${this.config.tournament_id}: ${e}`);
return;
}
}
......@@ -198,7 +95,7 @@ export class Challonge {
async putScore(matchId: number, match: MatchPost, retried = 0) {
try {
await axios.put(
`https://api.challonge.com/v1/tournaments/${this.config.tournament_id}/matches/${matchId}.json`,
`${this.config.challonge_url}/v1/tournaments/${this.config.tournament_id}/matches/${matchId}.json`,
{
api_key: this.config.api_key,
match: match,
......@@ -208,7 +105,7 @@ export class Challonge {
this.previousTime = undefined;
return true;
} catch (e) {
this.log.error(`Failed to put score for match ${matchId}`, e);
this.log.error(`Failed to put score for match ${matchId}: ${e}`);
if (retried < 5) {
this.log.info(`Retrying match ${matchId}`);
return this.putScore(matchId, match, retried + 1);
......@@ -218,4 +115,34 @@ export class Challonge {
}
}
}
// DELETE /v1/tournaments/${tournament_id}/participants/clear.json?api_key=xxx returns ANY
async clearParticipants() {
try {
await axios.delete(`${this.config.challonge_url}/v1/tournaments/${this.config.tournament_id}/participants/clear.json`, {
params: {
api_key: this.config.api_key
},
validateStatus: () => true,
})
return true;
} catch (e) {
this.log.error(`Failed to clear participants for tournament ${this.config.tournament_id}: ${e}`);
return false;
}
}
// POST /v1/tournaments/${tournament_id}/participants/bulk_add.json { api_key: string, participants: { name: string }[] } returns ANY
async uploadParticipants(participantNames: string[]) {
try {
await axios.post(`${this.config.challonge_url}/v1/tournaments/${this.config.tournament_id}/participants/bulk_add.json`, {
api_key: this.config.api_key,
participants: participantNames.map(name => ({ name })),
});
return true;
} catch (e) {
this.log.error(`Failed to upload participants for tournament ${this.config.tournament_id}: ${e}`);
return false;
}
}
}
......@@ -726,12 +726,12 @@ class DataManager {
newScore.name = name;
return await this.saveRandomDuelScore(newScore);
}
async getRandomDuelScoreDisplay(name) {
async getRandomDuelScoreDisplay(name, displayName) {
const score = await this.getRandomDuelScore(name);
if (!score) {
return `${name.split("$")[0]} \${random_score_blank}`;
return `${displayName} \${random_score_blank}`;
}
return score.getScoreText();
return score.getScoreText(displayName);
}
async randomDuelPlayerWin(name) {
const score = await this.getOrCreateRandomDuelScore(name);
......
......@@ -723,12 +723,12 @@ export class DataManager {
newScore.name = name;
return await this.saveRandomDuelScore(newScore);
}
async getRandomDuelScoreDisplay(name: string) {
async getRandomDuelScoreDisplay(name: string, displayName: string) {
const score = await this.getRandomDuelScore(name);
if(!score) {
return `${name.split("$")[0]} \${random_score_blank}`;
return `${displayName} \${random_score_blank}`;
}
return score.getScoreText();
return score.getScoreText(displayName);
}
async randomDuelPlayerWin(name: string) {
const score = await this.getOrCreateRandomDuelScore(name);
......
......@@ -33,8 +33,7 @@ let RandomDuelScore = class RandomDuelScore extends CreateAndUpdateTimeBase_1.Cr
++this.fleeCount;
this.lose();
}
getScoreText() {
const displayName = this.getDisplayName();
getScoreText(displayName) {
const total = this.winCount + this.loseCount;
if (this.winCount < 2 && total < 3) {
return `${displayName} \${random_score_not_enough}`;
......
......@@ -40,8 +40,7 @@ export class RandomDuelScore extends CreateAndUpdateTimeBase {
this.lose();
}
getScoreText() {
const displayName = this.getDisplayName();
getScoreText(displayName: string) {
const total = this.winCount + this.loseCount;
if (this.winCount < 2 && total < 3) {
return `${displayName} \${random_score_not_enough}`;
......
......@@ -90,6 +90,7 @@
"record_match_scores": false,
"post_match_scores": false,
"post_match_accesskey": "123456",
"disable_chat": false,
"blank_pass_modes": {
"S": true,
"M": true,
......@@ -163,7 +164,8 @@
"cache_ttl": 60000,
"no_match_mode": false,
"api_key": "123",
"tournament_id": "456"
"tournament_id": "456",
"challonge_url": "https://api.challonge.com"
},
"deck_log": {
"enabled": false,
......
......@@ -209,6 +209,7 @@
"refresh_failed": "Refresh field failed.",
"banned_athletic_deck_part1": "Entertainment Mode does not allow top ",
"banned_athletic_deck_part2": " popular meta decks. Please change your deck.",
"chat_disabled": "Chat is disabled in this room.",
"using_athletic_deck": " is using a competitive deck."
},
"es-es": {
......@@ -567,6 +568,7 @@
"refresh_fail": "刷新场面失败。",
"banned_athletic_deck_part1": "娱乐匹配中禁止使用使用数前",
"banned_athletic_deck_part2": "的竞技卡组。请更换卡组。",
"chat_disabled": "本房间禁止聊天。",
"using_athletic_deck": " 正在使用竞技卡组。"
},
"ko-kr": {
......
......@@ -78,7 +78,8 @@
],
"STOC_HS_PlayerEnter": [
{"name": "name", "type": "unsigned short", "length": 20, "encoding": "UTF-16LE"},
{"name": "pos", "type": "unsigned char"}
{"name": "pos", "type": "unsigned char"},
{"name": "padding", "type": "unsigned char"}
],
"STOC_HS_PlayerChange": [
{"name": "status", "type": "unsigned char"}
......
This diff is collapsed.
This diff is collapsed.
......@@ -23,6 +23,8 @@ const auth = require('./ygopro-auth.js');
const settings = loadJSON('./config/config.json');
config = settings.modules.tournament_mode;
challonge_config = settings.modules.challonge;
const { Challonge } = require('./challonge');
const challonge = new Challonge(challonge_config);
ssl_config = settings.modules.http.ssl;
const _async = require("async");
......@@ -73,7 +75,7 @@ const readDeck = async function(deck_name, deck_full_path) {
const deck={};
deck.name=deck_name;
deck_text = await fs.promises.readFile(deck_full_path, { encoding: "ASCII" });
deck_array = deck_text.split("\n");
deck_array = deck_text.split(/\r?\n/);
deck.main = [];
deck.extra = [];
deck.side = [];
......@@ -87,7 +89,7 @@ const readDeck = async function(deck_name, deck_full_path) {
current_deck = deck.side;
}
card = parseInt(line);
if (!isNaN(card)) {
if (!isNaN(card) && !line.endsWith("#")) {
current_deck.push(card);
}
}
......@@ -156,19 +158,11 @@ const UploadToChallonge = async function () {
sendResponse("读取玩家列表完毕,共有" + player_list.length + "名玩家。");
try {
sendResponse("开始清空 Challonge 玩家列表。");
await axios.delete(`https://api.challonge.com/v1/tournaments/${challonge_config.tournament_id}/participants/clear.json`, {
params: {
api_key: challonge_config.api_key
},
validateStatus: () => true,
});
await challonge.clearParticipants();
sendResponse("开始上传玩家列表至 Challonge。");
for (const chunk of _.chunk(player_list, 10)) {
sendResponse(`开始上传玩家 ${chunk.join(', ')} 至 Challonge。`);
await axios.post(`https://api.challonge.com/v1/tournaments/${challonge_config.tournament_id}/participants/bulk_add.json`, {
api_key: challonge_config.api_key,
participants: chunk.map(name => ({ name })),
});
await challonge.uploadParticipants(chunk);
}
sendResponse("玩家列表上传完成。");
} catch (e) {
......
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