1
0
mirror of https://github.com/craigerl/aprsd.git synced 2025-06-17 05:42:36 -04:00

Merge pull request #1 from hemna/master

Added standard python main()
This commit is contained in:
Craig Lamparter 2018-11-21 11:58:56 -08:00 committed by GitHub
commit 26ca8563f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

332
aprsd.py
View File

@ -8,7 +8,7 @@
# APRS messages: # APRS messages:
# l(ocation) = descriptive location of calling station # l(ocation) = descriptive location of calling station
# w(eather) = temp, (hi/low) forecast, later forecast # w(eather) = temp, (hi/low) forecast, later forecast
# t(ime) = respond with the current time # t(ime) = respond with the current time
# f(ortune) = respond with a short fortune # f(ortune) = respond with a short fortune
# -email_addr email text = send an email # -email_addr email text = send an email
# -2 = display the last 2 emails received # -2 = display the last 2 emails received
@ -33,7 +33,7 @@ from email.mime.text import MIMEText
import subprocess import subprocess
import datetime import datetime
import calendar import calendar
from imapclient import IMAPClient, SEEN from imapclient import IMAPClient, SEEN
import email import email
import threading import threading
import signal import signal
@ -114,8 +114,8 @@ def resend_email(count):
today = str(day) + "-" + month + "-" + str(year) today = str(day) + "-" + month + "-" + str(year)
global shortcuts global shortcuts
shortcuts_inverted = dict([[v,k] for k,v in shortcuts.items()]) # swap key/value shortcuts_inverted = dict([[v,k] for k,v in shortcuts.items()]) # swap key/value
server = IMAPClient('imap.yourdomain.com', use_uid=True) server = IMAPClient('imap.yourdomain.com', use_uid=True)
server.login('KM6XXX@yourdomain.org', 'yourpassword') server.login('KM6XXX@yourdomain.org', 'yourpassword')
select_info = server.select_folder('INBOX') select_info = server.select_folder('INBOX')
@ -158,18 +158,18 @@ def check_email_thread():
threading.Timer(55, check_email_thread).start() # how do we skip first run? threading.Timer(55, check_email_thread).start() # how do we skip first run?
global shortcuts global shortcuts
shortcuts_inverted = dict([[v,k] for k,v in shortcuts.items()]) # swap key/value shortcuts_inverted = dict([[v,k] for k,v in shortcuts.items()]) # swap key/value
date = datetime.datetime.now() date = datetime.datetime.now()
month = date.strftime("%B")[:3] # Nov, Mar, Apr month = date.strftime("%B")[:3] # Nov, Mar, Apr
day = date.day day = date.day
year = date.year year = date.year
today = str(day) + "-" + month + "-" + str(year) today = str(day) + "-" + month + "-" + str(year)
server = IMAPClient('imap.yourdomain.com', use_uid=True) server = IMAPClient('imap.yourdomain.com', use_uid=True)
server.login('KM6XXX@yourdomain.org', 'yourpassword') server.login('KM6XXX@yourdomain.org', 'yourpassword')
select_info = server.select_folder('INBOX') select_info = server.select_folder('INBOX')
messages = server.search(['SINCE', today]) messages = server.search(['SINCE', today])
#print("%d messages received today" % len(messages)) #print("%d messages received today" % len(messages))
@ -226,20 +226,20 @@ def send_message_thread(tocall, message, this_message_number, retry_count):
global ack_dict global ack_dict
line = USER + ">APRS::" + tocall + ":" + message + "{" + str(this_message_number) + "\n" line = USER + ">APRS::" + tocall + ":" + message + "{" + str(this_message_number) + "\n"
for i in range(retry_count, 0, -1): for i in range(retry_count, 0, -1):
print "DEBUG: send_message_thread msg:ack combos are: " print "DEBUG: send_message_thread msg:ack combos are: "
pprint.pprint(ack_dict) pprint.pprint(ack_dict)
if ack_dict[this_message_number] != 1: if ack_dict[this_message_number] != 1:
print "Sending message_______________ " + str(this_message_number) + "(Tx" + str(i) + ")" print "Sending message_______________ " + str(this_message_number) + "(Tx" + str(i) + ")"
print "Raw : " + line, print "Raw : " + line,
print "To : " + tocall print "To : " + tocall
print "Message : " + message print "Message : " + message
tn.write(line) tn.write(line)
sleeptime = (retry_count - i + 1) * 31 # decaying repeats, 31 to 93 second intervals sleeptime = (retry_count - i + 1) * 31 # decaying repeats, 31 to 93 second intervals
time.sleep(sleeptime) time.sleep(sleeptime)
else: else:
break break
return return
### end send_message_thread ### end send_message_thread
def send_message(tocall, message): def send_message(tocall, message):
@ -248,7 +248,7 @@ def send_message(tocall, message):
retry_count = 3 retry_count = 3
if message_number > 98: # global if message_number > 98: # global
message_number = 0 message_number = 0
message_number += 1 message_number += 1
if len(ack_dict) > 90: # empty ack dict if it's really big, could result in key error later if len(ack_dict) > 90: # empty ack dict if it's really big, could result in key error later
print "DEBUG: Length of ack dictionary is big at " + str(len(ack_dict)) + " clearing." print "DEBUG: Length of ack dictionary is big at " + str(len(ack_dict)) + " clearing."
ack_dict.clear() ack_dict.clear()
@ -273,21 +273,21 @@ def process_message(line):
m = re.search(searchstring, line) m = re.search(searchstring, line)
fullmessage = m.group(1) fullmessage = m.group(1)
ack_attached = re.search('(.*){([0-9A-Z]+)', fullmessage) # ack formats include: {1, {AB}, {12 ack_attached = re.search('(.*){([0-9A-Z]+)', fullmessage) # ack formats include: {1, {AB}, {12
if ack_attached: # "{##" suffix means radio wants an ack back if ack_attached: # "{##" suffix means radio wants an ack back
message = ack_attached.group(1) # message content message = ack_attached.group(1) # message content
ack_num = ack_attached.group(2) # suffix number to use in ack ack_num = ack_attached.group(2) # suffix number to use in ack
else: else:
message = fullmessage message = fullmessage
ack_num = "0" # ack not requested, but lets send one as 0 ack_num = "0" # ack not requested, but lets send one as 0
print "Received message______________" print "Received message______________"
print "Raw : " + line print "Raw : " + line
print "From : " + fromcall print "From : " + fromcall
print "Message : " + message print "Message : " + message
print "Msg number : " + str(ack_num) print "Msg number : " + str(ack_num)
return (fromcall, message, ack_num) return (fromcall, message, ack_num)
### end process_message() ### end process_message()
@ -295,24 +295,24 @@ def send_email(to_addr, content):
print "Sending Email_________________" print "Sending Email_________________"
global shortcuts global shortcuts
if to_addr in shortcuts: if to_addr in shortcuts:
print "To : " + to_addr , print "To : " + to_addr ,
to_addr = shortcuts[to_addr] to_addr = shortcuts[to_addr]
print " (" + to_addr + ")" print " (" + to_addr + ")"
subject = BASECALLSIGN subject = BASECALLSIGN
#content = content + "\n\n(NOTE: reply with one line)" #content = content + "\n\n(NOTE: reply with one line)"
print "Subject : " + subject print "Subject : " + subject
print "Body : " + content print "Body : " + content
msg = MIMEText(content) msg = MIMEText(content)
msg['Subject'] = subject msg['Subject'] = subject
msg['From'] = "KM6XXX@yourdomain.org" msg['From'] = "KM6XXX@yourdomain.org"
msg['To'] = to_addr msg['To'] = to_addr
s = smtplib.SMTP_SSL('smtp.yourdomain.com', 465) s = smtplib.SMTP_SSL('smtp.yourdomain.com', 465)
s.login("KM6XXX@yourdomain.org", "yourpassword") s.login("KM6XXX@yourdomain.org", "yourpassword")
try: try:
s.sendmail("KM6XXX@yourdomain.org", [to_addr], msg.as_string()) s.sendmail("KM6XXX@yourdomain.org", [to_addr], msg.as_string())
except Exception, e: except Exception, e:
print "Sendmail Error!!!!!!!!!" print "Sendmail Error!!!!!!!!!"
s.quit() s.quit()
return(-1) return(-1)
s.quit() s.quit()
@ -323,165 +323,167 @@ def send_email(to_addr, content):
### main() ### ### main() ###
try: def main():
tn = telnetlib.Telnet(HOST, 14580) try:
except Exception, e: tn = telnetlib.Telnet(HOST, 14580)
print "Telnet session failed.\n" except Exception, e:
sys.exit(-1) print "Telnet session failed.\n"
sys.exit(-1)
time.sleep(2) time.sleep(2)
tn.write("user " + USER + " pass " + PASS + " vers aprsd 0.99\n" ) tn.write("user " + USER + " pass " + PASS + " vers aprsd 0.99\n" )
time.sleep(2) time.sleep(2)
check_email_thread() # start email reader thread check_email_thread() # start email reader thread
while True: while True:
line = "" line = ""
try: try:
for char in tn.read_until("\n",100): for char in tn.read_until("\n",100):
line = line + char line = line + char
line = line.replace('\n', '') line = line.replace('\n', '')
print line print line
searchstring = '::' + USER searchstring = '::' + USER
if re.search(searchstring, line): # is aprs message to us, not beacon, status, etc if re.search(searchstring, line): # is aprs message to us, not beacon, status, etc
(fromcall, message, ack) = process_message(line) (fromcall, message, ack) = process_message(line)
else: else:
message = "noise" message = "noise"
continue continue
# ACK (ack##)
if re.search('^ack[0-9]+', message):
a = re.search('^ack([0-9]+)', message) # put message_number:1 in dict to record the ack
ack_dict.update({int(a.group(1)):1})
continue
# EMAIL (-)
elif re.search('^-.*', message): # is email command
searchstring = '^' + BASECALLSIGN + '.*'
if re.search(searchstring, fromcall): # only I can do email
r = re.search('^-([0-9])[0-9]*$', message) # digits only, first one is number of emails to resend
if r is not None:
resend_email(r.group(1))
elif re.search('^-([A-Za-z0-9_\-\.@]+) (.*)', message): # -user@address.com body of email
a = re.search('^-([A-Za-z0-9_\-\.@]+) (.*)', message) # (same search again)
if a is not None:
to_addr = a.group(1)
content = a.group(2)
if content == 'mapme': # send recipient link to aprs.fi map
content = "Click for my location: http://aprs.fi/" + BASECALLSIGN
too_soon = 0
now = time.time()
if ack in email_sent_dict: # see if we sent this msg number recently
timedelta = now - email_sent_dict[ack]
if ( timedelta < 300 ): # five minutes
too_soon = 1
if not too_soon or ack == 0:
send_result = send_email(to_addr, content)
if send_result != 0:
send_message(fromcall, "-" + to_addr + " failed")
else:
#send_message(fromcall, "-" + to_addr + " sent")
if len(email_sent_dict) > 98: # clear email sent dictionary if somehow goes over 100
print "DEBUG: email_sent_dict is big (" + str(len(email_sent_dict)) + ") clearing out."
email_sent_dict.clear()
email_sent_dict[ack] = now
else:
print "\nEmail for message number " + ack + " recently sent, not sending again.\n"
else:
send_message(fromcall, "Bad email address")
# TIME (t) # ACK (ack##)
elif re.search('^t', message): if re.search('^ack[0-9]+', message):
stm = time.localtime() a = re.search('^ack([0-9]+)', message) # put message_number:1 in dict to record the ack
h = stm.tm_hour ack_dict.update({int(a.group(1)):1})
m = stm.tm_min continue
cur_time = fuzzy(h, m, 1)
reply = cur_time + " (" + str(h) + ":" + str(m).rjust(2, '0') + "PDT)" + " (" + message.rstrip() + ")"
thread = threading.Thread(target = send_message, args = (fromcall, reply))
thread.start()
# FORTUNE (f)
elif re.search('^f', message):
process = subprocess.Popen(['/usr/games/fortune', '-s', '-n 60'], stdout=subprocess.PIPE)
reply = process.communicate()[0]
send_message(fromcall, reply.rstrip())
# PING (p) # EMAIL (-)
elif re.search('^p', message): elif re.search('^-.*', message): # is email command
stm = time.localtime() searchstring = '^' + BASECALLSIGN + '.*'
h = stm.tm_hour if re.search(searchstring, fromcall): # only I can do email
m = stm.tm_min r = re.search('^-([0-9])[0-9]*$', message) # digits only, first one is number of emails to resend
s = stm.tm_sec if r is not None:
reply = "Pong! " + str(h).zfill(2) + ":" + str(m).zfill(2) + ":" + str(s).zfill(2) resend_email(r.group(1))
send_message(fromcall, reply.rstrip()) elif re.search('^-([A-Za-z0-9_\-\.@]+) (.*)', message): # -user@address.com body of email
a = re.search('^-([A-Za-z0-9_\-\.@]+) (.*)', message) # (same search again)
if a is not None:
to_addr = a.group(1)
content = a.group(2)
if content == 'mapme': # send recipient link to aprs.fi map
content = "Click for my location: http://aprs.fi/" + BASECALLSIGN
too_soon = 0
now = time.time()
if ack in email_sent_dict: # see if we sent this msg number recently
timedelta = now - email_sent_dict[ack]
if ( timedelta < 300 ): # five minutes
too_soon = 1
if not too_soon or ack == 0:
send_result = send_email(to_addr, content)
if send_result != 0:
send_message(fromcall, "-" + to_addr + " failed")
else:
#send_message(fromcall, "-" + to_addr + " sent")
if len(email_sent_dict) > 98: # clear email sent dictionary if somehow goes over 100
print "DEBUG: email_sent_dict is big (" + str(len(email_sent_dict)) + ") clearing out."
email_sent_dict.clear()
email_sent_dict[ack] = now
else:
print "\nEmail for message number " + ack + " recently sent, not sending again.\n"
else:
send_message(fromcall, "Bad email address")
# LOCATION (l) "8 Miles E Auburn CA 1771' 38.91547,-120.99500 0.1h ago" # TIME (t)
elif re.search('^l', message): elif re.search('^t', message):
# get my last location, get descriptive name from weather service stm = time.localtime()
try: h = stm.tm_hour
url = "http://api.aprs.fi/api/get?name=" + fromcall + "&what=loc&apikey=104070.f9lE8qg34L8MZF&format=json" m = stm.tm_min
response = urllib.urlopen(url) cur_time = fuzzy(h, m, 1)
aprs_data = json.loads(response.read()) reply = cur_time + " (" + str(h) + ":" + str(m).rjust(2, '0') + "PDT)" + " (" + message.rstrip() + ")"
lat = aprs_data['entries'][0]['lat'] thread = threading.Thread(target = send_message, args = (fromcall, reply))
lon = aprs_data['entries'][0]['lng'] thread.start()
try: # altitude not always provided
alt = aprs_data['entries'][0]['altitude'] # FORTUNE (f)
except: elif re.search('^f', message):
alt = 0 process = subprocess.Popen(['/usr/games/fortune', '-s', '-n 60'], stdout=subprocess.PIPE)
altfeet = int(alt * 3.28084) reply = process.communicate()[0]
aprs_lasttime_seconds = aprs_data['entries'][0]['lasttime']
aprs_lasttime_seconds = aprs_lasttime_seconds.encode('ascii',errors='ignore') #unicode to ascii
delta_seconds = time.time() - int(aprs_lasttime_seconds)
delta_hours = delta_seconds / 60 / 60
url2 = "https://forecast.weather.gov/MapClick.php?lat=" + str(lat) + "&lon=" + str(lon) + "&FcstType=json"
response2 = urllib.urlopen(url2)
wx_data = json.loads(response2.read())
reply = wx_data['location']['areaDescription'] + " " + str(altfeet) + "' " + str(lat) + "," + str(lon) + " " + str("%.1f" % round(delta_hours,1)) + "h ago"
reply = reply.encode('ascii',errors='ignore') # unicode to ascii
send_message(fromcall, reply.rstrip())
except:
reply = "Unable to find you (send beacon?)"
send_message(fromcall, reply.rstrip()) send_message(fromcall, reply.rstrip())
# WEATHER (w) "42F(68F/48F) Haze. Tonight, Haze then Chance Rain." # PING (p)
elif re.search('^w', message): elif re.search('^p', message):
# get my last location from aprsis then get weather from weather service stm = time.localtime()
try: h = stm.tm_hour
url = "http://api.aprs.fi/api/get?name=" + fromcall + "&what=loc&apikey=104070.f9lE8qg34L8MZF&format=json" m = stm.tm_min
response = urllib.urlopen(url) s = stm.tm_sec
aprs_data = json.loads(response.read()) reply = "Pong! " + str(h).zfill(2) + ":" + str(m).zfill(2) + ":" + str(s).zfill(2)
lat = aprs_data['entries'][0]['lat']
lon = aprs_data['entries'][0]['lng']
url2 = "https://forecast.weather.gov/MapClick.php?lat=" + str(lat) + "&lon=" + str(lon) + "&FcstType=json"
response2 = urllib.urlopen(url2)
wx_data = json.loads(response2.read())
reply = wx_data['currentobservation']['Temp'] + "F(" + wx_data['data']['temperature'][0] + "F/" + wx_data['data']['temperature'][1] + "F) " + wx_data['data']['weather'][0] + ". " + wx_data['time']['startPeriodName'][1] + ", " + wx_data['data']['weather'][1] + "."
reply = reply.encode('ascii',errors='ignore') # unicode to ascii
send_message(fromcall, reply.rstrip()) send_message(fromcall, reply.rstrip())
except:
reply = "Unable to find you (send beacon?)" # LOCATION (l) "8 Miles E Auburn CA 1771' 38.91547,-120.99500 0.1h ago"
elif re.search('^l', message):
# get my last location, get descriptive name from weather service
try:
url = "http://api.aprs.fi/api/get?name=" + fromcall + "&what=loc&apikey=104070.f9lE8qg34L8MZF&format=json"
response = urllib.urlopen(url)
aprs_data = json.loads(response.read())
lat = aprs_data['entries'][0]['lat']
lon = aprs_data['entries'][0]['lng']
try: # altitude not always provided
alt = aprs_data['entries'][0]['altitude']
except:
alt = 0
altfeet = int(alt * 3.28084)
aprs_lasttime_seconds = aprs_data['entries'][0]['lasttime']
aprs_lasttime_seconds = aprs_lasttime_seconds.encode('ascii',errors='ignore') #unicode to ascii
delta_seconds = time.time() - int(aprs_lasttime_seconds)
delta_hours = delta_seconds / 60 / 60
url2 = "https://forecast.weather.gov/MapClick.php?lat=" + str(lat) + "&lon=" + str(lon) + "&FcstType=json"
response2 = urllib.urlopen(url2)
wx_data = json.loads(response2.read())
reply = wx_data['location']['areaDescription'] + " " + str(altfeet) + "' " + str(lat) + "," + str(lon) + " " + str("%.1f" % round(delta_hours,1)) + "h ago"
reply = reply.encode('ascii',errors='ignore') # unicode to ascii
send_message(fromcall, reply.rstrip())
except:
reply = "Unable to find you (send beacon?)"
send_message(fromcall, reply.rstrip())
# WEATHER (w) "42F(68F/48F) Haze. Tonight, Haze then Chance Rain."
elif re.search('^w', message):
# get my last location from aprsis then get weather from weather service
try:
url = "http://api.aprs.fi/api/get?name=" + fromcall + "&what=loc&apikey=104070.f9lE8qg34L8MZF&format=json"
response = urllib.urlopen(url)
aprs_data = json.loads(response.read())
lat = aprs_data['entries'][0]['lat']
lon = aprs_data['entries'][0]['lng']
url2 = "https://forecast.weather.gov/MapClick.php?lat=" + str(lat) + "&lon=" + str(lon) + "&FcstType=json"
response2 = urllib.urlopen(url2)
wx_data = json.loads(response2.read())
reply = wx_data['currentobservation']['Temp'] + "F(" + wx_data['data']['temperature'][0] + "F/" + wx_data['data']['temperature'][1] + "F) " + wx_data['data']['weather'][0] + ". " + wx_data['time']['startPeriodName'][1] + ", " + wx_data['data']['weather'][1] + "."
reply = reply.encode('ascii',errors='ignore') # unicode to ascii
send_message(fromcall, reply.rstrip())
except:
reply = "Unable to find you (send beacon?)"
send_message(fromcall, reply)
# USAGE
else:
reply = "usage: time, fortune, loc, weath, -emailaddr emailbody, -#(resend)"
send_message(fromcall, reply) send_message(fromcall, reply)
time.sleep(1) # let any threads do their thing, then ack
send_ack(fromcall, ack) # send an ack last
# USAGE except Exception, e:
else: print "Error in mainline loop:"
reply = "usage: time, fortune, loc, weath, -emailaddr emailbody, -#(resend)" print "%s" % str(e)
send_message(fromcall, reply) print "Exiting."
#sys.exit(1) # merely a suggestion
os._exit(1)
time.sleep(1) # let any threads do their thing, then ack # end while True
send_ack(fromcall, ack) # send an ack last tn.close()
exit()
except Exception, e:
print "Error in mainline loop:"
print "%s" % str(e)
print "Exiting."
#sys.exit(1) # merely a suggestion
os._exit(1)
# end while True
tn.close() if __name__ == "__main__":
main()
exit()