25 Commits

Author SHA1 Message Date
34dbc91d1e Profile: Implement custom profile backgrounds 2023-08-07 16:23:18 +02:00
2d11fdfdad DB: Add fields for custom profile backgrounds 2023-08-07 16:22:39 +02:00
db98cc21b2 GeneralUtil: Add download file function 2023-08-07 16:21:29 +02:00
696d0f136d 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.
2023-08-04 12:07:47 +02:00
24510dcc4c Bot: Change ENV to NODE_ENV in ready.js 2023-08-01 00:41:59 +02:00
ac54231ced Bot: Clear global commands when running as dev 2023-07-31 17:18:22 +02:00
b0311a3bb3 Rendering: Better card debug output
- Add timestamp
- Larger font
2023-07-31 17:17:30 +02:00
44eb1783ac Debug: Catch and print errors when fetching from Jose 2023-07-31 15:53:30 +02:00
e2dab416f2 GeneralUtils/Profile: Shorten numbers to K M B format
- Implemented for currencies on the profile
2023-07-31 15:52:42 +02:00
724621f8da Rendering: Add debug output in develop mode
- Burn node info into rendered card
- Log job def onto console
2023-07-31 14:49:00 +02:00
56ff2f96a5 Merge pull request #58 from JanGross/master
Merge master into dev-new-rendering
2023-07-31 11:38:40 +02:00
fe9b52c02d Editprofile: Properly defer updates on non-modal interactions
Also fixes interaction filtering by user id.
Also fixes collector filtering by modal customID
2023-06-12 22:31:28 +02:00
2d9f66acd4 Bump node versiom to 20
node:20-alpine
2023-06-12 21:56:45 +02:00
487ee866ba Cleanup legacy rendering code and files
also removes dockerfile and imagemagick.
2023-06-12 21:55:13 +02:00
14fbe1ab5d Rendering: Reposition labels for top-aligned default 2023-06-12 21:27:27 +02:00
7175271233 Rendering: Add placeholder frame
and fix profile card alignment
2023-06-12 16:40:11 +02:00
f84a1deddb Profile: Add stats bar to new rendering 2023-06-12 14:38:40 +02:00
5dd106354b Profile: Add userimage and status to new profile 2023-06-12 01:36:56 +02:00
9a79521a27 Profile: Add new rendering for username and showcase 2023-06-11 23:55:27 +02:00
b0d08f119e Editprofile: Remove top level defer which broke modal inputs 2023-06-11 21:55:30 +02:00
ce5cfafd1d Rendering: Hide card if it's unclaimed 2023-06-08 14:13:37 +02:00
8b35534517 Rendering: Replace card stack rendering with external API call 2023-06-08 13:47:49 +02:00
795d3b444e API: Serve static assets via express 2023-06-08 12:17:16 +02:00
46bc9ae711 Rendering: Add configurable Jose endpoint 2023-06-08 10:13:03 +02:00
a98f5e0ebc Rendering: WIP Replacing renderer with external API 2023-06-02 12:36:18 +02:00
11 changed files with 397 additions and 707 deletions

25
Docs/API/README.md Normal file
View File

@@ -0,0 +1,25 @@
# 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_

View File

@@ -1,9 +1,8 @@
const { SlashCommandBuilder, ComponentType, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js");
const { customAlphabet, random } = require("nanoid");
const { Card, User, Wishlist, Character, sequelize } = require("../models");
const { customAlphabet } = require("nanoid");
const { Card, User, Wishlist, Character } = 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();
@@ -203,10 +202,10 @@ module.exports = {
switch (i.customId) {
case 'testbatch':
i.deferUpdate();
interaction.channel.send("Beep boop fetching test renders");
interaction.channel.send("Beep boop test batch of 5");
let testCard = await Card.build({
characterId: 1,
userId: Math.floor(Math.random() * 10),
characterId: 0,
userId: 1,
identifier: "0xffff",
quality: 1,
printNr: 0,
@@ -219,37 +218,15 @@ module.exports = {
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}`);
for (let index = 0; index < 5; index++) {
testCard.printNr = index;
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;
}
});

View File

@@ -0,0 +1,32 @@
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 });
}
}

View File

@@ -80,11 +80,7 @@ module.exports = {
cards.sort((a, b) => a.characterId - b.characterId);
const row = new ActionRowBuilder();
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 deckImage = await Rendering.renderCardStack(cards);
let notableProps = [];
let pings = [];
for (let i = 0; i < cards.length; i++) {
@@ -223,10 +219,7 @@ module.exports = {
console.log(`Collected ${collected.size} interactions.`);
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 deckImage = await Rendering.renderCardStack(cards);
message.edit({ components: [], files: [new AttachmentBuilder(deckImage)] });
});

View File

@@ -37,10 +37,7 @@ module.exports = {
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).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 cardImage = await Rendering.renderCard(card);
renderedCards[slot] = cardImage;
} else {
renderedCards[slot] = `${process.env.ASSET_URL}/cards/card_cover.png`;

View File

@@ -70,10 +70,7 @@ module.exports = {
interaction.editReply({ content: "Card not found" });
return;
}
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 cardImage = await Rendering.renderCard(card);
let description = "";
//Add a new line after every 4th (long) word or after a full stop

945
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -12,14 +12,14 @@
"license": "ISC",
"dependencies": {
"@discordjs/rest": "^0.3.0",
"axios": "^1.8.2",
"axios": "^0.27.2",
"body-parser": "^1.20.2",
"discord-api-types": "^0.37.2",
"discord.js": "^14.0.0",
"dotenv": "^16.0.0",
"express": "^4.21.2",
"express": "^4.18.2",
"mysql2": "^2.3.3",
"nanoid": "^3.3.8",
"nanoid": "^3.0.0",
"nodemon": "^2.0.15",
"sequelize": ">=6.28.1",
"sequelize-cli": "^6.4.1",

View File

@@ -3,25 +3,25 @@
module.exports = {
async up (queryInterface, Sequelize) {
await queryInterface.bulkInsert('Groups', [{
await queryInterface.bulkInsert('Bands', [{
id: 1,
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.',
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.',
imageURL: 'https://cdn.discordapp.com/attachments/851543504831119380/1009467684490063892/unknown.png',
enabled: true
}]);
await queryInterface.bulkInsert('Characters', [{
id: 1,
groupId: 1,
name: 'Group Member 1',
bandId: 1,
name: 'Band 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,
groupId: 1,
name: 'Group Member 2',
bandId: 1,
name: 'Band Member 2',
description: 'Band Member 2 is a Japanese drummer and founding member of TEST-BAND',
imageIdentifier: 'testband/akane.png',
enabled: true

View File

@@ -6,9 +6,8 @@ module.exports = {
id: 1,
maintenance: 0,
adminIDs: '["222457277708369928"]',
claimTimeout: 300000,
dropTimeout: 900000,
patreonTierRoles: '{"1083018874263453868":1,"1083018984921759744":2,"1083019067184664607":3,"1083019116111216702":4,"1084519566354423918":5}'
pullTimeout: 300000,
dropTimeout: 900000
}]);
},

View File

@@ -7,9 +7,6 @@ module.exports = {
discordId: '123456789',
active: false,
privacy: 0,
nextDropReset: new Date(),
nextClaimReset: new Date(),
nextDaily: new Date(),
createdAt: new Date(),
updatedAt: new Date()
}]);