diff --git a/pica-wekan/README.md b/pica-wekan/README.md index 5d204200d615601b0af9997a2ed802b312a5d865..fd37d9e06562fb534813038be0c81e13988b9bf4 100644 --- a/pica-wekan/README.md +++ b/pica-wekan/README.md @@ -18,3 +18,23 @@ Il suffit d'un `docker-compose up -d`. Il suffit de mettre à jour les tags des images dans le [docker-compose.yml](./docker-compose.yml), et de vérifier au niveau de la page des [Releases](https://github.com/wekan/wekan/releases) s'il y a eu des modifications. On pourra aussi consulter le Compose du dépôt officiel pour effectuer les nouvelles configurations. + +### Configurer des hooks + +Wekan a une fonctionnalité pour [déclencher un *hook*](https://github.com/wekan/wekan/pull/1119) lors de l'activité sur un board. +Grâce aux hooks, on peut configurer Mattermost pour recevoir un message sur un canal donné dès qu'une activité est détectée. + +Le souci est que Wekan [déclenche les hooks sortants pour chaque activité](https://github.com/wekan/wekan/wiki/Webhook-data), même les plus insignifiantes, ce qui spamme le canal. + +L'idée est donc de passer par un intermédiaire pour filter les hooks selon le type d'activité, et ne transmettre que les activités intéressantes. C'est ce que fait le petit outil [filter-hooks](./filter-hooks). + +Lorsque l'on configure un hook entrant dans Mattermost, on obtient un lien de la forme `https://team.picasoft.net/hooks/XXX`, qui permet de poster le message du hook sur un canal donné. Plutôt que d'entrer directement cette URL dans Wekan, on utilisera une URL de la forme : +``` +https://kanban.picasoft.net/forward_hooks?url=https://team.picasoft.net/hooks/XXX +``` + +`filter-hooks` répond lorsqu'une requête arrive sur `/foward_hooks`, et transmet le hook à `url` s'il est pertinent. + +Les hooks "pertinents" sont définis directement [dans le code](./filter-hooks/main.py). + +Le code est très basique voire bancal et pourra être amélioré. diff --git a/pica-wekan/docker-compose.yml b/pica-wekan/docker-compose.yml index 7adc41045fea39ed8a692de1956a84cbf8e92230..753ae94f1e25f5d9fb22f179d9a83bd516257a5c 100644 --- a/pica-wekan/docker-compose.yml +++ b/pica-wekan/docker-compose.yml @@ -28,9 +28,6 @@ services: environment: - MONGO_URL=mongodb://wekan-db:27017/wekan - ROOT_URL=https://kanban.picasoft.net - # Don't send webhook on card click - - CARD_OPENED_WEBHOOK_ENABLED=false - - WEBHOOKS_ATTRIBUTES=cardId,listId,user # ==== WEKAN API AND EXPORT BOARD ==== # Wekan Export Board works when WITH_API=true. # https://github.com/wekan/wekan/wiki/REST-API @@ -39,9 +36,6 @@ services: - ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURES_BEFORE=10 - ACCOUNTS_LOCKOUT_KNOWN_USERS_PERIOD=60 - ACCOUNTS_LOCKOUT_KNOWN_USERS_FAILURE_WINDOW=15 - # Mail sender - - MAIL_FROM=Pikan <wekan@picasoft.net> - env_file: ./secrets/wekan.secrets depends_on: - wekan-db labels: @@ -52,3 +46,17 @@ services: - docker_default - wekan restart: unless-stopped + + wekan-filter-hooks: + image: registry.picasoft.net/wekan-filter-hooks:v0.1 + build: ./filter-hooks + container_name: wekan-filter-hooks + environment: + - WEKAN_URL=https://kanban.picasoft.net + networks: + - docker_default + labels: + traefik.enable: true + traefik.port: 5000 + traefik.frontend.rule: Host:kanban.picasoft.net;Path:/forward_hooks + restart: unless-stopped diff --git a/pica-wekan/filter-hooks/CHANGELOG.md b/pica-wekan/filter-hooks/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..9a4bf03eb45e8467005204e092d74fa3fd60d0c8 --- /dev/null +++ b/pica-wekan/filter-hooks/CHANGELOG.md @@ -0,0 +1,3 @@ +## v0.1 + +Initial release. diff --git a/pica-wekan/filter-hooks/Dockerfile b/pica-wekan/filter-hooks/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..b044765d380ae2fbff9d3521a33eb3c85d04f23f --- /dev/null +++ b/pica-wekan/filter-hooks/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3 +LABEL maintainer="quentinduchemin@tuta.io" + +RUN mkdir /app +COPY . /app + +WORKDIR /app + +RUN pip install --no-cache-dir -r requirements.txt + +CMD [ "gunicorn", "--bind", "0.0.0.0:5000", "--reload", "main:app" ] diff --git a/pica-wekan/filter-hooks/main.py b/pica-wekan/filter-hooks/main.py new file mode 100644 index 0000000000000000000000000000000000000000..5c2cc4395541bbb0320c25e86a1fea8187a261ad --- /dev/null +++ b/pica-wekan/filter-hooks/main.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import json +import os +import logging + +import requests + +from flask import Flask, request + +# Configure logging +log_format = '%(asctime)s [%(levelname)s] ' \ + '%(message)s (%(filename)s:%(lineno)d)' +logging.basicConfig( + format=log_format, + datefmt='%Y/%m/%d %H:%M:%S', + level=logging.INFO +) + +# Run Flask +app = Flask(__name__) + +# List of hooks to forward to the real hook URL +# See https://github.com/wekan/wekan/wiki/Webhook-data +allowed_hooks = { + 'act-createCard': 'créé', + 'act-moveCard': 'déplacé', + 'act-addComment': 'commenté' +} + +@app.route('/', methods=['POST']) +def filter_hook(): + hook_data = json.loads(request.data) + event = hook_data['description'] + # Check if we must forward hook + if event in allowed_hooks: + # Retrieve card URL + card_url_pos = hook_data['text'].index(os.environ['KANBAN_URL']) + card_url = hook_data['text'][card_url_pos:] + + # Text like '<user> a <verbe> la carte <nom>' with a link + text = f'{hook_data["user"]} a [{allowed_hooks[event]} la carte **{hook_data["card"]}**]({card_url})' + + # If card moved, add origin and destination lists + if event == 'act-moveCard': + # Yes, I'm ashamed of this ugly parsing, thanks for asking. + from_list = hook_data['text'].split('from list')[1].split('"')[1] + to_list = hook_data['text'].split('to list')[1].split('"')[1] + text += f' de {from_list} vers {to_list}.' + + # If there is a new comment, add it as a citation + elif event == 'act-addComment': + text += f'\n>{hook_data["comment"]}' + + # If new card, add the list it belongs to + elif event == 'act-createCard': + to_list = hook_data['text'].split('to list')[1].split('"')[1] + text += f' dans {to_list}' + + # Forward the hook + r = requests.post(request.args['url'], json={'text': text}) + if r.status_code != 200: + logging.error(f'Unable to forward hook : {r}') + else: + logging.info('Hook forwarded.') + # HTTP No Content + return ('', 204) diff --git a/pica-wekan/filter-hooks/requirements.txt b/pica-wekan/filter-hooks/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..2f904d01514821b3cde42fef0fbdf11924615d30 --- /dev/null +++ b/pica-wekan/filter-hooks/requirements.txt @@ -0,0 +1,3 @@ +Flask +gunicorn +requests diff --git a/wsgi.py b/wsgi.py new file mode 100644 index 0000000000000000000000000000000000000000..ba0cd0d8c017342a1d765ee670ccb4ab08d497c4 --- /dev/null +++ b/wsgi.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from main import app + +if __name__ == "__main__": + app.run()