icalingua-python-bot/ica-rs/src/tailchat.rs

144 lines
5.5 KiB
Rust
Raw Normal View History

2024-03-30 18:30:43 +08:00
pub mod client;
2024-03-30 12:54:52 +08:00
pub mod events;
2024-06-15 00:35:21 +08:00
use std::sync::Arc;
2024-06-10 16:05:24 +08:00
use colored::Colorize;
2024-03-30 16:59:06 +08:00
use md5::{Digest, Md5};
2024-06-03 23:52:16 +08:00
use reqwest::ClientBuilder as reqwest_ClientBuilder;
2024-06-15 00:35:35 +08:00
use rust_socketio::async_callback;
2024-03-30 12:54:52 +08:00
use rust_socketio::asynchronous::{Client, ClientBuilder};
use rust_socketio::{Event, Payload, TransportType};
2024-06-05 00:09:08 +08:00
use serde_json::{json, Value};
2024-03-30 12:54:52 +08:00
use tracing::{event, span, Level};
2024-03-30 16:59:06 +08:00
use crate::config::TailchatConfig;
2024-06-15 00:35:21 +08:00
use crate::data_struct::tailchat::status::{BotStatus, LoginData};
2024-03-30 12:54:52 +08:00
use crate::error::{ClientResult, TailchatError};
2024-08-18 02:34:32 +08:00
use crate::{async_any_callback_with_state, async_callback_with_state, version_str, StopGetter};
2024-03-30 12:54:52 +08:00
2024-03-30 16:59:06 +08:00
pub async fn start_tailchat(
config: TailchatConfig,
2024-03-30 18:30:43 +08:00
stop_reciver: StopGetter,
2024-03-30 16:59:06 +08:00
) -> ClientResult<(), TailchatError> {
2024-03-30 12:54:52 +08:00
let span = span!(Level::INFO, "Tailchat Client");
let _enter = span.enter();
event!(Level::INFO, "tailchat-async-rs v{} initing", crate::TAILCHAT_VERSION);
2024-03-30 16:59:06 +08:00
let mut hasher = Md5::new();
hasher.update(config.app_id.as_bytes());
hasher.update(config.app_secret.as_bytes());
let token = format!("{:x}", hasher.finalize());
let mut header_map = reqwest::header::HeaderMap::new();
header_map.append("Content-Type", "application/json".parse().unwrap());
2024-03-30 18:30:43 +08:00
let client = reqwest_ClientBuilder::new().default_headers(header_map.clone()).build()?;
2024-03-30 16:59:06 +08:00
let status = match client
.post(format!("{}/api/openapi/bot/login", config.host))
2024-03-30 18:30:43 +08:00
.body(json! {{"appId": config.app_id, "token": token}}.to_string())
2024-03-30 16:59:06 +08:00
.send()
.await
{
Ok(resp) => {
if resp.status().is_success() {
2024-03-30 18:30:43 +08:00
let raw_data = resp.text().await?;
2024-06-10 16:05:24 +08:00
2024-03-30 18:30:43 +08:00
let json_data = serde_json::from_str::<Value>(&raw_data).unwrap();
let login_data = serde_json::from_value::<LoginData>(json_data["data"].clone());
match login_data {
Ok(data) => data,
Err(e) => {
event!(Level::ERROR, "login failed: {}|{}", e, raw_data);
return Err(TailchatError::LoginFailed(e.to_string()));
}
}
2024-03-30 16:59:06 +08:00
} else {
2024-03-30 18:30:43 +08:00
return Err(TailchatError::LoginFailed(resp.text().await?));
2024-03-30 16:59:06 +08:00
}
}
Err(e) => return Err(TailchatError::LoginFailed(e.to_string())),
};
2024-03-30 18:30:43 +08:00
2024-06-15 01:34:03 +08:00
status.update_to_global();
2024-06-15 00:35:21 +08:00
let sharded_status = BotStatus::new(status.user_id.clone());
let sharded_status = Arc::new(sharded_status);
2024-03-30 18:30:43 +08:00
let socket = ClientBuilder::new(config.host)
.auth(json!({"token": status.jwt.clone()}))
.transport_type(TransportType::Websocket)
2024-06-15 00:35:35 +08:00
.on_any(async_any_callback_with_state!(events::any_event, sharded_status.clone()))
2024-06-15 00:35:21 +08:00
.on(
"notify:chat.message.add",
async_callback_with_state!(events::on_message, sharded_status.clone()),
)
.on("notify:chat.message.delete", async_callback!(events::on_msg_delete))
2024-06-10 16:05:24 +08:00
.on(
"notify:chat.converse.updateDMConverse",
async_callback!(events::on_converse_update),
2024-06-10 16:05:24 +08:00
)
2024-06-04 00:22:36 +08:00
// .on("notify:chat.message.update", wrap_callback!(events::on_message))
// .on("notify:chat.message.addReaction", wrap_callback!(events::on_msg_update))
2024-03-30 18:30:43 +08:00
.connect()
2024-06-02 23:52:12 +08:00
.await
.unwrap();
2024-06-10 16:05:24 +08:00
event!(Level::INFO, "{}", "已经连接到 tailchat!".green());
2024-06-02 23:52:12 +08:00
2024-06-04 00:22:36 +08:00
// sleep for 500ms to wait for the connection to be established
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
2024-06-03 23:52:16 +08:00
2024-06-02 23:52:12 +08:00
socket.emit("chat.converse.findAndJoinRoom", json!([])).await.unwrap();
2024-03-30 18:30:43 +08:00
2024-06-10 16:05:24 +08:00
event!(Level::INFO, "{}", "tailchat 已经加入房间".green());
2024-03-30 14:24:19 +08:00
2024-08-02 19:25:39 +08:00
if config.notice_start {
2024-08-02 21:34:28 +08:00
event!(Level::INFO, "正在发送启动消息");
for (group, con) in config.notice_room {
event!(Level::INFO, "发送启动消息到: {}|{}", con, group);
2024-08-02 19:25:39 +08:00
let startup_msg =
crate::data_struct::tailchat::messages::SendingMessage::new_without_meta(
2024-08-18 02:38:15 +08:00
format!("{}\n启动成功", version_str()),
2024-08-02 21:34:28 +08:00
con.clone(),
Some(group.clone()),
2024-08-02 19:25:39 +08:00
);
2024-08-02 21:34:28 +08:00
// 反正是 tailchat, 不需要等, 直接发
if let Err(e) = socket.emit("chat.message.sendMessage", startup_msg.as_value()).await {
event!(Level::ERROR, "发送启动消息失败: {}", e);
}
2024-08-02 19:25:39 +08:00
}
}
2024-03-30 18:30:43 +08:00
stop_reciver.await.ok();
event!(Level::INFO, "socketio client stopping");
match socket.disconnect().await {
Ok(_) => {
event!(Level::INFO, "socketio client stopped");
Ok(())
}
Err(e) => {
// 单独处理 SocketIoError(IncompleteResponseFromEngineIo(WebsocketError(AlreadyClosed)))
match e {
rust_socketio::Error::IncompleteResponseFromEngineIo(inner_e) => {
if inner_e.to_string().contains("AlreadyClosed") {
event!(Level::INFO, "socketio client stopped");
2024-05-26 01:08:48 +08:00
Ok(())
2024-03-30 18:30:43 +08:00
} else {
event!(Level::ERROR, "socketio client stopped with error: {:?}", inner_e);
Err(TailchatError::SocketIoError(
rust_socketio::Error::IncompleteResponseFromEngineIo(inner_e),
))
}
}
e => {
event!(Level::ERROR, "socketio client stopped with error: {}", e);
Err(TailchatError::SocketIoError(e))
}
}
}
}
2024-03-30 12:54:52 +08:00
}