From a98f5e0ebcb7dc3c8b912317ac1fbf1d05ec1066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Gro=C3=9F?= Date: Fri, 2 Jun 2023 12:36:18 +0200 Subject: [PATCH 01/24] Rendering: WIP Replacing renderer with external API --- commands/view.js | 6 +-- util/rendering.js | 94 +++++++++++++++++++++++------------------------ 2 files changed, 49 insertions(+), 51 deletions(-) diff --git a/commands/view.js b/commands/view.js index 7716580..d044838 100644 --- a/commands/view.js +++ b/commands/view.js @@ -71,8 +71,6 @@ module.exports = { return; } let cardImage = await Rendering.renderCard(card); - //get base filename - let filename = cardImage.split("/").pop(); let description = ""; //Add a new line after every 4th (long) word or after a full stop @@ -92,7 +90,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 +106,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 }); }, /** diff --git a/util/rendering.js b/util/rendering.js index b028943..1cc597e 100644 --- a/util/rendering.js +++ b/util/rendering.js @@ -2,6 +2,7 @@ const sharp = require('sharp'); const crypto = require('crypto'); const fs = require('fs'); const { Character } = require('../models'); +const axios = require('axios').default const QualityColors = { 1: {r: 0, g: 0, b: 0}, //bad @@ -53,61 +54,60 @@ module.exports = { } }); - if (!card.userId) { +/* 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}`); + **/ + console.log(`Rendering card or 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(` - - ${character.name} - - `); - - 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 - }); + let characterImage = `http://vps5.minzkraut.com:6789/cards/${character.imageIdentifier}`; + console.log("Character iomage ", characterImage); + let job = { + "type": "card", + "size": { + "width": 600, + "height": 1000 + }, + "elements": [ + { + "type": "image", + "asset": `${characterImage}`, + "x": 0, + "y": 300, + "width": 600, + "height": 1000 + }, + { + "type": "text", + "text": `${character.name}`, + "fontSize": 55, + "x": 0, + "y": 700, + "width": 600, + "height": 300, + "horizontalAlignment": "center" + }, + { + "type": "image", + "asset": "https://cdn.discordapp.com/attachments/1083687175998152714/1113486254953222205/rainbow_overlay.png", + "x": 0, + "y": 300, + "width": 600, + "height": 1000 + } + ] } + + console.log("Fetching ", ); + let { data } = await axios.post('https://jose.toho.mnz.gg/jobs', job); + console.log("Fetched ", data); + return data["path"]; - if (isAnimated) { - await cardImage.gif({effort: 1}) - } else { - await cardImage.png(); - } - - let extension = isAnimated ? 'gif' : 'png'; - await cardImage.toFile(`./assets/image_cache/${hash}.${extension}`); - - return `./assets/image_cache/${hash}.${extension}`; } } -- 2.49.1 From 46bc9ae711e1200b1f169d7708594c1c4b6e0899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Gro=C3=9F?= Date: Thu, 8 Jun 2023 10:13:03 +0200 Subject: [PATCH 02/24] Rendering: Add configurable Jose endpoint --- .env.example | 3 ++- util/rendering.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index c0a73b7..484f4d0 100644 --- a/.env.example +++ b/.env.example @@ -9,4 +9,5 @@ DB_ROOTPW= DB_PORT= API_PORT=3080 API_ACCESS_TOKEN= -HOMEPAGE_URL= \ No newline at end of file +HOMEPAGE_URL= +JOSE_ENDPOINT= \ No newline at end of file diff --git a/util/rendering.js b/util/rendering.js index 1cc597e..6bd9d1d 100644 --- a/util/rendering.js +++ b/util/rendering.js @@ -1,3 +1,4 @@ +require("dotenv").config(); const sharp = require('sharp'); const crypto = require('crypto'); const fs = require('fs'); @@ -105,7 +106,7 @@ module.exports = { } console.log("Fetching ", ); - let { data } = await axios.post('https://jose.toho.mnz.gg/jobs', job); + let { data } = await axios.post(`${process.env.JOSE_ENDPOINT}/jobs`, job); console.log("Fetched ", data); return data["path"]; -- 2.49.1 From 795d3b444e5aee558ccdcae517ee50e2976c7294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Gro=C3=9F?= Date: Thu, 8 Jun 2023 12:15:54 +0200 Subject: [PATCH 03/24] API: Serve static assets via express --- .env.example | 5 ++++- api/jsonApi.js | 2 ++ util/rendering.js | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 484f4d0..8c6c4f7 100644 --- a/.env.example +++ b/.env.example @@ -10,4 +10,7 @@ DB_PORT= API_PORT=3080 API_ACCESS_TOKEN= HOMEPAGE_URL= -JOSE_ENDPOINT= \ No newline at end of file + +#Rendering +JOSE_ENDPOINT= +ASSET_URL= \ No newline at end of file diff --git a/api/jsonApi.js b/api/jsonApi.js index b30235d..2921cce 100644 --- a/api/jsonApi.js +++ b/api/jsonApi.js @@ -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; diff --git a/util/rendering.js b/util/rendering.js index 6bd9d1d..229df75 100644 --- a/util/rendering.js +++ b/util/rendering.js @@ -67,7 +67,7 @@ module.exports = { **/ console.log(`Rendering card or character ${character.name} ${character.imageIdentifier}`); - let characterImage = `http://vps5.minzkraut.com:6789/cards/${character.imageIdentifier}`; + let characterImage = `${process.env.ASSET_URL}/cards/${character.imageIdentifier}`; console.log("Character iomage ", characterImage); let job = { "type": "card", -- 2.49.1 From 8b3553451775b05028633dfd46a9f1fb1136cfa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Gro=C3=9F?= Date: Thu, 8 Jun 2023 13:47:49 +0200 Subject: [PATCH 04/24] Rendering: Replace card stack rendering with external API call --- util/rendering.js | 66 +++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/util/rendering.js b/util/rendering.js index 229df75..ca1a05a 100644 --- a/util/rendering.js +++ b/util/rendering.js @@ -19,33 +19,49 @@ 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) { @@ -80,7 +96,7 @@ module.exports = { "type": "image", "asset": `${characterImage}`, "x": 0, - "y": 300, + "y": 0, "width": 600, "height": 1000 }, @@ -98,7 +114,7 @@ module.exports = { "type": "image", "asset": "https://cdn.discordapp.com/attachments/1083687175998152714/1113486254953222205/rainbow_overlay.png", "x": 0, - "y": 300, + "y": 0, "width": 600, "height": 1000 } -- 2.49.1 From ce5cfafd1dae16417e6b969b59e7b7bd9778cf19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Gro=C3=9F?= Date: Thu, 8 Jun 2023 14:13:37 +0200 Subject: [PATCH 05/24] Rendering: Hide card if it's unclaimed --- util/rendering.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/util/rendering.js b/util/rendering.js index ca1a05a..c9e6b3a 100644 --- a/util/rendering.js +++ b/util/rendering.js @@ -84,6 +84,13 @@ module.exports = { console.log(`Rendering card or character ${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 = ' '; + } + console.log("Character iomage ", characterImage); let job = { "type": "card", @@ -109,21 +116,13 @@ module.exports = { "width": 600, "height": 300, "horizontalAlignment": "center" - }, - { - "type": "image", - "asset": "https://cdn.discordapp.com/attachments/1083687175998152714/1113486254953222205/rainbow_overlay.png", - "x": 0, - "y": 0, - "width": 600, - "height": 1000 } ] } console.log("Fetching ", ); let { data } = await axios.post(`${process.env.JOSE_ENDPOINT}/jobs`, job); - console.log("Fetched ", data); + console.log("Fetched ", data["path"]); return data["path"]; } -- 2.49.1 From b0d08f119eb4ff37d909ae508167f45f1e1cfdcb Mon Sep 17 00:00:00 2001 From: Minzkraut Date: Sun, 11 Jun 2023 21:55:30 +0200 Subject: [PATCH 06/24] Editprofile: Remove top level defer which broke modal inputs --- commands/editprofile.js | 1 - 1 file changed, 1 deletion(-) diff --git a/commands/editprofile.js b/commands/editprofile.js index 349c54f..0001fbf 100644 --- a/commands/editprofile.js +++ b/commands/editprofile.js @@ -55,7 +55,6 @@ module.exports = { const collector = message.createMessageComponentCollector({ componentType: ComponentType.Button, time: 25000 }) collector.on('collect', async (i) => { - await i.deferReply(); switch (i.customId) { case 'editStatus': await this.openStatusModal(i, user, profile); -- 2.49.1 From 9a79521a277bf8e10d77bd77c1be6bbb668eaf23 Mon Sep 17 00:00:00 2001 From: Minzkraut Date: Sun, 11 Jun 2023 23:55:27 +0200 Subject: [PATCH 07/24] Profile: Add new rendering for username and showcase --- commands/profile.js | 65 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/commands/profile.js b/commands/profile.js index caee635..20a0b93 100644 --- a/commands/profile.js +++ b/commands/profile.js @@ -1,3 +1,4 @@ +require("dotenv").config(); const { SlashCommandBuilder, MessageAttachment, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js"); const { Card, User, Character } = require("../models"); const { UserUtils, Compositing, Rendering } = require("../util"); @@ -52,19 +53,73 @@ module.exports = { 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) { + console.log(`Iterating card ${card.id}`); let cardImage = await Rendering.renderCard(card); renderedCards.push(cardImage); } else { - renderedCards.push('/app/assets/cards/missing_image.png'); + renderedCards.push(`${process.env.ASSET_URL}/cards/card_cover.png`); } + })); + let job = { + "type": "profile", + "size": { + "width": 1200, + "height": 600 + }, + "elements": [ + { + "type": "image", + "asset": `${renderedCards[0]}`, + "x": 25, + "y": 85, + "width": 300, + "height": 500 + }, + { + "type": "image", + "asset": `${renderedCards[1]}`, + "x": 350, + "y": 300, + "width": 150, + "height": 250 + }, + { + "type": "image", + "asset": `${renderedCards[2]}`, + "x": 510, + "y": 300, + "width": 150, + "height": 250 + }, + { + "type": "image", + "asset": `${renderedCards[3]}`, + "x": 670, + "y": 300, + "width": 150, + "height": 250 + }, + { + "type": "text", + "text": this.encodeStr(discordUser.username.substr(0,15)+(discordUser.username.length>15?'...':'')), + "fontSize": 30, + "x": 25, + "y": 25, + "width": 300, + "height": 30, + "horizontalAlignment": "center" + } + ] } - - let profileImage = await Compositing.renderProfile(profile, background, renderedCards); - await interaction.editReply({ files: [profileImage] }); + + console.log("Fetching ", ); + let { data } = await axios.post(`${process.env.JOSE_ENDPOINT}/jobs`, job); + console.log("Fetched ", data); + await interaction.editReply({ files: [data["path"]] }); }, encodeStr: function(str) { let charMapping = { -- 2.49.1 From 5dd106354be8954fa0c7aa395821bf6a455db978 Mon Sep 17 00:00:00 2001 From: Minzkraut Date: Mon, 12 Jun 2023 01:36:56 +0200 Subject: [PATCH 08/24] Profile: Add userimage and status to new profile --- commands/profile.js | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/commands/profile.js b/commands/profile.js index 20a0b93..7d0dad8 100644 --- a/commands/profile.js +++ b/commands/profile.js @@ -30,7 +30,6 @@ module.exports = { let profile = await user.getProfile(); let customStatus = this.encodeStr(profile.customStatus); - customStatus = customStatus.replace(/(.{0,40}[\s])/g, '$1'); 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?'...':''))); @@ -41,15 +40,7 @@ module.exports = { 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( - '' - ); - 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 = []; @@ -83,7 +74,7 @@ module.exports = { "type": "image", "asset": `${renderedCards[1]}`, "x": 350, - "y": 300, + "y": 325, "width": 150, "height": 250 }, @@ -91,7 +82,7 @@ module.exports = { "type": "image", "asset": `${renderedCards[2]}`, "x": 510, - "y": 300, + "y": 325, "width": 150, "height": 250 }, @@ -99,10 +90,18 @@ module.exports = { "type": "image", "asset": `${renderedCards[3]}`, "x": 670, - "y": 300, + "y": 325, "width": 150, "height": 250 }, + { + "type": "image", + "asset": userImage, + "x": 350, + "y": 50, + "width": 150, + "height": 150 + }, { "type": "text", "text": this.encodeStr(discordUser.username.substr(0,15)+(discordUser.username.length>15?'...':'')), @@ -112,6 +111,16 @@ module.exports = { "width": 300, "height": 30, "horizontalAlignment": "center" + }, + { + "type": "text", + "text": customStatus, + "fontSize": 30, + "x": 550, + "y": 25, + "width": 600, + "height": 300, + "horizontalAlignment": "left" } ] } -- 2.49.1 From f84a1deddbd1c8b88b5ddaf3a500b0092fb65fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Gro=C3=9F?= Date: Mon, 12 Jun 2023 14:38:40 +0200 Subject: [PATCH 09/24] Profile: Add stats bar to new rendering --- commands/profile.js | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/commands/profile.js b/commands/profile.js index 7d0dad8..1d0d2e1 100644 --- a/commands/profile.js +++ b/commands/profile.js @@ -112,12 +112,52 @@ module.exports = { "height": 30, "horizontalAlignment": "center" }, + { + "type": "text", + "text": `CC: ${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": `${await user.primaryCurrency} ${CURRENCY_NAMES[1]}`, + "fontSize": 30, + "x": 850, + "y": 20, + "width": 150, + "height": 30, + "horizontalAlignment": "left" + }, + { + "type": "text", + "text": `${await user.secondaryCurrency} ${CURRENCY_NAMES[2]}`, + "fontSize": 30, + "x": 1000, + "y": 20, + "width": 150, + "height": 30, + "horizontalAlignment": "left" + }, { "type": "text", "text": customStatus, "fontSize": 30, "x": 550, - "y": 25, + "y": 30, "width": 600, "height": 300, "horizontalAlignment": "left" -- 2.49.1 From 717527123347bc561e2755e6874e53d8130d1600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Gro=C3=9F?= Date: Mon, 12 Jun 2023 16:40:11 +0200 Subject: [PATCH 10/24] Rendering: Add placeholder frame and fix profile card alignment --- commands/profile.js | 24 ++++++++++++------------ util/rendering.js | 8 ++++++++ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/commands/profile.js b/commands/profile.js index 1d0d2e1..b9a14ad 100644 --- a/commands/profile.js +++ b/commands/profile.js @@ -73,26 +73,26 @@ module.exports = { { "type": "image", "asset": `${renderedCards[1]}`, - "x": 350, - "y": 325, - "width": 150, - "height": 250 + "x": 375, + "y": 310, + "width": 175, + "height": 275 }, { "type": "image", "asset": `${renderedCards[2]}`, - "x": 510, - "y": 325, - "width": 150, - "height": 250 + "x": 560, + "y": 310, + "width": 175, + "height": 275 }, { "type": "image", "asset": `${renderedCards[3]}`, - "x": 670, - "y": 325, - "width": 150, - "height": 250 + "x": 745, + "y": 310, + "width": 175, + "height": 275 }, { "type": "image", diff --git a/util/rendering.js b/util/rendering.js index c9e6b3a..0491284 100644 --- a/util/rendering.js +++ b/util/rendering.js @@ -102,6 +102,14 @@ module.exports = { { "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, -- 2.49.1 From 14fbe1ab5d26cad2d6388c53f1df00b9a87a45a7 Mon Sep 17 00:00:00 2001 From: Minzkraut Date: Mon, 12 Jun 2023 21:27:27 +0200 Subject: [PATCH 11/24] Rendering: Reposition labels for top-aligned default --- commands/profile.js | 6 +++--- util/rendering.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/commands/profile.js b/commands/profile.js index b9a14ad..af330e3 100644 --- a/commands/profile.js +++ b/commands/profile.js @@ -105,9 +105,9 @@ module.exports = { { "type": "text", "text": this.encodeStr(discordUser.username.substr(0,15)+(discordUser.username.length>15?'...':'')), - "fontSize": 30, + "fontSize": 32, "x": 25, - "y": 25, + "y": 20, "width": 300, "height": 30, "horizontalAlignment": "center" @@ -157,7 +157,7 @@ module.exports = { "text": customStatus, "fontSize": 30, "x": 550, - "y": 30, + "y": 55, "width": 600, "height": 300, "horizontalAlignment": "left" diff --git a/util/rendering.js b/util/rendering.js index 0491284..cf8fad3 100644 --- a/util/rendering.js +++ b/util/rendering.js @@ -120,7 +120,7 @@ module.exports = { "text": `${character.name}`, "fontSize": 55, "x": 0, - "y": 700, + "y": 850, "width": 600, "height": 300, "horizontalAlignment": "center" -- 2.49.1 From 487ee866ba02271fe79b747c9688a2594b453349 Mon Sep 17 00:00:00 2001 From: Minzkraut Date: Mon, 12 Jun 2023 21:55:13 +0200 Subject: [PATCH 12/24] Cleanup legacy rendering code and files also removes dockerfile and imagemagick. --- Dockerfile | 3 --- commands/profile.js | 32 +++++--------------------------- docker-compose.yml | 2 +- util/compositing.js | 42 ------------------------------------------ util/rendering.js | 18 +----------------- 5 files changed, 7 insertions(+), 90 deletions(-) delete mode 100644 Dockerfile delete mode 100644 util/compositing.js diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index d0f4688..0000000 --- a/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -# syntax=docker/dockerfile:1 -FROM node:16.9.0-alpine -RUN apk add --no-cache imagemagick \ No newline at end of file diff --git a/commands/profile.js b/commands/profile.js index af330e3..de05285 100644 --- a/commands/profile.js +++ b/commands/profile.js @@ -1,11 +1,9 @@ require("dotenv").config(); -const { SlashCommandBuilder, MessageAttachment, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js"); -const { Card, User, Character } = require("../models"); -const { UserUtils, Compositing, Rendering } = require("../util"); +const { SlashCommandBuilder } = require("discord.js"); +const { Card } = require("../models"); +const { UserUtils, Rendering } = require("../util"); const axios = require("axios"); -const sharp = require("sharp"); const { CURRENCY_NAMES } = require("../config/constants"); -const fs = require('fs'); const pageSize = 8; @@ -29,17 +27,8 @@ module.exports = { let profile = await user.getProfile(); - let customStatus = this.encodeStr(profile.customStatus); + 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 userImage = discordUser.displayAvatarURL({format: 'png', size: 128}).split('webp')[0] + 'png'; let slots = ['slotOne', 'slotTwo', 'slotThree', 'slotFour']; @@ -104,7 +93,7 @@ module.exports = { }, { "type": "text", - "text": this.encodeStr(discordUser.username.substr(0,15)+(discordUser.username.length>15?'...':'')), + "text": discordUser.username.substr(0,15)+(discordUser.username.length>15?'...':''), "fontSize": 32, "x": 25, "y": 20, @@ -169,16 +158,5 @@ module.exports = { let { data } = await axios.post(`${process.env.JOSE_ENDPOINT}/jobs`, job); console.log("Fetched ", data); await interaction.editReply({ files: [data["path"]] }); - }, - encodeStr: function(str) { - let charMapping = { - '&': '&', - '"': '"', - '<': '<', - '>': '>' - }; - return str.replace(/([\&"<>])/g, function(str, item) { - return charMapping[item]; - }); } } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index e093317..4b5fbee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.7" services: bot: - build: . + image: node:16.9.0-alpine command: sh -c "npm config set cache /app/.npm_cache --global && npm install && npx sequelize db:migrate && node ." restart: unless-stopped environment: diff --git a/util/compositing.js b/util/compositing.js deleted file mode 100644 index eb660db..0000000 --- a/util/compositing.js +++ /dev/null @@ -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; - } -} diff --git a/util/rendering.js b/util/rendering.js index cf8fad3..77eb248 100644 --- a/util/rendering.js +++ b/util/rendering.js @@ -1,7 +1,4 @@ require("dotenv").config(); -const sharp = require('sharp'); -const crypto = require('crypto'); -const fs = require('fs'); const { Character } = require('../models'); const axios = require('axios').default @@ -14,7 +11,6 @@ const QualityColors = { 6: {r: 255, g: 255, b: 0} //shiny } -//TODO: Handle missing images module.exports = { name: "Rendering", renderCardStack: async function(cards) { @@ -71,17 +67,7 @@ module.exports = { } }); -/* 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 or character ${character.name} ${character.imageIdentifier}`); + console.log(`Rendering card ${card.id} ${character.name} ${character.imageIdentifier}`); let characterImage = `${process.env.ASSET_URL}/cards/${character.imageIdentifier}`; @@ -91,7 +77,6 @@ module.exports = { character.name = ' '; } - console.log("Character iomage ", characterImage); let job = { "type": "card", "size": { @@ -132,6 +117,5 @@ module.exports = { let { data } = await axios.post(`${process.env.JOSE_ENDPOINT}/jobs`, job); console.log("Fetched ", data["path"]); return data["path"]; - } } -- 2.49.1 From 2d9f66acd4d03409432a4906f7c078da588a7dbd Mon Sep 17 00:00:00 2001 From: Minzkraut Date: Mon, 12 Jun 2023 21:56:45 +0200 Subject: [PATCH 13/24] Bump node versiom to 20 node:20-alpine --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 4b5fbee..197e6b5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.7" services: bot: - image: node:16.9.0-alpine + 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: -- 2.49.1 From fe9b52c02d0e6b55de5e1509e858c6eb06fbdee4 Mon Sep 17 00:00:00 2001 From: Minzkraut Date: Mon, 12 Jun 2023 22:31:28 +0200 Subject: [PATCH 14/24] Editprofile: Properly defer updates on non-modal interactions Also fixes interaction filtering by user id. Also fixes collector filtering by modal customID --- commands/editprofile.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/commands/editprofile.js b/commands/editprofile.js index 0001fbf..8b8c624 100644 --- a/commands/editprofile.js +++ b/commands/editprofile.js @@ -51,8 +51,8 @@ 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) => { switch (i.customId) { @@ -63,18 +63,22 @@ module.exports = { await this.openShowcaseModal(i, user, profile); 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; @@ -88,8 +92,6 @@ module.exports = { } }); await message.edit({ components: newComponents }); - let msg = await i.editReply({content: '...'}); - await msg.delete(); }); }, async openShowcaseModal(interaction, user, profile) { @@ -117,13 +119,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) { @@ -143,7 +144,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(); @@ -161,7 +162,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) -- 2.49.1 From 724621f8da8b2826eda33d5ca1d753b70bb68fd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Gro=C3=9F?= Date: Mon, 31 Jul 2023 14:48:33 +0200 Subject: [PATCH 15/24] Rendering: Add debug output in develop mode - Burn node info into rendered card - Log job def onto console --- commands/debug.js | 48 ++++++++++++++++++++++++++++++++++++++++++++++- util/rendering.js | 32 +++++++++++++++++++++++-------- 2 files changed, 71 insertions(+), 9 deletions(-) diff --git a/commands/debug.js b/commands/debug.js index 4ca5321..8a80644 100644 --- a/commands/debug.js +++ b/commands/debug.js @@ -1,7 +1,7 @@ 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 { UserUtils, CardUtils, GeneralUtils, Rendering } = require("../util"); const { PATREON } = require("../config/constants"); const stores = require("../stores"); require('dotenv').config(); @@ -28,6 +28,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 +182,51 @@ 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 test batch of 5"); + let testCard = await Card.build({ + characterId: 0, + userId: 1, + identifier: "0xffff", + quality: 1, + printNr: 0, + + }); + let testCharacter = Character.build({ + id: 0, + groupId: 0, + name: "test", + imageIdentifier: "azur-lane/akashi.png", + enabled: true + }) + for (let index = 0; index < 5; index++) { + testCard.printNr = index; + let render = await Rendering.renderCard(testCard, testCharacter).catch(function(error){interaction.channel.send(JSON.stringify(error))}); + await interaction.channel.send(render); + } + break; + } + }); + break; default: interaction.editReply({ diff --git a/util/rendering.js b/util/rendering.js index 77eb248..ee73bba 100644 --- a/util/rendering.js +++ b/util/rendering.js @@ -60,12 +60,14 @@ module.exports = { return data["path"]; }, - renderCard: async function(card) { - const character = await Character.findOne({ - where: { - id: card.characterId - } - }); + renderCard: async function(card, character=null) { + if(!character) { + character = await Character.findOne({ + where: { + id: card.characterId + } + }); + } console.log(`Rendering card ${card.id} ${character.name} ${character.imageIdentifier}`); @@ -112,10 +114,24 @@ module.exports = { } ] } + + if(process.env.NODE_ENV === "development") { + debugElement = { + "type": "text", + "text": `Jose-Endpoint: ${process.env.JOSE_ENDPOINT}\nNode: %nodeid% \nPrint: ${card.printNr} uid: ${card.identifier}\n Serve-Mode: %servemode%`, + "fontSize": 25, + "x": 0, + "y": 50, + "width": 600, + "height": 800, + "horizontalAlignment": "center" + } + job.elements.push(debugElement) + } - console.log("Fetching ", ); + console.log("Fetching ", JSON.stringify(job)); let { data } = await axios.post(`${process.env.JOSE_ENDPOINT}/jobs`, job); - console.log("Fetched ", data["path"]); + console.log("Fetched ", JSON.stringify(data)); return data["path"]; } } -- 2.49.1 From e2dab416f293423651533d9973c370afd4acef76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Gro=C3=9F?= Date: Mon, 31 Jul 2023 15:52:42 +0200 Subject: [PATCH 16/24] GeneralUtils/Profile: Shorten numbers to K M B format - Implemented for currencies on the profile --- commands/profile.js | 20 ++++++++++---------- util/general.js | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/commands/profile.js b/commands/profile.js index de05285..7254c51 100644 --- a/commands/profile.js +++ b/commands/profile.js @@ -1,7 +1,7 @@ require("dotenv").config(); const { SlashCommandBuilder } = require("discord.js"); const { Card } = require("../models"); -const { UserUtils, Rendering } = require("../util"); +const { UserUtils, Rendering, GeneralUtils } = require("../util"); const axios = require("axios"); const { CURRENCY_NAMES } = require("../config/constants"); @@ -103,7 +103,7 @@ module.exports = { }, { "type": "text", - "text": `CC: ${await Card.count({where: {userId: user.id}})}`, + "text": `CC: ${GeneralUtils.formatNumber(await Card.count({where: {userId: user.id}}))}`, "fontSize": 30, "x": 550, "y": 20, @@ -123,31 +123,31 @@ module.exports = { }, { "type": "text", - "text": `${await user.primaryCurrency} ${CURRENCY_NAMES[1]}`, + "text": `${GeneralUtils.formatNumber(await user.primaryCurrency)} ${CURRENCY_NAMES[1]}`, "fontSize": 30, "x": 850, "y": 20, - "width": 150, + "width": 170, "height": 30, "horizontalAlignment": "left" }, { "type": "text", - "text": `${await user.secondaryCurrency} ${CURRENCY_NAMES[2]}`, + "text": `${await GeneralUtils.formatNumber(user.secondaryCurrency)} ${CURRENCY_NAMES[2]}`, "fontSize": 30, - "x": 1000, + "x": 1020, "y": 20, - "width": 150, + "width": 170, "height": 30, "horizontalAlignment": "left" }, { "type": "text", "text": customStatus, - "fontSize": 30, + "fontSize": 25, "x": 550, - "y": 55, - "width": 600, + "y": 65, + "width": 625, "height": 300, "horizontalAlignment": "left" } diff --git a/util/general.js b/util/general.js index 739ec57..316d978 100644 --- a/util/general.js +++ b/util/general.js @@ -17,5 +17,23 @@ 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; } } -- 2.49.1 From 44eb1783ac4c5009a459663adb76a26ad5df62a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Gro=C3=9F?= Date: Mon, 31 Jul 2023 15:53:30 +0200 Subject: [PATCH 17/24] Debug: Catch and print errors when fetching from Jose --- commands/debug.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/commands/debug.js b/commands/debug.js index 8a80644..1b8d973 100644 --- a/commands/debug.js +++ b/commands/debug.js @@ -220,7 +220,11 @@ module.exports = { }) for (let index = 0; index < 5; index++) { testCard.printNr = index; - let render = await Rendering.renderCard(testCard, testCharacter).catch(function(error){interaction.channel.send(JSON.stringify(error))}); + let render = await Rendering.renderCard(testCard, testCharacter).catch(async function(error){ + await interaction.channel.send(JSON.stringify(error)); + await interaction.channel.send(JSON.stringify(error.response?.data)); + return; + }); await interaction.channel.send(render); } break; -- 2.49.1 From b0311a3bb33bf9e772a16ae3b08a6bab718a9ee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Gro=C3=9F?= Date: Mon, 31 Jul 2023 17:17:30 +0200 Subject: [PATCH 18/24] Rendering: Better card debug output - Add timestamp - Larger font --- util/rendering.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/rendering.js b/util/rendering.js index ee73bba..6c3fdbd 100644 --- a/util/rendering.js +++ b/util/rendering.js @@ -118,8 +118,8 @@ module.exports = { if(process.env.NODE_ENV === "development") { debugElement = { "type": "text", - "text": `Jose-Endpoint: ${process.env.JOSE_ENDPOINT}\nNode: %nodeid% \nPrint: ${card.printNr} uid: ${card.identifier}\n Serve-Mode: %servemode%`, - "fontSize": 25, + "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, -- 2.49.1 From ac54231ced49e391d8a65c73059fad9fddd34b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Gro=C3=9F?= Date: Mon, 31 Jul 2023 17:18:22 +0200 Subject: [PATCH 19/24] Bot: Clear global commands when running as dev --- events/ready.js | 1 + 1 file changed, 1 insertion(+) diff --git a/events/ready.js b/events/ready.js index 2984d9a..f7f01b1 100644 --- a/events/ready.js +++ b/events/ready.js @@ -21,6 +21,7 @@ module.exports = { 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"); } -- 2.49.1 From 24510dcc4c8435373e40e0b096ddc901028d5500 Mon Sep 17 00:00:00 2001 From: Minzkraut Date: Tue, 1 Aug 2023 00:41:59 +0200 Subject: [PATCH 20/24] Bot: Change ENV to NODE_ENV in ready.js --- events/ready.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/events/ready.js b/events/ready.js index f7f01b1..1ab2fb7 100644 --- a/events/ready.js +++ b/events/ready.js @@ -16,7 +16,7 @@ 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"); -- 2.49.1 From 696d0f136d4b23f64ac8548ee1860fb993d16b0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Gro=C3=9F?= Date: Fri, 4 Aug 2023 12:07:47 +0200 Subject: [PATCH 21/24] Profile: Fix showcase card being out of order. The showcase cards were rendered based on the order in which the render calls returned within the slots.map call. Using the original slot keys fixes this issue. --- commands/profile.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/commands/profile.js b/commands/profile.js index 7254c51..ceb12d7 100644 --- a/commands/profile.js +++ b/commands/profile.js @@ -38,9 +38,9 @@ module.exports = { if (card) { console.log(`Iterating card ${card.id}`); let cardImage = await Rendering.renderCard(card); - renderedCards.push(cardImage); + renderedCards[slot] = cardImage; } else { - renderedCards.push(`${process.env.ASSET_URL}/cards/card_cover.png`); + renderedCards[slot] = `${process.env.ASSET_URL}/cards/card_cover.png`; } })); @@ -53,7 +53,7 @@ module.exports = { "elements": [ { "type": "image", - "asset": `${renderedCards[0]}`, + "asset": `${renderedCards['slotOne']}`, "x": 25, "y": 85, "width": 300, @@ -61,7 +61,7 @@ module.exports = { }, { "type": "image", - "asset": `${renderedCards[1]}`, + "asset": `${renderedCards['slotTwo']}`, "x": 375, "y": 310, "width": 175, @@ -69,7 +69,7 @@ module.exports = { }, { "type": "image", - "asset": `${renderedCards[2]}`, + "asset": `${renderedCards['slotThree']}`, "x": 560, "y": 310, "width": 175, @@ -77,7 +77,7 @@ module.exports = { }, { "type": "image", - "asset": `${renderedCards[3]}`, + "asset": `${renderedCards['slotFour']}`, "x": 745, "y": 310, "width": 175, @@ -155,6 +155,9 @@ module.exports = { } 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"]] }); -- 2.49.1 From db98cc21b2899658046229607862f23b5c262d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Gro=C3=9F?= Date: Mon, 7 Aug 2023 16:21:29 +0200 Subject: [PATCH 22/24] GeneralUtil: Add download file function --- util/general.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/util/general.js b/util/general.js index 316d978..1b7b53a 100644 --- a/util/general.js +++ b/util/general.js @@ -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", @@ -35,5 +37,10 @@ module.exports = { } return num; - } + }, + + downloadFile: async function(url, path) { + let imageBuffer = await axios.get(url, { responseType: 'arraybuffer' }); + fs.writeFileSync(path, imageBuffer.data); + }, } -- 2.49.1 From 2d11fdfdadd4b1f566bdbf193468f6f8bf55835e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Gro=C3=9F?= Date: Mon, 7 Aug 2023 16:22:39 +0200 Subject: [PATCH 23/24] DB: Add fields for custom profile backgrounds --- .../20230807103447-add_custom_profile_bg.js | 15 +++++++++++++++ models/profile.js | 1 + 2 files changed, 16 insertions(+) create mode 100644 migrations/20230807103447-add_custom_profile_bg.js diff --git a/migrations/20230807103447-add_custom_profile_bg.js b/migrations/20230807103447-add_custom_profile_bg.js new file mode 100644 index 0000000..4509860 --- /dev/null +++ b/migrations/20230807103447-add_custom_profile_bg.js @@ -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'); + } +}; diff --git a/models/profile.js b/models/profile.js index bbed633..489fd54 100644 --- a/models/profile.js +++ b/models/profile.js @@ -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, -- 2.49.1 From 34dbc91d1e8aab69daab7447c1d3ccc5e4d5fef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Gro=C3=9F?= Date: Mon, 7 Aug 2023 16:23:18 +0200 Subject: [PATCH 24/24] Profile: Implement custom profile backgrounds --- .gitignore | 1 + assets/userdata/profiles/.gitkeep | 0 commands/editprofile.js | 36 +++++++++++++++++++++++++++---- commands/profile.js | 14 +++++++++++- config/constants.js | 9 +++++--- 5 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 assets/userdata/profiles/.gitkeep diff --git a/.gitignore b/.gitignore index c3bec1c..1524d30 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ assets/image_cache assets/cards assets/import +assets/userdata ### Visual Studio Code ### .vscode diff --git a/assets/userdata/profiles/.gitkeep b/assets/userdata/profiles/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/commands/editprofile.js b/commands/editprofile.js index 8b8c624..771a9d6 100644 --- a/commands/editprofile.js +++ b/commands/editprofile.js @@ -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() @@ -62,6 +77,19 @@ 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; diff --git a/commands/profile.js b/commands/profile.js index ceb12d7..feee576 100644 --- a/commands/profile.js +++ b/commands/profile.js @@ -24,7 +24,7 @@ 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 = profile.customStatus; @@ -154,6 +154,18 @@ module.exports = { ] } + 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)}\`\`\``); diff --git a/config/constants.js b/config/constants.js index b05c89b..645e279 100644 --- a/config/constants.js +++ b/config/constants.js @@ -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.49.1