sharp-chat/SharpChat.Common/Channels/Channel.cs

160 lines
5.6 KiB
C#

using SharpChat.Events;
using SharpChat.Sessions;
using SharpChat.Users;
using System;
using System.Collections.Generic;
using System.Linq;
namespace SharpChat.Channels {
public class Channel : IChannel, IEventHandler {
public const int ID_LENGTH = 8;
public string ChannelId { get; }
public string Name { get; private set; }
public string Topic { get; private set; }
public bool IsTemporary { get; private set; }
public int MinimumRank { get; private set; }
public bool AutoJoin { get; private set; }
public uint MaxCapacity { get; private set; }
public int Order { get; private set; }
public long OwnerId { get; private set; }
private readonly object Sync = new();
private HashSet<long> Users { get; } = new();
private Dictionary<string, long> Sessions { get; } = new();
public bool HasTopic
=> !string.IsNullOrWhiteSpace(Topic);
public string Password { get; private set; } = string.Empty;
public bool HasPassword
=> !string.IsNullOrWhiteSpace(Password);
public Channel(
string channelId,
string name,
string topic,
bool temp,
int minimumRank,
string password,
bool autoJoin,
uint maxCapacity,
long ownerId,
int order
) {
ChannelId = channelId ?? throw new ArgumentNullException(nameof(channelId));
Name = name ?? throw new ArgumentNullException(nameof(name));
Topic = topic;
IsTemporary = temp;
MinimumRank = minimumRank;
Password = password ?? string.Empty;
AutoJoin = autoJoin;
MaxCapacity = maxCapacity;
OwnerId = ownerId;
Order = order;
}
public bool VerifyPassword(string password) {
if(password == null)
throw new ArgumentNullException(nameof(password));
lock(Sync)
return !HasPassword || Password.Equals(password);
}
public bool HasUser(IUser user) {
if(user == null)
return false;
lock(Sync)
return Users.Contains(user.UserId);
}
public bool HasSession(ISession session) {
if(session == null)
return false;
lock(Sync)
return Sessions.ContainsKey(session.SessionId);
}
public void GetUserIds(Action<IEnumerable<long>> callback) {
if(callback == null)
throw new ArgumentNullException(nameof(callback));
lock(Sync)
callback(Users);
}
public void GetSessionIds(Action<IEnumerable<string>> callback) {
if(callback == null)
throw new ArgumentNullException(nameof(callback));
lock(Sync)
callback(Sessions.Keys);
}
public int CountUsers() {
lock(Sync)
return Users.Count;
}
public int CountUserSessions(IUser user) {
if(user == null)
throw new ArgumentNullException(nameof(user));
lock(Sync)
return Sessions.Values.Count(u => u == user.UserId);
}
public void HandleEvent(object sender, IEvent evt) {
switch(evt) {
case ChannelUpdateEvent update: // Owner?
lock(Sync) {
if(update.HasName)
Name = update.Name;
if(update.HasTopic)
Topic = update.Topic;
if(update.IsTemporary.HasValue)
IsTemporary = update.IsTemporary.Value;
if(update.MinimumRank.HasValue)
MinimumRank = update.MinimumRank.Value;
if(update.HasPassword)
Password = update.Password;
if(update.AutoJoin.HasValue)
AutoJoin = update.AutoJoin.Value;
if(update.MaxCapacity.HasValue)
MaxCapacity = update.MaxCapacity.Value;
if(update.Order.HasValue)
Order = update.Order.Value;
}
break;
case ChannelUserJoinEvent cuje:
lock(Sync) {
Sessions.Add(cuje.SessionId, cuje.UserId);
Users.Add(cuje.UserId);
}
break;
case ChannelSessionJoinEvent csje:
lock(Sync)
Sessions.Add(csje.SessionId, csje.UserId);
break;
case ChannelUserLeaveEvent cule:
lock(Sync) {
Users.Remove(cule.UserId);
Queue<string> delete = new(Sessions.Where(s => s.Value == cule.UserId).Select(s => s.Key));
while(delete.TryDequeue(out string sessionId))
Sessions.Remove(sessionId);
}
break;
case ChannelSessionLeaveEvent csle:
lock(Sync)
Sessions.Remove(csle.SessionId);
break;
}
}
public bool Equals(IChannel other)
=> other != null && ChannelId.Equals(other.ChannelId);
public override string ToString()
=> $@"<Channel {ChannelId}#{Name}>";
}
}