Do you have iCal feeds with a bunch of stuff you don't need? Do you want to modify events generated by your rostering system?
iCal Filter Proxy is a simple service for proxying multiple iCal feeds while applying a list of filters to remove or modify events to suit your use case.
- Proxy multiple calendars
- Define a list of filters per calendar
- Match events using basic text and regex conditions
- Remove or modify events as they are proxied
- Go
- golang-ical
- yaml.v3
- DALL-E 2 (app icon)
Docker images are published to Docker Hub. You'll need a config file (see below) mounted into the container at /app/config.yaml
.
For example:
docker run -d \
--name=ical-filter-proxy \
-v config.yaml:/app/config.yaml \
-p 8080:8080/tcp \
--restart unless-stopped \
yungwood/ical-filter-proxy:latest
You can also adapt the included docker-compose.yaml
example.
You can deploy iCal Filter Proxy using the helm chart from yungwood/helm-charts/ical-filter-proxy
.
helm repo add yungwood https://yungwood.github.io/helm-charts/
helm install --name your-release yungwood/ical-filter-proxy
You can also build the app and container from source.
# clone this repo
git clone [email protected]:yungwood/ical-filter-proxy.git
cd ical-filter-proxy
# build container image
docker build -t ical-filter-proxy:latest .
Calendars and filters are defined in a yaml config file. By default this is config.yaml
(use the -config
switch to change this). The configuration must define at least one calendar for ical-filter-proxy to start.
Example configuration (with comments):
calendars:
# basic example
- name: example # used as slug in URL - e.g. ical-filter-proxy:8080/calendars/example/feed?token=changeme
publish_name: "My Calendar" # the published name of the calendar - uses upstream value if this line is skipped
token: "changeme" # optional - token must be used to pull iCal feed if defined
public: false # optional - must be true if token is blank or not defined
feed_url: "https://my-upstream-calendar.url/feed.ics" # URL for the upstream iCal feed
filters: # optional - if no filters defined the upstream calendar is proxied as parsed
- description: "Remove an event based on a regex"
remove: true # events matching this filter will be removed
match: # optional - all events will match if no rules defined
summary: # match on event summary (title)
contains: "deleteme" # must contain 'deleteme'
- description: "Remove descriptions from all events"
transform: # optional
description: # modify event description
remove: true # replace with a blank string
# example: removing noise from an Office 365 calendar
- name: outlook
token: "changeme"
feed_url: "https://outlook.office365.com/owa/calendar/.../reachcalendar.ics"
filters:
- description: "Remove canceled events" # canceled events remain with a 'Canceled:' prefix until removed
remove: true
match:
summary:
prefix: "Canceled: "
- description: "Remove events without descriptions"
remove: true
match:
description:
empty: true
- description: "Remove public holidays"
remove: true
match:
summary:
regex: ".*[Pp]ublic [Hh]oliday.*"
# example: cleaning up an OpsGenie feed
- name: opsgenie
token: "changeme"
feed_url: "https://company.app.opsgenie.com/webapi/webcal/getRecentSchedule?webcalToken=..."
filters:
- description: "Keep oncall schedule events and fix names"
match:
summary:
contains: "schedule: oncall"
stop: true # stops processing any more filters
transform:
summary:
replace: "On-Call" # replace the event summary (title)
- description: "Remove all other events"
remove: true
Calendar events are filtered using a similar concept to email filtering. A list of filters is defined for each calendar in the config.
Each event parsed from feed_url
is evaluated against the filters in sequence.
- All
match
rules for a filter must be true to match an event - A filter with no
match
rules will always match - When a match is found:
- if
remove
istrue
the event is discarded transform
rules are applied to the event- if
stop
istrue
no more filters are processed
- if
- If no match is found the event is retained by default
Each filter can specify match conditions against the following event properties:
summary
(string value)location
(string value)description
(string value)url
(string value)
These match conditions are available for a string value:
empty
- iftrue
, property must be absent or emptycontains
- property must contain this valueprefix
- property must start with this valuesuffix
- property must end with this valueregex
- property must match the given regular expression (an invalid regex will result in no matches)
Transformations can be applied to the following event properties:
summary
- string valuelocation
- string valuedescription
- string valueurl
- string value
The following transformations are available for strings:
replace
- the property is replace with this valueremove
- iftrue
the property is set to a blank string
You can load feed_url
and token
values from files by specifying the feed_url_file
and token_file
fields in the calendar configuration. When these fields are set, any values directly provided for feed_url
or token
are ignored.
For example:
calendars:
- name: example
token_file: "/run/secrets/outlook-token"
feed_url_file: "/run/secrets/outlook-feed"
There are a few more features I would like to add before I call the project "stable" and release version 1.0.
- Time based event conditions
- Caching
- Prometheus metrics
- Testing
If you have a suggestion that would make this better, please feel free to open an issue or send a pull request.
This project is licensed under the MIT License. See the LICENSE file for details.
This project was inspired by darkphnx/ical-filter-proxy. I needed more flexibility with filtering rules and the ability to modify event descriptions... plus I wanted an excuse to finally write something in Go.