chat-system
A multi-protocol async chat crate for Rust. Provides a single unified Messenger trait for IRC, Matrix, Discord, Telegram, Slack, Signal, WhatsApp, Microsoft Teams, Google Chat, iMessage, Webhook, and Console — with full rich-text support for every platform's native format.
The primary way to use this crate is through the generic interface: MessengerConfig is a serde-tagged enum whose protocol field selects the backend at runtime, so the protocol is just a field in your config file rather than a compile-time choice.
Features
| Feature flag | Protocols added | Extra dependencies |
|---|---|---|
| (default) | IRC, Discord, Telegram, Slack, Teams, Google Chat, iMessage, Webhook, Console | none |
matrix |
Matrix (via matrix-sdk) |
matrix-sdk |
whatsapp |
WhatsApp (via wa-rs) |
wa-rs family |
signal-cli |
Signal (via signal-cli subprocess) |
(external binary only) |
full |
All of the above | all optional deps |
Quick Start
[]
= "0.1"
= { = "1", = ["full"] }
Generic interface (recommended)
MessengerConfig deserializes from any serde-compatible source. The protocol field picks the backend; everything else is the same [Messenger] trait regardless of platform.
use ;
use IrcConfig;
async
Loading the config from a file
Because MessengerConfig derives serde::Deserialize, any serde-compatible source works.
TOML (config.toml):
= "discord"
= "my-bot"
= "Bot TOKEN_HERE"
# use ;
let toml_str = read_to_string?;
let config: MessengerConfig = from_str?;
let mut client = new;
client.initialize.await?;
JSON (config.json):
# use ;
let json_str = read_to_string?;
let config: MessengerConfig = from_str?;
let mut client = new;
client.initialize.await?;
Multi-platform with MessengerManager
MessengerManager holds multiple GenericMessenger instances and broadcasts / receives across all of them at once.
use ;
use ;
async
Reactions
// Add / remove a reaction (no-op on platforms that don't support it)
client.add_reaction.await?;
client.remove_reaction.await?;
// Reactions arrive on received messages where the platform populates them
for msg in client.receive_messages.await?
Profile pictures
// Retrieve a user's profile picture URL (None if not supported)
if let Some = client.get_profile_picture.await?
// Update the bot's own profile picture
client.set_profile_picture.await?;
Replies
use SendOptions;
client.send_message_with_options.await?;
Incoming reply messages expose the parent ID via msg.reply_to.
Search
use SearchQuery;
let results = client.search_messages.await?;
for msg in results
Rich Text
use ;
let msg = RichText;
println!; // **Hello**, world! [click](https://example.com)
println!; // <b>Hello</b>, world! <a href="…">click</a>
println!; // *Hello*, world! <https://example.com|click>
println!; // \x02Hello\x02, world! click (https://example.com)
println!; // *Hello*, world! click (https://example.com)
println!; // <b>Hello</b>, world! <a href="…">click</a>
// Parse from Markdown
let rt = from_markdown;
Channel Capabilities
Each ChannelType exposes its feature set so you can decide at runtime what to offer:
use ChannelType;
let caps = Slack.descriptor.capabilities;
println!; // true
println!; // true
for ct in ALL
Markdown Converters
use ;
let html = markdown_to_telegram_html;
// → "<b>bold</b> and <code>code</code>"
let slack = markdown_to_slack;
// → "*bold* <https://x.com|link>"
// Split long messages respecting Telegram's 4096-char limit:
let chunks = chunk_markdown_html;
Server
A server is a named container of listeners. It owns no address, port, or protocol — those belong to the listeners. Different listeners can speak different protocols while feeding into the same handler.
Programmatic (recommended)
use Server;
use IrcListener;
use ChatServer;
async
With TLS (feature tls)
Enable the tls feature to use TlsIrcListener for encrypted connections:
use Server;
use ;
use ChatServer;
use Arc;
async
Config-driven with GenericServer
ServerConfig uses the ListenerConfig trait (via typetag), so listener configs are extensible and can be deserialized from any serde format.
JSON (server.json):
use ;
async
Examples
# Generic interface (recommended starting point — no credentials needed)
# IRC client + server
# Other protocols
Protocol-specific clients
When you need direct access to protocol-specific features, you can construct the concrete type directly. The Messenger trait is still the primary interface:
use IrcMessenger;
use Messenger;
async
IRC TLS
The IRC messenger supports both plaintext and encrypted connections:
| Mode | Port | Setting |
|---|---|---|
| Plaintext | 6667 | .with_tls(false) |
| Encrypted (RFC 7194) | 6697 | .with_tls(true) |
// Plaintext
let mut bot = new.with_tls;
// TLS
let mut bot = new.with_tls;
| Network | Host | Plaintext port | TLS port |
|---|---|---|---|
| Libera.Chat | irc.libera.chat |
6667 | 6697 |
| Freenode | irc.freenode.net |
6667 | 6697 |
| Undernet | irc.undernet.org |
6667 | 6697 |