From f539e99652b49eee9221de2652fbcbbc13b93734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: Fri, 22 Feb 2019 18:23:36 +0200 Subject: [PATCH] Matrix integration WIP. cc: #175 --- hc/api/models.py | 5 +- hc/api/transports.py | 24 ++++++++- hc/front/forms.py | 24 +++++++++ hc/front/urls.py | 1 + hc/front/views.py | 36 ++++++++++++- hc/settings.py | 4 ++ static/img/integrations/matrix.png | Bin 0 -> 1034 bytes templates/front/channels.html | 13 +++++ templates/integrations/add_matrix.html | 48 ++++++++++++++++++ .../integrations/matrix_description.html | 6 +++ .../matrix_description_formatted.html | 6 +++ 11 files changed, 163 insertions(+), 4 deletions(-) create mode 100644 static/img/integrations/matrix.png create mode 100644 templates/integrations/add_matrix.html create mode 100644 templates/integrations/matrix_description.html create mode 100644 templates/integrations/matrix_description_formatted.html diff --git a/hc/api/models.py b/hc/api/models.py index c9f29e69..5318254b 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -43,7 +43,8 @@ CHANNEL_KINDS = (("email", "Email"), ("telegram", "Telegram"), ("sms", "SMS"), ("zendesk", "Zendesk"), - ("trello", "Trello")) + ("trello", "Trello"), + ("matrix", "Matrix")) PO_PRIORITIES = { -2: "lowest", @@ -339,6 +340,8 @@ class Channel(models.Model): return transports.Sms(self) elif self.kind == "trello": return transports.Trello(self) + elif self.kind == "matrix": + return transports.Matrix(self) else: raise NotImplementedError("Unknown channel kind: %s" % self.kind) diff --git a/hc/api/transports.py b/hc/api/transports.py index ce39708c..b621d08d 100644 --- a/hc/api/transports.py +++ b/hc/api/transports.py @@ -3,7 +3,7 @@ from django.template.loader import render_to_string from django.utils import timezone import json import requests -from urllib.parse import quote +from urllib.parse import quote, urlencode from hc.accounts.models import Profile from hc.lib import emails @@ -343,6 +343,28 @@ class VictorOps(HttpTransport): return self.post(self.channel.value, json=payload) +class Matrix(HttpTransport): + def get_url(self): + s = quote(self.channel.value) + + url = settings.MATRIX_HOMESERVER + url += "/_matrix/client/r0/rooms/%s/send/m.room.message?" % s + url += urlencode({"access_token": settings.MATRIX_ACCESS_TOKEN}) + return url + + def notify(self, check): + plain = tmpl("matrix_description.html", check=check) + formatted = tmpl("matrix_description_formatted.html", check=check) + payload = { + "msgtype": "m.text", + "body": plain, + "format": "org.matrix.custom.html", + "formatted_body": formatted + } + + return self.post(self.get_url(), json=payload) + + class Discord(HttpTransport): def notify(self, check): text = tmpl("slack_message.json", check=check) diff --git a/hc/front/forms.py b/hc/front/forms.py index c82de82c..28a48890 100644 --- a/hc/front/forms.py +++ b/hc/front/forms.py @@ -1,11 +1,14 @@ from datetime import timedelta as td import json import re +from urllib.parse import quote, urlencode from django import forms +from django.conf import settings from django.core.validators import RegexValidator from hc.front.validators import (CronExpressionValidator, TimezoneValidator, WebhookValidator) +import requests class NameTagsForm(forms.Form): @@ -116,3 +119,24 @@ class AddSmsForm(forms.Form): class ChannelNameForm(forms.Form): name = forms.CharField(max_length=100, required=False) + + +class AddMatrixForm(forms.Form): + error_css_class = "has-error" + alias = forms.CharField(max_length=40) + + def clean_alias(self): + v = self.cleaned_data["alias"] + + # validate it by trying to join + url = settings.MATRIX_HOMESERVER + url += "/_matrix/client/r0/join/%s?" % quote(v) + url += urlencode({"access_token": settings.MATRIX_ACCESS_TOKEN}) + doc = requests.post(url, {}).json() + if "error" in doc: + raise forms.ValidationError( + "Response from Matrix: %s" % doc["error"]) + + self.cleaned_data["room_id"] = doc["room_id"] + + return v diff --git a/hc/front/urls.py b/hc/front/urls.py index 7c6b4d4e..f2ddf2a6 100644 --- a/hc/front/urls.py +++ b/hc/front/urls.py @@ -38,6 +38,7 @@ channel_urls = [ path('add_sms/', views.add_sms, name="hc-add-sms"), path('add_trello/', views.add_trello, name="hc-add-trello"), path('add_trello/settings/', views.trello_settings, name="hc-trello-settings"), + path('add_matrix/', views.add_matrix, name="hc-add-matrix"), path('/checks/', views.channel_checks, name="hc-channel-checks"), path('/name/', views.update_channel_name, name="hc-channel-name"), path('/remove/', views.remove_channel, name="hc-remove-channel"), diff --git a/hc/front/views.py b/hc/front/views.py index 9a5f136b..2cd70a9f 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -1,6 +1,6 @@ from datetime import datetime, timedelta as td import json -from urllib.parse import urlencode +from urllib.parse import urlencode, quote from croniter import croniter from django.conf import settings @@ -24,7 +24,7 @@ from hc.api.transports import Telegram from hc.front.forms import (AddWebhookForm, NameTagsForm, TimeoutForm, AddUrlForm, AddEmailForm, AddOpsGenieForm, CronForm, AddSmsForm, - ChannelNameForm, EmailSettingsForm) + ChannelNameForm, EmailSettingsForm, AddMatrixForm) from hc.front.schemas import telegram_callback from hc.front.templatetags.hc_extras import (num_down_title, down_title, sortchecks) @@ -550,6 +550,7 @@ def channels(request): "enable_sms": settings.TWILIO_AUTH is not None, "enable_pd": settings.PD_VENDOR_KEY is not None, "enable_trello": settings.TRELLO_APP_KEY is not None, + "enable_matrix": settings.MATRIX_ACCESS_TOKEN is not None, "use_payments": settings.USE_PAYMENTS } @@ -1144,6 +1145,37 @@ def add_trello(request): return render(request, "integrations/add_trello.html", ctx) +@login_required +def add_matrix(request): + if settings.MATRIX_ACCESS_TOKEN is None: + raise Http404("matrix integration is not available") + + if request.method == "POST": + form = AddMatrixForm(request.POST) + if form.is_valid(): + channel = Channel(project=request.project, kind="matrix") + channel.value = form.cleaned_data["room_id"] + + # If user supplied room alias instead of ID, use it as channel name + alias = form.cleaned_data["alias"] + if not alias.startswith("!"): + channel.name = alias + + channel.save() + + channel.assign_all_checks() + messages.success(request, "The Matrix integration has been added!") + return redirect("hc-channels") + else: + form = AddMatrixForm() + + ctx = { + "page": "channels", + "form": form + } + return render(request, "integrations/add_matrix.html", ctx) + + @login_required @require_POST def trello_settings(request): diff --git a/hc/settings.py b/hc/settings.py index 6f345746..144326cb 100644 --- a/hc/settings.py +++ b/hc/settings.py @@ -196,6 +196,10 @@ PD_VENDOR_KEY = os.getenv("PD_VENDOR_KEY") # Trello TRELLO_APP_KEY = os.getenv("TRELLO_APP_KEY") +# Matrix +MATRIX_HOMESERVER = os.getenv("MATRIX_HOMESERVER") +MATRIX_ACCESS_TOKEN = os.getenv("MATRIX_ACCESS_TOKEN") + if os.path.exists(os.path.join(BASE_DIR, "hc/local_settings.py")): from .local_settings import * else: diff --git a/static/img/integrations/matrix.png b/static/img/integrations/matrix.png new file mode 100644 index 0000000000000000000000000000000000000000..a7ca9b0cac00dc3ba94c24a72638eebb1fc70336 GIT binary patch literal 1034 zcmV+l1oiugP)yPlDqv*{=`4+9{sD`G6|qU|wQ~!L zq);PR`Lt0iq9ABc;}0}Qf-CN>Z{}-rE_<2W=Q_LZJ@+klV3^r^&&-_9``#bto|!e^ z1h5{~FbP~Qfc?N0;GAF60-pG}So~Y?@dXN~_2lQp^ zcv#Q%ux~qPHB`V}i(n6Q1L%5S%%AZRcp&MW*O?^M>^A`Gd?oM?@JP~Bbl+;AfRR&U zz^~q%2#wFcf z2xJH7c&lkiBDIG)U~}jr(Ov;-fRl;xvQ~BrSmXCPTdOPmnz-y0ux~E^5`K1o&U~6^ z8Rb1HABmp>zX0C?)7~=LUT=}J?||*VDKo1)tuW=vg?=6G8GzRU6K1vzcsd^?n<}_5 z`jxAMnz0&lm3O1_nVRzT@^OED(L}+1&4aA&>8#C3ZdCZ*iZ33~%>C;H?hurAmPQ^BsQazWA+Gh1$E zE#C`C9pGN1oLrSRz_^*U&8%?y6+TCvuc;2O$_Fx{fMBSh+8-W`RFbMF31q;tI70FB z`;uO`eb?845zL&0i}L8!j<4I(d> zGGGSGfEh3YF1DOZkGW7Em=}B5H@W7M0fsQE)gbH&_N|2k1j7O>qa{*!Zut?*uMo)F zz!i+*%)flC7dsON$Ymlqb=31Siqv?MNMMEY-+@gDsE0LQ`mdm*8Q|^*>Xj&a`jPjR zA72BnKiMGT%c7Kj>Xng8^r5Eq%qOch-@Itm&|XdYP*eLn(PzL6m;p0j2F!pNaA_}i zEqoB`@9jqVgIH%U{DW8l(~`QQtWnmpet>DjvqzQ)2=*KZ&2eYr&ZvfZd14QeVq{r;91poj507*qoM6N<$ Ef+=|6Z~y=R literal 0 HcmV?d00001 diff --git a/templates/front/channels.html b/templates/front/channels.html index b6b0cb11..ba50e0ab 100644 --- a/templates/front/channels.html +++ b/templates/front/channels.html @@ -80,6 +80,8 @@ Trello board {{ ch.trello_board_list|first }}, list {{ ch.trello_board_list|last }} + {% elif ch.kind == "matrix" %} + Matrix {{ ch.value }} {% else %} {{ ch.kind }} {% endif %} @@ -279,6 +281,17 @@ Add Integration {% endif %} + {% if enable_matrix %} +
  • + Matrix icon + +

    Matrix

    +

    Post notifications to a Matrix room.

    + + Add Integration +
  • + {% endif %}