package protocol import "runtime" // Tag names. Single-letter tags are the only ones relays index, which is why // the spec mirrors multi-letter tags with a single-letter one wherever a query // has to work: `x` mirrors `audio`, `a` mirrors `song`. const ( TagD = "d" TagA = "a" TagE = "e" TagP = "p" TagX = "x" TagK = "k" TagI = "i" TagT = "t" TagG = "g" TagR = "r" TagL = "L" TagLValue = "l" TagARoot = "A" TagKRoot = "K" TagPRoot = "P" TagDay = "D" TagN = "n" TagTitle = "title" TagSummary = "summary" TagDescription = "description" TagImage = "image" TagContent = "content" TagPublishedAt = "published_at" TagStatus = "status" TagLocation = "location" TagPrice = "price" TagStart = "start" TagEnd = "end" TagStartTzid = "start_tzid" TagFb = "fb" TagRole = "role" TagArtist = "artist" TagAlbum = "album" TagTrack = "track" TagDisc = "disc" TagDuration = "duration" TagGenre = "genre" TagReleaseDate = "release-date" TagISRC = "isrc" TagLanguage = "language" TagBPM = "bpm" TagKey = "key" TagCopyright = "copyright" TagAudio = "audio" TagMaster = "master" TagCodec = "codec" TagServer = "server" TagMint = "mint" TagRetailer = "retailer" TagExpiration = "expiration" TagFilter = "filter" TagFp = "fp" TagCatalog = "catalog" TagTier = "tier" TagPayment = "payment" TagToken = "token" TagChannel = "channel" TagPeriod = "period" TagMode = "mode" TagBytes = "bytes" TagAmountSats = "amount_sats" TagPaid = "paid" TagSats = "sats" TagPlays = "plays" TagDownloads = "downloads" TagListeners = "listeners" TagReceipts = "receipts" TagBlind = "blind" TagHost = "host" TagStatement = "statement" TagDleq = "dleq" TagSong = "song" TagEncore = "encore" TagFeat = "feat" TagCover = "cover" TagCapacity = "capacity" TagPosition = "position" TagJoinedAt = "joined_at" TagStudio = "studio" TagFormat = "format" TagFormatDetail = "format-detail" TagConditionMedia = "condition-media" TagConditionSleeve = "condition-sleeve" TagPressing = "pressing" TagIncludes = "includes" TagQuantity = "quantity" TagTerritory = "territory" TagBarcode = "barcode" TagSizes = "sizes" TagColor = "color" TagMaterial = "material" TagPrintMethod = "print-method" TagDesigner = "designer" TagWeight = "weight" TagSleeve = "sleeve" TagRelay = "relay" TagChallenge = "challenge" TagT0 = "t0" TagT1 = "t1" TagAmount = "amount" TagUnit = "unit" ) // Controlled vocabularies. The documents call these conventions rather than // closed registries - "new role IDs emerge as the community uses them" - so // none of them is enforced on parse. They exist to spell values once. const ( // Role namespaces for NIP-32 label events (confirmations and attestations). NamespaceCredit = "musiquay.credit" NamespaceAttest = "musiquay.attest" // Label values on a confirmation (NamespaceCredit). LabelConfirmed = "confirmed" LabelDenied = "denied" LabelCorrected = "corrected" // Label value on an attestation (NamespaceAttest). LabelAttests = "attests" // Access filter tiers. The d tag is ":", so these // values are the head of the d value, not a tag of their own. TierFan = "fan" TierRetail = "retail" TierSub = "sub" // Filter structure declaration. Only one value is defined; an unknown // value or version is a fail-closed condition on the host. FilterBloom = "bloom" // Payment legs accepted on an access filter and declared on an order. PaymentCashu = "cashu" PaymentLightning = "lightning" // Delivery receipt modes. ModeDownload = "download" ModeStream = "stream" // Counter provenance: listeners is always an estimate. Approx = "approx" // RSVP statuses (NIP-52 plus the waitlist convention). StatusAccepted = "accepted" StatusDeclined = "declined" StatusTentative = "tentative" StatusWaitlist = "waitlist" // Free/busy indicator beside an RSVP status. FbFree = "free" FbBusy = "busy" // Classified listing status. StatusActive = "active" // Edition format family. FormatVinyl = "vinyl" FormatCD = "CD" FormatCassette = "cassette" // Setlist song annotations. SongFeat = "feat" SongCover = "cover" ) // AccessTier reports whether s is one of the three defined filter tiers. The // d tag of an access filter is ":". func AccessTier(s string) (ok bool) { switch s { case TierFan, TierRetail, TierSub: return true } return false } // RosterRoles is the event-roster vocabulary, grouped as the spec groups it. // Actors include companies: a rental house is as much a participant as a // drummer, and the protocol does not distinguish them. var RosterRoles []string // CreditRoles is the release and track credit vocabulary. It extends the roster // vocabulary with the composition, production and studio side. Instruments are // carried inside the role token ("performer-cello"), which is why the performer // family is open-ended rather than a list. var CreditRoles []string var rolesReady bool // initRoles fills the two tables on first use. Lazy rather than an init() // or an initializer, for two reasons: the test harness does not run a package's // init (moxie test compiles the package under test as the build root and its // init never reaches initAll), and every allocation stored into a package // global must outlive this call - the frame arena is released on return, so the // tables are built in the root arena. nostr/pkg/kind does the same. func initRoles() { if rolesReady { return } rolesReady = true runtime.SovereignSetArena(runtime.RootArena()) RosterRoles = []string{ // Organization "promoter", "booking-agent", "venue", "label", "sponsor", // Sound "sound-foh", "sound-monitors", "sound-broadcast", "sound-recording", // Technical "lighting", "stage-manager", "backline", "av-tech", "rigger", // Performance. "performer" is the generic form the music, calendar and // setlist examples use; the roster vocabulary refines it per billing. "performer", "performer-headliner", "performer-support", "performer-session", "performer-guest", "performer-dj", "performer-mc", // Crew "road-crew", "tour-manager", "merch", "van-driver", // Venue staff "door", "bar", "security", "venue-manager", "venue-tech", "catering", // Documentation "photographer", "filmmaker", "live-stream", "journalist", "reviewer", // Creative "designer", "vj", "visual-artist", // Service companies "van-rental", "backline-rental", "pa-rental", "lighting-rental", "catering-company", "production-company", "studio", } CreditRoles = []string{ // Composition "songwriter", "lyricist", "composer", "arranger", "orchestrator", // Performance "performer-vocals", "performer-guitar", "performer-bass", "performer-drums", "performer-keys", "featured-artist", // Production "producer", "co-producer", "executive-producer", // Recording "recording-engineer", "assistant-engineer", // Mixing "mix-engineer", "mix-assistant", // Mastering "mastering-engineer", // Label and business "a-and-r", "manager", "distributor", // Visual "artwork-designer", "illustrator", "layout-designer", "typography", // Video "video-director", "cinematographer", "video-editor", "colorist", "vfx", // Written "liner-notes", "translator", // Manufacturing "pressing-plant", "replication-plant", "duplication-house", "print-house", } } // KnownRole reports whether role appears in either vocabulary. Unknown roles // are legal - the vocabularies are conventions - so this is for display and // validation warnings, never a gate. func KnownRole(role string) (ok bool) { initRoles() for _, r := range RosterRoles { if r == role { return true } } for _, r := range CreditRoles { if r == role { return true } } // performer- is open-ended by design. if len(role) > 10 && role[:10] == "performer-" { return true } return false }