Files
zap-stream-api/NostrStreamer/Services/StreamEventBuilder.cs
2024-08-28 10:05:41 +01:00

130 lines
4.1 KiB
C#

using System.ComponentModel;
using Nostr.Client.Client;
using Nostr.Client.Keys;
using Nostr.Client.Messages;
using Nostr.Client.Requests;
using NostrStreamer.Database;
namespace NostrStreamer.Services;
public class StreamEventBuilder
{
private const NostrKind StreamEventKind = (NostrKind)30_311;
private const NostrKind StreamChatKind = (NostrKind)1311;
private readonly Config _config;
private readonly ViewCounter _viewCounter;
private readonly INostrClient _nostrClient;
public StreamEventBuilder(Config config, ViewCounter viewCounter, INostrClient nostrClient)
{
_config = config;
_viewCounter = viewCounter;
_nostrClient = nostrClient;
}
public NostrEvent CreateStreamEvent(UserStream stream)
{
var status = stream.State switch
{
UserStreamState.Planned => "planned",
UserStreamState.Live => "live",
UserStreamState.Ended => "ended",
_ => throw new InvalidEnumArgumentException()
};
var tags = new List<NostrEventTag>
{
new("d", stream.Id.ToString()),
new("title", stream.Title ?? ""),
new("summary", stream.Summary ?? ""),
new("image", stream.Thumbnail ?? stream.Image ?? ""),
new("status", status),
new("p", stream.PubKey, "wss://relay.zap.stream", "host"),
new("relays", _config.Relays),
new("starts", new DateTimeOffset(stream.Starts).ToUnixTimeSeconds().ToString()),
new("service", new Uri(_config.ApiHost, "/api/nostr").ToString())
};
if (status == "live")
{
var viewers = _viewCounter.Current(stream.Id);
tags.Add(new("streaming", new Uri(_config.DataHost, $"stream/{stream.Id}.m3u8").ToString()));
tags.Add(new("current_participants", viewers.ToString()));
if (!string.IsNullOrEmpty(stream.ContentWarning))
{
tags.Add(new("content-warning", stream.ContentWarning));
}
}
else if (status == "ended")
{
if (stream.Endpoint.Capabilities
.Any(a => a.StartsWith("dvr:", StringComparison.InvariantCultureIgnoreCase)))
{
tags.Add(new("recording", new Uri(_config.DataHost, $"recording/{stream.Id}.m3u8").ToString()));
}
if (stream.Ends.HasValue)
{
tags.Add(new("ends", new DateTimeOffset(stream.Ends.Value).ToUnixTimeSeconds().ToString()));
}
}
foreach (var tag in stream.SplitTags())
{
tags.Add(new("t", tag));
}
if (!string.IsNullOrEmpty(stream.Goal))
{
tags.Add(new("goal", stream.Goal));
}
var ev = new NostrEvent
{
Kind = StreamEventKind,
Content = "",
CreatedAt = DateTime.Now,
Tags = new NostrEventTags(tags)
};
return ev.Sign(NostrPrivateKey.FromBech32(_config.PrivateKey));
}
public NostrEvent CreateStreamChat(UserStream stream, string message)
{
var pk = NostrPrivateKey.FromBech32(_config.PrivateKey);
var ev = new NostrEvent
{
Kind = StreamChatKind,
Content = message,
CreatedAt = DateTime.Now,
Tags = new NostrEventTags(
new NostrEventTag("a", $"{(int)StreamEventKind}:{pk.DerivePublicKey().Hex}:{stream.Id}")
)
};
return ev.Sign(pk);
}
public NostrEvent CreateDm(UserStream stream, string message)
{
var pk = NostrPrivateKey.FromBech32(_config.PrivateKey);
var ev = new NostrEvent
{
Kind = NostrKind.EncryptedDm,
Content = message,
CreatedAt = DateTime.Now,
Tags = new NostrEventTags(
new NostrEventTag("p", stream.PubKey)
)
};
return ev.EncryptDirect(pk, NostrPublicKey.FromHex(stream.PubKey)).Sign(pk);
}
public void BroadcastEvent(NostrEvent ev)
{
_nostrClient.Send(new NostrEventRequest(ev));
}
}