forked from GithubBackups/healthchecks
Matrix integration WIP. cc: #175
This commit is contained in:
parent
15a853bd8a
commit
f539e99652
@ -43,7 +43,8 @@ CHANNEL_KINDS = (("email", "Email"),
|
|||||||
("telegram", "Telegram"),
|
("telegram", "Telegram"),
|
||||||
("sms", "SMS"),
|
("sms", "SMS"),
|
||||||
("zendesk", "Zendesk"),
|
("zendesk", "Zendesk"),
|
||||||
("trello", "Trello"))
|
("trello", "Trello"),
|
||||||
|
("matrix", "Matrix"))
|
||||||
|
|
||||||
PO_PRIORITIES = {
|
PO_PRIORITIES = {
|
||||||
-2: "lowest",
|
-2: "lowest",
|
||||||
@ -339,6 +340,8 @@ class Channel(models.Model):
|
|||||||
return transports.Sms(self)
|
return transports.Sms(self)
|
||||||
elif self.kind == "trello":
|
elif self.kind == "trello":
|
||||||
return transports.Trello(self)
|
return transports.Trello(self)
|
||||||
|
elif self.kind == "matrix":
|
||||||
|
return transports.Matrix(self)
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("Unknown channel kind: %s" % self.kind)
|
raise NotImplementedError("Unknown channel kind: %s" % self.kind)
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ from django.template.loader import render_to_string
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
import json
|
import json
|
||||||
import requests
|
import requests
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote, urlencode
|
||||||
|
|
||||||
from hc.accounts.models import Profile
|
from hc.accounts.models import Profile
|
||||||
from hc.lib import emails
|
from hc.lib import emails
|
||||||
@ -343,6 +343,28 @@ class VictorOps(HttpTransport):
|
|||||||
return self.post(self.channel.value, json=payload)
|
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):
|
class Discord(HttpTransport):
|
||||||
def notify(self, check):
|
def notify(self, check):
|
||||||
text = tmpl("slack_message.json", check=check)
|
text = tmpl("slack_message.json", check=check)
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
from datetime import timedelta as td
|
from datetime import timedelta as td
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
from urllib.parse import quote, urlencode
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.conf import settings
|
||||||
from django.core.validators import RegexValidator
|
from django.core.validators import RegexValidator
|
||||||
from hc.front.validators import (CronExpressionValidator, TimezoneValidator,
|
from hc.front.validators import (CronExpressionValidator, TimezoneValidator,
|
||||||
WebhookValidator)
|
WebhookValidator)
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
class NameTagsForm(forms.Form):
|
class NameTagsForm(forms.Form):
|
||||||
@ -116,3 +119,24 @@ class AddSmsForm(forms.Form):
|
|||||||
|
|
||||||
class ChannelNameForm(forms.Form):
|
class ChannelNameForm(forms.Form):
|
||||||
name = forms.CharField(max_length=100, required=False)
|
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
|
||||||
|
@ -38,6 +38,7 @@ channel_urls = [
|
|||||||
path('add_sms/', views.add_sms, name="hc-add-sms"),
|
path('add_sms/', views.add_sms, name="hc-add-sms"),
|
||||||
path('add_trello/', views.add_trello, name="hc-add-trello"),
|
path('add_trello/', views.add_trello, name="hc-add-trello"),
|
||||||
path('add_trello/settings/', views.trello_settings, name="hc-trello-settings"),
|
path('add_trello/settings/', views.trello_settings, name="hc-trello-settings"),
|
||||||
|
path('add_matrix/', views.add_matrix, name="hc-add-matrix"),
|
||||||
path('<uuid:code>/checks/', views.channel_checks, name="hc-channel-checks"),
|
path('<uuid:code>/checks/', views.channel_checks, name="hc-channel-checks"),
|
||||||
path('<uuid:code>/name/', views.update_channel_name, name="hc-channel-name"),
|
path('<uuid:code>/name/', views.update_channel_name, name="hc-channel-name"),
|
||||||
path('<uuid:code>/remove/', views.remove_channel, name="hc-remove-channel"),
|
path('<uuid:code>/remove/', views.remove_channel, name="hc-remove-channel"),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from datetime import datetime, timedelta as td
|
from datetime import datetime, timedelta as td
|
||||||
import json
|
import json
|
||||||
from urllib.parse import urlencode
|
from urllib.parse import urlencode, quote
|
||||||
|
|
||||||
from croniter import croniter
|
from croniter import croniter
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -24,7 +24,7 @@ from hc.api.transports import Telegram
|
|||||||
from hc.front.forms import (AddWebhookForm, NameTagsForm,
|
from hc.front.forms import (AddWebhookForm, NameTagsForm,
|
||||||
TimeoutForm, AddUrlForm, AddEmailForm,
|
TimeoutForm, AddUrlForm, AddEmailForm,
|
||||||
AddOpsGenieForm, CronForm, AddSmsForm,
|
AddOpsGenieForm, CronForm, AddSmsForm,
|
||||||
ChannelNameForm, EmailSettingsForm)
|
ChannelNameForm, EmailSettingsForm, AddMatrixForm)
|
||||||
from hc.front.schemas import telegram_callback
|
from hc.front.schemas import telegram_callback
|
||||||
from hc.front.templatetags.hc_extras import (num_down_title, down_title,
|
from hc.front.templatetags.hc_extras import (num_down_title, down_title,
|
||||||
sortchecks)
|
sortchecks)
|
||||||
@ -550,6 +550,7 @@ def channels(request):
|
|||||||
"enable_sms": settings.TWILIO_AUTH is not None,
|
"enable_sms": settings.TWILIO_AUTH is not None,
|
||||||
"enable_pd": settings.PD_VENDOR_KEY is not None,
|
"enable_pd": settings.PD_VENDOR_KEY is not None,
|
||||||
"enable_trello": settings.TRELLO_APP_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
|
"use_payments": settings.USE_PAYMENTS
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1144,6 +1145,37 @@ def add_trello(request):
|
|||||||
return render(request, "integrations/add_trello.html", ctx)
|
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
|
@login_required
|
||||||
@require_POST
|
@require_POST
|
||||||
def trello_settings(request):
|
def trello_settings(request):
|
||||||
|
@ -196,6 +196,10 @@ PD_VENDOR_KEY = os.getenv("PD_VENDOR_KEY")
|
|||||||
# Trello
|
# Trello
|
||||||
TRELLO_APP_KEY = os.getenv("TRELLO_APP_KEY")
|
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")):
|
if os.path.exists(os.path.join(BASE_DIR, "hc/local_settings.py")):
|
||||||
from .local_settings import *
|
from .local_settings import *
|
||||||
else:
|
else:
|
||||||
|
BIN
static/img/integrations/matrix.png
Normal file
BIN
static/img/integrations/matrix.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
@ -80,6 +80,8 @@
|
|||||||
Trello
|
Trello
|
||||||
board <span>{{ ch.trello_board_list|first }}</span>,
|
board <span>{{ ch.trello_board_list|first }}</span>,
|
||||||
list <span>{{ ch.trello_board_list|last }}</span>
|
list <span>{{ ch.trello_board_list|last }}</span>
|
||||||
|
{% elif ch.kind == "matrix" %}
|
||||||
|
Matrix <span>{{ ch.value }}</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ ch.kind }}
|
{{ ch.kind }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -279,6 +281,17 @@
|
|||||||
<a href="{% url 'hc-add-trello' %}" class="btn btn-primary">Add Integration</a>
|
<a href="{% url 'hc-add-trello' %}" class="btn btn-primary">Add Integration</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if enable_matrix %}
|
||||||
|
<li>
|
||||||
|
<img src="{% static 'img/integrations/matrix.png' %}"
|
||||||
|
class="icon" alt="Matrix icon" />
|
||||||
|
|
||||||
|
<h2>Matrix</h2>
|
||||||
|
<p>Post notifications to a Matrix room.</p>
|
||||||
|
|
||||||
|
<a href="{% url 'hc-add-matrix' %}" class="btn btn-primary">Add Integration</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
<li class="link-to-github">
|
<li class="link-to-github">
|
||||||
<img src="{% static 'img/integrations/missing.png' %}"
|
<img src="{% static 'img/integrations/missing.png' %}"
|
||||||
class="icon" alt="Suggest New Integration" />
|
class="icon" alt="Suggest New Integration" />
|
||||||
|
48
templates/integrations/add_matrix.html
Normal file
48
templates/integrations/add_matrix.html
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load humanize static hc_extras %}
|
||||||
|
|
||||||
|
{% block title %}Add Matrix - {% site_name %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<h1>Matrix</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
If your team uses <a href="https://matrix.org/">Matrix</a>,
|
||||||
|
you can set up {% site_name %} to post notifications
|
||||||
|
to an appropriate Matrix room.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>Integration Settings</h2>
|
||||||
|
|
||||||
|
<form method="post" class="form-horizontal">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
<div class="form-group {{ form.room_id.css_classes }}">
|
||||||
|
<label for="alias" class="col-sm-2 control-label">Room Alias or ID</label>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input
|
||||||
|
id="alias"
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
name="alias"
|
||||||
|
placeholder="!abc:matrix.org"
|
||||||
|
value="{{ form.alias.value|default:"" }}">
|
||||||
|
|
||||||
|
{% if form.alias.errors %}
|
||||||
|
<div class="help-block">
|
||||||
|
{{ form.alias.errors|join:"" }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="col-sm-offset-2 col-sm-10">
|
||||||
|
<button type="submit" class="btn btn-primary">Save Integration</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
6
templates/integrations/matrix_description.html
Normal file
6
templates/integrations/matrix_description.html
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{% load humanize %}
|
||||||
|
{% if check.status == "down" %}
|
||||||
|
{{ check.name_then_code }} is DOWN. Last ping was {{ check.last_ping|naturaltime }}.
|
||||||
|
{% else %}
|
||||||
|
{{ check.name_then_code }} is now UP.
|
||||||
|
{% endif %}
|
6
templates/integrations/matrix_description_formatted.html
Normal file
6
templates/integrations/matrix_description_formatted.html
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{% load humanize %}
|
||||||
|
{% if check.status == "down" %}
|
||||||
|
<b>{{ check.name_then_code }}</b> is <b>DOWN</b>. Last ping was {{ check.last_ping|naturaltime }}.
|
||||||
|
{% else %}
|
||||||
|
<b>{{ check.name_then_code }}</b> is now <b>UP</b>.
|
||||||
|
{% endif %}
|
Loading…
x
Reference in New Issue
Block a user