View: Add mixed view with autocomplete
for cards and characters
This commit is contained in:
127
commands/view.js
127
commands/view.js
@@ -1,6 +1,8 @@
|
|||||||
const { SlashCommandBuilder, AttachmentBuilder, EmbedBuilder } = require("discord.js");
|
const { SlashCommandBuilder, AttachmentBuilder, EmbedBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, ComponentType } = require("discord.js");
|
||||||
const { Card, User, Band, Character } = require("../models");
|
const { Card, User, Band, Character } = require("../models");
|
||||||
const Rendering = require("../util/rendering");
|
const { Rendering, UserUtils } = require("../util");
|
||||||
|
const fs = require("fs");
|
||||||
|
const edit = require("./edit");
|
||||||
|
|
||||||
//fetch all cards owned by the user and list them
|
//fetch all cards owned by the user and list them
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@@ -9,33 +11,51 @@ module.exports = {
|
|||||||
.setDescription("View a specific card")
|
.setDescription("View a specific card")
|
||||||
.addStringOption((option) =>
|
.addStringOption((option) =>
|
||||||
option
|
option
|
||||||
.setName("card")
|
.setName("type")
|
||||||
.setDescription("Card identifier")
|
.setDescription("The thing to view")
|
||||||
.setRequired(false)
|
.setRequired(true)
|
||||||
|
.addChoices(
|
||||||
|
{ name: 'card', value: 'card' },
|
||||||
|
{ name: 'character', value: 'character' },
|
||||||
|
{ name: 'band', value: 'band' }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.addStringOption((option) =>
|
||||||
|
option
|
||||||
|
.setName("id")
|
||||||
|
.setDescription("Thing identifier")
|
||||||
|
.setRequired(true)
|
||||||
|
.setAutocomplete(true)
|
||||||
),
|
),
|
||||||
async execute(interaction) {
|
async execute(interaction) {
|
||||||
await interaction.deferReply();
|
await interaction.deferReply();
|
||||||
const cardId = interaction.options.getString('card');
|
|
||||||
const card = await Card.findOne({
|
switch (interaction.options.getString("type")) {
|
||||||
|
case "card":
|
||||||
|
this.viewCard(interaction, interaction.options.getString("id"));
|
||||||
|
break;
|
||||||
|
case "character":
|
||||||
|
this.viewCharacter(interaction, interaction.options.getString("id"));
|
||||||
|
break;
|
||||||
|
case "band":
|
||||||
|
interaction.editReply({ content: "Band view is not yet implemented" });
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async viewCard(interaction, cardIdentifier) {
|
||||||
|
let card = await Card.findOne({
|
||||||
where: {
|
where: {
|
||||||
identifier: cardId
|
identifier: cardIdentifier
|
||||||
},
|
},
|
||||||
include: [
|
include: [
|
||||||
{ model: Character, include:
|
{ model: Character, include: [{ model: Band }] },
|
||||||
[Band] },
|
{ model: User}
|
||||||
User
|
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
if (!card) {
|
|
||||||
interaction.reply({
|
|
||||||
content: "Card not found",
|
|
||||||
ephemeral: true
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let cardImage = await Rendering.renderCard(card);
|
let cardImage = await Rendering.renderCard(card);
|
||||||
let attachment = new AttachmentBuilder(card);
|
|
||||||
//get base filename
|
//get base filename
|
||||||
let filename = cardImage.split("/").pop();
|
let filename = cardImage.split("/").pop();
|
||||||
|
|
||||||
@@ -62,12 +82,81 @@ module.exports = {
|
|||||||
.addFields(
|
.addFields(
|
||||||
{ name: "Owned by", value: `<@${card.User.discordId}>` },
|
{ name: "Owned by", value: `<@${card.User.discordId}>` },
|
||||||
{ name: "Band", value: `${card.Character.Band.name}` },
|
{ name: "Band", value: `${card.Character.Band.name}` },
|
||||||
|
{ name: "Character ID", value: `${card.Character.id}` },
|
||||||
{ name: 'Print Number', value: `${card.printNr}`, inline: true },
|
{ name: 'Print Number', value: `${card.printNr}`, inline: true },
|
||||||
{ name: 'Quality', value: `${card.quality}`, inline: true }
|
{ name: 'Quality', value: `${card.quality}`, inline: true }
|
||||||
)
|
)
|
||||||
.setColor(0x00ff00)
|
.setColor(0x00ff00)
|
||||||
.setFooter({ text: `${card.identifier}`, iconURL: 'https://cdn.discordapp.com/attachments/856904078754971658/1017431187234508820/fp.png' })
|
.setFooter({ text: `${card.identifier}`, iconURL: 'https://cdn.discordapp.com/attachments/856904078754971658/1017431187234508820/fp.png' })
|
||||||
.setTimestamp(card.createdAt);
|
.setTimestamp(card.createdAt);
|
||||||
|
|
||||||
const message = await interaction.editReply({ embeds: [embed], files: [cardImage], fetchReply: true });
|
const message = await interaction.editReply({ embeds: [embed], files: [cardImage], fetchReply: true });
|
||||||
|
},
|
||||||
|
|
||||||
|
async viewCharacter(interaction, characterId) {
|
||||||
|
let isAdmin = await UserUtils.getPermissionLevel(interaction.member) == 2;
|
||||||
|
let character = await Character.findOne({
|
||||||
|
where: { id: characterId },
|
||||||
|
include: [Band]
|
||||||
|
});
|
||||||
|
if (!character) {
|
||||||
|
interaction.editReply({ content: "Character not found" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let imagePath = `./assets/cards/${character.imageIdentifier}`;
|
||||||
|
//if image doesn't exist, use placeholder
|
||||||
|
if (!fs.existsSync(imagePath)) {
|
||||||
|
imagePath = "./assets/cards/missing_image.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
//get base filename
|
||||||
|
let filename = imagePath.split("/").pop();
|
||||||
|
|
||||||
|
let description = "";
|
||||||
|
//Add a new line after every 4th (long) word or after a full stop
|
||||||
|
let words = character.description.split(" ");
|
||||||
|
let count = 0;
|
||||||
|
for (let i = 0; i < words.length; i++) {
|
||||||
|
description += words[i] + " ";
|
||||||
|
if (words[i].length > 3) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
if (count >= 4 || words[i].endsWith(".")) {
|
||||||
|
description += "\n";
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setTitle(`${character.name}`)
|
||||||
|
.setDescription(description)
|
||||||
|
.setImage(`attachment://${filename}`)
|
||||||
|
.setThumbnail(character.Band.imageURL)
|
||||||
|
.addFields(
|
||||||
|
{ name: "Band", value: `${character.Band.name}` },
|
||||||
|
{ name: "Character ID", value: `${character.id}` },
|
||||||
|
)
|
||||||
|
.setColor(0x00ff00)
|
||||||
|
|
||||||
|
let row;
|
||||||
|
if (isAdmin) {
|
||||||
|
row = new ActionRowBuilder()
|
||||||
|
.addComponents(
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setCustomId(`edit-char-${character.id}`)
|
||||||
|
.setLabel("Edit")
|
||||||
|
.setStyle(ButtonStyle.Danger)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const message = await interaction.editReply({ embeds: [embed], files: [imagePath], components: [row], fetchReply: true });
|
||||||
|
const filter = (m) => m.member.user.id === interaction.member.user.id;
|
||||||
|
const collector = message.createMessageComponentCollector({ filter, componentType: ComponentType.Button, time: 120000 });
|
||||||
|
|
||||||
|
collector.on('collect', async (m) => {
|
||||||
|
console.log(`Collected ${m.customId}`);
|
||||||
|
if (m.customId === `edit-char-${character.id}`) {
|
||||||
|
await m.reply({ content: "Editing not implemented", ephemeral: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
61
events/autocompleteRequest.js
Normal file
61
events/autocompleteRequest.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
const { InteractionType } = require('discord.js');
|
||||||
|
const { UserUtils } = require('../util');
|
||||||
|
const { Card, Character, User } = require('../models');
|
||||||
|
const Sequelize = require('sequelize');
|
||||||
|
module.exports = {
|
||||||
|
name: "interactionCreate",
|
||||||
|
async execute (interaction) {
|
||||||
|
let isRegistered = await UserUtils.registrationCheck(interaction);
|
||||||
|
if (!isRegistered) return;
|
||||||
|
if (interaction.type !== InteractionType.ApplicationCommandAutocomplete) return;
|
||||||
|
console.log(`Autocomplete request from ${interaction.user.tag} (${interaction.user.id}) for ${interaction.commandName} with ${interaction.options.getFocused(true).value}`);
|
||||||
|
if (interaction.commandName === 'view') {
|
||||||
|
const viewType = interaction.options.getString('type');
|
||||||
|
let focusedOption = interaction.options.getFocused(true);
|
||||||
|
|
||||||
|
let choices = [];
|
||||||
|
|
||||||
|
switch (viewType) {
|
||||||
|
case 'card':
|
||||||
|
const cards = await Card.findAll({
|
||||||
|
where: {
|
||||||
|
identifier: {
|
||||||
|
[Sequelize.Op.like]: `%${focusedOption.value}%`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
include: [{ model: Character }, { model: User }],
|
||||||
|
limit: 10
|
||||||
|
});
|
||||||
|
for (let i = 0; i < cards.length; i++) {
|
||||||
|
choices.push({
|
||||||
|
name: `${cards[i].identifier} - ${cards[i].Character.name}`,
|
||||||
|
value: cards[i].identifier
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'character':
|
||||||
|
if(focusedOption.value.length < 3) break;
|
||||||
|
const characters = await Character.findAll({
|
||||||
|
where: {
|
||||||
|
name: {
|
||||||
|
[Sequelize.Op.like]: `%${focusedOption.value}%`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
limit: 10
|
||||||
|
});
|
||||||
|
for (let i = 0; i < characters.length; i++) {
|
||||||
|
choices.push({
|
||||||
|
name: characters[i].name,
|
||||||
|
value: `${characters[i].id}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'band':
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.respond(choices);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user