mirror of
https://github.com/SrIzan10/awesome-plugins.git
synced 2026-05-01 10:35:27 +00:00
refactor: simpler plugins (#68)
* 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
This commit is contained in:
58
TypeScript/assertFields.ts
Normal file
58
TypeScript/assertFields.ts
Normal file
@@ -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();
|
||||
})
|
||||
}
|
||||
@@ -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!
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
})
|
||||
}
|
||||
@@ -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>);
|
||||
@@ -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",
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
@@ -132,8 +127,7 @@ export function confirmation(
|
||||
|
||||
return controller.stop();
|
||||
}
|
||||
|
||||
return controller.next();
|
||||
},
|
||||
};
|
||||
return controller.next()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* Allows you to set cooldowns (or "ratelimits") for commands
|
||||
*
|
||||
* limits user/channel/guild actions,
|
||||
* @author @trueharuu [<@504698587221852172>]
|
||||
* @version 1.0.0
|
||||
* @example
|
||||
@@ -17,7 +17,7 @@
|
||||
* ```
|
||||
*/
|
||||
|
||||
import { CommandType, Context, EventPlugin, PluginType } from "@sern/handler";
|
||||
import {CommandControlPlugin, CommandType, Context, controller } from "@sern/handler";
|
||||
import { GuildMember } from "discord.js";
|
||||
/**
|
||||
* actions/seconds
|
||||
@@ -107,44 +107,38 @@ function add(
|
||||
| Cooldown
|
||||
>,
|
||||
message?: CooldownResponse
|
||||
): EventPlugin<CommandType.Both> {
|
||||
) {
|
||||
const raw = items.map((c) => {
|
||||
if (!Array.isArray(c)) return c;
|
||||
return parseCooldown(c[0] as CooldownLocation, c[1]);
|
||||
}) as Array<Cooldown>;
|
||||
return CommandControlPlugin<CommandType.Both>(async (context, args) => {
|
||||
for (const { location, actions, seconds } of raw) {
|
||||
const id = getPropertyForLocation(context, location);
|
||||
const cooldown = map.get(id!);
|
||||
|
||||
return {
|
||||
name: "cooldown",
|
||||
description: "limits user/channel/guild actions",
|
||||
type: PluginType.Event,
|
||||
async execute([context], controller) {
|
||||
for (const { location, actions, seconds } of raw) {
|
||||
const id = getPropertyForLocation(context, location);
|
||||
const cooldown = map.get(id);
|
||||
|
||||
if (!cooldown) {
|
||||
map.set(id, 1, seconds * 1000);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cooldown >= actions) {
|
||||
if (message) {
|
||||
await message({
|
||||
location,
|
||||
actions: cooldown,
|
||||
maxActions: actions,
|
||||
seconds,
|
||||
context,
|
||||
});
|
||||
}
|
||||
return controller.stop();
|
||||
}
|
||||
|
||||
map.set(id, cooldown + 1, seconds * 1000);
|
||||
if (!cooldown) {
|
||||
map.set(id!, 1, seconds * 1000);
|
||||
continue;
|
||||
}
|
||||
return controller.next();
|
||||
},
|
||||
};
|
||||
|
||||
if (cooldown >= actions) {
|
||||
if (message) {
|
||||
await message({
|
||||
location,
|
||||
actions: cooldown,
|
||||
maxActions: actions,
|
||||
seconds,
|
||||
context,
|
||||
});
|
||||
}
|
||||
return controller.stop();
|
||||
}
|
||||
|
||||
map.set(id!, cooldown + 1, seconds * 1000);
|
||||
}
|
||||
return controller.next();
|
||||
});
|
||||
}
|
||||
|
||||
type Location = (value: CooldownString) => ReturnType<typeof add>;
|
||||
|
||||
@@ -16,20 +16,16 @@
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
import { CommandType, EventPlugin, PluginType } from "@sern/handler";
|
||||
import {CommandControlPlugin, CommandType, controller } from "@sern/handler";
|
||||
export function dmOnly(
|
||||
content?: string,
|
||||
ephemeral?: boolean
|
||||
): EventPlugin<CommandType.Both> {
|
||||
return {
|
||||
type: PluginType.Event,
|
||||
description: "Allows commands to be run in DM only",
|
||||
async execute(event, controller) {
|
||||
const [ctx] = event;
|
||||
if (ctx.channel?.isDMBased()) return controller.next();
|
||||
content?: string,
|
||||
ephemeral?: boolean
|
||||
) {
|
||||
// For discord.js you should have the Partials.Channel and DirectMessages intent enabled.
|
||||
return CommandControlPlugin<CommandType.Both>(async (ctx, _) => {
|
||||
if (ctx.channel?.isDMBased()) return controller.next();
|
||||
|
||||
if (content) await ctx.reply({ content, ephemeral }); // Change this if you want or remove it for silent deny
|
||||
return controller.stop();
|
||||
},
|
||||
};
|
||||
if (content) await ctx.reply({ content, ephemeral }); // Change this if you want or remove it for silent deny
|
||||
return controller.stop();
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,46 +17,40 @@
|
||||
* ```
|
||||
*/
|
||||
import {
|
||||
ChannelType,
|
||||
GuildTextBasedChannel,
|
||||
TextBasedChannel,
|
||||
TextChannel,
|
||||
ChannelType,
|
||||
GuildTextBasedChannel,
|
||||
TextBasedChannel,
|
||||
TextChannel,
|
||||
} from "discord.js";
|
||||
import { CommandType, EventPlugin, Nullish, PluginType } from "@sern/handler";
|
||||
import {CommandControlPlugin, CommandType, controller } from "@sern/handler";
|
||||
function isGuildText(
|
||||
channel: Nullish<TextBasedChannel>
|
||||
channel: TextBasedChannel | null
|
||||
): channel is GuildTextBasedChannel {
|
||||
return (
|
||||
channel?.type == ChannelType.GuildPublicThread ||
|
||||
channel?.type == ChannelType.GuildPrivateThread
|
||||
);
|
||||
return (
|
||||
channel?.type == ChannelType.GuildPublicThread ||
|
||||
channel?.type == ChannelType.GuildPrivateThread
|
||||
);
|
||||
}
|
||||
export function nsfwOnly(
|
||||
onFail: string,
|
||||
ephemeral: boolean
|
||||
): EventPlugin<CommandType.Both> {
|
||||
return {
|
||||
type: PluginType.Event,
|
||||
description: "Checks if the channel is nsfw or not.",
|
||||
async execute(event, controller) {
|
||||
const [ctx] = event;
|
||||
//checking if command was executed in dms
|
||||
if (ctx.guild === null) {
|
||||
await ctx.reply({ content: onFail, ephemeral });
|
||||
return controller.stop();
|
||||
}
|
||||
//channel is thread (not supported by nsfw)
|
||||
if (isGuildText(ctx.channel) == true) {
|
||||
await ctx.reply({ content: onFail, ephemeral });
|
||||
return controller.stop();
|
||||
}
|
||||
if (!(ctx.channel! as TextChannel).nsfw) {
|
||||
//channel is not nsfw
|
||||
await ctx.reply({ content: onFail, ephemeral });
|
||||
return controller.stop();
|
||||
}
|
||||
//continues to command if nsfw
|
||||
return controller.next();
|
||||
},
|
||||
};
|
||||
onFail: string,
|
||||
ephemeral: boolean
|
||||
) {
|
||||
return CommandControlPlugin<CommandType.Both>(async (ctx, _) => {
|
||||
if (ctx.guild === null) {
|
||||
await ctx.reply({ content: onFail, ephemeral });
|
||||
return controller.stop();
|
||||
}
|
||||
//channel is thread (not supported by nsfw)
|
||||
if (isGuildText(ctx.channel) == true) {
|
||||
await ctx.reply({ content: onFail, ephemeral });
|
||||
return controller.stop();
|
||||
}
|
||||
if (!(ctx.channel! as TextChannel).nsfw) {
|
||||
//channel is not nsfw
|
||||
await ctx.reply({ content: onFail, ephemeral });
|
||||
return controller.stop();
|
||||
}
|
||||
//continues to command if nsfw
|
||||
return controller.next();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,18 +17,13 @@
|
||||
* ```
|
||||
*/
|
||||
|
||||
import { CommandType, EventPlugin, PluginType } from "@sern/handler";
|
||||
import { CommandType, CommandControlPlugin, controller } from "@sern/handler";
|
||||
const ownerIDs = ["697795666373640213"]; //! Fill your ID
|
||||
export function ownerOnly(): EventPlugin<CommandType.Both> {
|
||||
return {
|
||||
type: PluginType.Event,
|
||||
description: "Allows only bot owner to run command",
|
||||
async execute(event, controller) {
|
||||
const [ctx] = event;
|
||||
if (ownerIDs.includes(ctx.user.id)) return controller.next();
|
||||
//* If you want to reply when the command fails due to user not being owner, you can use following
|
||||
// await ctx.reply("Only owner can run it!!!");
|
||||
return controller.stop(); //! Important: It stops the execution of command!
|
||||
},
|
||||
};
|
||||
export function ownerOnly() {
|
||||
return CommandControlPlugin<CommandType.Both>((ctx,args) => {
|
||||
if (ownerIDs.includes(ctx.user.id)) return controller.next();
|
||||
//* If you want to reply when the command fails due to user not being owner, you can use following
|
||||
// await ctx.reply("Only owner can run it!!!");
|
||||
return controller.stop(); //! Important: It stops the execution of command!
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,29 +17,24 @@
|
||||
* ```
|
||||
*/
|
||||
|
||||
import { type GuildMember, PermissionResolvable } from "discord.js";
|
||||
import { CommandType, EventPlugin, PluginType } from "@sern/handler";
|
||||
import type { GuildMember, PermissionResolvable } from "discord.js";
|
||||
import {CommandControlPlugin, CommandType, controller } from "@sern/handler";
|
||||
export function permCheck(
|
||||
perm: PermissionResolvable,
|
||||
response: string
|
||||
): EventPlugin<CommandType.Both> {
|
||||
return {
|
||||
type: PluginType.Event,
|
||||
description: "Checks for specified perm",
|
||||
async execute(event, controller) {
|
||||
const [ctx] = event;
|
||||
if (ctx.guild === null) {
|
||||
ctx.reply("This command cannot be used here");
|
||||
console.warn(
|
||||
"PermCheck > A command stopped because we couldn't check a users permissions (was used in dms)"
|
||||
); //delete this line if you dont want to be notified when a command is used outside of a guild/server
|
||||
return controller.stop();
|
||||
}
|
||||
if (!(ctx.member! as GuildMember).permissions.has(perm)) {
|
||||
await ctx.reply(response);
|
||||
return controller.stop();
|
||||
}
|
||||
return controller.next();
|
||||
},
|
||||
};
|
||||
) {
|
||||
return CommandControlPlugin<CommandType.Both>(async (ctx, args) => {
|
||||
if (ctx.guild === null) {
|
||||
await ctx.reply("This command cannot be used here");
|
||||
console.warn(
|
||||
"PermCheck > A command stopped because we couldn't check a users permissions (was used in dms)"
|
||||
); //delete this line if you dont want to be notified when a command is used outside of a guild/server
|
||||
return controller.stop();
|
||||
}
|
||||
if (!(ctx.member! as GuildMember).permissions.has(perm)) {
|
||||
await ctx.reply(response);
|
||||
return controller.stop();
|
||||
}
|
||||
return controller.next();
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,200 +17,184 @@
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
import {
|
||||
CommandPlugin,
|
||||
CommandType,
|
||||
PluginType,
|
||||
SernOptionsData,
|
||||
SlashCommand,
|
||||
} from "@sern/handler";
|
||||
import {
|
||||
ApplicationCommandData,
|
||||
ApplicationCommandType,
|
||||
PermissionResolvable,
|
||||
} from "discord.js";
|
||||
/**
|
||||
* This is the dependency getter that is created from Sern.makeDependencies.
|
||||
* import it here so that this plugin has access to your bot's dependencies
|
||||
*/
|
||||
import { useContainer } from "../index.js";
|
||||
import {CommandInitPlugin, CommandType, controller, SernOptionsData, SlashCommand} from '@sern/handler'
|
||||
import {ApplicationCommandData, ApplicationCommandType, PermissionResolvable} from "discord.js";
|
||||
import {useContainer} from "../index.js";
|
||||
|
||||
export function publish(
|
||||
options?: PublishOptions
|
||||
): CommandPlugin<
|
||||
| CommandType.Slash
|
||||
| CommandType.Both
|
||||
| CommandType.CtxUser
|
||||
| CommandType.CtxMsg
|
||||
> {
|
||||
return {
|
||||
type: PluginType.Command,
|
||||
description: "Manage Application Commands",
|
||||
name: "slash-auto-publish",
|
||||
async execute({ mod: module }, controller) {
|
||||
// Users need to provide their own useContainer function.
|
||||
const [client] = useContainer("@sern/client");
|
||||
const defaultOptions = {
|
||||
guildIds: [],
|
||||
dmPermission: undefined,
|
||||
defaultMemberPermissions: null,
|
||||
};
|
||||
export const CommandTypeRaw = {
|
||||
[CommandType.Both]: ApplicationCommandType.ChatInput,
|
||||
[CommandType.CtxUser]: ApplicationCommandType.Message,
|
||||
[CommandType.CtxMsg]: ApplicationCommandType.User,
|
||||
[CommandType.Slash]: ApplicationCommandType.ChatInput,
|
||||
} as const;
|
||||
|
||||
options = { ...defaultOptions, ...options } as PublishOptions &
|
||||
ValidPublishOptions;
|
||||
let { defaultMemberPermissions, dmPermission, guildIds } =
|
||||
options as unknown as ValidPublishOptions;
|
||||
export function publish<T extends
|
||||
| CommandType.Both
|
||||
| CommandType.Slash
|
||||
| CommandType.CtxMsg
|
||||
| CommandType.CtxUser
|
||||
>(
|
||||
options?: PublishOptions
|
||||
) {
|
||||
return CommandInitPlugin<T>(async ({ module }) => {
|
||||
// Users need to provide their own useContainer function.
|
||||
const [client] = useContainer("@sern/client");
|
||||
const defaultOptions = {
|
||||
guildIds: [],
|
||||
dmPermission: undefined,
|
||||
defaultMemberPermissions: null,
|
||||
};
|
||||
|
||||
function c(e: unknown) {
|
||||
console.error("publish command didnt work for", module.name);
|
||||
console.error(e);
|
||||
}
|
||||
const log =
|
||||
(...message: any[]) =>
|
||||
() =>
|
||||
console.log(...message);
|
||||
const logged = (...message: any[]) => log(message);
|
||||
/**
|
||||
* a local function that returns either one value or the other,
|
||||
* depending on {t}'s CommandType. If the commandtype of
|
||||
* this module is CommandType.Both or CommandType.Text or CommandType.Slash,
|
||||
* return 'is', else return 'els'
|
||||
* @param t
|
||||
* @returns S | T
|
||||
*/
|
||||
const appCmd = <V extends CommandType, S, T>(t: V) => {
|
||||
return (is: S, els: T) =>
|
||||
(t & CommandType.Both) !== 0 ? is : els;
|
||||
};
|
||||
const curAppType = CommandTypeRaw[module.type];
|
||||
const createCommandData = () => {
|
||||
const cmd = appCmd(module.type);
|
||||
return {
|
||||
name: module.name,
|
||||
type: curAppType,
|
||||
description: cmd(module.description, ""),
|
||||
options: cmd(
|
||||
optionsTransformer(
|
||||
(module as SlashCommand).options ?? []
|
||||
),
|
||||
[]
|
||||
),
|
||||
defaultMemberPermissions,
|
||||
dmPermission,
|
||||
} as ApplicationCommandData;
|
||||
};
|
||||
options = {...defaultOptions, ...options} as PublishOptions &
|
||||
ValidPublishOptions;
|
||||
let {defaultMemberPermissions, dmPermission, guildIds} =
|
||||
options as unknown as ValidPublishOptions;
|
||||
|
||||
try {
|
||||
const commandData = createCommandData();
|
||||
function c(e: unknown) {
|
||||
console.error("publish command didnt work for", module.name);
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
if (!guildIds.length) {
|
||||
const cmd = (
|
||||
await client.application!.commands.fetch()
|
||||
).find(
|
||||
(c) => c.name === module.name && c.type === curAppType
|
||||
);
|
||||
if (cmd) {
|
||||
if (!cmd.equals(commandData, true)) {
|
||||
logged(
|
||||
`Found differences in global command ${module.name}`
|
||||
);
|
||||
cmd.edit(commandData).then(
|
||||
log(
|
||||
`${module.name} updated with new data successfully!`
|
||||
)
|
||||
);
|
||||
}
|
||||
return controller.next();
|
||||
}
|
||||
client
|
||||
.application!.commands.create(commandData)
|
||||
.then(log("Command created", module.name))
|
||||
.catch(c);
|
||||
return controller.next();
|
||||
}
|
||||
const log =
|
||||
(...message: any[]) =>
|
||||
() =>
|
||||
console.log(...message);
|
||||
const logged = (...message: any[]) => log(message);
|
||||
/**
|
||||
* a local function that returns either one value or the other,
|
||||
* depending on {t}'s CommandType. If the commandtype of
|
||||
* this module is CommandType.Both or CommandType.Text or CommandType.Slash,
|
||||
* return 'is', else return 'els'
|
||||
* @param t
|
||||
* @returns S | T
|
||||
*/
|
||||
const appCmd = <V extends CommandType, S, T>(t: V) => {
|
||||
return (is: S, els: T) =>
|
||||
(t & CommandType.Both) !== 0 ? is : els;
|
||||
};
|
||||
const curAppType = CommandTypeRaw[module.type];
|
||||
const createCommandData = () => {
|
||||
const cmd = appCmd(module.type);
|
||||
return {
|
||||
name: module.name,
|
||||
type: curAppType,
|
||||
description: cmd(module.description, ""),
|
||||
options: cmd(
|
||||
optionsTransformer(
|
||||
(module as SlashCommand).options ?? []
|
||||
),
|
||||
[]
|
||||
),
|
||||
defaultMemberPermissions,
|
||||
dmPermission,
|
||||
} as ApplicationCommandData;
|
||||
};
|
||||
|
||||
for (const id of guildIds) {
|
||||
const guild = await client.guilds.fetch(id).catch(c);
|
||||
if (!guild) continue;
|
||||
const guildCmd = (await guild.commands.fetch()).find(
|
||||
(c) => c.name === module.name && c.type === curAppType
|
||||
);
|
||||
if (guildCmd) {
|
||||
if (!guildCmd.equals(commandData, true)) {
|
||||
logged(
|
||||
`Found differences in command ${module.name}`
|
||||
);
|
||||
guildCmd
|
||||
.edit(commandData)
|
||||
.then(
|
||||
log(
|
||||
`${module.name} updated with new data successfully!`
|
||||
)
|
||||
)
|
||||
.catch(c);
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
guild.commands
|
||||
.create(commandData)
|
||||
.then(
|
||||
log(
|
||||
"Guild Command created",
|
||||
module.name,
|
||||
guild.name
|
||||
)
|
||||
)
|
||||
.catch(c);
|
||||
}
|
||||
return controller.next();
|
||||
} catch (e) {
|
||||
logged("Command did not register" + module.name);
|
||||
logged(e);
|
||||
return controller.stop();
|
||||
}
|
||||
},
|
||||
};
|
||||
try {
|
||||
const commandData = createCommandData();
|
||||
|
||||
if (!guildIds.length) {
|
||||
const cmd = (
|
||||
await client.application!.commands.fetch()
|
||||
).find(
|
||||
(c) => c.name === module.name && c.type === curAppType
|
||||
);
|
||||
if (cmd) {
|
||||
if (!cmd.equals(commandData, true)) {
|
||||
logged(
|
||||
`Found differences in global command ${module.name}`
|
||||
);
|
||||
cmd.edit(commandData).then(
|
||||
log(
|
||||
`${module.name} updated with new data successfully!`
|
||||
)
|
||||
);
|
||||
}
|
||||
return controller.next();
|
||||
}
|
||||
client
|
||||
.application!.commands.create(commandData)
|
||||
.then(log("Command created", module.name))
|
||||
.catch(c);
|
||||
return controller.next();
|
||||
}
|
||||
|
||||
for (const id of guildIds) {
|
||||
const guild = await client.guilds.fetch(id).catch(c);
|
||||
if (!guild) continue;
|
||||
const guildCmd = (await guild.commands.fetch()).find(
|
||||
(c) => c.name === module.name && c.type === curAppType
|
||||
);
|
||||
if (guildCmd) {
|
||||
if (!guildCmd.equals(commandData, true)) {
|
||||
logged(
|
||||
`Found differences in command ${module.name}`
|
||||
);
|
||||
guildCmd
|
||||
.edit(commandData)
|
||||
.then(
|
||||
log(
|
||||
`${module.name} updated with new data successfully!`
|
||||
)
|
||||
)
|
||||
.catch(c);
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
guild.commands
|
||||
.create(commandData)
|
||||
.then(
|
||||
log(
|
||||
"Guild Command created",
|
||||
module.name,
|
||||
guild.name
|
||||
)
|
||||
)
|
||||
.catch(c);
|
||||
}
|
||||
return controller.next();
|
||||
} catch (e) {
|
||||
logged("Command did not register" + module.name);
|
||||
logged(e);
|
||||
return controller.stop();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function optionsTransformer(ops: Array<SernOptionsData>) {
|
||||
return ops.map((el) =>
|
||||
el.autocomplete ? (({ command, ...el }) => el)(el) : el
|
||||
);
|
||||
return ops.map((el) =>
|
||||
el.autocomplete ? (({command, ...el}) => el)(el) : el
|
||||
);
|
||||
}
|
||||
|
||||
export const CommandTypeRaw = {
|
||||
[CommandType.Both]: ApplicationCommandType.ChatInput,
|
||||
[CommandType.CtxUser]: ApplicationCommandType.Message,
|
||||
[CommandType.CtxMsg]: ApplicationCommandType.User,
|
||||
[CommandType.Slash]: ApplicationCommandType.ChatInput,
|
||||
} as const;
|
||||
|
||||
export type NonEmptyArray<T extends `${number}` = `${number}`> = [T, ...T[]];
|
||||
|
||||
export interface ValidPublishOptions {
|
||||
guildIds: string[];
|
||||
dmPermission: boolean;
|
||||
defaultMemberPermissions: PermissionResolvable;
|
||||
guildIds: string[];
|
||||
dmPermission: boolean;
|
||||
defaultMemberPermissions: PermissionResolvable;
|
||||
}
|
||||
|
||||
interface GuildPublishOptions {
|
||||
guildIds?: NonEmptyArray;
|
||||
defaultMemberPermissions?: PermissionResolvable;
|
||||
dmPermission?: never;
|
||||
guildIds?: NonEmptyArray;
|
||||
defaultMemberPermissions?: PermissionResolvable;
|
||||
dmPermission?: never;
|
||||
}
|
||||
|
||||
interface GlobalPublishOptions {
|
||||
defaultMemberPermissions?: PermissionResolvable;
|
||||
dmPermission?: false;
|
||||
guildIds?: never;
|
||||
defaultMemberPermissions?: PermissionResolvable;
|
||||
dmPermission?: false;
|
||||
guildIds?: never;
|
||||
}
|
||||
|
||||
type BasePublishOptions = GuildPublishOptions | GlobalPublishOptions;
|
||||
|
||||
export type PublishOptions = BasePublishOptions &
|
||||
(
|
||||
| Required<Pick<BasePublishOptions, "defaultMemberPermissions">>
|
||||
| (
|
||||
| Required<Pick<BasePublishOptions, "dmPermission">>
|
||||
| Required<Pick<BasePublishOptions, "guildIds">>
|
||||
)
|
||||
);
|
||||
(
|
||||
| Required<Pick<BasePublishOptions, "defaultMemberPermissions">>
|
||||
| (
|
||||
| Required<Pick<BasePublishOptions, "dmPermission">>
|
||||
| Required<Pick<BasePublishOptions, "guildIds">>
|
||||
)
|
||||
);
|
||||
@@ -1,3 +1,4 @@
|
||||
// @ts-nocheck
|
||||
/**
|
||||
* This is perm check, it allows users to parse the permission you want and let the plugin do the rest. (check bot or user for that perm).
|
||||
*
|
||||
@@ -19,10 +20,7 @@
|
||||
|
||||
import type { GuildMember, PermissionResolvable } from "discord.js";
|
||||
import {
|
||||
CommandType,
|
||||
Controller,
|
||||
EventPlugin,
|
||||
PluginType,
|
||||
CommandType, CommandControlPlugin, controller,
|
||||
} from "@sern/handler";
|
||||
|
||||
function payload(resp?: string) {
|
||||
@@ -33,72 +31,68 @@ function payload(resp?: string) {
|
||||
} as const;
|
||||
}
|
||||
|
||||
export function requirePermission<T extends CommandType>(
|
||||
export function requirePermission(
|
||||
target: "user" | "bot" | "both",
|
||||
perm: PermissionResolvable[],
|
||||
response?: string
|
||||
): EventPlugin<T> {
|
||||
return {
|
||||
type: PluginType.Event,
|
||||
description: "Checks bot/user perms",
|
||||
async execute(event, controller: Controller) {
|
||||
const [ctx] = event;
|
||||
if (ctx.guild === null) {
|
||||
ctx.reply(payload("This command cannot be used here"));
|
||||
console.warn(
|
||||
"PermCheck > A command stopped because we couldn't check a users permissions (was used in dms)"
|
||||
); //delete this line if you dont want to be notified when a command is used outside of a guild/server
|
||||
return controller.stop();
|
||||
}
|
||||
const bot = (await ctx.guild.members.fetchMe({
|
||||
cache: false,
|
||||
})!) as GuildMember;
|
||||
const memm = ctx.member! as GuildMember;
|
||||
switch (target) {
|
||||
//*********************************************************************************************************************//
|
||||
case "bot":
|
||||
if (!bot.permissions.has(perm)) {
|
||||
if (!response)
|
||||
response = `I cannot use this command, please give me \`${perm.join(
|
||||
", "
|
||||
)}\` permission(s).`;
|
||||
await ctx.reply(payload(response));
|
||||
return controller.stop();
|
||||
}
|
||||
return controller.next();
|
||||
//*********************************************************************************************************************//
|
||||
case "user":
|
||||
if (!memm.permissions.has(perm)) {
|
||||
if (!response)
|
||||
response = `You cannot use this command because you are missing \`${perm.join(
|
||||
", "
|
||||
)}\` permission(s).`;
|
||||
await ctx.reply(payload(response));
|
||||
return controller.stop();
|
||||
}
|
||||
return controller.next();
|
||||
//*********************************************************************************************************************//
|
||||
case "both":
|
||||
if (
|
||||
!bot.permissions.has(perm) ||
|
||||
!memm.permissions.has(perm)
|
||||
) {
|
||||
if (!response)
|
||||
response = `Please ensure <@${bot.user.id}> and <@${
|
||||
memm.user.id
|
||||
}> both have \`${perm.join(", ")}\` permission(s).`;
|
||||
await ctx.reply(payload(response));
|
||||
return controller.stop();
|
||||
}
|
||||
return controller.next();
|
||||
//*********************************************************************************************************************//
|
||||
default:
|
||||
console.warn(
|
||||
"Perm Check >>> You didn't specify user or bot."
|
||||
);
|
||||
ctx.reply(payload("User or Bot was not specified."));
|
||||
) {
|
||||
return CommandControlPlugin<CommandType.Both>(async (ctx, args) => {
|
||||
if (ctx.guild === null) {
|
||||
ctx.reply(payload("This command cannot be used here"));
|
||||
console.warn(
|
||||
"PermCheck > A command stopped because we couldn't check a users permissions (was used in dms)"
|
||||
); //delete this line if you dont want to be notified when a command is used outside of a guild/server
|
||||
return controller.stop();
|
||||
}
|
||||
const bot = (await ctx.guild.members.fetchMe({
|
||||
cache: false,
|
||||
})!) as GuildMember;
|
||||
const memm = ctx.member! as GuildMember;
|
||||
switch (target) {
|
||||
//*********************************************************************************************************************//
|
||||
case "bot":
|
||||
if (!bot.permissions.has(perm)) {
|
||||
if (!response)
|
||||
response = `I cannot use this command, please give me \`${perm.join(
|
||||
", "
|
||||
)}\` permission(s).`;
|
||||
await ctx.reply(payload(response));
|
||||
return controller.stop();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
return controller.next();
|
||||
//*********************************************************************************************************************//
|
||||
case "user":
|
||||
if (!memm.permissions.has(perm)) {
|
||||
if (!response)
|
||||
response = `You cannot use this command because you are missing \`${perm.join(
|
||||
", "
|
||||
)}\` permission(s).`;
|
||||
await ctx.reply(payload(response));
|
||||
return controller.stop();
|
||||
}
|
||||
return controller.next();
|
||||
//*********************************************************************************************************************//
|
||||
case "both":
|
||||
if (
|
||||
!bot.permissions.has(perm) ||
|
||||
!memm.permissions.has(perm)
|
||||
) {
|
||||
if (!response)
|
||||
response = `Please ensure <@${bot.user.id}> and <@${
|
||||
memm.user.id
|
||||
}> both have \`${perm.join(", ")}\` permission(s).`;
|
||||
await ctx.reply(payload(response));
|
||||
return controller.stop();
|
||||
}
|
||||
return controller.next();
|
||||
//*********************************************************************************************************************//
|
||||
default:
|
||||
console.warn(
|
||||
"Perm Check >>> You didn't specify user or bot."
|
||||
);
|
||||
ctx.reply(payload("User or Bot was not specified."));
|
||||
return controller.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -19,25 +19,24 @@
|
||||
* ```
|
||||
*/
|
||||
|
||||
import { CommandType, EventPlugin, PluginType } from "@sern/handler";
|
||||
import { CommandType, controller, CommandControlPlugin } from "@sern/handler";
|
||||
|
||||
export function serverOnly(
|
||||
guildId: string[],
|
||||
failMessage = "This command is not available in this guild. \nFor permission to use in your server, please contact my developer."
|
||||
): EventPlugin<CommandType.Both> {
|
||||
return {
|
||||
type: PluginType.Event,
|
||||
description: "Checks if a command is available in a specific server.",
|
||||
async execute([ctx, args], controller) {
|
||||
if (!guildId.includes(ctx.guildId)) {
|
||||
await ctx.reply(failMessage).then(async (m) => {
|
||||
setTimeout(async () => {
|
||||
await m.delete();
|
||||
}, 3000);
|
||||
});
|
||||
return controller.stop();
|
||||
}
|
||||
return controller.next();
|
||||
},
|
||||
};
|
||||
guildId: string[],
|
||||
failMessage = "This command is not available in this guild. \nFor permission to use in your server, please contact my developer."
|
||||
) {
|
||||
return CommandControlPlugin<CommandType.Both>(async ( ctx, _) => {
|
||||
if(ctx.guildId == null) {
|
||||
return controller.stop()
|
||||
}
|
||||
if (!guildId.includes(ctx.guildId)) {
|
||||
ctx.reply(failMessage).then(async (m) => {
|
||||
setTimeout(async () => {
|
||||
await m.delete();
|
||||
}, 3000);
|
||||
});
|
||||
return controller.stop();
|
||||
}
|
||||
return controller.next();
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user