docs: update README for Web UI, SSE, link cards/summary; add CHANGELOG Beta 2 (v0.2.0)
This commit is contained in:
parent
2c20b9a638
commit
2f9ab6a414
3 changed files with 61 additions and 40 deletions
13
CHANGELOG.md
13
CHANGELOG.md
|
|
@ -2,6 +2,19 @@
|
|||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## Beta 2 (v0.2.0)
|
||||
- Web UI overhaul: fixed header, single-scroll layout, auto pin-to-bottom, infinite scroll history
|
||||
- Channel selector moved into menubar brand; commit hash shown subtly
|
||||
- SSE realtime updates and JSON APIs for info, channels, tail, history, stream
|
||||
- Link cards: robust OG/Twitter parsing, X (Twitter) oEmbed embeds, YouTube oEmbed, direct image previews
|
||||
- On-demand link summaries with caching; inline actions (🌚/🌝, ▾) and collapse/expand
|
||||
- Dedicated link-only LLM prompt; improved fetch headers; readability extraction; image-to-vision support
|
||||
- Mention detection/rate limiting/quiet hours retained and refined
|
||||
- Store: case-insensitive channel queries, msgid de-dup, WAL, history paging
|
||||
- Metrics: messages ingested, notifications sent, pruned, connection gauge
|
||||
- Docker: multi-arch build, healthcheck; version/commit/build injected
|
||||
- Numerous bug fixes (template rendering, static assets path, scroll duplicates, initial bottom snap)
|
||||
|
||||
## Beta 1 (initial release)
|
||||
- soju-specific raw connector with event playback and CHATHISTORY fallback
|
||||
- Message storage (SQLite, WAL), msgid de-dup, retention job
|
||||
|
|
|
|||
76
README.md
76
README.md
|
|
@ -6,7 +6,7 @@ An IRC bouncer companion service for soju that:
|
|||
- Notifies you on mentions via Pushover (default)
|
||||
- Stores messages in SQLite for summaries and on-demand inspection
|
||||
- Generates AI digests (OpenAI by default) on schedule or on demand
|
||||
- Exposes a small HTTP API for health, tailing messages, metrics, and triggering digests
|
||||
- Exposes a small HTTP API and a minimal Web UI (Pico.css) for status, tail, history, link cards, and on-demand summaries
|
||||
|
||||
Note: this is not a bot and never replies in IRC. It passively attaches as a soju multi-client on your main account.
|
||||
|
||||
|
|
@ -18,20 +18,20 @@ If you use soju as a bouncer, you may want per-client alerts and AI summaries wi
|
|||
|
||||
- Language: Go (single static binary, low memory footprint)
|
||||
- Long-lived IRC client: raw IRC using a lightweight parser (sorcix/irc) with an irssi-style handshake tailored for soju
|
||||
- Message storage: SQLite via modernc.org/sqlite
|
||||
- Message storage: SQLite via modernc.org/sqlite (WAL enabled)
|
||||
- Scheduling: github.com/robfig/cron/v3
|
||||
- Notifications: github.com/gregdel/pushover
|
||||
- Summarization (LLM): github.com/sashabaranov/go-openai
|
||||
- HTTP API: Go stdlib `net/http`
|
||||
- HTTP API + Web UI: Go stdlib `net/http` + `html/template` + embedded static assets
|
||||
|
||||
Runtime modules:
|
||||
|
||||
- `internal/soju`: soju connection, capability negotiation, irssi-style PASS/USER auth, joins, message ingestion, event playback, CHATHISTORY fallback
|
||||
- `internal/store`: SQLite schema and queries
|
||||
- `internal/notifier`: Pushover notifier (pluggable interface)
|
||||
- `internal/summarizer`: OpenAI client with GPT-5 defaults, GPT-4o-mini fallback
|
||||
- `internal/summarizer`: OpenAI client with GPT-5 defaults, GPT-4o-mini fallback; separate link-summarization prompt
|
||||
- `internal/scheduler`: cron-based digest scheduling and daily retention job
|
||||
- `internal/httpapi`: `/healthz`, `/ready`, `/tail`, `/trigger`, `/metrics`
|
||||
- `internal/httpapi`: `/healthz`, `/ready`, `/tail`, `/trigger`, `/metrics`, Web UI and JSON APIs
|
||||
- `internal/config`: env config loader and helpers
|
||||
|
||||
## Features
|
||||
|
|
@ -41,7 +41,11 @@ Runtime modules:
|
|||
- AI digest generation: concise natural summaries (no rigid sections); integrates pasted multi-line posts and referenced link context; image links sent to GPT‑5 as vision inputs
|
||||
- Configurable schedules (cron), quiet hours, and summary parameters
|
||||
- Local persistence with retention pruning (daily at 03:00)
|
||||
- HTTP endpoints: health, tail, metrics, on-demand digests
|
||||
- Web UI with:
|
||||
- Realtime chat tail via SSE; auto-scroll to bottom; preload older history with infinite scroll-up
|
||||
- Link cards with OG/Twitter metadata (X posts via oEmbed), YouTube oEmbed embeds, direct image previews
|
||||
- Inline on-demand link summarization with caching (24h), and a single summarize toggle (🌚/🌝)
|
||||
- Channel selector in the menubar, login interstitial using `HTTP_TOKEN`
|
||||
|
||||
## How it works
|
||||
|
||||
|
|
@ -60,20 +64,21 @@ Runtime modules:
|
|||
4) Messages and mentions:
|
||||
- Each `PRIVMSG` is stored with server-time when available
|
||||
- Mentions trigger Pushover notifications subject to quiet hours, urgency, and rate limits
|
||||
- Debug logs include: mention delivered or suppression reason (backfill, quiet hours, rate limit)
|
||||
|
||||
5) Summarization:
|
||||
- `/trigger` or the scheduler loads a window and calls OpenAI
|
||||
- GPT‑5 context: ~272k input tokens + up to 128k output tokens (400k total)
|
||||
- Summaries are concise/natural and integrate multi-line posts, article text (readability-extracted), and image links (vision)
|
||||
- Digests: `/trigger` or the scheduler loads a window and calls OpenAI with a conversation-focused prompt
|
||||
- Link summaries: dedicated prompt that ignores chat context; fetches page content with readability; includes oEmbed hints for YouTube and X; passes images to vision models
|
||||
|
||||
6) HTTP API:
|
||||
6) HTTP + JSON API:
|
||||
- `/healthz` → `200 ok`
|
||||
- `/ready` → `200` only when connected to soju
|
||||
- `/tail?channel=#chan&limit=N` → plaintext tail (chronological)
|
||||
- `/trigger?channel=#chan&window=6h` → returns digest and sends via notifier
|
||||
- `/tail?channel=#chan&limit=N` → JSON tail for UI
|
||||
- `/history?channel=#chan&before=<RFC3339>&limit=N` → JSON older messages (infinite scroll)
|
||||
- `/trigger?channel=#chan&window=6h` → returns digest JSON and (optionally) pushes via notifier
|
||||
- `/linkcard?url=...` → card JSON (title/desc/image or embed HTML)
|
||||
- `/linksummary?url=...` → brief AI summary of a single URL (cached 24h)
|
||||
- `/metrics` → Prometheus text metrics
|
||||
- Protect `/tail` and `/trigger` with `HTTP_TOKEN` via Bearer, `token` query, `X-Auth-Token`, or basic auth (`token:<HTTP_TOKEN>`)
|
||||
- Protect UI + JSON with `HTTP_TOKEN` cookie; APIs also allow Bearer/query token
|
||||
|
||||
## Health and readiness
|
||||
|
||||
|
|
@ -222,7 +227,7 @@ Compose (with localhost bind suitable for Synology reverse proxy):
|
|||
```yaml
|
||||
services:
|
||||
sojuboy:
|
||||
image: code.cravey.net/your-user/sojuboy:v0.1.0-beta1
|
||||
image: code.cravey.net/your-user/sojuboy:v0.2.0-beta2
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
ports:
|
||||
|
|
@ -241,7 +246,7 @@ services:
|
|||
```yaml
|
||||
services:
|
||||
sojuboy:
|
||||
image: code.cravey.net/your-user/sojuboy:v0.1.0-beta1
|
||||
image: code.cravey.net/your-user/sojuboy:v0.2.0-beta2
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:8080:8080" # bind only to localhost; fronted by DSM Reverse Proxy
|
||||
|
|
@ -316,16 +321,16 @@ services:
|
|||
| OPENAI_API_KEY | (empty) |
|
||||
| OPENAI_BASE_URL | (empty) |
|
||||
| OPENAI_MODEL | gpt-5 |
|
||||
| OPENAI_MAX_TOKENS | 700 |
|
||||
| OPENAI_MAX_TOKENS | 128000 |
|
||||
| SUMM_FOLLOW_LINKS | true |
|
||||
| SUMM_LINK_TIMEOUT | 6s |
|
||||
| SUMM_LINK_MAX_BYTES | 262144 |
|
||||
| SUMM_GROUP_WINDOW | 90s |
|
||||
| SUMM_MAX_LINKS | 5 |
|
||||
| SUMM_LINK_TIMEOUT | 20s |
|
||||
| SUMM_LINK_MAX_BYTES | 1048576 |
|
||||
| SUMM_GROUP_WINDOW | 120s |
|
||||
| SUMM_MAX_LINKS | 20 |
|
||||
| SUMM_MAX_GROUPS | 0 |
|
||||
| SUMM_TIMEOUT | 5m |
|
||||
| SUMM_TIMEOUT | 10m |
|
||||
| DIGEST_CRON | 0 */6 * * * |
|
||||
| DIGEST_WINDOW | 6h |
|
||||
| DIGEST_WINDOW | 24h |
|
||||
| QUIET_HOURS | (empty) |
|
||||
| NOTIFY_BACKFILL | false |
|
||||
| MENTION_MIN_INTERVAL | 30s |
|
||||
|
|
@ -335,7 +340,7 @@ services:
|
|||
| HTTP_LISTEN | :8080 |
|
||||
| HTTP_TOKEN | (empty) |
|
||||
| STORE_PATH | /data/app.db |
|
||||
| STORE_RETENTION_DAYS | 7 |
|
||||
| STORE_RETENTION_DAYS | 365 |
|
||||
| LOG_LEVEL | info |
|
||||
|
||||
## Pushover setup
|
||||
|
|
@ -355,25 +360,22 @@ services:
|
|||
## HTTP API
|
||||
|
||||
- `GET /healthz` → `200 ok`
|
||||
- `GET /tail?channel=%23chan&limit=50`
|
||||
- Returns plaintext messages (chronological)
|
||||
- Auth: provide `HTTP_TOKEN` as a Bearer token (or query param `token=`)
|
||||
- `GET /trigger?channel=%23chan&window=6h`
|
||||
- Returns plaintext digest
|
||||
- Also sends via notifier when configured
|
||||
- Auth as above
|
||||
- `GET /tail?channel=%23chan&limit=50` (JSON)
|
||||
- `GET /history?channel=%23chan&before=<RFC3339>&limit=50` (JSON)
|
||||
- `GET /trigger?channel=%23chan&window=6h` (JSON)
|
||||
- `GET /linkcard?url=…` (JSON)
|
||||
- `GET /linksummary?url=…` (JSON)
|
||||
- `GET /metrics`
|
||||
- Prometheus metrics: `sojuboy_messages_ingested_total`, `sojuboy_notifications_sent_total`, `sojuboy_messages_pruned_total`, `sojuboy_connected`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- Empty tail while there’s activity
|
||||
- Ensure the service logs `join requested:` followed by `joined` for your channels
|
||||
- Ensure the service logs readiness and joins for your channels
|
||||
- Confirm `.env` `CHANNELS` contains your channels
|
||||
- Check for `/metrics` and logs for recent message ingestion
|
||||
- Check `/metrics` and logs for recent message ingestion
|
||||
|
||||
- 401 Unauthorized from `/tail` or `/trigger`
|
||||
- Provide `Authorization: Bearer $HTTP_TOKEN` or `?token=$HTTP_TOKEN`
|
||||
- 401 Unauthorized from UI/API
|
||||
- Log in at `/login` with `HTTP_TOKEN`, or pass it via Bearer/`token=`
|
||||
|
||||
- OpenAI 502/URL errors
|
||||
- Ensure `OPENAI_BASE_URL=https://api.openai.com/v1`
|
||||
|
|
@ -394,7 +396,7 @@ Project layout (selected):
|
|||
- `internal/store` – SQLite schema and queries
|
||||
- `internal/notifier` – Pushover notifier
|
||||
- `internal/summarizer` – OpenAI client and prompts
|
||||
- `internal/httpapi` – health, tail, trigger, metrics endpoints
|
||||
- `internal/httpapi` – UI and endpoints
|
||||
- `internal/scheduler` – cron jobs
|
||||
|
||||
Go toolchain: see `go.mod` (Go 1.23), Dockerfile builds static binary for a distroless image.
|
||||
|
|
|
|||
|
|
@ -294,11 +294,17 @@ func (o *OpenAI) SummarizeLink(ctx context.Context, rawURL string) (string, erro
|
|||
},
|
||||
MaxCompletionTokens: o.maxTokens,
|
||||
}
|
||||
if !reasoningLike { req.Temperature = 0.2 }
|
||||
if !reasoningLike {
|
||||
req.Temperature = 0.2
|
||||
}
|
||||
|
||||
resp, err := client.CreateChatCompletion(ctx, req)
|
||||
if err != nil { return "", err }
|
||||
if len(resp.Choices) == 0 { return "", nil }
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(resp.Choices) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
return strings.TrimSpace(resp.Choices[0].Message.Content), nil
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue