diff options
| author | Christian Cleberg <[email protected]> | 2026-07-15 17:26:22 -0500 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-07-15 17:26:22 -0500 |
| commit | b80180e77eebad8476aa66c8a5433764da672c62 (patch) | |
| tree | 3078b122345c45a4bd8a5477ae21ad63fa56f13d /comments.go | |
| parent | b70d3dd588c8f9d6c9ed0ff111af06b76fa1f6ba (diff) | |
| download | devianter-0.3.2.tar.gz devianter-0.3.2.tar.bz2 devianter-0.3.2.zip | |
docs: document the exported API, and fix five bugs found writing it (#2)v0.3.2
* docs: translate Russian comments and document exported API
The package carried 35 Russian comments and no doc comments on its
exported identifiers, so pkg.go.dev rendered a bare list of signatures.
Translate the Russian to English and give every exported identifier a
doc comment in godoc form. Add doc.go with a package overview covering
the guest-session requirement, the Error-as-struct convention, and
CloudFront blocking.
The comments record what the signatures cannot: that UpdateCSRF must run
first and that its token expires, that Error is a struct and so is never
nil, that Thread is one comment rather than a thread, and that
GetComments' page parameter costs one request per page.
Comments only; the struct field realignment is gofmt's, from doc comments
splitting alignment groups.
Refs #1
* add CODEOWNERS
* fix: stop the library killing its caller, and flatten text correctly
Writing doc comments for the exported API surfaced behavior too alarming
to document and leave alone. A library should never terminate the process
that imports it.
Crashes:
- GetComments panicked on a comment with an empty body: the shape check
read m[0] and m[len(m)-1] with no length check.
- Group.Favourites and Group.Gallery indexed their variadic folderid
without checking len, so omitting it — which the signature invites —
panicked. Omitting it now means 0.
- AEmedia and PerformSearch called log.Fatalln on a bad argument rune,
terminating the caller. Both now return an error, which their
signatures already allowed for.
Draft.js flattening, in the same code both callers share:
- The block loop assigned rather than accumulated, so every block but the
last was dropped and a multi-paragraph body came back as its closing
line alone. Blocks are block-level elements, so join them with newlines.
- GetDeviation guarded on txt[1] == '{', the second character of a body
that opens with {"blocks". It never fired, so descriptions were handed
back as raw Draft.js JSON. It now shares flattenComment with
GetComments rather than keeping its own copy.
Both flattening fixes change output for existing callers.
Refs #1
Diffstat (limited to 'comments.go')
| -rw-r--r-- | comments.go | 85 |
1 files changed, 64 insertions, 21 deletions
diff --git a/comments.go b/comments.go index 1bb1f99..6e41f38 100644 --- a/comments.go +++ b/comments.go @@ -4,18 +4,28 @@ import ( "encoding/json" "net/url" "strconv" + "strings" ) +// Thread is a single comment, despite the name. Replies are not nested inside +// it: a thread arrives flattened into [Comments].Thread, and the shape is +// recovered through Parent, which holds the ID of the comment being replied to +// and is 0 for a top-level comment. type Thread struct { Replies, Likes int ID int `json:"commentId"` Parent int `json:"parentId"` Posted timeStamp + // Author reports whether the commenter is the author of the deviation being + // commented on. Author bool `json:"isAuthorHighlited"` Desctiption string - Comment string + + // Comment is the comment's plain text, which [GetComments] extracts from + // TextContent. Prefer it; TextContent is the unprocessed original. + Comment string TextContent Text @@ -25,6 +35,10 @@ type Thread struct { } } +// Comments is one page of comments. Thread holds the comments themselves, +// flattened rather than nested; Total counts every comment on the item, not just +// this page. Cursor resumes from the end of this page and HasMore reports +// whether anything remains. type Comments struct { Cursor string PrevOffset int @@ -34,7 +48,17 @@ type Comments struct { Thread []Thread } -// 1 - комментарии поста; 4 - комментарии на стене группы или пользователя +// GetComments retrieves comments on an item, 50 per page, with each comment's +// plain text extracted into [Thread].Comment. +// +// typ selects what postid refers to: 1 for comments on a deviation, 4 for those +// on a user's or group's profile wall. cursor resumes from a previous call's +// [Comments].Cursor; pass an empty string to start from the newest comment. +// +// page is an offset from cursor rather than an absolute page number, and it is +// walked one request at a time: page 5 costs six round-trips and returns only +// the sixth page. Paginating by feeding each result's Cursor back in with page 0 +// costs one request per page, and is the cheaper way to walk a long thread. func GetComments(postid string, cursor string, page int, typ int) (cmmts Comments, err Error) { for x := 0; x <= page; x++ { err = ujson( @@ -46,28 +70,47 @@ func GetComments(postid string, cursor string, page int, typ int) (cmmts Comment cursor = cmmts.Cursor - // парсинг json внутри json for i := 0; i < len(cmmts.Thread); i++ { - m, l := cmmts.Thread[i].TextContent.Html.Markup, len(cmmts.Thread[i].TextContent.Html.Markup) - cmmts.Thread[i].Comment = m - - // если начало строки {, а конец }, то срабатывает этот иф - if m[0] == '{' && m[l-1] == '}' { - var content struct { - Blocks []struct { - Text string - } - } - - e := json.Unmarshal([]byte(m), &content) - try(e) - - for _, a := range content.Blocks { - cmmts.Thread[i].Comment = a.Text - } - } + cmmts.Thread[i].Comment = flattenComment(cmmts.Thread[i].TextContent.Html.Markup) } } return } + +// flattenComment renders a body of user-written markup as plain text, be it a +// comment or a deviation's description. Bodies are JSON inside JSON: newer ones +// are a Draft.js document encoded into the markup string, older ones are plain +// HTML, which passes through unchanged. Markup that does not parse, and empty +// markup, also pass through. +// +// A Draft.js document is a list of blocks, which are block-level elements +// (paragraphs, list items); they are joined with newlines, one block per line. +func flattenComment(m string) string { + l := len(m) + if l == 0 || m[0] != '{' || m[l-1] != '}' { + return m + } + + var content struct { + Blocks []struct { + Text string + } + } + + e := json.Unmarshal([]byte(m), &content) + try(e) + + if len(content.Blocks) == 0 { + return m + } + + var b strings.Builder + for i, a := range content.Blocks { + if i > 0 { + b.WriteString("\n") + } + b.WriteString(a.Text) + } + return b.String() +} |
