Skip to main content

maa_framework/
agent_client.rs

1//! Agent client for connecting to AgentServer.
2//!
3//! This module allows delegating custom recognition and action execution
4//! to a separate process running an AgentServer.
5
6use crate::{
7    buffer, common, controller::Controller, resource::Resource, sys, tasker::Tasker, MaaError,
8    MaaResult,
9};
10use std::ptr::NonNull;
11
12/// Agent client for remote custom component execution.
13///
14/// Connects to an AgentServer to delegate custom recognition and action
15/// execution to a separate process.
16pub struct AgentClient {
17    handle: NonNull<sys::MaaAgentClient>,
18    _resource_guard: Option<Resource>,
19    _controller_guard: Option<Controller>,
20    _tasker_guard: Option<Tasker>,
21}
22
23unsafe impl Send for AgentClient {}
24unsafe impl Sync for AgentClient {}
25
26impl AgentClient {
27    /// Create a new agent client.
28    ///
29    /// # Arguments
30    /// * `identifier` - Optional connection identifier for matching specific AgentServer
31    pub fn new(identifier: Option<&str>) -> MaaResult<Self> {
32        let id_buffer = if let Some(id) = identifier {
33            let mut buf = buffer::MaaStringBuffer::new()?;
34            buf.set(id)?;
35            Some(buf)
36        } else {
37            None
38        };
39
40        let handle = unsafe {
41            sys::MaaAgentClientCreateV2(
42                id_buffer
43                    .as_ref()
44                    .map(|b| b.raw())
45                    .unwrap_or(std::ptr::null_mut()),
46            )
47        };
48
49        NonNull::new(handle)
50            .map(|ptr| Self {
51                handle: ptr,
52                _resource_guard: None,
53                _controller_guard: None,
54                _tasker_guard: None,
55            })
56            .ok_or(MaaError::FrameworkError(-1))
57    }
58
59    /// Get the connection identifier.
60    pub fn identifier(&self) -> Option<String> {
61        let buffer = buffer::MaaStringBuffer::new().ok()?;
62        let ret = unsafe { sys::MaaAgentClientIdentifier(self.handle.as_ptr(), buffer.raw()) };
63        if ret != 0 {
64            Some(buffer.to_string())
65        } else {
66            None
67        }
68    }
69
70    /// Bind a resource to receive custom recognitions and actions from AgentServer.
71    ///
72    /// Takes ownership of the Resource to ensure it stays alive while bound.
73    pub fn bind(&mut self, resource: Resource) -> MaaResult<()> {
74        let ret = unsafe { sys::MaaAgentClientBindResource(self.handle.as_ptr(), resource.raw()) };
75        common::check_bool(ret)?;
76
77        self._resource_guard = Some(resource);
78        Ok(())
79    }
80
81    /// Register resource event sink to forward events to AgentServer.
82    pub fn register_resource_sink(&mut self, resource: Resource) -> MaaResult<()> {
83        let ret = unsafe {
84            sys::MaaAgentClientRegisterResourceSink(self.handle.as_ptr(), resource.raw())
85        };
86        common::check_bool(ret)?;
87        self._resource_guard = Some(resource);
88        Ok(())
89    }
90
91    /// Register controller event sink to forward events to AgentServer.
92    pub fn register_controller_sink(&mut self, controller: Controller) -> MaaResult<()> {
93        let ret = unsafe {
94            sys::MaaAgentClientRegisterControllerSink(self.handle.as_ptr(), controller.raw())
95        };
96        common::check_bool(ret)?;
97        self._controller_guard = Some(controller);
98        Ok(())
99    }
100
101    /// Register tasker event sink to forward events to AgentServer.
102    pub fn register_tasker_sink(&mut self, tasker: Tasker) -> MaaResult<()> {
103        let ret =
104            unsafe { sys::MaaAgentClientRegisterTaskerSink(self.handle.as_ptr(), tasker.raw()) };
105        common::check_bool(ret)?;
106        self._tasker_guard = Some(tasker);
107        Ok(())
108    }
109
110    /// Register all event sinks (resource, controller, tasker) at once.
111    pub fn register_sinks(
112        &mut self,
113        resource: Resource,
114        controller: Controller,
115        tasker: Tasker,
116    ) -> MaaResult<()> {
117        self.register_resource_sink(resource)?;
118        self.register_controller_sink(controller)?;
119        self.register_tasker_sink(tasker)
120    }
121
122    /// Connect to the AgentServer.
123    pub fn connect(&self) -> MaaResult<()> {
124        let ret = unsafe { sys::MaaAgentClientConnect(self.handle.as_ptr()) };
125        common::check_bool(ret)
126    }
127
128    /// Disconnect from the AgentServer.
129    pub fn disconnect(&self) -> MaaResult<()> {
130        let ret = unsafe { sys::MaaAgentClientDisconnect(self.handle.as_ptr()) };
131        common::check_bool(ret)
132    }
133
134    /// Check if currently connected to AgentServer.
135    pub fn connected(&self) -> bool {
136        unsafe { sys::MaaAgentClientConnected(self.handle.as_ptr()) != 0 }
137    }
138
139    /// Check if the connection is alive.
140    pub fn alive(&self) -> bool {
141        unsafe { sys::MaaAgentClientAlive(self.handle.as_ptr()) != 0 }
142    }
143
144    /// Set the connection timeout.
145    ///
146    /// # Arguments
147    /// * `milliseconds` - Timeout in milliseconds
148    pub fn set_timeout(&self, milliseconds: i64) -> MaaResult<()> {
149        let ret = unsafe { sys::MaaAgentClientSetTimeout(self.handle.as_ptr(), milliseconds) };
150        common::check_bool(ret)
151    }
152
153    /// Get the list of custom recognitions available on the AgentServer.
154    pub fn custom_recognition_list(&self) -> MaaResult<Vec<String>> {
155        let buffer = buffer::MaaStringListBuffer::new()?;
156        let ret = unsafe {
157            sys::MaaAgentClientGetCustomRecognitionList(self.handle.as_ptr(), buffer.raw())
158        };
159        if ret != 0 {
160            Ok(buffer.to_vec())
161        } else {
162            Err(MaaError::FrameworkError(0))
163        }
164    }
165
166    /// Get the list of custom actions available on the AgentServer.
167    pub fn custom_action_list(&self) -> MaaResult<Vec<String>> {
168        let buffer = buffer::MaaStringListBuffer::new()?;
169        let ret =
170            unsafe { sys::MaaAgentClientGetCustomActionList(self.handle.as_ptr(), buffer.raw()) };
171        if ret != 0 {
172            Ok(buffer.to_vec())
173        } else {
174            Err(MaaError::FrameworkError(0))
175        }
176    }
177
178    /// Get the raw handle pointer.
179    pub fn raw(&self) -> *mut sys::MaaAgentClient {
180        self.handle.as_ptr()
181    }
182}
183
184impl Drop for AgentClient {
185    fn drop(&mut self) {
186        unsafe { sys::MaaAgentClientDestroy(self.handle.as_ptr()) }
187    }
188}