diff --git a/commands/view.js b/commands/view.js new file mode 100644 index 0000000..7215733 --- /dev/null +++ b/commands/view.js @@ -0,0 +1,37 @@ +const { SlashCommandBuilder, AttachmentBuilder } = require("discord.js"); +const { Card, User, Character } = require("../models"); +const Rendering = require("../util/rendering"); + +//fetch all cards owned by the user and list them +module.exports = { + data: new SlashCommandBuilder() + .setName("view") + .setDescription("View a specific card") + .addStringOption((option) => + option + .setName("card") + .setDescription("Card identifier") + .setRequired(false) + ), + async execute(interaction) { + await interaction.deferReply(); + const cardId = interaction.options.getString('card'); + const card = await Card.findOne({ + where: { + identifier: cardId + }, + include: [Character] + }); + if (!card) { + interaction.reply({ + content: "Card not found", + ephemeral: true + }); + return; + } + + let cardImage = await Rendering.renderCard(card); + const message = await interaction.editReply({ content: '', files: [new AttachmentBuilder(cardImage, { name: 'card.gif' })], fetchReply: true }); + + } +} \ No newline at end of file diff --git a/util/rendering.js b/util/rendering.js index f0554d5..c92d248 100644 --- a/util/rendering.js +++ b/util/rendering.js @@ -1,25 +1,15 @@ const sharp = require('sharp'); +const crypto = require('crypto'); +const fs = require('fs'); const { Character } = require('../models'); +//TODO: Handle missing images module.exports = { name: "Rendering", renderCardStack: async function(cards) { for (let card of cards) { - const character = await Character.findOne({ - where: { - id: card.characterId - } - }); - card.imageIdentifier = card.userId ? character.imageIdentifier : 'card_cover.png'; - - const cardImage = await sharp(`./assets/cards/${card.imageIdentifier}`); - if (card.userId === 1) { - cardImage.grayscale() - .modulate({ - brightness: 0.5 - }); - } - card.preProcessed = await cardImage.png().toBuffer(); + console.log(`Iterating card ${card.id}`); + card['render'] = await this.renderCard(card); } const image = await sharp({ @@ -27,20 +17,59 @@ module.exports = { width: 900, height: 500, channels: 4, - background: { r: 255, g: 0, b: 0, alpha: 0.5 } + background: { r: 255, g: 0, b: 0, alpha: 0.5 }, + animated: true } }) .composite([ - { input: cards[0].preProcessed, gravity: 'northwest' }, - { input: cards[1].preProcessed, gravity: 'centre' }, - { input: cards[2].preProcessed, 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(); + { input: cards[0].render, gravity: 'northwest' }, + { input: cards[1].render, gravity: 'centre' }, + { input: cards[2].render, gravity: 'northeast' }, + ]); - return image; + + await image.gif({effort: 1}).toFile(`./assets/image_cache/test.gif`); + + return `./assets/image_cache/test.gif`; + + }, + renderCard: async function(card) { + const character = await Character.findOne({ + where: { + id: card.characterId + } + }); + card.imageIdentifier = card.userId ? character.imageIdentifier : 'card_cover.png'; + + let hash = crypto.createHash('md5').update(card.imageIdentifier).digest('hex'); + //TODO: Add switch to turn off or bypass caching + if (fs.existsSync(`./assets/image_cache/${hash}.gif`)) { + return fs.createReadStream(`./assets/image_cache/${hash}.gif`); + } + + console.log(`Rendering card ${hash}`); + + let border = await sharp(`./assets/border.svg`).toBuffer(); + //BUGBUG: Custom fonts not loading + let label = Buffer.from(` + + ${character.name} + + `); + + const cardImage = await sharp(`./assets/cards/${card.imageIdentifier}`, { animated: true, pages: -1 }); + await cardImage.resize(300, 500); + await cardImage.composite([ + {input: border, top:0, left: 0, tile: true}, + {input: label, top:0, left: 0, tile: true}]); + if (card.userId === 1) { + cardImage.grayscale() + .modulate({ + brightness: 0.5 + }); + } + await cardImage.gif({effort: 1}).toFile(`./assets/image_cache/${hash}.gif`); + + return `./assets/image_cache/${hash}.gif`; } }