Skip to content

Commit 39a9aaa

Browse files
committed
Simplify support chat backend to single-conversation model
Drop conversation_id, SupportConversation, and the SupportStreamEvent wrapper; stream a bare StreamEvent::Support(SupportMessage) instead of nested conversation events.
1 parent d164b20 commit 39a9aaa

11 files changed

Lines changed: 17 additions & 257 deletions

File tree

core/apps/api/src/main.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,6 @@ fn mount_routes(rocket: Rocket<Build>, admin_enabled: bool) -> Rocket<Build> {
133133
devices::create_device_referral_v2,
134134
devices::use_device_referral_code_v2,
135135
devices::redeem_device_rewards_v2,
136-
support::get_support_conversation,
137136
support::get_support_messages,
138137
support::post_support_action,
139138
support::post_support_image,

core/apps/api/src/support/client.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use primitives::{SupportAction, SupportConversation, SupportMessage, SupportMessageInput};
1+
use primitives::{SupportAction, SupportMessage, SupportMessageInput};
22
use std::{error::Error, future::Future};
33
use storage::{Database, NewSupportSessionRow, SupportSessionsRepository, models::DeviceRow};
44

@@ -17,10 +17,6 @@ impl SupportApiClient {
1717
}
1818
}
1919

20-
pub async fn conversation(&self, device: &DeviceRow) -> Result<Option<SupportConversation>, Box<dyn Error + Send + Sync>> {
21-
self.with_session(device, |session| async move { self.chatwoot.conversation(&session).await }).await
22-
}
23-
2420
pub async fn messages(&self, device: &DeviceRow, from_timestamp: Option<u64>) -> Result<Vec<SupportMessage>, Box<dyn Error + Send + Sync>> {
2521
self.with_session(device, |session| async move { self.chatwoot.messages(&session, from_timestamp).await })
2622
.await

core/apps/api/src/support/mod.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
mod client;
22

33
pub use client::SupportApiClient;
4-
use primitives::{SupportAction, SupportConversation, SupportMessage, SupportMessageInput};
4+
use primitives::{SupportAction, SupportMessage, SupportMessageInput};
55
use rocket::{Data, State, data::ToByteUnit, get, http::ContentType, post, serde::json::Json, tokio::sync::Mutex};
66

77
use crate::{
@@ -11,11 +11,6 @@ use crate::{
1111

1212
const MAX_SUPPORT_IMAGE_BYTES: u64 = 10 * 1024 * 1024;
1313

14-
#[get("/devices/support")]
15-
pub async fn get_support_conversation(device: AuthenticatedDevice, client: &State<Mutex<SupportApiClient>>) -> Result<ApiResponse<Option<SupportConversation>>, ApiError> {
16-
Ok(client.lock().await.conversation(&device.device_row).await?.into())
17-
}
18-
1914
#[get("/devices/support/messages?<from_timestamp>")]
2015
pub async fn get_support_messages(
2116
device: AuthenticatedDevice,

core/crates/primitives/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,7 @@ pub mod stream;
226226
pub use self::stream::{StreamBalanceUpdate, StreamEvent, StreamMessage, StreamMessagePrices, StreamTransactionsUpdate, StreamWalletUpdate, device_stream_channel};
227227
pub mod support;
228228
pub use self::support::{
229-
SupportAction, SupportAgent, SupportConversation, SupportConversationStatus, SupportMessage, SupportMessageDeliveryStatus, SupportMessageImage, SupportMessageInput,
230-
SupportMessageSender, SupportStreamEvent, SupportTypingStatus,
229+
SupportAction, SupportAgent, SupportMessage, SupportMessageDeliveryStatus, SupportMessageImage, SupportMessageInput, SupportMessageSender, SupportTypingStatus,
231230
};
232231
pub mod asset_balance;
233232
pub use self::asset_balance::{AddressBalances, AssetBalance, Balance};

core/crates/primitives/src/stream.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use serde::{Deserialize, Serialize};
22
use typeshare::typeshare;
33

4-
use crate::{AssetId, InAppNotification, SupportStreamEvent, TransactionId, WalletId, WebSocketPricePayload};
4+
use crate::{AssetId, InAppNotification, SupportMessage, TransactionId, WalletId, WebSocketPricePayload};
55

66
pub const DEVICE_STREAM_CHANNEL_PREFIX: &str = "stream:device:";
77

@@ -22,7 +22,7 @@ pub enum StreamEvent {
2222
Perpetual(StreamWalletUpdate),
2323
InAppNotification(StreamNotificationUpdate),
2424
FiatTransaction(StreamWalletUpdate),
25-
Support(SupportStreamEvent),
25+
Support(SupportMessage),
2626
}
2727

2828
#[derive(Debug, Clone, Serialize, Deserialize)]

core/crates/primitives/src/support.rs

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,6 @@ use chrono::{DateTime, Utc};
33
use serde::{Deserialize, Serialize};
44
use typeshare::typeshare;
55

6-
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7-
#[typeshare(swift = "Equatable, CaseIterable, Sendable")]
8-
#[serde(rename_all = "lowercase")]
9-
pub enum SupportConversationStatus {
10-
Open,
11-
Resolved,
12-
}
13-
146
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
157
#[typeshare(swift = "Equatable, CaseIterable, Sendable")]
168
#[serde(rename_all = "lowercase")]
@@ -53,20 +45,6 @@ impl SupportMessageSender {
5345
}
5446
}
5547

56-
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
57-
#[typeshare(swift = "Sendable, Equatable, Hashable, Identifiable")]
58-
#[serde(rename_all = "camelCase")]
59-
pub struct SupportConversation {
60-
pub id: String,
61-
pub status: SupportConversationStatus,
62-
#[serde(skip_serializing_if = "Option::is_none")]
63-
pub first_message: Option<String>,
64-
#[serde(skip_serializing_if = "Option::is_none")]
65-
pub last_message: Option<String>,
66-
pub last_activity_at: DateTime<Utc>,
67-
pub unread_count: i32,
68-
}
69-
7048
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7149
#[typeshare(swift = "Sendable, Equatable")]
7250
#[serde(rename_all = "camelCase")]
@@ -90,7 +68,6 @@ pub struct SupportMessageImage {
9068
#[serde(rename_all = "camelCase")]
9169
pub struct SupportMessage {
9270
pub id: String,
93-
pub conversation_id: String,
9471
pub content: String,
9572
pub sender: SupportMessageSender,
9673
pub delivery_status: SupportMessageDeliveryStatus,
@@ -121,10 +98,3 @@ pub enum SupportAction {
12198
LastSeen,
12299
}
123100

124-
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
125-
#[serde(tag = "type", content = "data", rename_all = "camelCase")]
126-
#[typeshare(swift = "Sendable")]
127-
pub enum SupportStreamEvent {
128-
Message(SupportMessage),
129-
Conversation(SupportConversation),
130-
}

core/crates/support/src/chatwoot.rs

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use chrono::Utc;
2-
use primitives::{Device, SupportConversation, SupportMessage, SupportTypingStatus};
2+
use primitives::{Device, SupportMessage, SupportTypingStatus};
33
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
44
use reqwest::multipart::{Form, Part};
55
use reqwest::{Client, RequestBuilder, Response};
@@ -8,9 +8,8 @@ use std::error::Error;
88
use std::io;
99

1010
use crate::{
11-
ChatwootConfigResponse, ChatwootContactResponse, ChatwootContactUpdate, ChatwootConversationResponse, ChatwootMessageInput, ChatwootMessagesResponse, ChatwootSession,
12-
ChatwootTypingInput, Message,
13-
constants::{PATH_CONFIG, PATH_CONTACT_SET_USER, PATH_CONVERSATIONS, PATH_MESSAGES, PATH_TOGGLE_TYPING, PATH_UPDATE_LAST_SEEN, QUERY_WIDGET_PUBLIC_TOKEN},
11+
ChatwootConfigResponse, ChatwootContactResponse, ChatwootContactUpdate, ChatwootMessageInput, ChatwootMessagesResponse, ChatwootSession, ChatwootTypingInput, Message,
12+
constants::{PATH_CONFIG, PATH_CONTACT_SET_USER, PATH_MESSAGES, PATH_TOGGLE_TYPING, PATH_UPDATE_LAST_SEEN, QUERY_WIDGET_PUBLIC_TOKEN},
1413
support_messages,
1514
};
1615

@@ -50,26 +49,6 @@ impl ChatwootClient {
5049
})
5150
}
5251

53-
pub async fn conversation(&self, session: &ChatwootSession) -> Result<Option<SupportConversation>, Box<dyn Error + Send + Sync>> {
54-
let conversation: ChatwootConversationResponse = self
55-
.json(
56-
self.authenticated(self.client.get(self.widget_url(PATH_CONVERSATIONS)), &session.auth_token)?
57-
.send()
58-
.await?,
59-
)
60-
.await?;
61-
62-
let Some(id) = conversation.id else {
63-
return Ok(None);
64-
};
65-
66-
let messages = self.messages(session, None).await?;
67-
let conversation = conversation
68-
.support_conversation(&messages)
69-
.ok_or_else(|| io::Error::other(format!("conversation {id} has no activity timestamp")))?;
70-
Ok(Some(conversation))
71-
}
72-
7352
pub async fn messages(&self, session: &ChatwootSession, from_timestamp: Option<u64>) -> Result<Vec<SupportMessage>, Box<dyn Error + Send + Sync>> {
7453
let response: ChatwootMessagesResponse = self
7554
.json(self.authenticated(self.client.get(self.widget_url(PATH_MESSAGES)), &session.auth_token)?.send().await?)
@@ -200,7 +179,6 @@ mod tests {
200179
fn message(id: &str, timestamp: i64) -> SupportMessage {
201180
SupportMessage {
202181
id: id.to_string(),
203-
conversation_id: "conversation".to_string(),
204182
content: id.to_string(),
205183
sender: SupportMessageSender::User,
206184
delivery_status: SupportMessageDeliveryStatus::Sent,

core/crates/support/src/client.rs

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
1-
use crate::{
2-
ChatwootWebhookPayload,
3-
constants::{EVENT_CONVERSATION_STATUS_CHANGED, EVENT_CONVERSATION_UPDATED, EVENT_MESSAGE_CREATED},
4-
markdown_plain_text,
5-
};
1+
use crate::{ChatwootWebhookPayload, constants::EVENT_MESSAGE_CREATED, markdown_plain_text};
62
use cacher::CacherClient;
73
use localizer::LanguageLocalizer;
84
use primitives::{
9-
Device, GorushNotification, PushNotification, PushNotificationTypes, StreamEvent, SupportMessage, SupportStreamEvent, device_stream_channel,
10-
push_notification::PushNotificationSupport,
5+
Device, GorushNotification, PushNotification, PushNotificationTypes, StreamEvent, SupportMessage, device_stream_channel, push_notification::PushNotificationSupport,
116
};
127
use std::error::Error;
138
use storage::database::devices::DevicesStore;
@@ -34,14 +29,10 @@ impl SupportClient {
3429
}
3530

3631
pub async fn process_webhook(&self, device: &Device, payload: &ChatwootWebhookPayload) -> Result<(usize, usize), Box<dyn Error + Send + Sync>> {
37-
match payload.event.as_str() {
38-
EVENT_MESSAGE_CREATED => self.process_message_created(device, payload).await,
39-
EVENT_CONVERSATION_UPDATED | EVENT_CONVERSATION_STATUS_CHANGED => self.process_conversation_updated(device, payload).await,
40-
_ => Ok((0, 0)),
32+
if payload.event.as_str() != EVENT_MESSAGE_CREATED {
33+
return Ok((0, 0));
4134
}
42-
}
4335

44-
async fn process_message_created(&self, device: &Device, payload: &ChatwootWebhookPayload) -> Result<(usize, usize), Box<dyn Error + Send + Sync>> {
4536
let notifications_count = if let Some(notification) = Self::build_notification(device, payload) {
4637
self.stream_producer.publish_notifications_support(NotificationsPayload::new(vec![notification])).await?;
4738
1
@@ -54,15 +45,6 @@ impl SupportClient {
5445
Ok((notifications_count, stream_events_count))
5546
}
5647

57-
async fn process_conversation_updated(&self, device: &Device, payload: &ChatwootWebhookPayload) -> Result<(usize, usize), Box<dyn Error + Send + Sync>> {
58-
if let Some(conversation) = payload.support_conversation() {
59-
self.publish_stream_event(device, SupportStreamEvent::Conversation(conversation)).await?;
60-
Ok((0, 1))
61-
} else {
62-
Ok((0, 0))
63-
}
64-
}
65-
6648
fn build_notification(device: &Device, payload: &ChatwootWebhookPayload) -> Option<GorushNotification> {
6749
if !payload.is_public_outgoing_message() {
6850
return None;
@@ -86,18 +68,14 @@ impl SupportClient {
8668
return Ok(0);
8769
}
8870

89-
self.publish_stream_event(device, SupportStreamEvent::Message(message)).await?;
71+
let channel = device_stream_channel(&device.id);
72+
self.cacher.publish(&channel, &StreamEvent::Support(message)).await?;
9073
Ok(1)
9174
}
9275

9376
fn should_publish_stream_message(message: &SupportMessage) -> bool {
9477
message.sender.is_agent()
9578
}
96-
97-
async fn publish_stream_event(&self, device: &Device, event: SupportStreamEvent) -> Result<(), Box<dyn Error + Send + Sync>> {
98-
let channel = device_stream_channel(&device.id);
99-
self.cacher.publish(&channel, &StreamEvent::Support(event)).await
100-
}
10179
}
10280

10381
#[cfg(test)]

core/crates/support/src/constants.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,11 @@ pub(crate) const CHATWOOT_DELIVERY_STATUS_DELIVERED: &str = "delivered";
33
pub(crate) const CHATWOOT_DELIVERY_STATUS_READ: &str = "read";
44
pub(crate) const CHATWOOT_DELIVERY_STATUS_SENT: &str = "sent";
55
pub(crate) const CHATWOOT_FILE_TYPE_IMAGE: &str = "image";
6-
pub(crate) const CHATWOOT_STATUS_RESOLVED: &str = "resolved";
76

8-
pub(crate) const EVENT_CONVERSATION_STATUS_CHANGED: &str = "conversation_status_changed";
9-
pub(crate) const EVENT_CONVERSATION_UPDATED: &str = "conversation_updated";
107
pub(crate) const EVENT_MESSAGE_CREATED: &str = "message_created";
118

129
pub(crate) const PATH_CONFIG: &str = "config";
1310
pub(crate) const PATH_CONTACT_SET_USER: &str = "contact/set_user";
14-
pub(crate) const PATH_CONVERSATIONS: &str = "conversations";
1511
pub(crate) const PATH_MESSAGES: &str = "messages";
1612
pub(crate) const PATH_TOGGLE_TYPING: &str = "conversations/toggle_typing";
1713
pub(crate) const PATH_UPDATE_LAST_SEEN: &str = "conversations/update_last_seen";

0 commit comments

Comments
 (0)