From 6e296bb6bed630789e3c383d68a24a455cca8ed3 Mon Sep 17 00:00:00 2001 From: LaoLittle Date: Thu, 8 Dec 2022 17:54:55 +0800 Subject: [PATCH] message receipt, forward message, face, new error, rename, new api for sending forward message, add group invite api --- Cargo.lock | 32 +++--- Cargo.toml | 4 +- src/client/mod.rs | 26 +++-- src/contact/friend.rs | 11 +- src/contact/group.rs | 25 +++++ src/contact/member.rs | 53 ++++----- src/error.rs | 2 + src/event/listener.rs | 1 + src/event/mod.rs | 6 +- src/message/face.rs | 53 +++++++++ src/message/ffi.rs | 34 ++++-- src/message/forward.rs | 234 +++++++++++++++++++++++++++++++++++++++ src/message/macros.rs | 1 + src/message/meta.rs | 28 +++++ src/message/mod.rs | 23 +++- src/plugin/ffi/friend.rs | 14 +-- src/plugin/ffi/group.rs | 58 +++++++++- src/plugin/ffi/mod.rs | 29 ++++- src/service/login.rs | 6 +- 19 files changed, 540 insertions(+), 100 deletions(-) create mode 100644 src/message/face.rs create mode 100644 src/message/forward.rs create mode 100644 src/message/macros.rs diff --git a/Cargo.lock b/Cargo.lock index ca6d878..ff6e99f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,7 +36,7 @@ dependencies = [ [[package]] name = "atri_bot" -version = "0.6.0" +version = "0.7.0" dependencies = [ "async-trait", "atri_ffi", @@ -65,9 +65,9 @@ dependencies = [ [[package]] name = "atri_ffi" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55c5976c568117a666aa3bcb620c88b3e6fbb8bd5256e5259f3aa4014d294b42" +checksum = "d032cf187d0f1c811c115c95e11b47d9c7a0dca08fbc7f9a8b00cb9e73ace4eb" [[package]] name = "autocfg" @@ -527,9 +527,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.137" +version = "0.2.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" +checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" [[package]] name = "libloading" @@ -935,18 +935,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.148" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc" +checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.148" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c" +checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4" dependencies = [ "proc-macro2", "quote", @@ -1036,9 +1036,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.104" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae548ec36cf198c0ef7710d3c230987c2d6d7bd98ad6edc0274462724c585ce" +checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908" dependencies = [ "proc-macro2", "quote", @@ -1119,9 +1119,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3" +checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" dependencies = [ "autocfg", "bytes", @@ -1134,7 +1134,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys", ] [[package]] @@ -1243,9 +1243,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-ident" diff --git a/Cargo.toml b/Cargo.toml index f20b29b..bf0789c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "atri_bot" -version = "0.6.0" +version = "0.7.0" edition = "2021" authors = ["LaoLittle"] description = "A simple bot" @@ -60,7 +60,7 @@ version = "0.1.17" version = "0.1.0" [dependencies.atri_ffi] -version = "0.5.0" +version = "0.6.1" [profile.release] lto = true diff --git a/src/client/mod.rs b/src/client/mod.rs index a747a7c..f26543c 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -26,7 +26,7 @@ use crate::{config, global_status}; pub struct Client(Arc); impl Client { - pub async fn new(id: i64, conf: BotConfiguration) -> Self { + pub async fn new(id: i64, conf: ClientConfiguration) -> Self { let b = imp::Client::new(id, conf).await; Self(Arc::new(b)) } @@ -307,7 +307,7 @@ mod imp { use crate::channel::GlobalEventBroadcastHandler; use crate::client::info::BotAccountInfo; - use crate::client::BotConfiguration; + use crate::client::ClientConfiguration; use crate::contact::friend::Friend; use crate::contact::group::Group; @@ -322,7 +322,7 @@ mod imp { } impl Client { - pub async fn new(id: i64, conf: BotConfiguration) -> Self { + pub async fn new(id: i64, conf: ClientConfiguration) -> Self { let work_dir = conf.work_dir(id); if !work_dir.is_dir() { @@ -406,14 +406,16 @@ mod imp { .pop() .ok_or_else(|| io::Error::new(ErrorKind::AddrNotAvailable, "重连失败"))?; - if let Ok(Ok(s)) = - tokio::time::timeout(Duration::from_secs(2), socket.connect(addr)).await - { - break s; - } else { - times += 1; - warn!("连接服务器{}失败, 尝试重连({}/{})", addr, times, total); + match tokio::time::timeout(Duration::from_secs(2), socket.connect(addr)).await { + Ok(Ok(s)) => break s, + Ok(Err(e)) => warn!( + "连接服务器{}失败, 尝试重连({}/{}): {}", + addr, times, total, e + ), + Err(_) => warn!("连接服务器{}超时, 尝试重连({}/{})", addr, times, total), } + + times += 1; }) } @@ -432,12 +434,12 @@ mod imp { } } -pub struct BotConfiguration { +pub struct ClientConfiguration { pub work_dir: Option, pub version: ricq::version::Version, } -impl BotConfiguration { +impl ClientConfiguration { fn work_dir(&self, id: i64) -> PathBuf { self.work_dir .as_ref() diff --git a/src/contact/friend.rs b/src/contact/friend.rs index 2ee0c67..0acae71 100644 --- a/src/contact/friend.rs +++ b/src/contact/friend.rs @@ -1,4 +1,5 @@ use crate::error::{AtriError, AtriResult}; +use crate::message::forward::ForwardMessage; use crate::message::image::Image; use crate::message::meta::{MessageReceipt, RecallMessage}; use crate::message::MessageChain; @@ -48,7 +49,7 @@ impl Friend { map.is_some() } - pub async fn send_message(&self, chain: MessageChain) -> AtriResult { + async fn _send_message(&self, chain: MessageChain) -> AtriResult { let result = self .client() .request_client() @@ -68,6 +69,14 @@ impl Friend { result.map(MessageReceipt::from).map_err(AtriError::from) } + pub async fn send_message>(&self, msg: M) -> AtriResult { + self._send_message(msg.into()).await + } + + async fn _send_forward_message(&self, _forward: ForwardMessage) -> AtriResult { + Err(AtriError::NotSupported) + } + pub async fn upload_image(&self, image: Vec) -> AtriResult { let f = self .client() diff --git a/src/contact/group.rs b/src/contact/group.rs index 43577ae..0d5798c 100644 --- a/src/contact/group.rs +++ b/src/contact/group.rs @@ -7,6 +7,7 @@ use tracing::error; use crate::contact::member::NamedMember; use crate::error::{AtriError, AtriResult}; +use crate::message::forward::ForwardMessage; use crate::message::image::Image; use crate::message::meta::{MessageReceipt, RecallMessage}; use crate::message::MessageChain; @@ -128,6 +129,22 @@ impl Group { self._send_message(msg.into()).await } + async fn _send_forward_message(&self, forward: ForwardMessage) -> AtriResult { + self.client() + .request_client() + .send_group_forward_message(self.id(), forward.into()) + .await + .map(MessageReceipt::from) + .map_err(AtriError::from) + } + + pub async fn send_forward_message>( + &self, + msg: M, + ) -> AtriResult { + self._send_forward_message(msg.into()).await + } + async fn _upload_image(&self, image: Vec) -> AtriResult { self.client() .request_client() @@ -175,6 +192,14 @@ impl Group { self._change_name(name.into()).await } + pub async fn invite(&self, id: i64) -> AtriResult<()> { + self.client() + .request_client() + .group_invite(self.id(), id) + .await + .map_err(AtriError::from) + } + pub async fn kick>( &self, member: M, diff --git a/src/contact/member.rs b/src/contact/member.rs index e294f9a..beabcf9 100644 --- a/src/contact/member.rs +++ b/src/contact/member.rs @@ -4,10 +4,9 @@ use crate::message::at::At; use crate::message::meta::{Anonymous, MessageReceipt}; use crate::message::MessageChain; use crate::{Client, GroupMemberInfo}; -use atri_ffi::contact::{FFIMember, MemberUnion}; +use atri_ffi::contact::FFIMember; +use atri_ffi::ffi::ForFFI; use atri_ffi::ManagedCloneable; -use ricq::RQError; -use std::mem::ManuallyDrop; use std::sync::Arc; use std::time::Duration; @@ -35,45 +34,39 @@ impl Member { pub async fn send_message(&self, chain: MessageChain) -> AtriResult { match self { Self::Named(named) => named.send_message(chain).await, - Self::Anonymous(..) => Err(AtriError::Protocol(RQError::Other( - "Send message to anonymous member is not supported".into(), - ))), + Self::Anonymous(..) => Err(AtriError::NotSupported), } } +} - pub fn into_ffi(self) -> FFIMember { - match self { +impl ForFFI for Member { + type FFIValue = FFIMember; + + fn into_ffi(self) -> Self::FFIValue { + let (is_named, ma) = match self { Self::Named(named) => { let ma = ManagedCloneable::from_value(named); - FFIMember { - is_named: true, - inner: MemberUnion { - named: ManuallyDrop::new(ma), - }, - } + (true, ma) } Self::Anonymous(ano) => { let ma = ManagedCloneable::from_value(ano); - FFIMember { - is_named: false, - inner: MemberUnion { - anonymous: ManuallyDrop::new(ma), - }, - } + (false, ma) } + }; + + FFIMember { + is_named, + inner: ma, } } - pub fn from_ffi(ffi: FFIMember) -> Self { - unsafe { - if ffi.is_named { - let named: NamedMember = ManuallyDrop::into_inner(ffi.inner.named).into_value(); - Self::Named(named) - } else { - let ano: AnonymousMember = - ManuallyDrop::into_inner(ffi.inner.anonymous).into_value(); - Self::Anonymous(ano) - } + fn from_ffi(value: Self::FFIValue) -> Self { + let ma = value.inner; + + if value.is_named { + Self::Named(ma.into_value()) + } else { + Self::Anonymous(ma.into_value()) } } } diff --git a/src/error.rs b/src/error.rs index 5346ce2..ae82f7a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,6 +9,7 @@ pub enum AtriError { IO(io::Error), Protocol(ricq::RQError), Login(LoginError), + NotSupported, } impl Display for AtriError { @@ -21,6 +22,7 @@ impl Display for AtriError { } Self::PluginError(e) => Display::fmt(e, f), Self::Protocol(e) => Display::fmt(e, f), + Self::NotSupported => f.write_str("operation not supported"), } } } diff --git a/src/event/listener.rs b/src/event/listener.rs index 17d1f6d..0d10ee7 100644 --- a/src/event/listener.rs +++ b/src/event/listener.rs @@ -287,6 +287,7 @@ unsafe impl Send for Listener {} unsafe impl Sync for Listener {} +#[must_use = "if unused the Listener will immediately close"] pub struct ListenerGuard { name: Arc, closed: Arc, diff --git a/src/event/mod.rs b/src/event/mod.rs index 14899f6..28eef17 100644 --- a/src/event/mod.rs +++ b/src/event/mod.rs @@ -263,11 +263,11 @@ impl ContactSubject for FriendMessageEvent { } } -pub type ClientLoginEvent = SharedEvent; +pub type ClientLoginEvent = SharedEvent; impl ClientLoginEvent { pub fn from(bot: Client) -> Self { - Self::new(imp::BotOnlineEvent { bot }) + Self::new(imp::ClientLoginEvent { bot }) } } @@ -294,7 +294,7 @@ mod imp { pub message: MessageChain, } - pub struct BotOnlineEvent { + pub struct ClientLoginEvent { pub bot: Client, } } diff --git a/src/message/face.rs b/src/message/face.rs new file mode 100644 index 0000000..d0cb8ee --- /dev/null +++ b/src/message/face.rs @@ -0,0 +1,53 @@ +use ricq::msg::{MessageElem, PushElem}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Face { + pub index: i32, + pub name: String, +} + +impl From for Face { + fn from(ricq::msg::elem::Face { index, name }: ricq::msg::elem::Face) -> Self { + Self { index, name } + } +} + +impl From for ricq::msg::elem::Face { + fn from(Face { index, name }: Face) -> Self { + Self { index, name } + } +} + +impl PushElem for Face { + fn push_to(elem: Self, vec: &mut Vec) { + let rq: ricq::msg::elem::Face = elem.into(); + PushElem::push_to(rq, vec); + } +} + +mod ffi { + use crate::message::face::Face; + use atri_ffi::ffi::ForFFI; + use atri_ffi::message::FFIFace; + + impl ForFFI for Face { + type FFIValue = FFIFace; + + fn into_ffi(self) -> Self::FFIValue { + let Face { index, name } = self; + + FFIFace { + index, + name: name.into(), + } + } + + fn from_ffi(FFIFace { index, name }: Self::FFIValue) -> Self { + Self { + index, + name: name.into(), + } + } + } +} diff --git a/src/message/ffi.rs b/src/message/ffi.rs index 5d418cf..1a8addc 100644 --- a/src/message/ffi.rs +++ b/src/message/ffi.rs @@ -1,5 +1,6 @@ use super::MessageChain; use crate::message::at::At; +use crate::message::face::Face; use crate::message::meta::{Anonymous, MessageMetadata, Reply}; use crate::message::MessageElement; use atri_ffi::ffi::ForFFI; @@ -7,7 +8,7 @@ use atri_ffi::message::meta::{ FFIAnonymous, FFIMessageMetadata, FFIReply, ANONYMOUS_FLAG, NONE_META, REPLY_FLAG, }; use atri_ffi::message::{ - FFIAt, FFIMessageChain, FFIMessageValue, MessageElementFlag, MessageElementUnion, + FFIAt, FFIMessageChain, FFIMessageElement, MessageElementFlag, MessageElementUnion, }; use atri_ffi::{ManagedCloneable, RustString, RustVec}; use std::mem::{ManuallyDrop, MaybeUninit}; @@ -17,7 +18,7 @@ impl ForFFI for MessageChain { fn into_ffi(self) -> Self::FFIValue { let meta = self.meta.into_ffi(); - let ffi: Vec = self + let ffi: Vec = self .elements .into_iter() .map(MessageElement::into_ffi) @@ -41,23 +42,23 @@ impl ForFFI for MessageChain { } impl ForFFI for MessageElement { - type FFIValue = FFIMessageValue; + type FFIValue = FFIMessageElement; fn into_ffi(self) -> Self::FFIValue { match self { - MessageElement::Text(s) => FFIMessageValue { + MessageElement::Text(s) => FFIMessageElement { t: MessageElementFlag::Text.value(), union: MessageElementUnion { text: ManuallyDrop::new(RustString::from(s)), }, }, - MessageElement::Image(img) => FFIMessageValue { + MessageElement::Image(img) => FFIMessageElement { t: MessageElementFlag::Image.value(), union: MessageElementUnion { image: ManuallyDrop::new(ManagedCloneable::from_value(img)), }, }, - MessageElement::At(At { target, display }) => FFIMessageValue { + MessageElement::At(At { target, display }) => FFIMessageElement { t: MessageElementFlag::At.value(), union: MessageElementUnion { at: ManuallyDrop::new({ @@ -68,14 +69,20 @@ impl ForFFI for MessageElement { }), }, }, - MessageElement::AtAll => FFIMessageValue { + MessageElement::AtAll => FFIMessageElement { t: MessageElementFlag::AtAll.value(), union: MessageElementUnion { at_all: () }, }, - or => FFIMessageValue { + MessageElement::Face(face) => FFIMessageElement { + t: MessageElementFlag::Face.value(), + union: MessageElementUnion { + face: ManuallyDrop::new(face.into_ffi()), + }, + }, + MessageElement::Unknown(rq) => FFIMessageElement { t: MessageElementFlag::Unknown.value(), union: MessageElementUnion { - unknown: ManuallyDrop::new(ManagedCloneable::from_value(or)), + unknown: ManuallyDrop::new(ManagedCloneable::from_value(rq)), }, }, } @@ -100,9 +107,12 @@ impl ForFFI for MessageElement { }) } MessageElementFlag::AtAll => MessageElement::AtAll, - MessageElementFlag::Unknown => { - ManuallyDrop::into_inner(value.union.unknown).into_value() + MessageElementFlag::Face => { + MessageElement::Face(Face::from_ffi(ManuallyDrop::into_inner(value.union.face))) } + MessageElementFlag::Unknown => MessageElement::Unknown( + ManuallyDrop::into_inner(value.union.unknown).into_value(), + ), } } } @@ -112,7 +122,7 @@ impl ForFFI for Reply { type FFIValue = FFIReply; fn into_ffi(self) -> Self::FFIValue { - let ffi: Vec = self + let ffi: Vec = self .elements .into_iter() .map(MessageElement::into_ffi) diff --git a/src/message/forward.rs b/src/message/forward.rs new file mode 100644 index 0000000..81de9b4 --- /dev/null +++ b/src/message/forward.rs @@ -0,0 +1,234 @@ +use crate::message::MessageChain; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ForwardMessage(Vec); + +impl ForwardMessage { + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn iter(&self) -> std::slice::Iter { + self.0.iter() + } + + pub fn from_message_chain( + sender_id: i64, + sender_name: String, + time: i32, + chain: MessageChain, + ) -> Self { + Self(vec![ForwardNode::NormalMessage { + info: ForwardNodeInfo { + sender_id, + sender_name, + time, + }, + chain, + }]) + } +} + +impl IntoIterator for ForwardMessage { + type Item = ForwardNode; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "type")] +#[serde(rename_all = "snake_case")] +pub enum ForwardNode { + NormalMessage { + #[serde(flatten)] + info: ForwardNodeInfo, + chain: MessageChain, + }, + ForwardMessage { + #[serde(flatten)] + info: ForwardNodeInfo, + forward: ForwardMessage, + }, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ForwardNodeInfo { + pub sender_id: i64, + pub sender_name: String, + pub time: i32, +} + +impl From<[ForwardNode; N]> for ForwardMessage { + fn from(value: [ForwardNode; N]) -> Self { + Self(value.to_vec()) + } +} + +impl From> for ForwardMessage { + fn from(value: Vec) -> Self { + let mut nodes: Vec = Vec::with_capacity(value.len()); + + for msg in value { + nodes.push(match msg { + ricq::structs::ForwardMessage::Message(node) => ForwardNode::NormalMessage { + info: ForwardNodeInfo { + sender_id: node.sender_id, + sender_name: node.sender_name, + time: node.time, + }, + chain: node.elements.into(), + }, + ricq::structs::ForwardMessage::Forward(node) => ForwardNode::ForwardMessage { + info: ForwardNodeInfo { + sender_id: node.sender_id, + sender_name: node.sender_name, + time: node.time, + }, + forward: node.nodes.into(), + }, + }); + } + + Self(nodes) + } +} + +impl From for Vec { + fn from(value: ForwardMessage) -> Self { + let mut nodes = Self::with_capacity(value.len()); + + for node in value.0 { + nodes.push(match node { + ForwardNode::NormalMessage { info, chain } => { + ricq::structs::ForwardMessage::Message(ricq::structs::MessageNode { + sender_id: info.sender_id, + time: info.time, + sender_name: info.sender_name, + elements: chain.into(), + }) + } + ForwardNode::ForwardMessage { info, forward: msg } => { + ricq::structs::ForwardMessage::Forward(ricq::structs::ForwardNode { + sender_id: info.sender_id, + time: info.time, + sender_name: info.sender_name, + nodes: msg.into(), + }) + } + }); + } + + nodes + } +} + +mod ffi { + use super::{ForwardMessage, ForwardNode, ForwardNodeInfo}; + use crate::message::MessageChain; + use atri_ffi::ffi::ForFFI; + use atri_ffi::message::forward::{FFIForwardNode, FFIForwardNodeInfo, ForwardNodeUnion}; + use atri_ffi::RustVec; + use std::mem::ManuallyDrop; + + impl ForFFI for ForwardNode { + type FFIValue = FFIForwardNode; + + fn into_ffi(self) -> Self::FFIValue { + match self { + Self::NormalMessage { info, chain } => FFIForwardNode { + is_normal: true, + info: info.into_ffi(), + inner: ForwardNodeUnion { + normal: ManuallyDrop::new(chain.into_ffi()), + }, + }, + Self::ForwardMessage { info, forward } => FFIForwardNode { + is_normal: false, + info: info.into_ffi(), + inner: ForwardNodeUnion { + forward: ManuallyDrop::new(forward.into_ffi()), + }, + }, + } + } + + fn from_ffi( + FFIForwardNode { + is_normal, + info, + inner, + }: Self::FFIValue, + ) -> Self { + unsafe { + if is_normal { + Self::NormalMessage { + info: ForwardNodeInfo::from_ffi(info), + chain: MessageChain::from_ffi(ManuallyDrop::into_inner(inner.normal)), + } + } else { + Self::ForwardMessage { + info: ForwardNodeInfo::from_ffi(info), + forward: ForwardMessage::from_ffi(ManuallyDrop::into_inner(inner.forward)), + } + } + } + } + } + + impl ForFFI for ForwardNodeInfo { + type FFIValue = FFIForwardNodeInfo; + + fn into_ffi(self) -> Self::FFIValue { + let ForwardNodeInfo { + sender_id, + sender_name, + time, + } = self; + + FFIForwardNodeInfo { + sender_id, + sender_name: sender_name.into(), + time, + } + } + + fn from_ffi( + FFIForwardNodeInfo { + sender_id, + sender_name, + time, + }: Self::FFIValue, + ) -> Self { + Self { + sender_id, + sender_name: sender_name.into(), + time, + } + } + } + + impl ForFFI for ForwardMessage { + type FFIValue = RustVec; + + fn into_ffi(self) -> Self::FFIValue { + self.0 + .into_iter() + .map(ForwardNode::into_ffi) + .collect::>() + .into() + } + + fn from_ffi(rs: Self::FFIValue) -> Self { + Self( + rs.into_vec() + .into_iter() + .map(ForwardNode::from_ffi) + .collect(), + ) + } + } +} diff --git a/src/message/macros.rs b/src/message/macros.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/message/macros.rs @@ -0,0 +1 @@ + diff --git a/src/message/meta.rs b/src/message/meta.rs index 9423255..a02d97b 100644 --- a/src/message/meta.rs +++ b/src/message/meta.rs @@ -159,3 +159,31 @@ impl PushElem for Reply { vec.insert(index, rq.into()); } } + +mod ffi { + use crate::message::meta::MessageReceipt; + use atri_ffi::ffi::ForFFI; + use atri_ffi::message::FFIMessageReceipt; + + impl ForFFI for MessageReceipt { + type FFIValue = FFIMessageReceipt; + + fn into_ffi(self) -> Self::FFIValue { + let MessageReceipt { seqs, rands, time } = self; + + FFIMessageReceipt { + seqs: seqs.into(), + rands: rands.into(), + time, + } + } + + fn from_ffi(FFIMessageReceipt { seqs, rands, time }: Self::FFIValue) -> Self { + Self { + seqs: seqs.into_vec(), + rands: rands.into_vec(), + time, + } + } + } +} diff --git a/src/message/mod.rs b/src/message/mod.rs index c31a43f..4265cb8 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -1,9 +1,13 @@ pub mod at; +pub mod face; pub mod ffi; +pub mod forward; pub mod image; +pub mod macros; pub mod meta; use crate::message::at::At; +use crate::message::face::Face; use crate::message::meta::{Anonymous, MessageMetadata, MessageReceipt, RecallMessage, Reply}; use crate::Text; use core::slice; @@ -12,7 +16,7 @@ use ricq::msg::elem::RQElem; use ricq::msg::{MessageElem, PushElem}; use ricq::structs::{FriendMessage, GroupMessage}; use serde::{Deserialize, Serialize}; -use std::fmt::{Debug, Formatter}; +use std::fmt::{Debug, Formatter, Write}; use std::vec; #[derive(Serialize, Deserialize, Clone, Default)] @@ -215,6 +219,7 @@ pub enum MessageElement { Image(Image), At(At), AtAll, + Face(Face), #[serde(skip)] Unknown(RQElem), } @@ -222,13 +227,22 @@ pub enum MessageElement { impl ToString for MessageElement { fn to_string(&self) -> String { let mut s = String::new(); + match self { Self::Text(t) => s.push_str(t), - Self::Image(img) => s.push_str(&format!("$[Image:{}]", img.url())), - Self::At(At { target, display }) => s.push_str(&format!("$[At:{display}({target})]")), + Self::Image(img) => { + let _ = write!(s, "$[Image:{}]", img.url()); + } + Self::At(At { target, display }) => { + let _ = write!(s, "$[At:{}({})]", display, target); + } Self::AtAll => s.push_str("$[AtAll]"), + Self::Face(f) => { + let _ = write!(s, "$[Face:{}]", f.name); + } Self::Unknown(rq) => s.push_str(&rq.to_string()), } + s } } @@ -249,6 +263,7 @@ impl From for RQElem { }, MessageElement::At(at) => RQElem::At(at.into()), MessageElement::AtAll => RQElem::At(At::ALL.into()), + MessageElement::Face(face) => RQElem::Face(face.into()), MessageElement::Unknown(rq) => rq, } } @@ -270,6 +285,7 @@ impl From for MessageElement { }) } } + RQElem::Face(face) => MessageElement::Face(face.into()), or => Self::Unknown(or), } } @@ -282,6 +298,7 @@ impl PushElem for MessageElement { Self::Image(img) => PushElem::push_to(img, vec), Self::At(at) => PushElem::push_to(at, vec), Self::AtAll => PushElem::push_to(At::ALL, vec), + Self::Face(face) => PushElem::push_to(face, vec), Self::Unknown(_rq) => {} } } diff --git a/src/plugin/ffi/friend.rs b/src/plugin/ffi/friend.rs index 31d693c..bc3fb38 100644 --- a/src/plugin/ffi/friend.rs +++ b/src/plugin/ffi/friend.rs @@ -1,11 +1,12 @@ use crate::contact::friend::Friend; +use crate::message::meta::MessageReceipt; use crate::message::MessageChain; use crate::plugin::cast_ref; use crate::plugin::ffi::future_block_on; use atri_ffi::error::FFIResult; use atri_ffi::ffi::ForFFI; use atri_ffi::future::FFIFuture; -use atri_ffi::message::FFIMessageChain; +use atri_ffi::message::{FFIMessageChain, FFIMessageReceipt}; use atri_ffi::{ManagedCloneable, RustStr, RustVec}; pub extern "C" fn friend_get_id(friend: *const ()) -> i64 { @@ -27,14 +28,11 @@ pub extern "C" fn friend_get_bot(friend: *const ()) -> ManagedCloneable { pub extern "C" fn friend_send_message( friend: *const (), chain: FFIMessageChain, -) -> FFIFuture> { +) -> FFIFuture> { FFIFuture::from(async move { let f: &Friend = cast_ref(friend); let chain = MessageChain::from_ffi(chain); - let result = f - .send_message(chain) - .await - .map(ManagedCloneable::from_value); + let result = f.send_message(chain).await.map(MessageReceipt::into_ffi); FFIResult::from(result) }) @@ -44,7 +42,7 @@ pub extern "C" fn friend_send_message_blocking( manager: *const (), group: *const (), chain: FFIMessageChain, -) -> FFIResult { +) -> FFIResult { let group: &Friend = cast_ref(group); let chain = MessageChain::from_ffi(chain); @@ -52,7 +50,7 @@ pub extern "C" fn friend_send_message_blocking( let result = group .send_message(chain) .await - .map(ManagedCloneable::from_value); + .map(MessageReceipt::into_ffi); FFIResult::from(result) }) diff --git a/src/plugin/ffi/group.rs b/src/plugin/ffi/group.rs index 00cd829..e4dc96c 100644 --- a/src/plugin/ffi/group.rs +++ b/src/plugin/ffi/group.rs @@ -1,11 +1,14 @@ use crate::contact::group::Group; use crate::message; +use crate::message::forward::ForwardMessage; +use crate::message::meta::MessageReceipt; use crate::plugin::cast_ref; use crate::plugin::ffi::future_block_on; use atri_ffi::error::FFIResult; use atri_ffi::ffi::ForFFI; use atri_ffi::future::FFIFuture; -use atri_ffi::message::FFIMessageChain; +use atri_ffi::message::forward::FFIForwardNode; +use atri_ffi::message::{FFIMessageChain, FFIMessageReceipt}; use atri_ffi::{ManagedCloneable, RustStr, RustVec}; use message::MessageChain; @@ -65,13 +68,13 @@ pub extern "C" fn group_find_or_refresh_member( pub extern "C" fn group_send_message( group: *const (), chain: FFIMessageChain, -) -> FFIFuture> { +) -> FFIFuture> { FFIFuture::from(async move { let group: &Group = cast_ref(group); let result = group .send_message(MessageChain::from_ffi(chain)) .await - .map(ManagedCloneable::from_value); + .map(MessageReceipt::into_ffi); FFIResult::from(result) }) @@ -81,7 +84,7 @@ pub extern "C" fn group_send_message_blocking( manager: *const (), group: *const (), chain: FFIMessageChain, -) -> FFIResult { +) -> FFIResult { let group: &Group = cast_ref(group); let chain = MessageChain::from_ffi(chain); @@ -89,7 +92,7 @@ pub extern "C" fn group_send_message_blocking( let result = group .send_message(chain) .await - .map(ManagedCloneable::from_value); + .map(MessageReceipt::into_ffi); FFIResult::from(result) }) @@ -163,3 +166,48 @@ pub extern "C" fn group_quit_blocking(manager: *const (), group: *const ()) -> b let group: &Group = cast_ref(group); future_block_on(manager, async move { group.quit().await }) } + +pub extern "C" fn group_send_forward_message( + group: *const (), + msg: RustVec, +) -> FFIFuture> { + let group: &Group = cast_ref(group); + let forward = ForwardMessage::from_ffi(msg); + FFIFuture::from(async move { + group + .send_forward_message(forward) + .await + .map(MessageReceipt::into_ffi) + .into() + }) +} + +pub extern "C" fn group_send_forward_message_blocking( + manager: *const (), + group: *const (), + msg: RustVec, +) -> FFIResult { + let group: &Group = cast_ref(group); + let forward = ForwardMessage::from_ffi(msg); + future_block_on(manager, async move { + group + .send_forward_message(forward) + .await + .map(MessageReceipt::into_ffi) + .into() + }) +} + +pub extern "C" fn group_invite(group: *const (), id: i64) -> FFIFuture> { + let group: &Group = cast_ref(group); + FFIFuture::from(async move { group.invite(id).await.into() }) +} + +pub extern "C" fn group_invite_blocking( + manager: *const (), + group: *const (), + id: i64, +) -> FFIResult<()> { + let group: &Group = cast_ref(group); + future_block_on(manager, async move { group.invite(id).await.into() }) +} diff --git a/src/plugin/ffi/mod.rs b/src/plugin/ffi/mod.rs index d389465..4ed6b51 100644 --- a/src/plugin/ffi/mod.rs +++ b/src/plugin/ffi/mod.rs @@ -19,15 +19,17 @@ use crate::plugin::ffi::event::{ }; use crate::plugin::ffi::group::{ group_change_name, group_change_name_blocking, group_find_member, group_find_or_refresh_member, - group_get_client, group_get_id, group_get_members, group_get_name, group_quit, - group_quit_blocking, group_send_message, group_send_message_blocking, group_upload_image, - group_upload_image_blocking, + group_get_client, group_get_id, group_get_members, group_get_name, group_invite, + group_invite_blocking, group_quit, group_quit_blocking, group_send_forward_message, + group_send_forward_message_blocking, group_send_message, group_send_message_blocking, + group_upload_image, group_upload_image_blocking, }; use crate::plugin::ffi::listener::{ listener_next_event_with_priority, listener_next_event_with_priority_blocking, new_listener, new_listener_c_func, new_listener_closure, }; use atri_ffi::error::FFIResult; +use std::ffi::{c_char, CStr}; use std::future::Future; use crate::plugin::cast_ref; @@ -47,11 +49,15 @@ use crate::plugin::ffi::message::{ }; use crate::PluginManager; use atri_ffi::future::FFIFuture; -use atri_ffi::Managed; +use atri_ffi::{Managed, RustString}; +use tracing::error; pub extern "C" fn plugin_get_function(sig: u16) -> *const () { extern "C" fn not_impl() { - panic!("No such operation"); + let bt = std::backtrace::Backtrace::force_capture(); + error!("插件执行了一个不存在的操作, stack backtrace: {}", bt); + + std::process::abort(); } macro_rules! match_function { @@ -101,12 +107,16 @@ pub extern "C" fn plugin_get_function(sig: u16) -> *const () { 407 => group_upload_image, 408 => group_quit, 409 => group_change_name, + 410 => group_send_forward_message, + 411 => group_invite, // blocking api 456 => group_send_message_blocking, 457 => group_upload_image_blocking, 458 => group_quit_blocking, 459 => group_change_name_blocking, + 460 => group_send_forward_message_blocking, + 461 => group_invite_blocking, // friend 500 => friend_get_id, @@ -152,6 +162,9 @@ pub extern "C" fn plugin_get_function(sig: u16) -> *const () { // serialize 30100 => message_chain_to_json, 30101 => message_chain_from_json, + + // ffi + 30500 => c_str_cvt, } } @@ -170,6 +183,12 @@ extern "C" fn plugin_manager_block_on(manager: *const (), future: FFIFuture RustString { + let cstr = unsafe { CStr::from_ptr(ptr) }; + + cstr.to_string_lossy().to_string().into() +} + fn future_block_on(manager: *const (), future: F) -> F::Output where F: Future, diff --git a/src/service/login.rs b/src/service/login.rs index 66827dc..5737a6f 100644 --- a/src/service/login.rs +++ b/src/service/login.rs @@ -8,7 +8,7 @@ use tokio::fs; use tokio::io::AsyncWriteExt; use tracing::{error, info, warn}; -use crate::client::BotConfiguration; +use crate::client::ClientConfiguration; use crate::config::login::{LoginConfig, DEFAULT_CONFIG}; use crate::error::AtriResult; use crate::{config, global_status, Client}; @@ -73,7 +73,7 @@ pub async fn login_clients() -> Result<(), RQError> { match login_client( account, &pwd, - BotConfiguration { + ClientConfiguration { work_dir: None, version: client .protocol @@ -117,7 +117,7 @@ pub async fn login_clients() -> Result<(), RQError> { async fn login_client( account: i64, password: &Option, - conf: BotConfiguration, + conf: ClientConfiguration, ) -> AtriResult { let client = Client::new(account, conf).await; client.start().await?;