ctx.config
const prefix = ctx.config.get("CMD_PREFIX");
const lang = ctx.config.get("LANGUAGE", "pt"); // com fallback
get(key, defaultValue?) — lê o manybot.toml do usuário. Padrão do 2º argumento: null.
ctx.i18n
export default async function (ctx) {
const { t } = ctx.i18n.createT(import.meta.url); // t() escopado pro seu plugin
if (ctx.msg.is("oi")) {
await ctx.send.text(t("bemVindo", { nome: ctx.msg.senderName }));
}
}
Estrutura esperada:
plugins/meu-plugin/
index.js
locale/
pt.json → { "bemVindo": "Bem-vindo, {{nome}}!" }
en.json
| Método | Assinatura | Descrição |
|---|---|---|
t |
(key, context?) |
Traduz chave dos locales do core. |
createT |
(import.meta.url) |
Retorna { t, lang } — t() escopado aos locales do plugin, lang é o idioma ativo no momento. |
reload |
() |
Recarrega traduções do disco. |
getCurrentLang |
() |
"pt", "en", etc. |
Sem tradução pro idioma configurado → cai pro
en.jsonautomaticamente.
ctx.té um atalho direto pro mesmotdo core (equivalente actx.i18n.t) — útil se seu plugin só precisa das traduções do core e não tem locale próprio.
LANGUAGEnão recarrega sozinho: diferente de outras chaves domanybot.toml, o idioma é carregado uma única vez por processo. MudarLANGUAGEno arquivo não muda o quet()/ctx.ttraduzem em runtime — é preciso chamarctx.i18n.reload()(de algum plugin) ou reiniciar o bot.
ctx.utils
ctx.utils.emptyFolder(DOWNLOADS_DIR); // apaga conteúdo sem remover a pasta
Só apaga arquivos diretamente dentro da pasta — não é recursivo, subpastas (e o conteúdo delas) ficam intactas. Lança erro se a pasta não existir; garanta que ela existe antes (ou envolva em
try/catch).
ctx.download
Fila serializada pra downloads pesados — não baixe direto no handler, isso trava o event loop e atrasa outras mensagens.
export default async function (ctx) {
if (!ctx.msg.is("video")) return;
const url = ctx.msg.args[0];
if (!url) return void await ctx.msg.reply.text("Informe uma URL.");
await ctx.msg.reply.text("Baixando, aguarde...");
ctx.download.enqueue(
async () => {
const filePath = await baixarVideo(url);
await ctx.send.video(filePath);
},
async (err) => {
ctx.log.error(`Download falhou: ${err.message}`);
await ctx.msg.reply.text("Falha no download.");
}
);
}
Só um job roda por vez. errorFn (segundo argumento) não é opcional — sempre passe os dois.
ctx.scheduler
Agenda tarefas recorrentes via cron, escopadas ao seu plugin (setup + runtime, mas normalmente
usado no setup() — chamar dentro do handler de mensagem registraria uma tarefa nova a cada
mensagem).
export async function setup(ctx) {
ctx.scheduler.schedule("0 9 * * 1", async () => {
await ctx.send.to("5511999999999@c.us").text("Bom dia! Relatório semanal:");
});
}
| Método | Assinatura | Descrição |
|---|---|---|
schedule(expression, fn) |
(string, () => Promise<void>) => { stop(): void } |
Registra uma tarefa cron; retorna um handle pra cancelar. |
expression segue a sintaxe padrão de cron (minuto, hora, dia do mês, mês, dia da semana). fn
roda sem receber ctx — feche sobre as variáveis que precisar, como no exemplo acima.
const tarefa = ctx.scheduler.schedule("*/5 * * * *", async () => { /* ... */ });
tarefa.stop(); // cancela — útil se a condição de agendar for dinâmica
Chamar
schedule()de novo com a mesma expressão cron no mesmo plugin substitui a tarefa anterior, em vez de acumular duas rodando em paralelo — seguro chamar de novo a cada hot-reload do plugin. Expressão cron inválida não lança erro: loga um aviso e retorna um handle que não faz nada.
ctx.storage
Diretório de dados persistentes do plugin (~/.manybot/data/<key>/), criado automaticamente.
import { readFileSync, writeFileSync, existsSync } from "fs";
const dbPath = ctx.storage.resolve("dados.json"); // cria subpastas se precisar
const dados = existsSync(dbPath) ? JSON.parse(readFileSync(dbPath, "utf-8")) : {};
dados[ctx.msg.sender] = Date.now();
writeFileSync(dbPath, JSON.stringify(dados, null, 2));
| Prop/Método | Descrição |
|---|---|
dir |
Caminho absoluto do diretório de dados. |
resolve(relativePath) |
Resolve caminho dentro de dir, criando subpastas. |
Sobrevive a reinstalações.
manyplug removepergunta antes de apagar (-Ypula tudo).resolve()rejeita tentativas de escapar do diretório (../, caminhos absolutos) — sempre retorna um caminho dentro dedir.
ctx.settings
Armazenamento de configurações por chat (ou global), persistido em disco — pense em "preferências
que o usuário ajusta pelo próprio WhatsApp", diferente de ctx.storage (dados livres do plugin).
// no chat atual
ctx.settings.set("boasVindas", true);
const ativo = ctx.settings.get("boasVindas", false); // com fallback
ctx.settings.getAll(); // todas as chaves deste chat
ctx.settings.delete("boasVindas");
ctx.settings.deleteAll();
// configuração global do bot, não ligada a nenhum chat
ctx.settings.global.set("modoManutencao", true);
ctx.settings.global.get("modoManutencao", false);
// configuração de outro chat específico
ctx.settings.forChat(outroChatId).set("idioma", "en");
Comunidades
Grupos que fazem parte da mesma comunidade do WhatsApp podem compartilhar configurações:
ctx.settings.link(communityId); // associa o chat atual a uma comunidade
ctx.settings.unlink(); // remove a associação do chat atual
ctx.settings.getCommunityId(); // string | null
ctx.settings.getCommunityChats(); // string[] — chats associados à mesma comunidade
| Método | Descrição |
|---|---|
get(key, default?) / getAll() |
Lê uma chave (ou todas) do chat atual. Sem 2º argumento, retorna undefined (não null — diferente de ctx.config.get()). |
set(key, value) / delete(key) / deleteAll() |
Escreve/remove no chat atual. value precisa ser serializável em JSON. |
global |
Mesmos métodos acima (get/set/delete/getAll/deleteAll), mas escopados ao bot inteiro, sem chat associado. |
forChat(chatId) |
Mesmos métodos acima, escopados a outro chat específico. |
link(communityId) / unlink() |
Associa/desassocia o chat atual a uma comunidade. |
getCommunityId() / getCommunityChats() |
Consulta a associação atual. |
No
setup(), sóctx.settings.globalestá disponível — sem chat atual, os métodos que operam no "chat atual" (incluindoforChat/link/unlink) não fazem sentido ali.
ctx.plugins
Comunicação entre plugins via API pública.
// plugins/meu-banco/index.js
export const api = {
async buscarUsuario(id) { /* ... */ },
};
// consumindo
const banco = ctx.plugins.require("meu-banco"); // lança erro se não existir
const stats = ctx.plugins.get("many-stats"); // null se não existir
if (ctx.plugins.exists("many-ai")) { /* feature flag */ }
| Método | Descrição |
|---|---|
get(name) |
API pública ou null. Dependência opcional. |
require(name) |
API pública ou lança erro. Dependência obrigatória. |
exists(name) |
true se o plugin está ativo. |
ctx.log
ctx.log.info("Iniciando processamento...");
ctx.log.warn("API key não configurada");
ctx.log.error(`Falha: ${err.message}`);
ctx.log.success("Sticker enviado!");
Prefira ctx.log a console.log — mantém formato consistente com o resto do bot.
ctx.botId
ctx.log.info(`Bot rodando como: ${ctx.botId}`);
string | null — pode ser null se o client ainda não tiver terminado de inicializar
(emite warning automático no log nesse caso).
ctx.wa (runtime only)
Escape hatch pro socket cru do Baileys (a biblioteca por trás da conexão com o WhatsApp) — pra quando algo que você precisa não está coberto pelo resto da API.
ctx.wa.sock; // instância do socket Baileys (@whiskeysockets/baileys) — API completa da lib
ctx.wa.store; // store interno do ManyBot (cache de contatos/chats)
ctx.wa.msg; // objeto de mensagem bruto do Baileys que disparou esse handler
await ctx.wa.downloadMedia(); // helper de download que não passa pelas checagens de ctx.msg
// exemplo: método do Baileys sem equivalente em ctx.admin/ctx.chat
export default async function (ctx) {
if (!ctx.msg.is("perfil-de-negocio")) return;
const perfil = await ctx.wa.sock.getBusinessProfile(ctx.msg.sender);
await ctx.msg.reply.text(JSON.stringify(perfil ?? "não é conta business"));
}
Use com cautela. Diferente do resto do
ctx, isso não passa pelas normalizações do ManyBot (formato de JID,guardOptions, tratamento de erro/reload) — bugs aqui não são pega pelo mesmo retry/disable de 3 tentativas de umdefault()normal quebrando, e IDs que saem direto dosock/msgvêm no formato nativo do Baileys (@s.whatsapp.net, não@c.us). Prefira sempre a API normal (ctx.msg,ctx.chat,ctx.admin, etc.) quando ela cobrir o que você precisa.
ctx.tg e ctx.dc também existem na interface (reservados pra Telegram e Discord), mas hoje são
sempre null — o ManyBot só tem driver de WhatsApp implementado.