Add full JSDocs for the plugin API
parent
60ae79d4e4
commit
d5219556a7
|
@ -7,17 +7,11 @@ The path is relative to the bot's folder.
|
|||
Plugins are automatically loaded on bot startup.
|
||||
|
||||
## Creating a plugin
|
||||
Create a `.js` file that exports a function.
|
||||
This function will be called when the plugin is loaded, with 1 argument: an object that has the following properties:
|
||||
* `bot` - the [Eris Client object](https://abal.moe/Eris/docs/Client)
|
||||
* `knex` - the [Knex database object](https://knexjs.org/#Builder)
|
||||
* `config` - the loaded config
|
||||
* `commands` - an object with functions to add and manage commands
|
||||
* `attachments` - an object with functions to save attachments and manage attachment storage types
|
||||
Plugins are simply `.js` files that export a function that gets called when the plugin is loaded.
|
||||
|
||||
See [src/plugins.js#L4](../src/plugins.js#L4) for more details
|
||||
For details about the function arguments, see [Plugin API](#plugin-api) below.
|
||||
|
||||
### Example plugin file
|
||||
### Example plugin
|
||||
This example adds a command `!mycommand` that replies with `"Reply from my custom plugin!"` when the command is used inside a modmail inbox thread channel.
|
||||
```js
|
||||
module.exports = function({ bot, knex, config, commands }) {
|
||||
|
@ -40,6 +34,36 @@ module.exports = function({ attachments }) {
|
|||
```
|
||||
To use this custom attachment storage type, you would set the `attachmentStorage` config option to `"original"`.
|
||||
|
||||
### Plugin API
|
||||
The first and only argument to the plugin function is an object with the following properties:
|
||||
|
||||
| Property | Description |
|
||||
| -------- | ----------- |
|
||||
| `bot` | [Eris Client instance](https://abal.moe/Eris/docs/Client) |
|
||||
| `knex` | [Knex database object](https://knexjs.org/#Builder) |
|
||||
| `config` | The loaded config |
|
||||
| `commands` | An object with functions to add and manage commands |
|
||||
| `attachments` | An object with functions to save attachments and manage attachment storage types |
|
||||
| — `addStorageType(name, handler)` | Function to add a new attachment storage type |
|
||||
| — `downloadAttachment(attachment)` | Function to add a new attachment storage type |
|
||||
|
||||
* `bot` - the [Eris Client object](https://abal.moe/Eris/docs/Client)
|
||||
* `knex` - the [Knex database object](https://knexjs.org/#Builder)
|
||||
* `config` - the loaded config
|
||||
* `commands` - an object with functions to add and manage commands
|
||||
* `attachments` - an object with functions to save attachments and manage attachment storage types
|
||||
* `attachments.addStorageType(name, handler)`
|
||||
|
||||
Create a `.js` file that exports a function.
|
||||
This function will be called when the plugin is loaded, with 1 argument: an object that has the following properties:
|
||||
* `bot` - the [Eris Client object](https://abal.moe/Eris/docs/Client)
|
||||
* `knex` - the [Knex database object](https://knexjs.org/#Builder)
|
||||
* `config` - the loaded config
|
||||
* `commands` - an object with functions to add and manage commands
|
||||
* `attachments` - an object with functions to save attachments and manage attachment storage types
|
||||
|
||||
See [src/plugins.js#L4](../src/plugins.js#L4) for more details
|
||||
|
||||
## Work in progress
|
||||
The current plugin API is fairly rudimentary and will be expanded on in the future.
|
||||
The API can change in non-major releases during this early stage. Keep an eye on [CHANGELOG.md](../CHANGELOG.md) for any changes.
|
||||
|
|
|
@ -5,6 +5,51 @@ const utils = require("./utils");
|
|||
const threads = require("./data/threads");
|
||||
const Thread = require("./data/Thread");
|
||||
|
||||
/**
|
||||
* @callback CommandFn
|
||||
* @param {Eris.Message} msg
|
||||
* @param {object} args
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback InboxThreadCommandHandler
|
||||
* @param {Eris.Message} msg
|
||||
* @param {object} args
|
||||
* @param {Thread} thread
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback AddGlobalCommandFn
|
||||
* @param {string} trigger
|
||||
* @param {string} parameters
|
||||
* @param {CommandFn} handler
|
||||
* @param {ICommandConfig} commandConfig
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback AddInboxServerCommandFn
|
||||
* @param {string} trigger
|
||||
* @param {string} parameters
|
||||
* @param {CommandFn} handler
|
||||
* @param {ICommandConfig} commandConfig
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback AddInboxThreadCommandFn
|
||||
* Add a command that can only be invoked in a thread on the inbox server
|
||||
*
|
||||
* @param {string} trigger
|
||||
* @param {string} parameters
|
||||
* @param {InboxThreadCommandHandler} handler
|
||||
* @param {ICommandConfig} commandConfig
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback AddAliasFn
|
||||
* @param {string} originalCmd
|
||||
* @param {string} alias
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
createCommandManager(bot) {
|
||||
const manager = new CommandManager({
|
||||
|
@ -52,6 +97,7 @@ module.exports = {
|
|||
|
||||
/**
|
||||
* Add a command that can be invoked anywhere
|
||||
* @type {AddGlobalCommandFn}
|
||||
*/
|
||||
const addGlobalCommand = (trigger, parameters, handler, commandConfig = {}) => {
|
||||
let aliases = aliasMap.has(trigger) ? [...aliasMap.get(trigger)] : [];
|
||||
|
@ -63,6 +109,7 @@ module.exports = {
|
|||
|
||||
/**
|
||||
* Add a command that can only be invoked on the inbox server
|
||||
* @type {AddInboxServerCommandFn}
|
||||
*/
|
||||
const addInboxServerCommand = (trigger, parameters, handler, commandConfig = {}) => {
|
||||
const aliases = aliasMap.has(trigger) ? [...aliasMap.get(trigger)] : [];
|
||||
|
@ -86,19 +133,9 @@ module.exports = {
|
|||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @callback InboxThreadCommandHandler
|
||||
* @param {Eris.Message} msg
|
||||
* @param {object} args
|
||||
* @param {Thread} thread
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add a command that can only be invoked in a thread on the inbox server
|
||||
* @param {string|RegExp} trigger
|
||||
* @param {string|IParameter[]} parameters
|
||||
* @param {InboxThreadCommandHandler} handler
|
||||
* @param {ICommandConfig} commandConfig
|
||||
* @type {AddInboxThreadCommandFn}
|
||||
*/
|
||||
const addInboxThreadCommand = (trigger, parameters, handler, commandConfig = {}) => {
|
||||
const aliases = aliasMap.has(trigger) ? [...aliasMap.get(trigger)] : [];
|
||||
|
@ -124,6 +161,9 @@ module.exports = {
|
|||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @type {AddAliasFn}
|
||||
*/
|
||||
const addAlias = (originalCmd, alias) => {
|
||||
if (! aliasMap.has(originalCmd)) {
|
||||
aliasMap.set(originalCmd, new Set());
|
||||
|
|
|
@ -26,21 +26,54 @@ function getErrorResult(msg = null) {
|
|||
}
|
||||
|
||||
/**
|
||||
* An attachment storage option that simply forwards the original attachment URL
|
||||
* @param {Eris.Attachment} attachment
|
||||
* @returns {{url: string}}
|
||||
* @callback AddAttachmentStorageTypeFn
|
||||
* @param {string} name
|
||||
* @param {AttachmentStorageTypeHandler} handler
|
||||
*/
|
||||
function passthroughOriginalAttachment(attachment) {
|
||||
|
||||
/**
|
||||
* @callback AttachmentStorageTypeHandler
|
||||
* @param {Eris.Attachment} attachment
|
||||
* @return {AttachmentStorageTypeResult|Promise<AttachmentStorageTypeResult>}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} AttachmentStorageTypeResult
|
||||
* @property {string} url
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback DownloadAttachmentFn
|
||||
* @param {Eris.Attachment} attachment
|
||||
* @param {number?} tries Used internally, don't pass
|
||||
* @return {Promise<DownloadAttachmentResult>}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} DownloadAttachmentResult
|
||||
* @property {string} path
|
||||
* @property {DownloadAttachmentCleanupFn} cleanup
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback DownloadAttachmentCleanupFn
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {AttachmentStorageTypeHandler}
|
||||
*/
|
||||
let passthroughOriginalAttachment; // Workaround to inconsistent IDE bug with @type and anonymous functions
|
||||
passthroughOriginalAttachment = (attachment) => {
|
||||
return { url: attachment.url };
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* An attachment storage option that downloads each attachment and serves them from a local web server
|
||||
* @param {Eris.Attachment} attachment
|
||||
* @param {Number=0} tries
|
||||
* @returns {Promise<{ url: string }>}
|
||||
* @type {AttachmentStorageTypeHandler}
|
||||
*/
|
||||
async function saveLocalAttachment(attachment) {
|
||||
let saveLocalAttachment; // Workaround to inconsistent IDE bug with @type and anonymous functions
|
||||
saveLocalAttachment = async (attachment) => {
|
||||
const targetPath = getLocalAttachmentPath(attachment.id);
|
||||
|
||||
try {
|
||||
|
@ -60,14 +93,12 @@ async function saveLocalAttachment(attachment) {
|
|||
const url = await getLocalAttachmentUrl(attachment.id, attachment.filename);
|
||||
|
||||
return { url };
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Eris.Attachment} attachment
|
||||
* @param {Number} tries
|
||||
* @returns {Promise<{ path: string, cleanup: function }>}
|
||||
* @type {DownloadAttachmentFn}
|
||||
*/
|
||||
function downloadAttachment(attachment, tries = 0) {
|
||||
const downloadAttachment = (attachment, tries = 0) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (tries > 3) {
|
||||
console.error("Attachment download failed after 3 tries:", attachment);
|
||||
|
@ -94,7 +125,7 @@ function downloadAttachment(attachment, tries = 0) {
|
|||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the filesystem path for the given attachment id
|
||||
|
@ -119,10 +150,10 @@ function getLocalAttachmentUrl(attachmentId, desiredName = null) {
|
|||
/**
|
||||
* An attachment storage option that downloads each attachment and re-posts them to a specified Discord channel.
|
||||
* The re-posted attachment is then linked in the actual thread.
|
||||
* @param {Eris.Attachment} attachment
|
||||
* @returns {Promise<{ url: string }>}
|
||||
* @type {AttachmentStorageTypeHandler}
|
||||
*/
|
||||
async function saveDiscordAttachment(attachment) {
|
||||
let saveDiscordAttachment; // Workaround to inconsistent IDE bug with @type and anonymous functions
|
||||
saveDiscordAttachment = async (attachment) => {
|
||||
if (attachment.size > 1024 * 1024 * 8) {
|
||||
return getErrorResult("attachment too large (max 8MB)");
|
||||
}
|
||||
|
@ -144,7 +175,7 @@ async function saveDiscordAttachment(attachment) {
|
|||
if (! savedAttachment) return getErrorResult();
|
||||
|
||||
return { url: savedAttachment.url };
|
||||
}
|
||||
};
|
||||
|
||||
async function createDiscordAttachmentMessage(channel, file, tries = 0) {
|
||||
tries++;
|
||||
|
@ -197,9 +228,12 @@ function saveAttachment(attachment) {
|
|||
return attachmentSavePromises[attachment.id];
|
||||
}
|
||||
|
||||
function addStorageType(name, handler) {
|
||||
/**
|
||||
* @type AddAttachmentStorageTypeFn
|
||||
*/
|
||||
const addStorageType = (name, handler) => {
|
||||
attachmentStorageTypes[name] = handler;
|
||||
}
|
||||
};
|
||||
|
||||
addStorageType("original", passthroughOriginalAttachment);
|
||||
addStorageType("local", saveLocalAttachment);
|
||||
|
|
|
@ -212,6 +212,20 @@ const defaultFormatters = {
|
|||
*/
|
||||
const formatters = { ...defaultFormatters };
|
||||
|
||||
/**
|
||||
* @typedef {object} FormattersExport
|
||||
* @property {MessageFormatters} formatters Read only
|
||||
* @property {function(FormatStaffReplyDM): void} setStaffReplyDMFormatter
|
||||
* @property {function(FormatStaffReplyThreadMessage): void} setStaffReplyThreadMessageFormatter
|
||||
* @property {function(FormatUserReplyThreadMessage): void} setUserReplyThreadMessageFormatter
|
||||
* @property {function(FormatStaffReplyEditNotificationThreadMessage): void} setStaffReplyEditNotificationThreadMessageFormatter
|
||||
* @property {function(FormatStaffReplyDeletionNotificationThreadMessage): void} setStaffReplyDeletionNotificationThreadMessageFormatter
|
||||
* @property {function(FormatLog): void} setLogFormatter
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {FormattersExport}
|
||||
*/
|
||||
module.exports = {
|
||||
formatters: new Proxy(formatters, {
|
||||
set() {
|
||||
|
@ -219,50 +233,26 @@ module.exports = {
|
|||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* @param {FormatStaffReplyDM} fn
|
||||
* @return {void}
|
||||
*/
|
||||
setStaffReplyDMFormatter(fn) {
|
||||
formatters.formatStaffReplyDM = fn;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {FormatStaffReplyThreadMessage} fn
|
||||
* @return {void}
|
||||
*/
|
||||
setStaffReplyThreadMessageFormatter(fn) {
|
||||
formatters.formatStaffReplyThreadMessage = fn;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {FormatUserReplyThreadMessage} fn
|
||||
* @return {void}
|
||||
*/
|
||||
setUserReplyThreadMessageFormatter(fn) {
|
||||
formatters.formatUserReplyThreadMessage = fn;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {FormatStaffReplyEditNotificationThreadMessage} fn
|
||||
* @return {void}
|
||||
*/
|
||||
setStaffReplyEditNotificationThreadMessageFormatter(fn) {
|
||||
formatters.formatStaffReplyEditNotificationThreadMessage = fn;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {FormatStaffReplyDeletionNotificationThreadMessage} fn
|
||||
* @return {void}
|
||||
*/
|
||||
setStaffReplyDeletionNotificationThreadMessageFormatter(fn) {
|
||||
formatters.formatStaffReplyDeletionNotificationThreadMessage = fn;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {FormatLog} fn
|
||||
* @return {void}
|
||||
*/
|
||||
setLogFormatter(fn) {
|
||||
formatters.formatLog = fn;
|
||||
},
|
||||
|
|
|
@ -26,17 +26,24 @@ const Eris = require("eris");
|
|||
* @return {void|Promise<void>}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @callback AddBeforeNewThreadHookFn
|
||||
* @param {BeforeNewThreadHookData} fn
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type BeforeNewThreadHookData[]
|
||||
*/
|
||||
const beforeNewThreadHooks = [];
|
||||
|
||||
/**
|
||||
* @param {BeforeNewThreadHookData} fn
|
||||
* @type {AddBeforeNewThreadHookFn}
|
||||
*/
|
||||
function beforeNewThread(fn) {
|
||||
let beforeNewThread; // Workaround to inconsistent IDE bug with @type and anonymous functions
|
||||
beforeNewThread = (fn) => {
|
||||
beforeNewThreadHooks.push(fn);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
const { CommandManager } = require("knub-command-manager");
|
||||
const { Client } = require("eris");
|
||||
const Knex = require("knex");
|
||||
|
||||
/**
|
||||
* @typedef {object} PluginAPI
|
||||
* @property {Client} bot
|
||||
* @property {Knex} knex
|
||||
* @property {ModmailConfig} config
|
||||
* @property {PluginCommandsAPI} commands
|
||||
* @property {PluginAttachmentsAPI} attachments
|
||||
* @property {PluginHooksAPI} hooks
|
||||
* @property {FormattersExport} formats
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PluginCommandsAPI
|
||||
* @property {CommandManager} manager
|
||||
* @property {AddGlobalCommandFn} addGlobalCommand
|
||||
* @property {AddInboxServerCommandFn} addInboxServerCommand
|
||||
* @property {AddInboxThreadCommandFn} addInboxThreadCommand
|
||||
* @property {AddAliasFn} addAlias
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PluginAttachmentsAPI
|
||||
* @property {AddAttachmentStorageTypeFn} addStorageType
|
||||
* @property {DownloadAttachmentFn} downloadAttachment
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {object} PluginHooksAPI
|
||||
* @property {AddBeforeNewThreadHookFn} beforeNewThread
|
||||
*/
|
|
@ -3,6 +3,13 @@ const { beforeNewThread } = require("./hooks/beforeNewThread");
|
|||
const formats = require("./formatters");
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* @param bot
|
||||
* @param knex
|
||||
* @param config
|
||||
* @param commands
|
||||
* @returns {PluginAPI}
|
||||
*/
|
||||
getPluginAPI({ bot, knex, config, commands }) {
|
||||
return {
|
||||
bot,
|
||||
|
|
Loading…
Reference in New Issue