diff --git a/Config/SettingsProvider.cs b/Config/SettingsProvider.cs index 8dc7afc..0eb629d 100644 --- a/Config/SettingsProvider.cs +++ b/Config/SettingsProvider.cs @@ -25,7 +25,7 @@ public static class SettingsProvider ConfigManager.Instance.Values = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(model)); ConfigManager.Instance.Save(); - Target.All.SendPackageTo(new Package(PackageType.MetaResponse, new ServerMetaResponsePackageContent { + Target.All.SendPackage(new Package(PackageType.MetaResponse, new ServerMetaResponsePackageContent { GuestsAllowed = ConfigManager.Instance.Values.GuestsAllowed, Name = ConfigManager.Instance.Values.ServerName, RegistrationAllowed = ConfigManager.Instance.Values.RegistrationAllowed diff --git a/Extensibility/Events/ConnectEventArgs.cs b/Extensibility/Events/ConnectEventArgs.cs index 88cc36a..334674f 100644 --- a/Extensibility/Events/ConnectEventArgs.cs +++ b/Extensibility/Events/ConnectEventArgs.cs @@ -3,12 +3,20 @@ namespace Neo.Core.Extensibility.Events { /// - /// Provides data for the event. + /// Provides the data for the event. /// public class ConnectEventArgs { + /// + /// The connecting. + /// public Client Client { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The connecting. public ConnectEventArgs(Client client) { this.Client = client; } diff --git a/Extensibility/Events/CreateElementEventArgs.cs b/Extensibility/Events/CreateElementEventArgs.cs index 09ada5f..d180edf 100644 --- a/Extensibility/Events/CreateElementEventArgs.cs +++ b/Extensibility/Events/CreateElementEventArgs.cs @@ -3,14 +3,27 @@ namespace Neo.Core.Extensibility.Events { /// - /// Provides data for all create events. + /// Provides the data for all create events. /// /// A creatable element. public class CreateElementEventArgs : ICancellableEvent { + /// + /// The creating the . + /// public User Creator { get; } + + /// + /// The element created by the . + /// public TElement Element { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The creating the . + /// The element created by the . public CreateElementEventArgs(User creator, TElement element) { this.Creator = creator; this.Element = element; diff --git a/Extensibility/Events/CustomEventArgs.cs b/Extensibility/Events/CustomEventArgs.cs index 5b88801..baecd0b 100644 --- a/Extensibility/Events/CustomEventArgs.cs +++ b/Extensibility/Events/CustomEventArgs.cs @@ -1,11 +1,29 @@ namespace Neo.Core.Extensibility.Events { /// - /// Provides data for the event. + /// Provides the data for the event. /// public class CustomEventArgs { - // TODO + /// + /// The name of this . + /// public string Name { get; } + + /// + /// The content of this . + /// + public object[] Content { get; } + + + /// + /// Initializes a new instance of the class. + /// + /// The name of this . + /// The content of this . + public CustomEventArgs(string name, params object[] content) { + this.Name = name; + this.Content = content; + } } } diff --git a/Extensibility/Events/DisconnectEventArgs.cs b/Extensibility/Events/DisconnectEventArgs.cs index 0f964fd..1420639 100644 --- a/Extensibility/Events/DisconnectEventArgs.cs +++ b/Extensibility/Events/DisconnectEventArgs.cs @@ -3,15 +3,38 @@ namespace Neo.Core.Extensibility.Events { /// - /// Provides data for the event. + /// Provides the data for the event. /// public class DisconnectEventArgs { + /// + /// The disconnecting. + /// public Client Client { get; } + + /// + /// The code of the disconnect provided by the socket. + /// public ushort Code { get; } + + /// + /// The reason of the disconnect provided by the socket. + /// public string Reason { get; } + + /// + /// Determines whether the disconnect was clean. + /// public bool WasClean { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The disconnecting. + /// The code of the disconnect provided by the socket. + /// The reason of the disconnect provided by the socket. + /// Whether the disconnect was clean. public DisconnectEventArgs(Client client, ushort code, string reason, bool wasClean) { this.Client = client; this.Code = code; diff --git a/Extensibility/Events/EditElementEventArgs.cs b/Extensibility/Events/EditElementEventArgs.cs index e72a11f..c9ca2cc 100644 --- a/Extensibility/Events/EditElementEventArgs.cs +++ b/Extensibility/Events/EditElementEventArgs.cs @@ -3,14 +3,27 @@ namespace Neo.Core.Extensibility.Events { /// - /// Provides data for all edit events. + /// Provides the data for all edit events. /// /// An editable element. public class EditElementEventArgs : ICancellableEvent { + /// + /// The editing the . + /// public User Editor { get; } + + /// + /// The element the edited. + /// public TElement Element { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The editing the . + /// The element the edited. public EditElementEventArgs(User editor, TElement element) { this.Editor = editor; this.Element = element; diff --git a/Extensibility/Events/EventType.cs b/Extensibility/Events/EventType.cs index 41b6b61..d582476 100644 --- a/Extensibility/Events/EventType.cs +++ b/Extensibility/Events/EventType.cs @@ -1,4 +1,5 @@ -using Neo.Core.Communication; +using System; +using Neo.Core.Communication; using Neo.Core.Networking; using Neo.Core.Shared; @@ -157,6 +158,7 @@ public enum EventType /// /// This event is raised when a is typing. /// + [Obsolete] Typing, /// diff --git a/Extensibility/Events/InputEventArgs.cs b/Extensibility/Events/InputEventArgs.cs index 888d60b..b923482 100644 --- a/Extensibility/Events/InputEventArgs.cs +++ b/Extensibility/Events/InputEventArgs.cs @@ -2,11 +2,27 @@ namespace Neo.Core.Extensibility.Events { + /// + /// Provides the data for the event. + /// public class InputEventArgs : ICancellableEvent { + /// + /// The sender of the input. + /// public User Sender { get; } + + /// + /// The input sent. + /// public string Input { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The sender of the input. + /// The input sent. public InputEventArgs(User sender, string input) { this.Sender = sender; this.Input = input; diff --git a/Extensibility/Events/JoinElementEventArgs.cs b/Extensibility/Events/JoinElementEventArgs.cs index 28d860c..4f91cec 100644 --- a/Extensibility/Events/JoinElementEventArgs.cs +++ b/Extensibility/Events/JoinElementEventArgs.cs @@ -3,14 +3,27 @@ namespace Neo.Core.Extensibility.Events { /// - /// Provides data for all join events. + /// Provides the data for all join events. /// /// A joinable element. public class JoinElementEventArgs : ICancellableEvent { + /// + /// The joining the . + /// public User Joiner { get; } + + /// + /// The element the joined. + /// public TElement Element { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The joining the . + /// The element the joined. public JoinElementEventArgs(User joiner, TElement element) { this.Joiner = joiner; this.Element = element; diff --git a/Extensibility/Events/LeaveElementEventArgs.cs b/Extensibility/Events/LeaveElementEventArgs.cs index 6fc43f5..ce3738b 100644 --- a/Extensibility/Events/LeaveElementEventArgs.cs +++ b/Extensibility/Events/LeaveElementEventArgs.cs @@ -3,14 +3,27 @@ namespace Neo.Core.Extensibility.Events { /// - /// Provides data for all leave events. + /// Provides the data for all leave events. /// /// A leavable element. public class LeaveElementEventArgs : ICancellableEvent { + /// + /// The leaving the . + /// public User Leaver { get; } + + /// + /// The element the left. + /// public TElement Element { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The leaving the . + /// The element the left. public LeaveElementEventArgs(User leaver, TElement element) { this.Leaver = leaver; this.Element = element; diff --git a/Extensibility/Events/ReceiveElementEventArgs.cs b/Extensibility/Events/ReceiveElementEventArgs.cs index fcbea57..f8e9c39 100644 --- a/Extensibility/Events/ReceiveElementEventArgs.cs +++ b/Extensibility/Events/ReceiveElementEventArgs.cs @@ -3,15 +3,27 @@ namespace Neo.Core.Extensibility.Events { /// - /// Provides data for all receive events. + /// Provides the data for all receive events. /// /// A receivable element. public class ReceiveElementEventArgs : ICancellableEvent { - // TODO + /// + /// The sender of the . + /// public Client Sender { get; } + + /// + /// The element received. + /// public TElement Element { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The sender of the . + /// The received element. public ReceiveElementEventArgs(Client sender, TElement element) { this.Sender = sender; this.Element = element; diff --git a/Extensibility/Events/RemoveElementEventArgs.cs b/Extensibility/Events/RemoveElementEventArgs.cs index d93e4f7..e5519d7 100644 --- a/Extensibility/Events/RemoveElementEventArgs.cs +++ b/Extensibility/Events/RemoveElementEventArgs.cs @@ -3,14 +3,27 @@ namespace Neo.Core.Extensibility.Events { /// - /// Provides data for all remove events. + /// Provides the data for all remove events. /// /// A removable element. public class RemoveElementEventArgs : ICancellableEvent { + /// + /// The removing the . + /// public User Remover { get; } + + /// + /// The element to remove. + /// public TElement Element { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The removing the . + /// The element to remove. public RemoveElementEventArgs(User remover, TElement element) { this.Remover = remover; this.Element = element; diff --git a/Extensibility/Events/TypingEventArgs.cs b/Extensibility/Events/TypingEventArgs.cs index 9e1c2c2..0f9efe3 100644 --- a/Extensibility/Events/TypingEventArgs.cs +++ b/Extensibility/Events/TypingEventArgs.cs @@ -4,15 +4,33 @@ namespace Neo.Core.Extensibility.Events { /// - /// Provides data for the event. + /// Provides the data for the event. /// [Obsolete] public class TypingEventArgs { + /// + /// The typing. + /// public User User { get; } + + /// + /// The in which this was invoked. + /// public Channel Channel { get; } + + /// + /// The input of the so far. + /// public string CurrentInput { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The typing. + /// The in which this was invoked. + /// The input of the so far. public TypingEventArgs(User user, Channel channel, string currentInput) { this.User = user; this.Channel = channel; diff --git a/Extensibility/Listener.cs b/Extensibility/Listener.cs index 5795524..3aac4b7 100644 --- a/Extensibility/Listener.cs +++ b/Extensibility/Listener.cs @@ -2,12 +2,26 @@ namespace Neo.Core.Extensibility { - // TODO: Add docs + /// + /// Represents an event listener of a . + /// public class Listener { + /// + /// The handler to execute. + /// public MethodInfo Method { get; } + + /// + /// The this belongs to. + /// public Plugin Plugin { get; } + /// + /// Initializes a new instance of the class. + /// + /// The handler to execute. + /// The this belongs to. public Listener(MethodInfo method, Plugin plugin) { this.Method = method; this.Plugin = plugin; diff --git a/Extensibility/Plugin.cs b/Extensibility/Plugin.cs index a519ab2..b0b1731 100644 --- a/Extensibility/Plugin.cs +++ b/Extensibility/Plugin.cs @@ -10,12 +10,22 @@ namespace Neo.Core.Extensibility { - // TODO: Add docs + /// + /// Represents the base class for an extensible component. + /// public abstract class Plugin { + /// + /// The unique id used to identify this . + /// public Guid InternalId { get; } = Guid.NewGuid(); + + /// + /// The namespace of this . + /// public abstract string Namespace { get; } + public virtual async Task OnInitialize() { } public virtual async Task OnCustom(CustomEventArgs args) { } @@ -61,6 +71,7 @@ public abstract class Plugin public virtual async Task OnBeforeGroupRemove(Before> args) { } public virtual async Task OnGroupRemoved(RemoveElementEventArgs args) { } + [Obsolete] public virtual async Task OnTyping(TypingEventArgs args) { } public virtual async Task OnBeforeInput(Before args) { } diff --git a/Extensibility/PluginLoader.cs b/Extensibility/PluginLoader.cs index 1ef1350..fe86f52 100644 --- a/Extensibility/PluginLoader.cs +++ b/Extensibility/PluginLoader.cs @@ -3,16 +3,24 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Threading.Tasks; namespace Neo.Core.Extensibility { - // TODO: Add docs + /// + /// Provides methods to load s. + /// public static class PluginLoader { + /// + /// Contains all loaded s. + /// public static List Plugins { get; } = new List(); - public static async Task InitializePlugin(string path) { + /// + /// Loads and initializes a . + /// + /// The path to the . + public static void InitializePlugin(string path) { if (File.Exists(path) && new FileInfo(path).Extension == ".dll") { Assembly.LoadFile(path); var assemblyTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).Where(t => typeof(Plugin).IsAssignableFrom(t) && t.IsClass).ToArray(); @@ -24,14 +32,14 @@ public static class PluginLoader var plugin = (Plugin) Activator.CreateInstance(type); - if (Plugins.Any(p => p.Namespace == plugin.Namespace)) { + if (Plugins.Any(_ => _.Namespace == plugin.Namespace)) { plugin = null; continue; } EventService.RegisterListeners(type, plugin); - await plugin.OnInitialize(); + plugin.OnInitialize(); Plugins.Add(plugin); } diff --git a/Management/ChannelManager.cs b/Management/ChannelManager.cs index bcafcf1..2a1555e 100644 --- a/Management/ChannelManager.cs +++ b/Management/ChannelManager.cs @@ -5,7 +5,6 @@ using Neo.Core.Communication; using Neo.Core.Communication.Packages; using Neo.Core.Config; -using Neo.Core.Database; using Neo.Core.Extensibility; using Neo.Core.Networking; using Neo.Core.Shared; @@ -14,13 +13,21 @@ namespace Neo.Core.Management { + /// + /// Provides methods to manage s. + /// public static class ChannelManager { + /// + /// Adds a to a and informs all other channel members. + /// + /// The to add. + /// The to add the to. private static void AddUserToChannel(User user, Channel channel) { channel.MemberIds.Add(user.InternalId); var message = MessagePackageContent.GetSystemMessage(user.Identity.Name + " ist dem Channel beigetreten.", channel.InternalId); - Pool.Server.SendPackageTo(new Target().AddMany(channel), new Package(PackageType.Message, message)); + new Target().AddMany(channel).SendPackage(new Package(PackageType.Message, message)); if (ConfigManager.Instance.Values.SaveSystemMessages) { channel.SaveMessage(message); @@ -29,6 +36,7 @@ public static class ChannelManager RefreshChannels(); } + [Obsolete] public static void CloseChannel(User user) { if (user.ActiveChannel != null) { var channel = user.ActiveChannel; @@ -37,20 +45,36 @@ public static class ChannelManager } } + /// + /// Creates a new for a . + /// + /// The that creates the . + /// The that owns the . + /// The to create. + /// Returns false if a channel with the given id already exists. Otherwise true. public static bool CreateChannel(this Plugin plugin, Member member, Channel channel) { - if (Pool.Server.Channels.Any(c => c.Id == channel.Id)) { + if (Pool.Server.Channels.Any(_ => _.Id == channel.Id)) { return false; } channel.Attributes.Add("neo.origin", plugin.InternalId); channel.Owner = member.InternalId; + + // TODO: Maybe add the channel to the server list? + AddUserToChannel(member, channel); return true; } + /// + /// Creates a new for a . + /// + /// The who creates the . + /// The to create. + /// Returns false if a channel with the given id already exists or the user is not authorized to create channels. Otherwise true. public static bool CreateChannel(this User user, Channel channel) { - if (Pool.Server.Channels.Any(c => c.Id == channel.Id)) { + if (Pool.Server.Channels.Any(_ => _.Id == channel.Id)) { return false; } @@ -70,8 +94,13 @@ public static class ChannelManager return true; } + /// + /// Deletes a for a . + /// + /// The to delete. + /// The who deletes the . + /// Returns false if the user is not authorized to delete channels. Otherwise true. public static bool DeleteChannel(this Channel channel, User user) { - if (!user.IsAuthorized("neo.channel.delete")) { return false; } @@ -82,25 +111,30 @@ public static class ChannelManager return true; } + /// + /// Returns the default . + /// + /// Returns the default . public static Channel GetMainChannel() { - return Pool.Server.Channels.Find(c => c.Attributes.ContainsKey("neo.channeltype") && c.Attributes["neo.channeltype"].ToString() == "main"); + return Pool.Server.Channels.Find(_ => _.Attributes.ContainsKey("neo.channeltype") && _.Attributes["neo.channeltype"].ToString() == "main"); } + // TODO: Add docs public static ChannelActionResult JoinChannel(this User user, Channel channel, string password = "") { if (!user.IsAuthorized("neo.channel.join.$")) { return ChannelActionResult.NotAllowed; } - if (channel.BlacklistedGroupIds.Count > 0 || channel.BlacklistedUserIds.Count > 0) { - if (user is Member member && channel.BlacklistedGroupIds.Any(member.Groups.Select(g => g.InternalId).Contains) || channel.BlacklistedUserIds.Contains(user.InternalId)) { - if (user.IsAuthorized("neo.channel.join.ignoreblacklist")) { - AddUserToChannel(user, channel); - return ChannelActionResult.Success; - } + //if (channel.BlacklistedGroupIds.Count > 0 || channel.BlacklistedUserIds.Count > 0) { + // if (user is Member member && channel.BlacklistedGroupIds.Any(member.Groups.Select(g => g.InternalId).Contains) || channel.BlacklistedUserIds.Contains(user.InternalId)) { + // if (user.IsAuthorized("neo.channel.join.ignoreblacklist")) { + // AddUserToChannel(user, channel); + // return ChannelActionResult.Success; + // } - return ChannelActionResult.Blacklisted; - } - } + // return ChannelActionResult.Blacklisted; + // } + //} if (!string.IsNullOrEmpty(channel.Password) && channel.Password != password) { if (user.IsAuthorized("neo.channel.join.ignorepassword")) { @@ -120,27 +154,33 @@ public static class ChannelManager return ChannelActionResult.Full; } - if (channel.WhitelistedGroupIds.Count > 0 || channel.WhitelistedUserIds.Count > 0) { - if (user is Member member && !channel.WhitelistedGroupIds.Any(member.Groups.Select(g => g.InternalId).Contains) || !channel.WhitelistedUserIds.Contains(user.InternalId)) { - if (user.IsAuthorized("neo.channel.join.ignorewhitelist")) { - AddUserToChannel(user, channel); - return ChannelActionResult.Success; - } + //if (channel.WhitelistedGroupIds.Count > 0 || channel.WhitelistedUserIds.Count > 0) { + // if (user is Member member && !channel.WhitelistedGroupIds.Any(member.Groups.Select(g => g.InternalId).Contains) || !channel.WhitelistedUserIds.Contains(user.InternalId)) { + // if (user.IsAuthorized("neo.channel.join.ignorewhitelist")) { + // AddUserToChannel(user, channel); + // return ChannelActionResult.Success; + // } - return ChannelActionResult.NotWhitelisted; - } - } + // return ChannelActionResult.NotWhitelisted; + // } + //} AddUserToChannel(user, channel); return ChannelActionResult.Success; } + // TODO: Add docs public static void LeaveChannel(this User user, Channel channel) { channel.ActiveMemberIds.Remove(user.InternalId); channel.MemberIds.Remove(user.InternalId); RefreshChannels(); } + /// + /// Moves a to a , effectively setting the channel to the users active channel. + /// + /// The to move. + /// The to move the to. public static void MoveToChannel(this User user, Channel channel) { if (!channel.ActiveMemberIds.Contains(user.InternalId)) { var currentActiveChannel = user.ActiveChannel; @@ -152,11 +192,17 @@ public static class ChannelManager channel.ActiveMemberIds.Add(user.InternalId); RefreshChannels(); - - user.ToTarget().SendPackageTo(new Package(PackageType.EnterChannelResponse, new EnterChannelResponsePackageContent(ChannelActionResult.Success, channel))); + user.ToTarget().SendPackage(new Package(PackageType.EnterChannelResponse, new EnterChannelResponsePackageContent(ChannelActionResult.Success, channel))); } } + /// + /// Opens a for a . Performs a join if the user is not member of the channel already and then moves the user into the channel. + /// + /// The to open the for. + /// The to open for the . + /// The password entered by the client. + /// public static ChannelActionResult OpenChannel(this User user, Channel channel, string password = "") { if (!channel.MemberIds.Contains(user.InternalId)) { var result = JoinChannel(user, channel, password); @@ -169,13 +215,20 @@ public static class ChannelManager return ChannelActionResult.Success; } + /// + /// Updates the list of s for each connected . + /// public static void RefreshChannels() { var channels = JsonConvert.DeserializeObject>(JsonConvert.SerializeObject(Pool.Server.Channels)); - channels.ForEach(c => c.Password = !string.IsNullOrEmpty(c.Password) ? "true" : null); + channels.ForEach(_ => _.Password = !string.IsNullOrEmpty(_.Password) ? "true" : null); - Pool.Server.SendPackageTo(Target.All, new Package(PackageType.ChannelListUpdate, channels)); + Target.All.SendPackage(new Package(PackageType.ChannelListUpdate, channels)); } + /// + /// Removes a and moves all remaining members to the default channel. + /// + /// The to remove. public static void RemoveChannel(Channel channel) { foreach (var activeMember in channel.ActiveMembers) { MoveToChannel(activeMember, GetMainChannel()); @@ -186,6 +239,7 @@ public static class ChannelManager } } + // TODO: Add docs [JsonConverter(typeof(StringEnumConverter))] public enum ChannelActionResult { diff --git a/Management/GroupManager.cs b/Management/GroupManager.cs index fd2f553..7765797 100644 --- a/Management/GroupManager.cs +++ b/Management/GroupManager.cs @@ -1,5 +1,4 @@ -using System; -using System.Linq; +using System.Linq; using Neo.Core.Authorization; using Neo.Core.Communication; using Neo.Core.Networking; @@ -9,25 +8,40 @@ namespace Neo.Core.Management { + /// + /// Provides methods to manage s. + /// public static class GroupManager { + /// + /// Adds a to the guest . + /// + /// The to add. public static void AddGuestToGroup(Guest guest) { GetGuestGroup().MemberIds.Add(guest.InternalId); + RefreshGroups(); } + /// + /// Adds a to a . + /// + /// The to add. + /// The to add the to. public static void AddMemberToGroup(Member member, Group group) { group.MemberIds.Add(member.InternalId); Pool.Server.DataProvider.Save(); + RefreshGroups(); } + // TODO: Add docs public static GroupActionResult CreateGroup(Group group, User creator) { if (!creator.IsAuthorized("neo.group.create")) { return GroupActionResult.NotAllowed; } - if (Pool.Server.Groups.Any(g => g.Id == group.Id)) { + if (Pool.Server.Groups.Any(_ => _.Id == group.Id)) { return GroupActionResult.IdInUse; } @@ -37,18 +51,19 @@ public static class GroupManager return GroupActionResult.Success; } + // TODO: Add docs public static GroupActionResult DeleteGroup(Group group, User deletor) { if (!deletor.IsAuthorized("neo.group.delete")) { return GroupActionResult.NotAllowed; } - var members = group.Members.Select(a => a.InternalId); + var members = group.Members.Select(_ => _.InternalId); Pool.Server.Groups.Remove(group); - members.Select(m => Pool.Server.Accounts.Find(a => a.InternalId == m)).ToList().ForEach(a => { - if (a.Groups.Count == 0) { - GetUserGroup().MemberIds.Add(a.InternalId); + members.Select(member => Pool.Server.Accounts.Find(account => account.InternalId == member)).ToList().ForEach(account => { + if (account.Groups.Count == 0) { + GetUserGroup().MemberIds.Add(account.InternalId); } }); @@ -57,38 +72,63 @@ public static class GroupManager return GroupActionResult.Success; } + /// + /// Returns the admin . + /// + /// Returns the admin . public static Group GetAdminGroup() { - return Pool.Server.Groups.Find(g => g.Attributes.ContainsKey("neo.grouptype") && g.Attributes["neo.grouptype"].ToString() == "admin"); + return Pool.Server.Groups.Find(_ => _.Attributes.ContainsKey("neo.grouptype") && _.Attributes["neo.grouptype"].ToString() == "admin"); } + /// + /// Returns the guest . + /// + /// Returns the guest . public static Group GetGuestGroup() { - return Pool.Server.Groups.Find(g => g.Attributes.ContainsKey("neo.grouptype") && g.Attributes["neo.grouptype"].ToString() == "guest"); + return Pool.Server.Groups.Find(_ => _.Attributes.ContainsKey("neo.grouptype") && _.Attributes["neo.grouptype"].ToString() == "guest"); } + /// + /// Returns the default . + /// + /// Returns the default . public static Group GetUserGroup() { - return Pool.Server.Groups.Find(g => g.Attributes.ContainsKey("neo.grouptype") && g.Attributes["neo.grouptype"].ToString() == "user"); + return Pool.Server.Groups.Find(_ => _.Attributes.ContainsKey("neo.grouptype") && _.Attributes["neo.grouptype"].ToString() == "user"); } + /// + /// Updates the list of s for each connected . + /// public static void RefreshGroups() { - Target.All.SendPackageTo(new Package(PackageType.GroupListUpdate, Pool.Server.Groups)); + Target.All.SendPackage(new Package(PackageType.GroupListUpdate, Pool.Server.Groups)); - Pool.Server.Users.ForEach(u => { - u.ToTarget().SendPackageTo(new Package(PackageType.GrantedPermissionsUpdate, u.GetAllPermissons())); - }); + Pool.Server.Users.ForEach(_ => _.ToTarget().SendPackage(new Package(PackageType.GrantedPermissionsUpdate, _.GetAllPermissons()))); } + /// + /// Removes a from the guest . + /// + /// The to remove. public static void RemoveGuestFromGroup(Guest guest) { GetGuestGroup().MemberIds.Remove(guest.InternalId); + RefreshGroups(); } + /// + /// Removes a from a . + /// + /// The to remove. + /// The to remove the from. public static void RemoveMemberFromGroup(Member member, Group group) { group.MemberIds.Remove(member.InternalId); Pool.Server.DataProvider.Save(); + RefreshGroups(); } } + // TODO: Add docs [JsonConverter(typeof(StringEnumConverter))] public enum GroupActionResult { diff --git a/Management/UserManager.cs b/Management/UserManager.cs index 3dcb78a..8d71b57 100644 --- a/Management/UserManager.cs +++ b/Management/UserManager.cs @@ -6,21 +6,34 @@ namespace Neo.Core.Management { + /// + /// Provides methods to manage s. + /// public static class UserManager { + /// + /// Returns the root . + /// + /// Returns the root . public static Account GetRoot() { - return Pool.Server.Accounts.Find(g => g.Attributes.ContainsKey("neo.usertype") && g.Attributes["neo.usertype"].ToString() == "root"); + return Pool.Server.Accounts.Find(_ => _.Attributes.ContainsKey("neo.usertype") && _.Attributes["neo.usertype"].ToString() == "root"); } + /// + /// Updates the list of s for each connected . + /// public static void RefreshAccounts() { var accounts = JsonConvert.DeserializeObject>(JsonConvert.SerializeObject(Pool.Server.Accounts)); - accounts.ForEach(a => a.Password = null); + accounts.ForEach(_ => _.Password = null); - Pool.Server.SendPackageTo(Target.All, new Package(PackageType.AccountListUpdate, accounts)); + Target.All.SendPackage(new Package(PackageType.AccountListUpdate, accounts)); } + /// + /// Updates the list of s for each connected . + /// public static void RefreshUsers() { - Pool.Server.SendPackageTo(Target.All, new Package(PackageType.UserListUpdate, Pool.Server.Users)); + Target.All.SendPackage(new Package(PackageType.UserListUpdate, Pool.Server.Users)); } } } diff --git a/Networking/BaseServer.cs b/Networking/BaseServer.cs index 2032bc1..ee87fb1 100644 --- a/Networking/BaseServer.cs +++ b/Networking/BaseServer.cs @@ -23,10 +23,29 @@ namespace Neo.Core.Networking /// public abstract class BaseServer { - private WebSocketServer webSocketServer; + /// + /// Contains all known s. + /// public List Accounts { get; set; } = new List(); + + /// + /// Contains all known s. + /// public List Channels { get; set; } = new List(); + + /// + /// Contains all connected s. + /// + public List Clients { get; set; } = new List(); + + /// + /// Contains all known s. + /// public List Groups { get; set; } = new List(); + + /// + /// Contains all connected s. + /// public List Users { get; set; } = new List(); public Dictionary KnownPermissions { get; set; } = new Dictionary { @@ -46,9 +65,7 @@ public abstract class BaseServer }; public DataProvider DataProvider { get; set; } - - public List Clients { get; set; } = new List(); - + // ReSharper disable once InconsistentNaming public RSAParameters RSAPublicParameters { get; private set; } @@ -58,15 +75,37 @@ public abstract class BaseServer protected string dataPath; + private WebSocketServer webSocketServer; + public abstract Task OnConnect(Client client); public abstract Task OnDisconnect(string clientId, ushort code, string reason, bool wasClean); public abstract Task OnError(string clientId, Exception ex, string message); public abstract Task OnPackage(string clientId, Package package); + /// + /// Returns the associated to a . + /// + /// The to get the from. + /// Returns the . + protected User GetUser(Client client) { + return GetUser(client.ClientId); + } + + /// + /// Returns the associated to a . + /// + /// The internal id of the to get the from. + /// Returns the . protected User GetUser(string clientId) { return Users.Find(u => u.Client.ClientId == clientId); } + /// + /// Initializes this . + /// + /// The path to the config file. + /// The path to the data directory. + /// The path to the plugins directory. public void Initialize(string configPath, string dataDirectoryPath, string pluginDirectoryPath) { ConfigManager.Instance.Load(configPath); Pool.Server = this; @@ -159,8 +198,9 @@ public abstract class BaseServer Logger.Instance.Log(LogLevel.Debug, "No guest group existed. Default guest group created"); } - foreach (var pluginFile in new DirectoryInfo(pluginDirectoryPath).EnumerateFiles("*.dll")) + foreach (var pluginFile in new DirectoryInfo(pluginDirectoryPath).EnumerateFiles("*.dll")) { PluginLoader.InitializePlugin(pluginFile.FullName); + } EventService.RaiseEvent(EventType.ServerInitialized, this); } @@ -171,8 +211,9 @@ public abstract class BaseServer /// The recipients of the . /// The to send. public void SendPackageTo(Target target, Package package) { - foreach (var client in Clients.FindAll(c => target.Targets.Contains(c.ClientId))) + foreach (var client in Clients.FindAll(c => target.Targets.Contains(c.ClientId))) { client.SendPackage(package); + } } /// @@ -196,9 +237,11 @@ public abstract class BaseServer //Logger.Instance.Log(LogLevel.Ok, "RSA key pair successfully generated"); Logger.Instance.Log(LogLevel.Info, $"Attempting to start WebSocket server on ws://{ConfigManager.Instance.Values.ServerAddress}:{ConfigManager.Instance.Values.ServerPort}..."); + webSocketServer = new WebSocketServer($"ws://{ConfigManager.Instance.Values.ServerAddress}:{ConfigManager.Instance.Values.ServerPort}"); webSocketServer.AddWebSocketService("/neo"); webSocketServer.Start(); + Logger.Instance.Log(LogLevel.Info, $"Use {Dns.GetHostEntry(Dns.GetHostName()).AddressList.ToList().Find(ip => ip.AddressFamily == AddressFamily.InterNetwork)} to connect to this instance.", true); Logger.Instance.Log(LogLevel.Ok, "WebSocket server successfully started. Waiting for connections..."); } @@ -209,8 +252,11 @@ public abstract class BaseServer public void Stop() { ConfigManager.Instance.Save(); DataProvider.Save(); + Logger.Instance.Log(LogLevel.Info, "Attempting to stop WebSocket server..."); + webSocketServer.Stop(); + Logger.Instance.Log(LogLevel.Ok, "WebSocket server successfully stopped"); } } diff --git a/Networking/WebServer.cs b/Networking/HttpServer.cs similarity index 84% rename from Networking/WebServer.cs rename to Networking/HttpServer.cs index eee5174..c6ed71a 100644 --- a/Networking/WebServer.cs +++ b/Networking/HttpServer.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net; using System.Net.Sockets; using System.Threading; @@ -9,10 +8,11 @@ namespace Neo.Core.Networking { - public class WebServer + /// + /// Represents a server used to handle all HTTP communication. + /// + public class HttpServer { - public int Port { get; private set; } - private static readonly IDictionary mimeTypeMappings = new Dictionary(StringComparer.InvariantCultureIgnoreCase) { #region Extension to MIME type list { ".asf", "video/x-ms-asf" }, @@ -82,54 +82,53 @@ public class WebServer #endregion }; + /// + /// The port this is listening on. + /// + public int Port { get; private set; } + private readonly string[] indexFiles = { "index.html", "index.htm", "default.html", "default.htm" }; - - private Thread thread; + private string rootDirectory; private HttpListener listener; private CancellationTokenSource cancellationTokenSource; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// Directory path to serve. + /// Path of the directory to serve. /// Port to listen on. - public WebServer(string path, int port) { + public HttpServer(string path, int port) { this.Initialize(path, port); } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// Directory path to serve. - public WebServer(string path) { + /// Path of the directory to serve. + public HttpServer(string path) { var l = new TcpListener(IPAddress.Loopback, 0); - l.Start(); + l.Start(); var port = ((IPEndPoint) l.LocalEndpoint).Port; - l.Stop(); this.Initialize(path, port); } - /// - /// Stops the server and aborts all threads. - /// - public void Stop() { - cancellationTokenSource.Cancel(); - - //thread.Abort(); - - listener.Stop(); + + private void Initialize(string path, int port) { + rootDirectory = path; + Port = port; - cancellationTokenSource.Dispose(); + cancellationTokenSource = new CancellationTokenSource(); + ThreadPool.QueueUserWorkItem(Listen, cancellationTokenSource.Token); } private void Listen(object obj) { @@ -141,8 +140,9 @@ public class WebServer try { listener.Start(); } catch { - Logger.Instance.Log(LogLevel.Error, "WebServer couldn't be started. Make sure you are running in high priviledged mode."); - Logger.Instance.Log(LogLevel.Error, "Error starting WebServer on :" + Port + ". Avatars won't be available."); + // TODO: Improve Log Messages + Logger.Instance.Log(LogLevel.Error, "HttpServer couldn't be started. Make sure you are running in high priviledged mode."); + Logger.Instance.Log(LogLevel.Error, "Error starting HttpServer on :" + Port + ". Avatars won't be available."); return; } @@ -175,20 +175,20 @@ public class WebServer if (File.Exists(filename)) { try { - var input = new FileStream(filename, FileMode.Open); + var stream = new FileStream(filename, FileMode.Open); context.Response.ContentType = mimeTypeMappings.TryGetValue(Path.GetExtension(filename), out var mime) ? mime : "application/octet-stream"; - context.Response.ContentLength64 = input.Length; + context.Response.ContentLength64 = stream.Length; context.Response.AddHeader("Date", DateTime.Now.ToString("r")); context.Response.AddHeader("Last-Modified", File.GetLastWriteTime(filename).ToString("r")); var buffer = new byte[1024 * 16]; int nbytes; - while ((nbytes = input.Read(buffer, 0, buffer.Length)) > 0) { + while ((nbytes = stream.Read(buffer, 0, buffer.Length)) > 0) { context.Response.OutputStream.Write(buffer, 0, nbytes); } - input.Close(); + stream.Close(); context.Response.StatusCode = (int) HttpStatusCode.OK; context.Response.OutputStream.Flush(); @@ -202,15 +202,13 @@ public class WebServer context.Response.OutputStream.Close(); } - private void Initialize(string path, int port) { - rootDirectory = path; - Port = port; - - cancellationTokenSource = new CancellationTokenSource(); - ThreadPool.QueueUserWorkItem(Listen, cancellationTokenSource.Token); - - //thread = new Thread(Listen); - //thread.Start(); + /// + /// Stops this . + /// + public void Stop() { + cancellationTokenSource.Cancel(); + listener.Stop(); + cancellationTokenSource.Dispose(); } } } \ No newline at end of file diff --git a/Networking/Target.cs b/Networking/Target.cs index 3682f06..9234026 100644 --- a/Networking/Target.cs +++ b/Networking/Target.cs @@ -22,6 +22,7 @@ public class Target private List targets = new List(); + /// /// Initializes a new instance of the class. /// @@ -43,6 +44,7 @@ public class Target targets.Add(user.Client.ClientId); } + /// /// Adds a recipient to the list of recipients. /// @@ -133,7 +135,11 @@ public class Target return this; } - public void SendPackageTo(Package package) { + /// + /// Sends a to all recipients. + /// + /// The to send. + public void SendPackage(Package package) { Pool.Server.SendPackageTo(this, package); } } diff --git a/Shared/Channel.cs b/Shared/Channel.cs index 8045e8c..9f0227b 100644 --- a/Shared/Channel.cs +++ b/Shared/Channel.cs @@ -148,13 +148,13 @@ public class Channel : IAttributable if (message.Contains('@')) { var mentions = message.Split(' ').ToList().Where(s => s.StartsWith('@') && Pool.Server.Users.Any(u => u.Identity.Id == s.Substring(1))).Select(s => s.Substring(1)).Distinct().ToList(); - new Target().AddMany(Pool.Server.Users.FindAll(u => mentions.Contains(u.Identity.Id)).ToArray()).SendPackageTo(new Package(PackageType.Mention, received)); + new Target().AddMany(Pool.Server.Users.FindAll(u => mentions.Contains(u.Identity.Id)).ToArray()).SendPackage(new Package(PackageType.Mention, received)); } - new Target().AddMany(this).Remove(sender).SendPackageTo(new Package(PackageType.Message, received)); + new Target().AddMany(this).Remove(sender).SendPackage(new Package(PackageType.Message, received)); if (sender.ActiveChannel == this) { - new Target(sender).SendPackageTo(new Package(PackageType.Message, sent)); + new Target(sender).SendPackage(new Package(PackageType.Message, sent)); } SaveMessage(received); diff --git a/Shared/Group.cs b/Shared/Group.cs index 84689b1..6a5e27b 100644 --- a/Shared/Group.cs +++ b/Shared/Group.cs @@ -7,23 +7,50 @@ namespace Neo.Core.Shared { + /// + /// Represents a set of s. + /// public class Group : IAttributable, IAuthorizable { + /// + /// The attributes assigned to this . + /// public Dictionary Attributes { get; set; } = new Dictionary(); + /// + /// The user-defined id of this . + /// public string Id { get; set; } + /// + /// The unique id used to identify this . + /// public Guid InternalId { get; set; } = Guid.NewGuid(); + /// + /// Contains the internal ids of all members of this . + /// public List MemberIds { get; set; } = new List(); + /// + /// Contains all s that are members of this . + /// [JsonIgnore] public List Members => MemberIds.Select(m => Pool.Server.Accounts.Find(a => a.InternalId == m)).ToList(); + /// + /// The name of this . + /// public string Name { get; set; } + /// + /// The permissions granted to all members of this . + /// public Dictionary Permissions { get; set; } = new Dictionary(); + /// + /// Determines the sort and inheritance order of this . + /// public int SortValue { get; set; } } } diff --git a/Shared/Guest.cs b/Shared/Guest.cs index ba57a3f..bc4cc4d 100644 --- a/Shared/Guest.cs +++ b/Shared/Guest.cs @@ -2,6 +2,9 @@ namespace Neo.Core.Shared { + /// + /// Represents an anonymous . + /// public class Guest : User { } diff --git a/Shared/Identity.cs b/Shared/Identity.cs index 5e45c52..402543a 100644 --- a/Shared/Identity.cs +++ b/Shared/Identity.cs @@ -1,9 +1,23 @@ namespace Neo.Core.Shared { + /// + /// Represents the public appearance of a . + /// public class Identity { + /// + /// The user-defined id of the . + /// public string Id { get; set; } + + /// + /// The name of the . + /// public string Name { get; set; } + + /// + /// The file extension of the avatar of the . + /// public string AvatarFileExtension { get; set; } } } diff --git a/Shared/LifeSpan.cs b/Shared/LifeSpan.cs index 07b085f..d62e6a4 100644 --- a/Shared/LifeSpan.cs +++ b/Shared/LifeSpan.cs @@ -3,12 +3,27 @@ namespace Neo.Core.Shared { + /// + /// Specifies the type of a . + /// [JsonConverter(typeof(StringEnumConverter))] public enum Lifespan { + /// + /// The will be deleted automatically on a given date. + /// Custom, + /// + /// The will be deleted automatically when all members are gone. + /// Volatile, + /// + /// The will be deleted automatically when the server stops. + /// Temporary, + /// + /// The will persist and has to be deleted manually. + /// Permanent } } diff --git a/Shared/Logger.cs b/Shared/Logger.cs index 56433d1..38656f4 100644 --- a/Shared/Logger.cs +++ b/Shared/Logger.cs @@ -3,8 +3,18 @@ namespace Neo.Core.Shared { + // TODO: Add Channels and Files as output channels + /// + /// Represents a single access point for logging information into different outputs. + /// + /// This class uses a thread-safe singleton lazy pattern and can only be accessed through the property. + /// + /// public sealed class Logger { + /// + /// Returns the only instance of the . + /// public static Logger Instance => instance.Value; private static readonly Lazy instance = new Lazy(() => new Logger()); @@ -20,6 +30,12 @@ public sealed class Logger { LogLevel.Fatal, ConsoleColor.Red } }; + /// + /// Logs the specified message to all outputs. + /// + /// The of this message. + /// The content of this message. + /// Whether the message should be displayed more prominently. public void Log(LogLevel level, string message, bool verbose = false) { if (verbose) { Console.WriteLine(); @@ -49,13 +65,34 @@ public sealed class Logger } } + /// + /// Specifies the level of a log message. + /// public enum LogLevel { + /// + /// The message contains debug information. + /// Debug, + /// + /// The message contains general information. + /// Info, + /// + /// The message contains information about a successful action. + /// Ok, + /// + /// The message contains warnings. + /// Warn, + /// + /// The message contains error information. + /// Error, + /// + /// The message contains information about a failed action. + /// Fatal } } diff --git a/Shared/Member.cs b/Shared/Member.cs index d3c9861..76c883e 100644 --- a/Shared/Member.cs +++ b/Shared/Member.cs @@ -4,24 +4,45 @@ namespace Neo.Core.Shared { + /// + /// Represents a registered . + /// public class Member : User { + /// + /// The associated with this . + /// public Account Account { get; set; } + /// + /// The attributes assigned to this . + /// public override Dictionary Attributes { get => Account.Attributes; set => Account.Attributes = value; } + /// + /// Contains all s this is a member of. + /// public List Groups => Account.Groups; + /// + /// The of this . + /// public override Identity Identity { get => Account.Identity; set => Account.Identity = value; } + /// + /// The unique id used to identify this . + /// public override Guid InternalId => Account.InternalId; + /// + /// The permissions granted to this . + /// public override Dictionary Permissions { get => Account.Permissions; set => Account.Permissions = value; diff --git a/Shared/Pool.cs b/Shared/Pool.cs index 4cb074a..440c894 100644 --- a/Shared/Pool.cs +++ b/Shared/Pool.cs @@ -2,8 +2,14 @@ namespace Neo.Core.Shared { + /// + /// Acts as an access point for the . + /// public static class Pool { + /// + /// The operating instance. + /// public static BaseServer Server { get; set; } } } diff --git a/Shared/User.cs b/Shared/User.cs index abf99d8..355462d 100644 --- a/Shared/User.cs +++ b/Shared/User.cs @@ -7,24 +7,53 @@ namespace Neo.Core.Shared { + /// + /// Represents a single person using the client. + /// public abstract class User : IAttributable, IAuthorizable { + /// + /// The this has currently opened. + /// [JsonIgnore] public Channel ActiveChannel => Pool.Server.Channels.Find(c => c.ActiveMemberIds.Contains(InternalId)); + /// + /// The attributes assigned to this . + /// public virtual Dictionary Attributes { get; set; } = new Dictionary(); + /// + /// Contains all s this is a member of. + /// [JsonIgnore] public List Channels => Pool.Server.Channels.FindAll(c => c.MemberIds.Contains(InternalId)); + /// + /// The associated with this . + /// public Client Client { get; set; } + /// + /// The of this . + /// public virtual Identity Identity { get; set; } + /// + /// The unique id used to identify this . + /// public virtual Guid InternalId { get; set; } = Guid.NewGuid(); + /// + /// The permissions granted to this . + /// public virtual Dictionary Permissions { get; set; } = new Dictionary(); + + /// + /// Returns a with this . + /// + /// Returns the created . public Target ToTarget() { return new Target(this); }