Create button event to gain the server access role (#398)
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
# Description Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. - Add the ability for button event interactions to be created - Added a button event interaction which gives a user the role which is specified in the config `verification.role` - Added a command to generate a button to activate the verify button event #232 ## Type of change Please delete options that are not relevant. - [x] New feature (non-breaking change which adds functionality) # How Has This Been Tested? Please describe the tests that you ran to verify the changes. Provide instructions so we can reproduce. Please also list any relevant details to your test configuration. # Checklist - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that provde my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules Reviewed-on: https://gitea.vylpes.xyz/RabbitLabs/vylbot-app/pulls/398 Reviewed-by: VylpesTester <tester@vylpes.com> Co-authored-by: Ethan Lane <ethan@vylpes.com> Co-committed-by: Ethan Lane <ethan@vylpes.com>
This commit is contained in:
parent
f0ab3b9080
commit
56f0d105be
11 changed files with 183 additions and 33 deletions
42
src/buttonEvents/verify.ts
Normal file
42
src/buttonEvents/verify.ts
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import { ButtonInteraction, CacheType } from "discord.js";
|
||||||
|
import { ButtonEvent } from "../type/buttonEvent";
|
||||||
|
import SettingsHelper from "../helpers/SettingsHelper";
|
||||||
|
|
||||||
|
export default class Verify extends ButtonEvent {
|
||||||
|
public override async execute(interaction: ButtonInteraction<CacheType>) {
|
||||||
|
if (!interaction.guildId || !interaction.guild) return;
|
||||||
|
|
||||||
|
const roleName = await SettingsHelper.GetSetting("verification.role", interaction.guildId);
|
||||||
|
|
||||||
|
if (!roleName) return;
|
||||||
|
|
||||||
|
const role = interaction.guild.roles.cache.find(x => x.name == roleName);
|
||||||
|
|
||||||
|
if (!role) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: `Unable to find the role, ${roleName}`,
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const member = interaction.guild.members.cache.find(x => x.id == interaction.user.id);
|
||||||
|
|
||||||
|
if (!member || !member.manageable) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: "Unable to give role to user",
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await member.roles.add(role);
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
content: "Given role",
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,10 +9,13 @@ import { Command } from "../type/command";
|
||||||
import { Events } from "./events";
|
import { Events } from "./events";
|
||||||
import { Util } from "./util";
|
import { Util } from "./util";
|
||||||
import AppDataSource from "../database/dataSources/appDataSource";
|
import AppDataSource from "../database/dataSources/appDataSource";
|
||||||
|
import ButtonEventItem from "../contracts/ButtonEventItem";
|
||||||
|
import { ButtonEvent } from "../type/buttonEvent";
|
||||||
|
|
||||||
export class CoreClient extends Client {
|
export class CoreClient extends Client {
|
||||||
private static _commandItems: ICommandItem[];
|
private static _commandItems: ICommandItem[];
|
||||||
private static _eventItems: IEventItem[];
|
private static _eventItems: IEventItem[];
|
||||||
|
private static _buttonEvents: ButtonEventItem[];
|
||||||
|
|
||||||
private _events: Events;
|
private _events: Events;
|
||||||
private _util: Util;
|
private _util: Util;
|
||||||
|
@ -25,12 +28,17 @@ export class CoreClient extends Client {
|
||||||
return this._eventItems;
|
return this._eventItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static get buttonEvents(): ButtonEventItem[] {
|
||||||
|
return this._buttonEvents;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(intents: number[], partials: Partials[]) {
|
constructor(intents: number[], partials: Partials[]) {
|
||||||
super({ intents: intents, partials: partials });
|
super({ intents: intents, partials: partials });
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
CoreClient._commandItems = [];
|
CoreClient._commandItems = [];
|
||||||
CoreClient._eventItems = [];
|
CoreClient._eventItems = [];
|
||||||
|
CoreClient._buttonEvents = [];
|
||||||
|
|
||||||
this._events = new Events();
|
this._events = new Events();
|
||||||
this._util = new Util();
|
this._util = new Util();
|
||||||
|
@ -73,4 +81,13 @@ export class CoreClient extends Client {
|
||||||
|
|
||||||
CoreClient._eventItems.push(item);
|
CoreClient._eventItems.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static RegisterButtonEvent(buttonId: string, event: ButtonEvent) {
|
||||||
|
const item: ButtonEventItem = {
|
||||||
|
ButtonId: buttonId,
|
||||||
|
Event: event,
|
||||||
|
};
|
||||||
|
|
||||||
|
CoreClient._buttonEvents.push(item);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +1,18 @@
|
||||||
import { Interaction } from "discord.js";
|
import { Interaction } from "discord.js";
|
||||||
import ICommandItem from "../contracts/ICommandItem";
|
import ChatInputCommand from "./interactionCreate/chatInputCommand";
|
||||||
import SettingsHelper from "../helpers/SettingsHelper";
|
import Button from "./interactionCreate/button";
|
||||||
import { CoreClient } from "./client";
|
|
||||||
|
|
||||||
export class Events {
|
export class Events {
|
||||||
public async onInteractionCreate(interaction: Interaction) {
|
public async onInteractionCreate(interaction: Interaction) {
|
||||||
if (!interaction.isChatInputCommand()) return;
|
|
||||||
if (!interaction.guildId) return;
|
if (!interaction.guildId) return;
|
||||||
|
|
||||||
const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId);
|
if (interaction.isChatInputCommand()) {
|
||||||
const disabledCommands = disabledCommandsString?.split(",");
|
ChatInputCommand.onChatInput(interaction);
|
||||||
|
|
||||||
const disabledCommandsMessage = await SettingsHelper.GetSetting("commands.disabled.message", interaction.guildId);
|
|
||||||
|
|
||||||
if (disabledCommands?.find(x => x == interaction.commandName)) {
|
|
||||||
await interaction.reply(disabledCommandsMessage || "This command is disabled.");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const item = CoreClient.commandItems.find(x => x.Name == interaction.commandName && !x.ServerId);
|
if (interaction.isButton()) {
|
||||||
const itemForServer = CoreClient.commandItems.find(x => x.Name == interaction.commandName && x.ServerId == interaction.guildId);
|
Button.onButtonClicked(interaction);
|
||||||
|
|
||||||
let itemToUse: ICommandItem;
|
|
||||||
|
|
||||||
if (!itemForServer) {
|
|
||||||
if (!item) {
|
|
||||||
await interaction.reply('Command not found');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
itemToUse = item;
|
|
||||||
} else {
|
|
||||||
itemToUse = itemForServer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
itemToUse.Command.execute(interaction);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit when bot is logged in and ready to use
|
// Emit when bot is logged in and ready to use
|
||||||
|
|
17
src/client/interactionCreate/button.ts
Normal file
17
src/client/interactionCreate/button.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { ButtonInteraction } from "discord.js";
|
||||||
|
import { CoreClient } from "../client";
|
||||||
|
|
||||||
|
export default class Button {
|
||||||
|
public static async onButtonClicked(interaction: ButtonInteraction) {
|
||||||
|
if (!interaction.isButton) return;
|
||||||
|
|
||||||
|
const item = CoreClient.buttonEvents.find(x => x.ButtonId == interaction.customId.split(" ")[0]);
|
||||||
|
|
||||||
|
if (!item) {
|
||||||
|
await interaction.reply("Event not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.Event.execute(interaction);
|
||||||
|
}
|
||||||
|
}
|
27
src/client/interactionCreate/chatInputCommand.ts
Normal file
27
src/client/interactionCreate/chatInputCommand.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { Interaction } from "discord.js";
|
||||||
|
import { CoreClient } from "../client";
|
||||||
|
import ICommandItem from "../../contracts/ICommandItem";
|
||||||
|
|
||||||
|
export default class ChatInputCommand {
|
||||||
|
public static async onChatInput(interaction: Interaction) {
|
||||||
|
if (!interaction.isChatInputCommand()) return;
|
||||||
|
|
||||||
|
const item = CoreClient.commandItems.find(x => x.Name == interaction.commandName && !x.ServerId);
|
||||||
|
const itemForServer = CoreClient.commandItems.find(x => x.Name == interaction.commandName && x.ServerId == interaction.guildId);
|
||||||
|
|
||||||
|
let itemToUse: ICommandItem;
|
||||||
|
|
||||||
|
if (!itemForServer) {
|
||||||
|
if (!item) {
|
||||||
|
await interaction.reply("Command not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
itemToUse = item;
|
||||||
|
} else {
|
||||||
|
itemToUse = itemForServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
itemToUse.Command.execute(interaction);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js";
|
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js";
|
||||||
import { existsSync, readFileSync } from "fs";
|
import { existsSync, readFileSync } from "fs";
|
||||||
import EmbedColours from "../constants/EmbedColours";
|
import EmbedColours from "../constants/EmbedColours";
|
||||||
import { Command } from "../type/command";
|
import { Command } from "../type/command";
|
||||||
|
import SettingsHelper from "../helpers/SettingsHelper";
|
||||||
|
|
||||||
interface IRules {
|
interface IRules {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
@ -14,13 +15,36 @@ export default class Rules extends Command {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
super.CommandBuilder = new SlashCommandBuilder()
|
this.CommandBuilder = new SlashCommandBuilder()
|
||||||
.setName("rules")
|
.setName('rules')
|
||||||
.setDescription("Send the rules embeds for this server")
|
.setDescription("Rules-related commands")
|
||||||
.setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator);
|
.setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
|
||||||
|
.addSubcommand(x =>
|
||||||
|
x
|
||||||
|
.setName('embeds')
|
||||||
|
.setDescription('Send the rules embeds for this server'))
|
||||||
|
.addSubcommand(x =>
|
||||||
|
x
|
||||||
|
.setName('access')
|
||||||
|
.setDescription('Send the server verification embed button'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async execute(interaction: CommandInteraction) {
|
public override async execute(interaction: CommandInteraction) {
|
||||||
|
if (!interaction.isChatInputCommand()) return;
|
||||||
|
|
||||||
|
switch (interaction.options.getSubcommand()) {
|
||||||
|
case "embeds":
|
||||||
|
await this.SendEmbeds(interaction);
|
||||||
|
break;
|
||||||
|
case "access":
|
||||||
|
await this.SendAccessButton(interaction);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
await interaction.reply("Subcommand doesn't exist.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async SendEmbeds(interaction: CommandInteraction) {
|
||||||
if (!interaction.guildId) return;
|
if (!interaction.guildId) return;
|
||||||
|
|
||||||
if (!existsSync(`${process.cwd()}/data/rules/${interaction.guildId}.json`)) {
|
if (!existsSync(`${process.cwd()}/data/rules/${interaction.guildId}.json`)) {
|
||||||
|
@ -71,4 +95,27 @@ export default class Rules extends Command {
|
||||||
|
|
||||||
await interaction.reply({ embeds: [ successEmbed ], ephemeral: true });
|
await interaction.reply({ embeds: [ successEmbed ], ephemeral: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async SendAccessButton(interaction: CommandInteraction) {
|
||||||
|
if (!interaction.guildId) return;
|
||||||
|
|
||||||
|
const buttonLabel = await SettingsHelper.GetSetting("rules.access.label", interaction.guildId);
|
||||||
|
|
||||||
|
const row = new ActionRowBuilder<ButtonBuilder>()
|
||||||
|
.addComponents([
|
||||||
|
new ButtonBuilder()
|
||||||
|
.setCustomId('verify')
|
||||||
|
.setStyle(ButtonStyle.Primary)
|
||||||
|
.setLabel(buttonLabel || "Verify")
|
||||||
|
]);
|
||||||
|
|
||||||
|
await interaction.channel?.send({
|
||||||
|
components: [ row ]
|
||||||
|
});
|
||||||
|
|
||||||
|
await interaction.reply({
|
||||||
|
content: "Success",
|
||||||
|
ephemeral: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -31,6 +31,7 @@ export default class DefaultValues {
|
||||||
|
|
||||||
// Rules (Command)
|
// Rules (Command)
|
||||||
this.values.push({ Key: "rules.file", Value: "data/rules/rules" });
|
this.values.push({ Key: "rules.file", Value: "data/rules/rules" });
|
||||||
|
this.values.push({ Key: "rules.access.label", Value: "Verify" });
|
||||||
|
|
||||||
// Channels
|
// Channels
|
||||||
this.values.push({ Key: "channels.logs.message", Value: "message-logs" });
|
this.values.push({ Key: "channels.logs.message", Value: "message-logs" });
|
||||||
|
|
8
src/contracts/ButtonEventItem.ts
Normal file
8
src/contracts/ButtonEventItem.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import { ButtonEvent } from "../type/buttonEvent";
|
||||||
|
|
||||||
|
interface ButtonEventItem {
|
||||||
|
ButtonId: string,
|
||||||
|
Event: ButtonEvent,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ButtonEventItem;
|
|
@ -37,6 +37,9 @@ import MessageDelete from "./events/MessageEvents/MessageDelete";
|
||||||
import MessageUpdate from "./events/MessageEvents/MessageUpdate";
|
import MessageUpdate from "./events/MessageEvents/MessageUpdate";
|
||||||
import MessageCreate from "./events/MessageEvents/MessageCreate";
|
import MessageCreate from "./events/MessageEvents/MessageCreate";
|
||||||
|
|
||||||
|
// Button Event Imports
|
||||||
|
import Verify from "./buttonEvents/verify";
|
||||||
|
|
||||||
export default class Registry {
|
export default class Registry {
|
||||||
public static RegisterCommands() {
|
public static RegisterCommands() {
|
||||||
CoreClient.RegisterCommand("about", new About());
|
CoreClient.RegisterCommand("about", new About());
|
||||||
|
@ -84,4 +87,8 @@ export default class Registry {
|
||||||
CoreClient.RegisterEvent(EventType.MessageUpdate, MessageUpdate);
|
CoreClient.RegisterEvent(EventType.MessageUpdate, MessageUpdate);
|
||||||
CoreClient.RegisterEvent(EventType.MessageCreate, MessageCreate);
|
CoreClient.RegisterEvent(EventType.MessageCreate, MessageCreate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static RegisterButtonEvents() {
|
||||||
|
CoreClient.RegisterButtonEvent("verify", new Verify());
|
||||||
|
}
|
||||||
}
|
}
|
5
src/type/buttonEvent.ts
Normal file
5
src/type/buttonEvent.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { ButtonInteraction } from "discord.js";
|
||||||
|
|
||||||
|
export abstract class ButtonEvent {
|
||||||
|
abstract execute(interaction: ButtonInteraction): Promise<void>;
|
||||||
|
}
|
|
@ -37,5 +37,6 @@ const client = new CoreClient([
|
||||||
|
|
||||||
registry.RegisterCommands();
|
registry.RegisterCommands();
|
||||||
registry.RegisterEvents();
|
registry.RegisterEvents();
|
||||||
|
registry.RegisterButtonEvents();
|
||||||
|
|
||||||
client.start();
|
client.start();
|
||||||
|
|
Loading…
Reference in a new issue