use std::sync::Arc;
use crate::types::{WsCmd, WsFrame};
use crate::types::Logger;
#[derive(Debug, Clone)]
pub enum MessageEvent {
Message(WsFrame),
Text(WsFrame),
Image(WsFrame),
Mixed(WsFrame),
Voice(WsFrame),
File(WsFrame),
Event(WsFrame),
EnterChat(WsFrame),
TemplateCardEvent(WsFrame),
FeedbackEvent(WsFrame),
}
pub struct MessageHandler {
logger: Arc<dyn Logger>,
}
impl MessageHandler {
pub fn new(logger: Arc<dyn Logger>) -> Self {
Self { logger }
}
pub fn handle_frame(&self, frame: &WsFrame) -> Vec<MessageEvent> {
match self.parse_frame(frame) {
Ok(events) => events,
Err(e) => {
self.logger.error(&format!("Failed to handle message: {}", e));
vec![]
}
}
}
fn parse_frame(&self, frame: &WsFrame) -> Result<Vec<MessageEvent>, String> {
if frame.cmd.as_deref() == Some(WsCmd::EVENT_CALLBACK) {
return Ok(self.handle_event_callback(frame));
}
Ok(self.handle_message_callback(frame))
}
fn handle_message_callback(&self, frame: &WsFrame) -> Vec<MessageEvent> {
let mut events = Vec::new();
events.push(MessageEvent::Message(frame.clone()));
let body = frame.body.as_ref().map(|v| v.as_object()).flatten();
let msgtype = body
.and_then(|b| b.get("msgtype"))
.and_then(|v| v.as_str())
.unwrap_or("");
match msgtype {
"text" => events.push(MessageEvent::Text(frame.clone())),
"image" => events.push(MessageEvent::Image(frame.clone())),
"mixed" => events.push(MessageEvent::Mixed(frame.clone())),
"voice" => events.push(MessageEvent::Voice(frame.clone())),
"file" => events.push(MessageEvent::File(frame.clone())),
_ => {
self.logger
.debug(&format!("Received unhandled message type: {}", msgtype));
}
}
events
}
fn handle_event_callback(&self, frame: &WsFrame) -> Vec<MessageEvent> {
let mut events = Vec::new();
events.push(MessageEvent::Event(frame.clone()));
let body = frame.body.as_ref().and_then(|v| v.as_object());
let event_type = body
.and_then(|b| b.get("event"))
.and_then(|v| v.as_object())
.and_then(|e| e.get("eventtype"))
.and_then(|v| v.as_str());
if let Some(event_type) = event_type {
match event_type {
"enter_chat" => events.push(MessageEvent::EnterChat(frame.clone())),
"template_card_event" => {
events.push(MessageEvent::TemplateCardEvent(frame.clone()))
}
"feedback_event" => events.push(MessageEvent::FeedbackEvent(frame.clone())),
_ => {
self.logger.debug(&format!(
"Received unknown event type: {}",
event_type
));
}
}
} else {
self.logger.debug(&format!(
"Received event callback without eventtype: {:?}",
body
));
}
events
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::logger::DefaultLogger;
use serde_json::json;
use std::sync::Arc;
#[test]
fn test_handle_text_message() {
let logger = Arc::new(DefaultLogger::default());
let handler = MessageHandler::new(logger);
let frame = WsFrame {
cmd: Some(WsCmd::CALLBACK.to_string()),
headers: crate::types::WsFrameHeaders::new("test_req_id"),
body: Some(json!({
"msgtype": "text",
"text": {"content": "Hello"}
})),
errcode: None,
errmsg: None,
};
let events = handler.handle_frame(&frame);
assert!(matches!(events[0], MessageEvent::Message(_)));
assert!(matches!(events[1], MessageEvent::Text(_)));
}
#[test]
fn test_handle_event_callback() {
let logger = Arc::new(DefaultLogger::default());
let handler = MessageHandler::new(logger);
let frame = WsFrame {
cmd: Some(WsCmd::EVENT_CALLBACK.to_string()),
headers: crate::types::WsFrameHeaders::new("test_req_id"),
body: Some(json!({
"event": {"eventtype": "enter_chat"}
})),
errcode: None,
errmsg: None,
};
let events = handler.handle_frame(&frame);
assert!(matches!(events[0], MessageEvent::Event(_)));
assert!(matches!(events[1], MessageEvent::EnterChat(_)));
}
}