Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.2.0"
".": "0.3.0"
}
6 changes: 3 additions & 3 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 115
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/xquik%2Fx-twitter-scraper-3b2c6c771ad1da0bbfeb0af115972929ed2c7fcd5e47a79556d66cd21431b224.yml
openapi_spec_hash: de2890233b68387bf5f9b6d19e7d87dc
configured_endpoints: 102
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/xquik%2Fx-twitter-scraper-46a9af86900d469595bc007c73410022306d0863a896758d9c3c1250fbe937a3.yml
openapi_spec_hash: d858851b15eb367466f343da3cb2d556
config_hash: 8894c96caeb6df84c9394518810221bd
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## 0.3.0 (2026-04-02)

Full Changelog: [v0.2.0...v0.3.0](https://github.com/Xquik-dev/x-twitter-scraper-cli/compare/v0.2.0...v0.3.0)

### Features

* **api:** api update ([2939f30](https://github.com/Xquik-dev/x-twitter-scraper-cli/commit/2939f308c9443e7e0fe53e1ac47f43413b6d78a5))
* **api:** api update ([600dade](https://github.com/Xquik-dev/x-twitter-scraper-cli/commit/600dadedbb67605a0619ff305b6859ac2d2b22f2))


### Bug Fixes

* handle empty data set using `--format explore` ([fb087bb](https://github.com/Xquik-dev/x-twitter-scraper-cli/commit/fb087bb19f7783f5c6b0d34a6c800da8d6ff6337))
* use `RawJSON` when iterating items with `--format explore` in the CLI ([9b49da1](https://github.com/Xquik-dev/x-twitter-scraper-cli/commit/9b49da128f9110d987a9174a6fa84419ae337e9b))

## 0.2.0 (2026-04-01)

Full Changelog: [v0.1.0...v0.2.0](https://github.com/Xquik-dev/x-twitter-scraper-cli/compare/v0.1.0...v0.2.0)
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/Xquik-dev/x-twitter-scraper-cli
go 1.25

require (
github.com/Xquik-dev/x-twitter-scraper-go v0.1.0
github.com/Xquik-dev/x-twitter-scraper-go v0.2.0
github.com/charmbracelet/bubbles v0.21.0
github.com/charmbracelet/bubbletea v1.3.6
github.com/charmbracelet/lipgloss v1.1.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
github.com/Xquik-dev/x-twitter-scraper-go v0.1.0 h1:7pI1R5oOjgMDKl86ItIM8meUOxtwcmgGVjHctRNXXXo=
github.com/Xquik-dev/x-twitter-scraper-go v0.1.0/go.mod h1:OHW3aIR8E3+ANa/mjFTZs1sG7ePzrBEmW0a8JUN+NvI=
github.com/Xquik-dev/x-twitter-scraper-go v0.2.0 h1:WEn0e9rZEQ+m82tPTuksxhluADV1Rjpj0zJ2LTrQRvs=
github.com/Xquik-dev/x-twitter-scraper-go v0.2.0/go.mod h1:OHW3aIR8E3+ANa/mjFTZs1sG7ePzrBEmW0a8JUN+NvI=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
Expand Down
38 changes: 35 additions & 3 deletions internal/jsonview/explorer.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package jsonview

import (
"bytes"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -309,6 +310,10 @@ func ExploreJSON(title string, json gjson.Result) error {
return err
}

type hasRawJSON interface {
RawJSON() string
}

// ExploreJSONStream explores JSON data loaded incrementally via an iterator
func ExploreJSONStream[T any](title string, it Iterator[T]) error {
anyIt := genericToAnyIterator(it)
Expand All @@ -327,12 +332,12 @@ func ExploreJSONStream[T any](title string, it Iterator[T]) error {
return err
}

// Convert items to JSON array
jsonBytes, err := json.Marshal(items)
arrayJSONBytes, err := marshalItemsToJSONArray(items)
if err != nil {
return err
}
arrayJSON := gjson.ParseBytes(jsonBytes)

arrayJSON := gjson.ParseBytes(arrayJSONBytes)
view, err := newTableView("", arrayJSON, false)
if err != nil {
return err
Expand All @@ -352,6 +357,29 @@ func ExploreJSONStream[T any](title string, it Iterator[T]) error {
return err
}

func marshalItemsToJSONArray(items []any) ([]byte, error) {
var buf bytes.Buffer
buf.WriteByte('[')

for i, item := range items {
if i > 0 {
buf.WriteByte(',')
}
if hasRaw, ok := item.(hasRawJSON); ok {
buf.WriteString(hasRaw.RawJSON())
} else {
jsonData, err := json.Marshal(item)
if err != nil {
return nil, err
}
buf.Write(jsonData)
}
}

buf.WriteByte(']')
return buf.Bytes(), nil
}

func (v *JSONViewer) current() JSONView { return v.stack[len(v.stack)-1] }
func (v *JSONViewer) Init() tea.Cmd { return nil }

Expand Down Expand Up @@ -406,6 +434,10 @@ func (v *JSONViewer) navigateForward() (tea.Model, tea.Cmd) {
return v, nil
}

if len(tableView.rowData) < 1 {
return v, nil
}

cursor := tableView.table.Cursor()
selected := tableView.rowData[cursor]
if !v.canNavigateInto(selected) {
Expand Down
60 changes: 60 additions & 0 deletions internal/jsonview/explorer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package jsonview

import (
"testing"

"github.com/charmbracelet/bubbles/help"
"github.com/tidwall/gjson"

"github.com/stretchr/testify/require"
)

func TestNavigateForward_EmptyRowData(t *testing.T) {
// An empty JSON array produces a TableView with no rows.
emptyArray := gjson.Parse("[]")
view, err := newTableView("", emptyArray, false)
require.NoError(t, err)

viewer := &JSONViewer{
stack: []JSONView{view},
root: "test",
help: help.New(),
}

// Should return without panicking despite the empty data set.
model, cmd := viewer.navigateForward()
require.Equal(t, model, viewer, "expected same viewer model returned")
require.Nil(t, cmd)

// Stack should remain unchanged (no new view pushed).
require.Equal(t, 1, len(viewer.stack), "expected stack length 1, got %d", len(viewer.stack))
}

// rawJSONItem implements HasRawJSON, returning pre-built JSON.
type rawJSONItem struct {
raw string
}

func (r rawJSONItem) RawJSON() string { return r.raw }

func TestMarshalItemsToJSONArray_WithHasRawJSON(t *testing.T) {
items := []any{
rawJSONItem{raw: `{"id":1,"name":"alice"}`},
rawJSONItem{raw: `{"id":2,"name":"bob"}`},
}

got, err := marshalItemsToJSONArray(items)
require.NoError(t, err)
require.JSONEq(t, `[{"id":1,"name":"alice"},{"id":2,"name":"bob"}]`, string(got))
}

func TestMarshalItemsToJSONArray_WithoutHasRawJSON(t *testing.T) {
items := []any{
map[string]any{"id": 1, "name": "alice"},
map[string]any{"id": 2, "name": "bob"},
}

got, err := marshalItemsToJSONArray(items)
require.NoError(t, err)
require.JSONEq(t, `[{"id":1,"name":"alice"},{"id":2,"name":"bob"}]`, string(got))
}
34 changes: 0 additions & 34 deletions pkg/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,9 @@ func init() {
Category: "API RESOURCE",
Suggest: true,
Commands: []*cli.Command{
&stylesRetrieve,
&stylesUpdate,
&stylesList,
&stylesDelete,
&stylesAnalyze,
&stylesCompare,
&stylesGetPerformance,
},
},
{
Expand Down Expand Up @@ -238,9 +234,7 @@ func init() {
Suggest: true,
Commands: []*cli.Command{
&xTweetsCreate,
&xTweetsRetrieve,
&xTweetsList,
&xTweetsDelete,
&xTweetsGetFavoriters,
&xTweetsGetQuotes,
&xTweetsGetReplies,
Expand All @@ -249,30 +243,11 @@ func init() {
&xTweetsSearch,
},
},
{
Name: "x:tweets:like",
Category: "API RESOURCE",
Suggest: true,
Commands: []*cli.Command{
&xTweetsLikeCreate,
&xTweetsLikeDelete,
},
},
{
Name: "x:tweets:retweet",
Category: "API RESOURCE",
Suggest: true,
Commands: []*cli.Command{
&xTweetsRetweetCreate,
&xTweetsRetweetDelete,
},
},
{
Name: "x:users",
Category: "API RESOURCE",
Suggest: true,
Commands: []*cli.Command{
&xUsersRetrieve,
&xUsersRetrieveBatch,
&xUsersRetrieveFollowers,
&xUsersRetrieveFollowersYouKnow,
Expand All @@ -285,15 +260,6 @@ func init() {
&xUsersRetrieveVerifiedFollowers,
},
},
{
Name: "x:users:follow",
Category: "API RESOURCE",
Suggest: true,
Commands: []*cli.Command{
&xUsersFollowCreate,
&xUsersFollowDeleteAll,
},
},
{
Name: "x:followers",
Category: "API RESOURCE",
Expand Down
6 changes: 3 additions & 3 deletions pkg/cmd/cmdutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ func countTerminalLines(data []byte, terminalWidth int) int {
return bytes.Count([]byte(wrap.String(string(data), terminalWidth)), []byte("\n"))
}

type HasRawJSON interface {
type hasRawJSON interface {
RawJSON() string
}

Expand All @@ -401,7 +401,7 @@ func ShowJSONIterator[T any](stdout *os.File, title string, iter jsonview.Iterat
for itemsToDisplay != 0 && iter.Next() {
item := iter.Current()
var obj gjson.Result
if hasRaw, ok := any(item).(HasRawJSON); ok {
if hasRaw, ok := any(item).(hasRawJSON); ok {
obj = gjson.Parse(hasRaw.RawJSON())
} else {
jsonData, err := json.Marshal(item)
Expand Down Expand Up @@ -448,7 +448,7 @@ func ShowJSONIterator[T any](stdout *os.File, title string, iter jsonview.Iterat
}
item := iter.Current()
var obj gjson.Result
if hasRaw, ok := any(item).(HasRawJSON); ok {
if hasRaw, ok := any(item).(hasRawJSON); ok {
obj = gjson.Parse(hasRaw.RawJSON())
} else {
jsonData, err := json.Marshal(item)
Expand Down
2 changes: 2 additions & 0 deletions pkg/cmd/integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ var integrationsUpdate = cli.Command{
},
&requestflag.Flag[map[string]any]{
Name: "filters",
Usage: "Event filter rules (JSON)",
BodyPath: "filters",
},
&requestflag.Flag[bool]{
Expand All @@ -91,6 +92,7 @@ var integrationsUpdate = cli.Command{
},
&requestflag.Flag[map[string]any]{
Name: "message-template",
Usage: "Custom message template (JSON)",
BodyPath: "messageTemplate",
},
&requestflag.Flag[string]{
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/radar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func TestRadarRetrieveTrendingTopics(t *testing.T) {
"--count", "0",
"--hours", "0",
"--region", "region",
"--source", "source",
"--source", "github",
)
})
}
Loading
Loading