mirror of
https://github.com/craigerl/aprsd.git
synced 2024-11-21 23:55:17 -05:00
Convert config to oslo_config
This patch is the initial conversion of the custom config and config file yaml format to oslo_config's configuration mechanism. The resulting config format is now an ini type file. The default location is ~/.config/aprsd/aprsd.conf This is a backwards incompatible change. You will have to rebuild the config file and edit it. Also any aprsd plugins can now define config options in code and add an setup.cfg entry_point definition oslo_config.opts = foo.conf = foo.conf:list_opts
This commit is contained in:
parent
ce3b29f990
commit
e13ca0061a
28
ChangeLog
28
ChangeLog
@ -1,9 +1,37 @@
|
|||||||
CHANGES
|
CHANGES
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
* Added rain formatting unit tests to WeatherPacket
|
||||||
|
* Fix Rain reporting in WeatherPacket send
|
||||||
|
* Removed Packet.send()
|
||||||
|
* Removed watchlist plugins
|
||||||
|
* Fix PluginManager.get\_plugins
|
||||||
|
* Cleaned up PluginManager
|
||||||
|
* Cleaned up PluginManager
|
||||||
|
* Update routing for weatherpacket
|
||||||
|
* Fix some WeatherPacket formatting
|
||||||
|
* Fix pep8 violation
|
||||||
|
* Add packet filtering for aprsd listen
|
||||||
|
* Added WeatherPacket encoding
|
||||||
|
* Updated webchat and listen for queue based RX
|
||||||
|
* reworked collecting and reporting stats
|
||||||
|
* Removed unused threading code
|
||||||
|
* Change RX packet processing to enqueu
|
||||||
|
* Make tracking objectstores work w/o initializing
|
||||||
|
* Cleaned up packet transmit class attributes
|
||||||
|
* Fix packets timestamp to int
|
||||||
|
* More messaging -> packets cleanup
|
||||||
|
* Cleaned out all references to messaging
|
||||||
|
* Added contructing a GPSPacket for sending
|
||||||
|
* cleanup webchat
|
||||||
|
* Reworked all packet processing
|
||||||
|
* Updated plugins and plugin interfaces for Packet
|
||||||
|
* Started using dataclasses to describe packets
|
||||||
|
|
||||||
v2.6.1
|
v2.6.1
|
||||||
------
|
------
|
||||||
|
|
||||||
|
* v2.6.1
|
||||||
* Fixed position report for webchat beacon
|
* Fixed position report for webchat beacon
|
||||||
* Try and fix broken 32bit qemu builds on 64bit system
|
* Try and fix broken 32bit qemu builds on 64bit system
|
||||||
* Add unit tests for webchat
|
* Add unit tests for webchat
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
# python included libs
|
# python included libs
|
||||||
import datetime
|
import datetime
|
||||||
|
from importlib.metadata import entry_points
|
||||||
|
from importlib.metadata import version as metadata_version
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import signal
|
import signal
|
||||||
@ -29,12 +31,11 @@ import time
|
|||||||
|
|
||||||
import click
|
import click
|
||||||
import click_completion
|
import click_completion
|
||||||
|
from oslo_config import cfg, generator
|
||||||
|
|
||||||
# local imports here
|
# local imports here
|
||||||
import aprsd
|
import aprsd
|
||||||
from aprsd import cli_helper
|
from aprsd import cli_helper, packets, stats, threads, utils
|
||||||
from aprsd import config as aprsd_config
|
|
||||||
from aprsd import packets, stats, threads, utils
|
|
||||||
|
|
||||||
|
|
||||||
# setup the global logger
|
# setup the global logger
|
||||||
@ -111,8 +112,32 @@ def check_version(ctx):
|
|||||||
@cli.command()
|
@cli.command()
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def sample_config(ctx):
|
def sample_config(ctx):
|
||||||
"""This dumps the config to stdout."""
|
"""Generate a sample Config file from aprsd and all installed plugins."""
|
||||||
click.echo(aprsd_config.dump_default_cfg())
|
|
||||||
|
def get_namespaces():
|
||||||
|
args = []
|
||||||
|
|
||||||
|
selected = entry_points(group="oslo.config.opts")
|
||||||
|
for entry in selected:
|
||||||
|
if "aprsd" in entry.name:
|
||||||
|
args.append("--namespace")
|
||||||
|
args.append(entry.name)
|
||||||
|
|
||||||
|
return args
|
||||||
|
|
||||||
|
args = get_namespaces()
|
||||||
|
config_version = metadata_version("oslo.config")
|
||||||
|
logging.basicConfig(level=logging.WARN)
|
||||||
|
conf = cfg.ConfigOpts()
|
||||||
|
generator.register_cli_opts(conf)
|
||||||
|
try:
|
||||||
|
conf(args, version=config_version)
|
||||||
|
except cfg.RequiredOptError:
|
||||||
|
conf.print_help()
|
||||||
|
if not sys.argv[1:]:
|
||||||
|
raise SystemExit
|
||||||
|
raise
|
||||||
|
generator.generate(conf)
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
|
@ -1,13 +1,22 @@
|
|||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
|
from pathlib import Path
|
||||||
import typing as t
|
import typing as t
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
from aprsd import config as aprsd_config
|
import aprsd
|
||||||
from aprsd.logging import log
|
from aprsd.logging import log
|
||||||
from aprsd.utils import trace
|
from aprsd.utils import trace
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
home = str(Path.home())
|
||||||
|
DEFAULT_CONFIG_DIR = f"{home}/.config/aprsd/"
|
||||||
|
DEFAULT_SAVE_FILE = f"{home}/.config/aprsd/aprsd.p"
|
||||||
|
DEFAULT_CONFIG_FILE = f"{home}/.config/aprsd/aprsd.conf"
|
||||||
|
|
||||||
|
|
||||||
F = t.TypeVar("F", bound=t.Callable[..., t.Any])
|
F = t.TypeVar("F", bound=t.Callable[..., t.Any])
|
||||||
|
|
||||||
common_options = [
|
common_options = [
|
||||||
@ -27,7 +36,7 @@ common_options = [
|
|||||||
"--config",
|
"--config",
|
||||||
"config_file",
|
"config_file",
|
||||||
show_default=True,
|
show_default=True,
|
||||||
default=aprsd_config.DEFAULT_CONFIG_FILE,
|
default=DEFAULT_CONFIG_FILE,
|
||||||
help="The aprsd config file to use for options.",
|
help="The aprsd config file to use for options.",
|
||||||
),
|
),
|
||||||
click.option(
|
click.option(
|
||||||
@ -51,16 +60,22 @@ def process_standard_options(f: F) -> F:
|
|||||||
def new_func(*args, **kwargs):
|
def new_func(*args, **kwargs):
|
||||||
ctx = args[0]
|
ctx = args[0]
|
||||||
ctx.ensure_object(dict)
|
ctx.ensure_object(dict)
|
||||||
|
if kwargs["config_file"]:
|
||||||
|
default_config_files = [kwargs["config_file"]]
|
||||||
|
else:
|
||||||
|
default_config_files = None
|
||||||
|
CONF(
|
||||||
|
[], project="aprsd", version=aprsd.__version__,
|
||||||
|
default_config_files=default_config_files,
|
||||||
|
)
|
||||||
ctx.obj["loglevel"] = kwargs["loglevel"]
|
ctx.obj["loglevel"] = kwargs["loglevel"]
|
||||||
ctx.obj["config_file"] = kwargs["config_file"]
|
ctx.obj["config_file"] = kwargs["config_file"]
|
||||||
ctx.obj["quiet"] = kwargs["quiet"]
|
ctx.obj["quiet"] = kwargs["quiet"]
|
||||||
ctx.obj["config"] = aprsd_config.parse_config(kwargs["config_file"])
|
|
||||||
log.setup_logging(
|
log.setup_logging(
|
||||||
ctx.obj["config"],
|
|
||||||
ctx.obj["loglevel"],
|
ctx.obj["loglevel"],
|
||||||
ctx.obj["quiet"],
|
ctx.obj["quiet"],
|
||||||
)
|
)
|
||||||
if ctx.obj["config"]["aprsd"].get("trace", False):
|
if CONF.trace_enabled:
|
||||||
trace.setup_tracing(["method", "api"])
|
trace.setup_tracing(["method", "api"])
|
||||||
|
|
||||||
del kwargs["loglevel"]
|
del kwargs["loglevel"]
|
||||||
|
114
aprsd/client.py
114
aprsd/client.py
@ -4,14 +4,15 @@ import time
|
|||||||
|
|
||||||
import aprslib
|
import aprslib
|
||||||
from aprslib.exceptions import LoginError
|
from aprslib.exceptions import LoginError
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
from aprsd import config as aprsd_config
|
|
||||||
from aprsd import exception
|
from aprsd import exception
|
||||||
from aprsd.clients import aprsis, kiss
|
from aprsd.clients import aprsis, kiss
|
||||||
from aprsd.packets import core, packet_list
|
from aprsd.packets import core, packet_list
|
||||||
from aprsd.utils import trace
|
from aprsd.utils import trace
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
TRANSPORT_APRSIS = "aprsis"
|
TRANSPORT_APRSIS = "aprsis"
|
||||||
TRANSPORT_TCPKISS = "tcpkiss"
|
TRANSPORT_TCPKISS = "tcpkiss"
|
||||||
@ -28,7 +29,6 @@ class Client:
|
|||||||
|
|
||||||
_instance = None
|
_instance = None
|
||||||
_client = None
|
_client = None
|
||||||
config = None
|
|
||||||
|
|
||||||
connected = False
|
connected = False
|
||||||
server_string = None
|
server_string = None
|
||||||
@ -41,11 +41,6 @@ class Client:
|
|||||||
# Put any initialization here.
|
# Put any initialization here.
|
||||||
return cls._instance
|
return cls._instance
|
||||||
|
|
||||||
def __init__(self, config=None):
|
|
||||||
"""Initialize the object instance."""
|
|
||||||
if config:
|
|
||||||
self.config = config
|
|
||||||
|
|
||||||
def set_filter(self, filter):
|
def set_filter(self, filter):
|
||||||
self.filter = filter
|
self.filter = filter
|
||||||
if self._client:
|
if self._client:
|
||||||
@ -74,12 +69,12 @@ class Client:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def is_enabled(config):
|
def is_enabled():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def transport(config):
|
def transport():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
@ -90,26 +85,38 @@ class Client:
|
|||||||
class APRSISClient(Client):
|
class APRSISClient(Client):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_enabled(config):
|
def is_enabled():
|
||||||
# Defaults to True if the enabled flag is non existent
|
# Defaults to True if the enabled flag is non existent
|
||||||
try:
|
try:
|
||||||
return config["aprs"].get("enabled", True)
|
return CONF.aprs_network.enabled
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_configured(config):
|
def is_configured():
|
||||||
if APRSISClient.is_enabled(config):
|
if APRSISClient.is_enabled():
|
||||||
# Ensure that the config vars are correctly set
|
# Ensure that the config vars are correctly set
|
||||||
config.check_option("aprs.login")
|
if not CONF.aprs_network.login:
|
||||||
config.check_option("aprs.password")
|
LOG.error("Config aprs_network.login not set.")
|
||||||
config.check_option("aprs.host")
|
raise exception.MissingConfigOptionException(
|
||||||
return True
|
"aprs_network.login is not set.",
|
||||||
|
)
|
||||||
|
if not CONF.aprs_network.password:
|
||||||
|
LOG.error("Config aprs_network.password not set.")
|
||||||
|
raise exception.MissingConfigOptionException(
|
||||||
|
"aprs_network.password is not set.",
|
||||||
|
)
|
||||||
|
if not CONF.aprs_network.host:
|
||||||
|
LOG.error("Config aprs_network.host not set.")
|
||||||
|
raise exception.MissingConfigOptionException(
|
||||||
|
"aprs_network.host is not set.",
|
||||||
|
)
|
||||||
|
|
||||||
|
return True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def transport(config):
|
def transport():
|
||||||
return TRANSPORT_APRSIS
|
return TRANSPORT_APRSIS
|
||||||
|
|
||||||
def decode_packet(self, *args, **kwargs):
|
def decode_packet(self, *args, **kwargs):
|
||||||
@ -118,10 +125,10 @@ class APRSISClient(Client):
|
|||||||
|
|
||||||
@trace.trace
|
@trace.trace
|
||||||
def setup_connection(self):
|
def setup_connection(self):
|
||||||
user = self.config["aprs"]["login"]
|
user = CONF.aprs_network.login
|
||||||
password = self.config["aprs"]["password"]
|
password = CONF.aprs_network.password
|
||||||
host = self.config["aprs"].get("host", "rotate.aprs.net")
|
host = CONF.aprs_network.host
|
||||||
port = self.config["aprs"].get("port", 14580)
|
port = CONF.aprs_network.port
|
||||||
connected = False
|
connected = False
|
||||||
backoff = 1
|
backoff = 1
|
||||||
aprs_client = None
|
aprs_client = None
|
||||||
@ -151,45 +158,43 @@ class APRSISClient(Client):
|
|||||||
class KISSClient(Client):
|
class KISSClient(Client):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_enabled(config):
|
def is_enabled():
|
||||||
"""Return if tcp or serial KISS is enabled."""
|
"""Return if tcp or serial KISS is enabled."""
|
||||||
if "kiss" not in config:
|
if CONF.kiss_serial.enabled:
|
||||||
return False
|
|
||||||
|
|
||||||
if config.get("kiss.serial.enabled", default=False):
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if config.get("kiss.tcp.enabled", default=False):
|
if CONF.kiss_tcp.enabled:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_configured(config):
|
def is_configured():
|
||||||
# Ensure that the config vars are correctly set
|
# Ensure that the config vars are correctly set
|
||||||
if KISSClient.is_enabled(config):
|
if KISSClient.is_enabled():
|
||||||
config.check_option(
|
transport = KISSClient.transport()
|
||||||
"aprsd.callsign",
|
|
||||||
default_fail=aprsd_config.DEFAULT_CONFIG_DICT["aprsd"]["callsign"],
|
|
||||||
)
|
|
||||||
transport = KISSClient.transport(config)
|
|
||||||
if transport == TRANSPORT_SERIALKISS:
|
if transport == TRANSPORT_SERIALKISS:
|
||||||
config.check_option("kiss.serial")
|
if not CONF.kiss_serial.device:
|
||||||
config.check_option("kiss.serial.device")
|
LOG.error("KISS serial enabled, but no device is set.")
|
||||||
|
raise exception.MissingConfigOptionException(
|
||||||
|
"kiss_serial.device is not set.",
|
||||||
|
)
|
||||||
elif transport == TRANSPORT_TCPKISS:
|
elif transport == TRANSPORT_TCPKISS:
|
||||||
config.check_option("kiss.tcp")
|
if not CONF.kiss_tcp.host:
|
||||||
config.check_option("kiss.tcp.host")
|
LOG.error("KISS TCP enabled, but no host is set.")
|
||||||
config.check_option("kiss.tcp.port")
|
raise exception.MissingConfigOptionException(
|
||||||
|
"kiss_tcp.host is not set.",
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
return True
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def transport(config):
|
def transport():
|
||||||
if config.get("kiss.serial.enabled", default=False):
|
if CONF.kiss_serial.enabled:
|
||||||
return TRANSPORT_SERIALKISS
|
return TRANSPORT_SERIALKISS
|
||||||
|
|
||||||
if config.get("kiss.tcp.enabled", default=False):
|
if CONF.kiss_tcp.enabled:
|
||||||
return TRANSPORT_TCPKISS
|
return TRANSPORT_TCPKISS
|
||||||
|
|
||||||
def decode_packet(self, *args, **kwargs):
|
def decode_packet(self, *args, **kwargs):
|
||||||
@ -208,7 +213,7 @@ class KISSClient(Client):
|
|||||||
|
|
||||||
@trace.trace
|
@trace.trace
|
||||||
def setup_connection(self):
|
def setup_connection(self):
|
||||||
client = kiss.KISS3Client(self.config)
|
client = kiss.KISS3Client()
|
||||||
return client
|
return client
|
||||||
|
|
||||||
|
|
||||||
@ -222,8 +227,7 @@ class ClientFactory:
|
|||||||
# Put any initialization here.
|
# Put any initialization here.
|
||||||
return cls._instance
|
return cls._instance
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self):
|
||||||
self.config = config
|
|
||||||
self._builders = {}
|
self._builders = {}
|
||||||
|
|
||||||
def register(self, key, builder):
|
def register(self, key, builder):
|
||||||
@ -231,23 +235,23 @@ class ClientFactory:
|
|||||||
|
|
||||||
def create(self, key=None):
|
def create(self, key=None):
|
||||||
if not key:
|
if not key:
|
||||||
if APRSISClient.is_enabled(self.config):
|
if APRSISClient.is_enabled():
|
||||||
key = TRANSPORT_APRSIS
|
key = TRANSPORT_APRSIS
|
||||||
elif KISSClient.is_enabled(self.config):
|
elif KISSClient.is_enabled():
|
||||||
key = KISSClient.transport(self.config)
|
key = KISSClient.transport()
|
||||||
|
|
||||||
LOG.debug(f"GET client '{key}'")
|
LOG.debug(f"GET client '{key}'")
|
||||||
builder = self._builders.get(key)
|
builder = self._builders.get(key)
|
||||||
if not builder:
|
if not builder:
|
||||||
raise ValueError(key)
|
raise ValueError(key)
|
||||||
return builder(self.config)
|
return builder()
|
||||||
|
|
||||||
def is_client_enabled(self):
|
def is_client_enabled(self):
|
||||||
"""Make sure at least one client is enabled."""
|
"""Make sure at least one client is enabled."""
|
||||||
enabled = False
|
enabled = False
|
||||||
for key in self._builders.keys():
|
for key in self._builders.keys():
|
||||||
try:
|
try:
|
||||||
enabled |= self._builders[key].is_enabled(self.config)
|
enabled |= self._builders[key].is_enabled()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -257,7 +261,7 @@ class ClientFactory:
|
|||||||
enabled = False
|
enabled = False
|
||||||
for key in self._builders.keys():
|
for key in self._builders.keys():
|
||||||
try:
|
try:
|
||||||
enabled |= self._builders[key].is_configured(self.config)
|
enabled |= self._builders[key].is_configured()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
except exception.MissingConfigOptionException as ex:
|
except exception.MissingConfigOptionException as ex:
|
||||||
@ -270,11 +274,11 @@ class ClientFactory:
|
|||||||
return enabled
|
return enabled
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def setup(config):
|
def setup():
|
||||||
"""Create and register all possible client objects."""
|
"""Create and register all possible client objects."""
|
||||||
global factory
|
global factory
|
||||||
|
|
||||||
factory = ClientFactory(config)
|
factory = ClientFactory()
|
||||||
factory.register(TRANSPORT_APRSIS, APRSISClient)
|
factory.register(TRANSPORT_APRSIS, APRSISClient)
|
||||||
factory.register(TRANSPORT_TCPKISS, KISSClient)
|
factory.register(TRANSPORT_TCPKISS, KISSClient)
|
||||||
factory.register(TRANSPORT_SERIALKISS, KISSClient)
|
factory.register(TRANSPORT_SERIALKISS, KISSClient)
|
||||||
|
@ -10,11 +10,12 @@ import sys
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
from oslo_config import cfg
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
|
|
||||||
# local imports here
|
# local imports here
|
||||||
import aprsd
|
import aprsd
|
||||||
from aprsd import cli_helper, client, packets, stats, threads, utils
|
from aprsd import cli_helper, client, packets, stats, threads
|
||||||
from aprsd.aprsd import cli
|
from aprsd.aprsd import cli
|
||||||
from aprsd.threads import rx
|
from aprsd.threads import rx
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ from aprsd.threads import rx
|
|||||||
# setup the global logger
|
# setup the global logger
|
||||||
# logging.basicConfig(level=logging.DEBUG) # level=10
|
# logging.basicConfig(level=logging.DEBUG) # level=10
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
CONF = cfg.CONF
|
||||||
console = Console()
|
console = Console()
|
||||||
|
|
||||||
|
|
||||||
@ -38,8 +40,8 @@ def signal_handler(sig, frame):
|
|||||||
|
|
||||||
|
|
||||||
class APRSDListenThread(rx.APRSDRXThread):
|
class APRSDListenThread(rx.APRSDRXThread):
|
||||||
def __init__(self, config, packet_queue, packet_filter=None):
|
def __init__(self, packet_queue, packet_filter=None):
|
||||||
super().__init__(config, packet_queue)
|
super().__init__(packet_queue)
|
||||||
self.packet_filter = packet_filter
|
self.packet_filter = packet_filter
|
||||||
|
|
||||||
def process_packet(self, *args, **kwargs):
|
def process_packet(self, *args, **kwargs):
|
||||||
@ -118,7 +120,6 @@ def listen(
|
|||||||
"""
|
"""
|
||||||
signal.signal(signal.SIGINT, signal_handler)
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
signal.signal(signal.SIGTERM, signal_handler)
|
signal.signal(signal.SIGTERM, signal_handler)
|
||||||
config = ctx.obj["config"]
|
|
||||||
|
|
||||||
if not aprs_login:
|
if not aprs_login:
|
||||||
click.echo(ctx.get_help())
|
click.echo(ctx.get_help())
|
||||||
@ -132,27 +133,19 @@ def listen(
|
|||||||
ctx.fail("Must set --aprs-password or APRS_PASSWORD")
|
ctx.fail("Must set --aprs-password or APRS_PASSWORD")
|
||||||
ctx.exit()
|
ctx.exit()
|
||||||
|
|
||||||
config["aprs"]["login"] = aprs_login
|
# CONF.aprs_network.login = aprs_login
|
||||||
config["aprs"]["password"] = aprs_password
|
# config["aprs"]["password"] = aprs_password
|
||||||
|
|
||||||
LOG.info(f"APRSD Listen Started version: {aprsd.__version__}")
|
LOG.info(f"APRSD Listen Started version: {aprsd.__version__}")
|
||||||
|
|
||||||
flat_config = utils.flatten_dict(config)
|
CONF.log_opt_values(LOG, logging.DEBUG)
|
||||||
LOG.info("Using CONFIG values:")
|
|
||||||
for x in flat_config:
|
|
||||||
if "password" in x or "aprsd.web.users.admin" in x:
|
|
||||||
LOG.info(f"{x} = XXXXXXXXXXXXXXXXXXX")
|
|
||||||
else:
|
|
||||||
LOG.info(f"{x} = {flat_config[x]}")
|
|
||||||
|
|
||||||
stats.APRSDStats(config)
|
|
||||||
|
|
||||||
# Try and load saved MsgTrack list
|
# Try and load saved MsgTrack list
|
||||||
LOG.debug("Loading saved MsgTrack object.")
|
LOG.debug("Loading saved MsgTrack object.")
|
||||||
|
|
||||||
# Initialize the client factory and create
|
# Initialize the client factory and create
|
||||||
# The correct client object ready for use
|
# The correct client object ready for use
|
||||||
client.ClientFactory.setup(config)
|
client.ClientFactory.setup()
|
||||||
# Make sure we have 1 client transport enabled
|
# Make sure we have 1 client transport enabled
|
||||||
if not client.factory.is_client_enabled():
|
if not client.factory.is_client_enabled():
|
||||||
LOG.error("No Clients are enabled in config.")
|
LOG.error("No Clients are enabled in config.")
|
||||||
@ -166,12 +159,11 @@ def listen(
|
|||||||
LOG.debug(f"Filter by '{filter}'")
|
LOG.debug(f"Filter by '{filter}'")
|
||||||
aprs_client.set_filter(filter)
|
aprs_client.set_filter(filter)
|
||||||
|
|
||||||
keepalive = threads.KeepAliveThread(config=config)
|
keepalive = threads.KeepAliveThread()
|
||||||
keepalive.start()
|
keepalive.start()
|
||||||
|
|
||||||
LOG.debug("Create APRSDListenThread")
|
LOG.debug("Create APRSDListenThread")
|
||||||
listen_thread = APRSDListenThread(
|
listen_thread = APRSDListenThread(
|
||||||
config=config,
|
|
||||||
packet_queue=threads.packet_queue,
|
packet_queue=threads.packet_queue,
|
||||||
packet_filter=packet_filter,
|
packet_filter=packet_filter,
|
||||||
)
|
)
|
||||||
|
@ -3,16 +3,16 @@ import signal
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
import click
|
import click
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
import aprsd
|
import aprsd
|
||||||
from aprsd import (
|
|
||||||
cli_helper, client, flask, packets, plugin, stats, threads, utils,
|
|
||||||
)
|
|
||||||
from aprsd import aprsd as aprsd_main
|
from aprsd import aprsd as aprsd_main
|
||||||
|
from aprsd import cli_helper, client, flask, packets, plugin, threads, utils
|
||||||
from aprsd.aprsd import cli
|
from aprsd.aprsd import cli
|
||||||
from aprsd.threads import rx
|
from aprsd.threads import rx
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
|
||||||
|
|
||||||
@ -32,10 +32,8 @@ LOG = logging.getLogger("APRSD")
|
|||||||
@cli_helper.process_standard_options
|
@cli_helper.process_standard_options
|
||||||
def server(ctx, flush):
|
def server(ctx, flush):
|
||||||
"""Start the aprsd server gateway process."""
|
"""Start the aprsd server gateway process."""
|
||||||
ctx.obj["config_file"]
|
|
||||||
loglevel = ctx.obj["loglevel"]
|
loglevel = ctx.obj["loglevel"]
|
||||||
quiet = ctx.obj["quiet"]
|
quiet = ctx.obj["quiet"]
|
||||||
config = ctx.obj["config"]
|
|
||||||
|
|
||||||
signal.signal(signal.SIGINT, aprsd_main.signal_handler)
|
signal.signal(signal.SIGINT, aprsd_main.signal_handler)
|
||||||
signal.signal(signal.SIGTERM, aprsd_main.signal_handler)
|
signal.signal(signal.SIGTERM, aprsd_main.signal_handler)
|
||||||
@ -50,19 +48,11 @@ def server(ctx, flush):
|
|||||||
LOG.info(msg)
|
LOG.info(msg)
|
||||||
LOG.info(f"APRSD Started version: {aprsd.__version__}")
|
LOG.info(f"APRSD Started version: {aprsd.__version__}")
|
||||||
|
|
||||||
flat_config = utils.flatten_dict(config)
|
CONF.log_opt_values(LOG, logging.DEBUG)
|
||||||
LOG.info("Using CONFIG values:")
|
|
||||||
for x in flat_config:
|
|
||||||
if "password" in x or "aprsd.web.users.admin" in x:
|
|
||||||
LOG.info(f"{x} = XXXXXXXXXXXXXXXXXXX")
|
|
||||||
else:
|
|
||||||
LOG.info(f"{x} = {flat_config[x]}")
|
|
||||||
|
|
||||||
stats.APRSDStats(config)
|
|
||||||
|
|
||||||
# Initialize the client factory and create
|
# Initialize the client factory and create
|
||||||
# The correct client object ready for use
|
# The correct client object ready for use
|
||||||
client.ClientFactory.setup(config)
|
client.ClientFactory.setup()
|
||||||
# Make sure we have 1 client transport enabled
|
# Make sure we have 1 client transport enabled
|
||||||
if not client.factory.is_client_enabled():
|
if not client.factory.is_client_enabled():
|
||||||
LOG.error("No Clients are enabled in config.")
|
LOG.error("No Clients are enabled in config.")
|
||||||
@ -77,30 +67,28 @@ def server(ctx, flush):
|
|||||||
client.factory.create().client
|
client.factory.create().client
|
||||||
|
|
||||||
# Now load the msgTrack from disk if any
|
# Now load the msgTrack from disk if any
|
||||||
packets.PacketList(config=config)
|
packets.PacketList()
|
||||||
if flush:
|
if flush:
|
||||||
LOG.debug("Deleting saved MsgTrack.")
|
LOG.debug("Deleting saved MsgTrack.")
|
||||||
packets.PacketTrack(config=config).flush()
|
packets.PacketTrack().flush()
|
||||||
packets.WatchList(config=config)
|
packets.WatchList()
|
||||||
packets.SeenList(config=config)
|
packets.SeenList()
|
||||||
else:
|
else:
|
||||||
# Try and load saved MsgTrack list
|
# Try and load saved MsgTrack list
|
||||||
LOG.debug("Loading saved MsgTrack object.")
|
LOG.debug("Loading saved MsgTrack object.")
|
||||||
packets.PacketTrack(config=config).load()
|
packets.PacketTrack().load()
|
||||||
packets.WatchList(config=config).load()
|
packets.WatchList().load()
|
||||||
packets.SeenList(config=config).load()
|
packets.SeenList().load()
|
||||||
|
|
||||||
# Create the initial PM singleton and Register plugins
|
# Create the initial PM singleton and Register plugins
|
||||||
LOG.info("Loading Plugin Manager and registering plugins")
|
LOG.info("Loading Plugin Manager and registering plugins")
|
||||||
plugin_manager = plugin.PluginManager(config)
|
plugin_manager = plugin.PluginManager()
|
||||||
plugin_manager.setup_plugins()
|
plugin_manager.setup_plugins()
|
||||||
|
|
||||||
rx_thread = rx.APRSDPluginRXThread(
|
rx_thread = rx.APRSDPluginRXThread(
|
||||||
packet_queue=threads.packet_queue,
|
packet_queue=threads.packet_queue,
|
||||||
config=config,
|
|
||||||
)
|
)
|
||||||
process_thread = rx.APRSDPluginProcessPacketThread(
|
process_thread = rx.APRSDPluginProcessPacketThread(
|
||||||
config=config,
|
|
||||||
packet_queue=threads.packet_queue,
|
packet_queue=threads.packet_queue,
|
||||||
)
|
)
|
||||||
rx_thread.start()
|
rx_thread.start()
|
||||||
@ -108,19 +96,19 @@ def server(ctx, flush):
|
|||||||
|
|
||||||
packets.PacketTrack().restart()
|
packets.PacketTrack().restart()
|
||||||
|
|
||||||
keepalive = threads.KeepAliveThread(config=config)
|
keepalive = threads.KeepAliveThread()
|
||||||
keepalive.start()
|
keepalive.start()
|
||||||
|
|
||||||
web_enabled = config.get("aprsd.web.enabled", default=False)
|
web_enabled = CONF.admin.web_enabled
|
||||||
|
|
||||||
if web_enabled:
|
if web_enabled:
|
||||||
aprsd_main.flask_enabled = True
|
aprsd_main.flask_enabled = True
|
||||||
(socketio, app) = flask.init_flask(config, loglevel, quiet)
|
(socketio, app) = flask.init_flask(loglevel, quiet)
|
||||||
socketio.run(
|
socketio.run(
|
||||||
app,
|
app,
|
||||||
allow_unsafe_werkzeug=True,
|
allow_unsafe_werkzeug=True,
|
||||||
host=config["aprsd"]["web"]["host"],
|
host=CONF.admin.web_ip,
|
||||||
port=config["aprsd"]["web"]["port"],
|
port=CONF.admin.web_port,
|
||||||
)
|
)
|
||||||
|
|
||||||
# If there are items in the msgTracker, then save them
|
# If there are items in the msgTracker, then save them
|
||||||
|
@ -4,10 +4,13 @@ from logging.handlers import RotatingFileHandler
|
|||||||
import queue
|
import queue
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
from aprsd import config as aprsd_config
|
from aprsd import config as aprsd_config
|
||||||
from aprsd.logging import rich as aprsd_logging
|
from aprsd.logging import rich as aprsd_logging
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
logging_queue = queue.Queue()
|
logging_queue = queue.Queue()
|
||||||
|
|
||||||
@ -15,13 +18,15 @@ logging_queue = queue.Queue()
|
|||||||
# Setup the logging faciility
|
# Setup the logging faciility
|
||||||
# to disable logging to stdout, but still log to file
|
# to disable logging to stdout, but still log to file
|
||||||
# use the --quiet option on the cmdln
|
# use the --quiet option on the cmdln
|
||||||
def setup_logging(config, loglevel, quiet):
|
def setup_logging(loglevel, quiet):
|
||||||
log_level = aprsd_config.LOG_LEVELS[loglevel]
|
log_level = aprsd_config.LOG_LEVELS[loglevel]
|
||||||
LOG.setLevel(log_level)
|
LOG.setLevel(log_level)
|
||||||
date_format = config["aprsd"].get("dateformat", aprsd_config.DEFAULT_DATE_FORMAT)
|
date_format = CONF.logging.get("date_format", aprsd_config.DEFAULT_DATE_FORMAT)
|
||||||
|
rh = None
|
||||||
|
fh = None
|
||||||
|
|
||||||
rich_logging = False
|
rich_logging = False
|
||||||
if config["aprsd"].get("rich_logging", False) and not quiet:
|
if CONF.logging.get("rich_logging", False) and not quiet:
|
||||||
log_format = "%(message)s"
|
log_format = "%(message)s"
|
||||||
log_formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
|
log_formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
|
||||||
rh = aprsd_logging.APRSDRichHandler(
|
rh = aprsd_logging.APRSDRichHandler(
|
||||||
@ -32,8 +37,8 @@ def setup_logging(config, loglevel, quiet):
|
|||||||
LOG.addHandler(rh)
|
LOG.addHandler(rh)
|
||||||
rich_logging = True
|
rich_logging = True
|
||||||
|
|
||||||
log_file = config["aprsd"].get("logfile", None)
|
log_file = CONF.logging.logfile
|
||||||
log_format = config["aprsd"].get("logformat", aprsd_config.DEFAULT_LOG_FORMAT)
|
log_format = CONF.logging.logformat
|
||||||
log_formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
|
log_formatter = logging.Formatter(fmt=log_format, datefmt=date_format)
|
||||||
|
|
||||||
if log_file:
|
if log_file:
|
||||||
@ -42,12 +47,16 @@ def setup_logging(config, loglevel, quiet):
|
|||||||
LOG.addHandler(fh)
|
LOG.addHandler(fh)
|
||||||
|
|
||||||
imap_logger = None
|
imap_logger = None
|
||||||
if config.get("aprsd.email.enabled", default=False) and config.get("aprsd.email.imap.debug", default=False):
|
if CONF.email_plugin.enabled and CONF.email_plugin.debug:
|
||||||
imap_logger = logging.getLogger("imapclient.imaplib")
|
imap_logger = logging.getLogger("imapclient.imaplib")
|
||||||
imap_logger.setLevel(log_level)
|
imap_logger.setLevel(log_level)
|
||||||
imap_logger.addHandler(fh)
|
if rh:
|
||||||
|
imap_logger.addHandler(rh)
|
||||||
|
if fh:
|
||||||
|
imap_logger.addHandler(fh)
|
||||||
|
|
||||||
if config.get("aprsd.web.enabled", default=False):
|
|
||||||
|
if CONF.admin.get("web_enabled", default=False):
|
||||||
qh = logging.handlers.QueueHandler(logging_queue)
|
qh = logging.handlers.QueueHandler(logging_queue)
|
||||||
q_log_formatter = logging.Formatter(
|
q_log_formatter = logging.Formatter(
|
||||||
fmt=aprsd_config.QUEUE_LOG_FORMAT,
|
fmt=aprsd_config.QUEUE_LOG_FORMAT,
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
import wrapt
|
import wrapt
|
||||||
|
|
||||||
from aprsd import stats, utils
|
from aprsd import stats, utils
|
||||||
from aprsd.packets import seen_list
|
from aprsd.packets import seen_list
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
|
||||||
|
|
||||||
@ -15,7 +17,6 @@ class PacketList:
|
|||||||
|
|
||||||
_instance = None
|
_instance = None
|
||||||
lock = threading.Lock()
|
lock = threading.Lock()
|
||||||
config = None
|
|
||||||
|
|
||||||
packet_list: utils.RingBuffer = utils.RingBuffer(1000)
|
packet_list: utils.RingBuffer = utils.RingBuffer(1000)
|
||||||
|
|
||||||
@ -25,17 +26,8 @@ class PacketList:
|
|||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
if cls._instance is None:
|
if cls._instance is None:
|
||||||
cls._instance = super().__new__(cls)
|
cls._instance = super().__new__(cls)
|
||||||
if "config" in kwargs:
|
|
||||||
cls._instance.config = kwargs["config"]
|
|
||||||
return cls._instance
|
return cls._instance
|
||||||
|
|
||||||
def __init__(self, config=None):
|
|
||||||
if config:
|
|
||||||
self.config = config
|
|
||||||
|
|
||||||
def _is_initialized(self):
|
|
||||||
return self.config is not None
|
|
||||||
|
|
||||||
@wrapt.synchronized(lock)
|
@wrapt.synchronized(lock)
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self.packet_list)
|
return iter(self.packet_list)
|
||||||
|
@ -2,11 +2,13 @@ import datetime
|
|||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
import wrapt
|
import wrapt
|
||||||
|
|
||||||
from aprsd.utils import objectstore
|
from aprsd.utils import objectstore
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
|
||||||
|
|
||||||
@ -16,21 +18,14 @@ class SeenList(objectstore.ObjectStoreMixin):
|
|||||||
_instance = None
|
_instance = None
|
||||||
lock = threading.Lock()
|
lock = threading.Lock()
|
||||||
data: dict = {}
|
data: dict = {}
|
||||||
config = None
|
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
if cls._instance is None:
|
if cls._instance is None:
|
||||||
cls._instance = super().__new__(cls)
|
cls._instance = super().__new__(cls)
|
||||||
if "config" in kwargs:
|
cls._instance._init_store()
|
||||||
if "config" in kwargs:
|
|
||||||
cls._instance.config = kwargs["config"]
|
|
||||||
cls._instance._init_store()
|
|
||||||
cls._instance.data = {}
|
cls._instance.data = {}
|
||||||
return cls._instance
|
return cls._instance
|
||||||
|
|
||||||
def is_initialized(self):
|
|
||||||
return self.config is not None
|
|
||||||
|
|
||||||
@wrapt.synchronized(lock)
|
@wrapt.synchronized(lock)
|
||||||
def update_seen(self, packet):
|
def update_seen(self, packet):
|
||||||
callsign = None
|
callsign = None
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
import wrapt
|
import wrapt
|
||||||
|
|
||||||
from aprsd.threads import tx
|
from aprsd.threads import tx
|
||||||
from aprsd.utils import objectstore
|
from aprsd.utils import objectstore
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
class PacketTrack(objectstore.ObjectStoreMixin):
|
class PacketTrack(objectstore.ObjectStoreMixin):
|
||||||
"""Class to keep track of outstanding text messages.
|
"""Class to keep track of outstanding text messages.
|
||||||
|
|
||||||
@ -23,7 +27,6 @@ class PacketTrack(objectstore.ObjectStoreMixin):
|
|||||||
_instance = None
|
_instance = None
|
||||||
_start_time = None
|
_start_time = None
|
||||||
lock = threading.Lock()
|
lock = threading.Lock()
|
||||||
config = None
|
|
||||||
|
|
||||||
data: dict = {}
|
data: dict = {}
|
||||||
total_tracked: int = 0
|
total_tracked: int = 0
|
||||||
@ -32,14 +35,9 @@ class PacketTrack(objectstore.ObjectStoreMixin):
|
|||||||
if cls._instance is None:
|
if cls._instance is None:
|
||||||
cls._instance = super().__new__(cls)
|
cls._instance = super().__new__(cls)
|
||||||
cls._instance._start_time = datetime.datetime.now()
|
cls._instance._start_time = datetime.datetime.now()
|
||||||
if "config" in kwargs:
|
|
||||||
cls._instance.config = kwargs["config"]
|
|
||||||
cls._instance._init_store()
|
cls._instance._init_store()
|
||||||
return cls._instance
|
return cls._instance
|
||||||
|
|
||||||
def is_initialized(self):
|
|
||||||
return self.config is not None
|
|
||||||
|
|
||||||
@wrapt.synchronized(lock)
|
@wrapt.synchronized(lock)
|
||||||
def __getitem__(self, name):
|
def __getitem__(self, name):
|
||||||
return self.data[name]
|
return self.data[name]
|
||||||
|
@ -2,12 +2,14 @@ import datetime
|
|||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
import wrapt
|
import wrapt
|
||||||
|
|
||||||
from aprsd import utils
|
from aprsd import utils
|
||||||
from aprsd.utils import objectstore
|
from aprsd.utils import objectstore
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
|
||||||
|
|
||||||
@ -17,24 +19,22 @@ class WatchList(objectstore.ObjectStoreMixin):
|
|||||||
_instance = None
|
_instance = None
|
||||||
lock = threading.Lock()
|
lock = threading.Lock()
|
||||||
data = {}
|
data = {}
|
||||||
config = None
|
|
||||||
|
|
||||||
def __new__(cls, *args, **kwargs):
|
def __new__(cls, *args, **kwargs):
|
||||||
if cls._instance is None:
|
if cls._instance is None:
|
||||||
cls._instance = super().__new__(cls)
|
cls._instance = super().__new__(cls)
|
||||||
if "config" in kwargs:
|
cls._instance._init_store()
|
||||||
cls._instance.config = kwargs["config"]
|
|
||||||
cls._instance._init_store()
|
|
||||||
cls._instance.data = {}
|
cls._instance.data = {}
|
||||||
return cls._instance
|
return cls._instance
|
||||||
|
|
||||||
def __init__(self, config=None):
|
def __init__(self, config=None):
|
||||||
if config:
|
ring_size = CONF.watch_list.packet_keep_count
|
||||||
self.config = config
|
|
||||||
|
|
||||||
ring_size = config["aprsd"]["watch_list"].get("packet_keep_count", 10)
|
if not self.is_enabled():
|
||||||
|
LOG.info("Watch List is disabled.")
|
||||||
|
|
||||||
for callsign in config["aprsd"]["watch_list"].get("callsigns", []):
|
if CONF.watch_list.callsigns:
|
||||||
|
for callsign in CONF.watch_list.callsigns:
|
||||||
call = callsign.replace("*", "")
|
call = callsign.replace("*", "")
|
||||||
# FIXME(waboring) - we should fetch the last time we saw
|
# FIXME(waboring) - we should fetch the last time we saw
|
||||||
# a beacon from a callsign or some other mechanism to find
|
# a beacon from a callsign or some other mechanism to find
|
||||||
@ -47,14 +47,8 @@ class WatchList(objectstore.ObjectStoreMixin):
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
def is_initialized(self):
|
|
||||||
return self.config is not None
|
|
||||||
|
|
||||||
def is_enabled(self):
|
def is_enabled(self):
|
||||||
if self.config and "watch_list" in self.config["aprsd"]:
|
return CONF.watch_list.enabled
|
||||||
return self.config["aprsd"]["watch_list"].get("enabled", False)
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def callsign_in_watchlist(self, callsign):
|
def callsign_in_watchlist(self, callsign):
|
||||||
return callsign in self.data
|
return callsign in self.data
|
||||||
@ -78,9 +72,8 @@ class WatchList(objectstore.ObjectStoreMixin):
|
|||||||
return str(now - self.last_seen(callsign))
|
return str(now - self.last_seen(callsign))
|
||||||
|
|
||||||
def max_delta(self, seconds=None):
|
def max_delta(self, seconds=None):
|
||||||
watch_list_conf = self.config["aprsd"]["watch_list"]
|
|
||||||
if not seconds:
|
if not seconds:
|
||||||
seconds = watch_list_conf["alert_time_seconds"]
|
seconds = CONF.watch_list.alert_time_seconds
|
||||||
max_timeout = {"seconds": seconds}
|
max_timeout = {"seconds": seconds}
|
||||||
return datetime.timedelta(**max_timeout)
|
return datetime.timedelta(**max_timeout)
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import re
|
|||||||
import textwrap
|
import textwrap
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
import pluggy
|
import pluggy
|
||||||
|
|
||||||
import aprsd
|
import aprsd
|
||||||
@ -15,6 +16,7 @@ from aprsd.packets import watch_list
|
|||||||
|
|
||||||
|
|
||||||
# setup the global logger
|
# setup the global logger
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
|
||||||
CORE_MESSAGE_PLUGINS = [
|
CORE_MESSAGE_PLUGINS = [
|
||||||
@ -211,6 +213,10 @@ class APRSDRegexCommandPluginBase(APRSDPluginBase, metaclass=abc.ABCMeta):
|
|||||||
|
|
||||||
@hookimpl
|
@hookimpl
|
||||||
def filter(self, packet: packets.core.MessagePacket):
|
def filter(self, packet: packets.core.MessagePacket):
|
||||||
|
if not self.enabled:
|
||||||
|
LOG.info(f"{self.__class__.__name__} is not enabled.")
|
||||||
|
return None
|
||||||
|
|
||||||
result = None
|
result = None
|
||||||
|
|
||||||
message = packet.get("message_text", None)
|
message = packet.get("message_text", None)
|
||||||
@ -220,7 +226,7 @@ class APRSDRegexCommandPluginBase(APRSDPluginBase, metaclass=abc.ABCMeta):
|
|||||||
# Only process messages destined for us
|
# Only process messages destined for us
|
||||||
# and is an APRS message format and has a message.
|
# and is an APRS message format and has a message.
|
||||||
if (
|
if (
|
||||||
tocall == self.config["aprs"]["login"]
|
tocall == CONF.callsign
|
||||||
and msg_format == "message"
|
and msg_format == "message"
|
||||||
and message
|
and message
|
||||||
):
|
):
|
||||||
@ -249,12 +255,11 @@ class APRSFIKEYMixin:
|
|||||||
"""Mixin class to enable checking the existence of the aprs.fi apiKey."""
|
"""Mixin class to enable checking the existence of the aprs.fi apiKey."""
|
||||||
|
|
||||||
def ensure_aprs_fi_key(self):
|
def ensure_aprs_fi_key(self):
|
||||||
try:
|
if not CONF.aprs_fi.apiKey:
|
||||||
self.config.check_option(["services", "aprs.fi", "apiKey"])
|
LOG.error("Config aprs_fi.apiKey is not set")
|
||||||
self.enabled = True
|
|
||||||
except Exception as ex:
|
|
||||||
LOG.error(f"Failed to find config aprs.fi:apikey {ex}")
|
|
||||||
self.enabled = False
|
self.enabled = False
|
||||||
|
else:
|
||||||
|
self.enabled = True
|
||||||
|
|
||||||
|
|
||||||
class HelpPlugin(APRSDRegexCommandPluginBase):
|
class HelpPlugin(APRSDRegexCommandPluginBase):
|
||||||
@ -410,21 +415,28 @@ class PluginManager:
|
|||||||
)
|
)
|
||||||
if plugin_obj:
|
if plugin_obj:
|
||||||
if isinstance(plugin_obj, APRSDWatchListPluginBase):
|
if isinstance(plugin_obj, APRSDWatchListPluginBase):
|
||||||
LOG.info(
|
if plugin_obj.enabled:
|
||||||
"Registering WatchList plugin '{}'({})".format(
|
LOG.info(
|
||||||
plugin_name,
|
"Registering WatchList plugin '{}'({})".format(
|
||||||
plugin_obj.version,
|
plugin_name,
|
||||||
),
|
plugin_obj.version,
|
||||||
)
|
),
|
||||||
self._watchlist_pm.register(plugin_obj)
|
)
|
||||||
|
self._watchlist_pm.register(plugin_obj)
|
||||||
|
else:
|
||||||
|
LOG.warning(f"Plugin {plugin_obj.__class__.__name__} is disabled")
|
||||||
else:
|
else:
|
||||||
LOG.info(
|
if plugin_obj.enabled:
|
||||||
"Registering plugin '{}'({})".format(
|
LOG.info(
|
||||||
plugin_name,
|
"Registering plugin '{}'({}) -- {}".format(
|
||||||
plugin_obj.version,
|
plugin_name,
|
||||||
),
|
plugin_obj.version,
|
||||||
)
|
plugin_obj.command_regex,
|
||||||
self._pluggy_pm.register(plugin_obj)
|
),
|
||||||
|
)
|
||||||
|
self._pluggy_pm.register(plugin_obj)
|
||||||
|
else:
|
||||||
|
LOG.warning(f"Plugin {plugin_obj.__class__.__name__} is disabled")
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
LOG.error(f"Couldn't load plugin '{plugin_name}'")
|
LOG.error(f"Couldn't load plugin '{plugin_name}'")
|
||||||
LOG.exception(ex)
|
LOG.exception(ex)
|
||||||
@ -443,7 +455,7 @@ class PluginManager:
|
|||||||
_help = HelpPlugin(self.config)
|
_help = HelpPlugin(self.config)
|
||||||
self._pluggy_pm.register(_help)
|
self._pluggy_pm.register(_help)
|
||||||
|
|
||||||
enabled_plugins = self.config["aprsd"].get("enabled_plugins", None)
|
enabled_plugins = CONF.enabled_plugins
|
||||||
if enabled_plugins:
|
if enabled_plugins:
|
||||||
for p_name in enabled_plugins:
|
for p_name in enabled_plugins:
|
||||||
self._load_plugin(p_name)
|
self._load_plugin(p_name)
|
||||||
|
@ -26,13 +26,18 @@ def get_aprs_fi(api_key, callsign):
|
|||||||
|
|
||||||
def get_weather_gov_for_gps(lat, lon):
|
def get_weather_gov_for_gps(lat, lon):
|
||||||
LOG.debug(f"Fetch station at {lat}, {lon}")
|
LOG.debug(f"Fetch station at {lat}, {lon}")
|
||||||
|
headers = requests.utils.default_headers()
|
||||||
|
headers.update(
|
||||||
|
{"User-Agent": "(aprsd, waboring@hemna.com)"},
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
url2 = (
|
url2 = (
|
||||||
"https://forecast.weather.gov/MapClick.php?lat=%s"
|
#"https://forecast.weather.gov/MapClick.php?lat=%s"
|
||||||
"&lon=%s&FcstType=json" % (lat, lon)
|
#"&lon=%s&FcstType=json" % (lat, lon)
|
||||||
|
f"https://api.weather.gov/points/{lat},{lon}"
|
||||||
)
|
)
|
||||||
LOG.debug(f"Fetching weather '{url2}'")
|
LOG.debug(f"Fetching weather '{url2}'")
|
||||||
response = requests.get(url2)
|
response = requests.get(url2, headers=headers)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LOG.error(e)
|
LOG.error(e)
|
||||||
raise Exception("Failed to get weather")
|
raise Exception("Failed to get weather")
|
||||||
|
@ -9,13 +9,16 @@ import threading
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
import imapclient
|
import imapclient
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
from aprsd import packets, plugin, stats, threads
|
from aprsd import packets, plugin, stats, threads
|
||||||
from aprsd.threads import tx
|
from aprsd.threads import tx
|
||||||
from aprsd.utils import trace
|
from aprsd.utils import trace
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
shortcuts_dict = None
|
||||||
|
|
||||||
|
|
||||||
class EmailInfo:
|
class EmailInfo:
|
||||||
@ -71,18 +74,18 @@ class EmailPlugin(plugin.APRSDRegexCommandPluginBase):
|
|||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
"""Ensure that email is enabled and start the thread."""
|
"""Ensure that email is enabled and start the thread."""
|
||||||
|
if CONF.email_plugin.enabled:
|
||||||
email_enabled = self.config["aprsd"]["email"].get("enabled", False)
|
|
||||||
if email_enabled:
|
|
||||||
self.enabled = True
|
self.enabled = True
|
||||||
|
shortcuts = _build_shortcuts_dict()
|
||||||
|
LOG.info(f"Email shortcuts {shortcuts}")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
LOG.info("Email services not enabled.")
|
LOG.info("Email services not enabled.")
|
||||||
|
self.enabled = False
|
||||||
|
|
||||||
def create_threads(self):
|
def create_threads(self):
|
||||||
if self.enabled:
|
if self.enabled:
|
||||||
return APRSDEmailThread(
|
return APRSDEmailThread()
|
||||||
config=self.config,
|
|
||||||
)
|
|
||||||
|
|
||||||
@trace.trace
|
@trace.trace
|
||||||
def process(self, packet: packets.MessagePacket):
|
def process(self, packet: packets.MessagePacket):
|
||||||
@ -97,18 +100,18 @@ class EmailPlugin(plugin.APRSDRegexCommandPluginBase):
|
|||||||
ack = packet.get("msgNo", "0")
|
ack = packet.get("msgNo", "0")
|
||||||
|
|
||||||
reply = None
|
reply = None
|
||||||
if not self.config["aprsd"]["email"].get("enabled", False):
|
if not CONF.email_plugin.enabled:
|
||||||
LOG.debug("Email is not enabled in config file ignoring.")
|
LOG.debug("Email is not enabled in config file ignoring.")
|
||||||
return "Email not enabled."
|
return "Email not enabled."
|
||||||
|
|
||||||
searchstring = "^" + self.config["ham"]["callsign"] + ".*"
|
searchstring = "^" + CONF.email_plugin.callsign + ".*"
|
||||||
# only I can do email
|
# only I can do email
|
||||||
if re.search(searchstring, fromcall):
|
if re.search(searchstring, fromcall):
|
||||||
# digits only, first one is number of emails to resend
|
# digits only, first one is number of emails to resend
|
||||||
r = re.search("^-([0-9])[0-9]*$", message)
|
r = re.search("^-([0-9])[0-9]*$", message)
|
||||||
if r is not None:
|
if r is not None:
|
||||||
LOG.debug("RESEND EMAIL")
|
LOG.debug("RESEND EMAIL")
|
||||||
resend_email(self.config, r.group(1), fromcall)
|
resend_email(r.group(1), fromcall)
|
||||||
reply = packets.NULL_MESSAGE
|
reply = packets.NULL_MESSAGE
|
||||||
# -user@address.com body of email
|
# -user@address.com body of email
|
||||||
elif re.search(r"^-([A-Za-z0-9_\-\.@]+) (.*)", message):
|
elif re.search(r"^-([A-Za-z0-9_\-\.@]+) (.*)", message):
|
||||||
@ -118,7 +121,7 @@ class EmailPlugin(plugin.APRSDRegexCommandPluginBase):
|
|||||||
to_addr = a.group(1)
|
to_addr = a.group(1)
|
||||||
content = a.group(2)
|
content = a.group(2)
|
||||||
|
|
||||||
email_address = get_email_from_shortcut(self.config, to_addr)
|
email_address = get_email_from_shortcut(to_addr)
|
||||||
if not email_address:
|
if not email_address:
|
||||||
reply = "Bad email address"
|
reply = "Bad email address"
|
||||||
return reply
|
return reply
|
||||||
@ -128,7 +131,7 @@ class EmailPlugin(plugin.APRSDRegexCommandPluginBase):
|
|||||||
content = (
|
content = (
|
||||||
"Click for my location: http://aprs.fi/{}" ""
|
"Click for my location: http://aprs.fi/{}" ""
|
||||||
).format(
|
).format(
|
||||||
self.config["ham"]["callsign"],
|
CONF.email_plugin.callsign,
|
||||||
)
|
)
|
||||||
too_soon = 0
|
too_soon = 0
|
||||||
now = time.time()
|
now = time.time()
|
||||||
@ -141,7 +144,7 @@ class EmailPlugin(plugin.APRSDRegexCommandPluginBase):
|
|||||||
too_soon = 1
|
too_soon = 1
|
||||||
if not too_soon or ack == 0:
|
if not too_soon or ack == 0:
|
||||||
LOG.info(f"Send email '{content}'")
|
LOG.info(f"Send email '{content}'")
|
||||||
send_result = send_email(self.config, to_addr, content)
|
send_result = send_email(to_addr, content)
|
||||||
reply = packets.NULL_MESSAGE
|
reply = packets.NULL_MESSAGE
|
||||||
if send_result != 0:
|
if send_result != 0:
|
||||||
reply = f"-{to_addr} failed"
|
reply = f"-{to_addr} failed"
|
||||||
@ -169,9 +172,9 @@ class EmailPlugin(plugin.APRSDRegexCommandPluginBase):
|
|||||||
return reply
|
return reply
|
||||||
|
|
||||||
|
|
||||||
def _imap_connect(config):
|
def _imap_connect():
|
||||||
imap_port = config["aprsd"]["email"]["imap"].get("port", 143)
|
imap_port = CONF.email_plugin.imap_port
|
||||||
use_ssl = config["aprsd"]["email"]["imap"].get("use_ssl", False)
|
use_ssl = CONF.email_plugin.imap_use_ssl
|
||||||
# host = CONFIG["aprsd"]["email"]["imap"]["host"]
|
# host = CONFIG["aprsd"]["email"]["imap"]["host"]
|
||||||
# msg = "{}{}:{}".format("TLS " if use_ssl else "", host, imap_port)
|
# msg = "{}{}:{}".format("TLS " if use_ssl else "", host, imap_port)
|
||||||
# LOG.debug("Connect to IMAP host {} with user '{}'".
|
# LOG.debug("Connect to IMAP host {} with user '{}'".
|
||||||
@ -179,7 +182,7 @@ def _imap_connect(config):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
server = imapclient.IMAPClient(
|
server = imapclient.IMAPClient(
|
||||||
config["aprsd"]["email"]["imap"]["host"],
|
CONF.email_plugin.imap_host,
|
||||||
port=imap_port,
|
port=imap_port,
|
||||||
use_uid=True,
|
use_uid=True,
|
||||||
ssl=use_ssl,
|
ssl=use_ssl,
|
||||||
@ -191,8 +194,8 @@ def _imap_connect(config):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
server.login(
|
server.login(
|
||||||
config["aprsd"]["email"]["imap"]["login"],
|
CONF.email_plugin.imap_login,
|
||||||
config["aprsd"]["email"]["imap"]["password"],
|
CONF.email_plugin.imap_password,
|
||||||
)
|
)
|
||||||
except (imaplib.IMAP4.error, Exception) as e:
|
except (imaplib.IMAP4.error, Exception) as e:
|
||||||
msg = getattr(e, "message", repr(e))
|
msg = getattr(e, "message", repr(e))
|
||||||
@ -208,15 +211,15 @@ def _imap_connect(config):
|
|||||||
return server
|
return server
|
||||||
|
|
||||||
|
|
||||||
def _smtp_connect(config):
|
def _smtp_connect():
|
||||||
host = config["aprsd"]["email"]["smtp"]["host"]
|
host = CONF.email_plugin.smtp_host
|
||||||
smtp_port = config["aprsd"]["email"]["smtp"]["port"]
|
smtp_port = CONF.email_plugin.smtp_port
|
||||||
use_ssl = config["aprsd"]["email"]["smtp"].get("use_ssl", False)
|
use_ssl = CONF.email_plugin.smtp_use_ssl
|
||||||
msg = "{}{}:{}".format("SSL " if use_ssl else "", host, smtp_port)
|
msg = "{}{}:{}".format("SSL " if use_ssl else "", host, smtp_port)
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
"Connect to SMTP host {} with user '{}'".format(
|
"Connect to SMTP host {} with user '{}'".format(
|
||||||
msg,
|
msg,
|
||||||
config["aprsd"]["email"]["imap"]["login"],
|
CONF.email_plugin.smtp_login,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -239,15 +242,15 @@ def _smtp_connect(config):
|
|||||||
|
|
||||||
LOG.debug(f"Connected to smtp host {msg}")
|
LOG.debug(f"Connected to smtp host {msg}")
|
||||||
|
|
||||||
debug = config["aprsd"]["email"]["smtp"].get("debug", False)
|
debug = CONF.email_plugin.debug
|
||||||
if debug:
|
if debug:
|
||||||
server.set_debuglevel(5)
|
server.set_debuglevel(5)
|
||||||
server.sendmail = trace.trace(server.sendmail)
|
server.sendmail = trace.trace(server.sendmail)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
server.login(
|
server.login(
|
||||||
config["aprsd"]["email"]["smtp"]["login"],
|
CONF.email_plugin.smtp_login,
|
||||||
config["aprsd"]["email"]["smtp"]["password"],
|
CONF.email_plugin.smtp_password,
|
||||||
)
|
)
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.error("Couldn't connect to SMTP Server")
|
LOG.error("Couldn't connect to SMTP Server")
|
||||||
@ -257,22 +260,40 @@ def _smtp_connect(config):
|
|||||||
return server
|
return server
|
||||||
|
|
||||||
|
|
||||||
def get_email_from_shortcut(config, addr):
|
def _build_shortcuts_dict():
|
||||||
if config["aprsd"]["email"].get("shortcuts", False):
|
global shortcuts_dict
|
||||||
return config["aprsd"]["email"]["shortcuts"].get(addr, addr)
|
if not shortcuts_dict:
|
||||||
|
if CONF.email_plugin.email_shortcuts:
|
||||||
|
shortcuts_dict = {}
|
||||||
|
tmp = CONF.email_plugin.email_shortcuts
|
||||||
|
for combo in tmp:
|
||||||
|
entry = combo.split("=")
|
||||||
|
shortcuts_dict[entry[0]] = entry[1]
|
||||||
|
else:
|
||||||
|
shortcuts_dict = {}
|
||||||
|
|
||||||
|
LOG.info(f"Shortcuts Dict {shortcuts_dict}")
|
||||||
|
return shortcuts_dict
|
||||||
|
|
||||||
|
|
||||||
|
def get_email_from_shortcut(addr):
|
||||||
|
if CONF.email_plugin.email_shortcuts:
|
||||||
|
shortcuts = _build_shortcuts_dict()
|
||||||
|
LOG.info(f"Shortcut lookup {addr} returns {shortcuts.get(addr, addr)}")
|
||||||
|
return shortcuts.get(addr, addr)
|
||||||
else:
|
else:
|
||||||
return addr
|
return addr
|
||||||
|
|
||||||
|
|
||||||
def validate_email_config(config, disable_validation=False):
|
def validate_email_config(disable_validation=False):
|
||||||
"""function to simply ensure we can connect to email services.
|
"""function to simply ensure we can connect to email services.
|
||||||
|
|
||||||
This helps with failing early during startup.
|
This helps with failing early during startup.
|
||||||
"""
|
"""
|
||||||
LOG.info("Checking IMAP configuration")
|
LOG.info("Checking IMAP configuration")
|
||||||
imap_server = _imap_connect(config)
|
imap_server = _imap_connect()
|
||||||
LOG.info("Checking SMTP configuration")
|
LOG.info("Checking SMTP configuration")
|
||||||
smtp_server = _smtp_connect(config)
|
smtp_server = _smtp_connect()
|
||||||
|
|
||||||
if imap_server and smtp_server:
|
if imap_server and smtp_server:
|
||||||
return True
|
return True
|
||||||
@ -376,16 +397,16 @@ def parse_email(msgid, data, server):
|
|||||||
|
|
||||||
|
|
||||||
@trace.trace
|
@trace.trace
|
||||||
def send_email(config, to_addr, content):
|
def send_email(to_addr, content):
|
||||||
shortcuts = config["aprsd"]["email"]["shortcuts"]
|
shortcuts = _build_shortcuts_dict()
|
||||||
email_address = get_email_from_shortcut(config, to_addr)
|
email_address = get_email_from_shortcut(to_addr)
|
||||||
LOG.info("Sending Email_________________")
|
LOG.info("Sending Email_________________")
|
||||||
|
|
||||||
if to_addr in shortcuts:
|
if to_addr in shortcuts:
|
||||||
LOG.info(f"To : {to_addr}")
|
LOG.info(f"To : {to_addr}")
|
||||||
to_addr = email_address
|
to_addr = email_address
|
||||||
LOG.info(f" ({to_addr})")
|
LOG.info(f" ({to_addr})")
|
||||||
subject = config["ham"]["callsign"]
|
subject = CONF.email_plugin.callsign
|
||||||
# content = content + "\n\n(NOTE: reply with one line)"
|
# content = content + "\n\n(NOTE: reply with one line)"
|
||||||
LOG.info(f"Subject : {subject}")
|
LOG.info(f"Subject : {subject}")
|
||||||
LOG.info(f"Body : {content}")
|
LOG.info(f"Body : {content}")
|
||||||
@ -395,13 +416,13 @@ def send_email(config, to_addr, content):
|
|||||||
|
|
||||||
msg = MIMEText(content)
|
msg = MIMEText(content)
|
||||||
msg["Subject"] = subject
|
msg["Subject"] = subject
|
||||||
msg["From"] = config["aprsd"]["email"]["smtp"]["login"]
|
msg["From"] = CONF.email_plugin.smtp_login
|
||||||
msg["To"] = to_addr
|
msg["To"] = to_addr
|
||||||
server = _smtp_connect(config)
|
server = _smtp_connect()
|
||||||
if server:
|
if server:
|
||||||
try:
|
try:
|
||||||
server.sendmail(
|
server.sendmail(
|
||||||
config["aprsd"]["email"]["smtp"]["login"],
|
CONF.email_plugin.smtp_login,
|
||||||
[to_addr],
|
[to_addr],
|
||||||
msg.as_string(),
|
msg.as_string(),
|
||||||
)
|
)
|
||||||
@ -415,19 +436,19 @@ def send_email(config, to_addr, content):
|
|||||||
|
|
||||||
|
|
||||||
@trace.trace
|
@trace.trace
|
||||||
def resend_email(config, count, fromcall):
|
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
|
||||||
year = date.year
|
year = date.year
|
||||||
today = f"{day}-{month}-{year}"
|
today = f"{day}-{month}-{year}"
|
||||||
|
|
||||||
shortcuts = config["aprsd"]["email"]["shortcuts"]
|
shortcuts = _build_shortcuts_dict()
|
||||||
# swap key/value
|
# swap key/value
|
||||||
shortcuts_inverted = {v: k for k, v in shortcuts.items()}
|
shortcuts_inverted = {v: k for k, v in shortcuts.items()}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
server = _imap_connect(config)
|
server = _imap_connect()
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception("Failed to Connect to IMAP. Cannot resend email ")
|
LOG.exception("Failed to Connect to IMAP. Cannot resend email ")
|
||||||
return
|
return
|
||||||
@ -467,7 +488,7 @@ def resend_email(config, count, fromcall):
|
|||||||
reply = "-" + from_addr + " * " + body.decode(errors="ignore")
|
reply = "-" + from_addr + " * " + body.decode(errors="ignore")
|
||||||
tx.send(
|
tx.send(
|
||||||
packets.MessagePacket(
|
packets.MessagePacket(
|
||||||
from_call=config["aprsd"]["callsign"],
|
from_call=CONF.callsign,
|
||||||
to_call=fromcall,
|
to_call=fromcall,
|
||||||
message_text=reply,
|
message_text=reply,
|
||||||
),
|
),
|
||||||
@ -490,7 +511,7 @@ def resend_email(config, count, fromcall):
|
|||||||
)
|
)
|
||||||
tx.send(
|
tx.send(
|
||||||
packets.MessagePacket(
|
packets.MessagePacket(
|
||||||
from_call=config["aprsd"]["callsign"],
|
from_call=CONF.callsign,
|
||||||
to_call=fromcall,
|
to_call=fromcall,
|
||||||
message_text=reply,
|
message_text=reply,
|
||||||
),
|
),
|
||||||
@ -504,9 +525,8 @@ def resend_email(config, count, fromcall):
|
|||||||
|
|
||||||
|
|
||||||
class APRSDEmailThread(threads.APRSDThread):
|
class APRSDEmailThread(threads.APRSDThread):
|
||||||
def __init__(self, config):
|
def __init__(self):
|
||||||
super().__init__("EmailThread")
|
super().__init__("EmailThread")
|
||||||
self.config = config
|
|
||||||
self.past = datetime.datetime.now()
|
self.past = datetime.datetime.now()
|
||||||
|
|
||||||
def loop(self):
|
def loop(self):
|
||||||
@ -527,7 +547,7 @@ class APRSDEmailThread(threads.APRSDThread):
|
|||||||
f"check_email_delay is {EmailInfo().delay} seconds ",
|
f"check_email_delay is {EmailInfo().delay} seconds ",
|
||||||
)
|
)
|
||||||
|
|
||||||
shortcuts = self.config["aprsd"]["email"]["shortcuts"]
|
shortcuts = _build_shortcuts_dict()
|
||||||
# swap key/value
|
# swap key/value
|
||||||
shortcuts_inverted = {v: k for k, v in shortcuts.items()}
|
shortcuts_inverted = {v: k for k, v in shortcuts.items()}
|
||||||
|
|
||||||
@ -538,7 +558,7 @@ class APRSDEmailThread(threads.APRSDThread):
|
|||||||
today = f"{day}-{month}-{year}"
|
today = f"{day}-{month}-{year}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
server = _imap_connect(self.config)
|
server = _imap_connect()
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception("IMAP Failed to connect")
|
LOG.exception("IMAP Failed to connect")
|
||||||
return True
|
return True
|
||||||
@ -611,8 +631,8 @@ class APRSDEmailThread(threads.APRSDThread):
|
|||||||
# config ham.callsign
|
# config ham.callsign
|
||||||
tx.send(
|
tx.send(
|
||||||
packets.MessagePacket(
|
packets.MessagePacket(
|
||||||
from_call=self.config["aprsd"]["callsign"],
|
from_call=CONF.callsign,
|
||||||
to_call=self.config["ham"]["callsign"],
|
to_call=CONF.email_plugin.callsign,
|
||||||
message_text=reply,
|
message_text=reply,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -2,10 +2,13 @@ import logging
|
|||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
from aprsd import packets, plugin, plugin_utils
|
from aprsd import packets, plugin, plugin_utils
|
||||||
from aprsd.utils import trace
|
from aprsd.utils import trace
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
|
||||||
|
|
||||||
@ -26,7 +29,7 @@ class LocationPlugin(plugin.APRSDRegexCommandPluginBase, plugin.APRSFIKEYMixin):
|
|||||||
message = packet.get("message_text", None)
|
message = packet.get("message_text", None)
|
||||||
# ack = packet.get("msgNo", "0")
|
# ack = packet.get("msgNo", "0")
|
||||||
|
|
||||||
api_key = self.config["services"]["aprs.fi"]["apiKey"]
|
api_key = CONF.aprs_fi.apiKey
|
||||||
|
|
||||||
# optional second argument is a callsign to search
|
# optional second argument is a callsign to search
|
||||||
a = re.search(r"^.*\s+(.*)", message)
|
a = re.search(r"^.*\s+(.*)", message)
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
from aprsd import packets, plugin
|
from aprsd import packets, plugin
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
|
||||||
|
|
||||||
@ -20,7 +23,7 @@ class NotifySeenPlugin(plugin.APRSDWatchListPluginBase):
|
|||||||
def process(self, packet: packets.MessagePacket):
|
def process(self, packet: packets.MessagePacket):
|
||||||
LOG.info("NotifySeenPlugin")
|
LOG.info("NotifySeenPlugin")
|
||||||
|
|
||||||
notify_callsign = self.config["aprsd"]["watch_list"]["alert_callsign"]
|
notify_callsign = CONF.watch_list.alert_callsign
|
||||||
fromcall = packet.from_call
|
fromcall = packet.from_call
|
||||||
|
|
||||||
wl = packets.WatchList()
|
wl = packets.WatchList()
|
||||||
@ -38,7 +41,7 @@ class NotifySeenPlugin(plugin.APRSDWatchListPluginBase):
|
|||||||
packet_type = packet.__class__.__name__
|
packet_type = packet.__class__.__name__
|
||||||
# we shouldn't notify the alert user that they are online.
|
# we shouldn't notify the alert user that they are online.
|
||||||
pkt = packets.MessagePacket(
|
pkt = packets.MessagePacket(
|
||||||
from_call=self.config["aprsd"]["callsign"],
|
from_call=CONF.callsign,
|
||||||
to_call=notify_callsign,
|
to_call=notify_callsign,
|
||||||
message_text=(
|
message_text=(
|
||||||
f"{fromcall} was just seen by type:'{packet_type}'"
|
f"{fromcall} was just seen by type:'{packet_type}'"
|
||||||
|
@ -2,11 +2,14 @@ import datetime
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
from aprsd import packets, plugin
|
from aprsd import packets, plugin
|
||||||
from aprsd.packets import tracker
|
from aprsd.packets import tracker
|
||||||
from aprsd.utils import trace
|
from aprsd.utils import trace
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
|
||||||
|
|
||||||
@ -17,6 +20,13 @@ class QueryPlugin(plugin.APRSDRegexCommandPluginBase):
|
|||||||
command_name = "query"
|
command_name = "query"
|
||||||
short_description = "APRSD Owner command to query messages in the MsgTrack"
|
short_description = "APRSD Owner command to query messages in the MsgTrack"
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
"""Do any plugin setup here."""
|
||||||
|
if not CONF.query_plugin.callsign:
|
||||||
|
LOG.error("Config query_plugin.callsign not set. Disabling plugin")
|
||||||
|
self.enabled = False
|
||||||
|
self.enabled = True
|
||||||
|
|
||||||
@trace.trace
|
@trace.trace
|
||||||
def process(self, packet: packets.MessagePacket):
|
def process(self, packet: packets.MessagePacket):
|
||||||
LOG.info("Query COMMAND")
|
LOG.info("Query COMMAND")
|
||||||
@ -32,7 +42,7 @@ class QueryPlugin(plugin.APRSDRegexCommandPluginBase):
|
|||||||
now.strftime("%H:%M:%S"),
|
now.strftime("%H:%M:%S"),
|
||||||
)
|
)
|
||||||
|
|
||||||
searchstring = "^" + self.config["ham"]["callsign"] + ".*"
|
searchstring = "^" + CONF.query_plugin.callsign + ".*"
|
||||||
# only I can do admin commands
|
# only I can do admin commands
|
||||||
if re.search(searchstring, fromcall):
|
if re.search(searchstring, fromcall):
|
||||||
|
|
||||||
|
@ -2,12 +2,14 @@ import logging
|
|||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
import pytz
|
import pytz
|
||||||
|
|
||||||
from aprsd import packets, plugin, plugin_utils
|
from aprsd import packets, plugin, plugin_utils
|
||||||
from aprsd.utils import fuzzy, trace
|
from aprsd.utils import fuzzy, trace
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
|
||||||
|
|
||||||
@ -74,7 +76,7 @@ class TimeOWMPlugin(TimePlugin, plugin.APRSFIKEYMixin):
|
|||||||
# if no second argument, search for calling station
|
# if no second argument, search for calling station
|
||||||
searchcall = fromcall
|
searchcall = fromcall
|
||||||
|
|
||||||
api_key = self.config["services"]["aprs.fi"]["apiKey"]
|
api_key = CONF.aprs_fi.apiKey
|
||||||
try:
|
try:
|
||||||
aprs_data = plugin_utils.get_aprs_fi(api_key, searchcall)
|
aprs_data = plugin_utils.get_aprs_fi(api_key, searchcall)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
|
@ -2,12 +2,14 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from aprsd import plugin, plugin_utils
|
from aprsd import plugin, plugin_utils
|
||||||
from aprsd.utils import trace
|
from aprsd.utils import trace
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
|
||||||
|
|
||||||
@ -34,10 +36,10 @@ class USWeatherPlugin(plugin.APRSDRegexCommandPluginBase, plugin.APRSFIKEYMixin)
|
|||||||
@trace.trace
|
@trace.trace
|
||||||
def process(self, packet):
|
def process(self, packet):
|
||||||
LOG.info("Weather Plugin")
|
LOG.info("Weather Plugin")
|
||||||
fromcall = packet.get("from")
|
fromcall = packet.from_call
|
||||||
# message = packet.get("message_text", None)
|
# message = packet.get("message_text", None)
|
||||||
# ack = packet.get("msgNo", "0")
|
# ack = packet.get("msgNo", "0")
|
||||||
api_key = self.config["services"]["aprs.fi"]["apiKey"]
|
api_key = CONF.aprs_fi.apiKey
|
||||||
try:
|
try:
|
||||||
aprs_data = plugin_utils.get_aprs_fi(api_key, fromcall)
|
aprs_data = plugin_utils.get_aprs_fi(api_key, fromcall)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
@ -58,17 +60,23 @@ class USWeatherPlugin(plugin.APRSDRegexCommandPluginBase, plugin.APRSFIKEYMixin)
|
|||||||
LOG.error(f"Couldn't fetch forecast.weather.gov '{ex}'")
|
LOG.error(f"Couldn't fetch forecast.weather.gov '{ex}'")
|
||||||
return "Unable to get weather"
|
return "Unable to get weather"
|
||||||
|
|
||||||
reply = (
|
LOG.info(f"WX data {wx_data}")
|
||||||
"%sF(%sF/%sF) %s. %s, %s."
|
|
||||||
% (
|
if wx_data["success"] == False:
|
||||||
wx_data["currentobservation"]["Temp"],
|
# Failed to fetch the weather
|
||||||
wx_data["data"]["temperature"][0],
|
reply = "Failed to fetch weather for location"
|
||||||
wx_data["data"]["temperature"][1],
|
else:
|
||||||
wx_data["data"]["weather"][0],
|
reply = (
|
||||||
wx_data["time"]["startPeriodName"][1],
|
"%sF(%sF/%sF) %s. %s, %s."
|
||||||
wx_data["data"]["weather"][1],
|
% (
|
||||||
)
|
wx_data["currentobservation"]["Temp"],
|
||||||
).rstrip()
|
wx_data["data"]["temperature"][0],
|
||||||
|
wx_data["data"]["temperature"][1],
|
||||||
|
wx_data["data"]["weather"][0],
|
||||||
|
wx_data["time"]["startPeriodName"][1],
|
||||||
|
wx_data["data"]["weather"][1],
|
||||||
|
)
|
||||||
|
).rstrip()
|
||||||
LOG.debug(f"reply: '{reply}' ")
|
LOG.debug(f"reply: '{reply}' ")
|
||||||
return reply
|
return reply
|
||||||
|
|
||||||
@ -119,13 +127,7 @@ class USMetarPlugin(plugin.APRSDRegexCommandPluginBase, plugin.APRSFIKEYMixin):
|
|||||||
# if no second argument, search for calling station
|
# if no second argument, search for calling station
|
||||||
fromcall = fromcall
|
fromcall = fromcall
|
||||||
|
|
||||||
try:
|
api_key = CONF.aprs_fi.apiKey
|
||||||
self.config.exists(["services", "aprs.fi", "apiKey"])
|
|
||||||
except Exception as ex:
|
|
||||||
LOG.error(f"Failed to find config aprs.fi:apikey {ex}")
|
|
||||||
return "No aprs.fi apikey found"
|
|
||||||
|
|
||||||
api_key = self.config["services"]["aprs.fi"]["apiKey"]
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
aprs_data = plugin_utils.get_aprs_fi(api_key, fromcall)
|
aprs_data = plugin_utils.get_aprs_fi(api_key, fromcall)
|
||||||
@ -187,6 +189,13 @@ class OWMWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
|
|||||||
command_name = "OpenWeatherMap"
|
command_name = "OpenWeatherMap"
|
||||||
short_description = "OpenWeatherMap weather of GPS Beacon location"
|
short_description = "OpenWeatherMap weather of GPS Beacon location"
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
if not CONF.owm_weather_plugin.apiKey:
|
||||||
|
LOG.error("Config.owm_weather_plugin.apiKey is not set. Disabling")
|
||||||
|
self.enabled = False
|
||||||
|
else:
|
||||||
|
self.enabled = True
|
||||||
|
|
||||||
def help(self):
|
def help(self):
|
||||||
_help = [
|
_help = [
|
||||||
"openweathermap: Send {} to get weather "
|
"openweathermap: Send {} to get weather "
|
||||||
@ -209,13 +218,8 @@ class OWMWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
|
|||||||
else:
|
else:
|
||||||
searchcall = fromcall
|
searchcall = fromcall
|
||||||
|
|
||||||
try:
|
api_key = CONF.aprs_fi.apiKey
|
||||||
self.config.exists(["services", "aprs.fi", "apiKey"])
|
|
||||||
except Exception as ex:
|
|
||||||
LOG.error(f"Failed to find config aprs.fi:apikey {ex}")
|
|
||||||
return "No aprs.fi apikey found"
|
|
||||||
|
|
||||||
api_key = self.config["services"]["aprs.fi"]["apiKey"]
|
|
||||||
try:
|
try:
|
||||||
aprs_data = plugin_utils.get_aprs_fi(api_key, searchcall)
|
aprs_data = plugin_utils.get_aprs_fi(api_key, searchcall)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
@ -230,21 +234,8 @@ class OWMWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
|
|||||||
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:
|
units = CONF.units
|
||||||
self.config.exists(["services", "openweathermap", "apiKey"])
|
api_key = CONF.owm_weather_plugin.apiKey
|
||||||
except Exception as ex:
|
|
||||||
LOG.error(f"Failed to find config openweathermap:apiKey {ex}")
|
|
||||||
return "No openweathermap apiKey found"
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.config.exists(["aprsd", "units"])
|
|
||||||
except Exception:
|
|
||||||
LOG.debug("Couldn't find untis in aprsd:services:units")
|
|
||||||
units = "metric"
|
|
||||||
else:
|
|
||||||
units = self.config["aprsd"]["units"]
|
|
||||||
|
|
||||||
api_key = self.config["services"]["openweathermap"]["apiKey"]
|
|
||||||
try:
|
try:
|
||||||
wx_data = plugin_utils.fetch_openweathermap(
|
wx_data = plugin_utils.fetch_openweathermap(
|
||||||
api_key,
|
api_key,
|
||||||
@ -317,6 +308,16 @@ class AVWXWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
|
|||||||
command_name = "AVWXWeather"
|
command_name = "AVWXWeather"
|
||||||
short_description = "AVWX weather of GPS Beacon location"
|
short_description = "AVWX weather of GPS Beacon location"
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
if not CONF.avwx_plugin.base_url:
|
||||||
|
LOG.error("Config avwx_plugin.base_url not specified. Disabling")
|
||||||
|
return False
|
||||||
|
elif not CONF.avwx_plugin.apiKey:
|
||||||
|
LOG.error("Config avwx_plugin.apiKey not specified. Disabling")
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
def help(self):
|
def help(self):
|
||||||
_help = [
|
_help = [
|
||||||
"avwxweather: Send {} to get weather "
|
"avwxweather: Send {} to get weather "
|
||||||
@ -339,13 +340,7 @@ class AVWXWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
|
|||||||
else:
|
else:
|
||||||
searchcall = fromcall
|
searchcall = fromcall
|
||||||
|
|
||||||
try:
|
api_key = CONF.aprs_fi.apiKey
|
||||||
self.config.exists(["services", "aprs.fi", "apiKey"])
|
|
||||||
except Exception as ex:
|
|
||||||
LOG.error(f"Failed to find config aprs.fi:apikey {ex}")
|
|
||||||
return "No aprs.fi apikey found"
|
|
||||||
|
|
||||||
api_key = self.config["services"]["aprs.fi"]["apiKey"]
|
|
||||||
try:
|
try:
|
||||||
aprs_data = plugin_utils.get_aprs_fi(api_key, searchcall)
|
aprs_data = plugin_utils.get_aprs_fi(api_key, searchcall)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
@ -360,21 +355,8 @@ class AVWXWeatherPlugin(plugin.APRSDRegexCommandPluginBase):
|
|||||||
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:
|
api_key = CONF.avwx_plugin.apiKey
|
||||||
self.config.exists(["services", "avwx", "apiKey"])
|
base_url = CONF.avwx_plugin.base_url
|
||||||
except Exception as ex:
|
|
||||||
LOG.error(f"Failed to find config avwx:apiKey {ex}")
|
|
||||||
return "No avwx apiKey found"
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.config.exists(self.config, ["services", "avwx", "base_url"])
|
|
||||||
except Exception as ex:
|
|
||||||
LOG.debug(f"Didn't find avwx:base_url {ex}")
|
|
||||||
base_url = "https://avwx.rest"
|
|
||||||
else:
|
|
||||||
base_url = self.config["services"]["avwx"]["base_url"]
|
|
||||||
|
|
||||||
api_key = self.config["services"]["avwx"]["apiKey"]
|
|
||||||
token = f"TOKEN {api_key}"
|
token = f"TOKEN {api_key}"
|
||||||
headers = {"Authorization": token}
|
headers = {"Authorization": token}
|
||||||
try:
|
try:
|
||||||
|
@ -2,12 +2,14 @@ import datetime
|
|||||||
import logging
|
import logging
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
import wrapt
|
import wrapt
|
||||||
|
|
||||||
import aprsd
|
import aprsd
|
||||||
from aprsd import packets, plugin, utils
|
from aprsd import packets, plugin, utils
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
|
||||||
|
|
||||||
@ -15,7 +17,6 @@ class APRSDStats:
|
|||||||
|
|
||||||
_instance = None
|
_instance = None
|
||||||
lock = threading.Lock()
|
lock = threading.Lock()
|
||||||
config = None
|
|
||||||
|
|
||||||
start_time = None
|
start_time = None
|
||||||
_aprsis_server = None
|
_aprsis_server = None
|
||||||
@ -67,10 +68,6 @@ class APRSDStats:
|
|||||||
cls._instance._aprsis_keepalive = datetime.datetime.now()
|
cls._instance._aprsis_keepalive = datetime.datetime.now()
|
||||||
return cls._instance
|
return cls._instance
|
||||||
|
|
||||||
def __init__(self, config=None):
|
|
||||||
if config:
|
|
||||||
self.config = config
|
|
||||||
|
|
||||||
@wrapt.synchronized(lock)
|
@wrapt.synchronized(lock)
|
||||||
@property
|
@property
|
||||||
def uptime(self):
|
def uptime(self):
|
||||||
@ -191,7 +188,7 @@ class APRSDStats:
|
|||||||
"aprsd": {
|
"aprsd": {
|
||||||
"version": aprsd.__version__,
|
"version": aprsd.__version__,
|
||||||
"uptime": utils.strfdelta(self.uptime),
|
"uptime": utils.strfdelta(self.uptime),
|
||||||
"callsign": self.config["aprsd"]["callsign"],
|
"callsign": CONF.callsign,
|
||||||
"memory_current": int(self.memory),
|
"memory_current": int(self.memory),
|
||||||
"memory_current_str": utils.human_size(self.memory),
|
"memory_current_str": utils.human_size(self.memory),
|
||||||
"memory_peak": int(self.memory_peak),
|
"memory_peak": int(self.memory_peak),
|
||||||
@ -201,7 +198,7 @@ class APRSDStats:
|
|||||||
},
|
},
|
||||||
"aprs-is": {
|
"aprs-is": {
|
||||||
"server": str(self.aprsis_server),
|
"server": str(self.aprsis_server),
|
||||||
"callsign": self.config["aprs"]["login"],
|
"callsign": CONF.aprs_network.login,
|
||||||
"last_update": last_aprsis_keepalive,
|
"last_update": last_aprsis_keepalive,
|
||||||
},
|
},
|
||||||
"packets": {
|
"packets": {
|
||||||
@ -215,7 +212,7 @@ class APRSDStats:
|
|||||||
"ack_sent": self._pkt_cnt["AckPacket"]["tx"],
|
"ack_sent": self._pkt_cnt["AckPacket"]["tx"],
|
||||||
},
|
},
|
||||||
"email": {
|
"email": {
|
||||||
"enabled": self.config["aprsd"]["email"]["enabled"],
|
"enabled": CONF.email_plugin.enabled,
|
||||||
"sent": int(self._email_tx),
|
"sent": int(self._email_tx),
|
||||||
"received": int(self._email_rx),
|
"received": int(self._email_rx),
|
||||||
"thread_last_update": last_update,
|
"thread_last_update": last_update,
|
||||||
|
@ -3,10 +3,13 @@ import logging
|
|||||||
import time
|
import time
|
||||||
import tracemalloc
|
import tracemalloc
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
from aprsd import client, packets, stats, utils
|
from aprsd import client, packets, stats, utils
|
||||||
from aprsd.threads import APRSDThread, APRSDThreadList
|
from aprsd.threads import APRSDThread, APRSDThreadList
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
|
||||||
|
|
||||||
@ -14,10 +17,9 @@ class KeepAliveThread(APRSDThread):
|
|||||||
cntr = 0
|
cntr = 0
|
||||||
checker_time = datetime.datetime.now()
|
checker_time = datetime.datetime.now()
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self):
|
||||||
tracemalloc.start()
|
tracemalloc.start()
|
||||||
super().__init__("KeepAlive")
|
super().__init__("KeepAlive")
|
||||||
self.config = config
|
|
||||||
max_timeout = {"hours": 0.0, "minutes": 2, "seconds": 0}
|
max_timeout = {"hours": 0.0, "minutes": 2, "seconds": 0}
|
||||||
self.max_delta = datetime.timedelta(**max_timeout)
|
self.max_delta = datetime.timedelta(**max_timeout)
|
||||||
|
|
||||||
@ -40,15 +42,9 @@ class KeepAliveThread(APRSDThread):
|
|||||||
stats_obj.set_memory(current)
|
stats_obj.set_memory(current)
|
||||||
stats_obj.set_memory_peak(peak)
|
stats_obj.set_memory_peak(peak)
|
||||||
|
|
||||||
try:
|
login = CONF.callsign
|
||||||
login = self.config["aprsd"]["callsign"]
|
|
||||||
except KeyError:
|
|
||||||
login = self.config["ham"]["callsign"]
|
|
||||||
|
|
||||||
if pkt_tracker.is_initialized():
|
tracked_packets = len(pkt_tracker)
|
||||||
tracked_packets = len(pkt_tracker)
|
|
||||||
else:
|
|
||||||
tracked_packets = 0
|
|
||||||
|
|
||||||
keepalive = (
|
keepalive = (
|
||||||
"{} - Uptime {} RX:{} TX:{} Tracker:{} Msgs TX:{} RX:{} "
|
"{} - Uptime {} RX:{} TX:{} Tracker:{} Msgs TX:{} RX:{} "
|
||||||
@ -77,7 +73,7 @@ class KeepAliveThread(APRSDThread):
|
|||||||
if delta > self.max_delta:
|
if delta > self.max_delta:
|
||||||
# We haven't gotten a keepalive from aprs-is in a while
|
# We haven't gotten a keepalive from aprs-is in a while
|
||||||
# reset the connection.a
|
# reset the connection.a
|
||||||
if not client.KISSClient.is_enabled(self.config):
|
if not client.KISSClient.is_enabled():
|
||||||
LOG.warning(f"Resetting connection to APRS-IS {delta}")
|
LOG.warning(f"Resetting connection to APRS-IS {delta}")
|
||||||
client.factory.create().reset()
|
client.factory.create().reset()
|
||||||
|
|
||||||
|
@ -4,18 +4,19 @@ import queue
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
import aprslib
|
import aprslib
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
from aprsd import client, packets, plugin
|
from aprsd import client, packets, plugin
|
||||||
from aprsd.threads import APRSDThread, tx
|
from aprsd.threads import APRSDThread, tx
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
|
||||||
|
|
||||||
class APRSDRXThread(APRSDThread):
|
class APRSDRXThread(APRSDThread):
|
||||||
def __init__(self, config, packet_queue):
|
def __init__(self, packet_queue):
|
||||||
super().__init__("RX_MSG")
|
super().__init__("RX_MSG")
|
||||||
self.config = config
|
|
||||||
self.packet_queue = packet_queue
|
self.packet_queue = packet_queue
|
||||||
self._client = client.factory.create()
|
self._client = client.factory.create()
|
||||||
|
|
||||||
@ -80,8 +81,7 @@ class APRSDProcessPacketThread(APRSDThread):
|
|||||||
will ack a message before sending the packet to the subclass
|
will ack a message before sending the packet to the subclass
|
||||||
for processing."""
|
for processing."""
|
||||||
|
|
||||||
def __init__(self, config, packet_queue):
|
def __init__(self, packet_queue):
|
||||||
self.config = config
|
|
||||||
self.packet_queue = packet_queue
|
self.packet_queue = packet_queue
|
||||||
super().__init__("ProcessPKT")
|
super().__init__("ProcessPKT")
|
||||||
self._loop_cnt = 1
|
self._loop_cnt = 1
|
||||||
@ -106,7 +106,7 @@ class APRSDProcessPacketThread(APRSDThread):
|
|||||||
def process_packet(self, packet):
|
def process_packet(self, packet):
|
||||||
"""Process a packet received from aprs-is server."""
|
"""Process a packet received from aprs-is server."""
|
||||||
LOG.debug(f"RXPKT-LOOP {self._loop_cnt}")
|
LOG.debug(f"RXPKT-LOOP {self._loop_cnt}")
|
||||||
our_call = self.config["aprsd"]["callsign"].lower()
|
our_call = CONF.callsign.lower()
|
||||||
|
|
||||||
from_call = packet.from_call
|
from_call = packet.from_call
|
||||||
if packet.addresse:
|
if packet.addresse:
|
||||||
@ -133,7 +133,7 @@ class APRSDProcessPacketThread(APRSDThread):
|
|||||||
# send an ack last
|
# send an ack last
|
||||||
tx.send(
|
tx.send(
|
||||||
packets.AckPacket(
|
packets.AckPacket(
|
||||||
from_call=self.config["aprsd"]["callsign"],
|
from_call=CONF.callsign,
|
||||||
to_call=from_call,
|
to_call=from_call,
|
||||||
msgNo=msg_id,
|
msgNo=msg_id,
|
||||||
),
|
),
|
||||||
@ -178,11 +178,11 @@ class APRSDPluginProcessPacketThread(APRSDProcessPacketThread):
|
|||||||
if isinstance(subreply, packets.Packet):
|
if isinstance(subreply, packets.Packet):
|
||||||
tx.send(subreply)
|
tx.send(subreply)
|
||||||
else:
|
else:
|
||||||
wl = self.config["aprsd"]["watch_list"]
|
wl = CONF.watch_list
|
||||||
to_call = wl["alert_callsign"]
|
to_call = wl["alert_callsign"]
|
||||||
tx.send(
|
tx.send(
|
||||||
packets.MessagePacket(
|
packets.MessagePacket(
|
||||||
from_call=self.config["aprsd"]["callsign"],
|
from_call=CONF.callsign,
|
||||||
to_call=to_call,
|
to_call=to_call,
|
||||||
message_text=subreply,
|
message_text=subreply,
|
||||||
),
|
),
|
||||||
@ -219,7 +219,7 @@ class APRSDPluginProcessPacketThread(APRSDProcessPacketThread):
|
|||||||
else:
|
else:
|
||||||
tx.send(
|
tx.send(
|
||||||
packets.MessagePacket(
|
packets.MessagePacket(
|
||||||
from_call=self.config["aprsd"]["callsign"],
|
from_call=CONF.callsign,
|
||||||
to_call=from_call,
|
to_call=from_call,
|
||||||
message_text=subreply,
|
message_text=subreply,
|
||||||
),
|
),
|
||||||
@ -238,7 +238,7 @@ class APRSDPluginProcessPacketThread(APRSDProcessPacketThread):
|
|||||||
LOG.debug(f"Sending '{reply}'")
|
LOG.debug(f"Sending '{reply}'")
|
||||||
tx.send(
|
tx.send(
|
||||||
packets.MessagePacket(
|
packets.MessagePacket(
|
||||||
from_call=self.config["aprsd"]["callsign"],
|
from_call=CONF.callsign,
|
||||||
to_call=from_call,
|
to_call=from_call,
|
||||||
message_text=reply,
|
message_text=reply,
|
||||||
),
|
),
|
||||||
@ -246,12 +246,12 @@ class APRSDPluginProcessPacketThread(APRSDProcessPacketThread):
|
|||||||
|
|
||||||
# If the message was for us and we didn't have a
|
# If the message was for us and we didn't have a
|
||||||
# response, then we send a usage statement.
|
# response, then we send a usage statement.
|
||||||
if to_call == self.config["aprsd"]["callsign"] and not replied:
|
if to_call == CONF.callsign and not replied:
|
||||||
LOG.warning("Sending help!")
|
LOG.warning("Sending help!")
|
||||||
message_text = "Unknown command! Send 'help' message for help"
|
message_text = "Unknown command! Send 'help' message for help"
|
||||||
tx.send(
|
tx.send(
|
||||||
packets.MessagePacket(
|
packets.MessagePacket(
|
||||||
from_call=self.config["aprsd"]["callsign"],
|
from_call=CONF.callsign,
|
||||||
to_call=from_call,
|
to_call=from_call,
|
||||||
message_text=message_text,
|
message_text=message_text,
|
||||||
),
|
),
|
||||||
@ -260,11 +260,11 @@ class APRSDPluginProcessPacketThread(APRSDProcessPacketThread):
|
|||||||
LOG.error("Plugin failed!!!")
|
LOG.error("Plugin failed!!!")
|
||||||
LOG.exception(ex)
|
LOG.exception(ex)
|
||||||
# Do we need to send a reply?
|
# Do we need to send a reply?
|
||||||
if to_call == self.config["aprsd"]["callsign"]:
|
if to_call == CONF.callsign:
|
||||||
reply = "A Plugin failed! try again?"
|
reply = "A Plugin failed! try again?"
|
||||||
tx.send(
|
tx.send(
|
||||||
packets.MessagePacket(
|
packets.MessagePacket(
|
||||||
from_call=self.config["aprsd"]["callsign"],
|
from_call=CONF.callsign,
|
||||||
to_call=from_call,
|
to_call=from_call,
|
||||||
message_text=reply,
|
message_text=reply,
|
||||||
),
|
),
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
import abc
|
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
from aprsd import config as aprsd_config
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
LOG = logging.getLogger("APRSD")
|
LOG = logging.getLogger("APRSD")
|
||||||
|
|
||||||
|
|
||||||
class ObjectStoreMixin(metaclass=abc.ABCMeta):
|
class ObjectStoreMixin:
|
||||||
"""Class 'MIXIN' intended to save/load object data.
|
"""Class 'MIXIN' intended to save/load object data.
|
||||||
|
|
||||||
The asumption of how this mixin is used:
|
The asumption of how this mixin is used:
|
||||||
@ -24,13 +24,6 @@ class ObjectStoreMixin(metaclass=abc.ABCMeta):
|
|||||||
When APRSD Starts, it calls load()
|
When APRSD Starts, it calls load()
|
||||||
aprsd server -f (flush) will wipe all saved objects.
|
aprsd server -f (flush) will wipe all saved objects.
|
||||||
"""
|
"""
|
||||||
@abc.abstractmethod
|
|
||||||
def is_initialized(self):
|
|
||||||
"""Return True if the class has been setup correctly.
|
|
||||||
|
|
||||||
If this returns False, the ObjectStore doesn't save anything.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self.data)
|
return len(self.data)
|
||||||
@ -44,25 +37,18 @@ class ObjectStoreMixin(metaclass=abc.ABCMeta):
|
|||||||
return self.data[id]
|
return self.data[id]
|
||||||
|
|
||||||
def _init_store(self):
|
def _init_store(self):
|
||||||
if self.is_initialized():
|
if not CONF.enable_save:
|
||||||
sl = self._save_location()
|
return
|
||||||
if not os.path.exists(sl):
|
sl = CONF.save_location
|
||||||
LOG.warning(f"Save location {sl} doesn't exist")
|
if not os.path.exists(sl):
|
||||||
try:
|
LOG.warning(f"Save location {sl} doesn't exist")
|
||||||
os.makedirs(sl)
|
try:
|
||||||
except Exception as ex:
|
os.makedirs(sl)
|
||||||
LOG.exception(ex)
|
except Exception as ex:
|
||||||
else:
|
LOG.exception(ex)
|
||||||
LOG.warning(f"{self.__class__.__name__} is not initialized")
|
|
||||||
|
|
||||||
def _save_location(self):
|
|
||||||
save_location = self.config.get("aprsd.save_location", None)
|
|
||||||
if not save_location:
|
|
||||||
save_location = aprsd_config.DEFAULT_CONFIG_DIR
|
|
||||||
return save_location
|
|
||||||
|
|
||||||
def _save_filename(self):
|
def _save_filename(self):
|
||||||
save_location = self._save_location()
|
save_location = CONF.save_location
|
||||||
|
|
||||||
return "{}/{}.p".format(
|
return "{}/{}.p".format(
|
||||||
save_location,
|
save_location,
|
||||||
@ -79,45 +65,48 @@ class ObjectStoreMixin(metaclass=abc.ABCMeta):
|
|||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
"""Save any queued to disk?"""
|
"""Save any queued to disk?"""
|
||||||
if self.is_initialized():
|
if not CONF.enable_save:
|
||||||
if len(self) > 0:
|
return
|
||||||
LOG.info(
|
if len(self) > 0:
|
||||||
f"{self.__class__.__name__}::Saving"
|
LOG.info(
|
||||||
f" {len(self)} entries to disk at"
|
f"{self.__class__.__name__}::Saving"
|
||||||
f"{self._save_location()}",
|
f" {len(self)} entries to disk at"
|
||||||
)
|
f"{CONF.save_location}",
|
||||||
with open(self._save_filename(), "wb+") as fp:
|
)
|
||||||
pickle.dump(self._dump(), fp)
|
with open(self._save_filename(), "wb+") as fp:
|
||||||
else:
|
pickle.dump(self._dump(), fp)
|
||||||
LOG.debug(
|
else:
|
||||||
"{} Nothing to save, flushing old save file '{}'".format(
|
LOG.debug(
|
||||||
self.__class__.__name__,
|
"{} Nothing to save, flushing old save file '{}'".format(
|
||||||
self._save_filename(),
|
self.__class__.__name__,
|
||||||
),
|
self._save_filename(),
|
||||||
)
|
),
|
||||||
self.flush()
|
)
|
||||||
|
self.flush()
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
if self.is_initialized():
|
if not CONF.enable_save:
|
||||||
if os.path.exists(self._save_filename()):
|
return
|
||||||
try:
|
if os.path.exists(self._save_filename()):
|
||||||
with open(self._save_filename(), "rb") as fp:
|
try:
|
||||||
raw = pickle.load(fp)
|
with open(self._save_filename(), "rb") as fp:
|
||||||
if raw:
|
raw = pickle.load(fp)
|
||||||
self.data = raw
|
if raw:
|
||||||
LOG.debug(
|
self.data = raw
|
||||||
f"{self.__class__.__name__}::Loaded {len(self)} entries from disk.",
|
LOG.debug(
|
||||||
)
|
f"{self.__class__.__name__}::Loaded {len(self)} entries from disk.",
|
||||||
LOG.debug(f"{self.data}")
|
)
|
||||||
except (pickle.UnpicklingError, Exception) as ex:
|
LOG.debug(f"{self.data}")
|
||||||
LOG.error(f"Failed to UnPickle {self._save_filename()}")
|
except (pickle.UnpicklingError, Exception) as ex:
|
||||||
LOG.error(ex)
|
LOG.error(f"Failed to UnPickle {self._save_filename()}")
|
||||||
self.data = {}
|
LOG.error(ex)
|
||||||
|
self.data = {}
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
"""Nuke the old pickle file that stored the old results from last aprsd run."""
|
"""Nuke the old pickle file that stored the old results from last aprsd run."""
|
||||||
if self.is_initialized():
|
if not CONF.enable_save:
|
||||||
if os.path.exists(self._save_filename()):
|
return
|
||||||
pathlib.Path(self._save_filename()).unlink()
|
if os.path.exists(self._save_filename()):
|
||||||
with self.lock:
|
pathlib.Path(self._save_filename()).unlink()
|
||||||
self.data = {}
|
with self.lock:
|
||||||
|
self.data = {}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#
|
#
|
||||||
# This file is autogenerated by pip-compile with Python 3.9
|
# This file is autogenerated by pip-compile with Python 3.10
|
||||||
# by the following command:
|
# by the following command:
|
||||||
#
|
#
|
||||||
# pip-compile --annotation-style=line --resolver=backtracking dev-requirements.in
|
# pip-compile --annotation-style=line --resolver=backtracking dev-requirements.in
|
||||||
#
|
#
|
||||||
add-trailing-comma==2.4.0 # via gray
|
add-trailing-comma==2.4.0 # via gray
|
||||||
alabaster==0.7.12 # via sphinx
|
alabaster==0.7.12 # via sphinx
|
||||||
attrs==22.1.0 # via jsonschema, pytest
|
attrs==22.2.0 # via jsonschema, pytest
|
||||||
autoflake==1.5.3 # via gray
|
autoflake==1.5.3 # via gray
|
||||||
babel==2.11.0 # via sphinx
|
babel==2.11.0 # via sphinx
|
||||||
black==22.12.0 # via gray
|
black==22.12.0 # via gray
|
||||||
@ -20,21 +20,20 @@ click==8.1.3 # via black, pip-tools
|
|||||||
colorama==0.4.6 # via tox
|
colorama==0.4.6 # via tox
|
||||||
commonmark==0.9.1 # via rich
|
commonmark==0.9.1 # via rich
|
||||||
configargparse==1.5.3 # via gray
|
configargparse==1.5.3 # via gray
|
||||||
coverage[toml]==6.5.0 # via pytest-cov
|
coverage[toml]==7.0.1 # via pytest-cov
|
||||||
distlib==0.3.6 # via virtualenv
|
distlib==0.3.6 # via virtualenv
|
||||||
docutils==0.19 # via sphinx
|
docutils==0.19 # via sphinx
|
||||||
exceptiongroup==1.0.4 # via pytest
|
exceptiongroup==1.1.0 # via pytest
|
||||||
filelock==3.8.2 # via tox, virtualenv
|
filelock==3.8.2 # via tox, virtualenv
|
||||||
fixit==0.1.4 # via gray
|
fixit==0.1.4 # via gray
|
||||||
flake8==6.0.0 # via -r dev-requirements.in, fixit, pep8-naming
|
flake8==6.0.0 # via -r dev-requirements.in, fixit, pep8-naming
|
||||||
gray==0.13.0 # via -r dev-requirements.in
|
gray==0.13.0 # via -r dev-requirements.in
|
||||||
identify==2.5.9 # via pre-commit
|
identify==2.5.11 # via pre-commit
|
||||||
idna==3.4 # via requests
|
idna==3.4 # via requests
|
||||||
imagesize==1.4.1 # via sphinx
|
imagesize==1.4.1 # via sphinx
|
||||||
importlib-metadata==5.1.0 # via sphinx
|
|
||||||
importlib-resources==5.10.1 # via fixit
|
importlib-resources==5.10.1 # via fixit
|
||||||
iniconfig==1.1.1 # via pytest
|
iniconfig==1.1.1 # via pytest
|
||||||
isort==5.11.2 # via -r dev-requirements.in, gray
|
isort==5.11.4 # via -r dev-requirements.in, gray
|
||||||
jinja2==3.1.2 # via sphinx
|
jinja2==3.1.2 # via sphinx
|
||||||
jsonschema==4.17.3 # via fixit
|
jsonschema==4.17.3 # via fixit
|
||||||
libcst==0.4.9 # via fixit
|
libcst==0.4.9 # via fixit
|
||||||
@ -46,8 +45,8 @@ nodeenv==1.7.0 # via pre-commit
|
|||||||
packaging==22.0 # via build, pyproject-api, pytest, sphinx, tox
|
packaging==22.0 # via build, pyproject-api, pytest, sphinx, tox
|
||||||
pathspec==0.10.3 # via black
|
pathspec==0.10.3 # via black
|
||||||
pep517==0.13.0 # via build
|
pep517==0.13.0 # via build
|
||||||
pep8-naming==0.13.2 # via -r dev-requirements.in
|
pep8-naming==0.13.3 # via -r dev-requirements.in
|
||||||
pip-tools==6.12.0 # via -r dev-requirements.in
|
pip-tools==6.12.1 # via -r dev-requirements.in
|
||||||
platformdirs==2.6.0 # via black, tox, virtualenv
|
platformdirs==2.6.0 # via black, tox, virtualenv
|
||||||
pluggy==1.0.0 # via pytest, tox
|
pluggy==1.0.0 # via pytest, tox
|
||||||
pre-commit==2.20.0 # via -r dev-requirements.in
|
pre-commit==2.20.0 # via -r dev-requirements.in
|
||||||
@ -58,7 +57,7 @@ pyproject-api==1.2.1 # via tox
|
|||||||
pyrsistent==0.19.2 # via jsonschema
|
pyrsistent==0.19.2 # via jsonschema
|
||||||
pytest==7.2.0 # via -r dev-requirements.in, pytest-cov
|
pytest==7.2.0 # via -r dev-requirements.in, pytest-cov
|
||||||
pytest-cov==4.0.0 # via -r dev-requirements.in
|
pytest-cov==4.0.0 # via -r dev-requirements.in
|
||||||
pytz==2022.6 # via babel
|
pytz==2022.7 # via babel
|
||||||
pyupgrade==3.3.1 # via gray
|
pyupgrade==3.3.1 # via gray
|
||||||
pyyaml==6.0 # via fixit, libcst, pre-commit
|
pyyaml==6.0 # via fixit, libcst, pre-commit
|
||||||
requests==2.28.1 # via sphinx
|
requests==2.28.1 # via sphinx
|
||||||
@ -74,15 +73,14 @@ sphinxcontrib-serializinghtml==1.1.5 # via sphinx
|
|||||||
tokenize-rt==5.0.0 # via add-trailing-comma, pyupgrade
|
tokenize-rt==5.0.0 # via add-trailing-comma, pyupgrade
|
||||||
toml==0.10.2 # via autoflake, pre-commit
|
toml==0.10.2 # via autoflake, pre-commit
|
||||||
tomli==2.0.1 # via black, build, coverage, mypy, pep517, pyproject-api, pytest, tox
|
tomli==2.0.1 # via black, build, coverage, mypy, pep517, pyproject-api, pytest, tox
|
||||||
tox==4.0.9 # via -r dev-requirements.in
|
tox==4.0.16 # via -r dev-requirements.in
|
||||||
typing-extensions==4.4.0 # via black, libcst, mypy, typing-inspect
|
typing-extensions==4.4.0 # via libcst, mypy, typing-inspect
|
||||||
typing-inspect==0.8.0 # via libcst
|
typing-inspect==0.8.0 # via libcst
|
||||||
unify==0.5 # via gray
|
unify==0.5 # via gray
|
||||||
untokenize==0.1.1 # via unify
|
untokenize==0.1.1 # via unify
|
||||||
urllib3==1.26.13 # via requests
|
urllib3==1.26.13 # via requests
|
||||||
virtualenv==20.17.1 # via pre-commit, tox
|
virtualenv==20.17.1 # via pre-commit, tox
|
||||||
wheel==0.38.4 # via pip-tools
|
wheel==0.38.4 # via pip-tools
|
||||||
zipp==3.11.0 # via importlib-metadata, importlib-resources
|
|
||||||
|
|
||||||
# The following packages are considered to be unsafe in a requirements file:
|
# The following packages are considered to be unsafe in a requirements file:
|
||||||
# pip
|
# pip
|
||||||
|
@ -29,3 +29,4 @@ user-agents
|
|||||||
pyopenssl
|
pyopenssl
|
||||||
dataclasses
|
dataclasses
|
||||||
dacite2
|
dacite2
|
||||||
|
oslo.config
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
#
|
#
|
||||||
# This file is autogenerated by pip-compile with Python 3.9
|
# This file is autogenerated by pip-compile with Python 3.10
|
||||||
# by the following command:
|
# by the following command:
|
||||||
#
|
#
|
||||||
# pip-compile --annotation-style=line --resolver=backtracking requirements.in
|
# pip-compile --annotation-style=line --resolver=backtracking requirements.in
|
||||||
#
|
#
|
||||||
aprslib==0.7.2 # via -r requirements.in
|
aprslib==0.7.2 # via -r requirements.in
|
||||||
attrs==22.1.0 # via -r requirements.in, ax253, kiss3
|
attrs==22.2.0 # via -r requirements.in, ax253, kiss3
|
||||||
ax253==0.1.5.post1 # via kiss3
|
ax253==0.1.5.post1 # via kiss3
|
||||||
beautifulsoup4==4.11.1 # via -r requirements.in
|
beautifulsoup4==4.11.1 # via -r requirements.in
|
||||||
bidict==0.22.0 # via python-socketio
|
bidict==0.22.0 # via python-socketio
|
||||||
bitarray==2.6.0 # via ax253, kiss3
|
bitarray==2.6.1 # via ax253, kiss3
|
||||||
certifi==2022.12.7 # via requests
|
certifi==2022.12.7 # via requests
|
||||||
cffi==1.15.1 # via cryptography
|
cffi==1.15.1 # via cryptography
|
||||||
charset-normalizer==2.1.1 # via requests
|
charset-normalizer==2.1.1 # via requests
|
||||||
@ -19,6 +19,7 @@ commonmark==0.9.1 # via rich
|
|||||||
cryptography==38.0.4 # via pyopenssl
|
cryptography==38.0.4 # via pyopenssl
|
||||||
dacite2==2.0.0 # via -r requirements.in
|
dacite2==2.0.0 # via -r requirements.in
|
||||||
dataclasses==0.6 # via -r requirements.in
|
dataclasses==0.6 # via -r requirements.in
|
||||||
|
debtcollector==2.5.0 # via oslo-config
|
||||||
dnspython==2.2.1 # via eventlet
|
dnspython==2.2.1 # via eventlet
|
||||||
eventlet==0.33.2 # via -r requirements.in
|
eventlet==0.33.2 # via -r requirements.in
|
||||||
flask==2.1.2 # via -r requirements.in, flask-classful, flask-httpauth, flask-socketio
|
flask==2.1.2 # via -r requirements.in, flask-classful, flask-httpauth, flask-socketio
|
||||||
@ -28,12 +29,15 @@ flask-socketio==5.3.2 # via -r requirements.in
|
|||||||
greenlet==2.0.1 # via eventlet
|
greenlet==2.0.1 # via eventlet
|
||||||
idna==3.4 # via requests
|
idna==3.4 # via requests
|
||||||
imapclient==2.3.1 # via -r requirements.in
|
imapclient==2.3.1 # via -r requirements.in
|
||||||
importlib-metadata==5.1.0 # via ax253, flask, kiss3
|
importlib-metadata==5.2.0 # via ax253, kiss3
|
||||||
itsdangerous==2.1.2 # via flask
|
itsdangerous==2.1.2 # via flask
|
||||||
jinja2==3.1.2 # via click-completion, flask
|
jinja2==3.1.2 # via click-completion, flask
|
||||||
kiss3==8.0.0 # via -r requirements.in
|
kiss3==8.0.0 # via -r requirements.in
|
||||||
markupsafe==2.1.1 # via jinja2
|
markupsafe==2.1.1 # via jinja2
|
||||||
pbr==5.11.0 # via -r requirements.in
|
netaddr==0.8.0 # via oslo-config
|
||||||
|
oslo-config==9.0.0 # via -r requirements.in
|
||||||
|
oslo-i18n==5.1.0 # via oslo-config
|
||||||
|
pbr==5.11.0 # via -r requirements.in, oslo-i18n, stevedore
|
||||||
pluggy==1.0.0 # via -r requirements.in
|
pluggy==1.0.0 # via -r requirements.in
|
||||||
pycparser==2.21 # via cffi
|
pycparser==2.21 # via cffi
|
||||||
pygments==2.13.0 # via rich
|
pygments==2.13.0 # via rich
|
||||||
@ -42,13 +46,15 @@ pyserial==3.5 # via pyserial-asyncio
|
|||||||
pyserial-asyncio==0.6 # via kiss3
|
pyserial-asyncio==0.6 # via kiss3
|
||||||
python-engineio==4.3.4 # via python-socketio
|
python-engineio==4.3.4 # via python-socketio
|
||||||
python-socketio==5.7.2 # via flask-socketio
|
python-socketio==5.7.2 # via flask-socketio
|
||||||
pytz==2022.6 # via -r requirements.in
|
pytz==2022.7 # via -r requirements.in
|
||||||
pyyaml==6.0 # via -r requirements.in
|
pyyaml==6.0 # via -r requirements.in, oslo-config
|
||||||
requests==2.28.1 # via -r requirements.in, update-checker
|
requests==2.28.1 # via -r requirements.in, oslo-config, update-checker
|
||||||
|
rfc3986==2.0.0 # via oslo-config
|
||||||
rich==12.6.0 # via -r requirements.in
|
rich==12.6.0 # via -r requirements.in
|
||||||
shellingham==1.5.0 # via click-completion
|
shellingham==1.5.0 # via click-completion
|
||||||
six==1.16.0 # via -r requirements.in, click-completion, eventlet, imapclient
|
six==1.16.0 # via -r requirements.in, click-completion, eventlet, imapclient
|
||||||
soupsieve==2.3.2.post1 # via beautifulsoup4
|
soupsieve==2.3.2.post1 # via beautifulsoup4
|
||||||
|
stevedore==4.1.1 # via oslo-config
|
||||||
tabulate==0.9.0 # via -r requirements.in
|
tabulate==0.9.0 # via -r requirements.in
|
||||||
thesmuggler==1.0.1 # via -r requirements.in
|
thesmuggler==1.0.1 # via -r requirements.in
|
||||||
ua-parser==0.16.1 # via user-agents
|
ua-parser==0.16.1 # via user-agents
|
||||||
@ -56,5 +62,5 @@ update-checker==0.18.0 # via -r requirements.in
|
|||||||
urllib3==1.26.13 # via requests
|
urllib3==1.26.13 # via requests
|
||||||
user-agents==2.2.0 # via -r requirements.in
|
user-agents==2.2.0 # via -r requirements.in
|
||||||
werkzeug==2.1.2 # via -r requirements.in, flask
|
werkzeug==2.1.2 # via -r requirements.in, flask
|
||||||
wrapt==1.14.1 # via -r requirements.in
|
wrapt==1.14.1 # via -r requirements.in, debtcollector
|
||||||
zipp==3.11.0 # via importlib-metadata
|
zipp==3.11.0 # via importlib-metadata
|
||||||
|
@ -34,6 +34,10 @@ packages =
|
|||||||
[entry_points]
|
[entry_points]
|
||||||
console_scripts =
|
console_scripts =
|
||||||
aprsd = aprsd.aprsd:main
|
aprsd = aprsd.aprsd:main
|
||||||
|
oslo.config.opts =
|
||||||
|
aprsd.conf = aprsd.conf.opts:list_opts
|
||||||
|
oslo.config.opts.defaults =
|
||||||
|
aprsd.conf = aprsd.conf:set_lib_defaults
|
||||||
|
|
||||||
[build_sphinx]
|
[build_sphinx]
|
||||||
source-dir = docs
|
source-dir = docs
|
||||||
|
@ -5,21 +5,20 @@ from aprsd.plugins import email
|
|||||||
|
|
||||||
class TestEmail(unittest.TestCase):
|
class TestEmail(unittest.TestCase):
|
||||||
def test_get_email_from_shortcut(self):
|
def test_get_email_from_shortcut(self):
|
||||||
config = {"aprsd": {"email": {"shortcuts": {}}}}
|
|
||||||
email_address = "something@something.com"
|
email_address = "something@something.com"
|
||||||
addr = f"-{email_address}"
|
addr = f"-{email_address}"
|
||||||
actual = email.get_email_from_shortcut(config, addr)
|
actual = email.get_email_from_shortcut(addr)
|
||||||
self.assertEqual(addr, actual)
|
self.assertEqual(addr, actual)
|
||||||
|
|
||||||
config = {"aprsd": {"email": {"nothing": "nothing"}}}
|
config = {"aprsd": {"email": {"nothing": "nothing"}}}
|
||||||
actual = email.get_email_from_shortcut(config, addr)
|
actual = email.get_email_from_shortcut(addr)
|
||||||
self.assertEqual(addr, actual)
|
self.assertEqual(addr, actual)
|
||||||
|
|
||||||
config = {"aprsd": {"email": {"shortcuts": {"not_used": "empty"}}}}
|
config = {"aprsd": {"email": {"shortcuts": {"not_used": "empty"}}}}
|
||||||
actual = email.get_email_from_shortcut(config, addr)
|
actual = email.get_email_from_shortcut(addr)
|
||||||
self.assertEqual(addr, actual)
|
self.assertEqual(addr, actual)
|
||||||
|
|
||||||
config = {"aprsd": {"email": {"shortcuts": {"-wb": email_address}}}}
|
config = {"aprsd": {"email": {"shortcuts": {"-wb": email_address}}}}
|
||||||
short = "-wb"
|
short = "-wb"
|
||||||
actual = email.get_email_from_shortcut(config, short)
|
actual = email.get_email_from_shortcut(short)
|
||||||
self.assertEqual(email_address, actual)
|
self.assertEqual(email_address, actual)
|
||||||
|
@ -17,6 +17,6 @@ class TestMain(unittest.TestCase):
|
|||||||
"""Test to make sure we fail."""
|
"""Test to make sure we fail."""
|
||||||
imap_mock.return_value = None
|
imap_mock.return_value = None
|
||||||
smtp_mock.return_value = {"smaiof": "fire"}
|
smtp_mock.return_value = {"smaiof": "fire"}
|
||||||
config = mock.MagicMock()
|
mock.MagicMock()
|
||||||
|
|
||||||
email.validate_email_config(config, True)
|
email.validate_email_config(True)
|
||||||
|
Loading…
Reference in New Issue
Block a user