More fixes, waitForInteraction (replacement for waitForReaction)

This commit is contained in:
Dark 2021-06-02 23:41:05 +02:00
parent edcfd2333f
commit d0c6e6f411
No known key found for this signature in database
GPG key ID: 384C4B4F5B1E25A8
13 changed files with 135 additions and 50 deletions

View file

@ -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 => {

View file

@ -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.",

View file

@ -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(`

View file

@ -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

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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) {

View file

@ -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"]),

View file

@ -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)) {

View file

@ -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) {

View 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;
}