sharp-chat/SharpChat.Common/Context.cs

146 lines
5.6 KiB
C#

using SharpChat.Bans;
using SharpChat.Channels;
using SharpChat.Configuration;
using SharpChat.Database;
using SharpChat.DataProvider;
using SharpChat.Events;
using SharpChat.Messages;
using SharpChat.Messages.Storage;
using SharpChat.RateLimiting;
using SharpChat.Sessions;
using SharpChat.Users;
using SharpChat.Users.Remote;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace SharpChat {
public class Context : IDisposable {
public const int ID_LENGTH = 8;
public string ServerId { get; }
public EventDispatcher Events { get; }
public SessionManager Sessions { get; }
public UserManager Users { get; }
public ChannelManager Channels { get; }
public ChannelUserRelations ChannelUsers { get; }
public MessageManager Messages { get; }
public BanManager Bans { get; }
public IDataProvider DataProvider { get; }
public RateLimitManager RateLimiting { get; }
public WelcomeMessage WelcomeMessage { get; }
public ChatBot Bot { get; } = new();
private Timer BumpTimer { get; }
public DateTimeOffset Created { get; }
public Context(IConfig config, IDatabaseBackend databaseBackend, IDataProvider dataProvider) {
if(config == null)
throw new ArgumentNullException(nameof(config));
ServerId = RNG.NextString(ID_LENGTH); // maybe read this from the cfg instead
Created = DateTimeOffset.Now; // read this from config definitely
DatabaseWrapper db = new(databaseBackend ?? throw new ArgumentNullException(nameof(databaseBackend)));
IMessageStorage msgStore = db.IsNullBackend
? new MemoryMessageStorage()
: new ADOMessageStorage(db);
Events = new EventDispatcher();
DataProvider = dataProvider ?? throw new ArgumentNullException(nameof(dataProvider));
Users = new UserManager(Events);
Sessions = new SessionManager(Events, Users, config.ScopeTo(@"sessions"), ServerId);
Messages = new MessageManager(Events, msgStore, config.ScopeTo(@"messages"));
Channels = new ChannelManager(Events, config, Bot);
ChannelUsers = new ChannelUserRelations(Events, Channels, Users, Sessions, Messages);
Bans = new BanManager(Users, DataProvider.BanClient, DataProvider.UserClient, Events);
RateLimiting = new RateLimitManager(config.ScopeTo(@"rateLimit"));
WelcomeMessage = new WelcomeMessage(config.ScopeTo(@"welcome"));
Events.AddEventHandler(Sessions);
Events.ProtectEventHandler(Sessions);
Events.AddEventHandler(Users);
Events.ProtectEventHandler(Users);
Events.AddEventHandler(Channels);
Events.ProtectEventHandler(Channels);
Events.AddEventHandler(ChannelUsers);
Events.ProtectEventHandler(ChannelUsers);
Events.AddEventHandler(Messages);
Events.ProtectEventHandler(Messages);
Events.StartProcessing();
Channels.UpdateChannels();
// Should probably not rely on Timers in the future
BumpTimer = new Timer(e => {
Logger.Write(@"Nuking dead sessions and bumping remote online status...");
Sessions.CheckTimeOut();
Sessions.GetActiveLocalSessions(sessions => {
Dictionary<IUser, List<ISession>> data = new();
foreach(ISession session in sessions) {
if(!data.ContainsKey(session.User))
data.Add(session.User, new());
data[session.User].Add(session);
}
DataProvider.UserClient.BumpUsers(
data.Select(kvp => new UserBumpInfo(kvp.Key, kvp.Value)),
() => Logger.Debug(@"Successfully bumped remote online status!"),
ex => { Logger.Write(@"Failed to bump remote online status."); Logger.Debug(ex); }
);
});
}, null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
}
public void BroadcastMessage(string text) {
Events.DispatchEvent(this, new BroadcastMessageEvent(Bot, text));
}
[Obsolete(@"Use ChannelUsers.JoinChannel")]
public void JoinChannel(IUser user, IChannel channel) {
// handle in channelusers
//channel.SendPacket(new UserChannelJoinPacket(user));
// send after join packet for v1
//user.SendPacket(new ContextClearPacket(channel, ContextClearMode.MessagesUsers));
// send after join
//ChannelUsers.GetUsers(channel, u => user.SendPacket(new ContextUsersPacket(u.Except(new[] { user }).OrderByDescending(u => u.Rank))));
// send after join, maybe add a capability that makes this implicit?
/*Messages.GetMessages(channel, m => {
foreach(IMessage msg in m)
user.SendPacket(new ContextMessagePacket(msg));
});*/
// should happen implicitly for v1 clients
//user.ForceChannel(channel);
}
private bool IsDisposed;
~Context()
=> DoDispose();
public void Dispose() {
DoDispose();
GC.SuppressFinalize(this);
}
private void DoDispose() {
if (IsDisposed)
return;
IsDisposed = true;
BumpTimer.Dispose();
Events.FinishProcessing();
}
}
}