From a6bf50c56781724093cc8461b5d6469dec8bfed7 Mon Sep 17 00:00:00 2001
From: HueByte
Date: Fri, 17 Jul 2026 19:09:56 +0000
Subject: [PATCH] deploy: 0c2e8eae8780c50a818660e33c2c6ff4f2b964eb
---
...choHub.Client.Commands.CommandHandler.html | 219 +-
.../EchoHub.Client.Services.ApiClient.html | 182 +-
...Hub.Client.Services.EchoHubConnection.html | 38 +-
...hoHub.Client.UI.Chat.AttachmentAction.html | 154 +
...b.Client.UI.Chat.AttachmentActionSpan.html | 554 +++
.../EchoHub.Client.UI.Chat.ChatLine.html | 67 +
api/client/EchoHub.Client.UI.Chat.html | 16 +
...Client.UI.Dialogs.ConnectDialogResult.html | 82 +-
...ient.UI.ListSources.ChannelListSource.html | 8 +-
api/client/EchoHub.Client.UI.MainWindow.html | 166 +-
...Hub.Core.Constants.MessageConventions.html | 322 ++
api/core/EchoHub.Core.Constants.html | 8 +
...choHub.Core.Contracts.IChannelService.html | 38 +
...hoHub.Core.Contracts.IChatBroadcaster.html | 51 +-
.../EchoHub.Core.Contracts.IChatService.html | 46 +-
...EchoHub.Core.Contracts.IEchoHubClient.html | 36 +
.../EchoHub.Core.Contracts.IUserService.html | 8 +-
api/core/EchoHub.Core.DTOs.ChannelDto.html | 47 +-
...EchoHub.Core.DTOs.CreateInviteRequest.html | 625 +++
...choHub.Core.DTOs.DeleteAccountRequest.html | 592 +++
...hoHub.Core.DTOs.ExportedAttachmentDto.html | 765 ++++
.../EchoHub.Core.DTOs.ExportedMessageDto.html | 730 ++++
api/core/EchoHub.Core.DTOs.InviteDto.html | 765 ++++
api/core/EchoHub.Core.DTOs.MessageDto.html | 47 +-
.../EchoHub.Core.DTOs.RegisterRequest.html | 47 +-
api/core/EchoHub.Core.DTOs.ReplyRefDto.html | 668 +++
.../EchoHub.Core.DTOs.ServerStatusDto.html | 47 +-
.../EchoHub.Core.DTOs.UserDataExportDto.html | 734 ++++
api/core/EchoHub.Core.DTOs.html | 35 +
api/core/EchoHub.Core.Models.Channel.html | 31 +
api/core/EchoHub.Core.Models.InviteCode.html | 461 ++
api/core/EchoHub.Core.Models.Message.html | 32 +
api/core/EchoHub.Core.Models.html | 6 +
...oHub.Core.Services.AsciiBannerService.html | 256 ++
api/core/EchoHub.Core.Services.html | 7 +
.../EchoHub.Server.Irc.IrcBroadcaster.html | 51 +-
...choHub.Server.Irc.IrcMessageFormatter.html | 91 +-
.../EchoHub.Server.Irc.IrcOptions.html | 34 +
...hoHub.Server.Config.ServerLogsOptions.html | 469 ++
.../EchoHub.Server.Config.SpamOptions.html | 564 +++
api/server/EchoHub.Server.Config.html | 14 +
...ub.Server.Controllers.FilesController.html | 7 +-
....Server.Controllers.InvitesController.html | 330 ++
...ub.Server.Controllers.UsersController.html | 132 +-
api/server/EchoHub.Server.Controllers.html | 6 +
.../EchoHub.Server.Data.EchoHubDbContext.html | 31 +
...er.Data.Migrations.AddChannelIsSystem.html | 306 ++
....Data.Migrations.AddInvitesAndReplies.html | 306 ++
.../EchoHub.Server.Data.Migrations.html | 8 +
api/server/EchoHub.Server.Hubs.ChatHub.html | 8 +-
...choHub.Server.Services.ChannelService.html | 48 +-
.../EchoHub.Server.Services.ChatService.html | 56 +-
...r.Services.ServerLogs.LogBacklogEntry.html | 629 +++
...Services.ServerLogs.ServerLogsService.html | 465 ++
...er.Services.ServerLogs.ServerLogsSink.html | 289 ++
...es.ServerLogs.ServerLogsStreamService.html | 306 ++
.../EchoHub.Server.Services.ServerLogs.html | 154 +
...ub.Server.Services.SignalRBroadcaster.html | 51 +-
.../EchoHub.Server.Services.SpamGuard.html | 379 ++
.../EchoHub.Server.Services.SpamVerdict.html | 586 +++
...hoHub.Server.Services.SpamVerdictKind.html | 157 +
.../EchoHub.Server.Services.UserService.html | 52 +-
api/server/EchoHub.Server.Services.html | 23 +
api/toc.html | 79 +
api/toc.json | 2 +-
articles/configuration.html | 147 +-
changelog/index.html | 1 +
changelog/toc.html | 3 +
changelog/toc.json | 2 +-
changelog/v0.2.15.html | 192 +
index.json | 210 +-
manifest.json | 360 ++
xrefmap.yml | 3882 +++++++++++++++--
73 files changed, 17893 insertions(+), 427 deletions(-)
create mode 100644 api/client/EchoHub.Client.UI.Chat.AttachmentAction.html
create mode 100644 api/client/EchoHub.Client.UI.Chat.AttachmentActionSpan.html
create mode 100644 api/core/EchoHub.Core.Constants.MessageConventions.html
create mode 100644 api/core/EchoHub.Core.DTOs.CreateInviteRequest.html
create mode 100644 api/core/EchoHub.Core.DTOs.DeleteAccountRequest.html
create mode 100644 api/core/EchoHub.Core.DTOs.ExportedAttachmentDto.html
create mode 100644 api/core/EchoHub.Core.DTOs.ExportedMessageDto.html
create mode 100644 api/core/EchoHub.Core.DTOs.InviteDto.html
create mode 100644 api/core/EchoHub.Core.DTOs.ReplyRefDto.html
create mode 100644 api/core/EchoHub.Core.DTOs.UserDataExportDto.html
create mode 100644 api/core/EchoHub.Core.Models.InviteCode.html
create mode 100644 api/core/EchoHub.Core.Services.AsciiBannerService.html
create mode 100644 api/server/EchoHub.Server.Config.ServerLogsOptions.html
create mode 100644 api/server/EchoHub.Server.Config.SpamOptions.html
create mode 100644 api/server/EchoHub.Server.Controllers.InvitesController.html
create mode 100644 api/server/EchoHub.Server.Data.Migrations.AddChannelIsSystem.html
create mode 100644 api/server/EchoHub.Server.Data.Migrations.AddInvitesAndReplies.html
create mode 100644 api/server/EchoHub.Server.Services.ServerLogs.LogBacklogEntry.html
create mode 100644 api/server/EchoHub.Server.Services.ServerLogs.ServerLogsService.html
create mode 100644 api/server/EchoHub.Server.Services.ServerLogs.ServerLogsSink.html
create mode 100644 api/server/EchoHub.Server.Services.ServerLogs.ServerLogsStreamService.html
create mode 100644 api/server/EchoHub.Server.Services.ServerLogs.html
create mode 100644 api/server/EchoHub.Server.Services.SpamGuard.html
create mode 100644 api/server/EchoHub.Server.Services.SpamVerdict.html
create mode 100644 api/server/EchoHub.Server.Services.SpamVerdictKind.html
create mode 100644 changelog/v0.2.15.html
diff --git a/api/client/EchoHub.Client.Commands.CommandHandler.html b/api/client/EchoHub.Client.Commands.CommandHandler.html
index 0c1c5b7..55e77bb 100644
--- a/api/client/EchoHub.Client.Commands.CommandHandler.html
+++ b/api/client/EchoHub.Client.Commands.CommandHandler.html
@@ -379,6 +379,96 @@ Class CommandHandler
+
+
+
+
+
+
+
public event Func<int?, int?, Task>? OnCreateInvite
+
+
+
+
+
+
+
+ Event Type
+
+ Func <int ?, int ?, Task >
+
+
+
+
+
+
+
+
+
+
+
+ OnDeleteAccount
+
+
+
+
+
+
+
+
public event Func<Task>? OnDeleteAccount
+
+
+
+
+
+
+
+ Event Type
+
+ Func <Task >
+
+
+
+
+
+
+
+
+
+
+
+ OnExportData
+
+
+
+
+
+
+
+
public event Func<Task>? OnExportData
+
+
+
+
+
+
+
+ Event Type
+
+ Func <Task >
+
+
+
+
+
+
+
+
+
+
OnHelp
@@ -499,6 +589,36 @@ Class CommandHandler
+
+
+
+
+
+
+
public event Func<Task>? OnListInvites
+
+
+
+
+
+
+
+ Event Type
+
+ Func <Task >
+
+
+
+
+
+
+
+
+
+
OnListUsers
@@ -679,6 +799,36 @@ Class CommandHandler
+
+
+
+
+
+
+
public event Func<string, Task>? OnRevokeInvite
+
+
+
+
+
+
+
+ Event Type
+
+ Func <string , Task >
+
+
+
+
+
+
+
+
+
+
OnRoomInfo
@@ -709,6 +859,66 @@ Class CommandHandler
+
+
+
+
+
+
+
public event Func<string, Task>? OnSendAction
+
+
+
+
+
+
+
+ Event Type
+
+ Func <string , Task >
+
+
+
+
+
+
+
+
+
+
+
+ OnSendBanner
+
+
+
+
+
+
+
+
public event Func<string, Task>? OnSendBanner
+
+
+
+
+
+
+
+ Event Type
+
+ Func <string , Task >
+
+
+
+
+
+
+
+
+
+
OnSendFile
@@ -894,11 +1104,14 @@ Class CommandHandler
-
+ Status update. A null status means "keep the current status"; a null message means
+"keep the current message" and an empty message means "clear it". The orchestrator
+resolves both against the session state.
+
-
public event Func<UserStatus, string?, Task>? OnSetStatus
+
public event Func<UserStatus?, string?, Task>? OnSetStatus
@@ -908,7 +1121,7 @@ Class CommandHandler
+
+
+ CreateInviteAsync(int?, int?)
+
+
+
+
+
+
+
+
public Task<InviteDto?> CreateInviteAsync(int? maxUses = null, int? expiresInHours = null)
+
+
+ Parameters
+
+ maxUses int ?
+
+ expiresInHours int ?
+
+
+
+ Returns
+
+ Task <InviteDto >
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -481,6 +519,43 @@ Class ApiClient
+
+
+ DeleteMyAccountAsync(string)
+
+
+
+ Deletes the caller's account. The password re-confirms intent.
+
+
+
+
+
public Task DeleteMyAccountAsync(string password)
+
+
+ Parameters
+
+ password string
+
+
+
+ Returns
+
+ Task
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -545,6 +620,38 @@ Class ApiClient
+
+
+ ExportMyDataAsync()
+
+
+
+ Downloads the caller's full data export as raw JSON text.
+
+
+
+
+
public Task<string> ExportMyDataAsync()
+
+
+
+ Returns
+
+ Task <string >
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -683,6 +790,37 @@ size, created date, room id) for the /meta command. Returns null if
+
+
+
+ GetInvitesAsync()
+
+
+
+
+
+
+
+
public Task<List<InviteDto>> GetInvitesAsync()
+
+
+
+ Returns
+
+ Task <List <InviteDto >>
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1035,8 +1173,8 @@ Used by EchoHubConnection for SignalR token provider.
-
- RegisterAsync(string, string, string?)
+
+ RegisterAsync(string, string, string?, string?)
@@ -1044,7 +1182,7 @@ Used by EchoHubConnection for SignalR token provider.
-
public Task<LoginResponse> RegisterAsync(string username, string password, string? displayName = null)
+
public Task<LoginResponse> RegisterAsync(string username, string password, string? displayName = null, string? inviteCode = null)
Parameters
@@ -1055,6 +1193,8 @@ Used by EchoHubConnection for SignalR token provider.
displayName string
+ inviteCode string
+
Returns
@@ -1111,6 +1251,42 @@ Used by EchoHubConnection for SignalR token provider.
+
+
+
+ RevokeInviteAsync(string)
+
+
+
+
+
+
+
+
public Task RevokeInviteAsync(string code)
+
+
+ Parameters
+
+ code string
+
+
+
+ Returns
+
+ Task
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/api/client/EchoHub.Client.Services.EchoHubConnection.html b/api/client/EchoHub.Client.Services.EchoHubConnection.html
index b25f25f..de6e1f3 100644
--- a/api/client/EchoHub.Client.Services.EchoHubConnection.html
+++ b/api/client/EchoHub.Client.Services.EchoHubConnection.html
@@ -510,8 +510,8 @@ Class EchoHubConnection
-
- SendMessageAsync(string, string)
+
+ SendMessageAsync(string, string, Guid?)
@@ -519,7 +519,7 @@ Class EchoHubConnection
+ replyToMessageId Guid ?
+
Returns
@@ -589,6 +591,36 @@ Class EchoHubConnection
+
+
+
+
+
+
+
public event Action<string>? OnChannelDeleted
+
+
+
+
+
+
+
+ Event Type
+
+ Action <string >
+
+
+
+
+
+
+
+
+
+
OnChannelNuked
diff --git a/api/client/EchoHub.Client.UI.Chat.AttachmentAction.html b/api/client/EchoHub.Client.UI.Chat.AttachmentAction.html
new file mode 100644
index 0000000..13f7302
--- /dev/null
+++ b/api/client/EchoHub.Client.UI.Chat.AttachmentAction.html
@@ -0,0 +1,154 @@
+
+
+
+
+ Enum AttachmentAction | EchoHub Documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Enum AttachmentAction
+
+
+
+
Namespace EchoHub .Client .UI .Chat
+
Assembly EchoHub.Client.dll
+
+
+ An action a click on an attachment line can trigger.
+
+
+
+
+
public enum AttachmentAction
+
+
+
+
+
+
+
+
+
+
+ Fields
+
+
+ OpenImage = 0
+
+
+ SaveImage = 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/api/client/EchoHub.Client.UI.Chat.AttachmentActionSpan.html b/api/client/EchoHub.Client.UI.Chat.AttachmentActionSpan.html
new file mode 100644
index 0000000..8219c1d
--- /dev/null
+++ b/api/client/EchoHub.Client.UI.Chat.AttachmentActionSpan.html
@@ -0,0 +1,554 @@
+
+
+
+
+ Struct AttachmentActionSpan | EchoHub Documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Struct AttachmentActionSpan
+
+
+
+
Namespace EchoHub .Client .UI .Chat
+
Assembly EchoHub.Client.dll
+
+
+ Inclusive column range on a chat line that triggers an attachment action when clicked.
+
+
+
+
+
public readonly struct AttachmentActionSpan : IEquatable<AttachmentActionSpan>
+
+
+
+
+
+
+
+ Implements
+
+
+
+
+
+
+
+ Inherited Members
+
+
+
+
+
+
+
+
+
+
+
+ Constructors
+
+
+
+
+
+
+ AttachmentActionSpan(int, int, AttachmentAction)
+
+
+
+ Inclusive column range on a chat line that triggers an attachment action when clicked.
+
+
+
+
+
public AttachmentActionSpan(int StartCol, int EndCol, AttachmentAction Action)
+
+
+ Parameters
+
+ StartCol int
+
+ EndCol int
+
+ Action AttachmentAction
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Properties
+
+
+
+
+
+
+ Action
+
+
+
+
+
+
+
+
public AttachmentAction Action { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ AttachmentAction
+
+
+
+
+
+
+
+
+
+
+
+
+
+ EndCol
+
+
+
+
+
+
+
+
public int EndCol { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ int
+
+
+
+
+
+
+
+
+
+
+
+
+
+ StartCol
+
+
+
+
+
+
+
+
public int StartCol { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ int
+
+
+
+
+
+
+
+
+
+
+ Methods
+
+
+
+
+
+
+ Deconstruct(out int, out int, out AttachmentAction)
+
+
+
+
+
+
+
+
public void Deconstruct(out int StartCol, out int EndCol, out AttachmentAction Action)
+
+
+ Parameters
+
+ StartCol int
+
+ EndCol int
+
+ Action AttachmentAction
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Equals(AttachmentActionSpan)
+
+
+
+
+
+
+
+
public bool Equals(AttachmentActionSpan other)
+
+
+ Parameters
+
+ other AttachmentActionSpan
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Equals(object)
+
+
+
+
+
+
+
+
public override bool Equals(object obj)
+
+
+ Parameters
+
+ obj object
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GetHashCode()
+
+
+
+
+
+
+
+
public override int GetHashCode()
+
+
+
+ Returns
+
+ int
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ToString()
+
+
+
+
+
+
+
+
public override string ToString()
+
+
+
+ Returns
+
+ string
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Operators
+
+
+
+
+
+
+ operator ==(AttachmentActionSpan, AttachmentActionSpan)
+
+
+
+
+
+
+
+
public static bool operator ==(AttachmentActionSpan left, AttachmentActionSpan right)
+
+
+ Parameters
+
+ left AttachmentActionSpan
+
+ right AttachmentActionSpan
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ operator !=(AttachmentActionSpan, AttachmentActionSpan)
+
+
+
+
+
+
+
+
public static bool operator !=(AttachmentActionSpan left, AttachmentActionSpan right)
+
+
+ Parameters
+
+ left AttachmentActionSpan
+
+ right AttachmentActionSpan
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/api/client/EchoHub.Client.UI.Chat.ChatLine.html b/api/client/EchoHub.Client.UI.Chat.ChatLine.html
index 4892807..b8d0793 100644
--- a/api/client/EchoHub.Client.UI.Chat.ChatLine.html
+++ b/api/client/EchoHub.Client.UI.Chat.ChatLine.html
@@ -216,6 +216,40 @@ Class ChatLine
+
+
+ ActionSpans
+
+
+
+ Clickable sub-line targets (e.g. the "[open]" and "[save original]" brackets under an
+image). Columns are relative to the unwrapped line, so only the first wrapped line
+keeps them. Null means the whole line uses the kind's default action.
+
+
+
+
+
public List<AttachmentActionSpan>? ActionSpans { get; set; }
+
+
+
+
+
+
+ Property Value
+
+ List <AttachmentActionSpan >
+
+
+
+
+
+
+
+
+
+
@@ -438,6 +472,39 @@ precedence over
+
+
+ JumpToMessageId
+
+
+
+ Set on a reply's quote line: activating the line jumps to this message
+if it is in the loaded history.
+
+
+
+
+
public Guid? JumpToMessageId { get; set; }
+
+
+
+
+
+
+ Property Value
+
+ Guid ?
+
+
+
+
+
+
+
+
+
+
diff --git a/api/client/EchoHub.Client.UI.Chat.html b/api/client/EchoHub.Client.UI.Chat.html
index 354aabd..d445584 100644
--- a/api/client/EchoHub.Client.UI.Chat.html
+++ b/api/client/EchoHub.Client.UI.Chat.html
@@ -121,6 +121,22 @@ so the UI layer can refresh.
A colored text segment within a chat line.
+
+Structs
+
+
+ AttachmentActionSpan
+ Inclusive column range on a chat line that triggers an attachment action when clicked.
+
+
+
+Enums
+
+
+ AttachmentAction
+ An action a click on an attachment line can trigger.
+
+
diff --git a/api/client/EchoHub.Client.UI.Dialogs.ConnectDialogResult.html b/api/client/EchoHub.Client.UI.Dialogs.ConnectDialogResult.html
index db84606..853a359 100644
--- a/api/client/EchoHub.Client.UI.Dialogs.ConnectDialogResult.html
+++ b/api/client/EchoHub.Client.UI.Dialogs.ConnectDialogResult.html
@@ -183,8 +183,8 @@ Class ConnectDialogResult
-
- ConnectDialogResult(string, string, string, bool, bool, string?)
+
+ ConnectDialogResult(string, string, string, bool, bool, string?, string?, string?)
@@ -193,7 +193,7 @@ Class ConnectDialogResult
+ DisplayName string
+
+ InviteCode string
+
@@ -227,6 +231,37 @@ Class ConnectDialogResult
+
+
+ DisplayName
+
+
+
+
+
+
+
+
public string? DisplayName { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ string
+
+
+
+
+
+
+
+
+
+
@@ -258,6 +293,37 @@ Class ConnectDialogResult
+
+
+ InviteCode
+
+
+
+
+
+
+
+
public string? InviteCode { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ string
+
+
+
+
+
+
+
+
+
+
@@ -450,8 +516,8 @@ Class ConnectDialogResult
-
- Deconstruct(out string, out string, out string, out bool, out bool, out string?)
+
+ Deconstruct(out string, out string, out string, out bool, out bool, out string?, out string?, out string?)
@@ -459,7 +525,7 @@ Class ConnectDialogResult
+ DisplayName string
+
+ InviteCode string
+
diff --git a/api/client/EchoHub.Client.UI.ListSources.ChannelListSource.html b/api/client/EchoHub.Client.UI.ListSources.ChannelListSource.html
index 11a9463..7f73ed1 100644
--- a/api/client/EchoHub.Client.UI.ListSources.ChannelListSource.html
+++ b/api/client/EchoHub.Client.UI.ListSources.ChannelListSource.html
@@ -459,8 +459,8 @@ Active channel gets a > indicator, unread channels are bright with a count ba
-
- Update(List<string>, Dictionary<string, int>, string, IReadOnlySet<string>?, IReadOnlySet<string>?, IReadOnlySet<string>?)
+
+ Update(List<string>, Dictionary<string, int>, string, IReadOnlySet<string>?, IReadOnlySet<string>?, IReadOnlySet<string>?, IReadOnlySet<string>?)
@@ -468,7 +468,7 @@ Active channel gets a > indicator, unread channels are bright with a count ba
-
public void Update(List<string> channels, Dictionary<string, int> unread, string activeChannel, IReadOnlySet<string>? protectedChannels = null, IReadOnlySet<string>? mentionChannels = null, IReadOnlySet<string>? privateChannels = null)
+
public void Update(List<string> channels, Dictionary<string, int> unread, string activeChannel, IReadOnlySet<string>? protectedChannels = null, IReadOnlySet<string>? mentionChannels = null, IReadOnlySet<string>? privateChannels = null, IReadOnlySet<string>? systemChannels = null)
Parameters
@@ -485,6 +485,8 @@ Active channel gets a > indicator, unread channels are bright with a count ba
privateChannels IReadOnlySet <string >
+ systemChannels IReadOnlySet <string >
+
diff --git a/api/client/EchoHub.Client.UI.MainWindow.html b/api/client/EchoHub.Client.UI.MainWindow.html
index f5992f1..9236d39 100644
--- a/api/client/EchoHub.Client.UI.MainWindow.html
+++ b/api/client/EchoHub.Client.UI.MainWindow.html
@@ -882,6 +882,37 @@ Class MainWindow
+
+
+ HasPendingReplyIndicator
+
+
+
+
+
+
+
+
public bool HasPendingReplyIndicator { get; }
+
+
+
+
+
+
+ Property Value
+
+ bool
+
+
+
+
+
+
+
+
+
+
Methods
@@ -943,8 +974,8 @@ Call after theme changes to refresh colors.
-
- EnsureChannelInList(string, bool?, bool?)
+
+ EnsureChannelInList(string, bool?, bool?, bool?)
@@ -953,7 +984,7 @@ Call after theme changes to refresh colors.
-
public void EnsureChannelInList(string channelName, bool? isPublic = null, bool? isProtected = null)
+
public void EnsureChannelInList(string channelName, bool? isPublic = null, bool? isProtected = null, bool? isSystem = null)
Parameters
@@ -964,6 +995,8 @@ Call after theme changes to refresh colors.
isProtected bool ?
+ isSystem bool ?
+
@@ -1193,6 +1226,38 @@ Call after theme changes to refresh colors.
+
+
+
+ SetReplyingTo(string?)
+
+
+
+ Shows/clears the "replying to" strip on the input frame's title. Pass null to clear.
+
+
+
+
+
public void SetReplyingTo(string? label)
+
+
+ Parameters
+
+ label string
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1730,6 +1795,38 @@ Parameters: channel name, absolute paths of existing files.
+
+ OnImageOpenRequested
+
+
+
+ Fired when the user activates an image's "[open]" action to view it without saving.
+Parameters: attachmentUrl, fileName.
+
+
+
+
+
public event Action<string, string>? OnImageOpenRequested
+
+
+
+
+
+
+
+ Event Type
+
+ Action <string , string >
+
+
+
+
+
+
+
+
+
+
OnImagePasted
@@ -1917,6 +2014,69 @@ screenshot tool). Parameters: channel name, PNG-encoded image bytes.
+
+ OnReplyCancelRequested
+
+
+
+ Fired when the user cancels a pending reply (Esc in the input field).
+
+
+
+
+
public event Action? OnReplyCancelRequested
+
+
+
+
+
+
+
+ Event Type
+
+ Action
+
+
+
+
+
+
+
+
+
+
+
+ OnReplyRequested
+
+
+
+ Fired when the user picks "Reply" on a message. Parameters: message id, sender username,
+a short plain-text snippet for the reply strip.
+
+
+
+
+
public event Action<Guid, string, string>? OnReplyRequested
+
+
+
+
+
+
+
+ Event Type
+
+ Action <Guid , string , string >
+
+
+
+
+
+
+
+
+
+
OnRollbackRequested
diff --git a/api/core/EchoHub.Core.Constants.MessageConventions.html b/api/core/EchoHub.Core.Constants.MessageConventions.html
new file mode 100644
index 0000000..c96562e
--- /dev/null
+++ b/api/core/EchoHub.Core.Constants.MessageConventions.html
@@ -0,0 +1,322 @@
+
+
+
+
+ Class MessageConventions | EchoHub Documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Class MessageConventions
+
+
+
+
Namespace EchoHub .Core .Constants
+
Assembly EchoHub.Core.dll
+
+
+ Cross-protocol message conventions. Action messages (/me) use the IRC CTCP ACTION
+wire format 0x01 + "ACTION " + text + 0x01 as the stored content, so IRC clients
+interoperate natively (irssi's /me arrives in exactly this shape) and the TUI renders
+"* nick text". In end-to-end encrypted rooms the marker encrypts along with the text.
+
+
+
+
+
public static class MessageConventions
+
+
+
+
+
+
+ Inheritance
+
+
+ MessageConventions
+
+
+
+
+
+
+ Inherited Members
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Fields
+
+
+
+
+
+ ActionPrefix
+
+
+
+
+
+
+
+
public const string ActionPrefix = "\u0001ACTION "
+
+
+
+
+
+ Field Value
+
+ string
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ActionSuffix
+
+
+
+
+
+
+
+
public const string ActionSuffix = "\u0001"
+
+
+
+
+
+ Field Value
+
+ string
+
+
+
+
+
+
+
+
+
+
+
+ Methods
+
+
+
+
+
+
+
+
+
+
+
+
public static string FormatAction(string text)
+
+
+ Parameters
+
+ text string
+
+
+
+ Returns
+
+ string
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TryParseAction(string, out string?)
+
+
+
+
+
+
+
+
public static bool TryParseAction(string content, out string? actionText)
+
+
+ Parameters
+
+ content string
+
+ actionText string
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/api/core/EchoHub.Core.Constants.html b/api/core/EchoHub.Core.Constants.html
index b78ecc8..2f12e38 100644
--- a/api/core/EchoHub.Core.Constants.html
+++ b/api/core/EchoHub.Core.Constants.html
@@ -98,6 +98,14 @@ Classes
HubConstants
+
+ MessageConventions
+ Cross-protocol message conventions. Action messages (/me) use the IRC CTCP ACTION
+wire format 0x01 + "ACTION " + text + 0x01 as the stored content, so IRC clients
+interoperate natively (irssi's /me arrives in exactly this shape) and the TUI renders
+"* nick text". In end-to-end encrypted rooms the marker encrypts along with the text.
+
+
ValidationConstants
diff --git a/api/core/EchoHub.Core.Contracts.IChannelService.html b/api/core/EchoHub.Core.Contracts.IChannelService.html
index c529e2e..3b8d36b 100644
--- a/api/core/EchoHub.Core.Contracts.IChannelService.html
+++ b/api/core/EchoHub.Core.Contracts.IChannelService.html
@@ -246,6 +246,44 @@ Interface IChannelService
+
+
+ EnsureSystemChannelAsync(string, string?)
+
+
+
+
+
+
+
+
Task<ChannelDto> EnsureSystemChannelAsync(string channelName, string? topic = null)
+
+
+ Parameters
+
+ channelName string
+
+ topic string
+
+
+
+ Returns
+
+ Task <ChannelDto >
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/api/core/EchoHub.Core.Contracts.IChatBroadcaster.html b/api/core/EchoHub.Core.Contracts.IChatBroadcaster.html
index 2ee6028..3748329 100644
--- a/api/core/EchoHub.Core.Contracts.IChatBroadcaster.html
+++ b/api/core/EchoHub.Core.Contracts.IChatBroadcaster.html
@@ -158,6 +158,42 @@ Interface IChatBroadcaster
+
+
+ SendChannelDeletedAsync(string)
+
+
+
+
+
+
+
+
Task SendChannelDeletedAsync(string channelName)
+
+
+ Parameters
+
+ channelName string
+
+
+
+ Returns
+
+ Task
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -310,16 +346,21 @@ Interface IChatBroadcaster
-
- SendMessageToChannelAsync(string, MessageDto)
+
+ SendMessageToChannelAsync(string, MessageDto, string?)
-
+ Broadcast a chat message to a channel. excludeConnectionId is the
+connection the message originated from (IRC convention: never echo a message back to
+the connection that sent it — its client already displayed it locally). Other
+connections of the same user (e.g. an IRC session alongside a TUI session) still
+receive the message.
+
-
Task SendMessageToChannelAsync(string channelName, MessageDto message)
+
Task SendMessageToChannelAsync(string channelName, MessageDto message, string? excludeConnectionId = null)
Parameters
@@ -328,6 +369,8 @@ Interface IChatBroadcaster
+ excludeConnectionId string
+
Returns
diff --git a/api/core/EchoHub.Core.Contracts.IChatService.html b/api/core/EchoHub.Core.Contracts.IChatService.html
index 3798d96..b753e17 100644
--- a/api/core/EchoHub.Core.Contracts.IChatService.html
+++ b/api/core/EchoHub.Core.Contracts.IChatService.html
@@ -120,6 +120,42 @@ Interface IChatService
+
+
+ BroadcastChannelDeletedAsync(string)
+
+
+
+
+
+
+
+
Task BroadcastChannelDeletedAsync(string channelName)
+
+
+ Parameters
+
+ channelName string
+
+
+
+ Returns
+
+ Task
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -394,8 +430,8 @@ Interface IChatService
-
- SendMessageAsync(Guid, string, string, string)
+
+ SendMessageAsync(Guid, string, string, string, string?, Guid?)
@@ -403,7 +439,7 @@ Interface IChatService
+ originConnectionId string
+
+ replyToMessageId Guid ?
+
Returns
diff --git a/api/core/EchoHub.Core.Contracts.IEchoHubClient.html b/api/core/EchoHub.Core.Contracts.IEchoHubClient.html
index d2bd040..b4117aa 100644
--- a/api/core/EchoHub.Core.Contracts.IEchoHubClient.html
+++ b/api/core/EchoHub.Core.Contracts.IEchoHubClient.html
@@ -121,6 +121,42 @@ Interface IEchoHubClient
+
+
+ ChannelDeleted(string)
+
+
+
+
+
+
+
+
Task ChannelDeleted(string channelName)
+
+
+ Parameters
+
+ channelName string
+
+
+
+ Returns
+
+ Task
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/api/core/EchoHub.Core.Contracts.IUserService.html b/api/core/EchoHub.Core.Contracts.IUserService.html
index b75b1e0..1a93a17 100644
--- a/api/core/EchoHub.Core.Contracts.IUserService.html
+++ b/api/core/EchoHub.Core.Contracts.IUserService.html
@@ -232,8 +232,8 @@ Interface IUserService
-
- RegisterUserAsync(string, string, string?)
+
+ RegisterUserAsync(string, string, string?, string?)
@@ -241,7 +241,7 @@ Interface IUserService
+ inviteCode string
+
Returns
diff --git a/api/core/EchoHub.Core.DTOs.ChannelDto.html b/api/core/EchoHub.Core.DTOs.ChannelDto.html
index dbcc195..46444db 100644
--- a/api/core/EchoHub.Core.DTOs.ChannelDto.html
+++ b/api/core/EchoHub.Core.DTOs.ChannelDto.html
@@ -182,8 +182,8 @@ Class ChannelDto
-
- ChannelDto(Guid, string, string?, bool, int, DateTimeOffset, bool, bool)
+
+ ChannelDto(Guid, string, string?, bool, int, DateTimeOffset, bool, bool, bool)
@@ -191,7 +191,7 @@ Class ChannelDto
+
+
+ IsSystem
+
+
+
+
+
+
+
+
public bool IsSystem { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ bool
+
+
+
+
+
+
+
+
+
+
@@ -514,8 +547,8 @@ Class ChannelDto
-
- Deconstruct(out Guid, out string, out string?, out bool, out int, out DateTimeOffset, out bool, out bool)
+
+ Deconstruct(out Guid, out string, out string?, out bool, out int, out DateTimeOffset, out bool, out bool, out bool)
@@ -523,7 +556,7 @@ Class ChannelDto
-
- MessageDto(Guid, string, string, string?, string, DateTimeOffset, List<AttachmentDto>?, List<EmbedDto>?, string?)
+
+ MessageDto(Guid, string, string, string?, string, DateTimeOffset, List<AttachmentDto>?, List<EmbedDto>?, string?, ReplyRefDto?)
@@ -191,7 +191,7 @@ Class MessageDto
+
+
+ ReplyTo
+
+
+
+
+
+
+
+
public ReplyRefDto? ReplyTo { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ ReplyRefDto
+
+
+
+
+
+
+
+
+
+
@@ -547,8 +580,8 @@ Class MessageDto
-
- Deconstruct(out Guid, out string, out string, out string?, out string, out DateTimeOffset, out List<AttachmentDto>?, out List<EmbedDto>?, out string?)
+
+ Deconstruct(out Guid, out string, out string, out string?, out string, out DateTimeOffset, out List<AttachmentDto>?, out List<EmbedDto>?, out string?, out ReplyRefDto?)
@@ -556,7 +589,7 @@ Class MessageDto
-
- RegisterRequest(string, string, string?)
+
+ RegisterRequest(string, string, string?, string?)
@@ -191,7 +191,7 @@ Class RegisterRequest
+ InviteCode string
+
@@ -281,6 +283,37 @@ Class RegisterRequest
+
+
+ InviteCode
+
+
+
+
+
+
+
+
public string? InviteCode { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ string
+
+
+
+
+
+
+
+
+
+
@@ -349,8 +382,8 @@ Class RegisterRequest
-
- Deconstruct(out string, out string, out string?)
+
+ Deconstruct(out string, out string, out string?, out string?)
@@ -358,7 +391,7 @@ Class RegisterRequest
+ InviteCode string
+
diff --git a/api/core/EchoHub.Core.DTOs.ReplyRefDto.html b/api/core/EchoHub.Core.DTOs.ReplyRefDto.html
new file mode 100644
index 0000000..60d999a
--- /dev/null
+++ b/api/core/EchoHub.Core.DTOs.ReplyRefDto.html
@@ -0,0 +1,668 @@
+
+
+
+
+ Class ReplyRefDto | EchoHub Documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Class ReplyRefDto
+
+
+
+
Namespace EchoHub .Core .DTOs
+
Assembly EchoHub.Core.dll
+
+
+ Reference to the message a reply targets. Content is treated exactly like
+message content on the wire: transport-encrypted, and for end-to-end encrypted rooms it is
+room ciphertext the client must decrypt (the server truncates only plaintext snippets).
+Null on a MessageDto when the original message no longer exists.
+
+
+
+
+
public record ReplyRefDto : IEquatable<ReplyRefDto>
+
+
+
+
+
+
+ Inheritance
+
+
+ ReplyRefDto
+
+
+
+
+ Implements
+
+
+
+
+
+
+
+ Inherited Members
+
+
+
+
+
+
+
+
+
+
+
+
+ Constructors
+
+
+
+
+
+
+ ReplyRefDto(ReplyRefDto)
+
+
+
+
+
+
+
+
protected ReplyRefDto(ReplyRefDto original)
+
+
+ Parameters
+
+ original ReplyRefDto
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ReplyRefDto(Guid, string, string)
+
+
+
+ Reference to the message a reply targets. Content is treated exactly like
+message content on the wire: transport-encrypted, and for end-to-end encrypted rooms it is
+room ciphertext the client must decrypt (the server truncates only plaintext snippets).
+Null on a MessageDto when the original message no longer exists.
+
+
+
+
+
public ReplyRefDto(Guid MessageId, string SenderUsername, string Content)
+
+
+ Parameters
+
+ MessageId Guid
+
+ SenderUsername string
+
+ Content string
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Properties
+
+
+
+
+
+
+ Content
+
+
+
+
+
+
+
+
public string Content { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ string
+
+
+
+
+
+
+
+
+
+
+
+
+
+ EqualityContract
+
+
+
+
+
+
+
+
protected virtual Type EqualityContract { get; }
+
+
+
+
+
+
+ Property Value
+
+ Type
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MessageId
+
+
+
+
+
+
+
+
public Guid MessageId { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ Guid
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SenderUsername
+
+
+
+
+
+
+
+
public string SenderUsername { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ string
+
+
+
+
+
+
+
+
+
+
+ Methods
+
+
+
+
+
+
+ Deconstruct(out Guid, out string, out string)
+
+
+
+
+
+
+
+
public void Deconstruct(out Guid MessageId, out string SenderUsername, out string Content)
+
+
+ Parameters
+
+ MessageId Guid
+
+ SenderUsername string
+
+ Content string
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Equals(ReplyRefDto?)
+
+
+
+
+
+
+
+
public virtual bool Equals(ReplyRefDto? other)
+
+
+ Parameters
+
+ other ReplyRefDto
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Equals(object?)
+
+
+
+
+
+
+
+
public override bool Equals(object? obj)
+
+
+ Parameters
+
+ obj object
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GetHashCode()
+
+
+
+
+
+
+
+
public override int GetHashCode()
+
+
+
+ Returns
+
+ int
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PrintMembers(StringBuilder)
+
+
+
+
+
+
+
+
protected virtual bool PrintMembers(StringBuilder builder)
+
+
+ Parameters
+
+ builder StringBuilder
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ToString()
+
+
+
+
+
+
+
+
public override string ToString()
+
+
+
+ Returns
+
+ string
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Operators
+
+
+
+
+
+
+ operator ==(ReplyRefDto?, ReplyRefDto?)
+
+
+
+
+
+
+
+
public static bool operator ==(ReplyRefDto? left, ReplyRefDto? right)
+
+
+ Parameters
+
+ left ReplyRefDto
+
+ right ReplyRefDto
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ operator !=(ReplyRefDto?, ReplyRefDto?)
+
+
+
+
+
+
+
+
public static bool operator !=(ReplyRefDto? left, ReplyRefDto? right)
+
+
+ Parameters
+
+ left ReplyRefDto
+
+ right ReplyRefDto
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/api/core/EchoHub.Core.DTOs.ServerStatusDto.html b/api/core/EchoHub.Core.DTOs.ServerStatusDto.html
index 5a5608c..cc7c636 100644
--- a/api/core/EchoHub.Core.DTOs.ServerStatusDto.html
+++ b/api/core/EchoHub.Core.DTOs.ServerStatusDto.html
@@ -182,8 +182,8 @@ Class ServerStatusDto
-
- ServerStatusDto(string, string?, int, int)
+
+ ServerStatusDto(string, string?, int, int, string)
@@ -191,7 +191,7 @@ Class ServerStatusDto
+ RegistrationMode string
+
@@ -345,6 +347,37 @@ Class ServerStatusDto
+
+
+ RegistrationMode
+
+
+
+
+
+
+
+
public string RegistrationMode { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ string
+
+
+
+
+
+
+
+
+
+
@@ -382,8 +415,8 @@ Class ServerStatusDto
-
- Deconstruct(out string, out string?, out int, out int)
+
+ Deconstruct(out string, out string?, out int, out int, out string)
@@ -391,7 +424,7 @@ Class ServerStatusDto
+ RegistrationMode string
+
diff --git a/api/core/EchoHub.Core.DTOs.UserDataExportDto.html b/api/core/EchoHub.Core.DTOs.UserDataExportDto.html
new file mode 100644
index 0000000..8d0589a
--- /dev/null
+++ b/api/core/EchoHub.Core.DTOs.UserDataExportDto.html
@@ -0,0 +1,734 @@
+
+
+
+
+ Class UserDataExportDto | EchoHub Documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Class UserDataExportDto
+
+
+
+
Namespace EchoHub .Core .DTOs
+
Assembly EchoHub.Core.dll
+
+
+ Everything the server holds about a user, as stored. For end-to-end encrypted rooms the
+message content in here is room ciphertext — the server cannot include plaintext it never had.
+
+
+
+
+
public record UserDataExportDto : IEquatable<UserDataExportDto>
+
+
+
+
+
+
+ Inheritance
+
+
+ UserDataExportDto
+
+
+
+
+ Implements
+
+
+
+
+
+
+
+ Inherited Members
+
+
+
+
+
+
+
+
+
+
+
+
+ Constructors
+
+
+
+
+
+
+ UserDataExportDto(UserDataExportDto)
+
+
+
+
+
+
+
+
protected UserDataExportDto(UserDataExportDto original)
+
+
+ Parameters
+
+ original UserDataExportDto
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UserDataExportDto(DateTimeOffset, string, UserProfileDto, List<ExportedMessageDto>, List<ExportedAttachmentDto>)
+
+
+
+ Everything the server holds about a user, as stored. For end-to-end encrypted rooms the
+message content in here is room ciphertext — the server cannot include plaintext it never had.
+
+
+
+
+
public UserDataExportDto(DateTimeOffset ExportedAt, string ServerName, UserProfileDto Profile, List<ExportedMessageDto> Messages, List<ExportedAttachmentDto> Attachments)
+
+
+ Parameters
+
+ ExportedAt DateTimeOffset
+
+ ServerName string
+
+ Profile UserProfileDto
+
+ Messages List <ExportedMessageDto >
+
+ Attachments List <ExportedAttachmentDto >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Properties
+
+
+
+
+
+
+ Attachments
+
+
+
+
+
+
+
+
public List<ExportedAttachmentDto> Attachments { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ List <ExportedAttachmentDto >
+
+
+
+
+
+
+
+
+
+
+
+
+
+ EqualityContract
+
+
+
+
+
+
+
+
protected virtual Type EqualityContract { get; }
+
+
+
+
+
+
+ Property Value
+
+ Type
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ExportedAt
+
+
+
+
+
+
+
+
public DateTimeOffset ExportedAt { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ DateTimeOffset
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Messages
+
+
+
+
+
+
+
+
public List<ExportedMessageDto> Messages { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ List <ExportedMessageDto >
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Profile
+
+
+
+
+
+
+
+
public UserProfileDto Profile { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ UserProfileDto
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ServerName
+
+
+
+
+
+
+
+
public string ServerName { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ string
+
+
+
+
+
+
+
+
+
+
+ Methods
+
+
+
+
+
+
+ Deconstruct(out DateTimeOffset, out string, out UserProfileDto, out List<ExportedMessageDto>, out List<ExportedAttachmentDto>)
+
+
+
+
+
+
+
+
public void Deconstruct(out DateTimeOffset ExportedAt, out string ServerName, out UserProfileDto Profile, out List<ExportedMessageDto> Messages, out List<ExportedAttachmentDto> Attachments)
+
+
+ Parameters
+
+ ExportedAt DateTimeOffset
+
+ ServerName string
+
+ Profile UserProfileDto
+
+ Messages List <ExportedMessageDto >
+
+ Attachments List <ExportedAttachmentDto >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Equals(UserDataExportDto?)
+
+
+
+
+
+
+
+
public virtual bool Equals(UserDataExportDto? other)
+
+
+ Parameters
+
+ other UserDataExportDto
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Equals(object?)
+
+
+
+
+
+
+
+
public override bool Equals(object? obj)
+
+
+ Parameters
+
+ obj object
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GetHashCode()
+
+
+
+
+
+
+
+
public override int GetHashCode()
+
+
+
+ Returns
+
+ int
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PrintMembers(StringBuilder)
+
+
+
+
+
+
+
+
protected virtual bool PrintMembers(StringBuilder builder)
+
+
+ Parameters
+
+ builder StringBuilder
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ToString()
+
+
+
+
+
+
+
+
public override string ToString()
+
+
+
+ Returns
+
+ string
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Operators
+
+
+
+
+
+
+ operator ==(UserDataExportDto?, UserDataExportDto?)
+
+
+
+
+
+
+
+
public static bool operator ==(UserDataExportDto? left, UserDataExportDto? right)
+
+
+ Parameters
+
+ left UserDataExportDto
+
+ right UserDataExportDto
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ operator !=(UserDataExportDto?, UserDataExportDto?)
+
+
+
+
+
+
+
+
public static bool operator !=(UserDataExportDto? left, UserDataExportDto? right)
+
+
+ Parameters
+
+ left UserDataExportDto
+
+ right UserDataExportDto
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/api/core/EchoHub.Core.DTOs.html b/api/core/EchoHub.Core.DTOs.html
index 4692b85..b31235c 100644
--- a/api/core/EchoHub.Core.DTOs.html
+++ b/api/core/EchoHub.Core.DTOs.html
@@ -147,6 +147,15 @@ attachment blob sizes plus message text length, so it is an estimate, not an exa
CreateChannelRequest
+
+ CreateInviteRequest
+
+
+
+ DeleteAccountRequest
+ Password re-confirmation for destructive self-service account actions.
+
+
EmbedDto
@@ -159,6 +168,18 @@ attachment blob sizes plus message text length, so it is an estimate, not an exa
ErrorResponse
+
+ ExportedAttachmentDto
+
+
+
+ ExportedMessageDto
+
+
+
+ InviteDto
+
+
JoinChannelResult
@@ -199,6 +220,14 @@ attachment blob sizes plus message text length, so it is an estimate, not an exa
RekeyChannelRequest
Passphrase change for an encrypted channel: the client proves knowledge of the old
passphrase (old auth key), then supplies the re-wrapped room key under the new one.
+
+
+
+ ReplyRefDto
+ Reference to the message a reply targets. Content is treated exactly like
+message content on the wire: transport-encrypted, and for end-to-end encrypted rooms it is
+room ciphertext the client must decrypt (the server truncates only plaintext snippets).
+Null on a MessageDto when the original message no longer exists.
@@ -225,6 +254,12 @@ passphrase (old auth key), then supplies the re-wrapped room key under the new o
UpdateTopicRequest
+
+ UserDataExportDto
+ Everything the server holds about a user, as stored. For end-to-end encrypted rooms the
+message content in here is room ciphertext — the server cannot include plaintext it never had.
+
+
UserDto
diff --git a/api/core/EchoHub.Core.Models.Channel.html b/api/core/EchoHub.Core.Models.Channel.html
index cf670c5..e847557 100644
--- a/api/core/EchoHub.Core.Models.Channel.html
+++ b/api/core/EchoHub.Core.Models.Channel.html
@@ -337,6 +337,37 @@ Class Channel
+
+
+ IsSystem
+
+
+
+
+
+
+
+
public bool IsSystem { get; set; }
+
+
+
+
+
+
+ Property Value
+
+ bool
+
+
+
+
+
+
+
+
+
+
diff --git a/api/core/EchoHub.Core.Models.InviteCode.html b/api/core/EchoHub.Core.Models.InviteCode.html
new file mode 100644
index 0000000..1852b00
--- /dev/null
+++ b/api/core/EchoHub.Core.Models.InviteCode.html
@@ -0,0 +1,461 @@
+
+
+
+
+ Class InviteCode | EchoHub Documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Class InviteCode
+
+
+
+
Namespace EchoHub .Core .Models
+
Assembly EchoHub.Core.dll
+
+
+ A registration invite code. When the server runs with Server:Registration = "invite",
+new accounts (REST and IRC alike) require a valid, unexpired, not-fully-used code.
+
+
+
+
+
public class InviteCode
+
+
+
+
+
+
+ Inheritance
+
+
+ InviteCode
+
+
+
+
+
+
+ Inherited Members
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Constructors
+
+
+
+
+
+
+ InviteCode()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Properties
+
+
+
+
+
+
+ Code
+
+
+
+
+
+
+
+
public required string Code { get; set; }
+
+
+
+
+
+
+ Property Value
+
+ string
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CreatedAt
+
+
+
+
+
+
+
+
public DateTimeOffset CreatedAt { get; set; }
+
+
+
+
+
+
+ Property Value
+
+ DateTimeOffset
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CreatedByUserId
+
+
+
+
+
+
+
+
public Guid CreatedByUserId { get; set; }
+
+
+
+
+
+
+ Property Value
+
+ Guid
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CreatedByUsername
+
+
+
+
+
+
+
+
public string CreatedByUsername { get; set; }
+
+
+
+
+
+
+ Property Value
+
+ string
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ExpiresAt
+
+
+
+ Null means the code never expires.
+
+
+
+
+
public DateTimeOffset? ExpiresAt { get; set; }
+
+
+
+
+
+
+ Property Value
+
+ DateTimeOffset ?
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Id
+
+
+
+
+
+
+
+
public Guid Id { get; set; }
+
+
+
+
+
+
+ Property Value
+
+ Guid
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MaxUses
+
+
+
+
+
+
+
+
public int MaxUses { get; set; }
+
+
+
+
+
+
+ Property Value
+
+ int
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UseCount
+
+
+
+
+
+
+
+
public int UseCount { get; set; }
+
+
+
+
+
+
+ Property Value
+
+ int
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/api/core/EchoHub.Core.Models.Message.html b/api/core/EchoHub.Core.Models.Message.html
index 603ae2d..9e7e46b 100644
--- a/api/core/EchoHub.Core.Models.Message.html
+++ b/api/core/EchoHub.Core.Models.Message.html
@@ -463,6 +463,38 @@ Class Message
+
+
+ ReplyToMessageId
+
+
+
+ Message this one replies to, if any. The target may since have been deleted.
+
+
+
+
+
public Guid? ReplyToMessageId { get; set; }
+
+
+
+
+
+
+ Property Value
+
+ Guid ?
+
+
+
+
+
+
+
+
+
+
diff --git a/api/core/EchoHub.Core.Models.html b/api/core/EchoHub.Core.Models.html
index cbca0c7..ff32b58 100644
--- a/api/core/EchoHub.Core.Models.html
+++ b/api/core/EchoHub.Core.Models.html
@@ -108,6 +108,12 @@ zero or more attachments alongside its text content (Discord-style).
ChannelMembership
+
+ InviteCode
+ A registration invite code. When the server runs with Server:Registration = "invite",
+new accounts (REST and IRC alike) require a valid, unexpired, not-fully-used code.
+
+
Message
diff --git a/api/core/EchoHub.Core.Services.AsciiBannerService.html b/api/core/EchoHub.Core.Services.AsciiBannerService.html
new file mode 100644
index 0000000..bfac3af
--- /dev/null
+++ b/api/core/EchoHub.Core.Services.AsciiBannerService.html
@@ -0,0 +1,256 @@
+
+
+
+
+ Class AsciiBannerService | EchoHub Documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Class AsciiBannerService
+
+
+
+
Namespace EchoHub .Core .Services
+
Assembly EchoHub.Core.dll
+
+
+ Renders short text as a 5-row block-character banner (the /banner command).
+Entirely local — a hand-rolled figlet-style font, no dependencies, no network.
+Output is plain message content, so it travels (and encrypts) like any other text.
+
+
+
+
+
public static class AsciiBannerService
+
+
+
+
+
+
+ Inheritance
+
+
+ AsciiBannerService
+
+
+
+
+
+
+ Inherited Members
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Fields
+
+
+
+
+
+
+
+
+
+
+
public const int MaxInputLength = 20
+
+
+
+
+
+ Field Value
+
+ int
+
+
+
+
+
+
+
+
+
+
+
+ Methods
+
+
+
+
+
+
+ Render(string)
+
+
+
+ Renders text as banner lines joined with '\n'.
+Returns null when the input is empty or contains no renderable characters.
+Characters outside the font (letters, digits, and basic punctuation) are skipped.
+
+
+
+
+
public static string? Render(string text)
+
+
+ Parameters
+
+ text string
+
+
+
+ Returns
+
+ string
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/api/core/EchoHub.Core.Services.html b/api/core/EchoHub.Core.Services.html
index d55fffa..dfa4acc 100644
--- a/api/core/EchoHub.Core.Services.html
+++ b/api/core/EchoHub.Core.Services.html
@@ -94,6 +94,13 @@
Classes
+
+ AsciiBannerService
+ Renders short text as a 5-row block-character banner (the /banner command).
+Entirely local — a hand-rolled figlet-style font, no dependencies, no network.
+Output is plain message content, so it travels (and encrypts) like any other text.
+
+
FileValidationHelper
diff --git a/api/server-irc/EchoHub.Server.Irc.IrcBroadcaster.html b/api/server-irc/EchoHub.Server.Irc.IrcBroadcaster.html
index 1170558..70151fd 100644
--- a/api/server-irc/EchoHub.Server.Irc.IrcBroadcaster.html
+++ b/api/server-irc/EchoHub.Server.Irc.IrcBroadcaster.html
@@ -233,6 +233,42 @@ Class IrcBroadcaster
+
+
+ SendChannelDeletedAsync(string)
+
+
+
+
+
+
+
+
public Task SendChannelDeletedAsync(string channelName)
+
+
+ Parameters
+
+ channelName string
+
+
+
+ Returns
+
+ Task
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -385,16 +421,21 @@ Class IrcBroadcaster
-
- SendMessageToChannelAsync(string, MessageDto)
+
+ SendMessageToChannelAsync(string, MessageDto, string?)
-
+ Broadcast a chat message to a channel. excludeConnectionId is the
+connection the message originated from (IRC convention: never echo a message back to
+the connection that sent it — its client already displayed it locally). Other
+connections of the same user (e.g. an IRC session alongside a TUI session) still
+receive the message.
+
-
public Task SendMessageToChannelAsync(string channelName, MessageDto message)
+
public Task SendMessageToChannelAsync(string channelName, MessageDto message, string? excludeConnectionId = null)
Parameters
@@ -403,6 +444,8 @@ Class IrcBroadcaster
+ excludeConnectionId string
+
Returns
diff --git a/api/server-irc/EchoHub.Server.Irc.IrcMessageFormatter.html b/api/server-irc/EchoHub.Server.Irc.IrcMessageFormatter.html
index 9985f63..5469eaf 100644
--- a/api/server-irc/EchoHub.Server.Irc.IrcMessageFormatter.html
+++ b/api/server-irc/EchoHub.Server.Irc.IrcMessageFormatter.html
@@ -152,63 +152,30 @@ Class IrcMessageFormatter
-
-
-
- Convert printable color tags ({F:RRGGBB}, {B:RRGGBB}, {X}) to ANSI escape codes for IRC clients.
-Also passes through content that already uses ANSI codes unchanged.
-
-
-
-
-
public static string ColorTagsToAnsi(string text)
-
-
- Parameters
-
- text string
-
-
-
- Returns
-
- string
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -323,6 +327,44 @@ Class ChannelService
+
+
+ EnsureSystemChannelAsync(string, string?)
+
+
+
+
+
+
+
+
public Task<ChannelDto> EnsureSystemChannelAsync(string channelName, string? topic = null)
+
+
+ Parameters
+
+ channelName string
+
+ topic string
+
+
+
+ Returns
+
+ Task <ChannelDto >
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/api/server/EchoHub.Server.Services.ChatService.html b/api/server/EchoHub.Server.Services.ChatService.html
index 0c343ae..9683e87 100644
--- a/api/server/EchoHub.Server.Services.ChatService.html
+++ b/api/server/EchoHub.Server.Services.ChatService.html
@@ -160,8 +160,8 @@ Class ChatService
-
- ChatService(IServiceScopeFactory, PresenceTracker, IEnumerable<IChatBroadcaster>, LinkEmbedService, IMessageEncryptionService, IChannelService, FileStorageService, ILogger<ChatService>)
+
+ ChatService(IServiceScopeFactory, PresenceTracker, IEnumerable<IChatBroadcaster>, LinkEmbedService, IMessageEncryptionService, IChannelService, FileStorageService, SpamGuard, ServerLogsService, ILogger<ChatService>)
@@ -169,7 +169,7 @@ Class ChatService
+
+
+ BroadcastChannelDeletedAsync(string)
+
+
+
+
+
+
+
+
public Task BroadcastChannelDeletedAsync(string channelName)
+
+
+ Parameters
+
+ channelName string
+
+
+
+ Returns
+
+ Task
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -481,8 +521,8 @@ Class ChatService
-
- SendMessageAsync(Guid, string, string, string)
+
+ SendMessageAsync(Guid, string, string, string, string?, Guid?)
@@ -490,7 +530,7 @@ Class ChatService
+
+
+ SendChannelDeletedAsync(string)
+
+
+
+
+
+
+
+
public Task SendChannelDeletedAsync(string channelName)
+
+
+ Parameters
+
+ channelName string
+
+
+
+ Returns
+
+ Task
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -385,16 +421,21 @@ Class SignalRBroadcaster
-
- SendMessageToChannelAsync(string, MessageDto)
+
+ SendMessageToChannelAsync(string, MessageDto, string?)
-
+ Broadcast a chat message to a channel. excludeConnectionId is the
+connection the message originated from (IRC convention: never echo a message back to
+the connection that sent it — its client already displayed it locally). Other
+connections of the same user (e.g. an IRC session alongside a TUI session) still
+receive the message.
+
-
public Task SendMessageToChannelAsync(string channelName, MessageDto message)
+
public Task SendMessageToChannelAsync(string channelName, MessageDto message, string? excludeConnectionId = null)
Parameters
@@ -403,6 +444,8 @@ Class SignalRBroadcaster
+ excludeConnectionId string
+
Returns
diff --git a/api/server/EchoHub.Server.Services.SpamGuard.html b/api/server/EchoHub.Server.Services.SpamGuard.html
new file mode 100644
index 0000000..f9f5fd8
--- /dev/null
+++ b/api/server/EchoHub.Server.Services.SpamGuard.html
@@ -0,0 +1,379 @@
+
+
+
+
+ Class SpamGuard | EchoHub Documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Class SpamGuard
+
+
+
+
Namespace EchoHub .Server .Services
+
Assembly EchoHub.Server.dll
+
+
+ In-memory, per-user spam protection consulted from ChatService (messages,
+joins) and ChannelService (channel creation), so every ingress protocol —
+SignalR and IRC alike — shares the same limits. State is per-process and never persisted;
+auto-mutes land in the normal mute store. Operates only on content the server already
+stores (for end-to-end encrypted rooms that is ciphertext) — nothing is decrypted.
+
+
+
+
+
public sealed class SpamGuard
+
+
+
+
+
+
+ Inheritance
+
+
+ SpamGuard
+
+
+
+
+
+
+ Inherited Members
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Constructors
+
+
+
+
+
+
+ SpamGuard(SpamOptions)
+
+
+
+
+
+
+
+
public SpamGuard(SpamOptions options)
+
+
+ Parameters
+
+ options SpamOptions
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Properties
+
+
+
+
+
+
+ Enabled
+
+
+
+
+
+
+
+
public bool Enabled { get; }
+
+
+
+
+
+
+ Property Value
+
+ bool
+
+
+
+
+
+
+
+
+
+
+ Methods
+
+
+
+
+
+
+ CheckChannelCreate(Guid, ServerRole, DateTimeOffset?)
+
+
+
+ Checks a channel creation. Rejections count as violations but never auto-mute.
+
+
+
+
+
public SpamVerdict CheckChannelCreate(Guid userId, ServerRole role, DateTimeOffset? nowOverride = null)
+
+
+ Parameters
+
+ userId Guid
+
+ role ServerRole
+
+ nowOverride DateTimeOffset ?
+
+
+
+ Returns
+
+ SpamVerdict
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CheckJoin(Guid, ServerRole, DateTimeOffset?)
+
+
+
+ Checks a channel join. Rejections count as violations but never auto-mute.
+
+
+
+
+
public SpamVerdict CheckJoin(Guid userId, ServerRole role, DateTimeOffset? nowOverride = null)
+
+
+ Parameters
+
+ userId Guid
+
+ role ServerRole
+
+ nowOverride DateTimeOffset ?
+
+
+
+ Returns
+
+ SpamVerdict
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CheckMessage(Guid, ServerRole, string, DateTimeOffset?)
+
+
+
+ Checks a message send. Rejections count as violations; enough violations inside the
+violation window escalate to AutoMute (evaluated only on
+rejected messages, so a clean message never mutes anyone).
+
+
+
+
+
public SpamVerdict CheckMessage(Guid userId, ServerRole role, string content, DateTimeOffset? nowOverride = null)
+
+
+ Parameters
+
+ userId Guid
+
+ role ServerRole
+
+ content string
+
+ nowOverride DateTimeOffset ?
+
+
+
+ Returns
+
+ SpamVerdict
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/api/server/EchoHub.Server.Services.SpamVerdict.html b/api/server/EchoHub.Server.Services.SpamVerdict.html
new file mode 100644
index 0000000..f963a64
--- /dev/null
+++ b/api/server/EchoHub.Server.Services.SpamVerdict.html
@@ -0,0 +1,586 @@
+
+
+
+
+ Struct SpamVerdict | EchoHub Documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Struct SpamVerdict
+
+
+
+
Namespace EchoHub .Server .Services
+
Assembly EchoHub.Server.dll
+
+
+
+
+
+
+
public readonly struct SpamVerdict : IEquatable<SpamVerdict>
+
+
+
+
+
+
+
+ Implements
+
+
+
+
+
+
+
+ Inherited Members
+
+
+
+
+
+
+
+
+
+
+
+ Constructors
+
+
+
+
+
+
+ SpamVerdict(SpamVerdictKind, string?, TimeSpan)
+
+
+
+
+
+
+
+
public SpamVerdict(SpamVerdictKind Kind, string? Reason = null, TimeSpan MuteDuration = default)
+
+
+ Parameters
+
+ Kind SpamVerdictKind
+
+ Reason string
+
+ MuteDuration TimeSpan
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Fields
+
+
+
+
+
+ Allowed
+
+
+
+
+
+
+
+
public static readonly SpamVerdict Allowed
+
+
+
+
+
+ Field Value
+
+ SpamVerdict
+
+
+
+
+
+
+
+
+
+
+
+ Properties
+
+
+
+
+
+
+ Kind
+
+
+
+
+
+
+
+
public SpamVerdictKind Kind { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ SpamVerdictKind
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MuteDuration
+
+
+
+
+
+
+
+
public TimeSpan MuteDuration { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ TimeSpan
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Reason
+
+
+
+
+
+
+
+
public string? Reason { get; init; }
+
+
+
+
+
+
+ Property Value
+
+ string
+
+
+
+
+
+
+
+
+
+
+ Methods
+
+
+
+
+
+
+ Deconstruct(out SpamVerdictKind, out string?, out TimeSpan)
+
+
+
+
+
+
+
+
public void Deconstruct(out SpamVerdictKind Kind, out string? Reason, out TimeSpan MuteDuration)
+
+
+ Parameters
+
+ Kind SpamVerdictKind
+
+ Reason string
+
+ MuteDuration TimeSpan
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Equals(SpamVerdict)
+
+
+
+
+
+
+
+
public bool Equals(SpamVerdict other)
+
+
+ Parameters
+
+ other SpamVerdict
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Equals(object)
+
+
+
+
+
+
+
+
public override bool Equals(object obj)
+
+
+ Parameters
+
+ obj object
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GetHashCode()
+
+
+
+
+
+
+
+
public override int GetHashCode()
+
+
+
+ Returns
+
+ int
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ToString()
+
+
+
+
+
+
+
+
public override string ToString()
+
+
+
+ Returns
+
+ string
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Operators
+
+
+
+
+
+
+ operator ==(SpamVerdict, SpamVerdict)
+
+
+
+
+
+
+
+
public static bool operator ==(SpamVerdict left, SpamVerdict right)
+
+
+ Parameters
+
+ left SpamVerdict
+
+ right SpamVerdict
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ operator !=(SpamVerdict, SpamVerdict)
+
+
+
+
+
+
+
+
public static bool operator !=(SpamVerdict left, SpamVerdict right)
+
+
+ Parameters
+
+ left SpamVerdict
+
+ right SpamVerdict
+
+
+
+ Returns
+
+ bool
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/api/server/EchoHub.Server.Services.SpamVerdictKind.html b/api/server/EchoHub.Server.Services.SpamVerdictKind.html
new file mode 100644
index 0000000..b95e9e3
--- /dev/null
+++ b/api/server/EchoHub.Server.Services.SpamVerdictKind.html
@@ -0,0 +1,157 @@
+
+
+
+
+ Enum SpamVerdictKind | EchoHub Documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Enum SpamVerdictKind
+
+
+
+
Namespace EchoHub .Server .Services
+
Assembly EchoHub.Server.dll
+
+
+
+
+
+
+
public enum SpamVerdictKind
+
+
+
+
+
+
+
+
+
+
+ Fields
+
+
+ Allowed = 0
+
+
+ AutoMute = 2
+
+ The user crossed the violation threshold — the caller should apply a timed mute.
+
+ Rejected = 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/api/server/EchoHub.Server.Services.UserService.html b/api/server/EchoHub.Server.Services.UserService.html
index 29ac265..78c6e1c 100644
--- a/api/server/EchoHub.Server.Services.UserService.html
+++ b/api/server/EchoHub.Server.Services.UserService.html
@@ -160,8 +160,8 @@ Class UserService
-
- UserService(IServiceScopeFactory)
+
+ UserService(IServiceScopeFactory, IConfiguration)
@@ -169,13 +169,15 @@ Class UserService
+
+
+ RegistrationMode
+
+
+
+ Registration mode from config: "open" (default), "invite", or "closed".
+
+
+
+
+
public string RegistrationMode { get; }
+
+
+
+
+
+
+ Property Value
+
+ string
+
+
+
+
+
+
+
+
+
+
Methods
@@ -305,8 +343,8 @@ Class UserService
-
- RegisterUserAsync(string, string, string?)
+
+ RegisterUserAsync(string, string, string?, string?)
@@ -314,7 +352,7 @@ Class UserService EchoHub.Client.UI.Chat
+
+ AttachmentAction
+
+
+ AttachmentActionSpan
+
ChatColors
@@ -307,6 +313,9 @@
HubConstants
+
+ MessageConventions
+
ValidationConstants
@@ -381,6 +390,12 @@
CreateChannelRequest
+
+ CreateInviteRequest
+
+
+ DeleteAccountRequest
+
EmbedDto
@@ -390,6 +405,15 @@
ErrorResponse
+
+ ExportedAttachmentDto
+
+
+ ExportedMessageDto
+
+
+ InviteDto
+
JoinChannelResult
@@ -420,6 +444,9 @@
RekeyChannelRequest
+
+ ReplyRefDto
+
SendMessageRequest
@@ -438,6 +465,9 @@
UpdateTopicRequest
+
+ UserDataExportDto
+
UserDto
@@ -472,6 +502,9 @@
ChannelMembership
+
+ InviteCode
+
Message
@@ -510,6 +543,9 @@
EchoHub.Core.Services
+
+ AsciiBannerService
+
FileValidationHelper
@@ -557,6 +593,12 @@
EchoHub.Server.Config
+
+
+ EchoHub.Server.Services.ServerLogs
+
+
+
EchoHub.Server.Setup
diff --git a/api/toc.json b/api/toc.json
index 8d9bb1b..cb37ac8 100644
--- a/api/toc.json
+++ b/api/toc.json
@@ -1,2 +1,2 @@
-{"items":[{"name":"Client","items":[{"name":"API Reference","includedFrom":"~/_api_meta/client/toc.yml","items":[{"name":"EchoHub.Client","href":"client/EchoHub.Client.html","topicHref":"client/EchoHub.Client.html","topicUid":"EchoHub.Client","type":"Namespace","items":[{"name":"AppOrchestrator","href":"client/EchoHub.Client.AppOrchestrator.html","topicHref":"client/EchoHub.Client.AppOrchestrator.html","topicUid":"EchoHub.Client.AppOrchestrator","type":"Class"}]},{"name":"EchoHub.Client.Commands","href":"client/EchoHub.Client.Commands.html","topicHref":"client/EchoHub.Client.Commands.html","topicUid":"EchoHub.Client.Commands","type":"Namespace","items":[{"name":"CommandHandler","href":"client/EchoHub.Client.Commands.CommandHandler.html","topicHref":"client/EchoHub.Client.Commands.CommandHandler.html","topicUid":"EchoHub.Client.Commands.CommandHandler","type":"Class"},{"name":"CommandResult","href":"client/EchoHub.Client.Commands.CommandResult.html","topicHref":"client/EchoHub.Client.Commands.CommandResult.html","topicUid":"EchoHub.Client.Commands.CommandResult","type":"Class"}]},{"name":"EchoHub.Client.Config","href":"client/EchoHub.Client.Config.html","topicHref":"client/EchoHub.Client.Config.html","topicUid":"EchoHub.Client.Config","type":"Namespace","items":[{"name":"AccountPreset","href":"client/EchoHub.Client.Config.AccountPreset.html","topicHref":"client/EchoHub.Client.Config.AccountPreset.html","topicUid":"EchoHub.Client.Config.AccountPreset","type":"Class"},{"name":"ClientConfig","href":"client/EchoHub.Client.Config.ClientConfig.html","topicHref":"client/EchoHub.Client.Config.ClientConfig.html","topicUid":"EchoHub.Client.Config.ClientConfig","type":"Class"},{"name":"ConfigManager","href":"client/EchoHub.Client.Config.ConfigManager.html","topicHref":"client/EchoHub.Client.Config.ConfigManager.html","topicUid":"EchoHub.Client.Config.ConfigManager","type":"Class"},{"name":"NotificationConfig","href":"client/EchoHub.Client.Config.NotificationConfig.html","topicHref":"client/EchoHub.Client.Config.NotificationConfig.html","topicUid":"EchoHub.Client.Config.NotificationConfig","type":"Class"},{"name":"SavedServer","href":"client/EchoHub.Client.Config.SavedServer.html","topicHref":"client/EchoHub.Client.Config.SavedServer.html","topicUid":"EchoHub.Client.Config.SavedServer","type":"Class"}]},{"name":"EchoHub.Client.Services","href":"client/EchoHub.Client.Services.html","topicHref":"client/EchoHub.Client.Services.html","topicUid":"EchoHub.Client.Services","type":"Namespace","items":[{"name":"ApiClient","href":"client/EchoHub.Client.Services.ApiClient.html","topicHref":"client/EchoHub.Client.Services.ApiClient.html","topicUid":"EchoHub.Client.Services.ApiClient","type":"Class"},{"name":"AsyncRunner","href":"client/EchoHub.Client.Services.AsyncRunner.html","topicHref":"client/EchoHub.Client.Services.AsyncRunner.html","topicUid":"EchoHub.Client.Services.AsyncRunner","type":"Class"},{"name":"AudioPlaybackService","href":"client/EchoHub.Client.Services.AudioPlaybackService.html","topicHref":"client/EchoHub.Client.Services.AudioPlaybackService.html","topicUid":"EchoHub.Client.Services.AudioPlaybackService","type":"Class"},{"name":"BackupInfo","href":"client/EchoHub.Client.Services.BackupInfo.html","topicHref":"client/EchoHub.Client.Services.BackupInfo.html","topicUid":"EchoHub.Client.Services.BackupInfo","type":"Class"},{"name":"ChannelPasswordRequiredException","href":"client/EchoHub.Client.Services.ChannelPasswordRequiredException.html","topicHref":"client/EchoHub.Client.Services.ChannelPasswordRequiredException.html","topicUid":"EchoHub.Client.Services.ChannelPasswordRequiredException","type":"Class"},{"name":"ClientEncryptionService","href":"client/EchoHub.Client.Services.ClientEncryptionService.html","topicHref":"client/EchoHub.Client.Services.ClientEncryptionService.html","topicUid":"EchoHub.Client.Services.ClientEncryptionService","type":"Class"},{"name":"ClipboardFiles","href":"client/EchoHub.Client.Services.ClipboardFiles.html","topicHref":"client/EchoHub.Client.Services.ClipboardFiles.html","topicUid":"EchoHub.Client.Services.ClipboardFiles","type":"Class"},{"name":"ClipboardImage","href":"client/EchoHub.Client.Services.ClipboardImage.html","topicHref":"client/EchoHub.Client.Services.ClipboardImage.html","topicUid":"EchoHub.Client.Services.ClipboardImage","type":"Class"},{"name":"EchoHubConnection","href":"client/EchoHub.Client.Services.EchoHubConnection.html","topicHref":"client/EchoHub.Client.Services.EchoHubConnection.html","topicUid":"EchoHub.Client.Services.EchoHubConnection","type":"Class"},{"name":"FolderPickResult","href":"client/EchoHub.Client.Services.FolderPickResult.html","topicHref":"client/EchoHub.Client.Services.FolderPickResult.html","topicUid":"EchoHub.Client.Services.FolderPickResult","type":"Class"},{"name":"JoinOutcome","href":"client/EchoHub.Client.Services.JoinOutcome.html","topicHref":"client/EchoHub.Client.Services.JoinOutcome.html","topicUid":"EchoHub.Client.Services.JoinOutcome","type":"Class"},{"name":"NativeFolderPicker","href":"client/EchoHub.Client.Services.NativeFolderPicker.html","topicHref":"client/EchoHub.Client.Services.NativeFolderPicker.html","topicUid":"EchoHub.Client.Services.NativeFolderPicker","type":"Class"},{"name":"NotificationSoundService","href":"client/EchoHub.Client.Services.NotificationSoundService.html","topicHref":"client/EchoHub.Client.Services.NotificationSoundService.html","topicUid":"EchoHub.Client.Services.NotificationSoundService","type":"Class"},{"name":"OutgoingAttachment","href":"client/EchoHub.Client.Services.OutgoingAttachment.html","topicHref":"client/EchoHub.Client.Services.OutgoingAttachment.html","topicUid":"EchoHub.Client.Services.OutgoingAttachment","type":"Class"},{"name":"PathSetup","href":"client/EchoHub.Client.Services.PathSetup.html","topicHref":"client/EchoHub.Client.Services.PathSetup.html","topicUid":"EchoHub.Client.Services.PathSetup","type":"Class"},{"name":"PickerOutcome","href":"client/EchoHub.Client.Services.PickerOutcome.html","topicHref":"client/EchoHub.Client.Services.PickerOutcome.html","topicUid":"EchoHub.Client.Services.PickerOutcome","type":"Enum"},{"name":"RoomKeyProtector","href":"client/EchoHub.Client.Services.RoomKeyProtector.html","topicHref":"client/EchoHub.Client.Services.RoomKeyProtector.html","topicUid":"EchoHub.Client.Services.RoomKeyProtector","type":"Class"},{"name":"RoomKeyStore","href":"client/EchoHub.Client.Services.RoomKeyStore.html","topicHref":"client/EchoHub.Client.Services.RoomKeyStore.html","topicUid":"EchoHub.Client.Services.RoomKeyStore","type":"Class"},{"name":"RoomLockedException","href":"client/EchoHub.Client.Services.RoomLockedException.html","topicHref":"client/EchoHub.Client.Services.RoomLockedException.html","topicUid":"EchoHub.Client.Services.RoomLockedException","type":"Class"},{"name":"UpdateBackupService","href":"client/EchoHub.Client.Services.UpdateBackupService.html","topicHref":"client/EchoHub.Client.Services.UpdateBackupService.html","topicUid":"EchoHub.Client.Services.UpdateBackupService","type":"Class"},{"name":"UpdateChecker","href":"client/EchoHub.Client.Services.UpdateChecker.html","topicHref":"client/EchoHub.Client.Services.UpdateChecker.html","topicUid":"EchoHub.Client.Services.UpdateChecker","type":"Class"}]},{"name":"EchoHub.Client.Themes","href":"client/EchoHub.Client.Themes.html","topicHref":"client/EchoHub.Client.Themes.html","topicUid":"EchoHub.Client.Themes","type":"Namespace","items":[{"name":"Theme","href":"client/EchoHub.Client.Themes.Theme.html","topicHref":"client/EchoHub.Client.Themes.Theme.html","topicUid":"EchoHub.Client.Themes.Theme","type":"Class"},{"name":"ThemeColors","href":"client/EchoHub.Client.Themes.ThemeColors.html","topicHref":"client/EchoHub.Client.Themes.ThemeColors.html","topicUid":"EchoHub.Client.Themes.ThemeColors","type":"Class"},{"name":"ThemeManager","href":"client/EchoHub.Client.Themes.ThemeManager.html","topicHref":"client/EchoHub.Client.Themes.ThemeManager.html","topicUid":"EchoHub.Client.Themes.ThemeManager","type":"Class"}]},{"name":"EchoHub.Client.UI","href":"client/EchoHub.Client.UI.html","topicHref":"client/EchoHub.Client.UI.html","topicUid":"EchoHub.Client.UI","type":"Namespace","items":[{"name":"MainWindow","href":"client/EchoHub.Client.UI.MainWindow.html","topicHref":"client/EchoHub.Client.UI.MainWindow.html","topicUid":"EchoHub.Client.UI.MainWindow","type":"Class"}]},{"name":"EchoHub.Client.UI.Chat","href":"client/EchoHub.Client.UI.Chat.html","topicHref":"client/EchoHub.Client.UI.Chat.html","topicUid":"EchoHub.Client.UI.Chat","type":"Namespace","items":[{"name":"ChatColors","href":"client/EchoHub.Client.UI.Chat.ChatColors.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatColors.html","topicUid":"EchoHub.Client.UI.Chat.ChatColors","type":"Class"},{"name":"ChatLine","href":"client/EchoHub.Client.UI.Chat.ChatLine.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatLine.html","topicUid":"EchoHub.Client.UI.Chat.ChatLine","type":"Class"},{"name":"ChatListSource","href":"client/EchoHub.Client.UI.Chat.ChatListSource.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatListSource.html","topicUid":"EchoHub.Client.UI.Chat.ChatListSource","type":"Class"},{"name":"ChatMessageManager","href":"client/EchoHub.Client.UI.Chat.ChatMessageManager.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatMessageManager.html","topicUid":"EchoHub.Client.UI.Chat.ChatMessageManager","type":"Class"},{"name":"ChatSegment","href":"client/EchoHub.Client.UI.Chat.ChatSegment.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatSegment.html","topicUid":"EchoHub.Client.UI.Chat.ChatSegment","type":"Class"}]},{"name":"EchoHub.Client.UI.Dialogs","href":"client/EchoHub.Client.UI.Dialogs.html","topicHref":"client/EchoHub.Client.UI.Dialogs.html","topicUid":"EchoHub.Client.UI.Dialogs","type":"Namespace","items":[{"name":"AudioPlayerDialog","href":"client/EchoHub.Client.UI.Dialogs.AudioPlayerDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.AudioPlayerDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.AudioPlayerDialog","type":"Class"},{"name":"ChannelPasswordDialog","href":"client/EchoHub.Client.UI.Dialogs.ChannelPasswordDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ChannelPasswordDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.ChannelPasswordDialog","type":"Class"},{"name":"ConnectDialog","href":"client/EchoHub.Client.UI.Dialogs.ConnectDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ConnectDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.ConnectDialog","type":"Class"},{"name":"ConnectDialogResult","href":"client/EchoHub.Client.UI.Dialogs.ConnectDialogResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ConnectDialogResult.html","topicUid":"EchoHub.Client.UI.Dialogs.ConnectDialogResult","type":"Class"},{"name":"CreateChannelDialog","href":"client/EchoHub.Client.UI.Dialogs.CreateChannelDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.CreateChannelDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.CreateChannelDialog","type":"Class"},{"name":"CreateChannelResult","href":"client/EchoHub.Client.UI.Dialogs.CreateChannelResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.CreateChannelResult.html","topicUid":"EchoHub.Client.UI.Dialogs.CreateChannelResult","type":"Class"},{"name":"ProfileAction","href":"client/EchoHub.Client.UI.Dialogs.ProfileAction.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ProfileAction.html","topicUid":"EchoHub.Client.UI.Dialogs.ProfileAction","type":"Enum"},{"name":"ProfileEditDialog","href":"client/EchoHub.Client.UI.Dialogs.ProfileEditDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ProfileEditDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.ProfileEditDialog","type":"Class"},{"name":"ProfileEditResult","href":"client/EchoHub.Client.UI.Dialogs.ProfileEditResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ProfileEditResult.html","topicUid":"EchoHub.Client.UI.Dialogs.ProfileEditResult","type":"Class"},{"name":"ProfileViewDialog","href":"client/EchoHub.Client.UI.Dialogs.ProfileViewDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ProfileViewDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.ProfileViewDialog","type":"Class"},{"name":"SearchDialog","href":"client/EchoHub.Client.UI.Dialogs.SearchDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.SearchDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.SearchDialog","type":"Class"},{"name":"SearchResult","href":"client/EchoHub.Client.UI.Dialogs.SearchResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.SearchResult.html","topicUid":"EchoHub.Client.UI.Dialogs.SearchResult","type":"Class"},{"name":"SearchResultType","href":"client/EchoHub.Client.UI.Dialogs.SearchResultType.html","topicHref":"client/EchoHub.Client.UI.Dialogs.SearchResultType.html","topicUid":"EchoHub.Client.UI.Dialogs.SearchResultType","type":"Enum"},{"name":"StatusDialog","href":"client/EchoHub.Client.UI.Dialogs.StatusDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.StatusDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.StatusDialog","type":"Class"},{"name":"StatusDialogResult","href":"client/EchoHub.Client.UI.Dialogs.StatusDialogResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.StatusDialogResult.html","topicUid":"EchoHub.Client.UI.Dialogs.StatusDialogResult","type":"Class"},{"name":"UpdateConfirmDialog","href":"client/EchoHub.Client.UI.Dialogs.UpdateConfirmDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.UpdateConfirmDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.UpdateConfirmDialog","type":"Class"}]},{"name":"EchoHub.Client.UI.Helpers","href":"client/EchoHub.Client.UI.Helpers.html","topicHref":"client/EchoHub.Client.UI.Helpers.html","topicUid":"EchoHub.Client.UI.Helpers","type":"Namespace","items":[{"name":"DroppedFileParser","href":"client/EchoHub.Client.UI.Helpers.DroppedFileParser.html","topicHref":"client/EchoHub.Client.UI.Helpers.DroppedFileParser.html","topicUid":"EchoHub.Client.UI.Helpers.DroppedFileParser","type":"Class"},{"name":"EmojiHelper","href":"client/EchoHub.Client.UI.Helpers.EmojiHelper.html","topicHref":"client/EchoHub.Client.UI.Helpers.EmojiHelper.html","topicUid":"EchoHub.Client.UI.Helpers.EmojiHelper","type":"Class"},{"name":"HexColorHelper","href":"client/EchoHub.Client.UI.Helpers.HexColorHelper.html","topicHref":"client/EchoHub.Client.UI.Helpers.HexColorHelper.html","topicUid":"EchoHub.Client.UI.Helpers.HexColorHelper","type":"Class"},{"name":"NickColorHelper","href":"client/EchoHub.Client.UI.Helpers.NickColorHelper.html","topicHref":"client/EchoHub.Client.UI.Helpers.NickColorHelper.html","topicUid":"EchoHub.Client.UI.Helpers.NickColorHelper","type":"Class"}]},{"name":"EchoHub.Client.UI.ListSources","href":"client/EchoHub.Client.UI.ListSources.html","topicHref":"client/EchoHub.Client.UI.ListSources.html","topicUid":"EchoHub.Client.UI.ListSources","type":"Namespace","items":[{"name":"ChannelListSource","href":"client/EchoHub.Client.UI.ListSources.ChannelListSource.html","topicHref":"client/EchoHub.Client.UI.ListSources.ChannelListSource.html","topicUid":"EchoHub.Client.UI.ListSources.ChannelListSource","type":"Class"},{"name":"SearchListSource","href":"client/EchoHub.Client.UI.ListSources.SearchListSource.html","topicHref":"client/EchoHub.Client.UI.ListSources.SearchListSource.html","topicUid":"EchoHub.Client.UI.ListSources.SearchListSource","type":"Class"},{"name":"UserListSource","href":"client/EchoHub.Client.UI.ListSources.UserListSource.html","topicHref":"client/EchoHub.Client.UI.ListSources.UserListSource.html","topicUid":"EchoHub.Client.UI.ListSources.UserListSource","type":"Class"}]}]},{"name":"Articles","includedFrom":"~/api/client-articles/toc.yml","items":[{"name":"Overview","href":"client-articles/index.html","topicHref":"client-articles/index.html"}]}]},{"name":"Core","items":[{"name":"API Reference","includedFrom":"~/_api_meta/core/toc.yml","items":[{"name":"EchoHub.Core.Constants","href":"core/EchoHub.Core.Constants.html","topicHref":"core/EchoHub.Core.Constants.html","topicUid":"EchoHub.Core.Constants","type":"Namespace","items":[{"name":"HubConstants","href":"core/EchoHub.Core.Constants.HubConstants.html","topicHref":"core/EchoHub.Core.Constants.HubConstants.html","topicUid":"EchoHub.Core.Constants.HubConstants","type":"Class"},{"name":"ValidationConstants","href":"core/EchoHub.Core.Constants.ValidationConstants.html","topicHref":"core/EchoHub.Core.Constants.ValidationConstants.html","topicUid":"EchoHub.Core.Constants.ValidationConstants","type":"Class"}]},{"name":"EchoHub.Core.Contracts","href":"core/EchoHub.Core.Contracts.html","topicHref":"core/EchoHub.Core.Contracts.html","topicUid":"EchoHub.Core.Contracts","type":"Namespace","items":[{"name":"ChannelListItem","href":"core/EchoHub.Core.Contracts.ChannelListItem.html","topicHref":"core/EchoHub.Core.Contracts.ChannelListItem.html","topicUid":"EchoHub.Core.Contracts.ChannelListItem","type":"Class"},{"name":"IChannelService","href":"core/EchoHub.Core.Contracts.IChannelService.html","topicHref":"core/EchoHub.Core.Contracts.IChannelService.html","topicUid":"EchoHub.Core.Contracts.IChannelService","type":"Interface"},{"name":"IChatBroadcaster","href":"core/EchoHub.Core.Contracts.IChatBroadcaster.html","topicHref":"core/EchoHub.Core.Contracts.IChatBroadcaster.html","topicUid":"EchoHub.Core.Contracts.IChatBroadcaster","type":"Interface"},{"name":"IChatService","href":"core/EchoHub.Core.Contracts.IChatService.html","topicHref":"core/EchoHub.Core.Contracts.IChatService.html","topicUid":"EchoHub.Core.Contracts.IChatService","type":"Interface"},{"name":"IEchoHubClient","href":"core/EchoHub.Core.Contracts.IEchoHubClient.html","topicHref":"core/EchoHub.Core.Contracts.IEchoHubClient.html","topicUid":"EchoHub.Core.Contracts.IEchoHubClient","type":"Interface"},{"name":"IMessageEncryptionService","href":"core/EchoHub.Core.Contracts.IMessageEncryptionService.html","topicHref":"core/EchoHub.Core.Contracts.IMessageEncryptionService.html","topicUid":"EchoHub.Core.Contracts.IMessageEncryptionService","type":"Interface"},{"name":"IUserService","href":"core/EchoHub.Core.Contracts.IUserService.html","topicHref":"core/EchoHub.Core.Contracts.IUserService.html","topicUid":"EchoHub.Core.Contracts.IUserService","type":"Interface"}]},{"name":"EchoHub.Core.DTOs","href":"core/EchoHub.Core.DTOs.html","topicHref":"core/EchoHub.Core.DTOs.html","topicUid":"EchoHub.Core.DTOs","type":"Namespace","items":[{"name":"ApiResponse","href":"core/EchoHub.Core.DTOs.ApiResponse.html","topicHref":"core/EchoHub.Core.DTOs.ApiResponse.html","topicUid":"EchoHub.Core.DTOs.ApiResponse","type":"Class"},{"name":"ApiResponse","href":"core/EchoHub.Core.DTOs.ApiResponse-1.html","topicHref":"core/EchoHub.Core.DTOs.ApiResponse-1.html","topicUid":"EchoHub.Core.DTOs.ApiResponse`1","type":"Class"},{"name":"AssignRoleRequest","href":"core/EchoHub.Core.DTOs.AssignRoleRequest.html","topicHref":"core/EchoHub.Core.DTOs.AssignRoleRequest.html","topicUid":"EchoHub.Core.DTOs.AssignRoleRequest","type":"Class"},{"name":"AttachmentDto","href":"core/EchoHub.Core.DTOs.AttachmentDto.html","topicHref":"core/EchoHub.Core.DTOs.AttachmentDto.html","topicUid":"EchoHub.Core.DTOs.AttachmentDto","type":"Class"},{"name":"AvatarUploadResponse","href":"core/EchoHub.Core.DTOs.AvatarUploadResponse.html","topicHref":"core/EchoHub.Core.DTOs.AvatarUploadResponse.html","topicUid":"EchoHub.Core.DTOs.AvatarUploadResponse","type":"Class"},{"name":"BanRequest","href":"core/EchoHub.Core.DTOs.BanRequest.html","topicHref":"core/EchoHub.Core.DTOs.BanRequest.html","topicUid":"EchoHub.Core.DTOs.BanRequest","type":"Class"},{"name":"ChannelCryptoDto","href":"core/EchoHub.Core.DTOs.ChannelCryptoDto.html","topicHref":"core/EchoHub.Core.DTOs.ChannelCryptoDto.html","topicUid":"EchoHub.Core.DTOs.ChannelCryptoDto","type":"Class"},{"name":"ChannelDto","href":"core/EchoHub.Core.DTOs.ChannelDto.html","topicHref":"core/EchoHub.Core.DTOs.ChannelDto.html","topicUid":"EchoHub.Core.DTOs.ChannelDto","type":"Class"},{"name":"ChannelError","href":"core/EchoHub.Core.DTOs.ChannelError.html","topicHref":"core/EchoHub.Core.DTOs.ChannelError.html","topicUid":"EchoHub.Core.DTOs.ChannelError","type":"Enum"},{"name":"ChannelMetaDto","href":"core/EchoHub.Core.DTOs.ChannelMetaDto.html","topicHref":"core/EchoHub.Core.DTOs.ChannelMetaDto.html","topicUid":"EchoHub.Core.DTOs.ChannelMetaDto","type":"Class"},{"name":"ChannelOperationResult","href":"core/EchoHub.Core.DTOs.ChannelOperationResult.html","topicHref":"core/EchoHub.Core.DTOs.ChannelOperationResult.html","topicUid":"EchoHub.Core.DTOs.ChannelOperationResult","type":"Class"},{"name":"CreateChannelRequest","href":"core/EchoHub.Core.DTOs.CreateChannelRequest.html","topicHref":"core/EchoHub.Core.DTOs.CreateChannelRequest.html","topicUid":"EchoHub.Core.DTOs.CreateChannelRequest","type":"Class"},{"name":"EmbedDto","href":"core/EchoHub.Core.DTOs.EmbedDto.html","topicHref":"core/EchoHub.Core.DTOs.EmbedDto.html","topicUid":"EchoHub.Core.DTOs.EmbedDto","type":"Class"},{"name":"EncryptionKeyResponse","href":"core/EchoHub.Core.DTOs.EncryptionKeyResponse.html","topicHref":"core/EchoHub.Core.DTOs.EncryptionKeyResponse.html","topicUid":"EchoHub.Core.DTOs.EncryptionKeyResponse","type":"Class"},{"name":"ErrorResponse","href":"core/EchoHub.Core.DTOs.ErrorResponse.html","topicHref":"core/EchoHub.Core.DTOs.ErrorResponse.html","topicUid":"EchoHub.Core.DTOs.ErrorResponse","type":"Class"},{"name":"JoinChannelResult","href":"core/EchoHub.Core.DTOs.JoinChannelResult.html","topicHref":"core/EchoHub.Core.DTOs.JoinChannelResult.html","topicUid":"EchoHub.Core.DTOs.JoinChannelResult","type":"Class"},{"name":"KickRequest","href":"core/EchoHub.Core.DTOs.KickRequest.html","topicHref":"core/EchoHub.Core.DTOs.KickRequest.html","topicUid":"EchoHub.Core.DTOs.KickRequest","type":"Class"},{"name":"LoginRequest","href":"core/EchoHub.Core.DTOs.LoginRequest.html","topicHref":"core/EchoHub.Core.DTOs.LoginRequest.html","topicUid":"EchoHub.Core.DTOs.LoginRequest","type":"Class"},{"name":"LoginResponse","href":"core/EchoHub.Core.DTOs.LoginResponse.html","topicHref":"core/EchoHub.Core.DTOs.LoginResponse.html","topicUid":"EchoHub.Core.DTOs.LoginResponse","type":"Class"},{"name":"MessageDto","href":"core/EchoHub.Core.DTOs.MessageDto.html","topicHref":"core/EchoHub.Core.DTOs.MessageDto.html","topicUid":"EchoHub.Core.DTOs.MessageDto","type":"Class"},{"name":"MuteRequest","href":"core/EchoHub.Core.DTOs.MuteRequest.html","topicHref":"core/EchoHub.Core.DTOs.MuteRequest.html","topicUid":"EchoHub.Core.DTOs.MuteRequest","type":"Class"},{"name":"PaginatedResponse","href":"core/EchoHub.Core.DTOs.PaginatedResponse-1.html","topicHref":"core/EchoHub.Core.DTOs.PaginatedResponse-1.html","topicUid":"EchoHub.Core.DTOs.PaginatedResponse`1","type":"Class"},{"name":"RefreshRequest","href":"core/EchoHub.Core.DTOs.RefreshRequest.html","topicHref":"core/EchoHub.Core.DTOs.RefreshRequest.html","topicUid":"EchoHub.Core.DTOs.RefreshRequest","type":"Class"},{"name":"RegisterRequest","href":"core/EchoHub.Core.DTOs.RegisterRequest.html","topicHref":"core/EchoHub.Core.DTOs.RegisterRequest.html","topicUid":"EchoHub.Core.DTOs.RegisterRequest","type":"Class"},{"name":"RekeyChannelRequest","href":"core/EchoHub.Core.DTOs.RekeyChannelRequest.html","topicHref":"core/EchoHub.Core.DTOs.RekeyChannelRequest.html","topicUid":"EchoHub.Core.DTOs.RekeyChannelRequest","type":"Class"},{"name":"SendMessageRequest","href":"core/EchoHub.Core.DTOs.SendMessageRequest.html","topicHref":"core/EchoHub.Core.DTOs.SendMessageRequest.html","topicUid":"EchoHub.Core.DTOs.SendMessageRequest","type":"Class"},{"name":"SendUrlRequest","href":"core/EchoHub.Core.DTOs.SendUrlRequest.html","topicHref":"core/EchoHub.Core.DTOs.SendUrlRequest.html","topicUid":"EchoHub.Core.DTOs.SendUrlRequest","type":"Class"},{"name":"ServerStatusDto","href":"core/EchoHub.Core.DTOs.ServerStatusDto.html","topicHref":"core/EchoHub.Core.DTOs.ServerStatusDto.html","topicUid":"EchoHub.Core.DTOs.ServerStatusDto","type":"Class"},{"name":"UpdateProfileRequest","href":"core/EchoHub.Core.DTOs.UpdateProfileRequest.html","topicHref":"core/EchoHub.Core.DTOs.UpdateProfileRequest.html","topicUid":"EchoHub.Core.DTOs.UpdateProfileRequest","type":"Class"},{"name":"UpdateStatusRequest","href":"core/EchoHub.Core.DTOs.UpdateStatusRequest.html","topicHref":"core/EchoHub.Core.DTOs.UpdateStatusRequest.html","topicUid":"EchoHub.Core.DTOs.UpdateStatusRequest","type":"Class"},{"name":"UpdateTopicRequest","href":"core/EchoHub.Core.DTOs.UpdateTopicRequest.html","topicHref":"core/EchoHub.Core.DTOs.UpdateTopicRequest.html","topicUid":"EchoHub.Core.DTOs.UpdateTopicRequest","type":"Class"},{"name":"UserDto","href":"core/EchoHub.Core.DTOs.UserDto.html","topicHref":"core/EchoHub.Core.DTOs.UserDto.html","topicUid":"EchoHub.Core.DTOs.UserDto","type":"Class"},{"name":"UserError","href":"core/EchoHub.Core.DTOs.UserError.html","topicHref":"core/EchoHub.Core.DTOs.UserError.html","topicUid":"EchoHub.Core.DTOs.UserError","type":"Enum"},{"name":"UserOperationResult","href":"core/EchoHub.Core.DTOs.UserOperationResult.html","topicHref":"core/EchoHub.Core.DTOs.UserOperationResult.html","topicUid":"EchoHub.Core.DTOs.UserOperationResult","type":"Class"},{"name":"UserPresenceDto","href":"core/EchoHub.Core.DTOs.UserPresenceDto.html","topicHref":"core/EchoHub.Core.DTOs.UserPresenceDto.html","topicUid":"EchoHub.Core.DTOs.UserPresenceDto","type":"Class"},{"name":"UserProfileDto","href":"core/EchoHub.Core.DTOs.UserProfileDto.html","topicHref":"core/EchoHub.Core.DTOs.UserProfileDto.html","topicUid":"EchoHub.Core.DTOs.UserProfileDto","type":"Class"}]},{"name":"EchoHub.Core.Models","href":"core/EchoHub.Core.Models.html","topicHref":"core/EchoHub.Core.Models.html","topicUid":"EchoHub.Core.Models","type":"Namespace","items":[{"name":"Attachment","href":"core/EchoHub.Core.Models.Attachment.html","topicHref":"core/EchoHub.Core.Models.Attachment.html","topicUid":"EchoHub.Core.Models.Attachment","type":"Class"},{"name":"AttachmentKind","href":"core/EchoHub.Core.Models.AttachmentKind.html","topicHref":"core/EchoHub.Core.Models.AttachmentKind.html","topicUid":"EchoHub.Core.Models.AttachmentKind","type":"Enum"},{"name":"Channel","href":"core/EchoHub.Core.Models.Channel.html","topicHref":"core/EchoHub.Core.Models.Channel.html","topicUid":"EchoHub.Core.Models.Channel","type":"Class"},{"name":"ChannelMembership","href":"core/EchoHub.Core.Models.ChannelMembership.html","topicHref":"core/EchoHub.Core.Models.ChannelMembership.html","topicUid":"EchoHub.Core.Models.ChannelMembership","type":"Class"},{"name":"Message","href":"core/EchoHub.Core.Models.Message.html","topicHref":"core/EchoHub.Core.Models.Message.html","topicUid":"EchoHub.Core.Models.Message","type":"Class"},{"name":"MessageType","href":"core/EchoHub.Core.Models.MessageType.html","topicHref":"core/EchoHub.Core.Models.MessageType.html","topicUid":"EchoHub.Core.Models.MessageType","type":"Enum"},{"name":"RefreshToken","href":"core/EchoHub.Core.Models.RefreshToken.html","topicHref":"core/EchoHub.Core.Models.RefreshToken.html","topicUid":"EchoHub.Core.Models.RefreshToken","type":"Class"},{"name":"ServerRole","href":"core/EchoHub.Core.Models.ServerRole.html","topicHref":"core/EchoHub.Core.Models.ServerRole.html","topicUid":"EchoHub.Core.Models.ServerRole","type":"Enum"},{"name":"User","href":"core/EchoHub.Core.Models.User.html","topicHref":"core/EchoHub.Core.Models.User.html","topicUid":"EchoHub.Core.Models.User","type":"Class"},{"name":"UserStatus","href":"core/EchoHub.Core.Models.UserStatus.html","topicHref":"core/EchoHub.Core.Models.UserStatus.html","topicUid":"EchoHub.Core.Models.UserStatus","type":"Enum"}]},{"name":"EchoHub.Core.Security","href":"core/EchoHub.Core.Security.html","topicHref":"core/EchoHub.Core.Security.html","topicUid":"EchoHub.Core.Security","type":"Namespace","items":[{"name":"RoomCrypto","href":"core/EchoHub.Core.Security.RoomCrypto.html","topicHref":"core/EchoHub.Core.Security.RoomCrypto.html","topicUid":"EchoHub.Core.Security.RoomCrypto","type":"Class"},{"name":"RoomCrypto.DerivedKeys","href":"core/EchoHub.Core.Security.RoomCrypto.DerivedKeys.html","topicHref":"core/EchoHub.Core.Security.RoomCrypto.DerivedKeys.html","topicUid":"EchoHub.Core.Security.RoomCrypto.DerivedKeys","type":"Class"}]},{"name":"EchoHub.Core.Services","href":"core/EchoHub.Core.Services.html","topicHref":"core/EchoHub.Core.Services.html","topicUid":"EchoHub.Core.Services","type":"Namespace","items":[{"name":"FileValidationHelper","href":"core/EchoHub.Core.Services.FileValidationHelper.html","topicHref":"core/EchoHub.Core.Services.FileValidationHelper.html","topicUid":"EchoHub.Core.Services.FileValidationHelper","type":"Class"},{"name":"ImageToAsciiService","href":"core/EchoHub.Core.Services.ImageToAsciiService.html","topicHref":"core/EchoHub.Core.Services.ImageToAsciiService.html","topicUid":"EchoHub.Core.Services.ImageToAsciiService","type":"Class"}]}]},{"name":"Articles","includedFrom":"~/api/core-articles/toc.yml","items":[{"name":"Overview","href":"core-articles/index.html","topicHref":"core-articles/index.html"}]}]},{"name":"Server","items":[{"name":"API Reference","includedFrom":"~/_api_meta/server/toc.yml","items":[{"name":"EchoHub.Server.Auth","href":"server/EchoHub.Server.Auth.html","topicHref":"server/EchoHub.Server.Auth.html","topicUid":"EchoHub.Server.Auth","type":"Namespace","items":[{"name":"JwtTokenService","href":"server/EchoHub.Server.Auth.JwtTokenService.html","topicHref":"server/EchoHub.Server.Auth.JwtTokenService.html","topicUid":"EchoHub.Server.Auth.JwtTokenService","type":"Class"}]},{"name":"EchoHub.Server.Config","href":"server/EchoHub.Server.Config.html","topicHref":"server/EchoHub.Server.Config.html","topicUid":"EchoHub.Server.Config","type":"Namespace","items":[{"name":"UploadLimits","href":"server/EchoHub.Server.Config.UploadLimits.html","topicHref":"server/EchoHub.Server.Config.UploadLimits.html","topicUid":"EchoHub.Server.Config.UploadLimits","type":"Class"}]},{"name":"EchoHub.Server.Controllers","href":"server/EchoHub.Server.Controllers.html","topicHref":"server/EchoHub.Server.Controllers.html","topicUid":"EchoHub.Server.Controllers","type":"Namespace","items":[{"name":"AuthController","href":"server/EchoHub.Server.Controllers.AuthController.html","topicHref":"server/EchoHub.Server.Controllers.AuthController.html","topicUid":"EchoHub.Server.Controllers.AuthController","type":"Class"},{"name":"ChannelsController","href":"server/EchoHub.Server.Controllers.ChannelsController.html","topicHref":"server/EchoHub.Server.Controllers.ChannelsController.html","topicUid":"EchoHub.Server.Controllers.ChannelsController","type":"Class"},{"name":"FilesController","href":"server/EchoHub.Server.Controllers.FilesController.html","topicHref":"server/EchoHub.Server.Controllers.FilesController.html","topicUid":"EchoHub.Server.Controllers.FilesController","type":"Class"},{"name":"ModerationController","href":"server/EchoHub.Server.Controllers.ModerationController.html","topicHref":"server/EchoHub.Server.Controllers.ModerationController.html","topicUid":"EchoHub.Server.Controllers.ModerationController","type":"Class"},{"name":"ServerController","href":"server/EchoHub.Server.Controllers.ServerController.html","topicHref":"server/EchoHub.Server.Controllers.ServerController.html","topicUid":"EchoHub.Server.Controllers.ServerController","type":"Class"},{"name":"UsersController","href":"server/EchoHub.Server.Controllers.UsersController.html","topicHref":"server/EchoHub.Server.Controllers.UsersController.html","topicUid":"EchoHub.Server.Controllers.UsersController","type":"Class"}]},{"name":"EchoHub.Server.Data","href":"server/EchoHub.Server.Data.html","topicHref":"server/EchoHub.Server.Data.html","topicUid":"EchoHub.Server.Data","type":"Namespace","items":[{"name":"EchoHubDbContext","href":"server/EchoHub.Server.Data.EchoHubDbContext.html","topicHref":"server/EchoHub.Server.Data.EchoHubDbContext.html","topicUid":"EchoHub.Server.Data.EchoHubDbContext","type":"Class"}]},{"name":"EchoHub.Server.Data.Migrations","href":"server/EchoHub.Server.Data.Migrations.html","topicHref":"server/EchoHub.Server.Data.Migrations.html","topicUid":"EchoHub.Server.Data.Migrations","type":"Namespace","items":[{"name":"AddAttachmentFileSize","href":"server/EchoHub.Server.Data.Migrations.AddAttachmentFileSize.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddAttachmentFileSize.html","topicUid":"EchoHub.Server.Data.Migrations.AddAttachmentFileSize","type":"Class"},{"name":"AddChannelEncryptionEnvelope","href":"server/EchoHub.Server.Data.Migrations.AddChannelEncryptionEnvelope.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddChannelEncryptionEnvelope.html","topicUid":"EchoHub.Server.Data.Migrations.AddChannelEncryptionEnvelope","type":"Class"},{"name":"AddChannelIsPublic","href":"server/EchoHub.Server.Data.Migrations.AddChannelIsPublic.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddChannelIsPublic.html","topicUid":"EchoHub.Server.Data.Migrations.AddChannelIsPublic","type":"Class"},{"name":"AddChannelMembership","href":"server/EchoHub.Server.Data.Migrations.AddChannelMembership.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddChannelMembership.html","topicUid":"EchoHub.Server.Data.Migrations.AddChannelMembership","type":"Class"},{"name":"AddChannelPasswordHash","href":"server/EchoHub.Server.Data.Migrations.AddChannelPasswordHash.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddChannelPasswordHash.html","topicUid":"EchoHub.Server.Data.Migrations.AddChannelPasswordHash","type":"Class"},{"name":"AddEncryptionSupport","href":"server/EchoHub.Server.Data.Migrations.AddEncryptionSupport.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddEncryptionSupport.html","topicUid":"EchoHub.Server.Data.Migrations.AddEncryptionSupport","type":"Class"},{"name":"AddMessageAttachments","href":"server/EchoHub.Server.Data.Migrations.AddMessageAttachments.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddMessageAttachments.html","topicUid":"EchoHub.Server.Data.Migrations.AddMessageAttachments","type":"Class"},{"name":"AddMessageEmbed","href":"server/EchoHub.Server.Data.Migrations.AddMessageEmbed.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddMessageEmbed.html","topicUid":"EchoHub.Server.Data.Migrations.AddMessageEmbed","type":"Class"},{"name":"AddModerationRoles","href":"server/EchoHub.Server.Data.Migrations.AddModerationRoles.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddModerationRoles.html","topicUid":"EchoHub.Server.Data.Migrations.AddModerationRoles","type":"Class"},{"name":"InitialCreate","href":"server/EchoHub.Server.Data.Migrations.InitialCreate.html","topicHref":"server/EchoHub.Server.Data.Migrations.InitialCreate.html","topicUid":"EchoHub.Server.Data.Migrations.InitialCreate","type":"Class"}]},{"name":"EchoHub.Server.Hubs","href":"server/EchoHub.Server.Hubs.html","topicHref":"server/EchoHub.Server.Hubs.html","topicUid":"EchoHub.Server.Hubs","type":"Namespace","items":[{"name":"ChatHub","href":"server/EchoHub.Server.Hubs.ChatHub.html","topicHref":"server/EchoHub.Server.Hubs.ChatHub.html","topicUid":"EchoHub.Server.Hubs.ChatHub","type":"Class"}]},{"name":"EchoHub.Server.Services","href":"server/EchoHub.Server.Services.html","topicHref":"server/EchoHub.Server.Services.html","topicUid":"EchoHub.Server.Services","type":"Namespace","items":[{"name":"ChannelService","href":"server/EchoHub.Server.Services.ChannelService.html","topicHref":"server/EchoHub.Server.Services.ChannelService.html","topicUid":"EchoHub.Server.Services.ChannelService","type":"Class"},{"name":"ChatService","href":"server/EchoHub.Server.Services.ChatService.html","topicHref":"server/EchoHub.Server.Services.ChatService.html","topicUid":"EchoHub.Server.Services.ChatService","type":"Class"},{"name":"DirectoryClaimStore","href":"server/EchoHub.Server.Services.DirectoryClaimStore.html","topicHref":"server/EchoHub.Server.Services.DirectoryClaimStore.html","topicUid":"EchoHub.Server.Services.DirectoryClaimStore","type":"Class"},{"name":"FileCleanupService","href":"server/EchoHub.Server.Services.FileCleanupService.html","topicHref":"server/EchoHub.Server.Services.FileCleanupService.html","topicUid":"EchoHub.Server.Services.FileCleanupService","type":"Class"},{"name":"FileStorageService","href":"server/EchoHub.Server.Services.FileStorageService.html","topicHref":"server/EchoHub.Server.Services.FileStorageService.html","topicUid":"EchoHub.Server.Services.FileStorageService","type":"Class"},{"name":"LinkEmbedService","href":"server/EchoHub.Server.Services.LinkEmbedService.html","topicHref":"server/EchoHub.Server.Services.LinkEmbedService.html","topicUid":"EchoHub.Server.Services.LinkEmbedService","type":"Class"},{"name":"MessageEncryptionService","href":"server/EchoHub.Server.Services.MessageEncryptionService.html","topicHref":"server/EchoHub.Server.Services.MessageEncryptionService.html","topicUid":"EchoHub.Server.Services.MessageEncryptionService","type":"Class"},{"name":"MuteExpirationService","href":"server/EchoHub.Server.Services.MuteExpirationService.html","topicHref":"server/EchoHub.Server.Services.MuteExpirationService.html","topicUid":"EchoHub.Server.Services.MuteExpirationService","type":"Class"},{"name":"PresenceTracker","href":"server/EchoHub.Server.Services.PresenceTracker.html","topicHref":"server/EchoHub.Server.Services.PresenceTracker.html","topicUid":"EchoHub.Server.Services.PresenceTracker","type":"Class"},{"name":"RegistrationStatus","href":"server/EchoHub.Server.Services.RegistrationStatus.html","topicHref":"server/EchoHub.Server.Services.RegistrationStatus.html","topicUid":"EchoHub.Server.Services.RegistrationStatus","type":"Class"},{"name":"ServerDirectoryService","href":"server/EchoHub.Server.Services.ServerDirectoryService.html","topicHref":"server/EchoHub.Server.Services.ServerDirectoryService.html","topicUid":"EchoHub.Server.Services.ServerDirectoryService","type":"Class"},{"name":"SignalRBroadcaster","href":"server/EchoHub.Server.Services.SignalRBroadcaster.html","topicHref":"server/EchoHub.Server.Services.SignalRBroadcaster.html","topicUid":"EchoHub.Server.Services.SignalRBroadcaster","type":"Class"},{"name":"UserService","href":"server/EchoHub.Server.Services.UserService.html","topicHref":"server/EchoHub.Server.Services.UserService.html","topicUid":"EchoHub.Server.Services.UserService","type":"Class"}]},{"name":"EchoHub.Server.Setup","href":"server/EchoHub.Server.Setup.html","topicHref":"server/EchoHub.Server.Setup.html","topicUid":"EchoHub.Server.Setup","type":"Namespace","items":[{"name":"DataMigrationService","href":"server/EchoHub.Server.Setup.DataMigrationService.html","topicHref":"server/EchoHub.Server.Setup.DataMigrationService.html","topicUid":"EchoHub.Server.Setup.DataMigrationService","type":"Class"},{"name":"DatabaseSetup","href":"server/EchoHub.Server.Setup.DatabaseSetup.html","topicHref":"server/EchoHub.Server.Setup.DatabaseSetup.html","topicUid":"EchoHub.Server.Setup.DatabaseSetup","type":"Class"},{"name":"FirstRunSetup","href":"server/EchoHub.Server.Setup.FirstRunSetup.html","topicHref":"server/EchoHub.Server.Setup.FirstRunSetup.html","topicUid":"EchoHub.Server.Setup.FirstRunSetup","type":"Class"}]}]},{"name":"Articles","includedFrom":"~/api/server-articles/toc.yml","items":[{"name":"Overview","href":"server-articles/index.html","topicHref":"server-articles/index.html"}]}]},{"name":"Server.Irc","items":[{"name":"API Reference","includedFrom":"~/_api_meta/server-irc/toc.yml","items":[{"name":"EchoHub.Server.Irc","href":"server-irc/EchoHub.Server.Irc.html","topicHref":"server-irc/EchoHub.Server.Irc.html","topicUid":"EchoHub.Server.Irc","type":"Namespace","items":[{"name":"IrcBroadcaster","href":"server-irc/EchoHub.Server.Irc.IrcBroadcaster.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcBroadcaster.html","topicUid":"EchoHub.Server.Irc.IrcBroadcaster","type":"Class"},{"name":"IrcClientConnection","href":"server-irc/EchoHub.Server.Irc.IrcClientConnection.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcClientConnection.html","topicUid":"EchoHub.Server.Irc.IrcClientConnection","type":"Class"},{"name":"IrcCommandHandler","href":"server-irc/EchoHub.Server.Irc.IrcCommandHandler.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcCommandHandler.html","topicUid":"EchoHub.Server.Irc.IrcCommandHandler","type":"Class"},{"name":"IrcGatewayService","href":"server-irc/EchoHub.Server.Irc.IrcGatewayService.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcGatewayService.html","topicUid":"EchoHub.Server.Irc.IrcGatewayService","type":"Class"},{"name":"IrcMessage","href":"server-irc/EchoHub.Server.Irc.IrcMessage.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcMessage.html","topicUid":"EchoHub.Server.Irc.IrcMessage","type":"Class"},{"name":"IrcMessageFormatter","href":"server-irc/EchoHub.Server.Irc.IrcMessageFormatter.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcMessageFormatter.html","topicUid":"EchoHub.Server.Irc.IrcMessageFormatter","type":"Class"},{"name":"IrcNumericReply","href":"server-irc/EchoHub.Server.Irc.IrcNumericReply.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcNumericReply.html","topicUid":"EchoHub.Server.Irc.IrcNumericReply","type":"Class"},{"name":"IrcOptions","href":"server-irc/EchoHub.Server.Irc.IrcOptions.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcOptions.html","topicUid":"EchoHub.Server.Irc.IrcOptions","type":"Class"},{"name":"IrcServiceExtensions","href":"server-irc/EchoHub.Server.Irc.IrcServiceExtensions.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcServiceExtensions.html","topicUid":"EchoHub.Server.Irc.IrcServiceExtensions","type":"Class"}]}]}]}]}
+{"items":[{"name":"Client","items":[{"name":"API Reference","includedFrom":"~/_api_meta/client/toc.yml","items":[{"name":"EchoHub.Client","href":"client/EchoHub.Client.html","topicHref":"client/EchoHub.Client.html","topicUid":"EchoHub.Client","type":"Namespace","items":[{"name":"AppOrchestrator","href":"client/EchoHub.Client.AppOrchestrator.html","topicHref":"client/EchoHub.Client.AppOrchestrator.html","topicUid":"EchoHub.Client.AppOrchestrator","type":"Class"}]},{"name":"EchoHub.Client.Commands","href":"client/EchoHub.Client.Commands.html","topicHref":"client/EchoHub.Client.Commands.html","topicUid":"EchoHub.Client.Commands","type":"Namespace","items":[{"name":"CommandHandler","href":"client/EchoHub.Client.Commands.CommandHandler.html","topicHref":"client/EchoHub.Client.Commands.CommandHandler.html","topicUid":"EchoHub.Client.Commands.CommandHandler","type":"Class"},{"name":"CommandResult","href":"client/EchoHub.Client.Commands.CommandResult.html","topicHref":"client/EchoHub.Client.Commands.CommandResult.html","topicUid":"EchoHub.Client.Commands.CommandResult","type":"Class"}]},{"name":"EchoHub.Client.Config","href":"client/EchoHub.Client.Config.html","topicHref":"client/EchoHub.Client.Config.html","topicUid":"EchoHub.Client.Config","type":"Namespace","items":[{"name":"AccountPreset","href":"client/EchoHub.Client.Config.AccountPreset.html","topicHref":"client/EchoHub.Client.Config.AccountPreset.html","topicUid":"EchoHub.Client.Config.AccountPreset","type":"Class"},{"name":"ClientConfig","href":"client/EchoHub.Client.Config.ClientConfig.html","topicHref":"client/EchoHub.Client.Config.ClientConfig.html","topicUid":"EchoHub.Client.Config.ClientConfig","type":"Class"},{"name":"ConfigManager","href":"client/EchoHub.Client.Config.ConfigManager.html","topicHref":"client/EchoHub.Client.Config.ConfigManager.html","topicUid":"EchoHub.Client.Config.ConfigManager","type":"Class"},{"name":"NotificationConfig","href":"client/EchoHub.Client.Config.NotificationConfig.html","topicHref":"client/EchoHub.Client.Config.NotificationConfig.html","topicUid":"EchoHub.Client.Config.NotificationConfig","type":"Class"},{"name":"SavedServer","href":"client/EchoHub.Client.Config.SavedServer.html","topicHref":"client/EchoHub.Client.Config.SavedServer.html","topicUid":"EchoHub.Client.Config.SavedServer","type":"Class"}]},{"name":"EchoHub.Client.Services","href":"client/EchoHub.Client.Services.html","topicHref":"client/EchoHub.Client.Services.html","topicUid":"EchoHub.Client.Services","type":"Namespace","items":[{"name":"ApiClient","href":"client/EchoHub.Client.Services.ApiClient.html","topicHref":"client/EchoHub.Client.Services.ApiClient.html","topicUid":"EchoHub.Client.Services.ApiClient","type":"Class"},{"name":"AsyncRunner","href":"client/EchoHub.Client.Services.AsyncRunner.html","topicHref":"client/EchoHub.Client.Services.AsyncRunner.html","topicUid":"EchoHub.Client.Services.AsyncRunner","type":"Class"},{"name":"AudioPlaybackService","href":"client/EchoHub.Client.Services.AudioPlaybackService.html","topicHref":"client/EchoHub.Client.Services.AudioPlaybackService.html","topicUid":"EchoHub.Client.Services.AudioPlaybackService","type":"Class"},{"name":"BackupInfo","href":"client/EchoHub.Client.Services.BackupInfo.html","topicHref":"client/EchoHub.Client.Services.BackupInfo.html","topicUid":"EchoHub.Client.Services.BackupInfo","type":"Class"},{"name":"ChannelPasswordRequiredException","href":"client/EchoHub.Client.Services.ChannelPasswordRequiredException.html","topicHref":"client/EchoHub.Client.Services.ChannelPasswordRequiredException.html","topicUid":"EchoHub.Client.Services.ChannelPasswordRequiredException","type":"Class"},{"name":"ClientEncryptionService","href":"client/EchoHub.Client.Services.ClientEncryptionService.html","topicHref":"client/EchoHub.Client.Services.ClientEncryptionService.html","topicUid":"EchoHub.Client.Services.ClientEncryptionService","type":"Class"},{"name":"ClipboardFiles","href":"client/EchoHub.Client.Services.ClipboardFiles.html","topicHref":"client/EchoHub.Client.Services.ClipboardFiles.html","topicUid":"EchoHub.Client.Services.ClipboardFiles","type":"Class"},{"name":"ClipboardImage","href":"client/EchoHub.Client.Services.ClipboardImage.html","topicHref":"client/EchoHub.Client.Services.ClipboardImage.html","topicUid":"EchoHub.Client.Services.ClipboardImage","type":"Class"},{"name":"EchoHubConnection","href":"client/EchoHub.Client.Services.EchoHubConnection.html","topicHref":"client/EchoHub.Client.Services.EchoHubConnection.html","topicUid":"EchoHub.Client.Services.EchoHubConnection","type":"Class"},{"name":"FolderPickResult","href":"client/EchoHub.Client.Services.FolderPickResult.html","topicHref":"client/EchoHub.Client.Services.FolderPickResult.html","topicUid":"EchoHub.Client.Services.FolderPickResult","type":"Class"},{"name":"JoinOutcome","href":"client/EchoHub.Client.Services.JoinOutcome.html","topicHref":"client/EchoHub.Client.Services.JoinOutcome.html","topicUid":"EchoHub.Client.Services.JoinOutcome","type":"Class"},{"name":"NativeFolderPicker","href":"client/EchoHub.Client.Services.NativeFolderPicker.html","topicHref":"client/EchoHub.Client.Services.NativeFolderPicker.html","topicUid":"EchoHub.Client.Services.NativeFolderPicker","type":"Class"},{"name":"NotificationSoundService","href":"client/EchoHub.Client.Services.NotificationSoundService.html","topicHref":"client/EchoHub.Client.Services.NotificationSoundService.html","topicUid":"EchoHub.Client.Services.NotificationSoundService","type":"Class"},{"name":"OutgoingAttachment","href":"client/EchoHub.Client.Services.OutgoingAttachment.html","topicHref":"client/EchoHub.Client.Services.OutgoingAttachment.html","topicUid":"EchoHub.Client.Services.OutgoingAttachment","type":"Class"},{"name":"PathSetup","href":"client/EchoHub.Client.Services.PathSetup.html","topicHref":"client/EchoHub.Client.Services.PathSetup.html","topicUid":"EchoHub.Client.Services.PathSetup","type":"Class"},{"name":"PickerOutcome","href":"client/EchoHub.Client.Services.PickerOutcome.html","topicHref":"client/EchoHub.Client.Services.PickerOutcome.html","topicUid":"EchoHub.Client.Services.PickerOutcome","type":"Enum"},{"name":"RoomKeyProtector","href":"client/EchoHub.Client.Services.RoomKeyProtector.html","topicHref":"client/EchoHub.Client.Services.RoomKeyProtector.html","topicUid":"EchoHub.Client.Services.RoomKeyProtector","type":"Class"},{"name":"RoomKeyStore","href":"client/EchoHub.Client.Services.RoomKeyStore.html","topicHref":"client/EchoHub.Client.Services.RoomKeyStore.html","topicUid":"EchoHub.Client.Services.RoomKeyStore","type":"Class"},{"name":"RoomLockedException","href":"client/EchoHub.Client.Services.RoomLockedException.html","topicHref":"client/EchoHub.Client.Services.RoomLockedException.html","topicUid":"EchoHub.Client.Services.RoomLockedException","type":"Class"},{"name":"UpdateBackupService","href":"client/EchoHub.Client.Services.UpdateBackupService.html","topicHref":"client/EchoHub.Client.Services.UpdateBackupService.html","topicUid":"EchoHub.Client.Services.UpdateBackupService","type":"Class"},{"name":"UpdateChecker","href":"client/EchoHub.Client.Services.UpdateChecker.html","topicHref":"client/EchoHub.Client.Services.UpdateChecker.html","topicUid":"EchoHub.Client.Services.UpdateChecker","type":"Class"}]},{"name":"EchoHub.Client.Themes","href":"client/EchoHub.Client.Themes.html","topicHref":"client/EchoHub.Client.Themes.html","topicUid":"EchoHub.Client.Themes","type":"Namespace","items":[{"name":"Theme","href":"client/EchoHub.Client.Themes.Theme.html","topicHref":"client/EchoHub.Client.Themes.Theme.html","topicUid":"EchoHub.Client.Themes.Theme","type":"Class"},{"name":"ThemeColors","href":"client/EchoHub.Client.Themes.ThemeColors.html","topicHref":"client/EchoHub.Client.Themes.ThemeColors.html","topicUid":"EchoHub.Client.Themes.ThemeColors","type":"Class"},{"name":"ThemeManager","href":"client/EchoHub.Client.Themes.ThemeManager.html","topicHref":"client/EchoHub.Client.Themes.ThemeManager.html","topicUid":"EchoHub.Client.Themes.ThemeManager","type":"Class"}]},{"name":"EchoHub.Client.UI","href":"client/EchoHub.Client.UI.html","topicHref":"client/EchoHub.Client.UI.html","topicUid":"EchoHub.Client.UI","type":"Namespace","items":[{"name":"MainWindow","href":"client/EchoHub.Client.UI.MainWindow.html","topicHref":"client/EchoHub.Client.UI.MainWindow.html","topicUid":"EchoHub.Client.UI.MainWindow","type":"Class"}]},{"name":"EchoHub.Client.UI.Chat","href":"client/EchoHub.Client.UI.Chat.html","topicHref":"client/EchoHub.Client.UI.Chat.html","topicUid":"EchoHub.Client.UI.Chat","type":"Namespace","items":[{"name":"AttachmentAction","href":"client/EchoHub.Client.UI.Chat.AttachmentAction.html","topicHref":"client/EchoHub.Client.UI.Chat.AttachmentAction.html","topicUid":"EchoHub.Client.UI.Chat.AttachmentAction","type":"Enum"},{"name":"AttachmentActionSpan","href":"client/EchoHub.Client.UI.Chat.AttachmentActionSpan.html","topicHref":"client/EchoHub.Client.UI.Chat.AttachmentActionSpan.html","topicUid":"EchoHub.Client.UI.Chat.AttachmentActionSpan","type":"Struct"},{"name":"ChatColors","href":"client/EchoHub.Client.UI.Chat.ChatColors.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatColors.html","topicUid":"EchoHub.Client.UI.Chat.ChatColors","type":"Class"},{"name":"ChatLine","href":"client/EchoHub.Client.UI.Chat.ChatLine.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatLine.html","topicUid":"EchoHub.Client.UI.Chat.ChatLine","type":"Class"},{"name":"ChatListSource","href":"client/EchoHub.Client.UI.Chat.ChatListSource.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatListSource.html","topicUid":"EchoHub.Client.UI.Chat.ChatListSource","type":"Class"},{"name":"ChatMessageManager","href":"client/EchoHub.Client.UI.Chat.ChatMessageManager.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatMessageManager.html","topicUid":"EchoHub.Client.UI.Chat.ChatMessageManager","type":"Class"},{"name":"ChatSegment","href":"client/EchoHub.Client.UI.Chat.ChatSegment.html","topicHref":"client/EchoHub.Client.UI.Chat.ChatSegment.html","topicUid":"EchoHub.Client.UI.Chat.ChatSegment","type":"Class"}]},{"name":"EchoHub.Client.UI.Dialogs","href":"client/EchoHub.Client.UI.Dialogs.html","topicHref":"client/EchoHub.Client.UI.Dialogs.html","topicUid":"EchoHub.Client.UI.Dialogs","type":"Namespace","items":[{"name":"AudioPlayerDialog","href":"client/EchoHub.Client.UI.Dialogs.AudioPlayerDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.AudioPlayerDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.AudioPlayerDialog","type":"Class"},{"name":"ChannelPasswordDialog","href":"client/EchoHub.Client.UI.Dialogs.ChannelPasswordDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ChannelPasswordDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.ChannelPasswordDialog","type":"Class"},{"name":"ConnectDialog","href":"client/EchoHub.Client.UI.Dialogs.ConnectDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ConnectDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.ConnectDialog","type":"Class"},{"name":"ConnectDialogResult","href":"client/EchoHub.Client.UI.Dialogs.ConnectDialogResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ConnectDialogResult.html","topicUid":"EchoHub.Client.UI.Dialogs.ConnectDialogResult","type":"Class"},{"name":"CreateChannelDialog","href":"client/EchoHub.Client.UI.Dialogs.CreateChannelDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.CreateChannelDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.CreateChannelDialog","type":"Class"},{"name":"CreateChannelResult","href":"client/EchoHub.Client.UI.Dialogs.CreateChannelResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.CreateChannelResult.html","topicUid":"EchoHub.Client.UI.Dialogs.CreateChannelResult","type":"Class"},{"name":"ProfileAction","href":"client/EchoHub.Client.UI.Dialogs.ProfileAction.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ProfileAction.html","topicUid":"EchoHub.Client.UI.Dialogs.ProfileAction","type":"Enum"},{"name":"ProfileEditDialog","href":"client/EchoHub.Client.UI.Dialogs.ProfileEditDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ProfileEditDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.ProfileEditDialog","type":"Class"},{"name":"ProfileEditResult","href":"client/EchoHub.Client.UI.Dialogs.ProfileEditResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ProfileEditResult.html","topicUid":"EchoHub.Client.UI.Dialogs.ProfileEditResult","type":"Class"},{"name":"ProfileViewDialog","href":"client/EchoHub.Client.UI.Dialogs.ProfileViewDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.ProfileViewDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.ProfileViewDialog","type":"Class"},{"name":"SearchDialog","href":"client/EchoHub.Client.UI.Dialogs.SearchDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.SearchDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.SearchDialog","type":"Class"},{"name":"SearchResult","href":"client/EchoHub.Client.UI.Dialogs.SearchResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.SearchResult.html","topicUid":"EchoHub.Client.UI.Dialogs.SearchResult","type":"Class"},{"name":"SearchResultType","href":"client/EchoHub.Client.UI.Dialogs.SearchResultType.html","topicHref":"client/EchoHub.Client.UI.Dialogs.SearchResultType.html","topicUid":"EchoHub.Client.UI.Dialogs.SearchResultType","type":"Enum"},{"name":"StatusDialog","href":"client/EchoHub.Client.UI.Dialogs.StatusDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.StatusDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.StatusDialog","type":"Class"},{"name":"StatusDialogResult","href":"client/EchoHub.Client.UI.Dialogs.StatusDialogResult.html","topicHref":"client/EchoHub.Client.UI.Dialogs.StatusDialogResult.html","topicUid":"EchoHub.Client.UI.Dialogs.StatusDialogResult","type":"Class"},{"name":"UpdateConfirmDialog","href":"client/EchoHub.Client.UI.Dialogs.UpdateConfirmDialog.html","topicHref":"client/EchoHub.Client.UI.Dialogs.UpdateConfirmDialog.html","topicUid":"EchoHub.Client.UI.Dialogs.UpdateConfirmDialog","type":"Class"}]},{"name":"EchoHub.Client.UI.Helpers","href":"client/EchoHub.Client.UI.Helpers.html","topicHref":"client/EchoHub.Client.UI.Helpers.html","topicUid":"EchoHub.Client.UI.Helpers","type":"Namespace","items":[{"name":"DroppedFileParser","href":"client/EchoHub.Client.UI.Helpers.DroppedFileParser.html","topicHref":"client/EchoHub.Client.UI.Helpers.DroppedFileParser.html","topicUid":"EchoHub.Client.UI.Helpers.DroppedFileParser","type":"Class"},{"name":"EmojiHelper","href":"client/EchoHub.Client.UI.Helpers.EmojiHelper.html","topicHref":"client/EchoHub.Client.UI.Helpers.EmojiHelper.html","topicUid":"EchoHub.Client.UI.Helpers.EmojiHelper","type":"Class"},{"name":"HexColorHelper","href":"client/EchoHub.Client.UI.Helpers.HexColorHelper.html","topicHref":"client/EchoHub.Client.UI.Helpers.HexColorHelper.html","topicUid":"EchoHub.Client.UI.Helpers.HexColorHelper","type":"Class"},{"name":"NickColorHelper","href":"client/EchoHub.Client.UI.Helpers.NickColorHelper.html","topicHref":"client/EchoHub.Client.UI.Helpers.NickColorHelper.html","topicUid":"EchoHub.Client.UI.Helpers.NickColorHelper","type":"Class"}]},{"name":"EchoHub.Client.UI.ListSources","href":"client/EchoHub.Client.UI.ListSources.html","topicHref":"client/EchoHub.Client.UI.ListSources.html","topicUid":"EchoHub.Client.UI.ListSources","type":"Namespace","items":[{"name":"ChannelListSource","href":"client/EchoHub.Client.UI.ListSources.ChannelListSource.html","topicHref":"client/EchoHub.Client.UI.ListSources.ChannelListSource.html","topicUid":"EchoHub.Client.UI.ListSources.ChannelListSource","type":"Class"},{"name":"SearchListSource","href":"client/EchoHub.Client.UI.ListSources.SearchListSource.html","topicHref":"client/EchoHub.Client.UI.ListSources.SearchListSource.html","topicUid":"EchoHub.Client.UI.ListSources.SearchListSource","type":"Class"},{"name":"UserListSource","href":"client/EchoHub.Client.UI.ListSources.UserListSource.html","topicHref":"client/EchoHub.Client.UI.ListSources.UserListSource.html","topicUid":"EchoHub.Client.UI.ListSources.UserListSource","type":"Class"}]}]},{"name":"Articles","includedFrom":"~/api/client-articles/toc.yml","items":[{"name":"Overview","href":"client-articles/index.html","topicHref":"client-articles/index.html"}]}]},{"name":"Core","items":[{"name":"API Reference","includedFrom":"~/_api_meta/core/toc.yml","items":[{"name":"EchoHub.Core.Constants","href":"core/EchoHub.Core.Constants.html","topicHref":"core/EchoHub.Core.Constants.html","topicUid":"EchoHub.Core.Constants","type":"Namespace","items":[{"name":"HubConstants","href":"core/EchoHub.Core.Constants.HubConstants.html","topicHref":"core/EchoHub.Core.Constants.HubConstants.html","topicUid":"EchoHub.Core.Constants.HubConstants","type":"Class"},{"name":"MessageConventions","href":"core/EchoHub.Core.Constants.MessageConventions.html","topicHref":"core/EchoHub.Core.Constants.MessageConventions.html","topicUid":"EchoHub.Core.Constants.MessageConventions","type":"Class"},{"name":"ValidationConstants","href":"core/EchoHub.Core.Constants.ValidationConstants.html","topicHref":"core/EchoHub.Core.Constants.ValidationConstants.html","topicUid":"EchoHub.Core.Constants.ValidationConstants","type":"Class"}]},{"name":"EchoHub.Core.Contracts","href":"core/EchoHub.Core.Contracts.html","topicHref":"core/EchoHub.Core.Contracts.html","topicUid":"EchoHub.Core.Contracts","type":"Namespace","items":[{"name":"ChannelListItem","href":"core/EchoHub.Core.Contracts.ChannelListItem.html","topicHref":"core/EchoHub.Core.Contracts.ChannelListItem.html","topicUid":"EchoHub.Core.Contracts.ChannelListItem","type":"Class"},{"name":"IChannelService","href":"core/EchoHub.Core.Contracts.IChannelService.html","topicHref":"core/EchoHub.Core.Contracts.IChannelService.html","topicUid":"EchoHub.Core.Contracts.IChannelService","type":"Interface"},{"name":"IChatBroadcaster","href":"core/EchoHub.Core.Contracts.IChatBroadcaster.html","topicHref":"core/EchoHub.Core.Contracts.IChatBroadcaster.html","topicUid":"EchoHub.Core.Contracts.IChatBroadcaster","type":"Interface"},{"name":"IChatService","href":"core/EchoHub.Core.Contracts.IChatService.html","topicHref":"core/EchoHub.Core.Contracts.IChatService.html","topicUid":"EchoHub.Core.Contracts.IChatService","type":"Interface"},{"name":"IEchoHubClient","href":"core/EchoHub.Core.Contracts.IEchoHubClient.html","topicHref":"core/EchoHub.Core.Contracts.IEchoHubClient.html","topicUid":"EchoHub.Core.Contracts.IEchoHubClient","type":"Interface"},{"name":"IMessageEncryptionService","href":"core/EchoHub.Core.Contracts.IMessageEncryptionService.html","topicHref":"core/EchoHub.Core.Contracts.IMessageEncryptionService.html","topicUid":"EchoHub.Core.Contracts.IMessageEncryptionService","type":"Interface"},{"name":"IUserService","href":"core/EchoHub.Core.Contracts.IUserService.html","topicHref":"core/EchoHub.Core.Contracts.IUserService.html","topicUid":"EchoHub.Core.Contracts.IUserService","type":"Interface"}]},{"name":"EchoHub.Core.DTOs","href":"core/EchoHub.Core.DTOs.html","topicHref":"core/EchoHub.Core.DTOs.html","topicUid":"EchoHub.Core.DTOs","type":"Namespace","items":[{"name":"ApiResponse","href":"core/EchoHub.Core.DTOs.ApiResponse.html","topicHref":"core/EchoHub.Core.DTOs.ApiResponse.html","topicUid":"EchoHub.Core.DTOs.ApiResponse","type":"Class"},{"name":"ApiResponse","href":"core/EchoHub.Core.DTOs.ApiResponse-1.html","topicHref":"core/EchoHub.Core.DTOs.ApiResponse-1.html","topicUid":"EchoHub.Core.DTOs.ApiResponse`1","type":"Class"},{"name":"AssignRoleRequest","href":"core/EchoHub.Core.DTOs.AssignRoleRequest.html","topicHref":"core/EchoHub.Core.DTOs.AssignRoleRequest.html","topicUid":"EchoHub.Core.DTOs.AssignRoleRequest","type":"Class"},{"name":"AttachmentDto","href":"core/EchoHub.Core.DTOs.AttachmentDto.html","topicHref":"core/EchoHub.Core.DTOs.AttachmentDto.html","topicUid":"EchoHub.Core.DTOs.AttachmentDto","type":"Class"},{"name":"AvatarUploadResponse","href":"core/EchoHub.Core.DTOs.AvatarUploadResponse.html","topicHref":"core/EchoHub.Core.DTOs.AvatarUploadResponse.html","topicUid":"EchoHub.Core.DTOs.AvatarUploadResponse","type":"Class"},{"name":"BanRequest","href":"core/EchoHub.Core.DTOs.BanRequest.html","topicHref":"core/EchoHub.Core.DTOs.BanRequest.html","topicUid":"EchoHub.Core.DTOs.BanRequest","type":"Class"},{"name":"ChannelCryptoDto","href":"core/EchoHub.Core.DTOs.ChannelCryptoDto.html","topicHref":"core/EchoHub.Core.DTOs.ChannelCryptoDto.html","topicUid":"EchoHub.Core.DTOs.ChannelCryptoDto","type":"Class"},{"name":"ChannelDto","href":"core/EchoHub.Core.DTOs.ChannelDto.html","topicHref":"core/EchoHub.Core.DTOs.ChannelDto.html","topicUid":"EchoHub.Core.DTOs.ChannelDto","type":"Class"},{"name":"ChannelError","href":"core/EchoHub.Core.DTOs.ChannelError.html","topicHref":"core/EchoHub.Core.DTOs.ChannelError.html","topicUid":"EchoHub.Core.DTOs.ChannelError","type":"Enum"},{"name":"ChannelMetaDto","href":"core/EchoHub.Core.DTOs.ChannelMetaDto.html","topicHref":"core/EchoHub.Core.DTOs.ChannelMetaDto.html","topicUid":"EchoHub.Core.DTOs.ChannelMetaDto","type":"Class"},{"name":"ChannelOperationResult","href":"core/EchoHub.Core.DTOs.ChannelOperationResult.html","topicHref":"core/EchoHub.Core.DTOs.ChannelOperationResult.html","topicUid":"EchoHub.Core.DTOs.ChannelOperationResult","type":"Class"},{"name":"CreateChannelRequest","href":"core/EchoHub.Core.DTOs.CreateChannelRequest.html","topicHref":"core/EchoHub.Core.DTOs.CreateChannelRequest.html","topicUid":"EchoHub.Core.DTOs.CreateChannelRequest","type":"Class"},{"name":"CreateInviteRequest","href":"core/EchoHub.Core.DTOs.CreateInviteRequest.html","topicHref":"core/EchoHub.Core.DTOs.CreateInviteRequest.html","topicUid":"EchoHub.Core.DTOs.CreateInviteRequest","type":"Class"},{"name":"DeleteAccountRequest","href":"core/EchoHub.Core.DTOs.DeleteAccountRequest.html","topicHref":"core/EchoHub.Core.DTOs.DeleteAccountRequest.html","topicUid":"EchoHub.Core.DTOs.DeleteAccountRequest","type":"Class"},{"name":"EmbedDto","href":"core/EchoHub.Core.DTOs.EmbedDto.html","topicHref":"core/EchoHub.Core.DTOs.EmbedDto.html","topicUid":"EchoHub.Core.DTOs.EmbedDto","type":"Class"},{"name":"EncryptionKeyResponse","href":"core/EchoHub.Core.DTOs.EncryptionKeyResponse.html","topicHref":"core/EchoHub.Core.DTOs.EncryptionKeyResponse.html","topicUid":"EchoHub.Core.DTOs.EncryptionKeyResponse","type":"Class"},{"name":"ErrorResponse","href":"core/EchoHub.Core.DTOs.ErrorResponse.html","topicHref":"core/EchoHub.Core.DTOs.ErrorResponse.html","topicUid":"EchoHub.Core.DTOs.ErrorResponse","type":"Class"},{"name":"ExportedAttachmentDto","href":"core/EchoHub.Core.DTOs.ExportedAttachmentDto.html","topicHref":"core/EchoHub.Core.DTOs.ExportedAttachmentDto.html","topicUid":"EchoHub.Core.DTOs.ExportedAttachmentDto","type":"Class"},{"name":"ExportedMessageDto","href":"core/EchoHub.Core.DTOs.ExportedMessageDto.html","topicHref":"core/EchoHub.Core.DTOs.ExportedMessageDto.html","topicUid":"EchoHub.Core.DTOs.ExportedMessageDto","type":"Class"},{"name":"InviteDto","href":"core/EchoHub.Core.DTOs.InviteDto.html","topicHref":"core/EchoHub.Core.DTOs.InviteDto.html","topicUid":"EchoHub.Core.DTOs.InviteDto","type":"Class"},{"name":"JoinChannelResult","href":"core/EchoHub.Core.DTOs.JoinChannelResult.html","topicHref":"core/EchoHub.Core.DTOs.JoinChannelResult.html","topicUid":"EchoHub.Core.DTOs.JoinChannelResult","type":"Class"},{"name":"KickRequest","href":"core/EchoHub.Core.DTOs.KickRequest.html","topicHref":"core/EchoHub.Core.DTOs.KickRequest.html","topicUid":"EchoHub.Core.DTOs.KickRequest","type":"Class"},{"name":"LoginRequest","href":"core/EchoHub.Core.DTOs.LoginRequest.html","topicHref":"core/EchoHub.Core.DTOs.LoginRequest.html","topicUid":"EchoHub.Core.DTOs.LoginRequest","type":"Class"},{"name":"LoginResponse","href":"core/EchoHub.Core.DTOs.LoginResponse.html","topicHref":"core/EchoHub.Core.DTOs.LoginResponse.html","topicUid":"EchoHub.Core.DTOs.LoginResponse","type":"Class"},{"name":"MessageDto","href":"core/EchoHub.Core.DTOs.MessageDto.html","topicHref":"core/EchoHub.Core.DTOs.MessageDto.html","topicUid":"EchoHub.Core.DTOs.MessageDto","type":"Class"},{"name":"MuteRequest","href":"core/EchoHub.Core.DTOs.MuteRequest.html","topicHref":"core/EchoHub.Core.DTOs.MuteRequest.html","topicUid":"EchoHub.Core.DTOs.MuteRequest","type":"Class"},{"name":"PaginatedResponse","href":"core/EchoHub.Core.DTOs.PaginatedResponse-1.html","topicHref":"core/EchoHub.Core.DTOs.PaginatedResponse-1.html","topicUid":"EchoHub.Core.DTOs.PaginatedResponse`1","type":"Class"},{"name":"RefreshRequest","href":"core/EchoHub.Core.DTOs.RefreshRequest.html","topicHref":"core/EchoHub.Core.DTOs.RefreshRequest.html","topicUid":"EchoHub.Core.DTOs.RefreshRequest","type":"Class"},{"name":"RegisterRequest","href":"core/EchoHub.Core.DTOs.RegisterRequest.html","topicHref":"core/EchoHub.Core.DTOs.RegisterRequest.html","topicUid":"EchoHub.Core.DTOs.RegisterRequest","type":"Class"},{"name":"RekeyChannelRequest","href":"core/EchoHub.Core.DTOs.RekeyChannelRequest.html","topicHref":"core/EchoHub.Core.DTOs.RekeyChannelRequest.html","topicUid":"EchoHub.Core.DTOs.RekeyChannelRequest","type":"Class"},{"name":"ReplyRefDto","href":"core/EchoHub.Core.DTOs.ReplyRefDto.html","topicHref":"core/EchoHub.Core.DTOs.ReplyRefDto.html","topicUid":"EchoHub.Core.DTOs.ReplyRefDto","type":"Class"},{"name":"SendMessageRequest","href":"core/EchoHub.Core.DTOs.SendMessageRequest.html","topicHref":"core/EchoHub.Core.DTOs.SendMessageRequest.html","topicUid":"EchoHub.Core.DTOs.SendMessageRequest","type":"Class"},{"name":"SendUrlRequest","href":"core/EchoHub.Core.DTOs.SendUrlRequest.html","topicHref":"core/EchoHub.Core.DTOs.SendUrlRequest.html","topicUid":"EchoHub.Core.DTOs.SendUrlRequest","type":"Class"},{"name":"ServerStatusDto","href":"core/EchoHub.Core.DTOs.ServerStatusDto.html","topicHref":"core/EchoHub.Core.DTOs.ServerStatusDto.html","topicUid":"EchoHub.Core.DTOs.ServerStatusDto","type":"Class"},{"name":"UpdateProfileRequest","href":"core/EchoHub.Core.DTOs.UpdateProfileRequest.html","topicHref":"core/EchoHub.Core.DTOs.UpdateProfileRequest.html","topicUid":"EchoHub.Core.DTOs.UpdateProfileRequest","type":"Class"},{"name":"UpdateStatusRequest","href":"core/EchoHub.Core.DTOs.UpdateStatusRequest.html","topicHref":"core/EchoHub.Core.DTOs.UpdateStatusRequest.html","topicUid":"EchoHub.Core.DTOs.UpdateStatusRequest","type":"Class"},{"name":"UpdateTopicRequest","href":"core/EchoHub.Core.DTOs.UpdateTopicRequest.html","topicHref":"core/EchoHub.Core.DTOs.UpdateTopicRequest.html","topicUid":"EchoHub.Core.DTOs.UpdateTopicRequest","type":"Class"},{"name":"UserDataExportDto","href":"core/EchoHub.Core.DTOs.UserDataExportDto.html","topicHref":"core/EchoHub.Core.DTOs.UserDataExportDto.html","topicUid":"EchoHub.Core.DTOs.UserDataExportDto","type":"Class"},{"name":"UserDto","href":"core/EchoHub.Core.DTOs.UserDto.html","topicHref":"core/EchoHub.Core.DTOs.UserDto.html","topicUid":"EchoHub.Core.DTOs.UserDto","type":"Class"},{"name":"UserError","href":"core/EchoHub.Core.DTOs.UserError.html","topicHref":"core/EchoHub.Core.DTOs.UserError.html","topicUid":"EchoHub.Core.DTOs.UserError","type":"Enum"},{"name":"UserOperationResult","href":"core/EchoHub.Core.DTOs.UserOperationResult.html","topicHref":"core/EchoHub.Core.DTOs.UserOperationResult.html","topicUid":"EchoHub.Core.DTOs.UserOperationResult","type":"Class"},{"name":"UserPresenceDto","href":"core/EchoHub.Core.DTOs.UserPresenceDto.html","topicHref":"core/EchoHub.Core.DTOs.UserPresenceDto.html","topicUid":"EchoHub.Core.DTOs.UserPresenceDto","type":"Class"},{"name":"UserProfileDto","href":"core/EchoHub.Core.DTOs.UserProfileDto.html","topicHref":"core/EchoHub.Core.DTOs.UserProfileDto.html","topicUid":"EchoHub.Core.DTOs.UserProfileDto","type":"Class"}]},{"name":"EchoHub.Core.Models","href":"core/EchoHub.Core.Models.html","topicHref":"core/EchoHub.Core.Models.html","topicUid":"EchoHub.Core.Models","type":"Namespace","items":[{"name":"Attachment","href":"core/EchoHub.Core.Models.Attachment.html","topicHref":"core/EchoHub.Core.Models.Attachment.html","topicUid":"EchoHub.Core.Models.Attachment","type":"Class"},{"name":"AttachmentKind","href":"core/EchoHub.Core.Models.AttachmentKind.html","topicHref":"core/EchoHub.Core.Models.AttachmentKind.html","topicUid":"EchoHub.Core.Models.AttachmentKind","type":"Enum"},{"name":"Channel","href":"core/EchoHub.Core.Models.Channel.html","topicHref":"core/EchoHub.Core.Models.Channel.html","topicUid":"EchoHub.Core.Models.Channel","type":"Class"},{"name":"ChannelMembership","href":"core/EchoHub.Core.Models.ChannelMembership.html","topicHref":"core/EchoHub.Core.Models.ChannelMembership.html","topicUid":"EchoHub.Core.Models.ChannelMembership","type":"Class"},{"name":"InviteCode","href":"core/EchoHub.Core.Models.InviteCode.html","topicHref":"core/EchoHub.Core.Models.InviteCode.html","topicUid":"EchoHub.Core.Models.InviteCode","type":"Class"},{"name":"Message","href":"core/EchoHub.Core.Models.Message.html","topicHref":"core/EchoHub.Core.Models.Message.html","topicUid":"EchoHub.Core.Models.Message","type":"Class"},{"name":"MessageType","href":"core/EchoHub.Core.Models.MessageType.html","topicHref":"core/EchoHub.Core.Models.MessageType.html","topicUid":"EchoHub.Core.Models.MessageType","type":"Enum"},{"name":"RefreshToken","href":"core/EchoHub.Core.Models.RefreshToken.html","topicHref":"core/EchoHub.Core.Models.RefreshToken.html","topicUid":"EchoHub.Core.Models.RefreshToken","type":"Class"},{"name":"ServerRole","href":"core/EchoHub.Core.Models.ServerRole.html","topicHref":"core/EchoHub.Core.Models.ServerRole.html","topicUid":"EchoHub.Core.Models.ServerRole","type":"Enum"},{"name":"User","href":"core/EchoHub.Core.Models.User.html","topicHref":"core/EchoHub.Core.Models.User.html","topicUid":"EchoHub.Core.Models.User","type":"Class"},{"name":"UserStatus","href":"core/EchoHub.Core.Models.UserStatus.html","topicHref":"core/EchoHub.Core.Models.UserStatus.html","topicUid":"EchoHub.Core.Models.UserStatus","type":"Enum"}]},{"name":"EchoHub.Core.Security","href":"core/EchoHub.Core.Security.html","topicHref":"core/EchoHub.Core.Security.html","topicUid":"EchoHub.Core.Security","type":"Namespace","items":[{"name":"RoomCrypto","href":"core/EchoHub.Core.Security.RoomCrypto.html","topicHref":"core/EchoHub.Core.Security.RoomCrypto.html","topicUid":"EchoHub.Core.Security.RoomCrypto","type":"Class"},{"name":"RoomCrypto.DerivedKeys","href":"core/EchoHub.Core.Security.RoomCrypto.DerivedKeys.html","topicHref":"core/EchoHub.Core.Security.RoomCrypto.DerivedKeys.html","topicUid":"EchoHub.Core.Security.RoomCrypto.DerivedKeys","type":"Class"}]},{"name":"EchoHub.Core.Services","href":"core/EchoHub.Core.Services.html","topicHref":"core/EchoHub.Core.Services.html","topicUid":"EchoHub.Core.Services","type":"Namespace","items":[{"name":"AsciiBannerService","href":"core/EchoHub.Core.Services.AsciiBannerService.html","topicHref":"core/EchoHub.Core.Services.AsciiBannerService.html","topicUid":"EchoHub.Core.Services.AsciiBannerService","type":"Class"},{"name":"FileValidationHelper","href":"core/EchoHub.Core.Services.FileValidationHelper.html","topicHref":"core/EchoHub.Core.Services.FileValidationHelper.html","topicUid":"EchoHub.Core.Services.FileValidationHelper","type":"Class"},{"name":"ImageToAsciiService","href":"core/EchoHub.Core.Services.ImageToAsciiService.html","topicHref":"core/EchoHub.Core.Services.ImageToAsciiService.html","topicUid":"EchoHub.Core.Services.ImageToAsciiService","type":"Class"}]}]},{"name":"Articles","includedFrom":"~/api/core-articles/toc.yml","items":[{"name":"Overview","href":"core-articles/index.html","topicHref":"core-articles/index.html"}]}]},{"name":"Server","items":[{"name":"API Reference","includedFrom":"~/_api_meta/server/toc.yml","items":[{"name":"EchoHub.Server.Auth","href":"server/EchoHub.Server.Auth.html","topicHref":"server/EchoHub.Server.Auth.html","topicUid":"EchoHub.Server.Auth","type":"Namespace","items":[{"name":"JwtTokenService","href":"server/EchoHub.Server.Auth.JwtTokenService.html","topicHref":"server/EchoHub.Server.Auth.JwtTokenService.html","topicUid":"EchoHub.Server.Auth.JwtTokenService","type":"Class"}]},{"name":"EchoHub.Server.Config","href":"server/EchoHub.Server.Config.html","topicHref":"server/EchoHub.Server.Config.html","topicUid":"EchoHub.Server.Config","type":"Namespace","items":[{"name":"ServerLogsOptions","href":"server/EchoHub.Server.Config.ServerLogsOptions.html","topicHref":"server/EchoHub.Server.Config.ServerLogsOptions.html","topicUid":"EchoHub.Server.Config.ServerLogsOptions","type":"Class"},{"name":"SpamOptions","href":"server/EchoHub.Server.Config.SpamOptions.html","topicHref":"server/EchoHub.Server.Config.SpamOptions.html","topicUid":"EchoHub.Server.Config.SpamOptions","type":"Class"},{"name":"UploadLimits","href":"server/EchoHub.Server.Config.UploadLimits.html","topicHref":"server/EchoHub.Server.Config.UploadLimits.html","topicUid":"EchoHub.Server.Config.UploadLimits","type":"Class"}]},{"name":"EchoHub.Server.Controllers","href":"server/EchoHub.Server.Controllers.html","topicHref":"server/EchoHub.Server.Controllers.html","topicUid":"EchoHub.Server.Controllers","type":"Namespace","items":[{"name":"AuthController","href":"server/EchoHub.Server.Controllers.AuthController.html","topicHref":"server/EchoHub.Server.Controllers.AuthController.html","topicUid":"EchoHub.Server.Controllers.AuthController","type":"Class"},{"name":"ChannelsController","href":"server/EchoHub.Server.Controllers.ChannelsController.html","topicHref":"server/EchoHub.Server.Controllers.ChannelsController.html","topicUid":"EchoHub.Server.Controllers.ChannelsController","type":"Class"},{"name":"FilesController","href":"server/EchoHub.Server.Controllers.FilesController.html","topicHref":"server/EchoHub.Server.Controllers.FilesController.html","topicUid":"EchoHub.Server.Controllers.FilesController","type":"Class"},{"name":"InvitesController","href":"server/EchoHub.Server.Controllers.InvitesController.html","topicHref":"server/EchoHub.Server.Controllers.InvitesController.html","topicUid":"EchoHub.Server.Controllers.InvitesController","type":"Class"},{"name":"ModerationController","href":"server/EchoHub.Server.Controllers.ModerationController.html","topicHref":"server/EchoHub.Server.Controllers.ModerationController.html","topicUid":"EchoHub.Server.Controllers.ModerationController","type":"Class"},{"name":"ServerController","href":"server/EchoHub.Server.Controllers.ServerController.html","topicHref":"server/EchoHub.Server.Controllers.ServerController.html","topicUid":"EchoHub.Server.Controllers.ServerController","type":"Class"},{"name":"UsersController","href":"server/EchoHub.Server.Controllers.UsersController.html","topicHref":"server/EchoHub.Server.Controllers.UsersController.html","topicUid":"EchoHub.Server.Controllers.UsersController","type":"Class"}]},{"name":"EchoHub.Server.Data","href":"server/EchoHub.Server.Data.html","topicHref":"server/EchoHub.Server.Data.html","topicUid":"EchoHub.Server.Data","type":"Namespace","items":[{"name":"EchoHubDbContext","href":"server/EchoHub.Server.Data.EchoHubDbContext.html","topicHref":"server/EchoHub.Server.Data.EchoHubDbContext.html","topicUid":"EchoHub.Server.Data.EchoHubDbContext","type":"Class"}]},{"name":"EchoHub.Server.Data.Migrations","href":"server/EchoHub.Server.Data.Migrations.html","topicHref":"server/EchoHub.Server.Data.Migrations.html","topicUid":"EchoHub.Server.Data.Migrations","type":"Namespace","items":[{"name":"AddAttachmentFileSize","href":"server/EchoHub.Server.Data.Migrations.AddAttachmentFileSize.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddAttachmentFileSize.html","topicUid":"EchoHub.Server.Data.Migrations.AddAttachmentFileSize","type":"Class"},{"name":"AddChannelEncryptionEnvelope","href":"server/EchoHub.Server.Data.Migrations.AddChannelEncryptionEnvelope.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddChannelEncryptionEnvelope.html","topicUid":"EchoHub.Server.Data.Migrations.AddChannelEncryptionEnvelope","type":"Class"},{"name":"AddChannelIsPublic","href":"server/EchoHub.Server.Data.Migrations.AddChannelIsPublic.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddChannelIsPublic.html","topicUid":"EchoHub.Server.Data.Migrations.AddChannelIsPublic","type":"Class"},{"name":"AddChannelIsSystem","href":"server/EchoHub.Server.Data.Migrations.AddChannelIsSystem.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddChannelIsSystem.html","topicUid":"EchoHub.Server.Data.Migrations.AddChannelIsSystem","type":"Class"},{"name":"AddChannelMembership","href":"server/EchoHub.Server.Data.Migrations.AddChannelMembership.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddChannelMembership.html","topicUid":"EchoHub.Server.Data.Migrations.AddChannelMembership","type":"Class"},{"name":"AddChannelPasswordHash","href":"server/EchoHub.Server.Data.Migrations.AddChannelPasswordHash.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddChannelPasswordHash.html","topicUid":"EchoHub.Server.Data.Migrations.AddChannelPasswordHash","type":"Class"},{"name":"AddEncryptionSupport","href":"server/EchoHub.Server.Data.Migrations.AddEncryptionSupport.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddEncryptionSupport.html","topicUid":"EchoHub.Server.Data.Migrations.AddEncryptionSupport","type":"Class"},{"name":"AddInvitesAndReplies","href":"server/EchoHub.Server.Data.Migrations.AddInvitesAndReplies.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddInvitesAndReplies.html","topicUid":"EchoHub.Server.Data.Migrations.AddInvitesAndReplies","type":"Class"},{"name":"AddMessageAttachments","href":"server/EchoHub.Server.Data.Migrations.AddMessageAttachments.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddMessageAttachments.html","topicUid":"EchoHub.Server.Data.Migrations.AddMessageAttachments","type":"Class"},{"name":"AddMessageEmbed","href":"server/EchoHub.Server.Data.Migrations.AddMessageEmbed.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddMessageEmbed.html","topicUid":"EchoHub.Server.Data.Migrations.AddMessageEmbed","type":"Class"},{"name":"AddModerationRoles","href":"server/EchoHub.Server.Data.Migrations.AddModerationRoles.html","topicHref":"server/EchoHub.Server.Data.Migrations.AddModerationRoles.html","topicUid":"EchoHub.Server.Data.Migrations.AddModerationRoles","type":"Class"},{"name":"InitialCreate","href":"server/EchoHub.Server.Data.Migrations.InitialCreate.html","topicHref":"server/EchoHub.Server.Data.Migrations.InitialCreate.html","topicUid":"EchoHub.Server.Data.Migrations.InitialCreate","type":"Class"}]},{"name":"EchoHub.Server.Hubs","href":"server/EchoHub.Server.Hubs.html","topicHref":"server/EchoHub.Server.Hubs.html","topicUid":"EchoHub.Server.Hubs","type":"Namespace","items":[{"name":"ChatHub","href":"server/EchoHub.Server.Hubs.ChatHub.html","topicHref":"server/EchoHub.Server.Hubs.ChatHub.html","topicUid":"EchoHub.Server.Hubs.ChatHub","type":"Class"}]},{"name":"EchoHub.Server.Services","href":"server/EchoHub.Server.Services.html","topicHref":"server/EchoHub.Server.Services.html","topicUid":"EchoHub.Server.Services","type":"Namespace","items":[{"name":"ChannelService","href":"server/EchoHub.Server.Services.ChannelService.html","topicHref":"server/EchoHub.Server.Services.ChannelService.html","topicUid":"EchoHub.Server.Services.ChannelService","type":"Class"},{"name":"ChatService","href":"server/EchoHub.Server.Services.ChatService.html","topicHref":"server/EchoHub.Server.Services.ChatService.html","topicUid":"EchoHub.Server.Services.ChatService","type":"Class"},{"name":"DirectoryClaimStore","href":"server/EchoHub.Server.Services.DirectoryClaimStore.html","topicHref":"server/EchoHub.Server.Services.DirectoryClaimStore.html","topicUid":"EchoHub.Server.Services.DirectoryClaimStore","type":"Class"},{"name":"FileCleanupService","href":"server/EchoHub.Server.Services.FileCleanupService.html","topicHref":"server/EchoHub.Server.Services.FileCleanupService.html","topicUid":"EchoHub.Server.Services.FileCleanupService","type":"Class"},{"name":"FileStorageService","href":"server/EchoHub.Server.Services.FileStorageService.html","topicHref":"server/EchoHub.Server.Services.FileStorageService.html","topicUid":"EchoHub.Server.Services.FileStorageService","type":"Class"},{"name":"LinkEmbedService","href":"server/EchoHub.Server.Services.LinkEmbedService.html","topicHref":"server/EchoHub.Server.Services.LinkEmbedService.html","topicUid":"EchoHub.Server.Services.LinkEmbedService","type":"Class"},{"name":"MessageEncryptionService","href":"server/EchoHub.Server.Services.MessageEncryptionService.html","topicHref":"server/EchoHub.Server.Services.MessageEncryptionService.html","topicUid":"EchoHub.Server.Services.MessageEncryptionService","type":"Class"},{"name":"MuteExpirationService","href":"server/EchoHub.Server.Services.MuteExpirationService.html","topicHref":"server/EchoHub.Server.Services.MuteExpirationService.html","topicUid":"EchoHub.Server.Services.MuteExpirationService","type":"Class"},{"name":"PresenceTracker","href":"server/EchoHub.Server.Services.PresenceTracker.html","topicHref":"server/EchoHub.Server.Services.PresenceTracker.html","topicUid":"EchoHub.Server.Services.PresenceTracker","type":"Class"},{"name":"RegistrationStatus","href":"server/EchoHub.Server.Services.RegistrationStatus.html","topicHref":"server/EchoHub.Server.Services.RegistrationStatus.html","topicUid":"EchoHub.Server.Services.RegistrationStatus","type":"Class"},{"name":"ServerDirectoryService","href":"server/EchoHub.Server.Services.ServerDirectoryService.html","topicHref":"server/EchoHub.Server.Services.ServerDirectoryService.html","topicUid":"EchoHub.Server.Services.ServerDirectoryService","type":"Class"},{"name":"SignalRBroadcaster","href":"server/EchoHub.Server.Services.SignalRBroadcaster.html","topicHref":"server/EchoHub.Server.Services.SignalRBroadcaster.html","topicUid":"EchoHub.Server.Services.SignalRBroadcaster","type":"Class"},{"name":"SpamGuard","href":"server/EchoHub.Server.Services.SpamGuard.html","topicHref":"server/EchoHub.Server.Services.SpamGuard.html","topicUid":"EchoHub.Server.Services.SpamGuard","type":"Class"},{"name":"SpamVerdict","href":"server/EchoHub.Server.Services.SpamVerdict.html","topicHref":"server/EchoHub.Server.Services.SpamVerdict.html","topicUid":"EchoHub.Server.Services.SpamVerdict","type":"Struct"},{"name":"SpamVerdictKind","href":"server/EchoHub.Server.Services.SpamVerdictKind.html","topicHref":"server/EchoHub.Server.Services.SpamVerdictKind.html","topicUid":"EchoHub.Server.Services.SpamVerdictKind","type":"Enum"},{"name":"UserService","href":"server/EchoHub.Server.Services.UserService.html","topicHref":"server/EchoHub.Server.Services.UserService.html","topicUid":"EchoHub.Server.Services.UserService","type":"Class"}]},{"name":"EchoHub.Server.Services.ServerLogs","href":"server/EchoHub.Server.Services.ServerLogs.html","topicHref":"server/EchoHub.Server.Services.ServerLogs.html","topicUid":"EchoHub.Server.Services.ServerLogs","type":"Namespace","items":[{"name":"LogBacklogEntry","href":"server/EchoHub.Server.Services.ServerLogs.LogBacklogEntry.html","topicHref":"server/EchoHub.Server.Services.ServerLogs.LogBacklogEntry.html","topicUid":"EchoHub.Server.Services.ServerLogs.LogBacklogEntry","type":"Class"},{"name":"ServerLogsService","href":"server/EchoHub.Server.Services.ServerLogs.ServerLogsService.html","topicHref":"server/EchoHub.Server.Services.ServerLogs.ServerLogsService.html","topicUid":"EchoHub.Server.Services.ServerLogs.ServerLogsService","type":"Class"},{"name":"ServerLogsSink","href":"server/EchoHub.Server.Services.ServerLogs.ServerLogsSink.html","topicHref":"server/EchoHub.Server.Services.ServerLogs.ServerLogsSink.html","topicUid":"EchoHub.Server.Services.ServerLogs.ServerLogsSink","type":"Class"},{"name":"ServerLogsStreamService","href":"server/EchoHub.Server.Services.ServerLogs.ServerLogsStreamService.html","topicHref":"server/EchoHub.Server.Services.ServerLogs.ServerLogsStreamService.html","topicUid":"EchoHub.Server.Services.ServerLogs.ServerLogsStreamService","type":"Class"}]},{"name":"EchoHub.Server.Setup","href":"server/EchoHub.Server.Setup.html","topicHref":"server/EchoHub.Server.Setup.html","topicUid":"EchoHub.Server.Setup","type":"Namespace","items":[{"name":"DataMigrationService","href":"server/EchoHub.Server.Setup.DataMigrationService.html","topicHref":"server/EchoHub.Server.Setup.DataMigrationService.html","topicUid":"EchoHub.Server.Setup.DataMigrationService","type":"Class"},{"name":"DatabaseSetup","href":"server/EchoHub.Server.Setup.DatabaseSetup.html","topicHref":"server/EchoHub.Server.Setup.DatabaseSetup.html","topicUid":"EchoHub.Server.Setup.DatabaseSetup","type":"Class"},{"name":"FirstRunSetup","href":"server/EchoHub.Server.Setup.FirstRunSetup.html","topicHref":"server/EchoHub.Server.Setup.FirstRunSetup.html","topicUid":"EchoHub.Server.Setup.FirstRunSetup","type":"Class"}]}]},{"name":"Articles","includedFrom":"~/api/server-articles/toc.yml","items":[{"name":"Overview","href":"server-articles/index.html","topicHref":"server-articles/index.html"}]}]},{"name":"Server.Irc","items":[{"name":"API Reference","includedFrom":"~/_api_meta/server-irc/toc.yml","items":[{"name":"EchoHub.Server.Irc","href":"server-irc/EchoHub.Server.Irc.html","topicHref":"server-irc/EchoHub.Server.Irc.html","topicUid":"EchoHub.Server.Irc","type":"Namespace","items":[{"name":"IrcBroadcaster","href":"server-irc/EchoHub.Server.Irc.IrcBroadcaster.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcBroadcaster.html","topicUid":"EchoHub.Server.Irc.IrcBroadcaster","type":"Class"},{"name":"IrcClientConnection","href":"server-irc/EchoHub.Server.Irc.IrcClientConnection.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcClientConnection.html","topicUid":"EchoHub.Server.Irc.IrcClientConnection","type":"Class"},{"name":"IrcCommandHandler","href":"server-irc/EchoHub.Server.Irc.IrcCommandHandler.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcCommandHandler.html","topicUid":"EchoHub.Server.Irc.IrcCommandHandler","type":"Class"},{"name":"IrcGatewayService","href":"server-irc/EchoHub.Server.Irc.IrcGatewayService.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcGatewayService.html","topicUid":"EchoHub.Server.Irc.IrcGatewayService","type":"Class"},{"name":"IrcMessage","href":"server-irc/EchoHub.Server.Irc.IrcMessage.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcMessage.html","topicUid":"EchoHub.Server.Irc.IrcMessage","type":"Class"},{"name":"IrcMessageFormatter","href":"server-irc/EchoHub.Server.Irc.IrcMessageFormatter.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcMessageFormatter.html","topicUid":"EchoHub.Server.Irc.IrcMessageFormatter","type":"Class"},{"name":"IrcNumericReply","href":"server-irc/EchoHub.Server.Irc.IrcNumericReply.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcNumericReply.html","topicUid":"EchoHub.Server.Irc.IrcNumericReply","type":"Class"},{"name":"IrcOptions","href":"server-irc/EchoHub.Server.Irc.IrcOptions.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcOptions.html","topicUid":"EchoHub.Server.Irc.IrcOptions","type":"Class"},{"name":"IrcServiceExtensions","href":"server-irc/EchoHub.Server.Irc.IrcServiceExtensions.html","topicHref":"server-irc/EchoHub.Server.Irc.IrcServiceExtensions.html","topicUid":"EchoHub.Server.Irc.IrcServiceExtensions","type":"Class"}]}]}]}]}
diff --git a/articles/configuration.html b/articles/configuration.html
index 1688500..5168c7d 100644
--- a/articles/configuration.html
+++ b/articles/configuration.html
@@ -247,15 +247,30 @@ export Server__PublicServer=true
Register on the public directory
-Server:PublicHost
-(empty)
-Public hostname for the directory listing (e.g. chat.example.com:5000)
+Server:PublicHosts
+[]
+Hostnames advertised to the directory (array, e.g. ["chat.example.com"]). Required when PublicServer is true
+
+
+Server:Tags
+[]
+Topic tags surfaced in the directory browser (array, e.g. ["community", "gaming"])
Server:Admins
[]
Array of admin usernames (e.g. ["alice", "bob"])
+
+Server:Registration
+open
+Registration mode: open, invite (codes via /invite, Admin+), or closed
+
+
+Server:DirectoryClaimPath
+(empty)
+Overrides where the directory claim token file is stored. Empty = directory-claim.json next to the SQLite database. Treat it as a secret and back it up with the database
+
Uploads
@@ -300,6 +315,76 @@ absent or partial Uploads section keeps the built-in defaults. See
The server sizes its request-body limits from these values, so raising a limit here is all
that's needed — no separate Kestrel tuning.
+Spam Protection
+Per-user flood, duplicate, join, and channel-create limits with auto-mute escalation. Mods and
+above are always exempt, and the defaults are lenient enough that a fast typist never trips them.
+An absent or partial Spam section keeps these defaults.
+
+
+
+Key
+Default
+Description
+
+
+
+
+Spam:Enabled
+true
+Master switch for all spam protection
+
+
+Spam:MaxMessagesPerWindow
+8
+Max messages per WindowSeconds before a send is rejected
+
+
+Spam:WindowSeconds
+5
+Sliding window (seconds) for the message-rate check
+
+
+Spam:MaxDuplicateMessages
+3
+Identical messages in a row tolerated before rejection (E2E rooms are exempt — ciphertext differs each time)
+
+
+Spam:AutoMuteMinutes
+5
+Auto-mute duration once a user hits the violation threshold. 0 disables auto-mute (rejections still apply)
+
+
+Spam:ViolationThreshold
+5
+Rejected sends within ViolationWindowMinutes that trigger an auto-mute
+
+
+Spam:ViolationWindowMinutes
+5
+Window (minutes) over which violations accumulate
+
+
+Spam:MaxJoinsPerWindow
+25
+Max first-time channel joins per JoinWindowSeconds — keep this above your public channel count
+
+
+Spam:JoinWindowSeconds
+30
+Sliding window (seconds) for the join-rate check
+
+
+Spam:MaxChannelCreatesPerWindow
+3
+Max channel creations per ChannelCreateWindowMinutes
+
+
+Spam:ChannelCreateWindowMinutes
+10
+Window (minutes) for the channel-create check
+
+
+
Encryption
@@ -333,6 +418,11 @@ that's needed — no separate Kestrel tuning.
+Storage:Path
+(empty)
+Directory for uploaded file blobs (attachments, avatars). Empty = uploads/ in the app directory; the Docker image sets it to /app/data/uploads on the persistent volume
+
+
Storage:CleanupIntervalHours
1
How often the cleanup job runs (hours)
@@ -396,6 +486,57 @@ that's needed — no separate Kestrel tuning.
+Server Logs Room
+When enabled, EchoHub auto-creates a read-only system channel and streams log events to it live,
+so operators can watch the server from inside the app. Log lines are never stored as messages —
+the rolling Serilog files remain the only persistence, and the room replays recent lines from those
+files when someone opens it.
+
+
+
+Key
+Default
+Description
+
+
+
+
+ServerLogs:Enabled
+true
+Master switch for the live log room
+
+
+ServerLogs:RoomName
+server-logs
+Name of the auto-created channel (reserved — users can't create a channel with this name)
+
+
+ServerLogs:MinRole
+Mod
+Minimum server role that can see and join the room (Member, Mod, Admin, Owner)
+
+
+ServerLogs:MinLevel
+Information
+Minimum log level streamed to the room (Verbose, Debug, Information, Warning, Error, Fatal) — affects only the room, not the file/console sinks
+
+
+ServerLogs:BacklogLines
+100
+Recent log lines replayed from file when someone opens the room
+
+
+ServerLogs:LogDirectory
+logs
+Directory holding the rolling log files (must match the Serilog file sink path below)
+
+
+ServerLogs:LogFilePattern
+echohub-server-*.log
+Filename glob for the rolling log files inside LogDirectory
+
+
+
Logging
EchoHub uses Serilog for structured logging — console output + daily rolling files with 14-day retention by default.
diff --git a/changelog/index.html b/changelog/index.html
index 0850e71..0d1fe79 100644
--- a/changelog/index.html
+++ b/changelog/index.html
@@ -90,6 +90,7 @@
Release history for EchoHub.
Releases
+v0.2.15 - Invite Codes, Data Export & Deletion, /me, /banner, Replies, Open Images In Browser & IRC Image Links
v0.2.14 - Clipboard Image & Multi-File Paste, E2E Room Unlock Fixes, Encrypted Key Cache & IRC Gateway Polish
v0.2.13 - Chat Visual Overhaul, Auto-Join All Channels & Persistent Read Positions
v0.2.12 - End-to-End Encrypted Channels, IRC Channel Keys, Image Save & Ctrl+W Crash Fix
diff --git a/changelog/toc.html b/changelog/toc.html
index cfaea68..c4c3be7 100644
--- a/changelog/toc.html
+++ b/changelog/toc.html
@@ -15,6 +15,9 @@
Overview
+
+ v0.2.15
+
v0.2.14
diff --git a/changelog/toc.json b/changelog/toc.json
index ce1fd04..c53ff6d 100644
--- a/changelog/toc.json
+++ b/changelog/toc.json
@@ -1,2 +1,2 @@
-{"items":[{"name":"Overview","href":"index.html","topicHref":"index.html"},{"name":"v0.2.14","href":"v0.2.14.html","topicHref":"v0.2.14.html"},{"name":"v0.2.13","href":"v0.2.13.html","topicHref":"v0.2.13.html"},{"name":"v0.2.12","href":"v0.2.12.html","topicHref":"v0.2.12.html"},{"name":"v0.2.11","href":"v0.2.11.html","topicHref":"v0.2.11.html"},{"name":"v0.2.10","href":"v0.2.10.html","topicHref":"v0.2.10.html"},{"name":"v0.2.9","href":"v0.2.9.html","topicHref":"v0.2.9.html"},{"name":"v0.2.8","href":"v0.2.8.html","topicHref":"v0.2.8.html"},{"name":"v0.2.7","href":"v0.2.7.html","topicHref":"v0.2.7.html"},{"name":"v0.2.6","href":"v0.2.6.html","topicHref":"v0.2.6.html"},{"name":"v0.2.5","href":"v0.2.5.html","topicHref":"v0.2.5.html"},{"name":"v0.2.4","href":"v0.2.4.html","topicHref":"v0.2.4.html"},{"name":"v0.2.3","href":"v0.2.3.html","topicHref":"v0.2.3.html"},{"name":"v0.2.2","href":"v0.2.2.html","topicHref":"v0.2.2.html"},{"name":"v0.2.1","href":"v0.2.1.html","topicHref":"v0.2.1.html"},{"name":"v0.2.0","href":"v0.2.0.html","topicHref":"v0.2.0.html"},{"name":"v0.1.1","href":"v0.1.1.html","topicHref":"v0.1.1.html"},{"name":"v0.1.0","href":"v0.1.0.html","topicHref":"v0.1.0.html"}]}
+{"items":[{"name":"Overview","href":"index.html","topicHref":"index.html"},{"name":"v0.2.15","href":"v0.2.15.html","topicHref":"v0.2.15.html"},{"name":"v0.2.14","href":"v0.2.14.html","topicHref":"v0.2.14.html"},{"name":"v0.2.13","href":"v0.2.13.html","topicHref":"v0.2.13.html"},{"name":"v0.2.12","href":"v0.2.12.html","topicHref":"v0.2.12.html"},{"name":"v0.2.11","href":"v0.2.11.html","topicHref":"v0.2.11.html"},{"name":"v0.2.10","href":"v0.2.10.html","topicHref":"v0.2.10.html"},{"name":"v0.2.9","href":"v0.2.9.html","topicHref":"v0.2.9.html"},{"name":"v0.2.8","href":"v0.2.8.html","topicHref":"v0.2.8.html"},{"name":"v0.2.7","href":"v0.2.7.html","topicHref":"v0.2.7.html"},{"name":"v0.2.6","href":"v0.2.6.html","topicHref":"v0.2.6.html"},{"name":"v0.2.5","href":"v0.2.5.html","topicHref":"v0.2.5.html"},{"name":"v0.2.4","href":"v0.2.4.html","topicHref":"v0.2.4.html"},{"name":"v0.2.3","href":"v0.2.3.html","topicHref":"v0.2.3.html"},{"name":"v0.2.2","href":"v0.2.2.html","topicHref":"v0.2.2.html"},{"name":"v0.2.1","href":"v0.2.1.html","topicHref":"v0.2.1.html"},{"name":"v0.2.0","href":"v0.2.0.html","topicHref":"v0.2.0.html"},{"name":"v0.1.1","href":"v0.1.1.html","topicHref":"v0.1.1.html"},{"name":"v0.1.0","href":"v0.1.0.html","topicHref":"v0.1.0.html"}]}
diff --git a/changelog/v0.2.15.html b/changelog/v0.2.15.html
new file mode 100644
index 0000000..711fcc4
--- /dev/null
+++ b/changelog/v0.2.15.html
@@ -0,0 +1,192 @@
+
+
+
+
+ v0.2.15 | EchoHub Documentation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+v0.2.15
+
+A big one. Server owners can gate registration behind invite codes, users can export or delete
+everything the server holds about them, and chat gets three classics — /me actions that
+interop natively with IRC, /banner ASCII text, and message replies with quote rendering and
+jump-to-original. Images become properly shareable: every image gets an [open] action that
+views it in your browser (or, in end-to-end encrypted rooms, decrypts and opens it locally).
+The IRC gateway stops painting ANSI art nobody asked for and posts plain image links any IRC
+client can open or auto-preview. And a long-standing dual-session annoyance is fixed: messages
+you send from the TUI now reach your own connected IRC client instantly.
+New Features
+
+Invite-gated registration — set Server:Registration to "invite" (or "closed") in
+appsettings.json and new accounts require a valid invite code; the default "open" keeps
+today's behavior. Admins mint codes with /invite [uses] [hours] (single-use, never-expiring
+by default), list them with /invite list, and revoke with /invite revoke <code>. Codes are
+unguessable (K7QM-3XPF style), stored only in your server's own database, and consumed
+atomically so a code's last use can't be taken twice. The IRC gateway respects the gate too:
+unknown nicknames connecting to an invite-only server get a clear error instead of a silent
+auto-registered account. The very first account (server owner bootstrap) is always allowed.
+Data export — /export downloads everything the server stores about you (profile, your
+messages, attachment metadata) as JSON into your download folder. Content from end-to-end
+encrypted rooms appears as ciphertext, because the server never had the plaintext — "you own
+the data", demonstrably.
+Account deletion — /deleteaccount permanently removes your account after a typed
+confirmation and password re-check: profile, sessions, and every file you uploaded are
+deleted. Your messages remain (deleting them would gut other people's conversations) but are
+re-attributed to a reserved deleted-user name. The last remaining Owner can't self-delete.
+/me actions — /me waves renders as * nick waves. On the wire it's a real CTCP
+ACTION, so irssi/WeeChat users see a native action line, and their /me renders properly in
+the TUI. Works in encrypted rooms (the marker encrypts with the text).
+/banner <text> — renders short text (up to 20 characters) as a 5-row block-letter
+banner. Rendered locally with a built-in font — no network, no dependencies — and sent as
+plain message content, so it works over IRC and in encrypted rooms.
+Message replies — right-click a message (or F6-select) and pick Reply : the input
+shows a "Replying to" strip (Esc cancels), and the sent message renders with a dim
+┌ nick: snippet quote line. Activating the quote line jumps to the original message.
+Being replied to lights up the channel like an @mention. IRC clients see the familiar
+> nick: snippet | text convention. In encrypted rooms the quoted snippet is decrypted
+client-side with the room key — the server still never reads room content.
+[open] images without saving them — every image attachment now shows [open] [↓ save original] beneath its preview. Open views the image in your default browser straight from the server; in end-to-end encrypted rooms (where a browser would only see ciphertext) the client downloads, decrypts with the room key, and opens the image in your OS viewer from a temp file instead. Both actions are individually clickable, Enter on the line opens, and the right-click menu carries both.
+Attachment links work in a browser — GET /api/files/{id} is now a capability URL: the unguessable GUID in the link is the access token (Discord-CDN style), so attachment links can be opened directly in a browser or shared to IRC without a login token. Images and audio are served inline so the browser displays them instead of forcing a download. Blobs from encrypted rooms remain ciphertext, so their links reveal nothing.
+IRC gets image links instead of terminal art — the gateway no longer floods IRC clients with truecolor-ANSI ASCII art for images. Each attachment is now a single line — [Image: photo.png] https://your-server/api/files/… — the convention every IRC client understands, and ones like TheLounge or IRCCloud auto-preview. Set the new Irc:PublicBaseUrl option (e.g. "https://chat.example.com") so those links come out absolute; unset, they fall back to relative paths as before.
+Spam protection — per-user message flood and duplicate-message limits, join and
+channel-creation throttles, and automatic escalation: enough rejected sends in a few minutes
+earns a timed auto-mute through the normal mute system (moderators see it, and it expires on
+its own). One guard covers every ingress — TUI and IRC clients hit the same limits. Mods and
+above are exempt, everything is configurable under the new Spam section
+(Spam:Enabled master switch, lenient defaults a fast typist won't trip), and the guard only
+ever sees stored content — encrypted-room messages stay ciphertext.
+
+Improvements
+
+Strict /status — /status garbage is now an error instead of silently becoming your
+status message (and resetting you to Online). Status messages moved to /status msg <text>,
+which preserves your current status; /status msg with no text clears it. The server also
+rejects out-of-range status values sent by misbehaving clients.
+Registration dialog — gained Invite Code support, and the Display Name field you could
+already type into is now actually sent with registration.
+
+Bug Fixes
+
+Messages sent from the TUI now reach your own IRC session. With the same account online via both the TUI and an IRC client, messages sent from the TUI never appeared in the IRC client until it reconnected (which replayed history). The gateway suppressed the sender's echo by nickname , which swallowed the message for every IRC connection on that account — it now excludes only the exact connection a message originated from, so all your other sessions (a second IRC client included) receive it immediately.
+Channel deleted event — when a channel is deleted, all connected clients are now immediately notified via the new ChannelDeleted SignalR event. The channel is removed from the channel list. Previously only the deleting client was able to delete the channel. Other clients would see the channel linger until the next manual refresh.
+Deleted accounts don't break history — channel history now left-joins users, so messages
+from deleted accounts render (as deleted-user) instead of silently disappearing.
+
+Notes for server operators
+
+New config key: Server:Registration — "open" (default), "invite", or "closed".
+New config section: Spam — flood/duplicate/join/create thresholds and auto-mute duration;
+see appsettings.example.json. On by default with lenient limits; Spam:Enabled: false
+turns it all off. Note Spam:MaxJoinsPerWindow counts only first-time channel joins —
+keep it above your public channel count so a new member's first connect isn't throttled.
+New REST endpoints: POST/GET/DELETE /api/invites (Admin+), GET /api/users/me/export,
+DELETE /api/users/me.
+One new database migration (AddInvitesAndReplies) applies automatically on startup.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/index.json b/index.json
index 01a868a..010cc65 100644
--- a/index.json
+++ b/index.json
@@ -12,7 +12,7 @@
"api/client/EchoHub.Client.Commands.CommandHandler.html": {
"href": "api/client/EchoHub.Client.Commands.CommandHandler.html",
"title": "Class CommandHandler | EchoHub Documentation",
- "summary": "Class CommandHandler Namespace EchoHub.Client.Commands Assembly EchoHub.Client.dll public class CommandHandler Inheritance object CommandHandler Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors CommandHandler() public CommandHandler() Methods HandleAsync(string) public Task HandleAsync(string input) Parameters input string Returns Task IsCommand(string) public bool IsCommand(string input) Parameters input string Returns bool Events OnAssignRole public event Func? OnAssignRole Event Type Func OnBanUser public event Func? OnBanUser Event Type Func OnChangeRoomPassword public event Func? OnChangeRoomPassword Event Type Func OnClearAttachments public event Func? OnClearAttachments Event Type Func OnHelp public event Func? OnHelp Event Type Func OnJoinChannel public event Func? OnJoinChannel Event Type Func OnKickUser public event Func? OnKickUser Event Type Func OnLeaveChannel public event Func? OnLeaveChannel Event Type Func OnListUsers public event Func? OnListUsers Event Type Func OnMuteUser public event Func? OnMuteUser Event Type Func OnNukeChannel public event Func? OnNukeChannel Event Type Func OnOpenProfile public event Func? OnOpenProfile Event Type Func OnOpenServers public event Func? OnOpenServers Event Type Func OnQuit public event Func? OnQuit Event Type Func OnRoomInfo public event Func? OnRoomInfo Event Type Func OnSendFile public event Func? OnSendFile Event Type Func OnSetAsciiSize public event Func? OnSetAsciiSize Event Type Func OnSetAvatar public event Func? OnSetAvatar Event Type Func OnSetColor public event Func? OnSetColor Event Type Func OnSetDownloadPath public event Func? OnSetDownloadPath Event Type Func OnSetNick public event Func? OnSetNick Event Type Func OnSetStatus public event Func? OnSetStatus Event Type Func OnSetTheme public event Func? OnSetTheme Event Type Func OnSetTopic public event Func? OnSetTopic Event Type Func OnTestSound public event Func? OnTestSound Event Type Func OnUnbanUser public event Func? OnUnbanUser Event Type Func OnUnmuteUser public event Func? OnUnmuteUser Event Type Func"
+ "summary": "Class CommandHandler Namespace EchoHub.Client.Commands Assembly EchoHub.Client.dll public class CommandHandler Inheritance object CommandHandler Inherited Members object.GetType() object.MemberwiseClone() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors CommandHandler() public CommandHandler() Methods HandleAsync(string) public Task HandleAsync(string input) Parameters input string Returns Task IsCommand(string) public bool IsCommand(string input) Parameters input string Returns bool Events OnAssignRole public event Func? OnAssignRole Event Type Func OnBanUser public event Func? OnBanUser Event Type Func OnChangeRoomPassword public event Func? OnChangeRoomPassword Event Type Func OnClearAttachments public event Func? OnClearAttachments Event Type Func OnCreateInvite public event Func? OnCreateInvite Event Type Func OnDeleteAccount public event Func? OnDeleteAccount Event Type Func OnExportData public event Func? OnExportData Event Type Func OnHelp public event Func? OnHelp Event Type Func OnJoinChannel public event Func? OnJoinChannel Event Type Func OnKickUser public event Func? OnKickUser Event Type Func OnLeaveChannel public event Func? OnLeaveChannel Event Type Func OnListInvites public event Func? OnListInvites Event Type Func OnListUsers public event Func? OnListUsers Event Type Func OnMuteUser public event Func? OnMuteUser Event Type Func OnNukeChannel public event Func? OnNukeChannel Event Type Func OnOpenProfile public event Func? OnOpenProfile Event Type Func OnOpenServers public event Func? OnOpenServers Event Type Func OnQuit public event Func? OnQuit Event Type Func OnRevokeInvite public event Func? OnRevokeInvite Event Type Func OnRoomInfo public event Func? OnRoomInfo Event Type Func OnSendAction public event Func? OnSendAction Event Type Func OnSendBanner public event Func? OnSendBanner Event Type Func OnSendFile public event Func? OnSendFile Event Type Func OnSetAsciiSize public event Func? OnSetAsciiSize Event Type Func OnSetAvatar public event Func? OnSetAvatar Event Type Func OnSetColor public event Func? OnSetColor Event Type Func OnSetDownloadPath public event Func? OnSetDownloadPath Event Type Func OnSetNick public event Func? OnSetNick Event Type Func OnSetStatus Status update. A null status means \"keep the current status\"; a null message means \"keep the current message\" and an empty message means \"clear it\". The orchestrator resolves both against the session state. public event Func? OnSetStatus Event Type Func OnSetTheme public event Func? OnSetTheme Event Type Func OnSetTopic public event Func? OnSetTopic Event Type Func OnTestSound public event Func? OnTestSound Event Type Func OnUnbanUser public event Func? OnUnbanUser Event Type Func OnUnmuteUser public event Func? OnUnmuteUser Event Type Func"
},
"api/client/EchoHub.Client.Commands.CommandResult.html": {
"href": "api/client/EchoHub.Client.Commands.CommandResult.html",
@@ -57,7 +57,7 @@
"api/client/EchoHub.Client.Services.ApiClient.html": {
"href": "api/client/EchoHub.Client.Services.ApiClient.html",
"title": "Class ApiClient | EchoHub Documentation",
- "summary": "Class ApiClient Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll public sealed class ApiClient : IDisposable Inheritance object ApiClient Implements IDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ApiClient(string) public ApiClient(string baseUrl) Parameters baseUrl string Properties BaseUrl public string BaseUrl { get; } Property Value string RefreshToken public string? RefreshToken { get; } Property Value string Token public string? Token { get; } Property Value string Methods AssignRoleAsync(string, ServerRole) public Task AssignRoleAsync(string username, ServerRole role) Parameters username string role ServerRole Returns Task BanUserAsync(string, string?) public Task BanUserAsync(string username, string? reason = null) Parameters username string reason string Returns Task CreateChannelAsync(string, string?, bool, string?, string?, string?) public Task CreateChannelAsync(string name, string? topic = null, bool isPublic = true, string? password = null, string? encryptionSalt = null, string? wrappedRoomKey = null) Parameters name string topic string isPublic bool password string encryptionSalt string wrappedRoomKey string Returns Task DeleteChannelAsync(string) public Task DeleteChannelAsync(string channelName) Parameters channelName string Returns Task DeleteMessageAsync(Guid) public Task DeleteMessageAsync(Guid messageId) Parameters messageId Guid Returns Task Dispose() public void Dispose() DownloadFileToTempAsync(string, string) public Task DownloadFileToTempAsync(string relativeUrl, string fileName) Parameters relativeUrl string fileName string Returns Task GetChannelCryptoAsync(string) Fetches a channel's public crypto metadata (whether it's E2E-encrypted and its key-derivation salt). Returns null when the channel doesn't exist. public Task GetChannelCryptoAsync(string channelName) Parameters channelName string Returns Task GetChannelMetaAsync(string) Fetches a channel's human-facing metadata (message count, unique posters, estimated size, created date, room id) for the /meta command. Returns null if it doesn't exist. public Task GetChannelMetaAsync(string channelName) Parameters channelName string Returns Task GetChannelsAsync() public Task> GetChannelsAsync() Returns Task> GetEncryptionKeyAsync() public Task GetEncryptionKeyAsync() Returns Task GetServerInfoAsync() public Task GetServerInfoAsync() Returns Task GetUserProfileAsync(string) public Task GetUserProfileAsync(string username) Parameters username string Returns Task GetValidTokenAsync() Returns a valid access token, refreshing if expired. Used by EchoHubConnection for SignalR token provider. public Task GetValidTokenAsync() Returns Task KickUserAsync(string, string?) public Task KickUserAsync(string username, string? reason = null) Parameters username string reason string Returns Task LoginAsync(string, string) public Task LoginAsync(string username, string password) Parameters username string password string Returns Task LoginWithRefreshTokenAsync(string) public Task LoginWithRefreshTokenAsync(string refreshToken) Parameters refreshToken string Returns Task LogoutAsync() public Task LogoutAsync() Returns Task MuteUserAsync(string, int?, string?) public Task MuteUserAsync(string username, int? durationMinutes = null, string? reason = null) Parameters username string durationMinutes int? reason string Returns Task NukeChannelAsync(string) public Task NukeChannelAsync(string channelName) Parameters channelName string Returns Task RefreshTokenAsync() public Task RefreshTokenAsync() Returns Task RegisterAsync(string, string, string?) public Task RegisterAsync(string username, string password, string? displayName = null) Parameters username string password string displayName string Returns Task RekeyChannelAsync(string, RekeyChannelRequest) public Task RekeyChannelAsync(string channelName, RekeyChannelRequest request) Parameters channelName string request RekeyChannelRequest Returns Task SendMessageWithAttachmentsAsync(string, string, IReadOnlyList, string?) Sends one message with optional text and one or more file attachments. For end-to-end encrypted channels each attachment carries a declared kind and a room-encrypted preview (empty when none); the caption is likewise room-encrypted. public Task SendMessageWithAttachmentsAsync(string channelName, string content, IReadOnlyList attachments, string? size = null) Parameters channelName string content string attachments IReadOnlyList size string Returns Task SendUrlAsync(string, string, string?) public Task SendUrlAsync(string channelName, string url, string? size = null) Parameters channelName string url string size string Returns Task UnbanUserAsync(string) public Task UnbanUserAsync(string username) Parameters username string Returns Task UnmuteUserAsync(string) public Task UnmuteUserAsync(string username) Parameters username string Returns Task UpdateChannelTopicAsync(string, string?) public Task UpdateChannelTopicAsync(string channelName, string? topic) Parameters channelName string topic string Returns Task UpdateProfileAsync(UpdateProfileRequest) public Task UpdateProfileAsync(UpdateProfileRequest request) Parameters request UpdateProfileRequest Returns Task UploadAvatarAsync(Stream, string) public Task UploadAvatarAsync(Stream imageStream, string fileName) Parameters imageStream Stream fileName string Returns Task Events OnTokensRefreshed public event Action? OnTokensRefreshed Event Type Action"
+ "summary": "Class ApiClient Namespace EchoHub.Client.Services Assembly EchoHub.Client.dll public sealed class ApiClient : IDisposable Inheritance object ApiClient Implements IDisposable Inherited Members object.GetType() object.ToString() object.Equals(object) object.Equals(object, object) object.ReferenceEquals(object, object) object.GetHashCode() Constructors ApiClient(string) public ApiClient(string baseUrl) Parameters baseUrl string Properties BaseUrl public string BaseUrl { get; } Property Value string RefreshToken public string? RefreshToken { get; } Property Value string Token public string? Token { get; } Property Value string Methods AssignRoleAsync(string, ServerRole) public Task AssignRoleAsync(string username, ServerRole role) Parameters username string role ServerRole Returns Task BanUserAsync(string, string?) public Task BanUserAsync(string username, string? reason = null) Parameters username string reason string Returns Task CreateChannelAsync(string, string?, bool, string?, string?, string?) public Task CreateChannelAsync(string name, string? topic = null, bool isPublic = true, string? password = null, string? encryptionSalt = null, string? wrappedRoomKey = null) Parameters name string topic string isPublic bool password string encryptionSalt string wrappedRoomKey string Returns Task CreateInviteAsync(int?, int?) public Task CreateInviteAsync(int? maxUses = null, int? expiresInHours = null) Parameters maxUses int? expiresInHours int? Returns Task DeleteChannelAsync(string) public Task DeleteChannelAsync(string channelName) Parameters channelName string Returns Task DeleteMessageAsync(Guid) public Task DeleteMessageAsync(Guid messageId) Parameters messageId Guid Returns Task DeleteMyAccountAsync(string) Deletes the caller's account. The password re-confirms intent. public Task DeleteMyAccountAsync(string password) Parameters password string Returns Task Dispose() public void Dispose() DownloadFileToTempAsync(string, string) public Task DownloadFileToTempAsync(string relativeUrl, string fileName) Parameters relativeUrl string fileName string Returns Task ExportMyDataAsync() Downloads the caller's full data export as raw JSON text. public Task ExportMyDataAsync() Returns Task GetChannelCryptoAsync(string) Fetches a channel's public crypto metadata (whether it's E2E-encrypted and its key-derivation salt). Returns null when the channel doesn't exist. public Task GetChannelCryptoAsync(string channelName) Parameters channelName string Returns Task GetChannelMetaAsync(string) Fetches a channel's human-facing metadata (message count, unique posters, estimated size, created date, room id) for the /meta command. Returns null if it doesn't exist. public Task GetChannelMetaAsync(string channelName) Parameters channelName string Returns Task GetChannelsAsync() public Task> GetChannelsAsync() Returns Task> GetEncryptionKeyAsync() public Task GetEncryptionKeyAsync() Returns Task GetInvitesAsync() public Task> GetInvitesAsync() Returns Task> GetServerInfoAsync() public Task GetServerInfoAsync() Returns Task GetUserProfileAsync(string) public Task GetUserProfileAsync(string username) Parameters username string Returns Task GetValidTokenAsync() Returns a valid access token, refreshing if expired. Used by EchoHubConnection for SignalR token provider. public Task GetValidTokenAsync() Returns Task KickUserAsync(string, string?) public Task KickUserAsync(string username, string? reason = null) Parameters username string reason string Returns Task LoginAsync(string, string) public Task LoginAsync(string username, string password) Parameters username string password string Returns Task LoginWithRefreshTokenAsync(string) public Task LoginWithRefreshTokenAsync(string refreshToken) Parameters refreshToken string Returns Task LogoutAsync() public Task LogoutAsync() Returns Task MuteUserAsync(string, int?, string?) public Task MuteUserAsync(string username, int? durationMinutes = null, string? reason = null) Parameters username string durationMinutes int? reason string Returns Task NukeChannelAsync(string) public Task NukeChannelAsync(string channelName) Parameters channelName string Returns Task RefreshTokenAsync() public Task RefreshTokenAsync() Returns Task RegisterAsync(string, string, string?, string?) public Task RegisterAsync(string username, string password, string? displayName = null, string? inviteCode = null) Parameters username string password string displayName string inviteCode string Returns Task RekeyChannelAsync(string, RekeyChannelRequest) public Task RekeyChannelAsync(string channelName, RekeyChannelRequest request) Parameters channelName string request RekeyChannelRequest Returns Task RevokeInviteAsync(string) public Task RevokeInviteAsync(string code) Parameters code string Returns Task SendMessageWithAttachmentsAsync(string, string, IReadOnlyList, string?) Sends one message with optional text and one or more file attachments. For end-to-end encrypted channels each attachment carries a declared kind and a room-encrypted preview (empty when none); the caption is likewise room-encrypted. public Task SendMessageWithAttachmentsAsync(string channelName, string content, IReadOnlyList attachments, string? size = null) Parameters channelName string content string attachments IReadOnlyList size string Returns Task SendUrlAsync(string, string, string?) public Task SendUrlAsync(string channelName, string url, string? size = null) Parameters channelName string url string size string Returns Task UnbanUserAsync(string) public Task UnbanUserAsync(string username) Parameters username string Returns Task UnmuteUserAsync(string) public Task UnmuteUserAsync(string username) Parameters username string Returns Task UpdateChannelTopicAsync(string, string?) public Task UpdateChannelTopicAsync(string channelName, string? topic) Parameters channelName string topic string Returns Task UpdateProfileAsync(UpdateProfileRequest) public Task UpdateProfileAsync(UpdateProfileRequest request) Parameters request UpdateProfileRequest Returns Task UploadAvatarAsync(Stream, string) public Task