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
|