@@ -21,10 +21,11 @@ use tracing::{debug, error, info, warn};
2121use tracing_subscriber:: EnvFilter ;
2222
2323use crate :: backend:: { SqliteBackend , BackendBase } ;
24- use crate :: events:: { Connected , Message as WAMessage , PairingQrCode } ;
24+ use crate :: events:: { Connected , LoggedOut , Message as WAMessage , PairingQrCode } ;
2525use crate :: exceptions:: UnsupportedBackend ;
26- use crate :: types:: JID ;
26+ use crate :: types:: { JID , UploadResponse } ;
2727use crate :: dispatcher:: Dispatcher ;
28+ use crate :: wacore:: MediaType ;
2829
2930static LOG_INIT : Once = Once :: new ( ) ;
3031
@@ -54,6 +55,54 @@ pub struct TryxClient {
5455
5556#[ pymethods]
5657impl TryxClient {
58+ fn upload_file < ' py > ( & self , py : Python < ' py > , path : String , media_type : Py < MediaType > ) -> PyResult < Bound < ' py , PyAny > > {
59+ let client = self . client_rx . borrow ( ) . clone ( ) . ok_or_else ( || {
60+ PyErr :: new :: < pyo3:: exceptions:: PyRuntimeError , _ > ( "Bot is not running" )
61+ } ) ?;
62+ let media_type_enum = media_type. bind ( py) . borrow_mut ( ) . to_wacore_enum ( ) ;
63+ let locals = get_current_locals ( py) ?;
64+ let data = std:: fs:: read ( path. clone ( ) ) . map_err ( |e| PyErr :: new :: < pyo3:: exceptions:: PyRuntimeError , _ > ( e. to_string ( ) ) ) ?;
65+ future_into_py_with_locals ( py, locals, async move {
66+ let url = client
67+ . upload ( data, media_type_enum)
68+ . await
69+ . map_err ( |e| PyErr :: new :: < pyo3:: exceptions:: PyRuntimeError , _ > ( e. to_string ( ) ) ) ?;
70+ let result= UploadResponse {
71+ url : url. url ,
72+ direct_path : url. direct_path ,
73+ media_key : url. media_key ,
74+ file_enc_sha256 : url. file_enc_sha256 ,
75+ file_sha256 : url. file_sha256 ,
76+ file_length : url. file_length ,
77+ } ;
78+ Ok ( result)
79+ } )
80+ }
81+ fn upload < ' py > ( & self , py : Python < ' py > , data : & [ u8 ] , media_type : Py < MediaType > ) -> PyResult < Bound < ' py , PyAny > > {
82+ let client = self . client_rx . borrow ( ) . clone ( ) . ok_or_else ( || {
83+ PyErr :: new :: < pyo3:: exceptions:: PyRuntimeError , _ > ( "Bot is not running" )
84+ } ) ?;
85+ let data_vec = data. to_vec ( ) ;
86+ let mtype = media_type. bind ( py) . borrow_mut ( ) . to_wacore_enum ( ) ;
87+ let locals = get_current_locals ( py) ?;
88+ future_into_py_with_locals :: < _ , UploadResponse > ( py, locals, async move {
89+ let url = client
90+ . upload ( data_vec, mtype)
91+ . await
92+ . map_err ( |e| PyErr :: new :: < pyo3:: exceptions:: PyRuntimeError , _ > ( e. to_string ( ) ) ) ?;
93+ let result= UploadResponse {
94+ url : url. url ,
95+ direct_path : url. direct_path ,
96+ media_key : url. media_key ,
97+ file_enc_sha256 : url. file_enc_sha256 ,
98+ file_sha256 : url. file_sha256 ,
99+ file_length : url. file_length ,
100+ } ;
101+ Ok ( result)
102+ } )
103+ // Ok(url)
104+ // })
105+ }
57106 fn send_message < ' py > ( & self , py : Python < ' py > , to : Py < JID > , message : Py < PyAny > ) -> PyResult < Bound < ' py , PyAny > > {
58107 let client = self . client_rx . borrow ( ) . clone ( ) . ok_or_else ( || {
59108 PyErr :: new :: < pyo3:: exceptions:: PyRuntimeError , _ > ( "Bot is not running" )
@@ -91,28 +140,47 @@ impl Tryx {
91140 tryx_client : Py < TryxClient > ,
92141 client_tx : watch:: Sender < Option < Arc < Client > > > ,
93142 ) -> PyResult < ( ) > {
143+ let ( pairing_qr_callbacks, message_callbacks, connected_callbacks, logout_callbacks) =
144+ Python :: attach ( |py| {
145+ let dispatcher = handlers. bind ( py) . borrow ( ) ;
146+ (
147+ dispatcher. pairing_qr_handlers ( py) ,
148+ dispatcher. message_handlers ( py) ,
149+ dispatcher. conneccted_handlers ( py) ,
150+ dispatcher. logout_handlers ( py) ,
151+ )
152+ } ) ;
153+ let pairing_qr_callbacks = Arc :: new ( pairing_qr_callbacks) ;
154+ let message_callbacks = Arc :: new ( message_callbacks) ;
155+ let connected_callbacks = Arc :: new ( connected_callbacks) ;
156+ let logout_callbacks = Arc :: new ( logout_callbacks) ;
157+ info ! (
158+ pairing_qr_handlers = pairing_qr_callbacks. len( ) ,
159+ message_handlers = message_callbacks. len( ) ,
160+ connected_handlers = connected_callbacks. len( ) ,
161+ logout_handlers = logout_callbacks. len( ) ,
162+ "cached dispatcher handlers for runtime"
163+ ) ;
164+
94165 info ! ( "building WhatsApp bot" ) ;
95166 let mut bot = Bot :: builder ( )
96167 . with_backend ( backend)
97168 . with_transport_factory ( TokioWebSocketTransportFactory :: new ( ) )
98169 . with_http_client ( UreqHttpClient :: new ( ) )
99170 . on_event ( move |event, _client| {
100- let handlers = Python :: attach ( |py| handlers. clone_ref ( py) ) ;
101171 let locals = locals. clone ( ) ;
172+ let pairing_qr_callbacks = Arc :: clone ( & pairing_qr_callbacks) ;
173+ let message_callbacks = Arc :: clone ( & message_callbacks) ;
174+ let connected_callbacks = Arc :: clone ( & connected_callbacks) ;
175+ let logout_callbacks = Arc :: clone ( & logout_callbacks) ;
102176 let tryx_client = Python :: attach ( |py| tryx_client. clone_ref ( py) ) ;
103177 async move {
104178 match event {
105179 Event :: PairingQrCode { code, timeout } => {
106180 info ! ( timeout_secs = timeout. as_secs( ) , "received pairing QR event" ) ;
107- let callbacks = Python :: attach ( |py| {
108- handlers
109- . bind ( py)
110- . borrow ( )
111- . pairing_qr_handlers ( py)
112- } ) ;
113- info ! ( handlers = callbacks. len( ) , "dispatching pairing QR handlers" ) ;
181+ info ! ( handlers = pairing_qr_callbacks. len( ) , "dispatching pairing QR handlers" ) ;
114182
115- for ( idx, callback) in callbacks . into_iter ( ) . enumerate ( ) {
183+ for ( idx, callback) in pairing_qr_callbacks . iter ( ) . enumerate ( ) {
116184 debug ! ( handler_index = idx, "calling pairing QR Python callback" ) ;
117185 let locals = locals. clone ( ) ;
118186 let py_future = Python :: attach ( |py| -> PyResult < _ > {
@@ -149,15 +217,9 @@ impl Tryx {
149217 }
150218 Event :: Message ( msg, info) => {
151219 debug ! ( message_id = %info. id, "received message event" ) ;
152- let callbacks = Python :: attach ( |py| {
153- handlers
154- . bind ( py)
155- . borrow ( )
156- . message_handlers ( py)
157- } ) ;
158- info ! ( handlers = callbacks. len( ) , message_id = %info. id, "dispatching message handlers" ) ;
220+ info ! ( handlers = message_callbacks. len( ) , message_id = %info. id, "dispatching message handlers" ) ;
159221
160- for ( idx, callback) in callbacks . into_iter ( ) . enumerate ( ) {
222+ for ( idx, callback) in message_callbacks . iter ( ) . enumerate ( ) {
161223 debug ! ( handler_index = idx, message_id = %info. id, "calling message Python callback" ) ;
162224 let locals = locals. clone ( ) ;
163225 let py_future = Python :: attach ( |py| -> PyResult < _ > {
@@ -194,8 +256,7 @@ impl Tryx {
194256 }
195257 }
196258 Event :: Connected ( _) => {
197- let callbacks = Python :: attach ( |py| handlers. clone_ref ( py) . bind ( py) . borrow ( ) . conneccted_handlers ( py) ) ;
198- for ( idx, callback) in callbacks. into_iter ( ) . enumerate ( ) {
259+ for ( idx, callback) in connected_callbacks. iter ( ) . enumerate ( ) {
199260 debug ! ( handler_index = idx, "calling connected event handler" ) ;
200261 let _ = Python :: attach ( |py| -> PyResult < _ > {
201262 let awaitable = callback. bind ( py) . call1 ( ( Connected { } , ) ) ?;
@@ -204,6 +265,17 @@ impl Tryx {
204265 } ) ;
205266 }
206267 }
268+ Event :: LoggedOut ( logout) => {
269+ for ( idx, callback) in logout_callbacks. iter ( ) . enumerate ( ) {
270+ debug ! ( handler_index = idx, "calling logged out event handler" ) ;
271+ let _ = Python :: attach ( |py| -> PyResult < _ > {
272+ let awaitable = callback. bind ( py) . call1 ( ( LoggedOut :: new ( logout. clone ( ) ) , ) ) ?;
273+ let fut = into_future ( awaitable) ?;
274+ Ok ( fut)
275+ } ) ;
276+ }
277+
278+ }
207279 _ => {
208280 debug ! ( "received event without registered dispatcher path" ) ;
209281 }
@@ -377,4 +449,5 @@ impl Tryx {
377449 } )
378450 } )
379451 }
452+
380453}
0 commit comments