2022-01-16 23:20:15 +00:00
import json
import boto3
import zlib
import base64
import datetime
from email . utils import parsedate
import os
def set_connection_header ( request , operation_name , * * kwargs ) :
request . headers [ ' Connection ' ] = ' keep-alive '
sns = boto3 . client ( " sns " , region_name = " us-east-1 " )
sns . meta . events . register ( ' request-created.sns ' , set_connection_header )
2022-05-17 22:41:52 +00:00
def check_fields_are_number ( field , telemetry ) :
if type ( telemetry [ field ] ) != float and type ( telemetry [ field ] ) != int :
return ( False , f " { field } should not be a float " )
return ( True , " " )
2022-01-16 23:20:15 +00:00
def telemetry_filter ( telemetry ) :
2022-05-17 22:41:52 +00:00
fields_to_check = [ " alt " , " lat " , " lon " ]
2023-07-06 09:46:46 +00:00
required_fields = [ " datetime " , " uploader_callsign " , " software_name " ] + fields_to_check
for field in required_fields :
if field not in telemetry :
return ( False , f " Missing { field } field " )
2022-05-17 22:41:52 +00:00
for field in fields_to_check :
field_check = check_fields_are_number ( field , telemetry )
if field_check [ 0 ] == False :
return field_check
2022-01-16 23:20:15 +00:00
if " dev " in telemetry :
return ( False , " All checks passed however payload contained dev flag so will not be uploaded to the database " )
return ( True , " " )
2023-07-06 09:46:46 +00:00
# Returns true for anything that should be hidden
def telemetry_hide_filter ( telemetry ) :
2023-07-07 02:08:26 +00:00
# Default Horus Binary callsigns
if telemetry [ " payload_callsign " ] in [ ' 4FSKTEST ' , ' 4FSKTEST-V2 ' ] :
2023-07-06 09:46:46 +00:00
return True
2023-07-07 02:08:26 +00:00
# Default pysondehub uploader callsign
if telemetry [ " uploader_callsign " ] in [ ' MYCALL ' ] :
return True
2023-07-06 09:46:46 +00:00
return False
2022-01-16 23:20:15 +00:00
def post ( payload ) :
sns . publish (
TopicArn = os . getenv ( " HAM_SNS_TOPIC " ) ,
Message = json . dumps ( payload )
)
def upload ( event , context ) :
if " isBase64Encoded " in event and event [ " isBase64Encoded " ] == True :
event [ " body " ] = base64 . b64decode ( event [ " body " ] )
if (
" content-encoding " in event [ " headers " ]
and event [ " headers " ] [ " content-encoding " ] == " gzip "
) :
event [ " body " ] = zlib . decompress ( event [ " body " ] , 16 + zlib . MAX_WBITS )
payloads = json . loads ( event [ " body " ] )
to_sns = [ ]
errors = [ ]
2023-08-02 04:25:16 +00:00
warnings = [ ]
2022-01-16 23:20:15 +00:00
for payload in payloads :
if " user-agent " in event [ " headers " ] :
event [ " time_server " ] = datetime . datetime . now ( ) . isoformat ( )
payload [ " user-agent " ] = event [ " headers " ] [ " user-agent " ]
payload [ " position " ] = f ' { payload [ " lat " ] } , { payload [ " lon " ] } '
2023-08-02 04:25:16 +00:00
payload [ " upload_time " ] = datetime . datetime . now ( ) . isoformat ( )
2022-01-16 23:20:15 +00:00
valid , error_message = telemetry_filter ( payload )
2023-07-06 09:46:46 +00:00
2023-08-02 04:25:16 +00:00
try :
_delta_time = (
datetime . datetime . now ( ) - datetime . datetime . fromisoformat ( payload [ " datetime " ] . replace ( " Z " , " " ) )
) . total_seconds ( )
except :
errors . append ( {
" error_message " : " Unable to parse datetime " ,
" payload " : payload
} )
future_time_threshold_seconds = 60
time_threshold_hours = 24
if _delta_time < - future_time_threshold_seconds :
payload [ " telemetry_hidden " ] = True
warnings . append ( {
" warning_message " : f " Payload reported time too far in the future. Either sonde time or system time is invalid. (Threshold: { future_time_threshold_seconds } seconds) " ,
" payload " : payload
} )
if " historical " not in payload or payload [ ' historical ' ] == False :
if abs ( _delta_time ) > ( 3600 * time_threshold_hours ) :
payload [ " telemetry_hidden " ] = True
warnings . append ( {
" warning_message " : f " Payload reported time too far from current UTC time. Either payload time or system time is invalid. (Threshold: { time_threshold_hours } hours) " ,
" payload " : payload
} )
2022-01-16 23:20:15 +00:00
if not valid :
errors . append ( {
" error_message " : error_message ,
" payload " : payload
} )
else :
2023-07-06 09:46:46 +00:00
# Apply hide field for anything that matches our filters
if telemetry_hide_filter ( payload ) :
payload [ " telemetry_hidden " ] = True
2022-01-16 23:20:15 +00:00
if " uploader_position " in payload :
if not payload [ " uploader_position " ] :
payload . pop ( " uploader_position " )
elif payload [ ' uploader_position ' ] [ 0 ] == None or payload [ ' uploader_position ' ] [ 1 ] == None :
payload . pop ( " uploader_position " )
else :
( payload [ " uploader_alt " ] , payload [ " uploader_position " ] ) = (
payload [ " uploader_position " ] [ 2 ] ,
f " { payload [ ' uploader_position ' ] [ 0 ] } , { payload [ ' uploader_position ' ] [ 1 ] } " ,
)
to_sns . append ( payload )
post ( to_sns )
2023-08-02 04:25:16 +00:00
return errors , warnings
2022-01-16 23:20:15 +00:00
def lambda_handler ( event , context ) :
try :
2023-08-02 04:25:16 +00:00
errors , warnings = upload ( event , context )
2022-01-16 23:20:15 +00:00
except zlib . error :
return { " statusCode " : 400 , " body " : " Could not decompress " }
except json . decoder . JSONDecodeError :
return { " statusCode " : 400 , " body " : " Not valid json " }
error_message = {
2023-08-02 04:25:16 +00:00
" message " : " some or all payloads could not be processed or have warnings " ,
" errors " : errors ,
" warnings " : warnings
2022-01-16 23:20:15 +00:00
}
2023-08-02 04:25:16 +00:00
if errors or warnings :
2022-01-16 23:20:15 +00:00
output = {
" statusCode " : 202 ,
" body " : json . dumps ( error_message ) ,
" headers " : {
" content-type " : " application/json "
}
}
print ( {
" statusCode " : 202 ,
" body " : error_message ,
" headers " : {
" content-type " : " application/json "
}
} )
return output
else :
return { " statusCode " : 200 , " body " : " ^v^ telm logged " }