Merge pull request #116 from supermoonie/master

保存图片、保存语音、发送卡片消息、拍一拍群友、邀请群成员、图片 OCR、转发消息
This commit is contained in:
Changhua 2024-01-25 17:17:13 +08:00 committed by GitHub
commit 0cf440ab6b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 468 additions and 9 deletions

View File

@ -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
}

View File

@ -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<request::Msg>,
}
/// 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<response::Msg>,
}
/// 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,
}
}

View File

@ -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<bool, Box<dyn std::error::Error>> {
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<bool, Box<dyn std::error::Error>> {
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<bool, Box<dyn std::error::Error>> {
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<bool, Box<dyn std::er
};
}
/** 保存附件 */
pub fn attach_msg(
id: u64,
thumb: String,
extra: String,
wechat: &mut WeChat,
) -> Result<bool, Box<dyn std::error::Error>> {
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<bool, Box<dyn std::error::Error>> {
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<bool, Box<dyn std::error::Error>> {
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<bool, Box<dyn std::error::Error>> {
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<Option<wcf::OcrMsg>, Box<dyn std::error::Error>> {
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<bool, Box<dyn std::error::Error>> {
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);
}
}