feat: add workflow toolkit and runtime support

This commit is contained in:
2026-07-06 10:45:19 +08:00
parent efc6fa7067
commit ed7f89d44f
44 changed files with 3770 additions and 127 deletions
+63 -5
View File
@@ -63,7 +63,7 @@ impl WeChatClient {
.unwrap_or_else(|_| "https://ilinkai.weixin.qq.com".to_string())
}
/// 版本号编码:0x00MMNNPP
const CLIENT_VERSION: u32 = 0x000100_00; // 1.0.0
const CLIENT_VERSION: u32 = 0x0001_0000; // 1.0.0
pub fn new(base_url: Option<String>) -> Self {
// 优先级: 显式参数 > WEIXIN_BASE_URL 环境变量 > 默认值
@@ -363,14 +363,72 @@ impl WeChatClient {
context_token: Option<&str>,
) -> Result<String, String> {
let client_id = uuid::Uuid::new_v4().to_string();
self.send_text_with_client_id(to, text, context_token, &client_id)
.await
}
/// 发送“输入中”状态,返回用于最终完成消息的 client_id。
pub async fn send_typing(
&self,
to: &str,
context_token: Option<&str>,
) -> Result<String, String> {
let client_id = uuid::Uuid::new_v4().to_string();
self.send_typing_with_client_id(to, context_token, &client_id)
.await?;
Ok(client_id.to_string())
}
/// 用已有 client_id 刷新“输入中”状态。
pub async fn send_typing_with_client_id(
&self,
to: &str,
context_token: Option<&str>,
client_id: &str,
) -> Result<String, String> {
self.send_bot_message(
to,
client_id,
WeixinMessage::STATE_TYPING,
None,
context_token,
)
.await
}
/// 使用已有 client_id 完成文本消息。
pub async fn send_text_with_client_id(
&self,
to: &str,
text: &str,
context_token: Option<&str>,
client_id: &str,
) -> Result<String, String> {
self.send_bot_message(
to,
client_id,
WeixinMessage::STATE_FINISH,
Some(vec![MessageItem::text(text)]),
context_token,
)
.await
}
async fn send_bot_message(
&self,
to: &str,
client_id: &str,
message_state: i32,
item_list: Option<Vec<MessageItem>>,
context_token: Option<&str>,
) -> Result<String, String> {
let msg = WeixinMessage {
from_user_id: Some(String::new()),
to_user_id: Some(to.to_string()),
client_id: Some(client_id.clone()),
client_id: Some(client_id.to_string()),
msg_type: Some(WeixinMessage::TYPE_BOT),
message_state: Some(2), // FINISH
item_list: Some(vec![MessageItem::text(text)]),
message_state: Some(message_state),
item_list,
context_token: context_token.map(|s| s.to_string()),
..Default::default()
};
@@ -387,7 +445,7 @@ impl WeChatClient {
.await
.map_err(|e| format!("发送消息网络错误: {}", e))?;
Ok(client_id)
Ok(client_id.to_string())
}
/// 设置 auth 状态(从持久化恢复)
+56 -2
View File
@@ -47,7 +47,7 @@ pub enum WeChatEvent {
/// 收到一条微信消息。
Message {
account_id: String,
message: WeixinMessage,
message: Box<WeixinMessage>,
updates_buf: String,
},
/// 某个账号的轮询发生错误。管理器会继续重试,除非任务被停止。
@@ -145,7 +145,7 @@ impl WeChatMultiAccountManager {
for message in resp.msgs {
let event = WeChatEvent::Message {
account_id: account_id.clone(),
message,
message: Box::new(message),
updates_buf: updates_buf.clone(),
};
if event_tx.send(event).await.is_err() {
@@ -230,6 +230,60 @@ impl WeChatMultiAccountManager {
runtime.client.send_text(to, text, context_token).await
}
/// 使用指定账号发送输入中状态,返回后续完成消息复用的 client_id。
pub async fn send_typing(
&self,
account_id: &str,
to: &str,
context_token: Option<&str>,
) -> Result<String, String> {
let runtime = self
.accounts
.get(account_id)
.ok_or_else(|| format!("微信账号未启动: {}", account_id))?;
runtime.client.send_typing(to, context_token).await
}
/// 使用指定账号刷新输入中状态。
pub async fn send_typing_with_client_id(
&self,
account_id: &str,
to: &str,
context_token: Option<&str>,
client_id: &str,
) -> Result<String, String> {
let runtime = self
.accounts
.get(account_id)
.ok_or_else(|| format!("微信账号未启动: {}", account_id))?;
runtime
.client
.send_typing_with_client_id(to, context_token, client_id)
.await
}
/// 使用指定账号完成已有输入中消息。
pub async fn send_text_with_client_id(
&self,
account_id: &str,
to: &str,
text: &str,
context_token: Option<&str>,
client_id: &str,
) -> Result<String, String> {
let runtime = self
.accounts
.get(account_id)
.ok_or_else(|| format!("微信账号未启动: {}", account_id))?;
runtime
.client
.send_text_with_client_id(to, text, context_token, client_id)
.await
}
}
impl Drop for WeChatMultiAccountManager {
+8
View File
@@ -30,6 +30,12 @@ impl BaseInfo {
}
}
impl Default for BaseInfo {
fn default() -> Self {
Self::new()
}
}
// ─── 消息类型 ───
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -232,6 +238,8 @@ impl WeixinMessage {
pub const TYPE_NONE: i32 = 0;
pub const TYPE_USER: i32 = 1;
pub const TYPE_BOT: i32 = 2;
pub const STATE_TYPING: i32 = 1;
pub const STATE_FINISH: i32 = 2;
/// 获取文本内容(如果有纯文本 item)
pub fn text_content(&self) -> Option<&str> {