forked from GithubBackups/healthchecks
Rate limiting for Telegram notifications (10 notifications per chat per minute)
This commit is contained in:
parent
76ae42bc8f
commit
4a43ed59fc
@ -3,6 +3,9 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
## v1.15.0-dev - Unreleased
|
## v1.15.0-dev - Unreleased
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
- Rate limiting for Telegram notifications (10 notifications per chat per minute)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
- "Get a single check" API call now supports read-only API keys (#346)
|
- "Get a single check" API call now supports read-only API keys (#346)
|
||||||
|
|
||||||
|
@ -813,3 +813,10 @@ class TokenBucket(models.Model):
|
|||||||
|
|
||||||
# 20 password attempts per day
|
# 20 password attempts per day
|
||||||
return TokenBucket.authorize(value, 20, 3600 * 24)
|
return TokenBucket.authorize(value, 20, 3600 * 24)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def authorize_telegram(telegram_id):
|
||||||
|
value = "tg-%s" % telegram_id
|
||||||
|
|
||||||
|
# 10 messages for a single chat per minute:
|
||||||
|
return TokenBucket.authorize(value, 10, 60)
|
||||||
|
@ -6,7 +6,7 @@ from unittest.mock import patch, Mock
|
|||||||
|
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from hc.api.models import Channel, Check, Notification
|
from hc.api.models import Channel, Check, Notification, TokenBucket
|
||||||
from hc.test import BaseTestCase
|
from hc.test import BaseTestCase
|
||||||
from requests.exceptions import ConnectionError, Timeout
|
from requests.exceptions import ConnectionError, Timeout
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
@ -602,6 +602,15 @@ class NotifyTestCase(BaseTestCase):
|
|||||||
n = Notification.objects.first()
|
n = Notification.objects.first()
|
||||||
self.assertEqual(n.error, 'Received status code 400 with a message: "Hi"')
|
self.assertEqual(n.error, 'Received status code 400 with a message: "Hi"')
|
||||||
|
|
||||||
|
def test_telegram_obeys_rate_limit(self):
|
||||||
|
self._setup_data("telegram", json.dumps({"id": 123}))
|
||||||
|
|
||||||
|
TokenBucket.objects.create(value="tg-123", tokens=0)
|
||||||
|
|
||||||
|
self.channel.notify(self.check)
|
||||||
|
n = Notification.objects.first()
|
||||||
|
self.assertEqual(n.error, "Rate limit exceeded")
|
||||||
|
|
||||||
@patch("hc.api.transports.requests.request")
|
@patch("hc.api.transports.requests.request")
|
||||||
def test_sms(self, mock_post):
|
def test_sms(self, mock_post):
|
||||||
self._setup_data("sms", "+1234567890")
|
self._setup_data("sms", "+1234567890")
|
||||||
|
@ -452,11 +452,18 @@ class Telegram(HttpTransport):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def send(cls, chat_id, text):
|
def send(cls, chat_id, text):
|
||||||
|
# Telegram.send is a separate method because it is also used in
|
||||||
|
# hc.front.views.telegram_bot to send invite links.
|
||||||
return cls.post(
|
return cls.post(
|
||||||
cls.SM, json={"chat_id": chat_id, "text": text, "parse_mode": "html"}
|
cls.SM, json={"chat_id": chat_id, "text": text, "parse_mode": "html"}
|
||||||
)
|
)
|
||||||
|
|
||||||
def notify(self, check):
|
def notify(self, check):
|
||||||
|
from hc.api.models import TokenBucket
|
||||||
|
|
||||||
|
if not TokenBucket.authorize_telegram(self.channel.telegram_id):
|
||||||
|
return "Rate limit exceeded"
|
||||||
|
|
||||||
text = tmpl("telegram_message.html", check=check)
|
text = tmpl("telegram_message.html", check=check)
|
||||||
return self.send(self.channel.telegram_id, text)
|
return self.send(self.channel.telegram_id, text)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user