Add reviews NIP
A concrete use case of https://github.com/nostr-protocol/nips/pull/878, which was too abstract. Reviews are useful for a lot of things, and a very simple concept.
@arkin0x @sroertgen @rodant
Great, that was too generic, this is very specific...
Reviews are useful for a lot of things
Oops.
Just kidding. I think this may be at the right level of abstraction.
What about making the scores from 0 to 100 to allow us to do just integer math instead of dealing with floating points?
What about making the scores from 0 to 100 to allow us to do just integer math instead of dealing with floating points?
Are we sure we don't want more precision? I could see an AI leaving reviews for things that are more precise than 1%.
What's the point of the L if kind:1986 already means "review"?
Same thing for the l although that's adding some more context (even though the context can be inferred by the e/a/p/t/whatever tag)
What's the point of the
Lifkind:1986already means "review"?Same thing for the
lalthough that's adding some more context (even though the context can be inferred by the e/a/p/t/whatever tag)
L tag is required by NIP 32, but you're right it's not super useful in this case. I chose l instead of some other single letter just because it's already in use. It's needed to differentiate between different types of resources (relays, websites, guids, movie ids or tmdb urls, etc).
What's the point of the
Lifkind:1986already means "review"? Same thing for thelalthough that's adding some more context (even though the context can be inferred by the e/a/p/t/whatever tag)L tag is required by NIP 32, but you're right it's not super useful in this case. I chose
linstead of some other single letter just because it's already in use. It's needed to differentiate between different types ofresources (relays, websites, guids, movie ids or tmdb urls, etc).
I assume you want the l tag for something like a review browser where you want to be able to query for "give me reviews of any relay"? that's the only use case I can imagine.
In my mind the most typical use case would be getting reviews of known things, so you would get a list of things and query for their reviews ({ "kinds": [1986], "#e": [...] })
Is that what you have in mind?
I assume you want the l tag for something like a review browser where you want to be able to query for "give me reviews of any relay"? that's the only use case I can imagine.
Yeah, exactly, coracle currently does this for the relay browse view. It's a valid use case for pretty much any product or service, e.g. review/shampoo or whatnot, based on l definitions.
can we just keep it to use the l and not the L here since this is not a NIP-32 anyway?
NIP 32 specifies that any event can self-label, so it would be inconsistent to not require it here. We could make L tags optional, which could be fine since clearly not every use case needs them.
Eh, started editing it, I think it's better to keep L required so there are fewer forms the l tags can take. Having a static length to a tag type keeps things simpler because mark always means the same thing, we all know the pain of e tags.
Coracle now has support for this.
After some discussion with @arkin0x we'd like to propose this approach for reviewing places with a QTS approach:
{
"kind": 1986,
"tags": [
["L", “qts/place”],
["l", “bitcoin”, “qts/place”],
["rating", “1”, "bitcoin/tax"],
["rating", “0.5”, "bitcoin/regulation”],
["rating", "0", "bitcoin/atm”],
["rating", “1”, "bitcoin/mining”],
["rating", “0.5”, "bitcoin/merchants”],
["l", “residence”, “qts/place”],
["rating", “1”, “residence/home-ownership”],
["rating", “0”, “residence/citizenship-pathway”],
["rating", “0”.5, “residence/residency-pathway”],
["rating", “1”, “residence/tax-residency-pathway”],
["rating", “1”, “residence/home-schooling”],
… and so on …
],
“content”: “I love this city from a Bitcoiner’s perspective!”
}
The "L" tag specifies the type of review. The "l" tags then are used to specify categories for the review. Each category can have an arbitrary number of ratings of either 0 (negative), 0.5 (neutral) or 1 (possitive). The rating name is set with the category in the third element of the rating array. The total score for a category would then be the sum of the ratings divided by the total number of ratings, for a final decimal score.
After some discussion with @arkin0x we'd like to propose this approach for reviewing places with a QTS approach:
For anyone who isn't familiar, QTS (qualitative thumb system) is an approach to reviews that quantifies a user's sentiment without asking them to translate their feelings into a number.
My original article about QTS was written in the context of the old NIP-32, but it works great as @satoshisound shows above with this NIP: https://habla.news/u/[email protected]/DLAfzJJpQDS4vj3wSleum
Additionally, @satoshisound and I agreed that the specific QTS implementation in my original article is more geared toward products (qts/product), whereas this kind of review example above is better suited to places. Hence the qts/place.
I'll be updating my article with this new information to help others implement more useful reviews using this NIP.
I'm working on a client which will include book reviews. After reading through this PR's comments, I decided to use QTS (thanks for the article @arkin0x !), but I implemented it sticking to what is currently described in the PR files. Looks something like this, but very much WIP:
{
kind: 31985, // New kind, for external content reviews
content: "this is the review comment",
...
tags: [
["d", "isbn:9780684832722"],
["rating", 0.75], // this is the total rating, calculated per the QTS system
["rating", 1, "recommended"], // the user gave a thumbs up to this book
["rating", 1, "Label 1"], // the user associated Label 1 to the book
["rating", 0, "Label 2"], // the user didn't associate Label 2 to the book
]
}
My thinking was that a client following QTS can ignore events which lack the recommended or thumb rating tag, but a client with a generic reviews feature can still display QTS reviews. The tradeoff is that the values for the labeled ratings would always be 0 or 1, so they'd for example show zero or five stars only, if that's how they display ratings. However, the global rating would still be just as meaningful in both systems.
non-technical interjection
What about making the scores from 0 to 100
As a end user facing scores between 0 and 100, 0 to 1, or 1 to 5, and so on, the review reviewer may have the question: what does 79 or 81 mean? What is the difference betwee 0.7 and 0.6?
I've noticed that the user friendly review reviewer apps provide an explanation, or a text qualification, as opposed to only providing a raw metric. See example from used car site Edmunds: "Fair, good, great".
I'm not sure if the above belongs in the reviews NIP, or is an implementation choice in nostr apps implementing reviews NIP.
added nostrability tracker https://github.com/nostrability/nostrability/issues/136
Also based on @arkin0x's QTS reviews approach, the below structure is what I'm using for product reviews:
{
"kind": 31555,
"tags": [
["d", "a:<listing kind>:<merchant pubkey>:<listing d-tag>"],
["rating", “1”, "thumb"],
["rating", “1”, "value”],
["rating", "1", "quality”],
["rating", “0”, "delivery”],
["rating", “1”, "communication”],
“content”: “Great product!”
}
The "thumb" rating label would represent 50% of the score weight and would be decided with a "good" (1) or "bad" (0) overall sentiment. The rest of the arbitrary rating labels would also be scored "good" (1) or "bad" (0), but with equal weight across the remaining 50%.
The labels here are exactly what I am using, but aren't necessary; client side I am just calculating the score based on the formula outlined above. (i.e.; totalScore = thumbScore * 0.5 + ((0.5 / numberOfRatingLabels) * eachArbitraryRatingLabel))
I'm working on a client which will include book reviews. After reading through this PR's comments, I decided to use QTS (thanks for the article @arkin0x !), but I implemented it sticking to what is currently described in the PR files. Looks something like this, but very much WIP:
{ kind: 31985, // New kind, for external content reviews content: "this is the review comment", ... tags: [ ["d", "isbn:9780684832722"], ["rating", 0.75], // this is the total rating, calculated per the QTS system ["rating", 1, "recommended"], // the user gave a thumbs up to this book ["rating", 1, "Label 1"], // the user associated Label 1 to the book ["rating", 0, "Label 2"], // the user didn't associate Label 2 to the book ] }My thinking was that a client following QTS can ignore events which lack the
recommendedorthumbrating tag, but a client with a generic reviews feature can still display QTS reviews. The tradeoff is that the values for the labeled ratings would always be 0 or 1, so they'd for example show zero or five stars only, if that's how they display ratings. However, the global rating would still be just as meaningful in both systems.
@pmrcunha Will you create a NIP for this? Or maybe a PR to update NIP-73 with the review kind.
@marykatefain
I'm working on a client which will include book reviews. After reading through this PR's comments, I decided to use QTS (thanks for the article @arkin0x !), but I implemented it sticking to what is currently described in the PR files. Looks something like this, but very much WIP:
{ kind: 31985, // New kind, for external content reviews content: "this is the review comment", ... tags: [ ["d", "isbn:9780684832722"], ["rating", 0.75], // this is the total rating, calculated per the QTS system ["rating", 1, "recommended"], // the user gave a thumbs up to this book ["rating", 1, "Label 1"], // the user associated Label 1 to the book ["rating", 0, "Label 2"], // the user didn't associate Label 2 to the book ] }My thinking was that a client following QTS can ignore events which lack the
recommendedorthumbrating tag, but a client with a generic reviews feature can still display QTS reviews. The tradeoff is that the values for the labeled ratings would always be 0 or 1, so they'd for example show zero or five stars only, if that's how they display ratings. However, the global rating would still be just as meaningful in both systems.@pmrcunha Will you create a NIP for this? Or maybe a PR to update NIP-73 with the review kind.
@marykatefain
It would be great to resurrect this again and make a few adjustments now that Bookstr is storming the scene! For Open Librarian I've used the structure below.
I made a specific design choice in using the hash of the ISBN rather than having it in thed tag in plain text. Users have the option to 'hide' books that are on their shelves in OL. While this doesn't give privacy per-se, it does make it more difficult for someone to guess what the ISBN would be. Sometimes folks read stuff that they don't want to be fully public. Anyway, this was the most efficient way I could decouple the other related data objects like reviews, progress etc. from the direct ISBN. The client pulls and decrypts the lists of books and after that it know what hashes to go fetch.
‘kind’ : 31025,
‘tags’ : [
‘d’ : <SHA-256 of ISBN value>
‘k’ : <NIP 73 external content k tag for books i.e., ‘isbn’>
‘rating’ : <normalised value between 0 and 1, optional mark>
‘raw’ : <optional raw rating value X/Y (e.g 5/10)>
...
't' : <optional hashtags for more additional categorization>
],
‘content’ : <optional additonal text for personal notes or more details>
}```
@RydalWater I'm working on Coordinators rating for Robosats and I find it fits my needs. Let's push on this one!
I'm working on a client which will include book reviews. After reading through this PR's comments, I decided to use QTS (thanks for the article @arkin0x !), but I implemented it sticking to what is currently described in the PR files. Looks something like this, but very much WIP:
{ kind: 31985, // New kind, for external content reviews content: "this is the review comment", ... tags: [ ["d", "isbn:9780684832722"], ["rating", 0.75], // this is the total rating, calculated per the QTS system ["rating", 1, "recommended"], // the user gave a thumbs up to this book ["rating", 1, "Label 1"], // the user associated Label 1 to the book ["rating", 0, "Label 2"], // the user didn't associate Label 2 to the book ] }My thinking was that a client following QTS can ignore events which lack the
recommendedorthumbrating tag, but a client with a generic reviews feature can still display QTS reviews. The tradeoff is that the values for the labeled ratings would always be 0 or 1, so they'd for example show zero or five stars only, if that's how they display ratings. However, the global rating would still be just as meaningful in both systems.@pmrcunha Will you create a NIP for this? Or maybe a PR to update NIP-73 with the review kind. @marykatefain
It would be great to resurrect this again and make a few adjustments now that Bookstr is storming the scene! For Open Librarian I've used the structure below.
I made a specific design choice in using the hash of the ISBN rather than having it in the
dtag in plain text. Users have the option to 'hide' books that are on their shelves in OL. While this doesn't give privacy per-se, it does make it more difficult for someone to guess what the ISBN would be. Sometimes folks read stuff that they don't want to be fully public. Anyway, this was the most efficient way I could decouple the other related data objects like reviews, progress etc. from the direct ISBN. The client pulls and decrypts the lists of books and after that it know what hashes to go fetch.‘kind’ : 31025, ‘tags’ : [ ‘d’ : <SHA-256 of ISBN value> ‘k’ : <NIP 73 external content k tag for books i.e., ‘isbn’> ‘rating’ : <normalised value between 0 and 1, optional mark> ‘raw’ : <optional raw rating value X/Y (e.g 5/10)> ... 't' : <optional hashtags for more additional categorization> ], ‘content’ : <optional additonal text for personal notes or more details> }```
Awesome to see we are both interested in this! I tried to base my review event on what had already been discussed in this thread, so we're pretty much aligned.
- I'm using Kind 31985 for book reviews
- d tag for ISBN (I'm pretty strongly opposed to hashing it. It's fake "privacy" and adds unnecessary obscurity. If people don't want to share what books they are reading, why are they leaving a review?)
- Agreed on k tag = "isbn"
- I'm using 0-1 rating which is normalized in the UI
- Neutral on adding an optional raw rating value, seems like it wouldn't hurt
Here's how my book reviews are currently structured:
{
"kind": 31985,
"tags": [
["d","isbn:9781529100624"],
["k", "isbn"],
["rating", "0.8"]
],
"content": "Good book",
}
Hope this helps and excited to collaborate on this!
Is there an event that represents a book? Basically, a request is made asking for a specific isbn, then, you grab all the data of a book that is useful, like title, cover, blurb and mention that event id in the 31985 along with the isbn value tag, like this:
{
"kind": 31985,
"tags": [
["d","isbn:9781529100624"],
["k", "isbn"],
["e", "<event-id-of-book-event>"],
["rating", "0.8"]
],
"content": "Good book",
}
The rationale for that is that if I have the events saved locally but don't have access to internet, I wouldn't be able to figure out which book it is just by the isbn code.
- d tag for ISBN (I'm pretty strongly opposed to hashing it. It's fake "privacy" and adds unnecessary obscurity. If people don't want to share what books they are reading, why are they leaving a review?)
The answer to this is that not all reviews are for someone else. Indeed, a user may just want to track for themselves what they thought about the book so that a client can use that information when creating recommendations, or summary statistics for reading habits. I've added a little more on my reasoning for this route over on the NIP-XX Progress Event discussion (100% not 'privacy', just 'hidden').
- Agreed on k tag = "isbn"
- I'm using 0-1 rating which is normalized in the UI
Cool, same here.
- Neutral on adding an optional raw rating value, seems like it wouldn't hurt
This was added as an optional fallback it isn't essential but could allow clients to see how it was provided to the user at the time.
Is there an event that represents a book? Basically, a request is made asking for a specific
isbn, then, you grab all the data of a book that is useful, like title, cover, blurb and mention that event id in the31985along with theisbnvalue tag, like this:{ "kind": 31985, "tags": [ ["d","isbn:9781529100624"], ["k", "isbn"], ["e", "<event-id-of-book-event>"], ["rating", "0.8"] ], "content": "Good book", }The rationale for that is that if I have the events saved locally but don't have access to internet, I wouldn't be able to figure out which book it is just by the
isbncode.
Yes I am coming around to this idea, not for adding them to reviews, but so that we can break the dependency on external APIs. @marykatefain you'll come to see when Open Library goes down (as the archive has suffered a few outages) that it can be painful. An ideal future would be to build book-details objects into the fabric of nostr. That said I think we probably want to take this discussion elsewhere so that it doesn't hijack this thread.
I added book reviews using kind 31985. Should I use 31025 instead? A couple other things:
- I removed the NIP 73 constraint on
dtags, since it doesn't fit the event review. It's better specified under specific review kinds. - I removed
kfrom book reviews, because the event kind prescribes that thedtag must use the NIP 73 isbn format. - I added
kto event reviews and changed thedtag to the event id (the pubkey is redundant, including it just adds a new format for no reason). - I left off
raw, this is basically impossible to coordinate on and/or meaningless as a data format, since most clients will render something more involved than plain text. We talked about this at length somewhere else. - I left off
ttags, people can always add these if they want to. If we're going to try to capture genre information, we should do something similar to #1043
No problem dropping the t and raw tags, these were optional anyway.
Regarding the d tag it isn’t pubkey but rather the hash of an ISBN. I use this method in my implementation as a means to find the events so I would prefer to remove the MUST and opt for SHOULD or MAY. Otherwise I guess I could just keep mine out of spec.
Kind number I used was 31025, happy to switch to 31985 if this is preferred and we can resolve the d tag requirements. If not I guess I can just keep using the kind and it won’t interfere with the preferred spec.
Robosats users will start rating their Coordinators with the next release following this NIP https://github.com/RoboSats/robosats/pull/1817/files#diff-0e8a2db3ba01fe63468e4c52e2018e371a6a1726c60834ed13202085fad5b911R61
I read all the discussion about and I saw the following issue:
Add Nostr events for some rating cases can be problematic for some reasons.
If I want to create a system to review my favorite shoes or perhaps review a market in my city? An Uber driver? Perhaps a specific product like Coca Cola?
It can probably generate a little chaos with people arbitrary creating new rating kinds for each minimal case they want. Not sure if it's a real problem. But I think it's a good idea to define a general pattern to review stuffs instead of create a separated event kind for each stuff.
There are some stuffs that can be rated/reviewed. In my case, I'm working in a system for rating people for a Web Of Trust in a justice protocol: Private Law Society.
In our specific case, we are using a binary rating system. We want to create a graph schema to describe if a people is trustful based on our social cycle (who I positive or negative rated and who we had business and the people that my trusted parties had business and/or trust). In our case, we have only binary values for rating and had business fields.
By the way, instead of create a specific kind to review books, why not define a fixed kind for any review?
My idea is something like this:
- We change the kind
31986(because RoboSats plans start using it) to be theReviewkind instead ofRelay review - We remains using the
dflag prefix to define the rating kind
So, for books rating, it could be like this:
{
"kind": 31986,
"content": "This book is very great!",
"tags": [
["d", "isbn:9781529100624"],
["rating", "0.8"],
]
}
For the Relay review we can do something like:
{
"kind": 31986,
"content": "This relay is very fast!",
"tags": [
["d", "relay:wss://relay.example.com"],
["rating", "1"]
]
}
In PLS case it could be something like this:
{
"kind": 31986,
"content": "This arbitror is very trustful!",
"tags": [
["d", "pls-rating:56fff0a8bd6a54973f39edf70ce058e4495d2a8024e2caf1c965822fc2f3dca2"],
["rating", "1"], // Should be 0 or 1 for our specific system
["had-business", "0"], // A flag for our internal using
]
}
Particularly I think it's not so good to PLS use tags for rating (it's a completely personal opinion). So, if I could define the way it would be implemented in PLS system, I would do something like this:
{
"pubkey": "0b884d0dd72c37fe0dbb4a3c422bf8cd632fb0bdc7be51749e545a449abbd54a", // As you may know, there's the rater pubkey
"kind": 31986,
"content": JSON.stringify({
"score": true, // True for trustful, false for untrustful
"businessAlreadyDone": true,
"description": "This arbitror is very trustful"
}),
"tags": [
["d", "pls-rating:56fff0a8bd6a54973f39edf70ce058e4495d2a8024e2caf1c965822fc2f3dca2"] // this hex is the rated pubkey
]
}
I see the result is not human readable, but if the client implements the correct interpretation for this data, it's ok to do it. For PLS there's not a problem. BTW, it's a decision for each project that want to implement something using this way.
There was my 2 cents about this discussion. Sorry if it doesn't makes sense. I'm still learning about this all. Perhaps it can be helpful to y'all or can give some idea to anyone.
Jumble uses kind:31987