1#![allow(clippy::too_many_arguments)]
3
4use jsonrpsee::{async_client::ClientBuilder, core::client::SubscriptionClientT, proc_macros::rpc};
5use serde::Deserialize;
6use serde_json::Value;
7use tokio::net::ToSocketAddrs;
8
9pub use jsonrpsee::core::ClientError as RpcClientError;
10
11#[rpc(client)]
12pub trait Rpc {
13 #[method(name = "addDevice", param_kind = map)]
14 async fn add_device(
15 &self,
16 account: Option<String>,
17 uri: String,
18 ) -> Result<Value, ErrorObjectOwned>;
19
20 #[method(name = "addStickerPack", param_kind = map)]
21 async fn add_sticker_pack(
22 &self,
23 account: Option<String>,
24 uri: String,
25 ) -> Result<Value, ErrorObjectOwned>;
26
27 #[method(name = "block", param_kind = map)]
28 fn block(
29 &self,
30 account: Option<String>,
31 recipients: Vec<String>,
32 #[allow(non_snake_case)] groupIds: Vec<String>,
33 ) -> Result<Value, ErrorObjectOwned>;
34
35 #[method(name = "deleteLocalAccountData", param_kind = map)]
36 fn delete_local_account_data(
37 &self,
38 account: Option<String>,
39 #[allow(non_snake_case)] ignoreRegistered: Option<bool>,
40 ) -> Result<Value, ErrorObjectOwned>;
41
42 #[method(name = "getAttachment", param_kind = map)]
43 fn get_attachment(
44 &self,
45 account: Option<String>,
46 id: String,
47 recipient: Option<String>,
48 #[allow(non_snake_case)] groupId: Option<String>,
49 ) -> Result<Value, ErrorObjectOwned>;
50
51 #[method(name = "getAvatar", param_kind = map)]
52 fn get_avatar(
53 &self,
54 account: Option<String>,
55 contact: Option<String>,
56 profile: Option<String>,
57 #[allow(non_snake_case)] groupId: Option<String>,
58 ) -> Result<Value, ErrorObjectOwned>;
59
60 #[method(name = "getSticker", param_kind = map)]
61 fn get_sticker(
62 &self,
63 account: Option<String>,
64 #[allow(non_snake_case)] packId: String,
65 #[allow(non_snake_case)] stickerId: u32,
66 ) -> Result<Value, ErrorObjectOwned>;
67
68 #[method(name = "getUserStatus", param_kind = map)]
69 fn get_user_status(
70 &self,
71 account: Option<String>,
72 recipients: Vec<String>,
73 usernames: Vec<String>,
74 ) -> Result<Value, ErrorObjectOwned>;
75
76 #[method(name = "joinGroup", param_kind = map)]
77 fn join_group(&self, account: Option<String>, uri: String) -> Result<Value, ErrorObjectOwned>;
78
79 #[allow(non_snake_case)]
80 #[method(name = "finishChangeNumber", param_kind = map)]
81 fn finish_change_number(
82 &self,
83 account: Option<String>,
84 number: String,
85 verificationCode: String,
86 pin: Option<String>,
87 ) -> Result<Value, ErrorObjectOwned>;
88
89 #[method(name = "finishLink", param_kind = map)]
90 fn finish_link(
91 &self,
92 #[allow(non_snake_case)] deviceLinkUri: String,
93 #[allow(non_snake_case)] deviceName: String,
94 ) -> Result<Value, ErrorObjectOwned>;
95
96 #[method(name = "listAccounts", param_kind = map)]
97 fn list_accounts(&self) -> Result<Value, ErrorObjectOwned>;
98
99 #[method(name = "listContacts", param_kind = map)]
100 fn list_contacts(
101 &self,
102 account: Option<String>,
103 recipients: Vec<String>,
104 #[allow(non_snake_case)] allRecipients: bool,
105 blocked: Option<bool>,
106 name: Option<String>,
107 ) -> Result<Value, ErrorObjectOwned>;
108
109 #[method(name = "listDevices", param_kind = map)]
110 fn list_devices(&self, account: Option<String>) -> Result<Value, ErrorObjectOwned>;
111
112 #[method(name = "listGroups", param_kind = map)]
113 fn list_groups(
114 &self,
115 account: Option<String>,
116 #[allow(non_snake_case)] groupIds: Vec<String>,
117 ) -> Result<Value, ErrorObjectOwned>;
118
119 #[method(name = "listIdentities", param_kind = map)]
120 fn list_identities(
121 &self,
122 account: Option<String>,
123 number: Option<String>,
124 ) -> Result<Value, ErrorObjectOwned>;
125
126 #[method(name = "listStickerPacks", param_kind = map)]
127 fn list_sticker_packs(&self, account: Option<String>) -> Result<Value, ErrorObjectOwned>;
128
129 #[method(name = "quitGroup", param_kind = map)]
130 fn quit_group(
131 &self,
132 account: Option<String>,
133 #[allow(non_snake_case)] groupId: String,
134 delete: bool,
135 admins: Vec<String>,
136 ) -> Result<Value, ErrorObjectOwned>;
137
138 #[method(name = "register", param_kind = map)]
139 fn register(
140 &self,
141 account: Option<String>,
142 voice: bool,
143 captcha: Option<String>,
144 ) -> Result<Value, ErrorObjectOwned>;
145
146 #[method(name = "removeContact", param_kind = map)]
147 fn remove_contact(
148 &self,
149 account: Option<String>,
150 recipient: String,
151 forget: bool,
152 hide: bool,
153 ) -> Result<Value, ErrorObjectOwned>;
154
155 #[method(name = "removeDevice", param_kind = map)]
156 fn remove_device(
157 &self,
158 account: Option<String>,
159 #[allow(non_snake_case)] deviceId: u32,
160 ) -> Result<Value, ErrorObjectOwned>;
161
162 #[method(name = "removePin", param_kind = map)]
163 fn remove_pin(&self, account: Option<String>) -> Result<Value, ErrorObjectOwned>;
164
165 #[method(name = "remoteDelete", param_kind = map)]
166 fn remote_delete(
167 &self,
168 account: Option<String>,
169 #[allow(non_snake_case)] targetTimestamp: u64,
170 recipients: Vec<String>,
171 #[allow(non_snake_case)] groupIds: Vec<String>,
172 #[allow(non_snake_case)] noteToSelf: bool,
173 ) -> Result<Value, ErrorObjectOwned>;
174
175 #[allow(non_snake_case)]
176 #[method(name = "send", param_kind = map)]
177 fn send(
178 &self,
179 account: Option<String>,
180 recipients: Vec<String>,
181 groupIds: Vec<String>,
182 noteToSelf: bool,
183 endSession: bool,
184 message: String,
185 attachments: Vec<String>,
186 viewOnce: bool,
187 mentions: Vec<String>,
188 textStyle: Vec<String>,
189 quoteTimestamp: Option<u64>,
190 quoteAuthor: Option<String>,
191 quoteMessage: Option<String>,
192 quoteMention: Vec<String>,
193 quoteTextStyle: Vec<String>,
194 quoteAttachment: Vec<String>,
195 previewUrl: Option<String>,
196 previewTitle: Option<String>,
197 previewDescription: Option<String>,
198 previewImage: Option<String>,
199 sticker: Option<String>,
200 storyTimestamp: Option<u64>,
201 storyAuthor: Option<String>,
202 editTimestamp: Option<u64>,
203 ) -> Result<Value, ErrorObjectOwned>;
204
205 #[method(name = "sendContacts", param_kind = map)]
206 fn send_contacts(&self, account: Option<String>) -> Result<Value, ErrorObjectOwned>;
207
208 #[method(name = "sendPaymentNotification", param_kind = map)]
209 fn send_payment_notification(
210 &self,
211 account: Option<String>,
212 recipient: String,
213 receipt: String,
214 note: String,
215 ) -> Result<Value, ErrorObjectOwned>;
216
217 #[method(name = "sendReaction", param_kind = map)]
218 fn send_reaction(
219 &self,
220 account: Option<String>,
221 recipients: Vec<String>,
222 #[allow(non_snake_case)] groupIds: Vec<String>,
223 #[allow(non_snake_case)] noteToSelf: bool,
224 emoji: String,
225 #[allow(non_snake_case)] targetAuthor: String,
226 #[allow(non_snake_case)] targetTimestamp: u64,
227 remove: bool,
228 story: bool,
229 ) -> Result<Value, ErrorObjectOwned>;
230
231 #[method(name = "sendReceipt", param_kind = map)]
232 fn send_receipt(
233 &self,
234 account: Option<String>,
235 recipient: String,
236 #[allow(non_snake_case)] targetTimestamps: Vec<u64>,
237 r#type: String,
238 ) -> Result<Value, ErrorObjectOwned>;
239
240 #[method(name = "sendSyncRequest", param_kind = map)]
241 fn send_sync_request(&self, account: Option<String>) -> Result<Value, ErrorObjectOwned>;
242
243 #[method(name = "sendTyping", param_kind = map)]
244 fn send_typing(
245 &self,
246 account: Option<String>,
247 recipients: Vec<String>,
248 #[allow(non_snake_case)] groupIds: Vec<String>,
249 stop: bool,
250 ) -> Result<Value, ErrorObjectOwned>;
251
252 #[method(name = "sendMessageRequestResponse", param_kind = map)]
253 fn send_message_request_response(
254 &self,
255 account: Option<String>,
256 recipients: Vec<String>,
257 #[allow(non_snake_case)] groupIds: Vec<String>,
258 r#type: String,
259 ) -> Result<Value, ErrorObjectOwned>;
260
261 #[method(name = "setPin", param_kind = map)]
262 fn set_pin(&self, account: Option<String>, pin: String) -> Result<Value, ErrorObjectOwned>;
263
264 #[method(name = "submitRateLimitChallenge", param_kind = map)]
265 fn submit_rate_limit_challenge(
266 &self,
267 account: Option<String>,
268 challenge: String,
269 captcha: String,
270 ) -> Result<Value, ErrorObjectOwned>;
271
272 #[method(name = "startChangeNumber", param_kind = map)]
273 fn start_change_number(
274 &self,
275 account: Option<String>,
276 number: String,
277 voice: bool,
278 captcha: Option<String>,
279 ) -> Result<Value, ErrorObjectOwned>;
280
281 #[method(name = "startLink", param_kind = map)]
282 fn start_link(&self, account: Option<String>) -> Result<JsonLink, ErrorObjectOwned>;
283
284 #[method(name = "trust", param_kind = map)]
285 fn trust(
286 &self,
287 account: Option<String>,
288 recipient: String,
289 #[allow(non_snake_case)] trustAllKnownKeys: bool,
290 #[allow(non_snake_case)] verifiedSafetyNumber: Option<String>,
291 ) -> Result<Value, ErrorObjectOwned>;
292
293 #[method(name = "unblock", param_kind = map)]
294 fn unblock(
295 &self,
296 account: Option<String>,
297 recipients: Vec<String>,
298 #[allow(non_snake_case)] groupIds: Vec<String>,
299 ) -> Result<Value, ErrorObjectOwned>;
300
301 #[method(name = "unregister", param_kind = map)]
302 fn unregister(
303 &self,
304 account: Option<String>,
305 #[allow(non_snake_case)] deleteAccount: bool,
306 ) -> Result<Value, ErrorObjectOwned>;
307
308 #[allow(non_snake_case)]
309 #[method(name = "updateAccount", param_kind = map)]
310 fn update_account(
311 &self,
312 account: Option<String>,
313 deviceName: Option<String>,
314 unrestrictedUnidentifiedSender: Option<bool>,
315 discoverableByNumber: Option<bool>,
316 numberSharing: Option<bool>,
317 ) -> Result<Value, ErrorObjectOwned>;
318
319 #[method(name = "updateConfiguration", param_kind = map)]
320 fn update_configuration(
321 &self,
322 account: Option<String>,
323 #[allow(non_snake_case)] readReceipts: Option<bool>,
324 #[allow(non_snake_case)] unidentifiedDeliveryIndicators: Option<bool>,
325 #[allow(non_snake_case)] typingIndicators: Option<bool>,
326 #[allow(non_snake_case)] linkPreviews: Option<bool>,
327 ) -> Result<Value, ErrorObjectOwned>;
328
329 #[method(name = "updateContact", param_kind = map)]
330 fn update_contact(
331 &self,
332 account: Option<String>,
333 recipient: String,
334 name: Option<String>,
335 expiration: Option<u32>,
336 ) -> Result<Value, ErrorObjectOwned>;
337
338 #[method(name = "updateGroup", param_kind = map)]
339 fn update_group(
340 &self,
341 account: Option<String>,
342 #[allow(non_snake_case)] groupId: Option<String>,
343 name: Option<String>,
344 description: Option<String>,
345 avatar: Option<String>,
346 member: Vec<String>,
347 #[allow(non_snake_case)] removeMember: Vec<String>,
348 admin: Vec<String>,
349 #[allow(non_snake_case)] removeAdmin: Vec<String>,
350 ban: Vec<String>,
351 unban: Vec<String>,
352 #[allow(non_snake_case)] resetLink: bool,
353 #[allow(non_snake_case)] link: Option<String>,
354 #[allow(non_snake_case)] setPermissionAddMember: Option<String>,
355 #[allow(non_snake_case)] setPermissionEditDetails: Option<String>,
356 #[allow(non_snake_case)] setPermissionSendMessages: Option<String>,
357 expiration: Option<u32>,
358 ) -> Result<Value, ErrorObjectOwned>;
359
360 #[method(name = "updateProfile", param_kind = map)]
361 fn update_profile(
362 &self,
363 account: Option<String>,
364 #[allow(non_snake_case)] givenName: Option<String>,
365 #[allow(non_snake_case)] familyName: Option<String>,
366 about: Option<String>,
367 #[allow(non_snake_case)] aboutEmoji: Option<String>,
368 #[allow(non_snake_case)] mobileCoinAddress: Option<String>,
369 avatar: Option<String>,
370 #[allow(non_snake_case)] removeAvatar: bool,
371 ) -> Result<Value, ErrorObjectOwned>;
372
373 #[method(name = "uploadStickerPack", param_kind = map)]
374 fn upload_sticker_pack(
375 &self,
376 account: Option<String>,
377 path: String,
378 ) -> Result<Value, ErrorObjectOwned>;
379
380 #[method(name = "verify", param_kind = map)]
381 fn verify(
382 &self,
383 account: Option<String>,
384 #[allow(non_snake_case)] verificationCode: String,
385 pin: Option<String>,
386 ) -> Result<Value, ErrorObjectOwned>;
387
388 #[subscription(
389 name = "subscribeReceive" => "receive",
390 unsubscribe = "unsubscribeReceive",
391 item = RecvMessage,
392 param_kind = map
393 )]
394 async fn subscribe_receive(&self, account: Option<String>) -> SubscriptionResult;
395
396 #[method(name = "version")]
397 fn version(&self) -> Result<Value, ErrorObjectOwned>;
398}
399
400#[allow(unused)]
401#[derive(Deserialize)]
402#[serde(rename_all = "camelCase")]
403pub struct JsonLink {
404 pub device_link_uri: String,
405}
406
407#[derive(Debug, Deserialize)]
408#[serde(rename_all = "camelCase")]
409pub struct RecvMessage {
410 pub envelope: Envelope,
411}
412
413#[allow(unused)]
414#[derive(Debug, Deserialize)]
415#[serde(rename_all = "camelCase")]
416pub struct Envelope {
417 pub source: String,
418 pub source_number: String,
419 pub source_uuid: String,
420 pub source_name: String,
421 pub source_device: i64,
422 pub timestamp: u64,
423 pub data_message: Option<DataMessage>,
424}
425
426#[derive(Debug, Deserialize)]
427#[serde(rename_all = "camelCase")]
428pub struct DataMessage {
429 pub timestamp: u64,
430 pub message: String,
431 #[serde(default)]
432 pub group_info: Option<GroupInfo>,
433}
434
435#[derive(Debug, Deserialize)]
436#[serde(rename_all = "camelCase")]
437pub struct GroupInfo {
438 pub group_id: String,
439}
440
441#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
443#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
444pub enum TrustLevel {
445 Untrusted,
446 TrustedUnverified,
447 TrustedVerified,
448}
449
450impl TrustLevel {
451 pub fn is_trusted(self) -> bool {
452 matches!(
453 self,
454 TrustLevel::TrustedUnverified | TrustLevel::TrustedVerified
455 )
456 }
457}
458
459#[derive(Debug, Deserialize)]
461#[serde(rename_all = "camelCase")]
462pub struct Identity {
463 pub safety_number: String,
464 pub trust_level: TrustLevel,
465}
466
467pub async fn connect_tcp(
469 tcp: impl ToSocketAddrs,
470) -> Result<impl SubscriptionClientT, std::io::Error> {
471 let (sender, receiver) = crate::transports::tcp::connect(tcp).await?;
472
473 Ok(ClientBuilder::default().build_with_tokio(sender, receiver))
474}
475
476#[cfg(unix)]
478pub async fn connect_ipc(
479 path: impl AsRef<std::path::Path>,
480) -> Result<impl SubscriptionClientT, std::io::Error> {
481 let (sender, receiver) = crate::transports::ipc::connect(path).await?;
482
483 Ok(ClientBuilder::default().build_with_tokio(sender, receiver))
484}
485
486impl Envelope {
487 pub async fn send_read_receipt(
489 &self,
490 client: &impl RpcClient,
491 account: impl Into<String>,
492 ) -> Result<(), RpcClientError> {
493 if let Some(dm) = self.data_message.as_ref() {
494 let _ = client
495 .send_receipt(
496 Some(account.into()),
497 self.source_uuid.clone(),
498 vec![dm.timestamp],
499 "read".into(),
500 )
501 .await?;
502 }
503 Ok(())
504 }
505}
506
507#[derive(Clone, Debug)]
509pub enum MessageTarget {
510 Recipients(Vec<String>),
512 Group(String),
514}
515
516pub struct SignalMessage {
518 pub sender: String,
519 pub target: MessageTarget,
520 pub message: String,
521 pub attachments: Vec<String>,
522}
523
524impl SignalMessage {
525 #[allow(non_snake_case)]
526 pub async fn send(self, client: &impl RpcClient) -> Result<(), RpcClientError> {
527 let message_len_utf16: usize = self.message.chars().map(|c| c.len_utf16()).sum();
529 let account = Some(self.sender);
556 let (recipients, groupIds) = match self.target {
557 MessageTarget::Recipients(r) => (r, vec![]),
558 MessageTarget::Group(g) => (vec![], vec![g]),
559 };
560 let noteToSelf = false;
561 let endSession = false;
562 let message = self.message;
563 let attachments = self.attachments;
564 let viewOnce = false;
565 let mentions = vec![];
566 let textStyle = vec![format!("0:{message_len_utf16}:MONOSPACE")];
567 let quoteTimestamp = None;
568 let quoteAuthor = None;
569 let quoteMention = vec![];
570 let quoteMessage = None;
571 let quoteTextStyle = vec![];
572 let quoteAttachment = vec![];
573 let previewUrl = None;
574 let previewTitle = None;
575 let previewDescription = None;
576 let previewImage = None;
577 let sticker = None;
578 let storyTimestamp = None;
579 let storyAuthor = None;
580 let editTimestamp = None;
581
582 let _resp = client
583 .send(
584 account,
585 recipients,
586 groupIds,
587 noteToSelf,
588 endSession,
589 message,
590 attachments,
591 viewOnce,
592 mentions,
593 textStyle,
594 quoteTimestamp,
595 quoteAuthor,
596 quoteMessage,
597 quoteMention,
598 quoteTextStyle,
599 quoteAttachment,
600 previewUrl,
601 previewTitle,
602 previewDescription,
603 previewImage,
604 sticker,
605 storyTimestamp,
606 storyAuthor,
607 editTimestamp,
608 )
609 .await?;
610 Ok(())
611 }
612}