mirror of
https://gitlab.computer.surgery/matrix/grapevine.git
synced 2025-12-18 08:11:24 +01:00
rename conduit to grapevine
This commit is contained in:
parent
5619d7e318
commit
360e020b64
14 changed files with 153 additions and 152 deletions
|
|
@ -49,11 +49,11 @@ artifacts:
|
||||||
image: nixos/nix:2.20.4
|
image: nixos/nix:2.20.4
|
||||||
script:
|
script:
|
||||||
- ./bin/nix-build-and-cache .#static-x86_64-unknown-linux-musl
|
- ./bin/nix-build-and-cache .#static-x86_64-unknown-linux-musl
|
||||||
- cp result/bin/conduit x86_64-unknown-linux-musl
|
- cp result/bin/grapevine x86_64-unknown-linux-musl
|
||||||
|
|
||||||
# Since the OCI image package is based on the binary package, this has the
|
# Since the OCI image package is based on the binary package, this has the
|
||||||
# fun side effect of uploading the normal binary too. Conduit users who are
|
|
||||||
# deploying with Nix can leverage this fact by adding our binary cache to
|
# deploying with Nix can leverage this fact by adding our binary cache to
|
||||||
|
# fun side effect of uploading the normal binary too. Grapevine users who are
|
||||||
# their systems.
|
# their systems.
|
||||||
#
|
#
|
||||||
# Note that although we have an `oci-image-x86_64-unknown-linux-musl`
|
# Note that although we have an `oci-image-x86_64-unknown-linux-musl`
|
||||||
|
|
@ -63,7 +63,7 @@ artifacts:
|
||||||
- cp result oci-image-amd64.tar.gz
|
- cp result oci-image-amd64.tar.gz
|
||||||
|
|
||||||
- ./bin/nix-build-and-cache .#static-aarch64-unknown-linux-musl
|
- ./bin/nix-build-and-cache .#static-aarch64-unknown-linux-musl
|
||||||
- cp result/bin/conduit aarch64-unknown-linux-musl
|
- cp result/bin/grapevine aarch64-unknown-linux-musl
|
||||||
|
|
||||||
- ./bin/nix-build-and-cache .#oci-image-aarch64-unknown-linux-musl
|
- ./bin/nix-build-and-cache .#oci-image-aarch64-unknown-linux-musl
|
||||||
- cp result oci-image-arm64v8.tar.gz
|
- cp result oci-image-arm64v8.tar.gz
|
||||||
|
|
|
||||||
100
Cargo.lock
generated
100
Cargo.lock
generated
|
|
@ -375,56 +375,6 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "conduit"
|
|
||||||
version = "0.7.0"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"axum",
|
|
||||||
"axum-server",
|
|
||||||
"base64",
|
|
||||||
"bytes",
|
|
||||||
"clap",
|
|
||||||
"figment",
|
|
||||||
"futures-util",
|
|
||||||
"hmac",
|
|
||||||
"http",
|
|
||||||
"hyper",
|
|
||||||
"image",
|
|
||||||
"jsonwebtoken",
|
|
||||||
"lru-cache",
|
|
||||||
"nix",
|
|
||||||
"num_cpus",
|
|
||||||
"opentelemetry",
|
|
||||||
"opentelemetry-jaeger",
|
|
||||||
"parking_lot",
|
|
||||||
"rand",
|
|
||||||
"regex",
|
|
||||||
"reqwest",
|
|
||||||
"ring",
|
|
||||||
"ruma",
|
|
||||||
"rusqlite",
|
|
||||||
"rust-argon2",
|
|
||||||
"rust-rocksdb",
|
|
||||||
"sd-notify",
|
|
||||||
"serde",
|
|
||||||
"serde_html_form",
|
|
||||||
"serde_json",
|
|
||||||
"serde_yaml",
|
|
||||||
"sha-1",
|
|
||||||
"thiserror",
|
|
||||||
"thread_local",
|
|
||||||
"tikv-jemallocator",
|
|
||||||
"tokio",
|
|
||||||
"tower",
|
|
||||||
"tower-http",
|
|
||||||
"tracing",
|
|
||||||
"tracing-flame",
|
|
||||||
"tracing-opentelemetry",
|
|
||||||
"tracing-subscriber",
|
|
||||||
"trust-dns-resolver",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-oid"
|
name = "const-oid"
|
||||||
version = "0.9.6"
|
version = "0.9.6"
|
||||||
|
|
@ -837,6 +787,56 @@ version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "grapevine"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"axum",
|
||||||
|
"axum-server",
|
||||||
|
"base64",
|
||||||
|
"bytes",
|
||||||
|
"clap",
|
||||||
|
"figment",
|
||||||
|
"futures-util",
|
||||||
|
"hmac",
|
||||||
|
"http",
|
||||||
|
"hyper",
|
||||||
|
"image",
|
||||||
|
"jsonwebtoken",
|
||||||
|
"lru-cache",
|
||||||
|
"nix",
|
||||||
|
"num_cpus",
|
||||||
|
"opentelemetry",
|
||||||
|
"opentelemetry-jaeger",
|
||||||
|
"parking_lot",
|
||||||
|
"rand",
|
||||||
|
"regex",
|
||||||
|
"reqwest",
|
||||||
|
"ring",
|
||||||
|
"ruma",
|
||||||
|
"rusqlite",
|
||||||
|
"rust-argon2",
|
||||||
|
"rust-rocksdb",
|
||||||
|
"sd-notify",
|
||||||
|
"serde",
|
||||||
|
"serde_html_form",
|
||||||
|
"serde_json",
|
||||||
|
"serde_yaml",
|
||||||
|
"sha-1",
|
||||||
|
"thiserror",
|
||||||
|
"thread_local",
|
||||||
|
"tikv-jemallocator",
|
||||||
|
"tokio",
|
||||||
|
"tower",
|
||||||
|
"tower-http",
|
||||||
|
"tracing",
|
||||||
|
"tracing-flame",
|
||||||
|
"tracing-opentelemetry",
|
||||||
|
"tracing-subscriber",
|
||||||
|
"trust-dns-resolver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.24"
|
version = "0.3.24"
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ dbg_macro = "warn"
|
||||||
str_to_string = "warn"
|
str_to_string = "warn"
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "conduit"
|
name = "grapevine"
|
||||||
description = "A Matrix homeserver written in Rust"
|
description = "A Matrix homeserver written in Rust"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
version = "0.7.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See also `rust-toolchain.toml`
|
# See also `rust-toolchain.toml`
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
env = pkgs: {
|
env = pkgs: {
|
||||||
CONDUIT_VERSION_EXTRA = self.shortRev or self.dirtyShortRev;
|
GRAPEVINE_VERSION_EXTRA = self.shortRev or self.dirtyShortRev;
|
||||||
ROCKSDB_INCLUDE_DIR = "${rocksdb' pkgs}/include";
|
ROCKSDB_INCLUDE_DIR = "${rocksdb' pkgs}/include";
|
||||||
ROCKSDB_LIB_DIR = "${rocksdb' pkgs}/lib";
|
ROCKSDB_LIB_DIR = "${rocksdb' pkgs}/lib";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -265,7 +265,7 @@ pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<registe
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is the first real user, grant them admin privileges
|
// If this is the first real user, grant them admin privileges
|
||||||
// Note: the server user, @conduit:servername, is generated first
|
// Note: the server user, @grapevine:servername, is generated first
|
||||||
if !is_guest {
|
if !is_guest {
|
||||||
if let Some(admin_room) = services().admin.get_admin_room()? {
|
if let Some(admin_room) = services().admin.get_admin_room()? {
|
||||||
if services()
|
if services()
|
||||||
|
|
|
||||||
|
|
@ -245,7 +245,7 @@ where
|
||||||
if parts.uri.to_string().contains('@') {
|
if parts.uri.to_string().contains('@') {
|
||||||
warn!(
|
warn!(
|
||||||
"Request uri contained '@' character. Make sure your \
|
"Request uri contained '@' character. Make sure your \
|
||||||
reverse proxy gives Conduit the raw uri (apache: use \
|
reverse proxy gives Grapevine the raw uri (apache: use \
|
||||||
nocanon)"
|
nocanon)"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ use tracing::{debug, error, warn};
|
||||||
///
|
///
|
||||||
/// # Examples:
|
/// # Examples:
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # use conduit::api::server_server::FedDest;
|
/// # use grapevine::api::server_server::FedDest;
|
||||||
/// # fn main() -> Result<(), std::net::AddrParseError> {
|
/// # fn main() -> Result<(), std::net::AddrParseError> {
|
||||||
/// FedDest::Literal("198.51.100.3:8448".parse()?);
|
/// FedDest::Literal("198.51.100.3:8448".parse()?);
|
||||||
/// FedDest::Literal("[2001:db8::4:5]:443".parse()?);
|
/// FedDest::Literal("[2001:db8::4:5]:443".parse()?);
|
||||||
|
|
@ -529,7 +529,7 @@ pub async fn get_server_version_route(
|
||||||
) -> Result<get_server_version::v1::Response> {
|
) -> Result<get_server_version::v1::Response> {
|
||||||
Ok(get_server_version::v1::Response {
|
Ok(get_server_version::v1::Response {
|
||||||
server: Some(get_server_version::v1::Server {
|
server: Some(get_server_version::v1::Server {
|
||||||
name: Some("Conduit".to_owned()),
|
name: Some(env!("CARGO_PKG_NAME").to_owned()),
|
||||||
version: Some(env!("CARGO_PKG_VERSION").to_owned()),
|
version: Some(env!("CARGO_PKG_VERSION").to_owned()),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|
@ -1042,7 +1042,7 @@ pub async fn get_backfill_route(
|
||||||
let all_events = services()
|
let all_events = services()
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
.pdus_until(user_id!("@doesntmatter:conduit.rs"), &body.room_id, until)?
|
.pdus_until(user_id!("@doesntmatter:grapevine"), &body.room_id, until)?
|
||||||
.take(limit.try_into().unwrap());
|
.take(limit.try_into().unwrap());
|
||||||
|
|
||||||
let events = all_events
|
let events = all_events
|
||||||
|
|
@ -1379,7 +1379,7 @@ pub async fn create_join_event_template_route(
|
||||||
);
|
);
|
||||||
let state_lock = mutex_state.lock().await;
|
let state_lock = mutex_state.lock().await;
|
||||||
|
|
||||||
// TODO: Conduit does not implement restricted join rules yet, we always reject
|
// TODO: Grapevine does not implement restricted join rules yet, we always reject
|
||||||
let join_rules_event = services().rooms.state_accessor.room_state_get(
|
let join_rules_event = services().rooms.state_accessor.room_state_get(
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&StateEventType::RoomJoinRules,
|
&StateEventType::RoomJoinRules,
|
||||||
|
|
@ -1403,7 +1403,7 @@ pub async fn create_join_event_template_route(
|
||||||
) {
|
) {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::UnableToAuthorizeJoin,
|
ErrorKind::UnableToAuthorizeJoin,
|
||||||
"Conduit does not support restricted rooms yet.",
|
"Grapevine does not support restricted rooms yet.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1470,7 +1470,7 @@ async fn create_join_event(
|
||||||
.event_handler
|
.event_handler
|
||||||
.acl_check(sender_servername, room_id)?;
|
.acl_check(sender_servername, room_id)?;
|
||||||
|
|
||||||
// TODO: Conduit does not implement restricted join rules yet, we always reject
|
// TODO: Grapevine does not implement restricted join rules yet, we always reject
|
||||||
let join_rules_event = services().rooms.state_accessor.room_state_get(
|
let join_rules_event = services().rooms.state_accessor.room_state_get(
|
||||||
room_id,
|
room_id,
|
||||||
&StateEventType::RoomJoinRules,
|
&StateEventType::RoomJoinRules,
|
||||||
|
|
@ -1494,7 +1494,7 @@ async fn create_join_event(
|
||||||
) {
|
) {
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::UnableToAuthorizeJoin,
|
ErrorKind::UnableToAuthorizeJoin,
|
||||||
"Conduit does not support restricted rooms yet.",
|
"Grapevine does not support restricted rooms yet.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,13 @@ use clap::Parser;
|
||||||
|
|
||||||
/// Returns the current version of the crate with extra info if supplied
|
/// Returns the current version of the crate with extra info if supplied
|
||||||
///
|
///
|
||||||
/// Set the environment variable `CONDUIT_VERSION_EXTRA` to any UTF-8 string to
|
/// Set the environment variable `GRAPEVINE_VERSION_EXTRA` to any UTF-8 string to
|
||||||
/// include it in parenthesis after the SemVer version. A common value are git
|
/// include it in parenthesis after the SemVer version. A common value are git
|
||||||
/// commit hashes.
|
/// commit hashes.
|
||||||
fn version() -> String {
|
fn version() -> String {
|
||||||
let cargo_pkg_version = env!("CARGO_PKG_VERSION");
|
let cargo_pkg_version = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
match option_env!("CONDUIT_VERSION_EXTRA") {
|
match option_env!("GRAPEVINE_VERSION_EXTRA") {
|
||||||
Some(x) => format!("{} ({})", cargo_pkg_version, x),
|
Some(x) => format!("{} ({})", cargo_pkg_version, x),
|
||||||
None => cargo_pkg_version.to_owned(),
|
None => cargo_pkg_version.to_owned(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ impl Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
if was_deprecated {
|
if was_deprecated {
|
||||||
warn!("Read conduit documentation and check your configuration if any new configuration parameters should be adjusted");
|
warn!("Read grapevine documentation and check your configuration if any new configuration parameters should be adjusted");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ impl Engine {
|
||||||
|
|
||||||
impl KeyValueDatabaseEngine for Arc<Engine> {
|
impl KeyValueDatabaseEngine for Arc<Engine> {
|
||||||
fn open(config: &Config) -> Result<Self> {
|
fn open(config: &Config) -> Result<Self> {
|
||||||
let path = Path::new(&config.database_path).join("conduit.db");
|
let path = Path::new(&config.database_path).join("grapevine.db");
|
||||||
|
|
||||||
// calculates cache-size per permanent connection
|
// calculates cache-size per permanent connection
|
||||||
// 1. convert MB to KiB
|
// 1. convert MB to KiB
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,7 @@ impl KeyValueDatabase {
|
||||||
fn check_db_setup(config: &Config) -> Result<()> {
|
fn check_db_setup(config: &Config) -> Result<()> {
|
||||||
let path = Path::new(&config.database_path);
|
let path = Path::new(&config.database_path);
|
||||||
|
|
||||||
let sqlite_exists = path.join("conduit.db").exists();
|
let sqlite_exists = path.join("grapevine.db").exists();
|
||||||
let rocksdb_exists = path.join("IDENTITY").exists();
|
let rocksdb_exists = path.join("IDENTITY").exists();
|
||||||
|
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|
@ -375,14 +375,14 @@ impl KeyValueDatabase {
|
||||||
// Matrix resource ownership is based on the server name; changing it
|
// Matrix resource ownership is based on the server name; changing it
|
||||||
// requires recreating the database from scratch.
|
// requires recreating the database from scratch.
|
||||||
if services().users.count()? > 0 {
|
if services().users.count()? > 0 {
|
||||||
let conduit_user =
|
let grapevine_user =
|
||||||
UserId::parse_with_server_name("conduit", services().globals.server_name())
|
UserId::parse_with_server_name("grapevine", services().globals.server_name())
|
||||||
.expect("@conduit:server_name is valid");
|
.expect("@grapevine:server_name is valid");
|
||||||
|
|
||||||
if !services().users.exists(&conduit_user)? {
|
if !services().users.exists(&grapevine_user)? {
|
||||||
error!(
|
error!(
|
||||||
"The {} server user does not exist, and the database is not new.",
|
"The {} server user does not exist, and the database is not new.",
|
||||||
conduit_user
|
grapevine_user
|
||||||
);
|
);
|
||||||
return Err(Error::bad_database(
|
return Err(Error::bad_database(
|
||||||
"Cannot reuse an existing database after changing the server name, please delete the old one first."
|
"Cannot reuse an existing database after changing the server name, please delete the old one first."
|
||||||
|
|
@ -935,17 +935,17 @@ impl KeyValueDatabase {
|
||||||
|
|
||||||
services().admin.start_handler();
|
services().admin.start_handler();
|
||||||
|
|
||||||
// Set emergency access for the conduit user
|
// Set emergency access for the grapevine user
|
||||||
match set_emergency_access() {
|
match set_emergency_access() {
|
||||||
Ok(pwd_set) => {
|
Ok(pwd_set) => {
|
||||||
if pwd_set {
|
if pwd_set {
|
||||||
warn!("The Conduit account emergency password is set! Please unset it as soon as you finish admin account recovery!");
|
warn!("The Grapevine account emergency password is set! Please unset it as soon as you finish admin account recovery!");
|
||||||
services().admin.send_message(RoomMessageEventContent::text_plain("The Conduit account emergency password is set! Please unset it as soon as you finish admin account recovery!"));
|
services().admin.send_message(RoomMessageEventContent::text_plain("The Grapevine account emergency password is set! Please unset it as soon as you finish admin account recovery!"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(
|
error!(
|
||||||
"Could not set the configured emergency password for the conduit user: {}",
|
"Could not set the configured emergency password for the grapevine user: {}",
|
||||||
e
|
e
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -1013,24 +1013,25 @@ impl KeyValueDatabase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the emergency password and push rules for the @conduit account in case emergency password is set
|
/// Sets the emergency password and push rules for the @grapevine account in case emergency password is set
|
||||||
fn set_emergency_access() -> Result<bool> {
|
fn set_emergency_access() -> Result<bool> {
|
||||||
let conduit_user = UserId::parse_with_server_name("conduit", services().globals.server_name())
|
let grapevine_user =
|
||||||
.expect("@conduit:server_name is a valid UserId");
|
UserId::parse_with_server_name("grapevine", services().globals.server_name())
|
||||||
|
.expect("@grapevine:server_name is a valid UserId");
|
||||||
|
|
||||||
services().users.set_password(
|
services().users.set_password(
|
||||||
&conduit_user,
|
&grapevine_user,
|
||||||
services().globals.emergency_password().as_deref(),
|
services().globals.emergency_password().as_deref(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (ruleset, res) = match services().globals.emergency_password() {
|
let (ruleset, res) = match services().globals.emergency_password() {
|
||||||
Some(_) => (Ruleset::server_default(&conduit_user), Ok(true)),
|
Some(_) => (Ruleset::server_default(&grapevine_user), Ok(true)),
|
||||||
None => (Ruleset::new(), Ok(false)),
|
None => (Ruleset::new(), Ok(false)),
|
||||||
};
|
};
|
||||||
|
|
||||||
services().account_data.update(
|
services().account_data.update(
|
||||||
None,
|
None,
|
||||||
&conduit_user,
|
&grapevine_user,
|
||||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||||
&serde_json::to_value(&GlobalAccountDataEvent {
|
&serde_json::to_value(&GlobalAccountDataEvent {
|
||||||
content: PushRulesEventContent { global: ruleset },
|
content: PushRulesEventContent { global: ruleset },
|
||||||
|
|
|
||||||
21
src/main.rs
21
src/main.rs
|
|
@ -76,15 +76,14 @@ async fn main() {
|
||||||
clap::parse();
|
clap::parse();
|
||||||
|
|
||||||
// Initialize config
|
// Initialize config
|
||||||
let raw_config =
|
let raw_config = Figment::new()
|
||||||
Figment::new()
|
.merge(
|
||||||
.merge(
|
Toml::file(Env::var("GRAPEVINE_CONFIG").expect(
|
||||||
Toml::file(Env::var("CONDUIT_CONFIG").expect(
|
"The GRAPEVINE_CONFIG env var needs to be set. Example: /etc/grapevine.toml",
|
||||||
"The CONDUIT_CONFIG env var needs to be set. Example: /etc/conduit.toml",
|
))
|
||||||
))
|
.nested(),
|
||||||
.nested(),
|
)
|
||||||
)
|
.merge(Env::prefixed("GRAPEVINE_").global());
|
||||||
.merge(Env::prefixed("CONDUIT_").global());
|
|
||||||
|
|
||||||
let config = match raw_config.extract::<Config>() {
|
let config = match raw_config.extract::<Config>() {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
|
|
@ -100,7 +99,7 @@ async fn main() {
|
||||||
opentelemetry::global::set_text_map_propagator(opentelemetry_jaeger::Propagator::new());
|
opentelemetry::global::set_text_map_propagator(opentelemetry_jaeger::Propagator::new());
|
||||||
let tracer = opentelemetry_jaeger::new_agent_pipeline()
|
let tracer = opentelemetry_jaeger::new_agent_pipeline()
|
||||||
.with_auto_split_batch(true)
|
.with_auto_split_batch(true)
|
||||||
.with_service_name("conduit")
|
.with_service_name("grapevine")
|
||||||
.install_batch(opentelemetry::runtime::Tokio)
|
.install_batch(opentelemetry::runtime::Tokio)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
|
let telemetry = tracing_opentelemetry::layer().with_tracer(tracer);
|
||||||
|
|
@ -515,7 +514,7 @@ async fn initial_sync(_uri: Uri) -> impl IntoResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn it_works() -> &'static str {
|
async fn it_works() -> &'static str {
|
||||||
"Hello from Conduit!"
|
"Hello from Grapevine!"
|
||||||
}
|
}
|
||||||
|
|
||||||
trait RouterExt {
|
trait RouterExt {
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ use super::pdu::PduBuilder;
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
#[cfg_attr(test, derive(Debug))]
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(name = "@conduit:server.name:", version = env!("CARGO_PKG_VERSION"))]
|
#[command(name = "@grapevine:server.name:", version = env!("CARGO_PKG_VERSION"))]
|
||||||
enum AdminCommand {
|
enum AdminCommand {
|
||||||
#[command(verbatim_doc_comment)]
|
#[command(verbatim_doc_comment)]
|
||||||
/// Register an appservice using its registration YAML
|
/// Register an appservice using its registration YAML
|
||||||
|
|
@ -128,7 +128,7 @@ enum AdminCommand {
|
||||||
/// # ```
|
/// # ```
|
||||||
ParsePdu,
|
ParsePdu,
|
||||||
|
|
||||||
/// Retrieve and print a PDU by ID from the Conduit database
|
/// Retrieve and print a PDU by ID from the Grapevine database
|
||||||
GetPdu {
|
GetPdu {
|
||||||
/// An event ID (a $ followed by the base64 reference hash)
|
/// An event ID (a $ followed by the base64 reference hash)
|
||||||
event_id: Box<EventId>,
|
event_id: Box<EventId>,
|
||||||
|
|
@ -137,10 +137,10 @@ enum AdminCommand {
|
||||||
/// Print database memory usage statistics
|
/// Print database memory usage statistics
|
||||||
MemoryUsage,
|
MemoryUsage,
|
||||||
|
|
||||||
/// Clears all of Conduit's database caches with index smaller than the amount
|
/// Clears all of Grapevine's database caches with index smaller than the amount
|
||||||
ClearDatabaseCaches { amount: u32 },
|
ClearDatabaseCaches { amount: u32 },
|
||||||
|
|
||||||
/// Clears all of Conduit's service caches with index smaller than the amount
|
/// Clears all of Grapevine's service caches with index smaller than the amount
|
||||||
ClearServiceCaches { amount: u32 },
|
ClearServiceCaches { amount: u32 },
|
||||||
|
|
||||||
/// Show configuration values
|
/// Show configuration values
|
||||||
|
|
@ -212,10 +212,11 @@ impl Service {
|
||||||
// TODO: Use futures when we have long admin commands
|
// TODO: Use futures when we have long admin commands
|
||||||
//let mut futures = FuturesUnordered::new();
|
//let mut futures = FuturesUnordered::new();
|
||||||
|
|
||||||
let conduit_user = UserId::parse(format!("@conduit:{}", services().globals.server_name()))
|
let grapevine_user =
|
||||||
.expect("@conduit:server_name is valid");
|
UserId::parse(format!("@grapevine:{}", services().globals.server_name()))
|
||||||
|
.expect("@grapevine:server_name is valid");
|
||||||
|
|
||||||
if let Ok(Some(conduit_room)) = services().admin.get_admin_room() {
|
if let Ok(Some(grapevine_room)) = services().admin.get_admin_room() {
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
Some(event) = receiver.recv() => {
|
Some(event) = receiver.recv() => {
|
||||||
|
|
@ -229,7 +230,7 @@ impl Service {
|
||||||
.roomid_mutex_state
|
.roomid_mutex_state
|
||||||
.write()
|
.write()
|
||||||
.await
|
.await
|
||||||
.entry(conduit_room.to_owned())
|
.entry(grapevine_room.to_owned())
|
||||||
.or_default(),
|
.or_default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -247,8 +248,8 @@ impl Service {
|
||||||
state_key: None,
|
state_key: None,
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&grapevine_user,
|
||||||
&conduit_room,
|
&grapevine_room,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
)
|
)
|
||||||
.await.unwrap();
|
.await.unwrap();
|
||||||
|
|
@ -306,7 +307,7 @@ impl Service {
|
||||||
|
|
||||||
// Parse chat messages from the admin room into an AdminCommand object
|
// Parse chat messages from the admin room into an AdminCommand object
|
||||||
fn parse_admin_command(&self, command_line: &str) -> std::result::Result<AdminCommand, String> {
|
fn parse_admin_command(&self, command_line: &str) -> std::result::Result<AdminCommand, String> {
|
||||||
// Note: argv[0] is `@conduit:servername:`, which is treated as the main command
|
// Note: argv[0] is `@grapevine:servername:`, which is treated as the main command
|
||||||
let mut argv: Vec<_> = command_line.split_whitespace().collect();
|
let mut argv: Vec<_> = command_line.split_whitespace().collect();
|
||||||
|
|
||||||
// Replace `help command` with `command --help`
|
// Replace `help command` with `command --help`
|
||||||
|
|
@ -566,10 +567,10 @@ impl Service {
|
||||||
if !services().users.exists(&user_id)?
|
if !services().users.exists(&user_id)?
|
||||||
|| user_id
|
|| user_id
|
||||||
== UserId::parse_with_server_name(
|
== UserId::parse_with_server_name(
|
||||||
"conduit",
|
"grapevine",
|
||||||
services().globals.server_name(),
|
services().globals.server_name(),
|
||||||
)
|
)
|
||||||
.expect("conduit user exists")
|
.expect("grapevine user exists")
|
||||||
{
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(RoomMessageEventContent::text_plain(
|
||||||
"The specified user does not exist!",
|
"The specified user does not exist!",
|
||||||
|
|
@ -854,13 +855,13 @@ impl Service {
|
||||||
|
|
||||||
// Utility to turn clap's `--help` text to HTML.
|
// Utility to turn clap's `--help` text to HTML.
|
||||||
fn usage_to_html(&self, text: &str, server_name: &ServerName) -> String {
|
fn usage_to_html(&self, text: &str, server_name: &ServerName) -> String {
|
||||||
// Replace `@conduit:servername:-subcmdname` with `@conduit:servername: subcmdname`
|
// Replace `@grapevine:servername:-subcmdname` with `@grapevine:servername: subcmdname`
|
||||||
let text = text.replace(
|
let text = text.replace(
|
||||||
&format!("@conduit:{server_name}:-"),
|
&format!("@grapevine:{server_name}:-"),
|
||||||
&format!("@conduit:{server_name}: "),
|
&format!("@grapevine:{server_name}: "),
|
||||||
);
|
);
|
||||||
|
|
||||||
// For the conduit admin room, subcommands become main commands
|
// For the grapevine admin room, subcommands become main commands
|
||||||
let text = text.replace("SUBCOMMAND", "COMMAND");
|
let text = text.replace("SUBCOMMAND", "COMMAND");
|
||||||
let text = text.replace("subcommand", "command");
|
let text = text.replace("subcommand", "command");
|
||||||
|
|
||||||
|
|
@ -914,7 +915,7 @@ impl Service {
|
||||||
// Improve the usage section
|
// Improve the usage section
|
||||||
let text = if command_body.is_empty() {
|
let text = if command_body.is_empty() {
|
||||||
// Wrap the usage line in code tags
|
// Wrap the usage line in code tags
|
||||||
let re = Regex::new("(?m)^USAGE:\n (@conduit:.*)$")
|
let re = Regex::new("(?m)^USAGE:\n (@grapevine:.*)$")
|
||||||
.expect("Regex compilation should not fail");
|
.expect("Regex compilation should not fail");
|
||||||
re.replace_all(&text, "USAGE:\n<code>$1</code>").to_string()
|
re.replace_all(&text, "USAGE:\n<code>$1</code>").to_string()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -935,7 +936,7 @@ impl Service {
|
||||||
|
|
||||||
/// Create the admin room.
|
/// Create the admin room.
|
||||||
///
|
///
|
||||||
/// Users in this room are considered admins by conduit, and the room can be
|
/// Users in this room are considered admins by grapevine, and the room can be
|
||||||
/// used to issue admin commands by talking to the server user inside it.
|
/// used to issue admin commands by talking to the server user inside it.
|
||||||
pub(crate) async fn create_admin_room(&self) -> Result<()> {
|
pub(crate) async fn create_admin_room(&self) -> Result<()> {
|
||||||
let room_id = RoomId::new(services().globals.server_name());
|
let room_id = RoomId::new(services().globals.server_name());
|
||||||
|
|
@ -954,11 +955,11 @@ impl Service {
|
||||||
let state_lock = mutex_state.lock().await;
|
let state_lock = mutex_state.lock().await;
|
||||||
|
|
||||||
// Create a user for the server
|
// Create a user for the server
|
||||||
let conduit_user =
|
let grapevine_user =
|
||||||
UserId::parse_with_server_name("conduit", services().globals.server_name())
|
UserId::parse_with_server_name("grapevine", services().globals.server_name())
|
||||||
.expect("@conduit:server_name is valid");
|
.expect("@grapevine:server_name is valid");
|
||||||
|
|
||||||
services().users.create(&conduit_user, None)?;
|
services().users.create(&grapevine_user, None)?;
|
||||||
|
|
||||||
let room_version = services().globals.default_room_version();
|
let room_version = services().globals.default_room_version();
|
||||||
let mut content = match room_version {
|
let mut content = match room_version {
|
||||||
|
|
@ -971,7 +972,7 @@ impl Service {
|
||||||
| RoomVersionId::V7
|
| RoomVersionId::V7
|
||||||
| RoomVersionId::V8
|
| RoomVersionId::V8
|
||||||
| RoomVersionId::V9
|
| RoomVersionId::V9
|
||||||
| RoomVersionId::V10 => RoomCreateEventContent::new_v1(conduit_user.clone()),
|
| RoomVersionId::V10 => RoomCreateEventContent::new_v1(grapevine_user.clone()),
|
||||||
RoomVersionId::V11 => RoomCreateEventContent::new_v11(),
|
RoomVersionId::V11 => RoomCreateEventContent::new_v11(),
|
||||||
_ => unreachable!("Validity of room version already checked"),
|
_ => unreachable!("Validity of room version already checked"),
|
||||||
};
|
};
|
||||||
|
|
@ -991,13 +992,13 @@ impl Service {
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some("".to_owned()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&grapevine_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// 2. Make conduit bot join
|
// 2. Make grapevine bot join
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
.timeline
|
.timeline
|
||||||
|
|
@ -1016,10 +1017,10 @@ impl Service {
|
||||||
})
|
})
|
||||||
.expect("event is valid, we just created it"),
|
.expect("event is valid, we just created it"),
|
||||||
unsigned: None,
|
unsigned: None,
|
||||||
state_key: Some(conduit_user.to_string()),
|
state_key: Some(grapevine_user.to_string()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&grapevine_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
)
|
)
|
||||||
|
|
@ -1027,7 +1028,7 @@ impl Service {
|
||||||
|
|
||||||
// 3. Power levels
|
// 3. Power levels
|
||||||
let mut users = BTreeMap::new();
|
let mut users = BTreeMap::new();
|
||||||
users.insert(conduit_user.clone(), 100.into());
|
users.insert(grapevine_user.clone(), 100.into());
|
||||||
|
|
||||||
services()
|
services()
|
||||||
.rooms
|
.rooms
|
||||||
|
|
@ -1044,7 +1045,7 @@ impl Service {
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some("".to_owned()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&grapevine_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
)
|
)
|
||||||
|
|
@ -1063,7 +1064,7 @@ impl Service {
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some("".to_owned()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&grapevine_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
)
|
)
|
||||||
|
|
@ -1084,7 +1085,7 @@ impl Service {
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some("".to_owned()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&grapevine_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
)
|
)
|
||||||
|
|
@ -1105,7 +1106,7 @@ impl Service {
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some("".to_owned()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&grapevine_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
)
|
)
|
||||||
|
|
@ -1125,7 +1126,7 @@ impl Service {
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some("".to_owned()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&grapevine_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
)
|
)
|
||||||
|
|
@ -1145,7 +1146,7 @@ impl Service {
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some("".to_owned()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&grapevine_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
)
|
)
|
||||||
|
|
@ -1171,7 +1172,7 @@ impl Service {
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some("".to_owned()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&grapevine_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
)
|
)
|
||||||
|
|
@ -1197,9 +1198,9 @@ impl Service {
|
||||||
.resolve_local_alias(&admin_room_alias)
|
.resolve_local_alias(&admin_room_alias)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invite the user to the conduit admin room.
|
/// Invite the user to the grapevine admin room.
|
||||||
///
|
///
|
||||||
/// In conduit, this is equivalent to granting admin privileges.
|
/// In grapevine, this is equivalent to granting admin privileges.
|
||||||
pub(crate) async fn make_user_admin(
|
pub(crate) async fn make_user_admin(
|
||||||
&self,
|
&self,
|
||||||
user_id: &UserId,
|
user_id: &UserId,
|
||||||
|
|
@ -1218,9 +1219,9 @@ impl Service {
|
||||||
let state_lock = mutex_state.lock().await;
|
let state_lock = mutex_state.lock().await;
|
||||||
|
|
||||||
// Use the server user to grant the new admin's power level
|
// Use the server user to grant the new admin's power level
|
||||||
let conduit_user =
|
let grapevine_user =
|
||||||
UserId::parse_with_server_name("conduit", services().globals.server_name())
|
UserId::parse_with_server_name("grapevine", services().globals.server_name())
|
||||||
.expect("@conduit:server_name is valid");
|
.expect("@grapevine:server_name is valid");
|
||||||
|
|
||||||
// Invite and join the real user
|
// Invite and join the real user
|
||||||
services()
|
services()
|
||||||
|
|
@ -1244,7 +1245,7 @@ impl Service {
|
||||||
state_key: Some(user_id.to_string()),
|
state_key: Some(user_id.to_string()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&grapevine_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
)
|
)
|
||||||
|
|
@ -1278,7 +1279,7 @@ impl Service {
|
||||||
|
|
||||||
// Set power level
|
// Set power level
|
||||||
let mut users = BTreeMap::new();
|
let mut users = BTreeMap::new();
|
||||||
users.insert(conduit_user.to_owned(), 100.into());
|
users.insert(grapevine_user.to_owned(), 100.into());
|
||||||
users.insert(user_id.to_owned(), 100.into());
|
users.insert(user_id.to_owned(), 100.into());
|
||||||
|
|
||||||
services()
|
services()
|
||||||
|
|
@ -1296,7 +1297,7 @@ impl Service {
|
||||||
state_key: Some("".to_owned()),
|
state_key: Some("".to_owned()),
|
||||||
redacts: None,
|
redacts: None,
|
||||||
},
|
},
|
||||||
&conduit_user,
|
&grapevine_user,
|
||||||
&room_id,
|
&room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ pub struct Service {
|
||||||
impl Service {
|
impl Service {
|
||||||
#[tracing::instrument(skip(self))]
|
#[tracing::instrument(skip(self))]
|
||||||
pub fn first_pdu_in_room(&self, room_id: &RoomId) -> Result<Option<Arc<PduEvent>>> {
|
pub fn first_pdu_in_room(&self, room_id: &RoomId) -> Result<Option<Arc<PduEvent>>> {
|
||||||
self.all_pdus(user_id!("@doesntmatter:conduit.rs"), room_id)?
|
self.all_pdus(user_id!("@doesntmatter:grapevine"), room_id)?
|
||||||
.next()
|
.next()
|
||||||
.map(|o| o.map(|(_, p)| Arc::new(p)))
|
.map(|o| o.map(|(_, p)| Arc::new(p)))
|
||||||
.transpose()
|
.transpose()
|
||||||
|
|
@ -485,20 +485,20 @@ impl Service {
|
||||||
.search
|
.search
|
||||||
.index_pdu(shortroomid, &pdu_id, &body)?;
|
.index_pdu(shortroomid, &pdu_id, &body)?;
|
||||||
|
|
||||||
let server_user = format!("@conduit:{}", services().globals.server_name());
|
let server_user = format!("@grapevine:{}", services().globals.server_name());
|
||||||
|
|
||||||
let to_conduit = body.starts_with(&format!("{server_user}: "))
|
let to_grapevine = body.starts_with(&format!("{server_user}: "))
|
||||||
|| body.starts_with(&format!("{server_user} "))
|
|| body.starts_with(&format!("{server_user} "))
|
||||||
|| body == format!("{server_user}:")
|
|| body == format!("{server_user}:")
|
||||||
|| body == server_user;
|
|| body == server_user;
|
||||||
|
|
||||||
// This will evaluate to false if the emergency password is set up so that
|
// This will evaluate to false if the emergency password is set up so that
|
||||||
// the administrator can execute commands as conduit
|
// the administrator can execute commands as grapevine
|
||||||
let from_conduit = pdu.sender == server_user
|
let from_grapevine = pdu.sender == server_user
|
||||||
&& services().globals.emergency_password().is_none();
|
&& services().globals.emergency_password().is_none();
|
||||||
|
|
||||||
if let Some(admin_room) = services().admin.get_admin_room()? {
|
if let Some(admin_room) = services().admin.get_admin_room()? {
|
||||||
if to_conduit && !from_conduit && admin_room == pdu.room_id {
|
if to_grapevine && !from_grapevine && admin_room == pdu.room_id {
|
||||||
services().admin.process_message(body);
|
services().admin.process_message(body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -838,16 +838,16 @@ impl Service {
|
||||||
.filter(|v| v.starts_with('@'))
|
.filter(|v| v.starts_with('@'))
|
||||||
.unwrap_or(sender.as_str());
|
.unwrap_or(sender.as_str());
|
||||||
let server_name = services().globals.server_name();
|
let server_name = services().globals.server_name();
|
||||||
let server_user = format!("@conduit:{}", server_name);
|
let server_user = format!("@grapevine:{}", server_name);
|
||||||
let content = serde_json::from_str::<ExtractMembership>(pdu.content.get())
|
let content = serde_json::from_str::<ExtractMembership>(pdu.content.get())
|
||||||
.map_err(|_| Error::bad_database("Invalid content in pdu."))?;
|
.map_err(|_| Error::bad_database("Invalid content in pdu."))?;
|
||||||
|
|
||||||
if content.membership == MembershipState::Leave {
|
if content.membership == MembershipState::Leave {
|
||||||
if target == server_user {
|
if target == server_user {
|
||||||
warn!("Conduit user cannot leave from admins room");
|
warn!("Grapevine user cannot leave from admins room");
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
"Conduit user cannot leave from admins room.",
|
"Grapevine user cannot leave from admins room.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -870,10 +870,10 @@ impl Service {
|
||||||
|
|
||||||
if content.membership == MembershipState::Ban && pdu.state_key().is_some() {
|
if content.membership == MembershipState::Ban && pdu.state_key().is_some() {
|
||||||
if target == server_user {
|
if target == server_user {
|
||||||
warn!("Conduit user cannot be banned in admins room");
|
warn!("Grapevine user cannot be banned in admins room");
|
||||||
return Err(Error::BadRequest(
|
return Err(Error::BadRequest(
|
||||||
ErrorKind::Forbidden,
|
ErrorKind::Forbidden,
|
||||||
"Conduit user cannot be banned in admins room.",
|
"Grapevine user cannot be banned in admins room.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1102,7 +1102,7 @@ impl Service {
|
||||||
#[tracing::instrument(skip(self, room_id))]
|
#[tracing::instrument(skip(self, room_id))]
|
||||||
pub async fn backfill_if_required(&self, room_id: &RoomId, from: PduCount) -> Result<()> {
|
pub async fn backfill_if_required(&self, room_id: &RoomId, from: PduCount) -> Result<()> {
|
||||||
let first_pdu = self
|
let first_pdu = self
|
||||||
.all_pdus(user_id!("@doesntmatter:conduit.rs"), room_id)?
|
.all_pdus(user_id!("@doesntmatter:grapevine"), room_id)?
|
||||||
.next()
|
.next()
|
||||||
.expect("Room is not empty")?;
|
.expect("Room is not empty")?;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue