1
0
mirror of https://github.com/craigerl/aprsd.git synced 2025-06-25 21:45:25 -04:00

Require ~/.aprsd/config.yml

This patch completes the migration to using a config.yml file.
~/.aprsd/config.yml is now required and all options for callsign,
imap, aprs user, passwords are in the config.  If there is no existing
~/.aprsd/config.yml file, then the app will output a sample config
and exit.

This patch also adds a global logging facility that allows logging all
commands to aprsd.log as well as stdout.  You can disable logging to
stdout by adding --quiet on the command line.  You can specify the log
level with --loglevel INFO.  By default the log level is DEBUG.

This patch also updates some formatting issues and small refactoring
to ensure that the logging facility and config is read prior to starting
any network connections and/or services.
This commit is contained in:
Walter A. Boring IV 2018-11-29 08:22:41 -05:00
parent 4c8d9c3b2c
commit ce7a30aa78
2 changed files with 439 additions and 371 deletions

View File

@ -1,9 +1,10 @@
#!/usr/bin/python -u #!/usr/bin/python -u
# #
# Listen on amateur radio aprs-is network for messages and respond to them. # Listen on amateur radio aprs-is network for messages and respond to them.
# You must have an amateur radio callsign to use this software. Put your # You must have an amateur radio callsign to use this software. You must
# callsign in the "USER" variable and update your aprs-is password in "PASS". # create an ~/.aprsd/config.yml file with all of the required settings. To
# You must also have an imap email account available for polling. # generate an example config.yml, just run aprsd, then copy the sample config
# to ~/.aprsd/config.yml and edit the settings.
# #
# APRS messages: # APRS messages:
# l(ocation) = descriptive location of calling station # l(ocation) = descriptive location of calling station
@ -21,23 +22,24 @@
# python included libs # python included libs
import argparse import argparse
import json
import urllib
import sys
import os
import telnetlib
import time
import re
from random import randint
import smtplib
from email.mime.text import MIMEText
import subprocess
import datetime import datetime
import calendar
import email import email
import threading import json
import signal import logging
import os
import pprint import pprint
import re
import signal
import smtplib
import subprocess
import sys
import telnetlib
import threading
import time
import urllib
from email.mime.text import MIMEText
from logging.handlers import RotatingFileHandler
# external lib imports # external lib imports
from imapclient import IMAPClient, SEEN from imapclient import IMAPClient, SEEN
@ -46,87 +48,60 @@ from imapclient import IMAPClient, SEEN
from aprsd.fuzzyclock import fuzzy from aprsd.fuzzyclock import fuzzy
import utils import utils
# setup the global logger
LOG = logging.getLogger('APRSD')
# global for the config yaml
CONFIG = None
# localization, please edit: # localization, please edit:
HOST = "noam.aprs2.net" # north america tier2 servers round robin # HOST = "noam.aprs2.net" # north america tier2 servers round robin
USER = "KM6XXX-9" # callsign of this aprs client with SSID # USER = "KM6XXX-9" # callsign of this aprs client with SSID
PASS = "99999" # google how to generate this # PASS = "99999" # google how to generate this
BASECALLSIGN = "KM6XXX" # callsign of radio in the field to which we send email # BASECALLSIGN = "KM6XXX" # callsign of radio in the field to which we send email
shortcuts = { # shortcuts = {
"aa" : "5551239999@vtext.com", # "aa" : "5551239999@vtext.com",
"cl" : "craiglamparter@somedomain.org", # "cl" : "craiglamparter@somedomain.org",
"wb" : "5553909472@vtext.com" # "wb" : "5553909472@vtext.com"
} # }
# globals - tell me a better way to update data being used by threads # globals - tell me a better way to update data being used by threads
email_sent_dict = {} # message_number:time combos so we don't resend the same email in five mins {int:int} email_sent_dict = {} # message_number:time combos so we don't resend the same email in five mins {int:int}
ack_dict = {} # message_nubmer:ack combos so we stop sending a message after an ack from radio {int:int} ack_dict = {} # message_nubmer:ack combos so we stop sending a message after an ack from radio {int:int}
message_number = 0 # current aprs radio message number, increments for each message we send over rf {int} message_number = 0 # current aprs radio message number, increments for each message we send over rf {int}
# global telnet connection object
tn = None
# command line args # command line args
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--user", parser.add_argument("--loglevel",
metavar="<user>", default='DEBUG',
default=utils.env("APRS_USER"), choices=['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG'],
help="The callsign of this ARPS client with SSID" help="The log level to use for aprsd.log")
" Default=env[APRS_USER]") parser.add_argument("--quiet",
action='store_true',
parser.add_argument("--host", help="Don't log to stdout")
metavar="<host>",
default=utils.env("APRS_HOST"),
help="The aprs host to use Default=env[APRS_HOST]")
parser.add_argument("--password",
metavar="<password>",
default=utils.env("APRS_PASSWORD"),
help="The aprs password Default=env[APRS_PASSWORD]")
parser.add_argument("--callsign",
metavar="<callsign>",
default=utils.env("APRS_CALLSIGN"),
help="The callsign of radio in the field to which we send "
"email Default=env[APRS_CALLSIGN]")
args = parser.parse_args() args = parser.parse_args()
if not args.user:
print("Missing the aprs user (env[APRS_USER])")
parser.print_help()
parser.exit()
else:
USER = args.user
if not args.password:
print("Missing the aprs password (env[APRS_PASSWORD])")
parser.print_help()
parser.exit()
else:
PASS = args.password
if not args.callsign:
print("Missing the aprs callsign (env[APRS_CALLSIGN])")
parser.print_help()
parser.exit()
else:
BASECALLSIGN = args.callsign
# Now read the ~/.aprds/config.yml def setup_connection():
config = utils.get_config() global tn
if 'shortcuts' in config: host = CONFIG['aprs']['host']
shortcuts = config['shortcuts'] LOG.debug("Setting up telnet connection to '%s'" % host)
else: try:
print("missing 'shortcuts' section of config.yml") tn = telnetlib.Telnet(host, 14580)
sys.exit(-1) except Exception, e:
LOG.critical("Telnet session failed.\n", e)
try:
tn = telnetlib.Telnet(HOST, 14580)
except Exception, e:
print "Telnet session failed.\n"
sys.exit(-1) sys.exit(-1)
def signal_handler(signal, frame): def signal_handler(signal, frame):
print("Ctrl+C, exiting.") LOG.info("Ctrl+C, exiting.")
#sys.exit(0) # thread ignores this #sys.exit(0) # thread ignores this
os._exit(0) os._exit(0)
signal.signal(signal.SIGINT, signal_handler)
### end signal_handler ### end signal_handler
def parse_email(msgid, data, server): def parse_email(msgid, data, server):
@ -178,15 +153,18 @@ def resend_email(count):
year = date.year year = date.year
today = str(day) + "-" + month + "-" + str(year) today = str(day) + "-" + month + "-" + str(year)
global shortcuts shortcuts = CONFIG['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) LOG.debug("resend_email: Connect to IMAP host '%s' with user '%s'" %
server.login('KM6XXX@yourdomain.org', 'yourpassword') (CONFIG['imap']['host'],
select_info = server.select_folder('INBOX') CONFIG['imap']['login']))
server = IMAPClient(CONFIG['imap']['host'], use_uid=True)
server.login(CONFIG['imap']['login'], CONFIG['imap']['password'])
# select_info = server.select_folder('INBOX')
messages = server.search(['SINCE', today]) messages = server.search(['SINCE', today])
#print("%d messages received today" % len(messages)) LOG.debug("%d messages received today" % len(messages))
msgexists = False msgexists = False
@ -216,13 +194,13 @@ def resend_email(count):
### end resend_email() ### end resend_email()
def check_email_thread(): def check_email_thread():
# print "Email thread disabled."
# return
# print "Email thread disabled." LOG.debug("Starting Email thread")
# return
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 shortcuts = CONFIG['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()
@ -231,17 +209,23 @@ def check_email_thread():
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) LOG.debug("Connect to IMAP host '%s' with user '%s'" %
server.login('KM6XXX@yourdomain.org', 'yourpassword') (CONFIG['imap']['host'],
select_info = server.select_folder('INBOX') CONFIG['imap']['login']))
server = IMAPClient(CONFIG['imap']['host'], use_uid=True)
server.login(CONFIG['imap']['login'], CONFIG['imap']['password'])
# select_info = server.select_folder('INBOX')
messages = server.search(['SINCE', today]) messages = server.search(['SINCE', today])
#print("%d messages received today" % len(messages)) LOG.debug("%d messages received today" % len(messages))
for msgid, data in server.fetch(messages, ['ENVELOPE']).items(): for msgid, data in server.fetch(messages, ['ENVELOPE']).items():
envelope = data[b'ENVELOPE'] envelope = data[b'ENVELOPE']
#print('ID:%d "%s" (%s)' % (msgid, envelope.subject.decode(), envelope.date )) LOG.debug('ID:%d "%s" (%s)' %
f = re.search('([[A-a][0-9]_-]+@[[A-a][0-9]_-\.]+)', str(envelope.from_[0]) ) (msgid, envelope.subject.decode(), envelope.date))
f = re.search('([[A-a][0-9]_-]+@[[A-a][0-9]_-\.]+)',
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:
@ -257,7 +241,7 @@ def check_email_thread():
reply = "-" + from_addr + " " + body reply = "-" + from_addr + " " + body
#print "Sending message via aprs: " + reply #print "Sending message via aprs: " + reply
send_message(BASECALLSIGN, reply) #radio send_message(CONFIG['ham']['callsign'], reply) #radio
server.add_flags(msgid, ['APRS']) #flag message as sent via aprs server.add_flags(msgid, ['APRS']) #flag message as sent via aprs
server.remove_flags(msgid, [SEEN]) #unset seen flag, will stay bold in email client server.remove_flags(msgid, [SEEN]) #unset seen flag, will stay bold in email client
@ -267,12 +251,12 @@ def check_email_thread():
def send_ack_thread(tocall, ack, retry_count): def send_ack_thread(tocall, ack, retry_count):
tocall = tocall.ljust(9) # pad to nine chars tocall = tocall.ljust(9) # pad to nine chars
line = USER + ">APRS::" + tocall + ":ack" + str(ack) + "\n" line = CONFIG['aprs']['login'] + ">APRS::" + tocall + ":ack" + str(ack) + "\n"
for i in range(retry_count, 0, -1): for i in range(retry_count, 0, -1):
print "Sending ack __________________ Tx(" + str(i) + ")" LOG.info("Sending ack __________________ Tx(" + str(i) + ")")
print "Raw : " + line, LOG.info("Raw : " + line)
print "To : " + tocall LOG.info("To : " + tocall)
print "Ack number : " + str(ack) LOG.info("Ack number : " + str(ack))
tn.write(line) tn.write(line)
time.sleep(31) # aprs duplicate detection is 30 secs? (21 only sends first, 28 skips middle) time.sleep(31) # aprs duplicate detection is 30 secs? (21 only sends first, 28 skips middle)
return() return()
@ -289,15 +273,17 @@ def send_ack(tocall, ack):
def send_message_thread(tocall, message, this_message_number, retry_count): 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 = (CONFIG['aprs']['login'] + ">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: " LOG.debug("DEBUG: send_message_thread msg:ack combos are: ")
pprint.pprint(ack_dict) LOG.debug(pprint.pformat(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) + ")" LOG.info("Sending message_______________ " +
print "Raw : " + line, str(this_message_number) + "(Tx" + str(i) + ")")
print "To : " + tocall LOG.info("Raw : " + line)
print "Message : " + message LOG.info("To : " + tocall)
LOG.info("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)
@ -315,17 +301,19 @@ def send_message(tocall, message):
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." LOG.debug("DEBUG: Length of ack dictionary is big at " + str(len(ack_dict)) + " clearing.")
ack_dict.clear() ack_dict.clear()
pprint.pprint(ack_dict) LOG.debug(pprint.pformat(ack_dict))
print "DEBUG: Cleared ack dictionary, ack_dict length is now " + str(len(ack_dict)) + "." LOG.debug("DEBUG: Cleared ack dictionary, ack_dict length is now " + str(len(ack_dict)) + ".")
ack_dict[message_number] = 0 # clear ack for this message number ack_dict[message_number] = 0 # clear ack for this message number
tocall = tocall.ljust(9) # pad to nine chars tocall = tocall.ljust(9) # pad to nine chars
message = message[:67] # max? ftm400 displays 64, raw msg shows 74 message = message[:67] # max? ftm400 displays 64, raw msg shows 74
# and ftm400-send is max 64. setting this to # and ftm400-send is max 64. setting this to
# 67 displays 64 on the ftm400. (+3 {01 suffix) # 67 displays 64 on the ftm400. (+3 {01 suffix)
# feature req: break long ones into two msgs # feature req: break long ones into two msgs
thread = threading.Thread(target = send_message_thread, args = (tocall, message, message_number, retry_count)) thread = threading.Thread(
target = send_message_thread,
args = (tocall, message, message_number, retry_count))
thread.start() thread.start()
return() return()
### end send_message() ### end send_message()
@ -334,7 +322,7 @@ def send_message(tocall, message):
def process_message(line): def process_message(line):
f = re.search('^(.*)>', line) f = re.search('^(.*)>', line)
fromcall = f.group(1) fromcall = f.group(1)
searchstring = '::' + USER + '[ ]*:(.*)' # verify this, callsign is padded out with spaces to colon searchstring = '::' + CONFIG['aprs']['login'] + '[ ]*:(.*)' # verify this, callsign is padded out with spaces to colon
m = re.search(searchstring, line) m = re.search(searchstring, line)
fullmessage = m.group(1) fullmessage = m.group(1)
@ -346,27 +334,27 @@ def process_message(line):
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______________" LOG.info("Received message______________")
print "Raw : " + line LOG.info("Raw : " + line)
print "From : " + fromcall LOG.info("From : " + fromcall)
print "Message : " + message LOG.info("Message : " + message)
print "Msg number : " + str(ack_num) LOG.info("Msg number : " + str(ack_num))
return (fromcall, message, ack_num) return (fromcall, message, ack_num)
### end process_message() ### end process_message()
def send_email(to_addr, content): def send_email(to_addr, content):
print "Sending Email_________________" LOG.info("Sending Email_________________")
global shortcuts shortcuts = CONFIG['shortcuts']
if to_addr in shortcuts: if to_addr in shortcuts:
print "To : " + to_addr , LOG.info("To : " + to_addr)
to_addr = shortcuts[to_addr] to_addr = shortcuts[to_addr]
print " (" + to_addr + ")" LOG.info(" (" + to_addr + ")")
subject = BASECALLSIGN subject = CONFIG['ham']['callsign']
#content = content + "\n\n(NOTE: reply with one line)" # content = content + "\n\n(NOTE: reply with one line)"
print "Subject : " + subject LOG.info("Subject : " + subject)
print "Body : " + content LOG.info("Body : " + content)
msg = MIMEText(content) msg = MIMEText(content)
msg['Subject'] = subject msg['Subject'] = subject
@ -376,8 +364,8 @@ def send_email(to_addr, content):
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:
print "Sendmail Error!!!!!!!!!" LOG.exception("Sendmail Error!!!!!!!!!")
s.quit() s.quit()
return(-1) return(-1)
s.quit() s.quit()
@ -385,15 +373,87 @@ def send_email(to_addr, content):
### end send_email ### end send_email
# Setup the logging faciility
# to disable logging to stdout, but still log to file
# use the --quiet option on the cmdln
def setup_logging(args):
global LOG
levels = {
'CRITICAL': logging.CRITICAL,
'ERROR': logging.ERROR,
'WARNING': logging.WARNING,
'INFO': logging.INFO,
'DEBUG': logging.DEBUG}
log_level = levels[args.loglevel]
LOG.setLevel(log_level)
log_format = ("%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s]"
" %(message)s")
date_format = '%m/%d/%Y %I:%M:%S %p'
log_formatter = logging.Formatter(fmt=log_format,
datefmt=date_format)
fh = RotatingFileHandler('aprsd.log',
maxBytes=(10248576*5),
backupCount=4)
fh.setFormatter(log_formatter)
LOG.addHandler(fh)
if not args.quiet:
sh = logging.StreamHandler(sys.stdout)
sh.setFormatter(log_formatter)
LOG.addHandler(sh)
# This method tries to parse the config yaml file
# and consume the settings.
# If the required params don't exist,
# it will look in the environment
def parse_config(args):
# for now we still use globals....ugh
global CONFIG, LOG
def fail(msg):
LOG.critical(msg)
sys.exit(-1)
def check_option(config, section, name=None):
if section in config:
if name and name not in config[section]:
fail("'%s' was not in '%s' section of config file" %
(name, section))
else:
fail("'%s' section wasn't in config file" % section)
# Now read the ~/.aprds/config.yml
config = utils.get_config()
check_option(config, 'shortcuts')
check_option(config, 'ham', 'callsign')
check_option(config, 'aprs', 'login')
check_option(config, 'aprs', 'password')
check_option(config, 'aprs', 'host')
check_option(config, 'imap', 'host')
check_option(config, 'imap', 'login')
check_option(config, 'imap', 'password')
CONFIG = config
LOG.info("aprsd config loaded")
### main() ### ### main() ###
def main(): def main(args=args):
setup_logging(args)
LOG.info("APRSD Started")
parse_config(args)
LOG.debug("Signal handler setup")
signal.signal(signal.SIGINT, signal_handler)
time.sleep(2) time.sleep(2)
setup_connection()
tn.write("user " + USER + " pass " + PASS + " vers aprsd 0.99\n" ) user = CONFIG['aprs']['login']
password = CONFIG['aprs']['password']
LOG.info("LOGIN to APRSD with user '%s'" % user)
tn.write("user " + user + " pass " + password + " 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
@ -404,9 +464,10 @@ def main():
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 LOG.info(line)
searchstring = '::' + USER searchstring = '::' + user
if re.search(searchstring, line): # is aprs message to us, not beacon, status, etc # is aprs message to us, not beacon, status, etc
if re.search(searchstring, line):
(fromcall, message, ack) = process_message(line) (fromcall, message, ack) = process_message(line)
else: else:
message = "noise" message = "noise"
@ -414,13 +475,14 @@ def main():
# ACK (ack##) # ACK (ack##)
if re.search('^ack[0-9]+', message): if re.search('^ack[0-9]+', message):
a = re.search('^ack([0-9]+)', message) # put message_number:1 in dict to record the ack # put message_number:1 in dict to record the ack
a = re.search('^ack([0-9]+)', message)
ack_dict.update({int(a.group(1)):1}) ack_dict.update({int(a.group(1)):1})
continue continue
# EMAIL (-) # EMAIL (-)
elif re.search('^-.*', message): # is email command elif re.search('^-.*', message): # is email command
searchstring = '^' + BASECALLSIGN + '.*' searchstring = '^' + CONFIG['ham']['callsign'] + '.*'
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:
@ -431,7 +493,7 @@ def main():
to_addr = a.group(1) to_addr = a.group(1)
content = a.group(2) content = a.group(2)
if content == 'mapme': # send recipient link to aprs.fi map if content == 'mapme': # send recipient link to aprs.fi map
content = "Click for my location: http://aprs.fi/" + BASECALLSIGN content = "Click for my location: http://aprs.fi/" + CONFIG['ham']['callsign']
too_soon = 0 too_soon = 0
now = time.time() now = time.time()
if ack in email_sent_dict: # see if we sent this msg number recently if ack in email_sent_dict: # see if we sent this msg number recently
@ -445,11 +507,11 @@ def main():
else: else:
#send_message(fromcall, "-" + to_addr + " sent") #send_message(fromcall, "-" + to_addr + " sent")
if len(email_sent_dict) > 98: # clear email sent dictionary if somehow goes over 100 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." LOG.debug("DEBUG: email_sent_dict is big (" + str(len(email_sent_dict)) + ") clearing out.")
email_sent_dict.clear() email_sent_dict.clear()
email_sent_dict[ack] = now email_sent_dict[ack] = now
else: else:
print "\nEmail for message number " + ack + " recently sent, not sending again.\n" LOG.info("Email for message number " + ack + " recently sent, not sending again.")
else: else:
send_message(fromcall, "Bad email address") send_message(fromcall, "Bad email address")
@ -534,9 +596,9 @@ def main():
send_ack(fromcall, ack) # send an ack last send_ack(fromcall, ack) # send an ack last
except Exception, e: except Exception, e:
print "Error in mainline loop:" LOG.error("Error in mainline loop:")
print "%s" % str(e) LOG.error("%s" % str(e))
print "Exiting." LOG.error("Exiting.")
#sys.exit(1) # merely a suggestion #sys.exit(1) # merely a suggestion
os._exit(1) os._exit(1)
@ -546,4 +608,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
main() main(args)

View File

@ -1,12 +1,20 @@
"""Utilities and helper functions.""" """Utilities and helper functions."""
import logging
import os import os
import pprint
import sys import sys
import yaml import yaml
# an example of what should be in the ~/.aprsd/config.yml # an example of what should be in the ~/.aprsd/config.yml
example_config = ''' example_config = '''
ham:
callsign: KFART
aprs:
login: someusername
password: password
host: noam.aprs2.net
shortcuts: shortcuts:
'aa': '5551239999@vtext.com' 'aa': '5551239999@vtext.com'
'cl': 'craiglamparter@somedomain.org' 'cl': 'craiglamparter@somedomain.org'
@ -19,12 +27,10 @@ smtp:
imap: imap:
login: imapuser login: imapuser
password: something dumb password: something dumb
ham:
callsign: something
basename: somebasename
''' '''
log = logging.getLogger('APRSD')
def env(*vars, **kwargs): def env(*vars, **kwargs):
"""This returns the first environment variable set. """This returns the first environment variable set.
if none are non-empty, defaults to '' or keyword arg default if none are non-empty, defaults to '' or keyword arg default
@ -44,6 +50,6 @@ def get_config():
config = yaml.load(stream) config = yaml.load(stream)
return config return config
else: else:
print("%s is missing, please create a config file" % config_file) log.critical("%s is missing, please create a config file" % config_file)
print("example config is\n %s" % example_config) print("\nCopy to ~/.aprsd/config.yml and edit\n\nSample config:\n %s" % example_config)
sys.exit(-1) sys.exit(-1)