Skip to content

Integrate chat support with live WebSocket streaming - #127

Open
vikasbolla wants to merge 1 commit into
IBM:mainfrom
vikasbolla:integrate_chatbot
Open

Integrate chat support with live WebSocket streaming#127
vikasbolla wants to merge 1 commit into
IBM:mainfrom
vikasbolla:integrate_chatbot

Conversation

@vikasbolla

Copy link
Copy Markdown
Contributor

This PR integrates a real-time chat support system that allows users to contact admins directly from the power-access-cloud UI.

  • User chat widget — users can open a chat panel, send messages, view history, and end conversations. The connection is maintained over WebSocket so messages are delivered instantly without polling.
  • Admin chat panel — admins can see all active conversations with live unread indicators, open any conversation to read the full history, reply in real time, and end conversations on behalf of users.
  • WebSocket infrastructure — an in-memory pub/sub hub routes messages between the user's session goroutine and the admin's session goroutine without any database polling. Admin replies reach the user's browser in microseconds.
  • Persistent history — all messages are stored in MongoDB. When a user reconnects or navigates back to the chat, their full conversation history is restored. Ended conversations are recorded with a sentinel so the state survives server restarts.
  • Async DB writes — admin replies are written to the database via a background worker queue so the admin UI never blocks waiting for a DB round trip.

@vikasbolla
vikasbolla force-pushed the integrate_chatbot branch 4 times, most recently from 9e3fdf8 to 2d16be6 Compare July 30, 2026 09:16
UserID: userID,
Username: username,
Message: EndedSentinel,
Sender: models.SenderSystem,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we have a record of who ended the conversation? the admin or user? Recording it as system, we won't know who ended it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be taken up as an enhancement.

{Key: fieldUserID, Value: "$" + fieldUserID},
{Key: fieldConversationID, Value: "$" + fieldConversationID},
}},
{Key: "message_count", Value: bson.D{{Key: "$sum", Value: 1}}},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a small comment helping us understanding the pipeline. This seems like, it loads one message per conversation when the admin window loads.


// Sender constants for ChatMessage.Sender.
const (
SenderUser = "user"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a suggestion, the best way to do this was declaring a type
type SenderType string (you may choose a better name)
then
SenderType User, Admin,System.
It's better coding standards. Thanks!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, changed it, thanks

// non-fatal: treat as not ended
}

history, err := dbCon.GetChatMessages(ctx, userID, conversationID)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please help me understand, does this load all the chat messages at once, or does it load a set of messages as and when we scroll in the frontend UI?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently it loads all the chat messages at once. But it is a good point, as I think when conversation are very long, it might be better to load when user scrolls in UI. Maybe we can take it as a enhacement

zap.String("userID", userID),
zap.Int64("conversationID", conversationID),
zap.Int("history_len", len(history)))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment explaining incomingFromAdminCh.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}

// AdminReplyToConversation inserts an admin message into an existing conversation.
func (db *MongoDB) AdminReplyToConversation(ctx context.Context, msg *models.ChatMessage) error {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both the InsertChatMessage and AdminReplyToConversation are the same, except for the context that is being passed, we can try combining it, rather than keeping duplicate code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, have modified it, thanks

// published to, so a single admin WS can receive notifications for all convs.
const adminBroadcastKey = "admin:broadcast"

var hub = &chatHub{

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to understand, why a slice of channels is necessary, let's discuss once. If not required, we can pick it up as an enhancement in a different PR.

@GunaKKIBM

Copy link
Copy Markdown
Member

Added minor comments, certain changes do not impact the functionality, I think the same can be addressed. Others can be taken up incrementally. Thank you for the PR!

Signed-off-by: Vikas <vikas.satyanarayana.bolla@ibm.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants