Simple relay import
This commit is contained in:
35
NostrRelay/INostrRelay.cs
Normal file
35
NostrRelay/INostrRelay.cs
Normal 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);
|
||||
}
|
13
NostrRelay/NostrRelay.csproj
Normal file
13
NostrRelay/NostrRelay.csproj
Normal 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
95
NostrRelay/Relay.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user