Skip to main content

tf_types/
bridge_matrix.rs

1//! Matrix bridge — Rust mirror.
2
3use serde::{Deserialize, Serialize};
4use serde_json::{json, Value};
5
6use crate::bridges::{Bridge, BridgeError, BridgeKind};
7
8#[derive(Clone, Debug, Serialize, Deserialize)]
9pub struct MatrixEvent {
10    pub event_id: String,
11    pub room_id: String,
12    #[serde(rename = "type")]
13    pub kind: String,
14    pub sender: String,
15    pub origin_server_ts: i64,
16    pub content: Value,
17    #[serde(default, skip_serializing_if = "Option::is_none")]
18    pub state_key: Option<String>,
19    #[serde(default, skip_serializing_if = "Option::is_none")]
20    pub signatures: Option<Value>,
21}
22
23#[derive(Clone, Debug, Default)]
24pub struct MatrixBridgeConfig {
25    pub bridge_id: String,
26    pub trust_domain: String,
27    pub default_level: Option<String>,
28}
29
30pub struct MatrixBridge {
31    cfg: MatrixBridgeConfig,
32}
33
34impl MatrixBridge {
35    pub fn new(cfg: MatrixBridgeConfig) -> Self {
36        MatrixBridge { cfg }
37    }
38
39    pub fn matrix_event_to_proof_event(&self, m: &MatrixEvent) -> Result<Value, BridgeError> {
40        if m.event_id.is_empty() || m.sender.is_empty() || m.kind.is_empty() {
41            return Err(BridgeError::InvalidInput(
42                "Matrix event missing event_id / sender / type".into(),
43            ));
44        }
45        let actor = map_sender(&m.sender)?;
46        let timestamp = ms_to_iso8601(m.origin_server_ts);
47        let from_message = m.kind == "m.room.message";
48        let tf_type = if from_message {
49            "matrix.message".to_string()
50        } else if let Some(rest) = m.kind.strip_prefix("m.") {
51            format!("matrix.{}", rest)
52        } else {
53            m.kind.clone()
54        };
55        Ok(json!({
56            "event_version": "1",
57            "id": m.event_id,
58            "type": tf_type,
59            "actor_id": actor,
60            "timestamp": timestamp,
61            "level": self.cfg.default_level.clone().unwrap_or_else(|| "L1".into()),
62            "context": {
63                "matrix": {
64                    "room_id": m.room_id,
65                    "state_key": m.state_key,
66                    "content": m.content,
67                    "server_signatures": m.signatures,
68                }
69            },
70            "signature": { "algorithm": "ed25519", "signer": actor, "signature": "AAAA" }
71        }))
72    }
73}
74
75impl Bridge for MatrixBridge {
76    fn bridge_id(&self) -> &str {
77        &self.cfg.bridge_id
78    }
79    fn kind(&self) -> BridgeKind {
80        BridgeKind::Matrix
81    }
82    fn trust_domain(&self) -> &str {
83        &self.cfg.trust_domain
84    }
85}
86
87pub fn map_sender(sender: &str) -> Result<String, BridgeError> {
88    let rest = sender.strip_prefix('@').ok_or_else(|| {
89        BridgeError::InvalidInput(format!("cannot map non-Matrix sender: {}", sender))
90    })?;
91    let colon = rest.find(':').ok_or_else(|| {
92        BridgeError::InvalidInput(format!("cannot map non-Matrix sender: {}", sender))
93    })?;
94    let local = &rest[..colon];
95    let server = &rest[colon + 1..];
96    Ok(format!("tf:actor:human:{}/{}", server, local))
97}
98
99fn ms_to_iso8601(ms: i64) -> String {
100    let secs = ms.div_euclid(1000);
101    let (y, m, d, h, mi, s) = secs_to_ymdhms(secs);
102    format!("{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z", y, m, d, h, mi, s)
103}
104
105fn secs_to_ymdhms(secs: i64) -> (i32, u32, u32, u32, u32, u32) {
106    let days = secs.div_euclid(86_400);
107    let time = secs.rem_euclid(86_400);
108    let hour = (time / 3600) as u32;
109    let minute = ((time % 3600) / 60) as u32;
110    let second = (time % 60) as u32;
111    let z = days + 719_468;
112    let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
113    let doe = (z - era * 146_097) as u64;
114    let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
115    let y = yoe as i64 + era * 400;
116    let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
117    let mp = (5 * doy + 2) / 153;
118    let d = (doy - (153 * mp + 2) / 5 + 1) as u32;
119    let m = if mp < 10 {
120        (mp + 3) as u32
121    } else {
122        (mp - 9) as u32
123    };
124    let year = if m <= 2 { y + 1 } else { y };
125    (year as i32, m, d, hour, minute, second)
126}