use crate::invoker::Actions;
use nagisa_types::event::ReactionKind;
use nagisa_types::prelude::*;
use serde_json::Value;
use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::{Arc, Mutex, OnceLock};
type OutgoingLogger = Box<dyn Fn(&Peer, &[Segment], Uin, &MessageId) + Send + Sync>;
static OUTGOING_LOGGERS: OnceLock<Mutex<Vec<OutgoingLogger>>> = OnceLock::new();
fn loggers() -> &'static Mutex<Vec<OutgoingLogger>> {
OUTGOING_LOGGERS.get_or_init(|| Mutex::new(Vec::new()))
}
pub fn add_outgoing_logger(f: OutgoingLogger) {
if let Ok(mut v) = loggers().lock() {
v.push(f);
}
}
pub fn set_outgoing_logger(f: OutgoingLogger) {
add_outgoing_logger(f);
}
fn log_outgoing(peer: &Peer, message: &[Segment], self_id: Uin, id: &MessageId) {
let guard = match loggers().lock() {
Ok(g) => g,
Err(_) => return,
};
if guard.is_empty() {
tracing::debug!(peer = ?peer, segments = message.len(), "发送消息");
return;
}
for log in guard.iter() {
log(peer, message, self_id, id);
}
}
#[derive(Clone)]
pub struct Bot {
inner: Arc<dyn Actions>,
self_id: Arc<AtomicI64>,
}
impl Bot {
pub fn new(inner: Arc<dyn Actions>, self_id: Uin) -> Self {
Self {
inner,
self_id: Arc::new(AtomicI64::new(self_id.0)),
}
}
pub fn self_id(&self) -> Uin {
Uin(self.self_id.load(Ordering::Relaxed))
}
pub fn set_self_id(&self, id: Uin) {
self.self_id.store(id.0, Ordering::Relaxed);
}
pub fn protocol(&self) -> Protocol {
self.inner.protocol()
}
pub fn vendor(&self) -> nagisa_types::vendor::Vendor {
self.inner.vendor()
}
pub fn supports(&self, cap: Capability) -> bool {
self.inner.supports(cap)
}
pub async fn send(&self, peer: &Peer, message: &[Segment]) -> Result<MessageId> {
let id = self.inner.send(peer, message).await?;
log_outgoing(peer, message, self.self_id(), &id);
Ok(id)
}
pub async fn recall(&self, id: &MessageId) -> Result<()> {
self.inner.recall(id).await
}
pub async fn get_message(&self, id: &MessageId) -> Result<MessageEvent> {
self.inner.get_message(id).await
}
pub async fn get_login_info(&self) -> Result<(Uin, String)> {
self.inner.get_login_info().await
}
pub async fn get_group_info(&self, group: Uin, no_cache: bool) -> Result<GroupInfo> {
self.inner.get_group_info(group, no_cache).await
}
pub async fn get_group_list(&self, no_cache: bool) -> Result<Vec<GroupInfo>> {
self.inner.get_group_list(no_cache).await
}
pub async fn get_group_member_info(
&self,
group: Uin,
user: Uin,
no_cache: bool,
) -> Result<MemberInfo> {
self.inner.get_group_member_info(group, user, no_cache).await
}
pub async fn get_group_member_list(&self, group: Uin, no_cache: bool) -> Result<Vec<MemberInfo>> {
self.inner.get_group_member_list(group, no_cache).await
}
pub async fn get_friend_list(&self, no_cache: bool) -> Result<Vec<FriendInfo>> {
self.inner.get_friend_list(no_cache).await
}
pub async fn set_group_member_mute(&self, group: Uin, user: Uin, duration: u32) -> Result<()> {
self.inner.set_group_member_mute(group, user, duration).await
}
pub async fn set_group_whole_mute(&self, group: Uin, enable: bool) -> Result<()> {
self.inner.set_group_whole_mute(group, enable).await
}
pub async fn set_group_admin(&self, group: Uin, user: Uin, enable: bool) -> Result<()> {
self.inner.set_group_admin(group, user, enable).await
}
pub async fn set_group_member_card(&self, group: Uin, user: Uin, card: &str) -> Result<()> {
self.inner.set_group_member_card(group, user, card).await
}
pub async fn set_group_name(&self, group: Uin, name: &str) -> Result<()> {
self.inner.set_group_name(group, name).await
}
pub async fn kick_group_member(&self, group: Uin, user: Uin, reject_add: bool) -> Result<()> {
self.inner.kick_group_member(group, user, reject_add).await
}
pub async fn handle_request(
&self,
token: &RequestToken,
approve: bool,
reason: Option<&str>,
) -> Result<()> {
self.inner.handle_request(token, approve, reason).await
}
pub async fn send_reaction(
&self,
group: Uin,
seq: i64,
face_id: &str,
kind: ReactionKind,
is_add: bool,
) -> Result<()> {
self.inner.send_reaction(group, seq, face_id, kind, is_add).await
}
pub async fn send_nudge(&self, peer: &Peer, target: Uin) -> Result<()> {
self.inner.send_nudge(peer, target).await
}
pub async fn upload_group_file(
&self,
group: Uin,
src: ResourceSource,
name: &str,
parent_folder_id: Option<&str>,
) -> Result<String> {
self.inner.upload_group_file(group, src, name, parent_folder_id).await
}
pub async fn upload_private_file(
&self,
user: Uin,
src: ResourceSource,
name: &str,
) -> Result<String> {
self.inner.upload_private_file(user, src, name).await
}
pub async fn get_user_info(&self, user: Uin, no_cache: bool) -> Result<UserInfo> {
self.inner.get_user_info(user, no_cache).await
}
pub async fn get_friend_info(&self, user: Uin) -> Result<FriendInfo> {
self.inner.get_friend_info(user).await
}
pub async fn get_message_history(
&self,
peer: &Peer,
start: Option<&MessageId>,
count: u32,
) -> Result<Vec<MessageEvent>> {
self.inner.get_message_history(peer, start, count).await
}
pub async fn get_history_messages_paged(
&self,
peer: &Peer,
start_seq: Option<i64>,
limit: u32,
) -> Result<(Vec<MessageEvent>, Option<i64>)> {
self.inner
.get_history_messages_paged(peer, start_seq, limit)
.await
}
pub async fn leave_group(&self, group: Uin, dismiss: bool) -> Result<()> {
self.inner.leave_group(group, dismiss).await
}
pub async fn set_group_member_special_title(
&self,
group: Uin,
user: Uin,
title: &str,
duration: i64,
) -> Result<()> {
self.inner
.set_group_member_special_title(group, user, title, duration)
.await
}
pub async fn mark_message_as_read(&self, peer: &Peer, id: &MessageId) -> Result<()> {
self.inner.mark_message_as_read(peer, id).await
}
pub async fn set_essence(&self, group: Uin, id: &MessageId, enable: bool) -> Result<()> {
self.inner.set_essence(group, id, enable).await
}
pub async fn get_resource_url(&self, resource_id: &str) -> Result<String> {
self.inner.get_resource_url(resource_id).await
}
pub async fn get_group_file_download_url(
&self,
group: Uin,
file_id: &str,
) -> Result<String> {
self.inner.get_group_file_download_url(group, file_id).await
}
pub async fn delete_group_file(&self, group: Uin, file_id: &str) -> Result<()> {
self.inner.delete_group_file(group, file_id).await
}
pub async fn get_group_requests(&self) -> Result<Vec<Request>> {
self.inner.get_group_requests().await
}
pub async fn get_cookies(&self, domain: Option<&str>) -> Result<String> {
self.inner.get_cookies(domain).await
}
pub async fn get_forward_messages(&self, forward_id: &str) -> Result<Vec<ForwardNode>> {
self.inner.get_forward_messages(forward_id).await
}
pub async fn delete_friend(&self, user: Uin) -> Result<()> {
self.inner.delete_friend(user).await
}
pub async fn set_friend_remark(&self, user: Uin, remark: &str) -> Result<()> {
self.inner.set_friend_remark(user, remark).await
}
pub async fn send_profile_like(&self, user: Uin, count: u32) -> Result<()> {
self.inner.send_profile_like(user, count).await
}
pub async fn get_friend_requests(&self) -> Result<Vec<Request>> {
self.inner.get_friend_requests().await
}
pub async fn send_group_announcement(
&self,
group: Uin,
content: &str,
image: Option<ResourceSource>,
) -> Result<String> {
self.inner.send_group_announcement(group, content, image).await
}
pub async fn get_group_announcements(&self, group: Uin) -> Result<Vec<Announcement>> {
self.inner.get_group_announcements(group).await
}
pub async fn delete_group_announcement(&self, group: Uin, announcement_id: &str) -> Result<()> {
self.inner.delete_group_announcement(group, announcement_id).await
}
pub async fn get_essence_messages(&self, group: Uin) -> Result<Vec<EssenceMessage>> {
self.inner.get_essence_messages(group).await
}
pub async fn set_peer_pin(&self, peer: &Peer, pinned: bool) -> Result<()> {
self.inner.set_peer_pin(peer, pinned).await
}
pub async fn get_peer_pins(&self) -> Result<(Vec<FriendInfo>, Vec<GroupInfo>)> {
self.inner.get_peer_pins().await
}
pub async fn get_impl_info(&self) -> Result<crate::ImplInfo> {
self.inner.get_impl_info().await
}
pub async fn send_friend_nudge(&self, user: Uin, is_self: bool) -> Result<()> {
self.inner.send_friend_nudge(user, is_self).await
}
pub async fn get_group_notices_paged(
&self,
start_seq: Option<i64>,
limit: u32,
is_filtered: bool,
) -> Result<(Vec<Notice>, Option<i64>)> {
self.inner
.get_group_notices_paged(start_seq, limit, is_filtered)
.await
}
pub async fn get_group_files(
&self,
group: Uin,
folder_id: Option<&str>,
) -> Result<GroupFileList> {
self.inner.get_group_files(group, folder_id).await
}
pub async fn get_private_file_download_url(
&self,
user: Uin,
file_id: &str,
hash: Option<&str>,
) -> Result<String> {
self.inner.get_private_file_download_url(user, file_id, hash).await
}
pub async fn create_group_folder(&self, group: Uin, name: &str) -> Result<String> {
self.inner.create_group_folder(group, name).await
}
pub async fn rename_group_folder(
&self,
group: Uin,
folder_id: &str,
new_name: &str,
) -> Result<()> {
self.inner.rename_group_folder(group, folder_id, new_name).await
}
pub async fn delete_group_folder(&self, group: Uin, folder_id: &str) -> Result<()> {
self.inner.delete_group_folder(group, folder_id).await
}
pub async fn move_group_file(
&self,
group: Uin,
file_id: &str,
source_folder_id: Option<&str>,
target_folder_id: Option<&str>,
) -> Result<()> {
self.inner.move_group_file(group, file_id, source_folder_id, target_folder_id).await
}
pub async fn rename_group_file(
&self,
group: Uin,
file_id: &str,
source_folder_id: Option<&str>,
new_name: &str,
) -> Result<()> {
self.inner.rename_group_file(group, file_id, source_folder_id, new_name).await
}
pub async fn set_group_avatar(&self, group: Uin, src: ResourceSource) -> Result<()> {
self.inner.set_group_avatar(group, src).await
}
pub async fn set_self_avatar(&self, src: ResourceSource) -> Result<()> {
self.inner.set_self_avatar(src).await
}
pub async fn set_self_nickname(&self, name: &str) -> Result<()> {
self.inner.set_self_nickname(name).await
}
pub async fn set_self_bio(&self, bio: &str) -> Result<()> {
self.inner.set_self_bio(bio).await
}
pub async fn get_status(&self) -> Result<ImplStatus> {
self.inner.get_status().await
}
pub async fn get_csrf_token(&self) -> Result<String> {
self.inner.get_csrf_token().await
}
pub async fn set_restart(&self, delay_ms: u32) -> Result<()> {
self.inner.set_restart(delay_ms).await
}
pub async fn clean_cache(&self) -> Result<()> {
self.inner.clean_cache().await
}
pub async fn can_send_image(&self) -> Result<bool> {
self.inner.can_send_image().await
}
pub async fn can_send_record(&self) -> Result<bool> {
self.inner.can_send_record().await
}
pub async fn get_group_honor_info(&self, group: Uin, kind: HonorKind) -> Result<HonorList> {
self.inner.get_group_honor_info(group, kind).await
}
pub async fn get_record(&self, file: &str, out_format: &str) -> Result<String> {
self.inner.get_record(file, out_format).await
}
pub async fn get_image(&self, file: &str) -> Result<String> {
self.inner.get_image(file).await
}
pub async fn call_raw(&self, action: &str, params: Value) -> Result<Value> {
self.inner.call_raw(action, params).await
}
pub fn actions(&self) -> &dyn Actions {
&*self.inner
}
pub fn noop() -> Self {
Self::new(Arc::new(NoopActions), Uin(0))
}
}
struct NoopActions;
#[async_trait::async_trait]
impl crate::invoker::ActionInvoker for NoopActions {
fn protocol(&self) -> Protocol {
Protocol::Milky
}
async fn send(&self, peer: &Peer, _message: &[Segment]) -> Result<MessageId> {
Ok(MessageId::from_seq(*peer, 0))
}
async fn call_raw(&self, _action: &str, _params: Value) -> Result<Value> {
Ok(Value::Null)
}
}
impl crate::invoker::OneBotActions for NoopActions {}
impl crate::invoker::MilkyActions for NoopActions {}