mod legacy;
mod lightspeed;
use crate::error::MessengerError;
use crate::events::Event;
pub fn parse_events(topic: &str, payload: &[u8]) -> Result<Vec<Event>, MessengerError> {
match topic {
"/t_ms" => legacy::parse_messages(payload),
"/ls_resp" => lightspeed::parse_lightspeed(payload),
"/thread_typing" => legacy::parse_typing(payload),
"/orca_presence" => legacy::parse_presence(payload),
_ => Ok(Vec::new()),
}
}
#[cfg(test)]
mod tests {
use crate::events::Event;
use super::parse_events;
#[test]
fn parses_tms_message_event() {
let payload = br#"{
"deltas": [
{
"class": "NewMessage",
"body": "hello",
"messageMetadata": {
"threadKey": {"threadFbId": "987"},
"actorFbId": "123",
"messageId": "mid.abc",
"timestamp": "1700000000000"
}
}
]
}"#;
let events = match parse_events("/t_ms", payload) {
Ok(events) => events,
Err(error) => panic!("failed to parse /t_ms payload: {error}"),
};
assert_eq!(events.len(), 1);
match &events[0] {
Event::Message(message) => {
assert_eq!(message.thread_id, "987");
assert_eq!(message.sender_id, "123");
}
_ => panic!("expected message event"),
}
}
#[test]
fn parses_tms_message_event_with_numeric_ids() {
let payload = br#"{
"deltas": [
{
"class": "NewMessage",
"body": "hello",
"messageMetadata": {
"threadKey": {"threadFbId": 987},
"actorFbId": 123,
"messageId": "mid.abc",
"timestamp": 1700000000000
}
}
]
}"#;
let events = match parse_events("/t_ms", payload) {
Ok(events) => events,
Err(error) => panic!("failed to parse /t_ms payload: {error}"),
};
assert_eq!(events.len(), 1);
match &events[0] {
Event::Message(message) => {
assert_eq!(message.thread_id, "987");
assert_eq!(message.sender_id, "123");
assert_eq!(message.timestamp_ms, Some(1700000000000));
}
_ => panic!("expected message event"),
}
}
#[test]
fn parses_tms_message_event_from_wrapped_payload() {
let payload = br#"{
"d": "{\"deltas\":[{\"class\":\"NewMessage\",\"body\":\"hello\",\"messageMetadata\":{\"threadKey\":{\"otherUserFbId\":\"999\"},\"actorFbId\":\"123\",\"messageId\":\"mid.wrapped\",\"timestamp\":\"1700000000001\"}}]}"
}"#;
let events = match parse_events("/t_ms", payload) {
Ok(events) => events,
Err(error) => panic!("failed to parse /t_ms payload: {error}"),
};
assert_eq!(events.len(), 1);
match &events[0] {
Event::Message(message) => {
assert_eq!(message.thread_id, "999");
assert_eq!(message.sender_id, "123");
assert_eq!(message.text.as_deref(), Some("hello"));
}
_ => panic!("expected message event"),
}
}
#[test]
fn parses_tms_message_event_without_class() {
let payload = br#"{
"delta": {
"messageMetadata": {
"threadKey": {"threadFbId": "456"},
"actorFbId": "123",
"messageId": "mid.noclass",
"timestamp": "1700000000002"
},
"body": "hello from delta"
}
}"#;
let events = match parse_events("/t_ms", payload) {
Ok(events) => events,
Err(error) => panic!("failed to parse /t_ms payload: {error}"),
};
assert_eq!(events.len(), 1);
match &events[0] {
Event::Message(message) => {
assert_eq!(message.thread_id, "456");
assert_eq!(message.sender_id, "123");
assert_eq!(message.text.as_deref(), Some("hello from delta"));
}
_ => panic!("expected message event"),
}
}
#[test]
fn parses_typing_event() {
let payload = br#"{"sender_fbid":"123","state":1,"thread":"987"}"#;
let events = match parse_events("/thread_typing", payload) {
Ok(events) => events,
Err(error) => panic!("failed to parse /thread_typing payload: {error}"),
};
assert_eq!(events.len(), 1);
match &events[0] {
Event::Typing(typing) => {
assert_eq!(typing.user_id, "123");
assert!(typing.is_typing);
}
_ => panic!("expected typing event"),
}
}
#[test]
fn parses_presence_event() {
let payload = br#"{"list":[{"u":"123","p":2,"l":1700000000000}]}"#;
let events = match parse_events("/orca_presence", payload) {
Ok(events) => events,
Err(error) => panic!("failed to parse /orca_presence payload: {error}"),
};
assert_eq!(events.len(), 1);
match &events[0] {
Event::Presence(presence) => {
assert_eq!(presence.user_id, "123");
assert!(presence.is_active);
}
_ => panic!("expected presence event"),
}
}
#[test]
fn parses_lightspeed_message_event() {
let payload = br#"{
"payload": {
"tasks": [
{
"payload": "{\"thread_id\":\"6988146184541722\",\"sender_id\":\"100077844107832\",\"message_id\":\"mid.ls.1\",\"text\":\"hello from ls\",\"timestamp_ms\":1700000002222}"
}
]
}
}"#;
let events = match parse_events("/ls_resp", payload) {
Ok(events) => events,
Err(error) => panic!("failed to parse /ls_resp payload: {error}"),
};
assert_eq!(events.len(), 1);
match &events[0] {
Event::Message(message) => {
assert_eq!(message.thread_id, "6988146184541722");
assert_eq!(message.sender_id, "100077844107832");
assert_eq!(message.text.as_deref(), Some("hello from ls"));
assert_eq!(message.message_id.as_deref(), Some("mid.ls.1"));
}
_ => panic!("expected message event"),
}
}
#[test]
fn parses_lightspeed_message_event_from_message_metadata_shape() {
let payload = br#"{
"payload": "{\"delta\":{\"class\":\"NewMessage\",\"body\":\"legacy-in-ls\",\"messageMetadata\":{\"threadKey\":{\"threadFbId\":\"123\"},\"actorFbId\":\"55\",\"messageId\":\"mid.mix\",\"timestamp\":\"1700000010000\"}}}"
}"#;
let events = match parse_events("/ls_resp", payload) {
Ok(events) => events,
Err(error) => panic!("failed to parse /ls_resp payload: {error}"),
};
assert_eq!(events.len(), 1);
match &events[0] {
Event::Message(message) => {
assert_eq!(message.thread_id, "123");
assert_eq!(message.sender_id, "55");
assert_eq!(message.message_id.as_deref(), Some("mid.mix"));
}
_ => panic!("expected message event"),
}
}
#[test]
fn parses_lightspeed_typing_operation() {
let payload = br#"{
"request_id": null,
"payload": "{\"name\":null,\"step\":[1,[5,\"updateTypingIndicator\",[19,\"6988146184541722\"],[19,\"100077844107832\"],true,[9]]]}",
"sp": ["updateTypingIndicator"],
"target": 3
}"#;
let events = match parse_events("/ls_resp", payload) {
Ok(events) => events,
Err(error) => panic!("failed to parse /ls_resp payload: {error}"),
};
assert_eq!(events.len(), 1);
match &events[0] {
Event::Typing(event) => {
assert_eq!(event.user_id, "100077844107832");
assert_eq!(event.thread_id.as_deref(), Some("6988146184541722"));
assert!(event.is_typing);
}
_ => panic!("expected typing event"),
}
}
#[test]
fn parses_lightspeed_insert_message_operation() {
let payload = br#"{
"request_id": null,
"payload": "{\"name\":null,\"step\":[1,[5,\"insertMessage\",\"testing\",[9],[19,\"80\"],[19,\"6988146184541722\"],[19,\"0\"],[19,\"1772703405343\"],[19,\"1772703405343\"],[9],\"mid.$gABjTrpL31hqi7qCdH2cvVtZE68sG\",\"7435256988091271942\",[19,\"100077844107832\"]]]}",
"sp": ["insertMessage"],
"target": 3
}"#;
let events = match parse_events("/ls_resp", payload) {
Ok(events) => events,
Err(error) => panic!("failed to parse /ls_resp payload: {error}"),
};
assert_eq!(events.len(), 1);
match &events[0] {
Event::Message(message) => {
assert_eq!(message.thread_id, "6988146184541722");
assert_eq!(message.sender_id, "100077844107832");
assert_eq!(
message.message_id.as_deref(),
Some("mid.$gABjTrpL31hqi7qCdH2cvVtZE68sG")
);
assert_eq!(message.text.as_deref(), Some("testing"));
assert_eq!(message.timestamp_ms, Some(1772703405343));
}
_ => panic!("expected message event"),
}
}
}