Add basic administration via telegram

This commit is contained in:
asonix 2022-11-02 17:58:52 -05:00
parent 279ac9400d
commit bd172753fb
6 changed files with 440 additions and 5 deletions

320
Cargo.lock generated
View file

@ -279,7 +279,7 @@ checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
[[package]]
name = "ap-relay"
version = "0.3.28"
version = "0.3.29"
dependencies = [
"activitystreams",
"activitystreams-ext",
@ -311,6 +311,7 @@ dependencies = [
"sha2",
"signature",
"sled",
"teloxide",
"thiserror",
"tokio",
"toml",
@ -325,6 +326,19 @@ dependencies = [
"uuid",
]
[[package]]
name = "aquamarine"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a941c39708478e8eea39243b5983f1c42d2717b3620ee91f4a52115fd02ac43f"
dependencies = [
"itertools 0.9.0",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "arc-swap"
version = "1.5.1"
@ -590,6 +604,16 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "clap"
version = "4.0.18"
@ -754,6 +778,41 @@ dependencies = [
"typenum",
]
[[package]]
name = "darling"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "dashmap"
version = "5.4.0"
@ -814,6 +873,15 @@ version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
name = "dptree"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d81175dab5ec79c30e0576df2ed2c244e1721720c302000bb321b107e82e265c"
dependencies = [
"futures",
]
[[package]]
name = "either"
version = "1.8.0"
@ -829,6 +897,16 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "erasable"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f11890ce181d47a64e5d1eb4b6caba0e7bae911a356723740d058a5d0340b7d"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "event-listener"
version = "2.5.3"
@ -903,6 +981,7 @@ checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
@ -1183,6 +1262,19 @@ dependencies = [
"want",
]
[[package]]
name = "hyper-rustls"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac"
dependencies = [
"http",
"hyper",
"rustls",
"tokio",
"tokio-rustls",
]
[[package]]
name = "hyper-timeout"
version = "0.4.1"
@ -1195,6 +1287,12 @@ dependencies = [
"tokio-io-timeout",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "0.3.0"
@ -1224,6 +1322,12 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "ipnet"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b"
[[package]]
name = "iri-string"
version = "0.5.6"
@ -1233,6 +1337,15 @@ dependencies = [
"serde",
]
[[package]]
name = "itertools"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
dependencies = [
"either",
]
[[package]]
name = "itertools"
version = "0.10.5"
@ -1415,6 +1528,16 @@ version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "mime_guess"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
@ -1448,6 +1571,12 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
[[package]]
name = "never"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91"
[[package]]
name = "new_debug_unreachable"
version = "1.0.4"
@ -1977,7 +2106,7 @@ checksum = "7f835c582e6bd972ba8347313300219fed5bfa52caf175298d860b61ff6069bb"
dependencies = [
"bytes",
"heck",
"itertools",
"itertools 0.10.5",
"lazy_static",
"log",
"multimap",
@ -1996,7 +2125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7345d5f0e08c0536d7ac7229952590239e77abf0a0100a1b1d890add6ea96364"
dependencies = [
"anyhow",
"itertools",
"itertools 0.10.5",
"proc-macro2",
"quote",
"syn",
@ -2051,6 +2180,15 @@ dependencies = [
"getrandom",
]
[[package]]
name = "rc-box"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0690759eabf094030c2cdabc25ade1395bac02210d920d655053c1d49583fd8"
dependencies = [
"erasable",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
@ -2095,6 +2233,47 @@ dependencies = [
"winapi",
]
[[package]]
name = "reqwest"
version = "0.11.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "431949c384f4e2ae07605ccaa56d1d9d2ecdb5cadd4f9577ccfab29f2e5149fc"
dependencies = [
"base64",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"hyper",
"hyper-rustls",
"ipnet",
"js-sys",
"log",
"mime",
"mime_guess",
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls",
"rustls-pemfile",
"serde",
"serde_json",
"serde_urlencoded",
"tokio",
"tokio-rustls",
"tokio-util",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"webpki-roots",
"winreg",
]
[[package]]
name = "ring"
version = "0.16.20"
@ -2180,7 +2359,7 @@ checksum = "85517cd381cf0c34694881d8aaf173107c6af7670e66cec18d7a1a8bfce3b758"
dependencies = [
"base64",
"bytecount",
"itertools",
"itertools 0.10.5",
"md5",
"mime",
"nom",
@ -2218,6 +2397,15 @@ dependencies = [
"webpki",
]
[[package]]
name = "rustls-pemfile"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55"
dependencies = [
"base64",
]
[[package]]
name = "ryu"
version = "1.0.11"
@ -2289,6 +2477,18 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_with_macros"
version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "sha1"
version = "0.10.5"
@ -2457,6 +2657,87 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20518fe4a4c9acf048008599e464deb21beeae3d3578418951a189c235a7a9a8"
[[package]]
name = "take_mut"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60"
[[package]]
name = "takecell"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20f34339676cdcab560c9a82300c4c2581f68b9369aedf0fae86f2ff9565ff3e"
[[package]]
name = "teloxide"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94734a391eb4f3b6172b285fc10593192f9bdb4c8a377075cff063d967f0e43b"
dependencies = [
"aquamarine",
"bytes",
"derive_more",
"dptree",
"futures",
"log",
"mime",
"pin-project",
"serde",
"serde_json",
"serde_with_macros",
"teloxide-core",
"teloxide-macros",
"thiserror",
"tokio",
"tokio-stream",
"tokio-util",
"url",
]
[[package]]
name = "teloxide-core"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9243a720aa9bddda324a7f90b4ab42887425524bf4d5d24a56b50bccb984b7c4"
dependencies = [
"bitflags",
"bytes",
"chrono",
"derive_more",
"either",
"futures",
"log",
"mime",
"never",
"once_cell",
"pin-project",
"rc-box",
"reqwest",
"serde",
"serde_json",
"serde_with_macros",
"take_mut",
"takecell",
"thiserror",
"tokio",
"tokio-util",
"url",
"uuid",
]
[[package]]
name = "teloxide-macros"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40a5fc46d9004706ee23e3b73a0f53518f28498f7297813fa9a505a29638ffd6"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tempfile"
version = "3.3.0"
@ -2888,6 +3169,15 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.8"
@ -2924,6 +3214,7 @@ dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
"serde",
]
[[package]]
@ -2995,6 +3286,18 @@ dependencies = [
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.83"
@ -3152,6 +3455,15 @@ version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
[[package]]
name = "winreg"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"winapi",
]
[[package]]
name = "yaml-rust"
version = "0.4.5"

View file

@ -1,7 +1,7 @@
[package]
name = "ap-relay"
description = "A simple activitypub relay"
version = "0.3.28"
version = "0.3.29"
authors = ["asonix <asonix@asonix.dog>"]
license = "AGPL-3.0"
readme = "README.md"
@ -48,6 +48,11 @@ serde_json = "1.0"
sha2 = { version = "0.10", features = ["oid"] }
signature = "1.6.4"
sled = "0.34.7"
teloxide = { version = "0.11.1", default-features = false, features = [
"ctrlc_handler",
"macros",
"rustls",
] }
thiserror = "1.0"
tracing = "0.1"
tracing-awc = "0.1.6"

View file

@ -91,6 +91,8 @@ PRETTY_LOG=false
PUBLISH_BLOCKS=true
SLED_PATH=./sled/db-0.34
OPENTELEMETRY_URL=localhost:4317
TELEGRAM_TOKEN=secret
TELEGRAM_ADMIN_HANDLE=your_handle
```
#### Descriptions
@ -116,6 +118,10 @@ Where to store the on-disk database of connected servers. This defaults to `./sl
The URL to the source code for the relay. This defaults to `https://git.asonix.dog/asonix/relay`, but should be changed if you're running a fork hosted elsewhere.
##### `OPENTELEMETRY_URL`
A URL for exporting opentelemetry spans. This is mostly useful for debugging. There is no default, since most people probably don't run an opentelemetry collector.
##### `TELEGRAM_TOKEN`
A Telegram Bot Token for running the relay administration bot. There is no default.
##### `TELEGRAM_ADMIN_HANDLE`
The handle of the telegram user allowed to administer the relay. There is no default.
### Subscribing
Mastodon admins can subscribe to this relay by adding the `/inbox` route to their relay settings.

View file

@ -30,6 +30,8 @@ pub(crate) struct ParsedConfig {
sled_path: PathBuf,
source_repo: IriString,
opentelemetry_url: Option<IriString>,
telegram_token: Option<String>,
telegram_admin_handle: Option<String>,
}
#[derive(Clone)]
@ -45,6 +47,8 @@ pub struct Config {
sled_path: PathBuf,
source_repo: IriString,
opentelemetry_url: Option<IriString>,
telegram_token: Option<String>,
telegram_admin_handle: Option<String>,
}
#[derive(Debug)]
@ -78,6 +82,8 @@ impl std::fmt::Debug for Config {
"opentelemetry_url",
&self.opentelemetry_url.as_ref().map(|url| url.to_string()),
)
.field("telegram_token", &"[redacted]")
.field("telegram_admin_handle", &self.telegram_admin_handle)
.finish()
}
}
@ -96,6 +102,8 @@ impl Config {
.set_default("sled_path", "./sled/db-0-34")?
.set_default("source_repo", "https://git.asonix.dog/asonix/relay")?
.set_default("opentelemetry_url", None as Option<&str>)?
.set_default("telegram_token", None as Option<&str>)?
.set_default("telegram_admin_handle", None as Option<&str>)?
.add_source(Environment::default())
.build()?;
@ -116,6 +124,8 @@ impl Config {
sled_path: config.sled_path,
source_repo: config.source_repo,
opentelemetry_url: config.opentelemetry_url,
telegram_token: config.telegram_token,
telegram_admin_handle: config.telegram_admin_handle,
})
}
@ -225,6 +235,13 @@ impl Config {
self.opentelemetry_url.as_ref()
}
pub(crate) fn telegram_info(&self) -> Option<(&str, &str)> {
self.telegram_token.as_deref().and_then(|token| {
let handle = self.telegram_admin_handle.as_deref()?;
Some((token, handle))
})
}
pub(crate) fn generate_url(&self, kind: UrlKind) -> IriString {
self.do_generate_url(kind).expect("Generated valid IRI")
}

View file

@ -22,6 +22,7 @@ mod jobs;
mod middleware;
mod requests;
mod routes;
mod telegram;
use self::{
args::Args,
@ -118,6 +119,10 @@ async fn main() -> Result<(), anyhow::Error> {
let (manager, job_server) =
create_workers(state.clone(), actors.clone(), media.clone(), config.clone());
if let Some((token, admin_handle)) = config.telegram_info() {
telegram::start(admin_handle.to_owned(), db.clone(), token);
}
let bind_address = config.bind_address();
HttpServer::new(move || {
App::new()

90
src/telegram.rs Normal file
View file

@ -0,0 +1,90 @@
use crate::db::Db;
use activitystreams::iri_string::types::IriString;
use std::sync::Arc;
use teloxide::{prelude::*, utils::command::BotCommands};
#[derive(BotCommands, Clone)]
#[command(
rename_rule = "lowercase",
description = "These commands are for administering AodeRelay"
)]
enum Command {
#[command(description = "Display this text.")]
Help,
#[command(description = "Block a domain from the relay.")]
Block { domain: IriString },
#[command(description = "Unblock a domain from the relay.")]
Unblock { domain: IriString },
#[command(description = "Allow a domain to connect to the relay (for RESTRICTED_MODE)")]
Allow { domain: IriString },
#[command(description = "Disallow a domain to connect to the relay (for RESTRICTED_MODE)")]
Disallow { domain: IriString },
}
pub(crate) fn start(admin_handle: String, db: Db, token: &str) {
let bot = Bot::new(token);
let admin_handle = Arc::new(admin_handle);
actix_rt::spawn(async move {
teloxide::repl(bot, move |bot: Bot, msg: Message, cmd: Command| {
let admin_handle = admin_handle.clone();
let db = db.clone();
async move {
if !is_admin(&admin_handle, &msg) {
return Ok(());
}
answer(bot, msg, cmd, db).await
}
})
.await;
});
}
fn is_admin(admin_handle: &str, message: &Message) -> bool {
message
.from()
.and_then(|user| user.username.as_deref())
.map(|username| username == admin_handle)
.unwrap_or(false)
}
async fn answer(bot: Bot, msg: Message, cmd: Command, db: Db) -> ResponseResult<()> {
match cmd {
Command::Help => {
bot.send_message(msg.chat.id, Command::descriptions().to_string())
.await?;
}
Command::Block { domain } => {
if db.add_blocks(vec![domain.to_string()]).await.is_ok() {
bot.send_message(msg.chat.id, format!("{} has been blocked", domain))
.await?;
}
}
Command::Unblock { domain } => {
if db.remove_blocks(vec![domain.to_string()]).await.is_ok() {
bot.send_message(msg.chat.id, format!("{} has been unblocked", domain))
.await?;
}
}
Command::Allow { domain } => {
if db.add_allows(vec![domain.to_string()]).await.is_ok() {
bot.send_message(msg.chat.id, format!("{} has been allowed", domain))
.await?;
}
}
Command::Disallow { domain } => {
if db.remove_allows(vec![domain.to_string()]).await.is_ok() {
bot.send_message(msg.chat.id, format!("{} has been disallwoed", domain))
.await?;
}
}
}
Ok(())
}