rebooting project sans history
This commit is contained in:
commit
874b1632c7
8 changed files with 1285 additions and 0 deletions
1
.python-version
Normal file
1
.python-version
Normal file
|
@ -0,0 +1 @@
|
|||
3.12
|
6
Dockerfile
Normal file
6
Dockerfile
Normal file
|
@ -0,0 +1,6 @@
|
|||
FROM python:3.12-slim-bookworm
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
||||
ADD . /app
|
||||
WORKDIR /app
|
||||
RUN uv sync --frozen --no-cache
|
||||
CMD ["/app/.venv/bin/fastapi", "run", "main.py", "--port", "8887"]
|
32
README.md
Normal file
32
README.md
Normal file
|
@ -0,0 +1,32 @@
|
|||
# rss
|
||||
|
||||
collection of rss-related tools
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.yml up -d
|
||||
```
|
||||
|
||||
example secrets file:
|
||||
|
||||
```bash
|
||||
# secrets.sh
|
||||
export RSS_HOST="http://localhost:8888"
|
||||
export REDDIT_CLIENT_ID="blah"
|
||||
export REDDIT_CLIENT_SECRET="blah"
|
||||
export REDDIT_USERNAME="blah"
|
||||
export REDDIT_PASSWORD="bla"
|
||||
```
|
||||
|
||||
editing secrets:
|
||||
|
||||
```bash
|
||||
age --decrypt -i path/to/key -o secrets.sh secrets.sh.age
|
||||
age -r $RECIPIENT -o secrets.sh.age secrets.sh
|
||||
```
|
||||
|
||||
example envrc:
|
||||
|
||||
```bash
|
||||
# .envrc
|
||||
age --decrypt -i path/to/key -o secrets.sh secrets.sh.age && source secrets.sh && rm secrets.sh
|
||||
```
|
44
docker-compose.yml
Normal file
44
docker-compose.yml
Normal file
|
@ -0,0 +1,44 @@
|
|||
services:
|
||||
miniflux:
|
||||
image: miniflux/miniflux:latest
|
||||
ports:
|
||||
- "8888:8080"
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- DATABASE_URL=postgres://miniflux:secret@db/miniflux?sslmode=disable
|
||||
- RUN_MIGRATIONS=1
|
||||
- CREATE_ADMIN=1
|
||||
- ADMIN_USERNAME=admin
|
||||
- ADMIN_PASSWORD=test123
|
||||
- HTTP_CLIENT_TIMEOUT=300
|
||||
|
||||
db:
|
||||
image: postgres:17-alpine
|
||||
environment:
|
||||
- POSTGRES_USER=miniflux
|
||||
- POSTGRES_PASSWORD=secret
|
||||
- POSTGRES_DB=miniflux
|
||||
volumes:
|
||||
- miniflux-db:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD", "pg_isready", "-U", "miniflux"]
|
||||
interval: 10s
|
||||
start_period: 30s
|
||||
|
||||
reddit_rss:
|
||||
build:
|
||||
context: ./
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- "8887:8887"
|
||||
environment:
|
||||
- REDDIT_CLIENT_ID
|
||||
- REDDIT_CLIENT_SECRET
|
||||
- REDDIT_PASSWORD
|
||||
- REDDIT_USERNAME
|
||||
- RSS_HOST
|
||||
|
||||
volumes:
|
||||
miniflux-db:
|
73
main.py
Normal file
73
main.py
Normal file
|
@ -0,0 +1,73 @@
|
|||
from contextlib import asynccontextmanager
|
||||
from datetime import datetime, timezone
|
||||
from os import environ
|
||||
|
||||
import asyncpraw
|
||||
from async_lru import alru_cache
|
||||
from fastapi import FastAPI, Response, HTTPException
|
||||
from feedgen.feed import FeedGenerator
|
||||
|
||||
|
||||
REDDIT_CLIENT_ID = environ["REDDIT_CLIENT_ID"]
|
||||
REDDIT_CLIENT_SECRET = environ["REDDIT_CLIENT_SECRET"]
|
||||
REDDIT_PASSWORD = environ["REDDIT_PASSWORD"]
|
||||
REDDIT_USERNAME = environ["REDDIT_USERNAME"]
|
||||
RSS_HOST = environ["RSS_HOST"]
|
||||
|
||||
|
||||
@alru_cache(ttl=15 * 60)
|
||||
async def fetch(feed, comments):
|
||||
fg = FeedGenerator()
|
||||
fg.id(f'{RSS_HOST}/{feed}')
|
||||
fg.title(f'r/{feed}')
|
||||
fg.link(href=f'{RSS_HOST}/{feed}')
|
||||
fg.description(f'r/{feed}')
|
||||
|
||||
client = asyncpraw.Reddit(
|
||||
user_agent="stuff",
|
||||
client_id=REDDIT_CLIENT_ID,
|
||||
client_secret=REDDIT_CLIENT_SECRET,
|
||||
username=REDDIT_USERNAME,
|
||||
password=REDDIT_PASSWORD,
|
||||
)
|
||||
|
||||
async with client as reddit:
|
||||
subreddit = await reddit.subreddit(feed)
|
||||
async for submission in subreddit.top(limit=30, time_filter="week"):
|
||||
if submission.selftext == "":
|
||||
continue
|
||||
|
||||
fe = fg.add_entry()
|
||||
fe.id(submission.id)
|
||||
fe.title(submission.title)
|
||||
fe.link(href=submission.url)
|
||||
fe.pubDate(datetime.fromtimestamp(submission.created_utc, timezone.utc))
|
||||
author = 'none' if submission.author is None else submission.author.name
|
||||
content = f"<p>{author} ({submission.score})</p>"
|
||||
content += f"<p>{submission.selftext_html}</p>"
|
||||
if comments:
|
||||
comments = ""
|
||||
for comment in await submission.comments():
|
||||
if isinstance(comment, asyncpraw.models.MoreComments):
|
||||
continue
|
||||
if comment.author is None:
|
||||
continue
|
||||
author = comment.author.name
|
||||
if author.lower() == 'automoderator':
|
||||
continue
|
||||
comments += f"<li>{author} ({comment.score}) {comment.body_html}"
|
||||
content += f"<ul>{comments}</ul>"
|
||||
|
||||
fe.content(content)
|
||||
|
||||
rss = fg.rss_str(pretty=True)
|
||||
return rss
|
||||
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.get("/{feed}")
|
||||
async def root(feed: str, comments: bool = False):
|
||||
rss = await fetch(feed, comments)
|
||||
return Response(content=rss, media_type="application/xml")
|
12
pyproject.toml
Normal file
12
pyproject.toml
Normal file
|
@ -0,0 +1,12 @@
|
|||
[project]
|
||||
name = "rss"
|
||||
version = "0.1.0"
|
||||
description = "reformats reddit for rss consumption"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"async-lru>=2.0.5",
|
||||
"asyncpraw>=7.8.1",
|
||||
"fastapi[standard]>=0.115.12",
|
||||
"feedgen>=1.0.0",
|
||||
]
|
BIN
secrets.sh.age
Normal file
BIN
secrets.sh.age
Normal file
Binary file not shown.
Loading…
Add table
Reference in a new issue