Skip to content

Commit

Permalink
refactor: simpler plugins (#68)
Browse files Browse the repository at this point in the history
* feat!: update ownerOnly

* feat!: update permCheck

* feat!: update publish

* feat!: update serverOnly

* feat!: update nsfwOnly

* feat!: update requirePermission

* feat!: update dmOnly

* feat!: update cooldown

* feat!: update confirmation

* feat!: update channelType

* feat!: update buttonConfirmation

* feat: addAssertFields (#67)

feat: add assertFields
  • Loading branch information
jacoobes committed Jan 27, 2023
1 parent 50017c3 commit a6d50c1
Show file tree
Hide file tree
Showing 12 changed files with 492 additions and 497 deletions.
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()
}
)
}

0 comments on commit a6d50c1

Please sign in to comment.