$expected
+ */
+ private function wellKnownHasExpectedNames(array $names, array $expected): bool
+ {
+ foreach ($expected as $local => $hex) {
+ if (!isset($names[$local]) || !hash_equals($hex, $names[$local])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
/**
* Absolute used/budget wall seconds for the comment phase, e.g. "127.4/600 s" (not a percentage).
*/
diff --git a/src/Service/MagazineContentService.php b/src/Service/MagazineContentService.php
index 33a7c6f..4faaa0f 100644
--- a/src/Service/MagazineContentService.php
+++ b/src/Service/MagazineContentService.php
@@ -290,6 +290,9 @@ final class MagazineContentService
{
$n = 0;
foreach ($this->getCategorySlugsFromStore() as $catSlug) {
+ // If a category 30040 wasn't persisted during the refresh phase (relay errors/timeouts),
+ // try one direct fetch here so long-form ingest and reports are not silently incomplete.
+ $this->warmCategoryIndexIfMissing($catSlug);
$all = $this->findAllLongformCoordinatesForCategory($catSlug);
if ($all === []) {
continue;
@@ -331,8 +334,23 @@ final class MagazineContentService
$totResolved = 0;
$totMissing = 0;
foreach ($this->getCategorySlugsFromStore() as $slug) {
+ $this->warmCategoryIndexIfMissing($slug);
$catIndex = $this->store->getCategory($slug);
if ($catIndex === null) {
+ $totMissing++;
+ $categories[] = [
+ 'slug' => $slug,
+ 'title' => $slug,
+ 'event_id' => '',
+ 'listed_total' => 0,
+ 'resolved_total' => 0,
+ 'missing_total' => 1,
+ 'entries' => [[
+ 'coordinate' => 'category:'.$slug,
+ 'status' => 'missing',
+ 'reason' => 'category_index_unavailable',
+ ]],
+ ];
continue;
}
$title = $slug;
diff --git a/src/Service/NostrClient.php b/src/Service/NostrClient.php
index 23c5913..ba7bb65 100644
--- a/src/Service/NostrClient.php
+++ b/src/Service/NostrClient.php
@@ -2847,6 +2847,43 @@ class NostrClient
'group_key' => $gkey,
'authors_filter' => $g['pubkey'],
]);
+ // Some relays fail to satisfy combined authors+#d filters for parameterized replaceables.
+ // Fallback: query by #d only, then enforce author and d-tag match client-side.
+ $fallbackReq = $this->createNostrRequest(
+ [$kindEnum],
+ ['tag' => ['#d', $dTags]],
+ $this->defaultRelaySet,
+ );
+ $fallbackEvents = $this->processResponse(
+ $fallbackReq->send(),
+ static fn (object $event) => $event,
+ );
+ $fallbackMatched = [];
+ $expectedPubkey = strtolower((string) $g['pubkey']);
+ $expectedD = array_fill_keys($dTags, true);
+ foreach ($fallbackEvents as $ev) {
+ if (!\is_object($ev)) {
+ continue;
+ }
+ $evPubkey = strtolower((string) ($ev->pubkey ?? ''));
+ if ($evPubkey !== $expectedPubkey) {
+ continue;
+ }
+ $evD = self::eventDTagValue($ev);
+ if ($evD === null || !isset($expectedD[$evD])) {
+ continue;
+ }
+ $fallbackMatched[] = $ev;
+ }
+ $this->logger->info('[longform_ingest] ingestLongform: fallback #d-only query result', [
+ 'group_key' => $gkey,
+ 'fallback_raw_wire_count' => \count($fallbackEvents),
+ 'fallback_matched_count' => \count($fallbackMatched),
+ ]);
+ if ($fallbackMatched !== []) {
+ $events = $fallbackMatched;
+ $rawCount = \count($events);
+ }
}
$merged = self::mergeNip33ParameterizedWireEvents($events);
$mergedDetail = [];
@@ -2860,13 +2897,43 @@ class NostrClient
'merged_count' => \count($merged),
'one_row_per_nip33_address' => $mergedDetail,
]);
+ $kindInt = (int) $g['kind'];
+ $authorHex = strtolower((string) $g['pubkey']);
+ $expectedAddresses = [];
+ foreach ($dTags as $dt) {
+ $expectedAddresses[$kindInt.':'.$authorHex.':'.$dt] = true;
+ }
+ $seenAddresses = [];
foreach ($merged as $event) {
if (!\is_object($event)) {
continue;
}
+ $addr = self::nip33ParameterizedReplaceableAddress($event);
+ if ($addr !== null) {
+ $seenAddresses[$addr] = true;
+ }
$article = $this->articleFactory->createFromLongFormContentEvent($event);
$this->saveEachArticleToTheDatabase($article);
}
+ foreach (array_keys($expectedAddresses) as $coordinate) {
+ if (isset($seenAddresses[$coordinate])) {
+ continue;
+ }
+ $this->logger->notice('[longform_ingest] ingestLongform: address missing after batch merge; trying author NIP-65 relays', [
+ 'coordinate' => $coordinate,
+ ]);
+ $byCoord = $this->getArticlesByCoordinates([$coordinate]);
+ $evExtra = $byCoord[$coordinate] ?? null;
+ if ($evExtra === null) {
+ $this->logger->warning('[longform_ingest] ingestLongform: still no event for coordinate (not on default or author relays)', [
+ 'coordinate' => $coordinate,
+ ]);
+
+ continue;
+ }
+ $article = $this->articleFactory->createFromLongFormContentEvent($evExtra);
+ $this->saveEachArticleToTheDatabase($article);
+ }
} catch (\Throwable $e) {
$this->logger->error(
sprintf('[longform_ingest] ingestLongform: exception in group %s: %s', (string) $gkey, $e->getMessage()),
diff --git a/templates/components/Molecules/NostrPreviewContent.html.twig b/templates/components/Molecules/NostrPreviewContent.html.twig
index c0b1584..11223c7 100644
--- a/templates/components/Molecules/NostrPreviewContent.html.twig
+++ b/templates/components/Molecules/NostrPreviewContent.html.twig
@@ -1,21 +1,36 @@
{% if preview.type == 'naddr' %}
-
- {% set _na_share = nostr_event_share(preview) %}
+ {% set naddr_title = null %}
+ {% set naddr_summary = null %}
+ {% if preview.tags is defined %}
+ {% for tag in preview.tags %}
+ {% if tag[0] == 'title' and naddr_title is null and tag[1] is defined and tag[1]|default('')|trim != '' %}
+ {% set naddr_title = tag[1] %}
+ {% elseif tag[0] == 'summary' and naddr_summary is null and tag[1] is defined and tag[1]|default('')|trim != '' %}
+ {% set naddr_summary = tag[1] %}
+ {% endif %}
+ {% endfor %}
+ {% endif %}
+ {% set _na_share = nostr_event_share(preview) %}
+
{% elseif preview.type == 'nevent' %}
{% if preview.kind == 9802 %}