{"id":27486097,"url":"https://github.com/jordanlambrecht/calendarr","last_synced_at":"2025-09-05T08:23:34.687Z","repository":{"id":286083919,"uuid":"960295968","full_name":"jordanlambrecht/calendarr","owner":"jordanlambrecht","description":"A docker container that will publish Sonarr and Radarr's release calendar to Discord + Slack on a weekly/daily basis","archived":false,"fork":false,"pushed_at":"2025-08-18T05:18:45.000Z","size":1890,"stargazers_count":71,"open_issues_count":8,"forks_count":5,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-31T02:50:57.852Z","etag":null,"topics":["discord","discord-bot","docker-compose","ical","plex","radarr","slack-bot","sonarr"],"latest_commit_sha":null,"homepage":"","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/jordanlambrecht.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":"jordanlambrecht","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"buy_me_a_coffee":"jordyjordy","thanks_dev":null,"custom":null}},"created_at":"2025-04-04T07:29:39.000Z","updated_at":"2025-08-27T18:07:31.000Z","dependencies_parsed_at":"2025-04-04T09:24:01.726Z","dependency_job_id":"c1b84305-2795-44fd-87d9-abfafa21092f","html_url":"https://github.com/jordanlambrecht/calendarr","commit_stats":null,"previous_names":["jordanlambrecht/arr-calendar-to-discord"],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/jordanlambrecht/calendarr","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jordanlambrecht%2Fcalendarr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jordanlambrecht%2Fcalendarr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jordanlambrecht%2Fcalendarr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jordanlambrecht%2Fcalendarr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jordanlambrecht","download_url":"https://codeload.github.com/jordanlambrecht/calendarr/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jordanlambrecht%2Fcalendarr/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273729330,"owners_count":25157409,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-05T02:00:09.113Z","response_time":402,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["discord","discord-bot","docker-compose","ical","plex","radarr","slack-bot","sonarr"],"created_at":"2025-04-16T18:01:20.870Z","updated_at":"2025-09-05T08:23:29.460Z","avatar_url":"https://github.com/jordanlambrecht.png","language":"Python","funding_links":["https://github.com/sponsors/jordanlambrecht","https://buymeacoffee.com/jordyjordy"],"categories":["Python","Complimenting Apps"],"sub_categories":[],"readme":"![Docker Pulls](https://img.shields.io/docker/pulls/jordyjordyjordy/calendarr)\n![GitHub Release](https://img.shields.io/github/v/release/jordanlambrecht/calendarr)\n![GitHub last commit](https://img.shields.io/github/last-commit/jordanlambrecht/calendarr)\n\n# 📆 Calendarr\n\nA simple Docker container that fetches upcoming airings/releases for TV shows and movies from Sonarr and Radarr calendars and posts them to Discord on a schedule.\n\n![Example Discord post](https://github.com/jordanlambrecht/calendarr/blob/main/public/calendarr_example_output_v2.png)\n\n## ✨ Features\n\n- Combines multiple Sonarr and Radarr calendar feeds\n- Groups shows and movies by day of the week\n- Runs on a customizable schedule (daily or weekly)\n- Supports both Discord and Slack notifications\n- Highly customizable configuration \n\n## 🚀 Usage\n\nImages available via either `ghcr.io/jordanlambrecht/calendarr:latest` or `jordyjordyjordy/calendarr:latest`\n\n### With Docker Compose (Recommended)\n\n1. In Discord, right click channel you want to add the script to -\u003e Edit Channel -\u003e Integrations, Webhooks -\u003e New Webhook -\u003e Copy Webhook URL\n\n2. Create a `.env` file with your configuration or remove '.example' from `.env.example`:\n\n```env\nDISCORD_WEBHOOK_URL=your_discord_webhook_url\nSLACK_WEBHOOK_URL=your_discord_webhook_url\nICS_URL_SONARR_1=your_sonarr_calendar_url\nICS_URL_SONARR_2=your_anime_sonarr_calendar_url\nICS_URL_RADARR_1=your_radarr_calendar_url\n\n...and so on and so on and turtles all the way down\n```\n### With Docker Run (If you like pain)\n\n```bash\ndocker run -d \\\n  --name calandarr \\\n  -e DISCORD_WEBHOOK_URL=\"https://discord.com/api/webhooks/your_webhook\" \\\n  -e CALENDAR_URLS='[{\"url\":\"https://sonarr.example.com/feed/calendar/api.ics\",\"type\":\"tv\"},{\"url\":\"https://radarr.example.com/feed/calendar/api.ics\",\"type\":\"movie\"}]' \\\n  -e CUSTOM_HEADER=\"My Media Guide\" \\\n  -e SHOW_DATE_RANGE=\"true\" \\\n  -e START_WEEK_ON_MONDAY=\"true\" \\\n  -e RUN_ON_STARTUP=\"true\" \\\n  jordyjordyjordy/calendarr:latest\n```\n\n\n### To Run Offschedule \n\n1. Start the container via the compose file with `docker compose up -d`\n2. Use the command `docker exec calandarr python /app/main.py` as willy nilly as you wish\n\n\n## 🛠️ Configuration\n\n| Variable                              | Type    | Default         | Description                                                                                             |\n| :------------------------------------ | :------ | :-------------- | :------------------------------------------------------------------------------------------------------ |\n| `ADD_LEADING_ZERO`                    | Boolean | `true`          | Add leading zero to single-digit hours (Optional)                                                       |\n| `CALENDAR_RANGE`                      | String  | `AUTO`          | Date range to fetch: `DAY`, `WEEK`, `AUTO` (Optional)                                                   |\n| `CALENDAR_URLS` *                     | String  | `[]`            | JSON array of calendar URLs and types (e.g., `[{\"url\":\"http://...\",\"type\":\"tv\"}]`)                       |\n| `CRON_SCHEDULE`                       | String  | `None`          | Custom CRON expression (Overrides `SCHEDULE_TYPE`, `SCHEDULE_DAY`, `RUN_TIME`) (Optional)               |\n| `CUSTOM_HEADER`                       | String  | `New Releases`  | Custom header text (Optional)                                                                           |\n| `DEBUG`                               | Boolean | `false`         | Enable debug logging (Optional)                                                                         |\n| `DEDUPLICATE_EVENTS`                  | Boolean | `true`          | Remove duplicate events from multiple sources (Optional)                                                |\n| `DISCORD_HIDE_MENTION_INSTRUCTIONS` | Boolean | `false`         | *Discord only* Hide the instruction text below the role mention (Optional)                              |\n| `DISCORD_MENTION_ROLE_ID`             | String  | `\"\"`            | *Discord only* Role ID to mention (Format: `123456789012345678`. Numbers only.) (Optional)             |\n| `DISCORD_WEBHOOK_URL` **              | String  | `\"\"`            | Discord webhook URL                                                                                     |\n| `DISPLAY_TIME`                        | Boolean | `true`          | Display the release time next to events (Optional)                                                      |\n| `ENABLE_CUSTOM_DISCORD_FOOTER`        | Boolean | `false`         | Enable custom footer for Discord messages (Optional)                                                    |\n| `ENABLE_CUSTOM_SLACK_FOOTER`          | Boolean | `false`         | Enable custom footer for Slack messages (Optional)                                                      |\n| `HTTP_TIMEOUT`                        | Integer | `30`            | Timeout in seconds for HTTP requests (Optional)                                                         |\n| `LOG_BACKUP_COUNT`                    | Integer | `15`            | Number of rotated log files to keep (Optional)                                                          |\n| `LOG_DIR`                             | String  | `/app/logs`     | Directory to store log files (Optional)                                                                 |\n| `LOG_FILE`                            | String  | `calendarr.log` | Name of the log file (Optional)                                                                         |\n| `LOG_MAX_SIZE_MB`                     | Integer | `1`             | Maximum size of a single log file in MB before rotation (Optional)                                      |\n| `PASSED_EVENT_HANDLING`               | String  | `DISPLAY`       | How to handle past events: `DISPLAY`, `HIDE`, `STRIKE` (Optional)                                       |\n| `RUN_ON_STARTUP`                      | Boolean | `false`         | Run the job immediately when the container starts (Optional)                                            |\n| `RUN_TIME`                            | String  | `09:00`         | Time to run job (HH:MM) (Optional)                                                                      |\n| `SCHEDULE_DAY`                        | String  | `1`             | Day of week for weekly schedule (`0`-`6`, Sunday-Saturday) (Optional. Default: Monday)                  |\n| `SCHEDULE_TYPE`                       | String  | `WEEKLY`        | `DAILY` or `WEEKLY` (Optional)                                                                          |\n| `SHOW_DATE_RANGE`                     | Boolean | `true`          | Show the date range in the header (Optional)                                                            |\n| `SHOW_TIMEZONE_IN_SUBHEADER`          | Boolean | `false`         | Show the configured timezone (Optional)                                                                 |\n| `SLACK_WEBHOOK_URL` ***             | String  | `\"\"`            | Slack webhook URL                                                                                       |\n| `START_WEEK_ON_MONDAY`                | Boolean | `true`          | Use Monday as the start of the week for color rotation (Optional)                                       |\n| `TZ` *                                | String  | `UTC`           | Timezone (e.g., `America/New_York`)                                                                     |\n| `USE_24_HOUR`                         | Boolean | `true`          | Use 24-hour time format (Optional)                                                                      |\n| `USE_DISCORD`                         | Boolean | `true`          | Enable Discord notifications (Optional)                                                                 |\n| `USE_SLACK`                           | Boolean | `false`         | Enable Slack notifications (Optional)                                                                   |\n\n\\* Required.\n** Required if `USE_DISCORD` is `true`.\n*** Required if `USE_SLACK` is `true`.\n\n## Schedule Configuration\n\nSet when and how often the calendar runs:\n\n- `RUN_TIME`: When to run each day (format: HH:MM in 24-hour time, e.g., \"09:30\")\n- `SCHEDULE_TYPE`: Either \"DAILY\" or \"WEEKLY\"\n- `CALENDAR_RANGE`: \"AUTO\", \"DAY\", or \"WEEK\" - controls how many days of events to show\n  - \"AUTO\": Uses a day's worth for daily schedules or a week for weekly schedules\n  - \"DAY\": Shows one day of events\n  - \"WEEK\": Shows an entire week of events\n\nYou can also use `CRON_SCHEDULE` for direct cron expressions (overrides all other schedule settings. Don't use this unless you have a good reason and know what you're doing)\n\n## ✍️ Custom Footers\n\nYou can add custom text to the end of your Discord and Slack announcements using Markdown files.\n\n1.  **Enable the Feature:** Set `ENABLE_CUSTOM_DISCORD_FOOTER: true` and/or `ENABLE_CUSTOM_SLACK_FOOTER: true` in your environment variables.\n2.  **Create a Volume Mount:** Add a volume mount in your `docker-compose.yml` to map a local directory (e.g., `./custom_footers`) to `/app/custom_footers` inside the container.\n\n    ```yaml\n    # docker-compose.yml example snippet\n    services:\n      calendarr:\n        # ... other stuff ...\n        volumes:\n          - ./logs:/app/logs:rw\n          - ./custom_footers:/app/custom_footers:rw # Add this line\n    ```\n3.  **Edit Footer Files:**\n    *   When you first start the container with the volume mount, Calendarr will automatically copy default template files (`discord_footer.md` and `slack_footer.md`) into your local `./custom_footers` directory (if they don't already exist).\n    *   Edit these files using standard Markdown (for Discord) or Slack's `mrkdwn` (for Slack) to customize your footer.\n\nIf the footer files are missing or cannot be read, the app will log a warning and omit the footer without failing.\n\n## 🤝 Obtaining Calendar URLs\n\n![Sonarr Calendar Options](https://github.com/jordanlambrecht/calendarr/blob/main/public/calendarr_sonarr_feed.png)\n\n### Sonarr\n\n1. Go to Calendar \u003e iCal Link\n2. Leave all three checkboxes blank\n3. Optionally set tags for shows you want to announce\n4. Copy the ical link\n\nAlternatively: \n\n1. Go to Settings \u003e General\n2. Under \"Security\" section, look for \"API Key\"\n3. Copy the API key\n4. Your calendar URL will be: `http://your-sonarr-url/feed/v3/calendar/Sonarr.ics?apikey=YOUR_API_KEY`\n\n### Radarr\n\n1. Go to Calendar \u003e iCal Link\n2. Leave all three checkboxes blank\n3. Optionally set tags for movies you want to announce\n4. Copy the ical link\n\nAlternatively: \n\n1. Go to Settings \u003e General\n2. Under \"Security\" section, look for \"API Key\"\n3. Copy the API key\n4. Your calendar URL will be: `http://your-radarr-url/feed/v3/calendar/Radarr.ics?apikey=YOUR_API_KEY`\n\n\n## Slack Webhooks Setup\n\nMore info [here](https://api.slack.com/messaging/webhooks) on how to obtain a slack webhook URL if you get lost.\n\nYou can set up the Slack app using the provided manifest file:\n\n1. Go to [https://api.slack.com/apps](https://api.slack.com/apps)\n2. Click \"Create New App\" and select \"From an app manifest\"\n3. Select your workspace and click \"Next\"\n4. Copy and paste the contents of the `slack-manifest.yaml` file from this repository\n5. Click \"Next\" and then \"Create\"\n6. Once created, navigate to \"Incoming Webhooks\" in the sidebar\n7. Toggle \"Activate Incoming Webhooks\" to On\n8. Click \"Add New Webhook to Workspace\"\n9. Select the channel where you want to receive updates\n10. Copy the Webhook URL provided and use it as your `SLACK_WEBHOOK_URL` environment variable\n\n## 🌟 First Timers\n\nIf you're new to Docker, it's fairly easy to get this going. I won't post an in-depth guide- there's [Plenty](https://docs.docker.com/compose/) on the internet. The general gist is:\n\n1. Install Docker Desktop for your platform (Windows, Mac, or Linux)\n2. Create a new folder for your Calendarr setup via Terminal: `mkdir calendarr \u0026\u0026 cd calendarr`\n3. Create these two files:\n  - A .env file with your configuration (see example above)\n  - A docker-compose.yml file with, at a minimum:\n\n  ```yaml\n  ---\n  name: calendarr\n  services:\n    calendarr:\n      image: ghcr.io/jordanlambrecht/calendarr:latest\n      restart: \"unless-stopped\"\n      container_name: calendarr\n      environment:\n        USE_DISCORD: \"true\"\n        DISCORD_WEBHOOK_URL: ${DISCORD_WEBHOOK_URL} # Reference the .env.example for more info\n        CALENDAR_URLS: \u003e\n          [{\n            \"url\":\"${ICS_URL_SONARR_1}\",\n            \"type\":\"tv\"\n          },\n          {\n            \"url\":\"${ICS_URL_RADARR_1}\",\n            \"type\":\"movie\"\n          }]\n        CUSTOM_HEADER: \"TV Guide - What's Up This Week\"\n        TZ: \"America/Chicago\"  # Change to your timezone\n        SCHEDULE_TYPE: \"WEEKLY\" # Or \"DAILY\"\n        RUN_TIME: \"09:00\"       # Time to run the job (HH:MM)\n      volumes:\n        - ./logs:/app/logs:rw\n  ```\n4. Open a terminal in that folder and run: `docker compose up -d`\n5. Check if it's working: `docker logs calendarr -f`\n\nThat's it! The container will immediately run once (if `RUN_ON_STARTUP` is `true`) and then according to the schedule you've set.\n\n\n## 🧑‍💻 Contributing\n\nThe two biggest things I need help with right now are:\n- Adding friendly timezone names to the `TIMEZONE_NAME_MAP` in the `constants.py` file\n- Translations. There is no localization structure implemented yet, but it would be great to get a head start in things like spanish, etc\n\n## 🛒 ToDo\nFeatures I'd like to maybe implement:\n\n- Localization\n- More platform integrations\n- Potentially a web ui\n\n\n## 🚧 Development\n\nIf you want to build the container yourself:\n\n```bash\ngit clone https://github.com/jordanlambrecht/calendarr.git\ncd calendarr\ndocker build -t calendarr .\n```\n\n## 🧑‍⚖️ License\n\nGNU GENERAL PUBLIC LICENSE\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjordanlambrecht%2Fcalendarr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjordanlambrecht%2Fcalendarr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjordanlambrecht%2Fcalendarr/lists"}