diff --git a/CHANGELOG.md b/CHANGELOG.md index 100646641..4fe0c59f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,13 @@ and `dc_event_emitter_unref()` should be used instead of `dc_accounts_event_emitter_unref`. - add `dc_contact_was_seen_recently()` #3560 +- jsonrpc: add functions: + - `deleteChat()` + - `getChatEncryptionInfo()` + - `getChatSecurejoinQrCodeSvg()` + - `leaveGroup()` + - `removeContactFromChat()` + - `addContactToChat()` - jsonrpc: add `is_broadcast` property to `ChatListItemFetchResult` #3584 - jsonrpc: add `was_seen_recently` property to `ChatListItemFetchResult`, `FullChat` and `Contact` #3584 diff --git a/deltachat-jsonrpc/src/api/mod.rs b/deltachat-jsonrpc/src/api/mod.rs index fabe091f0..733e38af6 100644 --- a/deltachat-jsonrpc/src/api/mod.rs +++ b/deltachat-jsonrpc/src/api/mod.rs @@ -1,6 +1,6 @@ use anyhow::{anyhow, bail, Context, Result}; use deltachat::{ - chat::{get_chat_media, get_chat_msgs, ChatId}, + chat::{add_contact_to_chat, get_chat_media, get_chat_msgs, remove_contact_from_chat, ChatId}, chatlist::Chatlist, config::Config, contact::{may_be_valid_addr, Contact, ContactId}, @@ -8,6 +8,8 @@ use deltachat::{ message::{Message, MsgId, Viewtype}, provider::get_provider_info, qr, + qr_code_generator::get_securejoin_qr_svg, + securejoin, webxdc::StatusUpdateSerial, }; use std::collections::BTreeMap; @@ -192,12 +194,10 @@ impl CommandApi { } /// Set configuration values from a QR code. (technically from the URI that is stored in the qrcode) - /// Before this function is called, dc_check_qr() should confirm the type of the - /// QR code is DC_QR_ACCOUNT or DC_QR_WEBRTC_INSTANCE. + /// Before this function is called, `checkQr()` should confirm the type of the + /// QR code is `account` or `webrtcInstance`. /// /// Internally, the function will call dc_set_config() with the appropriate keys, - /// e.g. `addr` and `mail_pw` for DC_QR_ACCOUNT - /// or `webrtc_instance` for DC_QR_WEBRTC_INSTANCE. async fn set_config_from_qr(&self, account_id: u32, qr_content: String) -> Result<()> { let ctx = self.get_context(account_id).await?; qr::set_config_from_qr(&ctx, &qr_content).await @@ -378,6 +378,108 @@ impl CommandApi { ChatId::new(chat_id).block(&ctx).await } + /// Delete a chat. + /// + /// Messages are deleted from the device and the chat database entry is deleted. + /// After that, the event #DC_EVENT_MSGS_CHANGED is posted. + /// + /// Things that are _not done_ implicitly: + /// + /// - Messages are **not deleted from the server**. + /// - The chat or the contact is **not blocked**, so new messages from the user/the group may appear as a contact request + /// and the user may create the chat again. + /// - **Groups are not left** - this would + /// be unexpected as (1) deleting a normal chat also does not prevent new mails + /// from arriving, (2) leaving a group requires sending a message to + /// all group members - especially for groups not used for a longer time, this is + /// really unexpected when deletion results in contacting all members again, + /// (3) only leaving groups is also a valid usecase. + /// + /// To leave a chat explicitly, use dc_remove_contact_from_chat() with + /// chat_id=DC_CONTACT_ID_SELF) + // TODO fix doc comment after adding dc_remove_contact_from_chat + async fn delete_chat(&self, account_id: u32, chat_id: u32) -> Result<()> { + let ctx = self.get_context(account_id).await?; + ChatId::new(chat_id).delete(&ctx).await + } + + /// Get encryption info for a chat. + /// Get a multi-line encryption info, containing encryption preferences of all members. + /// Can be used to find out why messages sent to group are not encrypted. + /// + /// returns Multi-line text + async fn get_chat_encryption_info(&self, account_id: u32, chat_id: u32) -> Result { + let ctx = self.get_context(account_id).await?; + ChatId::new(chat_id).get_encryption_info(&ctx).await + } + + /// Get QR code (text and SVG) that will offer an Setup-Contact or Verified-Group invitation. + /// The QR code is compatible to the OPENPGP4FPR format + /// so that a basic fingerprint comparison also works e.g. with OpenKeychain. + /// + /// The scanning device will pass the scanned content to `checkQr()` then; + /// if `checkQr()` returns `askVerifyContact` or `askVerifyGroup` + /// an out-of-band-verification can be joined using dc_join_securejoin() + /// + /// chat_id: If set to a group-chat-id, + /// the Verified-Group-Invite protocol is offered in the QR code; + /// works for protected groups as well as for normal groups. + /// If not set, the Setup-Contact protocol is offered in the QR code. + /// See https://countermitm.readthedocs.io/en/latest/new.html + /// for details about both protocols. + // TODO fix doc comment after adding dc_join_securejoin + async fn get_chat_securejoin_qr_code_svg( + &self, + account_id: u32, + chat_id: Option, + ) -> Result<(String, String)> { + let ctx = self.get_context(account_id).await?; + let chat = chat_id.map(ChatId::new); + Ok(( + securejoin::get_securejoin_qr(&ctx, chat).await?, + get_securejoin_qr_svg(&ctx, chat).await?, + )) + } + + async fn leave_group(&self, account_id: u32, chat_id: u32) -> Result<()> { + let ctx = self.get_context(account_id).await?; + remove_contact_from_chat(&ctx, ChatId::new(chat_id), ContactId::SELF).await + } + + /// Remove a member from a group. + /// + /// If the group is already _promoted_ (any message was sent to the group), + /// all group members are informed by a special status message that is sent automatically by this function. + /// + /// Sends out #DC_EVENT_CHAT_MODIFIED and #DC_EVENT_MSGS_CHANGED if a status message was sent. + async fn remove_contact_from_chat( + &self, + account_id: u32, + chat_id: u32, + contact_id: u32, + ) -> Result<()> { + let ctx = self.get_context(account_id).await?; + remove_contact_from_chat(&ctx, ChatId::new(chat_id), ContactId::new(contact_id)).await + } + + /// Add a member to a group. + /// + /// If the group is already _promoted_ (any message was sent to the group), + /// all group members are informed by a special status message that is sent automatically by this function. + /// + /// If the group has group protection enabled, only verified contacts can be added to the group. + /// + /// Sends out #DC_EVENT_CHAT_MODIFIED and #DC_EVENT_MSGS_CHANGED if a status message was sent. + async fn add_contact_to_chat( + &self, + account_id: u32, + chat_id: u32, + contact_id: u32, + ) -> Result<()> { + let ctx = self.get_context(account_id).await?; + add_contact_to_chat(&ctx, ChatId::new(chat_id), ContactId::new(contact_id)).await + } + // for now only text messages, because we only used text messages in desktop thusfar async fn add_device_message( &self, diff --git a/deltachat-jsonrpc/typescript/generated/client.ts b/deltachat-jsonrpc/typescript/generated/client.ts index 9b01d0b65..44fa7fdbe 100644 --- a/deltachat-jsonrpc/typescript/generated/client.ts +++ b/deltachat-jsonrpc/typescript/generated/client.ts @@ -111,12 +111,10 @@ export class RawClient { /** * Set configuration values from a QR code. (technically from the URI that is stored in the qrcode) - * Before this function is called, dc_check_qr() should confirm the type of the - * QR code is DC_QR_ACCOUNT or DC_QR_WEBRTC_INSTANCE. + * Before this function is called, `checkQr()` should confirm the type of the + * QR code is `account` or `webrtcInstance`. * * Internally, the function will call dc_set_config() with the appropriate keys, - * e.g. `addr` and `mail_pw` for DC_QR_ACCOUNT - * or `webrtc_instance` for DC_QR_WEBRTC_INSTANCE. */ public setConfigFromQr(accountId: T.U32, qrContent: string): Promise { return (this._transport.request('set_config_from_qr', [accountId, qrContent] as RPC.Params)) as Promise; @@ -216,6 +214,93 @@ export class RawClient { return (this._transport.request('block_chat', [accountId, chatId] as RPC.Params)) as Promise; } + /** + * Delete a chat. + * + * Messages are deleted from the device and the chat database entry is deleted. + * After that, the event #DC_EVENT_MSGS_CHANGED is posted. + * + * Things that are _not done_ implicitly: + * + * - Messages are **not deleted from the server**. + * - The chat or the contact is **not blocked**, so new messages from the user/the group may appear + * and the user may create the chat again. + * - **Groups are not left** - this would + * be unexpected as (1) deleting a normal chat also does not prevent new mails + * from arriving, (2) leaving a group requires sending a message to + * all group members - especially for groups not used for a longer time, this is + * really unexpected when deletion results in contacting all members again, + * (3) only leaving groups is also a valid usecase. + * + * To leave a chat explicitly, use dc_remove_contact_from_chat() with + * chat_id=DC_CONTACT_ID_SELF) + */ + public deleteChat(accountId: T.U32, chatId: T.U32): Promise { + return (this._transport.request('delete_chat', [accountId, chatId] as RPC.Params)) as Promise; + } + + /** + * Get encryption info for a chat. + * Get a multi-line encryption info, containing encryption preferences of all members. + * Can be used to find out why messages sent to group are not encrypted. + * + * returns Multi-line text + */ + public getChatEncryptionInfo(accountId: T.U32, chatId: T.U32): Promise { + return (this._transport.request('get_chat_encryption_info', [accountId, chatId] as RPC.Params)) as Promise; + } + + /** + * Get QR code (text and SVG) that will offer an Setup-Contact or Verified-Group invitation. + * The QR code is compatible to the OPENPGP4FPR format + * so that a basic fingerprint comparison also works e.g. with OpenKeychain. + * + * The scanning device will pass the scanned content to `checkQr()` then; + * if `checkQr()` returns `askVerifyContact` or `askVerifyGroup` + * an out-of-band-verification can be joined using dc_join_securejoin() + * + * chat_id: If set to a group-chat-id, + * the Verified-Group-Invite protocol is offered in the QR code; + * works for protected groups as well as for normal groups. + * If not set, the Setup-Contact protocol is offered in the QR code. + * See https://countermitm.readthedocs.io/en/latest/new.html + * for details about both protocols. + */ + public getChatSecurejoinQrCodeSvg(accountId: T.U32, chatId: (T.U32|null)): Promise<[string,string]> { + return (this._transport.request('get_chat_securejoin_qr_code_svg', [accountId, chatId] as RPC.Params)) as Promise<[string,string]>; + } + + + public leaveGroup(accountId: T.U32, chatId: T.U32): Promise { + return (this._transport.request('leave_group', [accountId, chatId] as RPC.Params)) as Promise; + } + + /** + * Remove a member from a group. + * + * If the group is already _promoted_ (any message was sent to the group), + * all group members are informed by a special status message that is sent automatically by this function. + * + * Sends out #DC_EVENT_CHAT_MODIFIED and #DC_EVENT_MSGS_CHANGED if a status message was sent. + */ + public removeContactFromChat(accountId: T.U32, chatId: T.U32, contactId: T.U32): Promise { + return (this._transport.request('remove_contact_from_chat', [accountId, chatId, contactId] as RPC.Params)) as Promise; + } + + /** + * Add a member to a group. + * + * If the group is already _promoted_ (any message was sent to the group), + * all group members are informed by a special status message that is sent automatically by this function. + * + * If the group has group protection enabled, only verified contacts can be added to the group. + * + * Sends out #DC_EVENT_CHAT_MODIFIED and #DC_EVENT_MSGS_CHANGED if a status message was sent. + */ + public addContactToChat(accountId: T.U32, chatId: T.U32, contactId: T.U32): Promise { + return (this._transport.request('add_contact_to_chat', [accountId, chatId, contactId] as RPC.Params)) as Promise; + } + public addDeviceMessage(accountId: T.U32, label: string, text: string): Promise { return (this._transport.request('add_device_message', [accountId, label, text] as RPC.Params)) as Promise; diff --git a/deltachat-jsonrpc/typescript/generated/types.ts b/deltachat-jsonrpc/typescript/generated/types.ts index 592780250..901feefb8 100644 --- a/deltachat-jsonrpc/typescript/generated/types.ts +++ b/deltachat-jsonrpc/typescript/generated/types.ts @@ -103,4 +103,4 @@ export type WebxdcMessageInfo={ * True if full internet access should be granted to the app. */ "internetAccess":boolean;}; -export type __AllTyps=[string,boolean,Record,U32,U32,null,(U32)[],U32,null,(U32|null),(Account)[],U32,Account,U32,string,(ProviderInfo|null),U32,boolean,U32,Record,U32,string,(string|null),null,U32,Record,null,U32,string,null,U32,string,Qr,U32,string,(string|null),U32,(string)[],Record,U32,null,U32,null,U32,(U32)[],U32,U32,Usize,U32,string,U32,U32,string,null,U32,(U32|null),(string|null),(U32|null),(ChatListEntry)[],U32,(ChatListEntry)[],Record,U32,U32,FullChat,U32,U32,null,U32,U32,null,U32,string,string,U32,U32,U32,U32,(U32)[],U32,U32,Message,U32,(U32)[],Record,U32,U32,Contact,U32,string,(string|null),U32,U32,U32,U32,U32,U32,null,U32,U32,null,U32,(Contact)[],U32,U32,(string|null),(U32)[],U32,U32,(string|null),(Contact)[],U32,(U32)[],Record,U32,(U32|null),Viewtype,(Viewtype|null),(Viewtype|null),(U32)[],U32,U32,string,string,null,U32,U32,U32,string,U32,U32,WebxdcMessageInfo,U32,string,U32,U32]; +export type __AllTyps=[string,boolean,Record,U32,U32,null,(U32)[],U32,null,(U32|null),(Account)[],U32,Account,U32,string,(ProviderInfo|null),U32,boolean,U32,Record,U32,string,(string|null),null,U32,Record,null,U32,string,null,U32,string,Qr,U32,string,(string|null),U32,(string)[],Record,U32,null,U32,null,U32,(U32)[],U32,U32,Usize,U32,string,U32,U32,string,null,U32,(U32|null),(string|null),(U32|null),(ChatListEntry)[],U32,(ChatListEntry)[],Record,U32,U32,FullChat,U32,U32,null,U32,U32,null,U32,U32,null,U32,U32,string,U32,(U32|null),[string,string],U32,U32,null,U32,U32,U32,null,U32,U32,U32,null,U32,string,string,U32,U32,U32,U32,(U32)[],U32,U32,Message,U32,(U32)[],Record,U32,U32,Contact,U32,string,(string|null),U32,U32,U32,U32,U32,U32,null,U32,U32,null,U32,(Contact)[],U32,U32,(string|null),(U32)[],U32,U32,(string|null),(Contact)[],U32,(U32)[],Record,U32,(U32|null),Viewtype,(Viewtype|null),(Viewtype|null),(U32)[],U32,U32,string,string,null,U32,U32,U32,string,U32,U32,WebxdcMessageInfo,U32,string,U32,U32];