Simple relay import

This commit is contained in:
2024-01-11 13:05:31 +00:00
parent 17df7e73d1
commit a0e59757b5
9 changed files with 232 additions and 0 deletions

35
NostrRelay/INostrRelay.cs Normal file
View File

@ -0,0 +1,35 @@
using System.Net;
using System.Net.WebSockets;
using Nostr.Client.Messages;
using Nostr.Client.Requests;
namespace NostrRelay;
public record NostrClientContext(WebSocket WebSocket, IPAddress Ip, string UserAgent);
public record HandleEventResponse(bool Ok, string? Message);
public interface INostrRelay
{
/// <summary>
/// If we should handle this connection
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
ValueTask<bool> AcceptConnection(NostrClientContext context);
/// <summary>
/// Respond to a request for content
/// </summary>
/// <param name="context"></param>
/// <param name="req"></param>
/// <returns></returns>
IAsyncEnumerable<NostrEvent> HandleRequest(NostrClientContext context, NostrRequest req);
/// <summary>
/// Handle new event publish
/// </summary>
/// <param name="context"></param>
/// <param name="ev"></param>
/// <returns></returns>
ValueTask<HandleEventResponse> HandleEvent(NostrClientContext context, NostrEvent ev);
}

View File

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Nostr.Client" Version="2.0.0" />
</ItemGroup>
</Project>

95
NostrRelay/Relay.cs Normal file
View File

@ -0,0 +1,95 @@
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<THandler> where THandler : INostrRelay
{
private readonly ILogger<NostrRelay<THandler>> _logger;
private readonly CancellationTokenSource _cts = new();
private readonly THandler _handler;
private readonly NostrClientContext _ctx;
public NostrRelay(THandler handler, NostrClientContext ctx, CancellationToken ct, ILogger<NostrRelay<THandler>> logger)
{
_handler = handler;
_ctx = ctx;
_logger = logger;
ct.Register(() => _cts.Cancel());
}
private async Task WriteResponse<T>(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<byte>.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<NostrRequest>(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<NostrEventRequest>(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);
}
}
}
}
}
}