diff --git a/assets/images/eventplugins.drawio.svg b/assets/images/eventplugins.drawio.svg new file mode 100644 index 000000000..b97d518a3 --- /dev/null +++ b/assets/images/eventplugins.drawio.svg @@ -0,0 +1,4 @@ + + + +
Event
Event
Plugin 1
Plugin 1
Plugin 2
Plugin 2
Plugin 3
Plugin 3
Is successful
Is successful
Command Executes
Command Executes
Ignore
Ignore
True
True
False
False
Text is not SVG - cannot display
\ No newline at end of file diff --git a/guide-en/README.md b/guide-en/README.md index 77649dcd5..73b6bf6a1 100644 --- a/guide-en/README.md +++ b/guide-en/README.md @@ -1,7 +1,26 @@ # Introduction -Welcome to the Sern's official guide. This guide will teach you how to use the Sern. +Welcome to sern's official guide. This guide will go through all the core features of the framework. +- 💖 Thank you for choosing sern to be your framework! + +- Teaching the discord.js library and / or Javascript / Typescript is out of scope of this project, so the documentation assumes you already know these elements. + +- discord.js@dev is the only supported version at the moment. There are plans to roll out a version of sern in discord.js@13.xx.xx in the future. ## You will learn -* How to use the Sern with the [Sern CLI](https://github.com/sern-handler/cli) -(TODO) +* [sern's goal](walkthrough/goal.md) +* How to use sern with the [CLI](walkthrough/cli.md) +* [Your first command](walkthrough/first-command.md) +* [The Context class](walkthrough/first-command.md#context-class) + +### Working with plugins +* [Plugins](walkthrough/plugins.md) + - [Command Plugins](walkthrough/plugins.md#command-plugins) + - [Event Plugins](walkthrough/plugins.md#event-plugins) +### Events +* [The SernEmitter class](walkthrough/sern-emitter.md) +* [Your first event](walkthrough/first-event.md) + +### Good to know +[//]: <> (Maybe redirect to the sern.config.json section in future) +* [sern.config.json](walkthrough/good-to-know.md) diff --git a/guide-en/walkthrough/cli.md b/guide-en/walkthrough/cli.md new file mode 100644 index 000000000..e93fa76f1 --- /dev/null +++ b/guide-en/walkthrough/cli.md @@ -0,0 +1,21 @@ +# CLI + +Setting up the [CLI](https://github.com/sern-handler/cli) is easy.
+- To start a brand new project, run : +``` +sern init (-y) +``` +Include the `-y` flag if you want to set up defaults. The default langauge is [Typescript](https://www.typescriptlang.org/)
+ +- To install [plugins](plugins.md) maintained by the community [repository](https://github.com/sern-handler/awesome-plugins), +``` +sern plugins +``` +This will display a menu selection of all installable plugins.
+**Note**: You must have a [sern.config.json](good-to-know.md) to use this command. +If you want to view plugins, visit the repository linked above. + +- To install extra utilities into your project +``` +sern extra +``` \ No newline at end of file diff --git a/guide-en/walkthrough/conclusion.md b/guide-en/walkthrough/conclusion.md new file mode 100644 index 000000000..553cb9f1c --- /dev/null +++ b/guide-en/walkthrough/conclusion.md @@ -0,0 +1,4 @@ +# Conclusion +If you reached this far, thank you for reading! We hope you have learned the necessities you need +to create a bot with the sern framework. If you have any other questions, bugs, feature requests, concerns, please join our +[community server](https://discord.gg/QgnfxWzrcj), and we'll be glad to answer your questions. \ No newline at end of file diff --git a/guide-en/walkthrough/first-command.md b/guide-en/walkthrough/first-command.md new file mode 100644 index 000000000..c72594e91 --- /dev/null +++ b/guide-en/walkthrough/first-command.md @@ -0,0 +1,106 @@ +# First Command +We will dissect a basic command. +If you installed a new project via the cli, This is the `ping` command located in src/commands folder. + +Typescript +```typescript +import { commandModule, CommandType } from '@sern/handler'; + +export default commandModule({ + type: CommandType.Both, + plugins: [], + description: 'A ping command', + //alias : [], + execute: async (ctx, args) => { + await ctx.reply({ content: 'Pong 🏓' }); + }, +}); +``` +Javascript +```javascript +const { CommandType, commandModule } = require('@sern/handler'); + +exports.default = commandModule({ + type: CommandType.Both, + plugins: [], + description: 'A ping command', + //alias : [], + execute: async (ctx, args) => { + await ctx.reply('Pong 🏓'); + }, +}) +``` +To view what each of these properties mean in depth, visit the [official documentation](https://sern-handler.js.org/docs). +### Types of command modules +Every command module `type` is part of an enum. This field allows type inference for the rest of a module's fields.
+All the command types can be found in the [official documentation](https://sern-handler.js.org/docs)! +

So, lets say you want to make a command module that listens to modals.

+ +**Note**: Keep in mind you'll need to send a modal with a custom id `dm-me`. This example below is the response to a modal being sent.
+ +Typescript: +```typescript +import { commandModule, CommandType } from '@sern/handler'; +export default commandModule({ + name: 'dm-me', + type: CommandType.Modal, + async execute (modal) { + const value = modal.fields.getTextInputValue('message'); + modal.client.users.fetch('182326315813306368').then( u => + u.send(value + ` from ${modal.user}`) + ); + modal.reply( { ephemeral:true, content: 'Sent' }) + } +}); +``` +Javascript: +```javascript +const { CommandType, commandModule } = require('@sern/handler'); +exports.default = commandModule({ + name: 'dm-me', + type: CommandType.Modal, + async execute (modal) { + const value = modal.fields.getTextInputValue('message'); + modal.client.users.fetch('182326315813306368').then( u => + u.send(value + ` from ${modal.user}`) + ); + modal.reply( { ephemeral:true, content: 'Sent' }) + } +}); +``` +Commands are straight forward. Keep in mind, every other property on the commandModule object is +optional **except** the type and execute function. + +# Context class +The provided Context class helps with modules of `CommandType.Both` ( A mixture of slash / legacy commands ) +In short, Slash Commands, Both Commands, Text Commands carry a Context data structure + +The Context class is passed into modules with type : +- `CommandType.Both` +- `CommandType.Slash` +- `CommandType.Text` + +Typescript: +```typescript +export default commandModule({ + name: 'ping', + type: CommandType.Both, + async execute(ctx: Context) { + await ctx.reply(`pong ${ctx.user}`) + // .reply is shared between both message and interaction! + // So is an User object! + } +}); +``` +Javascript: +```javascript +exports.default = commandModule({ + name: 'ping', + type: CommandType.Both, + async execute(ctx) { //ctx is a Context instance + await ctx.reply(`pong ${ctx.user}`) + // .reply is shared between both message and interaction! + // So is an User object! + } +}); +``` diff --git a/guide-en/walkthrough/first-event.md b/guide-en/walkthrough/first-event.md new file mode 100644 index 000000000..8934e9c48 --- /dev/null +++ b/guide-en/walkthrough/first-event.md @@ -0,0 +1,58 @@ +# Your first event module +We will dissect a basic event module.
+Typescript: +```typescript +export default eventModule({ + type: EventType.Sern, + plugins : [], //NOT SUPPORTED YET!! + name: 'module.activate', //name of event. + execute(event) { + console.log(event); + } +}) +``` +Javascript: +```javascript +exports.default = eventModule({ + type: EventType.Sern, + plugins : [], //NOT SUPPORTED YET!! + name: 'module.activate', + execute(event) { + console.log(event); + } +}) +``` +Like command modules, the `type` property denotes what kind of event it is, which +can be found [here](https://sern-handler.js.org/docs). + +To view what each of these properties mean in depth, visit the [official documentation](https://sern-handler.js.org/docs). + +
+Event modules are laid out similarly to command modules. These listen to any and all event you provide. +In the current version 1.1.0-beta, plugins are not supported. + +### Another example of an event module + +Typescript: +```typescript +export default eventModule({ + type: EventType.Discord, + plugins : [], //NOT SUPPORTED YET!! + name: 'guildMemberAdd', //name of event. + async execute(member) { + await member.client.channels.fetch('channel-id').send(`Welcome, ${member}`); + } +}) +``` + +Javascript: +```typescript +exports.default = eventModule({ + type: EventType.Discord, + plugins : [], //NOT SUPPORTED YET!! + name: 'guildMemberAdd', //name of event. + async execute(member) { + await member.client.channels.fetch('channel-id').send(`Welcome, ${member}`); + } +}) +``` \ No newline at end of file diff --git a/guide-en/walkthrough/goal.md b/guide-en/walkthrough/goal.md new file mode 100644 index 000000000..cdf6e54e4 --- /dev/null +++ b/guide-en/walkthrough/goal.md @@ -0,0 +1,6 @@ + +# Goal + +sern strives to be minimalist, but with all batteries included. Meaning, this framework provides the necessary tools +to start up a bot in minutes, and leaves plenty room space to customize your experience and create an amazing project. +It should include all the tools for any bot at any scale. diff --git a/guide-en/walkthrough/good-to-know.md b/guide-en/walkthrough/good-to-know.md new file mode 100644 index 000000000..3d04e5492 --- /dev/null +++ b/guide-en/walkthrough/good-to-know.md @@ -0,0 +1,18 @@ +# Good to know + +## sern.config.json +A sern.config.json, although not necessary, allows your project to communicate with our cli. +For example, when installing typescript plugins, the language property is necessary to install from our +[open source repository](https://github.com/sern-handler/awesome-plugins). +Using the cli and running `sern init --sync` on pre-existing projects should install this json file in the root directory given. +Or, if this is a brand-new project, `sern init` automatically installs it. + +```json +{ + "language": "typescript", + "paths": { + "base": "src", + "commands": "commands" + } +} +``` \ No newline at end of file diff --git a/guide-en/walkthrough/plugins.md b/guide-en/walkthrough/plugins.md new file mode 100644 index 000000000..549ffe983 --- /dev/null +++ b/guide-en/walkthrough/plugins.md @@ -0,0 +1,148 @@ +# Plugins + +As of now, modules seem a little underwhelming. It appears that sern doesn't have all the features of a standard handler, +which manages permissions, categorizes, cool-downs, publishes application commands, role permissions, etc. Many important +parts that manage access and help streamline command creation to make are apparently absent. +Below is an example of an event plugin, one of the types of plugins. + +Typescript: +```typescript +export function serenOnly(): EventPlugin { + return { + type: PluginType.Event, + async execute([ctx, args], controller) { + if (ctx.user.id !== "182326315813306368") { + await ctx.reply({content: "You cannot use this command"}) + return controller.stop() + } + return controller.next(); + } + } +} +``` +Javascript: +```javascript +export function serenOnly() { + return { + type: PluginType.Event, + async execute([ctx, args], controller) { + if (ctx.user.id !== "182326315813306368") { + await ctx.reply({content: "You cannot use this command"}) + return controller.stop() + } + return controller.next(); + } + } +} +``` + +
As part of sern's extensibility, the plugins feature make sern just as powerful, if not more powerful than +standard handlers. +Plugins modify and add new behavior to standard modules, extending customizability and implementing automation. + +
At the moment, there are two types of plugins: + +- Command Plugins +- Event Plugins + +## Command Plugins +All modules are registered into sern's system. With command plugins, you can modify how commands are loaded, +or do some kind of preprocessing before they are loaded into sern. +### The controller object +```typescript +export interface Controller { + next: () => Ok; + stop: () => Err; +} +``` +An instance of the above object is passed into every plugin.
+This controls whether a module is stored into sern.
+Typescript: +```typescript +export function inDir(dir : string) : CommandPlugin { + return { + type: PluginType.Command, + async execute(wrapper, { absPath, module }, controller) { + if(path.dirname(absPath) !== dir) { + console.log(+new Date(), `${module.name} is not in the correct directory!`); + return controller.stop() + } + console.log(+new Date(), `${module.name} is in the correct directory!`); + return controller.next(); //continue + } + } +} +``` +Javascript: +```javascript +export function inDir(dir : string) { + return { + type: PluginType.Command, + async execute(wrapper, { absPath, module }, controller) { + if(path.dirname(absPath) !== dir) { + console.log(+new Date(), `${module.name} is not in the correct directory!`); + return controller.stop() + } + console.log(+new Date(), `${module.name} is in the correct directory!`); + return controller.next(); //continue + } + } +} +``` +Above, this simple plugin logs that the module has been loaded along with a timestamp.
+Again, it is up to **you** to define plugin logic! The possibilities to customize your bots is endless. + +## Event Plugins +![event-plugins](/assets/images/eventplugins.drawio.svg)
+- An event is emitted by discord.js. +- This event is passed to all plugins (**in order!!**), +- If all are successful, + +The command is executed. Calling `controller.stop()` notifies sern that this command should not be run, +and this event is ignored. +

+This flexible structure allows you to write reusable preconditions, permissions verifiers, +argument verifiers, and much more. + +

So, what does a command module look like with plugins?

+ +Typescript: +```typescript +import { commandModule, CommandType } from '@sern/handler'; + +export default commandModule({ + type: CommandType.Both, + plugins: [ + inDir("other"), + serenOnly() + ], + description: 'A ping command', + //alias : [], + execute: async (ctx, args) => { + await ctx.reply({ content: 'Pong 🏓' }); + }, +}); +``` +Javascript: +```typescript +const { commandModule, CommandType } = require('@sern/handler'); + +exports.default = commandModule({ + type: CommandType.Both, + plugins: [ + inDir("other"), + serenOnly() //The plugins in this section applied to this module! + ], + description: 'A ping command', + //alias : [], + execute: async (ctx, args) => { + await ctx.reply({ content: 'Pong 🏓' }); + }, +}); +``` +Can you predict the behavior of this command? + +- Before loading into sern, this command module will check if this module is in the correct directory `other`. +- Before an event occurs, this command module will check if the user has the id `182326315813306368`. + +If all plugins return `controller.next()`, this command replies `Pong 🏓` \ No newline at end of file diff --git a/guide-en/walkthrough/sern-emitter.md b/guide-en/walkthrough/sern-emitter.md new file mode 100644 index 000000000..d4640f33c --- /dev/null +++ b/guide-en/walkthrough/sern-emitter.md @@ -0,0 +1,7 @@ +# The SernEmitter class +You're shipped with the SernEmitter. This EventEmitter listens to +- command modules executing and its status, the `module.activate` event +- command modules registered and its status, the `module.register` event +- any errors that are recoverable, the `error` event + +
You can put these and other event listeners into event modules! diff --git a/index.html b/index.html index ac5d64854..db957c13b 100644 --- a/index.html +++ b/index.html @@ -1,7 +1,7 @@ - Sern - Handlers. Redefined. + sern - Handlers. Redefined.