@page "/Rooms/{RoomId}/Policies"
@using LibMatrix
@using ArcaneLibs.Extensions
@using LibMatrix.EventTypes.Spec.State
@using LibMatrix.EventTypes.Spec.State.Policy
@using System.Diagnostics
@using LibMatrix.RoomTypes
@using System.Collections.Frozen
@using System.Reflection
@using ArcaneLibs.Attributes
@using LibMatrix.EventTypes
@using MatrixUtils.Web.Shared.PolicyEditorComponents
Policy list editor - Editing @RoomId
@* Enable avatars (WILL EXPOSE YOUR IP TO TARGET HOMESERVERS!) *@
Create new policy
@if (Loading) {
Loading...
}
else if (PolicyEventsByType is not { Count: > 0 }) {
No policies yet
}
else {
@foreach (var (type, value) in PolicyEventsByType) {
@(GetValidPolicyEventsByType(type).Count) active,
@(GetInvalidPolicyEventsByType(type).Count) invalid
(@value.Count total)
@(GetPolicyTypeName(type).ToLower())
}
@foreach (var type in KnownPolicyTypes.OrderByDescending(t => GetPolicyEventsByType(t).Count)) {
@($"{GetPolicyTypeName(type)}: {GetPolicyEventsByType(type).Count} policies")
@{
var policies = GetValidPolicyEventsByType(type);
var invalidPolicies = GetInvalidPolicyEventsByType(type);
// enumerate all properties with friendly name
var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => (x.GetFriendlyNameOrNull() ?? x.GetJsonPropertyNameOrNull()) is not null)
.Where(x => x.GetCustomAttribute() is null)
.ToFrozenSet();
var propNames = props.Select(x => x.GetFriendlyNameOrNull() ?? x.GetJsonPropertyName()!).ToFrozenSet();
}
@foreach (var name in propNames) {
@name
}
Actions
@foreach (var policy in policies.OrderBy(x => x.RawContent?["entity"]?.GetValue())) {
@{
var typedContent = policy.TypedContent!;
var proxySafeProps = typedContent.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => props.Any(y => y.Name == x.Name))
.ToFrozenSet();
Console.WriteLine($"{proxySafeProps?.Count} proxy safe props found in {policies.FirstOrDefault()?.TypedContent?.GetType()}");
}
@foreach (var prop in proxySafeProps ?? Enumerable.Empty()) {
@prop.GetGetMethod()?.Invoke(typedContent, null)
}
@if (PowerLevels.UserHasStatePermission(Homeserver.WhoAmI.UserId, policy.Type)) {
Edit
Remove
@if (policy.IsLegacyType) {
Update policy type
}
}
}
@("Invalid " + GetPolicyTypeName(type).ToLower())
State key
Json contents
@foreach (var policy in invalidPolicies) {
@policy.StateKey
@policy.RawContent.ToJson(true, false)
}
}
}
@if (CurrentlyEditingEvent is not null) {
}
@code {
#if DEBUG
private const bool Debug = true;
#else
private const bool Debug = false;
#endif
private bool Loading { get; set; } = true;
//get room list
// - sync withroom list filter
// Type = support.feline.msc3784
//support.feline.policy.lists.msc.v1
[Parameter]
public string RoomId { get; set; } = null!;
private bool _enableAvatars;
private StateEventResponse? _currentlyEditingEvent;
// static readonly Dictionary Avatars = new();
// static readonly Dictionary Servers = new();
// private static List PolicyEvents { get; set; } = new();
private Dictionary> PolicyEventsByType { get; set; } = new();
private StateEventResponse? CurrentlyEditingEvent {
get => _currentlyEditingEvent;
set {
_currentlyEditingEvent = value;
StateHasChanged();
}
}
// public bool EnableAvatars {
// get => _enableAvatars;
// set {
// _enableAvatars = value;
// if (value) GetAllAvatars();
// }
// }
private AuthenticatedHomeserverGeneric Homeserver { get; set; }
private GenericRoom Room { get; set; }
private RoomPowerLevelEventContent PowerLevels { get; set; }
protected override async Task OnInitializedAsync() {
var sw = Stopwatch.StartNew();
await base.OnInitializedAsync();
Homeserver = (await RMUStorage.GetCurrentSessionOrNavigate())!;
if (Homeserver is null) return;
Room = Homeserver.GetRoom(RoomId!);
PowerLevels = (await Room.GetPowerLevelsAsync())!;
await LoadStatesAsync();
Console.WriteLine($"Policy list editor initialized in {sw.Elapsed}!");
}
private async Task LoadStatesAsync() {
Loading = true;
var states = Room.GetFullStateAsync();
PolicyEventsByType.Clear();
await foreach (var state in states) {
if (state is null) continue;
if (!state.MappedType.IsAssignableTo(typeof(PolicyRuleEventContent))) continue;
if (!PolicyEventsByType.ContainsKey(state.MappedType)) PolicyEventsByType.Add(state.MappedType, new());
PolicyEventsByType[state.MappedType].Add(state);
}
Loading = false;
StateHasChanged();
}
// private async Task GetAllAvatars() {
// // if (!_enableAvatars) return;
// Console.WriteLine("Getting avatars...");
// var users = GetValidPolicyEventsByType(typeof(UserPolicyRuleEventContent)).Select(x => x.RawContent!["entity"]!.GetValue()).Where(x => x.Contains(':') && !x.Contains("*")).ToList();
// Console.WriteLine($"Got {users.Count} users!");
// var usersByHomeServer = users.GroupBy(x => x!.Split(':')[1]).ToDictionary(x => x.Key!, x => x.ToList());
// Console.WriteLine($"Got {usersByHomeServer.Count} homeservers!");
// var homeserverTasks = usersByHomeServer.Keys.Select(x => RemoteHomeserver.TryCreate(x)).ToAsyncEnumerable();
// await foreach (var server in homeserverTasks) {
// if (server is null) continue;
// var profileTasks = usersByHomeServer[server.BaseUrl].Select(x => TryGetProfile(server, x)).ToList();
// await Task.WhenAll(profileTasks);
// profileTasks.RemoveAll(x => x.Result is not { Value: { AvatarUrl: not null } });
// foreach (var profile in profileTasks.Select(x => x.Result!.Value)) {
// // if (profile is null) continue;
// if (!string.IsNullOrWhiteSpace(profile.Value.AvatarUrl)) {
// var url = await hsResolver.ResolveMediaUri(server.BaseUrl, profile.Value.AvatarUrl);
// Avatars.TryAdd(profile.Key, url);
// }
// else Avatars.TryAdd(profile.Key, null);
// }
//
// StateHasChanged();
// }
// }
//
// private async Task?> TryGetProfile(RemoteHomeserver server, string mxid) {
// try {
// return new KeyValuePair(mxid, await server.GetProfileAsync(mxid));
// }
// catch {
// return null;
// }
// }
private List GetPolicyEventsByType(Type type) => PolicyEventsByType.ContainsKey(type) ? PolicyEventsByType[type] : [];
private List GetValidPolicyEventsByType(Type type) => GetPolicyEventsByType(type)
.Where(x => !string.IsNullOrWhiteSpace(x.RawContent?["entity"]?.GetValue())).ToList();
private List GetInvalidPolicyEventsByType(Type type) => GetPolicyEventsByType(type)
.Where(x => string.IsNullOrWhiteSpace(x.RawContent?["entity"]?.GetValue())).ToList();
private string? GetPolicyTypeNameOrNull(Type type) => type.GetFriendlyNamePluralOrNull()
?? type.GetCustomAttributes()
.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x.EventName))?.EventName;
private string GetPolicyTypeName(Type type) => GetPolicyTypeNameOrNull(type) ?? type.Name;
private async Task RemovePolicyAsync(StateEventResponse policyEvent) {
await Room.SendStateEventAsync(policyEvent.Type, policyEvent.StateKey, new { });
PolicyEventsByType[policyEvent.MappedType].Remove(policyEvent);
await LoadStatesAsync();
}
private async Task UpdatePolicyAsync(StateEventResponse policyEvent) {
await Room.SendStateEventAsync(policyEvent.Type, policyEvent.StateKey, policyEvent.RawContent);
CurrentlyEditingEvent = null;
await LoadStatesAsync();
}
private async Task UpgradePolicyAsync(StateEventResponse policyEvent) {
policyEvent.RawContent["upgraded_from_type"] = policyEvent.Type;
await LoadStatesAsync();
}
private static FrozenSet KnownPolicyTypes = StateEvent.KnownStateEventTypes.Where(x => x.IsAssignableTo(typeof(PolicyRuleEventContent))).ToFrozenSet();
// event types, unnamed
private static Dictionary PolicyTypes = KnownPolicyTypes
.ToDictionary(x => x.GetCustomAttributes().First(y => !string.IsNullOrWhiteSpace(y.EventName)).EventName, x => x);
}