The code is a Quake 3 server management tool that queries server status, captures all stats, and logs chat messages. It uses various modules and functions to interact with the server, including the gamedig
module for querying server status and the sendRcon
function for sending RCON commands to the server.
npm run import -- "quake3 server status"
var importer = require('../Core')
var gamedig = require('gamedig')
var serverApi = importer.import("quake 3 server commands")
var { sendRcon, nextAllResponses, udpClient } = importer.import("quake 3 server commands")
var discordApi = importer.import("discord api")
var {authorizeGateway} = importer.import("authorize discord")
var {parseConfigStr} = importer.import("quake 3 server responses")
var removeCtrlChars = importer.import("remove ctrl characters")
async function getStatus(ip, port) {
return gamedig.query({
type: 'quake3',
host: ip,
port: port
}).then((state) => {
return state
}).catch((error) => {
console.log('Server is offline', error)
})
}
async function captureAllStats() {
var masters = await serverApi.listMasters('master.ioquake3.org', void 0, false)
//var status = await getStatus(masters[1].ip, masters[1].port)
var status = await getStatus('45.32.237.139', 27960)
console.log(status.bots)
}
//typedef enum {
var SV_EVENT = {
MAPCHANGE: 0,
CLIENTSAY: 1,
MATCHEND: 2,
CALLADMIN: 3,
CLIENTDIED: 4,
CLIENTWEAPON: 5,
CLIENTRESPAWN: 6,
CLIENTAWARD: 7,
GETSTATUS: 8,
SERVERINFO: 9,
CONNECTED: 10,
DISCONNECT: 11,
}
//} recentEvent_t;
async function getChats(channelId) {
var match = (/^(.*?):*([0-9]+)*$/ig).exec()
await sendRcon('127.0.0.1', 27960, '', 'recentPassword')
var response = await nextAllResponses()
if(!response) return
var maxTime = 0
var parsed = response.map(function (r) {
return JSON.parse(r.content)
})
var chats = parsed.filter(function (r) {
if(r.timestamp > maxTime)
maxTime = r.timestamp
return r.type == SV_EVENT.CLIENTSAY
})
var call = parsed.filter(function (r) {
return r.type == SV_EVENT.CALLADMIN
})
var status = parsed.filter(function (r) {
return r.type == SV_EVENT.GETSTATUS
})
var server = {}
if(status.length) {
Object.assign(server, parseConfigStr(status[0].value))
}
var info = parsed.filter(function (r) {
return r.type == SV_EVENT.SERVERINFO
})
if(info.length) {
Object.assign(server, parseConfigStr(info[0].value))
}
var match = parsed.filter(function (r) {
return r.type == SV_EVENT.MATCHEND
})
if(match.length) {
// TODO: save to SQL database
console.log(match[match.length-1])
}
var discordSocket = await authorizeGateway()
//console.log(await discordApi.getGuildRoles('752561748611039362'))
if(call.length) {
await discordApi.triggerTyping(channelId)
}
for(var i = 0; i < call.length; i++) {
try {
//console.log('Say: ' + call[i].value)
await discordApi.createMessage({
embed: {
title: removeCtrlChars(server.hostname || server.sv_hostname || server.gamename),
description: server.ip + ':' + server.port,
color: 0xdda60f,
fields: [
{
name: call[i].value,
value: `<@&752605581029802155> [Connect](https://quake.games/?connect%20${'127.0.0.1:27960'})`,
inline: false
},
]
},
allowed_mentions: {
parse: ['users', 'roles'],
users: [],
roles: []
}
}, channelId)
//await discordApi.createMessage(`@admin ${call[i].value}`, channelId)
} catch (e) {
console.log(e)
}
}
}
module.exports = getChats
// Import required modules
const importer = require('../Core');
const { join } = require('path');
const gamedig = require('gamedig');
const serverApi = importer.import('quake 3 server commands');
const {
sendRcon,
nextAllResponses,
udpClient,
} = serverApi;
const discordApi = importer.import('discord api');
const { authorizeGateway } = importer.import('authorize discord');
const { parseConfigStr } = importer.import('quake 3 server responses');
const removeCtrlChars = importer.import('remove ctrl characters');
// Define constants
const SV_EVENT = Object.freeze({
MAPCHANGE: 0,
CLIENTSAY: 1,
MATCHEND: 2,
CALLADMIN: 3,
CLIENTDIED: 4,
CLIENTWEAPON: 5,
CLIENTRESPAWN: 6,
CLIENTAWARD: 7,
GETSTATUS: 8,
SERVERINFO: 9,
CONNECTED: 10,
DISCONNECT: 11,
});
// Define type for recent event
type RecentEvent = 'MAPCHANGE' | 'CLIENTSAY' | 'MATCHEND' | 'CALLADMIN' | 'CLIENTDIED' | 'CLIENTWEAPON' | 'CLIENTRESPAWN' | 'CLIENTAWARD' | 'GETSTATUS' | 'SERVERINFO' | 'CONNECTED' | 'DISCONNECT';
// Define a function to get server status
async function getStatus(ip: string, port: number) {
try {
const state = await gamedig.query({
type: 'quake3',
host: ip,
port: port,
});
return state;
} catch (error) {
console.log('Server is offline:', error);
return null;
}
}
// Define a function to capture all stats
async function captureAllStats() {
const masters = await serverApi.listMasters('master.ioquake3.org', void 0, false);
const status = await getStatus('45.32.237.139', 27960);
console.log(status?.bots);
}
// Define a function to get chats
async function getChats(channelId: string) {
// Send RCON command to get recent events
await sendRcon('127.0.0.1', 27960, '','recentPassword');
// Get all responses
const responses = await nextAllResponses();
// Check if any responses were received
if (!responses) return;
// Parse responses into JSON
const parsedResponses = responses.map((r) => JSON.parse(r.content));
// Get events
const events = parsedResponses.filter((r) => r.type ==='recentEvent');
// Get specific events
const chats = events.filter((r) => r.event.type === SV_EVENT.CLIENTSAY);
const calls = events.filter((r) => r.event.type === SV_EVENT.CALLADMIN);
const status = events.filter((r) => r.event.type === SV_EVENT.GETSTATUS);
const server = {};
if (status.length) {
Object.assign(server, parseConfigStr(status[0].value));
}
// Get server info
const info = parsedResponses.filter((r) => r.type === SV_EVENT.SERVERINFO);
if (info.length) {
Object.assign(server, parseConfigStr(info[0].value));
}
// Get match end events
const matches = parsedResponses.filter((r) => r.type === SV_EVENT.MATCHEND);
if (matches.length) {
// TODO: save to SQL database
console.log(matches[matches.length - 1]);
}
// Get Discord socket
const discordSocket = await authorizeGateway();
// Trigger typing
if (calls.length) {
await discordApi.triggerTyping(channelId);
}
// Create messages
for (const call of calls) {
try {
await discordApi.createMessage({
embed: {
title: removeCtrlChars(server.hostname || server.sv_hostname || server.gamename),
description: server.ip + ':' + server.port,
color: 0xdda60f,
fields: [
{
name: call.value,
value: `<@&752605581029802155> [Connect](https://quake.games/?connect%20${'127.0.0.1:27960'})`,
inline: false,
},
],
},
allowed_mentions: {
parse: ['users', 'roles'],
users: [],
roles: [],
},
}, channelId);
} catch (e) {
console.log(e);
}
}
}
module.exports = getChats;
Code Breakdown: Quake 3 Server Status and Chat Capture
The code imports various modules and functions from other files using the require
function:
var importer = require('../Core')
var gamedig = require('gamedig')
var serverApi = importer.import('quake 3 server commands')
var { sendRcon, nextAllResponses, udpClient } = importer.import('quake 3 server commands')
var discordApi = importer.import('discord api')
var {authorizeGateway} = importer.import('authorize discord')
var {parseConfigStr} = importer.import('quake 3 server responses')
var removeCtrlChars = importer.import('remove ctrl characters')
The getStatus
function uses the gamedig
module to query the Quake 3 server status:
async function getStatus(ip, port) {
return gamedig.query({
type: 'quake3',
host: ip,
port: port
}).then((state) => {
return state
}).catch((error) => {
console.log('Server is offline', error)
})
}
The captureAllStats
function uses the getStatus
function to retrieve the server status and logs the bots array:
async function captureAllStats() {
var masters = await serverApi.listMasters('master.ioquake3.org', void 0, false)
var status = await getStatus('45.32.237.139', 27960)
console.log(status.bots)
}
The code defines an enum type SV_EVENT
with various event types:
var SV_EVENT = {
MAPCHANGE: 0,
CLIENTSAY: 1,
MATCHEND: 2,
CALLADMIN: 3,
CLIENTDIED: 4,
CLIENTWEAPON: 5,
CLIENTRESPAWN: 6,
CLIENTAWARD: 7,
GETSTATUS: 8,
SERVERINFO: 9,
CONNECTED: 10,
DISCONNECT: 11,
}
The getChats
function uses the sendRcon
function to send a password to the server, retrieves the response using nextAllResponses
, and parses the chat messages:
async function getChats(channelId) {
//...
}
However, the implementation of the getChats
function is incomplete and contains some errors (e.g., parseConfi
should be parseConfigStr
).