feat: resolve nip5 for OG tags
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
using AngleSharp;
|
using AngleSharp;
|
||||||
using AngleSharp.Dom;
|
using AngleSharp.Dom;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Nostr.Client.Identifiers;
|
using Nostr.Client.Identifiers;
|
||||||
using Nostr.Client.Messages;
|
using Nostr.Client.Messages;
|
||||||
using Nostr.Client.Utils;
|
using Nostr.Client.Utils;
|
||||||
@ -17,11 +18,14 @@ public class OpenGraphController : Controller
|
|||||||
{
|
{
|
||||||
private readonly ILogger<OpenGraphController> _logger;
|
private readonly ILogger<OpenGraphController> _logger;
|
||||||
private readonly RedisStore _redisStore;
|
private readonly RedisStore _redisStore;
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
|
||||||
public OpenGraphController(ILogger<OpenGraphController> logger, RedisStore redisStore)
|
public OpenGraphController(ILogger<OpenGraphController> logger, RedisStore redisStore, HttpClient httpClient)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_redisStore = redisStore;
|
_redisStore = redisStore;
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_httpClient.Timeout = TimeSpan.FromSeconds(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -51,11 +55,13 @@ public class OpenGraphController : Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NostrIdentifierParser.TryParse(id, out var nid) || (nid = NostrBareIdentifier.Parse(id)) != default)
|
if ((NostrIdentifierParser.TryParse(id, out var nid) && nid != default) ||
|
||||||
|
(nid = NostrBareIdentifier.Parse(id)) != default ||
|
||||||
|
(nid = await TryParseNip05(id, cts)) != default)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (nid!.Hrp is "nevent" or "note" or "naddr")
|
if (nid.Hrp is "nevent" or "note" or "naddr")
|
||||||
{
|
{
|
||||||
var ev = await _redisStore.GetEvent(nid);
|
var ev = await _redisStore.GetEvent(nid);
|
||||||
if (ev != default)
|
if (ev != default)
|
||||||
@ -105,7 +111,7 @@ public class OpenGraphController : Controller
|
|||||||
{
|
{
|
||||||
case (long)NostrKind.LiveEvent:
|
case (long)NostrKind.LiveEvent:
|
||||||
{
|
{
|
||||||
var host = ev.Tags.FirstOrDefault(a => a is {Key: "p", Values: [_, _, "host", ..]})?.Values[1] ?? ev.PubKey.ToHex();
|
var host = ev.Tags.FirstOrDefault(a => a is {Key: "p", Values: [_, _, "host"]})?.Values[1] ?? ev.PubKey.ToHex();
|
||||||
var hostProfile = await _redisStore.GetProfile(host);
|
var hostProfile = await _redisStore.GetProfile(host);
|
||||||
var hostName = hostProfile?.Name ?? profile?.Name ?? "Nostrich";
|
var hostName = hostProfile?.Name ?? profile?.Name ?? "Nostrich";
|
||||||
var stream = ev.GetFirstTagValue("streaming") ?? ev.GetFirstTagValue("recording") ?? "";
|
var stream = ev.GetFirstTagValue("streaming") ?? ev.GetFirstTagValue("recording") ?? "";
|
||||||
@ -164,7 +170,7 @@ public class OpenGraphController : Controller
|
|||||||
|
|
||||||
private async Task<CachedMeta?> GetProfileMeta(CompactProfile? profile)
|
private async Task<CachedMeta?> GetProfileMeta(CompactProfile? profile)
|
||||||
{
|
{
|
||||||
var titleContent = $"Snort - {profile?.Name ?? "Nostrich"}'s Profile";
|
var titleContent = $"{profile?.Name ?? "Nostrich"}'s Profile";
|
||||||
var aboutContent = profile?.About?.Length > 160 ? profile.About[..160] : profile?.About ?? "";
|
var aboutContent = profile?.About?.Length > 160 ? profile.About[..160] : profile?.About ?? "";
|
||||||
var imageContent = profile?.Picture ?? "https://snort.social/nostrich_512.png";
|
var imageContent = profile?.Picture ?? "https://snort.social/nostrich_512.png";
|
||||||
return new CachedMeta
|
return new CachedMeta
|
||||||
@ -211,6 +217,37 @@ public class OpenGraphController : Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
record HeadElement(string Element, List<KeyValuePair<string, string>> Attributes);
|
record HeadElement(string Element, List<KeyValuePair<string, string>> Attributes);
|
||||||
|
|
||||||
|
private async Task<NostrIdentifier?> TryParseNip05(string id, CancellationToken cts)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (id.Contains("@"))
|
||||||
|
{
|
||||||
|
var idSplit = id.Split("@");
|
||||||
|
var url = new Uri($"https://{idSplit[1]}/.well-known/nostr.json?name={Uri.EscapeDataString(idSplit[0])}");
|
||||||
|
var json = await _httpClient.GetStringAsync(url, cts);
|
||||||
|
var parsed = JsonConvert.DeserializeObject<NostrJson>(json);
|
||||||
|
var match = parsed?.Names?.FirstOrDefault(a => a.Key.Equals(idSplit[0], StringComparison.CurrentCultureIgnoreCase));
|
||||||
|
if (match.HasValue && !string.IsNullOrEmpty(match.Value.Value))
|
||||||
|
{
|
||||||
|
return new NostrProfileIdentifier(match.Value.Value.ToLower(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Failed to parse nostr address {id} {msg}", id, ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
class NostrJson
|
||||||
|
{
|
||||||
|
[JsonProperty("names")]
|
||||||
|
public Dictionary<string, string>? Names { get; init; } = new();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[ProtoContract]
|
[ProtoContract]
|
||||||
|
Reference in New Issue
Block a user