Update for APRSD 3.0.0 release.

This provides the new config options defined in the plugin code
itself.
This commit is contained in:
Hemna 2022-12-29 08:49:01 -05:00
parent b624055d06
commit 63726e426e
11 changed files with 197 additions and 155 deletions

View File

@ -1,9 +1,12 @@
import logging import logging
from oslo_config import cfg
from slack_sdk import WebClient from slack_sdk import WebClient
import aprsd_slack_plugin import aprsd_slack_plugin
CONF = cfg.CONF
LOG = logging.getLogger("APRSD") LOG = logging.getLogger("APRSD")
@ -39,33 +42,29 @@ class SlackPluginBase:
""" """
version = aprsd_slack_plugin.__version__ version = aprsd_slack_plugin.__version__
swc = None
slack_channels = None
def setup_slack(self): def setup_slack(self):
"""Create the slack require client from config.""" """Create the slack require client from config."""
# signing_secret = self.config["slack"]["signing_secret"] if not CONF.aprsd_slack_plugin.signing_secret:
try: LOG.error("Failed to find config aprsd_slack_plugin.signing_secret")
self.config.exists(["services", "slack", "bot_token"]) return "No slack signing_secret found"
except Exception as ex:
LOG.error("Failed to find config slack:bot_token {}".format(ex))
return "No slack bot_token found"
bot_token = self.config["services"]["slack"]["bot_token"] if not CONF.aprsd_slack_plugin.bot_token:
if not bot_token:
LOG.error( LOG.error(
"APRSD config is missing slack: bot_token:<token>. " "APRSD config is missing aprsd_slack_plugin.bot_token. "
"Please install the slack app and get the " "Please install the slack app and get the "
"Bot User OAth Access Token.", "Bot User OAth Access Token.",
) )
return False return False
self.swc = WebClient(token=bot_token) if not CONF.aprsd_slack_plugin.channels:
self.slack_channels = self.config["services"]["slack"].get("channels", None) LOG.error("aprsd_slack_plugin.channels is missing")
if not self.slack_channels:
LOG.error(
"APRSD config is missing slack: channels: <name> "
"Please add a slack channel name to send messages.",
)
return False return False
self.swc = WebClient(token=CONF.aprsd_slack_plugin.bot_token)
self.slack_channels = CONF.aprsd_slack_plugin.channels
return True return True

View File

@ -0,0 +1,10 @@
import logging
from oslo_config import cfg
from aprsd_slack_plugin.conf import slack
CONF = cfg.CONF
slack.register_opts(CONF)

View File

@ -0,0 +1,80 @@
# Copyright 2015 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
This is the single point of entry to generate the sample configuration
file for Nova. It collects all the necessary info from the other modules
in this package. It is assumed that:
* every other module in this package has a 'list_opts' function which
return a dict where
* the keys are strings which are the group names
* the value of each key is a list of config options for that group
* the nova.conf package doesn't have further packages with config options
* this module is only used in the context of sample file generation
"""
import collections
import importlib
import os
import pkgutil
LIST_OPTS_FUNC_NAME = "list_opts"
def _tupleize(dct):
"""Take the dict of options and convert to the 2-tuple format."""
return [(key, val) for key, val in dct.items()]
def list_opts():
opts = collections.defaultdict(list)
module_names = _list_module_names()
imported_modules = _import_modules(module_names)
_append_config_options(imported_modules, opts)
return _tupleize(opts)
def _list_module_names():
module_names = []
package_path = os.path.dirname(os.path.abspath(__file__))
for _, modname, ispkg in pkgutil.iter_modules(path=[package_path]):
if modname == "opts" or ispkg:
continue
else:
module_names.append(modname)
return module_names
def _import_modules(module_names):
imported_modules = []
for modname in module_names:
mod = importlib.import_module("aprsd_slack_plugin.conf." + modname)
if not hasattr(mod, LIST_OPTS_FUNC_NAME):
msg = "The module 'aprsd_slack_plugin.conf.%s' should have a '%s' "\
"function which returns the config options." % \
(modname, LIST_OPTS_FUNC_NAME)
raise Exception(msg)
else:
imported_modules.append(mod)
return imported_modules
def _append_config_options(imported_modules, config_options):
for mod in imported_modules:
configs = mod.list_opts()
for key, val in configs.items():
config_options[key].extend(val)

View File

@ -0,0 +1,43 @@
from oslo_config import cfg
slack_group = cfg.OptGroup(
name="aprsd_slack_plugin",
title="APRSD Slack Plugin settings",
)
slack_opts = [
cfg.StrOpt(
"signing_secret",
default=None,
help="Your Slack account signing secret"
"You have to create a slack bot account first. "
"https://api.slack.com/start/building/bolt-python",
),
cfg.StrOpt(
"bot_token",
default=None,
help="Your Slack bot's token",
),
cfg.ListOpt(
"channels",
default=None,
help="The channels you want messages sent to. This is a CSV list"
"of slack channel names.",
),
]
ALL_OPTS = (
slack_opts
)
def register_opts(cfg):
cfg.register_group(slack_group)
cfg.register_opts(ALL_OPTS, group=slack_group)
def list_opts():
return {
slack_group.name: slack_opts,
}

View File

@ -3,15 +3,21 @@ import re
import time import time
from aprsd import packets, plugin, plugin_utils from aprsd import packets, plugin, plugin_utils
from oslo_config import cfg
import aprsd_slack_plugin import aprsd_slack_plugin
from aprsd_slack_plugin import base_plugin from aprsd_slack_plugin import base_plugin
CONF = cfg.CONF
LOG = logging.getLogger("APRSD") LOG = logging.getLogger("APRSD")
class SlackLocationPlugin(base_plugin.SlackPluginBase, plugin.APRSDRegexCommandPluginBase): class SlackLocationPlugin(
base_plugin.SlackPluginBase,
plugin.APRSDRegexCommandPluginBase,
plugin.APRSFIKEYMixin,
):
"""SlackCommandPlugin. """SlackCommandPlugin.
This APRSD plugin looks for the location command comming in This APRSD plugin looks for the location command comming in
@ -35,7 +41,7 @@ class SlackLocationPlugin(base_plugin.SlackPluginBase, plugin.APRSDRegexCommandP
Install the app/bot into your workspace. Install the app/bot into your workspace.
Edit your ~/.config/aprsd/aprsd.yml and add the section Edit your ~/.config/aprsd/aprsd.conf and add the section
slack: slack:
signing_secret: <signing secret token here> signing_secret: <signing secret token here>
bot_token: <Bot User OAuth Access Token here> bot_token: <Bot User OAuth Access Token here>
@ -48,24 +54,21 @@ class SlackLocationPlugin(base_plugin.SlackPluginBase, plugin.APRSDRegexCommandP
command_regex = "^[lL]" command_regex = "^[lL]"
command_name = "location-slack" command_name = "location-slack"
def setup(self):
self.ensure_aprs_fi_key()
if self.enabled:
config_set = self.setup_slack()
if not config_set:
self.enabled = False
def process(self, packet): def process(self, packet):
LOG.info("SlackCommandPlugin") LOG.info("SlackCommandPlugin")
fromcall = packet.from_call fromcall = packet.from_call
message = packet.message_text message = packet.message_text
is_setup = self.setup_slack()
if not is_setup:
return
# get last location of a callsign, get descriptive name from weather service # get last location of a callsign, get descriptive name from weather service
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"]
# 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)

View File

@ -2,11 +2,13 @@ import logging
import re import re
from aprsd import packets from aprsd import packets
from oslo_config import cfg
import aprsd_slack_plugin import aprsd_slack_plugin
from aprsd_slack_plugin import base_plugin from aprsd_slack_plugin import base_plugin
CONF = cfg.CONF
LOG = logging.getLogger("APRSD") LOG = logging.getLogger("APRSD")
@ -46,19 +48,20 @@ class SlackMessagePlugin(base_plugin.SlackPluginBase):
command_regex = "^[sS]" command_regex = "^[sS]"
command_name = "message-slack" command_name = "message-slack"
def setup(self):
config_set = self.setup_slack()
if not config_set:
self.enabled = False
else:
self.enabled = True
def command(self, packet): def command(self, packet):
message = packet.message_text message = packet.message_text
fromcall = packet.from_call fromcall = packet.from_call
LOG.info(f"SlackMessagePlugin '{message}'") LOG.info(f"SlackMessagePlugin '{message}'")
is_setup = self.setup_slack()
if not is_setup:
LOG.error("Slack isn't setup!")
return
# 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)
LOG.debug(a)
if a is not None: if a is not None:
searchcall = a.group(1) searchcall = a.group(1)
searchcall = searchcall.upper() searchcall = searchcall.upper()

View File

@ -1,10 +1,13 @@
import logging import logging
from aprsd import messaging, packets, plugin from aprsd import messaging, packets, plugin
from oslo_config import cfg
import aprsd_slack_plugin import aprsd_slack_plugin
from aprsd_slack_plugin import base_plugin from aprsd_slack_plugin import base_plugin
CONF = cfg.CONF
LOG = logging.getLogger("APRSD") LOG = logging.getLogger("APRSD")
@ -16,10 +19,17 @@ class SlackNotifyPlugin(
version = aprsd_slack_plugin.__version__ version = aprsd_slack_plugin.__version__
def setup(self):
config_set = self.setup_slack()
if not config_set:
self.enabled = False
else:
self.enabled = True
def process(self, packet): def process(self, packet):
LOG.info("SlackCommandPlugin") LOG.info("SlackCommandPlugin")
fromcall = packet["from"] fromcall = packet.from_call
# message = packet["message_text"] # message = packet["message_text"]
is_setup = self.setup_slack() is_setup = self.setup_slack()
@ -29,13 +39,13 @@ class SlackNotifyPlugin(
wl = packets.WatchList() wl = packets.WatchList()
if wl.is_old(packet["from"]): if wl.is_old(packet["from"]):
# get last location of a callsign, get descriptive name from weather service # get last location of a callsign, get descriptive name from weather service
callsign_url = "<http://aprs.fi/info/a/{}|{}>".format(fromcall, fromcall) callsign_url = f"<http://aprs.fi/info/a/{fromcall}|{fromcall}>"
message = {} message = {}
message["username"] = "APRSD - Slack Notification Plugin" message["username"] = "APRSD - Slack Notification Plugin"
message["icon_emoji"] = ":satellite_antenna:" message["icon_emoji"] = ":satellite_antenna:"
message["attachments"] = [{}] message["attachments"] = [{}]
message["text"] = "{} - Is now on APRS".format(callsign_url) message["text"] = f"{callsign_url} - Is now on APRS"
message["channel"] = "#hemna" message["channel"] = "#hemna"
LOG.debug(message) LOG.debug(message)

View File

@ -1,111 +0,0 @@
#
# This file is autogenerated by pip-compile with Python 3.9
# by the following command:
#
# pip-compile --annotation-style=line --resolver=backtracking requirements-dev.in
#
alabaster==0.7.12 # via sphinx
aprsd==2.6.1 # via -r requirements-dev.in
aprslib==0.7.2 # via aprsd
attrs==22.1.0 # via aprsd, ax253, kiss3, pytest
ax253==0.1.5.post1 # via aprsd, kiss3
babel==2.11.0 # via sphinx
beautifulsoup4==4.11.1 # via aprsd
bidict==0.22.0 # via aprsd, python-socketio
bitarray==2.6.0 # via aprsd, ax253, kiss3
black==22.12.0 # via -r requirements-dev.in
build==0.9.0 # via pip-tools
cachetools==5.2.0 # via tox
certifi==2022.12.7 # via aprsd, requests
cffi==1.15.1 # via aprsd, cryptography
cfgv==3.3.1 # via pre-commit
chardet==5.1.0 # via tox
charset-normalizer==2.1.1 # via aprsd, requests
click==8.1.3 # via aprsd, black, click-completion, flask, pip-tools
click-completion==0.5.2 # via aprsd
colorama==0.4.6 # via tox
commonmark==0.9.1 # via aprsd, rich
coverage[toml]==6.5.0 # via pytest-cov
cryptography==38.0.4 # via aprsd, pyopenssl
distlib==0.3.6 # via virtualenv
dnspython==2.2.1 # via aprsd, eventlet
docutils==0.19 # via sphinx
eventlet==0.33.2 # via aprsd
exceptiongroup==1.0.4 # via pytest
filelock==3.8.2 # via tox, virtualenv
flake8==6.0.0 # via -r requirements-dev.in, pep8-naming
flask==2.1.2 # via aprsd, flask-classful, flask-httpauth, flask-socketio
flask-classful==0.14.2 # via aprsd
flask-httpauth==4.7.0 # via aprsd
flask-socketio==5.3.2 # via aprsd
greenlet==2.0.1 # via aprsd, eventlet
identify==2.5.10 # via pre-commit
idna==3.4 # via aprsd, requests
imagesize==1.4.1 # via sphinx
imapclient==2.3.1 # via aprsd
importlib-metadata==5.1.0 # via aprsd, ax253, flask, kiss3, sphinx
iniconfig==1.1.1 # via pytest
isort==5.11.3 # via -r requirements-dev.in
itsdangerous==2.1.2 # via aprsd, flask
jinja2==3.1.2 # via aprsd, click-completion, flask, sphinx
kiss3==8.0.0 # via aprsd
markupsafe==2.1.1 # via aprsd, jinja2
mccabe==0.7.0 # via flake8
mypy==0.991 # via -r requirements-dev.in
mypy-extensions==0.4.3 # via black, mypy
nodeenv==1.7.0 # via pre-commit
packaging==22.0 # via build, pyproject-api, pytest, sphinx, tox
pathspec==0.10.3 # via black
pbr==5.11.0 # via -r requirements-dev.in, aprsd
pep517==0.13.0 # via build
pep8-naming==0.13.2 # via -r requirements-dev.in
pip-tools==6.12.1 # via -r requirements-dev.in
platformdirs==2.6.0 # via black, tox, virtualenv
pluggy==1.0.0 # via aprsd, pytest, tox
pre-commit==2.20.0 # via -r requirements-dev.in
pycodestyle==2.10.0 # via flake8
pycparser==2.21 # via aprsd, cffi
pyflakes==3.0.1 # via flake8
pygments==2.13.0 # via aprsd, rich, sphinx
pyopenssl==22.1.0 # via aprsd
pyproject-api==1.2.1 # via tox
pyserial==3.5 # via aprsd, pyserial-asyncio
pyserial-asyncio==0.6 # via aprsd, kiss3
pytest==7.2.0 # via -r requirements-dev.in, pytest-cov
pytest-cov==4.0.0 # via -r requirements-dev.in
python-engineio==4.3.4 # via aprsd, python-socketio
python-socketio==5.7.2 # via aprsd, flask-socketio
pytz==2022.6 # via aprsd, babel
pyyaml==6.0 # via aprsd, pre-commit
requests==2.28.1 # via aprsd, sphinx, update-checker
rich==12.6.0 # via aprsd
shellingham==1.5.0 # via aprsd, click-completion
six==1.16.0 # via aprsd, click-completion, eventlet, imapclient
snowballstemmer==2.2.0 # via sphinx
soupsieve==2.3.2.post1 # via aprsd, beautifulsoup4
sphinx==5.3.0 # via -r requirements-dev.in
sphinxcontrib-applehelp==1.0.2 # via sphinx
sphinxcontrib-devhelp==1.0.2 # via sphinx
sphinxcontrib-htmlhelp==2.0.0 # via sphinx
sphinxcontrib-jsmath==1.0.1 # via sphinx
sphinxcontrib-qthelp==1.0.3 # via sphinx
sphinxcontrib-serializinghtml==1.1.5 # via sphinx
tabulate==0.9.0 # via aprsd
thesmuggler==1.0.1 # via aprsd
toml==0.10.2 # via pre-commit
tomli==2.0.1 # via black, build, coverage, mypy, pep517, pyproject-api, pytest, tox
tox==4.0.14 # via -r requirements-dev.in
typing-extensions==4.4.0 # via black, mypy
ua-parser==0.16.1 # via aprsd, user-agents
update-checker==0.18.0 # via aprsd
urllib3==1.26.13 # via aprsd, requests
user-agents==2.2.0 # via aprsd
virtualenv==20.17.1 # via pre-commit, tox
werkzeug==2.1.2 # via aprsd, flask
wheel==0.38.4 # via pip-tools
wrapt==1.14.1 # via aprsd
zipp==3.11.0 # via aprsd, importlib-metadata
# The following packages are considered to be unsafe in a requirements file:
# pip
# setuptools

View File

@ -1,4 +1,5 @@
pbr pbr
slack_sdk>=3.0 slack_sdk>=3.0
slackeventsapi>=2.1.0 slackeventsapi>=2.1.0
aprsd>=2.7.0 aprsd>=3.0.0
oslo_config

View File

@ -4,13 +4,13 @@
# #
# pip-compile --annotation-style=line --resolver=backtracking requirements.in # pip-compile --annotation-style=line --resolver=backtracking requirements.in
# #
aprsd==2.6.1 # via -r requirements.in aprsd==3.0.0 # via -r requirements.in
aprslib==0.7.2 # via aprsd aprslib==0.7.2 # via aprsd
attrs==22.1.0 # via aprsd, ax253, kiss3 attrs==22.2.0 # via aprsd, ax253, kiss3
ax253==0.1.5.post1 # via aprsd, kiss3 ax253==0.1.5.post1 # via aprsd, kiss3
beautifulsoup4==4.11.1 # via aprsd beautifulsoup4==4.11.1 # via aprsd
bidict==0.22.0 # via aprsd, python-socketio bidict==0.22.0 # via aprsd, python-socketio
bitarray==2.6.0 # via aprsd, ax253, kiss3 bitarray==2.6.1 # via aprsd, ax253, kiss3
certifi==2022.12.7 # via aprsd, requests certifi==2022.12.7 # via aprsd, requests
cffi==1.15.1 # via aprsd, cryptography cffi==1.15.1 # via aprsd, cryptography
charset-normalizer==2.1.1 # via aprsd, requests charset-normalizer==2.1.1 # via aprsd, requests
@ -27,7 +27,7 @@ flask-socketio==5.3.2 # via aprsd
greenlet==2.0.1 # via aprsd, eventlet greenlet==2.0.1 # via aprsd, eventlet
idna==3.4 # via aprsd, requests idna==3.4 # via aprsd, requests
imapclient==2.3.1 # via aprsd imapclient==2.3.1 # via aprsd
importlib-metadata==5.1.0 # via aprsd, ax253, flask, kiss3 importlib-metadata==5.2.0 # via aprsd, ax253, flask, kiss3
itsdangerous==2.1.2 # via aprsd, flask itsdangerous==2.1.2 # via aprsd, flask
jinja2==3.1.2 # via aprsd, click-completion, flask jinja2==3.1.2 # via aprsd, click-completion, flask
kiss3==8.0.0 # via aprsd kiss3==8.0.0 # via aprsd
@ -42,7 +42,7 @@ pyserial==3.5 # via aprsd, pyserial-asyncio
pyserial-asyncio==0.6 # via aprsd, kiss3 pyserial-asyncio==0.6 # via aprsd, kiss3
python-engineio==4.3.4 # via aprsd, python-socketio python-engineio==4.3.4 # via aprsd, python-socketio
python-socketio==5.7.2 # via aprsd, flask-socketio python-socketio==5.7.2 # via aprsd, flask-socketio
pytz==2022.6 # via aprsd pytz==2022.7 # via aprsd
pyyaml==6.0 # via aprsd pyyaml==6.0 # via aprsd
requests==2.28.1 # via aprsd, update-checker requests==2.28.1 # via aprsd, update-checker
rich==12.6.0 # via aprsd rich==12.6.0 # via aprsd

View File

@ -17,6 +17,10 @@ description_file =
README.rst README.rst
summary = Amateur radio APRS daemon which listens for messages and responds summary = Amateur radio APRS daemon which listens for messages and responds
[options.entry_points]
oslo.config.opts =
aprsd_slack_plugin.conf = aprsd_slack_plugin.conf.opts:list_opts
[global] [global]
setup-hooks = setup-hooks =
pbr.hooks.setup_hook pbr.hooks.setup_hook