2024-03-30 18:30:43 +08:00
|
|
|
pub mod client;
|
2024-03-30 12:54:52 +08:00
|
|
|
pub mod events;
|
|
|
|
|
|
|
|
use futures_util::FutureExt;
|
2024-03-30 16:59:06 +08:00
|
|
|
use md5::{Digest, Md5};
|
|
|
|
use reqwest::{Body, ClientBuilder as reqwest_ClientBuilder};
|
2024-03-30 12:54:52 +08:00
|
|
|
use rust_socketio::asynchronous::{Client, ClientBuilder};
|
2024-06-02 23:52:12 +08:00
|
|
|
// use rust_socketio::packet::PacketParser;
|
2024-03-30 12:54:52 +08:00
|
|
|
use rust_socketio::{Event, Payload, TransportType};
|
2024-03-30 18:30:43 +08:00
|
|
|
use serde_json::{from_str, from_value, 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-03-30 18:30:43 +08:00
|
|
|
use crate::data_struct::tailchat::status::LoginData;
|
2024-03-30 12:54:52 +08:00
|
|
|
use crate::error::{ClientResult, TailchatError};
|
2024-03-30 18:30:43 +08:00
|
|
|
use crate::{wrap_any_callback, wrap_callback, StopGetter};
|
2024-03-30 12:54:52 +08:00
|
|
|
|
2024-06-02 23:52:12 +08:00
|
|
|
// mod packet_parser {
|
|
|
|
// use bytes::{buf, Bytes};
|
|
|
|
// use rust_socketio::error::Error;
|
|
|
|
// use rust_socketio::packet::{Packet, PacketId};
|
|
|
|
// use serde::{Deserialize, Serialize};
|
|
|
|
// use serde_json::Value as JsonValue;
|
|
|
|
// use msgpack_simple::{MsgPack, MapElement}
|
|
|
|
|
|
|
|
// // #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
|
|
|
// // #[serde(rename = "map")]
|
|
|
|
// // struct SerdeValue {
|
|
|
|
// // #[serde(rename = "@type")]
|
|
|
|
// // r#type: i64,
|
|
|
|
// // data: rmpv::Value,
|
|
|
|
// // nsp: String,
|
|
|
|
// // }
|
|
|
|
|
|
|
|
// pub fn encode(packet: &Packet) -> Bytes {
|
|
|
|
// let raw_str = packet.data.as_ref().map(|v| v.to_string()).unwrap_or("{}".to_string());
|
|
|
|
// let raw_value: rmpv::Value = serde_json::from_str(&raw_str).unwrap();
|
|
|
|
// let value = SerdeValue {
|
|
|
|
// r#type: packet.packet_type as u8 as i64,
|
|
|
|
// data: raw_value,
|
|
|
|
// nsp: packet.nsp.clone(),
|
|
|
|
// };
|
|
|
|
// let mut buffer = rmp_serde::to_vec(&value).unwrap();
|
|
|
|
// println!("encoding packet: {:?} {:?}", packet, value);
|
|
|
|
|
|
|
|
// buffer.reverse();
|
|
|
|
// // 前面加上 \x83\xa4
|
|
|
|
// buffer.push(0xa4);
|
|
|
|
// buffer.push(0x83);
|
|
|
|
// buffer.reverse();
|
|
|
|
// Bytes::from(buffer)
|
|
|
|
// }
|
|
|
|
|
|
|
|
// pub fn default_decode(payload: &Bytes) -> Result<Packet, Error> {
|
|
|
|
// println!("decoding bytes {:?}", payload);
|
|
|
|
// todo!()
|
|
|
|
// }
|
|
|
|
// }
|
2024-05-26 01:08:48 +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?;
|
|
|
|
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-02 23:52:12 +08:00
|
|
|
// header_map.append("X-Token", status.jwt.clone().parse().unwrap());
|
2024-03-30 18:30:43 +08:00
|
|
|
|
2024-06-02 23:52:12 +08:00
|
|
|
// let packet_parser =
|
|
|
|
// PacketParser::new(Box::new(packet_parser::encode), Box::new(packet_parser::default_decode));
|
2024-03-30 18:30:43 +08:00
|
|
|
|
|
|
|
let socket = ClientBuilder::new(config.host)
|
|
|
|
.auth(json!({"token": status.jwt.clone()}))
|
|
|
|
.transport_type(TransportType::Websocket)
|
|
|
|
.on_any(wrap_any_callback!(events::any_event))
|
2024-06-02 23:52:12 +08:00
|
|
|
// .on("connect", wrap_callback!(events::on_connect))
|
|
|
|
// .on("chat.message.sendMessage", wrap_callback!(events::on_message))
|
2024-03-30 18:30:43 +08:00
|
|
|
.connect()
|
2024-06-02 23:52:12 +08:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
event!(Level::INFO, "tailchat connected");
|
|
|
|
|
|
|
|
socket.emit("chat.converse.findAndJoinRoom", json!([])).await.unwrap();
|
|
|
|
socket
|
|
|
|
.emit(
|
|
|
|
"chat.message.sendMessage",
|
|
|
|
json!({"content": "shenbot v 0.1.0", "roomId": 1, "type": 1}),
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2024-03-30 18:30:43 +08:00
|
|
|
|
2024-06-02 23:52:12 +08:00
|
|
|
event!(Level::INFO, "tailchat joined room");
|
2024-03-30 16:59:06 +08:00
|
|
|
// notify:chat.message.delete
|
|
|
|
// notify:chat.message.add
|
2024-03-30 14:24:19 +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
|
|
|
}
|