Skip to content

iCal proxy with support for user-defined filtering rules

License

Notifications You must be signed in to change notification settings

yungwood/ical-filter-proxy

Repository files navigation


Logo

iCal Filter Proxy

iCal proxy with support for user-defined filtering rules

What's this thing?

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.

Features

  • 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

Built With

Setup

Docker

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.

Kubernetes

You can deploy iCal Filter Proxy using the included helm chart from charts/ical-filter-proxy.

Build from source

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 the ical-filter-proxy binary
go build .

# build container image
docker build \
  --build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
  --build-arg REVISION=$(git rev-parse HEAD) \
  --build-arg VERSION=$(git rev-parse --short HEAD) \
  -t ical-filter-proxy:latest .

Configuration

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" # token used to pull iCal feed - authentication is disabled when blank
    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
    publish_name: "My Outlook Calendar" # the published name of the calendar - uses upstream value if this line is skipped
    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

unsafe: false # optional - must be enabled if any calendars do not have a token

Filters

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 is true the event is discarded
    • transform rules are applied to the event
    • if stop is true no more filters are processed
  • If no match is found the event is retained by default

Match conditions

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 - if true, property must be absent or empty
  • contains - property must contain this value
  • prefix - property must start with this value
  • suffix - property must end with this value
  • regex - property must match the given regular expression (an invalid regex will result in no matches)

Transformations

Transformations can be applied to the following event properties:

  • summary - string value
  • location - string value
  • description - string value
  • url - string value

The following transformations are available for strings:

  • replace - the property is replace with this value
  • remove - if true the property is set to a blank string

Roadmap to 1.0

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
  • Support for ical_url_file and token_file in config (vault secrets)
  • Prometheus metrics
  • Testing

Contributing

If you have a suggestion that would make this better, please feel free to open an issue or send a pull request.

License

This project is licensed under the MIT License. See the LICENSE file for details.

Acknowledgments

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.