mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-08 23:54:39 +00:00
feat(quartz,cli): add KindNames registry + amy kind
Introduce a canonical, i18n-free event-kind registry in quartz:
`com.vitorpamplona.quartz.kinds.KindNames` maps each of the 145 known
kinds to an English label + the defining NIP. The data is ported from
Amethyst's relay-view `kindDisplayName` mapping (the NIP derived from
each event class's package), so the "what is kind N" knowledge now lives
once in quartz instead of only in the Android UI.
`amy kind <N|NAME>` looks a kind up by number (label + NIP) or searches
labels by name — a thin wrapper over KindNames, dispatched statelessly
(no account/network).
i18n split: quartz holds the canonical English (quartz is intentionally
translation-free); localized front ends overlay their own strings and can
fall back to KindNames.nameFor() for kinds they don't translate. amy
prints the English directly.
Verified: kind 1/0/30023/1059/24133 labels+NIPs, name search ("podcast"
→ 4), unknown → known:false, text + JSON output.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011SapGdtAc1j7woifoCZ9fY
This commit is contained in:
@@ -217,6 +217,7 @@ Army-knife verbs that operate purely on their arguments. They never touch
|
||||
| `amy key decrypt NCRYPTSEC --password X` | NIP-49 decrypt back to nsec/hex/npub. |
|
||||
| `amy filter [filter flags]` | Assemble and print a NIP-01 filter JSON from the same flags `fetch`/`subscribe` use — no query is sent. |
|
||||
| `amy nip N` / `amy nip list` | Look up a NIP — the `nostr-protocol/nips` repo first, then a Nostr wiki/long-form fallback. `list` fetches the index. |
|
||||
| `amy kind N` / `amy kind NAME` | Look up an event kind's label + defining NIP (number), or search labels by name. Backed by quartz's `KindNames` registry. |
|
||||
| `amy relay info URL` | Fetch and print a relay's NIP-11 information document. |
|
||||
|
||||
### Remote signing (NIP-46 bunker)
|
||||
|
||||
+5
-6
@@ -94,7 +94,7 @@ vs streaming `subscribe`). Stateless verbs run with no account or network.
|
||||
| `filter` | `amy filter` | ✅ | stateless — assemble + print a filter JSON. |
|
||||
| `blossom` | `amy blossom` | ✅ | upload/download/list/delete/check/mirror (reuses commons `BlossomClient`). |
|
||||
| `nip` | `amy nip` | ✅ | repo-first lookup + Nostr wiki/long-form fallback; `nip list`. |
|
||||
| `kind` | `amy kind` | 🆕 | needs a kind→schema registry (nak embeds one); none in quartz today. |
|
||||
| `kind` | `amy kind` | ✅ | quartz `KindNames` registry (kind → English label + NIP), ported from the Android relay view; number lookup + name search. |
|
||||
| `sync` | `amy sync` | ✅ | NIP-77 Negentropy reconcile with the local store (down/up/both). |
|
||||
| `git` | `amy git` | ✅ in part | NIP-34 repo announce/list/show/issue. clone/push (packfile transport) out of scope. |
|
||||
| `podcast` | `amy podcast` | ✅ | NIP-F4 show metadata (10154) + episode publish (54) + list. |
|
||||
@@ -105,15 +105,15 @@ vs streaming `subscribe`). Stateless verbs run with no account or network.
|
||||
|
||||
nak has 34 functional commands. Coverage:
|
||||
|
||||
- **Full / equivalent (18):** `event`, `req`(→`fetch`+`subscribe`), `filter`,
|
||||
- **Full / equivalent (19):** `event`, `req`(→`fetch`+`subscribe`), `filter`,
|
||||
`count`, `decode`, `encode`, `verify`, `relay`, `bunker`(+nostrconnect+auth_url),
|
||||
`encrypt`, `decrypt`, `gift`, `publish`, `sync`, `profile`, `podcast`, `nip`,
|
||||
`blossom`. Protocol-sensitive ones (`bunker`, `sync`, `key` NIP-49,
|
||||
`kind`, `blossom`. Protocol-sensitive ones (`bunker`, `sync`, `key` NIP-49,
|
||||
`encode`/`decode`) are interop-verified against the real `nak` binary.
|
||||
- **Partial / adapted (4):** `key` (no `expand`/`combine`/`validate`/`default`),
|
||||
`git` (NIP-34 events only — no packfile transport), `outbox` (shows NIP-65 vs
|
||||
nak's hints DB), `fetch` (filter-based, not nip19-hint resolution).
|
||||
- **Missing (12):** `kind` (needs a kind-schema registry), `admin` (NIP-86),
|
||||
- **Missing (11):** `admin` (NIP-86),
|
||||
`serve` (geode is a standalone relay), `dekey` (NIP-4E), `wallet` (NIP-60
|
||||
Cashu), `mcp`, `curl` (NIP-98), `fs` (FUSE), `group`/`nip29` (NIP-29 — amy has
|
||||
MLS/Marmot instead), `spell` (MuSig2/FROST), `validate` (RoK schema).
|
||||
@@ -125,8 +125,7 @@ a large surface nak lacks: Marmot/MLS, NIP-17 DMs, zaps, CLINK offer/debit,
|
||||
NIP-02 follow, NIP-50 search, napplets, profile edit, store management, account
|
||||
management.
|
||||
|
||||
**Cheap remaining wins:** `kind` (needs a registry), the `key`
|
||||
`expand`/`combine`/`validate`/`default` sub-verbs.
|
||||
**Cheap remaining wins:** the `key` `expand`/`combine`/`validate`/`default` sub-verbs.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ import com.vitorpamplona.amethyst.cli.commands.GroupCommands
|
||||
import com.vitorpamplona.amethyst.cli.commands.InitCommands
|
||||
import com.vitorpamplona.amethyst.cli.commands.KeyCommands
|
||||
import com.vitorpamplona.amethyst.cli.commands.KeyPackageCommands
|
||||
import com.vitorpamplona.amethyst.cli.commands.KindCommand
|
||||
import com.vitorpamplona.amethyst.cli.commands.LoginCommand
|
||||
import com.vitorpamplona.amethyst.cli.commands.MarmotResetCommand
|
||||
import com.vitorpamplona.amethyst.cli.commands.MessageCommands
|
||||
@@ -172,6 +173,7 @@ private suspend fun dispatch(argv: Array<String>): Int {
|
||||
"key" -> return KeyCommands.dispatch(tail)
|
||||
"filter" -> return FilterCommand.run(tail)
|
||||
"nip" -> return NipCommand.run(tail)
|
||||
"kind" -> return KindCommand.run(tail)
|
||||
}
|
||||
|
||||
// `relay info URL` is a stateless NIP-11 fetch — no account needed. The
|
||||
@@ -350,6 +352,7 @@ private fun printUsage() {
|
||||
| [--id …] [--tag …] … same flags fetch/subscribe use (no query sent)
|
||||
| nip N show a NIP (repo first, then a Nostr wiki/long-form fallback)
|
||||
| nip list fetch the NIP index (README) from the repo
|
||||
| kind N|NAME look up an event kind's label + NIP (number, or search by name)
|
||||
|
|
||||
|Identity:
|
||||
| init [--nsec NSEC] create or import a bare identity (no defaults published)
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.cli.commands
|
||||
|
||||
import com.vitorpamplona.amethyst.cli.Args
|
||||
import com.vitorpamplona.amethyst.cli.Output
|
||||
import com.vitorpamplona.quartz.kinds.KindNames
|
||||
|
||||
/**
|
||||
* `amy kind <N|name>` — look up a Nostr event kind (nak's `kind`). Local,
|
||||
* accountless. A number prints that kind's label + defining NIP; a text query
|
||||
* searches labels.
|
||||
*
|
||||
* kind 1 -> {"kind":1,"name":"Short Text Note","nip":"10",...}
|
||||
* kind "text note" -> {"query":…,"matches":[…]}
|
||||
*
|
||||
* Thin assembly only: the canonical kind registry lives in quartz
|
||||
* (`KindNames`) — the same data the Android relay view localizes on top of.
|
||||
*/
|
||||
object KindCommand {
|
||||
fun run(rest: Array<String>): Int {
|
||||
val args = Args(rest)
|
||||
val arg = args.positional(0, "kind-number-or-name").trim()
|
||||
val n = arg.toIntOrNull()
|
||||
if (n != null) {
|
||||
val info = KindNames.infoFor(n)
|
||||
Output.emit(
|
||||
mapOf(
|
||||
"kind" to n,
|
||||
"name" to info?.name,
|
||||
"nip" to info?.nip,
|
||||
"known" to (info != null),
|
||||
),
|
||||
)
|
||||
return 0
|
||||
}
|
||||
val matches = KindNames.search(arg)
|
||||
Output.emit(
|
||||
mapOf(
|
||||
"query" to arg,
|
||||
"count" to matches.size,
|
||||
"matches" to matches.map { (kind, info) -> mapOf("kind" to kind, "name" to info.name, "nip" to info.nip) },
|
||||
),
|
||||
)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.quartz.kinds
|
||||
|
||||
import com.vitorpamplona.quartz.experimental.attestations.attestation.AttestationEvent
|
||||
import com.vitorpamplona.quartz.experimental.attestations.proficiency.AttestorProficiencyEvent
|
||||
import com.vitorpamplona.quartz.experimental.attestations.recommendation.AttestorRecommendationEvent
|
||||
import com.vitorpamplona.quartz.experimental.attestations.request.AttestationRequestEvent
|
||||
import com.vitorpamplona.quartz.experimental.audio.header.AudioHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.audio.track.AudioTrackEvent
|
||||
import com.vitorpamplona.quartz.experimental.edits.TextNoteModificationEvent
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.chat.EphemeralChatEvent
|
||||
import com.vitorpamplona.quartz.experimental.ephemChat.list.EphemeralChatListEvent
|
||||
import com.vitorpamplona.quartz.experimental.fitness.workout.WorkoutRecordEvent
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryPrologueEvent
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStoryReadingStateEvent
|
||||
import com.vitorpamplona.quartz.experimental.interactiveStories.InteractiveStorySceneEvent
|
||||
import com.vitorpamplona.quartz.experimental.medical.FhirResourceEvent
|
||||
import com.vitorpamplona.quartz.experimental.music.playlist.MusicPlaylistEvent
|
||||
import com.vitorpamplona.quartz.experimental.music.track.MusicTrackEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.data.FileStorageEvent
|
||||
import com.vitorpamplona.quartz.experimental.nip95.header.FileStorageHeaderEvent
|
||||
import com.vitorpamplona.quartz.experimental.nipA3.PaymentTargetsEvent
|
||||
import com.vitorpamplona.quartz.experimental.nipsOnNostr.NipTextEvent
|
||||
import com.vitorpamplona.quartz.experimental.nns.NNSEvent
|
||||
import com.vitorpamplona.quartz.experimental.notifications.wake.WakeUpEvent
|
||||
import com.vitorpamplona.quartz.experimental.profileGallery.ProfileGalleryEntryEvent
|
||||
import com.vitorpamplona.quartz.experimental.zapPolls.ZapPollEvent
|
||||
import com.vitorpamplona.quartz.nip01Core.metadata.MetadataEvent
|
||||
import com.vitorpamplona.quartz.nip02FollowList.ContactListEvent
|
||||
import com.vitorpamplona.quartz.nip03Timestamp.OtsEvent
|
||||
import com.vitorpamplona.quartz.nip04Dm.messages.PrivateDmEvent
|
||||
import com.vitorpamplona.quartz.nip09Deletions.DeletionEvent
|
||||
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.files.ChatMessageEncryptedFileHeaderEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.messages.ChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip17Dm.settings.ChatMessageRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip18Reposts.GenericRepostEvent
|
||||
import com.vitorpamplona.quartz.nip18Reposts.RepostEvent
|
||||
import com.vitorpamplona.quartz.nip22Comments.CommentEvent
|
||||
import com.vitorpamplona.quartz.nip23LongContent.LongTextNoteEvent
|
||||
import com.vitorpamplona.quartz.nip25Reactions.ReactionEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelCreateEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelHideMessageEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMetadataEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.admin.ChannelMuteUserEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.list.ChannelListEvent
|
||||
import com.vitorpamplona.quartz.nip28PublicChat.message.ChannelMessageEvent
|
||||
import com.vitorpamplona.quartz.nip30CustomEmoji.pack.EmojiPackEvent
|
||||
import com.vitorpamplona.quartz.nip30CustomEmoji.selection.EmojiPackSelectionEvent
|
||||
import com.vitorpamplona.quartz.nip34Git.issue.GitIssueEvent
|
||||
import com.vitorpamplona.quartz.nip34Git.patch.GitPatchEvent
|
||||
import com.vitorpamplona.quartz.nip34Git.reply.GitReplyEvent
|
||||
import com.vitorpamplona.quartz.nip34Git.repository.GitRepositoryEvent
|
||||
import com.vitorpamplona.quartz.nip35Torrents.TorrentCommentEvent
|
||||
import com.vitorpamplona.quartz.nip35Torrents.TorrentEvent
|
||||
import com.vitorpamplona.quartz.nip37Drafts.DraftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip37Drafts.privateOutbox.PrivateOutboxRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip38UserStatus.StatusEvent
|
||||
import com.vitorpamplona.quartz.nip42RelayAuth.RelayAuthEvent
|
||||
import com.vitorpamplona.quartz.nip46RemoteSigner.NostrConnectEvent
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.events.LnZapPaymentRequestEvent
|
||||
import com.vitorpamplona.quartz.nip47WalletConnect.events.LnZapPaymentResponseEvent
|
||||
import com.vitorpamplona.quartz.nip50Search.SearchRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.PinListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.BookmarkListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.bookmarkList.OldBookmarkListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.followList.FollowListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.geohashList.GeohashListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.hashtagList.HashtagListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.labeledBookmarkList.LabeledBookmarkListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.muteList.MuteListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.peopleList.PeopleListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.BlockedRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.BroadcastRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.IndexerRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.ProxyRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.RelayFeedsListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relayLists.TrustedRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip51Lists.relaySets.RelaySetEvent
|
||||
import com.vitorpamplona.quartz.nip52Calendar.appt.day.CalendarDateSlotEvent
|
||||
import com.vitorpamplona.quartz.nip52Calendar.appt.time.CalendarTimeSlotEvent
|
||||
import com.vitorpamplona.quartz.nip52Calendar.calendar.CalendarEvent
|
||||
import com.vitorpamplona.quartz.nip52Calendar.rsvp.CalendarRSVPEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.chat.LiveActivitiesChatMessageEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingRoomEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.meetingSpaces.MeetingSpaceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.nestsServers.NestsServersEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.presence.MeetingRoomPresenceEvent
|
||||
import com.vitorpamplona.quartz.nip53LiveActivities.streaming.LiveActivitiesEvent
|
||||
import com.vitorpamplona.quartz.nip54Wiki.WikiNoteEvent
|
||||
import com.vitorpamplona.quartz.nip56Reports.ReportEvent
|
||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
|
||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapPrivateEvent
|
||||
import com.vitorpamplona.quartz.nip57Zaps.LnZapRequestEvent
|
||||
import com.vitorpamplona.quartz.nip58Badges.accepted.AcceptedBadgeSetEvent
|
||||
import com.vitorpamplona.quartz.nip58Badges.award.BadgeAwardEvent
|
||||
import com.vitorpamplona.quartz.nip58Badges.definition.BadgeDefinitionEvent
|
||||
import com.vitorpamplona.quartz.nip58Badges.profile.ProfileBadgesEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.seals.SealedRumorEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.EphemeralGiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
|
||||
import com.vitorpamplona.quartz.nip62RequestToVanish.RequestToVanishEvent
|
||||
import com.vitorpamplona.quartz.nip64Chess.challenge.accept.LiveChessGameAcceptEvent
|
||||
import com.vitorpamplona.quartz.nip64Chess.challenge.offer.LiveChessGameChallengeEvent
|
||||
import com.vitorpamplona.quartz.nip64Chess.draw.LiveChessDrawOfferEvent
|
||||
import com.vitorpamplona.quartz.nip64Chess.end.LiveChessGameEndEvent
|
||||
import com.vitorpamplona.quartz.nip64Chess.game.ChessGameEvent
|
||||
import com.vitorpamplona.quartz.nip64Chess.jester.JesterEvent
|
||||
import com.vitorpamplona.quartz.nip64Chess.move.LiveChessMoveEvent
|
||||
import com.vitorpamplona.quartz.nip65RelayList.AdvertisedRelayListEvent
|
||||
import com.vitorpamplona.quartz.nip66RelayMonitor.discovery.RelayDiscoveryEvent
|
||||
import com.vitorpamplona.quartz.nip66RelayMonitor.monitor.RelayMonitorEvent
|
||||
import com.vitorpamplona.quartz.nip68Picture.PictureEvent
|
||||
import com.vitorpamplona.quartz.nip71Video.VideoHorizontalEvent
|
||||
import com.vitorpamplona.quartz.nip71Video.VideoNormalEvent
|
||||
import com.vitorpamplona.quartz.nip71Video.VideoShortEvent
|
||||
import com.vitorpamplona.quartz.nip71Video.VideoVerticalEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.approval.CommunityPostApprovalEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.definition.CommunityDefinitionEvent
|
||||
import com.vitorpamplona.quartz.nip72ModCommunities.follow.CommunityListEvent
|
||||
import com.vitorpamplona.quartz.nip75ZapGoals.GoalEvent
|
||||
import com.vitorpamplona.quartz.nip78AppData.AppSpecificDataEvent
|
||||
import com.vitorpamplona.quartz.nip84Highlights.HighlightEvent
|
||||
import com.vitorpamplona.quartz.nip85TrustedAssertions.list.TrustProviderListEvent
|
||||
import com.vitorpamplona.quartz.nip85TrustedAssertions.users.ContactCardEvent
|
||||
import com.vitorpamplona.quartz.nip88Polls.poll.PollEvent
|
||||
import com.vitorpamplona.quartz.nip88Polls.response.PollResponseEvent
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
|
||||
import com.vitorpamplona.quartz.nip89AppHandlers.recommendation.AppRecommendationEvent
|
||||
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryRequest.NIP90ContentDiscoveryRequestEvent
|
||||
import com.vitorpamplona.quartz.nip90Dvms.contentDiscoveryResponse.NIP90ContentDiscoveryResponseEvent
|
||||
import com.vitorpamplona.quartz.nip90Dvms.status.NIP90StatusEvent
|
||||
import com.vitorpamplona.quartz.nip90Dvms.userDiscoveryRequest.NIP90UserDiscoveryRequestEvent
|
||||
import com.vitorpamplona.quartz.nip90Dvms.userDiscoveryResponse.NIP90UserDiscoveryResponseEvent
|
||||
import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent
|
||||
import com.vitorpamplona.quartz.nip96FileStorage.config.FileServersEvent
|
||||
import com.vitorpamplona.quartz.nip98HttpAuth.HTTPAuthorizationEvent
|
||||
import com.vitorpamplona.quartz.nip99Classifieds.ClassifiedsEvent
|
||||
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceEvent
|
||||
import com.vitorpamplona.quartz.nipA0VoiceMessages.VoiceReplyEvent
|
||||
import com.vitorpamplona.quartz.nipA4PublicMessages.PublicMessageEvent
|
||||
import com.vitorpamplona.quartz.nipB0WebBookmarks.WebBookmarkEvent
|
||||
import com.vitorpamplona.quartz.nipB7Blossom.BlossomAuthorizationEvent
|
||||
import com.vitorpamplona.quartz.nipB7Blossom.BlossomServersEvent
|
||||
import com.vitorpamplona.quartz.nipF4Podcasts.authored.AuthoredPodcastsEvent
|
||||
import com.vitorpamplona.quartz.nipF4Podcasts.episode.PodcastEpisodeEvent
|
||||
import com.vitorpamplona.quartz.nipF4Podcasts.favorites.FavoritePodcastsListEvent
|
||||
import com.vitorpamplona.quartz.nipF4Podcasts.metadata.PodcastMetadataEvent
|
||||
|
||||
/**
|
||||
* Human-readable label and defining NIP for a Nostr event kind.
|
||||
*/
|
||||
data class KindName(
|
||||
val name: String,
|
||||
val nip: String?,
|
||||
)
|
||||
|
||||
/**
|
||||
* Canonical, **i18n-free** registry mapping event-kind numbers to an English
|
||||
* label and the NIP that defines them. This is the single source of truth for
|
||||
* "what is kind N" across the project:
|
||||
*
|
||||
* - headless consumers (the `amy` CLI) print [nameFor] directly;
|
||||
* - localized front ends (the Android app) overlay their own translated
|
||||
* strings on top and fall back to [nameFor] for kinds they don't translate.
|
||||
*
|
||||
* Keep this list as the place new kinds are registered; translations are a
|
||||
* platform concern layered on top, never a fork of this data.
|
||||
*/
|
||||
object KindNames {
|
||||
val names: Map<Int, KindName> =
|
||||
mapOf(
|
||||
AcceptedBadgeSetEvent.KIND to KindName("Accepted Badge Set", "58"),
|
||||
AdvertisedRelayListEvent.KIND to KindName("Outbox Relays", "65"),
|
||||
AppDefinitionEvent.KIND to KindName("Apps", "89"),
|
||||
AppRecommendationEvent.KIND to KindName("App Recommendations", "89"),
|
||||
AppSpecificDataEvent.KIND to KindName("User Settings", "78"),
|
||||
AudioHeaderEvent.KIND to KindName("Audio Header", null),
|
||||
AudioTrackEvent.KIND to KindName("Audio Track", null),
|
||||
MusicTrackEvent.KIND to KindName("Music Track", null),
|
||||
MusicPlaylistEvent.KIND to KindName("Music Playlist", null),
|
||||
PodcastEpisodeEvent.KIND to KindName("Podcast Episode", "F4"),
|
||||
PodcastMetadataEvent.KIND to KindName("Podcast Show", "F4"),
|
||||
AuthoredPodcastsEvent.KIND to KindName("Authored Podcasts", "F4"),
|
||||
FavoritePodcastsListEvent.KIND to KindName("Favorite Podcasts", "F4"),
|
||||
AttestationEvent.KIND to KindName("Attestation", null),
|
||||
AttestationRequestEvent.KIND to KindName("Attestation Request", null),
|
||||
AttestorRecommendationEvent.KIND to KindName("Attestor Recommendation", null),
|
||||
AttestorProficiencyEvent.KIND to KindName("Attestor Proficiency", null),
|
||||
BadgeAwardEvent.KIND to KindName("Badge Awards", "58"),
|
||||
BadgeDefinitionEvent.KIND to KindName("Badge Definitions", "58"),
|
||||
BlockedRelayListEvent.KIND to KindName("Blocked Relays", "51"),
|
||||
BlossomServersEvent.KIND to KindName("Blossom Servers", "B7"),
|
||||
NestsServersEvent.KIND to KindName("Nests Servers", "53"),
|
||||
BlossomAuthorizationEvent.KIND to KindName("Blossom Auth", "B7"),
|
||||
BroadcastRelayListEvent.KIND to KindName("Broadcast Relays", "51"),
|
||||
BookmarkListEvent.KIND to KindName("Bookmark List", "51"),
|
||||
OldBookmarkListEvent.KIND to KindName("Old Bookmark List", "51"),
|
||||
CalendarDateSlotEvent.KIND to KindName("Day Appointment", "52"),
|
||||
CalendarEvent.KIND to KindName("Calendar", "52"),
|
||||
CalendarTimeSlotEvent.KIND to KindName("Appointment", "52"),
|
||||
CalendarRSVPEvent.KIND to KindName("Appt RSVP", "52"),
|
||||
ChessGameEvent.KIND to KindName("Chess Games", "64"),
|
||||
JesterEvent.KIND to KindName("Chess Auth", "64"),
|
||||
RelayFeedsListEvent.KIND to KindName("Favorite Relays", "51"),
|
||||
LiveChessGameChallengeEvent.KIND to KindName("Chess Challenges", "64"),
|
||||
LiveChessGameAcceptEvent.KIND to KindName("Chess Game Accept", "64"),
|
||||
LiveChessMoveEvent.KIND to KindName("Chess Move", "64"),
|
||||
LiveChessGameEndEvent.KIND to KindName("Chess Game End", "64"),
|
||||
LiveChessDrawOfferEvent.KIND to KindName("Chess Draw Offer", "64"),
|
||||
ChannelCreateEvent.KIND to KindName("Channel Definition", "28"),
|
||||
ChannelHideMessageEvent.KIND to KindName("Channel Hide Msg", "28"),
|
||||
ChannelListEvent.KIND to KindName("Channel List", "28"),
|
||||
ChannelMessageEvent.KIND to KindName("Channel Message", "28"),
|
||||
ChannelMetadataEvent.KIND to KindName("Channel Metadata", "28"),
|
||||
ChannelMuteUserEvent.KIND to KindName("Channel Mute User", "28"),
|
||||
ChatMessageEncryptedFileHeaderEvent.KIND to KindName("DM File", "17"),
|
||||
ChatMessageEvent.KIND to KindName("DM Message", "17"),
|
||||
ChatMessageRelayListEvent.KIND to KindName("DM Relays", "17"),
|
||||
ClassifiedsEvent.KIND to KindName("Classifieds", "99"),
|
||||
CommentEvent.KIND to KindName("Comments", "22"),
|
||||
CommunityDefinitionEvent.KIND to KindName("Community Def", "72"),
|
||||
CommunityListEvent.KIND to KindName("Community List", "72"),
|
||||
CommunityPostApprovalEvent.KIND to KindName("Community Post", "72"),
|
||||
ContactListEvent.KIND to KindName("Follow List", "02"),
|
||||
DeletionEvent.KIND to KindName("Deletions", "09"),
|
||||
DraftWrapEvent.KIND to KindName("Drafts", "37"),
|
||||
EmojiPackEvent.KIND to KindName("Emoji Packs", "30"),
|
||||
EmojiPackSelectionEvent.KIND to KindName("Emoji Pack List", "30"),
|
||||
EphemeralChatEvent.KIND to KindName("Ephemeral Chat", null),
|
||||
EphemeralChatListEvent.KIND to KindName("Ephemeral Chatrooms", null),
|
||||
FileHeaderEvent.KIND to KindName("File Headers", "94"),
|
||||
ProfileGalleryEntryEvent.KIND to KindName("Profile Gallery", null),
|
||||
FileServersEvent.KIND to KindName("File Servers", "96"),
|
||||
FileStorageEvent.KIND to KindName("Blob Data", null),
|
||||
FileStorageHeaderEvent.KIND to KindName("Blob Headers", null),
|
||||
FhirResourceEvent.KIND to KindName("Medical Data", null),
|
||||
FollowListEvent.KIND to KindName("Follow Packs", "51"),
|
||||
GenericRepostEvent.KIND to KindName("Reposts (16)", "18"),
|
||||
GeohashListEvent.KIND to KindName("Geohash Follows", "51"),
|
||||
GiftWrapEvent.KIND to KindName("GiftWraps", "59"),
|
||||
EphemeralGiftWrapEvent.KIND to KindName("GiftWraps", "59"),
|
||||
GitIssueEvent.KIND to KindName("Git Issue", "34"),
|
||||
GitPatchEvent.KIND to KindName("Git Patch", "34"),
|
||||
GitRepositoryEvent.KIND to KindName("Git Repo", "34"),
|
||||
GitReplyEvent.KIND to KindName("Git Reply", "34"),
|
||||
GoalEvent.KIND to KindName("Zap Goals", "75"),
|
||||
HashtagListEvent.KIND to KindName("Hashtag Follows", "51"),
|
||||
HighlightEvent.KIND to KindName("Highlights", "84"),
|
||||
HTTPAuthorizationEvent.KIND to KindName("Http Auth", "98"),
|
||||
IndexerRelayListEvent.KIND to KindName("Index Relay List", "51"),
|
||||
InteractiveStoryPrologueEvent.KIND to KindName("Adventure Prologue", null),
|
||||
InteractiveStorySceneEvent.KIND to KindName("Adventure Scene", null),
|
||||
InteractiveStoryReadingStateEvent.KIND to KindName("Adventure Reading", null),
|
||||
LabeledBookmarkListEvent.KIND to KindName("Named Bookmarks", "51"),
|
||||
LiveActivitiesChatMessageEvent.KIND to KindName("Live Chats", "53"),
|
||||
LiveActivitiesEvent.KIND to KindName("Live Streams", "53"),
|
||||
LnZapEvent.KIND to KindName("Zaps", "57"),
|
||||
LnZapPaymentRequestEvent.KIND to KindName("NWC Request", "47"),
|
||||
LnZapPaymentResponseEvent.KIND to KindName("NWC Response", "47"),
|
||||
LnZapPrivateEvent.KIND to KindName("Private Zaps", "57"),
|
||||
LnZapRequestEvent.KIND to KindName("Zap Req", "57"),
|
||||
LongTextNoteEvent.KIND to KindName("Blogs", "23"),
|
||||
MeetingRoomEvent.KIND to KindName("Meeting Room", "53"),
|
||||
MeetingRoomPresenceEvent.KIND to KindName("Room Presence", "53"),
|
||||
MeetingSpaceEvent.KIND to KindName("Meeting Space", "53"),
|
||||
MetadataEvent.KIND to KindName("Profile", "01"),
|
||||
MuteListEvent.KIND to KindName("Mute List", "51"),
|
||||
NNSEvent.KIND to KindName("NNS", null),
|
||||
NipTextEvent.KIND to KindName("NIP", null),
|
||||
NostrConnectEvent.KIND to KindName("Nostr Connect", "46"),
|
||||
NIP90StatusEvent.KIND to KindName("DVM Status", "90"),
|
||||
NIP90ContentDiscoveryRequestEvent.KIND to KindName("DVM Content Req", "90"),
|
||||
NIP90ContentDiscoveryResponseEvent.KIND to KindName("DVM Content Resp", "90"),
|
||||
NIP90UserDiscoveryRequestEvent.KIND to KindName("DVM User Req", "90"),
|
||||
NIP90UserDiscoveryResponseEvent.KIND to KindName("DVM User Resp", "90"),
|
||||
OtsEvent.KIND to KindName("OTS", "03"),
|
||||
PaymentTargetsEvent.KIND to KindName("PayTo", null),
|
||||
PeopleListEvent.KIND to KindName("People Lists", "51"),
|
||||
ProfileBadgesEvent.KIND to KindName("Profile Badges", "58"),
|
||||
PictureEvent.KIND to KindName("Pictures", "68"),
|
||||
WorkoutRecordEvent.KIND to KindName("Workouts", null),
|
||||
PinListEvent.KIND to KindName("Pins", "51"),
|
||||
ZapPollEvent.KIND to KindName("Zap Poll", null),
|
||||
PollEvent.KIND to KindName("Poll", "88"),
|
||||
PollResponseEvent.KIND to KindName("Poll Response", "88"),
|
||||
PrivateDmEvent.KIND to KindName("NIP-04 DMs", "04"),
|
||||
PrivateOutboxRelayListEvent.KIND to KindName("Private Relays", "37"),
|
||||
ProxyRelayListEvent.KIND to KindName("Proxy Relays", "51"),
|
||||
PublicMessageEvent.KIND to KindName("Public Message", "A4"),
|
||||
ReactionEvent.KIND to KindName("Reactions", "25"),
|
||||
ContactCardEvent.KIND to KindName("Contact Card", "85"),
|
||||
RelayAuthEvent.KIND to KindName("Relay Auth", "42"),
|
||||
RelayDiscoveryEvent.KIND to KindName("Relay Discovery", "66"),
|
||||
RelayMonitorEvent.KIND to KindName("Relay Monitor Announcement", "66"),
|
||||
RelaySetEvent.KIND to KindName("Relay Set", "51"),
|
||||
ReportEvent.KIND to KindName("Reports", "56"),
|
||||
RepostEvent.KIND to KindName("Reposts", "18"),
|
||||
RequestToVanishEvent.KIND to KindName("User Delete", "62"),
|
||||
SealedRumorEvent.KIND to KindName("Seals", "59"),
|
||||
SearchRelayListEvent.KIND to KindName("Search Relays", "50"),
|
||||
StatusEvent.KIND to KindName("User Status", "38"),
|
||||
TextNoteEvent.KIND to KindName("Notes", "10"),
|
||||
TextNoteModificationEvent.KIND to KindName("Edits", null),
|
||||
TorrentEvent.KIND to KindName("Torrents", "35"),
|
||||
TorrentCommentEvent.KIND to KindName("Torrent Comments", "35"),
|
||||
TrustedRelayListEvent.KIND to KindName("Trusted Relays", "51"),
|
||||
TrustProviderListEvent.KIND to KindName("Trusted Providers", "85"),
|
||||
VideoHorizontalEvent.KIND to KindName("Video (Repl)", "71"),
|
||||
VideoVerticalEvent.KIND to KindName("Shorts (Repl)", "71"),
|
||||
VideoNormalEvent.KIND to KindName("Video", "71"),
|
||||
VideoShortEvent.KIND to KindName("Shorts", "71"),
|
||||
VoiceEvent.KIND to KindName("Voice Msg", "A0"),
|
||||
VoiceReplyEvent.KIND to KindName("Voice Reply", "A0"),
|
||||
WakeUpEvent.KIND to KindName("WakeUp", null),
|
||||
WebBookmarkEvent.KIND to KindName("Web Bookmark", "B0"),
|
||||
WikiNoteEvent.KIND to KindName("Wiki", "54"),
|
||||
)
|
||||
|
||||
fun infoFor(kind: Int): KindName? = names[kind]
|
||||
|
||||
fun nameFor(kind: Int): String? = names[kind]?.name
|
||||
|
||||
fun nipFor(kind: Int): String? = names[kind]?.nip
|
||||
|
||||
/** Case-insensitive substring search by label; returns matching (kind, info) sorted by kind. */
|
||||
fun search(query: String): List<Pair<Int, KindName>> {
|
||||
val q = query.trim().lowercase()
|
||||
if (q.isEmpty()) return emptyList()
|
||||
return names.entries
|
||||
.filter {
|
||||
it.value.name
|
||||
.lowercase()
|
||||
.contains(q)
|
||||
}.map { it.key to it.value }
|
||||
.sortedBy { it.first }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user