nips icon indicating copy to clipboard operation
nips copied to clipboard

Add reviews NIP

Open staab opened this issue 2 years ago • 31 comments

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.

staab avatar Nov 13 '23 21:11 staab

@arkin0x @sroertgen @rodant

staab avatar Nov 13 '23 21:11 staab

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.

fiatjaf avatar Nov 13 '23 21:11 fiatjaf

What about making the scores from 0 to 100 to allow us to do just integer math instead of dealing with floating points?

fiatjaf avatar Nov 13 '23 21:11 fiatjaf

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%.

staab avatar Nov 13 '23 22:11 staab

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)

pablof7z avatar Nov 13 '23 22:11 pablof7z

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)

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).

staab avatar Nov 13 '23 22:11 staab

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)

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).

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?

pablof7z avatar Nov 13 '23 22:11 pablof7z

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.

staab avatar Nov 14 '23 00:11 staab

can we just keep it to use the l and not the L here since this is not a NIP-32 anyway?

pablof7z avatar Nov 14 '23 00:11 pablof7z

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.

staab avatar Nov 14 '23 00:11 staab

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.

staab avatar Nov 14 '23 00:11 staab

Coracle now has support for this.

staab avatar Nov 16 '23 13:11 staab

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.

satoshisound avatar Feb 06 '24 16:02 satoshisound

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.

arkin0x avatar Feb 06 '24 16:02 arkin0x

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.

pmrcunha avatar Nov 22 '24 15:11 pmrcunha

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".

Frame 34

I'm not sure if the above belongs in the reviews NIP, or is an implementation choice in nostr apps implementing reviews NIP.

alltheseas avatar Nov 26 '24 22:11 alltheseas

added nostrability tracker https://github.com/nostrability/nostrability/issues/136

alltheseas avatar Nov 26 '24 22:11 alltheseas

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))

calvadev avatar Nov 26 '24 23:11 calvadev

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.

@pmrcunha Will you create a NIP for this? Or maybe a PR to update NIP-73 with the review kind.

@marykatefain

alexgleason avatar Mar 20 '25 03:03 alexgleason

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.

@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 avatar Mar 23 '25 16:03 RydalWater

@RydalWater I'm working on Coordinators rating for Robosats and I find it fits my needs. Let's push on this one!

KoalaSat avatar Mar 23 '25 20:03 KoalaSat

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.

@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>
}```

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!

marykatefain avatar Mar 24 '25 20:03 marykatefain

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.

patrickReiis avatar Mar 24 '25 22:03 patrickReiis

  • 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.

RydalWater avatar Mar 25 '25 07:03 RydalWater

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.

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.

RydalWater avatar Mar 25 '25 07:03 RydalWater

I added book reviews using kind 31985. Should I use 31025 instead? A couple other things:

  • I removed the NIP 73 constraint on d tags, since it doesn't fit the event review. It's better specified under specific review kinds.
  • I removed k from book reviews, because the event kind prescribes that the d tag must use the NIP 73 isbn format.
  • I added k to event reviews and changed the d tag 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 t tags, 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

staab avatar Mar 25 '25 16:03 staab

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.

RydalWater avatar Mar 25 '25 16:03 RydalWater

Robosats users will start rating their Coordinators with the next release following this NIP https://github.com/RoboSats/robosats/pull/1817/files#diff-0e8a2db3ba01fe63468e4c52e2018e371a6a1726c60834ed13202085fad5b911R61

KoalaSat avatar Mar 26 '25 16:03 KoalaSat

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 the Review kind instead of Relay review
  • We remains using the d flag 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.

kiuusai avatar Apr 02 '25 19:04 kiuusai

Jumble uses kind:31987

CodyTseng avatar Sep 20 '25 14:09 CodyTseng