Expand description
For first-time users it’s recommended to get hands-on experience by running some example bots on GitHub before writing their own.
§How to write a SimpleX bot?
First of all, you must use a tokio runtime. The current simploxide implementation heavily
depends on it.
Secondly, it’s recommended to use simploxide_client::prelude::* if you don’t want your import
section to explode. The prelude reexports all top-level types required for sending requests,
destructuring responses and matching events, you’ll still need to manually import intermediary
types and there are a lot of them, the prelude just greately reduces the amount of the same
imports per file.
§Now to the bot
The most common bot structure will look like this:
use simploxide_client::prelude::*;
use futures::stream::TryStreamExt;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// Init websocket connection with SimpleX daemon
let (client, mut events) = simploxide_client::connect("ws://127.0.0.1:5225").await?;
// Pre-query and validate stuff
client.do_some_initialization().await?;
// Implement event reactor
while let Some(ev) = events.try_next().await? {
match ev {
Event::SomeEvent1(SomeEvent1 { data }) => {
client.process_event1(data).await?;
}
Event::SomeEvent2(SomeEvent2 { data }) => {
client.process_event2(data).await?;
break;
}
_ => (), // Ignore events you're not interested in.
}
}
// (Optional) some cleanup
Ok(())
}- Initialize a web socket connection with the simplex-chat daemon. You can run simplex-chat as
a daemon with
simplex-chat -p <port>command. - Prequery some info and do some validations required for your bot to work: this typically includes getting or creating the bot address, switching to the right bot user, etc
- Start an event reactor loop and process the events.
Everything looks simple and trivial but the reactor part in the example above is terribly inefficient. It reacts on events sequentially waiting for client to respond to the first event before processing the second. This can be fine if your bot doesn’t need to operate under a heavy-load, such reactor would also be useful during the development because it is trivial to debug however, for production it’s advisable to enable full asynchronous multi-threaded processing that can be achieved by simply moving the event handlers into tokio tasks:
// Implement event reactor
while let Some(ev) = events.try_next().await? {
let client = client.clone();
match ev {
Event::SomeEvent1(SomeEvent1 { data }) => {
tokio::spawn(async move {
client.process_event1(data).await?;
});
}
Event::SomeEvent2(SomeEvent2 { data }) => {
tokio::spawn(async move {
client.process_event2(data).await?;
client.disconnect();
});
}
_ => (), // Ignore events you're not interested in.
}
}Note, that we can’t terminate the event loop with a break statetement because the event is
being processed asynchronously in its own task. You can call client.disconnect() in this case
to initiate a graceful shutdown which will eventually end the event stream, but even with
strong guarantees the graceful shutdown provides it cannot guarantee that events, which
occurred before the shutdown, will be processed to completion as tasks may need to send several
requests to complete successfully, so if this is important for you application to process
events atomically you should use primitives like tokio channels and notifies to break the loop
without dropping the web socket connection.
§A simpler use case
Some applications may not need to react on events, they can act like scripts, or like remote controllers for a SimpleX chat instance. In this case, drop the event stream immediately to prevent events from buffering and leaking memory:
// Init websocket connection with SimpleX daemon
let (client, events) = simploxide_client::connect("ws://127.0.0.1:5225").await?;
drop(events);§More complicated use case
Some applications may have several event loops, so the reactor could be moved into a separate async task. In this case it’s recommended to save the handle of the tokio task and await it before the program exits to prevent data losses(e.g. to ensure that client.disconnect() is called).
// Init websocket connection with SimpleX daemon
let (client, events) = simploxide_client::connect("ws://127.0.0.1:5225").await?;
let handle = tokio::spawn(event_reactor(events));
//..
handle.await§How to work with this documentation?
The Client page should become your main page. From there you can reach the deepest corners
of the docs in a structured manner. Looking at other modules is not very helpful unless you’re
looking for something specific.
If you need to understand how async is being implemented in the client check out the core
docs.
Re-exports§
pub use simploxide_api_types as types;pub use simploxide_core as core;
Modules§
- commands
- events
- prelude
- Re-exports everything that is needed to send commands, match events and destructure responses
- responses
Structs§
- Client
- A high level simplex client that implements
ClientApiwhich provides typed client methods with automatic command serialization/response deserialization.
Enums§
- WsError
- Possible WebSocket errors.
Traits§
Functions§
- connect
- A wrapper over
simploxide_core::connectthat turnssimploxide_core::RawClientintoClientand the event queue into the event stream with automatic event deserialization.