Como fazer um plugin
Aqui você vai aprender tudo que precisa para começar a desenvolver plugins para o ManyBot.
Caso ainda não leu sobre os plugins, é recomendado ler antes de continuar.
Índice
Exemplo mínimo
Antes de entrar nos detalhes, veja como é um plugin funcional do começo ao fim:
// plugins/meu-plugin/index.js
export default async function (ctx) {
const { msg } = ctx;
// ignora tudo que não for o comando
if (!msg.is("oi")) return;
await msg.reply(`Olá, ${ctx.msg.senderName}!`);
}
É só isso. Um arquivo exportando uma função default que recebe ctx — o objeto com toda a
API do bot. Para entender o que mais está disponível no ctx, veja a
Referência da API.
Estrutura padrão de um plugin
meu-plugin/
├── index.js
├── manyplug.json
├── package.json
├── README.md
├── .gitignore
└── locale/
├── en.json
├── es.json
└── pt.json
O comando
manyplug initmonta essa estrutura automaticamente. Veja manyplug init.
index.js
Arquivo principal do plugin — é aqui onde começa a execução.
Você exporta a função default, que o bot chama em cada mensagem recebida. Opcionalmente,
pode exportar também uma função setup, que roda uma única vez quando o bot conecta.
// setup: executado uma vez quando o bot conecta (opcional)
export async function setup(ctx) {
ctx.log.info("meu-plugin inicializado!");
}
// default: executado em cada mensagem recebida (obrigatório)
export default async function (ctx) {
if (!ctx.msg.is("hello")) return;
await ctx.msg.reply("Hello!");
}
Alguns pontos importantes:
- Todos os plugins recebem todas as mensagens. O bot não filtra por comando antes de
chamar seu plugin — você mesmo decide se age ou ignora, geralmente com um
if (!msg.is(...)) returnno topo. - Plugins rodam em sequência. Cada mensagem passa por todos os plugins ativos, um por um. Se o seu plugin lançar um erro não tratado, o kernel o desativa automaticamente para não quebrar os outros.
- O bot também recebe as próprias mensagens. Se o seu plugin responde a qualquer coisa
(não só comandos), filtre
ctx.msg.fromMepara não entrar em loop:
export default async function (ctx) {
if (ctx.msg.fromMe) return;
// lógica do plugin...
}
manyplug.json
Arquivo de metadados do plugin. Obrigatório para publicar no registro oficial, recomendado mesmo em plugins privados.
{
"name": "meu-plugin",
"key": "eu/meu-plugin",
"version": "1.0.0",
"description": "Meu plugin legal pro ManyBot.",
"category": "utility",
"service": false,
"author": {
"name": "eu",
"email": "meu@email.com",
"website": "www.meusite.com"
},
"license": "MIT",
"repo": "https://github.com/eu/meu-plugin.many",
"main": "index.js",
"dependencies": {
"dependencia-npm": ">=10"
},
"externalDependencies": {
"ffmpeg": {
"command": "ffmpeg",
"optional": false
}
}
}
Campos
name (obrigatório)
Nome do plugin. Deve conter apenas letras minúsculas, números e hífens. Deve ser único por autor.
version (obrigatório)
Versão atual do plugin. Use o formato que preferir — SemVer, CalVer, tanto faz.
category (obrigatório)
Categoria do plugin. Valores possíveis:
| Valor | Quando usar |
|---|---|
utility |
Ferramentas de uso geral |
media |
Download, conversão ou envio de mídia |
games |
Jogos e interações por turnos |
integration |
Integrações com APIs e serviços externos |
service |
Plugins que rodam em background sem comandos diretos |
admin |
Ferramentas de administração de grupos ou do próprio bot |
fun |
Entretenimento sem categoria específica |
key
Chave global no formato autor/nome. Usada para referenciar o plugin no mpindex e em
dependências de outros plugins. Sem ela, o ManyPlug instala em manydev/<nome> e o validate vai reclamar.
description
Descrição curta do que o plugin faz. Exibida na listagem do registro.
author
Informações do autor. Somente name é obrigatório. Aceita também uma string simples para compatibilidade.
license
Licença de código aberto do plugin — define como o código pode ser distribuído e modificado por outros.
repo
Repositório Git do seu plugin. Deve ser apenas um e pode ser de qualquer forja e serviço (ex. GitHub, GitLab). Não é obrigatório, serve apenas de identificação quando for publicado.
service
true se o plugin roda em background como um serviço (sem comandos diretos, ex: um monitor
que envia alertas periódicos). false se é acionado por comandos do usuário. Padrão: false.
main
Nome do arquivo de entrada. Normalmente "index.js". Se omitido, o ManyBot procura por index.js.
dependencies
Pacotes npm necessários. O ManyPlug os instala automaticamente ao instalar o plugin:
{
"dependencies": {
"nome-do-pacote": ">=1.0.0"
}
}
externalDependencies
Programas externos que precisam estar instalados no sistema (ex: yt-dlp, ffmpeg):
{
"externalDependencies": {
"yt-dlp": {
"command": "yt-dlp",
"optional": false
},
"ffmpeg": {
"command": "ffmpeg",
"optional": true
}
}
}
command— comando usado para verificar se o programa está disponível noPATHoptional— sefalse, o ManyPlug avisa sobre a dependência ausente na instalação; setrue, é tratado como aviso menor
package.json
Arquivo mínimo. O único campo obrigatório é "type": "module" — necessário para que o
Node.js trate seus arquivos como ESM (o formato que o ManyBot usa):
{
"type": "module"
}
Se seu plugin tiver dependências npm, elas também aparecem aqui — mas o ManyPlug gerencia isso
para você automaticamente a partir do manyplug.json.
README.md
Arquivo Markdown explicando seu plugin. Não é obrigatório, mas é altamente recomendado — especialmente se você pretende publicar. É o que outros usuários vão ler para entender o que seu plugin faz e como configurar.
Prefira escrever em inglês para alcançar mais pessoas, mas português também é aceito.
Exemplo real (plugin manymedia):
# ManyMedia
Download videos and audio from YouTube, Reddit, Instagram, and other yt-dlp supported
sites — either sending the file directly to chat or uploading to a storage server and
sharing the link.
## Features
- **Multi-site support**: YouTube, Reddit, Instagram, SoundCloud, TikTok, and any
other yt-dlp compatible site
- **Audio extraction**: Downloads and extracts MP3 at best quality
- **Flexible delivery**: Send file directly to chat, or upload to a storage server
and reply with the link
- **Upload retry**: Failed uploads are retried up to 4 times with a 3-second delay
- **Queued processing**: Downloads run in a queue to prevent resource contention
- **Automatic cleanup**: Temporary files removed after delivery
## Requirements
- `yt-dlp` installed and available in `PATH`
- `ffmpeg` for converting filetypes (e.g. mp4 to mp3) — optional for download only
- `cookies.txt` in the project root (required for YouTube, Reddit, and sites that
need authentication)
## Usage
/video https://youtube.com/watch?v=...
/audio https://youtube.com/watch?v=...
## Configuration
Add to `manybot.conf`:
| Key | Default | Description |
|--------------------|---------|----------------------------------------------------------------|
| `UPL_MEDIA_TO_SRV` | `no` | Set to `yes` to upload to a server and reply with a link |
| `MEDIA_SRV_API_KEY`| — | API key for the storage server (required when `UPL_MEDIA_TO_SRV=yes`) |
locale
Diretório com as traduções do seu plugin. O ManyBot carrega automaticamente o arquivo
correspondente ao idioma configurado no .conf.
Estrutura:
locale/
├── en.json
├── pt.json
└── es.json
locale/en.json:
{
"hello": "Hello, {{name}}!",
"error": {
"generic": "Something went wrong. Try again."
}
}
locale/pt.json:
{
"hello": "Olá, {{name}}!",
"error": {
"generic": "Algo deu errado. Tente novamente."
}
}
E no código:
export default async function (ctx) {
const prefix = ctx.config.get("CMD_PREFIX");
const { t } = ctx.i18n.createT(import.meta.url);
if (!ctx.msg.is(prefix + "hello")) return;
// {{name}} é substituído pelo valor passado no segundo argumento
await ctx.msg.reply(t("hello", { name: ctx.msg.senderName }));
}
Locales não são obrigatórios, mas são incentivados. Sem locale, o plugin simplesmente não oferece suporte a múltiplos idiomas.
Para mais detalhes sobre a API de i18n, veja ctx.i18n.
Publicando seu plugin
Aqui é somente se quiser que outras pessoas usem seu plugin a partir do índice oficial. Totalmente dispensável.
1. Valide e teste localmente
Antes de qualquer coisa, rode o validador e instale localmente:
manyplug validate .
manyplug install --local .
O validate checa campos obrigatórios, tipos, entry point, locale e dependências externas.
Corrija tudo que ele apontar antes de continuar. Depois confirme que o plugin funciona
rodando o bot normalmente.
2. Crie um repositório Git
Suba o código para o GitHub, Codeberg, GitLab ou outra forja de sua preferência.
A convenção (não obrigatória, mas incentivada) é adicionar o sufixo .many ao nome do
repositório — ex: https://codeberg.org/usuario/meu-plugin.many. Serve só para identificação.
3. Envie uma solicitação
Mande um email para manybot@pm.me com o seguinte formato:
Assunto: [PLUGIN-REQUEST] Nome do seu plugin
# O que ele faz?
Descrição resumida do plugin.
# Repositório(s)
GitHub: https://github.com/.../...
Codeberg: https://codeberg.org/.../...
O email pode ser em inglês ou português. Certifique-se de ter um README explicativo no repositório — é o que vai ser lido durante a revisão.
Caso não queira enviar via email, é possível enviar na nossa comunidade do Discord ou WhatsApp.
4. Revisão e publicação
Após a revisão, você receberá um email com:
- Se o plugin foi aceito ou não, e o motivo
- Feedback sobre o código ou documentação
- Qualquer outra informação relevante
O atendimento é 100% humano e anônimo. Sem medo de perguntar.
Se aceito, o plugin entra no índice oficial (mpindex) e fica disponível para instalação
via manyplug install autor/nome:
"eu/meu-plugin": {
"repos": {
"codeberg": {
"master": "https://codeberg.org/eu/meu-plugin.many",
"dev": "https://codeberg.org/eu/meu-plugin.many"
},
"github": {
"master": "https://github.com/eu/meu-plugin.many",
"dev": "https://github.com/eu/meu-plugin.many"
}
},
"manifest": "https://raw.githubusercontent.com/eu/meu-plugin.many/refs/heads/master/manyplug.json",
"readme": "https://raw.githubusercontent.com/eu/meu-plugin.many/refs/heads/master/README.md"
}