The code is a Discord bot that responds to challenge commands, which trigger the launch of a Quake III Arena server with customized settings based on environment variables and command input. The bot uses regular expressions to parse challenge commands, connects to a Quake III Arena server via the serverApi
module, and handles errors by displaying a message when no servers are available.
npm run import -- "challenge discord command"
var importer = require('../Core')
var discordApi = importer.import("discord api")
var serverApi = importer.import("quake 3 server commands")
var CHALLENGE = /(@[^:@\s]+\s*chall?[ae]nge|chall?[ae]nge\s*@[^:@\s]+)\s*([^:@\s]*?)\s*([^:@\s]*?)/ig
var DEFAULT_HOST = process.env.DEFAULT_HOST || 'http://quakeiiiarena.com/play/'
var MODS = typeof process.env.DEFAULT_MODS == 'string'
? JSON.parse(process.env.DEFAULT_MODS)
: [
'baseq3',
'freon'
]
async function challengeCommand(command) {
if(!command.private && (!command.mentions || command.mentions.length === 0))
return
var options = CHALLENGE.exec(command.content)
var launch = (options ? options[2] : '') || ''
var map = (options ? options[3] : '') || ''
var message = 'I read you'
var instruction = ''
if(!MODS.includes(launch) && map.length === 0) {
map = launch
launch = ''
}
if(map.length === 0) {
map = 'q3dm17'
}
if(launch.length == 0) {
instruction += ', assuming baseq3 on map ' + map
} else if(command.launching) {
instruction += ' ' + launch + ' on map ' + map
}
if(!command.launching && !command.content.match(/:thumbsup:/ig)) {
message = 'Waiting for reaction'
instruction += ', react with :thumbsup: to launch'
}
if(command.launching) {
message = 'Launching'
await discordApi.createMessage(message + instruction + '\n```BOT'+command.id+'L\nbeep boop\n```\n', command.channel_id)
await discordApi.triggerTyping(command.channel_id)
var masters = await serverApi.listMasters(void 0, void 0, false)
if(masters.length === 0) {
await discordApi.createMessage(`Boo hoo, no servers available. :cry:`
+ '\n```BOT'+command.id+'L\nbeep boop\n```\n', command.channel_id)
return
}
await serverApi.sendRcon(masters[0].ip, masters[0].port, '\exec ' + launch + '.cfg')
await serverApi.sendRcon(masters[0].ip, masters[0].port, '\map ' + map)
await new Promise(resolve => setTimeout(resolve, 1000))
await discordApi.createMessage(`Match is ready ${DEFAULT_HOST}?connect%20${masters[0].ip}:${masters[0].port} (${masters[0].ip}:${masters[0].port})`
+ '\n```BOT'+command.id+'L\nbeep boop\n```\n', command.channel_id)
} else if (instruction.length > 0) {
await discordApi.createMessage(message + instruction + '\n```BOT'+command.id+'\nbeep boop\n```\n', command.channel_id)
}
}
module.exports = challengeCommand
const { DiscordAPI } = require('../Core');
const { serverCommands, api } = new DiscordAPI();
const { Quake3Server } = require('../Quake3Server');
const CHALLENGE_REGEX = /(@[^:@\s]+\s*chall?[ae]nge|chall?[ae]nge\s*@[^:@\s]+)\s*([^:@\s]*?)\s*([^:@\s]*?)/ig;
const DEFAULT_HOST = process.env.DEFAULT_HOST || 'http://quakeiiiarena.com/play/';
const DEFAULT_MODS = process.env.DEFAULT_MODS || ['baseq3', 'freon'];
const MAP_DEFAULT = 'q3dm17';
class ChallengeCommand {
async execute(command) {
if (!command.private && (!command.mentions || command.mentions.length === 0)) return;
const { content } = command;
const match = CHALLENGE_REGEX.exec(content);
if (!match) return;
const [,, launch, map] = match;
const { message, instruction } = this.parseLaunch(launch, map);
if (!command.launching) {
await this.createMessage(command.channel_id, message, instruction);
return;
}
await this.launchMap(command.channel_id, launch, map);
}
parseLaunch(launch, map) {
if (!DEFAULT_MODS.includes(launch) && map.length === 0) {
map = launch;
launch = '';
}
if (map.length === 0) map = MAP_DEFAULT;
let message = 'I read you';
let instruction = '';
if (launch.length === 0) {
instruction += `, assuming ${DEFAULT_MODS[0]} on map ${map}`;
} else {
instruction += ` ${launch} on map ${map}`;
}
return { message, instruction };
}
async createMessage(channelId, message, instruction) {
const instructionText = instruction? `, react with :thumbsup: to launch` : '';
await api.createMessage(message + instruction + '\n```BOT' + channelId + 'L\nbeep boop\n```\n', channelId);
if (instructionText) await api.createMessage(`Waiting for reaction${instructionText} \n```BOT' + channelId + 'L\nbeep boop\n````, channelId);
}
async launchMap(channelId, launch, map) {
this.triggerTyping(channelId);
const masters = await Quake3Server.listMasters();
if (masters.length === 0) {
await api.createMessage(`Boo hoo, no servers available. :cry:` + '\n```BOT' + channelId + 'L\nbeep boop\n```\n', channelId);
return;
}
const master = masters[0];
await Quake3Server.sendRcon(master.ip, master.port, `\\exec ${launch}.cfg`);
await Quake3Server.sendRcon(master.ip, master.port, `\\map ${map}`);
await new Promise(resolve => setTimeout(resolve, 1000));
await api.createMessage(`Match is ready ${DEFAULT_HOST}?connect%20${master.ip}:${master.port} (${master.ip}:${master.port})\n```BOT' + channelId + 'L\nbeep boop\n```\n`, channelId);
}
triggerTyping(channelId) {
api.triggerTyping(channelId);
}
}
module.exports = new ChallengeCommand();
The code starts by importing two modules using the require
function:
var importer = require('../Core')
var discordApi = importer.import('discord api')
var serverApi = importer.import('quake 3 server commands')
These modules are likely custom modules for interacting with the Discord API and a Quake III Arena server.
The code defines a regular expression CHALLENGE
to match challenge commands:
var CHALLENGE = /(@[^:@\s]+\s*chall?[ae]nge|chall?[ae]nge\s*@[^:@\s]+)\s*([^:@\s]*?)\s*([^:@\s]*?)/ig
This regular expression matches commands in the format @username chall[ae]nge [launch] [map]
, where launch
and map
are optional.
The code retrieves environment variables for the default host and mods:
var DEFAULT_HOST = process.env.DEFAULT_HOST || 'http://quakeiiiarena.com/play/'
var MODS = typeof process.env.DEFAULT_MODS =='string'
? JSON.parse(process.env.DEFAULT_MODS)
: [
'baseq3',
'freon'
]
These variables are used to determine the default host and mods for the Quake III Arena server.
The code defines an asynchronous function challengeCommand
to handle challenge commands:
async function challengeCommand(command) {
//...
}
This function takes a command
object as an argument and uses the CHALLENGE
regular expression to parse the command content. It then determines the launch and map values based on the command content and environment variables.
The code uses the serverApi
module to connect to a Quake III Arena server and send rcon commands to launch the game:
if(command.launching) {
//...
await serverApi.sendRcon(masters[0].ip, masters[0].port, '\exec'+ launch + '.cfg')
await serverApi.sendRcon(masters[0].ip, masters[0].port, '\map'+ map)
}
This code assumes that the serverApi
module provides a way to connect to a Quake III Arena server and send rcon commands to launch the game.
The code includes error handling to display a message when no servers are available:
if(masters.length === 0) {
await discordApi.createMessage(`Boo hoo, no servers available. :cry:`
+ '\n```BOT'+command.id+'L\nbeep boop\n```\n', command.channel_id)
return
}
This code displays a message when no servers are available and returns from the function.