X-Git-Url: https://git.nmode.ca/signal-cli/blobdiff_plain/5ed9db4f08e52ed0c42cb42740f85d2ad346e13c..d84362eb0f022d8bd22321afb5f082b3881f316c:/client/src/main.rs diff --git a/client/src/main.rs b/client/src/main.rs index ea9cb9c8..71903e1d 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -1,66 +1,80 @@ -use clap::Parser; -use jsonrpc_client_transports::{RpcError, TypedSubscriptionStream}; -use jsonrpc_core::{futures_util::StreamExt, Value}; use std::{path::PathBuf, time::Duration}; + +use clap::Parser; +use jsonrpsee::core::client::{Error as RpcError, Subscription, SubscriptionClientT}; +use serde_json::Value; use tokio::{select, time::sleep}; -use crate::cli::{GroupPermission, LinkState}; +use cli::Cli; + +use crate::cli::{CliCommands, GroupPermission, LinkState}; +use crate::jsonrpc::RpcClient; mod cli; -#[allow(clippy::too_many_arguments)] +#[allow(non_snake_case, clippy::too_many_arguments)] mod jsonrpc; -mod tcp; +mod transports; const DEFAULT_TCP: &str = "127.0.0.1:7583"; const DEFAULT_SOCKET_SUFFIX: &str = "signal-cli/socket"; +const DEFAULT_HTTP: &str = "http://localhost:8080/api/v1/rpc"; #[tokio::main] async fn main() -> Result<(), anyhow::Error> { let cli = cli::Cli::parse(); - let client = connect(&cli) - .await - .map_err(|e| anyhow::anyhow!("Failed to connect to socket: {e}"))?; + let result = connect(cli).await; + + match result { + Ok(Value::Null) => {} + Ok(v) => println!("{v}"), + Err(e) => return Err(anyhow::anyhow!("JSON-RPC command failed: {e:?}")), + } + Ok(()) +} - let result = match cli.command { - cli::CliCommands::Receive { timeout } => { - let mut stream = client - .subscribe_receive(cli.account) - .map_err(|e| anyhow::anyhow!("JSON-RPC command failed: {:?}", e))?; +async fn handle_command( + cli: Cli, + client: impl SubscriptionClientT + Sync, +) -> Result { + match cli.command { + CliCommands::Receive { timeout } => { + let mut stream = client.subscribe_receive(cli.account).await?; { while let Some(v) = stream_next(timeout, &mut stream).await { - let v = v.map_err(|e| anyhow::anyhow!("JSON-RPC command failed: {:?}", e))?; + let v = v?; println!("{v}"); } } - return Ok(()); + stream.unsubscribe().await?; + Ok(Value::Null) } - cli::CliCommands::AddDevice { uri } => client.add_device(cli.account, uri).await, - cli::CliCommands::Block { + CliCommands::AddDevice { uri } => client.add_device(cli.account, uri).await, + CliCommands::Block { recipient, group_id, } => client.block(cli.account, recipient, group_id).await, - cli::CliCommands::DeleteLocalAccountData { ignore_registered } => { + CliCommands::DeleteLocalAccountData { ignore_registered } => { client .delete_local_account_data(cli.account, ignore_registered) .await } - cli::CliCommands::GetUserStatus { recipient } => { + CliCommands::GetUserStatus { recipient } => { client.get_user_status(cli.account, recipient).await } - cli::CliCommands::JoinGroup { uri } => client.join_group(cli.account, uri).await, - cli::CliCommands::Link { name } => { + CliCommands::JoinGroup { uri } => client.join_group(cli.account, uri).await, + CliCommands::Link { name } => { let url = client .start_link(cli.account) .await - .map_err(|e| anyhow::anyhow!("JSON-RPC command startLink failed: {e:?}",))? + .map_err(|e| RpcError::Custom(format!("JSON-RPC command startLink failed: {e:?}")))? .device_link_uri; println!("{}", url); client.finish_link(url, name).await } - cli::CliCommands::ListAccounts => client.list_accounts().await, - cli::CliCommands::ListContacts { + CliCommands::ListAccounts => client.list_accounts().await, + CliCommands::ListContacts { recipient, all_recipients, blocked, @@ -70,16 +84,14 @@ async fn main() -> Result<(), anyhow::Error> { .list_contacts(cli.account, recipient, all_recipients, blocked, name) .await } - cli::CliCommands::ListDevices => client.list_devices(cli.account).await, - cli::CliCommands::ListGroups { + CliCommands::ListDevices => client.list_devices(cli.account).await, + CliCommands::ListGroups { detailed: _, group_id, } => client.list_groups(cli.account, group_id).await, - cli::CliCommands::ListIdentities { number } => { - client.list_identities(cli.account, number).await - } - cli::CliCommands::ListStickerPacks => client.list_sticker_packs(cli.account).await, - cli::CliCommands::QuitGroup { + CliCommands::ListIdentities { number } => client.list_identities(cli.account, number).await, + CliCommands::ListStickerPacks => client.list_sticker_packs(cli.account).await, + CliCommands::QuitGroup { group_id, delete, admin, @@ -88,17 +100,23 @@ async fn main() -> Result<(), anyhow::Error> { .quit_group(cli.account, group_id, delete, admin) .await } - cli::CliCommands::Register { voice, captcha } => { + CliCommands::Register { voice, captcha } => { client.register(cli.account, voice, captcha).await } - cli::CliCommands::RemoveContact { recipient, forget } => { - client.remove_contact(cli.account, recipient, forget).await + CliCommands::RemoveContact { + recipient, + forget, + hide, + } => { + client + .remove_contact(cli.account, recipient, forget, hide) + .await } - cli::CliCommands::RemoveDevice { device_id } => { + CliCommands::RemoveDevice { device_id } => { client.remove_device(cli.account, device_id).await } - cli::CliCommands::RemovePin => client.remove_pin(cli.account).await, - cli::CliCommands::RemoteDelete { + CliCommands::RemovePin => client.remove_pin(cli.account).await, + CliCommands::RemoteDelete { target_timestamp, recipient, group_id, @@ -114,7 +132,7 @@ async fn main() -> Result<(), anyhow::Error> { ) .await } - cli::CliCommands::Send { + CliCommands::Send { recipient, group_id, note_to_self, @@ -122,13 +140,21 @@ async fn main() -> Result<(), anyhow::Error> { message, attachment, mention, + text_style, quote_timestamp, quote_author, quote_message, quote_mention, + quote_text_style, + quote_attachment, + preview_url, + preview_title, + preview_description, + preview_image, sticker, story_timestamp, story_author, + edit_timestamp, } => { client .send( @@ -140,18 +166,26 @@ async fn main() -> Result<(), anyhow::Error> { message.unwrap_or_default(), attachment, mention, + text_style, quote_timestamp, quote_author, quote_message, quote_mention, + quote_text_style, + quote_attachment, + preview_url, + preview_title, + preview_description, + preview_image, sticker, story_timestamp, story_author, + edit_timestamp, ) .await } - cli::CliCommands::SendContacts => client.send_contacts(cli.account).await, - cli::CliCommands::SendPaymentNotification { + CliCommands::SendContacts => client.send_contacts(cli.account).await, + CliCommands::SendPaymentNotification { recipient, receipt, note, @@ -160,7 +194,7 @@ async fn main() -> Result<(), anyhow::Error> { .send_payment_notification(cli.account, recipient, receipt, note) .await } - cli::CliCommands::SendReaction { + CliCommands::SendReaction { recipient, group_id, note_to_self, @@ -184,7 +218,7 @@ async fn main() -> Result<(), anyhow::Error> { ) .await } - cli::CliCommands::SendReceipt { + CliCommands::SendReceipt { recipient, target_timestamp, r#type, @@ -201,8 +235,8 @@ async fn main() -> Result<(), anyhow::Error> { ) .await } - cli::CliCommands::SendSyncRequest => client.send_sync_request(cli.account).await, - cli::CliCommands::SendTyping { + CliCommands::SendSyncRequest => client.send_sync_request(cli.account).await, + CliCommands::SendTyping { recipient, group_id, stop, @@ -211,13 +245,13 @@ async fn main() -> Result<(), anyhow::Error> { .send_typing(cli.account, recipient, group_id, stop) .await } - cli::CliCommands::SetPin { pin } => client.set_pin(cli.account, pin).await, - cli::CliCommands::SubmitRateLimitChallenge { challenge, captcha } => { + CliCommands::SetPin { pin } => client.set_pin(cli.account, pin).await, + CliCommands::SubmitRateLimitChallenge { challenge, captcha } => { client .submit_rate_limit_challenge(cli.account, challenge, captcha) .await } - cli::CliCommands::Trust { + CliCommands::Trust { recipient, trust_all_known_keys, verified_safety_number, @@ -231,17 +265,30 @@ async fn main() -> Result<(), anyhow::Error> { ) .await } - cli::CliCommands::Unblock { + CliCommands::Unblock { recipient, group_id, } => client.unblock(cli.account, recipient, group_id).await, - cli::CliCommands::Unregister { delete_account } => { + CliCommands::Unregister { delete_account } => { client.unregister(cli.account, delete_account).await } - cli::CliCommands::UpdateAccount { device_name } => { - client.update_account(cli.account, device_name).await + CliCommands::UpdateAccount { + device_name, + unrestricted_unidentified_sender, + discoverable_by_number, + number_sharing, + } => { + client + .update_account( + cli.account, + device_name, + unrestricted_unidentified_sender, + discoverable_by_number, + number_sharing, + ) + .await } - cli::CliCommands::UpdateConfiguration { + CliCommands::UpdateConfiguration { read_receipts, unidentified_delivery_indicators, typing_indicators, @@ -257,7 +304,7 @@ async fn main() -> Result<(), anyhow::Error> { ) .await } - cli::CliCommands::UpdateContact { + CliCommands::UpdateContact { recipient, expiration, name, @@ -266,7 +313,7 @@ async fn main() -> Result<(), anyhow::Error> { .update_contact(cli.account, recipient, name, expiration) .await } - cli::CliCommands::UpdateGroup { + CliCommands::UpdateGroup { group_id, name, description, @@ -319,7 +366,7 @@ async fn main() -> Result<(), anyhow::Error> { ) .await } - cli::CliCommands::UpdateProfile { + CliCommands::UpdateProfile { given_name, family_name, about, @@ -341,26 +388,94 @@ async fn main() -> Result<(), anyhow::Error> { ) .await } - cli::CliCommands::UploadStickerPack { path } => { + CliCommands::UploadStickerPack { path } => { client.upload_sticker_pack(cli.account, path).await } - cli::CliCommands::Verify { + CliCommands::Verify { verification_code, pin, } => client.verify(cli.account, verification_code, pin).await, - cli::CliCommands::Version => client.version().await, - }; - - result - .map(|v| println!("{v}")) - .map_err(|e| anyhow::anyhow!("JSON-RPC command failed: {e:?}",))?; - Ok(()) + CliCommands::Version => client.version().await, + CliCommands::AddStickerPack { uri } => client.add_sticker_pack(cli.account, uri).await, + CliCommands::FinishChangeNumber { + number, + verification_code, + pin, + } => { + client + .finish_change_number(cli.account, number, verification_code, pin) + .await + } + CliCommands::GetAttachment { + id, + recipient, + group_id, + } => { + client + .get_attachment(cli.account, id, recipient, group_id) + .await + } + CliCommands::GetAvatar { + contact, + profile, + group_id, + } => { + client + .get_avatar(cli.account, contact, profile, group_id) + .await + } + CliCommands::GetSticker { + pack_id, + sticker_id, + } => client.get_sticker(cli.account, pack_id, sticker_id).await, + CliCommands::StartChangeNumber { + number, + voice, + captcha, + } => { + client + .start_change_number(cli.account, number, voice, captcha) + .await + } + CliCommands::SendMessageRequestResponse { + recipient, + group_id, + r#type, + } => { + client + .send_message_request_response( + cli.account, + recipient, + group_id, + match r#type { + cli::MessageRequestResponseType::Accept => "accept".to_owned(), + cli::MessageRequestResponseType::Delete => "delete".to_owned(), + }, + ) + .await + } + } } -async fn connect(cli: &cli::Cli) -> Result { - if let Some(tcp) = cli.json_rpc_tcp { +async fn connect(cli: Cli) -> Result { + if let Some(http) = &cli.json_rpc_http { + let uri = if let Some(uri) = http { + uri + } else { + DEFAULT_HTTP + }; + let client = jsonrpc::connect_http(uri) + .await + .map_err(|e| RpcError::Custom(format!("Failed to connect to socket: {e}")))?; + + handle_command(cli, client).await + } else if let Some(tcp) = cli.json_rpc_tcp { let socket_addr = tcp.unwrap_or_else(|| DEFAULT_TCP.parse().unwrap()); - jsonrpc::connect_tcp(socket_addr).await + let client = jsonrpc::connect_tcp(socket_addr) + .await + .map_err(|e| RpcError::Custom(format!("Failed to connect to socket: {e}")))?; + + handle_command(cli, client).await } else { let socket_path = cli .json_rpc_socket @@ -374,13 +489,17 @@ async fn connect(cli: &cli::Cli) -> Result { }) }) .unwrap_or_else(|| ("/run".to_owned() + DEFAULT_SOCKET_SUFFIX).into()); - jsonrpc::connect_unix(socket_path).await + let client = jsonrpc::connect_unix(socket_path) + .await + .map_err(|e| RpcError::Custom(format!("Failed to connect to socket: {e}")))?; + + handle_command(cli, client).await } } async fn stream_next( timeout: f64, - stream: &mut TypedSubscriptionStream, + stream: &mut Subscription, ) -> Option> { if timeout < 0.0 { stream.next().await