Commit 049030e
committed
v1.7.1: FxTwitter Article enrichment — read+analyze auto-resolve Article bodies
Closes the Article-blindness gap surfaced when analyzing
@longevityboris's 2056280764447113305 ("Two species.", 204K imp, 17 likes,
1 profile click): xmaster previously saw only the t.co wrapper and
preflight was scoring an empty string. Now it scores the full 6,387-char
Article body.
## What changed
- New `src/providers/fxtwitter.rs` — soft-dependency client for
api.fxtwitter.com. Returns `Article { id, title, body, body_chars,
cover_image_url }` flattened from Draft.js blocks (header-two → "## ",
unordered-list-item → "- ", blockquote → "> ", etc.). 2.5s timeout,
graceful degradation on any failure.
- `xmaster read <id>`: when the tweet text looks like a single t.co URL,
auto-fetches the Article body via FxTwitter and surfaces it in the
output as an optional `article` field. Skips when
`settings.disable_fxtwitter = true`.
- `xmaster analyze <id_or_url>`: auto-detects tweet IDs (15-25 digit
numeric) and X URLs (`x.com/.../status/<id>`), fetches the tweet,
resolves Article bodies through FxTwitter, then runs preflight on the
actual content. Free-form text input still works unchanged. No new
flag — pure friction reduction.
- `Settings.disable_fxtwitter: bool` (default false) — opt-out for users
who want strict-v2-API-only behavior. Set via
`xmaster config set settings.disable_fxtwitter true`.
- preflight.rs hashtag counter — fixed false positive on markdown
header prefixes from flattened Article bodies. Now counts `#X` only
where X is alphanumeric AND previous char isn't `#`, so `## Heading`
doesn't trigger excessive_hashtags but `#rust #cli` still does.
## Verified live on Boris's "Two species." Article (id 2056280764447113305)
Before v1.7.1:
`xmaster read 2056280764447113305` → text: "https://t.co/MAoQl3DbpC"
(article body invisible)
After v1.7.1:
`xmaster read 2056280764447113305` → article: {
id: "2056263966406451201",
title: "Two species.",
body_chars: 6373,
body: "AI is making most people dumber..." (full 6,373-char body)
}
`xmaster analyze 2056280764447113305` → scores the full Article (D, 45)
with real findings: weak_hook_no_weight ("Two species." carries no
number/named-entity/status-verb), long_form_too_long (6,387 > 5,000),
negative_sentiment.
## Why FxTwitter, not the private GraphQL
- v2 API doesn't expose Article bodies (only t.co + entities.urls)
- Private GraphQL `ArticleEntityGet`-style endpoints rotate operation IDs
every X deploy (xmaster's `article draft` path already feels this pain)
- FxTwitter (community service powering Discord/Telegram unfurls) returns
the Article body as Draft.js blocks via `api.fxtwitter.com/i/status/<id>`
with no auth, sub-second response, MIT-licensed, actively maintained
- Soft dep: timeout 2.5s, opt-out flag, falls back to v2 response on any
failure — never blocks the user
## Tests
99 unit (+2 for the hashtag-markdown fix) + 29 integration pass; clippy
-D warnings clean. Two new fxtwitter.rs unit tests for block flattening
and wrapper detection.1 parent 444a3e8 commit 049030e
8 files changed
Lines changed: 351 additions & 4 deletions
File tree
- src
- commands
- intel
- providers
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
178 | 178 | | |
179 | 179 | | |
180 | 180 | | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
181 | 194 | | |
182 | 195 | | |
183 | 196 | | |
| |||
188 | 201 | | |
189 | 202 | | |
190 | 203 | | |
191 | | - | |
| 204 | + | |
192 | 205 | | |
193 | 206 | | |
194 | 207 | | |
195 | 208 | | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
6 | 7 | | |
7 | 8 | | |
8 | 9 | | |
| |||
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
24 | 29 | | |
25 | 30 | | |
26 | 31 | | |
| |||
41 | 46 | | |
42 | 47 | | |
43 | 48 | | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
44 | 57 | | |
45 | 58 | | |
46 | 59 | | |
| |||
57 | 70 | | |
58 | 71 | | |
59 | 72 | | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
60 | 84 | | |
61 | 85 | | |
62 | 86 | | |
| |||
72 | 96 | | |
73 | 97 | | |
74 | 98 | | |
| 99 | + | |
75 | 100 | | |
76 | 101 | | |
77 | 102 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
103 | 103 | | |
104 | 104 | | |
105 | 105 | | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
106 | 114 | | |
107 | 115 | | |
108 | 116 | | |
109 | 117 | | |
110 | 118 | | |
111 | 119 | | |
| 120 | + | |
112 | 121 | | |
113 | 122 | | |
114 | 123 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1001 | 1001 | | |
1002 | 1002 | | |
1003 | 1003 | | |
1004 | | - | |
| 1004 | + | |
| 1005 | + | |
| 1006 | + | |
| 1007 | + | |
| 1008 | + | |
| 1009 | + | |
| 1010 | + | |
| 1011 | + | |
| 1012 | + | |
| 1013 | + | |
| 1014 | + | |
| 1015 | + | |
| 1016 | + | |
| 1017 | + | |
| 1018 | + | |
| 1019 | + | |
| 1020 | + | |
| 1021 | + | |
| 1022 | + | |
| 1023 | + | |
| 1024 | + | |
| 1025 | + | |
1005 | 1026 | | |
1006 | 1027 | | |
1007 | 1028 | | |
| |||
1769 | 1790 | | |
1770 | 1791 | | |
1771 | 1792 | | |
| 1793 | + | |
| 1794 | + | |
| 1795 | + | |
| 1796 | + | |
| 1797 | + | |
| 1798 | + | |
| 1799 | + | |
| 1800 | + | |
| 1801 | + | |
| 1802 | + | |
| 1803 | + | |
| 1804 | + | |
| 1805 | + | |
| 1806 | + | |
| 1807 | + | |
| 1808 | + | |
| 1809 | + | |
| 1810 | + | |
| 1811 | + | |
| 1812 | + | |
| 1813 | + | |
| 1814 | + | |
| 1815 | + | |
| 1816 | + | |
| 1817 | + | |
1772 | 1818 | | |
0 commit comments