Compare commits
2 Commits
513a9d1582
...
a3103f73c3
| Author | SHA1 | Date | |
|---|---|---|---|
| a3103f73c3 | |||
| 5348ab487b |
@@ -5,7 +5,7 @@
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": { "project": ["./tsconfig.json"] },
|
||||
"parserOptions": { "project": ["./tsconfig.eslint.json"] },
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
@@ -17,10 +17,7 @@
|
||||
"allowNumber" : false
|
||||
}
|
||||
],
|
||||
|
||||
/* dev */
|
||||
"no-debugger": "off",
|
||||
|
||||
|
||||
/* important */
|
||||
"prefer-const": "error",
|
||||
"quotes": ["error", "single"],
|
||||
|
||||
10
dist/api.js
vendored
10
dist/api.js
vendored
@@ -26,7 +26,7 @@ exports.getPlayerInteraction = exports.getPlayer = void 0;
|
||||
/*eslint prefer-const: "error"*/
|
||||
const cheerio = __importStar(require("cheerio"));
|
||||
const https_1 = __importDefault(require("https"));
|
||||
const util_1 = require("./util");
|
||||
const main_1 = require("./util/main");
|
||||
const Lang = __importStar(require("./lang"));
|
||||
const uniteApiRegex = {
|
||||
//$1 = name, $2 = id
|
||||
@@ -109,7 +109,7 @@ function readHTML(html) {
|
||||
extraLines.push(line);
|
||||
}
|
||||
if (lines.length === 0)
|
||||
throw (0, util_1.emsg)('Unable to read data, please try again');
|
||||
throw (0, main_1.emsg)('Unable to read data, please try again');
|
||||
//bring the first lines removed back into the data
|
||||
lines = [
|
||||
...lines,
|
||||
@@ -122,7 +122,7 @@ function readHTML(html) {
|
||||
if (regex[0].test(line)) { //is master/has elo
|
||||
const regexData = line.match(regex[0]);
|
||||
if (!regexData || regexData.length < 4)
|
||||
throw (0, util_1.emsg)('Unable to read data, please try again');
|
||||
throw (0, main_1.emsg)('Unable to read data, please try again');
|
||||
foundData.level = regexData[1];
|
||||
foundData.rank = regexData[2];
|
||||
foundData.elo = regexData[3];
|
||||
@@ -130,7 +130,7 @@ function readHTML(html) {
|
||||
else { //is not master/has a class
|
||||
const regexData = line.match(regex[1]);
|
||||
if (!regexData || regexData.length < 4)
|
||||
throw (0, util_1.emsg)('Unable to read data, please try again');
|
||||
throw (0, main_1.emsg)('Unable to read data, please try again');
|
||||
foundData.level = regexData[1];
|
||||
foundData.rank = regexData[2];
|
||||
foundData.class = regexData[3];
|
||||
@@ -210,7 +210,7 @@ async function getPlayerInteraction(interaction) {
|
||||
await interaction.deferReply();
|
||||
const data = await getPlayer(username);
|
||||
if (data === null)
|
||||
throw (0, util_1.emsg)('api.noUser');
|
||||
throw (0, main_1.emsg)('api.noUser');
|
||||
else
|
||||
sendPlayerEmbed(interaction, data);
|
||||
}
|
||||
|
||||
4
dist/index.js
vendored
4
dist/index.js
vendored
@@ -25,7 +25,7 @@ const api_1 = require("./api");
|
||||
const discord_1 = require("./discord");
|
||||
const Lang = __importStar(require("./lang"));
|
||||
const queue_1 = require("./queue");
|
||||
const util_1 = require("./util");
|
||||
const main_1 = require("./util/main");
|
||||
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');
|
||||
@@ -76,7 +76,7 @@ CLIENT.on('interactionCreate', async (interaction) => {
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
if (e instanceof util_1.errorMessage) {
|
||||
if (e instanceof main_1.errorMessage) {
|
||||
if (interaction.deferred || interaction.replied)
|
||||
interaction.editReply(e.msg);
|
||||
else
|
||||
|
||||
103
dist/lang.js
vendored
103
dist/lang.js
vendored
@@ -1,100 +1,7 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getEmbed = exports.get = exports.setLang = void 0;
|
||||
const discord_js_1 = require("discord.js");
|
||||
/* UTIL FUNCS */
|
||||
function template(str, args) {
|
||||
return str.replace(/{\w+}/g, str => {
|
||||
const key = str.substring(1, str.length - 1);
|
||||
if (key in args)
|
||||
return args[key];
|
||||
return key;
|
||||
});
|
||||
}
|
||||
function bigString(str) {
|
||||
if (typeof str === 'object')
|
||||
return str.join('\n');
|
||||
return str;
|
||||
}
|
||||
function resolveColor(color) {
|
||||
color = color.replace(/[^0-9a-f]/gi, '');
|
||||
const colorNum = [0, 0, 0];
|
||||
if (color.length === 3 || color.length === 6) {
|
||||
const colorSpl = /([0-9a-f]{1,2})([0-9a-f]{1,2})([0-9a-f]{1,2})/.exec(color);
|
||||
if (!colorSpl)
|
||||
return colorNum;
|
||||
for (let i = 0; i < colorSpl.length && i < colorNum.length; i++)
|
||||
colorNum[i] = parseInt(colorSpl[i], 16);
|
||||
}
|
||||
return colorNum;
|
||||
}
|
||||
function embedObjStr(embedData, fallback = '') {
|
||||
if (embedData.content !== undefined)
|
||||
return bigString(embedData.content);
|
||||
if (embedData.description !== undefined)
|
||||
return bigString(embedData.description);
|
||||
return fallback;
|
||||
}
|
||||
function embedObjEmbed(embedObj, args = {}) {
|
||||
const embed = new discord_js_1.MessageEmbed(), { author, color, description, fields, footer, image, thumbnail, timestamp, title, url } = embedObj;
|
||||
if (author !== undefined) {
|
||||
let authorFix;
|
||||
if (typeof author === 'string')
|
||||
authorFix = {
|
||||
name: template(author, args)
|
||||
};
|
||||
else {
|
||||
const { name, icon, url } = author;
|
||||
authorFix = {
|
||||
name: template(name, args)
|
||||
};
|
||||
if (icon !== undefined)
|
||||
authorFix.icon = template(icon, args);
|
||||
if (url !== undefined)
|
||||
authorFix.url = template(url, args);
|
||||
}
|
||||
embed.setAuthor(authorFix);
|
||||
}
|
||||
if (footer !== undefined) {
|
||||
let footerFix;
|
||||
if (typeof footer === 'string')
|
||||
footerFix = {
|
||||
text: template(footer, args)
|
||||
};
|
||||
else {
|
||||
const { text, icon } = footer;
|
||||
footerFix = {
|
||||
text: template(text, args)
|
||||
};
|
||||
if (icon !== undefined)
|
||||
footerFix.icon = template(icon, args);
|
||||
}
|
||||
embed.setFooter(footerFix);
|
||||
}
|
||||
if (color !== undefined)
|
||||
embed.setColor(resolveColor(template(color, args)));
|
||||
if (description !== undefined)
|
||||
embed.setDescription(template(bigString(description), args));
|
||||
if (image !== undefined)
|
||||
embed.setImage(template(image, args));
|
||||
if (thumbnail !== undefined)
|
||||
embed.setThumbnail(template(thumbnail, args));
|
||||
if (title !== undefined)
|
||||
embed.setTitle(template(title, args));
|
||||
if (url !== undefined)
|
||||
embed.setURL(template(url, args));
|
||||
if (timestamp === true)
|
||||
embed.setTimestamp();
|
||||
else if (typeof timestamp === 'string')
|
||||
embed.setTimestamp(new Date(template(timestamp, args)));
|
||||
else if (timestamp !== false)
|
||||
embed.setTimestamp(timestamp);
|
||||
fields?.forEach(field => {
|
||||
embed.addField(template(field.name, args), template(bigString(field.value), args), field.inline);
|
||||
});
|
||||
return embed;
|
||||
}
|
||||
/* LANG */
|
||||
const lang_1 = require("./util/lang");
|
||||
const LANG = {
|
||||
en: {
|
||||
main: {
|
||||
@@ -190,9 +97,9 @@ function get(id, args = {}) {
|
||||
if (key in finding) {
|
||||
const found = finding[key];
|
||||
if (typeof found === 'string')
|
||||
return template(found, args);
|
||||
return (0, lang_1.template)(found, args);
|
||||
if (found.embed === true)
|
||||
return embedObjStr(found, id);
|
||||
return (0, lang_1.embedObjStr)(found, id);
|
||||
finding = found;
|
||||
}
|
||||
else
|
||||
@@ -219,11 +126,11 @@ function getEmbed(id, args = {}, otherOptions = {}) {
|
||||
if (key in finding) {
|
||||
const found = finding[key];
|
||||
if (typeof found === 'string') {
|
||||
embedData.content = template(found, args);
|
||||
embedData.content = (0, lang_1.template)(found, args);
|
||||
break;
|
||||
}
|
||||
if (found.embed === true) {
|
||||
const embedObj = found, { content } = embedObj, embed = embedObjEmbed(embedObj, args);
|
||||
const embedObj = found, { content } = embedObj, embed = (0, lang_1.embedObjEmbed)(embedObj, args);
|
||||
embedData.embeds.push(embed);
|
||||
if (content !== undefined)
|
||||
embedData.content = content;
|
||||
|
||||
21
dist/queue.js
vendored
21
dist/queue.js
vendored
@@ -26,9 +26,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.QueueCommands = exports.queueContains = exports.discordInit = exports.queueRemove = exports.queueCreate = void 0;
|
||||
const discord_js_1 = require("discord.js");
|
||||
const fs = __importStar(require("fs"));
|
||||
const util_1 = require("./util");
|
||||
const Lang = __importStar(require("./lang"));
|
||||
//load queues from file
|
||||
const discord_1 = require("./util/discord");
|
||||
const main_1 = require("./util/main");
|
||||
//load queues from file`
|
||||
if (!fs.existsSync('./queues.json'))
|
||||
fs.writeFileSync('./queues.json', '{}');
|
||||
const _QUEUE = fs.readFileSync('./queues.json').toString(), QUEUE = new Map();
|
||||
@@ -101,7 +102,7 @@ exports.discordInit = discordInit;
|
||||
function getInfo(interaction) {
|
||||
const info = QUEUE.get(interaction.channelId);
|
||||
if (!info)
|
||||
throw (0, util_1.emsg)('discord.noQueue');
|
||||
throw (0, main_1.emsg)('discord.noQueue');
|
||||
return info;
|
||||
}
|
||||
/**
|
||||
@@ -111,8 +112,8 @@ function getInfo(interaction) {
|
||||
* @returns object containing each
|
||||
*/
|
||||
const getAll = (interaction) => ({
|
||||
member: (0, util_1.getMember)(interaction),
|
||||
channel: (0, util_1.getChannel)(interaction),
|
||||
member: (0, discord_1.getMember)(interaction),
|
||||
channel: (0, discord_1.getChannel)(interaction),
|
||||
info: getInfo(interaction)
|
||||
});
|
||||
/**
|
||||
@@ -133,11 +134,11 @@ exports.queueContains = queueContains;
|
||||
* @throws errorMessage class if it cannot be left
|
||||
*/
|
||||
function open(interaction) {
|
||||
(0, util_1.memberIsModThrow)(interaction);
|
||||
(0, discord_1.memberIsModThrow)(interaction);
|
||||
const { channelId } = interaction, teamsize = interaction.options.getInteger('teamsize', true);
|
||||
const existing = QUEUE.get(channelId);
|
||||
if (existing)
|
||||
throw (0, util_1.emsg)(Lang.get('error.discord.noCreate', {
|
||||
throw (0, main_1.emsg)(Lang.get('error.discord.noCreate', {
|
||||
teamsize: existing.teamsize.toString()
|
||||
}));
|
||||
queueCreate(channelId, teamsize);
|
||||
@@ -151,7 +152,7 @@ function open(interaction) {
|
||||
* @throws errorMessage class if it cannot be joined
|
||||
*/
|
||||
async function close(interaction) {
|
||||
(0, util_1.memberIsModThrow)(interaction);
|
||||
(0, discord_1.memberIsModThrow)(interaction);
|
||||
QUEUE.delete(interaction.channelId);
|
||||
await interaction.reply(Lang.get('discord.close'));
|
||||
}
|
||||
@@ -177,7 +178,7 @@ async function queue(interaction) {
|
||||
async function join(interaction) {
|
||||
const { member, info, channel } = getAll(interaction);
|
||||
if (queueContains(interaction))
|
||||
throw (0, util_1.emsg)('discord.inQueue');
|
||||
throw (0, main_1.emsg)('discord.inQueue');
|
||||
info.players.push(member);
|
||||
QUEUE.set(interaction.channelId, info);
|
||||
await interaction.reply(Lang.get('discord.join'));
|
||||
@@ -191,7 +192,7 @@ async function join(interaction) {
|
||||
async function leave(interaction) {
|
||||
const { member, info } = getAll(interaction);
|
||||
if (!queueContains(interaction))
|
||||
throw (0, util_1.emsg)('discord.notInQueue');
|
||||
throw (0, main_1.emsg)('discord.notInQueue');
|
||||
info.players.splice(info.players.indexOf(member), 1);
|
||||
QUEUE.set(interaction.channelId, info);
|
||||
await interaction.reply(Lang.get('discord.leave'));
|
||||
|
||||
41
dist/util/discord.js
vendored
Normal file
41
dist/util/discord.js
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.memberIsModThrow = exports.memberIsMod = exports.getChannel = exports.getMember = void 0;
|
||||
const discord_js_1 = require("discord.js");
|
||||
const main_1 = require("./main");
|
||||
/**
|
||||
* get the GuildMember of an interaction
|
||||
* @param interaction
|
||||
* @throws errorMessage class if it cannot be read
|
||||
* @returns member
|
||||
*/
|
||||
function getMember(interaction) {
|
||||
const member = interaction.member;
|
||||
if (!(member instanceof discord_js_1.GuildMember))
|
||||
throw (0, main_1.emsg)('general.noMember');
|
||||
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) {
|
||||
const channel = interaction.channel;
|
||||
if (!(channel instanceof discord_js_1.TextChannel))
|
||||
throw (0, main_1.emsg)('general.noChannel');
|
||||
return channel;
|
||||
}
|
||||
exports.getChannel = getChannel;
|
||||
function memberIsMod(interaction) {
|
||||
const member = getMember(interaction);
|
||||
return member.permissionsIn(interaction.channelId).has('MANAGE_MESSAGES');
|
||||
}
|
||||
exports.memberIsMod = memberIsMod;
|
||||
function memberIsModThrow(interaction) {
|
||||
if (!memberIsMod(interaction))
|
||||
throw (0, main_1.emsg)('discord.notMod');
|
||||
}
|
||||
exports.memberIsModThrow = memberIsModThrow;
|
||||
100
dist/util/lang.js
vendored
Normal file
100
dist/util/lang.js
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.embedObjEmbed = exports.embedObjStr = exports.resolveColor = exports.bigString = exports.template = void 0;
|
||||
const discord_js_1 = require("discord.js");
|
||||
function template(str, args) {
|
||||
return str.replace(/{\w+}/g, str => {
|
||||
const key = str.substring(1, str.length - 1);
|
||||
if (key in args)
|
||||
return args[key];
|
||||
return key;
|
||||
});
|
||||
}
|
||||
exports.template = template;
|
||||
function bigString(str) {
|
||||
if (typeof str === 'object')
|
||||
return str.join('\n');
|
||||
return str;
|
||||
}
|
||||
exports.bigString = bigString;
|
||||
function resolveColor(color) {
|
||||
color = color.replace(/[^0-9a-f]/gi, '');
|
||||
const colorNum = [0, 0, 0];
|
||||
if (color.length === 3 || color.length === 6) {
|
||||
const colorSpl = /([0-9a-f]{1,2})([0-9a-f]{1,2})([0-9a-f]{1,2})/.exec(color);
|
||||
if (!colorSpl)
|
||||
return colorNum;
|
||||
for (let i = 0; i < colorSpl.length && i < colorNum.length; i++)
|
||||
colorNum[i] = parseInt(colorSpl[i], 16);
|
||||
}
|
||||
return colorNum;
|
||||
}
|
||||
exports.resolveColor = resolveColor;
|
||||
function embedObjStr(embedData, fallback = '') {
|
||||
if (embedData.content !== undefined)
|
||||
return bigString(embedData.content);
|
||||
if (embedData.description !== undefined)
|
||||
return bigString(embedData.description);
|
||||
return fallback;
|
||||
}
|
||||
exports.embedObjStr = embedObjStr;
|
||||
function embedObjEmbed(embedObj, args = {}) {
|
||||
const embed = new discord_js_1.MessageEmbed(), { author, color, description, fields, footer, image, thumbnail, timestamp, title, url } = embedObj;
|
||||
if (author !== undefined) {
|
||||
let authorFix;
|
||||
if (typeof author === 'string')
|
||||
authorFix = {
|
||||
name: template(author, args)
|
||||
};
|
||||
else {
|
||||
const { name, icon, url } = author;
|
||||
authorFix = {
|
||||
name: template(name, args)
|
||||
};
|
||||
if (icon !== undefined)
|
||||
authorFix.icon = template(icon, args);
|
||||
if (url !== undefined)
|
||||
authorFix.url = template(url, args);
|
||||
}
|
||||
embed.setAuthor(authorFix);
|
||||
}
|
||||
if (footer !== undefined) {
|
||||
let footerFix;
|
||||
if (typeof footer === 'string')
|
||||
footerFix = {
|
||||
text: template(footer, args)
|
||||
};
|
||||
else {
|
||||
const { text, icon } = footer;
|
||||
footerFix = {
|
||||
text: template(text, args)
|
||||
};
|
||||
if (icon !== undefined)
|
||||
footerFix.icon = template(icon, args);
|
||||
}
|
||||
embed.setFooter(footerFix);
|
||||
}
|
||||
if (color !== undefined)
|
||||
embed.setColor(resolveColor(template(color, args)));
|
||||
if (description !== undefined)
|
||||
embed.setDescription(template(bigString(description), args));
|
||||
if (image !== undefined)
|
||||
embed.setImage(template(image, args));
|
||||
if (thumbnail !== undefined)
|
||||
embed.setThumbnail(template(thumbnail, args));
|
||||
if (title !== undefined)
|
||||
embed.setTitle(template(title, args));
|
||||
if (url !== undefined)
|
||||
embed.setURL(template(url, args));
|
||||
if (timestamp === true)
|
||||
embed.setTimestamp();
|
||||
else if (typeof timestamp === 'string')
|
||||
embed.setTimestamp(new Date(template(timestamp, args)));
|
||||
else if (timestamp !== false)
|
||||
embed.setTimestamp(timestamp);
|
||||
fields?.forEach(field => {
|
||||
embed.addField(template(field.name, args), template(bigString(field.value), args), field.inline);
|
||||
});
|
||||
return embed;
|
||||
}
|
||||
exports.embedObjEmbed = embedObjEmbed;
|
||||
60
dist/util/main.js
vendored
Normal file
60
dist/util/main.js
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
"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]; } });
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.emsg = exports.errorMessage = exports.shuffle = void 0;
|
||||
const Lang = __importStar(require("../lang"));
|
||||
/**
|
||||
* shuffles an array
|
||||
* https://stackoverflow.com/a/2450976/2856416
|
||||
* @param array an array
|
||||
* @returns an array but shuffled
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function shuffle(array) {
|
||||
let currentIndex = array.length, randomIndex;
|
||||
// While there remain elements to shuffle...
|
||||
while (currentIndex != 0) {
|
||||
// Pick a remaining element...
|
||||
randomIndex = Math.floor(Math.random() * currentIndex);
|
||||
currentIndex--;
|
||||
// And swap it with the current element.
|
||||
[array[currentIndex], array[randomIndex]] = [
|
||||
array[randomIndex], array[currentIndex]
|
||||
];
|
||||
}
|
||||
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(Lang.get(`error.${msg}`), ephemeral);
|
||||
exports.emsg = emsg;
|
||||
17
src/api.ts
17
src/api.ts
@@ -3,8 +3,9 @@ import * as cheerio from 'cheerio';
|
||||
import { CommandInteraction } from 'discord.js';
|
||||
import { IncomingMessage } from 'http';
|
||||
import http from 'https';
|
||||
import { emsg } from './util';
|
||||
import { emsg } from './util/main';
|
||||
import * as Lang from './lang';
|
||||
import { uniteApiData } from './types/api';
|
||||
|
||||
const uniteApiRegex = {
|
||||
//$1 = name, $2 = id
|
||||
@@ -16,20 +17,6 @@ const uniteApiRegex = {
|
||||
]
|
||||
};
|
||||
|
||||
type uniteApiData = {
|
||||
name: string,
|
||||
id: string,
|
||||
avatar: string,
|
||||
|
||||
level: string,
|
||||
rank: string,
|
||||
class: string|null,
|
||||
elo: string|null,
|
||||
battles: string,
|
||||
wins: string,
|
||||
winrate: string
|
||||
}
|
||||
|
||||
/**
|
||||
* gets the html of the uniteApi page for the player
|
||||
* @param name name of player
|
||||
|
||||
@@ -5,7 +5,7 @@ import { getPlayerInteraction } from './api';
|
||||
import { registerCommands } from './discord';
|
||||
import * as Lang from './lang';
|
||||
import { discordInit, QueueCommands } from './queue';
|
||||
import { errorMessage } from './util';
|
||||
import { errorMessage } from './util/main';
|
||||
const CLIENT = new Client({ intents: [Intents.FLAGS.GUILDS] });
|
||||
|
||||
//init logs with a timestamp
|
||||
|
||||
199
src/lang.ts
199
src/lang.ts
@@ -1,199 +1,4 @@
|
||||
import { MessageEmbed } from 'discord.js';
|
||||
|
||||
/* TYPES */
|
||||
|
||||
//this is a generic type, and needs 'any'
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type basicObject = {[keys: string]: any};
|
||||
type basicObjectStr = {[keys: string]: string};
|
||||
|
||||
type URL = string;
|
||||
|
||||
type bigString = string | string[];
|
||||
interface embedData {
|
||||
content?: string,
|
||||
embeds: MessageEmbed[]
|
||||
}
|
||||
interface embedField {
|
||||
name: string,
|
||||
value: bigString,
|
||||
inline?: boolean
|
||||
}
|
||||
|
||||
interface authorData {
|
||||
name: string,
|
||||
url?: string,
|
||||
icon?: string
|
||||
}
|
||||
interface footerData {
|
||||
text: string,
|
||||
icon?: string
|
||||
}
|
||||
interface embedObj {
|
||||
embed: true,
|
||||
|
||||
content?: string,
|
||||
title?: string,
|
||||
description?: bigString,
|
||||
url?: URL,
|
||||
color?: string,
|
||||
footer?: string | footerData,
|
||||
thumbnail?: URL,
|
||||
image?: URL,
|
||||
author?: string | authorData,
|
||||
fields?: embedField[],
|
||||
timestamp?: boolean | string | number
|
||||
}
|
||||
|
||||
type LangObj = { [keys:string]: LangObj | embedObj | string }
|
||||
type LangObjWhole = { [langid:string]: LangObj }
|
||||
|
||||
/* UTIL FUNCS */
|
||||
|
||||
function template(str: string, args: basicObject): string {
|
||||
|
||||
return str.replace(/{\w+}/g, str => {
|
||||
|
||||
const key = str.substring(1, str.length-1);
|
||||
|
||||
if (key in args)
|
||||
return args[key];
|
||||
|
||||
return key;
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function bigString(str: bigString): string {
|
||||
if (typeof str === 'object')
|
||||
return str.join('\n');
|
||||
return str;
|
||||
}
|
||||
|
||||
function resolveColor(color: string): [number, number, number] {
|
||||
color = color.replace(/[^0-9a-f]/gi, '');
|
||||
|
||||
const colorNum: [number, number, number] = [0, 0, 0];
|
||||
|
||||
if (color.length === 3 || color.length === 6) {
|
||||
|
||||
const colorSpl = /([0-9a-f]{1,2})([0-9a-f]{1,2})([0-9a-f]{1,2})/.exec(color);
|
||||
|
||||
if (!colorSpl)
|
||||
return colorNum;
|
||||
|
||||
for (let i = 0; i < colorSpl.length && i < colorNum.length; i++)
|
||||
colorNum[i] = parseInt(colorSpl[i], 16);
|
||||
|
||||
}
|
||||
|
||||
return colorNum;
|
||||
}
|
||||
|
||||
function embedObjStr(embedData: embedObj, fallback = ''): string {
|
||||
|
||||
if (embedData.content !== undefined)
|
||||
return bigString(embedData.content);
|
||||
|
||||
if (embedData.description !== undefined)
|
||||
return bigString(embedData.description);
|
||||
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function embedObjEmbed(embedObj: embedObj, args: basicObjectStr = {}): MessageEmbed {
|
||||
const embed = new MessageEmbed(),
|
||||
{ author, color, description, fields, footer, image, thumbnail, timestamp, title, url } = embedObj;
|
||||
|
||||
if (author !== undefined) {
|
||||
|
||||
let authorFix: authorData;
|
||||
|
||||
if (typeof author === 'string')
|
||||
authorFix = {
|
||||
name: template(author, args)
|
||||
};
|
||||
else {
|
||||
|
||||
const {name, icon, url} = author;
|
||||
|
||||
authorFix = {
|
||||
name: template(name, args)
|
||||
};
|
||||
|
||||
if (icon !== undefined)
|
||||
authorFix.icon = template(icon, args);
|
||||
|
||||
if (url !== undefined)
|
||||
authorFix.url = template(url, args);
|
||||
|
||||
}
|
||||
|
||||
embed.setAuthor(authorFix);
|
||||
|
||||
}
|
||||
|
||||
if (footer !== undefined) {
|
||||
|
||||
let footerFix: footerData;
|
||||
|
||||
if (typeof footer === 'string')
|
||||
footerFix = {
|
||||
text: template(footer, args)
|
||||
};
|
||||
else {
|
||||
|
||||
const {text, icon} = footer;
|
||||
|
||||
footerFix = {
|
||||
text: template(text, args)
|
||||
};
|
||||
|
||||
if (icon !== undefined)
|
||||
footerFix.icon = template(icon, args);
|
||||
|
||||
}
|
||||
|
||||
embed.setFooter(footerFix);
|
||||
|
||||
}
|
||||
|
||||
if (color !== undefined)
|
||||
embed.setColor(resolveColor(template(color, args)));
|
||||
|
||||
if (description !== undefined)
|
||||
embed.setDescription(template(bigString(description), args));
|
||||
|
||||
if (image !== undefined)
|
||||
embed.setImage(template(image, args));
|
||||
|
||||
if (thumbnail !== undefined)
|
||||
embed.setThumbnail(template(thumbnail, args));
|
||||
|
||||
if (title !== undefined)
|
||||
embed.setTitle(template(title, args));
|
||||
|
||||
if (url !== undefined)
|
||||
embed.setURL(template(url, args));
|
||||
|
||||
if (timestamp === true)
|
||||
embed.setTimestamp();
|
||||
else if (typeof timestamp === 'string')
|
||||
embed.setTimestamp(new Date(template(timestamp, args)));
|
||||
else if (timestamp !== false)
|
||||
embed.setTimestamp(timestamp);
|
||||
|
||||
fields?.forEach(field => {
|
||||
embed.addField(template(field.name, args), template(bigString(field.value), args), field.inline);
|
||||
});
|
||||
|
||||
return embed;
|
||||
}
|
||||
|
||||
/* LANG */
|
||||
|
||||
|
||||
import { embedObjEmbed, embedObjStr, template } from './util/lang';
|
||||
|
||||
const LANG: LangObjWhole = {
|
||||
|
||||
@@ -378,5 +183,3 @@ export function getEmbed(id: string, args: basicObjectStr = {}, otherOptions: ba
|
||||
|
||||
return embedData;
|
||||
}
|
||||
|
||||
debugger;
|
||||
|
||||
@@ -5,10 +5,12 @@ join message should contain your current position in the queue, editing it to ke
|
||||
|
||||
import { Client, CommandInteraction, TextChannel } from 'discord.js';
|
||||
import * as fs from 'fs';
|
||||
import { emsg, getChannel, getMember, memberIsModThrow, queueInfo, queueInfoBase } from './util';
|
||||
import * as Lang from './lang';
|
||||
import { queueInfo, queueInfoBase } from './types/queue';
|
||||
import { getChannel, getMember, memberIsModThrow } from './util/discord';
|
||||
import { emsg } from './util/main';
|
||||
|
||||
//load queues from file
|
||||
//load queues from file`
|
||||
if (!fs.existsSync('./queues.json'))
|
||||
fs.writeFileSync('./queues.json', '{}');
|
||||
|
||||
|
||||
13
src/types/api.d.ts
vendored
Normal file
13
src/types/api.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
export interface uniteApiData {
|
||||
name: string,
|
||||
id: string,
|
||||
avatar: string,
|
||||
|
||||
level: string,
|
||||
rank: string,
|
||||
class: string|null,
|
||||
elo: string|null,
|
||||
battles: string,
|
||||
wins: string,
|
||||
winrate: string
|
||||
}
|
||||
55
src/types/lang.d.ts
vendored
Normal file
55
src/types/lang.d.ts
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
//this is a generic type, and needs 'any'
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type basicObject = {[keys: string]: any};
|
||||
type basicObjectStr = {[keys: string]: string};
|
||||
|
||||
type bigString = string | string[];
|
||||
interface embedData {
|
||||
content?: string,
|
||||
embeds: MessageEmbed[]
|
||||
}
|
||||
interface embedField {
|
||||
name: string,
|
||||
value: bigString,
|
||||
inline?: boolean
|
||||
}
|
||||
|
||||
interface authorData {
|
||||
name: string,
|
||||
url?: string,
|
||||
icon?: string
|
||||
}
|
||||
interface footerData {
|
||||
text: string,
|
||||
icon?: string
|
||||
}
|
||||
interface embedObj {
|
||||
embed: true,
|
||||
|
||||
content?: string,
|
||||
title?: string,
|
||||
description?: bigString,
|
||||
/**
|
||||
* URL
|
||||
*/
|
||||
url?: string,
|
||||
/**
|
||||
* #FFFFFF
|
||||
*/
|
||||
color?: string,
|
||||
footer?: string | footerData,
|
||||
thumbnail?: string,
|
||||
/**
|
||||
* URL
|
||||
*/
|
||||
image?: string,
|
||||
/**
|
||||
* URL
|
||||
*/
|
||||
author?: string | authorData,
|
||||
fields?: embedField[],
|
||||
timestamp?: boolean | string | number
|
||||
}
|
||||
|
||||
type LangObj = { [keys:string]: LangObj | embedObj | string }
|
||||
type LangObjWhole = { [langid:string]: LangObj }
|
||||
8
src/types/queue.d.ts
vendored
Normal file
8
src/types/queue.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import { GuildMember } from 'discord.js';
|
||||
|
||||
export interface queueInfoBase {
|
||||
teamsize: number
|
||||
}
|
||||
export interface queueInfo extends queueInfoBase {
|
||||
players: GuildMember[]
|
||||
}
|
||||
97
src/util.ts
97
src/util.ts
@@ -1,97 +0,0 @@
|
||||
import { CommandInteraction, GuildMember, TextChannel } from 'discord.js';
|
||||
import * as Lang from './lang';
|
||||
|
||||
/**
|
||||
* shuffles an array
|
||||
* https://stackoverflow.com/a/2450976/2856416
|
||||
* @param array an array
|
||||
* @returns an array but shuffled
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function shuffle(array: any[]) {
|
||||
let currentIndex = array.length, randomIndex;
|
||||
|
||||
// While there remain elements to shuffle...
|
||||
while (currentIndex != 0) {
|
||||
|
||||
// Pick a remaining element...
|
||||
randomIndex = Math.floor(Math.random() * currentIndex);
|
||||
currentIndex--;
|
||||
|
||||
// And swap it with the current element.
|
||||
[array[currentIndex], array[randomIndex]] = [
|
||||
array[randomIndex], array[currentIndex]];
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class errorMessage {
|
||||
public msg: string;
|
||||
public ephemeral: boolean;
|
||||
|
||||
constructor(msg: string, ephemeral = 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 = true) => new errorMessage(Lang.get(`error.${msg}`), ephemeral);
|
||||
|
||||
|
||||
|
||||
export interface queueInfoBase {
|
||||
teamsize: number
|
||||
}
|
||||
export interface queueInfo extends queueInfoBase{
|
||||
players: GuildMember[]
|
||||
}
|
||||
|
||||
/**
|
||||
* get the GuildMember of an interaction
|
||||
* @param interaction
|
||||
* @throws errorMessage class if it cannot be read
|
||||
* @returns member
|
||||
*/
|
||||
export function getMember(interaction: CommandInteraction): GuildMember {
|
||||
const member = interaction.member;
|
||||
|
||||
if (!(member instanceof GuildMember))
|
||||
throw emsg('general.noMember');
|
||||
|
||||
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 {
|
||||
const channel = interaction.channel;
|
||||
|
||||
if (!(channel instanceof TextChannel))
|
||||
throw emsg('general.noChannel');
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
|
||||
export function memberIsMod(interaction: CommandInteraction): boolean {
|
||||
const member = getMember(interaction);
|
||||
return member.permissionsIn(interaction.channelId).has('MANAGE_MESSAGES');
|
||||
}
|
||||
|
||||
export function memberIsModThrow(interaction: CommandInteraction) {
|
||||
if (!memberIsMod(interaction))
|
||||
throw emsg('discord.notMod');
|
||||
}
|
||||
43
src/util/discord.ts
Normal file
43
src/util/discord.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { CommandInteraction, GuildMember, TextChannel } from 'discord.js';
|
||||
import { emsg } from './main';
|
||||
|
||||
/**
|
||||
* get the GuildMember of an interaction
|
||||
* @param interaction
|
||||
* @throws errorMessage class if it cannot be read
|
||||
* @returns member
|
||||
*/
|
||||
export function getMember(interaction: CommandInteraction): GuildMember {
|
||||
const member = interaction.member;
|
||||
|
||||
if (!(member instanceof GuildMember))
|
||||
throw emsg('general.noMember');
|
||||
|
||||
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 {
|
||||
const channel = interaction.channel;
|
||||
|
||||
if (!(channel instanceof TextChannel))
|
||||
throw emsg('general.noChannel');
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
|
||||
export function memberIsMod(interaction: CommandInteraction): boolean {
|
||||
const member = getMember(interaction);
|
||||
return member.permissionsIn(interaction.channelId).has('MANAGE_MESSAGES');
|
||||
}
|
||||
|
||||
export function memberIsModThrow(interaction: CommandInteraction) {
|
||||
if (!memberIsMod(interaction))
|
||||
throw emsg('discord.notMod');
|
||||
}
|
||||
142
src/util/lang.ts
Normal file
142
src/util/lang.ts
Normal file
@@ -0,0 +1,142 @@
|
||||
import { MessageEmbed } from 'discord.js';
|
||||
|
||||
export function template(str: string, args: basicObject): string {
|
||||
|
||||
return str.replace(/{\w+}/g, str => {
|
||||
|
||||
const key = str.substring(1, str.length-1);
|
||||
|
||||
if (key in args)
|
||||
return args[key];
|
||||
|
||||
return key;
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
export function bigString(str: bigString): string {
|
||||
if (typeof str === 'object')
|
||||
return str.join('\n');
|
||||
return str;
|
||||
}
|
||||
|
||||
export function resolveColor(color: string): [number, number, number] {
|
||||
color = color.replace(/[^0-9a-f]/gi, '');
|
||||
|
||||
const colorNum: [number, number, number] = [0, 0, 0];
|
||||
|
||||
if (color.length === 3 || color.length === 6) {
|
||||
|
||||
const colorSpl = /([0-9a-f]{1,2})([0-9a-f]{1,2})([0-9a-f]{1,2})/.exec(color);
|
||||
|
||||
if (!colorSpl)
|
||||
return colorNum;
|
||||
|
||||
for (let i = 0; i < colorSpl.length && i < colorNum.length; i++)
|
||||
colorNum[i] = parseInt(colorSpl[i], 16);
|
||||
|
||||
}
|
||||
|
||||
return colorNum;
|
||||
}
|
||||
|
||||
export function embedObjStr(embedData: embedObj, fallback = ''): string {
|
||||
|
||||
if (embedData.content !== undefined)
|
||||
return bigString(embedData.content);
|
||||
|
||||
if (embedData.description !== undefined)
|
||||
return bigString(embedData.description);
|
||||
|
||||
return fallback;
|
||||
}
|
||||
|
||||
export function embedObjEmbed(embedObj: embedObj, args: basicObjectStr = {}): MessageEmbed {
|
||||
const embed = new MessageEmbed(),
|
||||
{ author, color, description, fields, footer, image, thumbnail, timestamp, title, url } = embedObj;
|
||||
|
||||
if (author !== undefined) {
|
||||
|
||||
let authorFix: authorData;
|
||||
|
||||
if (typeof author === 'string')
|
||||
authorFix = {
|
||||
name: template(author, args)
|
||||
};
|
||||
else {
|
||||
|
||||
const {name, icon, url} = author;
|
||||
|
||||
authorFix = {
|
||||
name: template(name, args)
|
||||
};
|
||||
|
||||
if (icon !== undefined)
|
||||
authorFix.icon = template(icon, args);
|
||||
|
||||
if (url !== undefined)
|
||||
authorFix.url = template(url, args);
|
||||
|
||||
}
|
||||
|
||||
embed.setAuthor(authorFix);
|
||||
|
||||
}
|
||||
|
||||
if (footer !== undefined) {
|
||||
|
||||
let footerFix: footerData;
|
||||
|
||||
if (typeof footer === 'string')
|
||||
footerFix = {
|
||||
text: template(footer, args)
|
||||
};
|
||||
else {
|
||||
|
||||
const {text, icon} = footer;
|
||||
|
||||
footerFix = {
|
||||
text: template(text, args)
|
||||
};
|
||||
|
||||
if (icon !== undefined)
|
||||
footerFix.icon = template(icon, args);
|
||||
|
||||
}
|
||||
|
||||
embed.setFooter(footerFix);
|
||||
|
||||
}
|
||||
|
||||
if (color !== undefined)
|
||||
embed.setColor(resolveColor(template(color, args)));
|
||||
|
||||
if (description !== undefined)
|
||||
embed.setDescription(template(bigString(description), args));
|
||||
|
||||
if (image !== undefined)
|
||||
embed.setImage(template(image, args));
|
||||
|
||||
if (thumbnail !== undefined)
|
||||
embed.setThumbnail(template(thumbnail, args));
|
||||
|
||||
if (title !== undefined)
|
||||
embed.setTitle(template(title, args));
|
||||
|
||||
if (url !== undefined)
|
||||
embed.setURL(template(url, args));
|
||||
|
||||
if (timestamp === true)
|
||||
embed.setTimestamp();
|
||||
else if (typeof timestamp === 'string')
|
||||
embed.setTimestamp(new Date(template(timestamp, args)));
|
||||
else if (timestamp !== false)
|
||||
embed.setTimestamp(timestamp);
|
||||
|
||||
fields?.forEach(field => {
|
||||
embed.addField(template(field.name, args), template(bigString(field.value), args), field.inline);
|
||||
});
|
||||
|
||||
return embed;
|
||||
}
|
||||
47
src/util/main.ts
Normal file
47
src/util/main.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import * as Lang from '../lang';
|
||||
|
||||
/**
|
||||
* shuffles an array
|
||||
* https://stackoverflow.com/a/2450976/2856416
|
||||
* @param array an array
|
||||
* @returns an array but shuffled
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function shuffle(array: any[]) {
|
||||
let currentIndex = array.length, randomIndex;
|
||||
|
||||
// While there remain elements to shuffle...
|
||||
while (currentIndex != 0) {
|
||||
|
||||
// Pick a remaining element...
|
||||
randomIndex = Math.floor(Math.random() * currentIndex);
|
||||
currentIndex--;
|
||||
|
||||
// And swap it with the current element.
|
||||
[array[currentIndex], array[randomIndex]] = [
|
||||
array[randomIndex], array[currentIndex]];
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class errorMessage {
|
||||
public msg: string;
|
||||
public ephemeral: boolean;
|
||||
|
||||
constructor(msg: string, ephemeral = 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 = true) => new errorMessage(Lang.get(`error.${msg}`), ephemeral);
|
||||
|
||||
11
tsconfig.eslint.json
Normal file
11
tsconfig.eslint.json
Normal file
@@ -0,0 +1,11 @@
|
||||
// Special typescript project file, used by eslint only.
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"include": [
|
||||
// repeated from base config's "include" setting
|
||||
"src",
|
||||
|
||||
// these are the eslint-only inclusions
|
||||
".eslintrc.json",
|
||||
]
|
||||
}
|
||||
@@ -11,5 +11,11 @@
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
}
|
||||
"typeRoots": [
|
||||
"./src/types/"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user