Compare commits
No commits in common. "f03542b58444c4c0f3254742f97b54df13be1652" and "d9b5dfe5d60d1fa3d4c44d25c87e479ffc7f06b9" have entirely different histories.
f03542b584
...
d9b5dfe5d6
|
@ -161,6 +161,3 @@ cython_debug/
|
||||||
|
|
||||||
# E.g. copies of old dev database
|
# E.g. copies of old dev database
|
||||||
*.bkp
|
*.bkp
|
||||||
|
|
||||||
# Database dumps
|
|
||||||
/*.json
|
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
jq '[
|
|
||||||
JOIN(
|
|
||||||
INDEX(
|
|
||||||
.[] | select(
|
|
||||||
.model == "app.helper"
|
|
||||||
);
|
|
||||||
.pk | tostring
|
|
||||||
);
|
|
||||||
.[] | select(
|
|
||||||
.model == "feedback.feedback"
|
|
||||||
and .fields.next_year
|
|
||||||
);
|
|
||||||
.pk | tostring;
|
|
||||||
.[0].fields as $feedback
|
|
||||||
| .[1].fields as $helper
|
|
||||||
| {
|
|
||||||
"name": $helper.name,
|
|
||||||
"next_year": $feedback.next_year,
|
|
||||||
"phone": $helper.phone,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
]'
|
|
|
@ -1,129 +0,0 @@
|
||||||
import json
|
|
||||||
import pathlib
|
|
||||||
import sys
|
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
|
||||||
from django.template import Context, Template
|
|
||||||
from phonenumber_field.phonenumber import PhoneNumber
|
|
||||||
|
|
||||||
from shiftregister.app.models import Helper
|
|
||||||
from shiftregister.messaging import Message, MessageType
|
|
||||||
from shiftregister.messaging.outbound import send
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
help = (
|
|
||||||
"Notify a list of phone numbers excluding those already found in the database."
|
|
||||||
)
|
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
|
||||||
parser.add_argument(
|
|
||||||
"-n",
|
|
||||||
"--dry-run",
|
|
||||||
action="store_true",
|
|
||||||
help="print messages that would be sent, but do not actually send them",
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
|
||||||
"numbers", help="path to numbers list as JSON", type=pathlib.Path
|
|
||||||
)
|
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
|
||||||
with options["numbers"].open() as f:
|
|
||||||
numbers = json.load(f)
|
|
||||||
|
|
||||||
if not isinstance(numbers, list) or not all(
|
|
||||||
map(lambda o: isinstance(o, dict) and "phone" in o, numbers)
|
|
||||||
):
|
|
||||||
raise CommandError(
|
|
||||||
"JSON document must be an array of objects containing (at least) the key 'phone'"
|
|
||||||
)
|
|
||||||
|
|
||||||
template = Template(sys.stdin.read().strip())
|
|
||||||
|
|
||||||
numbers_count = len(numbers)
|
|
||||||
existing_numbers = set(Helper.objects.values_list("phone", flat=True))
|
|
||||||
numbers = list(
|
|
||||||
filter(
|
|
||||||
lambda o: PhoneNumber.from_string(o["phone"]) not in existing_numbers,
|
|
||||||
numbers,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
self.stderr.write(
|
|
||||||
self.style.WARNING(
|
|
||||||
f"{numbers_count-len(numbers)} already found in database"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
messages = (
|
|
||||||
Message(
|
|
||||||
i,
|
|
||||||
recipient=o["phone"],
|
|
||||||
text=template.render(Context(o)),
|
|
||||||
type=MessageType.OUTBOUND,
|
|
||||||
)
|
|
||||||
for i, o in enumerate(numbers)
|
|
||||||
)
|
|
||||||
|
|
||||||
if options["dry_run"]:
|
|
||||||
messages = list(messages)
|
|
||||||
|
|
||||||
self.stderr.write(
|
|
||||||
self.style.WARNING(f"would send {len(messages)} message(s)")
|
|
||||||
)
|
|
||||||
self.stderr.write()
|
|
||||||
|
|
||||||
for message in messages:
|
|
||||||
if int(message.key) != 0:
|
|
||||||
self.stderr.write()
|
|
||||||
self.stderr.write(self.style.WARNING("---"))
|
|
||||||
self.stderr.write()
|
|
||||||
|
|
||||||
self.stderr.write(self.style.WARNING(f"to: {message.recipient}"))
|
|
||||||
self.stderr.write(self.style.WARNING(f"length: {len(message.text)}"))
|
|
||||||
self.stderr.write()
|
|
||||||
self.stderr.write(self.style.WARNING(message.text))
|
|
||||||
|
|
||||||
if len(messages) > 0:
|
|
||||||
self.stderr.write()
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
self.stderr.write()
|
|
||||||
|
|
||||||
all_messages = set(messages)
|
|
||||||
sent_messages = set()
|
|
||||||
error = None
|
|
||||||
try:
|
|
||||||
for message in send(all_messages):
|
|
||||||
sent_messages.add(message)
|
|
||||||
|
|
||||||
self.stderr.write()
|
|
||||||
self.stderr.write(
|
|
||||||
self.style.SUCCESS(
|
|
||||||
f"\r{str(len(sent_messages)).rjust(len(str(len(all_messages))))} / {len(all_messages)}"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
self.stderr.write()
|
|
||||||
except Exception as e:
|
|
||||||
error = e
|
|
||||||
|
|
||||||
self.stderr.write(
|
|
||||||
self.style.WARNING(f"sent {len(sent_messages)} out of {len(all_messages)}")
|
|
||||||
)
|
|
||||||
self.stderr.write(
|
|
||||||
self.style.WARNING(f"numbers to which messages could not be sent:")
|
|
||||||
)
|
|
||||||
self.stdout.write(
|
|
||||||
self.style.ERROR(
|
|
||||||
json.dumps(
|
|
||||||
[
|
|
||||||
{"phone": message.recipient}
|
|
||||||
for message in all_messages - sent_messages
|
|
||||||
]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if error:
|
|
||||||
raise CommandError(error)
|
|
|
@ -41,8 +41,7 @@ class WebhookReceiver(BaseReceiver):
|
||||||
if not sender:
|
if not sender:
|
||||||
raise ValueError("message has no sender")
|
raise ValueError("message has no sender")
|
||||||
|
|
||||||
logger.info(
|
logging.getLogger("django.server").info(
|
||||||
f"received sms via webhook\nkey: {key}\nfrom: {sender}\nadditional fields: {pformat(kwargs)}\n\n{text}"
|
f"received sms via webhook\nkey: {key}\nfrom: {sender}\nadditional fields: {pformat(kwargs)}\n\n{text}"
|
||||||
)
|
)
|
||||||
|
|
||||||
yield Message(key, sender=sender, text=text, type=MessageType.INBOUND)
|
yield Message(key, sender=sender, text=text, type=MessageType.INBOUND)
|
||||||
|
|
|
@ -45,10 +45,11 @@ class Message:
|
||||||
self.created_at = created_at
|
self.created_at = created_at
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if isinstance(other, Message):
|
if other is None:
|
||||||
return self.key == other.key
|
return False
|
||||||
|
elif isinstance(other, Message):
|
||||||
|
other = other.key
|
||||||
|
else:
|
||||||
|
other = str(other)
|
||||||
|
|
||||||
return NotImplemented
|
return self.key == other
|
||||||
|
|
||||||
def __hash__(self):
|
|
||||||
return hash(self.key)
|
|
||||||
|
|
|
@ -240,23 +240,3 @@ SMS_OUTBOUND_BACKEND = ".".join(
|
||||||
)
|
)
|
||||||
SMS_SETTINGS = env.dict("SMS_SETTINGS", default={})
|
SMS_SETTINGS = env.dict("SMS_SETTINGS", default={})
|
||||||
SMS_WEBHOOK_SECRET = env("SMS_WEBHOOK_SECRET", default=None)
|
SMS_WEBHOOK_SECRET = env("SMS_WEBHOOK_SECRET", default=None)
|
||||||
|
|
||||||
# Logging
|
|
||||||
# https://docs.djangoproject.com/en/5.0/howto/logging/#customize-logging-configuration
|
|
||||||
|
|
||||||
LOGGING = {
|
|
||||||
"version": 1,
|
|
||||||
"disable_existing_loggers": False,
|
|
||||||
"handlers": {
|
|
||||||
"console": {
|
|
||||||
"level": "DEBUG" if DEBUG else "WARNING",
|
|
||||||
"class": "logging.StreamHandler",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"loggers": {
|
|
||||||
"shiftregister": {
|
|
||||||
"level": "DEBUG" if DEBUG else "WARNING",
|
|
||||||
"handlers": ["console"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue