From 7ff0bbd10825de682be453c93e1e1334dbfef722 Mon Sep 17 00:00:00 2001 From: super_wang Date: Mon, 15 Jan 2024 22:27:05 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=9D=E5=AD=98=E5=9B=BE=E7=89=87=E3=80=81?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E8=AF=AD=E9=9F=B3=E3=80=81=E5=8F=91=E9=80=81?= =?UTF-8?q?=E5=8D=A1=E7=89=87=E6=B6=88=E6=81=AF=E3=80=81=E6=8B=8D=E4=B8=80?= =?UTF-8?q?=E6=8B=8D=E7=BE=A4=E5=8F=8B=E3=80=81=E9=82=80=E8=AF=B7=E7=BE=A4?= =?UTF-8?q?=E6=88=90=E5=91=98=E3=80=81=E5=9B=BE=E7=89=87=20OCR=E3=80=81?= =?UTF-8?q?=E8=BD=AC=E5=8F=91=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- clients/rust/wcferry/proto/wcf.proto | 61 +++++- clients/rust/wcferry/src/proto/wcf.rs | 130 +++++++++++- clients/rust/wcferry/src/wechat.rs | 286 +++++++++++++++++++++++++- 3 files changed, 468 insertions(+), 9 deletions(-) diff --git a/clients/rust/wcferry/proto/wcf.proto b/clients/rust/wcferry/proto/wcf.proto index 5cdc36b..4e0bedd 100644 --- a/clients/rust/wcferry/proto/wcf.proto +++ b/clients/rust/wcferry/proto/wcf.proto @@ -12,20 +12,29 @@ enum Functions { FUNC_GET_DB_NAMES = 0x13; FUNC_GET_DB_TABLES = 0x14; FUNC_GET_USER_INFO = 0x15; + FUNC_GET_AUDIO_MSG = 0x16; FUNC_SEND_TXT = 0x20; FUNC_SEND_IMG = 0x21; FUNC_SEND_FILE = 0x22; FUNC_SEND_XML = 0x23; FUNC_SEND_EMOTION = 0x24; + FUNC_SEND_RICH_TXT = 0x25; + FUNC_SEND_PAT_MSG = 0x26; + FUNC_FORWARD_MSG = 0x27; FUNC_ENABLE_RECV_TXT = 0x30; FUNC_DISABLE_RECV_TXT = 0x40; FUNC_EXEC_DB_QUERY = 0x50; FUNC_ACCEPT_FRIEND = 0x51; FUNC_RECV_TRANSFER = 0x52; FUNC_REFRESH_PYQ = 0x53; + FUNC_DOWNLOAD_ATTACH = 0x54; + FUNC_GET_CONTACT_INFO = 0x55; + FUNC_REVOKE_MSG = 0x56; FUNC_DECRYPT_IMAGE = 0x60; + FUNC_EXEC_OCR = 0x61; FUNC_ADD_ROOM_MEMBERS = 0x70; FUNC_DEL_ROOM_MEMBERS = 0x71; + FUNC_INV_ROOM_MEMBERS = 0x72; } message Request @@ -39,12 +48,17 @@ message Request PathMsg file = 5; DbQuery query = 6; Verification v = 7; - AddMembers m = 8; + MemberMgmt m = 8; // 群成员管理,添加、删除、邀请 XmlMsg xml = 9; DecPath dec = 10; Transfer tf = 11; uint64 ui64 = 12; // 64 位整数,通用 bool flag = 13; + AttachMsg att = 14; + AudioMsg am = 15; + RichText rt = 16; + PatMsg pm = 17; + ForwardMsg fm = 18; } } @@ -62,6 +76,7 @@ message Response DbTables tables = 8; // 表列表 DbRows rows = 9; // 行列表 UserInfo ui = 10; // 个人信息 + OcrMsg ocr = 11; // OCR 结果 }; } @@ -150,7 +165,7 @@ message Verification int32 scene = 3; // 添加方式:17 名片,30 扫码 } -message AddMembers +message MemberMgmt { string roomid = 1; // 要加的群ID string wxids = 2; // 要加群的人列表,逗号分隔 @@ -176,3 +191,45 @@ message Transfer string tfid = 2; // 转账id transferid string taid = 3; // Transaction id } + +message AttachMsg +{ + uint64 id = 1; // 消息 id + string thumb = 2; // 消息中的 thumb + string extra = 3; // 消息中的 extra +} + +message AudioMsg +{ + uint64 id = 1; // 语音消息 id + string dir = 2; // 存放目录 +} + +message RichText +{ + string name = 1; // 显示名字 + string account = 2; // 公众号 id + string title = 3; // 标题 + string digest = 4; // 摘要 + string url = 5; // 链接 + string thumburl = 6; // 缩略图 + string receiver = 7; // 接收人 +} + +message PatMsg +{ + string roomid = 1; // 群 id + string wxid = 2; // wxid +} + +message OcrMsg +{ + int32 status = 1; // 状态 + string result = 2; // 结果 +} + +message ForwardMsg +{ + uint64 id = 1; // 待转发消息 ID + string receiver = 2; // 转发接收目标,群为 roomId,个人为 wxid +} diff --git a/clients/rust/wcferry/src/proto/wcf.rs b/clients/rust/wcferry/src/proto/wcf.rs index 13b282d..8a981d1 100644 --- a/clients/rust/wcferry/src/proto/wcf.rs +++ b/clients/rust/wcferry/src/proto/wcf.rs @@ -3,7 +3,10 @@ pub struct Request { #[prost(enumeration = "Functions", tag = "1")] pub func: i32, - #[prost(oneof = "request::Msg", tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13")] + #[prost( + oneof = "request::Msg", + tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18" + )] pub msg: ::core::option::Option, } /// Nested message and enum types in `Request`. @@ -23,8 +26,9 @@ pub mod request { Query(super::DbQuery), #[prost(message, tag = "7")] V(super::Verification), + /// 群成员管理,添加、删除、邀请 #[prost(message, tag = "8")] - M(super::AddMembers), + M(super::MemberMgmt), #[prost(message, tag = "9")] Xml(super::XmlMsg), #[prost(message, tag = "10")] @@ -36,6 +40,16 @@ pub mod request { Ui64(u64), #[prost(bool, tag = "13")] Flag(bool), + #[prost(message, tag = "14")] + Att(super::AttachMsg), + #[prost(message, tag = "15")] + Am(super::AudioMsg), + #[prost(message, tag = "16")] + Rt(super::RichText), + #[prost(message, tag = "17")] + Pm(super::PatMsg), + #[prost(message, tag = "18")] + Fm(super::ForwardMsg), } } #[allow(clippy::derive_partial_eq_without_eq)] @@ -43,7 +57,7 @@ pub mod request { pub struct Response { #[prost(enumeration = "Functions", tag = "1")] pub func: i32, - #[prost(oneof = "response::Msg", tags = "2, 3, 4, 5, 6, 7, 8, 9, 10")] + #[prost(oneof = "response::Msg", tags = "2, 3, 4, 5, 6, 7, 8, 9, 10, 11")] pub msg: ::core::option::Option, } /// Nested message and enum types in `Response`. @@ -78,6 +92,9 @@ pub mod response { /// 个人信息 #[prost(message, tag = "10")] Ui(super::UserInfo), + /// OCR 结果 + #[prost(message, tag = "11")] + Ocr(super::OcrMsg), } } #[allow(clippy::derive_partial_eq_without_eq)] @@ -274,7 +291,7 @@ pub struct Verification { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct AddMembers { +pub struct MemberMgmt { /// 要加的群ID #[prost(string, tag = "1")] pub roomid: ::prost::alloc::string::String, @@ -321,6 +338,84 @@ pub struct Transfer { #[prost(string, tag = "3")] pub taid: ::prost::alloc::string::String, } +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AttachMsg { + /// 消息 id + #[prost(uint64, tag = "1")] + pub id: u64, + /// 消息中的 thumb + #[prost(string, tag = "2")] + pub thumb: ::prost::alloc::string::String, + /// 消息中的 extra + #[prost(string, tag = "3")] + pub extra: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AudioMsg { + /// 语音消息 id + #[prost(uint64, tag = "1")] + pub id: u64, + /// 存放目录 + #[prost(string, tag = "2")] + pub dir: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RichText { + /// 显示名字 + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + /// 公众号 id + #[prost(string, tag = "2")] + pub account: ::prost::alloc::string::String, + /// 标题 + #[prost(string, tag = "3")] + pub title: ::prost::alloc::string::String, + /// 摘要 + #[prost(string, tag = "4")] + pub digest: ::prost::alloc::string::String, + /// 链接 + #[prost(string, tag = "5")] + pub url: ::prost::alloc::string::String, + /// 缩略图 + #[prost(string, tag = "6")] + pub thumburl: ::prost::alloc::string::String, + /// 接收人 + #[prost(string, tag = "7")] + pub receiver: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PatMsg { + /// 群 id + #[prost(string, tag = "1")] + pub roomid: ::prost::alloc::string::String, + /// wxid + #[prost(string, tag = "2")] + pub wxid: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct OcrMsg { + /// 状态 + #[prost(int32, tag = "1")] + pub status: i32, + /// 结果 + #[prost(string, tag = "2")] + pub result: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ForwardMsg { + /// 待转发消息 ID + #[prost(uint64, tag = "1")] + pub id: u64, + /// 转发接收目标,群为 roomId,个人为 wxid + #[prost(string, tag = "2")] + pub receiver: ::prost::alloc::string::String, +} #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] pub enum Functions { @@ -332,20 +427,29 @@ pub enum Functions { FuncGetDbNames = 19, FuncGetDbTables = 20, FuncGetUserInfo = 21, + FuncGetAudioMsg = 22, FuncSendTxt = 32, FuncSendImg = 33, FuncSendFile = 34, FuncSendXml = 35, FuncSendEmotion = 36, + FuncSendRichTxt = 37, + FuncSendPatMsg = 38, + FuncForwardMsg = 39, FuncEnableRecvTxt = 48, FuncDisableRecvTxt = 64, FuncExecDbQuery = 80, FuncAcceptFriend = 81, FuncRecvTransfer = 82, FuncRefreshPyq = 83, + FuncDownloadAttach = 84, + FuncGetContactInfo = 85, + FuncRevokeMsg = 86, FuncDecryptImage = 96, + FuncExecOcr = 97, FuncAddRoomMembers = 112, FuncDelRoomMembers = 113, + FuncInvRoomMembers = 114, } impl Functions { /// String value of the enum field names used in the ProtoBuf definition. @@ -362,20 +466,29 @@ impl Functions { Functions::FuncGetDbNames => "FUNC_GET_DB_NAMES", Functions::FuncGetDbTables => "FUNC_GET_DB_TABLES", Functions::FuncGetUserInfo => "FUNC_GET_USER_INFO", + Functions::FuncGetAudioMsg => "FUNC_GET_AUDIO_MSG", Functions::FuncSendTxt => "FUNC_SEND_TXT", Functions::FuncSendImg => "FUNC_SEND_IMG", Functions::FuncSendFile => "FUNC_SEND_FILE", Functions::FuncSendXml => "FUNC_SEND_XML", Functions::FuncSendEmotion => "FUNC_SEND_EMOTION", + Functions::FuncSendRichTxt => "FUNC_SEND_RICH_TXT", + Functions::FuncSendPatMsg => "FUNC_SEND_PAT_MSG", + Functions::FuncForwardMsg => "FUNC_FORWARD_MSG", Functions::FuncEnableRecvTxt => "FUNC_ENABLE_RECV_TXT", Functions::FuncDisableRecvTxt => "FUNC_DISABLE_RECV_TXT", Functions::FuncExecDbQuery => "FUNC_EXEC_DB_QUERY", Functions::FuncAcceptFriend => "FUNC_ACCEPT_FRIEND", Functions::FuncRecvTransfer => "FUNC_RECV_TRANSFER", Functions::FuncRefreshPyq => "FUNC_REFRESH_PYQ", + Functions::FuncDownloadAttach => "FUNC_DOWNLOAD_ATTACH", + Functions::FuncGetContactInfo => "FUNC_GET_CONTACT_INFO", + Functions::FuncRevokeMsg => "FUNC_REVOKE_MSG", Functions::FuncDecryptImage => "FUNC_DECRYPT_IMAGE", + Functions::FuncExecOcr => "FUNC_EXEC_OCR", Functions::FuncAddRoomMembers => "FUNC_ADD_ROOM_MEMBERS", Functions::FuncDelRoomMembers => "FUNC_DEL_ROOM_MEMBERS", + Functions::FuncInvRoomMembers => "FUNC_INV_ROOM_MEMBERS", } } /// Creates an enum from field names used in the ProtoBuf definition. @@ -389,20 +502,29 @@ impl Functions { "FUNC_GET_DB_NAMES" => Some(Self::FuncGetDbNames), "FUNC_GET_DB_TABLES" => Some(Self::FuncGetDbTables), "FUNC_GET_USER_INFO" => Some(Self::FuncGetUserInfo), + "FUNC_GET_AUDIO_MSG" => Some(Self::FuncGetAudioMsg), "FUNC_SEND_TXT" => Some(Self::FuncSendTxt), "FUNC_SEND_IMG" => Some(Self::FuncSendImg), "FUNC_SEND_FILE" => Some(Self::FuncSendFile), "FUNC_SEND_XML" => Some(Self::FuncSendXml), "FUNC_SEND_EMOTION" => Some(Self::FuncSendEmotion), + "FUNC_SEND_RICH_TXT" => Some(Self::FuncSendRichTxt), + "FUNC_SEND_PAT_MSG" => Some(Self::FuncSendPatMsg), + "FUNC_FORWARD_MSG" => Some(Self::FuncForwardMsg), "FUNC_ENABLE_RECV_TXT" => Some(Self::FuncEnableRecvTxt), "FUNC_DISABLE_RECV_TXT" => Some(Self::FuncDisableRecvTxt), "FUNC_EXEC_DB_QUERY" => Some(Self::FuncExecDbQuery), "FUNC_ACCEPT_FRIEND" => Some(Self::FuncAcceptFriend), "FUNC_RECV_TRANSFER" => Some(Self::FuncRecvTransfer), "FUNC_REFRESH_PYQ" => Some(Self::FuncRefreshPyq), + "FUNC_DOWNLOAD_ATTACH" => Some(Self::FuncDownloadAttach), + "FUNC_GET_CONTACT_INFO" => Some(Self::FuncGetContactInfo), + "FUNC_REVOKE_MSG" => Some(Self::FuncRevokeMsg), "FUNC_DECRYPT_IMAGE" => Some(Self::FuncDecryptImage), + "FUNC_EXEC_OCR" => Some(Self::FuncExecOcr), "FUNC_ADD_ROOM_MEMBERS" => Some(Self::FuncAddRoomMembers), "FUNC_DEL_ROOM_MEMBERS" => Some(Self::FuncDelRoomMembers), + "FUNC_INV_ROOM_MEMBERS" => Some(Self::FuncInvRoomMembers), _ => None, } } diff --git a/clients/rust/wcferry/src/wechat.rs b/clients/rust/wcferry/src/wechat.rs index 91eb843..d2397ba 100644 --- a/clients/rust/wcferry/src/wechat.rs +++ b/clients/rust/wcferry/src/wechat.rs @@ -673,6 +673,7 @@ pub fn accept_new_friend( }; } +/* 添加群成员 */ pub fn add_chatroom_members( roomid: String, wxids: String, @@ -680,7 +681,7 @@ pub fn add_chatroom_members( ) -> Result> { let req = wcf::Request { func: wcf::Functions::FuncAddRoomMembers.into(), - msg: Some(wcf::request::Msg::M(wcf::AddMembers { roomid, wxids })), + msg: Some(wcf::request::Msg::M(wcf::MemberMgmt { roomid, wxids })), }; let response = match send_cmd(wechat, req) { Ok(res) => res, @@ -702,6 +703,37 @@ pub fn add_chatroom_members( }; } +/* 邀请群成员 */ +pub fn inv_chatroom_members( + roomid: String, + wxids: String, + wechat: &mut WeChat, +) -> Result> { + let req = wcf::Request { + func: wcf::Functions::FuncInvRoomMembers.into(), + msg: Some(wcf::request::Msg::M(wcf::MemberMgmt { 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 del_chatroom_members( roomid: String, wxids: String, @@ -709,7 +741,7 @@ pub fn del_chatroom_members( ) -> Result> { let req = wcf::Request { func: wcf::Functions::FuncDelRoomMembers.into(), - msg: Some(wcf::request::Msg::M(wcf::AddMembers { roomid, wxids })), + msg: Some(wcf::request::Msg::M(wcf::MemberMgmt { roomid, wxids })), }; let response = match send_cmd(wechat, req) { Ok(res) => res, @@ -820,6 +852,199 @@ pub fn refresh_pyq(id: u64, wechat: &mut WeChat) -> Result Result> { + let req = wcf::Request { + func: wcf::Functions::FuncDownloadAttach.into(), + msg: Some(wcf::request::Msg::Att(wcf::AttachMsg { id, thumb, extra })), + }; + 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 get_audio_msg( + id: u64, + dir: String, + wechat: &mut WeChat, +) -> Result> { + let req = wcf::Request { + func: wcf::Functions::FuncGetAudioMsg.into(), + msg: Some(wcf::request::Msg::Am(wcf::AudioMsg { id, dir })), + }; + 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 sned_rich_text( + name: String, + account: String, + title: String, + digest: String, + url: String, + thumburl: String, + receiver: String, + wechat: &mut WeChat, +) -> Result> { + let req = wcf::Request { + func: wcf::Functions::FuncSendRichTxt.into(), + msg: Some(wcf::request::Msg::Rt(wcf::RichText { + name, + account, + title, + digest, + url, + thumburl, + receiver, + })), + }; + 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 sned_pat_msg( + roomid: String, + wxid: String, + wechat: &mut WeChat, +) -> Result> { + let req = wcf::Request { + func: wcf::Functions::FuncSendPatMsg.into(), + msg: Some(wcf::request::Msg::Pm(wcf::PatMsg { roomid, wxid })), + }; + 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); + } + }; +} + +/** OCR */ +pub fn exec_ocr( + path: PathBuf, + wechat: &mut WeChat, +) -> Result, Box> { + let req = wcf::Request { + func: wcf::Functions::FuncExecOcr.into(), + msg: Some(wcf::request::Msg::Str(String::from(path.to_str().unwrap()))), + }; + let response = match send_cmd(wechat, req) { + Ok(res) => res, + Err(e) => { + error!("命令发送失败: {}", e); + return Err("OCR失败".into()); + } + }; + if response.is_none() { + return Ok(None); + } + match response.unwrap() { + wcf::response::Msg::Ocr(msg) => { + return Ok(Some(msg)); + } + _ => { + return Ok(None); + } + }; +} + +/** 转发消息 */ +pub fn forward_msg( + id: u64, + receiver: String, + wechat: &mut WeChat, +) -> Result> { + let req = wcf::Request { + func: wcf::Functions::FuncForwardMsg.into(), + msg: Some(wcf::request::Msg::Fm(wcf::ForwardMsg { id, receiver })), + }; + 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] @@ -885,7 +1110,7 @@ mod test { let mut wechat = crate::wechat::WeChat::default(); let mut socket = crate::wechat::enable_listen(&mut wechat).unwrap(); for _index in 0..5 { - let _ = crate::wechat::refresh_pyq(0, &mut wechat); + // let _ = crate::wechat::refresh_pyq(0, &mut wechat); let msg = crate::wechat::recv_msg(&mut socket).unwrap(); println!("WxMsg: {:?}", msg); println!("--------------------------------------------------"); @@ -964,4 +1189,59 @@ mod test { .unwrap(); println!("Status: {}", status); } + + #[test] + fn test_attach_msg() { + let mut wechat = crate::wechat::WeChat::default(); + let status = + crate::wechat::attach_msg(1, String::from(""), String::from(""), &mut wechat).unwrap(); + println!("Status: {}", status); + } + + #[test] + fn test_get_audio_msg() { + let mut wechat = crate::wechat::WeChat::default(); + let status = + crate::wechat::get_audio_msg(7072630968956565993, String::from("C:/"), &mut wechat) + .unwrap(); + println!("Status: {}", status); + } + + #[test] + fn test_send_pat_msg() { + let mut wechat = crate::wechat::WeChat::default(); + let status = crate::wechat::sned_pat_msg( + String::from("21262247140@chatroom"), + String::from("jingmo0614"), + &mut wechat, + ) + .unwrap(); + println!("Status: {}", status); + } + + #[test] + fn test_exec_ocr() { + use std::path::PathBuf; + + let mut wechat = crate::wechat::WeChat::default(); + let ocr_msg = crate::wechat::exec_ocr( + PathBuf::from("C:\\Users\\Administrator\\Pictures\\5.png"), + &mut wechat, + ) + .unwrap() + .unwrap(); + println!("ocr: {:?}", ocr_msg); + } + + #[test] + fn test_forward_msg() { + let mut wechat = crate::wechat::WeChat::default(); + let status = crate::wechat::forward_msg( + 5744142522200397761, + String::from("21262247140@chatroom"), + &mut wechat, + ) + .unwrap(); + println!("Status: {}", status); + } }