x-reader 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +206 -0
- package/dist/api/client.d.ts +65 -0
- package/dist/api/client.js +527 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/constants.d.ts +23 -0
- package/dist/api/constants.js +60 -0
- package/dist/api/constants.js.map +1 -0
- package/dist/api/features.d.ts +14 -0
- package/dist/api/features.js +158 -0
- package/dist/api/features.js.map +1 -0
- package/dist/api/parser.d.ts +23 -0
- package/dist/api/parser.js +236 -0
- package/dist/api/parser.js.map +1 -0
- package/dist/api/query-ids.d.ts +31 -0
- package/dist/api/query-ids.js +201 -0
- package/dist/api/query-ids.js.map +1 -0
- package/dist/api/types.d.ts +91 -0
- package/dist/api/types.js +3 -0
- package/dist/api/types.js.map +1 -0
- package/dist/cli/index.d.ts +15 -0
- package/dist/cli/index.js +258 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/auth.d.ts +21 -0
- package/dist/utils/auth.js +128 -0
- package/dist/utils/auth.js.map +1 -0
- package/dist/utils/format.d.ts +15 -0
- package/dist/utils/format.js +62 -0
- package/dist/utils/format.js.map +1 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# x-reader
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/x-reader)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://nodejs.org)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
|
|
8
|
+
**Read-only Twitter/X CLI** for searching tweets, reading timelines, bookmarks, and replies from the terminal. Inspired by [Bird CLI](https://github.com/steipete/bird).
|
|
9
|
+
|
|
10
|
+
> Uses your own X/Twitter cookies. One user, one account. No API keys required.
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- **Search** tweets by keyword or advanced query (`from:`, `to:`, `filter:`, etc.)
|
|
15
|
+
- **Read** a user's timeline
|
|
16
|
+
- **Read** a single tweet by ID or URL
|
|
17
|
+
- **Read** replies to any tweet
|
|
18
|
+
- **Read** your bookmarks (with `--all` for full export)
|
|
19
|
+
- **Look up** user profiles by handle
|
|
20
|
+
- **JSON output** for piping into other tools (`jq`, scripts, etc.)
|
|
21
|
+
- **Auto-discovers** X's rotating GraphQL query IDs (no manual updates needed)
|
|
22
|
+
- **Zero config** beyond two browser cookies
|
|
23
|
+
|
|
24
|
+
## Why x-reader?
|
|
25
|
+
|
|
26
|
+
- No Twitter API keys or developer account needed
|
|
27
|
+
- Read-only by design (no accidental tweets, likes, or follows)
|
|
28
|
+
- Works with X's current GraphQL endpoints
|
|
29
|
+
- Lightweight: single dependency (Commander)
|
|
30
|
+
- Scriptable: JSON output for automation and data pipelines
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
### From npm (recommended)
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install -g x-reader
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### From source
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
git clone https://github.com/DjinnFoundry/x-reader.git
|
|
44
|
+
cd x-reader
|
|
45
|
+
npm install
|
|
46
|
+
npm run build
|
|
47
|
+
npm link
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quick start
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# 1. Set up authentication (interactive)
|
|
54
|
+
x-reader setup
|
|
55
|
+
|
|
56
|
+
# 2. Search tweets
|
|
57
|
+
x-reader search "machine learning"
|
|
58
|
+
|
|
59
|
+
# 3. Read a user's timeline
|
|
60
|
+
x-reader user-tweets @naval -n 10
|
|
61
|
+
|
|
62
|
+
# 4. Export your bookmarks as JSON
|
|
63
|
+
x-reader bookmarks --all --format json > bookmarks.json
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Authentication
|
|
67
|
+
|
|
68
|
+
You need two cookies from x.com: `auth_token` and `ct0`.
|
|
69
|
+
|
|
70
|
+
### Option 1: Interactive setup
|
|
71
|
+
```bash
|
|
72
|
+
x-reader setup
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Option 2: Environment variables
|
|
76
|
+
```bash
|
|
77
|
+
export AUTH_TOKEN="your_auth_token"
|
|
78
|
+
export CT0="your_ct0"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Option 3: CLI flags
|
|
82
|
+
```bash
|
|
83
|
+
x-reader search "query" --auth-token xxx --ct0 yyy
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Where to find your cookies
|
|
87
|
+
1. Go to [x.com](https://x.com) and log in
|
|
88
|
+
2. Open DevTools (F12) -> Application -> Cookies -> x.com
|
|
89
|
+
3. Copy `auth_token` and `ct0` values
|
|
90
|
+
|
|
91
|
+
Config is saved to `~/.config/x-reader/config.json`.
|
|
92
|
+
|
|
93
|
+
## Usage
|
|
94
|
+
|
|
95
|
+
### Search tweets
|
|
96
|
+
```bash
|
|
97
|
+
x-reader search "machine learning"
|
|
98
|
+
x-reader search "from:elonmusk" -n 5
|
|
99
|
+
x-reader search "AI safety" --format json
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Read a user's timeline
|
|
103
|
+
```bash
|
|
104
|
+
x-reader user-tweets @steipete
|
|
105
|
+
x-reader user-tweets elonmusk -n 10
|
|
106
|
+
x-reader user-tweets @naval --format json
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Read a single tweet
|
|
110
|
+
```bash
|
|
111
|
+
x-reader read 1234567890
|
|
112
|
+
x-reader read https://x.com/user/status/1234567890
|
|
113
|
+
x-reader read https://x.com/user/status/1234567890 --format json
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Read replies
|
|
117
|
+
```bash
|
|
118
|
+
x-reader replies 1234567890
|
|
119
|
+
x-reader replies https://x.com/user/status/1234567890 --format json
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Export bookmarks
|
|
123
|
+
```bash
|
|
124
|
+
x-reader bookmarks
|
|
125
|
+
x-reader bookmarks -n 50
|
|
126
|
+
x-reader bookmarks --all --format json
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### User lookup
|
|
130
|
+
```bash
|
|
131
|
+
x-reader user-lookup @steipete
|
|
132
|
+
x-reader user-lookup naval --format json
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Query ID management
|
|
136
|
+
```bash
|
|
137
|
+
# Show cached query IDs
|
|
138
|
+
x-reader query-ids
|
|
139
|
+
|
|
140
|
+
# Force refresh from x.com (when IDs rotate)
|
|
141
|
+
x-reader query-ids --refresh
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Output formats
|
|
145
|
+
|
|
146
|
+
- **text** (default) - human-readable
|
|
147
|
+
- **json** - machine-readable, pipe to `jq` or save to file
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# Pipe to jq
|
|
151
|
+
x-reader search "typescript" --format json | jq '.tweets[].text'
|
|
152
|
+
|
|
153
|
+
# Save to file
|
|
154
|
+
x-reader bookmarks --all --format json > my-bookmarks.json
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Programmatic use
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
import { XReaderClient } from 'x-reader';
|
|
161
|
+
|
|
162
|
+
const client = new XReaderClient({
|
|
163
|
+
cookies: { authToken: '...', ct0: '...' },
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const result = await client.search('hello world', 10);
|
|
167
|
+
console.log(result.tweets);
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## How it works
|
|
171
|
+
|
|
172
|
+
X uses GraphQL endpoints with rotating query IDs embedded in their client-side JavaScript bundles. x-reader auto-discovers these IDs by scraping the JS bundles, caching them locally with a 24-hour TTL. No manual ID updates needed.
|
|
173
|
+
|
|
174
|
+
If you get 404 errors, force a refresh:
|
|
175
|
+
```bash
|
|
176
|
+
x-reader query-ids --refresh
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Architecture
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
src/
|
|
183
|
+
├── api/
|
|
184
|
+
│ ├── client.ts # Main API client (read-only operations)
|
|
185
|
+
│ ├── constants.ts # Bearer token, URLs, default query IDs
|
|
186
|
+
│ ├── features.ts # GraphQL feature flags per operation
|
|
187
|
+
│ ├── parser.ts # Response parsing (raw JSON -> Tweet/User)
|
|
188
|
+
│ ├── query-ids.ts # Auto-discovery of query IDs from x.com JS
|
|
189
|
+
│ └── types.ts # TypeScript interfaces
|
|
190
|
+
├── cli/
|
|
191
|
+
│ └── index.ts # CLI entry point (commander)
|
|
192
|
+
├── utils/
|
|
193
|
+
│ ├── auth.ts # Cookie resolution (env, config, bird compat)
|
|
194
|
+
│ └── format.ts # Output formatting
|
|
195
|
+
└── index.ts # Library exports
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Credits
|
|
199
|
+
|
|
200
|
+
- Inspired by [Bird CLI](https://github.com/steipete/bird) by [@steipete](https://x.com/steipete)
|
|
201
|
+
- Uses the same public bearer token as the X web client
|
|
202
|
+
- Query ID discovery mechanism reverse-engineered from Bird v0.8.0
|
|
203
|
+
|
|
204
|
+
## License
|
|
205
|
+
|
|
206
|
+
MIT
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XReader API Client — Read-only Twitter/X GraphQL client.
|
|
3
|
+
*
|
|
4
|
+
* Provides: search, user-tweets, tweet-detail, replies, bookmarks, user-lookup.
|
|
5
|
+
* Does NOT provide any write operations (tweet, like, retweet, follow, etc.)
|
|
6
|
+
*/
|
|
7
|
+
import type { ClientOptions, TweetsResult, TweetResult, UserResult } from './types.js';
|
|
8
|
+
export declare class XReaderClient {
|
|
9
|
+
private authToken;
|
|
10
|
+
private ct0;
|
|
11
|
+
private cookieHeader;
|
|
12
|
+
private userAgent;
|
|
13
|
+
private timeoutMs?;
|
|
14
|
+
private quoteDepth;
|
|
15
|
+
private clientUuid;
|
|
16
|
+
constructor(options: ClientOptions);
|
|
17
|
+
private getHeaders;
|
|
18
|
+
private fetchWithTimeout;
|
|
19
|
+
private sleep;
|
|
20
|
+
private getQueryIds;
|
|
21
|
+
/**
|
|
22
|
+
* Try a request with multiple query IDs, auto-refresh on 404.
|
|
23
|
+
*/
|
|
24
|
+
private tryWithQueryIds;
|
|
25
|
+
search(query: string, count?: number, options?: {
|
|
26
|
+
cursor?: string;
|
|
27
|
+
includeRaw?: boolean;
|
|
28
|
+
}): Promise<TweetsResult>;
|
|
29
|
+
getTweet(tweetId: string, options?: {
|
|
30
|
+
includeRaw?: boolean;
|
|
31
|
+
}): Promise<TweetResult>;
|
|
32
|
+
getReplies(tweetId: string, options?: {
|
|
33
|
+
includeRaw?: boolean;
|
|
34
|
+
}): Promise<TweetsResult>;
|
|
35
|
+
getUserTweets(userId: string, count?: number, options?: {
|
|
36
|
+
cursor?: string;
|
|
37
|
+
includeRaw?: boolean;
|
|
38
|
+
}): Promise<TweetsResult>;
|
|
39
|
+
getBookmarks(count?: number, options?: {
|
|
40
|
+
cursor?: string;
|
|
41
|
+
includeRaw?: boolean;
|
|
42
|
+
}): Promise<TweetsResult>;
|
|
43
|
+
getUserByUsername(username: string): Promise<UserResult>;
|
|
44
|
+
/**
|
|
45
|
+
* Search with automatic pagination to collect up to `count` tweets.
|
|
46
|
+
*/
|
|
47
|
+
searchAll(query: string, count: number, options?: {
|
|
48
|
+
maxPages?: number;
|
|
49
|
+
includeRaw?: boolean;
|
|
50
|
+
}): Promise<TweetsResult>;
|
|
51
|
+
/**
|
|
52
|
+
* Get user tweets with pagination.
|
|
53
|
+
*/
|
|
54
|
+
getUserTweetsAll(userId: string, count: number, options?: {
|
|
55
|
+
maxPages?: number;
|
|
56
|
+
includeRaw?: boolean;
|
|
57
|
+
}): Promise<TweetsResult>;
|
|
58
|
+
/**
|
|
59
|
+
* Get all bookmarks with pagination.
|
|
60
|
+
*/
|
|
61
|
+
getBookmarksAll(count?: number, options?: {
|
|
62
|
+
maxPages?: number;
|
|
63
|
+
includeRaw?: boolean;
|
|
64
|
+
}): Promise<TweetsResult>;
|
|
65
|
+
}
|