diff --git a/gbapp/MESSAGE_TYPES.md b/gbapp/MESSAGE_TYPES.md new file mode 100644 index 00000000..c86687db --- /dev/null +++ b/gbapp/MESSAGE_TYPES.md @@ -0,0 +1,100 @@ +# Message Types Documentation + +## Overview + +The botserver uses a simple enum-based system for categorizing different types of messages flowing through the system. This document describes each message type and its usage. + +## Message Type Enum + +The `MessageType` enum is defined in both Rust (backend) and JavaScript (frontend) to ensure consistency across the entire application. + +### Backend (Rust) +Location: `src/core/shared/message_types.rs` + +### Frontend (JavaScript) +Location: `ui/shared/messageTypes.js` + +## Message Types + +| Value | Name | Description | Usage | +|-------|------|-------------|-------| +| 0 | `EXTERNAL` | Messages from external systems | WhatsApp, Instagram, Teams, and other external channel integrations | +| 1 | `USER` | User messages from web interface | Regular user input from the web chat interface | +| 2 | `BOT_RESPONSE` | Bot responses | Can contain either regular text content or JSON-encoded events (theme changes, thinking indicators, etc.) | +| 3 | `CONTINUE` | Continue interrupted response | Used when resuming a bot response that was interrupted | +| 4 | `SUGGESTION` | Suggestion or command message | Used for contextual suggestions and command messages | +| 5 | `CONTEXT_CHANGE` | Context change notification | Signals when the conversation context has changed | + +## Special Handling for BOT_RESPONSE (Type 2) + +The `BOT_RESPONSE` type requires special handling in the frontend because it can contain two different types of content: + +### 1. Regular Text Content +Standard bot responses containing plain text or markdown that should be displayed directly to the user. + +### 2. Event Messages +JSON-encoded objects with the following structure: +```json +{ + "event": "event_type", + "data": { + // Event-specific data + } +} +``` + +#### Supported Events: +- `thinking_start` - Bot is processing/thinking +- `thinking_end` - Bot finished processing +- `warn` - Warning message to display +- `context_usage` - Context usage update +- `change_theme` - Theme customization data + +## Frontend Detection Logic + +The frontend uses the following logic to differentiate between regular content and event messages: + +1. Check if `message_type === 2` (BOT_RESPONSE) +2. Check if content starts with `{` or `[` (potential JSON) +3. Attempt to parse as JSON +4. If successful and has `event` and `data` properties, handle as event +5. Otherwise, process as regular message content + +## Usage Examples + +### Rust Backend +```rust +use crate::shared::message_types::MessageType; + +let response = BotResponse { + // ... other fields + message_type: MessageType::BOT_RESPONSE, + // ... +}; +``` + +### JavaScript Frontend +```javascript +if (message.message_type === MessageType.BOT_RESPONSE) { + // Handle bot response +} + +if (isUserMessage(message)) { + // Handle user message +} +``` + +## Migration Notes + +When migrating from magic numbers to the MessageType enum: + +1. Replace all hardcoded message type numbers with the appropriate constant +2. Import the MessageType module/script where needed +3. Use the helper functions for type checking when available + +## Benefits + +1. **Type Safety**: Reduces errors from using wrong message type numbers +2. **Readability**: Code is self-documenting with named constants +3. **Maintainability**: Easy to add new message types or modify existing ones +4. **Consistency**: Same values used across frontend and backend \ No newline at end of file diff --git a/src/core/shared/message_types.rs b/src/core/shared/message_types.rs new file mode 100644 index 00000000..a0d51817 --- /dev/null +++ b/src/core/shared/message_types.rs @@ -0,0 +1,82 @@ +use serde::{Deserialize, Serialize}; + +/// Enum representing different types of messages in the bot system +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(transparent)] +pub struct MessageType(pub i32); + +impl MessageType { + /// Regular message from external systems (WhatsApp, Instagram, etc.) + pub const EXTERNAL: MessageType = MessageType(0); + + /// User message from web interface + pub const USER: MessageType = MessageType(1); + + /// Bot response (can be regular content or event) + pub const BOT_RESPONSE: MessageType = MessageType(2); + + /// Continue interrupted response + pub const CONTINUE: MessageType = MessageType(3); + + /// Suggestion or command message + pub const SUGGESTION: MessageType = MessageType(4); + + /// Context change notification + pub const CONTEXT_CHANGE: MessageType = MessageType(5); +} + +impl From for MessageType { + fn from(value: i32) -> Self { + MessageType(value) + } +} + +impl From for i32 { + fn from(value: MessageType) -> Self { + value.0 + } +} + +impl Default for MessageType { + fn default() -> Self { + MessageType::USER + } +} + +impl std::fmt::Display for MessageType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let name = match self.0 { + 0 => "EXTERNAL", + 1 => "USER", + 2 => "BOT_RESPONSE", + 3 => "CONTINUE", + 4 => "SUGGESTION", + 5 => "CONTEXT_CHANGE", + _ => "UNKNOWN", + }; + write!(f, "{}", name) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_message_type_conversion() { + assert_eq!(i32::from(MessageType::USER), 1); + assert_eq!(MessageType::from(2), MessageType::BOT_RESPONSE); + } + + #[test] + fn test_message_type_display() { + assert_eq!(MessageType::USER.to_string(), "USER"); + assert_eq!(MessageType::BOT_RESPONSE.to_string(), "BOT_RESPONSE"); + } + + #[test] + fn test_message_type_equality() { + assert_eq!(MessageType::USER, MessageType(1)); + assert_ne!(MessageType::USER, MessageType::BOT_RESPONSE); + } +} diff --git a/tests/websocket_test.html b/tests/websocket_test.html new file mode 100644 index 00000000..2dcfbea8 --- /dev/null +++ b/tests/websocket_test.html @@ -0,0 +1,247 @@ + + + + + + WebSocket Message Handler Test + + + +

WebSocket Message Handler Test Suite

+ +
+

Test Cases

+ + +
+ +
+

Individual Tests

+ + + + + + +
+ +
+ + + + diff --git a/ui/shared/messageTypes.js b/ui/shared/messageTypes.js new file mode 100644 index 00000000..8a6ce5f6 --- /dev/null +++ b/ui/shared/messageTypes.js @@ -0,0 +1,100 @@ +/** + * Message Type Constants + * Defines the different types of messages in the bot system + * These values must match the server-side MessageType enum in Rust + */ + +const MessageType = { + /** Regular message from external systems (WhatsApp, Instagram, etc.) */ + EXTERNAL: 0, + + /** User message from web interface */ + USER: 1, + + /** Bot response (can be regular content or event) */ + BOT_RESPONSE: 2, + + /** Continue interrupted response */ + CONTINUE: 3, + + /** Suggestion or command message */ + SUGGESTION: 4, + + /** Context change notification */ + CONTEXT_CHANGE: 5 +}; + +/** + * Get the name of a message type + * @param {number} type - The message type number + * @returns {string} The name of the message type + */ +function getMessageTypeName(type) { + const names = { + 0: 'EXTERNAL', + 1: 'USER', + 2: 'BOT_RESPONSE', + 3: 'CONTINUE', + 4: 'SUGGESTION', + 5: 'CONTEXT_CHANGE' + }; + return names[type] || 'UNKNOWN'; +} + +/** + * Check if a message is a bot response + * @param {Object} message - The message object + * @returns {boolean} True if the message is a bot response + */ +function isBotResponse(message) { + return message && message.message_type === MessageType.BOT_RESPONSE; +} + +/** + * Check if a message is a user message + * @param {Object} message - The message object + * @returns {boolean} True if the message is from a user + */ +function isUserMessage(message) { + return message && message.message_type === MessageType.USER; +} + +/** + * Check if a message is a context change + * @param {Object} message - The message object + * @returns {boolean} True if the message is a context change + */ +function isContextChange(message) { + return message && message.message_type === MessageType.CONTEXT_CHANGE; +} + +/** + * Check if a message is a suggestion + * @param {Object} message - The message object + * @returns {boolean} True if the message is a suggestion + */ +function isSuggestion(message) { + return message && message.message_type === MessageType.SUGGESTION; +} + +// Export for use in other modules (if using modules) +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + MessageType, + getMessageTypeName, + isBotResponse, + isUserMessage, + isContextChange, + isSuggestion + }; +} + +// Also make available globally for non-module scripts +if (typeof window !== 'undefined') { + window.MessageType = MessageType; + window.getMessageTypeName = getMessageTypeName; + window.isBotResponse = isBotResponse; + window.isUserMessage = isUserMessage; + window.isContextChange = isContextChange; + window.isSuggestion = isSuggestion; +}