anastasis

Credential backup and recovery protocol and service
Log | Files | Refs | Submodules | README | LICENSE

commit 07c013863b82f1086456eab618c86ebbd9d9b6e4
parent e8cfd6adc6bf27ddb32f8ea35445c3415057a0b5
Author: Christian Grothoff <grothoff@gnunet.org>
Date:   Wed, 22 Oct 2025 14:55:38 +0200

update SMS transmission script to poll for status change

Diffstat:
Asrc/authorization/anastasis-authorization-sms-clicksend.sh | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/authorization/anastasis-authorization-sms.sh | 155++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 278 insertions(+), 16 deletions(-)

diff --git a/src/authorization/anastasis-authorization-sms-clicksend.sh b/src/authorization/anastasis-authorization-sms-clicksend.sh @@ -0,0 +1,139 @@ +#!/bin/bash +# This file is in the public domain. +# Send an SMS using ClickSend API + +set -eu + +# Check shared secrets +if [ -x "$CLICKSEND_USERNAME" ] +then + echo "CLICKSEND_USERNAME not set in environment" + exit 1 +fi +if [ -x "$CLICKSEND_API_KEY" ] +then + echo "CLICKSEND_API_KEY not set in environment" + exit 1 +fi + +if [ $# -ne 1 ]; then + echo "Usage: $0 <phone_number>" 1>&2 + exit 1 +fi + +PHONE_NUMBER="$1" +MESSAGE=$(cat -) + +TMPFILE=$(mktemp /tmp/clicksend-sms-logging-XXXXXX) + +RESPONSE=$(curl --silent --show-error --fail \ + --url https://rest.clicksend.com/v3/sms/send \ + --request POST \ + --header 'Content-Type: application/json' \ + --user "$CLICKSEND_USERNAME:$CLICKSEND_API_KEY" \ + --data "{ + \"messages\": [{ + \"source\": \"bash-script\", + \"to\": \"$PHONE_NUMBER\", + \"body\": \"$MESSAGE\" + }] + }") + +echo "$RESPONSE" > "$TMPFILE" + +RESPONSE_CODE=$(echo "$RESPONSE" | jq -r '.response_code') + +if [ "$RESPONSE_CODE" != "SUCCESS" ]; +then + echo "Failed to send message, got response code $RESPONSE_CODE." 1>&2 + exit 1 +fi + +MESSAGE_ID=$(echo "$RESPONSE" | jq -r '.data.messages[0].message_id') + +if [ "$MESSAGE_ID" == "null" ]; +then + echo "Failed to retrieve message ID." 1>&2 + exit 1 +fi + +MESSAGE_STATUS=$(echo "$RESPONSE" | jq -r '.data.messages[0].status') + +if [ "$MESSAGE_STATUS" == "SUCCESS" ]; +then + echo "Message delivered successfully." 1>&2 + exit 0 +fi + +MAX_ITERATIONS=12 + +# Poll message status +echo "Polling message status (message_id: $MESSAGE_ID)..." 1>&2 +for N in $(seq 1 "$MAX_ITERATIONS") +do + STATUS_RESPONSE=$(curl --silent --show-error --fail \ + --url "https://rest.clicksend.com/v3/sms/receipts/$MESSAGE_ID" \ + --user "$CLICKSEND_USERNAME:$CLICKSEND_API_KEY") + + echo "$STATUS_RESPONSE" >> "$TMPFILE" + + RESPONSE_CODE=$(echo "$RESPONSE" | jq -r '.response_code') + + if [ "$RESPONSE_CODE" != "SUCCESS" ]; + then + echo "Failed to get message status, assuming failure." 1>&2 + exit 1 + fi + + STATUS_CODE=$(echo "$STATUS_RESPONSE" | jq -r '.data.status_code') + STATUS_TEXT=$(echo "$STATUS_RESPONSE" | jq -r '.data.status_text') + STATUS=$(echo "$STATUS_TEXT" | awk --field-separator ':' '{print $1}') + + case "$STATUS_CODE" in + "200") + case "$STATUS" in + "Success"|"Sent") + # Message sent to the network for delivery, wait a bit + sleep 1 + ;; + "Queued"|"Scheduled") + # queued for delivery, sleep a bit longer + sleep 10 + ;; + "WaitApproval") + # Human in the loop (strange), sleep even longer + sleep 120 + ;; + *) + # Unexpected status, keep trying + sleep 5 + ;; + esac + ;; + "201") + # Message delivered to the handset + echo "Message delivered successfully." 1>&2 + exit 0 + ;; + "300") + # Temporary network error, clicksend will retry automatically, sleep a bit + sleep 20 + ;; + "301") + # Delivery failed + echo "Message delivery failed: $DESCRIPTION" 1>&2 + exit 1 + ;; + "FAILED"|"INVALID_RECIPIENT") + exit 1 + ;; + *) + sleep 5 + ;; + esac +done + +echo "Unclear message delivery status $STATUS_CODE ($DESCRIPTION) after $MAX_ITERATIONS iterations. Assuming failure." 1>&2 +exit 1 + + diff --git a/src/authorization/anastasis-authorization-sms.sh b/src/authorization/anastasis-authorization-sms.sh @@ -1,31 +1,154 @@ #!/bin/bash # This file is in the public domain. +# Send an SMS using Telesign API set -eu # Check shared secrets if [ -x "$TELESIGN_AUTH_TOKEN" ] then - echo "TELESIGN_AUTH_TOKEN not sent in environment" + echo "TELESIGN_AUTH_TOKEN not set in environment" exit 1 fi +if [ $# -ne 1 ]; then + echo "Usage: $0 <phone_number>" 1>&2 + exit 1 +fi + +PHONE_NUMBER="$1" MESSAGE=$(cat -) -TMPFILE=$(mktemp /tmp/sms-loggingXXXXXX) -STATUS=$(curl --request POST \ - --url https://rest-api.telesign.com/v1/messaging \ - --header 'authorization: Basic $TELESIGN_AUTH_TOKEN' \ - --header 'content-type: application/x-www-form-urlencoded' \ - --data account_livecycle_event=transact \ - --data "message=$MESSAGE" \ - --data message_type=OTP \ - --data "phone_number=$1" \ - -w "%{http_code}" -s -o $TMPFILE) -case $STATUS in - 200|203|250|290|291|295) - exit 0; + +TMPFILE=$(mktemp /tmp/telesign-sms-logging-XXXXXX) + +RESPONSE=$(curl --silent --show-error --fail \ + --url https://rest-api.telesign.com/v1/messaging \ + --request POST \ + --header "Authorization: Basic $TELESIGN_AUTH_TOKEN" \ + --header "Content-Type: application/x-www-form-urlencoded" \ + --data account_livecycle_event=transact \ + --data "phone_number=$PHONE_NUMBER" \ + --data-urlencode "message=$MESSAGE" \ + --data "message_type=OTP") + +echo "$RESPONSE" > "$TMPFILE" +REFERENCE_ID=$(jq -r '.reference_id' "$TMPFILE") + +if [ "$REFERENCE_ID" == "null" ]; +then + echo "Failed to retrieve reference ID." 1>&2 + exit 1 +fi + +STATUS_CODE=$(echo "$RESPONSE" | jq -r '.status.code') + +case "$STATUS_CODE" in + "200") + # Delivered to headset. Should basically never happen here. + exit 0 ;; - *) - exit 1; + "203"|"292"|"295") + # Delivered to gateway + sleep 2 + ;; + "207"|"211"|"220"|"221"|"222"|"231"|"237"|"238") + # Failure to deliver (hard) + echo "Could not deliver" 1>&2 + exit 1 ;; + "210") + # Temporary phone error + ;; + "250") + # Final status unknown + echo "Final status unknown, assuming success" 1>&2 + exit 0 + ;; + "290") + # Message in progress, go into loop below + sleep 2 + ;; + "502"|"503"|"504"|"505"|"506"|"507"|"508"|"509"|"510"|"511"|"512"|"513"|"514"|"515"|"517"|"520"|"521") + echo "Carrier problem ($STATUS_CODE)" 1>&2 + exit 1 + ;; + "10000") + # Internal error at telesign... + echo "Telesign internal error" 1>&2 + exit 1 + ;; + "10019"|"10020") + # Rate limit exceeded. Treating as hard failure for now. + echo "Rate limit exceeded" 1>&2 + exit 1 + ;; + *) + # Many possible status codes for failure... + echo "Message delivery failed: $STATUS_CODE" 1>&2 + exit 1 + ;; esac + +MAX_ITERATIONS=12 + +# Poll for message status +echo "Polling message status (reference_id: $REFERENCE_ID)..." 1>&2 +for N in $(seq 1 "$MAX_ITERATIONS") +do + STATUS_RESPONSE=$(curl --silent --show-error --fail \ + --url "https://rest-api.telesign.com/v1/messaging/$REFERENCE_ID" \ + --header "Authorization: Basic $TELESIGN_AUTH_TOKEN") + + echo "$STATUS_RESPONSE" >> "$TMPFILE" + + STATUS_CODE=$(echo "$STATUS_RESPONSE" | jq -r '.status.code') + DESCRIPTION=$(echo "$STATUS_RESPONSE" | jq -r '.status.description') + + case "$STATUS_CODE" in + "200") + # Delivered to headset. Great! + echo "Delivered to headset" 1>&2 + exit 0 + ;; + "203"|"290"|"292"|"295") + # Delivered to gateway, wait a bit for an update + sleep 2 + ;; + "210") + # Temporary phone error + sleep 15 + ;; + "207"|"211"|"220"|"221"|"222"|"231"|"237"|"238") + # Failure to deliver (hard) + echo "Could not deliver" 1>&2 + exit 1 + ;; + "250") + # Final status unknown + echo "Final status unknown, assuming success" 1>&2 + exit 0 + ;; + "502"|"503"|"504"|"505"|"506"|"507"|"508"|"509"|"510"|"511"|"512"|"513"|"514"|"515"|"517"|"520"|"521") + echo "Carrier problem ($STATUS_CODE)" 1>&2 + exit 1 + ;; + "10000") + # Internal error at telesign... + echo "Telesign internal error" 1>&2 + exit 1 + ;; + "10019"|"10020") + # Rate limit exceeded. Treating as hard failure for now. + echo "Rate limit exceeded" 1>&2 + exit 1 + ;; + *) + # Many possible status codes for failure... + echo "Message delivery failed: $STATUS_CODE" 1>&2 + exit 1 + ;; + esac +done + +echo "Unclear message delivery status $STATUS_CODE ($DESCRIPTION) after $MAX_ITERATIONS iterations. Assuming failure." 1>&2 exit 1 +