Compare commits
36 Commits
dev-new-re
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb6aaaff3f | ||
|
|
265f4a7864 | ||
|
|
284c015509 | ||
|
|
e010a25c72 | ||
|
|
4595ea9f07 | ||
|
|
b08fd54288 | ||
|
|
324f008fbf | ||
| 111cf4aead | |||
| 1eab3e7853 | |||
| d4a9c4543a | |||
| f5837c4b25 | |||
| 36de2c1715 | |||
| 4d72f8562e | |||
| 2f63f172c2 | |||
| 7962266b26 | |||
| 6edc6771ef | |||
| 596a6b5dc5 | |||
| de37b9d348 | |||
| 643c027ce8 | |||
| 0c232a55cb | |||
| d7a99968bc | |||
| b099c29ecf | |||
| aaaac430e6 | |||
| 56a76b0fbb | |||
| e50449ec03 | |||
| 2a4b58f7af | |||
| e1cc82eb32 | |||
| 6d35019e3e | |||
| 490db1c70f | |||
| 21ccd3a3d1 | |||
| a33a7d737e | |||
| 5a31ef95fb | |||
| f2ac0ed10e | |||
| 6d86799e02 | |||
| 189e126983 | |||
| d99b8ab4d6 |
@@ -10,3 +10,7 @@ DB_PORT=
|
||||
API_PORT=3080
|
||||
API_ACCESS_TOKEN=
|
||||
HOMEPAGE_URL=
|
||||
|
||||
#Rendering
|
||||
JOSE_ENDPOINT=
|
||||
ASSET_URL=
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,7 @@
|
||||
assets/image_cache
|
||||
assets/cards
|
||||
assets/import
|
||||
assets/userdata
|
||||
|
||||
### Visual Studio Code ###
|
||||
.vscode
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
FROM node:16.9.0-alpine
|
||||
RUN apk add --no-cache imagemagick
|
||||
@@ -1,25 +0,0 @@
|
||||
# API Documentation
|
||||
|
||||
## Introduction
|
||||
|
||||
This API provides CRU (Create, Read, Update) operations for various models.
|
||||
|
||||
The API requires an API key for some operations.
|
||||
|
||||
The endpoints provided by this API are:
|
||||
|
||||
## Generic:
|
||||
- / - List all routes (JSON Response)
|
||||
- /ping - Returns pong
|
||||
- /stats - Simple stats about the bot (Record counts and uptime)
|
||||
- /most-recent-drop - Returns the most recent drop (Requires API Key)
|
||||
|
||||
## For characters:
|
||||
- [`/characters`](characters.md#get-characters)
|
||||
- [`/characters/:character_id`](characters.md#get-characterscharacter_id)
|
||||
|
||||
## For groups:
|
||||
_TODO_
|
||||
|
||||
## For Badges:
|
||||
_TODO_
|
||||
@@ -93,4 +93,6 @@ app.use(PREFIX, router);
|
||||
app.use(PREFIX, groupRoutes);
|
||||
app.use(PREFIX, badgeRoutes);
|
||||
app.use(PREFIX, characterRoutes);
|
||||
app.use('/assets', express.static('assets'));
|
||||
|
||||
module.exports = app;
|
||||
|
||||
0
assets/userdata/profiles/.gitkeep
Normal file
0
assets/userdata/profiles/.gitkeep
Normal file
@@ -1,8 +1,9 @@
|
||||
const { SlashCommandBuilder, ComponentType, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js");
|
||||
const { customAlphabet } = require("nanoid");
|
||||
const { Card, User, Wishlist, Character } = require("../models");
|
||||
const { UserUtils, CardUtils, GeneralUtils } = require("../util");
|
||||
const { customAlphabet, random } = require("nanoid");
|
||||
const { Card, User, Wishlist, Character, sequelize } = require("../models");
|
||||
const { UserUtils, CardUtils, GeneralUtils, Rendering } = require("../util");
|
||||
const { PATREON } = require("../config/constants");
|
||||
const axios = require('axios').default
|
||||
const stores = require("../stores");
|
||||
require('dotenv').config();
|
||||
|
||||
@@ -28,6 +29,7 @@ module.exports = {
|
||||
{ name: 'toggle_maintenance', value: 'toggle_maintenance' },
|
||||
{ name: 'store', value: 'store' },
|
||||
{ name: 'wishlist', value: 'wishlist' },
|
||||
{ name: 'rendering', value: 'rendering' },
|
||||
{ name: 'patreon', value: 'patreon' }
|
||||
)
|
||||
)
|
||||
@@ -181,6 +183,77 @@ module.exports = {
|
||||
|
||||
let patreon = await UserUtils.getPatreonPerks(interaction.client, extUser ? extUser : user);
|
||||
interaction.channel.send(JSON.stringify(patreon));
|
||||
break;
|
||||
case "rendering":
|
||||
const row = new ActionRowBuilder();
|
||||
row.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setCustomId(`testbatch`)
|
||||
.setLabel(`Render test batch`)
|
||||
.setStyle(ButtonStyle.Primary),
|
||||
);
|
||||
interaction.editReply({
|
||||
content: `Jose endpoint: ${process.env.JOSE_ENDPOINT}\n Asset URL: ${process.env.ASSET_URL}`,
|
||||
components: [row],
|
||||
ephemeral: false
|
||||
});
|
||||
const filter = (m) => m.user.id === interaction.user.id;
|
||||
const collector = interaction.channel.createMessageComponentCollector({ filter: filter, componentType: ComponentType.Button, time: 60000 });
|
||||
collector.on('collect', async (i) => {
|
||||
switch (i.customId) {
|
||||
case 'testbatch':
|
||||
i.deferUpdate();
|
||||
interaction.channel.send("Beep boop fetching test renders");
|
||||
let testCard = await Card.build({
|
||||
characterId: 1,
|
||||
userId: Math.floor(Math.random() * 10),
|
||||
identifier: "0xffff",
|
||||
quality: 1,
|
||||
printNr: 0,
|
||||
|
||||
});
|
||||
let testCharacter = Character.build({
|
||||
id: 0,
|
||||
groupId: 0,
|
||||
name: "test",
|
||||
imageIdentifier: "azur-lane/akashi.png",
|
||||
enabled: true
|
||||
})
|
||||
|
||||
let testCards = [ { ...testCard},{ ...testCard},{ ...testCard},{ ...testCard},{ ...testCard}, { ...testCard},{ ...testCard},{ ...testCard},{ ...testCard},{ ...testCard} ];
|
||||
let startTime = Date.now();
|
||||
let renderedStack = await Rendering.renderCardStack([testCard, testCard, testCard]);
|
||||
let execTime = Date.now() - startTime;
|
||||
await interaction.channel.send(renderedStack);
|
||||
await interaction.channel.send(`Stack rendering took ${execTime} ms`);
|
||||
|
||||
|
||||
let total = 0;
|
||||
startTime = Date.now()
|
||||
await Promise.all(testCards.map(async card => {
|
||||
console.log(`Iterating card `);
|
||||
card.characterId = (await Character.findAll({where: {enabled: true},order: sequelize.random(),limit: 1}))[0].id;
|
||||
card.id = 0;
|
||||
card.identifier = CardUtils.generateIdentifier();
|
||||
card.userId = 1;
|
||||
let startTime = Date.now();
|
||||
card['render'] = await Rendering.renderCard(card);
|
||||
let execTime = Date.now() - startTime;
|
||||
total += execTime;
|
||||
card['timing'] = `${card.identifier} Card rendering took ${execTime} ms`;
|
||||
}));
|
||||
let toatalExecTime = Date.now() - startTime;
|
||||
|
||||
await interaction.channel.send(testCards.map(card => {return `${card['identifier']} ${card['render']}` }).join('\n'));
|
||||
await interaction.channel.send(testCards.map(card => {return card['timing'] }).join('\n'))
|
||||
|
||||
let joseStats = (await axios.get(`${process.env.JOSE_ENDPOINT}/status`)).data;
|
||||
await interaction.channel.send(`Active Nodes: ${joseStats.nodes.count} Queued Jobs: ${joseStats.jobs.queued.count}`);
|
||||
await interaction.channel.send(`Total time for ${testCards.length} Cards: ${toatalExecTime}\nAverage time per card: ${total / testCards.length}`);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
break;
|
||||
default:
|
||||
interaction.editReply({
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
const sharp = require('sharp');
|
||||
const { SlashCommandBuilder, AttachmentBuilder, EmbedBuilder } = require('discord.js');
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("debugrendering")
|
||||
.setDescription("Debug rendering"),
|
||||
permissionLevel: 2,
|
||||
async execute(interaction) {
|
||||
|
||||
const image = await sharp({
|
||||
create: {
|
||||
width: 900,
|
||||
height: 500,
|
||||
channels: 4,
|
||||
background: { r: 255, g: 0, b: 0, alpha: 0.5 }
|
||||
}
|
||||
})
|
||||
.composite([
|
||||
{ input: './assets/cards/test/test.png', gravity: 'northwest' },
|
||||
{ input: './assets/cards/test/test.png', gravity: 'centre' },
|
||||
{ input: './assets/cards/test/test.png', gravity: 'northeast' },
|
||||
{ input: './assets/overlays/rainbow_overlay.png', gravity: 'northwest' },
|
||||
{ input: './assets/overlays/rainbow_overlay.png', gravity: 'centre' },
|
||||
{ input: './assets/overlays/rainbow_overlay.png', gravity: 'northeast' },
|
||||
])
|
||||
.png()
|
||||
.toBuffer();
|
||||
|
||||
const file = new AttachmentBuilder(image);
|
||||
const message = await interaction.reply({ content: 'asd', files: [file], fetchReply: true });
|
||||
}
|
||||
}
|
||||
@@ -80,7 +80,11 @@ module.exports = {
|
||||
cards.sort((a, b) => a.characterId - b.characterId);
|
||||
|
||||
const row = new ActionRowBuilder();
|
||||
let deckImage = await Rendering.renderCardStack(cards);
|
||||
let deckImage = await Rendering.renderCardStack(cards).catch(async err => {
|
||||
await interaction.channel.send(`Uooh an error! ${err.response?.status} ${err.response?.statusText} \n ${err.response?.data.message} \n ${err.response?.data.jobId}`);
|
||||
});
|
||||
if (!deckImage) { return; }
|
||||
|
||||
let notableProps = [];
|
||||
let pings = [];
|
||||
for (let i = 0; i < cards.length; i++) {
|
||||
@@ -219,7 +223,10 @@ module.exports = {
|
||||
console.log(`Collected ${collected.size} interactions.`);
|
||||
|
||||
|
||||
let deckImage = await Rendering.renderCardStack(cards);
|
||||
let deckImage = await Rendering.renderCardStack(cards).catch(async err => {
|
||||
await interaction.channel.send(`Uooh an error! ${err.response?.status} ${err.response?.statusText} \n ${err.response?.data.message} \n ${err.response?.data.jobId}`);
|
||||
});
|
||||
if (!deckImage){ return; }
|
||||
message.edit({ components: [], files: [new AttachmentBuilder(deckImage)] });
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const { SlashCommandBuilder, ComponentType, TextInputBuilder, TextInputStyle, ActionRowBuilder, ButtonBuilder, ButtonStyle, ModalBuilder } = require("discord.js");
|
||||
const { SlashCommandBuilder, ComponentType, TextInputBuilder, TextInputStyle, ActionRowBuilder, ButtonBuilder, ButtonStyle, ModalBuilder, Attachment } = require("discord.js");
|
||||
const { Card, User, Character } = require("../models");
|
||||
const { UserUtils, ReplyUtils } = require("../util");
|
||||
const { UserUtils, ReplyUtils, GeneralUtils } = require("../util");
|
||||
|
||||
const pageSize = 8;
|
||||
|
||||
@@ -8,12 +8,18 @@ const pageSize = 8;
|
||||
module.exports = {
|
||||
data: new SlashCommandBuilder()
|
||||
.setName("editprofile")
|
||||
.setDescription("Edit your profile"),
|
||||
.setDescription("Edit your profile")
|
||||
.addAttachmentOption((option) =>
|
||||
option
|
||||
.setName("attachement")
|
||||
.setDescription("Attachement to be used")
|
||||
.setRequired(false)
|
||||
),
|
||||
permissionLevel: 0,
|
||||
async execute(interaction) {
|
||||
await interaction.deferReply();
|
||||
let user = await UserUtils.getUserByDiscordId(interaction.member.id);
|
||||
|
||||
let patreon = await UserUtils.getPatreonPerks(interaction.client, user);
|
||||
let profile = await user.getProfile();
|
||||
|
||||
|
||||
@@ -31,6 +37,15 @@ module.exports = {
|
||||
.setStyle(ButtonStyle.Primary)
|
||||
);
|
||||
|
||||
if (patreon.perks?.["custom_bg"] === true && interaction.options.getAttachment("attachement")) {
|
||||
mainRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
.setLabel('Set attachment as custom background')
|
||||
.setCustomId('setCustomBg')
|
||||
.setStyle(ButtonStyle.Primary)
|
||||
);
|
||||
}
|
||||
|
||||
const pingRow = new ActionRowBuilder();
|
||||
pingRow.addComponents(
|
||||
new ButtonBuilder()
|
||||
@@ -51,11 +66,10 @@ module.exports = {
|
||||
let message = await interaction.editReply({ content: "", components: [mainRow, pingRow], fetchReply: true });
|
||||
|
||||
//filter only events from the user who triggered the command
|
||||
const filter = (m) => m.author.id === interaction.author.id;
|
||||
const collector = message.createMessageComponentCollector({ componentType: ComponentType.Button, time: 25000 })
|
||||
const filter = (m) => m.user.id === interaction.user.id;
|
||||
const collector = message.createMessageComponentCollector({ filter: filter, componentType: ComponentType.Button, time: 300000 })
|
||||
|
||||
collector.on('collect', async (i) => {
|
||||
await i.deferReply();
|
||||
switch (i.customId) {
|
||||
case 'editStatus':
|
||||
await this.openStatusModal(i, user, profile);
|
||||
@@ -63,19 +77,36 @@ module.exports = {
|
||||
case 'editShowcase':
|
||||
await this.openShowcaseModal(i, user, profile);
|
||||
break;
|
||||
case 'setCustomBg':
|
||||
await i.deferReply();
|
||||
let allowedContentTypes = [ "image/png", "image/jpeg" ];
|
||||
let image = interaction.options.getAttachment("attachement");
|
||||
if (!allowedContentTypes.includes(image.contentType)) {
|
||||
await i.editReply({ content: "An invalid image has been attached. Allowed are .png and .jpeg", ephemeral: true });
|
||||
return;
|
||||
}
|
||||
await GeneralUtils.downloadFile(image.url, `/app/assets/userdata/profiles/${image.id}_${image.name}`);
|
||||
profile.customBackground = `${process.env.ASSET_URL}/userdata/profiles/${image.id}_${image.name}`;
|
||||
await profile.save();
|
||||
await i.editReply('Custom profile background has been set!');
|
||||
break;
|
||||
case 'toggle-wishlist-ping':
|
||||
await i.deferUpdate();
|
||||
user.wishlistPing = !user.wishlistPing;
|
||||
user.save();
|
||||
break;
|
||||
case 'toggle-drop-ping':
|
||||
await i.deferUpdate();
|
||||
user.dropPing = !user.dropPing;
|
||||
user.save();
|
||||
break;
|
||||
case 'toggle-daily-ping':
|
||||
await i.deferUpdate();
|
||||
user.dailyPing = !user.dailyPing;
|
||||
user.save();
|
||||
break;
|
||||
default:
|
||||
await i.deferReply();
|
||||
i.editReply({ content: "Invalid selection" });
|
||||
return;
|
||||
break;
|
||||
@@ -89,8 +120,6 @@ module.exports = {
|
||||
}
|
||||
});
|
||||
await message.edit({ components: newComponents });
|
||||
let msg = await i.editReply({content: '...'});
|
||||
await msg.delete();
|
||||
});
|
||||
},
|
||||
async openShowcaseModal(interaction, user, profile) {
|
||||
@@ -118,13 +147,12 @@ module.exports = {
|
||||
|
||||
let submitted = await interaction.awaitModalSubmit({
|
||||
time: 60000,
|
||||
filter: i => i.user.id === interaction.user.id,
|
||||
filter: i => i.user.id === interaction.user.id && i.customId === 'cardSlotModal',
|
||||
}).catch(error => {
|
||||
//Error includes timeout
|
||||
console.error(error)
|
||||
return null
|
||||
})
|
||||
|
||||
if (submitted) {
|
||||
let updatePayload = {};
|
||||
for (slot of slots) {
|
||||
@@ -144,7 +172,7 @@ module.exports = {
|
||||
},
|
||||
async openStatusModal(interaction, user, profile) {
|
||||
const modal = new ModalBuilder()
|
||||
.setCustomId('descriptionModal')
|
||||
.setCustomId('statusModal')
|
||||
.setTitle('Edit profile status/description');
|
||||
|
||||
let row = new ActionRowBuilder();
|
||||
@@ -162,7 +190,7 @@ module.exports = {
|
||||
|
||||
let submitted = await interaction.awaitModalSubmit({
|
||||
time: 300000,
|
||||
filter: i => i.user.id === interaction.user.id,
|
||||
filter: i => i.user.id === interaction.user.id && i.customId === 'statusModal',
|
||||
}).catch(error => {
|
||||
//Error includes timeout
|
||||
console.error(error)
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
const { SlashCommandBuilder, MessageAttachment, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js");
|
||||
const { Card, User, Character } = require("../models");
|
||||
const { UserUtils, Compositing, Rendering } = require("../util");
|
||||
require("dotenv").config();
|
||||
const { SlashCommandBuilder } = require("discord.js");
|
||||
const { Card } = require("../models");
|
||||
const { UserUtils, Rendering, GeneralUtils } = require("../util");
|
||||
const axios = require("axios");
|
||||
const sharp = require("sharp");
|
||||
const { CURRENCY_NAMES } = require("../config/constants");
|
||||
const fs = require('fs');
|
||||
|
||||
const pageSize = 8;
|
||||
|
||||
@@ -25,56 +24,157 @@ module.exports = {
|
||||
|
||||
let discordUser = interaction.options.getUser("user") ? interaction.options.getUser("user") : interaction.member.user;
|
||||
let user = await UserUtils.getUserByDiscordId(discordUser.id);
|
||||
|
||||
let patreon = await UserUtils.getPatreonPerks(interaction.client, user);
|
||||
let profile = await user.getProfile();
|
||||
|
||||
let customStatus = this.encodeStr(profile.customStatus);
|
||||
customStatus = customStatus.replace(/(.{0,40}[\s])/g, '<tspan x="443" dy="1.2em">$1</tspan>');
|
||||
let customStatus = profile.customStatus;
|
||||
|
||||
let profileTemplate = fs.readFileSync('/app/assets/profile/profile.svg').toString();
|
||||
profileTemplate = profileTemplate.replace(/{{USERNAME}}/g, this.encodeStr(discordUser.username.substr(0,15)+(discordUser.username.length>15?'...':'')));
|
||||
profileTemplate = profileTemplate.replace(/{{PROFILE_TEXT}}/g, customStatus );
|
||||
profileTemplate = profileTemplate.replace(/{{HEADER_COLOR}}/g, '190,31,97');
|
||||
profileTemplate = profileTemplate.replace(/{{CC}}/g, await Card.count({where: {userId: user.id}}));
|
||||
profileTemplate = profileTemplate.replace(/{{LVL}}/g, await user.level().currentLevel);
|
||||
profileTemplate = profileTemplate.replace(/{{CUR_1}}/g, `${await user.primaryCurrency} ${CURRENCY_NAMES[1]}`);
|
||||
profileTemplate = profileTemplate.replace(/{{CUR_2}}/g, `${await user.secondaryCurrency} ${CURRENCY_NAMES[2]}`);
|
||||
|
||||
let userImageBuffer = await axios.get(discordUser.displayAvatarURL({format: 'png', size: 128}), { responseType: 'arraybuffer' });
|
||||
userImage = await sharp(userImageBuffer.data);
|
||||
const rect = new Buffer.from(
|
||||
'<svg><rect x="0" y="0" width="128" height="128" rx="100%" ry="100%"/></svg>'
|
||||
);
|
||||
userImage = await userImage.composite([{input: rect, blend: 'dest-in' }]).png().toBuffer();
|
||||
|
||||
let background = await sharp(Buffer.from(profileTemplate, 'utf8'))
|
||||
.composite([{ input: userImage, left: 360, top: 20 }]).png().toBuffer();
|
||||
let userImage = discordUser.displayAvatarURL({format: 'png', size: 128}).split('webp')[0] + 'png';
|
||||
|
||||
let slots = ['slotOne', 'slotTwo', 'slotThree', 'slotFour'];
|
||||
let renderedCards = [];
|
||||
for (slot of slots) {
|
||||
await Promise.all(slots.map(async slot => {
|
||||
let card = await Card.findOne({ where: { id: profile[slot], burned: false } });
|
||||
if (card) {
|
||||
let cardImage = await Rendering.renderCard(card);
|
||||
renderedCards.push(cardImage);
|
||||
console.log(`Iterating card ${card.id}`);
|
||||
let cardImage = await Rendering.renderCard(card).catch(async err => {
|
||||
await interaction.channel.send(`Uooh an error! ${err.response?.status} ${err.response?.statusText} \n ${err.response?.data.message} \n ${err.response?.data.jobId}`);
|
||||
});
|
||||
if (!cardImage) { return; }
|
||||
renderedCards[slot] = cardImage;
|
||||
} else {
|
||||
renderedCards.push('/app/assets/cards/missing_image.png');
|
||||
renderedCards[slot] = `${process.env.ASSET_URL}/cards/card_cover.png`;
|
||||
}
|
||||
}));
|
||||
|
||||
let job = {
|
||||
"type": "profile",
|
||||
"size": {
|
||||
"width": 1200,
|
||||
"height": 600
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"type": "image",
|
||||
"asset": `${renderedCards['slotOne']}`,
|
||||
"x": 25,
|
||||
"y": 85,
|
||||
"width": 300,
|
||||
"height": 500
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"asset": `${renderedCards['slotTwo']}`,
|
||||
"x": 375,
|
||||
"y": 310,
|
||||
"width": 175,
|
||||
"height": 275
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"asset": `${renderedCards['slotThree']}`,
|
||||
"x": 560,
|
||||
"y": 310,
|
||||
"width": 175,
|
||||
"height": 275
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"asset": `${renderedCards['slotFour']}`,
|
||||
"x": 745,
|
||||
"y": 310,
|
||||
"width": 175,
|
||||
"height": 275
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"asset": userImage,
|
||||
"x": 350,
|
||||
"y": 50,
|
||||
"width": 150,
|
||||
"height": 150
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": discordUser.username.substr(0,15)+(discordUser.username.length>15?'...':''),
|
||||
"fontSize": 32,
|
||||
"x": 25,
|
||||
"y": 20,
|
||||
"width": 300,
|
||||
"height": 30,
|
||||
"horizontalAlignment": "center"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": `CC: ${GeneralUtils.formatNumber(await Card.count({where: {userId: user.id}}))}`,
|
||||
"fontSize": 30,
|
||||
"x": 550,
|
||||
"y": 20,
|
||||
"width": 150,
|
||||
"height": 30,
|
||||
"horizontalAlignment": "left"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": `LVL: ${await user.level().currentLevel}`,
|
||||
"fontSize": 30,
|
||||
"x": 700,
|
||||
"y": 20,
|
||||
"width": 150,
|
||||
"height": 30,
|
||||
"horizontalAlignment": "left"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": `${GeneralUtils.formatNumber(await user.primaryCurrency)} ${CURRENCY_NAMES[1]}`,
|
||||
"fontSize": 30,
|
||||
"x": 850,
|
||||
"y": 20,
|
||||
"width": 170,
|
||||
"height": 30,
|
||||
"horizontalAlignment": "left"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": `${await GeneralUtils.formatNumber(user.secondaryCurrency)} ${CURRENCY_NAMES[2]}`,
|
||||
"fontSize": 30,
|
||||
"x": 1020,
|
||||
"y": 20,
|
||||
"width": 170,
|
||||
"height": 30,
|
||||
"horizontalAlignment": "left"
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": customStatus,
|
||||
"fontSize": 25,
|
||||
"x": 550,
|
||||
"y": 65,
|
||||
"width": 625,
|
||||
"height": 300,
|
||||
"horizontalAlignment": "left"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
let profileImage = await Compositing.renderProfile(profile, background, renderedCards);
|
||||
await interaction.editReply({ files: [profileImage] });
|
||||
},
|
||||
encodeStr: function(str) {
|
||||
let charMapping = {
|
||||
'&': '&',
|
||||
'"': '"',
|
||||
'<': '<',
|
||||
'>': '>'
|
||||
};
|
||||
return str.replace(/([\&"<>])/g, function(str, item) {
|
||||
return charMapping[item];
|
||||
});
|
||||
if (patreon.perks?.['custom_bg'] && profile.customBackground) {
|
||||
job.elements.unshift(
|
||||
{
|
||||
"type": "image",
|
||||
"asset": profile.customBackground,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 1200,
|
||||
"height": 600
|
||||
}
|
||||
);
|
||||
}
|
||||
console.log("Fetching ", );
|
||||
if(process.env.NODE_ENV === "development") {
|
||||
await interaction.channel.send(`\`\`\`${JSON.stringify(job)}\`\`\``);
|
||||
}
|
||||
let { data } = await axios.post(`${process.env.JOSE_ENDPOINT}/jobs`, job);
|
||||
console.log("Fetched ", data);
|
||||
await interaction.editReply({ files: [data["path"]] });
|
||||
}
|
||||
}
|
||||
@@ -70,9 +70,10 @@ module.exports = {
|
||||
interaction.editReply({ content: "Card not found" });
|
||||
return;
|
||||
}
|
||||
let cardImage = await Rendering.renderCard(card);
|
||||
//get base filename
|
||||
let filename = cardImage.split("/").pop();
|
||||
let cardImage = await Rendering.renderCard(card).catch(async err => {
|
||||
await interaction.channel.send(`Uooh an error! ${err.response?.status} ${err.response?.statusText} \n ${err.response?.data.message} \n ${err.response?.data.jobId}`);
|
||||
});
|
||||
if (!cardImage) { return; }
|
||||
|
||||
let description = "";
|
||||
//Add a new line after every 4th (long) word or after a full stop
|
||||
@@ -92,7 +93,7 @@ module.exports = {
|
||||
const embed = new EmbedBuilder()
|
||||
.setTitle(`${card.Character.name}`)
|
||||
.setDescription(description)
|
||||
.setImage(`attachment://${filename}`)
|
||||
.setImage(cardImage)
|
||||
.setThumbnail(card.Character.Group.imageURL)
|
||||
.addFields(
|
||||
{ name: "Owned by", value: `<@${card.User.discordId}>` },
|
||||
@@ -108,7 +109,7 @@ module.exports = {
|
||||
embed.setColor(0xff0000);
|
||||
embed.addFields({ name: "Burned", value: "This card has been burned" });
|
||||
}
|
||||
const message = await interaction.editReply({ embeds: [embed], files: [cardImage], fetchReply: true });
|
||||
const message = await interaction.editReply({ embeds: [embed], fetchReply: true });
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -101,7 +101,8 @@ const PATREON = {
|
||||
wishlist: 15,
|
||||
currency: 1.25,
|
||||
daily: 1.5
|
||||
}
|
||||
},
|
||||
custom_bg: true
|
||||
},
|
||||
4 : {
|
||||
modifiers: {
|
||||
@@ -110,7 +111,8 @@ const PATREON = {
|
||||
wishlist: 25,
|
||||
currency: 1.75,
|
||||
daily: 2
|
||||
}
|
||||
},
|
||||
custom_bg: true
|
||||
},
|
||||
5 : {
|
||||
modifiers: {
|
||||
@@ -119,7 +121,8 @@ const PATREON = {
|
||||
wishlist: 45,
|
||||
currency: 2,
|
||||
daily: 4
|
||||
}
|
||||
},
|
||||
custom_bg: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ version: "3.7"
|
||||
|
||||
services:
|
||||
bot:
|
||||
build: .
|
||||
image: node:20-alpine
|
||||
command: sh -c "npm config set cache /app/.npm_cache --global && npm install && npx sequelize db:migrate && node ."
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
|
||||
@@ -16,11 +16,12 @@ module.exports = {
|
||||
(async () => {
|
||||
try {
|
||||
console.log("Registering commands...");
|
||||
if(process.env.ENV === "production") {
|
||||
if(process.env.NODE_ENV === "production") {
|
||||
await rest.put(Routes.applicationCommands(CLIENT_ID), {body: commands });
|
||||
await rest.put(Routes.applicationGuildCommands(CLIENT_ID, process.env.GUILD_ID), {body: [] }); //Clear Guild commands on prod
|
||||
console.log("Global commands registered");
|
||||
} else {
|
||||
await rest.put(Routes.applicationCommands(CLIENT_ID), {body: [] }); //Clear global commands on dev
|
||||
await rest.put(Routes.applicationGuildCommands(CLIENT_ID, process.env.GUILD_ID), {body: commands });
|
||||
console.log("Local commands registered");
|
||||
}
|
||||
|
||||
15
migrations/20230807103447-add_custom_profile_bg.js
Normal file
15
migrations/20230807103447-add_custom_profile_bg.js
Normal file
@@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
/** @type {import('sequelize-cli').Migration} */
|
||||
module.exports = {
|
||||
async up (queryInterface, Sequelize) {
|
||||
await queryInterface.addColumn('Profiles', 'customBackground', {
|
||||
type: Sequelize.STRING,
|
||||
allowNull: true
|
||||
});
|
||||
},
|
||||
|
||||
async down (queryInterface, Sequelize) {
|
||||
await queryInterface.removeColumn('Profiles', 'customBackground');
|
||||
}
|
||||
};
|
||||
@@ -20,6 +20,7 @@ module.exports = (sequelize, DataTypes) => {
|
||||
Profile.init({
|
||||
userId: DataTypes.INTEGER,
|
||||
customStatus: DataTypes.STRING,
|
||||
customBackground: DataTypes.STRING,
|
||||
slotOne: DataTypes.INTEGER,
|
||||
slotTwo: DataTypes.INTEGER,
|
||||
slotThree: DataTypes.INTEGER,
|
||||
|
||||
337
package-lock.json
generated
337
package-lock.json
generated
@@ -10,13 +10,13 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@discordjs/rest": "^0.3.0",
|
||||
"axios": "^0.27.2",
|
||||
"axios": "^1.6.0",
|
||||
"body-parser": "^1.20.2",
|
||||
"discord-api-types": "^0.37.2",
|
||||
"discord.js": "^14.0.0",
|
||||
"dotenv": "^16.0.0",
|
||||
"express": "^4.18.2",
|
||||
"mysql2": "^2.3.3",
|
||||
"express": "^4.19.2",
|
||||
"mysql2": "^3.9.8",
|
||||
"nanoid": "^3.0.0",
|
||||
"nodemon": "^2.0.15",
|
||||
"sequelize": ">=6.28.1",
|
||||
@@ -96,6 +96,14 @@
|
||||
"node": ">=16.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/busboy": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz",
|
||||
"integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@sapphire/async-queue": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz",
|
||||
@@ -233,12 +241,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
|
||||
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
|
||||
"integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.9",
|
||||
"form-data": "^4.0.0"
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
@@ -378,17 +387,6 @@
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"node_modules/busboy": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
||||
"dependencies": {
|
||||
"streamsearch": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.16.0"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
@@ -552,9 +550,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
@@ -766,13 +764,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/es5-ext": {
|
||||
"version": "0.10.62",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
|
||||
"integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
|
||||
"version": "0.10.64",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz",
|
||||
"integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"es6-iterator": "^2.0.3",
|
||||
"es6-symbol": "^3.1.3",
|
||||
"esniff": "^2.0.1",
|
||||
"next-tick": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -822,6 +821,25 @@
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
||||
},
|
||||
"node_modules/esniff": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz",
|
||||
"integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==",
|
||||
"dependencies": {
|
||||
"d": "^1.0.1",
|
||||
"es5-ext": "^0.10.62",
|
||||
"event-emitter": "^0.3.5",
|
||||
"type": "^2.7.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/esniff/node_modules/type": {
|
||||
"version": "2.7.2",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
|
||||
"integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw=="
|
||||
},
|
||||
"node_modules/etag": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
@@ -848,16 +866,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.18.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
||||
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
|
||||
"version": "4.19.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
|
||||
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.1",
|
||||
"body-parser": "1.20.2",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.5.0",
|
||||
"cookie": "0.6.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
@@ -888,29 +906,6 @@
|
||||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/body-parser": {
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
|
||||
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.4",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.11.0",
|
||||
"raw-body": "2.5.1",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
@@ -919,36 +914,11 @@
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/express/node_modules/raw-body": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
||||
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/ext": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
|
||||
@@ -1025,9 +995,9 @@
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
||||
"version": "1.15.6",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
@@ -1429,9 +1399,9 @@
|
||||
"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="
|
||||
},
|
||||
"node_modules/long": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
||||
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
|
||||
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
@@ -1578,16 +1548,16 @@
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"node_modules/mysql2": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz",
|
||||
"integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==",
|
||||
"version": "3.9.8",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.8.tgz",
|
||||
"integrity": "sha512-+5JKNjPuks1FNMoy9TYpl77f+5frbTklz7eb3XDwbpsERRLEeXiW2PDEkakYF50UuKU2qwfGnyXpKYvukv8mGA==",
|
||||
"dependencies": {
|
||||
"denque": "^2.0.1",
|
||||
"denque": "^2.1.0",
|
||||
"generate-function": "^2.3.1",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"long": "^4.0.0",
|
||||
"lru-cache": "^6.0.0",
|
||||
"named-placeholders": "^1.1.2",
|
||||
"long": "^5.2.1",
|
||||
"lru-cache": "^8.0.0",
|
||||
"named-placeholders": "^1.1.3",
|
||||
"seq-queue": "^0.0.5",
|
||||
"sqlstring": "^2.3.2"
|
||||
},
|
||||
@@ -1595,6 +1565,14 @@
|
||||
"node": ">= 8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mysql2/node_modules/lru-cache": {
|
||||
"version": "8.0.5",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz",
|
||||
"integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==",
|
||||
"engines": {
|
||||
"node": ">=16.14"
|
||||
}
|
||||
},
|
||||
"node_modules/named-placeholders": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
|
||||
@@ -1856,6 +1834,11 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
},
|
||||
"node_modules/pseudomap": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||
@@ -2361,14 +2344,6 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/streamsearch": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
||||
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
@@ -2607,14 +2582,14 @@
|
||||
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "5.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.21.0.tgz",
|
||||
"integrity": "sha512-HOjK8l6a57b2ZGXOcUsI5NLfoTrfmbOl90ixJDl0AEFG4wgHNDQxtZy15/ZQp7HhjkpaGlp/eneMgtsu1dIlUA==",
|
||||
"version": "5.28.4",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz",
|
||||
"integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==",
|
||||
"dependencies": {
|
||||
"busboy": "^1.6.0"
|
||||
"@fastify/busboy": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.18"
|
||||
"node": ">=14.0"
|
||||
}
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
@@ -2826,6 +2801,11 @@
|
||||
"resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.2.0.tgz",
|
||||
"integrity": "sha512-/8qNbebFzLWKOOg+UV+RB8itp4SmU5jw0tBUD3ifElW6rYNOj1Ku5JaSW7lLl/WgjjxF01l/1uQPCzkwr110vg=="
|
||||
},
|
||||
"@fastify/busboy": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz",
|
||||
"integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ=="
|
||||
},
|
||||
"@sapphire/async-queue": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz",
|
||||
@@ -2933,12 +2913,13 @@
|
||||
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.27.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
|
||||
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
|
||||
"integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.14.9",
|
||||
"form-data": "^4.0.0"
|
||||
"follow-redirects": "^1.15.0",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
@@ -3039,14 +3020,6 @@
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"busboy": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
||||
"requires": {
|
||||
"streamsearch": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
@@ -3175,9 +3148,9 @@
|
||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
|
||||
},
|
||||
"cookie": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
|
||||
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="
|
||||
},
|
||||
"cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
@@ -3347,12 +3320,13 @@
|
||||
}
|
||||
},
|
||||
"es5-ext": {
|
||||
"version": "0.10.62",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
|
||||
"integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
|
||||
"version": "0.10.64",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz",
|
||||
"integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==",
|
||||
"requires": {
|
||||
"es6-iterator": "^2.0.3",
|
||||
"es6-symbol": "^3.1.3",
|
||||
"esniff": "^2.0.1",
|
||||
"next-tick": "^1.1.0"
|
||||
}
|
||||
},
|
||||
@@ -3396,6 +3370,24 @@
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
||||
},
|
||||
"esniff": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz",
|
||||
"integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==",
|
||||
"requires": {
|
||||
"d": "^1.0.1",
|
||||
"es5-ext": "^0.10.62",
|
||||
"event-emitter": "^0.3.5",
|
||||
"type": "^2.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"type": {
|
||||
"version": "2.7.2",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
|
||||
"integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"etag": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
@@ -3416,16 +3408,16 @@
|
||||
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
|
||||
},
|
||||
"express": {
|
||||
"version": "4.18.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
||||
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
|
||||
"version": "4.19.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
|
||||
"integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
|
||||
"requires": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.1",
|
||||
"body-parser": "1.20.2",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.5.0",
|
||||
"cookie": "0.6.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
@@ -3453,25 +3445,6 @@
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"body-parser": {
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
|
||||
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
|
||||
"requires": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.4",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.11.0",
|
||||
"raw-body": "2.5.1",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
@@ -3480,29 +3453,10 @@
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"raw-body": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
||||
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
|
||||
"requires": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"unpipe": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -3574,9 +3528,9 @@
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
|
||||
"version": "1.15.6",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA=="
|
||||
},
|
||||
"form-data": {
|
||||
"version": "4.0.0",
|
||||
@@ -3863,9 +3817,9 @@
|
||||
"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="
|
||||
},
|
||||
"long": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
||||
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
|
||||
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
|
||||
},
|
||||
"lru-cache": {
|
||||
"version": "6.0.0",
|
||||
@@ -3973,18 +3927,25 @@
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"mysql2": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz",
|
||||
"integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==",
|
||||
"version": "3.9.8",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.8.tgz",
|
||||
"integrity": "sha512-+5JKNjPuks1FNMoy9TYpl77f+5frbTklz7eb3XDwbpsERRLEeXiW2PDEkakYF50UuKU2qwfGnyXpKYvukv8mGA==",
|
||||
"requires": {
|
||||
"denque": "^2.0.1",
|
||||
"denque": "^2.1.0",
|
||||
"generate-function": "^2.3.1",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"long": "^4.0.0",
|
||||
"lru-cache": "^6.0.0",
|
||||
"named-placeholders": "^1.1.2",
|
||||
"long": "^5.2.1",
|
||||
"lru-cache": "^8.0.0",
|
||||
"named-placeholders": "^1.1.3",
|
||||
"seq-queue": "^0.0.5",
|
||||
"sqlstring": "^2.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "8.0.5",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz",
|
||||
"integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"named-placeholders": {
|
||||
@@ -4167,6 +4128,11 @@
|
||||
"ipaddr.js": "1.9.1"
|
||||
}
|
||||
},
|
||||
"proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
|
||||
},
|
||||
"pseudomap": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||
@@ -4503,11 +4469,6 @@
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
|
||||
},
|
||||
"streamsearch": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
||||
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
@@ -4689,11 +4650,11 @@
|
||||
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
|
||||
},
|
||||
"undici": {
|
||||
"version": "5.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.21.0.tgz",
|
||||
"integrity": "sha512-HOjK8l6a57b2ZGXOcUsI5NLfoTrfmbOl90ixJDl0AEFG4wgHNDQxtZy15/ZQp7HhjkpaGlp/eneMgtsu1dIlUA==",
|
||||
"version": "5.28.4",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz",
|
||||
"integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==",
|
||||
"requires": {
|
||||
"busboy": "^1.6.0"
|
||||
"@fastify/busboy": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"universalify": {
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@discordjs/rest": "^0.3.0",
|
||||
"axios": "^0.27.2",
|
||||
"axios": "^1.6.0",
|
||||
"body-parser": "^1.20.2",
|
||||
"discord-api-types": "^0.37.2",
|
||||
"discord.js": "^14.0.0",
|
||||
"dotenv": "^16.0.0",
|
||||
"express": "^4.18.2",
|
||||
"mysql2": "^2.3.3",
|
||||
"express": "^4.19.2",
|
||||
"mysql2": "^3.9.8",
|
||||
"nanoid": "^3.0.0",
|
||||
"nodemon": "^2.0.15",
|
||||
"sequelize": ">=6.28.1",
|
||||
|
||||
@@ -3,25 +3,25 @@
|
||||
module.exports = {
|
||||
async up (queryInterface, Sequelize) {
|
||||
|
||||
await queryInterface.bulkInsert('Bands', [{
|
||||
await queryInterface.bulkInsert('Groups', [{
|
||||
id: 1,
|
||||
name: 'TEST-BAND',
|
||||
description: 'Test-Band (stylized as TEST-BAND) is an all girl rock band from Tokyo that formed in July 2013. The band combines a rock sound with a maid image modeled on Japanese maid cafés.',
|
||||
name: 'TEST-GROUP',
|
||||
description: 'Test-Group (stylized as TEST-GROUP) is an all girl rock band from Tokyo that formed in July 2013. The band combines a rock sound with a maid image modeled on Japanese maid cafés.',
|
||||
imageURL: 'https://cdn.discordapp.com/attachments/851543504831119380/1009467684490063892/unknown.png',
|
||||
enabled: true
|
||||
}]);
|
||||
await queryInterface.bulkInsert('Characters', [{
|
||||
id: 1,
|
||||
bandId: 1,
|
||||
name: 'Band Member 1',
|
||||
groupId: 1,
|
||||
name: 'Group Member 1',
|
||||
description: 'Band Member 1 is a Japanese singer, songwriter and guitarist. She is the initial founding member and main lyricist for TEST-BAND.',
|
||||
imageIdentifier: 'testband/miku.png',
|
||||
enabled: true
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
bandId: 1,
|
||||
name: 'Band Member 2',
|
||||
groupId: 1,
|
||||
name: 'Group Member 2',
|
||||
description: 'Band Member 2 is a Japanese drummer and founding member of TEST-BAND',
|
||||
imageIdentifier: 'testband/akane.png',
|
||||
enabled: true
|
||||
|
||||
@@ -6,8 +6,9 @@ module.exports = {
|
||||
id: 1,
|
||||
maintenance: 0,
|
||||
adminIDs: '["222457277708369928"]',
|
||||
pullTimeout: 300000,
|
||||
dropTimeout: 900000
|
||||
claimTimeout: 300000,
|
||||
dropTimeout: 900000,
|
||||
patreonTierRoles: '{"1083018874263453868":1,"1083018984921759744":2,"1083019067184664607":3,"1083019116111216702":4,"1084519566354423918":5}'
|
||||
}]);
|
||||
},
|
||||
|
||||
|
||||
@@ -7,6 +7,9 @@ module.exports = {
|
||||
discordId: '123456789',
|
||||
active: false,
|
||||
privacy: 0,
|
||||
nextDropReset: new Date(),
|
||||
nextClaimReset: new Date(),
|
||||
nextDaily: new Date(),
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
}]);
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
const { spawn } = require('child_process');
|
||||
const crypto = require('crypto');
|
||||
const fs = require('fs');
|
||||
const { Card } = require('../models');
|
||||
|
||||
//TODO: Handle missing images
|
||||
module.exports = {
|
||||
name: "Compositing",
|
||||
renderProfile: async function(profile, background, renderedCards) {
|
||||
let hash = crypto.createHash('md5').update(JSON.stringify(profile) + background).digest('hex');
|
||||
|
||||
let outFile = `/app/assets/image_cache/profiles/${hash}.gif`;
|
||||
console.log('Rendering profile to ' + outFile);
|
||||
|
||||
//composite {overlay} {background} [{mask}] [-compose {method}] {result}
|
||||
let args = ['png:-', 'null:',
|
||||
'\(', `${renderedCards[0]}`, '-coalesce', '\)',
|
||||
'-geometry', '+25+85', '-compose', 'over', '-layers', 'composite',
|
||||
'null:', '\(', `${renderedCards[1]}`, '-coalesce', '-resize', '170x283', '\)',
|
||||
'-geometry', '+350+300', '-compose', 'over', '-layers', 'composite',
|
||||
'null:', '\(', `${renderedCards[2]}`, '-coalesce', '-resize', '170x283', '\)',
|
||||
'-geometry', '+535+300', '-compose', 'over', '-layers', 'composite',
|
||||
'null:', '\(', `${renderedCards[3]}`, '-coalesce', '-resize', '170x283', '\)',
|
||||
'-geometry', '+720+300', '-compose', 'over', '-layers', 'composite',
|
||||
'-layers', 'optimize', outFile];
|
||||
|
||||
console.log('GM Args: ' + args);
|
||||
|
||||
const composite = spawn('convert', args);
|
||||
composite.stdin.write(background);
|
||||
composite.stdin.end();
|
||||
|
||||
composite.stderr.on('data', (data) => {
|
||||
console.log(`stdout: ${data}`);
|
||||
});
|
||||
const exitCode = await new Promise( (resolve, reject) => {
|
||||
composite.on('close', resolve);
|
||||
})
|
||||
|
||||
return outFile;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
const { Bot } = require("../models");
|
||||
const crypto = require("crypto");
|
||||
const { ReactionUserManager } = require("discord.js");
|
||||
const axios = require("axios");
|
||||
const fs = require("fs");
|
||||
|
||||
module.exports = {
|
||||
name: "GeneralUtils",
|
||||
@@ -17,5 +19,28 @@ module.exports = {
|
||||
|
||||
generateLogID: async function() {
|
||||
return crypto.randomBytes(4).toString("hex");
|
||||
}
|
||||
},
|
||||
|
||||
formatNumber: function(num, precision = 1) {
|
||||
const map = [
|
||||
{ suffix: 'T', threshold: 1e12 },
|
||||
{ suffix: 'B', threshold: 1e9 },
|
||||
{ suffix: 'M', threshold: 1e6 },
|
||||
{ suffix: 'K', threshold: 1e3 },
|
||||
{ suffix: '', threshold: 1 },
|
||||
];
|
||||
|
||||
const found = map.find((x) => Math.abs(num) >= x.threshold);
|
||||
if (found) {
|
||||
const formatted = (Math.floor((num / found.threshold)*10) / 10) + found.suffix;
|
||||
return formatted;
|
||||
}
|
||||
|
||||
return num;
|
||||
},
|
||||
|
||||
downloadFile: async function(url, path) {
|
||||
let imageBuffer = await axios.get(url, { responseType: 'arraybuffer' });
|
||||
fs.writeFileSync(path, imageBuffer.data);
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const sharp = require('sharp');
|
||||
const crypto = require('crypto');
|
||||
const fs = require('fs');
|
||||
require("dotenv").config();
|
||||
const { Character } = require('../models');
|
||||
const axios = require('axios').default
|
||||
|
||||
const QualityColors = {
|
||||
1: {r: 0, g: 0, b: 0}, //bad
|
||||
@@ -12,102 +11,127 @@ const QualityColors = {
|
||||
6: {r: 255, g: 255, b: 0} //shiny
|
||||
}
|
||||
|
||||
//TODO: Handle missing images
|
||||
module.exports = {
|
||||
name: "Rendering",
|
||||
renderCardStack: async function(cards) {
|
||||
|
||||
for (let card of cards) {
|
||||
await Promise.all(cards.map(async card => {
|
||||
console.log(`Iterating card ${card.id}`);
|
||||
card['render'] = await this.renderCard(card);
|
||||
}));
|
||||
|
||||
let job = {
|
||||
"type": "stack",
|
||||
"size": {
|
||||
"width": 900,
|
||||
"height": 500
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"type": "image",
|
||||
"asset": `${cards[0].render}`,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 300,
|
||||
"height": 500
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"asset": `${cards[1].render}`,
|
||||
"x": 300,
|
||||
"y": 0,
|
||||
"width": 300,
|
||||
"height": 500
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"asset": `${cards[2].render}`,
|
||||
"x": 600,
|
||||
"y": 0,
|
||||
"width": 300,
|
||||
"height": 500
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
let image = await sharp({
|
||||
create: {
|
||||
width: 900,
|
||||
height: 500,
|
||||
channels: 4,
|
||||
background: { r: 0, g: 0, b: 0, alpha: 0.0 },
|
||||
animated: true
|
||||
}
|
||||
}).composite([
|
||||
{ input: cards[0].render, gravity: 'northwest' },
|
||||
{ input: cards[1].render, gravity: 'centre' },
|
||||
{ input: cards[2].render, gravity: 'northeast' },
|
||||
]);
|
||||
|
||||
let hash = crypto.createHash('md5').update("CHANGEME").digest('hex');
|
||||
try {
|
||||
await image.gif({effort: 1}).toFile(`./assets/image_cache/${hash}.gif`);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
return `./assets/image_cache/${hash}.gif`;
|
||||
console.log("Fetching ", );
|
||||
let { data } = await axios.post(`${process.env.JOSE_ENDPOINT}/jobs`, job);
|
||||
console.log("Fetched ", data);
|
||||
return data["path"];
|
||||
|
||||
},
|
||||
renderCard: async function(card) {
|
||||
const character = await Character.findOne({
|
||||
where: {
|
||||
id: card.characterId
|
||||
}
|
||||
});
|
||||
|
||||
if (!card.userId) {
|
||||
return './assets/cards/card_cover.png';
|
||||
}
|
||||
|
||||
let hash = crypto.createHash('md5').update(character.imageIdentifier + card.quality + (card.userId == 1 ? 'unclaimed' : 'claimed')).digest('hex');
|
||||
//TODO: Add switch to turn off or bypass caching
|
||||
if (fs.existsSync(`./assets/image_cache/${hash}.gif`)) {
|
||||
return `./assets/image_cache/${hash}.gif`;
|
||||
}
|
||||
|
||||
console.log(`Rendering card ${hash} for character ${character.name} ${character.imageIdentifier}`);
|
||||
|
||||
let filetype = character.imageIdentifier.split('.').pop();
|
||||
let isAnimated = ['gif', 'webp'].includes(filetype);
|
||||
|
||||
let border = await sharp(`./assets/overlays/border.svg`).tint(QualityColors[card.quality]).toBuffer();
|
||||
//BUGBUG: Custom fonts not loading
|
||||
let label = Buffer.from(`
|
||||
<svg width="300" height="500">
|
||||
<text x="50%" y="95%" text-anchor="middle" style="font-size:28px;">${character.name}</text>
|
||||
</svg>
|
||||
`);
|
||||
|
||||
let cardImage;
|
||||
try {
|
||||
console.log("Loading character image");
|
||||
cardImage = await sharp(`./assets/cards/${character.imageIdentifier}`,
|
||||
{ animated: isAnimated, pages: (isAnimated ? -1 : 1) });
|
||||
await cardImage.toBuffer();
|
||||
} catch (error) {
|
||||
console.log(`Missing character image: ${character.imageIdentifier}`);
|
||||
cardImage = await sharp(`./assets/cards/missing_image.png`);
|
||||
}
|
||||
console.log("rendering");
|
||||
await cardImage.resize(300, 500);
|
||||
await cardImage.composite([
|
||||
{input: border, top:0, left: 0, tile: true},
|
||||
{input: label, top:0, left: 0, tile: true}]);
|
||||
//BUGBUG: Grayscale does not apply to card border
|
||||
if (card.userId === 1) {
|
||||
await cardImage.grayscale()
|
||||
.modulate({
|
||||
brightness: 0.5
|
||||
renderCard: async function(card, character=null) {
|
||||
if(!character) {
|
||||
character = await Character.findOne({
|
||||
where: {
|
||||
id: card.characterId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isAnimated) {
|
||||
await cardImage.gif({effort: 1})
|
||||
} else {
|
||||
await cardImage.png();
|
||||
console.log(`Rendering card ${card.id} ${character.name} ${character.imageIdentifier}`);
|
||||
|
||||
let characterImage = `${process.env.ASSET_URL}/cards/${character.imageIdentifier}`;
|
||||
|
||||
//Hide character info if the card is unclaimed
|
||||
if (!card.userId) {
|
||||
characterImage = `${process.env.ASSET_URL}/cards/card_cover.png`;
|
||||
character.name = ' ';
|
||||
}
|
||||
|
||||
let extension = isAnimated ? 'gif' : 'png';
|
||||
await cardImage.toFile(`./assets/image_cache/${hash}.${extension}`);
|
||||
let job = {
|
||||
"type": "card",
|
||||
"size": {
|
||||
"width": 600,
|
||||
"height": 1000
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"type": "image",
|
||||
"asset": `${characterImage}`,
|
||||
"x": 10,
|
||||
"y": 10,
|
||||
"width": 580,
|
||||
"height": 980
|
||||
},
|
||||
{
|
||||
"type": "image",
|
||||
"asset": `${process.env.ASSET_URL}/overlays/default_frame.png`,
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 600,
|
||||
"height": 1000
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"text": `${character.name}`,
|
||||
"fontSize": 55,
|
||||
"x": 0,
|
||||
"y": 850,
|
||||
"width": 600,
|
||||
"height": 300,
|
||||
"horizontalAlignment": "center"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return `./assets/image_cache/${hash}.${extension}`;
|
||||
if(process.env.NODE_ENV === "development") {
|
||||
debugElement = {
|
||||
"type": "text",
|
||||
"text": `Jose-Endpoint: \n${process.env.JOSE_ENDPOINT}\nNode: \n%nodeid% \nPrint: ${card.printNr} uid: ${card.identifier}\n Serve-Mode: %servemode%\n TS:%timestamp%`,
|
||||
"fontSize": 35,
|
||||
"x": 0,
|
||||
"y": 50,
|
||||
"width": 600,
|
||||
"height": 800,
|
||||
"horizontalAlignment": "center"
|
||||
}
|
||||
job.elements.push(debugElement)
|
||||
}
|
||||
|
||||
console.log("Fetching ", JSON.stringify(job));
|
||||
let { data } = await axios.post(`${process.env.JOSE_ENDPOINT}/jobs`, job);
|
||||
console.log("Fetched ", JSON.stringify(data));
|
||||
return data["path"];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user