tf_types/
bridge_matrix.rs1use 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}