Compare commits

...

3 Commits

Author SHA1 Message Date
Luca 30aaa91474 chore: update dependencies 2025-03-11 01:45:55 +01:00
Luca db3926316c feat(messaging): add seven.io backend 2025-03-11 01:45:19 +01:00
Luca b8dcb02ef1 style: speed up isort when `env` exists 2025-03-11 01:08:26 +01:00
3 changed files with 111 additions and 2 deletions
requirements.txtsetup.cfg
shiftregister/messaging/backends

View File

@ -2,7 +2,7 @@ amqp==5.1.1
asgiref==3.8.1
async-timeout==4.0.2
beautifulsoup4==4.12.3
billiard==4.2.0
billiard==4.2.1
celery==5.4.0
certifi==2021.10.8
charset-normalizer==2.0.12
@ -23,7 +23,7 @@ packaging==21.3
persisting-theory==1.0
phonenumbers==8.12.47
prompt-toolkit==3.0.29
psycopg2-binary==2.9.9
psycopg2-binary==2.9.10
pyparsing==3.0.8
pypng==0.20220715.0
python-dateutil==2.8.2

View File

@ -1,4 +1,5 @@
[isort]
extend_skip=env
line_length=88
profile=black
skip_gitignore=True

View File

@ -0,0 +1,108 @@
from datetime import datetime, timezone
import requests
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils.timezone import now
from ..exceptions import OutboundMessageError
from ..message import Message, MessageType
from .abc import Receiver as BaseReceiver
from .abc import Sender as BaseSender
__all__ = ("Receiver", "Sender")
BASE_URL = "https://gateway.seven.io/api"
RESPONSE_CODES = {
"100": "Sent",
"101": "Message could not be sent",
"201": "Invalid sender",
"202": "Invalid recipient",
"301": "Missing 'to' parameter",
"305": "Invalid 'text' parameter",
"401": "Parameter 'text' too long",
"402": "Message has already been sent",
"403": "Exceeded daily limit for recipient",
"500": "Insufficient balance",
"600": "Error while sending message",
"900": "Authentication failed",
"901": "Signature verification failed",
"902": "Access denied",
"903": "Request IP is not in allow list",
}
class Receiver(BaseReceiver):
fetch = None
def handle(self, data, webhook_event, webhook_timestamp, **kwargs):
if webhook_event != "sms_mo":
return
yield Message(
data["id"],
recipient=data["system"],
sender=data["sender"],
text=data["text"],
type=MessageType.INBOUND,
created_at=datetime.fromtimestamp(data["time"], timezone.utc),
)
class Sender(BaseSender):
def __init__(self):
for setting in ("sevenio_api_key", "sevenio_sender"):
try:
settings.SMS_SETTINGS[setting]
except KeyError:
raise ImproperlyConfigured(
f"'{setting}' must be set in SMS_SETTINGS for seven.io backend"
)
sender = settings.SMS_SETTINGS["sevenio_sender"]
sender_is_alnum = sender.isalnum()
sender_is_num = sender.isnumeric()
if (
not sender_is_alnum
or len(sender) > 16
or not sender_is_num
and len(sender) > 11
):
raise ImproperlyConfigured("invalid 'sevenio_sender' in SMS_SETTINGS")
self.client = requests.Session()
self.client.headers["Accept"] = "application/json"
self.client.headers["X-Api-Key"] = settings.SMS_SETTINGS["sevenio_api_key"]
self.sender = sender
def send(self, messages):
for message in messages:
data = {
"from": self.sender,
"label": message.key,
"text": message.text,
"to": message.recipient,
}
r = self.client.post(f"{BASE_URL}/sms", data=data)
r.raise_for_status()
resp = r.json()
code = resp["success"]
if code != "100":
raise OutboundMessageError(
RESPONSE_CODES.get(code, f"Unknown error: {code}")
)
for m in resp["messages"]:
if m["success"]:
yield Message(
m["label"],
recipient=m["recipient"],
sender=m["sender"],
text=m["text"],
type=MessageType.OUTBOUND,
created_at=now(),
)