aboutsummaryrefslogtreecommitdiff
path: root/comments.go
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-07-15 17:26:22 -0500
committerGitHub <[email protected]>2026-07-15 17:26:22 -0500
commitb80180e77eebad8476aa66c8a5433764da672c62 (patch)
tree3078b122345c45a4bd8a5477ae21ad63fa56f13d /comments.go
parentb70d3dd588c8f9d6c9ed0ff111af06b76fa1f6ba (diff)
downloaddevianter-b80180e77eebad8476aa66c8a5433764da672c62.tar.gz
devianter-b80180e77eebad8476aa66c8a5433764da672c62.tar.bz2
devianter-b80180e77eebad8476aa66c8a5433764da672c62.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.go85
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()
+}