The code is a Discord bot written in JavaScript using the Discord.js library, which imports various modules for interacting with the Discord API and performing specific bot functions. The bot defines regular expressions, arrays, and a configuration function that extracts command content and attachments, computes a filename, and creates a new file based on the command's content and attachments.
npm run import -- "discord bot"
var ip6addr = require('ip6addr')
var importer = require('../Core')
var challengeCommand = importer.import("challenge discord command")
var discordApi = importer.import("discord api")
var {
getInfo, sendRcon, nextInfoResponse,
nextPrintResponse
} = importer.import("quake 3 server commands")
var formatQuake3Response = importer.import("format quake 3 response")
var removeCtrlChars = importer.import("remove ctrl characters")
var personality = [
'Yeehaw!',
'Balls to wall!',
'Do it to it!',
'Got it!',
'Let\'s play!',
'Roger that!',
'I read you!',
'Buenos Dias!'
]
var lose = [
'Error. Error.',
'Oops.',
'Boo hoo!',
'Phooey!',
'Au revoir, mon amis.',
'#*&^@#!!',
]
var discordCommands = {
CHALLENGE: /^[!\\\/]?(<@[^:@\s]+>\s*chall?[ae]nge|chall?[ae]nge\s*<@[^:@\s]+>)\s*([^:@\s]*?)\s*([^:@\s]*?)/ig,
CONNECT: /^[!\\\/]?(rcon)?conn?ect\s*([0-9\.a-z-_]+(:[0-9]+)*)$/ig,
RCON: /^[!\\\/]?rcon(pass?wo?rd)?\s+([^"\s]+)\s*(.*)$/ig,
DISCONNECT: /[!\\\/]?disconn?ect/ig,
CONFIG: /^[!\\\/]?(\w*)(\.cfg|config|configure)/ig,
LOAD: /^[!\\\/]?(load|map)\s*(\w*)/ig,
COMMAND: /^[!\\\/]/ig,
HELLO: /^[!\\\/](\w\s*){0,2}hello(\w\s*){0,2}/ig,
UNKNOWN: /.*/ig,
}
async function configCommand(command) {
if(!command.attachments && !command.embed) return
var user = command.author.username
var options = discordCommands.CONFIG.exec(command.content)
var options2 = command.attachments
.map(a => discordCommands.CONFIG.exec(a.filename))
.filter(a => a)[0]
var name = options ? options[1] : options2 ? options2[1] : ''
.replace(options[2], '')
.replace(options2[2], '')
.replace(new RegExp(user, 'ig'), '')
.replace(/[^0-9-_a-z]/ig, '-')
if(name.length === 0) {
await discordApi.createMessage(`Couldn't compute filename.` + '\n```BOT'+command.id+'\nbeep boop\n```\n', command.channel_id)
return
}
var file = 'player-' + user + '-' + name + '.cfg'
await discordApi.triggerTyping(command.channel_id)
// TODO: remote post
//await remoteGet(command.attachments[0].url, file, '/home/freonjs/baseq3-cc/conf/')
await discordApi.createMessage(`exec conf/player-${user}-${name}` + '\n```BOT'+command.id+'\nbeep boop\n```\n', command.channel_id)
}
var userLogins = {}
// username: {address, password, lastUsed, }
async function connectCommand(command) {
// TODO: record last address and password given
var user = command.author.username
var options = discordCommands.CONNECT.exec(command.content)
if(typeof userLogins[user] == 'undefined')
userLogins[user] = {}
userLogins[user] = {
address: options[2] || userLogins[user].address || 'quakeIIIarena.com',
password: userLogins[user].password || 'password123!'
}
// TODO: try to connect to server and respond with a getinfo print out
await discordApi.triggerTyping(command.channel_id)
var match = (/^(.*?):*([0-9]+)*$/ig).exec(userLogins[user].address)
await getInfo(match[1], parseInt(match[2]) || 27960)
var info = await nextInfoResponse()
var filteredKeys = Object.keys(info)
.filter(k => k != 'challenge'
&& k != 'hostname'
&& k != 'sv_hostname'
&& k != 'mapname'
&& k != 'clients'
&& k != 'g_humanplayers'
&& k != 'sv_maxclients'
&& k != 'ip'
&& k != 'port')
.map(k => removeCtrlChars(k))
var filteredValues = filteredKeys
.map(k => removeCtrlChars(info[k]))
var json = {
content: '\n```BOT'+command.id+'\nbeep boop\n```\n',
embeds: [{
title: removeCtrlChars(info.sv_hostname || info.hostname || info.gamename || info.game || ''),
description: info.ip + ':' + info.port,
color: 0xdda60f,
fields: [
{
name: 'Map',
value: info.mapname,
inline: false
},
{
name: 'Players',
value: info.clients + ' (' + (info.g_humanplayers || '?') + ' humans)' + '/' + info.sv_maxclients,
inline: false
},
{
name: 'Key',
value: '```http\n' + filteredKeys.join('\n') + '```',
inline: true
},
{
name: 'Value',
value: '```yaml\n' + filteredValues.join('\n') + '```',
inline: true
}
]
}]
}
if(command.interaction)
await discordApi.updateInteraction(json, command.id, command.token)
else
await discordApi.createMessage(json, command.channel_id)
}
async function rconCommand(command) {
var user = command.author.username
var options = discordCommands.RCON.exec(command.content)
if(typeof userLogins[user] == 'undefined')
userLogins[user] = {}
userLogins[user] = {
address: userLogins[user].address || 'quakeIIIarena.com',
password: options[2] || userLogins[user].password || 'password123!'
}
await discordApi.triggerTyping(command.channel_id)
var match = (/^(.*?):*([0-9]+)*$/ig).exec(userLogins[user].address)
await sendRcon(match[1], parseInt(match[2]) || 27960,
options[3] && options[3].length > 0
? options[3]
: 'cmdlist',
userLogins[user].password)
var response = await nextPrintResponse()
response = formatQuake3Response(response.content, command, response)
if(typeof response == 'string')
response += '\n```BOT'+command.id+'\nbeep boop\n```\n'
else if(typeof response == 'object')
response.content = '\n```BOT'+command.id+'\nbeep boop\n```\n'
if(command.interaction)
await discordApi.updateInteraction(response, command.id, command.token)
else
await discordApi.createMessage(response, command.channel_id)
}
async function chatCommand(command) {
if(command.interaction)
await discordApi.updateInteraction(`Hello.` + '\n```BOT'+command.id+'\nbeep boop\n```\n', command.id, command.token)
else
await discordApi.createMessage(`Hello.` + '\n```BOT'+command.id+'\nbeep boop\n```\n', command.channel_id)
return
}
module.exports = {
discordCommands,
challengeCommand,
configCommand,
connectCommand,
rconCommand,
chatCommand,
}
# Discord Bot Configuration
=====================================
This module exports various functions to handle Discord bot commands.
### Configuration
```javascript
const ip6addr = require('ip6addr');
const importer = require('../Core');
const discordApi = importer.import('discord api');
const {
getInfo, sendRcon, nextInfoResponse, nextPrintResponse
} = importer.import('quake 3 server commands');
const formatQuake3Response = importer.import('format quake 3 response');
const removeCtrlChars = importer.import('remove ctrl characters');
const personality = [
'Yeehaw!',
'Balls to wall!',
'Do it to it!',
'Got it!',
'Let\'s play!',
'Roger that!',
'I read you!',
'Buenos Dias!'
];
const lose = [
'Error. Error.',
'Oops.',
'Boo hoo!',
'Phooey!',
'Au revoir, mon amis.',
'#*&^@#!!',
];
const discordCommands = {
CHALLENGE: /^[!\\\/]?(<@[^:@\s]+>\s*chall?[ae]nge|chall?[ae]nge\s*<@[^:@\s]+>)\s*([^:@\s]*?)\s*([^:@\s]*?)/ig,
CONNECT: /^[!\\\/]?(rcon)?conn?ect\s*([0-9\.a-z-_]+(:[0-9]+)*)$/ig,
RCON: /^[!\\\/]?rcon(pass?wo?rd)?\s+([^"\s]+)\s*(.*)$/ig,
DISCONNECT: /[!\\\/]?disconn?ect/ig,
CONFIG: /^[!\\\/]?(\w*)(\.cfg|config|configure)/ig,
LOAD: /^[!\\\/]?(load|map)\s*(\w*)/ig,
COMMAND: /^[!\\\/]/ig,
HELLO: /^[!\\\/](\w\s*){0,2}hello(\w\s*){0,2}/ig,
UNKNOWN: /.*/ig,
};
const userLogins = {};
async function configCommand(command) {
if (!command.attachments &&!command.embed) return;
const user = command.author.username;
const options = discordCommands.CONFIG.exec(command.content);
const filename = options? options[1].replace(options[2], '') : '';
// Format filename based on username
const formattedFilename = filename.replace(new RegExp(user, 'ig'), '').replace(/[^0-9-_a-z]/ig, '-');
if (formattedFilename.length === 0) {
await discordApi.createMessage(`Couldn't compute filename.`, command.channel_id);
return;
}
// Create configuration file path
const configFile = `player-${user}-${formattedFilename}.cfg`;
// Trigger typing and post message
await discordApi.triggerTyping(command.channel_id);
await discordApi.createMessage(`exec conf/${configFile}`, command.channel_id);
}
async function connectCommand(command) {
const user = command.author.username;
const options = discordCommands.CONNECT.exec(command.content);
// Store user login information
if (!userLogins[user]) {
userLogins[user] = { address: 'quakeIIIarena.com', password: 'password123!' };
}
userLogins[user].address = options[2] || userLogins[user].address;
userLogins[user].password = userLogins[user].password || 'password123!';
// Get Quake 3 server info
const address = userLogins[user].address;
const port = parseInt(/^(.*?):*([0-9]+)*$/ig.exec(address)[2]) || 27960;
await getInfo(address, port);
const info = await nextInfoResponse();
// Filter server info and format response
const filteredKeys = Object.keys(info).filter(k => k!== 'challenge'
&& k!== 'hostname'
&& k!=='sv_hostname'
&& k!=='mapname'
&& k!== 'clients'
&& k!== 'g_humanplayers'
&& k!=='sv_maxclients'
&& k!== 'ip'
&& k!== 'port');
const filteredValues = filteredKeys.map(k => removeCtrlChars(info[k]));
// Create response object
const json = {
embeds: [{
title: removeCtrlChars(info.sv_hostname || info.hostname || info.gamename || info.game || ''),
description: info.ip + ':' + info.port,
color: 0xdda60f,
fields: [
{
name: 'Map',
value: info.mapname,
inline: false
},
{
name: 'Players',
value: info.clients +'(' + (info.g_humanplayers || '?') +'humans)' + '/' + info.sv_maxclients,
inline: false
},
{
name: 'Key',
value: '```http\n' + filteredKeys.join('\n') + '```',
inline: true
},
{
name: 'Value',
value: '```yaml\n' + filteredValues.join('\n') + '```',
inline: true
}
]
}]
};
// Update interaction or create message
if (command.interaction) {
await discordApi.updateInteraction(json, command.id, command.token);
} else {
await discordApi.createMessage(json, command.channel_id);
}
}
async function rconCommand(command) {
const user = command.author.username;
const options = discordCommands.RCON.exec(command.content);
// Store user login information
if (!userLogins[user]) {
userLogins[user] = { address: 'quakeIIIarena.com', password: 'password123!' };
}
userLogins[user].address = userLogins[user].address || 'quakeIIIarena.com';
userLogins[user].password = options[2] || userLogins[user].password || 'password123!';
// Send RCON command and get response
const address = userLogins[user].address;
const port = parseInt(/^(.*?):*([0-9]+)*$/ig.exec(address)[2]) || 27960;
await sendRcon(address, port, options[3] || 'cmdlist', userLogins[user].password);
const response = await nextPrintResponse();
// Format response
const formattedResponse = formatQuake3Response(response.content, command, response);
// Update interaction or create message
if (command.interaction) {
await discordApi.updateInteraction(formattedResponse, command.id, command.token);
} else {
await discordApi.createMessage(formattedResponse, command.channel_id);
}
}
async function chatCommand(command) {
const response = `Hello.` + '\n```BOT'+command.id+'\nbeep boop\n```\n';
// Update interaction or create message
if (command.interaction) {
await discordApi.updateInteraction(response, command.id, command.token);
} else {
await discordApi.createMessage(response, command.channel_id);
}
}
module.exports = {
discordCommands,
connectCommand,
rconCommand,
configCommand,
chatCommand
};
Code Breakdown
The code is written in JavaScript and appears to be a Discord bot using the Discord.js library. It consists of the following sections:
The code imports various modules using the require
function:
ip6addr
: a module for working with IPv6 addressesimporter
: a module that imports other modulesdiscordApi
: a module for interacting with the Discord APIchallengeCommand
, getInfo
, sendRcon
, nextInfoResponse
, nextPrintResponse
, formatQuake3Response
, and removeCtrlChars
: modules that provide specific functionality for the botThe code defines several regular expressions for matching Discord commands:
discordCommands
: an object containing regular expressions for each command (e.g. CHALLENGE
, CONNECT
, RCON
, etc.)The code defines two arrays:
personality
: an array of phrases that the bot can respond withlose
: an array of phrases that the bot can respond with when it's unable to perform an actionThe code defines a single function:
configCommand(command)
: an asynchronous function that takes a Discord command as input and performs the following actions:
The function uses several variables and functions imported from other modules, including discordApi
, getInfo
, and nextInfoResponse
.