mirror of
https://github.com/a-sync/game-server-watcher.git
synced 2026-07-28 00:12:35 -04:00
discord bot, index redirect, refactoring
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="7; url='https://github.com/a-sync/game-server-watcher'" />
|
||||
</head>
|
||||
<body>
|
||||
gsw...index
|
||||
<p>Please follow <a href="https://github.com/a-sync/game-server-watcher">this link</a>.</p>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
13
server.js
13
server.js
@@ -11,8 +11,8 @@ const watcher_1 = require("./src/watcher");
|
||||
const CACHE_MAX_AGE = parseInt(process.env.CACHE_MAX_AGE || '0', 10);
|
||||
const APP_HOST = process.env.app_host || '0.0.0.0';
|
||||
const APP_PORT = parseInt(process.env.app_port || '8080', 10);
|
||||
const DBG = Boolean(process.env.DBG || false);
|
||||
const SECRET = process.env.SECRET || 'secret';
|
||||
const DBG = Boolean(process.env.DBG || false);
|
||||
(0, http_1.createServer)(async (req, res) => {
|
||||
if (DBG)
|
||||
console.log('DBG: %j %j', (new Date()), req.url);
|
||||
@@ -24,17 +24,6 @@ const SECRET = process.env.SECRET || 'secret';
|
||||
});
|
||||
fs_1.default.createReadStream('./index.html').pipe(res);
|
||||
}
|
||||
else if (reqUrl.pathname === '/discord/post') { //DEBUG
|
||||
console.log('REQ.HEADERS', req.headers);
|
||||
let body = '';
|
||||
req.on('data', (chunk) => {
|
||||
body += chunk; // convert Buffer to string
|
||||
});
|
||||
req.on('end', () => {
|
||||
console.log('POST.DATA:', String(body));
|
||||
res.end('');
|
||||
});
|
||||
}
|
||||
else if (reqUrl.pathname === '/ping') {
|
||||
console.log('ping');
|
||||
res.end('pong');
|
||||
|
||||
16
server.ts
16
server.ts
@@ -4,14 +4,13 @@ import { URL } from 'url';
|
||||
|
||||
import 'dotenv/config';
|
||||
|
||||
import { main, WatcherConfig } from './src/watcher';
|
||||
import { main } from './src/watcher';
|
||||
|
||||
const CACHE_MAX_AGE = parseInt(process.env.CACHE_MAX_AGE || '0', 10);
|
||||
const APP_HOST = process.env.app_host || '0.0.0.0';
|
||||
const APP_PORT = parseInt(process.env.app_port || '8080', 10);
|
||||
|
||||
const DBG = Boolean(process.env.DBG || false);
|
||||
const SECRET = process.env.SECRET || 'secret';
|
||||
const DBG = Boolean(process.env.DBG || false);
|
||||
|
||||
createServer(async (req, res) => {
|
||||
if (DBG) console.log('DBG: %j %j', (new Date()), req.url);
|
||||
@@ -24,17 +23,6 @@ createServer(async (req, res) => {
|
||||
});
|
||||
fs.createReadStream('./index.html').pipe(res);
|
||||
}
|
||||
else if (reqUrl.pathname === '/discord/post') {//DEBUG
|
||||
console.log('REQ.HEADERS', req.headers);
|
||||
let body = '';
|
||||
req.on('data', (chunk) => {
|
||||
body += chunk; // convert Buffer to string
|
||||
});
|
||||
req.on('end', () => {
|
||||
console.log('POST.DATA:', String(body));
|
||||
res.end('');
|
||||
});
|
||||
}
|
||||
else if (reqUrl.pathname === '/ping') {
|
||||
console.log('ping');
|
||||
res.end('pong');
|
||||
|
||||
@@ -1,81 +1,150 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.serverUpdate = exports.init = void 0;
|
||||
const discord_js_1 = require("discord.js");
|
||||
//https://github.com/soulkobk/DiscordBot_GameStatus
|
||||
const prefix = '@gsw';
|
||||
let client;
|
||||
function init(token) {
|
||||
client = new discord_js_1.Client({
|
||||
//messageEditHistoryMaxSize: 0,
|
||||
//ws: {intents: ['GUILDS', 'GUILD_MESSAGES']}
|
||||
const lowdb_1 = require("@commonify/lowdb");
|
||||
const hhmmss_1 = __importDefault(require("./lib/hhmmss"));
|
||||
const DATA_PATH = process.env.DATA_PATH || './data/';
|
||||
const DBG = Boolean(process.env.DBG || false);
|
||||
const adapter = new lowdb_1.JSONFile(DATA_PATH + 'discord.json');
|
||||
const db = new lowdb_1.Low(adapter);
|
||||
const serverInfoMessages = [];
|
||||
let bot;
|
||||
async function init(token) {
|
||||
console.log('discord-bot starting...');
|
||||
bot = new discord_js_1.Client({
|
||||
messageEditHistoryMaxSize: 0,
|
||||
ws: { intents: ['GUILDS', 'GUILD_MESSAGES'] }
|
||||
});
|
||||
console.log(' dc.init'); //DEBUG
|
||||
client.on('ready', () => {
|
||||
console.log(' dc.client.READY'); //DEBUG
|
||||
bot.on('error', e => {
|
||||
console.error('discord-bot ERROR', e.message || e);
|
||||
});
|
||||
client.on('message', msg => {
|
||||
if (msg.content === 'ping') {
|
||||
msg.reply('pong');
|
||||
await new Promise((resolve, reject) => {
|
||||
bot.once('ready', () => {
|
||||
console.log('discord-bot ready', bot.user);
|
||||
bot.removeListener('error', reject);
|
||||
resolve();
|
||||
});
|
||||
bot.once('error', reject);
|
||||
if (DBG) {
|
||||
bot.on('message', msg => {
|
||||
if (msg.content === 'ping') {
|
||||
msg.reply('pong');
|
||||
}
|
||||
});
|
||||
}
|
||||
return bot.login(token);
|
||||
});
|
||||
// client.on('messageCreate', (message) => {
|
||||
// console.log('!!!!!dc.messageCreate', message.author, message.content);//DEBUG
|
||||
// if (message.author.bot) return;
|
||||
// if (!message.content.startsWith(prefix)) return;
|
||||
// const commandBody = message.content.slice(prefix.length);
|
||||
// const args = commandBody.split(' ');
|
||||
// const command = String(args.shift()).toLowerCase();
|
||||
// if (command === 'ping') {
|
||||
// const timeTaken = Date.now() - message.createdTimestamp;
|
||||
// message.reply(`Pong! ${timeTaken}ms`);
|
||||
// }
|
||||
// });
|
||||
return client.login(token);
|
||||
await db.read();
|
||||
db.data = db.data || [];
|
||||
}
|
||||
exports.init = init;
|
||||
async function serverUpdate(gs) {
|
||||
if (!gs.info)
|
||||
return;
|
||||
console.log('discord.serverUpdate', gs.config.host, gs.config.port, gs.config.discord);
|
||||
/*
|
||||
for (const cid of gs.config.discord.chatIds) {
|
||||
let m = await getServerInfoMessage(cid, gs.config.host, gs.config.port);
|
||||
|
||||
const stats = gs.history.stats();
|
||||
let statsText = '';
|
||||
if (stats.length > 0) {
|
||||
const s = stats.pop();
|
||||
if (s) {
|
||||
statsText = ' (hourly max: ' + s.max + ', hourly avg: ' + s.avg.toFixed(1) + ')';
|
||||
}
|
||||
}
|
||||
|
||||
const infoText: string[] = [
|
||||
gs.niceName,
|
||||
gs.info.game + ' / ' + gs.info.map,
|
||||
gs.info.connect,
|
||||
'Players ' + gs.info.playersNum + '/' + gs.info.playersMax + statsText
|
||||
];
|
||||
|
||||
if (gs.info?.players.length > 0) {
|
||||
infoText.push('```');
|
||||
for(const p of gs.info?.players) {
|
||||
let playerLine = '';
|
||||
if (p.raw?.time !== undefined) {
|
||||
playerLine += '[' + hhmmss(p.raw.time) + '] ';
|
||||
}
|
||||
playerLine += p.name;
|
||||
if (p.raw?.score !== undefined) {
|
||||
playerLine += ' [score: ' + p.raw.score + ']';
|
||||
}
|
||||
infoText.push(playerLine);
|
||||
}
|
||||
infoText.push('```');
|
||||
}
|
||||
|
||||
m.setText(infoText.join('\n'));
|
||||
}
|
||||
*/
|
||||
if (DBG)
|
||||
console.log('discord.serverUpdate', gs.config.host, gs.config.port, gs.config.discord);
|
||||
for (const cid of gs.config.discord.channelIds) {
|
||||
let m = await getServerInfoMessage(cid, gs.config.host, gs.config.port);
|
||||
m.updatePost(gs);
|
||||
}
|
||||
}
|
||||
exports.serverUpdate = serverUpdate;
|
||||
async function getServerInfoMessage(cid, host, port) {
|
||||
let m = serverInfoMessages.find(n => {
|
||||
return n.channelId === cid && n.host === host && n.port === port;
|
||||
});
|
||||
if (!m) {
|
||||
m = new ServerInfoMessage(cid, host, port);
|
||||
let msgId;
|
||||
if (db.data) {
|
||||
const md = db.data.find(d => {
|
||||
return d.channelId === cid && d.host === host && d.port === port;
|
||||
});
|
||||
if (md) {
|
||||
msgId = md.messageId;
|
||||
}
|
||||
}
|
||||
await m.init(msgId);
|
||||
serverInfoMessages.push(m);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
class ServerInfoMessage {
|
||||
constructor(channelId, host, port) {
|
||||
this.messageId = '0';
|
||||
this.channelId = channelId;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
async init(msgId) {
|
||||
this.channel = await bot.channels.fetch(this.channelId);
|
||||
if (msgId) {
|
||||
this.messageId = msgId;
|
||||
this.message = await this.channel.messages.fetch(msgId);
|
||||
}
|
||||
else {
|
||||
let embed = new discord_js_1.MessageEmbed();
|
||||
embed.setTitle('Initializing server info... ' + (new Date()).toISOString());
|
||||
embed.setColor('#00ff00');
|
||||
this.message = await this.channel.send({ embed });
|
||||
this.messageId = this.message.id;
|
||||
}
|
||||
if (db.data) {
|
||||
const mi = db.data.findIndex(d => {
|
||||
return d.channelId === this.channelId && d.host === this.host && d.port === this.port;
|
||||
});
|
||||
if (mi === -1 || mi === undefined) {
|
||||
db.data.push({
|
||||
channelId: this.channelId,
|
||||
host: this.host,
|
||||
port: this.port,
|
||||
messageId: this.messageId
|
||||
});
|
||||
}
|
||||
else {
|
||||
db.data[mi].messageId = this.messageId;
|
||||
}
|
||||
await db.write();
|
||||
}
|
||||
}
|
||||
async updatePost(gs) {
|
||||
var _a, _b, _c, _d;
|
||||
if (!this.message)
|
||||
return;
|
||||
const embed = new discord_js_1.MessageEmbed();
|
||||
embed.setTitle(gs.niceName + ' offline...');
|
||||
embed.setColor('#ff0000');
|
||||
embed.setFooter('Last updated');
|
||||
embed.setTimestamp();
|
||||
if (gs.info && gs.online) {
|
||||
embed.setTitle(gs.niceName);
|
||||
embed.setColor('#000000');
|
||||
embed.addField('Game', gs.info.game, true);
|
||||
embed.addField('Map', gs.info.map, true);
|
||||
embed.addField('Connect', 'steam://connect/' + gs.info.connect);
|
||||
if (((_a = gs.info) === null || _a === void 0 ? void 0 : _a.players.length) > 0) {
|
||||
const pNames = [];
|
||||
const pTimes = [];
|
||||
const pScores = [];
|
||||
let c = 0;
|
||||
for (const p of (_b = gs.info) === null || _b === void 0 ? void 0 : _b.players) {
|
||||
c++;
|
||||
pNames.push(p.name || 'n/a');
|
||||
pTimes.push((0, hhmmss_1.default)(((_c = p.raw) === null || _c === void 0 ? void 0 : _c.time) || 0));
|
||||
pScores.push(((_d = p.raw) === null || _d === void 0 ? void 0 : _d.score) || '0');
|
||||
}
|
||||
embed.addField('Players ' + gs.info.playersNum + '/' + gs.info.playersMax, '```\n' + pNames.join('\n').slice(0, 1016) + '\n```', true);
|
||||
embed.addField('Time', '```\n' + pTimes.join('\n').slice(0, 1016) + '\n```', true);
|
||||
embed.addField('Score', '```\n' + pScores.join('\n').slice(0, 1016) + '\n```', true);
|
||||
}
|
||||
}
|
||||
try {
|
||||
await this.message.edit(null, { embed });
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e.message || e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,89 +1,183 @@
|
||||
|
||||
import {Client, MessageEmbed, Intents} from 'discord.js';
|
||||
import { Client, MessageEmbed, TextChannel, Message } from 'discord.js';
|
||||
import { Low, JSONFile } from '@commonify/lowdb';
|
||||
import { GameServer } from './game-server';
|
||||
import hhmmss from './lib/hhmmss';
|
||||
|
||||
//https://github.com/soulkobk/DiscordBot_GameStatus
|
||||
const prefix = '@gsw';
|
||||
let client: Client;
|
||||
const DATA_PATH = process.env.DATA_PATH || './data/';
|
||||
const DBG = Boolean(process.env.DBG || false);
|
||||
|
||||
export function init(token: string) {
|
||||
client = new Client({
|
||||
//messageEditHistoryMaxSize: 0,
|
||||
//ws: {intents: ['GUILDS', 'GUILD_MESSAGES']}
|
||||
interface DiscordData {
|
||||
channelId: string;
|
||||
host: string;
|
||||
port: number;
|
||||
messageId: string;
|
||||
}
|
||||
|
||||
const adapter = new JSONFile<DiscordData[]>(DATA_PATH + 'discord.json');
|
||||
const db = new Low<DiscordData[]>(adapter);
|
||||
|
||||
const serverInfoMessages: ServerInfoMessage[] = [];
|
||||
|
||||
let bot: Client;
|
||||
export async function init(token: string) {
|
||||
console.log('discord-bot starting...');
|
||||
bot = new Client({
|
||||
messageEditHistoryMaxSize: 0,
|
||||
ws: { intents: ['GUILDS', 'GUILD_MESSAGES'] }
|
||||
});
|
||||
|
||||
console.log(' dc.init');//DEBUG
|
||||
bot.on('error', e => {
|
||||
console.error('discord-bot ERROR', e.message || e);
|
||||
});
|
||||
|
||||
client.on('ready', ()=> {
|
||||
console.log(' dc.client.READY');//DEBUG
|
||||
})
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
bot.once('ready', () => {
|
||||
console.log('discord-bot ready', bot.user);
|
||||
|
||||
client.on('message', msg => {
|
||||
if (msg.content === 'ping') {
|
||||
msg.reply('pong');
|
||||
bot.removeListener('error', reject);
|
||||
resolve();
|
||||
});
|
||||
|
||||
bot.once('error', reject);
|
||||
|
||||
if (DBG) {
|
||||
bot.on('message', msg => {
|
||||
if (msg.content === 'ping') {
|
||||
msg.reply('pong');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return bot.login(token);
|
||||
});
|
||||
|
||||
// client.on('messageCreate', (message) => {
|
||||
// console.log('!!!!!dc.messageCreate', message.author, message.content);//DEBUG
|
||||
|
||||
// if (message.author.bot) return;
|
||||
// if (!message.content.startsWith(prefix)) return;
|
||||
|
||||
// const commandBody = message.content.slice(prefix.length);
|
||||
// const args = commandBody.split(' ');
|
||||
// const command = String(args.shift()).toLowerCase();
|
||||
|
||||
// if (command === 'ping') {
|
||||
// const timeTaken = Date.now() - message.createdTimestamp;
|
||||
// message.reply(`Pong! ${timeTaken}ms`);
|
||||
// }
|
||||
// });
|
||||
|
||||
return client.login(token);
|
||||
await db.read();
|
||||
db.data = db.data || [];
|
||||
}
|
||||
|
||||
export async function serverUpdate(gs: GameServer) {
|
||||
if (!gs.info) return;
|
||||
if (DBG) console.log('discord.serverUpdate', gs.config.host, gs.config.port, gs.config.discord);
|
||||
|
||||
console.log('discord.serverUpdate', gs.config.host, gs.config.port, gs.config.discord);
|
||||
/*
|
||||
for (const cid of gs.config.discord.chatIds) {
|
||||
for (const cid of gs.config.discord.channelIds) {
|
||||
let m = await getServerInfoMessage(cid, gs.config.host, gs.config.port);
|
||||
|
||||
const stats = gs.history.stats();
|
||||
let statsText = '';
|
||||
if (stats.length > 0) {
|
||||
const s = stats.pop();
|
||||
if (s) {
|
||||
statsText = ' (hourly max: ' + s.max + ', hourly avg: ' + s.avg.toFixed(1) + ')';
|
||||
}
|
||||
}
|
||||
|
||||
const infoText: string[] = [
|
||||
gs.niceName,
|
||||
gs.info.game + ' / ' + gs.info.map,
|
||||
gs.info.connect,
|
||||
'Players ' + gs.info.playersNum + '/' + gs.info.playersMax + statsText
|
||||
];
|
||||
|
||||
if (gs.info?.players.length > 0) {
|
||||
infoText.push('```');
|
||||
for(const p of gs.info?.players) {
|
||||
let playerLine = '';
|
||||
if (p.raw?.time !== undefined) {
|
||||
playerLine += '[' + hhmmss(p.raw.time) + '] ';
|
||||
}
|
||||
playerLine += p.name;
|
||||
if (p.raw?.score !== undefined) {
|
||||
playerLine += ' [score: ' + p.raw.score + ']';
|
||||
}
|
||||
infoText.push(playerLine);
|
||||
}
|
||||
infoText.push('```');
|
||||
}
|
||||
|
||||
m.setText(infoText.join('\n'));
|
||||
m.updatePost(gs);
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
async function getServerInfoMessage(cid: string, host: string, port: number) {
|
||||
let m = serverInfoMessages.find(n => {
|
||||
return n.channelId === cid && n.host === host && n.port === port;
|
||||
});
|
||||
|
||||
if (!m) {
|
||||
m = new ServerInfoMessage(cid, host, port);
|
||||
|
||||
let msgId;
|
||||
if (db.data) {
|
||||
const md = db.data.find(d => {
|
||||
return d.channelId === cid && d.host === host && d.port === port;
|
||||
});
|
||||
if (md) {
|
||||
msgId = md.messageId;
|
||||
}
|
||||
}
|
||||
|
||||
await m.init(msgId);
|
||||
|
||||
serverInfoMessages.push(m);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
class ServerInfoMessage {
|
||||
public channelId: string;
|
||||
public host: string;
|
||||
public port: number;
|
||||
public messageId: string = '0';
|
||||
private channel?: TextChannel;
|
||||
private message?: Message;
|
||||
|
||||
constructor(channelId: string, host: string, port: number) {
|
||||
this.channelId = channelId;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
async init(msgId?: string) {
|
||||
this.channel = await bot.channels.fetch(this.channelId) as TextChannel;
|
||||
|
||||
if (msgId) {
|
||||
this.messageId = msgId;
|
||||
this.message = await this.channel.messages.fetch(msgId);
|
||||
} else {
|
||||
let embed = new MessageEmbed();
|
||||
embed.setTitle('Initializing server info... ' + (new Date()).toISOString());
|
||||
embed.setColor('#00ff00');
|
||||
|
||||
this.message = await this.channel.send({ embed });
|
||||
this.messageId = this.message.id;
|
||||
}
|
||||
|
||||
if (db.data) {
|
||||
const mi = db.data.findIndex(d => {
|
||||
return d.channelId === this.channelId && d.host === this.host && d.port === this.port;
|
||||
});
|
||||
|
||||
if (mi === -1 || mi === undefined) {
|
||||
db.data.push({
|
||||
channelId: this.channelId,
|
||||
host: this.host,
|
||||
port: this.port,
|
||||
messageId: this.messageId
|
||||
});
|
||||
} else {
|
||||
db.data[mi].messageId = this.messageId;
|
||||
}
|
||||
|
||||
await db.write();
|
||||
}
|
||||
}
|
||||
|
||||
async updatePost(gs: GameServer) {
|
||||
if (!this.message) return;
|
||||
const embed = new MessageEmbed();
|
||||
embed.setTitle(gs.niceName + ' offline...');
|
||||
embed.setColor('#ff0000');
|
||||
embed.setFooter('Last updated');
|
||||
embed.setTimestamp();
|
||||
|
||||
if (gs.info && gs.online) {
|
||||
embed.setTitle(gs.niceName);
|
||||
embed.setColor('#000000');
|
||||
|
||||
embed.addField('Game', gs.info.game, true);
|
||||
embed.addField('Map', gs.info.map, true);
|
||||
embed.addField('Connect', 'steam://connect/' + gs.info.connect);
|
||||
|
||||
if (gs.info?.players.length > 0) {
|
||||
const pNames: string[] = [];
|
||||
const pTimes: string[] = [];
|
||||
const pScores: string[] = [];
|
||||
let c = 0;
|
||||
for (const p of gs.info?.players) {
|
||||
c++;
|
||||
pNames.push(p.name || 'n/a');
|
||||
pTimes.push(hhmmss(p.raw?.time || 0));
|
||||
pScores.push(p.raw?.score || '0');
|
||||
}
|
||||
|
||||
embed.addField('Players ' + gs.info.playersNum + '/' + gs.info.playersMax, '```\n' + pNames.join('\n').slice(0, 1016) + '\n```', true);
|
||||
embed.addField('Time', '```\n' + pTimes.join('\n').slice(0, 1016) + '\n```', true);
|
||||
embed.addField('Score', '```\n' + pScores.join('\n').slice(0, 1016) + '\n```', true);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.message.edit(null, { embed });
|
||||
} catch (e: any) {
|
||||
console.log(e.message || e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,24 +27,26 @@ function saveDb() {
|
||||
exports.saveDb = saveDb;
|
||||
class GameServer {
|
||||
constructor(config) {
|
||||
console.log('game-server.init', config.host, config.port, config.type, config.appId);
|
||||
this.online = false;
|
||||
console.log('game-server init', config.host, config.port, config.type, config.appId);
|
||||
this.config = config;
|
||||
this.history = new ServerHistory(config.host + ':' + config.port);
|
||||
this._niceName = config.host + ':' + config.port;
|
||||
}
|
||||
async update() {
|
||||
let info;
|
||||
info = await this.gamedig();
|
||||
let info = await this.gamedig();
|
||||
if (!info && STEAM_WEB_API_KEY) {
|
||||
info = await this.steam();
|
||||
}
|
||||
if (info) {
|
||||
this.online = true;
|
||||
this.info = info;
|
||||
this.history.add(info);
|
||||
}
|
||||
else {
|
||||
console.error('game-server.update no info!');
|
||||
this.online = false;
|
||||
console.error('game-server not available', this.config.host, this.config.port);
|
||||
}
|
||||
return;
|
||||
}
|
||||
async gamedig() {
|
||||
try {
|
||||
@@ -111,21 +113,25 @@ class GameServer {
|
||||
}
|
||||
get niceName() {
|
||||
var _a;
|
||||
let nn = ((_a = this.info) === null || _a === void 0 ? void 0 : _a.name) || '';
|
||||
for (let i = 0; i < nn.length; i++) {
|
||||
if (nn[i] == '^') {
|
||||
nn = nn.slice(0, i) + ' ' + nn.slice(i + 2);
|
||||
}
|
||||
else if (nn[i] == '█') {
|
||||
nn = nn.slice(0, i) + ' ' + nn.slice(i + 1);
|
||||
}
|
||||
else if (nn[i] == '<27>') {
|
||||
nn = nn.slice(0, i) + ' ' + nn.slice(i + 2);
|
||||
let nn = (_a = this.info) === null || _a === void 0 ? void 0 : _a.name;
|
||||
if (nn) {
|
||||
for (let i = 0; i < nn.length; i++) {
|
||||
if (nn[i] == '^') {
|
||||
nn = nn.slice(0, i) + ' ' + nn.slice(i + 2);
|
||||
}
|
||||
else if (nn[i] == '█') {
|
||||
nn = nn.slice(0, i) + ' ' + nn.slice(i + 1);
|
||||
}
|
||||
else if (nn[i] == '<27>') {
|
||||
nn = nn.slice(0, i) + ' ' + nn.slice(i + 2);
|
||||
}
|
||||
;
|
||||
}
|
||||
;
|
||||
if (nn)
|
||||
this._niceName = nn;
|
||||
}
|
||||
;
|
||||
return nn;
|
||||
return this._niceName;
|
||||
}
|
||||
}
|
||||
exports.GameServer = GameServer;
|
||||
|
||||
@@ -55,29 +55,31 @@ export class GameServer {
|
||||
public info?: Info;
|
||||
public config: WatcherConfig;
|
||||
public history: ServerHistory;
|
||||
private _niceName: string;
|
||||
public online: boolean = false;
|
||||
|
||||
constructor(config: WatcherConfig) {
|
||||
console.log('game-server.init', config.host, config.port, config.type, config.appId);
|
||||
console.log('game-server init', config.host, config.port, config.type, config.appId);
|
||||
this.config = config;
|
||||
this.history = new ServerHistory(config.host + ':' + config.port);
|
||||
this._niceName = config.host + ':' + config.port;
|
||||
}
|
||||
|
||||
async update() {
|
||||
let info: Info | null;
|
||||
info = await this.gamedig();
|
||||
let info = await this.gamedig();
|
||||
|
||||
if (!info && STEAM_WEB_API_KEY) {
|
||||
info = await this.steam();
|
||||
}
|
||||
|
||||
if (info) {
|
||||
this.online = true;
|
||||
this.info = info;
|
||||
this.history.add(info);
|
||||
} else {
|
||||
console.error('game-server.update no info!');
|
||||
this.online = false;
|
||||
console.error('game-server not available', this.config.host, this.config.port);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
async gamedig(): Promise<Info | null> {
|
||||
@@ -88,7 +90,7 @@ export class GameServer {
|
||||
type: this.config.type,
|
||||
}) as qRes;
|
||||
|
||||
const raw = res.raw as { game: string; folder: string;};
|
||||
const raw = res.raw as { game: string; folder: string; };
|
||||
const game = raw.game || raw.folder || this.config.type;
|
||||
|
||||
const players: Player[] = res.players;//todo: map / filter
|
||||
@@ -151,19 +153,23 @@ export class GameServer {
|
||||
}
|
||||
|
||||
get niceName() {
|
||||
let nn = this.info?.name || '';
|
||||
let nn = this.info?.name;
|
||||
|
||||
for (let i = 0; i < nn.length; i++) {
|
||||
if (nn[i] == '^') {
|
||||
nn = nn.slice(0, i) + ' ' + nn.slice(i+2);
|
||||
} else if (nn[i] == '█') {
|
||||
nn = nn.slice(0, i) + ' ' + nn.slice(i+1);
|
||||
} else if (nn[i] == '<27>') {
|
||||
nn = nn.slice(0, i) + ' ' + nn.slice(i+2);
|
||||
if (nn) {
|
||||
for (let i = 0; i < nn.length; i++) {
|
||||
if (nn[i] == '^') {
|
||||
nn = nn.slice(0, i) + ' ' + nn.slice(i + 2);
|
||||
} else if (nn[i] == '█') {
|
||||
nn = nn.slice(0, i) + ' ' + nn.slice(i + 1);
|
||||
} else if (nn[i] == '<EFBFBD>') {
|
||||
nn = nn.slice(0, i) + ' ' + nn.slice(i + 2);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
return nn;
|
||||
if (nn) this._niceName = nn;
|
||||
}
|
||||
|
||||
return this._niceName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,7 +203,7 @@ class ServerHistory {
|
||||
|
||||
const d = new Date();
|
||||
const dh = this.yyyymmddhh(d);
|
||||
|
||||
|
||||
if (!db.data.population[this.id]) {
|
||||
db.data.population[this.id] = [];
|
||||
}
|
||||
|
||||
@@ -4,12 +4,14 @@ exports.default = (sec_num) => {
|
||||
let hours = Math.floor(sec_num / 3600);
|
||||
let minutes = Math.floor((sec_num - (hours * 3600)) / 60);
|
||||
let secs = Math.floor(sec_num - (hours * 3600) - (minutes * 60));
|
||||
//if (hours < 10) {hours = `0${hours}`;}
|
||||
if (hours == 0) {
|
||||
hours = '';
|
||||
if (hours === 0) {
|
||||
hours = '00';
|
||||
}
|
||||
else if (hours < 10) {
|
||||
hours = `0${hours}`;
|
||||
}
|
||||
else {
|
||||
hours = `${hours}:`;
|
||||
hours = `${hours}`;
|
||||
}
|
||||
if (minutes < 10) {
|
||||
minutes = `0${minutes}`;
|
||||
@@ -17,5 +19,5 @@ exports.default = (sec_num) => {
|
||||
if (secs < 10) {
|
||||
secs = `0${secs}`;
|
||||
}
|
||||
return [hours, minutes, ':', secs].join('');
|
||||
return [hours, ':', minutes, ':', secs].join('');
|
||||
};
|
||||
|
||||
@@ -3,10 +3,10 @@ export default (sec_num: number) => {
|
||||
let minutes: number | string = Math.floor((sec_num - (hours * 3600)) / 60);
|
||||
let secs: number | string = Math.floor(sec_num - (hours * 3600) - (minutes * 60));
|
||||
|
||||
//if (hours < 10) {hours = `0${hours}`;}
|
||||
if (hours == 0) {hours = '';}
|
||||
else {hours = `${hours}:`;}
|
||||
if (minutes < 10) { minutes = `0${minutes}`; }
|
||||
if (secs < 10) { secs = `0${secs}`; }
|
||||
return [hours, minutes, ':', secs].join('');
|
||||
if (hours === 0) { hours = '00' }
|
||||
else if (hours < 10) { hours = `0${hours}` }
|
||||
else { hours = `${hours}` }
|
||||
if (minutes < 10) { minutes = `0${minutes}` }
|
||||
if (secs < 10) { secs = `0${secs}` }
|
||||
return [hours, ':', minutes, ':', secs].join('');
|
||||
}
|
||||
|
||||
@@ -5,9 +5,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.serverUpdate = exports.init = void 0;
|
||||
const grammy_1 = require("grammy");
|
||||
const hhmmss_1 = __importDefault(require("./lib/hhmmss"));
|
||||
const lowdb_1 = require("@commonify/lowdb");
|
||||
const hhmmss_1 = __importDefault(require("./lib/hhmmss"));
|
||||
const DATA_PATH = process.env.DATA_PATH || './data/';
|
||||
const DBG = Boolean(process.env.DBG || false);
|
||||
const adapter = new lowdb_1.JSONFile(DATA_PATH + 'telegram.json');
|
||||
const db = new lowdb_1.Low(adapter);
|
||||
const serverInfoMessages = [];
|
||||
@@ -15,14 +16,21 @@ let bot;
|
||||
async function init(token) {
|
||||
console.log('telegram-bot starting...');
|
||||
bot = new grammy_1.Bot(token);
|
||||
bot.catch(e => {
|
||||
console.error('telegram-bot ERROR', e.message || e);
|
||||
});
|
||||
const me = await bot.api.getMe();
|
||||
console.log('telegram-bot ready', me);
|
||||
// bot.on('message:text', ctx => {ctx.reply('echo: ' + ctx.message.text);});
|
||||
// bot.command('start', ctx => ctx.reply('cmd.start.response'));
|
||||
// bot.start();
|
||||
if (DBG) {
|
||||
bot.on('message:text', ctx => {
|
||||
if (ctx.message.text === 'ping')
|
||||
ctx.reply('pong');
|
||||
});
|
||||
// bot.command('ping', ctx => ctx.reply('/pong'));
|
||||
bot.start();
|
||||
}
|
||||
await db.read();
|
||||
db.data = db.data || [];
|
||||
return;
|
||||
}
|
||||
exports.init = init;
|
||||
async function getServerInfoMessage(cid, host, port) {
|
||||
@@ -46,42 +54,11 @@ async function getServerInfoMessage(cid, host, port) {
|
||||
return m;
|
||||
}
|
||||
async function serverUpdate(gs) {
|
||||
var _a, _b, _c, _d;
|
||||
if (!gs.info)
|
||||
return;
|
||||
console.log('telegram.serverUpdate', gs.config.host, gs.config.port, gs.config.telegram);
|
||||
if (DBG)
|
||||
console.log('telegram.serverUpdate', gs.config.host, gs.config.port, gs.config.telegram);
|
||||
for (const cid of gs.config.telegram.chatIds) {
|
||||
let m = await getServerInfoMessage(cid, gs.config.host, gs.config.port);
|
||||
const stats = gs.history.stats();
|
||||
let statsText = '';
|
||||
if (stats.length > 0) {
|
||||
const s = stats.pop();
|
||||
if (s) {
|
||||
statsText = ' (hourly max: ' + s.max + ', hourly avg: ' + s.avg.toFixed(1) + ')';
|
||||
}
|
||||
}
|
||||
const infoText = [
|
||||
gs.niceName,
|
||||
gs.info.game + ' / ' + gs.info.map,
|
||||
gs.info.connect,
|
||||
'Players ' + gs.info.playersNum + '/' + gs.info.playersMax + statsText
|
||||
];
|
||||
if (((_a = gs.info) === null || _a === void 0 ? void 0 : _a.players.length) > 0) {
|
||||
infoText.push('```');
|
||||
for (const p of (_b = gs.info) === null || _b === void 0 ? void 0 : _b.players) {
|
||||
let playerLine = '';
|
||||
if (((_c = p.raw) === null || _c === void 0 ? void 0 : _c.time) !== undefined) {
|
||||
playerLine += '[' + (0, hhmmss_1.default)(p.raw.time) + '] ';
|
||||
}
|
||||
playerLine += p.name;
|
||||
if (((_d = p.raw) === null || _d === void 0 ? void 0 : _d.score) !== undefined) {
|
||||
playerLine += ' [score: ' + p.raw.score + ']';
|
||||
}
|
||||
infoText.push(playerLine);
|
||||
}
|
||||
infoText.push('```');
|
||||
}
|
||||
m.setText(infoText.join('\n'));
|
||||
m.updatePost(gs);
|
||||
}
|
||||
}
|
||||
exports.serverUpdate = serverUpdate;
|
||||
@@ -118,10 +95,42 @@ class ServerInfoMessage {
|
||||
await db.write();
|
||||
}
|
||||
}
|
||||
async setText(text) {
|
||||
console.log('setText', this.host, this.port);
|
||||
async updatePost(gs) {
|
||||
var _a, _b;
|
||||
let infoText = gs.niceName + ' offline...';
|
||||
if (gs.info && gs.online) {
|
||||
const stats = gs.history.stats();
|
||||
let statsText = '';
|
||||
if (stats.length > 0) {
|
||||
const s = stats.slice(-1).pop();
|
||||
if (s) {
|
||||
statsText = ' (hourly avg/max: ' + s.avg.toFixed(1) + '/' + s.max + ') ';
|
||||
}
|
||||
}
|
||||
infoText = [
|
||||
gs.niceName,
|
||||
gs.info.game + ' / ' + gs.info.map,
|
||||
gs.info.connect,
|
||||
'Players ' + gs.info.playersNum + '/' + gs.info.playersMax + statsText
|
||||
].join('\n');
|
||||
if (gs.info.players.length > 0 && gs.info.players[0].name !== undefined) {
|
||||
const pnArr = [];
|
||||
for (const p of gs.info.players) {
|
||||
let playerLine = '';
|
||||
if (((_a = p.raw) === null || _a === void 0 ? void 0 : _a.time) !== undefined) {
|
||||
playerLine += (0, hhmmss_1.default)(p.raw.time) + ' ';
|
||||
}
|
||||
playerLine += p.name;
|
||||
if (((_b = p.raw) === null || _b === void 0 ? void 0 : _b.score) !== undefined) {
|
||||
playerLine += ' (' + p.raw.score + ')';
|
||||
}
|
||||
pnArr.push(playerLine);
|
||||
}
|
||||
infoText += '```\n' + pnArr.join('\n').slice(0, 4088 - infoText.length) + '\n```';
|
||||
}
|
||||
}
|
||||
try {
|
||||
await bot.api.editMessageText(this.chatId, this.messageId, text, { parse_mode: 'Markdown' });
|
||||
await bot.api.editMessageText(this.chatId, this.messageId, infoText, { parse_mode: 'Markdown' });
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e.message || e);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { Bot } from 'grammy';
|
||||
import { Low, JSONFile } from '@commonify/lowdb';
|
||||
import { GameServer } from './game-server';
|
||||
import hhmmss from './lib/hhmmss';
|
||||
import { Low, JSONFile } from '@commonify/lowdb';
|
||||
|
||||
const DATA_PATH = process.env.DATA_PATH || './data/';
|
||||
const DBG = Boolean(process.env.DBG || false);
|
||||
|
||||
interface TelegramData {
|
||||
chatId: string;
|
||||
@@ -22,17 +23,24 @@ export async function init(token: string) {
|
||||
console.log('telegram-bot starting...');
|
||||
bot = new Bot(token);
|
||||
|
||||
bot.catch(e => {
|
||||
console.error('telegram-bot ERROR', e.message || e);
|
||||
});
|
||||
|
||||
const me = await bot.api.getMe();
|
||||
console.log('telegram-bot ready', me);
|
||||
|
||||
// bot.on('message:text', ctx => {ctx.reply('echo: ' + ctx.message.text);});
|
||||
// bot.command('start', ctx => ctx.reply('cmd.start.response'));
|
||||
// bot.start();
|
||||
if(DBG) {
|
||||
bot.on('message:text', ctx => {
|
||||
if (ctx.message.text === 'ping')
|
||||
ctx.reply('pong');
|
||||
});
|
||||
// bot.command('ping', ctx => ctx.reply('/pong'));
|
||||
bot.start();
|
||||
}
|
||||
|
||||
await db.read();
|
||||
db.data = db.data || [];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
async function getServerInfoMessage(cid: string, host: string, port: number) {
|
||||
@@ -62,46 +70,11 @@ async function getServerInfoMessage(cid: string, host: string, port: number) {
|
||||
}
|
||||
|
||||
export async function serverUpdate(gs: GameServer) {
|
||||
if (!gs.info) return;
|
||||
|
||||
console.log('telegram.serverUpdate', gs.config.host, gs.config.port, gs.config.telegram);
|
||||
if (DBG) console.log('telegram.serverUpdate', gs.config.host, gs.config.port, gs.config.telegram);
|
||||
|
||||
for (const cid of gs.config.telegram.chatIds) {
|
||||
let m = await getServerInfoMessage(cid, gs.config.host, gs.config.port);
|
||||
|
||||
const stats = gs.history.stats();
|
||||
let statsText = '';
|
||||
if (stats.length > 0) {
|
||||
const s = stats.pop();
|
||||
if (s) {
|
||||
statsText = ' (hourly max: ' + s.max + ', hourly avg: ' + s.avg.toFixed(1) + ')';
|
||||
}
|
||||
}
|
||||
|
||||
const infoText: string[] = [
|
||||
gs.niceName,
|
||||
gs.info.game + ' / ' + gs.info.map,
|
||||
gs.info.connect,
|
||||
'Players ' + gs.info.playersNum + '/' + gs.info.playersMax + statsText
|
||||
];
|
||||
|
||||
if (gs.info?.players.length > 0) {
|
||||
infoText.push('```');
|
||||
for(const p of gs.info?.players) {
|
||||
let playerLine = '';
|
||||
if (p.raw?.time !== undefined) {
|
||||
playerLine += '[' + hhmmss(p.raw.time) + '] ';
|
||||
}
|
||||
playerLine += p.name;
|
||||
if (p.raw?.score !== undefined) {
|
||||
playerLine += ' [score: ' + p.raw.score + ']';
|
||||
}
|
||||
infoText.push(playerLine);
|
||||
}
|
||||
infoText.push('```');
|
||||
}
|
||||
|
||||
m.setText(infoText.join('\n'));
|
||||
m.updatePost(gs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,10 +118,45 @@ class ServerInfoMessage {
|
||||
}
|
||||
}
|
||||
|
||||
async setText(text: string) {
|
||||
console.log('setText', this.host, this.port);
|
||||
async updatePost(gs: GameServer) {
|
||||
let infoText = gs.niceName + ' offline...';
|
||||
|
||||
if (gs.info && gs.online) {
|
||||
const stats = gs.history.stats();
|
||||
let statsText = '';
|
||||
if (stats.length > 0) {
|
||||
const s = stats.slice(-1).pop();
|
||||
if (s) {
|
||||
statsText = ' (hourly avg/max: ' + s.avg.toFixed(1) + '/' + s.max + ') ';
|
||||
}
|
||||
}
|
||||
|
||||
infoText = [
|
||||
gs.niceName,
|
||||
gs.info.game + ' / ' + gs.info.map,
|
||||
gs.info.connect,
|
||||
'Players ' + gs.info.playersNum + '/' + gs.info.playersMax + statsText
|
||||
].join('\n');
|
||||
|
||||
if (gs.info.players.length > 0 && gs.info.players[0].name !== undefined) {
|
||||
const pnArr: string[] = [];
|
||||
for(const p of gs.info.players) {
|
||||
let playerLine = '';
|
||||
if (p.raw?.time !== undefined) {
|
||||
playerLine += hhmmss(p.raw.time) + ' ';
|
||||
}
|
||||
playerLine += p.name;
|
||||
if (p.raw?.score !== undefined) {
|
||||
playerLine += ' (' + p.raw.score + ')';
|
||||
}
|
||||
pnArr.push(playerLine);
|
||||
}
|
||||
infoText += '```\n' + pnArr.join('\n').slice(0, 4088 - infoText.length) + '\n```';
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await bot.api.editMessageText(this.chatId, this.messageId, text, {parse_mode: 'Markdown'});
|
||||
await bot.api.editMessageText(this.chatId, this.messageId, infoText, {parse_mode: 'Markdown'});
|
||||
} catch (e: any) {
|
||||
console.log(e.message || e);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
@@ -25,10 +29,11 @@ const discordBot = __importStar(require("./discord-bot"));
|
||||
const telegramBot = __importStar(require("./telegram-bot"));
|
||||
const fs_1 = require("fs");
|
||||
const { readFile } = fs_1.promises;
|
||||
const REFRESH_TIME_MINUTES = parseInt(process.env.REFRESH_TIME_MINUTES || '1', 10);
|
||||
const REFRESH_TIME_MINUTES = parseInt(process.env.REFRESH_TIME_MINUTES || '5', 10);
|
||||
const DISCORD_BOT_TOKEN = process.env.DISCORD_BOT_TOKEN || '';
|
||||
const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN || '';
|
||||
const GSW_CONFIG = process.env.GSW_CONFIG || './config/default.config.json';
|
||||
const DBG = Boolean(process.env.DBG || false);
|
||||
class Watcher {
|
||||
constructor() {
|
||||
this.servers = [];
|
||||
@@ -47,8 +52,9 @@ class Watcher {
|
||||
this.servers.push(gs);
|
||||
}
|
||||
}
|
||||
check() {
|
||||
console.log('watcher checking...');
|
||||
async check() {
|
||||
if (DBG)
|
||||
console.log('watcher checking...');
|
||||
const promises = [];
|
||||
for (const gs of this.servers) {
|
||||
promises.push(gs.update().then(() => {
|
||||
@@ -60,24 +66,20 @@ class Watcher {
|
||||
}
|
||||
}));
|
||||
}
|
||||
return Promise.allSettled(promises).then(() => (0, game_server_1.saveDb)());
|
||||
await Promise.allSettled(promises);
|
||||
await (0, game_server_1.saveDb)();
|
||||
}
|
||||
}
|
||||
let loop = null;
|
||||
async function main() {
|
||||
console.log('reading config...', GSW_CONFIG);
|
||||
console.log('reading config', GSW_CONFIG);
|
||||
const buffer = await readFile(GSW_CONFIG);
|
||||
console.log('buffer', buffer.toString());
|
||||
try {
|
||||
const conf = JSON.parse(buffer.toString());
|
||||
const watcher = new Watcher();
|
||||
await watcher.init(conf);
|
||||
console.log('starting loop...', REFRESH_TIME_MINUTES);
|
||||
loop = setInterval(async () => { await watcher.check(); }, 1000 * 60 * REFRESH_TIME_MINUTES);
|
||||
await watcher.check();
|
||||
} catch (e) {
|
||||
console.error(e.message || e);
|
||||
}
|
||||
// return watcher;
|
||||
}
|
||||
exports.main = main;
|
||||
|
||||
@@ -5,10 +5,11 @@ import * as telegramBot from './telegram-bot';
|
||||
import { promises as fs } from 'fs';
|
||||
const { readFile } = fs;
|
||||
|
||||
const REFRESH_TIME_MINUTES = parseInt(process.env.REFRESH_TIME_MINUTES || '1', 10);
|
||||
const REFRESH_TIME_MINUTES = parseInt(process.env.REFRESH_TIME_MINUTES || '5', 10);
|
||||
const DISCORD_BOT_TOKEN = process.env.DISCORD_BOT_TOKEN || '';
|
||||
const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN || '';
|
||||
const GSW_CONFIG = process.env.GSW_CONFIG || './config/default.config.json';
|
||||
const DBG = Boolean(process.env.DBG || false);
|
||||
|
||||
export interface WatcherConfig {
|
||||
host: string;
|
||||
@@ -45,8 +46,8 @@ class Watcher {
|
||||
}
|
||||
}
|
||||
|
||||
check() {
|
||||
console.log('watcher checking...');
|
||||
async check() {
|
||||
if (DBG) console.log('watcher checking...');
|
||||
|
||||
const promises: Promise<void>[] = [];
|
||||
for (const gs of this.servers) {
|
||||
@@ -61,13 +62,14 @@ class Watcher {
|
||||
}));
|
||||
}
|
||||
|
||||
return Promise.allSettled(promises).then(() => saveDb());
|
||||
await Promise.allSettled(promises);
|
||||
await saveDb();
|
||||
}
|
||||
}
|
||||
|
||||
let loop = null;
|
||||
export async function main() {
|
||||
console.log('reading config...', GSW_CONFIG);
|
||||
console.log('reading config', GSW_CONFIG);
|
||||
const buffer = await readFile(GSW_CONFIG);
|
||||
const conf = JSON.parse(buffer.toString());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user