A fast and modular WhatsApp bot framework for Rust with procedural macro commands and auto registration. Built with Rust and whatsapp-rust library.
- High-performance asynchronous runtime powered by Tokio
- Native Rust performance with a low memory footprint
- Native Rust performance
- Procedural macro command system
- Auto-generated project scaffolding
- Multi-session support (run one or multiple WhatsApp accounts)
git clone https://github.com/arsa0x/viola.git
cd viola
cargo build --releaseThe compiled binary will be located at:
target/release/violaYou can place the binary anywhere or add it to your $PATH.
Create your first WhatsApp session:
viola session newOr specify a custom name:
viola session new personalThis creates a directory for the session containing its own configuration and authentication data.
Viola stores all session data in your operating system's standard configuration directory using the directories crate.
The layout looks like this:
viola/
└── sessions
├── personal
│ ├── config
│ └── store.redb
└── default
├── config
└── store.redb
Typical locations are:
| Platform | Location |
|---|---|
| Linux | ~/.config/viola/ |
| macOS | ~/Library/Application Support/viola/ |
| Windows | %APPDATA%\viola\ |
Each session has its own config file.
For example:
~/.config/viola/
└── sessions
├── personal
│ ├── config
│ └── store.redb
└── default
├── config
└── store.redbEdit the configuration before starting the bot.
Run the only available session:
violaor
viola runRun a specific session:
viola run --session personalRun every session simultaneously:
viola run --allOn the first launch, Viola will display a QR code for pairing with WhatsApp.
Create a session:
viola session new [name]List all sessions:
viola session listRemove a session:
viola session remove <name>There are two ways to register a command: the procedural macro (recommended for most commands), or manual registration via linkme::distributed_slice when you need direct control over the Command struct.
use viola_core::Context;
use viola_macros::command;
use whatsapp_rust::anyhow;
#[command(
triggers = [""], // required
category = "", // required
owner_only = false, // optional
group_only = false, // optional
description = "", // optional
help = "" // optional
)]
async fn command_name(ctx: Context) -> anyhow::Result<()> {
ctx.send().text("hi there!").quoted().await?;
Ok(())
}use linkme::distributed_slice;
use viola_core::{COMMANDS, Command, Context};
use whatsapp_rust::anyhow;
#[distributed_slice(COMMANDS)]
static CMD: Command = Command {
name: "",
triggers: &[""],
category: "",
owner_only: false,
group_only: false,
help: None,
description: None,
execute: |ctx: Context| Box::pin(execute(ctx)),
};
async fn execute(ctx: Context) -> anyhow::Result<()> {
ctx.send().text("manual").await
}Both approaches register into the same COMMANDS distributed slice, so commands defined either way are discovered and dispatched identically at runtime — pick whichever fits the command better.
Each session maintains its own configuration file.
Example:
# Multiple prefixes separated by |
prefixes=.|!
# WhatsApp owner numbers separated by |
owners=628123456789|628123456780
# Available modes:
# public | group | owner
mode=public
.
├── src # bot entry point
├── viola_core # command system, context and config
├── viola_macros # procedural macros for command registration
└── viola_command # collection of all bot commands- Message module — builders for sending text, media, reactions, and interactive (buttons/list) messages via
ctx.send().
Licensed under the MIT License. See LICENSE for more information.