summaryrefslogtreecommitdiffstats
path: root/internal/formatter/x.go
diff options
context:
space:
mode:
authors <[email protected]>2025-11-13 14:43:15 -0500
committers <[email protected]>2025-11-13 14:43:15 -0500
commit344a6f6415c3c1b593677adec3b8844e0839971b (patch)
treeb05291ecdf21917b27e9e234eeb997c2706966d5 /internal/formatter/x.go
parenta5fc01a03753c9a18ddeaf13610dd99b4b311b80 (diff)
downloaddborg-344a6f6415c3c1b593677adec3b8844e0839971b.tar.gz
dborg-344a6f6415c3c1b593677adec3b8844e0839971b.zip
created pretty printing for all commandsv1.0.0
Diffstat (limited to 'internal/formatter/x.go')
-rw-r--r--internal/formatter/x.go379
1 files changed, 379 insertions, 0 deletions
diff --git a/internal/formatter/x.go b/internal/formatter/x.go
new file mode 100644
index 0000000..f701797
--- /dev/null
+++ b/internal/formatter/x.go
@@ -0,0 +1,379 @@
+package formatter
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "strings"
+ "text/tabwriter"
+
+ "git.db.org.ai/dborg/internal/models"
+)
+
+func FormatXHistory(resp *models.XResponse, asJSON bool) (string, error) {
+ if asJSON {
+ data, err := json.MarshalIndent(resp, "", " ")
+ if err != nil {
+ return "", fmt.Errorf("failed to marshal JSON: %w", err)
+ }
+ return string(data), nil
+ }
+
+ var buf bytes.Buffer
+
+ if resp.Error != "" {
+ fmt.Fprintf(&buf, "%s\n", Red(fmt.Sprintf("Error: %s", resp.Error)))
+ return buf.String(), nil
+ }
+
+ fmt.Fprintf(&buf, "%s\n\n", Bold(Cyan(fmt.Sprintf("Username History for @%s", resp.Username))))
+
+ if len(resp.PreviousUsernames) == 0 {
+ fmt.Fprintf(&buf, "%s\n", Gray("No previous usernames found"))
+ } else {
+ w := tabwriter.NewWriter(&buf, 0, 0, 2, ' ', 0)
+ fmt.Fprintf(w, "%s\t%s\t%s\n", Bold("#"), Bold("Username"), Bold("Time Ago"))
+
+ for i, u := range resp.PreviousUsernames {
+ fmt.Fprintf(w, "%s\t%s\t%s\n",
+ Yellow(fmt.Sprintf("%d", i+1)),
+ Cyan(fmt.Sprintf("@%s", u.Username)),
+ Gray(u.TimeAgo))
+ }
+ w.Flush()
+ }
+
+ fmt.Fprintf(&buf, "\n%s ", Blue("Credits Remaining:"))
+ if resp.Credits.Unlimited {
+ fmt.Fprintf(&buf, "%s\n", Green("Unlimited"))
+ } else {
+ fmt.Fprintf(&buf, "%s\n", FormatCredits(int64(resp.Credits.Remaining)))
+ }
+
+ return buf.String(), nil
+}
+
+func FormatXTweets(streamResp *models.TweetsStreamResponse, asJSON bool) (string, error) {
+ if asJSON {
+ data, err := json.MarshalIndent(streamResp, "", " ")
+ if err != nil {
+ return "", fmt.Errorf("failed to marshal JSON: %w", err)
+ }
+ return string(data), nil
+ }
+
+ var buf bytes.Buffer
+
+ if streamResp.Error != "" {
+ fmt.Fprintf(&buf, "%s\n", Red(fmt.Sprintf("Error: %s", streamResp.Error)))
+ return buf.String(), nil
+ }
+
+ if streamResp.Tweet != nil {
+ tweet := streamResp.Tweet
+
+ if tweet.Name != "" {
+ fmt.Fprintf(&buf, "%s ", Cyan(tweet.Name))
+ }
+ if tweet.Handle != "" {
+ fmt.Fprintf(&buf, "%s", Gray(fmt.Sprintf("(@%s)", tweet.Handle)))
+ }
+ if tweet.Name != "" || tweet.Handle != "" {
+ fmt.Fprintf(&buf, "\n")
+ }
+
+ if tweet.Text != "" {
+ fmt.Fprintf(&buf, "%s\n", Green(tweet.Text))
+ }
+
+ if tweet.Type != "" {
+ fmt.Fprintf(&buf, "%s %s\n", Dim("Type:"), tweet.Type)
+ }
+
+ if tweet.URL != "" {
+ fmt.Fprintf(&buf, "%s\n", Blue(tweet.URL))
+ }
+
+ fmt.Fprintf(&buf, "%s\n", Dim("───"))
+ }
+
+ if streamResp.Progress != nil {
+ fmt.Fprintf(&buf, "%s %s/%s\n",
+ Dim("Progress:"),
+ Yellow(fmt.Sprintf("%d", streamResp.Progress.Current)),
+ Yellow(fmt.Sprintf("%d", streamResp.Progress.Total)))
+ }
+
+ if streamResp.Complete != nil {
+ fmt.Fprintf(&buf, "\n%s\n", Bold(Green("Complete!")))
+ fmt.Fprintf(&buf, "%s %s\n", Dim("Duration:"), streamResp.Complete.Duration)
+ fmt.Fprintf(&buf, "%s %s\n",
+ Dim("Total Fetched:"),
+ Green(fmt.Sprintf("%d", streamResp.Complete.TotalFetched)))
+ if streamResp.Complete.TotalFailed > 0 {
+ fmt.Fprintf(&buf, "%s %s\n",
+ Dim("Total Failed:"),
+ Yellow(fmt.Sprintf("%d", streamResp.Complete.TotalFailed)))
+ }
+ }
+
+ return buf.String(), nil
+}
+
+func FormatXFirstFollowers(resp *models.FirstFollowersResponse, asJSON bool) (string, error) {
+ if asJSON {
+ data, err := json.MarshalIndent(resp, "", " ")
+ if err != nil {
+ return "", fmt.Errorf("failed to marshal JSON: %w", err)
+ }
+ return string(data), nil
+ }
+
+ var buf bytes.Buffer
+
+ fmt.Fprintf(&buf, "%s\n\n", Bold(Cyan(fmt.Sprintf("First 20 Followers of @%s", resp.Username))))
+
+ if len(resp.Followers) == 0 {
+ fmt.Fprintf(&buf, "%s\n", Gray("No followers found"))
+ } else {
+ w := tabwriter.NewWriter(&buf, 0, 0, 2, ' ', 0)
+ fmt.Fprintf(w, "%s\t%s\t%s\n", Bold("#"), Bold("Username"), Bold("Name"))
+
+ for _, f := range resp.Followers {
+ fmt.Fprintf(w, "%s\t%s\t%s\n",
+ Yellow(fmt.Sprintf("%d", f.Number)),
+ Cyan(fmt.Sprintf("@%s", f.Username)),
+ Green(f.Name))
+ }
+ w.Flush()
+ }
+
+ fmt.Fprintf(&buf, "\n%s ", Blue("Credits Remaining:"))
+ if resp.Credits.Unlimited {
+ fmt.Fprintf(&buf, "%s\n", Green("Unlimited"))
+ } else {
+ fmt.Fprintf(&buf, "%s\n", FormatCredits(int64(resp.Credits.Remaining)))
+ }
+
+ return buf.String(), nil
+}
+
+func FormatXNotableFollowers(resp *models.NotableFollowersResponse, asJSON bool) (string, error) {
+ if asJSON {
+ data, err := json.MarshalIndent(resp, "", " ")
+ if err != nil {
+ return "", fmt.Errorf("failed to marshal JSON: %w", err)
+ }
+ return string(data), nil
+ }
+
+ var buf bytes.Buffer
+
+ fmt.Fprintf(&buf, "%s\n\n", Bold(Cyan(fmt.Sprintf("Notable Followers of @%s", resp.Username))))
+
+ if len(resp.Followers) == 0 {
+ fmt.Fprintf(&buf, "%s\n", Gray("No notable followers found"))
+ } else {
+ w := tabwriter.NewWriter(&buf, 0, 0, 2, ' ', 0)
+ fmt.Fprintf(w, "%s\t%s\t%s\n", Bold("Username"), Bold("Followers"), Bold("Score"))
+
+ for _, f := range resp.Followers {
+ fmt.Fprintf(w, "%s\t%s\t%s\n",
+ Cyan(fmt.Sprintf("@%s", f.Username)),
+ Green(f.FollowerCount),
+ Magenta(fmt.Sprintf("%.2f", f.Score)))
+ }
+ w.Flush()
+ }
+
+ fmt.Fprintf(&buf, "\n%s ", Blue("Credits Remaining:"))
+ if resp.Credits.Unlimited {
+ fmt.Fprintf(&buf, "%s\n", Green("Unlimited"))
+ } else {
+ fmt.Fprintf(&buf, "%s\n", FormatCredits(int64(resp.Credits.Remaining)))
+ }
+
+ return buf.String(), nil
+}
+
+func FormatXReplies(streamResp *models.TweetsStreamResponse, asJSON bool) (string, error) {
+ if asJSON {
+ data, err := json.MarshalIndent(streamResp, "", " ")
+ if err != nil {
+ return "", fmt.Errorf("failed to marshal JSON: %w", err)
+ }
+ return string(data), nil
+ }
+
+ var buf bytes.Buffer
+
+ if streamResp.Error != "" {
+ fmt.Fprintf(&buf, "%s\n", Red(fmt.Sprintf("Error: %s", streamResp.Error)))
+ return buf.String(), nil
+ }
+
+ if streamResp.Tweet != nil {
+ tweet := streamResp.Tweet
+
+ if tweet.Name != "" {
+ fmt.Fprintf(&buf, "%s ", Cyan(tweet.Name))
+ }
+ if tweet.Handle != "" {
+ fmt.Fprintf(&buf, "%s", Gray(fmt.Sprintf("(@%s)", tweet.Handle)))
+ }
+ if tweet.Name != "" || tweet.Handle != "" {
+ fmt.Fprintf(&buf, "\n")
+ }
+
+ if tweet.Text != "" {
+ lines := strings.Split(tweet.Text, "\n")
+ for _, line := range lines {
+ if len(line) > 80 {
+ wrapped := wrapText(line, 80)
+ for _, wLine := range wrapped {
+ fmt.Fprintf(&buf, "%s\n", Green(wLine))
+ }
+ } else {
+ fmt.Fprintf(&buf, "%s\n", Green(line))
+ }
+ }
+ }
+
+ if tweet.URL != "" {
+ fmt.Fprintf(&buf, "%s\n", Blue(tweet.URL))
+ }
+
+ fmt.Fprintf(&buf, "%s\n", Dim("───"))
+ }
+
+ if streamResp.Progress != nil {
+ fmt.Fprintf(&buf, "%s %s/%s\n",
+ Dim("Progress:"),
+ Yellow(fmt.Sprintf("%d", streamResp.Progress.Current)),
+ Yellow(fmt.Sprintf("%d", streamResp.Progress.Total)))
+ }
+
+ if streamResp.Complete != nil {
+ fmt.Fprintf(&buf, "\n%s\n", Bold(Green("Complete!")))
+ fmt.Fprintf(&buf, "%s %s\n", Dim("Duration:"), streamResp.Complete.Duration)
+ fmt.Fprintf(&buf, "%s %s\n",
+ Dim("Total Replies:"),
+ Green(fmt.Sprintf("%d", streamResp.Complete.TotalFetched)))
+ if streamResp.Complete.TotalFailed > 0 {
+ fmt.Fprintf(&buf, "%s %s\n",
+ Dim("Total Failed:"),
+ Yellow(fmt.Sprintf("%d", streamResp.Complete.TotalFailed)))
+ }
+ }
+
+ return buf.String(), nil
+}
+
+func FormatXSearch(streamResp *models.TweetsStreamResponse, asJSON bool) (string, error) {
+ if asJSON {
+ data, err := json.MarshalIndent(streamResp, "", " ")
+ if err != nil {
+ return "", fmt.Errorf("failed to marshal JSON: %w", err)
+ }
+ return string(data), nil
+ }
+
+ var buf bytes.Buffer
+
+ if streamResp.Error != "" {
+ fmt.Fprintf(&buf, "%s\n", Red(fmt.Sprintf("Error: %s", streamResp.Error)))
+ return buf.String(), nil
+ }
+
+ if streamResp.Tweet != nil {
+ tweet := streamResp.Tweet
+
+ if tweet.Name != "" {
+ fmt.Fprintf(&buf, "%s ", Cyan(tweet.Name))
+ }
+ if tweet.Handle != "" {
+ fmt.Fprintf(&buf, "%s", Gray(fmt.Sprintf("(@%s)", tweet.Handle)))
+ }
+ if tweet.Name != "" || tweet.Handle != "" {
+ fmt.Fprintf(&buf, "\n")
+ }
+
+ if tweet.Text != "" {
+ lines := strings.Split(tweet.Text, "\n")
+ for _, line := range lines {
+ if len(line) > 80 {
+ wrapped := wrapText(line, 80)
+ for _, wLine := range wrapped {
+ fmt.Fprintf(&buf, "%s\n", Green(wLine))
+ }
+ } else {
+ fmt.Fprintf(&buf, "%s\n", Green(line))
+ }
+ }
+ }
+
+ if tweet.Type != "" {
+ fmt.Fprintf(&buf, "%s %s\n", Dim("Type:"), tweet.Type)
+ }
+
+ if tweet.URL != "" {
+ fmt.Fprintf(&buf, "%s\n", Blue(tweet.URL))
+ }
+
+ fmt.Fprintf(&buf, "%s\n", Dim("───"))
+ }
+
+ if streamResp.Progress != nil {
+ fmt.Fprintf(&buf, "%s %s/%s\n",
+ Dim("Progress:"),
+ Yellow(fmt.Sprintf("%d", streamResp.Progress.Current)),
+ Yellow(fmt.Sprintf("%d", streamResp.Progress.Total)))
+ }
+
+ if streamResp.Complete != nil {
+ fmt.Fprintf(&buf, "\n%s\n", Bold(Green("Search Complete!")))
+ fmt.Fprintf(&buf, "%s %s\n", Dim("Duration:"), streamResp.Complete.Duration)
+ fmt.Fprintf(&buf, "%s %s\n",
+ Dim("Total Results:"),
+ Green(fmt.Sprintf("%d", streamResp.Complete.TotalFetched)))
+ if streamResp.Complete.TotalFailed > 0 {
+ fmt.Fprintf(&buf, "%s %s\n",
+ Dim("Total Failed:"),
+ Yellow(fmt.Sprintf("%d", streamResp.Complete.TotalFailed)))
+ }
+ }
+
+ return buf.String(), nil
+}
+
+func wrapText(text string, width int) []string {
+ if len(text) <= width {
+ return []string{text}
+ }
+
+ var lines []string
+ words := strings.Fields(text)
+ if len(words) == 0 {
+ return []string{text}
+ }
+
+ var currentLine strings.Builder
+ for _, word := range words {
+ if currentLine.Len() == 0 {
+ currentLine.WriteString(word)
+ } else if currentLine.Len()+1+len(word) <= width {
+ currentLine.WriteString(" ")
+ currentLine.WriteString(word)
+ } else {
+ lines = append(lines, currentLine.String())
+ currentLine.Reset()
+ currentLine.WriteString(word)
+ }
+ }
+
+ if currentLine.Len() > 0 {
+ lines = append(lines, currentLine.String())
+ }
+
+ return lines
+}