{"id":22488491,"url":"https://github.com/aserper/masto-rss","last_synced_at":"2026-04-04T15:33:35.463Z","repository":{"id":210809692,"uuid":"727502806","full_name":"aserper/masto-rss","owner":"aserper","description":"A Mastodon bot that posts RSS updates to a Mastodon account","archived":false,"fork":false,"pushed_at":"2026-04-03T01:19:16.000Z","size":370,"stargazers_count":33,"open_issues_count":0,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-04-03T11:22:48.849Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aserper.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-12-05T01:50:09.000Z","updated_at":"2026-04-03T01:19:19.000Z","dependencies_parsed_at":null,"dependency_job_id":"9b3a5943-e595-4ed3-87b1-022f9f39985a","html_url":"https://github.com/aserper/masto-rss","commit_stats":null,"previous_names":["aserper/masto-rss"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/aserper/masto-rss","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aserper%2Fmasto-rss","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aserper%2Fmasto-rss/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aserper%2Fmasto-rss/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aserper%2Fmasto-rss/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aserper","download_url":"https://codeload.github.com/aserper/masto-rss/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aserper%2Fmasto-rss/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31403959,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-06T17:17:49.132Z","updated_at":"2026-04-04T15:33:35.422Z","avatar_url":"https://github.com/aserper.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"![Masto-RSS Header](header.jpg)\n\n# Masto-RSS\n\n[![CI/CD](https://img.shields.io/github/actions/workflow/status/aserper/masto-rss/ci.yml?style=for-the-badge\u0026logo=github\u0026label=CI/CD)](https://github.com/aserper/masto-rss/actions/workflows/ci.yml)\n[![Docker Hub](https://img.shields.io/badge/docker%20hub-amitserper%2Fmasto--rss-blue?style=for-the-badge\u0026logo=docker\u0026logoColor=white)](https://hub.docker.com/r/amitserper/masto-rss)\n[![GHCR](https://img.shields.io/badge/ghcr.io-masto--rss-blue?style=for-the-badge\u0026logo=docker\u0026logoColor=white)](https://github.com/aserper/masto-rss/pkgs/container/masto-rss)\n[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-yellow.svg?style=for-the-badge)](LICENSE)\n[![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue.svg?style=for-the-badge\u0026logo=python\u0026logoColor=white)](https://www.python.org/downloads/)\n[![GitHub stars](https://img.shields.io/github/stars/aserper/masto-rss.svg?style=social)](https://github.com/aserper/masto-rss)\n[![GitHub forks](https://img.shields.io/github/forks/aserper/masto-rss.svg?style=social\u0026label=Fork)](https://github.com/aserper/masto-rss/network)\n\nA simple, lightweight Mastodon bot that automatically posts updates from RSS feeds to the Fediverse. Built with Python and designed to run seamlessly in Docker with multiarch support (amd64 \u0026 arm64).\n\n## Features\n\n- Automatically monitors RSS/Atom feeds and posts new items to Mastodon\n- Persistent state tracking to avoid duplicate posts\n- Configurable post visibility (public, unlisted, private, direct)\n- Lightweight Python Slim-based Docker image\n- Multiarch support (amd64 \u0026 arm64) for broad compatibility\n- Continuous monitoring with configurable check intervals\n\n## Quick Start\n\n### Using Docker (Recommended)\n\nThe easiest way to run Masto-RSS is using the pre-built multiarch Docker images available on both Docker Hub and GitHub Container Registry.\n\n#### Pull from Docker Hub\n\n```bash\ndocker pull amitserper/masto-rss:latest\n```\n\n#### Pull from GitHub Container Registry\n\n```bash\ndocker pull ghcr.io/aserper/masto-rss:latest\n```\n\n#### Run the Bot\n\n```bash\ndocker run -d \\\n  --name masto-rss-bot \\\n  -e MASTODON_CLIENT_ID=\"your_client_id\" \\\n  -e MASTODON_CLIENT_SECRET=\"your_client_secret\" \\\n  -e MASTODON_ACCESS_TOKEN=\"your_access_token\" \\\n  -e MASTODON_INSTANCE_URL=\"https://mastodon.social\" \\\n  -e RSS_FEED_URL=\"https://example.com/feed.xml\" \\\n  -e TOOT_VISIBILITY=\"public\" \\\n  -e CHECK_INTERVAL=\"300\" \\\n  -v /path/to/state:/state \\\n  amitserper/masto-rss:latest\n```\n\n\u003e **Important:** Use a bind mount for `/state` to persist the list of processed feed items across container restarts.\n\n### Using Docker Compose\n\nCreate a `docker-compose.yml`:\n\n```yaml\nversion: '3.8'\n\nservices:\n  masto-rss:\n    image: amitserper/masto-rss:latest\n    # Or use GHCR: ghcr.io/aserper/masto-rss:latest\n    container_name: masto-rss-bot\n    restart: unless-stopped\n    environment:\n      MASTODON_CLIENT_ID: \"your_client_id\"\n      MASTODON_CLIENT_SECRET: \"your_client_secret\"\n      MASTODON_ACCESS_TOKEN: \"your_access_token\"\n      MASTODON_INSTANCE_URL: \"https://mastodon.social\"\n      RSS_FEED_URL: \"https://example.com/feed.xml\"\n      TOOT_VISIBILITY: \"public\"\n      CHECK_INTERVAL: \"300\"\n    volumes:\n      - ./state:/state\n```\n\nThen run:\n\n```bash\ndocker-compose up -d\n```\n\n\n### Multiple Feeds\nTo monitor multiple feeds, you can either:\n- Use the `RSS_FEEDS` environment variable (comma-separated list)\n- Use the `FEEDS_FILE` environment variable (path to file with one URL per line)\n\n#### Run with Multiple Feeds (Docker)\n\n```bash\ndocker run -d \\\n  --name masto-rss-bot \\\n  -e MASTODON_CLIENT_ID=\"your_client_id\" \\\n  -e MASTODON_CLIENT_SECRET=\"your_client_secret\" \\\n  -e MASTODON_ACCESS_TOKEN=\"your_access_token\" \\\n  -e MASTODON_INSTANCE_URL=\"https://mastodon.social\" \\\n  -e RSS_FEEDS=\"https://feed1.com/rss,https://feed2.com/rss\" \\\n  -e TOOT_VISIBILITY=\"public\" \\\n  -e CHECK_INTERVAL=\"300\" \\\n  -v /path/to/state:/state \\\n  amitserper/masto-rss:latest\n```\n\n## Configuration\n\nAll configuration is done via environment variables:\n\n| Variable | Description | Required | Example |\n|----------|-------------|----------|---------|\n| `MASTODON_CLIENT_ID` | Mastodon application client ID | Yes | `abc123...` |\n| `MASTODON_CLIENT_SECRET` | Mastodon application client secret | Yes | `xyz789...` |\n| `MASTODON_ACCESS_TOKEN` | Mastodon access token | Yes | `token123...` |\n| `MASTODON_INSTANCE_URL` | URL of your Mastodon instance | Yes | `https://mastodon.social` |\n| `RSS_FEED_URL` | Single RSS/Atom feed URL (Legacy) | No* | `https://example.com/feed.xml` |\n| `RSS_FEEDS` | Comma-separated list of feed URLs | No* | `https://site1.com,https://site2.com` |\n| `FEEDS_FILE` | Path to file containing list of feed URLs | No* | `/config/feeds.txt` |\n| `TOOT_VISIBILITY` | Post visibility level | Yes | `public`, `unlisted`, `private`, or `direct` |\n| `CHECK_INTERVAL` | Seconds between feed checks | Yes | `300` (5 minutes) |\n| `ENABLE_REPLIES` | Enable sarcastic replies to mentions | No | `true` or `false` (default: false) |\n| `PROCESSED_ENTRIES_FILE`| Custom path for state file | No | `/state/processed.txt` |\n\n\\* At least one of `RSS_FEED_URL`, `RSS_FEEDS`, or `FEEDS_FILE` must be provided.\n\n### Getting Mastodon API Credentials\n\n1. Log into your Mastodon instance\n2. Go to **Settings** → **Development** → **New Application**\n3. Give it a name (e.g., \"RSS Bot\")\n4. Set scopes to `write:statuses`\n5. Save and copy the client ID, client secret, and access token\n\n## Building from Source\n\n### Build Locally\n\n```bash\ngit clone https://github.com/aserper/masto-rss.git\ncd masto-rss\ndocker build -t masto-rss .\n```\n\n### Build Multiarch Images\n\n```bash\n# Set up buildx\ndocker buildx create --use\n\n# Build for both architectures\ndocker buildx build \\\n  --platform linux/amd64,linux/arm64 \\\n  -t yourusername/masto-rss:latest \\\n  --push \\\n  .\n```\n\n## Running Without Docker\n\nIf you prefer to run the bot directly with Python:\n\n```bash\n# Clone the repository\ngit clone https://github.com/aserper/masto-rss.git\ncd masto-rss\n\n# Install dependencies\npip install -r requirements.txt\n\n# Set environment variables\nexport MASTODON_CLIENT_ID=\"your_client_id\"\nexport MASTODON_CLIENT_SECRET=\"your_client_secret\"\nexport MASTODON_ACCESS_TOKEN=\"your_access_token\"\nexport MASTODON_INSTANCE_URL=\"https://mastodon.social\"\nexport RSS_FEED_URL=\"https://example.com/feed.xml\"\nexport TOOT_VISIBILITY=\"public\"\nexport CHECK_INTERVAL=\"300\"\n\n# Run the bot\npython main.py\n```\n\n\u003e **Note:** When running without Docker, the bot stores its state in `/state/processed_entries.txt`. Make sure this directory exists or modify [main.py](main.py#L15) to use a different path.\n\n## How It Works\n\n1. The bot fetches the RSS feed at regular intervals (defined by `CHECK_INTERVAL`)\n2. For each feed item, it checks if the item's URL has been processed before\n3. If the item is new, it posts to Mastodon with the format: `{title}\\n\\n{link}`\n4. The item URL is saved to prevent duplicate posts\n5. The process repeats indefinitely\n\n## Architecture\n\n- **Base Image:** Python 3.12-slim (stable \u0026 compatible)\n- **Python Version:** 3.10+\n- **Platforms:** linux/amd64, linux/arm64\n- **Dependencies:** feedparser, mastodon.py (see [requirements.txt](requirements.txt))\n\n## State Persistence\n\nThe bot maintains state in `/state/processed_entries.txt` to track which feed items have already been posted. This prevents duplicate posts across restarts.\n\n**Important:** Always mount `/state` as a volume to preserve this state file.\n\n## Contributing\n\nContributions are welcome! Feel free to:\n\n- Report bugs by opening an issue\n- Submit pull requests for improvements\n- Suggest new features or enhancements\n\n## License\n\nThis project is licensed under the GNU General Public License v3.0 - see the [LICENSE](LICENSE) file for details.\n\n## Support\n\nIf you find this project useful, please consider giving it a star on GitHub!\n\n## Links\n\n- [Docker Hub Repository](https://hub.docker.com/r/amitserper/masto-rss)\n- [GitHub Container Registry](https://github.com/aserper/masto-rss/pkgs/container/masto-rss)\n- [Source Code](https://github.com/aserper/masto-rss)\n- [Issues](https://github.com/aserper/masto-rss/issues)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faserper%2Fmasto-rss","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faserper%2Fmasto-rss","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faserper%2Fmasto-rss/lists"}