Browse Source

Enable Duplicate APRS Message Checking

This will avoid sending multiple SMS messages when an APRS user has no received an ack for the sent messages.
aprs-duplicate-filtering
Mike 2 years ago
committed by GitHub
parent
commit
b76f7832ee
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 88 additions and 14 deletions
  1. +88
    -14
      sms.py

+ 88
- 14
sms.py View File

@ -2,6 +2,7 @@ import socket
import re
from flask import Flask, request, jsonify
from twilio.rest import Client
import time
app = Flask(__name__)
@ -19,7 +20,7 @@ TWILIO_PHONE_NUMBER = '+NUMBER' # Your Twilio phone number
# APRS credentials
APRS_CALLSIGN = 'CALLSIGN'
APRS_PASSCODE = 'PASSCODE'
APRS_SERVER = 'roate.aprs2.net'
APRS_SERVER = 'rotate.aprs2.net'
APRS_PORT = 14580
# Initialize the socket
@ -28,6 +29,17 @@ aprs_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Dictionary to store the last received APRS message ID for each user
user_last_message_id = {}
processed_message_ids = set()
# Outside the main loop, initialize a dictionary to store message history
received_aprs_messages = {}
received_acks = {}
RETRY_INTERVAL = 60 # Adjust this as needed
MAX_RETRIES = 3 # Adjust this as needed
def send_ack_message(sender, message_id):
ack_message = 'ack{}'.format(message_id)
@ -49,6 +61,7 @@ def send_rej_message(sender, message_id):
print("Sent REJ to {}: {}".format(sender, rej_message))
print("Outgoing REJ packet: {}".format(rej_packet.decode()))
def send_sms(twilio_phone_number, to_phone_number, from_callsign, body_message):
# Initialize the Twilio client
client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
@ -68,14 +81,14 @@ def send_sms(twilio_phone_number, to_phone_number, from_callsign, body_message):
def format_aprs_packet(callsign, message):
sender_length = len(callsign)
spaces_after_sender = ' ' * max(0, 9 - sender_length)
aprs_packet_format = '{}>NA7Q::{}{}:{}\r\n'.format(APRS_CALLSIGN, callsign, spaces_after_sender, message)
spaces_after_sender = ' ' * max(1, 9 - sender_length)
aprs_packet_format = '{}>APRS::{}{}:{}\r\n'.format(APRS_CALLSIGN, callsign, spaces_after_sender, message)
return aprs_packet_format
# Dictionary to store the mapping of aliases (callsigns) to phone numbers
alias_map = {
'alias1': '1234567890', # Replace 'alias1' with the desired alias and '1234567890' with the corresponding phone number.
'alias2': '0987654321', # Add more entries as needed for other aliases and phone numbers.
'laura': '5032985265', # Replace 'alias1' with the desired alias and '1234567890' with the corresponding phone number.
'alias2': '9876543210', # Add more entries as needed for other aliases and phone numbers.
# Add more entries as needed.
}
@ -99,7 +112,7 @@ def receive_sms():
if len(parts) == 2:
# Extract the 10-digit phone number from the sender's phone number
sender_phone_number = from_phone_number[-10:]
callsign = parts[0][1:].upper() # Convert to uppercase
callsign = parts[0][1:].upper() #Convert to UPPERCASE
aprs_message = parts[1]
# Get the last APRS message ID sent to this user
@ -120,8 +133,34 @@ def receive_sms():
# Format the APRS packet and send it to the APRS server
aprs_packet = format_aprs_packet(callsign, "@{} {}".format(sender_phone_number, aprs_message + "{" + str(last_message_id)))
aprs_socket.sendall(aprs_packet.encode())
#print(user_last_message_id)
#print(last_message_id)
print("Sent APRS message to {}: {}".format(callsign, aprs_message))
print("Outgoing APRS packet: {}".format(aprs_packet.strip()))
time.sleep(5) # Sleeping here allows time for incoming ack before retry
# Retry sending the message if ACK is not received
retry_count = 0
ack_received = False # Flag to track whether ACK is received
while retry_count < MAX_RETRIES and not ack_received:
if str(last_message_id) in received_acks.get(callsign, set()):
print("Message ACK received. No further retries needed.")
ack_received = True
# Reset retry count and remove the ACK ID
retry_count = 0
received_acks.get(callsign, set()).discard(str(last_message_id))
else:
print("ACK not received. Retrying in {} seconds.".format(RETRY_INTERVAL))
aprs_socket.sendall(aprs_packet.encode())
retry_count += 1
time.sleep(RETRY_INTERVAL) # Pause for the defined interval
if ack_received:
print("ACK received during retries. No further retries needed.")
elif retry_count >= MAX_RETRIES:
print("Max retries reached. No ACK received for the message.")
return jsonify({'status': 'success'})
else:
@ -165,15 +204,25 @@ def receive_aprs_messages():
if len(parts) >= 2:
from_callsign = parts[0].split('>')[0].strip()
message_text = ':'.join(parts[1:]).strip()
# Extract and process ACK ID if present
if "ack" in message_text:
parts = message_text.split("ack", 1)
if len(parts) == 2 and parts[1].isdigit():
ack_id = parts[1]
process_ack_id(from_callsign, ack_id)
# End RXd ACK ID for MSG Retries
# Check if the message contains "{"
if "{" in message_text:
message_id = message_text.split('{')[1].strip('}')
# Remove the first 11 characters from the message to exclude the "Callsign :" prefix
verbose_message = message_text[11:].split('{')[0].strip()
# If private_mode is enabled, check against allowed_callsigns; otherwise, process normally
# Inside the receive_aprs_messages function
if private_mode:
# Use regular expression to match main callsign and accept all SSIDs
callsign_pattern = re.compile(r'^({})(-\d+)?$'.format('|'.join(map(re.escape, allowed_callsigns))))
@ -186,13 +235,16 @@ def receive_aprs_messages():
print("From: {}".format(from_callsign))
print("Message: {}".format(verbose_message))
print("Message ID: {}".format(message_id))
print(user_last_message_id)
# Check if the verbose message contains the desired format with a number or an alias
pattern = r'@(\d{10}|\w+) (.+)'
match = re.match(pattern, verbose_message)
# Send ACK
send_ack_message(from_callsign, message_id)
send_ack_message(from_callsign, message_id)
if match:
recipient = match.group(1)
@ -212,12 +264,32 @@ def receive_aprs_messages():
last_message_id = user_last_message_id.get(from_callsign, 0)
last_message_id += 1
user_last_message_id[from_callsign] = last_message_id
# Check for duplicate messages
if (aprs_message, message_id) in received_aprs_messages.get(from_callsign, set()):
print("Duplicate message detected. Skipping SMS sending.")
send_ack_message(from_callsign, message_id)
else:
# Mark the message as received
received_aprs_messages.setdefault(from_callsign, set()).add((aprs_message, message_id))
# Send SMS
send_sms(TWILIO_PHONE_NUMBER, phone_number, from_callsign, aprs_message)
# Add this line to mark the message ID as processed
processed_message_ids.add(message_id)
# else:
print("Recipient not found in alias map or not a 10-digit number: {}".format(recipient))
# Extract and process ACK ID if present
if message_text.startswith("ack"):
ack_id = message_text[3:] # Remove the "ack" prefix
process_ack_id(from_callsign, ack_id)
else:
print("Recipient not found in alias map or not a 10-digit number: {}".format(recipient))
pass
# Send ACK
@ -231,15 +303,17 @@ def receive_aprs_messages():
# Close the socket connection when done
aprs_socket.close()
#Implementation for ack check with Message Retries #TODO
def process_ack_id(from_callsign, ack_id):
print("Received ACK from {}: {}".format(from_callsign, ack_id))
received_acks.setdefault(from_callsign, set()).add(ack_id)
if __name__ == '__main__':
print("APRS bot is running. Waiting for APRS messages...")
# Run the Flask web application in a separate thread to handle incoming SMS messages
from threading import Thread
webhook_thread = Thread(target=app.run, kwargs={'host': '0.0.0.0', 'port': 5000})
webhook_thread = Thread(target=app.run, kwargs={'host': '23.138.32.57', 'port': 5000})
webhook_thread.start()
# Start listening for APRS messages


Loading…
Cancel
Save