From bcd1fe18561cbda0f26f53464325fccd177e42ad Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timo=20K=C3=B6sters?= <timo@koesters.xyz>
Date: Mon, 5 Oct 2020 22:19:22 +0200
Subject: [PATCH] feat: admin room

---
 Cargo.lock                      |  15 -
 src/client_server/account.rs    | 301 +++++++++++++++--
 src/client_server/membership.rs | 196 ++++++-----
 src/client_server/message.rs    |  43 ++-
 src/client_server/profile.rs    | 140 ++++----
 src/client_server/redact.rs     |  37 +--
 src/client_server/room.rs       | 556 +++++++++++++++-----------------
 src/client_server/state.rs      |  31 +-
 src/database/rooms.rs           |  49 ++-
 src/database/users.rs           |   5 +
 src/main.rs                     |   1 +
 src/server_server.rs            |  64 +++-
 12 files changed, 864 insertions(+), 574 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index ed20ca37..6571f7cd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1547,7 +1547,6 @@ dependencies = [
 [[package]]
 name = "ruma"
 version = "0.0.1"
-source = "git+https://github.com/timokoesters/ruma?branch=timo-fed-fixes#47fab87325b71b7f6c2fb3cd276d1f813e42abf7"
 dependencies = [
  "ruma-api",
  "ruma-appservice-api",
@@ -1563,7 +1562,6 @@ dependencies = [
 [[package]]
 name = "ruma-api"
 version = "0.17.0-alpha.1"
-source = "git+https://github.com/timokoesters/ruma?branch=timo-fed-fixes#47fab87325b71b7f6c2fb3cd276d1f813e42abf7"
 dependencies = [
  "http",
  "percent-encoding",
@@ -1578,7 +1576,6 @@ dependencies = [
 [[package]]
 name = "ruma-api-macros"
 version = "0.17.0-alpha.1"
-source = "git+https://github.com/timokoesters/ruma?branch=timo-fed-fixes#47fab87325b71b7f6c2fb3cd276d1f813e42abf7"
 dependencies = [
  "proc-macro-crate",
  "proc-macro2",
@@ -1589,7 +1586,6 @@ dependencies = [
 [[package]]
 name = "ruma-appservice-api"
 version = "0.2.0-alpha.1"
-source = "git+https://github.com/timokoesters/ruma?branch=timo-fed-fixes#47fab87325b71b7f6c2fb3cd276d1f813e42abf7"
 dependencies = [
  "ruma-api",
  "ruma-common",
@@ -1602,7 +1598,6 @@ dependencies = [
 [[package]]
 name = "ruma-client-api"
 version = "0.10.0-alpha.1"
-source = "git+https://github.com/timokoesters/ruma?branch=timo-fed-fixes#47fab87325b71b7f6c2fb3cd276d1f813e42abf7"
 dependencies = [
  "assign",
  "http",
@@ -1621,7 +1616,6 @@ dependencies = [
 [[package]]
 name = "ruma-common"
 version = "0.2.0"
-source = "git+https://github.com/timokoesters/ruma?branch=timo-fed-fixes#47fab87325b71b7f6c2fb3cd276d1f813e42abf7"
 dependencies = [
  "js_int",
  "ruma-api",
@@ -1635,7 +1629,6 @@ dependencies = [
 [[package]]
 name = "ruma-events"
 version = "0.22.0-alpha.1"
-source = "git+https://github.com/timokoesters/ruma?branch=timo-fed-fixes#47fab87325b71b7f6c2fb3cd276d1f813e42abf7"
 dependencies = [
  "js_int",
  "ruma-common",
@@ -1650,7 +1643,6 @@ dependencies = [
 [[package]]
 name = "ruma-events-macros"
 version = "0.22.0-alpha.1"
-source = "git+https://github.com/timokoesters/ruma?branch=timo-fed-fixes#47fab87325b71b7f6c2fb3cd276d1f813e42abf7"
 dependencies = [
  "proc-macro-crate",
  "proc-macro2",
@@ -1661,7 +1653,6 @@ dependencies = [
 [[package]]
 name = "ruma-federation-api"
 version = "0.0.3"
-source = "git+https://github.com/timokoesters/ruma?branch=timo-fed-fixes#47fab87325b71b7f6c2fb3cd276d1f813e42abf7"
 dependencies = [
  "js_int",
  "ruma-api",
@@ -1676,7 +1667,6 @@ dependencies = [
 [[package]]
 name = "ruma-identifiers"
 version = "0.17.4"
-source = "git+https://github.com/timokoesters/ruma?branch=timo-fed-fixes#47fab87325b71b7f6c2fb3cd276d1f813e42abf7"
 dependencies = [
  "rand",
  "ruma-identifiers-macros",
@@ -1688,7 +1678,6 @@ dependencies = [
 [[package]]
 name = "ruma-identifiers-macros"
 version = "0.17.4"
-source = "git+https://github.com/timokoesters/ruma?branch=timo-fed-fixes#47fab87325b71b7f6c2fb3cd276d1f813e42abf7"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1699,7 +1688,6 @@ dependencies = [
 [[package]]
 name = "ruma-identifiers-validation"
 version = "0.1.1"
-source = "git+https://github.com/timokoesters/ruma?branch=timo-fed-fixes#47fab87325b71b7f6c2fb3cd276d1f813e42abf7"
 dependencies = [
  "serde",
  "strum",
@@ -1708,7 +1696,6 @@ dependencies = [
 [[package]]
 name = "ruma-serde"
 version = "0.2.3"
-source = "git+https://github.com/timokoesters/ruma?branch=timo-fed-fixes#47fab87325b71b7f6c2fb3cd276d1f813e42abf7"
 dependencies = [
  "form_urlencoded",
  "itoa",
@@ -1720,7 +1707,6 @@ dependencies = [
 [[package]]
 name = "ruma-signatures"
 version = "0.6.0-dev.1"
-source = "git+https://github.com/timokoesters/ruma?branch=timo-fed-fixes#47fab87325b71b7f6c2fb3cd276d1f813e42abf7"
 dependencies = [
  "base64",
  "ring",
@@ -1970,7 +1956,6 @@ checksum = "7345c971d1ef21ffdbd103a75990a15eb03604fc8b8852ca8cb418ee1a099028"
 [[package]]
 name = "state-res"
 version = "0.1.0"
-source = "git+https://github.com/timokoesters/state-res?branch=spec-comp#d11a3feb5307715ab5d86af8f25d4bccfee6264b"
 dependencies = [
  "itertools",
  "js_int",
diff --git a/src/client_server/account.rs b/src/client_server/account.rs
index 7e0f942e..66b4a62a 100644
--- a/src/client_server/account.rs
+++ b/src/client_server/account.rs
@@ -1,3 +1,5 @@
+use std::{collections::BTreeMap, convert::TryInto};
+
 use super::{State, DEVICE_ID_LENGTH, SESSION_ID_LENGTH, TOKEN_LENGTH};
 use crate::{pdu::PduBuilder, utils, ConduitResult, Database, Error, Ruma};
 use ruma::{
@@ -11,8 +13,11 @@ use ruma::{
             uiaa::{AuthFlow, UiaaInfo},
         },
     },
-    events::{room::member, EventType},
-    UserId,
+    events::{
+        room::canonical_alias, room::guest_access, room::history_visibility, room::join_rules,
+        room::member, room::name, room::topic, EventType,
+    },
+    RoomAliasId, RoomId, RoomVersionId, UserId,
 };
 
 use register::RegistrationKind;
@@ -73,7 +78,7 @@ pub fn get_register_available_route(
     feature = "conduit_bin",
     post("/_matrix/client/r0/register", data = "<body>")
 )]
-pub fn register_route(
+pub async fn register_route(
     db: State<'_, Database>,
     body: Ruma<register::Request<'_>>,
 ) -> ConduitResult<register::Response> {
@@ -202,6 +207,265 @@ pub fn register_route(
         body.initial_device_display_name.clone(),
     )?;
 
+    // If this is the first user on this server, create the admins room
+    if db.users.count() == 1 {
+        // Create a user for the server
+        let conduit_user = UserId::parse_with_server_name("conduit", db.globals.server_name())
+            .expect("@conduit:server_name is valid");
+
+        db.users.create(&conduit_user, "")?;
+
+        let room_id = RoomId::new(db.globals.server_name());
+
+        let mut content = ruma::events::room::create::CreateEventContent::new(conduit_user.clone());
+        content.federate = true;
+        content.predecessor = None;
+        content.room_version = RoomVersionId::Version6;
+
+        // 1. The room create event
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomCreate,
+                content: serde_json::to_value(content).expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some("".to_owned()),
+                redacts: None,
+            },
+            &conduit_user,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
+
+        // 2. Make conduit bot join
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomMember,
+                content: serde_json::to_value(member::MemberEventContent {
+                    membership: member::MembershipState::Join,
+                    displayname: None,
+                    avatar_url: None,
+                    is_direct: None,
+                    third_party_invite: None,
+                })
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some(conduit_user.to_string()),
+                redacts: None,
+            },
+            &conduit_user,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
+
+        // 3. Power levels
+        let mut users = BTreeMap::new();
+        users.insert(conduit_user.clone(), 100.into());
+        users.insert(user_id.clone(), 100.into());
+
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomPowerLevels,
+                content: serde_json::to_value(
+                    ruma::events::room::power_levels::PowerLevelsEventContent {
+                        ban: 50.into(),
+                        events: BTreeMap::new(),
+                        events_default: 0.into(),
+                        invite: 50.into(),
+                        kick: 50.into(),
+                        redact: 50.into(),
+                        state_default: 50.into(),
+                        users,
+                        users_default: 0.into(),
+                        notifications: ruma::events::room::power_levels::NotificationPowerLevels {
+                            room: 50.into(),
+                        },
+                    },
+                )
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some("".to_owned()),
+                redacts: None,
+            },
+            &conduit_user,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
+
+        // 4.1 Join Rules
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomJoinRules,
+                content: serde_json::to_value(join_rules::JoinRulesEventContent::new(
+                    join_rules::JoinRule::Invite,
+                ))
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some("".to_owned()),
+                redacts: None,
+            },
+            &conduit_user,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
+
+        // 4.2 History Visibility
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomHistoryVisibility,
+                content: serde_json::to_value(
+                    history_visibility::HistoryVisibilityEventContent::new(
+                        history_visibility::HistoryVisibility::Shared,
+                    ),
+                )
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some("".to_owned()),
+                redacts: None,
+            },
+            &conduit_user,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
+
+        // 4.3 Guest Access
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomGuestAccess,
+                content: serde_json::to_value(guest_access::GuestAccessEventContent::new(
+                    guest_access::GuestAccess::Forbidden,
+                ))
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some("".to_owned()),
+                redacts: None,
+            },
+            &conduit_user,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
+
+        // 6. Events implied by name and topic
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomName,
+                content: serde_json::to_value(
+                    name::NameEventContent::new("Admin Room".to_owned()).map_err(|_| {
+                        Error::BadRequest(ErrorKind::InvalidParam, "Name is invalid.")
+                    })?,
+                )
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some("".to_owned()),
+                redacts: None,
+            },
+            &conduit_user,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
+
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomTopic,
+                content: serde_json::to_value(topic::TopicEventContent {
+                    topic: format!("Manage {}", db.globals.server_name()),
+                })
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some("".to_owned()),
+                redacts: None,
+            },
+            &conduit_user,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
+
+        // Room alias
+        let alias: RoomAliasId = format!("#admins:{}", db.globals.server_name())
+            .try_into()
+            .expect("#admins:server_name is a valid alias name");
+
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomCanonicalAlias,
+                content: serde_json::to_value(canonical_alias::CanonicalAliasEventContent {
+                    alias: Some(alias.clone()),
+                    alt_aliases: Vec::new(),
+                })
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some("".to_owned()),
+                redacts: None,
+            },
+            &conduit_user,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
+
+        db.rooms.set_alias(&alias, Some(&room_id), &db.globals)?;
+
+        // Invite and join the real user
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomMember,
+                content: serde_json::to_value(member::MemberEventContent {
+                    membership: member::MembershipState::Invite,
+                    displayname: None,
+                    avatar_url: None,
+                    is_direct: None,
+                    third_party_invite: None,
+                })
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some(user_id.to_string()),
+                redacts: None,
+            },
+            &conduit_user,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomMember,
+                content: serde_json::to_value(member::MemberEventContent {
+                    membership: member::MembershipState::Join,
+                    displayname: None,
+                    avatar_url: None,
+                    is_direct: None,
+                    third_party_invite: None,
+                })
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some(user_id.to_string()),
+                redacts: None,
+            },
+            &user_id,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
+    }
+
     Ok(register::Response {
         access_token: Some(token),
         user_id,
@@ -354,23 +618,20 @@ pub async fn deactivate_route(
             third_party_invite: None,
         };
 
-        db.rooms
-            .build_and_append_pdu(
-                PduBuilder {
-                    event_type: EventType::RoomMember,
-                    content: serde_json::to_value(event)
-                        .expect("event is valid, we just created it"),
-                    unsigned: None,
-                    state_key: Some(sender_id.to_string()),
-                    redacts: None,
-                },
-                &sender_id,
-                &room_id,
-                &db.globals,
-                &db.sending,
-                &db.account_data,
-            )
-            .await?;
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomMember,
+                content: serde_json::to_value(event).expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some(sender_id.to_string()),
+                redacts: None,
+            },
+            &sender_id,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
     }
 
     // Remove devices and mark account as deactivated
diff --git a/src/client_server/membership.rs b/src/client_server/membership.rs
index 628045de..526e82f1 100644
--- a/src/client_server/membership.rs
+++ b/src/client_server/membership.rs
@@ -108,22 +108,20 @@ pub async fn leave_room_route(
 
     event.membership = member::MembershipState::Leave;
 
-    db.rooms
-        .build_and_append_pdu(
-            PduBuilder {
-                event_type: EventType::RoomMember,
-                content: serde_json::to_value(event).expect("event is valid, we just created it"),
-                unsigned: None,
-                state_key: Some(sender_id.to_string()),
-                redacts: None,
-            },
-            &sender_id,
-            &body.room_id,
-            &db.globals,
-            &db.sending,
-            &db.account_data,
-        )
-        .await?;
+    db.rooms.build_and_append_pdu(
+        PduBuilder {
+            event_type: EventType::RoomMember,
+            content: serde_json::to_value(event).expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some(sender_id.to_string()),
+            redacts: None,
+        },
+        &sender_id,
+        &body.room_id,
+        &db.globals,
+        &db.sending,
+        &db.account_data,
+    )?;
 
     Ok(leave_room::Response::new().into())
 }
@@ -139,29 +137,27 @@ pub async fn invite_user_route(
     let sender_id = body.sender_id.as_ref().expect("user is authenticated");
 
     if let invite_user::IncomingInvitationRecipient::UserId { user_id } = &body.recipient {
-        db.rooms
-            .build_and_append_pdu(
-                PduBuilder {
-                    event_type: EventType::RoomMember,
-                    content: serde_json::to_value(member::MemberEventContent {
-                        membership: member::MembershipState::Invite,
-                        displayname: db.users.displayname(&user_id)?,
-                        avatar_url: db.users.avatar_url(&user_id)?,
-                        is_direct: None,
-                        third_party_invite: None,
-                    })
-                    .expect("event is valid, we just created it"),
-                    unsigned: None,
-                    state_key: Some(user_id.to_string()),
-                    redacts: None,
-                },
-                &sender_id,
-                &body.room_id,
-                &db.globals,
-                &db.sending,
-                &db.account_data,
-            )
-            .await?;
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomMember,
+                content: serde_json::to_value(member::MemberEventContent {
+                    membership: member::MembershipState::Invite,
+                    displayname: db.users.displayname(&user_id)?,
+                    avatar_url: db.users.avatar_url(&user_id)?,
+                    is_direct: None,
+                    third_party_invite: None,
+                })
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some(user_id.to_string()),
+                redacts: None,
+            },
+            &sender_id,
+            &body.room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
 
         Ok(invite_user::Response.into())
     } else {
@@ -199,22 +195,20 @@ pub async fn kick_user_route(
     event.membership = ruma::events::room::member::MembershipState::Leave;
     // TODO: reason
 
-    db.rooms
-        .build_and_append_pdu(
-            PduBuilder {
-                event_type: EventType::RoomMember,
-                content: serde_json::to_value(event).expect("event is valid, we just created it"),
-                unsigned: None,
-                state_key: Some(body.user_id.to_string()),
-                redacts: None,
-            },
-            &sender_id,
-            &body.room_id,
-            &db.globals,
-            &db.sending,
-            &db.account_data,
-        )
-        .await?;
+    db.rooms.build_and_append_pdu(
+        PduBuilder {
+            event_type: EventType::RoomMember,
+            content: serde_json::to_value(event).expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some(body.user_id.to_string()),
+            redacts: None,
+        },
+        &sender_id,
+        &body.room_id,
+        &db.globals,
+        &db.sending,
+        &db.account_data,
+    )?;
 
     Ok(kick_user::Response::new().into())
 }
@@ -257,22 +251,20 @@ pub async fn ban_user_route(
             },
         )?;
 
-    db.rooms
-        .build_and_append_pdu(
-            PduBuilder {
-                event_type: EventType::RoomMember,
-                content: serde_json::to_value(event).expect("event is valid, we just created it"),
-                unsigned: None,
-                state_key: Some(body.user_id.to_string()),
-                redacts: None,
-            },
-            &sender_id,
-            &body.room_id,
-            &db.globals,
-            &db.sending,
-            &db.account_data,
-        )
-        .await?;
+    db.rooms.build_and_append_pdu(
+        PduBuilder {
+            event_type: EventType::RoomMember,
+            content: serde_json::to_value(event).expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some(body.user_id.to_string()),
+            redacts: None,
+        },
+        &sender_id,
+        &body.room_id,
+        &db.globals,
+        &db.sending,
+        &db.account_data,
+    )?;
 
     Ok(ban_user::Response::new().into())
 }
@@ -306,22 +298,20 @@ pub async fn unban_user_route(
 
     event.membership = ruma::events::room::member::MembershipState::Leave;
 
-    db.rooms
-        .build_and_append_pdu(
-            PduBuilder {
-                event_type: EventType::RoomMember,
-                content: serde_json::to_value(event).expect("event is valid, we just created it"),
-                unsigned: None,
-                state_key: Some(body.user_id.to_string()),
-                redacts: None,
-            },
-            &sender_id,
-            &body.room_id,
-            &db.globals,
-            &db.sending,
-            &db.account_data,
-        )
-        .await?;
+    db.rooms.build_and_append_pdu(
+        PduBuilder {
+            event_type: EventType::RoomMember,
+            content: serde_json::to_value(event).expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some(body.user_id.to_string()),
+            redacts: None,
+        },
+        &sender_id,
+        &body.room_id,
+        &db.globals,
+        &db.sending,
+        &db.account_data,
+    )?;
 
     Ok(unban_user::Response::new().into())
 }
@@ -640,6 +630,7 @@ async fn join_room_by_id_helper(
                 &serde_json::to_value(&**pdu).expect("PDU is valid value"),
                 &db.globals,
                 &db.account_data,
+                &db.sending,
             )?;
 
             if state_events.contains(ev_id) {
@@ -657,23 +648,20 @@ async fn join_room_by_id_helper(
             third_party_invite: None,
         };
 
-        db.rooms
-            .build_and_append_pdu(
-                PduBuilder {
-                    event_type: EventType::RoomMember,
-                    content: serde_json::to_value(event)
-                        .expect("event is valid, we just created it"),
-                    unsigned: None,
-                    state_key: Some(sender_id.to_string()),
-                    redacts: None,
-                },
-                &sender_id,
-                &room_id,
-                &db.globals,
-                &db.sending,
-                &db.account_data,
-            )
-            .await?;
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomMember,
+                content: serde_json::to_value(event).expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some(sender_id.to_string()),
+                redacts: None,
+            },
+            &sender_id,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
     }
 
     Ok(join_room_by_id::Response::new(room_id.clone()).into())
diff --git a/src/client_server/message.rs b/src/client_server/message.rs
index 5a4488fe..c32bd687 100644
--- a/src/client_server/message.rs
+++ b/src/client_server/message.rs
@@ -49,29 +49,26 @@ pub async fn send_message_event_route(
     let mut unsigned = serde_json::Map::new();
     unsigned.insert("transaction_id".to_owned(), body.txn_id.clone().into());
 
-    let event_id = db
-        .rooms
-        .build_and_append_pdu(
-            PduBuilder {
-                event_type: body.content.event_type().into(),
-                content: serde_json::from_str(
-                    body.json_body
-                        .as_ref()
-                        .ok_or(Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?
-                        .get(),
-                )
-                .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?,
-                unsigned: Some(unsigned),
-                state_key: None,
-                redacts: None,
-            },
-            &sender_id,
-            &body.room_id,
-            &db.globals,
-            &db.sending,
-            &db.account_data,
-        )
-        .await?;
+    let event_id = db.rooms.build_and_append_pdu(
+        PduBuilder {
+            event_type: body.content.event_type().into(),
+            content: serde_json::from_str(
+                body.json_body
+                    .as_ref()
+                    .ok_or(Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?
+                    .get(),
+            )
+            .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid JSON body."))?,
+            unsigned: Some(unsigned),
+            state_key: None,
+            redacts: None,
+        },
+        &sender_id,
+        &body.room_id,
+        &db.globals,
+        &db.sending,
+        &db.account_data,
+    )?;
 
     db.transaction_ids
         .add_txnid(sender_id, device_id, &body.txn_id, event_id.as_bytes())?;
diff --git a/src/client_server/profile.rs b/src/client_server/profile.rs
index 686d4c3e..9c6bd512 100644
--- a/src/client_server/profile.rs
+++ b/src/client_server/profile.rs
@@ -31,43 +31,41 @@ pub async fn set_displayname_route(
     // Send a new membership event and presence update into all joined rooms
     for room_id in db.rooms.rooms_joined(&sender_id) {
         let room_id = room_id?;
-        db.rooms
-            .build_and_append_pdu(
-                PduBuilder {
-                    event_type: EventType::RoomMember,
-                    content: serde_json::to_value(ruma::events::room::member::MemberEventContent {
-                        displayname: body.displayname.clone(),
-                        ..serde_json::from_value::<Raw<_>>(
-                            db.rooms
-                                .room_state_get(
-                                    &room_id,
-                                    &EventType::RoomMember,
-                                    &sender_id.to_string(),
-                                )?
-                                .ok_or_else(|| {
-                                    Error::bad_database(
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomMember,
+                content: serde_json::to_value(ruma::events::room::member::MemberEventContent {
+                    displayname: body.displayname.clone(),
+                    ..serde_json::from_value::<Raw<_>>(
+                        db.rooms
+                            .room_state_get(
+                                &room_id,
+                                &EventType::RoomMember,
+                                &sender_id.to_string(),
+                            )?
+                            .ok_or_else(|| {
+                                Error::bad_database(
                                     "Tried to send displayname update for user not in the room.",
                                 )
-                                })?
-                                .content
-                                .clone(),
-                        )
-                        .expect("from_value::<Raw<..>> can never fail")
-                        .deserialize()
-                        .map_err(|_| Error::bad_database("Database contains invalid PDU."))?
-                    })
-                    .expect("event is valid, we just created it"),
-                    unsigned: None,
-                    state_key: Some(sender_id.to_string()),
-                    redacts: None,
-                },
-                &sender_id,
-                &room_id,
-                &db.globals,
-                &db.sending,
-                &db.account_data,
-            )
-            .await?;
+                            })?
+                            .content
+                            .clone(),
+                    )
+                    .expect("from_value::<Raw<..>> can never fail")
+                    .deserialize()
+                    .map_err(|_| Error::bad_database("Database contains invalid PDU."))?
+                })
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some(sender_id.to_string()),
+                redacts: None,
+            },
+            &sender_id,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
 
         // Presence update
         db.rooms.edus.update_presence(
@@ -125,43 +123,41 @@ pub async fn set_avatar_url_route(
     // Send a new membership event and presence update into all joined rooms
     for room_id in db.rooms.rooms_joined(&sender_id) {
         let room_id = room_id?;
-        db.rooms
-            .build_and_append_pdu(
-                PduBuilder {
-                    event_type: EventType::RoomMember,
-                    content: serde_json::to_value(ruma::events::room::member::MemberEventContent {
-                        avatar_url: body.avatar_url.clone(),
-                        ..serde_json::from_value::<Raw<_>>(
-                            db.rooms
-                                .room_state_get(
-                                    &room_id,
-                                    &EventType::RoomMember,
-                                    &sender_id.to_string(),
-                                )?
-                                .ok_or_else(|| {
-                                    Error::bad_database(
-                                        "Tried to send avatar url update for user not in the room.",
-                                    )
-                                })?
-                                .content
-                                .clone(),
-                        )
-                        .expect("from_value::<Raw<..>> can never fail")
-                        .deserialize()
-                        .map_err(|_| Error::bad_database("Database contains invalid PDU."))?
-                    })
-                    .expect("event is valid, we just created it"),
-                    unsigned: None,
-                    state_key: Some(sender_id.to_string()),
-                    redacts: None,
-                },
-                &sender_id,
-                &room_id,
-                &db.globals,
-                &db.sending,
-                &db.account_data,
-            )
-            .await?;
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomMember,
+                content: serde_json::to_value(ruma::events::room::member::MemberEventContent {
+                    avatar_url: body.avatar_url.clone(),
+                    ..serde_json::from_value::<Raw<_>>(
+                        db.rooms
+                            .room_state_get(
+                                &room_id,
+                                &EventType::RoomMember,
+                                &sender_id.to_string(),
+                            )?
+                            .ok_or_else(|| {
+                                Error::bad_database(
+                                    "Tried to send avatar url update for user not in the room.",
+                                )
+                            })?
+                            .content
+                            .clone(),
+                    )
+                    .expect("from_value::<Raw<..>> can never fail")
+                    .deserialize()
+                    .map_err(|_| Error::bad_database("Database contains invalid PDU."))?
+                })
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some(sender_id.to_string()),
+                redacts: None,
+            },
+            &sender_id,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
 
         // Presence update
         db.rooms.edus.update_presence(
diff --git a/src/client_server/redact.rs b/src/client_server/redact.rs
index 24df8dd7..b13cd802 100644
--- a/src/client_server/redact.rs
+++ b/src/client_server/redact.rs
@@ -18,26 +18,23 @@ pub async fn redact_event_route(
 ) -> ConduitResult<redact_event::Response> {
     let sender_id = body.sender_id.as_ref().expect("user is authenticated");
 
-    let event_id = db
-        .rooms
-        .build_and_append_pdu(
-            PduBuilder {
-                event_type: EventType::RoomRedaction,
-                content: serde_json::to_value(redaction::RedactionEventContent {
-                    reason: body.reason.clone(),
-                })
-                .expect("event is valid, we just created it"),
-                unsigned: None,
-                state_key: None,
-                redacts: Some(body.event_id.clone()),
-            },
-            &sender_id,
-            &body.room_id,
-            &db.globals,
-            &db.sending,
-            &db.account_data,
-        )
-        .await?;
+    let event_id = db.rooms.build_and_append_pdu(
+        PduBuilder {
+            event_type: EventType::RoomRedaction,
+            content: serde_json::to_value(redaction::RedactionEventContent {
+                reason: body.reason.clone(),
+            })
+            .expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: None,
+            redacts: Some(body.event_id.clone()),
+        },
+        &sender_id,
+        &body.room_id,
+        &db.globals,
+        &db.sending,
+        &db.account_data,
+    )?;
 
     Ok(redact_event::Response { event_id }.into())
 }
diff --git a/src/client_server/room.rs b/src/client_server/room.rs
index d21148bb..28d30e29 100644
--- a/src/client_server/room.rs
+++ b/src/client_server/room.rs
@@ -53,47 +53,43 @@ pub async fn create_room_route(
     content.room_version = RoomVersionId::Version6;
 
     // 1. The room create event
-    db.rooms
-        .build_and_append_pdu(
-            PduBuilder {
-                event_type: EventType::RoomCreate,
-                content: serde_json::to_value(content).expect("event is valid, we just created it"),
-                unsigned: None,
-                state_key: Some("".to_owned()),
-                redacts: None,
-            },
-            &sender_id,
-            &room_id,
-            &db.globals,
-            &db.sending,
-            &db.account_data,
-        )
-        .await?;
+    db.rooms.build_and_append_pdu(
+        PduBuilder {
+            event_type: EventType::RoomCreate,
+            content: serde_json::to_value(content).expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some("".to_owned()),
+            redacts: None,
+        },
+        &sender_id,
+        &room_id,
+        &db.globals,
+        &db.sending,
+        &db.account_data,
+    )?;
 
     // 2. Let the room creator join
-    db.rooms
-        .build_and_append_pdu(
-            PduBuilder {
-                event_type: EventType::RoomMember,
-                content: serde_json::to_value(member::MemberEventContent {
-                    membership: member::MembershipState::Join,
-                    displayname: db.users.displayname(&sender_id)?,
-                    avatar_url: db.users.avatar_url(&sender_id)?,
-                    is_direct: Some(body.is_direct),
-                    third_party_invite: None,
-                })
-                .expect("event is valid, we just created it"),
-                unsigned: None,
-                state_key: Some(sender_id.to_string()),
-                redacts: None,
-            },
-            &sender_id,
-            &room_id,
-            &db.globals,
-            &db.sending,
-            &db.account_data,
-        )
-        .await?;
+    db.rooms.build_and_append_pdu(
+        PduBuilder {
+            event_type: EventType::RoomMember,
+            content: serde_json::to_value(member::MemberEventContent {
+                membership: member::MembershipState::Join,
+                displayname: db.users.displayname(&sender_id)?,
+                avatar_url: db.users.avatar_url(&sender_id)?,
+                is_direct: Some(body.is_direct),
+                third_party_invite: None,
+            })
+            .expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some(sender_id.to_string()),
+            redacts: None,
+        },
+        &sender_id,
+        &room_id,
+        &db.globals,
+        &db.sending,
+        &db.account_data,
+    )?;
 
     // 3. Power levels
     let mut users = BTreeMap::new();
@@ -123,22 +119,20 @@ pub async fn create_room_route(
         })
         .expect("event is valid, we just created it")
     };
-    db.rooms
-        .build_and_append_pdu(
-            PduBuilder {
-                event_type: EventType::RoomPowerLevels,
-                content: power_levels_content,
-                unsigned: None,
-                state_key: Some("".to_owned()),
-                redacts: None,
-            },
-            &sender_id,
-            &room_id,
-            &db.globals,
-            &db.sending,
-            &db.account_data,
-        )
-        .await?;
+    db.rooms.build_and_append_pdu(
+        PduBuilder {
+            event_type: EventType::RoomPowerLevels,
+            content: power_levels_content,
+            unsigned: None,
+            state_key: Some("".to_owned()),
+            redacts: None,
+        },
+        &sender_id,
+        &room_id,
+        &db.globals,
+        &db.sending,
+        &db.account_data,
+    )?;
 
     // 4. Events set by preset
 
@@ -149,84 +143,76 @@ pub async fn create_room_route(
     });
 
     // 4.1 Join Rules
-    db.rooms
-        .build_and_append_pdu(
-            PduBuilder {
-                event_type: EventType::RoomJoinRules,
-                content: match preset {
-                    create_room::RoomPreset::PublicChat => serde_json::to_value(
-                        join_rules::JoinRulesEventContent::new(join_rules::JoinRule::Public),
-                    )
-                    .expect("event is valid, we just created it"),
-                    // according to spec "invite" is the default
-                    _ => serde_json::to_value(join_rules::JoinRulesEventContent::new(
-                        join_rules::JoinRule::Invite,
-                    ))
-                    .expect("event is valid, we just created it"),
-                },
-                unsigned: None,
-                state_key: Some("".to_owned()),
-                redacts: None,
-            },
-            &sender_id,
-            &room_id,
-            &db.globals,
-            &db.sending,
-            &db.account_data,
-        )
-        .await?;
-
-    // 4.2 History Visibility
-    db.rooms
-        .build_and_append_pdu(
-            PduBuilder {
-                event_type: EventType::RoomHistoryVisibility,
-                content: serde_json::to_value(
-                    history_visibility::HistoryVisibilityEventContent::new(
-                        history_visibility::HistoryVisibility::Shared,
-                    ),
+    db.rooms.build_and_append_pdu(
+        PduBuilder {
+            event_type: EventType::RoomJoinRules,
+            content: match preset {
+                create_room::RoomPreset::PublicChat => serde_json::to_value(
+                    join_rules::JoinRulesEventContent::new(join_rules::JoinRule::Public),
                 )
                 .expect("event is valid, we just created it"),
-                unsigned: None,
-                state_key: Some("".to_owned()),
-                redacts: None,
+                // according to spec "invite" is the default
+                _ => serde_json::to_value(join_rules::JoinRulesEventContent::new(
+                    join_rules::JoinRule::Invite,
+                ))
+                .expect("event is valid, we just created it"),
             },
-            &sender_id,
-            &room_id,
-            &db.globals,
-            &db.sending,
-            &db.account_data,
-        )
-        .await?;
+            unsigned: None,
+            state_key: Some("".to_owned()),
+            redacts: None,
+        },
+        &sender_id,
+        &room_id,
+        &db.globals,
+        &db.sending,
+        &db.account_data,
+    )?;
+
+    // 4.2 History Visibility
+    db.rooms.build_and_append_pdu(
+        PduBuilder {
+            event_type: EventType::RoomHistoryVisibility,
+            content: serde_json::to_value(history_visibility::HistoryVisibilityEventContent::new(
+                history_visibility::HistoryVisibility::Shared,
+            ))
+            .expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some("".to_owned()),
+            redacts: None,
+        },
+        &sender_id,
+        &room_id,
+        &db.globals,
+        &db.sending,
+        &db.account_data,
+    )?;
 
     // 4.3 Guest Access
-    db.rooms
-        .build_and_append_pdu(
-            PduBuilder {
-                event_type: EventType::RoomGuestAccess,
-                content: match preset {
-                    create_room::RoomPreset::PublicChat => {
-                        serde_json::to_value(guest_access::GuestAccessEventContent::new(
-                            guest_access::GuestAccess::Forbidden,
-                        ))
-                        .expect("event is valid, we just created it")
-                    }
-                    _ => serde_json::to_value(guest_access::GuestAccessEventContent::new(
-                        guest_access::GuestAccess::CanJoin,
+    db.rooms.build_and_append_pdu(
+        PduBuilder {
+            event_type: EventType::RoomGuestAccess,
+            content: match preset {
+                create_room::RoomPreset::PublicChat => {
+                    serde_json::to_value(guest_access::GuestAccessEventContent::new(
+                        guest_access::GuestAccess::Forbidden,
                     ))
-                    .expect("event is valid, we just created it"),
-                },
-                unsigned: None,
-                state_key: Some("".to_owned()),
-                redacts: None,
+                    .expect("event is valid, we just created it")
+                }
+                _ => serde_json::to_value(guest_access::GuestAccessEventContent::new(
+                    guest_access::GuestAccess::CanJoin,
+                ))
+                .expect("event is valid, we just created it"),
             },
-            &sender_id,
-            &room_id,
-            &db.globals,
-            &db.sending,
-            &db.account_data,
-        )
-        .await?;
+            unsigned: None,
+            state_key: Some("".to_owned()),
+            redacts: None,
+        },
+        &sender_id,
+        &room_id,
+        &db.globals,
+        &db.sending,
+        &db.account_data,
+    )?;
 
     // 5. Events listed in initial_state
     for event in &body.initial_state {
@@ -240,90 +226,82 @@ pub async fn create_room_route(
             continue;
         }
 
-        db.rooms
-            .build_and_append_pdu(
-                pdu_builder,
-                &sender_id,
-                &room_id,
-                &db.globals,
-                &db.sending,
-                &db.account_data,
-            )
-            .await?;
+        db.rooms.build_and_append_pdu(
+            pdu_builder,
+            &sender_id,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
     }
 
     // 6. Events implied by name and topic
     if let Some(name) = &body.name {
-        db.rooms
-            .build_and_append_pdu(
-                PduBuilder {
-                    event_type: EventType::RoomName,
-                    content: serde_json::to_value(
-                        name::NameEventContent::new(name.clone()).map_err(|_| {
-                            Error::BadRequest(ErrorKind::InvalidParam, "Name is invalid.")
-                        })?,
-                    )
-                    .expect("event is valid, we just created it"),
-                    unsigned: None,
-                    state_key: Some("".to_owned()),
-                    redacts: None,
-                },
-                &sender_id,
-                &room_id,
-                &db.globals,
-                &db.sending,
-                &db.account_data,
-            )
-            .await?;
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomName,
+                content: serde_json::to_value(
+                    name::NameEventContent::new(name.clone()).map_err(|_| {
+                        Error::BadRequest(ErrorKind::InvalidParam, "Name is invalid.")
+                    })?,
+                )
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some("".to_owned()),
+                redacts: None,
+            },
+            &sender_id,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
     }
 
     if let Some(topic) = &body.topic {
-        db.rooms
-            .build_and_append_pdu(
-                PduBuilder {
-                    event_type: EventType::RoomTopic,
-                    content: serde_json::to_value(topic::TopicEventContent {
-                        topic: topic.clone(),
-                    })
-                    .expect("event is valid, we just created it"),
-                    unsigned: None,
-                    state_key: Some("".to_owned()),
-                    redacts: None,
-                },
-                &sender_id,
-                &room_id,
-                &db.globals,
-                &db.sending,
-                &db.account_data,
-            )
-            .await?;
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomTopic,
+                content: serde_json::to_value(topic::TopicEventContent {
+                    topic: topic.clone(),
+                })
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some("".to_owned()),
+                redacts: None,
+            },
+            &sender_id,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
     }
 
     // 7. Events implied by invite (and TODO: invite_3pid)
     for user in &body.invite {
-        db.rooms
-            .build_and_append_pdu(
-                PduBuilder {
-                    event_type: EventType::RoomMember,
-                    content: serde_json::to_value(member::MemberEventContent {
-                        membership: member::MembershipState::Invite,
-                        displayname: db.users.displayname(&user)?,
-                        avatar_url: db.users.avatar_url(&user)?,
-                        is_direct: Some(body.is_direct),
-                        third_party_invite: None,
-                    })
-                    .expect("event is valid, we just created it"),
-                    unsigned: None,
-                    state_key: Some(user.to_string()),
-                    redacts: None,
-                },
-                &sender_id,
-                &room_id,
-                &db.globals,
-                &db.sending,
-                &db.account_data,
-            )
-            .await?;
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type: EventType::RoomMember,
+                content: serde_json::to_value(member::MemberEventContent {
+                    membership: member::MembershipState::Invite,
+                    displayname: db.users.displayname(&user)?,
+                    avatar_url: db.users.avatar_url(&user)?,
+                    is_direct: Some(body.is_direct),
+                    third_party_invite: None,
+                })
+                .expect("event is valid, we just created it"),
+                unsigned: None,
+                state_key: Some(user.to_string()),
+                redacts: None,
+            },
+            &sender_id,
+            &room_id,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
     }
 
     // Homeserver specific stuff
@@ -395,29 +373,24 @@ pub async fn upgrade_room_route(
 
     // Send a m.room.tombstone event to the old room to indicate that it is not intended to be used any further
     // Fail if the sender does not have the required permissions
-    let tombstone_event_id = db
-        .rooms
-        .build_and_append_pdu(
-            PduBuilder {
-                event_type: EventType::RoomTombstone,
-                content: serde_json::to_value(
-                    ruma::events::room::tombstone::TombstoneEventContent {
-                        body: "This room has been replaced".to_string(),
-                        replacement_room: replacement_room.clone(),
-                    },
-                )
-                .expect("event is valid, we just created it"),
-                unsigned: None,
-                state_key: Some("".to_owned()),
-                redacts: None,
-            },
-            sender_id,
-            &body.room_id,
-            &db.globals,
-            &db.sending,
-            &db.account_data,
-        )
-        .await?;
+    let tombstone_event_id = db.rooms.build_and_append_pdu(
+        PduBuilder {
+            event_type: EventType::RoomTombstone,
+            content: serde_json::to_value(ruma::events::room::tombstone::TombstoneEventContent {
+                body: "This room has been replaced".to_string(),
+                replacement_room: replacement_room.clone(),
+            })
+            .expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some("".to_owned()),
+            redacts: None,
+        },
+        sender_id,
+        &body.room_id,
+        &db.globals,
+        &db.sending,
+        &db.account_data,
+    )?;
 
     // Get the old room federations status
     let federate = serde_json::from_value::<Raw<ruma::events::room::create::CreateEventContent>>(
@@ -444,48 +417,44 @@ pub async fn upgrade_room_route(
     create_event_content.room_version = new_version;
     create_event_content.predecessor = predecessor;
 
-    db.rooms
-        .build_and_append_pdu(
-            PduBuilder {
-                event_type: EventType::RoomCreate,
-                content: serde_json::to_value(create_event_content)
-                    .expect("event is valid, we just created it"),
-                unsigned: None,
-                state_key: Some("".to_owned()),
-                redacts: None,
-            },
-            sender_id,
-            &replacement_room,
-            &db.globals,
-            &db.sending,
-            &db.account_data,
-        )
-        .await?;
+    db.rooms.build_and_append_pdu(
+        PduBuilder {
+            event_type: EventType::RoomCreate,
+            content: serde_json::to_value(create_event_content)
+                .expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some("".to_owned()),
+            redacts: None,
+        },
+        sender_id,
+        &replacement_room,
+        &db.globals,
+        &db.sending,
+        &db.account_data,
+    )?;
 
     // Join the new room
-    db.rooms
-        .build_and_append_pdu(
-            PduBuilder {
-                event_type: EventType::RoomMember,
-                content: serde_json::to_value(member::MemberEventContent {
-                    membership: member::MembershipState::Join,
-                    displayname: db.users.displayname(&sender_id)?,
-                    avatar_url: db.users.avatar_url(&sender_id)?,
-                    is_direct: None,
-                    third_party_invite: None,
-                })
-                .expect("event is valid, we just created it"),
-                unsigned: None,
-                state_key: Some(sender_id.to_string()),
-                redacts: None,
-            },
-            sender_id,
-            &replacement_room,
-            &db.globals,
-            &db.sending,
-            &db.account_data,
-        )
-        .await?;
+    db.rooms.build_and_append_pdu(
+        PduBuilder {
+            event_type: EventType::RoomMember,
+            content: serde_json::to_value(member::MemberEventContent {
+                membership: member::MembershipState::Join,
+                displayname: db.users.displayname(&sender_id)?,
+                avatar_url: db.users.avatar_url(&sender_id)?,
+                is_direct: None,
+                third_party_invite: None,
+            })
+            .expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some(sender_id.to_string()),
+            redacts: None,
+        },
+        sender_id,
+        &replacement_room,
+        &db.globals,
+        &db.sending,
+        &db.account_data,
+    )?;
 
     // Recommended transferable state events list from the specs
     let transferable_state_events = vec![
@@ -507,22 +476,20 @@ pub async fn upgrade_room_route(
             None => continue, // Skipping missing events.
         };
 
-        db.rooms
-            .build_and_append_pdu(
-                PduBuilder {
-                    event_type,
-                    content: event_content,
-                    unsigned: None,
-                    state_key: Some("".to_owned()),
-                    redacts: None,
-                },
-                sender_id,
-                &replacement_room,
-                &db.globals,
-                &db.sending,
-                &db.account_data,
-            )
-            .await?;
+        db.rooms.build_and_append_pdu(
+            PduBuilder {
+                event_type,
+                content: event_content,
+                unsigned: None,
+                state_key: Some("".to_owned()),
+                redacts: None,
+            },
+            sender_id,
+            &replacement_room,
+            &db.globals,
+            &db.sending,
+            &db.account_data,
+        )?;
     }
 
     // Moves any local aliases to the new room
@@ -552,24 +519,21 @@ pub async fn upgrade_room_route(
     power_levels_event_content.invite = new_level;
 
     // Modify the power levels in the old room to prevent sending of events and inviting new users
-    let _ = db
-        .rooms
-        .build_and_append_pdu(
-            PduBuilder {
-                event_type: EventType::RoomPowerLevels,
-                content: serde_json::to_value(power_levels_event_content)
-                    .expect("event is valid, we just created it"),
-                unsigned: None,
-                state_key: Some("".to_owned()),
-                redacts: None,
-            },
-            sender_id,
-            &body.room_id,
-            &db.globals,
-            &db.sending,
-            &db.account_data,
-        )
-        .await;
+    let _ = db.rooms.build_and_append_pdu(
+        PduBuilder {
+            event_type: EventType::RoomPowerLevels,
+            content: serde_json::to_value(power_levels_event_content)
+                .expect("event is valid, we just created it"),
+            unsigned: None,
+            state_key: Some("".to_owned()),
+            redacts: None,
+        },
+        sender_id,
+        &body.room_id,
+        &db.globals,
+        &db.sending,
+        &db.account_data,
+    )?;
 
     // Return the replacement room id
     Ok(upgrade_room::Response { replacement_room }.into())
diff --git a/src/client_server/state.rs b/src/client_server/state.rs
index 46182a12..1e13b42f 100644
--- a/src/client_server/state.rs
+++ b/src/client_server/state.rs
@@ -213,23 +213,20 @@ pub async fn send_state_event_for_key_helper(
         }
     }
 
-    let event_id = db
-        .rooms
-        .build_and_append_pdu(
-            PduBuilder {
-                event_type: content.event_type().into(),
-                content: json,
-                unsigned: None,
-                state_key,
-                redacts: None,
-            },
-            &sender_id,
-            &room_id,
-            &db.globals,
-            &db.sending,
-            &db.account_data,
-        )
-        .await?;
+    let event_id = db.rooms.build_and_append_pdu(
+        PduBuilder {
+            event_type: content.event_type().into(),
+            content: json,
+            unsigned: None,
+            state_key,
+            redacts: None,
+        },
+        &sender_id,
+        &room_id,
+        &db.globals,
+        &db.sending,
+        &db.account_data,
+    )?;
 
     Ok(event_id)
 }
diff --git a/src/database/rooms.rs b/src/database/rooms.rs
index 108edb5d..ab05b390 100644
--- a/src/database/rooms.rs
+++ b/src/database/rooms.rs
@@ -10,7 +10,7 @@ use ruma::{
     events::{
         ignored_user_list,
         room::{
-            member,
+            member, message,
             power_levels::{self, PowerLevelsEventContent},
         },
         EventType,
@@ -440,6 +440,7 @@ impl Rooms {
         pdu_json: &serde_json::Value,
         globals: &super::globals::Globals,
         account_data: &super::account_data::AccountData,
+        sending: &super::sending::Sending,
     ) -> Result<Vec<u8>> {
         self.replace_pdu_leaves(&pdu.room_id, &pdu.event_id)?;
 
@@ -452,7 +453,8 @@ impl Rooms {
         self.edus
             .private_read_set(&pdu.room_id, &pdu.sender, index, &globals)?;
 
-        let mut pdu_id = pdu.room_id.as_bytes().to_vec();
+        let room_id = pdu.room_id.clone();
+        let mut pdu_id = room_id.as_bytes().to_vec();
         pdu_id.push(0xff);
         pdu_id.extend_from_slice(&index.to_be_bytes());
 
@@ -503,6 +505,45 @@ impl Rooms {
                         key.extend_from_slice(&pdu_id);
                         self.tokenids.insert(key, &[])?;
                     }
+
+                    if body.starts_with(&format!("@conduit:{}: ", globals.server_name()))
+                        && self
+                            .id_from_alias(
+                                &format!("#admins:{}", globals.server_name())
+                                    .try_into()
+                                    .expect("#admins:server_name is a valid room alias"),
+                            )?
+                            .as_ref()
+                            == Some(&pdu.room_id)
+                    {
+                        let mut parts = body.split_whitespace().skip(1);
+                        if let Some(command) = parts.next() {
+                            let args = parts.collect::<Vec<_>>();
+
+                            self.build_and_append_pdu(
+                                PduBuilder {
+                                    event_type: EventType::RoomMessage,
+                                    content: serde_json::to_value(
+                                        message::TextMessageEventContent {
+                                            body: format!("Command: {}, Args: {:?}", command, args),
+                                            formatted: None,
+                                            relates_to: None,
+                                        },
+                                    )
+                                    .expect("event is valid, we just created it"),
+                                    unsigned: None,
+                                    state_key: None,
+                                    redacts: None,
+                                },
+                                &UserId::try_from(format!("@conduit:{}", globals.server_name()))
+                                    .expect("@conduit:server_name is valid"),
+                                &room_id,
+                                &globals,
+                                &sending,
+                                &account_data,
+                            )?;
+                        }
+                    }
                 }
             }
             _ => {}
@@ -570,7 +611,7 @@ impl Rooms {
     }
 
     /// Creates a new persisted data unit and adds it to a room.
-    pub async fn build_and_append_pdu(
+    pub fn build_and_append_pdu(
         &self,
         pdu_builder: PduBuilder,
         sender: &UserId,
@@ -793,7 +834,7 @@ impl Rooms {
             .expect("json is object")
             .insert("event_id".to_owned(), pdu.event_id.to_string().into());
 
-        let pdu_id = self.append_pdu(&pdu, &pdu_json, globals, account_data)?;
+        let pdu_id = self.append_pdu(&pdu, &pdu_json, globals, account_data, sending)?;
 
         self.append_to_state(&pdu_id, &pdu)?;
 
diff --git a/src/database/users.rs b/src/database/users.rs
index 2e26c1ea..0d35e362 100644
--- a/src/database/users.rs
+++ b/src/database/users.rs
@@ -57,6 +57,11 @@ impl Users {
         Ok(())
     }
 
+    /// Returns the number of users registered on this server.
+    pub fn count(&self) -> usize {
+        self.userid_password.iter().count()
+    }
+
     /// Find out which user an access token belongs to.
     pub fn find_from_token(&self, token: &str) -> Result<Option<(UserId, String)>> {
         self.token_userdeviceid
diff --git a/src/main.rs b/src/main.rs
index fa1cc5ca..8fb5fda9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -126,6 +126,7 @@ fn setup_rocket() -> rocket::Rocket {
                 server_server::get_public_rooms_filtered_route,
                 server_server::send_transaction_message_route,
                 server_server::get_missing_events_route,
+                server_server::get_profile_information_route,
             ],
         )
         .attach(AdHoc::on_attach("Config", |mut rocket| async {
diff --git a/src/server_server.rs b/src/server_server.rs
index 2d52c4ac..0c175bfb 100644
--- a/src/server_server.rs
+++ b/src/server_server.rs
@@ -1,4 +1,5 @@
 use crate::{client_server, ConduitResult, Database, Error, PduEvent, Result, Ruma};
+use get_profile_information::v1::ProfileField;
 use http::header::{HeaderValue, AUTHORIZATION, HOST};
 use log::warn;
 use rocket::{get, post, put, response::content::Json, State};
@@ -10,6 +11,7 @@ use ruma::{
                 get_server_keys, get_server_version::v1 as get_server_version, ServerKey, VerifyKey,
             },
             event::get_missing_events,
+            query::get_profile_information,
             transactions::send_transaction_message,
         },
         OutgoingRequest,
@@ -362,9 +364,9 @@ pub fn send_transaction_message_route<'a>(
         let pdu = serde_json::from_value::<PduEvent>(value.clone())
             .expect("all ruma pdus are conduit pdus");
         if db.rooms.exists(&pdu.room_id)? {
-            let pdu_id = db
-                .rooms
-                .append_pdu(&pdu, &value, &db.globals, &db.account_data)?;
+            let pdu_id =
+                db.rooms
+                    .append_pdu(&pdu, &value, &db.globals, &db.account_data, &db.sending)?;
             db.rooms.append_to_state(&pdu_id, &pdu)?;
         }
     }
@@ -416,3 +418,59 @@ pub fn get_missing_events_route<'a>(
 
     Ok(get_missing_events::v1::Response { events }.into())
 }
+
+#[cfg_attr(
+    feature = "conduit_bin",
+    get("/_matrix/federation/v1/query/profile", data = "<body>")
+)]
+pub fn get_profile_information_route<'a>(
+    db: State<'a, Database>,
+    body: Ruma<get_profile_information::v1::Request<'_>>,
+) -> ConduitResult<get_profile_information::v1::Response> {
+    let mut displayname = None;
+    let mut avatar_url = None;
+
+    match body.field {
+        Some(ProfileField::DisplayName) => displayname = db.users.displayname(&body.user_id)?,
+        Some(ProfileField::AvatarUrl) => avatar_url = db.users.avatar_url(&body.user_id)?,
+        None => {
+            displayname = db.users.displayname(&body.user_id)?;
+            avatar_url = db.users.avatar_url(&body.user_id)?;
+        }
+    }
+
+    Ok(get_profile_information::v1::Response {
+        displayname,
+        avatar_url,
+    }
+    .into())
+}
+
+/*
+#[cfg_attr(
+    feature = "conduit_bin",
+    get("/_matrix/federation/v2/invite/<_>/<_>", data = "<body>")
+)]
+pub fn get_user_devices_route<'a>(
+    db: State<'a, Database>,
+    body: Ruma<membership::v1::Request<'_>>,
+) -> ConduitResult<get_profile_information::v1::Response> {
+    let mut displayname = None;
+    let mut avatar_url = None;
+
+    match body.field {
+        Some(ProfileField::DisplayName) => displayname = db.users.displayname(&body.user_id)?,
+        Some(ProfileField::AvatarUrl) => avatar_url = db.users.avatar_url(&body.user_id)?,
+        None => {
+            displayname = db.users.displayname(&body.user_id)?;
+            avatar_url = db.users.avatar_url(&body.user_id)?;
+        }
+    }
+
+    Ok(get_profile_information::v1::Response {
+        displayname,
+        avatar_url,
+    }
+    .into())
+}
+*/