diff --git a/formatter/formatter.go b/formatter/formatter.go index 69e4a6e..7a60557 100644 --- a/formatter/formatter.go +++ b/formatter/formatter.go @@ -20,11 +20,8 @@ func Format(data map[string]interface{}) (string, error) { return "", fmt.Errorf("error formatting JSON: %v", err) } - // Convert to string for gjson parsing - jsonStr := string(jsonData) - // Create a summary based on the object type - summary := createSummary(jsonStr) + summary := createSummary(jsonData) // Combine the full JSON first, followed by the summary at the bottom result := fmt.Sprintf("%s\n\n%s", string(jsonData), summary) @@ -32,13 +29,13 @@ func Format(data map[string]interface{}) (string, error) { } // createSummary generates a human-readable summary of the ActivityPub object or nodeinfo -func createSummary(jsonStr string) string { +func createSummary(jsonStr []byte) string { // Try to detect nodeinfo - if gjson.Get(jsonStr, "software.name").Exists() && gjson.Get(jsonStr, "version").Exists() { + if gjson.GetBytes(jsonStr, "software.name").Exists() && gjson.GetBytes(jsonStr, "version").Exists() { return nodeInfoSummary(jsonStr) } - objectType := gjson.Get(jsonStr, "type").String() + objectType := gjson.GetBytes(jsonStr, "type").String() // Build a header with the object type bold := color.New(color.Bold).SprintFunc() @@ -50,7 +47,7 @@ func createSummary(jsonStr string) string { header := fmt.Sprintf("%s: %s\n", bold("Type"), cyan(objectType)) // Add sensitive content warning if present - if gjson.Get(jsonStr, "sensitive").Bool() { + if gjson.GetBytes(jsonStr, "sensitive").Bool() { header += fmt.Sprintf("%s: %s\n", red(bold("WARNING")), red("Sensitive Content!")) } @@ -59,7 +56,7 @@ func createSummary(jsonStr string) string { summaryParts = append(summaryParts, header) // Add ID if available - if id := gjson.Get(jsonStr, "id").String(); id != "" { + if id := gjson.GetBytes(jsonStr, "id").String(); id != "" { summaryParts = append(summaryParts, fmt.Sprintf("%s: %s", bold("Original URL"), green(id))) } @@ -85,7 +82,7 @@ func createSummary(jsonStr string) string { } // nodeInfoSummary generates a summary for nodeinfo objects -func nodeInfoSummary(jsonStr string) string { +func nodeInfoSummary(jsonStr []byte) string { bold := color.New(color.Bold).SprintFunc() cyan := color.New(color.FgCyan).SprintFunc() green := color.New(color.FgGreen).SprintFunc() @@ -93,14 +90,14 @@ func nodeInfoSummary(jsonStr string) string { red := color.New(color.FgRed).SprintFunc() parts := []string{} - parts = append(parts, fmt.Sprintf("%s: %s", bold("NodeInfo Version"), cyan(gjson.Get(jsonStr, "version").String()))) - parts = append(parts, fmt.Sprintf("%s: %s %s", bold("Software"), green(gjson.Get(jsonStr, "software.name").String()), yellow(gjson.Get(jsonStr, "software.version").String()))) - if repo := gjson.Get(jsonStr, "software.repository").String(); repo != "" { + parts = append(parts, fmt.Sprintf("%s: %s", bold("NodeInfo Version"), cyan(gjson.GetBytes(jsonStr, "version").String()))) + parts = append(parts, fmt.Sprintf("%s: %s %s", bold("Software"), green(gjson.GetBytes(jsonStr, "software.name").String()), yellow(gjson.GetBytes(jsonStr, "software.version").String()))) + if repo := gjson.GetBytes(jsonStr, "software.repository").String(); repo != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Repository"), green(repo))) } // Color openRegistrations green if true, red if false - openReg := gjson.Get(jsonStr, "openRegistrations") + openReg := gjson.GetBytes(jsonStr, "openRegistrations") openRegStr := openReg.String() var openRegColored string if openReg.Exists() { @@ -113,66 +110,66 @@ func nodeInfoSummary(jsonStr string) string { openRegColored = openRegStr } parts = append(parts, fmt.Sprintf("%s: %s", bold("Open Registrations"), openRegColored)) - if protocols := gjson.Get(jsonStr, "protocols").Array(); len(protocols) > 0 { + if protocols := gjson.GetBytes(jsonStr, "protocols").Array(); len(protocols) > 0 { var plist []string for _, p := range protocols { plist = append(plist, p.String()) } parts = append(parts, fmt.Sprintf("%s: %s", bold("Protocols"), strings.Join(plist, ", "))) } - if users := gjson.Get(jsonStr, "usage.users.total").Int(); users > 0 { - activeMonth := gjson.Get(jsonStr, "usage.users.activeMonth").Int() - activeHalfyear := gjson.Get(jsonStr, "usage.users.activeHalfyear").Int() + if users := gjson.GetBytes(jsonStr, "usage.users.total").Int(); users > 0 { + activeMonth := gjson.GetBytes(jsonStr, "usage.users.activeMonth").Int() + activeHalfyear := gjson.GetBytes(jsonStr, "usage.users.activeHalfyear").Int() parts = append(parts, fmt.Sprintf("%s: %d (active month: %d, halfyear: %d)", bold("Users"), users, activeMonth, activeHalfyear)) } - if posts := gjson.Get(jsonStr, "usage.localPosts").Int(); posts > 0 { + if posts := gjson.GetBytes(jsonStr, "usage.localPosts").Int(); posts > 0 { parts = append(parts, fmt.Sprintf("%s: %d", bold("Local Posts"), posts)) } - if comments := gjson.Get(jsonStr, "usage.localComments").Int(); comments > 0 { + if comments := gjson.GetBytes(jsonStr, "usage.localComments").Int(); comments > 0 { parts = append(parts, fmt.Sprintf("%s: %d", bold("Local Comments"), comments)) } - if nodeName := gjson.Get(jsonStr, "metadata.nodeName").String(); nodeName != "" { + if nodeName := gjson.GetBytes(jsonStr, "metadata.nodeName").String(); nodeName != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Node Name"), cyan(nodeName))) } - if nodeDesc := gjson.Get(jsonStr, "metadata.nodeDescription").String(); nodeDesc != "" { + if nodeDesc := gjson.GetBytes(jsonStr, "metadata.nodeDescription").String(); nodeDesc != "" { parts = append(parts, fmt.Sprintf("%s:\n%s", bold("Node Description"), nodeDesc)) } return strings.Join(parts, "\n") } // formatActor formats actor-type objects (Person, Service, etc.) -func formatActor(jsonStr string, parts []string, bold, cyan, green, red, yellow func(a ...interface{}) string) []string { - if name := gjson.Get(jsonStr, "name").String(); name != "" { +func formatActor(jsonStr []byte, parts []string, bold, cyan, green, red, yellow func(a ...interface{}) string) []string { + if name := gjson.GetBytes(jsonStr, "name").String(); name != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Name"), cyan(name))) } - if preferredUsername := gjson.Get(jsonStr, "preferredUsername").String(); preferredUsername != "" { + if preferredUsername := gjson.GetBytes(jsonStr, "preferredUsername").String(); preferredUsername != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Username"), red(preferredUsername))) } - if url := gjson.Get(jsonStr, "url").String(); url != "" { + if url := gjson.GetBytes(jsonStr, "url").String(); url != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("URL"), green(url))) } - iconUrl := gjson.Get(jsonStr, "icon.url").String() + iconUrl := gjson.GetBytes(jsonStr, "icon.url").String() if iconUrl != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Avatar"), green(iconUrl))) } - if summary := gjson.Get(jsonStr, "summary").String(); summary != "" { + if summary := gjson.GetBytes(jsonStr, "summary").String(); summary != "" { md := htmlToMarkdown(summary) parts = append(parts, fmt.Sprintf("%s:\n%s", bold("Summary"), renderMarkdown(md))) } - if published := gjson.Get(jsonStr, "published").String(); published != "" { + if published := gjson.GetBytes(jsonStr, "published").String(); published != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Published"), yellow(formatDate(published)))) } - if followers := gjson.Get(jsonStr, "followers").String(); followers != "" { + if followers := gjson.GetBytes(jsonStr, "followers").String(); followers != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Followers"), green(followers))) } - if following := gjson.Get(jsonStr, "following").String(); following != "" { + if following := gjson.GetBytes(jsonStr, "following").String(); following != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Following"), green(following))) } @@ -180,13 +177,13 @@ func formatActor(jsonStr string, parts []string, bold, cyan, green, red, yellow } // formatContent formats content-type objects (Note, Article, Page, etc.) -func formatContent(jsonStr string, parts []string, bold, green, yellow func(a ...interface{}) string) []string { +func formatContent(jsonStr []byte, parts []string, bold, green, yellow func(a ...interface{}) string) []string { // Show the name/title if present (especially for Page/thread) - if name := gjson.Get(jsonStr, "name").String(); name != "" { + if name := gjson.GetBytes(jsonStr, "name").String(); name != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Title"), name)) } - if content := gjson.Get(jsonStr, "content").String(); content != "" { + if content := gjson.GetBytes(jsonStr, "content").String(); content != "" { md := htmlToMarkdown(content) // Truncate the content if its too big. if len(md) > 1200 { @@ -196,7 +193,7 @@ func formatContent(jsonStr string, parts []string, bold, green, yellow func(a .. } // Check for attachments (images, videos, etc.) - attachments := gjson.Get(jsonStr, "attachment").Array() + attachments := gjson.GetBytes(jsonStr, "attachment").Array() if len(attachments) > 0 { parts = append(parts, fmt.Sprintf("%s:", bold("Attachments"))) for i, attachment := range attachments { @@ -221,7 +218,7 @@ func formatContent(jsonStr string, parts []string, bold, green, yellow func(a .. parts = append(parts, attachmentInfo) // For type Page and attachment type Link, show href if present - objectType := gjson.Get(jsonStr, "type").String() + objectType := gjson.GetBytes(jsonStr, "type").String() if objectType == "Page" && attachmentType == "Link" && href != "" { parts = append(parts, fmt.Sprintf(" URL: %s", green(href))) } else if url != "" { @@ -230,39 +227,39 @@ func formatContent(jsonStr string, parts []string, bold, green, yellow func(a .. } } - if published := gjson.Get(jsonStr, "published").String(); published != "" { + if published := gjson.GetBytes(jsonStr, "published").String(); published != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Published"), yellow(formatDate(published)))) } - if updated := gjson.Get(jsonStr, "updated").String(); updated != "" { + if updated := gjson.GetBytes(jsonStr, "updated").String(); updated != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Updated"), yellow(formatDate(updated)))) } - if attributedTo := gjson.Get(jsonStr, "attributedTo").String(); attributedTo != "" { + if attributedTo := gjson.GetBytes(jsonStr, "attributedTo").String(); attributedTo != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Author"), green(attributedTo))) } - if to := gjson.Get(jsonStr, "to").Array(); len(to) > 0 { + if to := gjson.GetBytes(jsonStr, "to").Array(); len(to) > 0 { parts = append(parts, fmt.Sprintf("%s: %s", bold("To"), green(formatArray(to)))) } - if cc := gjson.Get(jsonStr, "cc").Array(); len(cc) > 0 { + if cc := gjson.GetBytes(jsonStr, "cc").Array(); len(cc) > 0 { parts = append(parts, fmt.Sprintf("%s: %s", bold("CC"), green(formatArray(cc)))) } - if inReplyTo := gjson.Get(jsonStr, "inReplyTo").String(); inReplyTo != "" { + if inReplyTo := gjson.GetBytes(jsonStr, "inReplyTo").String(); inReplyTo != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("In Reply To"), green(inReplyTo))) } // Include endTime for Question type - if endTime := gjson.Get(jsonStr, "endTime").String(); endTime != "" { + if endTime := gjson.GetBytes(jsonStr, "endTime").String(); endTime != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("End Time"), yellow(formatDate(endTime)))) } // Include options (oneOf/anyOf) for Question type - options := gjson.Get(jsonStr, "oneOf").Array() + options := gjson.GetBytes(jsonStr, "oneOf").Array() if len(options) == 0 { - options = gjson.Get(jsonStr, "anyOf").Array() + options = gjson.GetBytes(jsonStr, "anyOf").Array() } if len(options) > 0 { parts = append(parts, fmt.Sprintf("%s:", bold("Poll Options"))) @@ -276,24 +273,24 @@ func formatContent(jsonStr string, parts []string, bold, green, yellow func(a .. } // formatActivity formats activity-type objects (Create, Like, etc.) -func formatActivity(jsonStr string, parts []string, bold, green, yellow func(a ...interface{}) string) []string { - if actor := gjson.Get(jsonStr, "actor").String(); actor != "" { +func formatActivity(jsonStr []byte, parts []string, bold, green, yellow func(a ...interface{}) string) []string { + if actor := gjson.GetBytes(jsonStr, "actor").String(); actor != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Actor"), actor)) } - if object := gjson.Get(jsonStr, "object").String(); object != "" { + if object := gjson.GetBytes(jsonStr, "object").String(); object != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Object"), object)) - } else if gjson.Get(jsonStr, "object").IsObject() { - objectType := gjson.Get(jsonStr, "object.type").String() + } else if gjson.GetBytes(jsonStr, "object").IsObject() { + objectType := gjson.GetBytes(jsonStr, "object.type").String() parts = append(parts, fmt.Sprintf("%s: %s", bold("Object Type"), yellow(objectType))) - if content := gjson.Get(jsonStr, "object.content").String(); content != "" { + if content := gjson.GetBytes(jsonStr, "object.content").String(); content != "" { md := htmlToMarkdown(content) parts = append(parts, fmt.Sprintf("%s:\n%s", bold("Content"), renderMarkdown(md))) } // Check for attachments in the object - attachments := gjson.Get(jsonStr, "object.attachment").Array() + attachments := gjson.GetBytes(jsonStr, "object.attachment").Array() if len(attachments) > 0 { parts = append(parts, fmt.Sprintf("%s:", bold("Attachments"))) for i, attachment := range attachments { @@ -323,11 +320,11 @@ func formatActivity(jsonStr string, parts []string, bold, green, yellow func(a . } } - if published := gjson.Get(jsonStr, "published").String(); published != "" { + if published := gjson.GetBytes(jsonStr, "published").String(); published != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Published"), yellow(formatDate(published)))) } - if target := gjson.Get(jsonStr, "target").String(); target != "" { + if target := gjson.GetBytes(jsonStr, "target").String(); target != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Target"), target)) } @@ -335,15 +332,15 @@ func formatActivity(jsonStr string, parts []string, bold, green, yellow func(a . } // formatCollection formats collection-type objects -func formatCollection(jsonStr string, parts []string, bold, green, yellow func(a ...interface{}) string) []string { - if totalItems := gjson.Get(jsonStr, "totalItems").Int(); totalItems > 0 { +func formatCollection(jsonStr []byte, parts []string, bold, green, yellow func(a ...interface{}) string) []string { + if totalItems := gjson.GetBytes(jsonStr, "totalItems").Int(); totalItems > 0 { parts = append(parts, fmt.Sprintf("%s: %d", bold("Total Items"), totalItems)) } // Show first few items if available - items := gjson.Get(jsonStr, "items").Array() + items := gjson.GetBytes(jsonStr, "items").Array() if len(items) == 0 { - items = gjson.Get(jsonStr, "orderedItems").Array() + items = gjson.GetBytes(jsonStr, "orderedItems").Array() } if len(items) > 0 { @@ -366,7 +363,7 @@ func formatCollection(jsonStr string, parts []string, bold, green, yellow func(a } } - if published := gjson.Get(jsonStr, "published").String(); published != "" { + if published := gjson.GetBytes(jsonStr, "published").String(); published != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Published"), yellow(formatDate(published)))) } @@ -374,24 +371,24 @@ func formatCollection(jsonStr string, parts []string, bold, green, yellow func(a } // formatMedia formats media-type objects (Image, Video, etc.) -func formatMedia(jsonStr string, parts []string, bold, green, yellow func(a ...interface{}) string) []string { - if name := gjson.Get(jsonStr, "name").String(); name != "" { +func formatMedia(jsonStr []byte, parts []string, bold, green, yellow func(a ...interface{}) string) []string { + if name := gjson.GetBytes(jsonStr, "name").String(); name != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Title"), name)) } - if url := gjson.Get(jsonStr, "url").String(); url != "" { + if url := gjson.GetBytes(jsonStr, "url").String(); url != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("URL"), green(url))) } - if duration := gjson.Get(jsonStr, "duration").String(); duration != "" { + if duration := gjson.GetBytes(jsonStr, "duration").String(); duration != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Duration"), duration)) } - if published := gjson.Get(jsonStr, "published").String(); published != "" { + if published := gjson.GetBytes(jsonStr, "published").String(); published != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Published"), yellow(formatDate(published)))) } - if attributedTo := gjson.Get(jsonStr, "attributedTo").String(); attributedTo != "" { + if attributedTo := gjson.GetBytes(jsonStr, "attributedTo").String(); attributedTo != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Author"), attributedTo)) } @@ -399,25 +396,25 @@ func formatMedia(jsonStr string, parts []string, bold, green, yellow func(a ...i } // formatEvent formats event-type objects -func formatEvent(jsonStr string, parts []string, bold, yellow func(a ...interface{}) string) []string { - if name := gjson.Get(jsonStr, "name").String(); name != "" { +func formatEvent(jsonStr []byte, parts []string, bold, yellow func(a ...interface{}) string) []string { + if name := gjson.GetBytes(jsonStr, "name").String(); name != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Title"), name)) } - if content := gjson.Get(jsonStr, "content").String(); content != "" { + if content := gjson.GetBytes(jsonStr, "content").String(); content != "" { md := htmlToMarkdown(content) parts = append(parts, fmt.Sprintf("%s:\n%s", bold("Description"), renderMarkdown(md))) } - if startTime := gjson.Get(jsonStr, "startTime").String(); startTime != "" { + if startTime := gjson.GetBytes(jsonStr, "startTime").String(); startTime != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Start Time"), yellow(formatDate(startTime)))) } - if endTime := gjson.Get(jsonStr, "endTime").String(); endTime != "" { + if endTime := gjson.GetBytes(jsonStr, "endTime").String(); endTime != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("End Time"), yellow(formatDate(endTime)))) } - if location := gjson.Get(jsonStr, "location").String(); location != "" { + if location := gjson.GetBytes(jsonStr, "location").String(); location != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Location"), location)) } @@ -425,12 +422,12 @@ func formatEvent(jsonStr string, parts []string, bold, yellow func(a ...interfac } // formatTombstone formats tombstone-type objects -func formatTombstone(jsonStr string, parts []string, bold, green, yellow func(a ...interface{}) string) []string { - if formerType := gjson.Get(jsonStr, "formerType").String(); formerType != "" { +func formatTombstone(jsonStr []byte, parts []string, bold, green, yellow func(a ...interface{}) string) []string { + if formerType := gjson.GetBytes(jsonStr, "formerType").String(); formerType != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Former Type"), formerType)) } - if deleted := gjson.Get(jsonStr, "deleted").String(); deleted != "" { + if deleted := gjson.GetBytes(jsonStr, "deleted").String(); deleted != "" { parts = append(parts, fmt.Sprintf("%s: %s", bold("Deleted"), yellow(formatDate(deleted)))) }