Skip to main content

matrix_bridge_telegram/
cli.rs

1use std::path::PathBuf;
2
3use clap::{Parser, Subcommand};
4use serde_json::json;
5
6#[derive(Parser, Debug)]
7#[command(name = "matrix-telegram-bridge")]
8#[command(about = "Matrix-Telegram Bridge", long_about = None)]
9pub struct Cli {
10    #[command(subcommand)]
11    pub command: Option<Commands>,
12
13    #[arg(short, long, env = "CONFIG_PATH", default_value = "config.yaml")]
14    pub config: PathBuf,
15
16    #[arg(short, long, env = "REGISTRATION_PATH")]
17    pub registration: Option<PathBuf>,
18}
19
20#[derive(Subcommand, Debug)]
21pub enum Commands {
22    #[command(about = "Generate a registration file for the Matrix homeserver")]
23    GenerateRegistration {
24        #[arg(short, long, default_value = "telegram-registration.yaml")]
25        output: PathBuf,
26
27        #[arg(long, default_value = "telegram")]
28        id: String,
29
30        #[arg(long, default_value = "http://localhost:8008")]
31        homeserver_url: String,
32
33        #[arg(long, default_value = "example.org")]
34        domain: String,
35    },
36
37    #[command(about = "Grant admin privileges to a Matrix user")]
38    Adminme {
39        #[arg(short, long)]
40        user: String,
41
42        #[arg(short, long)]
43        room: Option<String>,
44
45        #[arg(short, long, default_value = "100")]
46        power_level: i64,
47    },
48
49    #[command(about = "Migrate data between database backends")]
50    Migrate {
51        #[arg(long, help = "Source database URL")]
52        from: String,
53
54        #[arg(long, help = "Destination database URL")]
55        to: String,
56
57        #[arg(short, long, help = "Dry run without making changes")]
58        dry_run: bool,
59    },
60
61    #[command(about = "List all bridged rooms")]
62    ListRooms {
63        #[arg(short, long, help = "Filter by chat type (user, group, channel)")]
64        chat_type: Option<String>,
65
66        #[arg(short, long, default_value = "100")]
67        limit: i64,
68    },
69
70    #[command(about = "Unbridge a room")]
71    Unbridge {
72        #[arg(short, long, help = "Matrix room ID")]
73        room: String,
74
75        #[arg(short, long, help = "Also leave the Matrix room")]
76        leave: bool,
77    },
78
79    #[command(about = "Validate the configuration file")]
80    ValidateConfig,
81
82    #[command(about = "Show bridge status")]
83    Status,
84}
85
86pub fn generate_registration(id: &str, homeserver_url: &str, domain: &str) -> String {
87    let as_token = generate_token();
88    let hs_token = generate_token();
89
90    let registration = json!({
91        "id": id,
92        "url": homeserver_url,
93        "as_token": as_token,
94        "hs_token": hs_token,
95        "sender_localpart": "_telegram_",
96        "rate_limited": false,
97        "protocols": ["telegram"],
98        "namespaces": {
99            "users": [{
100                "exclusive": true,
101                "regex": format!("@_telegram_.*:{}", domain)
102            }],
103            "aliases": [{
104                "exclusive": true,
105                "regex": format!("#_telegram_.*:{}", domain)
106            }],
107            "rooms": []
108        }
109    });
110
111    serde_yaml::to_string(&registration).unwrap_or_default()
112}
113
114fn generate_token() -> String {
115    use std::collections::hash_map::RandomState;
116    use std::hash::{BuildHasher, Hasher};
117
118    let state = RandomState::new();
119    let mut hasher = state.build_hasher();
120    hasher.write_u64(
121        std::time::SystemTime::now()
122            .duration_since(std::time::UNIX_EPOCH)
123            .unwrap_or_default()
124            .as_nanos() as u64,
125    );
126    format!("{:x}", hasher.finish())
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[test]
134    fn generate_registration_produces_valid_yaml() {
135        let yaml = generate_registration("test", "http://localhost:8008", "example.org");
136        assert!(yaml.contains("id: test"));
137        assert!(yaml.contains("as_token:"));
138        assert!(yaml.contains("hs_token:"));
139        assert!(yaml.contains("protocols:"));
140    }
141}