| @ -0,0 +1,252 @@ | |||
| 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 | |||