dvach-cli — specification (README.md)
Build a CLI for browsing the 2ch.su (Двач) imageboard. Written in Python
with Typer for command handling and
Rich for formatted output.
The package is already scaffolded in src/dvach_cli/. The binary is
named dvach (see pyproject.toml). Use uv sync to install deps and
the entry point; use uv run dvach … to run it.
Environment
The base URL for API calls is controlled by DVACH_DOMAIN (env) or the
global --domain flag. Default: 2ch.su (implied scheme https).
Important: DVACH_DOMAIN / --domain may include a URL scheme.
If it starts with http:// or https://, use it verbatim. Otherwise
prefix https://. This lets the tool point at local test servers over
plain HTTP.
API endpoints
GET /api/mobile/v2/boards— array of boards:
{ "id": "b", "name": "Random", "category": "..." }GET /{board}/catalog.json—{ "threads": [...] }, each thread:
{ num, subject, comment, posts_count, files_count, views, timestamp, sticky, closed, tags }GET /{board}/res/{thread_id}.json— thread data:
{ title, posts_count, files_count, unique_posters, is_closed, threads: [ { posts: [Post, ...] } ] }, where
Post = { num, comment (HTML), name, subject, date, timestamp, files: [{ displayname, name, size, path }] }.
On HTTP error: print red error line and exit with code 1.
URL shortcut
Wherever a command takes <board> <thread_id> positional args, the
caller may instead pass a full thread URL as the board argument
(leaving thread_id as 0/default). URL shape:
https://2ch.XX/<board>/res/<num>.html optionally with #<post> anchor.
Regex: https?://2ch\.\w+/(\w+)/res/(\d+)\.html(?:#\d+)?
HTML → text
Post comment fields are HTML. Conversion rules:
- Reply links
<a ... data-num="NNNN">...</a>→>>NNNN <br>/<br/>→ newline- All other tags → stripped
- Decode HTML entities (
&,>, etc.) - Collapse 3+ consecutive newlines to 2
This helper is also used for text-based filtering in catalog and
search (matching is done on plain text, not raw HTML).
Reply detection
A post "replies to" N if its comment contains
<a ... data-num="N">...</a> or a >>N token after HTML stripping.
Extract in order of appearance, deduplicate per post.
Commands
All commands support --raw to emit JSON on stdout instead of Rich
output. Exit 0 on success or soft "not found"; exit 1 on HTTP/parse error.
dvach boards [query] [--raw]
List all boards, optionally filtered by substring match
(case-insensitive) on board id, name, or category.
--raw: JSON array of filtered board objects (verbatim from API).
dvach catalog <board> [query] [-n LIMIT] [--sort {bump,date,posts,views}] [--raw]
Fetch {board}/catalog.json. Optionally filter by substring on thread
subject or textual form of comment. Sort modes:
bump(default) — keep API orderdate—timestampdescendingposts—posts_countdescendingviews—viewsdescending
Take first LIMIT entries (default 20). --raw: JSON array of thread
entries (verbatim, filtered+sorted+sliced).
dvach open <board> <thread_id> [--raw]
Fetch the thread, print title + metadata + OP. --raw:
dvach read <board> <thread_id> [from_index] [-n COUNT] [--raw]
Print COUNT posts starting at from_index (0 = OP). If from_index is past
the end, print a warning and exit 0. Default COUNT 20. --raw: JSON
array of the sliced post objects.
dvach post <board> <thread_id> <post_num> [--raw]
Find post by num. If not found, yellow warning + exit 0. Otherwise
print post + list of post numbers replying to it. --raw: the post
object verbatim.
dvach replies <board> <thread_id> <post_num> [-d DEPTH] [--raw]
Recursively find posts that reply to post_num, up to DEPTH levels
(default 3). Each discovered post appears once. Sort by original index
in the thread (asc). --raw: JSON array of reply post objects verbatim.
dvach search <board> <thread_id> <query> [-n LIMIT] [--raw]
Iterate posts in order, include those whose html_to_text(comment)
contains query (case-insensitive). Stop after LIMIT matches (default 20).
--raw: JSON array of matching posts verbatim.
dvach last <board> <thread_id> [-n COUNT] [--raw]
Last COUNT posts of the thread (default 20). --raw: JSON array of
those posts verbatim.
What to deliver
A working implementation in src/dvach_cli/ that uv sync installs and
uv run dvach … runs. Split code across files inside the package as
you like; keep dvach_cli.cli:app as the entry point.
The grader invokes the tool as a subprocess with DVACH_DOMAIN pointing
at a local HTTP fixture server and parses --raw output — treat the
--raw shape as a hard contract.