diff --git a/clients/rust/wcferry/Cargo.toml b/clients/rust/wcferry/Cargo.toml index d38aea0..1bed567 100644 --- a/clients/rust/wcferry/Cargo.toml +++ b/clients/rust/wcferry/Cargo.toml @@ -12,6 +12,7 @@ nng = "1.0.1" serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } log = "0.4.17" +hex = "0.4" [build-dependencies] tonic-build = "0.8.4" diff --git a/clients/rust/wcferry/build.rs b/clients/rust/wcferry/build.rs index 5946ae7..a843baa 100644 --- a/clients/rust/wcferry/build.rs +++ b/clients/rust/wcferry/build.rs @@ -3,7 +3,7 @@ .build_client(true) .build_server(false) .out_dir("src/proto") - .compile(&["proto/wcf.proto"], &["."]) + .compile(&["proto/wcf.proto", "proto/roomdata.proto"], &["."]) .expect("failed to compile protos"); Ok(()) } diff --git a/clients/rust/wcferry/proto/roomdata.proto b/clients/rust/wcferry/proto/roomdata.proto new file mode 100644 index 0000000..f0adec2 --- /dev/null +++ b/clients/rust/wcferry/proto/roomdata.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; +package roomdata; + +message RoomData { + + message RoomMember { + string wxid = 1; + string name = 2; + int32 state = 3; + } + + repeated RoomMember members = 1; + + int32 field_2 = 2; + int32 field_3 = 3; + int32 field_4 = 4; + int32 room_capacity = 5; + int32 field_6 = 6; + int64 field_7 = 7 [jstype = JS_STRING]; + int64 field_8 = 8 [jstype = JS_STRING]; +} diff --git a/clients/rust/wcferry/src/proto/roomdata.rs b/clients/rust/wcferry/src/proto/roomdata.rs new file mode 100644 index 0000000..c62efc7 --- /dev/null +++ b/clients/rust/wcferry/src/proto/roomdata.rs @@ -0,0 +1,33 @@ +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RoomData { + #[prost(message, repeated, tag = "1")] + pub members: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "2")] + pub field_2: i32, + #[prost(int32, tag = "3")] + pub field_3: i32, + #[prost(int32, tag = "4")] + pub field_4: i32, + #[prost(int32, tag = "5")] + pub room_capacity: i32, + #[prost(int32, tag = "6")] + pub field_6: i32, + #[prost(int64, tag = "7")] + pub field_7: i64, + #[prost(int64, tag = "8")] + pub field_8: i64, +} +/// Nested message and enum types in `RoomData`. +pub mod room_data { + #[allow(clippy::derive_partial_eq_without_eq)] + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct RoomMember { + #[prost(string, tag = "1")] + pub wxid: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub name: ::prost::alloc::string::String, + #[prost(int32, tag = "3")] + pub state: i32, + } +} diff --git a/clients/rust/wcferry/src/wechat.rs b/clients/rust/wcferry/src/wechat.rs index d2397ba..0ccfba4 100644 --- a/clients/rust/wcferry/src/wechat.rs +++ b/clients/rust/wcferry/src/wechat.rs @@ -12,6 +12,10 @@ pub mod wcf { include!("proto/wcf.rs"); } +pub mod roomdata { + include!("proto/roomdata.rs"); +} + #[derive(Clone, Debug)] pub struct WeChat { pub url: String, @@ -30,6 +34,46 @@ pub struct UserInfo { pub home: String, } +#[derive(Clone, Debug, Default)] +pub struct ChatRoom { + /// 群聊ID + pub room_id: String, + /// 群聊成员 + pub room_data: roomdata::RoomData, + /// 群聊头像 + pub room_head_img_url: Option, + /// 公告 + pub room_announcement: Option, +} + +#[derive(Clone, Debug, Default)] +pub struct ContactInfo { + /// 微信ID + pub wxid: String, + /// 微信号 + pub alias: Option, + /// 删除标记 + pub del_flag: u8, + /// 类型 + pub contact_type: u8, + /// 备注 + pub remark: Option, + /// 昵称 + pub nick_name: Option, + /// 昵称拼音首字符 + pub py_initial: Option, + /// 昵称全拼 + pub quan_pin: Option, + /// 备注拼音首字母 + pub remark_py_initial: Option, + /// 备注全拼 + pub remark_quan_pin: Option, + /// 小头像 + pub small_head_url: Option, + /// 大头像 + pub big_head_url: Option, +} + impl Default for WeChat { fn default() -> Self { WeChat::new(false) @@ -268,6 +312,218 @@ pub fn get_contacts( }; } +pub fn query_all_contact_info( + wechat: &mut WeChat, +) -> Result, Box> { + let contacts = crate::wechat::exec_db_query( + wechat, + String::from("MicroMsg.db"), + String::from("select * from Contact"), + ) + .unwrap(); + if contacts.len() == 0 { + return Ok(vec![]); + } else { + let mut contact_list: Vec = vec![]; + for contact in contacts.into_iter() { + let mut contact_info = ContactInfo::default(); + let fields = &contact.fields; + for field in fields.into_iter() { + if field.column.eq("UserName") { + contact_info.wxid = String::from_utf8(field.content.clone()).unwrap(); + } else if field.column.eq("Alias") { + contact_info.alias = Some(String::from_utf8(field.content.clone()).unwrap()); + } else if field.column.eq("DelFlag") { + contact_info.del_flag = *field.content.get(0).unwrap(); + } else if field.column.eq("Type") { + contact_info.contact_type = *field.content.get(0).unwrap(); + } else if field.column.eq("Remark") { + contact_info.remark = Some(String::from_utf8(field.content.clone()).unwrap()); + } else if field.column.eq("NickName") { + contact_info.nick_name = + Some(String::from_utf8(field.content.clone()).unwrap()); + } else if field.column.eq("PYInitial") { + contact_info.py_initial = + Some(String::from_utf8(field.content.clone()).unwrap()); + } else if field.column.eq("QuanPin") { + contact_info.quan_pin = Some(String::from_utf8(field.content.clone()).unwrap()); + } else if field.column.eq("RemarkPYInitial") { + contact_info.remark_py_initial = + Some(String::from_utf8(field.content.clone()).unwrap()); + } else if field.column.eq("RemarkQuanPin") { + contact_info.remark_quan_pin = + Some(String::from_utf8(field.content.clone()).unwrap()); + } + } + if !contact_info.wxid.is_empty() { + let contact_heads = crate::wechat::exec_db_query( + wechat, + String::from("MicroMsg.db"), + String::from(format!( + "select * from ContactHeadImgUrl where usrName = '{}'", + contact_info.wxid + )), + ) + .unwrap(); + if contact_heads.len() > 0 { + let contact_head = contact_heads.get(0).unwrap(); + let head_fields = &contact_head.fields; + for field in head_fields.into_iter() { + if field.column.eq("smallHeadImgUrl") { + contact_info.small_head_url = + Some(String::from_utf8(field.content.clone()).unwrap()); + } else if field.column.eq("bigHeadImgUrl") { + contact_info.big_head_url = + Some(String::from_utf8(field.content.clone()).unwrap()); + } + } + } + } + contact_list.push(contact_info); + } + + Ok(contact_list) + } +} + +pub fn query_contact_info( + wechat: &mut WeChat, + wxid: String, +) -> Result, Box> { + let contacts = crate::wechat::exec_db_query( + wechat, + String::from("MicroMsg.db"), + String::from(format!( + "select * from Contact where UserName = '{}'", + wxid.clone() + )), + ) + .unwrap(); + if contacts.len() == 0 { + return Ok(None); + } else { + let contact = contacts.get(0).unwrap(); + let mut contact_info = ContactInfo::default(); + let fields = &contact.fields; + for field in fields.into_iter() { + if field.column.eq("UserName") { + contact_info.wxid = String::from_utf8(field.content.clone()).unwrap(); + } else if field.column.eq("Alias") { + contact_info.alias = Some(String::from_utf8(field.content.clone()).unwrap()); + } else if field.column.eq("DelFlag") { + contact_info.del_flag = *field.content.get(0).unwrap(); + } else if field.column.eq("Type") { + contact_info.contact_type = *field.content.get(0).unwrap(); + } else if field.column.eq("Remark") { + contact_info.remark = Some(String::from_utf8(field.content.clone()).unwrap()); + } else if field.column.eq("NickName") { + contact_info.nick_name = Some(String::from_utf8(field.content.clone()).unwrap()); + } else if field.column.eq("PYInitial") { + contact_info.py_initial = Some(String::from_utf8(field.content.clone()).unwrap()); + } else if field.column.eq("QuanPin") { + contact_info.quan_pin = Some(String::from_utf8(field.content.clone()).unwrap()); + } else if field.column.eq("RemarkPYInitial") { + contact_info.remark_py_initial = + Some(String::from_utf8(field.content.clone()).unwrap()); + } else if field.column.eq("RemarkQuanPin") { + contact_info.remark_quan_pin = + Some(String::from_utf8(field.content.clone()).unwrap()); + } + } + let contact_heads = crate::wechat::exec_db_query( + wechat, + String::from("MicroMsg.db"), + String::from(format!( + "select * from ContactHeadImgUrl where usrName = '{}'", + wxid + )), + ) + .unwrap(); + if contact_heads.len() > 0 { + let contact_head = contact_heads.get(0).unwrap(); + let head_fields = &contact_head.fields; + for field in head_fields.into_iter() { + if field.column.eq("smallHeadImgUrl") { + contact_info.small_head_url = + Some(String::from_utf8(field.content.clone()).unwrap()); + } else if field.column.eq("bigHeadImgUrl") { + contact_info.big_head_url = + Some(String::from_utf8(field.content.clone()).unwrap()); + } + } + } + Ok(Some(contact_info)) + } +} + +pub fn query_chat_room_info( + wechat: &mut WeChat, + wxid: String, +) -> Result, Box> { + let contacts = crate::wechat::exec_db_query( + wechat, + String::from("MicroMsg.db"), + String::from(format!( + "select * from ChatRoom where ChatRoomName = '{}'", + wxid.clone() + )), + ) + .unwrap(); + if contacts.len() == 0 { + return Ok(None); + } else { + let contact = contacts.get(0).unwrap(); + let mut chat_room = ChatRoom::default(); + let fields = &contact.fields; + for field in fields.into_iter() { + if field.column.eq("ChatRoomName") { + chat_room.room_id = String::from_utf8(field.content.clone()).unwrap(); + } else if field.column.eq("RoomData") { + chat_room.room_data = roomdata::RoomData::decode(field.content.as_slice()).unwrap(); + } + } + let contact_heads = crate::wechat::exec_db_query( + wechat, + String::from("MicroMsg.db"), + String::from(format!( + "select * from ContactHeadImgUrl where usrName = '{}'", + wxid + )), + ) + .unwrap(); + if contact_heads.len() > 0 { + let contact_head = contact_heads.get(0).unwrap(); + let head_fields = &contact_head.fields; + for field in head_fields.into_iter() { + if field.column.eq("smallHeadImgUrl") { + chat_room.room_head_img_url = + Some(String::from_utf8(field.content.clone()).unwrap()); + } + } + } + let contact_heads = crate::wechat::exec_db_query( + wechat, + String::from("MicroMsg.db"), + String::from(format!( + "select * from ChatRoomInfo where ChatRoomName = '{}'", + wxid + )), + ) + .unwrap(); + if contact_heads.len() > 0 { + let contact_head = contact_heads.get(0).unwrap(); + let head_fields = &contact_head.fields; + for field in head_fields.into_iter() { + if field.column.eq("Announcement") { + chat_room.room_announcement = + Some(String::from_utf8(field.content.clone()).unwrap()); + } + } + } + Ok(Some(chat_room)) + } +} + pub fn get_db_names(wechat: &mut WeChat) -> Result, Box> { let req = wcf::Request { func: wcf::Functions::FuncGetDbNames.into(), @@ -1064,6 +1320,170 @@ mod test { println!("IsLogin: {}", is_login); } + #[test] + fn test_get_db_names() { + let mut wechat = crate::wechat::WeChat::default(); + let dbs = crate::wechat::get_db_names(&mut wechat).unwrap(); + println!("dbs: {:?}", dbs); + } + + #[test] + fn test_get_db_tables() { + let mut wechat = crate::wechat::WeChat::default(); + let tables: Vec = + crate::wechat::get_db_tables(&mut wechat, String::from("MicroMsg.db")).unwrap(); + println!("tables: {:?}", tables); + } + + #[test] + fn test_exec_db_query_contact() { + use hex; + pub mod roomdata { + include!("proto/roomdata.rs"); + } + + let mut wechat = crate::wechat::WeChat::default(); + let chat_rooms = crate::wechat::exec_db_query( + &mut wechat, + String::from("MicroMsg.db"), + String::from("select * from Contact"), + ) + .unwrap(); + for item in chat_rooms.into_iter() { + let fields = item.fields; + println!("-------分割线-------"); + for field in fields.into_iter() { + if field.column.eq("Reserved7") || field.column.eq("ExtraBuf") { + let c = hex::encode(&field.content); + print!("{}: {:?}, ", field.column, c); + } else { + print!( + "{}: {}, ", + field.column, + String::from_utf8(field.content).unwrap() + ); + } + } + println!(); + } + } + + #[test] + fn test_exec_db_query_chatroom() { + pub mod roomdata { + include!("proto/roomdata.rs"); + } + use prost::Message; + + let mut wechat = crate::wechat::WeChat::default(); + let chat_rooms = crate::wechat::exec_db_query( + &mut wechat, + String::from("MicroMsg.db"), + String::from("select * from ChatRoom where ChatRoomName = '21262247140@chatroom'"), + ) + .unwrap(); + for item in chat_rooms.into_iter() { + let fields = item.fields; + for field in fields.into_iter() { + if field.column.eq("RoomData") { + let c = roomdata::RoomData::decode(field.content.as_slice()).unwrap(); + println!("{}: {:?}", field.column, c); + } else { + println!( + "{}: {}", + field.column, + String::from_utf8(field.content).unwrap() + ); + } + } + } + } + + #[test] + fn test_query_chat_room_info() { + let mut wechat = crate::wechat::WeChat::default(); + let chat_room = + crate::wechat::query_chat_room_info(&mut wechat, String::from("48585852775@chatroom")) + .unwrap(); + print!("chat_room: {:?}", chat_room); + } + + #[test] + fn test_exec_db_query_chatroom_info() { + pub mod roomdata { + include!("proto/roomdata.rs"); + } + use prost::Message; + + let mut wechat = crate::wechat::WeChat::default(); + let chat_rooms = crate::wechat::exec_db_query( + &mut wechat, + String::from("MicroMsg.db"), + String::from("select * from ChatRoomInfo where ChatRoomName = '21262247140@chatroom'"), + ) + .unwrap(); + for item in chat_rooms.into_iter() { + let fields = item.fields; + for field in fields.into_iter() { + if field.column.eq("RoomData") { + let c = roomdata::RoomData::decode(field.content.as_slice()).unwrap(); + println!("{}: {:?}", field.column, c); + } else { + println!( + "{}: {}", + field.column, + String::from_utf8(field.content).unwrap() + ); + } + } + } + } + + #[test] + fn test_exec_db_query_contact_head_img_url() { + pub mod roomdata { + include!("proto/roomdata.rs"); + } + use prost::Message; + + let mut wechat = crate::wechat::WeChat::default(); + let chat_rooms = crate::wechat::exec_db_query( + &mut wechat, + String::from("MicroMsg.db"), + String::from( + "select * from ContactHeadImgUrl where usrName = '25984981691552386@openim'", + ), + ) + .unwrap(); + for item in chat_rooms.into_iter() { + println!("-------分割线-------"); + let fields = item.fields; + for field in fields.into_iter() { + if field.column.eq("RoomData") { + let c = roomdata::RoomData::decode(field.content.as_slice()).unwrap(); + println!("{}: {:?}", field.column, c); + } else { + println!( + "{}: {}", + field.column, + String::from_utf8(field.content).unwrap() + ); + } + } + } + } + + #[test] + fn test_query_contact_info() { + let mut wechat = crate::wechat::WeChat::default(); + let contact_info = crate::wechat::query_contact_info( + &mut wechat, + String::from("25984981691552386@openim"), + ) + .unwrap(); + println!("contact_info: {:?}", contact_info); + } + #[test] fn test_get_self_wx_id() { let mut wechat = crate::wechat::WeChat::default(); @@ -1083,9 +1503,9 @@ mod test { let mut wechat = crate::wechat::WeChat::default(); let status = crate::wechat::send_text( &mut wechat, - String::from("Hello, wcferry!"), - String::from("filehelper"), - String::from(""), + String::from("艾特测试"), + String::from("21262247140@chatroom"), + String::from("@jingmo0614"), ) .unwrap(); println!("Success: {}", status);