sharp-chat/SharpChat/ChannelManager.cs

161 lines
5.3 KiB
C#
Raw Normal View History

2022-08-30 15:00:58 +00:00
using SharpChat.Packet;
using System;
using System.Collections.Generic;
using System.Linq;
namespace SharpChat {
public class ChannelException : Exception { }
public class ChannelExistException : ChannelException { }
public class ChannelInvalidNameException : ChannelException { }
public class ChannelManager : IDisposable {
2023-02-07 15:01:56 +00:00
private readonly List<ChatChannel> Channels = new();
2022-08-30 15:00:58 +00:00
public readonly ChatContext Context;
public bool IsDisposed { get; private set; }
public ChannelManager(ChatContext context) {
Context = context;
}
private ChatChannel _DefaultChannel;
public ChatChannel DefaultChannel {
get {
2023-02-07 15:01:56 +00:00
if(_DefaultChannel == null)
2022-08-30 15:00:58 +00:00
_DefaultChannel = Channels.FirstOrDefault();
return _DefaultChannel;
}
set {
2023-02-07 15:01:56 +00:00
if(value == null)
2022-08-30 15:00:58 +00:00
return;
2023-02-07 15:01:56 +00:00
if(Channels.Contains(value))
2022-08-30 15:00:58 +00:00
_DefaultChannel = value;
}
}
public void Add(ChatChannel channel) {
2023-02-07 15:01:56 +00:00
if(channel == null)
2022-08-30 15:00:58 +00:00
throw new ArgumentNullException(nameof(channel));
2023-02-07 15:01:56 +00:00
if(!channel.Name.All(c => char.IsLetter(c) || char.IsNumber(c) || c == '-'))
2022-08-30 15:00:58 +00:00
throw new ChannelInvalidNameException();
2023-02-07 15:01:56 +00:00
if(Get(channel.Name) != null)
2022-08-30 15:00:58 +00:00
throw new ChannelExistException();
// Add channel to the listing
Channels.Add(channel);
// Set as default if there's none yet
2023-02-07 15:01:56 +00:00
if(_DefaultChannel == null)
2022-08-30 15:00:58 +00:00
_DefaultChannel = channel;
// Broadcast creation of channel
2023-02-07 15:01:56 +00:00
foreach(ChatUser user in Context.Users.OfHierarchy(channel.Rank))
2022-08-30 15:00:58 +00:00
user.Send(new ChannelCreatePacket(channel));
}
public void Remove(ChatChannel channel) {
2023-02-07 15:01:56 +00:00
if(channel == null || channel == DefaultChannel)
2022-08-30 15:00:58 +00:00
return;
// Remove channel from the listing
Channels.Remove(channel);
// Move all users back to the main channel
// TODO: Replace this with a kick. SCv2 supports being in 0 channels, SCv1 should force the user back to DefaultChannel.
2023-02-07 15:01:56 +00:00
foreach(ChatUser user in channel.GetUsers()) {
2022-08-30 15:00:58 +00:00
Context.SwitchChannel(user, DefaultChannel, string.Empty);
}
// Broadcast deletion of channel
2023-02-07 15:01:56 +00:00
foreach(ChatUser user in Context.Users.OfHierarchy(channel.Rank))
2022-08-30 15:00:58 +00:00
user.Send(new ChannelDeletePacket(channel));
}
public bool Contains(ChatChannel chan) {
2023-02-07 15:01:56 +00:00
if(chan == null)
2022-08-30 15:00:58 +00:00
return false;
2023-02-07 15:01:56 +00:00
lock(Channels)
2022-08-30 15:00:58 +00:00
return Channels.Contains(chan) || Channels.Any(c => c.Name.ToLowerInvariant() == chan.Name.ToLowerInvariant());
}
public void Update(ChatChannel channel, string name = null, bool? temporary = null, int? hierarchy = null, string password = null) {
2023-02-07 15:01:56 +00:00
if(channel == null)
2022-08-30 15:00:58 +00:00
throw new ArgumentNullException(nameof(channel));
2023-02-07 15:01:56 +00:00
if(!Channels.Contains(channel))
2022-08-30 15:00:58 +00:00
throw new ArgumentException(@"Provided channel is not registered with this manager.", nameof(channel));
string prevName = channel.Name;
int prevHierarchy = channel.Rank;
bool nameUpdated = !string.IsNullOrWhiteSpace(name) && name != prevName;
2023-02-07 15:01:56 +00:00
if(nameUpdated) {
if(!name.All(c => char.IsLetter(c) || char.IsNumber(c) || c == '-'))
2022-08-30 15:00:58 +00:00
throw new ChannelInvalidNameException();
2023-02-07 15:01:56 +00:00
if(Get(name) != null)
2022-08-30 15:00:58 +00:00
throw new ChannelExistException();
channel.Name = name;
}
2023-02-07 15:01:56 +00:00
if(temporary.HasValue)
2022-08-30 15:00:58 +00:00
channel.IsTemporary = temporary.Value;
2023-02-07 15:01:56 +00:00
if(hierarchy.HasValue)
2022-08-30 15:00:58 +00:00
channel.Rank = hierarchy.Value;
2023-02-07 15:01:56 +00:00
if(password != null)
2022-08-30 15:00:58 +00:00
channel.Password = password;
// Users that no longer have access to the channel/gained access to the channel by the hierarchy change should receive delete and create packets respectively
2023-02-07 15:01:56 +00:00
foreach(ChatUser user in Context.Users.OfHierarchy(channel.Rank)) {
2022-08-30 15:00:58 +00:00
user.Send(new ChannelUpdatePacket(prevName, channel));
2023-02-07 15:01:56 +00:00
if(nameUpdated)
2022-08-30 15:00:58 +00:00
user.ForceChannel();
}
}
public ChatChannel Get(string name) {
2023-02-07 15:01:56 +00:00
if(string.IsNullOrWhiteSpace(name))
2022-08-30 15:00:58 +00:00
return null;
return Channels.FirstOrDefault(x => x.Name.ToLowerInvariant() == name.ToLowerInvariant());
}
public IEnumerable<ChatChannel> GetUser(ChatUser user) {
2023-02-07 15:01:56 +00:00
if(user == null)
2022-08-30 15:00:58 +00:00
return null;
return Channels.Where(x => x.HasUser(user));
}
public IEnumerable<ChatChannel> OfHierarchy(int hierarchy) {
2023-02-07 15:01:56 +00:00
lock(Channels)
2022-08-30 15:00:58 +00:00
return Channels.Where(c => c.Rank <= hierarchy).ToList();
}
2023-02-07 15:01:56 +00:00
~ChannelManager() {
DoDispose();
}
2022-08-30 15:00:58 +00:00
2023-02-07 15:01:56 +00:00
public void Dispose() {
DoDispose();
GC.SuppressFinalize(this);
}
2022-08-30 15:00:58 +00:00
2023-02-07 15:01:56 +00:00
private void DoDispose() {
if(IsDisposed)
2022-08-30 15:00:58 +00:00
return;
IsDisposed = true;
Channels.Clear();
}
}
}