NostrServices/FASTERlay/FASTERRelay.cs
2024-01-12 16:22:38 +00:00

119 lines
3.4 KiB
C#

using FASTER.core;
using Nostr.Client.Messages;
using Nostr.Client.Requests;
using NostrRelay;
namespace FASTERlay;
public class FasterRelay : INostrRelay, IDisposable
{
private readonly ClientSession<byte[], byte[], byte[], byte[], Empty, SimpleFunctions<byte[], byte[]>> _session;
private readonly ClientSession<string, byte[], byte[], byte[], Empty, TagSetFunctions> _tagsSession;
public FasterRelay(NostrStore store)
{
_session = store.MainStore.For(new SimpleFunctions<byte[], byte[]>()).NewSession<SimpleFunctions<byte[], byte[]>>();
_tagsSession = store.TagStore.For(new TagSetFunctions()).NewSession<TagSetFunctions>();
}
public ValueTask<bool> AcceptConnection(NostrClientContext context)
{
return ValueTask.FromResult(true);
}
public async IAsyncEnumerable<NostrEvent> HandleRequest(NostrClientContext context, NostrRequest req)
{
var iter = _session.Iterate();
var filter = req.NostrFilter;
while (iter.GetNext(out _, out _, out var bytes))
{
var ev = NostrBuf.Decode(bytes);
if (ev != default)
{
if (filter.Since.HasValue && ev.CreatedAt <= filter.Since)
{
continue;
}
if (filter.Until.HasValue && ev.CreatedAt > filter.Until)
{
continue;
}
if (filter.Ids != default && !filter.Ids.Contains(ev.Id!))
{
continue;
}
if (filter.Authors != default && !filter.Authors.Contains(ev.Pubkey!))
{
continue;
}
if (filter.Kinds != default && !filter.Kinds.Contains(ev.Kind))
{
continue;
}
yield return ev;
}
}
}
public async ValueTask<HandleEventResponse> HandleEvent(NostrClientContext context, NostrEvent ev)
{
await SaveEvent(ev);
return new(true, null);
}
public void Dispose()
{
_session.Dispose();
_tagsSession.Dispose();
}
private async Task SaveEvent(NostrEvent ev)
{
var idBytes = Convert.FromHexString(ev.Id!);
var eventBytes = NostrBuf.Encode(ev);
await _session.UpsertAsync(ref idBytes, ref eventBytes);
var indexTags = ev.Tags!.Where(a => a.TagIdentifier.Length == 1);
foreach (var tag in indexTags)
{
var kIndex = $"{tag.TagIdentifier}.{tag.AdditionalData[0]}";
await _tagsSession.RMWAsync(ref kIndex, ref idBytes);
}
await _session.CompletePendingAsync();
await _tagsSession.CompletePendingAsync();
}
private async Task<NostrEvent?> GetEvent(string id)
{
var idBytes = Convert.FromHexString(id);
var read = await _session.ReadAsync(ref idBytes);
if (read.Status.Found)
{
var data = read.Complete().output;
return NostrBuf.Decode(data);
}
return default;
}
private class TagSetFunctions : SimpleFunctions<string, byte[]>
{
public TagSetFunctions() : base((l, r) =>
{
var output = new byte[l.Length + r.Length];
Buffer.BlockCopy(l, 0, output, 0, l.Length);
Buffer.BlockCopy(r, 0, output, l.Length, r.Length);
return output;
})
{
}
}
}