matrix_bridge_telegram/
cli.rs1use 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(®istration).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}