return created message in message fetch API

This commit is contained in:
Abhinav Raut
2025-07-06 19:51:44 +05:30
parent 4203b82e90
commit ccc5940dd9
5 changed files with 67 additions and 37 deletions

View File

@@ -744,7 +744,7 @@ func handleCreateConversation(r *fastglue.Request) error {
}
// Send reply to the created conversation.
if err := app.conversation.SendReply(media, req.InboxID, auser.ID /**sender_id**/, conversationUUID, req.Content, to, nil /**cc**/, nil /**bcc**/, map[string]any{} /**meta**/); err != nil {
if _, err := app.conversation.SendReply(media, req.InboxID, auser.ID /**sender_id**/, conversationUUID, req.Content, to, nil /**cc**/, nil /**bcc**/, map[string]any{} /**meta**/); err != nil {
// Delete the conversation if reply fails.
if err := app.conversation.DeleteConversation(conversationUUID); err != nil {
app.lo.Error("error deleting conversation", "error", err)

View File

@@ -162,13 +162,15 @@ func handleSendMessage(r *fastglue.Request) error {
}
if req.Private {
if err := app.conversation.SendPrivateNote(media, user.ID, cuuid, req.Message); err != nil {
return sendErrorEnvelope(r, err)
}
} else {
if err := app.conversation.SendReply(media, conv.InboxID, user.ID, cuuid, req.Message, req.To, req.CC, req.BCC, map[string]any{} /**meta**/); err != nil {
message, err := app.conversation.SendPrivateNote(media, user.ID, cuuid, req.Message)
if err != nil {
return sendErrorEnvelope(r, err)
}
return r.SendEnvelope(message)
}
return r.SendEnvelope(true)
message, err := app.conversation.SendReply(media, conv.InboxID, user.ID, cuuid, req.Message, req.To, req.CC, req.BCC, map[string]any{} /**meta**/)
if err != nil {
return sendErrorEnvelope(r, err)
}
return r.SendEnvelope(message)
}

View File

@@ -920,14 +920,17 @@ func (m *Manager) ApplyAction(action amodels.RuleAction, conv models.Conversatio
}
return m.UpdateConversationStatus(conv.UUID, statusID, "", "", user)
case amodels.ActionSendPrivateNote:
return m.SendPrivateNote([]mmodels.Media{}, user.ID, conv.UUID, action.Value[0])
_, err := m.SendPrivateNote([]mmodels.Media{}, user.ID, conv.UUID, action.Value[0])
if err != nil {
return fmt.Errorf("sending private note: %w", err)
}
case amodels.ActionReply:
// Make recipient list.
to, cc, bcc, err := m.makeRecipients(conv.ID, conv.Contact.Email.String, conv.InboxMail)
if err != nil {
return fmt.Errorf("making recipients for reply action: %w", err)
}
return m.SendReply(
_, err = m.SendReply(
[]mmodels.Media{},
conv.InboxID,
user.ID,
@@ -938,6 +941,9 @@ func (m *Manager) ApplyAction(action amodels.RuleAction, conv models.Conversatio
bcc,
map[string]any{}, /**meta**/
)
if err != nil {
return fmt.Errorf("sending reply: %w", err)
}
case amodels.ActionSetSLA:
slaID, err := strconv.Atoi(action.Value[0])
if err != nil {
@@ -951,6 +957,7 @@ func (m *Manager) ApplyAction(action amodels.RuleAction, conv models.Conversatio
default:
return fmt.Errorf("unknown action: %s", action.Type)
}
return nil
}
// RemoveConversationAssignee removes the assignee from the conversation.
@@ -991,10 +998,16 @@ func (m *Manager) SendCSATReply(actorUserID int, conversation models.Conversatio
// Make recipient list.
to, cc, bcc, err := m.makeRecipients(conversation.ID, conversation.Contact.Email.String, conversation.InboxMail)
if err != nil {
return fmt.Errorf("making recipients for CSAT reply: %w", err)
return envelope.NewError(envelope.GeneralError, m.i18n.Ts("globals.messages.errorCreating", "name", "{globals.terms.csat}"), nil)
}
return m.SendReply(nil /**media**/, conversation.InboxID, actorUserID, conversation.UUID, message, to, cc, bcc, meta)
// Send CSAT reply.
_, err = m.SendReply(nil /**media**/, conversation.InboxID, actorUserID, conversation.UUID, message, to, cc, bcc, meta)
if err != nil {
m.lo.Error("error sending CSAT reply", "conversation_uuid", conversation.UUID, "error", err)
return envelope.NewError(envelope.GeneralError, m.i18n.Ts("globals.messages.errorCreating", "name", "{globals.terms.csat}"), nil)
}
return nil
}
// DeleteConversation deletes a conversation.

View File

@@ -356,8 +356,7 @@ func (m *Manager) MarkMessageAsPending(uuid string) error {
}
// SendPrivateNote inserts a private message in a conversation.
func (m *Manager) SendPrivateNote(media []mmodels.Media, senderID int, conversationUUID, content string) error {
// Insert Message.
func (m *Manager) SendPrivateNote(media []mmodels.Media, senderID int, conversationUUID, content string) (models.Message, error) {
message := models.Message{
ConversationUUID: conversationUUID,
SenderID: senderID,
@@ -369,18 +368,25 @@ func (m *Manager) SendPrivateNote(media []mmodels.Media, senderID int, conversat
Private: true,
Media: media,
}
return m.InsertMessage(&message)
if err := m.InsertMessage(&message); err != nil {
return models.Message{}, err
}
return message, nil
}
// SendReply inserts a reply message in a conversation.
func (m *Manager) SendReply(media []mmodels.Media, inboxID, senderID int, conversationUUID, content string, to, cc, bcc []string, meta map[string]interface{}) error {
// Save to, cc and bcc in meta.
func (m *Manager) SendReply(media []mmodels.Media, inboxID, senderID int, conversationUUID, content string, to, cc, bcc []string, meta map[string]interface{}) (models.Message, error) {
var (
message = models.Message{}
)
// Clear empty fields in to, cc, bcc.
to = stringutil.RemoveEmpty(to)
cc = stringutil.RemoveEmpty(cc)
bcc = stringutil.RemoveEmpty(bcc)
if len(to) == 0 {
return envelope.NewError(envelope.GeneralError, m.i18n.Ts("globals.messages.empty", "name", "`to`"), nil)
return message, envelope.NewError(envelope.GeneralError, m.i18n.Ts("globals.messages.empty", "name", "`to`"), nil)
}
meta["to"] = to
@@ -393,22 +399,22 @@ func (m *Manager) SendReply(media []mmodels.Media, inboxID, senderID int, conver
metaJSON, err := json.Marshal(meta)
if err != nil {
return envelope.NewError(envelope.GeneralError, m.i18n.Ts("globals.messages.errorMarshalling", "name", "{globals.terms.meta}"), nil)
return message, envelope.NewError(envelope.GeneralError, m.i18n.Ts("globals.messages.errorMarshalling", "name", "{globals.terms.meta}"), nil)
}
// Generage unique source ID i.e. message-id for email.
inbox, err := m.inboxStore.GetDBRecord(inboxID)
if err != nil {
return err
return message, err
}
sourceID, err := stringutil.GenerateEmailMessageID(conversationUUID, inbox.From)
if err != nil {
m.lo.Error("error generating source message id", "error", err)
return envelope.NewError(envelope.GeneralError, m.i18n.T("conversation.errorGeneratingMessageID"), nil)
return message, envelope.NewError(envelope.GeneralError, m.i18n.T("conversation.errorGeneratingMessageID"), nil)
}
// Insert Message.
message := models.Message{
message = models.Message{
ConversationUUID: conversationUUID,
SenderID: senderID,
Type: models.MessageOutgoing,
@@ -421,16 +427,17 @@ func (m *Manager) SendReply(media []mmodels.Media, inboxID, senderID int, conver
Meta: metaJSON,
SourceID: null.StringFrom(sourceID),
}
return m.InsertMessage(&message)
if err := m.InsertMessage(&message); err != nil {
return models.Message{}, err
}
return message, nil
}
// InsertMessage inserts a message and attaches the media to the message.
func (m *Manager) InsertMessage(message *models.Message) error {
// Private message is always sent.
if message.Private {
message.Status = models.MessageStatusSent
}
if len(message.Meta) == 0 || string(message.Meta) == "null" {
message.Meta = json.RawMessage(`{}`)
}
@@ -438,14 +445,16 @@ func (m *Manager) InsertMessage(message *models.Message) error {
// Convert HTML content to text for search.
message.TextContent = stringutil.HTML2Text(message.Content)
// Insert Message.
if err := m.q.InsertMessage.QueryRow(message.Type, message.Status, message.ConversationID, message.ConversationUUID, message.Content, message.TextContent, message.SenderID, message.SenderType,
message.Private, message.ContentType, message.SourceID, message.Meta).Scan(&message.ID, &message.UUID, &message.CreatedAt); err != nil {
// Insert and scan the message into the struct.
if err := m.q.InsertMessage.Get(message,
message.Type, message.Status, message.ConversationID, message.ConversationUUID,
message.Content, message.TextContent, message.SenderID, message.SenderType,
message.Private, message.ContentType, message.SourceID, message.Meta); err != nil {
m.lo.Error("error inserting message in db", "error", err)
return envelope.NewError(envelope.GeneralError, m.i18n.Ts("globals.messages.errorInserting", "name", "{globals.terms.message}"), nil)
}
// Attach message to the media.
// Attach just inserted message to the media.
for _, media := range message.Media {
m.mediaStore.Attach(media.ID, mmodels.ModelMessages, message.ID)
}
@@ -465,14 +474,20 @@ func (m *Manager) InsertMessage(message *models.Message) error {
// Broadcast new message.
m.BroadcastNewMessage(message)
// Refetch message and send webhook event for message created.
updatedMessage, err := m.GetMessage(message.UUID)
if err != nil {
m.lo.Error("error fetching updated message for webhook event", "uuid", message.UUID, "error", err)
} else {
m.webhookStore.TriggerEvent(wmodels.EventMessageCreated, updatedMessage)
// Refetch message if this message has media attachments, as media gets linked after inserting the message.
if len(message.Media) > 0 {
refetchedMessage, err := m.GetMessage(message.UUID)
if err != nil {
m.lo.Error("error fetching message after insert", "error", err)
} else {
// Replace the message in the struct with the refetched message.
*message = refetchedMessage
}
}
// Trigger webhook for new message created.
m.webhookStore.TriggerEvent(wmodels.EventMessageCreated, message)
return nil
}
@@ -530,7 +545,7 @@ func (m *Manager) InsertConversationActivity(activityType, conversationUUID, new
content, err := m.getMessageActivityContent(activityType, newValue, actor.FullName())
if err != nil {
m.lo.Error("error could not generate activity content", "error", err)
return err
return envelope.NewError(envelope.GeneralError, m.i18n.Ts("globals.messages.errorGenerating", "name", "{globals.terms.activityMessage}"), nil)
}
message := models.Message{

View File

@@ -509,9 +509,9 @@ inserted_msg AS (
$1, $2, (SELECT id FROM conversation_id),
$5, $6, $7, $8, $9, $10, $11, $12
)
RETURNING id, uuid, created_at, conversation_id
RETURNING *
)
SELECT id, uuid, created_at FROM inserted_msg;
SELECT * FROM inserted_msg;
-- name: message-exists-by-source-id
SELECT conversation_id