mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2025-07-29 11:18:30 +00:00
298 lines
6.9 KiB
Rust
298 lines
6.9 KiB
Rust
|
use std::{collections::BTreeMap, sync::Arc};
|
||
|
|
||
|
use conduit::{Error, Result};
|
||
|
use ruma::{
|
||
|
api::client::error::ErrorKind,
|
||
|
events::{
|
||
|
room::{
|
||
|
canonical_alias::RoomCanonicalAliasEventContent,
|
||
|
create::RoomCreateEventContent,
|
||
|
guest_access::{GuestAccess, RoomGuestAccessEventContent},
|
||
|
history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
|
||
|
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||
|
member::{MembershipState, RoomMemberEventContent},
|
||
|
name::RoomNameEventContent,
|
||
|
power_levels::RoomPowerLevelsEventContent,
|
||
|
preview_url::RoomPreviewUrlsEventContent,
|
||
|
topic::RoomTopicEventContent,
|
||
|
},
|
||
|
TimelineEventType,
|
||
|
},
|
||
|
RoomId, RoomVersionId,
|
||
|
};
|
||
|
use serde_json::value::to_raw_value;
|
||
|
use tracing::warn;
|
||
|
|
||
|
use crate::{pdu::PduBuilder, services};
|
||
|
|
||
|
/// Create the admin room.
|
||
|
///
|
||
|
/// Users in this room are considered admins by conduit, and the room can be
|
||
|
/// used to issue admin commands by talking to the server user inside it.
|
||
|
pub async fn create_admin_room() -> Result<()> {
|
||
|
let room_id = RoomId::new(services().globals.server_name());
|
||
|
|
||
|
services().rooms.short.get_or_create_shortroomid(&room_id)?;
|
||
|
|
||
|
let mutex_state = Arc::clone(
|
||
|
services()
|
||
|
.globals
|
||
|
.roomid_mutex_state
|
||
|
.write()
|
||
|
.await
|
||
|
.entry(room_id.clone())
|
||
|
.or_default(),
|
||
|
);
|
||
|
let state_lock = mutex_state.lock().await;
|
||
|
|
||
|
// Create a user for the server
|
||
|
let server_user = &services().globals.server_user;
|
||
|
services().users.create(server_user, None)?;
|
||
|
|
||
|
let room_version = services().globals.default_room_version();
|
||
|
let mut content = match room_version {
|
||
|
RoomVersionId::V1
|
||
|
| RoomVersionId::V2
|
||
|
| RoomVersionId::V3
|
||
|
| RoomVersionId::V4
|
||
|
| RoomVersionId::V5
|
||
|
| RoomVersionId::V6
|
||
|
| RoomVersionId::V7
|
||
|
| RoomVersionId::V8
|
||
|
| RoomVersionId::V9
|
||
|
| RoomVersionId::V10 => RoomCreateEventContent::new_v1(server_user.clone()),
|
||
|
RoomVersionId::V11 => RoomCreateEventContent::new_v11(),
|
||
|
_ => {
|
||
|
warn!("Unexpected or unsupported room version {}", room_version);
|
||
|
return Err(Error::BadRequest(
|
||
|
ErrorKind::BadJson,
|
||
|
"Unexpected or unsupported room version found",
|
||
|
));
|
||
|
},
|
||
|
};
|
||
|
|
||
|
content.federate = true;
|
||
|
content.predecessor = None;
|
||
|
content.room_version = room_version;
|
||
|
|
||
|
// 1. The room create event
|
||
|
services()
|
||
|
.rooms
|
||
|
.timeline
|
||
|
.build_and_append_pdu(
|
||
|
PduBuilder {
|
||
|
event_type: TimelineEventType::RoomCreate,
|
||
|
content: to_raw_value(&content).expect("event is valid, we just created it"),
|
||
|
unsigned: None,
|
||
|
state_key: Some(String::new()),
|
||
|
redacts: None,
|
||
|
},
|
||
|
server_user,
|
||
|
&room_id,
|
||
|
&state_lock,
|
||
|
)
|
||
|
.await?;
|
||
|
|
||
|
// 2. Make conduit bot join
|
||
|
services()
|
||
|
.rooms
|
||
|
.timeline
|
||
|
.build_and_append_pdu(
|
||
|
PduBuilder {
|
||
|
event_type: TimelineEventType::RoomMember,
|
||
|
content: to_raw_value(&RoomMemberEventContent {
|
||
|
membership: MembershipState::Join,
|
||
|
displayname: None,
|
||
|
avatar_url: None,
|
||
|
is_direct: None,
|
||
|
third_party_invite: None,
|
||
|
blurhash: None,
|
||
|
reason: None,
|
||
|
join_authorized_via_users_server: None,
|
||
|
})
|
||
|
.expect("event is valid, we just created it"),
|
||
|
unsigned: None,
|
||
|
state_key: Some(server_user.to_string()),
|
||
|
redacts: None,
|
||
|
},
|
||
|
server_user,
|
||
|
&room_id,
|
||
|
&state_lock,
|
||
|
)
|
||
|
.await?;
|
||
|
|
||
|
// 3. Power levels
|
||
|
let mut users = BTreeMap::new();
|
||
|
users.insert(server_user.clone(), 100.into());
|
||
|
|
||
|
services()
|
||
|
.rooms
|
||
|
.timeline
|
||
|
.build_and_append_pdu(
|
||
|
PduBuilder {
|
||
|
event_type: TimelineEventType::RoomPowerLevels,
|
||
|
content: to_raw_value(&RoomPowerLevelsEventContent {
|
||
|
users,
|
||
|
..Default::default()
|
||
|
})
|
||
|
.expect("event is valid, we just created it"),
|
||
|
unsigned: None,
|
||
|
state_key: Some(String::new()),
|
||
|
redacts: None,
|
||
|
},
|
||
|
server_user,
|
||
|
&room_id,
|
||
|
&state_lock,
|
||
|
)
|
||
|
.await?;
|
||
|
|
||
|
// 4.1 Join Rules
|
||
|
services()
|
||
|
.rooms
|
||
|
.timeline
|
||
|
.build_and_append_pdu(
|
||
|
PduBuilder {
|
||
|
event_type: TimelineEventType::RoomJoinRules,
|
||
|
content: to_raw_value(&RoomJoinRulesEventContent::new(JoinRule::Invite))
|
||
|
.expect("event is valid, we just created it"),
|
||
|
unsigned: None,
|
||
|
state_key: Some(String::new()),
|
||
|
redacts: None,
|
||
|
},
|
||
|
server_user,
|
||
|
&room_id,
|
||
|
&state_lock,
|
||
|
)
|
||
|
.await?;
|
||
|
|
||
|
// 4.2 History Visibility
|
||
|
services()
|
||
|
.rooms
|
||
|
.timeline
|
||
|
.build_and_append_pdu(
|
||
|
PduBuilder {
|
||
|
event_type: TimelineEventType::RoomHistoryVisibility,
|
||
|
content: to_raw_value(&RoomHistoryVisibilityEventContent::new(HistoryVisibility::Shared))
|
||
|
.expect("event is valid, we just created it"),
|
||
|
unsigned: None,
|
||
|
state_key: Some(String::new()),
|
||
|
redacts: None,
|
||
|
},
|
||
|
server_user,
|
||
|
&room_id,
|
||
|
&state_lock,
|
||
|
)
|
||
|
.await?;
|
||
|
|
||
|
// 4.3 Guest Access
|
||
|
services()
|
||
|
.rooms
|
||
|
.timeline
|
||
|
.build_and_append_pdu(
|
||
|
PduBuilder {
|
||
|
event_type: TimelineEventType::RoomGuestAccess,
|
||
|
content: to_raw_value(&RoomGuestAccessEventContent::new(GuestAccess::Forbidden))
|
||
|
.expect("event is valid, we just created it"),
|
||
|
unsigned: None,
|
||
|
state_key: Some(String::new()),
|
||
|
redacts: None,
|
||
|
},
|
||
|
server_user,
|
||
|
&room_id,
|
||
|
&state_lock,
|
||
|
)
|
||
|
.await?;
|
||
|
|
||
|
// 5. Events implied by name and topic
|
||
|
let room_name = format!("{} Admin Room", services().globals.server_name());
|
||
|
services()
|
||
|
.rooms
|
||
|
.timeline
|
||
|
.build_and_append_pdu(
|
||
|
PduBuilder {
|
||
|
event_type: TimelineEventType::RoomName,
|
||
|
content: to_raw_value(&RoomNameEventContent::new(room_name))
|
||
|
.expect("event is valid, we just created it"),
|
||
|
unsigned: None,
|
||
|
state_key: Some(String::new()),
|
||
|
redacts: None,
|
||
|
},
|
||
|
server_user,
|
||
|
&room_id,
|
||
|
&state_lock,
|
||
|
)
|
||
|
.await?;
|
||
|
|
||
|
services()
|
||
|
.rooms
|
||
|
.timeline
|
||
|
.build_and_append_pdu(
|
||
|
PduBuilder {
|
||
|
event_type: TimelineEventType::RoomTopic,
|
||
|
content: to_raw_value(&RoomTopicEventContent {
|
||
|
topic: format!("Manage {}", services().globals.server_name()),
|
||
|
})
|
||
|
.expect("event is valid, we just created it"),
|
||
|
unsigned: None,
|
||
|
state_key: Some(String::new()),
|
||
|
redacts: None,
|
||
|
},
|
||
|
server_user,
|
||
|
&room_id,
|
||
|
&state_lock,
|
||
|
)
|
||
|
.await?;
|
||
|
|
||
|
// 6. Room alias
|
||
|
let alias = &services().globals.admin_alias;
|
||
|
|
||
|
services()
|
||
|
.rooms
|
||
|
.timeline
|
||
|
.build_and_append_pdu(
|
||
|
PduBuilder {
|
||
|
event_type: TimelineEventType::RoomCanonicalAlias,
|
||
|
content: to_raw_value(&RoomCanonicalAliasEventContent {
|
||
|
alias: Some(alias.clone()),
|
||
|
alt_aliases: Vec::new(),
|
||
|
})
|
||
|
.expect("event is valid, we just created it"),
|
||
|
unsigned: None,
|
||
|
state_key: Some(String::new()),
|
||
|
redacts: None,
|
||
|
},
|
||
|
server_user,
|
||
|
&room_id,
|
||
|
&state_lock,
|
||
|
)
|
||
|
.await?;
|
||
|
|
||
|
services()
|
||
|
.rooms
|
||
|
.alias
|
||
|
.set_alias(alias, &room_id, server_user)?;
|
||
|
|
||
|
// 7. (ad-hoc) Disable room previews for everyone by default
|
||
|
services()
|
||
|
.rooms
|
||
|
.timeline
|
||
|
.build_and_append_pdu(
|
||
|
PduBuilder {
|
||
|
event_type: TimelineEventType::RoomPreviewUrls,
|
||
|
content: to_raw_value(&RoomPreviewUrlsEventContent {
|
||
|
disabled: true,
|
||
|
})
|
||
|
.expect("event is valid we just created it"),
|
||
|
unsigned: None,
|
||
|
state_key: Some(String::new()),
|
||
|
redacts: None,
|
||
|
},
|
||
|
server_user,
|
||
|
&room_id,
|
||
|
&state_lock,
|
||
|
)
|
||
|
.await?;
|
||
|
|
||
|
Ok(())
|
||
|
}
|