Commit 181dcaa8 authored by nanahira's avatar nanahira

Merge branch 'database' into mc

parents 02164f83 6057583f
Pipeline #1248 passed with stages
in 10 minutes and 3 seconds
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
jsconfig.json jsconfig.json
coffeelint.json coffeelint.json
.vscode/ .vscode/
.idea
password.json password.json
config.*.json config.*.json
...@@ -13,6 +14,7 @@ config.user.bak ...@@ -13,6 +14,7 @@ config.user.bak
/windbot /windbot
/decks /decks
/decks_save* /decks_save*
/deck_log
/replays /replays
/node_modules /node_modules
/ssl /ssl
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
jsconfig.json jsconfig.json
coffeelint.json coffeelint.json
.vscode/ .vscode/
.idea
password.json password.json
config.*.json config.*.json
...@@ -13,6 +14,7 @@ config.user.bak ...@@ -13,6 +14,7 @@ config.user.bak
/windbot /windbot
/decks /decks
/decks_save* /decks_save*
/deck_log
/replays /replays
/node_modules /node_modules
/ssl /ssl
......
# Dockerfile for SRVPro # Dockerfile for SRVPro
FROM node:12-buster-slim FROM node:14-buster-slim
RUN npm install -g pm2 RUN npm install -g pm2
# apt # apt
RUN apt update && \ RUN apt update && \
env DEBIAN_FRONTEND=noninteractive apt install -y wget git build-essential libevent-dev libsqlite3-dev mono-complete p7zip-full redis-server python3 liblua5.3-dev && \ env DEBIAN_FRONTEND=noninteractive apt install -y wget git build-essential libevent-dev libsqlite3-dev mono-complete p7zip-full python3 liblua5.3-dev && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
# srvpro # srvpro
......
# Dockerfile for SRVPro Lite # Dockerfile for SRVPro Lite
FROM node:12-buster-slim FROM node:14-buster-slim
# apt # apt
RUN apt update && \ RUN apt update && \
......
...@@ -117,7 +117,7 @@ class Replay ...@@ -117,7 +117,7 @@ class Replay
@header == null ? false : @header.isTag @header == null ? false : @header.isTag
@fromFile: (filePath) -> @fromFile: (filePath) ->
Replay.fromBuffer fs.readFileSync filePath Replay.fromBuffer await fs.promises.readFile filePath
@fromBuffer: (buffer) -> @fromBuffer: (buffer) ->
reader = new ReplayReader buffer reader = new ReplayReader buffer
......
...@@ -175,8 +175,8 @@ ...@@ -175,8 +175,8 @@
}; };
} }
static fromFile(filePath) { static async fromFile(filePath) {
return Replay.fromBuffer(fs.readFileSync(filePath)); return Replay.fromBuffer((await fs.promises.readFile(filePath)));
} }
static fromBuffer(buffer) { static fromBuffer(buffer) {
......
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DataManager = void 0;
const moment_1 = __importDefault(require("moment"));
const typeorm_1 = require("typeorm");
const CloudReplay_1 = require("./entities/CloudReplay");
const CloudReplayPlayer_1 = require("./entities/CloudReplayPlayer");
const Ban_1 = require("./entities/Ban");
const RandomDuelBan_1 = require("./entities/RandomDuelBan");
const underscore_1 = __importDefault(require("underscore"));
const DuelLog_1 = require("./entities/DuelLog");
const DuelLogPlayer_1 = require("./entities/DuelLogPlayer");
const User_1 = require("./entities/User");
const RandomDuelScore_1 = require("./entities/RandomDuelScore");
class DataManager {
constructor(config, log) {
this.config = config;
this.ready = false;
this.log = log;
}
async transaction(fun) {
const runner = await this.db.createQueryRunner();
await runner.connect();
await runner.startTransaction();
let result = false;
try {
result = await fun(runner.manager);
}
catch (e) {
result = false;
this.log.warn(`Failed running transaction: ${e.toString()}`);
}
if (result) {
await runner.commitTransaction();
}
else {
await runner.rollbackTransaction();
}
}
async init() {
this.db = await typeorm_1.createConnection({
type: "mysql",
synchronize: true,
entities: ["./data-manager/entities/*.js"],
...this.config
});
this.ready = true;
}
async getCloudReplaysFromKey(key) {
try {
const replays = await this.db.createQueryBuilder(CloudReplay_1.CloudReplay, "replay")
.where("exists (select id from cloud_replay_player where cloud_replay_player.cloudReplayId = replay.id and cloud_replay_player.key = :key)", { key })
.orderBy("replay.date", "DESC")
.limit(10)
.leftJoinAndSelect("replay.players", "player")
.getMany();
return replays;
}
catch (e) {
this.log.warn(`Failed to load replay of ${key}: ${e.toString()}`);
return [];
}
}
async getCloudReplayFromId(id) {
try {
return await this.db.getRepository(CloudReplay_1.CloudReplay).findOne(id, { relations: ["players"] });
}
catch (e) {
this.log.warn(`Failed to load replay R#${id}: ${e.toString()}`);
return null;
}
}
async getRandomCloudReplay() {
try {
const [minQuery, maxQuery] = await Promise.all(["min", "max"].map(minOrMax => this.db.createQueryBuilder()
.select(`${minOrMax}(id)`, "value")
.from(CloudReplay_1.CloudReplay, "replay")
.getRawOne()));
if (!minQuery || !maxQuery) {
return null;
}
const [minId, maxId] = [minQuery, maxQuery].map(query => parseInt(query.value));
const targetId = Math.floor((maxId - minId) * Math.random()) + minId;
return await this.db.createQueryBuilder(CloudReplay_1.CloudReplay, "replay")
.where("replay.id >= :targetId", { targetId })
.orderBy("replay.id", "ASC")
.limit(4) //there may be 4 players
.leftJoinAndSelect("replay.players", "player")
.getOne();
}
catch (e) {
this.log.warn(`Failed to load random replay: ${e.toString()}`);
return null;
}
}
async saveCloudReplay(id, buffer, playerInfos) {
const replay = new CloudReplay_1.CloudReplay();
replay.id = id;
replay.fromBuffer(buffer);
replay.date = moment_1.default().toDate();
const players = playerInfos.map(p => {
const player = CloudReplayPlayer_1.CloudReplayPlayer.fromPlayerInfo(p);
return player;
});
await this.transaction(async (mdb) => {
try {
const nreplay = await mdb.save(replay);
for (let player of players) {
player.cloudReplay = nreplay;
}
await mdb.save(players);
return true;
}
catch (e) {
this.log.warn(`Failed to save replay R#${replay.id}: ${e.toString()}`);
return false;
}
});
}
async checkBan(field, value) {
const banQuery = {};
banQuery[field] = value;
try {
return await this.db.getRepository(Ban_1.Ban).findOne(banQuery);
}
catch (e) {
this.log.warn(`Failed to load ban ${field} ${value}: ${e.toString()}`);
return null;
}
}
async checkBanWithNameAndIP(name, ip) {
try {
return await this.db.getRepository(Ban_1.Ban).findOne({ name, ip });
}
catch (e) {
this.log.warn(`Failed to load ban ${name} ${ip}: ${e.toString()}`);
return null;
}
}
getBan(name, ip) {
const ban = new Ban_1.Ban();
ban.ip = ip;
ban.name = name;
return ban;
}
async banPlayer(ban) {
try {
const repo = this.db.getRepository(Ban_1.Ban);
if (await repo.findOne({
ip: ban.ip,
name: ban.name
})) {
return;
}
return await repo.save(ban);
}
catch (e) {
this.log.warn(`Failed to update ban ${JSON.stringify(ban)}: ${e.toString()}`);
return null;
}
}
async getRandomDuelBan(ip) {
const repo = this.db.getRepository(RandomDuelBan_1.RandomDuelBan);
try {
const ban = await repo.findOne(ip);
//console.log(ip, ban);
return ban;
}
catch (e) {
this.log.warn(`Failed to fetch random duel ban ${ip}: ${e.toString()}`);
return null;
}
}
async updateRandomDuelBan(ban) {
const repo = this.db.getRepository(RandomDuelBan_1.RandomDuelBan);
try {
await repo.save(ban);
}
catch (e) {
this.log.warn(`Failed to update random duel ban ${ban.ip}: ${e.toString()}`);
}
}
async randomDuelBanPlayer(ip, reason, countadd) {
const count = countadd || 1;
const repo = this.db.getRepository(RandomDuelBan_1.RandomDuelBan);
try {
let ban = await repo.findOne(ip);
if (ban) {
ban.count += count;
const banTime = ban.count > 3 ? Math.pow(2, ban.count - 3) * 2 : 0;
const banDate = moment_1.default(ban.time);
if (moment_1.default().isAfter(banDate)) {
ban.time = moment_1.default().add(banTime, 'm').toDate();
}
else {
ban.time = moment_1.default(banDate).add(banTime, 'm').toDate();
}
if (!underscore_1.default.contains(ban.reasons, reason)) {
ban.reasons.push(reason);
}
ban.needTip = 1;
}
else {
ban = new RandomDuelBan_1.RandomDuelBan();
ban.ip = ip;
ban.time = moment_1.default().toDate();
ban.count = count;
ban.reasons = [reason];
ban.needTip = 1;
}
return await repo.save(ban);
}
catch (e) {
this.log.warn(`Failed to update random duel ban ${ip}: ${e.toString()}`);
return null;
}
}
async getAllDuelLogs() {
const repo = this.db.getRepository(DuelLog_1.DuelLog);
try {
const allDuelLogs = await repo.find({ relations: ["players"] });
return allDuelLogs;
}
catch (e) {
this.log.warn(`Failed to fetch duel logs: ${e.toString()}`);
return [];
}
}
async getDuelLogFromId(id) {
const repo = this.db.getRepository(DuelLog_1.DuelLog);
try {
const duelLog = await repo.findOne(id, { relations: ["players"] });
return duelLog;
}
catch (e) {
this.log.warn(`Failed to fetch duel logs: ${e.toString()}`);
return null;
}
}
async getDuelLogFromRecoverSearch(realName) {
const repo = this.db.getRepository(DuelLog_1.DuelLog);
try {
const duelLogs = await repo.createQueryBuilder("duelLog")
.where("startDeckBuffer is not null and currentDeckBuffer is not null and roomMode != 2 and exists (select id from duel_log_player where duel_log_player.duelLogId = duelLog.id and duel_log_player.realName = :realName)", { realName })
.orderBy("duelLog.id", "DESC")
.limit(10)
.leftJoinAndSelect("duelLog.players", "player")
.getMany();
return duelLogs;
}
catch (e) {
this.log.warn(`Failed to fetch duel logs: ${e.toString()}`);
return null;
}
}
async getDuelLogJSON(tournamentModeSettings) {
const allDuelLogs = await this.getAllDuelLogs();
return allDuelLogs.map(duelLog => duelLog.getViewJSON(tournamentModeSettings));
}
async getAllReplayFilenames() {
const allDuelLogs = await this.getAllDuelLogs();
return allDuelLogs.map(duelLog => duelLog.replayFileName);
}
async clearDuelLog() {
const runner = this.db.createQueryRunner();
try {
await runner.connect();
await runner.startTransaction();
await runner.query("SET FOREIGN_KEY_CHECKS = 0; ");
await runner.clearTable("duel_log_player");
await runner.clearTable("duel_log");
await runner.query("SET FOREIGN_KEY_CHECKS = 1; ");
await runner.commitTransaction();
}
catch (e) {
await runner.rollbackTransaction();
this.log.warn(`Failed to clear duel logs: ${e.toString()}`);
}
}
async saveDuelLog(name, roomId, cloudReplayId, replayFilename, roomMode, duelCount, playerInfos) {
const duelLog = new DuelLog_1.DuelLog();
duelLog.name = name;
duelLog.time = moment_1.default().toDate();
duelLog.roomId = roomId;
duelLog.cloudReplayId = cloudReplayId;
duelLog.replayFileName = replayFilename;
duelLog.roomMode = roomMode;
duelLog.duelCount = duelCount;
const players = playerInfos.map(p => DuelLogPlayer_1.DuelLogPlayer.fromDuelLogPlayerInfo(p));
await this.transaction(async (mdb) => {
try {
const savedDuelLog = await mdb.save(duelLog);
for (let player of players) {
player.duelLog = savedDuelLog;
}
await mdb.save(players);
return true;
}
catch (e) {
this.log.warn(`Failed to save duel log ${name}: ${e.toString()}`);
return false;
}
});
}
async getUser(key) {
const repo = this.db.getRepository(User_1.User);
try {
const user = await repo.findOne(key);
return user;
}
catch (e) {
this.log.warn(`Failed to fetch user: ${e.toString()}`);
return null;
}
}
async getOrCreateUser(key) {
const user = await this.getUser(key);
if (user) {
return user;
}
const newUser = new User_1.User();
newUser.key = key;
return await this.saveUser(newUser);
}
async saveUser(user) {
const repo = this.db.getRepository(User_1.User);
try {
return await repo.save(user);
}
catch (e) {
this.log.warn(`Failed to save user: ${e.toString()}`);
return null;
}
}
async getUserChatColor(key) {
const user = await this.getUser(key);
return user ? user.chatColor : null;
}
async setUserChatColor(key, color) {
let user = await this.getOrCreateUser(key);
user.chatColor = color;
return await this.saveUser(user);
}
async migrateChatColors(data) {
await this.transaction(async (mdb) => {
try {
const users = [];
for (let key in data) {
const chatColor = data[key];
let user = await mdb.findOne(User_1.User, key);
if (!user) {
user = new User_1.User();
user.key = key;
}
user.chatColor = chatColor;
users.push(user);
}
await mdb.save(users);
return true;
}
catch (e) {
this.log.warn(`Failed to migrate chat color data: ${e.toString()}`);
return false;
}
});
}
async getRandomDuelScore(name) {
const repo = this.db.getRepository(RandomDuelScore_1.RandomDuelScore);
try {
const score = await repo.findOne(name);
return score;
}
catch (e) {
this.log.warn(`Failed to fetch random duel score ${name}: ${e.toString()}`);
return null;
}
}
async saveRandomDuelScore(score) {
const repo = this.db.getRepository(RandomDuelScore_1.RandomDuelScore);
try {
return await repo.save(score);
}
catch (e) {
this.log.warn(`Failed to save random duel score: ${e.toString()}`);
return null;
}
}
async getOrCreateRandomDuelScore(name) {
const score = await this.getRandomDuelScore(name);
if (score) {
return score;
}
const newScore = new RandomDuelScore_1.RandomDuelScore();
newScore.name = name;
return await this.saveRandomDuelScore(newScore);
}
async getRandomDuelScoreDisplay(name) {
const score = await this.getRandomDuelScore(name);
if (!score) {
return `${name.split("$")[0]} \${random_score_blank}`;
}
return score.getScoreText();
}
async randomDuelPlayerWin(name) {
const score = await this.getOrCreateRandomDuelScore(name);
score.win();
await this.saveRandomDuelScore(score);
}
async randomDuelPlayerLose(name) {
const score = await this.getOrCreateRandomDuelScore(name);
score.lose();
await this.saveRandomDuelScore(score);
}
async randomDuelPlayerFlee(name) {
const score = await this.getOrCreateRandomDuelScore(name);
score.flee();
await this.saveRandomDuelScore(score);
}
async getRandomScoreTop10() {
try {
const scores = await this.db.getRepository(RandomDuelScore_1.RandomDuelScore)
.createQueryBuilder("score")
.orderBy("score.win", "DESC")
.addOrderBy("score.lose", "ASC")
.addOrderBy("score.flee", "ASC")
.limit(10)
.getMany();
return scores.map(score => [score.getDisplayName(), {
win: score.winCount,
lose: score.loseCount,
flee: score.fleeCount,
combo: score.winCombo
}]);
}
catch (e) {
this.log.warn(`Failed to fetch random duel score ${name}: ${e.toString()}`);
return [];
}
}
}
exports.DataManager = DataManager;
//# sourceMappingURL=DataManager.js.map
\ No newline at end of file
import moment from "moment";
import bunyan from "bunyan";
import {Connection, ConnectionOptions, createConnection, EntityManager} from "typeorm";
import {CloudReplay} from "./entities/CloudReplay";
import {CloudReplayPlayer} from "./entities/CloudReplayPlayer";
import {Ban} from "./entities/Ban";
import {RandomDuelBan} from "./entities/RandomDuelBan";
import _ from "underscore";
import {DuelLog} from "./entities/DuelLog";
import {Deck} from "./DeckEncoder";
import {DuelLogPlayer} from "./entities/DuelLogPlayer";
import {User} from "./entities/User";
import {RandomDuelScore} from "./entities/RandomDuelScore";
interface BasePlayerInfo {
name: string;
pos: number
}
export interface CloudReplayPlayerInfo extends BasePlayerInfo {
key: string;
}
export interface DuelLogPlayerInfo extends BasePlayerInfo {
realName: string;
startDeckBuffer: Buffer;
deck: Deck;
isFirst: boolean;
winner: boolean;
ip: string;
score: number;
lp: number;
cardCount: number;
}
export class DataManager {
config: ConnectionOptions;
ready: boolean;
db: Connection;
log: bunyan;
constructor(config: ConnectionOptions, log: bunyan) {
this.config = config;
this.ready = false;
this.log = log;
}
private async transaction(fun: (mdb: EntityManager) => Promise<boolean>) {
const runner = await this.db.createQueryRunner();
await runner.connect();
await runner.startTransaction();
let result = false;
try {
result = await fun(runner.manager);
} catch(e) {
result = false;
this.log.warn(`Failed running transaction: ${e.toString()}`)
}
if(result) {
await runner.commitTransaction();
} else {
await runner.rollbackTransaction();
}
}
async init() {
this.db = await createConnection({
type: "mysql",
synchronize: true,
entities: ["./data-manager/entities/*.js"],
...this.config
});
this.ready = true;
}
async getCloudReplaysFromKey(key: string) {
try {
const replays = await this.db.createQueryBuilder(CloudReplay, "replay")
.where("exists (select id from cloud_replay_player where cloud_replay_player.cloudReplayId = replay.id and cloud_replay_player.key = :key)", { key })
.orderBy("replay.date", "DESC")
.limit(10)
.leftJoinAndSelect("replay.players", "player")
.getMany();
return replays;
} catch (e) {
this.log.warn(`Failed to load replay of ${key}: ${e.toString()}`);
return [];
}
}
async getCloudReplayFromId(id: number) {
try {
return await this.db.getRepository(CloudReplay).findOne(id, { relations: ["players"] });
} catch (e) {
this.log.warn(`Failed to load replay R#${id}: ${e.toString()}`);
return null;
}
}
async getRandomCloudReplay() {
try {
const [minQuery, maxQuery] = await Promise.all(["min", "max"].map(minOrMax => this.db.createQueryBuilder()
.select(`${minOrMax}(id)`, "value")
.from(CloudReplay, "replay")
.getRawOne()
));
if(!minQuery || !maxQuery) {
return null;
}
const [minId, maxId] = [minQuery, maxQuery].map(query => parseInt(query.value));
const targetId = Math.floor((maxId - minId) * Math.random()) + minId;
return await this.db.createQueryBuilder(CloudReplay, "replay")
.where("replay.id >= :targetId", {targetId})
.orderBy("replay.id", "ASC")
.limit(4) //there may be 4 players
.leftJoinAndSelect("replay.players", "player")
.getOne();
} catch (e) {
this.log.warn(`Failed to load random replay: ${e.toString()}`);
return null;
}
}
async saveCloudReplay(id: number, buffer: Buffer, playerInfos: CloudReplayPlayerInfo[]) {
const replay = new CloudReplay();
replay.id = id;
replay.fromBuffer(buffer);
replay.date = moment().toDate();
const players = playerInfos.map(p => {
const player = CloudReplayPlayer.fromPlayerInfo(p);
return player;
});
await this.transaction(async (mdb) => {
try {
const nreplay = await mdb.save(replay);
for (let player of players) {
player.cloudReplay = nreplay;
}
await mdb.save(players);
return true;
} catch (e) {
this.log.warn(`Failed to save replay R#${replay.id}: ${e.toString()}`);
return false;
}
});
}
async checkBan(field: string, value: string) {
const banQuery: any = {};
banQuery[field] = value;
try {
return await this.db.getRepository(Ban).findOne(banQuery);
} catch (e) {
this.log.warn(`Failed to load ban ${field} ${value}: ${e.toString()}`);
return null;
}
}
async checkBanWithNameAndIP(name: string, ip: string) {
try {
return await this.db.getRepository(Ban).findOne({ name, ip });
} catch (e) {
this.log.warn(`Failed to load ban ${name} ${ip}: ${e.toString()}`);
return null;
}
}
getBan(name: string, ip: string) {
const ban = new Ban();
ban.ip = ip;
ban.name = name;
return ban;
}
async banPlayer(ban: Ban) {
try {
const repo = this.db.getRepository(Ban);
if (await repo.findOne({
ip: ban.ip,
name: ban.name
})) {
return;
}
return await repo.save(ban);
} catch (e) {
this.log.warn(`Failed to update ban ${JSON.stringify(ban)}: ${e.toString()}`);
return null;
}
}
async getRandomDuelBan(ip: string) {
const repo = this.db.getRepository(RandomDuelBan);
try {
const ban = await repo.findOne(ip);
//console.log(ip, ban);
return ban;
} catch (e) {
this.log.warn(`Failed to fetch random duel ban ${ip}: ${e.toString()}`);
return null;
}
}
async updateRandomDuelBan(ban: RandomDuelBan) {
const repo = this.db.getRepository(RandomDuelBan);
try {
await repo.save(ban);
} catch (e) {
this.log.warn(`Failed to update random duel ban ${ban.ip}: ${e.toString()}`);
}
}
async randomDuelBanPlayer(ip: string, reason: string, countadd?: number) {
const count = countadd || 1;
const repo = this.db.getRepository(RandomDuelBan);
try {
let ban = await repo.findOne(ip);
if(ban) {
ban.count += count;
const banTime = ban.count > 3 ? Math.pow(2, ban.count - 3) * 2 : 0;
const banDate = moment(ban.time);
if(moment().isAfter(banDate)) {
ban.time = moment().add(banTime, 'm').toDate();
} else {
ban.time = moment(banDate).add(banTime, 'm').toDate();
}
if(!_.contains(ban.reasons, reason)) {
ban.reasons.push(reason);
}
ban.needTip = 1;
} else {
ban = new RandomDuelBan();
ban.ip = ip;
ban.time = moment().toDate();
ban.count = count;
ban.reasons = [reason];
ban.needTip = 1;
}
return await repo.save(ban);
} catch (e) {
this.log.warn(`Failed to update random duel ban ${ip}: ${e.toString()}`);
return null;
}
}
async getAllDuelLogs() {
const repo = this.db.getRepository(DuelLog);
try {
const allDuelLogs = await repo.find({relations: ["players"]});
return allDuelLogs;
} catch (e) {
this.log.warn(`Failed to fetch duel logs: ${e.toString()}`);
return [];
}
}
async getDuelLogFromId(id: number) {
const repo = this.db.getRepository(DuelLog);
try {
const duelLog = await repo.findOne(id, {relations: ["players"]});
return duelLog;
} catch (e) {
this.log.warn(`Failed to fetch duel logs: ${e.toString()}`);
return null;
}
}
async getDuelLogFromRecoverSearch(realName: string) {
const repo = this.db.getRepository(DuelLog);
try {
const duelLogs = await repo.createQueryBuilder("duelLog")
.where("startDeckBuffer is not null and currentDeckBuffer is not null and roomMode != 2 and exists (select id from duel_log_player where duel_log_player.duelLogId = duelLog.id and duel_log_player.realName = :realName)", { realName })
.orderBy("duelLog.id", "DESC")
.limit(10)
.leftJoinAndSelect("duelLog.players", "player")
.getMany();
return duelLogs;
} catch (e) {
this.log.warn(`Failed to fetch duel logs: ${e.toString()}`);
return null;
}
}
async getDuelLogJSON(tournamentModeSettings: any) {
const allDuelLogs = await this.getAllDuelLogs();
return allDuelLogs.map(duelLog => duelLog.getViewJSON(tournamentModeSettings));
}
async getAllReplayFilenames() {
const allDuelLogs = await this.getAllDuelLogs();
return allDuelLogs.map(duelLog => duelLog.replayFileName);
}
async clearDuelLog() {
const runner = this.db.createQueryRunner();
try {
await runner.connect();
await runner.startTransaction();
await runner.query("SET FOREIGN_KEY_CHECKS = 0; ");
await runner.clearTable("duel_log_player");
await runner.clearTable("duel_log");
await runner.query("SET FOREIGN_KEY_CHECKS = 1; ");
await runner.commitTransaction();
} catch (e) {
await runner.rollbackTransaction();
this.log.warn(`Failed to clear duel logs: ${e.toString()}`);
}
}
async saveDuelLog(name: string, roomId: number, cloudReplayId: number, replayFilename: string, roomMode: number, duelCount: number, playerInfos: DuelLogPlayerInfo[]) {
const duelLog = new DuelLog();
duelLog.name = name;
duelLog.time = moment().toDate();
duelLog.roomId = roomId;
duelLog.cloudReplayId = cloudReplayId;
duelLog.replayFileName = replayFilename;
duelLog.roomMode = roomMode;
duelLog.duelCount = duelCount;
const players = playerInfos.map(p => DuelLogPlayer.fromDuelLogPlayerInfo(p));
await this.transaction(async (mdb) => {
try {
const savedDuelLog = await mdb.save(duelLog);
for (let player of players) {
player.duelLog = savedDuelLog;
}
await mdb.save(players);
return true;
} catch (e) {
this.log.warn(`Failed to save duel log ${name}: ${e.toString()}`);
return false;
}
});
}
async getUser(key: string) {
const repo = this.db.getRepository(User);
try {
const user = await repo.findOne(key);
return user;
} catch (e) {
this.log.warn(`Failed to fetch user: ${e.toString()}`);
return null;
}
}
async getOrCreateUser(key: string) {
const user = await this.getUser(key);
if(user) {
return user;
}
const newUser = new User();
newUser.key = key;
return await this.saveUser(newUser);
}
async saveUser(user: User) {
const repo = this.db.getRepository(User);
try {
return await repo.save(user);
} catch (e) {
this.log.warn(`Failed to save user: ${e.toString()}`);
return null;
}
}
async getUserChatColor(key: string) {
const user = await this.getUser(key);
return user ? user.chatColor : null;
}
async setUserChatColor(key: string, color: string) {
let user = await this.getOrCreateUser(key);
user.chatColor = color;
return await this.saveUser(user);
}
async migrateChatColors(data: any) {
await this.transaction(async (mdb) => {
try {
const users: User[] = [];
for(let key in data) {
const chatColor: string = data[key];
let user = await mdb.findOne(User, key);
if(!user) {
user = new User();
user.key = key;
}
user.chatColor = chatColor;
users.push(user);
}
await mdb.save(users);
return true;
} catch (e) {
this.log.warn(`Failed to migrate chat color data: ${e.toString()}`);
return false;
}
});
}
async getRandomDuelScore(name: string) {
const repo = this.db.getRepository(RandomDuelScore);
try {
const score = await repo.findOne(name);
return score;
} catch (e) {
this.log.warn(`Failed to fetch random duel score ${name}: ${e.toString()}`);
return null;
}
}
async saveRandomDuelScore(score: RandomDuelScore) {
const repo = this.db.getRepository(RandomDuelScore);
try {
return await repo.save(score);
} catch (e) {
this.log.warn(`Failed to save random duel score: ${e.toString()}`);
return null;
}
}
async getOrCreateRandomDuelScore(name: string) {
const score = await this.getRandomDuelScore(name);
if(score) {
return score;
}
const newScore = new RandomDuelScore();
newScore.name = name;
return await this.saveRandomDuelScore(newScore);
}
async getRandomDuelScoreDisplay(name: string) {
const score = await this.getRandomDuelScore(name);
if(!score) {
return `${name.split("$")[0]} \${random_score_blank}`;
}
return score.getScoreText();
}
async randomDuelPlayerWin(name: string) {
const score = await this.getOrCreateRandomDuelScore(name);
score.win();
await this.saveRandomDuelScore(score);
}
async randomDuelPlayerLose(name: string) {
const score = await this.getOrCreateRandomDuelScore(name);
score.lose();
await this.saveRandomDuelScore(score);
}
async randomDuelPlayerFlee(name: string) {
const score = await this.getOrCreateRandomDuelScore(name);
score.flee();
await this.saveRandomDuelScore(score);
}
async getRandomScoreTop10() {
try {
const scores = await this.db.getRepository(RandomDuelScore)
.createQueryBuilder("score")
.orderBy("score.win", "DESC")
.addOrderBy("score.lose", "ASC")
.addOrderBy("score.flee", "ASC")
.limit(10)
.getMany();
return scores.map(score => [score.getDisplayName(), {
win: score.winCount,
lose: score.loseCount,
flee: score.fleeCount,
combo: score.winCombo
}]);
} catch (e) {
this.log.warn(`Failed to fetch random duel score ${name}: ${e.toString()}`);
return [];
}
}
}
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.decodeDeck = exports.encodeDeck = void 0;
const assert_1 = __importDefault(require("assert"));
function encodeDeck(deck) {
let pointer = 0;
const bufferSize = (2 + deck.main.length + deck.side.length) * 4;
const buffer = Buffer.allocUnsafe(bufferSize);
buffer.writeInt32LE(deck.main.length, pointer);
pointer += 4;
buffer.writeInt32LE(deck.side.length, pointer);
pointer += 4;
for (let cardCode of deck.main.concat(deck.side)) {
buffer.writeInt32LE(cardCode, pointer);
pointer += 4;
}
assert_1.default(pointer === bufferSize, `Invalid buffer size. Expected: ${bufferSize}. Got: ${pointer}`);
return buffer;
}
exports.encodeDeck = encodeDeck;
function decodeDeck(buffer) {
let pointer = 0;
const mainLength = buffer.readInt32LE(pointer);
pointer += 4;
const sideLength = buffer.readInt32LE(pointer);
pointer += 4;
const correctBufferLength = (2 + mainLength + sideLength) * 4;
assert_1.default(buffer.length >= (2 + mainLength + sideLength) * 4, `Invalid buffer size. Expected: ${correctBufferLength}. Got: ${buffer.length}`);
const main = [];
const side = [];
for (let i = 0; i < mainLength; ++i) {
main.push(buffer.readInt32LE(pointer));
pointer += 4;
}
for (let i = 0; i < sideLength; ++i) {
side.push(buffer.readInt32LE(pointer));
pointer += 4;
}
return { main, side };
}
exports.decodeDeck = decodeDeck;
//# sourceMappingURL=DeckEncoder.js.map
\ No newline at end of file
import assert from "assert";
export interface Deck {
main: number[];
side: number[];
}
export function encodeDeck(deck: Deck) {
let pointer = 0;
const bufferSize = (2 + deck.main.length + deck.side.length) * 4;
const buffer = Buffer.allocUnsafe(bufferSize);
buffer.writeInt32LE(deck.main.length, pointer);
pointer += 4;
buffer.writeInt32LE(deck.side.length, pointer);
pointer += 4;
for(let cardCode of deck.main.concat(deck.side)) {
buffer.writeInt32LE(cardCode, pointer);
pointer += 4;
}
assert(pointer === bufferSize, `Invalid buffer size. Expected: ${bufferSize}. Got: ${pointer}`);
return buffer;
}
export function decodeDeck(buffer: Buffer): Deck {
let pointer = 0;
const mainLength = buffer.readInt32LE(pointer);
pointer += 4;
const sideLength = buffer.readInt32LE(pointer);
pointer += 4;
const correctBufferLength = (2 + mainLength + sideLength) * 4;
assert(buffer.length >= (2 + mainLength + sideLength) * 4, `Invalid buffer size. Expected: ${correctBufferLength}. Got: ${buffer.length}`);
const main: number[] = [];
const side: number[] = [];
for(let i = 0; i < mainLength; ++i) {
main.push(buffer.readInt32LE(pointer));
pointer += 4;
}
for(let i = 0; i < sideLength; ++i) {
side.push(buffer.readInt32LE(pointer));
pointer += 4;
}
return {main, side};
}
\ No newline at end of file
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Ban = void 0;
const typeorm_1 = require("typeorm");
let Ban = class Ban {
};
__decorate([
typeorm_1.PrimaryGeneratedColumn({ unsigned: true, type: "bigint" }),
__metadata("design:type", Number)
], Ban.prototype, "id", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column({ type: "varchar", length: 64, nullable: true }),
__metadata("design:type", String)
], Ban.prototype, "ip", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column({ type: "varchar", length: 20, nullable: true }),
__metadata("design:type", String)
], Ban.prototype, "name", void 0);
Ban = __decorate([
typeorm_1.Entity(),
typeorm_1.Unique(["ip", "name"])
], Ban);
exports.Ban = Ban;
//# sourceMappingURL=Ban.js.map
\ No newline at end of file
import {Column, Entity, Index, PrimaryGeneratedColumn, Unique} from "typeorm";
@Entity()
@Unique(["ip", "name"])
export class Ban {
@PrimaryGeneratedColumn({ unsigned: true, type: "bigint" })
id: number;
@Index()
@Column({ type: "varchar", length: 64, nullable: true })
ip: string;
@Index()
@Column({ type: "varchar", length: 20, nullable: true })
name: string;
}
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BasePlayer = void 0;
const typeorm_1 = require("typeorm");
class BasePlayer {
}
__decorate([
typeorm_1.PrimaryGeneratedColumn({ unsigned: true, type: "bigint" }),
__metadata("design:type", Number)
], BasePlayer.prototype, "id", void 0);
__decorate([
typeorm_1.Column({ type: "varchar", length: 20 }),
__metadata("design:type", String)
], BasePlayer.prototype, "name", void 0);
__decorate([
typeorm_1.Column({ type: "tinyint" }),
__metadata("design:type", Number)
], BasePlayer.prototype, "pos", void 0);
exports.BasePlayer = BasePlayer;
//# sourceMappingURL=BasePlayer.js.map
\ No newline at end of file
import {Column, PrimaryGeneratedColumn} from "typeorm";
export abstract class BasePlayer {
@PrimaryGeneratedColumn({unsigned: true, type: "bigint"})
id: number;
@Column({ type: "varchar", length: 20 })
name: string;
@Column({ type: "tinyint" })
pos: number;
}
\ No newline at end of file
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CloudReplay = void 0;
const typeorm_1 = require("typeorm");
const CloudReplayPlayer_1 = require("./CloudReplayPlayer");
const underscore_1 = __importDefault(require("underscore"));
const moment_1 = __importDefault(require("moment"));
let CloudReplay = class CloudReplay {
fromBuffer(buffer) {
this.data = buffer.toString("base64");
}
toBuffer() {
return Buffer.from(this.data, "base64");
}
getDateString() {
return moment_1.default(this.date).format('YYYY-MM-DD HH:mm:ss');
}
getPlayerNamesString() {
const playerInfos = underscore_1.default.clone(this.players);
playerInfos.sort((p1, p2) => p1.pos - p2.pos);
return playerInfos[0].name + (playerInfos[2] ? "+" + playerInfos[2].name : "") + " VS " + (playerInfos[1] ? playerInfos[1].name : "AI") + (playerInfos[3] ? "+" + playerInfos[3].name : "");
}
getDisplayString() {
return `R#${this.id} ${this.getPlayerNamesString()} ${this.getDateString()}`;
}
};
__decorate([
typeorm_1.PrimaryColumn({ unsigned: true, type: "bigint" }),
__metadata("design:type", Number)
], CloudReplay.prototype, "id", void 0);
__decorate([
typeorm_1.Column({ type: "text" }),
__metadata("design:type", String)
], CloudReplay.prototype, "data", void 0);
__decorate([
typeorm_1.Column({ type: "datetime" }),
__metadata("design:type", Date)
], CloudReplay.prototype, "date", void 0);
__decorate([
typeorm_1.OneToMany(() => CloudReplayPlayer_1.CloudReplayPlayer, player => player.cloudReplay),
__metadata("design:type", Array)
], CloudReplay.prototype, "players", void 0);
CloudReplay = __decorate([
typeorm_1.Entity()
], CloudReplay);
exports.CloudReplay = CloudReplay;
//# sourceMappingURL=CloudReplay.js.map
\ No newline at end of file
import {Column, Entity, OneToMany, PrimaryColumn} from "typeorm";
import {CloudReplayPlayer} from "./CloudReplayPlayer";
import _ from "underscore";
import moment from "moment";
@Entity()
export class CloudReplay {
@PrimaryColumn({ unsigned: true, type: "bigint" })
id: number;
@Column({ type: "text" })
data: string;
fromBuffer(buffer: Buffer) {
this.data = buffer.toString("base64");
}
toBuffer() {
return Buffer.from(this.data, "base64");
}
@Column({ type: "datetime" })
date: Date;
getDateString() {
return moment(this.date).format('YYYY-MM-DD HH:mm:ss')
}
@OneToMany(() => CloudReplayPlayer, player => player.cloudReplay)
players: CloudReplayPlayer[];
getPlayerNamesString() {
const playerInfos = _.clone(this.players);
playerInfos.sort((p1, p2) => p1.pos - p2.pos);
return playerInfos[0].name + (playerInfos[2] ? "+" + playerInfos[2].name : "") + " VS " + (playerInfos[1] ? playerInfos[1].name : "AI") + (playerInfos[3] ? "+" + playerInfos[3].name : "");
}
getDisplayString() {
return `R#${this.id} ${this.getPlayerNamesString()} ${this.getDateString()}`;
}
}
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var CloudReplayPlayer_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.CloudReplayPlayer = void 0;
const typeorm_1 = require("typeorm");
const CloudReplay_1 = require("./CloudReplay");
const BasePlayer_1 = require("./BasePlayer");
let CloudReplayPlayer = CloudReplayPlayer_1 = class CloudReplayPlayer extends BasePlayer_1.BasePlayer {
static fromPlayerInfo(info) {
const p = new CloudReplayPlayer_1();
p.key = info.key;
p.name = info.name;
p.pos = info.pos;
return p;
}
};
__decorate([
typeorm_1.Index(),
typeorm_1.Column({ type: "varchar", length: 128 }),
__metadata("design:type", String)
], CloudReplayPlayer.prototype, "key", void 0);
__decorate([
typeorm_1.ManyToOne(() => CloudReplay_1.CloudReplay, replay => replay.players),
__metadata("design:type", CloudReplay_1.CloudReplay)
], CloudReplayPlayer.prototype, "cloudReplay", void 0);
CloudReplayPlayer = CloudReplayPlayer_1 = __decorate([
typeorm_1.Entity()
], CloudReplayPlayer);
exports.CloudReplayPlayer = CloudReplayPlayer;
//# sourceMappingURL=CloudReplayPlayer.js.map
\ No newline at end of file
import {Column, Entity, Index, ManyToOne} from "typeorm";
import {CloudReplayPlayerInfo} from "../DataManager";
import {CloudReplay} from "./CloudReplay";
import {BasePlayer} from "./BasePlayer";
@Entity()
export class CloudReplayPlayer extends BasePlayer {
@Index()
@Column({ type: "varchar", length: 128 })
key: string;
@ManyToOne(() => CloudReplay, replay => replay.players)
cloudReplay: CloudReplay;
static fromPlayerInfo(info: CloudReplayPlayerInfo) {
const p = new CloudReplayPlayer();
p.key = info.key;
p.name = info.name;
p.pos = info.pos;
return p;
}
}
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DuelLog = void 0;
const typeorm_1 = require("typeorm");
const DuelLogPlayer_1 = require("./DuelLogPlayer");
const moment_1 = __importDefault(require("moment"));
const underscore_1 = __importDefault(require("underscore"));
let DuelLog = class DuelLog {
getViewString() {
const viewPlayers = underscore_1.default.clone(this.players);
viewPlayers.sort((p1, p2) => p1.pos - p2.pos);
const playerString = viewPlayers[0].realName.split("$")[0] + (viewPlayers[2] ? "+" + viewPlayers[2].realName.split("$")[0] : "") + " VS " + (viewPlayers[1] ? viewPlayers[1].realName.split("$")[0] : "AI") + (viewPlayers[3] ? "+" + viewPlayers[3].realName.split("$")[0] : "");
return `<${this.id}> ${playerString} ${moment_1.default(this.time).format("YYYY-MM-DD HH-mm-ss")}`;
}
getViewJSON(tournamentModeSettings) {
const data = {
id: this.id,
time: moment_1.default(this.time).format("YYYY-MM-DD HH:mm:ss"),
name: this.name + (tournamentModeSettings.show_info ? " (Duel:" + this.duelCount + ")" : ""),
roomid: this.roomId,
cloud_replay_id: "R#" + this.cloudReplayId,
replay_filename: this.replayFileName,
roommode: this.roomMode,
players: this.players.map(player => {
return {
pos: player.pos,
is_first: player.isFirst === 1,
name: player.name + (tournamentModeSettings.show_ip ? " (IP: " + player.ip.slice(7) + ")" : "") + (tournamentModeSettings.show_info && !(this.roomMode === 2 && player.pos % 2 > 0) ? " (Score:" + player.score + " LP:" + (player.lp != null ? player.lp : "???") + (this.roomMode !== 2 ? " Cards:" + (player.cardCount != null ? player.cardCount : "???") : "") + ")" : ""),
winner: player.winner === 1
};
})
};
return data;
}
};
__decorate([
typeorm_1.PrimaryGeneratedColumn({ unsigned: true, type: "bigint" }),
__metadata("design:type", Number)
], DuelLog.prototype, "id", void 0);
__decorate([
typeorm_1.Column("datetime"),
__metadata("design:type", Date)
], DuelLog.prototype, "time", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column({ type: "varchar", length: 20 }),
__metadata("design:type", String)
], DuelLog.prototype, "name", void 0);
__decorate([
typeorm_1.Column("int"),
__metadata("design:type", Number)
], DuelLog.prototype, "roomId", void 0);
__decorate([
typeorm_1.Column("bigint"),
__metadata("design:type", Number)
], DuelLog.prototype, "cloudReplayId", void 0);
__decorate([
typeorm_1.Column({ type: "varchar", length: 256 }),
__metadata("design:type", String)
], DuelLog.prototype, "replayFileName", void 0);
__decorate([
typeorm_1.Column("tinyint", { unsigned: true }),
__metadata("design:type", Number)
], DuelLog.prototype, "roomMode", void 0);
__decorate([
typeorm_1.Column("tinyint", { unsigned: true }),
__metadata("design:type", Number)
], DuelLog.prototype, "duelCount", void 0);
__decorate([
typeorm_1.OneToMany(() => DuelLogPlayer_1.DuelLogPlayer, player => player.duelLog),
__metadata("design:type", Array)
], DuelLog.prototype, "players", void 0);
DuelLog = __decorate([
typeorm_1.Entity()
], DuelLog);
exports.DuelLog = DuelLog;
//# sourceMappingURL=DuelLog.js.map
\ No newline at end of file
import {Column, Entity, Index, OneToMany, PrimaryGeneratedColumn} from "typeorm";
import {DuelLogPlayer} from "./DuelLogPlayer";
import moment from "moment";
import _ from "underscore";
@Entity()
export class DuelLog {
@PrimaryGeneratedColumn({unsigned: true, type: "bigint"})
id: number;
@Column("datetime")
time: Date;
@Index()
@Column({type: "varchar", length: 20})
name: string;
@Column("int")
roomId: number;
@Column("bigint")
cloudReplayId: number; // not very needed to become a relation
@Column({type: "varchar", length: 256})
replayFileName: string;
@Column("tinyint", {unsigned: true})
roomMode: number;
@Column("tinyint", {unsigned: true})
duelCount: number;
@OneToMany(() => DuelLogPlayer, player => player.duelLog)
players: DuelLogPlayer[];
getViewString() {
const viewPlayers = _.clone(this.players);
viewPlayers.sort((p1, p2) => p1.pos - p2.pos);
const playerString = viewPlayers[0].realName.split("$")[0] + (viewPlayers[2] ? "+" + viewPlayers[2].realName.split("$")[0] : "") + " VS " + (viewPlayers[1] ? viewPlayers[1].realName.split("$")[0] : "AI") + (viewPlayers[3] ? "+" + viewPlayers[3].realName.split("$")[0] : "");
return `<${this.id}> ${playerString} ${moment(this.time).format("YYYY-MM-DD HH-mm-ss")}`;
}
getViewJSON(tournamentModeSettings: any) {
const data = {
id: this.id,
time: moment(this.time).format("YYYY-MM-DD HH:mm:ss"),
name: this.name + (tournamentModeSettings.show_info ? " (Duel:" + this.duelCount + ")" : ""),
roomid: this.roomId,
cloud_replay_id: "R#" + this.cloudReplayId,
replay_filename: this.replayFileName,
roommode: this.roomMode,
players: this.players.map(player => {
return {
pos: player.pos,
is_first: player.isFirst === 1,
name: player.name + (tournamentModeSettings.show_ip ? " (IP: " + player.ip.slice(7) + ")" : "") + (tournamentModeSettings.show_info && !(this.roomMode === 2 && player.pos % 2 > 0) ? " (Score:" + player.score + " LP:" + (player.lp != null ? player.lp : "???") + (this.roomMode !== 2 ? " Cards:" + (player.cardCount != null ? player.cardCount : "???") : "") + ")" : ""),
winner: player.winner === 1
}
})
}
return data;
}
}
\ No newline at end of file
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var DuelLogPlayer_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DuelLogPlayer = void 0;
const typeorm_1 = require("typeorm");
const BasePlayer_1 = require("./BasePlayer");
const DuelLog_1 = require("./DuelLog");
const DeckEncoder_1 = require("../DeckEncoder");
let DuelLogPlayer = DuelLogPlayer_1 = class DuelLogPlayer extends BasePlayer_1.BasePlayer {
setStartDeck(deck) {
if (deck === null) {
this.startDeckBuffer = null;
return;
}
this.startDeckBuffer = DeckEncoder_1.encodeDeck(deck).toString("base64");
}
getStartDeck() {
return DeckEncoder_1.decodeDeck(Buffer.from(this.startDeckBuffer, "base64"));
}
setCurrentDeck(deck) {
if (deck === null) {
this.currentDeckBuffer = null;
return;
}
this.currentDeckBuffer = DeckEncoder_1.encodeDeck(deck).toString("base64");
}
getCurrentDeck() {
return DeckEncoder_1.decodeDeck(Buffer.from(this.currentDeckBuffer, "base64"));
}
static fromDuelLogPlayerInfo(info) {
const p = new DuelLogPlayer_1();
p.name = info.name;
p.pos = info.pos;
p.realName = info.realName;
p.lp = info.lp;
p.ip = info.ip;
p.score = info.score;
p.cardCount = info.cardCount;
p.isFirst = info.isFirst ? 1 : 0;
p.winner = info.winner ? 1 : 0;
p.startDeckBuffer = info.startDeckBuffer.toString("base64");
p.setCurrentDeck(info.deck);
return p;
}
};
__decorate([
typeorm_1.Index(),
typeorm_1.Column({ type: "varchar", length: 20 }),
__metadata("design:type", String)
], DuelLogPlayer.prototype, "realName", void 0);
__decorate([
typeorm_1.Column({ type: "varchar", length: 64, nullable: true }),
__metadata("design:type", String)
], DuelLogPlayer.prototype, "ip", void 0);
__decorate([
typeorm_1.Column("tinyint", { unsigned: true }),
__metadata("design:type", Number)
], DuelLogPlayer.prototype, "isFirst", void 0);
__decorate([
typeorm_1.Column("tinyint"),
__metadata("design:type", Number)
], DuelLogPlayer.prototype, "score", void 0);
__decorate([
typeorm_1.Column("int", { nullable: true }),
__metadata("design:type", Number)
], DuelLogPlayer.prototype, "lp", void 0);
__decorate([
typeorm_1.Column("smallint", { nullable: true }),
__metadata("design:type", Number)
], DuelLogPlayer.prototype, "cardCount", void 0);
__decorate([
typeorm_1.Column("text", { nullable: true }),
__metadata("design:type", String)
], DuelLogPlayer.prototype, "startDeckBuffer", void 0);
__decorate([
typeorm_1.Column("text", { nullable: true }),
__metadata("design:type", String)
], DuelLogPlayer.prototype, "currentDeckBuffer", void 0);
__decorate([
typeorm_1.Column("tinyint"),
__metadata("design:type", Number)
], DuelLogPlayer.prototype, "winner", void 0);
__decorate([
typeorm_1.ManyToOne(() => DuelLog_1.DuelLog, duelLog => duelLog.players),
__metadata("design:type", DuelLog_1.DuelLog)
], DuelLogPlayer.prototype, "duelLog", void 0);
DuelLogPlayer = DuelLogPlayer_1 = __decorate([
typeorm_1.Entity()
], DuelLogPlayer);
exports.DuelLogPlayer = DuelLogPlayer;
//# sourceMappingURL=DuelLogPlayer.js.map
\ No newline at end of file
import {Column, Entity, Index, ManyToOne} from "typeorm";
import {BasePlayer} from "./BasePlayer";
import {DuelLog} from "./DuelLog";
import {Deck, decodeDeck, encodeDeck} from "../DeckEncoder";
import {DuelLogPlayerInfo} from "../DataManager";
@Entity()
export class DuelLogPlayer extends BasePlayer {
@Index()
@Column({ type: "varchar", length: 20 })
realName: string;
@Column({ type: "varchar", length: 64, nullable: true })
ip: string;
@Column("tinyint", {unsigned: true})
isFirst: number;
@Column("tinyint")
score: number;
@Column("int", {nullable: true})
lp: number;
@Column("smallint", {nullable: true})
cardCount: number;
@Column("text", {nullable: true})
startDeckBuffer: string;
@Column("text", {nullable: true})
currentDeckBuffer: string;
@Column("tinyint")
winner: number;
setStartDeck(deck: Deck) {
if(deck === null) {
this.startDeckBuffer = null;
return;
}
this.startDeckBuffer = encodeDeck(deck).toString("base64");
}
getStartDeck() {
return decodeDeck(Buffer.from(this.startDeckBuffer, "base64"));
}
setCurrentDeck(deck: Deck) {
if(deck === null) {
this.currentDeckBuffer = null;
return;
}
this.currentDeckBuffer = encodeDeck(deck).toString("base64");
}
getCurrentDeck() {
return decodeDeck(Buffer.from(this.currentDeckBuffer, "base64"));
}
@ManyToOne(() => DuelLog, duelLog => duelLog.players)
duelLog: DuelLog;
static fromDuelLogPlayerInfo(info: DuelLogPlayerInfo) {
const p = new DuelLogPlayer();
p.name = info.name;
p.pos = info.pos;
p.realName = info.realName;
p.lp = info.lp;
p.ip = info.ip;
p.score = info.score;
p.cardCount = info.cardCount;
p.isFirst = info.isFirst ? 1 : 0;
p.winner = info.winner ? 1 : 0;
p.startDeckBuffer = info.startDeckBuffer.toString("base64");
p.setCurrentDeck(info.deck);
return p;
}
}
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RandomDuelBan = void 0;
const typeorm_1 = require("typeorm");
let RandomDuelBan = class RandomDuelBan {
setNeedTip(need) {
this.needTip = need ? 1 : 0;
}
getNeedTip() {
return this.needTip > 0 ? true : false;
}
};
__decorate([
typeorm_1.PrimaryColumn({ type: "varchar", length: 64 }),
__metadata("design:type", String)
], RandomDuelBan.prototype, "ip", void 0);
__decorate([
typeorm_1.Column("datetime"),
__metadata("design:type", Date)
], RandomDuelBan.prototype, "time", void 0);
__decorate([
typeorm_1.Column("smallint"),
__metadata("design:type", Number)
], RandomDuelBan.prototype, "count", void 0);
__decorate([
typeorm_1.Column({ type: "simple-array" }),
__metadata("design:type", Array)
], RandomDuelBan.prototype, "reasons", void 0);
__decorate([
typeorm_1.Column({ type: "tinyint", unsigned: true }),
__metadata("design:type", Number)
], RandomDuelBan.prototype, "needTip", void 0);
RandomDuelBan = __decorate([
typeorm_1.Entity()
], RandomDuelBan);
exports.RandomDuelBan = RandomDuelBan;
//# sourceMappingURL=RandomDuelBan.js.map
\ No newline at end of file
import {Column, Entity, PrimaryColumn} from "typeorm";
@Entity()
export class RandomDuelBan {
@PrimaryColumn({type: "varchar", length: 64})
ip: string;
@Column("datetime")
time: Date;
@Column("smallint")
count: number;
@Column({type: "simple-array"})
reasons: string[]
@Column({type: "tinyint", unsigned: true})
needTip: number;
setNeedTip(need: boolean) {
this.needTip = need ? 1 : 0;
}
getNeedTip() {
return this.needTip > 0 ? true : false;
}
}
\ No newline at end of file
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RandomDuelScore = void 0;
const typeorm_1 = require("typeorm");
let RandomDuelScore = class RandomDuelScore {
getDisplayName() {
return this.name.split("$")[0];
}
win() {
++this.winCount;
++this.winCombo;
}
lose() {
++this.loseCount;
this.winCombo = 0;
}
flee() {
++this.fleeCount;
this.lose();
}
getScoreText() {
const displayName = this.getDisplayName();
const total = this.winCount + this.loseCount;
if (this.winCount < 2 && total < 3) {
return `${displayName} \${random_this_not_enough}`;
}
if (this.winCombo >= 2) {
return `\${random_this_part1}${displayName} \${random_this_part2} ${Math.ceil(this.winCount / total * 100)}\${random_this_part3} ${Math.ceil(this.fleeCount / total * 100)}\${random_this_part4_combo}${this.winCombo}\${random_this_part5_combo}`;
}
else {
//return displayName + " 的今日战绩:胜率" + Math.ceil(this.winCount/total*100) + "%,逃跑率" + Math.ceil(this.fleeCount/total*100) + "%," + this.winCombo + "连胜中!"
return `\${random_this_part1}${displayName} \${random_this_part2} ${Math.ceil(this.winCount / total * 100)}\${random_this_part3} ${Math.ceil(this.fleeCount / total * 100)}\${random_this_part4}`;
}
}
};
__decorate([
typeorm_1.PrimaryColumn({ type: "varchar", length: 20 }),
__metadata("design:type", String)
], RandomDuelScore.prototype, "name", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column("int", { unsigned: true, default: 0 }),
__metadata("design:type", Number)
], RandomDuelScore.prototype, "winCount", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column("int", { unsigned: true, default: 0 }),
__metadata("design:type", Number)
], RandomDuelScore.prototype, "loseCount", void 0);
__decorate([
typeorm_1.Index(),
typeorm_1.Column("int", { unsigned: true, default: 0 }),
__metadata("design:type", Number)
], RandomDuelScore.prototype, "fleeCount", void 0);
__decorate([
typeorm_1.Column("int", { unsigned: true, default: 0 }),
__metadata("design:type", Number)
], RandomDuelScore.prototype, "winCombo", void 0);
RandomDuelScore = __decorate([
typeorm_1.Entity()
], RandomDuelScore);
exports.RandomDuelScore = RandomDuelScore;
//# sourceMappingURL=RandomDuelScore.js.map
\ No newline at end of file
import {Column, Entity, Index, PrimaryColumn} from "typeorm";
@Entity()
export class RandomDuelScore {
@PrimaryColumn({type: "varchar", length: 20})
name: string;
@Index()
@Column("int", {unsigned: true, default: 0})
winCount: number;
@Index()
@Column("int", {unsigned: true, default: 0})
loseCount: number;
@Index()
@Column("int", {unsigned: true, default: 0})
fleeCount: number;
@Column("int", {unsigned: true, default: 0})
winCombo: number;
getDisplayName() {
return this.name.split("$")[0];
}
win() {
++this.winCount;
++this.winCombo;
}
lose() {
++this.loseCount;
this.winCombo = 0;
}
flee() {
++this.fleeCount;
this.lose();
}
getScoreText() {
const displayName = this.getDisplayName();
const total = this.winCount + this.loseCount;
if (this.winCount < 2 && total < 3) {
return `${displayName} \${random_score_not_enough}`;
}
if (this.winCombo >= 2) {
return `\${random_score_part1}${displayName} \${random_score_part2} ${Math.ceil(this.winCount / total * 100)}\${random_score_part3} ${Math.ceil(this.fleeCount / total * 100)}\${random_score_part4_combo}${this.winCombo}\${random_score_part5_combo}`;
} else {
//return displayName + " 的今日战绩:胜率" + Math.ceil(this.winCount/total*100) + "%,逃跑率" + Math.ceil(this.fleeCount/total*100) + "%," + this.winCombo + "连胜中!"
return `\${random_score_part1}${displayName} \${random_score_part2} ${Math.ceil(this.winCount / total * 100)}\${random_score_part3} ${Math.ceil(this.fleeCount / total * 100)}\${random_score_part4}`;
}
}
}
\ No newline at end of file
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.User = void 0;
const typeorm_1 = require("typeorm");
let User = class User {
};
__decorate([
typeorm_1.PrimaryColumn({ type: "varchar", length: 128 }),
__metadata("design:type", String)
], User.prototype, "key", void 0);
__decorate([
typeorm_1.Column("varchar", { length: 16, nullable: true }),
__metadata("design:type", String)
], User.prototype, "chatColor", void 0);
User = __decorate([
typeorm_1.Entity()
], User);
exports.User = User;
//# sourceMappingURL=User.js.map
\ No newline at end of file
import {Column, Entity, PrimaryColumn} from "typeorm";
@Entity()
export class User {
@PrimaryColumn({type: "varchar", length: 128})
key: string;
@Column("varchar", {length: 16, nullable: true})
chatColor: string;
}
...@@ -82,13 +82,18 @@ ...@@ -82,13 +82,18 @@
"ready_time": 20, "ready_time": 20,
"hang_timeout": 90 "hang_timeout": 90
}, },
"cloud_replay": { "mysql": {
"enabled": false, "enabled": false,
"redis": { "db": {
"host": "127.0.0.1", "host": "127.0.0.1",
"port": 6379 "port": 3306,
"username": "root",
"password": "localhost",
"database": "srvpro"
}
}, },
"never_expire": false, "cloud_replay": {
"enabled": false,
"enable_halfway_watch": true "enable_halfway_watch": true
}, },
"windbot": { "windbot": {
...@@ -315,8 +320,6 @@ ...@@ -315,8 +320,6 @@
} }
}, },
"ban": { "ban": {
"banned_user": [],
"banned_ip": [],
"illegal_id": [ "illegal_id": [
"^Lv\\.-*\\d+\\s*(.*)", "^Lv\\.-*\\d+\\s*(.*)",
"^VIP\\.\\d+\\s*(.*)" "^VIP\\.\\d+\\s*(.*)"
......
...@@ -24,14 +24,6 @@ ...@@ -24,14 +24,6 @@
] ]
} }
}, },
"duel_log": {
"file": "./config/duel_log.json",
"duel_log": []
},
"chat_color": {
"file": "./config/chat_color.json",
"save_list": {}
},
"users": { "users": {
"file": "./config/admin_user.json", "file": "./config/admin_user.json",
"permission_examples": { "permission_examples": {
......
...@@ -67,14 +67,14 @@ ...@@ -67,14 +67,14 @@
"exp_value_part2": " exp", "exp_value_part2": " exp",
"exp_value_part3": ", your score is ", "exp_value_part3": ", your score is ",
"exp_value_part4": ". These points may be reset after the stable release of MyCard.", "exp_value_part4": ". These points may be reset after the stable release of MyCard.",
"random_score_part1": "Today's record of ", "random_score_part1": "Record of ",
"random_score_part2": ": ", "random_score_part2": ": ",
"random_score_part3": "% WIN, ", "random_score_part3": "% WIN, ",
"random_score_part4_combo": "% RAGE QUIT, WIN COMBO ", "random_score_part4_combo": "% RAGE QUIT, WIN COMBO ",
"random_score_part5_combo": " !", "random_score_part5_combo": " !",
"random_score_part4": "%", "random_score_part4": "%",
"random_score_blank": " didn't have enough duel record today.", "random_score_blank": " didn't have enough duel record.",
"random_score_not_enough": " didn't have enough duel record today.", "random_score_not_enough": " didn't have enough duel record.",
"lp_low_opponent": "もはやお前のライフは風前の灯!", "lp_low_opponent": "もはやお前のライフは風前の灯!",
"lp_low_self": "*Low LP Alert*", "lp_low_self": "*Low LP Alert*",
"kicked_by_player": "was evicted from the game.", "kicked_by_player": "was evicted from the game.",
...@@ -386,13 +386,13 @@ ...@@ -386,13 +386,13 @@
"exp_value_part3": ",你的战斗力是", "exp_value_part3": ",你的战斗力是",
"exp_value_part4": "。竞技场部分积分按赛季重置。", "exp_value_part4": "。竞技场部分积分按赛季重置。",
"random_score_part1": "", "random_score_part1": "",
"random_score_part2": "的今日战绩:胜率", "random_score_part2": "的战绩:胜率",
"random_score_part3": "%,逃跑率", "random_score_part3": "%,逃跑率",
"random_score_part4_combo": "%,", "random_score_part4_combo": "%,",
"random_score_part5_combo": "连胜中!", "random_score_part5_combo": "连胜中!",
"random_score_part4": "%", "random_score_part4": "%",
"random_score_blank": "的今日战绩:正在统计中", "random_score_blank": "的战绩:正在统计中",
"random_score_not_enough": "的今日战绩:正在统计中", "random_score_not_enough": "的战绩:正在统计中",
"lp_low_opponent": "你的生命已经如风中残烛了!", "lp_low_opponent": "你的生命已经如风中残烛了!",
"lp_low_self": "背水一战!", "lp_low_self": "背水一战!",
"kicked_by_player": "被请出了房间", "kicked_by_player": "被请出了房间",
......
...@@ -11,11 +11,6 @@ ...@@ -11,11 +11,6 @@
"cwd": "/ygopro-server/windbot/", "cwd": "/ygopro-server/windbot/",
"args": "ServerMode=true ServerPort=2399", "args": "ServerMode=true ServerPort=2399",
"interpreter": "mono" "interpreter": "mono"
},
{
"name": "redis-server",
"script": "/usr/bin/redis-server",
"cwd": "/redis"
} }
] ]
} }
...@@ -5,37 +5,50 @@ ...@@ -5,37 +5,50 @@
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@babel/code-frame": { "@babel/code-frame": {
"version": "7.10.1", "version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
"integrity": "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==", "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
"requires": { "requires": {
"@babel/highlight": "^7.10.1" "@babel/highlight": "^7.10.4"
} }
}, },
"@babel/helper-validator-identifier": { "@babel/helper-validator-identifier": {
"version": "7.10.1", "version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
"integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==" "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw=="
}, },
"@babel/highlight": { "@babel/highlight": {
"version": "7.10.1", "version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.1.tgz", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
"integrity": "sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==", "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
"requires": { "requires": {
"@babel/helper-validator-identifier": "^7.10.1", "@babel/helper-validator-identifier": "^7.10.4",
"chalk": "^2.0.0", "chalk": "^2.0.0",
"js-tokens": "^4.0.0" "js-tokens": "^4.0.0"
} }
}, },
"@sqltools/formatter": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.2.tgz",
"integrity": "sha512-/5O7Fq6Vnv8L6ucmPjaWbVG1XkP4FO+w5glqfkIsq3Xw4oyNAdJddbnYodNDAfjVUvo/rrSCTom4kAND7T1o5Q=="
},
"@types/bunyan": {
"version": "1.8.6",
"resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.6.tgz",
"integrity": "sha512-YiozPOOsS6bIuz31ilYqR5SlLif4TBWsousN2aCWLi5233nZSX19tFbcQUPdR7xJ8ypPyxkCGNxg0CIV5n9qxQ==",
"requires": {
"@types/node": "*"
}
},
"@types/node": { "@types/node": {
"version": "14.0.13", "version": "14.0.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.13.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.13.tgz",
"integrity": "sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA==" "integrity": "sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA=="
}, },
"@types/underscore": { "@types/underscore": {
"version": "1.10.0", "version": "1.10.24",
"resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.10.0.tgz", "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.10.24.tgz",
"integrity": "sha512-ZAbqul7QAKpM2h1PFGa5ETN27ulmqtj0QviYHasw9LffvXZvVHuraOx/FOsIPPDNGZN0Qo1nASxxSfMYOtSoCw==" "integrity": "sha512-T3NQD8hXNW2sRsSbLNjF/aBo18MyJlbw0lSpQHB/eZZtScPdexN4HSa8cByYwTw9Wy7KuOFr81mlDQcQQaZ79w=="
}, },
"abbrev": { "abbrev": {
"version": "1.1.1", "version": "1.1.1",
...@@ -43,9 +56,9 @@ ...@@ -43,9 +56,9 @@
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
}, },
"ajv": { "ajv": {
"version": "6.12.2", "version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"requires": { "requires": {
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0", "fast-json-stable-stringify": "^2.0.0",
...@@ -66,6 +79,16 @@ ...@@ -66,6 +79,16 @@
"color-convert": "^1.9.0" "color-convert": "^1.9.0"
} }
}, },
"any-promise": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
"integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8="
},
"app-root-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.0.0.tgz",
"integrity": "sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw=="
},
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
...@@ -80,6 +103,21 @@ ...@@ -80,6 +103,21 @@
"readable-stream": "^2.0.6" "readable-stream": "^2.0.6"
} }
}, },
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"requires": {
"sprintf-js": "~1.0.2"
},
"dependencies": {
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
}
}
},
"asn1": { "asn1": {
"version": "0.2.4", "version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
...@@ -109,9 +147,9 @@ ...@@ -109,9 +147,9 @@
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
}, },
"aws4": { "aws4": {
"version": "1.10.0", "version": "1.11.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
"integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==" "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA=="
}, },
"axios": { "axios": {
"version": "0.19.2", "version": "0.19.2",
...@@ -126,6 +164,11 @@ ...@@ -126,6 +164,11 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
}, },
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"bcrypt-pbkdf": { "bcrypt-pbkdf": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
...@@ -134,6 +177,11 @@ ...@@ -134,6 +177,11 @@
"tweetnacl": "^0.14.3" "tweetnacl": "^0.14.3"
} }
}, },
"bignumber.js": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
"integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A=="
},
"binary": { "binary": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
...@@ -143,6 +191,15 @@ ...@@ -143,6 +191,15 @@
"chainsaw": "~0.1.0" "chainsaw": "~0.1.0"
} }
}, },
"block-stream": {
"version": "0.0.9",
"resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
"integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
"optional": true,
"requires": {
"inherits": "~2.0.0"
}
},
"brace-expansion": { "brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
...@@ -152,6 +209,15 @@ ...@@ -152,6 +209,15 @@
"concat-map": "0.0.1" "concat-map": "0.0.1"
} }
}, },
"buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
"requires": {
"base64-js": "^1.3.1",
"ieee754": "^1.1.13"
}
},
"buffer-writer": { "buffer-writer": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-1.0.1.tgz", "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-1.0.1.tgz",
...@@ -163,16 +229,21 @@ ...@@ -163,16 +229,21 @@
"integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=" "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s="
}, },
"bunyan": { "bunyan": {
"version": "1.8.12", "version": "1.8.14",
"resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.14.tgz",
"integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=", "integrity": "sha512-LlahJUxXzZLuw/hetUQJmRgZ1LF6+cr5TPpRj6jf327AsiIq2jhYEH4oqUUkVKTor+9w2BT3oxVwhzE5lw9tcg==",
"requires": { "requires": {
"dtrace-provider": "~0.8", "dtrace-provider": "~0.8",
"moment": "^2.10.6", "moment": "^2.19.3",
"mv": "~2", "mv": "~2",
"safe-json-stringify": "~1" "safe-json-stringify": "~1"
} }
}, },
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
},
"caseless": { "caseless": {
"version": "0.12.0", "version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
...@@ -206,11 +277,160 @@ ...@@ -206,11 +277,160 @@
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
}, },
"cli-highlight": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.4.tgz",
"integrity": "sha512-s7Zofobm20qriqDoU9sXptQx0t2R9PEgac92mENNm7xaEe1hn71IIMsXMK+6encA6WRCWWxIGQbipr3q998tlQ==",
"requires": {
"chalk": "^3.0.0",
"highlight.js": "^9.6.0",
"mz": "^2.4.0",
"parse5": "^5.1.1",
"parse5-htmlparser2-tree-adapter": "^5.1.1",
"yargs": "^15.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz",
"integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==",
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"string-width": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"requires": {
"ansi-regex": "^5.0.0"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"requires": {
"has-flag": "^4.0.0"
}
},
"yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"requires": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
"find-up": "^4.1.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.2"
}
}
}
},
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"string-width": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"requires": {
"ansi-regex": "^5.0.0"
}
}
}
},
"code-point-at": { "code-point-at": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
}, },
"coffeescript": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.5.1.tgz",
"integrity": "sha512-J2jRPX0eeFh5VKyVnoLrfVFgLZtnnmp96WQSLAS8OrLm2wtQLcnikYKe1gViJKDH7vucjuhHvBKKBP3rKcD1tQ=="
},
"color-convert": { "color-convert": {
"version": "1.9.3", "version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
...@@ -268,6 +488,11 @@ ...@@ -268,6 +488,11 @@
"ms": "^2.1.1" "ms": "^2.1.1"
} }
}, },
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
},
"deep-extend": { "deep-extend": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
...@@ -288,16 +513,16 @@ ...@@ -288,16 +513,16 @@
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
}, },
"denque": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz",
"integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ=="
},
"detect-libc": { "detect-libc": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
}, },
"dotenv": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz",
"integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw=="
},
"dtrace-provider": { "dtrace-provider": {
"version": "0.8.8", "version": "0.8.8",
"resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz", "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz",
...@@ -316,6 +541,11 @@ ...@@ -316,6 +541,11 @@
"safer-buffer": "^2.1.0" "safer-buffer": "^2.1.0"
} }
}, },
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"error-ex": { "error-ex": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
...@@ -324,11 +554,21 @@ ...@@ -324,11 +554,21 @@
"is-arrayish": "^0.2.1" "is-arrayish": "^0.2.1"
} }
}, },
"escalade": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
},
"escape-string-regexp": { "escape-string-regexp": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
}, },
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"extend": { "extend": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
...@@ -340,15 +580,29 @@ ...@@ -340,15 +580,29 @@
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
}, },
"fast-deep-equal": { "fast-deep-equal": {
"version": "3.1.1", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
}, },
"fast-json-stable-stringify": { "fast-json-stable-stringify": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
}, },
"figlet": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/figlet/-/figlet-1.5.0.tgz",
"integrity": "sha512-ZQJM4aifMpz6H19AW1VqvZ7l4pOE9p7i/3LyxgO2kp+PO/VcDYNqIHEMtkccqIhTXMKci4kjueJr/iCQEaT/Ww=="
},
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"requires": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
}
},
"follow-redirects": { "follow-redirects": {
"version": "1.5.10", "version": "1.5.10",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
...@@ -493,6 +747,11 @@ ...@@ -493,6 +747,11 @@
} }
} }
}, },
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"getpass": { "getpass": {
"version": "0.1.7", "version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
...@@ -525,14 +784,22 @@ ...@@ -525,14 +784,22 @@
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
}, },
"har-validator": { "har-validator": {
"version": "5.1.3", "version": "5.1.5",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
"requires": { "requires": {
"ajv": "^6.5.5", "ajv": "^6.12.3",
"har-schema": "^2.0.0" "har-schema": "^2.0.0"
} }
}, },
"has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
"requires": {
"ansi-regex": "^2.0.0"
}
},
"has-flag": { "has-flag": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
...@@ -543,6 +810,11 @@ ...@@ -543,6 +810,11 @@
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
}, },
"highlight.js": {
"version": "9.18.3",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.3.tgz",
"integrity": "sha512-zBZAmhSupHIl5sITeMqIJnYCDfAEc3Gdkqj65wC1lpI468MMQeeQkhcIAvk+RylAkxrCcI9xy9piHiXeQ1BdzQ=="
},
"http-signature": { "http-signature": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
...@@ -561,6 +833,11 @@ ...@@ -561,6 +833,11 @@
"safer-buffer": ">= 2.1.2 < 3" "safer-buffer": ">= 2.1.2 < 3"
} }
}, },
"ieee754": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="
},
"ignore-walk": { "ignore-walk": {
"version": "3.0.3", "version": "3.0.3",
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz", "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
...@@ -611,6 +888,12 @@ ...@@ -611,6 +888,12 @@
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
}, },
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
"optional": true
},
"isstream": { "isstream": {
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
...@@ -626,15 +909,24 @@ ...@@ -626,15 +909,24 @@
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
}, },
"js-yaml": {
"version": "3.14.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
"integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
}
},
"jsbn": { "jsbn": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
}, },
"json-parse-better-errors": { "json-parse-even-better-errors": {
"version": "1.0.2", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="
}, },
"json-schema": { "json-schema": {
"version": "0.2.3", "version": "0.2.3",
...@@ -690,6 +982,14 @@ ...@@ -690,6 +982,14 @@
} }
} }
}, },
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"requires": {
"p-locate": "^4.1.0"
}
},
"lru-cache": { "lru-cache": {
"version": "2.7.3", "version": "2.7.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
...@@ -752,9 +1052,9 @@ ...@@ -752,9 +1052,9 @@
} }
}, },
"moment": { "moment": {
"version": "2.26.0", "version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz", "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==" "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
}, },
"ms": { "ms": {
"version": "2.1.2", "version": "2.1.2",
...@@ -772,10 +1072,39 @@ ...@@ -772,10 +1072,39 @@
"rimraf": "~2.4.0" "rimraf": "~2.4.0"
} }
}, },
"mysql": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
"integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
"requires": {
"bignumber.js": "9.0.0",
"readable-stream": "2.3.7",
"safe-buffer": "5.1.2",
"sqlstring": "2.3.1"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"mz": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
"requires": {
"any-promise": "^1.0.0",
"object-assign": "^4.0.1",
"thenify-all": "^1.0.0"
}
},
"nan": { "nan": {
"version": "2.14.1", "version": "2.14.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
"integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
"optional": true
}, },
"natives": { "natives": {
"version": "1.1.6", "version": "1.1.6",
...@@ -789,15 +1118,74 @@ ...@@ -789,15 +1118,74 @@
"optional": true "optional": true
}, },
"needle": { "needle": {
"version": "2.5.0", "version": "2.5.2",
"resolved": "https://registry.npmjs.org/needle/-/needle-2.5.0.tgz", "resolved": "https://registry.npmjs.org/needle/-/needle-2.5.2.tgz",
"integrity": "sha512-o/qITSDR0JCyCKEQ1/1bnUXMmznxabbwi/Y4WwJElf+evwJNFNwIDMCCt5IigFVxgeGBJESLohGtIS9gEzo1fA==", "integrity": "sha512-LbRIwS9BfkPvNwNHlsA41Q29kL2L/6VaOJ0qisM5lLWsTV3nP15abO5ITL6L81zqFhzjRKDAYjpcBcwM0AVvLQ==",
"requires": { "requires": {
"debug": "^3.2.6", "debug": "^3.2.6",
"iconv-lite": "^0.4.4", "iconv-lite": "^0.4.4",
"sax": "^1.2.4" "sax": "^1.2.4"
} }
}, },
"node-addon-api": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.0.tgz",
"integrity": "sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA=="
},
"node-gyp": {
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
"integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==",
"optional": true,
"requires": {
"fstream": "^1.0.0",
"glob": "^7.0.3",
"graceful-fs": "^4.1.2",
"mkdirp": "^0.5.0",
"nopt": "2 || 3",
"npmlog": "0 || 1 || 2 || 3 || 4",
"osenv": "0",
"request": "^2.87.0",
"rimraf": "2",
"semver": "~5.3.0",
"tar": "^2.0.0",
"which": "1"
},
"dependencies": {
"fstream": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
"integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
"optional": true,
"requires": {
"graceful-fs": "^4.1.2",
"inherits": "~2.0.0",
"mkdirp": ">=0.5 0",
"rimraf": "2"
}
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"optional": true,
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"optional": true
}
}
},
"node-pre-gyp": { "node-pre-gyp": {
"version": "0.11.0", "version": "0.11.0",
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz", "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz",
...@@ -828,6 +1216,15 @@ ...@@ -828,6 +1216,15 @@
"path-is-absolute": "^1.0.0" "path-is-absolute": "^1.0.0"
} }
}, },
"nopt": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
"integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
"requires": {
"abbrev": "1",
"osenv": "^0.1.4"
}
},
"rimraf": { "rimraf": {
"version": "2.7.1", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
...@@ -835,16 +1232,30 @@ ...@@ -835,16 +1232,30 @@
"requires": { "requires": {
"glob": "^7.1.3" "glob": "^7.1.3"
} }
},
"tar": {
"version": "4.4.13",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
"integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.8.6",
"minizlib": "^1.2.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.3"
}
} }
} }
}, },
"nopt": { "nopt": {
"version": "4.0.3", "version": "3.0.6",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
"integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
"optional": true,
"requires": { "requires": {
"abbrev": "1", "abbrev": "1"
"osenv": "^0.1.4"
} }
}, },
"npm-bundled": { "npm-bundled": {
...@@ -933,22 +1344,66 @@ ...@@ -933,22 +1344,66 @@
"resolved": "https://registry.npmjs.org/over/-/over-0.0.5.tgz", "resolved": "https://registry.npmjs.org/over/-/over-0.0.5.tgz",
"integrity": "sha1-8phS5w/X4l82DgE6jsRMgq7bVwg=" "integrity": "sha1-8phS5w/X4l82DgE6jsRMgq7bVwg="
}, },
"p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"requires": {
"p-limit": "^2.2.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
},
"packet-reader": { "packet-reader": {
"version": "0.3.1", "version": "0.3.1",
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-0.3.1.tgz", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-0.3.1.tgz",
"integrity": "sha1-zWLmCvjX/qinBexP+ZCHHEaHHyc=" "integrity": "sha1-zWLmCvjX/qinBexP+ZCHHEaHHyc="
}, },
"parent-require": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/parent-require/-/parent-require-1.0.0.tgz",
"integrity": "sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc="
},
"parse-json": { "parse-json": {
"version": "5.0.0", "version": "5.1.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz",
"integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==",
"requires": { "requires": {
"@babel/code-frame": "^7.0.0", "@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1", "error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1", "json-parse-even-better-errors": "^2.3.0",
"lines-and-columns": "^1.1.6" "lines-and-columns": "^1.1.6"
} }
}, },
"parse5": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz",
"integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug=="
},
"parse5-htmlparser2-tree-adapter": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-5.1.1.tgz",
"integrity": "sha512-CF+TKjXqoqyDwHqBhFQ+3l5t83xYi6fVT1tQNg+Ye0JRLnTxWvIroCjEp1A0k4lneHNBGnICUf0cfYVYGEazqw==",
"requires": {
"parse5": "^5.1.1"
}
},
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
},
"path-is-absolute": { "path-is-absolute": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
...@@ -1074,6 +1529,11 @@ ...@@ -1074,6 +1529,11 @@
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
}, },
"q": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
"integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc="
},
"qs": { "qs": {
"version": "6.5.2", "version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
...@@ -1116,34 +1576,10 @@ ...@@ -1116,34 +1576,10 @@
} }
} }
}, },
"redis": { "reflect-metadata": {
"version": "3.0.2", "version": "0.1.13",
"resolved": "https://registry.npmjs.org/redis/-/redis-3.0.2.tgz", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
"integrity": "sha512-PNhLCrjU6vKVuMOyFu7oSP296mwBkcE6lrAjruBYG5LgdSqtRBoVQIylrMyVZD/lkF24RSNNatzvYag6HRBHjQ==", "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg=="
"requires": {
"denque": "^1.4.1",
"redis-commands": "^1.5.0",
"redis-errors": "^1.2.0",
"redis-parser": "^3.0.0"
}
},
"redis-commands": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz",
"integrity": "sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg=="
},
"redis-errors": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
"integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60="
},
"redis-parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
"integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=",
"requires": {
"redis-errors": "^1.0.0"
}
}, },
"request": { "request": {
"version": "2.88.2", "version": "2.88.2",
...@@ -1172,6 +1608,16 @@ ...@@ -1172,6 +1608,16 @@
"uuid": "^3.3.2" "uuid": "^3.3.2"
} }
}, },
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
},
"rimraf": { "rimraf": {
"version": "2.4.5", "version": "2.4.5",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz",
...@@ -1202,15 +1648,24 @@ ...@@ -1202,15 +1648,24 @@
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
}, },
"semver": { "semver": {
"version": "5.7.1", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8="
}, },
"set-blocking": { "set-blocking": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
}, },
"sha.js": {
"version": "2.4.11",
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
"integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
"requires": {
"inherits": "^2.0.1",
"safe-buffer": "^5.0.1"
}
},
"sigmund": { "sigmund": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
...@@ -1235,14 +1690,20 @@ ...@@ -1235,14 +1690,20 @@
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==" "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug=="
}, },
"sqlite3": { "sqlite3": {
"version": "4.2.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.2.0.tgz", "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.0.0.tgz",
"integrity": "sha512-roEOz41hxui2Q7uYnWsjMOTry6TcNUNmp8audCx18gF10P2NknwdpF+E+HKvz/F2NvPKGGBF4NGc+ZPQ+AABwg==", "integrity": "sha512-rjvqHFUaSGnzxDy2AHCwhHy6Zp6MNJzCPGYju4kD8yi6bze4d1/zMTg6C7JI49b7/EM7jKMTvyfN/4ylBKdwfw==",
"requires": { "requires": {
"nan": "^2.12.1", "node-addon-api": "2.0.0",
"node-gyp": "3.x",
"node-pre-gyp": "^0.11.0" "node-pre-gyp": "^0.11.0"
} }
}, },
"sqlstring": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
"integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A="
},
"sshpk": { "sshpk": {
"version": "1.16.1", "version": "1.16.1",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
...@@ -1316,17 +1777,50 @@ ...@@ -1316,17 +1777,50 @@
} }
}, },
"tar": { "tar": {
"version": "4.4.13", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
"integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
"optional": true,
"requires": { "requires": {
"chownr": "^1.1.1", "block-stream": "*",
"fs-minipass": "^1.2.5", "fstream": "^1.0.12",
"minipass": "^2.8.6", "inherits": "2"
"minizlib": "^1.2.1", },
"mkdirp": "^0.5.0", "dependencies": {
"safe-buffer": "^5.1.2", "fstream": {
"yallist": "^3.0.3" "version": "1.0.12",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
"integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
"optional": true,
"requires": {
"graceful-fs": "^4.1.2",
"inherits": "~2.0.0",
"mkdirp": ">=0.5 0",
"rimraf": "2"
}
},
"graceful-fs": {
"version": "4.2.4",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
"integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
"optional": true
}
}
},
"thenify": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
"requires": {
"any-promise": "^1.0.0"
}
},
"thenify-all": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
"integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=",
"requires": {
"thenify": ">= 3.1.0 < 4"
} }
}, },
"through": { "through": {
...@@ -1348,6 +1842,11 @@ ...@@ -1348,6 +1842,11 @@
"resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
"integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=" "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk="
}, },
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
},
"tunnel-agent": { "tunnel-agent": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
...@@ -1366,15 +1865,114 @@ ...@@ -1366,15 +1865,114 @@
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
"integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==" "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg=="
}, },
"typeorm": {
"version": "0.2.29",
"resolved": "https://registry.npmjs.org/typeorm/-/typeorm-0.2.29.tgz",
"integrity": "sha512-ih1vrTe3gEAGKRcWlcsTRxTL7gNjacQE498wVGuJ3ZRujtMqPZlbAWuC7xDzWCRjQnkZYNwZQeG9UgKfxSHB5g==",
"requires": {
"@sqltools/formatter": "1.2.2",
"app-root-path": "^3.0.0",
"buffer": "^5.5.0",
"chalk": "^4.1.0",
"cli-highlight": "^2.1.4",
"debug": "^4.1.1",
"dotenv": "^8.2.0",
"glob": "^7.1.6",
"js-yaml": "^3.14.0",
"mkdirp": "^1.0.4",
"reflect-metadata": "^0.1.13",
"sha.js": "^2.4.11",
"tslib": "^1.13.0",
"xml2js": "^0.4.23",
"yargonaut": "^1.1.2",
"yargs": "^16.0.3"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"debug": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
"requires": {
"ms": "2.1.2"
}
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
},
"mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"typescript": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.5.tgz",
"integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ=="
},
"ultron": { "ultron": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
"integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=" "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po="
}, },
"underscore": { "underscore": {
"version": "1.10.2", "version": "1.11.0",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.11.0.tgz",
"integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==" "integrity": "sha512-xY96SsN3NA461qIRKZ/+qox37YXPtSBswMGfiNptr+wrt6ds4HaMw23TP612fEyGekRE6LNRiLYr/aqbHXNedw=="
}, },
"underscore.string": { "underscore.string": {
"version": "3.3.5", "version": "3.3.5",
...@@ -1396,9 +1994,9 @@ ...@@ -1396,9 +1994,9 @@
} }
}, },
"uri-js": { "uri-js": {
"version": "4.2.2", "version": "4.4.0",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
"requires": { "requires": {
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
...@@ -1423,6 +2021,20 @@ ...@@ -1423,6 +2021,20 @@
"extsprintf": "^1.2.0" "extsprintf": "^1.2.0"
} }
}, },
"which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"optional": true,
"requires": {
"isexe": "^2.0.0"
}
},
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"wide-align": { "wide-align": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
...@@ -1431,6 +2043,67 @@ ...@@ -1431,6 +2043,67 @@
"string-width": "^1.0.2 || 2" "string-width": "^1.0.2 || 2"
} }
}, },
"wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"requires": {
"color-convert": "^2.0.1"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"string-width": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"requires": {
"ansi-regex": "^5.0.0"
}
}
}
},
"wrappy": { "wrappy": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
...@@ -1445,15 +2118,172 @@ ...@@ -1445,15 +2118,172 @@
"ultron": "1.0.x" "ultron": "1.0.x"
} }
}, },
"xml2js": {
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
"integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
"requires": {
"sax": ">=0.6.0",
"xmlbuilder": "~11.0.0"
}
},
"xmlbuilder": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
},
"xtend": { "xtend": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
}, },
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
},
"yallist": { "yallist": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
},
"yargonaut": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/yargonaut/-/yargonaut-1.1.4.tgz",
"integrity": "sha512-rHgFmbgXAAzl+1nngqOcwEljqHGG9uUZoPjsdZEs1w5JW9RXYzrSvH/u70C1JE5qFi0qjsdhnUX/dJRpWqitSA==",
"requires": {
"chalk": "^1.1.1",
"figlet": "^1.1.1",
"parent-require": "^1.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
},
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"requires": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
}
},
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
}
}
},
"yargs": {
"version": "16.1.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.1.0.tgz",
"integrity": "sha512-upWFJOmDdHN0syLuESuvXDmrRcWd1QafJolHskzaw79uZa7/x53gxQKiR07W59GWY1tFhhU/Th9DrtSfpS782g==",
"requires": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.0",
"y18n": "^5.0.2",
"yargs-parser": "^20.2.2"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"requires": {
"color-convert": "^2.0.1"
}
},
"cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^7.0.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"string-width": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"requires": {
"ansi-regex": "^5.0.0"
}
},
"wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
}
},
"y18n": {
"version": "5.0.5",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz",
"integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg=="
},
"yargs-parser": {
"version": "20.2.4",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
"integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA=="
}
}
},
"yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
} }
} }
} }
...@@ -11,29 +11,35 @@ ...@@ -11,29 +11,35 @@
], ],
"author": "zh99998 <zh99998@gmail.com>, mercury233 <me@mercury233.me>, Nanahira <78877@qq.com>", "author": "zh99998 <zh99998@gmail.com>, mercury233 <me@mercury233.me>, Nanahira <78877@qq.com>",
"dependencies": { "dependencies": {
"@types/bunyan": "^1.8.6",
"@types/node": "^14.0.13", "@types/node": "^14.0.13",
"@types/underscore": "^1.10.0", "@types/underscore": "^1.10.24",
"async": "^3.2.0", "async": "^3.2.0",
"axios": "^0.19.2", "axios": "^0.19.2",
"bunyan": "latest", "bunyan": "^1.8.14",
"challonge": "latest", "challonge": "latest",
"coffeescript": "^2.5.1",
"deepmerge": "latest", "deepmerge": "latest",
"formidable": "latest", "formidable": "latest",
"geoip-country-lite": "latest", "geoip-country-lite": "latest",
"load-json-file": "latest", "load-json-file": "latest",
"lzma": "^2.3.2", "lzma": "^2.3.2",
"moment": "latest", "moment": "^2.29.1",
"mysql": "^2.18.1",
"pg": "^6.4.2", "pg": "^6.4.2",
"q": "^1.5.1",
"querystring": "^0.2.0", "querystring": "^0.2.0",
"redis": "latest",
"request": "latest", "request": "latest",
"sqlite3": "latest", "sqlite3": "latest",
"underscore": "latest", "typeorm": "^0.2.29",
"typescript": "^4.0.5",
"underscore": "^1.11.0",
"underscore.string": "latest", "underscore.string": "latest",
"ws": "^1.1.1" "ws": "^1.1.1"
}, },
"license": "AGPL-3.0", "license": "AGPL-3.0",
"scripts": { "scripts": {
"build": "coffee -c *.coffee && tsc",
"start": "node ygopro-server.js", "start": "node ygopro-server.js",
"tournament": "node ygopro-tournament.js", "tournament": "node ygopro-tournament.js",
"pre": "node ygopro-pre.js", "pre": "node ygopro-pre.js",
......
...@@ -4,11 +4,15 @@ ...@@ -4,11 +4,15 @@
"target": "esnext", "target": "esnext",
"esModuleInterop": true, "esModuleInterop": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": true "sourceMap": true
}, },
"compileOnSave": true, "compileOnSave": true,
"allowJs": true, "allowJs": true,
"include": [ "include": [
"*.ts" "*.ts",
"data-manager/*.ts",
"data-manager/entities/*.ts"
] ]
} }
...@@ -59,7 +59,7 @@ add_log = (message) -> ...@@ -59,7 +59,7 @@ add_log = (message) ->
text = mt.format('YYYY-MM-DD HH:mm:ss') + " --> " + message + "\n" text = mt.format('YYYY-MM-DD HH:mm:ss') + " --> " + message + "\n"
res = false res = false
try try
await util.promisify(fs.appendFile)("./logs/"+mt.format('YYYY-MM-DD')+".log", text) await fs.promises.appendFile("./logs/"+mt.format('YYYY-MM-DD')+".log", text)
res = true res = true
catch catch
res = false res = false
...@@ -69,7 +69,7 @@ add_log = (message) -> ...@@ -69,7 +69,7 @@ add_log = (message) ->
default_data = loadJSON('./data/default_data.json') default_data = loadJSON('./data/default_data.json')
setting_save = (settings) -> setting_save = (settings) ->
try try
await util.promisify(fs.writeFile)(settings.file, JSON.stringify(settings, null, 2)) await fs.promises.writeFile(settings.file, JSON.stringify(settings, null, 2))
catch e catch e
add_log("save fail"); add_log("save fail");
return return
......
...@@ -73,7 +73,7 @@ ...@@ -73,7 +73,7 @@
text = mt.format('YYYY-MM-DD HH:mm:ss') + " --> " + message + "\n"; text = mt.format('YYYY-MM-DD HH:mm:ss') + " --> " + message + "\n";
res = false; res = false;
try { try {
await util.promisify(fs.appendFile)("./logs/" + mt.format('YYYY-MM-DD') + ".log", text); await fs.promises.appendFile("./logs/" + mt.format('YYYY-MM-DD') + ".log", text);
res = true; res = true;
} catch (error) { } catch (error) {
res = false; res = false;
...@@ -86,7 +86,7 @@ ...@@ -86,7 +86,7 @@
setting_save = async function(settings) { setting_save = async function(settings) {
var e; var e;
try { try {
await util.promisify(fs.writeFile)(settings.file, JSON.stringify(settings, null, 2)); await fs.promises.writeFile(settings.file, JSON.stringify(settings, null, 2));
} catch (error) { } catch (error) {
e = error; e = error;
add_log("save fail"); add_log("save fail");
......
...@@ -20,6 +20,8 @@ _.mixin(_.str.exports()) ...@@ -20,6 +20,8 @@ _.mixin(_.str.exports())
request = require 'request' request = require 'request'
axios = require 'axios' axios = require 'axios'
qs = require "querystring" qs = require "querystring"
zlib = require 'zlib'
axios = require 'axios'
bunyan = require 'bunyan' bunyan = require 'bunyan'
log = global.log = bunyan.createLogger name: "mycard" log = global.log = bunyan.createLogger name: "mycard"
...@@ -82,35 +84,66 @@ merge = require 'deepmerge' ...@@ -82,35 +84,66 @@ merge = require 'deepmerge'
loadJSON = require('load-json-file').sync loadJSON = require('load-json-file').sync
loadJSONAsync = require('load-json-file')
util = require("util") util = require("util")
Q = require("q")
#heapdump = require 'heapdump' #heapdump = require 'heapdump'
# 配置 checkFileExists = (path) =>
# 导入旧配置 try
if not fs.existsSync('./config') await fs.promises.access(path)
fs.mkdirSync('./config') return true
try catch e
oldconfig=loadJSON('./config.user.json') return false
createDirectoryIfNotExists = (path) =>
try
if path and !await checkFileExists(path)
await fs.promises.mkdir(path, {recursive: true})
catch e
log.warn("Failed to create directory #{path}: #{e.toString()}")
setting_save = global.setting_save = (settings) ->
try
await fs.promises.writeFile(settings.file, JSON.stringify(settings, null, 2))
catch e
log.warn("setting save fail", e.toString())
return
setting_change = global.setting_change = (settings, path, val) ->
# path should be like "modules:welcome"
log.info("setting changed", path, val) if _.isString(val)
path=path.split(':')
if path.length == 0
settings[path[0]]=val
else
target=settings
while path.length > 1
key=path.shift()
target=target[key]
key = path.shift()
target[key] = val
await setting_save(settings)
return
importOldConfig = () ->
try
oldconfig=await loadJSONAsync('./config.user.json')
if oldconfig.tips if oldconfig.tips
oldtips = {} oldtips = {}
oldtips.file = './config/tips.json' oldtips.file = './config/tips.json'
oldtips.tips = oldconfig.tips oldtips.tips = oldconfig.tips
fs.writeFileSync(oldtips.file, JSON.stringify(oldtips, null, 2)) await fs.promises.writeFile(oldtips.file, JSON.stringify(oldtips, null, 2))
delete oldconfig.tips delete oldconfig.tips
if oldconfig.dialogues if oldconfig.dialogues
olddialogues = {} olddialogues = {}
olddialogues.file = './config/dialogues.json' olddialogues.file = './config/dialogues.json'
olddialogues.dialogues = oldconfig.dialogues olddialogues.dialogues = oldconfig.dialogues
fs.writeFileSync(olddialogues.file, JSON.stringify(olddialogues, null, 2)) await fs.promises.writeFile(olddialogues.file, JSON.stringify(olddialogues, null, 2))
delete oldconfig.dialogues delete oldconfig.dialogues
if oldconfig.modules
if oldconfig.modules.tournament_mode and oldconfig.modules.tournament_mode.duel_log
oldduellog = {}
oldduellog.file = './config/duel_log.json'
oldduellog.duel_log = oldconfig.modules.tournament_mode.duel_log
fs.writeFileSync(oldduellog.file, JSON.stringify(oldduellog, null, 2))
delete oldconfig.oldduellog
oldbadwords={} oldbadwords={}
if oldconfig.ban if oldconfig.ban
if oldconfig.ban.badword_level0 if oldconfig.ban.badword_level0
...@@ -123,72 +156,98 @@ try ...@@ -123,72 +156,98 @@ try
oldbadwords.level3 = oldconfig.ban.badword_level3 oldbadwords.level3 = oldconfig.ban.badword_level3
if not _.isEmpty(oldbadwords) if not _.isEmpty(oldbadwords)
oldbadwords.file = './config/badwords.json' oldbadwords.file = './config/badwords.json'
fs.writeFileSync(oldbadwords.file, JSON.stringify(oldbadwords, null, 2)) await fs.promises.writeFile(oldbadwords.file, JSON.stringify(oldbadwords, null, 2))
delete oldconfig.ban.badword_level0 delete oldconfig.ban.badword_level0
delete oldconfig.ban.badword_level1 delete oldconfig.ban.badword_level1
delete oldconfig.ban.badword_level2 delete oldconfig.ban.badword_level2
delete oldconfig.ban.badword_level3 delete oldconfig.ban.badword_level3
if not _.isEmpty(oldconfig) if not _.isEmpty(oldconfig)
# log.info oldconfig # log.info oldconfig
fs.writeFileSync('./config/config.json', JSON.stringify(oldconfig, null, 2)) await fs.promises.writeFile('./config/config.json', JSON.stringify(oldconfig, null, 2))
log.info 'imported old config from config.user.json' log.info 'imported old config from config.user.json'
fs.renameSync('./config.user.json', './config.user.bak') await fs.promises.rename('./config.user.json', './config.user.bak')
catch e catch e
log.info e unless e.code == 'ENOENT' log.info e unless e.code == 'ENOENT'
setting_save = global.setting_save = (settings, callback) -> auth = global.auth = require './ygopro-auth.js'
if !callback ygopro = global.ygopro = require './ygopro.js'
callback = (err) -> roomlist = null
if(err)
log.warn("setting save fail", err.toString())
fs.writeFile(settings.file, JSON.stringify(settings, null, 2), callback)
return
setting_change = global.setting_change = (settings, path, val, callback) -> settings = {}
# path should be like "modules:welcome" tips = null
log.info("setting changed", path, val) if _.isString(val) dialogues = null
path=path.split(':') badwords = null
if path.length == 0 lflists = global.lflists = []
settings[path[0]]=val real_windbot_server_ip = null
else long_resolve_cards = []
target=settings ReplayParser = null
while path.length > 1 athleticChecker = null
key=path.shift() users_cache = {}
target=target[key] geoip = null
key = path.shift() dataManager = null
target[key] = val windbots = []
setting_save(settings, callback) disconnect_list = {} # {old_client, old_server, room_id, timeout, deckbuf}
return
# 读取配置 challonge = null
default_config = loadJSON('./data/default_config.json') challonge_cache = {
if fs.existsSync('./config/config.json') participants: null
matches: null
}
challonge_queue_callbacks = {
participants: []
matches: []
}
is_challonge_requesting = {
participants: null
matches: null
}
get_callback = () ->
replaced_index = () ->
refresh_challonge_cache = () ->
class ResolveData
constructor: (@func) ->
resolved: false
resolve: (err, data) ->
if @resolved
return false
@resolved = true
@func(err, data)
return true
loadLFList = (path) ->
try try
config = loadJSON('./config/config.json') for list in (await fs.promises.readFile(path, 'utf8')).match(/!.*/g)
date=list.match(/!([\d\.]+)/)
continue unless date
lflists.push({date: moment(list.match(/!([\d\.]+)/)[1], 'YYYY.MM.DD').utcOffset("-08:00"), tcg: list.indexOf('TCG') != -1})
catch
init = () ->
await createDirectoryIfNotExists("./config")
await importOldConfig()
defaultConfig = await loadJSONAsync('./data/default_config.json')
if await checkFileExists("./config/config.json")
try
config = await loadJSONAsync('./config/config.json')
catch e catch e
console.error("Failed reading config: ", e.toString()) console.error("Failed reading config: ", e.toString())
process.exit(1) process.exit(1)
else else
config = {} config = {}
settings = global.settings = merge(defaultConfig, config, { arrayMerge: (destination, source) -> source })
settings = global.settings = merge(default_config, config, { arrayMerge: (destination, source) -> source }) #import old configs
imported = false
auth = global.auth = require './ygopro-auth.js' #reset http.quick_death_rule from true to 1
if settings.modules.http.quick_death_rule == true
#import old configs
imported = false
#reset http.quick_death_rule from true to 1
if settings.modules.http.quick_death_rule == true
settings.modules.http.quick_death_rule = 1 settings.modules.http.quick_death_rule = 1
imported = true imported = true
#import the old redis port #import the old passwords to new admin user system
if settings.modules.cloud_replay.redis_port if settings.modules.http.password
settings.modules.cloud_replay.redis.port = settings.modules.cloud_replay.redis_port await auth.add_user("olduser", settings.modules.http.password, true, {
delete settings.modules.cloud_replay.redis_port
imported = true
#import the old passwords to new admin user system
if settings.modules.http.password
auth.add_user("olduser", settings.modules.http.password, true, {
"get_rooms": true, "get_rooms": true,
"shout": true, "shout": true,
"stop": true, "stop": true,
...@@ -199,8 +258,8 @@ if settings.modules.http.password ...@@ -199,8 +258,8 @@ if settings.modules.http.password
}) })
delete settings.modules.http.password delete settings.modules.http.password
imported = true imported = true
if settings.modules.tournament_mode.password if settings.modules.tournament_mode.password
auth.add_user("tournament", settings.modules.tournament_mode.password, true, { await auth.add_user("tournament", settings.modules.tournament_mode.password, true, {
"duel_log": true, "duel_log": true,
"download_replay": true, "download_replay": true,
"clear_duel_log": true, "clear_duel_log": true,
...@@ -209,136 +268,124 @@ if settings.modules.tournament_mode.password ...@@ -209,136 +268,124 @@ if settings.modules.tournament_mode.password
}) })
delete settings.modules.tournament_mode.password delete settings.modules.tournament_mode.password
imported = true imported = true
if settings.modules.pre_util.password if settings.modules.pre_util.password
auth.add_user("pre", settings.modules.pre_util.password, true, { await auth.add_user("pre", settings.modules.pre_util.password, true, {
"pre_dashboard": true "pre_dashboard": true
}) })
delete settings.modules.pre_util.password delete settings.modules.pre_util.password
imported = true imported = true
if settings.modules.update_util.password if settings.modules.update_util.password
auth.add_user("update", settings.modules.update_util.password, true, { await auth.add_user("update", settings.modules.update_util.password, true, {
"update_dashboard": true "update_dashboard": true
}) })
delete settings.modules.update_util.password delete settings.modules.update_util.password
imported = true imported = true
#import the old enable_priority hostinfo #import the old enable_priority hostinfo
if settings.hostinfo.enable_priority or settings.hostinfo.enable_priority == false if settings.hostinfo.enable_priority or settings.hostinfo.enable_priority == false
if settings.hostinfo.enable_priority if settings.hostinfo.enable_priority
settings.hostinfo.duel_rule = 3 settings.hostinfo.duel_rule = 3
else else
settings.hostinfo.duel_rule = 5 settings.hostinfo.duel_rule = 5
delete settings.hostinfo.enable_priority delete settings.hostinfo.enable_priority
imported = true imported = true
#import the old Challonge api key option #import the old Challonge api key option
if settings.modules.challonge.api_key if settings.modules.challonge.api_key
settings.modules.challonge.options.apiKey = settings.modules.challonge.api_key settings.modules.challonge.options.apiKey = settings.modules.challonge.api_key
delete settings.modules.challonge.api_key delete settings.modules.challonge.api_key
imported = true imported = true
#import the old random_duel.blank_pass_match option #import the old random_duel.blank_pass_match option
if settings.modules.random_duel.blank_pass_match == true if settings.modules.random_duel.blank_pass_match == true
settings.modules.random_duel.blank_pass_modes = {"S":true,"M":true,"T":false} settings.modules.random_duel.blank_pass_modes = {"S":true,"M":true,"T":false}
delete settings.modules.random_duel.blank_pass_match delete settings.modules.random_duel.blank_pass_match
imported = true imported = true
if settings.modules.random_duel.blank_pass_match == false if settings.modules.random_duel.blank_pass_match == false
settings.modules.random_duel.blank_pass_modes = {"S":true,"M":false,"T":false} settings.modules.random_duel.blank_pass_modes = {"S":true,"M":false,"T":false}
delete settings.modules.random_duel.blank_pass_match delete settings.modules.random_duel.blank_pass_match
imported = true imported = true
#finish #finish
if imported if imported
setting_save(settings) await setting_save(settings)
if settings.modules.mysql.enabled
# 读取数据 DataManager = require('./data-manager/DataManager.js').DataManager
default_data = loadJSON('./data/default_data.json') dataManager = global.dataManager = new DataManager(settings.modules.mysql.db, log)
try await dataManager.init()
tips = global.tips = loadJSON('./config/tips.json') else
catch log.warn("Some functions may be limited without MySQL .")
if settings.modules.cloud_replay.enabled
settings.modules.cloud_replay.enabled = false
await setting_save(settings)
log.warn("Cloud replay cannot be enabled because no MySQL.")
if settings.modules.enable_recover.enabled
settings.modules.enable_recover.enabled = false
await setting_save(settings)
log.warn("Recover mode cannot be enabled because no MySQL.")
if settings.modules.chat_color.enabled
settings.modules.chat_color.enabled = false
await setting_save(settings)
log.warn("Chat color cannot be enabled because no MySQL.")
if settings.modules.random_duel.record_match_scores
settings.modules.random_duel.record_match_scores = false
await setting_save(settings)
log.warn("Cannot record random match scores because no MySQL.")
# 读取数据
default_data = await loadJSONAsync('./data/default_data.json')
try
tips = global.tips = await loadJSONAsync('./config/tips.json')
catch
tips = global.tips = default_data.tips tips = global.tips = default_data.tips
setting_save(tips) await setting_save(tips)
try try
dialogues = global.dialogues = loadJSON('./config/dialogues.json') dialogues = global.dialogues = await loadJSONAsync('./config/dialogues.json')
catch catch
dialogues = global.dialogues = default_data.dialogues dialogues = global.dialogues = default_data.dialogues
setting_save(dialogues) await setting_save(dialogues)
try try
badwords = global.badwords = loadJSON('./config/badwords.json') badwords = global.badwords = await loadJSONAsync('./config/badwords.json')
catch catch
badwords = global.badwords = default_data.badwords badwords = global.badwords = default_data.badwords
setting_save(badwords) await setting_save(badwords)
try if settings.modules.chat_color.enabled and await checkFileExists('./config/chat_color.json')
duel_log = global.duel_log = loadJSON('./config/duel_log.json') try
catch chat_color = await loadJSONAsync('./config/chat_color.json')
duel_log = global.duel_log = default_data.duel_log if chat_color
setting_save(duel_log) await dataManager.migrateChatColors(chat_color.save_list);
try await fs.promises.rename('./config/chat_color.json', './config/chat_color.json.bak')
chat_color = global.chat_color = loadJSON('./config/chat_color.json') log.info("Chat color migrated.")
catch catch
chat_color = global.chat_color = default_data.chat_color try
setting_save(chat_color) cppversion = parseInt(await fs.promises.readFile('ygopro/gframe/game.cpp', 'utf8').match(/PRO_VERSION = ([x\dABCDEF]+)/)[1], '16')
await setting_change(settings, "version", cppversion)
try
cppversion = parseInt(fs.readFileSync('ygopro/gframe/game.cpp', 'utf8').match(/PRO_VERSION = ([x\dABCDEF]+)/)[1], '16')
setting_change(settings, "version", cppversion)
log.info "ygopro version 0x"+settings.version.toString(16), "(from source code)" log.info "ygopro version 0x"+settings.version.toString(16), "(from source code)"
catch catch
#settings.version = settings.version_default #settings.version = settings.version_default
log.info "ygopro version 0x"+settings.version.toString(16), "(from config)" log.info "ygopro version 0x"+settings.version.toString(16), "(from config)"
# load the lflist of current date # load the lflist of current date
lflists = global.lflists = [] await loadLFList('ygopro/expansions/lflist.conf')
# expansions/lflist await loadLFList('ygopro/lflist.conf')
try
for list in fs.readFileSync('ygopro/expansions/lflist.conf', 'utf8').match(/!.*/g)
date=list.match(/!([\d\.]+)/)
continue unless date
lflists.push({date: moment(list.match(/!([\d\.]+)/)[1], 'YYYY.MM.DD').utcOffset("-08:00"), tcg: list.indexOf('TCG') != -1})
catch
# lflist
try
for list in fs.readFileSync('ygopro/lflist.conf', 'utf8').match(/!.*/g)
date=list.match(/!([\d\.]+)/)
continue unless date
lflists.push({date: moment(list.match(/!([\d\.]+)/)[1], 'YYYY.MM.DD').utcOffset("-08:00"), tcg: list.indexOf('TCG') != -1})
catch
if settings.modules.cloud_replay.enabled if settings.modules.windbot.enabled
redis = require 'redis' windbots = global.windbots = (await loadJSONAsync(settings.modules.windbot.botlist)).windbots
zlib = require 'zlib'
redisdb = global.redisdb = redis.createClient(settings.modules.cloud_replay.redis)
redisdb.on 'error', (err)->
log.warn err
return
if settings.modules.windbot.enabled
windbots = global.windbots = loadJSON(settings.modules.windbot.botlist).windbots
real_windbot_server_ip = global.real_windbot_server_ip = settings.modules.windbot.server_ip real_windbot_server_ip = global.real_windbot_server_ip = settings.modules.windbot.server_ip
if !settings.modules.windbot.server_ip.includes("127.0.0.1") if !settings.modules.windbot.server_ip.includes("127.0.0.1")
dns = require('dns') dns = require('dns')
dns.lookup(settings.modules.windbot.server_ip,(err,addr) -> real_windbot_server_ip = global.real_windbot_server_ip = await util.promisify(dns.lookup)(settings.modules.windbot.server_ip)
if(!err) if settings.modules.heartbeat_detection.enabled
real_windbot_server_ip = global.real_windbot_server_ip = addr long_resolve_cards = global.long_resolve_cards = await loadJSONAsync('./data/long_resolve_cards.json')
)
if settings.modules.heartbeat_detection.enabled
long_resolve_cards = global.long_resolve_cards = loadJSON('./data/long_resolve_cards.json')
if settings.modules.tournament_mode.enable_recover if settings.modules.tournament_mode.enable_recover
ReplayParser = global.ReplayParser = require "./Replay.js" ReplayParser = global.ReplayParser = require "./Replay.js"
if settings.modules.athletic_check.enabled if settings.modules.athletic_check.enabled
AthleticChecker = require("./athletic-check.js").AthleticChecker AthleticChecker = require("./athletic-check.js").AthleticChecker
athleticChecker = global.athleticChecker = new AthleticChecker(settings.modules.athletic_check) athleticChecker = global.athleticChecker = new AthleticChecker(settings.modules.athletic_check)
# 组件 if settings.modules.http.websocket_roomlist
ygopro = global.ygopro = require './ygopro.js' roomlist = global.roomlist = require './roomlist.js'
roomlist = global.roomlist = require './roomlist.js' if settings.modules.http.websocket_roomlist if settings.modules.i18n.auto_pick
if settings.modules.i18n.auto_pick
geoip = require('geoip-country-lite') geoip = require('geoip-country-lite')
# cache users of mycard login if settings.modules.mycard.enabled
users_cache = {}
if settings.modules.mycard.enabled
pgClient = require('pg').Client pgClient = require('pg').Client
pg_client = global.pg_client = new pgClient(settings.modules.mycard.auth_database) pg_client = global.pg_client = new pgClient(settings.modules.mycard.auth_database)
pg_client.on 'error', (err) -> pg_client.on 'error', (err) ->
...@@ -359,35 +406,22 @@ if settings.modules.mycard.enabled ...@@ -359,35 +406,22 @@ if settings.modules.mycard.enabled
log.info "loading mycard user..." log.info "loading mycard user..."
pg_client.connect() pg_client.connect()
if settings.modules.arena_mode.enabled and settings.modules.arena_mode.init_post.enabled if settings.modules.arena_mode.enabled and settings.modules.arena_mode.init_post.enabled
request.post { url : settings.modules.arena_mode.init_post.url , qs : { postData = qs.stringify({
ak: settings.modules.arena_mode.init_post.accesskey, ak: settings.modules.arena_mode.init_post.accesskey,
arena: settings.modules.arena_mode.mode arena: settings.modules.arena_mode.mode
}}, (error, response, body)=> })
if error try
log.warn 'ARENA INIT POST ERROR', error await axios.post(settings.modules.arena_mode.init_post.url + "?" + postData, {
else responseType: "json"
if response.statusCode >= 400 })
log.warn 'ARENA INIT POST FAIL', response.statusCode, response.statusMessage, body catch e
#else log.warn 'ARENA INIT POST ERROR', e
# log.info 'ARENA INIT POST OK', response.statusCode, response.statusMessage
return
class ResolveData
constructor: (@func) ->
resolved: false
resolve: (err, data) ->
if @resolved
return false
@resolved = true
@func(err, data)
return true
if settings.modules.challonge.enabled if settings.modules.challonge.enabled
challonge_module_name = 'challonge' challonge_module_name = 'challonge'
if settings.modules.challonge.use_custom_module if settings.modules.challonge.use_custom_module
challonge_module_name = settings.modules.challonge.use_custom_module challonge_module_name = settings.modules.challonge.use_custom_module
challonge = global.challonge = require(challonge_module_name).createClient(settings.modules.challonge.options) challonge = global.challonge = require(challonge_module_name).createClient(settings.modules.challonge.options)
if settings.modules.challonge.cache_ttl
challonge_cache = { challonge_cache = {
participants: null participants: null
matches: null matches: null
...@@ -440,94 +474,181 @@ if settings.modules.challonge.enabled ...@@ -440,94 +474,181 @@ if settings.modules.challonge.enabled
challonge_cache.matches = null challonge_cache.matches = null
return return
refresh_challonge_cache() refresh_challonge_cache()
# challonge.participants._index({
# id: settings.modules.challonge.tournament_id,
# callback: (() ->
# challonge.matches._index({
# id: settings.modules.challonge.tournament_id,
# callback: (() ->
# return
# )
# })
# return
# )
# })
if settings.modules.challonge.cache_ttl if settings.modules.challonge.cache_ttl
setInterval(refresh_challonge_cache, settings.modules.challonge.cache_ttl) setInterval(refresh_challonge_cache, settings.modules.challonge.cache_ttl)
if settings.modules.tips.get
load_tips()
if settings.modules.tips.enabled
setInterval ()->
for room in ROOM_all when room and room.established
ygopro.stoc_send_random_tip_to_room(room) if room.duel_stage == ygopro.constants.DUEL_STAGE.SIDING or room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN
return
, 30000
if settings.modules.dialogues.get
load_dialogues()
if settings.modules.random_duel.post_match_scores and settings.modules.mysql.enabled
setInterval(()->
scores = await dataManager.getRandomScoreTop10()
try
await axios.post(settings.modules.random_duel.post_match_scores, {
headers: { 'content-type': 'application/x-www-form-urlencoded' },
data: qs.stringify({
accesskey: settings.modules.random_duel.post_match_accesskey,
rank: JSON.stringify(scores)
responseType: "json"
})
})
catch e
log.warn 'RANDOM SCORE POST ERROR', e.toString()
return
, 60000)
if settings.modules.random_duel.enabled
setInterval ()->
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and room.random_type and room.last_active_time and room.waiting_for_player and room.get_disconnected_count() == 0 and (!settings.modules.side_timeout or room.duel_stage != ygopro.constants.DUEL_STAGE.SIDING) and !room.recovered
time_passed = Math.floor((moment() - room.last_active_time) / 1000)
#log.info time_passed
if time_passed >= settings.modules.random_duel.hang_timeout
room.last_active_time = moment()
await ROOM_ban_player(room.waiting_for_player.name, room.waiting_for_player.ip, "${random_ban_reason_AFK}")
room.scores[room.waiting_for_player.name_vpass] = -9
#log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED)
CLIENT_send_replays(room.waiting_for_player, room)
CLIENT_kick(room.waiting_for_player)
else if time_passed >= (settings.modules.random_duel.hang_timeout - 20) and not (time_passed % 10)
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${afk_warn_part1}#{settings.modules.random_duel.hang_timeout - time_passed}${afk_warn_part2}", ygopro.constants.COLORS.RED)
ROOM_unwelcome(room, room.waiting_for_player, "${random_ban_reason_AFK}")
return
, 1000
if settings.modules.mycard.enabled
setInterval ()->
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and room.arena and room.last_active_time and room.waiting_for_player and room.get_disconnected_count() == 0 and (!settings.modules.side_timeout or room.duel_stage != ygopro.constants.DUEL_STAGE.SIDING) and !room.recovered
time_passed = Math.floor((moment() - room.last_active_time) / 1000)
#log.info time_passed
if time_passed >= settings.modules.random_duel.hang_timeout
room.last_active_time = moment()
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED)
room.scores[room.waiting_for_player.name_vpass] = -9
#log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
CLIENT_send_replays(room.waiting_for_player, room)
CLIENT_kick(room.waiting_for_player)
else if time_passed >= (settings.modules.random_duel.hang_timeout - 20) and not (time_passed % 10)
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${afk_warn_part1}#{settings.modules.random_duel.hang_timeout - time_passed}${afk_warn_part2}", ygopro.constants.COLORS.RED)
if true # settings.modules.arena_mode.punish_quit_before_match
for room in ROOM_all when room and room.arena and room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and room.get_playing_player().length < 2
player = room.get_playing_player()[0]
if player and player.join_time and !player.arena_quit_free
waited_time = moment() - player.join_time
if waited_time >= 30000
ygopro.stoc_send_chat(player, "${arena_wait_timeout}", ygopro.constants.COLORS.BABYBLUE)
player.arena_quit_free = true
else if waited_time >= 5000 and waited_time < 6000
ygopro.stoc_send_chat(player, "${arena_wait_hint}", ygopro.constants.COLORS.BABYBLUE)
return
, 1000
if settings.modules.heartbeat_detection.enabled
setInterval ()->
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and (room.hostinfo.time_limit == 0 or room.duel_stage != ygopro.constants.DUEL_STAGE.DUELING) and !room.windbot
for player in room.get_playing_player() when player and (room.duel_stage != ygopro.constants.DUEL_STAGE.SIDING or player.selected_preduel)
CLIENT_heartbeat_register(player, true)
return
, settings.modules.heartbeat_detection.interval
if settings.modules.windbot.enabled and settings.modules.windbot.spawn
spawn_windbot()
setInterval ()->
current_time = moment()
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and room.hostinfo.auto_death and !room.auto_death_triggered and current_time - moment(room.start_time) > 60000 * room.hostinfo.auto_death
room.auto_death_triggered = true
room.start_death()
, 1000
net.createServer(netRequestHandler).listen settings.port, ->
log.info "server started", settings.port
return
if settings.modules.stop
log.info "NOTE: server not open due to config, ", settings.modules.stop
http_server = http.createServer(httpRequestListener)
http_server.listen settings.modules.http.port
if settings.modules.http.ssl.enabled
https = require 'https'
options =
cert: await fs.promises.readFile(settings.modules.http.ssl.cert)
key: await fs.promises.readFile(settings.modules.http.ssl.key)
https_server = https.createServer(options, httpRequestListener)
if settings.modules.http.websocket_roomlist and roomlist
roomlist.init https_server, ROOM_all
https_server.listen settings.modules.http.ssl.port
mkdirList = [
"./plugins",
settings.modules.tournament_mode.deck_path,
settings.modules.tournament_mode.replay_path,
settings.modules.tournament_mode.log_save_path,
settings.modules.deck_log.local
]
for path in mkdirList
await createDirectoryIfNotExists(path)
plugin_list = await fs.promises.readdir("./plugins")
for plugin_filename in plugin_list
plugin_path = process.cwd() + "/plugins/" + plugin_filename
require(plugin_path)
log.info("Plugin loaded:", plugin_filename)
return
# 获取可用内存 # 获取可用内存
memory_usage = global.memory_usage = 0 memory_usage = global.memory_usage = 0
get_memory_usage = get_memory_usage = ()-> get_memory_usage = global.get_memory_usage = ()->
prc_free = exec("free") percentUsed = os.freemem() / os.totalmem() * 100
prc_free.stdout.on 'data', (data)->
lines = data.toString().split(/\n/g)
line = lines[0].split(/\s+/)
new_free = if line[6] == 'available' then true else false
line = lines[1].split(/\s+/)
total = parseInt(line[1], 10)
free = parseInt(line[3], 10)
buffers = parseInt(line[5], 10)
if new_free
actualFree = parseInt(line[6], 10)
else
cached = parseInt(line[6], 10)
actualFree = free + buffers + cached
percentUsed = parseFloat(((1 - (actualFree / total)) * 100).toFixed(2))
memory_usage = global.memory_usage = percentUsed memory_usage = global.memory_usage = percentUsed
return return
return
get_memory_usage() get_memory_usage()
setInterval(get_memory_usage, 3000) setInterval(get_memory_usage, 3000)
Cloud_replay_ids = global.Cloud_replay_ids = []
ROOM_all = global.ROOM_all = [] ROOM_all = global.ROOM_all = []
ROOM_players_oppentlist = global.ROOM_players_oppentlist = {} ROOM_players_oppentlist = global.ROOM_players_oppentlist = {}
ROOM_players_banned = global.ROOM_players_banned = []
ROOM_players_scores = global.ROOM_players_scores = {}
ROOM_connected_ip = global.ROOM_connected_ip = {} ROOM_connected_ip = global.ROOM_connected_ip = {}
ROOM_bad_ip = global.ROOM_bad_ip = {} ROOM_bad_ip = global.ROOM_bad_ip = {}
# ban a user manually and permanently # ban a user manually and permanently
ban_user = global.ban_user = (name, callback) -> ban_user = global.ban_user = (name) ->
settings.ban.banned_user.push(name) if !settings.modules.mysql.enabled
setting_save(settings) throw "MySQL is not enabled"
bad_ip = [] bans = [dataManager.getBan(name, null)]
_async.each(ROOM_all, (room, done)-> for room in ROOM_all when room and room.established
if !(room and room.established) for playerType in ["players", "watchers"]
done() for player in room[playerType] when player.name == name or bans.find(ban => player.ip == ban.ip)
return bans.push(dataManager.getBan(name, player.ip))
_async.each(["players", "watchers"], (player_type, _done)-> ROOM_bad_ip[player.ip]=99
_async.each(room[player_type], (player, __done)->
if player and (player.name == name or bad_ip.indexOf(player.ip) != -1)
bad_ip.push(player.ip)
ROOM_bad_ip[bad_ip]=99
settings.ban.banned_ip.push(player.ip)
ygopro.stoc_send_chat_to_room(room, "#{player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED) ygopro.stoc_send_chat_to_room(room, "#{player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED)
CLIENT_send_replays(player, room) CLIENT_send_replays(player, room)
CLIENT_kick(player) CLIENT_kick(player)
__done() for ban in bans
, _done) await dataManager.banPlayer(ban)
, done)
, callback)
return return
# automatically ban user to use random duel # automatically ban user to use random duel
ROOM_ban_player = global.ROOM_ban_player = (name, ip, reason, countadd = 1)-> ROOM_ban_player = global.ROOM_ban_player = (name, ip, reason, countadd = 1)->
return if settings.modules.test_mode.no_ban_player return if settings.modules.test_mode.no_ban_player or !settings.modules.mysql.enabled
bannedplayer = _.find ROOM_players_banned, (bannedplayer)-> await dataManager.randomDuelBanPlayer(ip, reason, countadd)
ip == bannedplayer.ip
if bannedplayer
bannedplayer.count = bannedplayer.count + countadd
bantime = if bannedplayer.count > 3 then Math.pow(2, bannedplayer.count - 3) * 2 else 0
bannedplayer.time = if moment() < bannedplayer.time then moment(bannedplayer.time).add(bantime, 'm') else moment().add(bantime, 'm')
bannedplayer.reasons.push(reason) if not _.find bannedplayer.reasons, (bannedreason)->
bannedreason == reason
bannedplayer.need_tip = true
else
bannedplayer = {"ip": ip, "time": moment(), "count": countadd, "reasons": [reason], "need_tip": true}
ROOM_players_banned.push(bannedplayer)
#log.info("banned", name, ip, reason, bannedplayer.count)
return return
ROOM_kick = (name, callback)-> ROOM_kick = (name, callback)->
...@@ -537,7 +658,7 @@ ROOM_kick = (name, callback)-> ...@@ -537,7 +658,7 @@ ROOM_kick = (name, callback)->
done() done()
return return
found = true found = true
room.termiate() room.terminate()
done() done()
, (err)-> , (err)->
callback(null, found) callback(null, found)
...@@ -546,92 +667,65 @@ ROOM_kick = (name, callback)-> ...@@ -546,92 +667,65 @@ ROOM_kick = (name, callback)->
ROOM_player_win = global.ROOM_player_win = (name)-> ROOM_player_win = global.ROOM_player_win = (name)->
if !ROOM_players_scores[name] if !settings.modules.mysql.enabled
ROOM_players_scores[name]={win:0, lose:0, flee:0, combo:0} return
ROOM_players_scores[name].win = ROOM_players_scores[name].win + 1 await dataManager.randomDuelPlayerWin(name)
ROOM_players_scores[name].combo = ROOM_players_scores[name].combo + 1
return return
ROOM_player_lose = global.ROOM_player_lose = (name)-> ROOM_player_lose = global.ROOM_player_lose = (name)->
if !ROOM_players_scores[name] if !settings.modules.mysql.enabled
ROOM_players_scores[name]={win:0, lose:0, flee:0, combo:0} return
ROOM_players_scores[name].lose = ROOM_players_scores[name].lose + 1 await dataManager.randomDuelPlayerLose(name)
ROOM_players_scores[name].combo = 0
return return
ROOM_player_flee = global.ROOM_player_flee = (name)-> ROOM_player_flee = global.ROOM_player_flee = (name)->
if !ROOM_players_scores[name] if !settings.modules.mysql.enabled
ROOM_players_scores[name]={win:0, lose:0, flee:0, combo:0}
ROOM_players_scores[name].flee = ROOM_players_scores[name].flee + 1
ROOM_players_scores[name].combo = 0
return return
await dataManager.randomDuelPlayerFlee(name)
ROOM_player_get_score = global.ROOM_player_get_score = (player)->
name = player.name_vpass
score = ROOM_players_scores[name]
if !score
return "#{player.name} ${random_score_blank}"
total = score.win + score.lose
if score.win < 2 and total < 3
return "#{player.name} ${random_score_not_enough}"
if score.combo >= 2
return "${random_score_part1}#{player.name} ${random_score_part2} #{Math.ceil(score.win/total*100)}${random_score_part3} #{Math.ceil(score.flee/total*100)}${random_score_part4_combo}#{score.combo}${random_score_part5_combo}"
#return player.name + " 的今日战绩:胜率" + Math.ceil(score.win/total*100) + "%,逃跑率" + Math.ceil(score.flee/total*100) + "%," + score.combo + "连胜中!"
else
return "${random_score_part1}#{player.name} ${random_score_part2} #{Math.ceil(score.win/total*100)}${random_score_part3} #{Math.ceil(score.flee/total*100)}${random_score_part4}"
return return
if settings.modules.random_duel.post_match_scores ROOM_player_get_score = global.ROOM_player_get_score = (player)->
setInterval(()-> if !settings.modules.mysql.enabled
scores_pair = _.pairs ROOM_players_scores return ""
scores_by_lose = _.sortBy(scores_pair, (score)-> return score[1].lose).reverse() # 败场由高到低 return await dataManager.getRandomDuelScoreDisplay(player.name_vpass)
scores_by_win = _.sortBy(scores_by_lose, (score)-> return score[1].win).reverse() # 然后胜场由低到高,再逆转,就是先排胜场再排败场
scores = _.first(scores_by_win, 10)
#log.info scores
request.post { url : settings.modules.random_duel.post_match_scores , form : {
accesskey: settings.modules.random_duel.post_match_accesskey,
rank: JSON.stringify(scores)
}}, (error, response, body)=>
if error
log.warn 'RANDOM SCORE POST ERROR', error
else
if response.statusCode != 204 and response.statusCode != 200
log.warn 'RANDOM SCORE POST FAIL', response.statusCode, response.statusMessage, body
#else
# log.info 'RANDOM SCORE POST OK', response.statusCode, response.statusMessage
return
return
, 60000)
ROOM_find_or_create_by_name = global.ROOM_find_or_create_by_name = (name, player_ip)-> ROOM_find_or_create_by_name = global.ROOM_find_or_create_by_name = (name, player_ip)->
uname=name.toUpperCase() uname=name.toUpperCase()
if settings.modules.windbot.enabled and (uname[0...2] == 'AI' or (!settings.modules.random_duel.enabled and uname == '')) if settings.modules.windbot.enabled and (uname[0...2] == 'AI' or (!settings.modules.random_duel.enabled and uname == ''))
return ROOM_find_or_create_ai(name) return ROOM_find_or_create_ai(name)
if settings.modules.random_duel.enabled and (uname == '' or uname == 'S' or uname == 'M' or uname == 'T') if settings.modules.random_duel.enabled and (uname == '' or uname == 'S' or uname == 'M' or uname == 'T')
return ROOM_find_or_create_random(uname, player_ip) return await ROOM_find_or_create_random(uname, player_ip)
if room = ROOM_find_by_name(name) if room = ROOM_find_by_name(name)
return room return room
else if memory_usage >= 90 else if memory_usage >= 90
return null return null
else else
return new Room(name) room = new Room(name)
if room.recover_duel_log_id
success = await room.initialize_recover()
if !success
return {"error": "${cloud_replay_no}"}
return room
ROOM_find_or_create_random = global.ROOM_find_or_create_random = (type, player_ip)-> ROOM_find_or_create_random = global.ROOM_find_or_create_random = (type, player_ip)->
bannedplayer = _.find ROOM_players_banned, (bannedplayer)-> if settings.modules.mysql.enabled
return player_ip == bannedplayer.ip randomDuelBanRecord = await dataManager.getRandomDuelBan(player_ip)
if bannedplayer if randomDuelBanRecord
if bannedplayer.count > 6 and moment() < bannedplayer.time if randomDuelBanRecord.count > 6 and moment().isBefore(randomDuelBanRecord.time)
return {"error": "${random_banned_part1}#{bannedplayer.reasons.join('${random_ban_reason_separator}')}${random_banned_part2}#{moment(bannedplayer.time).fromNow(true)}${random_banned_part3}"} return {"error": "${random_banned_part1}#{randomDuelBanRecord.reasons.join('${random_ban_reason_separator}')}${random_banned_part2}#{moment(randomDuelBanRecord.time).fromNow(true)}${random_banned_part3}"}
if bannedplayer.count > 3 and moment() < bannedplayer.time and bannedplayer.need_tip and type != 'T' if randomDuelBanRecord.count > 3 and moment().isBefore(randomDuelBanRecord.time) and randomDuelBanRecord.getNeedTip() and type != 'T'
bannedplayer.need_tip = false randomDuelBanRecord.setNeedTip(false)
return {"error": "${random_deprecated_part1}#{bannedplayer.reasons.join('${random_ban_reason_separator}')}${random_deprecated_part2}#{moment(bannedplayer.time).fromNow(true)}${random_deprecated_part3}"} await dataManager.updateRandomDuelBan(randomDuelBanRecord)
else if bannedplayer.need_tip return {"error": "${random_deprecated_part1}#{randomDuelBanRecord.reasons.join('${random_ban_reason_separator}')}${random_deprecated_part2}#{moment(randomDuelBanRecord.time).fromNow(true)}${random_deprecated_part3}"}
bannedplayer.need_tip = false else if randomDuelBanRecord.getNeedTip()
return {"error": "${random_warn_part1}#{bannedplayer.reasons.join('${random_ban_reason_separator}')}${random_warn_part2}"} randomDuelBanRecord.setNeedTip(false)
else if bannedplayer.count > 2 await dataManager.updateRandomDuelBan(randomDuelBanRecord)
bannedplayer.need_tip = true return {"error": "${random_warn_part1}#{randomDuelBanRecord.reasons.join('${random_ban_reason_separator}')}${random_warn_part2}"}
else if randomDuelBanRecord.count > 2
randomDuelBanRecord.setNeedTip(true)
await dataManager.updateRandomDuelBan(randomDuelBanRecord)
max_player = if type == 'T' then 4 else 2 max_player = if type == 'T' then 4 else 2
playerbanned = (bannedplayer and bannedplayer.count > 3 and moment() < bannedplayer.time) playerbanned = (randomDuelBanRecord and randomDuelBanRecord.count > 3 and moment() < randomDuelBanRecord.time)
result = _.find ROOM_all, (room)-> result = _.find ROOM_all, (room)->
return room and room.random_type != '' and room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and !room.windbot and return room and room.random_type != '' and room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and !room.windbot and
((type == '' and ((type == '' and
...@@ -868,13 +962,13 @@ CLIENT_is_able_to_reconnect = global.CLIENT_is_able_to_reconnect = (client, deck ...@@ -868,13 +962,13 @@ CLIENT_is_able_to_reconnect = global.CLIENT_is_able_to_reconnect = (client, deck
if !room if !room
CLIENT_reconnect_unregister(client) CLIENT_reconnect_unregister(client)
return false return false
if deckbuf and !_.isEqual(deckbuf, disconnect_info.deckbuf) if deckbuf and deckbuf.compare(disconnect_info.deckbuf) != 0
return false return false
return true return true
CLIENT_get_kick_reconnect_target = global.CLIENT_get_kick_reconnect_target = (client, deckbuf) -> CLIENT_get_kick_reconnect_target = global.CLIENT_get_kick_reconnect_target = (client, deckbuf) ->
for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and !room.windbot for room in ROOM_all when room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and !room.windbot
for player in room.get_playing_player() when !player.closed and player.name == client.name and (settings.modules.challonge.enabled or player.pass == client.pass) and (settings.modules.mycard.enabled or settings.modules.tournament_mode.enabled or player.ip == client.ip or (client.vpass and client.vpass == player.vpass)) and (!deckbuf or _.isEqual(player.start_deckbuf, deckbuf)) for player in room.get_playing_player() when !player.closed and player.name == client.name and (settings.modules.challonge.enabled or player.pass == client.pass) and (settings.modules.mycard.enabled or settings.modules.tournament_mode.enabled or player.ip == client.ip or (client.vpass and client.vpass == player.vpass)) and (!deckbuf or deckbuf.compare(player.start_deckbuf) == 0)
return player return player
return null return null
...@@ -998,9 +1092,6 @@ CLIENT_kick_reconnect = global.CLIENT_kick_reconnect = (client, deckbuf) -> ...@@ -998,9 +1092,6 @@ CLIENT_kick_reconnect = global.CLIENT_kick_reconnect = (client, deckbuf) ->
CLIENT_reconnect_unregister(client, true) CLIENT_reconnect_unregister(client, true)
return return
if settings.modules.reconnect.enabled
disconnect_list = {} # {old_client, old_server, room_id, timeout, deckbuf}
CLIENT_heartbeat_unregister = global.CLIENT_heartbeat_unregister = (client) -> CLIENT_heartbeat_unregister = global.CLIENT_heartbeat_unregister = (client) ->
if !settings.modules.heartbeat_detection.enabled or !client.heartbeat_timeout if !settings.modules.heartbeat_detection.enabled or !client.heartbeat_timeout
return false return false
...@@ -1209,14 +1300,7 @@ class Room ...@@ -1209,14 +1300,7 @@ class Room
@recovered = true @recovered = true
@recovering = true @recovering = true
@recover_from_turn = parseInt(param[4]) @recover_from_turn = parseInt(param[4])
duel_log_id = parseInt(param[3]) @recover_duel_log_id = parseInt(param[3])
@recover_duel_log = _.find(duel_log.duel_log, (duel) ->
return duel.id == duel_log_id and duel.roommode != 2 and duel.players[0].deck
)
if !@recover_duel_log || !fs.existsSync(settings.modules.tournament_mode.replay_path + @recover_duel_log.replay_filename)
@error = "${cloud_replay_no}"
return
@recover_replay = ReplayParser.fromFile(settings.modules.tournament_mode.replay_path + @recover_duel_log.replay_filename)
@recover_buffers = [[], [], [], []] @recover_buffers = [[], [], [], []]
@welcome = "${recover_hint}" @welcome = "${recover_hint}"
...@@ -1227,12 +1311,16 @@ class Room ...@@ -1227,12 +1311,16 @@ class Room
if (settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.block_replay_to_player) or (@hostinfo.mode == 1 and settings.modules.replay_delay) if (settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.block_replay_to_player) or (@hostinfo.mode == 1 and settings.modules.replay_delay)
@hostinfo.replay_mode |= 0x2 @hostinfo.replay_mode |= 0x2
if !@recovered
@spawn()
spawn: (firstSeed) ->
param = [0, @hostinfo.lflist, @hostinfo.rule, @hostinfo.mode, @hostinfo.duel_rule, param = [0, @hostinfo.lflist, @hostinfo.rule, @hostinfo.mode, @hostinfo.duel_rule,
(if @hostinfo.no_check_deck then 'T' else 'F'), (if @hostinfo.no_shuffle_deck then 'T' else 'F'), (if @hostinfo.no_check_deck then 'T' else 'F'), (if @hostinfo.no_shuffle_deck then 'T' else 'F'),
@hostinfo.start_lp, @hostinfo.start_hand, @hostinfo.draw_count, @hostinfo.time_limit, @hostinfo.replay_mode] @hostinfo.start_lp, @hostinfo.start_hand, @hostinfo.draw_count, @hostinfo.time_limit, @hostinfo.replay_mode]
if @recovered if firstSeed
param.push(@recover_replay.header.seed) param.push(firstSeed)
seeds = getSeedTimet(2) seeds = getSeedTimet(2)
param.push(seeds[i]) for i in [0...2] param.push(seeds[i]) for i in [0...2]
else else
...@@ -1358,33 +1446,12 @@ class Room ...@@ -1358,33 +1446,12 @@ class Room
replay_id = @cloud_replay_id replay_id = @cloud_replay_id
if @has_ygopro_error if @has_ygopro_error
log_rep_id = true log_rep_id = true
player_names=@player_datas[0].name + (if @player_datas[2] then "+" + @player_datas[2].name else "") +
" VS " +
(if @player_datas[1] then @player_datas[1].name else "AI") +
(if @player_datas[3] then "+" + @player_datas[3].name else "")
player_ips=[]
_.each @player_datas, (player)->
player_ips.push(player.key)
return
recorder_buffer=Buffer.concat(@recorder_buffers) recorder_buffer=Buffer.concat(@recorder_buffers)
player_datas = @player_datas
zlib.deflate recorder_buffer, (err, replay_buffer) -> zlib.deflate recorder_buffer, (err, replay_buffer) ->
replay_buffer=replay_buffer.toString('binary') dataManager.saveCloudReplay(replay_id, replay_buffer, player_datas).catch((err) ->
#log.info err, replay_buffer log.warn("Replay save error: R##{replay_id} #{err.toString()}")
date_time=moment().format('YYYY-MM-DD HH:mm:ss') )
#replay_id=Math.floor(Math.random()*100000000)
redisdb.hmset("replay:"+replay_id,
"replay_id", replay_id,
"replay_buffer", replay_buffer,
"player_names", player_names,
"date_time", date_time)
if !log_rep_id and !settings.modules.cloud_replay.never_expire
redisdb.expire("replay:"+replay_id, 60*60*24)
recorded_ip=[]
_.each player_ips, (player_ip)->
return if _.contains(recorded_ip, player_ip)
recorded_ip.push player_ip
redisdb.lpush(player_ip+":replays", replay_id)
return
if log_rep_id if log_rep_id
log.info "error replay: R#" + replay_id log.info "error replay: R#" + replay_id
return return
...@@ -1402,6 +1469,23 @@ class Room ...@@ -1402,6 +1469,23 @@ class Room
roomlist.delete this if !@windbot and @established and settings.modules.http.websocket_roomlist roomlist.delete this if !@windbot and @established and settings.modules.http.websocket_roomlist
return return
initialize_recover: ->
@recover_duel_log = await dataManager.getDuelLogFromId(@recover_duel_log_id)
#console.log(@recover_duel_log, fs.existsSync(settings.modules.tournament_mode.replay_path + @recover_duel_log.replayFileName))
if !@recover_duel_log || !fs.existsSync(settings.modules.tournament_mode.replay_path + @recover_duel_log.replayFileName)
@terminate()
return false
try
@recover_replay = await ReplayParser.fromFile(settings.modules.tournament_mode.replay_path + @recover_duel_log.replayFileName)
@spawn(@recover_replay.header.seed)
return true
catch e
log.warn("LOAD RECOVER REPLAY FAIL", e.toString())
@terminate()
return false
get_playing_player: -> get_playing_player: ->
playing_player = [] playing_player = []
_.each @players, (player)-> _.each @players, (player)->
...@@ -1593,19 +1677,22 @@ class Room ...@@ -1593,19 +1677,22 @@ class Room
ygopro.stoc_send_chat_to_room(this, "${death_cancel}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat_to_room(this, "${death_cancel}", ygopro.constants.COLORS.BABYBLUE)
return true return true
termiate: -> terminate: ->
if @duel_stage != ygopro.constants.DUEL_STAGE.BEGIN if @duel_stage != ygopro.constants.DUEL_STAGE.BEGIN
@scores[@dueling_players[0].name_vpass] = 0 @scores[@dueling_players[0].name_vpass] = 0
@scores[@dueling_players[1].name_vpass] = 0 @scores[@dueling_players[1].name_vpass] = 0
@kicked = true @kicked = true
@send_replays() @send_replays()
if @process
try
@process.kill() @process.kill()
catch e
@delete() @delete()
finish_recover: (fail) -> finish_recover: (fail) ->
if fail if fail
ygopro.stoc_send_chat_to_room(this, "${recover_fail}", ygopro.constants.COLORS.RED) ygopro.stoc_send_chat_to_room(this, "${recover_fail}", ygopro.constants.COLORS.RED)
@termiate() @terminate()
else else
ygopro.stoc_send_chat_to_room(this, "${recover_success}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat_to_room(this, "${recover_success}", ygopro.constants.COLORS.BABYBLUE)
@recovering = false @recovering = false
...@@ -1628,7 +1715,7 @@ class Room ...@@ -1628,7 +1715,7 @@ class Room
await return await return
# 网络连接 # 网络连接
net.createServer (client) -> netRequestHandler = (client) ->
client.ip = client.remoteAddress client.ip = client.remoteAddress
client.is_local = client.ip and (client.ip.includes('127.0.0.1') or client.ip.includes(real_windbot_server_ip)) client.is_local = client.ip and (client.ip.includes('127.0.0.1') or client.ip.includes(real_windbot_server_ip))
...@@ -1725,24 +1812,23 @@ net.createServer (client) -> ...@@ -1725,24 +1812,23 @@ net.createServer (client) ->
return return
if settings.modules.cloud_replay.enabled if settings.modules.cloud_replay.enabled
client.open_cloud_replay= (err, replay)-> client.open_cloud_replay = (replay)->
if err or !replay if !replay
ygopro.stoc_die(client, "${cloud_replay_no}") ygopro.stoc_die(client, "${cloud_replay_no}")
return return
redisdb.expire("replay:"+replay.replay_id, 60*60*48) buffer=replay.toBuffer()
buffer=Buffer.from(replay.replay_buffer,'binary') replay_buffer = null
zlib.unzip buffer, (err, replay_buffer) -> try
if err replay_buffer = await util.promisify(zlib.unzip)(buffer)
catch e
log.info "cloud replay unzip error: " + err log.info "cloud replay unzip error: " + err
ygopro.stoc_send_chat(client, "${cloud_replay_error}", ygopro.constants.COLORS.RED) ygopro.stoc_die(client, "${cloud_replay_error}")
CLIENT_kick(client)
return return
ygopro.stoc_send_chat(client, "${cloud_replay_playing} R##{replay.replay_id} #{replay.player_names} #{replay.date_time}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat(client, "${cloud_replay_playing} #{replay.getDisplayString()}", ygopro.constants.COLORS.BABYBLUE)
client.write replay_buffer, ()-> client.write replay_buffer, ()->
CLIENT_kick(client) CLIENT_kick(client)
return return
return return
return
# 需要重构 # 需要重构
# 客户端到服务端(ctos)协议分析 # 客户端到服务端(ctos)协议分析
...@@ -1809,12 +1895,6 @@ net.createServer (client) -> ...@@ -1809,12 +1895,6 @@ net.createServer (client) ->
return return
return return
.listen settings.port, ->
log.info "server started", settings.port
return
if settings.modules.stop
log.info "NOTE: server not open due to config, ", settings.modules.stop
deck_name_match = global.deck_name_match = (deck_name, player_name) -> deck_name_match = global.deck_name_match = (deck_name, player_name) ->
if deck_name == player_name or deck_name == player_name + ".ydk" or deck_name == player_name + ".ydk.ydk" if deck_name == player_name or deck_name == player_name + ".ydk" or deck_name == player_name + ".ydk.ydk"
...@@ -1891,66 +1971,34 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)-> ...@@ -1891,66 +1971,34 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
else if info.pass.toUpperCase()=="R" and settings.modules.cloud_replay.enabled else if info.pass.toUpperCase()=="R" and settings.modules.cloud_replay.enabled
ygopro.stoc_send_chat(client,"${cloud_replay_hint}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat(client,"${cloud_replay_hint}", ygopro.constants.COLORS.BABYBLUE)
redisdb.lrange CLIENT_get_authorize_key(client)+":replays", 0, 2, (err, result)-> replays = await dataManager.getCloudReplaysFromKey(CLIENT_get_authorize_key(client))
_.each result, (replay_id,id)-> for replay,index in replays
redisdb.hgetall "replay:"+replay_id, (err, replay)-> ygopro.stoc_send_chat(client,"<#{index + 1}> #{replay.getDisplayString()}", ygopro.constants.COLORS.BABYBLUE)
if err or !replay ygopro.stoc_send client, 'ERROR_MSG', {
log.info "cloud replay getall error: " + err if err
return
ygopro.stoc_send_chat(client,"<#{id-0+1}> R##{replay_id} #{replay.player_names} #{replay.date_time}", ygopro.constants.COLORS.BABYBLUE)
return
return
return
# 强行等待异步执行完毕_(:з」∠)_
setTimeout (()->
ygopro.stoc_send client, 'ERROR_MSG',{
msg: 1 msg: 1
code: 9 code: 9
} }
CLIENT_kick(client) CLIENT_kick(client)
return), 500
else if info.pass.toUpperCase()=="RC" and settings.modules.tournament_mode.enable_recover else if info.pass.toUpperCase()=="RC" and settings.modules.tournament_mode.enable_recover
ygopro.stoc_send_chat(client,"${recover_replay_hint}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat(client,"${recover_replay_hint}", ygopro.constants.COLORS.BABYBLUE)
available_logs = duel_log.duel_log.filter((duel) -> available_logs = await dataManager.getDuelLogFromRecoverSearch(client.name_vpass)
return duel.id and duel.players[0].deck and duel.roommode != 2 and _.any(duel.players, (player) -> for duelLog in available_logs
return player.real_name == client.name_vpass ygopro.stoc_send_chat(client, duelLog.getViewString(), ygopro.constants.COLORS.BABYBLUE)
)
).slice(0, 8)
_.each(available_logs, (duel) ->
player_names = duel.players[0].real_name.split("$")[0] + (if duel.players[2] then "+" + duel.players[2].real_name.split("$")[0] else "") +
" VS " +
(if duel.players[1] then duel.players[1].real_name.split("$")[0] else "AI") +
(if duel.players[3] then "+" + duel.players[3].real_name.split("$")[0] else "")
ygopro.stoc_send_chat(client,"<#{duel.id}> #{player_names} #{duel.time}", ygopro.constants.COLORS.BABYBLUE)
)
# 强行等待异步执行完毕_(:з」∠)_
setTimeout (()->
ygopro.stoc_send client, 'ERROR_MSG',{ ygopro.stoc_send client, 'ERROR_MSG',{
msg: 1 msg: 1
code: 9 code: 9
} }
CLIENT_kick(client) CLIENT_kick(client)
return), 500
else if info.pass[0...2].toUpperCase()=="R#" and settings.modules.cloud_replay.enabled else if info.pass[0...2].toUpperCase()=="R#" and settings.modules.cloud_replay.enabled
replay_id=info.pass.split("#")[1] replay_id=info.pass.split("#")[1]
if (replay_id>0 and replay_id<=9) replay = await dataManager.getCloudReplayFromId(replay_id)
redisdb.lindex client.ip+":replays", replay_id-1, (err, replay_id)-> await client.open_cloud_replay(replay)
if err or !replay_id
log.info "cloud replay replayid error: " + err if err
ygopro.stoc_die(client, "${cloud_replay_no}")
return
redisdb.hgetall "replay:"+replay_id, client.open_cloud_replay
return
else if replay_id
redisdb.hgetall "replay:"+replay_id, client.open_cloud_replay
else
ygopro.stoc_die(client, "${cloud_replay_no}")
else if info.pass.toUpperCase()=="W" and settings.modules.cloud_replay.enabled else if info.pass.toUpperCase()=="W" and settings.modules.cloud_replay.enabled
replay_id=Cloud_replay_ids[Math.floor(Math.random()*Cloud_replay_ids.length)] replay = await dataManager.getRandomCloudReplay()
redisdb.hgetall "replay:"+replay_id, client.open_cloud_replay await client.open_cloud_replay(replay)
else if info.version != settings.version and !settings.alternative_versions.includes(info.version) else if info.version != settings.version and !settings.alternative_versions.includes(info.version)
ygopro.stoc_send_chat(client, settings.modules.update, ygopro.constants.COLORS.RED) ygopro.stoc_send_chat(client, settings.modules.update, ygopro.constants.COLORS.RED)
...@@ -2062,7 +2110,7 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)-> ...@@ -2062,7 +2110,7 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
if match_permit and !match_permit.permit if match_permit and !match_permit.permit
ygopro.stoc_die(client, '${invalid_password_unauthorized}') ygopro.stoc_die(client, '${invalid_password_unauthorized}')
return return
room = ROOM_find_or_create_by_name('M#' + info.pass.slice(8)) room = await ROOM_find_or_create_by_name('M#' + info.pass.slice(8))
if room if room
for player in room.get_playing_player() when player and player.name == client.name for player in room.get_playing_player() when player and player.name == client.name
ygopro.stoc_die(client, '${invalid_password_unauthorized}') ygopro.stoc_die(client, '${invalid_password_unauthorized}')
...@@ -2189,6 +2237,8 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)-> ...@@ -2189,6 +2237,8 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
if(err) if(err)
ygopro.stoc_die(client, err) ygopro.stoc_die(client, err)
return return
create_room_with_action(data.get_user.original, data.get_user.decrypted, data.match_permit) create_room_with_action(data.get_user.original, data.get_user.decrypted, data.match_permit)
) )
...@@ -2260,7 +2310,7 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)-> ...@@ -2260,7 +2310,7 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
create_room_name = 'M#' + found.id create_room_name = 'M#' + found.id
if recover_match if recover_match
create_room_name = recover_match[0] + ',' + create_room_name create_room_name = recover_match[0] + ',' + create_room_name
room = ROOM_find_or_create_by_name(create_room_name) room = await ROOM_find_or_create_by_name(create_room_name)
if room if room
room.challonge_info = found room.challonge_info = found
# room.max_player = 2 # room.max_player = 2
...@@ -2301,13 +2351,15 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)-> ...@@ -2301,13 +2351,15 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
log.warn("MULTI LOGIN", client.name, client.ip) log.warn("MULTI LOGIN", client.name, client.ip)
ygopro.stoc_die(client, "${too_much_connection}" + client.ip) ygopro.stoc_die(client, "${too_much_connection}" + client.ip)
else if _.indexOf(settings.ban.banned_user, client.name) > -1 #账号被封 else if settings.modules.mysql.enabled and await dataManager.checkBan("name", client.name) #账号被封
settings.ban.banned_ip.push(client.ip) exactBan = await dataManager.checkBanWithNameAndIP(client.name, client.ip)
setting_save(settings) if !exactBan
exactBan = dataManager.getBan(client.name, client.ip)
await dataManager.banPlayer(exactBan)
log.warn("BANNED USER LOGIN", client.name, client.ip) log.warn("BANNED USER LOGIN", client.name, client.ip)
ygopro.stoc_die(client, "${banned_user_login}") ygopro.stoc_die(client, "${banned_user_login}")
else if _.indexOf(settings.ban.banned_ip, client.ip) > -1 #IP被封 else if settings.modules.mysql.enabled and await dataManager.checkBan("ip", client.ip) #IP被封
log.warn("BANNED IP LOGIN", client.name, client.ip) log.warn("BANNED IP LOGIN", client.name, client.ip)
ygopro.stoc_die(client, "${banned_ip_login}") ygopro.stoc_die(client, "${banned_ip_login}")
...@@ -2344,7 +2396,7 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)-> ...@@ -2344,7 +2396,7 @@ ygopro.ctos_follow 'JOIN_GAME', true, (buffer, info, client, server, datas)->
buffer = struct.buffer buffer = struct.buffer
#log.info 'join_game',info.pass, client.name #log.info 'join_game',info.pass, client.name
room = ROOM_find_or_create_by_name(info.pass, client.ip) room = await ROOM_find_or_create_by_name(info.pass, client.ip)
if !room if !room
ygopro.stoc_die(client, "${server_full}") ygopro.stoc_die(client, "${server_full}")
else if room.error else if room.error
...@@ -2397,9 +2449,9 @@ ygopro.stoc_follow 'JOIN_GAME', false, (buffer, info, client, server, datas)-> ...@@ -2397,9 +2449,9 @@ ygopro.stoc_follow 'JOIN_GAME', false, (buffer, info, client, server, datas)->
#client.score_shown = true #client.score_shown = true
return return
if settings.modules.random_duel.record_match_scores and room.random_type == 'M' if settings.modules.random_duel.record_match_scores and room.random_type == 'M'
ygopro.stoc_send_chat_to_room(room, ROOM_player_get_score(client), ygopro.constants.COLORS.GREEN) ygopro.stoc_send_chat_to_room(room, await ROOM_player_get_score(client), ygopro.constants.COLORS.GREEN)
for player in room.players when player.pos != 7 and player != client for player in room.players when player.pos != 7 and player != client
ygopro.stoc_send_chat(client, ROOM_player_get_score(player), ygopro.constants.COLORS.GREEN) ygopro.stoc_send_chat(client, await ROOM_player_get_score(player), ygopro.constants.COLORS.GREEN)
if !room.recorder if !room.recorder
room.recorder = recorder = net.connect room.port, -> room.recorder = recorder = net.connect room.port, ->
ygopro.ctos_send recorder, 'PLAYER_INFO', { ygopro.ctos_send recorder, 'PLAYER_INFO', {
...@@ -2447,25 +2499,8 @@ ygopro.stoc_follow 'JOIN_GAME', false, (buffer, info, client, server, datas)-> ...@@ -2447,25 +2499,8 @@ ygopro.stoc_follow 'JOIN_GAME', false, (buffer, info, client, server, datas)->
await return await return
# 登场台词 # 登场台词
load_dialogues = global.load_dialogues = (callback) -> load_dialogues = global.load_dialogues = () ->
request return await loadRemoteData(dialogues, "dialogues", settings.modules.dialogues.get)
url: settings.modules.dialogues.get
json: true
, (error, response, body)->
if _.isString body
log.warn "dialogues bad json", body
else if error or !body
log.warn 'dialogues error', error, response
else
setting_change(dialogues, "dialogues", body)
log.info "dialogues loaded", _.size dialogues.dialogues
if callback
callback(error, body)
return
await return
if settings.modules.dialogues.get
load_dialogues()
ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)-> ygopro.stoc_follow 'GAME_MSG', true, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid] room=ROOM_all[client.rid]
...@@ -2788,7 +2823,7 @@ ygopro.ctos_follow 'HS_KICK', true, (buffer, info, client, server, datas)-> ...@@ -2788,7 +2823,7 @@ ygopro.ctos_follow 'HS_KICK', true, (buffer, info, client, server, datas)->
client.kick_count = if client.kick_count then client.kick_count+1 else 1 client.kick_count = if client.kick_count then client.kick_count+1 else 1
if client.kick_count>=5 and room.random_type if client.kick_count>=5 and room.random_type
ygopro.stoc_send_chat_to_room(room, "#{client.name} ${kicked_by_system}", ygopro.constants.COLORS.RED) ygopro.stoc_send_chat_to_room(room, "#{client.name} ${kicked_by_system}", ygopro.constants.COLORS.RED)
ROOM_ban_player(player.name, player.ip, "${random_ban_reason_zombie}") await ROOM_ban_player(player.name, player.ip, "${random_ban_reason_zombie}")
CLIENT_kick(client) CLIENT_kick(client)
return true return true
ygopro.stoc_send_chat_to_room(room, "#{player.name} ${kicked_by_player}", ygopro.constants.COLORS.RED) ygopro.stoc_send_chat_to_room(room, "#{player.name} ${kicked_by_player}", ygopro.constants.COLORS.RED)
...@@ -2903,7 +2938,7 @@ wait_room_start = (room, time)-> ...@@ -2903,7 +2938,7 @@ wait_room_start = (room, time)->
else else
for player in room.players for player in room.players
if player and player.is_host if player and player.is_host
ROOM_ban_player(player.name, player.ip, "${random_ban_reason_zombie}") await ROOM_ban_player(player.name, player.ip, "${random_ban_reason_zombie}")
ygopro.stoc_send_chat_to_room(room, "#{player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED) ygopro.stoc_send_chat_to_room(room, "#{player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED)
CLIENT_kick(player) CLIENT_kick(player)
await return await return
...@@ -2934,30 +2969,26 @@ ygopro.stoc_send_random_tip_to_room = (room)-> ...@@ -2934,30 +2969,26 @@ ygopro.stoc_send_random_tip_to_room = (room)->
ygopro.stoc_send_chat_to_room(room, "Tip: " + tips.tips[Math.floor(Math.random() * tips.tips.length)]) ygopro.stoc_send_chat_to_room(room, "Tip: " + tips.tips[Math.floor(Math.random() * tips.tips.length)])
await return await return
load_tips = global.load_tips = (callback)-> loadRemoteData = global.loadRemoteData = (loadObject, name, url)->
request try
url: settings.modules.tips.get body = (await axios.get(url, {
json: true responseType: "json"
, (error, response, body)-> })).data
if _.isString body if _.isString body
log.warn "tips bad json", body log.warn "#{name} bad json", body
else if error or !body return false
log.warn 'tips error', error, response if !body
else log.warn "#{name} empty", body
setting_change(tips, "tips", body) return false
log.info "tips loaded", tips.tips.length await setting_change(loadObject, name, body)
if callback log.info "#{name} loaded"
callback(error, body) return true
return catch e
await return log.warn "#{name} error", e
return false
if settings.modules.tips.get load_tips = global.load_tips = ()->
load_tips() return await loadRemoteData(tips, "tips", settings.modules.tips.get)
setInterval ()->
for room in ROOM_all when room and room.established
ygopro.stoc_send_random_tip_to_room(room) if room.duel_stage == ygopro.constants.DUEL_STAGE.SIDING or room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN
return
, 30000
ygopro.stoc_follow 'DUEL_START', false, (buffer, info, client, server, datas)-> ygopro.stoc_follow 'DUEL_START', false, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid] room=ROOM_all[client.rid]
...@@ -2969,10 +3000,10 @@ ygopro.stoc_follow 'DUEL_START', false, (buffer, info, client, server, datas)-> ...@@ -2969,10 +3000,10 @@ ygopro.stoc_follow 'DUEL_START', false, (buffer, info, client, server, datas)->
roomlist.start room if !room.windbot and settings.modules.http.websocket_roomlist roomlist.start room if !room.windbot and settings.modules.http.websocket_roomlist
#room.duels = [] #room.duels = []
room.dueling_players = [] room.dueling_players = []
for player in room.players when player.pos != 7 for player in room.get_playing_player()
room.dueling_players[player.pos] = player room.dueling_players[player.pos] = player
room.scores[player.name_vpass] = 0 room.scores[player.name_vpass] = 0
room.player_datas.push key: CLIENT_get_authorize_key(player), name: player.name room.player_datas.push key: CLIENT_get_authorize_key(player), name: player.name, pos: player.pos
if room.random_type == 'T' if room.random_type == 'T'
# 双打房不记录匹配过 # 双打房不记录匹配过
ROOM_players_oppentlist[player.ip] = null ROOM_players_oppentlist[player.ip] = null
...@@ -3123,19 +3154,18 @@ ygopro.ctos_follow 'CHAT', true, (buffer, info, client, server, datas)-> ...@@ -3123,19 +3154,18 @@ ygopro.ctos_follow 'CHAT', true, (buffer, info, client, server, datas)->
for cname,cvalue of ygopro.constants.COLORS when cvalue > 10 for cname,cvalue of ygopro.constants.COLORS when cvalue > 10
ygopro.stoc_send_chat(client, cname, cvalue) ygopro.stoc_send_chat(client, cname, cvalue)
else if cmsg.toLowerCase() == "default" else if cmsg.toLowerCase() == "default"
chat_color.save_list[cip] = false await dataManager.setUserChatColor(cip, null)
setting_save(chat_color)
ygopro.stoc_send_chat(client, "${set_chat_color_default}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat(client, "${set_chat_color_default}", ygopro.constants.COLORS.BABYBLUE)
else else
ccolor = cmsg.toUpperCase() ccolor = cmsg.toUpperCase()
if ygopro.constants.COLORS[ccolor] and ygopro.constants.COLORS[ccolor] > 10 and ygopro.constants.COLORS[ccolor] < 20 if ygopro.constants.COLORS[ccolor] and ygopro.constants.COLORS[ccolor] > 10 and ygopro.constants.COLORS[ccolor] < 20
chat_color.save_list[cip] = ccolor await dataManager.setUserChatColor(cip, ccolor)
setting_save(chat_color)
ygopro.stoc_send_chat(client, "${set_chat_color_part1}" + ccolor + "${set_chat_color_part2}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat(client, "${set_chat_color_part1}" + ccolor + "${set_chat_color_part2}", ygopro.constants.COLORS.BABYBLUE)
else else
ygopro.stoc_send_chat(client, "${color_not_found_part1}" + ccolor + "${color_not_found_part2}", ygopro.constants.COLORS.RED) ygopro.stoc_send_chat(client, "${color_not_found_part1}" + ccolor + "${color_not_found_part2}", ygopro.constants.COLORS.RED)
else else
if color = chat_color.save_list[cip] color = await dataManager.getUserChatColor(cip)
if color
ygopro.stoc_send_chat(client, "${get_chat_color_part1}" + color + "${get_chat_color_part2}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat(client, "${get_chat_color_part1}" + color + "${get_chat_color_part2}", ygopro.constants.COLORS.BABYBLUE)
else else
ygopro.stoc_send_chat(client, "${get_chat_color_default}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat(client, "${get_chat_color_default}", ygopro.constants.COLORS.BABYBLUE)
...@@ -3166,8 +3196,8 @@ ygopro.ctos_follow 'CHAT', true, (buffer, info, client, server, datas)-> ...@@ -3166,8 +3196,8 @@ ygopro.ctos_follow 'CHAT', true, (buffer, info, client, server, datas)->
cancel = true cancel = true
if client.abuse_count>0 if client.abuse_count>0
ygopro.stoc_send_chat(client, "${banned_duel_tip}", ygopro.constants.COLORS.RED) ygopro.stoc_send_chat(client, "${banned_duel_tip}", ygopro.constants.COLORS.RED)
ROOM_ban_player(client.name, client.ip, "${random_ban_reason_abuse}") await ROOM_ban_player(client.name, client.ip, "${random_ban_reason_abuse}")
ROOM_ban_player(client.name, client.ip, "${random_ban_reason_abuse}", 3) await ROOM_ban_player(client.name, client.ip, "${random_ban_reason_abuse}", 3)
CLIENT_send_replays(client, room) CLIENT_send_replays(client, room)
CLIENT_kick(client) CLIENT_kick(client)
return true return true
...@@ -3221,7 +3251,7 @@ ygopro.ctos_follow 'CHAT', true, (buffer, info, client, server, datas)-> ...@@ -3221,7 +3251,7 @@ ygopro.ctos_follow 'CHAT', true, (buffer, info, client, server, datas)->
ROOM_unwelcome(room, client, "${random_ban_reason_abuse}") ROOM_unwelcome(room, client, "${random_ban_reason_abuse}")
if client.abuse_count>=5 if client.abuse_count>=5
ygopro.stoc_send_chat_to_room(room, "#{client.name} ${chat_banned}", ygopro.constants.COLORS.RED) ygopro.stoc_send_chat_to_room(room, "#{client.name} ${chat_banned}", ygopro.constants.COLORS.RED)
ROOM_ban_player(client.name, client.ip, "${random_ban_reason_abuse}") await ROOM_ban_player(client.name, client.ip, "${random_ban_reason_abuse}")
if !cancel and settings.modules.display_watchers and client.is_post_watcher if !cancel and settings.modules.display_watchers and client.is_post_watcher
ygopro.stoc_send_chat_to_room(room, "#{client.name}: #{msg}", 9) ygopro.stoc_send_chat_to_room(room, "#{client.name}: #{msg}", 9)
return true return true
...@@ -3278,13 +3308,14 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)-> ...@@ -3278,13 +3308,14 @@ ygopro.ctos_follow 'UPDATE_DECK', true, (buffer, info, client, server, datas)->
room.last_active_time = moment() room.last_active_time = moment()
if room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and room.recovering if room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and room.recovering
recover_player_data = _.find(room.recover_duel_log.players, (player) -> recover_player_data = _.find(room.recover_duel_log.players, (player) ->
return player.real_name == client.name_vpass and _.isEqual(buffer, Buffer.from(player.deckbuf, "base64")) return player.realName == client.name_vpass and buffer.compare(Buffer.from(player.startDeckBuffer, "base64")) == 0
) )
if recover_player_data if recover_player_data
struct.set("mainc", recover_player_data.deck.main.length) recoveredDeck = recover_player_data.getCurrentDeck()
struct.set("sidec", recover_player_data.deck.side.length) struct.set("mainc", recoveredDeck.main.length)
struct.set("deckbuf", recover_player_data.deck.main.concat(recover_player_data.deck.side)) struct.set("sidec", recoveredDeck.side.length)
if recover_player_data.is_first struct.set("deckbuf", recoveredDeck.main.concat(recoveredDeck.side))
if recover_player_data.isFirst
room.determine_firstgo = client room.determine_firstgo = client
else else
struct.set("mainc", 1) struct.set("mainc", 1)
...@@ -3429,7 +3460,7 @@ ygopro.stoc_follow 'CHAT', true, (buffer, info, client, server, datas)-> ...@@ -3429,7 +3460,7 @@ ygopro.stoc_follow 'CHAT', true, (buffer, info, client, server, datas)->
for player in room.players when player and player.pos == pid for player in room.players when player and player.pos == pid
tplayer = player tplayer = player
return unless tplayer return unless tplayer
tcolor = chat_color.save_list[CLIENT_get_authorize_key(tplayer)] tcolor = await dataManager.getUserChatColor(CLIENT_get_authorize_key(tplayer));
if tcolor if tcolor
ygopro.stoc_send client, 'CHAT', { ygopro.stoc_send client, 'CHAT', {
player: ygopro.constants.COLORS[tcolor] player: ygopro.constants.COLORS[tcolor]
...@@ -3531,15 +3562,12 @@ ygopro.stoc_follow 'CHANGE_SIDE', false, (buffer, info, client, server, datas)-> ...@@ -3531,15 +3562,12 @@ ygopro.stoc_follow 'CHANGE_SIDE', false, (buffer, info, client, server, datas)->
ygopro.stoc_follow 'REPLAY', true, (buffer, info, client, server, datas)-> ygopro.stoc_follow 'REPLAY', true, (buffer, info, client, server, datas)->
room=ROOM_all[client.rid] room=ROOM_all[client.rid]
return settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.block_replay_to_player or settings.modules.replay_delay unless room return settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.block_replay_to_player or settings.modules.replay_delay unless room
if settings.modules.cloud_replay.enabled and room.random_type
Cloud_replay_ids.push room.cloud_replay_id
if !room.replays[room.duel_count - 1] if !room.replays[room.duel_count - 1]
# console.log("Replay saved: ", room.duel_count - 1, client.pos) # console.log("Replay saved: ", room.duel_count - 1, client.pos)
room.replays[room.duel_count - 1] = buffer room.replays[room.duel_count - 1] = buffer
if settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.replay_safe or settings.modules.tournament_mode.enable_recover if settings.modules.mysql.enabled
if client.pos == 0 if client.pos == 0
dueltime=moment().format('YYYY-MM-DD HH-mm-ss') replay_filename=moment().format("YYYY-MM-DD HH-mm-ss")
replay_filename=dueltime
if room.hostinfo.mode != 2 if room.hostinfo.mode != 2
for player,i in room.dueling_players for player,i in room.dueling_players
replay_filename=replay_filename + (if i > 0 then " VS " else " ") + player.name replay_filename=replay_filename + (if i > 0 then " VS " else " ") + player.name
...@@ -3547,127 +3575,34 @@ ygopro.stoc_follow 'REPLAY', true, (buffer, info, client, server, datas)-> ...@@ -3547,127 +3575,34 @@ ygopro.stoc_follow 'REPLAY', true, (buffer, info, client, server, datas)->
for player,i in room.dueling_players for player,i in room.dueling_players
replay_filename=replay_filename + (if i > 0 then (if i == 2 then " VS " else " & ") else " ") + player.name replay_filename=replay_filename + (if i > 0 then (if i == 2 then " VS " else " & ") else " ") + player.name
replay_filename=replay_filename.replace(/[\/\\\?\*]/g, '_')+".yrp" replay_filename=replay_filename.replace(/[\/\\\?\*]/g, '_')+".yrp"
duellog = { playerInfos = room.dueling_players.map((player) ->
id: duel_log.duel_log.length + 1, return {
time: dueltime, name: player.name
name: room.name + (if settings.modules.tournament_mode.show_info then (" (Duel:" + room.duel_count + ")") else ""), pos: player.pos
roomid: room.process_pid.toString(), realName: player.name_vpass
cloud_replay_id: "R#"+room.cloud_replay_id, startDeckBuffer: player.start_deckbuf
replay_filename: replay_filename,
roommode: room.hostinfo.mode,
players: (for player in room.dueling_players
real_name: player.name_vpass,
deckbuf: player.start_deckbuf.toString("base64"),
deck: { deck: {
main: player.main, main: player.main,
side: player.side side: player.side
} }
pos: player.pos isFirst: player.is_first
is_first: player.is_first
name: player.name + (if settings.modules.tournament_mode.show_ip and !player.is_local then (" (IP: " + player.ip.slice(7) + ")") else "") + (if settings.modules.tournament_mode.show_info and not (room.hostinfo.mode == 2 and player.pos % 2 > 0) then (" (Score:" + room.scores[player.name_vpass] + " LP:" + (if player.lp? then player.lp else room.hostinfo.start_lp) + (if room.hostinfo.mode != 2 then (" Cards:" + (if player.card_count? then player.card_count else room.hostinfo.start_hand)) else "") + ")") else ""),
winner: player.pos == room.winner winner: player.pos == room.winner
) ip: player.ip
score: room.scores[player.name_vpass]
lp: if player.lp? then player.lp else room.hostinfo.start_lp
cardCount: if player.card_count? then player.card_count else room.hostinfo.start_hand
} }
duel_log.duel_log.unshift duellog )
setting_save(duel_log)
fs.writeFile(settings.modules.tournament_mode.replay_path + replay_filename, buffer, (err)-> fs.writeFile(settings.modules.tournament_mode.replay_path + replay_filename, buffer, (err)->
if err then log.warn "SAVE REPLAY ERROR", replay_filename, err if err then log.warn "SAVE REPLAY ERROR", replay_filename, err
) )
dataManager.saveDuelLog(room.name, room.process_pid.toString(), room.cloud_replay_id, replay_filename, room.hostinfo.mode, room.duel_count, playerInfos) # no synchronize here because too slow
if settings.modules.cloud_replay.enabled and settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.replay_safe if settings.modules.cloud_replay.enabled and settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.replay_safe
ygopro.stoc_send_chat(client, "${cloud_replay_delay_part1}R##{room.cloud_replay_id}${cloud_replay_delay_part2}", ygopro.constants.COLORS.BABYBLUE) ygopro.stoc_send_chat(client, "${cloud_replay_delay_part1}R##{room.cloud_replay_id}${cloud_replay_delay_part2}", ygopro.constants.COLORS.BABYBLUE)
await return settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.block_replay_to_player or settings.modules.replay_delay and room.hostinfo.mode == 1 await return settings.modules.tournament_mode.enabled and settings.modules.tournament_mode.block_replay_to_player or settings.modules.replay_delay and room.hostinfo.mode == 1
else else
await return settings.modules.replay_delay and room.hostinfo.mode == 1 await return settings.modules.replay_delay and room.hostinfo.mode == 1
if settings.modules.random_duel.enabled
setInterval ()->
_async.each(ROOM_all, (room, done) ->
if !(room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and room.random_type and room.last_active_time and room.waiting_for_player and room.get_disconnected_count() == 0 and (!settings.modules.side_timeout or room.duel_stage != ygopro.constants.DUEL_STAGE.SIDING) and !room.recovered)
done()
return
time_passed = Math.floor((moment() - room.last_active_time) / 1000)
#log.info time_passed
if time_passed >= settings.modules.random_duel.hang_timeout
room.last_active_time = moment()
ROOM_ban_player(room.waiting_for_player.name, room.waiting_for_player.ip, "${random_ban_reason_AFK}")
room.scores[room.waiting_for_player.name_vpass] = -9
#log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED)
CLIENT_send_replays(room.waiting_for_player, room)
CLIENT_kick(room.waiting_for_player)
else if time_passed >= (settings.modules.random_duel.hang_timeout - 20) and not (time_passed % 10)
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${afk_warn_part1}#{settings.modules.random_duel.hang_timeout - time_passed}${afk_warn_part2}", ygopro.constants.COLORS.RED)
ROOM_unwelcome(room, room.waiting_for_player, "${random_ban_reason_AFK}")
done()
return
)
return
, 1000
if settings.modules.mycard.enabled
setInterval ()->
_async.each(ROOM_all, (room, done) ->
if not (room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and room.arena and room.last_active_time and room.waiting_for_player and room.get_disconnected_count() == 0 and (!settings.modules.side_timeout or room.duel_stage != ygopro.constants.DUEL_STAGE.SIDING) and !room.recovered)
done()
return
time_passed = Math.floor((moment() - room.last_active_time) / 1000)
#log.info time_passed
if time_passed >= settings.modules.random_duel.hang_timeout
room.last_active_time = moment()
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${kicked_by_system}", ygopro.constants.COLORS.RED)
room.scores[room.waiting_for_player.name_vpass] = -9
#log.info room.waiting_for_player.name, room.scores[room.waiting_for_player.name_vpass]
CLIENT_send_replays(room.waiting_for_player, room)
CLIENT_kick(room.waiting_for_player)
else if time_passed >= (settings.modules.random_duel.hang_timeout - 20) and not (time_passed % 10)
ygopro.stoc_send_chat_to_room(room, "#{room.waiting_for_player.name} ${afk_warn_part1}#{settings.modules.random_duel.hang_timeout - time_passed}${afk_warn_part2}", ygopro.constants.COLORS.RED)
done()
return
)
if true # settings.modules.arena_mode.punish_quit_before_match
_async.each(ROOM_all, (room, done) ->
if not (room and room.arena and room.duel_stage == ygopro.constants.DUEL_STAGE.BEGIN and room.get_playing_player().length < 2)
done()
return
player = room.get_playing_player()[0]
if player and player.join_time and !player.arena_quit_free
waited_time = moment() - player.join_time
if waited_time >= 30000
ygopro.stoc_send_chat(player, "${arena_wait_timeout}", ygopro.constants.COLORS.BABYBLUE)
player.arena_quit_free = true
else if waited_time >= 5000 and waited_time < 6000
ygopro.stoc_send_chat(player, "${arena_wait_hint}", ygopro.constants.COLORS.BABYBLUE)
done()
return
)
return
, 1000
if settings.modules.heartbeat_detection.enabled
setInterval ()->
_async.each ROOM_all, (room, done)->
if room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and (room.hostinfo.time_limit == 0 or room.duel_stage != ygopro.constants.DUEL_STAGE.DUELING) and !room.windbot
_async.each(room.get_playing_player(), (player, _done)->
if player and (room.duel_stage != ygopro.constants.DUEL_STAGE.SIDING or player.selected_preduel)
CLIENT_heartbeat_register(player, true)
_done()
, done)
else
done()
return
, settings.modules.heartbeat_detection.interval
setInterval ()->
current_time = moment()
_async.each ROOM_all, (room, done)->
if room and room.duel_stage != ygopro.constants.DUEL_STAGE.BEGIN and room.hostinfo.auto_death and !room.auto_death_triggered and current_time - moment(room.start_time) > 60000 * room.hostinfo.auto_death
room.auto_death_triggered = true
room.start_death()
done()
, 1000
# spawn windbot # spawn windbot
windbot_looplimit = 0 windbot_looplimit = 0
windbot_process = global.windbot_process = null windbot_process = global.windbot_process = null
...@@ -3704,18 +3639,15 @@ spawn_windbot = global.spawn_windbot = () -> ...@@ -3704,18 +3639,15 @@ spawn_windbot = global.spawn_windbot = () ->
return return
return return
if settings.modules.windbot.enabled and settings.modules.windbot.spawn
spawn_windbot()
global.rebooted = false global.rebooted = false
#http #http
if settings.modules.http if true
addCallback = (callback, text)-> addCallback = (callback, text)->
if not callback then return text if not callback then return text
return callback + "( " + text + " );" return callback + "( " + text + " );"
requestListener = (request, response)-> httpRequestListener = (request, response)->
parseQueryString = true parseQueryString = true
u = url.parse(request.url, parseQueryString) u = url.parse(request.url, parseQueryString)
#pass_validated = u.query.pass == settings.modules.http.password #pass_validated = u.query.pass == settings.modules.http.password
...@@ -3757,17 +3689,17 @@ if settings.modules.http ...@@ -3757,17 +3689,17 @@ if settings.modules.http
) )
else if u.pathname == '/api/duellog' and settings.modules.tournament_mode.enabled else if u.pathname == '/api/duellog' and settings.modules.mysql.enabled
if !await auth.auth(u.query.username, u.query.pass, "duel_log", "duel_log") if !await auth.auth(u.query.username, u.query.pass, "duel_log", "duel_log")
response.writeHead(200) response.writeHead(200)
response.end(addCallback(u.query.callback, "[{name:'密码错误'}]")) response.end(addCallback(u.query.callback, "[{name:'密码错误'}]"))
return return
else else
response.writeHead(200) response.writeHead(200)
duellog = JSON.stringify duel_log.duel_log, null, 2 duellog = JSON.stringify(await dataManager.getDuelLogJSON(settings.modules.tournament_mode), null, 2)
response.end(addCallback(u.query.callback, duellog)) response.end(addCallback(u.query.callback, duellog))
else if u.pathname == '/api/archive.zip' and settings.modules.tournament_mode.enabled else if u.pathname == '/api/archive.zip' and settings.modules.mysql.enabled
if !await auth.auth(u.query.username, u.query.pass, "download_replay", "download_replay_archive") if !await auth.auth(u.query.username, u.query.pass, "download_replay", "download_replay_archive")
response.writeHead(403) response.writeHead(403)
response.end("Invalid password.") response.end("Invalid password.")
...@@ -3777,9 +3709,9 @@ if settings.modules.http ...@@ -3777,9 +3709,9 @@ if settings.modules.http
archive_name = moment().format('YYYY-MM-DD HH-mm-ss') + ".zip" archive_name = moment().format('YYYY-MM-DD HH-mm-ss') + ".zip"
archive_args = ["a", "-mx0", "-y", archive_name] archive_args = ["a", "-mx0", "-y", archive_name]
check = false check = false
for replay in duel_log.duel_log for filename in await dataManager.getAllReplayFilenames()
check = true check = true
archive_args.push(replay.replay_filename) archive_args.push(filename)
if !check if !check
response.writeHead(403) response.writeHead(403)
response.end("Duel logs not found.") response.end("Duel logs not found.")
...@@ -3810,7 +3742,7 @@ if settings.modules.http ...@@ -3810,7 +3742,7 @@ if settings.modules.http
response.writeHead(403) response.writeHead(403)
response.end("Failed reading replays. " + error) response.end("Failed reading replays. " + error)
else if u.pathname == '/api/clearlog' and settings.modules.tournament_mode.enabled else if u.pathname == '/api/clearlog' and settings.modules.mysql.enabled
if !await auth.auth(u.query.username, u.query.pass, "clear_duel_log", "clear_duel_log") if !await auth.auth(u.query.username, u.query.pass, "clear_duel_log", "clear_duel_log")
response.writeHead(200) response.writeHead(200)
response.end(addCallback(u.query.callback, "[{name:'密码错误'}]")) response.end(addCallback(u.query.callback, "[{name:'密码错误'}]"))
...@@ -3818,15 +3750,14 @@ if settings.modules.http ...@@ -3818,15 +3750,14 @@ if settings.modules.http
else else
response.writeHead(200) response.writeHead(200)
if settings.modules.tournament_mode.log_save_path if settings.modules.tournament_mode.log_save_path
fs.writeFile(settings.modules.tournament_mode.log_save_path + 'duel_log.' + moment().format('YYYY-MM-DD HH-mm-ss') + '.json', JSON.stringify(duel_log, null, 2), (err) -> fs.writeFile(settings.modules.tournament_mode.log_save_path + 'duel_log.' + moment().format('YYYY-MM-DD HH-mm-ss') + '.json', JSON.stringify(await dataManager.getDuelLogJSON(settings.modules.tournament_mode), null, 2), (err) ->
if err if err
log.warn 'DUEL LOG SAVE ERROR', err log.warn 'DUEL LOG SAVE ERROR', err
) )
duel_log.duel_log = [] await dataManager.clearDuelLog()
setting_save(duel_log)
response.end(addCallback(u.query.callback, "[{name:'Success'}]")) response.end(addCallback(u.query.callback, "[{name:'Success'}]"))
else if _.startsWith(u.pathname, '/api/replay') and settings.modules.tournament_mode.enabled else if _.startsWith(u.pathname, '/api/replay') and settings.modules.mysql.enabled
if !await auth.auth(u.query.username, u.query.pass, "download_replay", "download_replay") if !await auth.auth(u.query.username, u.query.pass, "download_replay", "download_replay")
response.writeHead(403) response.writeHead(403)
response.end("密码错误") response.end("密码错误")
...@@ -3841,15 +3772,13 @@ if settings.modules.http ...@@ -3841,15 +3772,13 @@ if settings.modules.http
response.writeHead(404) response.writeHead(404)
response.end("bad filename") response.end("bad filename")
return return
fs.readFile(settings.modules.tournament_mode.replay_path + filename, (error, buffer)-> try
if error buffer = await fs.promises.readFile(settings.modules.tournament_mode.replay_path + filename)
response.writeHead(404)
response.end("未找到文件 " + filename)
else
response.writeHead(200, { "Content-Type": "application/octet-stream", "Content-Disposition": "attachment" }) response.writeHead(200, { "Content-Type": "application/octet-stream", "Content-Disposition": "attachment" })
response.end(buffer) response.end(buffer)
return catch e
) response.writeHead(404)
response.end("未找到文件 " + filename)
else if u.pathname == '/api/message' else if u.pathname == '/api/message'
#if !pass_validated #if !pass_validated
...@@ -3877,7 +3806,7 @@ if settings.modules.http ...@@ -3877,7 +3806,7 @@ if settings.modules.http
u.query.stop = false u.query.stop = false
response.writeHead(200) response.writeHead(200)
try try
await util.promisify(setting_change)(settings, 'modules:stop', u.query.stop) await setting_change(settings, 'modules:stop', u.query.stop)
response.end(addCallback(u.query.callback, "['stop ok', '" + u.query.stop + "']")) response.end(addCallback(u.query.callback, "['stop ok', '" + u.query.stop + "']"))
catch err catch err
response.end(addCallback(u.query.callback, "['stop fail', '" + u.query.stop + "']")) response.end(addCallback(u.query.callback, "['stop fail', '" + u.query.stop + "']"))
...@@ -3888,7 +3817,7 @@ if settings.modules.http ...@@ -3888,7 +3817,7 @@ if settings.modules.http
response.end(addCallback(u.query.callback, "['密码错误', 0]")) response.end(addCallback(u.query.callback, "['密码错误', 0]"))
return return
try try
await util.promisify(setting_change)(settings, 'modules:welcome', u.query.welcome) await setting_change(settings, 'modules:welcome', u.query.welcome)
response.end(addCallback(u.query.callback, "['welcome ok', '" + u.query.welcome + "']")) response.end(addCallback(u.query.callback, "['welcome ok', '" + u.query.welcome + "']"))
catch err catch err
response.end(addCallback(u.query.callback, "['welcome fail', '" + u.query.welcome + "']")) response.end(addCallback(u.query.callback, "['welcome fail', '" + u.query.welcome + "']"))
...@@ -3906,39 +3835,39 @@ if settings.modules.http ...@@ -3906,39 +3835,39 @@ if settings.modules.http
response.writeHead(200) response.writeHead(200)
response.end(addCallback(u.query.callback, "['密码错误', 0]")) response.end(addCallback(u.query.callback, "['密码错误', 0]"))
return return
load_tips((err)-> success = await load_tips()
response.writeHead(200) response.writeHead(200)
if(err) if success
response.end(addCallback(u.query.callback, "['tip fail', '" + settings.modules.tips.get + "']"))
else
response.end(addCallback(u.query.callback, "['tip ok', '" + settings.modules.tips.get + "']")) response.end(addCallback(u.query.callback, "['tip ok', '" + settings.modules.tips.get + "']"))
) else
response.end(addCallback(u.query.callback, "['tip fail', '" + settings.modules.tips.get + "']"))
else if u.query.loaddialogues else if u.query.loaddialogues
if !await auth.auth(u.query.username, u.query.pass, "change_settings", "change_dialogues") if !await auth.auth(u.query.username, u.query.pass, "change_settings", "change_dialogues")
response.writeHead(200) response.writeHead(200)
response.end(addCallback(u.query.callback, "['密码错误', 0]")) response.end(addCallback(u.query.callback, "['密码错误', 0]"))
return return
load_dialogues((err)-> success = await load_dialogues()
response.writeHead(200) response.writeHead(200)
if(err) if success
response.end(addCallback(u.query.callback, "['dialogues fail', '" + settings.modules.dialogues.get + "']")) response.end(addCallback(u.query.callback, "['dialogue ok', '" + settings.modules.tips.get + "']"))
else else
response.end(addCallback(u.query.callback, "['dialogues ok', '" +settings.modules.dialogues.get + "']")) response.end(addCallback(u.query.callback, "['dialogue fail', '" + settings.modules.tips.get + "']"))
)
else if u.query.ban else if u.query.ban
if !await auth.auth(u.query.username, u.query.pass, "ban_user", "ban_user") if !await auth.auth(u.query.username, u.query.pass, "ban_user", "ban_user")
response.writeHead(200) response.writeHead(200)
response.end(addCallback(u.query.callback, "['密码错误', 0]")) response.end(addCallback(u.query.callback, "['密码错误', 0]"))
return return
ban_user(u.query.ban, (err)-> try
await ban_user(u.query.ban)
catch e
log.warn("ban fail", e.toString())
response.writeHead(200) response.writeHead(200)
if(err)
response.end(addCallback(u.query.callback, "['ban fail', '" + u.query.ban + "']")) response.end(addCallback(u.query.callback, "['ban fail', '" + u.query.ban + "']"))
else return
response.writeHead(200)
response.end(addCallback(u.query.callback, "['ban ok', '" + u.query.ban + "']")) response.end(addCallback(u.query.callback, "['ban ok', '" + u.query.ban + "']"))
)
else if u.query.kick else if u.query.kick
if !await auth.auth(u.query.username, u.query.pass, "kick_user", "kick_user") if !await auth.auth(u.query.username, u.query.pass, "kick_user", "kick_user")
...@@ -4023,24 +3952,4 @@ if settings.modules.http ...@@ -4023,24 +3952,4 @@ if settings.modules.http
response.end() response.end()
return return
http_server = http.createServer(requestListener) init()
http_server.listen settings.modules.http.port
if settings.modules.http.ssl.enabled
https = require 'https'
options =
cert: fs.readFileSync(settings.modules.http.ssl.cert)
key: fs.readFileSync(settings.modules.http.ssl.key)
https_server = https.createServer(options, requestListener)
if settings.modules.http.websocket_roomlist and roomlist
roomlist.init https_server, ROOM_all
https_server.listen settings.modules.http.ssl.port
if not fs.existsSync('./plugins')
fs.mkdirSync('./plugins')
plugin_list = fs.readdirSync("./plugins")
for plugin_filename in plugin_list
plugin_path = process.cwd() + "/plugins/" + plugin_filename
require(plugin_path)
log.info("Plugin loaded:", plugin_filename)
This source diff could not be displayed because it is too large. You can view the blob instead.
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