Liking cljdoc? Tell your friends :D

dk.simongray.podcast.comments

The public comments on an episode or a show, from the thread below the root post that podcast:socialInteract names.

The comments of these protocols come in one shape:

  • ActivityPub, which Mastodon, PeerTube and the podcast host Castopod speak
  • the AT Protocol, which Bluesky speaks, from an AppView
  • Nostr, from relays, which also has the comments about an episode that names no root post

A thread is a map. An unread thread is a map of the :protocol and the :uri of a podcast:socialInteract, and a thread that was read also has:

  • the id of the :root post
  • the :comments by id
  • their :authors by id
  • the :report of what went wrong

A comment:

{:id          "https://example.social/users/ann/statuses/2"
 :url         "https://example.social/@ann/2"  ; its web page
 :author      "https://example.social/users/ann"  ; in :authors
 :in-reply-to "https://example.social/users/bob/statuses/1"
 :published   #inst "2026-09-19T00:22:52Z"
 :updated     #inst "2026-09-19T00:30:00Z"
 :title       "…"         ; of a video or an article
 :content     "<p>…</p>"  ; sanitized HTML with absolute links
 :language    "en"
 :summary     "…"         ; a content warning
 :sensitive?  true        ; to show behind a warning
 :labels      #{"graphic-media"}  ; what makes it sensitive
 :attachments [{:kind :image :url "…" :preview "…" :alt "…"
                :media-type "image/png" :width 1200 :height 800
                :aspect-ratio [1200 800]}]
 :poll        {:choices [{:name "Yes" :votes 3}] :multiple? true
               :ends #inst "2026-09-20T00:00:00Z"}
 :counts      {:replies 2 :reposts 1 :likes 5 :quotes 0}
 :hidden?     true        ; hidden by the author of the root post
 :read-at     #inst "2026-09-28T12:00:00Z"}

A deleted post is :deleted?, and a post that podcast-clj didn't get has an :error that tells why. The :counts are those that the server of a comment gives with it. On Nostr, they're the :likes and :reposts that the relays gave, one for each author. An author is a map of :id, :name, :handle, :url and :avatar. The handle is @name@domain on ActivityPub, @handle on the AT Protocol and npub1… on Nostr. A thread that podcast-clj read through the Mastodon API has :via :mastodon-api. The keys in this namespace, such as ::collection, record how far the reading went.

The public comments on an episode or a show, from the thread below the
root post that podcast:socialInteract names.

The comments of these protocols come in one shape:

- ActivityPub, which Mastodon, PeerTube and the podcast host Castopod
  speak
- the AT Protocol, which Bluesky speaks, from an AppView
- Nostr, from relays, which also has the comments about an episode that
  names no root post

A thread is a map. An unread thread is a map of the :protocol and the
:uri of a podcast:socialInteract, and a thread that was read also has:

- the id of the :root post
- the :comments by id
- their :authors by id
- the :report of what went wrong

A comment:

    {:id          "https://example.social/users/ann/statuses/2"
     :url         "https://example.social/@ann/2"  ; its web page
     :author      "https://example.social/users/ann"  ; in :authors
     :in-reply-to "https://example.social/users/bob/statuses/1"
     :published   #inst "2026-09-19T00:22:52Z"
     :updated     #inst "2026-09-19T00:30:00Z"
     :title       "…"         ; of a video or an article
     :content     "<p>…</p>"  ; sanitized HTML with absolute links
     :language    "en"
     :summary     "…"         ; a content warning
     :sensitive?  true        ; to show behind a warning
     :labels      #{"graphic-media"}  ; what makes it sensitive
     :attachments [{:kind :image :url "…" :preview "…" :alt "…"
                    :media-type "image/png" :width 1200 :height 800
                    :aspect-ratio [1200 800]}]
     :poll        {:choices [{:name "Yes" :votes 3}] :multiple? true
                   :ends #inst "2026-09-20T00:00:00Z"}
     :counts      {:replies 2 :reposts 1 :likes 5 :quotes 0}
     :hidden?     true        ; hidden by the author of the root post
     :read-at     #inst "2026-09-28T12:00:00Z"}

A deleted post is :deleted?, and a post that podcast-clj didn't get has
an :error that tells why. The :counts are those that the server of a
comment gives with it. On Nostr, they're the :likes and :reposts that
the relays gave, one for each author. An author is a map of :id, :name,
:handle, :url and :avatar. The handle is @name@domain on ActivityPub,
@handle on the AT Protocol and npub1… on Nostr. A thread that
podcast-clj read through the Mastodon API has :via :mastodon-api. The
keys in this namespace, such as ::collection, record how far the
reading went.
raw docstring

changesclj/s

(changes old new)

What changed between two readings of one thread, the old and the new, e.g. to tell the listener of new replies:

{:added   [comment …]  ; the comments that `old` didn't have
 :edited  [comment …]  ; those with a later :updated
 :deleted [id …]}      ; those deleted since

The comments are in the order of their :published time. The root of a thread about an episode, which has no content, isn't added.

What changed between two readings of one thread, the `old` and the
`new`, e.g. to tell the listener of new replies:

    {:added   [comment …]  ; the comments that `old` didn't have
     :edited  [comment …]  ; those with a later :updated
     :deleted [id …]}      ; those deleted since

The comments are in the order of their :published time. The root of a
thread about an episode, which has no content, isn't added.
sourceraw docstring

default-limitsclj/s

The limits of a fetch that its options don't set.

The limits of a fetch that its options don't set.
sourceraw docstring

fetch!clj/s

(fetch! thread)
(fetch! thread opts)

Read thread as far as the limits of opts allow, and give it with the :report of what went wrong. On the JVM it gives the thread, and in ClojureScript a promise of it.

The thread is an unread thread, or one that an earlier fetch gave. A second fetch reads on from where the first stopped, and it asks again for what failed for a time. To read what's new, fetch the unread thread again, and compare the two readings with changes. The opts are these, with default-limits for the limits:

  • :appview, to read the threads of the AT Protocol from
  • :status-paths, in place of activitypub/status-paths
  • :relays, to read the threads of Nostr from, in place of comments.nostr/relays
  • :max-comments, the most posts to ask for in a fetch
  • :max-requests, after which no round starts
  • :max-seconds, after which no request starts
  • :concurrency, the number of requests under way at a time
  • :max-pages, the most pages of a collection in a round
  • :timeout-seconds, for each request
  • :max-bytes, of each answer
  • :allow-pred, which URLs a request can go to
  • :user-agent, as http/send! takes it
  • :send, in place of client/send! and nostr/send!
  • :now-fn, a function that gives the time, the clock by default

The requests go out in rounds. The root post comes first, then the posts nearest to it, then the authors. The pages of a collection come one after another. A server that didn't answer in time, or that answered 429 Too Many Requests, gets no more requests in the same fetch. The time that's left limits each request. Nothing throws.

A thread also has limits of size that no option sets, such as 10,000 comments in all. The report counts the replies that such a limit cut.

The servers of a thread choose the URLs that this fetches. So each request carries a guard as its :allow-pred, which lets nothing go to a private network. To reach your own network, give (constantly true) as :allow-pred. A :send keeps to the :timeout-seconds and the :allow-pred of each request. A :send that signs its requests can also read the servers that answer only signed requests.

A request to a relay has the :url of the relay and the :filters of NIP-01, and goes over a WebSocket. Your :send gets such requests too, and answers them as nostr/send! does.

Read `thread` as far as the limits of `opts` allow, and give it with the
:report of what went wrong. On the JVM it gives the thread, and in
ClojureScript a promise of it.

The `thread` is an unread thread, or one that an earlier fetch gave. A
second fetch reads on from where the first stopped, and it asks again
for what failed for a time. To read what's new, fetch the unread thread
again, and compare the two readings with changes. The `opts` are these,
with default-limits for the limits:

- :appview, to read the threads of the AT Protocol from
- :status-paths, in place of activitypub/status-paths
- :relays, to read the threads of Nostr from, in place of
  comments.nostr/relays
- :max-comments, the most posts to ask for in a fetch
- :max-requests, after which no round starts
- :max-seconds, after which no request starts
- :concurrency, the number of requests under way at a time
- :max-pages, the most pages of a collection in a round
- :timeout-seconds, for each request
- :max-bytes, of each answer
- :allow-pred, which URLs a request can go to
- :user-agent, as http/send! takes it
- :send, in place of client/send! and nostr/send!
- :now-fn, a function that gives the time, the clock by default

The requests go out in rounds. The root post comes first, then the posts
nearest to it, then the authors. The pages of a collection come one
after another. A server that didn't answer in time, or that answered
429 Too Many Requests, gets no more requests in the same fetch. The time
that's left limits each request. Nothing throws.

A thread also has limits of size that no option sets, such as 10,000
comments in all. The report counts the replies that such a limit cut.

The servers of a thread choose the URLs that this fetches. So each
request carries a guard as its :allow-pred, which lets nothing go to a
private network. To reach your own network, give (constantly true) as
:allow-pred. A :send keeps to the :timeout-seconds and the :allow-pred
of each request. A :send that signs its requests can also read the
servers that answer only signed requests.

A request to a relay has the :url of the relay and the :filters of
NIP-01, and goes over a WebSocket. Your :send gets such requests too,
and answers them as nostr/send! does.
sourceraw docstring

hiccupclj/s

(hiccup thread comment)
(hiccup {:keys [protocol] :as thread}
        {:keys [content] :as comment}
        {:keys [link-fn] :as opts})

The content of comment, a comment of thread, as Hiccup that a page can show, with opts. The result is a sequence of nodes to put inside a parent element.

The opts are these:

  • :link-fn, a function of a part, with the :protocol of the thread. It gives the URL that a mention, a hashtag or a reference links to, or nil. So you choose where they lead, in one function for each protocol.
  • :emoji?, to show the custom emoji of ActivityPub as images
  • :names, a map from the ids of profiles of Nostr to names, in place of the names of the authors of the thread

For example:

(hiccup thread comment
        {:link-fn (fn [{:keys [type id tag]}]
                    (case type
                      :mention (str "/profiles/" id)
                      :hashtag (str "/tags/" tag)
                      nil))})

When :link-fn gives nil, a mention of ActivityPub keeps the link that the post has, and a mention of another protocol has no link.

The :content of a comment is the same content as HTML, without the links of an app. A comment that an older version of podcast-clj read has only that HTML, and the Hiccup is then made of it.

The content of `comment`, a comment of `thread`, as Hiccup that a page
can show, with `opts`. The result is a sequence of nodes to put inside
a parent element.

The `opts` are these:

- :link-fn, a function of a part, with the :protocol of the thread. It
  gives the URL that a mention, a hashtag or a reference links to, or
  nil. So you choose where they lead, in one function for each
  protocol.
- :emoji?, to show the custom emoji of ActivityPub as images
- :names, a map from the ids of profiles of Nostr to names, in place of
  the names of the authors of the thread

For example:

    (hiccup thread comment
            {:link-fn (fn [{:keys [type id tag]}]
                        (case type
                          :mention (str "/profiles/" id)
                          :hashtag (str "/tags/" tag)
                          nil))})

When :link-fn gives nil, a mention of ActivityPub keeps the link that the
post has, and a mention of another protocol has no link.

The :content of a comment is the same content as HTML, without the
links of an app. A comment that an older version of podcast-clj read
has only that HTML, and the Hiccup is then made of it.
sourceraw docstring

next-pageclj/s

(next-page exchange)

The request for the page after the one in exchange, an exchange of the reading of a collection with a :request and a :response. It's nil after the last page, and for any other exchange. Only the collections of ActivityPub come in pages.

The request for the page after the one in `exchange`, an exchange of
the reading of a collection with a :request and a :response. It's nil
after the last page, and for any other exchange. Only the collections of
ActivityPub come in pages.
sourceraw docstring

partsclj/s

(parts thread {:keys [content] :as comment})

The parts of the content of comment, a comment of thread, in their order. Each part is a map with its :type and the :text that the content shows for it:

{:type :text :text "Good episode, "}
{:type :mention :text "@ann" :id "https://example.social/users/ann"}
{:type :hashtag :text "#podcast" :tag "podcast"}
{:type :link :text "example.org" :url "https://example.org/"}
{:type :reference :text "nevent1qqs9…m9d82" :uri "nostr:nevent1…"}
{:type :emoji :text ":blobcat:" :shortcode "blobcat"
 :url "https://…"}

The :id of a mention is the id of an author, as the :authors of a thread have it. It's the id of an actor on ActivityPub, a DID on the AT Protocol and a public key on Nostr. A part has a :url when the content itself names a web page for it. A reference names an event of Nostr, and an emoji is a custom emoji of ActivityPub.

The parts are for an app that writes the content in its own way.

The parts of the content of `comment`, a comment of `thread`, in their
order. Each part is a map with its :type and the :text that the content
shows for it:

    {:type :text :text "Good episode, "}
    {:type :mention :text "@ann" :id "https://example.social/users/ann"}
    {:type :hashtag :text "#podcast" :tag "podcast"}
    {:type :link :text "example.org" :url "https://example.org/"}
    {:type :reference :text "nevent1qqs9…m9d82" :uri "nostr:nevent1…"}
    {:type :emoji :text ":blobcat:" :shortcode "blobcat"
     :url "https://…"}

The :id of a mention is the id of an author, as the :authors of a
thread have it. It's the id of an actor on ActivityPub, a DID on the AT
Protocol and a public key on Nostr. A part has a :url when the content
itself names a web page for it. A reference names an event of Nostr,
and an emoji is a custom emoji of ActivityPub.

The parts are for an app that writes the content in its own way.
sourceraw docstring

protocolsclj/s

The protocols of podcast:socialInteract whose threads can be read.

The protocols of podcast:socialInteract whose threads can be read.
sourceraw docstring

readable?clj/s

(readable? interact)
(readable? interact opts)

Whether the thread of interact, a podcast:socialInteract as a map of :protocol and :uri, can be read with opts.

The protocol of the thread must be one of protocols, and its URI must have the form of that protocol. For ActivityPub, the URI is an http or https URL. For the AT Protocol, it's the at URI of a post or the URL of its page on bsky.app, and opts must name an :appview. For Nostr, it's the name of a note, or the id that NIP-73 gives a show or an episode.

Whether the thread of `interact`, a podcast:socialInteract as a map of
:protocol and :uri, can be read with `opts`.

The protocol of the thread must be one of protocols, and its URI must
have the form of that protocol. For ActivityPub, the URI is an http or
https URL. For the AT Protocol, it's the at URI of a post or the URL of
its page on bsky.app, and `opts` must name an :appview. For Nostr, it's
the name of a note, or the id that NIP-73 gives a show or an episode.
sourceraw docstring

reportclj/s

(report thread)
(report {:keys [protocol uri] :as thread} opts)

The diagnostics of thread as it was read with opts, which name the :appview for the AT Protocol.

They tell why podcast-clj didn't read the thread, a post, a collection of replies or an author. They also tell whether it left out replies, how many replies a limit of size cut, and whether a limit stopped the fetch. For Nostr, they tell how many events from a relay aren't verified.

The diagnostics of `thread` as it was read with `opts`, which name the
:appview for the AT Protocol.

They tell why podcast-clj didn't read the thread, a post, a collection
of replies or an author. They also tell whether it left out replies, how
many replies a limit of size cut, and whether a limit stopped the fetch.
For Nostr, they tell how many events from a relay aren't verified.
sourceraw docstring

requestsclj/s

(requests thread)
(requests thread opts)

The requests that thread needs next, as opts allow, in the order to send them. There are none when podcast-clj can read no more of it.

The root post comes first. Then come the posts and the collections of replies nearest to it, and then their authors. They ask for no more posts than the :max-comments of opts or default-limits allows, counted from the start of the reading. The opts also have these:

  • :appview, where the requests for the AT Protocol go
  • :status-paths, for the Mastodon API, in place of activitypub/status-paths
  • :relays, where the requests for Nostr go, besides the relays that the root names

A server that didn't answer in time, or that answered 429, gets no more requests until the next reading. Each request is a request map, and each URL comes once.

The requests that `thread` needs next, as `opts` allow, in the order to
send them. There are none when podcast-clj can read no more of it.

The root post comes first. Then come the posts and the collections of
replies nearest to it, and then their authors. They ask for no more
posts than the :max-comments of `opts` or default-limits allows,
counted from the start of the reading. The `opts` also have these:

- :appview, where the requests for the AT Protocol go
- :status-paths, for the Mastodon API, in place of
  activitypub/status-paths
- :relays, where the requests for Nostr go, besides the relays that the
  root names

A server that didn't answer in time, or that answered 429, gets no more
requests until the next reading. Each request is a request map, and
each URL comes once.
sourceraw docstring

resumedclj/s

(resumed thread)

The thread as a new reading of it starts, which an app that sends the requests itself applies first.

A failure that can pass with time kept a post, an author, a page of replies or a root from the last reading. Such a part is unread again. The failure is :retry? in the report: the request failed, or the server answered 429 or a status from 500. The thread also notes how many comments it holds, so that :max-comments counts the comments of this reading.

The `thread` as a new reading of it starts, which an app that sends the
requests itself applies first.

A failure that can pass with time kept a post, an author, a page of
replies or a root from the last reading. Such a part is unread again.
The failure is :retry? in the report: the request failed, or the server
answered 429 or a status from 500. The thread also notes how many
comments it holds, so that :max-comments counts the comments of this
reading.
sourceraw docstring

send!clj/s

(send! request)

Send the request map request over HTTP, or over a WebSocket when its URL is that of a relay.

Send the request map `request` over HTTP, or over a WebSocket when its
URL is that of a relay.
sourceraw docstring

textclj/s

(text thread comment)
(text thread comment opts)

The content of comment, a comment of thread, as plain text, with opts. It's the text that a reader sees in the Hiccup of the content, with a blank line between two paragraphs.

(text thread comment)  ; => "Is this @Ann approved? #podcast"

The text isn't the text that the author sent. A note of Nostr names a profile as a nostr: URI of 69 characters, and the text has the name of the profile in its place. The text of a post of ActivityPub has no tags of HTML.

The opts are these:

  • :links?, to put the URL of each link after it
  • :names, as hiccup takes it
The content of `comment`, a comment of `thread`, as plain text, with
`opts`. It's the text that a reader sees in the Hiccup of the content,
with a blank line between two paragraphs.

    (text thread comment)  ; => "Is this @Ann approved? #podcast"

The text isn't the text that the author sent. A note of Nostr names a
profile as a nostr: URI of 69 characters, and the text has the name of
the profile in its place. The text of a post of ActivityPub has no tags
of HTML.

The `opts` are these:

- :links?, to put the URL of each link after it
- :names, as hiccup takes it
sourceraw docstring

threadclj/s

(thread feed item)
(thread feed item opts)

The unread thread of comments on item of feed that can be read with opts, or nil.

It's the first podcast:socialInteract of the item, by priority, that can be read. When the item names none, it's the first of the feed. There's none when the podcaster wants no public comments.

The unread thread of comments on `item` of `feed` that can be read with
`opts`, or nil.

It's the first podcast:socialInteract of the item, by priority, that can
be read. When the item names none, it's the first of the feed. There's
none when the podcaster wants no public comments.
sourceraw docstring

treeclj/s

(tree thread)
(tree {:keys [root comments authors] :as thread} {:keys [hidden?] :as opts})

The comments of thread in the shape to show them, with opts, or nil until the root post is read.

The tree is the root post, with its :author as the map of its author. Its :replies are comments of the same shape, oldest first. An unread reply isn't in the tree, and neither are the replies below it. The tree leaves out the replies that the author of the root post hid, unless opts has :hidden?. A deleted post, or a post that podcast-clj didn't get, is in the tree only when a reply below it is. The tree ends 256 levels down.

The comments of `thread` in the shape to show them, with `opts`, or nil
until the root post is read.

The tree is the root post, with its :author as the map of its author.
Its :replies are comments of the same shape, oldest first. An unread
reply isn't in the tree, and neither are the replies below it. The tree
leaves out the replies that the author of the root post hid, unless
`opts` has :hidden?. A deleted post, or a post that podcast-clj didn't
get, is in the tree only when a reply below it is. The tree ends 256
levels down.
sourceraw docstring

with-exchangesclj/s

(with-exchanges thread exchanges at)

The thread with the exchanges of one of its requests read into it at the instant at.

The exchanges are the exchange of the request and, for a collection, the exchanges of the pages after it. Each exchange is a map of the :request and the :response. The response is a response map, or a map of the :error when the request failed. Such a response is also :timeout? when the request took too long, and :retry? when a later request can get an answer.

The `thread` with the `exchanges` of one of its requests read into it at
the instant `at`.

The exchanges are the exchange of the request and, for a collection, the
exchanges of the pages after it. Each exchange is a map of the :request
and the :response. The response is a response map, or a map of the
:error when the request failed. Such a response is also :timeout? when
the request took too long, and :retry? when a later request can get an
answer.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close