Browse Source

Alerts to SMS, work in progress.

pull/133/head
Pēteris Caune 7 years ago
parent
commit
25fb11bb3e
20 changed files with 177 additions and 73 deletions
  1. +4
    -1
      hc/api/models.py
  2. +21
    -0
      hc/api/transports.py
  3. +10
    -0
      hc/front/forms.py
  4. +46
    -0
      hc/front/tests/test_add_sms.py
  5. +1
    -0
      hc/front/urls.py
  6. +25
    -2
      hc/front/views.py
  7. +5
    -0
      hc/settings.py
  8. BIN
      static/img/integrations/sms.png
  9. +11
    -0
      templates/front/channels.html
  10. +9
    -0
      templates/front/welcome.html
  11. +0
    -7
      templates/integrations/add_discord.html
  12. +0
    -9
      templates/integrations/add_email.html
  13. +0
    -9
      templates/integrations/add_hipchat.html
  14. +0
    -9
      templates/integrations/add_opsgenie.html
  15. +0
    -9
      templates/integrations/add_pd.html
  16. +44
    -0
      templates/integrations/add_sms.html
  17. +0
    -9
      templates/integrations/add_telegram.html
  18. +0
    -9
      templates/integrations/add_victorops.html
  19. +0
    -9
      templates/integrations/add_webhook.html
  20. +1
    -0
      templates/integrations/sms_message.html

+ 4
- 1
hc/api/models.py View File

@ -36,7 +36,8 @@ CHANNEL_KINDS = (("email", "Email"),
("opsgenie", "OpsGenie"),
("victorops", "VictorOps"),
("discord", "Discord"),
("telegram", "Telegram"))
("telegram", "Telegram"),
("sms", "SMS"))
PO_PRIORITIES = {
-2: "lowest",
@ -270,6 +271,8 @@ class Channel(models.Model):
return transports.Discord(self)
elif self.kind == "telegram":
return transports.Telegram(self)
elif self.kind == "sms":
return transports.Sms(self)
else:
raise NotImplementedError("Unknown channel kind: %s" % self.kind)


+ 21
- 0
hc/api/transports.py View File

@ -296,3 +296,24 @@ class Telegram(HttpTransport):
def notify(self, check):
text = tmpl("telegram_message.html", check=check)
return self.send(self.channel.telegram_id, text)
class Sms(HttpTransport):
URL = 'https://api.twilio.com/2010-04-01/Accounts/%s/Messages.json'
def is_noop(self, check):
return check.status != "down"
def notify(self, check):
url = self.URL % settings.TWILIO_ACCOUNT
auth = (settings.TWILIO_ACCOUNT, settings.TWILIO_AUTH)
text = tmpl("sms_message.html", check=check,
site_name=settings.SITE_NAME)
data = {
'From': settings.TWILIO_FROM,
'To': self.channel.value,
'Body': text,
}
return self.post(url, data=data, auth=auth)

+ 10
- 0
hc/front/forms.py View File

@ -1,4 +1,5 @@
from django import forms
from django.core.validators import RegexValidator
from hc.front.validators import (CronExpressionValidator, TimezoneValidator,
WebhookValidator)
@ -64,3 +65,12 @@ class AddWebhookForm(forms.Form):
def get_value(self):
d = self.cleaned_data
return "\n".join((d["value_down"], d["value_up"], d["post_data"]))
phone_validator = RegexValidator(regex='^\+\d{5,15}$',
message="Invalid phone number format.")
class AddSmsForm(forms.Form):
error_css_class = "has-error"
value = forms.CharField(max_length=16, validators=[phone_validator])

+ 46
- 0
hc/front/tests/test_add_sms.py View File

@ -0,0 +1,46 @@
from django.test.utils import override_settings
from hc.api.models import Channel
from hc.test import BaseTestCase
@override_settings(TWILIO_ACCOUNT="foo", TWILIO_AUTH="foo", TWILIO_FROM="123")
class AddSmsTestCase(BaseTestCase):
url = "/integrations/add_sms/"
def test_instructions_work(self):
self.client.login(username="[email protected]", password="password")
r = self.client.get(self.url)
self.assertContains(r, "Get a SMS message")
def test_it_creates_channel(self):
form = {"value": "+1234567890"}
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url, form)
self.assertRedirects(r, "/integrations/")
c = Channel.objects.get()
self.assertEqual(c.kind, "sms")
self.assertEqual(c.value, "+1234567890")
def test_it_rejects_bad_number(self):
form = {"value": "not a phone number address"}
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url, form)
self.assertContains(r, "Invalid phone number format.")
def test_it_trims_whitespace(self):
form = {"value": " +1234567890 "}
self.client.login(username="[email protected]", password="password")
self.client.post(self.url, form)
c = Channel.objects.get()
self.assertEqual(c.value, "+1234567890")
@override_settings(TWILIO_AUTH=None)
def test_it_requires_credentials(self):
self.client.login(username="[email protected]", password="password")
r = self.client.get("/integrations/add_sms/")
self.assertEqual(r.status_code, 404)

+ 1
- 0
hc/front/urls.py View File

@ -26,6 +26,7 @@ channel_urls = [
url(r'^add_victorops/$', views.add_victorops, name="hc-add-victorops"),
url(r'^telegram/bot/$', views.telegram_bot),
url(r'^add_telegram/$', views.add_telegram, name="hc-add-telegram"),
url(r'^add_sms/$', views.add_sms, name="hc-add-sms"),
url(r'^([\w-]+)/checks/$', views.channel_checks, name="hc-channel-checks"),
url(r'^([\w-]+)/remove/$', views.remove_channel, name="hc-remove-channel"),
url(r'^([\w-]+)/verify/([\w-]+)/$', views.verify_email,


+ 25
- 2
hc/front/views.py View File

@ -25,7 +25,7 @@ from hc.api.models import (DEFAULT_GRACE, DEFAULT_TIMEOUT, Channel, Check,
from hc.api.transports import Telegram
from hc.front.forms import (AddWebhookForm, NameTagsForm,
TimeoutForm, AddUrlForm, AddPdForm, AddEmailForm,
AddOpsGenieForm, CronForm)
AddOpsGenieForm, CronForm, AddSmsForm)
from hc.front.schemas import telegram_callback
from hc.lib import jsonschema
from pytz import all_timezones
@ -106,6 +106,7 @@ def index(request):
"enable_pushover": settings.PUSHOVER_API_TOKEN is not None,
"enable_discord": settings.DISCORD_CLIENT_ID is not None,
"enable_telegram": settings.TELEGRAM_TOKEN is not None,
"enable_sms": settings.TWILIO_AUTH is not None,
"registration_open": settings.REGISTRATION_OPEN
}
@ -350,7 +351,8 @@ def channels(request):
"enable_pushbullet": settings.PUSHBULLET_CLIENT_ID is not None,
"enable_pushover": settings.PUSHOVER_API_TOKEN is not None,
"enable_discord": settings.DISCORD_CLIENT_ID is not None,
"enable_telegram": settings.TELEGRAM_TOKEN is not None
"enable_telegram": settings.TELEGRAM_TOKEN is not None,
"enable_sms": settings.TWILIO_AUTH is not None
}
return render(request, "front/channels.html", ctx)
@ -811,6 +813,27 @@ def add_telegram(request):
return render(request, "integrations/add_telegram.html", ctx)
@login_required
def add_sms(request):
if settings.TWILIO_AUTH is None:
raise Http404("sms integration is not available")
if request.method == "POST":
form = AddSmsForm(request.POST)
if form.is_valid():
channel = Channel(user=request.team.user, kind="sms")
channel.value = form.cleaned_data["value"]
channel.save()
channel.assign_all_checks()
return redirect("hc-channels")
else:
form = AddSmsForm()
ctx = {"page": "channels", "form": form}
return render(request, "integrations/add_sms.html", ctx)
def privacy(request):
return render(request, "front/privacy.html", {})


+ 5
- 0
hc/settings.py View File

@ -157,6 +157,11 @@ PUSHBULLET_CLIENT_SECRET = None
TELEGRAM_BOT_NAME = "ExampleBot"
TELEGRAM_TOKEN = None
# SMS (Twilio) integration -- override in local_settings.py
TWILIO_ACCOUNT = None
TWILIO_AUTH = None
TWILIO_FROM = None
if os.path.exists(os.path.join(BASE_DIR, "hc/local_settings.py")):
from .local_settings import *
else:


BIN
static/img/integrations/sms.png View File

Before After
Width: 128  |  Height: 128  |  Size: 3.9 KiB

+ 11
- 0
templates/front/channels.html View File

@ -152,6 +152,17 @@
<a href="{% url 'hc-add-email' %}" class="btn btn-primary">Add Integration</a>
</li>
{% if enable_sms %}
<li>
<img src="{% static 'img/integrations/sms.png' %}"
class="icon" alt="SMS icon" />
<h2>SMS</h2>
<p>Get a text message to your phone when check goes down.</p>
<a href="{% url 'hc-add-sms' %}" class="btn btn-primary">Add Integration</a>
</li>
{% endif %}
<li>
<img src="{% static 'img/integrations/webhook.png' %}"
class="icon" alt="Webhook icon" />


+ 9
- 0
templates/front/welcome.html View File

@ -236,6 +236,15 @@
<td>Good old email messages.</td>
</tr>
{% if enable_sms %}
<tr>
<td>
<img width="22" height="22" alt="Email icon" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAYAAAAehFoBAAAFHElEQVRYw+2Ya2xURRSAv5m72+32wbbYgpSUElqUCPgAohJgoSgQpZHERIlK4gNisEL4IyEm/uCHYoIxhoeERKLiO8QEJBhJoVBKmhAoUihQ5WlTlmK3pbuw7733jj/AS7fbUkqV7iacP3cy99yTb86cOefcgfvy/4roafKz/bPyHDbb0MEEc4iwd/H0uuu9An+1f1ZmWNpXKNTbwJgUcegfCD73Gvrm1eU1ugW8/tBzQ7RofDcwNUUjoSrL1Be8WV4TkQBaLL4lhWEB5oak7VMAsf7gvMc002hIg/NmKN0sldIw56dJgtCkTc6TQqjidElpCkZJoZQtfZKwsN8RbLajgPEjKsjPKsah5RDVA1wN/cWxlm3oZhR32XKc9jwADjd/TWeoxfo2KyOfGaXLbnpIUdX0IQAZtmzGj6igILsUp91FzAjhD3s44dlOMNbRK0ufwEWuR6mYuAabdCTMl+Km6cpuAlEvJUOfIjdzOAD+iIdDF7+8pVcwk9JCt7WpVU3gchbx4uPrrEV2lUu+Y7cFln0BTypemABrKv22+mMLZycU0IeGzU7SmVC0IAG2L5v98vDQ7NHW+JcT7+HxHceuOXlwyCNE9WCSfm7mcEa4xtPqP4nLWcTwIeOSbWaVWOMDZ9dxqnUXmsygMKeMQNR7W54+PSzFrTWFYp0AxI0wLZ1HiRuhXrxc3uUperCpJdk0zBhXrp0eOPD1yN/WeM649ynIKetVNxK/BkBZ4UyksDH2ZjhE4v5Em9E2azyjrJJR+VPuOCT6BG649PONDAgU5JTx0qRNzH54JVkZyc2cP+whqgfItLuYVLyQ/KxRmMrAGziXoNfo2YFhxgHIcQyjYuLHzJ/wEflZxQMHPt9eS1XTGmvrBIJxw+fyypQtSd522HO50H4QgCkli26c+s6jCWEF4A2cZWfjKvxhjzVXMvRJXp60mdEPTB0YMMA5bw3fH3mdI83fopvRG3C2XJ4e/VaCnl1zcqZtX0Lsn2nbh11zJtls9TfyY/0Sas9tsEJGkxnMKH134MD/HrQjzd/w68kPrLn8rFGJxV7Yuew7bh2cuBHhYkcdmrT3aNNUOicv72Tb75WYyrCyTPec3++05g97rJgLx31dKqXood4rqv9ci8s5klCsg7gRSdLJcxYTjHVYWSZmBDGVnpA97hp42piljHBNIBBtQzdiCQejawbpKh5fAx5f7x3rxKIXGF9UwfVIGzEjiMs50vJqOO6zwu6ugAFs0kGes7jbdhocbfnprvsYKWy4nEVJ8/XN3w3Mwxc76rDJDLIdBdikg2Csg/bAeRo922kPXgCgI3SRiH6NWA+VD6Az1IypdJRSN/uFBvKzS8h1DMOuOQnH/XSGmjnVuguP7/jtG7ZNNc98oYRYkibt5VqppFBp08ArpaRSXE2jS5R2KZSsSxdgKcxa6VWx34AzqfgD1032v+Ped1iuLq/RTVO8BgRS+BLtrKmJRVZpXl6+p96Q2nSgPsWcHFOKDaGIfGL59D2Xk9ahFGJjzZzJUpqTBTLvZql1A893360uHzYJxNb/GtaU5qlwSNu/cl5VsM/by66ysfbZlUKxtpfXHqWb05Y9s6/5Xnl9IHcSHZpQc5feQ9h+tZfdJGwKsWCpu/r0vY7ru/GwIYR6dbl776Dk7/56WAnU4kp39Y5BKx79pF1VObN6K4Mo/QHeuMy995NBL893mMB/8Lqnr0iJfqIvBQ3zgD27843VYrXJfbkvqSf/AF8f89OScY4/AAAAAElFTkSuQmCC" />
</td>
<td>SMS text messages.</td>
</tr>
{% endif %}
<tr>
<td>
<img width="22" height="22" alt="Webhook icon" src="data:image/gif;base64,R0lGODdhLAAsAOMQAEBCQEpMS8s8aF5gXmxubtRkhoWIhp2gnuGRqLi6uOm3yfHJ1tja2PHZ4+/s6/3//CwAAAAALAAsAAAE/vDJSau9OOvNu/+gtyBFWSBLGDqF4L5u0aidAt8vQmv23eK6ncUBKygoi5/LIawgXgXM0xVsPogxCcMQCBgYEuXM2hMcHQGAGhAAl6tC8cOwXg+uUOtDKRnU10x5VkpMfn8AgVlWUwIzdH93e4pNbw8MaXUHSGNWMDMMBJhsehdljRMMawakFkoCCGMEa2CsE1gvKVeqtRQNghIHs1YJtBOEFA6GAwwHBs4HxRsJaZoTlRXBh3bRF48ABBR8F4baaqsY5AADTA+MnBWpa12i5t2ZtL5UGnQEDAwODrZkqoBmTQJbcjIwu5CNDTtgBiUw0sfBgYEBAwgcfNCwmgRMtuAk3fCQDkA1WaMkOFijaQEOMxwzEnBG0xnKP0wasosH4KCpPAnKCe35IKiajTx9voxidOihg003rlSjKd+NFI8CHNjKtdmhNhyFfVQTaeIrXWpCWih5cg2FjhKSlDgyhyUGi2wGbGzq8YqovhMaBrjgj7C8hxDXaGTiIMFNANwspfmCiotdC96GnqPAU02AAfTUZWhYDrDKzNo2332sGLGFaeU2cmD2LIFrhTadyebFu7dvGhEAADs=" />


+ 0
- 7
templates/integrations/add_discord.html View File

@ -28,10 +28,3 @@
</div>
</div>
{% endblock %}
{% block scripts %}
{% compress js %}
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
{% endcompress %}
{% endblock %}

+ 0
- 9
templates/integrations/add_email.html View File

@ -52,13 +52,4 @@
</form>
</div>
</div>
{% endblock %}
{% block scripts %}
{% compress js %}
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
{% endcompress %}
{% endblock %}

+ 0
- 9
templates/integrations/add_hipchat.html View File

@ -92,13 +92,4 @@
</form>
</div>
</div>
{% endblock %}
{% block scripts %}
{% compress js %}
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
{% endcompress %}
{% endblock %}

+ 0
- 9
templates/integrations/add_opsgenie.html View File

@ -90,13 +90,4 @@
</form>
</div>
</div>
{% endblock %}
{% block scripts %}
{% compress js %}
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
{% endcompress %}
{% endblock %}

+ 0
- 9
templates/integrations/add_pd.html View File

@ -91,13 +91,4 @@
</form>
</div>
</div>
{% endblock %}
{% block scripts %}
{% compress js %}
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
{% endcompress %}
{% endblock %}

+ 44
- 0
templates/integrations/add_sms.html View File

@ -0,0 +1,44 @@
{% extends "base.html" %}
{% load compress humanize staticfiles hc_extras %}
{% block title %}Notification Channels - {% site_name %}{% endblock %}
{% block content %}
<div class="row">
<div class="col-sm-12">
<h1>SMS</h1>
<p>Get a SMS message to your specified number when check goes down.</p>
<h2>Integration Settings</h2>
<form method="post" class="form-horizontal" action="{% url 'hc-add-sms' %}">
{% csrf_token %}
<div class="form-group {{ form.value.css_classes }}">
<label for="id_number" class="col-sm-2 control-label">Phone Number</label>
<div class="col-sm-3">
<input
id="id_number"
type="tel"
class="form-control"
name="value"
placeholder="+1234567890"
value="{{ form.value.value|default:"" }}">
{% if form.value.errors %}
<div class="help-block">
{{ form.value.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 %}

+ 0
- 9
templates/integrations/add_telegram.html View File

@ -89,16 +89,7 @@
src="{% static 'img/integrations/setup_telegram_3.png' %}">
</div>
</div>
{% endif %}
</div>
</div>
{% endblock %}
{% block scripts %}
{% compress js %}
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
{% endcompress %}
{% endblock %}

+ 0
- 9
templates/integrations/add_victorops.html View File

@ -105,13 +105,4 @@
</form>
</div>
</div>
{% endblock %}
{% block scripts %}
{% compress js %}
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
{% endcompress %}
{% endblock %}

+ 0
- 9
templates/integrations/add_webhook.html View File

@ -113,13 +113,4 @@
</form>
</div>
</div>
{% endblock %}
{% block scripts %}
{% compress js %}
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
{% endcompress %}
{% endblock %}

+ 1
- 0
templates/integrations/sms_message.html View File

@ -0,0 +1 @@
{% load humanize %}{{ site_name }}: The check "{{ check.name_then_code }}" is DOWN. Last ping was {{ check.last_ping|naturaltime }}.

Loading…
Cancel
Save