rustybook-messenger 0.2.0

Messenger client for Rustybook
Documentation
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"),
        }
    }
}