Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: simpler plugins #68

Merged
merged 12 commits into from
Jan 27, 2023
58 changes: 58 additions & 0 deletions TypeScript/assertFields.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//@ts-nocheck
/**
* This plugin checks the fields of a ModalSubmitInteraction
* with regex or a custom callback
*
* @author @jacoobes [<@182326315813306368>]
* @version 1.0.0
* @example
* ```ts
* export default commandModule({
* type: CommandType.Modal,
* plugins: [
* assertFields({
* fields: {
* // check the modal field "mcUsernameInput" with the regex /a+b+c/
* mcUsernameInput: /a+b+c+/
* },
* failure: (errors, interaction) => {
* interaction.reply(errors.join("\n"))
* }
* }),
* ],
* execute: ctx => {
* ctx.reply("nice!")
* }
* })
* ```
*/
import { CommandControlPlugin, CommandType, controller } from "@sern/handler";
import type { ModalSubmitInteraction } from "discord.js";

type Assertion =
| RegExp
| ((value : string) => boolean);

export function assertFields(config: {
fields: Record<string, Assertion>,
failure: (errors: string[], interaction: ModalSubmitInteraction) => any
}) {
return CommandControlPlugin<CommandType.Modal>(modal => {
const pairs = Object.entries(config.fields);
const errors = [];
for(const [ field, assertion ] of pairs) {
// Keep in mind this doesn't check for typos!
// feel free to add more checks.
const input = modal.fields.getTextInputValue(field)
const resolvedAssertion = assertion instanceof RegExp ? (value: string) => assertion.test(value) : assertion;
if(!resolvedAssertion(input)) {
errors.push(input + " failed to pass assertion " + resolvedAssertion.toString() )
}
}
if(errors.length > 0) {
config.failure(errors, modal);
return controller.stop();
}
return controller.next();
})
}
155 changes: 76 additions & 79 deletions TypeScript/buttonConfirmation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @ts-nocheck
//@ts-nocheck
/**
* This is buttonConfirmation plugin, it runs confirmation prompt in the form of buttons.
* Note that you need to use edit/editReply in the command itself because we are already replying in the plugin!
Expand All @@ -19,94 +19,91 @@
* ```
*/

import { CommandType, EventPlugin, PluginType } from "@sern/handler";
import {CommandControlPlugin, CommandType, controller} from "@sern/handler";
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
ComponentType,
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
ComponentType,
} from "discord.js";

export function confirmation(
options?: Partial<ConfirmationOptions>
): EventPlugin<CommandType.Both> {
return {
type: PluginType.Event,
description: "Confirms",
async execute([ctx], controller) {
options = {
content: "Do you want to proceed?",
denialMessage: "Cancelled",
labels: ["No", "Yes"],
time: 60_000,
wrongUserResponse: "Not for you!",
...options,
};
options?: Partial<ConfirmationOptions>
) {
return CommandControlPlugin<CommandType.Both>(async (ctx, args) => {
options = {
content: "Do you want to proceed?",
denialMessage: "Cancelled",
labels: ["No", "Yes"],
time: 60_000,
wrongUserResponse: "Not for you!",
...options,
};

const buttons = options.labels!.map((l, i) => {
return new ButtonBuilder()
.setCustomId(l)
.setLabel(l)
.setStyle(
i === 0 ? ButtonStyle.Danger : ButtonStyle.Success
);
});
const sent = await ctx.reply({
content: options.content,
components: [
new ActionRowBuilder<ButtonBuilder>().setComponents(
buttons
),
],
});
const buttons = options.labels!.map((l, i) => {
return new ButtonBuilder()
.setCustomId(l)
.setLabel(l)
.setStyle(
i === 0 ? ButtonStyle.Danger : ButtonStyle.Success
);
});
const sent = await ctx.reply({
content: options.content,
components: [
new ActionRowBuilder<ButtonBuilder>().setComponents(
buttons
),
],
});

const collector = sent.createMessageComponentCollector({
componentType: ComponentType.Button,
filter: (i) => i.user.id === ctx.user.id,
time: options.time,
});
const collector = sent.createMessageComponentCollector({
componentType: ComponentType.Button,
filter: (i) => i.user.id === ctx.user.id,
time: options.time,
});

return new Promise((resolve) => {
collector.on("collect", async (i) => {
await i.update({ components: [] });
collector.stop();
if (i.customId === options!.labels![1]) {
resolve(controller.next());
return;
}
await i.editReply({
content: options?.denialMessage,
});
resolve(controller.stop());
});
return new Promise((resolve) => {
collector.on("collect", async (i) => {
await i.update({ components: [] });
collector.stop();
if (i.customId === options!.labels![1]) {
resolve(controller.next());
return;
}
await i.editReply({
content: options?.denialMessage,
});
resolve(controller.stop());
});

collector.on("end", async (c) => {
if (c.size) return;
buttons.forEach((b) => b.setDisabled());
await sent.edit({
components: [
new ActionRowBuilder<ButtonBuilder>().setComponents(
buttons
),
],
});
});
collector.on("end", async (c) => {
if (c.size) return;
buttons.forEach((b) => b.setDisabled());
await sent.edit({
components: [
new ActionRowBuilder<ButtonBuilder>().setComponents(
buttons
),
],
});
});

collector.on("ignore", async (i) => {
await i.reply({
content: options?.wrongUserResponse,
ephemeral: true,
});
});
});
},
};
collector.on("ignore", async (i) => {
await i.reply({
content: options?.wrongUserResponse,
ephemeral: true,
});
});
});
});
}

interface ConfirmationOptions {
content: string;
denialMessage: string;
time: number;
labels: [string, string];
wrongUserResponse: string;
content: string;
denialMessage: string;
time: number;
labels: [string, string];
wrongUserResponse: string;
}

43 changes: 19 additions & 24 deletions TypeScript/channelType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,23 @@
* ```
*/
import { ChannelType } from "discord.js";
import { CommandType, EventPlugin, PluginType } from "@sern/handler";
import {CommandControlPlugin, CommandType, controller } from "@sern/handler";
export function channelType(
channelType: ChannelType[],
onFail?: string
): EventPlugin<CommandType.Both> {
return {
type: PluginType.Event,
description: "Checks the channel type.",
async execute(event, controller) {
const [ctx] = event;
let channel = ctx.channel?.type;
//for some reason the dm channel type was returning undefined at some points
if (channel === undefined) {
channel = ChannelType.DM;
}
if (channelType.includes(channel)) {
return controller.next();
}
if (onFail) {
await ctx.reply(onFail);
}
return controller.stop();
},
};
}
channelType: ChannelType[],
onFail?: string
){
return CommandControlPlugin<CommandType.Both>(async (ctx, args) => {
let channel = ctx.channel?.type;
//for some reason the dm channel type was returning undefined at some points
if (channel === undefined) {
channel = ChannelType.DM;
}
if (channelType.includes(channel)) {
return controller.next();
}
if (onFail) {
await ctx.reply(onFail);
}
return controller.stop();
})
}
24 changes: 9 additions & 15 deletions TypeScript/confirmation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@
* type : CommandType.Both
* plugins: [confirmation()],
* execute: (ctx, args) => {
* ctx.reply('hola');
* ctx.interaction.followUp('Hello welcome to the secret club')
* }
* })
* ```
*/

import { CommandType, Context, EventPlugin, PluginType } from "@sern/handler";
import {CommandControlPlugin, CommandType, Context, controller } from "@sern/handler";
import type { Awaitable, Message, MessageReaction, User } from "discord.js";

type Callback<T> = Awaitable<T> | ((context: Context) => Awaitable<T>);
Expand All @@ -43,7 +43,7 @@ interface Emojis {
}

const defaultOptions: ConfirmationOptions = {
timeout: 1000,
timeout: 5000,
message: "Are you sure you want to proceed?",
onTimeout: "confirmation timed out",
onCancel: "confirmation cancelled",
Expand All @@ -60,18 +60,15 @@ const defaultOptions: ConfirmationOptions = {

export function confirmation(
raw: Partial<ConfirmationOptions> = {}
): EventPlugin<CommandType.Both> {
) {
const options: ConfirmationOptions = Object.assign({}, defaultOptions, raw);
return {
name: "confirmation",
type: PluginType.Event,
async execute([context], controller) {
return CommandControlPlugin<CommandType.Both>(async (context, _) => {
if (typeof options.message === "function") {
options.message = await options.message(context);
}

const response = await context.reply(await options.message);
let { yes, no } = options.emojis;
let {yes, no} = options.emojis;
if (typeof yes === "function") {
yes = await yes(context);
}
Expand Down Expand Up @@ -103,10 +100,8 @@ export function confirmation(
await response.edit(await options.onTimeout);
await response.reactions.removeAll();
}

return controller.stop();
}

const reaction = recieved.first();
if (!reaction) {
return controller.stop();
Expand All @@ -132,8 +127,7 @@ export function confirmation(

return controller.stop();
}

return controller.next();
},
};
return controller.next()
}
)
}