use std::{env, path::PathBuf, process::Command, time::Duration, vec}; use log::{error, info, warn}; use nng::options::{Options, RecvTimeout}; use prost::Message; use std::collections::HashMap; const DEFAULT_URL: &'static str = "tcp://127.0.0.1:10086"; const LISTEN_URL: &'static str = "tcp://127.0.0.1:10087"; pub mod wcf { include!("proto/wcf.rs"); } #[derive(Clone, Debug)] pub struct WeChat { pub url: String, pub wcf_path: PathBuf, pub debug: bool, pub socket: nng::Socket, pub listening: bool, pub enable_accept_firend: bool, } #[derive(Clone, Debug)] pub struct UserInfo { pub wxid: String, pub name: String, pub mobile: String, pub home: String, } impl Default for WeChat { fn default() -> Self { WeChat::new(false) } } impl WeChat { pub fn new(debug: bool) -> Self { let path = env::current_dir().unwrap().join("lib").join("wcf.exe"); let _ = start(path.clone(), debug); let socket = connect(&DEFAULT_URL).unwrap(); WeChat { url: String::from(DEFAULT_URL), wcf_path: path, debug, socket, listening: false, enable_accept_firend: false, } } } fn start(wcf_path: PathBuf, debug: bool) -> Result<(), Box> { let mut args = vec!["start"]; if debug { args.push("debug"); } info!( "wcf_path: {}, debug: {}", wcf_path.clone().to_str().unwrap(), debug ); let _ = match Command::new(wcf_path.to_str().unwrap()).args(args).output() { Ok(output) => output, Err(e) => { error!("命令行启动失败: {}", e); return Err("服务启动失败".into()); } }; Ok(()) } pub fn stop(wechat: &mut WeChat) -> Result<(), Box> { let _ = disable_listen(wechat); wechat.socket.close(); let output = Command::new(wechat.wcf_path.to_str().unwrap()) .args(["stop"]) .output(); let _output = match output { Ok(output) => output, Err(e) => { error!("服务停止失败: {}", e); return Err("服务停止失败".into()); } }; info!("服务已停止: {}", wechat.url); Ok(()) } fn connect(url: &str) -> Result> { let client = match nng::Socket::new(nng::Protocol::Pair1) { Ok(client) => client, Err(e) => { error!("Socket创建失败: {}", e); return Err("连接服务失败".into()); } }; match client.set_opt::(Some(Duration::from_millis(5000))) { Ok(()) => (), Err(e) => { error!("连接参数设置失败: {}", e); return Err("连接参数设置失败".into()); } }; match client.set_opt::(Some(Duration::from_millis(5000))) { Ok(()) => (), Err(e) => { error!("连接参数设置失败: {}", e); return Err("连接参数设置失败".into()); } }; match client.dial(url) { Ok(()) => (), Err(e) => { error!("连接服务失败: {}", e); return Err("连接服务失败".into()); } }; Ok(client) } fn send_cmd( wechat: &WeChat, req: wcf::Request, ) -> Result, Box> { let mut buf = Vec::with_capacity(req.encoded_len()); match req.encode(&mut buf) { Ok(()) => (), Err(e) => { error!("序列化失败: {}", e); return Err("通信失败".into()); } }; let msg = nng::Message::from(&buf[..]); let _ = match wechat.socket.send(msg) { Ok(()) => {} Err(e) => { error!("Socket发送失败: {:?}, {}", e.0, e.1); return Err("通信失败".into()); } }; let mut msg = match wechat.socket.recv() { Ok(msg) => msg, Err(e) => { error!("Socket接收失败: {}", e); return Err("通信失败".into()); } }; // 反序列化为prost消息 let response = match wcf::Response::decode(msg.as_slice()) { Ok(res) => res, Err(e) => { error!("反序列化失败: {}", e); return Err("通信失败".into()); } }; msg.clear(); Ok(response.msg) } pub fn is_login(wechat: &WeChat) -> Result> { let req = wcf::Request { func: wcf::Functions::FuncIsLogin.into(), msg: None, }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("登录状态检查失败".into()); } }; if response.is_none() { return Ok(false); } match response.unwrap() { wcf::response::Msg::Status(status) => { return Ok(1 == status); } _ => { return Ok(false); } }; } pub fn get_self_wx_id(wechat: &mut WeChat) -> Result, Box> { let req = wcf::Request { func: wcf::Functions::FuncGetSelfWxid.into(), msg: None, }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("获取微信ID失败".into()); } }; if response.is_none() { return Ok(None); } match response.unwrap() { wcf::response::Msg::Str(wx_id) => { return Ok(Some(wx_id)); } _ => { return Ok(None); } }; } pub fn get_user_info(wechat: &mut WeChat) -> Result, Box> { let req = wcf::Request { func: wcf::Functions::FuncGetUserInfo.into(), msg: None, }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("获取用户信息失败".into()); } }; if response.is_none() { return Ok(None); } match response.unwrap() { wcf::response::Msg::Ui(user_info) => { return Ok(Some(UserInfo { wxid: user_info.wxid, name: user_info.name, mobile: user_info.mobile, home: user_info.home, })); } _ => { return Ok(None); } }; } pub fn get_contacts( wechat: &mut WeChat, ) -> Result, Box> { info!("获取联系人"); let req = wcf::Request { func: wcf::Functions::FuncGetContacts.into(), msg: None, }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("获取微信联系人失败".into()); } }; if response.is_none() { return Ok(None); } match response.unwrap() { wcf::response::Msg::Contacts(contact) => { return Ok(Some(contact)); } _ => { return Ok(None); } }; } pub fn get_db_names(wechat: &mut WeChat) -> Result, Box> { let req = wcf::Request { func: wcf::Functions::FuncGetDbNames.into(), msg: None, }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("获取微信数据库失败".into()); } }; if response.is_none() { return Ok(vec![]); } match response.unwrap() { wcf::response::Msg::Dbs(dbs) => { return Ok(dbs.names); } _ => { return Ok(vec![]); } }; } pub fn get_db_tables( wechat: &mut WeChat, db: String, ) -> Result, Box> { let req = wcf::Request { func: wcf::Functions::FuncGetDbTables.into(), msg: Some(wcf::request::Msg::Str(db)), }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("获取数据库表失败".into()); } }; if response.is_none() { return Ok(vec![]); } match response.unwrap() { wcf::response::Msg::Tables(tables) => { let tables = tables.tables; return Ok(tables); } _ => { return Ok(vec![]); } }; } pub fn exec_db_query( wechat: &mut WeChat, db: String, sql: String, ) -> Result, Box> { let req = wcf::Request { func: wcf::Functions::FuncExecDbQuery.into(), msg: Some(wcf::request::Msg::Query(wcf::DbQuery { db, sql })), }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("执行SQL失败".into()); } }; if response.is_none() { return Ok(vec![]); } match response.unwrap() { wcf::response::Msg::Rows(rows) => { let rows = rows.rows; return Ok(rows); } _ => { return Ok(vec![]); } }; } /** * @param msg: 消息内容(如果是 @ 消息则需要有跟 @ 的人数量相同的 @) * @param receiver: 消息接收人,私聊为 wxid(wxid_xxxxxxxxxxxxxx),群聊为 * roomid(xxxxxxxxxx@chatroom) * @param aters: 群聊时要 @ 的人(私聊时为空字符串),多个用逗号分隔。@所有人 用 * notify@all(必须是群主或者管理员才有权限) * @return int * @Description 发送文本消息 * @author Changhua * @example sendText(" Hello @ 某人1 @ 某人2 ", " xxxxxxxx @ chatroom ", * "wxid_xxxxxxxxxxxxx1,wxid_xxxxxxxxxxxxx2"); */ pub fn send_text( wechat: &mut WeChat, msg: String, receiver: String, aters: String, ) -> Result> { let text_msg = wcf::TextMsg { msg, receiver, aters, }; let req = wcf::Request { func: wcf::Functions::FuncSendTxt.into(), msg: Some(wcf::request::Msg::Txt(text_msg)), }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("微信消息发送失败".into()); } }; if response.is_none() { return Ok(false); } match response.unwrap() { wcf::response::Msg::Status(status) => { return Ok(1 == status); } _ => { return Ok(false); } }; } pub fn send_image( wechat: &mut WeChat, path: PathBuf, receiver: String, ) -> Result> { let image_msg = wcf::PathMsg { path: String::from(path.to_str().unwrap()), receiver, }; let req = wcf::Request { func: wcf::Functions::FuncSendImg.into(), msg: Some(wcf::request::Msg::File(image_msg)), }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("图片发送失败".into()); } }; if response.is_none() { return Ok(false); } match response.unwrap() { wcf::response::Msg::Status(status) => { return Ok(1 == status); } _ => { return Ok(false); } }; } pub fn send_file( wechat: &mut WeChat, path: PathBuf, receiver: String, ) -> Result> { let image_msg = wcf::PathMsg { path: String::from(path.to_str().unwrap()), receiver, }; let req = wcf::Request { func: wcf::Functions::FuncSendFile.into(), msg: Some(wcf::request::Msg::File(image_msg)), }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("文件发送失败".into()); } }; if response.is_none() { return Ok(false); } match response.unwrap() { wcf::response::Msg::Status(status) => { return Ok(1 == status); } _ => { return Ok(false); } }; } pub fn send_xml( wechat: &mut WeChat, xml: String, path: PathBuf, receiver: String, xml_type: i32, ) -> Result> { let xml_msg = wcf::XmlMsg { content: xml, path: String::from(path.to_str().unwrap()), receiver, r#type: xml_type, }; let req = wcf::Request { func: wcf::Functions::FuncSendXml.into(), msg: Some(wcf::request::Msg::Xml(xml_msg)), }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("微信XML消息发送失败".into()); } }; if response.is_none() { return Ok(false); } match response.unwrap() { wcf::response::Msg::Status(status) => { return Ok(1 == status); } _ => { return Ok(false); } }; } pub fn send_emotion( wechat: &mut WeChat, path: PathBuf, receiver: String, ) -> Result> { let image_msg = wcf::PathMsg { path: String::from(path.to_str().unwrap()), receiver, }; let req = wcf::Request { func: wcf::Functions::FuncSendEmotion.into(), msg: Some(wcf::request::Msg::File(image_msg)), }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("微信表情发送失败".into()); } }; if response.is_none() { return Ok(false); } match response.unwrap() { wcf::response::Msg::Status(status) => { return Ok(1 == status); } _ => { return Ok(false); } }; } pub fn enable_listen(wechat: &mut WeChat) -> Result> { if wechat.listening { return Err("消息接收服务已开启".into()); } let req = wcf::Request { func: wcf::Functions::FuncEnableRecvTxt.into(), msg: None, }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("消息接收服务启动失败".into()); } }; if response.is_none() { return Err("消息接收服务启动失败".into()); } let client = connect(LISTEN_URL).unwrap(); wechat.listening = true; Ok(client) } pub fn disable_listen(wechat: &mut WeChat) -> Result> { if !wechat.listening { return Ok(true); } let req = wcf::Request { func: wcf::Functions::FuncDisableRecvTxt.into(), msg: None, }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("消息接收服务停止失败".into()); } }; if response.is_none() { return Err("消息接收服务停止失败".into()); } wechat.listening = false; return Ok(true); } pub fn recv_msg(client: &nng::Socket) -> Result, Box> { let mut msg = match client.recv() { Ok(msg) => msg, Err(e) => { warn!("Socket消息接收失败: {}", e); return Ok(None); } }; // 反序列化为prost消息 let res = match wcf::Response::decode(msg.as_slice()) { Ok(res) => res, Err(e) => { error!("反序列化失败: {}", e); return Err("微信消息接收失败".into()); } }; msg.clear(); let res_msg = res.msg; match res_msg { Some(wcf::response::Msg::Wxmsg(msg)) => { return Ok(Some(msg)); } _ => { return Ok(None); } } } /** * 获取消息类型 * {"47": "石头剪刀布 | 表情图片", "62": "小视频", "43": "视频", "1": "文字", "10002": "撤回消息", "40": "POSSIBLEFRIEND_MSG", "10000": "红包、系统消息", "37": "好友确认", "48": "位置", "42": "名片", "49": "共享实时位置、文件、转账、链接", "3": "图片", "34": "语音", "9999": "SYSNOTICE", "52": "VOIPNOTIFY", "53": "VOIPINVITE", "51": "微信初始化", "50": "VOIPMSG"} */ pub fn get_msg_types( wechat: &mut WeChat, ) -> Result, Box> { let req = wcf::Request { func: wcf::Functions::FuncGetMsgTypes.into(), msg: None, }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("获取消息类型失败".into()); } }; if response.is_none() { return Ok(HashMap::default()); } match response.unwrap() { wcf::response::Msg::Types(msg_types) => { let types = msg_types.types; return Ok(types); } _ => { return Ok(HashMap::default()); } }; } pub fn accept_new_friend( v3: String, v4: String, scene: i32, wechat: &mut WeChat, ) -> Result> { let req = wcf::Request { func: wcf::Functions::FuncAcceptFriend.into(), msg: Some(wcf::request::Msg::V(wcf::Verification { v3, v4, scene })), }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("同意加好友请求失败".into()); } }; if response.is_none() { return Ok(false); } match response.unwrap() { wcf::response::Msg::Status(status) => { return Ok(status == 1); } _ => { return Ok(false); } }; } pub fn add_chatroom_members( roomid: String, wxids: String, wechat: &mut WeChat, ) -> Result> { let req = wcf::Request { func: wcf::Functions::FuncAddRoomMembers.into(), msg: Some(wcf::request::Msg::M(wcf::AddMembers { roomid, wxids })), }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("微信群加人失败".into()); } }; if response.is_none() { return Ok(false); } match response.unwrap() { wcf::response::Msg::Status(status) => { return Ok(status == 1); } _ => { return Ok(false); } }; } pub fn decrypt_image( src: String, dst: String, wechat: &mut WeChat, ) -> Result> { let req = wcf::Request { func: wcf::Functions::FuncDecryptImage.into(), msg: Some(wcf::request::Msg::Dec(wcf::DecPath { src, dst })), }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("图片解密失败".into()); } }; if response.is_none() { return Ok(false); } match response.unwrap() { wcf::response::Msg::Status(status) => { return Ok(status == 1); } _ => { return Ok(false); } }; } pub fn recv_transfer( wxid: String, transferid: String, wechat: &mut WeChat, ) -> Result> { let req = wcf::Request { func: wcf::Functions::FuncRecvTransfer.into(), msg: Some(wcf::request::Msg::Tf(wcf::Transfer { wxid, tid: transferid, })), }; let response = match send_cmd(wechat, req) { Ok(res) => res, Err(e) => { error!("命令发送失败: {}", e); return Err("接收转账失败".into()); } }; if response.is_none() { return Ok(false); } match response.unwrap() { wcf::response::Msg::Status(status) => { return Ok(status == 1); } _ => { return Ok(false); } }; } mod test { #[test] fn test_start_stop() { use std::thread; use std::time::Duration; let mut wechat = crate::wechat::WeChat::new(false); thread::sleep(Duration::from_secs(20)); let _ = crate::wechat::stop(&mut wechat); } #[test] fn test_is_login() { let mut wechat = crate::wechat::WeChat::default(); let is_login = crate::wechat::is_login(&mut wechat).unwrap(); println!("IsLogin: {}", is_login); } #[test] fn test_get_self_wx_id() { let mut wechat = crate::wechat::WeChat::default(); let wx_id = crate::wechat::get_self_wx_id(&mut wechat).unwrap().unwrap(); println!("WxId: {}", wx_id); } #[test] fn test_get_contacts() { let mut wechat = crate::wechat::WeChat::default(); let contacts = crate::wechat::get_contacts(&mut wechat).unwrap().unwrap(); println!("WxId: {:?}", contacts); } #[test] fn test_send_text() { let mut wechat = crate::wechat::WeChat::default(); let status = crate::wechat::send_text( &mut wechat, String::from("Hello, wcferry!"), String::from("******@chatroom"), String::from(""), ) .unwrap(); println!("Success: {}", status); } #[test] fn test_send_image() { use std::path::PathBuf; let mut wechat = crate::wechat::WeChat::default(); let status = crate::wechat::send_image( &mut wechat, PathBuf::from("C:\\Users\\Administrator\\Pictures\\1.jpg"), String::from("****@chatroom"), ) .unwrap(); println!("Success: {}", status); } #[test] fn test_recv_msg() { let mut wechat = crate::wechat::WeChat::default(); let mut socket = crate::wechat::enable_listen(&mut wechat).unwrap(); for _ in 1..100 { let msg = crate::wechat::recv_msg(&mut socket).unwrap(); println!("WxMsg: {:?}", msg); } let _ = crate::wechat::disable_listen(&mut wechat); } #[test] fn test_get_msg_types() { let mut wechat = crate::wechat::WeChat::default(); let types = crate::wechat::get_msg_types(&mut wechat); println!("{:?}", types); } #[test] fn test_accept_new_friend() { let mut wechat = crate::wechat::WeChat::default(); let v3 = String::from("v3_020b3826fd03010000000000d65613e9435fd2000000501ea9a3dba12f95f6b60a0536a1adb6b4e20a513856625d11892e0635fe745d9c7ee96937f341a860c34107c6417414e5b41e427fc3d26a6af2590a1f@stranger"); let v4 = String::from("v4_000b708f0b0400000100000000003c3767b326120d5b5795b98031641000000050ded0b020927e3c97896a09d47e6e9eac7eea28e4a39b49644b3b702b82268c1d40370261e3ae6eb543d231fbd29ee7a326598ba810316c10171871103ad967ca4d147d9f6dd8fa5ccd4986042520a1173c8138e5afe21f795ee50fecf58b4ac5269acd80028627dbf65fd17ca57c0e479fbe0392288a6f42@stranger"); let status = crate::wechat::accept_new_friend(v3, v4, 17, &mut wechat).unwrap(); println!("Status: {}", status); } #[test] fn test_add_chatroom_members() { let mut wechat = crate::wechat::WeChat::default(); let status = crate::wechat::add_chatroom_members( String::from("*****@chatroom"), String::from("****"), &mut wechat, ) .unwrap(); println!("Status: {}", status); } #[test] fn test_get_user_info() { let mut wechat = crate::wechat::WeChat::default(); let user_info = crate::wechat::get_user_info(&mut wechat).unwrap(); println!("UserInfo: {:?}", user_info); } #[test] fn test_recv_transfer() { let mut wechat = crate::wechat::WeChat::default(); let status = crate::wechat::recv_transfer( String::from("****"), String::from("1000050001202305070217704377865"), &mut wechat, ) .unwrap(); println!("Status: {}", status); } #[test] fn test_decrypt_image() { let mut wechat = crate::wechat::WeChat::default(); let status = crate::wechat::decrypt_image( String::from("C:\\Users\\Administrator\\Documents\\WeChat Files\\****\\FileStorage\\MsgAttach\\c963b851e0578c320c2966c6fc49e35c\\Image\\2023-05\\c66044e188c64452e236e53eff73324b.dat"), String::from("C:\\foo"), &mut wechat, ) .unwrap(); println!("Status: {}", status); } }