using System.Buffers; using System.Net.WebSockets; using System.Text; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using Nostr.Client.Json; using Nostr.Client.Requests; using Nostr.Client.Responses; namespace NostrRelay; public class NostrRelay where THandler : INostrRelay { private readonly ILogger> _logger; private readonly CancellationTokenSource _cts = new(); private readonly THandler _handler; private readonly NostrClientContext _ctx; public NostrRelay(THandler handler, NostrClientContext ctx, CancellationToken ct, ILogger> logger) { _handler = handler; _ctx = ctx; _logger = logger; ct.Register(() => _cts.Cancel()); } private async Task WriteResponse(T obj) { var rspJson = JsonConvert.SerializeObject(obj, NostrSerializer.Settings); _logger.LogDebug("Sending {msg}", rspJson); await _ctx.WebSocket.SendAsync(Encoding.UTF8.GetBytes(rspJson), WebSocketMessageType.Text, true, _cts.Token); } public async Task Read() { if (!await _handler.AcceptConnection(_ctx)) return; var offset = 0; var mem = MemoryPool.Shared.Rent(1024 * 1024); while (!_cts.IsCancellationRequested) { var msg = await _ctx.WebSocket.ReceiveAsync(mem.Memory[offset..], _cts.Token); if (msg.MessageType is WebSocketMessageType.Text) { var buff = mem.Memory[..(offset + msg.Count)]; offset = !msg.EndOfMessage ? offset + msg.Count : 0; if (!msg.EndOfMessage) continue; var str = Encoding.UTF8.GetString(buff.Span); _logger.LogDebug("Got msg {msg}", str); if (str.StartsWith("[\"REQ\"")) { var req = JsonConvert.DeserializeObject(str, NostrSerializer.Settings); if (req != default) { await foreach (var ev in _handler.HandleRequest(_ctx, req)) { await WriteResponse(new NostrEventResponse() { MessageType = "EVENT", Subscription = req.Subscription, Event = ev }); } var rsp = new NostrEoseResponse { MessageType = "EOSE", Subscription = req.Subscription }; await WriteResponse(rsp); } } else if (str.StartsWith("[\"EVENT\"")) { var req = JsonConvert.DeserializeObject(str, NostrSerializer.Settings); if (req != default) { var result = await _handler.HandleEvent(_ctx, req.Event); var rsp = new NostrOkResponse() { MessageType = "OK", EventId = req.Event.Id, Accepted = result.Ok, Message = result.Message ?? "" }; await WriteResponse(rsp); } } } } } }