1
0
mirror of https://github.com/craigerl/aprsd.git synced 2025-02-03 09:44:15 -05:00

fix bugs after beautification and yaml config additions. Convert to sockets. case insensitive commands

This commit is contained in:
craigerl 2020-12-02 12:25:39 -08:00
parent 78add91f3c
commit abc63791f1

View File

@ -27,13 +27,14 @@ import email
import json import json
import logging import logging
import os import os
import socket
import pprint import pprint
import re import re
import signal import signal
import smtplib import smtplib
import subprocess import subprocess
import sys import sys
import telnetlib #import telnetlib
import threading import threading
import time import time
import urllib import urllib
@ -79,7 +80,7 @@ ack_dict = {}
# send over rf {int} # send over rf {int}
message_number = 0 message_number = 0
# global telnet connection object # global telnet connection object -- not needed anymore
tn = None tn = None
# command line args # command line args
@ -95,16 +96,31 @@ parser.add_argument("--quiet",
args = parser.parse_args() args = parser.parse_args()
#def setup_connection():
# global tn
# host = CONFIG['aprs']['host']
# port = CONFIG['aprs']['port']
# LOG.debug("Setting up telnet connection to '%s:%s'" % (host, port))
# try:
# tn = telnetlib.Telnet(host, port)
# except Exception:
# LOG.exception("Telnet session failed.")
# sys.exit(-1)
def setup_connection(): def setup_connection():
global tn global sock
host = CONFIG['aprs']['host'] global sock_file
port = CONFIG['aprs']['port']
LOG.debug("Setting up telnet connection to '%s:%s'" % (host, port))
try: try:
tn = telnetlib.Telnet(host, port) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except Exception: sock.connect((CONFIG['aprs']['host'], 14580))
LOG.exception("Telnet session failed.") except Exception, e:
sys.exit(-1) print "Unable to connect to APRS-IS server.\n"
print str(e)
os._exit(1)
sock_file = sock.makefile(mode='r', bufsize=0 )
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) # disable nagle algorithm
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 512) # buffer size
def signal_handler(signal, frame): def signal_handler(signal, frame):
@ -114,61 +130,52 @@ def signal_handler(signal, frame):
# end signal_handler # end signal_handler
def parse_email(msgid, data, server): def parse_email(msgid, data, server):
envelope = data[b'ENVELOPE'] envelope = data[b'ENVELOPE']
# print('ID:%d "%s" (%s)' % f = re.search('([\.\w_-]+@[\.\w_-]+)', str(envelope.from_[0]) ) # email address match
# (msgid, envelope.subject.decode(), envelope.date))
# email address match
f = re.search('([\.\w_-]+@[\.\w_-]+)', str(envelope.from_[0]))
if f is not None: if f is not None:
from_addr = f.group(1) from_addr = f.group(1)
else: else:
from_addr = "noaddr" from_addr = "noaddr"
m = server.fetch([msgid], ['RFC822']) m = server.fetch([msgid], ['RFC822'])
msg = email.message_from_string(m[msgid]['RFC822']) msg = email.message_from_string(m[msgid]['RFC822'])
if msg.is_multipart(): if msg.is_multipart():
text = "" text = ""
html = None html = None
body = "* unreadable msg received" # default in case body somehow isn't set below - happened once
# default in case body somehow isn't set below - happened once for part in msg.get_payload():
body = "* unreadable msg received" if part.get_content_charset() is None:
for part in msg.get_payload(): # We cannot know the character set, so return decoded "something"
if part.get_content_charset() is None: text = part.get_payload(decode=True)
# We cannot know the character set, so return decoded "something" continue
text = part.get_payload(decode=True)
continue charset = part.get_content_charset()
charset = part.get_content_charset() if part.get_content_type() == 'text/plain':
text = unicode(part.get_payload(decode=True), str(charset), "ignore").encode('utf8', 'replace')
if part.get_content_type() == 'text/plain':
text = unicode(part.get_payload(decode=True), str(charset), if part.get_content_type() == 'text/html':
"ignore").encode('utf8', 'replace') html = unicode(part.get_payload(decode=True), str(charset), "ignore").encode('utf8', 'replace')
if part.get_content_type() == 'text/html': if text is not None:
html = unicode(part.get_payload(decode=True), str(charset), body = text.strip() # strip removes white space fore and aft of string
"ignore").encode('utf8', 'replace') else:
body = html.strip()
if text is not None:
# strip removes white space fore and aft of string
body = text.strip()
else:
body = html.strip()
else: else:
text = unicode(msg.get_payload(decode=True), msg.get_content_charset(), if msg.get_content_charset() == None: # email.uscc.net sends no charset, blows up unicode function below
'ignore').encode('utf8', 'replace') text = unicode(msg.get_payload(decode=True), 'US-ASCII', 'ignore').encode('utf8', 'replace')
body = text.strip() else:
text = unicode(msg.get_payload(decode=True), msg.get_content_charset(), 'ignore').encode('utf8', 'replace')
# strip all html tags body = text.strip()
body = re.sub('<[^<]+?>', '', body)
body = re.sub('<[^<]+?>', '', body) # strip all html tags
# strip CR/LF, make it one line, .rstrip fails at this body = body.replace("\n", " ").replace("\r", " ") # strip CR/LF, make it one line, .rstrip fails at this
body = body.replace("\n", " ").replace("\r", " ")
return(body, from_addr) return(body, from_addr)
# end parse_email ## end parse_email
def resend_email(count):
def resend_email(count, fromcall):
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
@ -304,7 +311,8 @@ def send_ack_thread(tocall, ack, retry_count):
LOG.info("Raw : %s" % line) LOG.info("Raw : %s" % line)
LOG.info("To : %s" % tocall) LOG.info("To : %s" % tocall)
LOG.info("Ack number : %s" % ack) LOG.info("Ack number : %s" % ack)
tn.write(line) #tn.write(line)
sock.send(line)
# aprs duplicate detection is 30 secs? # aprs duplicate detection is 30 secs?
# (21 only sends first, 28 skips middle) # (21 only sends first, 28 skips middle)
time.sleep(31) time.sleep(31)
@ -334,7 +342,8 @@ def send_message_thread(tocall, message, this_message_number, retry_count):
LOG.info("Raw : " + line) LOG.info("Raw : " + line)
LOG.info("To : " + tocall) LOG.info("To : " + tocall)
LOG.info("Message : " + message) LOG.info("Message : " + message)
tn.write(line) #tn.write(line)
sock.send(line)
# decaying repeats, 31 to 93 second intervals # decaying repeats, 31 to 93 second intervals
sleeptime = (retry_count - i + 1) * 31 sleeptime = (retry_count - i + 1) * 31
time.sleep(sleeptime) time.sleep(sleeptime)
@ -484,7 +493,9 @@ def main(args=args):
user = CONFIG['aprs']['login'] user = CONFIG['aprs']['login']
password = CONFIG['aprs']['password'] password = CONFIG['aprs']['password']
LOG.info("LOGIN to APRSD with user '%s'" % user) LOG.info("LOGIN to APRSD with user '%s'" % user)
tn.write("user %s pass %s vers aprsd 0.99\n" % (user, password)) #tn.write("user %s pass %s vers aprsd 0.99\n" % (user, password))
sock.send("user %s pass %s vers aprsd 0.99\n" % (user, password))
time.sleep(2) time.sleep(2)
check_email_thread() # start email reader thread check_email_thread() # start email reader thread
@ -493,9 +504,10 @@ def main(args=args):
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', '')
line = sock_file.readline().strip()
LOG.info(line) LOG.info(line)
searchstring = '::%s' % user searchstring = '::%s' % user
# is aprs message to us, not beacon, status, etc # is aprs message to us, not beacon, status, etc
@ -518,7 +530,7 @@ def main(args=args):
if re.search(searchstring, fromcall): # only I can do email 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 r = re.search('^-([0-9])[0-9]*$', message) # digits only, first one is number of emails to resend
if r is not None: if r is not None:
resend_email(r.group(1)) resend_email(r.group(1), fromcall)
elif re.search('^-([A-Za-z0-9_\-\.@]+) (.*)', message): # -user@address.com body of email 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) a = re.search('^-([A-Za-z0-9_\-\.@]+) (.*)', message) # (same search again)
if a is not None: if a is not None:
@ -548,7 +560,7 @@ def main(args=args):
send_message(fromcall, "Bad email address") send_message(fromcall, "Bad email address")
# TIME (t) # TIME (t)
elif re.search('^t', message): elif re.search('^[tT]', message):
stm = time.localtime() stm = time.localtime()
h = stm.tm_hour h = stm.tm_hour
m = stm.tm_min m = stm.tm_min
@ -558,13 +570,13 @@ def main(args=args):
thread.start() thread.start()
# FORTUNE (f) # FORTUNE (f)
elif re.search('^f', message): elif re.search('^[fF]', message):
process = subprocess.Popen(['/usr/games/fortune', '-s', '-n 60'], stdout=subprocess.PIPE) process = subprocess.Popen(['/usr/games/fortune', '-s', '-n 60'], stdout=subprocess.PIPE)
reply = process.communicate()[0] reply = process.communicate()[0]
send_message(fromcall, reply.rstrip()) send_message(fromcall, reply.rstrip())
# PING (p) # PING (p)
elif re.search('^p', message): elif re.search('^[pP]', message):
stm = time.localtime() stm = time.localtime()
h = stm.tm_hour h = stm.tm_hour
m = stm.tm_min m = stm.tm_min
@ -573,18 +585,50 @@ def main(args=args):
send_message(fromcall, reply.rstrip()) send_message(fromcall, reply.rstrip())
# LOCATION (l) "8 Miles E Auburn CA 1771' 38.91547,-120.99500 0.1h ago" # LOCATION (l) "8 Miles E Auburn CA 1771' 38.91547,-120.99500 0.1h ago"
elif re.search('^l', message): # elif re.search('^l', message):
# get my last location, get descriptive name from weather service # # 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())
elif re.search('^[lL]', message):
# get last location of a callsign, get descriptive name from weather service
try: try:
url = "http://api.aprs.fi/api/get?name=" + fromcall + "&what=loc&apikey=104070.f9lE8qg34L8MZF&format=json" a = re.search('^.*\s+(.*)', message) # optional second argument is a callsign to search
if a is not None:
searchcall = a.group(1)
searchcall = searchcall.upper()
else:
searchcall = fromcall # if no second argument, search for calling station
url = "http://api.aprs.fi/api/get?name=" + searchcall + "&what=loc&apikey=104070.f9lE8qg34L8MZF&format=json"
response = urllib.urlopen(url) response = urllib.urlopen(url)
aprs_data = json.loads(response.read()) aprs_data = json.loads(response.read())
lat = aprs_data['entries'][0]['lat'] lat = aprs_data['entries'][0]['lat']
lon = aprs_data['entries'][0]['lng'] lon = aprs_data['entries'][0]['lng']
try: # altitude not always provided try: # altitude not always provided
alt = aprs_data['entries'][0]['altitude'] alt = aprs_data['entries'][0]['altitude']
except: except:
alt = 0 alt = 0
altfeet = int(alt * 3.28084) altfeet = int(alt * 3.28084)
aprs_lasttime_seconds = aprs_data['entries'][0]['lasttime'] aprs_lasttime_seconds = aprs_data['entries'][0]['lasttime']
aprs_lasttime_seconds = aprs_lasttime_seconds.encode('ascii',errors='ignore') #unicode to ascii aprs_lasttime_seconds = aprs_lasttime_seconds.encode('ascii',errors='ignore') #unicode to ascii
@ -593,15 +637,15 @@ def main(args=args):
url2 = "https://forecast.weather.gov/MapClick.php?lat=" + str(lat) + "&lon=" + str(lon) + "&FcstType=json" url2 = "https://forecast.weather.gov/MapClick.php?lat=" + str(lat) + "&lon=" + str(lon) + "&FcstType=json"
response2 = urllib.urlopen(url2) response2 = urllib.urlopen(url2)
wx_data = json.loads(response2.read()) 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 = searchcall + ": " + 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 reply = reply.encode('ascii',errors='ignore') # unicode to ascii
send_message(fromcall, reply.rstrip()) send_message(fromcall, reply.rstrip())
except: except:
reply = "Unable to find you (send beacon?)" reply = "Unable to find station " + searchcall + ". Sending beacons?"
send_message(fromcall, reply.rstrip()) send_message(fromcall, reply.rstrip())
# WEATHER (w) "42F(68F/48F) Haze. Tonight, Haze then Chance Rain." # WEATHER (w) "42F(68F/48F) Haze. Tonight, Haze then Chance Rain."
elif re.search('^w', message): elif re.search('^[wW]', message):
# get my last location from aprsis then get weather from # get my last location from aprsis then get weather from
# weather service # weather service
try: try:
@ -633,8 +677,7 @@ def main(args=args):
# USAGE # USAGE
else: else:
reply = ("usage: time, fortune, loc, weath, -emailaddr " reply = "usage: time, fortune, loc, weath"
"emailbody, -#(resend)")
send_message(fromcall, reply) send_message(fromcall, reply)
# let any threads do their thing, then ack # let any threads do their thing, then ack
@ -650,7 +693,10 @@ def main(args=args):
os._exit(1) os._exit(1)
# end while True # end while True
tn.close() #tn.close()
sock.shutdown(0)
sock.close()
exit() exit()