mirror of
https://github.com/RedWizardsLab/EchoHub.git
synced 2026-09-04 23:34:10 +02:00
96 lines
3.1 KiB
C#
96 lines
3.1 KiB
C#
using System.Net.Sockets;
|
|
using System.Text;
|
|
using EchoHub.Core.Constants;
|
|
|
|
namespace EchoHub.Server.Irc;
|
|
|
|
/// <summary>
|
|
/// Manages a single IRC client TCP connection.
|
|
/// </summary>
|
|
public sealed class IrcClientConnection : IAsyncDisposable
|
|
{
|
|
private readonly TcpClient _tcpClient;
|
|
private readonly StreamReader _reader;
|
|
private readonly StreamWriter _writer;
|
|
private readonly SemaphoreSlim _writeLock = new(1, 1);
|
|
|
|
// Connection identity
|
|
public string ConnectionId { get; } = $"{HubConstants.IrcConnectionIdPrefix}{Guid.NewGuid()}";
|
|
|
|
// Registration state
|
|
public string? Nickname { get; set; }
|
|
public string? Username { get; set; }
|
|
public string? RealName { get; set; }
|
|
public string? Password { get; set; }
|
|
public Guid? UserId { get; set; }
|
|
public bool IsRegistered { get; set; }
|
|
public bool IsAuthenticated { get; set; }
|
|
public bool IsSasl { get; set; }
|
|
public bool CapNegotiating { get; set; }
|
|
|
|
// Channel state — thread-safe: written by command handler, read by broadcaster threads
|
|
private readonly HashSet<string> _joinedChannels = new(StringComparer.OrdinalIgnoreCase);
|
|
private readonly object _channelLock = new();
|
|
|
|
// Away state
|
|
public string? AwayMessage { get; set; }
|
|
|
|
public string Hostmask => $"{Nickname}!{Username ?? Nickname}@echohub";
|
|
|
|
public void JoinChannel(string channel) { lock (_channelLock) _joinedChannels.Add(channel); }
|
|
public void LeaveChannel(string channel) { lock (_channelLock) _joinedChannels.Remove(channel); }
|
|
public bool IsInChannel(string channel) { lock (_channelLock) return _joinedChannels.Contains(channel); }
|
|
public List<string> GetJoinedChannels() { lock (_channelLock) return [.. _joinedChannels]; }
|
|
|
|
public IrcClientConnection(TcpClient tcpClient, Stream stream)
|
|
{
|
|
_tcpClient = tcpClient;
|
|
var utf8NoBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
|
|
_reader = new StreamReader(stream, utf8NoBom);
|
|
_writer = new StreamWriter(stream, utf8NoBom) { AutoFlush = true, NewLine = "\r\n" };
|
|
}
|
|
|
|
public async Task<string?> ReadLineAsync(CancellationToken ct)
|
|
{
|
|
try
|
|
{
|
|
return await _reader.ReadLineAsync(ct);
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public async Task SendAsync(string line)
|
|
{
|
|
await _writeLock.WaitAsync();
|
|
try
|
|
{
|
|
await _writer.WriteLineAsync(line);
|
|
}
|
|
catch
|
|
{
|
|
// Connection lost — swallow
|
|
}
|
|
finally
|
|
{
|
|
_writeLock.Release();
|
|
}
|
|
}
|
|
|
|
public Task SendNumericAsync(string serverName, string numeric, string target, string text)
|
|
=> SendAsync($":{serverName} {numeric} {target} {text}");
|
|
|
|
public Task SendNumericAsync(string serverName, string numeric, string text)
|
|
=> SendNumericAsync(serverName, numeric, Nickname ?? "*", text);
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
try { _tcpClient.Close(); } catch { }
|
|
_reader.Dispose();
|
|
_writer.Dispose();
|
|
_writeLock.Dispose();
|
|
}
|
|
}
|