12 Commits
2.3.2 ... 2.3.7

Author SHA1 Message Date
synt-xerror
ba08626bd8 support to root on get_id and optional chat filtering 2026-03-17 10:20:33 -03:00
synt-xerror
1ce6740a3b support to root on get_id and optional chat filtering 2026-03-17 10:16:24 -03:00
synt-xerror
f0f87dde78 Bump version to 2.3.6 2026-03-17 10:16:21 -03:00
synt-xerror
12a9cf122f adding root support 2026-03-17 08:12:55 -03:00
synt-xerror
48e851fd05 Bump version to 2.3.5 2026-03-17 08:12:52 -03:00
synt-xerror
ae256da596 root support 2026-03-17 08:10:16 -03:00
synt-xerror
92e2ea2337 support for comments on config file 2026-03-16 22:27:07 -03:00
synt-xerror
438e674eff Bump version to 2.3.4 2026-03-16 22:27:01 -03:00
synt-xerror
5b74cf2dc5 new sticker limit 2026-03-16 22:23:35 -03:00
synt-xerror
4f5d937265 Bump version to 2.3.3 2026-03-16 22:23:32 -03:00
synt-xerror
544dc770cd new config file 2026-03-16 21:56:58 -03:00
synt-xerror
5fbe257625 Bump version to 2.3.2 2026-03-16 21:56:55 -03:00
12 changed files with 414 additions and 36 deletions

2
.gitignore vendored
View File

@@ -7,3 +7,5 @@ node_modules/
cookies.txt cookies.txt
bin/ bin/
mychats.txt mychats.txt
manybot.conf
update.log

162
README.md
View File

@@ -1,15 +1,157 @@
![ManyBot Logo](logo.png) ![ManyBot Logo](logo.png)
Criei esse bot para servir um grupo de amigos. Meu foco não é fazer ele funcionar para todo mundo. 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).
Ele é 100% local e gratuito, sem necessidade de APIs burocraticas. Usufrui da biblioteca `whatsapp-web.js`, que permite bastante coisa mesmo sem a API oficial. Algumas funcionalidades desse bot incluem:
- Suporte a múltiplos chats em uma única sessão
Você consegue totalmente clonar esse repoistório e rodar seu próprio ManyBot. A licenca GPLv3 permite que você modifique o que quiser e faça seu próprio bot, mas se for publicar, deve ser open source assim como o ManyBot original. - Comandos de jogos
- Download de mídia via yt-dlp
Algumas funcionalidades desse bot inclui:
- Funciona em multiplos chats em apenas uma única sessão
- Comandos de jogos e download com yt-dlp
- Gerador de figurinhas - Gerador de figurinhas
- Ferramenta para pegar IDs dos chats
- Entre outros
# Screenshots
---
# Requisitos
- Node.js
- NPM
- Sistema Linux ou Windows
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.
# Instalação (Linux)
1. Clone o repositório e entre:
```
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):
```
touch manybot.conf
nano manybot.conf
```
3. Nele você pode configurar algumas coisas do ManyBot. Esse é o arquivo base para que possa modificar:
```
CLIENT_ID=manybot
BOT_PREFIX=🤖 *ManyBot:*
CMD_PREFIX=!
CHATS=[
123456789@c.us,
123456789@g.us
]
```
- **CLIENT_ID:** ID do cliente, serve para identificar sua sessão.
- **BOT_PREFIX:** Prefixo/nome do bot, o que aparece sempre que manda uma mensagem.
- **CMD_PREFIX:** Prefixo do comando, o caractere que você usa para executar um comando (!many, !figurinha).
- **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.
4. Execute o script de instalação:
```
bash ./setup
```
5. Rode o bot pela primeira vez:
```
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:
```
node ./src/main.js
```
## Atualizações
É recomendável sempre ter a versão mais recente do ManyBot. Para isso, temos um utilitário logo na raíz. Para executar:
```
bash ./update
```
## 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.
1. Configurando o diretório
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)
2. Criando o serviço
Abra o arquivo:
```
/etc/systemd/system/manybot.service
```
E cole o seguinte conteúdo:
```
[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:
```
systemctl daemon-reload
```
Inicie o serviço:
```
systemctl start manybot
```
Habilite para que ele seja iniciado junto com o seu sistema (opcional):
```
systemctl enable manybot
```
## Comandos
O ManyBot vem com alguns comandos pré-definidos, sendo esses:
- `video <link>`: baixa um vídeo da internet
- `audio <link>`: também baixa um vídeo, mas manda apenas o áudio
- `figurinha`: gerador de figurinhas
- `adivinhação`: jogo simples de adivnhação de um número entre 1 a 100
O prefixo para ativá-los depende da sua configuração, mas por padrão é o ponto de exclamação (`!`).
# Considerações
ManyBot é distribuído sob a licença GPLv3. Você pode usar, modificar e redistribuir o software conforme os termos da licença.
Saiba mais sobre as permissões lendo o arquivo [[LICENSE]] ou em: https://www.gnu.org/licenses/quick-guide-gplv3.pt-br.html

View File

@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
# development tool # ferramenta de desenvolvimento apenas, pode apagar se quiser
# ./deploy <commit> <branch> <version (if branch=master)> # ./deploy <commit> <branch> <version (if branch=master)>
COMMIT_MSG="$1" COMMIT_MSG="$1"
@@ -12,15 +12,6 @@ if [ -z "$COMMIT_MSG" ] || [ -z "$BRANCH" ]; then
exit 1 exit 1
fi fi
echo "Rewriting config.js"
cat > "src/config.js" << 'EOF'
export const CLIENT_ID = "bot_permanente";
export const BOT_PREFIX = "🤖 *ManyBot:* ";
export const CHATS = [
// coloque os chats que quer aqui
];
EOF
# mudar para a branch # mudar para a branch
git checkout $BRANCH || { echo "Error ao change to $BRANCH"; exit 1; } git checkout $BRANCH || { echo "Error ao change to $BRANCH"; exit 1; }

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "whatsapp-bot", "name": "whatsapp-bot",
"version": "2.3.1", "version": "2.3.6",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "whatsapp-bot", "name": "whatsapp-bot",
"version": "2.3.1", "version": "2.3.6",
"dependencies": { "dependencies": {
"node-addon-api": "^7", "node-addon-api": "^7",
"node-gyp": "^12.2.0", "node-gyp": "^12.2.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "manybot", "name": "manybot",
"version": "2.3.1", "version": "2.3.6",
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"node-addon-api": "^7", "node-addon-api": "^7",

28
setup
View File

@@ -1,6 +1,9 @@
#!/bin/bash #!/bin/bash
set -e set -e
# Salvando diretório para evitar problemas
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# ------------------------ # ------------------------
# Cores # Cores
# ------------------------ # ------------------------
@@ -48,8 +51,6 @@ cat << "EOF"
website: www.mlplovers.com.br/manybot website: www.mlplovers.com.br/manybot
repo: github.com/synt-xerror/manybot repo: github.com/synt-xerror/manybot
A Amizade é Mágica!
EOF EOF
echo -e "${RESET}" echo -e "${RESET}"
} }
@@ -190,6 +191,27 @@ fi
log_debug "Total de arquivos para baixar: ${#files[@]}" log_debug "Total de arquivos para baixar: ${#files[@]}"
# ------------------------
# Config
# ------------------------
log_info "Criando arquivo de configuração"
cat > "src/config.js" << 'EOF'
export const CLIENT_ID = "bot_permanente";
export const BOT_PREFIX = "🤖 *ManyBot:* ";
export const CMD_PREFIX = "!";
export const CHATS = [
// coloque os chats que quer aqui
];
EOF
if [[ -f src/config.js ]]; then
log_ok "Arquivo de configuração criado"
else
log_error "Erro durante criação do arquivo de configuração"
fi
# ------------------------ # ------------------------
# Download # Download
# ------------------------ # ------------------------
@@ -201,4 +223,4 @@ for file in "${files[@]}"; do
download_file "$url" "$dest" download_file "$url" "$dest"
done done
log_ok "Setup concluído com sucesso" log_ok "Setup concluído com sucesso.\nRode sempre na raíz: 'node src/main.js' (ou equivalente) para rodar o bot."

View File

@@ -16,7 +16,15 @@ logger.info(isTermux
// ── Instância ───────────────────────────────────────────────── // ── Instância ─────────────────────────────────────────────────
export const client = new Client({ export const client = new Client({
authStrategy: new LocalAuth({ clientId: CLIENT_ID }), authStrategy: new LocalAuth({ clientId: CLIENT_ID }),
puppeteer: { headless: true, ...resolvePuppeteerConfig() }, puppeteer: {
headless: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
...(resolvePuppeteerConfig().args || [])
],
...resolvePuppeteerConfig()
},
}); });
// ── Eventos ─────────────────────────────────────────────────── // ── Eventos ───────────────────────────────────────────────────

View File

@@ -21,7 +21,7 @@ const DOWNLOADS_DIR = path.resolve("downloads");
const FFMPEG = os.platform() === "win32" ? ".\\bin\\ffmpeg.exe" : "./bin/ffmpeg"; const FFMPEG = os.platform() === "win32" ? ".\\bin\\ffmpeg.exe" : "./bin/ffmpeg";
const MAX_STICKER_SIZE = 900 * 1024; const MAX_STICKER_SIZE = 900 * 1024;
const SESSION_TIMEOUT = 2 * 60 * 1000; const SESSION_TIMEOUT = 2 * 60 * 1000;
const MAX_MEDIA = 10; const MAX_MEDIA = 30;
// ── Helpers ─────────────────────────────────────────────────── // ── Helpers ───────────────────────────────────────────────────
function ensureDownloadsDir() { function ensureDownloadsDir() {

View File

@@ -1,5 +1,64 @@
export const CLIENT_ID = "bot_permanente"; import fs from "fs";
export const BOT_PREFIX = "🤖 *ManyBot:* ";
export const CHATS = [ function parseConf(raw) {
// coloque os chats que quer aqui // Remove comentários inline e de linha inteira, preservando estrutura
]; const lines = raw.split("\n");
const cleaned = [];
let insideList = false;
let buffer = "";
for (let line of lines) {
// Remove comentário inline (# ...) — mas só fora de strings
line = line.replace(/#.*$/, "").trim();
if (!line) continue;
if (!insideList) {
if (line.includes("=[") && !line.includes("]")) {
// Início de lista multilinha
insideList = true;
buffer = line;
} else {
cleaned.push(line);
}
} else {
buffer += line;
if (line.includes("]")) {
// Fim da lista
insideList = false;
cleaned.push(buffer);
buffer = "";
}
}
}
// Parseia cada linha chave=valor
const result = {};
for (const line of cleaned) {
const eqIdx = line.indexOf("=");
if (eqIdx === -1) continue;
const key = line.slice(0, eqIdx).trim();
const raw = line.slice(eqIdx + 1).trim();
if (raw.startsWith("[") && raw.endsWith("]")) {
result[key] = raw
.slice(1, -1)
.split(",")
.map(x => x.trim())
.filter(Boolean);
} else {
result[key] = raw;
}
}
return result;
}
const raw = fs.readFileSync("manybot.conf", "utf8");
const config = parseConf(raw);
export const CLIENT_ID = config.CLIENT_ID ?? "bot_permanente";
export const BOT_PREFIX = config.BOT_PREFIX ?? "🤖 *ManyBot:* ";
export const CMD_PREFIX = config.CMD_PREFIX ?? "!";
export const CHATS = config.CHATS ?? [];

View File

@@ -16,7 +16,11 @@ export async function handleMessage(msg) {
const chat = await msg.getChat(); const chat = await msg.getChat();
const chatId = getChatId(chat); const chatId = getChatId(chat);
// se CHATS estiver vazio, ele pega todos os chats.
// se nao, ele pega os que estão na lista.
if (CHATS.length > 0) {
if (!CHATS.includes(chatId)) return; if (!CHATS.includes(chatId)) return;
}
const ctx = await buildMessageContext(msg, chat, BOT_PREFIX); const ctx = await buildMessageContext(msg, chat, BOT_PREFIX);
logger.msg(ctx); logger.msg(ctx);

View File

@@ -5,6 +5,7 @@
import pkg from "whatsapp-web.js"; import pkg from "whatsapp-web.js";
import qrcode from "qrcode-terminal"; import qrcode from "qrcode-terminal";
import { CLIENT_ID } from "../config.js"; import { CLIENT_ID } from "../config.js";
import { resolvePuppeteerConfig } from "../client/environment.js";
const { Client, LocalAuth } = pkg; const { Client, LocalAuth } = pkg;
@@ -17,7 +18,15 @@ if (!arg) {
const client = new Client({ const client = new Client({
authStrategy: new LocalAuth({ clientId: CLIENT_ID }), authStrategy: new LocalAuth({ clientId: CLIENT_ID }),
puppeteer: { headless: true }, puppeteer: {
headless: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
...(resolvePuppeteerConfig().args || [])
],
...resolvePuppeteerConfig()
},
}); });
client.on("qr", (qr) => { client.on("qr", (qr) => {

141
update Normal file
View File

@@ -0,0 +1,141 @@
#!/bin/bash
# ==============================================================================
# update.sh — Script de atualização segura do ManyBot
# ==============================================================================
set -euo pipefail
# ------------------------------------------------------------------------------
# Configuração
# ------------------------------------------------------------------------------
dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
tmp_dir="$dir/tmp"
log_file="$dir/update.log"
config_items=(".wwebjs_auth" ".wwebjs_cache" "node_modules")
# ------------------------------------------------------------------------------
# Logging
# ------------------------------------------------------------------------------
log() {
local level="$1"; shift
local msg="$*"
local timestamp
timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
echo "[$timestamp] [$level] $msg" | tee -a "$log_file"
}
# ------------------------------------------------------------------------------
# Trap: executa em caso de qualquer erro
# ------------------------------------------------------------------------------
cleanup_on_error() {
local exit_code=$?
log "ERROR" "Falha durante a atualização (código: $exit_code)."
if [ -d "$tmp_dir" ] && [ "$(ls -A "$tmp_dir" 2>/dev/null)" ]; then
log "WARN" "Arquivos de configuração preservados em: $tmp_dir"
log "WARN" "Restaure manualmente com: mv $tmp_dir/* $dir/"
fi
exit $exit_code
}
trap cleanup_on_error ERR
# ------------------------------------------------------------------------------
# Validações iniciais
# ------------------------------------------------------------------------------
log "INFO" "Iniciando atualização do ManyBot..."
# Verifica dependências obrigatórias
for cmd in git npm; do
if ! command -v "$cmd" &>/dev/null; then
log "ERROR" "Dependência ausente: '$cmd' não encontrado no PATH."
exit 1
fi
done
# Verifica se está em um repositório git
if ! git -C "$dir" rev-parse --is-inside-work-tree &>/dev/null; then
log "ERROR" "'$dir' não é um repositório git."
exit 1
fi
# Exibe o remote para transparência
remote_url=$(git -C "$dir" remote get-url origin 2>/dev/null || echo "(não definido)")
log "INFO" "Remote: $remote_url"
# Verifica se há alterações locais não commitadas
if ! git -C "$dir" diff --quiet || ! git -C "$dir" diff --cached --quiet; then
log "WARN" "Existem alterações locais não commitadas. Elas serão descartadas pelo reset."
read -r -p "Deseja continuar mesmo assim? [s/N] " confirm
confirm="${confirm:-N}"
if [[ ! "$confirm" =~ ^[sS]$ ]]; then
log "INFO" "Atualização cancelada pelo usuário."
exit 0
fi
fi
# ------------------------------------------------------------------------------
# Backup dos arquivos de configuração
# ------------------------------------------------------------------------------
rm -rf "$tmp_dir"
mkdir -p "$tmp_dir"
backed_up=()
for item in "${config_items[@]}"; do
src="$dir/$item"
if [ -e "$src" ]; then
mv "$src" "$tmp_dir/"
backed_up+=("$item")
log "INFO" "Backup: $item → tmp/"
fi
done
# ------------------------------------------------------------------------------
# Atualização do repositório
# ------------------------------------------------------------------------------
log "INFO" "Buscando atualizações..."
git -C "$dir" fetch origin
branch=$(git -C "$dir" rev-parse --abbrev-ref HEAD)
log "INFO" "Branch atual: $branch"
# Registra o hash antes e depois para auditoria
hash_before=$(git -C "$dir" rev-parse HEAD)
git -C "$dir" reset --hard "origin/$branch"
hash_after=$(git -C "$dir" rev-parse HEAD)
if [ "$hash_before" = "$hash_after" ]; then
log "INFO" "Repositório já estava atualizado (sem mudanças)."
else
log "INFO" "Atualizado: $hash_before → $hash_after"
fi
# ------------------------------------------------------------------------------
# Instalação de dependências
# ------------------------------------------------------------------------------
log "INFO" "Instalando dependências..."
npm ci --omit=dev 2>&1 | tee -a "$log_file"
# ------------------------------------------------------------------------------
# Restauração dos arquivos de configuração
# ------------------------------------------------------------------------------
if [ ${#backed_up[@]} -gt 0 ]; then
for item in "${backed_up[@]}"; do
src="$tmp_dir/$item"
dst="$dir/$item"
if [ -e "$src" ]; then
# Remove o que npm possa ter criado (ex: node_modules)
rm -rf "$dst"
mv "$src" "$dst"
log "INFO" "Restaurado: $item"
else
log "WARN" "Item esperado no backup não encontrado: $item"
fi
done
fi
# Limpa tmp apenas após restauração bem-sucedida
rm -rf "$tmp_dir"
# ------------------------------------------------------------------------------
# Concluído
# ------------------------------------------------------------------------------
log "INFO" "ManyBot atualizado com sucesso."