import socket import smtplib import re import time import requests from email.mime.text import MIMEText from imapclient import IMAPClient import email from email.mime.multipart import MIMEMultipart import threading # APRS credentials APRS_CALLSIGN = 'CALL' APRS_PASSCODE = 'PASS' APRS_SERVER = 'rotate.aprs2.net' APRS_PORT = 14580 # Initialize the socket aprs_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) aprs_socket_lock = threading.Lock() # Email configuration SMTP_SERVER = 'smtp.gmail.com' SMTP_PORT = 587 IMAP_SERVER = 'imap.gmail.com' IMAP_PORT = 993 SENDER_EMAIL = 'EMAIL@gmail.com' SENDER_PASSWORD = 'APP_PASSCODE' # Alias dictionary alias_email_map = { 'mike': 'mike@gmail.com', 'john': 'john@gmail.com', 'larry': 'larry@yahoo.com', 'kevin': 'kevin@gmail.com', # Add more aliases and their corresponding email addresses here } # Optimization parameters CHECK_INTERVAL = 0 # Sleep interval between email checks (in seconds) # Message counter for numbering APRS messages message_counter = 1 def send_aprs_message(recipient_call, message_body): global message_counter spaces_after_recipient = ' ' * max(0, 9 - len(recipient_call)) aprs_message = '{}>APRS::{}{}:{}{{{}\r'.format(APRS_CALLSIGN, recipient_call, spaces_after_recipient, message_body, message_counter) message_packet = aprs_message.encode() with aprs_socket_lock: # Acquire lock before sending APRS message aprs_socket.sendall(message_packet) print(message_packet) print(aprs_message) print("Sent APRS message to {}: {}".format(recipient_call, message_body)) print("Outgoing APRS packet: {}".format(aprs_message)) # Exclude the closing "}" message_counter += 1 def send_ack_message(sender, message_id): if message_id.isdigit(): ack_message = 'ack{}'.format(message_id) sender_length = len(sender) spaces_after_sender = ' ' * max(0, 9 - sender_length) ack_packet_format = '{}>APRS::{}{}:{}\r\n'.format(APRS_CALLSIGN, sender, spaces_after_sender, ack_message) ack_packet = ack_packet_format.encode() aprs_socket.sendall(ack_packet) print("Sent ACK to {}: {}".format(sender, ack_message)) print("Outgoing ACK packet: {}".format(ack_packet.decode())) def get_email_body(email_message): if email_message.is_multipart(): # If the email is multipart, extract the text content for part in email_message.walk(): content_type = part.get_content_type() if content_type == "text/plain": payload = part.get_payload(decode=True).decode() return payload.strip() else: # If the email is plain text, return the body return email_message.get_payload().strip() return None def check_emails(): try: with IMAPClient(IMAP_SERVER, port=IMAP_PORT) as client: client.login(SENDER_EMAIL, SENDER_PASSWORD) client.select_folder('INBOX') messages = client.search(['UNSEEN']) if messages: print('Received', len(messages), 'new email(s)') for msgid, message_data in client.fetch(messages, ['RFC822']).items(): raw_email = message_data[b'RFC822'] email_message = email.message_from_bytes(raw_email) sender_match = re.search(r'<([^>]+)>', email_message['From']) if sender_match: sender = sender_match.group(1) else: sender = email_message['From'] subject = email_message['Subject'] email_body = get_email_body(email_message) aprs_in_subject = 'email' in subject.lower() print("\nReceived an email:") print("From:", sender) print("Subject:", subject) print("Body:", email_body) print("Contains email:", aprs_in_subject) if aprs_in_subject: # Use regular expression to extract the APRS call and message match = re.search(r'@([A-Z0-9-]+)\s+(.+)', email_body, re.IGNORECASE) if match: recipient_call = match.group(1) message_body = match.group(2) print("APRS Callsign-SSID found in email body:", recipient_call) print("Message in email body:", message_body) # Prepend sender's email to the message message_body_with_sender = "@{} {}".format(sender, message_body) send_aprs_message(recipient_call, message_body_with_sender) print("Sent APRS message to {}: {}".format(recipient_call, message_body_with_sender)) else: print("No valid APRS Callsign-SSID and message found in email body.") # Mark the email as read client.set_flags(msgid, [b'\\Seen']) except Exception as e: print('An error occurred:', e) def receive_aprs_messages(): # Connect to the APRS server aprs_socket.connect((APRS_SERVER, APRS_PORT)) print("Connected to APRS server with callsign: {}".format(APRS_CALLSIGN)) # Send login information with APRS callsign and passcode login_str = 'user {} pass {} vers APRS-Email-Bot 1.0\r\n'.format(APRS_CALLSIGN, APRS_PASSCODE) aprs_socket.sendall(login_str.encode()) print("Sent login information.") buffer = "" try: while True: data = aprs_socket.recv(1024) if not data: break # Add received data to the buffer buffer += data.decode() # Split buffer into lines lines = buffer.split('\n') # Process each line for line in lines[:-1]: if line.startswith('#'): continue # Process APRS message print("Received raw APRS packet: {}".format(line.strip())) parts = line.strip().split(':') if len(parts) >= 2: from_callsign = parts[0].split('>')[0].strip() message_text = ':'.join(parts[1:]).strip() # 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() # Check if the message starts with "@" to handle both email and alias if verbose_message.startswith('@'): email_match = re.match(r'@([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,4})\s+(.+)', verbose_message) if email_match: recipient_email = email_match.group(1) message_body = email_match.group(2) send_email(from_callsign, message_body, recipient_email) else: alias = verbose_message[1:].split()[0] recipient_email = alias_email_map.get(alias) if recipient_email: message_body = verbose_message[len(alias)+2:] send_email(from_callsign, message_body, recipient_email) else: print("Alias '{}' not found in the alias_email_map. Email not sent.".format(alias)) send_ack_message(from_callsign, message_id) # The last line might be an incomplete packet, so keep it in the buffer buffer = lines[-1] except Exception as e: print("Error receiving APRS messages: {}".format(e)) finally: # Close the socket connection when done aprs_socket.close() def send_email(subject, body, recipient_email): message = MIMEText(body) message['From'] = SENDER_EMAIL message['To'] = recipient_email message['Subject'] = subject try: server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT) server.starttls() server.login(SENDER_EMAIL, SENDER_PASSWORD) server.sendmail(SENDER_EMAIL, recipient_email, message.as_string()) server.quit() print('Email sent successfully!') except smtplib.SMTPException as e: print('Email could not be sent:', e) def listen_emails(): while True: check_emails() time.sleep(CHECK_INTERVAL) def start_email_listener(): email_thread = threading.Thread(target=listen_emails) email_thread.daemon = True email_thread.start() print('Email listener started.') def start_aprs_receiver(): aprs_thread = threading.Thread(target=receive_aprs_messages) aprs_thread.daemon = True aprs_thread.start() print('APRS receiver started.') if __name__ == '__main__': print("APRS bot is running. Waiting for APRS messages...") start_email_listener() receive_aprs_messages() start_aprs_receiver() # Start the APRS receiver thread