12 Commits
0.0.3 ... main

Author SHA1 Message Date
5e50466718 Add Mastobot documentation with feed rotors 2026-03-03 20:15:04 +01:00
a217464a7f fix image name
Some checks failed
Build & Release / build-docker-image (push) Successful in 1m11s
Build & Release / deploy-to-production (push) Failing after 5s
2026-02-24 20:24:08 +01:00
ea9e9556f4 scope docker credentials
Some checks failed
Build & Release / build-docker-image (push) Successful in 2m39s
Build & Release / deploy-to-production (push) Failing after 6s
2026-02-24 20:12:53 +01:00
0d45b21bce Update image name
Some checks failed
Build & Release / build-docker-image (push) Failing after 46s
Build & Release / deploy-to-production (push) Has been skipped
2026-02-24 18:31:17 +01:00
3af5f8ff61 Update image name
Some checks failed
Build & Release / build-docker-image (push) Failing after 46s
Build & Release / deploy-to-production (push) Has been skipped
2026-02-24 18:26:28 +01:00
bf939790c0 Update image name
Some checks failed
Build & Release / build-docker-image (push) Failing after 47s
Build & Release / deploy-to-production (push) Has been skipped
2026-02-24 18:24:23 +01:00
6a0b836c0f Update image name
Some checks failed
Build & Release / deploy-to-production (push) Has been skipped
Build & Release / build-docker-image (push) Failing after 47s
2026-02-24 17:55:10 +01:00
16c2c8e386 Log build version
Some checks failed
Build & Release / build-docker-image (push) Failing after 2m36s
Build & Release / deploy-to-production (push) Has been skipped
2026-02-18 19:51:41 +01:00
aeeeebd3da Fix docker image version arg
All checks were successful
Build & Release / build-docker-image (push) Successful in 3m19s
Build & Release / deploy-to-production (push) Successful in 6s
2026-02-18 18:51:16 +01:00
2134eed508 Fix mastobot embed url
All checks were successful
Build & Release / build-docker-image (push) Successful in 1m29s
Build & Release / deploy-to-production (push) Successful in 6s
2026-02-18 18:33:38 +01:00
0ebb992280 Rename image
Some checks failed
Build & Release / build-docker-image (push) Successful in 1m24s
Build & Release / deploy-to-production (push) Failing after 5s
2026-02-17 15:02:25 +01:00
6758d1f0bf Update build act container to Ubuntu 24.04
Some checks failed
Build & Release / build-docker-image (push) Successful in 4m4s
Build & Release / deploy-to-production (push) Failing after 5s
2026-02-17 14:30:45 +01:00
4 changed files with 126 additions and 7 deletions

View File

@@ -7,7 +7,7 @@ on:
env:
ENDPOINT: services-3
STACK: mastodon
IMAGE: com.devsoap/mastobot
IMAGE: john/mastobot
TAG: ${{ gitea.ref_name }}
CACHE_NAME: cache-python-dependencies-mastobot
RUNNER_TOOL_CACHE: /toolcache
@@ -17,7 +17,7 @@ jobs:
build-docker-image:
runs-on: node20
container:
image: catthehacker/ubuntu:act-20.04
image: catthehacker/ubuntu:act-24.04
steps:
- name: Checkout Docker file
uses: actions/checkout@v4
@@ -31,14 +31,16 @@ jobs:
registry: ${{ secrets.DOCKER_REGISTRY }}
username: ${{ secrets.DOCKER_REGISTRY_USER }}
password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
scope: ${{secrets.DOCKER_REGISTRY}}/${{env.IMAGE}}@push
logout: true
- name: Build and push Docker image
uses: docker/build-push-action@v4
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: true
build-args: |
VERSION=${{env.TAG}}
version=${{env.TAG}}
tags: |
${{secrets.DOCKER_REGISTRY}}/${{env.IMAGE}}:${{env.TAG}}
${{secrets.DOCKER_REGISTRY}}/${{env.IMAGE}}:latest

View File

@@ -6,6 +6,7 @@ COPY ./app /code/app
COPY ./log_config.yml /code/log_config.yml
ARG version
ENV VERSION=${version}
RUN echo "Build Version: $VERSION"
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget --spider --quiet --tries=1 --timeout=5 --server-response http://127.0.0.1:8000/health 2>&1 | grep "200 OK" > /dev/null
CMD ["uvicorn", "main:app", "--app-dir", "app", "--log-config", "log_config.yml", "--host","0.0.0.0", "--port", "8000"]

111
README.md Normal file
View File

@@ -0,0 +1,111 @@
# Mastobot — Mastodon Feed Automation Service
Automatically syncs RSS feeds from Finnish and international sources to your custom Mastodon instance.
## Features
- **7 RSS feed rotors** - Pull updates from news, weather, and community sources
- **Custom content formatting** - Each feed has tailored message formatting (e.g., hashtags for Finnish feeds)
- **Intelligent tagging** - Uses OpenAI API to generate or translate hashtags for Yle.fi English news
- **Static embed page** - Generate personalized Mastodon profile pages via S3 upload
- **Health monitoring** - Built-in health check endpoint
## Available Feed Rotors
| Router | Source | Language | Features |
|--------|--------|----------|----------|
| `yle_fi` | Yle.fi News | fi | Includes hashtags, custom cleaning |
| `yle_en` | Yle.fi News | en | AI-generated translated tags |
| `the_local` | The Local | en | Includes hashtags |
| `taloustaito` | Taloustaito | fi | First 3 articles only |
| `sur` | Sur Weather News | en | Includes hashtags |
| `hn` | Hacker News | en | Title + link only |
| `embed` | Your Mastodon | any | Static profile page generator |
## Tech Stack
- **Backend**: FastAPI (Python)
- **Database**: None (state stored in memory per request)
- **Deployment**: Docker Compose with development hot-reload
- **AI Integration**: OpenAI API for tag translation/generation
- **Storage**: Amazon S3 for embed pages
- **External APIs**: RSS feedparser, Mastodon v1 API, BeautifulSoup
## Setup
### Prerequisites
- Docker (compose)
- Custom Mastodon instance with API access
- OpenAI API key (for `yle_en` rotor only)
- AWS credentials (for `embed` rotor S3 upload)
### Development
```bash
# Load environment variables
# Copy .env.example to .env and fill in:
# - mastodon_server: https://your.mastodon.local
# - feeds__: {source_name: {url, account_id, token}}
# - openai_api_key: sk-your-api-key
# - aws_access_key_id / aws_secret_access_key (optional)
# Run development server with hot-reload
uvicorn app.main:app --host 0.0.0.0 --port 8000
# Or use Docker Compose
docker compose up --build
```
### Configuration Example
```yaml
feeds__ :
fuengirola__ :
url: https://fuengirola.fi/fi/rss/
account_id: 12345678
token: your_mastodon_token
hn__ :
url: https://hacker-news.ycombinator.com/hlnews.xml
account_id: 12345678
token: your_mastodon_token
```
## Usage
### Health Check
```bash
curl http://localhost:8000/health
```
Returns: `{"status": "healthy"}`
### Trigger Manual Feed Update
Call specific rotors via HTTP:
```bash
curl http://localhost:8000/rss?source=fuengirola
```
See individual router docs for endpoints.
## API Endpoints
| Endpoint | Description |
|----------|-------------|
| `/` | Welcome message |
| `/health` | Health check |
| `/embed/generate` | Generate static embed page at S3 key |
| `/rss?fource=fuengirola` | Fuengirola weather news |
| `/rss?source=yle_fi` | Yle.fi Finnish news |
| `/rss?source=yle_en` | Yle.fi English news (AI tags) |
| `/rss?source=the_local` | The Local articles |
| `/rss?source=taloustaito` | Taloustaito economics |
| `/rss?source=sur` | Weather news with hashtags |
| `/rss?source=hn` | Hacker News |
## License
Proprietary - All Rights Reserved for John Ahlroos

View File

@@ -18,12 +18,17 @@ logger = logging.getLogger(__name__)
@router.get("/generate", summary="Embeddable Mastodon Feed")
async def generate_static_page(settings: Annotated[Settings, Depends(get_settings)]):
mastodon_token = settings.feeds['embed']['token']
mastodon_server = settings.mastodon_server
mastodon_aid = settings.feeds['embed']['account_id']
mastodon_token = str(settings.feeds['embed']['token'])
mastodon_get_statuses_url=f'{mastodon_server}/api/v1/accounts/{mastodon_aid}/statuses'
s3_bucket = settings.feeds['embed']['s3_bucket']
s3_filename = settings.feeds['embed']['s3_key']
mastodon_get_statuses_url=settings.feeds['embed']['url']
try:
latest_statuses = load_latest_statuses(mastodon_get_statuses_url, mastodon_token,20)
latest_statuses = load_latest_statuses(mastodon_get_statuses_url, mastodon_token, 20)
latest_statuses = [status for status in latest_statuses if status['in_reply_to_id'] == None]
latest_statuses = [status for status in latest_statuses if status['in_reply_to_account_id'] == None]
latest_statuses = [status for status in latest_statuses if status['reblog'] == None]