tag.mx raw
1 package protocol
2
3 import "runtime"
4 // Tag names. Single-letter tags are the only ones relays index, which is why
5 // the spec mirrors multi-letter tags with a single-letter one wherever a query
6 // has to work: `x` mirrors `audio`, `a` mirrors `song`.
7 const (
8 TagD = "d"
9 TagA = "a"
10 TagE = "e"
11 TagP = "p"
12 TagX = "x"
13 TagK = "k"
14 TagI = "i"
15 TagT = "t"
16 TagG = "g"
17 TagR = "r"
18 TagL = "L"
19 TagLValue = "l"
20 TagARoot = "A"
21 TagKRoot = "K"
22 TagPRoot = "P"
23 TagDay = "D"
24 TagN = "n"
25 TagTitle = "title"
26 TagSummary = "summary"
27 TagDescription = "description"
28 TagImage = "image"
29 TagContent = "content"
30 TagPublishedAt = "published_at"
31 TagStatus = "status"
32 TagLocation = "location"
33 TagPrice = "price"
34 TagStart = "start"
35 TagEnd = "end"
36 TagStartTzid = "start_tzid"
37 TagFb = "fb"
38 TagRole = "role"
39 TagArtist = "artist"
40 TagAlbum = "album"
41 TagTrack = "track"
42 TagDisc = "disc"
43 TagDuration = "duration"
44 TagGenre = "genre"
45 TagReleaseDate = "release-date"
46 TagISRC = "isrc"
47 TagLanguage = "language"
48 TagBPM = "bpm"
49 TagKey = "key"
50 TagCopyright = "copyright"
51 TagAudio = "audio"
52 TagMaster = "master"
53 TagCodec = "codec"
54 TagServer = "server"
55 TagMint = "mint"
56 TagRetailer = "retailer"
57 TagExpiration = "expiration"
58 TagFilter = "filter"
59 TagFp = "fp"
60 TagCatalog = "catalog"
61 TagTier = "tier"
62 TagPayment = "payment"
63 TagToken = "token"
64 TagChannel = "channel"
65 TagPeriod = "period"
66 TagMode = "mode"
67 TagBytes = "bytes"
68 TagAmountSats = "amount_sats"
69 TagPaid = "paid"
70 TagSats = "sats"
71 TagPlays = "plays"
72 TagDownloads = "downloads"
73 TagListeners = "listeners"
74 TagReceipts = "receipts"
75 TagBlind = "blind"
76 TagHost = "host"
77 TagStatement = "statement"
78 TagDleq = "dleq"
79 TagSong = "song"
80 TagEncore = "encore"
81 TagFeat = "feat"
82 TagCover = "cover"
83 TagCapacity = "capacity"
84 TagPosition = "position"
85 TagJoinedAt = "joined_at"
86 TagStudio = "studio"
87 TagFormat = "format"
88 TagFormatDetail = "format-detail"
89 TagConditionMedia = "condition-media"
90 TagConditionSleeve = "condition-sleeve"
91 TagPressing = "pressing"
92 TagIncludes = "includes"
93 TagQuantity = "quantity"
94 TagTerritory = "territory"
95 TagBarcode = "barcode"
96 TagSizes = "sizes"
97 TagColor = "color"
98 TagMaterial = "material"
99 TagPrintMethod = "print-method"
100 TagDesigner = "designer"
101 TagWeight = "weight"
102 TagSleeve = "sleeve"
103 TagRelay = "relay"
104 TagChallenge = "challenge"
105 TagT0 = "t0"
106 TagT1 = "t1"
107 TagAmount = "amount"
108 TagUnit = "unit"
109 )
110
111 // Controlled vocabularies. The documents call these conventions rather than
112 // closed registries - "new role IDs emerge as the community uses them" - so
113 // none of them is enforced on parse. They exist to spell values once.
114 const (
115 // Role namespaces for NIP-32 label events (confirmations and attestations).
116 NamespaceCredit = "musiquay.credit"
117 NamespaceAttest = "musiquay.attest"
118
119 // Label values on a confirmation (NamespaceCredit).
120 LabelConfirmed = "confirmed"
121 LabelDenied = "denied"
122 LabelCorrected = "corrected"
123
124 // Label value on an attestation (NamespaceAttest).
125 LabelAttests = "attests"
126
127 // Access filter tiers. The d tag is "<tier>:<asset-address>", so these
128 // values are the head of the d value, not a tag of their own.
129 TierFan = "fan"
130 TierRetail = "retail"
131 TierSub = "sub"
132
133 // Filter structure declaration. Only one value is defined; an unknown
134 // value or version is a fail-closed condition on the host.
135 FilterBloom = "bloom"
136
137 // Payment legs accepted on an access filter and declared on an order.
138 PaymentCashu = "cashu"
139 PaymentLightning = "lightning"
140
141 // Delivery receipt modes.
142 ModeDownload = "download"
143 ModeStream = "stream"
144
145 // Counter provenance: listeners is always an estimate.
146 Approx = "approx"
147
148 // RSVP statuses (NIP-52 plus the waitlist convention).
149 StatusAccepted = "accepted"
150 StatusDeclined = "declined"
151 StatusTentative = "tentative"
152 StatusWaitlist = "waitlist"
153
154 // Free/busy indicator beside an RSVP status.
155 FbFree = "free"
156 FbBusy = "busy"
157
158 // Classified listing status.
159 StatusActive = "active"
160
161 // Edition format family.
162 FormatVinyl = "vinyl"
163 FormatCD = "CD"
164 FormatCassette = "cassette"
165
166 // Setlist song annotations.
167 SongFeat = "feat"
168 SongCover = "cover"
169 )
170
171 // AccessTier reports whether s is one of the three defined filter tiers. The
172 // d tag of an access filter is "<tier>:<asset-address>".
173 func AccessTier(s string) (ok bool) {
174 switch s {
175 case TierFan, TierRetail, TierSub:
176 return true
177 }
178 return false
179 }
180
181 // RosterRoles is the event-roster vocabulary, grouped as the spec groups it.
182 // Actors include companies: a rental house is as much a participant as a
183 // drummer, and the protocol does not distinguish them.
184 var RosterRoles []string
185
186 // CreditRoles is the release and track credit vocabulary. It extends the roster
187 // vocabulary with the composition, production and studio side. Instruments are
188 // carried inside the role token ("performer-cello"), which is why the performer
189 // family is open-ended rather than a list.
190 var CreditRoles []string
191
192 var rolesReady bool
193
194 // initRoles fills the two tables on first use. Lazy rather than an init()
195 // or an initializer, for two reasons: the test harness does not run a package's
196 // init (moxie test compiles the package under test as the build root and its
197 // init never reaches initAll), and every allocation stored into a package
198 // global must outlive this call - the frame arena is released on return, so the
199 // tables are built in the root arena. nostr/pkg/kind does the same.
200 func initRoles() {
201 if rolesReady {
202 return
203 }
204 rolesReady = true
205 runtime.SovereignSetArena(runtime.RootArena())
206 RosterRoles = []string{
207 // Organization
208 "promoter", "booking-agent", "venue", "label", "sponsor",
209 // Sound
210 "sound-foh", "sound-monitors", "sound-broadcast", "sound-recording",
211 // Technical
212 "lighting", "stage-manager", "backline", "av-tech", "rigger",
213 // Performance. "performer" is the generic form the music, calendar and
214 // setlist examples use; the roster vocabulary refines it per billing.
215 "performer", "performer-headliner", "performer-support", "performer-session",
216 "performer-guest", "performer-dj", "performer-mc",
217 // Crew
218 "road-crew", "tour-manager", "merch", "van-driver",
219 // Venue staff
220 "door", "bar", "security", "venue-manager", "venue-tech", "catering",
221 // Documentation
222 "photographer", "filmmaker", "live-stream", "journalist", "reviewer",
223 // Creative
224 "designer", "vj", "visual-artist",
225 // Service companies
226 "van-rental", "backline-rental", "pa-rental", "lighting-rental",
227 "catering-company", "production-company", "studio",
228 }
229 CreditRoles = []string{
230 // Composition
231 "songwriter", "lyricist", "composer", "arranger", "orchestrator",
232 // Performance
233 "performer-vocals", "performer-guitar", "performer-bass", "performer-drums",
234 "performer-keys", "featured-artist",
235 // Production
236 "producer", "co-producer", "executive-producer",
237 // Recording
238 "recording-engineer", "assistant-engineer",
239 // Mixing
240 "mix-engineer", "mix-assistant",
241 // Mastering
242 "mastering-engineer",
243 // Label and business
244 "a-and-r", "manager", "distributor",
245 // Visual
246 "artwork-designer", "illustrator", "layout-designer", "typography",
247 // Video
248 "video-director", "cinematographer", "video-editor", "colorist", "vfx",
249 // Written
250 "liner-notes", "translator",
251 // Manufacturing
252 "pressing-plant", "replication-plant", "duplication-house", "print-house",
253 }
254 }
255
256 // KnownRole reports whether role appears in either vocabulary. Unknown roles
257 // are legal - the vocabularies are conventions - so this is for display and
258 // validation warnings, never a gate.
259 func KnownRole(role string) (ok bool) {
260 initRoles()
261 for _, r := range RosterRoles {
262 if r == role {
263 return true
264 }
265 }
266 for _, r := range CreditRoles {
267 if r == role {
268 return true
269 }
270 }
271 // performer-<instrument> is open-ended by design.
272 if len(role) > 10 && role[:10] == "performer-" {
273 return true
274 }
275 return false
276 }
277