mirror of
https://github.com/ZeppelinBot/Zeppelin.git
synced 2025-03-15 05:41:51 +00:00
More fixes, waitForInteraction (replacement for waitForReaction)
This commit is contained in:
parent
edcfd2333f
commit
d0c6e6f411
13 changed files with 135 additions and 50 deletions
|
@ -183,7 +183,7 @@ export function getPluginConfigPreprocessor(
|
|||
};
|
||||
}
|
||||
|
||||
export function sendSuccessMessage(
|
||||
export async function sendSuccessMessage(
|
||||
pluginData: AnyPluginData<any>,
|
||||
channel: TextChannel,
|
||||
body: string,
|
||||
|
@ -194,8 +194,9 @@ export function sendSuccessMessage(
|
|||
const content: MessageOptions = allowedMentions
|
||||
? { content: formattedBody, allowedMentions }
|
||||
: { content: formattedBody };
|
||||
|
||||
return channel
|
||||
.send({ content }) // Force line break
|
||||
.send({ ...content, split: false }) // Force line break
|
||||
.catch(err => {
|
||||
const channelInfo = channel.guild ? `${channel.id} (${channel.guild.id})` : `${channel.id}`;
|
||||
logger.warn(`Failed to send success message to ${channelInfo}): ${err.code} ${err.message}`);
|
||||
|
@ -203,7 +204,7 @@ export function sendSuccessMessage(
|
|||
});
|
||||
}
|
||||
|
||||
export function sendErrorMessage(
|
||||
export async function sendErrorMessage(
|
||||
pluginData: AnyPluginData<any>,
|
||||
channel: TextChannel,
|
||||
body: string,
|
||||
|
@ -214,6 +215,7 @@ export function sendErrorMessage(
|
|||
const content: MessageOptions = allowedMentions
|
||||
? { content: formattedBody, allowedMentions }
|
||||
: { content: formattedBody };
|
||||
|
||||
return channel
|
||||
.send({ ...content, split: false }) // Force line break
|
||||
.catch(err => {
|
||||
|
|
|
@ -32,7 +32,6 @@ export const ArchiveChannelCmd = channelArchiverCmd({
|
|||
async run({ message: msg, args, pluginData }) {
|
||||
if (!args["attachment-channel"]) {
|
||||
const confirmed = await confirm(
|
||||
pluginData.client,
|
||||
msg.channel,
|
||||
msg.author.id,
|
||||
"No `-attachment-channel` specified. Continue? Attachments will not be available in the log if their message is deleted.",
|
||||
|
|
|
@ -31,7 +31,6 @@ export const ResetAllCounterValuesCmd = typedGuildCommand<CountersPluginType>()(
|
|||
|
||||
const counterName = counter.name || args.counterName;
|
||||
const confirmed = await confirm(
|
||||
pluginData.client,
|
||||
message.channel,
|
||||
message.author.id,
|
||||
trimMultilineString(`
|
||||
|
|
|
@ -137,7 +137,7 @@ export async function log(pluginData: GuildPluginData<LogsPluginType>, type: Log
|
|||
const batched = opts.batched ?? true;
|
||||
const batchTime = opts.batch_time ?? 1000;
|
||||
const cfg = pluginData.config.get();
|
||||
const parse: MessageMentionTypes[] | undefined = cfg.allow_user_mentions ? ["users"] : undefined;
|
||||
const parse: MessageMentionTypes[] = cfg.allow_user_mentions ? ["users"] : [];
|
||||
|
||||
if (batched) {
|
||||
// If we're batching log messages, gather all log messages within the set batch_time into a single message
|
||||
|
|
|
@ -12,6 +12,7 @@ import { CasesPlugin } from "../../../plugins/Cases/CasesPlugin";
|
|||
import { CaseTypes } from "../../../data/CaseTypes";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { banLock } from "../../../utils/lockNameHelpers";
|
||||
import { waitForButtonConfirm } from "../../../utils/waitForInteraction";
|
||||
|
||||
const opts = {
|
||||
mod: ct.member({ option: true }),
|
||||
|
@ -76,11 +77,12 @@ export const BanCmd = modActionsCmd({
|
|||
}
|
||||
|
||||
// Ask the mod if we should update the existing ban
|
||||
const alreadyBannedMsg = await msg.channel.send("User is already banned, update ban?");
|
||||
const reply = false; // await waitForReaction(pluginData.client, alreadyBannedMsg, ["✅", "❌"], msg.author.id); FIXME waiting on waitForButton
|
||||
|
||||
alreadyBannedMsg.delete().catch(noop);
|
||||
if (!reply /* || reply.name === "❌"*/) {
|
||||
const reply = await waitForButtonConfirm(
|
||||
msg.channel,
|
||||
{ content: "Failed to message the user. Log the warning anyway?" },
|
||||
{ confirmText: "Yes", cancelText: "No", restrictToId: msg.member.id },
|
||||
);
|
||||
if (!reply) {
|
||||
sendErrorMessage(pluginData, msg.channel, "User already banned, update cancelled by moderator");
|
||||
lock.unlock();
|
||||
return;
|
||||
|
@ -124,11 +126,12 @@ export const BanCmd = modActionsCmd({
|
|||
}
|
||||
} else {
|
||||
// Ask the mod if we should upgrade to a forceban as the user is not on the server
|
||||
const notOnServerMsg = await msg.channel.send("User not found on the server, forceban instead?");
|
||||
const reply = false; // await waitForReaction(pluginData.client, notOnServerMsg, ["✅", "❌"], msg.author.id); Waiting for waitForButton
|
||||
|
||||
notOnServerMsg.delete().catch(noop);
|
||||
if (!reply /*|| reply.name === "❌"*/) {
|
||||
const reply = await waitForButtonConfirm(
|
||||
msg.channel,
|
||||
{ content: "User not on server, forceban instead?" },
|
||||
{ confirmText: "Yes", cancelText: "No", restrictToId: msg.member.id },
|
||||
);
|
||||
if (!reply) {
|
||||
sendErrorMessage(pluginData, msg.channel, "User not on server, ban cancelled by moderator");
|
||||
lock.unlock();
|
||||
return;
|
||||
|
|
|
@ -5,6 +5,7 @@ import { noop, resolveMember, resolveUser } from "../../../utils";
|
|||
import { isBanned } from "../functions/isBanned";
|
||||
|
||||
import { actualMuteUserCmd } from "../functions/actualMuteUserCmd";
|
||||
import { waitForButtonConfirm } from "../../../utils/waitForInteraction";
|
||||
|
||||
const opts = {
|
||||
mod: ct.member({ option: true }),
|
||||
|
@ -54,11 +55,13 @@ export const MuteCmd = modActionsCmd({
|
|||
return;
|
||||
} else {
|
||||
// Ask the mod if we should upgrade to a forcemute as the user is not on the server
|
||||
const notOnServerMsg = await msg.channel.send("User not found on the server, forcemute instead?");
|
||||
const reply = false; // await waitForReaction(pluginData.client, notOnServerMsg, ["✅", "❌"], msg.author.id); FIXME waiting on waitForButton
|
||||
const reply = await waitForButtonConfirm(
|
||||
msg.channel,
|
||||
{ content: "User not found on the server, forcemute instead?" },
|
||||
{ confirmText: "Yes", cancelText: "No", restrictToId: msg.member.id },
|
||||
);
|
||||
|
||||
notOnServerMsg.delete().catch(noop);
|
||||
if (!reply /*|| reply.name === "❌"*/) {
|
||||
if (!reply) {
|
||||
sendErrorMessage(pluginData, msg.channel, "User not on server, mute cancelled by moderator");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { resolveUser, resolveMember, noop } from "../../../utils";
|
|||
import { MutesPlugin } from "../../../plugins/Mutes/MutesPlugin";
|
||||
import { actualUnmuteCmd } from "../functions/actualUnmuteUserCmd";
|
||||
import { isBanned } from "../functions/isBanned";
|
||||
import { waitForButtonConfirm } from "../../../utils/waitForInteraction";
|
||||
|
||||
const opts = {
|
||||
mod: ct.member({ option: true }),
|
||||
|
@ -60,11 +61,13 @@ export const UnmuteCmd = modActionsCmd({
|
|||
return;
|
||||
} else {
|
||||
// Ask the mod if we should upgrade to a forceunmute as the user is not on the server
|
||||
const notOnServerMsg = await msg.channel.send("User not found on the server, forceunmute instead?");
|
||||
const reply = false; // await waitForReaction(pluginData.client, notOnServerMsg, ["✅", "❌"], msg.author.id); FIXME waiting on waitForButton
|
||||
const reply = await waitForButtonConfirm(
|
||||
msg.channel,
|
||||
{ content: "User not on server, forceunmute instead?" },
|
||||
{ confirmText: "Yes", cancelText: "No", restrictToId: msg.member.id },
|
||||
);
|
||||
|
||||
notOnServerMsg.delete().catch(noop);
|
||||
if (!reply /*|| reply.name === "❌"*/) {
|
||||
if (!reply) {
|
||||
sendErrorMessage(pluginData, msg.channel, "User not on server, unmute cancelled by moderator");
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -6,11 +6,12 @@ import { formatReasonWithAttachments } from "../functions/formatReasonWithAttach
|
|||
import { CasesPlugin } from "../../Cases/CasesPlugin";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { CaseTypes } from "../../../data/CaseTypes";
|
||||
import { errorMessage, resolveMember, resolveUser, stripObjectToScalars } from "../../../utils";
|
||||
import { errorMessage, resolveMember, resolveUser } from "../../../utils";
|
||||
import { isBanned } from "../functions/isBanned";
|
||||
import { readContactMethodsFromArgs } from "../functions/readContactMethodsFromArgs";
|
||||
import { warnMember } from "../functions/warnMember";
|
||||
import { TextChannel } from "discord.js";
|
||||
import { waitForButtonConfirm } from "../../../utils/waitForInteraction";
|
||||
|
||||
export const WarnCmd = modActionsCmd({
|
||||
trigger: "warn",
|
||||
|
@ -69,13 +70,12 @@ export const WarnCmd = modActionsCmd({
|
|||
const casesPlugin = pluginData.getPlugin(CasesPlugin);
|
||||
const priorWarnAmount = await casesPlugin.getCaseTypeAmountForUserId(memberToWarn.id, CaseTypes.Warn);
|
||||
if (config.warn_notify_enabled && priorWarnAmount >= config.warn_notify_threshold) {
|
||||
const tooManyWarningsMsg = await msg.channel.send(
|
||||
config.warn_notify_message.replace("{priorWarnings}", `${priorWarnAmount}`),
|
||||
const reply = await waitForButtonConfirm(
|
||||
msg.channel,
|
||||
{ content: config.warn_notify_message.replace("{priorWarnings}", `${priorWarnAmount}`) },
|
||||
{ confirmText: "Yes", cancelText: "No", restrictToId: msg.member.id },
|
||||
);
|
||||
|
||||
const reply = false; // await waitForReaction(pluginData.client, tooManyWarningsMsg, ["✅", "❌"], msg.author.id); FIXME waiting on waitForButton
|
||||
tooManyWarningsMsg.delete();
|
||||
if (!reply /*|| reply.name === "❌"*/) {
|
||||
if (!reply) {
|
||||
msg.channel.send(errorMessage("Warn cancelled by moderator"));
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,10 @@ export async function isBanned(
|
|||
}
|
||||
|
||||
try {
|
||||
const potentialBan = await Promise.race([pluginData.guild.bans.fetch({ user: userId }), sleep(timeout)]);
|
||||
const potentialBan = await Promise.race([
|
||||
pluginData.guild.bans.fetch({ user: userId }).catch(() => null),
|
||||
sleep(timeout),
|
||||
]);
|
||||
return potentialBan != null;
|
||||
} catch (e) {
|
||||
if (isDiscordRESTError(e) && e.code === 10026) {
|
||||
|
|
|
@ -14,7 +14,8 @@ import { CasesPlugin } from "../../Cases/CasesPlugin";
|
|||
import { CaseTypes } from "../../../data/CaseTypes";
|
||||
import { LogType } from "../../../data/LogType";
|
||||
import { renderTemplate } from "../../../templateFormatter";
|
||||
import { GuildMember } from "discord.js";
|
||||
import { GuildMember, MessageOptions } from "discord.js";
|
||||
import { waitForButtonConfirm } from "../../../utils/waitForInteraction";
|
||||
|
||||
export async function warnMember(
|
||||
pluginData: GuildPluginData<ModActionsPluginType>,
|
||||
|
@ -43,12 +44,13 @@ export async function warnMember(
|
|||
|
||||
if (!notifyResult.success) {
|
||||
if (warnOptions.retryPromptChannel && pluginData.guild.channels.resolve(warnOptions.retryPromptChannel.id)) {
|
||||
const failedMsg = await warnOptions.retryPromptChannel.send(
|
||||
"Failed to message the user. Log the warning anyway?",
|
||||
const reply = await waitForButtonConfirm(
|
||||
warnOptions.retryPromptChannel,
|
||||
{ content: "Failed to message the user. Log the warning anyway?" },
|
||||
{ confirmText: "Yes", cancelText: "No", restrictToId: warnOptions.caseArgs?.modId },
|
||||
);
|
||||
const reply = false; // await waitForReaction(pluginData.client, failedMsg, ["✅", "❌"]); FIXME waiting on waitForButton
|
||||
failedMsg.delete();
|
||||
if (!reply /*|| reply.name === "❌"*/) {
|
||||
|
||||
if (!reply) {
|
||||
return {
|
||||
status: "failed",
|
||||
error: "Failed to message user",
|
||||
|
@ -74,7 +76,7 @@ export async function warnMember(
|
|||
noteDetails: notifyResult.text ? [ucfirst(notifyResult.text)] : [],
|
||||
});
|
||||
|
||||
const mod = await resolveUser(pluginData.client, modId);
|
||||
const mod = await pluginData.guild.members.fetch(modId);
|
||||
pluginData.state.serverLogs.log(LogType.MEMBER_WARN, {
|
||||
mod: stripObjectToScalars(mod),
|
||||
member: stripObjectToScalars(member, ["user", "roles"]),
|
||||
|
|
|
@ -51,7 +51,7 @@ export async function applyReactionRoleReactionsToMessage(
|
|||
|
||||
// Remove old reactions, if any
|
||||
try {
|
||||
await targetMessage.removeReactions();
|
||||
await targetMessage.reactions.removeAll();
|
||||
} catch (e) {
|
||||
if (isDiscordRESTError(e)) {
|
||||
errors.push(`Error ${e.code} while removing old reactions: ${e.message}`);
|
||||
|
@ -74,7 +74,7 @@ export async function applyReactionRoleReactionsToMessage(
|
|||
const emoji = isSnowflake(rawEmoji) ? `foo:${rawEmoji}` : rawEmoji;
|
||||
|
||||
try {
|
||||
await targetMessage.addReaction(emoji);
|
||||
await targetMessage.reactions.add(emoji);
|
||||
await sleep(1250); // Make sure we don't hit rate limits
|
||||
} catch (e) {
|
||||
if (isDiscordRESTError(e)) {
|
||||
|
|
|
@ -27,9 +27,12 @@ import {
|
|||
GuildAuditLogsEntry,
|
||||
GuildChannel,
|
||||
GuildMember,
|
||||
Interaction,
|
||||
Invite,
|
||||
Message,
|
||||
MessageActionRow,
|
||||
MessageAttachment,
|
||||
MessageComponent,
|
||||
MessageEmbed,
|
||||
MessageEmbedOptions,
|
||||
MessageMentionOptions,
|
||||
|
@ -41,6 +44,7 @@ import {
|
|||
User,
|
||||
} from "discord.js";
|
||||
import { ChannelTypeStrings } from "./types";
|
||||
import { waitForButtonConfirm } from "./utils/waitForInteraction";
|
||||
|
||||
const fsp = fs.promises;
|
||||
|
||||
|
@ -1160,7 +1164,7 @@ export async function resolveUser<T>(bot, value) {
|
|||
|
||||
// If we have the user cached, return that directly
|
||||
if (bot.users.cache.has(userId)) {
|
||||
return bot.users.get(userId);
|
||||
return bot.users.fetch(userId);
|
||||
}
|
||||
|
||||
// We don't want to spam the API by trying to fetch unknown users again and again,
|
||||
|
@ -1169,9 +1173,8 @@ export async function resolveUser<T>(bot, value) {
|
|||
return new UnknownUser({ id: userId });
|
||||
}
|
||||
|
||||
const freshUser = await bot.getRESTUser(userId).catch(noop);
|
||||
const freshUser = await bot.users.fetch(userId, true, true).catch(noop);
|
||||
if (freshUser) {
|
||||
bot.users.add(freshUser, bot);
|
||||
return freshUser;
|
||||
}
|
||||
|
||||
|
@ -1272,15 +1275,11 @@ export async function resolveInvite<T extends boolean>(
|
|||
}
|
||||
|
||||
export async function confirm(
|
||||
bot: Client,
|
||||
channel: TextChannel,
|
||||
userId: string,
|
||||
content: StringResolvable | MessageOptions,
|
||||
) {
|
||||
const msg = await channel.send(content);
|
||||
const reply: any = {}; // await helpers.waitForReaction(bot, msg, ["✅", "❌"], userId); FIXME waiting on waitForButton
|
||||
msg.delete().catch(noop);
|
||||
return reply && reply.name === "✅";
|
||||
): Promise<boolean> {
|
||||
return waitForButtonConfirm(channel, { content }, { restrictToId: userId });
|
||||
}
|
||||
|
||||
export function messageSummary(msg: SavedMessage) {
|
||||
|
|
72
backend/src/utils/waitForInteraction.ts
Normal file
72
backend/src/utils/waitForInteraction.ts
Normal file
|
@ -0,0 +1,72 @@
|
|||
import {
|
||||
TextChannel,
|
||||
MessageActionRow,
|
||||
MessageOptions,
|
||||
MessageButton,
|
||||
Client,
|
||||
Interaction,
|
||||
MessageComponentInteraction,
|
||||
} from "discord.js";
|
||||
import { PluginError } from "knub";
|
||||
import { noop } from "knub/dist/utils";
|
||||
import moment from "moment";
|
||||
|
||||
export async function waitForComponent(
|
||||
channel: TextChannel,
|
||||
toPost: MessageOptions,
|
||||
components: MessageActionRow[],
|
||||
options?: WaitForOptions,
|
||||
) {
|
||||
throw new PluginError("Unimplemented method waitForComponent called.");
|
||||
}
|
||||
|
||||
export async function waitForButtonConfirm(
|
||||
channel: TextChannel,
|
||||
toPost: MessageOptions,
|
||||
options?: WaitForOptions,
|
||||
): Promise<boolean> {
|
||||
return new Promise(async resolve => {
|
||||
const idMod = `${channel.guild.id}-${moment.utc().valueOf()}`;
|
||||
const row = new MessageActionRow().addComponents([
|
||||
new MessageButton()
|
||||
.setStyle("SUCCESS")
|
||||
.setLabel(options?.confirmText || "Confirm")
|
||||
.setType("BUTTON")
|
||||
.setCustomID(`confirmButton:${idMod}`),
|
||||
|
||||
new MessageButton()
|
||||
.setStyle("DANGER")
|
||||
.setLabel(options?.cancelText || "Cancel")
|
||||
.setType("BUTTON")
|
||||
.setCustomID(`cancelButton:${idMod}`),
|
||||
]);
|
||||
const message = await channel.send({ ...toPost, components: [row], split: false });
|
||||
|
||||
const filter = (iac: MessageComponentInteraction) => iac.message.id === message.id;
|
||||
const collector = message.createMessageComponentInteractionCollector(filter, { time: 10000 });
|
||||
|
||||
collector.on("collect", (interaction: MessageComponentInteraction) => {
|
||||
if (options?.restrictToId && options.restrictToId !== interaction.user.id) {
|
||||
interaction.reply(`You are not permitted to use these buttons.`, { ephemeral: true });
|
||||
} else {
|
||||
if (interaction.customID === `confirmButton:${idMod}`) {
|
||||
message.delete();
|
||||
resolve(true);
|
||||
} else if (interaction.customID === `cancelButton:${idMod}`) {
|
||||
message.delete();
|
||||
resolve(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
collector.on("end", () => {
|
||||
if (!message.deleted) message.delete().catch(noop);
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export interface WaitForOptions {
|
||||
restrictToId?: string;
|
||||
confirmText?: string;
|
||||
cancelText?: string;
|
||||
}
|
Loading…
Add table
Reference in a new issue