WIP basic card dropping

This commit is contained in:
2022-08-18 19:24:44 +02:00
parent 29e3e6de23
commit ae60732836
13 changed files with 265 additions and 58 deletions

View File

@@ -39,7 +39,7 @@ module.exports = {
message += `------------------------ \n`; message += `------------------------ \n`;
} }
interaction.reply({ interaction.reply({
content: message, content: message.substring(0, 1500),
ephemeral: false ephemeral: false
}); });

View File

@@ -1,5 +1,7 @@
const { SlashCommandBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js"); const { SlashCommandBuilder, ComponentType, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js");
const { customAlphabet } = require("nanoid"); const { customAlphabet } = require("nanoid");
const { Card, User, Character } = require("../models");
const Util = require("../util/cards");
module.exports = { module.exports = {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
@@ -9,20 +11,12 @@ module.exports = {
option option
.setName("feature") .setName("feature")
.setDescription("The command to debug") .setDescription("The command to debug")
.setRequired(true) .setRequired(false)
), ),
async execute(interaction) { async execute(interaction) {
const row = new ActionRowBuilder() const identifier = Util.generateIdentifier();
.addComponents(
new ButtonBuilder()
.setCustomId('primary')
.setLabel('Primary')
.setStyle(ButtonStyle.Primary),
);
await interaction.reply({ content: 'Pong!', components: [row] });
return;
switch (interaction.options.getString("feature")) { switch (interaction.options.getString("feature")) {
case "ping": case "ping":
interaction.reply({ interaction.reply({
@@ -42,6 +36,15 @@ module.exports = {
ephemeral: false ephemeral: false
}); });
break; break;
case "clear_cards":
const cards = await Card.findAll();
for (let i = 0; i < cards.length; i++) {
await cards[i].destroy();
}
interaction.reply({
content: `Cleared ${cards.length} cards`,
ephemeral: false
});
} }
} }
} }

48
commands/debugDrop.js Normal file
View File

@@ -0,0 +1,48 @@
const { SlashCommandBuilder } = require("discord.js");
const { Card, User } = require("../models");
const { customAlphabet } = require("nanoid");
module.exports = {
data: new SlashCommandBuilder()
.setName("debug_drop")
.setDescription("Drop a card")
.addIntegerOption((option) =>
option
.setName("id")
.setDescription("The id of the character to drop")
.setRequired(true)
),
async execute(interaction) {
//get user id from database given the userID
const user = await User.findOne({
where: {
discordId: interaction.member.id
}
});
//create new card with the given character id, and the user id
const nanoid = customAlphabet('23456789ABCDEFGHJKLMNPRSTUVWXYZ',6); //Up to 887.503.681
const identifier = nanoid();
const existingCharacterCount = await Card.count({
where: {
characterId: interaction.options.getInteger("id")
}
});
const card = await Card.create({
characterId: interaction.options.getInteger("id"),
identifier: identifier,
quality: 1,
printNr: existingCharacterCount + 1,
userId: user.id
});
//reply with the new card id
interaction.reply({
content: `Dropped card ${card.id}`,
ephemeral: false
});
}
}

View File

@@ -1,47 +1,93 @@
const { SlashCommandBuilder } = require("discord.js"); const { SlashCommandBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle, ComponentType } = require("discord.js");
const { Card, User } = require("../models"); const { Card, User, Character } = require("../models");
const { customAlphabet } = require("nanoid"); const { customAlphabet } = require("nanoid");
const { CardUtils, UserUtils, ReplyUtils } = require("../util");
const card = require("../models/card");
module.exports = { module.exports = {
data: new SlashCommandBuilder() data: new SlashCommandBuilder()
.setName("drop") .setName("drop")
.setDescription("Drop a card") .setDescription("Drop a card"),
.addIntegerOption((option) =>
option
.setName("id")
.setDescription("The id of the character to drop")
.setRequired(true)
),
async execute(interaction) { async execute(interaction) {
//get user id from database given the userID
const user = await User.findOne({ const user = await User.findOne({
where: { where: {
discordId: interaction.member.id discordId: interaction.member.id
} }
}); });
//create new card with the given character id, and the user id //Generate 3 cards, each is persisted with an initial userId of NULL
const nanoid = customAlphabet('23456789ABCDEFGHJKLMNPRSTUVWXYZ',6); //Up to 887.503.681 const cards = [];
const identifier = nanoid(); for (let i = 0; i < 3; i++) {
const existingCharacterCount = await Card.count({ //get number of characters in database
const characterId = Math.floor(Math.random() * await CardUtils.getCharacterCount()) + 1;
console.log(`characterId: ${characterId}`);
let newCard = await Card.create({
characterId: characterId,
identifier: CardUtils.generateIdentifier(),
quality: 1,
printNr: await CardUtils.getNextPrintNumber(characterId),
});
cards.push(newCard);
}
let reply = "You have dropped the following cards: \n";
const row = new ActionRowBuilder();
for (const [i, card] of cards.entries()) {
let character = await Character.findOne({
where: { where: {
characterId: interaction.options.getInteger("id") id: card.characterId
}
});
reply += `IID: ${card.id} - ID:${card.identifier} \nP: ${card.printNr} Q: ${card.quality} \nC: ${character.name} \n----------\n`;
//Add claim button for each card
row.addComponents(
new ButtonBuilder()
.setCustomId(`claim-${i}-${card.identifier}`)
.setLabel(`Claim ${i+1}`)
.setStyle(ButtonStyle.Primary),
);
}
const message = await interaction.reply({ content: reply, components: [row], fetchReply: true });
const filter = m => m.author.id === interaction.user.id;
const collector = message.createMessageComponentCollector({ componentType: ComponentType.Button, time: 15000 });
collector.on('collect', async i => {
let cardId = i.customId.split("-")[1];
if (await cards[cardId].userId) { i.reply({ content: "This card has already been claimed!", ephemeral: true }); return; }
let claimUser = await UserUtils.getUserByDiscordId(i.user.id);
if (claimUser) {
//Update card with the user id
cards[cardId].userId = claimUser.id;
await cards[cardId].save();
//fetch character name from database given the character id
let character = await Character.findOne({
attributes: ["name"],
where: {
id: cards[cardId].characterId
}
});
i.reply({ content: `${i.user} (${claimUser.id}) claimed ${character.name}`, ephemeral: false });
let newRow = ReplyUtils.recreateComponents(i.message.components);
newRow.components[cardId].setLabel("Claimed");
newRow.components[cardId].setStyle(ButtonStyle.Success);
newRow.components[cardId].setDisabled(true);
message.edit({ components: [newRow] });
} }
}); });
const card = await Card.create({ collector.on('end', collected => {
characterId: interaction.options.getInteger("id"), console.log(`Collected ${collected.size} interactions.`);
identifier: identifier, message.interaction.editReply({ components: [], deferred: true });
quality: 1,
printNr: existingCharacterCount + 1,
userId: user.id
});
//reply with the new card id
interaction.reply({
content: `Dropped card ${card.id}`,
ephemeral: false
}); });
} }

View File

@@ -2,10 +2,12 @@ require("dotenv").config();
const { REST } = require("@discordjs/rest"); const { REST } = require("@discordjs/rest");
const { Routes } = require("discord-api-types/v10") const { Routes } = require("discord-api-types/v10")
const { Guild, User } = require("../models"); const { Guild, User } = require("../models");
const { UserUtils } = require("../util");
module.exports = { module.exports = {
name: "interactionCreate", name: "interactionCreate",
async execute (interaction) { async execute (interaction) {
if (!UserUtils.registrationCheck(interaction)) return;
if (!interaction.isCommand()) return; if (!interaction.isCommand()) return;
const guild = await interaction.guild; const guild = await interaction.guild;
@@ -30,19 +32,6 @@ module.exports = {
}); });
} }
//check if the user exists in the database, if not tell him to use the /register command
let user = await User.findOne({
where: {
discordId: interaction.member.id
}
});
if (!user && interaction.commandName !== "register") {
interaction.reply({
content: `You are not registered, use the /register command`,
ephemeral: false
});
return;
}
const command = interaction.client.commands.get(interaction.commandName); const command = interaction.client.commands.get(interaction.commandName);
@@ -53,7 +42,7 @@ module.exports = {
} catch (err) { } catch (err) {
if (err) console.log(err); if (err) console.log(err);
await interaction.reply({ await interaction.reply({
content: `An error occured processing the command :(\n \`\`\`${JSON.stringify(err, null, 2)}\`\`\``, content: `An error occured processing the command :(\n \`\`\`${err.stack}\`\`\``,
ephemeral: false ephemeral: false
}); });
} }

View File

@@ -0,0 +1,18 @@
'use strict';
module.exports = {
async up (queryInterface, Sequelize) {
await queryInterface.changeColumn('Cards', 'userId', {
type: Sequelize.INTEGER,
allowNull: true,
defaultValue: null
});
},
async down (queryInterface, Sequelize) {
await queryInterface.changeColumn('Cards', 'userId', {
type: Sequelize.INTEGER,
allowNull: false
});
}
};

View File

@@ -15,7 +15,6 @@ module.exports = (sequelize, DataTypes) => {
} }
} }
Band.init({ Band.init({
uniqueId: DataTypes.INTEGER,
name: DataTypes.STRING, name: DataTypes.STRING,
description: DataTypes.TEXT, description: DataTypes.TEXT,
imageURL: DataTypes.STRING, imageURL: DataTypes.STRING,

View File

@@ -10,7 +10,6 @@ module.exports = (sequelize, DataTypes) => {
* The `models/index` file will call this method automatically. * The `models/index` file will call this method automatically.
*/ */
static associate(models) { static associate(models) {
Character.belongsToMany(models.Card, { through: 'CardCharacter' });
Character.belongsTo(models.Band, { foreignKey: 'bandId', }); Character.belongsTo(models.Band, { foreignKey: 'bandId', });
} }
} }

23
util/cards.js Normal file
View File

@@ -0,0 +1,23 @@
const { customAlphabet } = require("nanoid");
const { Card, Character } = require("../models");
module.exports = {
name: "CardUtils",
generateIdentifier: function() {
const nanoid = customAlphabet('6789BCDFGHJKLMNPQRTW',6);
return nanoid();
},
getNextPrintNumber: async function(characterId) {
let count = await Card.count({
where: {
characterId: characterId
}
});
return count + 1;
},
getCharacterCount: async function(characterId) {
return await Character.count();
}
}

View File

@@ -14,7 +14,9 @@ async function syncDb() {
} }
module.exports = { module.exports = {
name: "DbUtils",
getDb, getDb,
syncDb syncDb
} }

25
util/index.js Normal file
View File

@@ -0,0 +1,25 @@
'use strict';
const fs = require('fs');
const path = require('path');
const basename = path.basename(__filename);
const utils = {};
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const util = require(path.join(__dirname, file));
utils[util.name] = util;
console.log(`Registered util: ${util.name}`);
});
Object.keys(utils).forEach(modelName => {
if (utils[modelName].associate) {
utils[modelName].associate(utils);
}
});
module.exports = utils;

27
util/reply.js Normal file
View File

@@ -0,0 +1,27 @@
const { ActionRowBuilder, ButtonBuilder, ButtonStyle, ComponentType } = require("discord.js");
module.exports = {
name: "ReplyUtils",
recreateComponents: function(components) {
console.log("Recreating components");
for (let i = 0; i < components.length; i++) {
let row = new ActionRowBuilder();
for (let j = 0; j < components[i].components.length; j++) {
console.log(components[i].components[j]);
console.log(`Recreating button ${components[i].components[j].customId}`);
let button = new ButtonBuilder();
button.setCustomId(components[i].components[j].customId);
button.setLabel(components[i].components[j].label);
button.setStyle(components[i].components[j].style);
if (components[i].components[j].emoji) {
button.setEmoji(components[i].components[j].emoji);
}
if (components[i].components[j].disabled) {
button.setDisabled(components[i].components[j].disabled);
}
row.addComponents(button);
}
return row;
}
},
}

28
util/users.js Normal file
View File

@@ -0,0 +1,28 @@
const { User } = require("../models");
module.exports = {
name: "UserUtils",
getUserByDiscordId: async function(discordId) {
return await User.findOne({
where: {
discordId: discordId
}
});
},
registrationCheck: async function(interaction) {
let user = await this.getUserByDiscordId(interaction.member.id);
if (user) {
return true;
}
if (!interaction.isButton() && interaction.commandName === "register") {
return true;
}
interaction.reply({
content: `${interaction.member} You are not registered, use the /register command`,
ephemeral: false
});
return false;
}
}