Repository navigation
feat: comment-moderation commands — report, ban, report-user, bans, moderation helpers - #18
Conversation
New commands
------------
comments report <comment_id> report a comment to publication owner + optionally Substack T&S
comments report-user <user_id> report a user profile (routes to substack.com host)
comments ban <comment_id> ban a commenter; expiry=ISO-8601, visibility=visible|delete_all_comments
comments bans <user_id> read existing bans for a user (read-only)
moderation report-categories fetch Substack reporting flow tree (read-only)
moderation delete-reasons fetch comment deletion reason codes (read-only)
All write commands:
- gated by is_write_enabled() (SUBSTACK_ENABLE_WRITE=true required)
- default to dry-run: print exact {method, url, body} without firing
- pass --no-dry-run / --yes to execute
Endpoints (reverse-engineered from Substack web bundle bb5e503)
---------------------------------------------------------------
POST /api/v1/comment/{id}/report
body: {publication_id, details, reportCategory, reportedToSubstack, report_context}
POST https://substack.com/api/v1/users/{id}/profile/report
body: {details, reportCategory, surface:"profile", reportedToSubstack, report_context}
POST /api/v1/comment_ban/{comment_id}
body: {user_id, expiry, commentVisibility}
GET /api/v1/user/{id}/bans
GET https://substack.com/api/v1/reporting/flows
GET /api/v1/comment/moderation/delete_reasons
Bundle findings
---------------
commentVisibility enum: "visible" | "delete_all_comments" (confirmed)
reportCategory values: spam, impersonation, hate, private_information,
plagiarism, illegal_activity, pornography, other (confirmed from module 74588)
expiry: ISO-8601 datetime string (inferred from isAfter() comparison in ban UI)
report_context: shape not found in bundle; accepted as optional JSON string
Tests: 24 new tests in test_moderation.py; full suite 385 passed
Motivation: scam accounts impersonating Charlie Garcia with WhatsApp solicitation
comments (17-111 per burst) need CLI-level report+ban for agent automation
…nt visibility Substack's ban modal posts expiry as one of 1d/30d/90d/1y/1000y (bundle module 92538) rather than an ISO datetime, and its comment_visibility select has three options (bundle module 5646): visible, delete_this_comment, delete_all_comments. Validate both in the client function and the CLI, and update docs/tests.
|
Correction pushed ( |
land round 1: 4 blocking findings @ 2bfcbf8Local gate at 2bfcbf8:
Non-blocking items (invalid |
…ion_core.py Address land round 1 review on PR griffinwork40#18: - comments report no longer calls the unverified /api/v1/publication/self endpoint (which fired even in dry-run and silently dropped publication_id on failure). publication_id is now an explicit optional --publication-id. - Split enums, core API functions and shared body builders into substack_cli/moderation_core.py so dry-run and live bodies come from the same builder; both modules are under 350 LOC. - Document the new commands in SKILL.md and the new routes in references/substack-api.md.
Summary
Adds CLI commands to report and ban scam accounts impersonating Charlie Garcia on charliepgarcia.substack.com ("Charlie WhatsApp +1672..." pattern, 17–111 filler comments per burst).
New commands
comments report <comment_id>comments report-user <user_id>comments ban <comment_id>comments bans <user_id>moderation report-categoriesmoderation delete-reasonsAll write commands:
SUBSTACK_ENABLE_WRITE=true{method, url, body}without firing--no-dry-run/--yesto executeEndpoints (reverse-engineered from bundle bb5e503)
Bundle findings
commentVisibilityenum:"visible"|"delete_all_comments"— confirmed from ban UI toggle in bundlereportCategoryvalues: spam, impersonation, hate, private_information, plagiarism, illegal_activity, pornography, other — confirmed from bundle module 74588expiry: ISO-8601 datetime string — inferred fromisAfter()comparison in ban UI; not live-verifiedreport_context: shape not found in bundle; accepted as optional--report-context <JSON>string; omit in most casesSmoke-test recipe (dry-run first, then real)
Tests
24 new tests in
tests/test_moderation.py. Full suite: 385 passed.Open questions / not live-verified
report_contextshape: unknown, accepted as optional JSON stringexpiryformat: inferred ISO-8601, not exercised against real APIsubstack.com(not yet exposed as a flag)