268 lines
6.1 KiB
Groff
268 lines
6.1 KiB
Groff
.TH MANYBOT-PLUGIN 1 "April 2026" "ManyBot 2.4.3" "User Commands"
|
||
.SH NAME
|
||
manybot-plugin \- ManyBot plugin development guide
|
||
.SH SYNOPSIS
|
||
.B manyplug.json
|
||
.I manifest file
|
||
.br
|
||
.I src/plugins/
|
||
.B plugin directory
|
||
.SH DESCRIPTION
|
||
ManyBot plugins extend the bot's functionality without modifying the core
|
||
kernel. The kernel connects to WhatsApp and distributes messages to plugins,
|
||
which decide how to respond.
|
||
.PP
|
||
Each plugin lives in its own folder under
|
||
.I src/plugins/
|
||
with a
|
||
.B manyplug.json
|
||
manifest file and an
|
||
.B index.js
|
||
entry point.
|
||
.SH PLUGIN STRUCTURE
|
||
.nf
|
||
src/plugins/
|
||
\(do__ my-plugin/
|
||
\(bu__ manyplug.json # Plugin manifest
|
||
\(bu__ index.js # Main entry point
|
||
\(bu__ locale/ # Translations (optional)
|
||
\(bu__ en.json
|
||
\(bu__ pt.json
|
||
\(bu__ es.json
|
||
.fi
|
||
.SH MANIFEST (manyplug.json)
|
||
Every plugin must include a manifest describing its metadata:
|
||
.PP
|
||
.nf
|
||
{
|
||
"name": "my-plugin",
|
||
"version": "1.0.0",
|
||
"category": "utility",
|
||
"service": false,
|
||
"dependencies": {
|
||
"axios": "^1.6.0"
|
||
}
|
||
}
|
||
.fi
|
||
.TP
|
||
.B name
|
||
Plugin identifier. Must match the folder name.
|
||
.TP
|
||
.B version
|
||
Semantic version (e.g., "1.0.0").
|
||
.TP
|
||
.B category
|
||
Plugin type: \fButility\fR, \fBmedia\fR, \fBgame\fR, \fBhumor\fR, or \fBinfo\fR.
|
||
.TP
|
||
.B service
|
||
\fBtrue\fR for background plugins (schedulers, listeners).
|
||
\fBfalse\fR for command/event-triggered plugins.
|
||
.TP
|
||
.B dependencies
|
||
Extra npm packages required. Install with \fBnpm install\fR from project root.
|
||
.SH PLUGIN ENTRY POINT
|
||
The
|
||
.I index.js
|
||
file must export a default async function:
|
||
.PP
|
||
.nf
|
||
.B "export default async function ({ msg, api }) {"
|
||
// Plugin logic here
|
||
.B "}"
|
||
.fi
|
||
.SS Parameters
|
||
.TP
|
||
.B msg
|
||
Message object containing:
|
||
.RS
|
||
.IP \(bu 2
|
||
\fBbody\fR - Full message text
|
||
.IP \(bu 2
|
||
\fBargs\fR - Array of message tokens
|
||
.IP \(bu 2
|
||
\fBtype\fR - Message type: chat, image, video, audio, sticker, ptt, document, location
|
||
.IP \(bu 2
|
||
\fBsender\fR - Sender ID (NUMBER@c.us or NUMBER@g.us)
|
||
.IP \(bu 2
|
||
\fBsenderName\fR - Display name
|
||
.IP \(bu 2
|
||
\fBfromMe\fR - True if bot sent the message
|
||
.IP \(bu 2
|
||
\fBhasMedia\fR - True if contains media
|
||
.IP \(bu 2
|
||
\fBhasReply\fR - True if it's a reply
|
||
.IP \(bu 2
|
||
\fBisGif\fR - True if media is GIF
|
||
.IP \(bu 2
|
||
\fBtimestamp\fR - Unix timestamp
|
||
.IP \(bu 2
|
||
\fBis(cmd)\fR - Check if message starts with command
|
||
.IP \(bu 2
|
||
\fBreply(text)\fR - Reply with quote
|
||
.IP \(bu 2
|
||
\fBdownloadMedia()\fR - Download media (returns {mimetype, data})
|
||
.IP \(bu 2
|
||
\fBgetReply()\fR - Get quoted message object
|
||
.RE
|
||
.TP
|
||
.B api
|
||
API object containing:
|
||
.RS
|
||
.IP \(bu 2
|
||
\fBsend(text)\fR - Send text message
|
||
.IP \(bu 2
|
||
\fBsendVideo(path)\fR - Send video file
|
||
.IP \(bu 2
|
||
\fBsendAudio(path)\fR - Send audio as voice message
|
||
.IP \(bu 2
|
||
\fBsendImage(path, caption?)\fR - Send image with optional caption
|
||
.IP \(bu 2
|
||
\fBsendSticker(bufferOrPath)\fR - Send sticker from buffer or file
|
||
.IP \(bu 2
|
||
\fBgetPlugin(name)\fR - Access another plugin's public API
|
||
.IP \(bu 2
|
||
\fBchat\fR - Chat info: \fBid\fR, \fBname\fR, \fBisGroup\fR
|
||
.IP \(bu 2
|
||
\fBlog.info(...), log.warn(...), log.error(...)\fR - Logging methods
|
||
.RE
|
||
.SH EXAMPLES
|
||
.SS Simple Command
|
||
.nf
|
||
import { CMD_PREFIX } from "../../config.js";
|
||
|
||
export default async function ({ msg, api }) {
|
||
if (!msg.is(CMD_PREFIX + "hi")) return;
|
||
await msg.reply("Hello! 👋");
|
||
}
|
||
.fi
|
||
.SS Command with Arguments
|
||
.nf
|
||
import { CMD_PREFIX } from "../../config.js";
|
||
|
||
export default async function ({ msg, api }) {
|
||
if (!msg.is(CMD_PREFIX + "calc")) return;
|
||
|
||
const [, a, op, b] = msg.args;
|
||
let result;
|
||
|
||
switch (op) {
|
||
case "+": result = Number(a) + Number(b); break;
|
||
case "-": result = Number(a) - Number(b); break;
|
||
default: return msg.reply("Invalid operator!");
|
||
}
|
||
|
||
await msg.reply(`Result: ${result}`);
|
||
}
|
||
.fi
|
||
.SS Processing Media
|
||
.nf
|
||
import { CMD_PREFIX } from "../../config.js";
|
||
|
||
export default async function ({ msg, api }) {
|
||
if (!msg.is(CMD_PREFIX + "echo")) return;
|
||
|
||
if (!msg.hasMedia) {
|
||
return msg.reply("Send media with the command!");
|
||
}
|
||
|
||
const media = await msg.downloadMedia();
|
||
await api.sendSticker(media.data);
|
||
}
|
||
.fi
|
||
.SS Exposing API to Other Plugins
|
||
.nf
|
||
// Public API for other plugins
|
||
export const api = {
|
||
formatDate: (d) => d.toLocaleDateString("en-US"),
|
||
wait: (ms) => new Promise(r => setTimeout(r, ms))
|
||
};
|
||
|
||
// Normal plugin logic
|
||
export default async function ({ msg }) {
|
||
if (msg.is("!ping")) await msg.reply("pong!");
|
||
}
|
||
.fi
|
||
.PP
|
||
Used by another plugin:
|
||
.nf
|
||
export default async function ({ msg, api }) {
|
||
const utils = api.getPlugin("utilities");
|
||
const date = utils.formatDate(new Date());
|
||
await msg.reply(`Today is ${date}`);
|
||
}
|
||
.fi
|
||
.SH TRANSLATIONS
|
||
Plugins can include their own translations:
|
||
.PP
|
||
.nf
|
||
import { createPluginI18n } from "../../utils/pluginI18n.js";
|
||
|
||
const { t } = createPluginI18n(import.meta.url);
|
||
|
||
export default async function ({ msg }) {
|
||
if (!msg.is(CMD_PREFIX + "hello")) return;
|
||
await msg.reply(t("greeting", { name: msg.senderName }));
|
||
}
|
||
.fi
|
||
.PP
|
||
Locale file (\fIlocale/en.json\fR):
|
||
.nf
|
||
{
|
||
"greeting": "Hello, {{name}}! 👋"
|
||
}
|
||
.fi
|
||
.PP
|
||
If the configured locale has no translation file, falls back to \fBen.json\fR.
|
||
Use \fB{{variable}}\fR syntax for interpolation.
|
||
.SH ENABLING A PLUGIN
|
||
Add the plugin folder name to
|
||
.I manybot.confR:
|
||
.PP
|
||
.nf
|
||
PLUGINS=[
|
||
many,
|
||
figurinha,
|
||
my-plugin
|
||
]
|
||
.fi
|
||
.PP
|
||
Restart ManyBot to load the plugin.
|
||
.SH ERROR HANDLING
|
||
If a plugin throws an error, the kernel automatically disables it.
|
||
Use try/catch for graceful error handling:
|
||
.PP
|
||
.nf
|
||
export default async function ({ msg, api }) {
|
||
try {
|
||
const result = await riskyOperation();
|
||
await msg.reply(result);
|
||
} catch (error) {
|
||
api.log.error("Plugin error:", error);
|
||
await msg.reply("Oops! Something went wrong.");
|
||
}
|
||
}
|
||
.fi
|
||
.SH CONFIGURATION ACCESS
|
||
Import settings from the main config:
|
||
.PP
|
||
.nf
|
||
import { CMD_PREFIX, CLIENT_ID, CHATS, PLUGINS } from "../../config.js";
|
||
|
||
// Custom config values also work
|
||
import { MY_API_KEY } from "../../config.js";
|
||
.fi
|
||
.PP
|
||
Add custom values to
|
||
.I manybot.confR:
|
||
.nf
|
||
MY_API_KEY=secret_key_here
|
||
.fi
|
||
.SH SEE ALSO
|
||
.BR manybot (1),
|
||
.BR manyplug (1),
|
||
.BR manybot.conf (5)
|
||
.SH AUTHOR
|
||
Written by synt-xerror.
|
||
.SH REPORTING BUGS
|
||
Report bugs at: https://github.com/synt-xerror/manybot/issues
|