mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-05-14 05:45:02 +00:00
feat: add clean message context menu command
This commit is contained in:
parent
6689f91a6a
commit
740aa39cd5
5 changed files with 107 additions and 32 deletions
|
@ -7,6 +7,7 @@ import {
|
||||||
Message,
|
Message,
|
||||||
MessageCreateOptions,
|
MessageCreateOptions,
|
||||||
MessageMentionOptions,
|
MessageMentionOptions,
|
||||||
|
ModalSubmitInteraction,
|
||||||
PermissionsBitField,
|
PermissionsBitField,
|
||||||
TextBasedChannel,
|
TextBasedChannel,
|
||||||
} from "discord.js";
|
} from "discord.js";
|
||||||
|
@ -104,6 +105,7 @@ export async function sendSuccessMessage(
|
||||||
channel: TextBasedChannel,
|
channel: TextBasedChannel,
|
||||||
body: string,
|
body: string,
|
||||||
allowedMentions?: MessageMentionOptions,
|
allowedMentions?: MessageMentionOptions,
|
||||||
|
responseInteraction?: ModalSubmitInteraction,
|
||||||
): Promise<Message | undefined> {
|
): Promise<Message | undefined> {
|
||||||
const emoji = pluginData.fullConfig.success_emoji || undefined;
|
const emoji = pluginData.fullConfig.success_emoji || undefined;
|
||||||
const formattedBody = successMessage(body, emoji);
|
const formattedBody = successMessage(body, emoji);
|
||||||
|
@ -111,13 +113,19 @@ export async function sendSuccessMessage(
|
||||||
? { content: formattedBody, allowedMentions }
|
? { content: formattedBody, allowedMentions }
|
||||||
: { content: formattedBody };
|
: { content: formattedBody };
|
||||||
|
|
||||||
return channel
|
if (responseInteraction) {
|
||||||
.send({ ...content }) // Force line break
|
await responseInteraction
|
||||||
.catch((err) => {
|
.editReply({ content: formattedBody, embeds: [], components: [] })
|
||||||
const channelInfo = "guild" in channel ? `${channel.id} (${channel.guild.id})` : channel.id;
|
.catch((err) => logger.error(`Interaction reply failed: ${err}`));
|
||||||
logger.warn(`Failed to send success message to ${channelInfo}): ${err.code} ${err.message}`);
|
} else {
|
||||||
return undefined;
|
return channel
|
||||||
});
|
.send({ ...content }) // Force line break
|
||||||
|
.catch((err) => {
|
||||||
|
const channelInfo = "guild" in channel ? `${channel.id} (${channel.guild.id})` : channel.id;
|
||||||
|
logger.warn(`Failed to send success message to ${channelInfo}): ${err.code} ${err.message}`);
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sendErrorMessage(
|
export async function sendErrorMessage(
|
||||||
|
@ -125,6 +133,7 @@ export async function sendErrorMessage(
|
||||||
channel: TextBasedChannel,
|
channel: TextBasedChannel,
|
||||||
body: string,
|
body: string,
|
||||||
allowedMentions?: MessageMentionOptions,
|
allowedMentions?: MessageMentionOptions,
|
||||||
|
responseInteraction?: ModalSubmitInteraction,
|
||||||
): Promise<Message | undefined> {
|
): Promise<Message | undefined> {
|
||||||
const emoji = pluginData.fullConfig.error_emoji || undefined;
|
const emoji = pluginData.fullConfig.error_emoji || undefined;
|
||||||
const formattedBody = errorMessage(body, emoji);
|
const formattedBody = errorMessage(body, emoji);
|
||||||
|
@ -132,13 +141,19 @@ export async function sendErrorMessage(
|
||||||
? { content: formattedBody, allowedMentions }
|
? { content: formattedBody, allowedMentions }
|
||||||
: { content: formattedBody };
|
: { content: formattedBody };
|
||||||
|
|
||||||
return channel
|
if (responseInteraction) {
|
||||||
.send({ ...content }) // Force line break
|
await responseInteraction
|
||||||
.catch((err) => {
|
.editReply({ content: formattedBody, embeds: [], components: [] })
|
||||||
const channelInfo = "guild" in channel ? `${channel.id} (${channel.guild.id})` : channel.id;
|
.catch((err) => logger.error(`Interaction reply failed: ${err}`));
|
||||||
logger.warn(`Failed to send error message to ${channelInfo}): ${err.code} ${err.message}`);
|
} else {
|
||||||
return undefined;
|
return channel
|
||||||
});
|
.send({ ...content }) // Force line break
|
||||||
|
.catch((err) => {
|
||||||
|
const channelInfo = "guild" in channel ? `${channel.id} (${channel.guild.id})` : channel.id;
|
||||||
|
logger.warn(`Failed to send error message to ${channelInfo}): ${err.code} ${err.message}`);
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBaseUrl(pluginData: AnyPluginData<any>) {
|
export function getBaseUrl(pluginData: AnyPluginData<any>) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { MutesPlugin } from "../Mutes/MutesPlugin";
|
||||||
import { UtilityPlugin } from "../Utility/UtilityPlugin";
|
import { UtilityPlugin } from "../Utility/UtilityPlugin";
|
||||||
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
import { zeppelinGuildPlugin } from "../ZeppelinPluginBlueprint";
|
||||||
import { BanCmd } from "./commands/BanUserCtxCmd";
|
import { BanCmd } from "./commands/BanUserCtxCmd";
|
||||||
|
import { CleanCmd } from "./commands/CleanMessageCtxCmd";
|
||||||
import { ModMenuCmd } from "./commands/ModMenuUserCtxCmd";
|
import { ModMenuCmd } from "./commands/ModMenuUserCtxCmd";
|
||||||
import { MuteCmd } from "./commands/MuteUserCtxCmd";
|
import { MuteCmd } from "./commands/MuteUserCtxCmd";
|
||||||
import { NoteCmd } from "./commands/NoteUserCtxCmd";
|
import { NoteCmd } from "./commands/NoteUserCtxCmd";
|
||||||
|
@ -49,7 +50,7 @@ export const ContextMenuPlugin = zeppelinGuildPlugin<ContextMenuPluginType>()({
|
||||||
|
|
||||||
defaultOptions,
|
defaultOptions,
|
||||||
|
|
||||||
contextMenuCommands: [ModMenuCmd, NoteCmd, WarnCmd, MuteCmd, BanCmd],
|
contextMenuCommands: [ModMenuCmd, NoteCmd, WarnCmd, MuteCmd, BanCmd, CleanCmd],
|
||||||
|
|
||||||
beforeLoad(pluginData) {
|
beforeLoad(pluginData) {
|
||||||
const { state, guild } = pluginData;
|
const { state, guild } = pluginData;
|
||||||
|
|
|
@ -1,4 +1,12 @@
|
||||||
import { ActionRowBuilder, ButtonInteraction, ModalBuilder, TextInputBuilder, TextInputStyle } from "discord.js";
|
import {
|
||||||
|
ActionRowBuilder,
|
||||||
|
Message,
|
||||||
|
MessageContextMenuCommandInteraction,
|
||||||
|
ModalBuilder,
|
||||||
|
ModalSubmitInteraction,
|
||||||
|
TextInputBuilder,
|
||||||
|
TextInputStyle,
|
||||||
|
} from "discord.js";
|
||||||
import { GuildPluginData } from "knub";
|
import { GuildPluginData } from "knub";
|
||||||
import { logger } from "../../../logger";
|
import { logger } from "../../../logger";
|
||||||
import { UtilityPlugin } from "../../../plugins/Utility/UtilityPlugin";
|
import { UtilityPlugin } from "../../../plugins/Utility/UtilityPlugin";
|
||||||
|
@ -9,7 +17,9 @@ export async function cleanAction(
|
||||||
pluginData: GuildPluginData<ContextMenuPluginType>,
|
pluginData: GuildPluginData<ContextMenuPluginType>,
|
||||||
amount: number,
|
amount: number,
|
||||||
target: string,
|
target: string,
|
||||||
interaction: ButtonInteraction,
|
targetMessage: Message,
|
||||||
|
targetChannel: string,
|
||||||
|
interaction: ModalSubmitInteraction,
|
||||||
) {
|
) {
|
||||||
const executingMember = await pluginData.guild.members.fetch(interaction.user.id);
|
const executingMember = await pluginData.guild.members.fetch(interaction.user.id);
|
||||||
const userCfg = await pluginData.config.getMatchingConfig({
|
const userCfg = await pluginData.config.getMatchingConfig({
|
||||||
|
@ -18,26 +28,27 @@ export async function cleanAction(
|
||||||
});
|
});
|
||||||
const utility = pluginData.getPlugin(UtilityPlugin);
|
const utility = pluginData.getPlugin(UtilityPlugin);
|
||||||
|
|
||||||
if (!userCfg.can_use || !(await utility.hasPermission(executingMember, interaction.channelId, "can_clean"))) {
|
if (!userCfg.can_use || !(await utility.hasPermission(executingMember, targetChannel, "can_clean"))) {
|
||||||
await interaction
|
await interaction
|
||||||
.editReply({ content: "Cannot clean: insufficient permissions", embeds: [], components: [] })
|
.editReply({ content: "Cannot clean: insufficient permissions", embeds: [], components: [] })
|
||||||
.catch((err) => logger.error(`Clean interaction reply failed: ${err}`));
|
.catch((err) => logger.error(`Clean interaction reply failed: ${err}`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement message cleaning
|
|
||||||
await interaction
|
await interaction
|
||||||
.editReply({
|
.editReply({
|
||||||
content: `TODO: Implementation incomplete`,
|
content: `Cleaning ${amount} messages from ${target}...`,
|
||||||
embeds: [],
|
embeds: [],
|
||||||
components: [],
|
components: [],
|
||||||
})
|
})
|
||||||
.catch((err) => logger.error(`Clean interaction reply failed: ${err}`));
|
.catch((err) => logger.error(`Clean interaction reply failed: ${err}`));
|
||||||
|
|
||||||
|
await utility.clean({ count: amount, channel: targetChannel, "response-interaction": interaction }, targetMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function launchCleanActionModal(
|
export async function launchCleanActionModal(
|
||||||
pluginData: GuildPluginData<ContextMenuPluginType>,
|
pluginData: GuildPluginData<ContextMenuPluginType>,
|
||||||
interaction: ButtonInteraction,
|
interaction: MessageContextMenuCommandInteraction,
|
||||||
target: string,
|
target: string,
|
||||||
) {
|
) {
|
||||||
const modalId = `${ModMenuActionType.CLEAN}:${interaction.id}`;
|
const modalId = `${ModMenuActionType.CLEAN}:${interaction.id}`;
|
||||||
|
@ -50,7 +61,9 @@ export async function launchCleanActionModal(
|
||||||
await interaction
|
await interaction
|
||||||
.awaitModalSubmit({ time: MODAL_TIMEOUT, filter: (i) => i.customId == modalId })
|
.awaitModalSubmit({ time: MODAL_TIMEOUT, filter: (i) => i.customId == modalId })
|
||||||
.then(async (submitted) => {
|
.then(async (submitted) => {
|
||||||
await submitted.deferUpdate().catch((err) => logger.error(`Clean interaction defer failed: ${err}`));
|
await submitted
|
||||||
|
.deferReply({ ephemeral: true })
|
||||||
|
.catch((err) => logger.error(`Clean interaction defer failed: ${err}`));
|
||||||
|
|
||||||
const amount = submitted.fields.getTextInputValue("amount");
|
const amount = submitted.fields.getTextInputValue("amount");
|
||||||
if (isNaN(Number(amount))) {
|
if (isNaN(Number(amount))) {
|
||||||
|
@ -60,7 +73,14 @@ export async function launchCleanActionModal(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await cleanAction(pluginData, Number(amount), target, interaction);
|
await cleanAction(
|
||||||
|
pluginData,
|
||||||
|
Number(amount),
|
||||||
|
target,
|
||||||
|
interaction.targetMessage,
|
||||||
|
interaction.channelId,
|
||||||
|
submitted,
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.catch((err) => logger.error(`Clean modal interaction failed: ${err}`));
|
.catch((err) => logger.error(`Clean modal interaction failed: ${err}`));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { PermissionFlagsBits } from "discord.js";
|
||||||
|
import { guildPluginMessageContextMenuCommand } from "knub";
|
||||||
|
import { launchCleanActionModal } from "../actions/clean";
|
||||||
|
|
||||||
|
export const CleanCmd = guildPluginMessageContextMenuCommand({
|
||||||
|
name: "Clean",
|
||||||
|
defaultMemberPermissions: PermissionFlagsBits.ManageMessages.toString(),
|
||||||
|
async run({ pluginData, interaction }) {
|
||||||
|
await launchCleanActionModal(pluginData, interaction, interaction.targetId);
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,4 +1,4 @@
|
||||||
import { Message, Snowflake, TextChannel, User } from "discord.js";
|
import { Message, ModalSubmitInteraction, Snowflake, TextChannel, User } from "discord.js";
|
||||||
import { GuildPluginData } from "knub";
|
import { GuildPluginData } from "knub";
|
||||||
import { allowTimeout } from "../../../RegExpRunner";
|
import { allowTimeout } from "../../../RegExpRunner";
|
||||||
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
import { commandTypeHelpers as ct } from "../../../commandTypes";
|
||||||
|
@ -77,17 +77,24 @@ export interface CleanArgs {
|
||||||
"has-invites"?: boolean;
|
"has-invites"?: boolean;
|
||||||
match?: RegExp;
|
match?: RegExp;
|
||||||
"to-id"?: string;
|
"to-id"?: string;
|
||||||
|
"response-interaction"?: ModalSubmitInteraction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function cleanCmd(pluginData: GuildPluginData<UtilityPluginType>, args: CleanArgs | any, msg) {
|
export async function cleanCmd(pluginData: GuildPluginData<UtilityPluginType>, args: CleanArgs | any, msg) {
|
||||||
if (args.count > MAX_CLEAN_COUNT || args.count <= 0) {
|
if (args.count > MAX_CLEAN_COUNT || args.count <= 0) {
|
||||||
sendErrorMessage(pluginData, msg.channel, `Clean count must be between 1 and ${MAX_CLEAN_COUNT}`);
|
sendErrorMessage(
|
||||||
|
pluginData,
|
||||||
|
msg.channel,
|
||||||
|
`Clean count must be between 1 and ${MAX_CLEAN_COUNT}`,
|
||||||
|
undefined,
|
||||||
|
args["response-interaction"],
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetChannel = args.channel ? pluginData.guild.channels.cache.get(args.channel as Snowflake) : msg.channel;
|
const targetChannel = args.channel ? pluginData.guild.channels.cache.get(args.channel as Snowflake) : msg.channel;
|
||||||
if (!targetChannel?.isTextBased()) {
|
if (!targetChannel?.isTextBased()) {
|
||||||
sendErrorMessage(pluginData, msg.channel, `Invalid channel specified`);
|
sendErrorMessage(pluginData, msg.channel, `Invalid channel specified`, undefined, args["response-interaction"]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,12 +106,21 @@ export async function cleanCmd(pluginData: GuildPluginData<UtilityPluginType>, a
|
||||||
categoryId: targetChannel.parentId,
|
categoryId: targetChannel.parentId,
|
||||||
});
|
});
|
||||||
if (configForTargetChannel.can_clean !== true) {
|
if (configForTargetChannel.can_clean !== true) {
|
||||||
sendErrorMessage(pluginData, msg.channel, `Missing permissions to use clean on that channel`);
|
sendErrorMessage(
|
||||||
|
pluginData,
|
||||||
|
msg.channel,
|
||||||
|
`Missing permissions to use clean on that channel`,
|
||||||
|
undefined,
|
||||||
|
args["response-interaction"],
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cleaningMessage = msg.channel.send("Cleaning...");
|
let cleaningMessage: Message | undefined = undefined;
|
||||||
|
if (!args["response-interaction"]) {
|
||||||
|
cleaningMessage = await msg.channel.send("Cleaning...");
|
||||||
|
}
|
||||||
|
|
||||||
const messagesToClean: Message[] = [];
|
const messagesToClean: Message[] = [];
|
||||||
let beforeId = msg.id;
|
let beforeId = msg.id;
|
||||||
|
@ -202,19 +218,31 @@ export async function cleanCmd(pluginData: GuildPluginData<UtilityPluginType>, a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
responseMsg = await sendSuccessMessage(pluginData, msg.channel, responseText);
|
responseMsg = await sendSuccessMessage(
|
||||||
|
pluginData,
|
||||||
|
msg.channel,
|
||||||
|
responseText,
|
||||||
|
undefined,
|
||||||
|
args["response-interaction"],
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
const responseText = `Found no messages to clean${note ? ` (${note})` : ""}!`;
|
const responseText = `Found no messages to clean${note ? ` (${note})` : ""}!`;
|
||||||
responseMsg = await sendErrorMessage(pluginData, msg.channel, responseText);
|
responseMsg = await sendErrorMessage(
|
||||||
|
pluginData,
|
||||||
|
msg.channel,
|
||||||
|
responseText,
|
||||||
|
undefined,
|
||||||
|
args["response-interaction"],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await (await cleaningMessage).delete();
|
cleaningMessage?.delete();
|
||||||
|
|
||||||
if (targetChannel.id === msg.channel.id) {
|
if (targetChannel.id === msg.channel.id) {
|
||||||
// Delete the !clean command and the bot response if a different channel wasn't specified
|
// Delete the !clean command and the bot response if a different channel wasn't specified
|
||||||
// (so as not to spam the cleaned channel with the command itself)
|
// (so as not to spam the cleaned channel with the command itself)
|
||||||
|
msg.delete().catch(noop);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
msg.delete().catch(noop);
|
|
||||||
responseMsg?.delete().catch(noop);
|
responseMsg?.delete().catch(noop);
|
||||||
}, CLEAN_COMMAND_DELETE_DELAY);
|
}, CLEAN_COMMAND_DELETE_DELAY);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue