5.8 KiB
Events, WebSockets, and channels
Webhook handlers use EventDispatcher for typed event callbacks, signature
verification, and encrypted payload processing. The optional ws feature adds
long connections for event delivery; see
examples/ws_client.rs.
Trusted user channels match the upstream Go SDK behavior. Configure the channel
before obtaining its control handle, start the client, then bind a user token
after the connection becomes ready. See
examples/ws_client.rs for the connection lifecycle
and examples/channel_send.rs for higher-level
messages.
use std::collections::HashMap;
use std::time::Duration;
# use larksuite_oapi_sdk_rs::{EventDispatcher, LarkClient};
# #[tokio::main]
# async fn main() -> Result<(), Box<dyn std::error::Error>> {
# let client = LarkClient::builder("APP_ID", "APP_SECRET").build()?;
# let dispatcher = EventDispatcher::new("VERIFICATION_TOKEN", "ENCRYPT_KEY");
let ws_client = client
.ws_client(dispatcher)
.channel_tag("trusted_channel")
.websocket_query_params(HashMap::from([("env".into(), "staging".into())]));
let control = ws_client.control();
let task = tokio::spawn(ws_client.start());
while control.connection_id().is_none() {
tokio::time::sleep(Duration::from_millis(50)).await;
}
control.attach_user("USER_ACCESS_TOKEN").await?;
// Later, before dropping the user session:
// control.detach_user("USER_ACCESS_TOKEN").await?;
control.close_and_wait().await;
task.await??;
# Ok(())
# }
WsClientControl::close requests shutdown without waiting, which is safe from
lifecycle callbacks. close_and_wait is for external teardown and waits until
the running client has stopped. The client honors a finite reconnect count from
the gateway bootstrap configuration, treats an inbound-silent connection as
failed after two ping intervals plus five seconds, and reconnects after either
that liveness timeout or a timed-out connection write. It bounds each
connection write to ten seconds by default and accepts write_timeout for a
different bound. Event callbacks run independently of the receive loop, so a
slow callback does not mask a silent gateway. Use
websocket_connector when a custom proxy, TLS, or test dial transport is
needed; it affects only the WebSocket gateway dial, not the bootstrap HTTP
client.
Calendar share-token joins
Calendar invitations provide an opaque share token through their link, QR code, or RSVP card. Join with that token; the endpoint deliberately has no event-ID alternative.
use larksuite_oapi_sdk_rs::service::calendar::v4::JoinCalendarEventReqBody;
use larksuite_oapi_sdk_rs::{LarkClient, RequestOption};
# async fn example() -> Result<(), larksuite_oapi_sdk_rs::LarkError> {
let client = LarkClient::builder("APP_ID", "APP_SECRET").build()?;
client
.calendar()
.calendar
.join_event(
&JoinCalendarEventReqBody::new("CALENDAR_SHARE_TOKEN"),
&RequestOption::default(),
)
.await?;
# Ok(())
# }
Use the optional axum feature for Axum HTTP adapters. The optional messaging
feature provides outbound send, reply, update, upload, and download operations
without WebSocket dependencies. The optional channel feature adds inbound
message normalization and runtime policy controls and enables both messaging
and ws.
Replies
ChannelMessaging::send preserves convenience behavior: a reply whose target has
disappeared may retry as a top-level send. For workflows that must never escape
a thread, use ChannelMessaging::reply or ChannelMessaging::reply_in_thread.
Both methods only call the reply endpoint and return its error unchanged. Set
uuid to opt into Lark's one-hour idempotency window; automatic split messages
derive distinct UUIDs, so the supplied UUID must leave room for a -N suffix.
use larksuite_oapi_sdk_rs::channel::SendInput;
use larksuite_oapi_sdk_rs::{LarkClient, RequestOption};
# async fn run() -> Result<(), Box<dyn std::error::Error>> {
let client = LarkClient::builder("APP_ID", "APP_SECRET").build()?;
let messaging = client.channel_messaging();
messaging
.reply_in_thread(
"om_parent_message",
&SendInput {
markdown: Some("A reply that stays in the topic".into()),
uuid: Some("order-42-status".into()),
..Default::default()
},
&RequestOption::default(),
)
.await?;
# Ok(())
# }
Message updates
Use ChannelMessaging::edit_text to update a text message. Use
ChannelMessaging::edit_card only for validated interactive-card documents; it
uses the separate card patch operation. See
examples/card_send.rs for Card JSON delivery and
examples/card_action_handler.rs for
interactive callback responses.
use larksuite_oapi_sdk_rs::card::v1::{Card, CardDocument, Div, Element, Header, Text};
use larksuite_oapi_sdk_rs::{LarkClient, RequestOption};
# async fn run() -> Result<(), Box<dyn std::error::Error>> {
let client = LarkClient::builder("APP_ID", "APP_SECRET").build()?;
let messaging = client.channel_messaging();
let card = CardDocument::new(
Card::new()
.header(Header::new("Done"))
.element(Element::Div(Div::new(Text::lark_md("Complete")))),
)?;
messaging
.edit_text("om_text_message", "Updated status", &RequestOption::default())
.await?;
messaging
.edit_card(
"om_card_message",
&card,
&RequestOption::default(),
)
.await?;
# Ok(())
# }
Cards
Use card::v1 or card::v2 to build versioned Card JSON, and validate it with
their CardDocument types before sending. CardActionHandler handles callback
payloads. See the Cards guide for CardKit and published Card Builder
templates.