rdt-cli 0.3.2__tar.gz → 0.4.0__tar.gz
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.
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/PKG-INFO +7 -3
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/README.md +6 -2
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/SKILL.md +16 -1
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/pyproject.toml +1 -1
- rdt_cli-0.4.0/rdt_cli/__init__.py +3 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/client.py +68 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/commands/browse.py +30 -8
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/constants.py +1 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/tests/test_cli.py +59 -2
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/tests/test_smoke.py +14 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/uv.lock +1 -1
- rdt_cli-0.3.2/rdt_cli/__init__.py +0 -3
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/.github/workflows/ci.yml +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/.github/workflows/publish.yml +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/.gitignore +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/SCHEMA.md +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/__main__.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/auth.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/cli.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/commands/__init__.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/commands/_common.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/commands/auth.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/commands/post.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/commands/search.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/commands/social.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/config.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/exceptions.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/fingerprint.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/index_cache.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/models.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/parser.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/session.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/rdt_cli/transports.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/tests/__init__.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/tests/fixtures/listing.json +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/tests/fixtures/morechildren.json +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/tests/fixtures/post_detail.json +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/tests/test_client.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/tests/test_parser.py +0 -0
- {rdt_cli-0.3.2 → rdt_cli-0.4.0}/tests/test_session.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rdt-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: A CLI for Reddit — browse feeds, read posts, search, and interact via terminal 📖
|
|
5
5
|
Project-URL: Homepage, https://github.com/jackwener/rdt-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/jackwener/rdt-cli
|
|
@@ -42,7 +42,7 @@ A CLI for Reddit — browse feeds, read posts, search, and interact via reverse-
|
|
|
42
42
|
## Features
|
|
43
43
|
|
|
44
44
|
- 🔐 **Auth** — auto-extract browser cookies, status check, whoami
|
|
45
|
-
- 🏠 **Feed** — browse home feed, popular,
|
|
45
|
+
- 🏠 **Feed** — browse home feed, popular, /r/all, and subscription-only feed (`--subs-only`)
|
|
46
46
|
- 📋 **Subreddits** — browse any subreddit with sort/time filters, view subreddit info
|
|
47
47
|
- 📰 **Posts** — read posts and comment trees with syntax highlighting
|
|
48
48
|
- 💬 **Expanded comments** — `--expand-more` loads additional `more comments` entries
|
|
@@ -95,6 +95,8 @@ rdt logout # Clear saved cookies
|
|
|
95
95
|
|
|
96
96
|
# ─── Browse ───────────────────────────────────────
|
|
97
97
|
rdt feed # Home feed (requires login)
|
|
98
|
+
rdt feed --subs-only # Subscriptions-only feed (no algorithm)
|
|
99
|
+
rdt feed --subs-only -n 5 --max-subs 10 # Limit per-sub posts and max subs
|
|
98
100
|
rdt popular # Popular posts
|
|
99
101
|
rdt popular --full-text # Show full titles
|
|
100
102
|
rdt all # /r/all
|
|
@@ -294,7 +296,7 @@ The built-in Gaussian jitter delay (~1s between requests) is intentional to mimi
|
|
|
294
296
|
## 功能特性
|
|
295
297
|
|
|
296
298
|
- 🔐 **认证** — 自动提取浏览器 Cookie,状态检查,用户信息
|
|
297
|
-
- 🏠 **浏览** — 首页 Feed、Popular、/r/all
|
|
299
|
+
- 🏠 **浏览** — 首页 Feed、Popular、/r/all、纯订阅 Feed(`--subs-only`)
|
|
298
300
|
- 📋 **子版块** — 浏览任意 subreddit(排序/时间过滤),查看子版块信息
|
|
299
301
|
- 📰 **帖子** — 阅读帖子和评论树
|
|
300
302
|
- 💬 **评论展开** — `--expand-more` 可展开额外评论
|
|
@@ -343,6 +345,8 @@ rdt logout # 清除缓存的 Cookie
|
|
|
343
345
|
|
|
344
346
|
# 浏览
|
|
345
347
|
rdt feed # 首页 Feed(需要登录)
|
|
348
|
+
rdt feed --subs-only # 纯订阅 Feed(无算法推荐)
|
|
349
|
+
rdt feed --subs-only -n 5 --max-subs 10 # 限制每个 sub 帖子数和最大 sub 数
|
|
346
350
|
rdt popular # 热门帖子
|
|
347
351
|
rdt all # /r/all
|
|
348
352
|
rdt sub python # 浏览子版块
|
|
@@ -19,7 +19,7 @@ A CLI for Reddit — browse feeds, read posts, search, and interact via reverse-
|
|
|
19
19
|
## Features
|
|
20
20
|
|
|
21
21
|
- 🔐 **Auth** — auto-extract browser cookies, status check, whoami
|
|
22
|
-
- 🏠 **Feed** — browse home feed, popular,
|
|
22
|
+
- 🏠 **Feed** — browse home feed, popular, /r/all, and subscription-only feed (`--subs-only`)
|
|
23
23
|
- 📋 **Subreddits** — browse any subreddit with sort/time filters, view subreddit info
|
|
24
24
|
- 📰 **Posts** — read posts and comment trees with syntax highlighting
|
|
25
25
|
- 💬 **Expanded comments** — `--expand-more` loads additional `more comments` entries
|
|
@@ -72,6 +72,8 @@ rdt logout # Clear saved cookies
|
|
|
72
72
|
|
|
73
73
|
# ─── Browse ───────────────────────────────────────
|
|
74
74
|
rdt feed # Home feed (requires login)
|
|
75
|
+
rdt feed --subs-only # Subscriptions-only feed (no algorithm)
|
|
76
|
+
rdt feed --subs-only -n 5 --max-subs 10 # Limit per-sub posts and max subs
|
|
75
77
|
rdt popular # Popular posts
|
|
76
78
|
rdt popular --full-text # Show full titles
|
|
77
79
|
rdt all # /r/all
|
|
@@ -271,7 +273,7 @@ The built-in Gaussian jitter delay (~1s between requests) is intentional to mimi
|
|
|
271
273
|
## 功能特性
|
|
272
274
|
|
|
273
275
|
- 🔐 **认证** — 自动提取浏览器 Cookie,状态检查,用户信息
|
|
274
|
-
- 🏠 **浏览** — 首页 Feed、Popular、/r/all
|
|
276
|
+
- 🏠 **浏览** — 首页 Feed、Popular、/r/all、纯订阅 Feed(`--subs-only`)
|
|
275
277
|
- 📋 **子版块** — 浏览任意 subreddit(排序/时间过滤),查看子版块信息
|
|
276
278
|
- 📰 **帖子** — 阅读帖子和评论树
|
|
277
279
|
- 💬 **评论展开** — `--expand-more` 可展开额外评论
|
|
@@ -320,6 +322,8 @@ rdt logout # 清除缓存的 Cookie
|
|
|
320
322
|
|
|
321
323
|
# 浏览
|
|
322
324
|
rdt feed # 首页 Feed(需要登录)
|
|
325
|
+
rdt feed --subs-only # 纯订阅 Feed(无算法推荐)
|
|
326
|
+
rdt feed --subs-only -n 5 --max-subs 10 # 限制每个 sub 帖子数和最大 sub 数
|
|
323
327
|
rdt popular # 热门帖子
|
|
324
328
|
rdt all # /r/all
|
|
325
329
|
rdt sub python # 浏览子版块
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: rdt-cli
|
|
3
3
|
description: Use rdt-cli for ALL Reddit operations — browsing feeds, reading posts, searching, viewing users, upvoting, saving, and subscribing. Invoke whenever the user requests any Reddit interaction.
|
|
4
4
|
author: jackwener
|
|
5
|
-
version: "0.
|
|
5
|
+
version: "0.4.0"
|
|
6
6
|
tags:
|
|
7
7
|
- reddit
|
|
8
8
|
- rdt
|
|
@@ -82,6 +82,7 @@ Payloads live under `.data`.
|
|
|
82
82
|
| Command | Description | Example |
|
|
83
83
|
|---------|-------------|---------|
|
|
84
84
|
| `rdt feed` | Browse home feed (requires login) | `rdt feed -n 10 --json` |
|
|
85
|
+
| `rdt feed --subs-only` | Subscriptions-only feed (no algorithm, sorted by time) | `rdt feed --subs-only -n 5 --json` |
|
|
85
86
|
| `rdt popular` | Browse /r/popular | `rdt popular -n 5 --json` |
|
|
86
87
|
| `rdt all` | Browse /r/all | `rdt all -n 10 --compact --json` |
|
|
87
88
|
| `rdt sub <name>` | Browse a subreddit | `rdt sub python -s top -t week` |
|
|
@@ -146,6 +147,13 @@ All listing commands (feed, popular, all, sub, user-posts, user-comments, saved,
|
|
|
146
147
|
| `--full-text` | Show full title without truncation |
|
|
147
148
|
| `-c, --compact` | Agent-friendly compact output (fewer fields) |
|
|
148
149
|
|
|
150
|
+
### Feed-specific Options
|
|
151
|
+
|
|
152
|
+
| Flag | Description |
|
|
153
|
+
|------|-------------|
|
|
154
|
+
| `--subs-only` | Show only posts from subscribed subreddits (sorted by time, no algorithm) |
|
|
155
|
+
| `--max-subs N` | Max subscriptions to fetch (default: 20) |
|
|
156
|
+
|
|
149
157
|
## Agent Workflow Examples
|
|
150
158
|
|
|
151
159
|
### Browse → Read → Upvote pipeline
|
|
@@ -185,6 +193,13 @@ rdt saved -n 20 --compact --json
|
|
|
185
193
|
rdt upvoted -n 20 --compact --json
|
|
186
194
|
```
|
|
187
195
|
|
|
196
|
+
### Subscriptions-only monitoring
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
rdt feed --subs-only -n 5 --compact --json
|
|
200
|
+
rdt feed --subs-only --max-subs 10 -o subs_feed.json
|
|
201
|
+
```
|
|
202
|
+
|
|
188
203
|
### Subreddit discovery
|
|
189
204
|
|
|
190
205
|
```bash
|
|
@@ -20,6 +20,7 @@ from .constants import (
|
|
|
20
20
|
SUBREDDIT_ABOUT_URL,
|
|
21
21
|
SUBREDDIT_SEARCH_URL,
|
|
22
22
|
SUBSCRIBE_URL,
|
|
23
|
+
SUBSCRIPTIONS_URL,
|
|
23
24
|
UNSAVE_URL,
|
|
24
25
|
USER_ABOUT_URL,
|
|
25
26
|
USER_COMMENTS_URL,
|
|
@@ -349,3 +350,70 @@ class RedditClient:
|
|
|
349
350
|
def post_comment(self, parent_fullname: str, text: str) -> dict:
|
|
350
351
|
"""Post a comment."""
|
|
351
352
|
return self._post(COMMENT_URL, data={"parent": parent_fullname, "text": text})
|
|
353
|
+
|
|
354
|
+
# ── Subscription feed ───────────────────────────────────────────
|
|
355
|
+
|
|
356
|
+
def get_my_subscriptions(
|
|
357
|
+
self, limit: int = 100, max_subs: int = 20,
|
|
358
|
+
) -> list[str]:
|
|
359
|
+
"""Get names of subscribed subreddits (up to max_subs)."""
|
|
360
|
+
names: list[str] = []
|
|
361
|
+
after: str | None = None
|
|
362
|
+
while len(names) < max_subs:
|
|
363
|
+
params: dict[str, Any] = {"limit": min(limit, 100), "raw_json": 1}
|
|
364
|
+
if after:
|
|
365
|
+
params["after"] = after
|
|
366
|
+
data = self._get(SUBSCRIPTIONS_URL, params=params)
|
|
367
|
+
children = data.get("data", {}).get("children", [])
|
|
368
|
+
if not children:
|
|
369
|
+
break
|
|
370
|
+
for child in children:
|
|
371
|
+
name = child.get("data", {}).get("display_name", "")
|
|
372
|
+
if name:
|
|
373
|
+
names.append(name)
|
|
374
|
+
if len(names) >= max_subs:
|
|
375
|
+
break
|
|
376
|
+
after = data.get("data", {}).get("after")
|
|
377
|
+
if not after:
|
|
378
|
+
break
|
|
379
|
+
return names
|
|
380
|
+
|
|
381
|
+
def get_subs_only_feed(
|
|
382
|
+
self,
|
|
383
|
+
limit_per_sub: int = DEFAULT_LIMIT,
|
|
384
|
+
max_subs: int = 20,
|
|
385
|
+
on_progress: Any = None,
|
|
386
|
+
) -> dict:
|
|
387
|
+
"""Aggregate newest posts from subscribed subreddits.
|
|
388
|
+
|
|
389
|
+
Returns a synthetic listing dict compatible with parse_listing().
|
|
390
|
+
"""
|
|
391
|
+
subs = self.get_my_subscriptions(max_subs=max_subs)
|
|
392
|
+
if not subs:
|
|
393
|
+
return {"data": {"children": [], "after": None}}
|
|
394
|
+
|
|
395
|
+
all_posts: list[dict] = []
|
|
396
|
+
seen_ids: set[str] = set()
|
|
397
|
+
|
|
398
|
+
for i, sub_name in enumerate(subs):
|
|
399
|
+
if on_progress:
|
|
400
|
+
on_progress(i + 1, len(subs), sub_name)
|
|
401
|
+
try:
|
|
402
|
+
data = self.get_subreddit(sub_name, sort="new", limit=limit_per_sub)
|
|
403
|
+
children = data.get("data", {}).get("children", [])
|
|
404
|
+
for child in children:
|
|
405
|
+
post = child.get("data", child)
|
|
406
|
+
pid = post.get("id", "")
|
|
407
|
+
if pid and pid not in seen_ids:
|
|
408
|
+
seen_ids.add(pid)
|
|
409
|
+
all_posts.append(child if "data" in child else {"data": child})
|
|
410
|
+
except RedditApiError as exc:
|
|
411
|
+
logger.warning("Skipping r/%s: %s", sub_name, exc)
|
|
412
|
+
|
|
413
|
+
# Sort by created_utc descending
|
|
414
|
+
all_posts.sort(
|
|
415
|
+
key=lambda c: c.get("data", {}).get("created_utc", 0),
|
|
416
|
+
reverse=True,
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
return {"data": {"children": all_posts, "after": None}}
|
|
@@ -159,24 +159,46 @@ def _resolve_current_username(client: RedditClient) -> str:
|
|
|
159
159
|
|
|
160
160
|
|
|
161
161
|
@click.command()
|
|
162
|
+
@click.option("--subs-only", is_flag=True, help="Show only posts from subscribed subreddits (sorted by time)")
|
|
163
|
+
@click.option("--max-subs", default=20, type=int, help="Max subscriptions to fetch (default: 20)")
|
|
162
164
|
@click.option("-n", "--limit", default=25, type=int, help="Number of posts (default: 25)")
|
|
163
165
|
@click.option("--after", default=None, help="Pagination cursor")
|
|
164
166
|
@listing_options
|
|
165
167
|
def feed(
|
|
168
|
+
subs_only: bool, max_subs: int,
|
|
166
169
|
limit: int, after: str | None,
|
|
167
170
|
as_json: bool, as_yaml: bool,
|
|
168
171
|
output_file: str | None, full_text: bool, compact: bool,
|
|
169
172
|
) -> None:
|
|
170
173
|
"""Browse your home feed (requires login)"""
|
|
171
174
|
cred = require_auth()
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
175
|
+
if subs_only:
|
|
176
|
+
if after:
|
|
177
|
+
console.print("[yellow]⚠ --after is ignored with --subs-only[/yellow]")
|
|
178
|
+
|
|
179
|
+
def _progress(current: int, total: int, name: str) -> None:
|
|
180
|
+
if not (as_json or as_yaml):
|
|
181
|
+
console.print(f" [dim]📡 [{current}/{total}] r/{name}[/dim]")
|
|
182
|
+
|
|
183
|
+
_handle_listing(
|
|
184
|
+
cred,
|
|
185
|
+
action=lambda c: c.get_subs_only_feed(
|
|
186
|
+
limit_per_sub=limit, max_subs=max_subs, on_progress=_progress,
|
|
187
|
+
),
|
|
188
|
+
data_title="📡 Subscriptions Feed",
|
|
189
|
+
next_cmd="",
|
|
190
|
+
as_json=as_json, as_yaml=as_yaml,
|
|
191
|
+
output_file=output_file, full_text=full_text, compact=compact,
|
|
192
|
+
)
|
|
193
|
+
else:
|
|
194
|
+
_handle_listing(
|
|
195
|
+
cred,
|
|
196
|
+
action=lambda c: c.get_home(limit=limit, after=after),
|
|
197
|
+
data_title="🏠 Home Feed",
|
|
198
|
+
next_cmd="rdt feed",
|
|
199
|
+
as_json=as_json, as_yaml=as_yaml,
|
|
200
|
+
output_file=output_file, full_text=full_text, compact=compact,
|
|
201
|
+
)
|
|
180
202
|
|
|
181
203
|
|
|
182
204
|
# ── popular ─────────────────────────────────────────────────────────
|
|
@@ -49,6 +49,7 @@ SAVE_URL = "/api/save"
|
|
|
49
49
|
UNSAVE_URL = "/api/unsave"
|
|
50
50
|
SUBSCRIBE_URL = "/api/subscribe"
|
|
51
51
|
COMMENT_URL = "/api/comment"
|
|
52
|
+
SUBSCRIPTIONS_URL = "/subreddits/mine/subscriber.json"
|
|
52
53
|
|
|
53
54
|
# ── Request Headers (Chrome 133, macOS) ─────────────────────────────
|
|
54
55
|
HEADERS = {
|
|
@@ -672,7 +672,7 @@ class TestMockedBrowse:
|
|
|
672
672
|
from rdt_cli.auth import Credential
|
|
673
673
|
|
|
674
674
|
cred = Credential(cookies={"reddit_session": "test"}, username="spez")
|
|
675
|
-
with patch("rdt_cli.
|
|
675
|
+
with patch("rdt_cli.commands._common.get_credential", return_value=cred):
|
|
676
676
|
with patch("rdt_cli.client.RedditClient.get_me", return_value={"name": "spez"}):
|
|
677
677
|
with patch("rdt_cli.client.RedditClient.get_user_saved", return_value=self._mock_listing()):
|
|
678
678
|
result = runner.invoke(cli, ["saved", "--json"])
|
|
@@ -682,13 +682,70 @@ class TestMockedBrowse:
|
|
|
682
682
|
from rdt_cli.auth import Credential
|
|
683
683
|
|
|
684
684
|
cred = Credential(cookies={"reddit_session": "test"}, username="spez")
|
|
685
|
-
with patch("rdt_cli.
|
|
685
|
+
with patch("rdt_cli.commands._common.get_credential", return_value=cred):
|
|
686
686
|
with patch("rdt_cli.client.RedditClient.get_me", return_value={"name": "spez"}):
|
|
687
687
|
with patch("rdt_cli.client.RedditClient.get_user_upvoted", return_value=self._mock_listing()):
|
|
688
688
|
result = runner.invoke(cli, ["upvoted", "--json"])
|
|
689
689
|
assert result.exit_code == 0
|
|
690
690
|
|
|
691
691
|
|
|
692
|
+
# ── Mocked subs-only feed ──────────────────────────────────────────
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
class TestSubsOnlyFeed:
|
|
696
|
+
"""Test --subs-only flag on feed command."""
|
|
697
|
+
|
|
698
|
+
def _mock_subs_listing(self, names):
|
|
699
|
+
"""Build a mock /subreddits/mine/subscriber response."""
|
|
700
|
+
return {
|
|
701
|
+
"data": {
|
|
702
|
+
"children": [{"data": {"display_name": n}} for n in names],
|
|
703
|
+
"after": None,
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
def _mock_sub_posts(self, subreddit, created_utc=1700000000):
|
|
708
|
+
return {
|
|
709
|
+
"data": {
|
|
710
|
+
"children": [
|
|
711
|
+
{"data": {"id": f"{subreddit}_1", "title": f"Post from {subreddit}",
|
|
712
|
+
"subreddit": subreddit, "author": "bob", "score": 10,
|
|
713
|
+
"num_comments": 1, "created_utc": created_utc}},
|
|
714
|
+
],
|
|
715
|
+
"after": None,
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
def test_feed_subs_only_json(self):
|
|
720
|
+
from rdt_cli.auth import Credential
|
|
721
|
+
cred = Credential(cookies={"reddit_session": "test"})
|
|
722
|
+
with patch("rdt_cli.commands._common.get_credential", return_value=cred):
|
|
723
|
+
with patch("rdt_cli.client.RedditClient.get_my_subscriptions", return_value=["python", "rust"]):
|
|
724
|
+
with patch("rdt_cli.client.RedditClient.get_subreddit") as mock_sub:
|
|
725
|
+
mock_sub.side_effect = [
|
|
726
|
+
self._mock_sub_posts("python", created_utc=1700000200),
|
|
727
|
+
self._mock_sub_posts("rust", created_utc=1700000100),
|
|
728
|
+
]
|
|
729
|
+
result = runner.invoke(cli, ["feed", "--subs-only", "--json"])
|
|
730
|
+
assert result.exit_code == 0
|
|
731
|
+
data = json.loads(result.output)
|
|
732
|
+
assert data["ok"] is True
|
|
733
|
+
|
|
734
|
+
def test_feed_subs_only_empty_subscriptions(self):
|
|
735
|
+
from rdt_cli.auth import Credential
|
|
736
|
+
cred = Credential(cookies={"reddit_session": "test"})
|
|
737
|
+
with patch("rdt_cli.commands._common.get_credential", return_value=cred):
|
|
738
|
+
with patch("rdt_cli.client.RedditClient.get_my_subscriptions", return_value=[]):
|
|
739
|
+
result = runner.invoke(cli, ["feed", "--subs-only", "--json"])
|
|
740
|
+
assert result.exit_code == 0
|
|
741
|
+
|
|
742
|
+
def test_feed_help_shows_subs_only(self):
|
|
743
|
+
result = runner.invoke(cli, ["feed", "--help"])
|
|
744
|
+
assert result.exit_code == 0
|
|
745
|
+
assert "--subs-only" in result.output
|
|
746
|
+
assert "--max-subs" in result.output
|
|
747
|
+
|
|
748
|
+
|
|
692
749
|
# ── Mocked search commands ──────────────────────────────────────────
|
|
693
750
|
|
|
694
751
|
|
|
@@ -554,6 +554,20 @@ class TestAuthenticatedViews:
|
|
|
554
554
|
if data:
|
|
555
555
|
assert data["ok"] is True
|
|
556
556
|
|
|
557
|
+
def test_feed_subs_only(self):
|
|
558
|
+
if not _authenticated():
|
|
559
|
+
pytest.skip("Authentication required for --subs-only")
|
|
560
|
+
result = _invoke("feed", "--subs-only", "-n", "3", "--max-subs", "3")
|
|
561
|
+
assert result.exit_code == 0
|
|
562
|
+
|
|
563
|
+
def test_feed_subs_only_json(self):
|
|
564
|
+
if not _authenticated():
|
|
565
|
+
pytest.skip("Authentication required for --subs-only")
|
|
566
|
+
result, data = _invoke_json("feed", "--subs-only", "-n", "3", "--max-subs", "3")
|
|
567
|
+
assert result.exit_code == 0
|
|
568
|
+
if data:
|
|
569
|
+
assert data["ok"] is True
|
|
570
|
+
|
|
557
571
|
|
|
558
572
|
# ── Pagination ─────────────────────────────────────────────────────
|
|
559
573
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|