From adb0250a4fe215b3798019c392425ebfaa81d29e Mon Sep 17 00:00:00 2001 From: Munch <127023971+captain-munch@users.noreply.github.com> Date: Mon, 10 Apr 2023 10:01:58 +0100 Subject: [PATCH] Merge admin verify rate limit (#58) * Better Verify Success Output * Add warning embed * Add errorEmbed when get no paginatedInscriptions * Allow admins to use the tool * Text updates. * Update message. * Attempted fix. * Another attempt. * Null the attributes. * Tiny text update. --------- Co-authored-by: rk1129 --- button/verify.js | 4 +- commands/channel-add.js | 52 +++++++++--------- commands/channel-check.js | 34 +++++------- commands/channel-remove.js | 31 +++++------ commands/collection-add.js | 14 ++++- commands/collection-marketplace.js | 17 +++++- commands/collection-remove.js | 10 +++- commands/collection-view.js | 8 ++- modal/verify-nft.js | 88 ++++++++++++++++++++++-------- 9 files changed, 159 insertions(+), 99 deletions(-) diff --git a/button/verify.js b/button/verify.js index ef44a18..e0122bc 100644 --- a/button/verify.js +++ b/button/verify.js @@ -28,7 +28,7 @@ module.exports = { const signatureInput = new TextInputBuilder() .setCustomId(SIGNATURE_ID) - .setLabel('BIP-322 signature') + .setLabel('BIP-322 Signature') .setStyle(TextInputStyle.Short) .setMaxLength(120) @@ -68,7 +68,7 @@ module.exports = { const bipMessageInput = new TextInputBuilder() .setCustomId('bipMessage') - .setLabel('BIP-322 message') + .setLabel('BIP-322 Message') .setStyle(TextInputStyle.Short) .setValue(message) .setRequired(false) diff --git a/commands/channel-add.js b/commands/channel-add.js index 5364c9b..8e7121f 100644 --- a/commands/channel-add.js +++ b/commands/channel-add.js @@ -1,44 +1,42 @@ -const { SlashCommandBuilder, ButtonBuilder, ActionRowBuilder, ButtonStyle } = require('discord.js') +const { SlashCommandBuilder, ButtonBuilder, ActionRowBuilder, ButtonStyle, PermissionFlagsBits } = require('discord.js') const errorEmbed = require('../embed/error-embed') const successEmbed = require('../embed/success-embed') const infoEmbed = require('../embed/info-embed') const warningEmbed = require('../embed/warning-embed') const ManageChannels = require('../db/manage-channels') -const { COMMON_ERROR } = require('../embed/error-messages') module.exports = { - data: new SlashCommandBuilder().setName('channel-add').setDescription('Add the verify bot to this channel.'), + data: new SlashCommandBuilder() + .setName('channel-add') + .setDescription('Add the verify bot to this channel.') + .setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild), async execute(interaction) { try { - if (interaction.user.id === interaction.member.guild.ownerId) { - await ManageChannels.create({ - channelId: interaction.channelId, - }) + await ManageChannels.create({ + channelId: interaction.channelId, + }) - const row = new ActionRowBuilder().addComponents( - new ButtonBuilder().setCustomId('verifyNFT').setLabel('Verify').setStyle(ButtonStyle.Primary) - ) + const row = new ActionRowBuilder().addComponents( + new ButtonBuilder().setCustomId('verifyNFT').setLabel('Verify').setStyle(ButtonStyle.Primary) + ) - const info = infoEmbed( - 'Verify your ownership', - 'Use a BIP-322 signature to prove that you own an inscription to receive a special holder role.' - ) + const info = infoEmbed( + 'Verify your ownership', + 'Use a BIP-322 signature to prove that you own an inscription to receive a special holder role.' + ) - await interaction.channel.send({ - message: '', - components: [row], - embeds: [info], - }) + await interaction.channel.send({ + message: '', + components: [row], + embeds: [info], + }) - const embed = successEmbed('Add verify bot', 'Successfully added the bot to this channel.') + const embed = successEmbed('Add verify bot', 'Successfully added the bot to this channel.') - return interaction.reply({ - embeds: [embed], - ephemeral: true, - }) - } - const embed = errorEmbed(COMMON_ERROR) - return interaction.reply({ embeds: [embed], ephemeral: true }) + return interaction.reply({ + embeds: [embed], + ephemeral: true, + }) } catch (error) { if (error.name === 'SequelizeUniqueConstraintError') { const embed = warningEmbed('Add verify bot', 'The bot is already in the channel.') diff --git a/commands/channel-check.js b/commands/channel-check.js index ab54620..546addb 100644 --- a/commands/channel-check.js +++ b/commands/channel-check.js @@ -1,37 +1,33 @@ -const { SlashCommandBuilder } = require('discord.js') +const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js') const errorEmbed = require('../embed/error-embed') const successEmbed = require('../embed/success-embed') const warningEmbed = require('../embed/warning-embed') -const { COMMON_ERROR } = require('../embed/error-messages') const ManageChannels = require('../db/manage-channels') module.exports = { data: new SlashCommandBuilder() .setName('channel-check') - .setDescription('Check if the verify bot is available in this channel'), + .setDescription('Check if the verify bot is available in this channel') + .setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild), async execute(interaction) { try { - if (interaction.user.id === interaction.member.guild.ownerId) { - const channelId = await ManageChannels.findOne({ - where: { - channelId: interaction.channelId, - }, - }) - if (channelId) { - const embed = successEmbed('Check channel', 'The bot is available in this channel.') - return interaction.reply({ - embeds: [embed], - ephemeral: true, - }) - } - const embed = warningEmbed('Check channel', 'The bot *is not available* in this channel.') + const channelId = await ManageChannels.findOne({ + where: { + channelId: interaction.channelId, + }, + }) + if (channelId) { + const embed = successEmbed('Check channel', 'The bot is available in this channel.') return interaction.reply({ embeds: [embed], ephemeral: true, }) } - const embed = errorEmbed(COMMON_ERROR) - return interaction.reply({ embeds: [embed], ephemeral: true }) + const embed = warningEmbed('Check channel', 'The bot *is not available* in this channel.') + return interaction.reply({ + embeds: [embed], + ephemeral: true, + }) } catch (error) { const embed = errorEmbed(error) return interaction.reply({ embeds: [embed], ephemeral: true }) diff --git a/commands/channel-remove.js b/commands/channel-remove.js index c48290e..3bc3f3e 100644 --- a/commands/channel-remove.js +++ b/commands/channel-remove.js @@ -1,30 +1,27 @@ -const { SlashCommandBuilder } = require('discord.js') +const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js') const errorEmbed = require('../embed/error-embed') const successEmbed = require('../embed/success-embed') const warningEmbed = require('../embed/warning-embed') -const { COMMON_ERROR } = require('../embed/error-messages') const ManageChannels = require('../db/manage-channels') module.exports = { - data: new SlashCommandBuilder().setName('channel-remove').setDescription('Remove the verify bot from this channel'), + data: new SlashCommandBuilder() + .setName('channel-remove') + .setDescription('Remove the verify bot from this channel') + .setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild), async execute(interaction) { try { - if (interaction.user.id === interaction.member.guild.ownerId) { - const rowCount = await ManageChannels.destroy({ - where: { - channelId: interaction.channelId, - }, - }) - if (!rowCount) { - const embed = warningEmbed('Remove Bot', "The bot doesn't exist in this channel.") - return interaction.reply({ embeds: [embed], ephemeral: true }) - } - - const embed = successEmbed('Removed Bot', 'The bot was removed from this channel.') - + const rowCount = await ManageChannels.destroy({ + where: { + channelId: interaction.channelId, + }, + }) + if (!rowCount) { + const embed = warningEmbed('Remove Bot', "The bot doesn't exist in this channel.") return interaction.reply({ embeds: [embed], ephemeral: true }) } - const embed = errorEmbed(COMMON_ERROR) + + const embed = successEmbed('Removed Bot', 'The bot was removed from this channel.') return interaction.reply({ embeds: [embed], ephemeral: true }) } catch (error) { const embed = errorEmbed(error) diff --git a/commands/collection-add.js b/commands/collection-add.js index 69642ea..9996664 100644 --- a/commands/collection-add.js +++ b/commands/collection-add.js @@ -1,4 +1,11 @@ -const { ActionRowBuilder, ModalBuilder, TextInputBuilder, TextInputStyle, SlashCommandBuilder } = require('discord.js') +const { + ActionRowBuilder, + ModalBuilder, + TextInputBuilder, + TextInputStyle, + SlashCommandBuilder, + PermissionFlagsBits, +} = require('discord.js') const errorEmbed = require('../embed/error-embed') const ManageChannels = require('../db/manage-channels') const { COMMON_ERROR } = require('../embed/error-messages') @@ -10,7 +17,8 @@ const INS_IDS_ID = 'insIds' module.exports = { data: new SlashCommandBuilder() .setName('collection-add') - .setDescription('Manually add collection details and assign role'), + .setDescription('Manually add collection details and assign role') + .setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild), async execute(interaction) { try { const channelId = await ManageChannels.findOne({ @@ -18,7 +26,7 @@ module.exports = { channelId: interaction.channelId, }, }) - if (interaction.user.id === interaction.member.guild.ownerId && channelId) { + if (channelId) { const modal = new ModalBuilder().setCustomId(MODAL_ID).setTitle('Add Collection') const nameInput = new TextInputBuilder() diff --git a/commands/collection-marketplace.js b/commands/collection-marketplace.js index 80336aa..a05eb2b 100644 --- a/commands/collection-marketplace.js +++ b/commands/collection-marketplace.js @@ -1,4 +1,4 @@ -const { SlashCommandBuilder } = require('discord.js') +const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js') const errorEmbed = require('../embed/error-embed') const successEmbed = require('../embed/success-embed') const warningEmbed = require('../embed/warning-embed') @@ -60,7 +60,9 @@ module.exports = { .addStringOption((option) => option.setName('link').setDescription('The link to the collection').setRequired(true)) .addStringOption((option) => option.setName('name').setDescription('Override the collection name').setRequired(false) - ), + ) + .setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild), + async execute(interaction) { try { const channelId = await ManageChannels.findOne({ @@ -68,7 +70,7 @@ module.exports = { channelId: interaction.channelId, }, }) - if (interaction.user.id === interaction.member.guild.ownerId && channelId) { + if (channelId) { const venue = interaction.options.getString('venue') const role = interaction.options.getRole('role') const url = interaction.options.getString('link') @@ -128,6 +130,15 @@ module.exports = { offset, collectionSymbol ) + + // This usually happens if API is down or rate limit is hit + if (paginatedInscriptions.length === 0) { + const embed = errorEmbed('The marketplace api is not responding, it may be unavailable or rate limited.') + return interaction.editReply({ + embeds: [embed], + ephemeral: true, + }) + } inscriptions.push(...paginatedInscriptions) const embed = infoEmbed( 'Fetching Inscriptions', diff --git a/commands/collection-remove.js b/commands/collection-remove.js index d256f64..cfdd709 100644 --- a/commands/collection-remove.js +++ b/commands/collection-remove.js @@ -1,4 +1,4 @@ -const { SlashCommandBuilder, ActionRowBuilder, StringSelectMenuBuilder } = require('discord.js') +const { SlashCommandBuilder, ActionRowBuilder, StringSelectMenuBuilder, PermissionFlagsBits } = require('discord.js') const errorEmbed = require('../embed/error-embed') const successEmbed = require('../embed/success-embed') const { Collections } = require('../db/collections-inscriptions') @@ -8,7 +8,11 @@ const { COMMON_ERROR } = require('../embed/error-messages') const REMOVE_COLLECTION_SELECTOR = 'removeCollectionSelector' module.exports = { - data: new SlashCommandBuilder().setName('collection-remove').setDescription('Remove a collection from the server'), + data: new SlashCommandBuilder() + .setName('collection-remove') + .setDescription('Remove a collection from the server') + .setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild), + async execute(interaction) { try { const channelId = await ManageChannels.findOne({ @@ -16,7 +20,7 @@ module.exports = { channelId: interaction.channelId, }, }) - if (interaction.user.id === interaction.member.guild.ownerId && channelId) { + if (channelId) { const collections = await Collections.findAll({ attributes: ['name', 'role', 'id'], where: { diff --git a/commands/collection-view.js b/commands/collection-view.js index a1607b0..85168b6 100644 --- a/commands/collection-view.js +++ b/commands/collection-view.js @@ -1,4 +1,4 @@ -const { SlashCommandBuilder } = require('discord.js') +const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js') const errorEmbed = require('../embed/error-embed') const infoEmbed = require('../embed/info-embed') const roleEmbed = require('../embed/role-embed') @@ -7,7 +7,11 @@ const sequelize = require('../db/db-connect') const commaNumber = require('comma-number') module.exports = { - data: new SlashCommandBuilder().setName('collection-view').setDescription('View all collections'), + data: new SlashCommandBuilder() + .setName('collection-view') + .setDescription('View all collections') + .setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild), + async execute(interaction) { try { const collections = await Collections.findAll({ diff --git a/modal/verify-nft.js b/modal/verify-nft.js index 05aa409..434e389 100644 --- a/modal/verify-nft.js +++ b/modal/verify-nft.js @@ -3,7 +3,10 @@ const errorEmbed = require('../embed/error-embed') const successEmbed = require('../embed/success-embed') const warningEmbed = require('../embed/warning-embed') const roleEmbed = require('../embed/role-embed') -const { getInscription } = require('../db/collections-inscriptions') +const commaNumber = require('comma-number') +const { Op } = require('sequelize') +const { getInscription, Collections, Inscriptions } = require('../db/collections-inscriptions') +const sequelize = require('../db/db-connect') const UserInscriptions = require('../db/user-inscriptions') const BipMessages = require('../db/bip-messages') const { MODAL_ID, SIGNATURE_ID, ADDRESS } = require('../button/verify') @@ -60,8 +63,12 @@ module.exports = { const warning = warningEmbed('Verify Problem', "The BIP-322 node couldn't verify your signature.") return await interaction.editReply({ embeds: [warning], ephemeral: true }) } - const inscriptions = await axios.get(`${process.env.ADDRESS_API}/${address}`) + + if (!Array.isArray(inscriptions.data)) { + const warning = warningEmbed('Verification Problem', 'There are no inscriptions in your wallet.') + return await interaction.editReply({ embeds: [warning], ephemeral: true }) + } const addedRoles = [] const notFoundRoles = [] @@ -76,42 +83,77 @@ module.exports = { await interaction.member.roles.add(role) addedRoles.push(roleEmbed(interaction, role.name)) // Everything has been allocated, lets upsert into the UserInscriptions table - await UserInscriptions.upsert({ - userId: interaction.user.id, - inscriptionId: inscription.id, + const userInscription = await UserInscriptions.findOne({ + where: { + inscriptionId: inscription.id, + userId: interaction.user.id, + }, }) + if (!userInscription) { + await UserInscriptions.create({ + inscriptionId: inscription.id, + userId: interaction.user.id, + }) + } } else { notFoundRoles.push(role.name) } } } - // Valid roles - if (addedRoles.length > 0) { - const roleRef = addedRoles.length > 1 ? 'roles' : 'role' - const embed = successEmbed( + const collections = await Collections.findAll({ + where: { + channelId: interaction.channelId, + }, + attributes: [ + 'id', + 'name', + 'role', + [sequelize.fn('COUNT', sequelize.col('Inscriptions.id')), 'inscriptionCount'], + ], + include: { + model: Inscriptions, + attributes: [], + include: { + model: UserInscriptions, + attributes: [], + where: { + userId: interaction.user.id, + }, + }, + }, + having: { + inscriptionCount: { + [Op.gt]: 0, + }, + }, + group: ['Collections.id'], + }) + + if (collections.length > 0) { + const rolePlural = addedRoles.length > 1 ? 'roles were' : 'role was' + const resultEmbed = successEmbed( 'Successfully verified', - `Your signature was validated and you were assigned the ${addedRoles.join(' ')} ${roleRef}.` + `Your signature was validated and the relevant ${rolePlural} assigned.` ) - - return interaction.editReply({ embeds: [embed], ephemeral: true }) - } - - // Invalid Roles - if (notFoundRoles.length > 0) { - const embed = - notFoundRoles.length > 1 - ? warningEmbed('Roles not found', `The **${notFoundRoles.join(' ')}** roles aren't available.`) - : warningEmbed('Role not found', `The **${notFoundRoles.join(' ')}** role isn't available.`) - return interaction.editReply({ embeds: [embed], ephemeral: true }) + collections.forEach((collection) => { + resultEmbed.addFields({ + name: collection.dataValues.name, + value: `${roleEmbed(interaction, collection.dataValues.role)} (${commaNumber( + collection.dataValues.inscriptionCount + )})`, + inline: true, + }) + }) + + return interaction.editReply({ embeds: [resultEmbed], ephemeral: true }) } - // Catch where no collections were matched const warning = warningEmbed( 'Verify Problem', "There's no matching collections for the inscriptions in your wallet." ) - return interaction.reply({ embeds: [warning], ephemeral: true }) + return interaction.editReply({ embeds: [warning], ephemeral: true }) } catch (error) { // Valid error from the RPC node if (error.response && error.response.status === 500) {