24 个版本 (5 个重大变更)
0.7.3 | 2021 年 10 月 24 日 |
---|---|
0.7.2 | 2021 年 6 月 21 日 |
0.7.1 | 2021 年 4 月 9 日 |
0.6.0 | 2021 年 3 月 13 日 |
#1530 in Web 编程
每月 154 次下载
用于 urbit-chatbot-framework
120KB
2.5K SLoC
Rust Urbit HTTP API
此库包装了 Urbit 船的 http 接口,并以易于使用的 Rust 包形式暴露。
所有实现细节,如认证 cookie、EventSource 连接、跟踪消息 ID 等,都自动为您处理,从而极大地提高了编写与 Urbit 船交互的 Rust 应用的体验。
此包目前允许开发者
- 授权自身并与船建立通道。
- 订阅任何应用程序/路径,以便可以读取船内正在发生的事件。
- 发布 pokes/scries/threads。
- 支持图存储,并具有本机 Rust
Graph
接口,用于处理图。 - 为 Urbit 聊天提供简单的 Rust 接口。
- 为 Urbit 笔记本提供简单的 Rust 接口。
基本设计
该库公开了 3 个主要结构,用于与 Urbit 船交互
ShipInterface
Channel
Subscription
通过 Channel
创建 Subscription
,而 Channel
是通过 ShipInterface
创建的。换句话说,您需要先使用 ShipInterface
连接到 Urbit 船,然后才能启动消息 Channel
,在您可以为应用程序/路径创建 Subscription
之前。
ShipInterface
ShipInterface
公开了一些有用的方法,这些方法在创建应用程序时将非常有用。
以下常用方法允许您创建新的 ShipInterface
(从而授权自身与船),并创建新的 Channel
。
/// Logs into the given ship and creates a new `ShipInterface`.
/// `ship_url` should be `http://ip:port` of the given ship. Example:
/// `http://0.0.0.0:8080`. `ship_code` is the code acquire from your ship
/// by typing `+code` in dojo.
pub fn new(ship_url: &str, ship_code: &str) -> Result<ShipInterface>;
/// Create a `Channel` using this `ShipInterface`
pub fn create_channel(&mut self) -> Result<Channel>;
您还可以通过蜘蛛进行窥视和运行线程。
/// Send a scry using the `ShipInterface`
pub fn scry(&self, app: &str, path: &str) -> Result<Response>;
/// Run a thread via spider using the `ShipInterface`
pub fn spider(&self, input_mark: &str, output_mark: &str, thread_name: &str, body: &JsonValue) -> Result<Response>;
Channel
Channel
是最有用的结构,因为它包含与 pokes 和订阅相关的所有方法。
查看 Channel
结构体的定义,有助于理解其工作原理。
// A Channel which is used to interact with a ship
pub struct Channel<'a> {
/// `ShipInterface` this channel is created from
pub ship_interface: &'a ShipInterface,
/// The uid of the channel
pub uid: String,
/// The url of the channel
pub url: String,
// The list of `Subscription`s for this channel
pub subscription_list: Vec<Subscription>,
// / The `EventSource` for this channel which reads all of
// / the SSE events.
event_receiver: ReceiverSource,
/// The current number of messages that have been sent out (which are
/// also defined as message ids) via this `Channel`
pub message_id_count: u64,
}
一旦创建了一个 Channel
,就会在单独的线程上创建一个与飞船的 EventSource
连接。这个线程接受所有传入的事件,并将它们排队在一个(Rust)无界通道上,该通道可以通过 event_receiver
内部访问。这个字段本身不是公开的,但在这个包中处理事件时,会使用一个针对应用程序开发者的更高级接口。
请注意,Channel
有一个 subscription_list
。如你所见,每个 Channel
都公开了创建订阅的方法,这些方法会自动添加到 subscription_list
中。一旦创建了/添加了 Subscription
到列表中,Channel
显然将通过 SSE 接收事件消息(这些消息将在 event_receiver
中排队等待读取)。
从应用程序开发者的角度来看,你所要做的就是调用你的 Channel
上的 parse_event_messages
方法,所有排队的将事件都会被处理并传递到正确的 Subscription
的 message_list
。这对于在一个通道上创建了多个 Subscriptions
时非常有用,因为消息将自动为你预排序。
一旦解析了事件消息,就可以简单地调用 find_subscription
方法来与 Subscription
交互并读取其消息。
以下是一个 Channel
公开的实用方法。
/// Sends a poke over the channel
pub fn poke(&mut self, app: &str, mark: &str, json: &JsonValue) -> Result<Response>;
/// Create a new `Subscription` and thus subscribes to events on the ship with the provided app/path.
pub fn create_new_subscription(&mut self, app: &str, path: &str) -> Result<CreationID>;
/// Parses SSE messages for this channel and moves them into
/// the proper corresponding `Subscription`'s `message_list`.
pub fn parse_event_messages(&mut self);
/// Finds the first `Subscription` in the list which has a matching
/// `app` and `path`;
pub fn find_subscription(&self, app: &str, path: &str) -> Option<&Subscription>;
/// Finds the first `Subscription` in the list which has a matching
/// `app` and `path`, removes it from the list, and tells the ship
/// that you are unsubscribing.
pub fn unsubscribe(&mut self, app: &str, path: &str) -> Option<bool>;
/// Deletes the channel
pub fn delete_channel(self);
/// Exposes an interface for interacting with a ship's Graph Store directly.
pub fn graph_store(&mut self) -> GraphStore;
/// Exposes an interface for interacting with Urbit chats.
pub fn chat(&mut self) -> Chat;
/// Exposes an interface for interacting with Urbit notebooks.
pub fn notebook(&mute self) -> Notebook;
Subscription
如前所述,一个 Subscription
包含它自己的 message_list
字段,消息在 Channel
处理后存储在这里。
从应用程序开发者的角度来看,这是 Subscription
结构体的唯一有用特性。一旦获取,它就用于简单地读取消息。
为了提高消息阅读体验,Subscription
结构体公开了一个有用的方法。
/// Pops a message from the front of `Subscription`'s `message_list`.
/// If no messages are left, returns `None`.
pub fn pop_message(&mut self) -> Option<String>;
代码示例
戳击示例
此示例演示了如何使用 ShipInterface
连接到飞船,打开一个 Channel
,在该通道上发出一个 poke
,然后删除 Channel
以完成。
// Import the `ShipInterface` struct
use urbit_http_api::ShipInterface;
fn main() {
// Create a new `ShipInterface` for a local ~zod ship
let mut ship_interface =
ShipInterface::new("http://0.0.0.0:8080", "lidlut-tabwed-pillex-ridrup").unwrap();
// Create a `Channel`
let mut channel = ship_interface.create_channel().unwrap();
// Issue a poke over the channel
let poke_res = channel.poke("hood", "helm-hi", &"This is a poke".into());
// Cleanup/delete the `Channel` once finished
channel.delete_channel();
}
图存储订阅示例
此示例演示了如何创建、交互和删除一个 Subscription
。在这种情况下,我们希望通过 Subscription
读取来自图存储的所有新更新 10 秒钟,然后执行清理。
use std::thread;
use std::time::Duration;
use urbit_http_api::ShipInterface;
fn main() {
// Create a new `ShipInterface` for a local ~zod ship
let mut ship_interface =
ShipInterface::new("http://0.0.0.0:8080", "lidlut-tabwed-pillex-ridrup").unwrap();
// Create a `Channel`
let mut channel = ship_interface.create_channel().unwrap();
// Create a `Subscription` for the `graph-store` app with the `/updates` path. This `Subscription`
// is automatically added to the `Channel`'s `subscription_list`.
channel
.create_new_subscription("graph-store", "/updates")
.unwrap();
// Create a loop that iterates 10 times
for _ in 0..10 {
// Parse all of the event messages to move them into the correct
// `Subscription`s in the `Channel`'s `subscription_list`.
channel.parse_event_messages();
// Find our graph-store `Subscription`
let gs_sub = channel.find_subscription("graph-store", "/updates").unwrap();
// Pop all of the messages from our `gs_sub` and print them
loop {
let pop_res = gs_sub.pop_message();
if let Some(mess) = &pop_res {
println!("Message: {:?}", mess);
}
// If no messages left, stop
if let None = &pop_res {
break;
}
}
// Wait for 1 second before trying to parse the event messages again
thread::sleep(Duration::new(1, 0));
}
// Once finished, unsubscribe/destroy our `Subscription`
channel.unsubscribe("graph-store", "/updates");
// Delete the channel
channel.delete_channel();
}
Urbit 聊天消息示例
此示例演示了如何使用 Chat
结构体接口连接到飞船并向 Urbit 聊天发送消息。
// Import the `ShipInterface` struct
use urbit_http_api::{ShipInterface, chat::Message};
fn main() {
// Create a new `ShipInterface` for a local ~zod ship
let mut ship_interface =
ShipInterface::new("http://0.0.0.0:8080", "lidlut-tabwed-pillex-ridrup").unwrap();
// Create a `Channel`
let mut channel = ship_interface.create_channel().unwrap();
// Create a `Message` which is formatted properly for an Urbit chat
let message = Message::new()
// Add text to your message
.add_text("Checkout this cool article by ~wicdev-wisryt:")
// Add a URL link to your message after the previous text (which gets automatically added on a new line)
.add_url("https://urbit.org/blog/io-in-hoon/")
// Add an image URL to your message after the previous url (which gets automatically added on a new line as a rendered image)
.add_url("https://media.urbit.org/site/posts/essays/zion-canyon-1.jpg");
// Send the message to a chat hosted by ~zod named "test-93".
// Note the connected ship must already have joined the chat in order to send a message to the chat.
let _mess_res = channel
.chat()
.send_message("~zod", "test-93", &message);
// Cleanup/delete the `Channel` once finished
channel.delete_channel();
}
Urbit 聊天订阅示例
此示例展示了如何利用更高级的 Chat
接口来订阅聊天并读取该聊天中发布的所有消息。
use std::thread;
use std::time::Duration;
use urbit_http_api::ShipInterface;
fn main() {
// Create a new `ShipInterface` for a local ~zod ship
let mut ship_interface =
ShipInterface::new("http://0.0.0.0:8080", "lidlut-tabwed-pillex-ridrup").unwrap();
// Create a `Channel`
let mut channel = ship_interface.create_channel().unwrap();
// Subscribe to a specific chat, and obtain a `Receiver` back which contains a stream of messages from the chat
let chat_receiver = channel
.chat()
.subscribe_to_chat("~mocrux-nomdep", "test-93")
.unwrap();
// Create a loop that iterates 10 times
for _ in 0..10 {
// If a message has been posted to the chat, unwrap it and acquire the `AuthoredMessage`
if let Ok(authored_message) = chat_receiver.try_recv() {
// Pretty print the author ship @p and the message contents
println!(
"~{}:{}",
authored_message.author,
authored_message.message.to_formatted_string()
);
}
// Wait for 1 second before checking again
thread::sleep(Duration::new(1, 0));
}
// Delete the channel
channel.delete_channel();
}
此库由 ~mocrux-nomdep([Robert Kornacki](https://github.com/robkorn))创建。
依赖关系
~10–23MB
~370K SLoC