Verified Commit 861009a9 authored by Quentin Duchemin's avatar Quentin Duchemin
Browse files

[Wekan] Add tool to filter and forward hooks by type

parent 74593bff
......@@ -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é.
......@@ -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
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" ]
#!/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)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from main import app
if __name__ == "__main__":
app.run()
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment