I recently discovered a decentralized chat app called Simplex. I found it while reading a Hacker News article about privacy-focused tools. Actually, it was the one about hiding your phone in a tub of lentils, anyway, after giving it a try, I believe Simplex is one of the best decentralized messaging apps I’ve encountered in a long time.
Delightfully Usable
One of the standout features of Simplex, beyond its decentralized and privacy-focused nature, is that it just works. This isn’t something you can say about many decentralized tools. Simplex is straightforward enough for non-technical users, which is rare in this space. You can install it from the App Store, share a QR code with your contacts, and you’re good. No complicated setup, no firewall headaches, and no fiddling with encryption keys. It works like any other messaging app, which is exactly what I want.
Simplex has an app available on all major app stores. It also has a CLI version for desktop power users. You create a QR code for your identity, share it with your contacts, and you’re good to go. You can create multiple identities, even throwaway incognito ones. The messenger functions like any other modern app, with support for voice and video calls, file sharing, image sharing, and more.
There’s also a CLI version of the app for those who prefer using the terminal. This is the version I’ll focus on for the rest of this article.
Extensible
I’m impressed by how easy it is to build scripts and bots for Simplex. There’s no need for API registration tokens or complicated SDKs. You just download a single executable from their GitHub releases page, and in a few minutes, you can have a functioning chatbot.
The simplicity here reminded me of the days I spent writing IRC scripts and bots, but with a much better UX and feature set on the client-side.
Building Bots
To build Simplex bots, you use the CLI app as a gateway. You can download the CLI executable from their GitHub releases page. If you’ve ever used a terminal-based IRC client like Irssi, that’s what you can expect here, also.
The CLI includes a -p
flag that allows you to run a WebSocket server. This server listens for CLI commands, enabling you to automate tasks and build bots using any programming language that supports WebSocket clients. While traditional TCP socket support would be nice, WebSockets do the job for now. I hope future versions support plain TCP sockets, as this would allow even more languages to be supported.
Getting Started with Bot Development
To start, download the Simplex CLI executable and run it with the -p
flag:
./simplex-chat -p 3030
This command starts a WebSocket server on your localhost. You can connect to it and send CLI commands mostly like you would in the CLI itself. Here’s a basic TypeScript example to connect to the WebSocket server:
import WebSocket from "ws";
const ws = new WebSocket("ws://localhost:3030");
To send a message in the CLI to the user Rick
, you would type @Rick Hello, world!
. You can replicate this in TypeScript:
const message = "@Rick Hello, world!";
Simplex requires you to send messages as JSON objects with a correlation ID. The correlation ID is a string and it is required on all messages. This ID helps you match responses with the commands you sent. Every response received will contain the correlation ID of the command that initiated the request:
const command = {
corrId: `id${Math.round(Math.random() * 999999)}`,
cmd: message,
};
When the WebSocket connection opens, you send your message by conversting the command object to JSON:
ws.on("open", () => {
const json = JSON.stringify(command);
ws.send(json);
});
To handle incoming messages, you can listen for the message
event:
ws.on("message", (data) => console.log(data.toString()));
Keep in mind that responses will come in as a buffer, so you’ll need to convert them to a string before working on them.
The Full Script
When you run this script, the CLI will return a JSON response object which is printed to the screen via console.log
.
I will leave it as an exercise for the reader to learn about the response schema. Happy hacking!
import WebSocket from "ws";
const ws = new WebSocket("ws://localhost:3030");
const message = "@Rick Hello, world!";
const command = {
corrId: `id${Math.round(Math.random() * 999999)}`,
cmd: message,
};
ws.on("open", () => {
const json = JSON.stringify(command);
ws.send(json);
});
ws.on("message", (data) => console.log(data.toString()));