mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-03-15 05:41:51 +00:00
chore: remove old button roles implementation
This commit is contained in:
parent
3fe71b3e27
commit
05334a772f
12 changed files with 50 additions and 450 deletions
|
@ -0,0 +1,49 @@
|
||||||
|
import { MigrationInterface, QueryRunner, Table } from "typeorm";
|
||||||
|
|
||||||
|
export class RemoveButtonRolesTable1650721020704 implements MigrationInterface {
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.dropTable("button_roles");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.createTable(
|
||||||
|
new Table({
|
||||||
|
name: "button_roles",
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: "guild_id",
|
||||||
|
type: "bigint",
|
||||||
|
isPrimary: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "channel_id",
|
||||||
|
type: "bigint",
|
||||||
|
isPrimary: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "message_id",
|
||||||
|
type: "bigint",
|
||||||
|
isPrimary: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "button_id",
|
||||||
|
type: "varchar",
|
||||||
|
length: "100",
|
||||||
|
isPrimary: true,
|
||||||
|
isUnique: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "button_group",
|
||||||
|
type: "varchar",
|
||||||
|
length: "100",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "button_name",
|
||||||
|
type: "varchar",
|
||||||
|
length: "100",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,29 +1,20 @@
|
||||||
import { PluginOptions } from "knub";
|
import { PluginOptions } from "knub";
|
||||||
import { ConfigPreprocessorFn } from "knub/dist/config/configTypes";
|
|
||||||
import { GuildButtonRoles } from "../../data/GuildButtonRoles";
|
|
||||||
import { GuildReactionRoles } from "../../data/GuildReactionRoles";
|
import { GuildReactionRoles } from "../../data/GuildReactionRoles";
|
||||||
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
|
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
|
||||||
import { Queue } from "../../Queue";
|
import { Queue } from "../../Queue";
|
||||||
import { isValidSnowflake } from "../../utils";
|
|
||||||
import { StrictValidationError } from "../../validatorUtils";
|
|
||||||
import { LogsPlugin } from "../Logs/LogsPlugin";
|
import { LogsPlugin } from "../Logs/LogsPlugin";
|
||||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||||
import { ClearReactionRolesCmd } from "./commands/ClearReactionRolesCmd";
|
import { ClearReactionRolesCmd } from "./commands/ClearReactionRolesCmd";
|
||||||
import { InitReactionRolesCmd } from "./commands/InitReactionRolesCmd";
|
import { InitReactionRolesCmd } from "./commands/InitReactionRolesCmd";
|
||||||
import { PostButtonRolesCmd } from "./commands/PostButtonRolesCmd";
|
|
||||||
import { RefreshReactionRolesCmd } from "./commands/RefreshReactionRolesCmd";
|
import { RefreshReactionRolesCmd } from "./commands/RefreshReactionRolesCmd";
|
||||||
import { AddReactionRoleEvt } from "./events/AddReactionRoleEvt";
|
import { AddReactionRoleEvt } from "./events/AddReactionRoleEvt";
|
||||||
import { ButtonInteractionEvt } from "./events/ButtonInteractionEvt";
|
|
||||||
import { MessageDeletedEvt } from "./events/MessageDeletedEvt";
|
import { MessageDeletedEvt } from "./events/MessageDeletedEvt";
|
||||||
import { ConfigSchema, ReactionRolesPluginType } from "./types";
|
import { ConfigSchema, ReactionRolesPluginType } from "./types";
|
||||||
import { autoRefreshLoop } from "./util/autoRefreshLoop";
|
|
||||||
import { getRowCount } from "./util/splitButtonsIntoRows";
|
|
||||||
|
|
||||||
const MIN_AUTO_REFRESH = 1000 * 60 * 15; // 15min minimum, let's not abuse the API
|
const MIN_AUTO_REFRESH = 1000 * 60 * 15; // 15min minimum, let's not abuse the API
|
||||||
|
|
||||||
const defaultOptions: PluginOptions<ReactionRolesPluginType> = {
|
const defaultOptions: PluginOptions<ReactionRolesPluginType> = {
|
||||||
config: {
|
config: {
|
||||||
button_groups: {},
|
|
||||||
auto_refresh_interval: MIN_AUTO_REFRESH,
|
auto_refresh_interval: MIN_AUTO_REFRESH,
|
||||||
remove_user_reactions: true,
|
remove_user_reactions: true,
|
||||||
|
|
||||||
|
@ -40,67 +31,6 @@ const defaultOptions: PluginOptions<ReactionRolesPluginType> = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const MAXIMUM_COMPONENT_ROWS = 5;
|
|
||||||
|
|
||||||
const configPreprocessor: ConfigPreprocessorFn<ReactionRolesPluginType> = (options) => {
|
|
||||||
if (options.config.button_groups) {
|
|
||||||
for (const [groupName, group] of Object.entries(options.config.button_groups)) {
|
|
||||||
const defaultButtonNames = Object.keys(group.default_buttons);
|
|
||||||
const defaultButtons = Object.values(group.default_buttons);
|
|
||||||
const menuNames = Object.keys(group.button_menus ?? []);
|
|
||||||
|
|
||||||
const defaultBtnRowCount = getRowCount(defaultButtons);
|
|
||||||
if (defaultBtnRowCount > MAXIMUM_COMPONENT_ROWS || defaultBtnRowCount === 0) {
|
|
||||||
throw new StrictValidationError([
|
|
||||||
`Invalid row count for default_buttons: You currently have ${defaultBtnRowCount}, the maximum is 5. A new row is started automatically each 5 consecutive buttons.`,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < defaultButtons.length; i++) {
|
|
||||||
const defBtn = defaultButtons[i];
|
|
||||||
if (!menuNames.includes(defBtn.role_or_menu) && !isValidSnowflake(defBtn.role_or_menu)) {
|
|
||||||
throw new StrictValidationError([
|
|
||||||
`Invalid value for default_buttons/${defaultButtonNames[i]}/role_or_menu: ${defBtn.role_or_menu} is neither an existing menu nor a valid snowflake.`,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (!defBtn.label && !defBtn.emoji) {
|
|
||||||
throw new StrictValidationError([
|
|
||||||
`Invalid values for default_buttons/${defaultButtonNames[i]}/(label|emoji): Must have label, emoji or both set for the button to be valid.`,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [menuName, menuButtonEntries] of Object.entries(group.button_menus ?? [])) {
|
|
||||||
const menuButtonNames = Object.keys(menuButtonEntries);
|
|
||||||
const menuButtons = Object.values(menuButtonEntries);
|
|
||||||
|
|
||||||
const menuButtonRowCount = getRowCount(menuButtons);
|
|
||||||
if (menuButtonRowCount > MAXIMUM_COMPONENT_ROWS || menuButtonRowCount === 0) {
|
|
||||||
throw new StrictValidationError([
|
|
||||||
`Invalid row count for button_menus/${menuName}: You currently have ${menuButtonRowCount}, the maximum is 5. A new row is started automatically each 5 consecutive buttons.`,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < menuButtons.length; i++) {
|
|
||||||
const menuBtn = menuButtons[i];
|
|
||||||
if (!menuNames.includes(menuBtn.role_or_menu) && !isValidSnowflake(menuBtn.role_or_menu)) {
|
|
||||||
throw new StrictValidationError([
|
|
||||||
`Invalid value for button_menus/${menuButtonNames[i]}/role_or_menu: ${menuBtn.role_or_menu} is neither an existing menu nor a valid snowflake.`,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
if (!menuBtn.label && !menuBtn.emoji) {
|
|
||||||
throw new StrictValidationError([
|
|
||||||
`Invalid values for default_buttons/${defaultButtonNames[i]}/(label|emoji): Must have label, emoji or both set for the button to be valid.`,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return options;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ReactionRolesPlugin = zeppelinGuildPlugin<ReactionRolesPluginType>()({
|
export const ReactionRolesPlugin = zeppelinGuildPlugin<ReactionRolesPluginType>()({
|
||||||
name: "reaction_roles",
|
name: "reaction_roles",
|
||||||
showInDocs: true,
|
showInDocs: true,
|
||||||
|
@ -117,23 +47,19 @@ export const ReactionRolesPlugin = zeppelinGuildPlugin<ReactionRolesPluginType>(
|
||||||
RefreshReactionRolesCmd,
|
RefreshReactionRolesCmd,
|
||||||
ClearReactionRolesCmd,
|
ClearReactionRolesCmd,
|
||||||
InitReactionRolesCmd,
|
InitReactionRolesCmd,
|
||||||
// PostButtonRolesCmd,
|
|
||||||
],
|
],
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
events: [
|
events: [
|
||||||
AddReactionRoleEvt,
|
AddReactionRoleEvt,
|
||||||
// ButtonInteractionEvt,
|
|
||||||
MessageDeletedEvt,
|
MessageDeletedEvt,
|
||||||
],
|
],
|
||||||
configPreprocessor,
|
|
||||||
|
|
||||||
beforeLoad(pluginData) {
|
beforeLoad(pluginData) {
|
||||||
const { state, guild } = pluginData;
|
const { state, guild } = pluginData;
|
||||||
|
|
||||||
state.reactionRoles = GuildReactionRoles.getGuildInstance(guild.id);
|
state.reactionRoles = GuildReactionRoles.getGuildInstance(guild.id);
|
||||||
state.savedMessages = GuildSavedMessages.getGuildInstance(guild.id);
|
state.savedMessages = GuildSavedMessages.getGuildInstance(guild.id);
|
||||||
state.buttonRoles = GuildButtonRoles.getGuildInstance(guild.id);
|
|
||||||
state.reactionRemoveQueue = new Queue();
|
state.reactionRemoveQueue = new Queue();
|
||||||
state.roleChangeQueue = new Queue();
|
state.roleChangeQueue = new Queue();
|
||||||
state.pendingRoleChanges = new Map();
|
state.pendingRoleChanges = new Map();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message, Snowflake } from "discord.js";
|
import { Message } from "discord.js";
|
||||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||||
import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
import { sendErrorMessage, sendSuccessMessage } from "../../../pluginUtils";
|
||||||
import { isDiscordAPIError } from "../../../utils";
|
import { isDiscordAPIError } from "../../../utils";
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
import { createHash } from "crypto";
|
|
||||||
import { MessageButton, Snowflake } from "discord.js";
|
|
||||||
import moment from "moment";
|
|
||||||
import { sendErrorMessage, sendSuccessMessage } from "src/pluginUtils";
|
|
||||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
|
||||||
import { reactionRolesCmd } from "../types";
|
|
||||||
import { splitButtonsIntoRows } from "../util/splitButtonsIntoRows";
|
|
||||||
|
|
||||||
export const PostButtonRolesCmd = reactionRolesCmd({
|
|
||||||
trigger: "reaction_roles post",
|
|
||||||
permission: "can_manage",
|
|
||||||
|
|
||||||
signature: {
|
|
||||||
channel: ct.textChannel(),
|
|
||||||
buttonGroup: ct.string(),
|
|
||||||
},
|
|
||||||
|
|
||||||
async run({ message: msg, args, pluginData }) {
|
|
||||||
const cfg = pluginData.config.get();
|
|
||||||
if (!cfg.button_groups) {
|
|
||||||
sendErrorMessage(pluginData, msg.channel, "No button groups defined in config");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const group = cfg.button_groups[args.buttonGroup];
|
|
||||||
|
|
||||||
if (!group) {
|
|
||||||
sendErrorMessage(pluginData, msg.channel, `No button group matches the name **${args.buttonGroup}**`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const buttons: MessageButton[] = [];
|
|
||||||
const toInsert: Array<{ customId; buttonGroup; buttonName }> = [];
|
|
||||||
for (const [buttonName, button] of Object.entries(group.default_buttons)) {
|
|
||||||
const customId = createHash("md5").update(`${buttonName}${moment.utc().valueOf()}`).digest("hex");
|
|
||||||
|
|
||||||
const btn = new MessageButton()
|
|
||||||
.setLabel(button.label ?? "")
|
|
||||||
.setStyle(button.style ?? "PRIMARY")
|
|
||||||
.setCustomId(customId)
|
|
||||||
.setDisabled(button.disabled ?? false);
|
|
||||||
|
|
||||||
if (button.emoji) {
|
|
||||||
const emo = pluginData.client.emojis.resolve(button.emoji as Snowflake) ?? button.emoji;
|
|
||||||
btn.setEmoji(emo);
|
|
||||||
}
|
|
||||||
|
|
||||||
buttons.push(btn);
|
|
||||||
toInsert.push({ customId, buttonGroup: args.buttonGroup, buttonName });
|
|
||||||
}
|
|
||||||
const rows = splitButtonsIntoRows(buttons, Object.values(group.default_buttons)); // new MessageActionRow().addComponents(buttons);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const newMsg = await args.channel.send({ content: group.message, components: rows });
|
|
||||||
|
|
||||||
for (const btn of toInsert) {
|
|
||||||
await pluginData.state.buttonRoles.add(
|
|
||||||
args.channel.id,
|
|
||||||
newMsg.id,
|
|
||||||
btn.customId,
|
|
||||||
btn.buttonGroup,
|
|
||||||
btn.buttonName,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
sendErrorMessage(pluginData, msg.channel, `Error trying to post message: ${e}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await sendSuccessMessage(pluginData, msg.channel, `Successfully posted message in <#${args.channel.id}>`);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,86 +0,0 @@
|
||||||
import { MessageComponentInteraction } from "discord.js";
|
|
||||||
import humanizeDuration from "humanize-duration";
|
|
||||||
import moment from "moment";
|
|
||||||
import { LogType } from "src/data/LogType";
|
|
||||||
import { logger } from "src/logger";
|
|
||||||
import { LogsPlugin } from "src/plugins/Logs/LogsPlugin";
|
|
||||||
import { MINUTES } from "src/utils";
|
|
||||||
import { idToTimestamp } from "src/utils/idToTimestamp";
|
|
||||||
import { reactionRolesEvt } from "../types";
|
|
||||||
import { handleModifyRole, handleOpenMenu } from "../util/buttonActionHandlers";
|
|
||||||
import { BUTTON_CONTEXT_SEPARATOR, resolveStatefulCustomId } from "../util/buttonCustomIdFunctions";
|
|
||||||
import { ButtonMenuActions } from "../util/buttonMenuActions";
|
|
||||||
|
|
||||||
const BUTTON_INVALIDATION_TIME = 15 * MINUTES;
|
|
||||||
|
|
||||||
export const ButtonInteractionEvt = reactionRolesEvt({
|
|
||||||
event: "interactionCreate",
|
|
||||||
|
|
||||||
async listener(meta) {
|
|
||||||
const int = meta.args.interaction;
|
|
||||||
if (!int.isMessageComponent()) return;
|
|
||||||
|
|
||||||
const cfg = meta.pluginData.config.get();
|
|
||||||
const split = int.customId.split(BUTTON_CONTEXT_SEPARATOR);
|
|
||||||
const context = (await resolveStatefulCustomId(meta.pluginData, int.customId)) ?? {
|
|
||||||
groupName: split[0],
|
|
||||||
action: split[1],
|
|
||||||
roleOrMenu: split[2],
|
|
||||||
stateless: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (context.stateless) {
|
|
||||||
if (context.roleOrMenu == null) {
|
|
||||||
// Not reaction from this plugin
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const timeSinceCreation = moment.utc().valueOf() - idToTimestamp(int.message.id)!;
|
|
||||||
if (timeSinceCreation >= BUTTON_INVALIDATION_TIME) {
|
|
||||||
sendEphemeralReply(
|
|
||||||
int,
|
|
||||||
`Sorry, but these buttons are invalid because they are older than ${humanizeDuration(
|
|
||||||
BUTTON_INVALIDATION_TIME,
|
|
||||||
)}.\nIf the menu is still available, open it again to assign yourself roles!`,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const group = cfg.button_groups[context.groupName];
|
|
||||||
if (!group) {
|
|
||||||
await sendEphemeralReply(int, `A configuration error was encountered, please contact the Administrators!`);
|
|
||||||
meta.pluginData.getPlugin(LogsPlugin).logBotAlert({
|
|
||||||
body: `**A configuration error occurred** on buttons for message ${int.message.id}, group **${context.groupName}** not found in config`,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that detected action is known by us
|
|
||||||
if (!(<any>Object).values(ButtonMenuActions).includes(context.action)) {
|
|
||||||
await sendEphemeralReply(int, `A internal error was encountered, please contact the Administrators!`);
|
|
||||||
meta.pluginData.getPlugin(LogsPlugin).logBotAlert({
|
|
||||||
body: `**A internal error occurred** on buttons for message ${int.message.id}, action **${context.action}** is not known`,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.action === ButtonMenuActions.MODIFY_ROLE) {
|
|
||||||
await handleModifyRole(meta.pluginData, int, group, context);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.action === ButtonMenuActions.OPEN_MENU) {
|
|
||||||
await handleOpenMenu(meta.pluginData, int, group, context);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.warn(
|
|
||||||
`Action ${context.action} on button ${int.customId} (Guild: ${int.guildId}, Channel: ${int.channelId}) is unknown!`,
|
|
||||||
);
|
|
||||||
await sendEphemeralReply(int, `A internal error was encountered, please contact the Administrators!`);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
async function sendEphemeralReply(interaction: MessageComponentInteraction, message: string) {
|
|
||||||
await interaction.reply({ content: message, ephemeral: true });
|
|
||||||
}
|
|
|
@ -8,7 +8,6 @@ export const MessageDeletedEvt = reactionRolesEvt({
|
||||||
async listener(meta) {
|
async listener(meta) {
|
||||||
const pluginData = meta.pluginData;
|
const pluginData = meta.pluginData;
|
||||||
|
|
||||||
await pluginData.state.buttonRoles.removeAllForMessageId(meta.args.message.id);
|
|
||||||
await pluginData.state.reactionRoles.removeFromMessage(meta.args.message.id);
|
await pluginData.state.reactionRoles.removeFromMessage(meta.args.message.id);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,40 +1,10 @@
|
||||||
import * as t from "io-ts";
|
import * as t from "io-ts";
|
||||||
import { BasePluginType, typedGuildCommand, typedGuildEventListener } from "knub";
|
import { BasePluginType, typedGuildCommand, typedGuildEventListener } from "knub";
|
||||||
import { GuildButtonRoles } from "src/data/GuildButtonRoles";
|
|
||||||
import { GuildReactionRoles } from "../../data/GuildReactionRoles";
|
import { GuildReactionRoles } from "../../data/GuildReactionRoles";
|
||||||
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
|
import { GuildSavedMessages } from "../../data/GuildSavedMessages";
|
||||||
import { Queue } from "../../Queue";
|
import { Queue } from "../../Queue";
|
||||||
import { tNullable } from "../../utils";
|
|
||||||
|
|
||||||
// These need to be updated every time discord adds/removes a style,
|
|
||||||
// but i cant figure out how to import MessageButtonStyles at runtime
|
|
||||||
enum ButtonStyles {
|
|
||||||
PRIMARY = 1,
|
|
||||||
SECONDARY = 2,
|
|
||||||
SUCCESS = 3,
|
|
||||||
DANGER = 4,
|
|
||||||
// LINK = 5, We do not want users to create link buttons, but it would be style 5
|
|
||||||
}
|
|
||||||
|
|
||||||
const ButtonOpts = t.type({
|
|
||||||
label: tNullable(t.string),
|
|
||||||
emoji: tNullable(t.string),
|
|
||||||
role_or_menu: t.string,
|
|
||||||
style: tNullable(t.keyof(ButtonStyles)), // https://discord.js.org/#/docs/main/master/typedef/MessageButtonStyle
|
|
||||||
disabled: tNullable(t.boolean),
|
|
||||||
end_row: tNullable(t.boolean),
|
|
||||||
});
|
|
||||||
export type TButtonOpts = t.TypeOf<typeof ButtonOpts>;
|
|
||||||
|
|
||||||
const ButtonPairOpts = t.type({
|
|
||||||
message: t.string,
|
|
||||||
default_buttons: t.record(t.string, ButtonOpts),
|
|
||||||
button_menus: tNullable(t.record(t.string, t.record(t.string, ButtonOpts))),
|
|
||||||
});
|
|
||||||
export type TButtonPairOpts = t.TypeOf<typeof ButtonPairOpts>;
|
|
||||||
|
|
||||||
export const ConfigSchema = t.type({
|
export const ConfigSchema = t.type({
|
||||||
button_groups: t.record(t.string, ButtonPairOpts),
|
|
||||||
auto_refresh_interval: t.number,
|
auto_refresh_interval: t.number,
|
||||||
remove_user_reactions: t.boolean,
|
remove_user_reactions: t.boolean,
|
||||||
can_manage: t.boolean,
|
can_manage: t.boolean,
|
||||||
|
@ -61,7 +31,6 @@ export interface ReactionRolesPluginType extends BasePluginType {
|
||||||
state: {
|
state: {
|
||||||
reactionRoles: GuildReactionRoles;
|
reactionRoles: GuildReactionRoles;
|
||||||
savedMessages: GuildSavedMessages;
|
savedMessages: GuildSavedMessages;
|
||||||
buttonRoles: GuildButtonRoles;
|
|
||||||
|
|
||||||
reactionRemoveQueue: Queue;
|
reactionRemoveQueue: Queue;
|
||||||
roleChangeQueue: Queue;
|
roleChangeQueue: Queue;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Snowflake, TextChannel } from "discord.js";
|
import { Snowflake, TextChannel } from "discord.js";
|
||||||
import { GuildPluginData } from "knub";
|
import { GuildPluginData } from "knub";
|
||||||
import { ReactionRole } from "../../../data/entities/ReactionRole";
|
import { ReactionRole } from "../../../data/entities/ReactionRole";
|
||||||
import { LogType } from "../../../data/LogType";
|
|
||||||
import { isDiscordAPIError, sleep } from "../../../utils";
|
import { isDiscordAPIError, sleep } from "../../../utils";
|
||||||
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
import { LogsPlugin } from "../../Logs/LogsPlugin";
|
||||||
import { ReactionRolesPluginType } from "../types";
|
import { ReactionRolesPluginType } from "../types";
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
import { MessageButton, MessageComponentInteraction, Snowflake } from "discord.js";
|
|
||||||
import { GuildPluginData } from "knub";
|
|
||||||
import { LogType } from "../../../data/LogType";
|
|
||||||
import { LogsPlugin } from "../../../plugins/Logs/LogsPlugin";
|
|
||||||
import { ReactionRolesPluginType, TButtonPairOpts } from "../types";
|
|
||||||
import { generateStatelessCustomId } from "./buttonCustomIdFunctions";
|
|
||||||
import { splitButtonsIntoRows } from "./splitButtonsIntoRows";
|
|
||||||
|
|
||||||
export async function handleOpenMenu(
|
|
||||||
pluginData: GuildPluginData<ReactionRolesPluginType>,
|
|
||||||
int: MessageComponentInteraction,
|
|
||||||
group: TButtonPairOpts,
|
|
||||||
context,
|
|
||||||
) {
|
|
||||||
const menuButtons: MessageButton[] = [];
|
|
||||||
if (group.button_menus == null) {
|
|
||||||
await int.reply({
|
|
||||||
content: `A configuration error was encountered, please contact the Administrators!`,
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
pluginData.getPlugin(LogsPlugin).logBotAlert({
|
|
||||||
body: `**A configuration error occurred** on buttons for message ${int.message.id}, no menus found in config`,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const menuButton of Object.values(group.button_menus[context.roleOrMenu])) {
|
|
||||||
const customId = await generateStatelessCustomId(pluginData, context.groupName, menuButton.role_or_menu);
|
|
||||||
|
|
||||||
const btn = new MessageButton()
|
|
||||||
.setLabel(menuButton.label ?? "")
|
|
||||||
.setStyle("PRIMARY")
|
|
||||||
.setCustomId(customId)
|
|
||||||
.setDisabled(menuButton.disabled ?? false);
|
|
||||||
|
|
||||||
if (menuButton.emoji) {
|
|
||||||
const emo = pluginData.client.emojis.resolve(menuButton.emoji as Snowflake) ?? menuButton.emoji;
|
|
||||||
btn.setEmoji(emo);
|
|
||||||
}
|
|
||||||
menuButtons.push(btn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (menuButtons.length === 0) {
|
|
||||||
await int.reply({
|
|
||||||
content: `A configuration error was encountered, please contact the Administrators!`,
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
pluginData.getPlugin(LogsPlugin).logBotAlert({
|
|
||||||
body: `**A configuration error occurred** on buttons for message ${int.message.id}, menu **${context.roleOrMenu}** not found in config`,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const rows = splitButtonsIntoRows(menuButtons, Object.values(group.button_menus[context.roleOrMenu])); // new MessageActionRow().addComponents(menuButtons);
|
|
||||||
|
|
||||||
int.reply({ content: `Click to add/remove a role`, components: rows, ephemeral: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function handleModifyRole(
|
|
||||||
pluginData: GuildPluginData<ReactionRolesPluginType>,
|
|
||||||
int: MessageComponentInteraction,
|
|
||||||
group: TButtonPairOpts,
|
|
||||||
context,
|
|
||||||
) {
|
|
||||||
const role = await pluginData.guild.roles.fetch(context.roleOrMenu);
|
|
||||||
if (!role) {
|
|
||||||
await int.reply({
|
|
||||||
content: `A configuration error was encountered, please contact the Administrators!`,
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
pluginData.getPlugin(LogsPlugin).logBotAlert({
|
|
||||||
body: `**A configuration error occurred** on buttons for message ${int.message.id}, role **${context.roleOrMenu}** not found on server`,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const member = await pluginData.guild.members.fetch(int.user.id);
|
|
||||||
try {
|
|
||||||
if (member.roles.cache.has(role.id)) {
|
|
||||||
await member.roles.remove(role, `Button Roles on message ${int.message.id}`);
|
|
||||||
await int.reply({ content: `Role **${role.name}** removed`, ephemeral: true });
|
|
||||||
} else {
|
|
||||||
await member.roles.add(role, `Button Roles on message ${int.message.id}`);
|
|
||||||
await int.reply({ content: `Role **${role.name}** added`, ephemeral: true });
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
await int.reply({
|
|
||||||
content: "A configuration error was encountered, please contact the Administrators!",
|
|
||||||
ephemeral: true,
|
|
||||||
});
|
|
||||||
pluginData.getPlugin(LogsPlugin).logBotAlert({
|
|
||||||
body: `**A configuration error occurred** on buttons for message ${int.message.id}, error: ${e}. We might be missing permissions!`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
import { Snowflake } from "discord.js";
|
|
||||||
import { GuildPluginData } from "knub";
|
|
||||||
import { ReactionRolesPluginType } from "../types";
|
|
||||||
import { ButtonMenuActions } from "./buttonMenuActions";
|
|
||||||
|
|
||||||
export const BUTTON_CONTEXT_SEPARATOR = ":rb:";
|
|
||||||
|
|
||||||
export async function getButtonAction(pluginData: GuildPluginData<ReactionRolesPluginType>, roleOrMenu: string) {
|
|
||||||
if (await pluginData.guild.roles.fetch(roleOrMenu as Snowflake).catch(() => false)) {
|
|
||||||
return ButtonMenuActions.MODIFY_ROLE;
|
|
||||||
} else {
|
|
||||||
return ButtonMenuActions.OPEN_MENU;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function generateStatelessCustomId(
|
|
||||||
pluginData: GuildPluginData<ReactionRolesPluginType>,
|
|
||||||
groupName: string,
|
|
||||||
roleOrMenu: string,
|
|
||||||
) {
|
|
||||||
let id = groupName + BUTTON_CONTEXT_SEPARATOR;
|
|
||||||
|
|
||||||
id += `${await getButtonAction(pluginData, roleOrMenu)}${BUTTON_CONTEXT_SEPARATOR}${roleOrMenu}`;
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function resolveStatefulCustomId(pluginData: GuildPluginData<ReactionRolesPluginType>, id: string) {
|
|
||||||
const button = await pluginData.state.buttonRoles.getForButtonId(id);
|
|
||||||
|
|
||||||
if (button) {
|
|
||||||
const group = pluginData.config.get().button_groups[button.button_group];
|
|
||||||
if (!group) return null;
|
|
||||||
const cfgButton = group.default_buttons[button.button_name];
|
|
||||||
|
|
||||||
return {
|
|
||||||
groupName: button.button_group,
|
|
||||||
action: await getButtonAction(pluginData, cfgButton.role_or_menu),
|
|
||||||
roleOrMenu: cfgButton.role_or_menu,
|
|
||||||
stateless: false,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
export enum ButtonMenuActions {
|
|
||||||
OPEN_MENU = "goto",
|
|
||||||
MODIFY_ROLE = "grant",
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
import { MessageActionRow, MessageButton } from "discord.js";
|
|
||||||
import { TButtonOpts } from "../types";
|
|
||||||
|
|
||||||
export function splitButtonsIntoRows(actualButtons: MessageButton[], configButtons: TButtonOpts[]): MessageActionRow[] {
|
|
||||||
const rows: MessageActionRow[] = [];
|
|
||||||
let curRow = new MessageActionRow();
|
|
||||||
let consecutive = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < actualButtons.length; i++) {
|
|
||||||
const aBtn = actualButtons[i];
|
|
||||||
const cBtn = configButtons[i];
|
|
||||||
|
|
||||||
curRow.addComponents(aBtn);
|
|
||||||
if (((consecutive + 1) % 5 === 0 || cBtn.end_row) && i + 1 < actualButtons.length) {
|
|
||||||
rows.push(curRow);
|
|
||||||
curRow = new MessageActionRow();
|
|
||||||
consecutive = 0;
|
|
||||||
} else {
|
|
||||||
consecutive++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (curRow.components.length >= 1) rows.push(curRow);
|
|
||||||
return rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getRowCount(configButtons: TButtonOpts[]): number {
|
|
||||||
let count = 1;
|
|
||||||
let consecutive = 0;
|
|
||||||
for (let i = 0; i < configButtons.length; i++) {
|
|
||||||
const cBtn = configButtons[i];
|
|
||||||
|
|
||||||
if (((consecutive + 1) % 5 === 0 || cBtn.end_row) && i + 1 < configButtons.length) {
|
|
||||||
count++;
|
|
||||||
consecutive = 0;
|
|
||||||
} else {
|
|
||||||
consecutive++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue