First commit
This commit is contained in:
48
CLAUDE.md
Normal file
48
CLAUDE.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project
|
||||
|
||||
A FreshRSS extension that enriches Bluesky posts in RSS feeds by fetching the full reply thread and embedded post content via the Bluesky public API, collapsing everything into a single article.
|
||||
|
||||
## Extension structure
|
||||
|
||||
FreshRSS expects the extension directory name to start with `x`. Required files:
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `metadata.json` | Extension metadata (`name`, `entrypoint` are required) |
|
||||
| `extension.php` | Main class — must be named `{entrypoint}Extension extends Minz_Extension` |
|
||||
| `configure.phtml` | Optional settings form; submitted values handled by `handleConfigureAction()` |
|
||||
|
||||
The extension is installed by dropping this directory into FreshRSS's `extensions/` folder.
|
||||
|
||||
## How it works
|
||||
|
||||
Two hooks work in tandem so both the web UI and API sync clients (Fever, GReader, etc.) receive enriched content:
|
||||
|
||||
1. **`EntryBeforeInsert`** (`fetchThread`) — fires once when a new entry is first saved to the DB. Fetches the thread immediately and stores it, so API clients get enriched content from the very first sync.
|
||||
2. **`EntryBeforeDisplay`** (`refreshThread`) — fires on every web render. Checks the file cache for staleness; if stale, re-fetches and calls `FreshRSS_Factory::createEntryDao()->updateEntry($entry)` to write the refreshed HTML back to the DB, keeping API clients up to date.
|
||||
3. **Staleness rules** (`needsRefetch`): posts ≥ 7 days old are frozen. Fresher posts use progressively tighter refresh windows — 10 min (< 1 h old), 1 h (< 24 h old), 12 h (< 7 d old).
|
||||
4. **Cache:** JSON files in `DATA_PATH/BlueskyThreads/{md5(url)}.json`, each containing `{html, fetched_at}`. On API failure, the stale cache is served as a fallback.
|
||||
5. **Handle → DID:** `GET https://public.api.bsky.app/xrpc/com.atproto.identity.resolveHandle?handle={handle}` (skipped if the handle is already a `did:` URI).
|
||||
6. **Thread fetch:** `GET https://public.api.bsky.app/xrpc/app.bsky.feed.getPostThread?uri=at://{did}/app.bsky.feed.post/{rkey}&depth={depth}&parentHeight=0`
|
||||
7. **Rendering:** `renderThread()` walks the recursive `threadViewPost` structure. Root post uses a bordered card style; replies are indented under a left border. Each post renders its richtext facets (links, mentions, hashtags) and embeds (images, external links, quoted posts, video).
|
||||
|
||||
## Key implementation details
|
||||
|
||||
- **Facets** — Bluesky richtext uses UTF-8 *byte* offsets (`byteStart`/`byteEnd`). `applyFacets()` walks the raw PHP byte string directly rather than converting to characters first.
|
||||
- **Embed type normalisation** — the API returns `$type` values like `app.bsky.embed.images#view`; the `#view` suffix is stripped before the switch statement.
|
||||
- **User config** — `depth` (int, 1–1000, default 10) is stored via `setUserConfigurationValue`/`getUserConfigurationValue` and read in `handleConfigureAction()` on POST.
|
||||
- **No auth required** — all requests go to `public.api.bsky.app` and need no credentials.
|
||||
|
||||
## Bluesky API reference
|
||||
|
||||
- Thread endpoint: `app.bsky.feed.getPostThread` — params: `uri` (AT-URI), `depth` (0–1000), `parentHeight` (0–1000)
|
||||
- Handle resolution: `com.atproto.identity.resolveHandle` — param: `handle`
|
||||
- Docs: https://docs.bsky.app/docs/api/app-bsky-feed-get-post-thread
|
||||
|
||||
## FreshRSS extension docs
|
||||
|
||||
https://freshrss.github.io/FreshRSS/en/developers/03_Backend/05_Extensions.html
|
||||
Reference in New Issue
Block a user