Crate vtubestudio
source ·Expand description
A library for interacting with the VTube Studio API.
This crate exposes a Client
for making requests to the VTube Studio websocket API, and
handles the work of mapping requests to responses (using tokio_tower::multiplex
).
The client wraps a set of configurable tower::Service
middleware for handling the
authentication flow, retries,
and reconnects, and uses tokio_tungstenite
as the
underlying websocket transport by default.
§Basic usage
The example below creates a Client
using the provided builder, which:
- connects to
ws://localhost:8001
usingtokio_tungstenite
- authenticates with an existing token (if present and valid)
- reconnects when disconnected, and retries the failed request on reconnection success
- requests a new auth token on receiving an auth error, and retries the initial failed request on authentication success
use vtubestudio::data::StatisticsRequest;
use vtubestudio::{Client, ClientEvent, Error};
#[tokio::main]
async fn main() -> Result<(), Error> {
// An auth token from a previous successful authentication request
let stored_token = Some("...".to_string());
let (mut client, mut events) = Client::builder()
.auth_token(stored_token)
.authentication("Plugin name", "Developer name", None)
.build_tungstenite();
tokio::spawn(async move {
while let Some(event) = events.next().await {
match event {
ClientEvent::NewAuthToken(new_token) => {
// This returns whenever the authentication middleware receives a new auth
// token. We can handle it by saving it somewhere, etc.
println!("Got new auth token: {new_token}");
}
_ => {
// Other events, such as connections/disconnections, API events, etc
println!("Got event: {:?}", event);
}
}
}
});
// Use the client to send a `StatisticsRequest`, handling authentication if necessary.
// The return type is inferred from the input type to be `StatisticsResponse`.
let resp = client.send(&StatisticsRequest {}).await?;
println!("VTube Studio has been running for {}ms", resp.uptime);
Ok(())
}
To send multiple outgoing requests at the same time without waiting for a request to come back,
you can clone the Client
per request (by default, the client wraps a
tower::buffer::Buffer
which adds an mpsc buffer in front of the underlying websocket
transport).
For an example of constructing a Client
manually without the builder, check the
no_middleware
example in the repo.
§Events
The ClientEventStream
returned from the ClientBuilder
will also return
Event
s if we subscribe to them.
The example below demonstrates subscribing to TestEvent
s, which
will be emitted every second.
use vtubestudio::data::{Event, EventSubscriptionRequest, TestEventConfig};
use vtubestudio::{Client, ClientEvent, Error};
#[tokio::main]
async fn main() -> Result<(), Error> {
// An auth token from a previous successful authentication request
let stored_token = Some("...".to_string());
let (mut client, mut events) = Client::builder()
.auth_token(stored_token)
.authentication("Plugin name", "Developer name", None)
.build_tungstenite();
println!("Please accept the permission pop-up in VTube Studio");
// Create the event subscription request, to be sent later.
let req = EventSubscriptionRequest::subscribe(&TestEventConfig {
test_message_for_event: "Hello from vtubestudio-rs!".to_owned(),
})?;
while let Some(client_event) = events.next().await {
match client_event {
// We receive a `Disconnected` client event whenever we are disconnected, including on
// startup. This can be used as a cue to refresh any event subscriptions.
ClientEvent::Disconnected => {
println!("Connecting...");
// Try to subscribe to test events, retrying on failure. Note that the client
// attempts to reconnect automatically when sending a request.
while let Err(e) = client.send(&req).await {
eprintln!("Failed to subscribe to test events: {e}");
eprintln!("Retrying in 2s...");
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
}
}
ClientEvent::Api(Event::Test(event)) => {
assert_eq!(event.your_test_message, "Hello from vtubestudio-rs!");
println!(
"VTube Studio has been running for {} seconds.",
event.counter
);
}
other => {
println!("Received event: {:?}", other)
}
}
}
Ok(())
}
§Project structure
client
provides a high level API dealing with typedRequest
/Response
types, which wraps a… ⏎service
, a stack oftower::Service
s that deal withRequestEnvelope
/ResponseEnvelope
pairs, and wraps a… ⏎
While the provided ClientBuilder
should be sufficient for most users, each of these layers
can be modified to add custom behavior if needed. E.g.,
- using a different combination of tower middleware
- using a different websocket library
- adding custom request/response types (as an escape hatch, if new request types or fields are added to the API and you don’t feel like waiting for them to be added to this library)
§Optional features
By default, the tokio-tungstenite
feature is enabled, which includes helpers related to the
tokio_tungstenite
websocket library. This can be disabled in your Cargo.toml
with
default-features = false
:
[dependencies]
vtubestudio = { version = "0.9.0", default-features = false }
Re-exports§
pub use crate::client::Client;
pub use crate::client::ClientBuilder;
pub use crate::client::ClientEvent;
pub use crate::client::ClientEventStream;
pub use crate::error::Error;
pub use crate::error::ErrorKind;
pub use crate::error::Result;
Modules§
- Utilities for creating
Client
s. - Codecs for converting to/from websocket message types.
- Request/response types for the VTube Studio API.
- Types related to error handling.