diff --git a/README.md b/README.md index c1d2668..0db2bc6 100644 --- a/README.md +++ b/README.md @@ -1,308 +1,188 @@ +
+ ![ManyBot Logo](logo.png) -ManyBot é um bot para WhatsApp que roda 100% localmente, sem depender da API oficial do WhatsApp. Ele utiliza a biblioteca `whatsapp-web.js`, que automatiza o WhatsApp Web sem depender de gráficos (headless). +

+ Bot para WhatsApp 100% local, sem API oficial +

-Algumas funcionalidades desse bot incluem: -- Suporte a múltiplos chats em uma única sessão -- Sistema de plugins — adicione, remova ou crie funcionalidades sem mexer no núcleo do bot +

+ Recursos . + Instalação . + Uso . + Plugins . + Documentação +

-# Exemplos +

+ 🇧🇷 Português · 🇺🇸 English +

-
+

+ Node.js 18+ + npm 9+ + License: GPL v3 + Platform +

+ +

+ whatsapp-web.js + Headless +

+ +
+ +> **Versão Oficial Online** +> Quer usar o ManyBot sem instalar? Adicione o bot oficial: +> +> **+55 (16) 99459-1903** +> +> Online 24h (quando possível) - Disponibilidade não garantida +> +> Ao adicionar, você concorda com os [Termos de Uso](TERMOS_pt-br.md) ![Exemplo do gerador de figurinhas](examples/figurinha.gif) -
+
--- -# Requisitos -- Node.js -- NPM -- Sistema Linux ou Windows +## Recursos -obs: Sistemas Android e iOS ainda não são 100% compatíveis. O suporte para Termux está em fases de testes e sem garantia de funcionamento correto. +- **100% Local** - Sem depender da API oficial do WhatsApp +- **Multi-chat** - Suporte a múltiplos chats em uma única sessão +- **Sistema de Plugins** - Adicione, remova ou crie funcionalidades sem mexer no núcleo +- **Headless** - Funciona em segundo plano sem interface gráfica +- **Fácil Configuração** - Arquivo de config simples e intuitivo -# Instalação (Linux) +--- + +## Instalação Rápida + +### Opção 1: Usar o Bot Oficial (Sem instalar) + +Adicione o número **+55 (16) 99459-1903** aos seus contatos e envie `!many` para ver os comandos disponíveis. + +**Status:** 🟢 Online (24h quando possível, mas sem garantia) + +> ⚠️ **Importante:** Ao usar o bot oficial, você concorda com os [Termos de Uso](TERMOS_pt-br.md). Leia antes de adicionar! + +--- + +### Opção 2: Instalar sua Própria Versão -1. Clone o repositório e entre: ```bash +# 1. Clone o repositório git clone https://github.com/synt-xerror/manybot cd manybot -``` -2. Crie e abra o arquivo de configuração (use o editor de sua preferência): -```bash -touch manybot.conf +# 2. Crie o arquivo de configuração +cp manybot.conf.example manybot.conf + +# 3. Configure conforme sua necessidade (veja a documentação) nano manybot.conf -``` -3. Nele você pode configurar algumas coisas do ManyBot. Esse é o arquivo base para que possa modificar: -```bash -# Comentários com '#' - -CLIENT_ID=bot_permanente -CMD_PREFIX=! -CHATS=[ - 123456789@c.us, - 123456789@g.us -] -PLUGINS=[ - video, - audio, - figurinha, - adivinhacao -] -``` -- **CLIENT_ID:** ID do cliente, serve para identificar sua sessão. - - Valor padrão: `bot_permanente` -- **CMD_PREFIX:** Prefixo do comando, o caractere que você usa para executar um comando (!many, !figurinha). - - Valor padrão: `!` -- **CHATS:** ID dos chats no qual você quer que o bot assista. Use o utilitário: `src/utils/get_id.js` para descobrir os IDs. Deixe vazio caso queira que funcione com qualquer chat. - - Valor padrão: (nenhum) -- **PLUGINS:** Lista de plugins ativos. Cada nome corresponde a uma pasta dentro de `src/plugins/`. Remova ou comente uma linha para desativar o plugin sem apagá-lo. - - Valor padrão: (nenhum) - -obs: o utilitário `src/utils/get_id.js` usa um CLIENT_ID separado para que não entre em conflito com a sessão principal do ManyBot. Você terá que escanear o QR Code novamente para executá-lo. - -4. Execute o script de instalação: -```bash +# 4. Execute o script de instalação bash ./setup -``` -5. Rode o bot pela primeira vez (você deve rodar da raiz, não dentro de `src`): -```bash -node ./src/main.js -``` -Ele vai pedir para que escaneie o QR Code com seu celular. - -No WhatsApp: -Menu (três pontos) > Dispositivos conectados > Conectar um dispositivo - -# Instalação (Windows) - -O uso desse bot foi pensado para rodar em um terminal Linux. No entanto, você pode usar o Git Bash, que simula um terminal Linux com Bash real: - -1. Para baixar o Git Bash: https://git-scm.com/install/windows -Selecione a versão que deseja (portátil ou instalador) - -2. Para baixar o Node.js: https://nodejs.org/pt-br/download -Role a tela e selecione "Instalador Windows (.msi)" -Ou se preferir, use um gerenciador de pacotes como mostra no conteúdo inicial - -Depois de instalar ambos, abra o Git Bash e execute exatamente os mesmos comandos mostrados na seção Linux. - -# Uso - -Feito a instalação, você pode executar o bot apenas rodando: -```bash +# 5. Rode o bot node ./src/main.js ``` -## Atualizações +📱 **Escaneie o QR Code** no WhatsApp: Menu → Dispositivos conectados → Conectar um dispositivo + +> **⚡ Pronto!** Veja a [documentação completa](docs/INSTALACAO.md) para mais detalhes. + +--- + +## 💻 Uso -É recomendável sempre ter a versão mais recente do ManyBot. Para isso, temos um utilitário logo na raíz. Para executar: ```bash +# Iniciar o bot +node ./src/main.js + +# Atualizar para a versão mais recente bash ./update + +# Descobrir IDs de chats +node src/utils/get_id.js ``` -## Criando um serviço (opcional) +--- -Se estiver rodando numa VPS ou apenas quer mais controle, é recomendável criar um serviço systemd. Siga os passos abaixo para saber como criar, habilitar e gerenciar um. +## 🔌 Plugins -1. Configurando o diretório +O ManyBot é construído em torno de um sistema de plugins. O kernel apenas conecta ao WhatsApp e distribui as mensagens — os plugins decidem o que fazer. -Primeiro passo é garantir que o diretório do ManyBot esteja no local adequado, é recomendável guardar em `/root/manybot` (os passos a seguir supõem que esteja essa localização) +### Plugins Incluídos -2. Criando o serviço +| Plugin | Comando | Descrição | +|--------|---------|-----------| +| **figurinha** | `!figurinha` | Converte imagens, GIFs e vídeos em figurinhas | +| **video** | `!video ` | Baixa vídeos da internet | +| **audio** | `!audio ` | Baixa áudio de vídeos como mensagem de voz | +| **adivinhacao** | `!adivinhacao comecar` | Jogo de adivinhação (1-100) | +| **forca** | `!forca comecar` | Jogo da forca clássico | +| **many** | `!many` | Lista todos os comandos disponíveis | +| **obrigado** | `!obrigado` | Responde agradecimentos | -Abra o arquivo: -```bash -/etc/systemd/system/manybot.service -``` +### Criar um Plugin -E cole o seguinte conteúdo: -```conf -[Unit] -Description=ManyBot -After=network.target - -[Service] -ExecStart=/usr/bin/env node /root/manybot/src/main.js -WorkingDirectory=/root/manybot -Restart=always -Environment=NODE_ENV=production - -[Install] -WantedBy=multi-user.target -``` - -3. Iniciando e habilitando o serviço: - -Primeiro reinicie o daemon do systemd: -```bash -systemctl daemon-reload -``` - -Inicie o serviço: -```bash -systemctl start manybot -``` - -Habilite para que ele seja iniciado junto com o seu sistema (opcional): -```bash -systemctl enable manybot -``` - -4. Gerenciando o serviço: - -Ver logs: -```bash -journalctl -u manybot -``` - -Em tempo real: -```bash -journalctl -u manybot -f -``` - -Parar o serviço: -```bash -systemctl stop manybot -``` - -Reiniciar o serviço: -```bash -systemctl restart manybot -``` - -Saiba mais sobre como gerenciar serviços em: https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units-pt -Sobre o journalctl: https://www.digitalocean.com/community/tutorials/how-to-use-journalctl-to-view-and-manipulate-systemd-logs-pt - -# Plugins - -O ManyBot é construído em torno de um sistema de plugins. O núcleo do bot (kernel) apenas conecta ao WhatsApp e distribui as mensagens — quem decide o que fazer com elas são os plugins. - -Isso significa que você pode adicionar, remover ou criar funcionalidades sem tocar no código principal do bot. - -## Plugins incluídos - -O ManyBot vem com alguns plugins prontos para uso, como: - -- **video** — baixa um vídeo da internet e envia no chat (`!video `) -- **audio** — baixa o áudio de um vídeo e envia como mensagem de voz (`!audio `) -- **figurinha** — converte imagens, GIFs e vídeos em figurinhas (`!figurinha`) -- **adivinhacao** — jogo de adivinhação de um número entre 1 e 100 (`!adivinhação começar`) -- **forca** — clássico jogo da forca (`!forca começar`) -- **many** — exibe a lista de comandos disponíveis (`!many`) -- **obrigado** — responde agradecimentos (`!obrigado`, `!valeu`, `!brigado`) - -Para ativar ou desativar qualquer um deles, basta editar a lista `PLUGINS` no `manybot.conf`. - -## Criando um plugin - -Cada plugin é uma pasta dentro de `plugins/` com um arquivo `index.js`. O bot carrega automaticamente todos os plugins listados no `manybot.conf`. - -A estrutura mínima de um plugin: - -``` -plugins/ -└── meu-plugin/ - └── index.js -``` - -O `index.js` deve exportar uma função `default` que o kernel chama a cada mensagem recebida. A função recebe `{ msg, api }` e decide por conta própria se age ou ignora: - -```js +```javascript // plugins/meu-plugin/index.js - -import { CMD_PREFIX } from "../../config.js" +import { CMD_PREFIX } from "../../config.js"; export default async function ({ msg, api }) { if (!msg.is(CMD_PREFIX + "oi")) return; - await msg.reply("Olá! 👋"); } ``` -### O objeto `msg` +Veja mais na [documentação de plugins](docs/PLUGINS.md). -Contém as informações da mensagem recebida: +--- -| Propriedade | Descrição | -|---|---| -| `msg.body` | Texto da mensagem | -| `msg.args` | Tokens da mensagem — `["!video", "https://..."]` | -| `msg.type` | Tipo — `"chat"`, `"image"`, `"video"`, `"audio"`, `"sticker"` | -| `msg.sender` | ID de quem enviou | -| `msg.senderName` | Nome de quem enviou | -| `msg.fromMe` | `true` se foi o próprio bot que enviou | -| `msg.hasMedia` | `true` se a mensagem tem mídia | -| `msg.hasReply` | `true` se é uma resposta a outra mensagem | -| `msg.isGif` | `true` se a mídia é um GIF | -| `msg.is(cmd)` | Retorna `true` se a mensagem começa com `cmd` | -| `msg.reply(text)` | Responde à mensagem com quote | -| `msg.downloadMedia()` | Baixa a mídia — retorna `{ mimetype, data }` | -| `msg.getReply()` | Retorna a mensagem citada, ou `null` | +## 📚 Documentação -### O objeto `api` +- [📥 Instalação Completa](docs/INSTALACAO.md) — Linux, Windows, Termux +- [⚙️ Configuração](docs/CONFIGURACAO.md) — Todas as opções do `manybot.conf` +- [🔌 Criando Plugins](docs/PLUGINS.md) — Guia completo de desenvolvimento +- [🖥️ Serviço Systemd](docs/SYSTEMD.md) — Rodar como serviço Linux +- [🛠️ API de Plugins](docs/API.md) — Referência de objetos `msg` e `api` -Contém tudo que o plugin pode fazer — enviar mensagens, acessar outros plugins, registrar logs: +## 🌍 Internacionalização -| Método | Descrição | -|---|---| -| `api.send(text)` | Envia texto no chat | -| `api.sendVideo(filePath)` | Envia um vídeo a partir de um arquivo local | -| `api.sendAudio(filePath)` | Envia um áudio a partir de um arquivo local | -| `api.sendImage(filePath, caption?)` | Envia uma imagem a partir de um arquivo local | -| `api.sendSticker(bufferOuPath)` | Envia uma figurinha — aceita `Buffer` ou caminho | -| `api.getPlugin(name)` | Retorna a API pública de outro plugin | -| `api.chat.id` | ID do chat atual | -| `api.chat.name` | Nome do chat atual | -| `api.chat.isGroup` | `true` se é um grupo | -| `api.log.info(...)` | Loga uma mensagem informativa | -| `api.log.warn(...)` | Loga um aviso | -| `api.log.error(...)` | Loga um erro | +O ManyBot suporta múltiplos idiomas. Configure no `manybot.conf`: -### Lendo o manybot.conf no plugin - -Se o seu plugin precisar de configurações próprias, você pode adicioná-las diretamente no `manybot.conf` e importá-las no código: - -```js -import { MEU_PREFIXO } from "../../src/config.js"; - -const prefixo = MEU_PREFIXO ?? "padrão"; +```bash +LANGUAGE=pt # Português +LANGUAGE=en # English +LANGUAGE=es # Español ``` -### Expondo uma API para outros plugins +- **Padrão:** Inglês (`en`) +- **Fallback:** Se o idioma selecionado não existir, o bot usa inglês -Um plugin pode expor funções para que outros plugins as utilizem. Para isso, basta exportar um objeto `api`: +--- -```js -// plugins/utilidades/index.js +## 📋 Requisitos -export const api = { - formatarData: (date) => date.toLocaleDateString("pt-BR"), -}; +- **Node.js** 18+ +- **NPM** 9+ +- **Linux** ou **Windows** (via Git Bash) -export default async function ({ msg }) { - // lógica normal do plugin -} -``` +> ⚠️ Android/iOS e Termux têm suporte experimental sem garantias. -Outro plugin pode chamar: +--- -```js -const utils = api.getPlugin("utilidades"); -utils.formatarData(new Date()); -``` +## 📝 Licença -### Erros no plugin +Distribuído sob a licença **GPLv3**. Veja [LICENSE](LICENSE) para mais detalhes. -Se um plugin lançar um erro, o kernel o desativa automaticamente e loga o problema — o restante dos plugins continua funcionando normalmente. Isso garante que um plugin com bug não derruba o bot inteiro. +--- -# Considerações +
-ManyBot é distribuído sob a licença GPLv3. Você pode usar, modificar e redistribuir o software conforme os termos da licença. +**[⬆ Voltar ao topo](#)** -Saiba mais sobre as permissões lendo o arquivo [LICENSE](LICENSE) ou em: https://www.gnu.org/licenses/quick-guide-gplv3.pt-br.html \ No newline at end of file +
diff --git a/README_EN.md b/README_EN.md new file mode 100644 index 0000000..e3b0677 --- /dev/null +++ b/README_EN.md @@ -0,0 +1,188 @@ +
+ +![ManyBot Logo](logo.png) + +

+ 100% Local WhatsApp Bot, no official API +

+ +

+ Features • + Install • + Usage • + Plugins • + Documentation +

+ +

+ 🇧🇷 Português · 🇺🇸 English +

+ +

+ Node.js 18+ + npm 9+ + License: GPL v3 + Platform +

+ +

+ whatsapp-web.js + Headless +

+ +
+ +> 🟢 **Official Instance Online** +> Want to use ManyBot without installing? Add the official bot: +> +> **+55 (16) 99459-1903** +> +> Online 24h (when possible) · Availability not guaranteed +> +> By adding, you agree to the [Terms of Use](TERMS_en-us.md) + +![Sticker generator example](examples/figurinha.gif) + +
+ +--- + +## ✨ Features + +- **100% Local** — No dependency on the official WhatsApp API +- **Multi-chat** — Support for multiple chats in a single session +- **Plugin System** — Add, remove, or create features without touching the core +- **Headless** — Runs in the background without a GUI +- **Easy Configuration** — Simple and intuitive config file + +--- + +## 🚀 Quick Install + +### Option 1: Use the Official Bot (No install) + +Add the number **+55 (16) 99459-1903** to your contacts and send `!many` to see available commands. + +**Status:** 🟢 Online (24h when possible, but no guarantee) + +> ⚠️ **Important:** By using the official bot, you agree to the [Terms of Use](TERMS_en-us.md). Read before adding! + +--- + +### Option 2: Install Your Own Version + +```bash +# 1. Clone the repository +git clone https://github.com/synt-xerror/manybot +cd manybot + +# 2. Create the config file +cp manybot.conf.example manybot.conf + +# 3. Configure as needed (see documentation) +nano manybot.conf + +# 4. Run the install script +bash ./setup + +# 5. Run the bot +node ./src/main.js +``` + +📱 **Scan the QR Code** on WhatsApp: Menu → Linked Devices → Link a Device + +> **⚡ Done!** See the [full documentation](docs/INSTALLATION.md) for more details. + +--- + +## 💻 Usage + +```bash +# Start the bot +node ./src/main.js + +# Update to the latest version +bash ./update + +# Discover chat IDs +node src/utils/get_id.js +``` + +--- + +## 🔌 Plugins + +ManyBot is built around a plugin system. The kernel only connects to WhatsApp and distributes messages — plugins decide what to do. + +### Included Plugins + +| Plugin | Command | Description | +|--------|---------|-------------| +| **sticker** | `!sticker` | Converts images, GIFs, and videos into stickers | +| **video** | `!video ` | Downloads videos from the internet | +| **audio** | `!audio ` | Downloads audio from videos as voice messages | +| **guess** | `!guess start` | Guessing game (1-100) | +| **hangman** | `!hangman start` | Classic hangman game | +| **many** | `!many` | Lists all available commands | +| **thanks** | `!thanks` | Responds to thanks | + +### Create a Plugin + +```javascript +// plugins/my-plugin/index.js +import { CMD_PREFIX } from "../../config.js"; + +export default async function ({ msg, api }) { + if (!msg.is(CMD_PREFIX + "hello")) return; + await msg.reply("Hello! 👋"); +} +``` + +See more in the [plugin documentation](docs/PLUGINS_EN.md). + +--- + +## 📚 Documentation + +- [📥 Full Installation](docs/INSTALLATION.md) — Linux, Windows, Termux +- [⚙️ Configuration](docs/CONFIGURATION.md) — All `manybot.conf` options +- [🔌 Creating Plugins](docs/PLUGINS.md) — Complete development guide +- [🖥️ Systemd Service](docs/SYSTEMD.md) — Run as a Linux service +- [🛠️ Plugin API](docs/API.md) — Reference for `msg` and `api` objects + +## 🌍 Internationalization + +ManyBot supports multiple languages. Configure in `manybot.conf`: + +```bash +LANGUAGE=pt # Português +LANGUAGE=en # English +LANGUAGE=es # Español +``` + +- **Default:** English (`en`) +- **Fallback:** If selected language doesn't exist, bot falls back to English + +--- + +## 📋 Requirements + +- **Node.js** 18+ +- **NPM** 9+ +- **Linux** or **Windows** (via Git Bash) + +> ⚠️ Android/iOS and Termux have experimental support with no guarantees. + +--- + +## 📝 License + +Distributed under the **GPLv3** license. See [LICENSE](LICENSE) for details. + +--- + +
+ +**[⬆ Back to top](#)** + +
diff --git a/TERMOS_pt-br.md b/TERMOS_pt-br.md index 473bc86..abb5d0b 100644 --- a/TERMOS_pt-br.md +++ b/TERMOS_pt-br.md @@ -1,6 +1,6 @@ ## Termos de Uso -> Este arquivo poderá ser excluído no futuro, assim que o site oficial for lançado. +> Este arquivo poderá ser movido no futuro, assim que o site oficial for lançado. ### 1. Natureza do Software diff --git a/TERMS_en-us.md b/TERMS_en-us.md index d6c385b..3cd6e9e 100644 --- a/TERMS_en-us.md +++ b/TERMS_en-us.md @@ -1,6 +1,6 @@ ## Terms of Use -. This is file may be deleted in the future as the official webpage is launched. +> This is file may be moved in the future as the official webpage is launched. ### 1. Nature of the Software diff --git a/docs/API (en).md b/docs/API (en).md new file mode 100644 index 0000000..f2bc673 --- /dev/null +++ b/docs/API (en).md @@ -0,0 +1,277 @@ +# 🛠️ API Reference + +Complete documentation of objects available in plugins. + +--- + +## The `msg` Object + +Contains information about the received message. + +### Properties + +| Property | Type | Description | +|----------|------|-------------| +| `msg.body` | `string` | Full text of the message | +| `msg.args` | `string[]` | Message tokens. E.g.: `"!video url"` → `["!video", "url"]` | +| `msg.type` | `string` | Message type: `chat`, `image`, `video`, `audio`, `sticker`, `ptt` (voice), `document`, `location` | +| `msg.sender` | `string` | Sender ID (format: `NUMBER@c.us` or `NUMBER@g.us`) | +| `msg.senderName` | `string` | Display name of the sender | +| `msg.fromMe` | `boolean` | `true` if the bot itself sent the message | +| `msg.hasMedia` | `boolean` | `true` if the message contains media | +| `msg.hasReply` | `boolean` | `true` if the message is a reply to another | +| `msg.isGif` | `boolean` | `true` if the media is a GIF | +| `msg.timestamp` | `number` | Unix timestamp of the message | + +### Methods + +#### `msg.is(cmd)` + +Checks whether the message starts with the specified command. + +```javascript +if (msg.is(CMD_PREFIX + "video")) { + // Execute command +} +``` + +**Returns:** `boolean` + +--- + +#### `msg.reply(text)` + +Replies to the current message with a quote (citation). + +```javascript +await msg.reply("Reply with citation!"); +``` + +**Parameters:** +- `text` (string): Text of the reply + +**Returns:** `Promise` + +--- + +#### `msg.downloadMedia()` + +Downloads the media from the message. + +```javascript +const media = await msg.downloadMedia(); +// Returns: { mimetype: "image/jpeg", data: "base64string..." } +``` + +**Returns:** `Promise<{ mimetype: string, data: string } | null>` + +--- + +#### `msg.getReply()` + +Returns the message that was quoted. + +```javascript +const quotedMsg = msg.getReply(); +if (quotedMsg) { + console.log(quotedMsg.body); +} +``` + +**Returns:** `msg object | null` + +--- + +## The `api` Object + +Contains methods for interacting with WhatsApp and other plugins. + +### Properties + +#### `api.chat` + +Information about the current chat. + +| Property | Type | Description | +|----------|------|-------------| +| `api.chat.id` | `string` | Chat ID | +| `api.chat.name` | `string` | Chat name | +| `api.chat.isGroup` | `boolean` | `true` if it is a group | + +--- + +### Send Methods + +#### `api.send(text)` + +Sends a text message. + +```javascript +await api.send("Message sent!"); +``` + +**Parameters:** +- `text` (string): Text to send + +**Returns:** `Promise` + +--- + +#### `api.sendVideo(filePath)` + +Sends a video from the file system. + +```javascript +await api.sendVideo("/path/to/video.mp4"); +``` + +**Parameters:** +- `filePath` (string): Path to the file + +**Returns:** `Promise` + +--- + +#### `api.sendAudio(filePath)` + +Sends audio as a voice message (PTT). + +```javascript +await api.sendAudio("/path/to/audio.mp3"); +``` + +**Parameters:** +- `filePath` (string): Path to the file + +**Returns:** `Promise` + +--- + +#### `api.sendImage(filePath, caption?)` + +Sends an image. + +```javascript +await api.sendImage("/path/to/image.jpg", "Optional caption"); +``` + +**Parameters:** +- `filePath` (string): Path to the file +- `caption` (string, optional): Image caption + +**Returns:** `Promise` + +--- + +#### `api.sendSticker(bufferOrPath)` + +Sends a sticker. Accepts a `Buffer` or a file path. + +```javascript +// With Buffer +const buffer = fs.readFileSync("image.png"); +await api.sendSticker(buffer); + +// With path +await api.sendSticker("/path/to/image.png"); +``` + +**Parameters:** +- `bufferOrPath` (`Buffer` | `string`): Image data or file path + +**Returns:** `Promise` + +--- + +### Plugin Methods + +#### `api.getPlugin(name)` + +Accesses the public API of another plugin. + +```javascript +const utils = api.getPlugin("utilities"); +const data = utils.formatDate(new Date()); +``` + +**Parameters:** +- `name` (string): Plugin name + +**Returns:** `object | undefined` + +--- + +### Log Methods + +#### `api.log.info(...args)` + +Informational log. + +```javascript +api.log.info("Message received:", msg.body); +``` + +#### `api.log.warn(...args)` + +Warning log. + +```javascript +api.log.warn("Missing config, using default"); +``` + +#### `api.log.error(...args)` + +Error log. + +```javascript +api.log.error("Failed to process:", error); +``` + +--- + +## Configuration Object + +Import settings from `manybot.conf`: + +```javascript +import { CMD_PREFIX, CLIENT_ID, CHATS, PLUGINS } from "../../config.js"; + +// Custom configurations also work +import { MY_PREFIX } from "../../config.js"; +``` + +--- + +## Full Example + +```javascript +import { CMD_PREFIX } from "../../config.js"; +import fs from "fs"; + +export default async function ({ msg, api }) { + // Ignore messages from the bot itself + if (msg.fromMe) return; + + // Command: !echo + if (!msg.is(CMD_PREFIX + "echo")) return; + + api.log.info("Echo command received from:", msg.senderName); + + // If it has media, download and resend + if (msg.hasMedia) { + const media = await msg.downloadMedia(); + await api.sendSticker(media.data); + return; + } + + // If it's a reply, echo the quoted message + if (msg.hasReply) { + const quoted = msg.getReply(); + await msg.reply(`You quoted: "${quoted.body}"`); + return; + } + + // Default response + await msg.reply("Send media or reply to a message!"); +} +``` \ No newline at end of file diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..6a28b9f --- /dev/null +++ b/docs/API.md @@ -0,0 +1,277 @@ +# 🛠️ Referência da API + +Documentação completa dos objetos disponíveis nos plugins. + +--- + +## Objeto `msg` + +Contém informações da mensagem recebida. + +### Propriedades + +| Propriedade | Tipo | Descrição | +|-------------|------|-----------| +| `msg.body` | `string` | Texto completo da mensagem | +| `msg.args` | `string[]` | Tokens da mensagem. Ex: `"!video url"` → `["!video", "url"]` | +| `msg.type` | `string` | Tipo da mensagem: `chat`, `image`, `video`, `audio`, `sticker`, `ptt` (voz), `document`, `location` | +| `msg.sender` | `string` | ID do remetente (formato: `NUMERO@c.us` ou `NUMERO@g.us`) | +| `msg.senderName` | `string` | Nome de exibição do remetente | +| `msg.fromMe` | `boolean` | `true` se o próprio bot enviou a mensagem | +| `msg.hasMedia` | `boolean` | `true` se a mensagem contém mídia | +| `msg.hasReply` | `boolean` | `true` se é uma resposta a outra mensagem | +| `msg.isGif` | `boolean` | `true` se a mídia é um GIF | +| `msg.timestamp` | `number` | Timestamp Unix da mensagem | + +### Métodos + +#### `msg.is(cmd)` + +Verifica se a mensagem começa com o comando especificado. + +```javascript +if (msg.is(CMD_PREFIX + "video")) { + // Executa comando +} +``` + +**Retorno:** `boolean` + +--- + +#### `msg.reply(text)` + +Responde à mensagem atual com quote (citação). + +```javascript +await msg.reply("Resposta com citação!"); +``` + +**Parâmetros:** +- `text` (string): Texto da resposta + +**Retorno:** `Promise` + +--- + +#### `msg.downloadMedia()` + +Baixa a mídia da mensagem. + +```javascript +const media = await msg.downloadMedia(); +// Retorna: { mimetype: "image/jpeg", data: "base64string..." } +``` + +**Retorno:** `Promise<{ mimetype: string, data: string } | null>` + +--- + +#### `msg.getReply()` + +Retorna a mensagem que foi citada. + +```javascript +const mensagemCitada = msg.getReply(); +if (mensagemCitada) { + console.log(mensagemCitada.body); +} +``` + +**Retorno:** `msg object | null` + +--- + +## Objeto `api` + +Contém métodos para interagir com o WhatsApp e outros plugins. + +### Propriedades + +#### `api.chat` + +Informações do chat atual. + +| Propriedade | Tipo | Descrição | +|-------------|------|-----------| +| `api.chat.id` | `string` | ID do chat | +| `api.chat.name` | `string` | Nome do chat | +| `api.chat.isGroup` | `boolean` | `true` se é grupo | + +--- + +### Métodos de Envio + +#### `api.send(text)` + +Envia uma mensagem de texto. + +```javascript +await api.send("Mensagem enviada!"); +``` + +**Parâmetros:** +- `text` (string): Texto a enviar + +**Retorno:** `Promise` + +--- + +#### `api.sendVideo(filePath)` + +Envia um vídeo do sistema de arquivos. + +```javascript +await api.sendVideo("/caminho/para/video.mp4"); +``` + +**Parâmetros:** +- `filePath` (string): Caminho para o arquivo + +**Retorno:** `Promise` + +--- + +#### `api.sendAudio(filePath)` + +Envia um áudio como mensagem de voz (PTT). + +```javascript +await api.sendAudio("/caminho/para/audio.mp3"); +``` + +**Parâmetros:** +- `filePath` (string): Caminho para o arquivo + +**Retorno:** `Promise` + +--- + +#### `api.sendImage(filePath, caption?)` + +Envia uma imagem. + +```javascript +await api.sendImage("/caminho/para/imagem.jpg", "Legenda opcional"); +``` + +**Parâmetros:** +- `filePath` (string): Caminho para o arquivo +- `caption` (string, opcional): Legenda da imagem + +**Retorno:** `Promise` + +--- + +#### `api.sendSticker(bufferOuPath)` + +Envia uma figurinha. Aceita `Buffer` ou caminho para arquivo. + +```javascript +// Com Buffer +const buffer = fs.readFileSync("imagem.png"); +await api.sendSticker(buffer); + +// Com caminho +await api.sendSticker("/caminho/para/imagem.png"); +``` + +**Parâmetros:** +- `bufferOuPath` (`Buffer` | `string`): Dados da imagem ou caminho + +**Retorno:** `Promise` + +--- + +### Métodos de Plugin + +#### `api.getPlugin(name)` + +Acessa a API pública de outro plugin. + +```javascript +const utils = api.getPlugin("utilidades"); +const data = utils.formatarData(new Date()); +``` + +**Parâmetros:** +- `name` (string): Nome do plugin + +**Retorno:** `object | undefined` + +--- + +### Métodos de Log + +#### `api.log.info(...args)` + +Log informativo. + +```javascript +api.log.info("Mensagem recebida:", msg.body); +``` + +#### `api.log.warn(...args)` + +Log de aviso. + +```javascript +api.log.warn("Configuração ausente, usando padrão"); +``` + +#### `api.log.error(...args)` + +Log de erro. + +```javascript +api.log.error("Falha ao processar:", erro); +``` + +--- + +## Objeto de Configuração + +Importe configurações do `manybot.conf`: + +```javascript +import { CMD_PREFIX, CLIENT_ID, CHATS, PLUGINS } from "../../config.js"; + +// Configurações personalizadas também funcionam +import { MEU_PREFIXO } from "../../config.js"; +``` + +--- + +## Exemplo Completo + +```javascript +import { CMD_PREFIX } from "../../config.js"; +import fs from "fs"; + +export default async function ({ msg, api }) { + // Ignora mensagens do próprio bot + if (msg.fromMe) return; + + // Comando: !eco + if (!msg.is(CMD_PREFIX + "eco")) return; + + api.log.info("Comando eco recebido de:", msg.senderName); + + // Se tem mídia, baixa e reenvia + if (msg.hasMedia) { + const media = await msg.downloadMedia(); + await api.sendSticker(media.data); + return; + } + + // Se é resposta, ecoa a mensagem citada + if (msg.hasReply) { + const citada = msg.getReply(); + await msg.reply(`Você citou: "${citada.body}"`); + return; + } + + // Resposta padrão + await msg.reply("Envie uma mídia ou responda uma mensagem!"); +} +``` diff --git a/docs/CONFIGURACAO.md b/docs/CONFIGURACAO.md new file mode 100644 index 0000000..7876473 --- /dev/null +++ b/docs/CONFIGURACAO.md @@ -0,0 +1,146 @@ +# ⚙️ Configuração + +Guia completo do arquivo `manybot.conf`. + +--- + +## Estrutura Básica + +```bash +# Comentários começam com '#' + +CLIENT_ID=bot_permanente +CMD_PREFIX=! +LANGUAGE=pt +CHATS=[] +PLUGINS=[] +``` + +--- + +## Opções + +### CLIENT_ID + +Identificador único da sessão do bot. + +```bash +CLIENT_ID=bot_permanente +``` + +- **Padrão:** `bot_permanente` +- **Uso:** Cria uma pasta `session/` com esse nome para armazenar dados de autenticação + +### CMD_PREFIX + +Caractere que indica o início de um comando. + +```bash +CMD_PREFIX=! +``` + +- **Padrão:** `!` +- **Exemplo:** Com prefixo `!`, o comando é `!figurinha`. Com `.`, seria `.figurinha`. + +### LANGUAGE + +Idioma das mensagens do bot. + +```bash +LANGUAGE=pt +``` + +- **Padrão:** `en` (inglês) +- **Opções:** `en` (inglês), `pt` (português), `es` (espanhol) +- **Nota:** Se o idioma selecionado não existir, o bot usará inglês como fallback + +### CHATS + +Lista de IDs de chats onde o bot responderá. + +```bash +CHATS=[ + 123456789@c.us, # Chat privado + 123456789@g.us # Grupo +] +``` + +- **Padrão:** `[]` (vazio = responde em todos) +- **Formato:** + - Privado: `NUMERO@c.us` + - Grupo: `NUMERO@g.us` + +#### Como descobrir o ID + +```bash +node src/utils/get_id.js +``` + +Escaneie o QR Code e mande uma mensagem no chat. O ID aparecerá no terminal. + +> Nota: O utilitário usa um `CLIENT_ID` separado para não conflitar com a sessão principal. + +### PLUGINS + +Lista de plugins a serem carregados. + +```bash +PLUGINS=[ + video, + audio, + figurinha, + adivinhacao, + forca, + many, + obrigado +] +``` + +- **Padrão:** `[]` (nenhum) +- Cada nome corresponde a uma pasta em `src/plugins/` +- Remova ou comente para desativar sem apagar + +--- + +## Configurações Personalizadas + +Você pode adicionar suas próprias variáveis para plugins: + +```bash +# manybot.conf +MEU_PREFIXO=> +API_KEY=minha_chave +``` + +E acessar no código do plugin: + +```javascript +import { MEU_PREFIXO, API_KEY } from "../../config.js"; +``` + +--- + +## Exemplo Completo + +```bash +# ManyBot Configuration + +CLIENT_ID=meu_bot_prod +CMD_PREFIX=/ +LANGUAGE=pt + +CHATS=[ + 5511999999999@c.us, + 5511888888888-123456789@g.us +] + +PLUGINS=[ + figurinha, + video, + audio, + many +] + +# Configurações extras +ADMIN_NUMBER=5511999999999@c.us +``` diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md new file mode 100644 index 0000000..a3ed47b --- /dev/null +++ b/docs/CONFIGURATION.md @@ -0,0 +1,146 @@ +# Configuration + +Complete guide for the `manybot.conf` file. + +--- + +## Basic Structure + +```bash +# Comments start with '#' + +CLIENT_ID=bot_permanente +CMD_PREFIX=! +LANGUAGE=en +CHATS=[] +PLUGINS=[] +``` + +--- + +## Options + +### CLIENT_ID + +Unique identifier for the bot session. + +```bash +CLIENT_ID=my_bot +``` + +- **Default:** `bot_permanente` +- **Usage:** Creates a `session/` folder with this name to store authentication data + +### CMD_PREFIX + +Character that indicates the start of a command. + +```bash +CMD_PREFIX=! +``` + +- **Default:** `!` +- **Example:** With prefix `!`, the command is `!sticker`. With `.`, it would be `.sticker`. + +### LANGUAGE + +Bot message language. + +```bash +LANGUAGE=en +``` + +- **Default:** `en` (English) +- **Options:** `en` (English), `pt` (Portuguese), `es` (Spanish) +- **Note:** If the selected language doesn't exist, the bot will fall back to English + +### CHATS + +List of chat IDs where the bot will respond. + +```bash +CHATS=[ + 123456789@c.us, # Private chat + 123456789@g.us # Group +] +``` + +- **Default:** `[]` (empty = responds to all) +- **Format:** + - Private: `NUMBER@c.us` + - Group: `NUMBER@g.us` + +#### How to discover the ID + +```bash +node src/utils/get_id.js +``` + +Scan the QR Code and send a message in the chat. The ID will appear in the terminal. + +> Note: The utility uses a separate `CLIENT_ID` to avoid conflicting with the main session. + +### PLUGINS + +List of plugins to be loaded. + +```bash +PLUGINS=[ + video, + audio, + sticker, + guess, + hangman, + many, + thanks +] +``` + +- **Default:** `[]` (none) +- Each name corresponds to a folder in `src/plugins/` +- Remove or comment to disable without deleting + +--- + +## Custom Settings + +You can add your own variables for plugins: + +```bash +# manybot.conf +MY_PREFIX=> +API_KEY=my_key +``` + +And access in the plugin code: + +```javascript +import { MY_PREFIX, API_KEY } from "../../config.js"; +``` + +--- + +## Complete Example + +```bash +# ManyBot Configuration + +CLIENT_ID=my_bot_prod +CMD_PREFIX=/ +LANGUAGE=en + +CHATS=[ + 5511999999999@c.us, + 5511888888888-123456789@g.us +] + +PLUGINS=[ + sticker, + video, + audio, + many +] + +# Extra settings +ADMIN_NUMBER=5511999999999@c.us +``` diff --git a/docs/INSTALACAO.md b/docs/INSTALACAO.md new file mode 100644 index 0000000..efce707 --- /dev/null +++ b/docs/INSTALACAO.md @@ -0,0 +1,144 @@ +# 📥 Instalação + +Guia completo de instalação do ManyBot em diferentes plataformas. + +--- + +## 📑 Índice + +- [Linux](#linux) +- [Windows](#windows) +- [Termux (Android)](#termux-android) + +--- + +## Linux + +### 1. Clone o repositório + +```bash +git clone https://github.com/synt-xerror/manybot +cd manybot +``` + +### 2. Configure o bot + +Crie o arquivo de configuração: + +```bash +touch manybot.conf +nano manybot.conf +``` + +Exemplo de configuração: + +```bash +# Comentários com '#' + +CLIENT_ID=bot_permanente +CMD_PREFIX=! +LANGUAGE=pt +CHATS=[ + 123456789@c.us, + 123456789@g.us +] +PLUGINS=[ + video, + audio, + figurinha, + adivinhacao +] +``` + +**Detalhes:** +- `CLIENT_ID`: ID da sessão (padrão: `bot_permanente`) +- `CMD_PREFIX`: Prefixo dos comandos (padrão: `!`) +- `LANGUAGE`: Idioma do bot - `pt`, `en` ou `es` (padrão: `en`) +- `CHATS`: IDs dos chats permitidos (deixe vazio para todos) +- `PLUGINS`: Lista de plugins ativos + +### 3. Execute a instalação + +```bash +bash ./setup +``` + +### 4. Primeira execução + +```bash +node ./src/main.js +``` + +Escaneie o QR Code no WhatsApp: + +**Menu → Dispositivos conectados → Conectar um dispositivo** + +--- + +## Windows + +O ManyBot foi pensado para Linux, mas funciona no Windows via **Git Bash**. + +### Pré-requisitos + +1. **Git Bash**: https://git-scm.com/download/win +2. **Node.js**: https://nodejs.org (escolha "Instalador Windows (.msi)") + +### Instalação + +Após instalar ambos, abra o **Git Bash** e siga os mesmos passos da [instalação Linux](#linux). + +--- + +## Termux (Android) + +> ⚠️ **Aviso:** Suporte experimental. Não há garantia de funcionamento. + +```bash +# Instale o Termux pela F-Droid (não use Play Store) +# https://f-droid.org/packages/com.termux/ + +# Atualize pacotes +pkg update && pkg upgrade + +# Instale dependências +pkg install nodejs git + +# Clone e instale +git clone https://github.com/synt-xerror/manybot +cd manybot +``` + +Siga os passos de configuração Linux a partir do passo 2. + +--- + +## 🔧 Resolução de Problemas + +### Erro ao escanear QR Code + +- Limpe os dados do Chrome/Chromium do Termux +- Delete a pasta `session/` e tente novamente + +### Bot não responde comandos + +- Verifique o `CMD_PREFIX` no `manybot.conf` +- Confira se o plugin está na lista `PLUGINS` + +### Erros de instalação + +```bash +# Limpe a cache do npm +npm cache clean --force + +# Reinstale dependências +rm -rf node_modules package-lock.json +npm install +``` + +--- + +## 📚 Próximos Passos + +- [Configuração avançada](./CONFIGURACAO.md) +- [Criando plugins](./PLUGINS.md) diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md new file mode 100644 index 0000000..9a20404 --- /dev/null +++ b/docs/INSTALLATION.md @@ -0,0 +1,144 @@ +# Installation + +Complete installation guide for ManyBot on different platforms. + +--- + +## Index + +- [Linux](#linux) +- [Windows](#windows) +- [Termux (Android)](#termux-android) + +--- + +## Linux + +### 1. Clone the repository + +```bash +git clone https://github.com/synt-xerror/manybot +cd manybot +``` + +### 2. Configure the bot + +Create the configuration file: + +```bash +touch manybot.conf +nano manybot.conf +``` + +Example configuration: + +```bash +# Comments with '#' + +CLIENT_ID=bot_permanente +CMD_PREFIX=! +LANGUAGE=en +CHATS=[ + 123456789@c.us, + 123456789@g.us +] +PLUGINS=[ + video, + audio, + sticker, + guess +] +``` + +**Details:** +- `CLIENT_ID`: Session ID (default: `bot_permanente`) +- `CMD_PREFIX`: Command prefix (default: `!`) +- `LANGUAGE`: Bot language - `pt`, `en`, or `es` (default: `en`) +- `CHATS`: Allowed chat IDs (leave empty for all) +- `PLUGINS`: List of active plugins + +### 3. Run installation + +```bash +bash ./setup +``` + +### 4. First run + +```bash +node ./src/main.js +``` + +Scan the QR Code in WhatsApp: + +**Menu → Linked Devices → Link a Device** + +--- + +## Windows + +ManyBot was designed for Linux, but works on Windows via **Git Bash**. + +### Prerequisites + +1. **Git Bash**: https://git-scm.com/download/win +2. **Node.js**: https://nodejs.org (choose "Windows Installer (.msi)") + +### Installation + +After installing both, open **Git Bash** and follow the same steps as the [Linux installation](#linux). + +--- + +## Termux (Android) + +> ⚠️ **Warning:** Experimental support. No guarantee of functionality. + +```bash +# Install Termux from F-Droid (don't use Play Store) +# https://f-droid.org/packages/com.termux/ + +# Update packages +pkg update && pkg upgrade + +# Install dependencies +pkg install nodejs git + +# Clone and install +git clone https://github.com/synt-xerror/manybot +cd manybot +``` + +Follow the Linux configuration steps from step 2. + +--- + +## Troubleshooting + +### QR Code scanning error + +- Clear Chrome/Chromium data from Termux +- Delete the `session/` folder and try again + +### Bot not responding to commands + +- Check `CMD_PREFIX` in `manybot.conf` +- Make sure the plugin is in the `PLUGINS` list + +### Installation errors + +```bash +# Clean npm cache +npm cache clean --force + +# Reinstall dependencies +rm -rf node_modules package-lock.json +npm install +``` + +--- + +## Next Steps + +- [Advanced configuration](./CONFIGURATION.md) +- [Creating plugins](./PLUGINS_EN.md) diff --git a/docs/PLUGINS (en).md b/docs/PLUGINS (en).md new file mode 100644 index 0000000..476a952 --- /dev/null +++ b/docs/PLUGINS (en).md @@ -0,0 +1,317 @@ +# 🔌 Creating Plugins + +Complete guide to creating plugins in ManyBot. + +--- + +## 📑 Index + +- [Basic Structure](#basic-structure) +- [Plugin Manifest](#plugin-manifest-manyplug-json) +- [Creating Your First Plugin](#creating-your-first-plugin) +- [Object API](#object-api) +- [Exposing API](#exposing-api-to-other-plugins) +- [Translating Your Plugin](#translating-your-plugin) +- [Error Handling](#error-handling) + +--- + +## Basic Structure + +``` +src/plugins/ +└── my-plugin/ + ├── index.js + ├── manyplug.json + └── locale/ (optional) + ├── en.json + ├── pt.json + └── es.json +``` + +`index.js` must export a `default` function that receives `{ msg, api }`: + +```javascript +export default async function ({ msg, api }) { + // Your logic here +} +``` + +--- + +## Plugin Manifest (manyplug.json) + +Every plugin should have a `manyplug.json` at its root. It describes the plugin and declares any extra npm dependencies it needs. + +```json +{ + "name": "my-plugin", + "version": "1.0.0", + "category": "utility", + "service": false, + "dependencies": {} +} +``` + +### Fields + +| Field | Type | Description | +|-------|------|-------------| +| `name` | `string` | Plugin identifier, must match the folder name | +| `version` | `string` | Semantic version (e.g., `"1.0.0"`) | +| `category` | `string` | Plugin category: `utility`, `media`, `game`, `humor`, `info` | +| `service` | `boolean` | `true` if the plugin runs in the background (scheduler, listener). `false` if triggered by a command or event | +| `dependencies` | `object` | Extra npm packages required by the plugin, same format as `package.json` | + +### Example with dependencies + +```json +{ + "name": "weather", + "version": "1.0.0", + "category": "utility", + "service": false, + "dependencies": { + "axios": "^1.6.0" + } +} +``` + +After adding dependencies, run `npm install` at the project root to install them. + +--- + +## Creating Your First Plugin + +### Example 1: Simple command + +```javascript +// plugins/greeting/index.js +import { CMD_PREFIX } from "../../config.js"; + +export default async function ({ msg, api }) { + // Only responds if the message starts with "!hi" + if (!msg.is(CMD_PREFIX + "hi")) return; + + await msg.reply("Hello! 👋"); +} +``` + +### Example 2: Command with arguments + +```javascript +// plugins/calculate/index.js +import { CMD_PREFIX } from "../../config.js"; + +export default async function ({ msg, api }) { + if (!msg.is(CMD_PREFIX + "calculate")) return; + + // msg.args = ["!calculate", "5", "+", "3"] + const [, a, operator, b] = msg.args; + + let result; + switch (operator) { + case "+": result = Number(a) + Number(b); break; + case "-": result = Number(a) - Number(b); break; + 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}`); +} +``` + +### Example 3: Processing media + +```javascript +// plugins/echo-media/index.js +import { CMD_PREFIX } from "../../config.js"; + +export default async function ({ msg, api }) { + if (!msg.is(CMD_PREFIX + "echo")) return; + + // Checks if the message has media + if (!msg.hasMedia) { + return msg.reply("Send a media file with the command!"); + } + + // Downloads the media + const media = await msg.downloadMedia(); + + // Resends in the chat + await api.sendSticker(media.data); +} +``` + +--- + +## Object API + +### `msg` Object + +| Property | Type | Description | +|----------|------|-------------| +| `msg.body` | `string` | Message text | +| `msg.args` | `string[]` | Message tokens | +| `msg.type` | `string` | Type: `chat`, `image`, `video`, `audio`, `sticker` | +| `msg.sender` | `string` | Sender ID | +| `msg.senderName` | `string` | Sender name | +| `msg.fromMe` | `boolean` | Whether the bot sent it | +| `msg.hasMedia` | `boolean` | Whether it has media | +| `msg.hasReply` | `boolean` | Whether it is a reply | +| `msg.isGif` | `boolean` | Whether it is a GIF | +| `msg.is(cmd)` | `function` | Checks if starts with command | +| `msg.reply(text)` | `function` | Replies with quote | +| `msg.downloadMedia()` | `function` | Returns `{ mimetype, data }` | +| `msg.getReply()` | `function` | Returns quoted message | + +### `api` Object + +| Method | Description | +|--------|-------------| +| `api.send(text)` | Sends text | +| `api.sendVideo(path)` | Sends video | +| `api.sendAudio(path)` | Sends audio (voice) | +| `api.sendImage(path, caption?)` | Sends image | +| `api.sendSticker(bufferOrPath)` | Sends sticker | +| `api.getPlugin(name)` | Accesses another plugin | +| `api.chat.id` | Chat ID | +| `api.chat.name` | Chat name | +| `api.chat.isGroup` | Whether it is a group | +| `api.log.info(...)` | Info log | +| `api.log.warn(...)` | Warning log | +| `api.log.error(...)` | Error log | + +--- + +## Exposing API to Other Plugins + +A plugin can export functions for others to use: + +```javascript +// plugins/utilities/index.js + +// Public API +export const api = { + formatDate: (date) => date.toLocaleDateString("en-US"), + formatCurrency: (value) => `$${value.toFixed(2)}`, + wait: (ms) => new Promise(resolve => setTimeout(resolve, ms)) +}; + +// Normal plugin logic +export default async function ({ msg }) { + if (msg.is("!ping")) { + await msg.reply("pong!"); + } +} +``` + +Another plugin using it: + +```javascript +// plugins/other/index.js +export default async function ({ msg, api }) { + const utils = api.getPlugin("utilities"); + + const date = utils.formatDate(new Date()); + await msg.reply(`Today is ${date}`); +} +``` + +--- + +## Translating Your Plugin + +Each plugin can have its own translations, completely independent from the bot core. The bot locale (set in `manybot.conf`) is used automatically. + +### Structure + +``` +src/plugins/ +└── my-plugin/ + ├── index.js + └── locale/ + ├── en.json + ├── pt.json + └── es.json +``` + +### locale/en.json + +```json +{ + "hello": "Hello, {{name}}! 👋", + "error": { + "notFound": "Item not found." + } +} +``` + +### index.js + +```javascript +import { CMD_PREFIX } from "../../config.js"; +import { createPluginI18n } from "../../utils/pluginI18n.js"; + +const { t } = createPluginI18n(import.meta.url); + +export default async function ({ msg }) { + if (!msg.is(CMD_PREFIX + "hi")) return; + + // Simple key + await msg.reply(t("hello", { name: msg.senderName })); + + // Nested key + await msg.reply(t("error.notFound")); +} +``` + +### Notes + +- If the configured locale has no translation file, falls back to `en.json`. +- If the key doesn't exist in any file, the key itself is returned as-is. +- Use `{{variable}}` syntax for interpolation. +- Each plugin manages its own translations — never import `t` from the bot core. + +--- + +## Error Handling + +If a plugin throws an error, the kernel automatically disables it: + +```javascript +export default async function ({ msg, api }) { + try { + // Code that might fail + const result = await somethingRisky(); + await msg.reply(result); + } catch (error) { + // Logs the error and notifies + api.log.error("Plugin error:", error); + await msg.reply("Oops! Something went wrong."); + } +} +``` + +--- + +## Enabling the Plugin + +After creating it, add to `manybot.conf`: + +```bash +PLUGINS=[ + # ... other plugins + my-plugin +] +``` + +Restart the bot to load it. + +--- + +## See Also + +- [API Reference](./API.md) +- [Plugin examples](../src/plugins/) \ No newline at end of file diff --git a/docs/PLUGINS.md b/docs/PLUGINS.md new file mode 100644 index 0000000..9275ebd --- /dev/null +++ b/docs/PLUGINS.md @@ -0,0 +1,317 @@ +# 🔌 Criando Plugins + +Guia completo para criar plugins no ManyBot. + +--- + +## 📑 Índice + +- [Estrutura Básica](#estrutura-básica) +- [Manifesto do Plugin](#manifesto-do-plugin-manyplugjson) +- [Criando Seu Primeiro Plugin](#criando-seu-primeiro-plugin) +- [API de Objetos](#api-de-objetos) +- [Expondo API](#expondo-api-para-outros-plugins) +- [Traduzindo Seu Plugin](#traduzindo-seu-plugin) +- [Tratamento de Erros](#tratamento-de-erros) + +--- + +## Estrutura Básica + +``` +src/plugins/ +└── meu-plugin/ + ├── index.js + ├── manyplug.json + └── locale/ (opcional) + ├── en.json + ├── pt.json + └── es.json +``` + +O `index.js` deve exportar uma função `default` que recebe `{ msg, api }`: + +```javascript +export default async function ({ msg, api }) { + // Sua lógica aqui +} +``` + +--- + +## Manifesto do Plugin (manyplug.json) + +Todo plugin deve ter um `manyplug.json` na raiz. Ele descreve o plugin e declara dependências npm extras que ele precisar. + +```json +{ + "name": "meu-plugin", + "version": "1.0.0", + "category": "utility", + "service": false, + "dependencies": {} +} +``` + +### Campos + +| Campo | Tipo | Descrição | +|-------|------|-----------| +| `name` | `string` | Identificador do plugin, deve ser igual ao nome da pasta | +| `version` | `string` | Versão semântica (ex: `"1.0.0"`) | +| `category` | `string` | Categoria do plugin: `utility`, `media`, `game`, `humor`, `info` | +| `service` | `boolean` | `true` se o plugin roda em segundo plano (agendador, listener). `false` se acionado por comando ou evento | +| `dependencies` | `object` | Pacotes npm extras necessários, mesmo formato do `package.json` | + +### Exemplo com dependências + +```json +{ + "name": "clima", + "version": "1.0.0", + "category": "utility", + "service": false, + "dependencies": { + "axios": "^1.6.0" + } +} +``` + +Após adicionar dependências, rode `npm install` na raiz do projeto para instalá-las. + +--- + +## Criando Seu Primeiro Plugin + +### Exemplo 1: Comando simples + +```javascript +// plugins/saudacao/index.js +import { CMD_PREFIX } from "../../config.js"; + +export default async function ({ msg, api }) { + // Só responde se a mensagem começar com "!oi" + if (!msg.is(CMD_PREFIX + "oi")) return; + + await msg.reply("Olá! 👋"); +} +``` + +### Exemplo 2: Comando com argumentos + +```javascript +// plugins/calcular/index.js +import { CMD_PREFIX } from "../../config.js"; + +export default async function ({ msg, api }) { + if (!msg.is(CMD_PREFIX + "calcular")) return; + + // msg.args = ["!calcular", "5", "+", "3"] + const [, a, operador, b] = msg.args; + + let resultado; + switch (operador) { + case "+": resultado = Number(a) + Number(b); break; + case "-": resultado = Number(a) - Number(b); break; + case "*": resultado = Number(a) * Number(b); break; + case "/": resultado = Number(a) / Number(b); break; + default: return msg.reply("Operador inválido!"); + } + + await msg.reply(`Resultado: ${resultado}`); +} +``` + +### Exemplo 3: Processando mídia + +```javascript +// plugins/echo-media/index.js +import { CMD_PREFIX } from "../../config.js"; + +export default async function ({ msg, api }) { + if (!msg.is(CMD_PREFIX + "echo")) return; + + // Verifica se a mensagem tem mídia + if (!msg.hasMedia) { + return msg.reply("Envie uma mídia com o comando!"); + } + + // Baixa a mídia + const media = await msg.downloadMedia(); + + // Reenvia no chat + await api.sendSticker(media.data); +} +``` + +--- + +## API de Objetos + +### Objeto `msg` + +| Propriedade | Tipo | Descrição | +|-------------|------|-----------| +| `msg.body` | `string` | Texto da mensagem | +| `msg.args` | `string[]` | Tokens da mensagem | +| `msg.type` | `string` | Tipo: `chat`, `image`, `video`, `audio`, `sticker` | +| `msg.sender` | `string` | ID do remetente | +| `msg.senderName` | `string` | Nome do remetente | +| `msg.fromMe` | `boolean` | Se o bot enviou | +| `msg.hasMedia` | `boolean` | Se tem mídia | +| `msg.hasReply` | `boolean` | Se é resposta | +| `msg.isGif` | `boolean` | Se é GIF | +| `msg.is(cmd)` | `function` | Verifica se começa com comando | +| `msg.reply(text)` | `function` | Responde com quote | +| `msg.downloadMedia()` | `function` | Retorna `{ mimetype, data }` | +| `msg.getReply()` | `function` | Retorna mensagem citada | + +### Objeto `api` + +| Método | Descrição | +|--------|-----------| +| `api.send(text)` | Envia texto | +| `api.sendVideo(path)` | Envia vídeo | +| `api.sendAudio(path)` | Envia áudio (voz) | +| `api.sendImage(path, caption?)` | Envia imagem | +| `api.sendSticker(bufferOrPath)` | Envia figurinha | +| `api.getPlugin(name)` | Acessa outro plugin | +| `api.chat.id` | ID do chat | +| `api.chat.name` | Nome do chat | +| `api.chat.isGroup` | Se é grupo | +| `api.log.info(...)` | Log informativo | +| `api.log.warn(...)` | Log de aviso | +| `api.log.error(...)` | Log de erro | + +--- + +## Expondo API para Outros Plugins + +Um plugin pode exportar funções para outros usarem: + +```javascript +// plugins/utilidades/index.js + +// API pública +export const api = { + formatarData: (date) => date.toLocaleDateString("pt-BR"), + formatarMoeda: (valor) => `R$ ${valor.toFixed(2).replace(".", ",")}`, + esperar: (ms) => new Promise(resolve => setTimeout(resolve, ms)) +}; + +// Lógica normal do plugin +export default async function ({ msg }) { + if (msg.is("!ping")) { + await msg.reply("pong!"); + } +} +``` + +Outro plugin usando: + +```javascript +// plugins/outro/index.js +export default async function ({ msg, api }) { + const utils = api.getPlugin("utilidades"); + + const data = utils.formatarData(new Date()); + await msg.reply(`Hoje é ${data}`); +} +``` + +--- + +## Traduzindo Seu Plugin + +Cada plugin pode ter suas próprias traduções, completamente independentes do core do bot. O locale do bot (definido no `manybot.conf`) é usado automaticamente. + +### Estrutura + +``` +src/plugins/ +└── meu-plugin/ + ├── index.js + └── locale/ + ├── en.json + ├── pt.json + └── es.json +``` + +### locale/pt.json + +```json +{ + "ola": "Olá, {{nome}}! 👋", + "erro": { + "naoEncontrado": "Item não encontrado." + } +} +``` + +### index.js + +```javascript +import { CMD_PREFIX } from "../../config.js"; +import { createPluginI18n } from "../../utils/pluginI18n.js"; + +const { t } = createPluginI18n(import.meta.url); + +export default async function ({ msg }) { + if (!msg.is(CMD_PREFIX + "oi")) return; + + // Chave simples + await msg.reply(t("ola", { nome: msg.senderName })); + + // Chave aninhada + await msg.reply(t("erro.naoEncontrado")); +} +``` + +### Observações + +- Se o locale configurado não tiver arquivo de tradução, cai automaticamente para `en.json`. +- Se a chave não existir em nenhum arquivo, a própria chave é retornada. +- Use a sintaxe `{{variavel}}` para interpolação. +- Cada plugin gerencia suas próprias traduções — nunca importe `t` do core do bot. + +--- + +## Tratamento de Erros + +Se um plugin lançar erro, o kernel o desativa automaticamente: + +```javascript +export default async function ({ msg, api }) { + try { + // Código que pode falhar + const resultado = await algoPerigoso(); + await msg.reply(resultado); + } catch (erro) { + // Loga o erro e notifica + api.log.error("Erro no plugin:", erro); + await msg.reply("Ops! Algo deu errado."); + } +} +``` + +--- + +## Ativando o Plugin + +Depois de criar, adicione ao `manybot.conf`: + +```bash +PLUGINS=[ + # ... outros plugins + meu-plugin +] +``` + +Reinicie o bot para carregar. + +--- + +## Veja Também + +- [Referência da API](./API.md) +- [Exemplos de plugins](../src/plugins/) \ No newline at end of file diff --git a/man/man1/manybot-plugin.1 b/man/man1/manybot-plugin.1 new file mode 100644 index 0000000..525e27f --- /dev/null +++ b/man/man1/manybot-plugin.1 @@ -0,0 +1,267 @@ +.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.conf R: +.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.conf R: +.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 diff --git a/man/man1/manybot.1 b/man/man1/manybot.1 new file mode 100644 index 0000000..dfbb7f2 --- /dev/null +++ b/man/man1/manybot.1 @@ -0,0 +1,171 @@ +.TH MANYBOT 1 "April 2026" "ManyBot 2.4.3" "User Commands" +.SH NAME +manybot \- local WhatsApp bot with plugin system +.SH SYNOPSIS +.B node +.I ./src/main.js +.br +.B node +.I src/utils/get_id.js +.br +.B bash +.I ./setup +.br +.B bash +.I ./update +.SH DESCRIPTION +ManyBot is a 100%% local WhatsApp bot that operates without relying on the +official WhatsApp API. It uses whatsapp-web.js to connect as a regular +WhatsApp client and provides a modular plugin system for extending functionality. +.PP +The bot supports multiple chats in a single session, runs headless (without +a GUI), and can be configured through a simple configuration file. +.SH COMMANDS +.TP +.B !many +List all available commands from loaded plugins. +.TP +.B !figurinha +Convert images, GIFs, and videos to stickers. +.TP +.B !video \fIURL\fR +Download videos from supported platforms. +.TP +.B !audio \fIURL\fR +Download audio from videos and send as voice message. +.TP +.B !adivinhacao comecar +Start a guessing game (1-100). +.TP +.B !forca comecar +Start a hangman game. +.TP +.B !obrigado +Bot responds with a thank-you message. +.PP +Command prefix can be configured via +.B CMD_PREFIX +in +.IR manybot.conf . +Default is +.BR ! . +.SH CONFIGURATION +ManyBot uses a configuration file +.I manybot.conf +in the project root. Key options: +.TP +.B CLIENT_ID=\fIname\fR +Unique identifier for the bot session. Creates a session/\fIname\fR folder +for authentication data. Default: \fIbot_permanente\fR. +.TP +.B CMD_PREFIX=\fIchar\fR +Character prefixing all commands. Default: \fI!\fR. +.TP +.B LANGUAGE=\fIcode\fR +Bot interface language: \fBen\fR (English), \fBpt\fR (Portuguese), or \fBes\fR (Spanish). +Default: \fBen\fR. +.TP +.B CHATS=[\fIid1\fR, \fIid2\fR, ...] +List of allowed chat IDs. Empty array allows all chats. +Format: \fBnumber@c.us\fR for private chats, \fBnumber@g.us\fR for groups. +.TP +.B PLUGINS=[\fIname1\fR, \fIname2\fR, ...] +List of plugins to load at startup. Each name corresponds to a folder in +.IR src/plugins/ . +.PP +See +.BR manybot.conf (5) +for complete configuration reference. +.SH PLUGINS +Plugins extend ManyBot functionality without modifying the kernel. Each plugin +is a folder under +.I src/plugins/ +containing: +.TP +.I index.js +Main plugin file exporting a default async function receiving \fB{ msg, api }\fR. +.TP +.I manyplug.json +Plugin manifest describing name, version, category, service status, and dependencies. +.TP +.I locale/ +Optional translation files (\fBen.json\fR, \fBpt.json\fR, \fBes.json\fR). +.PP +Plugins receive two objects: +.TP +.B msg +Message information including \fBbody\fR, \fBargs\fR, \fBtype\fR, \fBsender\fR, +\fBhasMedia\fR, methods \fBis()\fR, \fBreply()\fR, \fBdownloadMedia()\fR. +.TP +.B api +Interaction methods including \fBsend()\fR, \fBsendVideo()\fR, \fBsendAudio()\fR, +\fBsendImage()\fR, \fBsendSticker()\fR, \fBgetPlugin()\fR, and \fBlog\fR methods. +.SH FILES +.TP +.I manybot.conf +Main configuration file. See \fBmanybot.conf(5)\fR. +.TP +.I session/ +Authentication data and WhatsApp session storage. +.TP +.I src/plugins/ +Plugin directory containing all installed plugins. +.TP +.I src/main.js +Bot entry point. +.TP +.I logs/ +Log files directory. +.TP +.I update.log +Update script log output. +.SH ENVIRONMENT +.TP +.B NODE_ENV +Set to \fBproduction\fR to disable development features. +.SH EXIT STATUS +.TP +.B 0 +Success +.TP +.B 1 +General error +.TP +.B 130 +Interrupted by user (Ctrl+C) +.SH EXAMPLES +.SS First run +.nf +$ node ./src/main.js +# Scan QR code with WhatsApp: +# Menu \-> Linked Devices \-> Link a Device +.fi +.SS Get chat IDs +.nf +$ node src/utils/get_id.js +# Send a message in the target chat to see the ID +.fi +.SS Update to latest version +.nf +$ bash ./update +.fi +.SH SECURITY +\(bu Bot runs with same privileges as the user running it +.br +\(bu Session data stored in \fIsession/\fR should be protected (chmod 700) +.br +\(bu CHATS whitelist recommended to limit bot exposure +.br +\(bu No official WhatsApp API keys required or used +.SH SEE ALSO +.BR manybot.conf (5), +.BR manybot-plugin (1), +.BR manyplug (1) +.SH AUTHOR +Written by synt-xerror. +.SH COPYRIGHT +Licensed under GPLv3. See LICENSE file for details. +.br +https://github.com/synt-xerror/manybot +.SH BUGS +Report bugs at: https://github.com/synt-xerror/manybot/issues diff --git a/man/man5/manybot.conf.5 b/man/man5/manybot.conf.5 new file mode 100644 index 0000000..2d5d890 --- /dev/null +++ b/man/man5/manybot.conf.5 @@ -0,0 +1,225 @@ +.TH MANYBOT.CONF 5 "April 2026" "ManyBot 2.4.3" "File Formats" +.SH NAME +manybot.conf \- ManyBot configuration file +.SH SYNOPSIS +.I manybot.conf +.SH DESCRIPTION +The +.I manybot.conf +file configures the ManyBot WhatsApp bot. It uses a simple key-value format +with support for multiline lists. Comments start with \fB#\fR. +.PP +The file must be located in the project root directory, alongside +.IR package.json . +.SH FORMAT +.nf +# Comments start with '#' + +KEY=value +KEY=[item1, item2, item3] +.fi +.SS Key-Value Pairs +Simple configuration values: +.PP +.nf +CLIENT_ID=my_bot +CMD_PREFIX=! +LANGUAGE=en +.fi +.SS Multiline Lists +Arrays spanning multiple lines: +.PP +.nf +CHATS=[ + 123456789@c.us, + 123456789@g.us +] + +PLUGINS=[ + many, + figurinha, + audio, + video +] +.fi +.SH OPTIONS +.SS Core Settings +.TP +.B CLIENT_ID=\fIstring\fR +Unique identifier for the bot session. +.RS +.IP \(bu 2 +Default: \fBbot_permanente\fR +.IP \(bu 2 +Creates a \fIsession/CLIENT_ID/\fR folder for authentication data +.IP \(bu 2 +Changing this starts a new session (requires QR code rescan) +.RE +.TP +.B CMD_PREFIX=\fIcharacter\fR +Character that prefixes all bot commands. +.RS +.IP \(bu 2 +Default: \fB!\fR +.IP \(bu 2 +Example: \fB!\fR makes commands like \fB!video\fR, \fB!audio\fR +.IP \(bu 2 +Changing to \fB.\fR would make commands \fB.video\fR, \fB.audio\fR +.RE +.TP +.B LANGUAGE=\fIcode\fR +Bot interface language. +.RS +.IP \(bu 2 +Default: \fBen\fR (English) +.IP \(bu 2 +Options: \fBen\fR, \fBpt\fR (Portuguese), \fBes\fR (Spanish) +.IP \(bu 2 +Fallback to English if selected language not found +.RE +.SS Chat Settings +.TP +.B CHATS=[\fIid1\fR, \fIid2\fR, ...] +Whitelist of chat IDs where the bot responds. +.RS +.IP \(bu 2 +Default: \fB[]\fR (empty = respond to all chats) +.IP \(bu 2 +Private chat format: \fBnumber@c.us\fR +.IP \(bu 2 +Group format: \fBnumber@g.us\fR or \fBnumber-number@g.us\fR +.IP \(bu 2 +Use \fBnode src/utils/get_id.js\fR to discover chat IDs +.RE +.SS Plugin Settings +.TP +.B PLUGINS=[\fIname1\fR, \fIname2\fR, ...] +List of plugins to load at startup. +.RS +.IP \(bu 2 +Default: \fB[]\fR (no plugins loaded) +.IP \(bu 2 +Each name must match a folder in \fIsrc/plugins/\fR +.IP \(bu 2 +Order matters: plugins load in listed order +.IP \(bu 2 +Comment out or remove to disable without deleting files +.RE +.SS Built-in Plugins +.TP +.B many +Lists all available commands. Required for \fB!many\fR to work. +.TP +.B figurinha +Converts images/GIFs/videos to WhatsApp stickers. +.TP +.B video +Downloads videos from URLs. +.TP +.B audio +Downloads audio from videos as voice messages. +.TP +.B adivinha\(,c\(oao +Guessing game (1-100). +.TP +.B forca +Hangman game. +.TP +.B obrigado +Responds to thank-you messages. +.SH CUSTOM SETTINGS +You can add any custom key-value pairs for use by plugins: +.PP +.nf +# In manybot.conf +ADMIN_NUMBER=5511999999999@c.us +API_KEY=your_secret_key +MAX_DOWNLOAD_SIZE=50MB +.fi +.PP +Access in plugins: +.nf +import { ADMIN_NUMBER, API_KEY, MAX_DOWNLOAD_SIZE } from "../../config.js"; +.fi +.SH EXAMPLES +.SS Minimal Configuration +.nf +# Basic bot setup +CLIENT_ID=my_bot +CMD_PREFIX=! +LANGUAGE=en + +PLUGINS=[ + many +] +.fi +.SS Production Configuration +.nf +# Production bot with whitelist +CLIENT_ID=bot_prod +CMD_PREFIX=/ +LANGUAGE=pt + +CHATS=[ + 5511999999999@c.us, + 5511888888888-123456789@g.us +] + +PLUGINS=[ + many, + figurinha, + video, + audio, + obrigado +] + +# Custom settings +ADMIN_NUMBER=5511999999999@c.us +LOG_LEVEL=info +.fi +.SS Development Configuration +.nf +# Debug/development setup +CLIENT_ID=bot_dev +CMD_PREFIX=! +LANGUAGE=en + +# Respond to all chats +CHATS=[] + +# All plugins for testing +PLUGINS=[ + many, + figurinha, + video, + audio, + adivinha\(,c\(oao, + forca, + obrigado +] +.fi +.SH FILES +.TP +.I manybot.conf +Main configuration file (must be created by user) +.TP +.I manybot.conf.example +Example configuration with documentation comments +.SH NOTES +\(bu Keys are case-sensitive +.br +\(bu Values are read as strings unless they're list syntax +.br +\(bu Inline comments supported: \fBKEY=value # comment\fR +.br +\(bu Multiline lists must end with \fB]\fR on its own line or last item line +.br +\(bu Whitespace in values is trimmed +.br +\(u Missing optional values use built-in defaults +.SH SEE ALSO +.BR manybot (1), +.BR manybot-plugin (1), +.BR manyplug (1) +.SH AUTHOR +Written by synt-xerror. diff --git a/manybot.conf.example b/manybot.conf.example new file mode 100644 index 0000000..0a8d4d1 --- /dev/null +++ b/manybot.conf.example @@ -0,0 +1,30 @@ +# ManyBot Configuration File +# Copy this file to manybot.conf and customize as needed + +# Bot identification +CLIENT_ID=meu_bot + +# Command prefix (default: !) +CMD_PREFIX=! + +# Language setting (en, pt, es) +# Default: en (English) +LANGUAGE=en + +# Allowed chats (leave empty for all chats) +# Format: chatId1@g.us, chatId2@g.us for groups +# Format: number@c.us for private chats +CHATS=[ +] + +# Active plugins +PLUGINS=[ +many, +figurinha, +audio, +video, +adivinhação, +forca, +obrigado, +a +] diff --git a/package-lock.json b/package-lock.json index 9b91d61..358ba90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,9 @@ "qrcode-terminal": "^0.12.0", "wa-sticker-formatter": "^4.4.4", "whatsapp-web.js": "^1.24.0" + }, + "devDependencies": { + "conventional-changelog-cli": "^5.0.0" } }, "node_modules/@babel/code-frame": { @@ -39,6 +42,33 @@ "node": ">=6.9.0" } }, + "node_modules/@conventional-changelog/git-client": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@conventional-changelog/git-client/-/git-client-2.6.0.tgz", + "integrity": "sha512-T+uPDciKf0/ioNNDpMGc8FDsehJClZP0yR3Q5MN6wE/Y/1QZ7F+80OgznnTCOlMEG4AV0LvH2UJi3C/nBnaBUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/child-process-utils": "^1.0.0", + "@simple-libs/stream-utils": "^1.2.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "conventional-commits-filter": "^5.0.0", + "conventional-commits-parser": "^6.3.0" + }, + "peerDependenciesMeta": { + "conventional-commits-filter": { + "optional": true + }, + "conventional-commits-parser": { + "optional": true + } + } + }, "node_modules/@gar/promise-retry": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@gar/promise-retry/-/promise-retry-1.0.2.tgz", @@ -51,6 +81,16 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/@hutson/parse-repository-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-5.0.0.tgz", + "integrity": "sha512-e5+YUKENATs1JgYHMzTr2MW/NDcXGfYFAuOQU8gJgF/kEh4EqKgfGrfLI67bMD4tbhZVlkigz/9YYwWcbOFthg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", @@ -127,6 +167,35 @@ "node": ">=18" } }, + "node_modules/@simple-libs/child-process-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@simple-libs/child-process-utils/-/child-process-utils-1.0.2.tgz", + "integrity": "sha512-/4R8QKnd/8agJynkNdJmNw2MBxuFTRcNFnE5Sg/G+jkSsV8/UBgULMzhizWWW42p8L5H7flImV2ATi79Ove2Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/stream-utils": "^1.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, + "node_modules/@simple-libs/stream-utils": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-1.2.0.tgz", + "integrity": "sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://ko-fi.com/dangreen" + } + }, "node_modules/@tokenizer/token": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", @@ -149,6 +218,13 @@ "undici-types": "~7.18.0" } }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/yauzl": { "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", @@ -180,6 +256,13 @@ "node": ">=6.5" } }, + "node_modules/add-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", + "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", + "dev": true, + "license": "MIT" + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -293,6 +376,13 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true, + "license": "MIT" + }, "node_modules/ast-types": { "version": "0.13.4", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", @@ -746,6 +836,17 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, "node_modules/compress-commons": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", @@ -769,6 +870,227 @@ "license": "MIT", "optional": true }, + "node_modules/conventional-changelog": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-6.0.0.tgz", + "integrity": "sha512-tuUH8H/19VjtD9Ig7l6TQRh+Z0Yt0NZ6w/cCkkyzUbGQTnUEmKfGtkC9gGfVgCfOL1Rzno5NgNF4KY8vR+Jo3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^8.0.0", + "conventional-changelog-atom": "^5.0.0", + "conventional-changelog-codemirror": "^5.0.0", + "conventional-changelog-conventionalcommits": "^8.0.0", + "conventional-changelog-core": "^8.0.0", + "conventional-changelog-ember": "^5.0.0", + "conventional-changelog-eslint": "^6.0.0", + "conventional-changelog-express": "^5.0.0", + "conventional-changelog-jquery": "^6.0.0", + "conventional-changelog-jshint": "^5.0.0", + "conventional-changelog-preset-loader": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-angular": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-8.3.0.tgz", + "integrity": "sha512-DOuBwYSqWzfwuRByY9O4oOIvDlkUCTDzfbOgcSbkY+imXXj+4tmrEFao3K+FxemClYfYnZzsvudbwrhje9VHDA==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-atom": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-5.1.0.tgz", + "integrity": "sha512-fw7GpI9jHNCWGBnTsPRI452ypQbNupGwsjrXfozvRNE0c92pJRpoj9rXfzDKUYJcsmk0H4XKaQjhjelwI9z27w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-cli": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-cli/-/conventional-changelog-cli-5.0.0.tgz", + "integrity": "sha512-9Y8fucJe18/6ef6ZlyIlT2YQUbczvoQZZuYmDLaGvcSBP+M6h+LAvf7ON7waRxKJemcCII8Yqu5/8HEfskTxJQ==", + "deprecated": "This package is no longer maintained. Please use the conventional-changelog package instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "add-stream": "^1.0.0", + "conventional-changelog": "^6.0.0", + "meow": "^13.0.0", + "tempfile": "^5.0.0" + }, + "bin": { + "conventional-changelog": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-codemirror": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-5.1.0.tgz", + "integrity": "sha512-iXhy63YczB+yWA9DrsYbquSYLvWKsK9M3WC+xQPEm8cOn4oXzKpmTp2uH3qi7+i10oTcGJTvq9lsBpZmMADaNg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-8.0.0.tgz", + "integrity": "sha512-eOvlTO6OcySPyyyk8pKz2dP4jjElYunj9hn9/s0OB+gapTO8zwS9UQWrZ1pmF2hFs3vw1xhonOLGcGjy/zgsuA==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-core": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-8.0.0.tgz", + "integrity": "sha512-EATUx5y9xewpEe10UEGNpbSHRC6cVZgO+hXQjofMqpy+gFIrcGvH3Fl6yk2VFKh7m+ffenup2N7SZJYpyD9evw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hutson/parse-repository-url": "^5.0.0", + "add-stream": "^1.0.0", + "conventional-changelog-writer": "^8.0.0", + "conventional-commits-parser": "^6.0.0", + "git-raw-commits": "^5.0.0", + "git-semver-tags": "^8.0.0", + "hosted-git-info": "^7.0.0", + "normalize-package-data": "^6.0.0", + "read-package-up": "^11.0.0", + "read-pkg": "^9.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-ember": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-5.1.0.tgz", + "integrity": "sha512-XNcgGcdJt7wh341BBML0CI8DKpqE5lKD1WahzFHGZFvKTzJr1rZW976cw7beqKLOBbzdrH9ZIkE/s2TfbOuM3g==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-eslint": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-6.1.0.tgz", + "integrity": "sha512-beWr3qzuEMN9gznMWa8PhTVfGkGXoq+XnUzViNXg5KygrgV728ZRqZngz3uPhz5+ayUhPrpNFYqIE0qHWz9NAw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-5.1.0.tgz", + "integrity": "sha512-g/s9eLohrefYTSNQaB6+k0ONbiVx41YOKBbIOIM3ST/NtedAgppCJnrpKXVN9sOmpPkN4vjFwURlfvpEDUjoeg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-jquery": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-6.1.0.tgz", + "integrity": "sha512-/sFhULybhFrMg+qc8MHHHSj7kTVMfx5C7rSM6Z9EjduVoAQJdGRq/wpv/SWPMQ+KPNSYHqDLwm/x2Z5hOcYvqQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-jshint": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-5.2.0.tgz", + "integrity": "sha512-OaatyvHXP1fjI7Mx0b1IkmhbhTsVHsytnsQSkOj4rhGbFMoTcfvbwm/vAtCzRMXOxojK1EDMBBmBj1pM9KNy/Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-preset-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-5.0.0.tgz", + "integrity": "sha512-SetDSntXLk8Jh1NOAl1Gu5uLiCNSYenB5tm0YVeZKePRIgDW9lQImromTwLa3c/Gae298tsgOM+/CYT9XAl0NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-8.4.0.tgz", + "integrity": "sha512-HHBFkk1EECxxmCi4CTu091iuDpQv5/OavuCUAuZmrkWpmYfyD816nom1CvtfXJ/uYfAAjavgHvXHX291tSLK8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/stream-utils": "^1.2.0", + "conventional-commits-filter": "^5.0.0", + "handlebars": "^4.7.7", + "meow": "^13.0.0", + "semver": "^7.5.2" + }, + "bin": { + "conventional-changelog-writer": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-commits-filter": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-5.0.0.tgz", + "integrity": "sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/conventional-commits-parser": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-6.3.0.tgz", + "integrity": "sha512-RfOq/Cqy9xV9bOA8N+ZH6DlrDR+5S3Mi0B5kACEjESpE+AviIpAptx9a9cFpWCCvgRtWT+0BbUw+e1BZfts9jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@simple-libs/stream-utils": "^1.2.0", + "meow": "^13.0.0" + }, + "bin": { + "conventional-commits-parser": "dist/cli/index.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -908,6 +1230,19 @@ "integrity": "sha512-nv7iKtNZQshSW2hKzYNr46nM/Cfh5SEvE2oV0/SEGgc9XupIY5ggf84Cz8eJIkBce7S3bmTAauFD6aysMpnqsQ==", "license": "BSD-3-Clause" }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -1156,6 +1491,19 @@ "url": "https://github.com/sindresorhus/file-type?sponsor=1" } }, + "node_modules/find-up-simple": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.1.tgz", + "integrity": "sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/fluent-ffmpeg": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.3.tgz", @@ -1289,6 +1637,40 @@ "node": ">= 14" } }, + "node_modules/git-raw-commits": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-5.0.1.tgz", + "integrity": "sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@conventional-changelog/git-client": "^2.6.0", + "meow": "^13.0.0" + }, + "bin": { + "git-raw-commits": "src/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/git-semver-tags": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-8.0.1.tgz", + "integrity": "sha512-zMbamckSNdlT4U48IMFa2Cn6FTzM+2yF6/gEmStPJI8PiLxd/bT6dw10+mc6u5Qe4fhrc/y9nU290FWjQhAV7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@conventional-changelog/git-client": "^2.6.0", + "meow": "^13.0.0" + }, + "bin": { + "git-semver-tags": "src/cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -1323,6 +1705,48 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, "node_modules/http-cache-semantics": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", @@ -1432,6 +1856,19 @@ "node": ">=0.8.19" } }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1480,6 +1917,16 @@ "node": ">=8" } }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -1654,6 +2101,19 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/meow": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", + "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mime": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", @@ -1868,6 +2328,13 @@ "node": ">= 0.6" } }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, "node_modules/netmask": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", @@ -1984,6 +2451,21 @@ "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -2334,6 +2816,62 @@ "rc": "cli.js" } }, + "node_modules/read-package-up": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", + "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up-simple": "^1.0.0", + "read-pkg": "^9.0.0", + "type-fest": "^4.6.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -2673,12 +3211,48 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "devOptional": true, "license": "BSD-3-Clause", - "optional": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/ssri": { "version": "13.0.1", "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.1.tgz", @@ -2830,6 +3404,32 @@ "streamx": "^2.12.5" } }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/tempfile": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tempfile/-/tempfile-5.0.0.tgz", + "integrity": "sha512-bX655WZI/F7EoTDw9JvQURqAXiPHi8o8+yFxPF2lWYyz1aHnmMRuXWqL6YB6GmeO0o4DIYWHLgGNi/X64T+X4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "temp-dir": "^3.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/text-decoder": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", @@ -2906,12 +3506,39 @@ "node": "*" } }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typed-query-selector": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.1.tgz", "integrity": "sha512-uzR+FzI8qrUEIu96oaeBJmd9E7CFEiQ3goA5qCVgc4s5llSubcfGHq9yUstZx/k4s9dXHVKsE35YWoFyvEqEHA==", "license": "MIT" }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/undici-types": { "version": "7.18.2", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", @@ -2919,6 +3546,19 @@ "license": "MIT", "optional": true }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unique-filename": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-5.0.0.tgz", @@ -3010,6 +3650,17 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "node_modules/wa-sticker-formatter": { "version": "4.4.4", "resolved": "https://registry.npmjs.org/wa-sticker-formatter/-/wa-sticker-formatter-4.4.4.tgz", @@ -3087,6 +3738,13 @@ "which": "bin/which" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index 806ddfd..a2e3de4 100755 --- a/package.json +++ b/package.json @@ -9,5 +9,11 @@ "qrcode-terminal": "^0.12.0", "wa-sticker-formatter": "^4.4.4", "whatsapp-web.js": "^1.24.0" + }, + "devDependencies": { + "conventional-changelog-cli": "^5.0.0" + }, + "scripts": { + "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0" } }