286 lines
7.6 KiB
Markdown
286 lines
7.6 KiB
Markdown
# Feishu SDK Rust
|
|
|
|
[](https://crates.io/crates/feishu-sdk)
|
|
[](https://docs.rs/feishu-sdk)
|
|
[](https://github.com/your-repo/feishu-sdk-rust)
|
|
|
|
> Notice: This is a community-maintained crate and is not an official Feishu/Lark SDK.
|
|
|
|
Rust SDK for Feishu/Lark Open Platform API. Provides a complete, type-safe interface to interact with Feishu APIs.
|
|
|
|
## Features
|
|
|
|
- **Complete API Coverage**: 1448+ operations from Feishu Open Platform
|
|
- **Token Management**: Automatic app_access_token and tenant_access_token handling
|
|
- **Event Processing**: Event subscription and card callback handling
|
|
- **WebSocket Support**: Long connection for real-time messaging
|
|
- **HTTP Server**: Built-in Salvo server for webhook handling
|
|
- **Type Safety**: Generated operation constants and typed APIs
|
|
- **Async/Await**: Full async support with tokio runtime
|
|
- **Pluggable Components**: Customizable cache, logger, serializer, and HTTP client
|
|
- **Code Generation**: Operation wrappers + typed model stubs from endpoint catalog
|
|
|
|
## Installation
|
|
|
|
Add to your `Cargo.toml`:
|
|
|
|
```toml
|
|
[dependencies]
|
|
feishu-sdk = "0.1"
|
|
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
|
```
|
|
|
|
### Optional Features
|
|
|
|
```toml
|
|
[dependencies]
|
|
feishu-sdk = { version = "0.1", features = ["server", "websocket"] }
|
|
```
|
|
|
|
- `server`: Enable HTTP server for event handling (Salvo)
|
|
- `websocket`: Enable WebSocket client for long connections
|
|
|
|
## Quick Start
|
|
|
|
### Basic Usage
|
|
|
|
```rust
|
|
use feishu_sdk::core::{Config, FEISHU_BASE_URL};
|
|
use feishu_sdk::Client;
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
let config = Config::builder("app_id", "app_secret")
|
|
.base_url(FEISHU_BASE_URL)
|
|
.build();
|
|
|
|
let client = Client::new(config)?;
|
|
|
|
// Get chat list
|
|
let resp = client
|
|
.operation("im.v1.chat.list")
|
|
.query_param("page_size", "20")
|
|
.send()
|
|
.await?;
|
|
|
|
println!("Status: {}", resp.status);
|
|
println!("Body: {}", String::from_utf8_lossy(&resp.body));
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
### Send Message
|
|
|
|
```rust
|
|
use feishu_sdk::core::Config;
|
|
use feishu_sdk::Client;
|
|
|
|
async fn send_message(client: &Client, chat_id: &str) -> Result<(), Box<dyn std::error::Error>> {
|
|
let message = serde_json::json!({
|
|
"receive_id": chat_id,
|
|
"msg_type": "text",
|
|
"content": r#"{"text":"Hello!"}"#
|
|
});
|
|
|
|
let resp = client
|
|
.operation("im.v1.messages.create")
|
|
.path_param("receive_id_type", "chat_id")
|
|
.body_json(&message)?
|
|
.send()
|
|
.await?;
|
|
|
|
println!("Message sent: {:?}", resp.json_value()?);
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
### Event Handling Server
|
|
|
|
```rust
|
|
use feishu_sdk::core::{noop_logger, Config};
|
|
use feishu_sdk::event::{EventDispatcher, EventDispatcherConfig};
|
|
use feishu_sdk::server::FeishuServer;
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let event_config = EventDispatcherConfig::new()
|
|
.verification_token("your_token");
|
|
|
|
let dispatcher = EventDispatcher::new(event_config, noop_logger());
|
|
|
|
// Register event handlers
|
|
dispatcher.on_event("im.message.receive_v1", |event| {
|
|
Box::pin(async move {
|
|
println!("Received message: {:?}", event);
|
|
Ok(None)
|
|
})
|
|
});
|
|
|
|
// Start server
|
|
let server = FeishuServer::new(dispatcher).port(8080);
|
|
server.run().await;
|
|
}
|
|
```
|
|
|
|
### WebSocket Client
|
|
|
|
```rust
|
|
use feishu_sdk::core::noop_logger;
|
|
use feishu_sdk::ws::{WebSocketClient, WsConfig};
|
|
use std::time::Duration;
|
|
|
|
#[tokio::main]
|
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
let config = WsConfig::new("wss://ws.feishu.cn/...")
|
|
.heartbeat_interval(Duration::from_secs(30))
|
|
.reconnect_delay(Duration::from_secs(5));
|
|
|
|
let client = WebSocketClient::new(config, noop_logger()).await?;
|
|
|
|
// Check connection status
|
|
if client.is_connected().await {
|
|
println!("Connected to WebSocket");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### Basic Configuration
|
|
|
|
```rust
|
|
use feishu_sdk::core::{Config, LogLevel, FEISHU_BASE_URL, LARK_BASE_URL};
|
|
use std::time::Duration;
|
|
|
|
let config = Config::builder("app_id", "app_secret")
|
|
.base_url(FEISHU_BASE_URL) // or LARK_BASE_URL for international
|
|
.log_level(LogLevel::Debug) // Set log level
|
|
.request_timeout(Duration::from_secs(30)) // Request timeout
|
|
.enable_token_cache(true) // Enable token caching
|
|
.build();
|
|
```
|
|
|
|
### Marketplace App Configuration
|
|
|
|
```rust
|
|
let config = Config::builder("app_id", "app_secret")
|
|
.marketplace_app() // Enable marketplace mode
|
|
.app_ticket("your_app_ticket") // Required for marketplace apps
|
|
.build();
|
|
```
|
|
|
|
### Helpdesk Configuration
|
|
|
|
```rust
|
|
let config = Config::builder("app_id", "app_secret")
|
|
.helpdesk("helpdesk_id", "helpdesk_token")
|
|
.build();
|
|
```
|
|
|
|
### Client Options
|
|
|
|
```rust
|
|
use feishu_sdk::client::{ClientBuilder, with_http_client, with_log_level, with_request_timeout, with_serializer};
|
|
use feishu_sdk::core::{Config, LogLevel};
|
|
|
|
let client = ClientBuilder::new(Config::builder("app_id", "app_secret").build())
|
|
.with(with_log_level(LogLevel::Debug))
|
|
.with(with_request_timeout(Duration::from_secs(60)))
|
|
.with(with_http_client(custom_http_client))
|
|
.with(with_serializer(custom_serializer))
|
|
.build()?;
|
|
```
|
|
|
|
## API Reference
|
|
|
|
### Operation IDs
|
|
|
|
Operations use the format: `{service}.{version}.{resource}.{method}`
|
|
|
|
Examples:
|
|
- `im.v1.messages.create` - Send message
|
|
- `im.v1.chat.list` - List chats
|
|
- `contact.v3.user.get` - Get user info
|
|
- `auth.v3.tenant_access_token.internal.post` - Get tenant token
|
|
|
|
### Typed API Wrappers
|
|
|
|
```rust
|
|
use feishu_sdk::api::all_services;
|
|
|
|
// Get chat info
|
|
let builder = all_services::im::v1::chat::get(&client, "oc_xxx")
|
|
.query_param("user_id_type", "open_id");
|
|
|
|
// List users
|
|
let builder = all_services::contact::v3::user::list(&client)
|
|
.query_param("department_id", "0")
|
|
.query_param("page_size", "50");
|
|
```
|
|
|
|
## Examples
|
|
|
|
See the `examples/` directory:
|
|
|
|
- `get_token.rs` - Get tenant access token
|
|
- `send_message.rs` - Send a message
|
|
- `get_user.rs` - Get user information
|
|
- `im_v1_message.rs` - IM message operations
|
|
- `drive_v1_file.rs` - Drive file operations
|
|
- `calendar_v4.rs` - Calendar operations
|
|
- `docx_v1.rs` - Docx document operations
|
|
- `sheets_v3.rs` - Sheets operations
|
|
- `bitable_v1.rs` - Bitable operations
|
|
- `approval_v4.rs` - Approval operations
|
|
- `event_server.rs` - Event handling server
|
|
- `card_handler.rs` - Card callback server
|
|
- `websocket_client.rs` - WebSocket client
|
|
- `full_app.rs` - End-to-end app example (client + event + card)
|
|
|
|
Run examples:
|
|
|
|
```bash
|
|
# Set environment variables
|
|
export FEISHU_APP_ID=your_app_id
|
|
export FEISHU_APP_SECRET=your_app_secret
|
|
|
|
# Run example
|
|
cargo run --example get_token
|
|
|
|
# Run server example (requires server feature)
|
|
cargo run --features server --example event_server
|
|
|
|
# Run websocket example (requires websocket feature)
|
|
cargo run --features websocket --example websocket_client
|
|
```
|
|
|
|
## Documentation
|
|
|
|
- Usage guide: [`docs/USAGE.md`](docs/USAGE.md)
|
|
- FAQ: [`docs/FAQ.md`](docs/FAQ.md)
|
|
- Migration guide: [`docs/MIGRATION.md`](docs/MIGRATION.md)
|
|
- Best practices: [`docs/BEST_PRACTICES.md`](docs/BEST_PRACTICES.md)
|
|
|
|
## Error Handling
|
|
|
|
```rust
|
|
use feishu_sdk::core::Error;
|
|
|
|
match client.operation("im.v1.messages.create").send().await {
|
|
Ok(resp) => println!("Success: {:?}", resp.json_value()?),
|
|
Err(Error::Api(api_error)) => {
|
|
println!("API Error: code={}, msg={}", api_error.code, api_error.msg);
|
|
}
|
|
Err(Error::RequestTimeout(_)) => println!("Request timed out"),
|
|
Err(e) => println!("Error: {}", e),
|
|
}
|
|
```
|
|
|
|
## License
|
|
|
|
MIT License
|
|
|