Crate simploxide_client

Crate simploxide_client 

Source
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(())

}
  1. Initialize a web socket connection with the simplex-chat daemon. You can run simplex-chat as a daemon with simplex-chat -p <port> command.
  2. 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
  3. 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 ClientApi which provides typed client methods with automatic command serialization/response deserialization.

Enums§

WsError
Possible WebSocket errors.

Traits§

ClientApi
CommandSyntax

Functions§

connect
A wrapper over simploxide_core::connect that turns simploxide_core::RawClient into Client and the event queue into the event stream with automatic event deserialization.

Type Aliases§

CoreError
CoreResult