Merge pull request #1735 from deltachat/invite-call-api

add APIs for videochats
This commit is contained in:
bjoern
2020-07-26 18:11:02 +02:00
committed by GitHub
14 changed files with 352 additions and 15 deletions

View File

@@ -1374,11 +1374,12 @@ pub(crate) fn msgtype_has_file(msgtype: Viewtype) -> bool {
Viewtype::Voice => true,
Viewtype::Video => true,
Viewtype::File => true,
Viewtype::VideochatInvitation => false,
}
}
async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<(), Error> {
if msg.viewtype == Viewtype::Text {
if msg.viewtype == Viewtype::Text || msg.viewtype == Viewtype::VideochatInvitation {
// the caller should check if the message text is empty
} else if msgtype_has_file(msg.viewtype) {
let blob = msg
@@ -1611,6 +1612,44 @@ pub async fn send_text_msg(
send_msg(context, chat_id, &mut msg).await
}
pub async fn send_videochat_invitation(context: &Context, chat_id: ChatId) -> Result<MsgId, Error> {
ensure!(
!chat_id.is_special(),
"video chat invitation cannot be sent to special chat: {}",
chat_id
);
let instance = if let Some(instance) = context.get_config(Config::WebrtcInstance).await {
if !instance.is_empty() {
instance
} else {
bail!("webrtc_instance is empty");
}
} else {
bail!("webrtc_instance not set");
};
let room = dc_create_id();
let instance = if instance.contains("$ROOM") {
instance.replace("$ROOM", &room)
} else {
format!("{}{}", instance, room)
};
let mut msg = Message::new(Viewtype::VideochatInvitation);
msg.param.set(Param::WebrtcRoom, &instance);
msg.text = Some(
context
.stock_string_repl_str(
StockMessage::VideochatInviteMsgBody,
Message::parse_webrtc_instance(&instance).1,
)
.await,
);
send_msg(context, chat_id, &mut msg).await
}
pub async fn get_chat_msgs(
context: &Context,
chat_id: ChatId,

View File

@@ -123,12 +123,8 @@ pub enum Config {
/// because we do not want to send a second warning)
NotifyAboutWrongPw,
/// address to webrtc signaling server (https://github.com/cracker0dks/basicwebrtc)
/// that should be used for opening video hangouts.
/// This property is only used in the UIs not by the core itself.
/// Format: https://example.com/subdir
/// The other properties that are needed for a call such as the roomname will be set by the client in the anchor part of the url.
BasicWebRTCInstance,
/// address to webrtc instance to use for videochats
WebrtcInstance,
}
impl Context {

View File

@@ -84,6 +84,19 @@ impl Default for KeyGenType {
}
}
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
#[repr(i8)]
pub enum VideochatType {
Unknown = 0,
BasicWebrtc = 1,
}
impl Default for VideochatType {
fn default() -> Self {
VideochatType::Unknown
}
}
pub const DC_HANDSHAKE_CONTINUE_NORMAL_PROCESSING: i32 = 0x01;
pub const DC_HANDSHAKE_STOP_NORMAL_PROCESSING: i32 = 0x02;
pub const DC_HANDSHAKE_ADD_DELETE_JOB: i32 = 0x04;
@@ -296,6 +309,9 @@ pub enum Viewtype {
/// The file is set via dc_msg_set_file()
/// and retrieved via dc_msg_get_file().
File = 60,
/// Message is an invitation to a videochat.
VideochatInvitation = 70,
}
impl Default for Viewtype {

View File

@@ -35,6 +35,7 @@ pub enum HeaderDef {
ChatContent,
ChatDuration,
ChatDispositionNotificationTo,
ChatWebrtcRoom,
Autocrypt,
AutocryptSetupMessage,
SecureJoin,

View File

@@ -638,6 +638,39 @@ impl Message {
None
}
/// split a webrtc_instance as defined by the corresponding config-value into a type and a url
pub fn parse_webrtc_instance(instance: &str) -> (VideochatType, String) {
let mut split = instance.splitn(2, ':');
let type_str = split.next().unwrap_or_default().to_lowercase();
let url = split.next();
if type_str == "basicwebrtc" {
(
VideochatType::BasicWebrtc,
url.unwrap_or_default().to_string(),
)
} else {
(VideochatType::Unknown, instance.to_string())
}
}
pub fn get_videochat_url(&self) -> Option<String> {
if self.viewtype == Viewtype::VideochatInvitation {
if let Some(instance) = self.param.get(Param::WebrtcRoom) {
return Some(Message::parse_webrtc_instance(instance).1);
}
}
None
}
pub fn get_videochat_type(&self) -> Option<VideochatType> {
if self.viewtype == Viewtype::VideochatInvitation {
if let Some(instance) = self.param.get(Param::WebrtcRoom) {
return Some(Message::parse_webrtc_instance(instance).0);
}
}
None
}
pub fn set_text(&mut self, text: Option<String>) {
self.text = text;
}
@@ -1241,6 +1274,13 @@ pub async fn get_summarytext_by_raw(
format!("{} {}", label, file_name)
}
}
Viewtype::VideochatInvitation => {
append_text = false;
context
.stock_str(StockMessage::VideochatInvitation)
.await
.into_owned()
}
_ => {
if param.get_cmd() != SystemMessage::LocationOnly {
"".to_string()
@@ -1799,4 +1839,19 @@ mod tests {
"Autocrypt Setup Message" // file name is not added for autocrypt setup messages
);
}
#[async_std::test]
async fn test_parse_webrtc_instance() {
let (webrtc_type, url) = Message::parse_webrtc_instance("basicwebrtc:https://foo/bar");
assert_eq!(webrtc_type, VideochatType::BasicWebrtc);
assert_eq!(url, "https://foo/bar");
let (webrtc_type, url) = Message::parse_webrtc_instance("bAsIcwEbrTc:url");
assert_eq!(webrtc_type, VideochatType::BasicWebrtc);
assert_eq!(url, "url");
let (webrtc_type, url) = Message::parse_webrtc_instance("https://foo/bar?key=val#key=val");
assert_eq!(webrtc_type, VideochatType::Unknown);
assert_eq!(url, "https://foo/bar?key=val#key=val");
}
}

View File

@@ -875,6 +875,19 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
if self.msg.viewtype == Viewtype::Sticker {
protected_headers.push(Header::new("Chat-Content".into(), "sticker".into()));
} else if self.msg.viewtype == Viewtype::VideochatInvitation {
protected_headers.push(Header::new(
"Chat-Content".into(),
"videochat-invitation".into(),
));
protected_headers.push(Header::new(
"Chat-Webrtc-Room".into(),
self.msg
.param
.get(Param::WebrtcRoom)
.unwrap_or_default()
.into(),
));
}
if self.msg.viewtype == Viewtype::Voice

View File

@@ -242,6 +242,19 @@ impl MimeMessage {
}
}
fn parse_videochat_headers(&mut self) {
if let Some(value) = self.get(HeaderDef::ChatContent).cloned() {
if value == "videochat-invitation" {
let instance = self.get(HeaderDef::ChatWebrtcRoom).cloned();
if let Some(part) = self.parts.first_mut() {
part.typ = Viewtype::VideochatInvitation;
part.param
.set(Param::WebrtcRoom, instance.unwrap_or_default());
}
}
}
}
/// Squashes mutlipart chat messages with attachment into single-part messages.
///
/// Delta Chat sends attachments, such as images, in two-part messages, with the first message
@@ -314,6 +327,7 @@ impl MimeMessage {
fn parse_headers(&mut self, context: &Context) -> Result<()> {
self.parse_system_message_headers(context)?;
self.parse_avatar_headers();
self.parse_videochat_headers();
self.squash_attachment_parts();
if let Some(ref subject) = self.get_subject() {
@@ -1449,6 +1463,28 @@ mod tests {
assert!(mimeparser.group_avatar.unwrap().is_change());
}
#[async_std::test]
async fn test_mimeparser_with_videochat() {
let t = TestContext::new().await;
let raw = include_bytes!("../test-data/message/videochat_invitation.eml");
let mimeparser = MimeMessage::from_bytes(&t.ctx, &raw[..]).await.unwrap();
assert_eq!(mimeparser.parts.len(), 1);
assert_eq!(mimeparser.parts[0].typ, Viewtype::VideochatInvitation);
assert_eq!(
mimeparser.parts[0]
.param
.get(Param::WebrtcRoom)
.unwrap_or_default(),
"https://example.org/p2p/?roomname=6HiduoAn4xN"
);
assert!(mimeparser.parts[0]
.msg
.contains("https://example.org/p2p/?roomname=6HiduoAn4xN"));
assert_eq!(mimeparser.user_avatar, None);
assert_eq!(mimeparser.group_avatar, None);
}
#[async_std::test]
async fn test_mimeparser_message_kml() {
let context = TestContext::new().await;

View File

@@ -68,6 +68,9 @@ pub enum Param {
/// For Messages
AttachGroupImage = b'A',
/// For Messages
WebrtcRoom = b'V',
/// For Messages: space-separated list of messaged IDs of forwarded copies.
///
/// This is used when a [crate::message::Message] is in the

View File

@@ -210,6 +210,12 @@ pub enum StockMessage {
#[strum(props(fallback = "Message deletion timer is set to 4 weeks."))]
MsgEphemeralTimerFourWeeks = 81,
#[strum(props(fallback = "Video chat invitation"))]
VideochatInvitation = 82,
#[strum(props(fallback = "You are invited to a video chat, click %1$s to join."))]
VideochatInviteMsgBody = 83,
}
/*