#!/usr/bin/env python3 import json import urllib.request from base64 import b64decode from subprocess import run import cherrypy from icalendar import Calendar, Todo GITEA_REPO_URL="https://git.bitmessage.org/api/v1" combined = {} def get_combined(token): for q in ("assigned", "review_requested"): req = urllib.request.Request(GITEA_REPO_URL + f"/repos/issues/search?state=open&{q}=true") req.add_header("Accept", "application/json") req.add_header("Content-Type", "application/json") req.add_header("Authorization", "token " + token) with urllib.request.urlopen(req) \ as response: issues = json.load(response) for issue in issues: _id = issue['id'] if _id in combined: combined[_id]['categories'].append(q) else: combined[_id] = issue combined[_id]['categories'] = [q] return combined def process_combined(combined): cal = Calendar() for _id, issue in combined.items(): todo = Todo() todo['uid'] = _id todo['dtstamp'] = issue['created_at'] if issue['due_date']: todo['due'] = issue['due_date'] todo['summary'] = issue['title'] todo['description'] = issue['body'] todo['categories'] = issue['categories'] cal.add_component(todo) return cal.to_ical() def get_token(input_token): token = input_token.removeprefix("Basic ") token = b64decode(token).decode('utf8', 'ignore') with cherrypy.HTTPError.handle(ValueError, 401): _, token = token.split(":", 2) return token class Root: @cherrypy.expose def todo(self): cherrypy.response.headers['WWW-Authenticate'] = \ 'Basic realm="ICS access"' cherrypy.response.headers['Content-Type'] = \ 'text/calendar' cherrypy.response.headers['Content-Disposition'] = \ 'attachment; filename="Gitea TODO.ics"' authorization = cherrypy.request.headers.get('Authorization', ':') if not authorization: raise cherrypy.HTTPError(401, 'Unauthorized') token = get_token(authorization) if not token: raise cherrypy.HTTPError(401, 'Unauthorized') combined = get_combined(token) if not combined: raise cherrypy.HTTPError(401, 'Unauthorized') return(process_combined(combined)) if __name__ == '__main__': cherrypy.config.update({'server.socket_host': '0.0.0.0', 'server.socket_port': 8080, }) cherrypy.quickstart(Root(), '/')