HH-469: close WebSocket fanout contract gaps (#109)

* HH-469: close websocket fanout contract gaps

* fix: unify message sender contracts

---------

Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
Rogee
2026-08-23 00:22:01 +08:00
committed by GitHub
co-authored by rogee
parent f91dc6ca1a
commit ef8931099c
14 changed files with 377 additions and 114 deletions
@@ -1227,7 +1227,8 @@ func (s *ContactHandlerCRUDTestSuite) TestListAttachmentsTimelineDepthMatchesCha
firstSender := first["sender"].(map[string]any)
s.Equal(float64(s.user.ID), firstSender["id"])
s.Equal("CRUDTestUser", firstSender["name"])
s.Equal("agent", firstSender["role"])
s.Equal("user", firstSender["type"])
s.NotContains(firstSender, "role")
s.Equal(float64(newMessage.CreatedAt.Unix()), first["created_at"])
second := payload[1].(map[string]any)
@@ -4,7 +4,6 @@ import (
"context"
"encoding/json"
"os"
"path/filepath"
"strings"
"time"
@@ -457,22 +456,22 @@ func serializeMessage(ctx context.Context, db *gorm.DB, message *model.Message,
case "contact":
var contact model.Contact
if err := db.WithContext(ctx).First(&contact, *message.SenderID).Error; err == nil {
payload.Sender = serializeContactWithContext(ctx, &contact)
payload.Sender = contact.PushEventData()
}
case "agent_bot":
var bot model.AgentBot
if err := db.WithContext(ctx).First(&bot, *message.SenderID).Error; err == nil {
payload.Sender = serializeAgentBotSender(&bot)
payload.Sender = bot.PushEventData()
}
case "captain_assistant":
var assistant model.CaptainAssistant
if err := db.WithContext(ctx).First(&assistant, *message.SenderID).Error; err == nil {
payload.Sender = serializeCaptainAssistantSender(&assistant)
payload.Sender = assistant.PushEventData()
}
default:
var user model.User
if err := db.WithContext(ctx).First(&user, *message.SenderID).Error; err == nil {
payload.Sender = serializeUser(&user, message.AccountID)
payload.Sender = user.PushEventData()
}
}
}
@@ -616,27 +615,7 @@ func serializeAttachment(ctx context.Context, db *gorm.DB, attachment *model.Att
}
func serializeAttachmentPushEventData(attachment *model.Attachment) map[string]any {
extension := strings.TrimPrefix(filepath.Ext(attachment.FileName), ".")
dataURL := nonEmpty(attachment.FileURL, attachment.ExternalURL)
payload := map[string]any{
"id": attachment.ID,
"message_id": attachment.MessageID,
"file_type": attachment.FileType,
"account_id": attachment.AccountID,
"data_url": dataURL,
"thumb_url": attachment.ThumbURL,
"file_size": attachment.FileSize,
"extension": extension,
"width": attachment.Width,
"height": attachment.Height,
}
if strings.TrimSpace(attachment.Metadata) != "" {
metadata := map[string]any{}
if json.Unmarshal([]byte(attachment.Metadata), &metadata) == nil {
payload["metadata"] = metadata
}
}
return payload
return attachment.PushEventData()
}
func serializeAttachmentWithConversation(ctx context.Context, db *gorm.DB, attachment *model.Attachment) map[string]any {
@@ -662,27 +641,27 @@ func serializeMessageSender(ctx context.Context, db *gorm.DB, message *model.Mes
if senderType == "contact" {
var contact model.Contact
if err := db.WithContext(ctx).First(&contact, *message.SenderID).Error; err == nil {
return serializeContactWithContext(ctx, &contact)
return contact.PushEventData()
}
return nil
}
if senderType == "agent_bot" {
var bot model.AgentBot
if err := db.WithContext(ctx).First(&bot, *message.SenderID).Error; err == nil {
return serializeAgentBotSender(&bot)
return bot.PushEventData()
}
return nil
}
if senderType == "captain_assistant" {
var assistant model.CaptainAssistant
if err := db.WithContext(ctx).First(&assistant, *message.SenderID).Error; err == nil {
return serializeCaptainAssistantSender(&assistant)
return assistant.PushEventData()
}
return nil
}
var user model.User
if err := db.WithContext(ctx).First(&user, *message.SenderID).Error; err == nil {
return serializeUser(&user, message.AccountID)
return user.PushEventData()
}
return nil
}
@@ -764,11 +743,6 @@ func serializeAgentBotSender(bot *model.AgentBot) map[string]any {
}
}
func serializeCaptainAssistantSender(assistant *model.CaptainAssistant) map[string]any {
avatarURL := strings.TrimRight(os.Getenv("FRONTEND_URL"), "/") + "/assets/images/dashboard/captain/logo.svg"
return map[string]any{"id": assistant.ID, "name": assistant.Name, "avatar_url": avatarURL, "description": assistant.Description, "created_at": assistant.CreatedAt, "type": "captain_assistant"}
}
func serializeAgentBotSlim(bot *model.AgentBot) map[string]any {
return map[string]any{
"id": bot.ID,
@@ -495,7 +495,8 @@ func (s *MessageHandlerTestSuite) TestCreate_ChatwootFrontendPayloadDefaultsOutg
assert.Equal(s.T(), float64(s.testUser.ID), sender["id"])
assert.Equal(s.T(), "Msg Handler Agent", sender["name"])
assert.Equal(s.T(), "Message Agent", sender["available_name"])
assert.Equal(s.T(), "message-agent@example.com", sender["email"])
assert.Equal(s.T(), "user", sender["type"])
assert.NotContains(s.T(), sender, "email")
}
func (s *MessageHandlerTestSuite) TestCreate_MultipartAttachmentPersistsAndSerializes() {