Skip to content

Commit

Permalink
feat: handle incoming edit messages (#263)
Browse files Browse the repository at this point in the history
  • Loading branch information
boxdot authored Jan 12, 2024
1 parent 220a40d commit fbe6148
Show file tree
Hide file tree
Showing 21 changed files with 385 additions and 69 deletions.

This file was deleted.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "gurk"
description = "Signal messenger client for terminal"
version = "0.4.0"
version = "0.4.2"
authors = ["boxdot <d@zerovolt.org>"]
edition = "2021"
keywords = ["signal", "tui"]
Expand Down
9 changes: 9 additions & 0 deletions migrations/002_edits.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
DROP INDEX idx_messages_edit;

DROP INDEX idx_messages_quote;

ALTER TABLE messages
DROP COLUMN edited;

ALTER TABLE messages
DROP COLUMN edit;
15 changes: 15 additions & 0 deletions migrations/002_edits.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- edit points to the original edited message
--
-- for a chain of edits original, edit1, edit2, ... each edit points to the arrived_at field of
-- the message original
ALTER TABLE messages
ADD COLUMN edit INTEGER;

ALTER TABLE messages
ADD COLUMN edited BOOLEAN NOT NULL DEFAULT FALSE;

CREATE INDEX idx_messages_quote
ON messages (quote);

CREATE INDEX idx_messages_edit
ON messages (edit);
13 changes: 9 additions & 4 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use presage::proto::{
use presage::proto::{AttachmentPointer, DataMessage, ReceiptMessage, SyncMessage, TypingMessage};
use regex_automata::Regex;
use tokio::sync::mpsc;
use tracing::{error, info, trace, warn};
use tracing::{error, info, warn};
use uuid::Uuid;

use std::borrow::Cow;
Expand Down Expand Up @@ -694,6 +694,9 @@ impl App {
});
return Ok(());
}
(metadata, ContentBody::SynchronizeMessage(sync_message)) => {
return self.handle_sync_message(metadata, sync_message);
}
(
Metadata {
sender:
Expand Down Expand Up @@ -796,7 +799,7 @@ impl App {
}

unhandled => {
trace!(?unhandled, "skipping unhandled message");
info!(?unhandled, "skipping unhandled message");
return Ok(());
}
};
Expand Down Expand Up @@ -1155,7 +1158,7 @@ impl App {
}
}

async fn ensure_contact_channel_exists(&mut self, uuid: Uuid, name: &str) -> usize {
pub(crate) async fn ensure_contact_channel_exists(&mut self, uuid: Uuid, name: &str) -> usize {
if let Some(channel_idx) = self
.channels
.items
Expand Down Expand Up @@ -1205,7 +1208,7 @@ impl App {
self.touch_channel(channel_idx);
}

fn touch_channel(&mut self, channel_idx: usize) {
pub(crate) fn touch_channel(&mut self, channel_idx: usize) {
if self.channels.state.selected() != Some(channel_idx) {
let channel_id = self.channels.items[channel_idx];
let mut channel = self
Expand Down Expand Up @@ -1520,6 +1523,8 @@ mod tests {
receipt: Default::default(),
body_ranges: Default::default(),
send_failed: Default::default(),
edit: Default::default(),
edited: Default::default(),
},
);

Expand Down
35 changes: 35 additions & 0 deletions src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,37 @@ pub struct Message {
pub(crate) body_ranges: Vec<BodyRange>,
#[serde(skip)]
pub(crate) send_failed: Option<String>,
/// Arrived at of the originally edited message
///
/// When several edits are done, this is the arrived_at of the very first original message.
#[serde(default)]
pub(crate) edit: Option<u64>,
/// Whether the message was edited
#[serde(default)]
pub(crate) edited: bool,
}

impl Message {
pub(crate) fn text(from_id: Uuid, arrived_at: u64, message: String) -> Self {
Self {
from_id,
message: Some(message),
arrived_at,
quote: Default::default(),
attachments: Default::default(),
reactions: Default::default(),
receipt: Default::default(),
body_ranges: Default::default(),
send_failed: Default::default(),
edit: Default::default(),
edited: Default::default(),
}
}

/// Returns whether this message is an edit of an another message
pub(crate) fn is_edit(&self) -> bool {
self.edit.is_some()
}
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand Down Expand Up @@ -269,6 +300,8 @@ impl Message {
receipt: Receipt::Sent,
body_ranges: body_ranges.into_iter().collect(),
send_failed: Default::default(),
edit: Default::default(),
edited: Default::default(),
}
}

Expand All @@ -287,6 +320,8 @@ impl Message {
.filter_map(BodyRange::from_proto)
.collect(),
send_failed: Default::default(),
edit: Default::default(),
edited: Default::default(),
})
}

Expand Down
129 changes: 129 additions & 0 deletions src/handlers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use anyhow::Context;
use presage::libsignal_service::content::Metadata;
use presage::proto::sync_message::Sent;
use presage::proto::{DataMessage, EditMessage, SyncMessage};
use tracing::warn;
use uuid::Uuid;

use crate::app::App;
use crate::data::{ChannelId, Message};
use crate::storage::MessageId;

impl App {
pub(super) fn handle_sync_message(
&mut self,
metadata: Metadata,
sync_message: SyncMessage,
) -> anyhow::Result<()> {
let Some(channel_id) = sync_message.channel_id() else {
warn!("dropping a sync message not attached to a channel");
return Ok(());
};

// edit message
if let Some(Sent {
edit_message:
Some(EditMessage {
target_sent_timestamp: Some(target_sent_timestamp),
data_message:
Some(DataMessage {
body: Some(body),
timestamp: Some(arrived_at),
..
}),
}),
..
}) = sync_message.sent
{
let from_id = metadata.sender.uuid;
// Note: target_sent_timestamp points to the previous edit or the original message
let edited = self
.storage
.message(MessageId::new(channel_id, target_sent_timestamp))
.context("no message to edit")?;

// get original message
let mut original = if let Some(arrived_at) = edited.edit {
// previous edit => get original message
self.storage
.message(MessageId::new(channel_id, arrived_at))
.context("no original edited message")?
.into_owned()
} else {
// original message => first edit
let original = edited.into_owned();

// preserve body of the original message; it is replaced below
let mut preserved = original.clone();
preserved.arrived_at = original.arrived_at + 1;
preserved.edit = Some(original.arrived_at);
self.storage.store_message(channel_id, preserved);

original
};

// store the incoming edit
self.storage.store_message(
channel_id,
Message {
edit: Some(original.arrived_at),
..Message::text(from_id, arrived_at, body.clone())
},
);

// override the body of the original message
original.message.replace(body);
original.edited = true;
self.storage.store_message(channel_id, original);

let channel_idx = self
.channels
.items
.iter()
.position(|id| id == &channel_id)
.context("editing message in non-existent channel")?;
self.touch_channel(channel_idx);
}

Ok(())
}
}

trait MessageExt {
fn sender_id(&self) -> Option<Uuid>;

/// Get a channel id a message
fn channel_id(&self) -> Option<ChannelId>;
}

impl MessageExt for SyncMessage {
fn sender_id(&self) -> Option<Uuid> {
todo!()
}

fn channel_id(&self) -> Option<ChannelId> {
// only sent sync message are attached to a conversation
let sent = self.sent.as_ref()?;
if let Some(uuid) = sent
.destination_service_id
.as_ref()
.and_then(|id| id.parse().ok())
{
Some(ChannelId::User(uuid))
} else {
let group_v2 = sent
.message
.as_ref()
.and_then(|message| message.group_v2.as_ref())
.or_else(|| {
sent.edit_message
.as_ref()?
.data_message
.as_ref()?
.group_v2
.as_ref()
})?;
ChannelId::from_master_key_bytes(group_v2.master_key.as_deref()?).ok()
}
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod data;
pub mod dev;
pub(crate) mod emoji;
pub mod event;
mod handlers;
pub mod input;
pub mod receipt;
pub mod shortcuts;
Expand Down
Loading

0 comments on commit fbe6148

Please sign in to comment.