2 个版本
0.4.4 | 2020年10月25日 |
---|---|
0.4.3 | 2020年10月25日 |
#50 in #chat-bot
82KB
1.5K SLoC
Bot-RS 核心库
用于实现构建机器人脚本的命令行工具 botrs 的插件和插件加载器。请阅读 Docs rs 中的文档以了解如何实现插件。 \r\n
lib.rs
:
Bot-RS 核心库
此库用于实现 Bot-RS 平台的插件和插件加载器。
实现插件
有两种类型的插件:简单插件 和 可流式插件。虽然 简单插件 拥有简单的接口,但 可流式插件 允许开发者以自定义方式处理传入的消息流并将其转换为传出消息流。这也意味着发送异步消息。而 简单插件 只对消息做出反应,可流式插件 可以随时发送消息。
以下各节将描述从创建 cargo 项目到构建库文件的整个过程。
实现简单插件
-
安装所需工具
- 通过 rustup 安装 rust 和 cargo
- cargo-edit:方便编辑包依赖项
-
使用以下命令创建 Cargo 项目(
cargo new --lib --vcs git <项目名称>
)。然后更改工作目录到该目录(cd hello-plugin
)。cargo new --lib --vcs git hello-plugin cd hello-plugin
-
将
bot-rs-core
的依赖添加到项目中。每个插件都必须实现 StreamablePlugin 特性。为了简化这个过程,"derive" 功能启用了 derive-macro,它可以从实现 Plugin 特性的结构体生成有效的 StreamablePlugin 实现代码。派生代码需要插件包包含对 futures crate 的依赖。我们也将添加这个依赖。由于 Plugin 特性包含异步函数,还需要对 async_trait crate 有依赖。为了处理来自 Twitch 的 (目前唯一支持的平台) irc 消息,还需要对 irc-rust crate 有依赖。为了能够记录消息,我们将使用 log crate 以及其 env_logger 实现来简化过程。cargo add bot-rs-core --features derive && \ cargo add futures && \ cargo add async-trait && \ cargo add irc-rust && \ cargo add log && \ cargo add env_logger
-
将以下片段添加到您的
Cargo.toml
中,以便将库编译为可加载的库文件,该文件将由插件加载器实现加载。[lib] crate-type = ["cdylib"]
-
现在我们可以实现实际的插件。将库根文件
src/lib.rs
的内容替换为以下内容// Simple logging facade #[macro_use] extern crate log; // Enables the derive-macro for StreamablePlugin. #[macro_use] extern crate bot_rs_core; use async_trait::async_trait; use bot_rs_core::Message; use bot_rs_core::plugin::{StreamablePlugin, Plugin, InvocationError, PluginInfo, PluginRegistrar}; use std::sync::Arc; use bot_rs_core::profile::Profile; // Reacts to an invocation of `!hello` with `Hello, @<sender name>!`. #[derive(StreamablePlugin)] struct HelloPlugin { profile: Profile } #[async_trait::async_trait] impl Plugin for HelloPlugin { async fn call(&self, msg: Message) -> Result<Vec<Message>, InvocationError> { // Check AccessRights before processing if let Some(false) = self.profile.rights().allowed(&msg) { // Only process messages not handled by filters or allowed return Ok(Vec::with_capacity(0)); } match &msg { Message::Irc(irc_message) => { match irc_message.get_command() { "PRIVMSG" => { let params = irc_message .params() .expect("missing params in PRIVMSG"); // First param in a PRIVMSG is the channel name let channel = params.iter() .next() .expect("no params in PRIVMSG"); let trailing = params.trailing; if trailing.is_none() { // Return no messages return Ok(Vec::with_capacity(0)); } let trailing = trailing.unwrap(); // Check if '!hello' command was called if !trailing.starts_with("!hello") { return Ok(Vec::with_capacity(0)); } let prefix = irc_message.prefix().expect("missing prefix in PRIVMSG"); let name = prefix.name(); Ok(vec![Message::Irc(irc_rust::Message::builder("PRIVMSG") .param(channel) .trailing(&format!("Hello, @{}!", name)) .build() .expect("failed to build irc message") )]) } } } } } // Return information about the plugin for identification. fn info(&self) -> PluginInfo { PluginInfo { name: "Hello Plugin".to_string(), version: env!("CARGO_PKG_VERSION").to_string(), authors: env!("CARGO_PKG_AUTHORS").to_string(), repo: option_env!("CARGO_PKG_REPOSITORY") .map(|repo| if repo.is_empty() { "No repo".to_string() } else { repo.to_string() }), commands: vec!["!hello".to_string()] } } } // This macro creates a static field which can be loaded by the plugin loader. bot_rs_core::export_command!(register); // The plugin loading mechanism uses this function for load and register. Initializing loggers and other dependencies has to be done here. extern "C" fn register(registrar: &mut PluginRegistrar) { env_logger::init(); // Is set on startup by Bot-RS CLI Tool let profile = Profile::active().unwrap(); registrar.register(Arc::new(HelloPlugin{ profile })) }
-
(可选) 优化插件文件以减小文件大小,以减少通过
cargo build
产生的文件大小。为此,将以下片段复制到您的Cargo.toml
[profile.release] lto = true codegen-units = 1 opt-level = "z"
-
构建插件文件:
cargo build --release
实现 StreamablePlugin
-
安装所需工具
- 通过 rustup 安装 rust 和 cargo
- cargo-edit:轻松编辑包依赖项
-
使用以下命令创建 Cargo 项目(
cargo new --lib --vcs git <项目名称>
)。然后更改工作目录到该目录(cd hello-plugin
)。cargo new --lib --vcs git hello-plugin cd hello-plugin
-
将
bot-rs-core
的依赖添加到项目中。由于 StreamablePlugin 特性包含异步函数,还需要对 async_trait crate 有依赖。为了处理来自 Twitch 的 (目前唯一支持的平台) irc 消息,还需要对 irc-rust crate 有依赖。为了能够记录消息,我们将使用 log crate 以及其 env_logger 实现来简化过程。这次我们还将需要 futures crate 的依赖,用于我们的 StreamablePlugin 实现。cargo add bot-rs-core && \ cargo add async-trait && \ cargo add irc-rust && \ cargo add log && \ cargo add env_logger && \ cargo add futures
-
将以下片段添加到您的
Cargo.toml
中,以便将库编译为可加载的库文件,该文件将由插件加载器实现加载。[lib] crate-type = ["cdylib"]
-
现在我们可以实现实际的插件。将库根文件
src/lib.rs
的内容替换为以下内容// Simple logging facade #[macro_use] extern crate log; extern crate futures; // Enables the derive-macro for StreamablePlugin. #[macro_use] extern crate bot_rs_core; use async_trait::async_trait; use bot_rs_core::Message; use bot_rs_core::plugin::{StreamablePlugin, Plugin, InvocationError, PluginInfo, PluginRegistrar}; use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender}; use std::sync::Arc; use futures::{StreamExt, SinkExt}; /// Reacts to an invocation of `!hello` with `Hello, @<sender name>!`. struct HelloPlugin; // For simplicity we'll be using the Plugin implementation showed in the previous section. But we'll implement the `StreamablePlugin` ourself this time. // <insert Plugin implementation of previous section here> // This implementation doesn'T require spawning new threads. This should be handled by the plugin-loader. #[async_trait] impl StreamablePlugin for HelloPlugin { async fn stream(&self, mut input: UnboundedReceiver<Message>, mut output: UnboundedSender<Vec<Message>>) -> Result<(), InvocationError> { // Read next message from input channel while let Some(msg) = input.next().await { // Call out Plugin implementation let results = self.call(msg).await?; // Send the results to the output channel output.send(results) .await.expect("failed to send results to output"); } Ok(()) } // Return information about the plugin for identification. fn info(&self) -> PluginInfo { Plugin::info(self) } } // This macro creates a static field which can be loaded by the plugin loader. export_command!(register); // The plugin loading mechanism uses this function for load and register. Initializing loggers and other dependencies has to be done here. extern "C" fn register(registrar: &mut PluginRegistrar) { env_logger::init(); registrar.register(Arc::new(HelloPlugin)) }
-
(可选) 优化插件文件以减小文件大小,以减少通过
cargo build
产生的文件大小。为此,将以下片段复制到您的Cargo.toml
[profile.release] lto = true codegen-units = 1 opt-level = "z"
-
构建插件文件:
cargo build --release
插件加载器
目前尚未公开文档。目前唯一存在的实现是 botrs cli。要使用插件,需要此 CLI 工具。
依赖项
~6–16MB
~327K SLoC