Compare commits
8 Commits
50c297bfea
...
alt
| Author | SHA1 | Date | |
|---|---|---|---|
| 4ad4f69f9f | |||
| dc0cc6685d | |||
| c04cd2ef12 | |||
| 763892443d | |||
| 9646d9f941 | |||
| fe866050d5 | |||
| 00d04c787d | |||
| f0283145dc |
@@ -1,6 +1,8 @@
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
|
||||
# 1800queue
|
||||
# 1800queue alt
|
||||
|
||||
This version does not run continuously, and requires each tourney to be started with a command
|
||||
|
||||
## Prerequirements
|
||||
|
||||
|
||||
34
dist/api.js
vendored
34
dist/api.js
vendored
@@ -24,7 +24,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getPlayerInteraction = exports.getPlayer = void 0;
|
||||
const cheerio = __importStar(require("cheerio"));
|
||||
const discord_js_1 = require("discord.js");
|
||||
const https_1 = __importDefault(require("https"));
|
||||
const util_1 = require("./util");
|
||||
const uniteApiRegex = {
|
||||
//$1 = name, $2 = id
|
||||
ogtitle: /unite api - (.+) \((.*)\)/i,
|
||||
@@ -68,11 +70,14 @@ function getHTML(name) {
|
||||
* interprets the html from getHTML()
|
||||
* @param name name of player
|
||||
* @returns player data from site
|
||||
* @throws errorMessage class if the request fails
|
||||
*/
|
||||
function readHTML(html) {
|
||||
let metaElems = cheerio.load(html)('meta').toArray(), foundData = {
|
||||
let $ = cheerio.load(html);
|
||||
let metaElems = $('meta').toArray(), foundData = {
|
||||
name: "",
|
||||
id: "",
|
||||
avatar: "",
|
||||
level: "",
|
||||
rank: "",
|
||||
elo: null,
|
||||
@@ -102,7 +107,7 @@ function readHTML(html) {
|
||||
extraLines.push(line);
|
||||
}
|
||||
if (!lines.length)
|
||||
throw 'Unable to read data, please try again';
|
||||
throw (0, util_1.emsg)('Unable to read data, please try again');
|
||||
//bring the first lines removed back into the data
|
||||
lines = [
|
||||
...lines,
|
||||
@@ -116,7 +121,7 @@ function readHTML(html) {
|
||||
if (regex[0].test(line)) { //is master/has elo
|
||||
let regexData = line.match(regex[0]);
|
||||
if (!regexData || regexData.length < 4)
|
||||
throw 'Unable to read data, please try again';
|
||||
throw (0, util_1.emsg)('Unable to read data, please try again');
|
||||
foundData.level = regexData[1];
|
||||
foundData.rank = regexData[2];
|
||||
foundData.elo = regexData[3];
|
||||
@@ -124,7 +129,7 @@ function readHTML(html) {
|
||||
else { //is not master/has a class
|
||||
let regexData = line.match(regex[1]);
|
||||
if (!regexData || regexData.length < 4)
|
||||
throw 'Unable to read data, please try again';
|
||||
throw (0, util_1.emsg)('Unable to read data, please try again');
|
||||
foundData.level = regexData[1];
|
||||
foundData.rank = regexData[2];
|
||||
foundData.class = regexData[3];
|
||||
@@ -148,6 +153,8 @@ function readHTML(html) {
|
||||
});
|
||||
}
|
||||
});
|
||||
foundData.avatar = $('.player-card-image img').attr('src') || "";
|
||||
foundData.avatar = foundData.avatar.replace('../', 'https://uniteapi.dev/');
|
||||
return foundData;
|
||||
}
|
||||
/**
|
||||
@@ -173,17 +180,32 @@ async function getPlayer(name) {
|
||||
return null;
|
||||
}
|
||||
exports.getPlayer = getPlayer;
|
||||
async function sendPlayerEmbed(interaction, data) {
|
||||
let embed = new discord_js_1.MessageEmbed()
|
||||
.setTitle(`${data.name} (${data.id})`)
|
||||
.setURL(`https://uniteapi.dev/p/${encodeURIComponent(data.name)}`)
|
||||
.setTimestamp()
|
||||
.setThumbnail(data.avatar)
|
||||
.setDescription(`Level ${data.level}
|
||||
${data.rank} ${data.elo ? `(${data.elo})` : `Class ${data.class}`}
|
||||
|
||||
**Battles** ${data.battles}
|
||||
**Wins** ${data.wins}
|
||||
**Win Rate** ${data.winrate}`);
|
||||
await interaction.editReply({ embeds: [embed] });
|
||||
}
|
||||
/**
|
||||
* calls getPlayer() with the name from the interaction
|
||||
* @param interaction discord interaction
|
||||
* @throws errorMessage class if the user cannot be found
|
||||
*/
|
||||
async function getPlayerInteraction(interaction) {
|
||||
let username = interaction.options.getString('username', true);
|
||||
await interaction.deferReply();
|
||||
let data = await getPlayer(username);
|
||||
if (data === null)
|
||||
await interaction.editReply('Unable to find user');
|
||||
throw (0, util_1.emsg)('Unable to find user');
|
||||
else
|
||||
await interaction.editReply('```\n' + JSON.stringify(data, null, 2) + '\n```');
|
||||
sendPlayerEmbed(interaction, data);
|
||||
}
|
||||
exports.getPlayerInteraction = getPlayerInteraction;
|
||||
|
||||
13
dist/discord.js
vendored
13
dist/discord.js
vendored
@@ -13,7 +13,8 @@ const commands = [
|
||||
type: 4,
|
||||
name: 'teamsize',
|
||||
description: 'size of each team',
|
||||
required: true
|
||||
required: true,
|
||||
min_value: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -30,17 +31,17 @@ const commands = [
|
||||
description: 'ready the queue and display team info'
|
||||
},
|
||||
{
|
||||
name: 'queueinfo',
|
||||
description: 'get info of the current queue'
|
||||
name: 'cancel',
|
||||
description: 'cancels the current queue (must have the Manage Messages permission)'
|
||||
},
|
||||
{
|
||||
name: 'elo',
|
||||
description: 'display elo information',
|
||||
name: 'player',
|
||||
description: 'display player information',
|
||||
options: [
|
||||
{
|
||||
type: 3,
|
||||
name: 'username',
|
||||
description: 'in game name',
|
||||
description: 'in game name or UniteApi short link',
|
||||
required: true
|
||||
}
|
||||
]
|
||||
|
||||
16
dist/index.js
vendored
16
dist/index.js
vendored
@@ -24,6 +24,7 @@ const fs = __importStar(require("fs"));
|
||||
const api_1 = require("./api");
|
||||
const discord_1 = require("./discord");
|
||||
const queue_1 = require("./queue");
|
||||
const util_1 = require("./util");
|
||||
const CLIENT = new discord_js_1.Client({ intents: [discord_js_1.Intents.FLAGS.GUILDS] });
|
||||
//init logs with a timestamp
|
||||
console.log(new Date().toISOString() + '\n\n');
|
||||
@@ -51,17 +52,20 @@ CLIENT.on('interactionCreate', async (interaction) => {
|
||||
await (0, queue_1.leaveQueue)(interaction);
|
||||
else if (interaction.commandName === 'ready')
|
||||
await (0, queue_1.readyQueue)(interaction);
|
||||
else if (interaction.commandName === 'queueinfo')
|
||||
await (0, queue_1.queueInfo)(interaction);
|
||||
else if (interaction.commandName === 'elo')
|
||||
else if (interaction.commandName === 'cancel')
|
||||
await (0, queue_1.cancelQueue)(interaction);
|
||||
else if (interaction.commandName === 'player')
|
||||
await (0, api_1.getPlayerInteraction)(interaction);
|
||||
}
|
||||
catch (e) {
|
||||
if (typeof e === 'string') {
|
||||
if (e instanceof util_1.errorMessage) {
|
||||
if (interaction.deferred || interaction.replied)
|
||||
interaction.editReply(e);
|
||||
interaction.editReply(e.msg);
|
||||
else
|
||||
interaction.reply(e);
|
||||
interaction.reply({
|
||||
content: e.msg,
|
||||
ephemeral: e.ephemeral
|
||||
});
|
||||
}
|
||||
else
|
||||
console.error(e);
|
||||
|
||||
165
dist/queue.js
vendored
165
dist/queue.js
vendored
@@ -1,132 +1,189 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.queueInfo = exports.readyQueue = exports.leaveQueue = exports.joinQueue = exports.createQueue = exports.queueContains = void 0;
|
||||
exports.cancelQueue = exports.readyQueue = exports.leaveQueue = exports.joinQueue = exports.createQueue = exports.queueContains = exports.getAll = exports.getInfo = void 0;
|
||||
const discord_js_1 = require("discord.js");
|
||||
const util_1 = require("./util");
|
||||
//maps ChannelID to QueueInfo
|
||||
const QUEUE = new Map();
|
||||
/**
|
||||
* get the GuildMember of an interaction
|
||||
* @param interaction
|
||||
* @throws string message if it cannot be read
|
||||
* @returns member
|
||||
*/
|
||||
function getMember(interaction) {
|
||||
let member = interaction.member;
|
||||
if (!(member instanceof discord_js_1.GuildMember))
|
||||
throw 'Unable to retrieve guild member information, please try again';
|
||||
return member;
|
||||
}
|
||||
/**
|
||||
* get the queueInfo of an interaction
|
||||
* @param interaction
|
||||
* @throws string message if it does not exist
|
||||
* @throws errorMessage class if it does not exist
|
||||
* @returns queue info
|
||||
*/
|
||||
function getInfo(interaction) {
|
||||
let info = QUEUE.get(interaction.channelId);
|
||||
if (!info)
|
||||
throw 'There is not an active queue in this channel, type `/queue` to create one';
|
||||
throw (0, util_1.emsg)('There is not an active queue in this channel, type `/queue` to create one');
|
||||
return info;
|
||||
}
|
||||
exports.getInfo = getInfo;
|
||||
/**
|
||||
* compiles all the get functions above
|
||||
* @param interaction
|
||||
* @throws if another get function throws
|
||||
* @returns object containing each
|
||||
*/
|
||||
const getAll = (interaction) => ({
|
||||
member: (0, util_1.getMember)(interaction),
|
||||
channel: (0, util_1.getChannel)(interaction),
|
||||
info: getInfo(interaction)
|
||||
});
|
||||
exports.getAll = getAll;
|
||||
/**
|
||||
* checks if the interaction data is already in the queue
|
||||
* @param interaction
|
||||
* @returns boolean
|
||||
*/
|
||||
function queueContains(interaction) {
|
||||
let member = getMember(interaction), info = getInfo(interaction);
|
||||
let { member, info } = (0, exports.getAll)(interaction);
|
||||
if (info.players.map(m => m.id).includes(member.id))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
exports.queueContains = queueContains;
|
||||
/**
|
||||
* creates the timeout for the queue
|
||||
* @param interaction
|
||||
* @returns time timeout identifier
|
||||
*/
|
||||
function setQueueTimeout(interaction) {
|
||||
let channel = (0, util_1.getChannel)(interaction);
|
||||
return setTimeout(() => {
|
||||
clearQueue(interaction);
|
||||
channel.send('Queue has been reset due to inactivity');
|
||||
}, 5 * 60 * 1000); //5 minutes
|
||||
}
|
||||
/**
|
||||
* updates the rich embed for the current queue
|
||||
* @param interaction
|
||||
*/
|
||||
async function sendQueueEmbed(interaction, closed = false) {
|
||||
let info = getInfo(interaction), origInteraction = info.initiator.interaction;
|
||||
let embed = new discord_js_1.MessageEmbed()
|
||||
.setTitle('Queue')
|
||||
.setAuthor({
|
||||
name: info.initiator.member.displayName,
|
||||
iconURL: info.initiator.member.displayAvatarURL({ dynamic: true })
|
||||
})
|
||||
.addField('Team Size', info.teamsize.toString(), true)
|
||||
.addField('Players Joined', info.players.length.toString(), true)
|
||||
.setFooter({ text: closed ? 'queue is finished' : 'type /join' });
|
||||
if (origInteraction.deferred || origInteraction.replied)
|
||||
await origInteraction.editReply({ embeds: [embed] });
|
||||
else
|
||||
await origInteraction.reply({ embeds: [embed] });
|
||||
}
|
||||
/**
|
||||
* sends the list of teams
|
||||
* @param interaction
|
||||
*/
|
||||
async function sendTeamsEmbed(interaction, teams) {
|
||||
let embed = new discord_js_1.MessageEmbed()
|
||||
.setTitle('Teams');
|
||||
teams.forEach((team, i) => {
|
||||
team.map(m => m.user.tag);
|
||||
embed.addField(`Title ${i + 1}`, team.join('\n'));
|
||||
});
|
||||
interaction.reply({ embeds: [embed] });
|
||||
}
|
||||
/**
|
||||
* sends the list of teams
|
||||
* @param interaction
|
||||
*/
|
||||
async function clearQueue(interaction) {
|
||||
let info = getInfo(interaction);
|
||||
sendQueueEmbed(interaction, true);
|
||||
clearTimeout(info.timeout);
|
||||
QUEUE.delete(interaction.channelId);
|
||||
}
|
||||
/**
|
||||
* creates a queue from an interaction
|
||||
* @param interaction
|
||||
* @throws string message if it cannot be created
|
||||
* @throws errorMessage class if it cannot be created
|
||||
*/
|
||||
async function createQueue(interaction) {
|
||||
let { channelId } = interaction, member = getMember(interaction), teamsize = interaction.options.getInteger('teamsize', true);
|
||||
let member = (0, util_1.getMember)(interaction), { channelId } = interaction, teamsize = interaction.options.getInteger('teamsize', true);
|
||||
if (QUEUE.has(channelId))
|
||||
throw 'There is already an active queue in this channel, ' + (queueContains(interaction) ? 'and you are already in it' : 'type `/join` to join'); //and you are already in it
|
||||
throw (0, util_1.emsg)('There is already an active queue in this channel, ' + (queueContains(interaction) ? 'and you are already in it' : 'type `/join` to join'));
|
||||
QUEUE.set(channelId, {
|
||||
players: [
|
||||
member
|
||||
],
|
||||
initiator: member,
|
||||
teamsize: teamsize
|
||||
initiator: {
|
||||
interaction,
|
||||
member
|
||||
},
|
||||
teamsize: teamsize,
|
||||
timeout: setQueueTimeout(interaction)
|
||||
});
|
||||
await interaction.reply(`Queue for teams of ${teamsize} has been created, and you have joined`);
|
||||
sendQueueEmbed(interaction);
|
||||
}
|
||||
exports.createQueue = createQueue;
|
||||
/**
|
||||
* joins a queue from an interaction
|
||||
* @param interaction
|
||||
* @throws string message if it cannot be joined
|
||||
* @throws errorMessage class if it cannot be joined
|
||||
*/
|
||||
async function joinQueue(interaction) {
|
||||
let member = getMember(interaction), info = getInfo(interaction);
|
||||
let { member, info } = (0, exports.getAll)(interaction);
|
||||
if (queueContains(interaction))
|
||||
throw 'You are already in the active queue';
|
||||
throw (0, util_1.emsg)('You are already in the active queue');
|
||||
info.players.push(member);
|
||||
clearTimeout(info.timeout);
|
||||
info.timeout = setQueueTimeout(interaction);
|
||||
QUEUE.set(interaction.channelId, info);
|
||||
sendQueueEmbed(interaction);
|
||||
await interaction.reply('Joined the queue');
|
||||
}
|
||||
exports.joinQueue = joinQueue;
|
||||
/**
|
||||
* leaves a queue from an interaction
|
||||
* @param interaction
|
||||
* @throws string message if it cannot be left
|
||||
* @throws errorMessage class if it cannot be left
|
||||
*/
|
||||
async function leaveQueue(interaction) {
|
||||
let member = getMember(interaction), info = getInfo(interaction);
|
||||
let { member, info } = (0, exports.getAll)(interaction);
|
||||
if (!queueContains(interaction))
|
||||
throw 'You aren\'t in the active queue';
|
||||
throw (0, util_1.emsg)('You aren\'t in the active queue');
|
||||
info.players.splice(info.players.indexOf(member), 1);
|
||||
clearTimeout(info.timeout);
|
||||
info.timeout = setQueueTimeout(interaction);
|
||||
QUEUE.set(interaction.channelId, info);
|
||||
sendQueueEmbed(interaction);
|
||||
await interaction.reply('Left the queue');
|
||||
}
|
||||
exports.leaveQueue = leaveQueue;
|
||||
/**
|
||||
* readys a queue from an interaction
|
||||
* @param interaction
|
||||
* @throws string message if it cannot be readied
|
||||
* @throws errorMessage class if it cannot be readied
|
||||
*/
|
||||
async function readyQueue(interaction) {
|
||||
let member = getMember(interaction), info = getInfo(interaction), { initiator } = info;
|
||||
if (member.id !== initiator.id)
|
||||
throw 'Only the queue initiator can ready the queue';
|
||||
//reset queue
|
||||
QUEUE.delete(interaction.channelId);
|
||||
if (info.players.filter(m => m.id !== initiator.id).length === 0)
|
||||
throw 'Nobody signed up for the queue, the queue has been reset';
|
||||
let { member, info } = (0, exports.getAll)(interaction), { initiator } = info;
|
||||
if (member.id !== initiator.member.id)
|
||||
throw (0, util_1.emsg)('Only the queue initiator can ready the queue');
|
||||
clearQueue(interaction);
|
||||
if (info.players.filter(m => m.id !== initiator.member.id).length === 0)
|
||||
throw (0, util_1.emsg)('Nobody signed up for the queue, the queue has been reset');
|
||||
//team data
|
||||
let playerlist = (0, util_1.shuffle)(info.players), teams = [];
|
||||
//fill team data
|
||||
for (let i = 0; i < playerlist.length; i += info.teamsize)
|
||||
teams.push(playerlist.slice(i, i + info.teamsize));
|
||||
//convert teams to strings
|
||||
let teamsStr = [];
|
||||
teams.forEach((team, i) => {
|
||||
let str = [`Team ${i + 1}`];
|
||||
team.forEach(m => str.push(` ${m.user.tag}`));
|
||||
teamsStr.push(str.join('\n'));
|
||||
});
|
||||
await interaction.reply('```\n' + teamsStr.join('\n\n') + '\n```');
|
||||
sendTeamsEmbed(interaction, teams);
|
||||
}
|
||||
exports.readyQueue = readyQueue;
|
||||
/**
|
||||
* sends the queue information from an interaction
|
||||
* readys a queue from an interaction
|
||||
* @param interaction
|
||||
* @throws string message if it cannot be read
|
||||
* @throws errorMessage class if it cannot be reset
|
||||
*/
|
||||
async function queueInfo(interaction) {
|
||||
let info = getInfo(interaction);
|
||||
await interaction.reply('```' + `
|
||||
players: ${info.players.map(p => p.user.tag).join('\n ')}
|
||||
initiator: ${info.initiator.user.tag}
|
||||
teamsize: ${info.teamsize}
|
||||
` + '```');
|
||||
async function cancelQueue(interaction) {
|
||||
let { info, member, channel } = (0, exports.getAll)(interaction);
|
||||
if (!member.permissionsIn(channel).has('MANAGE_MESSAGES'))
|
||||
throw (0, util_1.emsg)('You do not have permission to run this command');
|
||||
clearQueue(interaction);
|
||||
await interaction.reply('Queue has been reset');
|
||||
}
|
||||
exports.queueInfo = queueInfo;
|
||||
exports.cancelQueue = cancelQueue;
|
||||
|
||||
44
dist/util.js
vendored
44
dist/util.js
vendored
@@ -1,6 +1,7 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.shuffle = void 0;
|
||||
exports.getChannel = exports.getMember = exports.emsg = exports.errorMessage = exports.shuffle = void 0;
|
||||
const discord_js_1 = require("discord.js");
|
||||
/**
|
||||
* shuffles an array
|
||||
* https://stackoverflow.com/a/2450976/2856416
|
||||
@@ -22,3 +23,44 @@ function shuffle(array) {
|
||||
return array;
|
||||
}
|
||||
exports.shuffle = shuffle;
|
||||
class errorMessage {
|
||||
constructor(msg, ephemeral = true) {
|
||||
this.msg = msg;
|
||||
this.ephemeral = ephemeral;
|
||||
}
|
||||
}
|
||||
exports.errorMessage = errorMessage;
|
||||
/**
|
||||
* a simple class to contain an error message and related data
|
||||
* @param msg error message
|
||||
* @param ephemeral (default=true)
|
||||
* @returns new errorMessage
|
||||
*/
|
||||
const emsg = (msg, ephemeral = true) => new errorMessage(msg, ephemeral);
|
||||
exports.emsg = emsg;
|
||||
/**
|
||||
* get the GuildMember of an interaction
|
||||
* @param interaction
|
||||
* @throws errorMessage class if it cannot be read
|
||||
* @returns member
|
||||
*/
|
||||
function getMember(interaction) {
|
||||
let member = interaction.member;
|
||||
if (!(member instanceof discord_js_1.GuildMember))
|
||||
throw (0, exports.emsg)('Unable to retrieve guild member information, please try again');
|
||||
return member;
|
||||
}
|
||||
exports.getMember = getMember;
|
||||
/**
|
||||
* get the TextChannel of an interaction
|
||||
* @param interaction
|
||||
* @throws errorMessage class if it cannot be read
|
||||
* @returns member
|
||||
*/
|
||||
function getChannel(interaction) {
|
||||
let channel = interaction.channel;
|
||||
if (!(channel instanceof discord_js_1.TextChannel))
|
||||
throw (0, exports.emsg)('Unable to retrieve text channel information, please try again');
|
||||
return channel;
|
||||
}
|
||||
exports.getChannel = getChannel;
|
||||
|
||||
86
package-lock.json
generated
86
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "1800queue",
|
||||
"version": "0.0.1",
|
||||
"name": "1800queue-alt",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "1800queue",
|
||||
"version": "0.0.1",
|
||||
"name": "1800queue-alt",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@discordjs/rest": "^0.3.0",
|
||||
@@ -58,17 +58,6 @@
|
||||
"npm": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@discordjs/builders/node_modules/@sindresorhus/is": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz",
|
||||
"integrity": "sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@discordjs/collection": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.4.0.tgz",
|
||||
@@ -96,30 +85,32 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@sapphire/async-queue": {
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.1.9.tgz",
|
||||
"integrity": "sha512-CbXaGwwlEMq+l1TRu01FJCvySJ1CEFKFclHT48nIfNeZXaAAmmwwy7scUKmYHPUa3GhoMp6Qr1B3eAJux6XgOQ==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.2.0.tgz",
|
||||
"integrity": "sha512-O5ND5Ljpef86X5oy8zXorQ754TMjWALcPSAgPBu4+76HLtDTrNoDyzU2uGE2G4A8Wv51u0MXHzGQ0WZ4GMtpIw==",
|
||||
"engines": {
|
||||
"node": ">=v14.0.0",
|
||||
"npm": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sapphire/snowflake": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.0.1.tgz",
|
||||
"integrity": "sha512-v+wCC2q9DK3OhG7Vcdt/8A/INAYiyhlMD5snakmXGBN1usLBwSGJVJBjDHv4VGI5C9YYl4UdW5Ovr3arvYsJXQ==",
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.1.0.tgz",
|
||||
"integrity": "sha512-K+OiqXSx4clIaXcoaghrCV56zsm3bZZ5SBpgJkgvAKegFFdETMntHviUfypjt8xVleIuDaNyQA4APOIl3BMcxg==",
|
||||
"engines": {
|
||||
"node": ">=v14.0.0",
|
||||
"npm": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sindresorhus/is": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
|
||||
"integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
|
||||
"dev": true,
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz",
|
||||
"integrity": "sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@szmarczak/http-timer": {
|
||||
@@ -896,6 +887,15 @@
|
||||
"node": ">=8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/got/node_modules/@sindresorhus/is": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
|
||||
"integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/graceful-fs": {
|
||||
"version": "4.2.9",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz",
|
||||
@@ -1986,13 +1986,6 @@
|
||||
"ts-mixer": "^6.0.0",
|
||||
"tslib": "^2.3.1",
|
||||
"zod": "^3.11.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sindresorhus/is": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz",
|
||||
"integrity": "sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@discordjs/collection": {
|
||||
@@ -2015,20 +2008,19 @@
|
||||
}
|
||||
},
|
||||
"@sapphire/async-queue": {
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.1.9.tgz",
|
||||
"integrity": "sha512-CbXaGwwlEMq+l1TRu01FJCvySJ1CEFKFclHT48nIfNeZXaAAmmwwy7scUKmYHPUa3GhoMp6Qr1B3eAJux6XgOQ=="
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.2.0.tgz",
|
||||
"integrity": "sha512-O5ND5Ljpef86X5oy8zXorQ754TMjWALcPSAgPBu4+76HLtDTrNoDyzU2uGE2G4A8Wv51u0MXHzGQ0WZ4GMtpIw=="
|
||||
},
|
||||
"@sapphire/snowflake": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.0.1.tgz",
|
||||
"integrity": "sha512-v+wCC2q9DK3OhG7Vcdt/8A/INAYiyhlMD5snakmXGBN1usLBwSGJVJBjDHv4VGI5C9YYl4UdW5Ovr3arvYsJXQ=="
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.1.0.tgz",
|
||||
"integrity": "sha512-K+OiqXSx4clIaXcoaghrCV56zsm3bZZ5SBpgJkgvAKegFFdETMntHviUfypjt8xVleIuDaNyQA4APOIl3BMcxg=="
|
||||
},
|
||||
"@sindresorhus/is": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
|
||||
"integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
|
||||
"dev": true
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.4.0.tgz",
|
||||
"integrity": "sha512-QppPM/8l3Mawvh4rn9CNEYIU9bxpXUCRMaX9yUpvBk1nMKusLKpfXGDEKExKaPhLzcn3lzil7pR6rnJ11HgeRQ=="
|
||||
},
|
||||
"@szmarczak/http-timer": {
|
||||
"version": "1.1.2",
|
||||
@@ -2618,6 +2610,14 @@
|
||||
"p-cancelable": "^1.0.0",
|
||||
"to-readable-stream": "^1.0.0",
|
||||
"url-parse-lax": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sindresorhus/is": {
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz",
|
||||
"integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "1800queue",
|
||||
"version": "0.0.1",
|
||||
"name": "1800queue-alt",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
@REM change to ccurrent directory
|
||||
cd %~f0\..\..\..\
|
||||
|
||||
@REM run
|
||||
npm install
|
||||
|
||||
@REM discord token
|
||||
if not exist "token" copy NUL "token"
|
||||
|
||||
@@ -14,3 +11,6 @@ if %size% equ 0 (
|
||||
set /p token="Enter your discord bot token: "
|
||||
echo | set /p=%id% > "token"
|
||||
)
|
||||
|
||||
@REM run
|
||||
npm install
|
||||
|
||||
42
src/api.ts
42
src/api.ts
@@ -1,7 +1,8 @@
|
||||
import * as cheerio from 'cheerio';
|
||||
import { CommandInteraction } from 'discord.js';
|
||||
import { CommandInteraction, MessageEmbed } from 'discord.js';
|
||||
import { IncomingMessage } from 'http';
|
||||
import http from 'https';
|
||||
import { emsg } from './util';
|
||||
|
||||
const uniteApiRegex = {
|
||||
//$1 = name, $2 = id
|
||||
@@ -16,6 +17,7 @@ const uniteApiRegex = {
|
||||
type uniteApiData = {
|
||||
name: string,
|
||||
id: string,
|
||||
avatar: string,
|
||||
|
||||
level: string,
|
||||
rank: string,
|
||||
@@ -71,12 +73,15 @@ function getHTML(name: string): Promise<string> {
|
||||
* interprets the html from getHTML()
|
||||
* @param name name of player
|
||||
* @returns player data from site
|
||||
* @throws errorMessage class if the request fails
|
||||
*/
|
||||
function readHTML(html: string): uniteApiData {
|
||||
let metaElems = cheerio.load(html)('meta').toArray(),
|
||||
let $ = cheerio.load(html)
|
||||
let metaElems = $('meta').toArray(),
|
||||
foundData: uniteApiData = {
|
||||
name: "",
|
||||
id: "",
|
||||
avatar: "",
|
||||
|
||||
level: "",
|
||||
rank: "",
|
||||
@@ -113,7 +118,7 @@ function readHTML(html: string): uniteApiData {
|
||||
}
|
||||
|
||||
if (!lines.length)
|
||||
throw 'Unable to read data, please try again';
|
||||
throw emsg('Unable to read data, please try again');
|
||||
|
||||
//bring the first lines removed back into the data
|
||||
lines = [
|
||||
@@ -133,7 +138,7 @@ function readHTML(html: string): uniteApiData {
|
||||
let regexData = line.match(regex[0]);
|
||||
|
||||
if (!regexData || regexData.length < 4)
|
||||
throw 'Unable to read data, please try again';
|
||||
throw emsg('Unable to read data, please try again');
|
||||
|
||||
foundData.level = regexData[1];
|
||||
foundData.rank = regexData[2];
|
||||
@@ -144,7 +149,7 @@ function readHTML(html: string): uniteApiData {
|
||||
let regexData = line.match(regex[1]);
|
||||
|
||||
if (!regexData || regexData.length < 4)
|
||||
throw 'Unable to read data, please try again';
|
||||
throw emsg('Unable to read data, please try again');
|
||||
|
||||
foundData.level = regexData[1];
|
||||
foundData.rank = regexData[2];
|
||||
@@ -179,7 +184,10 @@ function readHTML(html: string): uniteApiData {
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
foundData.avatar = $('.player-card-image img').attr('src') || "";
|
||||
foundData.avatar = foundData.avatar.replace('../', 'https://uniteapi.dev/');
|
||||
|
||||
return foundData;
|
||||
|
||||
@@ -209,9 +217,27 @@ export async function getPlayer(name: string): Promise<uniteApiData|null> {
|
||||
return null;
|
||||
}
|
||||
|
||||
async function sendPlayerEmbed(interaction: CommandInteraction, data: uniteApiData) {
|
||||
|
||||
let embed = new MessageEmbed()
|
||||
.setTitle(`${data.name} (${data.id})`)
|
||||
.setURL(`https://uniteapi.dev/p/${encodeURIComponent(data.name)}`)
|
||||
.setTimestamp()
|
||||
.setThumbnail(data.avatar)
|
||||
.setDescription(`Level ${data.level}
|
||||
${data.rank} ${data.elo ? `(${data.elo})` : `Class ${data.class}`}
|
||||
|
||||
**Battles** ${data.battles}
|
||||
**Wins** ${data.wins}
|
||||
**Win Rate** ${data.winrate}`);
|
||||
|
||||
await interaction.editReply({embeds: [embed]});
|
||||
}
|
||||
|
||||
/**
|
||||
* calls getPlayer() with the name from the interaction
|
||||
* @param interaction discord interaction
|
||||
* @throws errorMessage class if the user cannot be found
|
||||
*/
|
||||
export async function getPlayerInteraction(interaction: CommandInteraction) {
|
||||
let username = interaction.options.getString('username', true);
|
||||
@@ -219,7 +245,7 @@ export async function getPlayerInteraction(interaction: CommandInteraction) {
|
||||
|
||||
let data = await getPlayer(username);
|
||||
if (data === null)
|
||||
await interaction.editReply('Unable to find user');
|
||||
throw emsg('Unable to find user');
|
||||
else
|
||||
await interaction.editReply('```\n'+JSON.stringify(data, null, 2)+'\n```');
|
||||
sendPlayerEmbed(interaction, data);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ const commands = [
|
||||
type: 4, //INTEGER
|
||||
name: 'teamsize',
|
||||
description: 'size of each team',
|
||||
required: true
|
||||
required: true,
|
||||
min_value: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -28,17 +29,17 @@ const commands = [
|
||||
description: 'ready the queue and display team info'
|
||||
},
|
||||
{
|
||||
name: 'queueinfo',
|
||||
description: 'get info of the current queue'
|
||||
name: 'cancel',
|
||||
description: 'cancels the current queue (must have the Manage Messages permission)'
|
||||
},
|
||||
{
|
||||
name: 'elo',
|
||||
description: 'display elo information',
|
||||
name: 'player',
|
||||
description: 'display player information',
|
||||
options: [
|
||||
{
|
||||
type: 3, //STRING
|
||||
name: 'username',
|
||||
description: 'in game name',
|
||||
description: 'in game name or UniteApi short link',
|
||||
required: true
|
||||
}
|
||||
]
|
||||
|
||||
20
src/index.ts
20
src/index.ts
@@ -3,7 +3,8 @@ import { Client, Intents } from 'discord.js';
|
||||
import * as fs from 'fs';
|
||||
import { getPlayerInteraction } from './api';
|
||||
import { registerCommands } from './discord';
|
||||
import { createQueue, joinQueue, leaveQueue, queueInfo, readyQueue } from './queue';
|
||||
import { cancelQueue, createQueue, joinQueue, leaveQueue, readyQueue } from './queue';
|
||||
import { errorMessage } from './util';
|
||||
const CLIENT = new Client({ intents: [Intents.FLAGS.GUILDS] });
|
||||
|
||||
//init logs with a timestamp
|
||||
@@ -37,18 +38,23 @@ CLIENT.on('interactionCreate', async interaction => {
|
||||
await leaveQueue(interaction);
|
||||
else if (interaction.commandName === 'ready')
|
||||
await readyQueue(interaction);
|
||||
else if (interaction.commandName === 'queueinfo')
|
||||
await queueInfo(interaction);
|
||||
else if (interaction.commandName === 'elo')
|
||||
else if (interaction.commandName === 'cancel')
|
||||
await cancelQueue(interaction);
|
||||
else if (interaction.commandName === 'player')
|
||||
await getPlayerInteraction(interaction);
|
||||
|
||||
} catch (e) {
|
||||
|
||||
if (typeof e === 'string') {
|
||||
if (e instanceof errorMessage) {
|
||||
|
||||
if (interaction.deferred || interaction.replied)
|
||||
interaction.editReply(e);
|
||||
interaction.editReply(e.msg);
|
||||
else
|
||||
interaction.reply(e);
|
||||
interaction.reply({
|
||||
content: e.msg,
|
||||
ephemeral: e.ephemeral
|
||||
});
|
||||
|
||||
} else console.error(e);
|
||||
|
||||
}
|
||||
|
||||
198
src/queue.ts
198
src/queue.ts
@@ -1,46 +1,36 @@
|
||||
import { info } from "console";
|
||||
import { CommandInteraction, GuildMember } from "discord.js";
|
||||
import { shuffle } from "./util";
|
||||
|
||||
type queueInfo = {
|
||||
players: GuildMember[],
|
||||
initiator: GuildMember,
|
||||
teamsize: number
|
||||
}
|
||||
import { CommandInteraction, GuildMember, MessageEmbed } from "discord.js";
|
||||
import { emsg, getChannel, getMember, queueInfo, shuffle } from "./util";
|
||||
|
||||
//maps ChannelID to QueueInfo
|
||||
const QUEUE = new Map<string, queueInfo>();
|
||||
|
||||
/**
|
||||
* get the GuildMember of an interaction
|
||||
* @param interaction
|
||||
* @throws string message if it cannot be read
|
||||
* @returns member
|
||||
*/
|
||||
function getMember(interaction: CommandInteraction): GuildMember {
|
||||
let member = interaction.member;
|
||||
|
||||
if (!(member instanceof GuildMember))
|
||||
throw 'Unable to retrieve guild member information, please try again';
|
||||
|
||||
return member;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the queueInfo of an interaction
|
||||
* @param interaction
|
||||
* @throws string message if it does not exist
|
||||
* @throws errorMessage class if it does not exist
|
||||
* @returns queue info
|
||||
*/
|
||||
function getInfo(interaction: CommandInteraction): queueInfo {
|
||||
export function getInfo(interaction: CommandInteraction): queueInfo {
|
||||
let info = QUEUE.get(interaction.channelId);
|
||||
|
||||
if (!info)
|
||||
throw 'There is not an active queue in this channel, type `/queue` to create one';
|
||||
throw emsg('There is not an active queue in this channel, type `/queue` to create one');
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* compiles all the get functions above
|
||||
* @param interaction
|
||||
* @throws if another get function throws
|
||||
* @returns object containing each
|
||||
*/
|
||||
export const getAll = (interaction: CommandInteraction) => ({
|
||||
member: getMember(interaction),
|
||||
channel: getChannel(interaction),
|
||||
info: getInfo(interaction)
|
||||
});
|
||||
|
||||
/**
|
||||
* checks if the interaction data is already in the queue
|
||||
* @param interaction
|
||||
@@ -48,8 +38,7 @@ function getInfo(interaction: CommandInteraction): queueInfo {
|
||||
*/
|
||||
export function queueContains(interaction: CommandInteraction): boolean {
|
||||
|
||||
let member = getMember(interaction),
|
||||
info = getInfo(interaction);
|
||||
let {member, info} = getAll(interaction);
|
||||
|
||||
if (info.players.map(m=>m.id).includes(member.id))
|
||||
return true;
|
||||
@@ -58,49 +47,119 @@ export function queueContains(interaction: CommandInteraction): boolean {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* creates the timeout for the queue
|
||||
* @param interaction
|
||||
* @returns time timeout identifier
|
||||
*/
|
||||
function setQueueTimeout(interaction: CommandInteraction) {
|
||||
let channel = getChannel(interaction);
|
||||
return setTimeout(() => {
|
||||
clearQueue(interaction);
|
||||
channel.send('Queue has been reset due to inactivity');
|
||||
}, 5*60*1000) //5 minutes
|
||||
}
|
||||
|
||||
/**
|
||||
* updates the rich embed for the current queue
|
||||
* @param interaction
|
||||
*/
|
||||
async function sendQueueEmbed(interaction: CommandInteraction, closed: boolean = false) {
|
||||
let info = getInfo(interaction),
|
||||
origInteraction = info.initiator.interaction;
|
||||
|
||||
let embed = new MessageEmbed()
|
||||
.setTitle('Queue')
|
||||
.setAuthor({
|
||||
name: info.initiator.member.displayName,
|
||||
iconURL: info.initiator.member.displayAvatarURL({dynamic: true})
|
||||
})
|
||||
.addField('Team Size', info.teamsize.toString(), true)
|
||||
.addField('Players Joined', info.players.length.toString(), true)
|
||||
.setFooter({text: closed ? 'queue is finished' : 'type /join'});
|
||||
|
||||
if (origInteraction.deferred || origInteraction.replied)
|
||||
await origInteraction.editReply({embeds: [embed]});
|
||||
else
|
||||
await origInteraction.reply({embeds: [embed]});
|
||||
}
|
||||
|
||||
/**
|
||||
* sends the list of teams
|
||||
* @param interaction
|
||||
*/
|
||||
async function sendTeamsEmbed(interaction: CommandInteraction, teams: GuildMember[][]) {
|
||||
let embed = new MessageEmbed()
|
||||
.setTitle('Teams');
|
||||
|
||||
teams.forEach((team, i) => {
|
||||
team.map(m => m.user.tag);
|
||||
embed.addField(`Team ${i+1}`, team.join('\n'))
|
||||
});
|
||||
|
||||
interaction.reply({embeds: [embed]});
|
||||
}
|
||||
|
||||
/**
|
||||
* sends the list of teams
|
||||
* @param interaction
|
||||
*/
|
||||
async function clearQueue(interaction: CommandInteraction) {
|
||||
let info = getInfo(interaction);
|
||||
sendQueueEmbed(interaction, true);
|
||||
clearTimeout(info.timeout);
|
||||
QUEUE.delete(interaction.channelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a queue from an interaction
|
||||
* @param interaction
|
||||
* @throws string message if it cannot be created
|
||||
* @throws errorMessage class if it cannot be created
|
||||
*/
|
||||
export async function createQueue(interaction: CommandInteraction) {
|
||||
|
||||
let {channelId} = interaction,
|
||||
member = getMember(interaction),
|
||||
let member = getMember(interaction),
|
||||
{channelId} = interaction,
|
||||
teamsize = interaction.options.getInteger('teamsize', true);
|
||||
|
||||
if (QUEUE.has(channelId))
|
||||
throw 'There is already an active queue in this channel, ' + (queueContains(interaction) ? 'and you are already in it' : 'type `/join` to join'); //and you are already in it
|
||||
throw emsg('There is already an active queue in this channel, ' + (queueContains(interaction) ? 'and you are already in it' : 'type `/join` to join'));
|
||||
|
||||
QUEUE.set(channelId, {
|
||||
players: [
|
||||
member
|
||||
],
|
||||
initiator: member,
|
||||
teamsize: teamsize
|
||||
initiator: {
|
||||
interaction,
|
||||
member
|
||||
},
|
||||
teamsize: teamsize,
|
||||
timeout: setQueueTimeout(interaction)
|
||||
});
|
||||
|
||||
await interaction.reply(`Queue for teams of ${teamsize} has been created, and you have joined`);
|
||||
sendQueueEmbed(interaction);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* joins a queue from an interaction
|
||||
* @param interaction
|
||||
* @throws string message if it cannot be joined
|
||||
* @throws errorMessage class if it cannot be joined
|
||||
*/
|
||||
export async function joinQueue(interaction: CommandInteraction) {
|
||||
|
||||
let member = getMember(interaction),
|
||||
info = getInfo(interaction);
|
||||
let {member, info} = getAll(interaction);
|
||||
|
||||
if (queueContains(interaction))
|
||||
throw 'You are already in the active queue';
|
||||
throw emsg('You are already in the active queue');
|
||||
|
||||
info.players.push(member);
|
||||
clearTimeout(info.timeout);
|
||||
info.timeout = setQueueTimeout(interaction)
|
||||
|
||||
QUEUE.set(interaction.channelId, info);
|
||||
|
||||
sendQueueEmbed(interaction);
|
||||
await interaction.reply('Joined the queue');
|
||||
|
||||
}
|
||||
@@ -108,20 +167,22 @@ export async function joinQueue(interaction: CommandInteraction) {
|
||||
/**
|
||||
* leaves a queue from an interaction
|
||||
* @param interaction
|
||||
* @throws string message if it cannot be left
|
||||
* @throws errorMessage class if it cannot be left
|
||||
*/
|
||||
export async function leaveQueue(interaction: CommandInteraction) {
|
||||
|
||||
let member = getMember(interaction),
|
||||
info = getInfo(interaction);
|
||||
let {member, info} = getAll(interaction);
|
||||
|
||||
if (!queueContains(interaction))
|
||||
throw 'You aren\'t in the active queue';
|
||||
throw emsg('You aren\'t in the active queue');
|
||||
|
||||
info.players.splice(info.players.indexOf(member), 1);
|
||||
clearTimeout(info.timeout);
|
||||
info.timeout = setQueueTimeout(interaction)
|
||||
|
||||
QUEUE.set(interaction.channelId, info);
|
||||
|
||||
sendQueueEmbed(interaction);
|
||||
await interaction.reply('Left the queue');
|
||||
|
||||
}
|
||||
@@ -129,22 +190,20 @@ export async function leaveQueue(interaction: CommandInteraction) {
|
||||
/**
|
||||
* readys a queue from an interaction
|
||||
* @param interaction
|
||||
* @throws string message if it cannot be readied
|
||||
* @throws errorMessage class if it cannot be readied
|
||||
*/
|
||||
export async function readyQueue(interaction: CommandInteraction) {
|
||||
|
||||
let member = getMember(interaction),
|
||||
info = getInfo(interaction),
|
||||
let {member, info} = getAll(interaction),
|
||||
{initiator} = info;
|
||||
|
||||
if (member.id !== initiator.id)
|
||||
throw 'Only the queue initiator can ready the queue';
|
||||
if (member.id !== initiator.member.id)
|
||||
throw emsg('Only the queue initiator can ready the queue');
|
||||
|
||||
//reset queue
|
||||
QUEUE.delete(interaction.channelId);
|
||||
clearQueue(interaction);
|
||||
|
||||
if (info.players.filter(m => m.id !== initiator.id).length === 0)
|
||||
throw 'Nobody signed up for the queue, the queue has been reset';
|
||||
if (info.players.filter(m => m.id !== initiator.member.id).length === 0)
|
||||
throw emsg('Nobody signed up for the queue, the queue has been reset');
|
||||
|
||||
//team data
|
||||
let playerlist: GuildMember[] = shuffle(info.players),
|
||||
@@ -154,33 +213,24 @@ export async function readyQueue(interaction: CommandInteraction) {
|
||||
for (let i = 0; i < playerlist.length; i+= info.teamsize)
|
||||
teams.push(playerlist.slice(i, i+info.teamsize));
|
||||
|
||||
//convert teams to strings
|
||||
let teamsStr: string[] = [];
|
||||
teams.forEach((team, i) => {
|
||||
let str = [`Team ${i+1}`];
|
||||
|
||||
team.forEach(m => str.push(` ${m.user.tag}`));
|
||||
|
||||
teamsStr.push(str.join('\n'));
|
||||
});
|
||||
|
||||
await interaction.reply('```\n'+teamsStr.join('\n\n')+'\n```');
|
||||
sendTeamsEmbed(interaction, teams);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* sends the queue information from an interaction
|
||||
* readys a queue from an interaction
|
||||
* @param interaction
|
||||
* @throws string message if it cannot be read
|
||||
* @throws errorMessage class if it cannot be reset
|
||||
*/
|
||||
export async function queueInfo(interaction: CommandInteraction) {
|
||||
export async function cancelQueue(interaction: CommandInteraction) {
|
||||
|
||||
let info = getInfo(interaction);
|
||||
let {info, member, channel} = getAll(interaction);
|
||||
|
||||
await interaction.reply('```'+`
|
||||
players: ${info.players.map(p => p.user.tag).join('\n ')}
|
||||
initiator: ${info.initiator.user.tag}
|
||||
teamsize: ${info.teamsize}
|
||||
`+'```');
|
||||
if (!member.permissionsIn(channel).has('MANAGE_MESSAGES'))
|
||||
throw emsg('You do not have permission to run this command');
|
||||
|
||||
}
|
||||
clearQueue(interaction);
|
||||
|
||||
await interaction.reply('Queue has been reset');
|
||||
|
||||
}
|
||||
|
||||
63
src/util.ts
63
src/util.ts
@@ -1,3 +1,4 @@
|
||||
import { CommandInteraction, GuildMember, TextChannel } from "discord.js";
|
||||
|
||||
/**
|
||||
* shuffles an array
|
||||
@@ -22,3 +23,65 @@ export function shuffle(array: any[]) {
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class errorMessage {
|
||||
public msg: string;
|
||||
public ephemeral: boolean;
|
||||
|
||||
constructor(msg: string, ephemeral: boolean = true) {
|
||||
this.msg = msg;
|
||||
this.ephemeral = ephemeral;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* a simple class to contain an error message and related data
|
||||
* @param msg error message
|
||||
* @param ephemeral (default=true)
|
||||
* @returns new errorMessage
|
||||
*/
|
||||
export const emsg = (msg: string, ephemeral: boolean = true) => new errorMessage(msg, ephemeral);
|
||||
|
||||
|
||||
|
||||
export type queueInfo = {
|
||||
players: GuildMember[],
|
||||
initiator: {
|
||||
interaction: CommandInteraction,
|
||||
member: GuildMember
|
||||
},
|
||||
teamsize: number,
|
||||
timeout: NodeJS.Timeout
|
||||
}
|
||||
|
||||
/**
|
||||
* get the GuildMember of an interaction
|
||||
* @param interaction
|
||||
* @throws errorMessage class if it cannot be read
|
||||
* @returns member
|
||||
*/
|
||||
export function getMember(interaction: CommandInteraction): GuildMember {
|
||||
let member = interaction.member;
|
||||
|
||||
if (!(member instanceof GuildMember))
|
||||
throw emsg('Unable to retrieve guild member information, please try again');
|
||||
|
||||
return member;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the TextChannel of an interaction
|
||||
* @param interaction
|
||||
* @throws errorMessage class if it cannot be read
|
||||
* @returns member
|
||||
*/
|
||||
export function getChannel(interaction: CommandInteraction): TextChannel {
|
||||
let channel = interaction.channel;
|
||||
|
||||
if (!(channel instanceof TextChannel))
|
||||
throw emsg('Unable to retrieve text channel information, please try again');
|
||||
|
||||
return channel;
|
||||
}
|
||||
Reference in New Issue
Block a user