2020-12-09 08:54:17 -05:00
|
|
|
# -*- coding: utf-8 -*-
|
2018-06-13 11:58:56 -04:00
|
|
|
#
|
|
|
|
# Listen on amateur radio aprs-is network for messages and respond to them.
|
2018-11-29 08:22:41 -05:00
|
|
|
# You must have an amateur radio callsign to use this software. You must
|
|
|
|
# create an ~/.aprsd/config.yml file with all of the required settings. To
|
|
|
|
# generate an example config.yml, just run aprsd, then copy the sample config
|
|
|
|
# to ~/.aprsd/config.yml and edit the settings.
|
2018-06-13 11:58:56 -04:00
|
|
|
#
|
|
|
|
# APRS messages:
|
2018-11-21 14:29:15 -05:00
|
|
|
# l(ocation) = descriptive location of calling station
|
|
|
|
# w(eather) = temp, (hi/low) forecast, later forecast
|
2018-11-21 14:56:35 -05:00
|
|
|
# t(ime) = respond with the current time
|
2018-06-13 11:58:56 -04:00
|
|
|
# f(ortune) = respond with a short fortune
|
|
|
|
# -email_addr email text = send an email
|
|
|
|
# -2 = display the last 2 emails received
|
2018-11-21 14:29:15 -05:00
|
|
|
# p(ing) = respond with Pong!/time
|
2018-06-13 11:58:56 -04:00
|
|
|
# anything else = respond with usage
|
|
|
|
#
|
|
|
|
# (C)2018 Craig Lamparter
|
|
|
|
# License GPLv2
|
2017-10-31 12:58:06 -04:00
|
|
|
#
|
|
|
|
|
2018-11-21 15:55:14 -05:00
|
|
|
# python included libs
|
2018-11-29 08:22:41 -05:00
|
|
|
import logging
|
2018-06-13 11:58:56 -04:00
|
|
|
import os
|
2020-12-20 12:14:51 -05:00
|
|
|
import random
|
2018-11-29 08:22:41 -05:00
|
|
|
import signal
|
|
|
|
import sys
|
|
|
|
import time
|
2020-12-17 10:00:47 -05:00
|
|
|
from logging import NullHandler
|
2020-12-09 14:13:35 -05:00
|
|
|
from logging.handlers import RotatingFileHandler
|
2018-11-29 08:22:41 -05:00
|
|
|
|
2020-12-17 10:00:47 -05:00
|
|
|
import aprslib
|
2020-12-09 08:54:17 -05:00
|
|
|
import click
|
|
|
|
import click_completion
|
2020-12-09 14:13:35 -05:00
|
|
|
import yaml
|
2018-06-13 11:58:56 -04:00
|
|
|
|
2018-11-21 15:55:14 -05:00
|
|
|
# local imports here
|
2020-12-04 08:56:26 -05:00
|
|
|
import aprsd
|
2020-12-17 10:00:47 -05:00
|
|
|
from aprsd import client, email, messaging, plugin, utils
|
2018-11-21 15:55:14 -05:00
|
|
|
|
2018-11-29 08:22:41 -05:00
|
|
|
# setup the global logger
|
2020-12-17 10:00:47 -05:00
|
|
|
# logging.basicConfig(level=logging.DEBUG) # level=10
|
2020-12-09 14:13:35 -05:00
|
|
|
LOG = logging.getLogger("APRSD")
|
2018-11-29 08:22:41 -05:00
|
|
|
|
2020-12-17 10:00:47 -05:00
|
|
|
LOG_LEVELS = {
|
|
|
|
"CRITICAL": logging.CRITICAL,
|
|
|
|
"ERROR": logging.ERROR,
|
|
|
|
"WARNING": logging.WARNING,
|
|
|
|
"INFO": logging.INFO,
|
|
|
|
"DEBUG": logging.DEBUG,
|
|
|
|
}
|
2018-11-29 08:22:41 -05:00
|
|
|
|
2020-12-20 12:14:51 -05:00
|
|
|
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
|
|
|
|
|
2018-11-21 14:29:15 -05:00
|
|
|
# localization, please edit:
|
2018-11-29 08:22:41 -05:00
|
|
|
# HOST = "noam.aprs2.net" # north america tier2 servers round robin
|
|
|
|
# USER = "KM6XXX-9" # callsign of this aprs client with SSID
|
|
|
|
# PASS = "99999" # google how to generate this
|
2019-05-15 16:12:59 -04:00
|
|
|
# BASECALLSIGN = "KM6XXX" # callsign of radio in the field to send email
|
2018-11-29 08:22:41 -05:00
|
|
|
# shortcuts = {
|
|
|
|
# "aa" : "5551239999@vtext.com",
|
|
|
|
# "cl" : "craiglamparter@somedomain.org",
|
|
|
|
# "wb" : "5553909472@vtext.com"
|
|
|
|
# }
|
2018-06-13 11:58:56 -04:00
|
|
|
|
2018-11-21 16:03:28 -05:00
|
|
|
|
2020-12-09 08:54:17 -05:00
|
|
|
def custom_startswith(string, incomplete):
|
|
|
|
"""A custom completion match that supports case insensitive matching."""
|
2020-12-09 14:13:35 -05:00
|
|
|
if os.environ.get("_CLICK_COMPLETION_COMMAND_CASE_INSENSITIVE_COMPLETE"):
|
2020-12-09 08:54:17 -05:00
|
|
|
string = string.lower()
|
|
|
|
incomplete = incomplete.lower()
|
|
|
|
return string.startswith(incomplete)
|
|
|
|
|
|
|
|
|
|
|
|
click_completion.core.startswith = custom_startswith
|
|
|
|
click_completion.init()
|
|
|
|
|
|
|
|
|
|
|
|
cmd_help = """Shell completion for click-completion-command
|
|
|
|
Available shell types:
|
|
|
|
\b
|
|
|
|
%s
|
|
|
|
Default type: auto
|
2020-12-09 14:13:35 -05:00
|
|
|
""" % "\n ".join(
|
|
|
|
"{:<12} {}".format(k, click_completion.core.shells[k])
|
|
|
|
for k in sorted(click_completion.core.shells.keys())
|
|
|
|
)
|
|
|
|
|
2020-12-09 08:54:17 -05:00
|
|
|
|
2020-12-20 12:14:51 -05:00
|
|
|
@click.group(help=cmd_help, context_settings=CONTEXT_SETTINGS)
|
2020-12-09 08:54:17 -05:00
|
|
|
@click.version_option()
|
|
|
|
def main():
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
@main.command()
|
2020-12-09 14:13:35 -05:00
|
|
|
@click.option(
|
|
|
|
"-i", "--case-insensitive/--no-case-insensitive", help="Case insensitive completion"
|
|
|
|
)
|
|
|
|
@click.argument(
|
|
|
|
"shell",
|
|
|
|
required=False,
|
|
|
|
type=click_completion.DocumentedChoice(click_completion.core.shells),
|
|
|
|
)
|
2020-12-09 08:54:17 -05:00
|
|
|
def show(shell, case_insensitive):
|
|
|
|
"""Show the click-completion-command completion code"""
|
2020-12-09 14:13:35 -05:00
|
|
|
extra_env = (
|
|
|
|
{"_CLICK_COMPLETION_COMMAND_CASE_INSENSITIVE_COMPLETE": "ON"}
|
|
|
|
if case_insensitive
|
|
|
|
else {}
|
|
|
|
)
|
2020-12-09 08:54:17 -05:00
|
|
|
click.echo(click_completion.core.get_code(shell, extra_env=extra_env))
|
|
|
|
|
|
|
|
|
|
|
|
@main.command()
|
2020-12-09 14:13:35 -05:00
|
|
|
@click.option(
|
|
|
|
"--append/--overwrite", help="Append the completion code to the file", default=None
|
|
|
|
)
|
|
|
|
@click.option(
|
|
|
|
"-i", "--case-insensitive/--no-case-insensitive", help="Case insensitive completion"
|
|
|
|
)
|
|
|
|
@click.argument(
|
|
|
|
"shell",
|
|
|
|
required=False,
|
|
|
|
type=click_completion.DocumentedChoice(click_completion.core.shells),
|
|
|
|
)
|
|
|
|
@click.argument("path", required=False)
|
2020-12-09 08:54:17 -05:00
|
|
|
def install(append, case_insensitive, shell, path):
|
|
|
|
"""Install the click-completion-command completion"""
|
2020-12-09 14:13:35 -05:00
|
|
|
extra_env = (
|
|
|
|
{"_CLICK_COMPLETION_COMMAND_CASE_INSENSITIVE_COMPLETE": "ON"}
|
|
|
|
if case_insensitive
|
|
|
|
else {}
|
|
|
|
)
|
|
|
|
shell, path = click_completion.core.install(
|
|
|
|
shell=shell, path=path, append=append, extra_env=extra_env
|
|
|
|
)
|
|
|
|
click.echo("%s completion installed in %s" % (shell, path))
|
2018-11-29 08:22:41 -05:00
|
|
|
|
|
|
|
|
2018-06-13 11:58:56 -04:00
|
|
|
def signal_handler(signal, frame):
|
2018-11-29 08:22:41 -05:00
|
|
|
LOG.info("Ctrl+C, exiting.")
|
2019-05-15 16:12:59 -04:00
|
|
|
# sys.exit(0) # thread ignores this
|
2018-11-29 08:22:41 -05:00
|
|
|
os._exit(0)
|
2020-12-09 14:13:35 -05:00
|
|
|
|
|
|
|
|
2020-12-08 12:27:24 -05:00
|
|
|
# end signal_handler
|
2018-11-29 08:22:41 -05:00
|
|
|
|
2019-05-15 16:12:59 -04:00
|
|
|
|
2018-11-29 08:22:41 -05:00
|
|
|
# Setup the logging faciility
|
|
|
|
# to disable logging to stdout, but still log to file
|
|
|
|
# use the --quiet option on the cmdln
|
2020-12-17 10:00:47 -05:00
|
|
|
def setup_logging(config, loglevel, quiet):
|
|
|
|
log_level = LOG_LEVELS[loglevel]
|
2018-11-29 08:22:41 -05:00
|
|
|
LOG.setLevel(log_level)
|
2020-12-15 11:10:46 -05:00
|
|
|
log_format = "[%(asctime)s] [%(threadName)-12s] [%(levelname)-5.5s]" " %(message)s"
|
2020-12-09 14:13:35 -05:00
|
|
|
date_format = "%m/%d/%Y %I:%M:%S %p"
|
|
|
|
log_formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
|
2020-12-17 10:00:47 -05:00
|
|
|
log_file = config["aprs"].get("logfile", None)
|
|
|
|
if log_file:
|
|
|
|
fh = RotatingFileHandler(log_file, maxBytes=(10248576 * 5), backupCount=4)
|
|
|
|
else:
|
|
|
|
fh = NullHandler()
|
2020-12-19 16:35:53 -05:00
|
|
|
|
2018-11-29 08:22:41 -05:00
|
|
|
fh.setFormatter(log_formatter)
|
|
|
|
LOG.addHandler(fh)
|
|
|
|
|
2020-12-09 08:54:17 -05:00
|
|
|
if not quiet:
|
2018-11-29 08:22:41 -05:00
|
|
|
sh = logging.StreamHandler(sys.stdout)
|
|
|
|
sh.setFormatter(log_formatter)
|
|
|
|
LOG.addHandler(sh)
|
|
|
|
|
2018-11-21 14:29:15 -05:00
|
|
|
|
2020-12-21 11:57:54 -05:00
|
|
|
def process_ack_packet(packet):
|
|
|
|
ack_num = packet.get("msgNo")
|
|
|
|
LOG.info("Got ack for message {}".format(ack_num))
|
|
|
|
messaging.log_message(
|
|
|
|
"ACK", packet["raw"], None, ack=ack_num, fromcall=packet["from"]
|
|
|
|
)
|
|
|
|
messaging.ack_dict.update({int(ack_num): 1})
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def process_mic_e_packet(packet):
|
|
|
|
LOG.info("Mic-E Packet detected. Currenlty unsupported.")
|
|
|
|
messaging.log_packet(packet)
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def process_message_packet(packet):
|
|
|
|
LOG.info("Got a message packet")
|
|
|
|
fromcall = packet["from"]
|
|
|
|
message = packet.get("message_text", None)
|
|
|
|
|
|
|
|
msg_number = packet.get("msgNo", None)
|
|
|
|
if msg_number:
|
|
|
|
ack = msg_number
|
|
|
|
else:
|
|
|
|
ack = "0"
|
|
|
|
|
|
|
|
messaging.log_message(
|
|
|
|
"Received Message", packet["raw"], message, fromcall=fromcall, ack=ack
|
|
|
|
)
|
|
|
|
|
|
|
|
found_command = False
|
|
|
|
# Get singleton of the PM
|
|
|
|
pm = plugin.PluginManager()
|
|
|
|
try:
|
|
|
|
results = pm.run(fromcall=fromcall, message=message, ack=ack)
|
|
|
|
for reply in results:
|
|
|
|
found_command = True
|
|
|
|
# A plugin can return a null message flag which signals
|
|
|
|
# us that they processed the message correctly, but have
|
|
|
|
# nothing to reply with, so we avoid replying with a usage string
|
|
|
|
if reply is not messaging.NULL_MESSAGE:
|
|
|
|
LOG.debug("Sending '{}'".format(reply))
|
|
|
|
messaging.send_message(fromcall, reply)
|
|
|
|
else:
|
|
|
|
LOG.debug("Got NULL MESSAGE from plugin")
|
|
|
|
|
|
|
|
if not found_command:
|
|
|
|
plugins = pm.get_plugins()
|
|
|
|
names = [x.command_name for x in plugins]
|
|
|
|
names.sort()
|
|
|
|
|
|
|
|
reply = "Usage: {}".format(", ".join(names))
|
|
|
|
messaging.send_message(fromcall, reply)
|
|
|
|
except Exception as ex:
|
|
|
|
LOG.exception("Plugin failed!!!", ex)
|
|
|
|
reply = "A Plugin failed! try again?"
|
|
|
|
messaging.send_message(fromcall, reply)
|
|
|
|
|
|
|
|
# let any threads do their thing, then ack
|
|
|
|
# send an ack last
|
|
|
|
messaging.send_ack(fromcall, ack)
|
|
|
|
LOG.debug("Packet processing complete")
|
|
|
|
|
|
|
|
|
2020-12-17 10:00:47 -05:00
|
|
|
def process_packet(packet):
|
|
|
|
"""Process a packet recieved from aprs-is server."""
|
2020-12-09 08:54:17 -05:00
|
|
|
|
2020-12-17 10:00:47 -05:00
|
|
|
LOG.debug("Process packet!")
|
|
|
|
try:
|
|
|
|
LOG.debug("Got message: {}".format(packet))
|
2020-12-09 08:54:17 -05:00
|
|
|
|
2020-12-21 11:57:54 -05:00
|
|
|
msg = packet.get("message_text", None)
|
|
|
|
msg_format = packet.get("format", None)
|
|
|
|
msg_response = packet.get("response", None)
|
|
|
|
if msg_format == "message" and msg:
|
|
|
|
# we want to send the message through the
|
|
|
|
# plugins
|
|
|
|
process_message_packet(packet)
|
|
|
|
return
|
|
|
|
elif msg_response == "ack":
|
|
|
|
process_ack_packet(packet)
|
|
|
|
return
|
|
|
|
|
|
|
|
if msg_format == "mic-e":
|
|
|
|
# process a mic-e packet
|
|
|
|
process_mic_e_packet(packet)
|
|
|
|
return
|
2020-12-17 10:00:47 -05:00
|
|
|
|
|
|
|
except (aprslib.ParseError, aprslib.UnknownFormat) as exp:
|
|
|
|
LOG.exception("Failed to parse packet from aprs-is", exp)
|
2020-12-11 14:23:08 -05:00
|
|
|
|
2020-12-17 10:00:47 -05:00
|
|
|
|
|
|
|
@main.command()
|
|
|
|
def sample_config():
|
|
|
|
"""This dumps the config to stdout."""
|
|
|
|
click.echo(yaml.dump(utils.DEFAULT_CONFIG_DICT))
|
2020-12-11 14:23:08 -05:00
|
|
|
|
|
|
|
|
2020-12-19 16:35:53 -05:00
|
|
|
@main.command()
|
|
|
|
@click.option(
|
|
|
|
"--loglevel",
|
|
|
|
default="DEBUG",
|
|
|
|
show_default=True,
|
|
|
|
type=click.Choice(
|
|
|
|
["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"], case_sensitive=False
|
|
|
|
),
|
|
|
|
show_choices=True,
|
|
|
|
help="The log level to use for aprsd.log",
|
|
|
|
)
|
|
|
|
@click.option("--quiet", is_flag=True, default=False, help="Don't log to stdout")
|
|
|
|
@click.option(
|
|
|
|
"-c",
|
|
|
|
"--config",
|
|
|
|
"config_file",
|
|
|
|
show_default=True,
|
|
|
|
default=utils.DEFAULT_CONFIG_FILE,
|
|
|
|
help="The aprsd config file to use for options.",
|
|
|
|
)
|
|
|
|
@click.option(
|
|
|
|
"--aprs-login",
|
|
|
|
envvar="APRS_LOGIN",
|
|
|
|
show_envvar=True,
|
|
|
|
help="What callsign to send the message from.",
|
|
|
|
)
|
|
|
|
@click.option(
|
|
|
|
"--aprs-password",
|
|
|
|
envvar="APRS_PASSWORD",
|
|
|
|
show_envvar=True,
|
|
|
|
help="the APRS-IS password for APRS_LOGIN",
|
|
|
|
)
|
|
|
|
@click.argument("tocallsign")
|
2020-12-20 12:14:51 -05:00
|
|
|
@click.argument("command", nargs=-1)
|
2020-12-19 16:35:53 -05:00
|
|
|
def send_message(
|
|
|
|
loglevel, quiet, config_file, aprs_login, aprs_password, tocallsign, command
|
|
|
|
):
|
|
|
|
"""Send a message to a callsign via APRS_IS."""
|
2020-12-20 16:33:18 -05:00
|
|
|
global got_ack, got_response
|
|
|
|
|
2020-12-19 16:35:53 -05:00
|
|
|
click.echo("{} {} {} {}".format(aprs_login, aprs_password, tocallsign, command))
|
|
|
|
|
|
|
|
click.echo("Load config")
|
|
|
|
config = utils.parse_config(config_file)
|
|
|
|
if not aprs_login:
|
|
|
|
click.echo("Must set --aprs_login or APRS_LOGIN")
|
|
|
|
return
|
|
|
|
|
|
|
|
if not aprs_password:
|
|
|
|
click.echo("Must set --aprs-password or APRS_PASSWORD")
|
|
|
|
return
|
|
|
|
|
|
|
|
config["aprs"]["login"] = aprs_login
|
|
|
|
config["aprs"]["password"] = aprs_password
|
|
|
|
messaging.CONFIG = config
|
|
|
|
|
|
|
|
setup_logging(config, loglevel, quiet)
|
|
|
|
LOG.info("APRSD Started version: {}".format(aprsd.__version__))
|
2020-12-20 12:14:51 -05:00
|
|
|
message_number = random.randint(1, 90)
|
|
|
|
if type(command) is tuple:
|
|
|
|
command = " ".join(command)
|
|
|
|
LOG.info("Sending Command '{}'".format(command))
|
2020-12-19 16:35:53 -05:00
|
|
|
|
2020-12-20 16:33:18 -05:00
|
|
|
got_ack = False
|
|
|
|
got_response = False
|
|
|
|
|
2020-12-19 16:35:53 -05:00
|
|
|
def rx_packet(packet):
|
2020-12-20 16:33:18 -05:00
|
|
|
global got_ack, got_response
|
2020-12-20 12:14:51 -05:00
|
|
|
# LOG.debug("Got packet back {}".format(packet))
|
2020-12-19 16:35:53 -05:00
|
|
|
resp = packet.get("response", None)
|
|
|
|
if resp == "ack":
|
2020-12-20 16:33:18 -05:00
|
|
|
ack_num = packet.get("msgNo")
|
|
|
|
LOG.info("We got ack for our sent message {}".format(ack_num))
|
|
|
|
messaging.log_packet(packet)
|
|
|
|
got_ack = True
|
|
|
|
else:
|
|
|
|
message = packet.get("message_text", None)
|
|
|
|
LOG.info("We got a new message")
|
|
|
|
fromcall = packet["from"]
|
|
|
|
msg_number = packet.get("msgNo", None)
|
|
|
|
if msg_number:
|
|
|
|
ack = msg_number
|
|
|
|
else:
|
|
|
|
ack = "0"
|
|
|
|
messaging.log_message(
|
|
|
|
"Received Message", packet["raw"], message, fromcall=fromcall, ack=ack
|
|
|
|
)
|
|
|
|
got_response = True
|
|
|
|
# Send the ack back?
|
|
|
|
messaging.send_ack_direct(fromcall, ack)
|
|
|
|
|
|
|
|
if got_ack and got_response:
|
2020-12-19 16:35:53 -05:00
|
|
|
sys.exit(0)
|
|
|
|
|
|
|
|
cl = client.Client(config)
|
2020-12-20 16:33:18 -05:00
|
|
|
|
|
|
|
# Send a message
|
|
|
|
# then we setup a consumer to rx messages
|
|
|
|
# We should get an ack back as well as a new message
|
|
|
|
# we should bail after we get the ack and send an ack back for the
|
|
|
|
# message
|
2020-12-20 12:14:51 -05:00
|
|
|
messaging.send_message_direct(tocallsign, command, message_number)
|
2020-12-19 16:35:53 -05:00
|
|
|
|
|
|
|
try:
|
|
|
|
# This will register a packet consumer with aprslib
|
|
|
|
# When new packets come in the consumer will process
|
|
|
|
# the packet
|
|
|
|
aprs_client = client.get_client()
|
|
|
|
aprs_client.consumer(rx_packet, raw=False)
|
|
|
|
except aprslib.exceptions.ConnectionDrop:
|
|
|
|
LOG.error("Connection dropped, reconnecting")
|
|
|
|
time.sleep(5)
|
|
|
|
# Force the deletion of the client object connected to aprs
|
|
|
|
# This will cause a reconnect, next time client.get_client()
|
|
|
|
# is called
|
|
|
|
cl.reset()
|
|
|
|
|
|
|
|
|
2019-05-15 16:12:59 -04:00
|
|
|
# main() ###
|
2020-12-09 08:54:17 -05:00
|
|
|
@main.command()
|
2020-12-09 14:13:35 -05:00
|
|
|
@click.option(
|
|
|
|
"--loglevel",
|
|
|
|
default="DEBUG",
|
|
|
|
show_default=True,
|
|
|
|
type=click.Choice(
|
|
|
|
["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"], case_sensitive=False
|
|
|
|
),
|
|
|
|
show_choices=True,
|
|
|
|
help="The log level to use for aprsd.log",
|
|
|
|
)
|
|
|
|
@click.option("--quiet", is_flag=True, default=False, help="Don't log to stdout")
|
2020-12-19 16:35:53 -05:00
|
|
|
@click.option(
|
|
|
|
"--disable-validation",
|
|
|
|
is_flag=True,
|
|
|
|
default=False,
|
|
|
|
help="Disable email shortcut validation. Bad email addresses can result in broken email responses!!",
|
|
|
|
)
|
2020-12-09 14:13:35 -05:00
|
|
|
@click.option(
|
|
|
|
"-c",
|
|
|
|
"--config",
|
|
|
|
"config_file",
|
|
|
|
show_default=True,
|
|
|
|
default=utils.DEFAULT_CONFIG_FILE,
|
|
|
|
help="The aprsd config file to use for options.",
|
|
|
|
)
|
2020-12-19 16:35:53 -05:00
|
|
|
def server(loglevel, quiet, disable_validation, config_file):
|
2020-12-09 08:54:17 -05:00
|
|
|
"""Start the aprsd server process."""
|
2018-11-29 08:22:41 -05:00
|
|
|
|
|
|
|
signal.signal(signal.SIGINT, signal_handler)
|
2020-12-17 10:00:47 -05:00
|
|
|
|
|
|
|
click.echo("Load config")
|
|
|
|
config = utils.parse_config(config_file)
|
|
|
|
|
|
|
|
# Force setting the config to the modules that need it
|
|
|
|
# TODO(Walt): convert these modules to classes that can
|
|
|
|
# Accept the config as a constructor param, instead of this
|
|
|
|
# hacky global setting
|
|
|
|
email.CONFIG = config
|
|
|
|
messaging.CONFIG = config
|
|
|
|
|
|
|
|
setup_logging(config, loglevel, quiet)
|
2020-12-04 08:56:26 -05:00
|
|
|
LOG.info("APRSD Started version: {}".format(aprsd.__version__))
|
2018-11-29 08:22:41 -05:00
|
|
|
|
2020-12-17 10:00:47 -05:00
|
|
|
# TODO(walt): Make email processing/checking optional?
|
|
|
|
# Maybe someone only wants this to process messages with plugins only.
|
2020-12-19 16:35:53 -05:00
|
|
|
valid = email.validate_email_config(config, disable_validation)
|
2020-12-04 08:56:26 -05:00
|
|
|
if not valid:
|
|
|
|
LOG.error("Failed to validate email config options")
|
|
|
|
sys.exit(-1)
|
2018-11-29 08:22:41 -05:00
|
|
|
|
2020-12-17 10:00:47 -05:00
|
|
|
# start the email thread
|
|
|
|
email.start_thread()
|
2018-11-29 08:22:41 -05:00
|
|
|
|
2020-12-17 10:00:47 -05:00
|
|
|
# Create the initial PM singleton and Register plugins
|
|
|
|
plugin_manager = plugin.PluginManager(config)
|
|
|
|
plugin_manager.setup_plugins()
|
|
|
|
cl = client.Client(config)
|
2020-12-06 14:54:11 -05:00
|
|
|
|
2020-12-17 10:00:47 -05:00
|
|
|
# setup and run the main blocking loop
|
2018-11-29 08:22:41 -05:00
|
|
|
while True:
|
2020-12-17 10:00:47 -05:00
|
|
|
# Now use the helper which uses the singleton
|
|
|
|
aprs_client = client.get_client()
|
|
|
|
|
|
|
|
# setup the consumer of messages and block until a messages
|
2018-11-29 08:22:41 -05:00
|
|
|
try:
|
2020-12-17 10:00:47 -05:00
|
|
|
# This will register a packet consumer with aprslib
|
|
|
|
# When new packets come in the consumer will process
|
|
|
|
# the packet
|
|
|
|
aprs_client.consumer(process_packet, raw=False)
|
|
|
|
except aprslib.exceptions.ConnectionDrop:
|
|
|
|
LOG.error("Connection dropped, reconnecting")
|
2020-12-04 12:43:11 -05:00
|
|
|
time.sleep(5)
|
2020-12-17 10:00:47 -05:00
|
|
|
# Force the deletion of the client object connected to aprs
|
|
|
|
# This will cause a reconnect, next time client.get_client()
|
|
|
|
# is called
|
|
|
|
cl.reset()
|
2020-12-05 21:59:58 -05:00
|
|
|
|
2017-10-31 12:58:06 -04:00
|
|
|
|
2018-11-21 14:56:35 -05:00
|
|
|
if __name__ == "__main__":
|
2020-12-09 08:54:17 -05:00
|
|
|
main()
|